Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
bbb44a0
fix: implement true grid pattern for Grid and Cross brushes
fank Feb 3, 2026
64280be
debug: add logging to diagnose grid pattern rendering issue
fank Feb 3, 2026
9c01686
debug: add delayed fill check and force pattern URL
fank Feb 3, 2026
e93f53b
debug: more comprehensive delayed check logging
fank Feb 3, 2026
c70505c
debug: add event listener for layer add and debug function
fank Feb 3, 2026
591f623
fix: set up add listener before addTo and apply pattern with delay
fank Feb 3, 2026
5b88eeb
debug: add check for L.SVG override and manual pattern apply function
fank Feb 3, 2026
9b1cb2b
fix: add direct L.SVG._updateStyle patch for pattern fills
fank Feb 3, 2026
ec23de4
debug: add detailed logging to _updateStyle patch
fank Feb 3, 2026
3036bc1
debug: add forceApplyPatterns function to directly modify SVG paths
fank Feb 3, 2026
7587d6a
debug: check SVG container for paths and patterns, try relative URL
fank Feb 3, 2026
6247ff3
debug: investigate DOM structure of paths and defs
fank Feb 3, 2026
f4fee1f
fix: use SVG renderer for shapes with pattern fills
fank Feb 3, 2026
cd24a6f
fix: add pattern to SVG renderer's defs element
fank Feb 3, 2026
f4db75b
chore: remove debug logging now that grid pattern is working
fank Feb 3, 2026
0564540
style: invert grid pattern - white lines on colored background
fank Feb 3, 2026
401f877
style: change grid lines from white to grey
fank Feb 3, 2026
1e7e9d0
style: match in-game grid - light background, dark grid lines (same c…
fank Feb 3, 2026
aa0d6f6
style: reduce grid line intensity from 1.0 to 0.5
fank Feb 3, 2026
88f7674
refactor: simplify marker code and remove redundancies
fank Feb 3, 2026
253ded7
chore: remove accidentally committed coverage files
fank Feb 3, 2026
2968e0e
chore: add coverage files to gitignore
fank Feb 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,8 @@ static/images/maps

# Plans
docs/plans/

# Test coverage
cover.out
coverage.out
coverage.html
4 changes: 4 additions & 0 deletions static/scripts/ocap.js
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,10 @@ function initMap (world) {
preferCanvas: true
});

// Create SVG renderer for shapes that need pattern fills (Canvas doesn't support SVG patterns)
window.svgRenderer = L.svg();
window.svgRenderer.addTo(map);


