door_models 5.4.8 → 5.5.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.
@@ -0,0 +1,2813 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var React = require('react');
6
+ var jsxRuntime = require('react/jsx-runtime');
7
+ var THREE = require('three');
8
+ var drei = require('@react-three/drei');
9
+ var csg = require('@react-three/csg');
10
+ var three = require('@react-spring/three');
11
+
12
+ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
13
+
14
+ function _interopNamespace(e) {
15
+ if (e && e.__esModule) return e;
16
+ var n = Object.create(null);
17
+ if (e) {
18
+ Object.keys(e).forEach(function (k) {
19
+ if (k !== 'default') {
20
+ var d = Object.getOwnPropertyDescriptor(e, k);
21
+ Object.defineProperty(n, k, d.get ? d : {
22
+ enumerable: true,
23
+ get: function () { return e[k]; }
24
+ });
25
+ }
26
+ });
27
+ }
28
+ n["default"] = e;
29
+ return Object.freeze(n);
30
+ }
31
+
32
+ var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
33
+ var THREE__namespace = /*#__PURE__*/_interopNamespace(THREE);
34
+
35
+ function _defineProperty(e, r, t) {
36
+ return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
37
+ value: t,
38
+ enumerable: !0,
39
+ configurable: !0,
40
+ writable: !0
41
+ }) : e[r] = t, e;
42
+ }
43
+ function ownKeys(e, r) {
44
+ var t = Object.keys(e);
45
+ if (Object.getOwnPropertySymbols) {
46
+ var o = Object.getOwnPropertySymbols(e);
47
+ r && (o = o.filter(function (r) {
48
+ return Object.getOwnPropertyDescriptor(e, r).enumerable;
49
+ })), t.push.apply(t, o);
50
+ }
51
+ return t;
52
+ }
53
+ function _objectSpread2(e) {
54
+ for (var r = 1; r < arguments.length; r++) {
55
+ var t = null != arguments[r] ? arguments[r] : {};
56
+ r % 2 ? ownKeys(Object(t), !0).forEach(function (r) {
57
+ _defineProperty(e, r, t[r]);
58
+ }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) {
59
+ Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r));
60
+ });
61
+ }
62
+ return e;
63
+ }
64
+ function _objectWithoutProperties(e, t) {
65
+ if (null == e) return {};
66
+ var o,
67
+ r,
68
+ i = _objectWithoutPropertiesLoose(e, t);
69
+ if (Object.getOwnPropertySymbols) {
70
+ var n = Object.getOwnPropertySymbols(e);
71
+ for (r = 0; r < n.length; r++) o = n[r], -1 === t.indexOf(o) && {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]);
72
+ }
73
+ return i;
74
+ }
75
+ function _objectWithoutPropertiesLoose(r, e) {
76
+ if (null == r) return {};
77
+ var t = {};
78
+ for (var n in r) if ({}.hasOwnProperty.call(r, n)) {
79
+ if (-1 !== e.indexOf(n)) continue;
80
+ t[n] = r[n];
81
+ }
82
+ return t;
83
+ }
84
+ function _toPrimitive(t, r) {
85
+ if ("object" != typeof t || !t) return t;
86
+ var e = t[Symbol.toPrimitive];
87
+ if (void 0 !== e) {
88
+ var i = e.call(t, r || "default");
89
+ if ("object" != typeof i) return i;
90
+ throw new TypeError("@@toPrimitive must return a primitive value.");
91
+ }
92
+ return ("string" === r ? String : Number)(t);
93
+ }
94
+ function _toPropertyKey(t) {
95
+ var i = _toPrimitive(t, "string");
96
+ return "symbol" == typeof i ? i : i + "";
97
+ }
98
+
99
+ const doorSettings = {
100
+ NOF: {
101
+ topThk: "0mm",
102
+ sidesThk: "0mm",
103
+ frameDepth: 0
104
+ },
105
+ AF20_40: {
106
+ frameDepth: 103,
107
+ topThk: "20mm",
108
+ sidesThk: "20mm",
109
+ doorStopOffset: "42mm",
110
+ doorStopWidth: 20,
111
+ doorStopDepth: 13,
112
+ notchWidth: "20mm",
113
+ notchDepth: "40mm",
114
+ notchposition: 0.01,
115
+ gasketDepth: 5,
116
+ gasketWidth: 5,
117
+ doorDepth: 40
118
+ },
119
+ AF20_50: {
120
+ doorDepth: 50,
121
+ frameDepth: 103,
122
+ topThk: "20mm",
123
+ sidesThk: "20mm",
124
+ doorStopOffset: "30mm",
125
+ doorStopWidth: 20,
126
+ doorStopDepth: 13,
127
+ notchWidth: "20mm",
128
+ notchDepth: "40mm",
129
+ notchposition: 0.01,
130
+ gasketDepth: 5,
131
+ gasketWidth: 5
132
+ },
133
+ AF40_40: {
134
+ frameDepth: 103,
135
+ topThk: "40mm",
136
+ sidesThk: "40mm",
137
+ doorStopOffset: "42mm",
138
+ doorStopWidth: 20,
139
+ doorStopDepth: 13,
140
+ notchWidth: "20mm",
141
+ notchDepth: "40mm",
142
+ notchposition: 0.01,
143
+ gasketDepth: 5,
144
+ gasketWidth: 5,
145
+ doorDepth: 40
146
+ },
147
+ AF40_50: {
148
+ doorDepth: 50,
149
+ frameDepth: 103,
150
+ topThk: "40mm",
151
+ sidesThk: "40mm",
152
+ doorStopOffset: "30mm",
153
+ doorStopWidth: 20,
154
+ doorStopDepth: 13,
155
+ notchWidth: "20mm",
156
+ notchDepth: "40mm",
157
+ notchposition: 0.01,
158
+ gasketDepth: 5,
159
+ gasketWidth: 5
160
+ },
161
+ WF_40: {
162
+ doorDepth: 39.6,
163
+ frameDepth: 103,
164
+ topThk: "31.6mm",
165
+ sidesThk: "31.6mm",
166
+ doorStopOffset: "0mm",
167
+ doorStopWidth: 16.4,
168
+ doorStopDepth: 58.4,
169
+ notchWidth: "20mm",
170
+ notchDepth: "40mm",
171
+ notchposition: 0.01,
172
+ gasketDepth: 5,
173
+ gasketWidth: 5
174
+ },
175
+ WF_50: {
176
+ doorDepth: 51.6,
177
+ frameDepth: 103,
178
+ topThk: "31.6mm",
179
+ sidesThk: "31.6mm",
180
+ doorStopOffset: "0mm",
181
+ doorStopWidth: 16,
182
+ doorStopDepth: 46.4,
183
+ notchWidth: "20mm",
184
+ notchDepth: "40mm",
185
+ notchposition: 0.01,
186
+ gasketDepth: 5,
187
+ gasketWidth: 5
188
+ },
189
+ WF_100: {
190
+ doorDepth: 102,
191
+ frameDepth: 102,
192
+ topThk: "52mm",
193
+ sidesThk: "52mm",
194
+ doorStopOffset: "17.6mm",
195
+ doorStopWidth: 16.4,
196
+ doorStopDepth: 44.8,
197
+ notchWidth: "16mm",
198
+ notchDepth: "66.5mm",
199
+ notchposition: 0.02,
200
+ gasketDepth: 5,
201
+ gasketWidth: 5,
202
+ secondDoorStopWidth: 29,
203
+ secondDoorStopDepth: 17.6,
204
+ secondDoorStopOffset: 0
205
+ },
206
+ WF_FLI: {
207
+ doorDepth: 51,
208
+ frameDepth: 102,
209
+ topThk: "52mm",
210
+ sidesThk: "52mm",
211
+ doorStopOffset: "17.4mm",
212
+ doorStopWidth: 16.4,
213
+ doorStopDepth: 34,
214
+ notchWidth: "14mm",
215
+ notchDepth: "66mm",
216
+ notchposition: 0.02,
217
+ gasketDepth: 5,
218
+ gasketWidth: 5,
219
+ secondDoorStopWidth: 29,
220
+ secondDoorStopDepth: 17.6,
221
+ secondDoorStopOffset: 0
222
+ },
223
+ WDGF_WDG100: {
224
+ doorDepth: 86,
225
+ frameDepth: 86,
226
+ topThk: "50mm",
227
+ sidesThk: "50mm",
228
+ doorStopOffset: "34mm",
229
+ doorStopWidth: 13,
230
+ doorStopDepth: 30,
231
+ notchWidth: "14mm",
232
+ notchDepth: "66mm",
233
+ notchposition: 0.02,
234
+ gasketDepth: 4.5,
235
+ gasketWidth: 5,
236
+ secondDoorStopWidth: 23,
237
+ secondDoorStopDepth: 30,
238
+ secondDoorStopOffset: 13
239
+ },
240
+ MXF_40: {
241
+ doorDepth: 38,
242
+ frameDepth: 103,
243
+ topThk: "18mm",
244
+ sidesThk: "18mm",
245
+ doorStopOffset: "45mm",
246
+ doorStopWidth: 15,
247
+ doorStopDepth: 15,
248
+ notchWidth: "20mm",
249
+ notchDepth: "40mm",
250
+ notchposition: 0.01,
251
+ gasketDepth: 5,
252
+ gasketWidth: 5
253
+ },
254
+ MXF_50: {
255
+ doorDepth: 50,
256
+ frameDepth: 103,
257
+ topThk: "18.2mm",
258
+ sidesThk: "18.2mm",
259
+ doorStopOffset: "33mm",
260
+ doorStopWidth: 15,
261
+ doorStopDepth: 15,
262
+ notchWidth: "20mm",
263
+ notchDepth: "40mm",
264
+ notchposition: 0.01,
265
+ gasketDepth: 5,
266
+ gasketWidth: 5
267
+ },
268
+ MXCAF_40: {
269
+ doorDepth: 38,
270
+ frameDepth: 103,
271
+ topThk: "18mm",
272
+ sidesThk: "18mm",
273
+ doorStopOffset: "45mm",
274
+ doorStopWidth: 15,
275
+ doorStopDepth: 15,
276
+ notchWidth: "20mm",
277
+ notchDepth: "40mm",
278
+ notchposition: 0.01,
279
+ gasketDepth: 5,
280
+ gasketWidth: 5
281
+ },
282
+ MXCAF_50: {
283
+ doorDepth: 50,
284
+ frameDepth: 103,
285
+ topThk: "18.2mm",
286
+ sidesThk: "18.2mm",
287
+ doorStopOffset: "33mm",
288
+ doorStopWidth: 15,
289
+ doorStopDepth: 15,
290
+ notchWidth: "20mm",
291
+ notchDepth: "40mm",
292
+ notchposition: 0.01,
293
+ gasketDepth: 5,
294
+ gasketWidth: 5
295
+ }
296
+ };
297
+
298
+ const _excluded = ["doorDepth"];
299
+ const ConfiguratorContext = /*#__PURE__*/React.createContext(undefined);
300
+ const ConfiguratorProvider = _ref => {
301
+ let {
302
+ children,
303
+ initialIs2D = false,
304
+ initialMaterials = {}
305
+ } = _ref;
306
+ const [materials, setMaterials] = React.useState(initialMaterials);
307
+ const [is2D, setIs2D] = React.useState(initialIs2D);
308
+ const [isPlaneVisible, setIsPlaneVisible] = React.useState(false);
309
+ const [isFrameVisible, setIsFrameVisible] = React.useState(true);
310
+ const [cpid, setCpid] = React.useState("");
311
+ const [totalHeight, setTotalHeight] = React.useState(2700);
312
+ const [totalWidth, setTotalWidth] = React.useState(974);
313
+ const [totalDepth, setTotalDepth] = React.useState(103);
314
+ const [glassVisible, setGlassVisible] = React.useState(false);
315
+ const [glassDepth, setGlassDepth] = React.useState(8);
316
+ const [isDoubleDoor, setIsDoubleDoor] = React.useState(false);
317
+ const [door, setDoor] = React.useState({
318
+ doorMaterial: "door_material",
319
+ doorWidth: 0,
320
+ doorHeight: 0,
321
+ theDoorDepth: 40,
322
+ doorPivot: "left",
323
+ doorOpening: "out"
324
+ });
325
+ const [doorFrame, setDoorFrame] = React.useState({
326
+ frameMaterial: "doorFrame_material",
327
+ doorStopMaterial: "doorStop_material",
328
+ gasketMaterial: "gasket_material",
329
+ hingeMaterial: "hinge_material",
330
+ glassMaterial: "glass_material",
331
+ frameDepth: 103,
332
+ topThk: "20mm",
333
+ sidesThk: "20mm",
334
+ doorStopOffset: "30mm",
335
+ doorStopWidth: 20,
336
+ doorStopDepth: 13,
337
+ notchWidth: "20mm",
338
+ notchDepth: "40mm",
339
+ notchposition: 0.01,
340
+ gasketDepth: 5,
341
+ gasketWidth: 5,
342
+ secondDoorStopWidth: 20,
343
+ secondDoorStopDepth: 30,
344
+ secondDoorStopOffset: 13
345
+ });
346
+ const [interiorFanlight, setInteriorFanlight] = React.useState({
347
+ visible: false,
348
+ height: 300,
349
+ material: "interiorFanlight_material"
350
+ });
351
+ const [exteriorFanlight, setExteriorFanlight] = React.useState({
352
+ visible: false,
353
+ height: 300,
354
+ depth: 60,
355
+ material: "exteriorFanlight_material"
356
+ });
357
+ const [occulus, setOcculus] = React.useState({
358
+ visible: false,
359
+ infillVisible: true,
360
+ x1: 150,
361
+ x2: 150,
362
+ y1: 150,
363
+ y2: 150,
364
+ material: "infill_material",
365
+ depth: 20
366
+ });
367
+ const [frontCoverPanel, setFrontCoverPanel] = React.useState({
368
+ visible: false,
369
+ width: 73,
370
+ height: 2200,
371
+ depth: 12,
372
+ material: "front_cover_material"
373
+ });
374
+ const [backCoverPanel, setBackCoverPanel] = React.useState({
375
+ visible: false,
376
+ width: 73,
377
+ height: 2200,
378
+ depth: 12,
379
+ material: "back_cover_material"
380
+ });
381
+ const [frontDoorPlane, setFrontDoorPlane] = React.useState({
382
+ visible: true,
383
+ material: "frontDoor_material"
384
+ });
385
+ const [backDoorPlane, setBackDoorPlane] = React.useState({
386
+ visible: true,
387
+ material: "frontDoor_material"
388
+ });
389
+ const [glass, setGlass] = React.useState({
390
+ visible: false,
391
+ material: "infill_material",
392
+ depth: 20
393
+ });
394
+ const [baseConfig, setBaseConfig] = React.useState(null);
395
+ const [frameType, setFrameType] = React.useState("AF20_40");
396
+ const [bodyType, setBodyType] = React.useState("40");
397
+ const [exteriorFanlightType, setExteriorFanlightType] = React.useState("WPFL");
398
+ const doorHeight = 0;
399
+ const [newPivotPosition, setNewPivotPosition] = React.useState(0);
400
+ const [, setParseMessage] = React.useState({
401
+ type: "",
402
+ text: ""
403
+ });
404
+
405
+ // DXF Export state
406
+ const [exportDXF, setExportDXF] = React.useState(null);
407
+ const [collectElementsFromGroup, setCollectElementsFromGroup] = React.useState(null);
408
+ React.useEffect(() => {
409
+ setIs2D(initialIs2D);
410
+ }, [initialIs2D]);
411
+ React.useEffect(() => {
412
+ if (initialMaterials) {
413
+ setMaterials(initialMaterials);
414
+ }
415
+ }, [initialMaterials]);
416
+ const handleParseCpid = React.useCallback(cpidToParse => {
417
+ const showMessage = function (text) {
418
+ let type = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "error";
419
+ setParseMessage({
420
+ text,
421
+ type
422
+ });
423
+ setTimeout(() => setParseMessage({
424
+ text: "",
425
+ type: ""
426
+ }), 4000);
427
+ };
428
+ if (!cpidToParse || !cpidToParse.trim().startsWith("P_")) {
429
+ showMessage("Invalid format: CPID must start with 'P_'.");
430
+ return;
431
+ }
432
+ const parts = cpidToParse.trim().split("_");
433
+ if (parts.length < 4) {
434
+ showMessage("Invalid format: Not enough parts in CPID.");
435
+ return;
436
+ }
437
+ const newConfig = {
438
+ fanlightVisible: false,
439
+ fanlightType: "WPFL",
440
+ interiorFanlightVisible: false,
441
+ occulusVisible: false,
442
+ frameType: "",
443
+ bodyType: ""
444
+ };
445
+ let currentIndex = 1;
446
+
447
+ // Door Type
448
+ if (parts[currentIndex] === "SD") {
449
+ setIsDoubleDoor(false);
450
+ setTotalHeight(2700);
451
+ setTotalWidth(974);
452
+ currentIndex++;
453
+ } else if (parts[currentIndex] === "DD") {
454
+ setIsDoubleDoor(true);
455
+ setTotalWidth(1802);
456
+ currentIndex++;
457
+ } else {
458
+ showMessage("Unsupported Door Type: Only 'SD' or 'DD' is supported.");
459
+ return;
460
+ }
461
+
462
+ //Fanlight Type
463
+ const fanlightPart = parts[currentIndex];
464
+ const validFanlights = ["WPFL", "ALDGFL", "ALSGFL"];
465
+ if (fanlightPart === "FH") {
466
+ newConfig.fanlightVisible = false;
467
+ currentIndex++;
468
+ } else if (validFanlights.includes(fanlightPart)) {
469
+ setExteriorFanlight(prev => _objectSpread2(_objectSpread2({}, prev), {}, {
470
+ height: 300
471
+ }));
472
+ newConfig.fanlightVisible = true;
473
+ newConfig.fanlightType = fanlightPart;
474
+ currentIndex++;
475
+ } else {
476
+ showMessage("Invalid Fanlight Type: '".concat(fanlightPart, "'."));
477
+ return;
478
+ }
479
+
480
+ // Frame and Body Type
481
+ const validSimpleFrameTypes = ["AF20", "AF40", "WF", "MXF", "MXCAF", "NOF"];
482
+ const validBodyTypes = ["40", "50", "FLI", "100", "WDG100", "SG8", "SG10", "SG12"];
483
+ let framePart = parts[currentIndex];
484
+ let bodyPartRaw = parts[currentIndex + 1] || "";
485
+ let frameFound = false;
486
+ if ("".concat(framePart, "_").concat(bodyPartRaw) === "WDGF_WDG100") {
487
+ newConfig.frameType = "WDGF_WDG100";
488
+ newConfig.bodyType = "WDG100";
489
+ currentIndex += 2;
490
+ frameFound = true;
491
+ } else if (validSimpleFrameTypes.includes(framePart)) {
492
+ let tempBody = bodyPartRaw.replace("OCC", "");
493
+ if (validBodyTypes.includes(tempBody)) {
494
+ newConfig.frameType = framePart === "NOF" ? "NOF" : "".concat(framePart, "_").concat(tempBody);
495
+ newConfig.bodyType = tempBody;
496
+ if (bodyPartRaw.endsWith("OCC")) {
497
+ newConfig.occulusVisible = true;
498
+ setOcculus(prev => _objectSpread2(_objectSpread2({}, prev), {}, {
499
+ x1: 150,
500
+ x2: 150,
501
+ y1: 150,
502
+ y2: 150
503
+ }));
504
+ }
505
+ currentIndex += 2;
506
+ frameFound = true;
507
+ }
508
+ }
509
+ if (!frameFound) {
510
+ showMessage("Invalid Frame/Body combination starting with '".concat(framePart, "'."));
511
+ return;
512
+ }
513
+ for (let i = currentIndex; i < parts.length; i++) {
514
+ if (parts[i] === "IFL") {
515
+ setInteriorFanlight(prev => _objectSpread2(_objectSpread2({}, prev), {}, {
516
+ height: 300
517
+ }));
518
+ newConfig.interiorFanlightVisible = true;
519
+ } else if (parts[i] === "OCC") {
520
+ newConfig.occulusVisible = true;
521
+ }
522
+ }
523
+
524
+ // Apply final configuration
525
+ setFrameType(newConfig.frameType);
526
+ setBodyType(newConfig.bodyType);
527
+ setExteriorFanlight(prev => _objectSpread2(_objectSpread2({}, prev), {}, {
528
+ visible: newConfig.fanlightVisible
529
+ }));
530
+ if (newConfig.fanlightVisible) {
531
+ setExteriorFanlightType(newConfig.fanlightType);
532
+ }
533
+ setInteriorFanlight(prev => _objectSpread2(_objectSpread2({}, prev), {}, {
534
+ visible: newConfig.interiorFanlightVisible
535
+ }));
536
+ setOcculus(prev => _objectSpread2(_objectSpread2({}, prev), {}, {
537
+ visible: newConfig.occulusVisible
538
+ }));
539
+ setCpid(cpidToParse);
540
+ showMessage("Configuration Applied!", "success");
541
+ }, [setCpid, setBodyType, setExteriorFanlight, setExteriorFanlightType, setFrameType, setInteriorFanlight, setOcculus, setIsDoubleDoor, setTotalWidth]);
542
+
543
+ // --- BodyType Effect ---
544
+ React.useEffect(() => {
545
+ let newDoorDepth = 40;
546
+ switch (bodyType) {
547
+ case "50":
548
+ newDoorDepth = 50;
549
+ break;
550
+ case "SG8":
551
+ newDoorDepth = 8;
552
+ break;
553
+ case "SG10":
554
+ newDoorDepth = 10;
555
+ break;
556
+ case "SG12":
557
+ newDoorDepth = 12;
558
+ break;
559
+ }
560
+ setDoor(prev => _objectSpread2(_objectSpread2({}, prev), {}, {
561
+ theDoorDepth: newDoorDepth
562
+ }));
563
+ }, [bodyType]);
564
+
565
+ // --- FrameType Effect ---
566
+ React.useEffect(() => {
567
+ setIsFrameVisible(frameType !== "NOF");
568
+ setGlassVisible(false);
569
+ let newSettings = {};
570
+ let lookupKey = frameType;
571
+ if (frameType.startsWith("AF20_SG")) {
572
+ lookupKey = "AF20_40";
573
+ } else if (frameType.startsWith("AF40_SG")) {
574
+ lookupKey = "AF40_40";
575
+ }
576
+ if (doorSettings[lookupKey]) {
577
+ newSettings = _objectSpread2({}, doorSettings[lookupKey]);
578
+ }
579
+ if (lookupKey === "WDGF_WDG100") {
580
+ setGlassVisible(true);
581
+ }
582
+
583
+ // Store pristine settings for scaling
584
+ setBaseConfig(newSettings);
585
+ const isMxCaf = frameType.startsWith("MXCAF");
586
+ setFrontCoverPanel(prev => _objectSpread2(_objectSpread2({}, prev), {}, {
587
+ visible: isMxCaf
588
+ }));
589
+ setBackCoverPanel(prev => _objectSpread2(_objectSpread2({}, prev), {}, {
590
+ visible: isMxCaf
591
+ }));
592
+ const {
593
+ doorDepth
594
+ } = newSettings,
595
+ newFrameSettings = _objectWithoutProperties(newSettings, _excluded);
596
+ setDoorFrame(prev => _objectSpread2(_objectSpread2({}, prev), newFrameSettings));
597
+ if (doorDepth !== undefined) {
598
+ setDoor(prev => _objectSpread2(_objectSpread2({}, prev), {}, {
599
+ theDoorDepth: doorDepth
600
+ }));
601
+ }
602
+
603
+ // Sync totalDepth with the frame's default depth when frame type changes.
604
+ if (newSettings.frameDepth) {
605
+ setTotalDepth(newSettings.frameDepth);
606
+ }
607
+ }, [frameType]);
608
+
609
+ // --- useEffect for scaling based on totalDepth ---
610
+ React.useEffect(() => {
611
+ if (!baseConfig || !baseConfig.frameDepth || baseConfig.frameDepth === 0) return;
612
+ const baseFrameDepth = baseConfig.frameDepth;
613
+ const ratio = totalDepth / baseFrameDepth;
614
+ const scaleProportionally = value => {
615
+ if (typeof value === "number") {
616
+ return value * ratio;
617
+ }
618
+ if (typeof value === "string" && value.endsWith("mm")) {
619
+ const num = parseFloat(value);
620
+ if (!isNaN(num)) {
621
+ return num * ratio + "mm";
622
+ }
623
+ }
624
+ return value; // Return original if not scalable
625
+ };
626
+ setDoorFrame(prev => _objectSpread2(_objectSpread2({}, prev), {}, {
627
+ frameDepth: totalDepth,
628
+ doorStopDepth: scaleProportionally(baseConfig.doorStopDepth),
629
+ notchDepth: scaleProportionally(baseConfig.notchDepth),
630
+ gasketDepth: scaleProportionally(baseConfig.gasketDepth),
631
+ secondDoorStopDepth: scaleProportionally(baseConfig.secondDoorStopDepth)
632
+ }));
633
+ if (baseConfig.doorDepth !== undefined) {
634
+ const baseDoorDepth = baseConfig.doorDepth;
635
+ const frameDelta = totalDepth - baseFrameDepth;
636
+ let newDoorDepth;
637
+ if (frameDelta > 0) {
638
+ // --- THIS IS THE VALUE TO TWEAK ---
639
+ // Increase this multiplier to make the door body grow faster when totalDepth increases.
640
+ // A value of 1.0 means it grows at the same rate as the frame.
641
+ // A value of 2.0 means it grows twice as fast as the frame.
642
+ const increaseMultiplier = 0.8;
643
+ newDoorDepth = baseDoorDepth + frameDelta * increaseMultiplier;
644
+ } else {
645
+ // When decreasing totalDepth, the door body scales down proportionally.
646
+ newDoorDepth = baseDoorDepth * ratio;
647
+ }
648
+ setDoor(prev => _objectSpread2(_objectSpread2({}, prev), {}, {
649
+ theDoorDepth: newDoorDepth
650
+ }));
651
+ }
652
+ const baseCoverPanelDepth = 12;
653
+ setFrontCoverPanel(prev => _objectSpread2(_objectSpread2({}, prev), {}, {
654
+ depth: baseCoverPanelDepth * ratio
655
+ }));
656
+ setBackCoverPanel(prev => _objectSpread2(_objectSpread2({}, prev), {}, {
657
+ depth: baseCoverPanelDepth * ratio
658
+ }));
659
+ }, [totalDepth, baseConfig]);
660
+
661
+ // --- Exterior Fanlight Logic ---
662
+ React.useEffect(() => {
663
+ if (!exteriorFanlight.visible) return;
664
+ switch (exteriorFanlightType) {
665
+ case "WPFL":
666
+ setExteriorFanlight(prev => _objectSpread2(_objectSpread2({}, prev), {}, {
667
+ depth: doorFrame.frameDepth
668
+ }));
669
+ break;
670
+ case "ALDGFL":
671
+ setExteriorFanlight(prev => _objectSpread2(_objectSpread2({}, prev), {}, {
672
+ depth: doorFrame.frameDepth
673
+ }));
674
+ break;
675
+ case "ALSGFL":
676
+ setExteriorFanlight(prev => _objectSpread2(_objectSpread2({}, prev), {}, {
677
+ depth: 40
678
+ }));
679
+ break;
680
+ }
681
+ }, [exteriorFanlight.visible, exteriorFanlightType, doorFrame.frameDepth, setExteriorFanlight]);
682
+ React.useEffect(() => {
683
+ setOcculus(prevOcculus => _objectSpread2(_objectSpread2({}, prevOcculus), {}, {
684
+ depth: door.theDoorDepth
685
+ }));
686
+ }, [door.theDoorDepth]);
687
+ React.useEffect(() => {
688
+ const topThkValue = parseInt(doorFrame.topThk) || 0;
689
+ const exteriorFanlightHeight = exteriorFanlight.visible ? exteriorFanlight.height : 0;
690
+ const newDoorHeight = totalHeight - topThkValue - exteriorFanlightHeight;
691
+ setDoor(prevDoor => _objectSpread2(_objectSpread2({}, prevDoor), {}, {
692
+ doorHeight: newDoorHeight > 0 ? newDoorHeight : 0
693
+ }));
694
+ }, [totalHeight, doorFrame.topThk, exteriorFanlight.visible, exteriorFanlight.height]);
695
+ React.useEffect(() => {
696
+ const sidesThkValue = parseInt(doorFrame.sidesThk) || 0;
697
+ const numDoors = isDoubleDoor ? 2 : 1;
698
+ const newDoorWidth = (totalWidth - sidesThkValue * 2) / numDoors;
699
+ setDoor(prevDoor => _objectSpread2(_objectSpread2({}, prevDoor), {}, {
700
+ doorWidth: newDoorWidth > 0 ? newDoorWidth : 0
701
+ }));
702
+ }, [totalWidth, doorFrame.sidesThk, isDoubleDoor]);
703
+ return /*#__PURE__*/jsxRuntime.jsx(ConfiguratorContext.Provider, {
704
+ value: {
705
+ materials,
706
+ setMaterials,
707
+ is2D,
708
+ setIs2D,
709
+ isPlaneVisible,
710
+ setIsPlaneVisible,
711
+ isFrameVisible,
712
+ setIsFrameVisible,
713
+ cpid,
714
+ setCpid,
715
+ door,
716
+ setDoor,
717
+ doorFrame,
718
+ setDoorFrame,
719
+ interiorFanlight,
720
+ setInteriorFanlight,
721
+ exteriorFanlight,
722
+ setExteriorFanlight,
723
+ occulus,
724
+ setOcculus,
725
+ frontCoverPanel,
726
+ setFrontCoverPanel,
727
+ backCoverPanel,
728
+ setBackCoverPanel,
729
+ frontDoorPlane,
730
+ setFrontDoorPlane,
731
+ backDoorPlane,
732
+ setBackDoorPlane,
733
+ glass,
734
+ setGlass,
735
+ frameType,
736
+ setFrameType,
737
+ bodyType,
738
+ setBodyType,
739
+ totalHeight,
740
+ setTotalHeight,
741
+ totalDepth,
742
+ setTotalDepth,
743
+ totalWidth,
744
+ setTotalWidth,
745
+ exteriorFanlightType,
746
+ setExteriorFanlightType,
747
+ glassVisible,
748
+ setGlassVisible,
749
+ glassDepth,
750
+ setGlassDepth,
751
+ handleParseCpid,
752
+ newPivotPosition,
753
+ setNewPivotPosition,
754
+ doorHeight,
755
+ isDoubleDoor,
756
+ setIsDoubleDoor,
757
+ exportDXF,
758
+ setExportDXF,
759
+ collectElementsFromGroup,
760
+ setCollectElementsFromGroup
761
+ },
762
+ children: children
763
+ });
764
+ };
765
+ const useConfigurator = () => {
766
+ const context = React.useContext(ConfiguratorContext);
767
+ if (!context) {
768
+ throw new Error("useConfigurator must be used within a ConfiguratorProvider");
769
+ }
770
+ return context;
771
+ };
772
+
773
+ // Reasonable defaults to avoid heavy anisotropy and ensure mips are generated.
774
+ const DEFAULT_ANISOTROPY = 8;
775
+ const DEFAULT_MIN_FILTER = THREE__namespace.LinearMipmapLinearFilter;
776
+ const DEFAULT_MAG_FILTER = THREE__namespace.LinearFilter;
777
+ const DEFAULT_WRAP = THREE__namespace.ClampToEdgeWrapping;
778
+ // Helper function to check for hex color codes
779
+ const isHexColor = str => /^#([0-9A-F]{3}){1,2}$/i.test(str);
780
+
781
+ /**
782
+ * A simple "builder" that creates a THREE.MeshStandardMaterial from a prop and an
783
+ * optional map of pre-loaded textures. It now loads textures if they are not pre-loaded.
784
+ * @param prop - The material definition (string or object).
785
+ * @param textures - A map of URL -> THREE.Texture, provided by useTexture.
786
+ * @returns A THREE.MeshStandardMaterial instance.
787
+ */
788
+ const buildMaterial = (prop, textures) => {
789
+ if (!prop) {
790
+ return new THREE__namespace.MeshStandardMaterial({
791
+ color: "#cccccc",
792
+ roughness: 0.8
793
+ });
794
+ }
795
+ const sourceValue = typeof prop === "string" ? prop : prop.value;
796
+ const opacity = typeof prop === "object" ? prop.opacity : 1;
797
+
798
+ // Start with base parameters.
799
+ const params = {
800
+ roughness: 0.8,
801
+ side: THREE__namespace.DoubleSide
802
+ };
803
+ if (isHexColor(sourceValue)) {
804
+ // It's a color. Explicitly set the color and ensure no texture is applied.
805
+ params.color = sourceValue;
806
+ params.map = null;
807
+ } else {
808
+ // It's a texture. Explicitly set the color to white to avoid tinting the texture.
809
+ params.color = "#ffffff";
810
+ let texture = null;
811
+ if (textures && textures[sourceValue]) {
812
+ // Use the pre-loaded texture.
813
+ texture = textures[sourceValue];
814
+ } else if (sourceValue) {
815
+ // Not pre-loaded, so load it now.
816
+ // TextureLoader caches results internally, so this is efficient.
817
+ texture = new THREE__namespace.TextureLoader().load(sourceValue);
818
+ }
819
+ if (texture && !texture.__configuredForDoorMaterials) {
820
+ texture.anisotropy = Math.min(texture.anisotropy || DEFAULT_ANISOTROPY, DEFAULT_ANISOTROPY);
821
+ texture.minFilter = DEFAULT_MIN_FILTER;
822
+ texture.magFilter = DEFAULT_MAG_FILTER;
823
+ texture.wrapS = DEFAULT_WRAP;
824
+ texture.wrapT = DEFAULT_WRAP;
825
+ texture.needsUpdate = true;
826
+ texture.__configuredForDoorMaterials = true;
827
+ }
828
+ params.map = texture;
829
+ }
830
+
831
+ // Handle transparency explicitly to prevent lingering state.
832
+ if (opacity !== undefined && opacity < 1) {
833
+ params.transparent = true;
834
+ params.opacity = opacity;
835
+ } else {
836
+ // Explicitly turn transparency off if opacity is 1 or undefined.
837
+ params.transparent = false;
838
+ params.opacity = 1;
839
+ }
840
+ return new THREE__namespace.MeshStandardMaterial(params);
841
+ };
842
+ const getFirstValidValue = values => {
843
+ for (const val of values) {
844
+ if (val) {
845
+ return val;
846
+ }
847
+ }
848
+ return undefined;
849
+ };
850
+
851
+ const useDoorMaterials = function () {
852
+ let materialsProp = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
853
+ const preloadErrorNotified = React.useRef(false);
854
+
855
+ // Cache built materials per unique key to avoid reallocating MeshStandardMaterials
856
+ const materialCache = React.useRef(new Map());
857
+
858
+ // 1. Collect all unique texture URLs from the props.
859
+ const textureUrls = React.useMemo(() => {
860
+ const urls = new Set();
861
+ // Iterate over all passed material props
862
+ Object.values(materialsProp).forEach(prop => {
863
+ // Get the value, whether it's a string or inside an object
864
+ const value = typeof prop === "string" ? prop : prop === null || prop === void 0 ? void 0 : prop.value;
865
+ // If it's a valid URL, add it to our set to avoid duplicates
866
+ if (value && (value.startsWith("http") || value.startsWith("/"))) {
867
+ urls.add(value);
868
+ }
869
+ });
870
+ return Array.from(urls);
871
+ }, [materialsProp]);
872
+
873
+ // Preload textures up front so they are ready when the scene mounts.
874
+ React.useEffect(() => {
875
+ textureUrls.forEach(url => {
876
+ try {
877
+ drei.useTexture.preload(url);
878
+ } catch (err) {
879
+ if (!preloadErrorNotified.current) {
880
+ preloadErrorNotified.current = true;
881
+ console.error("[textures] preload failed for", url, err);
882
+ alert("Texture preload failed; check network or texture URLs. Rendering may be degraded.");
883
+ }
884
+ }
885
+ });
886
+ }, [textureUrls]);
887
+
888
+ // 2. Load all textures at once.
889
+ // !! THIS IS THE MAGIC !!
890
+ // `useTexture` will pause ("suspend") this component's rendering
891
+ // until all textures in the array are fully loaded.
892
+ const loadedTextures = drei.useTexture(textureUrls);
893
+
894
+ // 3. Create a map from URL back to the loaded THREE.Texture for easy lookup.
895
+ const textureMap = React.useMemo(() => {
896
+ const map = {};
897
+ textureUrls.forEach((url, index) => {
898
+ map[url] = loadedTextures[index];
899
+ });
900
+ return map;
901
+ }, [textureUrls, loadedTextures]);
902
+
903
+ // 4. Now that textures are guaranteed to be loaded, build all materials.
904
+ // This memo ensures the materials are only created when props or textures change.
905
+ return React.useMemo(() => {
906
+ const build = prop => {
907
+ var _prop$value, _prop$opacity;
908
+ const key = typeof prop === "string" ? "s:".concat(prop) : prop ? "o:".concat((_prop$value = prop.value) !== null && _prop$value !== void 0 ? _prop$value : "", ":").concat((_prop$opacity = prop.opacity) !== null && _prop$opacity !== void 0 ? _prop$opacity : "") : "__none__";
909
+ const textureKey = textureUrls.join("|");
910
+ const cacheKey = "".concat(key, "::").concat(textureKey);
911
+ const cached = materialCache.current.get(cacheKey);
912
+ if (cached) return cached;
913
+ const created = buildMaterial(prop, textureMap);
914
+ materialCache.current.set(cacheKey, created);
915
+ return created;
916
+ };
917
+ return {
918
+ doorMaterial: build(materialsProp.Body),
919
+ frameMaterial: build(getFirstValidValue([materialsProp.D_PRF_COLOR, materialsProp.D_FR_SRF_DECOR, materialsProp.D_FR_SRF_DECOR_S2])),
920
+ gasketMaterial: build(materialsProp.D_GASKET),
921
+ doorStopMaterial: build(getFirstValidValue([materialsProp.D_PRF_COLOR, materialsProp.D_FR_SRF_DECOR, materialsProp.D_FR_SRF_DECOR_S2])),
922
+ interiorFanlightMaterial: build(materialsProp.D_SRF_DECOR),
923
+ exteriorFanlightMaterial: build(materialsProp.D_SRF_DECOR),
924
+ occulusInfillMaterial: build(materialsProp.D_FR_COLOR),
925
+ glassInfillMaterial: build(materialsProp.D_FR_COLOR),
926
+ frontCoverPanelMaterial: build(getFirstValidValue([materialsProp.D_PRF_COLOR, materialsProp.D_FR_SRF_DECOR, materialsProp.D_FR_SRF_DECOR_S2])),
927
+ backCoverPanelMaterial: build(getFirstValidValue([materialsProp.D_PRF_COLOR, materialsProp.D_FR_SRF_DECOR, materialsProp.D_FR_SRF_DECOR_S2])),
928
+ frontDoorPlaneMaterial: build(materialsProp.D_SRF_DECOR_2),
929
+ backDoorPlaneMaterial: build(materialsProp.D_SRF_DECOR),
930
+ hingeBodyMaterial: build(materialsProp.Hinge),
931
+ hingeAccentMaterial: build(materialsProp.HingeCuts)
932
+ };
933
+ }, [materialsProp, textureMap, textureUrls]);
934
+ };
935
+
936
+ /******************************************************************************
937
+ Copyright (c) Microsoft Corporation.
938
+
939
+ Permission to use, copy, modify, and/or distribute this software for any
940
+ purpose with or without fee is hereby granted.
941
+
942
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
943
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
944
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
945
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
946
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
947
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
948
+ PERFORMANCE OF THIS SOFTWARE.
949
+ ***************************************************************************** */
950
+
951
+ var __assign = function() {
952
+ __assign = Object.assign || function __assign(t) {
953
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
954
+ s = arguments[i];
955
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
956
+ }
957
+ return t;
958
+ };
959
+ return __assign.apply(this, arguments);
960
+ };
961
+
962
+ function __awaiter(thisArg, _arguments, P, generator) {
963
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
964
+ return new (P || (P = Promise))(function (resolve, reject) {
965
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
966
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
967
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
968
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
969
+ });
970
+ }
971
+
972
+ function __generator(thisArg, body) {
973
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
974
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
975
+ function verb(n) { return function (v) { return step([n, v]); }; }
976
+ function step(op) {
977
+ if (f) throw new TypeError("Generator is already executing.");
978
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
979
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
980
+ if (y = 0, t) op = [op[0] & 2, t.value];
981
+ switch (op[0]) {
982
+ case 0: case 1: t = op; break;
983
+ case 4: _.label++; return { value: op[1], done: false };
984
+ case 5: _.label++; y = op[1]; op = [0]; continue;
985
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
986
+ default:
987
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
988
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
989
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
990
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
991
+ if (t[2]) _.ops.pop();
992
+ _.trys.pop(); continue;
993
+ }
994
+ op = body.call(thisArg, _);
995
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
996
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
997
+ }
998
+ }
999
+
1000
+ typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
1001
+ var e = new Error(message);
1002
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
1003
+ };
1004
+
1005
+ // Three.js Y-up -> IFC / CAD coords:
1006
+ // IFC X = x
1007
+ // IFC Y = z (depth)
1008
+ // IFC Z = y (height)
1009
+ /**
1010
+ * Convert a Three.js Vector3 (X, Y, Z) into IFC / CAD-style coordinates (X, Z, Y).
1011
+ *
1012
+ * In Three.js:
1013
+ * - X = horizontal
1014
+ * - Y = up
1015
+ * - Z = depth
1016
+ *
1017
+ * In IFC / CAD for this project:
1018
+ * - X = X
1019
+ * - Y = Z (depth)
1020
+ * - Z = Y (height)
1021
+ *
1022
+ * @param {THREE.Vector3} vec3 - Original Three.js vector.
1023
+ * @returns {{ x: number, y: number, z: number }} - Re-mapped IFC-style coordinates.
1024
+ */
1025
+ function threeToIfcCoords(vec3) {
1026
+ return {
1027
+ x: vec3.x,
1028
+ y: vec3.z,
1029
+ z: vec3.y
1030
+ };
1031
+ }
1032
+ /**
1033
+ * Trigger a file download in the browser from a Blob.
1034
+ *
1035
+ * @param {Blob} blob - File data as a Blob.
1036
+ * @param {string} name - File name shown to the user (e.g. "project_123.dxf").
1037
+ */
1038
+ function downloadBlob(blob, name) {
1039
+ var a = document.createElement("a");
1040
+ var url = URL.createObjectURL(blob);
1041
+ a.href = url;
1042
+ a.download = name;
1043
+ a.click();
1044
+ setTimeout(function () {
1045
+ return URL.revokeObjectURL(url);
1046
+ }, 100);
1047
+ }
1048
+ /**
1049
+ * useDxfExportPayload
1050
+ *
1051
+ * High-level hook to:
1052
+ * - Walk a Three.js group (rootGroupRef) and extract all exportable meshes.
1053
+ * - Convert their geometry into IFC-style coordinates (X, Z, Y).
1054
+ * - Build a JSON payload of "elements" (positions + indices + IFC type).
1055
+ * - Optionally call the `/api/ifc-dxf` endpoint and download the resulting DXF.
1056
+ *
1057
+ * You typically:
1058
+ * 1. Wrap all exportable meshes in a single `<group ref={exportGroupRef}>`.
1059
+ * 2. Call `useDxfExportPayload({ rootGroupRef, getPropsForMesh })`.
1060
+ * 3. Use:
1061
+ * - `collectElementsFromGroup()` if you just want the raw payload.
1062
+ * - `exportDxfFromServer()` if you want to call the API + download DXF.
1063
+ *
1064
+ * @param {Object} params
1065
+ * @param {React.RefObject<THREE.Group>} params.rootGroupRef
1066
+ * Parent group containing all exportable meshes (walls, doors, etc.).
1067
+ * @param {(mesh: THREE.Mesh) => { ifcType?: string, props?: any }} [params.getPropsForMesh]
1068
+ * Optional mapper to attach IFC metadata (ifcType + props) to each mesh.
1069
+ * If omitted, the hook falls back to `mesh.userData` heuristics.
1070
+ * @param {string|number} [params.projectNumber='1987348']
1071
+ * Optional project identifier used only for naming in DXF export (e.g. "1987348").
1072
+ * @param {string} [params.version='V0.1']
1073
+ * Optional version label used only for naming in DXF export (e.g. "V0.1").
1074
+ *
1075
+ * @returns {{
1076
+ * collectElementsFromGroup: () => {
1077
+ * elements: Array<{
1078
+ * uuid: string,
1079
+ * name: string,
1080
+ * ifcType: string,
1081
+ * props: any,
1082
+ * worldPositions: number[],
1083
+ * localPositions: number[],
1084
+ * matrixWorld: number[],
1085
+ * indices: number[]
1086
+ * }>,
1087
+ * stats: {
1088
+ * meshCount: number,
1089
+ * elementCount: number,
1090
+ * vertexCount: number,
1091
+ * triangleCount: number
1092
+ * },
1093
+ * countsByType: Record<string, number>
1094
+ * },
1095
+ * exportDxfFromServer: (options?: {
1096
+ * projectName?: string,
1097
+ * schema?: string,
1098
+ * format?: string,
1099
+ * bakeWorld?: boolean,
1100
+ * compat?: any,
1101
+ * fileName?: string
1102
+ * }) => Promise<void>
1103
+ * }}
1104
+ */
1105
+ function useDxfExportPayload(_a) {
1106
+ var _this = this;
1107
+ var _b = _a === void 0 ? {} : _a,
1108
+ rootGroupRef = _b.rootGroupRef,
1109
+ getPropsForMesh = _b.getPropsForMesh,
1110
+ _c = _b.projectNumber,
1111
+ projectNumber = _c === void 0 ? "1987348" : _c,
1112
+ // used for naming (DXF file, project label)
1113
+ _d = _b.version,
1114
+ // used for naming (DXF file, project label)
1115
+ version = _d === void 0 ? "V1.0" : _d;
1116
+ var collectElementsFromGroup = React.useCallback(function () {
1117
+ var root = rootGroupRef === null || rootGroupRef === void 0 ? void 0 : rootGroupRef.current;
1118
+ if (!root) {
1119
+ console.warn("[DXF-PAYLOAD] rootGroupRef.current is null, nothing to export");
1120
+ alert("DXF export aborted: no export group found in the scene.");
1121
+ return {
1122
+ elements: [],
1123
+ stats: {
1124
+ meshCount: 0,
1125
+ elementCount: 0,
1126
+ vertexCount: 0,
1127
+ triangleCount: 0
1128
+ },
1129
+ countsByType: {}
1130
+ };
1131
+ }
1132
+ // Make sure transforms are up to date for the whole subtree
1133
+ root.updateWorldMatrix(true, true);
1134
+ var elements = [];
1135
+ var meshCount = 0;
1136
+ var vertexCount = 0;
1137
+ var triangleCount = 0;
1138
+ var tmpLocal = new THREE__namespace.Vector3();
1139
+ var tmpWorld = new THREE__namespace.Vector3();
1140
+ root.traverse(function (obj) {
1141
+ var _a, _b, _c, _d, _e;
1142
+ var mesh = obj;
1143
+ if (!mesh.isMesh) return;
1144
+ // Skip helper meshes (like invisible selection boxes)
1145
+ var mat = mesh.material;
1146
+ if (mat && mat.transparent && mat.opacity === 0) return;
1147
+ var geometry = mesh.geometry;
1148
+ if (!geometry || !geometry.attributes || !geometry.attributes.position) {
1149
+ console.warn("[DXF-PAYLOAD] Mesh skipped: missing geometry or position attribute", mesh.name || mesh.uuid);
1150
+ return;
1151
+ }
1152
+ var positionAttr = geometry.getAttribute("position");
1153
+ var vCount = positionAttr.count;
1154
+ if (vCount < 3) {
1155
+ console.warn("[DXF-PAYLOAD] Mesh skipped: fewer than 3 vertices", {
1156
+ name: mesh.name,
1157
+ uuid: mesh.uuid
1158
+ });
1159
+ return;
1160
+ }
1161
+ meshCount += 1;
1162
+ vertexCount += vCount;
1163
+ var localPositions = new Array(vCount * 3);
1164
+ var worldPositions = new Array(vCount * 3);
1165
+ var worldMatrix = mesh.matrixWorld;
1166
+ // Build local + world positions (already in IFC axes)
1167
+ for (var i = 0; i < vCount; i++) {
1168
+ tmpLocal.fromBufferAttribute(positionAttr, i);
1169
+ // Local IFC coords (no world matrix): x, z, y
1170
+ var li = threeToIfcCoords(tmpLocal);
1171
+ localPositions[3 * i + 0] = li.x;
1172
+ localPositions[3 * i + 1] = li.y;
1173
+ localPositions[3 * i + 2] = li.z;
1174
+ // World IFC coords
1175
+ tmpWorld.copy(tmpLocal).applyMatrix4(worldMatrix);
1176
+ var wi = threeToIfcCoords(tmpWorld);
1177
+ worldPositions[3 * i + 0] = wi.x;
1178
+ worldPositions[3 * i + 1] = wi.y;
1179
+ worldPositions[3 * i + 2] = wi.z;
1180
+ }
1181
+ // Indices (triangles)
1182
+ var indices;
1183
+ if (geometry.index) {
1184
+ indices = Array.from(geometry.index.array);
1185
+ } else {
1186
+ // Assume non-indexed geometry is already triangulated (3 verrtices pe tri)
1187
+ var triCount = Math.floor(vCount / 3);
1188
+ var indexCount = triCount * 3;
1189
+ indices = new Array(indexCount);
1190
+ for (var i = 0; i < indexCount; i++) {
1191
+ indices[i] = i;
1192
+ }
1193
+ }
1194
+ // Optional flip: mimic current buildElementsForIfcAndDxf behaviour
1195
+ try {
1196
+ if (worldMatrix.determinant() >= 0) {
1197
+ for (var i = 0; i < indices.length; i += 3) {
1198
+ var tmp = indices[i + 1];
1199
+ indices[i + 1] = indices[i + 2];
1200
+ indices[i + 2] = tmp;
1201
+ }
1202
+ }
1203
+ } catch (_f) {
1204
+ // ignore
1205
+ }
1206
+ triangleCount += indices.length / 3;
1207
+ // IFC type + props
1208
+ var ifcType = "IfcBuildingElementProxy";
1209
+ var props = {};
1210
+ if (typeof getPropsForMesh === "function") {
1211
+ var extra = getPropsForMesh(mesh) || {};
1212
+ ifcType = extra.ifcType || ifcType;
1213
+ props = extra.props || props;
1214
+ } else {
1215
+ // Fallback: derive from userData / type (not used in your current setup)
1216
+ ifcType = ((_a = mesh.userData) === null || _a === void 0 ? void 0 : _a.ifcType) || (((_b = mesh.userData) === null || _b === void 0 ? void 0 : _b.type) === "door" ? "IfcDoor" : ((_c = mesh.userData) === null || _c === void 0 ? void 0 : _c.type) === "wall" ? "IfcWall" : "IfcBuildingElementProxy");
1217
+ var color = mesh.material && mesh.material.color && typeof mesh.material.color.getHexString === "function" ? "#".concat(mesh.material.color.getHexString()) : undefined;
1218
+ props = __assign(__assign({}, mesh.userData), {
1219
+ materialName: (_d = mesh.material) === null || _d === void 0 ? void 0 : _d.name,
1220
+ colorHex: color
1221
+ });
1222
+ }
1223
+ var element = {
1224
+ uuid: mesh.uuid,
1225
+ name: mesh.name || ((_e = mesh.userData) === null || _e === void 0 ? void 0 : _e.name) || "",
1226
+ ifcType: ifcType,
1227
+ props: props,
1228
+ worldPositions: worldPositions,
1229
+ localPositions: localPositions,
1230
+ matrixWorld: Array.from(worldMatrix.elements),
1231
+ indices: indices
1232
+ };
1233
+ elements.push(element);
1234
+ });
1235
+ var stats = {
1236
+ meshCount: meshCount,
1237
+ elementCount: elements.length,
1238
+ vertexCount: vertexCount,
1239
+ triangleCount: triangleCount
1240
+ };
1241
+ // Extra: count elements per IFC type (walls / doors / others)
1242
+ var countsByType = elements.reduce(function (acc, el) {
1243
+ var key = el.ifcType || "UNKNOWN";
1244
+ acc[key] = (acc[key] || 0) + 1;
1245
+ return acc;
1246
+ }, {});
1247
+ console.log("[DXF-PAYLOAD] collectElementsFromGroup() summary:", stats);
1248
+ console.log("[DXF-PAYLOAD] countsByType:", countsByType);
1249
+ if (elements[0]) {
1250
+ var e0 = elements[0];
1251
+ console.log("[DXF-PAYLOAD] First element preview:", {
1252
+ uuid: e0.uuid,
1253
+ name: e0.name,
1254
+ ifcType: e0.ifcType,
1255
+ worldPositionsLength: e0.worldPositions.length,
1256
+ indicesLength: e0.indices.length
1257
+ });
1258
+ }
1259
+ return {
1260
+ elements: elements,
1261
+ stats: stats,
1262
+ countsByType: countsByType
1263
+ };
1264
+ }, [rootGroupRef, getPropsForMesh]);
1265
+ /**
1266
+ * Call the `/api/ifc-dxf` endpoint using the current scene geometry and
1267
+ * immediately download the resulting DXF.
1268
+ *
1269
+ * The geometry is collected from `rootGroupRef` via `collectElementsFromGroup()`,
1270
+ * then POSTed as `{ projectName, schema, format, bakeWorld, compat, elements }`.
1271
+ *
1272
+ * @async
1273
+ * @function exportDxfFromServer
1274
+ * @param {Object} [options]
1275
+ * @param {string} [options.projectName=`Roometry Project ${projectNumber}`]
1276
+ * Project label forwarded to the backend. If omitted, a dynamic default is
1277
+ * used that includes the `projectNumber` (e.g. "Roometry Project 1987348").
1278
+ * @param {string} [options.schema='IFC4']
1279
+ * IFC schema hint forwarded to the backend (currently "IFC4" or "IFC2X3").
1280
+ * @param {string} [options.format='tfs']
1281
+ * Internal IFC body format flag forwarded to the backend.
1282
+ * @param {boolean} [options.bakeWorld=false]
1283
+ * Whether the backend should bake world transforms into IFC geometry.
1284
+ * @param {any} [options.compat={ useRootContextForBody: true }]
1285
+ * Extra compatibility flags passed through to the IFC builder.
1286
+ * @param {string} [options.fileName=`${projectNumber}_v${version}_.dxf`]
1287
+ * Downloaded DXF file name. By default it includes `projectNumber` and `version`
1288
+ * so exports are easily traceable (e.g. "1987348_vV0.1_.dxf").
1289
+ *
1290
+ * @returns {Promise<void>}
1291
+ */
1292
+ var exportDxfFromServer = React.useCallback(function (_a) {
1293
+ var _b = _a === void 0 ? {} : _a,
1294
+ _c = _b.projectName,
1295
+ projectName = _c === void 0 ? "Roometry Project ".concat(projectNumber) : _c,
1296
+ // dynamic default using projectNumber
1297
+ _d = _b.schema,
1298
+ // dynamic default using projectNumber
1299
+ schema = _d === void 0 ? "IFC4" : _d,
1300
+ _e = _b.format,
1301
+ format = _e === void 0 ? "tfs" : _e,
1302
+ _f = _b.bakeWorld,
1303
+ bakeWorld = _f === void 0 ? false : _f,
1304
+ _g = _b.compat,
1305
+ compat = _g === void 0 ? {
1306
+ useRootContextForBody: true
1307
+ } : _g,
1308
+ _h = _b.fileName,
1309
+ fileName = _h === void 0 ? "".concat(projectNumber, "_v").concat(version, "_.dxf") : _h;
1310
+ return __awaiter(_this, void 0, void 0, function () {
1311
+ var _j, elements, stats, countsByType, res, txt, payload, dxfText, dxfBlob, err_1, msg;
1312
+ return __generator(this, function (_k) {
1313
+ switch (_k.label) {
1314
+ case 0:
1315
+ _j = collectElementsFromGroup(), elements = _j.elements, stats = _j.stats, countsByType = _j.countsByType;
1316
+ if (!elements.length) {
1317
+ console.warn("[DXF] No elements to export");
1318
+ alert("DXF export failed: no geometry to export");
1319
+ return [2 /*return*/];
1320
+ }
1321
+ console.log("[DXF] Payload for /api/ifc-dxf:", __assign(__assign({}, stats), {
1322
+ countsByType: countsByType,
1323
+ firstElement: elements[0] && {
1324
+ uuid: elements[0].uuid,
1325
+ name: elements[0].name,
1326
+ ifcType: elements[0].ifcType,
1327
+ worldPositionsLength: elements[0].worldPositions.length,
1328
+ indicesLength: elements[0].indices.length
1329
+ }
1330
+ }));
1331
+ _k.label = 1;
1332
+ case 1:
1333
+ _k.trys.push([1, 6,, 7]);
1334
+ return [4 /*yield*/, fetch("http://192.168.30.92:3009/api/ifc-dxf", {
1335
+ method: "POST",
1336
+ headers: {
1337
+ "Content-Type": "application/json"
1338
+ },
1339
+ body: JSON.stringify({
1340
+ projectName: projectName,
1341
+ schema: schema,
1342
+ format: format,
1343
+ bakeWorld: bakeWorld,
1344
+ compat: compat,
1345
+ elements: elements
1346
+ })
1347
+ })];
1348
+ case 2:
1349
+ res = _k.sent();
1350
+ if (!!res.ok) return [3 /*break*/, 4];
1351
+ return [4 /*yield*/, res.text()];
1352
+ case 3:
1353
+ txt = _k.sent();
1354
+ console.error("[DXF] Server error:", txt);
1355
+ alert("DXF export failed: " + txt);
1356
+ return [2 /*return*/];
1357
+ case 4:
1358
+ return [4 /*yield*/, res.json()];
1359
+ case 5:
1360
+ payload = _k.sent();
1361
+ console.log("[DXF] Server stats:", payload.stats);
1362
+ dxfText = payload.dxf;
1363
+ if (typeof dxfText !== "string") {
1364
+ console.error("[DXF] Invalid DXF in response:", payload);
1365
+ alert("DXF export failed: invalid DXF payload from server");
1366
+ return [2 /*return*/];
1367
+ }
1368
+ dxfBlob = new Blob([dxfText], {
1369
+ type: "image/vnd.dxf"
1370
+ });
1371
+ downloadBlob(dxfBlob, fileName);
1372
+ return [3 /*break*/, 7];
1373
+ case 6:
1374
+ err_1 = _k.sent();
1375
+ msg = err_1 instanceof Error ? err_1.message : String(err_1);
1376
+ console.error("[DXF] Network error:", err_1);
1377
+ alert("DXF export failed (network): " + msg);
1378
+ return [3 /*break*/, 7];
1379
+ case 7:
1380
+ return [2 /*return*/];
1381
+ }
1382
+ });
1383
+ });
1384
+ }, [collectElementsFromGroup, projectNumber, version]);
1385
+ return {
1386
+ collectElementsFromGroup: collectElementsFromGroup,
1387
+ exportDxfFromServer: exportDxfFromServer
1388
+ };
1389
+ }
1390
+
1391
+ const CAST_SHADOW_MESHES = false;
1392
+ const RECEIVE_SHADOW_MESHES = false;
1393
+ const ROSE_SEGMENTS = 20;
1394
+ const HANDLE_SEGMENTS = 20;
1395
+ const HANDLE_SPHERE_SEGMENTS = 16;
1396
+ const KEYHOLE_SEGMENTS = 12;
1397
+ const HINGE_SEGMENTS = 16;
1398
+
1399
+ function DoorFrame(_ref) {
1400
+ let {
1401
+ materials,
1402
+ doorModelsProps
1403
+ } = _ref;
1404
+ const {
1405
+ doorOpening: doorOpeningProp
1406
+ } = doorModelsProps;
1407
+ const {
1408
+ isFrameVisible,
1409
+ door,
1410
+ doorFrame,
1411
+ interiorFanlight,
1412
+ exteriorFanlight,
1413
+ frontCoverPanel,
1414
+ backCoverPanel,
1415
+ frameType,
1416
+ glassVisible,
1417
+ glassDepth,
1418
+ isDoubleDoor
1419
+ } = useConfigurator();
1420
+
1421
+ // --- Dimension calculations ---
1422
+ const {
1423
+ doorWidth,
1424
+ doorHeight,
1425
+ theDoorDepth
1426
+ } = door;
1427
+ const doorOpening = doorOpeningProp || door.doorOpening;
1428
+ const {
1429
+ frameDepth,
1430
+ topThk,
1431
+ sidesThk,
1432
+ doorStopWidth,
1433
+ doorStopDepth,
1434
+ doorStopOffset,
1435
+ notchWidth,
1436
+ notchDepth,
1437
+ notchposition,
1438
+ gasketWidth,
1439
+ gasketDepth,
1440
+ secondDoorStopWidth,
1441
+ secondDoorStopDepth
1442
+ } = doorFrame;
1443
+ const {
1444
+ height: interiorFanlightHeightValue
1445
+ } = interiorFanlight;
1446
+ const {
1447
+ height: exteriorFanlightHeightValue,
1448
+ depth: exteriorFanlightDepthValue
1449
+ } = exteriorFanlight;
1450
+ const {
1451
+ width: architraveWidth,
1452
+ depth: architraveDepth
1453
+ } = frontCoverPanel;
1454
+ const interiorFanlightHeight = interiorFanlight.visible ? interiorFanlightHeightValue : 0;
1455
+ const topThkValue = parseInt(topThk) || 20;
1456
+ const sidesThkValue = parseInt(sidesThk) || 20;
1457
+
1458
+ // Convert dimensions from mm to meters
1459
+ const interiorFanlightHeightM = interiorFanlightHeight / 1000;
1460
+ const doorWidthM = doorWidth / 1000;
1461
+ const totalOpeningHeightM = doorHeight / 1000;
1462
+ const doorDepthM = theDoorDepth / 1000;
1463
+ const frameDepthM = frameDepth / 1000;
1464
+ const topFrameWidthM = topThkValue / 1000;
1465
+ const sidesFrameWidthM = sidesThkValue / 1000;
1466
+ const totalOpeningWidthM = isDoubleDoor ? doorWidthM * 2 : doorWidthM;
1467
+ const exteriorFanlightHeightM = exteriorFanlight.visible ? exteriorFanlightHeightValue / 1000 : 0;
1468
+ const exteriorFanlightDepthM = exteriorFanlightDepthValue / 1000;
1469
+ const doorStopWidthM = doorStopWidth / 1000;
1470
+ const doorStopDepthM = doorStopDepth / 1000;
1471
+ const architraveProfileM = architraveWidth / 1000;
1472
+ const architraveDepthM = architraveDepth / 1000;
1473
+ const gasketWidthM = gasketWidth / 1000;
1474
+ const gasketDepthM = gasketDepth / 1000;
1475
+ const doorStopOffsetFromEdgeM = parseInt(doorStopOffset) / 1000;
1476
+ const notchWidthM = (parseInt(notchWidth) || 20) / 1000;
1477
+ const notchDepthM = (parseInt(notchDepth) || 40) / 1000;
1478
+ const isOpeningIn = doorOpening === "in";
1479
+ const doorStopPositionZ = React.useMemo(() => isOpeningIn ? frameDepthM / 2 - doorStopOffsetFromEdgeM - doorStopDepthM / 2 : -frameDepthM / 2 + doorStopOffsetFromEdgeM + doorStopDepthM / 2, [isOpeningIn, frameDepthM, doorStopOffsetFromEdgeM, doorStopDepthM]);
1480
+ const gasketZPosition = React.useMemo(() => isOpeningIn ? doorStopPositionZ - doorStopDepthM / 2 - gasketDepthM / 2 : doorStopPositionZ + doorStopDepthM / 2 + gasketDepthM / 2, [isOpeningIn, doorStopPositionZ, doorStopDepthM, gasketDepthM]);
1481
+ const secondDoorStopWidthM = secondDoorStopWidth / 1000;
1482
+ const secondDoorStopDepthM = secondDoorStopDepth / 1000;
1483
+ const secondDoorStopPositionZ = isOpeningIn ? frameDepthM / 2 - secondDoorStopDepthM / 2 : -frameDepthM / 2 + secondDoorStopDepthM / 2;
1484
+ const secondGasketZPosition = isOpeningIn ? secondDoorStopPositionZ - secondDoorStopDepthM / 2 - gasketDepthM / 2 : secondDoorStopPositionZ + secondDoorStopDepthM / 2 + gasketDepthM / 2;
1485
+ const doorCenterZ = React.useMemo(() => {
1486
+ if (frameType === "WF_FLI") {
1487
+ return isOpeningIn ? frameDepthM / 2 - doorDepthM / 2 : -frameDepthM / 2 + doorDepthM / 2;
1488
+ }
1489
+ if (["WDGF_WDG100", "WF_100"].includes(frameType)) {
1490
+ return 0;
1491
+ }
1492
+ const gasketFaceZ = isOpeningIn ? gasketZPosition - gasketDepthM / 2 : gasketZPosition + gasketDepthM / 2;
1493
+ return isOpeningIn ? gasketFaceZ - doorDepthM / 2 : gasketFaceZ + doorDepthM / 2;
1494
+ }, [isOpeningIn, gasketZPosition, gasketDepthM, doorDepthM, frameType, frameDepthM]);
1495
+ const GlassPanelDepthM = glassDepth / 1000;
1496
+
1497
+ // --- Centering & Positioning ---
1498
+ const sideFrameCenterY = topFrameWidthM / 2;
1499
+ const interiorFanlightYPosition = totalOpeningHeightM / 2 - interiorFanlightHeightM / 2;
1500
+ const topFrameCenterY = totalOpeningHeightM / 2 + topFrameWidthM / 2;
1501
+ const exteriorFanlightYPosition = topFrameCenterY + topFrameWidthM / 2 + exteriorFanlightHeightM / 2;
1502
+ const frontArchitraveZ = frameDepthM / 2 + architraveDepthM / 2;
1503
+ const backArchitraveZ = -frameDepthM / 2 - architraveDepthM / 2;
1504
+ const leftArchitraveX = -totalOpeningWidthM / 2 - architraveProfileM / 2;
1505
+ const rightArchitraveX = totalOpeningWidthM / 2 + architraveProfileM / 2;
1506
+ const topArchitraveY = totalOpeningHeightM / 2 + architraveProfileM / 2;
1507
+ const topArchitraveWidth = totalOpeningWidthM + 2 * architraveProfileM;
1508
+ const topGasketYPosition = totalOpeningHeightM / 2 - doorStopWidthM + gasketWidthM / 2;
1509
+ const leftGasketXPosition = -totalOpeningWidthM / 2 + doorStopWidthM - gasketWidthM / 2;
1510
+ const rightGasketXPosition = totalOpeningWidthM / 2 - doorStopWidthM + gasketWidthM / 2;
1511
+ const secondTopGasketYPosition = totalOpeningHeightM / 2 - secondDoorStopWidthM + gasketWidthM / 2;
1512
+ const secondLeftGasketXPosition = -totalOpeningWidthM / 2 + secondDoorStopWidthM - gasketWidthM / 2;
1513
+ const secondRightGasketXPosition = totalOpeningWidthM / 2 - secondDoorStopWidthM + gasketWidthM / 2;
1514
+ const leftGlass_Z = -frameDepthM / 2 + GlassPanelDepthM / 2 - GlassPanelDepthM;
1515
+ const rightGlass_Z = frameDepthM / 2 - GlassPanelDepthM / 2 + GlassPanelDepthM;
1516
+ return /*#__PURE__*/jsxRuntime.jsxs(jsxRuntime.Fragment, {
1517
+ children: [frontCoverPanel.visible && /*#__PURE__*/jsxRuntime.jsxs("group", {
1518
+ "position-z": frontArchitraveZ,
1519
+ children: [/*#__PURE__*/jsxRuntime.jsx("mesh", {
1520
+ position: [0, topArchitraveY, 0],
1521
+ castShadow: CAST_SHADOW_MESHES,
1522
+ material: materials.frontCoverPanelMaterial,
1523
+ children: /*#__PURE__*/jsxRuntime.jsx("boxGeometry", {
1524
+ args: [topArchitraveWidth, architraveProfileM, architraveDepthM]
1525
+ })
1526
+ }), /*#__PURE__*/jsxRuntime.jsx("mesh", {
1527
+ position: [leftArchitraveX, 0, 0],
1528
+ castShadow: CAST_SHADOW_MESHES,
1529
+ material: materials.frontCoverPanelMaterial,
1530
+ children: /*#__PURE__*/jsxRuntime.jsx("boxGeometry", {
1531
+ args: [architraveProfileM, totalOpeningHeightM, architraveDepthM]
1532
+ })
1533
+ }), /*#__PURE__*/jsxRuntime.jsx("mesh", {
1534
+ position: [rightArchitraveX, 0, 0],
1535
+ castShadow: CAST_SHADOW_MESHES,
1536
+ material: materials.frontCoverPanelMaterial,
1537
+ children: /*#__PURE__*/jsxRuntime.jsx("boxGeometry", {
1538
+ args: [architraveProfileM, totalOpeningHeightM, architraveDepthM]
1539
+ })
1540
+ })]
1541
+ }), backCoverPanel.visible && /*#__PURE__*/jsxRuntime.jsxs("group", {
1542
+ "position-z": backArchitraveZ,
1543
+ children: [/*#__PURE__*/jsxRuntime.jsx("mesh", {
1544
+ position: [0, topArchitraveY, 0],
1545
+ castShadow: CAST_SHADOW_MESHES,
1546
+ material: materials.backCoverPanelMaterial,
1547
+ children: /*#__PURE__*/jsxRuntime.jsx("boxGeometry", {
1548
+ args: [topArchitraveWidth, architraveProfileM, architraveDepthM]
1549
+ })
1550
+ }), /*#__PURE__*/jsxRuntime.jsx("mesh", {
1551
+ position: [leftArchitraveX, 0, 0],
1552
+ castShadow: CAST_SHADOW_MESHES,
1553
+ material: materials.backCoverPanelMaterial,
1554
+ children: /*#__PURE__*/jsxRuntime.jsx("boxGeometry", {
1555
+ args: [architraveProfileM, totalOpeningHeightM, architraveDepthM]
1556
+ })
1557
+ }), /*#__PURE__*/jsxRuntime.jsx("mesh", {
1558
+ position: [rightArchitraveX, 0, 0],
1559
+ castShadow: CAST_SHADOW_MESHES,
1560
+ material: materials.backCoverPanelMaterial,
1561
+ children: /*#__PURE__*/jsxRuntime.jsx("boxGeometry", {
1562
+ args: [architraveProfileM, totalOpeningHeightM, architraveDepthM]
1563
+ })
1564
+ })]
1565
+ }), exteriorFanlight.visible && /*#__PURE__*/jsxRuntime.jsx("mesh", {
1566
+ position: [0, exteriorFanlightYPosition, 0],
1567
+ castShadow: CAST_SHADOW_MESHES,
1568
+ material: materials.exteriorFanlightMaterial,
1569
+ children: /*#__PURE__*/jsxRuntime.jsx("boxGeometry", {
1570
+ args: [totalOpeningWidthM + sidesFrameWidthM * 2, exteriorFanlightHeightM, exteriorFanlightDepthM]
1571
+ })
1572
+ }), isFrameVisible && /*#__PURE__*/jsxRuntime.jsxs("group", {
1573
+ children: [/*#__PURE__*/jsxRuntime.jsx("mesh", {
1574
+ castShadow: CAST_SHADOW_MESHES,
1575
+ position: [0, topFrameCenterY, 0],
1576
+ children: /*#__PURE__*/jsxRuntime.jsxs(csg.Geometry, {
1577
+ useGroups: true,
1578
+ children: [/*#__PURE__*/jsxRuntime.jsx(csg.Base, {
1579
+ name: "frame-base",
1580
+ material: materials.frameMaterial,
1581
+ children: /*#__PURE__*/jsxRuntime.jsx("boxGeometry", {
1582
+ args: [totalOpeningWidthM, topFrameWidthM, frameDepthM]
1583
+ })
1584
+ }), !["MXF_40", "MXF_50", "MXCAF_40", "MXCAF_50"].includes(frameType) && /*#__PURE__*/jsxRuntime.jsx(csg.Subtraction, {
1585
+ name: "cut-exterior",
1586
+ material: materials.frameMaterial,
1587
+ position: [0, notchposition, 0],
1588
+ children: /*#__PURE__*/jsxRuntime.jsx("boxGeometry", {
1589
+ args: [totalOpeningWidthM + 0.01, notchWidthM, notchDepthM]
1590
+ })
1591
+ })]
1592
+ })
1593
+ }), /*#__PURE__*/jsxRuntime.jsx("mesh", {
1594
+ castShadow: CAST_SHADOW_MESHES,
1595
+ position: [-totalOpeningWidthM / 2 - sidesFrameWidthM / 2, sideFrameCenterY, 0],
1596
+ children: /*#__PURE__*/jsxRuntime.jsxs(csg.Geometry, {
1597
+ useGroups: true,
1598
+ children: [/*#__PURE__*/jsxRuntime.jsx(csg.Base, {
1599
+ name: "frame-base",
1600
+ material: materials.frameMaterial,
1601
+ children: /*#__PURE__*/jsxRuntime.jsx("boxGeometry", {
1602
+ args: [sidesFrameWidthM, totalOpeningHeightM + topFrameWidthM, frameDepthM]
1603
+ })
1604
+ }), !["MXF_40", "MXF_50", "MXCAF_40", "MXCAF_50"].includes(frameType) && /*#__PURE__*/jsxRuntime.jsx(csg.Subtraction, {
1605
+ name: "cut-exterior",
1606
+ material: materials.frameMaterial,
1607
+ position: [-notchposition, 0, 0],
1608
+ children: /*#__PURE__*/jsxRuntime.jsx("boxGeometry", {
1609
+ args: [notchWidthM, totalOpeningHeightM + topFrameWidthM + 0.01, notchDepthM]
1610
+ })
1611
+ })]
1612
+ })
1613
+ }), /*#__PURE__*/jsxRuntime.jsx("mesh", {
1614
+ castShadow: CAST_SHADOW_MESHES,
1615
+ position: [totalOpeningWidthM / 2 + sidesFrameWidthM / 2, sideFrameCenterY, 0],
1616
+ children: /*#__PURE__*/jsxRuntime.jsxs(csg.Geometry, {
1617
+ useGroups: true,
1618
+ children: [/*#__PURE__*/jsxRuntime.jsx(csg.Base, {
1619
+ name: "frame-base",
1620
+ material: materials.frameMaterial,
1621
+ children: /*#__PURE__*/jsxRuntime.jsx("boxGeometry", {
1622
+ args: [sidesFrameWidthM, totalOpeningHeightM + topFrameWidthM, frameDepthM]
1623
+ })
1624
+ }), !["MXF_40", "MXF_50", "MXCAF_40", "MXCAF_50"].includes(frameType) && /*#__PURE__*/jsxRuntime.jsx(csg.Subtraction, {
1625
+ name: "cut-exterior",
1626
+ material: materials.frameMaterial,
1627
+ position: [notchposition, 0, 0],
1628
+ children: /*#__PURE__*/jsxRuntime.jsx("boxGeometry", {
1629
+ args: [notchWidthM, totalOpeningHeightM + topFrameWidthM + 0.01, notchDepthM]
1630
+ })
1631
+ })]
1632
+ })
1633
+ }), /*#__PURE__*/jsxRuntime.jsx("mesh", {
1634
+ castShadow: CAST_SHADOW_MESHES,
1635
+ position: [0, totalOpeningHeightM / 2 - doorStopWidthM / 2, doorStopPositionZ],
1636
+ material: materials.doorStopMaterial,
1637
+ children: /*#__PURE__*/jsxRuntime.jsx("boxGeometry", {
1638
+ args: [totalOpeningWidthM, doorStopWidthM, doorStopDepthM]
1639
+ })
1640
+ }), /*#__PURE__*/jsxRuntime.jsx("mesh", {
1641
+ castShadow: CAST_SHADOW_MESHES,
1642
+ position: [-totalOpeningWidthM / 2 + doorStopWidthM / 2, 0, doorStopPositionZ],
1643
+ material: materials.doorStopMaterial,
1644
+ children: /*#__PURE__*/jsxRuntime.jsx("boxGeometry", {
1645
+ args: [doorStopWidthM, totalOpeningHeightM, doorStopDepthM]
1646
+ })
1647
+ }), /*#__PURE__*/jsxRuntime.jsx("mesh", {
1648
+ castShadow: CAST_SHADOW_MESHES,
1649
+ position: [totalOpeningWidthM / 2 - doorStopWidthM / 2, 0, doorStopPositionZ],
1650
+ material: materials.doorStopMaterial,
1651
+ children: /*#__PURE__*/jsxRuntime.jsx("boxGeometry", {
1652
+ args: [doorStopWidthM, totalOpeningHeightM, doorStopDepthM]
1653
+ })
1654
+ }), frameType !== "WF_FLI" && /*#__PURE__*/jsxRuntime.jsxs(jsxRuntime.Fragment, {
1655
+ children: [/*#__PURE__*/jsxRuntime.jsx("mesh", {
1656
+ castShadow: CAST_SHADOW_MESHES,
1657
+ position: [0, topGasketYPosition, gasketZPosition],
1658
+ material: materials.gasketMaterial,
1659
+ children: /*#__PURE__*/jsxRuntime.jsx("boxGeometry", {
1660
+ args: [totalOpeningWidthM - 0.03, gasketWidthM + 0.005, gasketDepthM]
1661
+ })
1662
+ }), /*#__PURE__*/jsxRuntime.jsx("mesh", {
1663
+ castShadow: CAST_SHADOW_MESHES,
1664
+ position: [leftGasketXPosition - 0.005, -0.005, gasketZPosition],
1665
+ material: materials.gasketMaterial,
1666
+ children: /*#__PURE__*/jsxRuntime.jsx("boxGeometry", {
1667
+ args: [gasketWidthM + 0.005, totalOpeningHeightM - 0.02, gasketDepthM]
1668
+ })
1669
+ }), /*#__PURE__*/jsxRuntime.jsx("mesh", {
1670
+ castShadow: CAST_SHADOW_MESHES,
1671
+ position: [rightGasketXPosition + 0.005, -0.005, gasketZPosition],
1672
+ material: materials.gasketMaterial,
1673
+ children: /*#__PURE__*/jsxRuntime.jsx("boxGeometry", {
1674
+ args: [gasketWidthM + 0.005, totalOpeningHeightM - 0.02, gasketDepthM]
1675
+ })
1676
+ })]
1677
+ }), ["WDGF_WDG100", "WF_100", "WF_FLI"].includes(frameType) && /*#__PURE__*/jsxRuntime.jsxs("group", {
1678
+ children: [/*#__PURE__*/jsxRuntime.jsxs("group", {
1679
+ children: [/*#__PURE__*/jsxRuntime.jsx("mesh", {
1680
+ castShadow: CAST_SHADOW_MESHES,
1681
+ position: [0, totalOpeningHeightM / 2 - secondDoorStopWidthM / 2, secondDoorStopPositionZ],
1682
+ material: materials.doorStopMaterial,
1683
+ children: /*#__PURE__*/jsxRuntime.jsx("boxGeometry", {
1684
+ args: [totalOpeningWidthM, secondDoorStopWidthM, secondDoorStopDepthM]
1685
+ })
1686
+ }), /*#__PURE__*/jsxRuntime.jsx("mesh", {
1687
+ castShadow: CAST_SHADOW_MESHES,
1688
+ position: [-totalOpeningWidthM / 2 + secondDoorStopWidthM / 2, 0, secondDoorStopPositionZ],
1689
+ material: materials.doorStopMaterial,
1690
+ children: /*#__PURE__*/jsxRuntime.jsx("boxGeometry", {
1691
+ args: [secondDoorStopWidthM, totalOpeningHeightM, secondDoorStopDepthM]
1692
+ })
1693
+ }), /*#__PURE__*/jsxRuntime.jsx("mesh", {
1694
+ castShadow: CAST_SHADOW_MESHES,
1695
+ position: [totalOpeningWidthM / 2 - secondDoorStopWidthM / 2, 0, secondDoorStopPositionZ],
1696
+ material: materials.doorStopMaterial,
1697
+ children: /*#__PURE__*/jsxRuntime.jsx("boxGeometry", {
1698
+ args: [secondDoorStopWidthM, totalOpeningHeightM, secondDoorStopDepthM]
1699
+ })
1700
+ })]
1701
+ }), /*#__PURE__*/jsxRuntime.jsxs("group", {
1702
+ children: [/*#__PURE__*/jsxRuntime.jsx("mesh", {
1703
+ castShadow: CAST_SHADOW_MESHES,
1704
+ position: [0, secondTopGasketYPosition - 0.005, secondGasketZPosition],
1705
+ material: materials.gasketMaterial,
1706
+ children: /*#__PURE__*/jsxRuntime.jsx("boxGeometry", {
1707
+ args: [totalOpeningWidthM - 0.03, gasketWidthM + 0.005, gasketDepthM]
1708
+ })
1709
+ }), /*#__PURE__*/jsxRuntime.jsx("mesh", {
1710
+ castShadow: CAST_SHADOW_MESHES,
1711
+ position: [secondLeftGasketXPosition - 0.005, -0.01, secondGasketZPosition],
1712
+ material: materials.gasketMaterial,
1713
+ children: /*#__PURE__*/jsxRuntime.jsx("boxGeometry", {
1714
+ args: [gasketWidthM + 0.005, totalOpeningHeightM - 0.04, gasketDepthM]
1715
+ })
1716
+ }), /*#__PURE__*/jsxRuntime.jsx("mesh", {
1717
+ castShadow: CAST_SHADOW_MESHES,
1718
+ position: [secondRightGasketXPosition + 0.005, -0.01, secondGasketZPosition],
1719
+ material: materials.gasketMaterial,
1720
+ children: /*#__PURE__*/jsxRuntime.jsx("boxGeometry", {
1721
+ args: [gasketWidthM + 0.004, totalOpeningHeightM - 0.04, gasketDepthM]
1722
+ })
1723
+ })]
1724
+ }), glassVisible && /*#__PURE__*/jsxRuntime.jsxs(jsxRuntime.Fragment, {
1725
+ children: [/*#__PURE__*/jsxRuntime.jsxs("group", {
1726
+ children: [["WDGF_WDG100", "WF_100"].includes(frameType) && /*#__PURE__*/jsxRuntime.jsx("mesh", {
1727
+ castShadow: CAST_SHADOW_MESHES,
1728
+ position: [0, topFrameCenterY - secondDoorStopWidthM / 2, leftGlass_Z],
1729
+ material: materials.glassInfillMaterial,
1730
+ children: /*#__PURE__*/jsxRuntime.jsx("boxGeometry", {
1731
+ args: [totalOpeningWidthM - 2 * secondDoorStopWidthM, topFrameWidthM + secondDoorStopWidthM, GlassPanelDepthM]
1732
+ })
1733
+ }), !["WDGF_WDG100", "WF_100"].includes(frameType) && /*#__PURE__*/jsxRuntime.jsx("mesh", {
1734
+ castShadow: CAST_SHADOW_MESHES,
1735
+ position: [0, topFrameCenterY, leftGlass_Z],
1736
+ material: materials.glassInfillMaterial,
1737
+ children: /*#__PURE__*/jsxRuntime.jsx("boxGeometry", {
1738
+ args: [totalOpeningWidthM - 2 * secondDoorStopWidthM, topFrameWidthM, GlassPanelDepthM]
1739
+ })
1740
+ }), /*#__PURE__*/jsxRuntime.jsx("mesh", {
1741
+ castShadow: CAST_SHADOW_MESHES,
1742
+ position: [-totalOpeningWidthM / 2 - sidesFrameWidthM / 2 + secondDoorStopWidthM / 2, sideFrameCenterY, leftGlass_Z],
1743
+ material: materials.glassInfillMaterial,
1744
+ children: /*#__PURE__*/jsxRuntime.jsx("boxGeometry", {
1745
+ args: [sidesFrameWidthM + secondDoorStopWidthM, totalOpeningHeightM + topFrameWidthM, GlassPanelDepthM]
1746
+ })
1747
+ }), /*#__PURE__*/jsxRuntime.jsx("mesh", {
1748
+ castShadow: CAST_SHADOW_MESHES,
1749
+ position: [totalOpeningWidthM / 2 + sidesFrameWidthM / 2 - secondDoorStopWidthM / 2, sideFrameCenterY, leftGlass_Z],
1750
+ material: materials.glassInfillMaterial,
1751
+ children: /*#__PURE__*/jsxRuntime.jsx("boxGeometry", {
1752
+ args: [sidesFrameWidthM + secondDoorStopWidthM, totalOpeningHeightM + topFrameWidthM, GlassPanelDepthM]
1753
+ })
1754
+ })]
1755
+ }), /*#__PURE__*/jsxRuntime.jsxs("group", {
1756
+ children: [/*#__PURE__*/jsxRuntime.jsx("mesh", {
1757
+ castShadow: CAST_SHADOW_MESHES,
1758
+ position: [0, topFrameCenterY, rightGlass_Z],
1759
+ material: materials.glassInfillMaterial,
1760
+ children: /*#__PURE__*/jsxRuntime.jsx("boxGeometry", {
1761
+ args: [totalOpeningWidthM, topFrameWidthM, GlassPanelDepthM]
1762
+ })
1763
+ }), /*#__PURE__*/jsxRuntime.jsx("mesh", {
1764
+ castShadow: CAST_SHADOW_MESHES,
1765
+ position: [-totalOpeningWidthM / 2 - sidesFrameWidthM / 2, sideFrameCenterY, rightGlass_Z],
1766
+ material: materials.glassInfillMaterial,
1767
+ children: /*#__PURE__*/jsxRuntime.jsx("boxGeometry", {
1768
+ args: [sidesFrameWidthM, totalOpeningHeightM + topFrameWidthM, GlassPanelDepthM]
1769
+ })
1770
+ }), /*#__PURE__*/jsxRuntime.jsx("mesh", {
1771
+ castShadow: CAST_SHADOW_MESHES,
1772
+ position: [totalOpeningWidthM / 2 + sidesFrameWidthM / 2, sideFrameCenterY, rightGlass_Z],
1773
+ material: materials.glassInfillMaterial,
1774
+ children: /*#__PURE__*/jsxRuntime.jsx("boxGeometry", {
1775
+ args: [sidesFrameWidthM, totalOpeningHeightM + topFrameWidthM, GlassPanelDepthM]
1776
+ })
1777
+ })]
1778
+ })]
1779
+ })]
1780
+ })]
1781
+ }), interiorFanlight.visible && /*#__PURE__*/jsxRuntime.jsx("mesh", {
1782
+ position: [0, interiorFanlightYPosition, doorCenterZ],
1783
+ castShadow: CAST_SHADOW_MESHES,
1784
+ material: materials.interiorFanlightMaterial,
1785
+ children: /*#__PURE__*/jsxRuntime.jsx("boxGeometry", {
1786
+ args: [totalOpeningWidthM, interiorFanlightHeightM, doorDepthM]
1787
+ })
1788
+ })]
1789
+ });
1790
+ }
1791
+
1792
+ const availableMaterials = {
1793
+ black: new THREE__namespace.MeshStandardMaterial({
1794
+ color: "#000000"
1795
+ }),
1796
+ metal: new THREE__namespace.MeshStandardMaterial({
1797
+ color: "#aaaaaa",
1798
+ metalness: 1.0,
1799
+ roughness: 0.4
1800
+ }),
1801
+ darkgrey: new THREE__namespace.MeshStandardMaterial({
1802
+ color: "#5e5e5e"
1803
+ }),
1804
+ grey: new THREE__namespace.MeshStandardMaterial({
1805
+ color: "#cccccc"
1806
+ }),
1807
+ yellow: new THREE__namespace.MeshStandardMaterial({
1808
+ color: "#ffff00"
1809
+ }),
1810
+ darkBrown: new THREE__namespace.MeshStandardMaterial({
1811
+ color: "#a0522d"
1812
+ }),
1813
+ brown: new THREE__namespace.MeshStandardMaterial({
1814
+ color: "#d2b48c"
1815
+ }),
1816
+ glass: new THREE__namespace.MeshStandardMaterial({
1817
+ color: "#ffffff",
1818
+ roughness: 0.1,
1819
+ transparent: true,
1820
+ opacity: 0.7
1821
+ }),
1822
+ aluminum: new THREE__namespace.MeshStandardMaterial({
1823
+ color: "#abb0aa",
1824
+ metalness: 0.8,
1825
+ roughness: 0.5,
1826
+ envMapIntensity: 1.2
1827
+ }),
1828
+ aluminumBrighter: new THREE__namespace.MeshStandardMaterial({
1829
+ color: "#cdcdcd",
1830
+ metalness: 0.8,
1831
+ roughness: 0.5,
1832
+ envMapIntensity: 1.2
1833
+ }),
1834
+ aluminumExtraBrighter: new THREE__namespace.MeshStandardMaterial({
1835
+ color: "#ececec",
1836
+ metalness: 0.8,
1837
+ roughness: 0.5,
1838
+ envMapIntensity: 1.2
1839
+ }),
1840
+ silver: new THREE__namespace.MeshStandardMaterial({
1841
+ color: "#c0c0c0",
1842
+ metalness: 1.0,
1843
+ roughness: 0.2
1844
+ }),
1845
+ gold: new THREE__namespace.MeshStandardMaterial({
1846
+ color: "#ffd700",
1847
+ metalness: 1.0,
1848
+ roughness: 0.3
1849
+ }),
1850
+ diamond: new THREE__namespace.MeshStandardMaterial({
1851
+ color: "#e0f7fa",
1852
+ metalness: 0.9,
1853
+ roughness: 0.05,
1854
+ transparent: true,
1855
+ opacity: 0.9,
1856
+ envMapIntensity: 1.5
1857
+ }),
1858
+ test_material: new THREE__namespace.MeshStandardMaterial({
1859
+ color: "red",
1860
+ roughness: 0.1
1861
+ })
1862
+ };
1863
+ new THREE__namespace.MeshStandardMaterial({
1864
+ color: "#cccccc",
1865
+ roughness: 0.8
1866
+ });
1867
+ new THREE__namespace.MeshStandardMaterial({
1868
+ color: "#000",
1869
+ roughness: 0.8
1870
+ });
1871
+
1872
+ const MergedPlate = _ref => {
1873
+ let {
1874
+ plateMaterial,
1875
+ roseThickness,
1876
+ faceZ
1877
+ } = _ref;
1878
+ const geometry = React.useMemo(() => {
1879
+ const shape = new THREE__namespace.Shape();
1880
+ const width = 0.1;
1881
+ const height = 0.24;
1882
+ const radius = 0.01;
1883
+ shape.moveTo(-width / 2, -height / 2);
1884
+ shape.lineTo(-width / 2, height / 2);
1885
+ shape.lineTo(width / 2 - radius, height / 2);
1886
+ shape.absarc(width / 2 - radius, height / 2 - radius, radius, Math.PI / 2, 0, true);
1887
+ shape.lineTo(width / 2, -height / 2 + radius);
1888
+ shape.absarc(width / 2 - radius, -height / 2 + radius, radius, 0, -Math.PI / 2, true);
1889
+ shape.lineTo(-width / 2, -height / 2);
1890
+ const extrudeSettings = {
1891
+ steps: 2,
1892
+ depth: roseThickness,
1893
+ bevelEnabled: false
1894
+ };
1895
+ return new THREE__namespace.ExtrudeGeometry(shape, extrudeSettings);
1896
+ }, [roseThickness]);
1897
+ return /*#__PURE__*/jsxRuntime.jsx("group", {
1898
+ "position-z": faceZ,
1899
+ children: /*#__PURE__*/jsxRuntime.jsx("mesh", {
1900
+ geometry: geometry,
1901
+ material: plateMaterial,
1902
+ castShadow: CAST_SHADOW_MESHES
1903
+ })
1904
+ });
1905
+ };
1906
+
1907
+ function GlassHandle(_ref) {
1908
+ let {
1909
+ position,
1910
+ doorDepthM,
1911
+ doorPivot
1912
+ } = _ref;
1913
+ const roseRadius = 0.035;
1914
+ const roseThickness = 0.005;
1915
+ const handleRadius = 0.015;
1916
+ const handleLength = 0.2;
1917
+ const faceZ = doorDepthM / 2 + roseThickness / 2;
1918
+ const connectorLength = handleLength / 3;
1919
+ return /*#__PURE__*/jsxRuntime.jsxs("group", {
1920
+ position: position,
1921
+ children: [[faceZ, -faceZ].map((z, i) => /*#__PURE__*/jsxRuntime.jsxs(React__default["default"].Fragment, {
1922
+ children: [/*#__PURE__*/jsxRuntime.jsx("mesh", {
1923
+ rotation: [Math.PI / 2, 0, 0],
1924
+ "position-z": z + (z > 0 ? roseThickness : -roseThickness) + 0.002,
1925
+ material: availableMaterials.aluminum,
1926
+ castShadow: CAST_SHADOW_MESHES,
1927
+ children: /*#__PURE__*/jsxRuntime.jsx("cylinderGeometry", {
1928
+ args: [roseRadius, roseRadius, roseThickness, ROSE_SEGMENTS]
1929
+ })
1930
+ }), /*#__PURE__*/jsxRuntime.jsxs("mesh", {
1931
+ position: [0, -0.08, z + (z > 0 ? roseThickness : -roseThickness)],
1932
+ castShadow: CAST_SHADOW_MESHES,
1933
+ children: [/*#__PURE__*/jsxRuntime.jsx("mesh", {
1934
+ rotation: [Math.PI / 2, 0, 0],
1935
+ position: [0, 0.0075, 0],
1936
+ material: availableMaterials.aluminum,
1937
+ children: /*#__PURE__*/jsxRuntime.jsx("cylinderGeometry", {
1938
+ args: [0.01, 0.01, roseThickness, KEYHOLE_SEGMENTS]
1939
+ })
1940
+ }), /*#__PURE__*/jsxRuntime.jsx("mesh", {
1941
+ position: [0, -0.0075, 0],
1942
+ material: availableMaterials.aluminum,
1943
+ children: /*#__PURE__*/jsxRuntime.jsx("boxGeometry", {
1944
+ args: [0.01, 0.015, roseThickness]
1945
+ })
1946
+ })]
1947
+ })]
1948
+ }, i)), /*#__PURE__*/jsxRuntime.jsxs("group", {
1949
+ "scale-x": doorPivot === "left" ? -1 : 1,
1950
+ children: [[faceZ, -faceZ].map((z, i) => /*#__PURE__*/jsxRuntime.jsx(MergedPlate, {
1951
+ faceZ: z,
1952
+ plateMaterial: availableMaterials.aluminumExtraBrighter,
1953
+ roseThickness: roseThickness
1954
+ }, "plate-".concat(i))), /*#__PURE__*/jsxRuntime.jsxs("group", {
1955
+ children: [/*#__PURE__*/jsxRuntime.jsx("mesh", {
1956
+ position: [handleLength / 2, 0, faceZ + connectorLength],
1957
+ rotation: [0, 0, Math.PI / 2],
1958
+ material: availableMaterials.aluminum,
1959
+ castShadow: CAST_SHADOW_MESHES,
1960
+ children: /*#__PURE__*/jsxRuntime.jsx("cylinderGeometry", {
1961
+ args: [handleRadius, handleRadius, handleLength, HANDLE_SEGMENTS]
1962
+ })
1963
+ }), /*#__PURE__*/jsxRuntime.jsx("mesh", {
1964
+ position: [0, 0, faceZ + connectorLength / 2],
1965
+ rotation: [Math.PI / 2, 0, 0],
1966
+ material: availableMaterials.aluminum,
1967
+ castShadow: CAST_SHADOW_MESHES,
1968
+ children: /*#__PURE__*/jsxRuntime.jsx("cylinderGeometry", {
1969
+ args: [handleRadius, handleRadius, connectorLength, HANDLE_SEGMENTS]
1970
+ })
1971
+ }), /*#__PURE__*/jsxRuntime.jsx("mesh", {
1972
+ position: [0, 0, faceZ + connectorLength],
1973
+ material: availableMaterials.aluminum,
1974
+ castShadow: CAST_SHADOW_MESHES,
1975
+ children: /*#__PURE__*/jsxRuntime.jsx("sphereGeometry", {
1976
+ args: [handleRadius, HANDLE_SPHERE_SEGMENTS, HANDLE_SPHERE_SEGMENTS]
1977
+ })
1978
+ })]
1979
+ }), /*#__PURE__*/jsxRuntime.jsxs("group", {
1980
+ children: [/*#__PURE__*/jsxRuntime.jsx("mesh", {
1981
+ position: [handleLength / 2, 0, -(faceZ + connectorLength)],
1982
+ rotation: [0, 0, Math.PI / 2],
1983
+ material: availableMaterials.aluminum,
1984
+ castShadow: CAST_SHADOW_MESHES,
1985
+ children: /*#__PURE__*/jsxRuntime.jsx("cylinderGeometry", {
1986
+ args: [handleRadius, handleRadius, handleLength, HANDLE_SEGMENTS]
1987
+ })
1988
+ }), /*#__PURE__*/jsxRuntime.jsx("mesh", {
1989
+ position: [0, 0, -(faceZ + connectorLength / 2)],
1990
+ rotation: [Math.PI / 2, 0, 0],
1991
+ material: availableMaterials.aluminum,
1992
+ castShadow: CAST_SHADOW_MESHES,
1993
+ children: /*#__PURE__*/jsxRuntime.jsx("cylinderGeometry", {
1994
+ args: [handleRadius, handleRadius, connectorLength, HANDLE_SEGMENTS]
1995
+ })
1996
+ }), /*#__PURE__*/jsxRuntime.jsx("mesh", {
1997
+ position: [0, 0, -(faceZ + connectorLength)],
1998
+ material: availableMaterials.aluminum,
1999
+ castShadow: CAST_SHADOW_MESHES,
2000
+ children: /*#__PURE__*/jsxRuntime.jsx("sphereGeometry", {
2001
+ args: [handleRadius, HANDLE_SPHERE_SEGMENTS, HANDLE_SPHERE_SEGMENTS]
2002
+ })
2003
+ })]
2004
+ })]
2005
+ })]
2006
+ });
2007
+ }
2008
+
2009
+ function StandardHandle(_ref) {
2010
+ let {
2011
+ position,
2012
+ doorDepthM,
2013
+ doorPivot,
2014
+ frameType,
2015
+ handleColor
2016
+ } = _ref;
2017
+ const frontGLTF = drei.useGLTF("/glb/HandlerStandardFront.glb");
2018
+ const backGLTF = drei.useGLTF("/glb/HandlerStandardBack.glb");
2019
+
2020
+ // Apply color to front handle materials
2021
+ React__default["default"].useEffect(() => {
2022
+ if (frontGLTF.materials) {
2023
+ Object.values(frontGLTF.materials).forEach(material => {
2024
+ if (material) {
2025
+ material.color.set(handleColor);
2026
+ }
2027
+ });
2028
+ }
2029
+ }, [frontGLTF.materials, handleColor]);
2030
+
2031
+ // Apply color to back handle materials
2032
+ React__default["default"].useEffect(() => {
2033
+ if (backGLTF.materials) {
2034
+ Object.values(backGLTF.materials).forEach(material => {
2035
+ if (material) {
2036
+ material.color.set(handleColor);
2037
+ }
2038
+ });
2039
+ }
2040
+ }, [backGLTF.materials, handleColor]);
2041
+
2042
+ // --- Thickness ---
2043
+ // default 5mm, but 8mm + 1–2mm if WDG frame
2044
+ const roseThickness = frameType === "WDGF_WDG100" ? 0.008 + 0.0015 : 0.005;
2045
+
2046
+ // face offset based on thickness
2047
+ const faceZ = doorDepthM / 2 + roseThickness / 2;
2048
+
2049
+ // Clone scenes
2050
+ const frontScene = React__default["default"].useMemo(() => frontGLTF.nodes.Scene.clone() || frontGLTF.nodes.root.clone(), [frontGLTF]);
2051
+ const backScene = React__default["default"].useMemo(() => backGLTF.nodes.Scene.clone() || backGLTF.nodes.root.clone(), [backGLTF]);
2052
+ return /*#__PURE__*/jsxRuntime.jsx("group", {
2053
+ scale: 1.5,
2054
+ position: position,
2055
+ children: /*#__PURE__*/jsxRuntime.jsxs("group", {
2056
+ scale: [doorPivot === "left" ? -1 : 1, 1, 1],
2057
+ children: [/*#__PURE__*/jsxRuntime.jsx("group", {
2058
+ position: [0, 0, faceZ],
2059
+ children: /*#__PURE__*/jsxRuntime.jsx("primitive", {
2060
+ object: frontScene,
2061
+ castShadow: CAST_SHADOW_MESHES
2062
+ })
2063
+ }), /*#__PURE__*/jsxRuntime.jsx("group", {
2064
+ position: [0, 0, -faceZ],
2065
+ rotation: [0, Math.PI, 0],
2066
+ children: /*#__PURE__*/jsxRuntime.jsx("primitive", {
2067
+ object: backScene,
2068
+ castShadow: CAST_SHADOW_MESHES
2069
+ })
2070
+ })]
2071
+ })
2072
+ });
2073
+ }
2074
+
2075
+ function DoorHandle(_ref) {
2076
+ let {
2077
+ bodyType,
2078
+ handleX,
2079
+ handleHeightM,
2080
+ doorDepthM,
2081
+ doorPivot,
2082
+ handleColor
2083
+ } = _ref;
2084
+ const isSingleGlass = ["SG8", "SG10", "SG12"].includes(bodyType);
2085
+ const handlePos = [handleX, handleHeightM, 0];
2086
+ const {
2087
+ frameType
2088
+ } = useConfigurator();
2089
+ return isSingleGlass ? /*#__PURE__*/jsxRuntime.jsx(GlassHandle, {
2090
+ position: [handleX, handleHeightM, -0.0024],
2091
+ doorDepthM: doorDepthM,
2092
+ doorPivot: doorPivot
2093
+ }) : /*#__PURE__*/jsxRuntime.jsx(StandardHandle, {
2094
+ position: handlePos,
2095
+ doorDepthM: doorDepthM,
2096
+ doorPivot: doorPivot,
2097
+ frameType: frameType,
2098
+ handleColor: handleColor
2099
+ });
2100
+ }
2101
+
2102
+ function GlassHinge(_ref) {
2103
+ let {
2104
+ position,
2105
+ pivot,
2106
+ frameSideWidth,
2107
+ doorDepthM,
2108
+ material,
2109
+ gasketMaterial
2110
+ } = _ref;
2111
+ const plateWidth = 130 / 1000;
2112
+ const plateHeight = 55 / 1000;
2113
+ const plateThickness = 8 / 1000;
2114
+ const gasketThickness = 1 / 1000;
2115
+ const pivotRadius = 10 / 1000;
2116
+ const pivotHeight = plateHeight;
2117
+ const sideMultiplier = pivot === "left" ? 1 : -1;
2118
+ const plateOffsetX = sideMultiplier * (plateWidth / 3 + frameSideWidth);
2119
+ const holeRadius = 10 / 1000;
2120
+ const holeSpacingX = 45 / 1000;
2121
+ return /*#__PURE__*/jsxRuntime.jsxs("group", {
2122
+ position: position,
2123
+ children: [/*#__PURE__*/jsxRuntime.jsx("mesh", {
2124
+ material: material,
2125
+ castShadow: CAST_SHADOW_MESHES,
2126
+ receiveShadow: RECEIVE_SHADOW_MESHES,
2127
+ "position-x": 0.01,
2128
+ children: /*#__PURE__*/jsxRuntime.jsx("cylinderGeometry", {
2129
+ args: [pivotRadius, pivotRadius, pivotHeight, HINGE_SEGMENTS]
2130
+ })
2131
+ }), /*#__PURE__*/jsxRuntime.jsxs("group", {
2132
+ "position-x": plateOffsetX,
2133
+ children: [/*#__PURE__*/jsxRuntime.jsx("mesh", {
2134
+ "position-z": -(doorDepthM / 2) - plateThickness / 2 - gasketThickness,
2135
+ material: material,
2136
+ castShadow: CAST_SHADOW_MESHES,
2137
+ children: /*#__PURE__*/jsxRuntime.jsx("boxGeometry", {
2138
+ args: [plateWidth, plateHeight, plateThickness]
2139
+ })
2140
+ }), /*#__PURE__*/jsxRuntime.jsx("mesh", {
2141
+ "position-z": -(doorDepthM / 2) - gasketThickness / 2,
2142
+ material: gasketMaterial,
2143
+ castShadow: CAST_SHADOW_MESHES,
2144
+ children: /*#__PURE__*/jsxRuntime.jsx("boxGeometry", {
2145
+ args: [plateWidth, plateHeight, gasketThickness]
2146
+ })
2147
+ }), /*#__PURE__*/jsxRuntime.jsx("mesh", {
2148
+ "position-z": doorDepthM / 2 + plateThickness / 2 + gasketThickness,
2149
+ castShadow: CAST_SHADOW_MESHES,
2150
+ children: /*#__PURE__*/jsxRuntime.jsxs(csg.Geometry, {
2151
+ useGroups: true,
2152
+ children: [/*#__PURE__*/jsxRuntime.jsx(csg.Base, {
2153
+ material: material,
2154
+ children: /*#__PURE__*/jsxRuntime.jsx("boxGeometry", {
2155
+ args: [plateWidth, plateHeight, plateThickness]
2156
+ })
2157
+ }), /*#__PURE__*/jsxRuntime.jsx(csg.Subtraction, {
2158
+ position: [-sideMultiplier * (holeSpacingX / 2), 0, 0],
2159
+ rotation: [Math.PI / 2, 0, 0],
2160
+ children: /*#__PURE__*/jsxRuntime.jsx("cylinderGeometry", {
2161
+ args: [holeRadius, holeRadius, plateThickness + 0.01, KEYHOLE_SEGMENTS]
2162
+ })
2163
+ }), /*#__PURE__*/jsxRuntime.jsx(csg.Subtraction, {
2164
+ position: [sideMultiplier * (holeSpacingX / 2), 0, 0],
2165
+ rotation: [Math.PI / 2, 0, 0],
2166
+ children: /*#__PURE__*/jsxRuntime.jsx("cylinderGeometry", {
2167
+ args: [holeRadius, holeRadius, plateThickness + 0.01, KEYHOLE_SEGMENTS]
2168
+ })
2169
+ })]
2170
+ })
2171
+ }), /*#__PURE__*/jsxRuntime.jsx("mesh", {
2172
+ "position-z": doorDepthM / 2 + gasketThickness / 2,
2173
+ material: gasketMaterial,
2174
+ castShadow: CAST_SHADOW_MESHES,
2175
+ children: /*#__PURE__*/jsxRuntime.jsx("boxGeometry", {
2176
+ args: [plateWidth, plateHeight, gasketThickness]
2177
+ })
2178
+ })]
2179
+ })]
2180
+ });
2181
+ }
2182
+
2183
+ function DoorStopCuts(_ref) {
2184
+ let {
2185
+ visible,
2186
+ width,
2187
+ height,
2188
+ stopWidth,
2189
+ stopDepth,
2190
+ stopPositionZ,
2191
+ centerZ,
2192
+ material,
2193
+ setName
2194
+ } = _ref;
2195
+ if (!visible) return null;
2196
+ return /*#__PURE__*/jsxRuntime.jsxs(jsxRuntime.Fragment, {
2197
+ children: [/*#__PURE__*/jsxRuntime.jsx(csg.Subtraction, {
2198
+ name: "cut-top-".concat(setName),
2199
+ material: material,
2200
+ position: [0, height / 2 - stopWidth / 2 + 0.0001, stopPositionZ - centerZ],
2201
+ children: /*#__PURE__*/jsxRuntime.jsx("boxGeometry", {
2202
+ args: [width + 0.01, stopWidth, stopDepth + 0.01]
2203
+ })
2204
+ }), /*#__PURE__*/jsxRuntime.jsx(csg.Subtraction, {
2205
+ name: "cut-left-".concat(setName),
2206
+ material: material,
2207
+ position: [-width / 2 + stopWidth / 2 - 0.0001, 0, stopPositionZ - centerZ],
2208
+ children: /*#__PURE__*/jsxRuntime.jsx("boxGeometry", {
2209
+ args: [stopWidth, height + 0.01, stopDepth + 0.01]
2210
+ })
2211
+ }), /*#__PURE__*/jsxRuntime.jsx(csg.Subtraction, {
2212
+ name: "cut-right-".concat(setName),
2213
+ material: material,
2214
+ position: [width / 2 - stopWidth / 2 + 0.0001, 0, stopPositionZ - centerZ],
2215
+ children: /*#__PURE__*/jsxRuntime.jsx("boxGeometry", {
2216
+ args: [stopWidth, height + 0.01, stopDepth + 0.01]
2217
+ })
2218
+ })]
2219
+ });
2220
+ }
2221
+
2222
+ function DoorLeaf(_ref) {
2223
+ let {
2224
+ pivot,
2225
+ xOffset,
2226
+ materials,
2227
+ handleColor
2228
+ } = _ref;
2229
+ // Destructure materials from props.
2230
+ const {
2231
+ doorMaterial,
2232
+ glassInfillMaterial,
2233
+ hingeBodyMaterial,
2234
+ gasketMaterial,
2235
+ occulusInfillMaterial,
2236
+ frontDoorPlaneMaterial,
2237
+ backDoorPlaneMaterial,
2238
+ hingeAccentMaterial
2239
+ } = materials;
2240
+
2241
+ // Get NON-MATERIAL state from the context.
2242
+ const {
2243
+ isFrameVisible,
2244
+ door,
2245
+ doorFrame,
2246
+ interiorFanlight,
2247
+ occulus,
2248
+ frontDoorPlane,
2249
+ backDoorPlane,
2250
+ frameType,
2251
+ bodyType,
2252
+ glassVisible,
2253
+ glassDepth
2254
+ } = useConfigurator();
2255
+
2256
+ // --- Dimension calculations ---
2257
+ const {
2258
+ doorWidth,
2259
+ doorHeight,
2260
+ theDoorDepth,
2261
+ doorOpening
2262
+ } = door;
2263
+ const {
2264
+ frameDepth,
2265
+ sidesThk,
2266
+ doorStopWidth,
2267
+ doorStopDepth,
2268
+ doorStopOffset,
2269
+ gasketDepth,
2270
+ secondDoorStopWidth,
2271
+ secondDoorStopDepth
2272
+ } = doorFrame;
2273
+ const initialDoorHeight = React.useRef(null);
2274
+ React.useEffect(() => {
2275
+ if (doorHeight > 0 && initialDoorHeight.current === null) {
2276
+ initialDoorHeight.current = doorHeight;
2277
+ }
2278
+ }, [doorHeight]);
2279
+ const {
2280
+ x1: occulusX1,
2281
+ x2: occulusX2,
2282
+ y1: occulusY1,
2283
+ y2: occulusY2,
2284
+ depth: occulusInfillDepth
2285
+ } = occulus;
2286
+ const interiorFanlightHeight = interiorFanlight.visible ? interiorFanlight.height : 0;
2287
+ const mainDoorHeight = doorHeight - interiorFanlightHeight;
2288
+ const sidesThkValue = parseInt(sidesThk) || 20;
2289
+
2290
+ // Convert dimensions from mm to meters
2291
+ const doorWidthM = doorWidth / 1000;
2292
+ const mainDoorHeightM = mainDoorHeight / 1000;
2293
+ const totalOpeningHeightM = doorHeight / 1000;
2294
+ const doorDepthM = theDoorDepth / 1000;
2295
+ const frameDepthM = frameDepth / 1000;
2296
+ const sidesFrameWidthM = sidesThkValue / 1000;
2297
+ const occulusX1M = occulusX1 / 1000;
2298
+ const occulusX2M = occulusX2 / 1000;
2299
+ const occulusY1M = occulusY1 / 1000;
2300
+ const occulusY2M = occulusY2 / 1000;
2301
+ const occulusInfillDepthM = occulusInfillDepth / 1000;
2302
+ const doorStopWidthM = doorStopWidth / 1000;
2303
+ const doorStopDepthM = doorStopDepth / 1000;
2304
+ const gasketDepthM = gasketDepth / 1000;
2305
+ const doorStopOffsetFromEdgeM = parseInt(doorStopOffset) / 1000;
2306
+ const isOpeningIn = doorOpening === "in";
2307
+ const doorStopPositionZ = React.useMemo(() => isOpeningIn ? frameDepthM / 2 - doorStopOffsetFromEdgeM - doorStopDepthM / 2 : -frameDepthM / 2 + doorStopOffsetFromEdgeM + doorStopDepthM / 2, [isOpeningIn, frameDepthM, doorStopOffsetFromEdgeM, doorStopDepthM]);
2308
+ const gasketZPosition = React.useMemo(() => isOpeningIn ? doorStopPositionZ - doorStopDepthM / 2 - gasketDepthM / 2 : doorStopPositionZ + doorStopDepthM / 2 + gasketDepthM / 2, [isOpeningIn, doorStopPositionZ, doorStopDepthM, gasketDepthM]);
2309
+ const secondDoorStopWidthM = secondDoorStopWidth / 1000;
2310
+ const secondDoorStopDepthM = secondDoorStopDepth / 1000;
2311
+ const secondDoorStopPositionZ = isOpeningIn ? frameDepthM / 2 - secondDoorStopDepthM / 2 : -frameDepthM / 2 + secondDoorStopDepthM / 2;
2312
+ const doorCenterZ = React.useMemo(() => {
2313
+ if (frameType === "WF_FLI") {
2314
+ return isOpeningIn ? frameDepthM / 2 - doorDepthM / 2 : -frameDepthM / 2 + doorDepthM / 2;
2315
+ }
2316
+ if (["WDGF_WDG100", "WF_100"].includes(frameType)) {
2317
+ return 0;
2318
+ }
2319
+ const gasketFaceZ = isOpeningIn ? gasketZPosition - gasketDepthM / 2 : gasketZPosition + gasketDepthM / 2;
2320
+ return isOpeningIn ? gasketFaceZ - doorDepthM / 2 : gasketFaceZ + doorDepthM / 2;
2321
+ }, [isOpeningIn, gasketZPosition, gasketDepthM, doorDepthM, frameType, frameDepthM]);
2322
+ const GlassPanelDepthM = glassDepth / 1000;
2323
+ const frontGlassOffsetZ = doorDepthM / 2 + GlassPanelDepthM / 2;
2324
+ const backGlassOffsetZ = -doorDepthM / 2 - GlassPanelDepthM / 2;
2325
+ const occulusWidthM = doorWidthM - occulusX1M - occulusX2M;
2326
+ const occulusHeightM = mainDoorHeightM - occulusY1M - occulusY2M;
2327
+ const occulusPositionXM = (occulusX1M - occulusX2M) / 2;
2328
+ const occulusPositionYM = (occulusY2M - occulusY1M) / 2;
2329
+ React.useEffect(() => {
2330
+ const materialsToUpdate = [frontDoorPlaneMaterial, backDoorPlaneMaterial];
2331
+ materialsToUpdate.forEach(material => {
2332
+ // Check if the material has a texture map
2333
+ if (material.map) {
2334
+ // Set the texture to repeat
2335
+ material.map.wrapS = THREE__namespace.RepeatWrapping;
2336
+ material.map.wrapT = THREE__namespace.RepeatWrapping;
2337
+ material.map.repeat.set(doorWidthM, mainDoorHeightM);
2338
+ material.map.needsUpdate = true;
2339
+ }
2340
+ });
2341
+ }, [doorWidthM, mainDoorHeightM, frontDoorPlaneMaterial, backDoorPlaneMaterial]);
2342
+ const [isOpen, setIsOpen] = React.useState(false);
2343
+ const hingeSideX = pivot === "left" ? -doorWidthM / 2 : doorWidthM / 2;
2344
+
2345
+ // --- PIVOT ---
2346
+ const hingeZ = React.useMemo(() => {
2347
+ if (frameType === "WF_FLI") {
2348
+ return isOpeningIn ? doorCenterZ - doorDepthM / 2 : doorCenterZ + doorDepthM / 2;
2349
+ }
2350
+ return isOpeningIn ? -frameDepthM / 2 : frameDepthM / 2;
2351
+ }, [frameType, isOpeningIn, doorCenterZ, doorDepthM, frameDepthM]);
2352
+ const doorOffsetZ = React.useMemo(() => doorCenterZ - hingeZ, [doorCenterZ, hingeZ]);
2353
+ const directionMultiplier = doorOpening === "in" ? 1 : -1;
2354
+ const {
2355
+ rotation
2356
+ } = three.useSpring({
2357
+ rotation: isOpen ? [0, (pivot === "left" ? Math.PI / 2 : -Math.PI / 2) * directionMultiplier, 0] : [0, 0, 0],
2358
+ config: {
2359
+ mass: 1,
2360
+ tension: 100,
2361
+ friction: 20
2362
+ }
2363
+ });
2364
+ const handleClick = () => setIsOpen(!isOpen);
2365
+ const doorYPosition = -totalOpeningHeightM / 2 + mainDoorHeightM / 2;
2366
+
2367
+ // Handle positioning logic
2368
+ const handleCenterYFromDoorBottom = 1.0;
2369
+ const safeHandleY = Math.min(Math.max(handleCenterYFromDoorBottom, 0.25), mainDoorHeightM - 0.25);
2370
+ const handleHeightM = -mainDoorHeightM / 2 + safeHandleY;
2371
+ const isSingleGlass = ["SG8", "SG10", "SG12"].includes(bodyType);
2372
+ const nonHingeSideX = pivot === "left" ? doorWidthM / 2 : -doorWidthM / 2;
2373
+ let handleX;
2374
+ if (isSingleGlass) {
2375
+ const plateHalfWidth = 0.05;
2376
+ handleX = nonHingeSideX + (pivot === "left" ? -plateHalfWidth : plateHalfWidth);
2377
+ } else {
2378
+ const latchMarginM = 0.08;
2379
+ handleX = nonHingeSideX + (pivot === "left" ? -latchMarginM : latchMarginM);
2380
+ }
2381
+
2382
+ // Hinge dimension calculations
2383
+ const hingeTotalHeightM = 120 / 1000;
2384
+ const hingeRadiusM = 8 / 1000;
2385
+ const capHeightM = 4 / 1000;
2386
+ const separatorHeightM = 4 / 1000;
2387
+ const barrelPartHeight = (hingeTotalHeightM - capHeightM * 2 - separatorHeightM) / 2;
2388
+ const isGlassDoor = React.useMemo(() => ["SG8", "SG10", "SG12"].includes(bodyType), [bodyType]);
2389
+
2390
+ // Hinge positioning logic
2391
+ const standardHingeYPositions = React.useMemo(() => {
2392
+ const numHinges = 4;
2393
+ const topMargin = 180 / 1000;
2394
+ const bottomMargin = 180 / 1000;
2395
+ const availableHeight = mainDoorHeightM - topMargin - bottomMargin;
2396
+ const spacing = availableHeight / (numHinges - 1);
2397
+ return Array.from({
2398
+ length: numHinges
2399
+ }, (_, i) => mainDoorHeightM / 2 - topMargin - i * spacing);
2400
+ }, [mainDoorHeightM]);
2401
+ const glassHingeYPositions = React.useMemo(() => {
2402
+ return [mainDoorHeightM / 2 - 250 / 1000, mainDoorHeightM / 2 - 450 / 1000, -mainDoorHeightM / 2 + 250 / 1000];
2403
+ }, [mainDoorHeightM]);
2404
+ const isStandardHingeVisible = React.useMemo(() => {
2405
+ if (isGlassDoor || !isFrameVisible) return false;
2406
+ return !["WDGF_WDG100", "WF_100", "WF_FLI"].includes(frameType);
2407
+ }, [isFrameVisible, frameType, isGlassDoor]);
2408
+ return /*#__PURE__*/jsxRuntime.jsxs("group", {
2409
+ "position-x": xOffset,
2410
+ children: [isStandardHingeVisible && standardHingeYPositions.map((y, index) => /*#__PURE__*/jsxRuntime.jsxs("group", {
2411
+ position: [hingeSideX, doorYPosition + y, hingeZ],
2412
+ children: [/*#__PURE__*/jsxRuntime.jsx("mesh", {
2413
+ material: hingeBodyMaterial,
2414
+ position: [0, separatorHeightM / 2 + barrelPartHeight / 2, 0],
2415
+ castShadow: CAST_SHADOW_MESHES,
2416
+ children: /*#__PURE__*/jsxRuntime.jsx("cylinderGeometry", {
2417
+ args: [hingeRadiusM, hingeRadiusM, barrelPartHeight, HINGE_SEGMENTS]
2418
+ })
2419
+ }), /*#__PURE__*/jsxRuntime.jsx("mesh", {
2420
+ material: hingeAccentMaterial,
2421
+ position: [0, separatorHeightM / 2 + barrelPartHeight + capHeightM / 2, 0],
2422
+ castShadow: CAST_SHADOW_MESHES,
2423
+ children: /*#__PURE__*/jsxRuntime.jsx("cylinderGeometry", {
2424
+ args: [hingeRadiusM, hingeRadiusM, capHeightM, HINGE_SEGMENTS]
2425
+ })
2426
+ })]
2427
+ }, "frame-hinge-".concat(index))), /*#__PURE__*/jsxRuntime.jsxs(three.a.group, {
2428
+ position: [hingeSideX, doorYPosition, hingeZ],
2429
+ rotation: rotation.to((x, y, z) => [x, y, z]),
2430
+ onClick: handleClick,
2431
+ onPointerOver: () => document.body.style.cursor = "pointer",
2432
+ onPointerOut: () => document.body.style.cursor = "auto",
2433
+ children: [isStandardHingeVisible && standardHingeYPositions.map((y, index) => /*#__PURE__*/jsxRuntime.jsxs("group", {
2434
+ position: [0, y, 0],
2435
+ children: [/*#__PURE__*/jsxRuntime.jsx("mesh", {
2436
+ material: hingeBodyMaterial,
2437
+ position: [0, -separatorHeightM / 2 - barrelPartHeight / 2, 0],
2438
+ castShadow: CAST_SHADOW_MESHES,
2439
+ children: /*#__PURE__*/jsxRuntime.jsx("cylinderGeometry", {
2440
+ args: [hingeRadiusM, hingeRadiusM, barrelPartHeight, HINGE_SEGMENTS]
2441
+ })
2442
+ }), /*#__PURE__*/jsxRuntime.jsx("mesh", {
2443
+ material: hingeAccentMaterial,
2444
+ position: [0, -separatorHeightM / 2 - barrelPartHeight - capHeightM / 2, 0],
2445
+ castShadow: CAST_SHADOW_MESHES,
2446
+ children: /*#__PURE__*/jsxRuntime.jsx("cylinderGeometry", {
2447
+ args: [hingeRadiusM, hingeRadiusM, capHeightM, HINGE_SEGMENTS]
2448
+ })
2449
+ }), /*#__PURE__*/jsxRuntime.jsx("mesh", {
2450
+ material: hingeAccentMaterial,
2451
+ position: [0, 0, 0],
2452
+ castShadow: CAST_SHADOW_MESHES,
2453
+ children: /*#__PURE__*/jsxRuntime.jsx("cylinderGeometry", {
2454
+ args: [hingeRadiusM, hingeRadiusM, separatorHeightM, HINGE_SEGMENTS]
2455
+ })
2456
+ })]
2457
+ }, "door-hinge-".concat(index))), isGlassDoor && isFrameVisible && glassHingeYPositions.map((y, index) => /*#__PURE__*/jsxRuntime.jsx(GlassHinge, {
2458
+ position: [0, y, doorOffsetZ + 0.0001],
2459
+ pivot: pivot,
2460
+ frameSideWidth: sidesFrameWidthM,
2461
+ doorDepthM: doorDepthM,
2462
+ material: hingeBodyMaterial,
2463
+ gasketMaterial: gasketMaterial
2464
+ }, "glass-hinge-".concat(index))), /*#__PURE__*/jsxRuntime.jsxs("group", {
2465
+ position: [-hingeSideX, 0, doorOffsetZ],
2466
+ children: [/*#__PURE__*/jsxRuntime.jsx("mesh", {
2467
+ castShadow: CAST_SHADOW_MESHES,
2468
+ receiveShadow: RECEIVE_SHADOW_MESHES,
2469
+ children: /*#__PURE__*/jsxRuntime.jsxs(csg.Geometry, {
2470
+ useGroups: true,
2471
+ children: [/*#__PURE__*/jsxRuntime.jsx(csg.Base, {
2472
+ name: "door-base",
2473
+ material: doorMaterial,
2474
+ children: /*#__PURE__*/jsxRuntime.jsx("boxGeometry", {
2475
+ args: [doorWidthM, mainDoorHeightM, doorDepthM]
2476
+ })
2477
+ }), /*#__PURE__*/jsxRuntime.jsx(DoorStopCuts, {
2478
+ visible: ["WDGF_WDG100", "WF_100", "WF_FLI"].includes(frameType),
2479
+ width: doorWidthM,
2480
+ height: totalOpeningHeightM,
2481
+ stopWidth: doorStopWidthM,
2482
+ stopDepth: doorStopDepthM,
2483
+ stopPositionZ: doorStopPositionZ,
2484
+ centerZ: doorCenterZ,
2485
+ material: doorMaterial,
2486
+ setName: "1"
2487
+ }), /*#__PURE__*/jsxRuntime.jsx(DoorStopCuts, {
2488
+ visible: ["WDGF_WDG100", "WF_100", "WF_FLI"].includes(frameType),
2489
+ width: doorWidthM,
2490
+ height: totalOpeningHeightM,
2491
+ stopWidth: secondDoorStopWidthM,
2492
+ stopDepth: secondDoorStopDepthM,
2493
+ stopPositionZ: secondDoorStopPositionZ,
2494
+ centerZ: doorCenterZ,
2495
+ material: doorMaterial,
2496
+ setName: "2"
2497
+ }), occulus.visible && occulusWidthM > 0 && occulusHeightM > 0 && /*#__PURE__*/jsxRuntime.jsx(csg.Subtraction, {
2498
+ position: [occulusPositionXM, occulusPositionYM, 0],
2499
+ children: /*#__PURE__*/jsxRuntime.jsx("boxGeometry", {
2500
+ args: [occulusWidthM, occulusHeightM, doorDepthM + 0.01]
2501
+ })
2502
+ })]
2503
+ })
2504
+ }), glassVisible && /*#__PURE__*/jsxRuntime.jsxs(jsxRuntime.Fragment, {
2505
+ children: [/*#__PURE__*/jsxRuntime.jsx("mesh", {
2506
+ position: [0, 0, frontGlassOffsetZ],
2507
+ castShadow: CAST_SHADOW_MESHES,
2508
+ material: glassInfillMaterial,
2509
+ children: /*#__PURE__*/jsxRuntime.jsx("boxGeometry", {
2510
+ args: [doorWidthM, mainDoorHeightM, GlassPanelDepthM]
2511
+ })
2512
+ }), !["WDGF_WDG100", "WF_100"].includes(frameType) && /*#__PURE__*/jsxRuntime.jsx("mesh", {
2513
+ position: [0, 0, backGlassOffsetZ],
2514
+ castShadow: CAST_SHADOW_MESHES,
2515
+ material: glassInfillMaterial,
2516
+ children: /*#__PURE__*/jsxRuntime.jsx("boxGeometry", {
2517
+ args: [doorWidthM, mainDoorHeightM, GlassPanelDepthM]
2518
+ })
2519
+ }), ["WDGF_WDG100", "WF_100"].includes(frameType) && /*#__PURE__*/jsxRuntime.jsx("mesh", {
2520
+ position: [0, -secondDoorStopWidthM / 2, backGlassOffsetZ],
2521
+ castShadow: CAST_SHADOW_MESHES,
2522
+ material: glassInfillMaterial,
2523
+ children: /*#__PURE__*/jsxRuntime.jsx("boxGeometry", {
2524
+ args: [doorWidthM - 2 * secondDoorStopWidthM, mainDoorHeightM - secondDoorStopWidthM, GlassPanelDepthM]
2525
+ })
2526
+ })]
2527
+ }), frontDoorPlane.visible && /*#__PURE__*/jsxRuntime.jsx("mesh", {
2528
+ position: [0, 0, doorDepthM / 2 + 0.0005],
2529
+ castShadow: CAST_SHADOW_MESHES,
2530
+ children: /*#__PURE__*/jsxRuntime.jsxs(csg.Geometry, {
2531
+ useGroups: true,
2532
+ children: [/*#__PURE__*/jsxRuntime.jsx(csg.Base, {
2533
+ material: frontDoorPlaneMaterial,
2534
+ children: /*#__PURE__*/jsxRuntime.jsx("boxGeometry", {
2535
+ args: [doorWidthM, mainDoorHeightM, 0.001]
2536
+ })
2537
+ }), occulus.visible && occulusWidthM > 0 && occulusHeightM > 0 && /*#__PURE__*/jsxRuntime.jsx(csg.Subtraction, {
2538
+ position: [occulusPositionXM, occulusPositionYM, 0],
2539
+ children: /*#__PURE__*/jsxRuntime.jsx("boxGeometry", {
2540
+ args: [occulusWidthM, occulusHeightM, 0.002]
2541
+ })
2542
+ })]
2543
+ })
2544
+ }), backDoorPlane.visible && /*#__PURE__*/jsxRuntime.jsx("mesh", {
2545
+ position: [0, 0, -doorDepthM / 2 - 0.0005],
2546
+ castShadow: CAST_SHADOW_MESHES,
2547
+ children: /*#__PURE__*/jsxRuntime.jsxs(csg.Geometry, {
2548
+ useGroups: true,
2549
+ children: [/*#__PURE__*/jsxRuntime.jsx(csg.Base, {
2550
+ material: backDoorPlaneMaterial,
2551
+ children: /*#__PURE__*/jsxRuntime.jsx("boxGeometry", {
2552
+ args: [doorWidthM, mainDoorHeightM, 0.0001]
2553
+ })
2554
+ }), occulus.visible && occulusWidthM > 0 && occulusHeightM > 0 && /*#__PURE__*/jsxRuntime.jsx(csg.Subtraction, {
2555
+ position: [occulusPositionXM, occulusPositionYM, 0],
2556
+ children: /*#__PURE__*/jsxRuntime.jsx("boxGeometry", {
2557
+ args: [occulusWidthM, occulusHeightM, 0.002]
2558
+ })
2559
+ })]
2560
+ })
2561
+ }), occulus.visible && occulus.infillVisible && occulusWidthM > 0 && occulusHeightM > 0 && /*#__PURE__*/jsxRuntime.jsx("mesh", {
2562
+ position: [occulusPositionXM, occulusPositionYM, 0],
2563
+ castShadow: CAST_SHADOW_MESHES,
2564
+ material: occulusInfillMaterial,
2565
+ children: /*#__PURE__*/jsxRuntime.jsx("boxGeometry", {
2566
+ args: [occulusWidthM, occulusHeightM, occulusInfillDepthM]
2567
+ })
2568
+ }), /*#__PURE__*/jsxRuntime.jsx(DoorHandle, {
2569
+ bodyType: bodyType,
2570
+ handleX: handleX,
2571
+ handleHeightM: handleHeightM,
2572
+ doorDepthM: doorDepthM,
2573
+ doorPivot: pivot,
2574
+ handleColor: handleColor
2575
+ })]
2576
+ })]
2577
+ })]
2578
+ });
2579
+ }
2580
+
2581
+ function DoorModels(props) {
2582
+ const {
2583
+ doorName,
2584
+ materials: materialsProp,
2585
+ doorPivot: doorPivotProp,
2586
+ doorOpening: doorOpeningProp,
2587
+ totalWidth: totalWidthProp,
2588
+ totalHeight: totalHeightProp,
2589
+ totalDepth: totalDepthProp
2590
+ } = props;
2591
+ const {
2592
+ is2D,
2593
+ totalWidth,
2594
+ door,
2595
+ setDoor,
2596
+ doorFrame,
2597
+ handleParseCpid,
2598
+ isDoubleDoor,
2599
+ setMaterials,
2600
+ setTotalWidth,
2601
+ setTotalHeight,
2602
+ setTotalDepth,
2603
+ setExportDXF,
2604
+ setCollectElementsFromGroup
2605
+ } = useConfigurator();
2606
+
2607
+ /**
2608
+ * Root Three.js group used for DXF export.
2609
+ * Every Mesh under this group will be visited by `useDxfExportPayload`.
2610
+ */
2611
+ const exportGroupRef = React.useRef(null);
2612
+
2613
+ /**
2614
+ * Optional mapper to attach IFC meta to each mesh.
2615
+ * This will be called by the DXF export hook for every mesh it finds.
2616
+ */
2617
+ const getPropsForMesh = React.useCallback(mesh => {
2618
+ const userData = mesh.userData || {};
2619
+ return {
2620
+ // You can adapt this logic to your own IFC type mapping
2621
+ ifcType: userData.ifcType || (userData.type === "door" ? "IfcDoor" : userData.type === "window" ? "IfcWindow" : "IfcBuildingElementProxy"),
2622
+ props: _objectSpread2({
2623
+ name: mesh.name
2624
+ }, userData)
2625
+ };
2626
+ }, []);
2627
+ const {
2628
+ collectElementsFromGroup,
2629
+ exportDxfFromServer
2630
+ } = useDxfExportPayload({
2631
+ rootGroupRef: exportGroupRef,
2632
+ getPropsForMesh,
2633
+ projectNumber: "1987348",
2634
+ version: "V1.0"
2635
+ });
2636
+
2637
+ /**
2638
+ * Convenience handler to trigger DXF export.
2639
+ * This calls the backend and downloads the DXF returned.
2640
+ */
2641
+ const exportDXF = React.useCallback(() => {
2642
+ exportDxfFromServer();
2643
+ }, [exportDxfFromServer]);
2644
+
2645
+ // Register export functions in context
2646
+ React.useEffect(() => {
2647
+ setExportDXF(() => exportDXF);
2648
+ setCollectElementsFromGroup(() => collectElementsFromGroup);
2649
+
2650
+ // Cleanup
2651
+ return () => {
2652
+ setExportDXF(null);
2653
+ setCollectElementsFromGroup(null);
2654
+ };
2655
+ }, [exportDXF, collectElementsFromGroup, setExportDXF, setCollectElementsFromGroup]);
2656
+ React.useEffect(() => {
2657
+ if (doorName) {
2658
+ handleParseCpid(doorName);
2659
+ }
2660
+ }, [doorName, handleParseCpid]);
2661
+ React.useEffect(() => {
2662
+ if (materialsProp) {
2663
+ setMaterials(materialsProp);
2664
+ }
2665
+ }, [materialsProp, setMaterials]);
2666
+ React.useEffect(() => {
2667
+ if (doorPivotProp || doorOpeningProp) {
2668
+ setDoor(prevDoor => _objectSpread2(_objectSpread2(_objectSpread2({}, prevDoor), doorPivotProp && {
2669
+ doorPivot: doorPivotProp
2670
+ }), doorOpeningProp && {
2671
+ doorOpening: doorOpeningProp
2672
+ }));
2673
+ }
2674
+ }, [doorPivotProp, doorOpeningProp, setDoor]);
2675
+ React.useEffect(() => {
2676
+ if (totalWidthProp !== undefined) {
2677
+ setTotalWidth(totalWidthProp);
2678
+ }
2679
+ }, [totalWidthProp, setTotalWidth]);
2680
+ React.useEffect(() => {
2681
+ if (totalHeightProp !== undefined) {
2682
+ setTotalHeight(totalHeightProp);
2683
+ }
2684
+ }, [totalHeightProp, setTotalHeight]);
2685
+ React.useEffect(() => {
2686
+ if (totalDepthProp !== undefined) {
2687
+ setTotalDepth(totalDepthProp);
2688
+ }
2689
+ }, [totalDepthProp, setTotalDepth]);
2690
+
2691
+ // --- Material Logic (Centralized in parent component) ---
2692
+ const allMaterials = useDoorMaterials(materialsProp);
2693
+
2694
+ // --- Dimension calculations ---
2695
+ const {
2696
+ doorWidth,
2697
+ doorHeight
2698
+ } = door;
2699
+ const doorPivot = doorPivotProp || door.doorPivot;
2700
+ const {
2701
+ topThk,
2702
+ frameDepth
2703
+ } = doorFrame;
2704
+ const initialDoorHeight = React.useRef(null);
2705
+ React.useEffect(() => {
2706
+ if (initialDoorHeight.current === null && doorHeight > 0) {
2707
+ initialDoorHeight.current = doorHeight;
2708
+ }
2709
+ }, [doorHeight]);
2710
+ const pivotNewPosition = React.useMemo(() => {
2711
+ if (initialDoorHeight.current === null || initialDoorHeight.current === 0) return 0;
2712
+ return (doorHeight - initialDoorHeight.current) / 2 / 1000;
2713
+ }, [doorHeight]);
2714
+ const topThkValue = parseInt(topThk) || 20;
2715
+
2716
+ // Convert dimensions from mm to meters
2717
+ const totalWidthM = totalWidthProp !== undefined ? totalWidthProp / 1000 : totalWidth / 1000;
2718
+ const totalHeightM = totalHeightProp !== undefined ? totalHeightProp / 1000 : doorHeight / 1000;
2719
+ const doorWidthM = doorWidth / 1000;
2720
+ const frameDepthM = frameDepth / 1000;
2721
+ const topFrameWidthM = topThkValue / 1000;
2722
+ const doorInstances = React.useMemo(() => {
2723
+ if (isDoubleDoor) {
2724
+ return [{
2725
+ key: "left",
2726
+ pivot: "left",
2727
+ x: -doorWidthM / 2
2728
+ }, {
2729
+ key: "right",
2730
+ pivot: "right",
2731
+ x: doorWidthM / 2
2732
+ }];
2733
+ }
2734
+ return [{
2735
+ key: "single",
2736
+ pivot: doorPivot,
2737
+ x: 0
2738
+ }];
2739
+ }, [isDoubleDoor, doorWidthM, doorPivot]);
2740
+
2741
+ // --- Centering & Positioning ---
2742
+ const yCenteringOffset = -topFrameWidthM / 2;
2743
+ if (is2D) {
2744
+ return /*#__PURE__*/jsxRuntime.jsx(jsxRuntime.Fragment, {
2745
+ children: /*#__PURE__*/jsxRuntime.jsx("group", {
2746
+ ref: exportGroupRef,
2747
+ "position-y": yCenteringOffset + 0.1,
2748
+ children: /*#__PURE__*/jsxRuntime.jsxs("mesh", {
2749
+ children: [/*#__PURE__*/jsxRuntime.jsx("boxGeometry", {
2750
+ args: [totalWidth / 1000, 0.1, frameDepth / 1000]
2751
+ }), /*#__PURE__*/jsxRuntime.jsx("meshBasicMaterial", {
2752
+ color: "white",
2753
+ side: THREE__namespace.DoubleSide
2754
+ })]
2755
+ })
2756
+ })
2757
+ });
2758
+ }
2759
+
2760
+ // Extract handleColor from materials prop
2761
+ const handleColor = materialsProp === null || materialsProp === void 0 ? void 0 : materialsProp.handleColor;
2762
+ return /*#__PURE__*/jsxRuntime.jsx(jsxRuntime.Fragment, {
2763
+ children: /*#__PURE__*/jsxRuntime.jsxs("group", {
2764
+ ref: exportGroupRef,
2765
+ children: [/*#__PURE__*/jsxRuntime.jsx("mesh", {
2766
+ visible: false,
2767
+ children: /*#__PURE__*/jsxRuntime.jsx("boxGeometry", {
2768
+ args: [totalWidthM, totalHeightM, frameDepthM]
2769
+ })
2770
+ }), /*#__PURE__*/jsxRuntime.jsxs("group", {
2771
+ "position-y": yCenteringOffset + pivotNewPosition,
2772
+ children: [/*#__PURE__*/jsxRuntime.jsx(DoorFrame, {
2773
+ materials: allMaterials,
2774
+ doorModelsProps: props
2775
+ }), doorInstances.map(instance => /*#__PURE__*/jsxRuntime.jsx(DoorLeaf, {
2776
+ pivot: instance.pivot,
2777
+ xOffset: instance.x,
2778
+ materials: allMaterials,
2779
+ handleColor: handleColor
2780
+ }, instance.key))]
2781
+ })]
2782
+ })
2783
+ });
2784
+ }
2785
+
2786
+ const DoorConfigurator = _ref => {
2787
+ let {
2788
+ doorName,
2789
+ materials,
2790
+ doorPivot,
2791
+ doorOpening,
2792
+ is2D = false,
2793
+ totalWidth,
2794
+ totalHeight,
2795
+ totalDepth
2796
+ } = _ref;
2797
+ return /*#__PURE__*/jsxRuntime.jsx(ConfiguratorProvider, {
2798
+ initialIs2D: is2D,
2799
+ children: /*#__PURE__*/jsxRuntime.jsx(DoorModels, {
2800
+ doorName: doorName,
2801
+ materials: materials,
2802
+ doorPivot: doorPivot,
2803
+ doorOpening: doorOpening,
2804
+ totalWidth: totalWidth,
2805
+ totalHeight: totalHeight,
2806
+ totalDepth: totalDepth
2807
+ })
2808
+ });
2809
+ };
2810
+
2811
+ exports["default"] = DoorConfigurator;
2812
+ exports.useConfigurator = useConfigurator;
2813
+ //# sourceMappingURL=index.cjs.js.map