Implement update handling for areas/lines/circles
This commit is contained in:
parent
a809b68bb2
commit
e77a777a66
256
src/api.ts
256
src/api.ts
@ -1,11 +1,18 @@
|
||||
import axios, {AxiosResponse} from 'axios';
|
||||
import {
|
||||
DynmapArea, DynmapCircle,
|
||||
DynmapArea,
|
||||
DynmapCircle,
|
||||
DynmapComponentConfig,
|
||||
DynmapConfigurationResponse, DynmapLine, DynmapMap, DynmapMarker, DynmapMarkerSet, DynmapMessageConfig,
|
||||
DynmapConfigurationResponse,
|
||||
DynmapLine,
|
||||
DynmapMap,
|
||||
DynmapMarker,
|
||||
DynmapMarkerSet,
|
||||
DynmapMarkerSetUpdates,
|
||||
DynmapMessageConfig,
|
||||
DynmapPlayer,
|
||||
DynmapServerConfig,
|
||||
DynmapUpdateResponse,
|
||||
DynmapServerConfig, DynmapUpdate,
|
||||
DynmapUpdateResponse, DynmapUpdates,
|
||||
DynmapWorld
|
||||
} from "@/dynmap";
|
||||
|
||||
@ -166,27 +173,29 @@ function buildMarkers(data: any): Map<string, DynmapMarker> {
|
||||
continue;
|
||||
}
|
||||
|
||||
const marker = data[key];
|
||||
|
||||
markers.set(key, {
|
||||
label: marker.label || '',
|
||||
location: {
|
||||
x: marker.x || 0,
|
||||
y: marker.y || 0,
|
||||
z: marker.z || 0,
|
||||
},
|
||||
dimensions: marker.dim ? marker.dim.split('x') : [16, 16],
|
||||
icon: marker.icon || "default",
|
||||
isHTML: marker.markup || false,
|
||||
minZoom: marker.minzoom || undefined,
|
||||
maxZoom: marker.maxZoom || undefined,
|
||||
popupContent: marker.desc || undefined,
|
||||
});
|
||||
markers.set(key, buildMarker(data[key]));
|
||||
}
|
||||
|
||||
return markers;
|
||||
}
|
||||
|
||||
function buildMarker(marker: any): DynmapMarker {
|
||||
return {
|
||||
label: marker.label || '',
|
||||
location: {
|
||||
x: marker.x || 0,
|
||||
y: marker.y || 0,
|
||||
z: marker.z || 0,
|
||||
},
|
||||
dimensions: marker.dim ? marker.dim.split('x') : [16, 16],
|
||||
icon: marker.icon || "default",
|
||||
isHTML: marker.markup || false,
|
||||
minZoom: marker.minzoom || undefined,
|
||||
maxZoom: marker.maxZoom || undefined,
|
||||
popupContent: marker.desc || undefined,
|
||||
};
|
||||
}
|
||||
|
||||
function buildAreas(data: any): Map<string, DynmapArea> {
|
||||
const areas = Object.freeze(new Map()) as Map<string, DynmapArea>;
|
||||
|
||||
@ -195,30 +204,32 @@ function buildAreas(data: any): Map<string, DynmapArea> {
|
||||
continue;
|
||||
}
|
||||
|
||||
const area = data[key];
|
||||
|
||||
areas.set(key, {
|
||||
style: {
|
||||
color: area.color || '#ff0000',
|
||||
opacity: area.opacity || 1,
|
||||
weight: area.weight || 1,
|
||||
fillColor: area.fillcolor || '#ff0000',
|
||||
fillOpacity: area.fillopacity || 0,
|
||||
},
|
||||
label: area.label || '',
|
||||
isHTML: area.markup || false,
|
||||
x: area.x || [0, 0],
|
||||
y: [area.ybottom || 0, area.ytop || 0],
|
||||
z: area.z || [0, 0],
|
||||
minZoom: area.minzoom || undefined,
|
||||
maxZoom: area.maxZoom || undefined,
|
||||
popupContent: area.desc || undefined,
|
||||
});
|
||||
areas.set(key, buildArea(data[key]));
|
||||
}
|
||||
|
||||
return areas;
|
||||
}
|
||||
|
||||
function buildArea(area: any): DynmapArea {
|
||||
return {
|
||||
style: {
|
||||
color: area.color || '#ff0000',
|
||||
opacity: area.opacity || 1,
|
||||
weight: area.weight || 1,
|
||||
fillColor: area.fillcolor || '#ff0000',
|
||||
fillOpacity: area.fillopacity || 0,
|
||||
},
|
||||
label: area.label || '',
|
||||
isHTML: area.markup || false,
|
||||
x: area.x || [0, 0],
|
||||
y: [area.ybottom || 0, area.ytop || 0],
|
||||
z: area.z || [0, 0],
|
||||
minZoom: area.minzoom || undefined,
|
||||
maxZoom: area.maxZoom || undefined,
|
||||
popupContent: area.desc || undefined,
|
||||
};
|
||||
}
|
||||
|
||||
function buildLines(data: any): Map<string, DynmapLine> {
|
||||
const lines = Object.freeze(new Map()) as Map<string, DynmapLine>;
|
||||
|
||||
@ -227,28 +238,30 @@ function buildLines(data: any): Map<string, DynmapLine> {
|
||||
continue;
|
||||
}
|
||||
|
||||
const line = data[key];
|
||||
|
||||
lines.set(key, {
|
||||
x: line.x || [0, 0],
|
||||
y: line.y || [0, 0],
|
||||
z: line.z || [0, 0],
|
||||
style: {
|
||||
color: line.color || '#ff0000',
|
||||
opacity: line.opacity || 1,
|
||||
weight: line.weight || 1,
|
||||
},
|
||||
label: line.label || '',
|
||||
isHTML: line.markup || false,
|
||||
minZoom: line.minzoom || undefined,
|
||||
maxZoom: line.maxZoom || undefined,
|
||||
popupContent: line.desc || undefined,
|
||||
});
|
||||
lines.set(key, buildLine(data[key]));
|
||||
}
|
||||
|
||||
return lines;
|
||||
}
|
||||
|
||||
function buildLine(line: any): DynmapLine {
|
||||
return {
|
||||
x: line.x || [0, 0],
|
||||
y: line.y || [0, 0],
|
||||
z: line.z || [0, 0],
|
||||
style: {
|
||||
color: line.color || '#ff0000',
|
||||
opacity: line.opacity || 1,
|
||||
weight: line.weight || 1,
|
||||
},
|
||||
label: line.label || '',
|
||||
isHTML: line.markup || false,
|
||||
minZoom: line.minzoom || undefined,
|
||||
maxZoom: line.maxZoom || undefined,
|
||||
popupContent: line.desc || undefined,
|
||||
};
|
||||
}
|
||||
|
||||
function buildCircles(data: any): Map<string, DynmapCircle> {
|
||||
const circles = Object.freeze(new Map()) as Map<string, DynmapCircle>;
|
||||
|
||||
@ -257,34 +270,117 @@ function buildCircles(data: any): Map<string, DynmapCircle> {
|
||||
continue;
|
||||
}
|
||||
|
||||
const circle = data[key];
|
||||
|
||||
circles.set(key, {
|
||||
location: {
|
||||
x: circle.x || 0,
|
||||
y: circle.y || 0,
|
||||
z: circle.z || 0,
|
||||
},
|
||||
radius: [circle.xr || 0, circle.zr || 0],
|
||||
style: {
|
||||
fillColor: circle.fillcolor || '#ff0000',
|
||||
fillOpacity: circle.fillopacity || 0,
|
||||
color: circle.color || '#ff0000',
|
||||
opacity: circle.opacity || 1,
|
||||
weight: circle.weight || 1,
|
||||
},
|
||||
label: circle.label || '',
|
||||
isHTML: circle.markup || false,
|
||||
|
||||
minZoom: circle.minzoom || undefined,
|
||||
maxZoom: circle.maxZoom || undefined,
|
||||
popupContent: circle.desc || undefined,
|
||||
});
|
||||
circles.set(key, buildCircle(data[key]));
|
||||
}
|
||||
|
||||
return circles;
|
||||
}
|
||||
|
||||
function buildCircle(circle: any): DynmapCircle {
|
||||
return {
|
||||
location: {
|
||||
x: circle.x || 0,
|
||||
y: circle.y || 0,
|
||||
z: circle.z || 0,
|
||||
},
|
||||
radius: [circle.xr || 0, circle.zr || 0],
|
||||
style: {
|
||||
fillColor: circle.fillcolor || '#ff0000',
|
||||
fillOpacity: circle.fillopacity || 0,
|
||||
color: circle.color || '#ff0000',
|
||||
opacity: circle.opacity || 1,
|
||||
weight: circle.weight || 1,
|
||||
},
|
||||
label: circle.label || '',
|
||||
isHTML: circle.markup || false,
|
||||
|
||||
minZoom: circle.minzoom || undefined,
|
||||
maxZoom: circle.maxZoom || undefined,
|
||||
popupContent: circle.desc || undefined,
|
||||
};
|
||||
}
|
||||
|
||||
function buildUpdates(data: Array<any>): DynmapUpdates {
|
||||
const updates = {
|
||||
markerSets: new Map<string, DynmapMarkerSetUpdates>(),
|
||||
tiles: new Map(),
|
||||
chat: [],
|
||||
}
|
||||
|
||||
for(const entry of data) {
|
||||
switch(entry.type) {
|
||||
case 'component': {
|
||||
if(!entry.id) {
|
||||
console.warn(`Ignoring component update without an ID`);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!entry.set) {
|
||||
console.warn(`Ignoring component update without a marker set`);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(entry.ctype !== 'markers') {
|
||||
console.warn(`Ignoring component with unknown ctype ${entry.ctype}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!updates.markerSets.has(entry.set)) {
|
||||
updates.markerSets.set(entry.set, {
|
||||
areaUpdates: [],
|
||||
markerUpdates: [],
|
||||
lineUpdates: [],
|
||||
circleUpdates: [],
|
||||
});
|
||||
}
|
||||
|
||||
const markerSetUpdates = updates.markerSets.get(entry.set),
|
||||
update: DynmapUpdate = {
|
||||
id: entry.id,
|
||||
removed: entry.msg.endsWith('deleted'),
|
||||
};
|
||||
|
||||
|
||||
if(entry.msg.startsWith("marker")) {
|
||||
update.payload = update.removed ? undefined : buildMarker(entry);
|
||||
markerSetUpdates!.markerUpdates.push(Object.freeze(update));
|
||||
} else if(entry.msg.startsWith("area")) {
|
||||
update.payload = update.removed ? undefined : buildArea(entry);
|
||||
markerSetUpdates!.areaUpdates.push(Object.freeze(update));
|
||||
|
||||
} else if(entry.msg.startsWith("circle")) {
|
||||
update.payload = update.removed ? undefined : buildCircle(entry);
|
||||
markerSetUpdates!.circleUpdates.push(Object.freeze(update));
|
||||
|
||||
} else if(entry.msg.startsWith("line")) {
|
||||
update.payload = update.removed ? undefined : buildLine(entry);
|
||||
markerSetUpdates!.lineUpdates.push(Object.freeze(update));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'chat':
|
||||
//TODO
|
||||
break;
|
||||
|
||||
case 'tile':
|
||||
if(!entry.name || !entry.timestamp) {
|
||||
console.warn(`Ignoring tile update without a name or timestamp`);
|
||||
break;
|
||||
}
|
||||
|
||||
updates.tiles.set(entry.name, entry.timestamp);
|
||||
break;
|
||||
|
||||
default:
|
||||
console.warn(`Ignoring unknown update type ${entry.type}`);
|
||||
}
|
||||
}
|
||||
|
||||
return updates;
|
||||
}
|
||||
|
||||
export default {
|
||||
getConfiguration(): Promise<DynmapConfigurationResponse> {
|
||||
return axios.get(window.config.url.configuration).then((response): DynmapConfigurationResponse => {
|
||||
@ -349,6 +445,7 @@ export default {
|
||||
configHash: data.configHash || 0,
|
||||
timestamp: data.timestamp || 0,
|
||||
players,
|
||||
updates: buildUpdates(data.updates || []),
|
||||
}
|
||||
});
|
||||
},
|
||||
@ -374,6 +471,7 @@ export default {
|
||||
lines = buildLines(set.lines || {});
|
||||
|
||||
sets.set(key, {
|
||||
id: key,
|
||||
label: set.label || "Unnamed set",
|
||||
hidden: set.hidden || false,
|
||||
priority: set.layerprio || 0,
|
||||
|
@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<GenericMarker v-for="[id, marker] in markerSet.markers" :options="marker" :key="id" :layer-group="layerGroup"></GenericMarker>
|
||||
<Areas :areas="markerSet.areas" :layer-group="layerGroup"></Areas>
|
||||
<Circles :circles="markerSet.circles" :layer-group="layerGroup"></Circles>
|
||||
<Lines :lines="markerSet.lines" :layer-group="layerGroup"></Lines>
|
||||
<Areas :layer-group="layerGroup" :set="markerSet"></Areas>
|
||||
<Circles :layer-group="layerGroup" :set="markerSet"></Circles>
|
||||
<Lines :layer-group="layerGroup" :set="markerSet"></Lines>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
|
@ -1,237 +1,106 @@
|
||||
<script lang="ts">
|
||||
import {defineComponent, computed} from "@vue/runtime-core";
|
||||
import L, {LatLngExpression} from 'leaflet';
|
||||
import {defineComponent, computed, onMounted, onUnmounted, watch} from "@vue/runtime-core";
|
||||
import {LayerGroup, Polyline, Polygon} from 'leaflet';
|
||||
import {useStore} from "@/store";
|
||||
import {DynmapArea} from "@/dynmap";
|
||||
import {DynmapArea, DynmapMarkerSet} from "@/dynmap";
|
||||
import {ActionTypes} from "@/store/action-types";
|
||||
import {createArea, updateArea} from "@/util/areas";
|
||||
import Util from '@/util';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
areas: {
|
||||
type: Object as () => Map<string, DynmapArea>,
|
||||
required: true
|
||||
set: {
|
||||
type: Object as () => DynmapMarkerSet,
|
||||
required: true,
|
||||
},
|
||||
layerGroup: {
|
||||
type: Object as () => L.LayerGroup,
|
||||
type: Object as () => LayerGroup,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
|
||||
setup() {
|
||||
setup(props) {
|
||||
let updateFrame = 0;
|
||||
|
||||
const store = useStore(),
|
||||
currentProjection = computed(() => store.state.currentProjection),
|
||||
layers = Object.freeze(new Map()) as Map<string, L.Path>;
|
||||
pendingUpdates = computed(() => {
|
||||
const markerSetUpdates = store.state.pendingSetUpdates.get(props.set.id);
|
||||
|
||||
return {
|
||||
layers,
|
||||
currentProjection,
|
||||
}
|
||||
},
|
||||
return markerSetUpdates && markerSetUpdates.areaUpdates.length;
|
||||
}),
|
||||
layers = Object.freeze(new Map()) as Map<string, Polygon | Polyline>,
|
||||
|
||||
watch: {
|
||||
//FIXME: Prevent unnecessary repositioning when changing worlds
|
||||
currentProjection() {
|
||||
const projection = useStore().state.currentProjection,
|
||||
latLng = (x: number, y: number, z: number) => {
|
||||
return projection.locationToLatLng({x, y, z});
|
||||
createAreas = () => {
|
||||
const converter = Util.getPointConverter();
|
||||
|
||||
props.set.areas.forEach((area: DynmapArea, id: string) => {
|
||||
const layer = createArea(area, converter);
|
||||
|
||||
layers.set(id, layer);
|
||||
props.layerGroup.addLayer(layer);
|
||||
});
|
||||
},
|
||||
|
||||
deleteArea = (id: string) => {
|
||||
let area = layers.get(id) as Polyline;
|
||||
|
||||
if(!area) {
|
||||
return;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
for (const [id, area] of this.areas) {
|
||||
this.updateArea(id, area, latLng);
|
||||
area.remove();
|
||||
layers.delete(id);
|
||||
},
|
||||
|
||||
handlePendingUpdates = () => {
|
||||
useStore().dispatch(ActionTypes.POP_AREA_UPDATES, {
|
||||
markerSet: props.set.id,
|
||||
amount: 10,
|
||||
}).then(updates => {
|
||||
const converter = Util.getPointConverter();
|
||||
|
||||
for(const update of updates) {
|
||||
if(update.removed) {
|
||||
console.log(`Deleting area ${update.id}`);
|
||||
deleteArea(update.id);
|
||||
} else {
|
||||
console.log(`Updating/creating area ${update.id}`);
|
||||
layers.set(update.id, updateArea(layers.get(update.id), update.payload as DynmapArea, converter));
|
||||
}
|
||||
}
|
||||
|
||||
if(pendingUpdates.value) {
|
||||
console.log('More updates left, scheduling frame');
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
updateFrame = requestAnimationFrame(() => handlePendingUpdates());
|
||||
} else {
|
||||
updateFrame = 0;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
//FIXME: Prevent unnecessary repositioning when changing worlds
|
||||
watch(currentProjection, () => {
|
||||
const converter = Util.getPointConverter();
|
||||
|
||||
for (const [id, area] of props.set.areas) {
|
||||
updateArea(layers.get(id), area, converter);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
mounted() {
|
||||
this.createAreas();
|
||||
},
|
||||
|
||||
unmounted() {
|
||||
watch(pendingUpdates, (newValue, oldValue) => {
|
||||
if(newValue && newValue > 0 && oldValue === 0 && !updateFrame) {
|
||||
updateFrame = requestAnimationFrame(() => handlePendingUpdates());
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(() => createAreas());
|
||||
onUnmounted(() => updateFrame && cancelAnimationFrame(updateFrame));
|
||||
},
|
||||
|
||||
render() {
|
||||
return null;
|
||||
},
|
||||
|
||||
methods: {
|
||||
createAreas() {
|
||||
const projection = useStore().state.currentProjection,
|
||||
latLng = (x: number, y: number, z: number) => {
|
||||
return projection.locationToLatLng({x, y, z});
|
||||
};
|
||||
|
||||
this.areas.forEach((area: DynmapArea, id: string) => {
|
||||
this.createArea(id, area, latLng);
|
||||
});
|
||||
},
|
||||
|
||||
createArea(id: string, options: DynmapArea, latLng: Function) {
|
||||
const outline = !options.style.fillOpacity || (options.style.fillOpacity <= 0),
|
||||
points = this.getPoints(options, latLng, outline),
|
||||
area: L.Path = outline ? new L.Polyline(points, options.style) : new L.Polygon(points, options.style);
|
||||
|
||||
if (options.label) {
|
||||
area.bindPopup(() => {
|
||||
const popup = document.createElement('span');
|
||||
|
||||
if (options.popupContent) {
|
||||
popup.classList.add('AreaPopup');
|
||||
popup.insertAdjacentHTML('afterbegin', options.popupContent);
|
||||
} else if (options.isHTML) {
|
||||
popup.classList.add('AreaPopup');
|
||||
popup.insertAdjacentHTML('afterbegin', options.label);
|
||||
} else {
|
||||
popup.textContent = options.label;
|
||||
}
|
||||
|
||||
return popup;
|
||||
});
|
||||
}
|
||||
|
||||
this.layers.set(id, area);
|
||||
this.layerGroup.addLayer(area);
|
||||
},
|
||||
|
||||
updateArea(id: string, options: DynmapArea, latLng: Function) {
|
||||
let area = this.layers.get(id) as L.Polyline,
|
||||
outline = (options.style && options.style.fillOpacity && (options.style.fillOpacity <= 0)) as boolean,
|
||||
points = this.getPoints(options, latLng, outline);
|
||||
|
||||
if (!area) {
|
||||
return;
|
||||
}
|
||||
|
||||
area.setLatLngs(points);
|
||||
area.redraw();
|
||||
},
|
||||
|
||||
getPoints(options: DynmapArea, latLng: Function, outline: boolean): LatLngExpression[] | LatLngExpression[][] {
|
||||
if (options.x.length === 2) { /* Only 2 points */
|
||||
if (options.y[0] === options.y[1]) {
|
||||
return this.get2DBoxPoints(options, latLng, outline);
|
||||
} else {
|
||||
return this.get3DBoxPoints(options, latLng);
|
||||
}
|
||||
} else {
|
||||
if (options.y[0] === options.y[1]) {
|
||||
return this.get2DShapePoints(options, latLng, outline);
|
||||
} else {
|
||||
return this.get3DShapePoints(options, latLng);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
get3DBoxPoints(options: DynmapArea, latLng: Function): LatLngExpression[][] {
|
||||
const maxX = options.x[0],
|
||||
minX = options.x[1],
|
||||
maxY = options.y[0],
|
||||
minY = options.y[1],
|
||||
maxZ = options.z[0],
|
||||
minZ = options.z[1];
|
||||
|
||||
return [
|
||||
[
|
||||
latLng(minX, minY, minZ),
|
||||
latLng(maxX, minY, minZ),
|
||||
latLng(maxX, minY, maxZ),
|
||||
latLng(minX, minY, maxZ)
|
||||
], [
|
||||
latLng(minX, maxY, minZ),
|
||||
latLng(maxX, maxY, minZ),
|
||||
latLng(maxX, maxY, maxZ),
|
||||
latLng(minX, maxY, maxZ)
|
||||
], [
|
||||
latLng(minX, minY, minZ),
|
||||
latLng(minX, maxY, minZ),
|
||||
latLng(maxX, maxY, minZ),
|
||||
latLng(maxX, minY, minZ)
|
||||
], [
|
||||
latLng(maxX, minY, minZ),
|
||||
latLng(maxX, maxY, minZ),
|
||||
latLng(maxX, maxY, maxZ),
|
||||
latLng(maxX, minY, maxZ)
|
||||
], [
|
||||
latLng(minX, minY, maxZ),
|
||||
latLng(minX, maxY, maxZ),
|
||||
latLng(maxX, maxY, maxZ),
|
||||
latLng(maxX, minY, maxZ)
|
||||
], [
|
||||
latLng(minX, minY, minZ),
|
||||
latLng(minX, maxY, minZ),
|
||||
latLng(minX, maxY, maxZ),
|
||||
latLng(minX, minY, maxZ)
|
||||
]
|
||||
];
|
||||
},
|
||||
|
||||
get2DBoxPoints(options: DynmapArea, latLng: Function, outline: boolean): LatLngExpression[] {
|
||||
const maxX = options.x[0],
|
||||
minX = options.x[1],
|
||||
minY = options.y[1],
|
||||
maxZ = options.z[0],
|
||||
minZ = options.z[1];
|
||||
|
||||
if (outline) {
|
||||
return [
|
||||
latLng(minX, minY, minZ),
|
||||
latLng(maxX, minY, minZ),
|
||||
latLng(maxX, minY, maxZ),
|
||||
latLng(minX, minY, maxZ),
|
||||
latLng(minX, minY, minZ)
|
||||
];
|
||||
} else {
|
||||
return [
|
||||
latLng(minX, minY, minZ),
|
||||
latLng(maxX, minY, minZ),
|
||||
latLng(maxX, minY, maxZ),
|
||||
latLng(minX, minY, maxZ)
|
||||
];
|
||||
}
|
||||
},
|
||||
|
||||
get3DShapePoints(options: DynmapArea, latLng: Function): LatLngExpression[][] {
|
||||
const toplist = [],
|
||||
botlist = [],
|
||||
polylist = [];
|
||||
|
||||
for (let i = 0; i < options.x.length; i++) {
|
||||
toplist[i] = latLng(options.x[i], options.y[0], options.z[i]);
|
||||
botlist[i] = latLng(options.x[i], options.y[1], options.z[i]);
|
||||
}
|
||||
|
||||
for (let i = 0; i < options.x.length; i++) {
|
||||
const sidelist = [];
|
||||
sidelist[0] = toplist[i];
|
||||
sidelist[1] = botlist[i];
|
||||
sidelist[2] = botlist[(i + 1) % options.x.length];
|
||||
sidelist[3] = toplist[(i + 1) % options.x.length];
|
||||
polylist[i] = sidelist;
|
||||
}
|
||||
|
||||
polylist[options.x.length] = botlist;
|
||||
polylist[options.x.length + 1] = toplist;
|
||||
|
||||
return polylist;
|
||||
},
|
||||
|
||||
get2DShapePoints(options: DynmapArea, latLng: Function, outline: boolean): LatLngExpression[] {
|
||||
const points = [];
|
||||
|
||||
for (let i = 0; i < options.x.length; i++) {
|
||||
points[i] = latLng(options.x[i], options.y[1], options.z[i]);
|
||||
}
|
||||
|
||||
if (outline) {
|
||||
points.push(points[0]);
|
||||
}
|
||||
|
||||
return points;
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@ -1,140 +1,111 @@
|
||||
<script lang="ts">
|
||||
import {defineComponent, computed} from "@vue/runtime-core";
|
||||
import L, {LatLngExpression} from 'leaflet';
|
||||
import {defineComponent, computed, onMounted, onUnmounted, watch} from "@vue/runtime-core";
|
||||
import {Polyline, LayerGroup, Polygon} from 'leaflet';
|
||||
import {useStore} from "@/store";
|
||||
import {DynmapCircle} from "@/dynmap";
|
||||
import {DynmapCircle, DynmapMarkerSet} from "@/dynmap";
|
||||
import {ActionTypes} from "@/store/action-types";
|
||||
import {createCircle, updateCircle} from "@/util/circles";
|
||||
import Util from '@/util';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
circles: {
|
||||
type: Object as () => Map<string, DynmapCircle>,
|
||||
required: true
|
||||
set: {
|
||||
type: Object as () => DynmapMarkerSet,
|
||||
required: true,
|
||||
},
|
||||
layerGroup: {
|
||||
type: Object as () => L.LayerGroup,
|
||||
type: Object as () => LayerGroup,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
|
||||
setup() {
|
||||
setup(props) {
|
||||
let updateFrame = 0;
|
||||
|
||||
const store = useStore(),
|
||||
currentProjection = computed(() => store.state.currentProjection),
|
||||
layers = Object.freeze(new Map()) as Map<string, L.Path>;
|
||||
pendingUpdates = computed(() => {
|
||||
const markerSetUpdates = store.state.pendingSetUpdates.get(props.set.id);
|
||||
|
||||
return {
|
||||
layers,
|
||||
currentProjection,
|
||||
}
|
||||
},
|
||||
return markerSetUpdates && markerSetUpdates.circleUpdates.length;
|
||||
}),
|
||||
layers = Object.freeze(new Map<string, Polyline | Polygon>()),
|
||||
|
||||
watch: {
|
||||
//FIXME: Prevent unnecessary repositioning when changing worlds
|
||||
currentProjection() {
|
||||
const projection = useStore().state.currentProjection,
|
||||
latLng = (x: number, y: number, z: number) => {
|
||||
return projection.locationToLatLng({x, y, z});
|
||||
createCircles = () => {
|
||||
const converter = Util.getPointConverter();
|
||||
|
||||
props.set.circles.forEach((circle: DynmapCircle, id: string) => {
|
||||
const layer = createCircle(circle, converter);
|
||||
|
||||
layers.set(id, layer);
|
||||
props.layerGroup.addLayer(layer);
|
||||
});
|
||||
},
|
||||
|
||||
deleteCircle = (id: string) => {
|
||||
let circle = layers.get(id) as Polyline;
|
||||
|
||||
if (!circle) {
|
||||
return;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
for (const [id, circle] of this.circles) {
|
||||
this.updateCircle(id, circle, latLng);
|
||||
circle.remove();
|
||||
layers.delete(id);
|
||||
},
|
||||
|
||||
handlePendingUpdates = () => {
|
||||
useStore().dispatch(ActionTypes.POP_CIRCLE_UPDATES, {
|
||||
markerSet: props.set.id,
|
||||
amount: 10,
|
||||
}).then(updates => {
|
||||
const converter = Util.getPointConverter();
|
||||
|
||||
for(const update of updates) {
|
||||
if(update.removed) {
|
||||
console.log(`Deleting circle ${update.id}`);
|
||||
deleteCircle(update.id);
|
||||
} else {
|
||||
console.log(`Updating/creating circle ${update.id}`);
|
||||
const layer = updateCircle(layers.get(update.id), update.payload as DynmapCircle, converter)
|
||||
|
||||
if(!layers.has(update.id)) {
|
||||
layers.set(update.id, layer);
|
||||
props.layerGroup.addLayer(layer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(pendingUpdates.value) {
|
||||
console.log('More updates left, scheduling frame');
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
updateFrame = requestAnimationFrame(() => handlePendingUpdates());
|
||||
} else {
|
||||
updateFrame = 0;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
//FIXME: Prevent unnecessary repositioning when changing worlds
|
||||
watch(currentProjection, () => {
|
||||
const converter = Util.getPointConverter();
|
||||
|
||||
for (const [id, circle] of props.set.circles) {
|
||||
updateCircle(layers.get(id), circle, converter);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
mounted() {
|
||||
this.createCircles();
|
||||
},
|
||||
|
||||
unmounted() {
|
||||
watch(pendingUpdates, (newValue, oldValue) => {
|
||||
if(newValue && newValue > 0 && oldValue === 0 && !updateFrame) {
|
||||
updateFrame = requestAnimationFrame(() => handlePendingUpdates());
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(() => createCircles());
|
||||
onUnmounted(() => updateFrame && cancelAnimationFrame(updateFrame));
|
||||
},
|
||||
|
||||
render() {
|
||||
return null;
|
||||
},
|
||||
|
||||
methods: {
|
||||
createCircles() {
|
||||
const projection = useStore().state.currentProjection,
|
||||
latLng = (x: number, y: number, z: number) => {
|
||||
return projection.locationToLatLng({x, y, z});
|
||||
};
|
||||
|
||||
this.circles.forEach((circle: DynmapCircle, id: string) => {
|
||||
this.createCircle(id, circle, latLng);
|
||||
});
|
||||
},
|
||||
|
||||
createCircle(id: string, options: DynmapCircle, latLng: Function) {
|
||||
const outline = !options.style.fillOpacity || (options.style.fillOpacity <= 0),
|
||||
points = this.getPoints(options, latLng, outline);
|
||||
let circle;
|
||||
|
||||
if(outline) {
|
||||
circle = new L.Polyline(points, options.style);
|
||||
} else {
|
||||
circle = new L.Polygon(points, options.style);
|
||||
}
|
||||
|
||||
if(options.label) {
|
||||
circle.bindPopup(() => {
|
||||
const popup = document.createElement('span');
|
||||
|
||||
if (options.popupContent) {
|
||||
popup.classList.add('CirclePopup');
|
||||
popup.insertAdjacentHTML('afterbegin', options.popupContent);
|
||||
} else if (options.isHTML) {
|
||||
popup.classList.add('CirclePopup');
|
||||
popup.insertAdjacentHTML('afterbegin', options.label);
|
||||
} else {
|
||||
popup.textContent = options.label;
|
||||
}
|
||||
|
||||
return popup;
|
||||
});
|
||||
}
|
||||
|
||||
this.layers.set(id, circle);
|
||||
this.layerGroup.addLayer(circle);
|
||||
},
|
||||
|
||||
getPoints(options: DynmapCircle, latLng: Function, outline: boolean): LatLngExpression[] {
|
||||
const points = [];
|
||||
|
||||
for(let i = 0; i < 360; i++) {
|
||||
const rad = i * Math.PI / 180.0,
|
||||
x = options.radius[0] * Math.sin(rad) + options.location.x,
|
||||
z = options.radius[1] * Math.cos(rad) + options.location.z;
|
||||
|
||||
console.log(x,options.location.y,z, latLng(x, options.location.y, z));
|
||||
|
||||
points.push(latLng(x, options.location.y, z));
|
||||
}
|
||||
|
||||
if(outline && points.length) {
|
||||
points.push(points[0]);
|
||||
}
|
||||
|
||||
return points;
|
||||
},
|
||||
|
||||
updateCircle(id: string, options: DynmapCircle, latLng: Function) {
|
||||
let circle = this.layers.get(id) as L.Polyline,
|
||||
outline = (options.style && options.style.fillOpacity && (options.style.fillOpacity <= 0)) as boolean,
|
||||
points = this.getPoints(options, latLng, outline);
|
||||
|
||||
if (!circle) {
|
||||
return;
|
||||
}
|
||||
|
||||
circle.setLatLngs(points);
|
||||
circle.redraw();
|
||||
},
|
||||
}
|
||||
})
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@ -1,122 +1,111 @@
|
||||
<script lang="ts">
|
||||
import {defineComponent, computed} from "@vue/runtime-core";
|
||||
import L, {LatLngExpression} from 'leaflet';
|
||||
import {defineComponent, computed, onMounted, onUnmounted, watch} from "@vue/runtime-core";
|
||||
import {Polyline, LayerGroup, Polygon} from 'leaflet';
|
||||
import {useStore} from "@/store";
|
||||
import {DynmapLine} from "@/dynmap";
|
||||
import {DynmapLine, DynmapMarkerSet} from "@/dynmap";
|
||||
import {ActionTypes} from "@/store/action-types";
|
||||
import {createLine, updateLine} from "@/util/lines";
|
||||
import Util from '@/util';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
lines: {
|
||||
type: Object as () => Map<string, DynmapLine>,
|
||||
required: true
|
||||
set: {
|
||||
type: Object as () => DynmapMarkerSet,
|
||||
required: true,
|
||||
},
|
||||
layerGroup: {
|
||||
type: Object as () => L.LayerGroup,
|
||||
type: Object as () => LayerGroup,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
|
||||
setup() {
|
||||
setup(props) {
|
||||
let updateFrame = 0;
|
||||
|
||||
const store = useStore(),
|
||||
currentProjection = computed(() => store.state.currentProjection),
|
||||
layers = Object.freeze(new Map()) as Map<string, L.Path>;
|
||||
pendingUpdates = computed(() => {
|
||||
const markerSetUpdates = store.state.pendingSetUpdates.get(props.set.id);
|
||||
|
||||
return {
|
||||
layers,
|
||||
currentProjection,
|
||||
}
|
||||
},
|
||||
return markerSetUpdates && markerSetUpdates.lineUpdates.length;
|
||||
}),
|
||||
layers = Object.freeze(new Map<string, Polyline | Polygon>()),
|
||||
|
||||
watch: {
|
||||
//FIXME: Prevent unnecessary repositioning when changing worlds
|
||||
currentProjection() {
|
||||
const projection = useStore().state.currentProjection,
|
||||
latLng = (x: number, y: number, z: number) => {
|
||||
return projection.locationToLatLng({x, y, z});
|
||||
createLines = () => {
|
||||
const converter = Util.getPointConverter();
|
||||
|
||||
props.set.lines.forEach((line: DynmapLine, id: string) => {
|
||||
const layer = createLine(line, converter);
|
||||
|
||||
layers.set(id, layer);
|
||||
props.layerGroup.addLayer(layer);
|
||||
});
|
||||
},
|
||||
|
||||
deleteLine = (id: string) => {
|
||||
let line = layers.get(id) as Polyline;
|
||||
|
||||
if (!line) {
|
||||
return;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
for (const [id, line] of this.lines) {
|
||||
this.updateLine(id, line, latLng);
|
||||
line.remove();
|
||||
layers.delete(id);
|
||||
},
|
||||
|
||||
handlePendingUpdates = () => {
|
||||
useStore().dispatch(ActionTypes.POP_LINE_UPDATES, {
|
||||
markerSet: props.set.id,
|
||||
amount: 10,
|
||||
}).then(updates => {
|
||||
const converter = Util.getPointConverter();
|
||||
|
||||
for(const update of updates) {
|
||||
if(update.removed) {
|
||||
console.log(`Deleting line ${update.id}`);
|
||||
deleteLine(update.id);
|
||||
} else {
|
||||
console.log(`Updating/creating line ${update.id}`);
|
||||
const layer = updateLine(layers.get(update.id), update.payload as DynmapLine, converter)
|
||||
|
||||
if(!layers.has(update.id)) {
|
||||
layers.set(update.id, layer);
|
||||
props.layerGroup.addLayer(layer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(pendingUpdates.value) {
|
||||
console.log('More updates left, scheduling frame');
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
updateFrame = requestAnimationFrame(() => handlePendingUpdates());
|
||||
} else {
|
||||
updateFrame = 0;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
//FIXME: Prevent unnecessary repositioning when changing worlds
|
||||
watch(currentProjection, () => {
|
||||
const converter = Util.getPointConverter();
|
||||
|
||||
for (const [id, line] of props.set.lines) {
|
||||
updateLine(layers.get(id), line, converter);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
mounted() {
|
||||
this.createLines();
|
||||
},
|
||||
|
||||
unmounted() {
|
||||
watch(pendingUpdates, (newValue, oldValue) => {
|
||||
if(newValue && newValue > 0 && oldValue === 0 && !updateFrame) {
|
||||
updateFrame = requestAnimationFrame(() => handlePendingUpdates());
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(() => createLines());
|
||||
onUnmounted(() => updateFrame && cancelAnimationFrame(updateFrame));
|
||||
},
|
||||
|
||||
render() {
|
||||
return null;
|
||||
},
|
||||
|
||||
methods: {
|
||||
createLines() {
|
||||
const projection = useStore().state.currentProjection,
|
||||
latLng = (x: number, y: number, z: number) => {
|
||||
return projection.locationToLatLng({x, y, z});
|
||||
};
|
||||
|
||||
this.lines.forEach((line: DynmapLine, id: string) => {
|
||||
this.createLine(id, line, latLng);
|
||||
});
|
||||
},
|
||||
|
||||
createLine(id: string, options: DynmapLine, latLng: Function) {
|
||||
const points = this.getPoints(options, latLng),
|
||||
line= new L.Polyline(points, options.style);
|
||||
|
||||
if(options.label) {
|
||||
line.bindPopup(() => {
|
||||
const popup = document.createElement('span');
|
||||
|
||||
if (options.popupContent) {
|
||||
popup.classList.add('LinePopup');
|
||||
popup.insertAdjacentHTML('afterbegin', options.popupContent);
|
||||
} else if (options.isHTML) {
|
||||
popup.classList.add('LinePopup');
|
||||
popup.insertAdjacentHTML('afterbegin', options.label);
|
||||
} else {
|
||||
popup.textContent = options.label;
|
||||
}
|
||||
|
||||
return popup;
|
||||
});
|
||||
}
|
||||
|
||||
this.layers.set(id, line);
|
||||
this.layerGroup.addLayer(line);
|
||||
},
|
||||
|
||||
getPoints(options: DynmapLine, latLng: Function): LatLngExpression[] {
|
||||
const points = [];
|
||||
|
||||
for(let i = 0; i < options.x.length; i++) {
|
||||
points.push(latLng(options.x[i], options.y[i], options.z[i]));
|
||||
}
|
||||
|
||||
return points;
|
||||
},
|
||||
|
||||
updateLine(id: string, options: DynmapLine, latLng: Function) {
|
||||
let line = this.layers.get(id) as L.Polyline,
|
||||
points = this.getPoints(options, latLng);
|
||||
|
||||
if (!line) {
|
||||
return;
|
||||
}
|
||||
|
||||
line.setLatLngs(points);
|
||||
line.redraw();
|
||||
},
|
||||
}
|
||||
})
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
38
src/dynmap.d.ts
vendored
38
src/dynmap.d.ts
vendored
@ -139,8 +139,8 @@ interface DynmapUpdateResponse {
|
||||
configHash: number;
|
||||
playerCount: number;
|
||||
players: Set<DynmapPlayer>;
|
||||
updates: DynmapUpdates;
|
||||
timestamp: number;
|
||||
//TODO: Tiles etc
|
||||
}
|
||||
|
||||
interface DynmapPlayer {
|
||||
@ -153,6 +153,7 @@ interface DynmapPlayer {
|
||||
}
|
||||
|
||||
interface DynmapMarkerSet {
|
||||
id: string,
|
||||
label: string;
|
||||
hidden: boolean;
|
||||
priority: number;
|
||||
@ -210,3 +211,38 @@ interface DynmapCircle {
|
||||
maxZoom?: number;
|
||||
popupContent?: string;
|
||||
}
|
||||
|
||||
interface DynmapUpdates {
|
||||
markerSets: Map<string, DynmapMarkerSetUpdates>,
|
||||
tiles: Map<string, number>,
|
||||
chat: Array<any> //TODO
|
||||
}
|
||||
|
||||
interface DynmapMarkerSetUpdates {
|
||||
markerUpdates: Array<DynmapMarkerUpdate>
|
||||
areaUpdates: Array<DynmapAreaUpdate>
|
||||
circleUpdates: Array<DynmapCircleUpdate>
|
||||
lineUpdates: Array<DynmapLineUpdate>
|
||||
}
|
||||
|
||||
interface DynmapUpdate {
|
||||
id: string,
|
||||
removed: boolean,
|
||||
payload?: any,
|
||||
}
|
||||
|
||||
interface DynmapMarkerUpdate extends DynmapUpdate {
|
||||
payload?: DynmapMarker
|
||||
}
|
||||
|
||||
interface DynmapAreaUpdate extends DynmapUpdate {
|
||||
payload?: DynmapArea
|
||||
}
|
||||
|
||||
interface DynmapCircleUpdate extends DynmapUpdate {
|
||||
payload?: DynmapCircle
|
||||
}
|
||||
|
||||
interface DynmapLineUpdate extends DynmapUpdate {
|
||||
payload?: DynmapLine
|
||||
}
|
@ -3,4 +3,8 @@ export enum ActionTypes {
|
||||
GET_UPDATE = "getUpdate",
|
||||
GET_MARKER_SETS = "getMarkerSets",
|
||||
SET_PLAYERS = "setPlayers",
|
||||
POP_MARKER_UPDATES = "popMarkerUpdates",
|
||||
POP_AREA_UPDATES = "popAreaUpdates",
|
||||
POP_CIRCLE_UPDATES = "popCircleUpdates",
|
||||
POP_LINE_UPDATES = "popLineUpdates",
|
||||
}
|
@ -4,7 +4,14 @@ import {State} from "@/store/state";
|
||||
import {ActionTypes} from "@/store/action-types";
|
||||
import API from '@/api';
|
||||
import {Mutations} from "@/store/mutations";
|
||||
import {DynmapConfigurationResponse, DynmapMarkerSet, DynmapPlayer, DynmapUpdateResponse} from "@/dynmap";
|
||||
import {
|
||||
DynmapAreaUpdate, DynmapCircleUpdate,
|
||||
DynmapConfigurationResponse, DynmapLineUpdate,
|
||||
DynmapMarkerSet,
|
||||
DynmapMarkerUpdate,
|
||||
DynmapPlayer,
|
||||
DynmapUpdateResponse
|
||||
} from "@/dynmap";
|
||||
|
||||
type AugmentedActionContext = {
|
||||
commit<K extends keyof Mutations>(
|
||||
@ -27,6 +34,22 @@ export interface Actions {
|
||||
{commit}: AugmentedActionContext,
|
||||
payload: Set<DynmapPlayer>
|
||||
):Promise<Map<string, DynmapMarkerSet>>
|
||||
[ActionTypes.POP_MARKER_UPDATES](
|
||||
{commit}: AugmentedActionContext,
|
||||
payload: {markerSet: string, amount: number}
|
||||
): Promise<DynmapMarkerUpdate[]>
|
||||
[ActionTypes.POP_AREA_UPDATES](
|
||||
{commit}: AugmentedActionContext,
|
||||
payload: {markerSet: string, amount: number}
|
||||
): Promise<DynmapAreaUpdate[]>
|
||||
[ActionTypes.POP_CIRCLE_UPDATES](
|
||||
{commit}: AugmentedActionContext,
|
||||
payload: {markerSet: string, amount: number}
|
||||
): Promise<DynmapCircleUpdate[]>
|
||||
[ActionTypes.POP_LINE_UPDATES](
|
||||
{commit}: AugmentedActionContext,
|
||||
payload: {markerSet: string, amount: number}
|
||||
): Promise<DynmapLineUpdate[]>
|
||||
}
|
||||
|
||||
export const actions: ActionTree<State, State> & Actions = {
|
||||
@ -52,10 +75,11 @@ export const actions: ActionTree<State, State> & Actions = {
|
||||
return Promise.reject("No current world");
|
||||
}
|
||||
|
||||
return API.getUpdate(state.updateRequestId, state.currentWorld.name, state.updateTimestamp.getUTCMilliseconds()).then(update => {
|
||||
return API.getUpdate(state.updateRequestId, state.currentWorld.name, state.updateTimestamp.valueOf()).then(update => {
|
||||
commit(MutationTypes.SET_WORLD_STATE, update.worldState);
|
||||
commit(MutationTypes.SET_UPDATE_TIMESTAMP, new Date(update.timestamp));
|
||||
commit(MutationTypes.INCREMENT_REQUEST_ID, undefined);
|
||||
commit(MutationTypes.ADD_MARKER_SET_UPDATES, update.updates.markerSets);
|
||||
|
||||
return dispatch(ActionTypes.SET_PLAYERS, update.players).then(() => {
|
||||
return update;
|
||||
@ -97,5 +121,58 @@ export const actions: ActionTree<State, State> & Actions = {
|
||||
|
||||
return markerSets;
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
[ActionTypes.POP_MARKER_UPDATES]({commit, state}, {markerSet, amount}: {markerSet: string, amount: number}): Promise<DynmapMarkerUpdate[]> {
|
||||
if(!state.markerSets.has(markerSet)) {
|
||||
console.log(`Marker set ${markerSet} doesn't exist`);
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
|
||||
const updates = state.pendingSetUpdates.get(markerSet)!.markerUpdates.slice(0, amount);
|
||||
|
||||
commit(MutationTypes.POP_MARKER_UPDATES, {markerSet, amount});
|
||||
|
||||
return Promise.resolve(updates);
|
||||
},
|
||||
|
||||
[ActionTypes.POP_AREA_UPDATES]({commit, state}, {markerSet, amount}: {markerSet: string, amount: number}): Promise<DynmapAreaUpdate[]> {
|
||||
if(!state.markerSets.has(markerSet)) {
|
||||
console.log(`Marker set ${markerSet} doesn't exist`);
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
|
||||
const updates = state.pendingSetUpdates.get(markerSet)!.areaUpdates.slice(0, amount);
|
||||
|
||||
commit(MutationTypes.POP_AREA_UPDATES, {markerSet, amount});
|
||||
|
||||
return Promise.resolve(updates);
|
||||
},
|
||||
|
||||
[ActionTypes.POP_CIRCLE_UPDATES]({commit, state}, {markerSet, amount}: {markerSet: string, amount: number}): Promise<DynmapCircleUpdate[]> {
|
||||
if(!state.markerSets.has(markerSet)) {
|
||||
console.log(`Marker set ${markerSet} doesn't exist`);
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
|
||||
const updates = state.pendingSetUpdates.get(markerSet)!.circleUpdates.slice(0, amount);
|
||||
|
||||
commit(MutationTypes.POP_CIRCLE_UPDATES, {markerSet, amount});
|
||||
|
||||
return Promise.resolve(updates);
|
||||
},
|
||||
|
||||
[ActionTypes.POP_LINE_UPDATES]({commit, state}, {markerSet, amount}: {markerSet: string, amount: number}): Promise<DynmapLineUpdate[]> {
|
||||
if(!state.markerSets.has(markerSet)) {
|
||||
console.log(`Marker set ${markerSet} doesn't exist`);
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
|
||||
const updates = state.pendingSetUpdates.get(markerSet)!.lineUpdates.slice(0, amount);
|
||||
|
||||
commit(MutationTypes.POP_LINE_UPDATES, {markerSet, amount});
|
||||
|
||||
return Promise.resolve(updates);
|
||||
},
|
||||
|
||||
}
|
@ -7,6 +7,11 @@ export enum MutationTypes {
|
||||
ADD_WORLD = 'addWorld',
|
||||
SET_WORLD_STATE = 'setWorldState',
|
||||
SET_UPDATE_TIMESTAMP = 'setUpdateTimestamp',
|
||||
ADD_MARKER_SET_UPDATES = 'addMarkerSetUpdates',
|
||||
POP_MARKER_UPDATES = 'popMarkerUpdates',
|
||||
POP_AREA_UPDATES = 'popAreaUpdates',
|
||||
POP_CIRCLE_UPDATES = 'popCircleUpdates',
|
||||
POP_LINE_UPDATES = 'popLineUpdates',
|
||||
INCREMENT_REQUEST_ID = 'incrementRequestId',
|
||||
SET_PLAYERS = 'setPlayers',
|
||||
SET_PLAYERS_ASYNC = 'setPlayersAsync',
|
||||
|
@ -2,7 +2,17 @@ import {MutationTree} from "vuex";
|
||||
import {MutationTypes} from "@/store/mutation-types";
|
||||
import {State} from "@/store/state";
|
||||
import {
|
||||
DynmapComponentConfig, DynmapMarkerSet,
|
||||
DynmapArea,
|
||||
DynmapAreaUpdate,
|
||||
DynmapCircle,
|
||||
DynmapCircleUpdate,
|
||||
DynmapComponentConfig,
|
||||
DynmapLine,
|
||||
DynmapLineUpdate,
|
||||
DynmapMarker,
|
||||
DynmapMarkerSet,
|
||||
DynmapMarkerSetUpdates,
|
||||
DynmapMarkerUpdate,
|
||||
DynmapMessageConfig,
|
||||
DynmapPlayer,
|
||||
DynmapServerConfig,
|
||||
@ -25,9 +35,15 @@ export type Mutations<S = State> = {
|
||||
[MutationTypes.ADD_WORLD](state: S, world: DynmapWorld): void
|
||||
[MutationTypes.SET_WORLD_STATE](state: S, worldState: DynmapWorldState): void
|
||||
[MutationTypes.SET_UPDATE_TIMESTAMP](state: S, time: Date): void
|
||||
[MutationTypes.ADD_MARKER_SET_UPDATES](state: S, updates: Map<string, DynmapMarkerSetUpdates>): void
|
||||
|
||||
[MutationTypes.POP_MARKER_UPDATES](state: S, payload: {markerSet: string, amount: number}): Array<DynmapMarkerUpdate>
|
||||
[MutationTypes.POP_AREA_UPDATES](state: S, payload: {markerSet: string, amount: number}): Array<DynmapAreaUpdate>
|
||||
[MutationTypes.POP_CIRCLE_UPDATES](state: S, payload: {markerSet: string, amount: number}): Array<DynmapCircleUpdate>
|
||||
[MutationTypes.POP_LINE_UPDATES](state: S, payload: {markerSet: string, amount: number}): Array<DynmapLineUpdate>
|
||||
|
||||
[MutationTypes.INCREMENT_REQUEST_ID](state: S): void
|
||||
// [MutationTypes.SET_PLAYERS](state: S, players: Array<DynmapPlayer>): void
|
||||
[MutationTypes.SET_PLAYERS_ASYNC](state: S, players: Set<DynmapPlayer>): void
|
||||
[MutationTypes.SET_PLAYERS_ASYNC](state: S, players: Set<DynmapPlayer>): Set<DynmapPlayer>
|
||||
[MutationTypes.SYNC_PLAYERS](state: S, keep: Set<string>): void
|
||||
[MutationTypes.SET_CURRENT_MAP](state: S, payload: CurrentMapPayload): void
|
||||
[MutationTypes.SET_CURRENT_PROJECTION](state: S, payload: DynmapProjection): void
|
||||
@ -73,6 +89,16 @@ export const mutations: MutationTree<State> & Mutations = {
|
||||
//Sets the existing marker sets from the last marker fetch
|
||||
[MutationTypes.SET_MARKER_SETS](state: State, markerSets: Map<string, DynmapMarkerSet>) {
|
||||
state.markerSets = markerSets;
|
||||
state.pendingSetUpdates.clear();
|
||||
|
||||
for(const entry of markerSets) {
|
||||
state.pendingSetUpdates.set(entry[0], {
|
||||
markerUpdates: [],
|
||||
areaUpdates: [],
|
||||
circleUpdates: [],
|
||||
lineUpdates: [],
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
[MutationTypes.ADD_WORLD](state: State, world: DynmapWorld) {
|
||||
@ -89,46 +115,101 @@ export const mutations: MutationTree<State> & Mutations = {
|
||||
state.updateTimestamp = timestamp;
|
||||
},
|
||||
|
||||
//Sets the timestamp of the last update fetch
|
||||
[MutationTypes.ADD_MARKER_SET_UPDATES](state: State, updates: Map<string, DynmapMarkerSetUpdates>) {
|
||||
for(const entry of updates) {
|
||||
if(!state.markerSets.has(entry[0])) {
|
||||
console.log(`Marker set ${entry[0]} doesn't exist`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const set = state.markerSets.get(entry[0]) as DynmapMarkerSet,
|
||||
setUpdates = state.pendingSetUpdates.get(entry[0]) as DynmapMarkerSetUpdates;
|
||||
|
||||
//Update non-reactive lists
|
||||
for(const update of entry[1].markerUpdates) {
|
||||
if(update.removed) {
|
||||
set.markers.delete(update.id);
|
||||
} else {
|
||||
set.markers.set(update.id, update.payload as DynmapMarker);
|
||||
}
|
||||
}
|
||||
|
||||
for(const update of entry[1].areaUpdates) {
|
||||
if(update.removed) {
|
||||
set.areas.delete(update.id);
|
||||
} else {
|
||||
set.areas.set(update.id, update.payload as DynmapArea);
|
||||
}
|
||||
}
|
||||
|
||||
for(const update of entry[1].circleUpdates) {
|
||||
if(update.removed) {
|
||||
set.circles.delete(update.id);
|
||||
} else {
|
||||
set.circles.set(update.id, update.payload as DynmapCircle);
|
||||
}
|
||||
}
|
||||
|
||||
for(const update of entry[1].lineUpdates) {
|
||||
if(update.removed) {
|
||||
set.lines.delete(update.id);
|
||||
} else {
|
||||
set.lines.set(update.id, update.payload as DynmapLine);
|
||||
}
|
||||
}
|
||||
|
||||
//Add to reactive pending updates lists
|
||||
setUpdates.markerUpdates = setUpdates.markerUpdates.concat(entry[1].markerUpdates);
|
||||
setUpdates.areaUpdates = setUpdates.areaUpdates.concat(entry[1].areaUpdates);
|
||||
setUpdates.circleUpdates = setUpdates.circleUpdates.concat(entry[1].circleUpdates);
|
||||
setUpdates.lineUpdates = setUpdates.lineUpdates.concat(entry[1].lineUpdates);
|
||||
}
|
||||
},
|
||||
|
||||
[MutationTypes.POP_MARKER_UPDATES](state: State, {markerSet, amount}): Array<DynmapMarkerUpdate> {
|
||||
if(!state.markerSets.has(markerSet)) {
|
||||
console.log(`Marker set ${markerSet} doesn't exist`);
|
||||
return [];
|
||||
}
|
||||
|
||||
return state.pendingSetUpdates.get(markerSet)!.markerUpdates.splice(0, amount);
|
||||
},
|
||||
|
||||
[MutationTypes.POP_AREA_UPDATES](state: State, {markerSet, amount}): Array<DynmapAreaUpdate> {
|
||||
if(!state.markerSets.has(markerSet)) {
|
||||
console.log(`Marker set ${markerSet} doesn't exist`);
|
||||
return [];
|
||||
}
|
||||
|
||||
return state.pendingSetUpdates.get(markerSet)!.areaUpdates.splice(0, amount);
|
||||
},
|
||||
|
||||
[MutationTypes.POP_CIRCLE_UPDATES](state: State, {markerSet, amount}): Array<DynmapCircleUpdate> {
|
||||
if(!state.markerSets.has(markerSet)) {
|
||||
console.log(`Marker set ${markerSet} doesn't exist`);
|
||||
return [];
|
||||
}
|
||||
|
||||
return state.pendingSetUpdates.get(markerSet)!.circleUpdates.splice(0, amount);
|
||||
},
|
||||
|
||||
[MutationTypes.POP_LINE_UPDATES](state: State, {markerSet, amount}): Array<DynmapLineUpdate> {
|
||||
if(!state.markerSets.has(markerSet)) {
|
||||
console.log(`Marker set ${markerSet} doesn't exist`);
|
||||
return [];
|
||||
}
|
||||
|
||||
return state.pendingSetUpdates.get(markerSet)!.lineUpdates.splice(0, amount);
|
||||
},
|
||||
|
||||
//Increments the request id for the next update fetch
|
||||
[MutationTypes.INCREMENT_REQUEST_ID](state: State) {
|
||||
state.updateRequestId++;
|
||||
},
|
||||
|
||||
// [MutationTypes.SET_PLAYERS](state: State, players: Array<DynmapPlayer>) {
|
||||
// const existingPlayers: Set<string> = new Set();
|
||||
//
|
||||
// players.forEach(player => {
|
||||
// existingPlayers.add(player.account);
|
||||
//
|
||||
// if (state.players.has(player.account)) {
|
||||
// const existing = state.players.get(player.account);
|
||||
//
|
||||
// existing!.health = player.health;
|
||||
// existing!.armor = player.armor;
|
||||
// existing!.location = Object.assign(existing!.location, player.location);
|
||||
// existing!.name = player.name;
|
||||
// existing!.sort = player.sort;
|
||||
// } else {
|
||||
// state.players.set(player.account, {
|
||||
// account: player.account,
|
||||
// health: player.health,
|
||||
// armor: player.armor,
|
||||
// location: player.location,
|
||||
// name: player.name,
|
||||
// sort: player.sort,
|
||||
// });
|
||||
// }
|
||||
// });
|
||||
//
|
||||
// for (const key of state.players.keys()) {
|
||||
// if (!existingPlayers.has(key)) {
|
||||
// state.players.delete(key);
|
||||
// }
|
||||
// }
|
||||
// },
|
||||
|
||||
//Set up to 10 players at once, returning the rest for future setting
|
||||
[MutationTypes.SET_PLAYERS_ASYNC](state: State, players: Set<DynmapPlayer>) {
|
||||
// Set up to 10 players at once
|
||||
[MutationTypes.SET_PLAYERS_ASYNC](state: State, players: Set<DynmapPlayer>): Set<DynmapPlayer> {
|
||||
let count = 0;
|
||||
|
||||
for(const player of players) {
|
||||
@ -154,9 +235,11 @@ export const mutations: MutationTree<State> & Mutations = {
|
||||
players.delete(player);
|
||||
|
||||
if(++count >= 10) {
|
||||
return players;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return players;
|
||||
},
|
||||
|
||||
//Removes all players not found in the provided keep set
|
||||
@ -184,6 +267,8 @@ export const mutations: MutationTree<State> & Mutations = {
|
||||
if(state.currentWorld !== newWorld) {
|
||||
state.currentWorld = state.worlds.get(worldName);
|
||||
state.markerSets.clear();
|
||||
state.pendingSetUpdates.clear();
|
||||
state.pendingTileUpdates = [];
|
||||
}
|
||||
|
||||
state.currentMap = state.maps.get(mapName);
|
||||
|
@ -1,6 +1,6 @@
|
||||
import {
|
||||
DynmapComponentConfig,
|
||||
DynmapMap, DynmapMarker, DynmapMarkerSet,
|
||||
DynmapMap, DynmapMarkerSet, DynmapMarkerSetUpdates,
|
||||
DynmapMessageConfig,
|
||||
DynmapPlayer,
|
||||
DynmapServerConfig,
|
||||
@ -18,9 +18,11 @@ export type State = {
|
||||
players: Map<string, DynmapPlayer>;
|
||||
markerSets: Map<string, DynmapMarkerSet>;
|
||||
|
||||
pendingSetUpdates: Map<string, DynmapMarkerSetUpdates>;
|
||||
pendingTileUpdates: Array<string>;
|
||||
|
||||
following?: DynmapPlayer;
|
||||
|
||||
// currentServer?: string;
|
||||
currentWorldState: DynmapWorldState;
|
||||
currentWorld?: DynmapWorld;
|
||||
currentMap?: DynmapMap;
|
||||
@ -65,7 +67,10 @@ export const state: State = {
|
||||
worlds: new Map(), //Defined (loaded) worlds with maps from configuration.json
|
||||
maps: new Map(), //Defined maps from configuration.json
|
||||
players: new Map(), //Online players from world.json
|
||||
|
||||
markerSets: new Map(), //Markers from world_markers.json. Contents of each set isn't reactive for performance reasons.
|
||||
pendingSetUpdates: new Map(), //Pending updates to markers/areas/etc for each marker set
|
||||
pendingTileUpdates: [], //Pending updates to map tiles
|
||||
|
||||
//Dynmap optional components
|
||||
components: {
|
||||
@ -73,6 +78,7 @@ export const state: State = {
|
||||
markers: {
|
||||
showLabels: false,
|
||||
},
|
||||
|
||||
// Optional "playermarkers" component. Settings for online player markers.
|
||||
// If not present, player markers will be disabled
|
||||
playerMarkers: undefined,
|
||||
@ -83,22 +89,23 @@ export const state: State = {
|
||||
//Optional clock component. Used for both "digitalclock" and "timeofdayclock". Shows world time/weather.
|
||||
clockControl: undefined,
|
||||
|
||||
//Optional "link" component. Adds button to get url for current position
|
||||
//Optional "link" component. Adds button to copy url for current position
|
||||
linkControl: false,
|
||||
|
||||
//Optional "logo" controls.
|
||||
logoControls: [],
|
||||
},
|
||||
|
||||
following: undefined,
|
||||
|
||||
currentWorld: undefined,
|
||||
currentMap: undefined,
|
||||
currentProjection: new DynmapProjection(), //Projection for converting location <-> latlg. Object itself isn't reactive for performance reasons
|
||||
currentWorldState: {
|
||||
raining: false,
|
||||
thundering: false,
|
||||
timeOfDay: 0,
|
||||
},
|
||||
currentMap: undefined,
|
||||
currentProjection: new DynmapProjection(), //Projection for converting location <-> latlg. Object itself isn't reactive for performance reasons
|
||||
|
||||
updateRequestId: 0,
|
||||
updateTimestamp: new Date(),
|
||||
|
@ -1,4 +1,5 @@
|
||||
import {DynmapPlayer} from "@/dynmap";
|
||||
import {useStore} from "@/store";
|
||||
|
||||
const headCache = new Map<DynmapPlayer, HTMLImageElement>();
|
||||
|
||||
@ -49,5 +50,13 @@ export default {
|
||||
}
|
||||
|
||||
return base + addition;
|
||||
},
|
||||
|
||||
getPointConverter() {
|
||||
const projection = useStore().state.currentProjection;
|
||||
|
||||
return (x: number, y: number, z: number) => {
|
||||
return projection.locationToLatLng({x, y, z});
|
||||
};
|
||||
}
|
||||
}
|
170
src/util/areas.ts
Normal file
170
src/util/areas.ts
Normal file
@ -0,0 +1,170 @@
|
||||
import {LatLngExpression, Polygon, Polyline} from "leaflet";
|
||||
import {DynmapArea} from "@/dynmap";
|
||||
|
||||
export const createArea = (options: DynmapArea, converter: Function): Polyline | Polygon => {
|
||||
const outline = !options.style.fillOpacity || (options.style.fillOpacity <= 0),
|
||||
points = getPoints(options, converter, outline),
|
||||
area = outline ? new Polyline(points, options.style) : new Polygon(points, options.style);
|
||||
|
||||
if (options.label) {
|
||||
area.bindPopup(() => createPopup(options));
|
||||
}
|
||||
|
||||
return area;
|
||||
};
|
||||
|
||||
export const updateArea = (area: Polyline | Polygon | undefined, options: DynmapArea, converter: Function): Polyline | Polygon => {
|
||||
const outline = (options.style && options.style.fillOpacity && (options.style.fillOpacity <= 0)) as boolean,
|
||||
points = getPoints(options, converter, outline);
|
||||
|
||||
if (!area) {
|
||||
return createArea(options, converter);
|
||||
}
|
||||
|
||||
area.unbindPopup();
|
||||
area.bindPopup(() => createPopup(options));
|
||||
area.setStyle(options.style);
|
||||
area.setLatLngs(points);
|
||||
area.redraw();
|
||||
|
||||
return area;
|
||||
};
|
||||
|
||||
export const createPopup = (options: DynmapArea): HTMLElement => {
|
||||
const popup = document.createElement('span');
|
||||
|
||||
if (options.popupContent) {
|
||||
popup.classList.add('AreaPopup');
|
||||
popup.insertAdjacentHTML('afterbegin', options.popupContent);
|
||||
} else if (options.isHTML) {
|
||||
popup.classList.add('AreaPopup');
|
||||
popup.insertAdjacentHTML('afterbegin', options.label);
|
||||
} else {
|
||||
popup.textContent = options.label;
|
||||
}
|
||||
|
||||
return popup;
|
||||
};
|
||||
|
||||
export const getPoints = (options: DynmapArea, converter: Function, outline: boolean): LatLngExpression[] | LatLngExpression[][] => {
|
||||
if (options.x.length === 2) { /* Only 2 points */
|
||||
if (options.y[0] === options.y[1]) {
|
||||
return get2DBoxPoints(options, converter, outline);
|
||||
} else {
|
||||
return get3DBoxPoints(options, converter);
|
||||
}
|
||||
} else {
|
||||
if (options.y[0] === options.y[1]) {
|
||||
return get2DShapePoints(options, converter, outline);
|
||||
} else {
|
||||
return get3DShapePoints(options, converter);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const get3DBoxPoints = (options: DynmapArea, converter: Function): LatLngExpression[][] => {
|
||||
const maxX = options.x[0],
|
||||
minX = options.x[1],
|
||||
maxY = options.y[0],
|
||||
minY = options.y[1],
|
||||
maxZ = options.z[0],
|
||||
minZ = options.z[1];
|
||||
|
||||
return [
|
||||
[
|
||||
converter(minX, minY, minZ),
|
||||
converter(maxX, minY, minZ),
|
||||
converter(maxX, minY, maxZ),
|
||||
converter(minX, minY, maxZ)
|
||||
], [
|
||||
converter(minX, maxY, minZ),
|
||||
converter(maxX, maxY, minZ),
|
||||
converter(maxX, maxY, maxZ),
|
||||
converter(minX, maxY, maxZ)
|
||||
], [
|
||||
converter(minX, minY, minZ),
|
||||
converter(minX, maxY, minZ),
|
||||
converter(maxX, maxY, minZ),
|
||||
converter(maxX, minY, minZ)
|
||||
], [
|
||||
converter(maxX, minY, minZ),
|
||||
converter(maxX, maxY, minZ),
|
||||
converter(maxX, maxY, maxZ),
|
||||
converter(maxX, minY, maxZ)
|
||||
], [
|
||||
converter(minX, minY, maxZ),
|
||||
converter(minX, maxY, maxZ),
|
||||
converter(maxX, maxY, maxZ),
|
||||
converter(maxX, minY, maxZ)
|
||||
], [
|
||||
converter(minX, minY, minZ),
|
||||
converter(minX, maxY, minZ),
|
||||
converter(minX, maxY, maxZ),
|
||||
converter(minX, minY, maxZ)
|
||||
]
|
||||
];
|
||||
};
|
||||
|
||||
export const get2DBoxPoints = (options: DynmapArea, converter: Function, outline: boolean): LatLngExpression[] => {
|
||||
const maxX = options.x[0],
|
||||
minX = options.x[1],
|
||||
minY = options.y[1],
|
||||
maxZ = options.z[0],
|
||||
minZ = options.z[1];
|
||||
|
||||
if (outline) {
|
||||
return [
|
||||
converter(minX, minY, minZ),
|
||||
converter(maxX, minY, minZ),
|
||||
converter(maxX, minY, maxZ),
|
||||
converter(minX, minY, maxZ),
|
||||
converter(minX, minY, minZ)
|
||||
];
|
||||
} else {
|
||||
return [
|
||||
converter(minX, minY, minZ),
|
||||
converter(maxX, minY, minZ),
|
||||
converter(maxX, minY, maxZ),
|
||||
converter(minX, minY, maxZ)
|
||||
];
|
||||
}
|
||||
};
|
||||
|
||||
export const get3DShapePoints = (options: DynmapArea, converter: Function): LatLngExpression[][] => {
|
||||
const toplist = [],
|
||||
botlist = [],
|
||||
polylist = [];
|
||||
|
||||
for (let i = 0; i < options.x.length; i++) {
|
||||
toplist[i] = converter(options.x[i], options.y[0], options.z[i]);
|
||||
botlist[i] = converter(options.x[i], options.y[1], options.z[i]);
|
||||
}
|
||||
|
||||
for (let i = 0; i < options.x.length; i++) {
|
||||
const sidelist = [];
|
||||
sidelist[0] = toplist[i];
|
||||
sidelist[1] = botlist[i];
|
||||
sidelist[2] = botlist[(i + 1) % options.x.length];
|
||||
sidelist[3] = toplist[(i + 1) % options.x.length];
|
||||
polylist[i] = sidelist;
|
||||
}
|
||||
|
||||
polylist[options.x.length] = botlist;
|
||||
polylist[options.x.length + 1] = toplist;
|
||||
|
||||
return polylist;
|
||||
};
|
||||
|
||||
export const get2DShapePoints = (options: DynmapArea, converter: Function, outline: boolean): LatLngExpression[] => {
|
||||
const points = [];
|
||||
|
||||
for (let i = 0; i < options.x.length; i++) {
|
||||
points[i] = converter(options.x[i], options.y[1], options.z[i]);
|
||||
}
|
||||
|
||||
if (outline) {
|
||||
points.push(points[0]);
|
||||
}
|
||||
|
||||
return points;
|
||||
}
|
71
src/util/circles.ts
Normal file
71
src/util/circles.ts
Normal file
@ -0,0 +1,71 @@
|
||||
import {DynmapCircle} from "@/dynmap";
|
||||
import {Polyline, Polygon, LatLngExpression} from "leaflet";
|
||||
|
||||
export const createCircle = (options: DynmapCircle, converter: Function): Polyline | Polygon => {
|
||||
const outline = !options.style.fillOpacity || (options.style.fillOpacity <= 0),
|
||||
points = getCirclePoints(options, converter, outline);
|
||||
let circle;
|
||||
|
||||
if(outline) {
|
||||
circle = new Polyline(points, options.style);
|
||||
} else {
|
||||
circle = new Polygon(points, options.style);
|
||||
}
|
||||
|
||||
if(options.label) {
|
||||
circle.bindPopup(() => createPopup(options));
|
||||
}
|
||||
|
||||
return circle;
|
||||
};
|
||||
|
||||
export const updateCircle = (circle: Polyline | Polygon | undefined, options: DynmapCircle, converter: Function): Polyline | Polygon => {
|
||||
const outline = (options.style && options.style.fillOpacity && (options.style.fillOpacity <= 0)) as boolean,
|
||||
points = getCirclePoints(options, converter, outline);
|
||||
|
||||
if (!circle) {
|
||||
return createCircle(options, converter);
|
||||
}
|
||||
|
||||
circle.unbindPopup();
|
||||
circle.bindPopup(() => createPopup(options));
|
||||
circle.setStyle(options.style);
|
||||
circle.setLatLngs(points);
|
||||
circle.redraw();
|
||||
|
||||
return circle;
|
||||
}
|
||||
|
||||
export const createPopup = (options: DynmapCircle) => {
|
||||
const popup = document.createElement('span');
|
||||
|
||||
if (options.popupContent) {
|
||||
popup.classList.add('CirclePopup');
|
||||
popup.insertAdjacentHTML('afterbegin', options.popupContent);
|
||||
} else if (options.isHTML) {
|
||||
popup.classList.add('CirclePopup');
|
||||
popup.insertAdjacentHTML('afterbegin', options.label);
|
||||
} else {
|
||||
popup.textContent = options.label;
|
||||
}
|
||||
|
||||
return popup;
|
||||
}
|
||||
|
||||
export const getCirclePoints = (options: DynmapCircle, converter: Function, outline: boolean): LatLngExpression[] => {
|
||||
const points = [];
|
||||
|
||||
for(let i = 0; i < 360; i++) {
|
||||
const rad = i * Math.PI / 180.0,
|
||||
x = options.radius[0] * Math.sin(rad) + options.location.x,
|
||||
z = options.radius[1] * Math.cos(rad) + options.location.z;
|
||||
|
||||
points.push(converter(x, options.location.y, z));
|
||||
}
|
||||
|
||||
if(outline && points.length) {
|
||||
points.push(points[0]);
|
||||
}
|
||||
|
||||
return points;
|
||||
};
|
55
src/util/lines.ts
Normal file
55
src/util/lines.ts
Normal file
@ -0,0 +1,55 @@
|
||||
import {DynmapLine} from "@/dynmap";
|
||||
import {Polyline, Polygon, LatLngExpression} from "leaflet";
|
||||
|
||||
export const createLine = (options: DynmapLine, converter: Function): Polyline | Polygon => {
|
||||
const points = getLinePoints(options, converter),
|
||||
line = new Polyline(points, options.style);
|
||||
|
||||
if(options.label) {
|
||||
line.bindPopup(() => createPopup(options));
|
||||
}
|
||||
|
||||
return line;
|
||||
};
|
||||
|
||||
export const updateLine = (line: Polyline | Polygon | undefined, options: DynmapLine, converter: Function): Polyline | Polygon => {
|
||||
const points = getLinePoints(options, converter);
|
||||
|
||||
if (!line) {
|
||||
return createLine(options, converter);
|
||||
}
|
||||
|
||||
line.unbindPopup();
|
||||
line.bindPopup(() => createPopup(options));
|
||||
line.setStyle(options.style);
|
||||
line.setLatLngs(points);
|
||||
line.redraw();
|
||||
|
||||
return line;
|
||||
}
|
||||
|
||||
export const createPopup = (options: DynmapLine) => {
|
||||
const popup = document.createElement('span');
|
||||
|
||||
if (options.popupContent) {
|
||||
popup.classList.add('LinePopup');
|
||||
popup.insertAdjacentHTML('afterbegin', options.popupContent);
|
||||
} else if (options.isHTML) {
|
||||
popup.classList.add('LinePopup');
|
||||
popup.insertAdjacentHTML('afterbegin', options.label);
|
||||
} else {
|
||||
popup.textContent = options.label;
|
||||
}
|
||||
|
||||
return popup;
|
||||
}
|
||||
|
||||
export const getLinePoints = (options: DynmapLine, converter: Function): LatLngExpression[] => {
|
||||
const points = [];
|
||||
|
||||
for(let i = 0; i < options.x.length; i++) {
|
||||
points.push(converter(options.x[i], options.y[i], options.z[i]));
|
||||
}
|
||||
|
||||
return points;
|
||||
};
|
Loading…
Reference in New Issue
Block a user