import {Entity} from "../utils/entity"; import {Part} from "./part"; import {RNG} from "../utils/rng"; export type Region = { id: string; axes: RegionAxis[]; children: Entity[]; } export type RegionAxis = { name: string; min?: number; max?: number; align?: 'start' | 'end' | 'center'; } export function applyAlign(region: Entity) { region.produce(applyAlignCore); } function applyAlignCore(region: Region) { if (region.children.length === 0) return; for (let axisIndex = 0; axisIndex < region.axes.length; axisIndex++) { const axis = region.axes[axisIndex]; if (!axis.align) continue; const positionValues = new Set(); for (const child of region.children) { positionValues.add(child.value.position[axisIndex] ?? 0); } const sortedPositions = Array.from(positionValues).sort((a, b) => a - b); const positionMap = new Map(); if (axis.align === 'start' && axis.min !== undefined) { sortedPositions.forEach((pos, index) => { positionMap.set(pos, axis.min! + index); }); } else if (axis.align === 'end' && axis.max !== undefined) { const count = sortedPositions.length; sortedPositions.forEach((pos, index) => { positionMap.set(pos, axis.max! - (count - 1 - index)); }); } else if (axis.align === 'center') { const count = sortedPositions.length; const min = axis.min ?? 0; const max = axis.max ?? count - 1; const range = max - min; const center = min + range / 2; sortedPositions.forEach((pos, index) => { const offset = index - (count - 1) / 2; positionMap.set(pos, center + offset); }); } for (const child of region.children) { child.produce(draft => { const currentPos = draft.position[axisIndex] ?? 0; draft.position[axisIndex] = positionMap.get(currentPos) ?? currentPos; }); } } region.children.sort((a, b) => { for (let i = 0; i < region.axes.length; i++) { const diff = (a.value.position[i] ?? 0) - (b.value.position[i] ?? 0); if (diff !== 0) return diff; } return 0; }); } export function shuffle(region: Entity, rng: RNG) { region.produce(region => shuffleCore(region, rng)); } function shuffleCore(region: Region, rng: RNG){ if (region.children.length <= 1) return; const children = [...region.children]; for (let i = children.length - 1; i > 0; i--) { const j = rng.nextInt(i + 1); const posI = [...children[i].value.position]; const posJ = [...children[j].value.position]; children[i].produce(draft => { draft.position = posJ; }); children[j].produce(draft => { draft.position = posI; }); } }