@pascal-app/core 0.5.0 → 0.5.1

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.
@@ -1,5 +1,5 @@
1
1
  import type { ThreeEvent } from '@react-three/fiber';
2
- import type { BuildingNode, CeilingNode, DoorNode, ItemNode, LevelNode, RoofNode, RoofSegmentNode, SiteNode, SlabNode, StairNode, StairSegmentNode, WallNode, WindowNode, ZoneNode } from '../schema';
2
+ import type { BuildingNode, CeilingNode, DoorNode, FenceNode, ItemNode, LevelNode, RoofNode, RoofSegmentNode, SiteNode, SlabNode, StairNode, StairSegmentNode, WallNode, WindowNode, ZoneNode } from '../schema';
3
3
  import type { AnyNode } from '../schema/types';
4
4
  export interface GridEvent {
5
5
  /** World-space intersection point on the grid plane. */
@@ -21,6 +21,7 @@ export interface NodeEvent<T extends AnyNode = AnyNode> {
21
21
  nativeEvent: ThreeEvent<PointerEvent>;
22
22
  }
23
23
  export type WallEvent = NodeEvent<WallNode>;
24
+ export type FenceEvent = NodeEvent<FenceNode>;
24
25
  export type ItemEvent = NodeEvent<ItemNode>;
25
26
  export type SiteEvent = NodeEvent<SiteNode>;
26
27
  export type BuildingEvent = NodeEvent<BuildingNode>;
@@ -74,7 +75,7 @@ type ThumbnailEvents = {
74
75
  'thumbnail:before-capture': undefined;
75
76
  'thumbnail:after-capture': undefined;
76
77
  };
77
- type EditorEvents = GridEvents & NodeEvents<'wall', WallEvent> & NodeEvents<'item', ItemEvent> & NodeEvents<'site', SiteEvent> & NodeEvents<'building', BuildingEvent> & NodeEvents<'level', LevelEvent> & NodeEvents<'zone', ZoneEvent> & NodeEvents<'slab', SlabEvent> & NodeEvents<'ceiling', CeilingEvent> & NodeEvents<'roof', RoofEvent> & NodeEvents<'roof-segment', RoofSegmentEvent> & NodeEvents<'stair', StairEvent> & NodeEvents<'stair-segment', StairSegmentEvent> & NodeEvents<'window', WindowEvent> & NodeEvents<'door', DoorEvent> & CameraControlEvents & ToolEvents & PresetEvents & ThumbnailEvents;
78
+ type EditorEvents = GridEvents & NodeEvents<'wall', WallEvent> & NodeEvents<'fence', FenceEvent> & NodeEvents<'item', ItemEvent> & NodeEvents<'site', SiteEvent> & NodeEvents<'building', BuildingEvent> & NodeEvents<'level', LevelEvent> & NodeEvents<'zone', ZoneEvent> & NodeEvents<'slab', SlabEvent> & NodeEvents<'ceiling', CeilingEvent> & NodeEvents<'roof', RoofEvent> & NodeEvents<'roof-segment', RoofSegmentEvent> & NodeEvents<'stair', StairEvent> & NodeEvents<'stair-segment', StairSegmentEvent> & NodeEvents<'window', WindowEvent> & NodeEvents<'door', DoorEvent> & CameraControlEvents & ToolEvents & PresetEvents & ThumbnailEvents;
78
79
  export declare const emitter: import("mitt").Emitter<EditorEvents>;
79
80
  export {};
80
81
  //# sourceMappingURL=bus.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"bus.d.ts","sourceRoot":"","sources":["../../src/events/bus.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAEpD,OAAO,KAAK,EACV,YAAY,EACZ,WAAW,EACX,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,eAAe,EACf,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,gBAAgB,EAChB,QAAQ,EACR,UAAU,EACV,QAAQ,EACT,MAAM,WAAW,CAAA;AAClB,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAA;AAG9C,MAAM,WAAW,SAAS;IACxB,wDAAwD;IACxD,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;IAClC;;;;OAIG;IACH,aAAa,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;IACvC,WAAW,EAAE,UAAU,CAAC,YAAY,CAAC,CAAA;CACtC;AAED,MAAM,WAAW,SAAS,CAAC,CAAC,SAAS,OAAO,GAAG,OAAO;IACpD,IAAI,EAAE,CAAC,CAAA;IACP,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;IAClC,aAAa,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;IACvC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;IACjC,eAAe,EAAE,MAAM,IAAI,CAAA;IAC3B,WAAW,EAAE,UAAU,CAAC,YAAY,CAAC,CAAA;CACtC;AAED,MAAM,MAAM,SAAS,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAA;AAC3C,MAAM,MAAM,SAAS,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAA;AAC3C,MAAM,MAAM,SAAS,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAA;AAC3C,MAAM,MAAM,aAAa,GAAG,SAAS,CAAC,YAAY,CAAC,CAAA;AACnD,MAAM,MAAM,UAAU,GAAG,SAAS,CAAC,SAAS,CAAC,CAAA;AAC7C,MAAM,MAAM,SAAS,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAA;AAC3C,MAAM,MAAM,SAAS,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAA;AAC3C,MAAM,MAAM,YAAY,GAAG,SAAS,CAAC,WAAW,CAAC,CAAA;AACjD,MAAM,MAAM,SAAS,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAA;AAC3C,MAAM,MAAM,gBAAgB,GAAG,SAAS,CAAC,eAAe,CAAC,CAAA;AACzD,MAAM,MAAM,UAAU,GAAG,SAAS,CAAC,SAAS,CAAC,CAAA;AAC7C,MAAM,MAAM,iBAAiB,GAAG,SAAS,CAAC,gBAAgB,CAAC,CAAA;AAC3D,MAAM,MAAM,WAAW,GAAG,SAAS,CAAC,UAAU,CAAC,CAAA;AAC/C,MAAM,MAAM,SAAS,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAA;AAG3C,eAAO,MAAM,aAAa,0GAShB,CAAA;AAEV,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAA;AAExD,KAAK,UAAU,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,IAAI;KACpC,CAAC,IAAI,GAAG,CAAC,IAAI,WAAW,EAAE,GAAG,CAAC;CAChC,CAAA;AAED,KAAK,UAAU,GAAG;KACf,CAAC,IAAI,QAAQ,WAAW,EAAE,GAAG,SAAS;CACxC,CAAA;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;CACtB;AAED,MAAM,WAAW,sBAAsB;IACrC,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,KAAK,mBAAmB,GAAG;IACzB,sBAAsB,EAAE,kBAAkB,CAAA;IAC1C,uBAAuB,EAAE,kBAAkB,CAAA;IAC3C,yBAAyB,EAAE,kBAAkB,CAAA;IAC7C,0BAA0B,EAAE,SAAS,CAAA;IACrC,0BAA0B,EAAE,SAAS,CAAA;IACrC,2BAA2B,EAAE,SAAS,CAAA;IACtC,oCAAoC,EAAE,sBAAsB,CAAA;CAC7D,CAAA;AAED,KAAK,UAAU,GAAG;IAChB,aAAa,EAAE,SAAS,CAAA;CACzB,CAAA;AAED,KAAK,YAAY,GAAG;IAClB,2BAA2B,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;IACjE,0BAA0B,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAA;CACvE,CAAA;AAED,KAAK,eAAe,GAAG;IACrB,0BAA0B,EAAE,SAAS,CAAA;IACrC,yBAAyB,EAAE,SAAS,CAAA;CACrC,CAAA;AAED,KAAK,YAAY,GAAG,UAAU,GAC5B,UAAU,CAAC,MAAM,EAAE,SAAS,CAAC,GAC7B,UAAU,CAAC,MAAM,EAAE,SAAS,CAAC,GAC7B,UAAU,CAAC,MAAM,EAAE,SAAS,CAAC,GAC7B,UAAU,CAAC,UAAU,EAAE,aAAa,CAAC,GACrC,UAAU,CAAC,OAAO,EAAE,UAAU,CAAC,GAC/B,UAAU,CAAC,MAAM,EAAE,SAAS,CAAC,GAC7B,UAAU,CAAC,MAAM,EAAE,SAAS,CAAC,GAC7B,UAAU,CAAC,SAAS,EAAE,YAAY,CAAC,GACnC,UAAU,CAAC,MAAM,EAAE,SAAS,CAAC,GAC7B,UAAU,CAAC,cAAc,EAAE,gBAAgB,CAAC,GAC5C,UAAU,CAAC,OAAO,EAAE,UAAU,CAAC,GAC/B,UAAU,CAAC,eAAe,EAAE,iBAAiB,CAAC,GAC9C,UAAU,CAAC,QAAQ,EAAE,WAAW,CAAC,GACjC,UAAU,CAAC,MAAM,EAAE,SAAS,CAAC,GAC7B,mBAAmB,GACnB,UAAU,GACV,YAAY,GACZ,eAAe,CAAA;AAEjB,eAAO,MAAM,OAAO,sCAAuB,CAAA"}
1
+ {"version":3,"file":"bus.d.ts","sourceRoot":"","sources":["../../src/events/bus.ts"],"names":[],"mappings":"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"}
@@ -7,6 +7,7 @@ export declare const sceneRegistry: {
7
7
  ceiling: Set<string>;
8
8
  level: Set<string>;
9
9
  wall: Set<string>;
10
+ fence: Set<string>;
10
11
  item: Set<string>;
11
12
  slab: Set<string>;
12
13
  zone: Set<string>;
@@ -1 +1 @@
1
- {"version":3,"file":"scene-registry.d.ts","sourceRoot":"","sources":["../../../src/hooks/scene-registry/scene-registry.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAA;AAEnC,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;IAyBxB,gFAAgF;;CAOjF,CAAA;AAED,wBAAgB,WAAW,CACzB,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,MAAM,OAAO,aAAa,CAAC,MAAM,EACvC,GAAG,EAAE,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,QAAQ,CAAC,QAkBrC"}
1
+ {"version":3,"file":"scene-registry.d.ts","sourceRoot":"","sources":["../../../src/hooks/scene-registry/scene-registry.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAA;AAEnC,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;IA0BxB,gFAAgF;;CAOjF,CAAA;AAED,wBAAgB,WAAW,CACzB,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,MAAM,OAAO,aAAa,CAAC,MAAM,EACvC,GAAG,EAAE,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,QAAQ,CAAC,QAkBrC"}
@@ -11,6 +11,7 @@ export const sceneRegistry = {
11
11
  ceiling: new Set(),
12
12
  level: new Set(),
13
13
  wall: new Set(),
14
+ fence: new Set(),
14
15
  item: new Set(),
15
16
  slab: new Set(),
16
17
  zone: new Set(),
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export type { BuildingEvent, CameraControlEvent, CeilingEvent, DoorEvent, EventSuffix, GridEvent, ItemEvent, LevelEvent, NodeEvent, RoofEvent, RoofSegmentEvent, SiteEvent, SlabEvent, StairEvent, StairSegmentEvent, WallEvent, WindowEvent, ZoneEvent, } from './events/bus';
1
+ export type { BuildingEvent, CameraControlEvent, CeilingEvent, DoorEvent, EventSuffix, FenceEvent, GridEvent, ItemEvent, LevelEvent, NodeEvent, RoofEvent, RoofSegmentEvent, SiteEvent, SlabEvent, StairEvent, StairSegmentEvent, WallEvent, WindowEvent, ZoneEvent, } from './events/bus';
2
2
  export { emitter, eventSuffixes } from './events/bus';
3
3
  export { sceneRegistry, useRegistry, } from './hooks/scene-registry/scene-registry';
4
4
  export { pointInPolygon, spatialGridManager } from './hooks/spatial-grid/spatial-grid-manager';
@@ -10,6 +10,7 @@ export { baseMaterial, glassMaterial } from './materials';
10
10
  export * from './schema';
11
11
  export { type ControlValue, type ItemInteractiveState, useInteractive, } from './store/use-interactive';
12
12
  export { default as useLiveTransforms, type LiveTransform } from './store/use-live-transforms';
13
+ export { FenceSystem } from './systems/fence/fence-system';
13
14
  export { clearSceneHistory, default as useScene } from './store/use-scene';
14
15
  export { CeilingSystem } from './systems/ceiling/ceiling-system';
15
16
  export { DoorSystem } from './systems/door/door-system';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,aAAa,EACb,kBAAkB,EAClB,YAAY,EACZ,SAAS,EACT,WAAW,EACX,SAAS,EACT,SAAS,EACT,UAAU,EACV,SAAS,EACT,SAAS,EACT,gBAAgB,EAChB,SAAS,EACT,SAAS,EACT,UAAU,EACV,iBAAiB,EACjB,SAAS,EACT,WAAW,EACX,SAAS,GACV,MAAM,cAAc,CAAA;AACrB,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AACrD,OAAO,EACL,aAAa,EACb,WAAW,GACZ,MAAM,uCAAuC,CAAA;AAC9C,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,2CAA2C,CAAA;AAC9F,OAAO,EACL,mBAAmB,EACnB,cAAc,GACf,MAAM,wCAAwC,CAAA;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,wCAAwC,CAAA;AACxE,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAC7D,OAAO,EACL,oBAAoB,EACpB,sBAAsB,EACtB,KAAK,KAAK,EACV,iBAAiB,GAClB,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AACzD,cAAc,UAAU,CAAA;AACxB,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,oBAAoB,EACzB,cAAc,GACf,MAAM,yBAAyB,CAAA;AAChC,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,KAAK,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAC9F,OAAO,EAAE,iBAAiB,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAC1E,OAAO,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAA;AAChE,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAA;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAA;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAA;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAA;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAA;AAC1D,OAAO,EACL,mBAAmB,EACnB,sBAAsB,EACtB,oBAAoB,EACpB,gBAAgB,GACjB,MAAM,+BAA+B,CAAA;AACtC,OAAO,EACL,oBAAoB,EACpB,KAAK,OAAO,EACZ,UAAU,EACV,KAAK,aAAa,GACnB,MAAM,8BAA8B,CAAA;AACrC,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAA;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAA;AAC7D,YAAY,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAA;AAC3D,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAA;AAC9F,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,aAAa,EACb,kBAAkB,EAClB,YAAY,EACZ,SAAS,EACT,WAAW,EACX,UAAU,EACV,SAAS,EACT,SAAS,EACT,UAAU,EACV,SAAS,EACT,SAAS,EACT,gBAAgB,EAChB,SAAS,EACT,SAAS,EACT,UAAU,EACV,iBAAiB,EACjB,SAAS,EACT,WAAW,EACX,SAAS,GACV,MAAM,cAAc,CAAA;AACrB,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AACrD,OAAO,EACL,aAAa,EACb,WAAW,GACZ,MAAM,uCAAuC,CAAA;AAC9C,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,2CAA2C,CAAA;AAC9F,OAAO,EACL,mBAAmB,EACnB,cAAc,GACf,MAAM,wCAAwC,CAAA;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,wCAAwC,CAAA;AACxE,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAC7D,OAAO,EACL,oBAAoB,EACpB,sBAAsB,EACtB,KAAK,KAAK,EACV,iBAAiB,GAClB,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AACzD,cAAc,UAAU,CAAA;AACxB,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,oBAAoB,EACzB,cAAc,GACf,MAAM,yBAAyB,CAAA;AAChC,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,KAAK,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAC9F,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAA;AAC1D,OAAO,EAAE,iBAAiB,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAC1E,OAAO,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAA;AAChE,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAA;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAA;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAA;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAA;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAA;AAC1D,OAAO,EACL,mBAAmB,EACnB,sBAAsB,EACtB,oBAAoB,EACpB,gBAAgB,GACjB,MAAM,+BAA+B,CAAA;AACtC,OAAO,EACL,oBAAoB,EACpB,KAAK,OAAO,EACZ,UAAU,EACV,KAAK,aAAa,GACnB,MAAM,8BAA8B,CAAA;AACrC,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAA;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAA;AAC7D,YAAY,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAA;AAC3D,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAA;AAC9F,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA"}
package/dist/index.js CHANGED
@@ -9,6 +9,7 @@ export { baseMaterial, glassMaterial } from './materials';
9
9
  export * from './schema';
10
10
  export { useInteractive, } from './store/use-interactive';
11
11
  export { default as useLiveTransforms } from './store/use-live-transforms';
12
+ export { FenceSystem } from './systems/fence/fence-system';
12
13
  export { clearSceneHistory, default as useScene } from './store/use-scene';
13
14
  export { CeilingSystem } from './systems/ceiling/ceiling-system';
14
15
  export { DoorSystem } from './systems/door/door-system';
@@ -5,6 +5,7 @@ export { DEFAULT_MATERIALS, MaterialPreset, MaterialProperties, MaterialSchema,
5
5
  export { BuildingNode } from './nodes/building';
6
6
  export { CeilingNode } from './nodes/ceiling';
7
7
  export { DoorNode, DoorSegment } from './nodes/door';
8
+ export { FenceBaseStyle, FenceNode, FenceStyle } from './nodes/fence';
8
9
  export { GuideNode } from './nodes/guide';
9
10
  export type { AnimationEffect, Asset, AssetInput, Control, Effect, Interactive, LightEffect, SliderControl, TemperatureControl, ToggleControl, } from './nodes/item';
10
11
  export { getScaledDimensions, ItemNode } from './nodes/item';
@@ -14,7 +15,7 @@ export { RoofSegmentNode, RoofType } from './nodes/roof-segment';
14
15
  export { ScanNode } from './nodes/scan';
15
16
  export { SiteNode } from './nodes/site';
16
17
  export { SlabNode } from './nodes/slab';
17
- export { StairNode } from './nodes/stair';
18
+ export { StairNode, StairRailingMode, StairTopLandingMode, StairType } from './nodes/stair';
18
19
  export { AttachmentSide, StairSegmentNode, StairSegmentType } from './nodes/stair-segment';
19
20
  export { WallNode } from './nodes/wall';
20
21
  export { WindowNode } from './nodes/window';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/schema/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAA;AAE3E,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAEvC,OAAO,EAAE,KAAK,UAAU,EAAE,KAAK,YAAY,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAA;AAExF,OAAO,EACL,iBAAiB,EACjB,cAAc,EACd,kBAAkB,EAClB,cAAc,EACd,eAAe,GAChB,MAAM,YAAY,CAAA;AACnB,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAC7C,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AACzC,YAAY,EACV,eAAe,EACf,KAAK,EACL,UAAU,EACV,OAAO,EACP,MAAM,EACN,WAAW,EACX,WAAW,EACX,aAAa,EACb,kBAAkB,EAClB,aAAa,GACd,MAAM,cAAc,CAAA;AACrB,OAAO,EAAE,mBAAmB,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAC5D,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AACvC,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AAChE,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAEvC,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AACvC,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AACzC,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAC1F,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AACvC,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AACvC,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAErD,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/schema/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAA;AAE3E,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAEvC,OAAO,EAAE,KAAK,UAAU,EAAE,KAAK,YAAY,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAA;AAExF,OAAO,EACL,iBAAiB,EACjB,cAAc,EACd,kBAAkB,EAClB,cAAc,EACd,eAAe,GAChB,MAAM,YAAY,CAAA;AACnB,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAC7C,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AACpD,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AACrE,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AACzC,YAAY,EACV,eAAe,EACf,KAAK,EACL,UAAU,EACV,OAAO,EACP,MAAM,EACN,WAAW,EACX,WAAW,EACX,aAAa,EACb,kBAAkB,EAClB,aAAa,GACd,MAAM,cAAc,CAAA;AACrB,OAAO,EAAE,mBAAmB,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAC5D,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AACvC,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AAChE,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAEvC,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AACvC,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AAC3F,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAC1F,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AACvC,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AACvC,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAErD,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA"}
@@ -9,6 +9,7 @@ export { DEFAULT_MATERIALS, MaterialPreset, MaterialProperties, MaterialSchema,
9
9
  export { BuildingNode } from './nodes/building';
10
10
  export { CeilingNode } from './nodes/ceiling';
11
11
  export { DoorNode, DoorSegment } from './nodes/door';
12
+ export { FenceBaseStyle, FenceNode, FenceStyle } from './nodes/fence';
12
13
  export { GuideNode } from './nodes/guide';
13
14
  export { getScaledDimensions, ItemNode } from './nodes/item';
14
15
  export { LevelNode } from './nodes/level';
@@ -18,7 +19,7 @@ export { ScanNode } from './nodes/scan';
18
19
  // Nodes
19
20
  export { SiteNode } from './nodes/site';
20
21
  export { SlabNode } from './nodes/slab';
21
- export { StairNode } from './nodes/stair';
22
+ export { StairNode, StairRailingMode, StairTopLandingMode, StairType } from './nodes/stair';
22
23
  export { AttachmentSide, StairSegmentNode, StairSegmentType } from './nodes/stair-segment';
23
24
  export { WallNode } from './nodes/wall';
24
25
  export { WindowNode } from './nodes/window';
@@ -0,0 +1,51 @@
1
+ import { z } from 'zod';
2
+ export declare const FenceStyle: z.ZodEnum<{
3
+ slat: "slat";
4
+ rail: "rail";
5
+ privacy: "privacy";
6
+ }>;
7
+ export declare const FenceBaseStyle: z.ZodEnum<{
8
+ floating: "floating";
9
+ grounded: "grounded";
10
+ }>;
11
+ export declare const FenceNode: z.ZodObject<{
12
+ object: z.ZodDefault<z.ZodLiteral<"node">>;
13
+ name: z.ZodOptional<z.ZodString>;
14
+ parentId: z.ZodDefault<z.ZodNullable<z.ZodString>>;
15
+ visible: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
16
+ camera: z.ZodOptional<z.ZodObject<{
17
+ position: z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber], null>;
18
+ target: z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber], null>;
19
+ mode: z.ZodDefault<z.ZodEnum<{
20
+ perspective: "perspective";
21
+ orthographic: "orthographic";
22
+ }>>;
23
+ fov: z.ZodOptional<z.ZodNumber>;
24
+ zoom: z.ZodOptional<z.ZodNumber>;
25
+ }, z.core.$strip>>;
26
+ metadata: z.ZodDefault<z.ZodOptional<z.ZodJSONSchema>>;
27
+ id: z.ZodDefault<z.ZodTemplateLiteral<`fence_${string}`>>;
28
+ type: z.ZodDefault<z.ZodLiteral<"fence">>;
29
+ start: z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>;
30
+ end: z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>;
31
+ height: z.ZodDefault<z.ZodNumber>;
32
+ thickness: z.ZodDefault<z.ZodNumber>;
33
+ baseHeight: z.ZodDefault<z.ZodNumber>;
34
+ postSpacing: z.ZodDefault<z.ZodNumber>;
35
+ postSize: z.ZodDefault<z.ZodNumber>;
36
+ topRailHeight: z.ZodDefault<z.ZodNumber>;
37
+ groundClearance: z.ZodDefault<z.ZodNumber>;
38
+ edgeInset: z.ZodDefault<z.ZodNumber>;
39
+ baseStyle: z.ZodDefault<z.ZodEnum<{
40
+ floating: "floating";
41
+ grounded: "grounded";
42
+ }>>;
43
+ color: z.ZodDefault<z.ZodString>;
44
+ style: z.ZodDefault<z.ZodEnum<{
45
+ slat: "slat";
46
+ rail: "rail";
47
+ privacy: "privacy";
48
+ }>>;
49
+ }, z.core.$strip>;
50
+ export type FenceNode = z.infer<typeof FenceNode>;
51
+ //# sourceMappingURL=fence.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fence.d.ts","sourceRoot":"","sources":["../../../src/schema/nodes/fence.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAGvB,eAAO,MAAM,UAAU;;;;EAAsC,CAAA;AAC7D,eAAO,MAAM,cAAc;;;EAAmC,CAAA;AAE9D,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAyBrB,CAAA;AAED,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,SAAS,CAAC,CAAA"}
@@ -0,0 +1,29 @@
1
+ import dedent from 'dedent';
2
+ import { z } from 'zod';
3
+ import { BaseNode, nodeType, objectId } from '../base';
4
+ export const FenceStyle = z.enum(['slat', 'rail', 'privacy']);
5
+ export const FenceBaseStyle = z.enum(['floating', 'grounded']);
6
+ export const FenceNode = BaseNode.extend({
7
+ id: objectId('fence'),
8
+ type: nodeType('fence'),
9
+ start: z.tuple([z.number(), z.number()]),
10
+ end: z.tuple([z.number(), z.number()]),
11
+ height: z.number().default(1.8),
12
+ thickness: z.number().default(0.08),
13
+ baseHeight: z.number().default(0.22),
14
+ postSpacing: z.number().default(2),
15
+ postSize: z.number().default(0.1),
16
+ topRailHeight: z.number().default(0.04),
17
+ groundClearance: z.number().default(0),
18
+ edgeInset: z.number().default(0.015),
19
+ baseStyle: FenceBaseStyle.default('grounded'),
20
+ color: z.string().default('#ffffff'),
21
+ style: FenceStyle.default('slat'),
22
+ }).describe(dedent `
23
+ Fence node - used to represent a fence segment in the building/site level coordinate system
24
+ - start/end: fence endpoints in level coordinate system
25
+ - height/thickness: overall fence dimensions in meters
26
+ - baseHeight/postSpacing/postSize/topRailHeight: exact geometric controls from the plan3D fence model
27
+ - groundClearance/edgeInset/baseStyle: fence support and inset configuration
28
+ - color/style: visual appearance options
29
+ `);
@@ -17,7 +17,7 @@ export declare const LevelNode: z.ZodObject<{
17
17
  metadata: z.ZodDefault<z.ZodOptional<z.ZodJSONSchema>>;
18
18
  id: z.ZodDefault<z.ZodTemplateLiteral<`level_${string}`>>;
19
19
  type: z.ZodDefault<z.ZodLiteral<"level">>;
20
- children: z.ZodDefault<z.ZodArray<z.ZodUnion<readonly [z.ZodDefault<z.ZodTemplateLiteral<`wall_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`zone_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`slab_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`ceiling_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`roof_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`stair_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`scan_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`guide_${string}`>>]>>>;
20
+ children: z.ZodDefault<z.ZodArray<z.ZodUnion<readonly [z.ZodDefault<z.ZodTemplateLiteral<`wall_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`fence_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`zone_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`slab_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`ceiling_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`roof_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`stair_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`scan_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`guide_${string}`>>]>>>;
21
21
  level: z.ZodDefault<z.ZodNumber>;
22
22
  }, z.core.$strip>;
23
23
  export type LevelNode = z.infer<typeof LevelNode>;
@@ -1 +1 @@
1
- {"version":3,"file":"level.d.ts","sourceRoot":"","sources":["../../../src/schema/nodes/level.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAWvB,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;iBAyBrB,CAAA;AAED,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,SAAS,CAAC,CAAA"}
1
+ {"version":3,"file":"level.d.ts","sourceRoot":"","sources":["../../../src/schema/nodes/level.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAYvB,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;iBA0BrB,CAAA;AAED,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,SAAS,CAAC,CAAA"}
@@ -2,6 +2,7 @@ import dedent from 'dedent';
2
2
  import { z } from 'zod';
3
3
  import { BaseNode, nodeType, objectId } from '../base';
4
4
  import { CeilingNode } from './ceiling';
5
+ import { FenceNode } from './fence';
5
6
  import { GuideNode } from './guide';
6
7
  import { RoofNode } from './roof';
7
8
  import { ScanNode } from './scan';
@@ -15,6 +16,7 @@ export const LevelNode = BaseNode.extend({
15
16
  children: z
16
17
  .array(z.union([
17
18
  WallNode.shape.id,
19
+ FenceNode.shape.id,
18
20
  ZoneNode.shape.id,
19
21
  SlabNode.shape.id,
20
22
  CeilingNode.shape.id,
@@ -1,4 +1,22 @@
1
1
  import { z } from 'zod';
2
+ export declare const StairRailingMode: z.ZodEnum<{
3
+ none: "none";
4
+ left: "left";
5
+ right: "right";
6
+ both: "both";
7
+ }>;
8
+ export declare const StairType: z.ZodEnum<{
9
+ straight: "straight";
10
+ curved: "curved";
11
+ spiral: "spiral";
12
+ }>;
13
+ export declare const StairTopLandingMode: z.ZodEnum<{
14
+ none: "none";
15
+ integrated: "integrated";
16
+ }>;
17
+ export type StairRailingMode = z.infer<typeof StairRailingMode>;
18
+ export type StairType = z.infer<typeof StairType>;
19
+ export type StairTopLandingMode = z.infer<typeof StairTopLandingMode>;
2
20
  export declare const StairNode: z.ZodObject<{
3
21
  object: z.ZodDefault<z.ZodLiteral<"node">>;
4
22
  name: z.ZodOptional<z.ZodString>;
@@ -50,6 +68,32 @@ export declare const StairNode: z.ZodObject<{
50
68
  }, z.core.$strip>>;
51
69
  position: z.ZodDefault<z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber], null>>;
52
70
  rotation: z.ZodDefault<z.ZodNumber>;
71
+ stairType: z.ZodDefault<z.ZodEnum<{
72
+ straight: "straight";
73
+ curved: "curved";
74
+ spiral: "spiral";
75
+ }>>;
76
+ width: z.ZodDefault<z.ZodNumber>;
77
+ totalRise: z.ZodDefault<z.ZodNumber>;
78
+ stepCount: z.ZodDefault<z.ZodNumber>;
79
+ thickness: z.ZodDefault<z.ZodNumber>;
80
+ fillToFloor: z.ZodDefault<z.ZodBoolean>;
81
+ innerRadius: z.ZodDefault<z.ZodNumber>;
82
+ sweepAngle: z.ZodDefault<z.ZodNumber>;
83
+ topLandingMode: z.ZodDefault<z.ZodEnum<{
84
+ none: "none";
85
+ integrated: "integrated";
86
+ }>>;
87
+ topLandingDepth: z.ZodDefault<z.ZodNumber>;
88
+ showCenterColumn: z.ZodDefault<z.ZodBoolean>;
89
+ showStepSupports: z.ZodDefault<z.ZodBoolean>;
90
+ railingMode: z.ZodDefault<z.ZodEnum<{
91
+ none: "none";
92
+ left: "left";
93
+ right: "right";
94
+ both: "both";
95
+ }>>;
96
+ railingHeight: z.ZodDefault<z.ZodNumber>;
53
97
  children: z.ZodDefault<z.ZodArray<z.ZodDefault<z.ZodTemplateLiteral<`sseg_${string}`>>>>;
54
98
  }, z.core.$strip>;
55
99
  export type StairNode = z.infer<typeof StairNode>;
@@ -1 +1 @@
1
- {"version":3,"file":"stair.d.ts","sourceRoot":"","sources":["../../../src/schema/nodes/stair.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAKvB,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAkBrB,CAAA;AAED,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,SAAS,CAAC,CAAA"}
1
+ {"version":3,"file":"stair.d.ts","sourceRoot":"","sources":["../../../src/schema/nodes/stair.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAKvB,eAAO,MAAM,gBAAgB;;;;;EAA4C,CAAA;AACzE,eAAO,MAAM,SAAS;;;;EAA2C,CAAA;AACjE,eAAO,MAAM,mBAAmB;;;EAAiC,CAAA;AAEjE,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAA;AAC/D,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,SAAS,CAAC,CAAA;AACjD,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAA;AAErE,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA8CrB,CAAA;AAED,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,SAAS,CAAC,CAAA"}
@@ -3,6 +3,9 @@ import { z } from 'zod';
3
3
  import { BaseNode, nodeType, objectId } from '../base';
4
4
  import { MaterialSchema } from '../material';
5
5
  import { StairSegmentNode } from './stair-segment';
6
+ export const StairRailingMode = z.enum(['none', 'left', 'right', 'both']);
7
+ export const StairType = z.enum(['straight', 'curved', 'spiral']);
8
+ export const StairTopLandingMode = z.enum(['none', 'integrated']);
6
9
  export const StairNode = BaseNode.extend({
7
10
  id: objectId('stair'),
8
11
  type: nodeType('stair'),
@@ -10,13 +13,41 @@ export const StairNode = BaseNode.extend({
10
13
  position: z.tuple([z.number(), z.number(), z.number()]).default([0, 0, 0]),
11
14
  // Rotation around Y axis in radians
12
15
  rotation: z.number().default(0),
16
+ stairType: StairType.default('straight'),
17
+ width: z.number().default(1.0),
18
+ totalRise: z.number().default(2.5),
19
+ stepCount: z.number().default(10),
20
+ thickness: z.number().default(0.25),
21
+ fillToFloor: z.boolean().default(true),
22
+ innerRadius: z.number().default(0.9),
23
+ sweepAngle: z.number().default(Math.PI / 2),
24
+ topLandingMode: StairTopLandingMode.default('none'),
25
+ topLandingDepth: z.number().default(0.9),
26
+ showCenterColumn: z.boolean().default(true),
27
+ showStepSupports: z.boolean().default(true),
28
+ railingMode: StairRailingMode.default('none'),
29
+ railingHeight: z.number().default(0.92),
13
30
  // Child stair segment IDs
14
31
  children: z.array(StairSegmentNode.shape.id).default([]),
15
32
  }).describe(dedent `
16
33
  Stair node - a container for stair segments.
17
- Acts as a group that holds one or more StairSegmentNodes (flights and landings).
18
- Segments chain together based on their attachmentSide to form complex staircase shapes.
34
+ Acts as a group that either holds one or more StairSegmentNodes (straight stairs)
35
+ or stores stair-level geometry properties for curved stairs.
19
36
  - position: center position of the stair group
20
37
  - rotation: rotation around Y axis
21
- - children: array of StairSegmentNode IDs
38
+ - stairType: straight (segment-based), curved (arc-based), or spiral
39
+ - width: stair width
40
+ - totalRise: total stair height
41
+ - stepCount: number of visible steps
42
+ - thickness: stair slab / tread thickness
43
+ - fillToFloor: whether the stair mass fills down to the floor or uses tread thickness only
44
+ - innerRadius: inner curve radius for curved stairs
45
+ - sweepAngle: total curved stair sweep in radians
46
+ - topLandingMode: optional integrated top landing for spiral stairs
47
+ - topLandingDepth: depth used to size the integrated spiral top landing
48
+ - showCenterColumn: whether spiral stairs render a center column
49
+ - showStepSupports: whether spiral stairs render step support brackets
50
+ - railingMode: whether to render railings and on which side(s)
51
+ - railingHeight: top height of the railing above the stair surface
52
+ - children: array of StairSegmentNode IDs for straight stairs
22
53
  `);
@@ -175,7 +175,7 @@ export declare const AnyNode: z.ZodDiscriminatedUnion<[z.ZodObject<{
175
175
  metadata: z.ZodDefault<z.ZodOptional<z.ZodJSONSchema>>;
176
176
  id: z.ZodDefault<z.ZodTemplateLiteral<`level_${string}`>>;
177
177
  type: z.ZodDefault<z.ZodLiteral<"level">>;
178
- children: z.ZodDefault<z.ZodArray<z.ZodUnion<readonly [z.ZodDefault<z.ZodTemplateLiteral<`wall_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`zone_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`slab_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`ceiling_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`roof_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`stair_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`scan_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`guide_${string}`>>]>>>;
178
+ children: z.ZodDefault<z.ZodArray<z.ZodUnion<readonly [z.ZodDefault<z.ZodTemplateLiteral<`wall_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`fence_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`zone_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`slab_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`ceiling_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`roof_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`stair_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`scan_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`guide_${string}`>>]>>>;
179
179
  level: z.ZodDefault<z.ZodNumber>;
180
180
  }, z.core.$strip>, z.ZodObject<{
181
181
  object: z.ZodDefault<z.ZodLiteral<"node">>;
@@ -241,6 +241,44 @@ export declare const AnyNode: z.ZodDiscriminatedUnion<[z.ZodObject<{
241
241
  interior: "interior";
242
242
  exterior: "exterior";
243
243
  }>>;
244
+ }, z.core.$strip>, z.ZodObject<{
245
+ object: z.ZodDefault<z.ZodLiteral<"node">>;
246
+ name: z.ZodOptional<z.ZodString>;
247
+ parentId: z.ZodDefault<z.ZodNullable<z.ZodString>>;
248
+ visible: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
249
+ camera: z.ZodOptional<z.ZodObject<{
250
+ position: z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber], null>;
251
+ target: z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber], null>;
252
+ mode: z.ZodDefault<z.ZodEnum<{
253
+ perspective: "perspective";
254
+ orthographic: "orthographic";
255
+ }>>;
256
+ fov: z.ZodOptional<z.ZodNumber>;
257
+ zoom: z.ZodOptional<z.ZodNumber>;
258
+ }, z.core.$strip>>;
259
+ metadata: z.ZodDefault<z.ZodOptional<z.ZodJSONSchema>>;
260
+ id: z.ZodDefault<z.ZodTemplateLiteral<`fence_${string}`>>;
261
+ type: z.ZodDefault<z.ZodLiteral<"fence">>;
262
+ start: z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>;
263
+ end: z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>;
264
+ height: z.ZodDefault<z.ZodNumber>;
265
+ thickness: z.ZodDefault<z.ZodNumber>;
266
+ baseHeight: z.ZodDefault<z.ZodNumber>;
267
+ postSpacing: z.ZodDefault<z.ZodNumber>;
268
+ postSize: z.ZodDefault<z.ZodNumber>;
269
+ topRailHeight: z.ZodDefault<z.ZodNumber>;
270
+ groundClearance: z.ZodDefault<z.ZodNumber>;
271
+ edgeInset: z.ZodDefault<z.ZodNumber>;
272
+ baseStyle: z.ZodDefault<z.ZodEnum<{
273
+ floating: "floating";
274
+ grounded: "grounded";
275
+ }>>;
276
+ color: z.ZodDefault<z.ZodString>;
277
+ style: z.ZodDefault<z.ZodEnum<{
278
+ slat: "slat";
279
+ rail: "rail";
280
+ privacy: "privacy";
281
+ }>>;
244
282
  }, z.core.$strip>, z.ZodObject<{
245
283
  object: z.ZodDefault<z.ZodLiteral<"node">>;
246
284
  name: z.ZodOptional<z.ZodString>;
@@ -630,6 +668,32 @@ export declare const AnyNode: z.ZodDiscriminatedUnion<[z.ZodObject<{
630
668
  }, z.core.$strip>>;
631
669
  position: z.ZodDefault<z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber], null>>;
632
670
  rotation: z.ZodDefault<z.ZodNumber>;
671
+ stairType: z.ZodDefault<z.ZodEnum<{
672
+ straight: "straight";
673
+ curved: "curved";
674
+ spiral: "spiral";
675
+ }>>;
676
+ width: z.ZodDefault<z.ZodNumber>;
677
+ totalRise: z.ZodDefault<z.ZodNumber>;
678
+ stepCount: z.ZodDefault<z.ZodNumber>;
679
+ thickness: z.ZodDefault<z.ZodNumber>;
680
+ fillToFloor: z.ZodDefault<z.ZodBoolean>;
681
+ innerRadius: z.ZodDefault<z.ZodNumber>;
682
+ sweepAngle: z.ZodDefault<z.ZodNumber>;
683
+ topLandingMode: z.ZodDefault<z.ZodEnum<{
684
+ none: "none";
685
+ integrated: "integrated";
686
+ }>>;
687
+ topLandingDepth: z.ZodDefault<z.ZodNumber>;
688
+ showCenterColumn: z.ZodDefault<z.ZodBoolean>;
689
+ showStepSupports: z.ZodDefault<z.ZodBoolean>;
690
+ railingMode: z.ZodDefault<z.ZodEnum<{
691
+ none: "none";
692
+ left: "left";
693
+ right: "right";
694
+ both: "both";
695
+ }>>;
696
+ railingHeight: z.ZodDefault<z.ZodNumber>;
633
697
  children: z.ZodDefault<z.ZodArray<z.ZodDefault<z.ZodTemplateLiteral<`sseg_${string}`>>>>;
634
698
  }, z.core.$strip>, z.ZodObject<{
635
699
  object: z.ZodDefault<z.ZodLiteral<"node">>;
@@ -691,9 +755,9 @@ export declare const AnyNode: z.ZodDiscriminatedUnion<[z.ZodObject<{
691
755
  height: z.ZodDefault<z.ZodNumber>;
692
756
  stepCount: z.ZodDefault<z.ZodNumber>;
693
757
  attachmentSide: z.ZodDefault<z.ZodEnum<{
694
- front: "front";
695
758
  left: "left";
696
759
  right: "right";
760
+ front: "front";
697
761
  }>>;
698
762
  fillToFloor: z.ZodDefault<z.ZodBoolean>;
699
763
  thickness: z.ZodDefault<z.ZodNumber>;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/schema/types.ts"],"names":[],"mappings":"AAAA,OAAO,CAAC,MAAM,KAAK,CAAA;AAkBnB,eAAO,MAAM,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2BAiBlB,CAAA;AAEF,MAAM,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,OAAO,CAAC,CAAA;AAC7C,MAAM,MAAM,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;AACzC,MAAM,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/schema/types.ts"],"names":[],"mappings":"AAAA,OAAO,CAAC,MAAM,KAAK,CAAA;AAmBnB,eAAO,MAAM,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2BAkBlB,CAAA;AAEF,MAAM,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,OAAO,CAAC,CAAA;AAC7C,MAAM,MAAM,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;AACzC,MAAM,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA"}
@@ -2,6 +2,7 @@ import z from 'zod';
2
2
  import { BuildingNode } from './nodes/building';
3
3
  import { CeilingNode } from './nodes/ceiling';
4
4
  import { DoorNode } from './nodes/door';
5
+ import { FenceNode } from './nodes/fence';
5
6
  import { GuideNode } from './nodes/guide';
6
7
  import { ItemNode } from './nodes/item';
7
8
  import { LevelNode } from './nodes/level';
@@ -20,6 +21,7 @@ export const AnyNode = z.discriminatedUnion('type', [
20
21
  BuildingNode,
21
22
  LevelNode,
22
23
  WallNode,
24
+ FenceNode,
23
25
  ItemNode,
24
26
  ZoneNode,
25
27
  SlabNode,
@@ -284,7 +284,7 @@ useScene.temporal.subscribe((state) => {
284
284
  // Mark sibling nodes dirty so they can update their geometry
285
285
  // (e.g. adjacent walls need to recalculate miter/junction geometry)
286
286
  const parent = currentNodes[parentId];
287
- if (parent && 'children' in parent) {
287
+ if (parent && 'children' in parent && Array.isArray(parent.children)) {
288
288
  for (const childId of parent.children) {
289
289
  markDirty(childId);
290
290
  }
@@ -0,0 +1,2 @@
1
+ export declare const FenceSystem: () => null;
2
+ //# sourceMappingURL=fence-system.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fence-system.d.ts","sourceRoot":"","sources":["../../../src/systems/fence/fence-system.tsx"],"names":[],"mappings":"AA+HA,eAAO,MAAM,WAAW,YAiBvB,CAAA"}
@@ -0,0 +1,120 @@
1
+ import { useFrame } from '@react-three/fiber';
2
+ import * as THREE from 'three';
3
+ import { mergeGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils.js';
4
+ import { sceneRegistry } from '../../hooks/scene-registry/scene-registry';
5
+ import useScene from '../../store/use-scene';
6
+ function getStyleDefaults(style) {
7
+ if (style === 'privacy') {
8
+ return { spacingFactor: 0.42, postFactor: 1.35, baseFactor: 1.2, topFactor: 1.2 };
9
+ }
10
+ if (style === 'rail') {
11
+ return { spacingFactor: 0.68, postFactor: 0.8, baseFactor: 0.85, topFactor: 0.85 };
12
+ }
13
+ return { spacingFactor: 0.3, postFactor: 0.55, baseFactor: 1, topFactor: 0.75 };
14
+ }
15
+ function createFenceParts(fence) {
16
+ const parts = [];
17
+ const length = Math.max(Math.hypot(fence.end[0] - fence.start[0], fence.end[1] - fence.start[1]), 0.01);
18
+ const panelDepth = Math.max(fence.thickness, 0.03);
19
+ const clearance = Math.max(fence.groundClearance, 0);
20
+ const styleDefaults = getStyleDefaults(fence.style);
21
+ const baseHeight = Math.max(fence.baseHeight * styleDefaults.baseFactor, 0.04);
22
+ const topRailHeight = Math.max(fence.topRailHeight * styleDefaults.topFactor, 0.01);
23
+ const verticalHeight = Math.max(fence.height - baseHeight - topRailHeight, 0.08);
24
+ const postWidth = Math.max(fence.postSize * styleDefaults.postFactor, 0.01);
25
+ const spacing = Math.max(fence.postSpacing * styleDefaults.spacingFactor, postWidth * 1.2);
26
+ const edgeInset = Math.max(fence.edgeInset ?? 0.015, 0.005);
27
+ const isFloating = fence.baseStyle === 'floating';
28
+ const baseY = isFloating ? clearance : 0;
29
+ const effectiveBaseHeight = baseHeight;
30
+ if (!isFloating) {
31
+ parts.push({
32
+ position: [0, baseY + effectiveBaseHeight / 2, 0],
33
+ scale: [length, effectiveBaseHeight, panelDepth * 1.05],
34
+ });
35
+ parts.push({
36
+ position: [0, baseY + effectiveBaseHeight + verticalHeight * 0.15, 0],
37
+ scale: [length, topRailHeight * 0.8, panelDepth * 0.35],
38
+ });
39
+ }
40
+ const count = Math.max(2, Math.floor((length - edgeInset * 2) / spacing) + 1);
41
+ const step = count > 1 ? (length - edgeInset * 2) / (count - 1) : 0;
42
+ const startX = -length / 2 + edgeInset;
43
+ const verticalY = baseY + effectiveBaseHeight + verticalHeight / 2;
44
+ for (let index = 0; index < count; index += 1) {
45
+ const x = count === 1 ? 0 : startX + step * index;
46
+ let posX = x;
47
+ const isEdgePost = index === 0 || index === count - 1;
48
+ if (count > 1) {
49
+ if (index === 0)
50
+ posX = -length / 2 + edgeInset + postWidth / 2;
51
+ else if (index === count - 1)
52
+ posX = length / 2 - edgeInset - postWidth / 2;
53
+ }
54
+ const postHeight = isFloating && isEdgePost
55
+ ? effectiveBaseHeight + verticalHeight + topRailHeight + clearance
56
+ : verticalHeight;
57
+ const postY = isFloating && isEdgePost ? postHeight / 2 : verticalY;
58
+ parts.push({
59
+ position: [posX, postY, 0],
60
+ scale: [postWidth, postHeight, Math.max(panelDepth * 0.35, 0.012)],
61
+ });
62
+ }
63
+ parts.push({
64
+ position: [0, baseY + effectiveBaseHeight + verticalHeight + topRailHeight / 2, 0],
65
+ scale: [length, topRailHeight, Math.max(panelDepth * 0.55, 0.018)],
66
+ });
67
+ if (isFloating) {
68
+ parts.push({
69
+ position: [0, baseY + effectiveBaseHeight + topRailHeight / 2, 0],
70
+ scale: [length, topRailHeight, Math.max(panelDepth * 0.55, 0.018)],
71
+ });
72
+ }
73
+ return parts;
74
+ }
75
+ function generateFenceGeometry(fence) {
76
+ const parts = createFenceParts(fence);
77
+ const geometries = parts.map((part) => {
78
+ const geometry = new THREE.BoxGeometry(1, 1, 1);
79
+ geometry.scale(part.scale[0], part.scale[1], part.scale[2]);
80
+ geometry.translate(part.position[0], part.position[1], part.position[2]);
81
+ return geometry;
82
+ });
83
+ const merged = mergeGeometries(geometries, false) ?? new THREE.BufferGeometry();
84
+ geometries.forEach((geometry) => geometry.dispose());
85
+ merged.computeVertexNormals();
86
+ return merged;
87
+ }
88
+ function updateFenceGeometry(fenceId) {
89
+ const node = useScene.getState().nodes[fenceId];
90
+ if (!node || node.type !== 'fence')
91
+ return;
92
+ const mesh = sceneRegistry.nodes.get(fenceId);
93
+ if (!mesh)
94
+ return;
95
+ const newGeometry = generateFenceGeometry(node);
96
+ mesh.geometry.dispose();
97
+ mesh.geometry = newGeometry;
98
+ const centerX = (node.start[0] + node.end[0]) / 2;
99
+ const centerZ = (node.start[1] + node.end[1]) / 2;
100
+ const angle = Math.atan2(node.end[1] - node.start[1], node.end[0] - node.start[0]);
101
+ mesh.position.set(centerX, 0, centerZ);
102
+ mesh.rotation.set(0, -angle, 0);
103
+ }
104
+ export const FenceSystem = () => {
105
+ const dirtyNodes = useScene((state) => state.dirtyNodes);
106
+ const clearDirty = useScene((state) => state.clearDirty);
107
+ useFrame(() => {
108
+ if (dirtyNodes.size === 0)
109
+ return;
110
+ const nodes = useScene.getState().nodes;
111
+ dirtyNodes.forEach((id) => {
112
+ const node = nodes[id];
113
+ if (!node || node.type !== 'fence')
114
+ return;
115
+ updateFenceGeometry(id);
116
+ clearDirty(id);
117
+ });
118
+ }, 4);
119
+ return null;
120
+ };
@@ -246,14 +246,16 @@ function updateMergedStairGeometry(stairNode, group, nodes) {
246
246
  const mergedMesh = group.getObjectByName('merged-stair');
247
247
  if (!mergedMesh)
248
248
  return;
249
+ if (stairNode.stairType === 'curved' || stairNode.stairType === 'spiral') {
250
+ replaceMeshGeometry(mergedMesh, createEmptyGeometry());
251
+ return;
252
+ }
249
253
  const children = stairNode.children ?? [];
250
254
  const segments = children
251
255
  .map((childId) => nodes[childId])
252
256
  .filter((n) => n?.type === 'stair-segment');
253
257
  if (segments.length === 0) {
254
- mergedMesh.geometry.dispose();
255
- mergedMesh.geometry = new THREE.BufferGeometry();
256
- mergedMesh.geometry.setAttribute('position', new THREE.Float32BufferAttribute([], 3));
258
+ replaceMeshGeometry(mergedMesh, createEmptyGeometry());
257
259
  return;
258
260
  }
259
261
  // Compute chained transforms for segments
@@ -271,11 +273,8 @@ function updateMergedStairGeometry(stairNode, group, nodes) {
271
273
  geo.applyMatrix4(_matrix);
272
274
  geometries.push(geo);
273
275
  }
274
- const merged = mergeGeometries(geometries, false);
275
- if (merged) {
276
- mergedMesh.geometry.dispose();
277
- mergedMesh.geometry = merged;
278
- }
276
+ const merged = mergeGeometries(geometries, false) ?? createEmptyGeometry();
277
+ replaceMeshGeometry(mergedMesh, merged);
279
278
  // Dispose individual geometries
280
279
  for (const geo of geometries) {
281
280
  geo.dispose();
@@ -332,6 +331,312 @@ function rotateXZ(x, z, angle) {
332
331
  const sin = Math.sin(angle);
333
332
  return [x * cos + z * sin, -x * sin + z * cos];
334
333
  }
334
+ function createEmptyGeometry() {
335
+ const geometry = new THREE.BufferGeometry();
336
+ geometry.setAttribute('position', new THREE.Float32BufferAttribute([], 3));
337
+ return geometry;
338
+ }
339
+ function replaceMeshGeometry(mesh, geometry) {
340
+ mesh.geometry.dispose();
341
+ mesh.geometry = geometry;
342
+ }
343
+ function generateStairRailingGeometry(stairNode, segments, transforms) {
344
+ const railingMode = stairNode.railingMode ?? 'none';
345
+ if (railingMode === 'none') {
346
+ return createEmptyGeometry();
347
+ }
348
+ const railHeight = Math.max(0.5, stairNode.railingHeight ?? 0.92);
349
+ const midRailHeight = Math.max(railHeight * 0.45, 0.35);
350
+ const railRadius = 0.022;
351
+ const postRadius = 0.018;
352
+ const inset = 0.06;
353
+ const landingInset = 0.08;
354
+ const geometries = [];
355
+ const segmentRailPaths = buildStairRailPaths(segments, transforms, railingMode, inset, landingInset);
356
+ for (const segmentRailPath of segmentRailPaths) {
357
+ for (const sidePath of segmentRailPath.sidePaths) {
358
+ const points = sidePath.points;
359
+ if (points.length === 0)
360
+ continue;
361
+ geometries.push(...buildBalusterGeometries(points, railHeight, postRadius));
362
+ geometries.push(...buildOffsetRailSegmentGeometries(points, railHeight, railRadius));
363
+ geometries.push(...buildOffsetRailSegmentGeometries(points, midRailHeight, railRadius * 0.8));
364
+ }
365
+ }
366
+ for (let index = 1; index < segmentRailPaths.length; index++) {
367
+ const previousPath = segmentRailPaths[index - 1];
368
+ const currentPath = segmentRailPaths[index];
369
+ if (!(previousPath && currentPath && currentPath.connectFromPrevious))
370
+ continue;
371
+ if (previousPath.segment.segmentType === 'landing')
372
+ continue;
373
+ for (const sidePath of currentPath.sidePaths) {
374
+ if (currentPath.segment.segmentType === 'landing')
375
+ continue;
376
+ const currentPoint = sidePath.points[0];
377
+ if (!currentPoint)
378
+ continue;
379
+ const previousSidePath = [...previousPath.sidePaths]
380
+ .map((entry) => ({
381
+ entry,
382
+ distance: entry.points.length
383
+ ? entry.points[entry.points.length - 1].distanceTo(currentPoint)
384
+ : Number.POSITIVE_INFINITY,
385
+ }))
386
+ .sort((left, right) => left.distance - right.distance)[0]?.entry;
387
+ const previousPoint = previousSidePath && previousSidePath.points.length > 0
388
+ ? previousSidePath.points[previousSidePath.points.length - 1]
389
+ : null;
390
+ if (!(previousPoint && currentPoint))
391
+ continue;
392
+ const connectorPoints = [previousPoint, currentPoint];
393
+ geometries.push(...buildOffsetRailSegmentGeometries(connectorPoints, railHeight, railRadius));
394
+ geometries.push(...buildOffsetRailSegmentGeometries(connectorPoints, midRailHeight, railRadius * 0.8));
395
+ }
396
+ }
397
+ const merged = mergeGeometries(geometries, false) ?? createEmptyGeometry();
398
+ for (const geometry of geometries) {
399
+ geometry.dispose();
400
+ }
401
+ return merged;
402
+ }
403
+ function buildStairRailPaths(segments, transforms, railingMode, inset, landingInset) {
404
+ const layouts = computeStairRailLayouts(segments, transforms);
405
+ if (railingMode === 'both') {
406
+ const isStraightLineDoubleLandingLayout = segments.length === 4 &&
407
+ segments[0]?.segmentType === 'stair' &&
408
+ segments[1]?.segmentType === 'landing' &&
409
+ segments[2]?.segmentType === 'stair' &&
410
+ segments[2]?.attachmentSide === 'front' &&
411
+ segments[3]?.segmentType === 'landing' &&
412
+ segments[3]?.attachmentSide === 'front';
413
+ return layouts.map((layout, index) => {
414
+ const segment = layout.segment;
415
+ const previousSegment = index > 0 ? segments[index - 1] : undefined;
416
+ const nextSegment = index < segments.length - 1 ? segments[index + 1] : undefined;
417
+ const hideLandingRailing = segment.segmentType === 'landing' &&
418
+ previousSegment?.segmentType === 'stair' &&
419
+ nextSegment?.segmentType === 'stair';
420
+ const visualTurnSide = nextSegment?.attachmentSide;
421
+ const sideCandidates = hideLandingRailing
422
+ ? visualTurnSide === 'left'
423
+ ? ['front', 'right']
424
+ : visualTurnSide === 'right'
425
+ ? ['front', 'left']
426
+ : ['left', 'right']
427
+ : segment.segmentType === 'landing'
428
+ ? nextSegment?.segmentType === 'landing' && visualTurnSide === 'left'
429
+ ? ['front', 'right']
430
+ : nextSegment?.segmentType === 'landing' && visualTurnSide === 'right'
431
+ ? ['front', 'left']
432
+ : visualTurnSide === 'left'
433
+ ? ['right']
434
+ : visualTurnSide === 'right'
435
+ ? ['left']
436
+ : ['left', 'right']
437
+ : ['left', 'right'];
438
+ const sidePaths = sideCandidates
439
+ .map((side) => buildSegmentRailPath(layout, side, previousSegment, nextSegment, inset, landingInset))
440
+ .filter((entry) => entry !== null);
441
+ return {
442
+ segment,
443
+ sidePaths: isStraightLineDoubleLandingLayout && index === 1
444
+ ? (['left', 'right']
445
+ .map((side) => buildSegmentRailPath(layout, side, previousSegment, nextSegment, inset, landingInset))
446
+ .filter((entry) => entry !== null))
447
+ : sidePaths,
448
+ connectFromPrevious: index > 0 &&
449
+ !(previousSegment?.segmentType === 'landing' && segment.segmentType === 'landing'),
450
+ };
451
+ });
452
+ }
453
+ const isStraightLineDoubleLandingLayout = segments.length === 4 &&
454
+ segments[0]?.segmentType === 'stair' &&
455
+ segments[1]?.segmentType === 'landing' &&
456
+ segments[2]?.segmentType === 'stair' &&
457
+ segments[2]?.attachmentSide === 'front' &&
458
+ segments[3]?.segmentType === 'landing' &&
459
+ segments[3]?.attachmentSide === 'front';
460
+ const resolved = [];
461
+ layouts.forEach((layout, index) => {
462
+ const segment = layout.segment;
463
+ const previousSegment = index > 0 ? segments[index - 1] : undefined;
464
+ const nextSegment = index < segments.length - 1 ? segments[index + 1] : undefined;
465
+ const nextAttachmentSide = nextSegment?.attachmentSide;
466
+ const isMiddleLandingBetweenFlights = segment.segmentType === 'landing' &&
467
+ previousSegment?.segmentType === 'stair' &&
468
+ nextSegment?.segmentType === 'stair';
469
+ const suppressLandingRailing = segment.segmentType === 'landing' &&
470
+ nextSegment?.segmentType === 'landing' &&
471
+ nextAttachmentSide === railingMode;
472
+ const landingContinuesOnPreferredSide = segment.segmentType === 'landing'
473
+ ? nextAttachmentSide == null ||
474
+ nextAttachmentSide === 'front' ||
475
+ nextAttachmentSide === railingMode
476
+ : true;
477
+ const sidePaths = suppressLandingRailing
478
+ ? []
479
+ : segment.segmentType !== 'landing'
480
+ ? [
481
+ buildSegmentRailPath(layout, railingMode, previousSegment, nextSegment, inset, landingInset),
482
+ ]
483
+ : isStraightLineDoubleLandingLayout
484
+ ? [
485
+ buildSegmentRailPath(layout, railingMode, previousSegment, nextSegment, inset, landingInset),
486
+ ]
487
+ : isMiddleLandingBetweenFlights && railingMode === 'left'
488
+ ? nextAttachmentSide === 'right'
489
+ ? [
490
+ buildSegmentRailPath(layout, 'front', previousSegment, nextSegment, inset, landingInset),
491
+ buildSegmentRailPath(layout, 'left', previousSegment, nextSegment, inset, landingInset),
492
+ ]
493
+ : []
494
+ : isMiddleLandingBetweenFlights && railingMode === 'right'
495
+ ? nextAttachmentSide === 'left'
496
+ ? [
497
+ buildSegmentRailPath(layout, 'front', previousSegment, nextSegment, inset, landingInset),
498
+ buildSegmentRailPath(layout, 'right', previousSegment, nextSegment, inset, landingInset),
499
+ ]
500
+ : []
501
+ : nextSegment?.segmentType === 'landing' &&
502
+ nextAttachmentSide != null &&
503
+ nextAttachmentSide !== 'front' &&
504
+ nextAttachmentSide !== railingMode
505
+ ? [
506
+ buildSegmentRailPath(layout, 'front', previousSegment, nextSegment, inset, landingInset),
507
+ buildSegmentRailPath(layout, railingMode, previousSegment, nextSegment, inset, landingInset),
508
+ ]
509
+ : [
510
+ buildSegmentRailPath(layout, railingMode, previousSegment, nextSegment, inset, landingInset),
511
+ ];
512
+ resolved.push({
513
+ segment,
514
+ sidePaths: sidePaths.filter((entry) => entry !== null),
515
+ connectFromPrevious: index > 0 &&
516
+ !suppressLandingRailing &&
517
+ sidePaths.length > 0 &&
518
+ (segment.segmentType === 'landing' ? landingContinuesOnPreferredSide : true),
519
+ });
520
+ });
521
+ return resolved;
522
+ }
523
+ function computeStairRailLayouts(segments, transforms) {
524
+ return segments.map((segment, index) => {
525
+ const transform = transforms[index];
526
+ const [centerOffsetX, centerOffsetZ] = rotateXZ(0, segment.length / 2, transform.rotation);
527
+ return {
528
+ center: [transform.position[0] + centerOffsetX, transform.position[2] + centerOffsetZ],
529
+ elevation: transform.position[1],
530
+ rotation: transform.rotation,
531
+ segment,
532
+ };
533
+ });
534
+ }
535
+ function buildSegmentRailPath(layout, side, previousSegment, nextSegment, inset, landingInset) {
536
+ const segment = layout.segment;
537
+ const segmentSteps = Math.max(1, segment.segmentType === 'landing' ? 1 : segment.stepCount);
538
+ const segmentStepDepth = segment.length / segmentSteps;
539
+ const segmentStepHeight = segment.segmentType === 'landing' ? 0 : segment.height / segmentSteps;
540
+ const segmentTopThickness = getSegmentTopThickness(segment);
541
+ const flightSideOffset = side === 'left' ? segment.width / 2 - 0.045 : -segment.width / 2 + 0.045;
542
+ const flightStartX = previousSegment?.segmentType === 'landing' ? -segment.length / 2 + landingInset : -segment.length / 2;
543
+ const flightEndX = nextSegment?.segmentType === 'landing' ? segment.length / 2 - landingInset : segment.length / 2;
544
+ if (segment.segmentType === 'landing') {
545
+ return buildLandingRailPathFromScratch(layout, side, previousSegment, nextSegment, segmentTopThickness, landingInset);
546
+ }
547
+ return {
548
+ side,
549
+ points: [
550
+ ...(previousSegment?.segmentType === 'landing'
551
+ ? []
552
+ : [
553
+ toRailLayoutWorldPoint(layout, flightStartX, segmentTopThickness, flightSideOffset),
554
+ ]),
555
+ ...Array.from({ length: segmentSteps }).map((_, index) => toRailLayoutWorldPoint(layout, -segment.length / 2 + segmentStepDepth * index + segmentStepDepth / 2, segmentStepHeight * (index + 1), flightSideOffset)),
556
+ ...(nextSegment?.segmentType === 'landing'
557
+ ? []
558
+ : [
559
+ toRailLayoutWorldPoint(layout, flightEndX, segment.height, flightSideOffset),
560
+ ]),
561
+ ],
562
+ };
563
+ }
564
+ function buildLandingRailPathFromScratch(layout, side, previousSegment, nextSegment, topY, inset) {
565
+ const segment = layout.segment;
566
+ const backX = -segment.length / 2 + inset;
567
+ const frontX = segment.length / 2 - inset;
568
+ const leftZ = segment.width / 2 - inset;
569
+ const rightZ = -segment.width / 2 + inset;
570
+ const edgePoints = side === 'left'
571
+ ? [
572
+ toRailLayoutWorldPoint(layout, backX, topY, leftZ),
573
+ toRailLayoutWorldPoint(layout, frontX, topY, leftZ),
574
+ ]
575
+ : side === 'right'
576
+ ? [
577
+ toRailLayoutWorldPoint(layout, backX, topY, rightZ),
578
+ toRailLayoutWorldPoint(layout, frontX, topY, rightZ),
579
+ ]
580
+ : [
581
+ // When the next flight turns, rail the visible leading edge nearest the turn opening.
582
+ toRailLayoutWorldPoint(layout, previousSegment?.segmentType === 'stair' &&
583
+ nextSegment?.attachmentSide &&
584
+ nextSegment.attachmentSide !== 'front'
585
+ ? backX
586
+ : frontX, topY, leftZ),
587
+ toRailLayoutWorldPoint(layout, previousSegment?.segmentType === 'stair' &&
588
+ nextSegment?.attachmentSide &&
589
+ nextSegment.attachmentSide !== 'front'
590
+ ? backX
591
+ : frontX, topY, rightZ),
592
+ ];
593
+ return {
594
+ side,
595
+ points: edgePoints,
596
+ };
597
+ }
598
+ function toRailLayoutWorldPoint(layout, localX, localY, localZ) {
599
+ const [offsetX, offsetZ] = rotateXZ(localZ, localX, layout.rotation);
600
+ return new THREE.Vector3(layout.center[0] + offsetX, layout.elevation + localY, layout.center[1] + offsetZ);
601
+ }
602
+ function buildOffsetRailSegmentGeometries(points, heightOffset, radius) {
603
+ const geometries = [];
604
+ for (let index = 0; index < points.length - 1; index++) {
605
+ const start = points[index];
606
+ const end = points[index + 1];
607
+ if (!(start && end))
608
+ continue;
609
+ const segmentGeometry = createCylinderBetweenPoints(start.clone().add(new THREE.Vector3(0, heightOffset, 0)), end.clone().add(new THREE.Vector3(0, heightOffset, 0)), radius, 8);
610
+ if (segmentGeometry) {
611
+ geometries.push(segmentGeometry);
612
+ }
613
+ }
614
+ return geometries;
615
+ }
616
+ function buildBalusterGeometries(points, height, radius) {
617
+ const geometries = [];
618
+ for (const point of points) {
619
+ const geometry = new THREE.CylinderGeometry(radius, radius, Math.max(height, 0.05), 8);
620
+ geometry.translate(point.x, point.y + height / 2, point.z);
621
+ geometries.push(geometry);
622
+ }
623
+ return geometries;
624
+ }
625
+ function getSegmentTopThickness(segment) {
626
+ return Math.max(segment.thickness ?? 0.25, 0.02);
627
+ }
628
+ function createCylinderBetweenPoints(start, end, radius, radialSegments) {
629
+ const direction = new THREE.Vector3().subVectors(end, start);
630
+ const length = direction.length();
631
+ if (length <= 1e-5)
632
+ return null;
633
+ const midpoint = new THREE.Vector3().addVectors(start, end).multiplyScalar(0.5);
634
+ const quaternion = new THREE.Quaternion().setFromUnitVectors(new THREE.Vector3(0, 1, 0), direction.clone().normalize());
635
+ const geometry = new THREE.CylinderGeometry(radius, radius, length, radialSegments);
636
+ geometry.applyQuaternion(quaternion);
637
+ geometry.translate(midpoint.x, midpoint.y, midpoint.z);
638
+ return geometry;
639
+ }
335
640
  /**
336
641
  * Computes the absolute Y height of a segment by traversing the stair's segment chain.
337
642
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pascal-app/core",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "Core library for Pascal 3D building editor",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",