@eturnity/eturnity_3d 7.20.1 → 7.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/package.json +2 -2
  2. package/src/EventManager/EventManager.js +48 -0
  3. package/src/Item/Item.js +12 -0
  4. package/src/Layer/Layer.js +119 -0
  5. package/src/Overlay/AirTeamOverlay.js +605 -0
  6. package/src/Overlay/ImageOverlay.js +309 -0
  7. package/src/Overlay/Overlay.js +255 -0
  8. package/src/Overlay/OverlayFactory.js +19 -0
  9. package/src/Overlay/OverlayLayer.js +36 -0
  10. package/src/components/ThreeCanvasViewer.vue +17 -6
  11. package/src/config.js +120 -17
  12. package/src/helper/UpdateRoofModuleFieldRelations.js +65 -25
  13. package/src/helper/cameraMixin.js +25 -0
  14. package/src/helper/materialMixin.js +14 -1
  15. package/src/helper/projectedMaterial.js +662 -0
  16. package/src/helper/render/base.js +5 -5
  17. package/src/helper/render/clear.js +0 -17
  18. package/src/helper/render/edge.js +2 -0
  19. package/src/helper/render/geometryHandler.js +60 -31
  20. package/src/helper/render/imageOverlay.js +25 -144
  21. package/src/helper/render/index.js +2 -0
  22. package/src/helper/render/mergedGeometry.js +11 -2
  23. package/src/helper/render/obstacle.js +2 -1
  24. package/src/helper/render/overlay.js +41 -0
  25. package/src/helper/render/projectionMaterial.js +17 -19
  26. package/src/helper/render/roof.js +49 -52
  27. package/src/helper/render/texture.js +25 -0
  28. package/src/helper/render/tile.js +84 -24
  29. package/src/helper/render/tileProjection.js +23 -14
  30. package/src/helper/renderMixin.js +22 -8
  31. package/src/helper/threeMixin.js +5 -2
  32. package/src/store/hydrateData.js +23 -11
  33. package/src/store/nodesEdgesCreation.js +78 -4
  34. package/src/store/three-d-module.js +1 -1
  35. package/dist/css/app.58568098.css +0 -1
  36. package/dist/img/reneSola_Virtus_2_JC320S_24_Bbw.85a0cb6d.png +0 -0
  37. package/dist/index.html +0 -15
  38. package/dist/js/app-legacy.aa792c98.js +0 -2
  39. package/dist/js/app-legacy.aa792c98.js.map +0 -1
  40. package/dist/js/chunk-vendors-legacy.9bb3863d.js +0 -41
  41. package/dist/js/chunk-vendors-legacy.9bb3863d.js.map +0 -1
