Extension Access Changes (#2193)

* Removed the concept of Providers entirely. Now extentions specify explicity extention types or archypes in their inject lists. Removed the concept of core-extensions entirely. All extensions are now equal. The concept of CameraProvider was replaced by SpeckleCamera which the SpeckleRenderer now uses and relie on the default camera controller extension to seed it.

* Fixed some compile errors

* Fixed compile errors. Had to make Extension concrete, but meh, it's fine I guess

* fix viewer types

* Removed generic arguments since they're no longer needed

---------

Co-authored-by: Kristaps Fabians Geikins <fabis94@live.com>
This commit is contained in:
Alexandru Popovici
2024-04-05 18:07:02 +03:00
committed by GitHub
parent 5127d21c02
commit d7f0325192
24 changed files with 266 additions and 351 deletions
@@ -6,10 +6,10 @@ import { SelectionExtension } from '@speckle/viewer'
import { BatchObject } from '@speckle/viewer'
import {
Extension,
ICameraProvider,
IViewer,
GeometryType,
MeshBatch
MeshBatch,
CameraController
} from '@speckle/viewer'
import {
Matrix4,
@@ -25,7 +25,7 @@ import {
export class BoxSelection extends Extension {
get inject() {
return [ICameraProvider.Symbol]
return [CameraController]
}
private selectionExtension: SelectionExtension
@@ -36,7 +36,14 @@ export class BoxSelection extends Extension {
private idsToSelect: Array<string> | null = []
public constructor(viewer: IViewer, private cameraController: ICameraProvider) {
get enabled(): boolean {
return this._enabled
}
set enabled(value: boolean) {
this._enabled = value
}
public constructor(viewer: IViewer, private cameraController: CameraController) {
super(viewer)
/** Get the SelectionExtension. We'll need it to remotely enable/disable it */
//@ts-ignore
@@ -11,6 +11,13 @@ import { PerspectiveCamera } from 'three'
export class CameraPlanes extends Extension {
private camerController: CameraController
get enabled(): boolean {
return this._enabled
}
set enabled(value: boolean) {
this._enabled = value
}
public constructor(viewer: IViewer) {
super(viewer)
this.camerController = viewer.getExtension(
@@ -1,8 +1,8 @@
import { Vector3, ICameraProvider, Extension, IViewer } from '@speckle/viewer'
import { Vector3, Extension, IViewer, CameraController } from '@speckle/viewer'
export class RotateCamera extends Extension {
get inject() {
return [ICameraProvider.Symbol]
return [CameraController]
}
private polar = {
azimuth: 0.001,
@@ -11,7 +11,14 @@ export class RotateCamera extends Extension {
origin: new Vector3()
}
public constructor(viewer: IViewer, private cameraController: ICameraProvider) {
get enabled(): boolean {
return this._enabled
}
set enabled(value: boolean) {
this._enabled = value
}
public constructor(viewer: IViewer, private cameraController: CameraController) {
super(viewer)
}
+4 -4
View File
@@ -21,8 +21,8 @@ import { DiffResult } from '@speckle/viewer'
import type { PipelineOptions } from '@speckle/viewer/dist/modules/pipeline/Pipeline'
import { Units } from '@speckle/viewer'
import { SelectionExtension } from '@speckle/viewer'
import { MeasurementsExtension } from '@speckle/viewer'
import { FilteringExtension } from '@speckle/viewer'
import { MeasurementsExtension } from '@speckle/viewer'
import { CameraController } from '@speckle/viewer'
import { UpdateFlags } from '@speckle/viewer'
import { Viewer } from '@speckle/viewer'
@@ -401,15 +401,15 @@ export default class Sandbox {
if (!box) {
box = this.viewer.getRenderer().sceneBox
}
this.viewer.getExtension<SectionTool>(SectionTool).setBox(box)
this.viewer.getExtension<SectionTool>(SectionTool).toggle()
this.viewer.getExtension(SectionTool).setBox(box)
this.viewer.getExtension(SectionTool).toggle()
})
const toggleProjection = this.tabs.pages[0].addButton({
title: 'Toggle Projection'
})
toggleProjection.on('click', () => {
this.viewer.getExtension<CameraController>(CameraController).toggleCameras()
this.viewer.getExtension(CameraController).toggleCameras()
})
const zoomExtents = this.tabs.pages[0].addButton({
+2 -101
View File
@@ -5,10 +5,7 @@ import {
SelectionEvent,
ViewerEvent,
DebugViewer,
Viewer,
Batch,
DrawRanges,
SpeckleBasicMaterial
Viewer
} from '@speckle/viewer'
import './style.css'
@@ -23,11 +20,6 @@ import {
} from '@speckle/viewer'
import { SectionTool } from '@speckle/viewer'
import { SectionOutlines } from '@speckle/viewer'
import { BoxSelection } from './Extensions/BoxSelection'
import { GeometryType } from '@speckle/viewer'
import { SpeckleStandardMaterial } from '@speckle/viewer'
import { Color, FrontSide } from 'three'
const createViewer = async (containerName: string, stream: string) => {
const container = document.querySelector<HTMLElement>(containerName)
@@ -58,7 +50,7 @@ const createViewer = async (containerName: string, stream: string) => {
const filtering = viewer.createExtension(FilteringExtension)
const explode = viewer.createExtension(ExplodeExtension)
const diff = viewer.createExtension(DiffExtension)
const boxSelect = viewer.createExtension(BoxSelection)
// const boxSelect = viewer.createExtension(BoxSelection)
// const rotateCamera = viewer.createExtension(RotateCamera)
cameraController // use it
selection // use it
@@ -98,97 +90,6 @@ const createViewer = async (containerName: string, stream: string) => {
Object.assign(sandbox.sceneParams.worldSize, viewer.World.worldSize)
Object.assign(sandbox.sceneParams.worldOrigin, viewer.World.worldOrigin)
sandbox.refresh()
const meshBatch = viewer
.getRenderer()
.batcher.getBatches(undefined, GeometryType.MESH)
.find((batch: Batch) => batch.renderViews.length > 2)
// const geom = meshBatch.mesh.geometry
// geom.groups.length = 0
// geom.addGroup(0, 216, 0)
// geom.addGroup(216, 1323, 0)
// geom.addGroup(1539, 540, 0)
// geom.addGroup(2079, 32268, 0)
// const material = new SpeckleStandardMaterial(
// {
// color: new Color('#00ff00'),
// emissive: 0x0,
// roughness: 1,
// metalness: 0,
// opacity: 1,
// side: FrontSide
// },
// ['USE_RTE']
// )
// meshBatch.setDrawRanges(
// {
// offset: 36,
// count: 36,
// material
// }
// {
// offset: 180,
// count: 1395,
// material
// },
// {
// offset: 1581,
// count: 32766,
// material
// }
// )
// const material = new SpeckleStandardMaterial(
// {
// color: new Color('#00ff00'),
// emissive: 0x0,
// roughness: 1,
// metalness: 0,
// opacity: 1,
// side: FrontSide
// },
// ['USE_RTE']
// )
// meshBatch.setDrawRanges(
// {
// offset: 9018,
// count: 36,
// material
// },
// {
// offset: 13878,
// count: 36,
// material
// }
// )
// const material0 = new SpeckleBasicMaterial({ color: 0xff0000 })
// const material1 = new SpeckleBasicMaterial({ color: 0x00ff00 })
// let groups = [
// {
// start: 0,
// count: 1350,
// materialIndex: 0
// }
// ]
// const drawRange = new DrawRanges()
// groups = drawRange.integrateRanges(
// groups,
// [material0, material1],
// [
// {
// offset: 0,
// count: 258,
// material: material1
// },
// {
// offset: 258,
// count: 1032,
// material: material1
// }
// ]
// )
})
viewer.on(ViewerEvent.UnloadComplete, () => {
+1
View File
@@ -65,6 +65,7 @@
"three-mesh-bvh": "0.5.17",
"tree-model": "1.0.7",
"troika-three-text": "0.47.2",
"type-fest": "^4.15.0",
"underscore": "1.13.6"
},
"devDependencies": {
+4 -3
View File
@@ -7,9 +7,10 @@ import { TreeNode, WorldTree } from './modules/tree/WorldTree'
import { Utils } from './modules/Utils'
import { World } from './modules/World'
import SpeckleRenderer from './modules/SpeckleRenderer'
import { Extension } from './modules/extensions/core-extensions/Extension'
import { Extension } from './modules/extensions/Extension'
import Input from './modules/input/Input'
import { Loader } from './modules/loaders/Loader'
import { type Constructor } from 'type-fest'
export interface ViewerParams {
showStats: boolean
@@ -173,8 +174,8 @@ export interface IViewer {
getRenderer(): SpeckleRenderer
getContainer(): HTMLElement
createExtension<T extends Extension>(type: new () => T): T
getExtension<T extends Extension>(type: new () => T): T
createExtension<T extends Extension>(type: Constructor<T>): T
getExtension<T extends Extension>(type: Constructor<T>): T
dispose(): void
}
+6 -10
View File
@@ -37,20 +37,17 @@ import {
} from './modules/extensions/measurements/MeasurementsExtension'
import { Units } from './modules/converter/Units'
import { SelectionExtension } from './modules/extensions/SelectionExtension'
import { CameraController } from './modules/extensions/core-extensions/CameraController'
import {
CameraControllerEvent,
CanonicalView,
ICameraProvider,
InlineView
} from './modules/extensions/core-extensions/Providers'
import { CameraController } from './modules/extensions/CameraController'
import { InlineView } from './modules/extensions/CameraController'
import { CanonicalView } from './modules/extensions/CameraController'
import { CameraEvent } from './modules/objects/SpeckleCamera'
import { SectionTool } from './modules/extensions/SectionTool'
import { SectionOutlines } from './modules/extensions/SectionOutlines'
import {
FilteringExtension,
FilteringState
} from './modules/extensions/FilteringExtension'
import { Extension } from './modules/extensions/core-extensions/Extension'
import { Extension } from './modules/extensions/Extension'
import { ExplodeExtension } from './modules/extensions/ExplodeExtension'
import {
DiffExtension,
@@ -86,14 +83,13 @@ export {
MeasurementType,
Units,
Extension,
ICameraProvider,
SelectionExtension,
CameraController,
SectionTool,
SectionOutlines,
MeasurementsExtension,
FilteringExtension,
CameraControllerEvent,
CameraEvent,
ExplodeExtension,
DiffExtension,
Loader,
+5 -8
View File
@@ -1,11 +1,8 @@
import { MathUtils } from 'three'
import { FilteringExtension, FilteringState } from './extensions/FilteringExtension'
import {
CanonicalView,
ICameraProvider,
InlineView,
PolarView
} from './extensions/core-extensions/Providers'
import { PolarView } from './extensions/CameraController'
import { InlineView } from './extensions/CameraController'
import { CanonicalView } from './extensions/CameraController'
import { SpeckleType } from './loaders/GeometryConverter'
import { Queries } from './queries/Queries'
import { Query, QueryArgsResultMap, QueryResult } from './queries/Query'
@@ -25,7 +22,7 @@ import {
} from '../IViewer'
import { TreeNode, WorldTree } from './tree/WorldTree'
import { Viewer } from './Viewer'
import { CameraController } from './extensions/core-extensions/CameraController'
import { CameraController } from './extensions/CameraController'
import { SectionTool } from './extensions/SectionTool'
import { SectionOutlines } from './extensions/SectionOutlines'
import {
@@ -49,7 +46,7 @@ class LegacySelectionExtension extends SelectionExtension {
}
class HighlightExtension extends SelectionExtension {
public constructor(viewer: IViewer, protected cameraProvider: ICameraProvider) {
public constructor(viewer: IViewer, protected cameraProvider: CameraController) {
super(viewer, cameraProvider)
const highlightMaterialData: SelectionExtensionOptions = {
selectionMaterialData: {
+15 -22
View File
@@ -43,10 +43,7 @@ import { Shadowcatcher } from './Shadowcatcher'
import SpeckleMesh from './objects/SpeckleMesh'
import { ExtendedIntersection } from './objects/SpeckleRaycaster'
import { BatchObject } from './batching/BatchObject'
import {
ICameraProvider,
CameraControllerEvent
} from './extensions/core-extensions/Providers'
import { CameraEvent, SpeckleCamera } from './objects/SpeckleCamera'
import Materials, {
RenderMaterial,
DisplayStyle,
@@ -58,7 +55,6 @@ import { SpeckleWebGLRenderer } from './objects/SpeckleWebGLRenderer'
import { SpeckleTypeAllRenderables } from './loaders/GeometryConverter'
import SpeckleInstancedMesh from './objects/SpeckleInstancedMesh'
import { BaseSpecklePass } from './pipeline/SpecklePass'
import { CameraController } from './extensions/core-extensions/CameraController'
import { MeshBatch } from './batching/MeshBatch'
export class RenderingStats {
@@ -118,7 +114,7 @@ export default class SpeckleRenderer {
private _shadowcatcher: Shadowcatcher = null
private cancel: { [subtreeId: string]: boolean } = {}
private _cameraProvider: ICameraProvider = null
private _speckleCamera: SpeckleCamera = null
private _clippingPlanes: Plane[] = []
private _clippingVolume: Box3 = new Box3()
@@ -217,33 +213,30 @@ export default class SpeckleRenderer {
/********
* Camera */
public get cameraProvider() {
return this._cameraProvider
public get speckleCamera(): SpeckleCamera {
return this._speckleCamera
}
public set cameraProvider(value: ICameraProvider) {
this._cameraProvider = value
this._cameraProvider.on(CameraControllerEvent.Dynamic, () => {
public set speckleCamera(value: SpeckleCamera) {
this._speckleCamera = value
this._speckleCamera.on(CameraEvent.Dynamic, () => {
this._needsRender = true
this.pipeline.onStationaryEnd()
})
this._cameraProvider.on(CameraControllerEvent.Stationary, () => {
this._speckleCamera.on(CameraEvent.Stationary, () => {
this._needsRender = true
this.pipeline.onStationaryBegin()
})
this._cameraProvider.on(CameraControllerEvent.FrameUpdate, (data: boolean) => {
this.needsRender = data
if (this.pipeline.needsAccumulation && data) {
this._speckleCamera.on(CameraEvent.FrameUpdate, (needsUpdate: boolean) => {
this.needsRender = needsUpdate
if (this.pipeline.needsAccumulation && needsUpdate) {
this.pipeline.reset()
}
})
this.cameraProvider.on(CameraControllerEvent.ProjectionChanged, () => {
;(this._cameraProvider as CameraController).setCameraPlanes(this.sceneBox)
})
}
public get renderingCamera() {
return this._cameraProvider.renderingCamera
return this._speckleCamera.renderingCamera
}
/**********
@@ -394,7 +387,7 @@ export default class SpeckleRenderer {
}
public update(deltaTime: number) {
if (!this._cameraProvider) return
if (!this._speckleCamera) return
this.batcher.update(deltaTime)
this.renderingCamera.updateMatrixWorld(true)
@@ -548,7 +541,7 @@ export default class SpeckleRenderer {
return
}
if (!this._cameraProvider) return
if (!this._speckleCamera) return
if (this._needsRender || this.pipeline.needsAccumulation) {
this._renderinStats.frameStart()
this.batcher.render(this.renderer)
@@ -614,7 +607,7 @@ export default class SpeckleRenderer {
/** We'll just update the shadowcatcher after all batches are loaded */
this.updateShadowCatcher()
this.updateClippingPlanes()
;(this._cameraProvider as CameraController).setCameraPlanes(this.sceneBox)
this._speckleCamera.setCameraPlanes(this.sceneBox)
delete this.cancel[subtreeId]
}
+36 -33
View File
@@ -23,12 +23,12 @@ import Logger from 'js-logger'
import { Query, QueryArgsResultMap, QueryResult } from './queries/Query'
import { Queries } from './queries/Queries'
import { Utils } from './Utils'
import { Extension } from './extensions/core-extensions/Extension'
import { ICameraProvider, IProvider } from './extensions/core-extensions/Providers'
import { Extension } from './extensions/Extension'
import Input from './input/Input'
import { CameraController } from './extensions/core-extensions/CameraController'
import { CameraController } from './extensions/CameraController'
import { SpeckleType } from './loaders/GeometryConverter'
import { Loader } from './loaders/Loader'
import { type Constructor } from 'type-fest'
export class Viewer extends EventEmitter implements IViewer {
/** Container and optional stats element */
@@ -51,7 +51,7 @@ export class Viewer extends EventEmitter implements IViewer {
protected loaders: { [id: string]: Loader } = {}
protected extensions: {
[id: string]: Extension | IProvider
[id: string]: Extension
} = {}
/** various utils/helpers */
@@ -75,41 +75,44 @@ export class Viewer extends EventEmitter implements IViewer {
return this.speckleRenderer.input
}
public createExtension<T extends Extension>(
type: new (viewer: IViewer, ...args) => T
): T {
const providersToInject = type.prototype.inject
const providers = []
Object.values(this.extensions).forEach((extension: IProvider) => {
const provides = extension.provide
if (provides && providersToInject.includes(provides)) providers.push(extension)
private getConstructorChain(obj: object) {
const cs = []
let pt = obj
do {
if ((pt = Object.getPrototypeOf(pt))) cs.push(pt.constructor || null)
} while (pt !== null)
return cs.map(function (c) {
return c ? c.toString().split(/\s|\(/)[1] : null
})
const extension = new type(this, ...providers)
/** Temporary until we implement proper providing for core */
if (ICameraProvider.isCameraProvider(extension)) {
this.speckleRenderer.cameraProvider = extension
}
this.extensions[type.name] = extension
return extension
}
public getExtension<T extends Extension | IProvider>(
type: new (viewer: IViewer, ...args) => T
): T {
const getConstructorChain = (obj) => {
const cs = []
let pt = obj
do {
if ((pt = Object.getPrototypeOf(pt))) cs.push(pt.constructor || null)
} while (pt !== null)
return cs.map(function (c) {
return c ? c.toString().split(/\s|\(/)[1] : null
})
}
public createExtension<T extends Extension>(type: Constructor<T>): T {
const extensionsToInject: Array<new (viewer: IViewer, ...args) => Extension> =
type.prototype.inject
const injectedExtensions: Array<Extension> = []
extensionsToInject.forEach((value: new (viewer: IViewer, ...args) => Extension) => {
if (this.extensions[value.name]) {
injectedExtensions.push(this.extensions[value.name])
return
}
for (const k in this.extensions) {
const prototypeChain = this.getConstructorChain(this.extensions[k])
if (prototypeChain.includes(value.name)) {
injectedExtensions.push(this.extensions[k])
}
}
})
const extension = new type(this, ...injectedExtensions)
this.extensions[type.name] = extension
return extension as T
}
public getExtension<T extends Extension>(type: Constructor<T>): T {
if (this.extensions[type.name]) return this.extensions[type.name] as T
else {
for (const k in this.extensions) {
const prototypeChain = getConstructorChain(this.extensions[k])
const prototypeChain = this.getConstructorChain(this.extensions[k])
if (prototypeChain.includes(type.name)) {
return this.extensions[k] as T
}
@@ -1,23 +1,38 @@
import * as THREE from 'three'
import CameraControls from 'camera-controls'
import { Extension } from './Extension'
import { SpeckleCameraControls } from '../../objects/SpeckleCameraControls'
import { SpeckleCameraControls } from '../objects/SpeckleCameraControls'
import { Box3, OrthographicCamera, PerspectiveCamera, Sphere, Vector3 } from 'three'
import { KeyboardKeyHold, HOLD_EVENT_TYPE } from 'hold-event'
import { CanonicalView, SpeckleView, InlineView, IViewer } from '../../..'
import {
CameraControllerEvent,
CameraProjection,
ICameraProvider,
PolarView
} from './Providers'
import { CameraProjection } from '../objects/SpeckleCamera'
import { CameraEvent, SpeckleCamera } from '../objects/SpeckleCamera'
import Logger from 'js-logger'
import { IViewer, SpeckleView } from '../../IViewer'
export class CameraController extends Extension implements ICameraProvider {
get provide() {
return ICameraProvider.Symbol
}
export type CanonicalView =
| 'front'
| 'back'
| 'up'
| 'top'
| 'down'
| 'bottom'
| 'right'
| 'left'
| '3d'
| '3D'
export type InlineView = {
position: Vector3
target: Vector3
}
export type PolarView = {
azimuth: number
polar: number
radius?: number
origin?: Vector3
}
export class CameraController extends Extension implements SpeckleCamera {
protected _renderingCamera: PerspectiveCamera | OrthographicCamera = null
protected perspectiveCamera: PerspectiveCamera = null
protected orthographicCamera: OrthographicCamera = null
@@ -99,19 +114,21 @@ export class CameraController extends Extension implements ICameraProvider {
this.setupWASDControls()
this._controls.addEventListener('rest', () => {
this.emit(CameraControllerEvent.Stationary)
this.emit(CameraEvent.Stationary)
})
this._controls.addEventListener('controlstart', () => {
this.emit(CameraControllerEvent.Dynamic)
this.emit(CameraEvent.Dynamic)
})
this._controls.addEventListener('controlend', () => {
if (this._controls.hasRested) this.emit(CameraControllerEvent.Stationary)
if (this._controls.hasRested) this.emit(CameraEvent.Stationary)
})
this._controls.addEventListener('control', () => {
this.emit(CameraControllerEvent.Dynamic)
this.emit(CameraEvent.Dynamic)
})
this.viewer.getRenderer().speckleCamera = this
}
setCameraView(objectIds: string[], transition: boolean, fit?: number): void
@@ -134,12 +151,12 @@ export class CameraController extends Extension implements ICameraProvider {
} else {
this.setView(arg0, arg1)
}
this.emit(CameraControllerEvent.Dynamic)
this.emit(CameraEvent.Dynamic)
}
public onEarlyUpdate(deltaTime: number) {
const changed = this._controls.update(deltaTime)
this.emit(CameraControllerEvent.FrameUpdate, changed)
this.emit(CameraEvent.FrameUpdate, changed)
}
public onResize() {
@@ -219,7 +236,8 @@ export class CameraController extends Extension implements ICameraProvider {
this.orthographicCamera.updateProjectionMatrix()
this._controls.camera = this.orthographicCamera
this.emit(CameraControllerEvent.ProjectionChanged, CameraProjection.ORTHOGRAPHIC)
this.setCameraPlanes(this.viewer.getRenderer().sceneBox)
this.emit(CameraEvent.ProjectionChanged, CameraProjection.ORTHOGRAPHIC)
}
protected setupPerspectiveCamera() {
@@ -230,7 +248,8 @@ export class CameraController extends Extension implements ICameraProvider {
this._controls.camera = this.perspectiveCamera
this._controls.zoomTo(1)
this.enableRotations()
this.emit(CameraControllerEvent.ProjectionChanged, CameraProjection.PERSPECTIVE)
this.setCameraPlanes(this.viewer.getRenderer().sceneBox)
this.emit(CameraEvent.ProjectionChanged, CameraProjection.PERSPECTIVE)
}
public disableRotations() {
@@ -9,7 +9,7 @@ import SpecklePointMaterial from '../materials/SpecklePointMaterial'
import SpeckleStandardMaterial from '../materials/SpeckleStandardMaterial'
import { NodeRenderView } from '../tree/NodeRenderView'
import { IViewer } from '../../IViewer'
import { Extension } from './core-extensions/Extension'
import { Extension } from './Extension'
import { SpeckleTypeAllRenderables } from '../loaders/GeometryConverter'
import { SpeckleLoader } from '../loaders/Speckle/SpeckleLoader'
@@ -41,6 +41,14 @@ interface VisualDiffResult {
}
export class DiffExtension extends Extension {
public get enabled(): boolean {
return this._enabled
}
/* eslint-disable @typescript-eslint/no-unused-vars */
public set enabled(value: boolean) {
this._enabled = value
}
protected tree: WorldTree = null
private addedMaterialMesh: SpeckleStandardMaterial = null
private changedNewMaterialMesh: SpeckleStandardMaterial = null
@@ -1,12 +1,23 @@
import { Vector3 } from 'three'
import { Extension } from './core-extensions/Extension'
import { Extension } from './Extension'
import { UpdateFlags } from '../../IViewer'
export class ExplodeExtension extends Extension {
protected _enabled: boolean = true
public get enabled(): boolean {
return this._enabled
}
public set enabled(value: boolean) {
this._enabled = value
}
private explodeTime = -1
private explodeRange = 0
public onEarlyUpdate() {
if (!this._enabled) return
if (this.explodeTime > -1) {
this.explode(this.explodeTime, this.explodeRange)
this.explodeTime = -1
@@ -0,0 +1,40 @@
import { IViewer } from '../..'
import EventEmitter from '../EventEmitter'
export class Extension extends EventEmitter {
public get inject(): Array<new (viewer: IViewer, ...args) => Extension> {
return []
}
protected viewer: IViewer
protected _enabled: boolean
public get enabled(): boolean {
return this._enabled
}
public set enabled(value: boolean) {
this._enabled = value
}
public constructor(viewer: IViewer, ...args: Extension[]) {
args
super()
this.viewer = viewer
}
public onEarlyUpdate(deltaTime?: number) {
deltaTime
/* EMPTY*/
}
public onLateUpdate(deltaTime?: number) {
deltaTime
/* EMPTY*/
}
public onRender() {
/* EMPTY*/
}
public onResize() {
/* EMPTY*/
}
}
@@ -5,7 +5,7 @@ import { Assets } from '../Assets'
import SpeckleRenderer from '../SpeckleRenderer'
import { FilterMaterialType } from '../materials/Materials'
import { NodeRenderView } from '../tree/NodeRenderView'
import { Extension } from './core-extensions/Extension'
import { Extension } from './Extension'
import { TreeNode, WorldTree } from '../tree/WorldTree'
import { IViewer, UpdateFlags, ViewerEvent } from '../../IViewer'
import {
@@ -40,6 +40,13 @@ export class FilteringExtension extends Extension {
return this.CurrentFilteringState
}
public get enabled(): boolean {
return this._enabled
}
public set enabled(value: boolean) {
this._enabled = value
}
public constructor(viewer: IViewer) {
super(viewer)
this.WTI = this.viewer.getWorldTree()
@@ -14,10 +14,9 @@ import { LineSegmentsGeometry } from 'three/examples/jsm/lines/LineSegmentsGeome
import { Geometry } from '../converter/Geometry'
import SpeckleGhostMaterial from '../materials/SpeckleGhostMaterial'
import SpeckleLineMaterial from '../materials/SpeckleLineMaterial'
import { Extension } from './core-extensions/Extension'
import { Extension } from './Extension'
import { IViewer } from '../..'
import { ISectionProvider } from './core-extensions/Providers'
import { SectionToolEvent } from './SectionTool'
import { SectionTool, SectionToolEvent } from './SectionTool'
import { GeometryType } from '../batching/Batch'
import { ObjectLayers } from '../../IViewer'
import { MeshBatch } from '../batching/MeshBatch'
@@ -38,7 +37,7 @@ export interface PlaneOutline {
export class SectionOutlines extends Extension {
public get inject() {
return [ISectionProvider.Symbol]
return [SectionTool]
}
private static readonly INITIAL_BUFFER_SIZE = 60000 // Must be a multiple of 6
private static readonly Z_OFFSET = -0.001
@@ -55,9 +54,8 @@ export class SectionOutlines extends Extension {
private planeOutlines: Record<string, PlaneOutline> = {}
private lastSectionPlanes: Plane[] = []
private sectionPlanesChanged: Plane[] = []
private _enabled = false
public constructor(viewer: IViewer, protected sectionProvider: ISectionProvider) {
public constructor(viewer: IViewer, protected sectionProvider: SectionTool) {
super(viewer)
this.planeOutlines[PlaneId.POSITIVE_X] = this.createPlaneOutline(PlaneId.POSITIVE_X)
this.planeOutlines[PlaneId.NEGATIVE_X] = this.createPlaneOutline(PlaneId.NEGATIVE_X)
@@ -100,7 +98,11 @@ export class SectionOutlines extends Extension {
return this.planeOutlines[planeId]
}
public enable(value: boolean) {
public get enabled(): boolean {
return this._enabled
}
public set enabled(value: boolean) {
this._enabled = value
for (const k in this.planeOutlines) {
this.planeOutlines[k].renderable.visible = value
@@ -108,7 +110,7 @@ export class SectionOutlines extends Extension {
}
public sectionUpdated(planes: Plane[]) {
if (!this.sectionProvider.enabled) this.enable(false)
if (!this.sectionProvider.enabled) this.enabled = false
for (const plane in this.planeOutlines) {
const clippingPlanes = planes.filter((value) => this.getPlaneId(value) !== plane)
this.planeOutlines[plane].renderable.material.clippingPlanes = clippingPlanes
@@ -315,7 +317,7 @@ export class SectionOutlines extends Extension {
}
private onSectionBoxDragStart() {
this.enable(false)
this.enabled = false
}
private onSectionBoxDragEnd() {
@@ -346,7 +348,7 @@ export class SectionOutlines extends Extension {
planes[k]
)
}
this.enable(this.sectionProvider.enabled)
this.enabled = this.sectionProvider.enabled
Logger.warn('Outline time: ', performance.now() - start)
}
@@ -17,10 +17,10 @@ import {
} from 'three'
import { TransformControls } from 'three/examples/jsm/controls/TransformControls'
import { IViewer, ObjectLayers } from '../../IViewer'
import { Extension } from './core-extensions/Extension'
import { CameraControllerEvent, ICameraProvider } from './core-extensions/Providers'
import { Extension } from './Extension'
import { CameraEvent } from '../objects/SpeckleCamera'
import { InputEvent } from '../input/Input'
import { ISectionProvider } from './core-extensions/Providers'
import { CameraController } from './CameraController'
export enum SectionToolEvent {
DragStart = 'section-box-drag-start',
@@ -28,13 +28,9 @@ export enum SectionToolEvent {
Updated = 'section-box-changed'
}
export class SectionTool extends Extension implements ISectionProvider {
export class SectionTool extends Extension {
public get inject() {
return [ICameraProvider.Symbol]
}
public get provide(): string {
return ISectionProvider.Symbol
return [CameraController]
}
protected dragging = false
@@ -60,7 +56,6 @@ export class SectionTool extends Extension implements ISectionProvider {
protected raycaster: Raycaster
private _enabled = false
public get enabled() {
return this._enabled
}
@@ -73,7 +68,7 @@ export class SectionTool extends Extension implements ISectionProvider {
this.viewer.requestRender()
}
constructor(viewer: IViewer, protected cameraProvider: ICameraProvider) {
constructor(viewer: IViewer, protected cameraProvider: CameraController) {
super(viewer)
this.viewer = viewer
@@ -160,11 +155,11 @@ export class SectionTool extends Extension implements ISectionProvider {
this._setupControls()
this._attachControlsToBox()
this.cameraProvider.on(CameraControllerEvent.ProjectionChanged, () => {
this.cameraProvider.on(CameraEvent.ProjectionChanged, () => {
this._setupControls()
this._attachControlsToBox()
})
this.cameraProvider.on(CameraControllerEvent.FrameUpdate, (data: boolean) => {
this.cameraProvider.on(CameraEvent.FrameUpdate, (data: boolean) => {
this.allowSelection = !data
})
this.viewer.getRenderer().input.on(InputEvent.Click, this._clickHandler.bind(this))
@@ -1,6 +1,5 @@
import { ExtendedIntersection } from '../objects/SpeckleRaycaster'
import { Extension } from './core-extensions/Extension'
import { ICameraProvider } from './core-extensions/Providers'
import { Extension } from './Extension'
import { NodeRenderView } from '../tree/NodeRenderView'
import { Material } from 'three'
import { InputEvent } from '../input/Input'
@@ -16,6 +15,7 @@ import Materials, { DisplayStyle, RenderMaterial } from '../materials/Materials'
import { StencilOutlineType } from '../../IViewer'
import { MaterialOptions } from '../materials/MaterialOptions'
import { TreeNode } from '../tree/WorldTree'
import { CameraController } from './CameraController'
export interface SelectionExtensionOptions {
selectionMaterialData: RenderMaterial & DisplayStyle & MaterialOptions
@@ -49,7 +49,7 @@ const DefaultSelectionExtensionOptions: SelectionExtensionOptions = {
export class SelectionExtension extends Extension {
public get inject() {
return [ICameraProvider.Symbol]
return [CameraController]
}
protected selectedNodes: Array<TreeNode> = []
@@ -77,7 +77,7 @@ export class SelectionExtension extends Extension {
this._enabled = value
}
public constructor(viewer: IViewer, protected cameraProvider: ICameraProvider) {
public constructor(viewer: IViewer, protected cameraProvider: CameraController) {
super(viewer)
this.viewer.on(ViewerEvent.ObjectClicked, this.onObjectClicked.bind(this))
this.viewer.on(ViewerEvent.ObjectDoubleClicked, this.onObjectDoubleClick.bind(this))
@@ -1,30 +0,0 @@
import { IViewer } from '../../..'
import EventEmitter from '../../EventEmitter'
export abstract class Extension extends EventEmitter {
public get inject() {
return []
}
protected viewer: IViewer
public constructor(viewer: IViewer) {
super()
this.viewer = viewer
}
public async init?()
public onEarlyUpdate(deltaTime?: number) {
deltaTime
/* EMPTY*/
}
public onLateUpdate(deltaTime?: number) {
deltaTime
/* EMPTY*/
}
public onRender() {
/* EMPTY*/
}
public onResize() {
/* EMPTY*/
}
}
@@ -1,78 +0,0 @@
/* eslint-disable @typescript-eslint/no-unsafe-declaration-merging */
import { PerspectiveCamera, OrthographicCamera, Vector3, Plane, Box3 } from 'three'
import { SpeckleView } from '../../..'
import { SectionToolEvent } from '../SectionTool'
import { SpeckleCameraControls } from '../../objects/SpeckleCameraControls'
export type CanonicalView =
| 'front'
| 'back'
| 'up'
| 'top'
| 'down'
| 'bottom'
| 'right'
| 'left'
| '3d'
| '3D'
export type InlineView = {
position: Vector3
target: Vector3
}
export type PolarView = {
azimuth: number
polar: number
radius?: number
origin?: Vector3
}
export enum CameraProjection {
PERSPECTIVE,
ORTHOGRAPHIC
}
export enum CameraControllerEvent {
Stationary,
Dynamic,
FrameUpdate,
ProjectionChanged
}
export interface IProvider {
get provide(): string
}
export interface ICameraProvider extends IProvider {
get enabled(): boolean
set enabled(val: boolean)
get renderingCamera(): PerspectiveCamera | OrthographicCamera
get controls(): SpeckleCameraControls
setCameraView(objectIds: string[], transition: boolean, fit?: number)
setCameraView(
view: CanonicalView | SpeckleView | InlineView | PolarView,
transition: boolean
)
setCameraView(box: Box3, transition: boolean, fit?: number)
on(e: CameraControllerEvent, handler: (data: boolean) => void)
removeListener(e: SectionToolEvent, handler: (data: Plane[]) => void)
}
export abstract class ICameraProvider {
public static readonly Symbol = 'ICameraProvider'
public static isCameraProvider(extension): extension is ICameraProvider {
return 'renderingCamera' in extension
}
}
export interface ISectionProvider extends IProvider {
get enabled(): boolean
set enabled(val: boolean)
on(e: SectionToolEvent, handler: (data: Plane[]) => void)
removeListener(e: SectionToolEvent, handler: (data: Plane[]) => void)
}
export abstract class ISectionProvider {
public static readonly Symbol = 'ISectionProvider'
}
@@ -9,9 +9,9 @@ import { ExtendedIntersection } from '../../objects/SpeckleRaycaster'
import Logger from 'js-logger'
import SpeckleMesh from '../../objects/SpeckleMesh'
import SpeckleGhostMaterial from '../../materials/SpeckleGhostMaterial'
import { Extension } from '../core-extensions/Extension'
import { Extension } from '../Extension'
import { InputEvent } from '../../input/Input'
import { ICameraProvider } from '../core-extensions/Providers'
import { CameraController } from '../CameraController'
export enum MeasurementType {
PERPENDICULAR,
@@ -36,7 +36,7 @@ const DefaultMeasurementsOptions = {
export class MeasurementsExtension extends Extension {
public get inject() {
return [ICameraProvider.Symbol]
return [CameraController]
}
protected renderer: SpeckleRenderer = null
@@ -48,7 +48,6 @@ export class MeasurementsExtension extends Extension {
protected _options: MeasurementOptions = Object.assign({}, DefaultMeasurementsOptions)
private _frameLock = false
private _enabled = false
private _paused = false
private _sceneHit = false
@@ -101,7 +100,7 @@ export class MeasurementsExtension extends Extension {
return this._activeMeasurement
}
public constructor(viewer: IViewer, protected cameraProvider: ICameraProvider) {
public constructor(viewer: IViewer, protected cameraProvider: CameraController) {
super(viewer)
this.renderer = viewer.getRenderer()
this.raycaster = new Raycaster()
@@ -0,0 +1,21 @@
import { Box3, OrthographicCamera, PerspectiveCamera } from 'three'
export enum CameraEvent {
Stationary,
Dynamic,
FrameUpdate,
ProjectionChanged
}
export interface SpeckleCamera {
get renderingCamera(): PerspectiveCamera | OrthographicCamera
get fieldOfView(): number
set fieldOfView(value: number)
get aspect(): number
on(type: CameraEvent, handler: (...args) => void)
setCameraPlanes(targetVolume: Box3, offsetScale?: number)
}
export enum CameraProjection {
PERSPECTIVE,
ORTHOGRAPHIC
}
+8
View File
@@ -14606,6 +14606,7 @@ __metadata:
three-mesh-bvh: 0.5.17
tree-model: 1.0.7
troika-three-text: 0.47.2
type-fest: ^4.15.0
typescript: ^4.5.4
underscore: 1.13.6
vitest: ^1.4.0
@@ -45776,6 +45777,13 @@ __metadata:
languageName: node
linkType: hard
"type-fest@npm:^4.15.0":
version: 4.15.0
resolution: "type-fest@npm:4.15.0"
checksum: 8da2b8c4556a6bbafd79c0d50b4f3ba6526942aead9c1687038980276eee72b95a1d195bc6f1408e0ebf96ebfbe9d33436b506b35ed4b68f14f8b3ff56753850
languageName: node
linkType: hard
"type-is@npm:^1.6.16, type-is@npm:^1.6.18, type-is@npm:~1.6.18":
version: 1.6.18
resolution: "type-is@npm:1.6.18"