@inweb/viewer-three 26.6.1 → 26.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,10 +2,16 @@ import { draggersRegistry, commandsRegistry, componentsRegistry, Loader, loaders
2
2
 
3
3
  export * from "@inweb/viewer-core";
4
4
 
5
- import { Line, Vector3, BufferGeometry, Float32BufferAttribute, LineBasicMaterial, Mesh, MeshBasicMaterial, DoubleSide, EventDispatcher, MOUSE, TOUCH, Spherical, Quaternion, Vector2, Plane, Object3D, Matrix4, Vector4, Raycaster, Controls, Clock, Box3, Sphere, MathUtils, Color, AmbientLight, DirectionalLight, HemisphereLight, OrthographicCamera, CylinderGeometry, Sprite, CanvasTexture, SRGBColorSpace, SpriteMaterial, LoadingManager, LoaderUtils, Scene, PerspectiveCamera, WebGLRenderer, LinearToneMapping } from "three";
5
+ import { Line, Vector3, BufferGeometry, Float32BufferAttribute, LineBasicMaterial, Mesh, MeshBasicMaterial, DoubleSide, EventDispatcher, MOUSE, TOUCH, Spherical, Quaternion, Vector2, Plane, Object3D, Matrix4, Vector4, Raycaster, Controls, Clock, Box3, Sphere, MathUtils, Color, AmbientLight, DirectionalLight, HemisphereLight, EdgesGeometry, OrthographicCamera, CylinderGeometry, Sprite, CanvasTexture, SRGBColorSpace, SpriteMaterial, LoadingManager, LoaderUtils, Scene, PerspectiveCamera, WebGLRenderer, LinearToneMapping } from "three";
6
6
 
7
7
  import { TransformControls } from "three/examples/jsm/controls/TransformControls.js";
8
8
 
9
+ import { LineSegmentsGeometry } from "three/examples/jsm/lines/LineSegmentsGeometry.js";
10
+
11
+ import { Wireframe } from "three/examples/jsm/lines/Wireframe.js";
12
+
13
+ import { LineMaterial } from "three/examples/jsm/lines/LineMaterial.js";
14
+
9
15
  import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
10
16
 
11
17
  import { EventEmitter2 } from "@inweb/eventemitter2";
@@ -1660,31 +1666,22 @@ class SelectionComponent {
1660
1666
  if (event.button !== 0) return;
1661
1667
  this.viewer.executeCommand("zoomToSelected");
1662
1668
  };
1663
- this.optionsChange = () => {
1664
- const {facesColor: facesColor, facesTransparancy: facesTransparancy} = this.viewer.options;
1665
- this.facesMaterial.color.setRGB(facesColor.r / 255, facesColor.g / 255, facesColor.b / 255);
1666
- this.facesMaterial.opacity = (255 - facesTransparancy) / 255;
1667
- this.viewer.update();
1669
+ this.initHighlighter = () => {
1670
+ this.highlighter = this.viewer.getComponent("HighlighterComponent");
1668
1671
  };
1669
1672
  this.viewer = viewer;
1670
1673
  this.raycaster = new Raycaster;
1671
1674
  this.downPosition = new Vector2;
1672
- const {facesColor: facesColor, facesTransparancy: facesTransparancy} = this.viewer.options;
1673
- this.facesMaterial = new MeshBasicMaterial;
1674
- this.facesMaterial.color.setRGB(facesColor.r / 255, facesColor.g / 255, facesColor.b / 255);
1675
- this.facesMaterial.opacity = (255 - facesTransparancy) / 255;
1676
- this.facesMaterial.transparent = true;
1677
1675
  this.viewer.addEventListener("pointerdown", this.onPointerDown);
1678
1676
  this.viewer.addEventListener("pointerup", this.onPointerUp);
1679
1677
  this.viewer.addEventListener("dblclick", this.onDoubleClick);
1680
- this.viewer.addEventListener("optionschange", this.optionsChange);
1678
+ this.viewer.addEventListener("initialize", this.initHighlighter);
1681
1679
  }
1682
1680
  dispose() {
1683
- this.facesMaterial.dispose();
1684
1681
  this.viewer.removeEventListener("pointerdown", this.onPointerDown);
1685
1682
  this.viewer.removeEventListener("pointerup", this.onPointerUp);
1686
1683
  this.viewer.removeEventListener("dblclick", this.onDoubleClick);
1687
- this.viewer.removeEventListener("optionschange", this.optionsChange);
1684
+ this.viewer.removeEventListener("initialize", this.initHighlighter);
1688
1685
  }
1689
1686
  getMousePosition(event, target) {
1690
1687
  return target.set(event.clientX, event.clientY);
@@ -1716,14 +1713,13 @@ class SelectionComponent {
1716
1713
  select(object) {
1717
1714
  if (object.isSelected) return;
1718
1715
  object.isSelected = true;
1719
- object.originalMaterial = object.material;
1720
- object.material = this.facesMaterial;
1716
+ this.highlighter.highlight(object);
1721
1717
  this.viewer.selected.push(object);
1722
1718
  }
1723
1719
  clearSelection() {
1724
1720
  this.viewer.selected.forEach((object => {
1725
1721
  object.isSelected = false;
1726
- object.material = object.originalMaterial;
1722
+ this.highlighter.unhighlight(object);
1727
1723
  }));
1728
1724
  this.viewer.selected.length = 0;
1729
1725
  }
@@ -2119,6 +2115,149 @@ class ResizeCanvasComponent {
2119
2115
  }
2120
2116
  }
2121
2117
 
2118
+ class HighlighterUtils {
2119
+ static isBreak(positions, i) {
2120
+ return isNaN(positions[i]) || isNaN(positions[i + 1]) || isNaN(positions[i + 2]) || positions[i] === Infinity || positions[i] === -Infinity || positions[i + 1] === Infinity || positions[i + 1] === -Infinity || positions[i + 2] === Infinity || positions[i + 2] === -Infinity;
2121
+ }
2122
+ static fromIndexedLine(positions, indices) {
2123
+ const lineGeometry = new LineSegmentsGeometry;
2124
+ const segments = [];
2125
+ for (let i = 0; i < indices.length; i += 2) {
2126
+ const idx1 = indices[i] * 3;
2127
+ const idx2 = indices[i + 1] * 3;
2128
+ if (indices[i] === -1 || indices[i + 1] === -1) {
2129
+ continue;
2130
+ }
2131
+ segments.push(positions[idx1], positions[idx1 + 1], positions[idx1 + 2], positions[idx2], positions[idx2 + 1], positions[idx2 + 2]);
2132
+ }
2133
+ if (segments.length === 0) return null;
2134
+ lineGeometry.setPositions(segments);
2135
+ return lineGeometry;
2136
+ }
2137
+ static fromNonIndexedLine(positions, isLineSegments) {
2138
+ const lineGeometry = new LineSegmentsGeometry;
2139
+ const segments = [];
2140
+ if (isLineSegments) {
2141
+ for (let i = 0; i < positions.length; i += 6) {
2142
+ if (i + 5 >= positions.length) break;
2143
+ if (HighlighterUtils.isBreak(positions, i) || HighlighterUtils.isBreak(positions, i + 3)) continue;
2144
+ segments.push(positions[i], positions[i + 1], positions[i + 2], positions[i + 3], positions[i + 4], positions[i + 5]);
2145
+ }
2146
+ } else {
2147
+ let lastValidIndex = -1;
2148
+ for (let i = 0; i < positions.length; i += 3) {
2149
+ if (HighlighterUtils.isBreak(positions, i)) {
2150
+ lastValidIndex = -1;
2151
+ continue;
2152
+ }
2153
+ if (lastValidIndex !== -1) {
2154
+ segments.push(positions[lastValidIndex], positions[lastValidIndex + 1], positions[lastValidIndex + 2], positions[i], positions[i + 1], positions[i + 2]);
2155
+ }
2156
+ lastValidIndex = i;
2157
+ }
2158
+ }
2159
+ if (segments.length === 0) return null;
2160
+ lineGeometry.setPositions(segments);
2161
+ return lineGeometry;
2162
+ }
2163
+ }
2164
+
2165
+ class HighlighterComponent {
2166
+ constructor(viewer) {
2167
+ this.geometryEnd = () => {
2168
+ const {facesColor: facesColor, facesTransparancy: facesTransparancy, edgesColor: edgesColor} = this.viewer.options;
2169
+ this.highlightMaterial = new MeshBasicMaterial({
2170
+ color: new Color(facesColor.r / 255, facesColor.g / 255, facesColor.b / 255),
2171
+ transparent: true,
2172
+ opacity: (255 - facesTransparancy) / 255,
2173
+ depthTest: false,
2174
+ depthWrite: false
2175
+ });
2176
+ this.outlineMaterial = new LineMaterial({
2177
+ color: new Color(edgesColor.r / 255, edgesColor.g / 255, edgesColor.b / 255),
2178
+ linewidth: 1.5,
2179
+ depthTest: false,
2180
+ depthWrite: false,
2181
+ resolution: new Vector2(window.innerWidth, window.innerHeight)
2182
+ });
2183
+ this.highlightLineMaterial = new LineBasicMaterial({
2184
+ color: new Color(facesColor.r / 255, facesColor.g / 255, facesColor.b / 255),
2185
+ depthTest: false,
2186
+ depthWrite: false
2187
+ });
2188
+ this.highlightLineGlowMaterial = new LineMaterial({
2189
+ color: new Color(facesColor.r / 255, facesColor.g / 255, facesColor.b / 255),
2190
+ linewidth: 5,
2191
+ transparent: true,
2192
+ opacity: .8,
2193
+ depthTest: true,
2194
+ depthWrite: true,
2195
+ resolution: new Vector2(window.innerWidth, window.innerHeight)
2196
+ });
2197
+ };
2198
+ this.optionsChange = () => {
2199
+ const {facesColor: facesColor, facesTransparancy: facesTransparancy, edgesColor: edgesColor} = this.viewer.options;
2200
+ this.highlightMaterial.color.setRGB(facesColor.r / 255, facesColor.g / 255, facesColor.b / 255);
2201
+ this.highlightMaterial.opacity = (255 - facesTransparancy) / 255;
2202
+ this.outlineMaterial.color.setRGB(edgesColor.r / 255, edgesColor.g / 255, edgesColor.b / 255);
2203
+ this.highlightLineMaterial.color.setRGB(facesColor.r / 255, facesColor.g / 255, facesColor.b / 255);
2204
+ this.highlightLineGlowMaterial.color.setRGB(facesColor.r / 255, facesColor.g / 255, facesColor.b / 255);
2205
+ this.viewer.update();
2206
+ };
2207
+ this.viewer = viewer;
2208
+ this.viewer.addEventListener("databasechunk", this.geometryEnd);
2209
+ this.viewer.addEventListener("optionschange", this.optionsChange);
2210
+ this.viewer.addEventListener("resize", this.viewerResize);
2211
+ this.geometryEnd();
2212
+ }
2213
+ dispose() {
2214
+ this.viewer.removeEventListener("databasechunk", this.geometryEnd);
2215
+ this.viewer.removeEventListener("optionschange", this.optionsChange);
2216
+ this.viewer.removeEventListener("resize", this.viewerResize);
2217
+ }
2218
+ highlight(object) {
2219
+ if (object.isHighlighted) return;
2220
+ if (object.isLine || object.isLineSegments) {
2221
+ const positions = object.geometry.attributes.position.array;
2222
+ const indices = object.geometry.index ? object.geometry.index.array : null;
2223
+ const lineGeometry = indices ? HighlighterUtils.fromIndexedLine(positions, indices) : HighlighterUtils.fromNonIndexedLine(positions, object.isLineSegments);
2224
+ const wireframe = new Wireframe(lineGeometry, this.highlightLineGlowMaterial);
2225
+ wireframe.position.copy(object.position);
2226
+ wireframe.rotation.copy(object.rotation);
2227
+ wireframe.scale.copy(object.scale);
2228
+ object.parent.add(wireframe);
2229
+ object.userData.highlightwireframe = wireframe;
2230
+ object.userData.originalMaterial = object.material;
2231
+ object.material = this.highlightLineMaterial;
2232
+ object.isHighlighted = true;
2233
+ } else if (object.isMesh) {
2234
+ const edgesGeometry = new EdgesGeometry(object.geometry, 30);
2235
+ const lineGeometry = (new LineSegmentsGeometry).fromEdgesGeometry(edgesGeometry);
2236
+ const wireframe = new Wireframe(lineGeometry, this.outlineMaterial);
2237
+ wireframe.position.copy(object.position);
2238
+ wireframe.rotation.copy(object.rotation);
2239
+ wireframe.scale.copy(object.scale);
2240
+ object.parent.add(wireframe);
2241
+ object.userData.highlightwireframe = wireframe;
2242
+ object.userData.originalMaterial = object.material;
2243
+ object.material = this.highlightMaterial;
2244
+ object.isHighlighted = true;
2245
+ }
2246
+ }
2247
+ unhighlight(object) {
2248
+ if (!object.isHighlighted) return;
2249
+ object.isHighlighted = false;
2250
+ object.material = object.userData.originalMaterial;
2251
+ object.userData.highlightwireframe.removeFromParent();
2252
+ delete object.userData.originalMaterial;
2253
+ delete object.userData.highlightwireframe;
2254
+ }
2255
+ viewerResize(event) {
2256
+ if (!this.outlineMaterial) return;
2257
+ this.outlineMaterial.resolution.set(event.width, event.height);
2258
+ }
2259
+ }
2260
+
2122
2261
  class WCSHelper extends Object3D {
2123
2262
  constructor(camera) {
2124
2263
  super();
@@ -2238,6 +2377,8 @@ components.registerComponent("ResizeCanvasComponent", (viewer => new ResizeCanva
2238
2377
 
2239
2378
  components.registerComponent("RenderLoopComponent", (viewer => new RenderLoopComponent(viewer)));
2240
2379
 
2380
+ components.registerComponent("HighlighterComponent", (viewer => new HighlighterComponent(viewer)));
2381
+
2241
2382
  components.registerComponent("SelectionComponent", (viewer => new SelectionComponent(viewer)));
2242
2383
 
2243
2384
  components.registerComponent("WCSHelperComponent", (viewer => new WCSHelperComponent(viewer)));
@@ -2274,30 +2415,30 @@ class GLTFLoadingManager extends LoadingManager {
2274
2415
  }
2275
2416
  }
2276
2417
 
2277
- class GLTFModelLoader extends Loader {
2418
+ class GLTFFileLoader extends Loader {
2278
2419
  constructor(viewer) {
2279
2420
  super();
2280
2421
  this.viewer = viewer;
2281
2422
  }
2282
- isSupport(model) {
2283
- return typeof model === "object" && typeof model.database === "string" && typeof model.downloadResource === "function" && /.gltf$/i.test(model.database);
2423
+ isSupport(file, format) {
2424
+ return (typeof file === "string" || file instanceof globalThis.File || file instanceof ArrayBuffer) && /(gltf|glb)$/i.test(format);
2284
2425
  }
2285
- async load(model) {
2286
- const url = `${model.httpClient.serverUrl}${model.path}/${model.database}`;
2287
- const manager = new GLTFLoadingManager(url);
2426
+ async load(file, format, params) {
2427
+ const manager = new GLTFLoadingManager(file, params);
2288
2428
  const loader = new GLTFLoader(manager);
2289
- loader.setRequestHeader(model.httpClient.headers);
2429
+ loader.setPath(manager.path);
2430
+ loader.setCrossOrigin(params.crossOrigin || loader.crossOrigin);
2431
+ loader.setWithCredentials(params.withCredentials || loader.withCredentials);
2290
2432
  const progress = event => {
2291
2433
  const {lengthComputable: lengthComputable, loaded: loaded, total: total} = event;
2292
2434
  const progress = lengthComputable ? loaded / total : 1;
2293
2435
  this.viewer.emitEvent({
2294
2436
  type: "geometryprogress",
2295
2437
  data: progress,
2296
- file: model.file,
2297
- model: model
2438
+ file: file
2298
2439
  });
2299
2440
  };
2300
- const gltf = await loader.loadAsync(url, progress);
2441
+ const gltf = await loader.loadAsync(manager.fileURL, progress);
2301
2442
  if (!this.viewer.scene) return this;
2302
2443
  this.viewer.scene.add(gltf.scene);
2303
2444
  this.viewer.models.push(gltf.scene);
@@ -2307,37 +2448,36 @@ class GLTFModelLoader extends Loader {
2307
2448
  this.viewer.emitEvent({
2308
2449
  type: "databasechunk",
2309
2450
  data: gltf.scene,
2310
- file: model.file,
2311
- model: model
2451
+ file: file
2312
2452
  });
2313
2453
  return this;
2314
2454
  }
2315
2455
  }
2316
2456
 
2317
- class GLTFFileLoader extends Loader {
2457
+ class GLTFCloudModelLoader extends Loader {
2318
2458
  constructor(viewer) {
2319
2459
  super();
2320
2460
  this.viewer = viewer;
2321
2461
  }
2322
- isSupport(file, format) {
2323
- return (typeof file === "string" || file instanceof globalThis.File || file instanceof ArrayBuffer) && /(gltf|glb)$/i.test(format);
2462
+ isSupport(model) {
2463
+ return typeof model === "object" && typeof model.database === "string" && typeof model.downloadResource === "function" && /.gltf$/i.test(model.database);
2324
2464
  }
2325
- async load(buffer, format, params) {
2326
- const manager = new GLTFLoadingManager(buffer, params);
2465
+ async load(model) {
2466
+ const url = `${model.httpClient.serverUrl}${model.path}/${model.database}`;
2467
+ const manager = new GLTFLoadingManager(url);
2327
2468
  const loader = new GLTFLoader(manager);
2328
- loader.setPath(manager.path);
2329
- loader.setCrossOrigin(params.crossOrigin || loader.crossOrigin);
2330
- loader.setWithCredentials(params.withCredentials || loader.withCredentials);
2469
+ loader.setRequestHeader(model.httpClient.headers);
2331
2470
  const progress = event => {
2332
2471
  const {lengthComputable: lengthComputable, loaded: loaded, total: total} = event;
2333
2472
  const progress = lengthComputable ? loaded / total : 1;
2334
2473
  this.viewer.emitEvent({
2335
2474
  type: "geometryprogress",
2336
2475
  data: progress,
2337
- file: buffer
2476
+ file: model.file,
2477
+ model: model
2338
2478
  });
2339
2479
  };
2340
- const gltf = await loader.loadAsync(manager.fileURL, progress);
2480
+ const gltf = await loader.loadAsync(url, progress);
2341
2481
  if (!this.viewer.scene) return this;
2342
2482
  this.viewer.scene.add(gltf.scene);
2343
2483
  this.viewer.models.push(gltf.scene);
@@ -2347,7 +2487,8 @@ class GLTFFileLoader extends Loader {
2347
2487
  this.viewer.emitEvent({
2348
2488
  type: "databasechunk",
2349
2489
  data: gltf.scene,
2350
- file: buffer
2490
+ file: model.file,
2491
+ model: model
2351
2492
  });
2352
2493
  return this;
2353
2494
  }
@@ -2355,10 +2496,10 @@ class GLTFFileLoader extends Loader {
2355
2496
 
2356
2497
  const loaders = loadersRegistry("threejs");
2357
2498
 
2358
- loaders.registerLoader("gltf", (viewer => new GLTFModelLoader(viewer)));
2359
-
2360
2499
  loaders.registerLoader("gltf-file", (viewer => new GLTFFileLoader(viewer)));
2361
2500
 
2501
+ loaders.registerLoader("gltf-cloud-model", (viewer => new GLTFCloudModelLoader(viewer)));
2502
+
2362
2503
  class Viewer extends EventEmitter2 {
2363
2504
  constructor(client) {
2364
2505
  super();
@@ -2436,24 +2577,24 @@ class Viewer extends EventEmitter2 {
2436
2577
  }
2437
2578
  dispose() {
2438
2579
  this.cancel();
2580
+ this.clear();
2439
2581
  this.emitEvent({
2440
2582
  type: "dispose"
2441
2583
  });
2584
+ this.removeAllListeners();
2585
+ this.setActiveDragger();
2442
2586
  this._components.forEach((component => component.dispose()));
2443
2587
  this._components = [];
2444
- this.setActiveDragger();
2445
- this.removeAllListeners();
2446
- this.clear();
2447
2588
  this._markup.dispose();
2448
2589
  if (this.canvas) {
2449
2590
  this.canvasEvents.forEach((x => this.canvas.removeEventListener(x, this.canvaseventlistener)));
2450
2591
  this.canvas = undefined;
2451
2592
  }
2452
2593
  if (this.renderer) this.renderer.dispose();
2594
+ this.helpers = undefined;
2595
+ this.scene = undefined;
2453
2596
  this.renderer = undefined;
2454
2597
  this.camera = undefined;
2455
- this.scene = undefined;
2456
- this.helpers = undefined;
2457
2598
  return this;
2458
2599
  }
2459
2600
  isInitialized() {
@@ -2670,6 +2811,9 @@ class Viewer extends EventEmitter2 {
2670
2811
  this.setActiveDragger(dragger.name);
2671
2812
  }
2672
2813
  }
2814
+ getComponent(name) {
2815
+ return this._components.find((component => component.name === name));
2816
+ }
2673
2817
  is3D() {
2674
2818
  return true;
2675
2819
  }
@@ -2715,9 +2859,6 @@ class Viewer extends EventEmitter2 {
2715
2859
  executeCommand(id, ...args) {
2716
2860
  return commands.executeCommand(id, this, ...args);
2717
2861
  }
2718
- getComponent(name) {
2719
- return this._components.find((component => component.name === name));
2720
- }
2721
2862
  drawViewpoint(viewpoint) {
2722
2863
  var _a, _b, _c;
2723
2864
  if (!this.renderer) return;