@pascal-app/core 1.1.0 → 1.1.2

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.
Files changed (133) hide show
  1. package/dist/agent-operations/__fixtures__/cases.d.ts +3 -2
  2. package/dist/agent-operations/__fixtures__/cases.d.ts.map +1 -1
  3. package/dist/agent-operations/__fixtures__/place-items-cases.d.ts.map +1 -1
  4. package/dist/agent-operations/__fixtures__/place-items-cases.js +28 -0
  5. package/dist/agent-operations/__fixtures__/roof-bearing-cases.d.ts +2618 -2618
  6. package/dist/agent-operations/asset-catalog.d.ts +21 -11
  7. package/dist/agent-operations/asset-catalog.d.ts.map +1 -1
  8. package/dist/agent-operations/asset-catalog.js +28 -10
  9. package/dist/agent-operations/asset-views.d.ts +8 -3
  10. package/dist/agent-operations/asset-views.d.ts.map +1 -1
  11. package/dist/agent-operations/asset-views.js +19 -3
  12. package/dist/agent-operations/batch.d.ts +4 -2
  13. package/dist/agent-operations/batch.d.ts.map +1 -1
  14. package/dist/agent-operations/batch.js +9 -6
  15. package/dist/agent-operations/catalog-asset.d.ts +15 -0
  16. package/dist/agent-operations/catalog-asset.d.ts.map +1 -0
  17. package/dist/agent-operations/catalog-asset.js +4 -0
  18. package/dist/agent-operations/circulation-spaces.d.ts +585 -585
  19. package/dist/agent-operations/design-brief.d.ts.map +1 -1
  20. package/dist/agent-operations/design-brief.js +2 -0
  21. package/dist/agent-operations/furnish-room.d.ts.map +1 -1
  22. package/dist/agent-operations/furnish-room.js +2 -1
  23. package/dist/agent-operations/index.d.ts +5716 -7
  24. package/dist/agent-operations/index.d.ts.map +1 -1
  25. package/dist/agent-operations/index.js +17 -3
  26. package/dist/agent-operations/level-reads.d.ts.map +1 -1
  27. package/dist/agent-operations/level-reads.js +4 -1
  28. package/dist/agent-operations/place-in-room.d.ts.map +1 -1
  29. package/dist/agent-operations/place-in-room.js +2 -1
  30. package/dist/agent-operations/place-items.d.ts.map +1 -1
  31. package/dist/agent-operations/place-items.js +2 -1
  32. package/dist/agent-operations/scene-queries.d.ts +3818 -3818
  33. package/dist/agent-operations/scene-view.d.ts.map +1 -1
  34. package/dist/agent-operations/scene-view.js +38 -13
  35. package/dist/agent-operations/search-assets.d.ts.map +1 -1
  36. package/dist/agent-operations/search-assets.js +6 -0
  37. package/dist/agent-operations/types.d.ts +10 -2
  38. package/dist/agent-operations/types.d.ts.map +1 -1
  39. package/dist/agent-operations/usage-checks.js +18 -3
  40. package/dist/agent-operations/verify-scene.d.ts +7 -3
  41. package/dist/agent-operations/verify-scene.d.ts.map +1 -1
  42. package/dist/agent-operations/verify-scene.js +15 -7
  43. package/dist/agent-tools/assets.js +1 -1
  44. package/dist/building/__fixtures__/wall-opening-cases.d.ts +30 -30
  45. package/dist/building/level-duplication.d.ts +3409 -3409
  46. package/dist/building/level-duplication.d.ts.map +1 -1
  47. package/dist/building/level-duplication.js +3 -1
  48. package/dist/building/wall-openings.d.ts +75 -75
  49. package/dist/commands/structure/shared.d.ts +3407 -3407
  50. package/dist/commands/structure/zone-wall-merge.d.ts +652 -652
  51. package/dist/hooks/spatial-grid/spatial-grid-sync.d.ts +3818 -3818
  52. package/dist/index.d.ts +2 -0
  53. package/dist/index.d.ts.map +1 -1
  54. package/dist/index.js +2 -0
  55. package/dist/lib/__fixtures__/mezzanine.d.ts +2003 -2003
  56. package/dist/lib/absorb-wall-separators.d.ts +1032 -1032
  57. package/dist/lib/asset-storage.d.ts.map +1 -1
  58. package/dist/lib/asset-storage.js +3 -0
  59. package/dist/lib/floor-foundation-datum.d.ts.map +1 -1
  60. package/dist/lib/floor-foundation-datum.js +5 -1
  61. package/dist/lib/floorplan/context.d.ts +95 -0
  62. package/dist/lib/floorplan/context.d.ts.map +1 -0
  63. package/dist/lib/floorplan/context.js +155 -0
  64. package/dist/lib/floorplan/index.d.ts +6 -0
  65. package/dist/lib/floorplan/index.d.ts.map +1 -0
  66. package/dist/lib/floorplan/index.js +5 -0
  67. package/dist/lib/floorplan/marks.d.ts +71 -0
  68. package/dist/lib/floorplan/marks.d.ts.map +1 -0
  69. package/dist/lib/floorplan/marks.js +157 -0
  70. package/dist/lib/floorplan/plan-math.d.ts +18 -0
  71. package/dist/lib/floorplan/plan-math.d.ts.map +1 -0
  72. package/dist/lib/floorplan/plan-math.js +82 -0
  73. package/dist/lib/floorplan/svg-paths.d.ts +17 -0
  74. package/dist/lib/floorplan/svg-paths.d.ts.map +1 -0
  75. package/dist/lib/floorplan/svg-paths.js +92 -0
  76. package/dist/lib/floorplan/wall-affordances.d.ts +31 -0
  77. package/dist/lib/floorplan/wall-affordances.d.ts.map +1 -0
  78. package/dist/lib/floorplan/wall-affordances.js +24 -0
  79. package/dist/lib/steps.d.ts +8 -0
  80. package/dist/lib/steps.d.ts.map +1 -0
  81. package/dist/lib/steps.js +7 -0
  82. package/dist/registry/__fixtures__/roof-planes.d.ts +69 -0
  83. package/dist/registry/__fixtures__/roof-planes.d.ts.map +1 -0
  84. package/dist/registry/__fixtures__/roof-planes.js +59 -0
  85. package/dist/registry/index.d.ts +2 -1
  86. package/dist/registry/index.d.ts.map +1 -1
  87. package/dist/registry/index.js +1 -0
  88. package/dist/registry/roofs.d.ts +34 -0
  89. package/dist/registry/roofs.d.ts.map +1 -0
  90. package/dist/registry/roofs.js +61 -0
  91. package/dist/registry/types.d.ts +30 -0
  92. package/dist/registry/types.d.ts.map +1 -1
  93. package/dist/registry/validate-relations.d.ts +5 -1
  94. package/dist/registry/validate-relations.d.ts.map +1 -1
  95. package/dist/registry/validate-relations.js +6 -2
  96. package/dist/schema/asset-url.d.ts +8 -0
  97. package/dist/schema/asset-url.d.ts.map +1 -1
  98. package/dist/schema/asset-url.js +17 -0
  99. package/dist/schema/nodes/roof.d.ts +1 -1
  100. package/dist/schema/nodes/roof.d.ts.map +1 -1
  101. package/dist/schema/nodes/roof.js +13 -8
  102. package/dist/schema/types.d.ts +1 -1
  103. package/dist/services/roof-level.d.ts.map +1 -1
  104. package/dist/services/roof-level.js +12 -2
  105. package/dist/services/roof-move.d.ts +4 -2
  106. package/dist/services/roof-move.d.ts.map +1 -1
  107. package/dist/services/roof-move.js +21 -9
  108. package/dist/spatial/usage-field/findings.d.ts +10 -1
  109. package/dist/spatial/usage-field/findings.d.ts.map +1 -1
  110. package/dist/spatial/usage-field/findings.js +56 -12
  111. package/dist/spatial/usage-field/measure.d.ts +3 -1
  112. package/dist/spatial/usage-field/measure.d.ts.map +1 -1
  113. package/dist/spatial/usage-field/measure.js +7 -2
  114. package/dist/spatial/usage-field/path.d.ts.map +1 -1
  115. package/dist/spatial/usage-field/path.js +28 -18
  116. package/dist/store/actions/node-actions.d.ts +2380 -2380
  117. package/dist/store/scene-annotations.d.ts +1279 -1279
  118. package/dist/systems/document-reconcile.d.ts.map +1 -1
  119. package/dist/systems/document-reconcile.js +1 -2
  120. package/dist/systems/roof/roof-elevation.d.ts +11 -6
  121. package/dist/systems/roof/roof-elevation.d.ts.map +1 -1
  122. package/dist/systems/roof/roof-elevation.js +24 -11
  123. package/dist/systems/slab/__fixtures__/floor-step.d.ts +2381 -2381
  124. package/dist/systems/slab/__fixtures__/raised-room.d.ts +3818 -3818
  125. package/dist/systems/stair/stair-opening-preview.d.ts +900 -900
  126. package/dist/utils/ensure-scene-openings.d.ts +4815 -4815
  127. package/dist/utils/floor-plate-migration.d.ts +1032 -1032
  128. package/dist/utils/owned-floor-opening-migration.d.ts +1032 -1032
  129. package/dist/utils/reconcile-structure-on-load.d.ts +2380 -2380
  130. package/dist/utils/scene-bounds.d.ts +32 -0
  131. package/dist/utils/scene-bounds.d.ts.map +1 -0
  132. package/dist/utils/scene-bounds.js +201 -0
  133. package/package.json +1 -1
