@ifc-lite/renderer 1.43.0 → 1.44.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/dist/camera-animation.d.ts +20 -32
  2. package/dist/camera-animation.d.ts.map +1 -1
  3. package/dist/camera-animation.js +71 -358
  4. package/dist/camera-animation.js.map +1 -1
  5. package/dist/camera-controls.d.ts +11 -51
  6. package/dist/camera-controls.d.ts.map +1 -1
  7. package/dist/camera-controls.js +140 -12
  8. package/dist/camera-controls.js.map +1 -1
  9. package/dist/camera-first-person.d.ts +52 -0
  10. package/dist/camera-first-person.d.ts.map +1 -0
  11. package/dist/camera-first-person.js +119 -0
  12. package/dist/camera-first-person.js.map +1 -0
  13. package/dist/camera-framing.d.ts +86 -0
  14. package/dist/camera-framing.d.ts.map +1 -0
  15. package/dist/camera-framing.js +230 -0
  16. package/dist/camera-framing.js.map +1 -0
  17. package/dist/camera-guards.d.ts +148 -0
  18. package/dist/camera-guards.d.ts.map +1 -0
  19. package/dist/camera-guards.js +150 -0
  20. package/dist/camera-guards.js.map +1 -0
  21. package/dist/camera-matrices.d.ts +14 -0
  22. package/dist/camera-matrices.d.ts.map +1 -0
  23. package/dist/camera-matrices.js +146 -0
  24. package/dist/camera-matrices.js.map +1 -0
  25. package/dist/camera-preset-view.d.ts +56 -0
  26. package/dist/camera-preset-view.d.ts.map +1 -0
  27. package/dist/camera-preset-view.js +180 -0
  28. package/dist/camera-preset-view.js.map +1 -0
  29. package/dist/camera-projection.d.ts +1 -1
  30. package/dist/camera-projection.d.ts.map +1 -1
  31. package/dist/camera-projection.js +83 -30
  32. package/dist/camera-projection.js.map +1 -1
  33. package/dist/camera-state.d.ts +62 -0
  34. package/dist/camera-state.d.ts.map +1 -0
  35. package/dist/camera-state.js +5 -0
  36. package/dist/camera-state.js.map +1 -0
  37. package/dist/camera.d.ts +22 -11
  38. package/dist/camera.d.ts.map +1 -1
  39. package/dist/camera.js +75 -120
  40. package/dist/camera.js.map +1 -1
  41. package/dist/environment.d.ts.map +1 -1
  42. package/dist/environment.js +23 -1
  43. package/dist/environment.js.map +1 -1
  44. package/dist/index.d.ts +154 -8
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/index.js +294 -45
  47. package/dist/index.js.map +1 -1
  48. package/dist/math.d.ts +49 -1
  49. package/dist/math.d.ts.map +1 -1
  50. package/dist/math.js +122 -67
  51. package/dist/math.js.map +1 -1
  52. package/dist/renderer-symbolic-overlays.d.ts.map +1 -1
  53. package/dist/renderer-symbolic-overlays.js +20 -25
  54. package/dist/renderer-symbolic-overlays.js.map +1 -1
  55. package/dist/scene-geometry.d.ts +32 -0
  56. package/dist/scene-geometry.d.ts.map +1 -1
  57. package/dist/scene-geometry.js +66 -0
  58. package/dist/scene-geometry.js.map +1 -1
  59. package/dist/scene.d.ts.map +1 -1
  60. package/dist/scene.js +22 -97
  61. package/dist/scene.js.map +1 -1
  62. package/dist/section-2d-lift.d.ts +105 -0
  63. package/dist/section-2d-lift.d.ts.map +1 -0
  64. package/dist/section-2d-lift.js +162 -0
  65. package/dist/section-2d-lift.js.map +1 -0
  66. package/dist/section-2d-line-buffer.d.ts +58 -0
  67. package/dist/section-2d-line-buffer.d.ts.map +1 -0
  68. package/dist/section-2d-line-buffer.js +109 -0
  69. package/dist/section-2d-line-buffer.js.map +1 -0
  70. package/dist/section-2d-overlay.d.ts +28 -77
  71. package/dist/section-2d-overlay.d.ts.map +1 -1
  72. package/dist/section-2d-overlay.js +161 -593
  73. package/dist/section-2d-overlay.js.map +1 -1
  74. package/dist/section-plane-basis.d.ts +22 -3
  75. package/dist/section-plane-basis.d.ts.map +1 -1
  76. package/dist/section-plane-basis.js +51 -10
  77. package/dist/section-plane-basis.js.map +1 -1
  78. package/dist/section-plane.d.ts.map +1 -1
  79. package/dist/section-plane.js +10 -1
  80. package/dist/section-plane.js.map +1 -1
  81. package/dist/shaders/section-2d-overlay.wgsl.d.ts +102 -0
  82. package/dist/shaders/section-2d-overlay.wgsl.d.ts.map +1 -0
  83. package/dist/shaders/section-2d-overlay.wgsl.js +281 -0
  84. package/dist/shaders/section-2d-overlay.wgsl.js.map +1 -0
  85. package/package.json +3 -2
