@eturnity/eturnity_3d 7.33.1 → 7.35.0-EPDM-10923.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@eturnity/eturnity_3d",
3
3
  "private": false,
4
- "version": "7.33.1",
4
+ "version": "7.35.0-EPDM-10923.0",
5
5
  "files": [
6
6
  "dist",
7
7
  "src"
@@ -15,7 +15,7 @@
15
15
  "prettier": "prettier --write \"**/*.{js,vue}\""
16
16
  },
17
17
  "dependencies": {
18
- "@eturnity/eturnity_maths": "7.32.0",
18
+ "@eturnity/eturnity_maths": "7.35.0-EPDM-10923.0",
19
19
  "@originjs/vite-plugin-commonjs": "1.0.3",
20
20
  "core-js": "2.6.12",
21
21
  "cors": "2.8.5",
@@ -26,10 +26,10 @@
26
26
  "leaflet.gridlayer.googlemutant": "0.14.0",
27
27
  "lodash": "4.17.21",
28
28
  "svd-js": "1.1.1",
29
- "three": "0.149.0",
29
+ "three": "0.142.0",
30
30
  "tween": "0.9.0",
31
31
  "uuid": "9.0.0",
32
- "vite-plugin-require": "1.1.14",
32
+ "vite-plugin-require": "1.2.14",
33
33
  "vue": "3.3.4",
34
34
  "vuex": "4.1.0"
35
35
  },
@@ -42,10 +42,10 @@
42
42
  "eslint-plugin-prettier": "4.2.1",
43
43
  "eslint-plugin-vue": "9.14.1",
44
44
  "html-webpack-plugin": "3.2.0",
45
- "prettier": "^2.8.4",
46
- "prettier-plugin-vue": "^1.1.6",
45
+ "prettier": "2.8.4",
46
+ "prettier-plugin-vue": "1.1.6",
47
47
  "vite": "2.7.2",
48
- "vite-plugin-env-compatible": "^2.0.1",
48
+ "vite-plugin-env-compatible": "2.0.1",
49
49
  "webpack": "4.46.0",
50
50
  "webpack-cli": "4.10.0"
51
51
  },
@@ -0,0 +1,97 @@
1
+ import ThreeDModelOverlay from './ThreeDModelOverlay'
2
+ import * as THREE from 'three'
3
+ import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
4
+ import { deltaLatLngToDistance } from '@eturnity/eturnity_maths'
5
+ export default class GLBOverlay extends ThreeDModelOverlay {
6
+ constructor(overlay, emit, origin) {
7
+ super(overlay, emit, origin, new GLTFLoader())
8
+ this.extracted = false
9
+ this.geoLocalisation = this.settings.geoLocalisation
10
+ this.has_plain_roof_simplified = false
11
+ }
12
+ loadFile() {
13
+ this.isReady = false
14
+ return new Promise((resolve) => {
15
+ if (this.overlayMesh) {
16
+ resolve(this.overlayMesh)
17
+ }
18
+ let resource = this.resources[0]
19
+ let overlayMesh = new THREE.Group()
20
+ this.loader.load(resource.file_url, (gltf) => {
21
+ const model = gltf.scene
22
+ model.rotateX(Math.PI / 2)
23
+ overlayMesh.userData.meshId = this.id
24
+ overlayMesh.userData.type = 'overlayMeshes'
25
+ overlayMesh.userData.version = this.version
26
+ overlayMesh.attach(model)
27
+ overlayMesh.position.set(0, 0, 0)
28
+ const box = new THREE.Box3().setFromObject(overlayMesh)
29
+ overlayMesh.position.set(0, 0, -box.min.z)
30
+
31
+ if (overlayMesh.getObjectByName('plain_roof_simplified')) {
32
+ this.has_plain_roof_simplified = true
33
+ this.type = 'airteam'
34
+ } else {
35
+ this.type = 'custom_3d_model'
36
+ }
37
+ overlayMesh.traverse((object) => {
38
+ if (object.material) {
39
+ object.material.metalness = 0
40
+ }
41
+ })
42
+ resolve(overlayMesh)
43
+ })
44
+ })
45
+ }
46
+ checkGeoLocDataAndOffset() {
47
+ const lat0 = this.overlayMesh.children[0].userData.latitude_origin
48
+ const lng0 = this.overlayMesh.children[0].userData.longitude_origin
49
+ if (lat0 && lng0 && this.origin) {
50
+ let { x, y } = deltaLatLngToDistance(
51
+ lat0 - this.origin.lat,
52
+ lng0 - this.origin.lng,
53
+ lat0
54
+ )
55
+ if (Math.hypot(x, y) < 100000) {
56
+ this.positionOffset.x = x
57
+ this.positionOffset.y = y
58
+ this.positionOffset.z = 0
59
+ } else {
60
+ this.positionOffset.x = 0
61
+ this.positionOffset.y = 0
62
+ this.positionOffset.z = 0
63
+ this.emit('overlay-offset-position-error', { id: this.id })
64
+ }
65
+ this.geoLocalisation = { x, y }
66
+ }
67
+ }
68
+ setOverlayVisibilityFor2D() {
69
+ if (
70
+ this.overlayMesh.children[0].children.some(
71
+ (child) => child.name == 'plain_roof_simplified'
72
+ )
73
+ ) {
74
+ this.overlayMesh.children[0].children.forEach(
75
+ (child) => (child.visible = child.name != 'plain_roof_simplified')
76
+ )
77
+ }
78
+ return this.overlayMesh
79
+ }
80
+ set3DObjectMaterialOpacity(obj) {
81
+ obj.children.forEach((child) => {
82
+ this.set3DObjectMaterialOpacity(child, this.opacity)
83
+ })
84
+ if (obj.material && obj.name != 'plain_roof_simplified') {
85
+ obj.visible = this.opacity > 0
86
+ obj.material.opacity = this.opacity
87
+ obj.material.transparent = this.opacity < 1
88
+ }
89
+ }
90
+ serializeSettings() {
91
+ return {
92
+ version: this.version,
93
+ corners: this.corners,
94
+ geoLocalisation: this.geoLocalisation,
95
+ }
96
+ }
97
+ }
@@ -1,12 +1,12 @@
1
1
  import Overlay from './Overlay'
2
- import AirTeamOverlay from './AirTeamOverlay'
2
+ import GLBOverlay from './GLBOverlay'
3
3
  import ImageOverlay from './ImageOverlay'
4
4
 
