@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.
- package/package.json +2 -2
- package/src/EventManager/EventManager.js +48 -0
- package/src/Item/Item.js +12 -0
- package/src/Layer/Layer.js +119 -0
- package/src/Overlay/AirTeamOverlay.js +605 -0
- package/src/Overlay/ImageOverlay.js +309 -0
- package/src/Overlay/Overlay.js +255 -0
- package/src/Overlay/OverlayFactory.js +19 -0
- package/src/Overlay/OverlayLayer.js +36 -0
- package/src/components/ThreeCanvasViewer.vue +17 -6
- package/src/config.js +120 -17
- package/src/helper/UpdateRoofModuleFieldRelations.js +65 -25
- package/src/helper/cameraMixin.js +25 -0
- package/src/helper/materialMixin.js +14 -1
- package/src/helper/projectedMaterial.js +662 -0
- package/src/helper/render/base.js +5 -5
- package/src/helper/render/clear.js +0 -17
- package/src/helper/render/edge.js +2 -0
- package/src/helper/render/geometryHandler.js +60 -31
- package/src/helper/render/imageOverlay.js +25 -144
- package/src/helper/render/index.js +2 -0
- package/src/helper/render/mergedGeometry.js +11 -2
- package/src/helper/render/obstacle.js +2 -1
- package/src/helper/render/overlay.js +41 -0
- package/src/helper/render/projectionMaterial.js +17 -19
- package/src/helper/render/roof.js +49 -52
- package/src/helper/render/texture.js +25 -0
- package/src/helper/render/tile.js +84 -24
- package/src/helper/render/tileProjection.js +23 -14
- package/src/helper/renderMixin.js +22 -8
- package/src/helper/threeMixin.js +5 -2
- package/src/store/hydrateData.js +23 -11
- package/src/store/nodesEdgesCreation.js +78 -4
- package/src/store/three-d-module.js +1 -1
- package/dist/css/app.58568098.css +0 -1
- package/dist/img/reneSola_Virtus_2_JC320S_24_Bbw.85a0cb6d.png +0 -0
- package/dist/index.html +0 -15
- package/dist/js/app-legacy.aa792c98.js +0 -2
- package/dist/js/app-legacy.aa792c98.js.map +0 -1
- package/dist/js/chunk-vendors-legacy.9bb3863d.js +0 -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
|
+
}
|