#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:
@@ -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() {
|
||||
|
||||
@@ -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'
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user