Merge pull request #514 from Pax1601/448-on-chrome-fps-drops-to-single-digits-when-zoomed-in

Implemented custom fast renderer for range circles
This commit is contained in:
Pax1601 2023-11-12 15:50:44 +01:00 committed by GitHub
commit dc61d364d1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 68 additions and 10 deletions

View File

@ -102,8 +102,6 @@ export class Map extends L.Map {
constructor(ID: string){
/* Init the leaflet map */
super(ID, {
zoomSnap: 0,
zoomDelta: 0.25,
preferCanvas: true,
doubleClickZoom: false,
zoomControl: false,

View File

@ -0,0 +1,56 @@
// @ts-nocheck
// This is a horrible hack. But it is needed at the moment to ovveride a default behaviour of Leaflet. TODO please fix me the proper way.
import { Circle, Point, Polyline } from 'leaflet';
/**
* This custom Circle object implements a faster render method for very big circles. When zoomed in, the default ctx.arc method
* is very slow since the circle is huge. Also, when zoomed in most of the circle points will be outside the screen and not needed. This
* simpler, faster renderer approximates the circle with line segements and only draws those currently visibile.
* A more refined version using arcs could be implemented but this works good enough.
*/
export class RangeCircle extends Circle {
_updatePath() {
if (!this._renderer._drawing || this._empty()) { return; }
var p = this._point,
ctx = this._renderer._ctx,
r = Math.max(Math.round(this._radius), 1),
s = (Math.max(Math.round(this._radiusY), 1) || r) / r;
if (s !== 1) {
ctx.save();
ctx.scale(1, s);
}
let pathBegun = false;
let dtheta = Math.PI * 2 / 120;
for (let theta = 0; theta <= Math.PI * 2; theta += dtheta) {
let p1 = new Point(p.x + r * Math.cos(theta), p.y / s + r * Math.sin(theta));
let p2 = new Point(p.x + r * Math.cos(theta + dtheta), p.y / s + r * Math.sin(theta + dtheta));
let l1 = this._map.layerPointToLatLng(p1);
let l2 = this._map.layerPointToLatLng(p2);
let line = new Polyline([l1, l2]);
if (this._map.getBounds().intersects(line.getBounds())) {
if (!pathBegun) {
ctx.beginPath();
ctx.moveTo(p1.x, p1.y);
pathBegun = true;
}
ctx.lineTo(p2.x, p2.y);
}
else {
if (pathBegun) {
this._renderer._fillStroke(ctx, this);
pathBegun = false;
}
}
}
if (pathBegun)
this._renderer._fillStroke(ctx, this);
if (s !== 1)
ctx.restore();
}
}

View File

@ -11,6 +11,7 @@ import { groundUnitDatabase } from './databases/groundunitdatabase';
import { navyUnitDatabase } from './databases/navyunitdatabase';
import { Weapon } from '../weapon/weapon';
import { Ammo, Contact, GeneralSettings, LoadoutBlueprint, ObjectIconOptions, Offset, Radio, TACAN, UnitData } from '../interfaces';
import { RangeCircle } from "../map/rangecircle";
var pathIcon = new Icon({
iconUrl: '/resources/theme/images/markers/marker-icon.png',
@ -97,8 +98,8 @@ export class Unit extends CustomMarker {
#pathMarkers: Marker[] = [];
#pathPolyline: Polyline;
#contactsPolylines: Polyline[] = [];
#engagementCircle: Circle;
#acquisitionCircle: Circle;
#engagementCircle: RangeCircle;
#acquisitionCircle: RangeCircle;
#miniMapMarker: CircleMarker | null = null;
#targetPositionMarker: TargetMarker;
#targetPositionPolyline: Polyline;
@ -167,8 +168,8 @@ export class Unit extends CustomMarker {
this.#pathPolyline.addTo(getApp().getMap());
this.#targetPositionMarker = new TargetMarker(new LatLng(0, 0));
this.#targetPositionPolyline = new Polyline([], { color: '#FF0000', weight: 3, opacity: 0.5, smoothFactor: 1 });
this.#engagementCircle = new Circle(this.getPosition(), { radius: 0, weight: 4, opacity: 1, fillOpacity: 0, dashArray: "4 8", interactive: false, bubblingMouseEvents: false });
this.#acquisitionCircle = new Circle(this.getPosition(), { radius: 0, weight: 2, opacity: 1, fillOpacity: 0, dashArray: "8 12", interactive: false, bubblingMouseEvents: false });
this.#engagementCircle = new RangeCircle(this.getPosition(), { radius: 0, weight: 4, opacity: 1, fillOpacity: 0, dashArray: "4 8", interactive: false, bubblingMouseEvents: false });
this.#acquisitionCircle = new RangeCircle(this.getPosition(), { radius: 0, weight: 2, opacity: 1, fillOpacity: 0, dashArray: "8 12", interactive: false, bubblingMouseEvents: false });
this.on('click', (e) => this.#onClick(e));
this.on('dblclick', (e) => this.#onDoubleClick(e));
@ -225,7 +226,7 @@ export class Unit extends CustomMarker {
case DataIndexes.alive: this.setAlive(dataExtractor.extractBool()); updateMarker = true; break;
case DataIndexes.human: this.#human = dataExtractor.extractBool(); break;
case DataIndexes.controlled: this.#controlled = dataExtractor.extractBool(); updateMarker = true; break;
case DataIndexes.coalition: this.#coalition = enumToCoalition(dataExtractor.extractUInt8()); updateMarker = true; this.#clearRanges(); break;
case DataIndexes.coalition: let newCoalition = enumToCoalition(dataExtractor.extractUInt8()); updateMarker = true; if (newCoalition != this.#coalition) this.#clearRanges(); this.#coalition = newCoalition; break;
case DataIndexes.country: this.#country = dataExtractor.extractUInt8(); break;
case DataIndexes.name: this.#name = dataExtractor.extractString(); break;
case DataIndexes.unitName: this.#unitName = dataExtractor.extractString(); break;
@ -1342,8 +1343,9 @@ export class Unit extends CustomMarker {
}
})
if (acquisitionRange !== this.#acquisitionCircle.getRadius())
if (acquisitionRange !== this.#acquisitionCircle.getRadius()) {
this.#acquisitionCircle.setRadius(acquisitionRange);
}
if (engagementRange !== this.#engagementCircle.getRadius())
this.#engagementCircle.setRadius(engagementRange);
@ -1368,7 +1370,8 @@ export class Unit extends CustomMarker {
break;
}
}
this.#acquisitionCircle.setLatLng(this.getPosition());
if (this.getPosition() != this.#acquisitionCircle.getLatLng())
this.#acquisitionCircle.setLatLng(this.getPosition());
}
else {
if (getApp().getMap().hasLayer(this.#acquisitionCircle))
@ -1392,7 +1395,8 @@ export class Unit extends CustomMarker {
break;
}
}
this.#engagementCircle.setLatLng(this.getPosition());
if (this.getPosition() != this.#engagementCircle.getLatLng())
this.#engagementCircle.setLatLng(this.getPosition());
}
else {
if (getApp().getMap().hasLayer(this.#engagementCircle))