@x-viewer/core 0.21.14 → 0.21.15

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.
@@ -119,6 +119,13 @@ export interface ModelConfig {
119
119
  * @default false
120
120
  */
121
121
  merge?: boolean;
122
+ /**
123
+ * When true, mesh geometry is quantized after load.
124
+ * When false, quantization is skipped.
125
+ * When omitted, {@link Viewer3d} uses {@link BaseViewer.enableQuantization};
126
+ * @hidden
127
+ */
128
+ quantize?: boolean;
122
129
  /**
123
130
  * Whether to generate and display edges/outlines for the model.
124
131
  * Useful for Viewer3d to show wireframe edges.
@@ -49,6 +49,29 @@ export declare const HighlightColorRgba: number[];
49
49
  * Object outline color.
50
50
  */
51
51
  export declare const OutlineColorRgba: number[];
52
+ /**
53
+ * 3D selection outline (EffectComposer OutlinePass) — saturated UI orange (gltfEditor-like).
54
+ * Composites after {@link OutputPass}; use display-referred sRGB.
55
+ */
56
+ export declare const SelectionOutlineVisibleColor = 16742144;
57
+ /**
58
+ * Hidden-edge tint for OutlinePass. Match {@link SelectionOutlineVisibleColor} so depth-classified “hidden”
59
+ * segments do not read as missing (closer to gltfeditor-style continuous orange).
60
+ */
61
+ export declare const SelectionOutlineHiddenColor = 16742144;
62
+ /**
63
+ * OutlinePass edge strength. Additive blend on bright backgrounds needs a higher value to stay visible.
64
+ */
65
+ export declare const SelectionOutlineEdgeStrength = 10;
66
+ /** No secondary blur halo (glow pass off). */
67
+ export declare const SelectionOutlineEdgeGlow = 0;
68
+ /** Blur radius in OutlinePass edge buffers — ~2–4 px at full internal resolution; see EffectManager pixel ratio. */
69
+ export declare const SelectionOutlineEdgeThickness = 3.5;
70
+ /**
71
+ * OutlinePass internal downsample (default in three.js is 2). Use 1 for sharper screen-space edges
72
+ * (slightly higher GPU cost).
73
+ */
74
+ export declare const SelectionOutlineDownSampleRatio = 1;
52
75
  /**
53
76
  * Default object opacity value.
54
77
  */
@@ -58,6 +58,32 @@ export declare class EffectManager {
58
58
  get unrealBloomPassEnabled(): boolean;
59
59
  set unrealBloomPassEnabled(enable: boolean);
60
60
  setOutlinePassSelectObjects(objects: THREE.Object3D[]): void;
61
+ /**
62
+ * Sets current selection outline objects.
63
+ * When objects is empty, the pass is disabled.
64
+ *
65
+ * Intended for 3D viewer selection highlighting; 2D callers should avoid using this.
66
+ */
67
+ setSelectionOutline(objects: THREE.Object3D[]): void;
68
+ /**
69
+ * OutlinePass is screen-space (depth/normal): not every geometric edge appears.
70
+ * Colors are sRGB display values (no HDR boost): outline runs after {@link OutputPass}, so it is not
71
+ * washed out by tone mapping.
72
+ */
73
+ private applySelectionOutlineAppearance;
74
+ /**
75
+ * The stock {@link OutlinePass} overlay material uses `AdditiveBlending`.
76
+ * Additive blend on a white background: `clamp(1 + x) = 1` — the outline is **mathematically invisible**.
77
+ *
78
+ * Replace the overlay with `NormalBlending` + a shader that outputs proper alpha so the edge color
79
+ * **covers** the background instead of being added to it. This works on any background brightness.
80
+ */
81
+ private patchOutlineOverlayBlending;
82
+ /**
83
+ * Internal buffers must use the same pixel dimensions as {@link EffectComposer} (logical size × DPR);
84
+ * otherwise on HiDPI screens edges are detected on an undersized grid and look ~1px thin and dim.
85
+ */
86
+ private outlinePassResolution;
61
87
  enableModelEdges(enable: boolean): void;
62
88
  showVertexNormals(show: boolean, size?: number): void;
63
89
  setSize(width: number, height: number): void;
@@ -74,8 +100,8 @@ export declare class EffectManager {
74
100
  private syncWatermarkResolution;
75
101
  private ensureComposerOutputPass;
76
102
  /**
77
- * Keep {@link composerOutputPass} last (screen output), and the GLSL watermark pass immediately before it
78
- * so it composites in linear space; OutputPass applies renderer tone mapping and output color space to the canvas.
103
+ * Order: …other passes… watermark (linear) {@link composerOutputPass} (tone map + output encoding)
104
+ * {@link outlinePass} when present (selection outline avoids tone mapping by compositing last).
79
105
  */
80
106
  private ensurePostprocessPassOrder;
81
107
  }
@@ -6,6 +6,11 @@ interface BaseModelLoadContext {
6
6
  /** Normalized lowercase format token; same semantics as historical `endsWith` checks. */
7
7
  readonly format: string;
8
8
  readonly onProgress?: (event: ProgressEvent) => void;
9
+ /**
10
+ * Per-load mesh quantization (see {@link ModelConfig.quantize}). When `false` or omitted on
11
+ * {@link ModelConfig}, loaders skip quantization; {@link Viewer3d} fills omission from {@link BaseViewer.enableQuantization}.
12
+ */
13
+ readonly quantize?: boolean;
9
14
  }
10
15
  /**
11
16
  * Context for model loading from URL.
@@ -43,7 +48,18 @@ export declare abstract class BaseModelLoader {
43
48
  * Override for non-suffix matching.
44
49
  */
45
50
  match(format: string): boolean;
51
+ /**
52
+ * When {@link ModelLoadContext.quantize} is strictly `true`, quantizes mesh
53
+ * {@link THREE.BufferGeometry} under `scene` (see {@link GeometryUtils.applyMeshQuantizationToSceneRoot}).
54
+ */
55
+ protected maybeQuantizeLoadedScene(scene: THREE.Object3D, quantize?: boolean): THREE.Object3D;
46
56
  load?(ctx: ModelLoadContext): Promise<THREE.Object3D | void>;
47
57
  loadFromBuffer?(ctx: ModelLoadFromBufferContext): Promise<THREE.Object3D | void>;
58
+ /**
59
+ * Called by ModelLoaderManager when the active LoadingManager changes
60
+ * (e.g. for multi-file local loads with a URL modifier).
61
+ * Implementations should reset any cached internal loader instances.
62
+ */
63
+ updateManager?(manager: THREE.LoadingManager | undefined): void;
48
64
  }
49
65
  export {};
@@ -5,17 +5,18 @@ import { BaseModelLoader } from "./BaseModelLoader";
5
5
  * glTF / GLB model loader wrapping GLTFLoader, DRACO path, and progress handling.
6
6
  */
7
7
  export declare class GltfLoader extends BaseModelLoader {
8
- private readonly manager?;
9
8
  readonly id = "loader.builtin.gltf";
10
9
  readonly priority = 0;
11
10
  readonly formats: readonly ["gltf", "glb"];
12
11
  private static dracoDecoderPath;
13
12
  static setDracoDecoderPath(path: string): void;
14
13
  private gltfLoader?;
15
- constructor(manager?: THREE.LoadingManager | undefined);
14
+ private manager?;
15
+ constructor(manager?: THREE.LoadingManager);
16
+ updateManager(manager: THREE.LoadingManager | undefined): void;
16
17
  load(ctx: ModelLoadContext): Promise<THREE.Object3D>;
17
18
  loadFromBuffer(ctx: ModelLoadFromBufferContext): Promise<THREE.Object3D>;
18
- parseToScene(data: ArrayBuffer | string, path: string, onLoad: (object: THREE.Object3D) => void, onError?: (event: ErrorEvent) => void): void;
19
+ parseToScene(data: ArrayBuffer | string, path: string, onLoad: (object: THREE.Object3D) => void, onError?: (event: ErrorEvent) => void, quantize?: boolean): void;
19
20
  private obtainLoader;
20
21
  private applyRequestHeader;
21
22
  private sanitizeUrlForLoad;
@@ -1,5 +1,6 @@
1
1
  import * as THREE from "three";
2
2
  import type { BaseModelLoader } from "./BaseModelLoader";
3
+ import type { ModelConfig } from "../../core/Configs";
3
4
  import type { FontManager } from "../../core/font";
4
5
  export declare class ModelLoaderManager {
5
6
  private manager?;
@@ -25,14 +26,20 @@ export declare class ModelLoaderManager {
25
26
  */
26
27
  getLoaderById(id: string): BaseModelLoader | undefined;
27
28
  private computeUrlModelFormat;
29
+ /**
30
+ * @param modelCfg Load-related fields from {@link ModelConfig} (`src` is the logical file name for
31
+ * format detection when `blobFetchUrl` is set).
32
+ * @param blobFetchUrl When set (e.g. `blob:` URL), requests use this URL while format is inferred from `modelCfg.src`.
33
+ */
28
34
  private loadModelInternal;
29
35
  private loadModelFromBufferInternal;
30
36
  setLoadManager(manager?: THREE.LoadingManager): void;
31
- loadLocalModel(url: string, src: string, onProgress?: (event: ProgressEvent) => void, mtlUrls?: string[]): Promise<THREE.Object3D>;
32
- loadModel(src: string, fileFormat?: string, onProgress?: (event: ProgressEvent) => void, requestHeader?: {
33
- [header: string]: string;
34
- }): Promise<THREE.Object3D | void>;
35
- loadModelFromBuffer(data: ArrayBuffer | Uint8Array, fileFormat: string, onProgress?: (event: ProgressEvent) => void): Promise<THREE.Object3D | void>;
37
+ /**
38
+ * @param blobUrl Fetch URL for the main file (often a `blob:` URL); {@link ModelConfig.src} should stay the real file name for format detection.
39
+ */
40
+ loadLocalModel(blobUrl: string, modelCfg: ModelConfig, onProgress?: (event: ProgressEvent) => void): Promise<THREE.Object3D>;
41
+ loadModel(modelCfg: ModelConfig, onProgress?: (event: ProgressEvent) => void, unsupportedMessage?: string): Promise<THREE.Object3D | void>;
42
+ loadModelFromBuffer(modelCfg: ModelConfig, onProgress?: (event: ProgressEvent) => void): Promise<THREE.Object3D | void>;
36
43
  /**
37
44
  * For dwg/dxf, we need to set font first.
38
45
  */
@@ -67,5 +67,50 @@ export declare class GeometryUtils {
67
67
  static convertWidthLineToMeshGeometry(points: THREE.Vector2[], width: number): THREE.BufferGeometry<THREE.NormalBufferAttributes, THREE.BufferGeometryEventMap> | undefined;
68
68
  static releaseGeometryManually(geometry: THREE.BufferGeometry): void;
69
69
  static getAdjacentNonRepeatPoints(points: THREE.Vector3[], epsilon?: number): THREE.Vector3[];
70
+ /**
71
+ * True when `attr` is a non-instanced per-vertex {@link THREE.BufferAttribute} or
72
+ * {@link THREE.InterleavedBufferAttribute} with `normalized: true` and an integer
73
+ * `TypedArray` backing (e.g. glTF normalized BYTE/SHORT or **KHR_mesh_quantization**).
74
+ * Use this to detect attributes already stored in normalized integer form (not only `position`).
75
+ */
76
+ static isNormalizedIntegerVertexAttribute(attr: unknown): boolean;
77
+ /**
78
+ * One axis of POSITION quantization: world coordinate → stored Int16 value in
79
+ * `[-32767, 32767]`, matching `KHR_mesh_quantization` with origin `center` and
80
+ * half-extent `halfExtent` on this axis.
81
+ */
82
+ static quantizePositionComponent(world: number, center: number, halfExtent: number): number;
83
+ /** Inverse of {@link GeometryUtils.quantizePositionComponent}: quantized → world units. */
84
+ static dequantizePositionComponent(quantized: number, center: number, halfExtent: number): number;
85
+ /**
86
+ * Quantizes geometry attributes in-place for KHR_mesh_quantization-style usage.
87
+ *
88
+ * Converts **position** (Float32 → Int16 normalized) when possible, and clears cached
89
+ * `boundingBox` / `boundingSphere` so world AABBs stay correct after baking the decode
90
+ * matrix into mesh transforms.
91
+ *
92
+ * If **position** already satisfies {@link GeometryUtils.isNormalizedIntegerVertexAttribute}
93
+ * (e.g. **KHR_mesh_quantization** from glTF), returns `null` and leaves the geometry unchanged
94
+ * (no double quantization).
95
+ *
96
+ * Other attributes (when present, non-instanced, backed by float arrays):
97
+ * **normal** → Int8 normalized, **uv** / **uv1–uv3** → Uint16 normalized ([0,1] only),
98
+ * **color** → Uint8 normalized ([0,1] only), **tangent** (vec4) → Int8 normalized.
99
+ * Does not touch **skinIndex** / **skinWeight** (skinning), morph targets, etc.
100
+ *
101
+ * @returns The position-decode `Matrix4` (`T(center) * S(halfExtents)`) when position was
102
+ * quantized, or `null` when position was left unchanged. Right-multiply into the
103
+ * mesh node matrix: `M_node_final = M_node_original * M_decode`.
104
+ */
105
+ static quantizeGeometry(geo: THREE.BufferGeometry): THREE.Matrix4 | null;
106
+ private static meshQuantMatScratch;
107
+ private static meshQuantInstanceTmp;
108
+ /**
109
+ * Walks `scene`, quantizes each unique mesh {@link THREE.BufferGeometry} (see
110
+ * {@link GeometryUtils.quantizeGeometry}), and bakes position decode into mesh
111
+ * transforms / instanced matrices (then syncs pq/s so {@link THREE.Object3D.matrixAutoUpdate}
112
+ * stays compatible with controls that edit position/quaternion/scale). Used after import.
113
+ */
114
+ static applyMeshQuantizationToSceneRoot(scene: THREE.Object3D): THREE.Object3D;
70
115
  static mergeBBoxes(bboxes: THREE.Box3[]): THREE.Box3[];
71
116
  }
@@ -29,6 +29,12 @@ export declare abstract class BaseViewer<BaseViewerEvents extends ViewerEvents =
29
29
  */
30
30
  loadedModels: Model[];
31
31
  protected inputManager: InputManager;
32
+ /**
33
+ * When true, mesh geometry is quantized after load for supported 3D formats (Viewer3d)
34
+ * when {@link ModelConfig.quantize} is omitted. Per-model `quantize` overrides this.
35
+ * Defaults to `false`; set at runtime (e.g. `viewer.enableQuantization = true`) or pass `quantize` on each {@link ModelConfig}.
36
+ */
37
+ enableQuantization: boolean;
32
38
  /**
33
39
  * Enables selecting an object
34
40
  */
@@ -84,6 +90,10 @@ export declare abstract class BaseViewer<BaseViewerEvents extends ViewerEvents =
84
90
  */
85
91
  enableRender: (time?: number) => void;
86
92
  resize(): void;
93
+ /**
94
+ * Fills {@link ModelConfig.quantize} from {@link enableQuantization} when omitted, without mutating `modelCfg`.
95
+ */
96
+ protected modelConfigForLoader(modelCfg: ModelConfig): ModelConfig;
87
97
  abstract loadModel(modelCfg: ModelConfig, onProgress?: (event: ProgressEvent) => void): Promise<void>;
88
98
  addModel(model: Model): void;
89
99
  removeModelById(modelId: string): void;
@@ -9,6 +9,11 @@ export declare class Viewer3d extends BaseViewer {
9
9
  loadedModels: Model3d[];
10
10
  private transformGizmo?;
11
11
  private transformGizmoCallbacks;
12
+ /**
13
+ * 3D selection set (supports multi-select). Order is insertion order.
14
+ * For legacy single-select APIs, the "highlighted object" is the last selected.
15
+ */
16
+ private selectedObjects;
12
17
  constructor(viewerCfg: Viewer3dConfig, homeView?: Viewpoint);
13
18
  is3d(): boolean;
14
19
  loadLocalModel(url: string, modelCfg: ModelConfig, manager?: THREE.LoadingManager, onProgress?: (event: ProgressEvent) => void): Promise<void>;
@@ -105,6 +110,17 @@ export declare class Viewer3d extends BaseViewer {
105
110
  */
106
111
  getHighlightedObject(): THREE.Object3D | undefined;
107
112
  clearHighlight(): void;
113
+ /**
114
+ * Adds an object to the current selection set (multi-select).
115
+ * Does not clear existing selection.
116
+ */
117
+ addObjectToSelection(object: THREE.Object3D): void;
118
+ /** Removes an object from current selection set (multi-select). */
119
+ removeObjectFromSelection(object: THREE.Object3D): void;
120
+ /** Returns a shallow copy of current selection set (multi-select). */
121
+ getSelectedObjects(): THREE.Object3D[];
122
+ /** True when {@link object} is in the current selection set. */
123
+ isObjectSelected(object: THREE.Object3D): boolean;
108
124
  setDoubleSidedMaterials(): void;
109
125
  revertDoubleSidedMaterials(): void;
110
126
  clearSelection(): void;
@@ -105,5 +105,10 @@ export declare enum ViewerEvent {
105
105
  /**
106
106
  * Triggered when selected object changed.
107
107
  */
108
- ObjectDeselected = "objectdeselected"
108
+ ObjectDeselected = "objectdeselected",
109
+ /**
110
+ * Triggered when a selected object's transform (position/rotation/scale) is changed,
111
+ * either by the transform gizmo or programmatically.
112
+ */
113
+ ObjectTransformed = "objecttransformed"
109
114
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@x-viewer/core",
3
- "version": "0.21.14",
3
+ "version": "0.21.15",
4
4
  "homepage": "https://dwg.thingraph.site/",
5
5
  "description": "WebGL-based 2D/3D viewer engine built on Three.js. View DWG/DXF/PDF files in the browser with no server required. Supports glTF, OBJ, FBX, IFC, STL and BimTiles formats.",
6
6
  "author": "Thingraph",
@@ -70,7 +70,7 @@
70
70
  "2d-polygon-boolean": "^1.0.1",
71
71
  "2d-polygon-self-intersections": "^1.3.1",
72
72
  "3d-tiles-renderer": "0.3.32",
73
- "@mlightcad/libredwg-web": "^0.6.7",
73
+ "@mlightcad/libredwg-web": "^0.7.1",
74
74
  "camera-controls": "^1.37.4",
75
75
  "clipper-lib": "^6.4.2",
76
76
  "concaveman": "^1.2.1",