Skip to content

Rotating and Flipping Shapes

Mystery2099 edited this page Sep 18, 2026 · 3 revisions

Rotating and flipping shapes

Directional blocks are the reason VoxLib exists in the first place. Build the shape once in one direction, then derive the other facings instead of maintaining four nearly identical copies.

VoxLib provides six transformations on VoxelShape. They return new shapes, cache repeated work, and never mutate the input.

The transformations

Function Effect
rotateLeft() 90 degrees counterclockwise around the Y axis
rotateRight() 90 degrees clockwise around the Y axis
flipHorizontal() 180 degrees around the Y axis
flipVertical() Mirrors Y coordinates, so top and bottom swap
flipZ() Mirrors Z coordinates, so north and south swap
rotate(transformation) Applies any VoxelShapeTransformation from the table above

flip() exists as an alias for flipHorizontal(). It was the original name in 1.1.0 and remains available for compatibility. The general rotate extension is named rotateWithTransformation in Java.

import com.github.mystery2099.voxlib.rotation.VoxelRotation.rotateLeft
import com.github.mystery2099.voxlib.rotation.VoxelRotation.rotateRight
import com.github.mystery2099.voxlib.rotation.VoxelRotation.flipHorizontal
import com.github.mystery2099.voxlib.rotation.VoxelRotation.flipVertical
import com.github.mystery2099.voxlib.rotation.VoxelRotation.flipZ
import com.github.mystery2099.voxlib.rotation.VoxelShapeTransformation

val eastFacing  = northFacing.rotateRight()
val southFacing = northFacing.flipHorizontal()
val westFacing  = northFacing.rotateLeft()
val upsideDown  = northFacing.flipVertical()
val mirrored    = northFacing.flipZ()

Mapping rotations to Direction

A common pattern is one master shape for a north-facing block, with all other facings derived from it. Using a shape whose "front" points north (negative Z):

Target facing Derivation
NORTH original shape
EAST rotateRight()
SOUTH flipHorizontal()
WEST rotateLeft()
fun shapeForDirection(base: VoxelShape, facing: Direction): VoxelShape = when (facing) {
    Direction.NORTH -> base
    Direction.EAST  -> base.rotateRight()
    Direction.SOUTH -> base.flipHorizontal()
    Direction.WEST  -> base.rotateLeft()
    else -> base
}

The table assumes your original shape points north. rotateRight turns it clockwise when viewed from above, so its front ends up pointing east. If your original faces another axis, adjust the mapping to match. When a rotation looks backwards, the debug overlay is usually faster than trying to reason it out from coordinates.

flipVertical and flipZ are reflections, not rotations. They are useful for top/bottom and north/south variants, such as an upside-down slab or a shelf mounted on the opposite face.

Special cases

  • An empty shape stays empty.
  • Shapes.block() (the full cube) returns itself unchanged for every transformation.

Both checks skip the cache entirely.

Caching

Repeating the same transformation on the same shape instance is cheap after the first call. Rebuilding the source shape each time defeats that benefit because the cache keys use object identity. Caching and performance explains the exact behavior and limits.

Java usage

Extension functions inside the Kotlin VoxelRotation object compile to instance methods with the receiver as the first parameter. Java accesses the singleton through INSTANCE:

import com.github.mystery2099.voxlib.rotation.VoxelRotation;
import com.github.mystery2099.voxlib.rotation.VoxelShapeTransformation;

VoxelRotation rotations = VoxelRotation.INSTANCE;
VoxelShape eastFacing = rotations.rotateRight(northFacing);
VoxelShape southFacing = rotations.flipHorizontal(northFacing);
VoxelShape transformed = rotations.rotateWithTransformation(
    northFacing,
    VoxelShapeTransformation.ROTATE_LEFT
);

Clone this wiki locally