LiveAtlas/src/util/areas.ts
2021-07-29 03:39:28 +01:00

205 lines
5.6 KiB
TypeScript

/*
* Copyright 2021 James Lyne
*
* Some portions of this file were taken from https://github.com/webbukkit/dynmap.
* These portions are Copyright 2020 Dynmap Contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {LatLngExpression} from "leaflet";
import LiveAtlasPolyline from "@/leaflet/vector/LiveAtlasPolyline";
import LiveAtlasPolygon from "@/leaflet/vector/LiveAtlasPolygon";
import {Coordinate, LiveAtlasArea} from "@/index";
import {arePointsEqual, createPopup, isStyleEqual, tooltipOptions} from "@/util/paths";
export const createArea = (options: LiveAtlasArea, converter: Function): LiveAtlasPolyline | LiveAtlasPolygon => {
const outline = !options.style.fillOpacity || (options.style.fillOpacity <= 0),
points = options.points.map(projectPointsMapCallback, converter) as LatLngExpression[] | LatLngExpression[][],
area = outline ? new LiveAtlasPolyline(points, options) : new LiveAtlasPolygon(points, options);
if (options.popupContent) {
area.bindPopup(() => createPopup(options, 'AreaPopup'));
}
if (options.tooltipContent) {
area.bindTooltip(() => options.tooltipContent as string, tooltipOptions);
}
return area;
};
export const updateArea = (area: LiveAtlasPolyline | LiveAtlasPolygon | undefined, options: LiveAtlasArea, converter: Function): LiveAtlasPolyline | LiveAtlasPolygon => {
if (!area) {
return createArea(options, converter);
}
const points = options.points.map(projectPointsMapCallback, converter) as LatLngExpression[] | LatLngExpression[][],
oldPoints = area.getLatLngs();
let dirty = false;
//Avoid pointless setStyle() redrawing by checking if styles have actually changed
if(!isStyleEqual(area.options, options.style)) {
area.setStyle(options.style); //FIXME: Maybe override setStyle to add an option for not redrawing
dirty = true;
}
if(!arePointsEqual(oldPoints.length === 1 ? oldPoints[0] : oldPoints, points)) {
area.setLatLngs(points);
dirty = true;
}
area.closePopup();
area.unbindPopup();
area.bindPopup(() => createPopup(options, 'AreaPopup'));
if(dirty) {
area.redraw();
}
return area;
};
const projectPointsMapCallback = function(this: Function, point: Coordinate | Coordinate[] | Coordinate[][]): LatLngExpression | LatLngExpression[] {
if(Array.isArray(point)) {
return point.map(projectPointsMapCallback, this) as LatLngExpression[];
} else {
// @ts-ignore
return this(point);
}
};
export const getPoints = (x: number[], y: [number, number], z: number[], outline: boolean): Coordinate[] | Coordinate[][] => {
if (x.length === 2) { /* Only 2 points */
if (y[0] === y[1]) {
return get2DBoxPoints(x, y, z, outline);
} else {
return get3DBoxPoints(x, y, z);
}
} else {
if (y[0] === y[1]) {
return get2DShapePoints(x, y, z, outline);
} else {
return get3DShapePoints(x, y, z);
}
}
};
export const get3DBoxPoints = (x: number[], y: [number, number], z: number[]): Coordinate[][] => {
const maxX = x[0],
minX = x[1],
maxY = y[0],
minY = y[1],
maxZ = z[0],
minZ = z[1];
return [
[
{x: minX, y: minY, z: minZ},
{x: maxX, y: minY, z: minZ},
{x: maxX, y: minY, z: maxZ},
{x: minX, y: minY, z: maxZ}
], [
{x: minX, y: maxY, z: minZ},
{x: maxX, y: maxY, z: minZ},
{x: maxX, y: maxY, z: maxZ},
{x: minX, y: maxY, z: maxZ}
], [
{x: minX, y: minY, z: minZ},
{x: minX, y: maxY, z: minZ},
{x: maxX, y: maxY, z: minZ},
{x: maxX, y: minY, z: minZ}
], [
{x: maxX, y: minY, z: minZ},
{x: maxX, y: maxY, z: minZ},
{x: maxX, y: maxY, z: maxZ},
{x: maxX, y: minY, z: maxZ}
], [
{x: minX, y: minY, z: maxZ},
{x: minX, y: maxY, z: maxZ},
{x: maxX, y: maxY, z: maxZ},
{x: maxX, y: minY, z: maxZ}
], [
{x: minX, y: minY, z: minZ},
{x: minX, y: maxY, z: minZ},
{x: minX, y: maxY, z: maxZ},
{x: minX, y: minY, z: maxZ}
]
];
};
export const get2DBoxPoints = (x: number[], y: [number, number], z: number[], outline: boolean): Coordinate[] => {
const maxX = x[0],
minX = x[1],
minY = y[1],
maxZ = z[0],
minZ = z[1];
if (outline) {
return [
{x: minX, y: minY, z: minZ},
{x: maxX, y: minY, z: minZ},
{x: maxX, y: minY, z: maxZ},
{x: minX, y: minY, z: maxZ},
{x: minX, y: minY, z: minZ}
];
} else {
return [
{x: minX, y: minY, z: minZ},
{x: maxX, y: minY, z: minZ},
{x: maxX, y: minY, z: maxZ},
{x: minX, y: minY, z: maxZ}
];
}
};
export const get3DShapePoints = (x: number[], y: [number, number], z: number[]): Coordinate[][] => {
const toplist = [],
botlist = [],
polylist = [];
for (let i = 0; i < x.length; i++) {
toplist[i] = {x: x[i], y: y[0], z: z[i]};
botlist[i] = {x: x[i], y: y[1], z: z[i]};
}
for (let i = 0; i < x.length; i++) {
polylist[i] = [
toplist[i],
botlist[i],
botlist[(i + 1) % x.length],
toplist[(i + 1) % x.length],
];
}
polylist[x.length] = botlist;
polylist[x.length + 1] = toplist;
return polylist;
};
export const get2DShapePoints = (x: number[], y: [number, number], z: number[], outline: boolean): Coordinate[] => {
const points = [];
for (let i = 0; i < x.length; i++) {
points[i] = {x: x[i], y: y[1], z: z[i]};
}
if (outline) {
points.push(points[0]);
}
return points;
};