@smarterplan/ngx-smarterplan-core 1.4.19 → 1.4.21

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.
Files changed (29) hide show
  1. package/esm2022/lib/helpers.service.mjs +28 -2
  2. package/esm2022/lib/matterport-extensions/security-camera/SecurityCamera.mjs +28 -7
  3. package/esm2022/lib/services/filter.service.mjs +8 -3
  4. package/esm2022/lib/services/matterport-object3d.service.mjs +657 -133
  5. package/esm2022/lib/services/matterport.service.mjs +330 -9
  6. package/esm2022/lib/services/models/base-object.service.mjs +10 -9
  7. package/esm2022/lib/services/models/equipment.service.mjs +27 -2
  8. package/esm2022/lib/services/models/feature.service.mjs +14 -1
  9. package/esm2022/lib/services/models/measurement.service.mjs +17 -1
  10. package/esm2022/lib/services/sweep-locator.service.mjs +114 -0
  11. package/esm2022/lib/services/threejs.service.mjs +840 -35
  12. package/esm2022/lib/services/utils/matterport-framing.util.mjs +63 -0
  13. package/esm2022/lib/services/viewer.service.mjs +193 -10
  14. package/esm2022/lib/types.service.mjs +3 -1
  15. package/esm2022/public-api.mjs +2 -1
  16. package/fesm2022/smarterplan-ngx-smarterplan-core.mjs +2886 -776
  17. package/fesm2022/smarterplan-ngx-smarterplan-core.mjs.map +1 -1
  18. package/lib/helpers.service.d.ts +17 -0
  19. package/lib/services/matterport-object3d.service.d.ts +125 -5
  20. package/lib/services/matterport.service.d.ts +106 -7
  21. package/lib/services/models/base-object.service.d.ts +3 -4
  22. package/lib/services/models/measurement.service.d.ts +1 -0
  23. package/lib/services/sweep-locator.service.d.ts +54 -0
  24. package/lib/services/threejs.service.d.ts +304 -10
  25. package/lib/services/utils/matterport-framing.util.d.ts +40 -0
  26. package/lib/services/viewer.service.d.ts +87 -2
  27. package/lib/types.service.d.ts +42 -1
  28. package/package.json +1 -1
  29. package/public-api.d.ts +1 -0
@@ -1,6 +1,6 @@
1
1
  import { NgZone } from '@angular/core';
2
2
  import { Subject } from 'rxjs';
3
- import { ThreeJsAlignment } from '../helpers.service';
3
+ import { ThreeJsAlignment, ThreeJsModelUp } from '../helpers.service';
4
4
  import { PoiType } from '../types.service';
5
5
  import * as i0 from "@angular/core";
6
6
  /**
@@ -22,6 +22,13 @@ export interface ThreeJsTag {
22
22
  poiType?: PoiType | null;
23
23
  /** Shown on hover. A marker with no identity is just a dot on a wall. */
24
24
  label?: string | null;
25
+ /** The Three.js capture this tag was placed in, when it was placed in one. */
26
+ originCaptureID?: string | null;
27
+ /**
28
+ * 'model' when the position is in the model's own coordinates rather than the space tags share:
29
+ * a tag placed in a model before it was calibrated. Only meaningful in the model it was placed in.
30
+ */
31
+ frame?: 'model' | null;
25
32
  /** Icon drawn on the tag. Falls back to a plain disc when absent or it fails to load. */
26
33
  iconUrl?: string | null;
27
34
  /** Tint for the fallback disc, as a CSS colour. */
@@ -55,7 +62,45 @@ export interface ThreeJsTagPlacement {
55
62
  poiType?: PoiType | null;
56
63
  }
57
64
  /** One tag seen in both spaces: where it is stored, and where it really sits on the model. */