@@ -0,0 +1,82 @@
1
+ import { getWallFaceOffsets } from '../../systems/wall/wall-frame.js';
2
+ // Baseline rotation (deg) that orients the plan-local scene "north up" on
3
+ // screen. The on-screen floor-plan scene `<g>` is rotated by
4
+ // `FLOORPLAN_VIEW_ROTATION_DEG + userRotation - buildingRotation`; PDF export
5
+ // uses the same user rotation so its orientation matches the live plan.
6
+ //
7
+ // North is world −Z: with a 0 baseline a rotation-0 reference image (top =
8
+ // −Z) reads upright in the north-aligned view, and "align north" maps to a
9
+ // 3D camera azimuth of 0. The old 90° baseline made north world −X, so
10
+ // north-up floorplan scans displayed sideways whenever the compass claimed
11
+ // the view was aligned.
12
+ export const FLOORPLAN_VIEW_ROTATION_DEG = 0;
13
+ export function clampPlanValue(value, min, max) {
14
+ return Math.min(Math.max(value, min), max);
15
+ }
16
+ export function rotatePlanVector(x, y, rotation) {
17
+ const cos = Math.cos(rotation);
18
+ const sin = Math.sin(rotation);
19
+ return [x * cos + y * sin, -x * sin + y * cos];
20
+ }
21
+ // Converts a world X/Z point into the floor-plan-local (building-local)
22
+ // frame used by the SVG scene `<g>` and every stored node position. The
23
+ // inverse of `floorplanLocalToWorldPoint`. Shared so the floor-plan panel
24
+ // and the 2D move overlay resolve the same frame — feeding a world-space
25
+ // `original` into a local-space cursor solver lands the drop off by the
26
+ // building's world X/Z (worse for an off-origin building).
27
+ export function worldToFloorplanLocalPoint(worldX, worldZ, buildingPosition, buildingRotationY) {
28
+ const dx = worldX - buildingPosition[0];
29
+ const dz = worldZ - buildingPosition[2];
30
+ const cos = Math.cos(buildingRotationY);
31
+ const sin = Math.sin(buildingRotationY);
32
+ return {
33
+ x: dx * cos - dz * sin,
34
+ y: dx * sin + dz * cos,
35
+ };
36
+ }
37
+ // Inverse of `worldToFloorplanLocalPoint`: floor-plan-local X/Y → world X/Z.
38
+ export function floorplanLocalToWorldPoint(point, buildingPosition, buildingRotationY) {
39
+ const localX = Array.isArray(point) ? point[0] : point.x;
40
+ const localY = Array.isArray(point) ? point[1] : point.y;
41
+ const cos = Math.cos(buildingRotationY);
42
+ const sin = Math.sin(buildingRotationY);
43
+ return {
44
+ x: buildingPosition[0] + localX * cos + localY * sin,
45
+ z: buildingPosition[2] - localX * sin + localY * cos,
46
+ };
47
+ }
48
+ export function interpolatePlanPoint(start, end, t) {
49
+ return {
50
+ x: start.x + (end.x - start.x) * t,
51
+ y: start.y + (end.y - start.y) * t,
52
+ };
53
+ }
54
+ export function getPlanPointDistance(start, end) {
55
+ return Math.hypot(end.x - start.x, end.y - start.y);
56
+ }
57
+ export function movePlanPointTowards(start, end, distance) {
58
+ const totalDistance = getPlanPointDistance(start, end);
59
+ if (totalDistance <= Number.EPSILON || distance <= 0) {
60
+ return start;
61
+ }
62
+ return interpolatePlanPoint(start, end, Math.min(1, distance / totalDistance));
63
+ }
64
+ export function getThickPlanLinePolygon(line, thickness, justification) {
65
+ const dx = line.end.x - line.start.x;
66
+ const dy = line.end.y - line.start.y;
67
+ const length = Math.hypot(dx, dy);
68
+ if (length <= Number.EPSILON || thickness <= 0) {
69
+ return [line.start, line.end, line.end, line.start];
70
+ }
71
+ const { a, b } = getWallFaceOffsets({ thickness, justification });
72
+ const normalX = (-dy / length) * a;
73
+ const normalY = (dx / length) * a;
74
+ const backX = (-dy / length) * -b;
75
+ const backY = (dx / length) * -b;
76
+ return [
77
+ { x: line.start.x + normalX, y: line.start.y + normalY },
78
+ { x: line.end.x + normalX, y: line.end.y + normalY },
79
+ { x: line.end.x - backX, y: line.end.y - backY },
80
+ { x: line.start.x - backX, y: line.start.y - backY },
81
+ ];
82
+ }
@@ -0,0 +1,17 @@
1
+ import type { Point2D } from '../../systems/wall/wall-mitering.js';
2
+ export declare function formatPolygonPath(points: Point2D[], holes?: Point2D[][]): string;
3
+ export declare function buildSvgPolylinePath(points: Point2D[]): string | null;
4
+ export declare function getArcPlanPoint(center: Point2D, radius: number, angle: number): Point2D;
5
+ export declare function buildSvgArcPath(center: Point2D, radius: number, startAngle: number, endAngle: number): string;
6
+ export declare function buildSvgAnnularSectorPath(center: Point2D, innerRadius: number, outerRadius: number, startAngle: number, endAngle: number): string;
7
+ export declare function formatSvgPolygonPoints(points: Point2D[]): string;
8
+ /**
9
+ * Three points defining an arrow head — tip + two trailing barbs.
10
+ * Returned as plain `Point2D` objects so consumers can either feed them
11
+ * straight into `formatSvgPolygonPoints` (for SVG `points=""`) or push
12
+ * them onto a `FloorplanGeometry.polygon.points` array. Mixing both
13
+ * downstream paths through a string-returning helper was awkward — see
14
+ * `nodes/src/stair/floorplan.ts` which needs the points as objects.
15
+ */
16
+ export declare function buildSvgArrowHeadPoints(point: Point2D, angle: number, size: number): Point2D[];
17
+ //# sourceMappingURL=svg-paths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"svg-paths.d.ts","sourceRoot":"","sources":["../../../src/lib/floorplan/svg-paths.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kCAAkC,CAAA;AAiB/D,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE,KAAK,GAAE,OAAO,EAAE,EAAO,UAoB3E;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,OAAO,EAAE,iBAWrD;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAKvF;AAED,wBAAgB,eAAe,CAC7B,MAAM,EAAE,OAAO,EACf,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,UAIjB;AAED,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,OAAO,EACf,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,UAWjB;AAWD,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,OAAO,EAAE,UAEvD;AAED;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,EAAE,CAW9F"}
@@ -0,0 +1,92 @@
1
+ function toSvgX(value) {
2
+ return value;
3
+ }
4
+ function toSvgY(value) {
5
+ return value;
6
+ }
7
+ function toSvgPoint(point) {
8
+ return {
9
+ x: toSvgX(point.x),
10
+ y: toSvgY(point.y),
11
+ };
12
+ }
13
+ export function formatPolygonPath(points, holes = []) {
14
+ const formatSubpath = (subpathPoints) => {
15
+ const [firstPoint, ...restPoints] = subpathPoints;
16
+ if (!firstPoint) {
17
+ return null;
18
+ }
19
+ const firstSvgPoint = toSvgPoint(firstPoint);
20
+ return [
21
+ `M ${firstSvgPoint.x} ${firstSvgPoint.y}`,
22
+ ...restPoints.map((point) => {
23
+ const svgPoint = toSvgPoint(point);
24
+ return `L ${svgPoint.x} ${svgPoint.y}`;
25
+ }),
26
+ 'Z',
27
+ ].join(' ');
28
+ };
29
+ return [points, ...holes].map(formatSubpath).filter(Boolean).join(' ');
30
+ }
31
+ export function buildSvgPolylinePath(points) {
32
+ if (points.length < 2) {
33
+ return null;
34
+ }
35
+ return points
36
+ .map((point, index) => {
37
+ const svgPoint = toSvgPoint(point);
38
+ return `${index === 0 ? 'M' : 'L'} ${svgPoint.x} ${svgPoint.y}`;
39
+ })
40
+ .join(' ');
41
+ }
42
+ export function getArcPlanPoint(center, radius, angle) {
43
+ return {
44
+ x: center.x + Math.cos(angle) * radius,
45
+ y: center.y + Math.sin(angle) * radius,
46
+ };
47
+ }
48
+ export function buildSvgArcPath(center, radius, startAngle, endAngle) {
49
+ const start = getArcPlanPoint(center, radius, startAngle);
50
+ return `M ${toSvgX(start.x)} ${toSvgY(start.y)} ${svgArcCommands(center, radius, startAngle, endAngle)}`;
51
+ }
52
+ export function buildSvgAnnularSectorPath(center, innerRadius, outerRadius, startAngle, endAngle) {
53
+ const outerStart = getArcPlanPoint(center, outerRadius, startAngle);
54
+ const innerEnd = getArcPlanPoint(center, innerRadius, endAngle);
55
+ return [
56
+ `M ${toSvgX(outerStart.x)} ${toSvgY(outerStart.y)}`,
57
+ svgArcCommands(center, outerRadius, startAngle, endAngle),
58
+ `L ${toSvgX(innerEnd.x)} ${toSvgY(innerEnd.y)}`,
59
+ svgArcCommands(center, innerRadius, endAngle, startAngle),
60
+ 'Z',
61
+ ].join(' ');
62
+ }
63
+ function svgArcCommands(center, radius, startAngle, endAngle) {
64
+ const delta = endAngle - startAngle;
65
+ const count = Math.max(1, Math.ceil(Math.abs(delta) / Math.PI));
66
+ return Array.from({ length: count }, (_, index) => {
67
+ const end = getArcPlanPoint(center, radius, startAngle + (delta * (index + 1)) / count);
68
+ return `A ${radius} ${radius} 0 0 ${delta >= 0 ? 1 : 0} ${toSvgX(end.x)} ${toSvgY(end.y)}`;
69
+ }).join(' ');
70
+ }
71
+ export function formatSvgPolygonPoints(points) {
72
+ return points.map((point) => `${toSvgX(point.x)},${toSvgY(point.y)}`).join(' ');
73
+ }
74
+ /**
75
+ * Three points defining an arrow head — tip + two trailing barbs.
76
+ * Returned as plain `Point2D` objects so consumers can either feed them
77
+ * straight into `formatSvgPolygonPoints` (for SVG `points=""`) or push
78
+ * them onto a `FloorplanGeometry.polygon.points` array. Mixing both
79
+ * downstream paths through a string-returning helper was awkward — see
80
+ * `nodes/src/stair/floorplan.ts` which needs the points as objects.
81
+ */
82
+ export function buildSvgArrowHeadPoints(point, angle, size) {
83
+ const left = {
84
+ x: point.x - size * Math.cos(angle - Math.PI / 6),
85
+ y: point.y - size * Math.sin(angle - Math.PI / 6),
86
+ };
87
+ const right = {
88
+ x: point.x - size * Math.cos(angle + Math.PI / 6),
89
+ y: point.y - size * Math.sin(angle + Math.PI / 6),
90
+ };
91
+ return [point, left, right];
92
+ }
@@ -0,0 +1,31 @@
1
+ import type { WallNode } from '../../schema/index.js';
2
+ import type { WallPlanPoint } from '../../systems/wall/wall-move.js';
3
+ /** The floor plan's affordance id for a wall end's pull grip (a `move-arrow`, variant `pull`). */
4
+ export declare const WALL_PULL_AFFORDANCE = "wall-pull";
5
+ /** What a pull grip carries: the wall, and which of its ends it pulls from. */
6
+ export type WallPullGripPayload = {
7
+ wallId: string;
8
+ endpoint: 'start' | 'end';
9
+ };
10
+ /** The grip's tooltip, in both views. */
11
+ export declare const WALL_PULL_GRIP_TITLE = "Drag to draw the next wall";
12
+ /**
13
+ * The pulled end of `wall` and the direction out of it: along the wall for a
14
+ * straight one, along its tangent for a curved one.
15
+ */
16
+ export declare function wallPullSeed(wall: Pick<WallNode, 'start' | 'end' | 'curveOffset'>, endpoint: 'start' | 'end'): {
17
+ start: WallPlanPoint;
18
+ reference: WallPlanPoint;
19
+ } | null;
20
+ /**
21
+ * The affordance a floor-plan `move-arrow` carries to be a wall push arrow;
22
+ * its payload is a `WallPushArrowPayload`. The editor's floor plan runs its
23
+ * wall push for it instead of a registry affordance session.
24
+ */
25
+ export declare const WALL_PUSH_AFFORDANCE = "wall-push";
26
+ /** A wall's or an invisible wall's side arrow: `wallId` is either. */
27
+ export type WallPushArrowPayload = {
28
+ wallId: string;
29
+ side: 'a' | 'b';
30
+ };
31
+ //# sourceMappingURL=wall-affordances.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wall-affordances.d.ts","sourceRoot":"","sources":["../../../src/lib/floorplan/wall-affordances.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAE5C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAA;AAEjE,kGAAkG;AAClG,eAAO,MAAM,oBAAoB,cAAc,CAAA;AAE/C,+EAA+E;AAC/E,MAAM,MAAM,mBAAmB,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,OAAO,GAAG,KAAK,CAAA;CAAE,CAAA;AAE/E,yCAAyC;AACzC,eAAO,MAAM,oBAAoB,+BAA+B,CAAA;AAEhE;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,OAAO,GAAG,KAAK,GAAG,aAAa,CAAC,EACrD,QAAQ,EAAE,OAAO,GAAG,KAAK,GACxB;IAAE,KAAK,EAAE,aAAa,CAAC;IAAC,SAAS,EAAE,aAAa,CAAA;CAAE,GAAG,IAAI,CAQ3D;AAED;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,cAAc,CAAA;AAE/C,sEAAsE;AACtE,MAAM,MAAM,oBAAoB,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,GAAG,GAAG,GAAG,CAAA;CAAE,CAAA"}
@@ -0,0 +1,24 @@
1
+ import { getWallCurveFrameAt } from '../../systems/wall/wall-curve.js';
2
+ /** The floor plan's affordance id for a wall end's pull grip (a `move-arrow`, variant `pull`). */
3
+ export const WALL_PULL_AFFORDANCE = 'wall-pull';
4
+ /** The grip's tooltip, in both views. */
5
+ export const WALL_PULL_GRIP_TITLE = 'Drag to draw the next wall';
6
+ /**
7
+ * The pulled end of `wall` and the direction out of it: along the wall for a
8
+ * straight one, along its tangent for a curved one.
9
+ */
10
+ export function wallPullSeed(wall, endpoint) {
11
+ const start = endpoint === 'start' ? [wall.start[0], wall.start[1]] : [wall.end[0], wall.end[1]];
12
+ const tangent = getWallCurveFrameAt(wall, endpoint === 'start' ? 0 : 1).tangent;
13
+ const sign = endpoint === 'start' ? -1 : 1;
14
+ const length = Math.hypot(tangent.x, tangent.y);
15
+ if (!(length > 1e-9))
16
+ return null;
17
+ return { start, reference: [(tangent.x / length) * sign, (tangent.y / length) * sign] };
18
+ }
19
+ /**
20
+ * The affordance a floor-plan `move-arrow` carries to be a wall push arrow;
21
+ * its payload is a `WallPushArrowPayload`. The editor's floor plan runs its
22
+ * wall push for it instead of a registry affordance session.
23
+ */
24
+ export const WALL_PUSH_AFFORDANCE = 'wall-push';
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Work in steps: each resume runs the next piece, and the last returns the result. A long
3
+ * computation written this way can be resumed one piece at a time by a caller that lets other work
4
+ * run in between; `inOneGo` runs it through.
5
+ */
6
+ export type Steps<Result> = Generator<void, Result, void>;
7
+ export declare function inOneGo<Result>(steps: Steps<Result>): Result;
8
+ //# sourceMappingURL=steps.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"steps.d.ts","sourceRoot":"","sources":["../../src/lib/steps.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,MAAM,KAAK,CAAC,MAAM,IAAI,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,CAAA;AAEzD,wBAAgB,OAAO,CAAC,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,MAAM,CAK5D"}
@@ -0,0 +1,7 @@
1
+ export function inOneGo(steps) {
2
+ for (;;) {
3
+ const step = steps.next();
4
+ if (step.done)
5
+ return step.value;
6
+ }
7
+ }
@@ -0,0 +1,69 @@
1
+ import { z } from 'zod';
2
+ import type { AnyNode } from '../../schema/types.js';
3
+ /**
4
+ * A plugin's solved roof plane, as an architect's roof holds it (`capabilities.roofs`): an outline
5
+ * in its roof's plan, rising from a baseline at a pitch (rise per 12 of run).
6
+ */
7
+ export declare const RoofPlane: z.ZodObject<{
8
+ object: z.ZodDefault<z.ZodLiteral<"node">>;
9
+ name: z.ZodOptional<z.ZodString>;
10
+ parentId: z.ZodDefault<z.ZodNullable<z.ZodString>>;
11
+ visible: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
12
+ camera: z.ZodOptional<z.ZodObject<{
13
+ position: z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber], null>;
14
+ target: z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber], null>;
15
+ mode: z.ZodDefault<z.ZodEnum<{
16
+ orthographic: "orthographic";
17
+ perspective: "perspective";
18
+ }>>;
19
+ fov: z.ZodOptional<z.ZodNumber>;
20
+ zoom: z.ZodOptional<z.ZodNumber>;
21
+ }, z.core.$strip>>;
22
+ metadata: z.ZodDefault<z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
23
+ provenance: z.ZodOptional<z.ZodObject<{
24
+ refs: z.ZodArray<z.ZodObject<{
25
+ ns: z.ZodOptional<z.ZodString>;
26
+ id: z.ZodString;
27
+ role: z.ZodOptional<z.ZodEnum<{
28
+ absorbed: "absorbed";
29
+ alias: "alias";
30
+ derived: "derived";
31
+ piece: "piece";
32
+ primary: "primary";
33
+ }>>;
34
+ }, z.core.$strip>>;
35
+ lineage: z.ZodOptional<z.ZodObject<{
36
+ op: z.ZodEnum<{
37
+ attach: "attach";
38
+ convert: "convert";
39
+ duplicate: "duplicate";
40
+ import: "import";
41
+ "make-independent": "make-independent";
42
+ merge: "merge";
43
+ promote: "promote";
44
+ split: "split";
45
+ }>;
46
+ fromIds: z.ZodArray<z.ZodString>;
47
+ }, z.core.$strip>>;
48
+ }, z.core.$strip>>;
49
+ id: z.ZodDefault<z.ZodTemplateLiteral<`roofplane_${string}`>>;
50
+ type: z.ZodDefault<z.ZodLiteral<"test:roof-plane">>;
51
+ polygon: z.ZodArray<z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>>;
52
+ baseline: z.ZodTuple<[z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>, z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>], null>;
53
+ baseHeight: z.ZodNumber;
54
+ pitch: z.ZodNumber;
55
+ }, z.core.$strip>;
56
+ export type RoofPlane = z.infer<typeof RoofPlane>;
57
+ /** The plane's surface over its roof's origin at a plan point: its eave, plus the rise of the run off it. */
58
+ export declare function roofPlaneHeightAt(plane: RoofPlane, x: number, z: number): number;
59
+ /** Registers the planes' kind; the test setup restores the registry after each test. */
60
+ export declare function registerRoofPlanes(): void;
61
+ /**
62
+ * A plane over the plan rectangle [x0, z0] to [x1, z1] of its roof, its eave along z0 at `eave`,
63
+ * rising toward z1 at `pitch` in 12.
64
+ */
65
+ export declare function roofPlane(id: string, [x0, z0, x1, z1]: [number, number, number, number], eave: number, { pitch, parentId }?: {
66
+ pitch?: number;
67
+ parentId?: string | null;
68
+ }): AnyNode;
69
+ //# sourceMappingURL=roof-planes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"roof-planes.d.ts","sourceRoot":"","sources":["../../../src/registry/__fixtures__/roof-planes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAA;AAMjD;;;GAGG;AACH,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAOpB,CAAA;AACF,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,SAAS,CAAC,CAAA;AAEjD,6GAA6G;AAC7G,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,SAAS,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAIhF;AAOD,wFAAwF;AACxF,wBAAgB,kBAAkB,IAAI,IAAI,CASzC;AAED;;;GAGG;AACH,wBAAgB,SAAS,CACvB,EAAE,EAAE,MAAM,EACV,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAClD,IAAI,EAAE,MAAM,EACZ,EAAE,KAAS,EAAE,QAAe,EAAE,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAO,GAChF,OAAO,CAiBT"}
@@ -0,0 +1,59 @@
1
+ import { z } from 'zod';
2
+ import { BaseNode, nodeType, objectId } from '../../schema/base.js';
3
+ import { registerNode } from '../registry.js';
4
+ const Pt = z.tuple([z.number(), z.number()]);
5
+ /**
6
+ * A plugin's solved roof plane, as an architect's roof holds it (`capabilities.roofs`): an outline
7
+ * in its roof's plan, rising from a baseline at a pitch (rise per 12 of run).
8
+ */
9
+ export const RoofPlane = BaseNode.extend({
10
+ id: objectId('roofplane'),
11
+ type: nodeType('test:roof-plane'),
12
+ polygon: z.array(Pt),
13
+ baseline: z.tuple([Pt, Pt]),
14
+ baseHeight: z.number(),
15
+ pitch: z.number(),
16
+ });
17
+ /** The plane's surface over its roof's origin at a plan point: its eave, plus the rise of the run off it. */
18
+ export function roofPlaneHeightAt(plane, x, z) {
19
+ const [[ax, az], [bx, bz]] = plane.baseline;
20
+ const run = Math.abs((x - ax) * (bz - az) - (z - az) * (bx - ax)) / Math.hypot(bx - ax, bz - az);
21
+ return plane.baseHeight + (run * plane.pitch) / 12;
22
+ }
23
+ const roofs = {
24
+ outline: (node) => node.polygon,
25
+ heightAt: (node, x, z) => roofPlaneHeightAt(node, x, z),
26
+ };
27
+ /** Registers the planes' kind; the test setup restores the registry after each test. */
28
+ export function registerRoofPlanes() {
29
+ registerNode({
30
+ kind: 'test:roof-plane',
31
+ schemaVersion: 1,
32
+ schema: RoofPlane,
33
+ category: 'structure',
34
+ defaults: () => ({}),
35
+ capabilities: { roofs },
36
+ });
37
+ }
38
+ /**
39
+ * A plane over the plan rectangle [x0, z0] to [x1, z1] of its roof, its eave along z0 at `eave`,
40
+ * rising toward z1 at `pitch` in 12.
41
+ */
42
+ export function roofPlane(id, [x0, z0, x1, z1], eave, { pitch = 6, parentId = null } = {}) {
43
+ return RoofPlane.parse({
44
+ id,
45
+ parentId,
46
+ polygon: [
47
+ [x0, z0],
48
+ [x1, z0],
49
+ [x1, z1],
50
+ [x0, z1],
51
+ ],
52
+ baseline: [
53
+ [x0, z0],
54
+ [x1, z0],
55
+ ],
56
+ baseHeight: eave,
57
+ pitch,
58
+ });
59
+ }
@@ -2,8 +2,9 @@ export type { ArcResizeHandle, CornerRadiusHandle, Cursor, EditorApi, EndpointMo
2
2
  export { nodeMechanism, nodeMechanismParts, toggleMechanism, toggleMechanismPart, toggleNodeMechanism, } from './mechanism.js';
3
3
  export { bakePolicyOf, discoverPlugins, extendPluginDiscovery, floorPlacedCollides, getHostRefFields, getInspectorExtensions, getNodePluginId, getRegistryVersion, getSelectableKinds, hasRegistry3DMoveTool, isDrawnViaTool, isDrawnViaToolKind, isNodeDuplicable, isNodeKindEnabled, isPresettable, isPresettableKind, isRegistryMovable, isRegistrySelectable, isSelectionHighlightEnabled, kindsWithBakePolicy, kindsWithFloorplanScope, loadPlugin, nodeRegistry, onRegistryChange, type PluginDiscovery, registerNode, resolveFacingIndicator, setPluginDiscovery, } from './registry.js';
4
4
  export { type CascadeContext, type ChildQuery, cascadeDirty, collectDescendants, type SpatialQuery, } from './relations-resolver.js';
5
+ export { type RoofFaceShape, roofFaceOf, roofFaceShapes, validateRoofRelations } from './roofs.js';
5
6
  export { createSceneApi, type SceneStoreLike, type SceneToolHost } from './scene-api.js';
6
7
  export { type CloneNodesIntoOptions, type CloneNodesIntoResult, cloneNodesInto, collectSubtree, type Subtree, withoutSourceIdentity, } from './subtree.js';
7
- export type { Affordance, AlignmentFootprint, AlignmentFootprintConfig, AnyNodeDefinition, AssemblyHostConfig, AssemblyReference, AssetRef, BakeGeometryAsyncBuilder, BakeGeometryBuilder, BakePolicy, BakeReplaceRenderer, BatchableConfig, Capabilities, CapabilityCtx, CutsContext, CuttableConfig, DimensionTerminator, DimensionTextPosition, DistributionRole, DragAction, DuplicableConfig, DuplicateSubtreeCloneArgs, DuplicateSubtreeCloneResult, EditorCtx, EvaluationContext, ExportAnimationContext, ExportProfile, FaceHostCapability, FaceHostPlacementArgs, FaceHostPlacementResult, FaceHostStoredPlacementArgs, FaceHostStoredValidityArgs, FloorPlacedConfig, FloorPlacedFootprint, FloorPlacedFootprintContext, FloorPlacedFootprintResolver, FloorPlacedFootprintsResolver, FloorplanAffordance, FloorplanAffordanceModifiers, FloorplanAffordancePoint, FloorplanAffordanceSession, FloorplanGeometry, FloorplanMoveTarget, FloorplanMoveTargetSession, FloorplanPalette, FloorplanPoint, FloorplanScope, FloorplanStyle, GeometryContext, GridSnapPositionArgs, GroupMoveSnapArgs, GroupMoveSnapResult, HostableConfig, IconRef, InspectorExtension, Issue, KeyboardAction, KeyboardActions, LazyComponent, LiveTransformLike, McpOverrides, MeasurementContribution, MeasurementFeature, MeasurementFeatureBinding, MeasurementFeatureGeometry, MeasurementSnapKind, MechanismCapability, MechanismPart, Modifiers, MovableConfig, MovableParentFrame, NodeCategory, NodeDefinition, NodePort, NodeQuickAction, NodeQuickActionIcon, NodeQuickActionNodeScope, NodeQuickActionProvider, NodeQuickActionResult, NodeRegistry, PaintCapability, PaintEffectiveMaterialArgs, PaintPatchArgs, PaintPreviewArgs, PaintResolveArgs, PaintScopeId, PaintSurfaceScopes, ParamAction, ParametricDescriptor, ParamField, ParamGroup, ParentFrameSnapMatch, PascalBakeExtras, PascalPartTag, PlacementNotice, Plugin, PortRef, Presentation, QuickMeasurementMetric, QuickMeasurementQuantity, QuickMeasurementReport, ReferenceDeclaration, ReferenceDeletePolicy, ReferenceNamespace, ReferencePath, ReferencePresetPolicy, ReferenceRole, Relations, RendererSource, RevealClears, RevealConfig, RevealLayout, RevealPhase, RevealPlanPoint, RevealStyle, RoofAccessoryConfig, RotatableConfig, ScalableConfig, SceneActionCapability, SceneApi, SelectableConfig, SlotDeclaration, SnapPointKind, SnapProfile, SnappableConfig, SnapServicesLike, SurfacePoint, SurfaceQuery, SurfaceRole, SurfacesConfig, SystemContribution, ToolHint, ToolHintChip, ToolOption, ToolOptionChoice, Vec2, } from './types.js';
8
+ export type { Affordance, AlignmentFootprint, AlignmentFootprintConfig, AnyNodeDefinition, AssemblyHostConfig, AssemblyReference, AssetRef, BakeGeometryAsyncBuilder, BakeGeometryBuilder, BakePolicy, BakeReplaceRenderer, BatchableConfig, Capabilities, CapabilityCtx, CutsContext, CuttableConfig, DimensionTerminator, DimensionTextPosition, DistributionRole, DragAction, DuplicableConfig, DuplicateSubtreeCloneArgs, DuplicateSubtreeCloneResult, EditorCtx, EvaluationContext, ExportAnimationContext, ExportProfile, FaceHostCapability, FaceHostPlacementArgs, FaceHostPlacementResult, FaceHostStoredPlacementArgs, FaceHostStoredValidityArgs, FloorPlacedConfig, FloorPlacedFootprint, FloorPlacedFootprintContext, FloorPlacedFootprintResolver, FloorPlacedFootprintsResolver, FloorplanAffordance, FloorplanAffordanceModifiers, FloorplanAffordancePoint, FloorplanAffordanceSession, FloorplanGeometry, FloorplanMoveTarget, FloorplanMoveTargetSession, FloorplanPalette, FloorplanPoint, FloorplanScope, FloorplanStyle, GeometryContext, GridSnapPositionArgs, GroupMoveSnapArgs, GroupMoveSnapResult, HostableConfig, IconRef, InspectorExtension, Issue, KeyboardAction, KeyboardActions, LazyComponent, LiveTransformLike, McpOverrides, MeasurementContribution, MeasurementFeature, MeasurementFeatureBinding, MeasurementFeatureGeometry, MeasurementSnapKind, MechanismCapability, MechanismPart, Modifiers, MovableConfig, MovableParentFrame, NodeCategory, NodeDefinition, NodePort, NodeQuickAction, NodeQuickActionIcon, NodeQuickActionNodeScope, NodeQuickActionProvider, NodeQuickActionResult, NodeRegistry, PaintCapability, PaintEffectiveMaterialArgs, PaintPatchArgs, PaintPreviewArgs, PaintResolveArgs, PaintScopeId, PaintSurfaceScopes, ParamAction, ParametricDescriptor, ParamField, ParamGroup, ParentFrameSnapMatch, PascalBakeExtras, PascalPartTag, PlacementNotice, Plugin, PortRef, Presentation, QuickMeasurementMetric, QuickMeasurementQuantity, QuickMeasurementReport, ReferenceDeclaration, ReferenceDeletePolicy, ReferenceNamespace, ReferencePath, ReferencePresetPolicy, ReferenceRole, Relations, RendererSource, RevealClears, RevealConfig, RevealLayout, RevealPhase, RevealPlanPoint, RevealStyle, RoofAccessoryConfig, RoofsCapability, RotatableConfig, ScalableConfig, SceneActionCapability, SceneApi, SelectableConfig, SlotDeclaration, SnapPointKind, SnapProfile, SnappableConfig, SnapServicesLike, SurfacePoint, SurfaceQuery, SurfaceRole, SurfacesConfig, SystemContribution, ToolHint, ToolHintChip, ToolOption, ToolOptionChoice, Vec2, } from './types.js';
8
9
  export { validateNodeRelations } from './validate-relations.js';
9
10
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/registry/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/registry/index.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  export { nodeMechanism, nodeMechanismParts, toggleMechanism, toggleMechanismPart, toggleNodeMechanism, } from './mechanism.js';
2
2
  export { bakePolicyOf, discoverPlugins, extendPluginDiscovery, floorPlacedCollides, getHostRefFields, getInspectorExtensions, getNodePluginId, getRegistryVersion, getSelectableKinds, hasRegistry3DMoveTool, isDrawnViaTool, isDrawnViaToolKind, isNodeDuplicable, isNodeKindEnabled, isPresettable, isPresettableKind, isRegistryMovable, isRegistrySelectable, isSelectionHighlightEnabled, kindsWithBakePolicy, kindsWithFloorplanScope, loadPlugin, nodeRegistry, onRegistryChange, registerNode, resolveFacingIndicator, setPluginDiscovery, } from './registry.js';
3
3
  export { cascadeDirty, collectDescendants, } from './relations-resolver.js';
4
+ export { roofFaceOf, roofFaceShapes, validateRoofRelations } from './roofs.js';
4
5
  export { createSceneApi } from './scene-api.js';
5
6
  export { cloneNodesInto, collectSubtree, withoutSourceIdentity, } from './subtree.js';
6
7
  export { validateNodeRelations } from './validate-relations.js';
@@ -0,0 +1,34 @@
1
+ import type { AnyNode, RoofNode } from '../schema/index.js';
2
+ import type { RoofsCapability } from './types.js';
3
+ type Nodes = Readonly<Record<string, AnyNode | undefined>>;
4
+ type Pt = [number, number];
5
+ /**
6
+ * The node's `roofs` capability, when its kind declares one: the node is a face of the roof holding
7
+ * it. Undefined for a roof's segments, which are its masses, and for kinds this runtime has not
8
+ * registered.
9
+ */
10
+ export declare function roofFaceOf(node: AnyNode | undefined): RoofsCapability | undefined;
11
+ /** A face of a roof in the frame of what holds the roof: its level's metres. */
12
+ export type RoofFaceShape = {
13
+ id: string;
14
+ /** The face's outline in that plan, `[x, z]` corners. */
15
+ outline: Pt[];
16
+ /** Its surface at its lowest corner (the eave) and at its highest, over that frame's floor. */
17
+ low: number;
18
+ high: number;
19
+ };
20
+ /**
21
+ * A roof's faces (`capabilities.roofs`), in `children` order, each in the plan and heights of what
22
+ * holds the roof: the roof's turn and position applied to the face's own frame. A face of a kind
23
+ * this runtime has not registered, or one with no area, is not read.
24
+ */
25
+ export declare function roofFaceShapes(roof: RoofNode, nodes: Nodes): RoofFaceShape[];
26
+ /**
27
+ * A roof holds its segments and faces of other kinds (`capabilities.roofs`), nothing else: a child
28
+ * a write adds to it, of a registered kind that is neither, is refused. What it held before stays,
29
+ * so a roof saved before the rule still edits, and so does a kind this runtime has not registered
30
+ * (a plugin loaded elsewhere).
31
+ */
32
+ export declare function validateRoofRelations(roof: AnyNode, nodes: Nodes, before?: Nodes): void;
33
+ export {};
34
+ //# sourceMappingURL=roofs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"roofs.d.ts","sourceRoot":"","sources":["../../src/registry/roofs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AAElD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAE9C,KAAK,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,GAAG,SAAS,CAAC,CAAC,CAAA;AAC1D,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;AAE1B;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,OAAO,GAAG,SAAS,GAAG,eAAe,GAAG,SAAS,CAEjF;AAED,gFAAgF;AAChF,MAAM,MAAM,aAAa,GAAG;IAC1B,EAAE,EAAE,MAAM,CAAA;IACV,yDAAyD;IACzD,OAAO,EAAE,EAAE,EAAE,CAAA;IACb,+FAA+F;IAC/F,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;CACb,CAAA;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,GAAG,aAAa,EAAE,CA0B5E;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,EAAE,KAAK,GAAG,IAAI,CAavF"}
@@ -0,0 +1,61 @@
1
+ import { nodeRegistry } from './registry.js';
2
+ /**
3
+ * The node's `roofs` capability, when its kind declares one: the node is a face of the roof holding
4
+ * it. Undefined for a roof's segments, which are its masses, and for kinds this runtime has not
5
+ * registered.
6
+ */
7
+ export function roofFaceOf(node) {
8
+ return node ? nodeRegistry.get(node.type)?.capabilities.roofs : undefined;
9
+ }
10
+ /**
11
+ * A roof's faces (`capabilities.roofs`), in `children` order, each in the plan and heights of what
12
+ * holds the roof: the roof's turn and position applied to the face's own frame. A face of a kind
13
+ * this runtime has not registered, or one with no area, is not read.
14
+ */
15
+ export function roofFaceShapes(roof, nodes) {
16
+ // The roof's turn about +Y, as three.js turns its group: local +z toward +x.
17
+ const cos = Math.cos(roof.rotation ?? 0);
18
+ const sin = Math.sin(roof.rotation ?? 0);
19
+ const [x0, y0, z0] = roof.position;
20
+ return (roof.children ?? []).flatMap((id) => {
21
+ const face = nodes[id];
22
+ const roofs = roofFaceOf(face);
23
+ if (!(face && roofs))
24
+ return [];
25
+ const corners = roofs.outline(face);
26
+ const heights = corners.map(([x, z]) => roofs.heightAt(face, x, z));
27
+ if (corners.length < 3 ||
28
+ !corners.every(([x, z]) => Number.isFinite(x) && Number.isFinite(z)) ||
29
+ !heights.every(Number.isFinite))
30
+ return [];
31
+ return [
32
+ {
33
+ id: face.id,
34
+ outline: corners.map(([x, z]) => [x0 + x * cos + z * sin, z0 - x * sin + z * cos]),
35
+ low: y0 + Math.min(...heights),
36
+ high: y0 + Math.max(...heights),
37
+ },
38
+ ];
39
+ });
40
+ }
41
+ /**
42
+ * A roof holds its segments and faces of other kinds (`capabilities.roofs`), nothing else: a child
43
+ * a write adds to it, of a registered kind that is neither, is refused. What it held before stays,
44
+ * so a roof saved before the rule still edits, and so does a kind this runtime has not registered
45
+ * (a plugin loaded elsewhere).
46
+ */
47
+ export function validateRoofRelations(roof, nodes, before) {
48
+ if (roof.type !== 'roof')
49
+ return;
50
+ const prior = before?.[roof.id];
51
+ const held = new Set(prior?.type === 'roof' ? (prior.children ?? []) : []);
52
+ for (const id of roof.children ?? []) {
53
+ const child = nodes[id];
54
+ if (!child || held.has(id) || child.type === 'roof-segment')
55
+ continue;
56
+ const definition = nodeRegistry.get(child.type);
57
+ if (!definition || definition.capabilities.roofs)
58
+ continue;
59
+ throw new Error(`roof ${roof.id} holds its segments and faces of kinds that roof (capabilities.roofs): ${child.type} ${id} is neither.`);
60
+ }
61
+ }
@@ -1165,6 +1165,12 @@ export type NodeDefinition<S extends ZodObject<any>> = {
1165
1165
  * into the active building's plan coordinates below level architecture.
1166
1166
  */
1167
1167
  floorplanScope?: FloorplanScope;
1168
+ /**
1169
+ * Levels other than its own whose plans also draw this node: a stair that
1170
+ * climbs to an upper floor shows on that floor's plan too. The plan builds
1171
+ * it with `ctx.parent` set to the drawn level.
1172
+ */
1173
+ floorplanLinkedLevelIds?: (node: z.infer<S>) => readonly AnyNodeId[];
1168
1174
  /**
1169
1175
  * 2D drag affordances keyed by the string identifier emitted on
1170
1176
  * `endpoint-handle` (and similar interactive floor-plan primitives) via
@@ -1675,6 +1681,15 @@ export type Capabilities = {
1675
1681
  centerY?: number;
1676
1682
  };
1677
1683
  roofAccessory?: RoofAccessoryConfig;
1684
+ /**
1685
+ * The kind roofs: a node of it is one face of the roof that holds it (a plugin's solved roof
1686
+ * plane), as a segment is one of the roof's masses. A roof lists it in `children` beside its
1687
+ * segments and renders it through the kind's own renderer under the roof's transform, so hiding,
1688
+ * selecting or deleting the roof takes its faces with it; the readers of a roof's extent and
1689
+ * heights (the storey it covers, the walls it stands on, a view's frame) count it through
1690
+ * `outline` and `heightAt`. See `RoofsCapability`.
1691
+ */
1692
+ roofs?: RoofsCapability;
1678
1693
  /**
1679
1694
  * Kind cuts a hole in the ceiling surface it is attached to (e.g. recessed
1680
1695
  * downlights). The viewer's `CeilingSystem` calls this for each child of a
@@ -2082,6 +2097,21 @@ export type RoofAccessoryConfig = {
2082
2097
  */
2083
2098
  dirtyHandledByOwnSystem?: boolean;
2084
2099
  };
2100
+ /**
2101
+ * One face of the roof holding the node, in that roof's frame: metres, `[x, z]` in its plan and
2102
+ * heights up from its origin. A roof standing unturned at its level's origin holds its faces in
2103
+ * the level's frame.
2104
+ */
2105
+ export type RoofsCapability = {
2106
+ /** The face's outline in its roof's plan, corners in order. */
2107
+ outline: (node: AnyNode) => readonly (readonly [x: number, z: number])[];
2108
+ /**
2109
+ * The height of the face's top surface over its roof's origin at a point of its outline. Readers
2110
+ * take a face's lowest and highest points at the outline's corners, so a curved face gives an
2111
+ * outline dense enough to hold them.
2112
+ */
2113
+ heightAt: (node: AnyNode, x: number, z: number) => number;
2114
+ };
2085
2115
  /**
2086
2116
  * Capability for kinds that cut a hole in their host ceiling when the node is
2087
2117
  * attached to a ceiling surface (e.g. recessed downlights). The viewer's