Added World to the API. Can be used to check world dimensions and offsets. Cleaned up and added some comment in Viewer. After removing the reference to a lot of issues arose. The frontend and preview-service seems to use it extensively. Changed the calls referencing the interactions to the ones in the API where it was aplicable, and commented out with a note added for the ones that were not aplicable. We proably need to expose a generic way to manipulate the camera from the API
This commit is contained in:
@@ -53,7 +53,7 @@ export default defineComponent({
|
||||
},
|
||||
methods: {
|
||||
setView(view: string) {
|
||||
this.viewer.interactions.rotateTo(view.toLowerCase())
|
||||
this.viewer.rotateTo(view.toLowerCase())
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -405,7 +405,8 @@ export default {
|
||||
sectionBox: this.viewer.sectionBox.getCurrentBox(),
|
||||
selection: null // TODO for later, lazy now
|
||||
},
|
||||
screenshot: this.viewer.interactions.screenshot()
|
||||
//@Dim: Changed this to use the API
|
||||
screenshot: this.viewer.screenshot()
|
||||
}
|
||||
if (this.$route.query.overlay) {
|
||||
commentInput.resources.push(
|
||||
@@ -445,7 +446,8 @@ export default {
|
||||
this.visible = false
|
||||
this.commentValue = { doc: null, attachments: [] }
|
||||
setIsAddingComment(false)
|
||||
this.viewer.interactions.deselectObjects()
|
||||
//@Dim: Changed this to use the API
|
||||
this.viewer.resetSelection()
|
||||
},
|
||||
sendStatusUpdate() {
|
||||
// TODO: typing or not
|
||||
|
||||
@@ -516,10 +516,11 @@ export default {
|
||||
if (camToSet[6] === 1) {
|
||||
this.viewer.toggleCameraProjection()
|
||||
}
|
||||
this.viewer.interactions.setLookAt(
|
||||
{ x: camToSet[0], y: camToSet[1], z: camToSet[2] }, // position
|
||||
{ x: camToSet[3], y: camToSet[4], z: camToSet[5] } // target
|
||||
)
|
||||
//@Dim: This needs to use the API.
|
||||
// this.viewer.interactions.setLookAt(
|
||||
// { x: camToSet[0], y: camToSet[1], z: camToSet[2] }, // position
|
||||
// { x: camToSet[3], y: camToSet[4], z: camToSet[5] } // target
|
||||
// )
|
||||
if (camToSet[6] === 1) {
|
||||
this.viewer.cameraHandler.activeCam.controls.zoom(camToSet[7], true)
|
||||
}
|
||||
|
||||
@@ -254,10 +254,11 @@ export default {
|
||||
if (camToSet[6] === 1) {
|
||||
this.viewer.toggleCameraProjection()
|
||||
}
|
||||
this.viewer.interactions.setLookAt(
|
||||
{ x: camToSet[0], y: camToSet[1], z: camToSet[2] }, // position
|
||||
{ x: camToSet[3], y: camToSet[4], z: camToSet[5] } // target
|
||||
)
|
||||
//@Dim: This needs to use the API
|
||||
// this.viewer.interactions.setLookAt(
|
||||
// { x: camToSet[0], y: camToSet[1], z: camToSet[2] }, // position
|
||||
// { x: camToSet[3], y: camToSet[4], z: camToSet[5] } // target
|
||||
// )
|
||||
if (camToSet[6] === 1) {
|
||||
this.viewer.cameraHandler.activeCam.controls.zoom(camToSet[7], true)
|
||||
}
|
||||
@@ -466,8 +467,8 @@ export default {
|
||||
uArrowEl.style.transform = `translate(${newTarget.x}px,${newTarget.y}px) rotate(${angle}rad)`
|
||||
uArrowEl.style.opacity = user.clipped ? '0' : '1'
|
||||
}
|
||||
|
||||
this.viewer.interactions.overlayObjects(selectedObjects)
|
||||
//@Dim: This shouldn't be needed anymore, right?
|
||||
// this.viewer.interactions.overlayObjects(selectedObjects)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -727,6 +727,6 @@ export async function resetFilter() {
|
||||
currentFilterState: null
|
||||
})
|
||||
|
||||
await viewer.reset()
|
||||
await viewer.resetFilters()
|
||||
viewer.applyFilter(null)
|
||||
}
|
||||
|
||||
@@ -269,6 +269,7 @@ import { Nullable } from '@/helpers/typeHelpers'
|
||||
import { getCamArray } from '@/main/lib/viewer/core/helpers/cameraHelper'
|
||||
import CommitObjectViewerScope from '@/main/components/viewer/CommitObjectViewerScope.vue'
|
||||
import PrioritizedPortal from '@/main/components/common/utility/PrioritizedPortal.vue'
|
||||
import { ViewerEvent } from '@speckle/viewer'
|
||||
|
||||
type ErroredResourceData = {
|
||||
error: boolean
|
||||
@@ -515,7 +516,7 @@ export default defineComponent({
|
||||
}
|
||||
}
|
||||
|
||||
this.viewer.on('busy', (val: boolean) => {
|
||||
this.viewer.on(ViewerEvent.Busy, (val: boolean) => {
|
||||
setIsViewerBusy(!!val)
|
||||
this.viewerBusy = val
|
||||
if (!val && this.camToSet) {
|
||||
@@ -525,12 +526,14 @@ export default defineComponent({
|
||||
if (this.camToSet[6] === 1) {
|
||||
this.viewer.toggleCameraProjection()
|
||||
}
|
||||
this.viewer.interactions.setLookAt(
|
||||
{ x: this.camToSet[0], y: this.camToSet[1], z: this.camToSet[2] }, // position
|
||||
{ x: this.camToSet[3], y: this.camToSet[4], z: this.camToSet[5] } // target
|
||||
)
|
||||
//@Dim: This needs to be replaced with a call from the API.
|
||||
// this.viewer.interactions.setLookAt(
|
||||
// { x: this.camToSet[0], y: this.camToSet[1], z: this.camToSet[2] }, // position
|
||||
// { x: this.camToSet[3], y: this.camToSet[4], z: this.camToSet[5] } // target
|
||||
// )
|
||||
if (this.camToSet[6] === 1) {
|
||||
this.viewer.cameraHandler.activeCam.controls.zoom(this.camToSet[7], true)
|
||||
//@Dim: This needs to be replaced with a call from the API.
|
||||
// this.viewer.cameraHandler.activeCam.controls.zoom(this.camToSet[7], true)
|
||||
}
|
||||
this.camToSet = null
|
||||
}, 200)
|
||||
@@ -726,7 +729,7 @@ export default defineComponent({
|
||||
async setFilters() {
|
||||
try {
|
||||
// repopulate object props
|
||||
this.objectProperties = await this.viewer.getObjectsProperties()
|
||||
this.objectProperties = this.viewer.getObjectProperties()
|
||||
} catch (e) {
|
||||
this.$eventHub.$emit('notification', {
|
||||
text: 'Failed to get object properties from viewer.'
|
||||
|
||||
@@ -26,15 +26,17 @@ async function pageFunction(objectUrl) {
|
||||
// Main call failed. Wait some time for other objects to load inside the viewer and generate the preview anyway
|
||||
await waitForAnimation(1000)
|
||||
}
|
||||
|
||||
v.interactions.zoomExtents(0.95, false)
|
||||
//@Dim: This needs to use the API
|
||||
// v.interactions.zoomExtents(0.95, false)
|
||||
await waitForAnimation(100)
|
||||
|
||||
// full 360
|
||||
for (let i = 0; i < 24; i++) {
|
||||
v.interactions.rotateCamera(undefined, undefined, false)
|
||||
//@Dim: This needs to use the API
|
||||
// v.interactions.rotateCamera(undefined, undefined, false)
|
||||
await waitForAnimation()
|
||||
ret.scr[i + ''] = v.interactions.screenshot()
|
||||
//@Dim: Changed this to use the API
|
||||
ret.scr[i + ''] = v.screenshot()
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
|
||||
import './style.css'
|
||||
import Sandbox from './Sandbox'
|
||||
import { IViewer } from '@speckle/viewer'
|
||||
|
||||
const container = document.querySelector<HTMLElement>('#renderer')
|
||||
if (!container) {
|
||||
@@ -20,7 +21,7 @@ params.showStats = true
|
||||
// 'https://speckle-xyz-assets.ams3.digitaloceanspaces.com/studio010.hdr'
|
||||
// 'http://localhost:3033/sample-hdri.exr'
|
||||
|
||||
const viewer = new Viewer(container, params)
|
||||
const viewer: IViewer = new Viewer(container, params)
|
||||
await viewer.init()
|
||||
|
||||
const sandbox = new Sandbox(viewer)
|
||||
@@ -39,8 +40,8 @@ viewer.on(
|
||||
)
|
||||
|
||||
viewer.on(ViewerEvent.LoadComplete, () => {
|
||||
Object.assign(Sandbox.sceneParams.worldSize, viewer.worldSize)
|
||||
Object.assign(Sandbox.sceneParams.worldOrigin, viewer.worldOrigin)
|
||||
Object.assign(Sandbox.sceneParams.worldSize, viewer.World.worldSize)
|
||||
Object.assign(Sandbox.sceneParams.worldOrigin, viewer.World.worldOrigin)
|
||||
sandbox.refresh()
|
||||
})
|
||||
|
||||
@@ -75,11 +76,3 @@ await sandbox.loadUrl(
|
||||
// 'https://latest.speckle.dev/streams/444bfbd6e4/commits/e22f696b08'
|
||||
// 'https://latest.speckle.dev/streams/92b620fb17/commits/af6098915b?c=%5B0.02144,-0.0377,0.05554,0.00566,0.00236,0,0,1%5D'
|
||||
)
|
||||
|
||||
// const dataTree: DataTree = viewer.getDataTree()
|
||||
// console.log(`Built data tree `, dataTree)
|
||||
// console.log(
|
||||
// dataTree.findAll((obj: SpeckleObject) => {
|
||||
// return obj.speckle_type === 'Objects.Geometry.Mesh'
|
||||
// })
|
||||
// )
|
||||
|
||||
@@ -3,6 +3,7 @@ import sampleHdri from './assets/sample-hdri.png'
|
||||
import { FilteringState } from './modules/filtering/FilteringManager'
|
||||
import { PropertyInfo } from './modules/filtering/PropertyManager'
|
||||
import { DataTree } from './modules/tree/DataTree'
|
||||
import { World } from './modules/World'
|
||||
|
||||
export interface ViewerParams {
|
||||
postprocessing: boolean
|
||||
@@ -48,7 +49,8 @@ export enum ViewerEvent {
|
||||
LoadComplete = 'load-complete',
|
||||
LoadProgress = 'load-progress',
|
||||
UnloadComplete = 'unload-complete',
|
||||
UnloadAllComplete = 'unload-all-complete'
|
||||
UnloadAllComplete = 'unload-all-complete',
|
||||
Busy = 'busy'
|
||||
}
|
||||
|
||||
export type SelectionEvent = {
|
||||
@@ -86,7 +88,6 @@ export const DefaultLightConfiguration: SunLightConfiguration = {
|
||||
*/
|
||||
export interface IViewer {
|
||||
init(): Promise<void>
|
||||
onWindowResize(): void
|
||||
on(eventType: ViewerEvent, handler: (arg) => void)
|
||||
toggleSectionBox(): void
|
||||
sectionBoxOff(): void
|
||||
@@ -144,6 +145,7 @@ export interface IViewer {
|
||||
|
||||
/** Data ops */
|
||||
getDataTree(): DataTree
|
||||
get World(): World
|
||||
|
||||
dispose(): void
|
||||
}
|
||||
|
||||
@@ -15,8 +15,9 @@ import {
|
||||
|
||||
import { SunLightConfiguration } from './IViewer'
|
||||
import { DataTree, ObjectPredicate, SpeckleObject } from './modules/tree/DataTree'
|
||||
import { World } from './modules/World'
|
||||
|
||||
export { Viewer, DefaultViewerParams, ViewerEvent, DefaultLightConfiguration }
|
||||
export { Viewer, DefaultViewerParams, ViewerEvent, DefaultLightConfiguration, World }
|
||||
|
||||
export type {
|
||||
IViewer,
|
||||
|
||||
@@ -133,12 +133,14 @@ export default class SectionBox {
|
||||
const val = !!event.value
|
||||
if (val) {
|
||||
this.dragging = val
|
||||
this.viewer.interactions.preventSelection = val
|
||||
//@Dim: Not sure what this needs to do in the new viewer
|
||||
// this.viewer.interactions.preventSelection = val
|
||||
this.viewer.cameraHandler.enabled = !val
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
this.dragging = val
|
||||
this.viewer.interactions.preventSelection = val
|
||||
//@Dim: Not sure what this needs to do in the new viewer
|
||||
// this.viewer.interactions.preventSelection = val
|
||||
this.viewer.cameraHandler.enabled = !val
|
||||
}, 100)
|
||||
}
|
||||
@@ -351,9 +353,10 @@ export default class SectionBox {
|
||||
|
||||
if (targetBox) box = targetBox
|
||||
else {
|
||||
/* //@Dim: Not sure what this needs to do in the new viewer
|
||||
if (this.viewer.interactions.selectedObjects.children.length !== 0) {
|
||||
box = new THREE.Box3().setFromObject(this.viewer.interactions.selectedObjects)
|
||||
} else if (this.viewer.speckleRenderer.allObjects.children.length !== 0) {
|
||||
} else*/ if (this.viewer.speckleRenderer.allObjects.children.length !== 0) {
|
||||
box = new THREE.Box3().setFromObject(this.viewer.speckleRenderer.allObjects)
|
||||
} else {
|
||||
box = new Box3(new THREE.Vector3(-1, -1, -1), new THREE.Vector3(1, 1, 1))
|
||||
|
||||
@@ -3,11 +3,10 @@ import Stats from 'three/examples/jsm/libs/stats.module'
|
||||
|
||||
import ViewerObjectLoader from './ViewerObjectLoader'
|
||||
import EventEmitter from './EventEmitter'
|
||||
import InteractionHandler from './legacy/InteractionHandler'
|
||||
import CameraHandler from './context/CameraHanlder'
|
||||
|
||||
import SectionBox from './SectionBox'
|
||||
import { Clock, Color, MathUtils, Texture, Vector3 } from 'three'
|
||||
import { Clock, Texture } from 'three'
|
||||
import { Assets } from './Assets'
|
||||
import { Optional } from '../helpers/typeHelper'
|
||||
import {
|
||||
@@ -21,32 +20,34 @@ import {
|
||||
import { World } from './World'
|
||||
import { TreeNode, WorldTree } from './tree/WorldTree'
|
||||
import SpeckleRenderer from './SpeckleRenderer'
|
||||
import {
|
||||
FilterMaterialType,
|
||||
FilteringManager,
|
||||
FilteringState
|
||||
} from './filtering/FilteringManager'
|
||||
import { FilteringManager, FilteringState } from './filtering/FilteringManager'
|
||||
import { PropertyInfo, PropertyManager } from './filtering/PropertyManager'
|
||||
import { SpeckleType } from './converter/GeometryConverter'
|
||||
import { DataTree } from './tree/DataTree'
|
||||
|
||||
export class Viewer extends EventEmitter implements IViewer {
|
||||
public speckleRenderer: SpeckleRenderer
|
||||
private clock: Clock
|
||||
/** Container and optional stats element */
|
||||
private container: HTMLElement
|
||||
private stats: Optional<Stats>
|
||||
private loaders: { [id: string]: ViewerObjectLoader } = {}
|
||||
private _needsRender: boolean
|
||||
private inProgressOperations: number
|
||||
|
||||
public sectionBox: SectionBox
|
||||
public interactions: InteractionHandler
|
||||
public cameraHandler: CameraHandler
|
||||
/** Viewer params used at init time */
|
||||
private startupParams: ViewerParams
|
||||
|
||||
/** Viewer components */
|
||||
public static Assets: Assets
|
||||
|
||||
private speckleRenderer: SpeckleRenderer
|
||||
private filteringManager: FilteringManager
|
||||
/** Legacy viewer components (will revisit soon) */
|
||||
public sectionBox: SectionBox
|
||||
public cameraHandler: CameraHandler
|
||||
|
||||
/** Render flag for on-demand rendering */
|
||||
private _needsRender: boolean
|
||||
|
||||
/** Misc members */
|
||||
private inProgressOperations: number
|
||||
private clock: Clock
|
||||
private loaders: { [id: string]: ViewerObjectLoader } = {}
|
||||
|
||||
public get needsRender(): boolean {
|
||||
return this._needsRender
|
||||
@@ -56,19 +57,9 @@ export class Viewer extends EventEmitter implements IViewer {
|
||||
this._needsRender = value || this._needsRender
|
||||
}
|
||||
|
||||
private _worldOrigin: Vector3 = new Vector3()
|
||||
public get worldSize() {
|
||||
World.worldBox.getCenter(this._worldOrigin)
|
||||
const size = new Vector3().subVectors(World.worldBox.max, World.worldBox.min)
|
||||
return {
|
||||
x: size.x,
|
||||
y: size.y,
|
||||
z: size.z
|
||||
}
|
||||
}
|
||||
|
||||
public get worldOrigin() {
|
||||
return this._worldOrigin
|
||||
/** Gets the World object. Currently it's used for statistics mostly */
|
||||
public get World(): World {
|
||||
return World
|
||||
}
|
||||
|
||||
public constructor(
|
||||
@@ -77,54 +68,84 @@ export class Viewer extends EventEmitter implements IViewer {
|
||||
) {
|
||||
super()
|
||||
|
||||
window.THREE = THREE // Do we really need this?
|
||||
this.startupParams = params
|
||||
this.clock = new THREE.Clock()
|
||||
|
||||
this.container = container || document.getElementById('renderer')
|
||||
|
||||
this.speckleRenderer = new SpeckleRenderer(this)
|
||||
this.speckleRenderer.create(this.container)
|
||||
new Assets(this.speckleRenderer.renderer)
|
||||
|
||||
this.cameraHandler = new CameraHandler(this)
|
||||
|
||||
if (params.showStats) {
|
||||
this.stats = Stats()
|
||||
this.container.appendChild(this.stats.dom)
|
||||
}
|
||||
this.loaders = {}
|
||||
this.startupParams = params
|
||||
this.clock = new THREE.Clock()
|
||||
this.inProgressOperations = 0
|
||||
|
||||
this.speckleRenderer = new SpeckleRenderer(this)
|
||||
this.speckleRenderer.create(this.container)
|
||||
window.addEventListener('resize', this.onWindowResize.bind(this), false)
|
||||
|
||||
this.loaders = {}
|
||||
new Assets(this.speckleRenderer.renderer)
|
||||
this.filteringManager = new FilteringManager(this.speckleRenderer)
|
||||
|
||||
this.cameraHandler = new CameraHandler(this)
|
||||
this.sectionBox = new SectionBox(this)
|
||||
this.sectionBox.off()
|
||||
this.sectionBox.controls.addEventListener('change', () => {
|
||||
this.speckleRenderer.updateClippingPlanes(this.sectionBox.planes)
|
||||
})
|
||||
|
||||
this.interactions = new InteractionHandler(this)
|
||||
|
||||
this.frame()
|
||||
this.onWindowResize()
|
||||
this.needsRender = true
|
||||
|
||||
this.inProgressOperations = 0
|
||||
|
||||
this.on(ViewerEvent.LoadComplete, (url) => {
|
||||
WorldTree.getRenderTree(url).buildRenderTree()
|
||||
this.speckleRenderer.addRenderTree(url)
|
||||
this.zoomExtents()
|
||||
})
|
||||
}
|
||||
|
||||
this.filteringManager = new FilteringManager(this.speckleRenderer)
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
// ;(window as any).WT = WorldTree
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
// ;(window as any).FM = this.filteringManager
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
// ;(window as any).V = this
|
||||
private onWindowResize() {
|
||||
this.speckleRenderer.renderer.setSize(
|
||||
this.container.offsetWidth,
|
||||
this.container.offsetHeight
|
||||
)
|
||||
this.needsRender = true
|
||||
}
|
||||
|
||||
private frame() {
|
||||
this.update()
|
||||
this.render()
|
||||
}
|
||||
|
||||
private update() {
|
||||
const delta = this.clock.getDelta()
|
||||
this.needsRender = this.cameraHandler.controls.update(delta)
|
||||
this.speckleRenderer.update(delta)
|
||||
this.stats?.update()
|
||||
requestAnimationFrame(this.frame.bind(this))
|
||||
}
|
||||
|
||||
private render() {
|
||||
if (this.needsRender) {
|
||||
this.speckleRenderer.render(this.cameraHandler.activeCam.camera)
|
||||
this._needsRender = false
|
||||
}
|
||||
}
|
||||
|
||||
public async init(): Promise<void> {
|
||||
if (this.startupParams.environmentSrc) {
|
||||
Assets.getEnvironment(this.startupParams.environmentSrc)
|
||||
.then((value: Texture) => {
|
||||
this.speckleRenderer.indirectIBL = value
|
||||
})
|
||||
.catch((reason) => {
|
||||
console.warn(reason)
|
||||
console.warn('Fallback to null environment!')
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
public on(eventType: ViewerEvent, listener: (arg) => void): void {
|
||||
super.on(eventType, listener)
|
||||
}
|
||||
|
||||
public getObjectProperties(resourceURL: string = null): PropertyInfo[] {
|
||||
@@ -225,54 +246,19 @@ export class Viewer extends EventEmitter implements IViewer {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* LEGACY: Handles (or tries to handle) old viewer filtering.
|
||||
* @param args legacy filter object
|
||||
*/
|
||||
public async applyFilter(filter: unknown) {
|
||||
filter
|
||||
// return this.FilteringManager.handleLegacyFilter(filter)
|
||||
}
|
||||
|
||||
public getDataTree(): DataTree {
|
||||
return WorldTree.getDataTree()
|
||||
}
|
||||
|
||||
public async init(): Promise<void> {
|
||||
if (this.startupParams.environmentSrc) {
|
||||
Assets.getEnvironment(this.startupParams.environmentSrc)
|
||||
.then((value: Texture) => {
|
||||
this.speckleRenderer.indirectIBL = value
|
||||
})
|
||||
.catch((reason) => {
|
||||
console.warn(reason)
|
||||
console.warn('Fallback to null environment!')
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
public on(eventType: ViewerEvent, listener: (arg) => void): void {
|
||||
super.on(eventType, listener)
|
||||
}
|
||||
|
||||
public onWindowResize() {
|
||||
this.speckleRenderer.renderer.setSize(
|
||||
this.container.offsetWidth,
|
||||
this.container.offsetHeight
|
||||
)
|
||||
this.needsRender = true
|
||||
}
|
||||
|
||||
private frame() {
|
||||
this.update()
|
||||
this.render()
|
||||
}
|
||||
|
||||
private update() {
|
||||
const delta = this.clock.getDelta()
|
||||
this.needsRender = this.cameraHandler.controls.update(delta)
|
||||
this.speckleRenderer.update(delta)
|
||||
this.stats?.update()
|
||||
requestAnimationFrame(this.frame.bind(this))
|
||||
}
|
||||
|
||||
private render() {
|
||||
if (this.needsRender) {
|
||||
this.speckleRenderer.render(this.cameraHandler.activeCam.camera)
|
||||
}
|
||||
}
|
||||
|
||||
public toggleSectionBox() {
|
||||
this.sectionBox.toggle()
|
||||
}
|
||||
@@ -342,15 +328,20 @@ export class Viewer extends EventEmitter implements IViewer {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* OBJECT LOADING/UNLOADING
|
||||
*/
|
||||
public async loadObject(url: string, token: string = null, enableCaching = true) {
|
||||
try {
|
||||
if (++this.inProgressOperations === 1) (this as EventEmitter).emit('busy', true)
|
||||
if (++this.inProgressOperations === 1)
|
||||
(this as EventEmitter).emit(ViewerEvent.Busy, true)
|
||||
|
||||
const loader = new ViewerObjectLoader(this, url, token, enableCaching)
|
||||
this.loaders[url] = loader
|
||||
await loader.load()
|
||||
} finally {
|
||||
if (--this.inProgressOperations === 0) (this as EventEmitter).emit('busy', false)
|
||||
if (--this.inProgressOperations === 0)
|
||||
(this as EventEmitter).emit(ViewerEvent.Busy, false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -364,14 +355,15 @@ export class Viewer extends EventEmitter implements IViewer {
|
||||
|
||||
public async unloadObject(url: string) {
|
||||
try {
|
||||
if (++this.inProgressOperations === 1) (this as EventEmitter).emit('busy', true)
|
||||
if (++this.inProgressOperations === 1)
|
||||
(this as EventEmitter).emit(ViewerEvent.Busy, true)
|
||||
delete this.loaders[url]
|
||||
this.speckleRenderer.removeRenderTree(url)
|
||||
WorldTree.getRenderTree(url).purge()
|
||||
WorldTree.getInstance().purge(url)
|
||||
} finally {
|
||||
if (--this.inProgressOperations === 0) {
|
||||
;(this as EventEmitter).emit('busy', false)
|
||||
;(this as EventEmitter).emit(ViewerEvent.Busy, false)
|
||||
console.warn(`Removed subtree ${url}`)
|
||||
;(this as EventEmitter).emit(ViewerEvent.UnloadComplete, url)
|
||||
}
|
||||
@@ -380,7 +372,8 @@ export class Viewer extends EventEmitter implements IViewer {
|
||||
|
||||
public async unloadAll() {
|
||||
try {
|
||||
if (++this.inProgressOperations === 1) (this as EventEmitter).emit('busy', true)
|
||||
if (++this.inProgressOperations === 1)
|
||||
(this as EventEmitter).emit(ViewerEvent.Busy, true)
|
||||
for (const key of Object.keys(this.loaders)) {
|
||||
delete this.loaders[key]
|
||||
}
|
||||
@@ -393,198 +386,13 @@ export class Viewer extends EventEmitter implements IViewer {
|
||||
WorldTree.getInstance().purge()
|
||||
} finally {
|
||||
if (--this.inProgressOperations === 0) {
|
||||
;(this as EventEmitter).emit('busy', false)
|
||||
;(this as EventEmitter).emit(ViewerEvent.Busy, false)
|
||||
console.warn(`Removed all subtrees`)
|
||||
;(this as EventEmitter).emit(ViewerEvent.UnloadAllComplete)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* LEGACY: Handles (or tries to handle) old viewer filtering.
|
||||
* @param args legacy filter object
|
||||
*/
|
||||
public async applyFilter(filter: unknown) {
|
||||
filter
|
||||
// return this.FilteringManager.handleLegacyFilter(filter)
|
||||
}
|
||||
|
||||
public debugGetFilterByNumericPropetyData(propertyName: string): {
|
||||
min: number
|
||||
max: number
|
||||
nodes: TreeNode[]
|
||||
} {
|
||||
const volumeNodes = []
|
||||
let min = Infinity
|
||||
let max = 0
|
||||
WorldTree.getInstance().walk((node: TreeNode) => {
|
||||
const params = node.model.raw.parameters
|
||||
if (params) {
|
||||
for (const k in params) {
|
||||
if (!(params[k] instanceof Object)) continue
|
||||
if (params[k].name === propertyName) {
|
||||
min = Math.min(min, params[k].value)
|
||||
max = Math.max(max, params[k].value)
|
||||
volumeNodes.push(node)
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
return {
|
||||
min,
|
||||
max,
|
||||
nodes: volumeNodes
|
||||
}
|
||||
}
|
||||
|
||||
public debugApplyByNumericPropetyFilter(
|
||||
data: { min: number; max: number; nodes: TreeNode[] },
|
||||
propertyName: string,
|
||||
min?: number,
|
||||
max?: number
|
||||
) {
|
||||
const start = performance.now()
|
||||
const nodesGradient = []
|
||||
const nodesGhost = []
|
||||
const values = []
|
||||
|
||||
/** This is the lazy approach */
|
||||
WorldTree.getInstance().walk((node: TreeNode) => {
|
||||
const params = node.model.raw.parameters
|
||||
if (params) {
|
||||
for (const k in params) {
|
||||
if (!(params[k] instanceof Object)) continue
|
||||
if (params[k].name === propertyName) {
|
||||
const propertyValue = params[k].value
|
||||
const passMin = min !== undefined ? propertyValue >= min : true
|
||||
const passMax = max !== undefined ? propertyValue <= max : true
|
||||
if (
|
||||
data.nodes.includes(node) &&
|
||||
passMin &&
|
||||
passMax &&
|
||||
!nodesGradient.includes(node)
|
||||
) {
|
||||
nodesGradient.push(node)
|
||||
values.push(propertyValue)
|
||||
}
|
||||
} else {
|
||||
if (!nodesGhost.includes(node)) nodesGhost.push(node)
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
this.speckleRenderer.clearFilter()
|
||||
this.speckleRenderer.beginFilter()
|
||||
const ghostRvs = []
|
||||
for (let k = 0; k < nodesGhost.length; k++) {
|
||||
ghostRvs.push(
|
||||
...WorldTree.getRenderTree().getRenderViewsForNode(nodesGhost[k], nodesGhost[k])
|
||||
)
|
||||
}
|
||||
this.speckleRenderer.applyFilter(ghostRvs, {
|
||||
filterType: FilterMaterialType.GHOST
|
||||
})
|
||||
|
||||
for (let k = 0; k < nodesGradient.length; k++) {
|
||||
const rvs = WorldTree.getRenderTree().getRenderViewsForNode(
|
||||
nodesGradient[k],
|
||||
nodesGradient[k]
|
||||
)
|
||||
// .map((value) => value.renderData.id)
|
||||
const t = (values[k] - data.min) / (data.max - data.min)
|
||||
this.speckleRenderer.applyFilter(rvs, {
|
||||
filterType: FilterMaterialType.GRADIENT,
|
||||
rampIndex: t
|
||||
})
|
||||
}
|
||||
|
||||
this.speckleRenderer.endFilter()
|
||||
console.warn(`Filter time: ${performance.now() - start}`)
|
||||
}
|
||||
|
||||
public debugGetFilterByNonNumericPropetyData(propertyName: string): {
|
||||
color?: { name: string; color: string; colorIndex: number; nodes: [] }
|
||||
} {
|
||||
// OG implementation
|
||||
const getColorHash = (objValue) => {
|
||||
const objValueAsString = '' + objValue
|
||||
let hash = 0
|
||||
for (let i = 0; i < objValueAsString.length; i++) {
|
||||
const chr = objValueAsString.charCodeAt(i)
|
||||
hash = (hash << 5) - hash + chr
|
||||
hash |= 0 // Convert to 32bit integer
|
||||
}
|
||||
hash = Math.abs(hash)
|
||||
const colorHue = hash % 360
|
||||
const rgb = new Color(`hsl(${colorHue}, 50%, 30%)`)
|
||||
return rgb.getHex()
|
||||
}
|
||||
|
||||
const data: {
|
||||
color?: { name: string; color: string; colorIndex: number; nodes: [] }
|
||||
} = {}
|
||||
|
||||
let colorCount = 0
|
||||
|
||||
/** This is the lazy approach */
|
||||
WorldTree.getInstance().walk((node: TreeNode) => {
|
||||
if (!node.model.atomic) return true
|
||||
const propertyValue = node.model.raw[propertyName]
|
||||
if (propertyValue !== null && propertyValue !== undefined) {
|
||||
const color = getColorHash(propertyValue.split('.').reverse()[0])
|
||||
if (data[color] === undefined) {
|
||||
data[color] = {
|
||||
name: propertyValue.split('.').reverse()[0],
|
||||
color: new Color(MathUtils.randInt(0, 0xffffff)).getHex(),
|
||||
colorIndex: colorCount,
|
||||
nodes: []
|
||||
}
|
||||
colorCount++
|
||||
}
|
||||
if (!data[color].nodes.includes(node)) data[color].nodes.push(node)
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
public debugApplyByNonNumericPropetyFilter(data: {
|
||||
color?: { name: string; color: number; colorIndex: number; nodes: [] }
|
||||
}) {
|
||||
const start = performance.now()
|
||||
const colors = Object.values(data)
|
||||
colors.sort((a, b) => a.colorIndex - b.colorIndex)
|
||||
|
||||
const rampTexture = Assets.generateDiscreetRampTexture(
|
||||
colors.map((val) => val.color)
|
||||
)
|
||||
this.speckleRenderer.clearFilter()
|
||||
this.speckleRenderer.beginFilter()
|
||||
|
||||
for (let k = 0; k < colors.length; k++) {
|
||||
const nodes = colors[k].nodes
|
||||
let rvs = []
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
rvs = rvs.concat(
|
||||
WorldTree.getRenderTree().getRenderViewsForNode(nodes[i], nodes[i])
|
||||
)
|
||||
}
|
||||
this.speckleRenderer.applyFilter(rvs, {
|
||||
filterType: FilterMaterialType.COLORED,
|
||||
rampIndex: colors[k].colorIndex / colors.length,
|
||||
rampIndexColor: new Color(colors[k].color),
|
||||
rampTexture
|
||||
})
|
||||
}
|
||||
this.speckleRenderer.endFilter()
|
||||
console.warn(`Filter time: ${performance.now() - start}`)
|
||||
}
|
||||
|
||||
public dispose() {
|
||||
// TODO: currently it's easier to simply refresh the page :)
|
||||
}
|
||||
|
||||
@@ -1,10 +1,25 @@
|
||||
import { Box3 } from 'three'
|
||||
import { Box3, Vector3 } from 'three'
|
||||
|
||||
export class World {
|
||||
/* This will no longer exist when we have a scene tree */
|
||||
private static readonly boxes: Array<Box3> = new Array<Box3>()
|
||||
public static readonly worldBox: Box3 = new Box3()
|
||||
|
||||
private static _worldOrigin: Vector3 = new Vector3()
|
||||
public static get worldSize() {
|
||||
World.worldBox.getCenter(this._worldOrigin)
|
||||
const size = new Vector3().subVectors(World.worldBox.max, World.worldBox.min)
|
||||
return {
|
||||
x: size.x,
|
||||
y: size.y,
|
||||
z: size.z
|
||||
}
|
||||
}
|
||||
|
||||
public static get worldOrigin() {
|
||||
return World._worldOrigin
|
||||
}
|
||||
|
||||
public static expandWorld(box: Box3) {
|
||||
World.boxes.push(box)
|
||||
World.updateWorld()
|
||||
|
||||
@@ -52,7 +52,8 @@ v.on('section-box', (status) => {
|
||||
})
|
||||
|
||||
window.viewerScreenshot = function () {
|
||||
let data = v.interactions.screenshot() // transparent png.
|
||||
//@Dim: Changed this to use the API
|
||||
let data = v.screenshot() // transparent png.
|
||||
|
||||
let pop = window.open()
|
||||
pop.document.title = 'super screenshot'
|
||||
@@ -64,5 +65,6 @@ window.viewerScreenshot = function () {
|
||||
}
|
||||
|
||||
window.zoomFast = function () {
|
||||
v.interactions.zoomExtents(0.95, false)
|
||||
//@Dim: This needs to use the API
|
||||
// v.interactions.zoomExtents(0.95, false)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user