@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,363 @@
1
+ import { createClass as _createClass, toConsumableArray as _toConsumableArray, classCallCheck as _classCallCheck } from '../../../_virtual/_rollupPluginBabelHelpers.js';
2
+ import * as THREE from 'three';
3
+
4
+ /**
5
+ * PathIntersectionDetector - Detects intersections between pipe paths
6
+ *
7
+ * This class handles all intersection detection logic for both computed paths
8
+ * (from pathfinding) and manual segments (user-positioned pipes).
9
+ *
10
+ * @class PathIntersectionDetector
11
+ */
12
+ var PathIntersectionDetector = /*#__PURE__*/function () {
13
+ /**
14
+ * Create a new PathIntersectionDetector
15
+ * @param {Object} sceneViewer - Reference to the scene viewer
16
+ */
17
+ function PathIntersectionDetector(sceneViewer) {
18
+ _classCallCheck(this, PathIntersectionDetector);
19
+ this.sceneViewer = sceneViewer;
20
+ }
21
+
22
+ /**
23
+ * Check for intersections between paths
24
+ * @param {Array} paths - Array of path objects from pathfinder
25
+ * @param {number} tolerance - Distance tolerance for considering pipes as intersecting (default: 0.2)
26
+ * @param {boolean} includeManualSegments - Whether to include manual segments in the check (default: true)
27
+ * @returns {Object} Intersection results
28
+ * @property {boolean} hasIntersections - Whether any intersections were found
29
+ * @property {Array<Object>} intersections - Array of intersection details
30
+ * @property {number} count - Total number of intersections found
31
+ */
32
+ return _createClass(PathIntersectionDetector, [{
33
+ key: "checkPathIntersections",
34
+ value: function checkPathIntersections(paths) {
35
+ var tolerance = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0.2;
36
+ var includeManualSegments = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
37
+ var intersections = [];
38
+
39
+ // Convert all paths to line segments for comparison
40
+ var pathSegments = paths.map(function (pathData, pathIndex) {
41
+ if (!pathData.path || pathData.path.length < 2) {
42
+ return null;
43
+ }
44
+ var segments = [];
45
+ var pathPoints = pathData.path.map(function (point) {
46
+ if (point instanceof THREE.Vector3) {
47
+ return point.clone();
48
+ } else if (Array.isArray(point)) {
49
+ return new THREE.Vector3().fromArray(point);
50
+ } else if (point.x !== undefined && point.y !== undefined && point.z !== undefined) {
51
+ return new THREE.Vector3(point.x, point.y, point.z);
52
+ }
53
+ return null;
54
+ }).filter(function (p) {
55
+ return p !== null;
56
+ });
57
+ for (var i = 0; i < pathPoints.length - 1; i++) {
58
+ segments.push({
59
+ start: pathPoints[i],
60
+ end: pathPoints[i + 1],
61
+ pathIndex: pathIndex,
62
+ segmentIndex: i,
63
+ pathFrom: pathData.from,
64
+ pathTo: pathData.to,
65
+ isFirstSegment: i === 0,
66
+ isLastSegment: i === pathPoints.length - 2,
67
+ isManual: false // Computed path segment
68
+ });
69
+ }
70
+ return segments;
71
+ }).filter(function (s) {
72
+ return s !== null;
73
+ }).flat();
74
+
75
+ // Add manual segments if requested
76
+ if (includeManualSegments) {
77
+ var manualSegments = this._collectManualSegments(pathSegments.length);
78
+ pathSegments.push.apply(pathSegments, _toConsumableArray(manualSegments));
79
+ console.log("\uD83D\uDCE6 Added ".concat(manualSegments.length, " manual segment(s) to intersection check"));
80
+ }
81
+ console.log("\uD83D\uDD0D Checking ".concat(pathSegments.length, " segments for intersections..."));
82
+
83
+ // Check each pair of segments
84
+ for (var i = 0; i < pathSegments.length; i++) {
85
+ for (var j = i + 1; j < pathSegments.length; j++) {
86
+ var seg1 = pathSegments[i];
87
+ var seg2 = pathSegments[j];
88
+
89
+ // Skip if segments are from the same path
90
+ if (seg1.pathIndex === seg2.pathIndex) {
91
+ continue;
92
+ }
93
+
94
+ // Check if segments intersect
95
+ var intersection = this._checkLineSegmentIntersection(seg1.start, seg1.end, seg2.start, seg2.end, tolerance);
96
+ if (intersection) {
97
+ // Check if this is a shared endpoint (connector/gateway)
98
+ // Paths naturally touch at shared connectors - this is not an intersection
99
+ var endpointTolerance = 0.01; // Very small tolerance for endpoint matching
100
+ var touchingAtEndpoint = this._areSegmentsTouchingAtSharedEndpoint(seg1, seg2, endpointTolerance);
101
+ if (touchingAtEndpoint) {
102
+ console.log("\u2139\uFE0F Segments share endpoint (not an intersection): ".concat(seg1.pathFrom, " \u2194 ").concat(seg2.pathFrom));
103
+ continue; // Skip this - it's a valid shared connection point
104
+ }
105
+ intersections.push({
106
+ path1: {
107
+ from: seg1.pathFrom,
108
+ to: seg1.pathTo,
109
+ segmentIndex: seg1.segmentIndex,
110
+ isManual: seg1.isManual
111
+ },
112
+ path2: {
113
+ from: seg2.pathFrom,
114
+ to: seg2.pathTo,
115
+ segmentIndex: seg2.segmentIndex,
116
+ isManual: seg2.isManual
117
+ },
118
+ point: intersection.point,
119
+ distance: intersection.distance,
120
+ type: intersection.type
121
+ });
122
+ var seg1Type = seg1.isManual ? 'manual' : 'computed';
123
+ var seg2Type = seg2.isManual ? 'manual' : 'computed';
124
+ console.log("\u26A0\uFE0F Intersection found between paths:\n Path 1 (".concat(seg1Type, "): ").concat(seg1.pathFrom, " \u2192 ").concat(seg1.pathTo, " (segment ").concat(seg1.segmentIndex, ")\n Path 2 (").concat(seg2Type, "): ").concat(seg2.pathFrom, " \u2192 ").concat(seg2.pathTo, " (segment ").concat(seg2.segmentIndex, ")\n Distance: ").concat(intersection.distance.toFixed(4), "\n Type: ").concat(intersection.type));
125
+ }
126
+ }
127
+ }
128
+ var result = {
129
+ hasIntersections: intersections.length > 0,
130
+ intersections: intersections,
131
+ count: intersections.length
132
+ };
133
+ if (result.hasIntersections) {
134
+ console.log("\uD83D\uDEA8 Found ".concat(result.count, " intersection(s) between paths"));
135
+ } else {
136
+ console.log("\u2705 No path intersections detected");
137
+ }
138
+ return result;
139
+ }
140
+
141
+ /**
142
+ * Collect manual segments from the scene and convert them to path segments format
143
+ * @private
144
+ * @param {number} startingPathIndex - The starting index for manual segment paths
145
+ * @returns {Array} Array of segment objects in the same format as computed paths
146
+ */
147
+ }, {
148
+ key: "_collectManualSegments",
149
+ value: function _collectManualSegments(startingPathIndex) {
150
+ var _this = this;
151
+ var manualSegments = [];
152
+ var sceneViewer = this.sceneViewer;
153
+ var pathIndex = startingPathIndex;
154
+
155
+ // Traverse the scene to find manual segments (lowercase "Segment-")
156
+ sceneViewer.scene.traverse(function (obj) {
157
+ var _obj$userData, _obj$userData2;
158
+ if (obj.uuid && obj.uuid.startsWith('Segment-') && ((_obj$userData = obj.userData) === null || _obj$userData === void 0 ? void 0 : _obj$userData.objectType) === 'segment' && ((_obj$userData2 = obj.userData) === null || _obj$userData2 === void 0 ? void 0 : _obj$userData2.isDeclared) === true) {
159
+ // Calculate segment endpoints from the cylinder geometry
160
+ var endpoints = _this._calculateSegmentEndpoints(obj);
161
+
162
+ // Create segment object in the same format as computed paths
163
+ manualSegments.push({
164
+ start: endpoints.start,
165
+ end: endpoints.end,
166
+ pathIndex: pathIndex,
167
+ segmentIndex: 0,
168
+ // Manual segments are single-segment paths
169
+ pathFrom: obj.userData.pathFrom || obj.uuid,
170
+ pathTo: obj.userData.pathTo || obj.uuid,
171
+ isFirstSegment: true,
172
+ isLastSegment: true,
173
+ isManual: true,
174
+ // Flag to identify manual segments
175
+ segmentUuid: obj.uuid
176
+ });
177
+ pathIndex++;
178
+ console.log("\uD83D\uDCE6 Collected manual segment: ".concat(obj.uuid));
179
+ }
180
+ });
181
+ return manualSegments;
182
+ }
183
+
184
+ /**
185
+ * Calculate the start and end points of a pipe segment in world coordinates
186
+ * @private
187
+ * @param {THREE.Object3D} segment - The pipe segment
188
+ * @returns {Object} Object with start and end points
189
+ */
190
+ }, {
191
+ key: "_calculateSegmentEndpoints",
192
+ value: function _calculateSegmentEndpoints(segment) {
193
+ // Get the segment's geometry to determine its length and orientation
194
+ var geometry = segment.geometry;
195
+ var length = geometry.parameters.height || 1;
196
+
197
+ // Calculate start and end points based on segment position and orientation
198
+ var startPoint = new THREE.Vector3();
199
+ var endPoint = new THREE.Vector3();
200
+
201
+ // Get the segment's direction vector
202
+ var direction = new THREE.Vector3(0, 1, 0);
203
+ direction.applyQuaternion(segment.quaternion);
204
+
205
+ // Calculate start point (half length back from center)
206
+ startPoint.copy(segment.position).sub(direction.clone().multiplyScalar(length / 2));
207
+
208
+ // Calculate end point (half length forward from center)
209
+ endPoint.copy(segment.position).add(direction.clone().multiplyScalar(length / 2));
210
+ return {
211
+ start: startPoint,
212
+ end: endPoint
213
+ };
214
+ }
215
+
216
+ /**
217
+ * Check if two segments are touching at a shared endpoint (connector/gateway)
218
+ * @private
219
+ * @param {Object} seg1 - First segment
220
+ * @param {Object} seg2 - Second segment
221
+ * @param {number} tolerance - Distance tolerance for considering points as the same
222
+ * @returns {boolean} True if segments share an endpoint
223
+ */
224
+ }, {
225
+ key: "_areSegmentsTouchingAtSharedEndpoint",
226
+ value: function _areSegmentsTouchingAtSharedEndpoint(seg1, seg2, tolerance) {
227
+ // Check all possible endpoint combinations
228
+ var endpoints = [
229
+ // seg1.start with seg2 endpoints
230
+ {
231
+ p1: seg1.start,
232
+ p2: seg2.start,
233
+ isSeg1Start: true,
234
+ isSeg2Start: true
235
+ }, {
236
+ p1: seg1.start,
237
+ p2: seg2.end,
238
+ isSeg1Start: true,
239
+ isSeg2Start: false
240
+ },
241
+ // seg1.end with seg2 endpoints
242
+ {
243
+ p1: seg1.end,
244
+ p2: seg2.start,
245
+ isSeg1Start: false,
246
+ isSeg2Start: true
247
+ }, {
248
+ p1: seg1.end,
249
+ p2: seg2.end,
250
+ isSeg1Start: false,
251
+ isSeg2Start: false
252
+ }];
253
+ for (var _i = 0, _endpoints = endpoints; _i < _endpoints.length; _i++) {
254
+ var _endpoints$_i = _endpoints[_i],
255
+ p1 = _endpoints$_i.p1,
256
+ p2 = _endpoints$_i.p2,
257
+ isSeg1Start = _endpoints$_i.isSeg1Start,
258
+ isSeg2Start = _endpoints$_i.isSeg2Start;
259
+ var distance = p1.distanceTo(p2);
260
+ if (distance <= tolerance) {
261
+ // Verify this is actually an endpoint of the path (not just any segment point)
262
+ var isSeg1Endpoint = isSeg1Start && seg1.isFirstSegment || !isSeg1Start && seg1.isLastSegment;
263
+ var isSeg2Endpoint = isSeg2Start && seg2.isFirstSegment || !isSeg2Start && seg2.isLastSegment;
264
+
265
+ // Only count as shared endpoint if at least one is a path endpoint
266
+ // (This handles both connector-to-connector and connector-to-gateway cases)
267
+ if (isSeg1Endpoint || isSeg2Endpoint) {
268
+ return true;
269
+ }
270
+ }
271
+ }
272
+ return false;
273
+ }
274
+
275
+ /**
276
+ * Check if two line segments intersect or come within tolerance distance
277
+ * @private
278
+ * @param {THREE.Vector3} p1 - Start of first segment
279
+ * @param {THREE.Vector3} p2 - End of first segment
280
+ * @param {THREE.Vector3} p3 - Start of second segment
281
+ * @param {THREE.Vector3} p4 - End of second segment
282
+ * @param {number} tolerance - Distance tolerance
283
+ * @returns {Object|null} Intersection details or null
284
+ */
285
+ }, {
286
+ key: "_checkLineSegmentIntersection",
287
+ value: function _checkLineSegmentIntersection(p1, p2, p3, p4, tolerance) {
288
+ // Calculate closest points on two line segments
289
+ var d1 = new THREE.Vector3().subVectors(p2, p1); // Direction of segment 1
290
+ var d2 = new THREE.Vector3().subVectors(p4, p3); // Direction of segment 2
291
+ var r = new THREE.Vector3().subVectors(p1, p3);
292
+ var a = d1.dot(d1);
293
+ var b = d1.dot(d2);
294
+ var c = d2.dot(d2);
295
+ var d = d1.dot(r);
296
+ var e = d2.dot(r);
297
+ var denom = a * c - b * b;
298
+
299
+ // Check if lines are parallel
300
+ if (Math.abs(denom) < 1e-10) {
301
+ // Lines are parallel, check if they're close enough
302
+ var distToLine = this._distancePointToLineSegment(p3, p1, p2);
303
+ if (distToLine <= tolerance) {
304
+ return {
305
+ point: p3.clone(),
306
+ distance: distToLine,
307
+ type: 'parallel'
308
+ };
309
+ }
310
+ return null;
311
+ }
312
+
313
+ // Calculate parameters for closest points
314
+ var s = (b * e - c * d) / denom;
315
+ var t = (a * e - b * d) / denom;
316
+
317
+ // Clamp to segment bounds
318
+ s = Math.max(0, Math.min(1, s));
319
+ t = Math.max(0, Math.min(1, t));
320
+
321
+ // Calculate closest points
322
+ var closestPoint1 = new THREE.Vector3().addVectors(p1, d1.clone().multiplyScalar(s));
323
+ var closestPoint2 = new THREE.Vector3().addVectors(p3, d2.clone().multiplyScalar(t));
324
+
325
+ // Calculate distance between closest points
326
+ var distance = closestPoint1.distanceTo(closestPoint2);
327
+
328
+ // Check if within tolerance
329
+ if (distance <= tolerance) {
330
+ var midPoint = new THREE.Vector3().addVectors(closestPoint1, closestPoint2).multiplyScalar(0.5);
331
+ return {
332
+ point: midPoint,
333
+ distance: distance,
334
+ type: distance < 0.01 ? 'crossing' : 'near'
335
+ };
336
+ }
337
+ return null;
338
+ }
339
+
340
+ /**
341
+ * Calculate distance from a point to a line segment
342
+ * @private
343
+ * @param {THREE.Vector3} point - The point
344
+ * @param {THREE.Vector3} lineStart - Start of line segment
345
+ * @param {THREE.Vector3} lineEnd - End of line segment
346
+ * @returns {number} Distance to line segment
347
+ */
348
+ }, {
349
+ key: "_distancePointToLineSegment",
350
+ value: function _distancePointToLineSegment(point, lineStart, lineEnd) {
351
+ var line = new THREE.Vector3().subVectors(lineEnd, lineStart);
352
+ var lineLength = line.length();
353
+ if (lineLength < 1e-10) {
354
+ return point.distanceTo(lineStart);
355
+ }
356
+ var t = Math.max(0, Math.min(1, new THREE.Vector3().subVectors(point, lineStart).dot(line) / (lineLength * lineLength)));
357
+ var projection = new THREE.Vector3().addVectors(lineStart, line.multiplyScalar(t));
358
+ return point.distanceTo(projection);
359
+ }
360
+ }]);
361
+ }();
362
+
363
+ export { PathIntersectionDetector };