@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.
- package/lib/esm/get-venue/index.js +1 -1
- package/lib/esm/navigator/index.js +1 -1
- package/lib/esm/renderer/{browser-OTR4GNXU.js → browser-MGEJZGLW.js} +1 -1
- package/lib/esm/renderer/chunk-C2IF7BQA.js +1 -0
- package/lib/esm/renderer/index.d.ts +137 -3085
- package/lib/esm/renderer/index.js +1 -1
- package/lib/mappedin.js +1 -1
- package/lib/node/index.js +1 -1
- package/package.json +2 -2
- package/lib/esm/renderer/chunk-T7KRJIFY.js +0 -1
|
@@ -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<
|
|
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
|
|
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:
|
|
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,
|
|
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
|
|
7670
|
-
|
|
7671
|
-
|
|
7672
|
-
|
|
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
|
-
|
|
7678
|
-
|
|
7679
|
-
|
|
7680
|
-
|
|
7681
|
-
|
|
7682
|
-
|
|
7683
|
-
|
|
7684
|
-
|
|
7685
|
-
|
|
7686
|
-
|
|
7687
|
-
|
|
7688
|
-
|
|
7689
|
-
|
|
7690
|
-
|
|
7691
|
-
|
|
7692
|
-
|
|
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
|
-
|