Incorporated unit info panel into unit control panel

Converted callbacks to events
Mouse info drawing moved to mouseinfopanel.ts
This commit is contained in:
dpassoni
2023-03-06 17:45:50 +01:00
parent fd20e5be79
commit a7a41e60c4
14 changed files with 250 additions and 201 deletions

View File

@@ -1,6 +1,8 @@
interface CustomEventMap {
"unitSelection": CustomEvent<Unit>,
"unitDeselection": CustomEvent<Unit>,
"unitsSelection": CustomEvent<Unit[]>,
"unitsDeselection": CustomEvent<Unit[]>,
"clearSelection": CustomEvent<>,
"unitCreation": CustomEvent<Unit>,
"unitDeletion": CustomEvent<Unit>,
@@ -10,8 +12,7 @@ interface CustomEventMap {
"unitLandCommand": CustomEvent<Unit>,
"unitSetAltitudeCommand": CustomEvent<Unit>,
"unitSetSpeedCommand": CustomEvent<Unit>,
"unitSetROECommand": CustomEvent<Unit>,
"unitSetReactionToThreatCommand": CustomEvent<Unit>,
"unitSetOption": CustomEvent<Unit>,
"groupCreation": CustomEvent<Unit[]>,
"groupDeletion": CustomEvent<Unit[]>,
"mapStateChanged": CustomEvent<string>,

View File

@@ -1,7 +1,6 @@
import * as L from "leaflet"
import { getContextMenu, getUnitsManager, getActiveCoalition, getMouseInfoPanel } from "..";
import { getContextMenu, getUnitsManager, getActiveCoalition } from "..";
import { spawnAircraft, spawnGroundUnit, spawnSmoke } from "../server/server";
import { bearing, distance, zeroAppend } from "../other/utils";
import { aircraftDatabase } from "../units/aircraftdatabase";
import { unitTypes } from "../units/unittypes";
import { BoxSelect } from "./boxselect";
@@ -28,11 +27,6 @@ export class Map extends L.Map {
#layer: L.TileLayer | null = null;
#preventLeftClick: boolean = false;
#leftClickTimer: number = 0;
#measurePoint: L.LatLng | null;
#measureIcon: L.Icon;
#measureMarker: L.Marker;
#measureLine: L.Polyline = new L.Polyline([], { color: '#2d3e50', weight: 3, opacity: 0.5, smoothFactor: 1, interactive: false });
#measureLineDiv: HTMLElement;
#lastMousePosition: L.Point = new L.Point(0, 0);
constructor(ID: string) {
@@ -45,15 +39,7 @@ export class Map extends L.Map {
/* Init the state machine */
this.#state = IDLE;
this.#measurePoint = null;
this.#measureIcon = new L.Icon({ iconUrl: 'images/pin.png', iconAnchor: [16, 32]});
this.#measureMarker = new L.Marker([0, 0], {icon: this.#measureIcon, interactive: false});
this.#measureLineDiv = document.createElement("div");
this.#measureLineDiv.classList.add("ol-measure-box");
this.#measureLineDiv.style.display = 'none';
document.body.appendChild(this.#measureLineDiv);
/* Register event handles */
this.on("click", (e: any) => this.#onClick(e));
@@ -63,7 +49,6 @@ 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('zoom', (e: any) => this.#onZoom(e));
}
setLayer(layerName: string) {
@@ -162,19 +147,7 @@ export class Map extends L.Map {
if (!this.#preventLeftClick) {
this.hideContextMenu();
if (this.#state === IDLE) {
if (e.originalEvent.ctrlKey)
if (!this.#measurePoint)
{
this.#measurePoint = e.latlng;
this.#measureMarker.setLatLng(e.latlng);
this.#measureMarker.addTo(this);
}
else
{
this.#measurePoint = null;
if (this.hasLayer(this.#measureMarker))
this.removeLayer(this.#measureMarker);
}
}
else if (this.#state === MOVE_UNIT) {
this.setState(IDLE);
@@ -223,17 +196,13 @@ export class Map extends L.Map {
#onMouseDown(e: any)
{
if ((e.originalEvent.which == 1) && (e.originalEvent.button == 0))
{
this.dragging.disable();
}
}
#onMouseUp(e: any)
{
if ((e.originalEvent.which == 1) && (e.originalEvent.button == 0))
{
this.dragging.enable();
}
}
#onMouseMove(e: any)
@@ -241,26 +210,10 @@ export class Map extends L.Map {
var selectedUnitPosition = null;
var selectedUnits = getUnitsManager().getSelectedUnits();
if (selectedUnits && selectedUnits.length == 1)
{
selectedUnitPosition = new L.LatLng(selectedUnits[0].getFlightData().latitude, selectedUnits[0].getFlightData().longitude);
}
getMouseInfoPanel()?.update(<L.LatLng>e.latlng, this.#measurePoint, selectedUnitPosition);
this.#lastMousePosition.x = e.originalEvent.x;
this.#lastMousePosition.y = e.originalEvent.y;
if ( this.#measurePoint)
this.#drawMeasureLine();
else
this.#hideMeasureLine();
}
#onZoom(e: any)
{
if (this.#measurePoint)
this.#drawMeasureLine();
else
this.#hideMeasureLine();
}
/* Spawning menus */
@@ -357,43 +310,4 @@ export class Map extends L.Map {
spawnGroundUnit(unitType, e.latlng, getActiveCoalition());
}}}), true);
}
#drawMeasureLine()
{
var mouseLatLng = this.containerPointToLatLng(this.#lastMousePosition);
if (this.#measurePoint != null)
{
var points = [this.#measurePoint, mouseLatLng];
this.#measureLine.setLatLngs(points);
var dist = distance(this.#measurePoint.lat, this.#measurePoint.lng, mouseLatLng.lat, mouseLatLng.lng);
var bear = bearing(this.#measurePoint.lat, this.#measurePoint.lng, mouseLatLng.lat, mouseLatLng.lng);
var startXY = this.latLngToContainerPoint(this.#measurePoint);
var dx = (this.#lastMousePosition.x - startXY.x);
var dy = (this.#lastMousePosition.y - startXY.y);
var angle = Math.atan2(dy, dx);
if (angle > Math.PI / 2)
angle = angle - Math.PI;
if (angle < -Math.PI / 2)
angle = angle + Math.PI;
this.#measureLineDiv.innerHTML = `${zeroAppend(Math.floor(bear), 3)}° / ${zeroAppend(Math.floor(dist*0.000539957), 3)} NM`
this.#measureLineDiv.style.left = (this.#lastMousePosition.x + startXY.x) / 2 - this.#measureLineDiv.offsetWidth / 2 + "px";
this.#measureLineDiv.style.top = (this.#lastMousePosition.y + startXY.y) / 2 - this.#measureLineDiv.offsetHeight / 2 + "px";
this.#measureLineDiv.style.rotate = angle + "rad";
this.#measureLineDiv.style.display = "";
}
if (!this.hasLayer(this.#measureLine))
this.#measureLine.addTo(this);
}
#hideMeasureLine()
{
this.#measureLineDiv.style.display = "none";
if (this.hasLayer(this.#measureLine))
this.removeLayer(this.#measureLine)
}
}

View File

@@ -1,14 +1,35 @@
import { LatLng } from "leaflet";
import { getMissionData } from "..";
import { Icon, LatLng, Marker, Polyline } from "leaflet";
import { getMap, getMissionData, getUnitsManager } from "..";
import { distance, bearing, zeroPad, zeroAppend } from "../other/utils";
import { Unit } from "../units/unit";
import { Panel } from "./panel";
export class MouseInfoPanel extends Panel {
#measureMarker: Marker;
#measurePoint: LatLng | null = null;
#measureIcon: Icon;
#measureLine: Polyline = new Polyline([], { color: '#2d3e50', weight: 3, opacity: 0.5, smoothFactor: 1, interactive: false });
#measureBox: HTMLElement;
constructor(ID: string) {
super(ID);
this.#measureIcon = new Icon({ iconUrl: 'images/pin.png', iconAnchor: [16, 32]});
this.#measureMarker = new Marker([0, 0], {icon: this.#measureIcon, interactive: false});
this.#measureBox = document.createElement("div");
this.#measureBox.classList.add("ol-measure-box");
document.body.appendChild(this.#measureBox);
getMap()?.on("click", (e: any) => this.#onMapClick(e));
getMap()?.on('zoom', (e: any) => this.#onZoom(e));
getMap()?.on('mousemove', (e: any) => this.#onMouseMove(e));
document.addEventListener('unitsSelection', (e: CustomEvent<Unit[]>) => this.#onUnitsSelection(e.detail));
document.addEventListener('clearSelection', () => this.#onClearSelection());
}
update(mousePosition: LatLng, measurePosition: LatLng | null, unitPosition: LatLng | null) {
#update(mousePosition: LatLng, measurePosition: LatLng | null, unitPosition: LatLng | null) {
var bullseyes = getMissionData().getBullseyes();
for (let idx in bullseyes)
{
@@ -16,7 +37,7 @@ export class MouseInfoPanel extends Panel {
var bear = bearing(bullseyes[idx].lat, bullseyes[idx].lng, mousePosition.lat, mousePosition.lng);
var el = <HTMLElement>this.getElement().querySelector(`#bullseye-${idx}`);
if (el != null)
el.innerHTML = `${zeroAppend(Math.floor(bear), 3)}° / ${zeroAppend(Math.floor(dist*0.000539957), 3)} NM`
el.innerText = `${zeroAppend(Math.floor(bear), 3)}° / ${zeroAppend(Math.floor(dist*0.000539957), 3)} NM`
}
if (measurePosition) {
@@ -24,33 +45,100 @@ export class MouseInfoPanel extends Panel {
var bear = bearing(measurePosition.lat, measurePosition.lng, mousePosition.lat, mousePosition.lng);
var el = <HTMLElement>this.getElement().querySelector(`#measure-position`);
if (el != null)
{
el.innerHTML = `${zeroAppend(Math.floor(bear), 3)}° / ${zeroAppend(Math.floor(dist*0.000539957), 3)} NM`
if (el.parentElement != null)
el.parentElement.style.display = 'flex'; //TODO: don't like that it's hardcoded
}
}
else {
var el = <HTMLElement>this.getElement().querySelector(`#measure-position`);
if (el != null && el.parentElement != null)
el.parentElement.style.display = 'none';
el.innerText = `${zeroAppend(Math.floor(bear), 3)}° / ${zeroAppend(Math.floor(dist*0.000539957), 3)} NM`
}
if (unitPosition) {
var dist = distance(unitPosition.lat, unitPosition.lng, mousePosition.lat, mousePosition.lng);
var bear = bearing(unitPosition.lat, unitPosition.lng, mousePosition.lat, mousePosition.lng);
var el = <HTMLElement>this.getElement().querySelector(`#unit-position`);
if (el != null)
{
el.innerHTML = `${zeroAppend(Math.floor(bear), 3)}° / ${zeroAppend(Math.floor(dist*0.000539957), 3)} NM`
if (el.parentElement != null)
el.parentElement.style.display = 'flex'; //TODO: don't like that its hardcoded
}
}
else {
var el = <HTMLElement>this.getElement().querySelector(`#unit-position`);
if (el != null && el.parentElement != null)
el.parentElement.style.display = 'none';
el.innerText = `${zeroAppend(Math.floor(bear), 3)}° / ${zeroAppend(Math.floor(dist*0.000539957), 3)} NM`
}
}
#onMapClick(e: any)
{
if (e.originalEvent.ctrlKey)
{
if (!this.#measurePoint)
{
this.#measureBox.classList.toggle("hide", false);
this.getElement().querySelector(`#measure-position`)?.classList.toggle("hide", false);
this.#measurePoint = e.latlng;
this.#measureMarker.setLatLng(e.latlng);
this.#measureMarker.addTo(getMap());
if (!getMap().hasLayer(this.#measureLine))
this.#measureLine.addTo(getMap());
}
else
{
this.#measureBox.classList.toggle("hide", true);
this.getElement().querySelector(`#measure-position`)?.classList.toggle("hide", true);
this.#measurePoint = null;
if (getMap().hasLayer(this.#measureMarker))
getMap().removeLayer(this.#measureMarker);
this.#measureLine.setLatLngs([]);
if (getMap().hasLayer(this.#measureLine))
getMap().removeLayer(this.#measureLine);
}
}
}
#drawMeasureLine()
{
var mouseLatLng = getMap().containerPointToLatLng(getMap().getMousePosition());
if (this.#measurePoint != null)
{
var points = [this.#measurePoint, mouseLatLng];
this.#measureLine.setLatLngs(points);
var dist = distance(this.#measurePoint.lat, this.#measurePoint.lng, mouseLatLng.lat, mouseLatLng.lng);
var bear = bearing(this.#measurePoint.lat, this.#measurePoint.lng, mouseLatLng.lat, mouseLatLng.lng);
var startXY = getMap().latLngToContainerPoint(this.#measurePoint);
var dx = (getMap().getMousePosition().x - startXY.x);
var dy = (getMap().getMousePosition().y - startXY.y);
var angle = Math.atan2(dy, dx);
if (angle > Math.PI / 2)
angle = angle - Math.PI;
if (angle < -Math.PI / 2)
angle = angle + Math.PI;
this.#measureBox.innerText = `${zeroAppend(Math.floor(bear), 3)}° / ${zeroAppend(Math.floor(dist*0.000539957), 3)} NM`
this.#measureBox.style.left = (getMap().getMousePosition().x + startXY.x) / 2 - this.#measureBox.offsetWidth / 2 + "px";
this.#measureBox.style.top = (getMap().getMousePosition().y + startXY.y) / 2 - this.#measureBox.offsetHeight / 2 + "px";
this.#measureBox.style.rotate = angle + "rad";
}
}
#onMouseMove(e: any)
{
var selectedUnitPosition = null;
var selectedUnits = getUnitsManager().getSelectedUnits();
if (selectedUnits && selectedUnits.length == 1)
selectedUnitPosition = new LatLng(selectedUnits[0].getFlightData().latitude, selectedUnits[0].getFlightData().longitude);
this.#update(<LatLng>e.latlng, this.#measurePoint, selectedUnitPosition);
this.#drawMeasureLine();
}
#onZoom(e: any)
{
this.#drawMeasureLine();
}
#onUnitsSelection(units: Unit[])
{
if (units.length == 1)
this.getElement().querySelector(`#unit-position`)?.classList.toggle("hide", false);
}
#onClearSelection()
{
this.#measureBox.classList.toggle("hide", true);
this.getElement().querySelector(`#unit-position`)?.classList.toggle("hide", true);
}
}

View File

@@ -1,5 +1,6 @@
export class Panel {
#element: HTMLElement
#visible: boolean = true;
constructor(ID: string) {
this.#element = <HTMLElement>document.getElementById(ID);
@@ -7,13 +8,19 @@ export class Panel {
show() {
this.#element.classList.toggle("hide", false);
this.#visible = true;
}
hide() {
this.#element.classList.toggle("hide", true);
this.#visible = false;
}
getElement() {
return this.#element;
}
getVisible(){
return this.#visible;
}
}

View File

@@ -30,11 +30,15 @@ export class UnitInfoPanel extends Panel {
this.#task = <HTMLElement>(this.getElement().querySelector("#task"));
this.#loadoutContainer = <HTMLElement>(this.getElement().querySelector("#loadout-container"));
document.addEventListener("unitsSelection", (e: CustomEvent<Unit[]>) => this.#onUnitsSelection(e.detail));
document.addEventListener("unitsDeselection", (e: CustomEvent<Unit[]>) => this.#onUnitsDeselection(e.detail));
document.addEventListener("unitUpdated", (e: CustomEvent<Unit>) => this.#onUnitUpdate(e.detail));
this.hide();
}
update(unit: Unit) {
if (this.getElement() != null) {
#onUnitUpdate(unit: Unit) {
if (this.getElement() != null && this.getVisible()) {
/* Set the unit info */
this.#unitName.innerText = unit.getData().unitName;
this.#groupName.innerText = unit.getData().groupName;
@@ -72,4 +76,18 @@ export class UnitInfoPanel extends Panel {
el.innerText = amount + "x" + displayName;
this.#loadoutContainer.appendChild(el);
}
#onUnitsSelection(units: Unit[]){
if (units.length == 1)
this.show();
else
this.hide();
}
#onUnitsDeselection(units: Unit[]){
if (units.length == 1)
this.show();
else
this.hide();
}
}

View File

@@ -49,7 +49,7 @@ export function getUnits(callback: CallableFunction, refresh: boolean = false) {
if (!DEMO)
GET(callback, `${UNITS_URI}/${refresh? REFRESH_URI: UPDATE_URI}}`);
else
callback(refresh? generateRandomUnitsDemoData(1000): {units:{}});
callback(refresh? generateRandomUnitsDemoData(100): {units:{}});
}
export function addDestination(ID: number, path: any) {
@@ -149,8 +149,8 @@ function generateRandomUnitsDemoData(unitsNumber: number)
for (let i = 0; i < unitsNumber; i++)
{
units[String(i)] = structuredClone(DEMO_UNIT_DATA);
units[String(i)].flightData.latitude += (Math.random() - 0.5) * 0.1;
units[String(i)].flightData.longitude += (Math.random() - 0.5) * 0.1;
units[String(i)].flightData.latitude += (Math.random() - 0.5) * 0.3;
units[String(i)].flightData.longitude += (Math.random() - 0.5) * 0.3;
}
return {"units": units};
}
@@ -163,8 +163,8 @@ const DEMO_UNIT_DATA = {
alive: true,
category: "Aircraft",
flightData: {
latitude: 37.3,
longitude: -116,
latitude: 37.2,
longitude: -115.8,
altitude: 2000,
heading: 0.5,
speed: 300

View File

@@ -87,6 +87,7 @@ export class Unit extends Marker {
}
setData(data: UnitData) {
document.dispatchEvent(new CustomEvent("unitUpdated", { detail: this }));
var updateMarker = true;
//if (this.getFlightData().latitude != response.flightData.latitude ||
// this.getFlightData().longitude != response.flightData.longitude ||
@@ -141,7 +142,10 @@ export class Unit extends Marker {
if ((this.getData().alive || !selected) && this.#selectable && this.#selected != selected) {
this.#selected = selected;
this.getElement()?.querySelector(".unit")?.setAttribute("data-is-selected", String(this.getSelected()));
document.dispatchEvent(new CustomEvent("unitSelection", { detail: this }));
if (selected)
document.dispatchEvent(new CustomEvent("unitSelection", { detail: this }));
else
document.dispatchEvent(new CustomEvent("unitDeselection", { detail: this }));
}
}

View File

@@ -1,6 +1,6 @@
import { LatLng, LatLngBounds } from "leaflet";
import { getMap, getUnitControlPanel, getUnitInfoPanel } from "..";
import { Unit, GroundUnit } from "./unit";
import { getMap } from "..";
import { Unit } from "./unit";
import { cloneUnit } from "../server/server";
import { IDLE, MOVE_UNIT } from "../map/map";
@@ -15,7 +15,8 @@ export class UnitsManager {
document.addEventListener('copy', () => this.copyUnits());
document.addEventListener('paste', () => this.pasteUnits());
document.addEventListener('unitSelection', (e: CustomEvent) => this.onUnitSelection(e.detail));
document.addEventListener('unitSelection', (e: CustomEvent) => this.#onUnitSelection(e.detail));
document.addEventListener('unitDeselection', (e: CustomEvent) => this.#onUnitDeselection(e.detail));
document.addEventListener('keydown', (event) => this.#onKeyDown(event));
}
@@ -56,15 +57,6 @@ export class UnitsManager {
Object.keys(data.units)
.filter((ID: string) => ID in this.#units)
.forEach((ID: string) => this.#units[parseInt(ID)].setData(data.units[ID]));
/* Update the unit info panel */
if (this.getSelectedUnits().length == 1) {
getUnitInfoPanel()?.show();
getUnitInfoPanel()?.update(this.getSelectedUnits()[0]);
}
else {
getUnitInfoPanel()?.hide();
}
}
forceUpdate() {
@@ -80,25 +72,6 @@ export class UnitsManager {
this.#units[ID]?.setSelected(true);
}
onUnitSelection(unit: Unit) {
if (this.getSelectedUnits().length > 0) {
getMap().setState(MOVE_UNIT);
/* Disable the firing of the selection event for a certain amount of time. This avoids firing many events if many units are selected */
if (!this.#selectionEventDisabled)
{
setTimeout(() => {
document.dispatchEvent(new CustomEvent("unitsSelection", {detail: this.getSelectedUnits()}));
this.#selectionEventDisabled = false;
}, 300);
this.#selectionEventDisabled = true;
}
}
else {
getMap().setState(IDLE);
document.dispatchEvent(new CustomEvent("clearSelection"));
}
}
selectFromBounds(bounds: LatLngBounds)
{
this.deselectAllUnits();
@@ -359,4 +332,32 @@ export class UnitsManager {
this.selectedUnitsDelete();
}
}
#onUnitSelection(unit: Unit) {
if (this.getSelectedUnits().length > 0) {
getMap().setState(MOVE_UNIT);
/* Disable the firing of the selection event for a certain amount of time. This avoids firing many events if many units are selected */
if (!this.#selectionEventDisabled)
{
setTimeout(() => {
document.dispatchEvent(new CustomEvent("unitsSelection", {detail: this.getSelectedUnits()}));
this.#selectionEventDisabled = false;
}, 300);
this.#selectionEventDisabled = true;
}
}
else {
getMap().setState(IDLE);
document.dispatchEvent(new CustomEvent("clearSelection"));
}
}
#onUnitDeselection(unit: Unit) {
if (this.getSelectedUnits().length == 0) {
getMap().setState(IDLE);
document.dispatchEvent(new CustomEvent("clearSelection"));
}
else
document.dispatchEvent(new CustomEvent("unitsDeselection", {detail: this.getSelectedUnits()}));
}
}