feat(3d): project a grid onto a curved surface, and a dot_map globe - #406
Merged
Merged
Conversation
…p globe Both halves of #387 — a dome of flat content and a real globe — need the same thing a per-node transform/perspective can't give: one shared vanishing point (or one shared hemisphere test) for a whole set of children, not a private one per node. style.layout-surface (cylinder|sphere) projects a container's direct children onto a curved surface at paint time only — taffy still lays out the grid flat, so nothing about layout itself changes. Each child gets a translate3d computed from its normalized offset from the container's center, run through the same css_perspective_m44/pre_concat machinery apply_plane_camera already uses for the camera's own 3D tilt (#400), just applied per child instead of per plane. Depth ordering is left at declaration/z-index order — a wide enough arc or turntable rotation can make a far cell paint over a near one, which #93 owns generally and isn't fixed here. dot_map gains projection: "orthographic", the closed-form lat/lng-to-screen projection plus the far-hemisphere visibility test a clip-path circle can't express (it just crops a flat map, it never curves). rotate.lng/lat aim the globe, limb_shading darkens towards the edge, and arcs are great-circle paths slerped in 3D and lifted off the surface, culled the same way as the dots so they don't get drawn straight through the globe. The historical flat mapping is untouched code, gated behind the projection's default, and pinned byte-identical by test. Both features are proven with pixel renders, not just the underlying geometry: a projected grid's columns are shown to differ in width where a flat grid's don't, and an orthographic globe's dot spacing is shown to converge towards the limb where the flat map's doesn't.
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.
Closes #387. Refs #388.
dot_mapas a globeprojection: "orthographic"turns the flat map into a sphere. The visibility test is the part aclip-path: circlecan never do — it has no notion of a sphere underneath, so it cuts a disc out of a flat map and shows the far hemisphere straight through.cos_crotate.lng/rotate.latto aim it, with a linear sweeplimb_shadingdarkening towards the edgearcsby 3D slerp, not linear lat/lng interpolation — which would not follow a great circle. Each sample is re-projected and visibility-tested on its own, so an arc crossing the horizon is cut into visible segments rather than drawn through the planet, and one entirely on the far side paints nothing.The world dot grid switches from fixed pixel spacing to fixed angular spacing at orthographic scale, so screen spacing converges towards the limb by construction rather than by a fudge.
layout-surfaceon a containerThe grid is laid out flat by taffy, then each cell is projected onto a cylinder or sphere with one perspective shared by the container — the shared vanishing point that per-node
transformcannot give. It reusescss_perspective_m44and thepre_concatidiomapply_plane_cameraestablished in #400, applied per child instead of per plane.layout_pass.rswas not touched, and that is the right answer rather than an omission: liketransformandperspective, this is purely paint-time.taffy_bridge.rsnever reads those either.cylinderhas noarc_yfield at all — compiler- and schema-enforced, rather than a shared struct with a field that is silently inert. That is the lesson from #339, #349 and #364 applied at the API level.Depth ordering is a stated limitation, not a silence. Children still paint in declaration order. For the intended use — a patch well under 180°, a dome of cards where every cell faces the camera — that is fine. A wide arc, or enough
rotate_yto swing a cell past the limb, can make it paint over a nearer one. #93 owns depth sorting generally and is untouched; the rule file and the type's own docs say so.A false-positive test, caught and fixed
dot_spacing_converges_towards_the_limbpassed under the stub at first: float noise madeedge_gap13.888888888888868 andcentre_gap13.888888888888888, so a naive<was satisfied by rounding alone. Requiring a real margin (edge < centre * 0.9) made it reportcentre=13.889px, edge=13.889pxand fail correctly.Worth recording as a general point: a bare
<between two floats that are mathematically equal is not a regression guard.Verification
18 new tests, each proven red by stubbing the projection:
Backward compatibility pinned both ways: a
dot_mapwith no projection field and a container with nolayout-surfacerender byte-identically.cargo fmt --all --check,cargo clippy --workspace --all-targets -- -D warnings,cargo test --workspace(1740) all clean. An end-to-end scenario mixing both features validates and renders.One thing to know for later
Both
SurfaceAngleand the globe'sRotationAngleare small self-containedFixed | Animated{from,to}types evaluated locally from the frame time — not wired into the shared keyframe pipeline, becauseanimator.rswas owned elsewhere while this was built. If a general hook lands there, these two ad hoc mini-animations are the first candidates to be unified into it.Written comment-free, per the codebase-wide rule from #345.