@pascal-app/mcp 1.0.0-beta.5 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/bridge/scene-bridge.d.ts.map +1 -1
- package/dist/bridge/scene-bridge.js +2 -2
- package/dist/server.d.ts +11 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +52 -0
- package/dist/tools/annotations.d.ts +28 -0
- package/dist/tools/annotations.d.ts.map +1 -0
- package/dist/tools/annotations.js +27 -0
- package/dist/tools/apply-patch.d.ts +7 -0
- package/dist/tools/apply-patch.d.ts.map +1 -1
- package/dist/tools/apply-patch.js +6 -2
- package/dist/tools/check-collisions.d.ts +59 -0
- package/dist/tools/check-collisions.d.ts.map +1 -1
- package/dist/tools/check-collisions.js +240 -19
- package/dist/tools/construction-tools.d.ts +23 -1
- package/dist/tools/construction-tools.d.ts.map +1 -1
- package/dist/tools/construction-tools.js +34 -8
- package/dist/tools/create-level.d.ts +7 -0
- package/dist/tools/create-level.d.ts.map +1 -1
- package/dist/tools/create-level.js +6 -3
- package/dist/tools/create-wall.d.ts +7 -0
- package/dist/tools/create-wall.d.ts.map +1 -1
- package/dist/tools/create-wall.js +6 -3
- package/dist/tools/cut-opening.d.ts +7 -0
- package/dist/tools/cut-opening.d.ts.map +1 -1
- package/dist/tools/cut-opening.js +6 -3
- package/dist/tools/delete-node.d.ts +7 -0
- package/dist/tools/delete-node.d.ts.map +1 -1
- package/dist/tools/delete-node.js +6 -3
- package/dist/tools/describe-node.d.ts.map +1 -1
- package/dist/tools/describe-node.js +2 -0
- package/dist/tools/door-clearance.d.ts +18 -2
- package/dist/tools/door-clearance.d.ts.map +1 -1
- package/dist/tools/door-clearance.js +101 -7
- package/dist/tools/duplicate-level.d.ts +7 -0
- package/dist/tools/duplicate-level.d.ts.map +1 -1
- package/dist/tools/duplicate-level.js +6 -2
- package/dist/tools/export-glb.d.ts.map +1 -1
- package/dist/tools/export-glb.js +4 -2
- package/dist/tools/export-json.d.ts.map +1 -1
- package/dist/tools/export-json.js +2 -0
- package/dist/tools/find-nodes.d.ts.map +1 -1
- package/dist/tools/find-nodes.js +2 -0
- package/dist/tools/get-node.d.ts.map +1 -1
- package/dist/tools/get-node.js +2 -0
- package/dist/tools/get-scene.d.ts.map +1 -1
- package/dist/tools/get-scene.js +2 -0
- package/dist/tools/layout-clearance.d.ts +2 -0
- package/dist/tools/layout-clearance.d.ts.map +1 -1
- package/dist/tools/layout-clearance.js +5 -1
- package/dist/tools/live-sync.d.ts +33 -3
- package/dist/tools/live-sync.d.ts.map +1 -1
- package/dist/tools/live-sync.js +36 -4
- package/dist/tools/measure.d.ts +1 -0
- package/dist/tools/measure.d.ts.map +1 -1
- package/dist/tools/measure.js +4 -0
- package/dist/tools/photo-to-scene/photo-to-scene.d.ts.map +1 -1
- package/dist/tools/photo-to-scene/photo-to-scene.js +2 -0
- package/dist/tools/place-item.d.ts +7 -0
- package/dist/tools/place-item.d.ts.map +1 -1
- package/dist/tools/place-item.js +6 -2
- package/dist/tools/redo.d.ts +7 -0
- package/dist/tools/redo.d.ts.map +1 -1
- package/dist/tools/redo.js +6 -4
- package/dist/tools/room-tools.d.ts +28 -0
- package/dist/tools/room-tools.d.ts.map +1 -1
- package/dist/tools/room-tools.js +20 -5
- package/dist/tools/scene-lifecycle/create-project.d.ts.map +1 -1
- package/dist/tools/scene-lifecycle/create-project.js +2 -0
- package/dist/tools/scene-lifecycle/delete-scene.d.ts.map +1 -1
- package/dist/tools/scene-lifecycle/delete-scene.js +2 -0
- package/dist/tools/scene-lifecycle/get-project-status.d.ts.map +1 -1
- package/dist/tools/scene-lifecycle/get-project-status.js +2 -0
- package/dist/tools/scene-lifecycle/list-scenes.d.ts.map +1 -1
- package/dist/tools/scene-lifecycle/list-scenes.js +2 -0
- package/dist/tools/scene-lifecycle/load-scene.d.ts.map +1 -1
- package/dist/tools/scene-lifecycle/load-scene.js +2 -0
- package/dist/tools/scene-lifecycle/rename-scene.d.ts.map +1 -1
- package/dist/tools/scene-lifecycle/rename-scene.js +2 -0
- package/dist/tools/scene-lifecycle/save-scene.d.ts.map +1 -1
- package/dist/tools/scene-lifecycle/save-scene.js +2 -0
- package/dist/tools/scene-lifecycle/test-utils.d.ts +5 -1
- package/dist/tools/scene-lifecycle/test-utils.d.ts.map +1 -1
- package/dist/tools/scene-lifecycle/test-utils.js +24 -0
- package/dist/tools/scene-query.d.ts.map +1 -1
- package/dist/tools/scene-query.js +6 -0
- package/dist/tools/set-zone.d.ts +7 -0
- package/dist/tools/set-zone.d.ts.map +1 -1
- package/dist/tools/set-zone.js +6 -3
- package/dist/tools/templates/create-from-template.d.ts.map +1 -1
- package/dist/tools/templates/create-from-template.js +2 -0
- package/dist/tools/templates/create-house-from-brief.d.ts.map +1 -1
- package/dist/tools/templates/create-house-from-brief.js +2 -0
- package/dist/tools/templates/list-templates.d.ts.map +1 -1
- package/dist/tools/templates/list-templates.js +2 -0
- package/dist/tools/undo.d.ts +7 -0
- package/dist/tools/undo.d.ts.map +1 -1
- package/dist/tools/undo.js +6 -4
- package/dist/tools/validate-scene.d.ts.map +1 -1
- package/dist/tools/validate-scene.js +2 -0
- package/dist/tools/variants/generate-variants.d.ts.map +1 -1
- package/dist/tools/variants/generate-variants.js +2 -0
- package/dist/tools/vision/analyze-floorplan-image.d.ts.map +1 -1
- package/dist/tools/vision/analyze-floorplan-image.js +2 -0
- package/dist/tools/vision/analyze-room-photo.d.ts.map +1 -1
- package/dist/tools/vision/analyze-room-photo.js +2 -0
- package/package.json +5 -5
|
@@ -16,6 +16,13 @@ export declare const createStoryShellInput: {
|
|
|
16
16
|
ceilingMaterialPreset: z.ZodOptional<z.ZodString>;
|
|
17
17
|
};
|
|
18
18
|
export declare const createStoryShellOutput: {
|
|
19
|
+
persistence: z.ZodOptional<z.ZodObject<{
|
|
20
|
+
status: z.ZodEnum<{
|
|
21
|
+
unbound: "unbound";
|
|
22
|
+
events_unsupported: "events_unsupported";
|
|
23
|
+
}>;
|
|
24
|
+
warning: z.ZodString;
|
|
25
|
+
}, z.core.$strip>>;
|
|
19
26
|
levelId: z.ZodString;
|
|
20
27
|
wallIds: z.ZodArray<z.ZodString>;
|
|
21
28
|
slabId: z.ZodNullable<z.ZodString>;
|
|
@@ -33,13 +40,14 @@ export declare const createRoofInput: {
|
|
|
33
40
|
width: z.ZodPipe<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>, z.ZodTransform<number, string | number>>;
|
|
34
41
|
depth: z.ZodPipe<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>, z.ZodTransform<number, string | number>>;
|
|
35
42
|
roofType: z.ZodDefault<z.ZodEnum<{
|
|
43
|
+
gable: "gable";
|
|
36
44
|
flat: "flat";
|
|
37
45
|
hip: "hip";
|
|
38
|
-
gable: "gable";
|
|
39
46
|
shed: "shed";
|
|
40
47
|
gambrel: "gambrel";
|
|
41
48
|
dutch: "dutch";
|
|
42
49
|
mansard: "mansard";
|
|
50
|
+
conical: "conical";
|
|
43
51
|
}>>;
|
|
44
52
|
pitch: z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>, z.ZodTransform<number, string | number>>>;
|
|
45
53
|
wallHeight: z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>, z.ZodTransform<number, string | number>>>;
|
|
@@ -49,6 +57,13 @@ export declare const createRoofInput: {
|
|
|
49
57
|
name: z.ZodOptional<z.ZodString>;
|
|
50
58
|
};
|
|
51
59
|
export declare const createRoofOutput: {
|
|
60
|
+
persistence: z.ZodOptional<z.ZodObject<{
|
|
61
|
+
status: z.ZodEnum<{
|
|
62
|
+
unbound: "unbound";
|
|
63
|
+
events_unsupported: "events_unsupported";
|
|
64
|
+
}>;
|
|
65
|
+
warning: z.ZodString;
|
|
66
|
+
}, z.core.$strip>>;
|
|
52
67
|
referenceLevelId: z.ZodString;
|
|
53
68
|
roofLevelId: z.ZodString;
|
|
54
69
|
createdRoofLevelId: z.ZodNullable<z.ZodString>;
|
|
@@ -83,6 +98,13 @@ export declare const createStairBetweenLevelsInput: {
|
|
|
83
98
|
name: z.ZodOptional<z.ZodString>;
|
|
84
99
|
};
|
|
85
100
|
export declare const createStairBetweenLevelsOutput: {
|
|
101
|
+
persistence: z.ZodOptional<z.ZodObject<{
|
|
102
|
+
status: z.ZodEnum<{
|
|
103
|
+
unbound: "unbound";
|
|
104
|
+
events_unsupported: "events_unsupported";
|
|
105
|
+
}>;
|
|
106
|
+
warning: z.ZodString;
|
|
107
|
+
}, z.core.$strip>>;
|
|
86
108
|
stairId: z.ZodString;
|
|
87
109
|
stairSegmentId: z.ZodString;
|
|
88
110
|
destinationSlabId: z.ZodNullable<z.ZodString>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"construction-tools.d.ts","sourceRoot":"","sources":["../../src/tools/construction-tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAcxE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"construction-tools.d.ts","sourceRoot":"","sources":["../../src/tools/construction-tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAcxE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAkBpD,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;CAsBjC,CAAA;AAED,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;CAOlC,CAAA;AAED,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;CA0B3B,CAAA;AAED,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;CAO5B,CAAA;AAED,eAAO,MAAM,6BAA6B;;;;;;;;;;;;;;;;;;;;;;;;;;CAiCzC,CAAA;AAED,eAAO,MAAM,8BAA8B;;;;;;;;;;;;;CAO1C,CAAA;AA4GD,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAgV1F"}
|
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
import { resolveStairTotalRise } from '@pascal-app/core';
|
|
2
2
|
import { CeilingNode, getActiveRoofHeight, LevelNode, RoofNode, RoofSegmentNode, SlabNode, StairNode, StairSegmentNode, WallNode, } from '@pascal-app/core/schema';
|
|
3
3
|
import { z } from 'zod';
|
|
4
|
-
import {
|
|
4
|
+
import { ADDITIVE_TOOL_ANNOTATIONS, DESTRUCTIVE_TOOL_ANNOTATIONS } from './annotations';
|
|
5
|
+
import { liveSyncOutput, persistencePayload, publishLiveSceneSnapshot } from './live-sync';
|
|
5
6
|
import { measurement } from './measurement';
|
|
6
7
|
import { NodeIdSchema, Vec2Schema, Vec3Schema } from './schemas';
|
|
7
|
-
const ROOF_TYPES = [
|
|
8
|
+
const ROOF_TYPES = [
|
|
9
|
+
'hip',
|
|
10
|
+
'gable',
|
|
11
|
+
'shed',
|
|
12
|
+
'gambrel',
|
|
13
|
+
'dutch',
|
|
14
|
+
'mansard',
|
|
15
|
+
'flat',
|
|
16
|
+
'conical',
|
|
17
|
+
];
|
|
8
18
|
const RAILING_MODES = ['none', 'left', 'right', 'both'];
|
|
9
19
|
export const createStoryShellInput = {
|
|
10
20
|
levelId: NodeIdSchema,
|
|
@@ -35,6 +45,7 @@ export const createStoryShellOutput = {
|
|
|
35
45
|
slabId: z.string().nullable(),
|
|
36
46
|
ceilingId: z.string().nullable(),
|
|
37
47
|
createdIds: z.array(z.string()),
|
|
48
|
+
...liveSyncOutput,
|
|
38
49
|
};
|
|
39
50
|
export const createRoofInput = {
|
|
40
51
|
levelId: NodeIdSchema,
|
|
@@ -67,6 +78,7 @@ export const createRoofOutput = {
|
|
|
67
78
|
createdRoofLevelId: z.string().nullable(),
|
|
68
79
|
roofId: z.string(),
|
|
69
80
|
roofSegmentId: z.string(),
|
|
81
|
+
...liveSyncOutput,
|
|
70
82
|
};
|
|
71
83
|
export const createStairBetweenLevelsInput = {
|
|
72
84
|
fromLevelId: NodeIdSchema,
|
|
@@ -108,6 +120,7 @@ export const createStairBetweenLevelsOutput = {
|
|
|
108
120
|
destinationSlabId: z.string().nullable(),
|
|
109
121
|
sourceCeilingId: z.string().nullable(),
|
|
110
122
|
openingPolygon: z.array(Vec2Schema),
|
|
123
|
+
...liveSyncOutput,
|
|
111
124
|
};
|
|
112
125
|
function textResult(payload) {
|
|
113
126
|
return {
|
|
@@ -190,6 +203,7 @@ export function registerConstructionTools(server, bridge) {
|
|
|
190
203
|
description: 'Create one level-owned building shell from a footprint: perimeter walls plus optional slab and ceiling. Use once per story; do not make first-floor walls span multiple stories.',
|
|
191
204
|
inputSchema: createStoryShellInput,
|
|
192
205
|
outputSchema: createStoryShellOutput,
|
|
206
|
+
annotations: ADDITIVE_TOOL_ANNOTATIONS,
|
|
193
207
|
}, async ({ levelId, footprint, wallHeight, wallThickness, createSlab, createCeiling, slabElevation, ceilingHeight, namePrefix, wallMaterialPreset, slabMaterialPreset, ceilingMaterialPreset, }) => {
|
|
194
208
|
const level = assertNode(bridge, levelId, 'level');
|
|
195
209
|
if (isRoofLevel(level)) {
|
|
@@ -246,13 +260,14 @@ export function registerConstructionTools(server, bridge) {
|
|
|
246
260
|
patches.push({ op: 'create', node: ceiling, parentId: levelId });
|
|
247
261
|
}
|
|
248
262
|
const result = bridge.applyPatch(patches);
|
|
249
|
-
await publishLiveSceneSnapshot(bridge, 'create_story_shell');
|
|
263
|
+
const persistence = await publishLiveSceneSnapshot(bridge, 'create_story_shell');
|
|
250
264
|
return textResult({
|
|
251
265
|
levelId,
|
|
252
266
|
wallIds,
|
|
253
267
|
slabId,
|
|
254
268
|
ceilingId,
|
|
255
269
|
createdIds: result.createdIds,
|
|
270
|
+
...persistencePayload(persistence),
|
|
256
271
|
});
|
|
257
272
|
});
|
|
258
273
|
server.registerTool('create_roof', {
|
|
@@ -260,10 +275,18 @@ export function registerConstructionTools(server, bridge) {
|
|
|
260
275
|
description: 'Create a roof container with one roof segment. By default creates a dedicated roof level above the reference level so exploded/solo level views can isolate the roof.',
|
|
261
276
|
inputSchema: createRoofInput,
|
|
262
277
|
outputSchema: createRoofOutput,
|
|
278
|
+
annotations: ADDITIVE_TOOL_ANNOTATIONS,
|
|
263
279
|
}, async ({ levelId, roofLevelId, useDedicatedRoofLevel, roofLevelLabel, roofLevelElevation, roofLevelHeight, center, width, depth, roofType, pitch, wallHeight, wallThickness, overhang, materialPreset, name, }) => {
|
|
280
|
+
const effectiveWidth = roofType === 'conical' ? Math.max(width, depth) : width;
|
|
281
|
+
const effectiveDepth = roofType === 'conical' ? effectiveWidth : depth;
|
|
264
282
|
// Peak height is derived from pitch + footprint + type; we still
|
|
265
283
|
// need it to size the auto-generated roof level container below.
|
|
266
|
-
const peakHeight = getActiveRoofHeight({
|
|
284
|
+
const peakHeight = getActiveRoofHeight({
|
|
285
|
+
roofType,
|
|
286
|
+
pitch,
|
|
287
|
+
width: effectiveWidth,
|
|
288
|
+
depth: effectiveDepth,
|
|
289
|
+
});
|
|
267
290
|
const referenceLevel = assertNode(bridge, levelId, 'level');
|
|
268
291
|
const patches = [];
|
|
269
292
|
let targetRoofLevelId = levelId;
|
|
@@ -294,8 +317,8 @@ export function registerConstructionTools(server, bridge) {
|
|
|
294
317
|
}
|
|
295
318
|
const segment = RoofSegmentNode.parse({
|
|
296
319
|
roofType,
|
|
297
|
-
width,
|
|
298
|
-
depth,
|
|
320
|
+
width: effectiveWidth,
|
|
321
|
+
depth: effectiveDepth,
|
|
299
322
|
wallHeight,
|
|
300
323
|
pitch,
|
|
301
324
|
wallThickness,
|
|
@@ -317,13 +340,14 @@ export function registerConstructionTools(server, bridge) {
|
|
|
317
340
|
{ op: 'create', node: roof, parentId: targetRoofLevelId },
|
|
318
341
|
{ op: 'create', node: segment, parentId: roof.id },
|
|
319
342
|
]);
|
|
320
|
-
await publishLiveSceneSnapshot(bridge, 'create_roof');
|
|
343
|
+
const persistence = await publishLiveSceneSnapshot(bridge, 'create_roof');
|
|
321
344
|
return textResult({
|
|
322
345
|
referenceLevelId: levelId,
|
|
323
346
|
roofLevelId: targetRoofLevelId,
|
|
324
347
|
createdRoofLevelId,
|
|
325
348
|
roofId: roof.id,
|
|
326
349
|
roofSegmentId: segment.id,
|
|
350
|
+
...persistencePayload(persistence),
|
|
327
351
|
});
|
|
328
352
|
});
|
|
329
353
|
server.registerTool('create_stair_between_levels', {
|
|
@@ -331,6 +355,7 @@ export function registerConstructionTools(server, bridge) {
|
|
|
331
355
|
description: 'Create a straight stair and a single rectangular manual opening in the destination slab/source ceiling. This disables stair auto-opening mode to avoid duplicate or irregular holes.',
|
|
332
356
|
inputSchema: createStairBetweenLevelsInput,
|
|
333
357
|
outputSchema: createStairBetweenLevelsOutput,
|
|
358
|
+
annotations: DESTRUCTIVE_TOOL_ANNOTATIONS,
|
|
334
359
|
}, async ({ fromLevelId, toLevelId, position, rotation, width, runLength, totalRise, stepCount, railingMode, destinationSlabId, sourceCeilingId, createDestinationSlabOpening, createSourceCeilingOpening, openingWidth, openingLength, openingOffset, openingCenter, openingRotation, materialPreset, name, }) => {
|
|
335
360
|
const fromLevel = assertNode(bridge, fromLevelId, 'level');
|
|
336
361
|
const toLevel = assertNode(bridge, toLevelId, 'level');
|
|
@@ -408,13 +433,14 @@ export function registerConstructionTools(server, bridge) {
|
|
|
408
433
|
});
|
|
409
434
|
}
|
|
410
435
|
bridge.applyPatch(patches);
|
|
411
|
-
await publishLiveSceneSnapshot(bridge, 'create_stair_between_levels');
|
|
436
|
+
const persistence = await publishLiveSceneSnapshot(bridge, 'create_stair_between_levels');
|
|
412
437
|
return textResult({
|
|
413
438
|
stairId: stair.id,
|
|
414
439
|
stairSegmentId: segment.id,
|
|
415
440
|
destinationSlabId: destinationSlab?.id ?? null,
|
|
416
441
|
sourceCeilingId: sourceCeiling?.id ?? null,
|
|
417
442
|
openingPolygon,
|
|
443
|
+
...persistencePayload(persistence),
|
|
418
444
|
});
|
|
419
445
|
});
|
|
420
446
|
}
|
|
@@ -8,6 +8,13 @@ export declare const createLevelInput: {
|
|
|
8
8
|
label: z.ZodOptional<z.ZodString>;
|
|
9
9
|
};
|
|
10
10
|
export declare const createLevelOutput: {
|
|
11
|
+
persistence: z.ZodOptional<z.ZodObject<{
|
|
12
|
+
status: z.ZodEnum<{
|
|
13
|
+
unbound: "unbound";
|
|
14
|
+
events_unsupported: "events_unsupported";
|
|
15
|
+
}>;
|
|
16
|
+
warning: z.ZodString;
|
|
17
|
+
}, z.core.$strip>>;
|
|
11
18
|
levelId: z.ZodString;
|
|
12
19
|
};
|
|
13
20
|
export declare function registerCreateLevel(server: McpServer, bridge: SceneOperations): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-level.d.ts","sourceRoot":"","sources":["../../src/tools/create-level.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAIxE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"create-level.d.ts","sourceRoot":"","sources":["../../src/tools/create-level.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAIxE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAOpD,eAAO,MAAM,gBAAgB;;;;;CAW5B,CAAA;AAED,eAAO,MAAM,iBAAiB;;;;;;;;;CAG7B,CAAA;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAgDpF"}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { DEFAULT_LEVEL_HEIGHT } from '@pascal-app/core';
|
|
2
2
|
import { LevelNode } from '@pascal-app/core/schema';
|
|
3
3
|
import { z } from 'zod';
|
|
4
|
+
import { ADDITIVE_TOOL_ANNOTATIONS } from './annotations';
|
|
4
5
|
import { ErrorCode, throwMcpError } from './errors';
|
|
5
|
-
import { publishLiveSceneSnapshot } from './live-sync';
|
|
6
|
+
import { liveSyncOutput, persistencePayload, publishLiveSceneSnapshot } from './live-sync';
|
|
6
7
|
import { measurement } from './measurement';
|
|
7
8
|
import { NodeIdSchema } from './schemas';
|
|
8
9
|
export const createLevelInput = {
|
|
@@ -19,6 +20,7 @@ export const createLevelInput = {
|
|
|
19
20
|
};
|
|
20
21
|
export const createLevelOutput = {
|
|
21
22
|
levelId: z.string(),
|
|
23
|
+
...liveSyncOutput,
|
|
22
24
|
};
|
|
23
25
|
export function registerCreateLevel(server, bridge) {
|
|
24
26
|
server.registerTool('create_level', {
|
|
@@ -26,6 +28,7 @@ export function registerCreateLevel(server, bridge) {
|
|
|
26
28
|
description: "Append a new level above the given building's current top level. height is stored as the level's floor-to-floor storey height.",
|
|
27
29
|
inputSchema: createLevelInput,
|
|
28
30
|
outputSchema: createLevelOutput,
|
|
31
|
+
annotations: ADDITIVE_TOOL_ANNOTATIONS,
|
|
29
32
|
}, async ({ buildingId, height, label }) => {
|
|
30
33
|
const parent = bridge.getNode(buildingId);
|
|
31
34
|
if (!parent) {
|
|
@@ -50,8 +53,8 @@ export function registerCreateLevel(server, bridge) {
|
|
|
50
53
|
...(label !== undefined ? { name: label } : {}),
|
|
51
54
|
});
|
|
52
55
|
const id = bridge.createNode(levelNode, buildingId);
|
|
53
|
-
await publishLiveSceneSnapshot(bridge, 'create_level');
|
|
54
|
-
const payload = { levelId: id };
|
|
56
|
+
const persistence = await publishLiveSceneSnapshot(bridge, 'create_level');
|
|
57
|
+
const payload = { levelId: id, ...persistencePayload(persistence) };
|
|
55
58
|
return {
|
|
56
59
|
content: [{ type: 'text', text: JSON.stringify(payload) }],
|
|
57
60
|
structuredContent: payload,
|
|
@@ -9,6 +9,13 @@ export declare const createWallInput: {
|
|
|
9
9
|
height: z.ZodOptional<z.ZodPipe<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>, z.ZodTransform<number, string | number>>>;
|
|
10
10
|
};
|
|
11
11
|
export declare const createWallOutput: {
|
|
12
|
+
persistence: z.ZodOptional<z.ZodObject<{
|
|
13
|
+
status: z.ZodEnum<{
|
|
14
|
+
unbound: "unbound";
|
|
15
|
+
events_unsupported: "events_unsupported";
|
|
16
|
+
}>;
|
|
17
|
+
warning: z.ZodString;
|
|
18
|
+
}, z.core.$strip>>;
|
|
12
19
|
wallId: z.ZodString;
|
|
13
20
|
};
|
|
14
21
|
export declare function registerCreateWall(server: McpServer, bridge: SceneOperations): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-wall.d.ts","sourceRoot":"","sources":["../../src/tools/create-wall.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAGxE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"create-wall.d.ts","sourceRoot":"","sources":["../../src/tools/create-wall.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAGxE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAOpD,eAAO,MAAM,eAAe;;;;;;CAS3B,CAAA;AAED,eAAO,MAAM,gBAAgB;;;;;;;;;CAG5B,CAAA;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAiDnF"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { WallNode } from '@pascal-app/core/schema';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
+
import { ADDITIVE_TOOL_ANNOTATIONS } from './annotations';
|
|
3
4
|
import { ErrorCode, throwMcpError } from './errors';
|
|
4
|
-
import { publishLiveSceneSnapshot } from './live-sync';
|
|
5
|
+
import { liveSyncOutput, persistencePayload, publishLiveSceneSnapshot } from './live-sync';
|
|
5
6
|
import { measurement } from './measurement';
|
|
6
7
|
import { NodeIdSchema, Vec2Schema } from './schemas';
|
|
7
8
|
export const createWallInput = {
|
|
@@ -16,6 +17,7 @@ export const createWallInput = {
|
|
|
16
17
|
};
|
|
17
18
|
export const createWallOutput = {
|
|
18
19
|
wallId: z.string(),
|
|
20
|
+
...liveSyncOutput,
|
|
19
21
|
};
|
|
20
22
|
export function registerCreateWall(server, bridge) {
|
|
21
23
|
server.registerTool('create_wall', {
|
|
@@ -23,6 +25,7 @@ export function registerCreateWall(server, bridge) {
|
|
|
23
25
|
description: 'Create a new wall on the given level between two 2D points. Thickness and height default to the core library defaults when omitted.',
|
|
24
26
|
inputSchema: createWallInput,
|
|
25
27
|
outputSchema: createWallOutput,
|
|
28
|
+
annotations: ADDITIVE_TOOL_ANNOTATIONS,
|
|
26
29
|
}, async ({ levelId, start, end, thickness, height }) => {
|
|
27
30
|
const parent = bridge.getNode(levelId);
|
|
28
31
|
if (!parent) {
|
|
@@ -44,8 +47,8 @@ export function registerCreateWall(server, bridge) {
|
|
|
44
47
|
...(height !== undefined ? { height } : {}),
|
|
45
48
|
});
|
|
46
49
|
const id = bridge.createNode(wall, levelId);
|
|
47
|
-
await publishLiveSceneSnapshot(bridge, 'create_wall');
|
|
48
|
-
const payload = { wallId: id };
|
|
50
|
+
const persistence = await publishLiveSceneSnapshot(bridge, 'create_wall');
|
|
51
|
+
const payload = { wallId: id, ...persistencePayload(persistence) };
|
|
49
52
|
return {
|
|
50
53
|
content: [{ type: 'text', text: JSON.stringify(payload) }],
|
|
51
54
|
structuredContent: payload,
|
|
@@ -12,6 +12,13 @@ export declare const cutOpeningInput: {
|
|
|
12
12
|
height: z.ZodPipe<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>, z.ZodTransform<number, string | number>>;
|
|
13
13
|
};
|
|
14
14
|
export declare const cutOpeningOutput: {
|
|
15
|
+
persistence: z.ZodOptional<z.ZodObject<{
|
|
16
|
+
status: z.ZodEnum<{
|
|
17
|
+
unbound: "unbound";
|
|
18
|
+
events_unsupported: "events_unsupported";
|
|
19
|
+
}>;
|
|
20
|
+
warning: z.ZodString;
|
|
21
|
+
}, z.core.$strip>>;
|
|
15
22
|
openingId: z.ZodString;
|
|
16
23
|
};
|
|
17
24
|
export declare function registerCutOpening(server: McpServer, bridge: SceneOperations): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cut-opening.d.ts","sourceRoot":"","sources":["../../src/tools/cut-opening.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAGxE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"cut-opening.d.ts","sourceRoot":"","sources":["../../src/tools/cut-opening.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAGxE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAQpD,eAAO,MAAM,eAAe;;;;;;;;;CAM3B,CAAA;AAED,eAAO,MAAM,gBAAgB;;;;;;;;;CAG5B,CAAA;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CA0DnF"}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { DoorNode, WindowNode } from '@pascal-app/core/schema';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
+
import { ADDITIVE_TOOL_ANNOTATIONS } from './annotations';
|
|
3
4
|
import { ErrorCode, throwMcpError } from './errors';
|
|
4
5
|
import { wallLength, wallLocalXFromT } from './geometry';
|
|
5
|
-
import { publishLiveSceneSnapshot } from './live-sync';
|
|
6
|
+
import { liveSyncOutput, persistencePayload, publishLiveSceneSnapshot } from './live-sync';
|
|
6
7
|
import { measurement } from './measurement';
|
|
7
8
|
import { NodeIdSchema } from './schemas';
|
|
8
9
|
export const cutOpeningInput = {
|
|
@@ -14,6 +15,7 @@ export const cutOpeningInput = {
|
|
|
14
15
|
};
|
|
15
16
|
export const cutOpeningOutput = {
|
|
16
17
|
openingId: z.string(),
|
|
18
|
+
...liveSyncOutput,
|
|
17
19
|
};
|
|
18
20
|
export function registerCutOpening(server, bridge) {
|
|
19
21
|
server.registerTool('cut_opening', {
|
|
@@ -21,6 +23,7 @@ export function registerCutOpening(server, bridge) {
|
|
|
21
23
|
description: 'Cut a door or window opening into an existing wall. position is a parametric 0..1 offset along the wall centreline.',
|
|
22
24
|
inputSchema: cutOpeningInput,
|
|
23
25
|
outputSchema: cutOpeningOutput,
|
|
26
|
+
annotations: ADDITIVE_TOOL_ANNOTATIONS,
|
|
24
27
|
}, async ({ wallId, type, position, width, height }) => {
|
|
25
28
|
const wall = bridge.getNode(wallId);
|
|
26
29
|
if (!wall) {
|
|
@@ -48,8 +51,8 @@ export function registerCutOpening(server, bridge) {
|
|
|
48
51
|
position: [base.position[0], 0.9 + height / 2, 0],
|
|
49
52
|
});
|
|
50
53
|
const id = bridge.createNode(opening, wallId);
|
|
51
|
-
await publishLiveSceneSnapshot(bridge, 'cut_opening');
|
|
52
|
-
const payload = { openingId: id };
|
|
54
|
+
const persistence = await publishLiveSceneSnapshot(bridge, 'cut_opening');
|
|
55
|
+
const payload = { openingId: id, ...persistencePayload(persistence) };
|
|
53
56
|
return {
|
|
54
57
|
content: [{ type: 'text', text: JSON.stringify(payload) }],
|
|
55
58
|
structuredContent: payload,
|
|
@@ -6,6 +6,13 @@ export declare const deleteNodeInput: {
|
|
|
6
6
|
cascade: z.ZodOptional<z.ZodBoolean>;
|
|
7
7
|
};
|
|
8
8
|
export declare const deleteNodeOutput: {
|
|
9
|
+
persistence: z.ZodOptional<z.ZodObject<{
|
|
10
|
+
status: z.ZodEnum<{
|
|
11
|
+
unbound: "unbound";
|
|
12
|
+
events_unsupported: "events_unsupported";
|
|
13
|
+
}>;
|
|
14
|
+
warning: z.ZodString;
|
|
15
|
+
}, z.core.$strip>>;
|
|
9
16
|
deletedIds: z.ZodArray<z.ZodString>;
|
|
10
17
|
};
|
|
11
18
|
export declare function registerDeleteNode(server: McpServer, bridge: SceneOperations): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"delete-node.d.ts","sourceRoot":"","sources":["../../src/tools/delete-node.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAExE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"delete-node.d.ts","sourceRoot":"","sources":["../../src/tools/delete-node.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAExE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAMpD,eAAO,MAAM,eAAe;;;CAG3B,CAAA;AAED,eAAO,MAAM,gBAAgB;;;;;;;;;CAG5B,CAAA;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CA8BnF"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { DESTRUCTIVE_TOOL_ANNOTATIONS } from './annotations';
|
|
2
3
|
import { ErrorCode, throwMcpError } from './errors';
|
|
3
|
-
import { publishLiveSceneSnapshot } from './live-sync';
|
|
4
|
+
import { liveSyncOutput, persistencePayload, publishLiveSceneSnapshot } from './live-sync';
|
|
4
5
|
import { NodeIdSchema } from './schemas';
|
|
5
6
|
export const deleteNodeInput = {
|
|
6
7
|
id: NodeIdSchema,
|
|
@@ -8,6 +9,7 @@ export const deleteNodeInput = {
|
|
|
8
9
|
};
|
|
9
10
|
export const deleteNodeOutput = {
|
|
10
11
|
deletedIds: z.array(z.string()),
|
|
12
|
+
...liveSyncOutput,
|
|
11
13
|
};
|
|
12
14
|
export function registerDeleteNode(server, bridge) {
|
|
13
15
|
server.registerTool('delete_node', {
|
|
@@ -15,6 +17,7 @@ export function registerDeleteNode(server, bridge) {
|
|
|
15
17
|
description: 'Delete a node. If it has children, pass `cascade: true` to delete descendants recursively.',
|
|
16
18
|
inputSchema: deleteNodeInput,
|
|
17
19
|
outputSchema: deleteNodeOutput,
|
|
20
|
+
annotations: DESTRUCTIVE_TOOL_ANNOTATIONS,
|
|
18
21
|
}, async ({ id, cascade }) => {
|
|
19
22
|
const node = bridge.getNode(id);
|
|
20
23
|
if (!node) {
|
|
@@ -22,8 +25,8 @@ export function registerDeleteNode(server, bridge) {
|
|
|
22
25
|
}
|
|
23
26
|
try {
|
|
24
27
|
const removed = bridge.deleteNode(id, cascade ?? false);
|
|
25
|
-
await publishLiveSceneSnapshot(bridge, 'delete_node');
|
|
26
|
-
const payload = { deletedIds: removed };
|
|
28
|
+
const persistence = await publishLiveSceneSnapshot(bridge, 'delete_node');
|
|
29
|
+
const payload = { deletedIds: removed, ...persistencePayload(persistence) };
|
|
27
30
|
return {
|
|
28
31
|
content: [{ type: 'text', text: JSON.stringify(payload) }],
|
|
29
32
|
structuredContent: payload,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"describe-node.d.ts","sourceRoot":"","sources":["../../src/tools/describe-node.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAGxE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"describe-node.d.ts","sourceRoot":"","sources":["../../src/tools/describe-node.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAGxE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAMpD,eAAO,MAAM,iBAAiB;;CAE7B,CAAA;AAED,eAAO,MAAM,kBAAkB;;;;;;;;CAQ9B,CAAA;AA0CD,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAiDrF"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { resolveCeilingHeight } from '@pascal-app/core';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
+
import { READ_ONLY_TOOL_ANNOTATIONS } from './annotations';
|
|
3
4
|
import { ErrorCode, throwMcpError } from './errors';
|
|
4
5
|
import { resolveReportedWallHeight } from './scene-query';
|
|
5
6
|
import { NodeIdSchema } from './schemas';
|
|
@@ -60,6 +61,7 @@ export function registerDescribeNode(server, bridge) {
|
|
|
60
61
|
description: 'Return a structured summary of a node including its ancestry, children IDs, key properties, and a short human description.',
|
|
61
62
|
inputSchema: describeNodeInput,
|
|
62
63
|
outputSchema: describeNodeOutput,
|
|
64
|
+
annotations: READ_ONLY_TOOL_ANNOTATIONS,
|
|
63
65
|
}, async ({ id }) => {
|
|
64
66
|
const node = bridge.getNode(id);
|
|
65
67
|
if (!node) {
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* See docs/layout-clearance-error-log.md for pitfalls (levels, gap sign, scale).
|
|
8
8
|
*/
|
|
9
|
-
import type
|
|
9
|
+
import { type AnyNode } from '@pascal-app/core/schema';
|
|
10
10
|
import { type Vec2 } from './geometry';
|
|
11
11
|
export type PlanAabb = {
|
|
12
12
|
minX: number;
|
|
@@ -14,6 +14,17 @@ export type PlanAabb = {
|
|
|
14
14
|
minZ: number;
|
|
15
15
|
maxZ: number;
|
|
16
16
|
};
|
|
17
|
+
export type ItemFootprintFailureReason = 'missing_dimensions' | 'non_finite_position' | 'non_finite_rotation' | 'non_finite_dimensions' | 'non_positive_plan_dimensions' | 'non_finite_scale' | 'zero_plan_scale' | 'non_planar_rotation' | 'unsupported_attachment';
|
|
18
|
+
export type ItemFootprintInspection = {
|
|
19
|
+
ok: true;
|
|
20
|
+
aabb: PlanAabb;
|
|
21
|
+
sourceDimensions: [number, number, number];
|
|
22
|
+
effectiveDimensions: [number, number, number];
|
|
23
|
+
rotationY: number;
|
|
24
|
+
} | {
|
|
25
|
+
ok: false;
|
|
26
|
+
reason: ItemFootprintFailureReason;
|
|
27
|
+
};
|
|
17
28
|
export type DoorKeepout = {
|
|
18
29
|
doorId: string;
|
|
19
30
|
wallId: string;
|
|
@@ -63,8 +74,13 @@ export declare function resolveNodeLevelId(nodeId: string, byId: Map<string, Any
|
|
|
63
74
|
* Prefer scaled dimensions when the node is available.
|
|
64
75
|
*/
|
|
65
76
|
export declare function itemPlanAabb(position: [number, number, number] | number[], dimensions: [number, number, number] | number[] | undefined, rotationYRad?: number): PlanAabb;
|
|
66
|
-
/**
|
|
77
|
+
/** Scaled plan footprint used by legacy placement and door-clearance callers. */
|
|
67
78
|
export declare function itemNodePlanAabb(node: AnyNode): PlanAabb | null;
|
|
79
|
+
export declare function inspectItemPlanFootprint(node: Extract<AnyNode, {
|
|
80
|
+
type: 'item';
|
|
81
|
+
}>, options?: {
|
|
82
|
+
floorOnly?: boolean;
|
|
83
|
+
}): ItemFootprintInspection;
|
|
68
84
|
/**
|
|
69
85
|
* Build a rectangular keep-out around a wall door, extruded perpendicular to the wall
|
|
70
86
|
* on both faces so either swing side is protected.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"door-clearance.d.ts","sourceRoot":"","sources":["../../src/tools/door-clearance.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"door-clearance.d.ts","sourceRoot":"","sources":["../../src/tools/door-clearance.ts"],"names":[],"mappings":"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"}
|
|
@@ -34,12 +34,31 @@ export function resolveNodeLevelId(nodeId, byId) {
|
|
|
34
34
|
if (current.type === 'level')
|
|
35
35
|
return current.id;
|
|
36
36
|
const parentId = current.parentId;
|
|
37
|
-
if (
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
if (parentId && byId.has(parentId)) {
|
|
38
|
+
current = byId.get(parentId);
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
current = findParentByChildren(current.id, byId);
|
|
40
42
|
}
|
|
41
43
|
return null;
|
|
42
44
|
}
|
|
45
|
+
function findParentByChildren(nodeId, byId) {
|
|
46
|
+
for (const candidate of byId.values()) {
|
|
47
|
+
if (!('children' in candidate) || !Array.isArray(candidate.children))
|
|
48
|
+
continue;
|
|
49
|
+
const containsNode = candidate.children.some((child) => {
|
|
50
|
+
if (typeof child === 'string')
|
|
51
|
+
return child === nodeId;
|
|
52
|
+
return (child !== null &&
|
|
53
|
+
typeof child === 'object' &&
|
|
54
|
+
'id' in child &&
|
|
55
|
+
child.id === nodeId);
|
|
56
|
+
});
|
|
57
|
+
if (containsNode)
|
|
58
|
+
return candidate;
|
|
59
|
+
}
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
43
62
|
/**
|
|
44
63
|
* Axis-aligned item footprint in plan (x/z), rotation-aware.
|
|
45
64
|
* Prefer scaled dimensions when the node is available.
|
|
@@ -59,13 +78,88 @@ export function itemPlanAabb(position, dimensions, rotationYRad = 0) {
|
|
|
59
78
|
maxZ: z + halfD,
|
|
60
79
|
};
|
|
61
80
|
}
|
|
62
|
-
/**
|
|
81
|
+
/** Scaled plan footprint used by legacy placement and door-clearance callers. */
|
|
63
82
|
export function itemNodePlanAabb(node) {
|
|
64
83
|
if (node.type !== 'item')
|
|
65
84
|
return null;
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
85
|
+
if (!Array.isArray(node.asset.dimensions))
|
|
86
|
+
return null;
|
|
87
|
+
const [width, height, depth] = getScaledDimensions(node);
|
|
88
|
+
const rotationY = Array.isArray(node.rotation) ? (node.rotation[1] ?? 0) : 0;
|
|
89
|
+
if (![node.position[0], node.position[2], width, height, depth, rotationY].every(Number.isFinite)) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
return itemPlanAabb(node.position, [Math.abs(width), Math.abs(height), Math.abs(depth)], rotationY);
|
|
93
|
+
}
|
|
94
|
+
export function inspectItemPlanFootprint(node, options) {
|
|
95
|
+
if (options?.floorOnly &&
|
|
96
|
+
(node.asset.attachTo === 'wall' ||
|
|
97
|
+
node.asset.attachTo === 'wall-side' ||
|
|
98
|
+
node.asset.attachTo === 'ceiling')) {
|
|
99
|
+
return { ok: false, reason: 'unsupported_attachment' };
|
|
100
|
+
}
|
|
101
|
+
const position = node.position;
|
|
102
|
+
if (!Array.isArray(position)) {
|
|
103
|
+
return { ok: false, reason: 'non_finite_position' };
|
|
104
|
+
}
|
|
105
|
+
const x = position[0];
|
|
106
|
+
const y = position[1];
|
|
107
|
+
const z = position[2];
|
|
108
|
+
if (!(Number.isFinite(x) && Number.isFinite(y) && Number.isFinite(z))) {
|
|
109
|
+
return { ok: false, reason: 'non_finite_position' };
|
|
110
|
+
}
|
|
111
|
+
const rotation = Array.isArray(node.rotation) ? node.rotation : [0, 0, 0];
|
|
112
|
+
const rotationX = rotation[0] ?? Number.NaN;
|
|
113
|
+
const rotationY = rotation[1] ?? Number.NaN;
|
|
114
|
+
const rotationZ = rotation[2] ?? Number.NaN;
|
|
115
|
+
if (!(Number.isFinite(rotationX) && Number.isFinite(rotationY) && Number.isFinite(rotationZ))) {
|
|
116
|
+
return { ok: false, reason: 'non_finite_rotation' };
|
|
117
|
+
}
|
|
118
|
+
if (Math.abs(rotationX) > 1e-6 || Math.abs(rotationZ) > 1e-6) {
|
|
119
|
+
return { ok: false, reason: 'non_planar_rotation' };
|
|
120
|
+
}
|
|
121
|
+
const dimensions = node.asset.dimensions;
|
|
122
|
+
if (!Array.isArray(dimensions) || dimensions.length !== 3) {
|
|
123
|
+
return { ok: false, reason: 'missing_dimensions' };
|
|
124
|
+
}
|
|
125
|
+
const width = dimensions[0] ?? Number.NaN;
|
|
126
|
+
const height = dimensions[1] ?? Number.NaN;
|
|
127
|
+
const depth = dimensions[2] ?? Number.NaN;
|
|
128
|
+
if (!(Number.isFinite(width) && Number.isFinite(height) && Number.isFinite(depth))) {
|
|
129
|
+
return { ok: false, reason: 'non_finite_dimensions' };
|
|
130
|
+
}
|
|
131
|
+
if (width <= 0 || depth <= 0) {
|
|
132
|
+
return { ok: false, reason: 'non_positive_plan_dimensions' };
|
|
133
|
+
}
|
|
134
|
+
const scale = Array.isArray(node.scale) ? node.scale : [1, 1, 1];
|
|
135
|
+
const scaleX = scale[0] ?? Number.NaN;
|
|
136
|
+
const scaleY = scale[1] ?? Number.NaN;
|
|
137
|
+
const scaleZ = scale[2] ?? Number.NaN;
|
|
138
|
+
if (!(Number.isFinite(scaleX) && Number.isFinite(scaleY) && Number.isFinite(scaleZ))) {
|
|
139
|
+
return { ok: false, reason: 'non_finite_scale' };
|
|
140
|
+
}
|
|
141
|
+
if (scaleX === 0 || scaleZ === 0) {
|
|
142
|
+
return { ok: false, reason: 'zero_plan_scale' };
|
|
143
|
+
}
|
|
144
|
+
const [scaledWidth, scaledHeight, scaledDepth] = getScaledDimensions(node);
|
|
145
|
+
const effectiveDimensions = [
|
|
146
|
+
Math.abs(scaledWidth),
|
|
147
|
+
Math.abs(scaledHeight),
|
|
148
|
+
Math.abs(scaledDepth),
|
|
149
|
+
];
|
|
150
|
+
if (!effectiveDimensions.every(Number.isFinite)) {
|
|
151
|
+
return { ok: false, reason: 'non_finite_dimensions' };
|
|
152
|
+
}
|
|
153
|
+
if (effectiveDimensions[0] <= 0 || effectiveDimensions[2] <= 0) {
|
|
154
|
+
return { ok: false, reason: 'non_positive_plan_dimensions' };
|
|
155
|
+
}
|
|
156
|
+
return {
|
|
157
|
+
ok: true,
|
|
158
|
+
aabb: itemPlanAabb(node.position, effectiveDimensions, rotationY),
|
|
159
|
+
sourceDimensions: [width, height, depth],
|
|
160
|
+
effectiveDimensions,
|
|
161
|
+
rotationY,
|
|
162
|
+
};
|
|
69
163
|
}
|
|
70
164
|
/**
|
|
71
165
|
* Build a rectangular keep-out around a wall door, extruded perpendicular to the wall
|