65
+ /** A distance measured on the model: its points, each segment's length, and a picture of it. */
66
+ export interface ThreeJsMeasurement {
67
+ /** In the frame tag coordinates are stored in, so the last one can be saved as the POI. */
68
+ points: {
69
+ x: number;
70
+ y: number;
71
+ z: number;
72
+ }[];
73
+ /** Metres between consecutive points, rounded like Matterport's measurements are. */
74
+ distances: number[];
75
+ /** JPEG data URI of the scene with the measurement drawn on it. */
76
+ screenshot: string;
77
+ }
78
+ /**
79
+ * A tag placed in this model whose stored coordinates had to change for it to stay where it is,
80
+ * because the calibration it is drawn under changed.
81
+ */
82
+ export interface ThreeJsRebasedTag {
83
+ id: string;
84
+ /** Its coordinates in the space tags share, under the new calibration. */
85
+ position: {
86
+ x: number;
87
+ y: number;
88
+ z: number;
89
+ };
90
+ /** Turns a direction stored with the tag - its surface normal - the same way. */
91
+ rotateDirection: (direction: {
92
+ x: number;
93
+ y: number;
94
+ z: number;
95
+ }) => {
96
+ x: number;
97
+ y: number;
98
+ z: number;
99
+ };
100
+ }
58
101
  export interface ThreeJsAlignmentPair {
102
+ /** The tag the pair was made from, when it came from one. */
103
+ tagId?: string;
59
104
  source: {
60
105
  x: number;
61
106
  y: number;
@@ -68,17 +113,27 @@ export interface ThreeJsAlignmentPair {
68
113
  };
69
114
  }
70
115
  /**
71
- * Solves the transform that maps stored tag coordinates onto the loaded model.
116
+ * Solves the transform that maps stored tag coordinates onto the loaded model, from as many matched
117
+ * tags as there are - up to three.
72
118
  *
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.
119
+ * Three pairs pin down everything: position, orientation and scale. A model that shares only a
120
+ * corner with the tags' space may not have three tags to offer, so fewer are accepted, each
121
+ * settling what it can and assuming the rest:
77
122
  *
78
- * Returns null when the points are collinear or degenerate, because a wrong alignment applied
79
- * silently is worse than none.
123
+ * - Two pairs: position, heading and scale. The one thing two points cannot say is how the model
124
+ * is tilted, so both spaces are taken to agree on which way is up.
125
+ * - One pair: position only. Heading and scale are taken to match already.
126
+ *
127
+ * `targetUp` is the model's vertical axis, which the tags' own vertical (+Y) is matched to in those
128
+ * two cases. Returns null when the points cannot answer the question asked of them - collinear for
129
+ * three, one above the other for two - because a wrong calibration applied silently is worse than
130
+ * none.
80
131
  */
81
- export declare function solveThreeJsAlignment(pairs: ThreeJsAlignmentPair[]): ThreeJsAlignment | null;
132
+ export declare function solveThreeJsAlignment(pairs: ThreeJsAlignmentPair[], targetUp?: {
133
+ x: number;
134
+ y: number;
135
+ z: number;
136
+ }): ThreeJsAlignment | null;
82
137
  export interface ObjAdded {
83
138
  id: string;
84
139
  name: string;
@@ -137,6 +192,64 @@ export declare class ThreeJsService {
137
192
  private pendingTagPlacement;
138
193
  /** Pointer position waiting for a preview pick, coalesced to one per frame. */
139
194
  private pendingPreviewEvent;
195
+ /**
196
+ * Tags whose position is in this model's own coordinates. A sibling of the tag group that does not
197
+ * carry the calibration: calibrating a model for the tags it imports must not move the ones that
198
+ * were placed straight onto it.
199
+ */
200
+ private modelTagGroup;
201
+ /**
202
+ * The capture on screen. A tag placed in a 3D model is recorded with it, which is how the tags
203
+ * placed in this one are told from those that came from elsewhere.
204
+ */
205
+ private currentCaptureID;
206
+ /**
207
+ * The model's surface as a grid: every cell some part of the surface passes through.
208
+ *
209
+ * It answers "is this point near the model?" without visiting the triangles, of which a captured
210
+ * model has millions - the question is asked for every tag, every time the calibration changes.
211
+ */
212
+ private surfaceCells;
213
+ private surfaceOrigin;
214
+ private surfaceCellSize;
215
+ /**
216
+ * How far from the model's surface a tag may be and still count as on it, in metres.
217
+ *
218
+ * Every tag of the space is offered to every model; this is what decides which ones a model
219
+ * shows. A tag marks something on a surface, so one that belongs here sits on the surface or
220
+ * close to it. One that lands deep inside the model, or well away from it, marks something that
221
+ * is not part of this model. The allowance covers a calibration done by hand and the model not
222
+ * matching the scan exactly, which together run to a few tens of centimetres.
223
+ */
224
+ private static readonly ON_MODEL_METRES;
225
+ /** Cells per axis the grid's keys can address, with room either side for points off the model. */
226
+ private static readonly SURFACE_GRID_SPAN;
227
+ private static readonly SURFACE_GRID_PAD;
228
+ /** Own tags moved to keep their place under the last calibration, waiting to be stored. */
229
+ private rebasedTags;
230
+ /** The type filter in force. Null shows every type. */
231
+ private visibleTagTypes;
232
+ private measuring;
233
+ /** Points clicked so far, in tag space. */
234
+ private measurePoints;
235
+ /**
236
+ * Everything drawn for the measurement. A child of the tag group, so its points share the frame
237
+ * tag coordinates are stored in - which is metres, and what the saved POI has to be in.
238
+ */
239
+ private measureGroup;
240
+ /** The segment that follows the pointer from the last point clicked. */
241
+ private measureRubberBand?;
242
+ private lastMeasurement;
243
+ /** Half the model's diagonal as placed in the scene, where it is centred on the origin. */
244
+ private modelWorldRadius;
245
+ /** The outermost group the model and its tags hang from: where "which way is up" is applied. */
246
+ private modelGroup?;
247
+ private modelUp;
248
+ /** While measuring: how many points and each segment's length. Null when it is abandoned. */
249
+ measurementProgress$: Subject<{
250
+ points: number;
251
+ distances: number[];
252
+ }>;
140
253
  /**
141
254
  * Layer carrying the model alone, so the depth pick can render it without the tags.
142
255
  *
@@ -144,6 +257,8 @@ export declare class ThreeJsService {
144
257
  * their parent passed, so marking the model's own meshes is enough - no re-parenting.
145
258
  */
146
259
  private static readonly PICK_LAYER;
260
+ /** The app's highlight colour, so a measurement reads as the active thing on screen. */
261
+ private static readonly MEASURE_COLOR;
147
262
  /** Cache Storage bucket holding downloaded models, and how many to keep. */
148
263
  private static readonly MODEL_CACHE;
149
264
  private static readonly MODEL_CACHE_SIZE;
@@ -160,6 +275,8 @@ export declare class ThreeJsService {
160
275
  private aligning;
161
276
  private alignmentPairs;
162
277
  private alignmentSource;
278
+ /** The tag `alignmentSource` was read from, kept so the same tag is not matched twice. */
279
+ private alignmentSourceTagId;
163
280
  /** Drives the alignment walkthrough's UI: how many pairs are done and what it wants next. */
164
281
  alignmentProgress$: Subject<{
165
282
  pairs: number;
@@ -220,7 +337,7 @@ export declare class ThreeJsService {
220
337
  x: number;
221
338
  y: number;
222
339
  z: number;
223
- }, cacheKey?: string): void;
340
+ }, cacheKey?: string, up?: ThreeJsModelUp): void;
224
341
  /**
225
342
  * Stops the model download in progress, if any.
226
343
  *
@@ -352,6 +469,34 @@ export declare class ThreeJsService {
352
469
  }): void;
353
470
  /** Centers, scales and adds a loaded object to the scene, then frames the camera on it. */
354
471
  private placeLoadedObjectInScene;
472
+ /**
473
+ * Which of the model's axes is its vertical: 'y' when it already stands upright, or when it is
474
+ * not clear enough to act on.
475
+ *
476
+ * Models arrive in two conventions: Y up (glTF, and what this viewer assumes) and Z up (most CAD
477
+ * and scanning exports). A Z-up model loads lying on its side. Two independent cues say which
478
+ * axis is the vertical one, and both have to agree before anything is turned:
479
+ *
480
+ * - Surface area. Floors and ceilings all face along the vertical, while walls are shared out
481
+ * between the two horizontal axes, so the vertical usually collects the most area.
482
+ * - Extent. A building is normally wider than it is tall, so the vertical is its shortest side.
483
+ *
484
+ * Either alone is wrong often enough to matter - a long corridor has more wall than floor, a
485
+ * tower is taller than it is wide - and turning a model that was fine is worse than leaving one
486
+ * that was not. A model that already has Y as its vertical is never touched.
487
+ */
488
+ private detectModelUp;
489
+ /** The rotation that brings the given model axis to point up. */
490
+ private uprightRotation;
491
+ /** Which of the model's axes is currently treated as up, whether chosen or worked out. */
492
+ getModelUp(): ThreeJsModelUp;
493
+ /**
494
+ * Turns the model so the given axis points up, at once.
495
+ *
496
+ * The model and its tags turn as one, so nothing stored has to be recomputed. The caller is
497
+ * expected to store the choice, or the next load goes back to working it out.
498
+ */
499
+ setModelUp(up: ThreeJsModelUp): void;
355
500
  /**
356
501
  * Keeps the drawing buffer and camera matched to the canvas as it is resized.
357
502
  *
@@ -384,6 +529,8 @@ export declare class ThreeJsService {
384
529
  private isPreviewingPlacement;
385
530
  /** Whether a tag is currently registered for this element. */
386
531
  hasTag(elementId: string): boolean;
532
+ /** How many tags are on show - the ones a person could actually click. */
533
+ getTagCount(): number;
387
534
  /**
388
535
  * Solves and applies the alignment collected so far, returning it so the caller can persist it.
389
536
  *
@@ -391,8 +538,82 @@ export declare class ThreeJsService {
391
538
  * the walkthrough stays open for the person to pick a better third point.
392
539
  */
393
540
  solveAlignmentFromPairs(): ThreeJsAlignment | null;
541
+ /**
542
+ * Applies a new calibration while keeping every tag placed in this model exactly where it is.
543
+ *
544
+ * A calibration is worked out for the tags that came from elsewhere. The model's own tags were put
545
+ * where they are by someone clicking the model, so they must not follow it. Their stored
546
+ * coordinates are rewritten instead: into the shared space as the new calibration defines it.
547
+ * That is also what makes them comparable with other visits from here on - a tag placed before
548
+ * the model was calibrated was in the model's own coordinates, visible nowhere else.
549
+ *
550
+ * The rewritten tags are kept for the caller to store; see takeRebasedTags().
551
+ */
552
+ private applyAlignmentKeepingOwnTags;
553
+ /** Hands over the tags the last calibration rewrote, once, for them to be stored. */
554
+ takeRebasedTags(): ThreeJsRebasedTag[];
555
+ /**
556
+ * Chooses the tag for the pair being matched by id, instead of by clicking it.
557
+ *
558
+ * Clicking a tag supposes it can be found. On a model that has never been calibrated the tags may
559
+ * sit far from it, or all on top of one another; a list has neither problem.
560
+ */
561
+ selectAlignmentTag(elementId: string): boolean;
562
+ private setAlignmentSource;
394
563
  /** Handles a click while the alignment walkthrough is running. */
395
564
  private handleAlignmentClick;
565
+ /**
566
+ * Starts a measurement: every click on the model adds a point, and each pair of consecutive
567
+ * points is a measured segment.
568
+ *
569
+ * It ends through finishMeasuring() or cancelMeasuring() rather than on its own after two points,
570
+ * because a measurement may have several segments - the same as Matterport's, whose results are
571
+ * stored as a list of distances.
572
+ */
573
+ startMeasuring(): void;
574
+ /**
575
+ * Abandons the measurement and removes what was drawn.
576
+ *
577
+ * Also what clears a finished measurement off the model once its form is saved or closed, which
578
+ * is why it only reports an abandonment when one was actually in progress.
579
+ */
580
+ cancelMeasuring(): void;
581
+ /**
582
+ * Ends the measurement and returns it, or null when there are not yet two points to measure
583
+ * between - in which case measuring simply carries on.
584
+ *
585
+ * The drawing stays on the model so it is still visible while the person names the measurement;
586
+ * cancelMeasuring() takes it away afterwards.
587
+ */
588
+ finishMeasuring(): ThreeJsMeasurement | null;
589
+ isMeasuring(): boolean;
590
+ /** The measurement finishMeasuring() last produced, for the form that saves it. */
591
+ getLastMeasurement(): ThreeJsMeasurement | null;
592
+ /** Metres between consecutive points. Tag space is in metres, so no conversion is involved. */
593
+ private measureDistances;
594
+ private addMeasurePoint;
595
+ /**
596
+ * A segment with a width in pixels.
597
+ *
598
+ * WebGL's own lines are one pixel wide whatever is asked for, which all but disappears over a
599
+ * photographic texture. This kind is drawn as screen-facing geometry, so the width holds.
600
+ */
601
+ private createMeasureLine;
602
+ /** Line widths are in pixels, which the material can only honour if it knows the canvas size. */
603
+ private updateMeasureLineResolution;
604
+ /** Stretches a segment from the last point to the pointer, or hides it when given null. */
605
+ private updateMeasureRubberBand;
606
+ private createMeasureLabelTexture;
607
+ /** Removes everything drawn for a measurement and forgets its points. */
608
+ private clearMeasurement;
609
+ /**
610
+ * A picture of the scene with the measurement on it, as Matterport attaches to its own.
611
+ *
612
+ * Tags are left out, as they are there: the picture is about the measurement. It is taken straight
613
+ * after a render because the drawing buffer is only guaranteed to hold the frame until the
614
+ * browser presents it, and scaled down since it is uploaded and shown as a thumbnail.
615
+ */
616
+ private captureMeasurementScreenshot;
396
617
  /** Arms tag placement: the next click on the model drops a tag instead of being ignored. */
397
618
  startTagPlacement(poiType?: PoiType | null): void;
398
619
  /** Leaves placement mode without placing anything. */
@@ -429,6 +650,79 @@ export declare class ThreeJsService {
429
650
  * filter that happens to match nothing.
430
651
  */
431
652
  setVisibleTagTypes(types: PoiType[] | null): void;
653
+ /** Says which capture is on screen, so the tags placed in it can be told from the rest. */
654
+ setCurrentCapture(captureID: string | null): void;
655
+ /** Every tag registered in the scene, shown or not. */
656
+ getTags(): {
657
+ id: string;
658
+ label: string | null;
659
+ poiType: PoiType | null;
660
+ visible: boolean;
661
+ /** Whether the calibration walkthrough can use it. */
662
+ calibratable: boolean;
663
+ }[];
664
+ /**
665
+ * How far the tags from elsewhere sit from the model, measured in model radii. Null without both.
666
+ *
667
+ * Before a model is calibrated those tags are wherever their own coordinates put them, which for
668
+ * a model with a different origin can be a long way off - too far to find, let alone click.
669
+ */
670
+ getTagDistanceFromModel(): number | null;
671
+ /**
672
+ * Hangs a tag from the group its coordinates belong to: outside the calibration when they are in
673
+ * this model's own coordinates, under it otherwise.
674
+ */
675
+ private parentTag;
676
+ /**
677
+ * The tags a calibration can be worked out from: those that came from somewhere else, whether or
678
+ * not they are on show - before calibrating, most of them will not be.
679
+ *
680
+ * Tags placed in this model are where they are because someone clicked there; matching them to
681
+ * "where they really are" would be circular. A tag in another model's own coordinates cannot be
682
+ * placed here at all. And a tag already used in this calibration cannot be used again - two
683
+ * pairs from one tag carry no more than one.
684
+ */
685
+ private calibrationCandidates;
686
+ /** How many tags there are to calibrate with, not counting those already used. */
687
+ getCalibrationCandidateCount(): number;
688
+ /**
689
+ * How many tags from elsewhere are not on this model, and so not shown on it.
690
+ *
691
+ * On a model that has not been calibrated, this is what suggests it should be: the tags are out
692
+ * there, just not where the model is.
693
+ */
694
+ getOffModelTagCount(): number;
695
+ /** Whether the tag was placed in the visit on screen. */
696
+ private isOwnedByVisit;
697
+ /**
698
+ * A tag shows when its type is not filtered out and it belongs on this model.
699
+ *
700
+ * One place decides, because the two used to set `visible` separately and whichever ran last
701
+ * undid the other.
702
+ */
703
+ private applyTagVisibility;
704
+ /**
705
+ * Whether a tag has a place on this model.
706
+ *
707
+ * Every tag of the space is offered to every model - there is one set of tags, not one per model.
708
+ * What a model shows of it is the part that sits on the model.
709
+ */
710
+ private belongsOnThisModel;
711
+ /** Whether a tag, drawn where its coordinates and the calibration put it, sits on the model. */
712
+ private isOnModel;
713
+ /** Re-decides every tag's visibility: the model, the calibration or the mode changed. */
714
+ private refreshTagVisibility;
715
+ private surfaceKey;
716
+ /**
717
+ * Lays the grid over the model's surface.
718
+ *
719
+ * Runs once per model, in one pass over its triangles. A triangle smaller than a cell is marked
720
+ * by its centre, which is every triangle of a scanned model; a larger one - the two triangles of
721
+ * a drawn wall - is sampled across its face, or the middle of the wall would read as empty.
722
+ */
723
+ private buildSurfaceGrid;
724
+ /** Whether some part of the model's surface lies within `reach` of a point, in model units. */
725
+ private isNearSurface;
432
726
  /**
433
727
  * Moves the camera to look at a tag, mirroring Matterport's goToTag.
434
728
  *
@@ -0,0 +1,40 @@
1
+ import { Point3D } from '../../types.service';
2
+ /** Matterport camera rotation, in degrees (x: pitch, y: yaw) */
3
+ export interface CameraRotation {
4
+ x: number;
5
+ y: number;
6
+ }
7
+ /**
8
+ * Camera rotation that shows `target` from `camera`, at a given horizontal place of the viewer.
9
+ * Matterport yaw is 0 when looking toward -Z and grows when turning left; pitch is positive upward.
10
+ * @param horizontalTanHalfFov tan(horizontal FOV / 2), i.e. 1 / projection[0] of the camera pose
11
+ * @param screenX where the target must appear, from -1 (left edge) to 1 (right edge); 0 = center.
12
+ * Used when part of the viewer is covered (side panel), to frame the visible area.
13
+ */
14
+ export declare function computeFramingRotation(camera: Point3D, target: Point3D, horizontalTanHalfFov?: number, screenX?: number): CameraRotation;
15
+ /**
16
+ * Horizontal place (-1..1) of the center of the visible part of the viewer.
17
+ * @param viewer client rect of the viewer iframe
18
+ * @param visibleLeft / visibleRight client X bounds of the part not covered by other panels
19
+ */
20
+ export declare function visibleCenterScreenX(viewer: {
21
+ left: number;
22
+ width: number;
23
+ }, visibleLeft: number, visibleRight: number): number;
24
+ /** Height of the floor under a sweep: its puck, or 1.5 m under the camera when the SDK gives none */
25
+ export declare function sweepFloorY(sweep: {
26
+ position: Point3D;
27
+ puckPosition?: Point3D;
28
+ floorPosition?: Point3D;
29
+ }): number;
30
+ /**
31
+ * Sweep closest to an object placed at `position`, among the sweeps of the same storey: the camera
32
+ * of the storey below is often nearer in 3D than a sweep of the same room. A sweep is on the same
33
+ * storey when the object stands between 0.5 m under its floor and 3 m above it; without any such
34
+ * sweep, all of them are considered. Distances are measured horizontally.
35
+ */
36
+ export declare function pickNearestSweepOnSameStorey(sweeps: {
37
+ sid: string;
38
+ position: Point3D;
39
+ floorY: number;
40
+ }[], position: Point3D): string | null;
@@ -1,9 +1,11 @@
1
1
  import { Subject } from 'rxjs';
2
2
  import { MatterportService } from './matterport.service';
3
- import { ThreeJsService } from './threejs.service';
3
+ import { ThreeJsMeasurement, ThreeJsService } from './threejs.service';
4
+ import { SweepLocatorService } from './sweep-locator.service';
4
5
  import { BaseTagService } from './tag.service';
5
6
  import { DbObjectType, POI, PoiType, Comment, Equipment, Ticket, Feature, SpModule } from '../types.service';
6
7
  import { PoiService } from './models/poi.service';
8
+ import { ThreeJsModelUp } from '../helpers.service';
7
9
  import { Config } from '../config';
8
10
  import * as i0 from "@angular/core";
9
11
  export declare class ViewerService {
@@ -12,6 +14,7 @@ export declare class ViewerService {
12
14
  private poiService;
13
15
  private matterportService;
14
16
  private threeJsService;
17
+ private sweepLocator;
15
18
  private viewerMode;
16
19
  currentModel3D: string;
17
20
  private tourUrl;
@@ -35,6 +38,20 @@ export declare class ViewerService {
35
38
  * lists that feed tags are exactly that.
36
39
  */
37
40
  currentSpaceID: string | null;
41
+ /**
42
+ * The Three.js capture on screen, or null in Matterport.
43
+ *
44
+ * A space can hold several 3D models. A tag placed in one is recorded with it, so that model
45
+ * keeps showing the tag whatever selection of tags it is otherwise restricted to.
46
+ */
47
+ currentCaptureID: string | null;
48
+ /**
49
+ * Whether the 3D model on screen has been calibrated against the tags' shared space.
50
+ *
51
+ * Calibrating is what says the model and that space overlap. Until then a tag placed in the model
52
+ * is in the model's own coordinates: it belongs to no Matterport sweep and to no other visit.
53
+ */
54
+ private threeJsCalibrated;
38
55
  sweepToMove: string | null;
39
56
  rotationToMove: {
40
57
  x: number;
@@ -62,7 +79,7 @@ export declare class ViewerService {
62
79
  };
63
80
  };
64
81
  private isObjectLoaded;
65
- constructor(sdkKey: string, config: Config, poiService: PoiService, matterportService: MatterportService, threeJsService: ThreeJsService);
82
+ constructor(sdkKey: string, config: Config, poiService: PoiService, matterportService: MatterportService, threeJsService: ThreeJsService, sweepLocator: SweepLocatorService);
66
83
  checkMode(indexMode: string): boolean;
67
84
  setMode(indexMode: string): void;
68
85
  setTourUrl(options: {
@@ -102,6 +119,30 @@ export declare class ViewerService {
102
119
  * of the pipeline that already drives Matterport.
103
120
  */
104
121
  private addThreeJsTagFromPOI;
122
+ /**
123
+ * Stores the tags the last calibration had to rewrite, and returns how many it could not.
124
+ *
125
+ * Calibrating keeps the tags placed in this model where they are by changing their coordinates
126
+ * rather than their place (see ThreeJsService.applyAlignmentKeepingOwnTags). The scene already
127
+ * shows the result; this writes it down. Call it once the calibration itself is stored - with the
128
+ * old calibration still on record, the old coordinates are the ones that match it.
129
+ */
130
+ persistRebasedThreeJsTags(): Promise<{
131
+ stored: number;
132
+ failed: number;
133
+ }>;
134
+ /** How many tags the calibration walkthrough could still use: the ones from elsewhere. */
135
+ getThreeJsCalibrationCandidateCount(): number;
136
+ /** How many tags from elsewhere are not on the 3D model, and so are not shown on it. */
137
+ getThreeJsOffModelTagCount(): number;
138
+ /** The tags registered in the Three.js scene, shown or not. */
139
+ getThreeJsTags(): {
140
+ id: string;
141
+ label: string;
142
+ poiType: PoiType;
143
+ visible: boolean;
144
+ calibratable: boolean;
145
+ }[];
105
146
  /**
106
147
  * The image to draw a tag with, mirroring what Matterport shows for the same POI.
107
148
  *
@@ -178,6 +219,50 @@ export declare class ViewerService {
178
219
  action_floorplan(): Promise<void>;
179
220
  action_toolbox_mesure(): void;
180
221
  action_toolbox_cancel_mesure(): void;
222
+ /** While measuring in the Three.js viewer: points and segment lengths so far, null if abandoned. */
223
+ get threeJsMeasurementProgress$(): Subject<{
224
+ points: number;
225
+ distances: number[];
226
+ }>;
227
+ /** Ends the Three.js measurement in progress, or returns null when it has fewer than two points. */
228
+ finishThreeJsMeasurement(): ThreeJsMeasurement | null;
229
+ /** The measurement last finished in the Three.js viewer, for the form that saves it. */
230
+ getThreeJsLastMeasurement(): ThreeJsMeasurement | null;
231
+ /**
232
+ * The Matterport sweep nearest to a point picked in the Three.js viewer, or null.
233
+ *
234
+ * For callers that write their own POI, as the measurement form does - anything created through
235
+ * createPoiForThreeJsPlacement already gets this.
236
+ */
237
+ findNearestSweepForThreeJs(point: {
238
+ x: number;
239
+ y: number;
240
+ z: number;
241
+ }): Promise<string | null>;
242
+ /**
243
+ * The metadata a POI placed in the Three.js viewer is stored with: which 3D model it was placed
244
+ * in, and - when that model is not calibrated - that its coordinates are the model's own.
245
+ */
246
+ buildThreeJsPoiMetadata(extra?: Record<string, any>): string;
247
+ /** Which of the 3D model's axes is being treated as up. */
248
+ getThreeJsModelUp(): ThreeJsModelUp;
249
+ /**
250
+ * Turns the 3D model to the next candidate for "which axis is up", and returns the capture
251
+ * metadata with that choice written in, for the caller to store.
252
+ *
253
+ * The vertical axis is worked out from the geometry when a model loads, and that can be wrong:
254
+ * a narrow corridor and a low slab are the same shape turned over. This is the way to correct it.
255
+ */
256
+ cycleThreeJsModelUp(existingMetadata: string | null | undefined): {
257
+ up: ThreeJsModelUp;
258
+ metadata: string;
259
+ } | null;
260
+ /** Chooses the tag for the calibration pair being matched by id, instead of by clicking it. */
261
+ selectThreeJsAlignmentTag(elementId: string): boolean;
262
+ /** How far the visible tags sit from the 3D model, in model radii. Null without both. */
263
+ getThreeJsTagDistanceFromModel(): number | null;
264
+ /** How many tags the Three.js scene is showing. */
265
+ getThreeJsTagCount(): number;
181
266
  action_update_poi(poi: POI, isValid: boolean, element: DbObjectType): Promise<POI>;
182
267
  /**
183
268
  * Writes the spot picked in the Three.js scene into the POI, mirroring the Matterport branch.
@@ -405,7 +405,9 @@ export type POI = {
405
405
  export declare enum ViewerInteractions {
406
406
  DEFAULT = 0,
407
407
  ADDING = 1,
408
- POSITIONING = 2
408
+ POSITIONING = 2,
409
+ /** A loaded 3D object follows the cursor until the user clicks (validate) or cancels */
410
+ PLACING_OBJECT3D = 3
409
411
  }
410
412
  export type Mission = {
411
413
  userID: string;
@@ -820,6 +822,8 @@ export type IObject3D = {
820
822
  id?: string;
821
823
  object: string;
822
824
  format: string;
825
+ /** Full url of the model file; when set it replaces the url built from the bundled assets */
826
+ url?: string;
823
827
  mode: string;
824
828
  position: {
825
829
  x: number;
@@ -838,6 +842,43 @@ export type IObject3D = {
838
842
  };
839
843
  viewFrustum: boolean;
840
844
  };
845
+ export type Point3D = {
846
+ x: number;
847
+ y: number;
848
+ z: number;
849
+ };
850
+ /** World-space bounding box of a 3D object */
851
+ export type Object3DBoundingBox = {
852
+ min: Point3D;
853
+ max: Point3D;
854
+ size: Point3D;
855
+ center: Point3D;
856
+ };
857
+ /** Local transform of a 3D object (rotation in radians, Euler XYZ) */
858
+ export type Object3DTransform = {
859
+ position: Point3D;
860
+ rotation: Point3D;
861
+ scale: Point3D;
862
+ };
863
+ /** Emitted when the transform of a 3D object changes (gizmo drag or setObject3DTransform) */
864
+ export type Object3DTransformChange = Object3DTransform & {
865
+ uuid: string;
866
+ };
867
+ export type GizmoMode = 'translate' | 'rotate' | 'scale';
868
+ /** Snapping steps of the gizmo. A missing / null value disables snapping for that mode */
869
+ export type GizmoSnap = {
870
+ /** meters */
871
+ translation?: number | null;
872
+ /** degrees */
873
+ rotationDeg?: number | null;
874
+ /** scale step */
875
+ scale?: number | null;
876
+ };
877
+ /** Result of an interactive placement (surface point under the cursor and its normal) */
878
+ export type Object3DPlacementResult = {
879
+ position: Point3D;
880
+ normal: Point3D;
881
+ };
841
882
  /**
842
883
  * DynamoDb object3D
843
884
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smarterplan/ngx-smarterplan-core",
3
- "version": "1.4.19",
3
+ "version": "1.4.21",
4
4
  "dependencies": {
5
5
  "tslib": "^2.5.0"
6
6
  },
package/public-api.d.ts CHANGED
@@ -7,6 +7,7 @@ export * from './lib/services/baseVisibility.service';
7
7
  export * from './lib/services/content.service';
8
8
  export * from './lib/services/viewer.service';
9
9
  export * from './lib/services/threejs.service';
10
+ export * from './lib/services/sweep-locator.service';
10
11
  export * from './lib/services/navigator.service';
11
12
  export * from './lib/services/zoneChange.service';
12
13
  export * from './lib/config';