fix(scene): rescale dash pattern when projecting wires to paper space
`viewport_content_wires` cloned model-space tessellations and overwrote `points` with paper-space projections (× scale) but left `pattern_length` and `pattern` at their model-coord values (pre-multiplied by PSLTSCALE's 1/vp_scale). The GPU then compared a paper-coord distance against a model-coord pattern, so dashed linetypes in viewport content collapsed to solid (a typical 9 mm paper line fell inside the first 180 mm dash). Multiply the projected wire's pattern by the same vp scale used for the points. Works for both PSLTSCALE on (paper-uniform dashes) and off (dashes shrink with viewport scale). Adds two diagnostic examples used to track this down: - inspect_lt: which layers/entities carry dashed linetypes - inspect_block: enumerate entities + linetypes inside a named block
This commit is contained in:
parent
66243e53ca
commit
a73dffdd76
3 changed files with 250 additions and 0 deletions
128
examples/inspect_block.rs
Normal file
128
examples/inspect_block.rs
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
// List entities inside a named block, showing their layer + resolved linetype.
|
||||
//
|
||||
// cargo run --release --example inspect_block -- <file> <block_name>
|
||||
|
||||
use acadrust::entities::EntityType;
|
||||
use acadrust::io::dwg::DwgReader;
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let path = std::env::args().nth(1).expect("usage: inspect_block <file> [block]");
|
||||
let block_name = std::env::args().nth(2);
|
||||
let doc = DwgReader::from_file(&path)?.read()?;
|
||||
|
||||
if block_name.is_none() {
|
||||
let mut blocks: Vec<_> = doc
|
||||
.block_records
|
||||
.iter()
|
||||
.map(|br| {
|
||||
let n = doc
|
||||
.entities()
|
||||
.filter(|e| e.common().owner_handle == br.handle)
|
||||
.count();
|
||||
(br.name.clone(), n)
|
||||
})
|
||||
.collect();
|
||||
blocks.sort_by_key(|(_, n)| std::cmp::Reverse(*n));
|
||||
println!("─── top 30 blocks by entity count ───────");
|
||||
for (name, n) in blocks.iter().take(30) {
|
||||
println!(" {:>5} {}", n, name);
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
let block_name = block_name.unwrap();
|
||||
|
||||
// Find the block record by name.
|
||||
let br = doc
|
||||
.block_records
|
||||
.iter()
|
||||
.find(|br| br.name.eq_ignore_ascii_case(&block_name))
|
||||
.ok_or_else(|| format!("block '{}' not found", block_name))?;
|
||||
|
||||
println!("block: \"{}\" handle={:?}", br.name, br.handle);
|
||||
println!();
|
||||
|
||||
let mut counts: std::collections::BTreeMap<&'static str, u32> = Default::default();
|
||||
let mut sample_by_kind: std::collections::BTreeMap<&'static str, Vec<String>> =
|
||||
Default::default();
|
||||
|
||||
for e in doc.entities() {
|
||||
if e.common().owner_handle != br.handle {
|
||||
continue;
|
||||
}
|
||||
let kind: &'static str = match e {
|
||||
EntityType::Line(_) => "Line",
|
||||
EntityType::LwPolyline(_) => "LwPolyline",
|
||||
EntityType::Polyline(_) => "Polyline",
|
||||
EntityType::Polyline2D(_) => "Polyline2D",
|
||||
EntityType::Arc(_) => "Arc",
|
||||
EntityType::Circle(_) => "Circle",
|
||||
EntityType::Spline(_) => "Spline",
|
||||
EntityType::Ellipse(_) => "Ellipse",
|
||||
EntityType::Text(_) => "Text",
|
||||
EntityType::MText(_) => "MText",
|
||||
EntityType::Insert(_) => "Insert",
|
||||
EntityType::Hatch(_) => "Hatch",
|
||||
EntityType::Solid(_) => "Solid",
|
||||
_ => "Other",
|
||||
};
|
||||
*counts.entry(kind).or_default() += 1;
|
||||
|
||||
let c = e.common();
|
||||
let extra = match e {
|
||||
EntityType::Insert(i) => format!(" → block=\"{}\"", i.block_name),
|
||||
_ => String::new(),
|
||||
};
|
||||
let line = format!(
|
||||
" [{:?}] layer=\"{}\" lt=\"{}\" lt_scale={:.4}{}",
|
||||
e.common().handle, c.layer, c.linetype, c.linetype_scale, extra
|
||||
);
|
||||
let bucket = sample_by_kind.entry(kind).or_default();
|
||||
if bucket.len() < 4 {
|
||||
bucket.push(line);
|
||||
}
|
||||
}
|
||||
|
||||
println!("─── entity counts in block ──────────────");
|
||||
for (k, v) in &counts {
|
||||
println!(" {:<12} {}", k, v);
|
||||
}
|
||||
println!();
|
||||
for (k, samples) in &sample_by_kind {
|
||||
println!("─── sample {} (first {}) ────────", k, samples.len());
|
||||
for s in samples {
|
||||
println!("{}", s);
|
||||
}
|
||||
println!();
|
||||
}
|
||||
|
||||
println!("─── relevant layers ─────────────────────");
|
||||
let mut seen: std::collections::BTreeSet<String> = Default::default();
|
||||
for e in doc.entities() {
|
||||
if e.common().owner_handle == br.handle {
|
||||
seen.insert(e.common().layer.clone());
|
||||
}
|
||||
}
|
||||
for layer_name in &seen {
|
||||
if let Some(l) = doc.layers.get(layer_name) {
|
||||
println!(
|
||||
" \"{}\" lt=\"{}\" off={} frozen={}",
|
||||
l.name, l.line_type, l.flags.off, l.flags.frozen
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
println!();
|
||||
println!("─── linetype table (dashed only) ────────");
|
||||
for lt in doc.line_types.iter() {
|
||||
if lt.elements.is_empty() || lt.is_continuous() {
|
||||
continue;
|
||||
}
|
||||
let elems: Vec<f64> = lt.elements.iter().map(|el| el.length).collect();
|
||||
println!(
|
||||
" {:<24} pattern_len={:.4} elements={:?}",
|
||||
lt.name, lt.pattern_length, elems
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
116
examples/inspect_lt.rs
Normal file
116
examples/inspect_lt.rs
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
// Summarise linetype usage in a DWG/DXF: which layers use which linetype,
|
||||
// and which entities carry an explicit (non-bylayer) dashed linetype.
|
||||
//
|
||||
// cargo run --release --example inspect_lt -- <file>
|
||||
|
||||
use acadrust::entities::EntityType;
|
||||
use acadrust::io::dwg::DwgReader;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let path = std::env::args().nth(1).expect("usage: inspect_lt <file>");
|
||||
let doc = DwgReader::from_file(&path)?.read()?;
|
||||
|
||||
println!("─── layers (linetype) ───────────────────");
|
||||
let dashed_lts: std::collections::BTreeSet<String> = doc
|
||||
.line_types
|
||||
.iter()
|
||||
.filter(|lt| !lt.elements.is_empty() && !lt.is_continuous())
|
||||
.map(|lt| lt.name.clone())
|
||||
.collect();
|
||||
println!(" dashed linetypes in table: {:?}", dashed_lts);
|
||||
println!();
|
||||
|
||||
let mut dashed_layers: BTreeMap<String, String> = BTreeMap::new();
|
||||
for l in doc.layers.iter() {
|
||||
if dashed_lts.contains(&l.line_type) {
|
||||
dashed_layers.insert(l.name.clone(), l.line_type.clone());
|
||||
}
|
||||
}
|
||||
println!(" layers using a dashed linetype:");
|
||||
if dashed_layers.is_empty() {
|
||||
println!(" (none)");
|
||||
}
|
||||
for (n, lt) in &dashed_layers {
|
||||
println!(" \"{}\" → \"{}\"", n, lt);
|
||||
}
|
||||
println!();
|
||||
|
||||
// Per-entity explicit dashed override (lt field non-empty and non-byLayer/byBlock/Continuous).
|
||||
let mut explicit_dashed: u32 = 0;
|
||||
let mut by_layer_in_dashed_layer: u32 = 0;
|
||||
let mut by_layer_in_continuous: u32 = 0;
|
||||
let mut by_kind: BTreeMap<&'static str, u32> = BTreeMap::new();
|
||||
let mut sample_explicit: Vec<String> = vec![];
|
||||
let mut sample_layer_inh: Vec<String> = vec![];
|
||||
|
||||
for e in doc.entities() {
|
||||
let c = e.common();
|
||||
let kind: &'static str = match e {
|
||||
EntityType::Line(_) => "Line",
|
||||
EntityType::LwPolyline(_) => "LwPolyline",
|
||||
EntityType::Polyline(_) => "Polyline",
|
||||
EntityType::Polyline2D(_) => "Polyline2D",
|
||||
EntityType::Arc(_) => "Arc",
|
||||
EntityType::Circle(_) => "Circle",
|
||||
EntityType::Spline(_) => "Spline",
|
||||
EntityType::Ellipse(_) => "Ellipse",
|
||||
EntityType::Insert(_) => "Insert",
|
||||
_ => "Other",
|
||||
};
|
||||
let lt = c.linetype.trim();
|
||||
let lt_norm = lt.to_ascii_lowercase();
|
||||
let is_bylayer = lt.is_empty() || lt_norm == "bylayer";
|
||||
let is_explicit_dashed = !is_bylayer
|
||||
&& lt_norm != "byblock"
|
||||
&& lt_norm != "continuous"
|
||||
&& dashed_lts.iter().any(|d| d.eq_ignore_ascii_case(lt));
|
||||
if is_explicit_dashed {
|
||||
explicit_dashed += 1;
|
||||
*by_kind.entry(kind).or_default() += 1;
|
||||
if sample_explicit.len() < 6 {
|
||||
sample_explicit.push(format!(
|
||||
" [{:?}] {} layer=\"{}\" lt=\"{}\" lt_scale={:.4}",
|
||||
c.handle, kind, c.layer, lt, c.linetype_scale
|
||||
));
|
||||
}
|
||||
} else if is_bylayer && dashed_layers.contains_key(&c.layer) {
|
||||
by_layer_in_dashed_layer += 1;
|
||||
if sample_layer_inh.len() < 6 {
|
||||
sample_layer_inh.push(format!(
|
||||
" [{:?}] {} layer=\"{}\" (inherits \"{}\") lt_scale={:.4}",
|
||||
c.handle,
|
||||
kind,
|
||||
c.layer,
|
||||
dashed_layers.get(&c.layer).unwrap(),
|
||||
c.linetype_scale
|
||||
));
|
||||
}
|
||||
} else if is_bylayer {
|
||||
by_layer_in_continuous += 1;
|
||||
}
|
||||
}
|
||||
|
||||
println!("─── entities w/ explicit dashed lt ──────");
|
||||
println!(" total: {}", explicit_dashed);
|
||||
for (k, v) in &by_kind {
|
||||
println!(" {}: {}", k, v);
|
||||
}
|
||||
for s in &sample_explicit {
|
||||
println!("{}", s);
|
||||
}
|
||||
println!();
|
||||
|
||||
println!("─── entities inheriting dashed via layer:");
|
||||
println!(" total: {}", by_layer_in_dashed_layer);
|
||||
for s in &sample_layer_inh {
|
||||
println!("{}", s);
|
||||
}
|
||||
println!();
|
||||
println!(
|
||||
"─── entities bylayer/continuous: {}",
|
||||
by_layer_in_continuous
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -1987,6 +1987,12 @@ impl Scene {
|
|||
out.points = clipped;
|
||||
out.color = [r * 0.80, g * 0.80, b * 0.80, a * 0.85];
|
||||
out.line_weight_px = wire.line_weight_px;
|
||||
// Wire's pattern was sized for model-space coords during
|
||||
// tessellation; we just projected points into paper coords
|
||||
// (× scale), so rescale the dash pattern by the same factor
|
||||
// to keep dimensional consistency in the GPU shader.
|
||||
out.pattern_length = wire.pattern_length * scale;
|
||||
out.pattern = wire.pattern.map(|v| v * scale);
|
||||
out.vp_scissor = Some([vp_x0, vp_y0, vp_x1, vp_y1]);
|
||||
projected.push(out);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue