From ba7c3efa2c5956136cded88985e13312471d9c02 Mon Sep 17 00:00:00 2001 From: Tom Herold Date: Thu, 17 Jul 2025 15:48:08 +0200 Subject: [PATCH 1/9] update threejs imports --- frontend/javascripts/libs/UpdatableTexture.ts | 30 ++-- .../libs/cuckoo/abstract_cuckoo_table.ts | 10 +- .../libs/cuckoo/cuckoo_table_uint32.ts | 6 +- frontend/javascripts/libs/mjs.ts | 2 + frontend/javascripts/libs/parse_stl_buffer.ts | 20 +-- frontend/javascripts/libs/stl_exporter.ts | 16 +- .../javascripts/libs/trackball_controls.ts | 50 +++---- .../libs/visibility_aware_raycaster.ts | 29 ++-- .../test/api/api_skeleton_latest.spec.ts | 30 ++-- .../test/misc/rotation_helpers.spec.ts | 38 ++--- .../test/mocks/updatable_texture.mock.ts | 21 ++- .../accessors/view_mode_accessors.spec.ts | 20 +-- frontend/javascripts/viewer/api/api_latest.ts | 19 +-- frontend/javascripts/viewer/constants.ts | 22 +-- .../viewer/controller/camera_controller.ts | 34 ++--- .../combinations/skeleton_handlers.ts | 12 +- .../controller/combinations/tool_controls.ts | 4 +- .../viewer/controller/custom_lod.ts | 18 +-- .../javascripts/viewer/controller/renderer.ts | 10 +- .../viewer/controller/scene_controller.ts | 112 ++++++++------ .../controller/segment_mesh_controller.ts | 75 +++++----- .../viewer/controller/td_controller.tsx | 12 +- .../viewer/geometries/arbitrary_plane.ts | 28 ++-- ...ompute_split_boundary_mesh_with_splines.ts | 53 ++++--- .../viewer/geometries/crosshair.ts | 22 +-- .../javascripts/viewer/geometries/cube.ts | 22 +-- .../viewer/geometries/helper_geometries.ts | 140 ++++++++++-------- .../geometries/materials/edge_shader.ts | 12 +- .../geometries/materials/node_shader.ts | 14 +- .../materials/plane_material_factory.ts | 34 ++--- .../plane_material_factory_helpers.ts | 28 ++-- .../javascripts/viewer/geometries/plane.ts | 57 ++++--- .../javascripts/viewer/geometries/skeleton.ts | 140 +++++++++--------- .../dataset_layer_transformation_accessor.ts | 28 ++-- .../viewer/model/accessors/flycam_accessor.ts | 17 +-- .../model/accessors/view_mode_accessor.ts | 22 +-- .../viewer/model/actions/ui_actions.ts | 3 +- .../model/bucket_data_handling/bucket.ts | 26 ++-- .../model/bucket_data_handling/data_cube.ts | 19 ++- .../viewer/model/helpers/rotation_helpers.ts | 10 +- .../viewer/model/reducers/flycam_reducer.ts | 9 +- .../model/sagas/skeletontracing_saga.ts | 8 +- .../javascripts/viewer/view/arbitrary_view.ts | 34 ++--- .../javascripts/viewer/view/plane_view.ts | 35 +++-- .../viewer/view/rendering_utils.ts | 28 ++-- 45 files changed, 726 insertions(+), 653 deletions(-) diff --git a/frontend/javascripts/libs/UpdatableTexture.ts b/frontend/javascripts/libs/UpdatableTexture.ts index bcd933c4388..a3ec662404a 100644 --- a/frontend/javascripts/libs/UpdatableTexture.ts +++ b/frontend/javascripts/libs/UpdatableTexture.ts @@ -1,4 +1,4 @@ -import * as THREE from "three"; +import { Texture, WebGLRenderer, WebGLUtils, LinearFilter, LinearMipMapLinearFilter } from "three"; import type { TypedArray } from "viewer/constants"; /* The UpdatableTexture class exposes a way to partially update a texture. @@ -17,24 +17,24 @@ import type { TypedArray } from "viewer/constants"; */ let originalTexSubImage2D: WebGL2RenderingContext["texSubImage2D"] | null = null; -class UpdatableTexture extends THREE.Texture { +class UpdatableTexture extends Texture { isUpdatableTexture: boolean = true; - renderer!: THREE.WebGLRenderer; + renderer!: WebGLRenderer; gl!: WebGL2RenderingContext; - utils!: THREE.WebGLUtils; + utils!: WebGLUtils; width: number | undefined; height: number | undefined; constructor( width: number, height: number, - format?: THREE.PixelFormat, - type?: THREE.TextureDataType, - mapping?: THREE.Mapping, - wrapS?: THREE.Wrapping, - wrapT?: THREE.Wrapping, - magFilter?: THREE.MagnificationTextureFilter, - minFilter?: THREE.MinificationTextureFilter, + format?: PixelFormat, + type?: TextureDataType, + mapping?: Mapping, + wrapS?: Wrapping, + wrapT?: Wrapping, + magFilter?: MagnificationTextureFilter, + minFilter?: MinificationTextureFilter, anisotropy?: number, ) { const imageData = { width, height, data: new Uint32Array(0) }; @@ -52,18 +52,18 @@ class UpdatableTexture extends THREE.Texture { anisotropy, ); - this.magFilter = magFilter !== undefined ? magFilter : THREE.LinearFilter; - this.minFilter = minFilter !== undefined ? minFilter : THREE.LinearMipMapLinearFilter; + this.magFilter = magFilter !== undefined ? magFilter : LinearFilter; + this.minFilter = minFilter !== undefined ? minFilter : LinearMipMapLinearFilter; this.generateMipmaps = false; this.flipY = false; this.unpackAlignment = 1; this.needsUpdate = true; } - setRenderer(renderer: THREE.WebGLRenderer) { + setRenderer(renderer: WebGLRenderer) { this.renderer = renderer; this.gl = this.renderer.getContext() as WebGL2RenderingContext; - this.utils = new THREE.WebGLUtils(this.gl, this.renderer.extensions); + this.utils = new WebGLUtils(this.gl, this.renderer.extensions); } isInitialized() { diff --git a/frontend/javascripts/libs/cuckoo/abstract_cuckoo_table.ts b/frontend/javascripts/libs/cuckoo/abstract_cuckoo_table.ts index 14863a87588..8d908e85e3c 100644 --- a/frontend/javascripts/libs/cuckoo/abstract_cuckoo_table.ts +++ b/frontend/javascripts/libs/cuckoo/abstract_cuckoo_table.ts @@ -1,5 +1,5 @@ import type UpdatableTexture from "libs/UpdatableTexture"; -import * as THREE from "three"; +import { UnsignedIntType, RGBAIntegerFormat, type PixelFormatGPU, type PixelFormat } from "three"; import { getRenderer } from "viewer/controller/renderer"; import { createUpdatableTexture } from "viewer/geometries/materials/plane_material_factory_helpers"; @@ -30,14 +30,14 @@ export abstract class AbstractCuckooTable { } static getTextureType() { - return THREE.UnsignedIntType; + return UnsignedIntType; } - static getTextureFormat(): THREE.PixelFormat { - return THREE.RGBAIntegerFormat; + static getTextureFormat(): PixelFormat { + return RGBAIntegerFormat; } - static getInternalFormat(): THREE.PixelFormatGPU { + static getInternalFormat(): PixelFormatGPU { return "RGBA32UI"; } diff --git a/frontend/javascripts/libs/cuckoo/cuckoo_table_uint32.ts b/frontend/javascripts/libs/cuckoo/cuckoo_table_uint32.ts index af840da0afe..debe413a262 100644 --- a/frontend/javascripts/libs/cuckoo/cuckoo_table_uint32.ts +++ b/frontend/javascripts/libs/cuckoo/cuckoo_table_uint32.ts @@ -1,4 +1,4 @@ -import * as THREE from "three"; +import { RGIntegerFormat, type PixelFormatGPU } from "three"; import type { NumberLike } from "viewer/store"; import { AbstractCuckooTable, EMPTY_KEY_VALUE } from "./abstract_cuckoo_table"; @@ -18,9 +18,9 @@ export class CuckooTableUint32 extends AbstractCuckooTable { return 2; } static getTextureFormat() { - return THREE.RGIntegerFormat; + return RGIntegerFormat; } - static getInternalFormat(): THREE.PixelFormatGPU { + static getInternalFormat(): PixelFormatGPU { return "RG32UI"; } static fromCapacity(requestedCapacity: number): CuckooTableUint32 { diff --git a/frontend/javascripts/libs/mjs.ts b/frontend/javascripts/libs/mjs.ts index 16adb115cee..1d656622d68 100644 --- a/frontend/javascripts/libs/mjs.ts +++ b/frontend/javascripts/libs/mjs.ts @@ -4,6 +4,7 @@ import _ from "lodash"; import type { Vector2, Vector3, Vector4 } from "viewer/constants"; import { chunk3 } from "viewer/model/helpers/chunk"; +import type { Vector3 as ThreeVector3 } from "three"; import mjs from "mjs"; @@ -274,6 +275,7 @@ function round(v: Vector3Like, r?: Float32Array | null | undefined) { } // @ts-ignore TS claims that the implementation doesn't match the overloading +function divide3(a: ThreeVector3, k: ThreeVector3, r?: ThreeVector3): Vector3; function divide3(a: Vector3, k: Vector3, r?: Vector3): Vector3; function divide3(a: Float32Array, k: Float32Array, r?: Float32Array) { if (r == null) r = new Float32Array(3); diff --git a/frontend/javascripts/libs/parse_stl_buffer.ts b/frontend/javascripts/libs/parse_stl_buffer.ts index c5b08661851..90fc36db241 100644 --- a/frontend/javascripts/libs/parse_stl_buffer.ts +++ b/frontend/javascripts/libs/parse_stl_buffer.ts @@ -1,7 +1,7 @@ // @ts-nocheck /* eslint-disable */ -import * as THREE from "three"; +import { BufferGeometry, BufferAttribute, Vector3, Float32BufferAttribute, LoaderUtils } from "three"; /** * @author aleeper / http://adamleeper.com/ * @author mrdoob / http://mrdoob.com/ @@ -103,7 +103,7 @@ export default function parse(data) { var dataOffset = 84; var faceLength = 12 * 4 + 2; - var geometry = new THREE.BufferGeometry(); + var geometry = new BufferGeometry(); var vertices = []; var normals = []; @@ -141,11 +141,11 @@ export default function parse(data) { } } - geometry.setAttribute("position", new THREE.BufferAttribute(new Float32Array(vertices), 3)); - geometry.setAttribute("normal", new THREE.BufferAttribute(new Float32Array(normals), 3)); + geometry.setAttribute("position", new BufferAttribute(new Float32Array(vertices), 3)); + geometry.setAttribute("normal", new BufferAttribute(new Float32Array(normals), 3)); if (hasColors) { - geometry.setAttribute("color", new THREE.BufferAttribute(new Float32Array(colors), 3)); + geometry.setAttribute("color", new BufferAttribute(new Float32Array(colors), 3)); geometry.hasColors = true; geometry.alpha = alpha; } @@ -154,7 +154,7 @@ export default function parse(data) { } function parseASCII(data) { - var geometry = new THREE.BufferGeometry(); + var geometry = new BufferGeometry(); var patternFace = /facet([\s\S]*?)endfacet/g; var faceCounter = 0; var patternFloat = /[\s]+([+-]?(?:\d*)(?:\.\d*)?(?:[eE][+-]?\d+)?)/.source; @@ -162,7 +162,7 @@ export default function parse(data) { var patternNormal = new RegExp("normal" + patternFloat + patternFloat + patternFloat, "g"); var vertices = []; var normals = []; - var normal = new THREE.Vector3(); + var normal = new Vector3(); var result; while ((result = patternFace.exec(data)) !== null) { @@ -200,14 +200,14 @@ export default function parse(data) { faceCounter++; } - geometry.setAttribute("position", new THREE.Float32BufferAttribute(vertices, 3)); - geometry.setAttribute("normal", new THREE.Float32BufferAttribute(normals, 3)); + geometry.setAttribute("position", new Float32BufferAttribute(vertices, 3)); + geometry.setAttribute("normal", new Float32BufferAttribute(normals, 3)); return geometry; } function ensureString(buffer) { if (typeof buffer !== "string") { - return THREE.LoaderUtils.decodeText(new Uint8Array(buffer)); + return LoaderUtils.decodeText(new Uint8Array(buffer)); } return buffer; diff --git a/frontend/javascripts/libs/stl_exporter.ts b/frontend/javascripts/libs/stl_exporter.ts index 21a22b74b9b..9f2c7e47841 100644 --- a/frontend/javascripts/libs/stl_exporter.ts +++ b/frontend/javascripts/libs/stl_exporter.ts @@ -1,5 +1,5 @@ /* eslint-disable */ -import * as THREE from "three"; +import { Scene, BufferGeometry, Vector3 } from "three"; // Original Source: https://github.com/mrdoob/three.js/blob/master/examples/js/exporters/STLExporter.js // Manual changes: @@ -34,7 +34,7 @@ class ChunkedDataView { } class STLExporter { - parse(scene: THREE.Scene, options: any = {}) { + parse(scene: Scene, options: any = {}) { const binary = options.binary !== undefined ? options.binary : false; // const objects: any[] = []; @@ -82,12 +82,12 @@ class STLExporter { outputString += "solid exported\n"; } - const vA = new THREE.Vector3(); - const vB = new THREE.Vector3(); - const vC = new THREE.Vector3(); - const cb = new THREE.Vector3(); - const ab = new THREE.Vector3(); - const normal = new THREE.Vector3(); + const vA = new Vector3(); + const vB = new Vector3(); + const vC = new Vector3(); + const cb = new Vector3(); + const ab = new Vector3(); + const normal = new Vector3(); for (let i = 0, il = objects.length; i < il; i++) { const object = objects[i].object3d; diff --git a/frontend/javascripts/libs/trackball_controls.ts b/frontend/javascripts/libs/trackball_controls.ts index f6d00abd4b3..937be63d9d9 100644 --- a/frontend/javascripts/libs/trackball_controls.ts +++ b/frontend/javascripts/libs/trackball_controls.ts @@ -1,5 +1,5 @@ import window, { document } from "libs/window"; -import * as THREE from "three"; +import {OrthographicCamera, Vector2, Vector3, Quaternion, EventDispatcher} from "three"; /** * The MIT License @@ -30,9 +30,9 @@ import * as THREE from "three"; interface ITrackballControls { new ( - object: THREE.OrthographicCamera, + object: OrthographicCamera, domElement: HTMLElement, - target: THREE.Vector3, + target: Vector3, updateCallback: (args: any) => void, ): ITrackballControls; @@ -50,7 +50,7 @@ interface ITrackballControls { minDistance: number; maxDistance: number; keys: number[]; - target: THREE.Vector3; + target: Vector3; rotateCamera: () => void; destroy: () => void; @@ -59,9 +59,9 @@ interface ITrackballControls { const TrackballControls = function ( this: any, - object: THREE.OrthographicCamera, + object: OrthographicCamera, domElement: HTMLElement, - target: THREE.Vector3, + target: Vector3, updateCallback: (args: any) => void, ) { const _this = this; @@ -95,28 +95,28 @@ const TrackballControls = function ( // [A, S, D] this.keys = [65, 83, 68]; // internals - this.target = target || new THREE.Vector3(); + this.target = target || new Vector3(); this.lastTarget = this.target.clone(); - const lastPosition = new THREE.Vector3(); + const lastPosition = new Vector3(); let _state = STATE.NONE; let _prevState = STATE.NONE; - const _eye = new THREE.Vector3(); + const _eye = new Vector3(); - const _rotateStart = new THREE.Vector3(); + const _rotateStart = new Vector3(); - const _rotateEnd = new THREE.Vector3(); + const _rotateEnd = new Vector3(); - const _zoomStart = new THREE.Vector2(); + const _zoomStart = new Vector2(); - const _zoomEnd = new THREE.Vector2(); + const _zoomEnd = new Vector2(); let _touchZoomDistanceStart = 0; let _touchZoomDistanceEnd = 0; - const _panStart = new THREE.Vector2(); + const _panStart = new Vector2(); - const _panEnd = new THREE.Vector2(); + const _panEnd = new Vector2(); // for reset this.target0 = this.target.clone(); @@ -154,7 +154,7 @@ const TrackballControls = function ( this.getMouseOnScreen = function getMouseOnScreen( pageX: number, pageY: number, - vector: THREE.Vector2, + vector: Vector2, ) { const screenBounds = _this.getScreenBounds(); @@ -165,9 +165,9 @@ const TrackballControls = function ( }; this.getMouseProjectionOnBall = (() => { - const objectUp = new THREE.Vector3(); - const mouseOnBall = new THREE.Vector3(); - return (pageX: number, pageY: number, projection: THREE.Vector3) => { + const objectUp = new Vector3(); + const mouseOnBall = new Vector3(); + return (pageX: number, pageY: number, projection: Vector3) => { const screenBounds = _this.getScreenBounds(); mouseOnBall.set( @@ -199,8 +199,8 @@ const TrackballControls = function ( })(); this.rotateCamera = (() => { - const axis = new THREE.Vector3(); - const quaternion = new THREE.Quaternion(); + const axis = new Vector3(); + const quaternion = new Quaternion(); return () => { let angle = Math.acos( _rotateStart.dot(_rotateEnd) / _rotateStart.length() / _rotateEnd.length(), @@ -250,9 +250,9 @@ const TrackballControls = function ( }; this.panCamera = (() => { - const mouseChange = new THREE.Vector2(); - const objectUp = new THREE.Vector3(); - const pan = new THREE.Vector3(); + const mouseChange = new Vector2(); + const objectUp = new Vector3(); + const pan = new Vector3(); return () => { mouseChange.copy(_panEnd).sub(_panStart); @@ -579,5 +579,5 @@ const TrackballControls = function ( this.update(); } as any as ITrackballControls; -TrackballControls.prototype = Object.create(THREE.EventDispatcher.prototype); +TrackballControls.prototype = Object.create(EventDispatcher.prototype); export default TrackballControls; diff --git a/frontend/javascripts/libs/visibility_aware_raycaster.ts b/frontend/javascripts/libs/visibility_aware_raycaster.ts index 48ca8a8e1e8..867359d6798 100644 --- a/frontend/javascripts/libs/visibility_aware_raycaster.ts +++ b/frontend/javascripts/libs/visibility_aware_raycaster.ts @@ -1,21 +1,20 @@ -import * as THREE from "three"; +import { Object3D, Intersection, Raycaster } from "three"; -export type RaycastIntersection = - THREE.Intersection; +export type RaycastIntersection = Intersection; -function ascSort(a: RaycastIntersection, b: RaycastIntersection) { +function ascSort(a: RaycastIntersection, b: RaycastIntersection) { return a.distance - b.distance; } -export default class VisibilityAwareRaycaster extends THREE.Raycaster { - // A modified version of the Raycaster.js from three.js. - // The original version can be found here: https://github.com/mrdoob/three.js/blob/dev/src/core/Raycaster.js. +export default class VisibilityAwareRaycaster extends Raycaster { + // A modified version of the Raycaster.js from js. + // The original version can be found here: https://github.com/mrdoob/js/blob/dev/src/core/Raycaster.js. // Types retrieved from https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/three/src/core/Raycaster.d.ts. - intersectObjects( - objects: THREE.Object3D[], + intersectObjects( + objects: Object3D[], recursive?: boolean, - intersects: THREE.Intersection[] = [], - ): THREE.Intersection[] { + intersects: Intersection[] = [], + ): Intersection[] { for (let i = 0, l = objects.length; i < l; i++) { if (objects[i].visible) { this.intersectObject(objects[i], recursive, intersects); @@ -26,11 +25,11 @@ export default class VisibilityAwareRaycaster extends THREE.Raycaster { return intersects; } - intersectObject( - object: THREE.Object3D, + intersectObject( + object: Object3D, recursive?: boolean, - intersects: THREE.Intersection[] = [], - ): THREE.Intersection[] { + intersects: Intersection[] = [], + ): Intersection[] { if (object.layers.test(this.layers)) { object.raycast(this, intersects); } diff --git a/frontend/javascripts/test/api/api_skeleton_latest.spec.ts b/frontend/javascripts/test/api/api_skeleton_latest.spec.ts index 1b2e6707c93..14ea137a043 100644 --- a/frontend/javascripts/test/api/api_skeleton_latest.spec.ts +++ b/frontend/javascripts/test/api/api_skeleton_latest.spec.ts @@ -14,7 +14,7 @@ import { import { enforceSkeletonTracing } from "viewer/model/accessors/skeletontracing_accessor"; import { setViewportAction } from "viewer/model/actions/view_mode_actions"; import { setRotationAction } from "viewer/model/actions/flycam_actions"; -import * as THREE from "three"; +import { MathUtils, Matrix4, Euler, Quaternion } from "three"; import { eulerAngleToReducerInternalMatrix, reducerInternalMatrixToEulerAngle, @@ -23,20 +23,20 @@ import testRotations from "test/fixtures/test_rotations"; import { map3 } from "libs/utils"; const toRadian = (arr: Vector3): Vector3 => [ - THREE.MathUtils.degToRad(arr[0]), - THREE.MathUtils.degToRad(arr[1]), - THREE.MathUtils.degToRad(arr[2]), + MathUtils.degToRad(arr[0]), + MathUtils.degToRad(arr[1]), + MathUtils.degToRad(arr[2]), ]; function applyRotationInFlycamReducerSpace( flycamRotationInRadian: Vector3, - rotationToApply: THREE.Euler, + rotationToApply: Euler, ): Vector3 { // eulerAngleToReducerInternalMatrix and reducerInternalMatrixToEulerAngle are tested in rotation_helpers.spec.ts. // Calculate expected rotation and make it a quaternion for equal comparison. const rotationMatrix = eulerAngleToReducerInternalMatrix(flycamRotationInRadian); const rotationMatrixWithViewport = rotationMatrix.multiply( - new THREE.Matrix4().makeRotationFromEuler(rotationToApply), + new Matrix4().makeRotationFromEuler(rotationToApply), ); const resultingAngle = reducerInternalMatrixToEulerAngle(rotationMatrixWithViewport); return resultingAngle; @@ -340,9 +340,7 @@ describe("API Skeleton", () => { flycamRotation, OrthoBaseRotations[planeId], ); - const rotationQuaternion = new THREE.Quaternion().setFromEuler( - new THREE.Euler(...rotationForComparison), - ); + const rotationQuaternion = new Quaternion().setFromEuler(new Euler(...rotationForComparison)); Store.dispatch(setRotationAction(flycamRotation)); Store.dispatch(setViewportAction(planeId)); api.tracing.createNode([10, 10, 10], { activate: true }); @@ -363,12 +361,12 @@ describe("API Skeleton", () => { viewport: OrthoViewToNumber[planeId], mag: 0, }); - const newNodeQuaternion = new THREE.Quaternion().setFromEuler( - new THREE.Euler(...toRadian(newNode.rotation)), + const newNodeQuaternion = new Quaternion().setFromEuler( + new Euler(...toRadian(newNode.rotation)), ); expect( rotationQuaternion.angleTo(newNodeQuaternion), - `Node rotation ${newNode.rotation} is not nearly equal to ${map3(THREE.MathUtils.radToDeg, rotationForComparison)} in viewport ${planeId}.`, + `Node rotation ${newNode.rotation} is not nearly equal to ${map3(MathUtils.radToDeg, rotationForComparison)} in viewport ${planeId}.`, ).toBeLessThan(0.000001); } }); @@ -382,9 +380,7 @@ describe("API Skeleton", () => { rotationInRadian, OrthoBaseRotations[planeId], ); - const rotationQuaternion = new THREE.Quaternion().setFromEuler( - new THREE.Euler(...resultingAngle), - ); + const rotationQuaternion = new Quaternion().setFromEuler(new Euler(...resultingAngle)); // Test node creation. Store.dispatch(setRotationAction(testRotation)); Store.dispatch(setViewportAction(planeId)); @@ -394,8 +390,8 @@ describe("API Skeleton", () => { const newNode = skeletonTracing.trees .getOrThrow(skeletonTracing.activeTreeId || -1) .nodes.getOrThrow(skeletonTracing.activeNodeId || -1); - const newNodeQuaternion = new THREE.Quaternion().setFromEuler( - new THREE.Euler(...toRadian(newNode.rotation)), + const newNodeQuaternion = new Quaternion().setFromEuler( + new Euler(...toRadian(newNode.rotation)), ); expect( rotationQuaternion.angleTo(newNodeQuaternion), diff --git a/frontend/javascripts/test/misc/rotation_helpers.spec.ts b/frontend/javascripts/test/misc/rotation_helpers.spec.ts index 63337fc0e90..d7dfb63273c 100644 --- a/frontend/javascripts/test/misc/rotation_helpers.spec.ts +++ b/frontend/javascripts/test/misc/rotation_helpers.spec.ts @@ -4,22 +4,18 @@ import { reducerInternalMatrixToEulerAngle, } from "viewer/model/helpers/rotation_helpers"; import type { Vector3 } from "viewer/constants"; -import * as THREE from "three"; +import { MathUtils, Quaternion, Euler } from "three"; import { map3 } from "libs/utils"; import testRotations from "test/fixtures/test_rotations"; describe("Rotation Helper Functions", () => { it("should result in an equal rotation after transforming into flycam reducer rotation space and back.", () => { for (const testRotation of testRotations) { - const inputRotationInRadian = map3(THREE.MathUtils.degToRad, testRotation); + const inputRotationInRadian = map3(MathUtils.degToRad, testRotation); const rotationMatrix = eulerAngleToReducerInternalMatrix(inputRotationInRadian); const resultingAngle = reducerInternalMatrixToEulerAngle(rotationMatrix); - const inputQuaternion = new THREE.Quaternion().setFromEuler( - new THREE.Euler(...inputRotationInRadian), - ); - const outputQuaternion = new THREE.Quaternion().setFromEuler( - new THREE.Euler(...resultingAngle), - ); + const inputQuaternion = new Quaternion().setFromEuler(new Euler(...inputRotationInRadian)); + const outputQuaternion = new Quaternion().setFromEuler(new Euler(...resultingAngle)); expect( inputQuaternion.angleTo(outputQuaternion), `Angle ${testRotation} is not converted properly`, @@ -29,35 +25,29 @@ describe("Rotation Helper Functions", () => { // This tests goal is to test and document that the output after converting back from the 'flycam rotation reducer space' the result needs to be interpreted as a XYZ Euler angle. it("should *not* result in an equal rotation after transforming into flycam reducer rotation space and back if interpreted in wrong euler order.", () => { const testRotation = [30, 90, 40] as Vector3; - const inputRotationInRadian = map3(THREE.MathUtils.degToRad, testRotation); + const inputRotationInRadian = map3(MathUtils.degToRad, testRotation); const rotationMatrix = eulerAngleToReducerInternalMatrix(inputRotationInRadian); const resultingAngle = reducerInternalMatrixToEulerAngle(rotationMatrix); - const inputQuaternionZYX = new THREE.Quaternion().setFromEuler( - new THREE.Euler(...inputRotationInRadian, "ZYX"), - ); - const outputQuaternionZYX = new THREE.Quaternion().setFromEuler( - new THREE.Euler(...resultingAngle, "ZYX"), + const inputQuaternionZYX = new Quaternion().setFromEuler( + new Euler(...inputRotationInRadian, "ZYX"), ); + const outputQuaternionZYX = new Quaternion().setFromEuler(new Euler(...resultingAngle, "ZYX")); expect( inputQuaternionZYX.angleTo(outputQuaternionZYX), `Angle ${testRotation} is equal although interpreted as 'ZYX' euler order. `, ).toBeGreaterThan(0.001); - const inputQuaternionYZX = new THREE.Quaternion().setFromEuler( - new THREE.Euler(...inputRotationInRadian, "YZX"), - ); - const outputQuaternionYZX = new THREE.Quaternion().setFromEuler( - new THREE.Euler(...resultingAngle, "YZX"), + const inputQuaternionYZX = new Quaternion().setFromEuler( + new Euler(...inputRotationInRadian, "YZX"), ); + const outputQuaternionYZX = new Quaternion().setFromEuler(new Euler(...resultingAngle, "YZX")); expect( inputQuaternionYZX.angleTo(outputQuaternionYZX), `Angle ${testRotation} is equal although interpreted as 'YZX' euler order. `, ).toBeGreaterThan(0.001); - const inputQuaternionXYZ = new THREE.Quaternion().setFromEuler( - new THREE.Euler(...inputRotationInRadian, "XYZ"), - ); - const outputQuaternionXYZ = new THREE.Quaternion().setFromEuler( - new THREE.Euler(...resultingAngle, "XYZ"), + const inputQuaternionXYZ = new Quaternion().setFromEuler( + new Euler(...inputRotationInRadian, "XYZ"), ); + const outputQuaternionXYZ = new Quaternion().setFromEuler(new Euler(...resultingAngle, "XYZ")); expect( inputQuaternionXYZ.angleTo(outputQuaternionXYZ), `Angle ${testRotation} is not equal although interpreted as 'XYZ' euler order should be correct. `, diff --git a/frontend/javascripts/test/mocks/updatable_texture.mock.ts b/frontend/javascripts/test/mocks/updatable_texture.mock.ts index d704bbe7b20..3eb7be0b1d9 100644 --- a/frontend/javascripts/test/mocks/updatable_texture.mock.ts +++ b/frontend/javascripts/test/mocks/updatable_texture.mock.ts @@ -1,4 +1,11 @@ -import * as THREE from "three"; +import { + RedFormat, + RedIntegerFormat, + RGFormat, + RGIntegerFormat, + RGBAFormat, + RGBAIntegerFormat, +} from "three"; import { vi } from "vitest"; /* @@ -7,12 +14,12 @@ import { vi } from "vitest"; * since RGBAFormat is also used for 3 channels which would make the key not unique. */ const formatToChannelCount = new Map([ - [THREE.RedFormat, 1], - [THREE.RedIntegerFormat, 1], - [THREE.RGFormat, 2], - [THREE.RGIntegerFormat, 2], - [THREE.RGBAFormat, 4], - [THREE.RGBAIntegerFormat, 4], + [RedFormat, 1], + [RedIntegerFormat, 1], + [RGFormat, 2], + [RGIntegerFormat, 2], + [RGBAFormat, 4], + [RGBAIntegerFormat, 4], ]); class MockUpdatableTexture { diff --git a/frontend/javascripts/test/model/accessors/view_mode_accessors.spec.ts b/frontend/javascripts/test/model/accessors/view_mode_accessors.spec.ts index 0f5c983cc09..e88c1d452ae 100644 --- a/frontend/javascripts/test/model/accessors/view_mode_accessors.spec.ts +++ b/frontend/javascripts/test/model/accessors/view_mode_accessors.spec.ts @@ -9,7 +9,7 @@ import { M4x4, V3 } from "libs/mjs"; import Dimensions from "viewer/model/dimensions"; import { setRotationAction } from "viewer/model/actions/flycam_actions"; import FlycamReducer from "viewer/model/reducers/flycam_reducer"; -import * as THREE from "three"; +import { MathUtils, Vector3 as ThreeVector3, Euler } from "three"; import { map3 } from "libs/utils"; import { getBaseVoxelFactorsInUnit } from "viewer/model/scaleinfo"; import { almostEqual } from "test/libs/transform_spec_helpers"; @@ -177,7 +177,7 @@ describe("View mode accessors", () => { // When using the rotation of the flycam for calculations, one has to invert the z value and interpret the resulting euler angle as ZYX. // More info about this at https://www.notion.so/scalableminds/3D-Rotations-3D-Scene-210b51644c6380c2a4a6f5f3c069738a?source=copy_link#22bb51644c6380138fdac454d4dac2f0. const rotationCorrected = [rotation[0], rotation[1], -rotation[2]] as Vector3; - const rotationInRadian = map3(THREE.MathUtils.degToRad, rotationCorrected); + const rotationInRadian = map3(MathUtils.degToRad, rotationCorrected); for (const offset of testOffsets) { const stateWithCorrectPlaneActive = update(initialState, { viewModeData: { plane: { activeViewport: { $set: OrthoViews.PLANE_XY } } }, @@ -193,8 +193,8 @@ describe("View mode accessors", () => { clickPositionAtViewportCenter, ); // Applying the rotation of 83° around the x axis to the offset. - const rotatedOffset = new THREE.Vector3(offset[0], offset[1], 0) - .applyEuler(new THREE.Euler(...rotationInRadian, "ZYX")) + const rotatedOffset = new ThreeVector3(offset[0], offset[1], 0) + .applyEuler(new Euler(...rotationInRadian, "ZYX")) .toArray(); const expectedPosition = [...V3.add(initialFlycamPosition, rotatedOffset)] as Vector3; almostEqual( @@ -213,7 +213,7 @@ describe("View mode accessors", () => { // When using the rotation of the flycam for calculations, one has to invert the z value and interpret the resulting euler angle as ZYX. // More info about this at https://www.notion.so/scalableminds/3D-Rotations-3D-Scene-210b51644c6380c2a4a6f5f3c069738a?source=copy_link#22bb51644c6380138fdac454d4dac2f0. const rotationCorrected = [rotation[0], rotation[1], -rotation[2]] as Vector3; - const rotationInRadian = map3(THREE.MathUtils.degToRad, rotationCorrected); + const rotationInRadian = map3(MathUtils.degToRad, rotationCorrected); for (const offset of testOffsets) { const stateWithAnisotropicScale = update(initialState, { dataset: { dataSource: { scale: { factor: { $set: anisotropicDatasetScale } } } }, @@ -230,9 +230,9 @@ describe("View mode accessors", () => { ); // Applying the rotation of 83° around the x axis to the offset and the apply the dataset scale. const scaleFactor = getBaseVoxelFactorsInUnit(rotatedState.dataset.dataSource.scale); - const rotatedAndScaledOffset = new THREE.Vector3(offset[0], offset[1], 0) - .applyEuler(new THREE.Euler(...rotationInRadian, "ZYX")) - .multiply(new THREE.Vector3(...scaleFactor)) + const rotatedAndScaledOffset = new ThreeVector3(offset[0], offset[1], 0) + .applyEuler(new Euler(...rotationInRadian, "ZYX")) + .multiply(new ThreeVector3(...scaleFactor)) .toArray(); const expectedPosition = [ ...V3.add(initialFlycamPosition, V3.round(rotatedAndScaledOffset)), @@ -259,7 +259,7 @@ describe("View mode accessors", () => { // When using the rotation of the flycam for calculations, one has to invert the z value and interpret the resulting euler angle as ZYX. // More info about this at https://www.notion.so/scalableminds/3D-Rotations-3D-Scene-210b51644c6380c2a4a6f5f3c069738a?source=copy_link#22bb51644c6380138fdac454d4dac2f0. const rotationCorrected = [rotation[0], rotation[1], -rotation[2]] as Vector3; - const rotationInRadian = map3(THREE.MathUtils.degToRad, rotationCorrected); + const rotationInRadian = map3(MathUtils.degToRad, rotationCorrected); for (const offset of testOffsets) { for (const planeId of OrthoViewValuesWithoutTDView) { const stateWithAnisotropicScale = update(initialState, { @@ -307,7 +307,7 @@ describe("View mode accessors", () => { // When using the rotation of the flycam for calculations, one has to invert the z value and interpret the resulting euler angle as ZYX. // More info about this at https://www.notion.so/scalableminds/3D-Rotations-3D-Scene-210b51644c6380c2a4a6f5f3c069738a?source=copy_link#22bb51644c6380138fdac454d4dac2f0. const rotationCorrected = [rotation[0], rotation[1], -rotation[2]] as Vector3; - const rotationInRadian = map3(THREE.MathUtils.degToRad, rotationCorrected); + const rotationInRadian = map3(MathUtils.degToRad, rotationCorrected); for (const offset of testOffsets) { for (const planeId of OrthoViewValuesWithoutTDView) { const stateWithAnisotropicScale = update(initialState, { diff --git a/frontend/javascripts/viewer/api/api_latest.ts b/frontend/javascripts/viewer/api/api_latest.ts index 5eacd61109a..d8eb96bfe5c 100644 --- a/frontend/javascripts/viewer/api/api_latest.ts +++ b/frontend/javascripts/viewer/api/api_latest.ts @@ -17,7 +17,7 @@ import { coalesce, mod } from "libs/utils"; import window, { location } from "libs/window"; import _ from "lodash"; import messages from "messages"; -import * as THREE from "three"; +import { Vector3 as ThreeVector3, Matrix4, Euler, Quaternion, MathUtils } from "three"; import TWEEN from "tween.js"; import { type APICompoundType, APICompoundTypeEnum, type ElementClass } from "types/api_types"; import type { AdditionalCoordinate } from "types/api_types"; @@ -1417,9 +1417,7 @@ class TracingApi { // As the node rotation was calculated in the way the flycam reducer does its matrix rotation // by using the rotation_helpers.ts there is no need to invert z and we must use XYZ ordered euler angles. const curRotation = getRotationInRadian(flycam, false); - const startQuaternion = new THREE.Quaternion().setFromEuler( - new THREE.Euler(...curRotation, "XYZ"), - ); + const startQuaternion = new Quaternion().setFromEuler(new Euler(...curRotation, "XYZ")); const isNotRotated = V3.equals(curRotation, [0, 0, 0]); const dimensionToSkip = skipCenteringAnimationInThirdDimension && activeViewport !== OrthoViews.TDView && isNotRotated @@ -1428,9 +1426,9 @@ class TracingApi { if (rotation == null) { rotation = curRotation; } else { - rotation = Utils.map3(THREE.MathUtils.degToRad, rotation); + rotation = Utils.map3(MathUtils.degToRad, rotation); } - const endQuaternion = new THREE.Quaternion().setFromEuler(new THREE.Euler(...rotation, "XYZ")); + const endQuaternion = new Quaternion().setFromEuler(new Euler(...rotation, "XYZ")); type Tweener = { positionX: number; @@ -1457,16 +1455,13 @@ class TracingApi { setPositionAction([this.positionX, this.positionY, this.positionZ], dimensionToSkip), ); // Interpolating rotation via quaternions to get shortest rotation. - const interpolatedQuaternion = new THREE.Quaternion().slerpQuaternions( + const interpolatedQuaternion = new Quaternion().slerpQuaternions( startQuaternion, endQuaternion, t, ); - const interpolatedEuler = new THREE.Euler().setFromQuaternion( - interpolatedQuaternion, - "XYZ", - ); - const interpolatedEulerInDegree = Utils.map3(THREE.MathUtils.radToDeg, [ + const interpolatedEuler = new Euler().setFromQuaternion(interpolatedQuaternion, "XYZ"); + const interpolatedEulerInDegree = Utils.map3(MathUtils.radToDeg, [ interpolatedEuler.x, interpolatedEuler.y, interpolatedEuler.z, diff --git a/frontend/javascripts/viewer/constants.ts b/frontend/javascripts/viewer/constants.ts index 1f73f93a136..5d4f0827401 100644 --- a/frontend/javascripts/viewer/constants.ts +++ b/frontend/javascripts/viewer/constants.ts @@ -1,4 +1,4 @@ -import * as THREE from "three"; +import { Euler, Matrix4 } from "three"; export type AdditionalCoordinate = { name: string; value: number }; export const ViewModeValues = ["orthogonal", "flight", "oblique"] as ViewMode[]; @@ -132,19 +132,19 @@ export const OrthoViewCrosshairColors: OrthoViewMap<[number, number]> = { // See the following or an explanation about the relative orientation of the viewports toward the XY viewport. // https://www.notion.so/scalableminds/3D-Rotations-3D-Scene-210b51644c6380c2a4a6f5f3c069738a?source=copy_link#22bb51644c63800e8682e92a5c91a519 export const OrthoBaseRotations = { - [OrthoViews.PLANE_XY]: new THREE.Euler(0, 0, 0), - [OrthoViews.PLANE_YZ]: new THREE.Euler(0, (3 / 2) * Math.PI, 0), - [OrthoViews.PLANE_XZ]: new THREE.Euler(Math.PI / 2, 0, 0), - [OrthoViews.TDView]: new THREE.Euler(Math.PI / 4, Math.PI / 4, Math.PI / 4), + [OrthoViews.PLANE_XY]: new Euler(0, 0, 0), + [OrthoViews.PLANE_YZ]: new Euler(0, (3 / 2) * Math.PI, 0), + [OrthoViews.PLANE_XZ]: new Euler(Math.PI / 2, 0, 0), + [OrthoViews.TDView]: new Euler(Math.PI / 4, Math.PI / 4, Math.PI / 4), }; -function correctCameraViewingDirection(baseEuler: THREE.Euler): THREE.Euler { - const cameraCorrectionEuler = new THREE.Euler(Math.PI, 0, 0); - const correctedEuler = new THREE.Euler(); +function correctCameraViewingDirection(baseEuler: Euler): Euler { + const cameraCorrectionEuler = new Euler(Math.PI, 0, 0); + const correctedEuler = new Euler(); correctedEuler.setFromRotationMatrix( - new THREE.Matrix4() + new Matrix4() .makeRotationFromEuler(baseEuler) - .multiply(new THREE.Matrix4().makeRotationFromEuler(cameraCorrectionEuler)), + .multiply(new Matrix4().makeRotationFromEuler(cameraCorrectionEuler)), "ZYX", ); @@ -157,7 +157,7 @@ export const OrthoCamerasBaseRotations = { [OrthoViews.PLANE_XY]: correctCameraViewingDirection(OrthoBaseRotations[OrthoViews.PLANE_XY]), [OrthoViews.PLANE_YZ]: correctCameraViewingDirection(OrthoBaseRotations[OrthoViews.PLANE_YZ]), [OrthoViews.PLANE_XZ]: correctCameraViewingDirection(OrthoBaseRotations[OrthoViews.PLANE_XZ]), - [OrthoViews.TDView]: new THREE.Euler(Math.PI / 4, Math.PI / 4, Math.PI / 4), + [OrthoViews.TDView]: new Euler(Math.PI / 4, Math.PI / 4, Math.PI / 4), }; export type BorderTabType = { diff --git a/frontend/javascripts/viewer/controller/camera_controller.ts b/frontend/javascripts/viewer/controller/camera_controller.ts index 55c0aa5dcb0..9f445a54789 100644 --- a/frontend/javascripts/viewer/controller/camera_controller.ts +++ b/frontend/javascripts/viewer/controller/camera_controller.ts @@ -2,7 +2,7 @@ import { V3 } from "libs/mjs"; import * as Utils from "libs/utils"; import _ from "lodash"; import * as React from "react"; -import * as THREE from "three"; +import { OrthographicCamera, Euler, Matrix4, Vector3 as ThreeVector3, Quaternion } from "three"; import TWEEN from "tween.js"; import type { OrthoView, OrthoViewMap, OrthoViewRects, Vector3 } from "viewer/constants"; import { @@ -24,7 +24,7 @@ import type { CameraData } from "viewer/store"; import Store from "viewer/store"; type Props = { - cameras: OrthoViewMap; + cameras: OrthoViewMap; onCameraPositionChanged: () => void; onTDCameraChanged: (userTriggered?: boolean) => void; setTargetAndFixPosition: () => void; @@ -40,15 +40,15 @@ function getQuaternionFromCamera(_up: Vector3, position: Vector3, center: Vector const correctedUp = V3.normalize(V3.cross(forward, right)); // Create a basis matrix - const rotationMatrix = new THREE.Matrix4(); + const rotationMatrix = new Matrix4(); rotationMatrix.makeBasis( - new THREE.Vector3(...right), - new THREE.Vector3(...correctedUp), - new THREE.Vector3(...forward), + new ThreeVector3(...right), + new ThreeVector3(...correctedUp), + new ThreeVector3(...forward), ); // Convert to quaternion - const quat = new THREE.Quaternion(); + const quat = new Quaternion(); quat.setFromRotationMatrix(rotationMatrix); return quat; } @@ -82,10 +82,10 @@ class CameraController extends React.PureComponent { // @ts-expect-error ts-migrate(2564) FIXME: Property 'storePropertyUnsubscribers' has no initi... Remove this comment to see the full error message storePropertyUnsubscribers: Array<(...args: Array) => any>; // Properties are only created here to avoid creating new objects for each update call. - flycamRotationEuler = new THREE.Euler(); - flycamRotationMatrix = new THREE.Matrix4(); - baseRotationMatrix = new THREE.Matrix4(); - totalRotationMatrix = new THREE.Matrix4(); + flycamRotationEuler = new Euler(); + flycamRotationMatrix = new Matrix4(); + baseRotationMatrix = new Matrix4(); + totalRotationMatrix = new Matrix4(); componentDidMount() { // Take the whole diagonal extent of the dataset to get the possible maximum extent of the dataset. @@ -233,7 +233,7 @@ class CameraController extends React.PureComponent { tdCamera.right = cameraData.right; tdCamera.top = cameraData.top; tdCamera.bottom = cameraData.bottom; - tdCamera.up = new THREE.Vector3(...cameraData.up); + tdCamera.up = new ThreeVector3(...cameraData.up); tdCamera.updateProjectionMatrix(); this.props.onCameraPositionChanged(); } @@ -302,11 +302,11 @@ export function rotate3DViewTo( [OrthoViews.TDView]: [0, 0, -1], }; - const positionOffsetVector = new THREE.Vector3(...positionOffsetMap[id]); - const upVector = new THREE.Vector3(...upVectorMap[id]); + const positionOffsetVector = new ThreeVector3(...positionOffsetMap[id]); + const upVector = new ThreeVector3(...upVectorMap[id]); // Rotate the positionOffsetVector and upVector by the flycam rotation. - const rotatedOffset = positionOffsetVector.applyEuler(new THREE.Euler(...flycamRotation, "ZYX")); - const rotatedUp = upVector.applyEuler(new THREE.Euler(...flycamRotation, "ZYX")); + const rotatedOffset = positionOffsetVector.applyEuler(new Euler(...flycamRotation, "ZYX")); + const rotatedUp = upVector.applyEuler(new Euler(...flycamRotation, "ZYX")); const position = [ flycamPos[0] + rotatedOffset.x, flycamPos[1] + rotatedOffset.y, @@ -330,7 +330,7 @@ export function rotate3DViewTo( const updateCameraTDView = (tweenState: TweenState, t: number) => { const { left, right, top, bottom } = tweenState; - const tweenedQuat = new THREE.Quaternion(); + const tweenedQuat = new Quaternion(); tweenedQuat.slerpQuaternions(startQuaternion, targetQuaternion, t); const tweened = getCameraFromQuaternion(tweenedQuat); // Use forward vector and currentFlycamPos (lookAt target) to calculate the current diff --git a/frontend/javascripts/viewer/controller/combinations/skeleton_handlers.ts b/frontend/javascripts/viewer/controller/combinations/skeleton_handlers.ts index 6df28f7764a..bddb11f2879 100644 --- a/frontend/javascripts/viewer/controller/combinations/skeleton_handlers.ts +++ b/frontend/javascripts/viewer/controller/combinations/skeleton_handlers.ts @@ -1,7 +1,7 @@ import { V3 } from "libs/mjs"; import { values } from "libs/utils"; import _ from "lodash"; -import * as THREE from "three"; +import { Euler, Matrix4, Vector3 as ThreeVector3, Scene } from "three"; import type { AdditionalCoordinate } from "types/api_types"; import type { OrthoView, Point2, Vector3, Viewport } from "viewer/constants"; import { OrthoBaseRotations, OrthoViewToNumber, OrthoViews } from "viewer/constants"; @@ -133,7 +133,7 @@ export function handleOpenContextMenu( isTouch: boolean, event: MouseEvent, meshId?: number | null | undefined, - meshIntersectionPosition?: Vector3 | null | undefined, + meshIntersectionPosition?: ThreeVector3 | null | undefined, unmappedSegmentId?: number | null | undefined, ) { const { activeViewport } = Store.getState().viewModeData.plane; @@ -170,9 +170,9 @@ export function handleOpenContextMenu( } // Already defined here at toplevel to avoid object recreation with each call. Make sure to not do anything async between read and writes. -const flycamRotationEuler = new THREE.Euler(); -const flycamRotationMatrix = new THREE.Matrix4(); -const movementVector = new THREE.Vector3(); +const flycamRotationEuler = new Euler(); +const flycamRotationMatrix = new Matrix4(); +const movementVector = new ThreeVector3(); export function moveNode( dx: number, @@ -425,7 +425,7 @@ export function maybeGetNodeIdFromPosition( // render the clicked viewport with picking enabled // we need a dedicated pickingScene, since we only want to render all nodes and no planes / bounding box / edges etc. const pickingNode = skeleton.startPicking(isTouch); - const pickingScene = new THREE.Scene(); + const pickingScene = new Scene(); pickingScene.add(pickingNode); const camera = planeView.getCameraForPlane(plane); diff --git a/frontend/javascripts/viewer/controller/combinations/tool_controls.ts b/frontend/javascripts/viewer/controller/combinations/tool_controls.ts index 47d57f61959..969c1d7a815 100644 --- a/frontend/javascripts/viewer/controller/combinations/tool_controls.ts +++ b/frontend/javascripts/viewer/controller/combinations/tool_controls.ts @@ -3,7 +3,7 @@ import type { ModifierKeys } from "libs/input"; import { V3 } from "libs/mjs"; import * as Utils from "libs/utils"; import { document } from "libs/window"; -import * as THREE from "three"; +import { Vector3 as ThreeVector3, Color } from "three"; import { ContourModeEnum, type OrthoView, @@ -758,7 +758,7 @@ export class QuickSelectToolController { } const [h, s, l] = getSegmentColorAsHSLA(state, volumeTracing.activeCellId); - const activeCellColor = new THREE.Color().setHSL(h, s, l); + const activeCellColor = new Color().setHSL(h, s, l); quickSelectGeometry.setColor(activeCellColor); startPos = calculateGlobalPos(state, pos).rounded; currentPos = startPos; diff --git a/frontend/javascripts/viewer/controller/custom_lod.ts b/frontend/javascripts/viewer/controller/custom_lod.ts index 52bbb986b51..9bfa3f8441f 100644 --- a/frontend/javascripts/viewer/controller/custom_lod.ts +++ b/frontend/javascripts/viewer/controller/custom_lod.ts @@ -1,15 +1,15 @@ -import * as THREE from "three"; +import { LOD, Group } from "three"; import { getTDViewZoom } from "viewer/model/accessors/view_mode_accessor"; import Store from "viewer/store"; -export default class CustomLOD extends THREE.LOD { - noLODGroup: THREE.Group; +export default class CustomLOD extends LOD { + noLODGroup: Group; lodLevelCount: number; constructor() { super(); this.lodLevelCount = 0; - this.noLODGroup = new THREE.Group(); + this.noLODGroup = new Group(); this.add(this.noLODGroup); } @@ -43,23 +43,23 @@ export default class CustomLOD extends THREE.LOD { } } - addNoLODSupportedMesh(meshGroup: THREE.Group) { + addNoLODSupportedMesh(meshGroup: Group) { this.noLODGroup.add(meshGroup); } - addLODMesh(meshGroup: THREE.Group, level: number) { + addLODMesh(meshGroup: Group, level: number) { while (this.lodLevelCount <= level) { - this.addLevel(new THREE.Group(), this.lodLevelCount); + this.addLevel(new Group(), this.lodLevelCount); this.lodLevelCount++; } this.levels[level].object.add(meshGroup); } - removeNoLODSupportedMesh(meshGroup: THREE.Group) { + removeNoLODSupportedMesh(meshGroup: Group) { this.noLODGroup.remove(meshGroup); } - removeLODMesh(meshGroup: THREE.Group, level: number) { + removeLODMesh(meshGroup: Group, level: number) { this.levels[level].object.remove(meshGroup); } } diff --git a/frontend/javascripts/viewer/controller/renderer.ts b/frontend/javascripts/viewer/controller/renderer.ts index 66a36f563c8..c3b6b167b63 100644 --- a/frontend/javascripts/viewer/controller/renderer.ts +++ b/frontend/javascripts/viewer/controller/renderer.ts @@ -1,9 +1,9 @@ import { notifyAboutDisposedRenderer } from "libs/UpdatableTexture"; import { document } from "libs/window"; -import * as THREE from "three"; +import { WebGLRenderer } from "three"; import { Store } from "viewer/singletons"; -let renderer: THREE.WebGLRenderer | null = null; +let renderer: WebGLRenderer | null = null; export function destroyRenderer(): void { if (renderer == null) { @@ -14,7 +14,7 @@ export function destroyRenderer(): void { notifyAboutDisposedRenderer(); } -function getRenderer(): THREE.WebGLRenderer { +function getRenderer(): WebGLRenderer { if (renderer != null) { return renderer; } @@ -23,7 +23,7 @@ function getRenderer(): THREE.WebGLRenderer { renderer = ( renderCanvasElement != null ? // Create a WebGL2 renderer - new THREE.WebGLRenderer({ + new WebGLRenderer({ canvas: renderCanvasElement, // This prevents flickering when rendering to a buffer instead of the canvas preserveDrawingBuffer: true, @@ -38,7 +38,7 @@ function getRenderer(): THREE.WebGLRenderer { antialias: Store.getState().userConfiguration.antialiasRendering, }) : {} - ) as THREE.WebGLRenderer; + ) as WebGLRenderer; return renderer; } diff --git a/frontend/javascripts/viewer/controller/scene_controller.ts b/frontend/javascripts/viewer/controller/scene_controller.ts index a38a11bf85d..9d2663ccbf3 100644 --- a/frontend/javascripts/viewer/controller/scene_controller.ts +++ b/frontend/javascripts/viewer/controller/scene_controller.ts @@ -4,7 +4,23 @@ import * as Utils from "libs/utils"; import window from "libs/window"; import _ from "lodash"; -import * as THREE from "three"; +import { + BufferGeometry, + Mesh, + Group, + WebGLRenderer, + Scene, + Vector3 as ThreeVector3, + Euler, + Matrix4, + BoxGeometry, + EdgesGeometry, + LineBasicMaterial, + LineSegments, + MeshBasicMaterial, + Line, + Color, +} from "three"; import { acceleratedRaycast, computeBoundsTree, disposeBoundsTree } from "three-mesh-bvh"; import type { BoundingBoxMinMaxType } from "types/bounding_box"; import type { OrthoView, OrthoViewMap, OrthoViewWithoutTDMap, Vector3 } from "viewer/constants"; @@ -60,9 +76,9 @@ import type CustomLOD from "./custom_lod"; import SegmentMeshController from "./segment_mesh_controller"; // Add the extension functions -THREE.BufferGeometry.prototype.computeBoundsTree = computeBoundsTree; -THREE.BufferGeometry.prototype.disposeBoundsTree = disposeBoundsTree; -THREE.Mesh.prototype.raycast = acceleratedRaycast; +BufferGeometry.prototype.computeBoundsTree = computeBoundsTree; +BufferGeometry.prototype.disposeBoundsTree = disposeBoundsTree; +Mesh.prototype.raycast = acceleratedRaycast; const CUBE_COLOR = 0x999999; const LAYER_CUBE_COLOR = 0xffff99; @@ -76,11 +92,11 @@ class SceneController { isPlaneVisible: OrthoViewMap; clippingDistanceInUnit: number; datasetBoundingBox!: Cube; - userBoundingBoxGroup!: THREE.Group; - layerBoundingBoxGroup!: THREE.Group; + userBoundingBoxGroup!: Group; + layerBoundingBoxGroup!: Group; userBoundingBoxes!: Array; layerBoundingBoxes!: { [layerName: string]: Cube }; - annotationToolsGeometryGroup!: THREE.Group; + annotationToolsGeometryGroup!: Group; highlightedBBoxId: number | null | undefined; taskCubeByTracingId: Record = {}; contour!: ContourGeometry; @@ -88,17 +104,17 @@ class SceneController { lineMeasurementGeometry!: LineMeasurementGeometry; areaMeasurementGeometry!: ContourGeometry; planes!: OrthoViewWithoutTDMap; - rootNode!: THREE.Group; - renderer!: THREE.WebGLRenderer; - scene!: THREE.Scene; - rootGroup!: THREE.Group; + rootNode!: Group; + renderer!: WebGLRenderer; + scene!: Scene; + rootGroup!: Group; segmentMeshController: SegmentMeshController; storePropertyUnsubscribers: Array<() => void>; - splitBoundaryMesh: THREE.Mesh | null = null; + splitBoundaryMesh: Mesh | null = null; // Created as instance properties to avoid creating objects in each update call. - private rotatedPositionOffsetVector = new THREE.Vector3(); - private flycamRotationEuler = new THREE.Euler(); + private rotatedPositionOffsetVector = new ThreeVector3(); + private flycamRotationEuler = new Euler(); // This class collects all the meshes displayed in the Skeleton View and updates position and scale of each // element depending on the provided flycam. @@ -118,9 +134,9 @@ class SceneController { this.renderer = getRenderer(); this.createMeshes(); this.bindToEvents(); - this.scene = new THREE.Scene(); + this.scene = new Scene(); this.highlightedBBoxId = null; - this.rootGroup = new THREE.Group(); + this.rootGroup = new Group(); this.scene.add( this.rootGroup.add( this.rootNode, @@ -135,7 +151,7 @@ class SceneController { // scene.scale does not have an effect. // The dimension(s) with the highest mag will not be distorted. this.rootGroup.scale.copy( - new THREE.Vector3(...Store.getState().dataset.dataSource.scale.factor), + new ThreeVector3(...Store.getState().dataset.dataSource.scale.factor), ); this.setupDebuggingMethods(); } @@ -155,13 +171,13 @@ class SceneController { constants.BUCKET_WIDTH * mag[1], constants.BUCKET_WIDTH * mag[2], ]; - const boxGeometry = new THREE.BoxGeometry(...bucketSize); - const edgesGeometry = new THREE.EdgesGeometry(boxGeometry); - const material = new THREE.LineBasicMaterial({ + const boxGeometry = new BoxGeometry(...bucketSize); + const edgesGeometry = new EdgesGeometry(boxGeometry); + const material = new LineBasicMaterial({ color: optColor || (zoomStep === 0 ? 0xff00ff : 0x00ffff), linewidth: 1, }); - const cube = new THREE.LineSegments(edgesGeometry, material); + const cube = new LineSegments(edgesGeometry, material); cube.position.x = position[0] + bucketSize[0] / 2; cube.position.y = position[1] + bucketSize[1] / 2; cube.position.z = position[2] + bucketSize[2] / 2; @@ -174,12 +190,12 @@ class SceneController { // Shrink voxels a bit so that it's easier to identify individual voxels. const cubeLength = _cubeLength.map((el) => el * 0.9); - const boxGeometry = new THREE.BoxGeometry(...cubeLength); - const material = new THREE.MeshBasicMaterial({ + const boxGeometry = new BoxGeometry(...cubeLength); + const material = new MeshBasicMaterial({ color: optColor || 0xff00ff, opacity: 0.5, }); - const cube = new THREE.Mesh(boxGeometry, material); + const cube = new Mesh(boxGeometry, material); cube.position.x = position[0] + cubeLength[0] / 2; cube.position.y = position[1] + cubeLength[1] / 2; cube.position.z = position[2] + cubeLength[2] / 2; @@ -187,19 +203,19 @@ class SceneController { return cube; }; - let renderedLines: THREE.Line[] = []; + let renderedLines: Line[] = []; // Utility function for visual debugging // @ts-ignore window.addLine = (a: Vector3, b: Vector3) => { - const material = new THREE.LineBasicMaterial({ + const material = new LineBasicMaterial({ color: 0x0000ff, }); const points = []; - points.push(new THREE.Vector3(...a)); - points.push(new THREE.Vector3(...b)); - const geometry = new THREE.BufferGeometry().setFromPoints(points); - const line = new THREE.Line(geometry, material); + points.push(new ThreeVector3(...a)); + points.push(new ThreeVector3(...b)); + const geometry = new BufferGeometry().setFromPoints(points); + const line = new Line(geometry, material); this.rootNode.add(line); renderedLines.push(line); }; @@ -215,14 +231,14 @@ class SceneController { }; // @ts-ignore - window.removeBucketMesh = (mesh: THREE.LineSegments) => this.rootNode.remove(mesh); + window.removeBucketMesh = (mesh: LineSegments) => this.rootNode.remove(mesh); } createMeshes(): void { this.userBoundingBoxes = []; - this.userBoundingBoxGroup = new THREE.Group(); - this.layerBoundingBoxGroup = new THREE.Group(); - this.annotationToolsGeometryGroup = new THREE.Group(); + this.userBoundingBoxGroup = new Group(); + this.layerBoundingBoxGroup = new Group(); + this.annotationToolsGeometryGroup = new Group(); const state = Store.getState(); // Cubes const { min, max } = getDatasetBoundingBox(state.dataset); @@ -248,19 +264,19 @@ class SceneController { this.planes[OrthoViews.PLANE_YZ].setBaseRotation(OrthoBaseRotations[OrthoViews.PLANE_YZ]); this.planes[OrthoViews.PLANE_XZ].setBaseRotation(OrthoBaseRotations[OrthoViews.PLANE_XZ]); - const planeGroup = new THREE.Group(); + const planeGroup = new Group(); for (const plane of _.values(this.planes)) { planeGroup.add(...plane.getMeshes()); } // Apply the inverse dataset scale factor to all planes to remove the scaling of the root group // to avoid shearing effects on rotated ortho viewport planes. For more info see plane.ts. planeGroup.scale.copy( - new THREE.Vector3(1, 1, 1).divide( - new THREE.Vector3(...Store.getState().dataset.dataSource.scale.factor), + new ThreeVector3(1, 1, 1).divide( + new ThreeVector3(...Store.getState().dataset.dataSource.scale.factor), ), ); - this.rootNode = new THREE.Group().add( + this.rootNode = new Group().add( this.userBoundingBoxGroup, this.layerBoundingBoxGroup, this.annotationToolsGeometryGroup.add( @@ -285,8 +301,8 @@ class SceneController { return () => {}; } - let splitBoundaryMesh: THREE.Mesh | null = null; - let splines: THREE.Object3D[] = []; + let splitBoundaryMesh: Mesh | null = null; + let splines: Object3D[] = []; try { const objects = computeSplitBoundaryMeshWithSplines(points); splitBoundaryMesh = objects.splitBoundaryMesh; @@ -297,7 +313,7 @@ class SceneController { return () => {}; } - const surfaceGroup = new THREE.Group(); + const surfaceGroup = new Group(); if (splitBoundaryMesh != null) { surfaceGroup.add(splitBoundaryMesh); } @@ -514,12 +530,12 @@ class SceneController { app.vent.emit("rerender"); } - getRootNode(): THREE.Object3D { + getRootNode(): Object3D { return this.rootNode; } setUserBoundingBoxes(bboxes: Array): void { - const newUserBoundingBoxGroup = new THREE.Group(); + const newUserBoundingBoxGroup = new Group(); this.userBoundingBoxes = bboxes.map(({ boundingBox, isVisible, color, id }) => { const { min, max } = boundingBox; const bbColor: Vector3 = [color[0] * 255, color[1] * 255, color[2] * 255]; @@ -540,9 +556,9 @@ class SceneController { this.rootNode.add(this.userBoundingBoxGroup); } - private applyTransformToGroup(transform: Transform, group: THREE.Group | CustomLOD) { + private applyTransformToGroup(transform: Transform, group: Group | CustomLOD) { if (transform.affineMatrix) { - const matrix = new THREE.Matrix4(); + const matrix = new Matrix4(); // @ts-ignore matrix.set(...transform.affineMatrix); // We need to disable matrixAutoUpdate as otherwise the update to the matrix will be lost. @@ -600,7 +616,7 @@ class SceneController { const dataset = state.dataset; const layers = getDataLayers(dataset); - const newLayerBoundingBoxGroup = new THREE.Group(); + const newLayerBoundingBoxGroup = new Group(); this.layerBoundingBoxes = Object.fromEntries( layers.map((layer) => { const boundingBox = getLayerBoundingBox(dataset, layer.name); @@ -619,7 +635,7 @@ class SceneController { state.datasetConfiguration.nativelyRenderedLayerName, )?.affineMatrix; if (transformMatrix) { - const matrix = new THREE.Matrix4(); + const matrix = new Matrix4(); // @ts-ignore matrix.set(...transformMatrix); mesh.applyMatrix4(matrix); @@ -723,7 +739,7 @@ class SceneController { plane.destroy(); } - this.rootNode = new THREE.Group(); + this.rootNode = new Group(); } bindToEvents(): void { diff --git a/frontend/javascripts/viewer/controller/segment_mesh_controller.ts b/frontend/javascripts/viewer/controller/segment_mesh_controller.ts index 032afb21c46..6da776212ee 100644 --- a/frontend/javascripts/viewer/controller/segment_mesh_controller.ts +++ b/frontend/javascripts/viewer/controller/segment_mesh_controller.ts @@ -1,7 +1,17 @@ import app from "app"; import { mergeVertices } from "libs/BufferGeometryUtils"; import _ from "lodash"; -import * as THREE from "three"; +import { + Mesh, + Color, + MeshLambertMaterial, + FrontSide, + BufferGeometry, + BufferAttribute, + Group, + AmbientLight, + DirectionalLight, +} from "three"; import { acceleratedRaycast } from "three-mesh-bvh"; import TWEEN from "tween.js"; import type { AdditionalCoordinate } from "types/api_types"; @@ -16,32 +26,31 @@ import { import Store from "viewer/store"; import { computeBvhAsync } from "libs/compute_bvh_async"; -import type { BufferAttribute } from "three"; import Constants from "viewer/constants"; import { NO_LOD_MESH_INDEX } from "viewer/model/sagas/meshes/common_mesh_saga"; import type { BufferGeometryWithInfo } from "./mesh_helpers"; // Add the raycast function. Assumes the BVH is available on // the `boundsTree` variable -THREE.Mesh.prototype.raycast = acceleratedRaycast; +Mesh.prototype.raycast = acceleratedRaycast; -const hslToSRGB = (hsl: Vector3) => new THREE.Color().setHSL(...hsl).convertSRGBToLinear(); +const hslToSRGB = (hsl: Vector3) => new Color().setHSL(...hsl).convertSRGBToLinear(); -const WHITE = new THREE.Color(1, 1, 1); +const WHITE = new Color(1, 1, 1); const ACTIVATED_COLOR = hslToSRGB([0.7, 0.9, 0.75]); const HOVERED_COLOR = hslToSRGB([0.65, 0.9, 0.75]); const ACTIVATED_COLOR_VEC3 = ACTIVATED_COLOR.toArray() as Vector3; const HOVERED_COLOR_VEC3 = HOVERED_COLOR.toArray() as Vector3; -type MeshMaterial = THREE.MeshLambertMaterial & { originalColor: Vector3 }; +type MeshMaterial = MeshLambertMaterial & { originalColor: Vector3 }; type HighlightState = Vector2 | "full" | null; -export type MeshSceneNode = THREE.Mesh & { +export type MeshSceneNode = Mesh & { hoveredState?: HighlightState; activeState?: HighlightState; parent: SceneGroupForMeshes; isMerged: boolean; }; -export type SceneGroupForMeshes = THREE.Group & { segmentId: number; children: MeshSceneNode[] }; +export type SceneGroupForMeshes = Group & { segmentId: number; children: MeshSceneNode[] }; const setRangeToColor = ( geometry: BufferGeometryWithInfo, @@ -56,13 +65,13 @@ const setRangeToColor = ( } }; -type GroupForLOD = THREE.Group & { +type GroupForLOD = Group & { children: SceneGroupForMeshes[]; forEach: (callback: (el: SceneGroupForMeshes) => void) => void; }; export default class SegmentMeshController { - lightsGroup: THREE.Group; + lightsGroup: Group; // meshesLayerLODRootGroup holds a CustomLOD for each segmentation layer with meshes. // Each CustomLOD group can hold multiple meshes. // meshesLayerLODRootGroup @@ -74,7 +83,7 @@ export default class SegmentMeshController { // - CustomLOD // - LOD X // - meshes - meshesLayerLODRootGroup: THREE.Group; + meshesLayerLODRootGroup: Group; meshesGroupsPerSegmentId: Record< string, // additionalCoordinatesString @@ -91,8 +100,8 @@ export default class SegmentMeshController { > = {}; constructor() { - this.lightsGroup = new THREE.Group(); - this.meshesLayerLODRootGroup = new THREE.Group(); + this.lightsGroup = new Group(); + this.meshesLayerLODRootGroup = new Group(); this.addLights(); } @@ -116,8 +125,8 @@ export default class SegmentMeshController { ): Promise { // Currently, this function is only used by ad hoc meshing. if (vertices.length === 0) return; - let bufferGeometry = new THREE.BufferGeometry(); - bufferGeometry.setAttribute("position", new THREE.BufferAttribute(vertices, 3)); + let bufferGeometry = new BufferGeometry(); + bufferGeometry.setAttribute("position", new BufferAttribute(vertices, 3)); bufferGeometry = mergeVertices(bufferGeometry); bufferGeometry.computeVertexNormals(); @@ -144,10 +153,10 @@ export default class SegmentMeshController { isMerged: boolean, ): MeshSceneNode { const color = this.getColorObjectForSegment(segmentId, layerName); - const meshMaterial = new THREE.MeshLambertMaterial({ + const meshMaterial = new MeshLambertMaterial({ vertexColors: true, }) as MeshMaterial; - meshMaterial.side = THREE.FrontSide; + meshMaterial.side = FrontSide; meshMaterial.transparent = true; const colorArray = color.convertSRGBToLinear().toArray() as Vector3; meshMaterial.originalColor = colorArray; @@ -159,12 +168,12 @@ export default class SegmentMeshController { for (let i = 0; i < geometry.attributes.position.count; i++) { colorBuffer.set(colorArray, i * 3); } - geometry.setAttribute("color", new THREE.BufferAttribute(colorBuffer, 3)); + geometry.setAttribute("color", new BufferAttribute(colorBuffer, 3)); // mesh.parent is still null at this moment, but when the mesh is // added to the group later, parent will be set. We'll ignore // this detail for now via the casting. - const mesh = new THREE.Mesh(geometry, meshMaterial) as any as MeshSceneNode; + const mesh = new Mesh(geometry, meshMaterial) as any as MeshSceneNode; mesh.isMerged = isMerged; const tweenAnimation = new TWEEN.Tween({ @@ -202,7 +211,7 @@ export default class SegmentMeshController { const targetGroup: SceneGroupForMeshes = _.get( this.meshesGroupsPerSegmentId, keys, - new THREE.Group(), + new Group(), ); _.setWith(this.meshesGroupsPerSegmentId, keys, targetGroup, Object); let layerLODGroup = this.meshesLayerLODRootGroup.getObjectByName(layerName) as @@ -235,11 +244,11 @@ export default class SegmentMeshController { scale[1] / dsScaleFactor[1], scale[2] / dsScaleFactor[2], ]; - targetGroup.scale.copy(new THREE.Vector3(...adaptedScale)); + targetGroup.scale.copy(new Vector3(...adaptedScale)); } const meshChunk = this.constructMesh(segmentId, layerName, geometry, opacity, isMerged); - const group = new THREE.Group() as SceneGroupForMeshes; + const group = new Group() as SceneGroupForMeshes; group.add(meshChunk); group.segmentId = segmentId; @@ -293,7 +302,7 @@ export default class SegmentMeshController { segmentId: number, layerName: string, additionalCoordinates?: AdditionalCoordinate[] | null, - ): THREE.Group | null { + ): Group | null { const additionalCoordKey = getAdditionalCoordinatesAsString(additionalCoordinates); const meshGroups = this.getMeshGroups(additionalCoordKey, layerName, segmentId); @@ -372,7 +381,7 @@ export default class SegmentMeshController { getColorObjectForSegment(segmentId: number, layerName: string) { const [hue, saturation, light] = getSegmentColorAsHSLA(Store.getState(), segmentId, layerName); - const color = new THREE.Color().setHSL(hue, saturation, light); + const color = new Color().setHSL(hue, saturation, light); color.convertSRGBToLinear(); return color; @@ -393,7 +402,7 @@ export default class SegmentMeshController { // Note that the PlaneView also attaches a directional light directly to the TD camera, // so that the light moves along the cam. - const ambientLight = new THREE.AmbientLight("white", settings.ambientIntensity); + const ambientLight = new AmbientLight("white", settings.ambientIntensity); this.lightsGroup.add(ambientLight); const lightPositions: Vector3[] = [ @@ -407,10 +416,10 @@ export default class SegmentMeshController { [-1, -1, -1], ]; - const directionalLights: THREE.DirectionalLight[] = []; + const directionalLights: DirectionalLight[] = []; lightPositions.forEach((pos, index) => { - const light = new THREE.DirectionalLight( + const light = new DirectionalLight( WHITE, // @ts-ignore settings[`dirLight${index + 1}Intensity`] || 1, @@ -426,7 +435,7 @@ export default class SegmentMeshController { layerName: string, segmentId: number, lod: number, - ): THREE.Group | null { + ): Group | null { const additionalCoordKey = getAdditionalCoordinatesAsString(additionalCoordinates); const keys = [additionalCoordKey, layerName, segmentId, lod]; @@ -437,7 +446,7 @@ export default class SegmentMeshController { additionalCoordKey: string, layerName: string, segmentId: number, - ): Record | null { + ): Record | null { const keys = [additionalCoordKey, layerName, segmentId]; return _.get(this.meshesGroupsPerSegmentId, keys, null); } @@ -534,7 +543,7 @@ export default class SegmentMeshController { } } - const setMaterialToUniformColor = (material: MeshMaterial, color: THREE.Color) => { + const setMaterialToUniformColor = (material: MeshMaterial, color: Color) => { material.vertexColors = false; material.color = color; material.needsUpdate = true; @@ -550,16 +559,14 @@ export default class SegmentMeshController { const isUniformColor = (mesh.activeState || mesh.hoveredState) === "full" || !mesh.isMerged; if (isUniformColor) { - let newColor = mesh.hoveredState - ? HOVERED_COLOR - : new THREE.Color(...mesh.material.originalColor); + let newColor = mesh.hoveredState ? HOVERED_COLOR : new Color(...mesh.material.originalColor); // Update the material for all meshes that belong to the current // segment ID. Only for adhoc meshes, these will contain multiple // children. For precomputed meshes, this will only affect one // mesh in the scene graph. parent.traverse((child) => { - if (child instanceof THREE.Mesh) { + if (child instanceof Mesh) { setMaterialToUniformColor(child.material, newColor); } }); diff --git a/frontend/javascripts/viewer/controller/td_controller.tsx b/frontend/javascripts/viewer/controller/td_controller.tsx index 0cc00e43e1e..38734d4084c 100644 --- a/frontend/javascripts/viewer/controller/td_controller.tsx +++ b/frontend/javascripts/viewer/controller/td_controller.tsx @@ -5,7 +5,7 @@ import * as Utils from "libs/utils"; import _ from "lodash"; import * as React from "react"; import { connect } from "react-redux"; -import * as THREE from "three"; +import { OrthographicCamera, Vector3 as ThreeVector3 } from "three"; import type { VoxelSize } from "types/api_types"; import { type OrthoView, @@ -41,7 +41,7 @@ import type { CameraData, StoreAnnotation, WebknossosState } from "viewer/store" import Store from "viewer/store"; import type PlaneView from "viewer/view/plane_view"; -export function threeCameraToCameraData(camera: THREE.OrthographicCamera): CameraData { +export function threeCameraToCameraData(camera: OrthographicCamera): CameraData { const { position, up, near, far, left, right, top, bottom } = camera; const objToArr = ({ x, y, z }: { x: number; y: number; z: number }): Vector3 => [x, y, z]; @@ -84,7 +84,7 @@ function getTDViewMouseControlsSkeleton(planeView: PlaneView): Record; + cameras: OrthoViewMap; planeView?: PlaneView; annotation?: StoreAnnotation; }; @@ -163,7 +163,7 @@ class TDController extends React.PureComponent { this.controls = new TrackballControls( tdCamera, view, - new THREE.Vector3(...pos), + new ThreeVector3(...pos), this.onTDCameraChanged, ); this.controls.noZoom = true; @@ -315,10 +315,10 @@ class TDController extends React.PureComponent { if (invertedDiff.every((el) => el === 0)) return; this.oldUnitPos = nmPosition; - const nmVector = new THREE.Vector3(...invertedDiff); + const nmVector = new ThreeVector3(...invertedDiff); // moves camera by the nm vector const camera = this.props.cameras[OrthoViews.TDView]; - const rotation = THREE.Vector3.prototype.multiplyScalar.call(camera.rotation.clone(), -1); + const rotation = ThreeVector3.prototype.multiplyScalar.call(camera.rotation.clone(), -1); // reverse euler order // @ts-expect-error ts-migrate(2339) FIXME: Property 'order' does not exist on type 'Vector3'. rotation.order = rotation.order.split("").reverse().join(""); diff --git a/frontend/javascripts/viewer/geometries/arbitrary_plane.ts b/frontend/javascripts/viewer/geometries/arbitrary_plane.ts index 305e6b39b37..d5634993b22 100644 --- a/frontend/javascripts/viewer/geometries/arbitrary_plane.ts +++ b/frontend/javascripts/viewer/geometries/arbitrary_plane.ts @@ -1,5 +1,5 @@ import _ from "lodash"; -import * as THREE from "three"; +import { Mesh, Matrix4, DoubleSide, PlaneGeometry, ShaderMaterial } from "three"; import constants, { OrthoViews } from "viewer/constants"; import getSceneController from "viewer/controller/scene_controller_provider"; import PlaneMaterialFactory from "viewer/geometries/materials/plane_material_factory"; @@ -20,8 +20,8 @@ import Store from "viewer/store"; // The result is then projected on a flat surface. const renderDebuggerPlane = false; type ArbitraryMeshes = { - mainPlane: THREE.Mesh; - debuggerPlane: THREE.Mesh | null | undefined; + mainPlane: Mesh; + debuggerPlane: Mesh | null | undefined; }; class ArbitraryPlane { @@ -44,7 +44,7 @@ class ArbitraryPlane { this.materialFactory.stopListening(); } - addToScene(scene: THREE.Scene) { + addToScene(scene: Scene) { _.values(this.meshes).forEach((mesh) => { if (mesh) { scene.add(mesh); @@ -56,12 +56,12 @@ class ArbitraryPlane { if (this.isDirty) { const matrix = getZoomedMatrix(Store.getState().flycam); - const updateMesh = (mesh: THREE.Mesh | null | undefined) => { + const updateMesh = (mesh: Mesh | null | undefined) => { if (!mesh) { return; } - const meshMatrix = new THREE.Matrix4(); + const meshMatrix = new Matrix4(); meshMatrix.set( matrix[0], matrix[4], @@ -82,7 +82,7 @@ class ArbitraryPlane { ); mesh.matrix.identity(); mesh.matrix.multiply(meshMatrix); - mesh.matrix.multiply(new THREE.Matrix4().makeRotationY(Math.PI)); + mesh.matrix.multiply(new Matrix4().makeRotationY(Math.PI)); mesh.matrixWorldNeedsUpdate = true; }; @@ -94,18 +94,18 @@ class ArbitraryPlane { } createMeshes(): ArbitraryMeshes { - const adaptPlane = (_plane: THREE.Mesh) => { + const adaptPlane = (_plane: Mesh) => { _plane.rotation.x = Math.PI; _plane.matrixAutoUpdate = false; - _plane.material.side = THREE.DoubleSide; + _plane.material.side = DoubleSide; return _plane; }; this.materialFactory = new PlaneMaterialFactory(OrthoViews.PLANE_XY, false, 4); const textureMaterial = this.materialFactory.setup().getMaterial(); const mainPlane = adaptPlane( - new THREE.Mesh( - new THREE.PlaneGeometry(constants.VIEWPORT_WIDTH, constants.VIEWPORT_WIDTH, 1, 1), + new Mesh( + new PlaneGeometry(constants.VIEWPORT_WIDTH, constants.VIEWPORT_WIDTH, 1, 1), textureMaterial, ), ); @@ -117,7 +117,7 @@ class ArbitraryPlane { } createDebuggerPlane() { - const debuggerMaterial = new THREE.ShaderMaterial({ + const debuggerMaterial = new ShaderMaterial({ uniforms: this.materialFactory.uniforms, vertexShader: ` uniform float sphericalCapRadius; @@ -158,8 +158,8 @@ class ArbitraryPlane { }); debuggerMaterial.transparent = true; shaderEditor.addMaterial(99, debuggerMaterial); - const debuggerPlane = new THREE.Mesh( - new THREE.PlaneGeometry(constants.VIEWPORT_WIDTH, constants.VIEWPORT_WIDTH, 50, 50), + const debuggerPlane = new Mesh( + new PlaneGeometry(constants.VIEWPORT_WIDTH, constants.VIEWPORT_WIDTH, 50, 50), debuggerMaterial, ); return debuggerPlane; diff --git a/frontend/javascripts/viewer/geometries/compute_split_boundary_mesh_with_splines.ts b/frontend/javascripts/viewer/geometries/compute_split_boundary_mesh_with_splines.ts index 03471484bcf..6cae39833c5 100644 --- a/frontend/javascripts/viewer/geometries/compute_split_boundary_mesh_with_splines.ts +++ b/frontend/javascripts/viewer/geometries/compute_split_boundary_mesh_with_splines.ts @@ -1,11 +1,22 @@ import { orderPointsWithMST } from "libs/order_points_with_mst"; import _ from "lodash"; -import * as THREE from "three"; +import { + Vector3 as ThreeVector3, + CatmullRomCurve3, + MathUtils, + BufferGeometry, + LineBasicMaterial, + Line, + Float32BufferAttribute, + MeshStandardMaterial, + DoubleSide, + Mesh, +} from "three"; import type { Vector3 } from "viewer/constants"; export default function computeSplitBoundaryMeshWithSplines(points: Vector3[]): { - splines: THREE.Object3D[]; - splitBoundaryMesh: THREE.Mesh; + splines: Object3D[]; + splitBoundaryMesh: Mesh; } { /** * Generates a smooth, interpolated 3D boundary mesh and corresponding spline visualizations @@ -34,7 +45,7 @@ export default function computeSplitBoundaryMeshWithSplines(points: Vector3[]): * to ensure a valid 3D surface can still be formed. * */ - const splines: THREE.Object3D[] = []; + const splines: Object3D[] = []; const unfilteredPointsByZ = _.groupBy(points, (p) => p[2]); const pointsByZ = _.omitBy(unfilteredPointsByZ, (value) => value.length < 2); @@ -56,7 +67,7 @@ export default function computeSplitBoundaryMeshWithSplines(points: Vector3[]): ]); } - const curvesByZ: Record = {}; + const curvesByZ: Record = {}; // Create curves for existing z-values const curves = _.compact( @@ -71,7 +82,7 @@ export default function computeSplitBoundaryMeshWithSplines(points: Vector3[]): adaptedZ += 0.1; } const points2D = orderPointsWithMST( - pointsByZ[zValue].map((p) => new THREE.Vector3(p[0], p[1], adaptedZ)), + pointsByZ[zValue].map((p) => new ThreeVector3(p[0], p[1], adaptedZ)), ); if (curveIdx > 0) { @@ -82,7 +93,7 @@ export default function computeSplitBoundaryMeshWithSplines(points: Vector3[]): const prevCurvePoints = curvesByZ[zValues[curveIdx - 1]].points; const distActual = currentCurvePoints[0].distanceTo(prevCurvePoints[0]); - const distFlipped = (currentCurvePoints.at(-1) as THREE.Vector3).distanceTo( + const distFlipped = (currentCurvePoints.at(-1) as ThreeVector3).distanceTo( prevCurvePoints[0], ); @@ -92,7 +103,7 @@ export default function computeSplitBoundaryMeshWithSplines(points: Vector3[]): } } - const curve = new THREE.CatmullRomCurve3(points2D); + const curve = new CatmullRomCurve3(points2D); curvesByZ[zValue] = curve; return curve; }), @@ -126,24 +137,24 @@ export default function computeSplitBoundaryMeshWithSplines(points: Vector3[]): const upperPoint = upperCurvePoints[i]; const alpha = (z - lowerZ) / (upperZ - lowerZ); // Interpolation factor - return new THREE.Vector3( - THREE.MathUtils.lerp(lowerPoint.x, upperPoint.x, alpha), - THREE.MathUtils.lerp(lowerPoint.y, upperPoint.y, alpha), + return new ThreeVector3( + MathUtils.lerp(lowerPoint.x, upperPoint.x, alpha), + MathUtils.lerp(lowerPoint.y, upperPoint.y, alpha), z, ); }); // Create the interpolated curve - const interpolatedCurve = new THREE.CatmullRomCurve3(interpolatedPoints); + const interpolatedCurve = new CatmullRomCurve3(interpolatedPoints); curvesByZ[z] = interpolatedCurve; } // Generate and display all curves Object.values(curvesByZ).forEach((curve) => { const curvePoints = curve.getPoints(numDivisions); - const geometry = new THREE.BufferGeometry().setFromPoints(curvePoints); - const material = new THREE.LineBasicMaterial({ color: 0xff0000 }); - const splineObject = new THREE.Line(geometry, material); + const geometry = new BufferGeometry().setFromPoints(curvePoints); + const material = new LineBasicMaterial({ color: 0xff0000 }); + const splineObject = new Line(geometry, material); splines.push(splineObject); }); @@ -172,25 +183,25 @@ export default function computeSplitBoundaryMeshWithSplines(points: Vector3[]): } } - // Convert to Three.js BufferGeometry - const geometry = new THREE.BufferGeometry(); - geometry.setAttribute("position", new THREE.Float32BufferAttribute(vertices, 3)); + // Convert to js BufferGeometry + const geometry = new BufferGeometry(); + geometry.setAttribute("position", new Float32BufferAttribute(vertices, 3)); geometry.setIndex(indices); geometry.computeVertexNormals(); // Smooth shading geometry.computeBoundsTree(); // Material and Mesh - const material = new THREE.MeshStandardMaterial({ + const material = new MeshStandardMaterial({ color: 0x0077ff, // A soft blue color metalness: 0.5, // Slight metallic effect roughness: 1, // Some surface roughness for a natural look - side: THREE.DoubleSide, // Render both sides + side: DoubleSide, // Render both sides flatShading: false, // Ensures smooth shading with computed normals opacity: 0.8, transparent: true, wireframe: false, }); - const splitBoundaryMesh = new THREE.Mesh(geometry, material); + const splitBoundaryMesh = new Mesh(geometry, material); return { splines, splitBoundaryMesh, diff --git a/frontend/javascripts/viewer/geometries/crosshair.ts b/frontend/javascripts/viewer/geometries/crosshair.ts index 53be190ce51..7e7408886c5 100644 --- a/frontend/javascripts/viewer/geometries/crosshair.ts +++ b/frontend/javascripts/viewer/geometries/crosshair.ts @@ -1,9 +1,9 @@ -import * as THREE from "three"; +import { Group, Matrix4, Vector3, RingGeometry, MeshBasicMaterial, DoubleSide, Mesh } from "three"; import { getZoomedMatrix } from "viewer/model/accessors/flycam_accessor"; import Store from "viewer/store"; class Crosshair { - mesh: THREE.Group; + mesh: Group; WIDTH: number; COLOR: string; SCALE_MIN: number; @@ -47,9 +47,9 @@ class Crosshair { m[11], m[15], ); - mesh.matrix.multiply(new THREE.Matrix4().makeRotationY(Math.PI)); - mesh.matrix.multiply(new THREE.Matrix4().makeTranslation(0, 0, 0.5)); - mesh.matrix.scale(new THREE.Vector3(this.scale, this.scale, this.scale)); + mesh.matrix.multiply(new Matrix4().makeRotationY(Math.PI)); + mesh.matrix.multiply(new Matrix4().makeTranslation(0, 0, 0.5)); + mesh.matrix.scale(new Vector3(this.scale, this.scale, this.scale)); mesh.matrixWorldNeedsUpdate = true; this.isDirty = false; } @@ -63,20 +63,20 @@ class Crosshair { } } - addToScene(scene: THREE.Scene) { + addToScene(scene: Scene) { scene.add(this.mesh); } - createMesh(): THREE.Group { + createMesh(): Group { const createCircle = (radius: number) => { - const geometry = new THREE.RingGeometry(radius, radius + 4, 64); - const material = new THREE.MeshBasicMaterial({ color: this.COLOR, side: THREE.DoubleSide }); - return new THREE.Mesh(geometry, material); + const geometry = new RingGeometry(radius, radius + 4, 64); + const material = new MeshBasicMaterial({ color: this.COLOR, side: DoubleSide }); + return new Mesh(geometry, material); }; const outerCircle = createCircle(this.WIDTH / 2); const innerCircle = createCircle(4); - const mesh = new THREE.Group(); + const mesh = new Group(); mesh.add(outerCircle); mesh.add(innerCircle); diff --git a/frontend/javascripts/viewer/geometries/cube.ts b/frontend/javascripts/viewer/geometries/cube.ts index 32e23b52dd0..b0f92fe3360 100644 --- a/frontend/javascripts/viewer/geometries/cube.ts +++ b/frontend/javascripts/viewer/geometries/cube.ts @@ -1,6 +1,6 @@ import app from "app"; import _ from "lodash"; -import * as THREE from "three"; +import { Line, BufferGeometry, LineBasicMaterial, Vector3 as ThreeVector3 } from "three"; import type { OrthoView, OrthoViewWithoutTDMap, Vector3 } from "viewer/constants"; import { OrthoViewValuesWithoutTDView, OrthoViews } from "viewer/constants"; import { getPosition } from "viewer/model/accessors/flycam_accessor"; @@ -23,8 +23,8 @@ class Cube { // current W position. Without the cross sections, the bounding box' wireframe // would only be visible when the current position matches the edge positions // of the bounding box. - crossSections: OrthoViewWithoutTDMap; - cube: THREE.Line; + crossSections: OrthoViewWithoutTDMap; + cube: Line; min: Vector3; max: Vector3; readonly showCrossSections: boolean; @@ -47,11 +47,11 @@ class Cube { this.initialized = false; this.visible = true; this.isHighlighted = properties.isHighlighted; - this.cube = new THREE.Line(new THREE.BufferGeometry(), this.getLineMaterial()); + this.cube = new Line(new BufferGeometry(), this.getLineMaterial()); this.crossSections = { - PLANE_XY: new THREE.Line(new THREE.BufferGeometry(), this.getLineMaterial()), - PLANE_XZ: new THREE.Line(new THREE.BufferGeometry(), this.getLineMaterial()), - PLANE_YZ: new THREE.Line(new THREE.BufferGeometry(), this.getLineMaterial()), + PLANE_XY: new Line(new BufferGeometry(), this.getLineMaterial()), + PLANE_XZ: new Line(new BufferGeometry(), this.getLineMaterial()), + PLANE_YZ: new Line(new BufferGeometry(), this.getLineMaterial()), }; if (this.min != null && this.max != null) { @@ -70,11 +70,11 @@ class Cube { getLineMaterial() { return this.isHighlighted - ? new THREE.LineBasicMaterial({ + ? new LineBasicMaterial({ color: Store.getState().uiInformation.theme === "light" ? 0xeeeeee : 0xffffff, linewidth: this.lineWidth, }) - : new THREE.LineBasicMaterial({ + : new LineBasicMaterial({ color: this.color, linewidth: this.lineWidth, }); @@ -89,7 +89,7 @@ class Cube { // box, we subtract Number.EPSILON. max = [max[0] - Number.EPSILON, max[1] - Number.EPSILON, max[2] - Number.EPSILON]; - const vec = (x: number, y: number, z: number) => new THREE.Vector3(x, y, z); + const vec = (x: number, y: number, z: number) => new ThreeVector3(x, y, z); this.cube.geometry.setFromPoints([ vec(min[0], min[1], min[2]), @@ -168,7 +168,7 @@ class Cube { } } - getMeshes(): Array { + getMeshes(): Line[] { return [this.cube].concat(_.values(this.crossSections)); } diff --git a/frontend/javascripts/viewer/geometries/helper_geometries.ts b/frontend/javascripts/viewer/geometries/helper_geometries.ts index a9df66d4b26..0bd8582cab8 100644 --- a/frontend/javascripts/viewer/geometries/helper_geometries.ts +++ b/frontend/javascripts/viewer/geometries/helper_geometries.ts @@ -1,19 +1,37 @@ import app from "app"; import { V3 } from "libs/mjs"; import ResizableBuffer from "libs/resizable_buffer"; -import * as THREE from "three"; +import { + Color, + Line, + BufferGeometry, + LineBasicMaterial, + BufferAttribute, + DynamicDrawUsage, + Vector2, + Euler, + PlaneGeometry, + MeshBasicMaterial, + DoubleSide, + Mesh, + DataTexture, + RGBAFormat, + RepeatWrapping, + Vector3 as ThreeVector3, + Group, +} from "three"; import { type OrthoView, OrthoViews, type Vector3 } from "viewer/constants"; import Dimensions from "viewer/model/dimensions"; import { getBaseVoxelInUnit } from "viewer/model/scaleinfo"; import Store from "viewer/store"; -export const CONTOUR_COLOR_NORMAL = new THREE.Color(0x0000ff); -export const CONTOUR_COLOR_DELETE = new THREE.Color(0xff0000); +export const CONTOUR_COLOR_NORMAL = new Color(0x0000ff); +export const CONTOUR_COLOR_DELETE = new Color(0xff0000); export class ContourGeometry { - color: THREE.Color; - line: THREE.Line; - connectingLine: THREE.Line; + color: Color; + line: Line; + connectingLine: Line; vertexBuffer: ResizableBuffer; connectingLinePositions: Float32Array; viewport: OrthoView; @@ -24,28 +42,25 @@ export class ContourGeometry { this.viewport = OrthoViews.PLANE_XY; this.showConnectingLine = showConnectingLine; - const edgeGeometry = new THREE.BufferGeometry(); - const positionAttribute = new THREE.BufferAttribute(new Float32Array(3), 3); - positionAttribute.setUsage(THREE.DynamicDrawUsage); + const edgeGeometry = new BufferGeometry(); + const positionAttribute = new BufferAttribute(new Float32Array(3), 3); + positionAttribute.setUsage(DynamicDrawUsage); edgeGeometry.setAttribute("position", positionAttribute); - this.line = new THREE.Line( + this.line = new Line( edgeGeometry, - new THREE.LineBasicMaterial({ + new LineBasicMaterial({ linewidth: 2, }), ); - const connectingLineGeometry = new THREE.BufferGeometry(); + const connectingLineGeometry = new BufferGeometry(); this.connectingLinePositions = new Float32Array(6); - const connectingLinePositionAttribute = new THREE.BufferAttribute( - this.connectingLinePositions, - 3, - ); - connectingLinePositionAttribute.setUsage(THREE.DynamicDrawUsage); + const connectingLinePositionAttribute = new BufferAttribute(this.connectingLinePositions, 3); + connectingLinePositionAttribute.setUsage(DynamicDrawUsage); connectingLineGeometry.setAttribute("position", connectingLinePositionAttribute); - positionAttribute.setUsage(THREE.DynamicDrawUsage); - this.connectingLine = new THREE.Line( + positionAttribute.setUsage(DynamicDrawUsage); + this.connectingLine = new Line( connectingLineGeometry, - new THREE.LineBasicMaterial({ + new LineBasicMaterial({ linewidth: 2, }), ); @@ -57,7 +72,7 @@ export class ContourGeometry { reset() { this.viewport = OrthoViews.PLANE_XY; this.line.material.color = this.color; - this.connectingLine.material.color = new THREE.Color(0x00ffff); + this.connectingLine.material.color = new Color(0x00ffff); this.vertexBuffer.clear(); this.connectingLinePositions.fill(0); this.finalizeMesh(); @@ -104,8 +119,8 @@ export class ContourGeometry { const mesh = this.line; if (mesh.geometry.attributes.position.array !== this.vertexBuffer.getBuffer()) { // Need to rebuild Geometry - const positionAttribute = new THREE.BufferAttribute(this.vertexBuffer.getBuffer(), 3); - positionAttribute.setUsage(THREE.DynamicDrawUsage); + const positionAttribute = new BufferAttribute(this.vertexBuffer.getBuffer(), 3); + positionAttribute.setUsage(DynamicDrawUsage); mesh.geometry.dispose(); mesh.geometry.setAttribute("position", positionAttribute); } @@ -125,16 +140,13 @@ export class ContourGeometry { const points = this.vertexBuffer.getBuffer(); let previousPointIndex = pointCount - 1; const dimIndices = Dimensions.getIndices(this.viewport); - const scaleVector = new THREE.Vector2( - voxelSizeFactor[dimIndices[0]], - voxelSizeFactor[dimIndices[1]], - ); + const scaleVector = new Vector2(voxelSizeFactor[dimIndices[0]], voxelSizeFactor[dimIndices[1]]); for (let i = 0; i < pointCount; i++) { - const start = new THREE.Vector2( + const start = new Vector2( points[previousPointIndex * 3 + dimIndices[0]], points[previousPointIndex * 3 + dimIndices[1]], ).multiply(scaleVector); - const end = new THREE.Vector2( + const end = new Vector2( points[i * 3 + dimIndices[0]], points[i * 3 + dimIndices[1]], ).multiply(scaleVector); @@ -160,42 +172,42 @@ export class ContourGeometry { } const rotations = { - [OrthoViews.PLANE_XY]: new THREE.Euler(0, 0, 0), - [OrthoViews.PLANE_YZ]: new THREE.Euler(Math.PI, -(1 / 2) * Math.PI, Math.PI), - [OrthoViews.PLANE_XZ]: new THREE.Euler((1 / 2) * Math.PI, 0, 0), + [OrthoViews.PLANE_XY]: new Euler(0, 0, 0), + [OrthoViews.PLANE_YZ]: new Euler(Math.PI, -(1 / 2) * Math.PI, Math.PI), + [OrthoViews.PLANE_XZ]: new Euler((1 / 2) * Math.PI, 0, 0), [OrthoViews.TDView]: null, }; export class QuickSelectGeometry { - color: THREE.Color; - meshGroup: THREE.Group; - centerMarkerColor: THREE.Color; - rectangle: THREE.Mesh; - centerMarker: THREE.Mesh; + color: Color; + meshGroup: Group; + centerMarkerColor: Color; + rectangle: Mesh; + centerMarker: Mesh; constructor() { this.color = CONTOUR_COLOR_NORMAL; - this.centerMarkerColor = new THREE.Color(0xff00ff); + this.centerMarkerColor = new Color(0xff00ff); - const geometry = new THREE.PlaneGeometry(1, 1); - const material = new THREE.MeshBasicMaterial({ - side: THREE.DoubleSide, + const geometry = new PlaneGeometry(1, 1); + const material = new MeshBasicMaterial({ + side: DoubleSide, transparent: true, opacity: 0.5, }); - this.rectangle = new THREE.Mesh(geometry, material); + this.rectangle = new Mesh(geometry, material); const baseWidth = getBaseVoxelInUnit(Store.getState().dataset.dataSource.scale.factor); - const centerGeometry = new THREE.PlaneGeometry(baseWidth, baseWidth); - const centerMaterial = new THREE.MeshBasicMaterial({ + const centerGeometry = new PlaneGeometry(baseWidth, baseWidth); + const centerMaterial = new MeshBasicMaterial({ color: this.centerMarkerColor, - side: THREE.DoubleSide, + side: DoubleSide, transparent: true, opacity: 0.9, }); - this.centerMarker = new THREE.Mesh(centerGeometry, centerMaterial); + this.centerMarker = new Mesh(centerGeometry, centerMaterial); - this.meshGroup = new THREE.Group(); + this.meshGroup = new Group(); this.meshGroup.add(this.rectangle); this.meshGroup.add(this.centerMarker); @@ -237,13 +249,13 @@ export class QuickSelectGeometry { this.rectangle.setRotationFromEuler(rotation); this.centerMarker.setRotationFromEuler(rotation); this.centerMarker.scale.copy( - new THREE.Vector3( + new ThreeVector3( ...Dimensions.transDim(scaleFactor.map((el) => 1 / el) as Vector3, activeViewport), ), ); } - setColor(color: THREE.Color) { + setColor(color: Color) { this.color = color; // Copy this.color into this.centerMarkerColor this.centerMarkerColor.copy(this.color); @@ -309,9 +321,9 @@ export class QuickSelectGeometry { attachTextureMask(ndData: Uint8Array, width: number, height: number) { // Attach the array as a binary mask so that the rectangle preview // is only rendered where the passed array is 1. - const texture = new THREE.DataTexture(ndData, width, height, THREE.RGBAFormat); - texture.wrapS = THREE.RepeatWrapping; - texture.wrapT = THREE.RepeatWrapping; + const texture = new DataTexture(ndData, width, height, RGBAFormat); + texture.wrapS = RepeatWrapping; + texture.wrapT = RepeatWrapping; texture.needsUpdate = true; const rectangle = this.rectangle; @@ -329,8 +341,8 @@ export class QuickSelectGeometry { // This class is used to display connected line segments and is used by the LineMeasurementTool. export class LineMeasurementGeometry { - color: THREE.Color; - line: THREE.Line; + color: Color; + line: Line; vertexBuffer: ResizableBuffer; viewport: OrthoView; visible: boolean; @@ -342,13 +354,13 @@ export class LineMeasurementGeometry { this.color = CONTOUR_COLOR_NORMAL; this.visible = false; - const lineGeometry = new THREE.BufferGeometry(); - const positionAttribute = new THREE.BufferAttribute(new Float32Array(3), 3); - positionAttribute.setUsage(THREE.DynamicDrawUsage); + const lineGeometry = new BufferGeometry(); + const positionAttribute = new BufferAttribute(new Float32Array(3), 3); + positionAttribute.setUsage(DynamicDrawUsage); lineGeometry.setAttribute("position", positionAttribute); - this.line = new THREE.Line( + this.line = new Line( lineGeometry, - new THREE.LineBasicMaterial({ + new LineBasicMaterial({ linewidth: 2, }), ); @@ -407,8 +419,8 @@ export class LineMeasurementGeometry { const mesh = this.line; if (mesh.geometry.attributes.position.array !== this.vertexBuffer.getBuffer()) { // Need to rebuild Geometry - const positionAttribute = new THREE.BufferAttribute(this.vertexBuffer.getBuffer(), 3); - positionAttribute.setUsage(THREE.DynamicDrawUsage); + const positionAttribute = new BufferAttribute(this.vertexBuffer.getBuffer(), 3); + positionAttribute.setUsage(DynamicDrawUsage); mesh.geometry.dispose(); mesh.geometry.setAttribute("position", positionAttribute); } @@ -420,7 +432,7 @@ export class LineMeasurementGeometry { } getDistance(voxelSizeFactor: Vector3): number { - const scaleVector = new THREE.Vector3(...voxelSizeFactor); + const scaleVector = new ThreeVector3(...voxelSizeFactor); const points = this.vertexBuffer.getBuffer(); const pointCount = this.vertexBuffer.getLength(); if (pointCount < 2) { @@ -428,8 +440,8 @@ export class LineMeasurementGeometry { } let accDistanceInUnit = 0; for (let i = 0; i < pointCount - 1; i++) { - const start = new THREE.Vector3(...points.subarray(i * 3, (i + 1) * 3)).multiply(scaleVector); - const end = new THREE.Vector3(...points.subarray((i + 1) * 3, (i + 2) * 3)).multiply( + const start = new ThreeVector3(...points.subarray(i * 3, (i + 1) * 3)).multiply(scaleVector); + const end = new ThreeVector3(...points.subarray((i + 1) * 3, (i + 2) * 3)).multiply( scaleVector, ); accDistanceInUnit += start.distanceTo(end); diff --git a/frontend/javascripts/viewer/geometries/materials/edge_shader.ts b/frontend/javascripts/viewer/geometries/materials/edge_shader.ts index 704b537b38e..09e06a41b9a 100644 --- a/frontend/javascripts/viewer/geometries/materials/edge_shader.ts +++ b/frontend/javascripts/viewer/geometries/materials/edge_shader.ts @@ -1,7 +1,7 @@ import { M4x4 } from "libs/mjs"; import type TPS3D from "libs/thin_plate_spline"; import _ from "lodash"; -import * as THREE from "three"; +import { RawShaderMaterial, type DataTexture, GLSL3 } from "three"; import { COLOR_TEXTURE_WIDTH_FIXED } from "viewer/geometries/materials/node_shader"; import type { Uniforms } from "viewer/geometries/materials/plane_material_factory"; import { getTransformsForSkeletonLayer } from "viewer/model/accessors/dataset_layer_transformation_accessor"; @@ -14,25 +14,25 @@ import { import { Store } from "viewer/singletons"; class EdgeShader { - material: THREE.RawShaderMaterial; + material: RawShaderMaterial; uniforms: Uniforms = {}; scaledTps: TPS3D | null = null; oldVertexShaderCode: string | null = null; storePropertyUnsubscribers: Array<() => void> = []; - constructor(treeColorTexture: THREE.DataTexture) { + constructor(treeColorTexture: DataTexture) { this.setupUniforms(treeColorTexture); - this.material = new THREE.RawShaderMaterial({ + this.material = new RawShaderMaterial({ uniforms: this.uniforms, vertexShader: this.getVertexShader(), fragmentShader: this.getFragmentShader(), transparent: true, - glslVersion: THREE.GLSL3, + glslVersion: GLSL3, }); shaderEditor.addMaterial("edge", this.material); } - setupUniforms(treeColorTexture: THREE.DataTexture): void { + setupUniforms(treeColorTexture: DataTexture): void { this.uniforms = { activeTreeId: { value: Number.NaN, diff --git a/frontend/javascripts/viewer/geometries/materials/node_shader.ts b/frontend/javascripts/viewer/geometries/materials/node_shader.ts index cfe3d6b5ccd..8876a8cc1d9 100644 --- a/frontend/javascripts/viewer/geometries/materials/node_shader.ts +++ b/frontend/javascripts/viewer/geometries/materials/node_shader.ts @@ -1,7 +1,7 @@ import { M4x4 } from "libs/mjs"; import type TPS3D from "libs/thin_plate_spline"; import _ from "lodash"; -import * as THREE from "three"; +import { RawShaderMaterial, type DataTexture, GLSL3 } from "three"; import { ViewModeValues, ViewModeValuesIndices } from "viewer/constants"; import type { Uniforms } from "viewer/geometries/materials/plane_material_factory"; import { getTransformsForSkeletonLayer } from "viewer/model/accessors/dataset_layer_transformation_accessor"; @@ -25,25 +25,25 @@ export const COLOR_TEXTURE_WIDTH = 1024.0; export const COLOR_TEXTURE_WIDTH_FIXED = COLOR_TEXTURE_WIDTH.toFixed(1); class NodeShader { - material: THREE.RawShaderMaterial; + material: RawShaderMaterial; uniforms: Uniforms = {}; scaledTps: TPS3D | null = null; oldVertexShaderCode: string | null = null; storePropertyUnsubscribers: Array<() => void> = []; - constructor(treeColorTexture: THREE.DataTexture) { + constructor(treeColorTexture: DataTexture) { this.setupUniforms(treeColorTexture); - this.material = new THREE.RawShaderMaterial({ + this.material = new RawShaderMaterial({ uniforms: this.uniforms, vertexShader: this.getVertexShader(), fragmentShader: this.getFragmentShader(), transparent: true, - glslVersion: THREE.GLSL3, + glslVersion: GLSL3, }); shaderEditor.addMaterial("node", this.material); } - setupUniforms(treeColorTexture: THREE.DataTexture): void { + setupUniforms(treeColorTexture: DataTexture): void { const state = Store.getState(); const { additionalCoordinates } = state.flycam; this.uniforms = { @@ -156,7 +156,7 @@ class NodeShader { ); } - getMaterial(): THREE.RawShaderMaterial { + getMaterial(): RawShaderMaterial { return this.material; } diff --git a/frontend/javascripts/viewer/geometries/materials/plane_material_factory.ts b/frontend/javascripts/viewer/geometries/materials/plane_material_factory.ts index 83d06b0dea5..c6153aacfb5 100644 --- a/frontend/javascripts/viewer/geometries/materials/plane_material_factory.ts +++ b/frontend/javascripts/viewer/geometries/materials/plane_material_factory.ts @@ -4,7 +4,7 @@ import { V3 } from "libs/mjs"; import type TPS3D from "libs/thin_plate_spline"; import * as Utils from "libs/utils"; import _ from "lodash"; -import * as THREE from "three"; +import { Vector3 as ThreeVector3, ShaderMaterial, DoubleSide, Matrix4, Euler } from "three"; import type { ValueOf } from "types/globals"; import { WkDevFlags } from "viewer/api/wk_dev"; import { BLEND_MODES, Identity4x4, type OrthoView, type Vector3 } from "viewer/constants"; @@ -71,7 +71,7 @@ export type Uniforms = Record< } >; -const DEFAULT_COLOR = new THREE.Vector3(255, 255, 255); +const DEFAULT_COLOR = new ThreeVector3(255, 255, 255); function sanitizeName(name: string | null | undefined): string { if (WkDevFlags.bucketDebugging.disableLayerNameSanitization) { @@ -116,7 +116,7 @@ function getTextureLayerInfos(): Params["textureLayerInfos"] { class PlaneMaterialFactory { planeID: OrthoView; isOrthogonal: boolean; - material: THREE.ShaderMaterial | undefined | null; + material: ShaderMaterial | undefined | null; uniforms: Uniforms = {}; attributes: Record = {}; shaderId: number; @@ -166,7 +166,7 @@ class PlaneMaterialFactory { // configured by the clippingDistance setting. It is necessary to calculate the position of the data that should be rendered by subtracting // the offset in the shader. Note, that the position offset should already be in world scale. positionOffset: { - value: new THREE.Vector3(0, 0, 0), + value: new ThreeVector3(0, 0, 0), }, zoomValue: { value: 1, @@ -187,10 +187,10 @@ class PlaneMaterialFactory { value: false, }, globalMousePosition: { - value: new THREE.Vector3(0, 0, 0), + value: new ThreeVector3(0, 0, 0), }, activeSegmentPosition: { - value: new THREE.Vector3(-1, -1, -1), + value: new ThreeVector3(-1, -1, -1), }, brushSizeInPixel: { value: 0, @@ -217,10 +217,10 @@ class PlaneMaterialFactory { value: OrthoViewValues.indexOf(this.planeID), }, bboxMin: { - value: new THREE.Vector3(0, 0, 0), + value: new ThreeVector3(0, 0, 0), }, bboxMax: { - value: new THREE.Vector3(0, 0, 0), + value: new ThreeVector3(0, 0, 0), }, renderBucketIndices: { value: false, @@ -253,7 +253,7 @@ class PlaneMaterialFactory { }, blendMode: { value: 1.0 }, isFlycamRotated: { value: false }, - inverseFlycamRotationMatrix: { value: new THREE.Matrix4() }, + inverseFlycamRotationMatrix: { value: new Matrix4() }, }; const activeMagIndices = getActiveMagIndicesForLayers(Store.getState()); @@ -442,7 +442,7 @@ class PlaneMaterialFactory { for (const [name, value] of Object.entries(additionalUniforms)) { this.uniforms[name] = value; } - this.material = new THREE.ShaderMaterial( + this.material = new ShaderMaterial( _.extend(options, { uniforms: this.uniforms, vertexShader: this.getVertexShader(), @@ -466,7 +466,7 @@ class PlaneMaterialFactory { this.uniforms.useBilinearFiltering.value = isEnabled; }; - this.material.side = THREE.DoubleSide; + this.material.side = DoubleSide; } startListeningForUniforms() { @@ -602,10 +602,10 @@ class PlaneMaterialFactory { const state = Store.getState(); const position = getPosition(state.flycam); - const toOrigin = new THREE.Matrix4().makeTranslation(...Utils.map3((p) => -p, position)); - const backToFlycamCenter = new THREE.Matrix4().makeTranslation(...position); - const invertRotation = new THREE.Matrix4() - .makeRotationFromEuler(new THREE.Euler(rotation[0], rotation[1], rotation[2], "ZYX")) + const toOrigin = new Matrix4().makeTranslation(...Utils.map3((p) => -p, position)); + const backToFlycamCenter = new Matrix4().makeTranslation(...position); + const invertRotation = new Matrix4() + .makeRotationFromEuler(new Euler(rotation[0], rotation[1], rotation[2], "ZYX")) .invert(); const inverseFlycamRotationMatrix = toOrigin .multiply(invertRotation) @@ -965,7 +965,7 @@ class PlaneMaterialFactory { if (settings.color != null) { const color = this.convertColor(settings.color); - this.uniforms[`${name}_color`].value = new THREE.Vector3(...color); + this.uniforms[`${name}_color`].value = new ThreeVector3(...color); } } @@ -973,7 +973,7 @@ class PlaneMaterialFactory { this.uniforms[`${name}_gammaCorrectionValue`].value = gammaCorrectionValue; } - getMaterial(): THREE.ShaderMaterial { + getMaterial(): ShaderMaterial { if (this.material == null) { throw new Error("Tried to access material, but it is null."); } diff --git a/frontend/javascripts/viewer/geometries/materials/plane_material_factory_helpers.ts b/frontend/javascripts/viewer/geometries/materials/plane_material_factory_helpers.ts index 9088b649fca..a05721c4405 100644 --- a/frontend/javascripts/viewer/geometries/materials/plane_material_factory_helpers.ts +++ b/frontend/javascripts/viewer/geometries/materials/plane_material_factory_helpers.ts @@ -1,25 +1,33 @@ import UpdatableTexture from "libs/UpdatableTexture"; -import * as THREE from "three"; +import { + type TextureDataType, + type WebGLRenderer, + type PixelFormat, + type PixelFormatGPU, + UVMapping, + ClampToEdgeWrapping, + NearestFilter, +} from "three"; // This function has to be in its own file as non-resolvable cycles are created otherwise export function createUpdatableTexture( width: number, height: number, - type: THREE.TextureDataType, - renderer: THREE.WebGLRenderer, - format: THREE.PixelFormat, - internalFormat?: THREE.PixelFormatGPU, + type: TextureDataType, + renderer: WebGLRenderer, + format: PixelFormat, + internalFormat?: PixelFormatGPU, ): UpdatableTexture { const newTexture = new UpdatableTexture( width, height, format, type, - THREE.UVMapping, - THREE.ClampToEdgeWrapping, - THREE.ClampToEdgeWrapping, - THREE.NearestFilter, - THREE.NearestFilter, + UVMapping, + ClampToEdgeWrapping, + ClampToEdgeWrapping, + NearestFilter, + NearestFilter, ); newTexture.setRenderer(renderer); diff --git a/frontend/javascripts/viewer/geometries/plane.ts b/frontend/javascripts/viewer/geometries/plane.ts index 1fc8b89f090..e6941044d54 100644 --- a/frontend/javascripts/viewer/geometries/plane.ts +++ b/frontend/javascripts/viewer/geometries/plane.ts @@ -1,6 +1,17 @@ import { V3 } from "libs/mjs"; import _ from "lodash"; -import * as THREE from "three"; +import { + Vector3 as ThreeVector3, + Euler, + PlaneGeometry, + Mesh, + LineSegments, + BufferGeometry, + BufferAttribute, + LineBasicMaterial, + Line, + Matrix4, +} from "three"; import type { OrthoView, Vector3 } from "viewer/constants"; import constants, { OrthoViewColors, @@ -32,24 +43,24 @@ class Plane { // This class is supposed to collect all the Geometries that belong to one single plane such as // the plane itself, its texture, borders and crosshairs. // @ts-expect-error ts-migrate(2564) FIXME: Property 'plane' has no initializer and is not def... Remove this comment to see the full error message - plane: THREE.Mesh; + plane: Mesh; planeID: OrthoView; materialFactory!: PlaneMaterialFactory; displayCrosshair: boolean; // @ts-expect-error ts-migrate(2564) FIXME: Property 'crosshair' has no initializer and is not... Remove this comment to see the full error message - crosshair: Array; + crosshair: Array; // @ts-expect-error ts-migrate(2564) FIXME: Property 'TDViewBorders' has no initializer and is... Remove this comment to see the full error message - TDViewBorders: THREE.Line; + TDViewBorders: Line; lastScaleFactors: [number, number]; // baseRotation is the base rotation the plane has in an unrotated scene. It will be applied additional to the flycams rotation. // Different baseRotations for each of the planes ensures that the planes stay orthogonal to each other. - baseRotation: THREE.Euler; + baseRotation: Euler; storePropertyUnsubscribers: Array<() => void> = []; datasetScaleFactor: Vector3 = [1, 1, 1]; // Properties are only created here to avoid new creating objects for each setRotation call. - baseRotationMatrix = new THREE.Matrix4(); - flycamRotationMatrix = new THREE.Matrix4(); + baseRotationMatrix = new Matrix4(); + flycamRotationMatrix = new Matrix4(); constructor(planeID: OrthoView) { this.planeID = planeID; @@ -59,7 +70,7 @@ class Plane { // dimension with the highest mag. In all other dimensions, the plane // is smaller in voxels, so that it is squared in nm. // --> scaleInfo.baseVoxel - this.baseRotation = new THREE.Euler(0, 0, 0); + this.baseRotation = new Euler(0, 0, 0); this.bindToEvents(); this.createMeshes(); } @@ -67,7 +78,7 @@ class Plane { createMeshes(): void { const pWidth = constants.VIEWPORT_WIDTH; // create plane - const planeGeo = new THREE.PlaneGeometry(pWidth, pWidth, PLANE_SUBDIVISION, PLANE_SUBDIVISION); + const planeGeo = new PlaneGeometry(pWidth, pWidth, PLANE_SUBDIVISION, PLANE_SUBDIVISION); this.materialFactory = new PlaneMaterialFactory( this.planeID, @@ -75,12 +86,12 @@ class Plane { OrthoViewValues.indexOf(this.planeID), ); const textureMaterial = this.materialFactory.setup().getMaterial(); - this.plane = new THREE.Mesh(planeGeo, textureMaterial); + this.plane = new Mesh(planeGeo, textureMaterial); // Create crosshairs this.crosshair = new Array(2); for (let i = 0; i <= 1; i++) { - const crosshairGeometry = new THREE.BufferGeometry(); + const crosshairGeometry = new BufferGeometry(); // biome-ignore format: don't format array const crosshairVertices = new Float32Array([ (-pWidth / 2) * i, (-pWidth / 2) * (1 - i), 0, @@ -88,9 +99,9 @@ class Plane { 25 * i, 25 * (1 - i), 0, (pWidth / 2) * i, (pWidth / 2) * (1 - i), 0, ]); - crosshairGeometry.setAttribute("position", new THREE.BufferAttribute(crosshairVertices, 3)); + crosshairGeometry.setAttribute("position", new BufferAttribute(crosshairVertices, 3)); - this.crosshair[i] = new THREE.LineSegments( + this.crosshair[i] = new LineSegments( crosshairGeometry, this.getLineBasicMaterial(OrthoViewCrosshairColors[this.planeID][i], 1), ); @@ -102,15 +113,15 @@ class Plane { // Create borders const vertices = [ - new THREE.Vector3(-pWidth / 2, -pWidth / 2, 0), - new THREE.Vector3(-pWidth / 2, pWidth / 2, 0), - new THREE.Vector3(pWidth / 2, pWidth / 2, 0), - new THREE.Vector3(pWidth / 2, -pWidth / 2, 0), - new THREE.Vector3(-pWidth / 2, -pWidth / 2, 0), + new ThreeVector3(-pWidth / 2, -pWidth / 2, 0), + new ThreeVector3(-pWidth / 2, pWidth / 2, 0), + new ThreeVector3(pWidth / 2, pWidth / 2, 0), + new ThreeVector3(pWidth / 2, -pWidth / 2, 0), + new ThreeVector3(-pWidth / 2, -pWidth / 2, 0), ]; - const tdBorderGeometry = new THREE.BufferGeometry().setFromPoints(vertices); + const tdBorderGeometry = new BufferGeometry().setFromPoints(vertices); - this.TDViewBorders = new THREE.Line( + this.TDViewBorders = new Line( tdBorderGeometry, this.getLineBasicMaterial(OrthoViewColors[this.planeID], 1), ); @@ -122,7 +133,7 @@ class Plane { getLineBasicMaterial = _.memoize( (color: number, linewidth: number) => - new THREE.LineBasicMaterial({ + new LineBasicMaterial({ color, linewidth, }), @@ -155,12 +166,12 @@ class Plane { this.getMeshes().map((mesh) => mesh.scale.set(...scaleVector)); } - setBaseRotation = (rotVec: THREE.Euler): void => { + setBaseRotation = (rotVec: Euler): void => { this.baseRotation.copy(rotVec); this.baseRotationMatrix.makeRotationFromEuler(this.baseRotation); }; - updateToFlycamRotation = (flycamRotationVec: THREE.Euler): void => { + updateToFlycamRotation = (flycamRotationVec: Euler): void => { // rotVec must be in "ZYX" order as this is how the flycam operates (see flycam_reducer setRotationReducer) this.flycamRotationMatrix.makeRotationFromEuler(flycamRotationVec); const combinedMatrix = this.flycamRotationMatrix.multiply(this.baseRotationMatrix); diff --git a/frontend/javascripts/viewer/geometries/skeleton.ts b/frontend/javascripts/viewer/geometries/skeleton.ts index d98497be1c1..e16739a673c 100644 --- a/frontend/javascripts/viewer/geometries/skeleton.ts +++ b/frontend/javascripts/viewer/geometries/skeleton.ts @@ -1,6 +1,17 @@ import * as Utils from "libs/utils"; import _ from "lodash"; -import * as THREE from "three"; +import { + BufferGeometry, + BufferAttribute, + Object3D, + Points, + LineSegments, + Group, + DataTexture, + RGBAFormat, + FloatType, + RawShaderMaterial, +} from "three"; import type { AdditionalCoordinate } from "types/api_types"; import type { Vector3, Vector4 } from "viewer/constants"; import EdgeShader from "viewer/geometries/materials/edge_shader"; @@ -18,8 +29,8 @@ import Store from "viewer/throttled_store"; const MAX_CAPACITY = 1000; -type BufferGeometryWithBufferAttributes = THREE.BufferGeometry & { - attributes: Record; +type BufferGeometryWithBufferAttributes = BufferGeometry & { + attributes: Record; }; type BufferHelper = typeof NodeBufferHelperType | typeof EdgeBufferHelperType; @@ -27,66 +38,63 @@ type Buffer = { capacity: number; nextIndex: number; geometry: BufferGeometryWithBufferAttributes; - mesh: THREE.Object3D; + mesh: Object3D; }; type BufferPosition = { buffer: Buffer; index: number; }; type BufferCollection = { - buffers: Array; + buffers: Buffer[]; idToBufferPosition: Map; - freeList: Array; + freeList: BufferPosition[]; helper: BufferHelper; - material: THREE.RawShaderMaterial; + material: RawShaderMaterial; }; -type BufferOperation = (position: BufferPosition) => Array; +type BufferOperation = (position: BufferPosition) => BufferAttribute[]; const NodeBufferHelperType = { - setAttributes(geometry: THREE.BufferGeometry, capacity: number): void { - geometry.setAttribute("position", new THREE.BufferAttribute(new Float32Array(capacity * 3), 3)); + setAttributes(geometry: BufferGeometry, capacity: number): void { + geometry.setAttribute("position", new BufferAttribute(new Float32Array(capacity * 3), 3)); const additionalCoordLength = (Store.getState().flycam.additionalCoordinates ?? []).length; for (const idx of _.range(0, additionalCoordLength)) { geometry.setAttribute( `additionalCoord_${idx}`, - new THREE.BufferAttribute(new Float32Array(capacity), 1), + new BufferAttribute(new Float32Array(capacity), 1), ); } - geometry.setAttribute("radius", new THREE.BufferAttribute(new Float32Array(capacity), 1)); - geometry.setAttribute("type", new THREE.BufferAttribute(new Float32Array(capacity), 1)); - geometry.setAttribute("isCommented", new THREE.BufferAttribute(new Float32Array(capacity), 1)); - geometry.setAttribute("nodeId", new THREE.BufferAttribute(new Float32Array(capacity), 1)); - geometry.setAttribute("treeId", new THREE.BufferAttribute(new Float32Array(capacity), 1)); + geometry.setAttribute("radius", new BufferAttribute(new Float32Array(capacity), 1)); + geometry.setAttribute("type", new BufferAttribute(new Float32Array(capacity), 1)); + geometry.setAttribute("isCommented", new BufferAttribute(new Float32Array(capacity), 1)); + geometry.setAttribute("nodeId", new BufferAttribute(new Float32Array(capacity), 1)); + geometry.setAttribute("treeId", new BufferAttribute(new Float32Array(capacity), 1)); }, - buildMesh(geometry: THREE.BufferGeometry, material: THREE.RawShaderMaterial): THREE.Object3D { - return new THREE.Points(geometry, material); + buildMesh(geometry: BufferGeometry, material: RawShaderMaterial): Object3D { + return new Points(geometry, material); }, supportsPicking: true, }; const EdgeBufferHelperType = { - setAttributes(geometry: THREE.BufferGeometry, capacity: number): void { - geometry.setAttribute( - "position", - new THREE.BufferAttribute(new Float32Array(capacity * 2 * 3), 3), - ); + setAttributes(geometry: BufferGeometry, capacity: number): void { + geometry.setAttribute("position", new BufferAttribute(new Float32Array(capacity * 2 * 3), 3)); const additionalCoordLength = (Store.getState().flycam.additionalCoordinates ?? []).length; for (const idx of _.range(0, additionalCoordLength)) { geometry.setAttribute( `additionalCoord_${idx}`, - new THREE.BufferAttribute(new Float32Array(capacity * 2), 1), + new BufferAttribute(new Float32Array(capacity * 2), 1), ); } - geometry.setAttribute("treeId", new THREE.BufferAttribute(new Float32Array(capacity * 2), 1)); + geometry.setAttribute("treeId", new BufferAttribute(new Float32Array(capacity * 2), 1)); }, - buildMesh(geometry: THREE.BufferGeometry, material: THREE.RawShaderMaterial): THREE.Object3D { - return new THREE.LineSegments(geometry, material); + buildMesh(geometry: BufferGeometry, material: RawShaderMaterial): Object3D { + return new LineSegments(geometry, material); }, supportsPicking: false, @@ -102,8 +110,8 @@ const EdgeBufferHelperType = { */ class Skeleton { - rootGroup: THREE.Group; - pickingNode: THREE.Object3D; + rootGroup: Group; + pickingNode: Object3D; // @ts-expect-error ts-migrate(2564) FIXME: Property 'prevTracing' has no initializer and is n... Remove this comment to see the full error message prevTracing: SkeletonTracing; // @ts-expect-error ts-migrate(2564) FIXME: Property 'nodes' has no initializer and is not def... Remove this comment to see the full error message @@ -111,7 +119,7 @@ class Skeleton { // @ts-expect-error ts-migrate(2564) FIXME: Property 'edges' has no initializer and is not def... Remove this comment to see the full error message edges: BufferCollection; // @ts-expect-error ts-migrate(2564) FIXME: Property 'treeColorTexture' has no initializer and... Remove this comment to see the full error message - treeColorTexture: THREE.DataTexture; + treeColorTexture: DataTexture; supportsPicking: boolean; stopStoreListening: () => void; @@ -123,8 +131,8 @@ class Skeleton { supportsPicking: boolean, ) { this.supportsPicking = supportsPicking; - this.rootGroup = new THREE.Group(); - this.pickingNode = new THREE.Object3D(); + this.rootGroup = new Group(); + this.pickingNode = new Object3D(); const skeletonTracing = skeletonTracingSelectorFn(Store.getState()); if (skeletonTracing != null) { this.reset(skeletonTracing); @@ -165,12 +173,12 @@ class Skeleton { const nodeCount = sum(trees.values().map((tree) => tree.nodes.size())); const edgeCount = sum(trees.values().map((tree) => tree.edges.size())); - this.treeColorTexture = new THREE.DataTexture( + this.treeColorTexture = new DataTexture( new Float32Array(COLOR_TEXTURE_WIDTH * COLOR_TEXTURE_WIDTH * 4), COLOR_TEXTURE_WIDTH, COLOR_TEXTURE_WIDTH, - THREE.RGBAFormat, - THREE.FloatType, + RGBAFormat, + FloatType, ); this.nodeShader = new NodeShader(this.treeColorTexture); this.edgeShader = new EdgeShader(this.treeColorTexture); @@ -219,7 +227,7 @@ class Skeleton { initializeBufferCollection( initialCapacity: number, - material: THREE.RawShaderMaterial, + material: RawShaderMaterial, helper: BufferHelper, ): BufferCollection { const initialBuffer = this.initializeBuffer( @@ -236,12 +244,8 @@ class Skeleton { }; } - initializeBuffer( - capacity: number, - material: THREE.RawShaderMaterial, - helper: BufferHelper, - ): Buffer { - const geometry = new THREE.BufferGeometry() as BufferGeometryWithBufferAttributes; + initializeBuffer(capacity: number, material: RawShaderMaterial, helper: BufferHelper): Buffer { + const geometry = new BufferGeometry() as BufferGeometryWithBufferAttributes; helper.setAttributes(geometry, capacity); const mesh = helper.buildMesh(geometry, material); // Frustum culling is disabled because nodes that are transformed @@ -491,15 +495,15 @@ class Skeleton { this.prevTracing = skeletonTracing; } - getAllNodes(): Array { + getAllNodes(): Object3D[] { return this.nodes.buffers.map((buffer) => buffer.mesh); } - getRootGroup(): THREE.Object3D { + getRootGroup(): Object3D { return this.rootGroup; } - startPicking(isTouch: boolean): THREE.Object3D { + startPicking(isTouch: boolean): Object3D { this.pickingNode.matrixAutoUpdate = false; this.pickingNode.matrix.copy(this.rootGroup.matrixWorld); this.nodes.material.uniforms.isTouch.value = isTouch ? 1 : 0; @@ -518,7 +522,7 @@ class Skeleton { * Combine node, edge and tree ids to a single unique id. * @param numbers - Array of node/edge id and treeId */ - combineIds(...numbers: Array): string { + combineIds(...numbers: number[]): string { return numbers.join(","); } @@ -553,31 +557,27 @@ class Skeleton { */ createNode(treeId: number, node: Node | UpdateActionNode | CreateActionNode) { const id = this.combineIds(node.id, treeId); - this.create( - id, - this.nodes, - ({ buffer, index }: BufferPosition): Array => { - const attributes = buffer.geometry.attributes; - const untransformedPosition = - "untransformedPosition" in node ? node.untransformedPosition : node.position; - attributes.position.set(untransformedPosition, index * 3); - - if (node.additionalCoordinates) { - for (const idx of _.range(0, node.additionalCoordinates.length)) { - const attributeAdditionalCoordinates = - buffer.geometry.attributes[`additionalCoord_${idx}`]; - attributeAdditionalCoordinates.set([node.additionalCoordinates[idx].value], index); - } + this.create(id, this.nodes, ({ buffer, index }: BufferPosition): BufferAttribute[] => { + const attributes = buffer.geometry.attributes; + const untransformedPosition = + "untransformedPosition" in node ? node.untransformedPosition : node.position; + attributes.position.set(untransformedPosition, index * 3); + + if (node.additionalCoordinates) { + for (const idx of _.range(0, node.additionalCoordinates.length)) { + const attributeAdditionalCoordinates = + buffer.geometry.attributes[`additionalCoord_${idx}`]; + attributeAdditionalCoordinates.set([node.additionalCoordinates[idx].value], index); } - attributes.radius.array[index] = node.radius; - attributes.type.array[index] = NodeTypes.NORMAL; - // @ts-expect-error ts-migrate(2542) FIXME: Index signature in type 'any[] | ArrayLike... Remove this comment to see the full error message - attributes.isCommented.array[index] = false; - attributes.nodeId.array[index] = node.id; - attributes.treeId.array[index] = treeId; - return _.values(attributes); - }, - ); + } + attributes.radius.array[index] = node.radius; + attributes.type.array[index] = NodeTypes.NORMAL; + // @ts-expect-error ts-migrate(2542) FIXME: Index signature in type 'any[] | ArrayLike... Remove this comment to see the full error message + attributes.isCommented.array[index] = false; + attributes.nodeId.array[index] = node.id; + attributes.treeId.array[index] = treeId; + return _.values(attributes); + }); } /** diff --git a/frontend/javascripts/viewer/model/accessors/dataset_layer_transformation_accessor.ts b/frontend/javascripts/viewer/model/accessors/dataset_layer_transformation_accessor.ts index 727e660455d..0ff7febc487 100644 --- a/frontend/javascripts/viewer/model/accessors/dataset_layer_transformation_accessor.ts +++ b/frontend/javascripts/viewer/model/accessors/dataset_layer_transformation_accessor.ts @@ -3,7 +3,7 @@ import MultiKeyMap from "libs/multi_key_map"; import { mod } from "libs/utils"; import _ from "lodash"; import memoizeOne from "memoize-one"; -import * as THREE from "three"; +import { Matrix4, Euler, Vector3 as ThreeVector3, Quaternion } from "three"; import type { APIDataLayer, APIDataset, @@ -106,7 +106,7 @@ export function getRotationSettingsFromTransformationIn90DegreeSteps( export function fromCenterToOrigin(bbox: BoundingBox): AffineTransformation { const center = bbox.getCenter(); - const translationMatrix = new THREE.Matrix4() + const translationMatrix = new Matrix4() .makeTranslation(-center[0], -center[1], -center[2]) .transpose(); // Column-major to row-major return { type: "affine", matrix: flatToNestedMatrix(translationMatrix.toArray()) }; @@ -114,7 +114,7 @@ export function fromCenterToOrigin(bbox: BoundingBox): AffineTransformation { export function fromOriginToCenter(bbox: BoundingBox): AffineTransformation { const center = bbox.getCenter(); - const translationMatrix = new THREE.Matrix4() + const translationMatrix = new Matrix4() .makeTranslation(center[0], center[1], center[2]) .transpose(); // Column-major to row-major return { type: "affine", matrix: flatToNestedMatrix(translationMatrix.toArray()) }; @@ -124,15 +124,15 @@ export function getRotationMatrixAroundAxis( axis: "x" | "y" | "z", rotationAndMirroringSettings: RotationAndMirroringSettings, ): AffineTransformation { - const euler = new THREE.Euler(); + const euler = new Euler(); const rotationInRadians = rotationAndMirroringSettings.rotationInDegrees * (Math.PI / 180); euler[axis] = rotationInRadians; - let rotationMatrix = new THREE.Matrix4().makeRotationFromEuler(euler); + let rotationMatrix = new Matrix4().makeRotationFromEuler(euler); if (rotationAndMirroringSettings.isMirrored) { - const scaleVector = new THREE.Vector3(1, 1, 1); + const scaleVector = new ThreeVector3(1, 1, 1); scaleVector[axis] = -1; rotationMatrix = rotationMatrix.multiply( - new THREE.Matrix4().makeScale(scaleVector.x, scaleVector.y, scaleVector.z), + new Matrix4().makeScale(scaleVector.x, scaleVector.y, scaleVector.z), ); } rotationMatrix = rotationMatrix.transpose(); // Column-major to row-major @@ -363,18 +363,18 @@ export const invertAndTranspose = _.memoize((mat: Matrix4x4) => { return M4x4.transpose(M4x4.inverse(mat)); }); -const translation = new THREE.Vector3(); -const scale = new THREE.Vector3(); -const quaternion = new THREE.Quaternion(); -const IDENTITY_QUATERNION = new THREE.Quaternion(); +const translation = new ThreeVector3(); +const scale = new ThreeVector3(); +const quaternion = new Quaternion(); +const IDENTITY_QUATERNION = new Quaternion(); -const NON_SCALED_VECTOR = new THREE.Vector3(1, 1, 1); +const NON_SCALED_VECTOR = new ThreeVector3(1, 1, 1); function isTranslationOnly(transformation?: AffineTransformation) { if (!transformation) { return false; } - const threeMatrix = new THREE.Matrix4() + const threeMatrix = new Matrix4() .fromArray(nestedToFlatMatrix(transformation.matrix)) .transpose(); threeMatrix.decompose(translation, quaternion, scale); @@ -385,7 +385,7 @@ function isOnlyRotatedOrMirrored(transformation?: AffineTransformation) { if (!transformation) { return false; } - const threeMatrix = new THREE.Matrix4() + const threeMatrix = new Matrix4() .fromArray(nestedToFlatMatrix(transformation.matrix)) .transpose(); threeMatrix.decompose(translation, quaternion, scale); diff --git a/frontend/javascripts/viewer/model/accessors/flycam_accessor.ts b/frontend/javascripts/viewer/model/accessors/flycam_accessor.ts index 7698d6c3baa..0e780c2db7e 100644 --- a/frontend/javascripts/viewer/model/accessors/flycam_accessor.ts +++ b/frontend/javascripts/viewer/model/accessors/flycam_accessor.ts @@ -3,7 +3,7 @@ import { M4x4, V3 } from "libs/mjs"; import { map3, mod } from "libs/utils"; import _ from "lodash"; import memoizeOne from "memoize-one"; -import * as THREE from "three"; +import { Euler, Matrix4, Object3D, MathUtils } from "three"; import type { AdditionalCoordinate, VoxelSize } from "types/api_types"; import { baseDatasetViewConfiguration } from "types/schemas/dataset_view_configuration.schema"; import type { @@ -302,7 +302,7 @@ function _getFlooredPosition(flycam: Flycam): Vector3 { } // Avoiding object creation with each call. -const flycamMatrixObject = new THREE.Matrix4(); +const flycamMatrixObject = new Matrix4(); // Returns the current rotation of the flycam in radians as an euler xyz tuple. // As the order in which the angles are applied is zyx (see flycam_reducer setRotationReducer), @@ -311,7 +311,7 @@ const flycamMatrixObject = new THREE.Matrix4(); function _getRotationInRadianFromMatrix(flycamMatrix: Matrix4x4, invertZ: boolean = true): Vector3 { // Somehow z rotation is inverted but the others are not. const zInvertFactor = invertZ ? -1 : 1; - const object = new THREE.Object3D(); + const object = new Object3D(); flycamMatrixObject.fromArray(flycamMatrix).transpose(); object.applyMatrix4(flycamMatrixObject); return [ @@ -344,20 +344,15 @@ function _isRotated(flycam: Flycam): boolean { return !V3.equals(getRotationInRadian(flycam), [0, 0, 0]); } -function _getFlycamRotationWithAppendedRotation( - flycam: Flycam, - rotationToAppend: THREE.Euler, -): Vector3 { +function _getFlycamRotationWithAppendedRotation(flycam: Flycam, rotationToAppend: Euler): Vector3 { const flycamRotation = getRotationInRadian(flycam, false); // Perform same operations as the flycam reducer does. First default 180° around z. let rotFlycamMatrix = eulerAngleToReducerInternalMatrix(flycamRotation); // Apply rotation - rotFlycamMatrix = rotFlycamMatrix.multiply( - new THREE.Matrix4().makeRotationFromEuler(rotationToAppend), - ); + rotFlycamMatrix = rotFlycamMatrix.multiply(new Matrix4().makeRotationFromEuler(rotationToAppend)); const rotationInRadian = reducerInternalMatrixToEulerAngle(rotFlycamMatrix); - const rotationInDegree = map3(THREE.MathUtils.radToDeg, rotationInRadian); + const rotationInDegree = map3(MathUtils.radToDeg, rotationInRadian); return rotationInDegree; } diff --git a/frontend/javascripts/viewer/model/accessors/view_mode_accessor.ts b/frontend/javascripts/viewer/model/accessors/view_mode_accessor.ts index 914d52a710b..47c9fae8ba8 100644 --- a/frontend/javascripts/viewer/model/accessors/view_mode_accessor.ts +++ b/frontend/javascripts/viewer/model/accessors/view_mode_accessor.ts @@ -1,7 +1,7 @@ import { V3 } from "libs/mjs"; import _ from "lodash"; import memoizeOne from "memoize-one"; -import * as THREE from "three"; +import { Euler, Matrix4, Vector3 as ThreeVector3 } from "three"; import type { OrthoView, OrthoViewExtents, @@ -95,11 +95,11 @@ export function getViewportScale(state: WebknossosState, viewport: Viewport): [n export type PositionWithRounding = { rounded: Vector3; floating: Vector3 }; // Avoiding object creation with each call. -const flycamRotationEuler = new THREE.Euler(); -const flycamRotationMatrix = new THREE.Matrix4(); -const flycamPositionMatrix = new THREE.Matrix4(); -const rotatedDiff = new THREE.Vector3(); -const planeRatioVector = new THREE.Vector3(); +const flycamRotationEuler = new Euler(); +const flycamRotationMatrix = new Matrix4(); +const flycamPositionMatrix = new Matrix4(); +const rotatedDiff = new ThreeVector3(); +const planeRatioVector = new ThreeVector3(); function _calculateMaybeGlobalPos( state: WebknossosState, @@ -173,17 +173,17 @@ function _calculateInViewportPos( flycamRotationInRadian: Vector3, planeRatio: Vector3, zoomStep: number, -): THREE.Vector3 { +): ThreeVector3 { // Difference in world space - const positionDiff = new THREE.Vector3(...V3.sub(globalPosition, flycamPosition)); + const positionDiff = new ThreeVector3(...V3.sub(globalPosition, flycamPosition)); // Inverse rotate the world difference vector into local plane-aligned space - const inverseRotationMatrix = new THREE.Matrix4() - .makeRotationFromEuler(new THREE.Euler(...flycamRotationInRadian, "ZYX")) + const inverseRotationMatrix = new Matrix4() + .makeRotationFromEuler(new Euler(...flycamRotationInRadian, "ZYX")) .invert(); // Unscale from voxel ratio (undo voxel scaling) - const posInScreenSpaceScaling = positionDiff.divide(new THREE.Vector3(...planeRatio)); + const posInScreenSpaceScaling = positionDiff.divide(new ThreeVector3(...planeRatio)); const rotatedIntoScreenSpace = posInScreenSpaceScaling.applyMatrix4(inverseRotationMatrix); const unzoomedPosition = rotatedIntoScreenSpace.multiplyScalar(1 / zoomStep); return unzoomedPosition; diff --git a/frontend/javascripts/viewer/model/actions/ui_actions.ts b/frontend/javascripts/viewer/model/actions/ui_actions.ts index 82535986e34..190ebdffba6 100644 --- a/frontend/javascripts/viewer/model/actions/ui_actions.ts +++ b/frontend/javascripts/viewer/model/actions/ui_actions.ts @@ -2,6 +2,7 @@ import type { OrthoView, Vector3 } from "viewer/constants"; import type { AnnotationTool } from "viewer/model/accessors/tool_accessor"; import type { BorderOpenStatus, Theme, WebknossosState } from "viewer/store"; import type { StartAIJobModalState } from "viewer/view/action-bar/starting_job_modals"; +import type { Vector3 as ThreeVector3 } from "three"; type SetDropzoneModalVisibilityAction = ReturnType; type SetVersionRestoreVisibilityAction = ReturnType; @@ -225,7 +226,7 @@ export const showContextMenuAction = ( globalPosition: Vector3 | null | undefined, viewport: OrthoView, meshId: number | null | undefined, - meshIntersectionPosition: Vector3 | null | undefined, + meshIntersectionPosition: ThreeVector3 | null | undefined, unmappedSegmentId: number | undefined | null, ) => ({ diff --git a/frontend/javascripts/viewer/model/bucket_data_handling/bucket.ts b/frontend/javascripts/viewer/model/bucket_data_handling/bucket.ts index b38978d9fba..f7bdb9b8f08 100644 --- a/frontend/javascripts/viewer/model/bucket_data_handling/bucket.ts +++ b/frontend/javascripts/viewer/model/bucket_data_handling/bucket.ts @@ -3,7 +3,15 @@ import { castForArrayType, mod } from "libs/utils"; import window from "libs/window"; import _ from "lodash"; import { type Emitter, createNanoEvents } from "nanoevents"; -import * as THREE from "three"; +import { + Color, + BufferGeometry, + LineBasicMaterial, + Line, + DataTexture, + RGBAFormat, + FloatType, +} from "three"; import type { BucketDataArray, ElementClass } from "types/api_types"; import type { AdditionalCoordinate } from "types/api_types"; import type { BoundingBoxMinMaxType } from "types/bounding_box"; @@ -99,7 +107,7 @@ export class DataBucket { readonly zoomedAddress: BucketAddress; visualizedMesh: Record | null | undefined; // @ts-expect-error ts-migrate(2564) FIXME: Property 'visualizationColor' has no initializer a... Remove this comment to see the full error message - visualizationColor: THREE.Color; + visualizationColor: Color; // If dirty, the bucket's data was potentially edited and needs to be // saved to the server. dirty: boolean; @@ -738,12 +746,12 @@ export class DataBucket { } const colors = [ - new THREE.Color(0, 0, 0), - new THREE.Color(255, 0, 0), - new THREE.Color(0, 255, 0), - new THREE.Color(0, 0, 255), - new THREE.Color(255, 0, 255), - new THREE.Color(255, 255, 0), + new Color(0, 0, 0), + new Color(255, 0, 0), + new Color(0, 255, 0), + new Color(0, 0, 255), + new Color(255, 0, 255), + new Color(255, 255, 0), ]; const zoomStep = getActiveMagIndexForLayer(Store.getState(), this.cube.layerName); @@ -768,7 +776,7 @@ export class DataBucket { } setVisualizationColor(colorDescriptor: string | number) { - const color = new THREE.Color(colorDescriptor); + const color = new Color(colorDescriptor); this.visualizationColor = color; if (this.visualizedMesh != null) { diff --git a/frontend/javascripts/viewer/model/bucket_data_handling/data_cube.ts b/frontend/javascripts/viewer/model/bucket_data_handling/data_cube.ts index 343e610aaf7..e457c94b45e 100644 --- a/frontend/javascripts/viewer/model/bucket_data_handling/data_cube.ts +++ b/frontend/javascripts/viewer/model/bucket_data_handling/data_cube.ts @@ -11,7 +11,7 @@ import { } from "libs/utils"; import _ from "lodash"; import { type Emitter, createNanoEvents } from "nanoevents"; -import * as THREE from "three"; +import { Color, Mesh, Raycaster, Vector3 as ThreeVector3, Ray } from "three"; import type { AdditionalAxis, BucketDataArray, ElementClass } from "types/api_types"; import type { AdditionalCoordinate } from "types/api_types"; import type { BoundingBoxMinMaxType } from "types/bounding_box"; @@ -569,7 +569,7 @@ class DataCube { zoomStep: number, progressCallback: ProgressCallback, use3D: boolean, - splitBoundaryMesh: THREE.Mesh | null, + splitBoundaryMesh: Mesh | null, ): Promise<{ bucketsWithLabeledVoxelsMap: LabelMasksByBucketAndW; wasBoundingBoxExceeded: boolean; @@ -1040,6 +1040,11 @@ class DataCube { return this.getVoxelIndexByVoxelOffset(voxelOffset); } + positionToZoomedAddress( + position: ThreeVector3, + additionalCoordinates: AdditionalCoordinate[] | null, + zoomStep: number, + ): BucketAddress; positionToZoomedAddress( position: Vector3, additionalCoordinates: AdditionalCoordinate[] | null, @@ -1074,7 +1079,7 @@ class DataCube { export default DataCube; -function checkLineIntersection(bentMesh: THREE.Mesh, pointAVec3: Vector3, pointBVec3: Vector3) { +function checkLineIntersection(bentMesh: Mesh, pointAVec3: Vector3, pointBVec3: Vector3) { /* Returns true if an intersection is found */ const geometry = bentMesh.geometry; @@ -1084,16 +1089,16 @@ function checkLineIntersection(bentMesh: THREE.Mesh, pointAVec3: Vector3, pointB geometry.computeBoundsTree(); } const scale = Store.getState().dataset.dataSource.scale.factor; - const pointA = new THREE.Vector3(...V3.scale3(pointAVec3, scale)); - const pointB = new THREE.Vector3(...V3.scale3(pointBVec3, scale)); + const pointA = new ThreeVector3(...V3.scale3(pointAVec3, scale)); + const pointB = new ThreeVector3(...V3.scale3(pointBVec3, scale)); // Create a ray from A to B - const ray = new THREE.Ray(); + const ray = new Ray(); ray.origin.copy(pointA); ray.direction.subVectors(pointB, pointA).normalize(); // Perform raycast - const raycaster = new THREE.Raycaster(); + const raycaster = new Raycaster(); raycaster.ray = ray; raycaster.far = pointA.distanceTo(pointB); raycaster.firstHitOnly = true; diff --git a/frontend/javascripts/viewer/model/helpers/rotation_helpers.ts b/frontend/javascripts/viewer/model/helpers/rotation_helpers.ts index 306afa035bb..82f91d8d296 100644 --- a/frontend/javascripts/viewer/model/helpers/rotation_helpers.ts +++ b/frontend/javascripts/viewer/model/helpers/rotation_helpers.ts @@ -1,12 +1,12 @@ import { V3 } from "libs/mjs"; import { mod } from "libs/utils"; -import * as THREE from "three"; +import { Matrix4, Euler } from "three"; import type { Vector3 } from "viewer/constants"; // Pre definitions to avoid redundant object creation. -const matrix = new THREE.Matrix4(); -const euler = new THREE.Euler(); -const invertedEulerMatrix = new THREE.Matrix4(); +const matrix = new Matrix4(); +const euler = new Euler(); +const invertedEulerMatrix = new Matrix4(); // This function performs the same operations as done in the flycam reducer for the setRotation action. // When rotation calculation are needed in the flycam matrix space, this function can be used to @@ -28,7 +28,7 @@ export function eulerAngleToReducerInternalMatrix(angleInRadian: Vector3): THREE } // Pre definitions to avoid redundant object creation. -const rotationFromMatrix = new THREE.Euler(); +const rotationFromMatrix = new Euler(); // The companion function of eulerAngleToReducerInternalMatrix converting a rotation back from the flycam reducer space. // The output is in radian and should be interpreted as if in ZYX order. diff --git a/frontend/javascripts/viewer/model/reducers/flycam_reducer.ts b/frontend/javascripts/viewer/model/reducers/flycam_reducer.ts index 627ab7aa19b..8a88c38afdb 100644 --- a/frontend/javascripts/viewer/model/reducers/flycam_reducer.ts +++ b/frontend/javascripts/viewer/model/reducers/flycam_reducer.ts @@ -3,7 +3,7 @@ import type { Matrix4x4 } from "libs/mjs"; import { M4x4, V3 } from "libs/mjs"; import * as Utils from "libs/utils"; import _ from "lodash"; -import * as THREE from "three"; +import { Euler, Matrix4, Vector3 as ThreeVector3 } from "three"; import type { Vector3 } from "viewer/constants"; import { ZOOM_STEP_INTERVAL, @@ -43,9 +43,9 @@ export function rotateOnAxis(currentMatrix: Matrix4x4, angle: number, axis: Vect } // Avoid creating new THREE object for some actions. -const flycamRotationEuler = new THREE.Euler(); -const flycamRotationMatrix = new THREE.Matrix4(); -const deltaInWorld = new THREE.Vector3(); +const flycamRotationEuler = new Euler(); +const flycamRotationMatrix = new Matrix4(); +const deltaInWorld = new ThreeVector3(); function rotateOnAxisWithDistance( zoomStep: number, @@ -100,6 +100,7 @@ function rotateReducer( }); } +export function getMatrixScale(voxelSize: ThreeVector3): Vector3; export function getMatrixScale(voxelSize: Vector3): Vector3 { const scale = [1 / voxelSize[0], 1 / voxelSize[1], 1 / voxelSize[2]]; const maxScale = Math.max(scale[0], scale[1], scale[2]); diff --git a/frontend/javascripts/viewer/model/sagas/skeletontracing_saga.ts b/frontend/javascripts/viewer/model/sagas/skeletontracing_saga.ts index 013b71e0cd0..cd5e70d5dd1 100644 --- a/frontend/javascripts/viewer/model/sagas/skeletontracing_saga.ts +++ b/frontend/javascripts/viewer/model/sagas/skeletontracing_saga.ts @@ -10,7 +10,7 @@ import { map3 } from "libs/utils"; import _ from "lodash"; import memoizeOne from "memoize-one"; import messages from "messages"; -import * as THREE from "three"; +import { MathUtils, Euler, Matrix4 } from "three"; import { actionChannel, all, @@ -94,9 +94,9 @@ import { takeWithBatchActionSupport } from "./saga_helpers"; function getNodeRotationWithoutPlaneRotation(activeNode: Readonly): Vector3 { // In orthogonal view mode, this active planes default rotation is added to the flycam rotation upon node creation. // To get the same flycam rotation as was active during node creation, the default rotation is calculated out from the nodes rotation. - const nodeRotationRadian = map3(THREE.MathUtils.degToRad, activeNode.rotation); + const nodeRotationRadian = map3(MathUtils.degToRad, activeNode.rotation); const nodeRotationInReducerFormatMatrix = eulerAngleToReducerInternalMatrix(nodeRotationRadian); - const viewportRotationMatrix = new THREE.Matrix4().makeRotationFromEuler( + const viewportRotationMatrix = new Matrix4().makeRotationFromEuler( OrthoBaseRotations[NumberToOrthoView[activeNode.viewport]], ); // Invert the rotation of the viewport to get the rotation configured during node creation. @@ -105,7 +105,7 @@ function getNodeRotationWithoutPlaneRotation(activeNode: Readonly): viewportRotationMatrixInverted, ); const rotationInRadian = reducerInternalMatrixToEulerAngle(rotationWithoutViewportRotation); - const flycamOnlyRotationInDegree = V3.round(map3(THREE.MathUtils.radToDeg, rotationInRadian)); + const flycamOnlyRotationInDegree = V3.round(map3(MathUtils.radToDeg, rotationInRadian)); return flycamOnlyRotationInDegree; } diff --git a/frontend/javascripts/viewer/view/arbitrary_view.ts b/frontend/javascripts/viewer/view/arbitrary_view.ts index 23435220c16..99a98c1fa33 100644 --- a/frontend/javascripts/viewer/view/arbitrary_view.ts +++ b/frontend/javascripts/viewer/view/arbitrary_view.ts @@ -1,7 +1,7 @@ import app from "app"; import window from "libs/window"; import _ from "lodash"; -import * as THREE from "three"; +import { Object3D, PerspectiveCamera, OrthographicCamera, Vector3, Matrix4 } from "three"; import TWEEN from "tween.js"; import type { OrthoViewMap, Viewport } from "viewer/constants"; import Constants, { ARBITRARY_CAM_DISTANCE, ArbitraryViewport, OrthoViews } from "viewer/constants"; @@ -20,11 +20,11 @@ import { import { clearCanvas, renderToTexture, setupRenderArea } from "viewer/view/rendering_utils"; type GeometryLike = { - addToScene: (obj: THREE.Object3D) => void; + addToScene: (obj: Object3D) => void; }; class ArbitraryView { - cameras: OrthoViewMap; + cameras: OrthoViewMap; // @ts-expect-error ts-migrate(2564) FIXME: Property 'plane' has no initializer and is not def... Remove this comment to see the full error message plane: ArbitraryPlane; animate: () => void; @@ -34,12 +34,12 @@ class ArbitraryView { isRunning: boolean = false; animationRequestId: number | null | undefined = null; // @ts-expect-error ts-migrate(2322) FIXME: Type 'null' is not assignable to type 'Perspective... Remove this comment to see the full error message - camera: THREE.PerspectiveCamera = null; + camera: PerspectiveCamera = null; // @ts-expect-error ts-migrate(2322) FIXME: Type 'null' is not assignable to type 'Orthographi... Remove this comment to see the full error message - tdCamera: THREE.OrthographicCamera = null; + tdCamera: OrthographicCamera = null; geometries: Array = []; // @ts-expect-error ts-migrate(2564) FIXME: Property 'group' has no initializer and is not def... Remove this comment to see the full error message - group: THREE.Object3D; + group: Object3D; cameraPosition: Array; unsubscribeFunctions: Array<() => void> = []; @@ -48,18 +48,18 @@ class ArbitraryView { this.setClippingDistance = this.setClippingDistanceImpl.bind(this); const { scene } = getSceneController(); - // Initialize main THREE.js components - this.camera = new THREE.PerspectiveCamera(45, 1, 50, 1000); + // Initialize main js components + this.camera = new PerspectiveCamera(45, 1, 50, 1000); // This name can be used to retrieve the camera from the scene this.camera.name = ArbitraryViewport; this.camera.matrixAutoUpdate = false; scene.add(this.camera); - const tdCamera = new THREE.OrthographicCamera(0, 0, 0, 0); - tdCamera.position.copy(new THREE.Vector3(10, 10, -10)); - tdCamera.up = new THREE.Vector3(0, 0, -1); + const tdCamera = new OrthographicCamera(0, 0, 0, 0); + tdCamera.position.copy(new Vector3(10, 10, -10)); + tdCamera.up = new Vector3(0, 0, -1); tdCamera.matrixAutoUpdate = true; this.tdCamera = tdCamera; - const dummyCamera = new THREE.OrthographicCamera(45, 1, 50, 1000); + const dummyCamera = new OrthographicCamera(45, 1, 50, 1000); this.cameras = { TDView: tdCamera, PLANE_XY: dummyCamera, @@ -70,7 +70,7 @@ class ArbitraryView { this.needsRerender = true; } - getCameras(): OrthoViewMap { + getCameras(): OrthoViewMap { return this.cameras; } @@ -92,7 +92,7 @@ class ArbitraryView { }), ); - this.group = new THREE.Object3D(); + this.group = new Object3D(); this.group.add(this.camera); getSceneController().rootGroup.add(this.group); this.resizeImpl(); @@ -165,9 +165,9 @@ class ArbitraryView { m[2], m[6], m[10], m[14], m[3], m[7], m[11], m[15], ); - camera.matrix.multiply(new THREE.Matrix4().makeRotationY(Math.PI)); + camera.matrix.multiply(new Matrix4().makeRotationY(Math.PI)); // @ts-expect-error ts-migrate(2556) FIXME: Expected 3 arguments, but got 0 or more. - camera.matrix.multiply(new THREE.Matrix4().makeTranslation(...this.cameraPosition)); + camera.matrix.multiply(new Matrix4().makeTranslation(...this.cameraPosition)); camera.matrixWorldNeedsUpdate = true; clearCanvas(renderer); const storeState = Store.getState(); @@ -254,7 +254,7 @@ class ArbitraryView { }; addGeometry(geometry: GeometryLike): void { - // Adds a new Three.js geometry to the scene. + // Adds a new js geometry to the scene. // This provides the public interface to the GeometryFactory. this.geometries.push(geometry); geometry.addToScene(this.group); diff --git a/frontend/javascripts/viewer/view/plane_view.ts b/frontend/javascripts/viewer/view/plane_view.ts index 1bbf0fd4208..8403767a2f4 100644 --- a/frontend/javascripts/viewer/view/plane_view.ts +++ b/frontend/javascripts/viewer/view/plane_view.ts @@ -2,7 +2,12 @@ import app from "app"; import VisibilityAwareRaycaster from "libs/visibility_aware_raycaster"; import window from "libs/window"; import _ from "lodash"; -import * as THREE from "three"; +import { + DirectionalLight, + OrthographicCamera, + Vector2 as ThreeVector2, + Vector3 as ThreeVector3, +} from "three"; import TWEEN from "tween.js"; import type { OrthoViewMap, Vector2, Vector3, Viewport } from "viewer/constants"; import Constants, { OrthoViewColors, OrthoViewValues, OrthoViews } from "viewer/constants"; @@ -33,10 +38,10 @@ const createDirLight = ( position: Vector3, target: Vector3, intensity: number, - camera: THREE.OrthographicCamera, + camera: OrthographicCamera, ) => { // @ts-ignore - const dirLight = new THREE.DirectionalLight(0x888888, intensity); + const dirLight = new DirectionalLight(0x888888, intensity); dirLight.position.set(...position); camera.add(dirLight); camera.add(dirLight.target); @@ -52,7 +57,7 @@ const MESH_HOVER_THROTTLING_DELAY = 50; let oldRaycasterHit: RaycasterHit = null; class PlaneView { - cameras: OrthoViewMap; + cameras: OrthoViewMap; running: boolean; needsRerender: boolean; unsubscribeFunctions: Array<() => void> = []; @@ -60,13 +65,13 @@ class PlaneView { constructor() { this.running = false; const { scene } = getSceneController(); - // Initialize main THREE.js components - const cameras = {} as OrthoViewMap; + // Initialize main js components + const cameras = {} as OrthoViewMap; for (const plane of OrthoViewValues) { // Let's set up cameras // No need to set any properties, because the cameras controller will deal with that - cameras[plane] = new THREE.OrthographicCamera(0, 0, 0, 0); + cameras[plane] = new OrthographicCamera(0, 0, 0, 0); // This name can be used to retrieve the camera from the scene cameras[plane].name = plane; scene.add(cameras[plane]); @@ -78,14 +83,14 @@ class PlaneView { this.cameras[OrthoViews.PLANE_XY].position.z = -1; this.cameras[OrthoViews.PLANE_YZ].position.x = 1; this.cameras[OrthoViews.PLANE_XZ].position.y = 1; - this.cameras[OrthoViews.TDView].position.copy(new THREE.Vector3(10, 10, -10)); - this.cameras[OrthoViews.PLANE_XY].up = new THREE.Vector3(0, -1, 0); - this.cameras[OrthoViews.PLANE_YZ].up = new THREE.Vector3(0, -1, 0); - this.cameras[OrthoViews.PLANE_XZ].up = new THREE.Vector3(0, 0, -1); - this.cameras[OrthoViews.TDView].up = new THREE.Vector3(0, 0, -1); + this.cameras[OrthoViews.TDView].position.copy(new ThreeVector3(10, 10, -10)); + this.cameras[OrthoViews.PLANE_XY].up = new ThreeVector3(0, -1, 0); + this.cameras[OrthoViews.PLANE_YZ].up = new ThreeVector3(0, -1, 0); + this.cameras[OrthoViews.PLANE_XZ].up = new ThreeVector3(0, 0, -1); + this.cameras[OrthoViews.TDView].up = new ThreeVector3(0, 0, -1); for (const plane of OrthoViewValues) { - this.cameras[plane].lookAt(new THREE.Vector3(0, 0, 0)); + this.cameras[plane].lookAt(new ThreeVector3(0, 0, 0)); } this.needsRerender = true; @@ -158,7 +163,7 @@ class PlaneView { } // Perform ray casting - const mouse = new THREE.Vector2( + const mouse = new ThreeVector2( (mousePosition[0] / tdViewport.width) * 2 - 1, ((mousePosition[1] / tdViewport.height) * 2 - 1) * -1, ); @@ -252,7 +257,7 @@ class PlaneView { this.draw(); }; - getCameras(): OrthoViewMap { + getCameras(): OrthoViewMap { return this.cameras; } diff --git a/frontend/javascripts/viewer/view/rendering_utils.ts b/frontend/javascripts/viewer/view/rendering_utils.ts index c4e5fd7bfe6..2c1ebc03111 100644 --- a/frontend/javascripts/viewer/view/rendering_utils.ts +++ b/frontend/javascripts/viewer/view/rendering_utils.ts @@ -1,6 +1,12 @@ import { saveAs } from "file-saver"; import { convertBufferToImage } from "libs/utils"; -import * as THREE from "three"; +import { + type WebGLRenderer, + type Scene, + type OrthographicCamera, + type PerspectiveCamera, + WebGLRenderTarget, +} from "three"; import { ARBITRARY_CAM_DISTANCE, type OrthoView } from "viewer/constants"; import constants, { ArbitraryViewport, @@ -17,7 +23,7 @@ const getBackgroundColor = (): number => Store.getState().uiInformation.theme === "dark" ? 0x000000 : 0xffffff; export const setupRenderArea = ( - renderer: THREE.WebGLRenderer, + renderer: WebGLRenderer, x: number, y: number, viewportWidth: number, @@ -32,14 +38,14 @@ export const setupRenderArea = ( renderer.setScissorTest(true); renderer.setClearColor(color === 0xffffff ? getBackgroundColor() : color, 1); }; -export const clearCanvas = (renderer: THREE.WebGLRenderer) => { +export const clearCanvas = (renderer: WebGLRenderer) => { setupRenderArea(renderer, 0, 0, renderer.domElement.width, renderer.domElement.height, 0xffffff); renderer.clear(); }; export function renderToTexture( plane: OrthoView | typeof ArbitraryViewport, - scene?: THREE.Scene, - camera?: THREE.OrthographicCamera | THREE.PerspectiveCamera, + scene?: Scene, + camera?: OrthographicCamera | PerspectiveCamera, // When withFarClipping is true, the user-specified clipping distance is used. // Note that the data planes might not be included in the rendered texture, since // these are exactly offset by the clipping distance. Currently, `withFarClipping` @@ -52,16 +58,14 @@ export function renderToTexture( const { renderer, scene: defaultScene } = SceneController; const state = Store.getState(); scene = scene || defaultScene; - camera = (camera || scene.getObjectByName(plane)) as - | THREE.OrthographicCamera - | THREE.PerspectiveCamera; + camera = (camera || scene.getObjectByName(plane)) as OrthographicCamera | PerspectiveCamera; // Don't respect withFarClipping for the TDViewport as we don't do any clipping for // nodes there. if (withFarClipping && plane !== OrthoViews.TDView) { - function adaptCameraToCurrentClippingDistance< - T extends THREE.OrthographicCamera | THREE.PerspectiveCamera, - >(camera: T): T { + function adaptCameraToCurrentClippingDistance( + camera: T, + ): T { const isArbitraryMode = constants.MODES_ARBITRARY.includes( state.temporaryConfiguration.viewMode, ); @@ -91,7 +95,7 @@ export function renderToTexture( renderer.setViewport(0, 0 + height, width, height); renderer.setScissorTest(false); renderer.setClearColor(clearColor === 0xffffff ? getBackgroundColor() : clearColor, 1); - const renderTarget = new THREE.WebGLRenderTarget(width, height); + const renderTarget = new WebGLRenderTarget(width, height); const buffer = new Uint8Array(width * height * 4); if (plane !== ArbitraryViewport) { From 3bc5f539059ef4e855bd2043f96f487bde5db970 Mon Sep 17 00:00:00 2001 From: Tom Herold Date: Thu, 17 Jul 2025 15:48:23 +0200 Subject: [PATCH 2/9] formatting --- frontend/javascripts/libs/UpdatableTexture.ts | 8 +++++++- .../libs/cuckoo/abstract_cuckoo_table.ts | 2 +- .../libs/cuckoo/cuckoo_table_uint32.ts | 2 +- frontend/javascripts/libs/mjs.ts | 2 +- frontend/javascripts/libs/parse_stl_buffer.ts | 2 +- frontend/javascripts/libs/stl_exporter.ts | 2 +- .../javascripts/libs/trackball_controls.ts | 2 +- .../libs/visibility_aware_raycaster.ts | 2 +- frontend/javascripts/viewer/api/api_latest.ts | 2 +- .../viewer/controller/camera_controller.ts | 8 +++++++- .../combinations/skeleton_handlers.ts | 2 +- .../controller/combinations/tool_controls.ts | 2 +- .../viewer/controller/custom_lod.ts | 2 +- .../viewer/controller/scene_controller.ts | 19 +++++++++--------- .../controller/segment_mesh_controller.ts | 12 +++++------ .../viewer/controller/td_controller.tsx | 2 +- .../viewer/geometries/arbitrary_plane.ts | 2 +- ...ompute_split_boundary_mesh_with_splines.ts | 14 ++++++------- .../viewer/geometries/crosshair.ts | 2 +- .../javascripts/viewer/geometries/cube.ts | 2 +- .../viewer/geometries/helper_geometries.ts | 20 +++++++++---------- .../geometries/materials/edge_shader.ts | 2 +- .../geometries/materials/node_shader.ts | 2 +- .../materials/plane_material_factory.ts | 2 +- .../plane_material_factory_helpers.ts | 8 ++++---- .../javascripts/viewer/geometries/plane.ts | 14 ++++++------- .../javascripts/viewer/geometries/skeleton.ts | 12 +++++------ .../dataset_layer_transformation_accessor.ts | 2 +- .../viewer/model/accessors/flycam_accessor.ts | 2 +- .../viewer/model/actions/ui_actions.ts | 2 +- .../model/bucket_data_handling/bucket.ts | 10 +--------- .../model/bucket_data_handling/data_cube.ts | 2 +- .../viewer/model/helpers/rotation_helpers.ts | 2 +- .../model/sagas/skeletontracing_saga.ts | 2 +- .../javascripts/viewer/view/arbitrary_view.ts | 2 +- .../viewer/view/rendering_utils.ts | 4 ++-- 36 files changed, 91 insertions(+), 88 deletions(-) diff --git a/frontend/javascripts/libs/UpdatableTexture.ts b/frontend/javascripts/libs/UpdatableTexture.ts index a3ec662404a..45f7191a41d 100644 --- a/frontend/javascripts/libs/UpdatableTexture.ts +++ b/frontend/javascripts/libs/UpdatableTexture.ts @@ -1,4 +1,10 @@ -import { Texture, WebGLRenderer, WebGLUtils, LinearFilter, LinearMipMapLinearFilter } from "three"; +import { + LinearFilter, + LinearMipMapLinearFilter, + Texture, + type WebGLRenderer, + WebGLUtils, +} from "three"; import type { TypedArray } from "viewer/constants"; /* The UpdatableTexture class exposes a way to partially update a texture. diff --git a/frontend/javascripts/libs/cuckoo/abstract_cuckoo_table.ts b/frontend/javascripts/libs/cuckoo/abstract_cuckoo_table.ts index 8d908e85e3c..1ec98d7a374 100644 --- a/frontend/javascripts/libs/cuckoo/abstract_cuckoo_table.ts +++ b/frontend/javascripts/libs/cuckoo/abstract_cuckoo_table.ts @@ -1,5 +1,5 @@ import type UpdatableTexture from "libs/UpdatableTexture"; -import { UnsignedIntType, RGBAIntegerFormat, type PixelFormatGPU, type PixelFormat } from "three"; +import { type PixelFormat, type PixelFormatGPU, RGBAIntegerFormat, UnsignedIntType } from "three"; import { getRenderer } from "viewer/controller/renderer"; import { createUpdatableTexture } from "viewer/geometries/materials/plane_material_factory_helpers"; diff --git a/frontend/javascripts/libs/cuckoo/cuckoo_table_uint32.ts b/frontend/javascripts/libs/cuckoo/cuckoo_table_uint32.ts index debe413a262..08d39d3e8d8 100644 --- a/frontend/javascripts/libs/cuckoo/cuckoo_table_uint32.ts +++ b/frontend/javascripts/libs/cuckoo/cuckoo_table_uint32.ts @@ -1,4 +1,4 @@ -import { RGIntegerFormat, type PixelFormatGPU } from "three"; +import { type PixelFormatGPU, RGIntegerFormat } from "three"; import type { NumberLike } from "viewer/store"; import { AbstractCuckooTable, EMPTY_KEY_VALUE } from "./abstract_cuckoo_table"; diff --git a/frontend/javascripts/libs/mjs.ts b/frontend/javascripts/libs/mjs.ts index 1d656622d68..b8a75eecdbc 100644 --- a/frontend/javascripts/libs/mjs.ts +++ b/frontend/javascripts/libs/mjs.ts @@ -2,9 +2,9 @@ // https://github.com/imbcmdth/mjs/blob/master/index.js // for all functions in M4x4, V2 and V3. import _ from "lodash"; +import type { Vector3 as ThreeVector3 } from "three"; import type { Vector2, Vector3, Vector4 } from "viewer/constants"; import { chunk3 } from "viewer/model/helpers/chunk"; -import type { Vector3 as ThreeVector3 } from "three"; import mjs from "mjs"; diff --git a/frontend/javascripts/libs/parse_stl_buffer.ts b/frontend/javascripts/libs/parse_stl_buffer.ts index 90fc36db241..892e254b27d 100644 --- a/frontend/javascripts/libs/parse_stl_buffer.ts +++ b/frontend/javascripts/libs/parse_stl_buffer.ts @@ -1,7 +1,7 @@ // @ts-nocheck /* eslint-disable */ -import { BufferGeometry, BufferAttribute, Vector3, Float32BufferAttribute, LoaderUtils } from "three"; +import { BufferAttribute, BufferGeometry, Float32BufferAttribute, LoaderUtils, Vector3 } from "three"; /** * @author aleeper / http://adamleeper.com/ * @author mrdoob / http://mrdoob.com/ diff --git a/frontend/javascripts/libs/stl_exporter.ts b/frontend/javascripts/libs/stl_exporter.ts index 9f2c7e47841..bdaebbf6eb1 100644 --- a/frontend/javascripts/libs/stl_exporter.ts +++ b/frontend/javascripts/libs/stl_exporter.ts @@ -1,5 +1,5 @@ /* eslint-disable */ -import { Scene, BufferGeometry, Vector3 } from "three"; +import { BufferGeometry, Scene, Vector3 } from "three"; // Original Source: https://github.com/mrdoob/three.js/blob/master/examples/js/exporters/STLExporter.js // Manual changes: diff --git a/frontend/javascripts/libs/trackball_controls.ts b/frontend/javascripts/libs/trackball_controls.ts index 937be63d9d9..776bc8b0c9f 100644 --- a/frontend/javascripts/libs/trackball_controls.ts +++ b/frontend/javascripts/libs/trackball_controls.ts @@ -1,5 +1,5 @@ import window, { document } from "libs/window"; -import {OrthographicCamera, Vector2, Vector3, Quaternion, EventDispatcher} from "three"; +import {EventDispatcher, OrthographicCamera, Quaternion, Vector2, Vector3 } from "three"; /** * The MIT License diff --git a/frontend/javascripts/libs/visibility_aware_raycaster.ts b/frontend/javascripts/libs/visibility_aware_raycaster.ts index 867359d6798..591e60e37fb 100644 --- a/frontend/javascripts/libs/visibility_aware_raycaster.ts +++ b/frontend/javascripts/libs/visibility_aware_raycaster.ts @@ -1,4 +1,4 @@ -import { Object3D, Intersection, Raycaster } from "three"; +import { type Intersection, type Object3D, Raycaster } from "three"; export type RaycastIntersection = Intersection; diff --git a/frontend/javascripts/viewer/api/api_latest.ts b/frontend/javascripts/viewer/api/api_latest.ts index d8eb96bfe5c..03718e25811 100644 --- a/frontend/javascripts/viewer/api/api_latest.ts +++ b/frontend/javascripts/viewer/api/api_latest.ts @@ -17,7 +17,7 @@ import { coalesce, mod } from "libs/utils"; import window, { location } from "libs/window"; import _ from "lodash"; import messages from "messages"; -import { Vector3 as ThreeVector3, Matrix4, Euler, Quaternion, MathUtils } from "three"; +import { Euler, MathUtils, Quaternion } from "three"; import TWEEN from "tween.js"; import { type APICompoundType, APICompoundTypeEnum, type ElementClass } from "types/api_types"; import type { AdditionalCoordinate } from "types/api_types"; diff --git a/frontend/javascripts/viewer/controller/camera_controller.ts b/frontend/javascripts/viewer/controller/camera_controller.ts index 9f445a54789..0b56f007c84 100644 --- a/frontend/javascripts/viewer/controller/camera_controller.ts +++ b/frontend/javascripts/viewer/controller/camera_controller.ts @@ -2,7 +2,13 @@ import { V3 } from "libs/mjs"; import * as Utils from "libs/utils"; import _ from "lodash"; import * as React from "react"; -import { OrthographicCamera, Euler, Matrix4, Vector3 as ThreeVector3, Quaternion } from "three"; +import { + Euler, + Matrix4, + type OrthographicCamera, + Quaternion, + Vector3 as ThreeVector3, +} from "three"; import TWEEN from "tween.js"; import type { OrthoView, OrthoViewMap, OrthoViewRects, Vector3 } from "viewer/constants"; import { diff --git a/frontend/javascripts/viewer/controller/combinations/skeleton_handlers.ts b/frontend/javascripts/viewer/controller/combinations/skeleton_handlers.ts index bddb11f2879..231bc4ee216 100644 --- a/frontend/javascripts/viewer/controller/combinations/skeleton_handlers.ts +++ b/frontend/javascripts/viewer/controller/combinations/skeleton_handlers.ts @@ -1,7 +1,7 @@ import { V3 } from "libs/mjs"; import { values } from "libs/utils"; import _ from "lodash"; -import { Euler, Matrix4, Vector3 as ThreeVector3, Scene } from "three"; +import { Euler, Matrix4, Scene, Vector3 as ThreeVector3 } from "three"; import type { AdditionalCoordinate } from "types/api_types"; import type { OrthoView, Point2, Vector3, Viewport } from "viewer/constants"; import { OrthoBaseRotations, OrthoViewToNumber, OrthoViews } from "viewer/constants"; diff --git a/frontend/javascripts/viewer/controller/combinations/tool_controls.ts b/frontend/javascripts/viewer/controller/combinations/tool_controls.ts index 969c1d7a815..e9eccf62790 100644 --- a/frontend/javascripts/viewer/controller/combinations/tool_controls.ts +++ b/frontend/javascripts/viewer/controller/combinations/tool_controls.ts @@ -3,7 +3,7 @@ import type { ModifierKeys } from "libs/input"; import { V3 } from "libs/mjs"; import * as Utils from "libs/utils"; import { document } from "libs/window"; -import { Vector3 as ThreeVector3, Color } from "three"; +import { Color } from "three"; import { ContourModeEnum, type OrthoView, diff --git a/frontend/javascripts/viewer/controller/custom_lod.ts b/frontend/javascripts/viewer/controller/custom_lod.ts index 9bfa3f8441f..66d31a315b8 100644 --- a/frontend/javascripts/viewer/controller/custom_lod.ts +++ b/frontend/javascripts/viewer/controller/custom_lod.ts @@ -1,4 +1,4 @@ -import { LOD, Group } from "three"; +import { Group, LOD } from "three"; import { getTDViewZoom } from "viewer/model/accessors/view_mode_accessor"; import Store from "viewer/store"; diff --git a/frontend/javascripts/viewer/controller/scene_controller.ts b/frontend/javascripts/viewer/controller/scene_controller.ts index 9d2663ccbf3..60fd1d5c42d 100644 --- a/frontend/javascripts/viewer/controller/scene_controller.ts +++ b/frontend/javascripts/viewer/controller/scene_controller.ts @@ -5,21 +5,20 @@ import window from "libs/window"; import _ from "lodash"; import { - BufferGeometry, - Mesh, - Group, - WebGLRenderer, - Scene, - Vector3 as ThreeVector3, - Euler, - Matrix4, BoxGeometry, + BufferGeometry, EdgesGeometry, + Euler, + Group, + Line, LineBasicMaterial, LineSegments, + Matrix4, + Mesh, MeshBasicMaterial, - Line, - Color, + Scene, + Vector3 as ThreeVector3, + type WebGLRenderer, } from "three"; import { acceleratedRaycast, computeBoundsTree, disposeBoundsTree } from "three-mesh-bvh"; import type { BoundingBoxMinMaxType } from "types/bounding_box"; diff --git a/frontend/javascripts/viewer/controller/segment_mesh_controller.ts b/frontend/javascripts/viewer/controller/segment_mesh_controller.ts index 6da776212ee..24754aa98b6 100644 --- a/frontend/javascripts/viewer/controller/segment_mesh_controller.ts +++ b/frontend/javascripts/viewer/controller/segment_mesh_controller.ts @@ -2,15 +2,15 @@ import app from "app"; import { mergeVertices } from "libs/BufferGeometryUtils"; import _ from "lodash"; import { - Mesh, + AmbientLight, + BufferAttribute, + BufferGeometry, Color, - MeshLambertMaterial, + DirectionalLight, FrontSide, - BufferGeometry, - BufferAttribute, Group, - AmbientLight, - DirectionalLight, + Mesh, + MeshLambertMaterial, } from "three"; import { acceleratedRaycast } from "three-mesh-bvh"; import TWEEN from "tween.js"; diff --git a/frontend/javascripts/viewer/controller/td_controller.tsx b/frontend/javascripts/viewer/controller/td_controller.tsx index 38734d4084c..ac8fea5a703 100644 --- a/frontend/javascripts/viewer/controller/td_controller.tsx +++ b/frontend/javascripts/viewer/controller/td_controller.tsx @@ -5,7 +5,7 @@ import * as Utils from "libs/utils"; import _ from "lodash"; import * as React from "react"; import { connect } from "react-redux"; -import { OrthographicCamera, Vector3 as ThreeVector3 } from "three"; +import { type OrthographicCamera, Vector3 as ThreeVector3 } from "three"; import type { VoxelSize } from "types/api_types"; import { type OrthoView, diff --git a/frontend/javascripts/viewer/geometries/arbitrary_plane.ts b/frontend/javascripts/viewer/geometries/arbitrary_plane.ts index d5634993b22..1464d2e7b93 100644 --- a/frontend/javascripts/viewer/geometries/arbitrary_plane.ts +++ b/frontend/javascripts/viewer/geometries/arbitrary_plane.ts @@ -1,5 +1,5 @@ import _ from "lodash"; -import { Mesh, Matrix4, DoubleSide, PlaneGeometry, ShaderMaterial } from "three"; +import { DoubleSide, Matrix4, Mesh, PlaneGeometry, ShaderMaterial } from "three"; import constants, { OrthoViews } from "viewer/constants"; import getSceneController from "viewer/controller/scene_controller_provider"; import PlaneMaterialFactory from "viewer/geometries/materials/plane_material_factory"; diff --git a/frontend/javascripts/viewer/geometries/compute_split_boundary_mesh_with_splines.ts b/frontend/javascripts/viewer/geometries/compute_split_boundary_mesh_with_splines.ts index 6cae39833c5..c481863b66a 100644 --- a/frontend/javascripts/viewer/geometries/compute_split_boundary_mesh_with_splines.ts +++ b/frontend/javascripts/viewer/geometries/compute_split_boundary_mesh_with_splines.ts @@ -1,16 +1,16 @@ import { orderPointsWithMST } from "libs/order_points_with_mst"; import _ from "lodash"; import { - Vector3 as ThreeVector3, - CatmullRomCurve3, - MathUtils, BufferGeometry, - LineBasicMaterial, - Line, - Float32BufferAttribute, - MeshStandardMaterial, + CatmullRomCurve3, DoubleSide, + Float32BufferAttribute, + Line, + LineBasicMaterial, + MathUtils, Mesh, + MeshStandardMaterial, + Vector3 as ThreeVector3, } from "three"; import type { Vector3 } from "viewer/constants"; diff --git a/frontend/javascripts/viewer/geometries/crosshair.ts b/frontend/javascripts/viewer/geometries/crosshair.ts index 7e7408886c5..38f522e24ad 100644 --- a/frontend/javascripts/viewer/geometries/crosshair.ts +++ b/frontend/javascripts/viewer/geometries/crosshair.ts @@ -1,4 +1,4 @@ -import { Group, Matrix4, Vector3, RingGeometry, MeshBasicMaterial, DoubleSide, Mesh } from "three"; +import { DoubleSide, Group, Matrix4, Mesh, MeshBasicMaterial, RingGeometry, Vector3 } from "three"; import { getZoomedMatrix } from "viewer/model/accessors/flycam_accessor"; import Store from "viewer/store"; diff --git a/frontend/javascripts/viewer/geometries/cube.ts b/frontend/javascripts/viewer/geometries/cube.ts index b0f92fe3360..3a34bed7939 100644 --- a/frontend/javascripts/viewer/geometries/cube.ts +++ b/frontend/javascripts/viewer/geometries/cube.ts @@ -1,6 +1,6 @@ import app from "app"; import _ from "lodash"; -import { Line, BufferGeometry, LineBasicMaterial, Vector3 as ThreeVector3 } from "three"; +import { BufferGeometry, Line, LineBasicMaterial, Vector3 as ThreeVector3 } from "three"; import type { OrthoView, OrthoViewWithoutTDMap, Vector3 } from "viewer/constants"; import { OrthoViewValuesWithoutTDView, OrthoViews } from "viewer/constants"; import { getPosition } from "viewer/model/accessors/flycam_accessor"; diff --git a/frontend/javascripts/viewer/geometries/helper_geometries.ts b/frontend/javascripts/viewer/geometries/helper_geometries.ts index 0bd8582cab8..40dd3a2b0d1 100644 --- a/frontend/javascripts/viewer/geometries/helper_geometries.ts +++ b/frontend/javascripts/viewer/geometries/helper_geometries.ts @@ -2,23 +2,23 @@ import app from "app"; import { V3 } from "libs/mjs"; import ResizableBuffer from "libs/resizable_buffer"; import { - Color, - Line, - BufferGeometry, - LineBasicMaterial, BufferAttribute, + BufferGeometry, + Color, + DataTexture, + DoubleSide, DynamicDrawUsage, - Vector2, Euler, - PlaneGeometry, - MeshBasicMaterial, - DoubleSide, + Group, + Line, + LineBasicMaterial, Mesh, - DataTexture, + MeshBasicMaterial, + PlaneGeometry, RGBAFormat, RepeatWrapping, Vector3 as ThreeVector3, - Group, + Vector2, } from "three"; import { type OrthoView, OrthoViews, type Vector3 } from "viewer/constants"; import Dimensions from "viewer/model/dimensions"; diff --git a/frontend/javascripts/viewer/geometries/materials/edge_shader.ts b/frontend/javascripts/viewer/geometries/materials/edge_shader.ts index 09e06a41b9a..376d3d69c86 100644 --- a/frontend/javascripts/viewer/geometries/materials/edge_shader.ts +++ b/frontend/javascripts/viewer/geometries/materials/edge_shader.ts @@ -1,7 +1,7 @@ import { M4x4 } from "libs/mjs"; import type TPS3D from "libs/thin_plate_spline"; import _ from "lodash"; -import { RawShaderMaterial, type DataTexture, GLSL3 } from "three"; +import { type DataTexture, GLSL3, RawShaderMaterial } from "three"; import { COLOR_TEXTURE_WIDTH_FIXED } from "viewer/geometries/materials/node_shader"; import type { Uniforms } from "viewer/geometries/materials/plane_material_factory"; import { getTransformsForSkeletonLayer } from "viewer/model/accessors/dataset_layer_transformation_accessor"; diff --git a/frontend/javascripts/viewer/geometries/materials/node_shader.ts b/frontend/javascripts/viewer/geometries/materials/node_shader.ts index 8876a8cc1d9..d0611102a04 100644 --- a/frontend/javascripts/viewer/geometries/materials/node_shader.ts +++ b/frontend/javascripts/viewer/geometries/materials/node_shader.ts @@ -1,7 +1,7 @@ import { M4x4 } from "libs/mjs"; import type TPS3D from "libs/thin_plate_spline"; import _ from "lodash"; -import { RawShaderMaterial, type DataTexture, GLSL3 } from "three"; +import { type DataTexture, GLSL3, RawShaderMaterial } from "three"; import { ViewModeValues, ViewModeValuesIndices } from "viewer/constants"; import type { Uniforms } from "viewer/geometries/materials/plane_material_factory"; import { getTransformsForSkeletonLayer } from "viewer/model/accessors/dataset_layer_transformation_accessor"; diff --git a/frontend/javascripts/viewer/geometries/materials/plane_material_factory.ts b/frontend/javascripts/viewer/geometries/materials/plane_material_factory.ts index c6153aacfb5..502f36abdff 100644 --- a/frontend/javascripts/viewer/geometries/materials/plane_material_factory.ts +++ b/frontend/javascripts/viewer/geometries/materials/plane_material_factory.ts @@ -4,7 +4,7 @@ import { V3 } from "libs/mjs"; import type TPS3D from "libs/thin_plate_spline"; import * as Utils from "libs/utils"; import _ from "lodash"; -import { Vector3 as ThreeVector3, ShaderMaterial, DoubleSide, Matrix4, Euler } from "three"; +import { DoubleSide, Euler, Matrix4, ShaderMaterial, Vector3 as ThreeVector3 } from "three"; import type { ValueOf } from "types/globals"; import { WkDevFlags } from "viewer/api/wk_dev"; import { BLEND_MODES, Identity4x4, type OrthoView, type Vector3 } from "viewer/constants"; diff --git a/frontend/javascripts/viewer/geometries/materials/plane_material_factory_helpers.ts b/frontend/javascripts/viewer/geometries/materials/plane_material_factory_helpers.ts index a05721c4405..5b2f2c35da7 100644 --- a/frontend/javascripts/viewer/geometries/materials/plane_material_factory_helpers.ts +++ b/frontend/javascripts/viewer/geometries/materials/plane_material_factory_helpers.ts @@ -1,12 +1,12 @@ import UpdatableTexture from "libs/UpdatableTexture"; import { - type TextureDataType, - type WebGLRenderer, + ClampToEdgeWrapping, + NearestFilter, type PixelFormat, type PixelFormatGPU, + type TextureDataType, UVMapping, - ClampToEdgeWrapping, - NearestFilter, + type WebGLRenderer, } from "three"; // This function has to be in its own file as non-resolvable cycles are created otherwise diff --git a/frontend/javascripts/viewer/geometries/plane.ts b/frontend/javascripts/viewer/geometries/plane.ts index e6941044d54..70ac95da68f 100644 --- a/frontend/javascripts/viewer/geometries/plane.ts +++ b/frontend/javascripts/viewer/geometries/plane.ts @@ -1,16 +1,16 @@ import { V3 } from "libs/mjs"; import _ from "lodash"; import { - Vector3 as ThreeVector3, - Euler, - PlaneGeometry, - Mesh, - LineSegments, - BufferGeometry, BufferAttribute, - LineBasicMaterial, + BufferGeometry, + Euler, Line, + LineBasicMaterial, + LineSegments, Matrix4, + Mesh, + PlaneGeometry, + Vector3 as ThreeVector3, } from "three"; import type { OrthoView, Vector3 } from "viewer/constants"; import constants, { diff --git a/frontend/javascripts/viewer/geometries/skeleton.ts b/frontend/javascripts/viewer/geometries/skeleton.ts index e16739a673c..5cdffb97755 100644 --- a/frontend/javascripts/viewer/geometries/skeleton.ts +++ b/frontend/javascripts/viewer/geometries/skeleton.ts @@ -1,16 +1,16 @@ import * as Utils from "libs/utils"; import _ from "lodash"; import { - BufferGeometry, BufferAttribute, + BufferGeometry, + DataTexture, + FloatType, + Group, + LineSegments, Object3D, Points, - LineSegments, - Group, - DataTexture, RGBAFormat, - FloatType, - RawShaderMaterial, + type RawShaderMaterial, } from "three"; import type { AdditionalCoordinate } from "types/api_types"; import type { Vector3, Vector4 } from "viewer/constants"; diff --git a/frontend/javascripts/viewer/model/accessors/dataset_layer_transformation_accessor.ts b/frontend/javascripts/viewer/model/accessors/dataset_layer_transformation_accessor.ts index 0ff7febc487..ce56bed6b04 100644 --- a/frontend/javascripts/viewer/model/accessors/dataset_layer_transformation_accessor.ts +++ b/frontend/javascripts/viewer/model/accessors/dataset_layer_transformation_accessor.ts @@ -3,7 +3,7 @@ import MultiKeyMap from "libs/multi_key_map"; import { mod } from "libs/utils"; import _ from "lodash"; import memoizeOne from "memoize-one"; -import { Matrix4, Euler, Vector3 as ThreeVector3, Quaternion } from "three"; +import { Euler, Matrix4, Quaternion, Vector3 as ThreeVector3 } from "three"; import type { APIDataLayer, APIDataset, diff --git a/frontend/javascripts/viewer/model/accessors/flycam_accessor.ts b/frontend/javascripts/viewer/model/accessors/flycam_accessor.ts index 0e780c2db7e..2f77ec7dbc5 100644 --- a/frontend/javascripts/viewer/model/accessors/flycam_accessor.ts +++ b/frontend/javascripts/viewer/model/accessors/flycam_accessor.ts @@ -3,7 +3,7 @@ import { M4x4, V3 } from "libs/mjs"; import { map3, mod } from "libs/utils"; import _ from "lodash"; import memoizeOne from "memoize-one"; -import { Euler, Matrix4, Object3D, MathUtils } from "three"; +import { type Euler, MathUtils, Matrix4, Object3D } from "three"; import type { AdditionalCoordinate, VoxelSize } from "types/api_types"; import { baseDatasetViewConfiguration } from "types/schemas/dataset_view_configuration.schema"; import type { diff --git a/frontend/javascripts/viewer/model/actions/ui_actions.ts b/frontend/javascripts/viewer/model/actions/ui_actions.ts index 190ebdffba6..09527eb23c4 100644 --- a/frontend/javascripts/viewer/model/actions/ui_actions.ts +++ b/frontend/javascripts/viewer/model/actions/ui_actions.ts @@ -1,8 +1,8 @@ +import type { Vector3 as ThreeVector3 } from "three"; import type { OrthoView, Vector3 } from "viewer/constants"; import type { AnnotationTool } from "viewer/model/accessors/tool_accessor"; import type { BorderOpenStatus, Theme, WebknossosState } from "viewer/store"; import type { StartAIJobModalState } from "viewer/view/action-bar/starting_job_modals"; -import type { Vector3 as ThreeVector3 } from "three"; type SetDropzoneModalVisibilityAction = ReturnType; type SetVersionRestoreVisibilityAction = ReturnType; diff --git a/frontend/javascripts/viewer/model/bucket_data_handling/bucket.ts b/frontend/javascripts/viewer/model/bucket_data_handling/bucket.ts index f7bdb9b8f08..11d51423577 100644 --- a/frontend/javascripts/viewer/model/bucket_data_handling/bucket.ts +++ b/frontend/javascripts/viewer/model/bucket_data_handling/bucket.ts @@ -3,15 +3,7 @@ import { castForArrayType, mod } from "libs/utils"; import window from "libs/window"; import _ from "lodash"; import { type Emitter, createNanoEvents } from "nanoevents"; -import { - Color, - BufferGeometry, - LineBasicMaterial, - Line, - DataTexture, - RGBAFormat, - FloatType, -} from "three"; +import { Color } from "three"; import type { BucketDataArray, ElementClass } from "types/api_types"; import type { AdditionalCoordinate } from "types/api_types"; import type { BoundingBoxMinMaxType } from "types/bounding_box"; diff --git a/frontend/javascripts/viewer/model/bucket_data_handling/data_cube.ts b/frontend/javascripts/viewer/model/bucket_data_handling/data_cube.ts index e457c94b45e..f730b5fbb5b 100644 --- a/frontend/javascripts/viewer/model/bucket_data_handling/data_cube.ts +++ b/frontend/javascripts/viewer/model/bucket_data_handling/data_cube.ts @@ -11,7 +11,7 @@ import { } from "libs/utils"; import _ from "lodash"; import { type Emitter, createNanoEvents } from "nanoevents"; -import { Color, Mesh, Raycaster, Vector3 as ThreeVector3, Ray } from "three"; +import { type Mesh, Ray, Raycaster, Vector3 as ThreeVector3 } from "three"; import type { AdditionalAxis, BucketDataArray, ElementClass } from "types/api_types"; import type { AdditionalCoordinate } from "types/api_types"; import type { BoundingBoxMinMaxType } from "types/bounding_box"; diff --git a/frontend/javascripts/viewer/model/helpers/rotation_helpers.ts b/frontend/javascripts/viewer/model/helpers/rotation_helpers.ts index 82f91d8d296..67fdd5e417b 100644 --- a/frontend/javascripts/viewer/model/helpers/rotation_helpers.ts +++ b/frontend/javascripts/viewer/model/helpers/rotation_helpers.ts @@ -1,6 +1,6 @@ import { V3 } from "libs/mjs"; import { mod } from "libs/utils"; -import { Matrix4, Euler } from "three"; +import { Euler, Matrix4 } from "three"; import type { Vector3 } from "viewer/constants"; // Pre definitions to avoid redundant object creation. diff --git a/frontend/javascripts/viewer/model/sagas/skeletontracing_saga.ts b/frontend/javascripts/viewer/model/sagas/skeletontracing_saga.ts index cd5e70d5dd1..0f9285fae4a 100644 --- a/frontend/javascripts/viewer/model/sagas/skeletontracing_saga.ts +++ b/frontend/javascripts/viewer/model/sagas/skeletontracing_saga.ts @@ -10,7 +10,7 @@ import { map3 } from "libs/utils"; import _ from "lodash"; import memoizeOne from "memoize-one"; import messages from "messages"; -import { MathUtils, Euler, Matrix4 } from "three"; +import { MathUtils, Matrix4 } from "three"; import { actionChannel, all, diff --git a/frontend/javascripts/viewer/view/arbitrary_view.ts b/frontend/javascripts/viewer/view/arbitrary_view.ts index 99a98c1fa33..ba14ad64832 100644 --- a/frontend/javascripts/viewer/view/arbitrary_view.ts +++ b/frontend/javascripts/viewer/view/arbitrary_view.ts @@ -1,7 +1,7 @@ import app from "app"; import window from "libs/window"; import _ from "lodash"; -import { Object3D, PerspectiveCamera, OrthographicCamera, Vector3, Matrix4 } from "three"; +import { Matrix4, Object3D, OrthographicCamera, PerspectiveCamera, Vector3 } from "three"; import TWEEN from "tween.js"; import type { OrthoViewMap, Viewport } from "viewer/constants"; import Constants, { ARBITRARY_CAM_DISTANCE, ArbitraryViewport, OrthoViews } from "viewer/constants"; diff --git a/frontend/javascripts/viewer/view/rendering_utils.ts b/frontend/javascripts/viewer/view/rendering_utils.ts index 2c1ebc03111..4ad5b824d90 100644 --- a/frontend/javascripts/viewer/view/rendering_utils.ts +++ b/frontend/javascripts/viewer/view/rendering_utils.ts @@ -1,11 +1,11 @@ import { saveAs } from "file-saver"; import { convertBufferToImage } from "libs/utils"; import { - type WebGLRenderer, - type Scene, type OrthographicCamera, type PerspectiveCamera, + type Scene, WebGLRenderTarget, + type WebGLRenderer, } from "three"; import { ARBITRARY_CAM_DISTANCE, type OrthoView } from "viewer/constants"; import constants, { From 823fb4cccaa054f84e0b61f4d33948d319d3c858 Mon Sep 17 00:00:00 2001 From: Tom Herold Date: Thu, 17 Jul 2025 16:11:21 +0200 Subject: [PATCH 3/9] update more threejs imports --- frontend/javascripts/libs/UpdatableTexture.ts | 6 ++ .../javascripts/libs/compute_bvh_async.ts | 6 +- .../javascripts/libs/order_points_with_mst.ts | 8 +-- frontend/javascripts/libs/window.ts | 4 +- .../test/api/api_skeleton_latest.spec.ts | 2 +- .../viewer/controller/mesh_helpers.ts | 6 +- .../controller/segment_mesh_controller.ts | 3 +- .../geometries/materials/edge_shader.ts | 2 +- .../data_rendering_logic.tsx | 67 +++++++++++-------- .../layer_rendering_manager.ts | 4 +- .../texture_bucket_manager.ts | 4 +- .../viewer/model/helpers/rotation_helpers.ts | 4 +- .../model/sagas/meshes/common_mesh_saga.ts | 4 +- 13 files changed, 69 insertions(+), 51 deletions(-) diff --git a/frontend/javascripts/libs/UpdatableTexture.ts b/frontend/javascripts/libs/UpdatableTexture.ts index 45f7191a41d..afeabd74bf3 100644 --- a/frontend/javascripts/libs/UpdatableTexture.ts +++ b/frontend/javascripts/libs/UpdatableTexture.ts @@ -1,9 +1,15 @@ import { LinearFilter, LinearMipMapLinearFilter, + type MagnificationTextureFilter, + type Mapping, + type MinificationTextureFilter, + type PixelFormat, Texture, + type TextureDataType, type WebGLRenderer, WebGLUtils, + type Wrapping, } from "three"; import type { TypedArray } from "viewer/constants"; diff --git a/frontend/javascripts/libs/compute_bvh_async.ts b/frontend/javascripts/libs/compute_bvh_async.ts index 0c4b88db88b..b252e83eb31 100644 --- a/frontend/javascripts/libs/compute_bvh_async.ts +++ b/frontend/javascripts/libs/compute_bvh_async.ts @@ -1,11 +1,11 @@ -import type * as THREE from "three"; +import type { BufferGeometry } from "three"; import type { MeshBVH } from "three-mesh-bvh"; // @ts-ignore import { GenerateMeshBVHWorker } from "three-mesh-bvh/src/workers/GenerateMeshBVHWorker"; const bvhWorker = new GenerateMeshBVHWorker(); const bvhQueue: Array<{ - geometry: THREE.BufferGeometry; + geometry: BufferGeometry; resolve: (bvh: MeshBVH) => void; reject: (error: unknown) => void; }> = []; @@ -32,7 +32,7 @@ async function processBvhQueue() { } } -export async function computeBvhAsync(geometry: THREE.BufferGeometry): Promise { +export async function computeBvhAsync(geometry: BufferGeometry): Promise { return new Promise((resolve, reject) => { bvhQueue.push({ geometry, resolve, reject }); processBvhQueue(); diff --git a/frontend/javascripts/libs/order_points_with_mst.ts b/frontend/javascripts/libs/order_points_with_mst.ts index c3d64a58948..1f33e4624db 100644 --- a/frontend/javascripts/libs/order_points_with_mst.ts +++ b/frontend/javascripts/libs/order_points_with_mst.ts @@ -1,6 +1,6 @@ -import type * as THREE from "three"; +import type { Vector3 } from "three"; -export function orderPointsWithMST(points: THREE.Vector3[]): THREE.Vector3[] { +export function orderPointsWithMST(points: Vector3[]): Vector3[] { /* * Find the order of points with the shortest distance heuristically. * This is done by computing the MST of the points and then traversing @@ -63,7 +63,7 @@ interface Edge { dist: number; } -function computeMST(points: THREE.Vector3[]): number[][] { +function computeMST(points: Vector3[]): number[][] { const edges: Edge[] = []; const numPoints = points.length; @@ -110,7 +110,7 @@ function traverseMstDfs(mst: number[][], startIdx = 0): number[] { return orderedPoints; } -function computePathLength(points: THREE.Vector3[], order: number[]): number { +function computePathLength(points: Vector3[], order: number[]): number { let length = 0; for (let i = 0; i < order.length - 1; i++) { length += points[order[i]].distanceTo(points[order[i + 1]]); diff --git a/frontend/javascripts/libs/window.ts b/frontend/javascripts/libs/window.ts index 22b3e6d18fd..0cfc2eb598d 100644 --- a/frontend/javascripts/libs/window.ts +++ b/frontend/javascripts/libs/window.ts @@ -1,6 +1,6 @@ // This module should be used to access the window object, so it can be mocked in the unit tests -import type * as THREE from "three"; +import type { ShaderMaterial } from "three"; import type { ArbitraryFunction, ArbitraryObject } from "types/globals"; import type TextureBucketManager from "viewer/model/bucket_data_handling/texture_bucket_manager"; @@ -75,7 +75,7 @@ const _window: Window & Olvy?: Olvy; OlvyConfig?: ArbitraryObject | null; managers?: Array; - materials?: Record; + materials?: Record; } = typeof window === "undefined" ? ({ diff --git a/frontend/javascripts/test/api/api_skeleton_latest.spec.ts b/frontend/javascripts/test/api/api_skeleton_latest.spec.ts index 14ea137a043..88cf0c741a3 100644 --- a/frontend/javascripts/test/api/api_skeleton_latest.spec.ts +++ b/frontend/javascripts/test/api/api_skeleton_latest.spec.ts @@ -395,7 +395,7 @@ describe("API Skeleton", () => { ); expect( rotationQuaternion.angleTo(newNodeQuaternion), - `Node rotation ${newNode.rotation} is not nearly equal to ${map3(THREE.MathUtils.radToDeg, resultingAngle)} in viewport ${planeId}.`, + `Node rotation ${newNode.rotation} is not nearly equal to ${map3(MathUtils.radToDeg, resultingAngle)} in viewport ${planeId}.`, ).toBeLessThan(0.000001); } } diff --git a/frontend/javascripts/viewer/controller/mesh_helpers.ts b/frontend/javascripts/viewer/controller/mesh_helpers.ts index 299eb33e2d4..57fb802baad 100644 --- a/frontend/javascripts/viewer/controller/mesh_helpers.ts +++ b/frontend/javascripts/viewer/controller/mesh_helpers.ts @@ -1,14 +1,14 @@ import type { meshApi } from "admin/rest_api"; import { V3 } from "libs/mjs"; import _ from "lodash"; -import type * as THREE from "three"; +import type { BufferGeometry } from "three"; import type { Vector3 } from "viewer/constants"; -export type BufferGeometryWithInfo = THREE.BufferGeometry & { +export type BufferGeometryWithInfo = BufferGeometry & { vertexSegmentMapping?: VertexSegmentMapping; }; -export type UnmergedBufferGeometryWithInfo = THREE.BufferGeometry & { +export type UnmergedBufferGeometryWithInfo = BufferGeometry & { unmappedSegmentId: number; vertexSegmentMapping?: VertexSegmentMapping; }; diff --git a/frontend/javascripts/viewer/controller/segment_mesh_controller.ts b/frontend/javascripts/viewer/controller/segment_mesh_controller.ts index 24754aa98b6..8cd93cb9cae 100644 --- a/frontend/javascripts/viewer/controller/segment_mesh_controller.ts +++ b/frontend/javascripts/viewer/controller/segment_mesh_controller.ts @@ -11,6 +11,7 @@ import { Group, Mesh, MeshLambertMaterial, + Vector3 as ThreeVector3, } from "three"; import { acceleratedRaycast } from "three-mesh-bvh"; import TWEEN from "tween.js"; @@ -244,7 +245,7 @@ export default class SegmentMeshController { scale[1] / dsScaleFactor[1], scale[2] / dsScaleFactor[2], ]; - targetGroup.scale.copy(new Vector3(...adaptedScale)); + targetGroup.scale.copy(new ThreeVector3(...adaptedScale)); } const meshChunk = this.constructMesh(segmentId, layerName, geometry, opacity, isMerged); diff --git a/frontend/javascripts/viewer/geometries/materials/edge_shader.ts b/frontend/javascripts/viewer/geometries/materials/edge_shader.ts index 376d3d69c86..a80a9cbeeea 100644 --- a/frontend/javascripts/viewer/geometries/materials/edge_shader.ts +++ b/frontend/javascripts/viewer/geometries/materials/edge_shader.ts @@ -96,7 +96,7 @@ class EdgeShader { ]; } - getMaterial(): THREE.RawShaderMaterial { + getMaterial(): RawShaderMaterial { return this.material; } diff --git a/frontend/javascripts/viewer/model/bucket_data_handling/data_rendering_logic.tsx b/frontend/javascripts/viewer/model/bucket_data_handling/data_rendering_logic.tsx index 3e81ecd9afc..b1c9b1e53b5 100644 --- a/frontend/javascripts/viewer/model/bucket_data_handling/data_rendering_logic.tsx +++ b/frontend/javascripts/viewer/model/bucket_data_handling/data_rendering_logic.tsx @@ -2,7 +2,18 @@ import ErrorHandling from "libs/error_handling"; import Toast from "libs/toast"; import { document } from "libs/window"; import _ from "lodash"; -import * as THREE from "three"; +import { + ByteType, + FloatType, + type PixelFormat, + type PixelFormatGPU, + RGBAFormat, + RGIntegerFormat, + ShortType, + type TextureDataType, + UnsignedByteType, + UnsignedShortType, +} from "three"; import type { ElementClass } from "types/api_types"; import constants from "viewer/constants"; import type { TypedArrayConstructor } from "../helpers/typed_buffer"; @@ -353,10 +364,10 @@ export const getSupportedValueRangeForElementClass = _.memoize( ); export function getDtypeConfigForElementClass(elementClass: ElementClass): { - textureType: THREE.TextureDataType; + textureType: TextureDataType; TypedArrayClass: TypedArrayConstructor; - pixelFormat: THREE.PixelFormat; - internalFormat: THREE.PixelFormatGPU | undefined; + pixelFormat: PixelFormat; + internalFormat: PixelFormatGPU | undefined; glslPrefix: "" | "u" | "i"; isSigned: boolean; packingDegree: number; @@ -367,9 +378,9 @@ export function getDtypeConfigForElementClass(elementClass: ElementClass): { switch (elementClass) { case "int8": return { - textureType: THREE.ByteType, + textureType: ByteType, TypedArrayClass: Int8Array, - pixelFormat: THREE.RGBAFormat, + pixelFormat: RGBAFormat, internalFormat: "RGBA8_SNORM", glslPrefix: "", isSigned: true, @@ -377,9 +388,9 @@ export function getDtypeConfigForElementClass(elementClass: ElementClass): { }; case "uint8": return { - textureType: THREE.UnsignedByteType, + textureType: UnsignedByteType, TypedArrayClass: Uint8Array, - pixelFormat: THREE.RGBAFormat, + pixelFormat: RGBAFormat, internalFormat: undefined, glslPrefix: "", isSigned: false, @@ -388,9 +399,9 @@ export function getDtypeConfigForElementClass(elementClass: ElementClass): { case "uint24": // Since uint24 layers are multi-channel, their intensity ranges are equal to uint8 return { - textureType: THREE.UnsignedByteType, + textureType: UnsignedByteType, TypedArrayClass: Uint8Array, - pixelFormat: THREE.RGBAFormat, + pixelFormat: RGBAFormat, internalFormat: undefined, glslPrefix: "", isSigned: false, @@ -399,9 +410,9 @@ export function getDtypeConfigForElementClass(elementClass: ElementClass): { case "uint16": return { - textureType: THREE.UnsignedShortType, + textureType: UnsignedShortType, TypedArrayClass: Uint16Array, - pixelFormat: THREE.RGIntegerFormat, + pixelFormat: RGIntegerFormat, internalFormat: "RG16UI", glslPrefix: "u", isSigned: false, @@ -410,9 +421,9 @@ export function getDtypeConfigForElementClass(elementClass: ElementClass): { case "int16": return { - textureType: THREE.ShortType, + textureType: ShortType, TypedArrayClass: Int16Array, - pixelFormat: THREE.RGIntegerFormat, + pixelFormat: RGIntegerFormat, internalFormat: "RG16I", glslPrefix: "i", isSigned: true, @@ -421,9 +432,9 @@ export function getDtypeConfigForElementClass(elementClass: ElementClass): { case "uint32": return { - textureType: THREE.UnsignedByteType, + textureType: UnsignedByteType, TypedArrayClass: Uint8Array, - pixelFormat: THREE.RGBAFormat, + pixelFormat: RGBAFormat, internalFormat: undefined, glslPrefix: "", isSigned: false, @@ -432,9 +443,9 @@ export function getDtypeConfigForElementClass(elementClass: ElementClass): { case "int32": return { - textureType: THREE.UnsignedByteType, + textureType: UnsignedByteType, TypedArrayClass: Uint8Array, - pixelFormat: THREE.RGBAFormat, + pixelFormat: RGBAFormat, internalFormat: undefined, glslPrefix: "", isSigned: true, @@ -443,9 +454,9 @@ export function getDtypeConfigForElementClass(elementClass: ElementClass): { case "uint64": return { - textureType: THREE.UnsignedByteType, + textureType: UnsignedByteType, TypedArrayClass: Uint8Array, - pixelFormat: THREE.RGBAFormat, + pixelFormat: RGBAFormat, internalFormat: undefined, glslPrefix: "", isSigned: false, @@ -454,9 +465,9 @@ export function getDtypeConfigForElementClass(elementClass: ElementClass): { case "int64": return { - textureType: THREE.UnsignedByteType, + textureType: UnsignedByteType, TypedArrayClass: Uint8Array, - pixelFormat: THREE.RGBAFormat, + pixelFormat: RGBAFormat, internalFormat: undefined, glslPrefix: "", isSigned: true, @@ -465,9 +476,9 @@ export function getDtypeConfigForElementClass(elementClass: ElementClass): { case "float": return { - textureType: THREE.FloatType, + textureType: FloatType, TypedArrayClass: Float32Array, - pixelFormat: THREE.RGBAFormat, + pixelFormat: RGBAFormat, internalFormat: undefined, glslPrefix: "", isSigned: true, @@ -477,9 +488,9 @@ export function getDtypeConfigForElementClass(elementClass: ElementClass): { // We do not fully support double case "double": return { - textureType: THREE.UnsignedByteType, + textureType: UnsignedByteType, TypedArrayClass: Uint8Array, - pixelFormat: THREE.RGBAFormat, + pixelFormat: RGBAFormat, internalFormat: undefined, glslPrefix: "", isSigned: true, @@ -488,9 +499,9 @@ export function getDtypeConfigForElementClass(elementClass: ElementClass): { default: return { - textureType: THREE.UnsignedByteType, + textureType: UnsignedByteType, TypedArrayClass: Uint8Array, - pixelFormat: THREE.RGBAFormat, + pixelFormat: RGBAFormat, internalFormat: undefined, glslPrefix: "", isSigned: false, diff --git a/frontend/javascripts/viewer/model/bucket_data_handling/layer_rendering_manager.ts b/frontend/javascripts/viewer/model/bucket_data_handling/layer_rendering_manager.ts index 50b401c75f0..191f49e423d 100644 --- a/frontend/javascripts/viewer/model/bucket_data_handling/layer_rendering_manager.ts +++ b/frontend/javascripts/viewer/model/bucket_data_handling/layer_rendering_manager.ts @@ -8,7 +8,7 @@ import { M4x4, type Matrix4x4, V3 } from "libs/mjs"; import Toast from "libs/toast"; import _ from "lodash"; import memoizeOne from "memoize-one"; -import type * as THREE from "three"; +import type { DataTexture } from "three"; import type { AdditionalCoordinate } from "types/api_types"; import type { BucketAddress, Vector3, Vector4, ViewMode } from "viewer/constants"; import { @@ -173,7 +173,7 @@ export default class LayerRenderingManager { } } - getDataTextures(): Array { + getDataTextures(): Array { if (!this.textureBucketManager) { // Initialize lazily since SceneController.renderer is not available earlier this.setupDataTextures(); diff --git a/frontend/javascripts/viewer/model/bucket_data_handling/texture_bucket_manager.ts b/frontend/javascripts/viewer/model/bucket_data_handling/texture_bucket_manager.ts index f981aa2e221..12665f88d33 100644 --- a/frontend/javascripts/viewer/model/bucket_data_handling/texture_bucket_manager.ts +++ b/frontend/javascripts/viewer/model/bucket_data_handling/texture_bucket_manager.ts @@ -4,7 +4,7 @@ import type { CuckooTableVec5 } from "libs/cuckoo/cuckoo_table_vec5"; import { waitForCondition } from "libs/utils"; import window from "libs/window"; import _ from "lodash"; -import type * as THREE from "three"; +import type { DataTexture } from "three"; import type { ElementClass } from "types/api_types"; import { WkDevFlags } from "viewer/api/wk_dev"; import constants, { type TypedArray } from "viewer/constants"; @@ -259,7 +259,7 @@ export default class TextureBucketManager { }); } - getTextures(): Array { + getTextures(): Array { return [this.lookUpCuckooTable._texture].concat(this.dataTextures); } diff --git a/frontend/javascripts/viewer/model/helpers/rotation_helpers.ts b/frontend/javascripts/viewer/model/helpers/rotation_helpers.ts index 67fdd5e417b..b9ba2048c8e 100644 --- a/frontend/javascripts/viewer/model/helpers/rotation_helpers.ts +++ b/frontend/javascripts/viewer/model/helpers/rotation_helpers.ts @@ -15,7 +15,7 @@ const invertedEulerMatrix = new Matrix4(); // should be used to transform the result back. // For some more info look at // https://www.notion.so/scalableminds/3D-Rotations-3D-Scene-210b51644c6380c2a4a6f5f3c069738a?source=copy_link#22bb51644c6380cf8302fb8f6749ae1d. -export function eulerAngleToReducerInternalMatrix(angleInRadian: Vector3): THREE.Matrix4 { +export function eulerAngleToReducerInternalMatrix(angleInRadian: Vector3): Matrix4 { // Perform same operations as the flycam reducer does. First default 180° around z. let matrixLikeInReducer = matrix.makeRotationZ(Math.PI); // Invert angle and interpret as ZYX order @@ -33,7 +33,7 @@ const rotationFromMatrix = new Euler(); // The companion function of eulerAngleToReducerInternalMatrix converting a rotation back from the flycam reducer space. // The output is in radian and should be interpreted as if in ZYX order. // Note: The matrix must be a rotation only matrix. -export function reducerInternalMatrixToEulerAngle(matrixInReducerFormat: THREE.Matrix4): Vector3 { +export function reducerInternalMatrixToEulerAngle(matrixInReducerFormat: Matrix4): Vector3 { const localRotationFromMatrix = rotationFromMatrix.setFromRotationMatrix( matrixInReducerFormat.clone().transpose(), "XYZ", diff --git a/frontend/javascripts/viewer/model/sagas/meshes/common_mesh_saga.ts b/frontend/javascripts/viewer/model/sagas/meshes/common_mesh_saga.ts index 1073bb7294f..6ad0522ee53 100644 --- a/frontend/javascripts/viewer/model/sagas/meshes/common_mesh_saga.ts +++ b/frontend/javascripts/viewer/model/sagas/meshes/common_mesh_saga.ts @@ -4,7 +4,7 @@ import exportToStl from "libs/stl_exporter"; import Toast from "libs/toast"; import Zip from "libs/zipjs_wrapper"; import messages from "messages"; -import type * as THREE from "three"; +import type { Group } from "three"; import { all, call, put, take, takeEvery } from "typed-redux-saga"; import getSceneController from "viewer/controller/scene_controller_provider"; import { @@ -90,7 +90,7 @@ function* downloadMeshCellsAsZIP( } } -const getSTLBlob = (geometry: THREE.Group, segmentId: number): Blob => { +const getSTLBlob = (geometry: Group, segmentId: number): Blob => { const stlDataViews = exportToStl(geometry); // Encode mesh and cell id property const { meshMarker, segmentIdIndex } = stlMeshConstants; From 90aa5cedbc0b7eaca8d485a40775ccac10afc982 Mon Sep 17 00:00:00 2001 From: Tom Herold Date: Thu, 17 Jul 2025 17:20:07 +0200 Subject: [PATCH 4/9] fix typechecking --- .../controller/combinations/skeleton_handlers.ts | 2 +- .../javascripts/viewer/controller/scene_controller.ts | 1 + .../javascripts/viewer/geometries/arbitrary_plane.ts | 2 +- .../compute_split_boundary_mesh_with_splines.ts | 1 + frontend/javascripts/viewer/geometries/crosshair.ts | 11 ++++++++++- .../javascripts/viewer/model/actions/ui_actions.ts | 3 +-- .../viewer/model/bucket_data_handling/data_cube.ts | 5 ----- .../viewer/model/reducers/flycam_reducer.ts | 1 - 8 files changed, 15 insertions(+), 11 deletions(-) diff --git a/frontend/javascripts/viewer/controller/combinations/skeleton_handlers.ts b/frontend/javascripts/viewer/controller/combinations/skeleton_handlers.ts index 231bc4ee216..8636c3d97d9 100644 --- a/frontend/javascripts/viewer/controller/combinations/skeleton_handlers.ts +++ b/frontend/javascripts/viewer/controller/combinations/skeleton_handlers.ts @@ -133,7 +133,7 @@ export function handleOpenContextMenu( isTouch: boolean, event: MouseEvent, meshId?: number | null | undefined, - meshIntersectionPosition?: ThreeVector3 | null | undefined, + meshIntersectionPosition?: Vector3 | null | undefined, unmappedSegmentId?: number | null | undefined, ) { const { activeViewport } = Store.getState().viewModeData.plane; diff --git a/frontend/javascripts/viewer/controller/scene_controller.ts b/frontend/javascripts/viewer/controller/scene_controller.ts index 60fd1d5c42d..cc2c1f9c890 100644 --- a/frontend/javascripts/viewer/controller/scene_controller.ts +++ b/frontend/javascripts/viewer/controller/scene_controller.ts @@ -16,6 +16,7 @@ import { Matrix4, Mesh, MeshBasicMaterial, + type Object3D, Scene, Vector3 as ThreeVector3, type WebGLRenderer, diff --git a/frontend/javascripts/viewer/geometries/arbitrary_plane.ts b/frontend/javascripts/viewer/geometries/arbitrary_plane.ts index 1464d2e7b93..7f734e97dc0 100644 --- a/frontend/javascripts/viewer/geometries/arbitrary_plane.ts +++ b/frontend/javascripts/viewer/geometries/arbitrary_plane.ts @@ -1,5 +1,5 @@ import _ from "lodash"; -import { DoubleSide, Matrix4, Mesh, PlaneGeometry, ShaderMaterial } from "three"; +import { DoubleSide, Matrix4, Mesh, PlaneGeometry, type Scene, ShaderMaterial } from "three"; import constants, { OrthoViews } from "viewer/constants"; import getSceneController from "viewer/controller/scene_controller_provider"; import PlaneMaterialFactory from "viewer/geometries/materials/plane_material_factory"; diff --git a/frontend/javascripts/viewer/geometries/compute_split_boundary_mesh_with_splines.ts b/frontend/javascripts/viewer/geometries/compute_split_boundary_mesh_with_splines.ts index c481863b66a..f3d9bb988e4 100644 --- a/frontend/javascripts/viewer/geometries/compute_split_boundary_mesh_with_splines.ts +++ b/frontend/javascripts/viewer/geometries/compute_split_boundary_mesh_with_splines.ts @@ -10,6 +10,7 @@ import { MathUtils, Mesh, MeshStandardMaterial, + Object3D, Vector3 as ThreeVector3, } from "three"; import type { Vector3 } from "viewer/constants"; diff --git a/frontend/javascripts/viewer/geometries/crosshair.ts b/frontend/javascripts/viewer/geometries/crosshair.ts index 38f522e24ad..50760b42763 100644 --- a/frontend/javascripts/viewer/geometries/crosshair.ts +++ b/frontend/javascripts/viewer/geometries/crosshair.ts @@ -1,4 +1,13 @@ -import { DoubleSide, Group, Matrix4, Mesh, MeshBasicMaterial, RingGeometry, Vector3 } from "three"; +import { + DoubleSide, + Group, + Matrix4, + Mesh, + MeshBasicMaterial, + RingGeometry, + type Scene, + Vector3, +} from "three"; import { getZoomedMatrix } from "viewer/model/accessors/flycam_accessor"; import Store from "viewer/store"; diff --git a/frontend/javascripts/viewer/model/actions/ui_actions.ts b/frontend/javascripts/viewer/model/actions/ui_actions.ts index 09527eb23c4..82535986e34 100644 --- a/frontend/javascripts/viewer/model/actions/ui_actions.ts +++ b/frontend/javascripts/viewer/model/actions/ui_actions.ts @@ -1,4 +1,3 @@ -import type { Vector3 as ThreeVector3 } from "three"; import type { OrthoView, Vector3 } from "viewer/constants"; import type { AnnotationTool } from "viewer/model/accessors/tool_accessor"; import type { BorderOpenStatus, Theme, WebknossosState } from "viewer/store"; @@ -226,7 +225,7 @@ export const showContextMenuAction = ( globalPosition: Vector3 | null | undefined, viewport: OrthoView, meshId: number | null | undefined, - meshIntersectionPosition: ThreeVector3 | null | undefined, + meshIntersectionPosition: Vector3 | null | undefined, unmappedSegmentId: number | undefined | null, ) => ({ diff --git a/frontend/javascripts/viewer/model/bucket_data_handling/data_cube.ts b/frontend/javascripts/viewer/model/bucket_data_handling/data_cube.ts index f730b5fbb5b..370f2a0c1d5 100644 --- a/frontend/javascripts/viewer/model/bucket_data_handling/data_cube.ts +++ b/frontend/javascripts/viewer/model/bucket_data_handling/data_cube.ts @@ -1040,11 +1040,6 @@ class DataCube { return this.getVoxelIndexByVoxelOffset(voxelOffset); } - positionToZoomedAddress( - position: ThreeVector3, - additionalCoordinates: AdditionalCoordinate[] | null, - zoomStep: number, - ): BucketAddress; positionToZoomedAddress( position: Vector3, additionalCoordinates: AdditionalCoordinate[] | null, diff --git a/frontend/javascripts/viewer/model/reducers/flycam_reducer.ts b/frontend/javascripts/viewer/model/reducers/flycam_reducer.ts index 8a88c38afdb..5b351ecbb0c 100644 --- a/frontend/javascripts/viewer/model/reducers/flycam_reducer.ts +++ b/frontend/javascripts/viewer/model/reducers/flycam_reducer.ts @@ -100,7 +100,6 @@ function rotateReducer( }); } -export function getMatrixScale(voxelSize: ThreeVector3): Vector3; export function getMatrixScale(voxelSize: Vector3): Vector3 { const scale = [1 / voxelSize[0], 1 / voxelSize[1], 1 / voxelSize[2]]; const maxScale = Math.max(scale[0], scale[1], scale[2]); From 72dd22824a33bdf3c1f17109351022a24129ecfc Mon Sep 17 00:00:00 2001 From: Tom Herold Date: Thu, 17 Jul 2025 17:31:42 +0200 Subject: [PATCH 5/9] use three.js src files instead of prebuild binaries --- .../geometries/compute_split_boundary_mesh_with_splines.ts | 2 +- webpack.config.js | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/frontend/javascripts/viewer/geometries/compute_split_boundary_mesh_with_splines.ts b/frontend/javascripts/viewer/geometries/compute_split_boundary_mesh_with_splines.ts index f3d9bb988e4..edfd650593e 100644 --- a/frontend/javascripts/viewer/geometries/compute_split_boundary_mesh_with_splines.ts +++ b/frontend/javascripts/viewer/geometries/compute_split_boundary_mesh_with_splines.ts @@ -10,7 +10,7 @@ import { MathUtils, Mesh, MeshStandardMaterial, - Object3D, + type Object3D, Vector3 as ThreeVector3, } from "three"; import type { Vector3 } from "viewer/constants"; diff --git a/webpack.config.js b/webpack.config.js index 433ca1a8773..063c5153959 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -141,6 +141,7 @@ module.exports = function (env = {}) { modules: [srcPath, nodePath, protoPath], alias: { react: path.resolve("./node_modules/react"), + three: path.resolve(__dirname, "node_modules/three/src/Three.js") // build three js from source instead of using their prebuild "binaries" }, extensions: [".ts", ".tsx", ".js", ".json"], fallback: { @@ -210,3 +211,5 @@ module.exports = function (env = {}) { ], }; }; + + From 6229f6f2b42d224923d100a954aecc8691f01da0 Mon Sep 17 00:00:00 2001 From: Daniel Werner Date: Tue, 22 Jul 2025 15:33:24 +0200 Subject: [PATCH 6/9] Fix Color and OrtographicCamera instantiations --- .../viewer/model/bucket_data_handling/bucket.ts | 10 +++++----- frontend/javascripts/viewer/view/arbitrary_view.ts | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/frontend/javascripts/viewer/model/bucket_data_handling/bucket.ts b/frontend/javascripts/viewer/model/bucket_data_handling/bucket.ts index 11d51423577..fa8ba90d61d 100644 --- a/frontend/javascripts/viewer/model/bucket_data_handling/bucket.ts +++ b/frontend/javascripts/viewer/model/bucket_data_handling/bucket.ts @@ -739,11 +739,11 @@ export class DataBucket { const colors = [ new Color(0, 0, 0), - new Color(255, 0, 0), - new Color(0, 255, 0), - new Color(0, 0, 255), - new Color(255, 0, 255), - new Color(255, 255, 0), + new Color(1, 0, 0), + new Color(0, 1, 0), + new Color(0, 0, 1), + new Color(1, 0, 1), + new Color(1, 1, 0), ]; const zoomStep = getActiveMagIndexForLayer(Store.getState(), this.cube.layerName); diff --git a/frontend/javascripts/viewer/view/arbitrary_view.ts b/frontend/javascripts/viewer/view/arbitrary_view.ts index ba14ad64832..90441cc3092 100644 --- a/frontend/javascripts/viewer/view/arbitrary_view.ts +++ b/frontend/javascripts/viewer/view/arbitrary_view.ts @@ -59,7 +59,7 @@ class ArbitraryView { tdCamera.up = new Vector3(0, 0, -1); tdCamera.matrixAutoUpdate = true; this.tdCamera = tdCamera; - const dummyCamera = new OrthographicCamera(45, 1, 50, 1000); + const dummyCamera = new OrthographicCamera(0, 0, 0, 0); this.cameras = { TDView: tdCamera, PLANE_XY: dummyCamera, From 2e7713d3602c45b2205a812676329e7c7487cc22 Mon Sep 17 00:00:00 2001 From: Daniel Werner Date: Tue, 22 Jul 2025 15:54:00 +0200 Subject: [PATCH 7/9] Avoid creating a new LineMaterial every time a bounding box is hovered --- .../javascripts/viewer/geometries/cube.ts | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/frontend/javascripts/viewer/geometries/cube.ts b/frontend/javascripts/viewer/geometries/cube.ts index 3a34bed7939..e20ca4a173e 100644 --- a/frontend/javascripts/viewer/geometries/cube.ts +++ b/frontend/javascripts/viewer/geometries/cube.ts @@ -69,15 +69,17 @@ class Cube { } getLineMaterial() { - return this.isHighlighted - ? new LineBasicMaterial({ - color: Store.getState().uiInformation.theme === "light" ? 0xeeeeee : 0xffffff, - linewidth: this.lineWidth, - }) - : new LineBasicMaterial({ - color: this.color, - linewidth: this.lineWidth, - }); + return new LineBasicMaterial({ + color: this.getLineColor(), + linewidth: this.lineWidth, + }); + } + + getLineColor(): number { + if (this.isHighlighted) { + return Store.getState().uiInformation.theme === "light" ? 0xeeeeee : 0xffffff; + } + return this.color; } setCorners(min: Vector3, max: Vector3) { @@ -179,7 +181,10 @@ class Cube { this.isHighlighted = highlighted; this.getMeshes().forEach((mesh) => { - mesh.material = this.getLineMaterial(); + // @ts-ignore We don't use material arrays + const meshMaterial: LineBasicMaterial = mesh.material; + meshMaterial.color.setHex(this.getLineColor()); + meshMaterial.linewidth = this.lineWidth; }); app.vent.emit("rerender"); } From b81ff6459b501406aadf073148b593c5976f82a9 Mon Sep 17 00:00:00 2001 From: Daniel Werner Date: Tue, 22 Jul 2025 16:29:01 +0200 Subject: [PATCH 8/9] Apply some of CodeRabbitAI's suggestions --- frontend/javascripts/libs/stl_exporter.ts | 4 ++-- .../viewer/controller/scene_controller.ts | 2 +- .../viewer/geometries/arbitrary_plane.ts | 4 +++- .../viewer/geometries/crosshair.ts | 4 +++- .../javascripts/viewer/geometries/skeleton.ts | 6 +++--- .../model/bucket_data_handling/bucket.ts | 3 +-- .../model/sagas/skeletontracing_saga.ts | 4 ++-- .../javascripts/viewer/view/arbitrary_view.ts | 21 ++++++++++++------- .../viewer/view/rendering_utils.ts | 10 ++++----- 9 files changed, 34 insertions(+), 24 deletions(-) diff --git a/frontend/javascripts/libs/stl_exporter.ts b/frontend/javascripts/libs/stl_exporter.ts index bdaebbf6eb1..8ada67bf0c8 100644 --- a/frontend/javascripts/libs/stl_exporter.ts +++ b/frontend/javascripts/libs/stl_exporter.ts @@ -1,5 +1,5 @@ /* eslint-disable */ -import { BufferGeometry, Scene, Vector3 } from "three"; +import { Scene, Vector3 } from "three"; // Original Source: https://github.com/mrdoob/three.js/blob/master/examples/js/exporters/STLExporter.js // Manual changes: @@ -44,7 +44,7 @@ class STLExporter { const geometry = object.geometry; if (geometry.isBufferGeometry !== true) { - throw new Error("STLExporter: Geometry is not of type THREE.BufferGeometry."); + throw new Error("STLExporter: Geometry is not of type BufferGeometry."); } const index = geometry.index; diff --git a/frontend/javascripts/viewer/controller/scene_controller.ts b/frontend/javascripts/viewer/controller/scene_controller.ts index cc2c1f9c890..5ee7816be4d 100644 --- a/frontend/javascripts/viewer/controller/scene_controller.ts +++ b/frontend/javascripts/viewer/controller/scene_controller.ts @@ -164,7 +164,7 @@ class SceneController { position: Vector3, zoomStep: number, mag: Vector3, - optColor?: string, + optColor?: string | null | undefined, ) => { const bucketSize = [ constants.BUCKET_WIDTH * mag[0], diff --git a/frontend/javascripts/viewer/geometries/arbitrary_plane.ts b/frontend/javascripts/viewer/geometries/arbitrary_plane.ts index 7f734e97dc0..905164fd7ce 100644 --- a/frontend/javascripts/viewer/geometries/arbitrary_plane.ts +++ b/frontend/javascripts/viewer/geometries/arbitrary_plane.ts @@ -24,6 +24,8 @@ type ArbitraryMeshes = { debuggerPlane: Mesh | null | undefined; }; +const flipYRotationMatrix = new Matrix4().makeRotationY(Math.PI); + class ArbitraryPlane { meshes: ArbitraryMeshes; isDirty: boolean; @@ -82,7 +84,7 @@ class ArbitraryPlane { ); mesh.matrix.identity(); mesh.matrix.multiply(meshMatrix); - mesh.matrix.multiply(new Matrix4().makeRotationY(Math.PI)); + mesh.matrix.multiply(flipYRotationMatrix); mesh.matrixWorldNeedsUpdate = true; }; diff --git a/frontend/javascripts/viewer/geometries/crosshair.ts b/frontend/javascripts/viewer/geometries/crosshair.ts index 50760b42763..817667107f7 100644 --- a/frontend/javascripts/viewer/geometries/crosshair.ts +++ b/frontend/javascripts/viewer/geometries/crosshair.ts @@ -11,6 +11,8 @@ import { import { getZoomedMatrix } from "viewer/model/accessors/flycam_accessor"; import Store from "viewer/store"; +const flipYRotationMatrix = new Matrix4().makeRotationY(Math.PI); + class Crosshair { mesh: Group; WIDTH: number; @@ -56,7 +58,7 @@ class Crosshair { m[11], m[15], ); - mesh.matrix.multiply(new Matrix4().makeRotationY(Math.PI)); + mesh.matrix.multiply(flipYRotationMatrix); mesh.matrix.multiply(new Matrix4().makeTranslation(0, 0, 0.5)); mesh.matrix.scale(new Vector3(this.scale, this.scale, this.scale)); mesh.matrixWorldNeedsUpdate = true; diff --git a/frontend/javascripts/viewer/geometries/skeleton.ts b/frontend/javascripts/viewer/geometries/skeleton.ts index 5cdffb97755..582f6f9c5fc 100644 --- a/frontend/javascripts/viewer/geometries/skeleton.ts +++ b/frontend/javascripts/viewer/geometries/skeleton.ts @@ -38,7 +38,7 @@ type Buffer = { capacity: number; nextIndex: number; geometry: BufferGeometryWithBufferAttributes; - mesh: Object3D; + mesh: Points | LineSegments; }; type BufferPosition = { buffer: Buffer; @@ -71,7 +71,7 @@ const NodeBufferHelperType = { geometry.setAttribute("treeId", new BufferAttribute(new Float32Array(capacity), 1)); }, - buildMesh(geometry: BufferGeometry, material: RawShaderMaterial): Object3D { + buildMesh(geometry: BufferGeometry, material: RawShaderMaterial): Points { return new Points(geometry, material); }, @@ -93,7 +93,7 @@ const EdgeBufferHelperType = { geometry.setAttribute("treeId", new BufferAttribute(new Float32Array(capacity * 2), 1)); }, - buildMesh(geometry: BufferGeometry, material: RawShaderMaterial): Object3D { + buildMesh(geometry: BufferGeometry, material: RawShaderMaterial): LineSegments { return new LineSegments(geometry, material); }, diff --git a/frontend/javascripts/viewer/model/bucket_data_handling/bucket.ts b/frontend/javascripts/viewer/model/bucket_data_handling/bucket.ts index fa8ba90d61d..4f931934920 100644 --- a/frontend/javascripts/viewer/model/bucket_data_handling/bucket.ts +++ b/frontend/javascripts/viewer/model/bucket_data_handling/bucket.ts @@ -98,8 +98,7 @@ export class DataBucket { readonly elementClass: ElementClass; readonly zoomedAddress: BucketAddress; visualizedMesh: Record | null | undefined; - // @ts-expect-error ts-migrate(2564) FIXME: Property 'visualizationColor' has no initializer a... Remove this comment to see the full error message - visualizationColor: Color; + visualizationColor: Color | null | undefined; // If dirty, the bucket's data was potentially edited and needs to be // saved to the server. dirty: boolean; diff --git a/frontend/javascripts/viewer/model/sagas/skeletontracing_saga.ts b/frontend/javascripts/viewer/model/sagas/skeletontracing_saga.ts index 0f9285fae4a..a3844a72ecb 100644 --- a/frontend/javascripts/viewer/model/sagas/skeletontracing_saga.ts +++ b/frontend/javascripts/viewer/model/sagas/skeletontracing_saga.ts @@ -92,8 +92,8 @@ import { ensureWkReady } from "./ready_sagas"; import { takeWithBatchActionSupport } from "./saga_helpers"; function getNodeRotationWithoutPlaneRotation(activeNode: Readonly): Vector3 { - // In orthogonal view mode, this active planes default rotation is added to the flycam rotation upon node creation. - // To get the same flycam rotation as was active during node creation, the default rotation is calculated out from the nodes rotation. + // In orthogonal view mode, the active planes' default rotation is added to the flycam rotation upon node creation. + // To get the same flycam rotation as was active during node creation, the default rotation is calculated from the nodes rotation. const nodeRotationRadian = map3(MathUtils.degToRad, activeNode.rotation); const nodeRotationInReducerFormatMatrix = eulerAngleToReducerInternalMatrix(nodeRotationRadian); const viewportRotationMatrix = new Matrix4().makeRotationFromEuler( diff --git a/frontend/javascripts/viewer/view/arbitrary_view.ts b/frontend/javascripts/viewer/view/arbitrary_view.ts index 90441cc3092..586608c6a39 100644 --- a/frontend/javascripts/viewer/view/arbitrary_view.ts +++ b/frontend/javascripts/viewer/view/arbitrary_view.ts @@ -1,9 +1,15 @@ import app from "app"; import window from "libs/window"; import _ from "lodash"; -import { Matrix4, Object3D, OrthographicCamera, PerspectiveCamera, Vector3 } from "three"; +import { + Matrix4, + Object3D, + OrthographicCamera, + PerspectiveCamera, + Vector3 as ThreeVector3, +} from "three"; import TWEEN from "tween.js"; -import type { OrthoViewMap, Viewport } from "viewer/constants"; +import type { OrthoViewMap, Vector3, Viewport } from "viewer/constants"; import Constants, { ARBITRARY_CAM_DISTANCE, ArbitraryViewport, OrthoViews } from "viewer/constants"; import getSceneController, { getSceneControllerOrNull, @@ -23,6 +29,8 @@ type GeometryLike = { addToScene: (obj: Object3D) => void; }; +const flipYRotationMatrix = new Matrix4().makeRotationY(Math.PI); + class ArbitraryView { cameras: OrthoViewMap; // @ts-expect-error ts-migrate(2564) FIXME: Property 'plane' has no initializer and is not def... Remove this comment to see the full error message @@ -40,7 +48,7 @@ class ArbitraryView { geometries: Array = []; // @ts-expect-error ts-migrate(2564) FIXME: Property 'group' has no initializer and is not def... Remove this comment to see the full error message group: Object3D; - cameraPosition: Array; + cameraPosition: Vector3; unsubscribeFunctions: Array<() => void> = []; constructor() { @@ -55,8 +63,8 @@ class ArbitraryView { this.camera.matrixAutoUpdate = false; scene.add(this.camera); const tdCamera = new OrthographicCamera(0, 0, 0, 0); - tdCamera.position.copy(new Vector3(10, 10, -10)); - tdCamera.up = new Vector3(0, 0, -1); + tdCamera.position.copy(new ThreeVector3(10, 10, -10)); + tdCamera.up = new ThreeVector3(0, 0, -1); tdCamera.matrixAutoUpdate = true; this.tdCamera = tdCamera; const dummyCamera = new OrthographicCamera(0, 0, 0, 0); @@ -165,8 +173,7 @@ class ArbitraryView { m[2], m[6], m[10], m[14], m[3], m[7], m[11], m[15], ); - camera.matrix.multiply(new Matrix4().makeRotationY(Math.PI)); - // @ts-expect-error ts-migrate(2556) FIXME: Expected 3 arguments, but got 0 or more. + camera.matrix.multiply(flipYRotationMatrix); camera.matrix.multiply(new Matrix4().makeTranslation(...this.cameraPosition)); camera.matrixWorldNeedsUpdate = true; clearCanvas(renderer); diff --git a/frontend/javascripts/viewer/view/rendering_utils.ts b/frontend/javascripts/viewer/view/rendering_utils.ts index 4ad5b824d90..4bf07162503 100644 --- a/frontend/javascripts/viewer/view/rendering_utils.ts +++ b/frontend/javascripts/viewer/view/rendering_utils.ts @@ -69,19 +69,19 @@ export function renderToTexture( const isArbitraryMode = constants.MODES_ARBITRARY.includes( state.temporaryConfiguration.viewMode, ); - camera = camera.clone() as T; + const adaptedCamera = camera.clone() as T; // The near value is already set in the camera (done in the CameraController/ArbitraryView). if (isArbitraryMode) { // The far value has to be set, since in normal rendering the far clipping is // achieved by the data plane which is not rendered during node picking - camera.far = ARBITRARY_CAM_DISTANCE; + adaptedCamera.far = ARBITRARY_CAM_DISTANCE; } else { // The far value has to be set, since in normal rendering the far clipping is // achieved by offsetting the plane instead of setting the far property. - camera.far = state.userConfiguration.clippingDistance; + adaptedCamera.far = state.userConfiguration.clippingDistance; } - camera.updateProjectionMatrix(); - return camera; + adaptedCamera.updateProjectionMatrix(); + return adaptedCamera; } camera = adaptCameraToCurrentClippingDistance(camera); From 605998e5243383553c4158f1127c207b28ebcb22 Mon Sep 17 00:00:00 2001 From: Daniel Werner Date: Tue, 22 Jul 2025 16:32:21 +0200 Subject: [PATCH 9/9] Improve webpack config comment --- webpack.config.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/webpack.config.js b/webpack.config.js index 063c5153959..113255adfbf 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -141,7 +141,7 @@ module.exports = function (env = {}) { modules: [srcPath, nodePath, protoPath], alias: { react: path.resolve("./node_modules/react"), - three: path.resolve(__dirname, "node_modules/three/src/Three.js") // build three js from source instead of using their prebuild "binaries" + three: path.resolve(__dirname, "node_modules/three/src/Three.js"), // build three js from source instead of using their prebuilt "binaries" to allow for a smaller bundle size }, extensions: [".ts", ".tsx", ".js", ".json"], fallback: { @@ -211,5 +211,3 @@ module.exports = function (env = {}) { ], }; }; - -