) => {
onStateChange({ ...state, room: { ...state.room, ...room } });
};
@@ -88,7 +34,7 @@ export function ControlPanel({
};
const placeEntrance = () => {
- onStateChange({ ...state, entrance: { x: 20, y: 20, width: 8, height: 8 } });
+ onStateChange({ ...state, entrance: { x: 20, y: 20, width: 20, height: 20 } });
};
const toggleLock = () => {
@@ -128,6 +74,16 @@ export function ControlPanel({
onStateChange({ ...state, furniture: updated });
};
+ const updateSelectedSize = (width: number, height: number) => {
+ if (!selectedId) return;
+ const updated = state.furniture.map((f) =>
+ f.id === selectedId
+ ? { ...f, shape: { ...f.shape, width: Math.max(10, width), height: Math.max(10, height) } }
+ : f,
+ );
+ onStateChange({ ...state, furniture: updated });
+ };
+
const selected = state.furniture.find((f) => f.id === selectedId);
return (
@@ -162,29 +118,10 @@ export function ControlPanel({
style={{ width: 80, marginLeft: 4 }}
/>
+
-
-
-
-
-
{state.entrance && (
@@ -209,8 +146,53 @@ export function ControlPanel({
)}
+ Premade Objects
+
Objects
+ {state.entrance && (
+
onSelect('__entrance__')}
+ style={{
+ padding: 4,
+ background: selectedId === '__entrance__' ? '#eee' : 'transparent',
+ cursor: 'pointer',
+ }}
+ >
+ Entrance
+
+ )}
{state.furniture.map((f) => (
))}
+ {selectedId === '__entrance__' && state.entrance && (
+
+ )}
{selected && (
@@ -243,6 +270,29 @@ export function ControlPanel({
+
Conditions:
{(['walk_to_and_sit', 'around_open_area', 'near_entrance', 'near_object', 'accessible', 'not_blocking_path', 'open_circulation'] as ConditionKind[]).map(
diff --git a/src/components/RoomCanvas.tsx b/src/components/RoomCanvas.tsx
index 0447e31..9192a1c 100644
--- a/src/components/RoomCanvas.tsx
+++ b/src/components/RoomCanvas.tsx
@@ -14,11 +14,8 @@ interface DragState {
offsetX: number;
offsetY: number;
resizing?: boolean;
- edge?: 'se' | 'e' | 's' | 'nw' | 'ne' | 'sw' | 'n' | 'w';
}
-const MIN_SIZE = 10;
-
export function RoomCanvas({ state, onStateChange, mode, selectedId, onSelect }: RoomCanvasProps) {
const canvasRef = useRef
(null);
const [drag, setDrag] = useState(null);
@@ -42,9 +39,20 @@ export function RoomCanvas({ state, onStateChange, mode, selectedId, onSelect }:
ctx.strokeRect(0, 0, state.room.width, state.room.height);
if (state.entrance) {
+ const e = state.entrance;
+ const w = e.width ?? 20;
+ const h = e.height ?? 20;
ctx.fillStyle = '#4caf50';
- ctx.fillRect(state.entrance.x - 4, state.entrance.y - 4, 8, 8);
- ctx.fillText('ENTRANCE', state.entrance.x + 6, state.entrance.y + 4);
+ ctx.fillRect(e.x, e.y, w, h);
+ ctx.strokeStyle = selectedId === '__entrance__' ? '#ff5722' : '#333';
+ ctx.lineWidth = selectedId === '__entrance__' ? 3 : 1;
+ ctx.strokeRect(e.x, e.y, w, h);
+ ctx.fillStyle = '#000';
+ ctx.font = '10px sans-serif';
+ ctx.fillText('ENTRANCE', e.x + 2, e.y + 12);
+ // resize handle (SE)
+ ctx.fillStyle = selectedId === '__entrance__' ? '#ff5722' : '#666';
+ ctx.fillRect(e.x + w - 4, e.y + h - 4, 8, 8);
}
for (const f of state.furniture) {
@@ -75,15 +83,21 @@ export function RoomCanvas({ state, onStateChange, mode, selectedId, onSelect }:
const handleMouseDown = (e: React.MouseEvent) => {
const pos = getMousePos(e);
- if (selectedId) {
- const f = state.furniture.find((x) => x.id === selectedId);
- if (f) {
- const seX = f.shape.x + f.shape.width;
- const seY = f.shape.y + f.shape.height;
- if (Math.abs(pos.x - seX) < 8 && Math.abs(pos.y - seY) < 8) {
- setDrag({ id: selectedId, offsetX: 0, offsetY: 0, resizing: true, edge: 'se' });
- return;
- }
+ if (state.entrance) {
+ const en = state.entrance;
+ const w = en.width ?? 20;
+ const h = en.height ?? 20;
+ const handleX = en.x + w - 4;
+ const handleY = en.y + h - 4;
+ if (Math.abs(pos.x - handleX) <= 6 && Math.abs(pos.y - handleY) <= 6) {
+ onSelect('__entrance__');
+ setDrag({ id: '__entrance__', offsetX: 0, offsetY: 0, resizing: true });
+ return;
+ }
+ if (pointInRect(pos, { x: en.x, y: en.y, w, h })) {
+ onSelect('__entrance__');
+ setDrag({ id: '__entrance__', offsetX: pos.x - en.x, offsetY: pos.y - en.y });
+ return;
}
}
for (let i = state.furniture.length - 1; i >= 0; i--) {
@@ -101,18 +115,22 @@ export function RoomCanvas({ state, onStateChange, mode, selectedId, onSelect }:
const handleMouseMove = (e: React.MouseEvent) => {
if (!drag) return;
const pos = getMousePos(e);
+ if (drag.id === '__entrance__' && state.entrance) {
+ const en = state.entrance;
+ if (drag.resizing) {
+ const newW = Math.max(10, pos.x - en.x);
+ const newH = Math.max(10, pos.y - en.y);
+ onStateChange({ ...state, entrance: { ...en, width: newW, height: newH } });
+ } else {
+ onStateChange({
+ ...state,
+ entrance: { ...en, x: pos.x - drag.offsetX, y: pos.y - drag.offsetY },
+ });
+ }
+ return;
+ }
const updated = state.furniture.map((f) => {
if (f.id !== drag.id) return f;
- if (drag.resizing && drag.edge === 'se') {
- return {
- ...f,
- shape: {
- ...f.shape,
- width: Math.max(MIN_SIZE, pos.x - f.shape.x),
- height: Math.max(MIN_SIZE, pos.y - f.shape.y),
- },
- };
- }
return {
...f,
shape: {
@@ -126,6 +144,10 @@ export function RoomCanvas({ state, onStateChange, mode, selectedId, onSelect }:
};
const handleMouseUp = () => {
+ if (drag && drag.id !== '__entrance__') {
+ const updated = state.furniture.map((f) => (f.id === drag.id ? { ...f, placed: true } : f));
+ onStateChange({ ...state, furniture: updated });
+ }
setDrag(null);
};
diff --git a/src/types/index.ts b/src/types/index.ts
index c7d600f..63d1a06 100644
--- a/src/types/index.ts
+++ b/src/types/index.ts
@@ -40,6 +40,7 @@ export interface ConditionSpec {
export interface FurnitureConfig {
id: string;
name: string;
+ type?: string;
shape: ShapeData;
conditions: ConditionSpec[];
color?: string;
@@ -102,4 +103,5 @@ export interface LayoutState {
furniture: FurnitureConfig[];
presets?: FurniturePreset[];
objectCount?: number;
+ objectAmounts?: Record;
}