<!-- https://minecraftdocs.dev/reference/math-and-primitives — this page as markdown. The book's index: https://minecraftdocs.dev/llms.txt; the whole book in one file: https://minecraftdocs.dev/llms-full.txt -->

# Math and primitives

> Verified against **Minecraft 26.3** · Reference · The coordinate spaces, geometry and randomness every system page assumes — looked up, not watched.

Every system in the game speaks in a handful of value types: an integer
block position, a chunk column, a 16³ section, a double-precision world
position, a direction, a box, a collision shape, a random source. They are
the types the codebase reaches for most — `BlockPos` alone has 1,229 importers, more than any other Minecraft class — and most of what is confusing
about "which coordinate is this"
is answered by knowing which type a method takes.

## The coordinate spaces

Six integer spaces and one double one, and every conversion is a named method.
The figure is the spaces from the finest to the coarsest, and the one that adds a dimension, each arrow the arithmetic of one step; the table under it names the method for each
arrow and, for all but `GlobalPos`, the way back, and the three that also pack to a long key are
the next section's.

```mermaid
flowchart TD
    V["Vec3: a double position"] -- "floor" --> B["BlockPos: one block"]
    B -- "shift 2" --> Q["QuartPos: a 4-block cell"]
    Q -- "shift 2" --> S["SectionPos: a 16-block cube"]
    B -- "shift 4" --> S
    S -- "drop y" --> C["ChunkPos: a 16-block column"]
    C -- "shift 5" --> R["region: 32 chunks a side, one file"]
    B -- "plus a dimension" --> G["GlobalPos"]
```

*The coordinate spaces as a ladder of sizes: after the first step, which floors, every step down is a right shift by as many bits as the next space is coarser, except the one that drops the y axis and the one that adds a dimension; the table names the method for each.*

| space | unit | type | owner / notes | conversions |
|---|---|---|---|---|
| **block** | 1 block, int | `BlockPos` (extends `Vec3i`) | immutable unless it is a `BlockPos.MutableBlockPos`, the loop subclass | `BlockPos.containing` floors a double position; `BlockPos.asLong` packs to a long and `BlockPos.of` unpacks one |
| **world position** | 1 block, double | `Vec3` (implements `Position`) | entity positions, ray casts, velocities | `Vec3.atCenterOf`, `Vec3.atLowerCornerOf`, `Vec3.atBottomCenterOf` from a `Vec3i`; `Vec3.directionFromRotation` from pitch/yaw |
| **chunk column** | 16 blocks | `ChunkPos` — a **record** of x and z | packed to a long, the key of the chunk maps in `ChunkMap` and the ticket system | `ChunkPos.containing` from a `BlockPos`; `ChunkPos.pack` / `ChunkPos.unpack`; `ChunkPos.getMinBlockX`, `ChunkPos.getWorldPosition` |
| **section** | 16³ cube | `SectionPos` (extends `Vec3i`) | lighting, entity sections, render sections | `SectionPos.of` from block/chunk/entity; `SectionPos.blockToSectionCoord` (shift 4), `SectionPos.sectionToBlockCoord`, `SectionPos.sectionRelative` (mask 15); `SectionPos.chunk` drops y; `SectionPos.asLong` |
| **quart / biome** | 4 blocks | `QuartPos` (static only) | biome sampling | `QuartPos.fromBlock` (shift 2), `QuartPos.toBlock`, `QuartPos.fromSection`, `QuartPos.toSection` |
| **region** | 32 chunks | none — methods on `ChunkPos` | the `.mca` file grid | `ChunkPos.getRegionX`, `ChunkPos.getRegionLocalX`, `ChunkPos.minFromRegion`; `ChunkPos.REGION_SIZE` |
| **dimension-qualified block** | — | `GlobalPos` — record of a `Level` key and a `BlockPos` | compass targets, beds, a player's last death location | `GlobalPos.of` |
| **integer box** | blocks | `BoundingBox` for structures; `BlockBox` is a newer record that nothing yet uses | structure bounds, piece placement | `BoundingBox.intersects`, `BoundingBox.encapsulate`; `BlockBox.aabb`, `BlockBox.contains` |
| **double box** | blocks | `AABB` (a class of six public final doubles) | entity bounding boxes, block shapes' bounds | `AABB.move`, `AABB.inflate`, `AABB.intersects`, `AABB.clip` |
| **pitch / yaw** | degrees, float | `Vec2` | look direction, as (xRot, yRot) — except the *Rotation* NBT tag, which stores it the other way round | `Direction.fromYRot`, `Direction.toYRot` |
| **pose rotation** | degrees, float ×3 | `Rotations` | `ArmorStand` poses, and nothing else | — |
| **model / render space** | float | JOML `Vector3f`, `Matrix4f`, `Quaternionf` (external) | everything under `client/renderer` | `Vec3.toVector3f`; `Direction.step`, `Direction.getRotation`; `com/mojang/math` `Axis` builds quaternions |

