Add venue planner: OOP furniture, shapes, conditions, room/birds-eye UI, layout+optimize modes, pathfinding, random search + NN stub

This commit is contained in:
2026-10-17 19:41:09 -04:00
parent 767d9502d6
commit a708a0cfe5
14 changed files with 985 additions and 118 deletions
+49
View File
@@ -0,0 +1,49 @@
import type { LayoutEvaluation, LayoutState } from '../types';
import { evaluateLayout } from '../layout/evaluator';
export interface NNOptions {
gridSize?: number;
iterations?: number;
}
export function optimizeWithNN(state: LayoutState, options: NNOptions = {}): { bestState: LayoutState; bestEval: LayoutEvaluation } {
const candidate: LayoutState = {
room: { ...state.room },
entrance: state.entrance ? { ...state.entrance } : null,
furniture: state.furniture.map((f) => ({
...f,
shape: { ...f.shape, points: f.shape.points?.map((p) => ({ ...p })) },
})),
};
const entrance = candidate.entrance;
const centerX = candidate.room.width / 2;
const centerY = candidate.room.height / 2;
for (const f of candidate.furniture) {
if (f.lockedForOptimization) continue;
let targetX = f.shape.x;
let targetY = f.shape.y;
const needsNearEntrance = f.conditions.some((c) => c.kind === 'near_entrance');
const needsCenter = f.conditions.some((c) => c.kind === 'around_open_area');
if (entrance && needsNearEntrance) {
targetX = entrance.x + (Math.random() - 0.5) * 40;
targetY = entrance.y + (Math.random() - 0.5) * 40;
} else if (needsCenter) {
targetX = centerX - f.shape.width / 2 + (Math.random() - 0.5) * 60;
targetY = centerY - f.shape.height / 2 + (Math.random() - 0.5) * 60;
} else {
targetX = Math.random() * (candidate.room.width - f.shape.width);
targetY = Math.random() * (candidate.room.height - f.shape.height);
}
if (targetX < 0) targetX = 0;
if (targetY < 0) targetY = 0;
if (targetX + f.shape.width > candidate.room.width) targetX = candidate.room.width - f.shape.width;
if (targetY + f.shape.height > candidate.room.height) targetY = candidate.room.height - f.shape.height;
f.shape.x = targetX;
f.shape.y = targetY;
}
const bestEval = evaluateLayout(candidate, options.gridSize || 10);
return { bestState: candidate, bestEval };
}