Complete 2D solid workflow

This commit is contained in:
ramox81 committed 2026-08-21 10:14:26 +03:00
1 parent 573ed3245e
commit 17dc0aff39
11 files changed
+257 -181

No files matched your search

+25 -45
View File
@@ -1,11 +1,10 @@
// 2D solid tool — interactive command.
//
// Command: SOLID (reachable as SO / SOLID2D — the bare SOLID verb currently
// toggles the shaded display) — pick three or four corner points and commit a
// filled triangle or quadrilateral. Four points may be picked either around
// the perimeter (the natural order shown by the rubber band) or in the
// traditional Z pattern. The command normalizes both forms to the DXF corner
// order before committing. Enter after the third point commits a triangle.
// Command: SOLID (reachable as SO / SOLID2D) — pick three or four corner points
// and commit a filled triangle or quadrilateral. Four-point input is preserved
// in the entity's documented Z order; after a quadrilateral, its opposite edge
// starts the next connected shape. Enter after the third point commits a
// triangle.
use acadrust::entities::Solid;
use acadrust::types::Vector3;
@@ -49,42 +48,12 @@ impl Solid2dCommand {
Vector3::new(p.x, p.y, p.z)
}
/// True when walking the four points in pick order produces a bow-tie.
/// SOLID2D currently commits into the default world-XY OCS, so use that
/// same plane when deciding whether the last two points need swapping.
fn pick_order_crosses(points: &[DVec3]) -> bool {
if points.len() != 4 {
return false;
}
let orient = |a: DVec3, b: DVec3, c: DVec3| {
(b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x)
};
let crosses = |a: DVec3, b: DVec3, c: DVec3, d: DVec3| {
let ab_c = orient(a, b, c);
let ab_d = orient(a, b, d);
let cd_a = orient(c, d, a);
let cd_b = orient(c, d, b);
((ab_c > 0.0 && ab_d < 0.0) || (ab_c < 0.0 && ab_d > 0.0))
&& ((cd_a > 0.0 && cd_b < 0.0) || (cd_a < 0.0 && cd_b > 0.0))
};
crosses(points[0], points[1], points[2], points[3])
|| crosses(points[1], points[2], points[3], points[0])
}
/// Convert either perimeter picks (1-2-3-4) or classic SOLID Z picks
/// (1-2-4-3 around the perimeter) into the entity's DXF Z order.
fn solid_from_four_points(points: &[DVec3]) -> Solid {
let perimeter_order = !Self::pick_order_crosses(points);
let (third, fourth) = if perimeter_order {
(points[3], points[2])
} else {
(points[2], points[3])
};
Solid::new(
Self::v3(points[0]),
Self::v3(points[1]),
Self::v3(third),
Self::v3(fourth),
Self::v3(points[2]),
Self::v3(points[3]),
)
}
}
@@ -126,7 +95,10 @@ impl CadCommand for Solid2dCommand {
.map(|point| self.plane.to_local(*point))
.collect();
let solid = Self::solid_from_four_points(&local);
CmdResult::CommitAndExit(self.plane.place_entity(EntityType::Solid(solid)))
let next_edge = [self.points[2], self.points[3]];
self.points.clear();
self.points.extend(next_edge);
CmdResult::CommitEntity(self.plane.place_entity(EntityType::Solid(solid)))
} else {
CmdResult::NeedPoint
}
@@ -154,14 +126,22 @@ impl CadCommand for Solid2dCommand {
if self.points.is_empty() {
return None;
}
// Outline the corners picked so far plus the cursor, closed back to the
// first point, as a rubber-band hint.
// Outline the exact Z-ordered boundary that would be committed. Do not
// reorder a crossing shape: point order is intentional geometry.
let mut preview = self.points.clone();
preview.push(pt);
if preview.len() == 4 && Self::pick_order_crosses(&preview) {
preview.swap(2, 3);
}
let mut pts: Vec<[f64; 3]> = preview.iter().map(|p| [p.x, p.y, p.z]).collect();
let display_order: Vec<usize> = if preview.len() == 4 {
vec![0, 1, 3, 2]
} else {
(0..preview.len()).collect()
};
let mut pts: Vec<[f64; 3]> = display_order
.iter()
.map(|index| {
let point = preview[*index];
[point.x, point.y, point.z]
})
.collect();
pts.push([preview[0].x, preview[0].y, preview[0].z]);
Some(WireModel::solid_f64(
"rubber_band".to_string(),