@pascal-app/mcp 1.0.3 → 1.0.5
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/CHANGELOG.md +76 -0
- package/README.md +181 -56
- package/dist/bin/pascal-mcp.js +9 -3
- package/dist/bridge/agent-session.d.ts +18 -0
- package/dist/bridge/agent-session.d.ts.map +1 -0
- package/dist/bridge/agent-session.js +15 -0
- package/dist/bridge/index.d.ts +1 -1
- package/dist/bridge/index.d.ts.map +1 -1
- package/dist/bridge/scene-bridge.d.ts +42 -6
- package/dist/bridge/scene-bridge.d.ts.map +1 -1
- package/dist/bridge/scene-bridge.js +232 -45
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/lib/safe-fetch.d.ts +22 -4
- package/dist/lib/safe-fetch.d.ts.map +1 -1
- package/dist/lib/safe-fetch.js +233 -134
- package/dist/operations/scene-operations.d.ts +41 -3
- package/dist/operations/scene-operations.d.ts.map +1 -1
- package/dist/operations/scene-operations.js +42 -1
- package/dist/prompts/from-brief.d.ts +1 -1
- package/dist/prompts/from-brief.d.ts.map +1 -1
- package/dist/prompts/from-brief.js +3 -3
- package/dist/prompts/index.d.ts +1 -1
- package/dist/prompts/index.d.ts.map +1 -1
- package/dist/prompts/iterate-on-feedback.d.ts +1 -1
- package/dist/prompts/iterate-on-feedback.d.ts.map +1 -1
- package/dist/prompts/iterate-on-feedback.js +3 -3
- package/dist/prompts/renovation-from-photos.d.ts +1 -1
- package/dist/prompts/renovation-from-photos.d.ts.map +1 -1
- package/dist/prompts/renovation-from-photos.js +2 -2
- package/dist/prompts/scene-guidance.d.ts.map +1 -1
- package/dist/prompts/scene-guidance.js +13 -6
- package/dist/resources/agent-guide.d.ts +1 -1
- package/dist/resources/agent-guide.d.ts.map +1 -1
- package/dist/resources/agent-guide.js +62 -33
- package/dist/resources/catalog-items.d.ts +5 -6
- package/dist/resources/catalog-items.d.ts.map +1 -1
- package/dist/resources/catalog-items.js +7 -9
- package/dist/resources/constraints.d.ts +1 -1
- package/dist/resources/constraints.d.ts.map +1 -1
- package/dist/resources/constraints.js +1 -1
- package/dist/resources/design-schema.d.ts +9 -0
- package/dist/resources/design-schema.d.ts.map +1 -0
- package/dist/resources/design-schema.js +21 -0
- package/dist/resources/index.d.ts +4 -2
- package/dist/resources/index.d.ts.map +1 -1
- package/dist/resources/index.js +5 -2
- package/dist/resources/scene-current.d.ts +1 -1
- package/dist/resources/scene-current.d.ts.map +1 -1
- package/dist/resources/scene-summary.d.ts +1 -1
- package/dist/resources/scene-summary.d.ts.map +1 -1
- package/dist/server.d.ts +25 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +71 -25
- package/dist/storage/disk-artifact-store.d.ts +9 -0
- package/dist/storage/disk-artifact-store.d.ts.map +1 -0
- package/dist/storage/disk-artifact-store.js +46 -0
- package/dist/storage/index.d.ts +1 -0
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/storage/index.js +1 -0
- package/dist/storage/sqlite-scene-store.d.ts +3 -3
- package/dist/storage/sqlite-scene-store.d.ts.map +1 -1
- package/dist/storage/sqlite-scene-store.js +4 -3
- package/dist/storage/types.d.ts +36 -7
- package/dist/storage/types.d.ts.map +1 -1
- package/dist/storage/types.js +12 -0
- package/dist/templates/empty-studio.d.ts +3 -3
- package/dist/templates/empty-studio.d.ts.map +1 -1
- package/dist/templates/garden-house.d.ts +3 -3
- package/dist/templates/garden-house.d.ts.map +1 -1
- package/dist/templates/index.d.ts.map +1 -1
- package/dist/templates/two-bedroom.d.ts +3 -3
- package/dist/templates/two-bedroom.d.ts.map +1 -1
- package/dist/tools/add-column.d.ts +5 -0
- package/dist/tools/add-column.d.ts.map +1 -0
- package/dist/tools/add-column.js +59 -0
- package/dist/tools/add-object.d.ts +76 -0
- package/dist/tools/add-object.d.ts.map +1 -0
- package/dist/tools/add-object.js +187 -0
- package/dist/tools/annotations.d.ts.map +1 -1
- package/dist/tools/apply-patch.d.ts +9 -2
- package/dist/tools/apply-patch.d.ts.map +1 -1
- package/dist/tools/apply-patch.js +36 -5
- package/dist/tools/asset-catalog.d.ts +9 -5
- package/dist/tools/asset-catalog.d.ts.map +1 -1
- package/dist/tools/asset-catalog.js +33 -12
- package/dist/tools/author-scene.d.ts +10 -0
- package/dist/tools/author-scene.d.ts.map +1 -0
- package/dist/tools/author-scene.js +140 -0
- package/dist/tools/check-collisions.d.ts +7 -7
- package/dist/tools/check-collisions.d.ts.map +1 -1
- package/dist/tools/check-collisions.js +7 -8
- package/dist/tools/clear-scene.d.ts +9 -0
- package/dist/tools/clear-scene.d.ts.map +1 -0
- package/dist/tools/clear-scene.js +59 -0
- package/dist/tools/collections.d.ts +4 -0
- package/dist/tools/collections.d.ts.map +1 -0
- package/dist/tools/collections.js +53 -0
- package/dist/tools/construction-tools.d.ts +12 -88
- package/dist/tools/construction-tools.d.ts.map +1 -1
- package/dist/tools/construction-tools.js +122 -381
- package/dist/tools/create-unit.d.ts +5 -3
- package/dist/tools/create-unit.d.ts.map +1 -1
- package/dist/tools/create-unit.js +2 -2
- package/dist/tools/errors.d.ts +15 -4
- package/dist/tools/errors.d.ts.map +1 -1
- package/dist/tools/errors.js +14 -4
- package/dist/tools/export-glb.d.ts +1 -1
- package/dist/tools/export-glb.d.ts.map +1 -1
- package/dist/tools/export-glb.js +2 -2
- package/dist/tools/export-json.d.ts +1 -1
- package/dist/tools/export-json.d.ts.map +1 -1
- package/dist/tools/export-json.js +2 -2
- package/dist/tools/geometry-script-worker.d.ts +2 -0
- package/dist/tools/geometry-script-worker.d.ts.map +1 -0
- package/dist/tools/geometry-script-worker.js +21 -0
- package/dist/tools/get-scene.d.ts +2 -1
- package/dist/tools/get-scene.d.ts.map +1 -1
- package/dist/tools/get-scene.js +5 -3
- package/dist/tools/honest-patch-guard.d.ts +2 -0
- package/dist/tools/honest-patch-guard.d.ts.map +1 -0
- package/dist/tools/honest-patch-guard.js +32 -0
- package/dist/tools/hosted-services.d.ts +4 -0
- package/dist/tools/hosted-services.d.ts.map +1 -0
- package/dist/tools/hosted-services.js +40 -0
- package/dist/tools/index.d.ts +12 -2
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +18 -32
- package/dist/tools/level-height-guard.d.ts +2 -0
- package/dist/tools/level-height-guard.d.ts.map +1 -0
- package/dist/tools/level-height-guard.js +11 -0
- package/dist/tools/list-units.d.ts +2 -2
- package/dist/tools/list-units.d.ts.map +1 -1
- package/dist/tools/list-units.js +2 -2
- package/dist/tools/live-sync.d.ts +25 -6
- package/dist/tools/live-sync.d.ts.map +1 -1
- package/dist/tools/live-sync.js +48 -27
- package/dist/tools/local-geometry-scripts.d.ts +8 -0
- package/dist/tools/local-geometry-scripts.d.ts.map +1 -0
- package/dist/tools/local-geometry-scripts.js +67 -0
- package/dist/tools/measure.d.ts +7 -1
- package/dist/tools/measure.d.ts.map +1 -1
- package/dist/tools/measure.js +38 -70
- package/dist/tools/measurement.d.ts +1 -36
- package/dist/tools/measurement.d.ts.map +1 -1
- package/dist/tools/measurement.js +2 -94
- package/dist/tools/node-world-point.d.ts +37 -0
- package/dist/tools/node-world-point.d.ts.map +1 -0
- package/dist/tools/node-world-point.js +457 -0
- package/dist/tools/normalize-schema-dialect.d.ts +1 -1
- package/dist/tools/normalize-schema-dialect.d.ts.map +1 -1
- package/dist/tools/normalize-schema-dialect.js +22 -3
- package/dist/tools/patch-guards.d.ts +60 -0
- package/dist/tools/patch-guards.d.ts.map +1 -0
- package/dist/tools/patch-guards.js +256 -0
- package/dist/tools/photo-to-scene/index.d.ts +1 -1
- package/dist/tools/photo-to-scene/index.d.ts.map +1 -1
- package/dist/tools/photo-to-scene/photo-to-scene.d.ts +3 -3
- package/dist/tools/photo-to-scene/photo-to-scene.d.ts.map +1 -1
- package/dist/tools/photo-to-scene/photo-to-scene.js +10 -9
- package/dist/tools/project-files.d.ts +4 -0
- package/dist/tools/project-files.d.ts.map +1 -0
- package/dist/tools/project-files.js +30 -0
- package/dist/tools/redo.d.ts +4 -2
- package/dist/tools/redo.d.ts.map +1 -1
- package/dist/tools/redo.js +4 -4
- package/dist/tools/retired-tools.d.ts +10 -0
- package/dist/tools/retired-tools.d.ts.map +1 -0
- package/dist/tools/retired-tools.js +22 -0
- package/dist/tools/roof-level-guard.d.ts +2 -0
- package/dist/tools/roof-level-guard.d.ts.map +1 -0
- package/dist/tools/roof-level-guard.js +22 -0
- package/dist/tools/room-tools.d.ts +36 -98
- package/dist/tools/room-tools.d.ts.map +1 -1
- package/dist/tools/room-tools.js +130 -565
- package/dist/tools/scene-lifecycle/create-project.d.ts +1 -1
- package/dist/tools/scene-lifecycle/create-project.d.ts.map +1 -1
- package/dist/tools/scene-lifecycle/create-project.js +31 -6
- package/dist/tools/scene-lifecycle/delete-scene.d.ts +3 -1
- package/dist/tools/scene-lifecycle/delete-scene.d.ts.map +1 -1
- package/dist/tools/scene-lifecycle/delete-scene.js +10 -9
- package/dist/tools/scene-lifecycle/get-project-status.d.ts +1 -1
- package/dist/tools/scene-lifecycle/get-project-status.d.ts.map +1 -1
- package/dist/tools/scene-lifecycle/get-project-status.js +6 -6
- package/dist/tools/scene-lifecycle/index.d.ts +1 -1
- package/dist/tools/scene-lifecycle/index.d.ts.map +1 -1
- package/dist/tools/scene-lifecycle/list-scenes.d.ts +1 -1
- package/dist/tools/scene-lifecycle/list-scenes.d.ts.map +1 -1
- package/dist/tools/scene-lifecycle/list-scenes.js +4 -4
- package/dist/tools/scene-lifecycle/load-scene.d.ts +2 -2
- package/dist/tools/scene-lifecycle/load-scene.d.ts.map +1 -1
- package/dist/tools/scene-lifecycle/load-scene.js +5 -5
- package/dist/tools/scene-lifecycle/metadata.d.ts +1 -1
- package/dist/tools/scene-lifecycle/rename-scene.d.ts +1 -1
- package/dist/tools/scene-lifecycle/rename-scene.d.ts.map +1 -1
- package/dist/tools/scene-lifecycle/rename-scene.js +6 -6
- package/dist/tools/scene-lifecycle/save-scene.d.ts +6 -3
- package/dist/tools/scene-lifecycle/save-scene.d.ts.map +1 -1
- package/dist/tools/scene-lifecycle/save-scene.js +124 -16
- package/dist/tools/scene-lifecycle/test-utils.d.ts +3 -3
- package/dist/tools/scene-lifecycle/test-utils.d.ts.map +1 -1
- package/dist/tools/scene-lifecycle/test-utils.js +2 -1
- package/dist/tools/set-unit-members.d.ts +4 -2
- package/dist/tools/set-unit-members.d.ts.map +1 -1
- package/dist/tools/set-unit-members.js +6 -6
- package/dist/tools/set-zone.d.ts +4 -2
- package/dist/tools/set-zone.d.ts.map +1 -1
- package/dist/tools/set-zone.js +30 -18
- package/dist/tools/shared-tools.d.ts +40 -0
- package/dist/tools/shared-tools.d.ts.map +1 -0
- package/dist/tools/shared-tools.js +574 -0
- package/dist/tools/structure-tools.d.ts +57 -0
- package/dist/tools/structure-tools.d.ts.map +1 -0
- package/dist/tools/structure-tools.js +57 -0
- package/dist/tools/templates/create-from-template.d.ts +2 -2
- package/dist/tools/templates/create-from-template.d.ts.map +1 -1
- package/dist/tools/templates/create-from-template.js +6 -6
- package/dist/tools/templates/create-house-from-brief.d.ts +1 -1
- package/dist/tools/templates/create-house-from-brief.d.ts.map +1 -1
- package/dist/tools/templates/create-house-from-brief.js +6 -6
- package/dist/tools/templates/index.d.ts +1 -1
- package/dist/tools/templates/index.d.ts.map +1 -1
- package/dist/tools/templates/list-templates.d.ts +1 -1
- package/dist/tools/templates/list-templates.d.ts.map +1 -1
- package/dist/tools/templates/list-templates.js +2 -2
- package/dist/tools/undo.d.ts +4 -2
- package/dist/tools/undo.d.ts.map +1 -1
- package/dist/tools/undo.js +4 -4
- package/dist/tools/unit-members.d.ts.map +1 -1
- package/dist/tools/unit-members.js +6 -6
- package/dist/tools/variants/generate-variants.d.ts +6 -6
- package/dist/tools/variants/generate-variants.d.ts.map +1 -1
- package/dist/tools/variants/generate-variants.js +6 -6
- package/dist/tools/variants/index.d.ts +1 -1
- package/dist/tools/variants/index.d.ts.map +1 -1
- package/dist/tools/variants/mutations.js +1 -1
- package/dist/tools/view-scene.d.ts +44 -0
- package/dist/tools/view-scene.d.ts.map +1 -0
- package/dist/tools/view-scene.js +75 -0
- package/dist/tools/vision/analyze-floorplan-image.d.ts +1 -1
- package/dist/tools/vision/analyze-floorplan-image.d.ts.map +1 -1
- package/dist/tools/vision/analyze-floorplan-image.js +7 -7
- package/dist/tools/vision/analyze-room-photo.d.ts +1 -1
- package/dist/tools/vision/analyze-room-photo.d.ts.map +1 -1
- package/dist/tools/vision/analyze-room-photo.js +7 -7
- package/dist/tools/vision/index.d.ts +1 -1
- package/dist/tools/vision/index.d.ts.map +1 -1
- package/dist/transports/http.d.ts +8 -2
- package/dist/transports/http.d.ts.map +1 -1
- package/dist/transports/http.js +9 -3
- package/dist/transports/stdio.d.ts +15 -9
- package/dist/transports/stdio.d.ts.map +1 -1
- package/dist/transports/stdio.js +18 -11
- package/dist/version.d.ts +5 -0
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +17 -3
- package/examples/coordinate-conventions-demo.md +12 -13
- package/examples/embed-in-agent.ts +6 -7
- package/examples/generate-apartment.md +13 -15
- package/examples/photo-to-scene.md +3 -4
- package/examples/renovate-from-photos.md +6 -9
- package/package.json +19 -9
- package/dist/tools/create-level.d.ts +0 -21
- package/dist/tools/create-level.d.ts.map +0 -1
- package/dist/tools/create-level.js +0 -63
- package/dist/tools/create-wall.d.ts +0 -22
- package/dist/tools/create-wall.d.ts.map +0 -1
- package/dist/tools/create-wall.js +0 -57
- package/dist/tools/cut-opening.d.ts +0 -25
- package/dist/tools/cut-opening.d.ts.map +0 -1
- package/dist/tools/cut-opening.js +0 -61
- package/dist/tools/delete-node.d.ts +0 -19
- package/dist/tools/delete-node.d.ts.map +0 -1
- package/dist/tools/delete-node.js +0 -40
- package/dist/tools/describe-node.d.ts +0 -17
- package/dist/tools/describe-node.d.ts.map +0 -1
- package/dist/tools/describe-node.js +0 -97
- package/dist/tools/door-clearance.d.ts +0 -135
- package/dist/tools/door-clearance.d.ts.map +0 -1
- package/dist/tools/door-clearance.js +0 -334
- package/dist/tools/duplicate-level.d.ts +0 -19
- package/dist/tools/duplicate-level.d.ts.map +0 -1
- package/dist/tools/duplicate-level.js +0 -60
- package/dist/tools/find-nodes.d.ts +0 -32
- package/dist/tools/find-nodes.d.ts.map +0 -1
- package/dist/tools/find-nodes.js +0 -107
- package/dist/tools/geometry.d.ts +0 -24
- package/dist/tools/geometry.d.ts.map +0 -1
- package/dist/tools/geometry.js +0 -94
- package/dist/tools/get-node.d.ts +0 -11
- package/dist/tools/get-node.d.ts.map +0 -1
- package/dist/tools/get-node.js +0 -29
- package/dist/tools/layout-clearance.d.ts +0 -105
- package/dist/tools/layout-clearance.d.ts.map +0 -1
- package/dist/tools/layout-clearance.js +0 -190
- package/dist/tools/place-item.d.ts +0 -22
- package/dist/tools/place-item.d.ts.map +0 -1
- package/dist/tools/place-item.js +0 -89
- package/dist/tools/scene-query.d.ts +0 -62
- package/dist/tools/scene-query.d.ts.map +0 -1
- package/dist/tools/scene-query.js +0 -670
- package/dist/tools/validate-scene.d.ts +0 -19
- package/dist/tools/validate-scene.d.ts.map +0 -1
- package/dist/tools/validate-scene.js +0 -28
|
@@ -1,334 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Door access keep-outs for MCP layout tools.
|
|
3
|
-
*
|
|
4
|
-
* Furniture that overlaps a door clear zone is reported as blocking the door.
|
|
5
|
-
* Used by furnish_room (skip placements) and verify_scene (layout issues).
|
|
6
|
-
*
|
|
7
|
-
* See docs/layout-clearance-error-log.md for pitfalls (levels, gap sign, scale).
|
|
8
|
-
*/
|
|
9
|
-
import { getScaledDimensions } from '@pascal-app/core/schema';
|
|
10
|
-
import { wallLength } from './geometry.js';
|
|
11
|
-
/** Plan depth (m) cleared on each side of the wall face through the opening. */
|
|
12
|
-
export const DEFAULT_DOOR_CLEAR_DEPTH = 0.65;
|
|
13
|
-
/** Extra half-width (m) beyond the door leaf along the wall. */
|
|
14
|
-
export const DEFAULT_DOOR_SIDE_PAD = 0.05;
|
|
15
|
-
/**
|
|
16
|
-
* True when A and B come closer than `gap` meters (including penetration).
|
|
17
|
-
* `gap` is the **minimum free space required** between boxes:
|
|
18
|
-
* expand each box by gap/2, then test intersection.
|
|
19
|
-
*/
|
|
20
|
-
export function aabbsOverlap(a, b, gap = 0) {
|
|
21
|
-
const g = gap;
|
|
22
|
-
return a.maxX + g > b.minX && a.minX - g < b.maxX && a.maxZ + g > b.minZ && a.minZ - g < b.maxZ;
|
|
23
|
-
}
|
|
24
|
-
/**
|
|
25
|
-
* Walk parentId chain to the enclosing level id (pure; no bridge required).
|
|
26
|
-
*/
|
|
27
|
-
export function resolveNodeLevelId(nodeId, byId) {
|
|
28
|
-
let current = byId.get(nodeId);
|
|
29
|
-
const seen = new Set();
|
|
30
|
-
while (current) {
|
|
31
|
-
if (seen.has(current.id))
|
|
32
|
-
return null;
|
|
33
|
-
seen.add(current.id);
|
|
34
|
-
if (current.type === 'level')
|
|
35
|
-
return current.id;
|
|
36
|
-
const parentId = current.parentId;
|
|
37
|
-
if (parentId && byId.has(parentId)) {
|
|
38
|
-
current = byId.get(parentId);
|
|
39
|
-
continue;
|
|
40
|
-
}
|
|
41
|
-
current = findParentByChildren(current.id, byId);
|
|
42
|
-
}
|
|
43
|
-
return null;
|
|
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
|
-
}
|
|
62
|
-
/**
|
|
63
|
-
* Axis-aligned item footprint in plan (x/z), rotation-aware.
|
|
64
|
-
* Prefer scaled dimensions when the node is available.
|
|
65
|
-
*/
|
|
66
|
-
export function itemPlanAabb(position, dimensions, rotationYRad = 0) {
|
|
67
|
-
const x = position[0] ?? 0;
|
|
68
|
-
const z = position[2] ?? 0;
|
|
69
|
-
const [w = 1, , d = 1] = dimensions ?? [1, 1, 1];
|
|
70
|
-
const cos = Math.abs(Math.cos(rotationYRad));
|
|
71
|
-
const sin = Math.abs(Math.sin(rotationYRad));
|
|
72
|
-
const halfW = (w * cos + d * sin) / 2;
|
|
73
|
-
const halfD = (w * sin + d * cos) / 2;
|
|
74
|
-
return {
|
|
75
|
-
minX: x - halfW,
|
|
76
|
-
maxX: x + halfW,
|
|
77
|
-
minZ: z - halfD,
|
|
78
|
-
maxZ: z + halfD,
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
/** Scaled plan footprint used by legacy placement and door-clearance callers. */
|
|
82
|
-
export function itemNodePlanAabb(node) {
|
|
83
|
-
if (node.type !== 'item')
|
|
84
|
-
return null;
|
|
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
|
-
};
|
|
163
|
-
}
|
|
164
|
-
/**
|
|
165
|
-
* Build a rectangular keep-out around a wall door, extruded perpendicular to the wall
|
|
166
|
-
* on both faces so either swing side is protected.
|
|
167
|
-
*/
|
|
168
|
-
export function doorKeepoutFromWall(wall, door, options) {
|
|
169
|
-
const clearDepth = options?.clearDepth ?? DEFAULT_DOOR_CLEAR_DEPTH;
|
|
170
|
-
const sidePad = options?.sidePad ?? DEFAULT_DOOR_SIDE_PAD;
|
|
171
|
-
const length = wallLength(wall);
|
|
172
|
-
if (length <= 1e-6)
|
|
173
|
-
return null;
|
|
174
|
-
const width = typeof door.width === 'number' && door.width > 0 ? door.width : 0.9;
|
|
175
|
-
const localX = Array.isArray(door.position) ? (door.position[0] ?? length / 2) : length / 2;
|
|
176
|
-
const [sx, sz] = wall.start;
|
|
177
|
-
const [ex, ez] = wall.end;
|
|
178
|
-
const dx = (ex - sx) / length;
|
|
179
|
-
const dz = (ez - sz) / length;
|
|
180
|
-
const nx = -dz;
|
|
181
|
-
const nz = dx;
|
|
182
|
-
const half = width / 2 + sidePad;
|
|
183
|
-
const corners = [];
|
|
184
|
-
for (const along of [localX - half, localX + half]) {
|
|
185
|
-
const cx = sx + dx * along;
|
|
186
|
-
const cz = sz + dz * along;
|
|
187
|
-
for (const side of [-clearDepth, clearDepth]) {
|
|
188
|
-
corners.push([cx + nx * side, cz + nz * side]);
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
const xs = corners.map((c) => c[0]);
|
|
192
|
-
const zs = corners.map((c) => c[1]);
|
|
193
|
-
return {
|
|
194
|
-
doorId: door.id,
|
|
195
|
-
wallId: wall.id,
|
|
196
|
-
levelId: options?.levelId ?? null,
|
|
197
|
-
width,
|
|
198
|
-
localX,
|
|
199
|
-
aabb: {
|
|
200
|
-
minX: Math.min(...xs),
|
|
201
|
-
maxX: Math.max(...xs),
|
|
202
|
-
minZ: Math.min(...zs),
|
|
203
|
-
maxZ: Math.max(...zs),
|
|
204
|
-
},
|
|
205
|
-
};
|
|
206
|
-
}
|
|
207
|
-
export function collectDoorKeepouts(nodes, options) {
|
|
208
|
-
const byId = new Map();
|
|
209
|
-
for (const node of nodes)
|
|
210
|
-
byId.set(node.id, node);
|
|
211
|
-
const keepouts = [];
|
|
212
|
-
for (const node of byId.values()) {
|
|
213
|
-
if (node.type !== 'door')
|
|
214
|
-
continue;
|
|
215
|
-
const wallId = node.wallId ?? node.parentId;
|
|
216
|
-
if (!wallId)
|
|
217
|
-
continue;
|
|
218
|
-
const wall = byId.get(wallId);
|
|
219
|
-
if (wall?.type !== 'wall')
|
|
220
|
-
continue;
|
|
221
|
-
const levelId = resolveNodeLevelId(wall.id, byId) ?? resolveNodeLevelId(node.id, byId);
|
|
222
|
-
if (options?.levelId && levelId !== options.levelId)
|
|
223
|
-
continue;
|
|
224
|
-
const keepout = doorKeepoutFromWall(wall, node, {
|
|
225
|
-
clearDepth: options?.clearDepth,
|
|
226
|
-
sidePad: options?.sidePad,
|
|
227
|
-
levelId,
|
|
228
|
-
});
|
|
229
|
-
if (keepout)
|
|
230
|
-
keepouts.push(keepout);
|
|
231
|
-
}
|
|
232
|
-
return keepouts;
|
|
233
|
-
}
|
|
234
|
-
export function itemBlocksDoorKeepout(itemAabb, keepout) {
|
|
235
|
-
return aabbsOverlap(itemAabb, keepout.aabb, 0.02);
|
|
236
|
-
}
|
|
237
|
-
export function findBlockedDoors(args) {
|
|
238
|
-
const nodes = [...args.nodes];
|
|
239
|
-
const byId = new Map(nodes.map((n) => [n.id, n]));
|
|
240
|
-
const keepouts = collectDoorKeepouts(nodes, {
|
|
241
|
-
clearDepth: args.clearDepth,
|
|
242
|
-
sidePad: args.sidePad,
|
|
243
|
-
levelId: args.levelId,
|
|
244
|
-
});
|
|
245
|
-
if (keepouts.length === 0)
|
|
246
|
-
return [];
|
|
247
|
-
const issues = [];
|
|
248
|
-
for (const node of nodes) {
|
|
249
|
-
if (node.type !== 'item')
|
|
250
|
-
continue;
|
|
251
|
-
const itemLevel = resolveNodeLevelId(node.id, byId);
|
|
252
|
-
if (args.levelId && itemLevel !== args.levelId)
|
|
253
|
-
continue;
|
|
254
|
-
const aabb = itemNodePlanAabb(node);
|
|
255
|
-
if (!aabb)
|
|
256
|
-
continue;
|
|
257
|
-
for (const keepout of keepouts) {
|
|
258
|
-
// Same-level only (multi-story safety).
|
|
259
|
-
if (keepout.levelId && itemLevel && keepout.levelId !== itemLevel)
|
|
260
|
-
continue;
|
|
261
|
-
if (args.levelId && keepout.levelId && keepout.levelId !== args.levelId)
|
|
262
|
-
continue;
|
|
263
|
-
if (!itemBlocksDoorKeepout(aabb, keepout))
|
|
264
|
-
continue;
|
|
265
|
-
const itemName = node.name ?? node.asset?.name ?? node.id;
|
|
266
|
-
issues.push({
|
|
267
|
-
doorId: keepout.doorId,
|
|
268
|
-
wallId: keepout.wallId,
|
|
269
|
-
itemId: node.id,
|
|
270
|
-
levelId: itemLevel,
|
|
271
|
-
itemName: typeof itemName === 'string' ? itemName : undefined,
|
|
272
|
-
message: `Door ${keepout.doorId} on wall ${keepout.wallId} is blocked by item ${itemName} (${node.id})`,
|
|
273
|
-
});
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
return issues;
|
|
277
|
-
}
|
|
278
|
-
/**
|
|
279
|
-
* Synthetic keep-outs for a room polygon edge that will host a door (before doors exist).
|
|
280
|
-
* Used by furnish_room when doorWallIndex is known.
|
|
281
|
-
*/
|
|
282
|
-
export function keepoutForPolygonEdge(polygon, edgeIndex, options) {
|
|
283
|
-
if (polygon.length < 3)
|
|
284
|
-
return null;
|
|
285
|
-
const i = ((edgeIndex % polygon.length) + polygon.length) % polygon.length;
|
|
286
|
-
const start = polygon[i];
|
|
287
|
-
const end = polygon[(i + 1) % polygon.length];
|
|
288
|
-
const wall = {
|
|
289
|
-
id: `edge-${i}`,
|
|
290
|
-
start,
|
|
291
|
-
end,
|
|
292
|
-
};
|
|
293
|
-
const length = wallLength(wall);
|
|
294
|
-
if (length <= 1e-6)
|
|
295
|
-
return null;
|
|
296
|
-
const width = options?.width ?? 0.9;
|
|
297
|
-
const t = options?.t ?? 0.5;
|
|
298
|
-
const localX = Math.min(Math.max(t * length, width / 2), length - width / 2);
|
|
299
|
-
const keepout = doorKeepoutFromWall(wall, {
|
|
300
|
-
id: `planned-door-${i}`,
|
|
301
|
-
position: [localX, 1.05, 0],
|
|
302
|
-
width,
|
|
303
|
-
}, { clearDepth: options?.clearDepth, sidePad: options?.sidePad });
|
|
304
|
-
return keepout?.aabb ?? null;
|
|
305
|
-
}
|
|
306
|
-
/**
|
|
307
|
-
* Whether an existing keep-out already covers this room-edge planned zone
|
|
308
|
-
* (so we do not double-count a real door on that edge).
|
|
309
|
-
*
|
|
310
|
-
* Requires the planned keep-out **center** to lie inside the existing keep-out,
|
|
311
|
-
* and the existing box to cover a large fraction of the planned area.
|
|
312
|
-
* Plain AABB overlap is not enough (nearby hallway doors must not suppress
|
|
313
|
-
* this room's entrance keep-out).
|
|
314
|
-
*/
|
|
315
|
-
export function keepoutCoversPlanned(existing, planned) {
|
|
316
|
-
const cx = (planned.minX + planned.maxX) / 2;
|
|
317
|
-
const cz = (planned.minZ + planned.maxZ) / 2;
|
|
318
|
-
const centerInside = cx >= existing.minX && cx <= existing.maxX && cz >= existing.minZ && cz <= existing.maxZ;
|
|
319
|
-
if (!centerInside)
|
|
320
|
-
return false;
|
|
321
|
-
// Intersection area / planned area must be substantial (same opening, not a glancing touch).
|
|
322
|
-
const ix0 = Math.max(existing.minX, planned.minX);
|
|
323
|
-
const ix1 = Math.min(existing.maxX, planned.maxX);
|
|
324
|
-
const iz0 = Math.max(existing.minZ, planned.minZ);
|
|
325
|
-
const iz1 = Math.min(existing.maxZ, planned.maxZ);
|
|
326
|
-
if (ix1 <= ix0 || iz1 <= iz0)
|
|
327
|
-
return false;
|
|
328
|
-
const inter = (ix1 - ix0) * (iz1 - iz0);
|
|
329
|
-
const plannedArea = Math.max(1e-9, (planned.maxX - planned.minX) * (planned.maxZ - planned.minZ));
|
|
330
|
-
return inter / plannedArea >= 0.5;
|
|
331
|
-
}
|
|
332
|
-
export function aabbFromPlan(a) {
|
|
333
|
-
return a;
|
|
334
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
-
import { z } from 'zod';
|
|
3
|
-
import type { SceneOperations } from '../operations/index.js';
|
|
4
|
-
export declare const duplicateLevelInput: {
|
|
5
|
-
levelId: z.ZodString;
|
|
6
|
-
};
|
|
7
|
-
export declare const duplicateLevelOutput: {
|
|
8
|
-
persistence: z.ZodOptional<z.ZodObject<{
|
|
9
|
-
status: z.ZodEnum<{
|
|
10
|
-
unbound: "unbound";
|
|
11
|
-
events_unsupported: "events_unsupported";
|
|
12
|
-
}>;
|
|
13
|
-
warning: z.ZodString;
|
|
14
|
-
}, z.core.$strip>>;
|
|
15
|
-
newLevelId: z.ZodString;
|
|
16
|
-
newNodeIds: z.ZodArray<z.ZodString>;
|
|
17
|
-
};
|
|
18
|
-
export declare function registerDuplicateLevel(server: McpServer, bridge: SceneOperations): void;
|
|
19
|
-
//# sourceMappingURL=duplicate-level.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"duplicate-level.d.ts","sourceRoot":"","sources":["../../src/tools/duplicate-level.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAGxE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAMpD,eAAO,MAAM,mBAAmB;;CAE/B,CAAA;AAED,eAAO,MAAM,oBAAoB;;;;;;;;;;CAIhC,CAAA;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAwDvF"}
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import { cloneLevelSubtree } from '@pascal-app/core/clone-scene-graph';
|
|
2
|
-
import { z } from 'zod';
|
|
3
|
-
import { ADDITIVE_TOOL_ANNOTATIONS } from './annotations.js';
|
|
4
|
-
import { ErrorCode, throwMcpError } from './errors.js';
|
|
5
|
-
import { liveSyncOutput, persistencePayload, publishLiveSceneSnapshot } from './live-sync.js';
|
|
6
|
-
import { NodeIdSchema } from './schemas.js';
|
|
7
|
-
export const duplicateLevelInput = {
|
|
8
|
-
levelId: NodeIdSchema,
|
|
9
|
-
};
|
|
10
|
-
export const duplicateLevelOutput = {
|
|
11
|
-
newLevelId: z.string(),
|
|
12
|
-
newNodeIds: z.array(z.string()),
|
|
13
|
-
...liveSyncOutput,
|
|
14
|
-
};
|
|
15
|
-
export function registerDuplicateLevel(server, bridge) {
|
|
16
|
-
server.registerTool('duplicate_level', {
|
|
17
|
-
title: 'Duplicate level',
|
|
18
|
-
description: 'Clone a level and all its descendants into a new subtree attached to the same building.',
|
|
19
|
-
inputSchema: duplicateLevelInput,
|
|
20
|
-
outputSchema: duplicateLevelOutput,
|
|
21
|
-
annotations: ADDITIVE_TOOL_ANNOTATIONS,
|
|
22
|
-
}, async ({ levelId }) => {
|
|
23
|
-
const node = bridge.getNode(levelId);
|
|
24
|
-
if (!node) {
|
|
25
|
-
throwMcpError(ErrorCode.InvalidParams, `Level not found: ${levelId}`);
|
|
26
|
-
}
|
|
27
|
-
if (node.type !== 'level') {
|
|
28
|
-
throwMcpError(ErrorCode.InvalidParams, `Node ${levelId} is a ${node.type}, expected level`);
|
|
29
|
-
}
|
|
30
|
-
// cloneLevelSubtree(nodes, levelId) — returns { clonedNodes, newLevelId, idMap }.
|
|
31
|
-
const { clonedNodes, newLevelId } = cloneLevelSubtree(bridge.getNodes(), levelId);
|
|
32
|
-
const buildingId = node.parentId ?? undefined;
|
|
33
|
-
// Flatten cloned subtree into create patches. The level node itself
|
|
34
|
-
// attaches to the original building; descendants attach to their
|
|
35
|
-
// already-remapped parent (encoded in `parentId`).
|
|
36
|
-
const patches = clonedNodes.map((n) => {
|
|
37
|
-
const isRoot = n.id === newLevelId;
|
|
38
|
-
const parentIdForBridge = isRoot
|
|
39
|
-
? buildingId
|
|
40
|
-
: (n.parentId ?? undefined);
|
|
41
|
-
const createOp = {
|
|
42
|
-
op: 'create',
|
|
43
|
-
node: n,
|
|
44
|
-
...(parentIdForBridge !== undefined ? { parentId: parentIdForBridge } : {}),
|
|
45
|
-
};
|
|
46
|
-
return createOp;
|
|
47
|
-
});
|
|
48
|
-
const result = bridge.applyPatch(patches);
|
|
49
|
-
const persistence = await publishLiveSceneSnapshot(bridge, 'duplicate_level');
|
|
50
|
-
const payload = {
|
|
51
|
-
newLevelId: newLevelId,
|
|
52
|
-
newNodeIds: result.createdIds,
|
|
53
|
-
...persistencePayload(persistence),
|
|
54
|
-
};
|
|
55
|
-
return {
|
|
56
|
-
content: [{ type: 'text', text: JSON.stringify(payload) }],
|
|
57
|
-
structuredContent: payload,
|
|
58
|
-
};
|
|
59
|
-
});
|
|
60
|
-
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
-
import { z } from 'zod';
|
|
3
|
-
import type { SceneOperations } from '../operations/index.js';
|
|
4
|
-
export declare const findNodesInput: {
|
|
5
|
-
type: z.ZodOptional<z.ZodEnum<{
|
|
6
|
-
site: "site";
|
|
7
|
-
building: "building";
|
|
8
|
-
level: "level";
|
|
9
|
-
wall: "wall";
|
|
10
|
-
fence: "fence";
|
|
11
|
-
slab: "slab";
|
|
12
|
-
door: "door";
|
|
13
|
-
item: "item";
|
|
14
|
-
zone: "zone";
|
|
15
|
-
stair: "stair";
|
|
16
|
-
ceiling: "ceiling";
|
|
17
|
-
roof: "roof";
|
|
18
|
-
"roof-segment": "roof-segment";
|
|
19
|
-
"stair-segment": "stair-segment";
|
|
20
|
-
scan: "scan";
|
|
21
|
-
guide: "guide";
|
|
22
|
-
window: "window";
|
|
23
|
-
}>>;
|
|
24
|
-
parentId: z.ZodOptional<z.ZodString>;
|
|
25
|
-
levelId: z.ZodOptional<z.ZodString>;
|
|
26
|
-
zoneId: z.ZodOptional<z.ZodString>;
|
|
27
|
-
};
|
|
28
|
-
export declare const findNodesOutput: {
|
|
29
|
-
nodes: z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
30
|
-
};
|
|
31
|
-
export declare function registerFindNodes(server: McpServer, bridge: SceneOperations): void;
|
|
32
|
-
//# sourceMappingURL=find-nodes.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"find-nodes.d.ts","sourceRoot":"","sources":["../../src/tools/find-nodes.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;AAwBpD,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;CAK1B,CAAA;AAED,eAAO,MAAM,eAAe;;CAE3B,CAAA;AAkCD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAwDlF"}
|
package/dist/tools/find-nodes.js
DELETED
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
import { pointInPolygon } from '@pascal-app/core/spatial-grid';
|
|
2
|
-
import { z } from 'zod';
|
|
3
|
-
import { READ_ONLY_TOOL_ANNOTATIONS } from './annotations.js';
|
|
4
|
-
import { NodeIdSchema } from './schemas.js';
|
|
5
|
-
const ALL_NODE_TYPES = [
|
|
6
|
-
'site',
|
|
7
|
-
'building',
|
|
8
|
-
'level',
|
|
9
|
-
'wall',
|
|
10
|
-
'fence',
|
|
11
|
-
'zone',
|
|
12
|
-
'slab',
|
|
13
|
-
'ceiling',
|
|
14
|
-
'roof',
|
|
15
|
-
'roof-segment',
|
|
16
|
-
'stair',
|
|
17
|
-
'stair-segment',
|
|
18
|
-
'item',
|
|
19
|
-
'door',
|
|
20
|
-
'window',
|
|
21
|
-
'scan',
|
|
22
|
-
'guide',
|
|
23
|
-
];
|
|
24
|
-
export const findNodesInput = {
|
|
25
|
-
type: z.enum(ALL_NODE_TYPES).optional(),
|
|
26
|
-
parentId: NodeIdSchema.optional(),
|
|
27
|
-
levelId: NodeIdSchema.optional(),
|
|
28
|
-
zoneId: NodeIdSchema.optional(),
|
|
29
|
-
};
|
|
30
|
-
export const findNodesOutput = {
|
|
31
|
-
nodes: z.array(z.record(z.string(), z.unknown())),
|
|
32
|
-
};
|
|
33
|
-
/** Compute a representative 2D point (x, z) for zone-filtering. */
|
|
34
|
-
function getPointForZoneFilter(node) {
|
|
35
|
-
if (node.type === 'wall' || node.type === 'fence') {
|
|
36
|
-
const [x1, z1] = node.start;
|
|
37
|
-
const [x2, z2] = node.end;
|
|
38
|
-
return [(x1 + x2) / 2, (z1 + z2) / 2];
|
|
39
|
-
}
|
|
40
|
-
if (node.type === 'item' ||
|
|
41
|
-
node.type === 'door' ||
|
|
42
|
-
node.type === 'window' ||
|
|
43
|
-
node.type === 'building' ||
|
|
44
|
-
node.type === 'stair' ||
|
|
45
|
-
node.type === 'roof') {
|
|
46
|
-
const [x, , z] = node.position;
|
|
47
|
-
return [x, z];
|
|
48
|
-
}
|
|
49
|
-
if (node.type === 'slab' || node.type === 'ceiling' || node.type === 'zone') {
|
|
50
|
-
const poly = node.polygon;
|
|
51
|
-
if (!poly || poly.length === 0)
|
|
52
|
-
return null;
|
|
53
|
-
let cx = 0;
|
|
54
|
-
let cz = 0;
|
|
55
|
-
for (const [x, z] of poly) {
|
|
56
|
-
cx += x;
|
|
57
|
-
cz += z;
|
|
58
|
-
}
|
|
59
|
-
return [cx / poly.length, cz / poly.length];
|
|
60
|
-
}
|
|
61
|
-
return null;
|
|
62
|
-
}
|
|
63
|
-
export function registerFindNodes(server, bridge) {
|
|
64
|
-
server.registerTool('find_nodes', {
|
|
65
|
-
title: 'Find nodes',
|
|
66
|
-
description: 'Find nodes matching any combination of type, parentId, levelId, or zoneId filters.',
|
|
67
|
-
inputSchema: findNodesInput,
|
|
68
|
-
outputSchema: findNodesOutput,
|
|
69
|
-
annotations: READ_ONLY_TOOL_ANNOTATIONS,
|
|
70
|
-
}, async (args) => {
|
|
71
|
-
const { type, parentId, levelId, zoneId } = args;
|
|
72
|
-
// Delegate type/parent/level filtering to the bridge.
|
|
73
|
-
const baseFilter = {};
|
|
74
|
-
if (type !== undefined)
|
|
75
|
-
baseFilter.type = type;
|
|
76
|
-
if (parentId !== undefined)
|
|
77
|
-
baseFilter.parentId = parentId;
|
|
78
|
-
if (levelId !== undefined)
|
|
79
|
-
baseFilter.levelId = levelId;
|
|
80
|
-
let results = bridge.findNodes(baseFilter);
|
|
81
|
-
// Zone-polygon filter: point-in-polygon on a representative 2D point.
|
|
82
|
-
if (zoneId) {
|
|
83
|
-
const zone = bridge.getNode(zoneId);
|
|
84
|
-
if (zone?.type !== 'zone') {
|
|
85
|
-
// Unknown zoneId → return empty list rather than throw; matches
|
|
86
|
-
// typical "filter" semantics.
|
|
87
|
-
results = [];
|
|
88
|
-
}
|
|
89
|
-
else {
|
|
90
|
-
const poly = zone.polygon;
|
|
91
|
-
results = results.filter((n) => {
|
|
92
|
-
const pt = getPointForZoneFilter(n);
|
|
93
|
-
if (!pt)
|
|
94
|
-
return false;
|
|
95
|
-
return pointInPolygon(pt[0], pt[1], poly);
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
const payload = {
|
|
100
|
-
nodes: results,
|
|
101
|
-
};
|
|
102
|
-
return {
|
|
103
|
-
content: [{ type: 'text', text: JSON.stringify(payload) }],
|
|
104
|
-
structuredContent: payload,
|
|
105
|
-
};
|
|
106
|
-
});
|
|
107
|
-
}
|
package/dist/tools/geometry.d.ts
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import type { WallNode } from '@pascal-app/core/schema';
|
|
2
|
-
export type Vec2 = [number, number];
|
|
3
|
-
export type Vec3 = [number, number, number];
|
|
4
|
-
export declare function distance2D(a: Vec2, b: Vec2): number;
|
|
5
|
-
export declare function wallLength(wall: Pick<WallNode, 'start' | 'end'>): number;
|
|
6
|
-
export declare function clamp(value: number, min: number, max: number): number;
|
|
7
|
-
export declare function wallLocalXFromT(wall: Pick<WallNode, 'start' | 'end'>, t: number, width: number): number;
|
|
8
|
-
export declare function projectWorldPointToWallLocalX(wall: Pick<WallNode, 'start' | 'end'>, position: Vec3): number;
|
|
9
|
-
export declare function polygonArea(points: Vec2[]): number;
|
|
10
|
-
export declare function polygonBounds(points: Vec2[]): {
|
|
11
|
-
minX: number;
|
|
12
|
-
maxX: number;
|
|
13
|
-
minZ: number;
|
|
14
|
-
maxZ: number;
|
|
15
|
-
width: number;
|
|
16
|
-
depth: number;
|
|
17
|
-
centerX: number;
|
|
18
|
-
centerZ: number;
|
|
19
|
-
};
|
|
20
|
-
export declare function pointInBoundsWithPadding(x: number, z: number, bounds: ReturnType<typeof polygonBounds>, padding: number): boolean;
|
|
21
|
-
export declare function pointOnSegment(point: Vec2, a: Vec2, b: Vec2, tolerance?: number): boolean;
|
|
22
|
-
export declare function pointInPolygon(point: Vec2, polygon: Vec2[], includeBoundary?: boolean): boolean;
|
|
23
|
-
export declare function polygonContainsPolygon(outer: Vec2[], inner: Vec2[]): boolean;
|
|
24
|
-
//# sourceMappingURL=geometry.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"geometry.d.ts","sourceRoot":"","sources":["../../src/tools/geometry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AAEvD,MAAM,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;AACnC,MAAM,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;AAE3C,wBAAgB,UAAU,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,GAAG,MAAM,CAInD;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,OAAO,GAAG,KAAK,CAAC,GAAG,MAAM,CAExE;AAED,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAGrE;AAED,wBAAgB,eAAe,CAC7B,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,OAAO,GAAG,KAAK,CAAC,EACrC,CAAC,EAAE,MAAM,EACT,KAAK,EAAE,MAAM,GACZ,MAAM,CAGR;AAED,wBAAgB,6BAA6B,CAC3C,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,OAAO,GAAG,KAAK,CAAC,EACrC,QAAQ,EAAE,IAAI,GACb,MAAM,CAWR;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,CASlD;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,IAAI,EAAE,GAAG;IAC7C,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;CAChB,CAiBA;AAED,wBAAgB,wBAAwB,CACtC,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,MAAM,EAAE,UAAU,CAAC,OAAO,aAAa,CAAC,EACxC,OAAO,EAAE,MAAM,GACd,OAAO,CAOT;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,SAAS,SAAO,GAAG,OAAO,CAOvF;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,eAAe,UAAO,GAAG,OAAO,CAgB5F;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,OAAO,CAE5E"}
|
package/dist/tools/geometry.js
DELETED
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
export function distance2D(a, b) {
|
|
2
|
-
const dx = b[0] - a[0];
|
|
3
|
-
const dz = b[1] - a[1];
|
|
4
|
-
return Math.sqrt(dx * dx + dz * dz);
|
|
5
|
-
}
|
|
6
|
-
export function wallLength(wall) {
|
|
7
|
-
return distance2D(wall.start, wall.end);
|
|
8
|
-
}
|
|
9
|
-
export function clamp(value, min, max) {
|
|
10
|
-
if (max < min)
|
|
11
|
-
return (min + max) / 2;
|
|
12
|
-
return Math.max(min, Math.min(max, value));
|
|
13
|
-
}
|
|
14
|
-
export function wallLocalXFromT(wall, t, width) {
|
|
15
|
-
const length = wallLength(wall);
|
|
16
|
-
return clamp(t * length, width / 2, length - width / 2);
|
|
17
|
-
}
|
|
18
|
-
export function projectWorldPointToWallLocalX(wall, position) {
|
|
19
|
-
const [sx, sz] = wall.start;
|
|
20
|
-
const [ex, ez] = wall.end;
|
|
21
|
-
const dx = ex - sx;
|
|
22
|
-
const dz = ez - sz;
|
|
23
|
-
const len = Math.sqrt(dx * dx + dz * dz);
|
|
24
|
-
if (len === 0)
|
|
25
|
-
return 0;
|
|
26
|
-
const px = position[0] - sx;
|
|
27
|
-
const pz = position[2] - sz;
|
|
28
|
-
const distance = px * (dx / len) + pz * (dz / len);
|
|
29
|
-
return clamp(distance, 0, len);
|
|
30
|
-
}
|
|
31
|
-
export function polygonArea(points) {
|
|
32
|
-
if (points.length < 3)
|
|
33
|
-
return 0;
|
|
34
|
-
let area = 0;
|
|
35
|
-
for (let i = 0; i < points.length; i++) {
|
|
36
|
-
const current = points[i];
|
|
37
|
-
const next = points[(i + 1) % points.length];
|
|
38
|
-
area += current[0] * next[1] - next[0] * current[1];
|
|
39
|
-
}
|
|
40
|
-
return Math.abs(area) / 2;
|
|
41
|
-
}
|
|
42
|
-
export function polygonBounds(points) {
|
|
43
|
-
const xs = points.map((p) => p[0]);
|
|
44
|
-
const zs = points.map((p) => p[1]);
|
|
45
|
-
const minX = Math.min(...xs);
|
|
46
|
-
const maxX = Math.max(...xs);
|
|
47
|
-
const minZ = Math.min(...zs);
|
|
48
|
-
const maxZ = Math.max(...zs);
|
|
49
|
-
return {
|
|
50
|
-
minX,
|
|
51
|
-
maxX,
|
|
52
|
-
minZ,
|
|
53
|
-
maxZ,
|
|
54
|
-
width: maxX - minX,
|
|
55
|
-
depth: maxZ - minZ,
|
|
56
|
-
centerX: (minX + maxX) / 2,
|
|
57
|
-
centerZ: (minZ + maxZ) / 2,
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
export function pointInBoundsWithPadding(x, z, bounds, padding) {
|
|
61
|
-
return (x >= bounds.minX + padding &&
|
|
62
|
-
x <= bounds.maxX - padding &&
|
|
63
|
-
z >= bounds.minZ + padding &&
|
|
64
|
-
z <= bounds.maxZ - padding);
|
|
65
|
-
}
|
|
66
|
-
export function pointOnSegment(point, a, b, tolerance = 1e-6) {
|
|
67
|
-
const cross = (point[1] - a[1]) * (b[0] - a[0]) - (point[0] - a[0]) * (b[1] - a[1]);
|
|
68
|
-
if (Math.abs(cross) > tolerance)
|
|
69
|
-
return false;
|
|
70
|
-
const dot = (point[0] - a[0]) * (b[0] - a[0]) + (point[1] - a[1]) * (b[1] - a[1]);
|
|
71
|
-
if (dot < -tolerance)
|
|
72
|
-
return false;
|
|
73
|
-
const lenSq = (b[0] - a[0]) ** 2 + (b[1] - a[1]) ** 2;
|
|
74
|
-
return dot <= lenSq + tolerance;
|
|
75
|
-
}
|
|
76
|
-
export function pointInPolygon(point, polygon, includeBoundary = true) {
|
|
77
|
-
if (polygon.length < 3)
|
|
78
|
-
return false;
|
|
79
|
-
let inside = false;
|
|
80
|
-
const [x, z] = point;
|
|
81
|
-
for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
|
|
82
|
-
const a = polygon[i];
|
|
83
|
-
const b = polygon[j];
|
|
84
|
-
if (pointOnSegment(point, a, b))
|
|
85
|
-
return includeBoundary;
|
|
86
|
-
const intersects = a[1] > z !== b[1] > z && x < ((b[0] - a[0]) * (z - a[1])) / (b[1] - a[1]) + a[0];
|
|
87
|
-
if (intersects)
|
|
88
|
-
inside = !inside;
|
|
89
|
-
}
|
|
90
|
-
return inside;
|
|
91
|
-
}
|
|
92
|
-
export function polygonContainsPolygon(outer, inner) {
|
|
93
|
-
return inner.every((point) => pointInPolygon(point, outer, true));
|
|
94
|
-
}
|