import {Entity} from "../Entity";
import {LonLat} from "../../LonLat";
import {Vec3} from "../../math/Vec3";
import type {NumberArray3} from "../../math/Vec3";
import type {NumberArray2} from "../../math/Vec2";
import type {NumberArray4} from "../../math/Vec4";
import type {HTMLImageElementExt} from "../../utils/ImagesCacheManager";
import {htmlColorToRgba} from "../../utils/shared";
import {PolylineHandler} from "./PolylineHandler";
import {PolylineBatchRenderer} from "./PolylineBatchRenderer";
import {Extent} from "../../Extent";
export type Geodetic = LonLat | NumberArray2 | NumberArray3
export type Cartesian = Vec3 | NumberArray3;
export type SegmentPath3vExt = Cartesian[];
export type SegmentPathLonLatExt = Geodetic[];
export type SegmentPathColor = NumberArray4[];
export type SegmentPath3v = Vec3[];
export type SegmentPathLonLat = LonLat[];
export interface TexParam {
texOffset: number;
strokeSize: number;
texOffsetSpeed: number;
}
type StrokeSource = string | HTMLImageElement | null;
export interface IPolylineParams {
altitude?: number;
thickness?: number;
opacity?: number;
color?: string | string[];
visibility?: boolean;
isClosed?: boolean;
pathColors?: SegmentPathColor[];
path3v?: SegmentPath3vExt[];
pathLonLat?: SegmentPathLonLatExt[];
src?: StrokeSource;
texParams?: Partial<TexParam>;
}
/**
* Polyline object.
* @class
* @param {Object} [options] - Polyline options:
* @param {number} [options.thickness] - Thickness in screen pixels 1.5 is default.
* @param {Number} [options.altitude] - Relative to ground layers altitude value.
* @param {string|string[]} [options.color] - Default line color or per-segment HTML colors.
* @param {Boolean} [options.opacity] - Line opacity.
* @param {Boolean} [options.visibility] - Polyline visibility. True default.
* @param {Boolean[]} [options.isClosed] - Closed geometry type identification, per-segment.
* @param {SegmentPathLonLatExt[]} [options.pathLonLat] - Polyline geodetic coordinates array. [[[0,0,0], [1,1,1],...]]
* @param {SegmentPath3vExt[]} [options.path3v] - LinesString cartesian coordinates array. [[[0,0,0], [1,1,1],...]]
* @param {SegmentPathColor[]} [options.pathColors] - Coordinates color. [[[1,0,0,1], [0,1,0,1],...]] for right and green colors.
* @param {TexParam} [options.texParams] - Texture params for all segments: texOffset, strokeSize and texOffsetSpeed.
*/
class Polyline {
static __counter__: number = 0;
protected __id: number;
public _entity: Entity | null;
public _extent: Extent = new Extent();
public _handler: PolylineHandler | null;
public _handlerIndex: number;
public _batchRenderer: PolylineBatchRenderer | null;
public _batchRendererIndexes: number[];
public _path3v: SegmentPath3vExt[];
protected _pathLonLat: SegmentPathLonLatExt[];
protected _pathColors: SegmentPathColor[];
protected _color: string[];
protected _segmentTexParams: Partial<TexParam> | null;
protected _src: StrokeSource;
protected _isClosed: boolean;
protected _visibility: boolean;
protected _image: HTMLImageElement | null;
protected _opacity: number = 1.0;
protected _thickness: number;
protected _altitude: number;
protected _pickingColor: Vec3 | null;
constructor(options: IPolylineParams = {}) {
this.__id = Polyline.__counter__++;
this._path3v = options.path3v || [];
this._pathLonLat = options.pathLonLat || [];
this._pathColors = options.pathColors || [];
this._color = Array.isArray(options.color) ? options.color.slice() : (options.color ? [options.color] : []);
this._segmentTexParams = options.texParams ? {...options.texParams} : null;
this._entity = null;
this._handler = null;
this._handlerIndex = -1;
this._batchRenderer = null;
this._batchRendererIndexes = [];
this._src = options.src || null;
this._isClosed = options.isClosed || false;
this._visibility = options.visibility !== undefined ? options.visibility : true;
this._image = null;
this._opacity = options.opacity !== undefined ? options.opacity : 1.0;
this._thickness = options.thickness || 1.5;
this._altitude = options.altitude || 0;
this._pickingColor = null;
}
public get _pathLonLatMerc(): LonLat[][] {
let res: LonLat[][] = [];
if (this._batchRenderer && this._batchRendererIndexes.length > 0) {
const paths = this._batchRenderer._pathLonLatMerc;
for (let i = 0; i < this._batchRendererIndexes.length; i++) {
const seg = paths[this._batchRendererIndexes[i]];
res.push(seg || []);
}
return res;
}
return this._pathLonLatMercFromLocal();
}
protected _pathLonLatMercFromLocal(): LonLat[][] {
const res: LonLat[][] = new Array(this._pathLonLat.length);
for (let i = 0; i < this._pathLonLat.length; i++) {
const seg = this._pathLonLat[i] || [];
const mercSeg: LonLat[] = new Array(seg.length);
for (let j = 0; j < seg.length; j++) {
const p = seg[j];
if (p instanceof LonLat) {
mercSeg[j] = p.forwardMercator();
} else if (p instanceof Array) {
mercSeg[j] = new LonLat(p[0], p[1], (p as number[])[2]).forwardMercator();
} else {
mercSeg[j] = new LonLat();
}
}
res[i] = mercSeg;
}
return res;
}
public set altitude(alt:number ){
this._altitude = alt;
}
public get altitude(): number {
return this._altitude;
}
public _addToBatchRenderer() {
const br = this._batchRenderer;
if (!br) return;
if (this._path3v.length > 0) {
for (let i = 0; i < this._path3v.length; i++) {
if (!this._path3v[i] || this._path3v[i].length === 0) continue;
const batchIndex = br._path3v.length;
br.appendPath3v(this._path3v[i], this._pathColors[i], this._opacity);
this._batchRendererIndexes.push(batchIndex);
this._applySegmentProps(batchIndex, i);
}
} else if (this._pathLonLat.length > 0) {
for (let i = 0; i < this._pathLonLat.length; i++) {
if (!this._pathLonLat[i] || this._pathLonLat[i].length === 0) continue;
const batchIndex = br._pathLonLat.length;
br.appendPathLonLat(this._pathLonLat[i], this._pathColors[i], this._opacity);
this._batchRendererIndexes.push(batchIndex);
this._applySegmentProps(batchIndex, i);
}
}
this._updateExtent();
}
public _removeFromBatchRenderer() {
const br = this._batchRenderer;
const handler = this._handler;
if (!br || !handler) return;
// Highest-first removal keeps lower indices stable
const indices = this._batchRendererIndexes.slice().sort((a, b) => b - a);
this._batchRendererIndexes.length = 0;
for (let i = 0; i < indices.length; i++) {
br.removePath(indices[i]);
handler.reindexAfterRemoval(indices[i], br);
}
}
protected _getDefaultHtmlColor(segmentIndex: number = 0): string | undefined {
return this._color[segmentIndex] || this._color[0];
}
protected _getDefaultPathColor(segmentIndex: number = 0): NumberArray4 | undefined {
const htmlColor = this._getDefaultHtmlColor(segmentIndex);
if (!htmlColor) return;
const c = htmlColorToRgba(htmlColor);
return [c.x, c.y, c.z, c.w];
}
protected _applySegmentProps(batchIndex: number, segmentIndex: number = 0) {
const br = this._batchRenderer!;
const htmlColor = this._getDefaultHtmlColor(segmentIndex);
const segPathColors = this._pathColors[segmentIndex];
const hasSegmentPathColors = !!segPathColors && segPathColors.length > 0;
if (htmlColor && !hasSegmentPathColors) {
const defaultColor = this._getDefaultPathColor(segmentIndex);
if (defaultColor) {
br.setPathColors([defaultColor], batchIndex, this._opacity);
}
}
if (this._pickingColor) {
br.setPathPickingColor3v(this._pickingColor, batchIndex);
}
br.setThickness(this._thickness, batchIndex);
if (this._isClosed) {
br.setPathClosed(this._isClosed, batchIndex);
}
if (this._src != null) {
br.setPathSrc(this._src, batchIndex);
} else if (this._image) {
br.setPathSrc(this._image, batchIndex);
}
const texParams = this._segmentTexParams;
if (texParams) {
br.setPathTexParams(texParams.texOffset, texParams.strokeSize, texParams.texOffsetSpeed, batchIndex);
}
}
protected _tryAddSegmentToBatch(segmentIndex: number): boolean {
const br = this._batchRenderer;
if (!br || segmentIndex < 0 || segmentIndex < this._batchRendererIndexes.length) {
return false;
}
const seg3v = this._path3v[segmentIndex];
const segLL = this._pathLonLat[segmentIndex];
const pointsCount = Math.max(seg3v ? seg3v.length : 0, segLL ? segLL.length : 0);
if (pointsCount <= 1) {
return false;
}
const batchIndex = br._path3v.length;
if (seg3v && seg3v.length > 0) {
br.appendPath3v(seg3v, this._pathColors[segmentIndex], this._opacity);
} else if (segLL && segLL.length > 0) {
br.appendPathLonLat(segLL, this._pathColors[segmentIndex], this._opacity);
} else {
return false;
}
if (segmentIndex <= this._batchRendererIndexes.length) {
this._batchRendererIndexes.splice(segmentIndex, 0, batchIndex);
} else {
this._batchRendererIndexes.push(batchIndex);
}
this._applySegmentProps(batchIndex, segmentIndex);
return true;
}
protected _updateExtent() {
let lonmin = Infinity, lonmax = -Infinity,
latmin = Infinity, latmax = -Infinity;
let hasData = false;
const hasBatchRendererPaths = !!this._batchRenderer && this._batchRendererIndexes.length > 0;
const paths = hasBatchRendererPaths
? this._batchRenderer!._pathLonLat
: this._pathLonLat;
const pathsCount = hasBatchRendererPaths ? this._batchRendererIndexes.length : paths.length;
for (let i = 0; i < pathsCount; i++) {
const segIndex = hasBatchRendererPaths ? this._batchRendererIndexes[i] : i;
const seg = paths[segIndex];
if (!seg) continue;
for (let j = 0; j < seg.length; j++) {
const p = seg[j];
if (!p) continue;
let lon: number, lat: number;
if (p instanceof LonLat) {
lon = p.lon;
lat = p.lat;
} else if (p instanceof Array) {
lon = (p as number[])[0];
lat = (p as number[])[1];
} else {
continue;
}
if (lon < lonmin) lonmin = lon;
if (lon > lonmax) lonmax = lon;
if (lat < latmin) latmin = lat;
if (lat > latmax) latmax = lat;
hasData = true;
}
}
if (hasData) {
this._extent.southWest.lon = lonmin;
this._extent.southWest.lat = latmin;
this._extent.northEast.lon = lonmax;
this._extent.northEast.lat = latmax;
} else {
this._extent.southWest.lon = 180.0;
this._extent.southWest.lat = 90.0;
this._extent.northEast.lon = -180.0;
this._extent.northEast.lat = -90.0;
}
}
public getExtent(): Extent {
return this._extent;
}
public getPath3v(): SegmentPath3vExt[] {
if (this._batchRenderer && this._batchRendererIndexes.length > 0) {
const paths = this._batchRenderer._path3v;
const res: SegmentPath3vExt[] = new Array(this._batchRendererIndexes.length);
for (let i = 0; i < this._batchRendererIndexes.length; i++) {
res[i] = (paths[this._batchRendererIndexes[i]] || []) as SegmentPath3vExt;
}
return res;
}
return this._path3v;
}
public getPathLonLat(): SegmentPathLonLatExt[] {
if (this._batchRenderer && this._batchRendererIndexes.length > 0) {
const paths = this._batchRenderer._pathLonLat;
const res: SegmentPathLonLatExt[] = new Array(this._batchRendererIndexes.length);
for (let i = 0; i < this._batchRendererIndexes.length; i++) {
res[i] = (paths[this._batchRendererIndexes[i]] || []) as SegmentPathLonLatExt;
}
return res;
}
return this._pathLonLat;
}
public getPathColors(): NumberArray4[][] {
return this._pathColors;
}
public setImage(image: HTMLImageElement) {
this._image = image;
if (this._batchRenderer) {
for (let i = 0; i < this._batchRendererIndexes.length; i++) {
this._batchRenderer.setPathSrc(image, this._batchRendererIndexes[i]);
}
}
}
public getImage(): (HTMLImageElementExt | null) {
return this._image;
}
/**
* Sets stroke source per segment (null = color-only).
* @public
*/
public setSrc(src: StrokeSource) {
this._src = src;
if (src == null) {
this._image = null;
}
if (!this._handler || !this._batchRenderer) {
return;
}
const targetRenderer = this._handler.getRenderer(this._opacity, src != null);
if (this._batchRenderer !== targetRenderer) {
this._removeFromBatchRenderer();
this._batchRenderer = targetRenderer;
this._addToBatchRenderer();
return;
}
if (!this._batchRenderer.isTextured) {
return;
}
for (let i = 0; i < this._batchRendererIndexes.length; i++) {
this._batchRenderer.setPathSrc(src, this._batchRendererIndexes[i]);
}
}
public getSrc(): StrokeSource {
return this._src;
}
/**
* Set closed/open state for one path segment.
* @public
*/
public set isClosed(isClosed: boolean) {
this._isClosed = isClosed;
if (this._batchRenderer) {
for (let i = 0; i < this._batchRendererIndexes.length; i++) {
this._batchRenderer.setPathClosed(isClosed, this._batchRendererIndexes[i]);
}
}
}
public get isClosed(): boolean {
return this._isClosed;
}
public setTextureDisabled() {
}
static setPathColors(
pathLonLat: SegmentPathLonLatExt[],
pathColors: SegmentPathColor[],
defaultColor: NumberArray4,
outColors: number[]
) {
PolylineBatchRenderer.setPathColors(pathLonLat, pathColors, defaultColor, outColors);
}
public setPointLonLat(lonlat: LonLat, index: number, segmentIndex: number) {
const seg = this._pathLonLat[segmentIndex];
if (seg) seg[index] = lonlat;
if (this._batchRenderer && segmentIndex < this._batchRendererIndexes.length) {
this._batchRenderer.setPointLonLat(lonlat, index, this._batchRendererIndexes[segmentIndex]);
}
this._updateExtent();
}
/**
* Changes cartesian point coordinates of the path
* @param {Vec3} coordinates - New coordinates
* @param {number} [index=0] - Path segment index
* @param {number} [segmentIndex=0] - Index of the point in the path segment
* @param {boolean} [skipLonLat=false] - Do not update geodetic coordinates
*/
public setPoint3v(coordinates: Vec3, index: number = 0, segmentIndex: number = 0, skipLonLat: boolean = false) {
const seg = this._path3v[segmentIndex];
if (seg) seg[index] = coordinates;
if (this._batchRenderer && segmentIndex < this._batchRendererIndexes.length) {
this._batchRenderer.setPoint3v(coordinates, index, this._batchRendererIndexes[segmentIndex], skipLonLat);
}
if (!skipLonLat) {
this._updateExtent();
}
}
/**
* Remove point from the path
* @param {number} index - Point index in a path segment
* @param {number} [segmentIndex=0] - Segment path index
*/
public removePoint(index: number, segmentIndex: number = 0) {
const seg3v = this._path3v[segmentIndex];
if (seg3v) seg3v.splice(index, 1);
const segLL = this._pathLonLat[segmentIndex];
if (segLL) segLL.splice(index, 1);
const segC = this._pathColors[segmentIndex];
if (segC) segC.splice(index, 1);
if (this._batchRenderer && segmentIndex < this._batchRendererIndexes.length) {
const batchIndex = this._batchRendererIndexes[segmentIndex];
const pointsCount = Math.max(seg3v ? seg3v.length : 0, segLL ? segLL.length : 0);
if (pointsCount > 1) {
this._batchRenderer.removePoint(index, batchIndex);
} else if (this._handler) {
this._batchRenderer.removePath(batchIndex);
this._handler.reindexAfterRemoval(batchIndex, this._batchRenderer);
this._batchRendererIndexes.splice(segmentIndex, 1);
}
}
this._updateExtent();
}
/**
* Insert point coordinates in a path segment
* @param {Vec3} point3v - Point coordinates
* @param {number} [index=0] - Index in the path
* @param {NumberArray4} [color] - Point color
* @param {number} [segmentIndex=0] - Path segment index
*/
public insertPoint3v(point3v: Vec3, index: number = 0, color?: NumberArray4, segmentIndex: number = 0) {
const seg = this._path3v[segmentIndex] || (this._path3v[segmentIndex] = []);
seg.splice(index, 0, point3v);
if (color) {
const segC = this._pathColors[segmentIndex] || (this._pathColors[segmentIndex] = []);
segC.splice(index, 0, color);
}
if (this._batchRenderer) {
if (segmentIndex < this._batchRendererIndexes.length) {
this._batchRenderer.insertPoint3v(point3v, index, color, this._batchRendererIndexes[segmentIndex]);
} else {
this._tryAddSegmentToBatch(segmentIndex);
}
}
this._updateExtent();
}
/**
* Append new point in the end of the path.
* @public
* @param {Vec3} point3v - New point coordinates.
* @param {number} [segmentIndex=0] - Path segment index, first by default.
* @param {NumberArray4} [color] - Point color
*/
public addPoint3v(point3v: Vec3, segmentIndex: number = 0, color?: NumberArray4) {
const seg = this._path3v[segmentIndex] || (this._path3v[segmentIndex] = []);
seg.push(point3v);
if (color) {
const segC = this._pathColors[segmentIndex] || (this._pathColors[segmentIndex] = []);
segC.push(color);
}
if (this._batchRenderer) {
if (segmentIndex < this._batchRendererIndexes.length) {
this._batchRenderer.addPoint3v(point3v, this._batchRendererIndexes[segmentIndex], color, this._opacity);
} else {
this._tryAddSegmentToBatch(segmentIndex);
}
}
this._updateExtent();
}
/**
* Append new geodetic point in the end of the path.
* @public
* @param {LonLat} lonLat - New coordinate.
* @param {number} [segmentIndex=0] - Path segment index, first by default.
* @param {NumberArray4} [color] - Point color.
*/
public addPointLonLat(lonLat: LonLat, segmentIndex: number = 0, color?: NumberArray4) {
const seg = this._pathLonLat[segmentIndex] || (this._pathLonLat[segmentIndex] = []);
seg.push(lonLat);
if (color) {
const segC = this._pathColors[segmentIndex] || (this._pathColors[segmentIndex] = []);
segC.push(color);
}
if (this._batchRenderer) {
if (segmentIndex < this._batchRendererIndexes.length) {
this._batchRenderer.addPointLonLat(lonLat, this._batchRendererIndexes[segmentIndex], color, this._opacity);
} else {
this._tryAddSegmentToBatch(segmentIndex);
}
}
this._updateExtent();
}
/**
* Change path point color
* @param {NumberArray4} color - New color
* @param {number} [index=0] - Point index
* @param {number} [segmentIndex=0] - Path segment index
*/
public setPointColor(color: NumberArray4, index: number = 0, segmentIndex: number = 0) {
const segC = this._pathColors[segmentIndex] || (this._pathColors[segmentIndex] = []);
segC[index] = color;
if (this._batchRenderer && segmentIndex < this._batchRendererIndexes.length) {
this._batchRenderer.setPointColor(color, index, this._batchRendererIndexes[segmentIndex], this._opacity);
}
}
/**
* Remove multiline path segment
* @param {number} index - Segment index in multiline
*/
public removePath(index: number) {
if (index < 0) return;
const hasBatchIndex = index < this._batchRendererIndexes.length;
const batchIndex = hasBatchIndex ? this._batchRendererIndexes[index] : -1;
if (hasBatchIndex) {
this._batchRendererIndexes.splice(index, 1);
}
this._path3v.splice(index, 1);
if (index < this._pathLonLat.length) this._pathLonLat.splice(index, 1);
if (index < this._pathColors.length) this._pathColors.splice(index, 1);
if (batchIndex > -1 && this._batchRenderer && this._handler) {
this._batchRenderer.removePath(batchIndex);
this._handler.reindexAfterRemoval(batchIndex, this._batchRenderer);
}
this._updateExtent();
}
public appendPath3v(path3v: SegmentPath3vExt, pathColors?: NumberArray4[]) {
this._path3v.push(path3v);
if (pathColors) this._pathColors.push(pathColors);
if (this._batchRenderer && path3v.length > 1) {
const batchIndex = this._batchRenderer._path3v.length;
this._batchRenderer.appendPath3v(path3v, pathColors, this._opacity);
this._batchRendererIndexes.push(batchIndex);
this._applySegmentProps(batchIndex, this._path3v.length - 1);
this._path3v[this._path3v.length - 1] = this._batchRenderer._path3v[batchIndex];
}
this._updateExtent();
}
public appendPathLonLat(pathLonLat: SegmentPathLonLatExt) {
this._pathLonLat.push(pathLonLat);
if (this._batchRenderer && pathLonLat.length > 1) {
const batchIndex = this._batchRenderer._path3v.length;
this._batchRenderer.appendPathLonLat(pathLonLat, undefined, this._opacity);
this._batchRendererIndexes.push(batchIndex);
this._applySegmentProps(batchIndex, this._pathLonLat.length - 1);
this._path3v[this._pathLonLat.length - 1] = this._batchRenderer._path3v[batchIndex];
}
this._updateExtent();
}
public setPathLonLatFast(pathLonLat: SegmentPathLonLatExt[], pathColors?: (SegmentPathColor | NumberArray4)[] | SegmentPathColor | NumberArray4) {
if (!pathColors) {
this.setPathLonLat(pathLonLat, undefined, true);
return;
}
const isSingleColor = Array.isArray(pathColors) && pathColors.length > 0 && typeof pathColors[0] === "number";
const normalized = isSingleColor ? [pathColors as NumberArray4] : pathColors as (SegmentPathColor | NumberArray4)[];
this.setPathLonLat(pathLonLat, normalized, true);
}
public setPath3vFast(path3v: SegmentPath3vExt[], pathColors?: (SegmentPathColor | NumberArray4)[] | SegmentPathColor | NumberArray4) {
if (!pathColors) {
this.setPath3v(path3v, undefined, true);
return;
}
const isSingleColor = Array.isArray(pathColors) && pathColors.length > 0 && typeof pathColors[0] === "number";
const normalized = isSingleColor ? [pathColors as NumberArray4] : pathColors as (SegmentPathColor | NumberArray4)[];
this.setPath3v(path3v, normalized, true);
}
/**
* Sets Polyline cartesian coordinates.
* @public
* @param {SegmentPath3vExt[]} path3v - Polyline path cartesian coordinates. (exactly 3 entries)
* @param {SegmentPathColor[]} [pathColors] - Polyline path cartesian coordinates. (exactly 3 entries)
* @param {Boolean} [forceEqual=false] - Makes assigning faster for size equal coordinates array.
*/
public setPath3v(path3v: SegmentPath3vExt[], pathColors?: (SegmentPathColor | NumberArray4)[], forceEqual?: boolean): void;
public setPath3v(path3v: SegmentPath3vExt, pathColors?: SegmentPathColor | NumberArray4, forceEqual?: boolean, segmentIndex?: number): void;
public setPath3v(path3v: SegmentPath3vExt[] | SegmentPath3vExt, pathColors?: (SegmentPathColor | NumberArray4)[] | SegmentPathColor | NumberArray4, forceEqual: boolean = false, segmentIndex?: number) {
if (segmentIndex !== undefined) {
this._path3v[segmentIndex] = path3v as SegmentPath3vExt;
const resolvedPathColors = pathColors ?? this._getDefaultPathColor(segmentIndex);
if (this._batchRenderer && segmentIndex < this._batchRendererIndexes.length) {
this._batchRenderer.setPath3v(
path3v as SegmentPath3vExt,
resolvedPathColors as SegmentPathColor | NumberArray4,
forceEqual,
this._batchRendererIndexes[segmentIndex]
);
}
} else {
const paths = path3v as SegmentPath3vExt[];
this._path3v = paths;
if (pathColors) {
const pc = pathColors as (SegmentPathColor | NumberArray4)[];
this._pathColors = new Array(paths.length);
for (let i = 0; i < paths.length; i++) {
this._pathColors[i] = (pc[i] as SegmentPathColor) || [];
}
}
if (this._batchRenderer) {
if (forceEqual && this._batchRendererIndexes.length === paths.length) {
const pc = pathColors as (SegmentPathColor | NumberArray4)[] | undefined;
for (let i = 0; i < paths.length; i++) {
this._batchRenderer.setPath3v(
paths[i],
pc?.[i] ?? this._getDefaultPathColor(i),
true,
this._batchRendererIndexes[i]
);
}
} else {
this._removeFromBatchRenderer();
this._addToBatchRenderer();
}
}
}
this._updateExtent();
}
/**
* Sets polyline geodetic coordinates.
* @public
* @param {SegmentPathLonLat[]} pathLonLat - Polyline path cartesian coordinates.
* @param {SegmentPathColor[]} pathColors - Polyline path points colors.
* @param {Boolean} [forceEqual=false] - OPTIMIZATION FLAG: Makes assigning faster for size equal coordinates array.
*/
public setPathLonLat(pathLonLat: SegmentPathLonLatExt[], pathColors?: (SegmentPathColor | NumberArray4)[], forceEqual?: boolean): void;
public setPathLonLat(pathLonLat: SegmentPathLonLatExt, pathColors?: SegmentPathColor | NumberArray4, forceEqual?: boolean, segmentIndex?: number): void;
public setPathLonLat(pathLonLat: SegmentPathLonLatExt[] | SegmentPathLonLatExt, pathColors?: (SegmentPathColor | NumberArray4)[] | SegmentPathColor | NumberArray4, forceEqual: boolean = false, segmentIndex?: number) {
if (segmentIndex !== undefined) {
this._pathLonLat[segmentIndex] = pathLonLat as SegmentPathLonLatExt;
const resolvedPathColors = pathColors ?? this._getDefaultPathColor(segmentIndex);
if (this._batchRenderer && segmentIndex < this._batchRendererIndexes.length) {
this._batchRenderer.setPathLonLat(
pathLonLat as SegmentPathLonLatExt,
resolvedPathColors as SegmentPathColor | NumberArray4,
forceEqual,
this._batchRendererIndexes[segmentIndex]
);
}
} else {
const paths = pathLonLat as SegmentPathLonLatExt[];
this._pathLonLat = paths;
if (pathColors) {
const pc = pathColors as (SegmentPathColor | NumberArray4)[];
this._pathColors = new Array(paths.length);
for (let i = 0; i < paths.length; i++) {
this._pathColors[i] = (pc[i] as SegmentPathColor) || [];
}
}
if (this._batchRenderer) {
if (forceEqual && this._batchRendererIndexes.length === paths.length) {
const pc = pathColors as (SegmentPathColor | NumberArray4)[] | undefined;
for (let i = 0; i < paths.length; i++) {
this._batchRenderer.setPathLonLat(
paths[i],
pc?.[i] ?? this._getDefaultPathColor(i),
true,
this._batchRendererIndexes[i]
);
}
} else {
this._removeFromBatchRenderer();
this._addToBatchRenderer();
}
}
}
this._updateExtent();
}
// ─── Visual properties ──────────────────────────────────────────────
/**
* Sets polyline opacity.
* @public
* @param {number} opacity - Opacity.
*/
public setOpacity(opacity: number) {
if (this._opacity === opacity) {
return;
}
this._opacity = opacity;
if (!this._handler) {
return;
}
const targetRenderer = this._handler.getRenderer(opacity, this._src != null);
if (this._batchRenderer !== targetRenderer) {
this._removeFromBatchRenderer();
this._batchRenderer = targetRenderer;
this._addToBatchRenderer();
return;
}
if (!this._batchRenderer) {
return;
}
for (let i = 0; i < this._batchRendererIndexes.length; i++) {
this._batchRenderer.setPathOpacity(this._opacity, this._batchRendererIndexes[i]);
}
}
/**
* Gets polyline opacity.
* @public
*/
public getOpacity(): number {
return this._opacity;
}
/**
* Sets Polyline thickness in screen pixels.
* @public
* @param {number} altitude - ALtitude value.
*/
public setAltitude(altitude: number) {
this._altitude = altitude;
// Altitude is renderer-wide property
if (this._batchRenderer) {
this._batchRenderer.setAltitude(altitude);
}
}
/**
* Sets Polyline thickness in screen pixels.
* @public
* @param {number} thickness - Thickness.
*/
public setThickness(thickness: number): void {
this._thickness = thickness;
if (this._batchRenderer) {
for (let i = 0; i < this._batchRendererIndexes.length; i++) {
this._batchRenderer.setThickness(thickness, this._batchRendererIndexes[i]);
}
}
}
/**
* Sets polyline segment color.
* @public
* @param {string} htmlColor - HTML color.
*/
public setColor(htmlColor: string): void {
this._color[0] = htmlColor;
if (this._batchRenderer) {
for (let i = 0; i < this._batchRendererIndexes.length; i++) {
this._batchRenderer.setColor(htmlColor, this._batchRendererIndexes[i], this._opacity);
}
}
}
public setPathTexOffset(texOffset: number, segmentIndex: number): void {
const texParams = this._segmentTexParams || (this._segmentTexParams = {});
texParams.texOffset = texOffset;
if (this._batchRenderer && segmentIndex < this._batchRendererIndexes.length) {
this._batchRenderer.setPathTexOffset(texOffset, this._batchRendererIndexes[segmentIndex]);
}
}
public setPathStrokeSize(strokeSize: number, segmentIndex: number): void {
const texParams = this._segmentTexParams || (this._segmentTexParams = {});
texParams.strokeSize = strokeSize;
if (this._batchRenderer && segmentIndex < this._batchRendererIndexes.length) {
this._batchRenderer.setPathStrokeSize(strokeSize, this._batchRendererIndexes[segmentIndex]);
}
}
public setPathTexOffsetSpeed(texOffsetSpeed: number, segmentIndex: number): void {
const texParams = this._segmentTexParams || (this._segmentTexParams = {});
texParams.texOffsetSpeed = texOffsetSpeed;
if (this._batchRenderer && segmentIndex < this._batchRendererIndexes.length) {
this._batchRenderer.setPathTexOffsetSpeed(texOffsetSpeed, this._batchRendererIndexes[segmentIndex]);
}
}
/**
* Sets visibility.
* @public
* @param {boolean} visibility - Polyline visibility.
*/
public setVisibility(visibility: boolean) {
this._visibility = visibility;
}
/**
* Gets Polyline visibility.
* @public
* @return {boolean} Polyline visibility.
*/
public getVisibility(): boolean {
return this._visibility;
}
public setPickingColor3v(color: Vec3) {
this._pickingColor = color;
if (this._batchRenderer) {
for (let i = 0; i < this._batchRendererIndexes.length; i++) {
this._batchRenderer.setPathPickingColor3v(color, this._batchRendererIndexes[i]);
}
}
}
public setPathColors(pathColors: SegmentPathColor[]): void;
public setPathColors(pathColors: SegmentPathColor, segmentIndex: number): void;
public setPathColors(pathColors: SegmentPathColor[] | SegmentPathColor, segmentIndex?: number) {
if (segmentIndex !== undefined) {
this._pathColors[segmentIndex] = pathColors as SegmentPathColor;
if (this._batchRenderer && segmentIndex < this._batchRendererIndexes.length) {
this._batchRenderer.setPathColors(pathColors as SegmentPathColor, this._batchRendererIndexes[segmentIndex], this._opacity);
}
} else {
this._pathColors = (pathColors as SegmentPathColor[]).slice();
if (this._batchRenderer) {
for (let i = 0; i < this._batchRendererIndexes.length && i < this._pathColors.length; i++) {
this._batchRenderer.setPathColors(this._pathColors[i], this._batchRendererIndexes[i], this._opacity);
}
}
}
}
/**
* Sets polyline color
* @param {string} htmlColor - HTML color.
*/
public setColorHTML(htmlColor: string) {
this._color[0] = htmlColor;
if (this._batchRenderer) {
for (let i = 0; i < this._batchRendererIndexes.length; i++) {
this._batchRenderer.setColor(htmlColor, this._batchRendererIndexes[i], this._opacity);
}
}
}
// ─── Lifecycle ──────────────────────────────────────────────────────
/**
* Clear polyline data.
* @public
*/
public clear() {
this._removeFromBatchRenderer();
this._path3v = [];
this._pathLonLat = [];
this._pathColors = [];
this._segmentTexParams = null;
}
/**
* Removes from an entity.
* @public
*/
public remove() {
if (this._handler) {
this._handler.remove(this);
}
}
}
export {Polyline};