`Position` is the three-double interface `Vec3` implements. `Vec3i` is the
int triple with the arithmetic — immutable to every caller that does not hold a `BlockPos.MutableBlockPos`, which the closer takes up — (`Vec3i.offset`,
`Vec3i.relative`, `Vec3i.distSqr`, `Vec3i.distManhattan`); `BlockPos` adds
the iteration helpers (`BlockPos.betweenClosed`, `BlockPos.withinManhattan`,
`BlockPos.spiralAround`), each of which walks a single reused
`BlockPos.MutableBlockPos` rather than allocating.
`BlockPos.breadthFirstTraversal` is the exception in the same class: it is not
an iterator at all, and allocates a queue of nodes and a set of visited longs.
`Cursor3D` is a *different* allocation-free box cursor — the one that also
classifies each position as inside, face, edge or corner
(`Cursor3D.TYPE_INSIDE`, `Cursor3D.TYPE_FACE`, `Cursor3D.TYPE_EDGE`,
`Cursor3D.TYPE_CORNER`) — and it is used by `SectionPos`, `BlockCollisions`
and `ClientLevel`, not by `BlockPos`.

Colours are a primitive too, and they live in `net/minecraft/util`: `ARGB` is
where every pack, unpack, lerp, multiply and alpha helper is, with
`CommonColors` for the named constants, `ColorRGBA` for the codec-friendly
value and `Brightness` for the packed block/sky light pair.

## Three long keys

Three long-packings appear everywhere as map keys.

- **`BlockPos.asLong`** — 26 bits X, 26 bits Z, 12 bits Y, high to low.
  `BlockPos.PACKED_HORIZONTAL_LENGTH` is derived from 30,000,000, the level's hard horizontal bound (`Level.MAX_LEVEL_SIZE`), which is why it is 26
  (`BlockPos.MAX_HORIZONTAL_COORDINATE` is 33,554,431); the remaining
  `BlockPos.PACKED_Y_LENGTH` is 12, giving −2048 to 2047. The *usable*
  range is narrower: `DimensionType` reserves a 32-block margin, so
  `DimensionType.MIN_Y` is −2032, `DimensionType.MAX_Y` is 2031 and
  `DimensionType.Y_SIZE` is 4064. `BlockPos.getFlatIndex` masks off the low
  four Y bits — it snaps a packed position to the bottom of its own
  16-block section, which is the skylight walk-up idiom, not a per-column
  key (the top eight Y bits survive). `BlockPos.STREAM_CODEC` sends the
  packed long; `Vec3i.STREAM_CODEC` sends three varints.
- **`SectionPos.asLong`** — 22 bits X, 22 bits Z, 20 bits Y. A section-relative
  position packs into a short (`SectionPos.sectionRelativePos`).
- **`ChunkPos.pack`** — X in the low 32 bits, Z in the high 32.
  `ChunkPos.INVALID_CHUNK_POS` is a sentinel; `ChunkPos.isValid` is bounded
  by `ChunkPyramid.MAX_CHUNK_COORDINATE_VALUE`, which lives with the chunk
  status pyramid because the safety margin is derived from how many
  neighbours generation reads.

