Skip to content

S3: SDK on Drawing 2.0 (Frame3; Pose3 wire-only) (epic-129) - #34

Merged
rafalmaciag merged 2 commits into
masterfrom
s3/epic-129-drawing2
Oct 24, 2026
Merged

rafalmaciag merged 2 commits into
masterfrom
s3/epic-129-drawing2

Conversation

@rafalmaciag

@rafalmaciag rafalmaciag commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

Epic-129 (Matrix), slice S3 "one pose algebra", the rocket-welder-sdk half. Drawing PR: modelingevolution/drawing#5.

Drawing version pin

ModelingEvolution.Drawing 2.0.0.72-preview.1. It is set in each csproj (PackageReference), with no central props file. That covers 12 packages plus examples/SimpleClient: Localization, Vision, Devices.Robot, Devices.DistanceSensor, RocketWelder.SDK, Devices.Camera, Runtime, Operations.Welding, AdaptivePoints, Devices.Motion (was 1.13.0.71), Devices.Welding and Hmi (the rest were 1.9.8.75). Masters never pin a preview: the lead bumps this branch to 2.0.0.72 once drawing #5 merges, before this PR merges.

Some packages also pull Drawing 1.x in transitively: Automation and Automation.Abstractions through ModelingEvolution.Signals 2.0.1.1 (Drawing 1.9.4.71), and Blazor through BlazorBlaze 1.1.5 (1.0.63.45). Neither DLL references Pose3, Rotation3 or any deleted member, so it is safe for them to resolve to 2.0 in a consumer's graph.

Call sites moved, and the unit scale each one uses

Site Before After Scale
Vision/CameraProjector.cs ctor Pose3d _cameraToGripper Frame3d, FromPose3(cameraToGripper, 1) once 1
CameraProjector camera → base (×3: ProjectPoseAt, ProjectPoint, TryProjectToPixel) gripperToBase * _cameraToGripper (Pose3 *) CameraToBase() = Frame3d.FromPose3(_getCurrentPosition(), 1) * _cameraToGripper 1
CameraProjector.ProjectPoseAt Vector3d.EZ.RotationTo(rayDir) Quaternion.FromTo(Vector3d.EZ, rayDir), new Frame3d(…).ToPose3(1) 1
CameraProjector.ProjectPoint surface.Rotation.Rotate(EZ) surface.TransformVector(EZ) on a Frame3d surface 1
CameraProjector surface overloads (Pose / Vector / Point / Points × Triangle3, a-b-c) Triangle3.ToPose(), Pose3d.FromSurface(a, b, c) ToFrame(), Frame3d.FromSurface(a, b, c); private in Frame3d cores; the public Pose3d overloads convert once 1
CameraProjector.GetTcpForCameraPose cameraPose * _cameraToGripper.Inverse() (FromPose3(cameraPose, 1) * _cameraToGripper.Inverse()).ToPose3(1) 1
Devices.Robot/RobotMotionExtensions.cs corner rounding Rotation3.Slerp ×3 Quaternion.Slerp ×3; ToQuaternion() once in, Rotation3.FromQuaternion once out n/a (rotation only)
Automation.Tests/CameraProjectorTryProjectToPixelTests NonIdentityPoseChain_MatchesHandComputedGroundTruth, NonIdentityPoseChain_PointBehindCamera_ReturnsFalse gripper * handEye then .TransformPoint Frame3.FromPose3(gripper, 1) * Frame3.FromPose3(handEye, 1); same expected numbers 1
Docs: Vision/docs/CameraProjector.md (3 snippets), IDistanceSensorViewLocator remarks Pose3.FromSurface, plane + Vector3, Vector3.RotationTo Frame3.FromSurface(…).ToPose3(1.0), plane with { Position = … }, Quaternion.FromTo —

Why scale 1: every length in and out of CameraProjector is in millimetres: the poses, the returned Point3/Vector3, ProjectPoseAt(distance) and the basePoint passed to TryProjectToPixel. Projecting through the intrinsics divides by Z, so it does not depend on the unit. If the frames were in metres, every input and output point would need a second conversion, so the frames stay in mm. The public ICameraProjector signatures are unchanged.

The compiler found no other call site: the solution and SimpleClient build with 0 errors on 2.0.0.72-preview.1. PL-2 package validation (dotnet pack of Abstractions, Devices.Welding and Automation.Abstractions) passes locally.

grep-gate job (TS-129-48)

