feat(properties): fill placeholder rows from acadrust fields + doc lookups

Bump acadrust to a46cb8b (spline knot parameterization + mtext line-space
style now stored) and populate rows the builders left blank:

Entity-local: spline Knot Parameterization + closed-spline Area (shoelace),
mtext Line space style, hatch Spacing (pattern-line offset), leader Text
offset (annotation offset), region Area + Perimeter (wire-loop approx).

Doc-dependent (resolved in app/properties.rs where the document is
reachable): insert Block Unit + Unit factor (block record units vs
INSUNITS), underlay Name + Path (definition object), leader Text style /
vertical placement / overall scale and tolerance Text style (dimension
style), multileader Max points + segment angles (mleader style).

Rows needing an annotation-scale / sheet-set subsystem or an internal
PDF/DWF layer parser are intentionally left blank.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
Hakan SevenandClaude Opus 4.8 committed 2026-07-03 00:05:58 +03:00
1 parent 0a9c27e39d
commit f50ad0dde4
7 files changed
+258 -8

No files matched your search

+9 -1
View File
@@ -150,7 +150,15 @@ fn properties(h: &Hatch) -> Vec<PropSection> {
),
ro("Layer override", "layer_override", String::new()),
ro("Double", "double", if h.is_double { "Yes" } else { "No" }),
ro("Spacing", "spacing", String::new()),
ro(
"Spacing",
"spacing",
h.pattern
.lines
.first()
.map(|l| format!("{:.4}", l.offset.length()))
.unwrap_or_default(),
),
ro("ISO pen width", "iso_pen_width", String::new()),
ro("Gradient colors", "gradient_colors", g.colors.len().to_string()),
ro("Gradient tint", "gradient_tint", format!("{:.4}", g.color_tint)),
+5 -1
View File
@@ -231,7 +231,11 @@ fn properties(leader: &Leader) -> Vec<PropSection> {
let text = vec![
edit("Text height", "text_height", leader.text_height),
ro("Text offset", "text_offset", String::new()),
ro(
"Text offset",
"text_offset",
format!("{:.4}", leader.annotation_offset.length()),
),
ro("Text style", "text_style", String::new()),
ro("Text color", "text_color", color_str(&leader.override_color)),
ro("Text position vert", "text_pos_vert", String::new()),
+8 -1
View File
@@ -272,7 +272,14 @@ fn properties(t: &MText, text_style_names: &[String]) -> Vec<PropSection> {
"line_space_distance",
format!("{line_space_distance:.4}"),
),
ro("Line space style", "line_space_style", String::new()),
ro(
"Line space style",
"line_space_style",
match t.line_spacing_style {
acadrust::entities::LineSpacingStyle::Exactly => "Exactly",
_ => "At least",
},
),
ro(
"Background mask",
"background_mask",
+36 -2
View File
@@ -52,6 +52,39 @@ fn translate_wires(wires: &mut Vec<acadrust::entities::Wire>, d: Vec3) {
}
}
/// Approximate a region's enclosed area and boundary perimeter from its
/// wireframe loops. Perimeter is the total edge length across every wire.
/// Area accumulates the Newell area vector of each loop (opposite-wound
/// holes subtract) and halves its magnitude — exact for a single planar
/// loop, approximate for multi-loop or curved regions. Returns zeros when
/// there is nothing to measure.
fn region_area_perimeter(wires: &[acadrust::entities::Wire]) -> (f64, f64) {
let mut perimeter = 0.0;
let (mut nx, mut ny, mut nz) = (0.0, 0.0, 0.0);
for wire in wires {
let pts = &wire.points;
if pts.len() < 2 {
continue;
}
for seg in pts.windows(2) {
let dx = seg[1].x - seg[0].x;
let dy = seg[1].y - seg[0].y;
let dz = seg[1].z - seg[0].z;
perimeter += (dx * dx + dy * dy + dz * dz).sqrt();
}
let n = pts.len();
for i in 0..n {
let a = &pts[i];
let b = &pts[(i + 1) % n];
nx += (a.y - b.y) * (a.z + b.z);
ny += (a.z - b.z) * (a.x + b.x);
nz += (a.x - b.x) * (a.y + b.y);
}
}
let area = 0.5 * (nx * nx + ny * ny + nz * nz).sqrt();
(area, perimeter)
}
// ── Solid3D ───────────────────────────────────────────────────────────────────
impl Grippable for Solid3D {
@@ -132,11 +165,12 @@ impl Grippable for Region {
impl PropertyEditable for Region {
fn geometry_properties(&self, _text_style_names: &[String]) -> Vec<PropSection> {
let (area, perimeter) = region_area_perimeter(&self.wires);
vec![PropSection {
title: "Geometry".into(),
props: vec![
ro("Area", "rgn_area", String::new()),
ro("Perimeter", "rgn_perimeter", String::new()),
ro("Area", "rgn_area", format!("{area:.4}")),
ro("Perimeter", "rgn_perimeter", format!("{perimeter:.4}")),
],
}]
}
+26 -2
View File
@@ -133,6 +133,30 @@ fn properties(spline: &Spline) -> Vec<PropSection> {
let closed = spline.flags.closed || spline.flags.periodic;
let yes_no = |b: bool| if b { "Yes" } else { "No" };
// Knot parameterization method (R2013+ DWG); older splines report 0.
let knot_param = match spline.knot_parameterization {
0 => "Chord",
1 => "Square Root",
2 => "Uniform",
_ => "Custom",
};
// Closed splines enclose an area; approximate it with the shoelace
// formula over the defining points projected to the XY plane, matching
// the polyline approximation already used for Length.
let area = if closed && pts.len() >= 3 {
let mut acc = 0.0;
for w in pts.windows(2) {
acc += w[0].x * w[1].y - w[1].x * w[0].y;
}
if let (Some(first), Some(last)) = (pts.first(), pts.last()) {
acc += last.x * first.y - first.x * last.y;
}
format!("{:.4}", acc.abs() * 0.5)
} else {
String::new()
};
vec![
PropSection {
title: "Data Points".into(),
@@ -161,7 +185,7 @@ fn properties(spline: &Spline) -> Vec<PropSection> {
format!("{:.4}", cp0.map(|p| p.z).unwrap_or(0.0)),
),
ro("Weight", "weight", format!("{:.4}", w0.unwrap_or(1.0))),
ro("Knot Parameterization", "knot_param", String::new()),
ro("Knot Parameterization", "knot_param", knot_param),
ro(
"Fit Point Count",
"fit_pt_count",
@@ -198,7 +222,7 @@ fn properties(spline: &Spline) -> Vec<PropSection> {
ro("Closed", "closed", yes_no(closed)),
ro("Planar", "planar", yes_no(spline.flags.planar)),
ro("Length", "length", format!("{length:.4}")),
ro("Area", "area", String::new()),
ro("Area", "area", area),
],
},
]