## Directions and symmetry

`Direction` is the six-valued enum in 3D-data order `DOWN, UP, NORTH, SOUTH,
WEST, EAST` (`Direction.get3DDataValue`); its horizontal subset has its own
order starting at south (`Direction.get2DDataValue`). `Direction.Axis` (X, Y, Z),
`Direction.AxisDirection` and `Direction.Plane` (horizontal, vertical) are
the nested helpers; `CompositeDirection.Direction8` the compass points; `FrontAndTop` the twelve
jigsaw and crafter orientations; `AxisCycle` the axis permutation
`VoxelShape` lookups use.

Block rotation is `Rotation` and `Mirror` (`world/level/block`), each of
which maps onto `OctahedralGroup` (`com/mojang/math`) — the 48-element
symmetry group of a cube, each element a permutation plus three inversion
flags. That group is not decoration: `Shapes.rotate`, `Shapes.rotateAll`,
`Shapes.rotateHorizontal` and `Shapes.rotateAttachFace` are how a block
declares one shape and is handed the rest — three more from
`Shapes.rotateHorizontal`, five from `Shapes.rotateAll`, and eleven from
`Shapes.rotateAttachFace`, which is `Shapes.rotateHorizontal` run once per
`AttachFace`. `Transformation` wraps a JOML
matrix and lazily decomposes it into translation, left rotation, scale and
right rotation for model JSON and a display entity's transformation.

There are two things called `Axis` in scope: `Direction.Axis` and the
quaternion factory in `com/mojang/math`. They are unrelated, and a grep finds a
third in the save-migration tree, which this book does not cover.

## Shapes and collision

A `VoxelShape` is a set of boxes on a per-axis coordinate grid, backed by a
`DiscreteVoxelShape` bit grid (`BitSetDiscreteVoxelShape`). `Shapes` is the
factory and algebra: `Shapes.block`, `Shapes.empty`, `Shapes.box`,
`Shapes.or`, `Shapes.join` with a `BooleanOp`, `Shapes.joinIsNotEmpty`,
`Shapes.collide`, `Shapes.blockOccludes`, with `Shapes.EPSILON` (1.0E-7) the tolerance the shape algebra's own comparisons use and `Shapes.BIG_EPSILON` a coarser 1.0E-6; a collision context's *is above* test uses a looser 1.0E-5. Implementations
differ by how the grid is stored — `CubeVoxelShape` (even divisions),
`ArrayVoxelShape` (explicit coordinate lists), `SliceShape` (a
one-cell-thick view, used for face culling and occlusion) — and `Shapes.joinUnoptimized` picks an `IndexMerger` strategy per axis, building a `CubeVoxelShape` only when all three merge evenly; `Shapes.join` is that followed by `VoxelShape.optimize`.

Context-free collision and face-sturdiness queries are cheap because they are mostly not computed (the outline, interaction and support shapes are asked of the block every time):
`BlockBehaviour.BlockStateBase.initCache` builds a
`BlockBehaviour.BlockStateBase.Cache` per block state holding the collision
shape, whether the collision shape reaches outside its cell, whether the collision shape is a full block,
and a per-face sturdiness array — but **only for a block whose shape is not
dynamic**, and the occlusion shape is not in it. That one is a field on
`BlockBehaviour.BlockStateBase` itself, built whether the cache is or not. Without a cache a block answers those queries live, and says it has a large collision shape whatever it has; and the query an entity's movement makes, with its `CollisionContext`, is never cached — it asks the block every time.

`CollisionContext` is what a shape query knows about who is asking:
`CollisionContext.of` an entity (`EntityCollisionContext` — descending,
bottom Y, held item, whether fluids collide), `CollisionContext.empty`,
`CollisionContext.placementContext` — all three of which are
`EntityCollisionContext`s, as is the `MinecartCollisionContext`
`CollisionContext.of` returns for a minecart under the experimental movement
flag. The one case that is neither is `PositionCollisionContext`, from
`CollisionContext.positionContext`. Ray casts
return a `HitResult`: `BlockHitResult` (position, face, inside,
world-border) or `EntityHitResult`.

