@photo-sphere-viewer/markers-plugin
Version:
Photo Sphere Viewer plugin to display various markers/hotspots on the viewer.
1,398 lines (1,368 loc) • 82.2 kB
JavaScript
/*!
* Photo Sphere Viewer / Markers Plugin 5.14.0
* @copyright 2015-2025 Damien "Mistic" Sorel
* @licence MIT (https://opensource.org/licenses/MIT)
*/
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var index_exports = {};
__export(index_exports, {
MarkersPlugin: () => MarkersPlugin,
events: () => events_exports
});
module.exports = __toCommonJS(index_exports);
var import_core17 = require("@photo-sphere-viewer/core");
// src/events.ts
var events_exports = {};
__export(events_exports, {
EnterMarkerEvent: () => EnterMarkerEvent,
GotoMarkerDoneEvent: () => GotoMarkerDoneEvent,
HideMarkersEvent: () => HideMarkersEvent,
LeaveMarkerEvent: () => LeaveMarkerEvent,
MarkerVisibilityEvent: () => MarkerVisibilityEvent,
MarkersPluginEvent: () => MarkersPluginEvent,
RenderMarkersListEvent: () => RenderMarkersListEvent,
SelectMarkerEvent: () => SelectMarkerEvent,
SelectMarkerListEvent: () => SelectMarkerListEvent,
SetMarkersEvent: () => SetMarkersEvent,
ShowMarkersEvent: () => ShowMarkersEvent,
UnselectMarkerEvent: () => UnselectMarkerEvent
});
var import_core = require("@photo-sphere-viewer/core");
var MarkersPluginEvent = class extends import_core.TypedEvent {
};
var _MarkerVisibilityEvent = class _MarkerVisibilityEvent extends MarkersPluginEvent {
/** @internal */
constructor(marker, visible) {
super(_MarkerVisibilityEvent.type);
this.marker = marker;
this.visible = visible;
}
};
_MarkerVisibilityEvent.type = "marker-visibility";
var MarkerVisibilityEvent = _MarkerVisibilityEvent;
var _GotoMarkerDoneEvent = class _GotoMarkerDoneEvent extends MarkersPluginEvent {
/** @internal */
constructor(marker) {
super(_GotoMarkerDoneEvent.type);
this.marker = marker;
}
};
_GotoMarkerDoneEvent.type = "goto-marker-done";
var GotoMarkerDoneEvent = _GotoMarkerDoneEvent;
var _LeaveMarkerEvent = class _LeaveMarkerEvent extends MarkersPluginEvent {
/** @internal */
constructor(marker) {
super(_LeaveMarkerEvent.type);
this.marker = marker;
}
};
_LeaveMarkerEvent.type = "leave-marker";
var LeaveMarkerEvent = _LeaveMarkerEvent;
var _EnterMarkerEvent = class _EnterMarkerEvent extends MarkersPluginEvent {
/** @internal */
constructor(marker) {
super(_EnterMarkerEvent.type);
this.marker = marker;
}
};
_EnterMarkerEvent.type = "enter-marker";
var EnterMarkerEvent = _EnterMarkerEvent;
var _SelectMarkerEvent = class _SelectMarkerEvent extends MarkersPluginEvent {
/** @internal */
constructor(marker, doubleClick, rightClick) {
super(_SelectMarkerEvent.type);
this.marker = marker;
this.doubleClick = doubleClick;
this.rightClick = rightClick;
}
};
_SelectMarkerEvent.type = "select-marker";
var SelectMarkerEvent = _SelectMarkerEvent;
var _SelectMarkerListEvent = class _SelectMarkerListEvent extends MarkersPluginEvent {
/** @internal */
constructor(marker) {
super(_SelectMarkerListEvent.type);
this.marker = marker;
}
};
_SelectMarkerListEvent.type = "select-marker-list";
var SelectMarkerListEvent = _SelectMarkerListEvent;
var _UnselectMarkerEvent = class _UnselectMarkerEvent extends MarkersPluginEvent {
/** @internal */
constructor(marker) {
super(_UnselectMarkerEvent.type);
this.marker = marker;
}
};
_UnselectMarkerEvent.type = "unselect-marker";
var UnselectMarkerEvent = _UnselectMarkerEvent;
var _HideMarkersEvent = class _HideMarkersEvent extends MarkersPluginEvent {
/** @internal */
constructor() {
super(_HideMarkersEvent.type);
}
};
_HideMarkersEvent.type = "hide-markers";
var HideMarkersEvent = _HideMarkersEvent;
var _SetMarkersEvent = class _SetMarkersEvent extends MarkersPluginEvent {
/** @internal */
constructor(markers) {
super(_SetMarkersEvent.type);
this.markers = markers;
}
};
_SetMarkersEvent.type = "set-markers";
var SetMarkersEvent = _SetMarkersEvent;
var _ShowMarkersEvent = class _ShowMarkersEvent extends MarkersPluginEvent {
/** @internal */
constructor() {
super(_ShowMarkersEvent.type);
}
};
_ShowMarkersEvent.type = "show-markers";
var ShowMarkersEvent = _ShowMarkersEvent;
var _RenderMarkersListEvent = class _RenderMarkersListEvent extends MarkersPluginEvent {
/** @internal */
constructor(markers) {
super(_RenderMarkersListEvent.type);
this.markers = markers;
}
};
_RenderMarkersListEvent.type = "render-markers-list";
var RenderMarkersListEvent = _RenderMarkersListEvent;
// src/MarkersButton.ts
var import_core2 = require("@photo-sphere-viewer/core");
// src/icons/pin.svg
var pin_default = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="10 9 81 81"><path fill="currentColor" d="M50.5 90S22.9 51.9 22.9 36.6 35.2 9 50.5 9s27.6 12.4 27.6 27.6S50.5 90 50.5 90zm0-66.3c-6.1 0-11 4.9-11 11s4.9 11 11 11 11-4.9 11-11-4.9-11-11-11z"/><!--Created by Rohith M S from the Noun Project--></svg>\n';
// src/MarkersButton.ts
var MarkersButton = class extends import_core2.AbstractButton {
constructor(navbar) {
super(navbar, {
className: "psv-markers-button",
icon: pin_default,
hoverScale: true,
collapsable: true,
tabbable: true
});
this.plugin = this.viewer.getPlugin("markers");
if (this.plugin) {
this.plugin.addEventListener(ShowMarkersEvent.type, this);
this.plugin.addEventListener(HideMarkersEvent.type, this);
this.toggleActive(true);
}
}
destroy() {
if (this.plugin) {
this.plugin.removeEventListener(ShowMarkersEvent.type, this);
this.plugin.removeEventListener(HideMarkersEvent.type, this);
}
super.destroy();
}
isSupported() {
return !!this.plugin;
}
handleEvent(e) {
if (e instanceof ShowMarkersEvent) {
this.toggleActive(true);
} else if (e instanceof HideMarkersEvent) {
this.toggleActive(false);
}
}
onClick() {
this.plugin.toggleAllMarkers();
}
};
MarkersButton.id = "markers";
// src/MarkersListButton.ts
var import_core4 = require("@photo-sphere-viewer/core");
// src/constants.ts
var import_core3 = require("@photo-sphere-viewer/core");
// src/icons/pin-list.svg
var pin_list_default = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="9 9 81 81"><path fill="currentColor" d="M37.5 90S9.9 51.9 9.9 36.6 22.2 9 37.5 9s27.6 12.4 27.6 27.6S37.5 90 37.5 90zm0-66.3c-6.1 0-11 4.9-11 11s4.9 11 11 11 11-4.9 11-11-4.9-11-11-11zM86.7 55H70c-1.8 0-3.3-1.5-3.3-3.3s1.5-3.3 3.3-3.3h16.7c1.8 0 3.3 1.5 3.3 3.3S88.5 55 86.7 55zm0-25h-15a3.3 3.3 0 0 1-3.3-3.3c0-1.8 1.5-3.3 3.3-3.3h15c1.8 0 3.3 1.5 3.3 3.3 0 1.8-1.5 3.3-3.3 3.3zM56.5 73h30c1.8 0 3.3 1.5 3.3 3.3 0 1.8-1.5 3.3-3.3 3.3h-30a3.3 3.3 0 0 1-3.3-3.3 3.2 3.2 0 0 1 3.3-3.3z"/><!--Created by Rohith M S from the Noun Project--></svg>\n';
// src/constants.ts
var SVG_NS = "http://www.w3.org/2000/svg";
var MARKER_DATA = "psvMarker";
var MARKER_DATA_KEY = import_core3.utils.dasherize(MARKER_DATA);
var ID_PANEL_MARKER = "marker";
var ID_PANEL_MARKERS_LIST = "markersList";
var DEFAULT_HOVER_SCALE = {
amount: 2,
duration: 100,
easing: "linear"
};
var MARKERS_LIST_TEMPLATE = (markers, title) => `
<div class="psv-panel-menu psv-panel-menu--stripped">
<h1 class="psv-panel-menu-title">${pin_list_default} ${title}</h1>
<ul class="psv-panel-menu-list">
${markers.map((marker) => `
<li data-${MARKER_DATA_KEY}="${marker.id}" class="psv-panel-menu-item" tabindex="0">
${marker.type === "image" ? `<span class="psv-panel-menu-item-icon"><img src="${marker.definition}"/></span>` : ""}
<span class="psv-panel-menu-item-label">${marker.getListContent()}</span>
</li>
`).join("")}
</ul>
</div>
`;
// src/MarkersListButton.ts
var MarkersListButton = class extends import_core4.AbstractButton {
constructor(navbar) {
super(navbar, {
className: " psv-markers-list-button",
icon: pin_list_default,
hoverScale: true,
collapsable: true,
tabbable: true
});
this.plugin = this.viewer.getPlugin("markers");
if (this.plugin) {
this.viewer.addEventListener(import_core4.events.ShowPanelEvent.type, this);
this.viewer.addEventListener(import_core4.events.HidePanelEvent.type, this);
}
}
destroy() {
this.viewer.removeEventListener(import_core4.events.ShowPanelEvent.type, this);
this.viewer.removeEventListener(import_core4.events.HidePanelEvent.type, this);
super.destroy();
}
isSupported() {
return !!this.plugin;
}
handleEvent(e) {
if (e instanceof import_core4.events.ShowPanelEvent) {
this.toggleActive(e.panelId === ID_PANEL_MARKERS_LIST);
} else if (e instanceof import_core4.events.HidePanelEvent) {
this.toggleActive(false);
}
}
onClick() {
this.plugin.toggleMarkersList();
}
};
MarkersListButton.id = "markersList";
// src/MarkersPlugin.ts
var import_core16 = require("@photo-sphere-viewer/core");
// src/CSS3DContainer.ts
var import_core5 = require("@photo-sphere-viewer/core");
var import_three2 = require("three");
// ../../node_modules/three/examples/jsm/renderers/CSS3DRenderer.js
var import_three = require("three");
var _position = new import_three.Vector3();
var _quaternion = new import_three.Quaternion();
var _scale = new import_three.Vector3();
var CSS3DObject = class extends import_three.Object3D {
/**
* Constructs a new CSS3D object.
*
* @param {DOMElement} [element] - The DOM element.
*/
constructor(element = document.createElement("div")) {
super();
this.isCSS3DObject = true;
this.element = element;
this.element.style.position = "absolute";
this.element.style.pointerEvents = "auto";
this.element.style.userSelect = "none";
this.element.setAttribute("draggable", false);
this.addEventListener("removed", function() {
this.traverse(function(object) {
if (object.element instanceof object.element.ownerDocument.defaultView.Element && object.element.parentNode !== null) {
object.element.remove();
}
});
});
}
copy(source, recursive) {
super.copy(source, recursive);
this.element = source.element.cloneNode(true);
return this;
}
};
var _matrix = new import_three.Matrix4();
var _matrix2 = new import_three.Matrix4();
var CSS3DRenderer = class {
/**
* Constructs a new CSS3D renderer.
*
* @param {CSS3DRenderer~Parameters} [parameters] - The parameters.
*/
constructor(parameters = {}) {
const _this = this;
let _width, _height;
let _widthHalf, _heightHalf;
const cache = {
camera: { style: "" },
objects: /* @__PURE__ */ new WeakMap()
};
const domElement = parameters.element !== void 0 ? parameters.element : document.createElement("div");
domElement.style.overflow = "hidden";
this.domElement = domElement;
const viewElement = document.createElement("div");
viewElement.style.transformOrigin = "0 0";
viewElement.style.pointerEvents = "none";
domElement.appendChild(viewElement);
const cameraElement = document.createElement("div");
cameraElement.style.transformStyle = "preserve-3d";
viewElement.appendChild(cameraElement);
this.getSize = function() {
return {
width: _width,
height: _height
};
};
this.render = function(scene, camera) {
const fov = camera.projectionMatrix.elements[5] * _heightHalf;
if (camera.view && camera.view.enabled) {
viewElement.style.transform = `translate( ${-camera.view.offsetX * (_width / camera.view.width)}px, ${-camera.view.offsetY * (_height / camera.view.height)}px )`;
viewElement.style.transform += `scale( ${camera.view.fullWidth / camera.view.width}, ${camera.view.fullHeight / camera.view.height} )`;
} else {
viewElement.style.transform = "";
}
if (scene.matrixWorldAutoUpdate === true) scene.updateMatrixWorld();
if (camera.parent === null && camera.matrixWorldAutoUpdate === true) camera.updateMatrixWorld();
let tx, ty;
if (camera.isOrthographicCamera) {
tx = -(camera.right + camera.left) / 2;
ty = (camera.top + camera.bottom) / 2;
}
const scaleByViewOffset = camera.view && camera.view.enabled ? camera.view.height / camera.view.fullHeight : 1;
const cameraCSSMatrix = camera.isOrthographicCamera ? `scale( ${scaleByViewOffset} )scale(` + fov + ")translate(" + epsilon(tx) + "px," + epsilon(ty) + "px)" + getCameraCSSMatrix(camera.matrixWorldInverse) : `scale( ${scaleByViewOffset} )translateZ(` + fov + "px)" + getCameraCSSMatrix(camera.matrixWorldInverse);
const perspective = camera.isPerspectiveCamera ? "perspective(" + fov + "px) " : "";
const style = perspective + cameraCSSMatrix + "translate(" + _widthHalf + "px," + _heightHalf + "px)";
if (cache.camera.style !== style) {
cameraElement.style.transform = style;
cache.camera.style = style;
}
renderObject(scene, scene, camera, cameraCSSMatrix);
};
this.setSize = function(width, height) {
_width = width;
_height = height;
_widthHalf = _width / 2;
_heightHalf = _height / 2;
domElement.style.width = width + "px";
domElement.style.height = height + "px";
viewElement.style.width = width + "px";
viewElement.style.height = height + "px";
cameraElement.style.width = width + "px";
cameraElement.style.height = height + "px";
};
function epsilon(value) {
return Math.abs(value) < 1e-10 ? 0 : value;
}
function getCameraCSSMatrix(matrix) {
const elements = matrix.elements;
return "matrix3d(" + epsilon(elements[0]) + "," + epsilon(-elements[1]) + "," + epsilon(elements[2]) + "," + epsilon(elements[3]) + "," + epsilon(elements[4]) + "," + epsilon(-elements[5]) + "," + epsilon(elements[6]) + "," + epsilon(elements[7]) + "," + epsilon(elements[8]) + "," + epsilon(-elements[9]) + "," + epsilon(elements[10]) + "," + epsilon(elements[11]) + "," + epsilon(elements[12]) + "," + epsilon(-elements[13]) + "," + epsilon(elements[14]) + "," + epsilon(elements[15]) + ")";
}
function getObjectCSSMatrix(matrix) {
const elements = matrix.elements;
const matrix3d = "matrix3d(" + epsilon(elements[0]) + "," + epsilon(elements[1]) + "," + epsilon(elements[2]) + "," + epsilon(elements[3]) + "," + epsilon(-elements[4]) + "," + epsilon(-elements[5]) + "," + epsilon(-elements[6]) + "," + epsilon(-elements[7]) + "," + epsilon(elements[8]) + "," + epsilon(elements[9]) + "," + epsilon(elements[10]) + "," + epsilon(elements[11]) + "," + epsilon(elements[12]) + "," + epsilon(elements[13]) + "," + epsilon(elements[14]) + "," + epsilon(elements[15]) + ")";
return "translate(-50%,-50%)" + matrix3d;
}
function hideObject(object) {
if (object.isCSS3DObject) object.element.style.display = "none";
for (let i = 0, l = object.children.length; i < l; i++) {
hideObject(object.children[i]);
}
}
function renderObject(object, scene, camera, cameraCSSMatrix) {
if (object.visible === false) {
hideObject(object);
return;
}
if (object.isCSS3DObject) {
const visible = object.layers.test(camera.layers) === true;
const element = object.element;
element.style.display = visible === true ? "" : "none";
if (visible === true) {
object.onBeforeRender(_this, scene, camera);
let style;
if (object.isCSS3DSprite) {
_matrix.copy(camera.matrixWorldInverse);
_matrix.transpose();
if (object.rotation2D !== 0) _matrix.multiply(_matrix2.makeRotationZ(object.rotation2D));
object.matrixWorld.decompose(_position, _quaternion, _scale);
_matrix.setPosition(_position);
_matrix.scale(_scale);
_matrix.elements[3] = 0;
_matrix.elements[7] = 0;
_matrix.elements[11] = 0;
_matrix.elements[15] = 1;
style = getObjectCSSMatrix(_matrix);
} else {
style = getObjectCSSMatrix(object.matrixWorld);
}
const cachedObject = cache.objects.get(object);
if (cachedObject === void 0 || cachedObject.style !== style) {
element.style.transform = style;
const objectData = { style };
cache.objects.set(object, objectData);
}
if (element.parentNode !== cameraElement) {
cameraElement.appendChild(element);
}
object.onAfterRender(_this, scene, camera);
}
}
for (let i = 0, l = object.children.length; i < l; i++) {
renderObject(object.children[i], scene, camera, cameraCSSMatrix);
}
}
}
};
// src/CSS3DContainer.ts
var CSS3DContainer = class {
constructor(viewer) {
this.viewer = viewer;
this.element = document.createElement("div");
this.element.className = "psv-markers-css3d-container";
this.renderer = new CSS3DRenderer({ element: this.element });
this.scene = new import_three2.Scene();
this.intersectionObserver = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
const marker = entry.target[MARKER_DATA];
if (marker.config.visible) {
marker.viewportIntersection = entry.isIntersecting;
}
});
}, {
root: this.element
});
viewer.addEventListener(import_core5.events.ReadyEvent.type, this, { once: true });
viewer.addEventListener(import_core5.events.SizeUpdatedEvent.type, this);
viewer.addEventListener(import_core5.events.RenderEvent.type, this);
}
handleEvent(e) {
switch (e.type) {
case import_core5.events.ReadyEvent.type:
case import_core5.events.SizeUpdatedEvent.type:
this.updateSize();
break;
case import_core5.events.RenderEvent.type:
this.render();
break;
}
}
destroy() {
this.viewer.removeEventListener(import_core5.events.ReadyEvent.type, this);
this.viewer.removeEventListener(import_core5.events.SizeUpdatedEvent.type, this);
this.viewer.removeEventListener(import_core5.events.RenderEvent.type, this);
this.intersectionObserver.disconnect();
}
updateSize() {
const size = this.viewer.getSize();
this.renderer.setSize(size.width, size.height);
}
render() {
this.renderer.render(this.scene, this.viewer.renderer.camera);
}
addObject(marker) {
this.scene.add(marker.threeElement);
this.intersectionObserver.observe(marker.domElement);
}
removeObject(marker) {
this.scene.remove(marker.threeElement);
this.intersectionObserver.unobserve(marker.domElement);
}
};
// src/MarkerType.ts
var import_core6 = require("@photo-sphere-viewer/core");
var MarkerType = /* @__PURE__ */ ((MarkerType3) => {
MarkerType3["image"] = "image";
MarkerType3["html"] = "html";
MarkerType3["element"] = "element";
MarkerType3["imageLayer"] = "imageLayer";
MarkerType3["videoLayer"] = "videoLayer";
MarkerType3["elementLayer"] = "elementLayer";
MarkerType3["polygon"] = "polygon";
MarkerType3["polygonPixels"] = "polygonPixels";
MarkerType3["polyline"] = "polyline";
MarkerType3["polylinePixels"] = "polylinePixels";
MarkerType3["square"] = "square";
MarkerType3["rect"] = "rect";
MarkerType3["circle"] = "circle";
MarkerType3["ellipse"] = "ellipse";
MarkerType3["path"] = "path";
return MarkerType3;
})(MarkerType || {});
function getMarkerType(config, allowNone = false) {
const found = [];
Object.keys(MarkerType).forEach((type) => {
if (config[type]) {
found.push(type);
}
});
if (found.length === 0 && !allowNone) {
throw new import_core6.PSVError(`missing marker content, either ${Object.keys(MarkerType).join(", ")}`);
} else if (found.length > 1) {
throw new import_core6.PSVError(`multiple marker content, either ${Object.keys(MarkerType).join(", ")}`);
}
return found[0];
}
// src/markers/AbstractStandardMarker.ts
var import_core9 = require("@photo-sphere-viewer/core");
var import_three3 = require("three");
// src/markers/AbstractDomMarker.ts
var import_core8 = require("@photo-sphere-viewer/core");
// src/markers/Marker.ts
var import_core7 = require("@photo-sphere-viewer/core");
var Marker = class {
constructor(viewer, plugin, config) {
this.viewer = viewer;
this.plugin = plugin;
/** @internal */
this.state = {
anchor: null,
visible: false,
staticTooltip: false,
position: null,
position2D: null,
positions3D: null,
size: null
};
if (!config.id) {
throw new import_core7.PSVError("missing marker id");
}
this.type = getMarkerType(config);
this.createElement();
this.update(config);
}
get id() {
return this.config.id;
}
get data() {
return this.config.data;
}
get domElement() {
return null;
}
get threeElement() {
return null;
}
get video() {
return null;
}
/**
* @internal
*/
destroy() {
delete this.viewer;
delete this.plugin;
delete this.element;
this.hideTooltip();
}
/**
* Checks if it is a 3D marker (imageLayer, videoLayer)
*/
is3d() {
return false;
}
/**
* Checks if it is a normal marker (image, html, element)
*/
isNormal() {
return false;
}
/**
* Checks if it is a polygon/polyline marker
*/
isPoly() {
return false;
}
/**
* Checks if it is an SVG marker
*/
isSvg() {
return false;
}
/**
* Checks if it is an CSS3D marker
*/
isCss3d() {
return false;
}
/**
* Updates the marker with new properties
* @throws {@link PSVError} if the configuration is invalid
* @internal
*/
update(config) {
const newType = getMarkerType(config, true);
if (newType !== void 0 && newType !== this.type) {
throw new import_core7.PSVError(`cannot change marker ${config.id} type`);
}
this.config = import_core7.utils.deepmerge(this.config, config);
if (typeof this.config.tooltip === "string") {
this.config.tooltip = { content: this.config.tooltip };
}
if (this.config.tooltip && !this.config.tooltip.trigger) {
this.config.tooltip.trigger = "hover";
}
if (import_core7.utils.isNil(this.config.visible)) {
this.config.visible = true;
}
if (import_core7.utils.isNil(this.config.zIndex)) {
this.config.zIndex = 1;
}
if (import_core7.utils.isNil(this.config.opacity)) {
this.config.opacity = 1;
}
if (this.config.rotation) {
const rot = this.config.rotation;
if (typeof rot === "object") {
this.config.rotation = {
yaw: rot.yaw ? import_core7.utils.parseAngle(rot.yaw, true, false) : 0,
pitch: rot.pitch ? import_core7.utils.parseAngle(rot.pitch, true, false) : 0,
roll: rot.roll ? import_core7.utils.parseAngle(rot.roll, true, false) : 0
};
} else {
this.config.rotation = {
yaw: 0,
pitch: 0,
roll: import_core7.utils.parseAngle(rot, true, false)
};
}
} else {
this.config.rotation = { yaw: 0, pitch: 0, roll: 0 };
}
this.state.anchor = import_core7.utils.parsePoint(this.config.anchor);
}
/**
* Returns the markers list content for the marker, it can be either :
* - the `listContent`
* - the `tooltip`
* - the `html`
* - the `id`
* @internal
*/
getListContent() {
if (this.config.listContent) {
return this.config.listContent;
} else if (this.config.tooltip?.content) {
return this.config.tooltip.content;
} else if (this.config.html) {
return this.config.html;
} else {
return this.id;
}
}
/**
* Display the tooltip of this marker
* @internal
*/
showTooltip(clientX, clientY, forceUpdate = false) {
if (this.state.visible && this.config.tooltip?.content && this.state.position2D) {
const config = {
...this.config.tooltip,
style: {
// prevents conflicts with tooltip tracking
pointerEvents: this.state.staticTooltip ? "auto" : "none"
},
data: this,
top: 0,
left: 0
};
if (this.isPoly() || this.is3d() || this.isCss3d()) {
if (clientX || clientY) {
const viewerPos = import_core7.utils.getPosition(this.viewer.container);
config.top = clientY - viewerPos.y + 10;
config.left = clientX - viewerPos.x;
config.box = {
// separate the tooltip from the cursor
width: 20,
height: 20
};
} else {
config.top = this.state.position2D.y;
config.left = this.state.position2D.x;
}
} else {
const position = this.viewer.dataHelper.vector3ToViewerCoords(this.state.positions3D[0]);
let width = this.state.size.width;
let height = this.state.size.height;
if (this.config.hoverScale && !this.state.staticTooltip) {
width *= this.config.hoverScale.amount;
height *= this.config.hoverScale.amount;
}
config.top = position.y - height * this.state.anchor.y + height / 2;
config.left = position.x - width * this.state.anchor.x + width / 2;
config.box = { width, height };
}
if (this.tooltip) {
if (forceUpdate) {
this.tooltip.update(this.config.tooltip.content, config);
} else {
this.tooltip.move(config);
}
} else {
this.tooltip = this.viewer.createTooltip(config);
}
}
}
/**
* Hides the tooltip of this marker
* @internal
*/
hideTooltip() {
if (this.tooltip) {
this.tooltip.hide();
this.tooltip = null;
}
}
};
// src/markers/AbstractDomMarker.ts
var AbstractDomMarker = class extends Marker {
get domElement() {
return this.element;
}
constructor(viewer, plugin, config) {
super(viewer, plugin, config);
}
afterCreateElement() {
this.element[MARKER_DATA] = this;
}
destroy() {
delete this.element[MARKER_DATA];
super.destroy();
}
update(config) {
super.update(config);
const element = this.domElement;
element.id = `psv-marker-${this.config.id}`;
element.setAttribute("class", "psv-marker");
if (this.state.visible) {
element.classList.add("psv-marker--visible");
}
if (this.config.tooltip) {
element.classList.add("psv-marker--has-tooltip");
}
if (this.config.content) {
element.classList.add("psv-marker--has-content");
}
if (this.config.className) {
import_core8.utils.addClasses(element, this.config.className);
}
element.style.opacity = `${this.config.opacity}`;
element.style.zIndex = `${30 + this.config.zIndex}`;
if (this.config.style) {
Object.assign(element.style, this.config.style);
}
}
};
// src/markers/AbstractStandardMarker.ts
var AbstractStandardMarker = class extends AbstractDomMarker {
constructor(viewer, plugin, config) {
super(viewer, plugin, config);
}
afterCreateElement() {
super.afterCreateElement();
this.domElement.addEventListener("transitionend", () => {
this.domElement.style.transition = "";
});
}
render({
viewerPosition,
zoomLevel,
hoveringMarker
}) {
this.__updateSize();
const position = this.viewer.dataHelper.vector3ToViewerCoords(this.state.positions3D[0]);
position.x -= this.state.size.width * this.state.anchor.x;
position.y -= this.state.size.height * this.state.anchor.y;
const isVisible = this.state.positions3D[0].dot(this.viewer.state.direction) > 0 && position.x + this.state.size.width >= 0 && position.x - this.state.size.width <= this.viewer.state.size.width && position.y + this.state.size.height >= 0 && position.y - this.state.size.height <= this.viewer.state.size.height;
if (isVisible) {
this.domElement.style.translate = `${position.x}px ${position.y}px 0px`;
this.applyScale({
zoomLevel,
viewerPosition,
mouseover: this === hoveringMarker
});
return position;
} else {
return null;
}
}
update(config) {
super.update(config);
if (!import_core9.utils.isExtendedPosition(this.config.position)) {
throw new import_core9.PSVError(`missing marker ${this.id} position`);
}
try {
this.state.position = this.viewer.dataHelper.cleanPosition(this.config.position);
} catch (e) {
throw new import_core9.PSVError(`invalid marker ${this.id} position`, e);
}
this.state.positions3D = [this.viewer.dataHelper.sphericalCoordsToVector3(this.state.position)];
const element = this.domElement;
element.classList.add("psv-marker--normal");
if (this.config.scale && Array.isArray(this.config.scale)) {
this.config.scale = { zoom: this.config.scale };
}
if (typeof this.config.hoverScale === "boolean") {
this.config.hoverScale = this.config.hoverScale ? this.plugin.config.defaultHoverScale || DEFAULT_HOVER_SCALE : null;
} else if (typeof this.config.hoverScale === "number") {
this.config.hoverScale = { amount: this.config.hoverScale };
} else if (!this.config.hoverScale) {
this.config.hoverScale = this.plugin.config.defaultHoverScale;
}
if (this.config.hoverScale) {
this.config.hoverScale = {
...this.plugin.config.defaultHoverScale,
...this.config.hoverScale
};
}
element.style.rotate = this.config.rotation.roll !== 0 ? import_three3.MathUtils.radToDeg(this.config.rotation.roll) + "deg" : null;
element.style.transformOrigin = `${this.state.anchor.x * 100}% ${this.state.anchor.y * 100}%`;
}
/**
* Computes the real size of a marker
* @description This is done by removing all it's transformations (if any) and making it visible
* before querying its bounding rect
*/
__updateSize() {
if (!this.needsUpdateSize) {
return;
}
const element = this.domElement;
const makeTransparent = !this.state.visible || !this.state.size;
if (makeTransparent) {
element.classList.add("psv-marker--transparent");
}
if (this.isSvg()) {
const rect = element.firstElementChild.getBoundingClientRect();
this.state.size = {
width: rect.width,
height: rect.height
};
} else {
this.state.size = {
width: element.offsetWidth,
height: element.offsetHeight
};
}
if (makeTransparent) {
element.classList.remove("psv-marker--transparent");
}
if (this.isSvg()) {
element.style.width = this.state.size.width + "px";
element.style.height = this.state.size.height + "px";
}
if (this.type !== "element" /* element */) {
this.needsUpdateSize = false;
}
}
/**
* Computes and applies the scale to the marker
*/
applyScale({
zoomLevel,
viewerPosition,
mouseover
}) {
if (mouseover !== null && this.config.hoverScale) {
this.domElement.style.transition = `scale ${this.config.hoverScale.duration}ms ${this.config.hoverScale.easing}`;
}
let scale = 1;
if (typeof this.config.scale === "function") {
scale = this.config.scale(zoomLevel, viewerPosition);
} else if (this.config.scale) {
if (Array.isArray(this.config.scale.zoom)) {
const [min, max] = this.config.scale.zoom;
scale *= min + (max - min) * import_core9.CONSTANTS.EASINGS.inQuad(zoomLevel / 100);
}
if (Array.isArray(this.config.scale.yaw)) {
const [min, max] = this.config.scale.yaw;
const halfFov = import_three3.MathUtils.degToRad(this.viewer.state.hFov) / 2;
const arc = Math.abs(import_core9.utils.getShortestArc(this.state.position.yaw, viewerPosition.yaw));
scale *= max + (min - max) * import_core9.CONSTANTS.EASINGS.outQuad(Math.max(0, (halfFov - arc) / halfFov));
}
}
if (mouseover && this.config.hoverScale) {
scale *= this.config.hoverScale.amount;
}
this.domElement.style.scale = `${scale}`;
}
};
// src/markers/Marker3D.ts
var import_core11 = require("@photo-sphere-viewer/core");
var import_three6 = require("three");
// ../shared/ChromaKeyMaterial.ts
var import_three4 = require("three");
// ../shared/shaders/chromaKey.fragment.glsl
var chromaKey_fragment_default = "// https://www.8thwall.com/playground/chromakey-threejs\n\nuniform sampler2D map;\nuniform float alpha;\nuniform bool keying;\nuniform vec3 color;\nuniform float similarity;\nuniform float smoothness;\nuniform float spill;\n\nvarying vec2 vUv;\n\nvec2 RGBtoUV(vec3 rgb) {\n return vec2(\n rgb.r * -0.169 + rgb.g * -0.331 + rgb.b * 0.5 + 0.5,\n rgb.r * 0.5 + rgb.g * -0.419 + rgb.b * -0.081 + 0.5\n );\n}\n\nvoid main(void) {\n gl_FragColor = texture2D(map, vUv);\n\n if (keying) {\n float chromaDist = distance(RGBtoUV(gl_FragColor.rgb), RGBtoUV(color));\n\n float baseMask = chromaDist - similarity;\n float fullMask = pow(clamp(baseMask / smoothness, 0., 1.), 1.5);\n gl_FragColor.a *= fullMask * alpha;\n\n float spillVal = pow(clamp(baseMask / spill, 0., 1.), 1.5);\n float desat = clamp(gl_FragColor.r * 0.2126 + gl_FragColor.g * 0.7152 + gl_FragColor.b * 0.0722, 0., 1.);\n gl_FragColor.rgb = mix(vec3(desat, desat, desat), gl_FragColor.rgb, spillVal);\n } else {\n gl_FragColor.a *= alpha;\n }\n}\n";
// ../shared/shaders/chromaKey.vertex.glsl
var chromaKey_vertex_default = "varying vec2 vUv;\nuniform vec2 repeat;\nuniform vec2 offset;\n\nvoid main() {\n vUv = uv * repeat + offset;\n gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n}\n";
// ../shared/ChromaKeyMaterial.ts
var ChromaKeyMaterial = class extends import_three4.ShaderMaterial {
get map() {
return this.uniforms.map.value;
}
set map(map) {
this.uniforms.map.value = map;
}
set alpha(alpha) {
this.uniforms.alpha.value = alpha;
}
get offset() {
return this.uniforms.offset.value;
}
get repeat() {
return this.uniforms.repeat.value;
}
set chromaKey(chromaKey) {
this.uniforms.keying.value = chromaKey?.enabled === true;
if (chromaKey?.enabled) {
if (typeof chromaKey.color === "object" && "r" in chromaKey.color) {
this.uniforms.color.value.set(
chromaKey.color.r / 255,
chromaKey.color.g / 255,
chromaKey.color.b / 255
);
} else {
this.uniforms.color.value.set(chromaKey.color ?? 65280);
}
this.uniforms.similarity.value = chromaKey.similarity ?? 0.2;
this.uniforms.smoothness.value = chromaKey.smoothness ?? 0.2;
}
}
constructor(params) {
super({
transparent: true,
depthTest: false,
depthWrite: false,
uniforms: {
map: { value: params?.map },
repeat: { value: new import_three4.Vector2(1, 1) },
offset: { value: new import_three4.Vector2(0, 0) },
alpha: { value: params?.alpha ?? 1 },
keying: { value: false },
color: { value: new import_three4.Color(65280) },
similarity: { value: 0.2 },
smoothness: { value: 0.2 },
spill: { value: 0.1 }
},
vertexShader: chromaKey_vertex_default,
fragmentShader: chromaKey_fragment_default
});
this.chromaKey = params?.chromaKey;
}
};
// ../shared/video-utils.ts
function createVideo({
src,
withCredentials,
muted,
autoplay
}) {
const video = document.createElement("video");
video.crossOrigin = withCredentials ? "use-credentials" : "anonymous";
video.loop = true;
video.playsInline = true;
video.autoplay = autoplay;
video.muted = muted;
video.preload = "metadata";
if (src instanceof MediaStream) {
video.srcObject = src;
} else {
video.src = src;
}
return video;
}
// src/utils.ts
var import_core10 = require("@photo-sphere-viewer/core");
var import_three5 = require("three");
function greatArcIntermediaryPoint(p1, p2, f) {
const [\u03BB1, \u03C61] = p1;
const [\u03BB2, \u03C62] = p2;
const r = import_core10.utils.greatArcDistance(p1, p2);
const a = Math.sin((1 - f) * r) / Math.sin(r);
const b = Math.sin(f * r) / Math.sin(r);
const x = a * Math.cos(\u03C61) * Math.cos(\u03BB1) + b * Math.cos(\u03C62) * Math.cos(\u03BB2);
const y = a * Math.cos(\u03C61) * Math.sin(\u03BB1) + b * Math.cos(\u03C62) * Math.sin(\u03BB2);
const z = a * Math.sin(\u03C61) + b * Math.sin(\u03C62);
return [Math.atan2(y, x), Math.atan2(z, Math.sqrt(x * x + y * y))];
}
function getPolygonCoherentPoints(points) {
const workPoints = [points[0]];
let k = 0;
for (let i = 1; i < points.length; i++) {
const d = points[i - 1][0] - points[i][0];
if (d > Math.PI) {
k += 1;
} else if (d < -Math.PI) {
k -= 1;
}
workPoints.push([points[i][0] + k * 2 * Math.PI, points[i][1]]);
}
return workPoints;
}
function getPolygonCenter(polygon) {
return polygon.reduce((sum, point) => sum.add(point), new import_three5.Vector3()).normalize();
}
function getPolylineCenter(polyline) {
const points = getPolygonCoherentPoints(polyline);
let length = 0;
const lengths = [];
for (let i = 0; i < points.length - 1; i++) {
const l = import_core10.utils.greatArcDistance(points[i], points[i + 1]) * import_core10.CONSTANTS.SPHERE_RADIUS;
lengths.push(l);
length += l;
}
let consumed = 0;
for (let j = 0; j < points.length - 1; j++) {
if (consumed + lengths[j] > length / 2) {
const r = (length / 2 - consumed) / lengths[j];
return greatArcIntermediaryPoint(points[j], points[j + 1], r);
}
consumed += lengths[j];
}
return points[Math.round(points.length / 2)];
}
var C = new import_three5.Vector3();
var N = new import_three5.Vector3();
var V = new import_three5.Vector3();
var X = new import_three5.Vector3();
var Y = new import_three5.Vector3();
var A = new import_three5.Vector3();
function getGreatCircleIntersection(P1, P2, direction) {
C.copy(direction).normalize();
N.crossVectors(P1, P2).normalize();
V.crossVectors(N, P1).normalize();
X.copy(P1).multiplyScalar(-C.dot(V));
Y.copy(V).multiplyScalar(C.dot(P1));
const H = new import_three5.Vector3().addVectors(X, Y).normalize();
A.crossVectors(H, C);
return H.applyAxisAngle(A, 0.01).multiplyScalar(import_core10.CONSTANTS.SPHERE_RADIUS);
}
// src/markers/Marker3D.ts
var Marker3D = class extends Marker {
get threeElement() {
return this.element;
}
get threeMesh() {
return this.threeElement.children[0];
}
get video() {
if (this.type === "videoLayer" /* videoLayer */) {
return this.threeMesh.material.map.image;
} else {
return null;
}
}
constructor(viewer, plugin, config) {
super(viewer, plugin, config);
}
is3d() {
return true;
}
createElement() {
const material = new ChromaKeyMaterial({ alpha: 0 });
const geometry = new import_three6.PlaneGeometry(1, 1);
const mesh = new import_three6.Mesh(geometry, material);
mesh.userData = { [MARKER_DATA]: this };
Object.defineProperty(mesh, "visible", {
enumerable: true,
get: function() {
return this.userData[MARKER_DATA].config.visible;
},
set: function(visible) {
this.userData[MARKER_DATA].config.visible = visible;
}
});
this.element = new import_three6.Group().add(mesh);
if (this.type === "videoLayer" /* videoLayer */) {
this.viewer.needsContinuousUpdate(true);
}
}
destroy() {
delete this.threeMesh.userData[MARKER_DATA];
if (this.type === "videoLayer" /* videoLayer */) {
this.video.pause();
this.viewer.needsContinuousUpdate(false);
}
super.destroy();
}
render() {
if (this.viewer.renderer.isObjectVisible(this.threeMesh)) {
return this.viewer.dataHelper.sphericalCoordsToViewerCoords(this.state.position);
} else {
return null;
}
}
update(config) {
super.update(config);
const mesh = this.threeMesh;
const group = mesh.parent;
const material = mesh.material;
if (import_core11.utils.isExtendedPosition(this.config.position)) {
try {
this.state.position = this.viewer.dataHelper.cleanPosition(this.config.position);
} catch (e) {
throw new import_core11.PSVError(`invalid marker ${this.id} position`, e);
}
if (!this.config.size) {
throw new import_core11.PSVError(`missing marker ${this.id} size`);
}
this.state.size = this.config.size;
mesh.scale.set(this.config.size.width / 100, this.config.size.height / 100, 1);
mesh.position.set(mesh.scale.x * (0.5 - this.state.anchor.x), mesh.scale.y * (this.state.anchor.y - 0.5), 0);
mesh.rotation.set(0, 0, 0);
this.viewer.dataHelper.sphericalCoordsToVector3(this.state.position, group.position);
group.lookAt(0, group.position.y, 0);
mesh.rotateY(-this.config.rotation.yaw);
mesh.rotateX(-this.config.rotation.pitch);
mesh.rotateZ(-this.config.rotation.roll);
const p = mesh.geometry.getAttribute("position");
this.state.positions3D = [0, 1, 3, 2].map((i) => {
const v3 = new import_three6.Vector3();
v3.fromBufferAttribute(p, i);
return mesh.localToWorld(v3);
});
} else {
if (this.config.position?.length !== 4) {
throw new import_core11.PSVError(`missing marker ${this.id} position`);
}
let positions;
try {
positions = this.config.position.map((p2) => this.viewer.dataHelper.cleanPosition(p2));
} catch (e) {
throw new import_core11.PSVError(`invalid marker ${this.id} position`, e);
}
const positions3D = positions.map((p2) => this.viewer.dataHelper.sphericalCoordsToVector3(p2));
const centroid = getPolygonCenter(positions3D);
this.state.position = this.viewer.dataHelper.vector3ToSphericalCoords(centroid);
this.state.positions3D = positions3D;
const p = mesh.geometry.getAttribute("position");
[
positions3D[0],
positions3D[1],
positions3D[3],
// not a mistake!
positions3D[2]
].forEach((v, i) => {
p.setX(i, v.x);
p.setY(i, v.y);
p.setZ(i, v.z);
});
p.needsUpdate = true;
this.__setTextureWrap(material);
}
switch (this.type) {
case "videoLayer" /* videoLayer */:
if (this.definition !== this.config.videoLayer) {
material.map?.dispose();
const video = createVideo({
src: this.config.videoLayer,
withCredentials: this.viewer.config.withCredentials(this.config.videoLayer),
muted: true,
autoplay: this.config.autoplay ?? true
});
const texture = new import_three6.VideoTexture(video);
material.map = texture;
material.alpha = 0;
video.addEventListener("loadedmetadata", () => {
if (!this.viewer) {
return;
}
material.alpha = this.config.opacity;
if (!import_core11.utils.isExtendedPosition(this.config.position)) {
mesh.material.userData[MARKER_DATA] = { width: video.videoWidth, height: video.videoHeight };
this.__setTextureWrap(material);
}
}, { once: true });
if (video.autoplay) {
video.play();
}
this.definition = this.config.videoLayer;
} else {
material.alpha = this.config.opacity;
}
break;
case "imageLayer" /* imageLayer */:
if (this.definition !== this.config.imageLayer) {
material.map?.dispose();
const texture = new import_three6.Texture();
material.map = texture;
material.alpha = 0;
this.viewer.textureLoader.loadImage(this.config.imageLayer).then((image) => {
if (!this.viewer) {
return;
}
if (!import_core11.utils.isExtendedPosition(this.config.position)) {
mesh.material.userData[MARKER_DATA] = { width: image.width, height: image.height };
this.__setTextureWrap(material);
}
texture.image = image;
texture.anisotropy = 4;
texture.needsUpdate = true;
material.alpha = this.config.opacity;
this.viewer.needsUpdate();
});
this.definition = this.config.imageLayer;
} else {
material.alpha = this.config.opacity;
}
break;
}
material.chromaKey = this.config.chromaKey;
mesh.renderOrder = 1e3 + this.config.zIndex;
mesh.geometry.boundingBox = null;
}
/**
* For layers positionned by corners, applies offset to the texture in order to keep its proportions
*/
__setTextureWrap(material) {
const imageSize = material.userData[MARKER_DATA];
if (!imageSize || !imageSize.height || !imageSize.width) {
material.repeat.set(1, 1);
material.offset.set(0, 0);
return;
}
const positions = this.config.position.map((p) => {
return this.viewer.dataHelper.cleanPosition(p);
});
const w1 = import_core11.utils.greatArcDistance(
[positions[0].yaw, positions[0].pitch],
[positions[1].yaw, positions[1].pitch]
);
const w2 = import_core11.utils.greatArcDistance(
[positions[3].yaw, positions[3].pitch],
[positions[2].yaw, positions[2].pitch]
);
const h1 = import_core11.utils.greatArcDistance(
[positions[1].yaw, positions[1].pitch],
[positions[2].yaw, positions[2].pitch]
);
const h2 = import_core11.utils.greatArcDistance(
[positions[0].yaw, positions[0].pitch],
[positions[3].yaw, positions[3].pitch]
);
const layerRatio = (w1 + w2) / (h1 + h2);
const imageRatio = imageSize.width / imageSize.height;
let hMargin = 0;
let vMargin = 0;
if (layerRatio < imageRatio) {
hMargin = imageRatio - layerRatio;
} else {
vMargin = 1 / imageRatio - 1 / layerRatio;
}
material.repeat.set(1 - hMargin, 1 - vMargin);
material.offset.set(hMargin / 2, vMargin / 2);
}
};
// src/markers/MarkerCSS3D.ts
var import_core12 = require("@photo-sphere-viewer/core");
var MarkerCSS3D = class extends AbstractDomMarker {
constructor(viewer, plugin, config) {
super(viewer, plugin, config);
/**
* @internal
*/
this.viewportIntersection = false;
}
get threeElement() {
return this.object;
}
isCss3d() {
return true;
}
createElement() {
this.element = document.createElement("div");
this.object = new CSS3DObject(this.element);
this.object.userData = { [MARKER_DATA]: this };
Object.defineProperty(this.object, "visible", {
enumerable: true,
get: function() {
return this.userData[MARKER_DATA].config.visible;
},
set: function(visible) {
this.userData[MARKER_DATA].config.visible = visible;
}
});
this.afterCreateElement();
}
destroy() {
delete this.object.userData[MARKER_DATA];
delete this.object;
super.destroy();
}
render({
viewerPosition,
zoomLevel
}) {
const element = this.domElement;
this.state.size = {
width: element.offsetWidth,
height: element.offsetHeight
};
const isVisible = this.state.positions3D[0].dot(this.viewer.state.direction) > 0 && this.viewportIntersection;
if (isVisible) {
const position = this.viewer.dataHelper.sphericalCoordsToViewerCoords(this.state.position);
this.config.elementLayer.updateMarker?.({
marker: this,
position,
viewerPosition,
zoomLevel,
viewerSize: this.viewer.state.size
});
return position;
} else {
return null;
}
}
update(config) {
super.update(config);
if (!import_core12.utils.isExtendedPosition(this.config.position)) {
throw new import_core12.PSVError(`missing marker ${this.id} position`);
}
try {
this.state.position = this.viewer.dataHelper.cleanPosition(this.config.position);
} catch (e) {
throw new import_core12.PSVError(`invalid marker ${this.id} position`, e);
}
this.state.positions3D = [this.viewer.dataHelper.sphericalCoordsToVector3(this.state.position)];
const object = this.threeElement;
const element = this.domElement;
element.classList.add("psv-marker--css3d");
element.childNodes.forEach((n) => n.remove());
element.appendChild(this.config.elementLayer);
this.config.elementLayer.style.display = "block";
object.position.copy(this.state.positions3D[0]).multiplyScalar(100);
object.lookAt(0, this.state.positions3D[0].y * 100, 0);
object.rotateY(-this.config.rotation.yaw);
object.rotateX(-this.config.rotation.pitch);
object.rotateZ(-this.config.rotation.roll);
}
};
// src/markers/MarkerNormal.ts
var import_core13 = require("@photo-sphere-viewer/core");
var MarkerNormal = class extends AbstractStandardMarker {
constructor(viewer, plugin, config) {
super(viewer, plugin, config);
}
isNormal() {
return true;
}
createElement() {
this.element = document.createElement("div");
this.afterCreateElement();
}
render(params) {
const position = s