#Integration: Implemented getViews and setViews at viewer API level. They still use the old 'way of working' but changing that will be trivial, since views are now part of the world tree liek everything else. Implemented canonical views, implemented screenshot. All of these are controllable from the sandbox for testing

This commit is contained in:
AlexandruPopovici
2022-08-12 16:34:04 +03:00
parent 3e194deb15
commit 66cd68077d
5 changed files with 192 additions and 8 deletions
+56
View File
@@ -10,6 +10,8 @@ export default class Sandbox {
private tabs
private filterControls
private steamsFolder
private viewsControls = []
private viewsFolder
private streams: { [url: string]: Array<unknown> } = {}
public static urlParams = {
@@ -47,6 +49,17 @@ export default class Sandbox {
viewer.on('load-complete', (url: string) => {
this.addStreamControls(url)
this.addViewControls()
})
viewer.on('unload-complete', (url: string) => {
this.removeViewControls()
this.addViewControls()
url
})
viewer.on('unload-all-complete', (url: string) => {
this.removeViewControls()
this.addViewControls()
url
})
}
@@ -89,6 +102,27 @@ export default class Sandbox {
delete this.streams[url]
}
private addViewControls() {
const views = this.viewer.getViews()
this.viewsFolder = this.tabs.pages[0].addFolder({
title: 'Views',
expanded: true
})
for (let k = 0; k < views.length; k++) {
this.viewsFolder
.addButton({
title: views[k].name
})
.on('click', () => {
this.viewer.setView(views[k].id)
})
}
}
private removeViewControls() {
this.viewsFolder.dispose()
}
public makeGenericUI() {
this.tabs.pages[0].addInput(Sandbox.urlParams, 'url', {
title: 'url'
@@ -148,6 +182,28 @@ export default class Sandbox {
showBatches.on('click', () => {
this.viewer.speckleRenderer.debugShowBatches()
})
const screenshot = this.tabs.pages[0].addButton({
title: 'Screenshot'
})
screenshot.on('click', async () => {
console.warn(await this.viewer.screenshot())
})
const canonicalViewsFolder = this.tabs.pages[0].addFolder({
title: 'Canonical Views',
expanded: true
})
const sides = ['front', 'back', 'top', 'bottom', 'right', 'left', '3d']
for (let k = 0; k < sides.length; k++) {
canonicalViewsFolder
.addButton({
title: sides[k]
})
.on('click', () => {
this.viewer.rotateTo(sides[k])
})
}
}
makeSceneUI() {
+3 -1
View File
@@ -40,4 +40,6 @@ sandbox.makeGenericUI()
sandbox.makeSceneUI()
sandbox.makeFilteringUI()
// Load demo object
sandbox.loadUrl('https://latest.speckle.dev/streams/ca0378725b/commits/e5a66562cc')
sandbox.loadUrl(
'https://latest.speckle.dev/streams/92b620fb17/commits/0ffebbd2f6?c=%5B-30.45977,17.60411,23.71672,-2.20374,1.01037,3.58707,0,1%5D'
)
+6
View File
@@ -47,12 +47,18 @@ export interface IViewer {
sectionBoxOn(): void
zoomExtents(fit?: number, transition?: boolean): void
toggleCameraProjection(): void
getViews()
setView(id: string, transition: boolean)
// This shouldn't be part of the API, it should be handled through `setView`
rotateTo(side: string, transition: boolean)
loadObject(url: string, token?: string, enableCaching?: boolean): Promise<void>
cancelLoad(url: string, unload?: boolean): Promise<void>
unloadObject(url: string): Promise<void>
unloadAll(): Promise<void>
screenshot(): Promise<string>
applyFilter(filter: unknown): Promise<unknown>
getObjectsProperties(includeAll?: boolean): unknown
+86 -1
View File
@@ -403,7 +403,7 @@ export default class SpeckleRenderer {
}
/** Taken from InteractionsHandler. Will revisit in the future */
zoomToBox(box, fit = 1.2, transition = true) {
public zoomToBox(box, fit = 1.2, transition = true) {
if (box.max.x === Infinity || box.max.x === -Infinity) {
box = new Box3(new Vector3(-1, -1, -1), new Vector3(1, 1, 1))
}
@@ -452,6 +452,91 @@ export default class SpeckleRenderer {
}
}
public setView(origin: Vector3, target: Vector3, transition = true) {
this.viewer.cameraHandler.activeCam.controls.setLookAt(
origin.x,
origin.y,
origin.z,
target.x,
target.y,
target.z,
transition
)
}
/**
* Rotates camera to some canonical views
* @param {string} side Can be any of front, back, up (top), down (bottom), right, left.
* @param {Number} fit [description]
* @param {Boolean} transition [description]
* @return {[type]} [description]
*/
public rotateTo(side: string, transition = true) {
const DEG90 = Math.PI * 0.5
const DEG180 = Math.PI
switch (side) {
case 'front':
this.viewer.cameraHandler.controls.rotateTo(0, DEG90, transition)
if (this.viewer.cameraHandler.activeCam.name === 'ortho')
this.viewer.cameraHandler.disableRotations()
break
case 'back':
this.viewer.cameraHandler.controls.rotateTo(DEG180, DEG90, transition)
if (this.viewer.cameraHandler.activeCam.name === 'ortho')
this.viewer.cameraHandler.disableRotations()
break
case 'up':
case 'top':
this.viewer.cameraHandler.controls.rotateTo(0, 0, transition)
if (this.viewer.cameraHandler.activeCam.name === 'ortho')
this.viewer.cameraHandler.disableRotations()
break
case 'down':
case 'bottom':
this.viewer.cameraHandler.controls.rotateTo(0, DEG180, transition)
if (this.viewer.cameraHandler.activeCam.name === 'ortho')
this.viewer.cameraHandler.disableRotations()
break
case 'right':
this.viewer.cameraHandler.controls.rotateTo(DEG90, DEG90, transition)
if (this.viewer.cameraHandler.activeCam.name === 'ortho')
this.viewer.cameraHandler.disableRotations()
break
case 'left':
this.viewer.cameraHandler.controls.rotateTo(-DEG90, DEG90, transition)
if (this.viewer.cameraHandler.activeCam.name === 'ortho')
this.viewer.cameraHandler.disableRotations()
break
case '3d':
case '3D':
default: {
let box
if (this.allObjects.children.length === 0)
box = new Box3(new Vector3(-1, -1, -1), new Vector3(1, 1, 1))
else box = new Box3().setFromObject(this.allObjects)
if (box.max.x === Infinity || box.max.x === -Infinity) {
box = new Box3(new Vector3(-1, -1, -1), new Vector3(1, 1, 1))
}
this.viewer.cameraHandler.controls.setPosition(
box.max.x,
box.max.y,
box.max.z,
transition
)
this.zoomExtents()
this.viewer.cameraHandler.enableRotations()
break
}
}
}
/** DEBUG */
public onObjectClickDebug(e) {
const result: Intersection = this.intersections.intersect(
+41 -6
View File
@@ -102,9 +102,6 @@ export class Viewer extends EventEmitter implements IViewer {
WorldTree.getRenderTree(url).buildRenderTree()
this.speckleRenderer.addRenderTree(url)
this.zoomExtents()
const views = this.__getViews()
console.warn(`Found ${views.length} VIEWS:`)
console.warn(views)
console.warn('Built stuff')
})
}
@@ -173,9 +170,45 @@ export class Viewer extends EventEmitter implements IViewer {
this.cameraHandler.toggleCameras()
}
public __getViews() {
return WorldTree.getInstance().findAll((node: TreeNode) => {
return node.model.renderView?.speckleType === SpeckleType.View3D
public getViews() {
return WorldTree.getInstance()
.findAll((node: TreeNode) => {
return node.model.renderView?.speckleType === SpeckleType.View3D
})
.map((v) => {
return {
name: v.model.raw.applicationId,
id: v.model.id,
view: v.model.raw
}
})
}
public setView(id: string, transition: boolean): void {
const view3DNode = WorldTree.getInstance().findId(id)
this.speckleRenderer.setView(
view3DNode.model.raw.origin,
view3DNode.model.raw.target,
transition
)
}
public rotateTo(side: string, transition = true) {
this.speckleRenderer.rotateTo(side)
transition
}
public screenshot(): Promise<string> {
return new Promise((resolve) => {
const sectionBoxVisible = this.sectionBox.display.visible
if (sectionBoxVisible) {
this.sectionBox.displayOff()
}
const screenshot = this.speckleRenderer.renderer.domElement.toDataURL('image/png')
if (sectionBoxVisible) {
this.sectionBox.displayOn()
}
resolve(screenshot)
})
}
@@ -210,6 +243,7 @@ export class Viewer extends EventEmitter implements IViewer {
if (--this.inProgressOperations === 0) {
;(this as EventEmitter).emit('busy', false)
console.warn(`Removed subtree ${url}`)
;(this as EventEmitter).emit('unload-complete', url)
}
}
}
@@ -231,6 +265,7 @@ export class Viewer extends EventEmitter implements IViewer {
if (--this.inProgressOperations === 0) {
;(this as EventEmitter).emit('busy', false)
console.warn(`Removed all subtrees`)
;(this as EventEmitter).emit('unload-all-complete')
}
}
}