@smarterplan/ngx-smarterplan-core 1.4.15 → 1.4.17
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.
- package/README.md +24 -24
- package/esm2022/lib/components/csv-export/csv-export.component.mjs +3 -3
- package/esm2022/lib/components/loader/loader.component.mjs +3 -3
- package/esm2022/lib/components/menu-bar/avatar/avatar.component.mjs +1 -1
- package/esm2022/lib/components/menu-bar/menu-bar.component.mjs +3 -3
- package/esm2022/lib/components/menu-bar/navigation-bar/navigation-bar.component.mjs +3 -3
- package/esm2022/lib/components/modal-switch-visit/modal-switch-visit.component.mjs +3 -3
- package/esm2022/lib/components/search-bar/search-bar.component.mjs +3 -3
- package/esm2022/lib/components/support-modal/support-modal.component.mjs +3 -3
- package/esm2022/lib/config.mjs +1 -1
- package/esm2022/lib/helpers.service.mjs +46 -1
- package/esm2022/lib/matterport-extensions/nest-thermostat/CanvasImage.mjs +1 -1
- package/esm2022/lib/matterport-extensions/nest-thermostat/CanvasRenderer.mjs +1 -1
- package/esm2022/lib/matterport-extensions/nest-thermostat/NestThermostat.mjs +1 -1
- package/esm2022/lib/matterport-extensions/nest-thermostat/PlaneRenderer.mjs +1 -1
- package/esm2022/lib/matterport-extensions/scene-component/SceneComponent.mjs +1 -1
- package/esm2022/lib/matterport-extensions/security-camera/SecurityCamera.mjs +22 -22
- package/esm2022/lib/matterport-extensions/tv-player/TvPlayer.mjs +1 -1
- package/esm2022/lib/matterport-extensions/view-frustum-mesh/ViewFrustumMesh.mjs +22 -22
- package/esm2022/lib/mattertagData.mjs +1 -1
- package/esm2022/lib/ngx-smarterplan-core.module.mjs +1 -1
- package/esm2022/lib/ngx-smarterplan-core.service.mjs +1 -1
- package/esm2022/lib/pipes/duration-to-string.pipe.mjs +1 -1
- package/esm2022/lib/pipes/format-date-number-to-digits.pipe.mjs +1 -1
- package/esm2022/lib/pipes/hashtag-from-id.pipe.mjs +1 -1
- package/esm2022/lib/pipes/safe-url.pipe.mjs +1 -1
- package/esm2022/lib/pipes/time-date-to-local-string.pipe.mjs +1 -1
- package/esm2022/lib/pipes/username-from-id.pipe.mjs +1 -1
- package/esm2022/lib/services/amplify-cache.service.mjs +1 -1
- package/esm2022/lib/services/base-tab.service.mjs +1 -1
- package/esm2022/lib/services/baseVisibility.service.mjs +1 -1
- package/esm2022/lib/services/content.service.mjs +1 -1
- package/esm2022/lib/services/filter.service.mjs +11 -1
- package/esm2022/lib/services/intervention.service.mjs +1 -1
- package/esm2022/lib/services/locale.service.mjs +1 -1
- package/esm2022/lib/services/matterport-import.service.mjs +1 -1
- package/esm2022/lib/services/matterport-measurement.service.mjs +1 -1
- package/esm2022/lib/services/matterport-navigation.service.mjs +1 -1
- package/esm2022/lib/services/matterport-object3d.service.mjs +1 -1
- package/esm2022/lib/services/matterport-pointer.service.mjs +6 -6
- package/esm2022/lib/services/matterport-tag-copy.service.mjs +1 -1
- package/esm2022/lib/services/matterport-tag.service.mjs +1 -1
- package/esm2022/lib/services/matterport.service.mjs +29 -18
- package/esm2022/lib/services/models/affectation.service.mjs +1 -1
- package/esm2022/lib/services/models/base-object.service.mjs +31 -4
- package/esm2022/lib/services/models/capture.service.mjs +1 -1
- package/esm2022/lib/services/models/comment.service.mjs +1 -1
- package/esm2022/lib/services/models/domain.service.mjs +1 -1
- package/esm2022/lib/services/models/equipment.service.mjs +14 -13
- package/esm2022/lib/services/models/event.service.mjs +1 -1
- package/esm2022/lib/services/models/feature.service.mjs +8 -7
- package/esm2022/lib/services/models/hashtag.service.mjs +1 -1
- package/esm2022/lib/services/models/layer.service.mjs +1 -1
- package/esm2022/lib/services/models/measurement.service.mjs +1 -1
- package/esm2022/lib/services/models/mission.service.mjs +1 -1
- package/esm2022/lib/services/models/navigation.service.mjs +1 -1
- package/esm2022/lib/services/models/node.service.mjs +1 -1
- package/esm2022/lib/services/models/object3D.service.mjs +1 -1
- package/esm2022/lib/services/models/operation.service.mjs +1 -1
- package/esm2022/lib/services/models/organisation.service.mjs +1 -1
- package/esm2022/lib/services/models/plan.service.mjs +1 -1
- package/esm2022/lib/services/models/poi.service.mjs +27 -27
- package/esm2022/lib/services/models/profile.service.mjs +1 -1
- package/esm2022/lib/services/models/property.service.mjs +1 -1
- package/esm2022/lib/services/models/space.service.mjs +48 -6
- package/esm2022/lib/services/models/template.service.mjs +1 -1
- package/esm2022/lib/services/models/ticket.service.mjs +27 -8
- package/esm2022/lib/services/models/visit.service.mjs +17 -5
- package/esm2022/lib/services/models/zone.service.mjs +1 -1
- package/esm2022/lib/services/navigator.service.mjs +14 -1
- package/esm2022/lib/services/s3.service.mjs +18 -1
- package/esm2022/lib/services/search.service.mjs +1 -1
- package/esm2022/lib/services/support.service.mjs +1 -1
- package/esm2022/lib/services/tag.service.mjs +16 -16
- package/esm2022/lib/services/threejs-import.service.mjs +151 -0
- package/esm2022/lib/services/threejs.service.mjs +1587 -0
- package/esm2022/lib/services/user.service.mjs +1 -1
- package/esm2022/lib/services/utils/matterport-transform.util.mjs +1 -1
- package/esm2022/lib/services/validators.service.mjs +1 -1
- package/esm2022/lib/services/viewer.service.mjs +402 -5
- package/esm2022/lib/services/zone-drawer.service.mjs +1 -1
- package/esm2022/lib/services/zoneChange.service.mjs +10 -1
- package/esm2022/lib/tokens/matterport-sdk-key.token.mjs +1 -1
- package/esm2022/lib/types.service.mjs +2 -1
- package/esm2022/lib/validators/email.directive.mjs +1 -1
- package/esm2022/lib/validators/no-empty.directive.mjs +1 -1
- package/esm2022/lib/validators/number.directive.mjs +1 -1
- package/esm2022/lib/validators/text.directive.mjs +1 -1
- package/esm2022/public-api.mjs +3 -1
- package/fesm2022/smarterplan-ngx-smarterplan-core.mjs +8046 -5724
- package/fesm2022/smarterplan-ngx-smarterplan-core.mjs.map +1 -1
- package/lib/helpers.service.d.ts +43 -0
- package/lib/services/models/base-object.service.d.ts +11 -1
- package/lib/services/models/equipment.service.d.ts +2 -1
- package/lib/services/models/feature.service.d.ts +2 -1
- package/lib/services/models/space.service.d.ts +22 -1
- package/lib/services/models/ticket.service.d.ts +4 -1
- package/lib/services/models/visit.service.d.ts +9 -1
- package/lib/services/s3.service.d.ts +8 -0
- package/lib/services/threejs-import.service.d.ts +31 -0
- package/lib/services/threejs.service.d.ts +521 -0
- package/lib/services/viewer.service.d.ts +152 -1
- package/lib/services/zoneChange.service.d.ts +9 -0
- package/lib/types.service.d.ts +15 -1
- package/package.json +1 -1
- package/public-api.d.ts +2 -0
- package/src/assets/vs-app.json +67 -67
|
@@ -0,0 +1,521 @@
|
|
|
1
|
+
import { NgZone } from '@angular/core';
|
|
2
|
+
import { Subject } from 'rxjs';
|
|
3
|
+
import { ThreeJsAlignment } from '../helpers.service';
|
|
4
|
+
import { PoiType } from '../types.service';
|
|
5
|
+
import * as i0 from "@angular/core";
|
|
6
|
+
/**
|
|
7
|
+
* A tag to draw in the Three.js scene, mirroring a Mattertag.
|
|
8
|
+
*
|
|
9
|
+
* Positions are in the model's own coordinate space - the same numbers stored in POI.coordinate -
|
|
10
|
+
* not scene space. The service reparents tags under the model so they inherit the centering and
|
|
11
|
+
* scaling applied at load, which means callers never deal with that transform.
|
|
12
|
+
*/
|
|
13
|
+
export interface ThreeJsTag {
|
|
14
|
+
/** The tagged element's id (POI.elementID), used to update or remove the tag later. */
|
|
15
|
+
id: string;
|
|
16
|
+
position: {
|
|
17
|
+
x: number;
|
|
18
|
+
y: number;
|
|
19
|
+
z: number;
|
|
20
|
+
};
|
|
21
|
+
/** Drives both the click route and type filtering, so it travels with the tag. */
|
|
22
|
+
poiType?: PoiType | null;
|
|
23
|
+
/** Shown on hover. A marker with no identity is just a dot on a wall. */
|
|
24
|
+
label?: string | null;
|
|
25
|
+
/** Icon drawn on the tag. Falls back to a plain disc when absent or it fails to load. */
|
|
26
|
+
iconUrl?: string | null;
|
|
27
|
+
/** Tint for the fallback disc, as a CSS colour. */
|
|
28
|
+
color?: string;
|
|
29
|
+
}
|
|
30
|
+
/** What a click on a tag reports: enough to route to the element's detail page. */
|
|
31
|
+
export interface ThreeJsTagClick {
|
|
32
|
+
elementId: string;
|
|
33
|
+
poiType: PoiType | null;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* A spot picked on the model for a new tag.
|
|
37
|
+
*
|
|
38
|
+
* Both vectors are in the model's own coordinate space, which is what POI.coordinate stores. Scene
|
|
39
|
+
* coordinates deliberately never leave this service: the model is scaled to a fixed size on load, so
|
|
40
|
+
* a scene-space position would silently mean somewhere else the moment the .glb is re-exported.
|
|
41
|
+
*/
|
|
42
|
+
export interface ThreeJsTagPlacement {
|
|
43
|
+
position: {
|
|
44
|
+
x: number;
|
|
45
|
+
y: number;
|
|
46
|
+
z: number;
|
|
47
|
+
};
|
|
48
|
+
/** Surface normal at the picked point - Matterport stores the same thing in POI.metadata. */
|
|
49
|
+
normal: {
|
|
50
|
+
x: number;
|
|
51
|
+
y: number;
|
|
52
|
+
z: number;
|
|
53
|
+
};
|
|
54
|
+
/** What the placement was armed for, so the caller knows which creation form to open. */
|
|
55
|
+
poiType?: PoiType | null;
|
|
56
|
+
}
|
|
57
|
+
/** One tag seen in both spaces: where it is stored, and where it really sits on the model. */
|
|
58
|
+
export interface ThreeJsAlignmentPair {
|
|
59
|
+
source: {
|
|
60
|
+
x: number;
|
|
61
|
+
y: number;
|
|
62
|
+
z: number;
|
|
63
|
+
};
|
|
64
|
+
target: {
|
|
65
|
+
x: number;
|
|
66
|
+
y: number;
|
|
67
|
+
z: number;
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Solves the transform that maps stored tag coordinates onto the loaded model.
|
|
72
|
+
*
|
|
73
|
+
* Takes exactly the correspondences a person can produce by hand: three tags, each pointed at twice
|
|
74
|
+
* - once where it currently sits, once where it belongs. That is the minimum that pins down all
|
|
75
|
+
* three parts of the answer. One pair fixes only translation; two leave the rotation about the line
|
|
76
|
+
* between them free; three resolve it.
|
|
77
|
+
*
|
|
78
|
+
* Returns null when the points are collinear or degenerate, because a wrong alignment applied
|
|
79
|
+
* silently is worse than none.
|
|
80
|
+
*/
|
|
81
|
+
export declare function solveThreeJsAlignment(pairs: ThreeJsAlignmentPair[]): ThreeJsAlignment | null;
|
|
82
|
+
export interface ObjAdded {
|
|
83
|
+
id: string;
|
|
84
|
+
name: string;
|
|
85
|
+
file: string;
|
|
86
|
+
position: {
|
|
87
|
+
x: number;
|
|
88
|
+
y: number;
|
|
89
|
+
z: number;
|
|
90
|
+
};
|
|
91
|
+
rotation: {
|
|
92
|
+
x: number;
|
|
93
|
+
y: number;
|
|
94
|
+
z: number;
|
|
95
|
+
};
|
|
96
|
+
scale: {
|
|
97
|
+
x: number;
|
|
98
|
+
y: number;
|
|
99
|
+
z: number;
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
export declare class ThreeJsService {
|
|
103
|
+
private ngZone;
|
|
104
|
+
private scene;
|
|
105
|
+
private camera;
|
|
106
|
+
private renderer;
|
|
107
|
+
private controls;
|
|
108
|
+
private animationFrameId;
|
|
109
|
+
private raycaster;
|
|
110
|
+
private mouse;
|
|
111
|
+
private isDraggingCamera;
|
|
112
|
+
private isDraggingTimer;
|
|
113
|
+
private clickableObjects;
|
|
114
|
+
objectHovered$: Subject<{
|
|
115
|
+
id: string;
|
|
116
|
+
event: MouseEvent;
|
|
117
|
+
}>;
|
|
118
|
+
objectClicked$: Subject<string>;
|
|
119
|
+
/** Emits when the user clicks a tag, mirroring Matterport's tag click. */
|
|
120
|
+
tagClicked$: Subject<ThreeJsTagClick>;
|
|
121
|
+
/** Emits a tag's label while the pointer is over it, and null when it leaves. */
|
|
122
|
+
tagHovered$: Subject<{
|
|
123
|
+
label: string;
|
|
124
|
+
event: MouseEvent;
|
|
125
|
+
}>;
|
|
126
|
+
private tagGroup;
|
|
127
|
+
private tagSprites;
|
|
128
|
+
private canvasResizeObserver?;
|
|
129
|
+
/** Aborts the in-flight model download. Parsing cannot be interrupted, only not started. */
|
|
130
|
+
private loadAbortController?;
|
|
131
|
+
private modelRoot?;
|
|
132
|
+
/** True while the next click on the model places a tag instead of orbiting. */
|
|
133
|
+
private placingTag;
|
|
134
|
+
/** What the armed placement is for, echoed back when the spot is picked. */
|
|
135
|
+
private placementPoiType;
|
|
136
|
+
/** Position and normal of the last placed tag, in model space, awaiting commit to a POI. */
|
|
137
|
+
private pendingTagPlacement;
|
|
138
|
+
/** Pointer position waiting for a preview pick, coalesced to one per frame. */
|
|
139
|
+
private pendingPreviewEvent;
|
|
140
|
+
/**
|
|
141
|
+
* Layer carrying the model alone, so the depth pick can render it without the tags.
|
|
142
|
+
*
|
|
143
|
+
* Layers are tested per object at render time and children are traversed regardless of whether
|
|
144
|
+
* their parent passed, so marking the model's own meshes is enough - no re-parenting.
|
|
145
|
+
*/
|
|
146
|
+
private static readonly PICK_LAYER;
|
|
147
|
+
/** Cache Storage bucket holding downloaded models, and how many to keep. */
|
|
148
|
+
private static readonly MODEL_CACHE;
|
|
149
|
+
private static readonly MODEL_CACHE_SIZE;
|
|
150
|
+
private pickTarget?;
|
|
151
|
+
private pickCamera?;
|
|
152
|
+
private pickDepthMaterial?;
|
|
153
|
+
private readonly pickPixel;
|
|
154
|
+
/** Emits once a tag is dropped on the model, so the UI can confirm and persist it. */
|
|
155
|
+
tagPlaced$: Subject<ThreeJsTagPlacement>;
|
|
156
|
+
/** Emits when the calibration walkthrough is abandoned, so the panel driving it can close. */
|
|
157
|
+
alignmentCancelled$: Subject<void>;
|
|
158
|
+
/** Translucent marker shown under the cursor while placing. */
|
|
159
|
+
private placementPreview?;
|
|
160
|
+
private aligning;
|
|
161
|
+
private alignmentPairs;
|
|
162
|
+
private alignmentSource;
|
|
163
|
+
/** Drives the alignment walkthrough's UI: how many pairs are done and what it wants next. */
|
|
164
|
+
alignmentProgress$: Subject<{
|
|
165
|
+
pairs: number;
|
|
166
|
+
awaiting: 'tag' | 'point' | null;
|
|
167
|
+
tagId?: string;
|
|
168
|
+
}>;
|
|
169
|
+
/** In-flight camera move started by goToTag, advanced by the render loop. */
|
|
170
|
+
private cameraTween;
|
|
171
|
+
/** Emits true while a heavy OBJ file is being parsed (in a worker, when available), false once done. */
|
|
172
|
+
modelLoading$: Subject<boolean>;
|
|
173
|
+
/**
|
|
174
|
+
* Download progress for GLB models, as a 0-1 fraction, or null when the total size is unknown.
|
|
175
|
+
*
|
|
176
|
+
* A null means the server did not send Content-Length (chunked responses, some proxies) and no
|
|
177
|
+
* percentage can be derived; show an indeterminate indicator rather than guessing at one.
|
|
178
|
+
*/
|
|
179
|
+
modelLoadProgress$: Subject<number>;
|
|
180
|
+
/**
|
|
181
|
+
* Emits when a load actually stopped short, which is not the same as asking it to.
|
|
182
|
+
*
|
|
183
|
+
* cancelModelLoad() can arrive after parse() has taken the main thread, and parse cannot be
|
|
184
|
+
* interrupted - the model finishes loading regardless. Only this tells the UI which of the two
|
|
185
|
+
* happened, so it does not offer to reload something that is already on screen.
|
|
186
|
+
*/
|
|
187
|
+
modelLoadCancelled$: Subject<void>;
|
|
188
|
+
private objParserWorker?;
|
|
189
|
+
private objParserPendingRequests;
|
|
190
|
+
constructor(ngZone: NgZone);
|
|
191
|
+
/**
|
|
192
|
+
* Registers a Web Worker used to parse heavy .obj files off the main thread.
|
|
193
|
+
* Must be instantiated by the consuming app (e.g. `new Worker(new URL('./obj-parser.worker', import.meta.url))`),
|
|
194
|
+
* since a library packaged with ng-packagr cannot bundle worker files itself.
|
|
195
|
+
* When no worker is provided, loadObjModel falls back to parsing on the main thread.
|
|
196
|
+
*/
|
|
197
|
+
setObjParserWorker(worker: Worker): void;
|
|
198
|
+
private requestObjParseInWorker;
|
|
199
|
+
/** Rebuilds a THREE.Mesh from the raw typed arrays sent back by the parser worker. */
|
|
200
|
+
private meshFromParsedData;
|
|
201
|
+
init(canvas: HTMLCanvasElement): void;
|
|
202
|
+
/**
|
|
203
|
+
* Path (served by the app) holding basis_transcoder.js/.wasm, which KTX2Loader needs to turn
|
|
204
|
+
* Basis Universal textures into whatever compressed format the local GPU actually supports.
|
|
205
|
+
*/
|
|
206
|
+
private ktx2TranscoderPath;
|
|
207
|
+
/** Overrides where the Basis transcoder files are served from. */
|
|
208
|
+
setKtx2TranscoderPath(path: string): void;
|
|
209
|
+
/**
|
|
210
|
+
* Loads a base 3D model in GLTF/GLB format - the preferred format for large models.
|
|
211
|
+
* Unlike .obj, geometry arrives as binary typed arrays (no text parsing to offload to a worker)
|
|
212
|
+
* and stays indexed, so far less data reaches the GPU. KTX2 textures are supported too: they
|
|
213
|
+
* stay compressed in VRAM instead of being decoded to raw pixels like JPEG/PNG.
|
|
214
|
+
*/
|
|
215
|
+
loadGLTFModel(url: string, cameraPosition?: {
|
|
216
|
+
x: number;
|
|
217
|
+
y: number;
|
|
218
|
+
z: number;
|
|
219
|
+
}, rotation?: {
|
|
220
|
+
x: number;
|
|
221
|
+
y: number;
|
|
222
|
+
z: number;
|
|
223
|
+
}, cacheKey?: string): void;
|
|
224
|
+
/**
|
|
225
|
+
* Stops the model download in progress, if any.
|
|
226
|
+
*
|
|
227
|
+
* Only the download can be interrupted: parse() runs synchronously and holds the main thread until
|
|
228
|
+
* it finishes, so once it has begun there is nothing to cancel. In practice the download is the
|
|
229
|
+
* long part, which is where someone reaches for this.
|
|
230
|
+
*/
|
|
231
|
+
cancelModelLoad(): void;
|
|
232
|
+
/**
|
|
233
|
+
* Abandons the load in progress without reporting it as cancelled.
|
|
234
|
+
*
|
|
235
|
+
* Dropping the controller is what makes the difference: onError treats an abort as user-initiated
|
|
236
|
+
* only while the controller that aborted is still the current one. Tearing down or rebuilding the
|
|
237
|
+
* scene is not something the person asked for, so it must not surface as "loading cancelled".
|
|
238
|
+
*/
|
|
239
|
+
private discardCurrentLoad;
|
|
240
|
+
/**
|
|
241
|
+
* Resolves once the browser has painted at least one frame.
|
|
242
|
+
*
|
|
243
|
+
* A requestAnimationFrame callback runs *before* that frame is painted, so a single one is not
|
|
244
|
+
* enough to guarantee pending DOM changes are on screen; the second fires after the first frame
|
|
245
|
+
* has gone out. Falls back to a timeout where rAF isn't available (SSR, tests).
|
|
246
|
+
*/
|
|
247
|
+
private afterNextPaint;
|
|
248
|
+
/**
|
|
249
|
+
* Downloads a file, reporting progress on modelLoadProgress$ as it arrives.
|
|
250
|
+
*
|
|
251
|
+
* GLTFLoader.load() would report progress through XHR, but this path deliberately uses fetch() +
|
|
252
|
+
* parse() instead (see loadGLTFModel for why), so progress is read off the response stream here.
|
|
253
|
+
* That is also the more useful number: on a model of this size the download dominates, and the
|
|
254
|
+
* stream reports it as it happens rather than in one jump at the end.
|
|
255
|
+
*/
|
|
256
|
+
private fetchWithProgress;
|
|
257
|
+
/**
|
|
258
|
+
* The model store, or null where the browser will not give us one.
|
|
259
|
+
*
|
|
260
|
+
* Cache Storage needs a secure context, so it is absent over plain http and in some embedded
|
|
261
|
+
* webviews. Every caller treats that as a miss - caching is an optimisation, never a requirement.
|
|
262
|
+
*/
|
|
263
|
+
private openModelCache;
|
|
264
|
+
/**
|
|
265
|
+
* A cached copy of this model, if one was stored.
|
|
266
|
+
*
|
|
267
|
+
* Keyed by the S3 key rather than the URL it is fetched from: that URL is signed, so it carries a
|
|
268
|
+
* fresh signature and expiry every time and would never match twice. The browser's own HTTP cache
|
|
269
|
+
* misses for the same reason, which is why this exists at all.
|
|
270
|
+
*/
|
|
271
|
+
private readModelFromCache;
|
|
272
|
+
/** Stores a model for next time, evicting the oldest once a few are held. */
|
|
273
|
+
private writeModelToCache;
|
|
274
|
+
/** Cache Storage keys on a URL, so the S3 key is expressed as one. It is never requested. */
|
|
275
|
+
private modelCacheUrl;
|
|
276
|
+
/**
|
|
277
|
+
* Fits the camera's clipping planes to how big the model actually is.
|
|
278
|
+
*
|
|
279
|
+
* Depth precision depends on the far/near ratio, and the fixed 0.1 / 10000 the camera starts with
|
|
280
|
+
* is a ratio of 100000 for a model only ~10 units across. That wastes most of the depth buffer on
|
|
281
|
+
* empty space and lets nearly coincident surfaces - which captured meshes have everywhere -
|
|
282
|
+
* flicker against each other.
|
|
283
|
+
*/
|
|
284
|
+
private adjustClippingPlanes;
|
|
285
|
+
/**
|
|
286
|
+
* Swaps the PBR materials GLTFLoader builds for the plain Phong ones the .obj path produces.
|
|
287
|
+
*
|
|
288
|
+
* MeshStandardMaterial keeps a specular reflection even at metalness 0, since every dielectric
|
|
289
|
+
* reflects some light, and Fresnel drives that reflection towards white as a surface turns away
|
|
290
|
+
* from the viewer. On an aerial view of a facade most surfaces sit at exactly those grazing
|
|
291
|
+
* angles, which paints white highlights over window frames and roof edges - the same model
|
|
292
|
+
* loaded as .obj never shows them, because MTLLoader builds Phong materials and OBJLoader zeroes
|
|
293
|
+
* their specular when the .mtl declares Ns 0.
|
|
294
|
+
*
|
|
295
|
+
* Captured models carry their lighting in the texture and want no specular response at all, so
|
|
296
|
+
* matching the .obj path here is both the fix and the more faithful choice.
|
|
297
|
+
*/
|
|
298
|
+
private convertToPhongMaterials;
|
|
299
|
+
/**
|
|
300
|
+
* Renders every material double-sided.
|
|
301
|
+
*
|
|
302
|
+
* Reality capture meshes routinely contain triangles with reversed winding - an artefact of the
|
|
303
|
+
* reconstruction, not of the file. With the default FrontSide those triangles are culled, leaving
|
|
304
|
+
* scattered holes that show the scene background through the model. Against a light background
|
|
305
|
+
* they read as white specks with no discernible pattern.
|
|
306
|
+
*
|
|
307
|
+
* Drawing both sides costs a little fill rate but is what makes captured geometry render as
|
|
308
|
+
* solid; other viewers of this kind of content do the same unconditionally.
|
|
309
|
+
*/
|
|
310
|
+
private forceDoubleSidedMaterials;
|
|
311
|
+
/**
|
|
312
|
+
* Computes real vertex normals for any geometry that arrived without them.
|
|
313
|
+
*
|
|
314
|
+
* A glTF without a NORMAL attribute is spec-compliant, and GLTFLoader handles it by switching the
|
|
315
|
+
* material to flatShading, which makes the shader derive the normal per pixel from screen-space
|
|
316
|
+
* derivatives. That derivation collapses on the degenerate, needle-thin triangles that reality
|
|
317
|
+
* capture meshes are full of: the face has almost no area, the derived normal is meaningless, and
|
|
318
|
+
* lighting blows those fragments out to white specks - densest exactly where fine detail is.
|
|
319
|
+
*
|
|
320
|
+
* Computing normals from the geometry instead is robust to those triangles, since a degenerate
|
|
321
|
+
* face contributes almost nothing to the average and its neighbours dominate.
|
|
322
|
+
*/
|
|
323
|
+
private ensureVertexNormals;
|
|
324
|
+
/**
|
|
325
|
+
* Configures how every texture in a model is sampled: maximum anisotropy, and no mipmapping.
|
|
326
|
+
*
|
|
327
|
+
* Photogrammetry atlases pack their UV islands with unused gutter pixels between them. Mipmapping
|
|
328
|
+
* averages neighbouring pixels at each level, so those gutters bleed further into the islands the
|
|
329
|
+
* smaller the level gets - and viewing the whole model at once uses the smallest levels, painting
|
|
330
|
+
* bright gutter pixels over the edges of fine detail. Sampling the full-resolution image avoids
|
|
331
|
+
* that, at the cost of some shimmer on minified surfaces.
|
|
332
|
+
*
|
|
333
|
+
* Note that anisotropy only takes effect while mipmapping is on, so with the settings below it is
|
|
334
|
+
* inert; it is set anyway so the sampling stays correct for anyone who re-enables mipmaps here.
|
|
335
|
+
*/
|
|
336
|
+
private tuneTextureSampling;
|
|
337
|
+
/**
|
|
338
|
+
* Loads an OBJ model with MTL.
|
|
339
|
+
* The heavy text-parsing step runs in a Web Worker when one has been registered via
|
|
340
|
+
* setObjParserWorker (see there for why the app, not this library, provides it); this keeps
|
|
341
|
+
* the main thread free while large architecture models (millions of vertices) are parsed.
|
|
342
|
+
* Falls back to parsing on the main thread otherwise.
|
|
343
|
+
*/
|
|
344
|
+
loadObjModel(mtlUrl: string, objUrl: string, cameraPosition?: {
|
|
345
|
+
x: number;
|
|
346
|
+
y: number;
|
|
347
|
+
z: number;
|
|
348
|
+
}, rotation?: {
|
|
349
|
+
x: number;
|
|
350
|
+
y: number;
|
|
351
|
+
z: number;
|
|
352
|
+
}): void;
|
|
353
|
+
/** Centers, scales and adds a loaded object to the scene, then frames the camera on it. */
|
|
354
|
+
private placeLoadedObjectInScene;
|
|
355
|
+
/**
|
|
356
|
+
* Keeps the drawing buffer and camera matched to the canvas as it is resized.
|
|
357
|
+
*
|
|
358
|
+
* renderer.setSize() writes explicit pixel width/height into the canvas's style, overriding the
|
|
359
|
+
* 100%/100% it starts with. Without this, the canvas keeps whatever size it had at startup: hiding
|
|
360
|
+
* the top menu grows the container, the canvas does not follow, and the strip that opens up shows
|
|
361
|
+
* the page behind it instead of the scene.
|
|
362
|
+
*
|
|
363
|
+
* The parent is observed rather than the canvas, because resizing the canvas is what this reacts
|
|
364
|
+
* to - watching the element we resize would retrigger the observer.
|
|
365
|
+
*/
|
|
366
|
+
private observeCanvasResize;
|
|
367
|
+
/**
|
|
368
|
+
* Starts the alignment walkthrough: pick a tag, then click where it really is, three times.
|
|
369
|
+
*
|
|
370
|
+
* Existing tags are the correspondences - they already carry the stored coordinate, so the person
|
|
371
|
+
* only has to supply the matching spot on the model. No new records are written; nothing is
|
|
372
|
+
* applied until solveAlignmentFromPairs() succeeds.
|
|
373
|
+
*/
|
|
374
|
+
startTagAlignment(): void;
|
|
375
|
+
cancelTagAlignment(): void;
|
|
376
|
+
isAligningTags(): boolean;
|
|
377
|
+
/**
|
|
378
|
+
* Whether the pointer should be showing the placement ghost.
|
|
379
|
+
*
|
|
380
|
+
* True while placing a tag, and while calibrating once a tag has been picked - that second click
|
|
381
|
+
* is the same question ("where on the model?") and deserves the same aiming aid. Not true for the
|
|
382
|
+
* first half of a pair, which is a click on a tag, not on the model.
|
|
383
|
+
*/
|
|
384
|
+
private isPreviewingPlacement;
|
|
385
|
+
/** Whether a tag is currently registered for this element. */
|
|
386
|
+
hasTag(elementId: string): boolean;
|
|
387
|
+
/**
|
|
388
|
+
* Solves and applies the alignment collected so far, returning it so the caller can persist it.
|
|
389
|
+
*
|
|
390
|
+
* Returns null when the three tags turn out to be collinear, in which case nothing is applied and
|
|
391
|
+
* the walkthrough stays open for the person to pick a better third point.
|
|
392
|
+
*/
|
|
393
|
+
solveAlignmentFromPairs(): ThreeJsAlignment | null;
|
|
394
|
+
/** Handles a click while the alignment walkthrough is running. */
|
|
395
|
+
private handleAlignmentClick;
|
|
396
|
+
/** Arms tag placement: the next click on the model drops a tag instead of being ignored. */
|
|
397
|
+
startTagPlacement(poiType?: PoiType | null): void;
|
|
398
|
+
/** Leaves placement mode without placing anything. */
|
|
399
|
+
cancelTagPlacement(): void;
|
|
400
|
+
/**
|
|
401
|
+
* Shows a translucent tag under the cursor while placing, so the spot is visible before commiting.
|
|
402
|
+
*
|
|
403
|
+
* Matterport does this with a tag that follows the pointer; the equivalent here is a preview
|
|
404
|
+
* sprite that gets removed the moment the real tag takes its place.
|
|
405
|
+
*/
|
|
406
|
+
private updatePlacementPreview;
|
|
407
|
+
/**
|
|
408
|
+
* The point under the cursor, read from the depth buffer instead of raycast, in tag space.
|
|
409
|
+
*
|
|
410
|
+
* A raycast walks every triangle of the mesh it hits: on a captured model that is millions of
|
|
411
|
+
* them, tens of milliseconds, every time the pointer moves. Coalescing to one per frame did not
|
|
412
|
+
* help, because the cost is the pick itself - it simply became one slow frame instead of several.
|
|
413
|
+
*
|
|
414
|
+
* Rendering the model's depth into a single pixel under the cursor answers the same question on
|
|
415
|
+
* the GPU in constant time, whatever the model's size. The camera's view offset is what narrows
|
|
416
|
+
* the whole image down to that one pixel, so the value read back is the depth the cursor is over.
|
|
417
|
+
*
|
|
418
|
+
* Used for the ghost only. The click that actually places a tag still raycasts: it is one pick,
|
|
419
|
+
* its cost does not matter, and it yields the surface normal, which depth alone cannot give.
|
|
420
|
+
*/
|
|
421
|
+
private pickPointOnGpu;
|
|
422
|
+
private removePlacementPreview;
|
|
423
|
+
/** Shows or hides one tag, leaving it registered so it can be brought back without reloading. */
|
|
424
|
+
setTagVisibility(elementId: string, visible: boolean): void;
|
|
425
|
+
/**
|
|
426
|
+
* Shows only tags of the given types, hiding the rest.
|
|
427
|
+
*
|
|
428
|
+
* Passing null shows everything - the "no filter" state, distinct from an empty array, which is a
|
|
429
|
+
* filter that happens to match nothing.
|
|
430
|
+
*/
|
|
431
|
+
setVisibleTagTypes(types: PoiType[] | null): void;
|
|
432
|
+
/**
|
|
433
|
+
* Moves the camera to look at a tag, mirroring Matterport's goToTag.
|
|
434
|
+
*
|
|
435
|
+
* Animated rather than teleported: jumping the camera loses the viewer's sense of where they were
|
|
436
|
+
* relative to the model. The approach distance follows the model's size so it frames sensibly
|
|
437
|
+
* whatever the units are.
|
|
438
|
+
*/
|
|
439
|
+
goToTag(elementId: string): void;
|
|
440
|
+
/** Advances the camera animation started by goToTag, if one is running. */
|
|
441
|
+
private stepCameraTween;
|
|
442
|
+
isPlacingTag(): boolean;
|
|
443
|
+
/** The spot picked by the last placement, for whoever commits it to a POI. */
|
|
444
|
+
getPendingTagPlacement(): ThreeJsTagPlacement | null;
|
|
445
|
+
/**
|
|
446
|
+
* Raycasts a click against the model's geometry and returns the hit, in world space.
|
|
447
|
+
*
|
|
448
|
+
* Returns null when the click missed, which is a normal outcome - the user clicked the sky - and
|
|
449
|
+
* leaves whatever mode is armed still armed, so they can simply try again.
|
|
450
|
+
*/
|
|
451
|
+
private raycastModel;
|
|
452
|
+
/**
|
|
453
|
+
* Turns a click into a point on the model, in the frame tag coordinates are persisted in.
|
|
454
|
+
*
|
|
455
|
+
* That frame is the tag group's, not the model's. A stored coordinate is read back as a sprite
|
|
456
|
+
* position inside that group, so a coordinate captured in any other frame comes back displaced by
|
|
457
|
+
* exactly however far the alignment moves the group. The two frames agree only while the
|
|
458
|
+
* alignment is identity, which is why this stayed invisible until a model was calibrated.
|
|
459
|
+
*/
|
|
460
|
+
private pickPlacementOnModel;
|
|
461
|
+
/**
|
|
462
|
+
* Where the user says a tag really belongs, in the space the solved alignment maps onto.
|
|
463
|
+
*
|
|
464
|
+
* setTagAlignment writes the solution to the tag group's own transform, which is expressed
|
|
465
|
+
* relative to its parent - so the target has to be in the parent's frame. Measuring it in the
|
|
466
|
+
* group's own frame instead would fold the current alignment into the answer, making each
|
|
467
|
+
* calibration depend on the one before it.
|
|
468
|
+
*/
|
|
469
|
+
private pickAlignmentTarget;
|
|
470
|
+
/**
|
|
471
|
+
* Sets the transform mapping captured tag coordinates onto this model's space.
|
|
472
|
+
*
|
|
473
|
+
* Every tag comes from the same source space, so this is one transform on the group rather than
|
|
474
|
+
* per-tag maths - which also means it applies retroactively to tags already placed, and can be
|
|
475
|
+
* adjusted live while someone dials the numbers in.
|
|
476
|
+
*
|
|
477
|
+
* Passing null resets to identity, i.e. tag coordinates are already in model space.
|
|
478
|
+
*/
|
|
479
|
+
setTagAlignment(alignment: ThreeJsAlignment | null): void;
|
|
480
|
+
/**
|
|
481
|
+
* Adds a tag, or moves/restyles the one already registered for this element.
|
|
482
|
+
*
|
|
483
|
+
* Upsert rather than a declarative "here is the full list" call, because tags arrive per type
|
|
484
|
+
* (equipment, then tickets, then measurements...) from separate subscriptions - replacing the set
|
|
485
|
+
* on each of those would wipe the types that came before.
|
|
486
|
+
*/
|
|
487
|
+
upsertTag(tag: ThreeJsTag): void;
|
|
488
|
+
/** Removes the tag registered for an element, if there is one. */
|
|
489
|
+
removeTag(elementId: string): void;
|
|
490
|
+
/** Removes every tag from the scene. */
|
|
491
|
+
clearTags(): void;
|
|
492
|
+
/** Loads an icon into an existing tag, leaving the fallback disc in place if it fails. */
|
|
493
|
+
private applyTagIcon;
|
|
494
|
+
/** Draws the default tag marker: a filled disc with a white ring, so it reads on any background. */
|
|
495
|
+
private createTagTexture;
|
|
496
|
+
/**
|
|
497
|
+
* Sizes a tag relative to the model.
|
|
498
|
+
*
|
|
499
|
+
* A sprite inherits the model's scale and the alignment's, so a fixed size would come out tiny on
|
|
500
|
+
* a large model and huge on a small one. Dividing by the group's world scale keeps tags the same
|
|
501
|
+
* size on screen whatever the model's units are.
|
|
502
|
+
*/
|
|
503
|
+
private scaleTagForModel;
|
|
504
|
+
/**
|
|
505
|
+
* Adds secondary interactive objects (e.g., POIs, sensors) to the scene.
|
|
506
|
+
*/
|
|
507
|
+
updateObjsToScene(objsAdded: ObjAdded[]): void;
|
|
508
|
+
private onMouseMove;
|
|
509
|
+
private onMouseClick;
|
|
510
|
+
/**
|
|
511
|
+
* Escape cancels whatever the pointer is currently committed to.
|
|
512
|
+
*
|
|
513
|
+
* Emits through the same subjects the explicit Cancel buttons use, so the UI driving them follows
|
|
514
|
+
* along instead of being left showing a mode that is no longer armed.
|
|
515
|
+
*/
|
|
516
|
+
private onKeyDown;
|
|
517
|
+
private animate;
|
|
518
|
+
destroy(): void;
|
|
519
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<ThreeJsService, never>;
|
|
520
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<ThreeJsService>;
|
|
521
|
+
}
|