@eturnity/eturnity_maths 9.7.0 → 9.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/package.json +1 -1
  2. package/src/coords.js +44 -11
  3. package/src/geometry.js +47 -14
  4. package/src/geometryShortDistance.js +442 -0
  5. package/src/index.js +3 -0
  6. package/src/intersectionCheck.js +165 -0
  7. package/src/intersectionPolygon.js +24 -1
  8. package/src/matrix.js +15 -1
  9. package/src/objects/Circle.js +55 -5
  10. package/src/objects/Line.js +530 -264
  11. package/src/objects/Measurement.js +335 -0
  12. package/src/objects/Plane.js +176 -0
  13. package/src/objects/Point.js +16 -7
  14. package/src/objects/Polygon.js +130 -2
  15. package/src/objects/derivedState/nodesEdgesCreation.js +112 -35
  16. package/src/objects/derivedState/updateComputedGeometryPolygon.js +1 -0
  17. package/src/objects/graph/graphCreation.js +5 -5
  18. package/src/objects/hydrate.js +229 -0
  19. package/src/objects/index.js +2 -0
  20. package/src/plane.js +76 -0
  21. package/src/splitMergePolygons.js +8 -8
  22. package/src/tests/Line/isTouchingLine.spec.js +233 -0
  23. package/src/tests/Plane/CoordinateChange.spec.js +32 -0
  24. package/src/tests/coords/toRealityRefFunction.spec.js +25 -0
  25. package/src/tests/geometry/getAngleInDegFrom2DENUVector.spec.js +60 -0
  26. package/src/tests/geometry/projectionOnPlaneFollowingVector.spec.js +215 -0
  27. package/src/tests/geometryShortDistance/areObjectsIntersecting.spec.js +426 -0
  28. package/src/tests/geometryShortDistance/findClosestPoint.spec.js +53 -0
  29. package/src/tests/geometryShortDistance/getShortestSegmentCircleCircle.spec.js +126 -0
  30. package/src/tests/geometryShortDistance/getShortestSegmentLineCircle.spec.js +124 -0
  31. package/src/tests/geometryShortDistance/getShortestSegmentPointLine.spec.js +133 -0
  32. package/src/tests/geometryShortDistance/getShortestSegmentPointPoint.spec.js +115 -0
@@ -1,11 +1,20 @@
1
- import { translate2D, getDegree } from '../geometry'
1
+ import {
2
+ translate2D,
3
+ getDegree,
4
+ isOnBorderOfPolygon,
5
+ getDistanceBetweenPoints,
6
+ get3DDistanceBetweenPoints,
7
+ } from '../geometry'
2
8
  import {
3
9
  substractVector,
4
10
  normalizeVector,
5
11
  multiplyVector,
6
12
  addVector,
13
+ areAlmostCollinear,
7
14
  } from '../vector'
8
-
15
+ import { Line } from './Line'
16
+ import { getPlaneFromPolygon } from '../plane'
17
+ import { defaultPlane } from './Plane'
9
18
  import { v4 as uuidv4 } from 'uuid'
10
19
  import { updateComputedGeometryPolygon } from './derivedState/updateComputedGeometryPolygon'
11
20
  import { rotateTransformation } from '../matrix'