// Hide marker popups once below a certain zoom level
map.on("zoom", function () {
Expand Down
172 changes: 127 additions & 45 deletions static/scripts/ocap.marker.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,81 @@
// Ensure pattern fills are applied after any style update
// This patches Leaflet's SVG renderer to always check for fillPattern
(function() {
if (!L.SVG) return;

const originalUpdateStyle = L.SVG.prototype._updateStyle;
L.SVG.prototype._updateStyle = function(layer) {
originalUpdateStyle.call(this, layer);

// Apply fill pattern if present (Canvas doesn't support this, only SVG)
if (layer.options && layer.options.fillPattern && layer._path) {
const patternUrl = L.Pattern._getPatternUrl(L.stamp(layer.options.fillPattern));
layer._path.setAttribute('fill', patternUrl);
}
};
})();

// Custom GridPattern for true grid/cross patterns (horizontal + vertical lines)
L.GridPattern = L.Pattern.extend({
options: {
weight: 2,
spaceWeight: 4,
color: '#ffffff', // Grid line color (default white)
backgroundColor: null, // Background color (if set, fills behind grid)
opacity: 1.0,
backgroundOpacity: 0.5
},

_addShapes: function () {
// Calculate size first
var w = this.options.weight;
var s = this.options.spaceWeight;
var size = w + s;

// Update pattern dimensions before creating shapes
this.options.width = size;
this.options.height = size;

// Add background rectangle if backgroundColor is set
if (this.options.backgroundColor) {
this._bg = new L.PatternRect({
x: 0,
y: 0,
width: size,
height: size,
fill: true,
fillColor: this.options.backgroundColor,
fillOpacity: this.options.backgroundOpacity,
stroke: false
});
this.addShape(this._bg);
}

// Horizontal line
this._hLine = new L.PatternPath({
stroke: true,
weight: this.options.weight,
color: this.options.color,
opacity: this.options.opacity,
d: 'M0 ' + (w / 2) + ' H ' + size
});

// Vertical line
this._vLine = new L.PatternPath({
stroke: true,
weight: this.options.weight,
color: this.options.color,
opacity: this.options.opacity,
d: 'M' + (w / 2) + ' 0 V ' + size
});

this.addShape(this._hLine);
this.addShape(this._vLine);
},

setStyle: L.Pattern.prototype.setStyle
});

class Marker {
constructor(type, text, player, color, startFrame, endFrame, side, positions, size, shape, brush) {
this._type = type;
Expand Down Expand Up @@ -118,18 +196,21 @@ class Marker {
break;
case "grid":
case "Grid":
case "GRID":
this._brushPatternOptions = {
color: this._color,
opacity: 0.8,
angle: 90,
weight: 1,
spaceWeight: 1
color: this._color, // Grid lines (marker color)
backgroundColor: this._color, // Light background (marker color, low opacity)
backgroundOpacity: 0.3,
opacity: 0.5, // Reduced intensity for grid lines
weight: 2,
spaceWeight: 6
};
this._useGridPattern = true; // Use L.GridPattern for true grid
this._shapeOptions = {
color: this._color,
stroke: false,
fill: true,
fillOpacity: 0.2
fillOpacity: 1.0 // Full opacity since pattern handles transparency
};
break;
case "fdiagonal":
Expand Down Expand Up @@ -183,18 +264,21 @@ class Marker {
break;
case "cross":
case "Cross":
case "CROSS":
this._brushPatternOptions = {
color: this._color,
opacity: 0.8,
angle: 90,
weight: 1,
spaceWeight: 1
color: this._color, // Cross lines (marker color)
backgroundColor: this._color, // Light background (marker color, low opacity)
backgroundOpacity: 0.3,
opacity: 0.5, // Reduced intensity for cross lines
weight: 2,
spaceWeight: 6
};
this._useGridPattern = true; // Use L.GridPattern for cross pattern
this._shapeOptions = {
color: this._color,
stroke: false,
fill: true,
fillOpacity: 0.2
fillOpacity: 1.0
};
break;
case "border":
Expand All @@ -219,9 +303,13 @@ class Marker {
break;
}

// Create stripe pattern if brush options were set
// Create pattern if brush options were set
if (this._brushPatternOptions) {
this._brushPattern = new L.StripePattern(this._brushPatternOptions);
if (this._useGridPattern) {
this._brushPattern = new L.GridPattern(this._brushPatternOptions);
} else {
this._brushPattern = new L.StripePattern(this._brushPatternOptions);
}
}
} else {
this._shapeOptions = {
Expand Down Expand Up @@ -273,14 +361,13 @@ class Marker {
let alpha = frameData[3];

if (this._shape === "RECTANGLE" && Array.isArray(pos[0])) {
console.debug("wrong RECTANGLE positions, converting to POLYLINE");
this._shape = "POLYLINE";
}

let latLng;
let points;
if (this._marker == null) {
// console.debug(`UPDATE AT FRAME: attempting to create marker ${this._name}`)
// console.log(`UPDATE AT FRAME: attempting to create marker ${this._name}`)

if (this._shape === "ICON") {
latLng = armaToLatLng(pos);
Expand Down Expand Up @@ -340,7 +427,7 @@ class Marker {
this._createMarker(points, dir, alpha);
}
} else {
// console.debug(`UPDATE AT FRAME: attempting to update marker ${this._name}`)
// console.log(`UPDATE AT FRAME: attempting to update marker ${this._name}`)

if (this._shape === "ICON") {
latLng = armaToLatLng(pos);
Expand Down Expand Up @@ -475,15 +562,7 @@ class Marker {

show (alpha) {
this._isShow = true;
if (this._shape == "ICON") {
this.setMarkerOpacity(alpha);
} else if (this._shape == "ELLIPSE") {
this.setMarkerOpacity(alpha);
} else if (this._shape == "RECTANGLE") {
this.setMarkerOpacity(alpha);
} else if (this._shape == "POLYLINE") {
this.setMarkerOpacity(alpha);
}
this.setMarkerOpacity(alpha);
}

_createMarker (latLng, dir, alpha) {
Expand Down Expand Up @@ -566,27 +645,30 @@ class Marker {
marker.setRotationAngle(dir);
}

if (this._shape === "ELLIPSE") {
// latLng now contains polygon points (calculated in _updateAtFrame)
if (this._brushPattern) {
this._brushPattern.addTo(map);
marker = L.polygon(latLng, { noClip: false, interactive: false, fillPattern: this._brushPattern });
L.Util.setOptions(marker, this._shapeOptions);
} else {
marker = L.polygon(latLng, { noClip: false, interactive: false });
L.Util.setOptions(marker, this._shapeOptions);
}
marker.addTo(systemMarkersLayerGroup);
} else if (this._shape === "RECTANGLE") {
if (this._brushPattern) {
this._brushPattern.addTo(map);
marker = L.polygon(latLng, { noClip: false, interactive: false, fillPattern: this._brushPattern });
L.Util.setOptions(marker, this._shapeOptions);
} else {
marker = L.polygon(latLng, { noClip: false, interactive: false });
L.Util.setOptions(marker, this._shapeOptions);
if (this._shape === "ELLIPSE" || this._shape === "RECTANGLE") {
// latLng contains polygon points (calculated in _updateAtFrame)
let polygonOptions = Object.assign({}, this._shapeOptions, { noClip: false, interactive: false });

if (this._brushPattern && window.svgRenderer) {
// Use SVG renderer for pattern fills (Canvas doesn't support SVG patterns)
polygonOptions.renderer = window.svgRenderer;

// Add pattern to SVG renderer's defs
if (window.svgRenderer._container) {
if (!map._svgDefRoot) {
map._svgDefRoot = L.SVG.create('defs');
window.svgRenderer._container.appendChild(map._svgDefRoot);
}
this._brushPattern._map = map;
this._brushPattern._initDom();
map._svgDefRoot.appendChild(this._brushPattern._dom);
this._brushPattern._addShapes();
this._brushPattern.redraw();
}
polygonOptions.fillPattern = this._brushPattern;
}

marker = L.polygon(latLng, polygonOptions);
marker.addTo(systemMarkersLayerGroup);
} else if (this._shape === "POLYLINE") {
marker = L.polyline(latLng, { color: this._color, opacity: 1, noClip: true, lineCap: 'butt', lineJoin: 'round', interactive: false })
Expand Down
Loading