@eturnity/eturnity_maths 7.20.0 → 7.24.1

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.
@@ -1,579 +1,643 @@
1
1
  import { PlanarFaceTree } from 'planar-face-discovery'
2
2
  import {
3
- substractVector,
4
- addVector,
5
- multiplyVector,
6
- dotProduct,
7
- vectorLength
3
+ substractVector,
4
+ addVector,
5
+ multiplyVector,
6
+ dotProduct,
7
+ vectorLength
8
8
  } from './vector'
9
9
  import {
10
- isAlmostSamePoint2D,
11
- getPointOnLine,
12
- getDistanceBetweenPoints,
13
- isPointBetweenSegment,
14
- isInsidePolygon,
15
- verticalProjectionOnPlane,
16
- polygonsHaveSame2DOutline
10
+ isAlmostSamePoint2D,
11
+ getPointOnLine,
12
+ getDistanceBetweenPoints,
13
+ isPointBetweenSegment,
14
+ isInsidePolygon,
15
+ verticalProjectionOnPlane,
16
+ polygonsHaveSame2DOutline,
17
+ calculateArea
17
18
  } from './geometry'
18
19
  import { groupBy } from 'lodash'
19
20
  import { Polygon } from './objects/Polygon'
20
21
  import { defaultBaseHeight, mmTolerance } from './config'
22
+ import { intersectOutlines } from './intersectionPolygon'
21
23
 