@@ -211,6 +220,122 @@ export class Polygon {
211
220
  resetOffset() {
212
221
  this.positionOffset = { x: 0, y: 0, z: 0 }
213
222
  }
223
+ isPointOnBorder(point, tolerance = 0.0001) {
224
+ return isOnBorderOfPolygon(point, this.outline)
225
+ }
226
+ isLineIntersecting(line) {
227
+ return this.outline.some((p, index) => {
228
+ const nextP = this.outline[(index + 1) % this.outline.length]
229
+ const edge = new Line(p, nextP, null)
230
+ return edge.isTouchingLine(line)
231
+ })
232
+ }
233
+ isCircleIntersecting(circle) {
234
+ const isPolygonStrictlyInsideCircle = this.outline.every((p, index) => {
235
+ return getDistanceBetweenPoints(p, circle.center) < circle.radius
236
+ })
237
+ const isPolygonStrictlyOutsideCircle = this.outline.every((p, index) => {
238
+ const nextP = this.outline[(index + 1) % this.outline.length]
239
+ const edge = new Line(p, nextP)
240
+ return edge.getDistanceToPoint(circle.center) > circle.radius
241
+ })
242
+ return !isPolygonStrictlyOutsideCircle && !isPolygonStrictlyInsideCircle
243
+ }
244
+ isPolygonIntersecting(polygon) {
245
+ return this.outline.some((p, index) => {
246
+ const nextP = this.outline[(index + 1) % this.outline.length]
247
+ const edge = new Line(p, nextP)
248
+ return edge.isTouchingLine(polygon)
249
+ })
250
+ }
251
+ getDistanceToPoint(point) {
252
+ return this.outline
253
+ .map((p, index) => {
254
+ const nextP = this.outline[(index + 1) % this.outline.length]
255
+ const edge = new Line(p, nextP)
256
+ return edge.getDistanceToPoint(point)
257
+ })
258
+ .reduce((minDistance, distance) => {
259
+ return distance ? Math.min(minDistance, distance) : minDistance
260
+ }, Infinity)
261
+ }
262
+ getEdgesParallelToLine(line) {
263
+ return this.outline
264
+ .map((p, index) => {
265
+ const nextP = this.outline[(index + 1) % this.outline.length]
266
+ const edge = new Line(p, nextP)
267
+ const edgeDirection = edge.getDirectionVector()
268
+ const lineDirection = line.getDirectionVector()
269
+ if (areAlmostCollinear(edgeDirection, lineDirection, 0.1)) {
270
+ //projection of one to the other is not empty
271
+ const projectionA = line.getProjectedPointFromTop(
272
+ edge.outline[0],
273
+ false
274
+ )
275
+ const projectionB = edge.getProjectedPointFromTop(
276
+ line.outline[0],
277
+ false
278
+ )
279
+ const projectionC = edge.getProjectedPointFromTop(
280
+ line.outline[1],
281
+ false
282
+ )
283
+ const projectionD = line.getProjectedPointFromTop(
284
+ edge.outline[1],
285
+ false
286
+ )
287
+ const isProjectionOnLineA = line.includesPoint(projectionA)
288
+ const isProjectionOnLineB = edge.includesPoint(projectionB)
289
+ const isProjectionOnLineC = edge.includesPoint(projectionC)
290
+ const isProjectionOnLineD = line.includesPoint(projectionD)
291
+
292
+ if (
293
+ isProjectionOnLineA ||
294
+ isProjectionOnLineB ||
295
+ isProjectionOnLineC ||
296
+ isProjectionOnLineD
297
+ ) {
298
+ return edge
299
+ }
300
+ }
301
+ })
302
+ .filter((edge) => edge)
303
+ }
304
+ getProjectedPoints(point, isInfinite = false) {
305
+ return this.outline.map((p, index) => {
306
+ const nextP = this.outline[(index + 1) % this.outline.length]
307
+ const edge = new Line(p, nextP)
308
+ return edge.getProjectedPointFromTop(point, isInfinite)
309
+ })
310
+ }
311
+ getPlaneProjectionWithPointVector(point, vector) {
312
+ const line = new Line(point, addVector(point, vector), '', true)
313
+ const plane = getPlaneFromPolygon(this)
314
+ const intersections = plane.getIntersections(line)
315
+ return intersections
316
+ }
317
+ getProjectedPointFromPlane(point, isInfinite = false, plane = defaultPlane) {
318
+ const closestEdge = this.getClosestPolygonEdgeFromPlane(point, plane)
319
+ return closestEdge.getProjectedPointFromPlane(point, isInfinite, plane)
320
+ }
321
+ getProjectedPointsFromPlane(point, isInfinite = false, plane = defaultPlane) {
322
+ return this.outline.map((p, index) => {
323
+ const nextP = this.outline[(index + 1) % this.outline.length]
324
+ const edge = new Line(p, nextP)
325
+ return edge.getProjectedPointFromPlane(point, isInfinite, plane)
326
+ })
327
+ }
328
+ getClosestPolygonEdgeFromPlane(point, plane = defaultPlane) {
329
+ const polygonEdgeData = this.outline.map((p, index) => {
330
+ const nextP = this.outline[(index + 1) % this.outline.length]
331
+ const edge = new Line(p, nextP)
332
+ const projectedPoint = edge.getProjectedPointFromPlane(point, true, plane)
333
+ const distanceToPoint = get3DDistanceBetweenPoints(projectedPoint, point)
334
+ return { distanceToPoint, projectedPoint, edge }
335
+ })
336
+ polygonEdgeData.sort((a, b) => a.distanceToPoint - b.distanceToPoint)
337
+ return polygonEdgeData[0].edge
338
+ }
214
339
  serialize() {
215
340
  const baseSerialization = {
216
341
  id: this.id,
@@ -274,6 +399,9 @@ export class Polygon {
274
399
  extraSerialization.row_index = this.row_index
275
400
  extraSerialization.col_index = this.col_index
276
401
  extraSerialization.moduleField = { id: this.moduleField.id }
402
+ } else if (this.layer == 'roof_plan_item') {
403
+ extraSerialization.colorIndex = this.colorIndex
404
+ extraSerialization.hasFillColor = this.hasFillColor
277
405
  }
278
406
  return JSON.parse(
279
407
  JSON.stringify({ ...baseSerialization, ...extraSerialization })
@@ -6,10 +6,10 @@ import {
6
6
  isAlmostSameSegment2D,
7
7
  isAlmostSameSegment3D,
8
8
  isAlmostSamePoint2D,
9
- verticalProjectionOnPlane
9
+ verticalProjectionOnPlane,
10
10
  } from '../../geometry'
11
11
 
12
- export function generateNodes2D(polygons) {
12
+ export function generateNodes2D(polygons, circles) {
13
13
  let nodes2D = []
14
14
  polygons
15
15
  .filter((poly) =>
@@ -18,7 +18,8 @@ export function generateNodes2D(polygons) {
18
18
  'obstacle',
19
19
  'tmpModuleField',
20
20
  'moduleField',
21
- 'construction'
21
+ 'construction',
22
+ 'roof_plan_item',
22
23
  ].includes(poly.layer)
23
24
  )
24
25
  .forEach((polygon) => {
@@ -36,46 +37,79 @@ export function generateNodes2D(polygons) {
36
37
  node2D.id = 'node2D'
37
38
  node2D.itemType = 'node2D'
38
39
  node2D.layer = polygon.layer
40
+ node2D.colorIndex = polygon.colorIndex
39
41
  node2D.masterHandle = {
40
42
  id: node2D.id,
41
43
  itemType: 'masterHandle',
42
44
  open: null,
43
- selected: null
45
+ selected: null,
44
46
  }
45
47
  nodes2D.push(node2D)
46
48
  }
47
49
  node2D.belongsTo.push({
48
- polygonId: polygon.id,
50
+ itemId: polygon.id,
49
51
  index: i,
50
- polygon
52
+ item: polygon,
51
53
  })
52
54
  node2D.id += '_' + polygon.id + '_' + i
53
55
  })
54
56
  })
55
-
57
+ circles.forEach((circle) => {
58
+ const point = circle.center
59
+ let node2D = nodes2D.find(
60
+ (node) =>
61
+ isAlmostSamePoint2D(node, point, mmTolerance) &&
62
+ node.layer == circle.layer
63
+ )
64
+ if (!node2D) {
65
+ node2D = new Point(point.x, point.y, point.z, circle.layer)
66
+ node2D.belongsTo = []
67
+ node2D.id = 'node2D'
68
+ node2D.itemType = 'node2D'
69
+ node2D.layer = circle.layer
70
+ node2D.colorIndex = circle.colorIndex
71
+ nodes2D.push(node2D)
72
+ }
73
+ node2D.belongsTo.push({
74
+ itemId: circle.id,
75
+ item: circle,
76
+ })
77
+ node2D.id += '_' + circle.id
78
+ })
56
79
  //all nodes has been created. now lets update open/selected field
57
80
  nodes2D.forEach((node2D) => {
58
81
  if (node2D.masterHandle) {
59
82
  node2D.masterHandle.open = node2D.belongsTo.some(
60
- (item) => item.index == null || item.polygon.outline[item.index].open
83
+ (item) =>
84
+ item.index == null ||
85
+ (item.item.type === 'polygon' && item.item.outline[item.index].open)
61
86
  )
62
87
  node2D.masterHandle.selected = node2D.belongsTo.every(
63
88
  (item) =>
64
- item.index == null || item.polygon.outline[item.index].selected
89
+ item.index == null ||
90
+ (item.item.type === 'polygon' && item.item.outline[item.index].selected)
65
91
  )
66
92
  node2D.masterHandle.locked = node2D.belongsTo.every(
67
- (item) => item.index == null || item.polygon.outline[item.index].locked
93
+ (item) =>
94
+ item.index == null ||
95
+ (item.item.type === 'polygon' && item.item.outline[item.index].locked)
68
96
  )
69
97
  }
70
98
  })
71
99
  return nodes2D
72
100
  }
73
101
 
74
- export function generateNodes3D(polygons) {
102
+ export function generateNodes3D(polygons, circles) {
75
103
  let nodes3D = []
76
104
  polygons
77
105
  .filter((poly) =>
78
- ['roof', 'obstacle', 'tmpModuleField', 'moduleField'].includes(poly.layer)
106
+ [
107
+ 'roof',
108
+ 'obstacle',
109
+ 'tmpModuleField',
110
+ 'moduleField',
111
+ 'roof_plan_item',
112
+ ].includes(poly.layer)
79
113
  )
80
114
  .forEach((polygon) => {
81
115
  const outline = polygon.outline
@@ -90,30 +124,55 @@ export function generateNodes3D(polygons) {
90
124
  node3D.id = 'node3D'
91
125
  node3D.itemType = 'node3D'
92
126
  node3D.layer = polygon.layer
127
+ node3D.colorIndex = polygon.colorIndex
93
128
  node3D.masterHandle = {
94
129
  id: node3D.id,
95
130
  itemType: 'masterHandle',
96
131
  open: null,
97
- selected: null
132
+ selected: null,
98
133
  }
99
134
  nodes3D.push(node3D)
100
135
  }
101
136
  node3D.belongsTo.push({
102
- polygonId: polygon.id,
137
+ itemId: polygon.id,
103
138
  index: i,
104
- polygon
139
+ item: polygon,
105
140
  })
106
141
  node3D.id += '_' + polygon.id + '_' + i
107
142
  })
108
143
  })
144
+ circles.forEach((circle) => {
145
+ const point = circle.center
146
+ let node3D = nodes3D.find(
147
+ (node) =>
148
+ isAlmostSamePoint3D(node, point, mmTolerance) &&
149
+ node.layer == circle.layer
150
+ )
151
+ if (!node3D) {
152
+ node3D = new Point(point.x, point.y, point.z, circle.layer)
153
+ node3D.belongsTo = []
154
+ node3D.id = 'node3D'
155
+ node3D.itemType = 'node3D'
156
+ node3D.layer = circle.layer
157
+ node3D.colorIndex = circle.colorIndex
158
+ nodes3D.push(node3D)
159
+ }
160
+ node3D.belongsTo.push({
161
+ itemId: circle.id,
162
+ item: circle,
163
+ })
164
+ node3D.id += '_' + circle.id
165
+ })
109
166
  //all nodes has been created. now lets update open/selected field
110
167
  nodes3D.forEach((node3D) => {
111
168
  if (node3D.masterHandle) {
112
169
  node3D.masterHandle.open = node3D.belongsTo.some(
113
- (item) => item.polygon.outline[item.index].open
170
+ (item) =>
171
+ item.item.type === 'polygon' && item.item.outline[item.index].open
114
172
  )
115
173
  node3D.masterHandle.selected = node3D.belongsTo.every(
116
- (item) => item.polygon.outline[item.index].selected
174
+ (item) =>
175
+ item.item.type === 'polygon' && item.item.outline[item.index].selected
117
176
  )
118
177
  }
119
178
  })
@@ -124,7 +183,13 @@ export function generateEdges2D(polygons, splitEdgesIds = []) {
124
183
  let edges2D = []
125
184
  polygons
126
185
  .filter((poly) =>
127
- ['roof', 'obstacle', 'moduleField', 'construction'].includes(poly.layer)
186
+ [
187
+ 'roof',
188
+ 'obstacle',
189
+ 'moduleField',
190
+ 'construction',
191
+ 'roof_plan_item',
192
+ ].includes(poly.layer)
128
193
  )
129
194
  .forEach((polygon) => {
130
195
  const outline = polygon.outline
@@ -152,20 +217,21 @@ export function generateEdges2D(polygons, splitEdgesIds = []) {
152
217
  edge2D.layer = polygon.layer
153
218
  edge2D.selected = false
154
219
  edge2D.itemType = 'edge2D'
220
+ edge2D.colorIndex = polygon.colorIndex
155
221
  edges2D.push(edge2D)
156
222
  }
157
223
  //add BelongToPolygon
158
224
  edge2D.belongsTo.push({
159
- polygonId: polygon.id,
225
+ itemId: polygon.id,
160
226
  index: i,
161
- polygon
227
+ item: polygon,
162
228
  })
163
229
  if (polygon.roofs && polygon.roofs.length > 0) {
164
230
  polygon.roofs.forEach((supportRoof) => {
165
231
  edge2D.belongsTo.push({
166
- polygonId: supportRoof.id,
232
+ itemId: supportRoof.id,
167
233
  index: null,
168
- polygon: supportRoof
234
+ item: supportRoof,
169
235
  })
170
236
  })
171
237
  }
@@ -183,8 +249,12 @@ export function generateEdges2D(polygons, splitEdgesIds = []) {
183
249
  if (index == 0) {
184
250
  return true
185
251
  }
186
- const outline0 = array[0].polygon.outline
187
- const outline1 = item.polygon.outline
252
+ // Only process polygon items (circles don't have outlines)
253
+ if (array[0].item.type !== 'polygon' || item.item.type !== 'polygon') {
254
+ return true
255
+ }
256
+ const outline0 = array[0].item.outline
257
+ const outline1 = item.item.outline
188
258
  let point0, point1, nextPoint0, nextPoint1
189
259
  let pointNull = []
190
260
  if (array[0].index !== null) {
@@ -194,13 +264,13 @@ export function generateEdges2D(polygons, splitEdgesIds = []) {
194
264
  pointNull.push(0)
195
265
  point0 = verticalProjectionOnPlane(
196
266
  outline1[item.index],
197
- array[0].polygon.normalVector,
198
- array[0].polygon.flatOutline[0]
267
+ array[0].item.normalVector,
268
+ array[0].item.flatOutline[0]
199
269
  )
200
270
  nextPoint0 = verticalProjectionOnPlane(
201
271
  outline1[(item.index + 1) % outline1.length],
202
- array[0].polygon.normalVector,
203
- array[0].polygon.flatOutline[0]
272
+ array[0].item.normalVector,
273
+ array[0].item.flatOutline[0]
204
274
  )
205
275
  }
206
276
  if (item.index !== null) {
@@ -210,13 +280,13 @@ export function generateEdges2D(polygons, splitEdgesIds = []) {
210
280
  pointNull.push(1)
211
281
  point1 = verticalProjectionOnPlane(
212
282
  outline0[array[0].index],
213
- item.polygon.normalVector,
214
- item.polygon.flatOutline[0]
283
+ item.item.normalVector,
284
+ item.item.flatOutline[0]
215
285
  )
216
286
  nextPoint1 = verticalProjectionOnPlane(
217
287
  outline0[(array[0].index + 1) % outline0.length],
218
- item.polygon.normalVector,
219
- item.polygon.flatOutline[0]
288
+ item.item.normalVector,
289
+ item.item.flatOutline[0]
220
290
  )
221
291
  }
222
292
  const isSameSeg = isAlmostSameSegment3D(
@@ -237,7 +307,13 @@ export function generateEdges3D(polygons) {
237
307
  let edges3D = []
238
308
  polygons
239
309
  .filter((poly) =>
240
- ['roof', 'obstacle', 'moduleField', 'construction'].includes(poly.layer)
310
+ [
311
+ 'roof',
312
+ 'obstacle',
313
+ 'moduleField',
314
+ 'construction',
315
+ 'roof_plan_item',
316
+ ].includes(poly.layer)
241
317
  )
242
318
  .forEach((polygon) => {
243
319
  const outline = polygon.outline
@@ -264,14 +340,15 @@ export function generateEdges3D(polygons) {
264
340
  edge3D.belongsTo = []
265
341
  edge3D.layer = polygon.layer
266
342
  edge3D.selected = false
343
+ edge3D.colorIndex = polygon.colorIndex
267
344
  edge3D.itemType = 'edge3D'
268
345
  edges3D.push(edge3D)
269
346
  }
270
347
  //add BelongToPolygon
271
348
  edge3D.belongsTo.push({
272
- polygonId: polygon.id,
349
+ itemId: polygon.id,
273
350
  index: i,
274
- polygon
351
+ item: polygon,
275
352
  })
276
353
  edge3D.id += '_' + polygon.id + '_' + i
277
354
  })
@@ -72,6 +72,7 @@ export function updateComputedGeometryPolygon(polygon) {
72
72
  'roof',
73
73
  'obstacle',
74
74
  'moduleField',
75
+ 'roof_plan_item'
75
76
  ].includes(polygon.layer)
76
77
  ) {
77
78
  return polygon
@@ -24,15 +24,15 @@ export function generateGraph(polygons, { includingRoofOnRoofs = true } = {}) {
24
24
  }
25
25
  //add BelongToPolygon
26
26
  edge2D.belongsTo.push({
27
- polygonId: polygon.id,
27
+ itemId: polygon.id,
28
28
  index: i,
29
- polygon
29
+ item: polygon
30
30
  })
31
31
  if (includingRoofOnRoofs && polygon.roofs && polygon.roofs.length == 1) {
32
32
  edge2D.belongsTo.push({
33
- polygonId: polygon.roofs[0].id,
33
+ itemId: polygon.roofs[0].id,
34
34
  index: null,
35
- polygon: polygon.roofs[0]
35
+ item: polygon.roofs[0]
36
36
  })
37
37
  }
38
38
  })
@@ -40,7 +40,7 @@ export function generateGraph(polygons, { includingRoofOnRoofs = true } = {}) {
40
40
  for (let k in edges2D) {
41
41
  let edge = edges2D[k]
42
42
  if (edge.belongsTo.length == 2) {
43
- g.addEdge(edge.belongsTo[0].polygonId, edge.belongsTo[1].polygonId)
43
+ g.addEdge(edge.belongsTo[0].itemId, edge.belongsTo[1].itemId)
44
44
  }
45
45
  }
46
46
  return g