import {Entity, EntityAccessor} from "../utils/entity"; import {Part} from "./part"; import {RNG} from "../utils/rng"; export type Region = Entity & { // aligning axes of the region axes: RegionAxis[]; // current children; expect no overlapped positions children: EntityAccessor[]; } export type RegionAxis = { name: string; min?: number; max?: number; align?: 'start' | 'end' | 'center'; } /** * for each axis, try to remove gaps in positions. * - if min exists and align is start, and there are parts at (for example) min+2 and min+4, then move them to min and min+1 * - if max exists and align is end, and there are parts at (for example) max-2 and max-4, then move them to max-1 and max-3 * - for center, move parts to the center, possibly creating parts placed at 0.5 positions * - sort children so that they're in ascending order on each axes. * @param region */ export function applyAlign(region: Region){ for (const axis of region.axes) { if (region.children.length === 0) continue; // 获取当前轴向上的所有位置 const positions = region.children.map(accessor => accessor.value.position); // 根据当前轴的位置排序 children region.children.sort((a, b) => { const posA = a.value.position[0] ?? 0; const posB = b.value.position[0] ?? 0; return posA - posB; }); if (axis.align === 'start' && axis.min !== undefined) { // 从 min 开始紧凑排列 region.children.forEach((accessor, index) => { const currentPos = accessor.value.position.slice(); currentPos[0] = axis.min! + index; accessor.value.position = currentPos; }); } else if (axis.align === 'end' && axis.max !== undefined) { // 从 max 开始向前紧凑排列 const count = region.children.length; region.children.forEach((accessor, index) => { const currentPos = accessor.value.position.slice(); currentPos[0] = axis.max! - (count - 1 - index); accessor.value.position = currentPos; }); } else if (axis.align === 'center') { // 居中排列 const count = region.children.length; const min = axis.min ?? 0; const max = axis.max ?? count - 1; const range = max - min; const center = min + range / 2; region.children.forEach((accessor, index) => { const currentPos = accessor.value.position.slice(); // 计算相对于中心的偏移 const offset = index - (count - 1) / 2; currentPos[0] = center + offset; accessor.value.position = currentPos; }); } } } /** * shuffle on each axis. for each axis, try to swap position. * @param region * @param rng */ export function shuffle(region: Region, rng: RNG){ if (region.children.length <= 1) return; // Fisher-Yates 洗牌算法 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.slice(); const posJ = children[j].value.position.slice(); children[i].value.position = posJ; children[j].value.position = posI; } }