@@ -0,0 +1,605 @@
1
+ import Overlay from './Overlay'
2
+ import * as THREE from 'three'
3
+ import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
4
+ import { layerColors } from '../config'
5
+ import {
6
+ getDistanceBetweenPoints,
7
+ addVector,
8
+ multiplyVector,
9
+ substractVector,
10
+ getAngleInDegFromCanvasVector,
11
+ normalize2DVector,
12
+ isInsidePolygon,
13
+ calculateBestFittingPlanePolygon,
14
+ verticalProjectionOnPlane,
15
+ vectorLength2D,
16
+ deltaLatLngToDistance,
17
+ rotateTransformation
18
+ } from '@eturnity/eturnity_maths'
19
+ let Paper
20
+ export default class AirTeamOverlay extends Overlay {
21
+ constructor(overlay, emit, origin) {
22
+ super(overlay, emit)
23
+ //this.updateVerticalProjectionIfNeeded()
24
+ this.canvasWidth = 400
25
+ this.canvasHeight = 400
26
+ this.origin = origin
27
+ this.imageUrl = null
28
+ this.initialiseModel().then((isReady) => {
29
+ this.isReady = isReady
30
+ this.version++
31
+ this.emit('overlay-is-ready', { id: this.id })
32
+ this.isReadyCallback.forEach((callback) => {
33
+ if (typeof callback === 'function') {
34
+ callback()
35
+ }
36
+ })
37
+ this.isReadyCallback = []
38
+ })
39
+ this.overlayMesh = null
40
+ this.placeholderPath = null
41
+ this.opacity = 1
42
+ this.extracted = false
43
+ this.geoLocalisation = this.settings.geoLocalisation
44
+ }
45
+ onLoad() {
46
+ return new Promise((resolve) => {
47
+ if (this.isReady) {
48
+ resolve(this)
49
+ } else {
50
+ this.isReadyCallback.push(resolve)
51
+ }
52
+ })
53
+ }
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
+ set3DObjectMaterialOpacity(obj) {
101
+ obj.children.forEach((child) => {
102
+ this.set3DObjectMaterialOpacity(child, this.opacity)
103
+ })
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
+ }
109
+ }
110
+ async renderOnThreeJS(threeJSComponent) {
111
+ if (this.overlayMesh && this.overlayMesh.children.length == 0) {
112
+ this.overlayMesh = await this.loadGLBFile()
113
+ }
114
+
115
+ if (!this.isReady) {
116
+ let waitingBall = threeJSComponent.meshes.overlayMeshes[this.id]
117
+ if (!waitingBall) {
118
+ const geometry = new THREE.SphereGeometry(1, 32, 32) // Initial radius of 5
119
+ const opacity = this.opacity == 0 ? this.opacity : 0.5
120
+ const material = new THREE.MeshBasicMaterial({
121
+ color: 0xffffff, // Grey color
122
+ transparent: true,
123
+ opacity // 50% transparent
124
+ })
125
+ // Create the mesh and add it to the scene
126
+ waitingBall = new THREE.Mesh(geometry, material)
127
+ waitingBall.name = 'waitingBall'
128
+ waitingBall.userData.type = 'overlayMeshes'
129
+ waitingBall.userData.meshId = this.id
130
+ waitingBall.userData.version = this.version
131
+ threeJSComponent.scene.add(waitingBall)
132
+ }
133
+ threeJSComponent.meshes.overlayMeshes[this.id] = waitingBall
134
+ this.hasPlaceholderMesh = true
135
+ return
136
+ }
137
+ let meshId = this.id
138
+ let overlayMesh = threeJSComponent.meshes.overlayMeshes[this.id]
139
+ if (this.hasPlaceholderMesh) {
140
+ threeJSComponent.clearMeshFromScene(overlayMesh, threeJSComponent.scene)
141
+ overlayMesh = null
142
+ this.hasPlaceholderMesh = false
143
+ }
144
+ if (!overlayMesh && this.overlayMesh) {
145
+ threeJSComponent.meshes.overlayMeshes[this.id] = this.overlayMesh
146
+ overlayMesh = this.overlayMesh
147
+ overlayMesh.children.forEach((mesh) => {
148
+ mesh.position.set(-this.boxCenter.x, -this.boxCenter.y, 0)
149
+ })
150
+ threeJSComponent.scene.add(overlayMesh)
151
+ }
152
+ if (!overlayMesh) {
153
+ overlayMesh = await this.loadGLBFile()
154
+ threeJSComponent.meshes.overlayMeshes[meshId] = overlayMesh
155
+ }
156
+ const positionOffset = multiplyVector(1 / 1000, this.positionOffset)
157
+ overlayMesh.position.set(0, 0, 0)
158
+ overlayMesh.rotateZ(-overlayMesh.rotation.z)
159
+ overlayMesh.rotateZ(-this.angleOffset)
160
+ const box = new THREE.Box3().setFromObject(overlayMesh)
161
+ overlayMesh.position.set(
162
+ positionOffset.x + this.boxCenter.x,
163
+ positionOffset.y + this.boxCenter.y,
164
+ -box.min.z
165
+ )
166
+
167
+ if (!threeJSComponent.scene.getObjectById(overlayMesh.id)) {
168
+ threeJSComponent.scene.add(overlayMesh)
169
+ threeJSComponent.render()
170
+ }
171
+ this.overlayMesh = overlayMesh
172
+ this.overlayMesh.frustumCulled = false
173
+ this.set3DObjectMaterialOpacity(this.overlayMesh)
174
+ }
175
+ renderOnPaperJS(paperJSComponent) {
176
+ Paper = paperJSComponent.paperScope
177
+ if (!this.isActive) {
178
+ return
179
+ }
180
+ paperJSComponent.paperLayer['imageOverlays'].activate()
181
+ let group = paperJSComponent.paths.imageOverlays[this.id]
182
+ if (!this.isReady) {
183
+ if (!group) {
184
+ if (!paperJSComponent.loadingSpinner) {
185
+ return
186
+ }
187
+ group = new Paper.Group()
188
+
189
+ group.addChild(this.renderImagePlaceHolder(paperJSComponent))
190
+ group.itemType = 'placeholderOverlays'
191
+ group.type = 'imageOverlays'
192
+ group.pathId = this.id
193
+ group.overlayId = this.id
194
+ group.visible = true
195
+ this.placeholderPath = group
196
+
197
+ paperJSComponent.paperLayer['loadingSpinners'].activate()
198
+ Paper.view.onFrame = (event) => this.animatePlaceholder(event)
199
+ }
200
+ paperJSComponent.paths.imageOverlays[this.id] = group
201
+ this.hasPlaceholderPath = true
202
+ return
203
+ }
204
+
205
+ if (this.hasPlaceholderPath) {
206
+ if (group) {
207
+ paperJSComponent.clearPath(group)
208
+ paperJSComponent.clearPath(
209
+ paperJSComponent.paths.loadingSpinners[this.id]
210
+ )
211
+ this.placeholderPath = null
212
+ }
213
+ group = null
214
+ this.hasPlaceholderPath = false
215
+ }
216
+
217
+ let imageOverlayPath
218
+ let handles
219
+ let isRotationHandleVisible =
220
+ paperJSComponent.selectedTool == 'selectOverlay' && this.isSelected
221
+
222
+ let corners = this.corners.map((p) => paperJSComponent.toCanvasRef(p))
223
+ if (group) {
224
+ imageOverlayPath = group.children[0]
225
+ handles = this.renderImageOverlayHandles(
226
+ corners,
227
+ isRotationHandleVisible,
228
+ group.children[1]
229
+ )
230
+ this.applyRasterToCorners(imageOverlayPath, corners)
231
+ group.visible = true
232
+ group.opacity = this.opacity
233
+ group.transparent = this.opacity != 1
234
+ // group.children[2].position=new Paper.Point(paperJSComponent.toCanvasRef(this.geoLocalisation))
235
+ } else {
236
+ group = new Paper.Group()
237
+ imageOverlayPath = this.loadImageOverlay(this.imageUrl, corners)
238
+ this.applyRasterToCorners(imageOverlayPath, corners)
239
+ handles = this.renderImageOverlayHandles(corners, isRotationHandleVisible)
240
+ group.addChild(imageOverlayPath)
241
+ 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
+ group.itemType = 'imageOverlays'
248
+ group.data.url = this.url
249
+ group.type = 'imageOverlays'
250
+ group.pathId = this.id
251
+ group.overlayId = this.id
252
+ group.visible = true
253
+ group.opacity = this.opacity
254
+ group.transparent = this.opacity != 1
255
+
256
+ imageOverlayPath.visible = true
257
+ paperJSComponent.paths.imageOverlays[this.id] = group
258
+ }
259
+ }
260
+ renderImagePlaceHolder(paperJSComponent) {
261
+ const toCanvasRef = paperJSComponent.canvasContext.toCanvasRef
262
+
263
+ const group = new Paper.Group()
264
+ const circle = new Paper.Path.Circle({
265
+ center: toCanvasRef({ x: 0, y: 0 }),
266
+ radius: 50,
267
+ fillColor: '#ffffff80'
268
+ })
269
+ circle.radius = 70
270
+ circle.itemData = 'circle'
271
+ const fillRectangle = new Paper.Path()
272
+ fillRectangle.fillColor = '#ffffff80'
273
+ fillRectangle.itemType = 'placeholder'
274
+ fillRectangle.itemData = this
275
+
276
+ fillRectangle.closePath()
277
+ this.placeHolderCorners.forEach((point) =>
278
+ fillRectangle.add(toCanvasRef(point))
279
+ )
280
+ fillRectangle.visible = true
281
+ fillRectangle.version = 0
282
+ group.visible = true
283
+ group.addChild(fillRectangle)
284
+ group.addChild(circle)
285
+ return group
286
+ }
287
+ animatePlaceholder(event) {
288
+ if (!this.placeholderPath || !this.hasPlaceholderPath) {
289
+ return
290
+ }
291
+ this.placeholderPath.visible = true
292
+ const baseRadius = 30
293
+ const amplitude = 40
294
+ const newRadius =
295
+ baseRadius + amplitude * Math.sin(event.time * 0.7 * Math.PI) ** 2
296
+ const circle = this.placeholderPath.children[0].children[1]
297
+ const scale = newRadius / circle.radius
298
+ circle.scale(scale)
299
+ circle.radius = newRadius
300
+ }
301
+ loadImageOverlay(url, corners) {
302
+ let rasterImageOverlay = new Paper.Raster({
303
+ source: url,
304
+ crossOrigin: 'Anonymous'
305
+ })
306
+ rasterImageOverlay.visible = false
307
+ rasterImageOverlay.onLoad = () => {
308
+ this.applyRasterToCorners(rasterImageOverlay, corners)
309
+ }
310
+ rasterImageOverlay.data.angle = 0
311
+ rasterImageOverlay.data.type = 'imageOverlayRaster'
312
+ rasterImageOverlay.data.overlayId = this.id
313
+ rasterImageOverlay.data.url = url
314
+ return rasterImageOverlay
315
+ }
316
+ applyRasterToCorners(texture, corners = this.corners) {
317
+ //remove angle of texture
318
+ let textureAngle = texture.angle || 0
319
+ texture.rotate(-textureAngle)
320
+
321
+ //make texture the same size to fit corners
322
+ const textureWidth = texture.bounds.width
323
+ const textureHeight = texture.bounds.height
324
+ const frameWidth = getDistanceBetweenPoints(corners[1], corners[0])
325
+ const frameHeight = getDistanceBetweenPoints(corners[3], corners[0])
326
+ const center = multiplyVector(0.5, addVector(corners[0], corners[2]))
327
+ if (texture.bounds.width) {
328
+ var a = frameWidth / textureWidth
329
+ var b = 0
330
+ var c = 0
331
+ var d = frameHeight / textureHeight
332
+ texture.transform(new Paper.Matrix(a, b, c, d, 0, 0))
333
+ //translate to fit top left corner
334
+ var tx = center.x - texture.bounds.center.x
335
+ var ty = center.y - texture.bounds.center.y
336
+ texture.transform(new Paper.Matrix(1, 0, 0, 1, tx, ty))
337
+ //rotate
338
+ let angle = getAngleInDegFromCanvasVector(
339
+ substractVector(corners[0], corners[3])
340
+ )
341
+
342
+ texture.angle = angle
343
+ texture.rotate(angle)
344
+ }
345
+ }
346
+ renderImageOverlayHandles(corners, rotateHandleVisible, group = null) {
347
+ let circleHandle
348
+ let hasPaths = !!group
349
+ let handles = group || new Paper.Group()
350
+ let u = normalize2DVector(substractVector(corners[1], corners[0]))
351
+ let v = normalize2DVector(substractVector(corners[0], corners[3]))
352
+
353
+ //rotation handle
354
+ let center = multiplyVector(0.5, addVector(corners[0], corners[2]))
355
+
356
+ let radius = vectorLength2D(substractVector(center, corners[0])) - 10
357
+ if (!hasPaths) {
358
+ circleHandle = new Paper.Path.Circle(center, radius)
359
+ circleHandle.fillColor = layerColors.imageOverlayHandle.fillColor
360
+ circleHandle.strokeWidth = layerColors.imageOverlayHandle.strokeWidth
361
+ circleHandle.strokeColor = layerColors.imageOverlayHandle.strokeColor
362
+ circleHandle.data.type = 'imageOverlayRotateHandle'
363
+ circleHandle.data.overlayId = this.id
364
+ circleHandle.radius = radius
365
+ handles.addChild(circleHandle)
366
+ } else {
367
+ circleHandle = handles.children[0]
368
+ let ratio = radius / circleHandle.radius
369
+ circleHandle.transform(new Paper.Matrix(ratio, 0, 0, ratio, 0, 0))
370
+ circleHandle.position = center
371
+ circleHandle.radius = radius
372
+ }
373
+
374
+ circleHandle.visible = rotateHandleVisible
375
+ return handles
376
+ }
377
+ async initialiseModel() {
378
+ this.overlayMesh = await this.loadGLBFile()
379
+ if (!this.geoLocalisation) {
380
+ this.checkGeoLocDataAndOffset()
381
+ }
382
+ this.imageUrl = await this.initialiseVerticalProjectionImg()
383
+ this.overlayMesh.position.set(
384
+ this.positionOffset.x / 1000,
385
+ this.positionOffset.y / 1000,
386
+ 0
387
+ )
388
+ this.overlayMesh.rotateZ(-this.angleOffset)
389
+ return true
390
+ }
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
+ }
412
+ }
413
+ initialiseVerticalProjectionImg() {
414
+ 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
+ // Create a canvas element
459
+ const canvas = this.createCanvas()
460
+
461
+ // Initialize Three.js
462
+ 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
+
472
+ const renderer = new THREE.WebGLRenderer({
473
+ canvas: canvas,
474
+ alpha: true
475
+ })
476
+ renderer.setSize(this.canvasWidth, this.canvasHeight)
477
+ renderer.setClearColor(0x000000, 0) // Make background transparent
478
+
479
+ scene.add(this.overlayMesh)
480
+
481
+ // Set camera position and look at the center of the model
482
+ this.overlayProjectionCamera.position.set(
483
+ boxCenter.x,
484
+ boxCenter.y,
485
+ boxCenter.z + 100
486
+ )
487
+ this.overlayProjectionCamera.lookAt(boxCenter.x, boxCenter.y, 0)
488
+ 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
+ // Render the scene
500
+ renderer.render(scene, this.overlayProjectionCamera)
501
+
502
+ // Generate image from canvas
503
+ 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
+ })
509
+ canvas.parentNode.removeChild(canvas)
510
+ resolve(imageUrl)
511
+ })
512
+ }
513
+ getProjectedPlanesOnOverlay(outlines) {
514
+ // const scene = new THREE.Scene()
515
+ 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) => {
526
+ let outlineBounds = outline.reduce(
527
+ (acc, cur) => {
528
+ acc.xMin = Math.min(acc.xMin, cur.x)
529
+ acc.xMax = Math.max(acc.xMax, cur.x)
530
+ acc.yMin = Math.min(acc.yMin, cur.y)
531
+ acc.yMax = Math.max(acc.yMax, cur.y)
532
+ return acc
533
+ },
534
+ { xMin: Infinity, xMax: -Infinity, yMin: Infinity, yMax: -Infinity }
535
+ )
536
+ let { xMin, xMax, yMin, yMax } = outlineBounds
537
+ const cloudPoints = []
538
+ let stepNum = 5
539
+ for (let i = 0; i < stepNum; i++) {
540
+ for (let j = 0; j < stepNum; j++) {
541
+ const testPoint = {
542
+ x: xMin + (i * (xMax - xMin)) / stepNum,
543
+ y: yMin + (j * (yMax - yMin)) / stepNum
544
+ }
545
+ if (isInsidePolygon(testPoint, outline)) {
546
+ RayCaster.set(
547
+ new THREE.Vector3(testPoint.x / 1000, testPoint.y / 1000, 0),
548
+ new THREE.Vector3(0, 0, 1)
549
+ )
550
+ let intersects = RayCaster.intersectObject(overlaySimplifiedRoof)
551
+ intersects.forEach((intersection) => {
552
+ if (intersection.object.name == 'textured_roof') {
553
+ cloudPoints.push(intersection.point)
554
+ }
555
+ })
556
+ }
557
+ }
558
+ }
559
+ return cloudPoints
560
+ })
561
+ const SVDResults = cloudsPoints.map((cloud) => {
562
+ if (cloud.length > 3) {
563
+ return calculateBestFittingPlanePolygon(cloud)
564
+ } else {
565
+ return null
566
+ }
567
+ })
568
+ const newOutlines = outlines.map((outline, index) => {
569
+ if (SVDResults[index]) {
570
+ const normalVector = SVDResults[index].normalVector
571
+ const referencePoint = multiplyVector(
572
+ 1000,
573
+ SVDResults[index].projectedOutline[0]
574
+ )
575
+
576
+ const newOutline = outline.map((p) =>
577
+ verticalProjectionOnPlane(p, normalVector, referencePoint)
578
+ )
579
+ return newOutline
580
+ } else {
581
+ return null
582
+ }
583
+ })
584
+ return newOutlines
585
+ }
586
+ createCanvas() {
587
+ const canvas = document.createElement('canvas')
588
+ canvas.width = this.canvasWidth
589
+ canvas.height = this.canvasHeight
590
+ canvas.style.position = 'fixed'
591
+ canvas.style.top = '100px'
592
+ canvas.style.left = '50px'
593
+ canvas.style.zIndex = 0
594
+ // Add the canvas to the document body
595
+ document.body.appendChild(canvas)
596
+ return canvas
597
+ }
598
+ serializeSettings() {
599
+ return {
600
+ version: this.version,
601
+ corners: this.corners,
602
+ geoLocalisation: this.geoLocalisation
603
+ }
604
+ }
605
+ }