Skip to content

interp: the tilted work plane, G68.2, G68.4 and G69 - #4600

Open
grandixximo wants to merge 3 commits into
LinuxCNC:masterfrom
grandixximo:twp-plane
Open

grandixximo wants to merge 3 commits into
LinuxCNC:masterfrom
grandixximo:twp-plane

Conversation

@grandixximo

@grandixximo grandixximo commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

A frame composed inside the offset chain, so the blocks between a definition and its cancel are programmed in the tilted plane while G54 itself is untouched:

world = TLO + G5x + Rz(rotation_xy) * (G92 + O + R * program)

G68.2 takes the Fanuc forms (0i-F operator manual B-64604EN-2, 5.7.1): three angles about the axes Q names (P0 Euler, P1 fixed axes); three points (P2), the origin on the first point, shifted by an optional Q0 along the plane's own axes, R on any one block; two vectors (P3), the X direction kept as given and the normal squared to it, refused 5 degrees or more off square. Each form takes an R about the plane's own Z. G68.4 composes onto the active plane. G69, init, M2, M30 and an abort cancel it. Rotary and UVW words do not pass through the plane. Each form refuses the words it does not read. While a plane is active G92, G52, G10 L2/L20/L10/L11 and a coordinate system change are refused, since each redefines the system the plane sits on. The docs list what is accepted beyond Fanuc: R on P0, P1 and P3, Q on P0, P1 orders that repeat an axis, and all three angles zero.

Canon gains SET_G68_FRAME and a frame stage in rotate_and_offset_pos() and its inverse, so positions, probe results and arcs follow the plane. Task status carries the plane; the python canons, AXIS and halui apply it, and the previews restate the plane in effect on the machine from status.

Second commit, suggested by Sigma1912: the preview draws each plane a program defines, a rectangle over the moves made under it with the plane's axes and its origin, and the plane in effect on the machine in its own colour.

Third commit: the nutating sim's G68.2 remap builds the same plane as the native code from the same program (R about the plane's own Z, the P2 origin and Q0, P3 on Q1 and Q2 with I J K as a direction).

This interpreter-only plane is what the kinematics side builds on later (G53.n, joint-space moves); on its own it needs no kinematics and works on any machine. Tests: tests/interp/g68-frame, tests/interp/g68-words, tests/workplane-preview, tests/glcanon/test_workplane.py, tests/gcode-renderer; docs in g-code.adoc. Part of the plan in #4374.

@Sigma1912

Copy link
Copy Markdown
Contributor

G68.2 P2 Q{0,1,2,3} uses words X,Y,Z,R but also accepts I,J,K which are silently ignored. Shouldn't I,J,K words be an error here?

@grandixximo

Copy link
Copy Markdown
Contributor Author

Yes, it should be an error, thanks. It was not only I, J, K on P2: every form now refuses the words it does not read, instead of ignoring them. That covers I, J or K with P2, R on a P2 block other than Q0, X, Y, Z or R on the P3 Q2 block, and A, B, C, U, V or W with any form. They are listed in the docs' error list, and tests/interp/g68-words checks each one.

The preview bug you found on Discord is fixed in the same push: after a plane was set from MDI, the previews restated it as a bare G68.2 or G68.4. In AXIS that gave "G68.4 needs an active tilted work plane" at line 0, or silently a plane at the origin, and in gmoccapy and qtvcp it failed for any plane. The previews now restate the machine's plane in full, and tests/workplane-preview compares where the preview and the machine end.

@Sigma1912

Copy link
Copy Markdown
Contributor

Pulled and continuing testing. Something seems off with the checking of Q word sequencing for G68.2 P2. It accepts starting the block sequence with Q1 which seems correct if I understand the docs right:
The 'Q0' block gives the origin and 'R'; without it the origin is the first point.
However it seems to expect the next block to contain Q0:

Test:

  1. MDI: g68.2 P2 q1 x100
  2. MDI: g68.2 P2 q2 x100

produces an error:

emc/task/emctask.cc 81: interp_error: G68.2 P2 expects Q0 here
G68.2 P2 expects Q0 here

PS
Tell me if my iterative work method bugs you :)

@grandixximo

grandixximo commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor Author

Tell me if my iterative work method bugs you :)

Nah, works for me, on it...

@grandixximo

Copy link
Copy Markdown
Contributor Author

Thanks, that one was a real bug: a P2 definition may start at Q1, but the next block still expected Q0. Once Q1 is given, Q0 now counts as left out, so Q1, Q2, Q3 builds the plane with its origin on the first point, and a Q0 after Q1 is refused. tests/interp/g68-words has both cases now. Keep them coming, the iterative testing helps a lot.

A frame composed inside the offset chain, so the blocks between a definition and its cancel are programmed in the tilted plane while G54 itself is untouched:

    world = TLO + G5x + Rz(rotation_xy) * (G92 + O + R * program)

O and R are the plane's origin and rotation in the coordinate system active when it was defined; rotary and UVW words do not pass through it.

