perf(wire): move LWDISPLAY toggle from CPU rebake to wire shader uniform

LWDISPLAY (Show Lineweight) flipping the toggle no longer triggers a full
retessellate. `render_style_for` always bakes the entity's resolved (layer-
inherited) lineweight, and the wire shader reads `Uniforms.lwdisplay_enable`
to either honour the per-vertex half_width or collapse the quad to a 1 px line.

Side effect: PDF export now uses the real lineweight regardless of LWDISPLAY,
matching AutoCAD's plot semantics (LWDISPLAY only affects screen render).

Co-Authored-By: Claude Opus 4.7 <[email protected]>
This commit is contained in:
HakanandClaude Opus 4.7 committed 2026-05-23 09:45:24 +03:00
1 parent fb3dd58d09
commit e9488f99dd
5 files changed
+38 -33

No files matched your search

+1 -1
View File
@@ -4522,7 +4522,7 @@ impl H7CAD {
Ok(Some(v)) => {
self.push_undo_snapshot(i, "LWDISPLAY");
self.tabs[i].scene.document.header.lineweight_display = v;
self.tabs[i].scene.bump_geometry();
// No retessellate — the wire shader honours the flag via uniforms.
self.tabs[i].dirty = true;
self.command_line.push_output(&format!(
"LWDISPLAY {}",
+1 -1
View File
@@ -2514,7 +2514,7 @@ impl H7CAD {
if i < self.tabs.len() {
let h = &mut self.tabs[i].scene.document.header;
h.lineweight_display = !h.lineweight_display;
self.tabs[i].scene.bump_geometry();
// No retessellate — the wire shader reads the flag from uniforms.
self.tabs[i].dirty = true;
}
Task::none()
+5 -3
View File
@@ -11,11 +11,13 @@ pub struct Uniforms {
/// hatch shader to substitute solid fill when pattern line spacing
/// falls below ~2 px (Phase 3.3 LOD).
pub world_per_pixel: f32,
pub _pad: f32,
/// LWDISPLAY toggle (1.0 = show lineweights, 0.0 = force 1 px).
/// Read by the wire shader so the toggle does not require a retessellate.
pub lwdisplay_enable: f32,
}
impl Uniforms {
pub fn new(camera: &Camera, bounds: Rectangle) -> Self {
pub fn new(camera: &Camera, bounds: Rectangle, lwdisplay_enable: bool) -> Self {
let half_h = camera.ortho_size();
let world_per_pixel = if bounds.height > 0.0 {
(2.0 * half_h) / bounds.height
@@ -27,7 +29,7 @@ impl Uniforms {
camera_pos: camera.position_vec4(),
viewport_size: [bounds.width, bounds.height],
world_per_pixel,
_pad: 0.0,
lwdisplay_enable: if lwdisplay_enable { 1.0 } else { 0.0 },
}
}
}
+20 -23
View File
@@ -253,27 +253,24 @@ pub(super) fn render_style_for(
let (pattern_length, pattern) = resolve_pattern(&document.line_types, lt_name, lt_scale);
let line_weight_px = {
// LWDISPLAY (header.lineweight_display): when false, every entity
// draws at the 1-pixel base width regardless of its assigned weight.
// AutoCAD's "Show Lineweight" toggle maps onto this header var.
if !document.header.lineweight_display {
1.0
} else {
let ew = &e.common().line_weight;
let resolved = match ew {
LineWeight::ByLayer | LineWeight::ByBlock | LineWeight::Default => document
.layers
.get(layer_name)
.map(|l| &l.line_weight)
.unwrap_or(&LineWeight::Default),
_ => ew,
};
const MM_TO_PX: f32 = 96.0 / 25.4;
resolved
.millimeters()
.map(|mm| (mm as f32 * MM_TO_PX).max(1.0))
.unwrap_or(1.0)
}
// LWDISPLAY is no longer evaluated here — the toggle is now applied in
// the wire shader via `Uniforms.lwdisplay_enable`, so we always bake the
// entity's resolved (layer-inherited) weight. Toggling lineweight
// visibility costs only a uniform write, not a retessellate.
let ew = &e.common().line_weight;
let resolved = match ew {
LineWeight::ByLayer | LineWeight::ByBlock | LineWeight::Default => document
.layers
.get(layer_name)
.map(|l| &l.line_weight)
.unwrap_or(&LineWeight::Default),
_ => ew,
};
const MM_TO_PX: f32 = 96.0 / 25.4;
resolved
.millimeters()
.map(|mm| (mm as f32 * MM_TO_PX).max(1.0))
.unwrap_or(1.0)
};
(entity_color, pattern_length, pattern, line_weight_px, aci)
@@ -376,7 +373,7 @@ impl Scene {
wipeout_hatches: self.wipeout_models_arc(),
images: self.images_arc(),
meshes: self.meshes_arc(),
uniforms: Uniforms::new(&cam, bounds),
uniforms: Uniforms::new(&cam, bounds, self.document.header.lineweight_display),
cam_rotation: cam.view_rotation_mat(),
hover_region,
bg_color,
@@ -420,7 +417,7 @@ impl Scene {
wipeout_hatches: self.wipeout_models_arc(),
images: self.images_arc(),
meshes: self.meshes_arc(),
uniforms: Uniforms::new(&cam, bounds),
uniforms: Uniforms::new(&cam, bounds, self.document.header.lineweight_display),
cam_rotation: cam.view_rotation_mat(),
hover_region,
bg_color: self.bg_color,
+11 -5
View File
@@ -11,10 +11,13 @@
// • pat0/pat1 encode up to 8 elements: positive=dash, negative=gap.
struct Uniforms {
view_proj: mat4x4<f32>,
camera_pos: vec4<f32>,
viewport_size: vec2<f32>,
_pad: vec2<f32>,
view_proj: mat4x4<f32>,
camera_pos: vec4<f32>,
viewport_size: vec2<f32>,
world_per_pixel: f32,
// LWDISPLAY toggle: 0.0 = force 1 px (half_width 0.5), 1.0 = use per-vertex
// baked half_width. Lets the LWT button switch without retessellating.
lwdisplay_enable: f32,
}
@group(0) @binding(0) var<uniform> u: Uniforms;
@@ -70,8 +73,11 @@ struct VertexOut {
// Select the clip-space position for this vertex's endpoint.
let clip_pos = mix(clip_a, clip_b, in.which_end);
// LWDISPLAY off → collapse to a 1-pixel-wide line (half_width = 0.5).
let hw = select(0.5, in.half_width, u.lwdisplay_enable > 0.5);
// Offset in clip space (multiply by w to un-apply perspective division).
let ndc_offset = perp_ndc * in.half_width * in.side;
let ndc_offset = perp_ndc * hw * in.side;
let final_clip = clip_pos + vec4<f32>(ndc_offset * clip_pos.w, 0.0, 0.0);
var out: VertexOut;