@qispace/vue3-player 0.0.5
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/README.md +18 -0
- package/dist/components/apartmentChooser/QiApartmentChooser.vue.d.ts +4 -0
- package/dist/components/apartmentChooser/QiApartmentChooser.vue.d.ts.map +1 -0
- package/dist/components/apartmentChooser/QiApartmentChooserNavigation.vue.d.ts +4 -0
- package/dist/components/apartmentChooser/QiApartmentChooserNavigation.vue.d.ts.map +1 -0
- package/dist/components/apartmentChooser/QiApartmentChooserRaster.vue.d.ts +4 -0
- package/dist/components/apartmentChooser/QiApartmentChooserRaster.vue.d.ts.map +1 -0
- package/dist/components/apartmentChooser/QiApartmentChooserRotator.vue.d.ts +4 -0
- package/dist/components/apartmentChooser/QiApartmentChooserRotator.vue.d.ts.map +1 -0
- package/dist/components/apartmentChooser/QiApartmentChooserSvg.vue.d.ts +4 -0
- package/dist/components/apartmentChooser/QiApartmentChooserSvg.vue.d.ts.map +1 -0
- package/dist/components/buildingPicker/QiBuildingChooser.vue.d.ts +3 -0
- package/dist/components/buildingPicker/QiBuildingChooser.vue.d.ts.map +1 -0
- package/dist/components/buildingPicker/QiBuildingChooserRaster.vue.d.ts +4 -0
- package/dist/components/buildingPicker/QiBuildingChooserRaster.vue.d.ts.map +1 -0
- package/dist/components/buildingPicker/QiBuildingChooserRasterOverlay.vue.d.ts +4 -0
- package/dist/components/buildingPicker/QiBuildingChooserRasterOverlay.vue.d.ts.map +1 -0
- package/dist/components/buildingPicker/QiBuildingChooserSceneImageStack.vue.d.ts +4 -0
- package/dist/components/buildingPicker/QiBuildingChooserSceneImageStack.vue.d.ts.map +1 -0
- package/dist/components/buildingPicker/QiBuildingChooserSunSimControl.vue.d.ts +5 -0
- package/dist/components/buildingPicker/QiBuildingChooserSunSimControl.vue.d.ts.map +1 -0
- package/dist/components/buildingPicker/QiBuildingChooserSvg.vue.d.ts +4 -0
- package/dist/components/buildingPicker/QiBuildingChooserSvg.vue.d.ts.map +1 -0
- package/dist/components/index.d.ts +4 -0
- package/dist/components/shared/QiFloatingCard.vue.d.ts +4 -0
- package/dist/components/shared/QiFloatingCard.vue.d.ts.map +1 -0
- package/dist/components/shared/QiHoverProbe.vue.d.ts +4 -0
- package/dist/components/shared/QiHoverProbe.vue.d.ts.map +1 -0
- package/dist/components/shared/QiNorthDirection.vue.d.ts +3 -0
- package/dist/components/shared/QiNorthDirection.vue.d.ts.map +1 -0
- package/dist/components/shared/QiZoomBox.vue.d.ts +4 -0
- package/dist/components/shared/QiZoomBox.vue.d.ts.map +1 -0
- package/dist/components/virtualTourV2/Disclaimer.vue.d.ts +4 -0
- package/dist/components/virtualTourV2/Disclaimer.vue.d.ts.map +1 -0
- package/dist/components/virtualTourV2/FullScreenButton.vue.d.ts +3 -0
- package/dist/components/virtualTourV2/FullScreenButton.vue.d.ts.map +1 -0
- package/dist/components/virtualTourV2/ManualSlideShow.vue.d.ts +4 -0
- package/dist/components/virtualTourV2/ManualSlideShow.vue.d.ts.map +1 -0
- package/dist/components/virtualTourV2/ManualSlideShowPlayer.vue.d.ts +4 -0
- package/dist/components/virtualTourV2/ManualSlideShowPlayer.vue.d.ts.map +1 -0
- package/dist/components/virtualTourV2/SingleImage.vue.d.ts +4 -0
- package/dist/components/virtualTourV2/SingleImage.vue.d.ts.map +1 -0
- package/dist/components/virtualTourV2/SocialShare.vue.d.ts +4 -0
- package/dist/components/virtualTourV2/SocialShare.vue.d.ts.map +1 -0
- package/dist/components/virtualTourV2/WaypointCarousel.vue.d.ts +4 -0
- package/dist/components/virtualTourV2/WaypointCarousel.vue.d.ts.map +1 -0
- package/dist/lib/virtualTour__/ApartmentConfig.d.ts +27 -0
- package/dist/lib/virtualTour__/ApartmentFiles.d.ts +7 -0
- package/dist/lib/virtualTour__/MinimapConfig.d.ts +23 -0
- package/dist/lib/virtualTour__/impl/HttpApartmentFiles.d.ts +19 -0
- package/dist/vue3-player.cjs.js +2 -0
- package/dist/vue3-player.esm.js +2992 -0
- package/dist/vue3-player.iife.js +3 -0
- package/dist/vue3-player.umd.js +3 -0
- package/package.json +65 -0
- package/src/components/apartmentChooser/QiApartmentChooser.vue +319 -0
- package/src/components/apartmentChooser/QiApartmentChooserNavigation.vue +88 -0
- package/src/components/apartmentChooser/QiApartmentChooserRaster.vue +408 -0
- package/src/components/apartmentChooser/QiApartmentChooserRotator.vue +202 -0
- package/src/components/apartmentChooser/QiApartmentChooserSvg.vue +257 -0
- package/src/components/apartmentChooser/index.js +5 -0
- package/src/components/buildingPicker/QiBuildingChooser.vue +61 -0
- package/src/components/buildingPicker/QiBuildingChooserRaster.vue +312 -0
- package/src/components/buildingPicker/QiBuildingChooserRasterOverlay.vue +96 -0
- package/src/components/buildingPicker/QiBuildingChooserSceneImageStack.vue +89 -0
- package/src/components/buildingPicker/QiBuildingChooserSunSimControl.vue +257 -0
- package/src/components/buildingPicker/QiBuildingChooserSvg.vue +414 -0
- package/src/components/buildingPicker/index.js +6 -0
- package/src/components/index.ts +4 -0
- package/src/components/shared/QiFloatingCard.vue +76 -0
- package/src/components/shared/QiHoverProbe.vue +22 -0
- package/src/components/shared/QiNorthDirection.vue +27 -0
- package/src/components/shared/QiZoomBox.vue +322 -0
- package/src/components/shared/index.js +4 -0
- package/src/components/virtualTourV2/Compass.vue +37 -0
- package/src/components/virtualTourV2/Disclaimer.vue +41 -0
- package/src/components/virtualTourV2/FloorLevel.vue +73 -0
- package/src/components/virtualTourV2/FullScreenButton.vue +23 -0
- package/src/components/virtualTourV2/InteriorSelector.vue +90 -0
- package/src/components/virtualTourV2/ManualSlideShow.vue +198 -0
- package/src/components/virtualTourV2/ManualSlideShowPlayer.vue +159 -0
- package/src/components/virtualTourV2/PlayerV2.vue +300 -0
- package/src/components/virtualTourV2/ScrollHelper.vue +74 -0
- package/src/components/virtualTourV2/SettingsUI.vue +355 -0
- package/src/components/virtualTourV2/SingleImage.vue +36 -0
- package/src/components/virtualTourV2/SocialShare.vue +163 -0
- package/src/components/virtualTourV2/TimeOfDay.vue +50 -0
- package/src/components/virtualTourV2/Tutorial.vue +690 -0
- package/src/components/virtualTourV2/ViewModeToggle.vue +24 -0
- package/src/components/virtualTourV2/VirtualTourV2.vue +876 -0
- package/src/components/virtualTourV2/WaypointCarousel.vue +162 -0
- package/src/components/virtualTourV2/index.js +1 -0
- package/src/components/virtualTourV2/minimapv2/MiniMapMap.vue +262 -0
- package/src/components/virtualTourV2/minimapv2/MiniMapV2.vue +110 -0
- package/src/components/virtualTourV2/minimapv2/MinimapCompass.vue +39 -0
- package/src/components/virtualTourV2/minimapv2/Moveable.vue +208 -0
- package/src/components/virtualTourV2/minimapv2/RotationMarkerV2.vue +79 -0
- package/src/components/virtualTourV2/minimapv2/SunsimulationSlider.vue +203 -0
- package/src/components/virtualTourV2/minimapv2/index.js +1 -0
- package/src/entry.esm.js +17 -0
- package/src/entry.js +13 -0
- package/src/entry.ts_ +17 -0
- package/src/lib/apartmentChooser/BuildingViewerModel.js +60 -0
- package/src/lib/apartmentChooser/CircularSlideshow.js +66 -0
- package/src/lib/apartmentChooser/RotationStep.js +31 -0
- package/src/lib/apartmentChooser/SceneRotator.js +25 -0
- package/src/lib/apartmentChooser/index.js +3 -0
- package/src/lib/apartmentChooser/throttle.js +15 -0
- package/src/lib/buildingPicker/BuildingMap.js +24 -0
- package/src/lib/buildingPicker/BuildingPickerResourceProvider.js +97 -0
- package/src/lib/buildingPicker/CanvasRaster.js +29 -0
- package/src/lib/buildingPicker/DayOfYearSelector.js +36 -0
- package/src/lib/buildingPicker/SampleRaster.js +14 -0
- package/src/lib/buildingPicker/index.js +5 -0
- package/src/lib/index.js +4 -0
- package/src/lib/shared/BatchLoadTracker.js +52 -0
- package/src/lib/shared/I18N.js +65 -0
- package/src/lib/shared/ResourceLoader.js +33 -0
- package/src/lib/shared/index.js +3 -0
- package/src/lib/virtualTour/CameraSnapshot.js +42 -0
- package/src/lib/virtualTour/FullscreenModel.js +69 -0
- package/src/lib/virtualTour/textures/arrow.png +0 -0
- package/src/lib/virtualTour/textures/compass-bg.png +0 -0
- package/src/lib/virtualTour/textures/compass-needle.png +0 -0
- package/src/lib/virtualTour/textures/compass-north.png +0 -0
- package/src/lib/virtualTour/textures/floor-1.svg +4 -0
- package/src/lib/virtualTour/textures/floor-2.svg +4 -0
- package/src/lib/virtualTour/textures/marker.png +0 -0
- package/src/lib/virtualTour/textures/tod-sun.png +0 -0
- package/src/lib/virtualTour__/ApartmentConfig.ts +80 -0
- package/src/lib/virtualTour__/ApartmentFiles.ts +8 -0
- package/src/lib/virtualTour__/CameraNavigator.js_ +74 -0
- package/src/lib/virtualTour__/CameraSnapshot.js +42 -0
- package/src/lib/virtualTour__/CoordConversions.js +43 -0
- package/src/lib/virtualTour__/FullscreenModel.js +69 -0
- package/src/lib/virtualTour__/MinimapConfig.ts +46 -0
- package/src/lib/virtualTour__/PlayerViewModel.js +423 -0
- package/src/lib/virtualTour__/config/ApartmentConfig.js +92 -0
- package/src/lib/virtualTour__/config/CameraConfig.js +97 -0
- package/src/lib/virtualTour__/config/Interaction.js +393 -0
- package/src/lib/virtualTour__/config/Panorama.js +78 -0
- package/src/lib/virtualTour__/config/PlayerConfig.js +812 -0
- package/src/lib/virtualTour__/config/rawinflate.export.js +833 -0
- package/src/lib/virtualTour__/config/shaders.js +24 -0
- package/src/lib/virtualTour__/impl/HttpApartmentFiles.ts +57 -0
- package/src/lib/virtualTour__/index.js +1 -0
- package/src/lib/virtualTour__/textures/arrow.png +0 -0
- package/src/lib/virtualTour__/textures/compass-bg.png +0 -0
- package/src/lib/virtualTour__/textures/compass-needle.png +0 -0
- package/src/lib/virtualTour__/textures/compass-north.png +0 -0
- package/src/lib/virtualTour__/textures/floor-1.svg +4 -0
- package/src/lib/virtualTour__/textures/floor-2.svg +4 -0
- package/src/lib/virtualTour__/textures/marker.png +0 -0
- package/src/lib/virtualTour__/textures/tod-sun.png +0 -0
- package/src/main.ts_ +24 -0
- package/src/shims-png.d.ts +4 -0
- package/src/shims-tsx.d.ts +11 -0
- package/src/shims-vue.d.ts +4 -0
- package/src/style.css +0 -0
- package/src/vite-env.d.ts +5 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import Vue from 'vue'
|
|
2
|
+
|
|
3
|
+
export class CameraNavigator {
|
|
4
|
+
constructor() {
|
|
5
|
+
this._state = Vue.observable({
|
|
6
|
+
waypoints: [],
|
|
7
|
+
selectedWaypointIndex: null,
|
|
8
|
+
selectedCameraId: null,
|
|
9
|
+
})
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
initialize(waypoints, cameraId, selectedWaypointIndex = 0) {
|
|
13
|
+
this._state.waypoints = waypoints
|
|
14
|
+
this.selectedWaypointIndex = selectedWaypointIndex % waypoints.length
|
|
15
|
+
if (!this._state.selectedCameraId) this._state.selectedCameraId = cameraId
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
selectNextWaypoint() {
|
|
19
|
+
if (this.selectedWaypointIndex === this.waypointCount - 1) {
|
|
20
|
+
this.selectedWaypointIndex = 0
|
|
21
|
+
} else {
|
|
22
|
+
this.selectedWaypointIndex += 1
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
selectPrevWaypoint() {
|
|
27
|
+
if (this.selectedWaypointIndex === 0) {
|
|
28
|
+
this.selectedWaypointIndex = this.waypointCount - 1
|
|
29
|
+
} else {
|
|
30
|
+
this.selectedWaypointIndex -= 1
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
get waypointCount() {
|
|
35
|
+
return this._state.waypoints.length
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
get waypoints() {
|
|
39
|
+
return this._state.waypoints
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
get selectedWaypointIndex() {
|
|
43
|
+
return this._state.selectedWaypointIndex
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
set selectedWaypointIndex(value) {
|
|
47
|
+
this._state.selectedWaypointIndex = value
|
|
48
|
+
if (this.selectedWaypoint) {
|
|
49
|
+
this._state.selectedCameraId = this.selectedWaypoint.id
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
get selectedWaypoint() {
|
|
54
|
+
return this._state.waypoints[this._state.selectedWaypointIndex]
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
get selectedCameraId() {
|
|
58
|
+
return this._state.selectedCameraId
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
set selectedCameraId(value) {
|
|
62
|
+
this._state.selectedCameraId = value
|
|
63
|
+
const waypointIndex = this._waypointIndexById(value)
|
|
64
|
+
if (waypointIndex > -1) {
|
|
65
|
+
this._state.selectedWaypointIndex = waypointIndex
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
_waypointIndexById(cameraId) {
|
|
70
|
+
return this._state.waypoints.findIndex(
|
|
71
|
+
(cam) => cam.id.toUpperCase() === cameraId.toUpperCase()
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
function padded(num) {
|
|
2
|
+
return ('0' + num).slice(-2)
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
// Makes a string in the format of YYYYMMDD HHMMSS
|
|
6
|
+
function makeTimestampString() {
|
|
7
|
+
const now = new Date()
|
|
8
|
+
return [
|
|
9
|
+
now.getFullYear().toString(),
|
|
10
|
+
padded(now.getMonth() + 1),
|
|
11
|
+
padded(now.getDate()),
|
|
12
|
+
' ',
|
|
13
|
+
padded(now.getHours()),
|
|
14
|
+
padded(now.getMinutes()),
|
|
15
|
+
padded(now.getSeconds()),
|
|
16
|
+
].join('')
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function makeDefaultFilename() {
|
|
20
|
+
return 'Visuado-Snapshot-' + makeTimestampString()
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export class CameraSnapshot {
|
|
24
|
+
constructor(data, cameraParams) {
|
|
25
|
+
this.data = data
|
|
26
|
+
this.cameraParams = cameraParams
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
download(filename = null) {
|
|
30
|
+
filename = filename || makeDefaultFilename()
|
|
31
|
+
const link = document.createElement('a')
|
|
32
|
+
if (typeof link.download === 'string') {
|
|
33
|
+
document.body.appendChild(link) //Firefox requires the link to be in the body
|
|
34
|
+
link.download = filename
|
|
35
|
+
link.href = this.data
|
|
36
|
+
link.click()
|
|
37
|
+
document.body.removeChild(link)
|
|
38
|
+
} else {
|
|
39
|
+
location.replace(uri)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import * as THREE from 'three'
|
|
2
|
+
|
|
3
|
+
function parseVectorString(str) {
|
|
4
|
+
return str.split(',', 3).map((c) => parseFloat(c))
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function clampAngle(angle) {
|
|
8
|
+
while (angle < -360) angle += 360
|
|
9
|
+
return angle % 360
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function toVector3([x, y, z]) {
|
|
13
|
+
return new THREE.Vector3(x, y, z)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function positionFromUnity([x, y, z]) {
|
|
17
|
+
return [x, y, -z]
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function positionToUnity([x, y, z]) {
|
|
21
|
+
return [x, y, -z]
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function rotationFromUnity([rotX, rotY, rotZ]) {
|
|
25
|
+
if (rotX > 180) rotX = rotX - 360
|
|
26
|
+
return [-rotX, clampAngle(rotY + 270), rotZ]
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function rotationToUnity([rotX, rotY, rotZ]) {
|
|
30
|
+
rotX = -rotX
|
|
31
|
+
if (rotX < 0) rotX += 360
|
|
32
|
+
rotY = rotY - 270
|
|
33
|
+
if (rotY < 0) rotY += 360
|
|
34
|
+
return [rotX, rotY, rotZ]
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function stringToVector3ConvertPositionFromUnity(str) {
|
|
38
|
+
return toVector3(positionFromUnity(parseVectorString(str)))
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function stringToVector3ConvertRotationFromUnity(str) {
|
|
42
|
+
return toVector3(rotationFromUnity(parseVectorString(str)))
|
|
43
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
export class FullscreenModel {
|
|
2
|
+
constructor(document, elemId) {
|
|
3
|
+
this._doc = document
|
|
4
|
+
this._elemId = elemId
|
|
5
|
+
this._state = { isActive: false }
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
get isActive() {
|
|
9
|
+
return this._state.isActive
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
setup() {
|
|
13
|
+
const events = [
|
|
14
|
+
'fullscreenchange',
|
|
15
|
+
'webkitfullscreenchange',
|
|
16
|
+
'mozfullscreenchange',
|
|
17
|
+
'msfullscreenchange',
|
|
18
|
+
]
|
|
19
|
+
const element = this._getElement()
|
|
20
|
+
events.forEach((event) => {
|
|
21
|
+
element.addEventListener(event, this._toggle.bind(this), false)
|
|
22
|
+
})
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
toggle() {
|
|
26
|
+
if (this.isActive) this._exit()
|
|
27
|
+
else this._enter()
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
_enter() {
|
|
31
|
+
|
|
32
|
+
const elem = this._getElement()
|
|
33
|
+
const method =
|
|
34
|
+
elem.requestFullscreen ||
|
|
35
|
+
elem.webkitRequestFullscreen ||
|
|
36
|
+
elem.mozRequestFullscreen ||
|
|
37
|
+
elem.msRequestFullscreen
|
|
38
|
+
|
|
39
|
+
if (method) {
|
|
40
|
+
method.apply(elem)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
_exit() {
|
|
46
|
+
const method =
|
|
47
|
+
this._doc.exitFullscreen ||
|
|
48
|
+
this._doc.webkitExitFullscreen ||
|
|
49
|
+
this._doc.mozExitFullscreen ||
|
|
50
|
+
this._doc.document.msExitFullscreen
|
|
51
|
+
if (method) {
|
|
52
|
+
method.apply(document)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
_getElement() {
|
|
57
|
+
return this._doc.getElementById(this._elemId)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
_toggle() {
|
|
61
|
+
const activeState = !!(
|
|
62
|
+
this._doc.fullscreenElement ||
|
|
63
|
+
this._doc.webkitFullscreenElement ||
|
|
64
|
+
this._doc.mozFullscreenElement ||
|
|
65
|
+
this._doc.msFullscreenElement
|
|
66
|
+
)
|
|
67
|
+
this._state.isActive = activeState
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { ProjectFiles, ProjectFile } from './ApartmentFiles'
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
export interface Coordinate3D{
|
|
5
|
+
x: number,
|
|
6
|
+
y: number,
|
|
7
|
+
z: number
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface MinimapStorey {
|
|
11
|
+
sunsimFilesTimeStamps: string[],
|
|
12
|
+
sunsimFiles: string[],
|
|
13
|
+
fovY: number,
|
|
14
|
+
rotation: Coordinate3D,
|
|
15
|
+
position: Coordinate3D,
|
|
16
|
+
}
|
|
17
|
+
export interface MinimapMetadata {
|
|
18
|
+
storeys: MinimapStorey[]
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export class MinimapConfig {
|
|
22
|
+
constructor(private readonly apartmentFiles: ProjectFiles) {}
|
|
23
|
+
|
|
24
|
+
async getMinimapData(): Promise<Object | undefined> {
|
|
25
|
+
const metadata = await this.readMinimapMetadata()
|
|
26
|
+
if (!metadata) return undefined
|
|
27
|
+
|
|
28
|
+
const fileNames = metadata.storeys[0].sunsimFiles
|
|
29
|
+
const files = fileNames.map((name) => this.constructMinimapFile(name).toPath())
|
|
30
|
+
|
|
31
|
+
// TODO: for DUPLEX this will be different
|
|
32
|
+
const minimapData = metadata.storeys[0]
|
|
33
|
+
|
|
34
|
+
return {...minimapData, sunsimFiles: files }
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
private async readMinimapMetadata(): Promise<MinimapMetadata | undefined> {
|
|
38
|
+
const metadataFile = this.constructMinimapFile('minimap.json')
|
|
39
|
+
const metadata = await metadataFile.readJson<MinimapMetadata>()
|
|
40
|
+
return metadata
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
private constructMinimapFile(filename: string): ProjectFile {
|
|
44
|
+
return this.apartmentFiles.makeFile('minimap', filename)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
import Vue from 'vue'
|
|
2
|
+
import axios from 'axios'
|
|
3
|
+
import PlayerConfig from './config/PlayerConfig'
|
|
4
|
+
import { CameraNavigator } from './CameraNavigator'
|
|
5
|
+
|
|
6
|
+
export const ViewModeVirtualTour = 'virtualTour'
|
|
7
|
+
export const ViewModeSlideShow = 'slideShow'
|
|
8
|
+
export const ViewModeManualSlideShow = 'manualSlideShow'
|
|
9
|
+
|
|
10
|
+
function parseTimeOfDay(floatValue) {
|
|
11
|
+
if (floatValue === undefined) return null
|
|
12
|
+
|
|
13
|
+
const hours = Math.floor(floatValue)
|
|
14
|
+
const minutes = Math.floor((hours - floatValue) * 60)
|
|
15
|
+
return { hours, minutes }
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function getFirstCameraId(cameras) {
|
|
19
|
+
const camera = cameras && cameras[0]
|
|
20
|
+
return camera ? camera.id : undefined
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function getAvailableViewModesFromUnitViewMode(unitViewMode) {
|
|
24
|
+
switch (unitViewMode) {
|
|
25
|
+
case 'slideShowOnly':
|
|
26
|
+
return [ViewModeSlideShow]
|
|
27
|
+
case 'virtualTourOnly':
|
|
28
|
+
return [ViewModeVirtualTour]
|
|
29
|
+
case 'virtualTourAndSlideShowDefaultVirtualTour':
|
|
30
|
+
case 'virtualTourAndSlideShowDefaultSlideShow':
|
|
31
|
+
return [ViewModeVirtualTour, ViewModeSlideShow]
|
|
32
|
+
case 'manualSlideShow':
|
|
33
|
+
return [ViewModeManualSlideShow]
|
|
34
|
+
default:
|
|
35
|
+
console.error('cannot initialize player without viewModes')
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function getInitialViewModeFromUnitViewMode(unitViewMode) {
|
|
40
|
+
switch (unitViewMode) {
|
|
41
|
+
case 'slideShowOnly':
|
|
42
|
+
case 'virtualTourAndSlideShowDefaultSlideShow':
|
|
43
|
+
return ViewModeSlideShow
|
|
44
|
+
case 'virtualTourAndSlideShowDefaultVirtualTour':
|
|
45
|
+
case 'virtualTourOnly':
|
|
46
|
+
return ViewModeVirtualTour
|
|
47
|
+
case 'manualSlideShow':
|
|
48
|
+
return ViewModeManualSlideShow
|
|
49
|
+
default:
|
|
50
|
+
console.error('cannot initialize player without viewModes')
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export default class PlayerViewModel {
|
|
55
|
+
static makeCdnModel(modelParams) {
|
|
56
|
+
return new CdnPlayerViewModel(modelParams)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
constructor(initData) {
|
|
60
|
+
this._state = Vue.observable({
|
|
61
|
+
_initialized: false,
|
|
62
|
+
_viewMode: getInitialViewModeFromUnitViewMode(initData.unitViewMode),
|
|
63
|
+
_availableViewModes: getAvailableViewModesFromUnitViewMode(
|
|
64
|
+
initData.unitViewMode
|
|
65
|
+
),
|
|
66
|
+
_selectedArchetypeIndex: initData.selectedArchetypeIndex || 0,
|
|
67
|
+
_cameras: [],
|
|
68
|
+
_timeOfDay: initData.timeOfDay,
|
|
69
|
+
_archetypes: initData.archetypes || [],
|
|
70
|
+
_cameraSnapshot: {
|
|
71
|
+
requested: null,
|
|
72
|
+
latest: null,
|
|
73
|
+
},
|
|
74
|
+
_panoramaOrientation: { lat: 0.0, lon: 0.0 },
|
|
75
|
+
adminMode: initData.adminMode || false,
|
|
76
|
+
})
|
|
77
|
+
this._navigator = new CameraNavigator()
|
|
78
|
+
|
|
79
|
+
this._cdnFileResolver =
|
|
80
|
+
typeof initData.cdnFileResolver === 'string'
|
|
81
|
+
? (name) => initData.cdnFileResolver + name
|
|
82
|
+
: initData.cdnFileResolver
|
|
83
|
+
this._apartmentId = initData.apartmentId
|
|
84
|
+
this._websiteUrl = initData.websiteUrl
|
|
85
|
+
this._unitViewMode = initData.unitViewMode
|
|
86
|
+
|
|
87
|
+
this._manualSlideshowImages = initData.manualSlideshowImages
|
|
88
|
+
|
|
89
|
+
this._addBuildVersionToUrls = initData.addBuildVersionToUrls
|
|
90
|
+
this._showCompass = !!initData.showCompass
|
|
91
|
+
this._showMiniMap = initData.showMiniMap
|
|
92
|
+
this._onInformationButtonPress = initData.onInformationButtonPress
|
|
93
|
+
this._mouseSensitivityFactor = initData.mouseSensitivity ? initData.mouseSensitivity : 1
|
|
94
|
+
|
|
95
|
+
this._initData = undefined
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
takeCameraSnapshot() {
|
|
99
|
+
this._state._cameraSnapshot.requested = new Date()
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async initializeData({ initialArchetypeId, initialWaypointIndex } = {}) {
|
|
103
|
+
if (this._state._viewMode === ViewModeManualSlideShow) {
|
|
104
|
+
this._initData = this._manualSlideshowImages
|
|
105
|
+
this._state._timeOfDay = undefined
|
|
106
|
+
this._state._cameras = this._manualSlideshowImages
|
|
107
|
+
|
|
108
|
+
this._navigator.initialize(
|
|
109
|
+
this._manualSlideshowImages,
|
|
110
|
+
getFirstCameraId(this._manualSlideshowImages),
|
|
111
|
+
0
|
|
112
|
+
)
|
|
113
|
+
} else {
|
|
114
|
+
if (this.archetypes.length === 0) {
|
|
115
|
+
await this._loadArchetypesFromConfig(initialArchetypeId)
|
|
116
|
+
} else if (initialArchetypeId) {
|
|
117
|
+
let index = this.archetypes.findIndex(
|
|
118
|
+
({ id }) => id === initialArchetypeId
|
|
119
|
+
)
|
|
120
|
+
if (index < 0) {
|
|
121
|
+
index = 0
|
|
122
|
+
}
|
|
123
|
+
this.selectArchetypeByIndex(index)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
this._state._archetypes = await this._loadMinimaps()
|
|
127
|
+
|
|
128
|
+
// TODO: With Ulven project, apartment.json is an empty stub file
|
|
129
|
+
// The code needs to be ready for such a situation
|
|
130
|
+
const apartments = (
|
|
131
|
+
await axios.get(this.resolveApartmentFile('apartment.json'))
|
|
132
|
+
).data
|
|
133
|
+
const thumbs = (await axios.get(this.resolveApartmentFile('thumbs.json')))
|
|
134
|
+
.data
|
|
135
|
+
|
|
136
|
+
this._initData = apartments
|
|
137
|
+
this._state._timeOfDay = parseTimeOfDay(apartments.timeInHours)
|
|
138
|
+
this._state._cameras = apartments.cameras
|
|
139
|
+
|
|
140
|
+
this._navigator.initialize(
|
|
141
|
+
thumbs.cameras,
|
|
142
|
+
getFirstCameraId(apartments.cameras),
|
|
143
|
+
initialWaypointIndex
|
|
144
|
+
)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
this._state._initialized = true
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
async _loadArchetypesFromConfig(initialArchetypeId) {
|
|
151
|
+
const projectConfig = (await axios.get(this.getFilename('/config.json')))
|
|
152
|
+
.data
|
|
153
|
+
|
|
154
|
+
this._state._archetypes = projectConfig.archetypes.map(
|
|
155
|
+
({ description, archetypeID, title, enabled }) => {
|
|
156
|
+
return {
|
|
157
|
+
id: archetypeID,
|
|
158
|
+
description,
|
|
159
|
+
label: title || archetypeID,
|
|
160
|
+
enabled,
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
let index = this.archetypes.findIndex(
|
|
166
|
+
({ id }) =>
|
|
167
|
+
id ===
|
|
168
|
+
(initialArchetypeId
|
|
169
|
+
? initialArchetypeId
|
|
170
|
+
: projectConfig.defaultArchetypeID)
|
|
171
|
+
)
|
|
172
|
+
if (index < 0) {
|
|
173
|
+
index = 0
|
|
174
|
+
}
|
|
175
|
+
this.selectArchetypeByIndex(index)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
async _loadMinimaps() {
|
|
179
|
+
return Promise.all(
|
|
180
|
+
this.archetypes.map(async (a) => {
|
|
181
|
+
try {
|
|
182
|
+
const metadata = (
|
|
183
|
+
await axios.get(this.resolveApartmentFile('/minimap/minimap.json'))
|
|
184
|
+
).data
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
let sunsimFilesByStorey = metadata.storeys.map((item) => { return item.sunsimFiles })
|
|
188
|
+
|
|
189
|
+
let sunsimFiles = []
|
|
190
|
+
|
|
191
|
+
for (let index = 0; index < sunsimFilesByStorey.length; index++) {
|
|
192
|
+
const element = sunsimFilesByStorey[index];
|
|
193
|
+
const fileUrls = element.map(name => this.resolveApartmentFile(`/minimap/${name}`))
|
|
194
|
+
sunsimFiles = [...sunsimFiles, fileUrls]
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const minimapData = { data: metadata.storeys, sunsimFiles }
|
|
198
|
+
return { ...a, minimapData }
|
|
199
|
+
} catch {
|
|
200
|
+
return a
|
|
201
|
+
}
|
|
202
|
+
})
|
|
203
|
+
)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
startPlayer(domElement, onStartedCb) {
|
|
207
|
+
const player = new PlayerConfig({
|
|
208
|
+
target: domElement,
|
|
209
|
+
addBuildVersionToUrls: this._addBuildVersionToUrls,
|
|
210
|
+
fileResolver: (name) => this.resolveApartmentFile(name),
|
|
211
|
+
onPlayerCameraChanged: (cameraID) => this.playerCameraChangedTo(cameraID),
|
|
212
|
+
panoramaOrientation: this._state._panoramaOrientation,
|
|
213
|
+
mouseSensitivityFactor: this._mouseSensitivityFactor
|
|
214
|
+
})
|
|
215
|
+
|
|
216
|
+
player.start(this._initData, this.getStartCameraId(), onStartedCb)
|
|
217
|
+
return player
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
playerCameraChangedTo(cameraId) {
|
|
221
|
+
this._navigator.selectedCameraId = cameraId
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
selectArchetypeByIndex(index) {
|
|
225
|
+
this._state._selectedArchetypeIndex = index
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
get isInitialized() {
|
|
229
|
+
return this._state._initialized
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
get viewMode() {
|
|
233
|
+
return this._state._viewMode
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
get availableViewModes() {
|
|
237
|
+
return this._state._availableViewModes
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
get isViewModeVirtualTour() {
|
|
241
|
+
return this._state._viewMode === ViewModeVirtualTour
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
get isViewModeSlideShow() {
|
|
245
|
+
return this._state._viewMode === ViewModeSlideShow
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
nextViewMode() {
|
|
249
|
+
if (this._state._availableViewModes.length > 1) {
|
|
250
|
+
const currentIndex = this._state._availableViewModes.findIndex(
|
|
251
|
+
(m) => m === this._state._viewMode
|
|
252
|
+
)
|
|
253
|
+
if (currentIndex === this._state._availableViewModes.length - 1) {
|
|
254
|
+
this._state._viewMode = this._state._availableViewModes[0]
|
|
255
|
+
} else {
|
|
256
|
+
this._state._viewMode =
|
|
257
|
+
this._state._availableViewModes[currentIndex + 1]
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
getStartCameraId() {
|
|
263
|
+
if (this._navigator.selectedCameraId)
|
|
264
|
+
return this._navigator.selectedCameraId
|
|
265
|
+
else throw new Error('Unable to detect a starting camera for virtual tour')
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
resolveCdnApartment() {
|
|
269
|
+
return `/${this.archetypeId}/${this.apartmentId}`
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
getFilename(name) {
|
|
273
|
+
|
|
274
|
+
if (this._state.adminMode) {
|
|
275
|
+
return this._cdnFileResolver(name) + `?v=${Date.now()}`
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
return this._cdnFileResolver(name)
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
resolveApartmentFile(name) {
|
|
284
|
+
if (!name.startsWith('/')) {
|
|
285
|
+
name = '/' + name
|
|
286
|
+
}
|
|
287
|
+
return this.getFilename(this.resolveCdnApartment() + name)
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
get apartmentId() {
|
|
291
|
+
return this._apartmentId
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
get archetype() {
|
|
295
|
+
const archetype = this.archetypes
|
|
296
|
+
? this.archetypes[this._state._selectedArchetypeIndex]
|
|
297
|
+
: undefined
|
|
298
|
+
return archetype
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
get minimapData() {
|
|
302
|
+
return this.archetype && this.archetype.minimapData
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
get archetypeId() {
|
|
306
|
+
const archetype = this.archetypes
|
|
307
|
+
? this.archetypes[this._state._selectedArchetypeIndex]
|
|
308
|
+
: undefined
|
|
309
|
+
return archetype && archetype.id
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
get selectedArchetypeIndex() {
|
|
313
|
+
return this._state._selectedArchetypeIndex
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
get websiteUrl() {
|
|
317
|
+
return this._websiteUrl
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
get archetypes() {
|
|
321
|
+
return this._state._archetypes
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
get thumbnails() {
|
|
325
|
+
if (this._state._viewMode === ViewModeManualSlideShow) {
|
|
326
|
+
return this._manualSlideshowImages
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
return this._navigator.waypoints.map((thumb) => ({
|
|
330
|
+
id: thumb.id,
|
|
331
|
+
url: this.getWaypointThumbnailUrl(this.archetypeId, thumb.id),
|
|
332
|
+
}))
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
getWaypointThumbnailUrl(interiorId, cameraId) {
|
|
336
|
+
return this.getFilename(
|
|
337
|
+
`/${interiorId}/${this.apartmentId}/thumbs/200/${cameraId}.jpg`
|
|
338
|
+
)
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
get selectedWaypointImageUrl() {
|
|
342
|
+
const selectedWaypoint = this._navigator.selectedWaypoint
|
|
343
|
+
|
|
344
|
+
if (this._state._viewMode === ViewModeManualSlideShow && selectedWaypoint) {
|
|
345
|
+
return selectedWaypoint.url
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
return selectedWaypoint
|
|
349
|
+
? this.getFilename(
|
|
350
|
+
`/${this.archetypeId}/${this.apartmentId}/images/1280/${selectedWaypoint.id}.jpg`
|
|
351
|
+
)
|
|
352
|
+
: undefined
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
get panoramaOrientation() {
|
|
356
|
+
return this._state._panoramaOrientation
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
get cameraSnapshot() {
|
|
360
|
+
return this._state._cameraSnapshot.latest
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
set cameraSnapshot(value) {
|
|
364
|
+
this._state._cameraSnapshot.latest = value
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
get cameraSnapshotRequested() {
|
|
368
|
+
return this._state._cameraSnapshot.requested
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
get allCameras() {
|
|
372
|
+
return this._state._cameras
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
get showCompass() {
|
|
376
|
+
return this._showCompass
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
get showMiniMap() {
|
|
380
|
+
return this._showMiniMap
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
get waypointNavigator() {
|
|
384
|
+
return this._navigator
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
get timeOfDay() {
|
|
388
|
+
return this._state._timeOfDay
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
get hasInformationButtonCallback() {
|
|
392
|
+
return !!this._onInformationButtonPress
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
get onInformationButtonPress() {
|
|
396
|
+
return this._onInformationButtonPress
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
class CdnPlayerViewModel extends PlayerViewModel {
|
|
401
|
+
constructor(modelParams) {
|
|
402
|
+
super({
|
|
403
|
+
...modelParams,
|
|
404
|
+
cdnFileResolver: (name) => modelParams.cdnRoot + name,
|
|
405
|
+
})
|
|
406
|
+
this._cdnRoot = modelParams.cdnRoot
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
dehydrate() {
|
|
410
|
+
return {
|
|
411
|
+
cdnRoot: this._cdnRoot,
|
|
412
|
+
apartmentId: this._apartmentId,
|
|
413
|
+
archetypes: this._state._archetypes,
|
|
414
|
+
selectedArchetypeIndex: this._state._selectedArchetypeIndex,
|
|
415
|
+
websiteUrl: this._websiteUrl,
|
|
416
|
+
unitViewMode: this._unitViewMode,
|
|
417
|
+
showCompass: this._showCompass,
|
|
418
|
+
showMiniMap: this._showMiniMap,
|
|
419
|
+
timeOfDay: this._timeOfDay,
|
|
420
|
+
onInformationButtonPress: this._onInformationButtonPress,
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}
|