## Two random families, a saved table, and a mixer

`RandomSource` (`net/minecraft/util`) is the interface. Most implementations
live in `world/level/levelgen`, and the legacy family shares `BitRandomSource`,
which defines `RandomSource.nextInt` and friends on top of a raw bit generator;
`XoroshiroRandomSource` implements `RandomSource` directly, and
`RandomSequences` keeps one more in `world/`. Two families coexist in one
process:

- **Legacy LCG** — `LegacyRandomSource` (the java.util.Random algorithm),
  `SingleThreadedRandomSource` (same, no atomics), `ThreadSafeLegacyRandomSource`.
  `RandomSource.create` returns a `LegacyRandomSource` with a uniquified
  seed; this is `Level.random` (inherited unchanged by `ServerLevel` and
  `ClientLevel`), `Entity.random`,
  `ParticleEngine.random`. `RandomSource.createThreadLocalInstance` returns a
  `SingleThreadedRandomSource` and is what `ClientLevel.animateTick` uses for
  block animation, and `LevelRenderer` for the block-destroy overlay.
- **Xoroshiro** — `XoroshiroRandomSource` (128-bit state via
  `RandomSupport.Seed128bit`), the one a noise settings file gets unless its required *legacy_random_source* flag asks for legacy:
  `NoiseGeneratorSettings.getRandomSource` returns
  `WorldgenRandom.Algorithm.XOROSHIRO` unless the settings opt into legacy.
  **Four of the seven shipped noise settings do opt in** — *nether*, *end*,
  *caves* and *floating_islands* — and the overworld is not one of them, nor
  are *amplified* and *large_biomes*. So an ordinary world runs both families at once: its overworld's noise is Xoroshiro and the noise of the two dimensions you walk into through a portal is legacy, while every dimension carves, and chooses where its structures go, with a legacy source, and decorates with a Xoroshiro one.
  `RandomState` forks the chosen algorithm positionally (`PositionalRandomFactory.at`,
  `PositionalRandomFactory.fromHashOf`) for the named noise consumers — the
  aquifer and the ore placer each get their own deterministic stream from
  the seed and position.

`WorldgenRandom` wraps any delegate and adds the seeding conventions —
`WorldgenRandom.setDecorationSeed`, `WorldgenRandom.setFeatureSeed`,
`WorldgenRandom.setLargeFeatureWithSalt`, `WorldgenRandom.seedSlimeChunk` —
that make a structure land in the same place for the same seed. Features go
through *those*, not through `RandomState`.

There is a third randomness path, on the Xoroshiro family: `RandomSequence` and `RandomSequences`, a saved, `Identifier`-keyed table of `XoroshiroRandomSource`
streams derived from the world seed, which is what makes a loot table and
`/random` reproducible across sessions. And two that are not a `RandomSource` at all: `LinearCongruentialGenerator`, the bare mixer `BiomeManager` uses for biome fuzzing, and `Level.randValue`, the integer `Level.getBlockRandomPos` steps for random-tick positions.

`RandomSource.nextGaussian` is produced by `MarsagliaPolarGaussian`, which caches a spare value — which is why reseeding a source resets it, in every implementation but the deprecated `ThreadSafeLegacyRandomSource` and `WorldgenRandom`, whose `WorldgenRandom.setSeed` reseeds only its delegate.

## `Mth` and `Util`

