refactor: transition to non child entity format
This commit is contained in:
+10
-18
@@ -1,5 +1,3 @@
|
||||
import {Entity} from "@/utils/entity";
|
||||
import {Region} from "./region";
|
||||
import {RNG} from "@/utils/rng";
|
||||
|
||||
export type Part = {
|
||||
@@ -10,27 +8,21 @@ export type Part = {
|
||||
|
||||
alignments?: string[];
|
||||
alignment?: string;
|
||||
region: Entity<Region>;
|
||||
regionId: string;
|
||||
position: number[];
|
||||
}
|
||||
|
||||
export function flip(part: Entity<Part>) {
|
||||
part.produce(draft => {
|
||||
if(!draft.sides)return;
|
||||
draft.side = ((draft.side||0) + 1) % draft.sides;
|
||||
});
|
||||
export function flip(part: Part) {
|
||||
if(!part.sides) return;
|
||||
part.side = ((part.side || 0) + 1) % part.sides;
|
||||
}
|
||||
|
||||
export function flipTo(part: Entity<Part>, side: number) {
|
||||
part.produce(draft => {
|
||||
if(!draft.sides || side >= draft.sides)return;
|
||||
draft.side = side;
|
||||
});
|
||||
export function flipTo(part: Part, side: number) {
|
||||
if(!part.sides || side >= part.sides) return;
|
||||
part.side = side;
|
||||
}
|
||||
|
||||
export function roll(part: Entity<Part>, rng: RNG) {
|
||||
part.produce(draft => {
|
||||
if(!draft.sides)return;
|
||||
draft.side = rng.nextInt(draft.sides);
|
||||
});
|
||||
export function roll(part: Part, rng: RNG) {
|
||||
if(!part.sides) return;
|
||||
part.side = rng.nextInt(part.sides);
|
||||
}
|
||||
|
||||
+84
-92
@@ -1,12 +1,11 @@
|
||||
import {batch, computed, ReadonlySignal, SignalOptions} from "@preact/signals-core";
|
||||
import {Entity} from "@/utils/entity";
|
||||
import {Part} from "./part";
|
||||
import {RNG} from "@/utils/rng";
|
||||
|
||||
export type Region = {
|
||||
id: string;
|
||||
axes: RegionAxis[];
|
||||
children: Entity<Part>[];
|
||||
childIds: string[];
|
||||
partMap: Record<string, string>;
|
||||
}
|
||||
|
||||
export type RegionAxis = {
|
||||
@@ -16,38 +15,39 @@ export type RegionAxis = {
|
||||
align?: 'start' | 'end' | 'center';
|
||||
}
|
||||
|
||||
export class RegionEntity extends Entity<Region> {
|
||||
public readonly partsMap: ReadonlySignal<Record<string, Entity<Part>>>;
|
||||
export function createRegion(id: string, axes: RegionAxis[]): Region {
|
||||
return {
|
||||
id,
|
||||
axes,
|
||||
childIds: [],
|
||||
partMap: {},
|
||||
};
|
||||
}
|
||||
|
||||
public constructor(id: string, t?: Region, options?: SignalOptions<Region>) {
|
||||
super(id, t, options);
|
||||
this.partsMap = computed(() => {
|
||||
const result: Record<string, Entity<Part>> = {};
|
||||
for (const child of this.value.children) {
|
||||
const key = child.value.position.join(',');
|
||||
result[key] = child;
|
||||
}
|
||||
return result;
|
||||
});
|
||||
function buildPartMap(region: Region, parts: Record<string, Part>) {
|
||||
const map: Record<string, string> = {};
|
||||
for (const childId of region.childIds) {
|
||||
const part = parts[childId];
|
||||
if (part) {
|
||||
map[part.position.join(',')] = childId;
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
export function applyAlign(region: Entity<Region>) {
|
||||
batch(() => {
|
||||
region.produce(applyAlignCore);
|
||||
});
|
||||
}
|
||||
|
||||
function applyAlignCore(region: Region) {
|
||||
if (region.children.length === 0) return;
|
||||
export function applyAlign(region: Region, parts: Record<string, Part>) {
|
||||
if (region.childIds.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<number>();
|
||||
for (const child of region.children) {
|
||||
positionValues.add(child.value.position[axisIndex] ?? 0);
|
||||
for (const childId of region.childIds) {
|
||||
const part = parts[childId];
|
||||
if (part) {
|
||||
positionValues.add(part.position[axisIndex] ?? 0);
|
||||
}
|
||||
}
|
||||
|
||||
const sortedPositions = Array.from(positionValues).sort((a, b) => a - b);
|
||||
@@ -75,86 +75,78 @@ function applyAlignCore(region: Region) {
|
||||
});
|
||||
}
|
||||
|
||||
for (const child of region.children) {
|
||||
child.produce(draft => {
|
||||
const currentPos = draft.position[axisIndex] ?? 0;
|
||||
draft.position[axisIndex] = positionMap.get(currentPos) ?? currentPos;
|
||||
});
|
||||
for (const childId of region.childIds) {
|
||||
const part = parts[childId];
|
||||
if (part) {
|
||||
const currentPos = part.position[axisIndex] ?? 0;
|
||||
part.position[axisIndex] = positionMap.get(currentPos) ?? currentPos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
region.children.sort((a, b) => {
|
||||
region.childIds.sort((aId, bId) => {
|
||||
const a = parts[aId];
|
||||
const b = parts[bId];
|
||||
if (!a || !b) return 0;
|
||||
for (let i = 0; i < region.axes.length; i++) {
|
||||
const diff = (a.value.position[i] ?? 0) - (b.value.position[i] ?? 0);
|
||||
const diff = (a.position[i] ?? 0) - (b.position[i] ?? 0);
|
||||
if (diff !== 0) return diff;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
||||
region.partMap = buildPartMap(region, parts);
|
||||
}
|
||||
|
||||
export function shuffle(region: Entity<Region>, rng: RNG) {
|
||||
batch(() => {
|
||||
region.produce(region => shuffleCore(region, rng));
|
||||
});
|
||||
}
|
||||
export function shuffle(region: Region, parts: Record<string, Part>, rng: RNG){
|
||||
if (region.childIds.length <= 1) return;
|
||||
|
||||
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 childIds = [...region.childIds];
|
||||
for (let i = childIds.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;
|
||||
});
|
||||
const partI = parts[childIds[i]];
|
||||
const partJ = parts[childIds[j]];
|
||||
if (!partI || !partJ) continue;
|
||||
|
||||
const posI = [...partI.position];
|
||||
const posJ = [...partJ.position];
|
||||
partI.position = posJ;
|
||||
partJ.position = posI;
|
||||
}
|
||||
|
||||
region.partMap = buildPartMap(region, parts);
|
||||
}
|
||||
|
||||
export function moveToRegion(part: Part, sourceRegion: Region, targetRegion: Region, position?: number[]) {
|
||||
sourceRegion.childIds = sourceRegion.childIds.filter(id => id !== part.id);
|
||||
delete sourceRegion.partMap[part.position.join(',')];
|
||||
|
||||
targetRegion.childIds.push(part.id);
|
||||
if (position) {
|
||||
part.position = position;
|
||||
}
|
||||
targetRegion.partMap[part.position.join(',')] = part.id;
|
||||
|
||||
part.regionId = targetRegion.id;
|
||||
}
|
||||
|
||||
export function moveToRegionAll(parts: Part[], sourceRegion: Region, targetRegion: Region, positions?: number[][]) {
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
const part = parts[i];
|
||||
sourceRegion.childIds = sourceRegion.childIds.filter(id => id !== part.id);
|
||||
delete sourceRegion.partMap[part.position.join(',')];
|
||||
|
||||
targetRegion.childIds.push(part.id);
|
||||
if (positions && positions[i]) {
|
||||
part.position = positions[i];
|
||||
}
|
||||
targetRegion.partMap[part.position.join(',')] = part.id;
|
||||
|
||||
part.regionId = targetRegion.id;
|
||||
}
|
||||
}
|
||||
|
||||
export function moveToRegion(part: Entity<Part>, targetRegion: Entity<Region>, position?: number[]) {
|
||||
const sourceRegion = part.value.region;
|
||||
batch(() => {
|
||||
sourceRegion.produce(draft => {
|
||||
draft.children = draft.children.filter(c => c.id !== part.id);
|
||||
});
|
||||
targetRegion.produce(draft => {
|
||||
draft.children.push(part);
|
||||
});
|
||||
part.produce(draft => {
|
||||
draft.region = targetRegion;
|
||||
if (position) draft.position = position;
|
||||
});
|
||||
});
|
||||
export function removeFromRegion(part: Part, region: Region) {
|
||||
region.childIds = region.childIds.filter(id => id !== part.id);
|
||||
delete region.partMap[part.position.join(',')];
|
||||
}
|
||||
|
||||
export function moveToRegionAll(parts: Entity<Part>[], targetRegion: Entity<Region>, positions?: number[][]) {
|
||||
batch(() => {
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
const part = parts[i];
|
||||
const sourceRegion = part.value.region;
|
||||
sourceRegion.produce(draft => {
|
||||
draft.children = draft.children.filter(c => c.id !== part.id);
|
||||
});
|
||||
targetRegion.produce(draft => {
|
||||
draft.children.push(part);
|
||||
});
|
||||
part.produce(draft => {
|
||||
draft.region = targetRegion;
|
||||
if (positions && positions[i]) draft.position = positions[i];
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function removeFromRegion(part: Entity<Part>) {
|
||||
const region = part.value.region;
|
||||
batch(() => {
|
||||
region.produce(draft => {
|
||||
draft.children = draft.children.filter(c => c.id !== part.id);
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user