forked from 1b-agent/opencadstudio-upstream
fix(render): correct polyline segment widths, flat arc end caps, and zero-width endpoints
This commit is contained in:
1 parent
6ca16738f0
commit
0ad31ece6f
8 files changed
+346
-73
No files matched your search
@@ -24,7 +24,7 @@ struct WireConst {
|
||||
align_end: f32,
|
||||
align_total: f32,
|
||||
world_half_width: f32,
|
||||
_pad1: f32,
|
||||
is_tapered: f32,
|
||||
_pad2: f32,
|
||||
marker_origin_high: vec4<f32>,
|
||||
marker_origin_low: vec4<f32>,
|
||||
@@ -121,7 +121,8 @@ fn vs_main(@builtin(vertex_index) vertex_index: u32, in: VertexIn) -> VertexOut
|
||||
|
||||
let rel_a = marker_relative(in.pos_a, in.pos_a_low, in.translation, in.translation_low);
|
||||
let rel_b = marker_relative(in.pos_b, in.pos_b_low, in.translation, in.translation_low);
|
||||
let is_world = wire_const.world_half_width > 0.0 || in.taper_ratio.x > 0.0 || in.taper_ratio.y > 0.0;
|
||||
let is_tapered = wire_const.is_tapered > 0.5;
|
||||
let is_world = wire_const.world_half_width > 0.0;
|
||||
|
||||
var final_clip: vec4<f32>;
|
||||
var out_dist: f32;
|
||||
@@ -134,8 +135,8 @@ fn vs_main(@builtin(vertex_index) vertex_index: u32, in: VertexIn) -> VertexOut
|
||||
// the segment and the plane normal), so the rectangle remains hosted rigidly
|
||||
// on the 3D plane when the camera rotates, orbits, or tilts, matching CAD
|
||||
// behavior and circle.wgsl planar arcs.
|
||||
let world_hw_a = select(wire_const.world_half_width, in.taper_ratio.x * wire_const.world_half_width, in.taper_ratio.x > 0.0);
|
||||
let world_hw_b = select(wire_const.world_half_width, in.taper_ratio.y * wire_const.world_half_width, in.taper_ratio.y > 0.0);
|
||||
let world_hw_a = select(wire_const.world_half_width, in.taper_ratio.x * wire_const.world_half_width, is_tapered);
|
||||
let world_hw_b = select(wire_const.world_half_width, in.taper_ratio.y * wire_const.world_half_width, is_tapered);
|
||||
let cur_world_hw = mix(world_hw_a, world_hw_b, which_end);
|
||||
let eff_hw = max(cur_world_hw, 0.5 * u.world_per_pixel);
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ struct WireConst {
|
||||
align_end: f32,
|
||||
align_total: f32,
|
||||
world_half_width: f32,
|
||||
_pad1: f32,
|
||||
is_tapered: f32,
|
||||
_pad2: f32,
|
||||
marker_origin_high: vec4<f32>,
|
||||
marker_origin_low: vec4<f32>,
|
||||
@@ -152,7 +152,8 @@ fn vs_main(@builtin(vertex_index) vertex_index: u32, in: VertexIn) -> VertexOut
|
||||
|
||||
let rel_a = marker_relative(seg_pos_a, seg_pos_a_low, in.translation, in.translation_low);
|
||||
let rel_b = marker_relative(seg_pos_b, seg_pos_b_low, in.translation, in.translation_low);
|
||||
let is_world = wire_const.world_half_width > 0.0 || seg_taper_ratio.x > 0.0 || seg_taper_ratio.y > 0.0;
|
||||
let is_tapered = wire_const.is_tapered > 0.5;
|
||||
let is_world = wire_const.world_half_width > 0.0;
|
||||
|
||||
var final_clip: vec4<f32>;
|
||||
var out_dist: f32;
|
||||
@@ -165,8 +166,8 @@ fn vs_main(@builtin(vertex_index) vertex_index: u32, in: VertexIn) -> VertexOut
|
||||
// the segment and the plane normal), so the rectangle remains hosted rigidly
|
||||
// on the 3D plane when the camera rotates, orbits, or tilts, matching CAD
|
||||
// behavior and circle.wgsl planar arcs.
|
||||
let world_hw_a = select(wire_const.world_half_width, seg_taper_ratio.x * wire_const.world_half_width, seg_taper_ratio.x > 0.0);
|
||||
let world_hw_b = select(wire_const.world_half_width, seg_taper_ratio.y * wire_const.world_half_width, seg_taper_ratio.y > 0.0);
|
||||
let world_hw_a = select(wire_const.world_half_width, seg_taper_ratio.x * wire_const.world_half_width, is_tapered);
|
||||
let world_hw_b = select(wire_const.world_half_width, seg_taper_ratio.y * wire_const.world_half_width, is_tapered);
|
||||
let cur_world_hw = mix(world_hw_a, world_hw_b, which_end);
|
||||
let eff_hw = max(cur_world_hw, 0.5 * u.world_per_pixel);
|
||||
|
||||
|
||||
+36
-20
@@ -207,32 +207,48 @@ fn in_dash(dist: f32, pat_len: f32, p0: vec4<f32>, p1: vec4<f32>) -> bool {
|
||||
let is_full_or_in_sweep = (sweep >= TAU - 1e-5) || (d_theta <= sweep);
|
||||
|
||||
if in.is_world > 0.5 {
|
||||
// Physical world-unit width (thick analytical circular arcs, donuts, tapered segments).
|
||||
// Distance-to-edge calculations are strictly performed in the circle plane's world units
|
||||
// so that 3D perspective projection yields exact concentric ellipses with invariant
|
||||
// physical stroke widths across any camera tilt or acute perspective angle.
|
||||
if is_full_or_in_sweep {
|
||||
let t = select(0.0, clamp(d_theta / sweep, 0.0, 1.0), sweep > 1e-5);
|
||||
let world_hw = mix(in.hw_ends.x, in.hw_ends.y, t);
|
||||
let eff_hw = max(world_hw, 0.5 * fw);
|
||||
let is_full = sweep >= TAU - 1e-5;
|
||||
if is_full {
|
||||
let eff_hw = max(in.hw_ends.x, 0.5 * fw);
|
||||
let d_world = abs(r - in.radius);
|
||||
let delta_px = (eff_hw - d_world) / fw;
|
||||
let cov = clamp(0.5 + delta_px, 0.0, 1.0);
|
||||
let subpixel_fade = min(1.0, (2.0 * world_hw) / fw);
|
||||
let subpixel_fade = min(1.0, (2.0 * in.hw_ends.x) / fw);
|
||||
alpha_cov = cov * subpixel_fade;
|
||||
arc_dist = in.radius * d_theta;
|
||||
} else {
|
||||
// Outside sweep: evaluate distance to start and end endpoints for smooth round end caps
|
||||
let p_start = in.radius * vec2<f32>(cos(sa), sin(sa));
|
||||
let p_end = in.radius * vec2<f32>(cos(ea), sin(ea));
|
||||
let d_start = length(in.local_pos - p_start);
|
||||
let d_end = length(in.local_pos - p_end);
|
||||
let eff_hw_a = max(in.hw_ends.x, 0.5 * fw);
|
||||
let eff_hw_b = max(in.hw_ends.y, 0.5 * fw);
|
||||
let cov_start = clamp(0.5 + (eff_hw_a - d_start) / fw, 0.0, 1.0) * min(1.0, (2.0 * in.hw_ends.x) / fw);
|
||||
let cov_end = clamp(0.5 + (eff_hw_b - d_end) / fw, 0.0, 1.0) * min(1.0, (2.0 * in.hw_ends.y) / fw);
|
||||
alpha_cov = max(cov_start, cov_end);
|
||||
arc_dist = select(in.radius * sweep, 0.0, cov_start >= cov_end);
|
||||
// Flat (radial) end caps perpendicular to arc tangent at start_angle and end_angle.
|
||||
let d_start_rad = r * d_theta;
|
||||
let d_end_rad = r * (sweep - d_theta);
|
||||
let d_before_sa = r * mod_tau(sa - theta);
|
||||
let d_after_ea = r * mod_tau(theta - ea);
|
||||
|
||||
var cov_angular: f32 = 0.0;
|
||||
var world_hw: f32 = 0.0;
|
||||
|
||||
if is_full_or_in_sweep {
|
||||
let t = select(0.0, clamp(d_theta / sweep, 0.0, 1.0), sweep > 1e-5);
|
||||
world_hw = mix(in.hw_ends.x, in.hw_ends.y, t);
|
||||
let d_inside = min(d_start_rad, d_end_rad);
|
||||
cov_angular = clamp(0.5 + d_inside / fw, 0.0, 1.0);
|
||||
arc_dist = in.radius * d_theta;
|
||||
} else {
|
||||
let d_outside = min(d_before_sa, d_after_ea);
|
||||
if d_outside <= 0.5 * fw {
|
||||
cov_angular = clamp(0.5 - d_outside / fw, 0.0, 1.0);
|
||||
world_hw = select(in.hw_ends.y, in.hw_ends.x, d_before_sa <= d_after_ea);
|
||||
arc_dist = select(in.radius * sweep, 0.0, d_before_sa <= d_after_ea);
|
||||
}
|
||||
}
|
||||
|
||||
if cov_angular > 0.0 {
|
||||
let eff_hw = max(world_hw, 0.5 * fw);
|
||||
let d_world = abs(r - in.radius);
|
||||
let delta_px = (eff_hw - d_world) / fw;
|
||||
let cov_radial = clamp(0.5 + delta_px, 0.0, 1.0);
|
||||
let subpixel_fade = min(1.0, (2.0 * world_hw) / fw);
|
||||
alpha_cov = cov_radial * cov_angular * subpixel_fade;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Lineweight stroke (screen-pixel width)
|
||||
|
||||
@@ -102,7 +102,7 @@ struct VertexOut {
|
||||
// constant world-unit band, then the screen-pixel lineweight (LWDISPLAY off
|
||||
// collapses to a hairline).
|
||||
fn resolve_hw(taper: f32, world_hw: f32, px_hw: f32) -> f32 {
|
||||
if taper > 0.0 { return max(taper / u.world_per_pixel, 0.5); }
|
||||
if taper >= 0.0 { return max(taper / u.world_per_pixel, 0.5); }
|
||||
if world_hw > 0.0 { return max(world_hw / u.world_per_pixel, 0.5); }
|
||||
var display_hw = max(px_hw * u.lineweight_scale, 0.5);
|
||||
if u.lineweight_scale < 0.0 {
|
||||
@@ -156,7 +156,7 @@ fn marker_relative(position_high: vec3<f32>, position_low: vec3<f32>, instance:
|
||||
// UTM-scale coordinates and after a cross-drawing paste, with no jitter.
|
||||
let rel_a = marker_relative(in.pos_a, in.pos_a_low, in);
|
||||
let rel_b = marker_relative(in.pos_b, in.pos_b_low, in);
|
||||
let is_world = in.misc.w > 0.0 || in.taper.x > 0.0 || in.taper.y > 0.0;
|
||||
let is_world = in.misc.w > 0.0 || in.taper.x >= 0.0 || in.taper.y >= 0.0;
|
||||
|
||||
var final_clip: vec4<f32>;
|
||||
var out_dist: f32;
|
||||
@@ -169,8 +169,8 @@ fn marker_relative(position_high: vec3<f32>, position_low: vec3<f32>, instance:
|
||||
// the segment and the plane normal), so the rectangle remains hosted rigidly
|
||||
// on the 3D plane when the camera rotates, orbits, or tilts, matching CAD
|
||||
// behavior and circle.wgsl planar arcs.
|
||||
let world_hw_a = select(in.misc.w, in.taper.x, in.taper.x > 0.0);
|
||||
let world_hw_b = select(in.misc.w, in.taper.y, in.taper.y > 0.0);
|
||||
let world_hw_a = select(in.misc.w, in.taper.x, in.taper.x >= 0.0);
|
||||
let world_hw_b = select(in.misc.w, in.taper.y, in.taper.y >= 0.0);
|
||||
let cur_world_hw = mix(world_hw_a, world_hw_b, which_end);
|
||||
let eff_hw = max(cur_world_hw, 0.5 * u.world_per_pixel);
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ struct WireConst {
|
||||
align_end: f32,
|
||||
align_total: f32,
|
||||
world_half_width: f32,
|
||||
_pad1: f32,
|
||||
is_tapered: f32,
|
||||
_pad2: f32,
|
||||
marker_origin_high: vec4<f32>,
|
||||
marker_origin_low: vec4<f32>,
|
||||
@@ -128,7 +128,8 @@ fn marker_relative(position_high: vec3<f32>, position_low: vec3<f32>, c: WireCon
|
||||
|
||||
let rel_a = marker_relative(in.pos_a, in.pos_a_low, c);
|
||||
let rel_b = marker_relative(in.pos_b, in.pos_b_low, c);
|
||||
let is_world = c.world_half_width > 0.0 || in.taper_ratio.x > 0.0 || in.taper_ratio.y > 0.0;
|
||||
let is_tapered = c.is_tapered > 0.5;
|
||||
let is_world = c.world_half_width > 0.0;
|
||||
|
||||
var final_clip: vec4<f32>;
|
||||
var out_dist: f32;
|
||||
@@ -141,8 +142,8 @@ fn marker_relative(position_high: vec3<f32>, position_low: vec3<f32>, c: WireCon
|
||||
// the segment and the plane normal), so the rectangle remains hosted rigidly
|
||||
// on the 3D plane when the camera rotates, orbits, or tilts, matching CAD
|
||||
// behavior and circle.wgsl planar arcs.
|
||||
let world_hw_a = select(c.world_half_width, in.taper_ratio.x * c.world_half_width, in.taper_ratio.x > 0.0);
|
||||
let world_hw_b = select(c.world_half_width, in.taper_ratio.y * c.world_half_width, in.taper_ratio.y > 0.0);
|
||||
let world_hw_a = select(c.world_half_width, in.taper_ratio.x * c.world_half_width, is_tapered);
|
||||
let world_hw_b = select(c.world_half_width, in.taper_ratio.y * c.world_half_width, is_tapered);
|
||||
let cur_world_hw = mix(world_hw_a, world_hw_b, which_end);
|
||||
let eff_hw = max(cur_world_hw, 0.5 * u.world_per_pixel);
|
||||
|
||||
|
||||
Reference in new issue
Block a user