fix(hatch): render weighted pattern strokes
Show styled boundaries only while selected.\n\nRefs #821
This commit is contained in:
parent
81de94fdbb
commit
fd801fac1b
6 changed files with 55 additions and 23 deletions
|
|
@ -295,9 +295,7 @@ impl StorageHatchBatch {
|
||||||
// along-line phase.
|
// along-line phase.
|
||||||
let perp_step = fam.dy;
|
let perp_step = fam.dy;
|
||||||
let along_step = fam.dx;
|
let along_step = fam.dx;
|
||||||
// Screen-space derivative drives 1-px line width
|
let line_width = h.line_weight_px.max(1.0);
|
||||||
// in the shader; this stored field is unused.
|
|
||||||
let line_width = 0.0_f32;
|
|
||||||
let period: f32 = fam.dashes.iter().map(|d| d.abs()).sum();
|
let period: f32 = fam.dashes.iter().map(|d| d.abs()).sum();
|
||||||
families.push(LineFamilyGpu {
|
families.push(LineFamilyGpu {
|
||||||
cos_a: fam.angle_deg.to_radians().cos(),
|
cos_a: fam.angle_deg.to_radians().cos(),
|
||||||
|
|
|
||||||
|
|
@ -286,7 +286,12 @@ impl TextureHatch {
|
||||||
texels.push([angle_r.cos(), angle_r.sin(), fam.x0, fam.y0]);
|
texels.push([angle_r.cos(), angle_r.sin(), fam.x0, fam.y0]);
|
||||||
texels.push([fam.dx, fam.dy, fam.dy, fam.dx]);
|
texels.push([fam.dx, fam.dy, fam.dy, fam.dx]);
|
||||||
// Counts as exact f32 (small integers → no denormal/bitcast risk).
|
// Counts as exact f32 (small integers → no denormal/bitcast risk).
|
||||||
texels.push([0.0, period, n_dashes as f32, dash_rel as f32]);
|
texels.push([
|
||||||
|
model.line_weight_px.max(1.0),
|
||||||
|
period,
|
||||||
|
n_dashes as f32,
|
||||||
|
dash_rel as f32,
|
||||||
|
]);
|
||||||
n_families += 1;
|
n_families += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -351,10 +351,8 @@ impl Scene {
|
||||||
(plain, texts)
|
(plain, texts)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Boundary outline wire for a hatch, reconstructed from its cached
|
/// Boundary wire for edit previews and selected hatches.
|
||||||
/// `HatchModel` (offsets from `world_origin`). Used only for edit previews —
|
pub(super) fn hatch_outline_wire(&self, handle: Handle) -> Option<WireModel> {
|
||||||
/// the normal render shows the fill, not this outline.
|
|
||||||
fn hatch_outline_wire(&self, handle: Handle) -> Option<WireModel> {
|
|
||||||
let m = self.hatches.get(&handle)?;
|
let m = self.hatches.get(&handle)?;
|
||||||
Self::hatch_model_outline_wire(handle, m)
|
Self::hatch_model_outline_wire(handle, m)
|
||||||
}
|
}
|
||||||
|
|
@ -375,12 +373,15 @@ impl Scene {
|
||||||
if pts.len() < 2 {
|
if pts.len() < 2 {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
Some(WireModel::solid_f64(
|
let mut wire = WireModel::solid_f64(
|
||||||
handle.value().to_string(),
|
handle.value().to_string(),
|
||||||
pts,
|
pts,
|
||||||
m.color,
|
m.color,
|
||||||
false,
|
false,
|
||||||
))
|
);
|
||||||
|
wire.aci = m.aci;
|
||||||
|
wire.line_weight_px = m.line_weight_px;
|
||||||
|
Some(wire)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fill-only geometry has no resident wire for the normal rollover xray.
|
/// Fill-only geometry has no resident wire for the normal rollover xray.
|
||||||
|
|
|
||||||
|
|
@ -3076,15 +3076,38 @@ impl Scene {
|
||||||
let Some(entity) = self.document.get_entity(handle) else {
|
let Some(entity) = self.document.get_entity(handle) else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
if !crate::scene::annotative::is_annotative(&self.document, entity)
|
if !self.resident_entity_visible(
|
||||||
|| !self.resident_entity_visible(
|
|
||||||
entity,
|
entity,
|
||||||
target_block,
|
target_block,
|
||||||
Some(&frozen),
|
Some(&frozen),
|
||||||
annotation_scale_handle,
|
annotation_scale_handle,
|
||||||
true,
|
true,
|
||||||
)
|
) {
|
||||||
{
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if matches!(entity, EntityType::Hatch(_)) {
|
||||||
|
if selected {
|
||||||
|
if let Some(mut wire) = self.hatch_outline_wire(handle) {
|
||||||
|
let (_, pattern_length, pattern, line_weight_px, aci) =
|
||||||
|
render_style_for_viewport(
|
||||||
|
&self.document,
|
||||||
|
entity,
|
||||||
|
content_viewport.then_some(inst.handle),
|
||||||
|
);
|
||||||
|
wire.color = WireModel::SELECTED;
|
||||||
|
wire.selected = true;
|
||||||
|
wire.pattern_length = pattern_length;
|
||||||
|
wire.pattern = pattern;
|
||||||
|
wire.line_weight_px = line_weight_px;
|
||||||
|
wire.aci = aci;
|
||||||
|
wires.push(wire);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !crate::scene::annotative::is_annotative(&self.document, entity) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -151,7 +151,6 @@ fn check_family(
|
||||||
let pz = xz.y - oz;
|
let pz = xz.y - oz;
|
||||||
|
|
||||||
let perp_step = fam.perp_step * scale;
|
let perp_step = fam.perp_step * scale;
|
||||||
let line_w = abs(fam.line_width * scale);
|
|
||||||
|
|
||||||
let perp = -px * sin_a + pz * cos_a;
|
let perp = -px * sin_a + pz * cos_a;
|
||||||
let k = round(perp / perp_step);
|
let k = round(perp / perp_step);
|
||||||
|
|
@ -163,6 +162,8 @@ fn check_family(
|
||||||
-ddx_xz.x * sin_a + ddx_xz.y * cos_a,
|
-ddx_xz.x * sin_a + ddx_xz.y * cos_a,
|
||||||
-ddy_xz.x * sin_a + ddy_xz.y * cos_a,
|
-ddy_xz.x * sin_a + ddy_xz.y * cos_a,
|
||||||
)) * 0.5;
|
)) * 0.5;
|
||||||
|
let width_px = select(1.0, max(fam.line_width, 1.0), u.lwdisplay_enable > 0.5);
|
||||||
|
let half_line = half_px * width_px;
|
||||||
|
|
||||||
// World units per screen pixel on each axis — used to light exactly the
|
// World units per screen pixel on each axis — used to light exactly the
|
||||||
// one pixel that contains a dot's centre (pixel-snapped, so the dot stays
|
// one pixel that contains a dot's centre (pixel-snapped, so the dot stays
|
||||||
|
|
@ -172,8 +173,8 @@ fn check_family(
|
||||||
|
|
||||||
// A fragment within ~1px of a line may be a dot; everything further out is
|
// A fragment within ~1px of a line may be a dot; everything further out is
|
||||||
// empty fill. (A dot's pixel sits on a line, so its perp offset is < 1px.)
|
// empty fill. (A dot's pixel sits on a line, so its perp offset is < 1px.)
|
||||||
if d > half_px * 2.0 { return false; }
|
if d > max(half_line, half_px * 2.0) { return false; }
|
||||||
if fam.n_dashes == 0u { return d <= half_px; }
|
if fam.n_dashes == 0u { return d <= half_line; }
|
||||||
|
|
||||||
let along_step = fam.along_step * scale;
|
let along_step = fam.along_step * scale;
|
||||||
let period = fam.period * scale;
|
let period = fam.period * scale;
|
||||||
|
|
@ -185,7 +186,7 @@ fn check_family(
|
||||||
for (var j = 0u; j < fam.n_dashes; j++) {
|
for (var j = 0u; j < fam.n_dashes; j++) {
|
||||||
let sv = dashes[fam.dash_offset + j] * scale;
|
let sv = dashes[fam.dash_offset + j] * scale;
|
||||||
if sv > 0.0 {
|
if sv > 0.0 {
|
||||||
if d <= half_px && t_mod >= pos && t_mod < pos + sv { return true; }
|
if d <= half_line && t_mod >= pos && t_mod < pos + sv { return true; }
|
||||||
pos = pos + sv;
|
pos = pos + sv;
|
||||||
} else if sv < 0.0 {
|
} else if sv < 0.0 {
|
||||||
pos = pos - sv;
|
pos = pos - sv;
|
||||||
|
|
@ -197,7 +198,8 @@ fn check_family(
|
||||||
let dtv = (t - pos) - round((t - pos) / period) * period;
|
let dtv = (t - pos) - round((t - pos) / period) * period;
|
||||||
let owx = -dtv * cos_a + dperp * sin_a;
|
let owx = -dtv * cos_a + dperp * sin_a;
|
||||||
let owy = -dtv * sin_a - dperp * cos_a;
|
let owy = -dtv * sin_a - dperp * cos_a;
|
||||||
if abs(owx / wpx) <= 0.5 && abs(owy / wpy) <= 0.5 { return true; }
|
let dot_half = width_px * 0.5;
|
||||||
|
if abs(owx / wpx) <= dot_half && abs(owy / wpy) <= dot_half { return true; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
|
||||||
|
|
@ -146,13 +146,15 @@ fn check_family(
|
||||||
-ddx_xz.x * sin_a + ddx_xz.y * cos_a,
|
-ddx_xz.x * sin_a + ddx_xz.y * cos_a,
|
||||||
-ddy_xz.x * sin_a + ddy_xz.y * cos_a,
|
-ddy_xz.x * sin_a + ddy_xz.y * cos_a,
|
||||||
)) * 0.5;
|
)) * 0.5;
|
||||||
|
let width_px = select(1.0, max(fam.line_width, 1.0), u.lwdisplay_enable > 0.5);
|
||||||
|
let half_line = half_px * width_px;
|
||||||
|
|
||||||
let wpx = length(vec2<f32>(ddx_xz.x, ddy_xz.x));
|
let wpx = length(vec2<f32>(ddx_xz.x, ddy_xz.x));
|
||||||
let wpy = length(vec2<f32>(ddx_xz.y, ddy_xz.y));
|
let wpy = length(vec2<f32>(ddx_xz.y, ddy_xz.y));
|
||||||
|
|
||||||
if d > half_px * 2.0 { return false; }
|
if d > max(half_line, half_px * 2.0) { return false; }
|
||||||
|
|
||||||
if fam.n_dashes == 0u { return d <= half_px; }
|
if fam.n_dashes == 0u { return d <= half_line; }
|
||||||
|
|
||||||
let along_step = fam.along_step * scale;
|
let along_step = fam.along_step * scale;
|
||||||
let period = fam.period * scale;
|
let period = fam.period * scale;
|
||||||
|
|
@ -165,7 +167,7 @@ fn check_family(
|
||||||
let idx = fam.dash_off + j;
|
let idx = fam.dash_off + j;
|
||||||
let sv = texel(h.dash_off + idx / 4u)[idx % 4u] * scale;
|
let sv = texel(h.dash_off + idx / 4u)[idx % 4u] * scale;
|
||||||
if sv > 0.0 {
|
if sv > 0.0 {
|
||||||
if d <= half_px && t_mod >= pos && t_mod < pos + sv { return true; }
|
if d <= half_line && t_mod >= pos && t_mod < pos + sv { return true; }
|
||||||
pos = pos + sv;
|
pos = pos + sv;
|
||||||
} else if sv < 0.0 {
|
} else if sv < 0.0 {
|
||||||
pos = pos - sv;
|
pos = pos - sv;
|
||||||
|
|
@ -173,7 +175,8 @@ fn check_family(
|
||||||
let dtv = (t - pos) - round((t - pos) / period) * period;
|
let dtv = (t - pos) - round((t - pos) / period) * period;
|
||||||
let owx = -dtv * cos_a + dperp * sin_a;
|
let owx = -dtv * cos_a + dperp * sin_a;
|
||||||
let owy = -dtv * sin_a - dperp * cos_a;
|
let owy = -dtv * sin_a - dperp * cos_a;
|
||||||
if abs(owx / wpx) <= 0.5 && abs(owy / wpy) <= 0.5 { return true; }
|
let dot_half = width_px * 0.5;
|
||||||
|
if abs(owx / wpx) <= dot_half && abs(owy / wpy) <= dot_half { return true; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue