@2112-lab/central-plant 0.1.39 → 0.1.41

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/dist/bundle/index.js +7991 -7054
  2. package/dist/cjs/src/core/centralPlant.js +48 -3
  3. package/dist/cjs/src/core/centralPlantInternals.js +75 -566
  4. package/dist/cjs/src/core/sceneViewer.js +38 -13
  5. package/dist/cjs/src/index.js +6 -4
  6. package/dist/cjs/src/managers/components/pathfindingManager.js +75 -60
  7. package/dist/cjs/src/managers/components/transformOperationsManager.js +929 -0
  8. package/dist/cjs/src/managers/controls/keyboardControlsManager.js +57 -1
  9. package/dist/cjs/src/managers/controls/transformControls.js +11 -3
  10. package/dist/cjs/src/managers/controls/transformControlsManager.js +563 -263
  11. package/dist/cjs/src/managers/pathfinding/ConnectorManager.js +385 -0
  12. package/dist/cjs/src/managers/pathfinding/PathIntersectionDetector.js +387 -0
  13. package/dist/cjs/src/managers/pathfinding/PathRenderingManager.js +401 -0
  14. package/dist/cjs/src/managers/pathfinding/pathfindingManager.js +378 -0
  15. package/dist/cjs/src/managers/pathfinding/sceneDataManager.js +256 -0
  16. package/dist/cjs/src/managers/scene/animationManager.js +145 -0
  17. package/dist/cjs/src/managers/scene/sceneExportManager.js +14 -13
  18. package/dist/cjs/src/managers/scene/sceneOperationsManager.js +516 -21
  19. package/dist/cjs/src/managers/scene/sceneTooltipsManager.js +1 -8
  20. package/dist/cjs/src/managers/system/operationHistoryManager.js +414 -0
  21. package/dist/cjs/src/managers/system/settingsManager.js +2 -1
  22. package/dist/cjs/src/utils/objectTypes.js +5 -7
  23. package/dist/esm/src/core/centralPlant.js +48 -3
  24. package/dist/esm/src/core/centralPlantInternals.js +76 -567
  25. package/dist/esm/src/core/sceneViewer.js +38 -13
  26. package/dist/esm/src/index.js +4 -3
  27. package/dist/esm/src/managers/components/pathfindingManager.js +75 -60
  28. package/dist/esm/src/managers/components/transformOperationsManager.js +904 -0
  29. package/dist/esm/src/managers/controls/keyboardControlsManager.js +57 -1
  30. package/dist/esm/src/managers/controls/transformControls.js +11 -3
  31. package/dist/esm/src/managers/controls/transformControlsManager.js +564 -264
  32. package/dist/esm/src/managers/pathfinding/ConnectorManager.js +361 -0
  33. package/dist/esm/src/managers/pathfinding/PathIntersectionDetector.js +363 -0
  34. package/dist/esm/src/managers/pathfinding/PathRenderingManager.js +377 -0
  35. package/dist/esm/src/managers/pathfinding/pathfindingManager.js +374 -0
  36. package/dist/esm/src/managers/pathfinding/sceneDataManager.js +232 -0
  37. package/dist/esm/src/managers/scene/animationManager.js +141 -0
  38. package/dist/esm/src/managers/scene/sceneExportManager.js +14 -13
  39. package/dist/esm/src/managers/scene/sceneOperationsManager.js +516 -21
  40. package/dist/esm/src/managers/scene/sceneTooltipsManager.js +1 -8
  41. package/dist/esm/src/managers/system/operationHistoryManager.js +409 -0
  42. package/dist/esm/src/managers/system/settingsManager.js +2 -1
  43. package/dist/esm/src/utils/objectTypes.js +5 -7
  44. package/dist/index.d.ts +2 -2
  45. package/package.json +1 -1
@@ -0,0 +1,904 @@
1
+ import { createClass as _createClass, createForOfIteratorHelper as _createForOfIteratorHelper, classCallCheck as _classCallCheck } from '../../../_virtual/_rollupPluginBabelHelpers.js';
2
+ import * as THREE from 'three';
3
+
4
+ var TransformOperationsManager = /*#__PURE__*/function () {
5
+ /**
6
+ * @param {Object} sceneViewer - The scene viewer instance
7
+ */
8
+ function TransformOperationsManager(sceneViewer) {
9
+ _classCallCheck(this, TransformOperationsManager);
10
+ this.sceneViewer = sceneViewer;
11
+ }
12
+
13
+ /**
14
+ * Translate a component by componentId
15
+ * @param {string} componentId - The UUID of the component to translate
16
+ * @param {string} axis - The axis to translate on ('x', 'y', or 'z')
17
+ * @param {number} value - The value to translate by
18
+ * @returns {boolean} True if translation was successful, false otherwise
19
+ */
20
+ return _createClass(TransformOperationsManager, [{
21
+ key: "translateComponent",
22
+ value: function translateComponent(componentId, axis, value) {
23
+ var _this$sceneViewer$man, _this$sceneViewer;
24
+ // Store transform parameters using the OperationHistoryManager
25
+ if ((_this$sceneViewer$man = this.sceneViewer.managers) !== null && _this$sceneViewer$man !== void 0 && _this$sceneViewer$man.operationHistoryManager) {
26
+ this.sceneViewer.managers.operationHistoryManager.addToOperationHistory('translateComponent', {
27
+ componentId: componentId,
28
+ axis: axis,
29
+ value: value
30
+ });
31
+ }
32
+
33
+ // Validate parameters
34
+ if (!componentId || !axis || value === undefined || value === null) {
35
+ console.error('❌ translateComponent(): Invalid parameters');
36
+ return false;
37
+ }
38
+ if (!['x', 'y', 'z'].includes(axis)) {
39
+ console.error("\u274C translateComponent(): Invalid axis '".concat(axis, "'. Must be 'x', 'y', or 'z'"));
40
+ return false;
41
+ }
42
+ if (!this.sceneViewer || !this.sceneViewer.scene) {
43
+ console.error('❌ translateComponent(): Scene viewer or scene not available');
44
+ return false;
45
+ }
46
+
47
+ // Find the component in the scene
48
+ var component = null;
49
+ this.sceneViewer.scene.traverse(function (child) {
50
+ var _child$userData;
51
+ if (child.uuid === componentId || ((_child$userData = child.userData) === null || _child$userData === void 0 ? void 0 : _child$userData.originalUuid) === componentId) {
52
+ component = child;
53
+ }
54
+ });
55
+ if (!component) {
56
+ console.error("\u274C translateComponent(): Component with ID '".concat(componentId, "' not found in scene"));
57
+ return false;
58
+ }
59
+
60
+ // Validate that it's actually a component
61
+ if (!component.userData || !component.userData.objectType) {
62
+ console.error("\u274C translateComponent(): Object with ID '".concat(componentId, "' is not a valid component"));
63
+ return false;
64
+ }
65
+
66
+ // Apply the translation
67
+ console.log("\uD83D\uDD04 translateComponent(): Translating component ".concat(componentId, " on ").concat(axis, " axis by ").concat(value));
68
+
69
+ // Update the Three.js object position
70
+ component.position[axis] += value;
71
+
72
+ // Update matrices
73
+ component.updateMatrix();
74
+ component.updateMatrixWorld(true);
75
+
76
+ // Check if component is underground and fix if needed (based on settings)
77
+ var checkUnderground = (_this$sceneViewer = this.sceneViewer) === null || _this$sceneViewer === void 0 || (_this$sceneViewer = _this$sceneViewer.managers) === null || _this$sceneViewer === void 0 || (_this$sceneViewer = _this$sceneViewer.settingsManager) === null || _this$sceneViewer === void 0 ? void 0 : _this$sceneViewer.getSetting('scene', 'checkUnderground');
78
+ if (checkUnderground) {
79
+ this.fixUndergroundComponent(component);
80
+ }
81
+
82
+ // Emit transform event if available
83
+ if (this.sceneViewer.emit && typeof this.sceneViewer.emit === 'function') {
84
+ this.sceneViewer.emit('objectTransformed', {
85
+ object: component,
86
+ transformType: 'translate',
87
+ axis: axis,
88
+ value: value
89
+ });
90
+ }
91
+ console.log("\u2705 translateComponent(): Component ".concat(componentId, " translated successfully"));
92
+ return true;
93
+ }
94
+
95
+ /**
96
+ * Translate a pipe segment by segmentId
97
+ * @param {string} segmentId - The UUID of the pipe segment to translate
98
+ * @param {string} axis - The axis to translate on ('x', 'y', or 'z')
99
+ * @param {number} value - The value to translate by
100
+ * @returns {boolean} True if translation was successful, false otherwise
101
+ */
102
+ }, {
103
+ key: "translateSegment",
104
+ value: function translateSegment(segmentId, axis, value) {
105
+ var _this$sceneViewer$man2, _segment$userData, _segment$userData2, _this$sceneViewer3, _this$sceneViewer$man4, _this$sceneViewer$man5;
106
+ // Store transform parameters using the OperationHistoryManager
107
+ if ((_this$sceneViewer$man2 = this.sceneViewer.managers) !== null && _this$sceneViewer$man2 !== void 0 && _this$sceneViewer$man2.operationHistoryManager) {
108
+ this.sceneViewer.managers.operationHistoryManager.addToOperationHistory('translateSegment', {
109
+ segmentId: segmentId,
110
+ axis: axis,
111
+ value: value
112
+ });
113
+ }
114
+
115
+ // Validate parameters
116
+ if (!segmentId || !axis || value === undefined || value === null) {
117
+ console.error('❌ translateSegment(): Invalid parameters');
118
+ return false;
119
+ }
120
+ if (!['x', 'y', 'z'].includes(axis)) {
121
+ console.error("\u274C translateSegment(): Invalid axis '".concat(axis, "'. Must be 'x', 'y', or 'z'"));
122
+ return false;
123
+ }
124
+ if (!this.sceneViewer || !this.sceneViewer.scene) {
125
+ console.error('❌ translateSegment(): Scene viewer or scene not available');
126
+ return false;
127
+ }
128
+
129
+ // Find the segment in the scene
130
+ var segment = null;
131
+ this.sceneViewer.scene.traverse(function (child) {
132
+ var _child$userData2;
133
+ if (child.uuid === segmentId || ((_child$userData2 = child.userData) === null || _child$userData2 === void 0 ? void 0 : _child$userData2.originalUuid) === segmentId) {
134
+ segment = child;
135
+ }
136
+ });
137
+ if (!segment) {
138
+ console.error("\u274C translateSegment(): Segment with ID '".concat(segmentId, "' not found in scene"));
139
+ return false;
140
+ }
141
+
142
+ // Validate that it's actually a pipe segment
143
+ if (!((_segment$userData = segment.userData) !== null && _segment$userData !== void 0 && _segment$userData.objectType) === 'segment') {
144
+ console.error("\u274C translateSegment(): Object with ID '".concat(segmentId, "' is not a valid pipe segment"));
145
+ return false;
146
+ }
147
+
148
+ // Check if segment is transformable
149
+ if (((_segment$userData2 = segment.userData) === null || _segment$userData2 === void 0 ? void 0 : _segment$userData2.transformable) === false) {
150
+ console.warn("\u26A0\uFE0F translateSegment(): Segment '".concat(segmentId, "' is marked as non-transformable (stub segment)"));
151
+ return false;
152
+ }
153
+ console.log('[translateSegment] segment:', segment);
154
+
155
+ // Calculate segment endpoints to check for nearby connectors
156
+ var segmentEndpoints = this.calculateSegmentEndpoints(segment);
157
+
158
+ // Check if there are any connectors (component or manual) at segment endpoints
159
+ var connectorsAtEndpoints = this.findConnectorsAtPositions([segmentEndpoints.start, segmentEndpoints.end], 0.15 // tolerance for position matching
160
+ );
161
+
162
+ // Filter out segment connectors - we only care about component connectors
163
+ var componentConnectorsAtEndpoints = connectorsAtEndpoints.filter(function (connector) {
164
+ var _connector$userData;
165
+ return ((_connector$userData = connector.userData) === null || _connector$userData === void 0 ? void 0 : _connector$userData.objectType) !== 'segment-connector';
166
+ });
167
+ if (componentConnectorsAtEndpoints.length > 0) {
168
+ var _this$sceneViewer$man3, _this$sceneViewer2;
169
+ console.warn("\u26A0\uFE0F translateSegment(): Segment '".concat(segmentId, "' has component connectors at endpoints. Splitting segment..."));
170
+ console.log(" Found ".concat(componentConnectorsAtEndpoints.length, " component connector(s):"), componentConnectorsAtEndpoints.map(function (c) {
171
+ var _c$parent;
172
+ return {
173
+ uuid: c.uuid,
174
+ parent: (_c$parent = c.parent) === null || _c$parent === void 0 ? void 0 : _c$parent.uuid
175
+ };
176
+ }));
177
+
178
+ // Split the segment using SceneOperationsManager (will auto-detect component connectors)
179
+ var _sceneOperationsManager = (_this$sceneViewer$man3 = this.sceneViewer.managers) === null || _this$sceneViewer$man3 === void 0 ? void 0 : _this$sceneViewer$man3.sceneOperationsManager;
180
+ if (!_sceneOperationsManager) {
181
+ console.error('❌ translateSegment(): SceneOperationsManager not available for splitting');
182
+ return false;
183
+ }
184
+
185
+ // Check if segment splitting is enabled in settings
186
+ var splitSegmentsEnabled = (_this$sceneViewer2 = this.sceneViewer) === null || _this$sceneViewer2 === void 0 || (_this$sceneViewer2 = _this$sceneViewer2.managers) === null || _this$sceneViewer2 === void 0 || (_this$sceneViewer2 = _this$sceneViewer2.settingsManager) === null || _this$sceneViewer2 === void 0 ? void 0 : _this$sceneViewer2.getSetting('scene', 'splitSegmentsWithComponentConnectors');
187
+ if (splitSegmentsEnabled === false) {
188
+ return false;
189
+ }
190
+ try {
191
+ var splitResult = _sceneOperationsManager.splitSegment(segment);
192
+ if (splitResult && splitResult.segment1 && splitResult.segment2) {
193
+ console.log("\u2705 Segment split: ".concat(splitResult.segment1.uuid, " (").concat(splitResult.segment1.geometry.parameters.height, "), ").concat(splitResult.segment2.uuid, " (").concat(splitResult.segment2.geometry.parameters.height, ")"));
194
+
195
+ // Determine which segment is the longer one (free side) to translate
196
+ var segment1Length = splitResult.segment1.geometry.parameters.height;
197
+ var segment2Length = splitResult.segment2.geometry.parameters.height;
198
+
199
+ // The longer segment is the one WITHOUT the component connector
200
+ var segmentToTranslate = segment1Length > segment2Length ? splitResult.segment1 : splitResult.segment2;
201
+ console.log("\uD83C\uDFAF Translating ".concat(segmentToTranslate.uuid, " (length: ").concat(segmentToTranslate.geometry.parameters.height, ") - the free segment"));
202
+
203
+ // Now translate the chosen segment (recursive call with the new segment)
204
+ var translateSuccess = this.translateSegment(segmentToTranslate.uuid, axis, value);
205
+ if (!translateSuccess) {
206
+ console.error('❌ translateSegment(): Failed to translate split segment half');
207
+ return false;
208
+ }
209
+ console.log('✅ Split and translate operation completed successfully');
210
+ return true;
211
+ } else {
212
+ console.error('❌ translateSegment(): Split operation failed');
213
+ return false;
214
+ }
215
+ } catch (error) {
216
+ console.error('❌ translateSegment(): Error splitting segment:', error);
217
+ return false;
218
+ }
219
+ }
220
+ console.log("\uD83D\uDD04 translateSegment(): Translating segment ".concat(segmentId, " on ").concat(axis, " axis by ").concat(value));
221
+
222
+ // Check if translationalOverrideForAutomaticSegments is enabled
223
+ var translationalOverride = (_this$sceneViewer3 = this.sceneViewer) === null || _this$sceneViewer3 === void 0 || (_this$sceneViewer3 = _this$sceneViewer3.managers) === null || _this$sceneViewer3 === void 0 || (_this$sceneViewer3 = _this$sceneViewer3.settingsManager) === null || _this$sceneViewer3 === void 0 ? void 0 : _this$sceneViewer3.getSetting('scene', 'translationalOverrideForAutomaticSegments');
224
+
225
+ // If override is disabled (false), check for intersections with computed segments
226
+ if (translationalOverride === false) {
227
+ console.log('🔍 Checking for intersections with computed segments (translationalOverrideForAutomaticSegments is disabled)');
228
+
229
+ // Temporarily apply the translation to check for intersections
230
+ var originalPosition = segment.position[axis];
231
+ segment.position[axis] += value;
232
+ segment.updateMatrix();
233
+ segment.updateMatrixWorld(true);
234
+
235
+ // Check if the translated segment intersects with any computed segments
236
+ var hasIntersection = this.checkSegmentIntersection(segment);
237
+ console.log("\uD83D\uDD04 translateSegment() hasIntersection:", hasIntersection);
238
+ if (hasIntersection) {
239
+ // Revert the translation
240
+ segment.position[axis] = originalPosition;
241
+ segment.updateMatrix();
242
+ segment.updateMatrixWorld(true);
243
+ console.warn("\u26A0\uFE0F translateSegment(): Translation canceled - segment would intersect with computed segment(s)");
244
+ return false;
245
+ }
246
+ console.log('✅ No intersections detected, proceeding with translation');
247
+ } else {
248
+ // Apply the translation to the Three.js object
249
+ segment.position[axis] += value;
250
+
251
+ // Update matrices
252
+ segment.updateMatrix();
253
+ segment.updateMatrixWorld(true);
254
+ }
255
+
256
+ // Validate PathfindingManager and SceneOperationsManager availability
257
+ var pathfindingManager = (_this$sceneViewer$man4 = this.sceneViewer.managers) === null || _this$sceneViewer$man4 === void 0 ? void 0 : _this$sceneViewer$man4.pathfindingManager;
258
+ var sceneOperationsManager = (_this$sceneViewer$man5 = this.sceneViewer.managers) === null || _this$sceneViewer$man5 === void 0 ? void 0 : _this$sceneViewer$man5.sceneOperationsManager;
259
+ if (!pathfindingManager || !sceneOperationsManager) {
260
+ console.error('❌ translateSegment(): PathfindingManager or SceneOperationsManager not available');
261
+ return false;
262
+ }
263
+
264
+ // Check if segment is already declared - if not, manualize it first
265
+ if (segment.userData.isDeclared !== true) {
266
+ console.log('🔌 Handling manual segment transformation via SceneOperationsManager (first move)');
267
+ try {
268
+ sceneOperationsManager.manualizeSegment(segment, this.sceneViewer.currentSceneData);
269
+ } catch (error) {
270
+ console.error('❌ translateSegment(): Error in manualizeSegment:', error);
271
+ return false;
272
+ }
273
+ } else {
274
+ console.log('🔄 Segment already declared, updating connector positions (subsequent move)');
275
+ }
276
+
277
+ // ALWAYS update connector positions after segment movement (first or subsequent moves)
278
+ this.updateSegmentConnectorPositions(segment);
279
+
280
+ // Regenerate paths to create new pipe segments that connect to the moved segment
281
+ console.log('🔄 Regenerating paths to create connecting pipe segments...');
282
+ try {
283
+ if (this.sceneViewer && typeof this.sceneViewer.updatePaths === 'function') {
284
+ this.sceneViewer.updatePaths();
285
+ console.log('✅ Paths regenerated successfully - new connecting segments created');
286
+ } else {
287
+ console.warn('⚠️ updatePaths method not available on sceneViewer');
288
+ }
289
+ } catch (error) {
290
+ console.error('❌ Error regenerating paths:', error);
291
+ }
292
+
293
+ // Emit transform event if available
294
+ if (this.sceneViewer.emit && typeof this.sceneViewer.emit === 'function') {
295
+ this.sceneViewer.emit('objectTransformed', {
296
+ object: segment,
297
+ transformType: 'translateSegment',
298
+ axis: axis,
299
+ value: value
300
+ });
301
+ }
302
+ console.log("\u2705 translateSegment(): Segment ".concat(segmentId, " translated and connections restructured successfully"));
303
+ return true;
304
+ }
305
+
306
+ /**
307
+ * Translate a gateway by gatewayId
308
+ * @param {string} gatewayId - The UUID of the gateway to translate
309
+ * @param {string} axis - The axis to translate on ('x', 'y', or 'z')
310
+ * @param {number} value - The value to translate by
311
+ * @returns {boolean} True if translation was successful, false otherwise
312
+ */
313
+ }, {
314
+ key: "translateGateway",
315
+ value: function translateGateway(gatewayId, axis, value) {
316
+ var _this$sceneViewer$man6, _this$sceneViewer$man7;
317
+ console.log("[Pathfinder] translateGateway started");
318
+
319
+ // Store transform parameters using the OperationHistoryManager
320
+ if ((_this$sceneViewer$man6 = this.sceneViewer.managers) !== null && _this$sceneViewer$man6 !== void 0 && _this$sceneViewer$man6.operationHistoryManager) {
321
+ this.sceneViewer.managers.operationHistoryManager.addToOperationHistory('translateGateway', {
322
+ gatewayId: gatewayId,
323
+ axis: axis,
324
+ value: value
325
+ });
326
+ }
327
+
328
+ // Validate parameters
329
+ if (!gatewayId || !axis || value === undefined || value === null) {
330
+ console.error('❌ translateGateway(): Invalid parameters');
331
+ return false;
332
+ }
333
+ if (!['x', 'y', 'z'].includes(axis)) {
334
+ console.error("\u274C translateGateway(): Invalid axis '".concat(axis, "'. Must be 'x', 'y', or 'z'"));
335
+ return false;
336
+ }
337
+ if (!this.sceneViewer || !this.sceneViewer.scene) {
338
+ console.error('❌ translateGateway(): Scene viewer or scene not available');
339
+ return false;
340
+ }
341
+
342
+ // Find the gateway in the scene
343
+ var gateway = null;
344
+ this.sceneViewer.scene.traverse(function (child) {
345
+ var _child$userData3;
346
+ if (child.uuid === gatewayId || ((_child$userData3 = child.userData) === null || _child$userData3 === void 0 ? void 0 : _child$userData3.originalUuid) === gatewayId) {
347
+ gateway = child;
348
+ }
349
+ });
350
+ if (!gateway) {
351
+ console.error("\u274C translateGateway(): Gateway with ID '".concat(gatewayId, "' not found in scene"));
352
+ return false;
353
+ }
354
+
355
+ // Validate that it's actually a gateway
356
+ if (!gateway.userData || gateway.userData.objectType !== 'gateway') {
357
+ console.error("\u274C translateGateway(): Object with ID '".concat(gatewayId, "' is not a valid gateway"));
358
+ return false;
359
+ }
360
+ console.log("\uD83D\uDD04 translateGateway(): Translating gateway ".concat(gatewayId, " on ").concat(axis, " axis by ").concat(value));
361
+
362
+ // Apply the translation to the Three.js object
363
+ gateway.position[axis] += value;
364
+
365
+ // Update matrices
366
+ gateway.updateMatrix();
367
+ gateway.updateMatrixWorld(true);
368
+
369
+ // Handle manual gateway transformation (convert to declared and process connections)
370
+ console.log('🔧 Handling manual gateway transformation via SceneOperationsManager');
371
+ var sceneOperationsManager = (_this$sceneViewer$man7 = this.sceneViewer.managers) === null || _this$sceneViewer$man7 === void 0 ? void 0 : _this$sceneViewer$man7.sceneOperationsManager;
372
+ if (!sceneOperationsManager) {
373
+ console.error('❌ translateGateway(): SceneOperationsManager not available');
374
+ return false;
375
+ }
376
+ try {
377
+ sceneOperationsManager.manualizeGateway(gateway, this.sceneViewer.currentSceneData);
378
+ } catch (error) {
379
+ console.error('❌ translateGateway(): Error in manualizeGateway:', error);
380
+ return false;
381
+ }
382
+
383
+ // Regenerate paths to reflect the gateway's new position
384
+ console.log('🔄 Regenerating paths to reflect gateway movement...');
385
+ try {
386
+ if (this.sceneViewer && typeof this.sceneViewer.updatePaths === 'function') {
387
+ this.sceneViewer.updatePaths();
388
+ console.log('✅ Paths regenerated successfully - routing updated for new gateway position');
389
+ } else {
390
+ console.warn('⚠️ updatePaths method not available on sceneViewer');
391
+ }
392
+ } catch (error) {
393
+ console.error('❌ Error regenerating paths:', error);
394
+ }
395
+
396
+ // Emit transform event if available
397
+ if (this.sceneViewer.emit && typeof this.sceneViewer.emit === 'function') {
398
+ this.sceneViewer.emit('objectTransformed', {
399
+ object: gateway,
400
+ transformType: 'translateGateway',
401
+ axis: axis,
402
+ value: value
403
+ });
404
+ }
405
+ console.log("\u2705 translateGateway(): Gateway ".concat(gatewayId, " translated and marked as declared successfully"));
406
+ return true;
407
+ }
408
+
409
+ /**
410
+ * Rotate a component by componentId
411
+ * @param {string} componentId - The UUID of the component to rotate
412
+ * @param {string} axis - The axis to rotate around ('x', 'y', or 'z')
413
+ * @param {number} value - The value to rotate by in degrees
414
+ * @returns {boolean} True if rotation was successful, false otherwise
415
+ */
416
+ }, {
417
+ key: "rotateComponent",
418
+ value: function rotateComponent(componentId, axis, value) {
419
+ // Validate parameters
420
+ if (!componentId || !axis || value === undefined || value === null) {
421
+ console.error('❌ rotateComponent(): Invalid parameters');
422
+ return false;
423
+ }
424
+ if (!['x', 'y', 'z'].includes(axis)) {
425
+ console.error("\u274C rotateComponent(): Invalid axis '".concat(axis, "'. Must be 'x', 'y', or 'z'"));
426
+ return false;
427
+ }
428
+ if (!this.sceneViewer || !this.sceneViewer.scene) {
429
+ console.error('❌ rotateComponent(): Scene viewer or scene not available');
430
+ return false;
431
+ }
432
+
433
+ // Find the component in the scene
434
+ var component = null;
435
+ this.sceneViewer.scene.traverse(function (child) {
436
+ var _child$userData4;
437
+ if (child.uuid === componentId || ((_child$userData4 = child.userData) === null || _child$userData4 === void 0 ? void 0 : _child$userData4.originalUuid) === componentId) {
438
+ component = child;
439
+ }
440
+ });
441
+ if (!component) {
442
+ console.error("\u274C rotateComponent(): Component with ID '".concat(componentId, "' not found in scene"));
443
+ return false;
444
+ }
445
+ if (!component.userData.libraryId) {
446
+ console.error("\u274C rotateComponent(): Object with ID '".concat(componentId, "' is not a valid component"));
447
+ return false;
448
+ }
449
+
450
+ // Store previous rotation for potential undo
451
+ var previousRotation = {
452
+ x: component.rotation.x,
453
+ y: component.rotation.y,
454
+ z: component.rotation.z
455
+ };
456
+
457
+ // Convert degrees to radians
458
+ var radians = value * Math.PI / 180;
459
+ console.log("\uD83D\uDD04 rotateComponent(): Rotating component ".concat(componentId, " around ").concat(axis, " axis by ").concat(value, " degrees (").concat(radians, " radians)"));
460
+
461
+ // Apply the rotation
462
+ component.rotation[axis] += radians;
463
+
464
+ // Update direction vectors for connectors if this is a 90-degree rotation
465
+ if (value % 90 === 0) {
466
+ this.updateConnectorDirections(component, axis, value);
467
+ }
468
+
469
+ // Update matrices
470
+ component.updateMatrix();
471
+ component.updateMatrixWorld(true);
472
+
473
+ // Emit transform event if available
474
+ if (this.sceneViewer.emit && typeof this.sceneViewer.emit === 'function') {
475
+ this.sceneViewer.emit('objectTransformed', {
476
+ object: component,
477
+ transformType: 'rotate',
478
+ axis: axis,
479
+ value: value,
480
+ radians: radians,
481
+ previousRotation: previousRotation
482
+ });
483
+ }
484
+ console.log("\u2705 rotateComponent(): Component ".concat(componentId, " rotated successfully"));
485
+ return true;
486
+ }
487
+
488
+ /**
489
+ * Update connector direction vectors after component rotation
490
+ * @param {THREE.Object3D} component - The component that was rotated
491
+ * @param {string} axis - The axis of rotation ('x', 'y', or 'z')
492
+ * @param {number} degrees - The rotation angle in degrees (should be multiple of 90)
493
+ * @private
494
+ */
495
+ }, {
496
+ key: "updateConnectorDirections",
497
+ value: function updateConnectorDirections(component, axis, degrees) {
498
+ if (!component || degrees % 90 !== 0) {
499
+ return;
500
+ }
501
+ console.log("\uD83D\uDD04 Updating connector directions after ".concat(degrees, "\xB0 rotation around ").concat(axis, " axis"));
502
+
503
+ // Simple approach: just log that directions may need manual verification
504
+ // In most cases, Three.js world matrices handle the actual positioning correctly
505
+ component.traverse(function (child) {
506
+ var _child$userData5, _child$userData6;
507
+ if (((_child$userData5 = child.userData) === null || _child$userData5 === void 0 ? void 0 : _child$userData5.objectType) === 'connector' && (_child$userData6 = child.userData) !== null && _child$userData6 !== void 0 && _child$userData6.direction) {
508
+ console.log("\uD83D\uDCCD Connector ".concat(child.uuid, " direction may need verification after rotation"));
509
+ }
510
+ });
511
+ }
512
+
513
+ /**
514
+ * Check if a component is positioned underground (below Z=0 in Z-up coordinate system)
515
+ * @param {THREE.Object3D} component - The component to check
516
+ * @returns {Object} Object with { isUnderground: boolean, lowestZ: number, correction: number }
517
+ */
518
+ }, {
519
+ key: "checkComponentUnderground",
520
+ value: function checkComponentUnderground(component) {
521
+ if (!component) {
522
+ return {
523
+ isUnderground: false,
524
+ lowestZ: 0,
525
+ correction: 0
526
+ };
527
+ }
528
+ try {
529
+ // Calculate bounding box in world coordinates
530
+ var boundingBox = new THREE.Box3().setFromObject(component);
531
+
532
+ // In Z-up coordinate system, ground is at z=0
533
+ // Check the lowest Z point of the bounding box
534
+ var lowestZ = boundingBox.min.z;
535
+ var isUnderground = lowestZ < 0;
536
+
537
+ // Calculate how much we need to move up to put the lowest point at ground level
538
+ var correction = isUnderground ? Math.abs(lowestZ) : 0;
539
+ return {
540
+ isUnderground: isUnderground,
541
+ lowestZ: lowestZ,
542
+ correction: correction
543
+ };
544
+ } catch (error) {
545
+ console.warn('⚠️ Error checking component underground status:', error);
546
+ return {
547
+ isUnderground: false,
548
+ lowestZ: 0,
549
+ correction: 0
550
+ };
551
+ }
552
+ }
553
+
554
+ /**
555
+ * Fix component position if it's underground
556
+ * @param {THREE.Object3D} component - The component to fix
557
+ * @returns {boolean} True if component was adjusted, false otherwise
558
+ */
559
+ }, {
560
+ key: "fixUndergroundComponent",
561
+ value: function fixUndergroundComponent(component) {
562
+ if (!component) {
563
+ return false;
564
+ }
565
+ var checkResult = this.checkComponentUnderground(component);
566
+ if (checkResult.isUnderground) {
567
+ var _component$userData;
568
+ var componentId = component.uuid || ((_component$userData = component.userData) === null || _component$userData === void 0 ? void 0 : _component$userData.originalUuid) || 'unknown';
569
+ console.log("\uD83D\uDD27 Component ".concat(componentId, " is underground (lowest Z: ").concat(checkResult.lowestZ.toFixed(3), "), moving up by ").concat(checkResult.correction.toFixed(3)));
570
+
571
+ // Move the component up so its lowest point is at ground level (z=0)
572
+ component.position.z += checkResult.correction;
573
+
574
+ // Update matrices
575
+ component.updateMatrix();
576
+ component.updateMatrixWorld(true);
577
+ console.log("\u2705 Component ".concat(componentId, " moved to surface (new position Z: ").concat(component.position.z.toFixed(3), ")"));
578
+ return true;
579
+ }
580
+ return false;
581
+ }
582
+
583
+ /**
584
+ * Check if a pipe segment is horizontal (Z-axis aligned in Z-up coordinate system)
585
+ * A segment is considered horizontal if its direction vector has minimal Z component
586
+ * @param {THREE.Object3D} segment - The pipe segment to check
587
+ * @param {number} tolerance - Angular tolerance in radians (default: ~5.7 degrees)
588
+ * @returns {boolean} True if segment is horizontal, false otherwise
589
+ * @private
590
+ */
591
+ }, {
592
+ key: "isSegmentHorizontal",
593
+ value: function isSegmentHorizontal(segment) {
594
+ var tolerance = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0.1;
595
+ if (!segment || !segment.geometry || !segment.quaternion) {
596
+ console.warn('⚠️ Invalid segment provided to isSegmentHorizontal');
597
+ return false;
598
+ }
599
+
600
+ // Get the segment's direction vector (cylinder aligned with Y-axis by default)
601
+ var direction = new THREE.Vector3(0, 1, 0);
602
+ direction.applyQuaternion(segment.quaternion);
603
+ direction.normalize();
604
+
605
+ // In Z-up coordinate system, horizontal segments should have Z component near 0
606
+ // A perfectly horizontal segment would have direction.z = 0
607
+ var zComponent = Math.abs(direction.z);
608
+
609
+ // Allow small tolerance for floating point precision
610
+ return zComponent < tolerance;
611
+ }
612
+
613
+ /**
614
+ * Calculate segment endpoints in world coordinates
615
+ * @param {THREE.Object3D} segment - The pipe segment
616
+ * @returns {Object} Object with start and end Vector3 points
617
+ * @private
618
+ */
619
+ }, {
620
+ key: "calculateSegmentEndpoints",
621
+ value: function calculateSegmentEndpoints(segment) {
622
+ // Get the segment's geometry to determine its length and orientation
623
+ var geometry = segment.geometry;
624
+ var length = geometry.parameters.height || 1;
625
+
626
+ // Calculate start and end points based on segment position and orientation
627
+ var startPoint = new THREE.Vector3();
628
+ var endPoint = new THREE.Vector3();
629
+
630
+ // Get the segment's direction vector
631
+ var direction = new THREE.Vector3(0, 1, 0);
632
+ direction.applyQuaternion(segment.quaternion);
633
+
634
+ // Calculate start point (half length back from center)
635
+ startPoint.copy(segment.position).sub(direction.clone().multiplyScalar(length / 2));
636
+
637
+ // Calculate end point (half length forward from center)
638
+ endPoint.copy(segment.position).add(direction.clone().multiplyScalar(length / 2));
639
+ return {
640
+ start: startPoint,
641
+ end: endPoint
642
+ };
643
+ }
644
+
645
+ /**
646
+ * Find all connectors (component or manual) at given positions
647
+ * @param {Array<THREE.Vector3>} positions - Array of positions to check
648
+ * @param {number} tolerance - Distance tolerance for position matching
649
+ * @returns {Array<THREE.Object3D>} Array of connector objects found at positions
650
+ * @private
651
+ */
652
+ }, {
653
+ key: "findConnectorsAtPositions",
654
+ value: function findConnectorsAtPositions(positions) {
655
+ var _this$sceneViewer4;
656
+ var tolerance = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0.15;
657
+ if (!((_this$sceneViewer4 = this.sceneViewer) !== null && _this$sceneViewer4 !== void 0 && _this$sceneViewer4.scene)) {
658
+ return [];
659
+ }
660
+ var foundConnectors = [];
661
+
662
+ // Traverse scene to find all connectors
663
+ this.sceneViewer.scene.traverse(function (child) {
664
+ var _child$userData7;
665
+ // Check if this is a connector (component connector or manual segment connector)
666
+ if (((_child$userData7 = child.userData) === null || _child$userData7 === void 0 ? void 0 : _child$userData7.objectType) === 'connector') {
667
+ // Get world position of connector
668
+ var connectorWorldPos = new THREE.Vector3();
669
+ child.getWorldPosition(connectorWorldPos);
670
+
671
+ // Check if connector is near any of the target positions
672
+ var _iterator = _createForOfIteratorHelper(positions),
673
+ _step;
674
+ try {
675
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
676
+ var position = _step.value;
677
+ var distance = connectorWorldPos.distanceTo(position);
678
+ if (distance <= tolerance) {
679
+ foundConnectors.push(child);
680
+ break; // Don't count the same connector twice
681
+ }
682
+ }
683
+ } catch (err) {
684
+ _iterator.e(err);
685
+ } finally {
686
+ _iterator.f();
687
+ }
688
+ }
689
+ });
690
+ return foundConnectors;
691
+ }
692
+
693
+ /**
694
+ * Update connector positions in scene data after a declared segment is moved
695
+ * @param {THREE.Object3D} segment - The segment whose connectors need updating
696
+ * @private
697
+ */
698
+ }, {
699
+ key: "updateSegmentConnectorPositions",
700
+ value: function updateSegmentConnectorPositions(segment) {
701
+ var _this$sceneViewer5;
702
+ console.log("updateSegmentConnectorPositions started:", segment);
703
+ if (!segment || !((_this$sceneViewer5 = this.sceneViewer) !== null && _this$sceneViewer5 !== void 0 && (_this$sceneViewer5 = _this$sceneViewer5.currentSceneData) !== null && _this$sceneViewer5 !== void 0 && _this$sceneViewer5.scene)) {
704
+ console.warn('⚠️ updateSegmentConnectorPositions(): Missing segment or scene data');
705
+ return;
706
+ }
707
+
708
+ // Helper function to clean up floating-point precision errors
709
+ var cleanPosition = function cleanPosition(value) {
710
+ return Math.abs(value) < 1e-10 ? 0 : value;
711
+ };
712
+
713
+ // Find all child connectors of the segment
714
+ var connectors = [];
715
+ segment.traverse(function (child) {
716
+ var _child$userData8;
717
+ if (((_child$userData8 = child.userData) === null || _child$userData8 === void 0 ? void 0 : _child$userData8.objectType) === 'segment-connector') {
718
+ connectors.push(child);
719
+ }
720
+ });
721
+ if (connectors.length === 0) {
722
+ console.warn("\u26A0\uFE0F No connectors found for segment ".concat(segment.uuid));
723
+ return;
724
+ }
725
+ console.log("\uD83D\uDCCD Updating ".concat(connectors.length, " connector positions in scene data for segment ").concat(segment.uuid));
726
+
727
+ // First, find the segment in scene data
728
+ var sceneDataSegment = this.sceneViewer.currentSceneData.scene.children.find(function (child) {
729
+ var _segment$userData3;
730
+ return child.uuid === segment.uuid || child.uuid === ((_segment$userData3 = segment.userData) === null || _segment$userData3 === void 0 ? void 0 : _segment$userData3.originalUuid);
731
+ });
732
+ if (!sceneDataSegment) {
733
+ console.warn("\u26A0\uFE0F Segment ".concat(segment.uuid, " not found in scene data"));
734
+ return;
735
+ }
736
+ if (!sceneDataSegment.children || !Array.isArray(sceneDataSegment.children)) {
737
+ console.warn("\u26A0\uFE0F Segment ".concat(segment.uuid, " has no children array in scene data"));
738
+ return;
739
+ }
740
+ console.log("\u2705 Found segment in scene data with ".concat(sceneDataSegment.children.length, " children"));
741
+
742
+ // Update each connector's position in scene data
743
+ connectors.forEach(function (connector) {
744
+ // Get world position
745
+ var worldPosition = new THREE.Vector3();
746
+ connector.getWorldPosition(worldPosition);
747
+
748
+ // Find the connector in the segment's children array (not root scene.children)
749
+ var sceneDataConnector = sceneDataSegment.children.find(function (child) {
750
+ var _connector$userData2;
751
+ return child.uuid === connector.uuid || child.uuid === ((_connector$userData2 = connector.userData) === null || _connector$userData2 === void 0 ? void 0 : _connector$userData2.originalUuid);
752
+ });
753
+ if (sceneDataConnector && sceneDataConnector.userData) {
754
+ // Update position as array [x, y, z] for pathfinder compatibility
755
+ sceneDataConnector.userData.position = [cleanPosition(worldPosition.x), cleanPosition(worldPosition.y), cleanPosition(worldPosition.z)];
756
+ console.log("\u2705 Updated connector ".concat(connector.uuid, " position to [").concat(sceneDataConnector.userData.position.join(', '), "]"));
757
+ } else {
758
+ console.warn("\u26A0\uFE0F Connector ".concat(connector.uuid, " not found in segment's children in scene data"));
759
+ }
760
+ });
761
+ }
762
+
763
+ /**
764
+ * Check if a segment intersects with any computed segments in the scene
765
+ * Uses segment-to-segment distance calculation, excluding endpoint connections
766
+ * @param {THREE.Object3D} segment - The segment to check for intersections
767
+ * @returns {boolean} True if segment intersects with any computed segment (not just touching at ends)
768
+ * @private
769
+ */
770
+ }, {
771
+ key: "checkSegmentIntersection",
772
+ value: function checkSegmentIntersection(segment) {
773
+ var _this$sceneViewer6,
774
+ _this = this;
775
+ if (!((_this$sceneViewer6 = this.sceneViewer) !== null && _this$sceneViewer6 !== void 0 && _this$sceneViewer6.scene)) {
776
+ return false;
777
+ }
778
+
779
+ // Get endpoints of the segment being checked
780
+ var segmentEndpoints = this.calculateSegmentEndpoints(segment);
781
+ var segmentRadius = segment.geometry.parameters.radiusTop || 0.05;
782
+
783
+ // Track if any intersection was found
784
+ var hasIntersection = false;
785
+
786
+ // Check all segments in the scene
787
+ this.sceneViewer.scene.traverse(function (child) {
788
+ var _child$userData9, _child$userData0;
789
+ // Skip the segment itself
790
+ if (child.uuid === segment.uuid) {
791
+ return;
792
+ }
793
+
794
+ // Only check computed segments (isDeclared === false or undefined)
795
+ if (((_child$userData9 = child.userData) === null || _child$userData9 === void 0 ? void 0 : _child$userData9.objectType) === 'segment' && ((_child$userData0 = child.userData) === null || _child$userData0 === void 0 ? void 0 : _child$userData0.isDeclared) !== true) {
796
+ // Get endpoints of the computed segment
797
+ var computedEndpoints = _this.calculateSegmentEndpoints(child);
798
+ var computedRadius = child.geometry.parameters.radiusTop || 0.05;
799
+
800
+ // Combined radius threshold - segments intersect if closer than this
801
+ var combinedRadius = segmentRadius + computedRadius;
802
+
803
+ // First, check if segments are connected at endpoints
804
+ // This is a cheaper check than full segment-to-segment distance
805
+ var endpointTolerance = 0.55; // Generous tolerance for endpoint connections
806
+
807
+ var d1 = segmentEndpoints.start.distanceTo(computedEndpoints.start);
808
+ var d2 = segmentEndpoints.start.distanceTo(computedEndpoints.end);
809
+ var d3 = segmentEndpoints.end.distanceTo(computedEndpoints.start);
810
+ var d4 = segmentEndpoints.end.distanceTo(computedEndpoints.end);
811
+
812
+ // Check if any endpoint of segment A is near any endpoint of segment B (they're connected)
813
+ var isConnectedAtEnds = d1 < endpointTolerance || d2 < endpointTolerance || d3 < endpointTolerance || d4 < endpointTolerance;
814
+
815
+ // If connected at ends, skip this segment (no collision)
816
+ if (isConnectedAtEnds) {
817
+ console.log("\u2705 Segments connected at endpoints - skipping intersection check (min endpoint distance: ".concat(Math.min(d1, d2, d3, d4).toFixed(3), ")"));
818
+ return;
819
+ }
820
+
821
+ // Calculate the minimum distance between the two line segments
822
+ var distance = _this.calculateSegmentToSegmentDistance(segmentEndpoints.start, segmentEndpoints.end, computedEndpoints.start, computedEndpoints.end);
823
+
824
+ // Only flag as intersection if segments are closer than combined radius
825
+ if (distance < combinedRadius) {
826
+ console.log("\uD83D\uDD34 Intersection detected with computed segment: ".concat(child.uuid || child.name));
827
+ console.log(" Distance: ".concat(distance.toFixed(3), ", Combined radius: ").concat(combinedRadius.toFixed(3)));
828
+ console.log(" Endpoint distances: [".concat(d1.toFixed(3), ", ").concat(d2.toFixed(3), ", ").concat(d3.toFixed(3), ", ").concat(d4.toFixed(3), "]"));
829
+ hasIntersection = true;
830
+ }
831
+ }
832
+ });
833
+ return hasIntersection;
834
+ }
835
+
836
+ /**
837
+ * Calculate the minimum distance between two line segments in 3D space
838
+ * @param {THREE.Vector3} p1 - Start point of first segment
839
+ * @param {THREE.Vector3} p2 - End point of first segment
840
+ * @param {THREE.Vector3} p3 - Start point of second segment
841
+ * @param {THREE.Vector3} p4 - End point of second segment
842
+ * @returns {number} Minimum distance between the two segments
843
+ * @private
844
+ */
845
+ }, {
846
+ key: "calculateSegmentToSegmentDistance",
847
+ value: function calculateSegmentToSegmentDistance(p1, p2, p3, p4) {
848
+ // Direction vectors
849
+ var d1 = new THREE.Vector3().subVectors(p2, p1); // Segment 1 direction
850
+ var d2 = new THREE.Vector3().subVectors(p4, p3); // Segment 2 direction
851
+ var r = new THREE.Vector3().subVectors(p1, p3); // Vector between start points
852
+
853
+ var a = d1.dot(d1); // Squared length of segment 1
854
+ var e = d2.dot(d2); // Squared length of segment 2
855
+ var f = d2.dot(r);
856
+
857
+ // Handle degenerate cases (zero-length segments)
858
+ if (a <= 1e-10 && e <= 1e-10) {
859
+ // Both segments are points
860
+ return p1.distanceTo(p3);
861
+ }
862
+ if (a <= 1e-10) {
863
+ // First segment is a point
864
+ var _t = Math.max(0, Math.min(1, f / e));
865
+ var closestPoint = new THREE.Vector3().addVectors(p3, d2.clone().multiplyScalar(_t));
866
+ return p1.distanceTo(closestPoint);
867
+ }
868
+ var c = d1.dot(r);
869
+ if (e <= 1e-10) {
870
+ // Second segment is a point
871
+ var _s = Math.max(0, Math.min(1, -c / a));
872
+ var _closestPoint = new THREE.Vector3().addVectors(p1, d1.clone().multiplyScalar(_s));
873
+ return p3.distanceTo(_closestPoint);
874
+ }
875
+
876
+ // General case - both segments have length
877
+ var b = d1.dot(d2);
878
+ var denom = a * e - b * b;
879
+
880
+ // Compute parameters for closest points
881
+ var s, t;
882
+ if (denom !== 0) {
883
+ s = Math.max(0, Math.min(1, (b * f - c * e) / denom));
884
+ } else {
885
+ s = 0;
886
+ }
887
+ t = (b * s + f) / e;
888
+ if (t < 0) {
889
+ t = 0;
890
+ s = Math.max(0, Math.min(1, -c / a));
891
+ } else if (t > 1) {
892
+ t = 1;
893
+ s = Math.max(0, Math.min(1, (b - c) / a));
894
+ }
895
+
896
+ // Calculate closest points
897
+ var c1 = new THREE.Vector3().addVectors(p1, d1.clone().multiplyScalar(s));
898
+ var c2 = new THREE.Vector3().addVectors(p3, d2.clone().multiplyScalar(t));
899
+ return c1.distanceTo(c2);
900
+ }
901
+ }]);
902
+ }();
903
+
904
+ export { TransformOperationsManager, TransformOperationsManager as default };