@@ -0,0 +1,281 @@
1
+ /* This Source Code Form is subject to the terms of the Mozilla Public
2
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
+ /**
5
+ * WGSL shaders for the section 2D overlay (cut-cap fills + overlay lines).
6
+ *
7
+ * Two pipelines share this file because they deliberately share ONE 160-byte
8
+ * uniform LAYOUT: the fill shader reads `viewProj … params2` (the first 144 B)
9
+ * and the line shader reads `viewProj`, `planeOffset` and the appended
10
+ * `lineColor` at byte offset 144. They share the layout, not the storage — each
11
+ * draw site gets its own record in one buffer, addressed by a dynamic
12
+ * bind-group offset; see {@link SECTION_2D_UNIFORM_SLOT_INDEX}.
13
+ * The two `struct Uniforms` declarations below
14
+ * must therefore stay field-for-field compatible, and every TypeScript writer
15
+ * must address the buffer through `SECTION_2D_UNIFORM_SLOTS` rather than a
16
+ * hand-written index. `section-2d-uniform-layout.test.ts` parses both structs
17
+ * out of these sources and fails if either drifts from the slot table.
18
+ */
19
+ /**
20
+ * Float32 indices of each field in the shared uniform buffer.
21
+ *
22
+ * WGSL std140-style layout: `mat4x4<f32>` is 64 B (16 floats) and each
23
+ * `vec4<f32>` is 16 B (4 floats), all naturally aligned, so the offsets are a
24
+ * simple running sum. These are FLOAT offsets — multiply by 4 for the byte
25
+ * offsets quoted in the shader comments.
26
+ */
27
+ export const SECTION_2D_UNIFORM_SLOTS = {
28
+ /** mat4x4<f32> — floats 0..15 (bytes 0..63) */
29
+ viewProj: 0,
30
+ /** vec4<f32> — floats 16..19 (bytes 64..79) */
31
+ planeOffset: 16,
32
+ /** vec4<f32> — floats 20..23 (bytes 80..95) */
33
+ capFillColor: 20,
34
+ /** vec4<f32> — floats 24..27 (bytes 96..111) */
35
+ capStrokeColor: 24,
36
+ /** vec4<f32> x=patternId, y=spacingPx, z=angleRad, w=widthPx — floats 28..31 */
37
+ params: 28,
38
+ /** vec4<f32> x=secondaryAngleRad, y,z,w reserved — floats 32..35 */
39
+ params2: 32,
40
+ /** vec4<f32> overlay / section-cut line colour — floats 36..39 (bytes 144..159) */
41
+ lineColor: 36,
42
+ };
43
+ /** Total float count of one uniform record (40 floats = 160 bytes). */
44
+ export const SECTION_2D_UNIFORM_FLOATS = 40;
45
+ /** Total byte size of one uniform record. */
46
+ export const SECTION_2D_UNIFORM_BYTES = SECTION_2D_UNIFORM_FLOATS * 4;
47
+ /**
48
+ * One uniform slot per draw site.
49
+ *
50
+ * The overlay renderer issues up to six draws into a **single** render pass —
51
+ * the section cut cap plus five world-space line families — and every one of
52
+ * them needs a different `lineColor` (and, for the cap, a different
53
+ * `planeOffset` and the whole cap-style tail). With one record shared between
54
+ * them the last `queue.writeBuffer` before submit is what the GPU sees for all
55
+ * six: `writeBuffer` is a *queue* operation, so it is applied before the
56
+ * command buffer that references it executes, no matter where in the encoding
57
+ * it was issued. Every earlier draw therefore rendered with the last draw's
58
+ * uniforms — most visibly, the clash box's colour bleeding onto the annotation,
59
+ * alignment, grid and DXF overlays, and the cap losing its fill colour and
60
+ * hatch to the zeroed tail the line draws write.
61
+ *
62
+ * Each site owning a fixed slot, addressed with a dynamic bind-group offset,
63
+ * is what makes the six draws independent while keeping the buffer, the layout
64
+ * and the bind group under the single owner issue #2456 insisted on. A slot per
65
+ * site (rather than a bump allocator) needs no per-frame reset hook: each site
66
+ * draws at most once per pass.
67
+ */
68
+ export const SECTION_2D_UNIFORM_SLOT_INDEX = {
69
+ /** The section cut cap: fill + outline, which share one record by design. */
70
+ sectionCut: 0,
71
+ annotation: 1,
72
+ alignment: 2,
73
+ grid: 3,
74
+ dxf: 4,
75
+ clashBox: 5,
76
+ };
77
+ /** How many uniform records the shared buffer holds. */
78
+ export const SECTION_2D_UNIFORM_SLOT_COUNT = 6;
79
+ /**
80
+ * Byte stride between uniform slots for `device`.
81
+ *
82
+ * A dynamic offset must be a multiple of `minUniformBufferOffsetAlignment`,
83
+ * which a device reports for itself (256 is the guaranteed-supported default,
84
+ * but a device may allow less). Round the 160-byte record up to that alignment
85
+ * rather than assuming either number.
86
+ */
87
+ export function sectionUniformSlotStride(device) {
88
+ const alignment = device.limits?.minUniformBufferOffsetAlignment ?? 256;
89
+ const safe = Number.isFinite(alignment) && alignment > 0 ? alignment : 256;
90
+ return Math.ceil(SECTION_2D_UNIFORM_BYTES / safe) * safe;
91
+ }
92
+ /**
93
+ * Fill shader for the cut cap.
94
+ *
95
+ * Applies the user-defined cap style (fill colour + screen-space hatch) on top
96
+ * of the EXACT 2D section polygons produced by SectionCutter. Per-vertex colour
97
+ * DRIVES the fill when a polygon opts in (alpha >= 0): a material-layer
98
+ * wall/slab fills each layer with its own IfcMaterial colour, matching the 3D
99
+ * build-up. Polygons that pass the sentinel alpha -1 fall back to the uniform
100
+ * cap style, so single-material cuts read as one architectural section exactly
101
+ * as before. Hatch + stroke apply over either base.
102
+ */
103
+ export const SECTION_2D_CAP_FILL_WGSL = /* wgsl */ `
104
+ struct Uniforms {
105
+ viewProj: mat4x4<f32>,
106
+ planeOffset: vec4<f32>, // Small offset to render slightly in front of section plane
107
+ capFillColor: vec4<f32>,
108
+ capStrokeColor: vec4<f32>,
109
+ // x=patternId, y=spacingPx, z=angleRad, w=widthPx
110
+ params: vec4<f32>,
111
+ // x=secondaryAngleRad, y,z,w reserved
112
+ params2: vec4<f32>,
113
+ }
114
+ @binding(0) @group(0) var<uniform> uniforms: Uniforms;
115
+
116
+ struct VertexInput {
117
+ @location(0) position: vec3<f32>,
118
+ @location(1) color: vec4<f32>,
119
+ }
120
+
121
+ struct VertexOutput {
122
+ @builtin(position) position: vec4<f32>,
123
+ @location(0) color: vec4<f32>,
124
+ }
125
+
126
+ @vertex
127
+ fn vs_main(input: VertexInput) -> VertexOutput {
128
+ var output: VertexOutput;
129
+ let offsetPos = input.position + uniforms.planeOffset.xyz;
130
+ output.position = uniforms.viewProj * vec4<f32>(offsetPos, 1.0);
131
+ output.color = input.color;
132
+ return output;
133
+ }
134
+
135
+ // Screen-space hatch pattern helpers (ported from section-cap.wgsl).
136
+ fn lineMask(u: f32, s: f32, w: f32) -> f32 {
137
+ let f = fract(u / s) * s;
138
+ let d = min(f, s - f);
139
+ return 1.0 - smoothstep(w * 0.5, w * 0.5 + 1.0, d);
140
+ }
141
+ fn rotate(p: vec2<f32>, a: f32) -> vec2<f32> {
142
+ let c = cos(a);
143
+ let s = sin(a);
144
+ return vec2<f32>(c * p.x - s * p.y, s * p.x + c * p.y);
145
+ }
146
+ fn hatchIntensity(fragCoord: vec2<f32>, patternId: u32, spacing: f32, angle: f32, width: f32, angle2: f32) -> f32 {
147
+ let p = fragCoord;
148
+ if (patternId == 0u) { return 0.0; } // solid
149
+ if (patternId == 1u) { // diagonal
150
+ let r = rotate(p, angle);
151
+ return lineMask(r.x, spacing, width);
152
+ }
153
+ if (patternId == 2u) { // cross-hatch
154
+ let r = rotate(p, angle);
155
+ let r2 = rotate(p, angle2);
156
+ return max(lineMask(r.x, spacing, width), lineMask(r2.x, spacing, width));
157
+ }
158
+ if (patternId == 3u) { return lineMask(p.y, spacing, width); } // horizontal
159
+ if (patternId == 4u) { return lineMask(p.x, spacing, width); } // vertical
160
+ if (patternId == 5u) {
161
+ // Concrete (ISO 128-50): clean regular dot grid. The previous
162
+ // version layered dashes on top which looked noisy and broken.
163
+ // Dots sit at every grid intersection; radius scales with
164
+ // stroke width so the user's width slider works consistently.
165
+ let gx = p.x - round(p.x / spacing) * spacing;
166
+ let gy = p.y - round(p.y / spacing) * spacing;
167
+ let d = sqrt(gx * gx + gy * gy);
168
+ let radius = max(1.0, width * 1.2);
169
+ return 1.0 - smoothstep(radius, radius + 1.0, d);
170
+ }
171
+ if (patternId == 6u) { // brick
172
+ let bandH = spacing;
173
+ let band = floor(p.y / bandH);
174
+ // Signed parity. p is @builtin(position) today, so it is a
175
+ // framebuffer coordinate and never negative — but u32() of a
176
+ // negative float is an out-of-range conversion in WGSL, and the
177
+ // moment this hatch is fed anything but fragCoord (a world- or
178
+ // drawing-space variant) the running bond would break across
179
+ // y = 0 in a backend-dependent way. i32(-1) & 1 is 1, so the
180
+ // parity alternates correctly in both directions.
181
+ let offset = select(0.0, bandH, (i32(band) & 1) == 1);
182
+ let horiz = lineMask(p.y, bandH, width);
183
+ let vertPos = p.x + offset * 0.5;
184
+ let vert = step(fract(vertPos / (bandH * 2.0)), 0.02);
185
+ return max(horiz, vert);
186
+ }
187
+ if (patternId == 7u) { // insulation
188
+ let y = spacing * 0.5 * sin(p.x * 6.2831853 / spacing) + p.y;
189
+ return lineMask(y, spacing, width);
190
+ }
191
+ return 0.0;
192
+ }
193
+
194
+ struct FragOut {
195
+ @location(0) color: vec4<f32>,
196
+ @location(1) objectId: vec4<f32>,
197
+ }
198
+
199
+ @fragment
200
+ fn fs_main(input: VertexOutput) -> FragOut {
201
+ let patternId = u32(uniforms.params.x + 0.5);
202
+ let spacing = max(2.0, uniforms.params.y);
203
+ let angle = uniforms.params.z;
204
+ let width = max(1.0, uniforms.params.w);
205
+ let angle2 = uniforms.params2.x;
206
+
207
+ let h = hatchIntensity(input.position.xy, patternId, spacing, angle, width, angle2);
208
+ // Per-polygon colour (a material-layer slab fills with its own
209
+ // IfcMaterial RGBA) overrides the uniform cap fill when present.
210
+ // Polygons without a colour carry the sentinel alpha -1 and fall back
211
+ // to the user's cap style, byte-identically. Hatch + stroke apply over
212
+ // whichever base is chosen, so the architectural hatch still works.
213
+ let useVertex = input.color.a >= 0.0;
214
+ let baseFill = select(uniforms.capFillColor, input.color, useVertex);
215
+ let rgb = mix(baseFill.rgb, uniforms.capStrokeColor.rgb, h * uniforms.capStrokeColor.a);
216
+ let a = max(baseFill.a, h * uniforms.capStrokeColor.a);
217
+
218
+ var out: FragOut;
219
+ out.color = vec4<f32>(rgb, a);
220
+ out.objectId = vec4<f32>(0.0, 0.0, 0.0, 0.0);
221
+ return out;
222
+ }
223
+ `;
224
+ /**
225
+ * Line shader for the section-cut outline and the standalone world-space line
226
+ * overlays (annotation / alignment / grid / DXF / clash box).
227
+ */
228
+ export const SECTION_2D_OVERLAY_LINE_WGSL = /* wgsl */ `
229
+ // Mirrors the fill shader's uniform layout so both pipelines can share
230
+ // one buffer; the line shader only reads viewProj, planeOffset and the
231
+ // appended lineColor (byte offset 144). capFillColor/… are unused here but
232
+ // declared for correct field offsets.
233
+ struct Uniforms {
234
+ viewProj: mat4x4<f32>,
235
+ planeOffset: vec4<f32>,
236
+ capFillColor: vec4<f32>,
237
+ capStrokeColor: vec4<f32>,
238
+ params: vec4<f32>,
239
+ params2: vec4<f32>,
240
+ lineColor: vec4<f32>,
241
+ }
242
+ @binding(0) @group(0) var<uniform> uniforms: Uniforms;
243
+
244
+ struct VertexInput {
245
+ @location(0) position: vec3<f32>,
246
+ }
247
+
248
+ struct VertexOutput {
249
+ @builtin(position) position: vec4<f32>,
250
+ }
251
+
252
+ @vertex
253
+ fn vs_main(input: VertexInput) -> VertexOutput {
254
+ var output: VertexOutput;
255
+ let offsetPos = input.position + uniforms.planeOffset.xyz;
256
+ let clip = uniforms.viewProj * vec4<f32>(offsetPos, 1.0);
257
+ // Reverse-Z decal nudge for lines coplanar with model faces
258
+ // (issue #812). WebGPU forbids depthStencil.depthBias on non-
259
+ // triangle topologies, so we do the equivalent in clip space:
260
+ // adding a small positive multiple of clip.w raises NDC z by a
261
+ // constant after the w-divide, which under reverse-Z means
262
+ // "slightly closer" — enough to beat MSAA jitter on annotation
263
+ // lines that ride exactly on a wall/floor.
264
+ output.position = vec4<f32>(clip.x, clip.y, clip.z + 5e-5 * clip.w, clip.w);
265
+ return output;
266
+ }
267
+
268
+ struct FragOutLine {
269
+ @location(0) color: vec4<f32>,
270
+ @location(1) objectId: vec4<f32>,
271
+ }
272
+
273
+ @fragment
274
+ fn fs_main(input: VertexOutput) -> FragOutLine {
275
+ var out: FragOutLine;
276
+ out.color = uniforms.lineColor; // consumer-themeable (defaults black)
277
+ out.objectId = vec4<f32>(0.0, 0.0, 0.0, 0.0);
278
+ return out;
279
+ }
280
+ `;
281
+ //# sourceMappingURL=section-2d-overlay.wgsl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"section-2d-overlay.wgsl.js","sourceRoot":"","sources":["../../src/shaders/section-2d-overlay.wgsl.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAE/D;;;;;;;;;;;;;;GAcG;AAEH;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG;IACtC,+CAA+C;IAC/C,QAAQ,EAAE,CAAC;IACX,+CAA+C;IAC/C,WAAW,EAAE,EAAE;IACf,+CAA+C;IAC/C,YAAY,EAAE,EAAE;IAChB,gDAAgD;IAChD,cAAc,EAAE,EAAE;IAClB,gFAAgF;IAChF,MAAM,EAAE,EAAE;IACV,oEAAoE;IACpE,OAAO,EAAE,EAAE;IACX,mFAAmF;IACnF,SAAS,EAAE,EAAE;CACL,CAAC;AAEX,uEAAuE;AACvE,MAAM,CAAC,MAAM,yBAAyB,GAAG,EAAE,CAAC;AAE5C,6CAA6C;AAC7C,MAAM,CAAC,MAAM,wBAAwB,GAAG,yBAAyB,GAAG,CAAC,CAAC;AAEtE;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,CAAC,MAAM,6BAA6B,GAAG;IAC3C,6EAA6E;IAC7E,UAAU,EAAE,CAAC;IACb,UAAU,EAAE,CAAC;IACb,SAAS,EAAE,CAAC;IACZ,IAAI,EAAE,CAAC;IACP,GAAG,EAAE,CAAC;IACN,QAAQ,EAAE,CAAC;CACH,CAAC;AAEX,wDAAwD;AACxD,MAAM,CAAC,MAAM,6BAA6B,GAAG,CAAC,CAAC;AAE/C;;;;;;;GAOG;AACH,MAAM,UAAU,wBAAwB,CAAC,MAAiB;IACxD,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,+BAA+B,IAAI,GAAG,CAAC;IACxE,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC;IAC3E,OAAO,IAAI,CAAC,IAAI,CAAC,wBAAwB,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;AAC3D,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,UAAU,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAwH5C,CAAC;AAER;;;GAGG;AACH,MAAM,CAAC,MAAM,4BAA4B,GAAG,UAAU,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAoDhD,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ifc-lite/renderer",
3
- "version": "1.43.0",
3
+ "version": "1.44.0",
4
4
  "description": "WebGPU renderer for IFC-Lite",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -13,7 +13,7 @@
13
13
  }
14
14
  },
15
15
  "dependencies": {
16
- "@ifc-lite/geometry": "^3.7.1",
16
+ "@ifc-lite/geometry": "^3.8.0",
17
17
  "@ifc-lite/spatial": "^1.14.13"
18
18
  },
19
19
  "devDependencies": {
@@ -46,6 +46,7 @@
46
46
  ],
47
47
  "scripts": {
48
48
  "build": "pnpm exec tsc",
49
+ "typecheck": "node ../../scripts/typecheck-tests.mjs",
49
50
  "dev": "pnpm exec tsc --watch",
50
51
  "test": "tsx --test src/*.test.ts"
51
52
  }