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.
- package/dist/index.cjs.js +2813 -0
- package/dist/index.cjs.js.map +1 -0
- package/dist/index.esm.js +2785 -0
- package/dist/index.esm.js.map +1 -0
- package/package.json +1 -1
|
@@ -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
|