Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
74 changes: 0 additions & 74 deletions .github/workflows/codeql.yml

This file was deleted.

168 changes: 168 additions & 0 deletions debug/variable-line-offset-demo.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
<!DOCTYPE html>
<html>
<head>
<title>Variable Line Offset Demo - Multi-Line Rendering</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<link rel="stylesheet" href="../dist/mapbox-gl.css" />
<style>
body {
margin: 0;
padding: 0;
font-family: Arial, sans-serif;
}
html, body, #map {
height: 100%;
}
.info-panel {
position: absolute;
top: 20px;
left: 20px;
background: white;
padding: 15px;
border-radius: 5px;
box-shadow: 0 2px 10px rgba(0,0,0,0.3);
max-width: 400px;
z-index: 1;
}
.info-panel h3 {
margin: 0 0 10px 0;
font-size: 16px;
}
.info-panel p {
margin: 5px 0;
font-size: 13px;
line-height: 1.4;
}
.info-panel code {
background: #f4f4f4;
padding: 2px 5px;
border-radius: 3px;
font-size: 12px;
}
.info-panel .legend {
margin-top: 10px;
padding-top: 10px;
border-top: 1px solid #eee;
}
.legend-item {
display: flex;
align-items: center;
margin: 5px 0;
}
</style>
</head>

<body>
<div class="info-panel">
<h3>Variable Line Offset Demo</h3>
<p><strong>Feature:</strong> <code>line-offset</code> with <code>line-progress</code></p>
<p>The red line offset varies from -10px to +10px along its length.</p>

<div class="legend">
<h4 style="margin: 10px 0 5px 0; font-size: 14px;">Layer Controls</h4>
<div class="legend-item">
<input type="checkbox" id="toggle-background" checked>
<label for="toggle-background">Background Line (20px)</label>
</div>
<div class="legend-item">
<input type="checkbox" id="toggle-variable" checked>
<label for="toggle-variable">Variable Offset Line</label>
</div>
</div>
</div>

<div id="map"></div>

<script src="../dist/mapbox-gl-dev.js"></script>

<script>
// Set access token (fallback token for localhost testing)
mapboxgl.accessToken = 'pk.eyJ1IjoiZ2wtanMtdGVhbSIsImEiOiJjbTV1d3l0d3AwMThnMmpzZ2M5OTNyeDE1In0.2nygBIo7PXbkFCCt6LEBgw';

var map = window.map = new mapboxgl.Map({
container: 'map',
devtools: true,
zoom: 13,
center: [-122.45, 37.78],
sprite: 'mapbox://sprites/mapbox/streets-v11',
glyphs: "mapbox://fonts/mapbox/{fontstack}/{range}.pbf",
hash: true
});

map.on('load', () => {
map.showTileBoundaries = false;

// Clear any cached tiles first
if (map.style && map.style.sourceCaches) {
Object.values(map.style.sourceCaches).forEach(sourceCache => {
if (sourceCache.clearTiles) sourceCache.clearTiles();
});
}

// Simple straight line in San Francisco
const data = {
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {},
"geometry": {
"coordinates": [
[-122.48, 37.78],
[-122.42, 37.78]
],
"type": "LineString"
}
}
]
};

// Add source with lineMetrics enabled for variable offset
map.addSource('line', {
type: 'geojson',
lineMetrics: true, // REQUIRED for line-progress
data: data
});

// Background line - thick gray line (20px)
map.addLayer({
id: 'background-line',
type: 'line',
source: 'line',
paint: {
'line-color': '#999',
'line-width': 20
}
});

// Variable offset line - red line with offset from -10 to +10
map.addLayer({
id: 'variable-line',
type: 'line',
source: 'line',
paint: {
'line-color': '#ff0000',
'line-width': 4,
'line-offset': [
'interpolate',
['linear'],
['line-progress'],
0, -10, // Start: -10px offset
1, 10 // End: +10px offset
]
}
});

// Layer Controls - Event Listeners
document.getElementById('toggle-background').addEventListener('change', (e) => {
map.setLayoutProperty('background-line', 'visibility', e.target.checked ? 'visible' : 'none');
});

