fix(parity): builder, ground shadow, gizmo wrapper, wordart, doc demos#61
Merged
Conversation
…sition
Three follow-up bugs from the three.js parity sweep:
1. Builder zoom + placement. The builder still spoke pre-parity
semantics: passed the unitless slider zoom directly to the camera
(rendered the scene at 0.006× CSS scale), `placeMeshOnFloor`
returned CSS-pixel positions in CSS-axis order, and
`projectScreenToWorldGround` inverted with `1/zoom` instead of
`BASE_TILE/zoom`. Mirrored gallery's `LEGACY_ZOOM_COMPAT` pattern,
flipped placement to world units in world-axis order, and fixed the
inverse scale.
2. React + Vue ground shadow lifted by the mesh. The per-mesh ground
shadow SVG is rendered inside the `.polycss-mesh` wrapper, which
translates by `position * BASE_TILE`. Pre-parity that was a no-op
(position was near zero); post-parity the wrapper now lifts the
shadow by the mesh's world Z, so it landed at the cube's vertical
midpoint instead of the floor. Subtract `meshPosZ * BASE_TILE`
from the projection target's Z so the wrapper translate cancels.
3. TransformControls wrapper placement + drag math. The gizmo wrapper
was emitting `translate3d(position[0]px, position[1]px,
position[2]px)` for a position that's now world units in world-axis
order — so the gizmo sat near scene origin instead of on the mesh.
Apply the world→CSS axis swap + ×BASE_TILE in all three renderers.
Vanilla's axis-drag math also added a CSS-px `t` directly to a
world-unit position with the wrong axis index; divide by
`SCENE_TILE_SIZE` and write to `WORLD_AXIS_FOR_CSS[cssAxis]`.
Vanilla rotation gizmo now uses the world axis the ring visually
wraps, snapshots the start position, and re-translates each tick so
the visible bbox center stays put (matches three.js's pivot-around-
center feel without requiring callers to pre-center via
`loadMesh({ center: true })`). `gizmoPosition` now applies the
current rotation to `centerOffset` so the gizmo follows the
spinning mesh.
WordArt passed the legacy unitless zoom slider value (range ~0.001–1.2) directly to the camera, which post-parity is px-per-world-unit — scenes rendered tiny. Multiply by BASE_TILE at the camera boundary so the existing slider range produces the same on-screen scale as before. PolyDemo was writing `light-direction` / `light-ambient` / `light-color` attributes on `<poly-scene>`, but the element reads `directional-direction` / `ambient-intensity` / `directional-color` (etc.) — the demo's lighting config was silently ignored on every docs page, falling back to the renderer default intensity 1, which is very dim under the post-parity physical-Lambert (/π) shading. Rename the attributes and bump the default when callers don't override (4.5 directional, 0.55 ambient — same shape as Gallery DEFAULT_SCENE). Generator-mode path also splits `state.light` into `directionalLight` + `ambientLight` for the imperative API.
Six test cases were pinned to the pre-parity drag math and bbox-center formula: - Vanilla `applyAxisDelta` now divides the CSS-px `t` by SCENE_TILE_SIZE and writes to WORLD_AXIS_FOR_CSS[cssAxis] (world axis), so dragging the X arrow moves `position[1]` (world Y) by t/50 — not `position[0]` by t CSS px. Three position-assertion tests updated. - Vanilla rotation: the X ring is built around WORLD_AXIS_FOR_CSS[0] = world Y, so the test now expects `rotation[1]` (not [0]) to be the changed component, with X and Z magnitudes near zero. Renamed from '(X-axis inverted)' to '(around world Y)' to reflect the new behavior. - React TransformControls wrapper now applies worldPositionToCss before adding the CSS-pixel bbox center. Two wrapper-transform assertions updated for the new pixel-space output.
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.
Summary
Six interactive surfaces broke during the Three.js parity sweep (#60) — none caught by the test suite. All share the same root cause: the renderers post-parity speak world units in world-axis order (and the camera's
zoomis px-per-world-unit), but a handful of call sites still spoke the old CSS-pixel/CSS-axis dialect.placeMeshOnFloorreturned CSS-pixel positions in CSS-axis order; the unitless zoom slider was passed directly to the camera (rendered at 0.006× CSS scale);projectScreenToWorldGroundinverted1/zoominstead ofBASE_TILE/zoom. Mirrored gallery'sLEGACY_ZOOM_COMPATpattern, flipped placement to world units in world-axis order, and fixed the inverse scale..polycss-mesh, which now translates byposition * BASE_TILE. Pre-parity that was effectively a no-op; post-parity it lifted the shadow by the mesh's world Z, so it landed at the cube's vertical midpoint instead of the floor. Compensated by subtractingmeshPosZ * BASE_TILEfrom the projection target's Z.translate3d(position[0]px, position[1]px, position[2]px)for what's now a world-unit / world-axis vector — gizmo sat near scene origin instead of on the mesh. Apply the world→CSS axis swap + ×BASE_TILE in all three renderers.applyAxisDeltawas adding a CSS-pxtdirectly to a world-unit position at the wrong index. Divide bySCENE_TILE_SIZEand write toWORLD_AXIS_FOR_CSS[cssAxis]. Same fix for the plane-drag callback.loadMesh({ center: true }).gizmoPositionapplies the current rotation tocenterOffsetso the gizmo follows the spinning mesh. Single global sign flip to match the screen-drag direction users expect.fitZoom * zoomScaleis a unitless CSS-scale value; multiply byBASE_TILEat the camera boundary.PolyDemo.astrowas writinglight-direction/light-ambient/light-colorattributes on<poly-scene>, but the element readsdirectional-direction/ambient-intensity/directional-color. The demo's lighting config was silently ignored on every docs page, falling back to renderer default intensity 1 (very dim under the post-parity physical-Lambert /π shading). Renamed the attributes and bumped the default tointensity: 4.5, ambient: 0.55(gallery's defaults). Generator-mode path also splitsstate.lightintodirectionalLight+ambientLightfor the imperative API.Test plan
/builder— place several primitives, verify they snap to the click point and stack on top faces; floor shadow stays at floor; Z-gizmo sits on the mesh/wordart— renders at the same on-screen size as pre-parity/gallerywith Mesh selection + Scene interactive enabled — gizmo centers on the selected mesh; translate arrows track the mouse; rotation rings rotate around the visible center in the dragged direction/guides/projections/— the tree GLB now renders bright (was very dark)/quickstart,/core-concepts,/guides/{performance,shapes,textures}/) for the new lighting defaults