entity/geometry/GeometryHandler.tstypescript
import * as mercator from "../../mercator";
import { RADIANS } from "../../math";
import { isCubeProj } from "../../proj/cube";
import { Extent } from "../../Extent";
import { Handler } from "../../webgl/Handler";
import type { WebGLBufferExt } from "../../webgl/Handler";
import { doubleToTwoFloatsV2 } from "../../math/doubleToTwoFloats";
import { Vector } from "../../layer/Vector";
import { Vec2 } from "../../math/Vec2";
import type { NumberArray2 } from "../../math/Vec2";
import { Node } from "../../quadTree/Node";
import { Vec3 } from "../../math/Vec3";
import { Vec4 } from "../../math/Vec4";
import type {
CoordinatesType,
Geometry,
ILineStringCoordinates,
IMultiLineStringCoordinates,
IMultiPolygonCoordinates,
IPolygonCoordinates
} from "./Geometry";
import { GeometryTypeEnum } from "./Geometry";
import earcut, { flatten } from "earcut";
import { concatArrays } from "../../utils/shared";
import { triangulateOnGrid } from "./triangulateOnGrid";
type GeometryOffsetKey =
| "_polyVerticesHandlerIndex"
| "_polyIndexesHandlerIndex"
| "_lineVerticesHandlerIndex"
| "_lineOrdersHandlerIndex"
| "_lineColorsHandlerIndex"
| "_lineThicknessHandlerIndex"
| "_lineIndexesHandlerIndex";
type GeometryLengthKey =
| "_polyVerticesLength"
| "_polyIndexesLength"
| "_lineVerticesLength"
| "_lineOrdersLength"
| "_lineColorsLength"
| "_lineThicknessLength"
| "_lineIndexesLength";
const POLYVERTICES_BUFFER = 0;
const POLYINDEXES_BUFFER = 1;
const POLYCOLORS_BUFFER = 2;
const LINEVERTICES_BUFFER = 3;
const LINEINDEXES_BUFFER = 4;
const LINEORDERS_BUFFER = 5;
const LINECOLORS_BUFFER = 6;
const LINETHICKNESS_BUFFER = 7;
const LINESTROKES_BUFFER = 8;
const LINESTROKECOLORS_BUFFER = 9;
const POLYPICKINGCOLORS_BUFFER = 10;
const LINEPICKINGCOLORS_BUFFER = 11;
function doubleToTwoFloats(v: NumberArray2, high: Vec2, low: Vec2) {
let x = v[0],
y = v[1];
if (x >= 0.0) {
let doubleHigh = Math.floor(x / 65536.0) * 65536.0;
high.x = Math.fround(doubleHigh);
low.x = Math.fround(x - doubleHigh);
} else {
let doubleHigh = Math.floor(-x / 65536.0) * 65536.0;
high.x = Math.fround(-doubleHigh);
low.x = Math.fround(x + doubleHigh);
}
if (y >= 0.0) {
let doubleHigh = Math.floor(y / 65536.0) * 65536.0;
high.y = Math.fround(doubleHigh);
low.y = Math.fround(y - doubleHigh);
} else {
let doubleHigh = Math.floor(-y / 65536.0) * 65536.0;
high.y = Math.fround(-doubleHigh);
low.y = Math.fround(y + doubleHigh);
}
}
let tempHigh = new Vec2(),
tempLow = new Vec2(),
tempHighLow = new Vec2();
// A direction is a unit vector, its high floats are the values and the low ones are what is left of them
function pushDirection(x: number, y: number, z: number, copies: number, outHigh: number[], outLow: number[]) {
const hx = Math.fround(x),
hy = Math.fround(y),
hz = Math.fround(z),
lx = Math.fround(x - hx),
ly = Math.fround(y - hy),
lz = Math.fround(z - hz);
for (let i = 0; i < copies; i++) {
outHigh.push(hx, hy, hz);
outLow.push(lx, ly, lz);
}
}
// The surface normal of the ellipsoid at the geodetic point
function toDirection(p: CoordinatesType, out: number[]) {
const lon = p[0] * RADIANS,
lat = p[1] * RADIANS,
c = Math.cos(lat);
out[0] = c * Math.cos(lon);
out[1] = c * Math.sin(lon);
out[2] = Math.sin(lat);
return out;
}
const _d0 = [0, 0, 0],
_d1 = [0, 0, 0];
/**
* Longest segment of a line or a polygon edge on the cube, in degrees.
*/
export const DENSIFY_STEP = 1.0;
/**
* Returns the path with points between the ones further apart than the step in longitude or latitude, the closed path
* gets them on the edge from its last point to the first one too.
* @param {Array.<CoordinatesType>} path - Geodetic points.
* @param {boolean} isClosed - The last point goes back to the first one.
* @param {number} [step] - Longest segment in degrees.
* @returns {Array.<CoordinatesType>} -
*/
export function densifyPath(
path: CoordinatesType[],
isClosed: boolean,
step: number = DENSIFY_STEP
): CoordinatesType[] {
const out: CoordinatesType[] = [];
const n = path.length;
for (let i = 0; i < n; i++) {
const a = path[i];
out.push(a);
const b = i < n - 1 ? path[i + 1] : isClosed && n > 2 ? path[0] : null;
if (!b) continue;
const k = Math.ceil(Math.max(Math.abs(b[0] - a[0]), Math.abs(b[1] - a[1])) / step);
for (let j = 1; j < k; j++) {
const t = j / k,
lon = a[0] + (b[0] - a[0]) * t,
lat = a[1] + (b[1] - a[1]) * t;
out.push(a.length > 2 && b.length > 2 ? [lon, lat, a[2]! + (b[2]! - a[2]!) * t] : [lon, lat]);
}
}
return out;
}
/**
* Returns the path without the points equal to the point before them, a closed path also without its last points
* equal to the first one, as a GeoJSON ring has it. A segment of no length has no direction, so a line can't join
* the segments around it.
* @param {Array.<CoordinatesType>} path - Geodetic points.
* @param {boolean} isClosed - The last point goes back to the first one.
* @returns {Array.<CoordinatesType>} -
*/
export function removeRepeatedPoints(path: CoordinatesType[], isClosed: boolean): CoordinatesType[] {
const out: CoordinatesType[] = [];
for (const p of path) {
const q = out[out.length - 1];
if (!q || p[0] !== q[0] || p[1] !== q[1]) {
out.push(p);
}
}
if (isClosed) {
while (out.length > 1 && out[out.length - 1][0] === out[0][0] && out[out.length - 1][1] === out[0][1]) {
out.pop();
}
}
return out;
}
// Appends the Web Mercator coordinates of the points from the index
function pushMercator(points: CoordinatesType[], from: number, out: number[]) {
for (let i = from; i < points.length; i++) {
out.push(mercator.forward_lon(points[i][0]), mercator.forward_lat(points[i][1]));
}
}
class GeometryHandler {
static __counter__: number = 0;
protected __id: number;
protected _layer: Vector;
protected _handler: Handler | null;
protected _geometries: Geometry[];
protected _updatedGeometryArr: Geometry[];
protected _updatedGeometry: Record<number, boolean>;
protected _removeGeometryExtentArr: Extent[];
protected _removeGeometryExtents: Record<number, boolean>;
// Polygon arrays
protected _polyVerticesHighMerc: number[];
protected _polyVerticesLowMerc: number[];
protected _polyDirectionsHigh: number[];
protected _polyDirectionsLow: number[];
protected _polyColors: number[];
protected _polyPickingColors: number[];
protected _polyIndexes: number[];
// Line arrays
protected _lineVerticesHighMerc: number[];
protected _lineVerticesLowMerc: number[];
protected _lineDirectionsHigh: number[];
protected _lineDirectionsLow: number[];
protected _lineOrders: number[];
protected _lineIndexes: number[];
protected _lineColors: number[];
protected _linePickingColors: number[];
protected _lineThickness: number[];
protected _lineStrokes: number[];
protected _lineStrokeColors: number[];
// Buffers
public _polyVerticesHighBufferMerc: WebGLBufferExt | null;
public _polyVerticesLowBufferMerc: WebGLBufferExt | null;
public _polyDirectionsHighBuffer: WebGLBufferExt | null;
public _polyDirectionsLowBuffer: WebGLBufferExt | null;
public _polyColorsBuffer: WebGLBufferExt | null;
public _polyPickingColorsBuffer: WebGLBufferExt | null;
public _polyIndexesBuffer: WebGLBufferExt | null;
public _lineVerticesHighBufferMerc: WebGLBufferExt | null;
public _lineVerticesLowBufferMerc: WebGLBufferExt | null;
public _lineDirectionsHighBuffer: WebGLBufferExt | null;
public _lineDirectionsLowBuffer: WebGLBufferExt | null;
public _lineColorsBuffer: WebGLBufferExt | null;
public _linePickingColorsBuffer: WebGLBufferExt | null;
public _lineThicknessBuffer: WebGLBufferExt | null;
public _lineStrokesBuffer: WebGLBufferExt | null;
public _lineStrokeColorsBuffer: WebGLBufferExt | null;
public _lineOrdersBuffer: WebGLBufferExt | null;
public _lineIndexesBuffer: WebGLBufferExt | null;
protected _buffersUpdateCallbacks: Function[];
protected _changedBuffers: boolean[];
/**
* The planet is a cube
*/
public _isCube: boolean;
constructor(layer: Vector) {
this.__id = GeometryHandler.__counter__++;
this._layer = layer;
this._handler = null;
this._geometries = [];
this._updatedGeometryArr = [];
this._updatedGeometry = {};
this._removeGeometryExtentArr = [];
this._removeGeometryExtents = {};
// Polygon arrays
this._polyVerticesHighMerc = [];
this._polyVerticesLowMerc = [];
this._polyDirectionsHigh = [];
this._polyDirectionsLow = [];
this._polyColors = [];
this._polyPickingColors = [];
this._polyIndexes = [];
// Line arrays
this._lineVerticesHighMerc = [];
this._lineVerticesLowMerc = [];
this._lineDirectionsHigh = [];
this._lineDirectionsLow = [];
this._lineOrders = [];
this._lineIndexes = [];
this._lineColors = [];
this._linePickingColors = [];
this._lineThickness = [];
this._lineStrokes = [];
this._lineStrokeColors = [];
// Buffers
this._polyVerticesHighBufferMerc = null;
this._polyVerticesLowBufferMerc = null;
this._polyDirectionsHighBuffer = null;
this._polyDirectionsLowBuffer = null;
this._polyColorsBuffer = null;
this._polyPickingColorsBuffer = null;
this._polyIndexesBuffer = null;
this._lineVerticesHighBufferMerc = null;
this._lineVerticesLowBufferMerc = null;
this._lineDirectionsHighBuffer = null;
this._lineDirectionsLowBuffer = null;
this._lineColorsBuffer = null;
this._linePickingColorsBuffer = null;
this._lineThicknessBuffer = null;
this._lineStrokesBuffer = null;
this._lineStrokeColorsBuffer = null;
this._lineOrdersBuffer = null;
this._lineIndexesBuffer = null;
this._buffersUpdateCallbacks = [];
this._buffersUpdateCallbacks[POLYVERTICES_BUFFER] = this.createPolyVerticesBuffer;
this._buffersUpdateCallbacks[POLYINDEXES_BUFFER] = this.createPolyIndexesBuffer;
this._buffersUpdateCallbacks[POLYCOLORS_BUFFER] = this.createPolyColorsBuffer;
this._buffersUpdateCallbacks[LINEVERTICES_BUFFER] = this.createLineVerticesBuffer;
this._buffersUpdateCallbacks[LINEINDEXES_BUFFER] = this.createLineIndexesBuffer;
this._buffersUpdateCallbacks[LINEORDERS_BUFFER] = this.createLineOrdersBuffer;
this._buffersUpdateCallbacks[LINECOLORS_BUFFER] = this.createLineColorsBuffer;
this._buffersUpdateCallbacks[LINETHICKNESS_BUFFER] = this.createLineThicknessBuffer;
this._buffersUpdateCallbacks[LINESTROKES_BUFFER] = this.createLineStrokesBuffer;
this._buffersUpdateCallbacks[LINESTROKECOLORS_BUFFER] = this.createLineStrokeColorsBuffer;
this._buffersUpdateCallbacks[POLYPICKINGCOLORS_BUFFER] = this.createPolyPickingColorsBuffer;
this._buffersUpdateCallbacks[LINEPICKINGCOLORS_BUFFER] = this.createLinePickingColorsBuffer;
this._changedBuffers = new Array(this._buffersUpdateCallbacks.length);
this._isCube = false;
}
/**
* Appends the directions of the line vertices in the order of appendLineData: each point of a path four times,
* after the point before the first one and before the point after the last one.
*/
static appendLineDirections(
pathArr: CoordinatesType[][],
isClosed: boolean,
outHigh: number[],
outLow: number[],
outHigh2: number[],
outLow2: number[]
) {
const push = (x: number, y: number, z: number) => {
pushDirection(x, y, z, 4, outHigh, outLow);
pushDirection(x, y, z, 4, outHigh2, outLow2);
};
for (let j = 0; j < pathArr.length; j++) {
const path = pathArr[j];
if (path.length === 0) {
continue;
}
const n = path.length;
if (isClosed) {
toDirection(path[n - 1], _d0);
push(_d0[0], _d0[1], _d0[2]);
} else {
toDirection(path[0], _d0);
toDirection(path[1] || path[0], _d1);
push(2 * _d0[0] - _d1[0], 2 * _d0[1] - _d1[1], 2 * _d0[2] - _d1[2]);
}
for (let i = 0; i < n; i++) {
toDirection(path[i], _d0);
push(_d0[0], _d0[1], _d0[2]);
}
if (isClosed) {
toDirection(path[0], _d0);
push(_d0[0], _d0[1], _d0[2]);
} else {
toDirection(path[n - 1], _d0);
toDirection(path[n - 2] || path[n - 1], _d1);
push(2 * _d0[0] - _d1[0], 2 * _d0[1] - _d1[1], 2 * _d0[2] - _d1[2]);
}
}
}
// The directions of the polygon vertices, the rings one after another as flatten() puts them
static appendPolygonDirections(rings: CoordinatesType[][], outHigh: number[], outLow: number[]) {
for (let j = 0; j < rings.length; j++) {
for (let k = 0; k < rings[j].length; k++) {
toDirection(rings[j][k], _d0);
pushDirection(_d0[0], _d0[1], _d0[2], 1, outHigh, outLow);
}
}
}
static appendLineData(
pathArr: CoordinatesType[][],
isClosed: boolean,
color: Vec4,
pickingColor: Vec3,
thickness: number,
strokeColor: Vec4,
strokeSize: number,
outVerticesHigh: number[],
outVerticesLow: number[],
outOrders: number[],
outIndexes: number[],
outColors: number[],
outPickingColors: number[],
outThickness: number[],
outStrokeColors: number[],
outStrokes: number[],
outVerticesHigh2: number[],
outVerticesLow2: number[]
) {
// Index of the first vertex appended below. After bringToFront() the last
// index entries belong to another geometry, so they can't be used for this.
var index = outVerticesHigh.length * 0.5;
outIndexes.push(index, index);
var t = thickness,
c = [color.x, color.y, color.z, color.w],
s = strokeSize,
sc = [strokeColor.x, strokeColor.y, strokeColor.z, strokeColor.w],
p = [pickingColor.x, pickingColor.y, pickingColor.z, 1.0];
for (let j = 0; j < pathArr.length; j++) {
var path = pathArr[j];
if (path.length === 0) {
continue;
}
let startIndex = index;
let last: CoordinatesType;
if (isClosed) {
last = path[path.length - 1];
} else {
let p0 = path[0],
p1 = path[1];
if (!p1) {
p1 = p0;
}
last = [p0[0] + p0[0] - p1[0], p0[1] + p0[1] - p1[1]];
}
doubleToTwoFloats(last as NumberArray2, tempHigh, tempLow);
outVerticesHigh.push(
tempHigh.x,
tempHigh.y,
tempHigh.x,
tempHigh.y,
tempHigh.x,
tempHigh.y,
tempHigh.x,
tempHigh.y
);
outVerticesLow.push(tempLow.x, tempLow.y, tempLow.x, tempLow.y, tempLow.x, tempLow.y, tempLow.x, tempLow.y);
outVerticesHigh2.push(
tempHigh.x,
tempHigh.y,
tempHigh.x,
tempHigh.y,
tempHigh.x,
tempHigh.y,
tempHigh.x,
tempHigh.y
);
outVerticesLow2.push(
tempLow.x,
tempLow.y,
tempLow.x,
tempLow.y,
tempLow.x,
tempLow.y,
tempLow.x,
tempLow.y
);
outOrders.push(1, -1, 2, -2);
outThickness.push(t, t, t, t);
outStrokes.push(s, s, s, s);
outColors.push(
c[0],
c[1],
c[2],
c[3],
c[0],
c[1],
c[2],
c[3],
c[0],
c[1],
c[2],
c[3],
c[0],
c[1],
c[2],
c[3]
);
outStrokeColors.push(
sc[0],
sc[1],
sc[2],
sc[3],
sc[0],
sc[1],
sc[2],
sc[3],
sc[0],
sc[1],
sc[2],
sc[3],
sc[0],
sc[1],
sc[2],
sc[3]
);
outPickingColors.push(
p[0],
p[1],
p[2],
p[3],
p[0],
p[1],
p[2],
p[3],
p[0],
p[1],
p[2],
p[3],
p[0],
p[1],
p[2],
p[3]
);
for (let i = 0; i < path.length; i++) {
let cur = path[i];
doubleToTwoFloats(cur as NumberArray2, tempHigh, tempLow);
outVerticesHigh.push(
tempHigh.x,
tempHigh.y,
tempHigh.x,
tempHigh.y,
tempHigh.x,
tempHigh.y,
tempHigh.x,
tempHigh.y
);
outVerticesLow.push(
tempLow.x,
tempLow.y,
tempLow.x,
tempLow.y,
tempLow.x,
tempLow.y,
tempLow.x,
tempLow.y
);
outVerticesHigh2.push(
tempHigh.x,
tempHigh.y,
tempHigh.x,
tempHigh.y,
tempHigh.x,
tempHigh.y,
tempHigh.x,
tempHigh.y
);
outVerticesLow2.push(
tempLow.x,
tempLow.y,
tempLow.x,
tempLow.y,
tempLow.x,
tempLow.y,
tempLow.x,
tempLow.y
);
outOrders.push(1, -1, 2, -2);
outThickness.push(t, t, t, t);
outStrokes.push(s, s, s, s);
outColors.push(
c[0],
c[1],
c[2],
c[3],
c[0],
c[1],
c[2],
c[3],
c[0],
c[1],
c[2],
c[3],
c[0],
c[1],
c[2],
c[3]
);
outStrokeColors.push(
sc[0],
sc[1],
sc[2],
sc[3],
sc[0],
sc[1],
sc[2],
sc[3],
sc[0],
sc[1],
sc[2],
sc[3],
sc[0],
sc[1],
sc[2],
sc[3]
);
outPickingColors.push(
p[0],
p[1],
p[2],
p[3],
p[0],
p[1],
p[2],
p[3],
p[0],
p[1],
p[2],
p[3],
p[0],
p[1],
p[2],
p[3]
);
outIndexes.push(index++, index++, index++, index++);
}
let first: CoordinatesType;
if (isClosed) {
first = path[0];
outIndexes.push(startIndex, startIndex + 1, startIndex + 1, startIndex + 1);
} else {
let p0 = path[path.length - 1],
p1 = path[path.length - 2];
if (!p1) {
p1 = p0;
}
first = [p0[0] + p0[0] - p1[0], p0[1] + p0[1] - p1[1]];
outIndexes.push(index - 1, index - 1, index - 1, index - 1);
}
doubleToTwoFloats(first as NumberArray2, tempHigh, tempLow);
outVerticesHigh.push(
tempHigh.x,
tempHigh.y,
tempHigh.x,
tempHigh.y,
tempHigh.x,
tempHigh.y,
tempHigh.x,
tempHigh.y
);
outVerticesLow.push(tempLow.x, tempLow.y, tempLow.x, tempLow.y, tempLow.x, tempLow.y, tempLow.x, tempLow.y);
outVerticesHigh2.push(
tempHigh.x,
tempHigh.y,
tempHigh.x,
tempHigh.y,
tempHigh.x,
tempHigh.y,
tempHigh.x,
tempHigh.y
);
outVerticesLow2.push(
tempLow.x,
tempLow.y,
tempLow.x,
tempLow.y,
tempLow.x,
tempLow.y,
tempLow.x,
tempLow.y
);
outOrders.push(1, -1, 2, -2);
outThickness.push(t, t, t, t);
outStrokes.push(s, s, s, s);
outColors.push(
c[0],
c[1],
c[2],
c[3],
c[0],
c[1],
c[2],
c[3],
c[0],
c[1],
c[2],
c[3],
c[0],
c[1],
c[2],
c[3]
);
outStrokeColors.push(
sc[0],
sc[1],
sc[2],
sc[3],
sc[0],
sc[1],
sc[2],
sc[3],
sc[0],
sc[1],
sc[2],
sc[3],
sc[0],
sc[1],
sc[2],
sc[3]
);
outPickingColors.push(
p[0],
p[1],
p[2],
p[3],
p[0],
p[1],
p[2],
p[3],
p[0],
p[1],
p[2],
p[3],
p[0],
p[1],
p[2],
p[3]
);
if (j < pathArr.length - 1) {
index += 8;
outIndexes.push(index, index);
}
}
}
public assignHandler(handler: Handler) {
this._handler = handler;
this._isCube = isCubeProj(this._layer._planet!.quadTreeStrategy.projection);
this.refresh();
if (handler.isInitialized()) {
this.update();
}
}
/**
* @public
* @param {Geometry} geometry - Geometry object.
*/
public add(geometry: Geometry) {
//
// Triangulates polygon and sets geometry data.
if (geometry._handlerIndex === -1) {
geometry._handler = this;
geometry._handlerIndex = this._geometries.length;
this._geometries.push(geometry);
let pickingColor = geometry._entity!._pickingColor.scaleTo(1 / 255);
geometry._polyVerticesHighMerc = [];
geometry._polyVerticesLowMerc = [];
geometry._lineVerticesHighMerc = [];
geometry._lineVerticesLowMerc = [];
geometry._polyDirectionsHigh = [];
geometry._polyDirectionsLow = [];
geometry._lineDirectionsHigh = [];
geometry._lineDirectionsLow = [];
const isCube = this._isCube;
if ((geometry._coordinates as IPolygonCoordinates)[0].length) {
if (geometry.type === GeometryTypeEnum.POLYGON) {
let coordinates = (geometry._coordinates as IPolygonCoordinates).map((ring) =>
removeRepeatedPoints(ring, true)
);
if (isCube) {
coordinates = coordinates.map((ring) => densifyPath(ring, true));
}
let ci: IPolygonCoordinates = [];
for (let j = 0; j < coordinates.length; j++) {
ci[j] = [];
for (let k = 0; k < coordinates[j].length; k++) {
ci[j][k] = [
mercator.forward_lon(coordinates[j][k][0]),
mercator.forward_lat(coordinates[j][k][1])
];
}
}
let data = flatten(ci);
let indexes: number[];
let points: CoordinatesType[] = [];
if (isCube) {
({ points, indexes } = triangulateOnGrid(coordinates, DENSIFY_STEP));
pushMercator(points, data.vertices.length * 0.5, data.vertices);
} else {
indexes = earcut(data.vertices, data.holes, 2);
}
geometry._polyVerticesHandlerIndex = this._polyVerticesHighMerc.length;
geometry._polyIndexesHandlerIndex = this._polyIndexes.length;
for (let i = 0; i < indexes.length; i++) {
this._polyIndexes.push(indexes[i] + geometry._polyVerticesHandlerIndex * 0.5);
}
let color = geometry._style.fillColor;
let verticesHigh = [],
verticesLow = [];
for (let i = 0; i < data.vertices.length * 0.5; i++) {
this._polyColors.push(color.x, color.y, color.z, color.w);
this._polyPickingColors.push(pickingColor.x, pickingColor.y, pickingColor.z, 1.0);
}
for (let i = 0; i < data.vertices.length; i++) {
doubleToTwoFloatsV2(data.vertices[i], tempHighLow);
verticesHigh[i] = tempHighLow.x;
verticesLow[i] = tempHighLow.y;
}
geometry._polyVerticesHighMerc = verticesHigh;
geometry._polyVerticesLowMerc = verticesLow;
concatArrays(this._polyVerticesHighMerc, verticesHigh);
concatArrays(this._polyVerticesLowMerc, verticesLow);
if (isCube) {
GeometryHandler.appendPolygonDirections(
[points],
geometry._polyDirectionsHigh,
geometry._polyDirectionsLow
);
concatArrays(this._polyDirectionsHigh, geometry._polyDirectionsHigh);
concatArrays(this._polyDirectionsLow, geometry._polyDirectionsLow);
}
geometry._polyVerticesLength = data.vertices.length;
geometry._polyIndexesLength = indexes.length;
// Creates polygon stroke data
geometry._lineVerticesHandlerIndex = this._lineVerticesHighMerc.length;
geometry._lineOrdersHandlerIndex = this._lineOrders.length;
geometry._lineIndexesHandlerIndex = this._lineIndexes.length;
geometry._lineColorsHandlerIndex = this._lineColors.length;
geometry._lineThicknessHandlerIndex = this._lineThickness.length;
if (isCube) {
GeometryHandler.appendLineDirections(
coordinates,
true,
this._lineDirectionsHigh,
this._lineDirectionsLow,
geometry._lineDirectionsHigh,
geometry._lineDirectionsLow
);
}
GeometryHandler.appendLineData(
ci,
true,
geometry._style.lineColor,
pickingColor,
geometry._style.lineWidth,
geometry._style.strokeColor,
geometry._style.strokeWidth,
this._lineVerticesHighMerc,
this._lineVerticesLowMerc,
this._lineOrders,
this._lineIndexes,
this._lineColors,
this._linePickingColors,
this._lineThickness,
this._lineStrokeColors,
this._lineStrokes,
geometry._lineVerticesHighMerc,
geometry._lineVerticesLowMerc
);
geometry._lineVerticesLength =
this._lineVerticesHighMerc.length - geometry._lineVerticesHandlerIndex;
geometry._lineOrdersLength = this._lineOrders.length - geometry._lineOrdersHandlerIndex;
geometry._lineIndexesLength = this._lineIndexes.length - geometry._lineIndexesHandlerIndex;
geometry._lineColorsLength = this._lineColors.length - geometry._lineColorsHandlerIndex;
geometry._lineThicknessLength = this._lineThickness.length - geometry._lineThicknessHandlerIndex;
} else if (geometry.type === GeometryTypeEnum.MULTIPOLYGON) {
let coordinates = (geometry._coordinates as IMultiPolygonCoordinates).map((polygon) =>
polygon.map((ring) => removeRepeatedPoints(ring, true))
);
if (isCube) {
coordinates = coordinates.map((polygon) => polygon.map((ring) => densifyPath(ring, true)));
}
let vertices: number[] = [],
indexes: number[] = [];
// Creates polygon stroke data
geometry._lineVerticesHandlerIndex = this._lineVerticesHighMerc.length;
geometry._lineOrdersHandlerIndex = this._lineOrders.length;
geometry._lineIndexesHandlerIndex = this._lineIndexes.length;
geometry._lineColorsHandlerIndex = this._lineColors.length;
geometry._lineThicknessHandlerIndex = this._lineThickness.length;
for (let i = 0; i < coordinates.length; i++) {
let cci: CoordinatesType[][] = coordinates[i];
let ci: CoordinatesType[][] = [];
for (let j = 0; j < cci.length; j++) {
ci[j] = [];
for (let k = 0; k < coordinates[i][j].length; k++) {
ci[j][k] = [mercator.forward_lon(cci[j][k][0]), mercator.forward_lat(cci[j][k][1])];
}
}
let data = flatten(ci);
let dataIndexes: number[];
let points: CoordinatesType[] = [];
if (isCube) {
({ points, indexes: dataIndexes } = triangulateOnGrid(cci, DENSIFY_STEP));
pushMercator(points, data.vertices.length * 0.5, data.vertices);
} else {
dataIndexes = earcut(data.vertices, data.holes, 2);
}
for (let j = 0; j < dataIndexes.length; j++) {
indexes.push(dataIndexes[j] + vertices.length * 0.5);
}
concatArrays(vertices, data.vertices);
if (isCube) {
GeometryHandler.appendLineDirections(
cci,
true,
this._lineDirectionsHigh,
this._lineDirectionsLow,
geometry._lineDirectionsHigh,
geometry._lineDirectionsLow
);
}
if (isCube) {
GeometryHandler.appendPolygonDirections(
[points],
geometry._polyDirectionsHigh,
geometry._polyDirectionsLow
);
}
GeometryHandler.appendLineData(
ci,
true,
geometry._style.lineColor,
pickingColor,
geometry._style.lineWidth,
geometry._style.strokeColor,
geometry._style.strokeWidth,
this._lineVerticesHighMerc,
this._lineVerticesLowMerc,
this._lineOrders,
this._lineIndexes,
this._lineColors,
this._linePickingColors,
this._lineThickness,
this._lineStrokeColors,
this._lineStrokes,
geometry._lineVerticesHighMerc,
geometry._lineVerticesLowMerc
);
}
geometry._polyVerticesHandlerIndex = this._polyVerticesHighMerc.length;
geometry._polyIndexesHandlerIndex = this._polyIndexes.length;
for (let i = 0; i < indexes.length; i++) {
this._polyIndexes.push(indexes[i] + geometry._polyVerticesHandlerIndex * 0.5);
}
let color = geometry._style.fillColor;
let verticesHigh = [],
verticesLow = [];
for (let i = 0; i < vertices.length * 0.5; i++) {
this._polyColors.push(color.x, color.y, color.z, color.w);
this._polyPickingColors.push(pickingColor.x, pickingColor.y, pickingColor.z, 1.0);
}
for (let i = 0; i < vertices.length; i++) {
doubleToTwoFloatsV2(vertices[i], tempHighLow);
verticesHigh[i] = tempHighLow.x;
verticesLow[i] = tempHighLow.y;
}
geometry._polyVerticesHighMerc = verticesHigh;
geometry._polyVerticesLowMerc = verticesLow;
concatArrays(this._polyVerticesHighMerc, verticesHigh);
concatArrays(this._polyVerticesLowMerc, verticesLow);
if (isCube) {
concatArrays(this._polyDirectionsHigh, geometry._polyDirectionsHigh);
concatArrays(this._polyDirectionsLow, geometry._polyDirectionsLow);
}
geometry._polyVerticesLength = vertices.length;
geometry._polyIndexesLength = indexes.length;
geometry._lineVerticesLength =
this._lineVerticesHighMerc.length - geometry._lineVerticesHandlerIndex;
geometry._lineOrdersLength = this._lineOrders.length - geometry._lineOrdersHandlerIndex;
geometry._lineIndexesLength = this._lineIndexes.length - geometry._lineIndexesHandlerIndex;
geometry._lineColorsLength = this._lineColors.length - geometry._lineColorsHandlerIndex;
geometry._lineThicknessLength = this._lineThickness.length - geometry._lineThicknessHandlerIndex;
} else if (geometry.type === GeometryTypeEnum.LINESTRING) {
let coordinates = removeRepeatedPoints(geometry._coordinates as ILineStringCoordinates, false);
if (isCube) {
coordinates = densifyPath(coordinates, false);
}
let ci = new Array(coordinates.length);
for (let j = 0; j < coordinates.length; j++) {
ci[j] = [mercator.forward_lon(coordinates[j][0]), mercator.forward_lat(coordinates[j][1])];
}
// Creates polygon stroke data
geometry._lineVerticesHandlerIndex = this._lineVerticesHighMerc.length;
geometry._lineOrdersHandlerIndex = this._lineOrders.length;
geometry._lineIndexesHandlerIndex = this._lineIndexes.length;
geometry._lineColorsHandlerIndex = this._lineColors.length;
geometry._lineThicknessHandlerIndex = this._lineThickness.length;
if (isCube) {
GeometryHandler.appendLineDirections(
[coordinates],
false,
this._lineDirectionsHigh,
this._lineDirectionsLow,
geometry._lineDirectionsHigh,
geometry._lineDirectionsLow
);
}
GeometryHandler.appendLineData(
[ci],
false,
geometry._style.lineColor,
pickingColor,
geometry._style.lineWidth,
geometry._style.strokeColor,
geometry._style.strokeWidth,
this._lineVerticesHighMerc,
this._lineVerticesLowMerc,
this._lineOrders,
this._lineIndexes,
this._lineColors,
this._linePickingColors,
this._lineThickness,
this._lineStrokeColors,
this._lineStrokes,
geometry._lineVerticesHighMerc,
geometry._lineVerticesLowMerc
);
geometry._lineVerticesLength =
this._lineVerticesHighMerc.length - geometry._lineVerticesHandlerIndex;
geometry._lineOrdersLength = this._lineOrders.length - geometry._lineOrdersHandlerIndex;
geometry._lineIndexesLength = this._lineIndexes.length - geometry._lineIndexesHandlerIndex;
geometry._lineColorsLength = this._lineColors.length - geometry._lineColorsHandlerIndex;
geometry._lineThicknessLength = this._lineThickness.length - geometry._lineThicknessHandlerIndex;
} else if (geometry.type === GeometryTypeEnum.MULTILINESTRING) {
let coordinates = (geometry._coordinates as IMultiLineStringCoordinates).map((line) =>
removeRepeatedPoints(line, false)
);
if (isCube) {
coordinates = coordinates.map((line) => densifyPath(line, false));
}
let ci: IMultiLineStringCoordinates = [];
for (let j = 0; j < coordinates.length; j++) {
ci[j] = [];
for (let k = 0; k < coordinates[j].length; k++) {
ci[j][k] = [
mercator.forward_lon(coordinates[j][k][0]),
mercator.forward_lat(coordinates[j][k][1])
];
}
}
// Creates polygon stroke data
geometry._lineVerticesHandlerIndex = this._lineVerticesHighMerc.length;
geometry._lineOrdersHandlerIndex = this._lineOrders.length;
geometry._lineIndexesHandlerIndex = this._lineIndexes.length;
geometry._lineColorsHandlerIndex = this._lineColors.length;
geometry._lineThicknessHandlerIndex = this._lineThickness.length;
if (isCube) {
GeometryHandler.appendLineDirections(
coordinates,
false,
this._lineDirectionsHigh,
this._lineDirectionsLow,
geometry._lineDirectionsHigh,
geometry._lineDirectionsLow
);
}
GeometryHandler.appendLineData(
ci,
false,
geometry._style.lineColor,
pickingColor,
geometry._style.lineWidth,
geometry._style.strokeColor,
geometry._style.strokeWidth,
this._lineVerticesHighMerc,
this._lineVerticesLowMerc,
this._lineOrders,
this._lineIndexes,
this._lineColors,
this._linePickingColors,
this._lineThickness,
this._lineStrokeColors,
this._lineStrokes,
geometry._lineVerticesHighMerc,
geometry._lineVerticesLowMerc
);
geometry._lineVerticesLength =
this._lineVerticesHighMerc.length - geometry._lineVerticesHandlerIndex;
geometry._lineOrdersLength = this._lineOrders.length - geometry._lineOrdersHandlerIndex;
geometry._lineIndexesLength = this._lineIndexes.length - geometry._lineIndexesHandlerIndex;
geometry._lineColorsLength = this._lineColors.length - geometry._lineColorsHandlerIndex;
geometry._lineThicknessLength = this._lineThickness.length - geometry._lineThicknessHandlerIndex;
}
}
// Refresh visibility
this.setGeometryVisibility(geometry);
!this._updatedGeometry[geometry.__id] && this._updatedGeometryArr.push(geometry);
this._updatedGeometry[geometry.__id] = true;
this.refresh();
}
}
/**
* Removes one geometry. Works in any array order, including after bringToFront(): every range
* after the removed one in an array moves down, and every index past its vertices goes down
* by its vertex count. Nothing moves when its ranges are at the ends of the arrays.
* @public
* @param {Geometry} geometry - Geometry to remove; ignored if it belongs to another handler.
*/
public remove(geometry: Geometry) {
const index = geometry._handlerIndex;
if (geometry._handler !== this || index === -1) {
return;
}
const all = this._geometries;
all.splice(index, 1);
for (let i = index; i < all.length; i++) {
all[i]._handlerIndex = i;
}
// A length of -1 means the geometry has no data of that kind, e.g. a line string has no polygon.
const pv = geometry._polyVerticesHandlerIndex,
pvl = geometry._polyVerticesLength,
pi = geometry._polyIndexesHandlerIndex,
pil = geometry._polyIndexesLength,
lv = geometry._lineVerticesHandlerIndex,
lvl = geometry._lineVerticesLength,
lo = geometry._lineOrdersHandlerIndex,
lol = geometry._lineOrdersLength,
lc = geometry._lineColorsHandlerIndex,
lcl = geometry._lineColorsLength,
lt = geometry._lineThicknessHandlerIndex,
ltl = geometry._lineThicknessLength,
li = geometry._lineIndexesHandlerIndex,
lil = geometry._lineIndexesLength;
// Whether anything follows the removed range in its arrays
let shift = false;
if (pil > 0) {
shift ||= pi + pil < this._polyIndexes.length;
this._polyIndexes.splice(pi, pil);
}
if (pvl > 0) {
if (pv + pvl < this._polyVerticesHighMerc.length) {
shift = true;
const first = pv * 0.5,
n = pvl * 0.5,
a = this._polyIndexes;
for (let i = 0; i < a.length; i++) {
if (a[i] >= first) a[i] -= n;
}
}
this._polyVerticesHighMerc.splice(pv, pvl);
this._polyVerticesLowMerc.splice(pv, pvl);
if (this._isCube) {
this._polyDirectionsHigh.splice(pv * 1.5, pvl * 1.5);
this._polyDirectionsLow.splice(pv * 1.5, pvl * 1.5);
}
this._polyColors.splice(pv * 2, pvl * 2);
this._polyPickingColors.splice(pv * 2, pvl * 2);
}
if (lil > 0) {
shift ||= li + lil < this._lineIndexes.length;
this._lineIndexes.splice(li, lil);
}
if (lvl > 0) {
if (lv + lvl < this._lineVerticesHighMerc.length) {
shift = true;
const first = lv * 0.5,
n = lvl * 0.5,
a = this._lineIndexes;
for (let i = 0; i < a.length; i++) {
if (a[i] >= first) a[i] -= n;
}
}
this._lineVerticesHighMerc.splice(lv, lvl);
this._lineVerticesLowMerc.splice(lv, lvl);
if (this._isCube) {
this._lineDirectionsHigh.splice(lv * 1.5, lvl * 1.5);
this._lineDirectionsLow.splice(lv * 1.5, lvl * 1.5);
}
}
if (lol > 0) {
shift ||= lo + lol < this._lineOrders.length;
this._lineOrders.splice(lo, lol);
}
if (lcl > 0) {
shift ||= lc + lcl < this._lineColors.length;
this._lineColors.splice(lc, lcl);
this._linePickingColors.splice(lc, lcl);
this._lineStrokeColors.splice(lc, lcl);
}
if (ltl > 0) {
shift ||= lt + ltl < this._lineThickness.length;
this._lineThickness.splice(lt, ltl);
this._lineStrokes.splice(lt, ltl);
}
if (shift) {
// Offsets of -1 (no data of that kind) are never above a real offset.
for (let i = 0; i < all.length; i++) {
const g = all[i];
if (pvl > 0 && g._polyVerticesHandlerIndex > pv) g._polyVerticesHandlerIndex -= pvl;
if (pil > 0 && g._polyIndexesHandlerIndex > pi) g._polyIndexesHandlerIndex -= pil;
if (lvl > 0 && g._lineVerticesHandlerIndex > lv) g._lineVerticesHandlerIndex -= lvl;
if (lol > 0 && g._lineOrdersHandlerIndex > lo) g._lineOrdersHandlerIndex -= lol;
if (lcl > 0 && g._lineColorsHandlerIndex > lc) g._lineColorsHandlerIndex -= lcl;
if (ltl > 0 && g._lineThicknessHandlerIndex > lt) g._lineThicknessHandlerIndex -= ltl;
if (lil > 0 && g._lineIndexesHandlerIndex > li) g._lineIndexesHandlerIndex -= lil;
}
}
this._detachGeometry(geometry);
this.refresh();
}
/**
* Removes geometries with one compaction pass over the shared arrays.
* Works in any array order, including after bringToFront().
* @public
* @param {Geometry[]} geometries - Geometries to remove; others are ignored.
*/
public removeGeometries(geometries: Geometry[]) {
const removed = new Set<Geometry>();
for (let i = 0; i < geometries.length; i++) {
const g = geometries[i];
if (g._handler === this && g._handlerIndex !== -1) {
removed.add(g);
}
}
if (!removed.size) return;
const all = this._geometries;
// Moves each kept geometry's range down to the write position. Ranges are visited in
// array order, which differs from the geometry list after bringToFront(). Arrays in one
// group share the geometry's offset field; scale is the number of values per offset unit.
// Index arrays also subtract how far their geometry's vertices moved.
// Returns how far each moved range went.
const compact = (
arrays: [number[], number][],
offsetKey: GeometryOffsetKey,
lengthKey: GeometryLengthKey,
verticesMoved?: Map<Geometry, number>
) => {
const moved = new Map<Geometry, number>();
const ranges: Geometry[] = [];
let sorted = true;
for (let k = 0; k < all.length; k++) {
const g = all[k];
// The geometry has no data of this kind
if (g[lengthKey] < 0) {
continue;
}
if (ranges.length && g[offsetKey] < ranges[ranges.length - 1][offsetKey]) {
sorted = false;
}
ranges.push(g);
}
if (!ranges.length) {
return moved;
}
if (!sorted) {
ranges.sort((a, b) => a[offsetKey] - b[offsetKey]);
}
let write = ranges[0][offsetKey];
for (let k = 0; k < ranges.length; k++) {
const g = ranges[k];
if (removed.has(g)) continue;
const offset = g[offsetKey],
length = g[lengthKey],
shift = verticesMoved ? (verticesMoved.get(g) || 0) * 0.5 : 0;
if (offset !== write || shift) {
for (const [a, scale] of arrays) {
const from = offset * scale,
to = write * scale,
n = length * scale;
for (let i = 0; i < n; i++) {
a[to + i] = a[from + i] - shift;
}
}
offset !== write && moved.set(g, offset - write);
}
g[offsetKey] = write;
write += length;
}
for (const [a, scale] of arrays) {
a.length = write * scale;
}
return moved;
};
const directions = (high: number[], low: number[]): [number[], number][] =>
this._isCube
? [
[high, 1.5],
[low, 1.5]
]
: [];
const polyVerticesMoved = compact(
[
[this._polyVerticesHighMerc, 1],
[this._polyVerticesLowMerc, 1],
...directions(this._polyDirectionsHigh, this._polyDirectionsLow),
[this._polyColors, 2],
[this._polyPickingColors, 2]
],
"_polyVerticesHandlerIndex",
"_polyVerticesLength"
);
compact([[this._polyIndexes, 1]], "_polyIndexesHandlerIndex", "_polyIndexesLength", polyVerticesMoved);
const lineVerticesMoved = compact(
[
[this._lineVerticesHighMerc, 1],
[this._lineVerticesLowMerc, 1],
...directions(this._lineDirectionsHigh, this._lineDirectionsLow)
],
"_lineVerticesHandlerIndex",
"_lineVerticesLength"
);
compact([[this._lineOrders, 1]], "_lineOrdersHandlerIndex", "_lineOrdersLength");
compact(
[
[this._lineColors, 1],
[this._linePickingColors, 1],
[this._lineStrokeColors, 1]
],
"_lineColorsHandlerIndex",
"_lineColorsLength"
);
compact(
[
[this._lineThickness, 1],
[this._lineStrokes, 1]
],
"_lineThicknessHandlerIndex",
"_lineThicknessLength"
);
compact([[this._lineIndexes, 1]], "_lineIndexesHandlerIndex", "_lineIndexesLength", lineVerticesMoved);
// Nothing before the first removed geometry moves in the list.
let start = all.length;
for (const g of removed) {
start = Math.min(start, g._handlerIndex);
}
let j = start;
for (let k = start; k < all.length; k++) {
const g = all[k];
if (!removed.has(g)) {
g._handlerIndex = j;
all[j++] = g;
}
}
all.length = j;
for (const g of removed) {
this._detachGeometry(g);
}
this.refresh();
}
protected _detachGeometry(geometry: Geometry) {
geometry._pickingReady = false;
geometry._handler = null;
geometry._handlerIndex = -1;
geometry._polyVerticesHighMerc = [];
geometry._polyVerticesLowMerc = [];
geometry._polyDirectionsHigh = [];
geometry._polyDirectionsLow = [];
geometry._polyVerticesLength = -1;
geometry._polyIndexesLength = -1;
geometry._polyVerticesHandlerIndex = -1;
geometry._polyIndexesHandlerIndex = -1;
geometry._lineVerticesHighMerc = [];
geometry._lineVerticesLowMerc = [];
geometry._lineDirectionsHigh = [];
geometry._lineDirectionsLow = [];
geometry._lineVerticesLength = -1;
geometry._lineOrdersLength = -1;
geometry._lineIndexesLength = -1;
geometry._lineColorsLength = -1;
geometry._lineThicknessLength = -1;
geometry._lineVerticesHandlerIndex = -1;
geometry._lineOrdersHandlerIndex = -1;
geometry._lineIndexesHandlerIndex = -1;
geometry._lineThicknessHandlerIndex = -1;
geometry._lineColorsHandlerIndex = -1;
!this._removeGeometryExtents[geometry.__id] && this._removeGeometryExtentArr.push(geometry.getExtent());
this._removeGeometryExtents[geometry.__id] = true;
}
protected _refreshRecursevely(geometry: Geometry, treeNode: Node) {
if (treeNode.ready) {
let lid = this._layer._id;
for (let i = 0; i < treeNode.nodes.length; i++) {
let ni = treeNode.nodes[i];
if (geometry.overlaps(ni.segment.getExtentLonLat())) {
this._refreshRecursevely(geometry, ni);
let m = ni.segment.materials[lid];
if (m && m.isReady) {
if (!m.segment.node.isRenderedOrFading()) {
m.layer.clearMaterial(m);
} else {
m.pickingReady = m.pickingReady && geometry._pickingReady;
m.isReady = false;
m._updateTexture = m.texture;
m._updatePickingMask = m.pickingMask;
}
geometry._pickingReady = true;
}
}
}
}
}
protected _refreshRecursevelyExt(extent: Extent, treeNode: Node) {
if (treeNode.ready) {
let lid = this._layer.__id;
for (let i = 0; i < treeNode.nodes.length; i++) {
let ni = treeNode.nodes[i];
if (extent.overlaps(ni.segment.getExtentLonLat())) {
this._refreshRecursevelyExt(extent, ni);
let m = ni.segment.materials[lid];
if (m && m.isReady) {
if (!m.textureExists || !m.segment.node.isRenderedOrFading()) {
m.layer.clearMaterial(m);
} else {
m.pickingReady = false;
m.isReady = false;
m._updateTexture = m.texture;
m._updatePickingMask = m.pickingMask;
}
}
}
}
}
}
protected _refreshPlanetNode(treeNode: Node) {
let i;
let e = this._removeGeometryExtentArr;
for (i = 0; i < e.length; i++) {
this._refreshRecursevelyExt(e[i], treeNode);
}
let g = this._updatedGeometryArr;
for (i = 0; i < g.length; i++) {
this._refreshRecursevely(g[i], treeNode);
}
}
protected _updatePlanet() {
let p = this._layer._planet;
if (p) {
let ql = p.quadTreeStrategy.quadTreeList;
for (let i = 0; i < ql.length; i++) {
this._refreshPlanetNode(ql[i]);
}
// p.quadTreeStrategy.quadTreeList.forEach((quadTree: Node) => {
// this._refreshPlanetNode(quadTree);
// });
}
this._updatedGeometryArr.length = 0;
this._updatedGeometryArr = [];
this._updatedGeometry = {};
this._removeGeometryExtentArr.length = 0;
this._removeGeometryExtentArr = [];
this._removeGeometryExtents = {};
}
protected refresh() {
let i = this._changedBuffers.length;
while (i--) {
this._setChangedBuffer(i);
}
}
protected _setChangedBuffer(index: number) {
this._changedBuffers[index] = true;
this._handler && (this._handler.needRedraw = true);
}
public update() {
if (this._handler) {
let needUpdate = false;
let i = this._changedBuffers.length;
while (i--) {
if (this._changedBuffers[i]) {
needUpdate = true;
this._buffersUpdateCallbacks[i].call(this);
this._changedBuffers[i] = false;
}
}
needUpdate && this._updatePlanet();
}
}
public setGeometryVisibility(geometry: Geometry) {
let v = geometry.getVisibility() ? 1.0 : 0.0;
let a = this._polyVerticesHighMerc,
b = this._polyVerticesLowMerc;
let l = geometry._polyVerticesLength;
let ind = geometry._polyVerticesHandlerIndex;
for (let i = 0; i < l; i++) {
a[ind + i] = geometry._polyVerticesHighMerc[i] * v;
b[ind + i] = geometry._polyVerticesLowMerc[i] * v;
}
a = this._lineVerticesHighMerc;
b = this._lineVerticesLowMerc;
l = geometry._lineVerticesLength;
ind = geometry._lineVerticesHandlerIndex;
for (let i = 0; i < l; i++) {
a[ind + i] = geometry._lineVerticesHighMerc[i] * v;
b[ind + i] = geometry._lineVerticesLowMerc[i] * v;
}
if (this._isCube) {
const hide = (to: number[], from: number[], index: number) => {
for (let i = 0; i < from.length; i++) {
to[index + i] = from[i] * v;
}
};
hide(this._polyDirectionsHigh, geometry._polyDirectionsHigh, geometry._polyVerticesHandlerIndex * 1.5);
hide(this._polyDirectionsLow, geometry._polyDirectionsLow, geometry._polyVerticesHandlerIndex * 1.5);
hide(this._lineDirectionsHigh, geometry._lineDirectionsHigh, geometry._lineVerticesHandlerIndex * 1.5);
hide(this._lineDirectionsLow, geometry._lineDirectionsLow, geometry._lineVerticesHandlerIndex * 1.5);
}
this._setChangedBuffer(POLYVERTICES_BUFFER);
this._setChangedBuffer(LINEVERTICES_BUFFER);
!this._updatedGeometry[geometry.__id] && this._updatedGeometryArr.push(geometry);
this._updatedGeometry[geometry.__id] = true;
}
public setPolyColorArr(geometry: Geometry, color: Vec4) {
let index = geometry._polyVerticesHandlerIndex * 2, // ... / 2 * 4
size = index + geometry._polyVerticesLength * 2; // ... / 2 * 4
let a = this._polyColors;
for (let i = index; i < size; i += 4) {
a[i] = color.x;
a[i + 1] = color.y;
a[i + 2] = color.z;
a[i + 3] = color.w;
}
this._setChangedBuffer(POLYCOLORS_BUFFER);
!this._updatedGeometry[geometry.__id] && this._updatedGeometryArr.push(geometry);
this._updatedGeometry[geometry.__id] = true;
}
public setLineStrokeColorArr(geometry: Geometry, color: Vec4) {
let index = geometry._lineColorsHandlerIndex,
size = index + geometry._lineColorsLength;
let a = this._lineStrokeColors;
for (let i = index; i < size; i += 4) {
a[i] = color.x;
a[i + 1] = color.y;
a[i + 2] = color.z;
a[i + 3] = color.w;
}
this._setChangedBuffer(LINESTROKECOLORS_BUFFER);
!this._updatedGeometry[geometry.__id] && this._updatedGeometryArr.push(geometry);
this._updatedGeometry[geometry.__id] = true;
}
public setLineColorArr(geometry: Geometry, color: Vec4) {
let index = geometry._lineColorsHandlerIndex,
size = index + geometry._lineColorsLength;
let a = this._lineColors;
for (let i = index; i < size; i += 4) {
a[i] = color.x;
a[i + 1] = color.y;
a[i + 2] = color.z;
a[i + 3] = color.w;
}
this._setChangedBuffer(LINECOLORS_BUFFER);
!this._updatedGeometry[geometry.__id] && this._updatedGeometryArr.push(geometry);
this._updatedGeometry[geometry.__id] = true;
}
public setLineStrokeArr(geometry: Geometry, width: number) {
// let index = geometry._lineStrokesHandlerIndex,
// size = index + geometry._lineStrokesLength;
// let a = this._lineStrokes;
// for (let i = index; i < size; i++) {
// a[i] = width;
// }
// this._changedBuffers[LINESTROKES_BUFFER] = true;
// !this._updatedGeometry[geometry.__id] && this._updatedGeometryArr.push(geometry);
// this._updatedGeometry[geometry.__id] = true;
}
public setLineThicknessArr(geometry: Geometry, width: number) {
let index = geometry._lineThicknessHandlerIndex,
size = index + geometry._lineThicknessLength;
let a = this._lineThickness;
for (let i = index; i < size; i++) {
a[i] = width;
}
this._setChangedBuffer(LINETHICKNESS_BUFFER);
!this._updatedGeometry[geometry.__id] && this._updatedGeometryArr.push(geometry);
this._updatedGeometry[geometry.__id] = true;
}
public bringToFront(geometry: Geometry) {
// A length of -1 means the geometry has no data of that kind, e.g. a line string has no polygon.
const polyLength = geometry._polyIndexesLength,
lineLength = geometry._lineIndexesLength;
let polyIndexes =
polyLength >= 0 ? this._polyIndexes.splice(geometry._polyIndexesHandlerIndex, polyLength) : [];
let lineIndexes =
lineLength >= 0 ? this._lineIndexes.splice(geometry._lineIndexesHandlerIndex, lineLength) : [];
this._geometries.splice(geometry._handlerIndex, 1);
let g = this._geometries;
for (let i = geometry._handlerIndex; i < g.length; i++) {
let gi = g[i];
gi._handlerIndex = i;
if (polyLength > 0 && gi._polyIndexesLength >= 0) {
gi._polyIndexesHandlerIndex -= polyLength;
}
if (lineLength > 0 && gi._lineIndexesLength >= 0) {
gi._lineIndexesHandlerIndex -= lineLength;
}
}
if (polyLength >= 0) {
geometry._polyIndexesHandlerIndex = this._polyIndexes.length;
}
if (lineLength >= 0) {
geometry._lineIndexesHandlerIndex = this._lineIndexes.length;
}
geometry._handlerIndex = this._geometries.length;
this._geometries.push(geometry);
concatArrays(this._polyIndexes, polyIndexes);
concatArrays(this._lineIndexes, lineIndexes);
this._setChangedBuffer(POLYINDEXES_BUFFER);
this._setChangedBuffer(LINEINDEXES_BUFFER);
!this._updatedGeometry[geometry.__id] && this._updatedGeometryArr.push(geometry);
this._updatedGeometry[geometry.__id] = true;
}
public createPolyVerticesBuffer() {
let h = this._handler!;
h.gl!.deleteBuffer(this._polyVerticesHighBufferMerc!);
this._polyVerticesHighBufferMerc = h.createArrayBuffer(
new Float32Array(this._polyVerticesHighMerc),
2,
this._polyVerticesHighMerc.length / 2
);
h.gl!.deleteBuffer(this._polyVerticesLowBufferMerc!);
this._polyVerticesLowBufferMerc = h.createArrayBuffer(
new Float32Array(this._polyVerticesLowMerc),
2,
this._polyVerticesLowMerc.length / 2
);
if (this._isCube) {
h.gl!.deleteBuffer(this._polyDirectionsHighBuffer!);
h.gl!.deleteBuffer(this._polyDirectionsLowBuffer!);
this._polyDirectionsHighBuffer = h.createArrayBuffer(
new Float32Array(this._polyDirectionsHigh),
3,
this._polyDirectionsHigh.length / 3
);
this._polyDirectionsLowBuffer = h.createArrayBuffer(
new Float32Array(this._polyDirectionsLow),
3,
this._polyDirectionsLow.length / 3
);
}
}
public createPolyIndexesBuffer() {
let h = this._handler!;
h.gl!.deleteBuffer(this._polyIndexesBuffer!);
this._polyIndexesBuffer = h.createElementArrayBuffer(
new Uint32Array(this._polyIndexes),
1,
this._polyIndexes.length
);
}
public createPolyColorsBuffer() {
let h = this._handler!;
h.gl!.deleteBuffer(this._polyColorsBuffer!);
this._polyColorsBuffer = h.createArrayBuffer(
new Float32Array(this._polyColors),
4,
this._polyColors.length / 4
);
}
public createPolyPickingColorsBuffer() {
let h = this._handler!;
h.gl!.deleteBuffer(this._polyPickingColorsBuffer!);
this._polyPickingColorsBuffer = h.createArrayBuffer(
new Float32Array(this._polyPickingColors),
4,
this._polyPickingColors.length / 4
);
}
public createLineVerticesBuffer() {
let h = this._handler!;
h.gl!.deleteBuffer(this._lineVerticesHighBufferMerc!);
this._lineVerticesHighBufferMerc = h.createArrayBuffer(
new Float32Array(this._lineVerticesHighMerc),
2,
this._lineVerticesHighMerc.length / 2
);
h.gl!.deleteBuffer(this._lineVerticesLowBufferMerc!);
this._lineVerticesLowBufferMerc = h.createArrayBuffer(
new Float32Array(this._lineVerticesLowMerc),
2,
this._lineVerticesLowMerc.length / 2
);
if (this._isCube) {
h.gl!.deleteBuffer(this._lineDirectionsHighBuffer!);
h.gl!.deleteBuffer(this._lineDirectionsLowBuffer!);
this._lineDirectionsHighBuffer = h.createArrayBuffer(
new Float32Array(this._lineDirectionsHigh),
3,
this._lineDirectionsHigh.length / 3
);
this._lineDirectionsLowBuffer = h.createArrayBuffer(
new Float32Array(this._lineDirectionsLow),
3,
this._lineDirectionsLow.length / 3
);
}
}
public createLineIndexesBuffer() {
let h = this._handler!;
h.gl!.deleteBuffer(this._lineIndexesBuffer!);
this._lineIndexesBuffer = h.createElementArrayBuffer(
new Uint32Array(this._lineIndexes),
1,
this._lineIndexes.length
);
}
public createLineOrdersBuffer() {
let h = this._handler!;
h.gl!.deleteBuffer(this._lineOrdersBuffer!);
this._lineOrdersBuffer = h.createArrayBuffer(
new Float32Array(this._lineOrders),
1,
this._lineOrders.length / 2
);
}
public createLineColorsBuffer() {
let h = this._handler!;
h.gl!.deleteBuffer(this._lineColorsBuffer!);
this._lineColorsBuffer = h.createArrayBuffer(
new Float32Array(this._lineColors),
4,
this._lineColors.length / 4
);
}
public createLinePickingColorsBuffer() {
let h = this._handler!;
h.gl!.deleteBuffer(this._linePickingColorsBuffer!);
this._linePickingColorsBuffer = h.createArrayBuffer(
new Float32Array(this._linePickingColors),
4,
this._linePickingColors.length / 4
);
}
public createLineThicknessBuffer() {
let h = this._handler!;
h.gl!.deleteBuffer(this._lineThicknessBuffer!);
this._lineThicknessBuffer = h.createArrayBuffer(
new Float32Array(this._lineThickness),
1,
this._lineThickness.length
);
}
public createLineStrokesBuffer() {
let h = this._handler!;
h.gl!.deleteBuffer(this._lineStrokesBuffer!);
this._lineStrokesBuffer = h.createArrayBuffer(new Float32Array(this._lineStrokes), 1, this._lineStrokes.length);
}
public createLineStrokeColorsBuffer() {
let h = this._handler!;
h.gl!.deleteBuffer(this._lineStrokeColorsBuffer!);
this._lineStrokeColorsBuffer = h.createArrayBuffer(
new Float32Array(this._lineStrokeColors),
4,
this._lineStrokeColors.length / 4
);
}
public clear() {
this._geometries = [];
this._polyVerticesHighMerc = [];
this._polyVerticesLowMerc = [];
this._polyDirectionsHigh = [];
this._polyDirectionsLow = [];
this._polyIndexes = [];
this._polyColors = [];
this._polyPickingColors = [];
this._lineVerticesHighMerc = [];
this._lineVerticesLowMerc = [];
this._lineDirectionsHigh = [];
this._lineDirectionsLow = [];
this._lineOrders = [];
this._lineIndexes = [];
this._lineColors = [];
this._linePickingColors = [];
this._lineThickness = [];
this._lineStrokeColors = [];
this._lineStrokes = [];
this._deleteBuffers();
this._polyVerticesHighBufferMerc = null;
this._polyVerticesLowBufferMerc = null;
this._polyDirectionsHighBuffer = null;
this._polyDirectionsLowBuffer = null;
this._polyIndexesBuffer = null;
this._polyColorsBuffer = null;
this._polyPickingColorsBuffer = null;
this._lineVerticesHighBufferMerc = null;
this._lineVerticesLowBufferMerc = null;
this._lineDirectionsHighBuffer = null;
this._lineDirectionsLowBuffer = null;
this._lineIndexesBuffer = null;
this._lineOrdersBuffer = null;
this._lineColorsBuffer = null;
this._linePickingColorsBuffer = null;
this._lineThicknessBuffer = null;
this._lineStrokeColorsBuffer = null;
this._lineStrokesBuffer = null;
this._updatedGeometryArr = [];
this._updatedGeometry = {};
this._removeGeometryExtentArr = [];
this._removeGeometryExtents = {};
this.refresh();
}
public _deleteBuffers() {
if (this._layer._planet && this._layer._planet.renderer) {
let h = this._layer._planet.renderer.handler,
gl = h.gl!;
if (gl) {
gl.deleteBuffer(this._polyVerticesHighBufferMerc!);
gl.deleteBuffer(this._polyVerticesLowBufferMerc!);
gl.deleteBuffer(this._polyDirectionsHighBuffer!);
gl.deleteBuffer(this._polyDirectionsLowBuffer!);
gl.deleteBuffer(this._polyIndexesBuffer!);
gl.deleteBuffer(this._polyColorsBuffer!);
gl.deleteBuffer(this._polyPickingColorsBuffer!);
gl.deleteBuffer(this._lineVerticesHighBufferMerc);
gl.deleteBuffer(this._lineVerticesLowBufferMerc);
gl.deleteBuffer(this._lineDirectionsHighBuffer);
gl.deleteBuffer(this._lineDirectionsLowBuffer);
gl.deleteBuffer(this._lineIndexesBuffer);
gl.deleteBuffer(this._lineOrdersBuffer);
gl.deleteBuffer(this._lineColorsBuffer);
gl.deleteBuffer(this._linePickingColorsBuffer);
gl.deleteBuffer(this._lineThicknessBuffer);
gl.deleteBuffer(this._lineStrokeColorsBuffer);
gl.deleteBuffer(this._lineStrokesBuffer);
}
}
}
}
export { GeometryHandler };