New .github/scripts/grep-gate.sh (same shape as drawing's) and a grep-gate job in .github/workflows/test.yml. test.yml now also triggers on changes to the script. Output on this branch:

G-18: 0 line(s)  [git grep -nE 'Pose3(<[^>]*>)?\.(Lerp|Distance|FromSurface)\b' -- csharp/]
G-19d: 0 line(s)  [git grep -nE 'Rotation3(<[^>]*>)?\.(Combine|Slerp)\b|\bRotationTo\s*\(' -- csharp/]

On master 46ff3ac, G-18 found 3 lines (the CameraProjector.md snippets) and G-19d found 4 (RobotMotionExtensions ×3, CameraProjector RotationTo). G-18 does not match an alias such as Pose3d.FromSurface. The 2 such calls in CameraProjector.cs failed to compile against 2.0 instead.

Test ledger (native, net10.0, Release)

Project master 46ff3ac (passed / failed / skipped) this branch
Devices.Motion.Tests 67 / 0 / 0 67 / 0 / 0
Automation.Tests 109 / 0 / 0 109 / 0 / 0 (2 rewritten, named above)
Operations.Welding.Tests 41 / 0 / 0 41 / 0 / 0
Types.Tests 56 / 0 / 0 56 / 0 / 0
AdaptivePoints.Tests 14 / 0 / 0 14 / 0 / 0
Devices.Robot.Tests 19 / 0 / 0 19 / 0 / 0
Devices.Welding.Tests 47 / 0 / 0 47 / 0 / 0
Http.Tests (not in CI) 155 / 0 / 0 155 / 0 / 0
Server.Tests (not in CI) 11 / 2 / 0 11 / 2 / 0 (same 2: Python venv, shared memory)
SDK.Tests (not in CI) hangs; killed at 15 min; 4 known /tmp permission failures before the hang same

No test was removed or added. 0 expectations changed.

How the SDK versions itself

There is no version in the files: release.sh takes the latest v*.*.* git tag (now v3.2.0) and pushes a new tag (./release.sh --minor gives v3.3.0). release.yml runs on the tag and passes /p:Version. csharp/Directory.Build.props holds only a fallback <Version Condition="'$(Version)' == ''">3.2.0</Version> for builds without /p:Version. Its comment says "Bump on each release alongside the git tag". This PR does not edit it.

Branch name

The branch is s3/epic-129-drawing2, not feature/*. That way no push publishes previews: preview-publish.yml runs only on feature/*.

🤖 Generated with Claude Code

https://claude.ai/code/session_014wH5aL3TZhJNvLqa8YTku8

- Every SDK csproj that references ModelingEvolution.Drawing moves to 2.0.0.72-preview.1
  (12 packages + SimpleClient; Devices.Motion was on 1.13.0.71, the rest on 1.9.8.75).
- CameraProjector: pose maths on Frame3/Quaternion; Pose3 is the API's wire format, converted
  once at the edge with scale 1 (every length in and out is mm). RotationTo -> Quaternion.FromTo,
  Pose3.FromSurface -> Frame3.FromSurface, Triangle3.ToPose -> ToFrame, Rotation.Rotate(EZ) ->
  TransformVector(EZ), Pose3 * / Inverse / TransformPoint/Vector -> Frame3.
- RobotMotionExtensions.RoundCorner: Rotation3.Slerp x3 -> Quaternion.Slerp, Rotation3 in/out once.
- CameraProjectorTryProjectToPixelTests: the 2 NonIdentityPoseChain tests compose the ground truth
  with Frame3 (same expected numbers).
- Docs: CameraProjector.md and IDistanceSensorViewLocator remarks name the Drawing 2.0 members.
- grep-gate job (TS-129-48): G-18 and G-19d over csharp/, fails on any line.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014wH5aL3TZhJNvLqa8YTku8
@rafalmaciag

Copy link
Copy Markdown
Contributor Author

APPROVE. I reviewed head 244a1b6 against master 46ff3ac. The Drawing source I read is bd027b8, which is the commit 2.0.0.72-preview.1 was packed from (checked against the commit= in the nuspec).

1. Every moved call site is correct maths. PASS

  • CameraProjector: units are consistent at every site. Each frame comes from a mm pose or from mm points (FromPose3(…, 1), Frame3.FromSurface, Triangle3.ToFrame). Everything composed with those frames is mm too: the live gripper pose, the hand-eye pose, the surface, distance in ProjectPoseAt, and basePoint in TryProjectToPixel. TransformVector is rotation only, so it has no unit, and the pinhole projection divides by Z. Scale 1 is therefore correct at every site, and the design's metres rule applies inside the chain and Matrix, not here.
  • Camera-to-gripper is converted once, in the constructor. ICameraProjector is unchanged. The public Pose3d overloads convert once and pass on to private in Frame3d cores.
  • Antiparallel FromTo cannot bite. I ran the old 1.9.8.75 RotationTo against the new FromTo → ToPose3(1) path. For rayDir exactly −Z, both give {Rx=180, Ry=0, Rz=180} and both map X to −X. The old code used a half-turn about +Y and the new one a half-turn about −Y, which is the same rotation. The generic direction matches to about 1e-15.
  • One real change in behaviour, and it is a fix: the old RotationTo snapped any ray within about 0.081° of ±Z (dot > 1−1e-6) to identity or the half-turn. FromTo is exact there; for example a +Z ray tilted 0.05° now gives Ry=0.05 instead of 0. Please mention this in the body.
  • RobotMotionExtensions: the old Rotation3.Slerp was already FromQuaternion(Quaternion.Slerp(ToQuaternion…)). The new code only drops the Euler round trip in the middle. It converts once in and once out.

2. No hidden semantics change. PASS

  • Across the whole repo, the only test diff is the two cameraToBase lines in CameraProjectorTryProjectToPixelTests (NonIdentityPoseChain_*). The expected pixel is the pinhole of qCamera and did not move.
  • Native Release run on the branch:
    • AdaptivePoints 14, Automation 109, Devices.Motion 67, Devices.Robot 19, Devices.Welding 47, Http 155, Operations.Welding 41 and Types 56: all 0 failed and 0 skipped, the same as master.
    • Server.Tests: 11/2/0, the same 2 host failures as on master.
    • SDK.Tests: 329/4/0 in 4 s on this host, the same 4 /tmp permission failures. It did not hang here, so the body's "hangs" is specific to the engineer's host.

3. Pins. PASS, with one correction to the body

  • All 12 package csprojs and examples/SimpleClient pin 2.0.0.72-preview.1, and nothing is left on 1.x.
  • The build has 0 NU1605/NU1608 lines, and there is no NoWarn or NU suppression anywhere.
  • "One Drawing version wins everywhere" is not literally true inside this repo. dotnet list package --include-transitive resolves Drawing 1.9.4.71 for Automation.Abstractions and Automation (through Signals), and 1.0.63.45 for Blazor and the two Blazor samples (through BlazorBlaze). Those projects reference no Drawing package directly and no project that does. Master is the same.
  • The 1.x resolution is harmless. A metadata scan shows those three SDK assemblies reference no Drawing type at all. Signals 2.0.1.1 uses only Frequency<T>, and BlazorBlaze 1.1.5 uses only Color, Point, Polygon and Rectangle members, and none of those files changed in drawing chore: add nuget cache files to gitignore #5. So in a consumer graph they unify on 2.0 safely, as the body says.

4. grep-gate. PASS

  • .github/scripts/grep-gate.sh on the branch: G-18 0 lines, G-19d 0 lines. The same rows on master: 3 and 4.
  • The alias gap is real. using Pose3d = ModelingEvolution.Drawing.Pose3<double>; appears in Vision/CameraProjector.cs, Vision/ICameraProjector.cs and Automation.Tests/CameraProjectorTryProjectToPixelTests.cs. There are no Rotation3 aliases.
  • Recommended follow-up (does not block): change G-18 to \bPose3(d|<[^>]*>)?\.(Lerp|Distance|FromSurface)\b. It finds 6 lines on master (the 3 Pose3d.FromSurface calls plus the 3 md snippets) and 0 on the branch.

5. Hygiene. PASS

  • The branch is s3/epic-129-drawing2. The Actions list for it shows only Tests (push and pull_request, both green), so no preview publish ran.
  • No version file was changed; Directory.Build.props is untouched.
  • The diff stays inside csharp/ and .github/.
  • The docs that named deleted members were updated (CameraProjector.md ×3, IDistanceSensorViewLocator remarks), and a grep finds no other doc that names a deleted member.
  • The ledger in the body is true, apart from the SDK.Tests "hangs" note above.

🤖 Generated with Claude Code

https://claude.ai/code/session_014wH5aL3TZhJNvLqa8YTku8

…-18a (Rotation3 algebra) per epic-129 test-scenarios; version fallback 3.3.0

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014wH5aL3TZhJNvLqa8YTku8
@rafalmaciag
rafalmaciag merged commit 49f8d5b into master Oct 24, 2026
4 checks passed
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.

1 participant