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