document.getElementById('toggle-variable').addEventListener('change', (e) => {
map.setLayoutProperty('variable-line', 'visibility', e.target.checked ? 'visible' : 'none');
});
});
</script>
</body>
</html>
27 changes: 22 additions & 5 deletions src/data/bucket/line_bucket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ type GradientTexture = {
type LineProgressFeatures = {
zOffset: number;
variableWidth: number;
variableOffset: number;
};

interface Subsegment {
Expand All @@ -121,6 +122,7 @@ class LineBucket implements Bucket {
lineClips: LineClips | null | undefined;
zOffsetValue: PossiblyEvaluatedValue<number>;
variableWidthValue: PossiblyEvaluatedValue<number>;
variableOffsetValue: PossiblyEvaluatedValue<number>;
lineFeature: BucketFeature;

e1: number;
Expand Down Expand Up @@ -455,6 +457,15 @@ class LineBucket implements Bucket {
this.variableWidthValue = lineWidth;
}

// Check layers for variable offset
for (const layer of this.layers) {
const lineOffset = layer.paint.get('line-offset').value;
if (lineOffset.kind !== 'constant' && lineOffset.isLineProgressConstant === false) {
this.variableOffsetValue = lineOffset;
break;
}
}

if (this.elevationType === 'road') {
const vertexOffset = this.layoutVertexArray.length;
const added = this.addElevatedRoadFeature(feature, geometry, canonical, elevationFeatures, join, cap, miterLimit, roundLimit);
Expand Down Expand Up @@ -1019,7 +1030,7 @@ class LineBucket implements Bucket {

evaluateLineProgressFeatures(distance: number): LineProgressFeatures | null {
assert(distance >= 0);
if (!this.variableWidthValue && this.elevationType !== 'offset') {
if (!this.variableWidthValue && !this.variableOffsetValue && this.elevationType !== 'offset') {
return null;
}
this.evaluationGlobals.lineProgress = 0;
Expand All @@ -1032,14 +1043,20 @@ class LineBucket implements Bucket {
if (this.variableWidthValue && this.variableWidthValue.kind !== 'constant') {
variableWidth = this.variableWidthValue.evaluate(this.evaluationGlobals, this.lineFeature) || 0.0;
}

let variableOffset = 0.0;
if (this.variableOffsetValue && this.variableOffsetValue.kind !== 'constant') {
variableOffset = this.variableOffsetValue.evaluate(this.evaluationGlobals, this.lineFeature) || 0.0;
}

if (this.elevationType !== 'offset') {
return {zOffset: 0.0, variableWidth};
return {zOffset: 0.0, variableWidth, variableOffset};
}
if (this.zOffsetValue.kind === 'constant') {
return {zOffset: this.zOffsetValue.value, variableWidth};
return {zOffset: this.zOffsetValue.value, variableWidth, variableOffset};
}
const zOffset: number = this.zOffsetValue.evaluate(this.evaluationGlobals, this.lineFeature) || 0.0;
return {zOffset, variableWidth};
return {zOffset, variableWidth, variableOffset};
}

/**
Expand Down Expand Up @@ -1181,7 +1198,7 @@ class LineBucket implements Bucket {
this.zOffsetVertexArray.emplaceBack(
lineProgressFeatures.zOffset,
lineProgressFeatures.variableWidth,
lineProgressFeatures.variableWidth
lineProgressFeatures.variableOffset
);
}
assert(this.zOffsetVertexArray.length === this.layoutVertexArray.length || this.elevationType !== 'offset');
Expand Down
5 changes: 5 additions & 0 deletions src/render/draw_line.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,11 @@ export default function drawLine(painter: Painter, sourceCache: SourceCache, lay
definesValues.push("VARIABLE_LINE_WIDTH");
}

const offset = layer.paint.get('line-offset').value;
if (offset.kind !== 'constant' && offset.isLineProgressConstant === false) {
definesValues.push("VARIABLE_LINE_OFFSET");
}

if (isDraping) {
if (painter.emissiveMode === 'dual-source-blending' && !constantEmissiveStrength) {
definesValues.push('DUAL_SOURCE_BLENDING');
Expand Down
2 changes: 1 addition & 1 deletion src/render/program/line_program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export type LinePatternUniformsType = {
['u_pattern_transition']: Uniform1f;
};

export type LineDefinesType = 'RENDER_LINE_GRADIENT' | 'RENDER_LINE_DASH' | 'RENDER_LINE_TRIM_OFFSET' | 'RENDER_LINE_BORDER' | 'LINE_JOIN_NONE' | 'ELEVATED' | 'VARIABLE_LINE_WIDTH' | 'CROSS_SLOPE_VERTICAL' | 'CROSS_SLOPE_HORIZONTAL' | 'ELEVATION_REFERENCE_SEA' | 'LINE_PATTERN_TRANSITION' | 'USE_MRT1' | 'DUAL_SOURCE_BLENDING';
export type LineDefinesType = 'RENDER_LINE_GRADIENT' | 'RENDER_LINE_DASH' | 'RENDER_LINE_TRIM_OFFSET' | 'RENDER_LINE_BORDER' | 'LINE_JOIN_NONE' | 'ELEVATED' | 'VARIABLE_LINE_WIDTH' | 'VARIABLE_LINE_OFFSET' | 'CROSS_SLOPE_VERTICAL' | 'CROSS_SLOPE_HORIZONTAL' | 'ELEVATION_REFERENCE_SEA' | 'LINE_PATTERN_TRANSITION' | 'USE_MRT1' | 'DUAL_SOURCE_BLENDING';

const lineUniforms = (context: Context): LineUniformsType => ({
'u_matrix': new UniformMatrix4f(context),
Expand Down
17 changes: 15 additions & 2 deletions src/shaders/line.vertex.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

in vec2 a_pos_normal;
in vec4 a_data;
#if defined(ELEVATED) || defined(ELEVATED_ROADS) || defined(VARIABLE_LINE_WIDTH)
#if defined(ELEVATED) || defined(ELEVATED_ROADS) || defined(VARIABLE_LINE_WIDTH) || defined(VARIABLE_LINE_OFFSET)
in vec3 a_z_offset_width;
#endif

Expand Down Expand Up @@ -130,14 +130,27 @@ void main() {

offset = -1.0 * offset * u_width_scale;

#ifdef VARIABLE_LINE_OFFSET
offset = -1.0 * a_z_offset_width.z * u_width_scale;
#endif

// these transformations used to be applied in the JS and native code bases.
// moved them into the shader for clarity and simplicity.
gapwidth = gapwidth / 2.0;
float halfwidth;
#ifdef VARIABLE_LINE_WIDTH
bool left = normal.y == 1.0;
float left_width = a_z_offset_width.y;
float right_width = a_z_offset_width.z;
float right_width = a_z_offset_width.y;

#ifdef VARIABLE_LINE_OFFSET
// When both features active: use symmetric width, offset from z component
offset = -1.0 * a_z_offset_width.z * u_width_scale;
#else
// Original behavior: z component is right_width for asymmetric lines
right_width = a_z_offset_width.z;
#endif

halfwidth = (u_width_scale * (left ? left_width : right_width)) / 2.0;
a_z_offset += left ? side_z_offset : 0.0;
v_normal = side_z_offset > 0.0 && left ? vec2(0.0) : v_normal;
Expand Down
7 changes: 6 additions & 1 deletion src/shaders/line_pattern.vertex.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

in vec2 a_pos_normal;
in vec4 a_data;
#if defined(ELEVATED) || defined(ELEVATED_ROADS)
#if defined(ELEVATED) || defined(ELEVATED_ROADS) || defined(VARIABLE_LINE_OFFSET)
in vec3 a_z_offset_width;
#endif
// Includes in order: a_uv_x, a_split_index, a_line_progress
Expand Down Expand Up @@ -128,6 +128,11 @@ void main() {
float halfwidth = (u_width_scale * width) / 2.0;
offset = -1.0 * offset * u_width_scale;

#ifdef VARIABLE_LINE_OFFSET
// Variable offset uses the same scaling as regular offset
offset = -1.0 * a_z_offset_width.z * u_width_scale;
#endif

float inset = gapwidth + (gapwidth > 0.0 ? ANTIALIASING : 0.0);
float outset = gapwidth + halfwidth * (gapwidth > 0.0 ? 2.0 : 1.0) + (halfwidth == 0.0 ? 0.0 : ANTIALIASING);

Expand Down
Loading
Loading