@smarterplan/ngx-smarterplan-core 1.4.14 → 1.4.16
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/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 +1 -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 +448 -62
- package/esm2022/lib/services/matterport-tag.service.mjs +1 -1
- package/esm2022/lib/services/matterport.service.mjs +17 -17
- package/esm2022/lib/services/models/affectation.service.mjs +1 -1
- package/esm2022/lib/services/models/base-object.service.mjs +1 -1
- 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 +20 -1
- package/esm2022/lib/services/models/equipment.service.mjs +4 -2
- package/esm2022/lib/services/models/event.service.mjs +1 -1
- package/esm2022/lib/services/models/feature.service.mjs +1 -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 +17 -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 +25 -6
- package/esm2022/lib/services/models/template.service.mjs +1 -1
- package/esm2022/lib/services/models/ticket.service.mjs +1 -1
- 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 +1 -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 +1306 -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 +263 -5
- package/esm2022/lib/services/zone-drawer.service.mjs +1 -1
- package/esm2022/lib/services/zoneChange.service.mjs +1 -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 +2759 -551
- package/fesm2022/smarterplan-ngx-smarterplan-core.mjs.map +1 -1
- package/lib/helpers.service.d.ts +43 -0
- package/lib/services/matterport-tag-copy.service.d.ts +137 -17
- package/lib/services/models/domain.service.d.ts +8 -0
- package/lib/services/models/feature.service.d.ts +0 -6
- package/lib/services/models/mission.service.d.ts +5 -0
- package/lib/services/models/space.service.d.ts +15 -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 +423 -0
- package/lib/services/viewer.service.d.ts +114 -1
- package/lib/types.service.d.ts +7 -1
- package/package.json +1 -1
- package/public-api.d.ts +2 -0
- package/src/assets/vs-app.json +67 -67
|
@@ -0,0 +1,423 @@
|
|
|
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
|
+
/** Icon drawn on the tag. Falls back to a plain disc when absent or it fails to load. */
|
|
24
|
+
iconUrl?: string | null;
|
|
25
|
+
/** Tint for the fallback disc, as a CSS colour. */
|
|
26
|
+
color?: string;
|
|
27
|
+
}
|
|
28
|
+
/** What a click on a tag reports: enough to route to the element's detail page. */
|
|
29
|
+
export interface ThreeJsTagClick {
|
|
30
|
+
elementId: string;
|
|
31
|
+
poiType: PoiType | null;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* A spot picked on the model for a new tag.
|
|
35
|
+
*
|
|
36
|
+
* Both vectors are in the model's own coordinate space, which is what POI.coordinate stores. Scene
|
|
37
|
+
* coordinates deliberately never leave this service: the model is scaled to a fixed size on load, so
|
|
38
|
+
* a scene-space position would silently mean somewhere else the moment the .glb is re-exported.
|
|
39
|
+
*/
|
|
40
|
+
export interface ThreeJsTagPlacement {
|
|
41
|
+
position: {
|
|
42
|
+
x: number;
|
|
43
|
+
y: number;
|
|
44
|
+
z: number;
|
|
45
|
+
};
|
|
46
|
+
/** Surface normal at the picked point - Matterport stores the same thing in POI.metadata. */
|
|
47
|
+
normal: {
|
|
48
|
+
x: number;
|
|
49
|
+
y: number;
|
|
50
|
+
z: number;
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/** One tag seen in both spaces: where it is stored, and where it really sits on the model. */
|
|
54
|
+
export interface ThreeJsAlignmentPair {
|
|
55
|
+
source: {
|
|
56
|
+
x: number;
|
|
57
|
+
y: number;
|
|
58
|
+
z: number;
|
|
59
|
+
};
|
|
60
|
+
target: {
|
|
61
|
+
x: number;
|
|
62
|
+
y: number;
|
|
63
|
+
z: number;
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Solves the transform that maps stored tag coordinates onto the loaded model.
|
|
68
|
+
*
|
|
69
|
+
* Takes exactly the correspondences a person can produce by hand: three tags, each pointed at twice
|
|
70
|
+
* - once where it currently sits, once where it belongs. That is the minimum that pins down all
|
|
71
|
+
* three parts of the answer. One pair fixes only translation; two leave the rotation about the line
|
|
72
|
+
* between them free; three resolve it.
|
|
73
|
+
*
|
|
74
|
+
* Returns null when the points are collinear or degenerate, because a wrong alignment applied
|
|
75
|
+
* silently is worse than none.
|
|
76
|
+
*/
|
|
77
|
+
export declare function solveThreeJsAlignment(pairs: ThreeJsAlignmentPair[]): ThreeJsAlignment | null;
|
|
78
|
+
export interface ObjAdded {
|
|
79
|
+
id: string;
|
|
80
|
+
name: string;
|
|
81
|
+
file: string;
|
|
82
|
+
position: {
|
|
83
|
+
x: number;
|
|
84
|
+
y: number;
|
|
85
|
+
z: number;
|
|
86
|
+
};
|
|
87
|
+
rotation: {
|
|
88
|
+
x: number;
|
|
89
|
+
y: number;
|
|
90
|
+
z: number;
|
|
91
|
+
};
|
|
92
|
+
scale: {
|
|
93
|
+
x: number;
|
|
94
|
+
y: number;
|
|
95
|
+
z: number;
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
export declare class ThreeJsService {
|
|
99
|
+
private ngZone;
|
|
100
|
+
private scene;
|
|
101
|
+
private camera;
|
|
102
|
+
private renderer;
|
|
103
|
+
private controls;
|
|
104
|
+
private animationFrameId;
|
|
105
|
+
private raycaster;
|
|
106
|
+
private mouse;
|
|
107
|
+
private isDraggingCamera;
|
|
108
|
+
private isDraggingTimer;
|
|
109
|
+
private clickableObjects;
|
|
110
|
+
objectHovered$: Subject<{
|
|
111
|
+
id: string;
|
|
112
|
+
event: MouseEvent;
|
|
113
|
+
}>;
|
|
114
|
+
objectClicked$: Subject<string>;
|
|
115
|
+
/** Emits when the user clicks a tag, mirroring Matterport's tag click. */
|
|
116
|
+
tagClicked$: Subject<ThreeJsTagClick>;
|
|
117
|
+
private tagGroup;
|
|
118
|
+
private tagSprites;
|
|
119
|
+
private canvasResizeObserver?;
|
|
120
|
+
/** Aborts the in-flight model download. Parsing cannot be interrupted, only not started. */
|
|
121
|
+
private loadAbortController?;
|
|
122
|
+
private modelRoot?;
|
|
123
|
+
/** True while the next click on the model places a tag instead of orbiting. */
|
|
124
|
+
private placingTag;
|
|
125
|
+
/** Position and normal of the last placed tag, in model space, awaiting commit to a POI. */
|
|
126
|
+
private pendingTagPlacement;
|
|
127
|
+
/** Emits once a tag is dropped on the model, so the UI can confirm and persist it. */
|
|
128
|
+
tagPlaced$: Subject<ThreeJsTagPlacement>;
|
|
129
|
+
/** Translucent marker shown under the cursor while placing. */
|
|
130
|
+
private placementPreview?;
|
|
131
|
+
private aligning;
|
|
132
|
+
private alignmentPairs;
|
|
133
|
+
private alignmentSource;
|
|
134
|
+
/** Drives the alignment walkthrough's UI: how many pairs are done and what it wants next. */
|
|
135
|
+
alignmentProgress$: Subject<{
|
|
136
|
+
pairs: number;
|
|
137
|
+
awaiting: 'tag' | 'point' | null;
|
|
138
|
+
tagId?: string;
|
|
139
|
+
}>;
|
|
140
|
+
/** In-flight camera move started by goToTag, advanced by the render loop. */
|
|
141
|
+
private cameraTween;
|
|
142
|
+
/** Emits true while a heavy OBJ file is being parsed (in a worker, when available), false once done. */
|
|
143
|
+
modelLoading$: Subject<boolean>;
|
|
144
|
+
/**
|
|
145
|
+
* Download progress for GLB models, as a 0-1 fraction, or null when the total size is unknown.
|
|
146
|
+
*
|
|
147
|
+
* A null means the server did not send Content-Length (chunked responses, some proxies) and no
|
|
148
|
+
* percentage can be derived; show an indeterminate indicator rather than guessing at one.
|
|
149
|
+
*/
|
|
150
|
+
modelLoadProgress$: Subject<number>;
|
|
151
|
+
/**
|
|
152
|
+
* Emits when a load actually stopped short, which is not the same as asking it to.
|
|
153
|
+
*
|
|
154
|
+
* cancelModelLoad() can arrive after parse() has taken the main thread, and parse cannot be
|
|
155
|
+
* interrupted - the model finishes loading regardless. Only this tells the UI which of the two
|
|
156
|
+
* happened, so it does not offer to reload something that is already on screen.
|
|
157
|
+
*/
|
|
158
|
+
modelLoadCancelled$: Subject<void>;
|
|
159
|
+
private objParserWorker?;
|
|
160
|
+
private objParserPendingRequests;
|
|
161
|
+
constructor(ngZone: NgZone);
|
|
162
|
+
/**
|
|
163
|
+
* Registers a Web Worker used to parse heavy .obj files off the main thread.
|
|
164
|
+
* Must be instantiated by the consuming app (e.g. `new Worker(new URL('./obj-parser.worker', import.meta.url))`),
|
|
165
|
+
* since a library packaged with ng-packagr cannot bundle worker files itself.
|
|
166
|
+
* When no worker is provided, loadObjModel falls back to parsing on the main thread.
|
|
167
|
+
*/
|
|
168
|
+
setObjParserWorker(worker: Worker): void;
|
|
169
|
+
private requestObjParseInWorker;
|
|
170
|
+
/** Rebuilds a THREE.Mesh from the raw typed arrays sent back by the parser worker. */
|
|
171
|
+
private meshFromParsedData;
|
|
172
|
+
init(canvas: HTMLCanvasElement): void;
|
|
173
|
+
/**
|
|
174
|
+
* Path (served by the app) holding basis_transcoder.js/.wasm, which KTX2Loader needs to turn
|
|
175
|
+
* Basis Universal textures into whatever compressed format the local GPU actually supports.
|
|
176
|
+
*/
|
|
177
|
+
private ktx2TranscoderPath;
|
|
178
|
+
/** Overrides where the Basis transcoder files are served from. */
|
|
179
|
+
setKtx2TranscoderPath(path: string): void;
|
|
180
|
+
/**
|
|
181
|
+
* Loads a base 3D model in GLTF/GLB format - the preferred format for large models.
|
|
182
|
+
* Unlike .obj, geometry arrives as binary typed arrays (no text parsing to offload to a worker)
|
|
183
|
+
* and stays indexed, so far less data reaches the GPU. KTX2 textures are supported too: they
|
|
184
|
+
* stay compressed in VRAM instead of being decoded to raw pixels like JPEG/PNG.
|
|
185
|
+
*/
|
|
186
|
+
loadGLTFModel(url: string, cameraPosition?: {
|
|
187
|
+
x: number;
|
|
188
|
+
y: number;
|
|
189
|
+
z: number;
|
|
190
|
+
}, rotation?: {
|
|
191
|
+
x: number;
|
|
192
|
+
y: number;
|
|
193
|
+
z: number;
|
|
194
|
+
}): void;
|
|
195
|
+
/**
|
|
196
|
+
* Stops the model download in progress, if any.
|
|
197
|
+
*
|
|
198
|
+
* Only the download can be interrupted: parse() runs synchronously and holds the main thread until
|
|
199
|
+
* it finishes, so once it has begun there is nothing to cancel. In practice the download is the
|
|
200
|
+
* long part, which is where someone reaches for this.
|
|
201
|
+
*/
|
|
202
|
+
cancelModelLoad(): void;
|
|
203
|
+
/**
|
|
204
|
+
* Abandons the load in progress without reporting it as cancelled.
|
|
205
|
+
*
|
|
206
|
+
* Dropping the controller is what makes the difference: onError treats an abort as user-initiated
|
|
207
|
+
* only while the controller that aborted is still the current one. Tearing down or rebuilding the
|
|
208
|
+
* scene is not something the person asked for, so it must not surface as "loading cancelled".
|
|
209
|
+
*/
|
|
210
|
+
private discardCurrentLoad;
|
|
211
|
+
/**
|
|
212
|
+
* Resolves once the browser has painted at least one frame.
|
|
213
|
+
*
|
|
214
|
+
* A requestAnimationFrame callback runs *before* that frame is painted, so a single one is not
|
|
215
|
+
* enough to guarantee pending DOM changes are on screen; the second fires after the first frame
|
|
216
|
+
* has gone out. Falls back to a timeout where rAF isn't available (SSR, tests).
|
|
217
|
+
*/
|
|
218
|
+
private afterNextPaint;
|
|
219
|
+
/**
|
|
220
|
+
* Downloads a file, reporting progress on modelLoadProgress$ as it arrives.
|
|
221
|
+
*
|
|
222
|
+
* GLTFLoader.load() would report progress through XHR, but this path deliberately uses fetch() +
|
|
223
|
+
* parse() instead (see loadGLTFModel for why), so progress is read off the response stream here.
|
|
224
|
+
* That is also the more useful number: on a model of this size the download dominates, and the
|
|
225
|
+
* stream reports it as it happens rather than in one jump at the end.
|
|
226
|
+
*/
|
|
227
|
+
private fetchWithProgress;
|
|
228
|
+
/**
|
|
229
|
+
* Fits the camera's clipping planes to how big the model actually is.
|
|
230
|
+
*
|
|
231
|
+
* Depth precision depends on the far/near ratio, and the fixed 0.1 / 10000 the camera starts with
|
|
232
|
+
* is a ratio of 100000 for a model only ~10 units across. That wastes most of the depth buffer on
|
|
233
|
+
* empty space and lets nearly coincident surfaces - which captured meshes have everywhere -
|
|
234
|
+
* flicker against each other.
|
|
235
|
+
*/
|
|
236
|
+
private adjustClippingPlanes;
|
|
237
|
+
/**
|
|
238
|
+
* Swaps the PBR materials GLTFLoader builds for the plain Phong ones the .obj path produces.
|
|
239
|
+
*
|
|
240
|
+
* MeshStandardMaterial keeps a specular reflection even at metalness 0, since every dielectric
|
|
241
|
+
* reflects some light, and Fresnel drives that reflection towards white as a surface turns away
|
|
242
|
+
* from the viewer. On an aerial view of a facade most surfaces sit at exactly those grazing
|
|
243
|
+
* angles, which paints white highlights over window frames and roof edges - the same model
|
|
244
|
+
* loaded as .obj never shows them, because MTLLoader builds Phong materials and OBJLoader zeroes
|
|
245
|
+
* their specular when the .mtl declares Ns 0.
|
|
246
|
+
*
|
|
247
|
+
* Captured models carry their lighting in the texture and want no specular response at all, so
|
|
248
|
+
* matching the .obj path here is both the fix and the more faithful choice.
|
|
249
|
+
*/
|
|
250
|
+
private convertToPhongMaterials;
|
|
251
|
+
/**
|
|
252
|
+
* Renders every material double-sided.
|
|
253
|
+
*
|
|
254
|
+
* Reality capture meshes routinely contain triangles with reversed winding - an artefact of the
|
|
255
|
+
* reconstruction, not of the file. With the default FrontSide those triangles are culled, leaving
|
|
256
|
+
* scattered holes that show the scene background through the model. Against a light background
|
|
257
|
+
* they read as white specks with no discernible pattern.
|
|
258
|
+
*
|
|
259
|
+
* Drawing both sides costs a little fill rate but is what makes captured geometry render as
|
|
260
|
+
* solid; other viewers of this kind of content do the same unconditionally.
|
|
261
|
+
*/
|
|
262
|
+
private forceDoubleSidedMaterials;
|
|
263
|
+
/**
|
|
264
|
+
* Computes real vertex normals for any geometry that arrived without them.
|
|
265
|
+
*
|
|
266
|
+
* A glTF without a NORMAL attribute is spec-compliant, and GLTFLoader handles it by switching the
|
|
267
|
+
* material to flatShading, which makes the shader derive the normal per pixel from screen-space
|
|
268
|
+
* derivatives. That derivation collapses on the degenerate, needle-thin triangles that reality
|
|
269
|
+
* capture meshes are full of: the face has almost no area, the derived normal is meaningless, and
|
|
270
|
+
* lighting blows those fragments out to white specks - densest exactly where fine detail is.
|
|
271
|
+
*
|
|
272
|
+
* Computing normals from the geometry instead is robust to those triangles, since a degenerate
|
|
273
|
+
* face contributes almost nothing to the average and its neighbours dominate.
|
|
274
|
+
*/
|
|
275
|
+
private ensureVertexNormals;
|
|
276
|
+
/**
|
|
277
|
+
* Configures how every texture in a model is sampled: maximum anisotropy, and no mipmapping.
|
|
278
|
+
*
|
|
279
|
+
* Photogrammetry atlases pack their UV islands with unused gutter pixels between them. Mipmapping
|
|
280
|
+
* averages neighbouring pixels at each level, so those gutters bleed further into the islands the
|
|
281
|
+
* smaller the level gets - and viewing the whole model at once uses the smallest levels, painting
|
|
282
|
+
* bright gutter pixels over the edges of fine detail. Sampling the full-resolution image avoids
|
|
283
|
+
* that, at the cost of some shimmer on minified surfaces.
|
|
284
|
+
*
|
|
285
|
+
* Note that anisotropy only takes effect while mipmapping is on, so with the settings below it is
|
|
286
|
+
* inert; it is set anyway so the sampling stays correct for anyone who re-enables mipmaps here.
|
|
287
|
+
*/
|
|
288
|
+
private tuneTextureSampling;
|
|
289
|
+
/**
|
|
290
|
+
* Loads an OBJ model with MTL.
|
|
291
|
+
* The heavy text-parsing step runs in a Web Worker when one has been registered via
|
|
292
|
+
* setObjParserWorker (see there for why the app, not this library, provides it); this keeps
|
|
293
|
+
* the main thread free while large architecture models (millions of vertices) are parsed.
|
|
294
|
+
* Falls back to parsing on the main thread otherwise.
|
|
295
|
+
*/
|
|
296
|
+
loadObjModel(mtlUrl: string, objUrl: string, cameraPosition?: {
|
|
297
|
+
x: number;
|
|
298
|
+
y: number;
|
|
299
|
+
z: number;
|
|
300
|
+
}, rotation?: {
|
|
301
|
+
x: number;
|
|
302
|
+
y: number;
|
|
303
|
+
z: number;
|
|
304
|
+
}): void;
|
|
305
|
+
/** Centers, scales and adds a loaded object to the scene, then frames the camera on it. */
|
|
306
|
+
private placeLoadedObjectInScene;
|
|
307
|
+
/**
|
|
308
|
+
* Keeps the drawing buffer and camera matched to the canvas as it is resized.
|
|
309
|
+
*
|
|
310
|
+
* renderer.setSize() writes explicit pixel width/height into the canvas's style, overriding the
|
|
311
|
+
* 100%/100% it starts with. Without this, the canvas keeps whatever size it had at startup: hiding
|
|
312
|
+
* the top menu grows the container, the canvas does not follow, and the strip that opens up shows
|
|
313
|
+
* the page behind it instead of the scene.
|
|
314
|
+
*
|
|
315
|
+
* The parent is observed rather than the canvas, because resizing the canvas is what this reacts
|
|
316
|
+
* to - watching the element we resize would retrigger the observer.
|
|
317
|
+
*/
|
|
318
|
+
private observeCanvasResize;
|
|
319
|
+
/**
|
|
320
|
+
* Starts the alignment walkthrough: pick a tag, then click where it really is, three times.
|
|
321
|
+
*
|
|
322
|
+
* Existing tags are the correspondences - they already carry the stored coordinate, so the person
|
|
323
|
+
* only has to supply the matching spot on the model. No new records are written; nothing is
|
|
324
|
+
* applied until solveAlignmentFromPairs() succeeds.
|
|
325
|
+
*/
|
|
326
|
+
startTagAlignment(): void;
|
|
327
|
+
cancelTagAlignment(): void;
|
|
328
|
+
isAligningTags(): boolean;
|
|
329
|
+
/**
|
|
330
|
+
* Solves and applies the alignment collected so far, returning it so the caller can persist it.
|
|
331
|
+
*
|
|
332
|
+
* Returns null when the three tags turn out to be collinear, in which case nothing is applied and
|
|
333
|
+
* the walkthrough stays open for the person to pick a better third point.
|
|
334
|
+
*/
|
|
335
|
+
solveAlignmentFromPairs(): ThreeJsAlignment | null;
|
|
336
|
+
/** Handles a click while the alignment walkthrough is running. */
|
|
337
|
+
private handleAlignmentClick;
|
|
338
|
+
/** Arms tag placement: the next click on the model drops a tag instead of being ignored. */
|
|
339
|
+
startTagPlacement(): void;
|
|
340
|
+
/** Leaves placement mode without placing anything. */
|
|
341
|
+
cancelTagPlacement(): void;
|
|
342
|
+
/**
|
|
343
|
+
* Shows a translucent tag under the cursor while placing, so the spot is visible before commiting.
|
|
344
|
+
*
|
|
345
|
+
* Matterport does this with a tag that follows the pointer; the equivalent here is a preview
|
|
346
|
+
* sprite that gets removed the moment the real tag takes its place.
|
|
347
|
+
*/
|
|
348
|
+
private updatePlacementPreview;
|
|
349
|
+
private removePlacementPreview;
|
|
350
|
+
/** Shows or hides one tag, leaving it registered so it can be brought back without reloading. */
|
|
351
|
+
setTagVisibility(elementId: string, visible: boolean): void;
|
|
352
|
+
/**
|
|
353
|
+
* Shows only tags of the given types, hiding the rest.
|
|
354
|
+
*
|
|
355
|
+
* Passing null shows everything - the "no filter" state, distinct from an empty array, which is a
|
|
356
|
+
* filter that happens to match nothing.
|
|
357
|
+
*/
|
|
358
|
+
setVisibleTagTypes(types: PoiType[] | null): void;
|
|
359
|
+
/**
|
|
360
|
+
* Moves the camera to look at a tag, mirroring Matterport's goToTag.
|
|
361
|
+
*
|
|
362
|
+
* Animated rather than teleported: jumping the camera loses the viewer's sense of where they were
|
|
363
|
+
* relative to the model. The approach distance follows the model's size so it frames sensibly
|
|
364
|
+
* whatever the units are.
|
|
365
|
+
*/
|
|
366
|
+
goToTag(elementId: string): void;
|
|
367
|
+
/** Advances the camera animation started by goToTag, if one is running. */
|
|
368
|
+
private stepCameraTween;
|
|
369
|
+
isPlacingTag(): boolean;
|
|
370
|
+
/** The spot picked by the last placement, for whoever commits it to a POI. */
|
|
371
|
+
getPendingTagPlacement(): ThreeJsTagPlacement | null;
|
|
372
|
+
/**
|
|
373
|
+
* Turns a click into a point on the model, in model space.
|
|
374
|
+
*
|
|
375
|
+
* Returns null when the click missed the geometry, which is a normal outcome - the user clicked
|
|
376
|
+
* the sky - and leaves placement mode armed so they can simply try again.
|
|
377
|
+
*/
|
|
378
|
+
private pickPlacementOnModel;
|
|
379
|
+
/**
|
|
380
|
+
* Sets the transform mapping captured tag coordinates onto this model's space.
|
|
381
|
+
*
|
|
382
|
+
* Every tag comes from the same source space, so this is one transform on the group rather than
|
|
383
|
+
* per-tag maths - which also means it applies retroactively to tags already placed, and can be
|
|
384
|
+
* adjusted live while someone dials the numbers in.
|
|
385
|
+
*
|
|
386
|
+
* Passing null resets to identity, i.e. tag coordinates are already in model space.
|
|
387
|
+
*/
|
|
388
|
+
setTagAlignment(alignment: ThreeJsAlignment | null): void;
|
|
389
|
+
/**
|
|
390
|
+
* Adds a tag, or moves/restyles the one already registered for this element.
|
|
391
|
+
*
|
|
392
|
+
* Upsert rather than a declarative "here is the full list" call, because tags arrive per type
|
|
393
|
+
* (equipment, then tickets, then measurements...) from separate subscriptions - replacing the set
|
|
394
|
+
* on each of those would wipe the types that came before.
|
|
395
|
+
*/
|
|
396
|
+
upsertTag(tag: ThreeJsTag): void;
|
|
397
|
+
/** Removes the tag registered for an element, if there is one. */
|
|
398
|
+
removeTag(elementId: string): void;
|
|
399
|
+
/** Removes every tag from the scene. */
|
|
400
|
+
clearTags(): void;
|
|
401
|
+
/** Loads an icon into an existing tag, leaving the fallback disc in place if it fails. */
|
|
402
|
+
private applyTagIcon;
|
|
403
|
+
/** Draws the default tag marker: a filled disc with a white ring, so it reads on any background. */
|
|
404
|
+
private createTagTexture;
|
|
405
|
+
/**
|
|
406
|
+
* Sizes a tag relative to the model.
|
|
407
|
+
*
|
|
408
|
+
* A sprite inherits the model's scale and the alignment's, so a fixed size would come out tiny on
|
|
409
|
+
* a large model and huge on a small one. Dividing by the group's world scale keeps tags the same
|
|
410
|
+
* size on screen whatever the model's units are.
|
|
411
|
+
*/
|
|
412
|
+
private scaleTagForModel;
|
|
413
|
+
/**
|
|
414
|
+
* Adds secondary interactive objects (e.g., POIs, sensors) to the scene.
|
|
415
|
+
*/
|
|
416
|
+
updateObjsToScene(objsAdded: ObjAdded[]): void;
|
|
417
|
+
private onMouseMove;
|
|
418
|
+
private onMouseClick;
|
|
419
|
+
private animate;
|
|
420
|
+
destroy(): void;
|
|
421
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<ThreeJsService, never>;
|
|
422
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<ThreeJsService>;
|
|
423
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Subject } from 'rxjs';
|
|
2
2
|
import { MatterportService } from './matterport.service';
|
|
3
|
+
import { ThreeJsService } from './threejs.service';
|
|
3
4
|
import { BaseTagService } from './tag.service';
|
|
4
5
|
import { DbObjectType, POI, PoiType, Comment, Equipment, Ticket, Feature, SpModule } from '../types.service';
|
|
5
6
|
import { PoiService } from './models/poi.service';
|
|
@@ -8,6 +9,7 @@ export declare class ViewerService {
|
|
|
8
9
|
private readonly sdkKey;
|
|
9
10
|
private poiService;
|
|
10
11
|
private matterportService;
|
|
12
|
+
private threeJsService;
|
|
11
13
|
private viewerMode;
|
|
12
14
|
currentModel3D: string;
|
|
13
15
|
private tourUrl;
|
|
@@ -50,7 +52,7 @@ export declare class ViewerService {
|
|
|
50
52
|
};
|
|
51
53
|
};
|
|
52
54
|
private isObjectLoaded;
|
|
53
|
-
constructor(sdkKey: string, poiService: PoiService, matterportService: MatterportService);
|
|
55
|
+
constructor(sdkKey: string, poiService: PoiService, matterportService: MatterportService, threeJsService: ThreeJsService);
|
|
54
56
|
checkMode(indexMode: string): boolean;
|
|
55
57
|
setMode(indexMode: string): void;
|
|
56
58
|
setTourUrl(options: {
|
|
@@ -82,7 +84,55 @@ export declare class ViewerService {
|
|
|
82
84
|
*/
|
|
83
85
|
addObjectTagsToViewer(objects: DbObjectType[]): Promise<void>;
|
|
84
86
|
actionAddObjectFromPOI(object: DbObjectType, poi: POI): Promise<any>;
|
|
87
|
+
/**
|
|
88
|
+
* Draws a POI as a tag in the Three.js scene.
|
|
89
|
+
*
|
|
90
|
+
* Both viewers are fed by the same subscriptions on the same POI records, so a tag created in
|
|
91
|
+
* either one shows up in the other without any syncing of its own - this is just the Three.js end
|
|
92
|
+
* of the pipeline that already drives Matterport.
|
|
93
|
+
*/
|
|
94
|
+
private addThreeJsTagFromPOI;
|
|
95
|
+
/** Moves the Three.js camera to frame a tag, mirroring Matterport's goToTag. */
|
|
96
|
+
goToThreeJsTag(elementId: string): void;
|
|
97
|
+
/** Shows only tags of the given POI types; null clears the filter. */
|
|
98
|
+
setVisibleThreeJsTagTypes(types: PoiType[] | null): void;
|
|
99
|
+
/**
|
|
100
|
+
* Alignment walkthrough: pick a tag, then click where it really sits, three times over.
|
|
101
|
+
*
|
|
102
|
+
* Uses tags that already exist, so nothing is created and nothing is written until the caller
|
|
103
|
+
* saves the result. See threeJsAlignmentProgress$ for what the UI should be asking for.
|
|
104
|
+
*/
|
|
105
|
+
startThreeJsTagAlignment(): void;
|
|
106
|
+
cancelThreeJsTagAlignment(): void;
|
|
107
|
+
get threeJsAlignmentProgress$(): Subject<{
|
|
108
|
+
pairs: number;
|
|
109
|
+
awaiting: "tag" | "point";
|
|
110
|
+
tagId?: string;
|
|
111
|
+
}>;
|
|
112
|
+
/**
|
|
113
|
+
* Solves the alignment and applies it to the scene, returning it as the JSON to store in the
|
|
114
|
+
* capture's metadata. Null means the three tags were collinear and nothing was applied.
|
|
115
|
+
*/
|
|
116
|
+
solveThreeJsTagAlignment(existingMetadata?: string): {
|
|
117
|
+
alignment: any;
|
|
118
|
+
metadata: string;
|
|
119
|
+
} | null;
|
|
120
|
+
/** Shows or hides a single Three.js tag. */
|
|
121
|
+
setThreeJsTagVisibility(elementId: string, visible: boolean): void;
|
|
85
122
|
action_delete_element(element: DbObjectType): Promise<void>;
|
|
123
|
+
/** Emits when a Three.js model load actually stopped short, rather than merely being asked to. */
|
|
124
|
+
get threeJsModelLoadCancelled$(): Subject<void>;
|
|
125
|
+
/** Stops the Three.js model download in progress. */
|
|
126
|
+
cancelThreeJsModelLoad(): void;
|
|
127
|
+
/** Emits the element id of a tag clicked in the Three.js scene. */
|
|
128
|
+
get threeJsTagClicked$(): Subject<import("./threejs.service").ThreeJsTagClick>;
|
|
129
|
+
/**
|
|
130
|
+
* Applies the visit's stored tag alignment, mapping captured tag coordinates onto this model.
|
|
131
|
+
*
|
|
132
|
+
* Safe to call before the model or any tag exists: it is a transform on the group both end up
|
|
133
|
+
* under, so it applies to whatever arrives later.
|
|
134
|
+
*/
|
|
135
|
+
setThreeJsTagAlignmentFromMetadata(metadata: string): void;
|
|
86
136
|
/**
|
|
87
137
|
* Enable tag placement menu to add a new element depending on its type (TICKET, DATA, EQUIPMENT etc)
|
|
88
138
|
*/
|
|
@@ -104,6 +154,17 @@ export declare class ViewerService {
|
|
|
104
154
|
action_toolbox_mesure(): void;
|
|
105
155
|
action_toolbox_cancel_mesure(): void;
|
|
106
156
|
action_update_poi(poi: POI, isValid: boolean, element: DbObjectType): Promise<POI>;
|
|
157
|
+
/**
|
|
158
|
+
* Writes the spot picked in the Three.js scene into the POI, mirroring the Matterport branch.
|
|
159
|
+
*
|
|
160
|
+
* The coordinate goes in the same field and the same space Matterport writes, which is what makes
|
|
161
|
+
* a tag created here show up in the right place there - and the other way round.
|
|
162
|
+
*/
|
|
163
|
+
private commitThreeJsTagPlacement;
|
|
164
|
+
/** Emits when a tag is dropped on the model in the Three.js scene. */
|
|
165
|
+
get threeJsTagPlaced$(): Subject<import("./threejs.service").ThreeJsTagPlacement>;
|
|
166
|
+
/** Leaves Three.js tag placement mode without placing anything. */
|
|
167
|
+
cancelThreeJsTagPlacement(): void;
|
|
107
168
|
editOnlyTagStyleForPoi(poi: POI, object: DbObjectType): void;
|
|
108
169
|
addIconAndSetForPoi(poi: POI, iconPath: string): void;
|
|
109
170
|
changeToRegistredIconForPoi(poi: POI, iconName: string): void;
|
|
@@ -114,6 +175,58 @@ export declare class ViewerService {
|
|
|
114
175
|
* @param element
|
|
115
176
|
*/
|
|
116
177
|
action_move_poi_visually(poi: POI, element: Ticket | Feature | Equipment | Comment): Promise<void>;
|
|
178
|
+
initThreeJs(canvas: HTMLCanvasElement): void;
|
|
179
|
+
/**
|
|
180
|
+
* Loads a 3D model into the Three.js scene (GLTF).
|
|
181
|
+
* Only works when the viewer is in 'threejs' mode.
|
|
182
|
+
*/
|
|
183
|
+
loadGLTFModel(url: string, cameraPosition?: {
|
|
184
|
+
x: number;
|
|
185
|
+
y: number;
|
|
186
|
+
z: number;
|
|
187
|
+
}, rotation?: {
|
|
188
|
+
x: number;
|
|
189
|
+
y: number;
|
|
190
|
+
z: number;
|
|
191
|
+
}): void;
|
|
192
|
+
/**
|
|
193
|
+
* Loads a GLB stored in S3, given its key (e.g. `visits/<id>/model.glb`).
|
|
194
|
+
*
|
|
195
|
+
* Resolves the key to a signed URL first, then loads it like any other. The signed URL only
|
|
196
|
+
* covers authentication - the bucket still needs a CORS rule allowing GET from the app's origin,
|
|
197
|
+
* or the browser blocks the download before it starts.
|
|
198
|
+
*
|
|
199
|
+
* `placementMetadata` is the capture's stored placement; when absent or unusable the viewer falls
|
|
200
|
+
* back to framing the model automatically.
|
|
201
|
+
*/
|
|
202
|
+
loadGLTFModelFromS3(key: string, placementMetadata?: string): Promise<void>;
|
|
203
|
+
/**
|
|
204
|
+
* Loads a 3D model into the Three.js scene (OBJ).
|
|
205
|
+
*/
|
|
206
|
+
loadObjModel(mtlUrl: string, objUrl: string, cameraPosition?: {
|
|
207
|
+
x: number;
|
|
208
|
+
y: number;
|
|
209
|
+
z: number;
|
|
210
|
+
}, rotation?: {
|
|
211
|
+
x: number;
|
|
212
|
+
y: number;
|
|
213
|
+
z: number;
|
|
214
|
+
}): void;
|
|
215
|
+
get threeJsObjectHovered$(): Subject<{
|
|
216
|
+
id: string;
|
|
217
|
+
event: MouseEvent;
|
|
218
|
+
}>;
|
|
219
|
+
get threeJsObjectClicked$(): Subject<string>;
|
|
220
|
+
/** Emits true while a heavy .obj file is being parsed (in a worker, when available), false once done. */
|
|
221
|
+
get threeJsModelLoading$(): Subject<boolean>;
|
|
222
|
+
/** Download progress for GLB models as a 0-1 fraction, or null when the size isn't known upfront. */
|
|
223
|
+
get threeJsModelLoadProgress$(): Subject<number>;
|
|
224
|
+
/**
|
|
225
|
+
* Registers the Web Worker used to parse heavy .obj files off the main thread.
|
|
226
|
+
* Must be created by the app (see obj-parser.worker.ts), since this library can't bundle worker files.
|
|
227
|
+
*/
|
|
228
|
+
setObjParserWorker(worker: Worker): void;
|
|
229
|
+
addThreeJsObjects(objs: any[]): void;
|
|
117
230
|
initMatterportSdk(spaceID: string, model3D: string, tagService: BaseTagService, module?: SpModule): Promise<void>;
|
|
118
231
|
removeForbiddenSweeps(forbiddenSweeps: string[]): void;
|
|
119
232
|
setObject3DLoadedInPosition(objectLoaded: boolean): void;
|
package/lib/types.service.d.ts
CHANGED
|
@@ -127,7 +127,8 @@ export declare enum EmailStatus {
|
|
|
127
127
|
export declare enum CaptureViewer {
|
|
128
128
|
CUPIX = "CUPIX",
|
|
129
129
|
MATTERPORT = "MATTERPORT",
|
|
130
|
-
SMARTERPLAN_V1 = "SMARTERPLAN_V1"
|
|
130
|
+
SMARTERPLAN_V1 = "SMARTERPLAN_V1",
|
|
131
|
+
THREEJS = "THREEJS"
|
|
131
132
|
}
|
|
132
133
|
export declare enum InventoryStatus {
|
|
133
134
|
SUCCESS = "SUCCESS",
|
|
@@ -595,6 +596,11 @@ export type Visit = {
|
|
|
595
596
|
node: Node;
|
|
596
597
|
name?: string | null;
|
|
597
598
|
nmbImagesImported: number;
|
|
599
|
+
/**
|
|
600
|
+
* Free-form metadata from the capture. Named after its source to keep it distinct from `name`,
|
|
601
|
+
* which comes from node.metadata. Three.js visits store their initial placement here.
|
|
602
|
+
*/
|
|
603
|
+
captureMetadata?: string | null;
|
|
598
604
|
};
|
|
599
605
|
export type Hashtag = {
|
|
600
606
|
name?: string | null;
|
package/package.json
CHANGED
package/public-api.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ export * from './lib/services/tag.service';
|
|
|
6
6
|
export * from './lib/services/baseVisibility.service';
|
|
7
7
|
export * from './lib/services/content.service';
|
|
8
8
|
export * from './lib/services/viewer.service';
|
|
9
|
+
export * from './lib/services/threejs.service';
|
|
9
10
|
export * from './lib/services/navigator.service';
|
|
10
11
|
export * from './lib/services/zoneChange.service';
|
|
11
12
|
export * from './lib/config';
|
|
@@ -18,6 +19,7 @@ export * from './lib/services/filter.service';
|
|
|
18
19
|
export * from './lib/services/search.service';
|
|
19
20
|
export * from './lib/services/zone-drawer.service';
|
|
20
21
|
export * from './lib/services/matterport-import.service';
|
|
22
|
+
export * from './lib/services/threejs-import.service';
|
|
21
23
|
export * from './lib/services/matterport-tag-copy.service';
|
|
22
24
|
export * from './lib/mattertagData';
|
|
23
25
|
export * from './lib/services/utils/matterport-transform.util';
|