# Horizon collision geometry The version target is Minecraft Java Edition **26.3**. Geometry is a simulation foundation; Horizon still has no Rust client, live world, or entity simulation. `horizon-core::geometry` provides `Vec3`, `Aabb`, `Axis`, `Direction`, `BlockHit`, and immutable `VoxelShape` values. Coordinates are doubles. Arithmetic keeps vanilla's operation order, signed zero, NaN comparisons, normalization threshold, and collision epsilon. It does not enable approximate or fused simulation math. Implemented vector operations include addition/subtraction, vector-to, component products, scaling, normalization, dot/cross products, interpolation, projection, distance/length, horizontal vectors and finite/equality checks. Rotations, look directions, alignment, axis replacement, direction offsets, and pitch/yaw extraction are also implemented. The full Vec3 API and entity integration remain absent. Trig uses vanilla's 65,536-entry float sine table with double angle scaling and Java long narrowing. The table is generated from the pinned formula in Rust; the geometry suite compares every generated entry against the runtime table. Inverse trig narrows to float at vanilla's conversion points. These comparisons validate the tested platform; other platforms need the same bit comparisons. Bounding boxes normalize endpoints with Java-compatible min/max. Queries include half-open containment, strict intersection, transforms, distances, ray clipping, block-coordinate construction with wrapping integer endpoints, setters, next-value deflation, and swept-box collision tests. A disjoint `intersect` preserves vanilla's constructor normalization rather than introducing a different empty-box rule. Shapes keep exact coordinate arrays and a packed occupancy grid. Greedy boxes are cached once in vanilla order. Collision-distance queries use occupied grid cells; ray and closest-point queries reuse the cached boxes. Column minima/maxima use vanilla's cyclic perpendicular coordinates and infinity for empty/outside cells. Cube shapes retain arithmetic lookup; moved shapes use array lookup even for a zero offset. Ray hits preserve face tie ordering, endpoint exclusion, tolerances, and the 0.001 inside-hit nudge. Boolean shape joins and overlap predicates preserve cube, identical, disjoint and epsilon-merger behavior. Shape operations reject truth tables that fill two empty cells, as vanilla does. Per-axis cube-coordinate metadata survives partial merges and is checked when importing cached shapes. Box construction preserves thin-box rejection and up to 1/8 cube quantization. Optimized joins reconstruct greedy boxes in vanilla order. Face/edge extraction and shape rotations remain absent. Discrete axis extrema are retained separately from occupied cells. A box can quantize to no occupied cells while retaining a finite minimum/maximum on other axes. That state affects vanilla's lower-block floor-height lookup. Empty joins also preserve their integer sentinel extrema; optimization and moving an empty shape return the canonical empty shape. Shape clones preserve identity and share immutable coordinate, occupancy and box storage. New shapes keep separate identities even when their grids are equal. This preserves alias-sensitive join shortcuts and avoids copying large grids when an operation returns an existing shape. The cache importer shares equal grid storage while retaining the distinct vanilla shape identities. `resolve_shape_movement` ports vanilla's movement resolver over a supplied shape list: Y first, then horizontal axes in movement-magnitude order, translating the box by resolved motion before each axis query. `candidate_step_heights` preserves float narrowing, duplicate identity, current-Y exclusion, and sorted candidate heights. `resolve_step_movement` adds the initial collision and step-up decision, including landing-height adjustment, a second collider query, and the first candidate improving horizontal movement. `entity_collision_bounds` preserves the initial entity query bounds. These helpers take caller-selected colliders; they do not choose world/entity shapes, set grounding, change velocity, or apply inside-block effects. The step-up corpus calls the actual private `Entity.collide` method for **17,680 cases** per JAR. Reference fixtures supply entity inputs and controlled block/entity/border shape lists; vanilla performs the movement decisions. Query bounds and results are compared bit for bit. This verifies the decision with those inputs, not live world selection or entity state transitions. `block_floor_height` returns the selected shape's maximum Y, or lazily queries the lower shape only when the current one is empty. Its 12,000-case corpus also checks the lower supplier's invocation count against `BlockGetter`. `traverse_blocks` visits blocks in vanilla ray order, including its endpoint nudge, strict corner comparisons, early callback return, integer wrap and signed-zero endpoint equality. It accepts finite rays and an explicit visit budget. Invalid geometry or exceeding that budget returns a typed error; earlier callbacks remain applied. Actual block/fluid ray selection is a separate integration task. Inclusive block-box iterators preserve X/Y/Z order, while directional iterators preserve vanilla's movement-dependent axis order and corner signs. AABB iteration includes floored maximum faces. They return independent position values and reject true volumes above the positive Java-int domain, instead of reproducing overflowed Java iteration counts. `sweep_intersected_blocks` ports movement-path visits from `BlockGetter.forEachBlockIntersectedBetween`: tiny movement, the initial box, ray-clipped footprints, packed-position deduplication, final box, step indices, and early callback stops. Stationary/tiny movement avoids a deduplication-set allocation. Inputs and computed geometry must be finite; a caller-selected work budget counts ray steps and iterator candidates, including suppressed duplicates. Earlier callbacks are not rolled back on an error. Block effects still need their own implementations and integration with entity movement. `horizon-world::collision::CachedCollisionRegistry` imports a private local export of vanilla cache data. The export includes DataVersion, all state descriptors, deduplicated shapes, coordinate-kind metadata, discrete extrema, and a per-state cache index. Import verifies every state ID against the block registry. Section lookup also requires the same borrowed registry. Imported nonempty extrema must agree with occupancy; empty extrema must be in the grid's domain or match vanilla's empty-join sentinel triple. Extrema form part of the grid-sharing key, so equal occupancy does not erase different metadata. There is no fallback shape for an unsupported state: `None` means block behavior must select it, while a cached empty shape is a valid `Some` value. This covers the cached branch of the **no-context** `BlockState.getCollisionShape` method. Vanilla's overload taking `CollisionContext` always calls block behavior; the cache cannot establish entity collision parity. Dynamic and entity-dependent selection, neighboring blocks, world borders, movement integration, fluids, block effects, and live collision broad-phase queries still need implementation. Run the comparisons after preparing the pinned artifacts: ```sh python3 tools/test_geometry_parity.py python3 tools/test_collision_parity.py ``` Both suites invoke actual vanilla methods from the authenticated client and server JARs. They compare canonical double bits, including infinities/NaNs, rather than using a numerical tolerance. The geometry corpus includes empty and sparse grids, greedy decomposition, overlapping boxes, rays, tiny movements and IEEE boundaries. It compares **1,320,187 output lines per JAR**, including **2,400 integer-box iterator cases**, **2,400 AABB iterator cases**, and **9,022 movement-path traces**. The registry suite exercises cached shape queries through resident section IDs, translation at negative and world-border coordinates, and invalid import rejection. Its export records **35,524 cached states**, **199 uncached states**, **6,093 shape identities**, and **330 distinct grids**. The comparison runs **213,144 queries** plus state/box/identity checks, producing **1,097,456 matching output lines per JAR**. These counts describe this branch and corpus; they are not a gameplay coverage percentage. Raw grids admit at most 1,048,576 cells with strictly increasing, non-NaN coordinates. Oversized or malformed imports/joins return typed errors. This bounded library API does not reproduce Java's complete exception/object surface. All exported data, test inputs, reference output, and compiled adapters remain under gitignored `.local/`. Success reports record exact sources, inputs, outputs, and reference hashes. A runner removes its prior success report before running and rejects edits to the captured sources during validation. Passing these suites does not prove whole-game parity or any FPS/render-distance improvement. Checked-in [geometry metadata](../benchmarks/rust-geometry-2026-10-05/scoped-parity.json) and [cached-collision metadata](../benchmarks/rust-collision-2026-10-05/scoped-parity.json) identify their tested source versions. Reports cover their recorded snapshots; later changes require fresh validation. Entity obstruction now evaluates ordered candidate snapshots, excluded entity identity, spectator/removal/building flags, shared root-vehicle identity and voxel-shape intersection. Empty shapes skip the query; candidate collection finishes before obstruction evaluation. Unit-block queries translate in floating point, including integer-coordinate boundaries. Run `python3 tools/test_entity_obstruction_parity.py` to compare against the actual vanilla `EntityGetter` default methods and entity metadata queries. The [entity-obstruction report](../benchmarks/rust-entity-obstruction-2026-10-06/scoped-parity.json) records **15,121 matching output lines per JAR** over composite shapes, boundary contacts, thin boxes, metadata filters, vehicle roots and injected query failures. The Java fixture privately allocates entity snapshots without running their constructors. Entity lifecycle, spatial indexing and vehicle-tree maintenance remain separate implementation work. Workspace tests, warning-free Clippy and **138 Python tests** passed at this snapshot. Legacy block solidity now uses explicit definition overrides and cached collision bounds, with force-on precedence and native mean-size/height thresholds. The no-context replaceable and liquid flags are kept independently. Solid rendering uses the occlusion definition flag and a full-shape test over imported raw occlusion geometry. Import validates the pinned DataVersion, full state identities and complete block definitions before building a shared property table. Predicates query this table in constant time. `python3 tools/test_block_properties_parity.py` compares all **35,723 states** and **18,096 synthetic cache/flag/shape cases**, including adjacent floating-point values around solidity thresholds and both occlusion flag values. The [block-properties report](../benchmarks/rust-block-properties-2026-10-06/scoped-parity.json) records **143,361 matching lines per JAR**. Definition flags, collision and raw occlusion data are private runtime inputs; Rust calculates solidity and solid rendering. The report was produced in an isolated staging copy and archived only after the integrated sources and copied fixtures matched every recorded hash. Item/fluid-context replacement and collision with live entities remain separate work. Face selection now preserves native cube-axis shortcuts, epsilon sample points, slice coordinates and clamped parent extrema, including sparse slices whose native bounds differ from occupied boxes. Repeated queries share face identities. The FULL, CENTER and RIGID support tests execute against the selected shape. `python3 tools/test_face_support_parity.py` compares **1,615 input shapes** and nested faces, producing **595,686 matching lines per JAR** in the [face-support report](../benchmarks/rust-face-support-2026-10-06/scoped-parity.json). The [refreshed geometry report](../benchmarks/rust-geometry-2026-10-06/scoped-parity.json) also retains **1,320,187 matching lines per JAR** after face slicing was added. Block-specific support-shape selection is a separate layer. Registered support caches now derive FULL/CENTER/RIGID masks from privately imported support geometry and share results by shape identity. The importer checks the data kind and full state identities. `None` explicitly requests uncached behavior; it never becomes an assumed unsupported face. `python3 tools/test_block_support_parity.py` compares **35,524 cached states** across six directions and three support kinds, while retaining **199 uncached states**. The [cached-support report](../benchmarks/rust-block-support-2026-10-06/scoped-parity.json) records **248,867 matching lines per JAR**. Face-cache storage is now lazy and attached to shape identity. Clones share it; new identities and moved shapes start with an empty cache. A regression test checks that cached faces do not retain their parent after it is dropped. The face and geometry differential suites passed again after this allocation change, as did workspace tests, Clippy and **140 Python tests**. No FPS claim is inferred from these library checks. Dynamic support selection now handles bamboo collision offsets, dripstone and sulfur-spike thickness/direction, the empty-context scaffolding support shape, powder snow, and shulker animation-state support faces. Moving-piston lookup preserves missing/wrong block-entity behavior and delegates active entity shape calculation to the world. The [dynamic-support report](../benchmarks/rust-dynamic-support-2026-10-06/scoped-parity.json) records **4,867,780 matching lines per JAR** over **107,308 fixtures** covering all **199 uncached states**, coordinate boundaries, block-entity snapshots and query failures. Run `python3 tools/test_dynamic_support_parity.py` to reproduce it. The current suite also compares no-entity-context collision geometry in **60,161 fixtures**, including **13,014 shulker progress fixtures**. Shulker collision bounds retain native float interpolation and differ from animation-state support faces. `BlockCollisionRegistry` dispatches cached and dynamic collision queries; entity movement contexts and block-entity lifecycle remain separate work. Workspace tests, warning-free Clippy and seven focused Python tests passed. Moving-piston collision now selects stationary extended bases and moving heads, translates shapes using native float progress arithmetic, and honors the movement-direction collision exclusion. Interpolation preserves float operation order, including negative partial ticks and IEEE edge cases. The separate collision-related state used for entity pushing retains its own sticky/head rules; entity pushing itself is not implemented by this module. `python3 tools/test_piston_shape_parity.py` compares **102,315 fixtures**, including every registered moved state and **10,000 raw float interpolation cases**. The [piston-shape report](../benchmarks/rust-piston-shape-2026-10-06/scoped-parity.json) records **1,096,811 matching lines per JAR**. This corpus uses absent block entities for moved-block collision selection. Workspace tests, warning-free Clippy and **142 Python tests** passed. Tick progression and live world mutation remain separate work. The refreshed dynamic-support corpus also includes **9,216 active piston snapshots**, composing block-entity lookup with piston collision generation and all support tests. It compares extension/retraction, source heads, progress boundaries, collision exclusions and query failures. These snapshots do not exercise piston ticks, pushed entities or chunk updates. The archived block-survival snapshot executes 51 rule kinds with 63 distinct rule/tag bindings over registry-bound solid flags, air identity, support tags and FULL/CENTER/RIGID geometry and empty-context collision. The verified snapshot covers **all 1,286 pinned block definitions and 35,723 states**. Candidate properties select attachment direction, hanging state, door/plant halves and pale moss carpet's serialized `bottom` property. Pressure plates and cactus flowers preserve repeated neighbor reads. Downward center support applies the unstable-bottom-center tag only where native helpers do. Upper doors require the same block below; upper double plants also require its lower half. Crop survival checks brightness against 8 before querying support; farmland preserves its conditional second read above, and dripleaf stems read both neighbors. Sugar cane, lily pads, frogspawn, spore blossoms, small dripleaves and tall seagrass preserve their independent fluid queries, source/full-water requirements and tag checks. Growing plants preserve head/body identity and kelp support restrictions. Scaffolding recomputes neighbor distance in native order; chorus plants and flowers preserve branching reads and early exits. Piston heads validate base type, facing and extension. Mangrove propagules, speleothems, pitcher crops, bells and shelf mushrooms use their version-specific attachment rules. Multiface blocks, vines, hanging moss, snow layers and sea pickles preserve support/collision query order. Fire uses imported ignition odds and waterlogging; cactus checks horizontal solids and lava before its support and upper liquid state. Paths recognize fence gates, and mushrooms preserve the light-override tag and solid-render requirement. Interrupted runs can be resumed with `python3 tools/test_block_survival_parity.py --resume`. The runner verifies source, environment, fixture and compiled-artifact hashes before reusing completed stages. A changed output is regenerated; changed inputs require a fresh run without `--resume`. Checkpoints stay under `.local`, and a parity report is written only after both native comparisons pass. `python3 tools/test_block_survival_parity.py` compares actual native `canSurvive` calls in both pinned JARs. The [block-survival report](../benchmarks/rust-block-survival-2026-10-06/scoped-parity.json) records **35,998,488 matching output lines per JAR** across **9,229,463 fixtures**. Each supported candidate state is exercised; behavior representatives are tested against every neighbor state, changing repeated reads, integer-coordinate wrapping, absent/closed/open shulker snapshots and injected block/entity query failures, plus light thresholds, independent fluid sequences and fluid-query failures. Native method ownership and support-tag fields supply private definition metadata; survival results are calculated in Rust. Workspace tests, Clippy and the **147-test Python suite** passed. This establishes the recorded survival scope; full placement, neighbor updates, scheduled behavior and complete block gameplay remain pending.