forked from 1b-agent/opencadstudio-upstream
fix(solid): normalize quadrilateral edges
Accept perimeter and DXF Z point orders while keeping screen fill and interaction on the WCS-aware geometry. Refs #617
This commit is contained in:
1 parent
6c62daf260
commit
7b50d6a5a3
5 files changed
+151
-70
No files matched your search
@@ -2,10 +2,10 @@
|
||||
//
|
||||
// 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. Points are taken in the order picked: the
|
||||
// third point is the corner diagonally opposite the second, so a four-point
|
||||
// solid is entered in a Z pattern (entering the corners in ring order yields
|
||||
// the classic bow-tie fill). Enter after the third point commits a triangle.
|
||||
// filled triangle or quadrilateral. Four points may be picked either around
|
||||
// the perimeter (the natural order shown by the rubber band) or in AutoCAD's
|
||||
// traditional Z pattern. The command normalizes both forms to the DXF corner
|
||||
// order before committing. Enter after the third point commits a triangle.
|
||||
|
||||
use acadrust::entities::Solid;
|
||||
use acadrust::types::Vector3;
|
||||
@@ -43,6 +43,45 @@ impl Solid2dCommand {
|
||||
fn v3(p: DVec3) -> Vector3 {
|
||||
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),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl CadCommand for Solid2dCommand {
|
||||
@@ -72,13 +111,7 @@ impl CadCommand for Solid2dCommand {
|
||||
fn on_point(&mut self, pt: DVec3) -> CmdResult {
|
||||
self.points.push(pt);
|
||||
if self.points.len() == 4 {
|
||||
let p = &self.points;
|
||||
let solid = Solid::new(
|
||||
Self::v3(p[0]),
|
||||
Self::v3(p[1]),
|
||||
Self::v3(p[2]),
|
||||
Self::v3(p[3]),
|
||||
);
|
||||
let solid = Self::solid_from_four_points(&self.points);
|
||||
CmdResult::CommitAndExit(EntityType::Solid(solid))
|
||||
} else {
|
||||
CmdResult::NeedPoint
|
||||
@@ -105,9 +138,13 @@ impl CadCommand for Solid2dCommand {
|
||||
}
|
||||
// Outline the corners picked so far plus the cursor, closed back to the
|
||||
// first point, as a rubber-band hint.
|
||||
let mut pts: Vec<[f64; 3]> = self.points.iter().map(|p| [p.x, p.y, p.z]).collect();
|
||||
pts.push([pt.x, pt.y, pt.z]);
|
||||
pts.push([self.points[0].x, self.points[0].y, self.points[0].z]);
|
||||
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();
|
||||
pts.push([preview[0].x, preview[0].y, preview[0].z]);
|
||||
Some(WireModel::solid_f64(
|
||||
"rubber_band".to_string(),
|
||||
pts,
|
||||
|
||||
Reference in new issue
Block a user