G68.2 takes the Fanuc forms: three angles about the axes Q names (P0 Euler, P1 fixed axes), three points over up to four blocks (P2, the origin on the first point, shifted by Q0 along the plane's own axes, R on any one block), two vectors over two blocks (P3, the X direction kept as given and the normal squared to it, refused 5 degrees or more off square, as Fanuc does), each with an R about the plane's own Z. G68.4 composes any of those onto the active plane. G69 cancels; so do init, M2 and M30. An abort cancels it and tells canon even when the read ahead had already cancelled: status carries the plane the executed canon stream last set, and an abort throws the queued G69 away, so the two disagree until canon is told. An explicit G69 tells canon either way.

Canon gains SET_G68_FRAME and the frame stage in rotate_and_offset_pos() and its inverse, so positions, probe results and arcs follow the plane. Task status carries g68_offset, g68_rotation and g68_active; the python canons, AXIS and halui apply it. While a plane is active G92, G52, G10 L2/L20/L10/L11 and a coordinate system change are refused, since each defines the system the plane sits on.

Each form takes only the words it reads; any other (I J K with P2, R off its block, X Y Z on the P3 Q2 block, A B C U V W anywhere) is refused rather than ignored. A P2 or P3 definition left unfinished in MDI is dropped when a program is opened. The previews restate the plane in effect on the machine as a full G68.2 block from status, on the machine's coordinate system, since the bare code in the active G-codes cannot carry it. Tests: tests/interp/g68-words, tests/workplane-preview. Both reported by Sigma1912.
Where a program's G68.2 planes sit is the hardest thing to check by reading it, so the preview draws each: a rectangle lying in the plane over the moves made under it, with a margin, the plane's X, Y and Z at the centre of the rectangle in the machine axis colours, and a cross at the plane's origin, which need not lie in the rectangle: a program drilling a sphere puts every origin at the centre and works out on the surface. The rectangle sits at the plane's Z where the program reaches it and otherwise at the nearest end of the Z range worked in, so a drilling cycle shows its holes; a plane nothing moved under gets a square a tenth of the program. A plane restated in a loop is one plane. The plane in effect on the machine, what status reports the last executed G68.2 set, is drawn over the others in its own colour, so the plane the program is in stands out; an MDI plane shows the same way.

The renderer already receives every plane through set_g68_frame(); it records each as a WorkPlaneRecord, origin and axes through its transform like a move endpoint, every move under it extends the extents, and the records reach the canon with the rest of the program, as glcanon_scene.WorkPlane. WorkPlanePart draws them in the program's frame through gcode.display_points(), the GEOMETRY transform for points that are not a parse's, gated by show_workplane, colours 'workplane' and 'workplane_active'. Suggested by Sigma1912.

tests/glcanon/test_workplane.py, on parsed programs; docs in the G68.2 section.
…ve code do

The sim's remap and the native G68.2 built different planes from the same program in four places; the remap's G68.2 and G68.4 now follow the native code, which follows the Fanuc 0i-F operator manual (B-64604EN-2, 5.7.1):

- R turned the plane about the work Z; it now turns it about the plane's own Z, in every form.
- The three-point form (P2) took Q0's X Y Z as the origin in the work coordinates. The origin is now the first point, and Q0, which may be left out, shifts it along the plane's own axes. R may be given on any one block; R on two blocks is refused, as natively, since the manual does not say which would win.
- The two-vector form (P3) read the X vector as a point and subtracted the origin from it, and it took Q0 and Q1 where Fanuc takes Q1 and Q2. It now takes Q1 and Q2, reads I J K as a direction, keeps X as given, squares the normal to it and refuses vectors 5 degrees or more off square, or a zero vector.
@grandixximo

Copy link
Copy Markdown
Contributor Author

I went through the Fanuc 0i-F operator manual (B-64604EN-2, 5.7.1) and brought G68.2 in line with it where the two differed. The same changes went into your nutating sim remap, so the sim and the native code now build the same plane from the same program:

  • P2: the origin is the first point, and Q0, which may be left out, shifts it along the plane's own axes after R. R may be given on any one block; R on two blocks is refused, since the manual does not say which would win.
  • P3: the X direction on Q1 is kept as given and the normal on Q2 is squared to it, as Fanuc does. Vectors 5 degrees or more off square, or a zero vector, are refused.
  • R turns the plane about its own Z in every form.

Four changes in the remap you should know about:

  • R used to turn the plane about the work Z. It now turns it about the plane's own Z. This changes the result of any existing program that uses R.
  • Q0 is optional in P2.
  • P3 now takes Q1 and Q2, like Fanuc, where it took Q0 and Q1.
  • P3 reads I J K on Q1 as a direction. It used to subtract the origin from it, as if it were a point.

The docs list what we accept beyond Fanuc: R on P0, P1 and P3, Q on P0, P1 orders that repeat an axis such as Q121, and all three angles zero, which Fanuc accepts only with parameter ATW set. If you do have access to a Fanuc, I'd like to know which R wins when it's given on two P2 blocks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants