Merge pull request #882 from Pax1601/856-camera-control-produces-jerky-movement-on-browsers-different-from-chrome

Fixed camera control on Firefox, added slider to control zoom level, …
This commit is contained in:
Pax1601 2024-03-17 15:58:24 +01:00 committed by GitHub
commit 61fb80d67f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 220 additions and 125 deletions

View File

@ -28,6 +28,7 @@
--accent-light-red: #F5B6B6;
--background-grey: #3d4651;
--background-dark-grey: #35393d;
--background-slate-blue: #363c43;
--background-offwhite: #f2f2f3;
--background-steel: #202831;

View File

@ -256,6 +256,7 @@ export const SHOW_UNIT_CONTACTS = "Show selected units contact lines";
export const SHOW_UNIT_PATHS = "Show selected unit paths";
export const SHOW_UNIT_TARGETS = "Show selected unit targets";
export const DCS_LINK_PORT = "DCS Camera link port";
export const DCS_LINK_RATIO = "DCS Camera zoom";
export enum DataIndexes {
startOfData = 0,

View File

@ -104,7 +104,7 @@ export class Slider extends Control {
/* Update the position of the slider */
var percentValue = parseFloat(this.#slider.value) / parseFloat(this.#slider.max) * 90 + 5;
this.#slider.style.background = `linear-gradient(to right, var(--accent-light-blue) 5%, var(--accent-light-blue) ${percentValue}%, var(--background-grey) ${percentValue}%, var(--background-grey) 100%)`
this.#slider.style.background = `linear-gradient(to right, var(--accent-light-blue) 5%, var(--accent-light-blue) ${percentValue}%, var(--background-dark-grey) ${percentValue}%, var(--background-dark-grey) 100%)`
}
this.setActive(true);
}

View File

@ -7,12 +7,12 @@ import { AirbaseContextMenu } from "../contextmenus/airbasecontextmenu";
import { Dropdown } from "../controls/dropdown";
import { Airbase } from "../mission/airbase";
import { Unit } from "../unit/unit";
import { bearing, createCheckboxOption, createTextInputOption, deg2rad, getGroundElevation, polyContains } from "../other/utils";
import { bearing, createCheckboxOption, createSliderInputOption, createTextInputOption, deg2rad, getGroundElevation, polyContains } from "../other/utils";
import { DestinationPreviewMarker } from "./markers/destinationpreviewmarker";
import { TemporaryUnitMarker } from "./markers/temporaryunitmarker";
import { ClickableMiniMap } from "./clickableminimap";
import { SVGInjector } from '@tanem/svg-injector'
import { defaultMapLayers, mapBounds, minimapBoundaries, IDLE, COALITIONAREA_DRAW_POLYGON, MOVE_UNIT, SHOW_UNIT_CONTACTS, HIDE_GROUP_MEMBERS, SHOW_UNIT_PATHS, SHOW_UNIT_TARGETS, SHOW_UNIT_LABELS, SHOW_UNITS_ENGAGEMENT_RINGS, SHOW_UNITS_ACQUISITION_RINGS, HIDE_UNITS_SHORT_RANGE_RINGS, FILL_SELECTED_RING, MAP_MARKER_CONTROLS, DCS_LINK_PORT, DCSMapsZoomLevelsByTheatre } from "../constants/constants";
import { defaultMapLayers, mapBounds, minimapBoundaries, IDLE, COALITIONAREA_DRAW_POLYGON, MOVE_UNIT, SHOW_UNIT_CONTACTS, HIDE_GROUP_MEMBERS, SHOW_UNIT_PATHS, SHOW_UNIT_TARGETS, SHOW_UNIT_LABELS, SHOW_UNITS_ENGAGEMENT_RINGS, SHOW_UNITS_ACQUISITION_RINGS, HIDE_UNITS_SHORT_RANGE_RINGS, FILL_SELECTED_RING, MAP_MARKER_CONTROLS, DCS_LINK_PORT, DCSMapsZoomLevelsByTheatre, DCS_LINK_RATIO } from "../constants/constants";
import { CoalitionArea } from "./coalitionarea/coalitionarea";
import { CoalitionAreaContextMenu } from "../contextmenus/coalitionareacontextmenu";
import { DrawingCursor } from "./coalitionarea/drawingcursor";
@ -103,6 +103,9 @@ export class Map extends L.Map {
#visibilityOptions: { [key: string]: boolean | string | number } = {}
#hiddenTypes: string[] = [];
#layerName: string = "";
#cameraOptionsXmlHttp: XMLHttpRequest | null = null;
#bradcastPositionXmlHttp: XMLHttpRequest | null = null;
#cameraZoomRatio: number = 1.0;
/**
*
@ -211,6 +214,14 @@ export class Map extends L.Map {
document.addEventListener("mapOptionsChanged", () => {
this.getContainer().toggleAttribute("data-hide-labels", !this.getVisibilityOptions()[SHOW_UNIT_LABELS]);
this.#cameraControlPort = this.getVisibilityOptions()[DCS_LINK_PORT] as number;
this.#cameraZoomRatio = 50 / (20 + (this.getVisibilityOptions()[DCS_LINK_RATIO] as number));
if (this.#slaveDCSCamera) {
this.#broadcastPosition();
window.setTimeout(() => {
this.#broadcastPosition();
}, 500); // DCS does not always apply the altitude correctly at the first set when changing map type
}
});
document.addEventListener("configLoaded", () => {
@ -254,6 +265,7 @@ export class Map extends L.Map {
/* Create the checkboxes to select the advanced visibility options */
this.addVisibilityOption(DCS_LINK_PORT, 3003, { min: 1024, max: 65535 });
this.addVisibilityOption(DCS_LINK_RATIO, 50, { min: 0, max: 100, slider: true });
this.#mapVisibilityOptionsDropdown.addHorizontalDivider();
@ -270,12 +282,16 @@ export class Map extends L.Map {
addVisibilityOption(option: string, defaultValue: boolean | number | string, options?: { [key: string]: any }) {
this.#visibilityOptions[option] = defaultValue;
if (typeof defaultValue === 'boolean')
if (typeof defaultValue === 'boolean') {
this.#mapVisibilityOptionsDropdown.addOptionElement(createCheckboxOption(option, option, defaultValue as boolean, (ev: any) => { this.#setVisibilityOption(option, ev); }, options));
else if (typeof defaultValue === 'number')
this.#mapVisibilityOptionsDropdown.addOptionElement(createTextInputOption(option, option, defaultValue.toString(), 'number', (ev: any) => { this.#setVisibilityOption(option, ev); }, options));
else
} else if (typeof defaultValue === 'number') {
if (options !== undefined && options?.slider === true)
this.#mapVisibilityOptionsDropdown.addOptionElement(createSliderInputOption(option, option, defaultValue, (ev: any) => { this.#setVisibilityOption(option, ev); }, options));
else
this.#mapVisibilityOptionsDropdown.addOptionElement(createTextInputOption(option, option, defaultValue.toString(), 'number', (ev: any) => { this.#setVisibilityOption(option, ev); }, options));
} else {
this.#mapVisibilityOptionsDropdown.addOptionElement(createTextInputOption(option, option, defaultValue, 'text', (ev: any) => { this.#setVisibilityOption(option, ev); }, options));
}
}
setLayer(layerName: string) {
@ -593,19 +609,43 @@ export class Map extends L.Map {
}
setSlaveDCSCamera(newSlaveDCSCamera: boolean) {
// if (this.#slaveDCSCameraAvailable || !newSlaveDCSCamera) { // Commented to experiment with usability
this.#slaveDCSCamera = newSlaveDCSCamera;
let button = document.getElementById("camera-link-control");
button?.classList.toggle("off", !newSlaveDCSCamera);
if (newSlaveDCSCamera)
this.#slaveDCSCamera = newSlaveDCSCamera;
let button = document.getElementById("camera-link-control");
button?.classList.toggle("off", !newSlaveDCSCamera);
if (this.#slaveDCSCamera) {
this.#broadcastPosition();
window.setTimeout(() => {
this.#broadcastPosition();
// }
}, 500); // DCS does not always apply the altitude correctly at the first set when changing map type
}
}
setCameraControlMode(newCameraControlMode: string) {
this.#cameraControlMode = newCameraControlMode;
if (this.#slaveDCSCamera)
if (this.#slaveDCSCamera) {
this.#broadcastPosition();
window.setTimeout(() => {
this.#broadcastPosition();
}, 500); // DCS does not always apply the altitude correctly at the first set when changing map type
}
}
increaseCameraZoom() {
const slider = document.querySelector(`label[title="${DCS_LINK_RATIO}"] input`);
if (slider instanceof HTMLInputElement) {
slider.value = String(Math.min(Number(slider.max), Number(slider.value) + 10));
slider.dispatchEvent(new Event('input'));
slider.dispatchEvent(new Event('mouseup'));
}
}
decreaseCameraZoom() {
const slider = document.querySelector(`label[title="${DCS_LINK_RATIO}"] input`);
if (slider instanceof HTMLInputElement) {
slider.value = String(Math.max(Number(slider.min), Number(slider.value) - 10));
slider.dispatchEvent(new Event('input'));
slider.dispatchEvent(new Event('mouseup'));
}
}
/* Event handlers */
@ -791,21 +831,24 @@ export class Map extends L.Map {
}
#broadcastPosition() {
if (this.#bradcastPositionXmlHttp?.readyState !== 4 && this.#bradcastPositionXmlHttp !== null)
return
getGroundElevation(this.getCenter(), (response: string) => {
var groundElevation: number | null = null;
try {
groundElevation = parseFloat(response);
var xmlHttp = new XMLHttpRequest();
xmlHttp.open("PUT", `http://127.0.0.1:${this.#cameraControlPort}`);
xmlHttp.setRequestHeader("Content-Type", "application/json");
this.#bradcastPositionXmlHttp = new XMLHttpRequest();
/* Using 127.0.0.1 instead of localhost because the LuaSocket version used in DCS only listens to IPv4. This avoids the lag caused by the
browser if it first tries to send the request on the IPv6 address for localhost */
this.#bradcastPositionXmlHttp.open("POST", `http://127.0.0.1:${this.#cameraControlPort}`);
const C = 40075016.686;
let mpp = C * Math.cos(deg2rad(this.getCenter().lat)) / Math.pow(2, this.getZoom() + 8);
let d = mpp * 1920;
let alt = d / 2 * 1 / Math.tan(deg2rad(40));
if (alt > 100000)
alt = 100000;
xmlHttp.send(JSON.stringify({ lat: this.getCenter().lat, lng: this.getCenter().lng, alt: alt + groundElevation, mode: this.#cameraControlMode }));
let alt = d / 2 * 1 / Math.tan(deg2rad(40)) * this.#cameraZoomRatio;
alt = Math.min(alt, 50000);
this.#bradcastPositionXmlHttp.send(JSON.stringify({ lat: this.getCenter().lat, lng: this.getCenter().lng, alt: alt + groundElevation, mode: this.#cameraControlMode }));
} catch {
console.warn("broadcastPosition: could not retrieve ground elevation")
}
@ -1002,23 +1045,30 @@ export class Map extends L.Map {
}
}
/* Check if the camera control plugin is available. Right now this will only change the color of the button, no changes in functionality */
#checkCameraPort(){
var xmlHttp = new XMLHttpRequest();
xmlHttp.open("OPTIONS", `http://127.0.0.1:${this.#cameraControlPort}`);
xmlHttp.onload = (res: any) => {
if (xmlHttp.status == 200)
this.#setSlaveDCSCameraAvailable(true);
else
this.#setSlaveDCSCameraAvailable(false);
};
xmlHttp.onerror = (res: any) => {
if (this.#cameraOptionsXmlHttp?.readyState !== 4)
this.#cameraOptionsXmlHttp?.abort()
this.#cameraOptionsXmlHttp = new XMLHttpRequest();
/* Using 127.0.0.1 instead of localhost because the LuaSocket version used in DCS only listens to IPv4. This avoids the lag caused by the
browser if it first tries to send the request on the IPv6 address for localhost */
this.#cameraOptionsXmlHttp.open("OPTIONS", `http://127.0.0.1:${this.#cameraControlPort}`);
this.#cameraOptionsXmlHttp.onload = (res: any) => {
if (this.#cameraOptionsXmlHttp !== null && this.#cameraOptionsXmlHttp.status == 204)
this.#setSlaveDCSCameraAvailable(true);
else
this.#setSlaveDCSCameraAvailable(false);
}
xmlHttp.ontimeout = (res: any) => {
this.#setSlaveDCSCameraAvailable(false);
}
xmlHttp.timeout = 500;
xmlHttp.send("");
};
this.#cameraOptionsXmlHttp.onerror = (res: any) => {
this.#setSlaveDCSCameraAvailable(false);
}
this.#cameraOptionsXmlHttp.ontimeout = (res: any) => {
this.#setSlaveDCSCameraAvailable(false);
}
this.#cameraOptionsXmlHttp.timeout = 500;
this.#cameraOptionsXmlHttp.send("");
}
}

View File

@ -368,6 +368,24 @@ export class OlympusApp {
"context": "olympus",
"ctrlKey": false,
"shiftKey": false
}).addKeyboardShortcut("increaseCameraZoom", {
"altKey": true,
"callback": () => {
this.getMap().increaseCameraZoom();
},
"code": "Equal",
"context": "olympus",
"ctrlKey": false,
"shiftKey": false
}).addKeyboardShortcut("decreaseCameraZoom", {
"altKey": true,
"callback": () => {
this.getMap().decreaseCameraZoom();
},
"code": "Minus",
"context": "olympus",
"ctrlKey": false,
"shiftKey": false
});
["KeyW", "KeyA", "KeyS", "KeyD", "ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown"].forEach(code => {

View File

@ -9,9 +9,10 @@ import { GROUND_UNIT_AIR_DEFENCE_REGEX, ROEs, emissionsCountermeasures, reaction
import { Dropdown } from "../controls/dropdown";
import { navyUnitDatabase } from "../unit/databases/navyunitdatabase";
import { DateAndTime, UnitBlueprint } from "../interfaces";
import { Slider } from "../controls/slider";
// comment
const usng = require( "usng.js" );
const usng = require("usng.js");
export function bearing(lat1: number, lon1: number, lat2: number, lon2: number) {
const φ1 = deg2rad(lat1); // φ, λ in radians
@ -45,8 +46,8 @@ export function bearingAndDistanceToLatLng(lat: number, lon: number, brng: numbe
const R = 6371e3; // metres
const φ1 = deg2rad(lat); // φ, λ in radians
const λ1 = deg2rad(lon);
const φ2 = Math.asin( Math.sin(φ1)*Math.cos(dist/R) + Math.cos(φ1)*Math.sin(dist/R)*Math.cos(brng) );
const λ2 = λ1 + Math.atan2(Math.sin(brng)*Math.sin(dist/R)*Math.cos(φ1), Math.cos(dist/R)-Math.sin(φ1)*Math.sin(φ2));
const φ2 = Math.asin(Math.sin(φ1) * Math.cos(dist / R) + Math.cos(φ1) * Math.sin(dist / R) * Math.cos(brng));
const λ2 = λ1 + Math.atan2(Math.sin(brng) * Math.sin(dist / R) * Math.cos(φ1), Math.cos(dist / R) - Math.sin(φ1) * Math.sin(φ2));
return new LatLng(rad2deg(φ2), rad2deg(λ2));
}
@ -64,14 +65,14 @@ export function ConvertDDToDMS(D: number, lng: boolean) {
return dir + zeroPad(deg, 2) + "°" + zeroPad(min, 2) + "'" + zeroPad(sec, 2) + "." + zeroPad(dec, 2) + "\"";
}
export function dataPointMap( container:HTMLElement, data:any) {
Object.keys( data ).forEach( ( key ) => {
const val = "" + data[ key ]; // Ensure a string
container.querySelectorAll( `[data-point="${key}"]`).forEach( el => {
export function dataPointMap(container: HTMLElement, data: any) {
Object.keys(data).forEach((key) => {
const val = "" + data[key]; // Ensure a string
container.querySelectorAll(`[data-point="${key}"]`).forEach(el => {
// We could probably have options here
if ( el instanceof HTMLInputElement ) {
if (el instanceof HTMLInputElement) {
el.value = val;
} else if ( el instanceof HTMLElement ) {
} else if (el instanceof HTMLElement) {
el.innerText = val;
}
});
@ -89,19 +90,19 @@ export function rad2deg(rad: number) {
}
export function generateUUIDv4() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
export function keyEventWasInInput( event:KeyboardEvent ) {
export function keyEventWasInInput(event: KeyboardEvent) {
const target = event.target;
return ( target instanceof HTMLElement && ( [ "INPUT", "TEXTAREA" ].includes( target.nodeName ) ) );
return (target instanceof HTMLElement && (["INPUT", "TEXTAREA"].includes(target.nodeName)));
}
export function reciprocalHeading(heading: number): number {
return heading > 180? heading - 180: heading + 180;
return heading > 180 ? heading - 180 : heading + 180;
}
/**
@ -113,7 +114,7 @@ export function reciprocalHeading(heading: number): number {
*
* */
export const zeroAppend = function (num: number, places: number, decimal: boolean = false) {
var string = decimal? num.toFixed(2): String(num);
var string = decimal ? num.toFixed(2) : String(num);
while (string.length < places) {
string = "0" + string;
}
@ -181,22 +182,22 @@ export type MGRS = {
zoneNumber: string
}
export function latLngToMGRS( lat:number, lng:number, precision:number = 4 ): MGRS | false {
export function latLngToMGRS(lat: number, lng: number, precision: number = 4): MGRS | false {
if ( precision < 0 || precision > 6 ) {
console.error( "latLngToMGRS: precision must be a number >= 0 and <= 6. Given precision: " + precision );
if (precision < 0 || precision > 6) {
console.error("latLngToMGRS: precision must be a number >= 0 and <= 6. Given precision: " + precision);
return false;
}
const mgrs = new usng.Converter().LLtoMGRS( lat, lng, precision );
const match = mgrs.match( new RegExp( `^(\\d{2})([A-Z])([A-Z])([A-Z])(\\d+)$` ) );
const easting = match[5].substr(0,match[5].length/2);
const northing = match[5].substr(match[5].length/2);
let output:MGRS = {
const mgrs = new usng.Converter().LLtoMGRS(lat, lng, precision);
const match = mgrs.match(new RegExp(`^(\\d{2})([A-Z])([A-Z])([A-Z])(\\d+)$`));
const easting = match[5].substr(0, match[5].length / 2);
const northing = match[5].substr(match[5].length / 2);
let output: MGRS = {
bandLetter: match[2],
columnLetter: match[3],
groups: [ match[1] + match[2], match[3] + match[4], easting, northing ],
groups: [match[1] + match[2], match[3] + match[4], easting, northing],
easting: easting,
northing: northing,
precision: precision,
@ -204,32 +205,32 @@ export function latLngToMGRS( lat:number, lng:number, precision:number = 4 ): MG
string: match[0],
zoneNumber: match[1]
}
return output;
}
export function latLngToUTM( lat:number, lng:number ) {
return new usng.Converter().LLtoUTM( lat, lng );
export function latLngToUTM(lat: number, lng: number) {
return new usng.Converter().LLtoUTM(lat, lng);
}
export function latLngToMercator(lat: number, lng: number): {x: number, y: number} {
export function latLngToMercator(lat: number, lng: number): { x: number, y: number } {
var rMajor = 6378137; //Equatorial Radius, WGS84
var shift = Math.PI * rMajor;
var x = lng * shift / 180;
var y = Math.log(Math.tan((90 + lat) * Math.PI / 360)) / (Math.PI / 180);
var shift = Math.PI * rMajor;
var x = lng * shift / 180;
var y = Math.log(Math.tan((90 + lat) * Math.PI / 360)) / (Math.PI / 180);
y = y * shift / 180;
return {x: x, y: y};
return { x: x, y: y };
}
export function mercatorToLatLng(x: number, y: number) {
var rMajor = 6378137; //Equatorial Radius, WGS84
var shift = Math.PI * rMajor;
var lng = x / shift * 180.0;
var lat = y / shift * 180.0;
var shift = Math.PI * rMajor;
var lng = x / shift * 180.0;
var lat = y / shift * 180.0;
lat = 180 / Math.PI * (2 * Math.atan(Math.exp(lat * Math.PI / 180.0)) - Math.PI / 2.0);
return { lng: lng, lat: lat };
return { lng: lng, lat: lat };
}
export function createDivWithClass(className: string) {
@ -267,21 +268,21 @@ export function nmToFt(nm: number) {
}
export function polyContains(latlng: LatLng, polygon: Polygon) {
var poly = polygon.toGeoJSON();
var poly = polygon.toGeoJSON();
return turf.inside(turf.point([latlng.lng, latlng.lat]), poly);
}
export function randomPointInPoly(polygon: Polygon): LatLng {
var bounds = polygon.getBounds();
var x_min = bounds.getEast();
var x_max = bounds.getWest();
var y_min = bounds.getSouth();
var y_max = bounds.getNorth();
var bounds = polygon.getBounds();
var x_min = bounds.getEast();
var x_max = bounds.getWest();
var y_min = bounds.getSouth();
var y_max = bounds.getNorth();
var lat = y_min + (Math.random() * (y_max - y_min));
var lng = x_min + (Math.random() * (x_max - x_min));
var poly = polygon.toGeoJSON();
var poly = polygon.toGeoJSON();
var inside = turf.inside(turf.point([lng, lat]), poly);
if (inside) {
@ -296,7 +297,7 @@ export function polygonArea(polygon: Polygon) {
return turf.area(poly);
}
export function randomUnitBlueprint(unitDatabase: UnitDatabase, options: {type?: string, role?: string, ranges?: string[], eras?: string[], coalition?: string} ) {
export function randomUnitBlueprint(unitDatabase: UnitDatabase, options: { type?: string, role?: string, ranges?: string[], eras?: string[], coalition?: string }) {
/* Start from all the unit blueprints in the database */
var unitBlueprints = Object.values(unitDatabase.getBlueprints());
@ -315,13 +316,13 @@ export function randomUnitBlueprint(unitDatabase: UnitDatabase, options: {type?:
/* Keep only the units that have a range included in the requested values */
if (options.ranges) {
unitBlueprints = unitBlueprints.filter((unitBlueprint: UnitBlueprint) => {
unitBlueprints = unitBlueprints.filter((unitBlueprint: UnitBlueprint) => {
var rangeType = "";
var range = unitBlueprint.acquisitionRange ;
var range = unitBlueprint.acquisitionRange;
if (range !== undefined) {
if (range >= 0 && range < 10000)
if (range >= 0 && range < 10000)
rangeType = "Short range";
else if (range >= 10000 && range < 100000)
else if (range >= 10000 && range < 100000)
rangeType = "Medium range";
else if (range >= 100000 && range < 999999)
rangeType = "Long range";
@ -332,15 +333,15 @@ export function randomUnitBlueprint(unitDatabase: UnitDatabase, options: {type?:
/* Keep only the units that have an era included in the requested values */
if (options.eras) {
unitBlueprints = unitBlueprints.filter((unitBlueprint: UnitBlueprint) => {
return unitBlueprint.era? options.eras?.includes(unitBlueprint.era): true;
unitBlueprints = unitBlueprints.filter((unitBlueprint: UnitBlueprint) => {
return unitBlueprint.era ? options.eras?.includes(unitBlueprint.era) : true;
});
}
/* Keep only the units that have the correct coalition, if selected */
if (options.coalition) {
unitBlueprints = unitBlueprints.filter((unitBlueprint: UnitBlueprint) => {
return (unitBlueprint.coalition && unitBlueprint.coalition !== "")? options.coalition === unitBlueprint.coalition: true;
unitBlueprints = unitBlueprints.filter((unitBlueprint: UnitBlueprint) => {
return (unitBlueprint.coalition && unitBlueprint.coalition !== "") ? options.coalition === unitBlueprint.coalition : true;
});
}
@ -353,7 +354,7 @@ export function getMarkerCategoryByName(name: string) {
return "aircraft";
else if (helicopterDatabase.getByName(name) != null)
return "helicopter";
else if (groundUnitDatabase.getByName(name) != null){
else if (groundUnitDatabase.getByName(name) != null) {
var type = groundUnitDatabase.getByName(name)?.type ?? "";
if (/\bAAA|SAM\b/.test(type) || /\bmanpad|stinger\b/i.test(type))
return "groundunit-sam";
@ -362,7 +363,7 @@ export function getMarkerCategoryByName(name: string) {
}
else if (navyUnitDatabase.getByName(name) != null)
return "navyunit";
else
else
return "aircraft"; // TODO add other unit types
}
@ -379,7 +380,7 @@ export function getUnitDatabaseByCategory(category: string) {
return null;
}
export function getCategoryBlueprintIconSVG(category:string, unitName:string) {
export function getCategoryBlueprintIconSVG(category: string, unitName: string) {
const path = "/resources/theme/images/buttons/visibility/";
@ -398,35 +399,35 @@ export function base64ToBytes(base64: string) {
}
export function enumToState(state: number) {
if (state < states.length)
if (state < states.length)
return states[state];
else
else
return states[0];
}
export function enumToROE(ROE: number) {
if (ROE < ROEs.length)
if (ROE < ROEs.length)
return ROEs[ROE];
else
else
return ROEs[0];
}
export function enumToReactionToThreat(reactionToThreat: number) {
if (reactionToThreat < reactionsToThreat.length)
if (reactionToThreat < reactionsToThreat.length)
return reactionsToThreat[reactionToThreat];
else
else
return reactionsToThreat[0];
}
export function enumToEmissioNCountermeasure(emissionCountermeasure: number) {
if (emissionCountermeasure < emissionsCountermeasures.length)
if (emissionCountermeasure < emissionsCountermeasures.length)
return emissionsCountermeasures[emissionCountermeasure];
else
else
return emissionsCountermeasures[0];
}
export function enumToCoalition(coalitionID: number) {
switch (coalitionID){
switch (coalitionID) {
case 0: return "neutral";
case 1: return "red";
case 2: return "blue";
@ -435,7 +436,7 @@ export function enumToCoalition(coalitionID: number) {
}
export function coalitionToEnum(coalition: string) {
switch (coalition){
switch (coalition) {
case "neutral": return 0;
case "red": return 1;
case "blue": return 2;
@ -463,7 +464,7 @@ export function convertDateAndTimeToDate(dateAndTime: DateAndTime) {
return new Date(year, month, date.Day, time.h, time.m, time.s);
}
export function createCheckboxOption(text: string, description: string, checked: boolean = true, callback: CallableFunction = (ev: any) => {}, options?:any) {
export function createCheckboxOption(text: string, description: string, checked: boolean = true, callback: CallableFunction = (ev: any) => { }, options?: any) {
options = {
"disabled": false,
"name": "",
@ -477,11 +478,11 @@ export function createCheckboxOption(text: string, description: string, checked:
label.title = description;
var input = document.createElement("input");
input.type = "checkbox";
input.checked = checked;
input.name = options.name;
input.disabled = options.disabled;
input.readOnly = options.readOnly;
input.value = options.value;
input.checked = checked;
input.name = options.name;
input.disabled = options.disabled;
input.readOnly = options.readOnly;
input.value = options.value;
var span = document.createElement("span");
span.innerText = text;
label.appendChild(input);
@ -504,7 +505,7 @@ export function getCheckboxOptions(dropdown: Dropdown) {
return values;
}
export function createTextInputOption(text: string, description: string, initialValue: string, type: string, callback: CallableFunction = (ev: any) => {}, options?:any) {
export function createTextInputOption(text: string, description: string, initialValue: string, type: string, callback: CallableFunction = (ev: any) => { }, options?: any) {
options = {
"disabled": false,
"name": "",
@ -516,15 +517,15 @@ export function createTextInputOption(text: string, description: string, initial
var label = document.createElement("label");
label.title = description;
var input = document.createElement("input");
input.type = type;
input.name = options.name;
input.disabled = options.disabled;
input.readOnly = options.readOnly;
input.type = type;
input.name = options.name;
input.disabled = options.disabled;
input.readOnly = options.readOnly;
if (options.min)
input.min = options.min;
if (options.max)
input.max = options.max;
input.value = initialValue;
input.value = initialValue;
input.style.width = "80px";
var span = document.createElement("span");
span.innerText = text;
@ -543,6 +544,28 @@ export function createTextInputOption(text: string, description: string, initial
return div as HTMLElement;
}
export function createSliderInputOption(text: string, description: string, initialValue: number, callback: CallableFunction = (ev: any) => { }, options?: any) {
var div = document.createElement("div");
var label = document.createElement("label");
label.title = description;
var input = new Slider(null, options.min ?? 0, options.max ?? 100, "", (val: number) => {
callback({currentTarget: {value: val}});
});
input.setValue(initialValue);
input.getContainer()?.querySelector(".ol-data-grid")?.classList.add("hide");
var span = document.createElement("span");
span.innerText = text;
span.style.width = "100%";
span.style.margin = "auto";
label.appendChild(span);
label.appendChild(input.getContainer() as HTMLElement);
label.style.display = "flex";
label.style.alignContent = "center";
label.style.width = "100%";
div.appendChild(label);
return div as HTMLElement;
}
export function getGroundElevation(latlng: LatLng, callback: CallableFunction) {
/* Get the ground elevation from the server endpoint */
const xhr = new XMLHttpRequest();

View File

@ -27,18 +27,18 @@ export class ShortcutManager extends Manager {
}
add( name: string, shortcut:any ) {
console.error( "ShortcutManager:add() cannot be used. Use addKeyboardShortcut or addMouseShortcut." );
add(name: string, shortcut: any) {
console.error("ShortcutManager:add() cannot be used. Use addKeyboardShortcut or addMouseShortcut.");
return this;
}
addKeyboardShortcut( name:string, shortcutKeyboardOptions:ShortcutKeyboardOptions ) {
super.add( name, new ShortcutKeyboard( shortcutKeyboardOptions ) );
addKeyboardShortcut(name: string, shortcutKeyboardOptions: ShortcutKeyboardOptions) {
super.add(name, new ShortcutKeyboard(shortcutKeyboardOptions));
return this;
}
addMouseShortcut( name:string, shortcutMouseOptions:ShortcutMouseOptions ) {
super.add( name, new ShortcutMouse( shortcutMouseOptions ) );
addMouseShortcut(name: string, shortcutMouseOptions: ShortcutMouseOptions) {
super.add(name, new ShortcutMouse(shortcutMouseOptions));
return this;
}

View File

@ -4,7 +4,7 @@ local _prevLuaExportStop = LuaExportStop
local server = nil
local port = 3003
local headers = "Access-Control-Allow-Origin: *\r\nAccess-Control-Allow-Methods: PUT, OPTIONS\r\nAccess-Control-Allow-Headers: *\r\nAccess-Control-Max-Age: 86400\r\nVary: Accept-Encoding, Origin\r\nKeep-Alive: timeout=2, max=100\r\nConnection: Keep-Alive\r\n\r\n"
local headers = "Access-Control-Allow-Private-Network: true\r\nAccess-Control-Allow-Origin: *\r\nAccess-Control-Allow-Methods: *\r\nAccess-Control-Allow-Headers: *\r\nAccess-Control-Max-Age: 86400\r\nVary: Accept-Encoding, Origin\r\n\r\n"
function startTCPServer()
log.write('OLYMPUSCAMERACONTROL.EXPORT.LUA', log.INFO, 'Starting TCP Server')
@ -30,11 +30,13 @@ function receiveTCP()
if client then
-- Set the timeout of the connection to 5ms
client:settimeout(0)
client:settimeout(0.005)
client:setoption("tcp-nodelay", true)
local acc = ""
local data = ""
log.write('OLYMPUSCAMERACONTROL.EXPORT.LUA', log.INFO, 'CONNECTION')
-- Start receiving data, accumulate it in acc
while data ~= nil do
@ -45,11 +47,11 @@ function receiveTCP()
if data == "" then
-- Is this an OPTIONS request?
if string.find(acc, "OPTIONS") ~= nil then
client:send("HTTP/1.1 200 OK\r\n" .. headers)
client:send("HTTP/1.1 204 OK\r\n" .. headers)
client:close()
-- Is this a PUT request?
elseif string.find(acc, "PUT") ~= nil then
elseif string.find(acc, "POST") ~= nil then
-- Extract the length of the body
local contentLength = string.match(acc, "Content%-Length: (%d+)")
if contentLength ~= nil then
@ -62,7 +64,7 @@ function receiveTCP()
local mode = string.match(body, '"mode":%s*"(%a+)"%s*[},]')
if lat ~= nil and lng ~= nil then
client:send("HTTP/1.1 200 OK\r\n" .. headers)
client:send("HTTP/1.1 200 OK\r\n" .. "Content-Type: application/json\r\n" .. headers)
local position = {}
position["lat"] = tonumber(lat)