5
5
  const OverlayFactory = {
6
6
  createOverlay(serializedOverlay, emit, origin = { lat: null, lng: null }) {
7
7
  let overlayInstance
8
- if (serializedOverlay.type == 'airteam') {
9
- overlayInstance = new AirTeamOverlay(serializedOverlay, emit, origin)
8
+ if (['airteam', 'custom_3d_model'].includes(serializedOverlay.type)) {
9
+ overlayInstance = new GLBOverlay(serializedOverlay, emit, origin)
10
10
  } else if (serializedOverlay.type == 'image_overlay') {
11
11
  overlayInstance = new ImageOverlay(serializedOverlay, emit)
12
12
  } else {
@@ -15,16 +15,17 @@ import {
15
15
  vectorLength2D,
16
16
  deltaLatLngToDistance,
17
17
  rotateTransformation,
18
+ getStandardDeviation,
18
19
  } from '@eturnity/eturnity_maths'
19
20
  let Paper
20
- export default class AirTeamOverlay extends Overlay {
21
- constructor(overlay, emit, origin) {
21
+ export default class ThreeDModelOverlay extends Overlay {
22
+ constructor(overlay, emit, origin, loader) {
22
23
  super(overlay, emit)
23
- //this.updateVerticalProjectionIfNeeded()
24
24
  this.canvasWidth = 400
25
25
  this.canvasHeight = 400
26
26
  this.origin = origin
27
27
  this.imageUrl = null
28
+ this.loader = loader
28
29
  this.initialiseModel().then((isReady) => {
29
30
  this.isReady = isReady
30
31
  this.version++
@@ -42,6 +43,7 @@ export default class AirTeamOverlay extends Overlay {
42
43
  this.extracted = false
43
44
  this.geoLocalisation = this.settings.geoLocalisation
44
45
  }
46
+ loadFile() {}
45
47
  onLoad() {
46
48
  return new Promise((resolve) => {
47
49
  if (this.isReady) {
@@ -51,65 +53,17 @@ export default class AirTeamOverlay extends Overlay {
51
53
  }
52
54
  })
53
55
  }
54
- setOpacity(opacity) {
55
- this.opacity = opacity
56
- this.emit('item-updated', this)
57
- this.emit('overlay-three-loaded', this)
58
- }
59
- hasVerticalProjection() {
60
- return this.resources.some((resource) => resource.file_type == 'image')
61
- }
62
- loadGLBFile() {
63
- this.isReady = false
64
- return new Promise((resolve) => {
65
- if (this.overlayMesh) {
66
- resolve(this.overlayMesh)
67
- }
68
- let resource = this.resources[0]
69
- let overlayMesh = new THREE.Group()
70
- const glbLoader = new GLTFLoader()
71
- glbLoader.load(resource.file_url, (gltf) => {
72
- const model = gltf.scene
73
- overlayMesh.attach(model)
74
- overlayMesh.userData.meshId = this.id
75
- model.rotateX(Math.PI / 2)
76
- model.children = model.children.filter((child) =>
77
- [
78
- 'textured_facade',
79
- 'textured_environment',
80
- 'textured_roof',
81
- 'plain_roof_simplified',
82
- ].includes(child.name)
83
- )
84
- model.children.forEach((child) => {
85
- if (['plain_roof_simplified'].includes(child.name)) {
86
- child.visible = false
87
- }
88
- })
89
-
90
- overlayMesh.userData.type = 'overlayMeshes'
91
- overlayMesh.userData.meshId = this.id
92
- overlayMesh.userData.version = this.version
93
- overlayMesh.position.set(0, 0, 0)
94
- const box = new THREE.Box3().setFromObject(overlayMesh)
95
- overlayMesh.position.set(0, 0, -box.min.z)
96
- resolve(overlayMesh)
97
- })
98
- })
99
- }
100
56
  set3DObjectMaterialOpacity(obj) {
101
57
  obj.children.forEach((child) => {
102
58
  this.set3DObjectMaterialOpacity(child, this.opacity)
103
59
  })
104
- if (obj.material && obj.name != 'plain_roof_simplified') {
105
- obj.visible = this.opacity > 0
106
- obj.material.opacity = this.opacity
107
- obj.material.transparent = this.opacity < 1
108
- }
60
+ obj.visible = this.opacity > 0
61
+ obj.material.opacity = this.opacity
62
+ obj.material.transparent = this.opacity < 1
109
63
  }
110
64
  async renderOnThreeJS(threeJSComponent) {
111
65
  if (this.overlayMesh && this.overlayMesh.children.length == 0) {
112
- this.overlayMesh = await this.loadGLBFile()
66
+ this.overlayMesh = await this.loadFile()
113
67
  }
114
68
 
115
69
  if (!this.isReady) {
@@ -150,7 +104,7 @@ export default class AirTeamOverlay extends Overlay {
150
104
  threeJSComponent.scene.add(overlayMesh)
151
105
  }
152
106
  if (!overlayMesh) {
153
- overlayMesh = await this.loadGLBFile()
107
+ overlayMesh = await this.loadFile()
154
108
  threeJSComponent.meshes.overlayMeshes[meshId] = overlayMesh
155
109
  }
156
110
  const positionOffset = multiplyVector(1 / 1000, this.positionOffset)
@@ -168,6 +122,7 @@ export default class AirTeamOverlay extends Overlay {
168
122
  threeJSComponent.scene.add(overlayMesh)
169
123
  threeJSComponent.render()
170
124
  }
125
+
171
126
  this.overlayMesh = overlayMesh
172
127
  this.overlayMesh.frustumCulled = false
173
128
  this.set3DObjectMaterialOpacity(this.overlayMesh)
@@ -231,7 +186,6 @@ export default class AirTeamOverlay extends Overlay {
231
186
  group.visible = true
232
187
  group.opacity = this.opacity
233
188
  group.transparent = this.opacity != 1
234
- // group.children[2].position=new Paper.Point(paperJSComponent.toCanvasRef(this.geoLocalisation))
235
189
  } else {
236
190
  group = new Paper.Group()
237
191
  imageOverlayPath = this.loadImageOverlay(this.imageUrl, corners)
@@ -239,11 +193,6 @@ export default class AirTeamOverlay extends Overlay {
239
193
  handles = this.renderImageOverlayHandles(corners, isRotationHandleVisible)
240
194
  group.addChild(imageOverlayPath)
241
195
  group.addChild(handles)
242
- // let circle=new Paper.Path.Circle(new Paper.Point(paperJSComponent.toCanvasRef(multiplyVector(1000,this.geoLocalisation))),10)
243
- // circle.strokeWidth=2
244
- // circle.strokeColor = 'lime'
245
- // group.addChild(circle)
246
-
247
196
  group.itemType = 'imageOverlays'
248
197
  group.data.url = this.url
249
198
  group.type = 'imageOverlays'
@@ -374,11 +323,66 @@ export default class AirTeamOverlay extends Overlay {
374
323
  circleHandle.visible = rotateHandleVisible
375
324
  return handles
376
325
  }
326
+ initialiseCornersAndProjectionCamera() {
327
+ const box = new THREE.Box3().setFromObject(this.overlayMesh)
328
+ const boxSize = box.getSize(new THREE.Vector3())
329
+ this.boxSize = boxSize
330
+ const boxCenter = box.getCenter(new THREE.Vector3())
331
+ const left = -boxSize.x / 2
332
+ const right = +boxSize.x / 2
333
+ const top = +boxSize.y / 2
334
+ const bottom = -boxSize.y / 2
335
+ this.boxCenter = boxCenter
336
+ const initialCorners = [
337
+ {
338
+ x: 1000 * (boxCenter.x + left),
339
+ y: 1000 * (boxCenter.y + top),
340
+ z: 0,
341
+ },
342
+ {
343
+ x: 1000 * (boxCenter.x + right),
344
+ y: 1000 * (boxCenter.y + top),
345
+ z: 0,
346
+ },
347
+ {
348
+ x: 1000 * (boxCenter.x + right),
349
+ y: 1000 * (boxCenter.y + bottom),
350
+ z: 0,
351
+ },
352
+ {
353
+ x: 1000 * (boxCenter.x + left),
354
+ y: 1000 * (boxCenter.y + bottom),
355
+ z: 0,
356
+ },
357
+ ]
358
+ this.corners = []
359
+ initialCorners.forEach((corner, index) => {
360
+ corner = rotateTransformation(
361
+ corner,
362
+ this.angleOffset,
363
+ multiplyVector(1000, boxCenter)
364
+ )
365
+ corner = addVector(corner, this.positionOffset)
366
+ this.corners[index] = corner
367
+ })
368
+ this.overlayProjectionCamera = new THREE.OrthographicCamera(
369
+ left,
370
+ right,
371
+ top,
372
+ bottom,
373
+ 0.1, // near
374
+ 1000 // far
375
+ )
376
+ }
377
+ checkGeoLocDataAndOffset() {
378
+ return
379
+ }
377
380
  async initialiseModel() {
378
- this.overlayMesh = await this.loadGLBFile()
381
+ this.overlayMesh = await this.loadFile()
379
382
  if (!this.geoLocalisation) {
380
383
  this.checkGeoLocDataAndOffset()
381
384
  }
385
+ this.initialiseCornersAndProjectionCamera()
382
386
  this.imageUrl = await this.initialiseVerticalProjectionImg()
383
387
  this.overlayMesh.position.set(
384
388
  this.positionOffset.x / 1000,
@@ -388,86 +392,24 @@ export default class AirTeamOverlay extends Overlay {
388
392
  this.overlayMesh.rotateZ(-this.angleOffset)
389
393
  return true
390
394
  }
391
- checkGeoLocDataAndOffset() {
392
- const lat0 = this.overlayMesh.children[0].userData.latitude_origin
393
- const lng0 = this.overlayMesh.children[0].userData.longitude_origin
394
- if (lat0 && lng0 && this.origin) {
395
- let { x, y } = deltaLatLngToDistance(
396
- lat0 - this.origin.lat,
397
- lng0 - this.origin.lng,
398
- lat0
399
- )
400
- if (Math.hypot(x, y) < 100000) {
401
- this.positionOffset.x = x
402
- this.positionOffset.y = y
403
- this.positionOffset.z = 0
404
- } else {
405
- this.positionOffset.x = 0
406
- this.positionOffset.y = 0
407
- this.positionOffset.z = 0
408
- this.emit('overlay-offset-position-error', { id: this.id })
409
- }
410
- this.geoLocalisation = { x, y }
411
- }
395
+ setOverlayVisibilityFor2D() {
396
+ return this.overlayMesh
412
397
  }
413
398
  initialiseVerticalProjectionImg() {
414
399
  return new Promise((resolve, reject) => {
415
- const box = new THREE.Box3().setFromObject(this.overlayMesh)
416
- const boxSize = box.getSize(new THREE.Vector3())
417
- this.boxSize = boxSize
418
- this.canvasWidth = boxSize.x * 100
419
- this.canvasHeight = boxSize.y * 100
420
- const boxCenter = box.getCenter(new THREE.Vector3())
421
- const left = -boxSize.x / 2
422
- const right = +boxSize.x / 2
423
- const top = +boxSize.y / 2
424
- const bottom = -boxSize.y / 2
425
- this.boxCenter = boxCenter
426
- const initialCorners = [
427
- {
428
- x: 1000 * (boxCenter.x + left),
429
- y: 1000 * (boxCenter.y + top),
430
- z: 0,
431
- },
432
- {
433
- x: 1000 * (boxCenter.x + right),
434
- y: 1000 * (boxCenter.y + top),
435
- z: 0,
436
- },
437
- {
438
- x: 1000 * (boxCenter.x + right),
439
- y: 1000 * (boxCenter.y + bottom),
440
- z: 0,
441
- },
442
- {
443
- x: 1000 * (boxCenter.x + left),
444
- y: 1000 * (boxCenter.y + bottom),
445
- z: 0,
446
- },
447
- ]
448
- this.corners = []
449
- initialCorners.forEach((corner, index) => {
450
- corner = rotateTransformation(
451
- corner,
452
- this.angleOffset,
453
- multiplyVector(1000, boxCenter)
454
- )
455
- corner = addVector(corner, this.positionOffset)
456
- this.corners[index] = corner
457
- })
458
400
  // Create a canvas element
401
+ this.canvasWidth = this.boxSize.x * 100
402
+ this.canvasHeight = this.boxSize.y * 100
403
+ const maxSize = Math.max(this.canvasWidth, this.canvasHeight)
404
+ const CANVAS_MAX_SIZE = 2048
405
+ if (maxSize > CANVAS_MAX_SIZE) {
406
+ this.canvasWidth *= CANVAS_MAX_SIZE / maxSize
407
+ this.canvasHeight *= CANVAS_MAX_SIZE / maxSize
408
+ }
459
409
  const canvas = this.createCanvas()
460
410
 
461
411
  // Initialize Three.js
462
412
  const scene = new THREE.Scene()
463
- this.overlayProjectionCamera = new THREE.OrthographicCamera(
464
- left,
465
- right,
466
- top,
467
- bottom,
468
- 0.1, // near
469
- 1000 // far
470
- )
471
413
 
472
414
  const renderer = new THREE.WebGLRenderer({
473
415
  canvas: canvas,
@@ -475,54 +417,40 @@ export default class AirTeamOverlay extends Overlay {
475
417
  })
476
418
  renderer.setSize(this.canvasWidth, this.canvasHeight)
477
419
  renderer.setClearColor(0x000000, 0) // Make background transparent
478
-
420
+ this.setOverlayVisibilityFor2D()
479
421
  scene.add(this.overlayMesh)
480
422
 
423
+ const ambientLight = new THREE.AmbientLight(0xffffff, 0.8)
424
+ ambientLight.name = 'overlayAmbientLight'
425
+ scene.add(ambientLight)
426
+ const light = new THREE.HemisphereLight(0xffffff, 0xffffff, 0.8)
427
+ ambientLight.name = 'overlayHemisphereLight'
428
+ scene.add(light)
429
+
481
430
  // Set camera position and look at the center of the model
482
431
  this.overlayProjectionCamera.position.set(
483
- boxCenter.x,
484
- boxCenter.y,
485
- boxCenter.z + 100
432
+ this.boxCenter.x,
433
+ this.boxCenter.y,
434
+ this.boxCenter.z + 100
486
435
  )
487
- this.overlayProjectionCamera.lookAt(boxCenter.x, boxCenter.y, 0)
436
+ this.overlayProjectionCamera.lookAt(this.boxCenter.x, this.boxCenter.y, 0)
488
437
  this.overlayProjectionCamera.updateProjectionMatrix()
489
- const childrenVisibility = this.overlayMesh.children[0].children.map(
490
- (child) => child.visible
491
- )
492
- this.overlayMesh.children[0].children.forEach(
493
- (child) => (child.visible = child.name == 'textured_roof')
494
- )
495
- const ambientLight = new THREE.AmbientLight(0xffffff, 0.8)
496
- this.overlayMesh.children[0].add(ambientLight)
497
- const light = new THREE.HemisphereLight(0xffffff, 0xffffff, 0.8)
498
- this.overlayMesh.children[0].add(light)
499
438
  // Render the scene
500
439
  renderer.render(scene, this.overlayProjectionCamera)
501
440
 
502
441
  // Generate image from canvas
503
442
  const imageUrl = canvas.toDataURL()
504
- this.overlayMesh.children[0].remove(light)
505
- this.overlayMesh.children[0].remove(ambientLight)
506
- this.overlayMesh.children[0].children.forEach((child, index) => {
507
- child.visible = childrenVisibility[index]
508
- })
443
+ scene.remove(light)
444
+ scene.remove(ambientLight)
509
445
  canvas.parentNode.removeChild(canvas)
510
446
  resolve(imageUrl)
511
447
  })
512
448
  }
513
- getProjectedPlanesOnOverlay(outlines) {
514
- // const scene = new THREE.Scene()
449
+ getProjectedPlanesOnOverlay(polygons) {
515
450
  const RayCaster = new THREE.Raycaster()
516
- // let overlaySimplifiedRoof = this.overlayMesh.getObjectByName(
517
- // 'plain_roof_simplified'
518
- // )
519
- // overlaySimplifiedRoof.visible = true
520
- // if(overlaySimplifiedRoof){
521
- // }else{
522
- let overlaySimplifiedRoof = this.overlayMesh.clone()
523
- //}
524
- //scene.add(overlayMesh)
525
- const cloudsPoints = outlines.map((outline) => {
451
+ const cloudsPoints = polygons.map((polygon) => {
452
+ const outline = polygon.outline
453
+ const holeOutlines = polygon.holes.map((h) => h.outline)
526
454
  let outlineBounds = outline.reduce(
527
455
  (acc, cur) => {
528
456
  acc.xMin = Math.min(acc.xMin, cur.x)
@@ -535,24 +463,27 @@ export default class AirTeamOverlay extends Overlay {
535
463
  )
536
464
  let { xMin, xMax, yMin, yMax } = outlineBounds
537
465
  const cloudPoints = []
538
- let stepNum = 5
539
- for (let i = 0; i < stepNum; i++) {
540
- for (let j = 0; j < stepNum; j++) {
466
+ let stepNum = 8
467
+ for (let i = 1; i < stepNum; i++) {
468
+ for (let j = 1; j < stepNum; j++) {
541
469
  const testPoint = {
542
470
  x: xMin + (i * (xMax - xMin)) / stepNum,
543
471
  y: yMin + (j * (yMax - yMin)) / stepNum,
544
472
  }
545
- if (isInsidePolygon(testPoint, outline)) {
473
+ if (
474
+ isInsidePolygon(testPoint, outline) &&
475
+ holeOutlines.every(
476
+ (holeOutline) => !isInsidePolygon(testPoint, holeOutline)
477
+ )
478
+ ) {
546
479
  RayCaster.set(
547
- new THREE.Vector3(testPoint.x / 1000, testPoint.y / 1000, 0),
548
- new THREE.Vector3(0, 0, 1)
480
+ new THREE.Vector3(testPoint.x / 1000, testPoint.y / 1000, 100),
481
+ new THREE.Vector3(0, 0, -1)
549
482
  )
550
- let intersects = RayCaster.intersectObject(overlaySimplifiedRoof)
551
- intersects.forEach((intersection) => {
552
- if (intersection.object.name == 'textured_roof') {
553
- cloudPoints.push(intersection.point)
554
- }
555
- })
483
+ let intersects = RayCaster.intersectObject(this.overlayMesh)
484
+ if (intersects.length > 0) {
485
+ cloudPoints.push(intersects[0].point)
486
+ }
556
487
  }
557
488
  }
558
489
  }
@@ -560,19 +491,31 @@ export default class AirTeamOverlay extends Overlay {
560
491
  })
561
492
  const SVDResults = cloudsPoints.map((cloud) => {
562
493
  if (cloud.length > 3) {
563
- return calculateBestFittingPlanePolygon(cloud)
494
+ let cloudInMm = cloud.map((p) => {
495
+ return multiplyVector(1000, p)
496
+ })
497
+ let SVDResult = calculateBestFittingPlanePolygon(cloudInMm)
498
+ let gaps = SVDResult.gaps
499
+ let standardDeviation = getStandardDeviation(gaps)
500
+ if (standardDeviation > 10 && cloudInMm.length > 10) {
501
+ let sortedGaps = gaps.map((p) => p)
502
+ sortedGaps = sortedGaps.sort((a, b) => a - b)
503
+ let threshold = sortedGaps[Math.floor(sortedGaps.length * 0.75)]
504
+ cloudInMm = cloudInMm.filter((_, index) => {
505
+ return gaps[index] <= threshold
506
+ })
507
+ SVDResult = calculateBestFittingPlanePolygon(cloudInMm)
508
+ }
509
+ return SVDResult
564
510
  } else {
565
511
  return null
566
512
  }
567
513
  })
568
- const newOutlines = outlines.map((outline, index) => {
514
+ const newOutlines = polygons.map((polygon, index) => {
515
+ let outline = polygon.outline
569
516
  if (SVDResults[index]) {
570
517
  const normalVector = SVDResults[index].normalVector
571
- const referencePoint = multiplyVector(
572
- 1000,
573
- SVDResults[index].projectedOutline[0]
574
- )
575
-
518
+ const referencePoint = SVDResults[index].projectedOutline[0]
576
519
  const newOutline = outline.map((p) =>
577
520
  verticalProjectionOnPlane(p, normalVector, referencePoint)
578
521
  )
@@ -32,7 +32,7 @@
32
32
  intervalId: null,
33
33
  }
34
34
  },
35
- beforeDestroy() {
35
+ beforeUnmount() {
36
36
  clearInterval(this.intervalId)
37
37
  cancelAnimationFrame(this.animationFrameId)
38
38
  OverlayLayer.removeEventListener(
@@ -6,113 +6,53 @@ import {
6
6
  } from '@eturnity/eturnity_maths'
7
7
  //this function just update the "roof" and "moduleField(s)" properties of panels,moduleFields and roofs
8
8
  export function UpdateRoofModuleFieldRelations(state) {
9
- const roofPolygons = state.polygons.filter((poly) => poly.layer == 'roof')
10
- const moduleFieldPolygons = state.polygons.filter(
9
+ let moduleFields = state.polygons.filter(
11
10
  (poly) => poly.layer == 'moduleField'
12
11
  )
13
- const panelPolygons = state.polygons.filter((poly) =>
12
+ let roofs = state.polygons.filter((poly) => poly.layer == 'roof')
13
+ const panels = state.polygons.filter((poly) =>
14
14
  ['panel', 'user_deactivated_panel'].includes(poly.layer)
15
15
  )
16
- panelPolygons.forEach((panel) => {
17
- panel.roof = null
18
- })
19
- //let's remove all moduleFields from roofs
20
16
 
21
- roofPolygons.forEach((roofPolygon) => {
22
- if (!roofPolygon.moduleFields) {
23
- roofPolygon.moduleFields = []
24
- }
25
- roofPolygon.moduleFields.filter((mf) => {
26
- moduleFieldPolygons.some((p) => p.id == mf.id)
17
+ let roofUnderPerModuleFieldId = {}
18
+ moduleFields.forEach((moduleField) => {
19
+ let roofModuleFieldIntersections = []
20
+ roofs.forEach((roof) => {
21
+ let intersection = intersectOutlines(roof.outline, moduleField.outline)
22
+ if (intersection.length) {
23
+ const intersectionArea = intersection.reduce((acc, cur) => {
24
+ acc += calculateArea(cur)
25
+ return acc
26
+ }, 0)
27
+ roofModuleFieldIntersections.push({ roof, intersectionArea })
28
+ }
27
29
  })
28
- })
29
- moduleFieldPolygons.forEach((moduleFieldPolygon) => {
30
- if (
31
- moduleFieldPolygon.roof &&
32
- !roofPolygons.some((r) => r.id == moduleFieldPolygon.roof.id)
33
- ) {
34
- moduleFieldPolygon.roof = null
30
+ roofModuleFieldIntersections.sort((a, b) => a.roof.area - b.roof.area)
31
+ roofModuleFieldIntersections.sort(
32
+ (a, b) => b.intersectionArea - a.intersectionArea
33
+ )
34
+ if (roofModuleFieldIntersections.length) {
35
+ roofUnderPerModuleFieldId[moduleField.id] =
36
+ roofModuleFieldIntersections[0].roof
35
37
  }
36
38
  })
37
-
38
- for (let k in moduleFieldPolygons) {
39
- const moduleField = moduleFieldPolygons[k]
40
- let roofPolygon = moduleField.roof
41
- if (!roofPolygon) {
42
- //check if everypoint is in the same roof and if they are not too far from the flat surface.(at least above)
43
- let roofPolygonCandidates = roofPolygons.map((roof) => {
44
- const intersection = intersectOutlines(
45
- moduleField.outline,
46
- roof.outline
47
- )
48
- if (intersection.length == 0) {
49
- return {
50
- roof,
51
- intersectionArea: 0,
52
- intersectionRatio: 0,
53
- areaRatio: 0,
54
- roofBiggerThanModuleField: 1,
55
- }
56
- }
57
- const intersectionArea_m2 = calculateArea(intersection[0]) / 1e6
58
- return {
59
- roof,
60
- intersectionArea: intersectionArea_m2,
61
- intersectionRatio: (intersectionArea_m2 / roof.area).toFixed(3),
62
- areaRatio: (moduleField.area / roof.area).toFixed(3),
63
- roofBiggerThanModuleField: roof.area >= moduleField.area ? 1 : -1,
39
+ roofs.forEach((roof) => (roof.moduleFields = []))
40
+ panels.forEach((panel) => {
41
+ panel.roof = null
42
+ })
43
+ moduleFields.forEach((moduleField) => {
44
+ let roof = roofUnderPerModuleFieldId[moduleField.id]
45
+ if (roof) {
46
+ moduleField.roof = roof
47
+ roof.moduleFields.push(moduleField)
48
+ panels.forEach((panel) => {
49
+ if (panel.moduleField.id == moduleField.id) {
50
+ panel.roof = roof
64
51
  }
65
52
  })
66
- //roofs on roof can lead to multiple roof under a point. moduleField is on the smallest
67
-
68
- roofPolygonCandidates
69
- .sort((a, b) => b.roof.area - a.roof.area)
70
- .sort((a, b) => a.intersectionArea - b.intersectionArea)
71
- .sort(
72
- (a, b) => a.roofBiggerThanModuleField - b.roofBiggerThanModuleField
73
- )
74
- let polygonCandidate = roofPolygonCandidates.pop()
75
- if (!polygonCandidate) {
76
- continue
77
- }
78
- roofPolygon = polygonCandidate.roof
79
- if (!roofPolygon) {
80
- continue
81
- }
82
-
83
- roofPolygon.moduleFields.push(moduleField)
84
53
  }
85
- roofPolygon = roofPolygons.find((r) => r.id == roofPolygon.id)
86
- roofPolygon.moduleFields = [
87
- ...roofPolygon.moduleFields.filter((mf) => mf.id != moduleField.id),
88
- moduleField,
89
- ]
90
- moduleField.roof = roofPolygon
91
- moduleField.panels.forEach((panel) => (panel.roof = roofPolygon))
92
- panelPolygons.forEach((p) => {
93
- if (p.moduleField.id == moduleField.id) {
94
- p.roof = roofPolygon
95
- }
96
- })
97
- }
98
-
99
- let newPolygons = state.polygons.map((p) => {
100
- if (
101
- !['roof', 'moduleField', 'panel', 'user_deactivated_panel'].includes(
102
- p.layer
103
- )
104
- )
105
- return p
106
- if (p.layer == 'roof') return roofPolygons.find((r) => r.id == p.id)
107
- if (p.layer == 'moduleField')
108
- return moduleFieldPolygons.find((mf) => mf.id == p.id)
109
- if (['panel', 'user_deactivated_panel'].includes(p.layer))
110
- return panelPolygons.find((panel) => panel.id == p.id)
111
54
  })
112
- state = { ...state, polygons: newPolygons }
113
-
114
55
  state = removeModuleFieldAndPanelsOnNoOrMultipleRoofs(state)
115
-
116
56
  return state
117
57
  }
118
58
 
@@ -25,9 +25,6 @@ export function UpdateRoofObstacleRelations(state, updateVersionEnable = true) {
25
25
 
26
26
  holePolygons.forEach((obstacle) => {
27
27
  //breaking circular reference for memory management
28
- for (let k in obstacle.roofs) {
29
- obstacle.roofs[k] = null
30
- }
31
28
  obstacle.roofs = []
32
29
 
33
30
  let roofsWithObstacle = roofPolygons.filter((roof) => {
@@ -21,7 +21,7 @@ export default {
21
21
  methods: {
22
22
  renderImageOverlayOnBackground() {
23
23
  const overlayGeometries = OverlayLayer.getReadyItems()
24
- .filter((overlay) => overlay.isReady && overlay.corners)
24
+ .filter((overlay) => overlay.isReady && overlay.corners && overlay.type=='image_overlay')
25
25
  .map((overlay) => {
26
26
  return overlay.getBackgroundGeometry()
27
27
  })
@@ -40,7 +40,6 @@ export default {
40
40
  this.applyTextureOnMesh(this.overlayMaterials, this.meshes.backgroundMesh)
41
41
 
42
42
  this.meshes.backgroundMesh.visible = true
43
-
44
43
  return
45
44
  },
46
45
  async getOverlaysMaterials() {
@@ -49,7 +48,8 @@ export default {
49
48
  overlayMaterialsPromises.push(overlay.getProjectionMaterial())
50
49
  })
51
50
  overlayMaterialsPromises = overlayMaterialsPromises.filter((p) => !!p)
52
- this.imageOverlayMaterial = await Promise.all(overlayMaterialsPromises)
51
+ const imageOverlayMaterial = await Promise.all(overlayMaterialsPromises)
52
+ this.imageOverlayMaterial=imageOverlayMaterial.filter(m=>!!m)
53
53
  return this.imageOverlayMaterial
54
54
  },
55
55
  },
@@ -28,7 +28,7 @@ export default {
28
28
  showableRoofs = this.roofs
29
29
  }
30
30
  showableRoofs.forEach((roofPolygon) => {
31
- const geometries = getBufferRoofMarginGeometry(roofPolygon)
31
+ const geometries = getBufferRoofMarginGeometry(roofPolygon, roofPolygon)
32
32
 
33
33
  geometries.forEach((geometry, index) => {
34
34
  if (geometry) {
@@ -1,5 +1,3 @@
1
- import * as THREE from 'three'
2
- import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
3
1
  import { mapState } from 'vuex'
4
2
  import OverlayLayer from '../../Overlay/OverlayLayer.js'
5
3
 
@@ -8,7 +6,6 @@ export default {
8
6
  return {}
9
7
  },
10
8
  created() {
11
- this.glbLoader = new GLTFLoader()
12
9
  },
13
10
  computed: {
14
11
  ...mapState({}),
@@ -106,16 +106,6 @@ export default {
106
106
  const latitude = this.layoutSettings.layout_latitude
107
107
  const longitude = this.layoutSettings.layout_longitude
108
108
  let corners = tileToCorners(x, y, zoom, latitude, longitude)
109
- let height, width, tileCenter
110
- let numberOfTiles = Math.pow(2, zoom)
111
- height =
112
- (2000 * Math.PI * earthRadius * Math.cos((latitude * Math.PI) / 180)) /
113
- numberOfTiles
114
- width = height
115
- tileCenter = {
116
- x: (corners[0].x + corners[2].x) / 2,
117
- y: (corners[0].y + corners[2].y) / 2,
118
- }
119
109
 
120
110
  camera.left = corners[0].x / 1000
121
111
  camera.right = corners[2].x / 1000
@@ -176,6 +176,11 @@ export default {
176
176
  method: this.renderOverlays,
177
177
  arg: [],
178
178
  })
179
+ methodList.push({
180
+ name: 'updateProjectionMaterials',
181
+ method: this.updateProjectionMaterials,
182
+ arg: [],
183
+ })
179
184
  case 'removeNodes': //params.nodes
180
185
  case 'removeEdges': //params.edges
181
186
  case 'translate': //params.edges
@@ -268,6 +273,11 @@ export default {
268
273
  method: this.renderRoofsMargin,
269
274
  arg: [],
270
275
  })
276
+ methodList.push({
277
+ name: 'renderTiles',
278
+ method: this.debouncedRenderTiles,
279
+ arg: [],
280
+ })
271
281
  break
272
282
  case 'removePanels': //params.panels
273
283
  case 'removeModuleField': //params:moduleField
@@ -105,12 +105,14 @@ export default {
105
105
  })
106
106
  },
107
107
  initialiseLights() {
108
- const ambientLight = new THREE.AmbientLight(0xf0f0f0, 0.5)
108
+ // const ambientLight = new THREE.AmbientLight(0xffffff, 0.5)
109
+ var hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444)
110
+ hemiLight.position.set(0, 0, 300)
111
+ this.scene.add(hemiLight)
109
112
  let directionalLight = new THREE.DirectionalLight(0xffffff, 0.5)
110
113
  directionalLight.position.set(0, 1000, 500)
111
114
  let directionalLight2 = new THREE.DirectionalLight(0xffffff, 0.5)
112
115
  directionalLight2.position.set(0, -1000, 500)
113
- this.scene.add(ambientLight)
114
116
  this.scene.add(directionalLight)
115
117
  this.scene.add(directionalLight2)
116
118
  },
@@ -24,9 +24,9 @@ function sanitizedOutline(outline) {
24
24
  }, [])
25
25
  }
26
26
  export async function hydratePolygons({
27
- roofsResponse,
28
- obstaclesResponse,
29
- moduleFieldsResponse,
27
+ roofsResponse = [],
28
+ obstaclesResponse = [],
29
+ moduleFieldsResponse = [],
30
30
  }) {
31
31
  //removing empty roof,obstacle and modulefield generated by BE on variant creation
32
32
  roofsResponse = roofsResponse.reduce((acc, cur) => {
@@ -124,6 +124,7 @@ export async function hydratePolygons({
124
124
  moduleField.version = moduleFieldResp.version
125
125
 
126
126
  moduleField.pvData = {}
127
+ moduleField.mountingData = {}
127
128
  moduleField.pvData.module_texture_version_id =
128
129
  moduleFieldResp.module_texture_version_id
129
130
  moduleField.data = {}
@@ -204,11 +205,13 @@ export async function hydratePolygons({
204
205
  })
205
206
 
206
207
  const roof = roofs.find((r) => r.id == moduleFieldResp.roof_uuid)
207
- if (!roof.moduleFields) {
208
- roof.moduleFields = []
208
+ if (roof) {
209
+ if (!roof.moduleFields) {
210
+ roof.moduleFields = []
211
+ }
212
+ roof.moduleFields.push(moduleField)
213
+ moduleField.roof = roof
209
214
  }
210
- roof.moduleFields.push(moduleField)
211
- moduleField.roof = roof
212
215
  moduleField.panels = MFpanels
213
216
  moduleField.userDeactivatedPanels = MFuserDeactivatedPanels
214
217
  try {
@@ -244,7 +247,6 @@ export async function hydrateData({ state, commit }, data) {
244
247
  let layers = getLayers()
245
248
  let selectedLayer = layers.find((layer) => layer.key == mapType)
246
249
  commit('mutate_selectedMapLayer', selectedLayer)
247
-
248
250
  if (overlaysResponse) {
249
251
  let origin
250
252
  if (layoutSettingResponse) {
@@ -3,7 +3,7 @@ import {
3
3
  generateNodes3D,
4
4
  generateEdges2D,
5
5
  generateEdges3D,
6
- } from './nodesEdgesCreation'
6
+ } from '@eturnity/eturnity_maths'
7
7
  import { hydrateData } from './hydrateData.js'
8
8
 
9
9
  const init_state = {
@@ -1,279 +0,0 @@
1
- import { Point, Line } from '@eturnity/eturnity_maths'
2
- import { mmTolerance, mmTolerance3dNode } from '../config'
3
- import {
4
- isAlmostSamePoint3D,
5
- isAlmostSameSegment2D,
6
- isAlmostSameSegment3D,
7
- isAlmostSamePoint2D,
8
- verticalProjectionOnPlane,
9
- } from '@eturnity/eturnity_maths'
10
-
11
- export function generateNodes2D(polygons) {
12
- let nodes2D = []
13
- polygons
14
- .filter((poly) =>
15
- [
16
- 'roof',
17
- 'obstacle',
18
- 'tmpModuleField',
19
- 'moduleField',
20
- 'construction',
21
- ].includes(poly.layer)
22
- )
23
- .forEach((polygon) => {
24
- const outline = polygon.outline
25
- outline.forEach((point, i) => {
26
- //check if nodes2D already exist
27
- let node2D = nodes2D.find(
28
- (node) =>
29
- isAlmostSamePoint2D(node, point, mmTolerance) &&
30
- node.layer == polygon.layer
31
- )
32
- if (!node2D) {
33
- node2D = new Point(point.x, point.y, point.z, polygon.layer)
34
- node2D.belongsTo = []
35
- node2D.id = 'node2D'
36
- node2D.itemType = 'node2D'
37
- node2D.layer = polygon.layer
38
- node2D.masterHandle = {
39
- id: node2D.id,
40
- itemType: 'masterHandle',
41
- open: null,
42
- selected: null,
43
- }
44
- nodes2D.push(node2D)
45
- }
46
- node2D.belongsTo.push({
47
- polygonId: polygon.id,
48
- index: i,
49
- polygon,
50
- })
51
- node2D.id += '_' + polygon.id + '_' + i
52
- })
53
- })
54
-
55
- //all nodes has been created. now lets update open/selected field
56
- nodes2D.forEach((node2D) => {
57
- if (node2D.masterHandle) {
58
- node2D.masterHandle.open = node2D.belongsTo.some(
59
- (item) => item.index == null || item.polygon.outline[item.index].open
60
- )
61
- node2D.masterHandle.selected = node2D.belongsTo.every(
62
- (item) =>
63
- item.index == null || item.polygon.outline[item.index].selected
64
- )
65
- node2D.masterHandle.locked = node2D.belongsTo.every(
66
- (item) => item.index == null || item.polygon.outline[item.index].locked
67
- )
68
- }
69
- })
70
- return nodes2D
71
- }
72
-
73
- export function generateNodes3D(polygons) {
74
- let nodes3D = []
75
- polygons
76
- .filter((poly) =>
77
- ['roof', 'obstacle', 'tmpModuleField', 'moduleField'].includes(poly.layer)
78
- )
79
- .forEach((polygon) => {
80
- const outline = polygon.outline
81
- outline.forEach((point, i) => {
82
- //check if nodes2D already exist
83
- let node3D = nodes3D.find((node) =>
84
- isAlmostSamePoint3D(node, point, mmTolerance3dNode)
85
- )
86
- if (!node3D) {
87
- node3D = new Point(point.x, point.y, point.z, polygon.layer)
88
- node3D.belongsTo = []
89
- node3D.id = 'node3D'
90
- node3D.itemType = 'node3D'
91
- node3D.layer = polygon.layer
92
- node3D.masterHandle = {
93
- id: node3D.id,
94
- itemType: 'masterHandle',
95
- open: null,
96
- selected: null,
97
- }
98
- nodes3D.push(node3D)
99
- }
100
- node3D.belongsTo.push({
101
- polygonId: polygon.id,
102
- index: i,
103
- polygon,
104
- })
105
- node3D.id += '_' + polygon.id + '_' + i
106
- })
107
- })
108
- //all nodes has been created. now lets update open/selected field
109
- nodes3D.forEach((node3D) => {
110
- if (node3D.masterHandle) {
111
- node3D.masterHandle.open = node3D.belongsTo.some(
112
- (item) => item.polygon.outline[item.index].open
113
- )
114
- node3D.masterHandle.selected = node3D.belongsTo.every(
115
- (item) => item.polygon.outline[item.index].selected
116
- )
117
- }
118
- })
119
- return nodes3D
120
- }
121
-
122
- export function generateEdges2D(polygons, splitEdgesIds = []) {
123
- let edges2D = []
124
- polygons
125
- .filter((poly) =>
126
- ['roof', 'obstacle', 'moduleField', 'construction'].includes(poly.layer)
127
- )
128
- .forEach((polygon) => {
129
- const outline = polygon.outline
130
- outline.forEach((point, i) => {
131
- const isLastPointOfPolygon = i == outline.length - 1
132
- if (!polygon.isClosed && isLastPointOfPolygon) {
133
- return
134
- }
135
- //check if edge2D already exist
136
- const nextPoint = outline[(i + 1) % outline.length]
137
- let edge2D = edges2D.find((edge) => {
138
- return (
139
- isAlmostSameSegment2D(
140
- [point, nextPoint],
141
- edge.outline,
142
- mmTolerance
143
- ) && edge.layer == polygon.layer
144
- )
145
- })
146
- if (!edge2D) {
147
- //create an edge at this position
148
- edge2D = new Line(point, nextPoint, polygon.layer)
149
- edge2D.id = 'edge2D'
150
- edge2D.belongsTo = []
151
- edge2D.layer = polygon.layer
152
- edge2D.selected = false
153
- edge2D.itemType = 'edge2D'
154
- edges2D.push(edge2D)
155
- }
156
- //add BelongToPolygon
157
- edge2D.belongsTo.push({
158
- polygonId: polygon.id,
159
- index: i,
160
- polygon,
161
- })
162
- if (polygon.roofs && polygon.roofs.length > 0) {
163
- polygon.roofs.forEach((supportRoof) => {
164
- edge2D.belongsTo.push({
165
- polygonId: supportRoof.id,
166
- index: null,
167
- polygon: supportRoof,
168
- })
169
- })
170
- }
171
- edge2D.id += '_' + polygon.id + '_' + i
172
- })
173
- })
174
- edges2D.forEach((edge2D) => {
175
- edge2D.isSplit = splitEdgesIds.includes(edge2D.id)
176
- if (edge2D.belongsTo.length > 2) {
177
- edge2D.belongsTo = edge2D.belongsTo.filter((i) => i.index !== null)
178
- }
179
- })
180
- edges2D.forEach((edge2D) => {
181
- const isNotSplit = edge2D.belongsTo.every((item, index, array) => {
182
- if (index == 0) {
183
- return true
184
- }
185
- const outline0 = array[0].polygon.outline
186
- const outline1 = item.polygon.outline
187
- let point0, point1, nextPoint0, nextPoint1
188
- let pointNull = []
189
- if (array[0].index !== null) {
190
- point0 = outline0[array[0].index]
191
- nextPoint0 = outline0[(array[0].index + 1) % outline0.length]
192
- } else {
193
- pointNull.push(0)
194
- point0 = verticalProjectionOnPlane(
195
- outline1[item.index],
196
- array[0].polygon.normalVector,
197
- array[0].polygon.flatOutline[0]
198
- )
199
- nextPoint0 = verticalProjectionOnPlane(
200
- outline1[(item.index + 1) % outline1.length],
201
- array[0].polygon.normalVector,
202
- array[0].polygon.flatOutline[0]
203
- )
204
- }
205
- if (item.index !== null) {
206
- point1 = outline1[item.index]
207
- nextPoint1 = outline1[(item.index + 1) % outline1.length]
208
- } else {
209
- pointNull.push(1)
210
- point1 = verticalProjectionOnPlane(
211
- outline0[array[0].index],
212
- item.polygon.normalVector,
213
- item.polygon.flatOutline[0]
214
- )
215
- nextPoint1 = verticalProjectionOnPlane(
216
- outline0[(array[0].index + 1) % outline0.length],
217
- item.polygon.normalVector,
218
- item.polygon.flatOutline[0]
219
- )
220
- }
221
- const isSameSeg = isAlmostSameSegment3D(
222
- [point0, nextPoint0],
223
- [point1, nextPoint1],
224
- mmTolerance
225
- )
226
- return isSameSeg
227
- })
228
- if (!isNotSplit && edge2D.belongsTo.length == 2 && edge2D.layer == 'roof') {
229
- edge2D.isSplit = true
230
- }
231
- })
232
- return edges2D
233
- }
234
-
235
- export function generateEdges3D(polygons) {
236
- let edges3D = []
237
- polygons
238
- .filter((poly) =>
239
- ['roof', 'obstacle', 'moduleField', 'construction'].includes(poly.layer)
240
- )
241
- .forEach((polygon) => {
242
- const outline = polygon.outline
243
- outline.forEach((point, i) => {
244
- const isLastPointOfPolygon = i == outline.length - 1
245
- if (!polygon.isClosed && isLastPointOfPolygon) {
246
- return
247
- }
248
- //check if edge2D already exist
249
- const nextPoint = outline[(i + 1) % outline.length]
250
- let edge3D = edges3D.find((edge) => {
251
- return (
252
- isAlmostSameSegment3D(
253
- [point, nextPoint],
254
- edge.outline,
255
- mmTolerance
256
- ) && edge.layer == polygon.layer
257
- )
258
- })
259
- if (!edge3D) {
260
- //create an edge at this position
261
- edge3D = new Line(point, nextPoint, polygon.layer)
262
- edge3D.id = 'edge3D'
263
- edge3D.belongsTo = []
264
- edge3D.layer = polygon.layer
265
- edge3D.selected = false
266
- edge3D.itemType = 'edge3D'
267
- edges3D.push(edge3D)
268
- }
269
- //add BelongToPolygon
270
- edge3D.belongsTo.push({
271
- polygonId: polygon.id,
272
- index: i,
273
- polygon,
274
- })
275
- edge3D.id += '_' + polygon.id + '_' + i
276
- })
277
- })
278
- return edges3D
279
- }