@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.
- package/.eslintrc.js +28 -0
- package/.prettierrc +7 -0
- package/babel.config.js +2 -2
- package/eslint.config.mjs +10 -0
- package/package.json +5 -3
- package/src/config.js +110 -50
- package/src/coords.js +21 -21
- package/src/geo.js +111 -100
- package/src/geometry.js +920 -819
- package/src/index.js +10 -11
- package/src/intersectionPolygon.js +34 -28
- package/src/lib/concaveman.js +181 -181
- package/src/matrix.js +56 -50
- package/src/miscellaneous.js +45 -0
- package/src/objects/Circle.js +76 -41
- package/src/objects/Line.js +275 -177
- package/src/objects/Point.js +30 -27
- package/src/objects/Polygon.js +324 -243
- package/src/objects/derivedState/AddMargin.js +142 -145
- package/src/objects/derivedState/updateComputedGeometryPolygon.js +274 -253
- package/src/objects/graph/DFS.js +69 -69
- package/src/objects/graph/graphCreation.js +47 -44
- package/src/objects/hydrate.js +26 -26
- package/src/snap.js +24 -18
- package/src/splitMergePolygons.js +585 -521
- package/src/test/maths.test.js +7 -7
- package/src/vector.js +66 -66
|
@@ -1,579 +1,643 @@
|
|
|
1
1
|
import { PlanarFaceTree } from 'planar-face-discovery'
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
substractVector,
|
|
4
|
+
addVector,
|
|
5
|
+
multiplyVector,
|
|
6
|
+
dotProduct,
|
|
7
|
+
vectorLength
|
|
8
8
|
} from './vector'
|
|
9
9
|
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
71
|
+
constructionPolyline,
|
|
72
|
+
edges,
|
|
73
|
+
roofs,
|
|
74
|
+
isClosedContructionPolyline
|
|
63
75
|
) {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
92
|
+
constructionPolyline,
|
|
93
|
+
edges,
|
|
94
|
+
roofs,
|
|
95
|
+
isClosedContructionPolyline
|
|
84
96
|
) {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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(
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
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
|
-
|
|
183
|
+
)
|
|
184
|
+
return intersections
|
|
158
185
|
}
|
|
159
186
|
|
|
160
187
|
export function getNodeList(intersections, edges, constructionPolyline) {
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
188
|
+
let nodeList = []
|
|
189
|
+
edges.forEach((edge) => {
|
|
190
|
+
nodeList.push(edge.outline[0])
|
|
191
|
+
nodeList.push(edge.outline[1])
|
|
192
|
+
})
|
|
166
193
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
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
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
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
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
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
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
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
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
267
|
+
nodeList,
|
|
268
|
+
intersections,
|
|
269
|
+
edges,
|
|
270
|
+
constructionPolyline,
|
|
271
|
+
isClosedContructionPolyline
|
|
241
272
|
) {
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
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
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
297
|
+
const polylineUsedIndexes = intersectionByPolyline.map(
|
|
298
|
+
(i) => i[0].polylineIndex
|
|
299
|
+
)
|
|
300
|
+
const edgeUsedIndexes = intersectionByEdge.map((i) => i[0].edgeIndex)
|
|
270
301
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
302
|
+
const untouchedEdgeIndexes = [...Array(edges.length).keys()].filter(
|
|
303
|
+
(k) => !edgeUsedIndexes.includes(k)
|
|
304
|
+
)
|
|
274
305
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
306
|
+
const untouchedPolylineIndexes = [
|
|
307
|
+
...Array(
|
|
308
|
+
constructionPolyline.outline.length - 1 + isClosedContructionPolyline
|
|
309
|
+
).keys()
|
|
310
|
+
].filter((k) => !polylineUsedIndexes.includes(k))
|
|
280
311
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
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
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
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
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
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
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
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
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
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
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
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
|
-
|
|
437
|
+
return cleanEdgeList
|
|
407
438
|
}
|
|
408
439
|
function forestRecursion(cycleTree) {
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
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
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
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
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
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
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
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
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
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
|
}
|