22
- export function mergePolygons(polygonIdsToMerge, edges, layer) {
23
- const outsideEdge = edges.filter((e) => e.belongsTo.length == 1)
24
- const insideEdge = edges.filter((e) => e.belongsTo.length == 2)
25
- const insideEdgeToKeep = insideEdge.filter((e) => {
26
- return (
27
- !polygonIdsToMerge.includes(e.belongsTo[0].polygonId) ||
24
+ export function mergePolygons(polygonIdsToMerge, edges, layer, polygons) {
25
+ console.log('mergePolygon')
26
+ const outsideEdge = edges.filter((e) => e.belongsTo.length == 1)
27
+ const insideEdge = edges.filter((e) => e.belongsTo.length == 2)
28
+ const insideEdgeToKeep = insideEdge.filter((e) => {
29
+ return (
30
+ !polygonIdsToMerge.includes(e.belongsTo[0].polygonId) ||
28
31
  !polygonIdsToMerge.includes(e.belongsTo[1].polygonId)
29
- )
30
- })
31
- const newEdges = [...outsideEdge, ...insideEdgeToKeep]
32
- //nodeList:
33
- let dirtyNodeList = []
34
- newEdges.forEach((edge) => {
35
- dirtyNodeList.push(edge.outline[0])
36
- dirtyNodeList.push(edge.outline[1])
37
- })
38
- const nodeList = []
39
- for (let node of dirtyNodeList) {
40
- const nodeFound = nodeList.find((n) => {
41
- return n && isAlmostSamePoint2D(n, node, mmTolerance)
42
- })
43
- if (!nodeFound) {
44
- nodeList.push(node)
45
- }
46
- }
47
- //edgeList (just in term of indexes)
48
- const edgeList = getEdgeListSimple(nodeList, newEdges)
49
- const outlineList = getOutlineList(nodeList, edgeList)
32
+ )
33
+ })
34
+ const newEdges = [...outsideEdge, ...insideEdgeToKeep]
35
+ //nodeList:
36
+ let dirtyNodeList = []
37
+ newEdges.forEach((edge) => {
38
+ dirtyNodeList.push(edge.outline[0])
39
+ dirtyNodeList.push(edge.outline[1])
40
+ })
41
+ const nodeList = []
42
+ for (let node of dirtyNodeList) {
43
+ const nodeFound = nodeList.find((n) => {
44
+ return n && isAlmostSamePoint2D(n, node, mmTolerance)
45
+ })
46
+ if (!nodeFound) {
47
+ nodeList.push(node)
48
+ }
49
+ }
50
+ //edgeList (just in term of indexes)
51
+ const edgeList = getEdgeListSimple(nodeList, newEdges)
52
+ const outlineList = getOutlineList(nodeList, edgeList)
50
53
 
51
- const polygonList = []
52
- for (let k = 0; k < outlineList.length; k++) {
53
- polygonList.push(new Polygon(outlineList[k], layer))
54
- }
55
- return polygonList
54
+ const polygonList = []
55
+ for (let k = 0; k < outlineList.length; k++) {
56
+ //check if roof with same outline already exist:
57
+ let polygon = polygons.find((polygon) =>
58
+ polygonsHaveSame2DOutline(outlineList[k], polygon.outline)
59
+ )
60
+ if (!polygon) {
61
+ let newPolygon = new Polygon(outlineList[k], layer)
62
+ polygonList.push(newPolygon)
63
+ }else{
64
+ polygonList.push(polygon)
65
+ }
66
+ }
67
+ return polygonList
56
68
  }
57
69
 
58
70
  export async function trySplitPolygons(
59
- constructionPolyline,
60
- edges,
61
- roofs,
62
- isClosedContructionPolyline
71
+ constructionPolyline,
72
+ edges,
73
+ roofs,
74
+ isClosedContructionPolyline
63
75
  ) {
64
- let result=roofs
65
- try {
66
- result = splitPolygons(
67
- constructionPolyline,
68
- edges,
69
- roofs,
70
- isClosedContructionPolyline
71
- )
72
- } catch (error) {
73
- console.error('An error occurred:', error);
74
- } finally {
75
- return result
76
- }
76
+ let result = roofs
77
+ try {
78
+ result = splitPolygons(
79
+ constructionPolyline,
80
+ edges,
81
+ roofs,
82
+ isClosedContructionPolyline
83
+ )
84
+ } catch (error) {
85
+ console.error('An error occurred:', error)
86
+ } finally {
87
+ return result
88
+ }
77
89
  }
78
90
 
79
91
  export async function splitPolygons(
80
- constructionPolyline,
81
- edges,
82
- roofs,
83
- isClosedContructionPolyline
92
+ constructionPolyline,
93
+ edges,
94
+ roofs,
95
+ isClosedContructionPolyline
84
96
  ) {
85
- constructionPolyline.outline.forEach((p) => {
86
- let supportRoof = roofs.find((roof) => isInsidePolygon(p, roof.outline))
87
- if (supportRoof) {
88
- p.z = verticalProjectionOnPlane(
89
- p,
90
- supportRoof.normalVector,
91
- supportRoof.flatOutline[0]
92
- ).z
93
- } else {
94
- p.z = defaultBaseHeight
95
- }
96
- })
97
-
98
- isClosedContructionPolyline = isClosedContructionPolyline ? 1 : 0
99
- //1. find intersection between constructionPolyline and edges
100
- const roofEdge = edges.filter((e) => e.layer == 'roof')
101
- let intersections = getIntersections(constructionPolyline, roofEdge,isClosedContructionPolyline)
102
- //2. gather all nodes+intersection in a list[[x,y],[x,y]]
103
- const nodeList = getNodeList(intersections, roofEdge, constructionPolyline)
104
- //3. generate all edges not cut, those cut and those from construction polyline
105
- const edgeList = getEdgeList(
106
- nodeList,
107
- intersections,
108
- roofEdge,
109
- constructionPolyline,
110
- isClosedContructionPolyline
111
- )
112
- //4. run planar-face-discovery to get all cycles and rebuild our polygons
113
- const outlineList = getOutlineList(nodeList, edgeList, roofs)
114
- const polygonList = []
115
- for (let k = 0; k < outlineList.length; k++) {
116
- //check if roof with same outline already exist:
117
- let roof = roofs.find((roof) =>
118
- polygonsHaveSame2DOutline(outlineList[k], roof.outline)
119
- )
120
- let newRoof=new Polygon(outlineList[k], 'roof')
121
- if (roof) {
122
- newRoof.id=roof.id
123
- }
124
- polygonList.push(newRoof)
125
- }
126
- return polygonList
97
+ console.log('splitPolygon')
98
+ constructionPolyline.outline.forEach((p) => {
99
+ let supportRoof = roofs.find((roof) => isInsidePolygon(p, roof.outline))
100
+ if (supportRoof) {
101
+ p.z = verticalProjectionOnPlane(
102
+ p,
103
+ supportRoof.normalVector,
104
+ supportRoof.flatOutline[0]
105
+ ).z
106
+ } else {
107
+ p.z = defaultBaseHeight
108
+ }
109
+ })
110
+
111
+ isClosedContructionPolyline = isClosedContructionPolyline ? 1 : 0
112
+ //1. find intersection between constructionPolyline and edges
113
+ const roofEdge = edges.filter((e) => e.layer == 'roof')
114
+ let intersections = getIntersections(
115
+ constructionPolyline,
116
+ roofEdge,
117
+ isClosedContructionPolyline
118
+ )
119
+ //2. gather all nodes+intersection in a list[[x,y],[x,y]]
120
+ const nodeList = getNodeList(intersections, roofEdge, constructionPolyline)
121
+ //3. generate all edges not cut, those cut and those from construction polyline
122
+ const edgeList = getEdgeList(
123
+ nodeList,
124
+ intersections,
125
+ roofEdge,
126
+ constructionPolyline,
127
+ isClosedContructionPolyline
128
+ )
129
+ //4. run planar-face-discovery to get all cycles and rebuild our polygons
130
+ const outlineList = getOutlineList(nodeList, edgeList, roofs)
131
+ const polygonList = []
132
+ for (let k = 0; k < outlineList.length; k++) {
133
+ //check if roof with same outline already exist:
134
+ let roof = roofs.find((roof) =>
135
+ polygonsHaveSame2DOutline(outlineList[k], roof.outline)
136
+ )
137
+ if (roof) {
138
+ polygonList.push(roof)
139
+ } else {
140
+ let newRoof = new Polygon(outlineList[k], 'roof')
141
+ polygonList.push(newRoof)
142
+ }
143
+ }
144
+ return polygonList
127
145
  }
128
146
 
129
- export function getIntersections(constructionPolyline, edges, isClosedContructionPolyline=0) {
130
- let intersections = []
131
- const polylineLength = constructionPolyline.outline.length
132
- for (let k = 0; k < polylineLength - 1 + isClosedContructionPolyline; k++) {
133
- const segStart = constructionPolyline.outline[k]
134
- const segEnd = constructionPolyline.outline[(k + 1) % polylineLength]
135
- for (let j = 0; j < edges.length; j++) {
136
- const edgeStart = edges[j].outline[0]
137
- const edgeEnd = edges[j].outline[1]
138
- const inters = getIntersectionSegmentWithParams(segStart, segEnd, edgeStart, edgeEnd)
139
- inters.forEach((inter) => {
140
- intersections.push({
141
- polylineIndex: k,
142
- edgeIndex: j,
143
- polylineParam: inter.param1,
144
- edgeParam: inter.param2,
145
- point: inter.point
146
- })
147
- })
148
- }
149
- }
150
- intersections = intersections.filter(
151
- (inter) =>
152
- inter.polylineParam >= 0 &&
147
+ export function getIntersections(
148
+ constructionPolyline,
149
+ edges,
150
+ isClosedContructionPolyline = 0
151
+ ) {
152
+ let intersections = []
153
+ const polylineLength = constructionPolyline.outline.length
154
+ for (let k = 0; k < polylineLength - 1 + isClosedContructionPolyline; k++) {
155
+ const segStart = constructionPolyline.outline[k]
156
+ const segEnd = constructionPolyline.outline[(k + 1) % polylineLength]
157
+ for (let j = 0; j < edges.length; j++) {
158
+ const edgeStart = edges[j].outline[0]
159
+ const edgeEnd = edges[j].outline[1]
160
+ const inters = getIntersectionSegmentWithParams(
161
+ segStart,
162
+ segEnd,
163
+ edgeStart,
164
+ edgeEnd
165
+ )
166
+ inters.forEach((inter) => {
167
+ intersections.push({
168
+ polylineIndex: k,
169
+ edgeIndex: j,
170
+ polylineParam: inter.param1,
171
+ edgeParam: inter.param2,
172
+ point: inter.point
173
+ })
174
+ })
175
+ }
176
+ }
177
+ intersections = intersections.filter(
178
+ (inter) =>
179
+ inter.polylineParam >= 0 &&
153
180
  inter.polylineParam <= 1 &&
154
181
  inter.edgeParam >= 0 &&
155
182
  inter.edgeParam <= 1
156
- )
157
- return intersections
183
+ )
184
+ return intersections
158
185
  }
159
186
 
160
187
  export function getNodeList(intersections, edges, constructionPolyline) {
161
- let nodeList = []
162
- edges.forEach((edge) => {
163
- nodeList.push(edge.outline[0])
164
- nodeList.push(edge.outline[1])
165
- })
188
+ let nodeList = []
189
+ edges.forEach((edge) => {
190
+ nodeList.push(edge.outline[0])
191
+ nodeList.push(edge.outline[1])
192
+ })
166
193
 
167
- constructionPolyline.outline.forEach((p) => {
168
- const node = nodeList.find((n) => {
169
- return isAlmostSamePoint2D(p, n, mmTolerance)
170
- })
171
- if (!node) {
172
- nodeList.push(p)
173
- }
174
- })
175
- const polylineLength = constructionPolyline.outline.length
176
- const polylineStart = nodeList.find((p) => {
177
- return isAlmostSamePoint2D(p, constructionPolyline.outline[0], mmTolerance)
178
- })
179
- const polylineEnd = nodeList.find((p) =>
180
- isAlmostSamePoint2D(
181
- p,
182
- constructionPolyline.outline[polylineLength - 1],
183
- mmTolerance
184
- )
185
- )
186
- constructionPolyline.outline.forEach((p, index) => {
187
- if (p.z == defaultBaseHeight || !p.z) {
188
- p.z =
194
+ constructionPolyline.outline.forEach((p) => {
195
+ const node = nodeList.find((n) => {
196
+ return isAlmostSamePoint2D(p, n, mmTolerance)
197
+ })
198
+ if (!node) {
199
+ nodeList.push(p)
200
+ }
201
+ })
202
+ const polylineLength = constructionPolyline.outline.length
203
+ const polylineStart = nodeList.find((p) => {
204
+ return isAlmostSamePoint2D(p, constructionPolyline.outline[0], mmTolerance)
205
+ })
206
+ const polylineEnd = nodeList.find((p) =>
207
+ isAlmostSamePoint2D(
208
+ p,
209
+ constructionPolyline.outline[polylineLength - 1],
210
+ mmTolerance
211
+ )
212
+ )
213
+ constructionPolyline.outline.forEach((p, index) => {
214
+ if (p.z == defaultBaseHeight || !p.z) {
215
+ p.z =
189
216
  polylineStart.z +
190
217
  (index / (polylineLength - 1)) * (polylineEnd.z - polylineStart.z)
191
- }
192
- nodeList.push(p)
193
- })
194
- intersections.forEach((inter) => {
195
- nodeList.push(inter.point)
196
- })
197
- const nodeListClean = []
198
- for (let k = 0; k < nodeList.length; k++) {
199
- const node = nodeList[k]
200
- const newNode = nodeListClean.find((n) => {
201
- return n && isAlmostSamePoint2D(n, node, mmTolerance)
202
- })
203
- if (!newNode) {
204
- nodeListClean.push(node)
205
- }
206
- }
207
- return nodeListClean
218
+ }
219
+ nodeList.push(p)
220
+ })
221
+ intersections.forEach((inter) => {
222
+ const edge = edges[inter.edgeIndex]
223
+ inter.point.z =
224
+ edge.outline[0].z +
225
+ inter.edgeParam * (edge.outline[1].z - edge.outline[0].z)
226
+ nodeList.push(inter.point)
227
+ })
228
+ const nodeListClean = []
229
+ for (let k = 0; k < nodeList.length; k++) {
230
+ const node = nodeList[k]
231
+ const newNode = nodeListClean.find((n) => {
232
+ return n && isAlmostSamePoint2D(n, node, mmTolerance)
233
+ })
234
+ if (!newNode) {
235
+ nodeListClean.push(node)
236
+ }
237
+ }
238
+ return nodeListClean
208
239
  }
209
240
 
210
241
  function getEdgeListSimple(nodeList, edges) {
211
- //create a list of edge in term of node Index from nodeList
212
- const edgeList = []
213
- for (let index in edges) {
214
- const node_0_index = nodeList.findIndex((n) =>
215
- isAlmostSamePoint2D(edges[index].outline[0], n, mmTolerance)
216
- )
217
- const node_1_index = nodeList.findIndex((n) =>
218
- isAlmostSamePoint2D(edges[index].outline[1], n, mmTolerance)
219
- )
220
- edgeList.push([node_0_index, node_1_index])
221
- }
242
+ //create a list of edge in term of node Index from nodeList
243
+ const edgeList = []
244
+ for (let index in edges) {
245
+ const node_0_index = nodeList.findIndex((n) =>
246
+ isAlmostSamePoint2D(edges[index].outline[0], n, mmTolerance)
247
+ )
248
+ const node_1_index = nodeList.findIndex((n) =>
249
+ isAlmostSamePoint2D(edges[index].outline[1], n, mmTolerance)
250
+ )
251
+ edgeList.push([node_0_index, node_1_index])
252
+ }
222
253
 
223
- const cleanEdgeList = []
224
- edgeList.forEach((edge) => {
225
- const cleanEdge = cleanEdgeList.find(
226
- (cleanEdge) => edge[0] == cleanEdge[0] && edge[1] == cleanEdge[1]
227
- )
228
- if (!cleanEdge && edge[0] != edge[1]) {
229
- cleanEdgeList.push(edge)
230
- }
231
- })
232
- return cleanEdgeList
254
+ const cleanEdgeList = []
255
+ edgeList.forEach((edge) => {
256
+ const cleanEdge = cleanEdgeList.find(
257
+ (cleanEdge) => edge[0] == cleanEdge[0] && edge[1] == cleanEdge[1]
258
+ )
259
+ if (!cleanEdge && edge[0] != edge[1]) {
260
+ cleanEdgeList.push(edge)
261
+ }
262
+ })
263
+ return cleanEdgeList
233
264
  }
234
265
 
235
266
  export function getEdgeList(
236
- nodeList,
237
- intersections,
238
- edges,
239
- constructionPolyline,
240
- isClosedContructionPolyline
267
+ nodeList,
268
+ intersections,
269
+ edges,
270
+ constructionPolyline,
271
+ isClosedContructionPolyline
241
272
  ) {
242
- //to find all edges, edges untouched and untouched construction line are first added
243
- //then we sort intersection according to their ratio within an edge to add cut edges,
244
- //same operation for construction line.
245
- const edgeList = []
246
- let intersectionByEdge = groupBy(intersections, (inter) => {
247
- return inter.edgeIndex
248
- })
249
- let intersectionByPolyline = groupBy(intersections, (inter) => {
250
- return inter.polylineIndex
251
- })
252
- intersectionByEdge = Object.values(intersectionByEdge)
253
- intersectionByPolyline = Object.values(intersectionByPolyline)
254
- intersectionByEdge.forEach((intersections) =>
255
- intersections.sort((a, b) => {
256
- return a.edgeParam - b.edgeParam
257
- })
258
- )
259
- intersectionByPolyline.forEach((intersections) =>
260
- intersections.sort((a, b) => {
261
- return a.polylineParam - b.polylineParam
262
- })
263
- )
264
- //get used and untouched edge from polyline and edges
273
+ //to find all edges, edges untouched and untouched construction line are first added
274
+ //then we sort intersection according to their ratio within an edge to add cut edges,
275
+ //same operation for construction line.
276
+ const edgeList = []
277
+ let intersectionByEdge = groupBy(intersections, (inter) => {
278
+ return inter.edgeIndex
279
+ })
280
+ let intersectionByPolyline = groupBy(intersections, (inter) => {
281
+ return inter.polylineIndex
282
+ })
283
+ intersectionByEdge = Object.values(intersectionByEdge)
284
+ intersectionByPolyline = Object.values(intersectionByPolyline)
285
+ intersectionByEdge.forEach((intersections) =>
286
+ intersections.sort((a, b) => {
287
+ return a.edgeParam - b.edgeParam
288
+ })
289
+ )
290
+ intersectionByPolyline.forEach((intersections) =>
291
+ intersections.sort((a, b) => {
292
+ return a.polylineParam - b.polylineParam
293
+ })
294
+ )
295
+ //get used and untouched edge from polyline and edges
265
296
 
266
- const polylineUsedIndexes = intersectionByPolyline.map(
267
- (i) => i[0].polylineIndex
268
- )
269
- const edgeUsedIndexes = intersectionByEdge.map((i) => i[0].edgeIndex)
297
+ const polylineUsedIndexes = intersectionByPolyline.map(
298
+ (i) => i[0].polylineIndex
299
+ )
300
+ const edgeUsedIndexes = intersectionByEdge.map((i) => i[0].edgeIndex)
270
301
 
271
- const untouchedEdgeIndexes = [...Array(edges.length).keys()].filter(
272
- (k) => !edgeUsedIndexes.includes(k)
273
- )
302
+ const untouchedEdgeIndexes = [...Array(edges.length).keys()].filter(
303
+ (k) => !edgeUsedIndexes.includes(k)
304
+ )
274
305
 
275
- const untouchedPolylineIndexes = [
276
- ...Array(
277
- constructionPolyline.outline.length - 1 + isClosedContructionPolyline
278
- ).keys()
279
- ].filter((k) => !polylineUsedIndexes.includes(k))
306
+ const untouchedPolylineIndexes = [
307
+ ...Array(
308
+ constructionPolyline.outline.length - 1 + isClosedContructionPolyline
309
+ ).keys()
310
+ ].filter((k) => !polylineUsedIndexes.includes(k))
280
311
 
281
- //get untouchEdge edge
282
- untouchedEdgeIndexes.forEach((index) => {
283
- const node_0_index = nodeList.findIndex((n) =>
284
- isAlmostSamePoint2D(edges[index].outline[0], n, mmTolerance)
285
- )
286
- const node_1_index = nodeList.findIndex((n) =>
287
- isAlmostSamePoint2D(edges[index].outline[1], n, mmTolerance)
288
- )
289
- edgeList.push([node_0_index, node_1_index])
290
- })
291
- //get untouchPolyline edge
292
- untouchedPolylineIndexes.forEach((index) => {
293
- const length = constructionPolyline.outline.length
294
- const node_0_index = nodeList.findIndex((n) =>
295
- isAlmostSamePoint2D(constructionPolyline.outline[index], n, mmTolerance)
296
- )
297
- const node_1_index = nodeList.findIndex((n) =>
298
- isAlmostSamePoint2D(
299
- constructionPolyline.outline[(index + 1) % length],
300
- n,
301
- mmTolerance
302
- )
303
- )
304
- edgeList.push([node_0_index, node_1_index])
305
- })
306
- //get touchedEdge edge
307
- intersectionByEdge.forEach((intersectionOnEdge) => {
308
- const length = intersectionOnEdge.length
309
- const node_0_index_start = nodeList.findIndex((n) =>
310
- isAlmostSamePoint2D(
311
- edges[intersectionOnEdge[0].edgeIndex].outline[0],
312
- n,
313
- mmTolerance
314
- )
315
- )
316
- const node_1_index_start = nodeList.findIndex((n) =>
317
- isAlmostSamePoint2D(intersectionOnEdge[0].point, n, mmTolerance)
318
- )
319
- edgeList.push([node_0_index_start, node_1_index_start])
312
+ //get untouchEdge edge
313
+ untouchedEdgeIndexes.forEach((index) => {
314
+ const node_0_index = nodeList.findIndex((n) =>
315
+ isAlmostSamePoint2D(edges[index].outline[0], n, mmTolerance)
316
+ )
317
+ const node_1_index = nodeList.findIndex((n) =>
318
+ isAlmostSamePoint2D(edges[index].outline[1], n, mmTolerance)
319
+ )
320
+ edgeList.push([node_0_index, node_1_index])
321
+ })
322
+ //get untouchPolyline edge
323
+ untouchedPolylineIndexes.forEach((index) => {
324
+ const length = constructionPolyline.outline.length
325
+ const node_0_index = nodeList.findIndex((n) =>
326
+ isAlmostSamePoint2D(constructionPolyline.outline[index], n, mmTolerance)
327
+ )
328
+ const node_1_index = nodeList.findIndex((n) =>
329
+ isAlmostSamePoint2D(
330
+ constructionPolyline.outline[(index + 1) % length],
331
+ n,
332
+ mmTolerance
333
+ )
334
+ )
335
+ edgeList.push([node_0_index, node_1_index])
336
+ })
337
+ //get touchedEdge edge
338
+ intersectionByEdge.forEach((intersectionOnEdge) => {
339
+ const length = intersectionOnEdge.length
340
+ const node_0_index_start = nodeList.findIndex((n) =>
341
+ isAlmostSamePoint2D(
342
+ edges[intersectionOnEdge[0].edgeIndex].outline[0],
343
+ n,
344
+ mmTolerance
345
+ )
346
+ )
347
+ const node_1_index_start = nodeList.findIndex((n) =>
348
+ isAlmostSamePoint2D(intersectionOnEdge[0].point, n, mmTolerance)
349
+ )
350
+ edgeList.push([node_0_index_start, node_1_index_start])
320
351
 
321
- for (let k = 0; k < length - 1; k++) {
322
- const node_0_index = nodeList.findIndex((n) =>
323
- isAlmostSamePoint2D(intersectionOnEdge[k].point, n, mmTolerance)
324
- )
325
- const node_1_index = nodeList.findIndex((n) =>
326
- isAlmostSamePoint2D(
327
- intersectionOnEdge[(k + 1) % length].point,
328
- n,
329
- mmTolerance
330
- )
331
- )
332
- edgeList.push([node_0_index, node_1_index])
333
- }
352
+ for (let k = 0; k < length - 1; k++) {
353
+ const node_0_index = nodeList.findIndex((n) =>
354
+ isAlmostSamePoint2D(intersectionOnEdge[k].point, n, mmTolerance)
355
+ )
356
+ const node_1_index = nodeList.findIndex((n) =>
357
+ isAlmostSamePoint2D(
358
+ intersectionOnEdge[(k + 1) % length].point,
359
+ n,
360
+ mmTolerance
361
+ )
362
+ )
363
+ edgeList.push([node_0_index, node_1_index])
364
+ }
334
365
 
335
- const node_0_index_end = nodeList.findIndex((n) =>
336
- isAlmostSamePoint2D(intersectionOnEdge[length - 1].point, n, mmTolerance)
337
- )
338
- const node_1_index_end = nodeList.findIndex((n) =>
339
- isAlmostSamePoint2D(
340
- edges[intersectionOnEdge[0].edgeIndex].outline[1],
341
- n,
342
- mmTolerance
343
- )
344
- )
345
- edgeList.push([node_0_index_end, node_1_index_end])
346
- })
347
- //get touchedPolyline edge
348
- intersectionByPolyline.forEach((intersectionOnPolyline) => {
349
- const length = intersectionOnPolyline.length
350
- const lengthPolyline = constructionPolyline.outline.length
351
- const node_0_index_start = nodeList.findIndex((n) =>
352
- isAlmostSamePoint2D(
353
- constructionPolyline.outline[intersectionOnPolyline[0].polylineIndex],
354
- n,
355
- mmTolerance
356
- )
357
- )
358
- const node_1_index_start = nodeList.findIndex((n) =>
359
- isAlmostSamePoint2D(intersectionOnPolyline[0].point, n, mmTolerance)
360
- )
361
- edgeList.push([node_0_index_start, node_1_index_start])
366
+ const node_0_index_end = nodeList.findIndex((n) =>
367
+ isAlmostSamePoint2D(intersectionOnEdge[length - 1].point, n, mmTolerance)
368
+ )
369
+ const node_1_index_end = nodeList.findIndex((n) =>
370
+ isAlmostSamePoint2D(
371
+ edges[intersectionOnEdge[0].edgeIndex].outline[1],
372
+ n,
373
+ mmTolerance
374
+ )
375
+ )
376
+ edgeList.push([node_0_index_end, node_1_index_end])
377
+ })
378
+ //get touchedPolyline edge
379
+ intersectionByPolyline.forEach((intersectionOnPolyline) => {
380
+ const length = intersectionOnPolyline.length
381
+ const lengthPolyline = constructionPolyline.outline.length
382
+ const node_0_index_start = nodeList.findIndex((n) =>
383
+ isAlmostSamePoint2D(
384
+ constructionPolyline.outline[intersectionOnPolyline[0].polylineIndex],
385
+ n,
386
+ mmTolerance
387
+ )
388
+ )
389
+ const node_1_index_start = nodeList.findIndex((n) =>
390
+ isAlmostSamePoint2D(intersectionOnPolyline[0].point, n, mmTolerance)
391
+ )
392
+ edgeList.push([node_0_index_start, node_1_index_start])
362
393
 
363
- for (let k = 0; k < length - 1; k++) {
364
- const node_0_index = nodeList.findIndex((n) =>
365
- isAlmostSamePoint2D(intersectionOnPolyline[k].point, n, mmTolerance)
366
- )
367
- const node_1_index = nodeList.findIndex((n) =>
368
- isAlmostSamePoint2D(
369
- intersectionOnPolyline[(k + 1) % length].point,
370
- n,
371
- mmTolerance
372
- )
373
- )
374
- edgeList.push([node_0_index, node_1_index])
375
- }
394
+ for (let k = 0; k < length - 1; k++) {
395
+ const node_0_index = nodeList.findIndex((n) =>
396
+ isAlmostSamePoint2D(intersectionOnPolyline[k].point, n, mmTolerance)
397
+ )
398
+ const node_1_index = nodeList.findIndex((n) =>
399
+ isAlmostSamePoint2D(
400
+ intersectionOnPolyline[(k + 1) % length].point,
401
+ n,
402
+ mmTolerance
403
+ )
404
+ )
405
+ edgeList.push([node_0_index, node_1_index])
406
+ }
376
407
 
377
- const node_0_index_end = nodeList.findIndex((n) =>
378
- isAlmostSamePoint2D(
379
- intersectionOnPolyline[length - 1].point,
380
- n,
381
- mmTolerance
382
- )
383
- )
384
- const node_1_index_end = nodeList.findIndex((n) =>
385
- isAlmostSamePoint2D(
386
- constructionPolyline.outline[
387
- (intersectionOnPolyline[0].polylineIndex + 1)%lengthPolyline
388
- ],
389
- n,
390
- mmTolerance
391
- )
392
- )
393
- edgeList.push([node_0_index_end, node_1_index_end])
394
- })
408
+ const node_0_index_end = nodeList.findIndex((n) =>
409
+ isAlmostSamePoint2D(
410
+ intersectionOnPolyline[length - 1].point,
411
+ n,
412
+ mmTolerance
413
+ )
414
+ )
415
+ const node_1_index_end = nodeList.findIndex((n) =>
416
+ isAlmostSamePoint2D(
417
+ constructionPolyline.outline[
418
+ (intersectionOnPolyline[0].polylineIndex + 1) % lengthPolyline
419
+ ],
420
+ n,
421
+ mmTolerance
422
+ )
423
+ )
424
+ edgeList.push([node_0_index_end, node_1_index_end])
425
+ })
395
426
 
396
- const cleanEdgeList = []
397
- edgeList.forEach((edge) => {
398
- const cleanEdge = cleanEdgeList.find(
399
- (cleanEdge) => edge[0] == cleanEdge[0] && edge[1] == cleanEdge[1]
400
- )
401
- if (!cleanEdge && edge[0] != edge[1]) {
402
- cleanEdgeList.push(edge)
403
- }
404
- })
427
+ const cleanEdgeList = []
428
+ edgeList.forEach((edge) => {
429
+ const cleanEdge = cleanEdgeList.find(
430
+ (cleanEdge) => edge[0] == cleanEdge[0] && edge[1] == cleanEdge[1]
431
+ )
432
+ if (!cleanEdge && edge[0] != edge[1]) {
433
+ cleanEdgeList.push(edge)
434
+ }
435
+ })
405
436
 
406
- return cleanEdgeList
437
+ return cleanEdgeList
407
438
  }
408
439
  function forestRecursion(cycleTree) {
409
- if (cycleTree.children.length > 0) {
410
- let cycles = cycleTree.children.reduce(function (done, curr) {
411
- done.push(...forestRecursion(curr))
412
- return done
413
- }, [])
414
- if (cycleTree.cycle.length) {
415
- cycles.push(cycleTree.cycle)
416
- }
417
- return cycles
418
- } else {
419
- return [cycleTree.cycle]
420
- }
440
+ if (cycleTree.children.length > 0) {
441
+ let cycles = cycleTree.children.reduce(function (done, curr) {
442
+ done.push(...forestRecursion(curr))
443
+ return done
444
+ }, [])
445
+ if (cycleTree.cycle.length) {
446
+ cycles.push(cycleTree.cycle)
447
+ }
448
+ return cycles
449
+ } else {
450
+ return [cycleTree.cycle]
451
+ }
421
452
  }
422
453
  export function getOutlineList(nodeList, edgeList, roofs = []) {
423
- const mode = 'outline'
424
- const minX = Math.min(...nodeList.map((n) => n.x))
425
- const minY = Math.min(...nodeList.map((n) => n.y))
426
- const nodes = nodeList.map((n) => [n.x - minX, n.y - minY])
427
- const solver = new PlanarFaceTree()
428
- const result = solver.discover(nodes, edgeList)
429
- const cycles = []
430
- if (mode == 'forest') {
431
- return result.forest
432
- }
433
- for (let k in result.forest) {
434
- const cycleTree = result.forest[k]
435
- let cycleArray = forestRecursion(cycleTree)
436
- cycles.push(...cycleArray)
437
- }
438
- let outlines = cycles
439
- .map((cycle) => {
440
- return cycle.map((index) => {
441
- return {
442
- x: nodeList[index].x,
443
- y: nodeList[index].y,
444
- z: nodeList[index].z
445
- }
446
- })
447
- })
448
- .filter((outline) => outline.length > 0)
449
- outlines.forEach((outline) => outline.pop())
454
+ const mode = 'outline'
455
+ const minX = Math.min(...nodeList.map((n) => n.x))
456
+ const minY = Math.min(...nodeList.map((n) => n.y))
457
+ const nodes = nodeList.map((n) => [n.x - minX, n.y - minY])
458
+ const solver = new PlanarFaceTree()
459
+ const result = solver.discover(nodes, edgeList)
460
+ const cycles = []
461
+ if (mode == 'forest') {
462
+ return result.forest
463
+ }
464
+ for (let k in result.forest) {
465
+ const cycleTree = result.forest[k]
466
+ let cycleArray = forestRecursion(cycleTree)
467
+ cycles.push(...cycleArray)
468
+ }
469
+ let outlines = cycles
470
+ .map((cycle) => {
471
+ return cycle.map((index) => {
472
+ return {
473
+ x: nodeList[index].x,
474
+ y: nodeList[index].y,
475
+ z: nodeList[index].z
476
+ }
477
+ })
478
+ })
479
+ .filter((outline) => outline.length > 0)
480
+ outlines.forEach((outline) => outline.pop())
450
481
 
451
- //check for existing roofs(for z value)
452
- outlines = outlines.map((outline) => {
453
- let roof = roofs.find((roof) =>
454
- polygonsHaveSame2DOutline(outline, roof.outline)
455
- )
456
- if (roof) {
457
- outline = JSON.parse(JSON.stringify(roof.outline))
458
- }
459
- return outline
460
- })
461
- return outlines
482
+ //check for existing roofs(for z value)
483
+ outlines = outlines.map((outline) => {
484
+ let roof = roofs.find((roof) =>
485
+ polygonsHaveSame2DOutline(outline, roof.outline)
486
+ )
487
+ if (roof) {
488
+ outline = JSON.parse(JSON.stringify(roof.outline))
489
+ } else {
490
+ let underneathRoofsIntersectionsData = roofs.map((roof) => {
491
+ const intersections = intersectOutlines(roof.outline, outline)
492
+ let area = 0
493
+ if (intersections.length) {
494
+ area = calculateArea(intersections[0])
495
+ }
496
+ return {
497
+ roof,
498
+ intersectionLength: intersections.length,
499
+ intersections,
500
+ area
501
+ }
502
+ })
503
+ underneathRoofsIntersectionsData =
504
+ underneathRoofsIntersectionsData.filter((d) => d.intersectionLength > 0)
505
+ if (underneathRoofsIntersectionsData.length > 0) {
506
+ underneathRoofsIntersectionsData.sort(
507
+ (a, b) => a.roof.area - b.roof.area
508
+ )
509
+ let roofUnderneath = underneathRoofsIntersectionsData[0].roof
510
+ outline = outline.map((p) =>
511
+ verticalProjectionOnPlane(
512
+ p,
513
+ roofUnderneath.normalVector,
514
+ roofUnderneath.flatOutline[0]
515
+ )
516
+ )
517
+ }
518
+ }
519
+ return outline
520
+ })
521
+ return outlines
462
522
  }
463
523
 
464
524
  function getIntersectionSegmentWithParams(A, B, C, D) {
465
- const u = substractVector(B, A)
466
- const v = substractVector(D, C)
467
- let h, j, M, P
468
- //A+hu=C+jv
469
- const u_normal = { x: -u.y, y: u.x, z: 0 }
470
- const v_normal = { x: -v.y, y: v.x, z: 0 }
471
- const denom_u = dotProduct(v, u_normal)
472
- const denom_v = dotProduct(u, v_normal)
473
- const AB = vectorLength(u)
474
- const CD = vectorLength(v)
475
- if (AB < mmTolerance && CD < mmTolerance) {
476
- if (getDistanceBetweenPoints(A, C) < mmTolerance * 2) {
477
- return [
478
- {
479
- point: A,
480
- param1: 0.5,
481
- param2: 0.5
482
- }
483
- ]
484
- }
485
- } else if (AB == 0) {
486
- //let's check if CD go through A
487
- P = getPointOnLine(A, C, D)
488
- if (getDistanceBetweenPoints(P, A) < mmTolerance) {
489
- j = vectorLength(substractVector(C, A)) / CD
490
- return [
491
- {
492
- point: A,
493
- param1: 0,
494
- param2: j
495
- }
496
- ]
497
- }
498
- } else if (CD == 0) {
499
- //let's check if AB go through C
500
- M = getPointOnLine(C, A, B)
501
- if (getDistanceBetweenPoints(M, C) < mmTolerance) {
502
- h = vectorLength(substractVector(C, A)) / AB
503
- return [
504
- {
505
- point: C,
506
- param1: h,
507
- param2: 0
508
- }
509
- ]
510
- }
511
- }
512
- if (Math.abs(denom_u) / (AB * CD) < 0.01) {
513
- //u,v parallel
514
- //let's check if they are close
515
- //distance from A to CD
516
- P = getPointOnLine(A, C, D)
517
- M = getPointOnLine(B, C, D)
518
- if (
519
- getDistanceBetweenPoints(A, P) < mmTolerance ||
525
+ A = { ...A, z: 0 }
526
+ B = { ...B, z: 0 }
527
+ C = { ...C, z: 0 }
528
+ D = { ...D, z: 0 }
529
+ const u = substractVector(B, A)
530
+ const v = substractVector(D, C)
531
+ let h, j, M, P
532
+ //A+hu=C+jv
533
+ const u_normal = { x: -u.y, y: u.x, z: 0 }
534
+ const v_normal = { x: -v.y, y: v.x, z: 0 }
535
+ const denom_u = dotProduct(v, u_normal)
536
+ const denom_v = dotProduct(u, v_normal)
537
+ const AB = vectorLength(u)
538
+ const CD = vectorLength(v)
539
+ if (AB < mmTolerance && CD < mmTolerance) {
540
+ if (getDistanceBetweenPoints(A, C) < mmTolerance * 2) {
541
+ return [
542
+ {
543
+ point: A,
544
+ param1: 0.5,
545
+ param2: 0.5
546
+ }
547
+ ]
548
+ }
549
+ } else if (AB == 0) {
550
+ //let's check if CD go through A
551
+ P = getPointOnLine(A, C, D)
552
+ if (getDistanceBetweenPoints(P, A) < mmTolerance) {
553
+ j = vectorLength(substractVector(C, A)) / CD
554
+ return [
555
+ {
556
+ point: A,
557
+ param1: 0,
558
+ param2: j
559
+ }
560
+ ]
561
+ }
562
+ } else if (CD == 0) {
563
+ //let's check if AB go through C
564
+ M = getPointOnLine(C, A, B)
565
+ if (getDistanceBetweenPoints(M, C) < mmTolerance) {
566
+ h = vectorLength(substractVector(C, A)) / AB
567
+ return [
568
+ {
569
+ point: C,
570
+ param1: h,
571
+ param2: 0
572
+ }
573
+ ]
574
+ }
575
+ }
576
+ if (Math.abs(denom_u) / (AB * CD) < 0.01) {
577
+ //u,v parallel
578
+ //let's check if they are close
579
+ //distance from A to CD
580
+ P = getPointOnLine(A, C, D)
581
+ M = getPointOnLine(B, C, D)
582
+ if (
583
+ getDistanceBetweenPoints(A, P) < mmTolerance ||
520
584
  getDistanceBetweenPoints(B, M) < mmTolerance
521
- ) {
522
- //AB and CD are colinear
523
- //let's find which point are in the middle
524
- const intersections = []
525
- //A between CD?
526
- if (isPointBetweenSegment(A, C, D)) {
527
- j = getDistanceBetweenPoints(A, C) / CD
528
- intersections.push({
529
- point: A,
530
- param1: 0,
531
- param2: j
532
- })
533
- }
534
- //B between CD
535
- if (isPointBetweenSegment(B, C, D)) {
536
- j = getDistanceBetweenPoints(B, C) / CD
537
- intersections.push({
538
- point: B,
539
- param1: 1,
540
- param2: j
541
- })
542
- }
543
- //C between AB
544
- if (isPointBetweenSegment(C, A, B)) {
545
- h = getDistanceBetweenPoints(A, C) / AB
546
- intersections.push({
547
- point: C,
548
- param1: h,
549
- param2: 0
550
- })
551
- }
552
- //D between AB
553
- if (isPointBetweenSegment(D, A, B)) {
554
- h = getDistanceBetweenPoints(A, D) / AB
555
- intersections.push({
556
- point: D,
557
- param1: h,
558
- param2: 1
559
- })
560
- }
561
- return intersections
562
- }
563
- return []
564
- }
565
- j = dotProduct(substractVector(A, C), u_normal) / denom_u
566
- h = dotProduct(substractVector(C, A), v_normal) / denom_v
567
- if (j < 0 || j > 1 || h < 0 || h > 1) {
568
- return []
569
- }
570
- const point = addVector(A, multiplyVector(h, u))
571
- point.z = C.z + j * (D.z - C.z)
572
- return [
573
- {
574
- point,
575
- param1: h,
576
- param2: j
577
- }
578
- ]
585
+ ) {
586
+ //AB and CD are colinear
587
+ //let's find which point are in the middle
588
+ const intersections = []
589
+ //A between CD?
590
+ if (isPointBetweenSegment(A, C, D)) {
591
+ j = getDistanceBetweenPoints(A, C) / CD
592
+ intersections.push({
593
+ point: A,
594
+ param1: 0,
595
+ param2: j
596
+ })
597
+ }
598
+ //B between CD
599
+ if (isPointBetweenSegment(B, C, D)) {
600
+ j = getDistanceBetweenPoints(B, C) / CD
601
+ intersections.push({
602
+ point: B,
603
+ param1: 1,
604
+ param2: j
605
+ })
606
+ }
607
+ //C between AB
608
+ if (isPointBetweenSegment(C, A, B)) {
609
+ h = getDistanceBetweenPoints(A, C) / AB
610
+ intersections.push({
611
+ point: C,
612
+ param1: h,
613
+ param2: 0
614
+ })
615
+ }
616
+ //D between AB
617
+ if (isPointBetweenSegment(D, A, B)) {
618
+ h = getDistanceBetweenPoints(A, D) / AB
619
+ intersections.push({
620
+ point: D,
621
+ param1: h,
622
+ param2: 1
623
+ })
624
+ }
625
+ return intersections
626
+ }
627
+ return []
628
+ }
629
+ j = dotProduct(substractVector(A, C), u_normal) / denom_u
630
+ h = dotProduct(substractVector(C, A), v_normal) / denom_v
631
+ if (j < 0 || j > 1 || h < 0 || h > 1) {
632
+ return []
633
+ }
634
+ const point = addVector(A, multiplyVector(h, u))
635
+ point.z = C.z + j * (D.z - C.z)
636
+ return [
637
+ {
638
+ point,
639
+ param1: h,
640
+ param2: j
641
+ }
642
+ ]
579
643
  }