@mappedin/mappedin-js 5.11.0 → 5.12.1

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.
@@ -1,10 +1,9 @@
1
1
  // Generated by dts-bundle v0.7.3
2
2
  // Dependencies for this module:
3
+ // ../@mappedin/mvf
3
4
  // ../three
4
5
  // ../@tweenjs/tween.js
5
6
  // ../minisearch
6
- // ../@mappedin/mvf
7
- // ../stats.js
8
7
 
9
8
  declare module '@mappedin/mappedin-js' {
10
9
  import { MapView } from '@mappedin/mappedin-js/renderer/public/MapView';
@@ -663,6 +662,7 @@ declare module '@mappedin/mappedin-js/get-venue' {
663
662
  export type { TLocationType, TNode, TImage, TLogo, TGalleryImage, TPhone, TSocial, TColor, TVortex, TPicture, TOpeningHours, TSiblingGroup, TState, TCategory, TEvent, TGeoReference, TMap, TMapGroup, TBuilding, TLocation, TPolygon, TPolygonRanking, TVenue, TMappedinAPI, } from '@mappedin/mappedin-js/get-venue/Mappedin.API.types';
664
663
  export type { TGetVenueOptions } from '@mappedin/mappedin-js/get-venue/Mappedin.types';
665
664
  import { MAP_RENDER_MODE } from '@mappedin/mappedin-js/get-venue/Mappedin.types';
665
+ import { ParsedMVF } from '@mappedin/mvf';
666
666
  export type TShowVenueOptions = {
667
667
  /**
668
668
  * Sets the initial background color of the map, including while loading.
@@ -748,7 +748,7 @@ declare module '@mappedin/mappedin-js/get-venue' {
748
748
  /**
749
749
  * @internal
750
750
  */
751
- export function downloadVenueBundleMVF(options: TGetVenueBundleOptions): Promise<unknown>;
751
+ export function downloadVenueBundleMVF(options: TGetVenueBundleOptions): Promise<Uint8Array>;
752
752
  /**
753
753
  * Returns a {@link Mappedin} object hydrated with JSON data.
754
754
  * @param {string|Object} mappedinSerializableData A JSON string or object representing a venue.
@@ -756,6 +756,11 @@ declare module '@mappedin/mappedin-js/get-venue' {
756
756
  * @returns {Mappedin} A new Mappedin object with data from the mappedinSerializableData parameter.
757
757
  */
758
758
  export function hydrateVenue(mappedinSerializableData: any, shouldPopulateBundledImagesAsBlobs?: boolean): Promise<Mappedin>;
759
+ /**
760
+ * @internal
761
+ * Returns a {@link Mappedin} object hydrated with MVF data.
762
+ */
763
+ export function hydrateVenueMVF(mvfData: ParsedMVF): Promise<Mappedin>;
759
764
  }
760
765
 
761
766
  declare module '@mappedin/mappedin-js/renderer/internal/Mappedin.BlueDot/Mappedin.BlueDot.core' {
@@ -1257,10 +1262,20 @@ declare module '@mappedin/mappedin-js/renderer/MapView.types' {
1257
1262
  */
1258
1263
  firstMap?: MappedinMap | string;
1259
1264
  /**
1265
+ * @experimental
1266
+ *
1260
1267
  * Multi-buffer rendering should improve performance but may cause issues on older GPUs/browsers
1261
- * @default true
1268
+ * @default false
1262
1269
  */
1263
1270
  multiBufferRendering?: boolean;
1271
+ /**
1272
+ * @experimental
1273
+ *
1274
+ * Journey path will be visible through other objects. Note: this is on by default, but requires the
1275
+ * `multiBufferRendering` option (which is off by default) to be turned on.
1276
+ * @default true
1277
+ */
1278
+ xRayPath?: boolean;
1264
1279
  /**
1265
1280
  * True if the `opacity` argument to `setBackgroundColor` should be considered.
1266
1281
  * @default true
@@ -2830,6 +2845,10 @@ declare module '@mappedin/mappedin-js/renderer/internal/outdoor-context/Mappedin
2830
2845
  export type TTileManagerOptions = {
2831
2846
  tileRenderMode?: TILE_RENDER_MODES;
2832
2847
  provider: IOutdoorContextProvider;
2848
+ /**
2849
+ * How many times bigger the bounding box should be than the map's longest side
2850
+ */
2851
+ boundingBoxMultiplier?: number;
2833
2852
  };
2834
2853
  export class TileManager {
2835
2854
  #private;
@@ -2847,6 +2866,9 @@ declare module '@mappedin/mappedin-js/renderer/internal/outdoor-context/Mappedin
2847
2866
  renderVisibleTiles: () => void;
2848
2867
  _renderVisibleTiles(): void;
2849
2868
  cachedZoomLevel: number;
2869
+ visible: boolean;
2870
+ fadeOut(): Promise<any>;
2871
+ fadeIn(): Promise<any>;
2850
2872
  fetchTiles(): void;
2851
2873
  zoomLevelToAltitudeMap: number[][];
2852
2874
  plane: Object3D;
@@ -3336,7 +3358,6 @@ declare module '@mappedin/mappedin-js/get-venue/Mappedin.types' {
3336
3358
 
3337
3359
  declare module '@mappedin/mappedin-js/get-venue/Mappedin' {
3338
3360
  import { Navigator } from '@mappedin/mappedin-js/navigator';
3339
- import { MVFData } from '@mappedin/mappedin-js/get-venue/Mappedin.MVF.types';
3340
3361
  import type { TGetVenueOptions, TGetVenueOptionsInternal } from '@mappedin/mappedin-js/get-venue/Mappedin.types';
3341
3362
  import { MappedinCategory } from '@mappedin/mappedin-js/get-venue/MappedinCategory';
3342
3363
  import { MappedinEvent } from '@mappedin/mappedin-js/get-venue/MappedinEvent';
@@ -3352,6 +3373,7 @@ declare module '@mappedin/mappedin-js/get-venue/Mappedin' {
3352
3373
  import { MappedinVenue } from '@mappedin/mappedin-js/get-venue/MappedinVenue';
3353
3374
  import { MappedinVortex } from '@mappedin/mappedin-js/get-venue/MappedinVortex';
3354
3375
  import { IAnalytics } from '@mappedin/mappedin-js/get-venue/Mappedin.CustomerAnalytics';
3376
+ import { ParsedMVF } from '@mappedin/mvf';
3355
3377
  export const defaultOptions: TGetVenueOptionsInternal & TGetVenueOptions;
3356
3378
  export enum MappedinCollectionType {
3357
3379
  CATEGORY = "categories",
@@ -3587,7 +3609,7 @@ declare module '@mappedin/mappedin-js/get-venue/Mappedin' {
3587
3609
  * @hidden
3588
3610
  * @internal
3589
3611
  */
3590
- hydrateFromMVF(mvfData: MVFData): Promise<undefined>;
3612
+ hydrateFromMVF(mvfData: ParsedMVF): Promise<undefined>;
3591
3613
  /**
3592
3614
  *
3593
3615
  * @experimental Hydrate the Mappedin instance using a response from either {@link Mappedin.toString}, {@link getVenueBundle} or by downloading the bundle manually
@@ -5050,6 +5072,7 @@ declare module '@mappedin/mappedin-js/renderer/internal' {
5050
5072
  export { default as MapObject } from '@mappedin/mappedin-js/renderer/internal/Mappedin.MapObject';
5051
5073
  export { default as CAMERA_LAYER } from '@mappedin/mappedin-js/renderer/internal/Mappedin.CameraLayers';
5052
5074
  export { default as AssetManager } from '@mappedin/mappedin-js/renderer/internal/Mappedin.AssetManager';
5075
+ export { BundleAssetManager } from '@mappedin/mappedin-js/renderer/bundle-asset-manager';
5053
5076
  /**
5054
5077
  * Internal Events and Payloads
5055
5078
  */
@@ -6017,26 +6040,6 @@ declare module '@mappedin/mappedin-js/renderer/private/Core.interface' {
6017
6040
  }
6018
6041
  }
6019
6042
 
6020
- declare module '@mappedin/mappedin-js/get-venue/Mappedin.MVF.types' {
6021
- import { ManifestCollection, NodeCollection, ObstructionCollection, SpaceCollection, Connections, Maps, EntranceCollection, MapStacks, StyleCollection } from '@mappedin/mvf';
6022
- export type WithIDs<T> = Map<string, T>;
6023
- /**
6024
- * The entire data collection for an MVF, in a single JSON collection.
6025
- *
6026
- */
6027
- export type MVFData = {
6028
- obstruction: WithIDs<ObstructionCollection>;
6029
- space: WithIDs<SpaceCollection>;
6030
- node: WithIDs<NodeCollection>;
6031
- entrance: WithIDs<EntranceCollection>;
6032
- connection: Connections;
6033
- map: Maps;
6034
- mapstack: MapStacks;
6035
- manifest: ManifestCollection;
6036
- style: StyleCollection;
6037
- };
6038
- }
6039
-
6040
6043
  declare module '@mappedin/mappedin-js/get-venue/MappedinLocationRankings' {
6041
6044
  import type { Mappedin } from '@mappedin/mappedin-js/get-venue/Mappedin';
6042
6045
  import type { TLocation } from '@mappedin/mappedin-js/get-venue/Mappedin.API.types';
@@ -6629,8 +6632,8 @@ declare module '@mappedin/mappedin-js/get-venue/Mappedin.TaskScheduler' {
6629
6632
 
6630
6633
  declare module '@mappedin/mappedin-js/renderer/MapView.SceneManager' {
6631
6634
  import { MappedinMap } from '@mappedin/mappedin-js/get-venue';
6632
- import type { ICore, TCameraTransform, TCameraAnimationOptions } from '@mappedin/mappedin-js/renderer/internal';
6633
- import { MapViewScene } from '@mappedin/mappedin-js/renderer/internal';
6635
+ import type { ICore, TCameraAnimationOptions, TFocusOnCameraOptions, TCameraTargets } from '@mappedin/mappedin-js/renderer/internal';
6636
+ import { MapObject, MapViewScene } from '@mappedin/mappedin-js/renderer/internal';
6634
6637
  export type TSceneTransitionOptions = {
6635
6638
  /**
6636
6639
  * Map to set as active during the transition. This will decide where the camera will be positioned, as well as which
@@ -6638,11 +6641,6 @@ declare module '@mappedin/mappedin-js/renderer/MapView.SceneManager' {
6638
6641
  */
6639
6642
  activeMap?: MappedinMap;
6640
6643
  verticalDistanceBetweenMaps?: number;
6641
- /**
6642
- * Camera options to use when transitioning to the new scene
6643
- */
6644
- cameraTransform?: TCameraTransform;
6645
- cameraAnimationOptions?: TCameraAnimationOptions;
6646
6644
  /**
6647
6645
  * Whether to auto focus on the active map or leave the camera where it is.
6648
6646
  * For single building venues, this should look the same way it did with MapManager
@@ -6651,10 +6649,22 @@ declare module '@mappedin/mappedin-js/renderer/MapView.SceneManager' {
6651
6649
  * @default true
6652
6650
  */
6653
6651
  autoFocusOnActiveMap?: boolean;
6652
+ /**
6653
+ * Where to focus the camera during transition to Scene. Will focus to fit the map if not provided.
6654
+ * Currently, will discard any targets that are not on the active map.
6655
+ */
6656
+ focusOn?: {
6657
+ targets?: TCameraTargets;
6658
+ options?: TFocusOnCameraOptions & TCameraAnimationOptions;
6659
+ };
6654
6660
  };
6655
6661
  class SceneManager {
6656
6662
  #private;
6657
6663
  currentScene: MapViewScene;
6664
+ /**
6665
+ * MapObjects that have been loaded and positioned in the scene
6666
+ */
6667
+ processedMapObjects: Set<MapObject>;
6658
6668
  constructor(core: ICore, startingScene: MapViewScene);
6659
6669
  get currentMap(): MappedinMap;
6660
6670
  renderGrid(): void;
@@ -6711,6 +6721,7 @@ declare module '@mappedin/mappedin-js/renderer/internal/Mappedin.MapObject' {
6711
6721
  labels: Set<any>;
6712
6722
  tooltips: Set<any>;
6713
6723
  markers: Set<any>;
6724
+ box: any;
6714
6725
  textObjects: any[];
6715
6726
  labelBatchCreator: null;
6716
6727
  imagesToFlip: any[];
@@ -6734,6 +6745,45 @@ declare module '@mappedin/mappedin-js/renderer/internal/Mappedin.MapObject' {
6734
6745
  _objLoaded(object: any): any;
6735
6746
  _objLoadedMerged(object: any): Promise<any>;
6736
6747
  enableImageFlipping(polygonId: any, rotation: any): void;
6748
+ fadeIn({ duration, delay, easing, onStart, onUpdate, onComplete, }: {
6749
+ duration?: number | undefined;
6750
+ delay?: number | undefined;
6751
+ easing?: any;
6752
+ onStart?: (() => void) | undefined;
6753
+ onUpdate?: (() => void) | undefined;
6754
+ onComplete?: (() => void) | undefined;
6755
+ }): {
6756
+ start(core: any): Promise<any>;
6757
+ }[];
6758
+ fadeOut({ duration, delay, easing, onStart, onUpdate, onComplete, }: {
6759
+ duration?: number | undefined;
6760
+ delay?: number | undefined;
6761
+ easing?: any;
6762
+ onStart?: (() => void) | undefined;
6763
+ onUpdate?: (() => void) | undefined;
6764
+ onComplete?: (() => void) | undefined;
6765
+ }): {
6766
+ start(core: any): Promise<any>;
6767
+ }[];
6768
+ fade({ direction, duration, delay, easing, onStart, onUpdate, onComplete, }: {
6769
+ direction?: string | undefined;
6770
+ duration?: number | undefined;
6771
+ delay?: number | undefined;
6772
+ easing?: any;
6773
+ onStart?: (() => void) | undefined;
6774
+ onUpdate?: (() => void) | undefined;
6775
+ onComplete?: (() => void) | undefined;
6776
+ }): {
6777
+ start(core: any): Promise<any>;
6778
+ }[];
6779
+ /**
6780
+ * Sets the opacity of all children (for now) of the map object
6781
+ */
6782
+ setIndoorGeometryOpacity(opacity: any): void;
6783
+ /**
6784
+ * Resets the transparent and opacity values of all children (for now) of the map object
6785
+ */
6786
+ resetIndoorGeometryOpacity(): void;
6737
6787
  elements: any;
6738
6788
  boundingBox: {
6739
6789
  min: any;
@@ -6877,648 +6927,6 @@ declare module '@mappedin/mappedin-js/renderer/internal/Mappedin.CameraLayers' {
6877
6927
  }
6878
6928
  }
6879
6929
 
6880
- import { Mesh, BufferAttribute, InstancedBufferAttribute, InstancedBufferGeometry, Object3D, Color, CanvasTexture, RawShaderMaterial, } from 'three';
6881
- import { getObjectId, getObject } from '@mappedin/mappedin-js/renderer/internal/utils';
6882
- import { HoverLabel } from '@mappedin/mappedin-js/renderer/internal';
6883
- import LabelAtlasFragment from '@mappedin/mappedin-js/renderer/internal/shaders/Mappedin.LabelAtlas.Fragment.glsl';
6884
- import LabelAtlasVertex from '@mappedin/mappedin-js/renderer/internal/shaders/Mappedin.LabelAtlas.Vertex.glsl';
6885
- export const DEFAULT_LABEL_SIZING = {
6886
- MARGIN: 2,
6887
- HEIGHT_MARGIN: 0.5,
6888
- SIZE: 1.2,
6889
- };
6890
- const FONT_SIZE = 56;
6891
- const SIZE_IN_METERS = FONT_SIZE / DEFAULT_LABEL_SIZING.SIZE; // About this, make it the same as legacy labels anyway
6892
- const PIXELS_PER_METER = FONT_SIZE / 2;
6893
- const METERS_PER_PIXEL = 1 / PIXELS_PER_METER;
6894
- const CANVAS_MAX_LABELS = 64;
6895
- const MIN_SLOT_SIZE = 16;
6896
- const LABEL_PADDING = 4;
6897
- class Label {
6898
- constructor() {
6899
- this.x = 0;
6900
- this.y = 0;
6901
- this.width = 0;
6902
- this.height = 0;
6903
- this.layer = null;
6904
- this.index = null;
6905
- }
6906
- /// This _just_ lays out the vertex into the attribute buffers.
6907
- layout(map, origin, size, rotation, uv, color) {
6908
- let angle = (this.canvasBounds.rotation * Math.PI) / 180;
6909
- let width = this.width * this.pixelsPerMu;
6910
- let height = this.height * this.pixelsPerMu;
6911
- // fix the alignment of the labels. This just shifts them around
6912
- // in canvasBound area to the left/right/center
6913
- let offset;
6914
- if (this.canvasBounds.align == 'left') {
6915
- offset = this.margin;
6916
- }
6917
- else if (this.canvasBounds.align == 'right') {
6918
- offset = this.canvasBounds.maxWidth - width - this.margin;
6919
- }
6920
- else if (this.canvasBounds.align == 'center') {
6921
- offset = (this.canvasBounds.maxWidth - width - this.margin) / 2;
6922
- }
6923
- let offsetX = offset * Math.cos(angle);
6924
- let offsetY = -offset * Math.sin(angle);
6925
- let index = this.index;
6926
- origin.setXYZ(index, this.canvasBounds.x - map.width / 2 + offsetX, -this.canvasBounds.y + map.height / 2 + offsetY, this.z + 0.1);
6927
- size.setXY(index, width, height);
6928
- rotation.setX(index, angle);
6929
- let canvas = this.layer.canvas;
6930
- uv.setXYZW(index, this.x / canvas.width, 1 - this.y / canvas.height, this.width / canvas.width, this.height / canvas.height);
6931
- color.setXYZ(index, this.color.r, this.color.g, this.color.b);
6932
- // mark all the arrays as needing to be updated (reuploaded) since
6933
- // they have now been changed.
6934
- origin.needsUpdate = true;
6935
- size.needsUpdate = true;
6936
- rotation.needsUpdate = true;
6937
- uv.needsUpdate = true;
6938
- color.needsUpdate = true;
6939
- }
6940
- }
6941
- class Slot {
6942
- constructor(x, y, width, height) {
6943
- this.x = x;
6944
- this.y = y;
6945
- this.width = width;
6946
- this.height = height;
6947
- }
6948
- leftOverSpace(glyph) {
6949
- // check to see if it would not fit.
6950
- if (this.width < glyph.width || this.height < glyph.height) {
6951
- return null;
6952
- }
6953
- return this.height * (this.width - glyph.width) + this.width * (this.height - glyph.height);
6954
- }
6955
- /// split this slot into 0-2 spaces.
6956
- split(label, spaces, index) {
6957
- label.x = this.x;
6958
- label.y = this.y;
6959
- if (this.width - label.height < MIN_SLOT_SIZE && this.height - label.height < MIN_SLOT_SIZE) {
6960
- // delete itself from the array
6961
- spaces.splice(index, 1);
6962
- }
6963
- else if (this.width === label.width) {
6964
- spaces.splice(index, 1, new Slot(this.x, this.y + label.height, this.width, this.height - label.height));
6965
- }
6966
- else if (this.height === label.height) {
6967
- spaces.splice(index, 1, new Slot(this.x + label.width, this.y, this.width - label.width, this.height));
6968
- }
6969
- else {
6970
- spaces.splice(index, 1, new Slot(this.x + label.width, this.y, this.width - label.width, label.height), new Slot(this.x, this.y + label.height, this.width, this.height - label.height));
6971
- }
6972
- }
6973
- }
6974
- class AtlasLayer {
6975
- /// Creates a new Atlas Layer,
6976
- constructor(atlas, map, width, height) {
6977
- // height can be anything so we have to match it to a close power of two
6978
- let power = 5; // start with 5 because 2^5 is 32 which is a good lower bounds
6979
- while (Math.pow(2, power) < height) {
6980
- power += 1;
6981
- }
6982
- height = Math.pow(2, power);
6983
- power = 10; // start with 5 because 2^10 is 1024 which is a good lower bounds
6984
- while (Math.pow(2, power) < width) {
6985
- power += 1;
6986
- }
6987
- width = Math.pow(2, power);
6988
- this.atlas = atlas;
6989
- // the map that this layer is used for, since this get rendered
6990
- // as a single object we cannot for any reason reuse a layer for a
6991
- // different map.
6992
- this.map = map;
6993
- this.canvasWidth = width;
6994
- this.canvasHeight = height;
6995
- // create a canvas and context to draw the text labels into
6996
- this.canvas = document.createElement('canvas');
6997
- this.canvas.width = this.canvasWidth;
6998
- this.canvas.height = this.canvasHeight;
6999
- this.context = this.canvas.getContext('2d');
7000
- this.context.textBaseline = 'bottom';
7001
- // the list of free space in the canvas that we can put blocks int
7002
- this.spaces = [new Slot(0, 0, this.canvas.width, this.canvas.height)];
7003
- // an array of free slots for when labels get destroyed
7004
- this.free = [];
7005
- // an array of labels that are contained
7006
- this.labels = [];
7007
- this.count = CANVAS_MAX_LABELS;
7008
- // Create buffers to hold the binary data for rendering data
7009
- let origin = new Float32Array(this.count * 3);
7010
- let size = new Float32Array(this.count * 2);
7011
- let rotation = new Float32Array(this.count);
7012
- let uv = new Float32Array(this.count * 4);
7013
- let color = new Float32Array(this.count * 3);
7014
- this.uv = new InstancedBufferAttribute(uv, 4, false, 1);
7015
- this.uv.set(uv);
7016
- this.uv.needsUpdate = true;
7017
- this.origin = new InstancedBufferAttribute(origin, 3, false, 1);
7018
- this.origin.set(origin);
7019
- this.origin.needsUpdate = true;
7020
- this.size = new InstancedBufferAttribute(size, 2, false, 1);
7021
- this.size.set(size);
7022
- this.size.needsUpdate = true;
7023
- this.rotation = new InstancedBufferAttribute(rotation, 1, false, 1);
7024
- this.rotation.set(rotation);
7025
- this.rotation.needsUpdate = true;
7026
- this.color = new InstancedBufferAttribute(color, 3, true, 1);
7027
- this.color.set(color);
7028
- this.color.needsUpdate = true;
7029
- // these will probably always be zero, but we want to make sure anyhow
7030
- // a none zero sized element might render with an invalid texture
7031
- // coordinate which would be pretty bad.
7032
- for (let i = 0; i < this.count; i++) {
7033
- this.zero(i);
7034
- }
7035
- // create the polygon, this is just a square 1x1
7036
- let positions = new Float32Array([0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0]);
7037
- let indicies = [0, 3, 1, 3, 2, 1];
7038
- // Setup the geometry buffer
7039
- this.geometry = new InstancedBufferGeometry();
7040
- this.geometry.setIndex(indicies);
7041
- this.geometry.setAttribute('position', new BufferAttribute(positions, 3));
7042
- this.geometry.setAttribute('size', this.size);
7043
- this.geometry.setAttribute('origin', this.origin);
7044
- this.geometry.setAttribute('rotation', this.rotation);
7045
- this.geometry.setAttribute('uv', this.uv);
7046
- this.geometry.setAttribute('color', this.color);
7047
- // Three allows for wrapping of a canvas into a texture, neat
7048
- this.texture = new CanvasTexture(this.canvas);
7049
- this.texture.anisotropy = 16;
7050
- // create the shader
7051
- this.material = new RawShaderMaterial();
7052
- this.material.vertexShader = LabelAtlasVertex;
7053
- this.material.fragmentShader = LabelAtlasFragment;
7054
- this.material.uniforms = {
7055
- textureLabel: { value: this.texture },
7056
- };
7057
- // marked as transparent because the we alpha blend the textures
7058
- this.material.transparent = true;
7059
- // create the mesh
7060
- this.mesh = new Mesh(this.geometry, this.material);
7061
- // create a 3D object for the scene graph containing the mesh
7062
- this.obj = new Object3D();
7063
- this.obj.add(this.mesh);
7064
- // This logic uses a custom vertex shader to position and size the
7065
- // actual vertices. This has a side effect of the renderer not actually
7066
- // knowing the size of the object. Disabling the frustum culling
7067
- // prevents this.
7068
- this.obj.frustumCulled = false;
7069
- this.mesh.frustumCulled = false;
7070
- // add to the map
7071
- this.map.add(this.obj);
7072
- }
7073
- // return the number of bytes left in this layer
7074
- space() {
7075
- return this.spaces.map(x => x.width * x.height).reduce((x, y) => x + y);
7076
- }
7077
- // set a index of the arrays to zero.
7078
- zero(index) {
7079
- this.origin.array[index * 3 + 0] = 0;
7080
- this.origin.array[index * 3 + 1] = 0;
7081
- this.origin.array[index * 3 + 2] = 0;
7082
- this.size.array[index * 2 + 0] = 0;
7083
- this.size.array[index * 2 + 1] = 0;
7084
- this.rotation.array[index] = 0;
7085
- this.uv.array[index * 4 + 0] = 0;
7086
- this.uv.array[index * 4 + 1] = 0;
7087
- this.uv.array[index * 4 + 2] = 0;
7088
- this.uv.array[index * 4 + 3] = 0;
7089
- this.color.array[index * 3 + 0] = 0;
7090
- this.color.array[index * 3 + 1] = 0;
7091
- this.color.array[index * 3 + 2] = 0;
7092
- this.origin.needsUpdate = true;
7093
- this.size.needsUpdate = true;
7094
- this.rotation.needsUpdate = true;
7095
- this.uv.needsUpdate = true;
7096
- this.color.needsUpdate = true;
7097
- }
7098
- // find space for a label, if there is no space this will return false.
7099
- // if space was found, the label is added to this Atlas and the label index
7100
- // is set.
7101
- //
7102
- // Note: The label is added for house keeping, the values of the buffers
7103
- // have not been configured yet. And the text has not been rendered
7104
- findSpace(label) {
7105
- // even if there is space, there is no space left in the draw buffers
7106
- if (this.free.length != 0 && this.labels.length == this.count) {
7107
- return false;
7108
- }
7109
- // search 'spaces' for the entry that fits the glyph best. 'best' means
7110
- // least amount of wasted space, ideally 0.
7111
- let best = -1;
7112
- for (let i = 0; i < this.spaces.length; i++) {
7113
- let space = this.spaces[i].leftOverSpace(label);
7114
- if (space === null) {
7115
- continue;
7116
- }
7117
- if (best === -1 || this.spaces[i].leftOverSpace(label) < space) {
7118
- best = i;
7119
- }
7120
- }
7121
- if (best !== -1) {
7122
- this.spaces[best].split(label, this.spaces, best);
7123
- label.layer = this;
7124
- // Append an element to an array if there no free slots,
7125
- // if there are free slots we reuse the space instead.
7126
- if (this.free.length === 0) {
7127
- label.index = this.labels.length;
7128
- this.labels.push(label);
7129
- }
7130
- else {
7131
- label.index = this.free.pop();
7132
- this.labels[label.index] = label;
7133
- }
7134
- }
7135
- // returns true if there was space found.
7136
- return best != -1;
7137
- }
7138
- // remove this label from the internal contents of this atlas.
7139
- remove(label) {
7140
- if (label.index === undefined) {
7141
- return;
7142
- }
7143
- // remove this object from the vertex array.
7144
- this.zero(label.index);
7145
- // clear the pixel memory to avoid reuse of this area getting messed up
7146
- this.context.clearRect(label.x, label.y, label.width, label.height);
7147
- // add the free spaces to the free list
7148
- this.spaces.push(new Slot(label.x, label.y, label.width, label.height));
7149
- this.free.push(label.index);
7150
- this.labels[label.index] = null;
7151
- // check to see if there is actually any labels left
7152
- let empty = true;
7153
- for (let iLabel of this.labels) {
7154
- empty = empty && iLabel != null;
7155
- if (!empty) {
7156
- break;
7157
- }
7158
- }
7159
- // if there are no labels left, remove this layer
7160
- if (empty) {
7161
- this.atlas.removeLayer(this);
7162
- }
7163
- }
7164
- // render a label to the canvas
7165
- render(label) {
7166
- this.context.save();
7167
- this.context.rect(label.x + 1, label.y + 1, label.width - 1 * 2, label.height - 1 * 2);
7168
- this.context.clip();
7169
- this.context.font = label.size + 'px ' + label.font;
7170
- for (let line of label.lines) {
7171
- this.context.fillText(line.text, line.x + label.x + LABEL_PADDING, line.y + label.y + line.height + LABEL_PADDING);
7172
- }
7173
- this.texture.needsUpdate = true;
7174
- this.context.restore();
7175
- }
7176
- // Reset canvas and re-render all labels
7177
- onWebGLContextRestored() {
7178
- this.canvas = document.createElement('canvas');
7179
- this.canvas.width = this.canvasWidth;
7180
- this.canvas.height = this.canvasHeight;
7181
- this.context = this.canvas.getContext('2d');
7182
- this.context.textBaseline = 'bottom';
7183
- this.texture = new CanvasTexture(this.canvas);
7184
- this.texture.anisotropy = 16;
7185
- this.material.uniforms.textureLabel.value = this.texture;
7186
- for (let label of this.labels) {
7187
- if (label != null) {
7188
- this.render(label);
7189
- }
7190
- }
7191
- }
7192
- // free all resources associated with this layer
7193
- dispose() {
7194
- this.geometry.dispose();
7195
- this.material.dispose();
7196
- this.texture.dispose();
7197
- }
7198
- }
7199
- class Atlas {
7200
- constructor() {
7201
- this.layers = [];
7202
- this.canvas = document.createElement('canvas');
7203
- this.context = this.canvas.getContext('2d');
7204
- }
7205
- /// adds label to the atlas
7206
- addLabel(map, lines, font, size, scope, pixelsPerScale) {
7207
- // get the max width / height
7208
- let height = 0;
7209
- let width = 0;
7210
- for (let line of lines) {
7211
- width = Math.max(Math.ceil(line.width + line.x), width);
7212
- height = Math.max(Math.ceil(line.height + line.y), height);
7213
- }
7214
- let label = new Label();
7215
- label.map = map;
7216
- label.width = width + LABEL_PADDING * 2;
7217
- label.height = height + LABEL_PADDING * 2;
7218
- label.lines = lines;
7219
- label.font = font;
7220
- label.size = size;
7221
- label.canvasBounds = scope.canvasBounds;
7222
- label.margin = scope.margin;
7223
- label.heightMargin = scope.heightMargin;
7224
- label.pixelsPerMu = pixelsPerScale;
7225
- label.z = scope.height;
7226
- label.view = scope;
7227
- label.color = scope.color;
7228
- let layer;
7229
- // find a slot to place this label in
7230
- for (let iLayer of this.layers) {
7231
- layer = iLayer;
7232
- if (layer.map !== map) {
7233
- layer = null;
7234
- continue;
7235
- }
7236
- if (layer.findSpace(label)) {
7237
- break;
7238
- }
7239
- layer = null;
7240
- }
7241
- // no space was found, create a new layer
7242
- if (layer == null) {
7243
- layer = new AtlasLayer(this, map, label.width, label.height);
7244
- this.layers.push(layer);
7245
- if (!layer.findSpace(label)) {
7246
- return;
7247
- }
7248
- // order the layers such that the least populated ones are first.
7249
- this.layers.sort((a, b) => b.space() - a.space());
7250
- }
7251
- layer.render(label);
7252
- label.layout(map.mapClass, layer.origin, layer.size, layer.rotation, layer.uv, layer.color);
7253
- label.layer = layer;
7254
- return label;
7255
- }
7256
- // delete a layer from the list of layers
7257
- removeLayer(layer) {
7258
- let index = this.layers.indexOf(layer);
7259
- if (index != -1) {
7260
- this.layers.splice(index, 1);
7261
- }
7262
- }
7263
- // measure the size of a element with a given font and size
7264
- measure(text, font, size) {
7265
- this.context.font = size + 'px ' + font;
7266
- return this.context.measureText(text);
7267
- }
7268
- // Retore all layers
7269
- onWebGLContextRestored() {
7270
- for (let layer of this.layers) {
7271
- layer.onWebGLContextRestored();
7272
- }
7273
- }
7274
- // cleans up all the layers of the Atlas
7275
- dispose() {
7276
- for (let layer of this.layers) {
7277
- layer.dispose();
7278
- }
7279
- }
7280
- }
7281
- class FlatLabel {
7282
- constructor(options, venue, mapObject, DEFAULT_FONT, polygonMeshesById, textLabelsByPolygonId, mapView, scope, atlas) {
7283
- let polygon = getObject(options.polygon, venue.polygons);
7284
- this.id = polygon.id;
7285
- this.text = (options.shortText || options.text).trim();
7286
- this.stateText = options.stateText ? ' (' + options.stateText + ')' : '';
7287
- this.fullText = this.stateText ? this.text + this.stateText : this.text;
7288
- this.font = options.font || DEFAULT_FONT;
7289
- this.atlas = atlas;
7290
- this.canvasBounds = options.canvasBounds || polygon.canvasBounds;
7291
- this.mapScale = mapObject.getMapScale() || 1;
7292
- this.margin = (options.margin || DEFAULT_LABEL_SIZING.MARGIN) * this.mapScale;
7293
- this.heightMargin = (options.heightMargin || DEFAULT_LABEL_SIZING.HEIGHT_MARGIN) * this.mapScale;
7294
- this.scaleMin = options.scaleMin || 0.25;
7295
- this.scaleStep = options.scaleStep > 0 ? options.scaleStep : 0.25;
7296
- this.multiline = options.multiline == undefined ? true : options.multiline;
7297
- this.height = Number(options.height) || null;
7298
- this.polygonMeshesById = polygonMeshesById;
7299
- this.polyId = options.polygonId || getObjectId(options.polygon, venue.polygons);
7300
- this.map = mapObject;
7301
- this.color = new Color(options.color || scope.colors.text || '#000000');
7302
- this.hoverColor = new Color(options.hoverColor || this.color);
7303
- this.baseColor = this.color;
7304
- this.hideOnCreate = false;
7305
- this.hoverLabelText = options.text.trim();
7306
- this.fontSize = options.fontSize * this.mapScale || options.size * SIZE_IN_METERS || FONT_SIZE;
7307
- this.hoverLabelMode = options.hoverLabelMode3D || HoverLabel.MODES.ALL;
7308
- this.hoverLabelClass = options.hoverLabelClass || 'mMapviewHoverLabel';
7309
- this.showHoverLabel =
7310
- this.hoverLabelMode == HoverLabel.MODES.ALL || this.hoverLabelMode == HoverLabel.MODES.NO_TEXT_LABEL_ONLY;
7311
- this.hoverIfLabelFails =
7312
- this.hoverLabelMode == HoverLabel.MODES.ALL || this.hoverLabelMode == HoverLabel.MODES.NO_TEXT_LABEL_ONLY;
7313
- this.textLabelsByPolygonId = textLabelsByPolygonId;
7314
- this.map.textObjects.push(this);
7315
- }
7316
- create() {
7317
- let scope = this;
7318
- // This could get killed before we even get to creation
7319
- if (this.doNotCreate) {
7320
- return;
7321
- }
7322
- if (scope.canvasBounds == null) {
7323
- scope.invalid = true;
7324
- scope.message = "No Canvas Bounds. Can't draw text '" + scope.text + "'";
7325
- scope.showHoverLabel = scope.hoverIfLabelFails;
7326
- return;
7327
- }
7328
- scope.showHoverLabel = this.hoverLabelMode == HoverLabel.MODES.ALL;
7329
- let muPerPixel = METERS_PER_PIXEL * this.mapScale;
7330
- let pixelsPerMu = PIXELS_PER_METER / this.mapScale;
7331
- let labelsToAttempt = [this.text];
7332
- if (this.stateText.length)
7333
- labelsToAttempt.unshift(this.fullText);
7334
- for (let attempt of labelsToAttempt) {
7335
- // attempt to layout the words
7336
- let words = attempt.split(' ').map(function (word) {
7337
- return {
7338
- width: scope.atlas.measure(word, scope.font, scope.fontSize).width,
7339
- word: word,
7340
- };
7341
- });
7342
- // convert the canvas bounds to an area minus in pixels
7343
- let availableWidth = (this.canvasBounds.maxWidth - this.margin * 2) * pixelsPerMu;
7344
- let availableHeight = (this.canvasBounds.maxHeight - this.heightMargin * 2) * pixelsPerMu;
7345
- let maxShrinkSteps = (1 - this.scaleMin) / this.scaleStep;
7346
- let minimumShrinkSteps = 0;
7347
- let spaceWidth = this.atlas.measure(' ', this.font, this.fontSize).width;
7348
- let lineHeight = getTextHeight(this.fontSize + 'px ' + this.font);
7349
- for (let i = 0, iLen = words.length; i < iLen; i++) {
7350
- let width = words[i].width;
7351
- let wShrinkSteps = Math.ceil((width - availableWidth) / (width * this.scaleStep));
7352
- minimumShrinkSteps = Math.max(wShrinkSteps, minimumShrinkSteps);
7353
- }
7354
- if (this.height == null) {
7355
- if (this.polygonMeshesById[this.polyId]) {
7356
- let target = this.polygonMeshesById[this.polyId];
7357
- if (!target.geometry.boundingBox) {
7358
- target.geometry.computeBoundingBox();
7359
- }
7360
- this.height = target.geometry.boundingBox.max.z + target.position.z;
7361
- }
7362
- else {
7363
- this.height = 0;
7364
- }
7365
- }
7366
- var lines;
7367
- var shrinkStep;
7368
- var currentScale;
7369
- let scaleStep = this.scaleStep;
7370
- shrink: for (shrinkStep = minimumShrinkSteps; shrinkStep <= maxShrinkSteps; shrinkStep++) {
7371
- currentScale = 1 - shrinkStep * scaleStep;
7372
- let thisLineWidth = words[0].width * currentScale;
7373
- lines = [
7374
- {
7375
- text: words[0].word,
7376
- width: thisLineWidth,
7377
- height: lineHeight * currentScale,
7378
- x: 0,
7379
- y: 0,
7380
- },
7381
- ];
7382
- for (let iWordSize = 1; iWordSize < words.length; iWordSize++) {
7383
- let wordSize = words[iWordSize];
7384
- let spaceForNextWord = (wordSize.width + spaceWidth) * currentScale;
7385
- if (thisLineWidth + spaceForNextWord < availableWidth) {
7386
- // Add to line
7387
- lines[lines.length - 1].text += ' ' + wordSize.word;
7388
- lines[lines.length - 1].width += spaceForNextWord;
7389
- thisLineWidth += spaceForNextWord;
7390
- }
7391
- else {
7392
- // New line
7393
- if (!this.multiline)
7394
- continue shrink;
7395
- lines[lines.length - 1].width = thisLineWidth;
7396
- thisLineWidth = wordSize.width * currentScale;
7397
- lines.push({
7398
- text: wordSize.word,
7399
- width: thisLineWidth,
7400
- height: lineHeight * currentScale,
7401
- x: 0,
7402
- y: lines[lines.length - 1].height + lines[lines.length - 1].y,
7403
- });
7404
- }
7405
- }
7406
- let requiredHeight = lines.length * lineHeight * currentScale;
7407
- if (requiredHeight <= availableHeight) {
7408
- this.invalid = false;
7409
- break;
7410
- }
7411
- }
7412
- if (this.invalid == false)
7413
- break;
7414
- }
7415
- if (this.invalid == false) {
7416
- // center each line
7417
- let lineTotalWidth = lines.map(x => Math.ceil(x.width)).reduce((a, b) => Math.max(a, b));
7418
- for (let line of lines) {
7419
- line.x = (lineTotalWidth - line.width) / 2;
7420
- }
7421
- // lines, font, size, scope, pixelsPerScale
7422
- this.label = this.atlas.addLabel(this.map, lines, this.font, this.fontSize * currentScale, this, muPerPixel);
7423
- }
7424
- this.created = true;
7425
- }
7426
- // eslint-disable-next-line class-methods-use-this
7427
- flipIfNeeded(cameraAngle) {
7428
- // this is done in the shader, so we will not do anything here
7429
- // this function simply exists for parity with 'TextLabel'
7430
- }
7431
- removeSelf(bulk) {
7432
- if (this.created) {
7433
- if (this.label != null) {
7434
- this.label.layer.remove(this.label);
7435
- this.label = null;
7436
- }
7437
- if (!bulk) {
7438
- this.map.textObjects = this.map.textObjects.filter(object => object != this);
7439
- // Delete from textLabelsByPolygonId
7440
- Object.keys(this.textLabelsByPolygonId).forEach(key => {
7441
- if (this.textLabelsByPolygonId[key] == this) {
7442
- delete this.textLabelsByPolygonId[key];
7443
- }
7444
- });
7445
- }
7446
- }
7447
- else {
7448
- this.doNotCreate = true;
7449
- }
7450
- }
7451
- setColor(textColor) {
7452
- const color = new Color(textColor);
7453
- const sameColor = this.color.equals(color);
7454
- const withLabel = Boolean(this.label?.layer);
7455
- this.color = color;
7456
- if (!sameColor && withLabel) {
7457
- this.label.color = this.color;
7458
- this.label.layout(this.map.mapClass, this.label.layer.origin, this.label.layer.size, this.label.layer.rotation, this.label.layer.uv, this.label.layer.color);
7459
- }
7460
- }
7461
- setHoverColor = color => {
7462
- this.hoverColor = new Color(color);
7463
- };
7464
- clearColor() {
7465
- this.setColor(this.baseColor);
7466
- }
7467
- hide() {
7468
- if (this.label) {
7469
- this.label.layer.zero(this.label.index);
7470
- }
7471
- else {
7472
- this.hideOnCreate = true;
7473
- }
7474
- }
7475
- show() {
7476
- if (this.label) {
7477
- let layer = this.label.layer;
7478
- let map = this.map;
7479
- let label = this.label;
7480
- label.layout(map.mapClass, layer.origin, layer.size, layer.rotation, layer.uv, layer.color);
7481
- }
7482
- else {
7483
- this.hideOnCreate = false;
7484
- }
7485
- }
7486
- toString() {
7487
- return this.hoverLabelText;
7488
- }
7489
- }
7490
- let textHeightCache = new Map();
7491
- var getTextHeight = function (font) {
7492
- // check if the font is in the cache because modifying the dom is expensive
7493
- let height = textHeightCache[font];
7494
- if (height != undefined) {
7495
- return height;
7496
- }
7497
- let text = document.createElement('span');
7498
- text.textContent = 'Hg';
7499
- text.style.font = font;
7500
- let block = document.createElement('div');
7501
- block.style.cssText = 'display: inline-block; width: 1px; height: 0px;';
7502
- let div = document.createElement('div');
7503
- div.appendChild(text);
7504
- div.appendChild(block);
7505
- let body = document.body;
7506
- body.appendChild(div);
7507
- try {
7508
- var bounding = text.getBoundingClientRect();
7509
- }
7510
- finally {
7511
- div.remove();
7512
- }
7513
- // add the element to the cache
7514
- if (bounding.height != undefined) {
7515
- textHeightCache[font] = bounding.height;
7516
- height = bounding.height;
7517
- }
7518
- return height;
7519
- };
7520
- export { Atlas, FlatLabel };
7521
-
7522
6930
  declare module '@mappedin/mappedin-js/renderer/internal/Mappedin.HoverLabel' {
7523
6931
  export default HoverLabel;
7524
6932
  /**
@@ -7664,32 +7072,48 @@ declare module '@mappedin/mappedin-js/renderer/internal/Mappedin.HTMLCollider' {
7664
7072
 
7665
7073
  declare module '@mappedin/mappedin-js/renderer/MapView.Scene' {
7666
7074
  import { MappedinMap } from '@mappedin/mappedin-js/get-venue';
7667
- import type { ICore, TSceneTransitionOptions } from '@mappedin/mappedin-js/renderer/internal';
7668
- import { MapObject } from '@mappedin/mappedin-js/renderer/internal';
7669
- export function getPanBoundsForObject(mapObject: MapObject, currentPanBounds: any): {
7670
- min: any;
7671
- max: any;
7672
- margin: any;
7673
- radius: any;
7674
- center: any;
7675
- };
7075
+ import type { ICore, SceneManager, TSceneTransitionOptions } from '@mappedin/mappedin-js/renderer/internal';
7076
+ import { MapObject, CAMERA_EASING_MODE } from '@mappedin/mappedin-js/renderer/internal';
7077
+ export const AUTO_FOCUS_DURATION = 500;
7078
+ export const AUTO_FOCUS_EASING = CAMERA_EASING_MODE.EASE_OUT;
7079
+ export const STARTING_TILT = 0.6;
7080
+ export const STARTING_ROTATION: undefined;
7676
7081
  class MapViewScene {
7677
- #private;
7678
- maps: MappedinMap[];
7679
- object: any;
7680
- currentMap: MappedinMap;
7681
- mapObjects: Map<MappedinMap['id'], MapObject>;
7682
- constructor(maps: MappedinMap[], core: ICore);
7683
- /**
7684
- * Determine each maps position and rotation relative to the refernce map
7685
- */
7686
- determineMapPositionAndRotation(map: MappedinMap): {
7687
- position: any[];
7688
- scale: number[];
7689
- rotation: number[];
7690
- };
7691
- /** Render scene */
7692
- render(map: MappedinMap, transitionOptions?: TSceneTransitionOptions, isStartingScene?: boolean): Promise<void>;
7082
+ #private;
7083
+ maps: MappedinMap[];
7084
+ mapObjectsArray: MapObject[];
7085
+ transitionOptions: TSceneTransitionOptions | undefined;
7086
+ object: any;
7087
+ verticalPaths: any;
7088
+ currentMap: MappedinMap;
7089
+ mapObjects: Map<MappedinMap['id'], MapObject>;
7090
+ constructor(maps: MappedinMap[], core: ICore);
7091
+ get mapObjectsByMapGroup(): {
7092
+ [x: string]: MapObject[];
7093
+ };
7094
+ /**
7095
+ * Determine each maps position and rotation relative to the refernce map
7096
+ */
7097
+ determineMapPositionAndRotation(map: MappedinMap): {
7098
+ position: any[];
7099
+ scale: number[];
7100
+ rotation: number[];
7101
+ };
7102
+ /**
7103
+ * Convenience function to initialize scene (for stacked maps)
7104
+ */
7105
+ prepare(sceneManager: SceneManager, transitionOptions?: TSceneTransitionOptions): Promise<void>;
7106
+ /**
7107
+ * Override this method if works need to be done when the window is resized
7108
+ */
7109
+ resize(): void;
7110
+ focusOnCurrentMap(transitionOptions?: TSceneTransitionOptions, isStartingScene?: boolean): Promise<unknown>;
7111
+ preRender(transitionOptions?: TSceneTransitionOptions): Promise<Promise<MapObject>[]>;
7112
+ /**
7113
+ * Override this method when unmounting a scene
7114
+ */
7115
+ unmount(): void;
7116
+ postRender(): void;
7693
7117
  }
7694
7118
  export default MapViewScene;
7695
7119
  }
@@ -7763,7 +7187,7 @@ declare module '@mappedin/mappedin-js/renderer/internal/Mappedin.Element' {
7763
7187
  static imagesLoadingInProgress: Map<string, Promise<[any, any]>>;
7764
7188
  _addImage(polygon: MappedinPolygon, mapClass: MappedinMap): void;
7765
7189
  static _normalizeColor(color: any): number;
7766
- static _getMaterial(color: string, opacity: number): any;
7190
+ static _getMaterial(color: string, opacity: number, map: MappedinMap['id']): any;
7767
7191
  static _clip(vertices: any, offset: any): any;
7768
7192
  }
7769
7193
  export default Element;
@@ -8174,2257 +7598,6 @@ declare module '@mappedin/mappedin-js/renderer/internal/Mappedin.BlueDot/Mappedi
8174
7598
  export default BlueDotUI;
8175
7599
  }
8176
7600
 
8177
- import { Euler, EventDispatcher, MOUSE, PlaneGeometry, Matrix4, MeshBasicMaterial, Mesh, Raycaster, Vector2, Vector3, Clock, } from 'three';
8178
- import TouchAnchor from '@mappedin/mappedin-js/renderer/internal/Mappedin.CameraControls.TouchAnchor';
8179
- import InputSet from '@mappedin/mappedin-js/renderer/internal/Mappedin.CameraControls.InputSet';
8180
- import { getProjectionScaleFactor, throttle, debounce } from '@mappedin/mappedin-js/renderer/internal/utils';
8181
- import { VIEW_STATE } from '@mappedin/mappedin-js/renderer/internal/Mappedin.MultiFloorView';
8182
- import { DebugUICheckbox } from '@mappedin/mappedin-js/renderer/internal/Mappedin.DebugUIControl';
8183
- import TWEEN from '@tweenjs/tween.js';
8184
- const CAMERA_CLIPPING_RADIUS = 10000;
8185
- const CAMERA_CONTROL_OPTIONS = {
8186
- chain: 'chain',
8187
- cancel: 'cancel',
8188
- };
8189
- const SCROLL_THROTTLE_MS = 100;
8190
- const dispatcher = new EventDispatcher();
8191
- /**
8192
- * The advanced, manual camera controls for {{#crossLink "MapView"}}{{/crossLink}}. You probably don't need to use this at all, instead relying on the MapView's {{#crossLink "MapView/focusOn:method"}}{{/crossLink}}, {{#crossLink "MapView/resetCamera:method"}}{{/crossLink}} and built in touch/mouse controls.
8193
- * This class will let you do things like change the min/max zoom, tilt, and pan, attach to camera events, and move/animate the camera to specifc points.
8194
- *
8195
- * The camera works by setting an anchor point on the scene at ground level and pointing the camera at it. {{#crossLink "CameraControls/zoom:method"}}{{/crossLink}} controls how far the camera is from the anchor, and {{#crossLink "CameraControls/tilt:method"}}{{/crossLink}}/{{#crossLink "CameraControls/rotate:method"}}{{/crossLink}} controls the angle the camera is rotated about it.
8196
- * The camera will always be pointed directly at the anchor point. {{#crossLink "CameraControls/pan:method"}}{{/crossLink}} or {{#crossLink "CameraControls/setPosition:method"}}{{/crossLink}} will move that anchor around on the 2D ground plane.
8197
- *
8198
- * Created for you automatically with a MapView, don't re-create yourself.
8199
- * @type {any}
8200
- *
8201
- * @class CameraControls
8202
- */
8203
- let CameraControls = function (camera, canvas, scene, core) {
8204
- /**
8205
- * Dispatcher
8206
- */
8207
- this.dispatcher = dispatcher;
8208
- this.camera = camera;
8209
- this.canvas = canvas;
8210
- this.elevation = camera.parent;
8211
- this.orbit = this.elevation.parent;
8212
- let intersection;
8213
- let TWO_PI = Math.PI * 2;
8214
- // @TODO: figure out a zero-magic solution
8215
- const CAMERA_ZRANGE = 100;
8216
- /**
8217
- * Factor that controls how fast zooming in and out happens in response to mouse wheel events
8218
- *
8219
- * @property zoomSpeed {Float}
8220
- * @default 5.0
8221
- */
8222
- this.zoomSpeed = 5.0;
8223
- /**
8224
- * Factor to multiple mouse movement by to get tilt/rotation.
8225
- *
8226
- * @property rotateSpeed {Float}
8227
- * @default 100
8228
- */
8229
- this.rotateSpeed = 100;
8230
- /**
8231
- * Disable or re-enable user input.
8232
- *
8233
- * @property enabled {Boolean}
8234
- * @default true
8235
- */
8236
- this.enabled = true;
8237
- /**
8238
- * Disable or re-enable user zoom.
8239
- *
8240
- * @property enableZoom {Boolean}
8241
- * @default true
8242
- */
8243
- this.enableZoom = true;
8244
- /**
8245
- * Disable or re-enable user pan.
8246
- *
8247
- * @property enablePan {Boolean}
8248
- * @default true
8249
- */
8250
- this.enablePan = true;
8251
- /**
8252
- * Disable or re-enable user pedestal.
8253
- *
8254
- * @property enablePedestal {Boolean}
8255
- * @default false
8256
- */
8257
- this.enablePedestal = false;
8258
- /**
8259
- * Max amount to allow scrolling maps down
8260
- * (In Z-axis units, at the origin, down is positive)
8261
- *
8262
- * @property maxPedestal {Number}
8263
- * @default Infinity
8264
- */
8265
- this.maxPedestal = Infinity;
8266
- /**
8267
- * Max amount to allow scrolling maps up
8268
- * (In Z-axis units, at the origin, up is negative)
8269
- *
8270
- * @property minPedestal {Number}
8271
- * @default -Infinity
8272
- */
8273
- this.minPedestal = -Infinity;
8274
- /**
8275
- * Disable or re-enable user tilt/rotation.
8276
- *
8277
- * @property enableRotate {Boolean}
8278
- * @default true
8279
- */
8280
- this.enableRotate = true;
8281
- /**
8282
- * This is actually the minium distance the camera can get from it's anchor on the ground. May be worth changing if your map has very tall buildings to avoid the camera clipping through them.
8283
- *
8284
- * @property minZoom {Number}
8285
- * @default 375
8286
- */
8287
- this.minZoom = 375;
8288
- /**
8289
- * Maximum distance the camera can get from it's anchor on the ground. Setting this too high will result in parts of the map falling out of the camera's clipping plane and disappearing.
8290
- *
8291
- * @property maxZoom {Number}
8292
- * @default 10000
8293
- */
8294
- this.maxZoom = 10000;
8295
- /**
8296
- * Initial min zoom; zoom cannot be restricted beyond this.
8297
- *
8298
- * @property initialMinZoom {Number}
8299
- * @default 375
8300
- */
8301
- this.initialMinZoom = 375;
8302
- /**
8303
- * Initial max zoom; zoom cannot be restricted beyond this.
8304
- *
8305
- * @property initialMaxZoom {Number}
8306
- * @default 10000
8307
- */
8308
- this.initialMaxZoom = 10000;
8309
- /**
8310
- * ignoreZoomLimits; use with caution for special effects
8311
- * @default false
8312
- */
8313
- this.ignoreZoomLimits = false;
8314
- // Internal Documentation
8315
- this.minExpandedZoom = 375;
8316
- // Internal Documentation
8317
- this.maxExpandedZoom = 100000;
8318
- /**
8319
- * Multiplier for min and max zoom, for convenience.
8320
- *
8321
- * @property zoomFactor {Number}
8322
- * @default 1
8323
- */
8324
- this.zoomFactor = 1;
8325
- /**
8326
- * Constrains the camera from panning to far away from the scene. It's set automatically based on the size of the map.
8327
- * If you want to change anything, you probably want to change the margin property, which is the factor the min and max in
8328
- * each dimension are multiplied by to give the true bounds. For example, on a truely huge venue a 1.25 margin could get you
8329
- * way out into space when zoomed in.
8330
- *
8331
- * @property panBounds {Object}
8332
- @property panBounds.margin {Number} The factor the multiply the size of the geometery by to give the true camera bounds.
8333
- @property panBounds.min {Object} An x, y pair representing the bounds of one corner of the map.
8334
- @property panBounds.max {Object} An x, y pair representing the bounds of the other corner of the map.
8335
- */
8336
- this.panBounds = {
8337
- margin: 1.25,
8338
- min: {
8339
- x: Infinity,
8340
- y: Infinity,
8341
- },
8342
- max: {
8343
- x: -Infinity,
8344
- y: -Infinity,
8345
- },
8346
- center: {
8347
- x: 0,
8348
- y: 0,
8349
- },
8350
- radius: 200,
8351
- };
8352
- /**
8353
- * Minium camera tilt, in radians. If it's anything other than 0, you won't be able to look at the venue from the top down perspective.
8354
- *
8355
- * @property minTilt {Number}
8356
- * @default 0.0
8357
- */
8358
- this.minTilt = 0.0;
8359
- /**
8360
- * Minium camera tilt, in radians. If you set it too high, the camera will be able to tilt down through the geometery of the scene, which will produce clipping issues.
8361
- *
8362
- * @property maxTilt {Number}
8363
- * @default 1.2
8364
- */
8365
- this.maxTilt = 1.2;
8366
- /**
8367
- * If you would really prefer to pan with the right mouse button and tilt/rotate with the left, you can swap the values here to achieve that.
8368
- *
8369
- * @property mouseButtons {Object}
8370
- @property mouseButtons.ORBIT=MOUSE.RIGHT {MOUSE} The button to use for tilt/rotation. Defaults to `MOUSE.RIGHT`.
8371
- @property mouseButtons.ZOOM=MOUSE.MIDDLE {MOUSE} The button to use for zoom behaviour. Don't change this.
8372
- @property mouseButtons.PAN=MOUSE.LEFT {MOUSE} The button to use for panning the camera. Defaults to `MOUSE.LEFT`.
8373
- */
8374
- this.mouseButtons = {
8375
- ORBIT: MOUSE.RIGHT,
8376
- ZOOM: MOUSE.MIDDLE,
8377
- PAN: MOUSE.LEFT,
8378
- };
8379
- let scope = this;
8380
- let cameraPlaneGeometery = new PlaneGeometry(1000000, 1000000); // Should set this to be the map bounds later
8381
- let cameraPlaneMaterial = new MeshBasicMaterial({
8382
- color: 0x000000,
8383
- visible: false,
8384
- });
8385
- let cameraPlane = new Mesh(cameraPlaneGeometery, cameraPlaneMaterial);
8386
- let raycaster = new Raycaster();
8387
- scene.add(cameraPlane);
8388
- let STATE = {
8389
- NONE: -1,
8390
- ROTATE: 0,
8391
- DOLLY: 1,
8392
- PAN: 2,
8393
- WHEEL_ZOOM: 3,
8394
- TOUCH_TILT: 4,
8395
- TOUCH_DOLLY: 5,
8396
- TOUCH_PAN: 6,
8397
- MULTI: 7,
8398
- PEDESTAL: 8,
8399
- TOUCH_PEDESTAL: 9,
8400
- };
8401
- let state = STATE.NONE;
8402
- let mouse = new Vector2();
8403
- let touches = [];
8404
- let touchOrigin = { offsetLeft: 0, offsetTop: 0 };
8405
- let rotateStart = new Vector2();
8406
- let rotateEnd = new Vector2();
8407
- let rotateDelta = new Vector2();
8408
- let panStart = new Vector2();
8409
- let panCameraStart = new Vector2();
8410
- let panEnd = new Vector2();
8411
- let panDelta = new Vector3();
8412
- let floorAnchor = new Vector3();
8413
- let dollyStart = new Vector2();
8414
- let dollyEnd = new Vector2();
8415
- let dollyDelta = new Vector2();
8416
- let resetZoom = false; // Indicates whether to reset the camera position and zoom level when panning while mouse zooming
8417
- let zoomStart;
8418
- let clock = new Clock(true);
8419
- let lastWheelTime = 0;
8420
- // If true, the controls will not be limited by min and max values.
8421
- let stayInsideBounds = true;
8422
- let WHEEL_ZOOM_MULTIPLIER = 10000;
8423
- // var currentTween = null;
8424
- /**
8425
- * Camera events you can attach a listener to (with `controls.addListener(event, function)`), if you want to do certain things. They'll be fired both by touch events and by functions you can call yourself like pan() and tilt()/
8426
- *
8427
- * @property CAMERA_EVENTS {Object}
8428
- * @final
8429
- @property CAMERA_EVENTS.CHANGE_EVENT {Object} Fired whenever the camera changes.
8430
- @property CAMERA_EVENTS.PAN_START_EVENT {Object} Fired when the camera starts panning.
8431
- @property CAMERA_EVENTS.PAN_END_EVENT {Object} Fired when the camera finishes panning.
8432
- @property CAMERA_EVENTS.ROTATE_START_EVENT {Object} Fired when the camera starts rotating.
8433
- @property CAMERA_EVENTS.ROTATE_END_EVENT {Object} Fired when the camera stops rotating.
8434
- @property CAMERA_EVENTS.ZOOM_START_EVENT {Object} Fired when the camera starts zooming.
8435
- @property CAMERA_EVENTS.ZOOM_END_EVENT {Object} Fired when the camera finishes zooming.
8436
- @property CAMERA_EVENTS.MULTI_START_EVENT {Object} Fired when the camera starts animating or you called setMulti. It means any one or more of pan, tilt, rotate and zoom could be changing. The individual pan/tilt/rotate/zoom events will NOT be fired.
8437
- @property CAMERA_EVENTS.MULTI_END_EVNT {Object} Fired when the camera stops animating, or has finished the setMulti call.
8438
- */
8439
- this.CAMERA_EVENTS = {};
8440
- this.CAMERA_EVENTS.CHANGE_EVENT = { type: 'change' };
8441
- this.CAMERA_EVENTS.PAN_START_EVENT = { type: 'panStart' };
8442
- this.CAMERA_EVENTS.PAN_END_EVENT = { type: 'panEnd' };
8443
- this.CAMERA_EVENTS.PEDESTAL_START_EVENT = { type: 'pedestalStart' };
8444
- this.CAMERA_EVENTS.PEDESTAL_CHANGE_EVENT = { type: 'pedestalChange' };
8445
- this.CAMERA_EVENTS.PEDESTAL_END_EVENT = { type: 'pedestalEnd' };
8446
- this.CAMERA_EVENTS.ROTATE_START_EVENT = { type: 'rotateStart' };
8447
- this.CAMERA_EVENTS.ROTATE_END_EVENT = { type: 'rotateEnd' };
8448
- this.CAMERA_EVENTS.ZOOM_START_EVENT = { type: 'zoomStart' };
8449
- this.CAMERA_EVENTS.ZOOM_END_EVENT = { type: 'zoomEnd' };
8450
- this.CAMERA_EVENTS.MULTI_START_EVENT = { type: 'multiStart' };
8451
- this.CAMERA_EVENTS.MULTI_END_EVENT = { type: 'multiEnd' };
8452
- this.CAMERA_EVENTS.MULTI_CANCEL_EVENT = { type: 'multiCancel' };
8453
- this.INTERACTION_EVENTS = {};
8454
- this.INTERACTION_EVENTS.USER_PAN_START_EVENT = { type: 'userPanStart' };
8455
- this.INTERACTION_EVENTS.USER_PEDESTAL_START_EVENT = {
8456
- type: 'userPedestalStart',
8457
- };
8458
- this.INTERACTION_EVENTS.USER_ROTATE_START_EVENT = { type: 'userRotateStart' };
8459
- this.INTERACTION_EVENTS.USER_DOLLY_START_EVENT = { type: 'userDollyStart' };
8460
- this.INTERACTION_EVENTS.USER_ZOOM_START_EVENT = { type: 'userZoomStart' };
8461
- this.INTERACTION_EVENTS.USER_TILT_START_EVENT = { type: 'userTiltStart' };
8462
- this.INTERACTION_EVENTS.USER_PAN_END_EVENT = { type: 'userPanEnd' };
8463
- this.INTERACTION_EVENTS.USER_PEDESTAL_END_EVENT = { type: 'userPedestalEnd' };
8464
- this.INTERACTION_EVENTS.USER_ROTATE_END_EVENT = { type: 'userRotateEnd' };
8465
- this.INTERACTION_EVENTS.USER_DOLLY_END_EVENT = { type: 'userDollyEnd' };
8466
- this.INTERACTION_EVENTS.USER_ZOOM_END_EVENT = { type: 'userZoomEnd' };
8467
- this.INTERACTION_EVENTS.USER_TILT_END_EVENT = { type: 'userTiltEnd' };
8468
- this.UPDATE_EVENTS = {};
8469
- this.UPDATE_EVENTS.POSITION_UPDATED_EVENT = { type: 'positionUpdated' };
8470
- this.UPDATE_EVENTS.ZOOM_UPDATED_EVENT = { type: 'zoomUpdated' };
8471
- this.UPDATE_EVENTS.TILT_UPDATED_EVENT = { type: 'tiltUpdated' };
8472
- this.UPDATE_EVENTS.ROTATION_UPDATED_EVENT = { type: 'rotationUpdated' };
8473
- let userInteracting = false;
8474
- const setUserInteracting = () => {
8475
- userInteracting = true;
8476
- };
8477
- const unsetUserInteracting = () => {
8478
- userInteracting = false;
8479
- };
8480
- for (let e in this.INTERACTION_EVENTS) {
8481
- const event = this.INTERACTION_EVENTS[e];
8482
- if (event.type.endsWith('Start')) {
8483
- scope.dispatcher.addEventListener(event.type, setUserInteracting);
8484
- }
8485
- }
8486
- for (let e in this.INTERACTION_EVENTS) {
8487
- const event = this.INTERACTION_EVENTS[e];
8488
- if (event.type.endsWith('End')) {
8489
- scope.dispatcher.addEventListener(event.type, unsetUserInteracting);
8490
- }
8491
- }
8492
- const dispatchPedestalChangeEvent = throttle(newZ => {
8493
- scope.dispatcher.dispatchEvent({
8494
- ...scope.CAMERA_EVENTS.PEDESTAL_CHANGE_EVENT,
8495
- pedestal: newZ,
8496
- scrolledToTop: scope.scrolledToTop,
8497
- scrolledToBottom: scope.scrolledToBottom,
8498
- scrollPercent: scope.scrollPercent,
8499
- });
8500
- }, SCROLL_THROTTLE_MS);
8501
- this.addDebugControls = function (debugUI) {
8502
- debugUI.addDebugControls({
8503
- CameraControls: [
8504
- {
8505
- name: 'Stay Inside Bounds',
8506
- description: 'Ensure the camera stays inside its bounds.\n\n' +
8507
- 'Caution: going too far outside-of-bounds may slow rendering' +
8508
- 'significantly.',
8509
- control: new DebugUICheckbox({
8510
- current: true,
8511
- onValueChanged: stay => {
8512
- stayInsideBounds = stay;
8513
- },
8514
- }),
8515
- },
8516
- ],
8517
- });
8518
- };
8519
- this.removeDebugControls = function (debugUI) {
8520
- debugUI.removeDebugControls({
8521
- CameraControls: ['Stay Inside Bounds'],
8522
- });
8523
- };
8524
- /**
8525
- * Pans the camera right and down from the current position
8526
- *
8527
- * @method pan
8528
- * @param right {Number} The units to move right. Negative will pan left. This is in relation to the global coordinate system, not the current camera rotation.
8529
- * @param down {Number} The units to move down. Negative will pan up. This is in relation to the global coordinate system, not the current camera rotation.
8530
- */
8531
- this.pan = function (right, down) {
8532
- if (isNaN(right) || isNaN(down)) {
8533
- return;
8534
- }
8535
- scope.setPosition(scope.orbit.position.x + right, scope.orbit.position.y + down);
8536
- scope.dispatcher.dispatchEvent(scope.UPDATE_EVENTS.POSITION_UPDATED_EVENT);
8537
- };
8538
- /**
8539
- * Sets the camera anchor to a specifc x/y positon, in the global reference frame. 0,0 will be roughly the middle of the map, and panBounds holds the min/max points.
8540
- *
8541
- * @method setPosition
8542
- * @param x {Number} The x position to move the camera to. +x will take you right, from the default camera rotation of 0.
8543
- * @param y {Number} The y position to move the camera to. +y will take you down (towards the viewer) in the default camera rotation of 0.
8544
- */
8545
- this.setPosition = function (x, y) {
8546
- if (isNaN(x) || isNaN(y)) {
8547
- return;
8548
- }
8549
- if (state == STATE.NONE) {
8550
- scope.dispatcher.dispatchEvent(scope.CAMERA_EVENTS.PAN_START_EVENT);
8551
- }
8552
- const dx = x - scope.panBounds.center.x;
8553
- const dy = y - scope.panBounds.center.y;
8554
- const targetDistanceFromBounds = Math.sqrt(dx * dx + dy * dy);
8555
- if (stayInsideBounds && targetDistanceFromBounds > scope.panBounds.radius) {
8556
- const angle = Math.atan2(dy, dx);
8557
- const target = {
8558
- x: Math.cos(angle) * scope.panBounds.radius + scope.panBounds.center.x,
8559
- y: Math.sin(angle) * scope.panBounds.radius + scope.panBounds.center.y,
8560
- };
8561
- scope.orbit.position.x = target.x;
8562
- scope.orbit.position.y = target.y;
8563
- }
8564
- else {
8565
- scope.orbit.position.x = x;
8566
- scope.orbit.position.y = y;
8567
- }
8568
- scope.dispatcher.dispatchEvent(scope.UPDATE_EVENTS.POSITION_UPDATED_EVENT);
8569
- scope.dispatcher.dispatchEvent(scope.CAMERA_EVENTS.CHANGE_EVENT);
8570
- if (state == STATE.NONE) {
8571
- scope.dispatcher.dispatchEvent(scope.this.CAMERA_EVENTS.PAN_END_EVENT);
8572
- }
8573
- };
8574
- /**
8575
- * Sets the rotation to a specific orientation, in radians. Mostly useful to orient the map a certain way for a physical directory.
8576
- *
8577
- * @method setRotation
8578
- * @param radians {Number} Absolute rotation to set the camera to, in radians. 0 in the starting point.
8579
- */
8580
- this.setRotation = function (radians) {
8581
- if (isNaN(radians)) {
8582
- return;
8583
- }
8584
- if (state == STATE.NONE) {
8585
- scope.dispatcher.dispatchEvent(scope.CAMERA_EVENTS.ROTATE_START_EVENT);
8586
- }
8587
- const newRotation = radians % TWO_PI;
8588
- if (newRotation !== scope.orbit.rotation.z) {
8589
- scope.orbit.rotation.z = newRotation;
8590
- scope.dispatcher.dispatchEvent(scope.UPDATE_EVENTS.ROTATION_UPDATED_EVENT);
8591
- }
8592
- scope.dispatcher.dispatchEvent(scope.CAMERA_EVENTS.CHANGE_EVENT);
8593
- if (state == STATE.NONE) {
8594
- scope.dispatcher.dispatchEvent(scope.CAMERA_EVENTS.ROTATE_END_EVENT);
8595
- }
8596
- };
8597
- /**
8598
- * Rotates the camera a set number of radians relative to the current rotation. Useful for an idle rotation animation.
8599
- *
8600
- * @method rotate
8601
- * @param radians {Number} Number of radians to rotate the camera.
8602
- */
8603
- this.rotate = function (radians) {
8604
- if (isNaN(radians)) {
8605
- return;
8606
- }
8607
- if (state == STATE.NONE) {
8608
- scope.dispatcher.dispatchEvent(scope.CAMERA_EVENTS.ROTATE_START_EVENT);
8609
- }
8610
- const newRotation = (scope.orbit.rotation.z + radians) % TWO_PI;
8611
- if (newRotation !== scope.orbit.rotation.z) {
8612
- scope.orbit.rotation.z = newRotation;
8613
- scope.dispatcher.dispatchEvent(scope.UPDATE_EVENTS.ROTATION_UPDATED_EVENT);
8614
- }
8615
- scope.dispatcher.dispatchEvent(scope.CAMERA_EVENTS.CHANGE_EVENT);
8616
- if (state == STATE.NONE) {
8617
- scope.dispatcher.dispatchEvent(scope.CAMERA_EVENTS.ROTATE_END_EVENT);
8618
- }
8619
- };
8620
- /**
8621
- * Sets the tilt to a specific level, in radians. 0 is top down. Bounded by minTilt and maxTilt.
8622
- *
8623
- * @method setTilt
8624
- * @param radians {Number} Tilt to set the camera to, in radians.
8625
- */
8626
- this.setTilt = function (radians) {
8627
- if (isNaN(radians)) {
8628
- return;
8629
- }
8630
- if (state == STATE.NONE) {
8631
- scope.dispatcher.dispatchEvent(scope.CAMERA_EVENTS.ROTATE_START_EVENT);
8632
- }
8633
- const newTilt = stayInsideBounds ? Math.max(Math.min(radians, scope.maxTilt), scope.minTilt) : radians;
8634
- if (newTilt !== scope.elevation.rotation.x) {
8635
- scope.elevation.rotation.x = newTilt;
8636
- scope.dispatcher.dispatchEvent(scope.UPDATE_EVENTS.TILT_UPDATED_EVENT);
8637
- }
8638
- scope.dispatcher.dispatchEvent(scope.CAMERA_EVENTS.CHANGE_EVENT);
8639
- if (state == STATE.NONE) {
8640
- scope.dispatcher.dispatchEvent(scope.CAMERA_EVENTS.ROTATE_END_EVENT);
8641
- }
8642
- };
8643
- /**
8644
- * Tilts the camera up or down by some number of radians. Bounded by minTilt and maxTilt.
8645
- *
8646
- * @method tilt
8647
- * @param radians {Number} Number of radians to increase or decrease the current tilt by.
8648
- */
8649
- this.tilt = function (radians) {
8650
- if (isNaN(radians)) {
8651
- return;
8652
- }
8653
- if (state == STATE.NONE) {
8654
- scope.dispatcher.dispatchEvent(scope.CAMERA_EVENTS.ROTATE_START_EVENT);
8655
- }
8656
- const newTilt = stayInsideBounds
8657
- ? Math.max(Math.min(radians + scope.getTilt(), scope.maxTilt), scope.minTilt)
8658
- : radians + scope.getTilt();
8659
- if (newTilt !== scope.elevation.rotation.x) {
8660
- scope.elevation.rotation.x = newTilt;
8661
- scope.dispatcher.dispatchEvent(scope.UPDATE_EVENTS.TILT_UPDATED_EVENT);
8662
- }
8663
- scope.dispatcher.dispatchEvent(scope.CAMERA_EVENTS.CHANGE_EVENT);
8664
- if (state == STATE.NONE) {
8665
- scope.dispatcher.dispatchEvent(scope.CAMERA_EVENTS.ROTATE_END_EVENT);
8666
- }
8667
- };
8668
- /**
8669
- * Sets the camera to be a certain distance from the anchor point, along it's tilt and rotation.
8670
- * Keeps it inside minZoom and maxZoom.
8671
- *
8672
- * @method setZoom
8673
- * @param zoom {Number} The distance to set the camera to.
8674
- */
8675
- this.setZoom = function (zoom) {
8676
- if (isNaN(zoom)) {
8677
- return;
8678
- }
8679
- if (state == STATE.NONE) {
8680
- scope.dispatcher.dispatchEvent(scope.CAMERA_EVENTS.ZOOM_START_EVENT);
8681
- }
8682
- let z = stayInsideBounds ? Math.min(Math.max(zoom, scope.getZoomScaledMin()), scope.getZoomScaledMax()) : zoom;
8683
- if (z !== scope.camera.position.z) {
8684
- scope.camera.position.z = z;
8685
- scope.dispatcher.dispatchEvent(scope.UPDATE_EVENTS.ZOOM_UPDATED_EVENT);
8686
- }
8687
- scope.camera.near = z / CAMERA_ZRANGE;
8688
- scope.camera.far = z * CAMERA_ZRANGE;
8689
- scope.camera.updateProjectionMatrix();
8690
- scope.dispatcher.dispatchEvent(scope.CAMERA_EVENTS.CHANGE_EVENT);
8691
- if (state == STATE.NONE) {
8692
- scope.dispatcher.dispatchEvent(scope.CAMERA_EVENTS.ZOOM_END_EVENT);
8693
- }
8694
- };
8695
- /**
8696
- * Moves the camera towards or away from the camera by a set amount. Positive will zoom in (bringing the distance closer to 0).
8697
- *
8698
- * @method zoom
8699
- * @param zoom {Number} The distance to increase or decrease the zoom.
8700
- */
8701
- this.zoom = function (zoomDelta) {
8702
- if (isNaN(zoomDelta)) {
8703
- return;
8704
- }
8705
- if (state == STATE.NONE) {
8706
- scope.dispatcher.dispatchEvent(scope.CAMERA_EVENTS.ZOOM_START_EVENT);
8707
- }
8708
- let newZoom = scope.getZoom() - zoomDelta;
8709
- scope.camera.position.z = stayInsideBounds
8710
- ? Math.min(Math.max(newZoom, scope.minZoom * scope.zoomFactor), scope.maxZoom * scope.zoomFactor)
8711
- : newZoom;
8712
- scope.dispatcher.dispatchEvent(scope.UPDATE_EVENTS.ZOOM_UPDATED_EVENT);
8713
- scope.dispatcher.dispatchEvent(scope.CAMERA_EVENTS.CHANGE_EVENT);
8714
- if (state == STATE.NONE) {
8715
- scope.dispatcher.dispatchEvent(scope.CAMERA_EVENTS.ZOOM_END_EVENT);
8716
- }
8717
- };
8718
- /**
8719
- * Zooms the camera in on the center of the current view.
8720
- *
8721
- * @method zoomIn
8722
- * @param duration
8723
- * @param curve
8724
- * @param callback
8725
- */
8726
- this.zoomIn = function (duration, curve, callback) {
8727
- let zoom = this.getZoom();
8728
- this.animateCamera({
8729
- zoom: zoom / 1.5,
8730
- }, duration, curve, callback);
8731
- };
8732
- /**
8733
- * Zooms the camera out from the center of the current view.
8734
- *
8735
- * @method zoomOut
8736
- * @param duration
8737
- * @param curve
8738
- * @param callback
8739
- */
8740
- this.zoomOut = function (duration, curve, callback) {
8741
- let zoom = this.getZoom();
8742
- this.animateCamera({
8743
- zoom: zoom * 1.5,
8744
- }, duration, curve, callback);
8745
- };
8746
- /**
8747
- * Expands the zoom limits to allow zooming to the specified distance.
8748
- *
8749
- * @method expandZoomLimits
8750
- * @param zoom {Number} The distance to allow zooming to.
8751
- * @param buffer {Number} The factor by which the user should be able to zoom beyond the specified distance.
8752
- */
8753
- this.expandZoomLimits = function (zoom, buffer) {
8754
- if (!buffer)
8755
- buffer = 1.2;
8756
- if (zoom * buffer > scope.maxZoom) {
8757
- scope.maxZoom = Math.min(zoom * buffer, scope.maxExpandedZoom);
8758
- }
8759
- if (zoom / buffer < scope.minZoom) {
8760
- scope.minZoom = Math.max(zoom / buffer, scope.minExpandedZoom);
8761
- }
8762
- };
8763
- /**
8764
- * Restricts the zoom limits, but will not restrict beyond the current zoom level.
8765
- *
8766
- * @method restrictZoomLimits
8767
- * @param zoom {Number} The number to limit zooming to.
8768
- * @param buffer {Number} The factor by which the user should be able to zoom beyond the specified distance.
8769
- */
8770
- this.restrictZoomLimits = function (zoom, buffer) {
8771
- if (!buffer)
8772
- buffer = 1.2;
8773
- if (zoom * buffer > scope.initialMaxZoom && zoom * buffer < scope.maxZoom) {
8774
- scope.maxZoom = Math.max(scope.getZoom(), zoom * buffer);
8775
- }
8776
- if (zoom / buffer < scope.initialMinZoom && zoom / buffer > scope.minZoom) {
8777
- scope.minZoom = Math.min(scope.getZoom(), zoom / buffer);
8778
- }
8779
- };
8780
- /**
8781
- * Returns the current camera position.
8782
- *
8783
- * @method getPosition
8784
- * @return {Object} An {x, y} object of the current camera postion.
8785
- */
8786
- this.getPosition = function () {
8787
- return {
8788
- x: scope.orbit.position.x,
8789
- y: scope.orbit.position.y,
8790
- z: scope.orbit.position.z,
8791
- };
8792
- };
8793
- this.getPedestal = function () {
8794
- return scope.orbit.position.z;
8795
- };
8796
- function setPedestal(z) {
8797
- if (typeof z === 'number' && !isNaN(z)) {
8798
- scope.orbit.position.z = z;
8799
- core.tryRendering();
8800
- }
8801
- }
8802
- Object.defineProperty(this, 'setPedestal', {
8803
- enumerable: false,
8804
- value: setPedestal,
8805
- });
8806
- /**
8807
- * The amount the camera is shifted up/down
8808
- * (corresponds to scroll amount in multifloor mode)
8809
- * 100% == camera is as far up as it will go
8810
- * 0% == camera is as far down as it will go
8811
- */
8812
- Object.defineProperty(this, 'scrollPercent', {
8813
- get() {
8814
- const min = this.minPedestal;
8815
- const max = this.maxPedestal;
8816
- let pedestal = this.getPedestal();
8817
- pedestal = Math.min(pedestal, max);
8818
- pedestal = Math.max(pedestal, min);
8819
- const offset = pedestal - min;
8820
- const portion = offset / (max - min);
8821
- return portion * 100;
8822
- },
8823
- });
8824
- /**
8825
- * Returns whether the maps are scrolled to bottom
8826
- */
8827
- Object.defineProperty(this, 'scrolledToTop', {
8828
- get() {
8829
- return this.maxPedestal === 0 || Math.ceil(this.getPedestal()) >= this.maxPedestal;
8830
- },
8831
- });
8832
- /**
8833
- * Returns whether the maps are scrolled to top
8834
- */
8835
- Object.defineProperty(this, 'scrolledToBottom', {
8836
- get() {
8837
- return this.minPedestal === 0 || Math.floor(this.getPedestal()) <= this.minPedestal;
8838
- },
8839
- });
8840
- /**
8841
- * Returns the current camera rotation.
8842
- *
8843
- * @method getRotation
8844
- * @return {Number} The current rotation of the camera, in radians.
8845
- */
8846
- this.getRotation = function () {
8847
- return scope.orbit.rotation.z;
8848
- };
8849
- /**
8850
- * Returns the current camera tilt
8851
- *
8852
- * @method getTilt
8853
- * @return {Number} The current tilt of the camera, in radians.
8854
- */
8855
- this.getTilt = function () {
8856
- return scope.elevation.rotation.x;
8857
- };
8858
- /**
8859
- * Returns the current camera zoom
8860
- *
8861
- * @method getZoom
8862
- * @return {Number} The distance of the camera from the anchor.
8863
- */
8864
- this.getZoom = function () {
8865
- return scope.camera.position.z;
8866
- };
8867
- /**
8868
- * Returns true if the camera is currently moving (it's animating, the user is manipulating it).
8869
- *
8870
- * @method isCameraMoving
8871
- * @return {Boolean} True if the camera is moving, false otherwise.
8872
- */
8873
- this.isCameraMoving = function () {
8874
- return state != STATE.NONE;
8875
- };
8876
- /**
8877
- * Returns true if the the user is currently manipulating the camera.
8878
- *
8879
- * @method isUserInteracting
8880
- * @return {Boolean} True if the user is currently manipulating the camera, false otherwise.
8881
- */
8882
- this.isUserInteracting = function () {
8883
- return userInteracting;
8884
- };
8885
- /**
8886
- * Returns the actual zoom minimum based on real-world distance.
8887
- *
8888
- * @method getZoomScaledMin
8889
- * @return {Number} The actual zoom minimum in map units.
8890
- */
8891
- this.getZoomScaledMin = function () {
8892
- return scope.minZoom * scope.zoomFactor;
8893
- };
8894
- /**
8895
- * Returns the actual zoom maximum based on visible map size.
8896
- *
8897
- * @method getZoomScaledMax
8898
- * @return {Number} The actual zoom maximum in map units.
8899
- */
8900
- this.getZoomScaledMax = function () {
8901
- let min = scope.minZoom * scope.zoomFactor;
8902
- return Math.max(scope.maxZoom, 1.5 * min);
8903
- };
8904
- let isAnimating = function () {
8905
- return TWEEN.getAll().some(function (value) {
8906
- return value._mMapAnimation;
8907
- });
8908
- };
8909
- const tweens = new Set();
8910
- function completeTween(tween) {
8911
- if (tween.isPlaying) {
8912
- tween.stop();
8913
- }
8914
- else {
8915
- tween.end();
8916
- }
8917
- tweens.delete(tween);
8918
- }
8919
- /**
8920
- * Cancel animation- for the new API
8921
- */
8922
- this.cancelAnimation = function () {
8923
- const tweensArray = Array.from(tweens);
8924
- let lastTween = tweensArray[tweensArray.length - 1];
8925
- // Make sure to emit an event if we are cancelling a tween
8926
- if (lastTween.state === STATE.MULTI) {
8927
- scope.dispatcher.dispatchEvent({
8928
- type: scope.CAMERA_EVENTS.MULTI_CANCEL_EVENT.type,
8929
- message: {
8930
- zooming: true,
8931
- rotating: true,
8932
- tilting: true,
8933
- },
8934
- });
8935
- }
8936
- completeTween(lastTween);
8937
- };
8938
- /**
8939
- * Animates the camera from it's current position to the state specified in target. You only
8940
- * need to specify the properties you actually want to change.
8941
- *
8942
- * You can also specify a duration, animation curve, and a callback for when it's done.
8943
- *
8944
- * @method animateCamera
8945
- * @param target {Object} A list of optional parameters you can set that represent the camera state.
8946
- @param [target.position] {Object} An {x, y, z} object representing the position to move to.
8947
- @param [target.zoom] {Number} The zoom level to end at.
8948
- @param [target.tilt] {Number} The tilt to end at, in radians.
8949
- @param [target.rotation] {Number} The rotation to end at, in radians.
8950
- @param [target.doNotAutoStart=false] {Boolean} Set this to true if you want to start the tween yourself.
8951
- * @param [duration] {Number} The duration to animate the camera for, in ms.
8952
- * @param [curve] {Mappedin.Easing} The animation curve to use for the animation.
8953
- * @param [callback] {Function} A callback that will be executed when the animation is done.
8954
- * @param [options] {Object} An Options object
8955
- * @param [options.mode=cancel] {Object} Mode determines what happens when multiple animateCamera calls happen at once. By Default,
8956
- it will cancel and omit any previous animations. Set to "chain" to chain instead
8957
- *
8958
- * @return {Mappedin.Tween} The tween being used, if you want to do anything to control it manually. Do not overide it's events.
8959
- */
8960
- this.animateCamera = function (target, duration, curve, callback, options) {
8961
- const _options = { mode: CAMERA_CONTROL_OPTIONS.cancel, ...options };
8962
- let tweenStart = {};
8963
- let tweenEnd = {};
8964
- let position = scope.getPosition();
8965
- if (target.position != null) {
8966
- if (target.position.x !== undefined) {
8967
- tweenStart.x = position.x;
8968
- tweenEnd.x = target.position.x;
8969
- }
8970
- if (target.position.y !== undefined) {
8971
- tweenStart.y = position.y;
8972
- tweenEnd.y = target.position.y;
8973
- }
8974
- if (target.position.z !== undefined) {
8975
- tweenStart.z = position.z;
8976
- tweenEnd.z = target.position.z;
8977
- }
8978
- }
8979
- if (target.zoom !== undefined) {
8980
- tweenStart.zoom = stayInsideBounds
8981
- ? Math.min(Math.max(scope.getZoom(), scope.getZoomScaledMin()), scope.getZoomScaledMax())
8982
- : scope.getZoom();
8983
- if (scope.viewState === VIEW_STATE.MULTI_FLOOR || !stayInsideBounds) {
8984
- tweenEnd.zoom = target.zoom;
8985
- }
8986
- else {
8987
- tweenEnd.zoom = Math.min(Math.max(target.zoom, scope.getZoomScaledMin()), scope.getZoomScaledMax());
8988
- }
8989
- }
8990
- if (target.rotation !== undefined) {
8991
- tweenStart.rotation = scope.getRotation();
8992
- const targetRotationMod2Pi = target.rotation % (2 * Math.PI);
8993
- if (Math.abs(targetRotationMod2Pi - tweenStart.rotation) > Math.PI) {
8994
- tweenEnd.rotation =
8995
- targetRotationMod2Pi < 0 ? 2 * Math.PI + targetRotationMod2Pi : -(2 * Math.PI - targetRotationMod2Pi);
8996
- }
8997
- else {
8998
- tweenEnd.rotation = targetRotationMod2Pi;
8999
- }
9000
- }
9001
- if (target.tilt !== undefined) {
9002
- tweenStart.tilt = scope.getTilt();
9003
- if (scope.viewState === VIEW_STATE.MULTI_FLOOR || !stayInsideBounds) {
9004
- tweenEnd.tilt = target.tilt;
9005
- }
9006
- else {
9007
- tweenEnd.tilt = Math.max(Math.min(target.tilt, scope.maxTilt), scope.minTilt);
9008
- }
9009
- }
9010
- const zooming = tweenStart.zoom !== tweenEnd.zoom;
9011
- const rotating = tweenStart.rotation !== tweenEnd.rotation;
9012
- const tilting = tweenStart.tilting !== tweenEnd.tilting;
9013
- var tween = new TWEEN.Tween(tweenStart)
9014
- .to(tweenEnd, process.env.TESTING ? 0 : duration)
9015
- .onUpdate(function (between) {
9016
- scope.setMulti({
9017
- x: between.x,
9018
- y: between.y,
9019
- z: between.z,
9020
- }, between.zoom, between.rotation, between.tilt);
9021
- })
9022
- .onComplete(function () {
9023
- // Why doesn't this dispatch scope.CAMERA_EVENTS.MULTI_END_EVENT..?
9024
- if (callback) {
9025
- callback();
9026
- }
9027
- completeTween(tween);
9028
- scope.dispatcher.dispatchEvent({
9029
- type: scope.CAMERA_EVENTS.MULTI_END_EVENT.type,
9030
- message: {
9031
- zooming,
9032
- rotating,
9033
- tilting,
9034
- },
9035
- });
9036
- })
9037
- .onStart(function () {
9038
- if (state != STATE.MULTI) {
9039
- state = STATE.MULTI;
9040
- tween.state = STATE.MULTI;
9041
- scope.dispatcher.dispatchEvent({
9042
- type: scope.CAMERA_EVENTS.MULTI_START_EVENT.type,
9043
- message: {
9044
- zooming,
9045
- rotating,
9046
- tilting,
9047
- },
9048
- });
9049
- }
9050
- scope.dispatcher.dispatchEvent(scope.CAMERA_EVENTS.CHANGE_EVENT);
9051
- });
9052
- // check for ongoing tweens
9053
- const tweensArray = Array.from(tweens);
9054
- let lastTween = tweensArray[tweensArray.length - 1];
9055
- let wait = false;
9056
- // if an ongoing tween exists, chain it to this new one, so it starts when that one ends
9057
- if (lastTween != null) {
9058
- if (_options.mode === CAMERA_CONTROL_OPTIONS.chain) {
9059
- wait = true;
9060
- lastTween.chain(tween);
9061
- }
9062
- else {
9063
- completeTween(lastTween);
9064
- lastTween = null;
9065
- }
9066
- }
9067
- tweens.add(tween);
9068
- if (curve) {
9069
- tween.easing(curve);
9070
- }
9071
- else {
9072
- tween.easing(TWEEN.Easing.Quadratic.In);
9073
- }
9074
- if (!target.doNotAutoStart && !wait) {
9075
- tween.start();
9076
- core.tryRendering();
9077
- }
9078
- tween._mMapAnimation = true;
9079
- return tween;
9080
- };
9081
- /**
9082
- * Allows you to set any of the Camera's position, zoom, rotation and tilt at once, with one function.
9083
- *
9084
- * @method setMulti
9085
- * @param [position] {Object} an {x, y, z} object representing the new position.
9086
- * @param [zoom] {Number} The new zoom distance.
9087
- * @param [rotation] {Number} The new rotation, in radians.
9088
- * @param [tilt] {Number} The new tilt, in radians.
9089
- */
9090
- this.setMulti = function (position, zoom, rotation, tilt) {
9091
- if (state == STATE.NONE) {
9092
- scope.dispatcher.dispatchEvent(scope.CAMERA_EVENTS.MULTI_START_EVENT);
9093
- }
9094
- let positionUpdated = false;
9095
- if (position != null) {
9096
- if (position.x !== undefined && !isNaN(position.x)) {
9097
- scope.orbit.position.x = position.x;
9098
- positionUpdated = true;
9099
- }
9100
- if (position.y !== undefined && !isNaN(position.y)) {
9101
- scope.orbit.position.y = position.y;
9102
- positionUpdated = true;
9103
- }
9104
- if (position.z !== undefined && !isNaN(position.z)) {
9105
- this.setPedestal(position.z);
9106
- }
9107
- }
9108
- if (positionUpdated) {
9109
- scope.dispatcher.dispatchEvent(scope.UPDATE_EVENTS.POSITION_UPDATED_EVENT);
9110
- }
9111
- const payload = { zooming: false, tilting: zoom, rotating: false };
9112
- if (zoom !== undefined && !isNaN(zoom)) {
9113
- const targetZoom = stayInsideBounds
9114
- ? Math.min(Math.max(zoom, scope.getZoomScaledMin()), scope.getZoomScaledMax())
9115
- : zoom;
9116
- if (targetZoom !== scope.camera.position.z) {
9117
- payload.zooming = true;
9118
- scope.camera.position.z = targetZoom;
9119
- scope.dispatcher.dispatchEvent(scope.UPDATE_EVENTS.ZOOM_UPDATED_EVENT);
9120
- // adjust camera near / far to prevent Z-fighting when extremely zoomed out
9121
- // this is only likely to happen in multifloor views, when FOV is reduced
9122
- if (scope.viewState === VIEW_STATE.MULTI_FLOOR) {
9123
- if (zoom > CAMERA_CLIPPING_RADIUS) {
9124
- scope.camera.near = zoom - CAMERA_CLIPPING_RADIUS;
9125
- scope.camera.far = zoom + CAMERA_CLIPPING_RADIUS;
9126
- }
9127
- }
9128
- else {
9129
- scope.camera.near = zoom / CAMERA_ZRANGE;
9130
- scope.camera.far = zoom * CAMERA_ZRANGE;
9131
- }
9132
- scope.camera.updateProjectionMatrix();
9133
- }
9134
- }
9135
- if (rotation !== undefined && !isNaN(rotation)) {
9136
- if (rotation !== scope.orbit.rotation.z) {
9137
- payload.rotating = true;
9138
- scope.orbit.rotation.z = rotation;
9139
- scope.dispatcher.dispatchEvent(scope.UPDATE_EVENTS.ROTATION_UPDATED_EVENT);
9140
- }
9141
- }
9142
- if (tilt !== undefined && !isNaN(tilt)) {
9143
- const newTilt = stayInsideBounds ? Math.max(Math.min(tilt, scope.maxTilt), scope.minTilt) : tilt;
9144
- if (newTilt !== scope.elevation.rotation.x) {
9145
- payload.tilting = true;
9146
- scope.elevation.rotation.x = newTilt;
9147
- scope.dispatcher.dispatchEvent(scope.UPDATE_EVENTS.TILT_UPDATED_EVENT);
9148
- }
9149
- }
9150
- scope.dispatcher.dispatchEvent(scope.CAMERA_EVENTS.CHANGE_EVENT);
9151
- if (state == STATE.NONE) {
9152
- scope.dispatcher.dispatchEvent({
9153
- type: scope.CAMERA_EVENTS.MULTI_END_EVENT.type,
9154
- message: payload,
9155
- });
9156
- }
9157
- };
9158
- function handleMouseDownRotate(event) {
9159
- rotateStart.set(event.clientX, event.clientY);
9160
- scope.dispatcher.dispatchEvent(scope.CAMERA_EVENTS.ROTATE_START_EVENT);
9161
- scope.dispatcher.dispatchEvent(scope.INTERACTION_EVENTS.USER_ROTATE_START_EVENT);
9162
- }
9163
- function handleMouseDownDolly(event) {
9164
- dollyStart.set(event.clientX, event.clientY);
9165
- scope.dispatcher.dispatchEvent(scope.CAMERA_EVENTS.ZOOM_START_EVENT);
9166
- scope.dispatcher.dispatchEvent(scope.INTERACTION_EVENTS.USER_DOLLY_START_EVENT);
9167
- }
9168
- function handleMouseDownPan() {
9169
- let pos = raycastToFloor(mouse);
9170
- panStart.set(pos.x, pos.y);
9171
- panCameraStart.set(scope.orbit.position.x, scope.orbit.position.y);
9172
- scope.dispatcher.dispatchEvent(scope.CAMERA_EVENTS.PAN_START_EVENT);
9173
- scope.dispatcher.dispatchEvent(scope.INTERACTION_EVENTS.USER_PAN_START_EVENT);
9174
- }
9175
- function handleMouseDownPedestal(event) {
9176
- const pos = { x: event.clientX, y: event.clientY };
9177
- scope.initialShiftPos = pos;
9178
- scope.initialPedestal = scope.getPedestal();
9179
- scope.pedestalScaleFactor = getProjectionScaleFactor(scope.camera.fov, scope.canvas.clientHeight, scope.getZoom());
9180
- scope.dispatcher.dispatchEvent(scope.CAMERA_EVENTS.PEDESTAL_START_EVENT);
9181
- scope.dispatcher.dispatchEvent(scope.INTERACTION_EVENTS.USER_PEDESTAL_START_EVENT);
9182
- }
9183
- function handleMouseMovePedestal(event) {
9184
- const pos = { x: event.clientX, y: event.clientY };
9185
- let newZ = scope.initialPedestal + scope.pedestalScaleFactor * (pos.y - scope.initialShiftPos.y);
9186
- // clamp to min/max
9187
- if (stayInsideBounds) {
9188
- if (newZ < scope.minPedestal) {
9189
- newZ = Math.max(newZ, scope.minPedestal);
9190
- }
9191
- else if (newZ > scope.maxPedestal) {
9192
- newZ = Math.min(newZ, scope.maxPedestal);
9193
- }
9194
- }
9195
- if (newZ === scope.getPedestal()) {
9196
- return;
9197
- }
9198
- scope.setPedestal(newZ);
9199
- dispatchPedestalChangeEvent(newZ);
9200
- }
9201
- function handleMouseMoveRotate(event) {
9202
- rotateEnd.set(event.clientX, event.clientY);
9203
- rotateDelta.subVectors(rotateEnd, rotateStart);
9204
- scope.rotate(-rotateDelta.x / scope.rotateSpeed);
9205
- scope.setTilt(scope.getTilt() - rotateDelta.y / scope.rotateSpeed);
9206
- rotateStart.copy(rotateEnd);
9207
- scope.dispatcher.dispatchEvent(scope.CAMERA_EVENTS.CHANGE_EVENT);
9208
- }
9209
- function handleMouseMoveDolly(event) {
9210
- dollyEnd.set(event.clientX, event.clientY);
9211
- dollyDelta.subVectors(dollyEnd, dollyStart);
9212
- scope.setZoom(dollyDelta.y * scope.zoomSpeed);
9213
- dollyStart.copy(dollyEnd);
9214
- scope.dispatcher.dispatchEvent(scope.CAMERA_EVENTS.CHANGE_EVENT);
9215
- }
9216
- function handleMouseMovePan() {
9217
- let pos = raycastToFloor(mouse);
9218
- panEnd.set(pos.x, pos.y);
9219
- panDelta.subVectors(panEnd, panStart);
9220
- scope.setPosition(panCameraStart.x - panDelta.x, panCameraStart.y - panDelta.y);
9221
- scope.dispatcher.dispatchEvent(scope.CAMERA_EVENTS.CHANGE_EVENT);
9222
- }
9223
- this.getProjectionScaleFactor = getProjectionScaleFactor;
9224
- /**
9225
- * Anything we need to do after we render the camera, like update anchors for the mouse/touch controls.
9226
- *
9227
- * @method postRender
9228
- * @private
9229
- */
9230
- this.postRender = function () {
9231
- if (state == STATE.PAN || state == STATE.TOUCH_PAN) {
9232
- raycaster.setFromCamera(mouse, camera);
9233
- intersection = raycaster.intersectObject(cameraPlane, false)[0];
9234
- if (intersection) {
9235
- panStart.set(intersection.point.x, intersection.point.y);
9236
- panCameraStart.set(scope.orbit.position.x, scope.orbit.position.y);
9237
- }
9238
- }
9239
- if (state == STATE.WHEEL_ZOOM && resetZoom) {
9240
- resetZoom = false;
9241
- zoomStart = scope.getZoom();
9242
- floorAnchor = raycastToFloor(mouse);
9243
- panCameraStart.set(scope.orbit.position.x, scope.orbit.position.y);
9244
- }
9245
- if (state == STATE.MULTI && isAnimating() == false) {
9246
- state = STATE.NONE;
9247
- scope.dispatcher.dispatchEvent(scope.CAMERA_EVENTS.MULTI_END_EVENT);
9248
- }
9249
- };
9250
- function scrollDelta(event) {
9251
- let delta = 0;
9252
- if (event.wheelDelta !== undefined) {
9253
- // WebKit / Opera / Explorer 9
9254
- delta = event.wheelDelta;
9255
- }
9256
- else if (event.detail !== undefined) {
9257
- // Firefox
9258
- delta = -event.detail;
9259
- }
9260
- return delta;
9261
- }
9262
- function canScrollZoom(event) {
9263
- let delta = scrollDelta(event);
9264
- let currentZoom = scope.getZoom();
9265
- return !((delta < 0 && currentZoom == scope.getZoomScaledMax()) ||
9266
- (delta > 0 && currentZoom == scope.getZoomScaledMin()));
9267
- }
9268
- function handleMouseWheel(event) {
9269
- scope.dispatcher.dispatchEvent(scope.CAMERA_EVENTS.ZOOM_START_EVENT);
9270
- if (!canScrollZoom(event)) {
9271
- if (lastWheelTime > 0) {
9272
- scope.dispatcher.dispatchEvent(scope.CAMERA_EVENTS.ZOOM_END_EVENT);
9273
- }
9274
- lastWheelTime = 0;
9275
- state = STATE.NONE;
9276
- return;
9277
- }
9278
- let currentZoom = scope.getZoom();
9279
- let zoom = currentZoom - ((scrollDelta(event) * currentZoom) / WHEEL_ZOOM_MULTIPLIER) * scope.zoomSpeed;
9280
- scope.setZoom(zoom);
9281
- let zoomPercent = zoom / zoomStart;
9282
- panDelta.subVectors(floorAnchor, panCameraStart).multiplyScalar(1 - zoomPercent);
9283
- scope.setPosition(panCameraStart.x + panDelta.x, panCameraStart.y + panDelta.y);
9284
- scope.dispatcher.dispatchEvent(scope.CAMERA_EVENTS.CHANGE_EVENT);
9285
- }
9286
- function onMouseDown(event) {
9287
- if (scope.enabled === false)
9288
- return;
9289
- mouseToScene(event);
9290
- event.preventDefault();
9291
- if (state == STATE.WHEEL_ZOOM) {
9292
- scope.dispatcher.dispatchEvent(scope.CAMERA_EVENTS.ZOOM_END_EVENT);
9293
- }
9294
- if (event.button === scope.mouseButtons.ORBIT) {
9295
- if (scope.enableRotate === false)
9296
- return;
9297
- handleMouseDownRotate(event);
9298
- state = STATE.ROTATE;
9299
- }
9300
- else if (event.button === scope.mouseButtons.ZOOM) {
9301
- if (scope.enableZoom === false)
9302
- return;
9303
- handleMouseDownDolly(event);
9304
- state = STATE.DOLLY;
9305
- }
9306
- else if (event.button === scope.mouseButtons.PAN) {
9307
- if (scope.enablePan) {
9308
- handleMouseDownPan(event);
9309
- state = STATE.PAN;
9310
- }
9311
- else if (scope.enablePedestal) {
9312
- handleMouseDownPedestal(event);
9313
- state = STATE.PEDESTAL;
9314
- }
9315
- }
9316
- if (state !== STATE.NONE) {
9317
- scope.canvas.addEventListener('mousemove', onMouseMove, false);
9318
- window.addEventListener('mouseup', onMouseUp, false);
9319
- window.addEventListener('mouseout', onMouseUp, false);
9320
- }
9321
- }
9322
- function onMouseMove(event) {
9323
- if (scope.enabled === false)
9324
- return;
9325
- event.preventDefault();
9326
- mouseToScene(event);
9327
- if (state === STATE.ROTATE) {
9328
- if (scope.enableRotate === false)
9329
- return;
9330
- handleMouseMoveRotate(event);
9331
- }
9332
- else if (state === STATE.DOLLY) {
9333
- if (scope.enableZoom === false)
9334
- return;
9335
- handleMouseMoveDolly(event);
9336
- }
9337
- else if (state === STATE.PAN) {
9338
- if (scope.enablePan === false)
9339
- return;
9340
- handleMouseMovePan(event);
9341
- }
9342
- else if (state === STATE.WHEEL_ZOOM) {
9343
- resetZoom = true;
9344
- state = STATE.NONE;
9345
- }
9346
- else if (state === STATE.PEDESTAL) {
9347
- handleMouseMovePedestal(event);
9348
- }
9349
- }
9350
- function onMouseUp() {
9351
- if (scope.enabled === false)
9352
- return;
9353
- document.removeEventListener('mousemove', onMouseMove, false);
9354
- window.removeEventListener('mouseup', onMouseUp, false);
9355
- window.removeEventListener('mouseout', onMouseUp, false);
9356
- switch (state) {
9357
- case STATE.PAN:
9358
- scope.dispatcher.dispatchEvent(scope.CAMERA_EVENTS.PAN_END_EVENT);
9359
- scope.dispatcher.dispatchEvent(scope.INTERACTION_EVENTS.USER_PAN_END_EVENT);
9360
- break;
9361
- case STATE.DOLLY:
9362
- scope.dispatcher.dispatchEvent(scope.CAMERA_EVENTS.ZOOM_END_EVENT);
9363
- scope.dispatcher.dispatchEvent(scope.INTERACTION_EVENTS.USER_DOLLY_END_EVENT);
9364
- break;
9365
- case STATE.ROTATE:
9366
- scope.dispatcher.dispatchEvent(scope.CAMERA_EVENTS.ROTATE_END_EVENT);
9367
- scope.dispatcher.dispatchEvent(scope.INTERACTION_EVENTS.USER_ROTATE_END_EVENT);
9368
- break;
9369
- case STATE.PEDESTAL:
9370
- scope.dispatcher.dispatchEvent(scope.CAMERA_EVENTS.PEDESTAL_END_EVENT);
9371
- scope.dispatcher.dispatchEvent(scope.INTERACTION_EVENTS.USER_PEDESTAL_END_EVENT);
9372
- break;
9373
- }
9374
- state = STATE.NONE;
9375
- }
9376
- let scrollTimer = null;
9377
- function onMouseWheel(event) {
9378
- if (scope.enabled === false || scope.enableZoom === false || (state !== STATE.NONE && state !== STATE.WHEEL_ZOOM))
9379
- return;
9380
- event.preventDefault();
9381
- event.stopPropagation();
9382
- mouseToScene(event);
9383
- if (canScrollZoom(event)) {
9384
- lastWheelTime = clock.getElapsedTime();
9385
- scope.dispatchUserZoomEvent();
9386
- }
9387
- if (state != STATE.WHEEL_ZOOM && canScrollZoom(event)) {
9388
- scope.canvas.addEventListener('mousemove', onMouseMove, false);
9389
- state = STATE.WHEEL_ZOOM;
9390
- zoomStart = scope.getZoom();
9391
- floorAnchor = raycastToFloor(mouse);
9392
- panCameraStart.set(scope.orbit.position.x, scope.orbit.position.y);
9393
- scope.dispatcher.dispatchEvent(scope.CAMERA_EVENTS.ZOOM_START_EVENT);
9394
- }
9395
- if (scrollTimer != null) {
9396
- clearTimeout(scrollTimer);
9397
- }
9398
- scrollTimer = setTimeout(() => {
9399
- scope.dispatcher.dispatchEvent(scope.CAMERA_EVENTS.ZOOM_END_EVENT);
9400
- }, 50);
9401
- handleMouseWheel(event);
9402
- }
9403
- function handleTouchStartPan(event) {
9404
- let pos = raycastToFloor(event.x ? event : touches[0]);
9405
- panStart.set(pos.x, pos.y);
9406
- panCameraStart.set(scope.orbit.position.x, scope.orbit.position.y);
9407
- scope.dispatcher.dispatchEvent(scope.INTERACTION_EVENTS.USER_PAN_START_EVENT);
9408
- scope.dispatcher.dispatchEvent(scope.CAMERA_EVENTS.PAN_START_EVENT);
9409
- }
9410
- function handleTouchMovePan(event) {
9411
- let pos = raycastToFloor(event.x ? event : touches[0]);
9412
- panEnd.set(pos.x, pos.y);
9413
- panDelta.subVectors(panEnd, panStart);
9414
- scope.setPosition(panCameraStart.x - panDelta.x, panCameraStart.y - panDelta.y);
9415
- scope.dispatcher.dispatchEvent(scope.CAMERA_EVENTS.CHANGE_EVENT);
9416
- }
9417
- function handleTouchStartDolly() {
9418
- scope.touchInputs = new InputSet(touches[0], touches[1]);
9419
- let dx = touches[0].x - touches[1].x;
9420
- let dy = touches[0].y - touches[1].y;
9421
- zoomStart = scope.getZoom();
9422
- let pos = { x: touches[0].x - dx / 2, y: touches[0].y - dy / 2 };
9423
- floorAnchor = raycastToFloor(pos);
9424
- scope.orbit.updateMatrixWorld();
9425
- scope.touchAnchor1 = new TouchAnchor(touches[0], scope.camera, cameraPlane);
9426
- scope.touchAnchor2 = new TouchAnchor(touches[1], scope.camera, cameraPlane);
9427
- panCameraStart.set(scope.orbit.position.x, scope.orbit.position.y);
9428
- scope.dispatcher.dispatchEvent(scope.INTERACTION_EVENTS.USER_DOLLY_START_EVENT);
9429
- scope.dispatcher.dispatchEvent(scope.CAMERA_EVENTS.MULTI_START_EVENT);
9430
- }
9431
- function handleTouchMoveDolly() {
9432
- scope.touchInputs.update(touches[0], touches[1]);
9433
- const first = scope.touchInputs.first;
9434
- const second = scope.touchInputs.second;
9435
- scope.touchAnchor1.viewCoordinate = { x: first.x, y: first.y };
9436
- scope.touchAnchor2.viewCoordinate = { x: second.x, y: second.y };
9437
- let newMatrix = makeTransformFromTouchAnchors(scope.touchAnchor1, scope.touchAnchor2);
9438
- if (newMatrix) {
9439
- setCameraFromTransformMatrix(newMatrix);
9440
- }
9441
- scope.orbit.updateMatrixWorld();
9442
- scope.touchAnchor1.updateAnchorState(scope.camera, first);
9443
- scope.touchAnchor2.updateAnchorState(scope.camera, second);
9444
- }
9445
- function handleTouchStartTilt(event) {
9446
- rotateStart.set(event.touches[1].clientX, event.touches[1].clientY);
9447
- scope.dispatcher.dispatchEvent(scope.INTERACTION_EVENTS.USER_TILT_START_EVENT);
9448
- scope.dispatcher.dispatchEvent(scope.CAMERA_EVENTS.ROTATE_START_EVENT);
9449
- }
9450
- function handleTouchMoveTilt(event) {
9451
- rotateEnd.set(event.touches[1].clientX, event.touches[1].clientY);
9452
- rotateDelta.subVectors(rotateEnd, rotateStart);
9453
- scope.setTilt(scope.getTilt() - rotateDelta.y / scope.rotateSpeed);
9454
- rotateStart.copy(rotateEnd);
9455
- }
9456
- function handleTouchStartPedestal(event) {
9457
- handleMouseDownPedestal(event.touches[0]);
9458
- }
9459
- function handleTouchMovePedestal(event) {
9460
- handleMouseMovePedestal(event.touches[0]);
9461
- }
9462
- function handleTouchEnd() {
9463
- if (state === STATE.TOUCH_TILT || state === STATE.TOUCH_DOLLY) {
9464
- scope.dispatcher.dispatchEvent(scope.CAMERA_EVENTS.MULTI_END_EVENT);
9465
- }
9466
- }
9467
- function onTouchStart(event) {
9468
- if (scope.enabled === false)
9469
- return;
9470
- updateTouchOrigin(event);
9471
- touchToScene(event);
9472
- switch (event.touches.length) {
9473
- case 1: // one-fingered touch: pan/pedestal
9474
- // note - they are mutually exclusive as they use the same event
9475
- // pedestal takes precedence
9476
- if (scope.enablePedestal) {
9477
- handleTouchStartPedestal(event);
9478
- state = STATE.TOUCH_PEDESTAL;
9479
- }
9480
- else if (scope.enableRotate) {
9481
- handleTouchStartPan(event);
9482
- state = STATE.TOUCH_PAN;
9483
- }
9484
- break;
9485
- case 2: // two-fingered touch: dolly
9486
- if (scope.enableZoom === false)
9487
- return;
9488
- handleTouchStartDolly(event);
9489
- state = STATE.TOUCH_DOLLY;
9490
- break;
9491
- case 3: // three-fingered touch: tilt
9492
- if (scope.enablePan === false)
9493
- return;
9494
- handleTouchStartTilt(event);
9495
- state = STATE.TOUCH_TILT;
9496
- break;
9497
- default:
9498
- state = STATE.NONE;
9499
- }
9500
- if (state !== STATE.NONE) {
9501
- // Probably don't need to do anything
9502
- }
9503
- }
9504
- function onTouchMove(event) {
9505
- if (scope.enabled === false)
9506
- return;
9507
- event.preventDefault();
9508
- event.stopPropagation();
9509
- touchToScene(event);
9510
- switch (event.touches.length) {
9511
- case 3: // one-fingered touch: rotate
9512
- if (scope.enableRotate === false)
9513
- return;
9514
- if (state !== STATE.TOUCH_TILT)
9515
- return;
9516
- handleTouchMoveTilt(event);
9517
- break;
9518
- case 2: // two-fingered touch: zoom
9519
- if (scope.enableZoom === false)
9520
- return;
9521
- if (state !== STATE.TOUCH_DOLLY)
9522
- return;
9523
- handleTouchMoveDolly(event);
9524
- break;
9525
- case 1: // One-fingered touch: pan/pedestal
9526
- if (scope.enablePedestal && state === STATE.TOUCH_PEDESTAL) {
9527
- handleTouchMovePedestal(event);
9528
- }
9529
- else if (scope.enablePan && state === STATE.TOUCH_PAN) {
9530
- handleTouchMovePan(event);
9531
- }
9532
- break;
9533
- default:
9534
- state = STATE.NONE;
9535
- }
9536
- }
9537
- function onTouchEnd(event) {
9538
- if (scope.enabled === false)
9539
- return;
9540
- handleTouchEnd(event);
9541
- switch (state) {
9542
- case STATE.PAN:
9543
- case STATE.TOUCH_PAN:
9544
- scope.dispatcher.dispatchEvent(scope.INTERACTION_EVENTS.USER_PAN_END_EVENT);
9545
- break;
9546
- case STATE.TOUCH_TILT:
9547
- scope.dispatcher.dispatchEvent(scope.CAMERA_EVENTS.PAN_END_EVENT);
9548
- scope.dispatcher.dispatchEvent(scope.INTERACTION_EVENTS.USER_TILT_END_EVENT);
9549
- break;
9550
- case STATE.DOLLY:
9551
- case STATE.TOUCH_DOLLY:
9552
- scope.dispatcher.dispatchEvent(scope.CAMERA_EVENTS.ZOOM_END_EVENT);
9553
- scope.dispatcher.dispatchEvent(scope.INTERACTION_EVENTS.USER_DOLLY_END_EVENT);
9554
- break;
9555
- case STATE.ROTATE:
9556
- scope.dispatcher.dispatchEvent(scope.CAMERA_EVENTS.ROTATE_END_EVENT);
9557
- scope.dispatcher.dispatchEvent(scope.INTERACTION_EVENTS.USER_ROTATE_END_EVENT);
9558
- break;
9559
- case STATE.PEDESTAL:
9560
- case STATE.TOUCH_PEDESTAL:
9561
- scope.dispatcher.dispatchEvent(scope.CAMERA_EVENTS.PEDESTAL_END_EVENT);
9562
- scope.dispatcher.dispatchEvent(scope.INTERACTION_EVENTS.USER_PEDESTAL_END_EVENT);
9563
- break;
9564
- }
9565
- state = STATE.NONE;
9566
- }
9567
- function onContextMenu(event) {
9568
- event.preventDefault();
9569
- }
9570
- function differenceBetweenAngles(a, b) {
9571
- return ((b - a + Math.PI) % (Math.PI * 2)) + ((Math.PI * 2) % (Math.PI * 2)) - Math.PI;
9572
- }
9573
- function makeTransformFromTouchAnchors(touchAnchor1, touchAnchor2) {
9574
- scope.orbit.updateMatrixWorld();
9575
- scope.camera.updateProjectionMatrix();
9576
- let p = scope.camera.projectionMatrix.clone();
9577
- let w1 = touchAnchor1.worldCoordinate;
9578
- let w2 = touchAnchor2.worldCoordinate;
9579
- let nw1 = touchAnchor1.reUnproject();
9580
- let nw2 = touchAnchor2.reUnproject();
9581
- if (!nw1 || !nw2) {
9582
- return null;
9583
- }
9584
- let s1 = touchAnchor1.viewCoordinate;
9585
- let s2 = touchAnchor2.viewCoordinate;
9586
- let xd1 = w2.x - w1.x;
9587
- let yd1 = w2.y - w1.y;
9588
- let angle1 = Math.atan2(yd1, xd1);
9589
- let xd2 = nw2.x - nw1.x;
9590
- let yd2 = nw2.y - nw1.y;
9591
- let angle2 = Math.atan2(yd2, xd2);
9592
- let zAngleDelta = differenceBetweenAngles(angle1, angle2);
9593
- let yawAdjustment = new Matrix4();
9594
- yawAdjustment.makeRotationZ(-zAngleDelta);
9595
- let t = yawAdjustment.clone();
9596
- t.multiply(touchAnchor1.snapHolderMatrix);
9597
- // Matrix subscript helper
9598
- let m = (o, a, b) => o.elements[(a - 1) * 4 + b - 1];
9599
- let x1 = m(p, 1, 1) * m(t, 1, 1) - s1.x * m(p, 3, 4) * m(t, 3, 1);
9600
- let y1 = m(p, 1, 1) * m(t, 1, 2) - s1.x * m(p, 3, 4) * m(t, 3, 2);
9601
- let z1 = m(p, 1, 1) * m(t, 1, 3) - s1.x * m(p, 3, 4) * m(t, 3, 3);
9602
- let c1 = w1.x * x1 + w1.y * y1 + w1.z * z1;
9603
- let x2 = m(p, 2, 2) * m(t, 2, 1) - s1.y * m(p, 3, 4) * m(t, 3, 1);
9604
- let y2 = m(p, 2, 2) * m(t, 2, 2) - s1.y * m(p, 3, 4) * m(t, 3, 2);
9605
- let z2 = m(p, 2, 2) * m(t, 2, 3) - s1.y * m(p, 3, 4) * m(t, 3, 3);
9606
- let c2 = w1.x * x2 + w1.y * y2 + w1.z * z2;
9607
- let x3 = m(p, 1, 1) * m(t, 1, 1) - s2.x * m(p, 3, 4) * m(t, 3, 1);
9608
- let y3 = m(p, 1, 1) * m(t, 1, 2) - s2.x * m(p, 3, 4) * m(t, 3, 2);
9609
- let z3 = m(p, 1, 1) * m(t, 1, 3) - s2.x * m(p, 3, 4) * m(t, 3, 3);
9610
- let c3 = w2.x * x3 + w2.y * y3 + w2.z * z3;
9611
- let xb = m(p, 2, 2) * m(t, 2, 1) - s2.y * m(p, 3, 4) * m(t, 3, 1);
9612
- let yb = m(p, 2, 2) * m(t, 2, 2) - s2.y * m(p, 3, 4) * m(t, 3, 2);
9613
- let zb = m(p, 2, 2) * m(t, 2, 3) - s2.y * m(p, 3, 4) * m(t, 3, 3);
9614
- let cb = w2.x * xb + w2.y * yb + w2.z * zb;
9615
- let dx1 = Math.abs(x3 - x1);
9616
- let dy1 = Math.abs(y3 - y1);
9617
- let dz1 = Math.abs(z3 - z1);
9618
- let dx2 = Math.abs(x3 - x2);
9619
- let dy2 = Math.abs(y3 - y2);
9620
- let dz2 = Math.abs(z3 - z2);
9621
- let dx3 = Math.abs(xb - x1);
9622
- let dy3 = Math.abs(yb - y1);
9623
- let dz3 = Math.abs(zb - z1);
9624
- let dx4 = Math.abs(xb - x2);
9625
- let dy4 = Math.abs(yb - y2);
9626
- let dz4 = Math.abs(zb - z2);
9627
- let min1 = Math.min(dx1 + dy1 + dz1, dx2 + dy2 + dz2);
9628
- let min2 = Math.min(dx3 + dy3 + dz3, dx4 + dy4 + dz4);
9629
- if (min1 < min2) {
9630
- x3 = xb;
9631
- y3 = yb;
9632
- z3 = zb;
9633
- c3 = cb;
9634
- }
9635
- let x4 = x2 * z1 - x1 * z2;
9636
- let y4 = y2 * z1 - y1 * z2;
9637
- let c4 = c2 * z1 - c1 * z2;
9638
- let x5 = x3 * z2 - x2 * z3;
9639
- let y5 = y3 * z2 - y2 * z3;
9640
- let c5 = c3 * z2 - c2 * z3;
9641
- let x = (c5 * y4 - c4 * y5) / (x5 * y4 - x4 * y5);
9642
- let y = (c5 * x4 - c4 * x5) / (y5 * x4 - y4 * x5);
9643
- let y6 = y2 * x1 - y1 * x2;
9644
- let z6 = z2 * x1 - z1 * x2;
9645
- let c6 = c2 * x1 - c1 * x2;
9646
- let y7 = y3 * x2 - y2 * x3;
9647
- let z7 = z3 * x2 - z2 * x3;
9648
- let c7 = c3 * x2 - c2 * x3;
9649
- let z = (c7 * y6 - c6 * y7) / (z7 * y6 - z6 * y7);
9650
- let finalTransformMatrix = t.clone();
9651
- finalTransformMatrix.elements[12] = x;
9652
- finalTransformMatrix.elements[13] = y;
9653
- finalTransformMatrix.elements[14] = z;
9654
- if (z > 0) {
9655
- return finalTransformMatrix;
9656
- }
9657
- else {
9658
- return null;
9659
- }
9660
- }
9661
- function setCameraFromTransformMatrix(transform) {
9662
- scope.orbit.updateMatrixWorld();
9663
- let newAngle = new Euler();
9664
- newAngle.setFromRotationMatrix(transform, 'ZYX');
9665
- let zoom = transform.elements[14] / Math.cos(newAngle.x);
9666
- let rotation = newAngle.z;
9667
- let vectorToGround = new Vector3(0, 0, 1);
9668
- vectorToGround.applyEuler(newAngle);
9669
- vectorToGround.setLength(zoom);
9670
- let groundPosition = new Vector3();
9671
- groundPosition.setFromMatrixPosition(transform);
9672
- groundPosition.sub(vectorToGround);
9673
- scope.setPosition(groundPosition.x, groundPosition.y);
9674
- scope.setZoom(zoom);
9675
- scope.setRotation(rotation);
9676
- }
9677
- function getMousePos(canvas, evt) {
9678
- return {
9679
- x: evt.offsetX,
9680
- y: evt.offsetY,
9681
- };
9682
- }
9683
- function getTouchPos(canvas, touch) {
9684
- return {
9685
- x: touch.clientX - touchOrigin.offsetLeft,
9686
- y: touch.clientY - touchOrigin.offsetTop,
9687
- };
9688
- }
9689
- function updateTouchOrigin(event) {
9690
- let touchOriginElement = scope.canvas;
9691
- touchOrigin.offsetLeft = touchOriginElement.getBoundingClientRect().left;
9692
- touchOrigin.offsetTop = touchOriginElement.getBoundingClientRect().top;
9693
- }
9694
- function raycastToFloor(pos) {
9695
- raycaster.setFromCamera(pos, camera);
9696
- let intersection = raycaster.intersectObject(cameraPlane, false)[0];
9697
- if (intersection) {
9698
- return intersection.point;
9699
- }
9700
- else {
9701
- return new Vector3();
9702
- }
9703
- }
9704
- function mouseToScene(event) {
9705
- let pos = getMousePos(scope.canvas, event);
9706
- mouse.x = (pos.x / scope.canvas.width) * core.resolutionScale * 2 - 1;
9707
- mouse.y = -((pos.y / scope.canvas.height) * core.resolutionScale) * 2 + 1;
9708
- return mouse;
9709
- }
9710
- function touchToScene(event) {
9711
- touches = [];
9712
- for (let i = 0, iLen = event.touches.length; i < iLen; i++) {
9713
- let touch = event.touches[i];
9714
- let pos = getTouchPos(scope.canvas, touch);
9715
- touches.push(new Vector2((pos.x / scope.canvas.width) * core.resolutionScale * 2 - 1, -((pos.y / scope.canvas.height) * core.resolutionScale) * 2 + 1));
9716
- }
9717
- mouse = touches[0];
9718
- }
9719
- /**
9720
- * Should probably be "pre-render". Anything we need to do before rendering the scene.
9721
- *
9722
- * @method update
9723
- * @private
9724
- */
9725
- this.update = function () {
9726
- TWEEN.update();
9727
- if (lastWheelTime > 0 && state == STATE.WHEEL_ZOOM && clock.getElapsedTime() - lastWheelTime > 0.2) {
9728
- lastWheelTime = 0;
9729
- state = STATE.NONE;
9730
- scope.dispatcher.dispatchEvent(scope.CAMERA_EVENTS.ZOOM_END_EVENT);
9731
- scope.canvas.removeEventListener('mousemove', onMouseMove, false);
9732
- }
9733
- };
9734
- let isUserZooming = false;
9735
- this.dispatchUserZoomDebounced = debounce(() => {
9736
- // dispatch user zoom end
9737
- isUserZooming = false;
9738
- scope.dispatcher.dispatchEvent(scope.INTERACTION_EVENTS.USER_ZOOM_END_EVENT);
9739
- }, 250);
9740
- this.dispatchUserZoomEvent = function () {
9741
- if (isUserZooming) {
9742
- scope.dispatchUserZoomDebounced();
9743
- }
9744
- else {
9745
- scope.dispatcher.dispatchEvent(scope.INTERACTION_EVENTS.USER_ZOOM_START_EVENT);
9746
- isUserZooming = true;
9747
- scope.dispatchUserZoomDebounced();
9748
- }
9749
- };
9750
- /**
9751
- * Disposes of the camera and all of it's events.
9752
- *
9753
- * @method dispose
9754
- * @private
9755
- */
9756
- this.dispose = function () {
9757
- for (let e in this.INTERACTION_EVENTS) {
9758
- const event = this.INTERACTION_EVENTS[e];
9759
- if (event.type.endsWith('Start')) {
9760
- scope.dispatcher.removeEventListener(event.type, setUserInteracting);
9761
- }
9762
- }
9763
- for (let e in this.INTERACTION_EVENTS) {
9764
- const event = this.INTERACTION_EVENTS[e];
9765
- if (event.type.endsWith('End')) {
9766
- scope.dispatcher.removeEventListener(event.type, unsetUserInteracting);
9767
- }
9768
- }
9769
- scope.canvas.removeEventListener('contextmenu', onContextMenu, false);
9770
- scope.canvas.removeEventListener('mousedown', onMouseDown, false);
9771
- scope.canvas.removeEventListener('wheel', onMouseWheel, { passive: false });
9772
- scope.canvas.removeEventListener('touchstart', onTouchStart, false);
9773
- scope.canvas.removeEventListener('touchend', onTouchEnd, false);
9774
- scope.canvas.removeEventListener('touchmove', onTouchMove, false);
9775
- scope.canvas.removeEventListener('mousemove', onMouseMove, false);
9776
- window.removeEventListener('mouseup', onMouseUp, false);
9777
- window.removeEventListener('mouseout', onMouseUp, false);
9778
- scene.remove(cameraPlane);
9779
- scope.dispatcher._listeners = {};
9780
- };
9781
- this.canvas.addEventListener('contextmenu', onContextMenu, false);
9782
- this.canvas.addEventListener('mousedown', onMouseDown, false);
9783
- this.canvas.addEventListener('wheel', onMouseWheel, { passive: false });
9784
- this.canvas.addEventListener('touchstart', onTouchStart, false);
9785
- this.canvas.addEventListener('touchend', onTouchEnd, false);
9786
- this.canvas.addEventListener('touchmove', onTouchMove, false);
9787
- };
9788
- export default CameraControls;
9789
-
9790
- import { Color, DepthTexture, Mesh, Vector2, NearestFilter, OrthographicCamera, PlaneBufferGeometry, RGBAFormat, Scene, ShaderMaterial, UnsignedIntType, WebGLRenderTarget, WebGL1Renderer, WebGLRenderer, DepthStencilFormat, sRGBEncoding, } from 'three';
9791
- import { CAMERA_LAYER, RENDER } from '@mappedin/mappedin-js/renderer/internal';
9792
- import CompositeVertexShader from '@mappedin/mappedin-js/renderer/internal/shaders/Mappedin.Composite.Vertex.glsl';
9793
- import CompositeFragmentShader from '@mappedin/mappedin-js/renderer/internal/shaders/Mappedin.Composite.Fragment.glsl';
9794
- import Stats from 'stats.js';
9795
- import Logger from '@mappedin/mappedin-js/--/common/Mappedin.Logger';
9796
- let SHOW_FPS = false;
9797
- let stats;
9798
- if (SHOW_FPS) {
9799
- stats = new Stats();
9800
- stats.showPanel(0); // 0: fps, 1: ms, 2: mb, 3+: custom
9801
- document.body.appendChild(stats.dom);
9802
- }
9803
- /**
9804
- * A class that controls the rendering resources for a canvas output.
9805
- *
9806
- * @class Renderer
9807
- * @private
9808
- */
9809
- export default class Renderer {
9810
- /**
9811
- * @constructor
9812
- * @param renderOptions {Object} Options for rendering
9813
- * @param [options.multiBufferRendering=false] {boolean}
9814
- * Whether to use a multi-buffer renderer
9815
- * @param [options.alpha=true] {boolean}
9816
- * If true, will allow for a semi-transparent background
9817
- * @param [options.antialias=false] {boolean}
9818
- * If true, will attempt to antialias rendering if supported
9819
- * @param [options.backgroundColor='#ffffff'] {Color | string}
9820
- * The color that will be displayed behind the scene
9821
- * @param [options.backgroundAlpha=1.0] {number}
9822
- * The opacity of the background color
9823
- * @param [options.onWebGLContextCreationError=null] {function}
9824
- * A callback that will be triggered if WebGL context creation fails
9825
- * @param [options.onWebGLContextLost=null] {function}
9826
- * A callback that will be triggered if the WebGL context is killed
9827
- * @param [options.onWebGLContextRestored=null] {function}
9828
- * A callback that will be triggered when three.js reacquires a WebGL context
9829
- * @param [options.onWebGLRendererError=null] {function}
9830
- * A callback that will be triggered if the renderer throws an error
9831
- */
9832
- constructor(renderOptions) {
9833
- renderOptions.multiBufferRendering = renderOptions.multiBufferRendering || false;
9834
- this.contextLost = false;
9835
- this.implementation = renderOptions.multiBufferRendering
9836
- ? new MultiBufferRenderer(renderOptions)
9837
- : new SingleBufferRenderer(renderOptions);
9838
- this.implementation = this.getRenderer(renderOptions);
9839
- this.onWebGLContextCreationError = renderOptions.onWebGLContextCreationError;
9840
- this.onWebGLContextLost = renderOptions.onWebGLContextLost;
9841
- this.onWebGLContextRestored = renderOptions.onWebGLContextRestored;
9842
- this.webGLContextCreationErrorListener = this.domElement().addEventListener('webglcontextcreationerror', e => this.reportWebGlContextCreationError(e));
9843
- this.webGLContextLostListener = this.domElement().addEventListener('webglcontextlost', e => this.reportWebGlContextLost(e));
9844
- this.webGLContextRestoredListener = this.domElement().addEventListener('webglcontextrestored', e => this.reportWebGLContextRestored(e));
9845
- let backgroundColor = renderOptions.backgroundColor;
9846
- if (typeof backgroundColor === 'string') {
9847
- backgroundColor = new Color(renderOptions.backgroundColor);
9848
- }
9849
- if (backgroundColor == null) {
9850
- backgroundColor = new Color('#ffffff');
9851
- }
9852
- let backgroundAlpha = renderOptions.backgroundAlpha;
9853
- if (backgroundAlpha == null) {
9854
- backgroundAlpha = 1.0;
9855
- }
9856
- this.shouldConsiderAlpha = renderOptions.alpha || false;
9857
- this.antialias = renderOptions.antialias || false;
9858
- this.setBackgroundColor(backgroundColor, backgroundAlpha);
9859
- }
9860
- /**
9861
- * Dispose of any resources and connections allocated by the renderer.
9862
- *
9863
- * @method destroy
9864
- */
9865
- destroy() {
9866
- this.domElement().removeEventListener('webglcontextcreationerror', this.webGLContextCreationErrorListener);
9867
- this.domElement().removeEventListener('webglcontextlost', this.webGLContextLostListener);
9868
- this.domElement().removeEventListener('webglcontextrestored', this.webGLContextRestoredListener);
9869
- this.implementation.destroy();
9870
- }
9871
- /**
9872
- * Render the scene to the provided framebuffer. A null framebuffer will
9873
- * render to the default canvas.
9874
- *
9875
- * @method render
9876
- * @param renderTask {RENDER}
9877
- * @param renderTarget {null or WebGLRenderTarget}
9878
- * @param scene {Scene}
9879
- * @param sceneCamera {Camera}
9880
- */
9881
- render(renderTask, renderTarget, scene, sceneCamera) {
9882
- this.implementation.render(renderTask, renderTarget, scene, sceneCamera);
9883
- }
9884
- /**
9885
- * Return the maximum supported anisotropy of this renderer.
9886
- *
9887
- * @method getMaxAnisotropy
9888
- * @return {number}
9889
- */
9890
- getMaxAnisotropy() {
9891
- return this.implementation.renderer.capabilities.getMaxAnisotropy();
9892
- }
9893
- /**
9894
- * Return the size of the renderer's target.
9895
- *
9896
- * @method getBufferSize
9897
- * @return {Vector2}
9898
- */
9899
- getBufferSize() {
9900
- return this.implementation.renderer.getSize(new Vector2());
9901
- }
9902
- /**
9903
- * Return the WebGL context associated with this renderer, to ensure
9904
- * other objects that use WebGL will put their textures in the same
9905
- * context.
9906
- *
9907
- * @method context
9908
- * @return {WebGLRenderingContext} context used by this renderer
9909
- */
9910
- getContext() {
9911
- return this.implementation.renderer.getContext();
9912
- }
9913
- /**
9914
- * Preload a texture, I think. Not actually sure what this does. TODO.
9915
- *
9916
- * @method preloadTexture
9917
- * @param texture {Texture}
9918
- */
9919
- preloadTexture(texture) {
9920
- this.implementation.renderer.initTexture(texture);
9921
- }
9922
- /**
9923
- * Set the renderer and all its internal buffers to the provided width and
9924
- * height in pixels.
9925
- *
9926
- * @method setBufferSize
9927
- * @param width {number}
9928
- * @param height {number}
9929
- */
9930
- setBufferSize(width, height) {
9931
- this.implementation.setBufferSize(width, height);
9932
- }
9933
- get backgroundColor() {
9934
- return this.implementation.backgroundColor;
9935
- }
9936
- get backgroundAlpha() {
9937
- return this.implementation.backgroundAlpha;
9938
- }
9939
- /**
9940
- * Set the color and opacity that will be drawn behind the scene.
9941
- *
9942
- * @method setBackgroundColor
9943
- * @param color {Color}
9944
- * @param alpha {number}
9945
- */
9946
- setBackgroundColor(color, alpha) {
9947
- if (!this.shouldConsiderAlpha) {
9948
- alpha = 1.0;
9949
- }
9950
- alpha = alpha !== undefined ? +alpha : 1.0;
9951
- alpha = alpha >= 0.0 && alpha <= 1.0 ? alpha : 1.0;
9952
- this.implementation.setBackgroundColor(color, alpha);
9953
- }
9954
- /**
9955
- * Assign an outdoor context to this renderer, which will be included as
9956
- * part of the `STATIC` rendering pass.
9957
- *
9958
- * @method setMapboxOutdoorContext
9959
- * @param {MapboxOutdoorContext} mapboxOutdoorContext context to draw
9960
- */
9961
- setMapboxOutdoorContext(mapboxOutdoorContext) {
9962
- this.implementation.setMapboxOutdoorContext(mapboxOutdoorContext);
9963
- }
9964
- // Return the DOM element to which this renderer will render when provided
9965
- // a `null` `renderTarget` in the `render` method. You can position this
9966
- // DOM element and add it to the DOM as you see fit.
9967
- domElement() {
9968
- return this.implementation.renderer.domElement;
9969
- }
9970
- // Return true if this renderer is available to use for rendering. If false,
9971
- // this means that the context is currently lost, or the renderer failed to
9972
- // initialize.
9973
- isAvailable() {
9974
- return this.implementation.renderer != null && !this.contextLost;
9975
- }
9976
- // Temporary solution until https://github.com/mrdoob/three.js/issues/12447 is addressed
9977
- disposeOfRenderLists() {
9978
- this.implementation.renderer.renderLists.dispose();
9979
- }
9980
- reportWebGlContextCreationError(e) {
9981
- if (this.onWebGLContextCreationError) {
9982
- this.onWebGLContextCreationError(e);
9983
- }
9984
- }
9985
- reportWebGlContextLost(e) {
9986
- e.preventDefault();
9987
- if (this.onWebGLContextLost) {
9988
- this.onWebGLContextLost(e);
9989
- }
9990
- this.contextLost = true;
9991
- }
9992
- reportWebGLContextRestored() {
9993
- if (this.onWebGLContextRestored) {
9994
- this.onWebGLContextRestored();
9995
- }
9996
- this.contextLost = false;
9997
- }
9998
- /**
9999
- * Sometimes we have to use WebGL 1 because of a regression in Chrome
10000
- * due to some unknown performance bug in Apple's OpenGL Angle backend on MacBook+Radeon GPU.
10001
- * The official recommendation is to turn on Metal backed ANGLE in your Chrome's about:flags.
10002
- * We can't force our users to do it, so we have to support both WebGL 1 & WebGL 2
10003
- * See: https://bugs.chromium.org/p/chromium/issues/detail?id=1245448
10004
- * The Method returns target WebGL version based on the hardware configuration.
10005
- *
10006
- * @param context WebGL context
10007
- * @returns {1|2} WebGL version
10008
- */
10009
- getTargetWebGLVersion(context) {
10010
- const webkit = /(AppleWebKit)/.test(navigator.userAgent);
10011
- const macOS = /(Mac)/.test(navigator.platform);
10012
- const debugInfo = context.getExtension('WEBGL_debug_renderer_info');
10013
- const problemGPUsRegexp = /radeon\spro\s4\d\d|radeon\spro\s5\d\d/i;
10014
- const problemGPUs = problemGPUsRegexp.test(context.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL));
10015
- return webkit && macOS && problemGPUs ? 1 : 2;
10016
- }
10017
- /**
10018
- *
10019
- * @param options
10020
- * @returns {SingleBufferRenderer|MultiBufferRenderer}
10021
- */
10022
- getRenderer(options) {
10023
- const BufferRenderer = options.multiBufferRendering ? MultiBufferRenderer : SingleBufferRenderer;
10024
- const bufferRenderer = new BufferRenderer(options);
10025
- const targetWebGLVersion = this.getTargetWebGLVersion(bufferRenderer.renderer.getContext());
10026
- if (targetWebGLVersion === 2) {
10027
- return bufferRenderer;
10028
- }
10029
- return new BufferRenderer(options, targetWebGLVersion);
10030
- }
10031
- }
10032
- /**
10033
- * A legacy renderer that renders all the elements in the scene to a single
10034
- * buffer. Does not support things like transparent paths behind polygons
10035
- * natively, but can potentially fake it.
10036
- *
10037
- * 2019/07/10 Terence Dickson
10038
- * Generally has worse performance under average loads compared to the
10039
- * multi-buffer renderer, as the multi-buffer renderer can avoid re-rendering
10040
- * the entire map if only animated elements on the map have changed.
10041
- *
10042
- * @class SingleBufferRenderer
10043
- * @private
10044
- */
10045
- class SingleBufferRenderer {
10046
- constructor(renderOptions, targetWebGLVersion = 2) {
10047
- try {
10048
- const options = {
10049
- alpha: renderOptions.alpha || false,
10050
- stencil: true,
10051
- outputEncoding: sRGBEncoding,
10052
- antialias: renderOptions.antialias,
10053
- powerPreference: 'high-performance',
10054
- preserveDrawingBuffer: true,
10055
- };
10056
- this.renderer = targetWebGLVersion === 2 ? new WebGLRenderer(options) : new WebGL1Renderer(options);
10057
- }
10058
- catch (e) {
10059
- if (renderOptions.onWebGLRendererError) {
10060
- renderOptions.onWebGLRendererError(e);
10061
- }
10062
- }
10063
- this.backgroundColor = new Color();
10064
- this.backgroundAlpha = 1.0;
10065
- this.mapboxOutdoorContext = null;
10066
- }
10067
- /**
10068
- * Dispose of the renderer.
10069
- */
10070
- destroy() {
10071
- this.renderer.dispose();
10072
- }
10073
- /**
10074
- * Re-render the scene. This ignores the `renderTask` argument, as all
10075
- * re-renders in the single-buffer renderer are full re-renders.
10076
- *
10077
- * @method render
10078
- * @param renderTask {RENDER}
10079
- * @param renderTarget {null or WebGLRenderTarget}
10080
- * @param scene {Scene}
10081
- * @param sceneCamera {Camera}
10082
- */
10083
- render(renderTask, renderTarget, scene, sceneCamera) {
10084
- sceneCamera.layers.enable(CAMERA_LAYER.STATIC);
10085
- sceneCamera.layers.enable(CAMERA_LAYER.ANIMATED);
10086
- sceneCamera.layers.enable(CAMERA_LAYER.ALWAYS_ON_TOP);
10087
- this.renderer.autoClear = false;
10088
- this.renderer.setClearColor(this.backgroundColor, this.backgroundAlpha);
10089
- this.renderer.setRenderTarget(renderTarget);
10090
- this.renderer.clear();
10091
- if (SHOW_FPS) {
10092
- stats.begin();
10093
- }
10094
- this.renderer.render(scene, sceneCamera);
10095
- if (SHOW_FPS) {
10096
- stats.end();
10097
- }
10098
- if (this.mapboxOutdoorContext != null) {
10099
- this.renderer.state.reset();
10100
- this.mapboxOutdoorContext.render({
10101
- resolution: this.renderer.getSize(new Vector2()),
10102
- camera: sceneCamera,
10103
- });
10104
- this.renderer.state.reset();
10105
- }
10106
- }
10107
- /**
10108
- * Set the size of the renderer.
10109
- *
10110
- * @method setBufferSize
10111
- * @param width {number}
10112
- * @param height {number}
10113
- */
10114
- setBufferSize(width, height) {
10115
- this.renderer.setSize(width, height, false);
10116
- }
10117
- /**
10118
- * Set the color and opacity that will be drawn behind the scene.
10119
- *
10120
- * @method setBackgroundColor
10121
- * @param color {Color}
10122
- * @param alpha {number}
10123
- */
10124
- setBackgroundColor(color, alpha) {
10125
- this.backgroundColor = color;
10126
- this.backgroundAlpha = alpha;
10127
- }
10128
- /**
10129
- * Assign an outdoor context to this renderer.
10130
- *
10131
- * @method setMapboxOutdoorContext
10132
- * @param {MapboxOutdoorContext} mapboxOutdoorContext context to draw
10133
- */
10134
- setMapboxOutdoorContext(mapboxOutdoorContext) {
10135
- this.mapboxOutdoorContext = mapboxOutdoorContext;
10136
- }
10137
- }
10138
- /**
10139
- * Our new renderer that renders static elements to one buffer and animated
10140
- * elements to another buffer, allowing only the latter buffer to be
10141
- * re-rendered if the camera isn't moving. There is also a third buffer that
10142
- * can be used to render elements that must appear always on top.
10143
- *
10144
- * Does not yet support antialiasing.
10145
- *
10146
- * 2019/07/10 Terence Dickson
10147
- * Generally has better performance under average loads compared to the
10148
- * single-buffer renderer, but does not work on IE 11.
10149
- *
10150
- * @class MultiBufferRenderer
10151
- * @private
10152
- */
10153
- class MultiBufferRenderer {
10154
- constructor(renderOptions, targetWebGLVersion = 2) {
10155
- try {
10156
- const options = {
10157
- alpha: renderOptions.alpha || false,
10158
- antialias: false,
10159
- stencil: true,
10160
- outputEncoding: sRGBEncoding,
10161
- powerPreference: 'high-performance',
10162
- preserveDrawingBuffer: true,
10163
- };
10164
- this.renderer = targetWebGLVersion === 2 ? new WebGLRenderer(options) : new WebGL1Renderer(options);
10165
- }
10166
- catch (e) {
10167
- if (renderOptions.onWebGLRendererError) {
10168
- renderOptions.onWebGLRendererError(e);
10169
- }
10170
- }
10171
- this.backgroundColor = new Color();
10172
- this.backgroundAlpha = 1.0;
10173
- this.mapboxOutdoorContext = null;
10174
- // The render target (color and depth textures) that will be used to
10175
- // store the static scene, to composite it with the animated scene.
10176
- this.staticSceneRenderTarget = new WebGLRenderTarget(this.renderer.width, this.renderer.height);
10177
- this.staticSceneRenderTarget.texture.format = RGBAFormat;
10178
- this.staticSceneRenderTarget.texture.minFilter = NearestFilter;
10179
- this.staticSceneRenderTarget.texture.magFilter = NearestFilter;
10180
- this.staticSceneRenderTarget.texture.generateMipmaps = false;
10181
- this.staticSceneRenderTarget.stencilBuffer = true;
10182
- this.staticSceneRenderTarget.depthBuffer = true;
10183
- this.staticSceneRenderTarget.depthTexture = new DepthTexture();
10184
- this.staticSceneRenderTarget.depthTexture.type = UnsignedIntType;
10185
- this.staticSceneRenderTarget.depthTexture.format = DepthStencilFormat;
10186
- // The render target that will be used to store the dynamic scene,
10187
- // re-rendered once per frame while animated elements exist in the
10188
- // 3D scene (e.g., paths.)
10189
- this.animatedSceneRenderTarget = new WebGLRenderTarget(this.renderer.width, this.renderer.height);
10190
- this.animatedSceneRenderTarget.texture.format = RGBAFormat;
10191
- this.animatedSceneRenderTarget.stencilBuffer = true;
10192
- this.animatedSceneRenderTarget.texture.minFilter = NearestFilter;
10193
- this.animatedSceneRenderTarget.texture.magFilter = NearestFilter;
10194
- this.animatedSceneRenderTarget.texture.generateMipmaps = false;
10195
- this.animatedSceneRenderTarget.stencilBuffer = true;
10196
- this.animatedSceneRenderTarget.depthBuffer = true;
10197
- this.animatedSceneRenderTarget.depthTexture = new DepthTexture();
10198
- this.animatedSceneRenderTarget.depthTexture.type = UnsignedIntType;
10199
- this.animatedSceneRenderTarget.depthTexture.format = DepthStencilFormat;
10200
- // The render target that will be used to store the scene whose elements
10201
- // are always drawn on top of everything else. Rendering is done once per
10202
- // frame while such elements exist in the 3D scene, just like the animated scene target.
10203
- // 2020/08/18 - Daniel Vijayakumar
10204
- // This buffer should be used for animated objects that must appear on top of everything
10205
- // else. I can't think of any use-cases where you'd ever want *static* elements to be
10206
- // always on top. If this is not adequate, we can solve this problem with yet a 4th buffer.
10207
- // although this would create a combinatoric explosion of render passes and might start
10208
- // eating into the gains of the multi-buffer approach.
10209
- this.alwaysOnTopSceneRenderTarget = new WebGLRenderTarget(this.renderer.width, this.renderer.height);
10210
- this.alwaysOnTopSceneRenderTarget.texture.format = RGBAFormat;
10211
- this.alwaysOnTopSceneRenderTarget.texture.minFilter = NearestFilter;
10212
- this.alwaysOnTopSceneRenderTarget.texture.magFilter = NearestFilter;
10213
- this.alwaysOnTopSceneRenderTarget.texture.generateMipmaps = false;
10214
- this.alwaysOnTopSceneRenderTarget.stencilBuffer = true;
10215
- this.alwaysOnTopSceneRenderTarget.depthBuffer = true;
10216
- this.alwaysOnTopSceneRenderTarget.depthTexture = new DepthTexture();
10217
- this.alwaysOnTopSceneRenderTarget.depthTexture.type = UnsignedIntType;
10218
- this.alwaysOnTopSceneRenderTarget.depthTexture.format = DepthStencilFormat;
10219
- this.compositeUniforms = {
10220
- staticSceneColorTexture: {
10221
- type: 't',
10222
- value: this.staticSceneRenderTarget.texture,
10223
- },
10224
- staticSceneDepthTexture: {
10225
- type: 't',
10226
- value: this.staticSceneRenderTarget.depthTexture,
10227
- },
10228
- animatedSceneColorTexture: {
10229
- type: 't',
10230
- value: this.animatedSceneRenderTarget.texture,
10231
- },
10232
- animatedSceneDepthTexture: {
10233
- type: 't',
10234
- value: this.animatedSceneRenderTarget.depthTexture,
10235
- },
10236
- alwaysOnTopSceneColorTexture: {
10237
- type: 't',
10238
- value: this.alwaysOnTopSceneRenderTarget.texture,
10239
- },
10240
- };
10241
- // The scene that renders a full-screen quad that will sample from
10242
- // all render targets to produce the composited result.
10243
- this.compositeCamera = new OrthographicCamera(-1, 1, 1, -1, 0, 1);
10244
- this.compositeMaterial = new ShaderMaterial({
10245
- uniforms: this.compositeUniforms,
10246
- vertexShader: CompositeVertexShader,
10247
- fragmentShader: CompositeFragmentShader,
10248
- });
10249
- this.compositeFullscreenQuad = new Mesh(new PlaneBufferGeometry(2, 2), this.compositeMaterial);
10250
- this.compositeScene = new Scene();
10251
- this.compositeScene.add(this.compositeFullscreenQuad);
10252
- }
10253
- /**
10254
- * Dispose of the renderer and its buffers.
10255
- */
10256
- destroy() {
10257
- this.staticSceneRenderTarget.dispose();
10258
- this.animatedSceneRenderTarget.dispose();
10259
- this.alwaysOnTopSceneRenderTarget.dispose();
10260
- this.renderer.dispose();
10261
- }
10262
- /**
10263
- * Compose all buffers into the final image that is sent to the viewport.
10264
- *
10265
- * @private
10266
- * @method renderComposite
10267
- * @param renderTarget {null or WebGLRenderTarget}
10268
- */
10269
- renderComposite(renderTarget) {
10270
- this.renderer.setRenderTarget(renderTarget);
10271
- this.renderer.clear();
10272
- // 23/05/2019 Terence Dickson
10273
- // We need to disable depth writes on the fullscreen-quad, since
10274
- // otherwise it is considered "very close to the camera" and will
10275
- // obscure the path rendering. Keep an eye on this if you update
10276
- // three.js, since we're modifying properties on the WebGL context
10277
- // itself, and three.js might decide to override this in the future.
10278
- this.renderer.getContext().depthMask(false);
10279
- this.renderer.render(this.compositeScene, this.compositeCamera);
10280
- this.renderer.getContext().depthMask(true);
10281
- }
10282
- /**
10283
- * Renders the scene's animated layer to its dedicated buffer.
10284
- *
10285
- * @private
10286
- * @method renderToAnimatedBuffer
10287
- * @param {*} scene
10288
- * @param {*} sceneCamera
10289
- */
10290
- renderToAnimatedBuffer(scene, sceneCamera) {
10291
- sceneCamera.layers.set(CAMERA_LAYER.ANIMATED);
10292
- this.renderer.setClearColor(new Color(), 0.0);
10293
- this.renderer.setRenderTarget(this.animatedSceneRenderTarget);
10294
- this.renderer.clear();
10295
- this.renderer.render(scene, sceneCamera);
10296
- }
10297
- /**
10298
- * Render the provided scene using the given camera, re-rendering only
10299
- * the animated parts.
10300
- *
10301
- * @method renderAnimated
10302
- * @param renderTarget {null or WebGLRenderTarget}
10303
- * @param scene {Scene}
10304
- * @param sceneCamera {Camera}
10305
- */
10306
- renderAnimated(renderTarget, scene, sceneCamera) {
10307
- this.renderToAnimatedBuffer(scene, sceneCamera);
10308
- this.renderComposite(renderTarget);
10309
- }
10310
- /**
10311
- * Renders the scene's always-on-top layer to its dedicated buffer.
10312
- *
10313
- * @private
10314
- * @method renderToAlwaysOnTopBuffer
10315
- * @param {*} scene
10316
- * @param {*} sceneCamera
10317
- */
10318
- renderToAlwaysOnTopBuffer(scene, sceneCamera) {
10319
- sceneCamera.layers.set(CAMERA_LAYER.ALWAYS_ON_TOP);
10320
- this.renderer.setClearColor(new Color(), 0.0);
10321
- this.renderer.setRenderTarget(this.alwaysOnTopSceneRenderTarget);
10322
- this.renderer.clear();
10323
- this.renderer.render(scene, sceneCamera);
10324
- }
10325
- /**
10326
- * Render the provided scene using the given camera, re-rendering only
10327
- * the parts that should appear on top of all other elements.
10328
- *
10329
- * @method renderAlwaysOnTop
10330
- * @param renderTarget {null or WebGLRenderTarget}
10331
- * @param scene {Scene}
10332
- * @param sceneCamera {Camera}
10333
- */
10334
- renderAlwaysOnTop(renderTarget, scene, sceneCamera) {
10335
- this.renderToAlwaysOnTopBuffer(scene, sceneCamera);
10336
- this.renderComposite(renderTarget);
10337
- }
10338
- /**
10339
- * Render all of the scene layers, then combine them.
10340
- *
10341
- * @method renderAll
10342
- * @param renderTarget {null or WebGLRenderTarget}
10343
- * @param scene {Scene}
10344
- * @param sceneCamera {Camera}
10345
- */
10346
- renderAll(renderTarget, scene, sceneCamera) {
10347
- // First, render to static buffer
10348
- sceneCamera.layers.set(CAMERA_LAYER.STATIC);
10349
- this.renderer.autoClear = false;
10350
- this.renderer.setClearColor(this.backgroundColor, this.backgroundAlpha);
10351
- this.renderer.setRenderTarget(this.staticSceneRenderTarget);
10352
- this.renderer.clear();
10353
- this.renderer.render(scene, sceneCamera);
10354
- // Then render mapbox outdoor context
10355
- if (this.mapboxOutdoorContext != null) {
10356
- this.renderer.state.reset();
10357
- this.mapboxOutdoorContext.render({
10358
- resolution: this.renderer.getSize(new Vector2()),
10359
- camera: sceneCamera,
10360
- renderTarget: this.staticSceneRenderTarget,
10361
- });
10362
- this.renderer.state.reset();
10363
- }
10364
- // Next, render the remaining buffers
10365
- this.renderToAnimatedBuffer(scene, sceneCamera);
10366
- this.renderToAlwaysOnTopBuffer(scene, sceneCamera);
10367
- // Finally, composite the scene
10368
- this.renderComposite(renderTarget);
10369
- }
10370
- /**
10371
- * Re-render the scene, depending on which parts of the scene have been
10372
- * invalidated.
10373
- *
10374
- * @method render
10375
- * @param renderTarget {null or WebGLRenderTarget}
10376
- * @param scene {Scene}
10377
- * @param sceneCamera {Camera}
10378
- */
10379
- render(renderTask, renderTarget, scene, sceneCamera) {
10380
- if (renderTask === RENDER.ANIMATED) {
10381
- this.renderAnimated(renderTarget, scene, sceneCamera);
10382
- }
10383
- else if (renderTask === RENDER.ALWAYS_ON_TOP) {
10384
- this.renderAlwaysOnTop(renderTarget, scene, sceneCamera);
10385
- }
10386
- else if (renderTask === RENDER.ALL) {
10387
- this.renderAll(renderTarget, scene, sceneCamera);
10388
- }
10389
- else {
10390
- Logger.error('Renderer.render called with invalid renderTask');
10391
- }
10392
- }
10393
- /**
10394
- * Set the size of the renderer, and all its internal buffers.
10395
- *
10396
- * @method setBufferSize
10397
- * @param width {number}
10398
- * @param height {number}
10399
- */
10400
- setBufferSize(width, height) {
10401
- this.renderer.setSize(width, height, false);
10402
- this.staticSceneRenderTarget.setSize(width, height);
10403
- this.animatedSceneRenderTarget.setSize(width, height);
10404
- this.alwaysOnTopSceneRenderTarget.setSize(width, height);
10405
- }
10406
- /**
10407
- * Set the color and opacity that will be drawn behind the scene.
10408
- *
10409
- * @method setBackgroundColor
10410
- * @param color {Color}
10411
- * @param alpha {number}
10412
- */
10413
- setBackgroundColor(color, alpha) {
10414
- this.backgroundColor = color;
10415
- this.backgroundAlpha = alpha;
10416
- }
10417
- /**
10418
- * Assign an outdoor context to this renderer.
10419
- *
10420
- * @method setMapboxOutdoorContext
10421
- * @param {MapboxOutdoorContext} mapboxOutdoorContext context to draw
10422
- */
10423
- setMapboxOutdoorContext(mapboxOutdoorContext) {
10424
- this.mapboxOutdoorContext = mapboxOutdoorContext;
10425
- }
10426
- }
10427
-
10428
7601
  declare module '@mappedin/mappedin-js/renderer/internal/utils' {
10429
7602
  /**
10430
7603
  * Utils function listing
@@ -10531,6 +7704,18 @@ declare module '@mappedin/mappedin-js/renderer/internal/utils' {
10531
7704
  export function isObject(item: any): any;
10532
7705
  export function cyrb53(str: any, seed?: number): number;
10533
7706
  export function addMarginMultiplierToBoundingBox(bbox: any, multiplier: any): any;
7707
+ export function tweenPromise({ from, to, duration, easing, delay, onUpdate, onStart, onComplete, }: {
7708
+ from: any;
7709
+ to: any;
7710
+ duration?: number | undefined;
7711
+ easing?: any;
7712
+ delay?: number | undefined;
7713
+ onUpdate?: ((_: any) => void) | undefined;
7714
+ onStart?: (() => void) | undefined;
7715
+ onComplete?: (() => void) | undefined;
7716
+ }): {
7717
+ start(core: any): Promise<any>;
7718
+ };
10534
7719
  export function getPrimaryLocationForPolygon(polygon: any, venue: any): any;
10535
7720
  export function determineStartingMap(venue: any, options: any): any;
10536
7721
  }
@@ -11240,73 +8425,6 @@ declare module '@mappedin/mappedin-js/navigator/interfaces/ILocation' {
11240
8425
 
11241
8426
 
11242
8427
 
11243
- precision mediump float;
11244
-
11245
- uniform sampler2D textureLabel;
11246
-
11247
- varying vec2 vUV;
11248
- varying vec3 vColor;
11249
-
11250
- void main() {
11251
- gl_FragColor = texture2D(textureLabel, vUV);
11252
- gl_FragColor.rgb = vColor;
11253
- }
11254
-
11255
- attribute vec2 position;
11256
- attribute vec3 origin;
11257
- attribute vec2 size;
11258
- attribute float rotation;
11259
- attribute vec4 uv;
11260
- attribute vec3 color;
11261
-
11262
- uniform mat4 projectionMatrix;
11263
- uniform mat4 viewMatrix;
11264
- uniform mat4 modelMatrix;
11265
-
11266
- varying vec2 vUV;
11267
- varying vec3 vColor;
11268
-
11269
- void main() {
11270
- vec4 mapDirectionVector = vec4(0, 1, 0, 0) * viewMatrix * modelMatrix;
11271
- float mapDirection = atan(mapDirectionVector.y, mapDirectionVector.x);
11272
-
11273
- float cosRotation = cos(rotation);
11274
- float sinRotation = sin(rotation);
11275
-
11276
- mat4 rotationMatrix = mat4(
11277
- vec4(cosRotation, -sinRotation, 0.0, 0.0),
11278
- vec4(sinRotation, cosRotation, 0.0, 0.0),
11279
- vec4(0.0, 0.0, 1.0, 0.0),
11280
- vec4(0.0, 0.0, 0.0, 1.0)
11281
- );
11282
-
11283
- mat4 translate = mat4(
11284
- vec4(1, 0, 0, 0),
11285
- vec4(0, 1, 0, 0),
11286
- vec4(0, 0, 1, 0),
11287
- vec4(origin, 1)
11288
- );
11289
-
11290
- gl_Position = projectionMatrix *
11291
- viewMatrix *
11292
- modelMatrix *
11293
- translate *
11294
- rotationMatrix *
11295
- (vec4(position * size - vec2(0, size.y / 2.0), 0, 1));
11296
-
11297
-
11298
- vec2 uvStart = uv.xy;
11299
- vec2 uvSize = uv.zw;
11300
-
11301
- if (sin(mapDirection + rotation) > 0.0) {
11302
- vUV = position * uvSize + vec2(uvStart.x, uvStart.y - uvSize.y);
11303
- } else {
11304
- vUV = (vec2(1, 1) - position) * uvSize + vec2(uvStart.x, uvStart.y - uvSize.y);
11305
- }
11306
-
11307
- vColor = color;
11308
- }
11309
-
11310
8428
  declare module '@mappedin/mappedin-js/renderer/internal/quad-tree' {
11311
8429
  export function contains(rect1: Rectangle, rect2: Rectangle): boolean;
11312
8430
  export function intersects(rect1: Rectangle, rect2: Rectangle): boolean;
@@ -11342,24 +8460,6 @@ declare module '@mappedin/mappedin-js/renderer/internal/quad-tree' {
11342
8460
  export { QuadTree, Rectangle };
11343
8461
  }
11344
8462
 
11345
- declare module '@mappedin/mappedin-js/renderer/internal/Mappedin.CameraControls.TouchAnchor' {
11346
- export default TouchAnchor;
11347
- /**
11348
- * An object representing a touch event anchored to a point along the z=0 plane.
11349
- * @type {any}
11350
- */
11351
- let TouchAnchor: any;
11352
- }
11353
-
11354
- declare module '@mappedin/mappedin-js/renderer/internal/Mappedin.CameraControls.InputSet' {
11355
- export default InputSet;
11356
- /**
11357
- *
11358
- * @type {any}
11359
- */
11360
- let InputSet: any;
11361
- }
11362
-
11363
8463
  declare module '@mappedin/mappedin-js/renderer/internal/Mappedin.MultiFloorView' {
11364
8464
  export namespace VIEW_STATE {
11365
8465
  const SINGLE_FLOOR: string;
@@ -11455,51 +8555,3 @@ declare module '@mappedin/mappedin-js/renderer/internal/Mappedin.MultiFloorView'
11455
8555
  }
11456
8556
  }
11457
8557
 
11458
- varying vec2 vUv;
11459
-
11460
- void main() {
11461
- vUv = uv;
11462
- gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
11463
- }
11464
-
11465
- varying vec2 vUv;
11466
- uniform sampler2D staticSceneColorTexture;
11467
- uniform sampler2D staticSceneDepthTexture;
11468
- uniform sampler2D animatedSceneColorTexture;
11469
- uniform sampler2D animatedSceneDepthTexture;
11470
- uniform sampler2D alwaysOnTopSceneColorTexture;
11471
-
11472
- vec4 alphaBlend(vec4 background, vec4 foreground) {
11473
- vec3 resultColor = (
11474
- foreground.rgb * foreground.a +
11475
- background.rgb * background.a * (1.0 - foreground.a)
11476
- );
11477
- float resultAlpha = foreground.a + background.a * (1.0 - foreground.a);
11478
- return vec4(resultColor / resultAlpha, resultAlpha);
11479
- }
11480
-
11481
- vec4 alphaBlendPremultipliedAlpha(vec4 background, vec4 foreground) {
11482
- return foreground + (background * (1.0 - foreground.a));
11483
- }
11484
-
11485
- void main() {
11486
- float staticDepth = texture2D(staticSceneDepthTexture, vUv).r;
11487
- float animatedDepth = texture2D(animatedSceneDepthTexture, vUv).r;
11488
-
11489
- vec4 staticColor = texture2D(staticSceneColorTexture, vUv).rgba;
11490
- vec4 animatedColor = texture2D(animatedSceneColorTexture, vUv).rgba;
11491
-
11492
- if (staticDepth < animatedDepth) {
11493
- // 19/06/10 Terence Dickson
11494
- // Spoiler alert! This is to help prepare for CE-1352, where alpha
11495
- // may be partial instead of zero. Not implementing that as part of
11496
- // this PR, however.
11497
- animatedColor.a *= 0.0;
11498
- }
11499
-
11500
- vec4 lowerColor = alphaBlend(staticColor, animatedColor);
11501
- vec4 alwaysOnTopColor = texture2D(alwaysOnTopSceneColorTexture, vUv).rgba;
11502
-
11503
- gl_FragColor = alphaBlendPremultipliedAlpha(lowerColor, alwaysOnTopColor);
11504
- }
11505
-