Added area editing functions

This commit is contained in:
Pax1601
2023-06-19 17:14:53 +02:00
parent f9f02c3eb0
commit 635c487c2b
24 changed files with 685 additions and 169 deletions

View File

@@ -1,40 +1,24 @@
import { LatLng, LatLngExpression, Polygon, PolylineOptions } from "leaflet";
import { LatLng, LatLngExpression, Map, Point, Polygon, PolylineOptions } from "leaflet";
import { getMap } from "..";
import { DRAW_POLYGON } from "./map";
import { CoalitionAreaHandle } from "./coalitionareahandle";
import { CoalitionAreaMiddleHandle } from "./coalitionareamiddlehandle";
export class CoalitionArea extends Polygon {
#coalition: string = "blue";
#selected: boolean = true;
#editing: boolean = true;
#handles: CoalitionAreaHandle[] = [];
#middleHandles: CoalitionAreaMiddleHandle[] = [];
#activeIndex: number = 0;
constructor(latlngs: LatLngExpression[] | LatLngExpression[][] | LatLngExpression[][][], options?: PolylineOptions) {
if (options === undefined)
if (options === undefined)
options = {};
options.bubblingMouseEvents = false;
super(latlngs, options);
this.on("click", (e: any) => {
if (!this.getSelected()) {
this.setSelected(true);
getMap().setState(DRAW_POLYGON);
}
else if (this.getEditing()) {
this.addLatLng(e.latlng);
this.addTemporaryLatLng(e.latlng);
}
});
this.on("contextmenu", (e: any) => {
if (!this.#editing)
getMap().showCoalitionAreaContextMenu(e, this);
else
this.setEditing(false);
});
this.#setColors();
this.#registerCallbacks();
}
setCoalition(coalition: string) {
@@ -50,10 +34,15 @@ export class CoalitionArea extends Polygon {
this.#selected = selected;
this.#setColors();
this.#setHandles();
if (!selected)
if (!this.getSelected() && this.getEditing()) {
/* Remove the vertex we were working on */
var latlngs = this.getLatLngs()[0] as LatLng[];
latlngs.splice(this.#activeIndex, 1);
this.setLatLngs(latlngs);
this.setEditing(false);
}
}
getSelected() {
return this.#selected;
}
@@ -61,6 +50,11 @@ export class CoalitionArea extends Polygon {
setEditing(editing: boolean) {
this.#editing = editing;
this.#setHandles();
var latlngs = this.getLatLngs()[0] as LatLng[];
/* Remove areas with less than 2 vertexes */
if (latlngs.length <= 2)
getMap().deleteCoalitionArea(this);
}
getEditing() {
@@ -68,38 +62,92 @@ export class CoalitionArea extends Polygon {
}
addTemporaryLatLng(latlng: LatLng) {
this.addLatLng(latlng);
this.#activeIndex++;
var latlngs = this.getLatLngs()[0] as LatLng[];
latlngs.splice(this.#activeIndex, 0, latlng);
this.setLatLngs(latlngs);
this.#setHandles();
}
moveTemporaryLatLng(latlng: LatLng) {
var latlngs = this.getLatLngs()[0] as LatLng[];
latlngs[latlngs.length - 1] = latlng;
latlngs[this.#activeIndex] = latlng;
this.setLatLngs(latlngs);
}
addLatLng(latlng: LatLngExpression | LatLngExpression[], latlngs?: LatLng[] | undefined): this {
super.addLatLng(latlng, latlngs)
this.#setHandles();
return this;
}
#setColors() {
this.setStyle({color: this.getSelected()? "white": this.#coalition, fillColor: this.#coalition});
const coalitionColor = this.getCoalition() === "blue" ? "#247be2" : "#ff5858";
this.setStyle({ color: this.getSelected() ? "white" : coalitionColor, fillColor: coalitionColor });
}
#setHandles() {
this.#handles.forEach((handle: CoalitionAreaHandle) => handle.removeFrom(getMap()));
this.#handles = [];
var latlngs = this.getLatLngs()[0] as LatLng[];
latlngs.forEach((latlng: LatLng, idx: number) => {
const handle = new CoalitionAreaHandle(latlng);
handle.addTo(getMap());
handle.on("dragend", (e: any) => {
var latlngs = this.getLatLngs()[0] as LatLng[];
latlngs[idx] = e.latlng;
this.setLatLngs(latlngs);
if (this.getSelected()) {
var latlngs = this.getLatLngs()[0] as LatLng[];
latlngs.forEach((latlng: LatLng, idx: number) => {
/* Add the polygon vertex handle (for moving the vertex) */
const handle = new CoalitionAreaHandle(latlng);
handle.addTo(getMap());
handle.on("drag", (e: any) => {
var latlngs = this.getLatLngs()[0] as LatLng[];
latlngs[idx] = e.target.getLatLng();
this.setLatLngs(latlngs);
this.#setMiddleHandles();
});
this.#handles.push(handle);
});
this.#handles.push(handle);
}
this.#setMiddleHandles();
}
#setMiddleHandles() {
this.#middleHandles.forEach((handle: CoalitionAreaMiddleHandle) => handle.removeFrom(getMap()));
this.#middleHandles = [];
var latlngs = this.getLatLngs()[0] as LatLng[];
if (this.getSelected() && latlngs.length >= 2) {
var lastLatLng: LatLng | null = null;
latlngs.concat([latlngs[0]]).forEach((latlng: LatLng, idx: number) => {
/* Add the polygon middle point handle (for adding new vertexes) */
if (lastLatLng != null) {
const handle1Point = getMap().latLngToLayerPoint(latlng);
const handle2Point = getMap().latLngToLayerPoint(lastLatLng);
const middlePoint = new Point((handle1Point.x + handle2Point.x) / 2, (handle1Point.y + handle2Point.y) / 2);
const middleLatLng = getMap().layerPointToLatLng(middlePoint);
const middleHandle = new CoalitionAreaMiddleHandle(middleLatLng);
middleHandle.addTo(getMap());
middleHandle.on("click", (e: any) => {
this.#activeIndex = idx - 1;
this.addTemporaryLatLng(middleLatLng);
});
this.#middleHandles.push(middleHandle);
}
lastLatLng = latlng;
});
}
}
#registerCallbacks() {
this.on("click", (e: any) => {
getMap().deselectAllCoalitionAreas();
if (!this.getSelected()) {
this.setSelected(true);
}
});
this.on("contextmenu", (e: any) => {
if (this.getSelected() && !this.getEditing())
getMap().showCoalitionAreaContextMenu(e, this);
else
this.setEditing(false);
});
}
onRemove(map: Map): this {
super.onRemove(map);
this.#handles.concat(this.#middleHandles).forEach((handle: CoalitionAreaHandle | CoalitionAreaMiddleHandle) => handle.removeFrom(getMap()));
return this;
}
}

View File

@@ -8,12 +8,12 @@ export class CoalitionAreaHandle extends CustomMarker {
createIcon() {
this.setIcon(new DivIcon({
iconSize: [52, 52],
iconAnchor: [26, 26],
className: "leaflet-target-marker",
iconSize: [24, 24],
iconAnchor: [12, 12],
className: "leaflet-coalitionarea-handle-marker",
}));
var el = document.createElement("div");
el.classList.add("ol-target-icon");
el.classList.add("ol-coalitionarea-handle-icon");
this.getElement()?.appendChild(el);
}
}

View File

@@ -0,0 +1,19 @@
import { DivIcon, LatLng } from "leaflet";
import { CustomMarker } from "./custommarker";
export class CoalitionAreaMiddleHandle extends CustomMarker {
constructor(latlng: LatLng) {
super(latlng, {interactive: true, draggable: false});
}
createIcon() {
this.setIcon(new DivIcon({
iconSize: [16, 16],
iconAnchor: [8, 8],
className: "leaflet-coalitionarea-middle-handle-marker",
}));
var el = document.createElement("div");
el.classList.add("ol-coalitionarea-middle-handle-icon");
this.getElement()?.appendChild(el);
}
}

View File

@@ -1,7 +1,12 @@
import { DivIcon } from "leaflet";
import { DivIcon, LatLngExpression, MarkerOptions } from "leaflet";
import { CustomMarker } from "./custommarker";
export class DestinationPreviewMarker extends CustomMarker {
constructor(latlng: LatLngExpression, options?: MarkerOptions) {
super(latlng, options);
this.setZIndexOffset(9999);
}
createIcon() {
this.setIcon(new DivIcon({
iconSize: [52, 52],

View File

@@ -0,0 +1,20 @@
import { DivIcon, LatLng } from "leaflet";
import { CustomMarker } from "./custommarker";
export class DrawingCursor extends CustomMarker {
constructor() {
super(new LatLng(0, 0), {interactive: false})
this.setZIndexOffset(9999);
}
createIcon() {
this.setIcon(new DivIcon({
iconSize: [24, 24],
iconAnchor: [0, 24],
className: "leaflet-draw-marker",
}));
var el = document.createElement("div");
el.classList.add("ol-draw-icon");
this.getElement()?.appendChild(el);
}
}

View File

@@ -16,6 +16,7 @@ import { layers as mapLayers, mapBounds, minimapBoundaries } from "../constants/
import { TargetMarker } from "./targetmarker";
import { CoalitionArea } from "./coalitionarea";
import { CoalitionAreaContextMenu } from "../controls/coalitionareacontextmenu";
import { DrawingCursor } from "./drawingmarker";
L.Map.addInitHook('addHandler', 'boxSelect', BoxSelect);
@@ -29,7 +30,7 @@ export const MOVE_UNIT = "Move unit";
export const BOMBING = "Bombing";
export const CARPET_BOMBING = "Carpet bombing";
export const FIRE_AT_AREA = "Fire at area";
export const DRAW_POLYGON = "Draw polygon";
export const DRAW_COALITIONAREA_POLYGON = "Draw Coalition Area";
export const visibilityControls: string[] = ["human", "dcs", "aircraft", "groundunit-sam", "groundunit-other", "navyunit", "airbase"];
export const visibilityControlsTootlips: string[] = ["Toggle human players visibility", "Toggle DCS controlled units visibility", "Toggle aircrafts visibility", "Toggle SAM units visibility", "Toggle ground units (not SAM) visibility", "Toggle navy units visibility", "Toggle airbases visibility"];
@@ -50,12 +51,15 @@ export class Map extends L.Map {
#miniMap: ClickableMiniMap | null = null;
#miniMapLayerGroup: L.LayerGroup;
#temporaryMarkers: TemporaryUnitMarker[] = [];
#destinationPreviewMarkers: DestinationPreviewMarker[] = [];
#targetMarker: TargetMarker;
#destinationGroupRotation: number = 0;
#computeDestinationRotation: boolean = false;
#destinationRotationCenter: L.LatLng | null = null;
#coalitionAreas: CoalitionArea[] = [];
#targetCursor: TargetMarker = new TargetMarker(new L.LatLng(0, 0), {interactive: false});
#destinationPreviewCursors: DestinationPreviewMarker[] = [];
#drawingCursor: DrawingCursor = new DrawingCursor();
#mapContextMenu: MapContextMenu = new MapContextMenu("map-contextmenu");
#unitContextMenu: UnitContextMenu = new UnitContextMenu("unit-contextmenu");
@@ -102,8 +106,8 @@ export class Map extends L.Map {
this.on('mousedown', (e: any) => this.#onMouseDown(e));
this.on('mouseup', (e: any) => this.#onMouseUp(e));
this.on('mousemove', (e: any) => this.#onMouseMove(e));
this.on('keydown', (e: any) => this.#updateDestinationPreview(e));
this.on('keyup', (e: any) => this.#updateDestinationPreview(e));
this.on('keydown', (e: any) => this.#onKeyDown(e));
this.on('keyup', (e: any) => this.#onKeyUp(e));
/* Event listeners */
document.addEventListener("toggleCoalitionVisibility", (ev: CustomEventInit) => {
@@ -120,9 +124,14 @@ export class Map extends L.Map {
Object.values(getUnitsManager().getUnits()).forEach((unit: Unit) => unit.updateVisibility());
});
document.addEventListener("toggleMapDraw", (ev: CustomEventInit) => {
document.addEventListener("toggleCoalitionAreaDraw", (ev: CustomEventInit) => {
const el = ev.detail._element;
document.addEventListener("mapStateChanged", () => el?.classList.toggle("off", !(this.getState() === DRAW_COALITIONAREA_POLYGON)));
if (ev.detail?.type == "polygon") {
this.setState(DRAW_POLYGON);
if (this.getState() !== DRAW_COALITIONAREA_POLYGON)
this.setState(DRAW_COALITIONAREA_POLYGON);
else
this.setState(IDLE);
}
})
@@ -143,9 +152,6 @@ export class Map extends L.Map {
return this.#createOptionButton(option, `visibility/${option.toLowerCase()}.svg`, visibilityControlsTootlips[index], "toggleUnitVisibility", `{"type": "${option}"}`);
});
document.querySelector("#unit-visibility-control")?.append(...this.#optionButtons["visibility"]);
/* Markers */
this.#targetMarker = new TargetMarker(new L.LatLng(0, 0), {interactive: false});
}
setLayer(layerName: string) {
@@ -172,30 +178,17 @@ export class Map extends L.Map {
/* State machine */
setState(state: string) {
this.#state = state;
this.#showCursor();
if (this.#state === IDLE) {
this.#resetDestinationMarkers();
this.#resetTargetMarker();
this.#deselectCoalitionAreas();
this.#showCursor();
}
else if (this.#state === MOVE_UNIT) {
this.#resetTargetMarker();
this.#deselectCoalitionAreas();
this.#createDestinationMarkers();
if (this.#destinationPreviewMarkers.length > 0)
this.#hideCursor();
}
else if ([BOMBING, CARPET_BOMBING, FIRE_AT_AREA].includes(this.#state)) {
this.#resetDestinationMarkers();
this.#deselectCoalitionAreas();
this.#createTargetMarker();
this.#hideCursor();
}
else if (this.#state === DRAW_POLYGON) {
this.#resetDestinationMarkers();
this.#resetTargetMarker();
this.#showCursor();
//@ts-ignore draggable option added by plugin
else if (this.#state === DRAW_COALITIONAREA_POLYGON) {
this.#coalitionAreas.push(new CoalitionArea([]));
this.#coalitionAreas[this.#coalitionAreas.length - 1].addTo(this);
}
@@ -206,6 +199,17 @@ export class Map extends L.Map {
return this.#state;
}
deselectAllCoalitionAreas() {
this.#coalitionAreas.forEach((coalitionArea: CoalitionArea) => coalitionArea.setSelected(false));
}
deleteCoalitionArea(coalitionArea: CoalitionArea) {
if (this.#coalitionAreas.includes(coalitionArea))
this.#coalitionAreas.splice(this.#coalitionAreas.indexOf(coalitionArea), 1);
if (this.hasLayer(coalitionArea))
this.removeLayer(coalitionArea);
}
/* Context Menus */
hideAllContextMenus() {
this.hideMapContextMenu();
@@ -393,7 +397,7 @@ export class Map extends L.Map {
});
if (closest) {
this.removeLayer(closest);
delete this.#temporaryMarkers[i];
this.#temporaryMarkers.splice(i, 1);
}
}
@@ -406,16 +410,14 @@ export class Map extends L.Map {
if (!this.#preventLeftClick) {
this.hideAllContextMenus();
if (this.#state === IDLE) {
this.deselectAllCoalitionAreas();
}
else if (this.#state === DRAW_POLYGON) {
/* This gets only called to create the first point of the area. All other points are added by the area itself */
else if (this.#state === DRAW_COALITIONAREA_POLYGON) {
if (this.getSelectedCoalitionArea()?.getEditing()) {
this.getSelectedCoalitionArea()?.addLatLng(e.latlng);
this.getSelectedCoalitionArea()?.addTemporaryLatLng(e.latlng);
}
else {
this.getSelectedCoalitionArea()?.setSelected(false);
this.deselectAllCoalitionAreas();
}
}
else {
@@ -426,7 +428,7 @@ export class Map extends L.Map {
}
#onDoubleClick(e: any) {
this.deselectAllCoalitionAreas();
}
#onContextMenu(e: any) {
@@ -492,20 +494,34 @@ export class Map extends L.Map {
this.#lastMousePosition.x = e.originalEvent.x;
this.#lastMousePosition.y = e.originalEvent.y;
this.#showCursor(e);
if (this.#state === MOVE_UNIT){
if (this.#computeDestinationRotation && this.#destinationRotationCenter != null)
this.#destinationGroupRotation = -bearing(this.#destinationRotationCenter.lat, this.#destinationRotationCenter.lng, this.getMouseCoordinates().lat, this.getMouseCoordinates().lng);
this.#updateDestinationPreview(e);
}
else if ([BOMBING, CARPET_BOMBING, FIRE_AT_AREA].includes(this.#state)) {
this.#targetMarker.setLatLng(this.getMouseCoordinates());
this.#targetCursor.setLatLng(this.getMouseCoordinates());
}
else if (this.#state === DRAW_POLYGON) {
if (this.getSelectedCoalitionArea()?.getEditing())
else if (this.#state === DRAW_COALITIONAREA_POLYGON) {
if (this.getSelectedCoalitionArea()?.getEditing() && !e.originalEvent.ctrlKey){
this.#drawingCursor.setLatLng(e.latlng);
this.getSelectedCoalitionArea()?.moveTemporaryLatLng(e.latlng);
}
}
}
#onKeyDown(e: any) {
this.#updateDestinationPreview(e);
this.#showCursor(e);
}
#onKeyUp(e: any) {
this.#updateDestinationPreview(e);
this.#showCursor(e);
}
#onZoom(e: any) {
if (this.#centerUnit != null)
this.#panToUnit(this.#centerUnit);
@@ -523,8 +539,8 @@ export class Map extends L.Map {
#updateDestinationPreview(e: any) {
Object.values(getUnitsManager().selectedUnitsComputeGroupDestination(this.#computeDestinationRotation && this.#destinationRotationCenter != null ? this.#destinationRotationCenter : this.getMouseCoordinates(), this.#destinationGroupRotation)).forEach((latlng: L.LatLng, idx: number) => {
if (idx < this.#destinationPreviewMarkers.length)
this.#destinationPreviewMarkers[idx].setLatLng(e.originalEvent.shiftKey ? latlng : this.getMouseCoordinates());
if (idx < this.#destinationPreviewCursors.length)
this.#destinationPreviewCursors[idx].setLatLng(e.originalEvent.shiftKey ? latlng : this.getMouseCoordinates());
})
}
@@ -541,12 +557,12 @@ export class Map extends L.Map {
return button;
}
#createDestinationMarkers() {
this.#resetDestinationMarkers();
#showDestinationCursors() {
this.#hideDestinationCursors();
if (getUnitsManager().getSelectedUnits({ excludeHumans: true }).length > 0) {
/* Create the unit destination preview markers */
this.#destinationPreviewMarkers = getUnitsManager().getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true }).map((unit: Unit) => {
this.#destinationPreviewCursors = getUnitsManager().getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true }).map((unit: Unit) => {
var marker = new DestinationPreviewMarker(this.getMouseCoordinates(), {interactive: false});
marker.addTo(this);
return marker;
@@ -554,38 +570,79 @@ export class Map extends L.Map {
}
}
#resetDestinationMarkers() {
#hideDestinationCursors() {
/* Remove all the destination preview markers */
this.#destinationPreviewMarkers.forEach((marker: L.Marker) => {
this.#destinationPreviewCursors.forEach((marker: L.Marker) => {
this.removeLayer(marker);
})
this.#destinationPreviewMarkers = [];
this.#destinationPreviewCursors = [];
this.#destinationGroupRotation = 0;
this.#computeDestinationRotation = false;
this.#destinationRotationCenter = null;
}
#createTargetMarker(){
this.#resetTargetMarker();
this.#targetMarker.addTo(this);
#showTargetCursor(){
this.#hideTargetCursor();
this.#targetCursor.addTo(this);
}
#resetTargetMarker() {
this.#targetMarker.setLatLng(new L.LatLng(0, 0));
this.removeLayer(this.#targetMarker);
#hideTargetCursor() {
this.#targetCursor.setLatLng(new L.LatLng(0, 0));
this.removeLayer(this.#targetCursor);
}
#deselectCoalitionAreas() {
this.getSelectedCoalitionArea()?.setSelected(false);
}
#showCursor() {
#showDefaultCursor() {
document.getElementById(this.#ID)?.classList.remove("hidden-cursor");
}
#hideCursor() {
#hideDefaultCursor() {
document.getElementById(this.#ID)?.classList.add("hidden-cursor");
}
#showDrawingCursor() {
this.#hideDefaultCursor();
if (!this.hasLayer(this.#drawingCursor))
this.#drawingCursor.addTo(this);
}
#hideDrawingCursor() {
if (this.hasLayer(this.#drawingCursor))
this.#drawingCursor.removeFrom(this);
}
#showCursor(e?: any) {
if (e?.originalEvent.ctrlKey) {
this.#hideDefaultCursor();
this.#hideDestinationCursors();
this.#hideTargetCursor();
this.#hideDrawingCursor();
this.#showDefaultCursor();
} else {
if (this.#state !== IDLE) this.#hideDefaultCursor();
if (this.#state !== MOVE_UNIT) this.#hideDestinationCursors();
if (![BOMBING, CARPET_BOMBING, FIRE_AT_AREA].includes(this.#state)) this.#hideTargetCursor();
if (this.#state !== DRAW_COALITIONAREA_POLYGON) this.#hideDrawingCursor();
if (this.#state === IDLE) this.#showDefaultCursor();
else if (this.#state === MOVE_UNIT) this.#showDestinationCursors();
else if ([BOMBING, CARPET_BOMBING, FIRE_AT_AREA].includes(this.#state)) this.#showTargetCursor();
else if (this.#state === DRAW_COALITIONAREA_POLYGON) {
if (this.getSelectedCoalitionArea()?.getEditing())
{
this.#hideDefaultCursor();
this.#showDrawingCursor();
}
else {
this.#hideDrawingCursor();
this.#showDefaultCursor();
}
}
}
}
}

View File

@@ -1,8 +1,11 @@
import { DivIcon } from "leaflet";
import { DivIcon, LatLngExpression, MarkerOptions } from "leaflet";
import { CustomMarker } from "./custommarker";
export class TargetMarker extends CustomMarker {
#interactive: boolean = false;
constructor(latlng: LatLngExpression, options?: MarkerOptions) {
super(latlng, options);
this.setZIndexOffset(9999);
}
createIcon() {
this.setIcon(new DivIcon({
@@ -12,7 +15,6 @@ export class TargetMarker extends CustomMarker {
}));
var el = document.createElement("div");
el.classList.add("ol-target-icon");
el.classList.toggle("ol-target-icon-interactive", this.#interactive)
this.getElement()?.appendChild(el);
}
}