`Mth` is the maths grab-bag (703 importers): `Mth.floor`, `Mth.clamp`,
`Mth.lerp`, `Mth.wrapDegrees`, `Mth.rotLerp`, `Mth.smallestEncompassingPowerOfTwo`,
`Mth.log2`, `Mth.positiveModulo`, `Mth.hsvToRgb`. `Mth.sin` and `Mth.cos`
are lookups in a 65,536-entry table (`Mth.cos` is the same table with a
quarter-turn phase shift), and the table is filled from the JDK's ordinary
sine rather than its strict one — so the platform-dependent step, if
you are chasing animation determinism, is the table's construction and not
the lookup. `Util` (in `net/minecraft/util`, 457 importers) is where the
executors live — `Util.backgroundExecutor`, `Util.ioPool`,
`Util.nonCriticalIoPool` — along with time sources and collection helpers;
`Unit` is the single-valued "void" type codecs and futures use. The other
numeric odds and ends worth knowing by name are `CubicSpline` (terrain
shaping), `InclusiveRange`, and `BitStorage`, the packed-integer array
underneath every palette.

## What trips people up

**`ChunkPos` is a record with no *asLong*.** The names are `ChunkPos.pack`
and `ChunkPos.unpack`; construction from a block is `ChunkPos.containing`;
the components are accessed as x() and z().

**`Vec3i.toMutable` returns a JOML `Vector3i`,** not a
`BlockPos.MutableBlockPos`; the mutable block position comes from `BlockPos.mutable` or its constructor, and its `BlockPos.MutableBlockPos.set` /
`BlockPos.MutableBlockPos.move` are the loop idiom.

**A `BlockPos` may be mutable.** `Vec3i` keeps protected setters that `BlockPos.MutableBlockPos`, a subclass of `BlockPos`, makes public; every other subclass treats them as final, so a `BlockPos` reference can hold a position that moves. `BlockPos.immutable` is the copy to call before
storing a mutable one.

**`Level.random` crashes when two threads draw at once.**
`LegacyRandomSource` holds an atomic seed not for safety but as a
*detector*: a draw or a reseed whose compare-and-set loses to another thread's write raises a `ThreadingDetector` exception, so concurrent use is caught when two uses collide and not otherwise. The genuinely safe variant,
`ThreadSafeLegacyRandomSource`, and `RandomSource.createThreadSafe` are both
deprecated. Touching a level's random from a worker is loud only when it collides.

**Tick randomness and worldgen randomness do not divide by family.** The LCG drives every `Level` and `Entity`, the Nether's and the End's noise, and every dimension's carvers and structure placement; the saved `RandomSequences` behind loot and `/random` are Xoroshiro, and so are the overworld's noise and every dimension's features.
`PositionalRandomFactory.parityConfigString` is implemented by **both**
families, so the parity dumps cover whichever one a dimension's noise settings choose.

**`BlockBox` is declared and unused.** It is a tidy `BlockPos`-pair record
in `net/minecraft/core`, and in 26.3 nothing calls it; structure bounds are
still `BoundingBox` in `world/level/levelgen/structure`. Worth knowing
before assuming a rename happened.

**`Direction.getRotation` treats UP as identity**, and `Direction.step`
returns a fresh JOML vector while `Direction.getUnitVec3f` returns the read-
only shared one.

**`SectionPos` has `SectionPos.x` as both an instance and a static method,** instance and static, same name.

**Not everything with *Block* in the name is in `net/minecraft/core`.**
`BlockBox`, `BlockMath` and `Cursor3D` are; `BlockUtil` is in
`net/minecraft/util` ([naming drift](naming-drift#the-four-you-will-hit-in-the-first-ten-minutes)).
`BlockMath` is model-rotation plumbing, not coordinates.

## Where to look

`Vec3i` · `BlockPos` · `ChunkPos` · `SectionPos` · `QuartPos` · `GlobalPos` ·
`Cursor3D` · `Vec3` · `AABB` · `BoundingBox` · `Direction` · `Rotation` ·
`OctahedralGroup` · `VoxelShape` · `Shapes` · `CollisionContext` ·
`HitResult` · `ARGB` · `Mth` · `RandomSource` · `BitRandomSource` ·
`WorldgenRandom` · `PositionalRandomFactory` · `RandomSequences` · `Util`

---

*Rules: names, never code · how the system works, not how the code reads ·
newest version only · every backticked name passes `tools/verify_names.py`.*
