onSelect('__entrance__')}>
@@ -391,19 +313,11 @@ export function ControlPanel({
)}
- {state.furniture.length === 0 ? (
-
No objects yet — add a preset or create one.
- ) : (
-
{state.furniture.map(renderObjectCard)}
- )}
>
)}
diff --git a/src/furniture/presets.ts b/src/furniture/presets.ts
index 450207d..50a31c3 100644
--- a/src/furniture/presets.ts
+++ b/src/furniture/presets.ts
@@ -6,6 +6,7 @@ export const DEFAULT_PRESETS: FurniturePreset[] = [
name: 'Table',
color: '#a1887f',
amount: 0,
+ isPreset: true,
shape: { type: 'rectangle', width: 80, height: 60 },
conditions: [
{ kind: 'walk_to_and_sit', weight: 1 },
@@ -17,6 +18,7 @@ export const DEFAULT_PRESETS: FurniturePreset[] = [
name: 'Chair',
color: '#d7ccc5',
amount: 0,
+ isPreset: true,
shape: { type: 'rectangle', width: 40, height: 40 },
conditions: [
{ kind: 'walk_to_and_sit', weight: 1 },
diff --git a/src/index.css b/src/index.css
index 4729efe..5f22d25 100644
--- a/src/index.css
+++ b/src/index.css
@@ -387,11 +387,29 @@ label {
text-align: center;
background: var(--bg-panel);
border: 1px solid var(--border);
- border-radius: 6px;
- padding: 4px 6px;
+ border-radius: var(--radius-sm);
+ padding: 5px 6px;
font-weight: 600;
}
+.swatch-row {
+ display: flex;
+ gap: 6px;
+}
+
+.swatch-btn {
+ width: 20px;
+ height: 20px;
+ border-radius: 50%;
+ padding: 0;
+ border: 2px solid transparent;
+ box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.3);
+}
+
+.swatch-btn.active {
+ border-color: var(--accent);
+}
+
.obj-count input:focus {
border-color: var(--accent);
box-shadow: 0 0 0 3px var(--accent-soft);
diff --git a/src/layout/evaluator.ts b/src/layout/evaluator.ts
index 1a548c9..e1d25b6 100644
--- a/src/layout/evaluator.ts
+++ b/src/layout/evaluator.ts
@@ -89,14 +89,19 @@ function scoreCondition(
case 'near_object': {
const tid = (c.params as any)?.targetId;
if (!tid) return 0.3;
- const t = state.furniture.find((x) => x.id === tid);
- if (!t) return 0.3;
+ const targets = state.furniture.filter((x) => x.id === tid || (x.type === tid && x.id !== f.id));
+ if (targets.length === 0) return 0.3;
const cx = f.shape.x + f.shape.width / 2;
const cy = f.shape.y + f.shape.height / 2;
- const tx = t.shape.x + t.shape.width / 2;
- const ty = t.shape.y + t.shape.height / 2;
- const d = Math.sqrt((cx - tx) ** 2 + (cy - ty) ** 2);
- return Math.max(0, 1 - d / 80);
+ let best = 0;
+ for (const t of targets) {
+ const tx = t.shape.x + t.shape.width / 2;
+ const ty = t.shape.y + t.shape.height / 2;
+ const d = Math.sqrt((cx - tx) ** 2 + (cy - ty) ** 2);
+ const s = Math.max(0, 1 - d / 80);
+ if (s > best) best = s;
+ }
+ return best;
}
case 'open_circulation': {
return path && path.length > 2 ? 0.7 : 0.5;
diff --git a/src/types/index.ts b/src/types/index.ts
index c6f9aa4..7037eae 100644
--- a/src/types/index.ts
+++ b/src/types/index.ts
@@ -96,6 +96,7 @@ export interface FurniturePreset {
conditions: ConditionSpec[];
color?: string;
amount?: number;
+ isPreset?: boolean;
}
export interface LayoutState {