Skip to content

logic generator

laurel Lin edited this page Aug 22, 2026 · 3 revisions

src/logic/generator.ts — Track shape assembly

The core pure logic: takes a TrackConfig (three parts + gauge + height) and outputs the CubeSpec sets for every TrackShape. Pure functions, Node-testable.

Assembly rules:

  • Straight track is laid along Z on the xz plane by default, left/right rail centers at x = ±gauge/2, lifted by the height.
  • A track is one full block (16 px) by default; a shorter rail part is tiled along Z to fill it (Create's rail segments are 8 px half-blocks, two per block).
  • Other shapes are derived from the straight track by rotation (following Kuayue's diag_template/ascending_template, expressed via Cube.rotation + origin rather than recomputed coordinates).

Configuration

export interface GeneratorOptions {
	length?: number;       // total track length along the laying direction (px), default 16 (one block)
	tieInterval?: number;  // tie spacing (px)
}

export type TrackAxis = 'x' | 'z';  // the track laying direction

Laying

// left center x=-g/2, right x=+g/2, lifted by heightPx, aligned to the part's lateral center (xMid).
// The part's axis-aligned rotation (e.g. [0,-90,0]) is baked first so rails become plain Z-aligned boxes.
export function placeRails(cfg: TrackConfig, opts: GeneratorOptions = {}): CubeSpec[];

// ensures the tie's long axis crosses X (perpendicular to the Z-laid rails); parts with their own
// rotation are trusted. Shared by placeTies and buildBaseParts.
export function orientTiePerpendicular(tie: PartModel): CubeSpec[];

// loops ties along Z every tieInterval, centered laterally; ties are not lifted (heightPx applies to the
// rails only — the "rail height above the bottom face")
export function placeTies(cfg: TrackConfig, opts: GeneratorOptions = {}): CubeSpec[];

export function centerOf(cubes: CubeSpec[]): Vec3;  // bbox center

Shapes

// x_ortho / z_ortho straight track (axis defaults to 'z')
export function straightZ(cfg: TrackConfig, opts: GeneratorOptions = {}): CubeSpec[];
export function straightX(cfg: TrackConfig, opts: GeneratorOptions = {}): CubeSpec[];
export function straight(cfg: TrackConfig, axis: TrackAxis = 'z', opts: GeneratorOptions = {}): ShapeSpec;

// 45° diagonal: Z track rotated ±45° about the group center (Y axis). diag = +45° (PD), diag_2 = −45°
// (ND). A diagonal spans the block's diagonal: 3 rail segments / 3 ties (24 px) vs. the straight's 2/2 (16).
export function diagonal(cfg: TrackConfig, mirror: boolean, opts: GeneratorOptions = {}): ShapeSpec;

// ascending: Z track rotated −45° about X, pivot at the track center (the block center (8,8) on the xz
// plane in Java models) — NOT the front edge (8,0). yaw decides the facing (south=0/north=180/east=270/west=90).
// After the tilt the lower end dips below the xz plane; the track is lifted so the lowest point lands
// exactly on the plane AFTER the whole-model Y offset: offset ≥0 → lift to 0; offset <0 → lift extra
// −wholeY to push the sunken track back to the plane. `lift` shifts from/to and origin together.
export function ascending(
	cfg: TrackConfig,
	dir: 'south' | 'north' | 'east' | 'west',
	opts: GeneratorOptions = {}
): ShapeSpec;

// portal track: portal_track remaps the track/tie faces (UV kept); portal_track_mip generates the two
// overlay cubes teleport_left / teleport_right wrapping each half of the ties (excluding the rails),
// sized as the tie bbox + wrap margin. With neither, it degrades to the plain straight track.
export function teleport(cfg: TrackConfig, axis: TrackAxis = 'z', opts: GeneratorOptions = {}): ShapeSpec;

// crossing: kind 'ortho' (Z + X straight), 'diag' (PD + ND diagonal), 'pd_zo' (PD diag + Z straight),
// 'nd_zo' (ND diag + Z straight). Naming matches the reference Kuayue assets: d1 = positive diagonal,
// d2 = negative diagonal; the zo variants are the base "diagonal + Z straight" crossings, the xo variants
// are the other base rotated 90° clockwise about the crossing center (flipping the diagonal sign:
// cross_d1_xo = PD + X from nd_zo, cross_d2_xo = ND + X from pd_zo; produced by crossXo / rotateCross90,
// where the −90° Y rotation is composed onto each cube's existing rotation so the diagonal survives).
export function cross(
	cfg: TrackConfig,
	kind: 'ortho' | 'diag' | 'pd_zo' | 'nd_zo',
	opts: GeneratorOptions = {}
): ShapeSpec;

Shape table & entry point

export interface ShapeDef {
	id: string;
	name: string;
	build: (cfg: TrackConfig, opts?: GeneratorOptions) => ShapeSpec;
}

allShapes generates all 11 track shapes — only the models the blockstates actually reference; the rest are expressed via rotation: no z_ortho (shape=zo = x_ortho rotated 90°), no ascending_north/east/west (blockstate y rotations of ascending_south), no teleport_x (all four directions from teleport via y rotations), and all four crossing models are generated. Each shape carries cubes and (for mesh parts) meshes — mesh geometry is placed/rotated like the cubes (rotateMesh), so mesh-only parts also appear in every directional shape. The curve base groups tie / segment_left / segment_right are created separately by buildBaseParts. The whole-model Y offset is applied here uniformly (translating rotation pivots too, lifting cubes and meshes).

export function allShapes(cfg: TrackConfig, opts: GeneratorOptions = {}): ShapeSpec[];

← Back to English API Reference

Clone this wiki locally