From 6eea1a3b7dafab7df6218ad59ea0e95a5eefdda1 Mon Sep 17 00:00:00 2001 From: Alexander Artemev Date: Thu, 28 Mar 2024 22:36:31 +0700 Subject: [PATCH] refs --- src/control/index.ts | 2 +- src/control/types.ts | 8 +-- src/external/evented.ts | 30 ++++++----- src/labelGroups.ts | 2 +- src/plugin.ts | 93 ++++++++++++++++++++++++++++++++-- src/realtyScene/realtyScene.ts | 14 ++++- src/types/events.ts | 40 +++++++-------- src/types/plugin.ts | 87 ++++++++++++++++--------------- src/types/realtyScene.ts | 52 +++++++++++-------- src/utils/common.ts | 8 --- src/utils/url.ts | 4 +- 11 files changed, 222 insertions(+), 118 deletions(-) diff --git a/src/control/index.ts b/src/control/index.ts index 04317ec..ec964f3 100644 --- a/src/control/index.ts +++ b/src/control/index.ts @@ -22,7 +22,7 @@ const content = /* HTML */ ` `; /** - * A control for change floor layer level on the plugin. + * A control for change floor layer level in the plugin. * It appears on the map only if you set the `floorControl` option within @type PluginOptions to `true`. * @hidden * @internal diff --git a/src/control/types.ts b/src/control/types.ts index 7b64598..a7803c4 100644 --- a/src/control/types.ts +++ b/src/control/types.ts @@ -1,5 +1,5 @@ /** - * Floor level data + * Floor level data. */ export interface FloorLevel { modelId: string; // id модели этажа или здания @@ -8,7 +8,7 @@ export interface FloorLevel { } /** - * Options for the method show + * Options for the show method. */ export interface ControlShowOptions { buildingModelId: string; @@ -17,7 +17,7 @@ export interface ControlShowOptions { } /** - * Event that emitted on button presses of the control + * Event that emitted on button presses of the control. */ export interface FloorChangeEvent { modelId: string; // id модели этажа или здания @@ -25,7 +25,7 @@ export interface FloorChangeEvent { export interface ControlEventTable { /** - * Emitted when floor's plan was changed + * Emitted when floor's plan was changed. */ floorchange: FloorChangeEvent; } diff --git a/src/external/evented.ts b/src/external/evented.ts index e246973..7dd8538 100644 --- a/src/external/evented.ts +++ b/src/external/evented.ts @@ -1,5 +1,5 @@ /** - * Event emitter + * Event emitter. */ export class Evented { private events: { [K in keyof M]?: Array<(ev: M[K]) => void> }; @@ -12,9 +12,10 @@ export class Evented { } /** - * Registers event listener - * @param type Event type - * @param listener Event handler + * Registers event listener. + * + * @param type Event type. + * @param listener Event handler. */ public on(type: K, listener: (ev: M[K]) => void): this { let eventsByType = this.events[type]; @@ -26,9 +27,10 @@ export class Evented { } /** - * Registers event listener which will be called once - * @param type Event type - * @param listener Event handler + * Registers event listener which will be called once. + * + * @param type Event type. + * @param listener Event handler. */ public once(type: K, listener: (ev: M[K]) => void): this { const wrapper = (data: M[K]) => { @@ -42,9 +44,10 @@ export class Evented { } /** - * Removes event listener registered with `on` - * @param type Event type - * @param listener Event handler + * Removes event listener registered with `on`. + * + * @param type Event type. + * @param listener Event handler. */ public off(type: K, listener: (ev: M[K]) => void): this { const eventsByType = this.events[type]; @@ -63,9 +66,10 @@ export class Evented { } /** - * Calls all event listeners with event type `type` - * @param type Event type - * @param data Data transferred to events + * Calls all event listeners with event type `type`. + * + * @param type Event type. + * @param data Data transferred to events. */ public emit(type: K, data?: M[K]): this { const eventsByType = this.events[type]; diff --git a/src/labelGroups.ts b/src/labelGroups.ts index f265b90..2a906fa 100644 --- a/src/labelGroups.ts +++ b/src/labelGroups.ts @@ -1,6 +1,6 @@ import type { Map as MapGL, Label } from '@2gis/mapgl/types'; import type { BuildingState, LabelGroupOptions } from './types/plugin'; -import { GltfPlugin } from './plugin'; +import type { GltfPlugin } from './plugin'; // import { pluginEvents } from './constants'; // import { createLabelEvenData } from './utils/events'; diff --git a/src/plugin.ts b/src/plugin.ts index 039bab1..18b4874 100644 --- a/src/plugin.ts +++ b/src/plugin.ts @@ -33,15 +33,16 @@ export class GltfPlugin extends Evented { private models: Map; private labelGroups: LabelGroups; private realtyScene?: RealtyScene; + private isDestroyed = false; /** - * The main class of the plugin + * The main class of the plugin. * * Example: * ```js - * const plugin = new GltfPlugin (map, { + * const plugin = new GltfPlugin(map, { * modelsLoadStrategy: 'waitAll', - * ambientLight: { color: 'white', intencity: 2.5 }, + * modelsBaseUrl: 'https://url_to_models', * }); * * plugin.addModels([ @@ -54,8 +55,8 @@ export class GltfPlugin extends Evented { * }, * ]); * ``` - * @param map The map instance - * @param pluginOptions GltfPlugin initialization options + * @param map The map instance. + * @param pluginOptions GltfPlugin initialization options. */ constructor(map: MapGL, pluginOptions?: PluginOptions) { super(); @@ -66,7 +67,11 @@ export class GltfPlugin extends Evented { this.labelGroups = new LabelGroups(this.map, this); } + /** + * Destroys the plugin. + */ public destroy() { + this.isDestroyed = true; this.models.forEach((model) => { model.instance.destroy(); }); @@ -75,6 +80,11 @@ export class GltfPlugin extends Evented { this.realtyScene?.destroy(); } + /** + * Sets options of the plugin. + * + * @param pluginOptions Plugin options that are available for setting. + */ public setOptions(pluginOptions: Pick, 'groundCoveringColor'>) { Object.keys(pluginOptions).forEach((option) => { switch (option) { @@ -87,10 +97,23 @@ export class GltfPlugin extends Evented { }); } + /** + * Adds a model to the map. + * + * @param modelToLoad Options of a model. + * @param hideOnLoad Set to `true` if a model should be hidden on loading completion. + */ public async addModel(modelToLoad: ModelOptions, hideOnLoad = false) { return this.addModels([modelToLoad], hideOnLoad ? [] : [modelToLoad.modelId]); } + /** + * Adds a list of models to the map. + * + * @param modelsToLoad An array of options of models. + * @param modelIdsToShow An array of ids of models that should be shown. If it's not provided + * all models will be shown. + */ public async addModels(modelsToLoad: ModelOptions[], modelIdsToShow?: string[]) { const loadingModels = modelsToLoad .filter((options) => { @@ -156,6 +179,10 @@ export class GltfPlugin extends Evented { }); return Promise.all(loadingModels).then((loadedModels) => { + if (this.isDestroyed) { + return; + } + if (this.options.modelsLoadStrategy !== 'waitAll') { return; } @@ -168,6 +195,12 @@ export class GltfPlugin extends Evented { }); } + /** + * Returns a current status of a model. + * There can be no model or it can be loading or loaded. + * + * @param id A model id. + */ public getModelStatus(id: string) { const model = this.models.get(id); if (!model) { @@ -177,6 +210,11 @@ export class GltfPlugin extends Evented { return !model.isLoaded ? ModelStatus.Loading : ModelStatus.Loaded; } + /** + * Removes a model from the map. + * + * @param id A model id. + */ public removeModel(id: string) { const model = this.models.get(id); if (model) { @@ -185,39 +223,84 @@ export class GltfPlugin extends Evented { } } + /** + * Removes models from the map. + * + * @param id Model ids. + */ public removeModels(ids: string[]) { ids.forEach((id) => this.removeModel(id)); } + /** + * Shows a model on the map. + * + * @param id A model id. + */ public showModel(id: string) { this.models.get(id)?.instance.show(); } + /** + * Shows models on the map. + * + * @param id Model ids. + */ public showModels(ids: string[]) { ids.forEach((id) => this.showModel(id)); } + /** + * Hides a model on the map. + * + * @param id A model id. + */ public hideModel(id: string) { this.models.get(id)?.instance.hide(); } + /** + * Hides models on the map. + * + * @param id Model ids. + */ public hideModels(ids: string[]) { ids.forEach((id) => this.hideModel(id)); } + /** + * Adds a group of labels to the map. + * + * @param options Options of the group of labels. + * @param state A state of active building and floor a group of labels is associated with. + */ public addLabelGroup(options: LabelGroupOptions, state?: BuildingState) { this.labelGroups.add(options, state); } + /** + * Removes a group of labels from the map. + * + * @param id A label group id. + */ public removeLabelGroup(id: string) { this.labelGroups.remove(id); } + /** + * Adds an interactive realty scene to the map. + * + * @param scene Options of the scene to add to the map. + * @param state A state of building and floor that should be active on realty scene initialization. + */ public async addRealtyScene(scene: BuildingOptions[], state?: BuildingState) { this.realtyScene = new RealtyScene(this, this.map, this.options); return this.realtyScene.init(scene, state); } + /** + * Removes an interactive realty scene from the map. + */ public removeRealtyScene() { this.realtyScene?.destroy(); this.realtyScene = undefined; diff --git a/src/realtyScene/realtyScene.ts b/src/realtyScene/realtyScene.ts index 2189e21..75bb529 100644 --- a/src/realtyScene/realtyScene.ts +++ b/src/realtyScene/realtyScene.ts @@ -42,6 +42,7 @@ export class RealtyScene { activeModelId: undefined, buildingVisibility: new Map(), }; + private isDestroyed = false; private groundCoveringSource: GeoJsonSource; private control: GltfFloorControl; @@ -167,6 +168,10 @@ export class RealtyScene { } else { if (modelStatus === ModelStatus.NoModel) { this.plugin.addModel(newModelOptions, true).then(() => { + if (this.isDestroyed) { + return; + } + if (this.state.activeModelId !== newModelOptions.modelId) { return; } @@ -292,6 +297,10 @@ export class RealtyScene { return this.plugin .addModels(Array.from(modelsToLoad.values()), Array.from(buildingVisibility.keys())) .then(() => { + if (this.isDestroyed) { + return; + } + this.setState({ activeModelId, buildingVisibility, @@ -315,6 +324,7 @@ export class RealtyScene { } public destroy() { + this.isDestroyed = true; this.map.off('styleload', this.onStyleLoad); this.plugin.off('click', this.onSceneClick); this.plugin.off('mouseover', this.onSceneMouseOver); @@ -367,9 +377,9 @@ export class RealtyScene { } } - private onStyleLoad() { + private onStyleLoad = () => { this.map.addLayer(GROUND_COVERING_LAYER); - } + }; private onSceneMouseOut = (ev: GltfPluginLabelEvent | GltfPluginModelEvent) => { if (ev.target.type !== 'model') { diff --git a/src/types/events.ts b/src/types/events.ts index 22482c0..4c3aa71 100644 --- a/src/types/events.ts +++ b/src/types/events.ts @@ -2,101 +2,101 @@ import type { ModelOptions, LabelOptions } from './plugin'; export interface ModelTarget { /** - * Type of the target + * The type of a target. */ type: 'model'; /** - * The targeted model + * A targeted model. */ data: ModelOptions; /** - * Identifier of the building's or floor's model + * An identifier of the building's or floor's model. */ modelId: string; } export interface LabelTarget { /** - * Type of the target + * The type of a target. */ type: 'label'; /** - * The targeted poi + * A targeted label. */ data: LabelOptions; /** - * Identifier of the building's model + * An identifier of the building's model. */ buildingId?: string; /** - * Identifier of the current floor + * An identifier of the current floor. */ floorId?: string; } /** - * The event type for pointer-related plugin events + * Event type for pointer-related plugin events. */ interface GltfPluginPointerEvent { /** - * The original DOM event + * An original DOM event. */ originalEvent: MouseEvent | TouchEvent; /** - * Geographical coordinates of the event + * Geographical coordinates of an event. */ lngLat: number[]; /** - * Screen coordinates of the event + * Screen coordinates of an event. */ point: number[]; } /** - * The event type for pointer-related plugin events emitted by the poi + * Event type for pointer-related plugin events emitted by a label. */ export interface GltfPluginLabelEvent extends GltfPluginPointerEvent { /** - * Target of the poi event + * A target of a label event. */ target: LabelTarget; } /** - * The event type for pointer-related plugin events emitted by the model + * Event type for pointer-related plugin events emitted by a model. */ export interface GltfPluginModelEvent extends GltfPluginPointerEvent { /** - * Target of the model event + * A target of a model event. */ target: ModelTarget; } /** - * The list of events that can be emitted by the glTF plugin instance + * List of events that can be emitted by the GLTF plugin instance. */ export interface GltfPluginEventTable { /** - * Emitted when model or poi are clicked + * Emitted when a model or a label is clicked. */ click: GltfPluginLabelEvent | GltfPluginModelEvent; /** - * Emitted when the user moves the pointer over the model or the poi + * Emitted when user moves pointer over a model or a label. */ mousemove: GltfPluginLabelEvent | GltfPluginModelEvent; /** - * Emitted when the user hovers over the model or the poi + * Emitted when user hovers over a model or a label. */ mouseover: GltfPluginLabelEvent | GltfPluginModelEvent; /** - * Emitted when the user moves the mouse away from the model or the poi + * Emitted when user moves mouse away from a model or a label. */ mouseout: GltfPluginLabelEvent | GltfPluginModelEvent; } diff --git a/src/types/plugin.ts b/src/types/plugin.ts index bd6ea2d..cbd1936 100644 --- a/src/types/plugin.ts +++ b/src/types/plugin.ts @@ -18,42 +18,41 @@ export type ControlPosition = */ export interface ControlOptions { /** - * Position of the control. + * A position of the control. */ position: ControlPosition; } /** - * Options for the hover state of models + * Options for the hover state of models. */ export interface HoverOptions { /** - * Hover color - * @default '#ffffff' + * A hover color. */ color: string; } /** - * Options for the plugin + * Options for the plugin. */ export interface PluginOptions { /** - * The url which is used for resolving of a model's relative url + * A URL which is used for resolving of a model's relative path. */ modelsBaseUrl?: string; /** - * Strategies for the loading of models: - * - dontWaitAll - show models as soon as possible - * - waitAll - show models only when all models are ready for the rendering + * Strategies for loading of models: + * - dontWaitAll - show models as soon as possible. + * - waitAll - show models only when all models are ready for rendering. */ modelsLoadStrategy?: 'dontWaitAll' | 'waitAll'; /** - * Settings for floors' control + * Settings for floors' control. */ floorsControl?: ControlOptions; /** - * Settings of the hovered models + * Settings of hovered models. */ hoverOptions?: HoverOptions; /** @@ -63,138 +62,142 @@ export interface PluginOptions { } /** - * State for the building's scene + * State for the building's scene. */ export interface BuildingState { /** - * Identifier of the building's model + * An identifier of the building's model. */ buildingId: string; /** - * Identifier of the floor's model + * An identifier of the floor's model. */ floorId?: string; } /** - * Options for a model + * Options for a model. */ export interface ModelOptions { /** - * Identifier of the model should be unique for every model + * An identifier of a model should be unique for every model. */ modelId: string; /** - * Geographical coordinates [longitude, latitude] + * Geographical coordinates [longitude, latitude]. */ coordinates: number[]; /** - * Url where the model is located + * URL where a model is located. */ modelUrl: string; /** - * Rotation of the model in degrees about the X axis + * Rotation of a model in degrees about the X axis. */ rotateX?: number; /** - * Rotation of the model in degrees about the Y axis + * Rotation of a model in degrees about the Y axis. */ rotateY?: number; /** - * Rotation of the model in degrees about the Z axis + * Rotation of a model in degrees about the Z axis. */ rotateZ?: number; /** - * Offset of the model along the X axis in meters + * Offset of a model along the X axis in meters. */ offsetX?: number; /** - * Offset of the model along the Y axis in meters + * Offset of a model along the Y axis in meters. */ offsetY?: number; /** - * Offset of the model along the Z axis in meters + * Offset of a model along the Z axis in meters. */ offsetZ?: number; /** - * Scale of the model + * Scale of a model. */ scale?: number; /** - * List of buildings' identifiers that should be hidden + * A list of buildings' identifiers that should be hidden. */ linkedIds?: string[]; /** - * User specific data + * User specific data. */ userData?: any; /** - * Interactivity of model. All models are interactive by default + * Interactivity of model. All models are interactive by default. */ interactive?: boolean; } /** - * Options for a poi + * Options for a label. */ export interface LabelOptions { /** - * Coordinate of the poi + * Coordinates of a label. */ coordinates: [number, number]; /** - * Elevation of the poi + * An elevation of a label. */ elevation?: number; /** - * Elevation of the poi + * A text of a label. */ text: string; /** - * User specific data + * User specific data. */ userData?: any; } /** - * Options for a poi group + * Options for a label group. */ export interface LabelGroupOptions { /** - * Identifier of the poi group to add + * An identifier of a label group to add. */ id: string; /** - * Elevation of the group of poi + * An elevation of a label group. */ elevation: number; /** - * Array of poi to add on the map + * An array of labels to add on the map */ labels: LabelOptions[]; /** - * Type of the poi + * Image settings for labels' text background. */ image?: LabelImage; /** - * Minimum display styleZoom of the poi group + * A minimum display styleZoom of a label group. */ minZoom?: number; /** - * Maximum display styleZoom of the poi group + * A maximum display styleZoom of a label group. */ maxZoom?: number; /** - * Size of the poi's font + * A size of a label's font. */ fontSize?: number; /** - * Color of the poi's font + * A color of a label's font. */ fontColor?: string; } +/** + * Status of a model. + * There can be no model or it can be loading or loaded. + */ export enum ModelStatus { NoModel, Loading, diff --git a/src/types/realtyScene.ts b/src/types/realtyScene.ts index c9d3328..60c52fd 100644 --- a/src/types/realtyScene.ts +++ b/src/types/realtyScene.ts @@ -2,53 +2,53 @@ import { FloorLevel } from '../control/types'; import type { ModelOptions, LabelGroupOptions } from './plugin'; /** - * Options for the map + * Options for the map. */ export interface MapOptions { /** - * Geographical center of the map + * Geographical center of the map. */ center?: number[]; /** - * Map's pitch angle in degrees + * Map's pitch angle in degrees. */ pitch?: number; /** - * Map's rotation angle in degrees + * Map's rotation angle in degrees. */ rotation?: number; /** - * Map's zoom + * Map's zoom. */ zoom?: number; } /** - * Options for a floor's plan on the realty scene + * Options for a floor's plan in the realty scene. */ export interface BuildingFloorOptions { /** - * Identifier of the floor's plan + * An identifier of the floor's plan. */ id: string; /** - * Text to add to the floors' control + * A text to add to the floors' control. */ text: string; /** - * Url of a model that represents the current floor's plan + * A URL of a model that represents the current floor's plan. */ modelUrl: string; /** - * Icon to add to the floors' control + * An icon to add to the floors' control. */ icon?: 'building' | 'parking' | string; /** - * List of poi groups connected with the floor's plan + * A list of groups of labels connected with the floor's plan. */ labelGroups?: LabelGroupOptions[]; /** - * Map's options to apply after selecting the particular floor + * Map's options to apply after selecting the particular floor. */ mapOptions?: MapOptions; /** @@ -60,41 +60,45 @@ export interface BuildingFloorOptions { } /** - * Options of popup that appears on hover of buildings + * Options of popup that appears on hover of buildings. */ export interface PopupOptions { /** - * Popup's coordinates + * Popup's coordinates. */ coordinates: number[]; /** - * Popup's title + * A popup's title. */ title: string; /** - * Popup's description + * A popup's description. */ description?: string; } /** - * Options for a building on the realty scene + * Options for a building in the realty scene. */ export interface BuildingOptions extends ModelOptions { /** - * Map's options to apply after selecting the particular building + * Map's options to apply after selecting the particular building. */ mapOptions?: MapOptions; /** - * List of the floors' plans connected with the particular building + * A list of the floors' plans connected with the particular building. */ floors?: BuildingFloorOptions[]; /** - * Popup options + * Popup options. */ popupOptions?: PopupOptions; } +/** + * @hidden + * @internal + */ export interface RealtySceneState { activeModelId?: string; @@ -102,10 +106,18 @@ export interface RealtySceneState { buildingVisibility: Map; } +/** + * @hidden + * @internal + */ export type BuildingOptionsInternal = Omit & { floors: FloorLevel[]; }; +/** + * @hidden + * @internal + */ export type BuildingFloorOptionsInternal = BuildingFloorOptions & { buildingOptions: ModelOptions; }; diff --git a/src/utils/common.ts b/src/utils/common.ts index c9f3f74..5fd50dd 100644 --- a/src/utils/common.ts +++ b/src/utils/common.ts @@ -1,11 +1,3 @@ -export function clamp(value: number, min: number, max: number): number { - value = Math.max(value, min); - value = Math.min(value, max); - return value; -} - -export type RequiredExcept = T & Required>; - type RequiredOptional = Exclude< { [K in keyof T]: T extends Record ? never : K; diff --git a/src/utils/url.ts b/src/utils/url.ts index 98f6dc4..33c21db 100644 --- a/src/utils/url.ts +++ b/src/utils/url.ts @@ -11,10 +11,10 @@ export function concatUrl(baseUrl: string, path: string) { } /** - * Checks whether passed url is absolute, i.e. it begins + * Checks whether passed URL is absolute, i.e. it begins * with http://, https:// or // * - * @param url - checked url + * @param url - checked URL */ export function isAbsoluteUrl(url: string): boolean { return /^https?:\/\//i.test(url);