@giszhc/three-model-viewer 0.0.0-stage → 1.0.0

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.
@@ -0,0 +1,69 @@
1
+ import { ModelLoadResult, ModelDisplayStyle } from '../types';
2
+ import * as THREE from 'three';
3
+ export interface ManagedMeshMaterial {
4
+ mesh: THREE.Mesh;
5
+ originalMaterial: THREE.Material | THREE.Material[];
6
+ plainMaterial: THREE.Material;
7
+ }
8
+ export declare class ModelLoader {
9
+ private currentZipContent;
10
+ private managedMaterials;
11
+ private isTextureEnabled;
12
+ private displayStyle;
13
+ private plainColor;
14
+ private currentClippingPlanes;
15
+ /**
16
+ * Load model from a ZIP file containing .obj, .mtl, and image textures
17
+ */
18
+ loadZip(source: File | Blob | ArrayBuffer | string, onProgress?: (percent: number, message: string) => void): Promise<ModelLoadResult>;
19
+ /**
20
+ * Load model directly from OBJ and optional MTL URLs
21
+ */
22
+ loadOBJ(objUrl: string, mtlUrl?: string, onProgress?: (percent: number, message: string) => void): Promise<ModelLoadResult>;
23
+ /**
24
+ * Post-process loaded model:
25
+ * - Build BVH acceleration trees on all meshes
26
+ * - Count vertices & faces
27
+ * - Cache original materials and prepare clay/white-model materials
28
+ * - Calculate bounding box and center
29
+ */
30
+ postProcessModel(group: THREE.Group): ModelLoadResult;
31
+ /**
32
+ * Internal helper to apply display style (solid/wireframe) to a managed mesh
33
+ */
34
+ private applyStyleToItem;
35
+ /**
36
+ * Set model display style: 'solid' (实体), 'wireframe' (线框)
37
+ */
38
+ setDisplayStyle(style: ModelDisplayStyle): void;
39
+ getDisplayStyle(): ModelDisplayStyle;
40
+ /**
41
+ * Toggle texture on/off
42
+ */
43
+ toggleTexture(showTexture: boolean): void;
44
+ getTextureState(): boolean;
45
+ /**
46
+ * Set color for non-texture display (defaults to Digital3DCloud standard CAD blue 0x4c8cd6)
47
+ */
48
+ setPlainColor(color: THREE.ColorRepresentation): void;
49
+ getPlainColor(): number;
50
+ /**
51
+ * Retrieves the maximum original texture resolution (width & height) from the model's materials
52
+ */
53
+ getModelTextureSize(): {
54
+ width: number;
55
+ height: number;
56
+ } | null;
57
+ /**
58
+ * Retrieve all meshes of the model for raycasting and snapping
59
+ */
60
+ getMeshes(): THREE.Mesh[];
61
+ /**
62
+ * Apply clipping planes to all materials
63
+ */
64
+ applyClippingPlanes(planes: THREE.Plane[]): void;
65
+ /**
66
+ * Clean up resources
67
+ */
68
+ cleanup(): void;
69
+ }
@@ -0,0 +1,14 @@
1
+ import * as THREE from 'three';
2
+ export interface ExtractedZipContent {
3
+ objContent: string | ArrayBuffer;
4
+ objFileName: string;
5
+ mtlContent: string | null;
6
+ mtlFileName: string | null;
7
+ blobUrls: Map<string, string>;
8
+ loadingManager: THREE.LoadingManager;
9
+ dispose: () => void;
10
+ }
11
+ export type ProgressCallback = (percent: number, message: string) => void;
12
+ export declare class ZipExtractor {
13
+ static extract(source: File | Blob | ArrayBuffer | string, onProgress?: ProgressCallback): Promise<ExtractedZipContent>;
14
+ }
package/dist/style.css ADDED
@@ -0,0 +1,2 @@
1
+ .gmv-css2d-label{pointer-events:none;-webkit-user-select:none;user-select:none;z-index:10;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif}.gmv-measure-badge{-webkit-backdrop-filter:blur(8px);color:#fff;white-space:nowrap;background:#121621e0;border:1px solid #ffffff2e;border-radius:6px;align-items:center;gap:6px;padding:4px 10px;font-size:12px;font-weight:500;transition:all .2s;display:inline-flex;transform:translate(-50%,-50%);box-shadow:0 4px 14px #00000059}.gmv-measure-badge.distance{border-left:3px solid #3b82f6}.gmv-measure-badge.height{border-left:3px solid #10b981}.gmv-measure-badge.area{border-left:3px solid #f59e0b}.gmv-measure-badge.volume{border-left:3px solid #8b5cf6}.gmv-measure-badge.angle{border-left:3px solid #ec4899}.gmv-volume-badge{-webkit-backdrop-filter:blur(10px);color:#fff;white-space:nowrap;pointer-events:none;background:#121621eb;border:1px solid #8b5cf666;border-left:3px solid #8b5cf6;border-radius:8px;flex-direction:column;align-items:center;gap:3px;padding:6px 12px;font-size:12px;font-weight:500;display:inline-flex;transform:translate(-50%,-100%);box-shadow:0 6px 20px #00000073}.gmv-dimension-badge{-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);color:#e2e8f0;letter-spacing:.3px;pointer-events:none;background:#141824d9;border:1px solid #ffffff26;border-radius:4px;align-items:center;gap:4px;padding:2px 8px;font-size:11px;font-weight:600;display:inline-flex;transform:translate(-50%,-50%);box-shadow:0 2px 8px #0000004d}.gmv-dimension-badge.dim-x{color:#f87171;border-color:#ef444466}.gmv-dimension-badge.dim-y{color:#34d399;border-color:#10b98166}.gmv-dimension-badge.dim-z{color:#60a5fa;border-color:#3b82f666}[data-theme=light] .gmv-dimension-badge{color:#1e293b;background:#ffffffeb;border-color:#00000026;box-shadow:0 4px 12px #0000001a}[data-theme=light] .gmv-measure-badge{color:#0f172a;background:#fffffff0;border-color:#00000026;box-shadow:0 6px 18px #0000001f}.gmv-interaction-tip{color:#f8fafc;pointer-events:none;z-index:100;background:#0f172ae6;border:1px solid #ffffff26;border-radius:9999px;align-items:center;gap:8px;padding:8px 16px;font-size:13px;transition:opacity .2s,transform .2s;display:flex;position:absolute;bottom:24px;left:50%;transform:translate(-50%);box-shadow:0 4px 16px #0000004d}.gmv-interaction-tip.hidden{opacity:0;pointer-events:none;transform:translate(-50%,10px)}.gmv-loading-overlay{-webkit-backdrop-filter:blur(12px);z-index:999;opacity:0;pointer-events:none;background:#0a0f1dbf;justify-content:center;align-items:center;transition:opacity .3s cubic-bezier(.16,1,.3,1);display:flex;position:absolute;inset:0}.gmv-loading-overlay.active{opacity:1;pointer-events:auto}.gmv-loading-card{text-align:center;background:#161e31f0;border:1px solid #38bdf859;border-radius:16px;flex-direction:column;align-items:center;gap:14px;width:380px;padding:24px 28px;transition:transform .3s cubic-bezier(.16,1,.3,1);display:flex;transform:scale(.92);box-shadow:0 20px 50px #0009,0 0 30px #38bdf833}.gmv-loading-overlay.active .gmv-loading-card{transform:scale(1)}.gmv-spinner-ring{border:3px solid #38bdf82e;border-top-color:#38bdf8;border-radius:50%;width:44px;height:44px;animation:.85s linear infinite gmv-spin}@keyframes gmv-spin{to{transform:rotate(360deg)}}.gmv-loading-title{color:#f1f5f9;letter-spacing:.3px;font-size:15px;font-weight:600}.gmv-loading-status{color:#94a3b8;white-space:nowrap;text-overflow:ellipsis;max-width:320px;min-height:18px;font-size:12px;overflow:hidden}.gmv-progress-track{background:#33415599;border-radius:999px;width:100%;height:6px;position:relative;overflow:hidden}.gmv-progress-bar{background:linear-gradient(90deg,#0284c7,#38bdf8,#818cf8);border-radius:999px;width:0%;height:100%;transition:width .25s cubic-bezier(.16,1,.3,1);box-shadow:0 0 12px #38bdf880}.gmv-progress-text{color:#38bdf8;align-self:flex-end;margin-top:-6px;font-size:12px;font-weight:700}.gmv-plane-badge{pointer-events:none;-webkit-user-select:none;user-select:none;-webkit-backdrop-filter:blur(8px);color:#fff;white-space:nowrap;border:1px solid #ffffff40;border-radius:6px;align-items:center;gap:4px;padding:3px 8px;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;font-size:11px;font-weight:600;display:inline-flex;box-shadow:0 4px 14px #00000073}.gmv-plane-badge-x{background:#dc2626e0;border-color:#f87171}.gmv-plane-badge-y{background:#059669e0;border-color:#34d399}.gmv-plane-badge-z{background:#2563ebe0;border-color:#60a5fa}
2
+ /*$vite$:1*/
@@ -0,0 +1,36 @@
1
+ import { BoundingBoxOptions } from '../types';
2
+ import * as THREE from 'three';
3
+ export declare class BoundingBoxTool {
4
+ private scene;
5
+ private boxGroup;
6
+ private wireframeBox;
7
+ private lineX;
8
+ private lineY;
9
+ private lineZ;
10
+ private labelX;
11
+ private labelY;
12
+ private labelZ;
13
+ private currentModel;
14
+ private isVisible;
15
+ private unit;
16
+ private localVertices;
17
+ private worldVertices;
18
+ private modelSize;
19
+ constructor(scene: THREE.Scene);
20
+ /**
21
+ * Attaches bounding box to a model and initializes dynamic dimensional lines
22
+ */
23
+ attachToModel(model: THREE.Object3D, options?: Partial<BoundingBoxOptions>): void;
24
+ private createDynamicLine;
25
+ /**
26
+ * Updates bounding box and selects the CURRENT front-facing 3 edges dynamically
27
+ * based on the model's current rotation and camera perspective!
28
+ */
29
+ update(camera: THREE.Camera): void;
30
+ private updateLine;
31
+ setVisible(visible: boolean): void;
32
+ getVisible(): boolean;
33
+ setUnit(unit: string): void;
34
+ detach(): void;
35
+ destroy(): void;
36
+ }
@@ -0,0 +1,39 @@
1
+ import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
2
+ import { TrackballControls } from 'three/addons/controls/TrackballControls.js';
3
+ import { AutoRotateAxis, AutoRotateDirection, AutoRotateOptions, CameraView, RotationLockAxis } from '../types';
4
+ import * as THREE from 'three';
5
+ import * as TWEEN from '@tweenjs/tween.js';
6
+ export declare class CameraTool {
7
+ private camera;
8
+ private controls;
9
+ private tweenGroup;
10
+ private currentTween;
11
+ private isAutoRotating;
12
+ private autoRotateAxis;
13
+ private autoRotateDirection;
14
+ private autoRotateSpeed;
15
+ private lockedAxis;
16
+ private lockedPitchAngle;
17
+ private lockedYawAngle;
18
+ constructor(camera: THREE.PerspectiveCamera, controls: OrbitControls | TrackballControls, tweenGroup?: TWEEN.Group);
19
+ setAutoRotate(enabled: boolean, axis?: AutoRotateAxis, direction?: AutoRotateDirection, speed?: number): void;
20
+ toggleAutoRotate(): boolean;
21
+ setAutoRotateAxis(axis: AutoRotateAxis): void;
22
+ setAutoRotateDirection(direction: AutoRotateDirection): void;
23
+ setAutoRotateSpeed(speed: number): void;
24
+ getAutoRotateState(): AutoRotateOptions;
25
+ setLockedAxis(axis: RotationLockAxis): void;
26
+ getLockedAxis(): RotationLockAxis;
27
+ /**
28
+ * Called each frame in render loop to apply auto-rotation and axis lock constraints
29
+ */
30
+ updatePerFrame(): void;
31
+ /**
32
+ * Smoothly transitions the camera to one of the 8 standard view orientations
33
+ */
34
+ setView(view: CameraView, targetObject: THREE.Object3D, duration?: number): Promise<void>;
35
+ /**
36
+ * Fits camera to view the whole object from current angle
37
+ */
38
+ fitToObject(object: THREE.Object3D, duration?: number): Promise<void>;
39
+ }
@@ -0,0 +1,140 @@
1
+ import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
2
+ import { TrackballControls } from 'three/addons/controls/TrackballControls.js';
3
+ import { ModelLoader } from '../loaders/ModelLoader';
4
+ import { ClippingAxis, SectioningState } from '../types';
5
+ import { EventEmitter } from '../core/EventEmitter';
6
+ import * as THREE from 'three';
7
+ export declare class ClippingTool {
8
+ private scene;
9
+ private camera;
10
+ private container;
11
+ private modelLoader;
12
+ private controls;
13
+ private emitter;
14
+ private planeX;
15
+ private planeY;
16
+ private planeZ;
17
+ private customPlane;
18
+ private isEnabled;
19
+ private activeAxis;
20
+ private helperVisible;
21
+ private planesGroup;
22
+ private interactive;
23
+ private widgets;
24
+ private isDraggingPlane;
25
+ private draggedWidget;
26
+ private dragStartPointer;
27
+ private dragStartPosition;
28
+ private dragScreenAxis;
29
+ private dragScreenAxisLenSq;
30
+ private raycaster;
31
+ private hoveredAxis;
32
+ private isPickingCustomPoints;
33
+ private customPoints;
34
+ private customMarkersGroup;
35
+ private modelBox;
36
+ private onPointerDownBound;
37
+ private onPointerMoveBound;
38
+ private onPointerMoveDragBound;
39
+ private onPointerUpDragBound;
40
+ private onDoubleClickBound;
41
+ constructor(scene: THREE.Scene, camera: THREE.PerspectiveCamera, container: HTMLElement, modelLoader: ModelLoader, controls?: OrbitControls | TrackballControls, emitter?: EventEmitter);
42
+ setControls(controls: OrbitControls | TrackballControls): void;
43
+ setEmitter(emitter: EventEmitter): void;
44
+ setInteractive(interactive: boolean): void;
45
+ /**
46
+ * Initializes or updates bounds from loaded model
47
+ */
48
+ setModelBounds(box: THREE.Box3): void;
49
+ /**
50
+ * Builds the 3 interactive clipping planes strictly fitting inside the model's bounding box
51
+ */
52
+ private rebuildPlaneWidgets;
53
+ private createPlaneWidget;
54
+ private clearPlaneWidgets;
55
+ private updateWidgetsVisibility;
56
+ /**
57
+ * 1. 开启 / 关闭 剖切总开关 (Enable/Disable Sectioning)
58
+ */
59
+ setSectioningEnabled(enabled: boolean): void;
60
+ getSectioningEnabled(): boolean;
61
+ /**
62
+ * 2. 独立开启或关闭某个轴的剖切 (Multi-Axis Toggle)
63
+ * 支持 X、Y、Z 任意多轴同时开启与同时剖切!
64
+ */
65
+ setAxisEnabled(axis: ClippingAxis, enabled: boolean): void;
66
+ toggleAxis(axis: ClippingAxis, enabled?: boolean): boolean;
67
+ isAxisEnabled(axis: ClippingAxis): boolean;
68
+ /**
69
+ * 设置激活/聚焦的轴向 (兼容已有调用)
70
+ */
71
+ setSectioningAxis(axis: ClippingAxis): void;
72
+ getSectioningAxis(): ClippingAxis;
73
+ /**
74
+ * 3. 隐藏 / 显示 剖切辅助面 (Hide Sectional Plane while keeping the cut)
75
+ */
76
+ setSectioningHelperVisible(visible: boolean): void;
77
+ getSectioningHelperVisible(): boolean;
78
+ /**
79
+ * 4. 独立反向某轴剖切 (Reverse Sectioning for a specific axis or active axis)
80
+ */
81
+ setAxisReverse(axis: ClippingAxis, reverse: boolean): void;
82
+ setSectioningReverse(reverse: boolean): void;
83
+ getSectioningReverse(): boolean;
84
+ invertAxis(axis: ClippingAxis): boolean;
85
+ /**
86
+ * 5. 重置剖切面至中心位置与默认正向状态
87
+ */
88
+ resetSectioning(axis?: ClippingAxis): void;
89
+ /**
90
+ * 获取多轴综合剖切状态
91
+ */
92
+ getSectioningState(): SectioningState;
93
+ private emitSectioningState;
94
+ private handlePointerDown;
95
+ private handlePointerMoveDrag;
96
+ private handlePointerUpDrag;
97
+ /**
98
+ * Hover highlighting across all enabled planes
99
+ */
100
+ private handlePointerMove;
101
+ private setHoveredAxis;
102
+ /**
103
+ * Double click on any plane quickly flips/inverts slicing direction of that specific plane
104
+ */
105
+ private handleDoubleClick;
106
+ setPlanePositionExplicit(axis: ClippingAxis, pos: number): void;
107
+ setAxisProgress(axis: ClippingAxis, progress: number): void;
108
+ getAxisProgress(axis: ClippingAxis): number;
109
+ private disableAxisInternal;
110
+ private disableAllAxesPlanes;
111
+ setAxisClipping(axis: ClippingAxis, progress: number, inverted?: boolean): void;
112
+ disableAxis(axis: ClippingAxis): void;
113
+ getAxisState(axis: ClippingAxis): {
114
+ enabled: boolean;
115
+ progress: number;
116
+ inverted: boolean;
117
+ position: number;
118
+ };
119
+ setPlanesVisible(visible: boolean): void;
120
+ getPlanesVisible(): boolean;
121
+ startCustomClip(): void;
122
+ cancelCustomClip(): void;
123
+ clearCustomPlane(): void;
124
+ invertCustomClip(): boolean;
125
+ hasCustomPlane(): boolean;
126
+ private handleCustomPointPick;
127
+ private addCustomPoint;
128
+ /**
129
+ * Updates custom knife markers to maintain constant pixel size on screen
130
+ */
131
+ updatePerFrame(viewportHeight?: number): void;
132
+ private updateSingleMarkerScale;
133
+ private clearCustomMarkers;
134
+ /**
135
+ * Applies all currently active clipping planes to model materials simultaneously!
136
+ */
137
+ private applyPlanes;
138
+ resetAll(): void;
139
+ destroy(): void;
140
+ }
@@ -0,0 +1,61 @@
1
+ import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
2
+ import { TrackballControls } from 'three/addons/controls/TrackballControls.js';
3
+ import { ModelLoader } from '../loaders/ModelLoader';
4
+ import { EventEmitter } from '../core/EventEmitter';
5
+ import { MeasurementResult, MeasurementType } from '../types';
6
+ import * as THREE from 'three';
7
+ export declare class MeasurementTool {
8
+ private scene;
9
+ private camera;
10
+ private container;
11
+ private modelLoader;
12
+ private emitter;
13
+ private controls;
14
+ private currentMode;
15
+ private unit;
16
+ private measurementGroup;
17
+ private results;
18
+ private activePoints;
19
+ private activePreviewGroup;
20
+ private activeMarkersGroup;
21
+ private activeLineGroup;
22
+ private snapCursorMesh;
23
+ private onPointerDownBound;
24
+ private onPointerMoveBound;
25
+ private onDoubleClickBound;
26
+ constructor(scene: THREE.Scene, camera: THREE.PerspectiveCamera, container: HTMLElement, modelLoader: ModelLoader, emitter: EventEmitter, controls?: OrbitControls | TrackballControls);
27
+ setControls(controls: OrbitControls | TrackballControls): void;
28
+ setUnit(unit: string): void;
29
+ setMode(mode: MeasurementType): void;
30
+ getMode(): MeasurementType;
31
+ getControls(): OrbitControls | TrackballControls | null;
32
+ private addListeners;
33
+ private removeListeners;
34
+ private handlePointerMove;
35
+ private handlePointerDown;
36
+ private addActiveMarker;
37
+ private handleDoubleClick;
38
+ private updateDynamicPreview;
39
+ private finishDistanceMeasurement;
40
+ private finishHeightMeasurement;
41
+ private finishAreaMeasurement;
42
+ private finishAngleMeasurement;
43
+ private addEndSphere;
44
+ undoLastPoint(): void;
45
+ cancelActive(): void;
46
+ private clearActiveLine;
47
+ private clearActiveMarkers;
48
+ /**
49
+ * Updates scale of all marker spheres dynamically per frame so they maintain
50
+ * a constant screen pixel size, completely independent of camera distance or model zoom!
51
+ */
52
+ updatePerFrame(viewportHeight?: number): void;
53
+ private updateSingleMeshScale;
54
+ /**
55
+ * Completely clears ALL measurements and CSS2D DOM elements
56
+ */
57
+ clearAll(): void;
58
+ private disposeObjectHierarchy;
59
+ getResults(): MeasurementResult[];
60
+ destroy(): void;
61
+ }
@@ -0,0 +1,21 @@
1
+ import { ExportImageOptions } from '../types';
2
+ import * as THREE from 'three';
3
+ export declare class ScreenshotTool {
4
+ private renderer;
5
+ private scene;
6
+ private camera;
7
+ private getModel;
8
+ constructor(renderer: THREE.WebGLRenderer, scene: THREE.Scene, camera: THREE.PerspectiveCamera, getModel?: () => THREE.Object3D | null);
9
+ /**
10
+ * Finds the maximum original texture resolution from meshes in the scene
11
+ */
12
+ getSceneMaxTextureSize(): {
13
+ width: number;
14
+ height: number;
15
+ } | null;
16
+ /**
17
+ * Exports high-definition screenshot of the current view.
18
+ * Supports 'original' / originalQuality which aligns directly to the model's actual texture resolution!
19
+ */
20
+ exportImage(options?: ExportImageOptions): Promise<string>;
21
+ }
@@ -0,0 +1,121 @@
1
+ import * as THREE from 'three';
2
+ export type CameraView = 'front' | 'back' | 'left' | 'right' | 'top' | 'bottom' | 'isometric' | 'isometric-back';
3
+ export type MeasurementType = 'distance' | 'area' | 'height' | 'angle' | 'none';
4
+ export type AutoRotateAxis = 'x' | 'y';
5
+ export type AutoRotateDirection = 'clockwise' | 'counter-clockwise';
6
+ export type RotationLockAxis = 'x' | 'y' | 'z' | null;
7
+ export interface AutoRotateOptions {
8
+ enabled: boolean;
9
+ axis: AutoRotateAxis;
10
+ direction: AutoRotateDirection;
11
+ speed: number;
12
+ }
13
+ export type ClippingAxis = 'x' | 'y' | 'z';
14
+ export interface ViewerOptions {
15
+ container: HTMLElement;
16
+ background?: string | number | null;
17
+ alpha?: boolean;
18
+ antialias?: boolean;
19
+ cameraFov?: number;
20
+ cameraNear?: number;
21
+ cameraFar?: number;
22
+ showBoundingBox?: boolean;
23
+ unit?: string;
24
+ controlsType?: 'trackball' | 'orbit';
25
+ }
26
+ export interface ModelLoadResult {
27
+ model: THREE.Group;
28
+ box: THREE.Box3;
29
+ size: THREE.Vector3;
30
+ center: THREE.Vector3;
31
+ vertexCount: number;
32
+ faceCount: number;
33
+ }
34
+ export interface MeasurementResult {
35
+ id: string;
36
+ type: MeasurementType;
37
+ value: number;
38
+ formattedValue: string;
39
+ unit: string;
40
+ points: THREE.Vector3[];
41
+ extra?: {
42
+ horizontalDistance?: number;
43
+ verticalHeight?: number;
44
+ angleDegree?: number;
45
+ area?: number;
46
+ };
47
+ }
48
+ export interface ExportImageOptions {
49
+ scale?: number | 'original';
50
+ originalQuality?: boolean;
51
+ targetSize?: {
52
+ width: number;
53
+ height: number;
54
+ };
55
+ modelOnly?: boolean;
56
+ type?: 'image/png' | 'image/jpeg';
57
+ quality?: number;
58
+ download?: boolean;
59
+ filename?: string;
60
+ background?: string | null;
61
+ }
62
+ export interface ClippingOptions {
63
+ axis?: ClippingAxis;
64
+ value?: number;
65
+ inverted?: boolean;
66
+ }
67
+ export interface ClippingChangeEvent {
68
+ axis?: ClippingAxis;
69
+ position?: number;
70
+ progress?: number;
71
+ inverted?: boolean;
72
+ enabled?: boolean;
73
+ axisClipping?: Record<ClippingAxis, number>;
74
+ customPlaneActive?: boolean;
75
+ }
76
+ export interface AxisSectionState {
77
+ enabled: boolean;
78
+ inverted: boolean;
79
+ progress: number;
80
+ position: number;
81
+ }
82
+ export interface SectioningState {
83
+ enabled: boolean;
84
+ axes: Record<ClippingAxis, AxisSectionState>;
85
+ helperVisible: boolean;
86
+ axis?: ClippingAxis;
87
+ isReverse?: boolean;
88
+ progress?: number;
89
+ position?: number;
90
+ }
91
+ export type ModelDisplayStyle = 'solid' | 'wireframe';
92
+ export interface PointsOptions {
93
+ size?: number;
94
+ color?: string | number;
95
+ sizeAttenuation?: boolean;
96
+ opacity?: number;
97
+ }
98
+ export interface BoundingBoxOptions {
99
+ visible: boolean;
100
+ showDimensions?: boolean;
101
+ color?: string | number;
102
+ unit?: string;
103
+ }
104
+ export type ViewerEvents = {
105
+ 'load:start': (url: string | File) => void;
106
+ 'load:progress': (progress: number, message: string) => void;
107
+ 'load:success': (result: ModelLoadResult) => void;
108
+ 'load:error': (error: Error) => void;
109
+ 'measure:start': (type: MeasurementType) => void;
110
+ 'measure:change': (points: THREE.Vector3[]) => void;
111
+ 'measure:complete': (result: MeasurementResult) => void;
112
+ 'measure:clear': () => void;
113
+ 'clipping:change': (info: ClippingChangeEvent) => void;
114
+ 'sectioning:change': (state: SectioningState) => void;
115
+ 'view:change': (view: CameraView) => void;
116
+ 'texture:toggle': (hasTexture: boolean) => void;
117
+ 'autorotate:change': (options: AutoRotateOptions) => void;
118
+ 'axislock:change': (lockedAxis: RotationLockAxis) => void;
119
+ 'style:change': (style: ModelDisplayStyle) => void;
120
+ 'pointsize:change'?: (size: number) => void;
121
+ };
@@ -0,0 +1,21 @@
1
+ import * as THREE from 'three';
2
+ /**
3
+ * Calculates the exact signed volume of a 3D Mesh using divergence theorem.
4
+ * Works accurately for closed (watertight) meshes.
5
+ */
6
+ export declare function computeMeshVolume(mesh: THREE.Mesh): number;
7
+ /**
8
+ * Computes the total volume of all meshes inside a Group or Object3D
9
+ */
10
+ export declare function computeGroupVolume(root: THREE.Object3D): {
11
+ meshVolume: number;
12
+ bboxVolume: number;
13
+ };
14
+ /**
15
+ * Calculates the area of a 3D polygon defined by an array of 3D vertices using Newell's method.
16
+ */
17
+ export declare function compute3DPolygonArea(points: THREE.Vector3[]): number;
18
+ /**
19
+ * Formats numeric value with unit
20
+ */
21
+ export declare function formatUnit(value: number, unit?: string, decimals?: number): string;
@@ -0,0 +1,14 @@
1
+ import * as THREE from 'three';
2
+ export interface SnapResult {
3
+ hit: boolean;
4
+ point: THREE.Vector3 | null;
5
+ normal: THREE.Vector3 | null;
6
+ isVertexSnapped: boolean;
7
+ distanceToVertex: number;
8
+ }
9
+ /**
10
+ * Finds intersection on model and optionally snaps to the nearest vertex
11
+ * of the intersected face if within the screen-space pixel threshold.
12
+ */
13
+ export declare function findSnapPoint(mouse: THREE.Vector2, // Normalized Device Coordinates (-1 to 1)
14
+ camera: THREE.Camera, modelMeshes: THREE.Mesh[], snapPixelThreshold?: number, viewportWidth?: number, viewportHeight?: number): SnapResult;
package/package.json CHANGED
@@ -1,6 +1,70 @@
1
1
  {
2
2
  "name": "@giszhc/three-model-viewer",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "1.0.0",
4
+ "description": "A modern, professional 3D model inspector and viewer SDK for Web applications. Features OBJ/MTL/ZIP loading, 3D measurements, dynamic dimensional bounding box, plane slicing, and high-res export.",
5
+ "main": "./dist/index.cjs",
6
+ "module": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "unpkg": "./dist/index.umd.js",
9
+ "jsdelivr": "./dist/index.umd.js",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js",
14
+ "require": "./dist/index.cjs",
15
+ "default": "./dist/index.umd.js"
16
+ },
17
+ "./style.css": "./dist/style.css"
18
+ },
19
+ "files": [
20
+ "dist",
21
+ "README.md",
22
+ "LICENSE"
23
+ ],
24
+ "keywords": [
25
+ "three",
26
+ "threejs",
27
+ "model-viewer",
28
+ "obj",
29
+ "mtl",
30
+ "zip",
31
+ "measurement",
32
+ "clipping",
33
+ "bounding-box",
34
+ "cad-viewer",
35
+ "3d"
36
+ ],
37
+ "author": "giszhc",
38
+ "license": "MIT",
39
+ "repository": {
40
+ "type": "git",
41
+ "url": "git+https://github.com/giszhc/three-model-viewer.git"
42
+ },
43
+ "bugs": {
44
+ "url": "https://github.com/giszhc/three-model-viewer/issues"
45
+ },
46
+ "homepage": "https://github.com/giszhc/three-model-viewer#readme",
47
+ "type": "module",
48
+ "dependencies": {
49
+ "@tweenjs/tween.js": "^25.0.0",
50
+ "gsap": "^3.15.0",
51
+ "jszip": "^3.10.2",
52
+ "three": "^0.186.1",
53
+ "three-mesh-bvh": "^0.9.16"
54
+ },
55
+ "devDependencies": {
56
+ "@types/three": "^0.186.0",
57
+ "typescript": "^5.8.2",
58
+ "vite": "^8.3.5",
59
+ "vite-plugin-dts": "^5.1.2"
60
+ },
61
+ "scripts": {
62
+ "dev": "vite",
63
+ "build:lib": "vite build",
64
+ "build:cdn": "vite build --mode cdn",
65
+ "build": "vite build && vite build --mode cdn",
66
+ "build:demo": "vite build --mode demo",
67
+ "preview": "vite preview",
68
+ "preview:demo": "vite preview --outDir dist-demo"
69
+ }
6
70
  }