fix(render): miter the joints of wide polyline bands

Each band segment was a rectangle ending square at its vertex, so the
outer corner of every joint showed a notch. Every segment end now carries
the tangent of half its turn, and the shader slides that end's corners
onto the joint's bisector so neighbours meet in a miter. Turns sharper
than the limit keep butt ends. Constant-width runs inside a polyline split
around its arcs no longer count as tapered, which had kept them unmitred.

Fixes #1565

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Hakan SevenandClaude Opus 5.5 committed 2026-10-04 20:44:08 +03:00
1 parent 4cfcc9dc38
commit d6dfd0d46d
6 files changed
+161 -23

No files matched your search

+8 -2
View File
@@ -46,6 +46,8 @@ struct VertexIn {
const MODEL_LINEWEIGHT_BOOST: f32 = 2.0;
const MODEL_LINEWEIGHT_MAX_PX: f32 = 10.0;
// Must match `MITER_LIMIT` in wire_gpu.rs.
const MITER_LIMIT: f32 = 8.0;
struct VertexOut {
@builtin(position) clip_pos: vec4<f32>,
@@ -161,13 +163,17 @@ fn vs_main(@builtin(vertex_index) vertex_index: u32, in: VertexIn) -> VertexOut
}
perp_world = normalize(perp_world);
// A constant-width band slides each corner along the segment onto
// its joint's bisector (miter), so neighbours meet without a notch.
let miter = select(in.taper_ratio, vec2<f32>(0.0), is_tapered) * MITER_LIMIT;
let along = mix(miter.x, miter.y, which_end) * eff_hw * side;
let pos_rel = mix(rel_a, rel_b, which_end);
let world_pos = pos_rel + perp_world * (eff_hw * side);
let world_pos = pos_rel + perp_world * (eff_hw * side) + world_dir * along;
var clip_pos = u.view_rot * vec4<f32>(world_pos, 1.0);
clip_pos = apply_draw_order(clip_pos, in.depth.x);
final_clip = clip_pos;
out_dist = mix(in.distances.x, in.distances.y, which_end);
out_dist = mix(in.distances.x, in.distances.y, which_end) + along * (in.distances.y - in.distances.x) / max(world_len, 1e-6);
out_cap = vec2<f32>(which_end * world_len, eff_hw * side);
out_cap_ends = vec3<f32>(world_len, world_hw_a, world_hw_b);
} else {
+9 -3
View File
@@ -70,6 +70,8 @@ struct VertexIn {
const MODEL_LINEWEIGHT_BOOST: f32 = 2.0;
const MODEL_LINEWEIGHT_MAX_PX: f32 = 10.0;
// Must match `MITER_LIMIT` in wire_gpu.rs.
const MITER_LIMIT: f32 = 8.0;
struct VertexOut {
@builtin(position) clip_pos: vec4<f32>,
@@ -144,7 +146,7 @@ fn vs_main(@builtin(vertex_index) vertex_index: u32, in: VertexIn) -> VertexOut
let seg_pos_b = vec3<f32>(seg.pos_b_x, seg.pos_b_y, seg.pos_b_z);
let seg_pos_b_low = vec3<f32>(seg.pos_b_low_x, seg.pos_b_low_y, seg.pos_b_low_z);
let seg_distances = vec2<f32>(seg.distance_a, seg.distance_b);
let seg_taper_ratio = unpack2x16unorm(seg.taper_ratio);
let seg_taper_ratio = unpack2x16snorm(seg.taper_ratio);
let which_end_arr = array<f32, 6>(0.0, 1.0, 1.0, 0.0, 1.0, 0.0);
let side_arr = array<f32, 6>(-1.0, -1.0, 1.0, -1.0, 1.0, 1.0);
let which_end = which_end_arr[corner];
@@ -192,13 +194,17 @@ fn vs_main(@builtin(vertex_index) vertex_index: u32, in: VertexIn) -> VertexOut
}
perp_world = normalize(perp_world);
// A constant-width band slides each corner along the segment onto
// its joint's bisector (miter), so neighbours meet without a notch.
let miter = select(seg_taper_ratio, vec2<f32>(0.0), is_tapered) * MITER_LIMIT;
let along = mix(miter.x, miter.y, which_end) * eff_hw * side;
let pos_rel = mix(rel_a, rel_b, which_end);
let world_pos = pos_rel + perp_world * (eff_hw * side);
let world_pos = pos_rel + perp_world * (eff_hw * side) + world_dir * along;
var clip_pos = u.view_rot * vec4<f32>(world_pos, 1.0);
clip_pos = apply_draw_order(clip_pos, in.depth.x);
final_clip = clip_pos;
out_dist = mix(seg_distances.x, seg_distances.y, which_end);
out_dist = mix(seg_distances.x, seg_distances.y, which_end) + along * (seg_distances.y - seg_distances.x) / max(world_len, 1e-6);
out_cap = vec2<f32>(which_end * world_len, eff_hw * side);
out_cap_ends = vec3<f32>(world_len, world_hw_a, world_hw_b);
} else {
+8 -2
View File
@@ -69,6 +69,8 @@ struct InstanceIn {
const MODEL_LINEWEIGHT_BOOST: f32 = 2.0;
const MODEL_LINEWEIGHT_MAX_PX: f32 = 10.0;
// Must match `MITER_LIMIT` in wire_gpu.rs.
const MITER_LIMIT: f32 = 8.0;
struct VertexOut {
@builtin(position) clip_pos: vec4<f32>,
@@ -191,13 +193,17 @@ fn marker_relative(position_high: vec3<f32>, position_low: vec3<f32>, instance:
}
perp_world = normalize(perp_world);
// A constant-width band slides each corner along the segment onto
// its joint's bisector (miter), so neighbours meet without a notch.
let miter = vec2<f32>(in.marker_origin_high.w, in.marker_origin_low.w) * MITER_LIMIT;
let along = mix(miter.x, miter.y, which_end) * eff_hw * side;
let pos_rel = mix(rel_a, rel_b, which_end);
let world_pos = pos_rel + perp_world * (eff_hw * side);
let world_pos = pos_rel + perp_world * (eff_hw * side) + world_dir * along;
var clip_pos = u.view_rot * vec4<f32>(world_pos, 1.0);
clip_pos = apply_draw_order(clip_pos, in.misc.x);
final_clip = clip_pos;
out_dist = mix(in.dists.x, in.dists.y, which_end);
out_dist = mix(in.dists.x, in.dists.y, which_end) + along * (in.dists.y - in.dists.x) / max(world_len, 1e-6);
out_cap = vec2<f32>(which_end * world_len, eff_hw * side);
out_cap_ends = vec3<f32>(world_len, world_hw_a, world_hw_b);
} else {
+8 -2
View File
@@ -55,6 +55,8 @@ struct InstanceIn {
const MODEL_LINEWEIGHT_BOOST: f32 = 2.0;
const MODEL_LINEWEIGHT_MAX_PX: f32 = 10.0;
// Must match `MITER_LIMIT` in wire_gpu.rs.
const MITER_LIMIT: f32 = 8.0;
struct VertexOut {
@builtin(position) clip_pos: vec4<f32>,
@@ -168,13 +170,17 @@ fn marker_relative(position_high: vec3<f32>, position_low: vec3<f32>, c: WireCon
}
perp_world = normalize(perp_world);
// A constant-width band slides each corner along the segment onto
// its joint's bisector (miter), so neighbours meet without a notch.
let miter = select(in.taper_ratio, vec2<f32>(0.0), is_tapered) * MITER_LIMIT;
let along = mix(miter.x, miter.y, which_end) * eff_hw * side;
let pos_rel = mix(rel_a, rel_b, which_end);
let world_pos = pos_rel + perp_world * (eff_hw * side);
let world_pos = pos_rel + perp_world * (eff_hw * side) + world_dir * along;
var clip_pos = u.view_rot * vec4<f32>(world_pos, 1.0);
clip_pos = apply_draw_order(clip_pos, c.draw_depth);
final_clip = clip_pos;
out_dist = mix(in.distance_a, in.distance_b, which_end);
out_dist = mix(in.distance_a, in.distance_b, which_end) + along * (in.distance_b - in.distance_a) / max(world_len, 1e-6);
out_cap = vec2<f32>(which_end * world_len, eff_hw * side);
out_cap_ends = vec3<f32>(world_len, world_hw_a, world_hw_b);
} else {