docs(mcp): document plan↔world coordinate convention#356
Open
marcelgruber wants to merge 2 commits into
Open
Conversation
Adds a 'Coordinate conventions' section to packages/mcp/README.md so authors generating geometry programmatically don't have to reverse- engineer the axis convention from source. Covers: - right-handed X-Z ground plane, Y up; metres; radian Euler rotations - the [x, z] -> (x, y, z) mapping used by wall.start/end and slab/zone/ceiling polygon/holes (no sign flip) - top-down-mirror caveat for coordinates computed against a north-up source (land surveys, north-up site plans, most 2-D plotting libs) - one rotated, non-axis-aligned slab example so the mapping is actually exercised Closes pascalorg#337. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…s demo Reviewer feedback on the original draft (PR pascalorg#356, issue pascalorg#337) revealed that the "mirrored when viewed top-down" wording is wrong for Pascal: the editor's viewports apply rotations, not mirrors. A 53-node MCP demonstration scene was built through the API to stress-test every claim the section makes; the geometry verifies the coordinate mapping but makes plain that external page coords arrive rotated, not mirrored. README changes: - Replace the "can arrive mirrored ... reflect across the appropriate axis" wording with the empirically-observed mechanism: the 2-D plan panel applies a 90° rotation (FLOORPLAN_VIEW_ROTATION_DEG in floorplan-panel.tsx) and the 3-D top-down snap defaults to a ~45° offset from world axes. - Replace "reflect across the axis if flipped" advice with "verify with a scaled guide image; apply whatever rotation (or reflection) your authoring side needs". - Link the demo files. Demo artifacts in packages/mcp/examples: - coordinate-conventions-demo.json: 71-node SceneGraph loadable via pascal-mcp --scene. Includes reference compass, axis-aligned baseline, the rotated-30° example verbatim, paired page-intent vs world-result L's for the external-coordinate gotcha, and a takeaway band. Demo B's geometry verified analytically (sides 6/4/6/4 m, AB·AD = 0, first edge 30° from +X). - coordinate-conventions-demo.md: companion that walks through what the scene is, what each demo proves, and how to load it. Closes pascalorg#337. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds a Coordinate conventions section to
packages/mcp/README.md,placed right before
## Toolsso authors read it before touchinggeometry. Backed by a 71-node MCP demonstration scene that stress-tests
every claim the section makes.
Closes #337.
Why
The README documents units (metres) and per-tool parameters well, but
there's no author-facing statement of the coordinate convention. When
generating geometry programmatically (e.g. from a land survey), this is
the first thing you need and currently has to be reverse-engineered from
source. The
examples/generate-apartment.mdwalkthrough only usesaxis-aligned boxes (
[0,0] → [10,0]), so the handedness never surfacesin any existing example.
What the new section covers
[x, y, z].[x, z] → (x, y, z)mapping for 2-D points (wall.start/end,slab/zone/ceilingpolygonandholes) — no sign flip.apply their own rotations on top of the world axes. The 2-D plan
panel wraps content in a +90° rotation (
FLOORPLAN_VIEW_ROTATION_DEGin
floorplan-panel.tsx), and the 3-D "top-down" snap defaults to a~45° offset between world and screen axes when invoked from the iso
default position. So a layout authored as if "Y = north, viewed
top-down" will arrive rotated relative to its source — and
possibly further reflected, depending on viewport state. The
reliable fix is a scaled guide image at known anchors.
whose vertices verify analytically as sides 6/4/6/4 m,
AB · AD = 0,heading 30.0007° from +X.
How this got verified
I stress-tested the section against actual Pascal-generated geometry
before publishing. An iterative MCP-driven demo (see
examples/coordinate-conventions-demo.md)was authored, persisted, and inspected in the editor. The first
version of this PR claimed external coordinates "arrive mirrored when
viewed top-down" — but inspecting the rendered demo showed they arrive
rotated, not mirrored, and the proposed "reflect across the axis"
corrective doesn't help. The section and the demo were rewritten before
pushing. The demo is now included with the PR so future readers can
verify the same way.
Demo files
packages/mcp/examples/coordinate-conventions-demo.json— 71-node SceneGraph: reference compass at the origin, axis-aligned
baseline (Demo A), rotated-30° example verbatim (Demo B, verified
analytically), paired page-intent / world-result L pair (Demo C), and
z-reflected variant showing the mis-corrective (Demo D), plus a
takeaway band on the south edge. Loadable with
pascal-mcp --scene examples/coordinate-conventions-demo.json.packages/mcp/examples/coordinate-conventions-demo.md— walks through what the scene is and what each demo proves.
What Demo B verified
Loading and inspecting the polygon
[[0,0],[5.196,3.0],[3.196,6.464],[-2.0,3.464]]:So the example in the README is, to 4 decimal places, a 6 × 4 m rectangle
with its first edge at 30° CCW from +X and adjacent edges perpendicular.
What Demo C / D showed about the doc
The L authored on a "north-up page" with page-+x right and page-+y up,
pasted directly as
[x, z], lands in Pascal's 2-D plan panel rotated90° clockwise — stem along screen-right rather than screen-up. The
z-reflected variant (Demo D) lands as a mirror of Demo C, not as a
page-correct L. That's why the original draft's "reflect across the
appropriate axis" advice was wrong for Pascal's actual viewport
behavior, and why the section now points at viewport rotations as the
mechanism.
Verified against
pascalorg/editor@ mainpackages/core/src/services/snap.ts— "Snaps a 3D point to a regulargrid in the X/Z plane, preserving Y."
packages/core/src/services/movement.ts—movePlanTowardpacks[x, y, z]and returns[result[0], result[2]].packages/nodes/src/{wall,slab,ceiling}/tool.tsx—Vector3construction uses
(plan[0], y, plan[1])(destructured form:([x, z]) => new Vector3(x, y, z)).packages/core/src/schema/nodes/level.ts—level: z.number()(floorbase elevation, metres).
packages/editor/src/components/editor/floorplan-panel.tsx—FLOORPLAN_VIEW_ROTATION_DEG = 90, applied as<g transform="rotate(${floorplanSceneRotationDeg})">around thefloor-plan content group.
Out-of-scope but worth surfacing
While building the demo scene I hit a few API quirks in the hosted
editor.pascal.appMCP server (v0.6.1) that are unrelated to thisdocs PR but a maintainer may want to track separately:
save_scenewithgraph+includeCurrentScene:falsetail-throwsMCP error -32600: undefined is not an object (evaluating 'this.loadProject')even though the SceneStore row is durably written (the demo JSON
here was published this way;
get_project_statusconfirms theexpected
nodeCountandgraphHash).apply_patchlive-syncs against the same brokenloadProjectpath;earlier deletes can persist while creates in the same batch roll
back, so a batch is not atomic in practice.
create_project'sisPrivate: falseflag is declared in the inputschema and the storage types, but the editor route still
307-redirects anonymous visitors. So "public sharing" via the API
alone doesn't currently produce an unauth-viewable URL.
"the editor scene failed to render"); shorter labels render fine.
Bisected against several other suspects (small zones, concave
half-scale slabs, thin slabs) — the name-length was the trigger.
Happy to break any of these out into separate issues if useful.
🤖 Generated with Claude Code