forked from 1b-agent/opencadstudio-upstream
fix: correct arc sweep direction for reversed normals and mirrored blocks
- Arc rendering now respects the normal vector: when normal.z < 0 the midpoint is placed on the clockwise side so the curve sweeps the right way - Removed spurious .to_radians() / .to_degrees() round-trips in arc.rs (angles are already stored in the unit the trig functions expect) - apply_grip and apply_transform use consistent angle units - normalize_insert_entity no longer converts arc angles; the old conversion was only needed by the now-removed .to_radians() call in to_truck - Added fix_mirrored_arc: swaps arc start/end angles when the INSERT has a mirrored scale (x_scale * y_scale < 0) so exploded arcs curve correctly Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
This commit is contained in:
1 parent
a255b99863
commit
e2f5715de8
3 files changed
+47
-30
No files matched your search
+20
-16
@@ -16,13 +16,17 @@ fn to_truck(arc: &Arc) -> TruckEntity {
|
|||||||
let cy = arc.center.y;
|
let cy = arc.center.y;
|
||||||
let cz = arc.center.z;
|
let cz = arc.center.z;
|
||||||
let r = arc.radius;
|
let r = arc.radius;
|
||||||
let sa = arc.start_angle.to_radians();
|
let sa = arc.start_angle;
|
||||||
let ea = arc.end_angle.to_radians();
|
let ea = arc.end_angle;
|
||||||
let mut end = ea;
|
// normal.z < 0 means the arc sweeps clockwise in the XY plane;
|
||||||
if end < sa {
|
// place the midpoint on the CW side so the curve goes the right way.
|
||||||
end += TAU;
|
let mid_a = if arc.normal.z < 0.0 {
|
||||||
}
|
let cw_span = if sa >= ea { sa - ea } else { sa - ea + TAU };
|
||||||
let mid_a = sa + (end - sa) * 0.5;
|
sa - cw_span * 0.5
|
||||||
|
} else {
|
||||||
|
let ccw_end = if ea >= sa { ea } else { ea + TAU };
|
||||||
|
sa + (ccw_end - sa) * 0.5
|
||||||
|
};
|
||||||
let p_start = Point3::new(cx + r * sa.cos(), cy + r * sa.sin(), cz);
|
let p_start = Point3::new(cx + r * sa.cos(), cy + r * sa.sin(), cz);
|
||||||
let p_end = Point3::new(cx + r * ea.cos(), cy + r * ea.sin(), cz);
|
let p_end = Point3::new(cx + r * ea.cos(), cy + r * ea.sin(), cz);
|
||||||
let p_mid = Point3::new(cx + r * mid_a.cos(), cy + r * mid_a.sin(), cz);
|
let p_mid = Point3::new(cx + r * mid_a.cos(), cy + r * mid_a.sin(), cz);
|
||||||
@@ -56,8 +60,8 @@ fn grips(arc: &Arc) -> Vec<GripDef> {
|
|||||||
arc.center.z as f32,
|
arc.center.z as f32,
|
||||||
);
|
);
|
||||||
let r = arc.radius as f32;
|
let r = arc.radius as f32;
|
||||||
let sa = (arc.start_angle as f32).to_radians();
|
let sa = arc.start_angle as f32;
|
||||||
let ea = (arc.end_angle as f32).to_radians();
|
let ea = arc.end_angle as f32;
|
||||||
let ma = sa + angle_span(sa, ea) * 0.5;
|
let ma = sa + angle_span(sa, ea) * 0.5;
|
||||||
vec![
|
vec![
|
||||||
diamond_grip(0, ctr),
|
diamond_grip(0, ctr),
|
||||||
@@ -111,16 +115,16 @@ fn apply_grip(arc: &mut Arc, grip_id: usize, apply: GripApply) {
|
|||||||
(1, GripApply::Absolute(p)) => {
|
(1, GripApply::Absolute(p)) => {
|
||||||
let dx = p.x - arc.center.x as f32;
|
let dx = p.x - arc.center.x as f32;
|
||||||
let dy = p.y - arc.center.y as f32;
|
let dy = p.y - arc.center.y as f32;
|
||||||
arc.start_angle = (dy as f64).atan2(dx as f64).to_degrees();
|
arc.start_angle = (dy as f64).atan2(dx as f64);
|
||||||
}
|
}
|
||||||
(2, GripApply::Absolute(p)) => {
|
(2, GripApply::Absolute(p)) => {
|
||||||
let dx = p.x - arc.center.x as f32;
|
let dx = p.x - arc.center.x as f32;
|
||||||
let dy = p.y - arc.center.y as f32;
|
let dy = p.y - arc.center.y as f32;
|
||||||
arc.end_angle = (dy as f64).atan2(dx as f64).to_degrees();
|
arc.end_angle = (dy as f64).atan2(dx as f64);
|
||||||
}
|
}
|
||||||
(3, GripApply::Translate(d)) => {
|
(3, GripApply::Translate(d)) => {
|
||||||
let sa = (arc.start_angle as f32).to_radians();
|
let sa = arc.start_angle as f32;
|
||||||
let ea = (arc.end_angle as f32).to_radians();
|
let ea = arc.end_angle as f32;
|
||||||
let span = angle_span(sa, ea);
|
let span = angle_span(sa, ea);
|
||||||
let mid_a = sa + span * 0.5;
|
let mid_a = sa + span * 0.5;
|
||||||
let current_mid_x = arc.center.x as f32 + arc.radius as f32 * mid_a.cos();
|
let current_mid_x = arc.center.x as f32 + arc.radius as f32 * mid_a.cos();
|
||||||
@@ -148,10 +152,10 @@ fn apply_transform(arc: &mut Arc, t: &EntityTransform) {
|
|||||||
);
|
);
|
||||||
let dx = (p2.x - p1.x) as f64;
|
let dx = (p2.x - p1.x) as f64;
|
||||||
let dy = (p2.y - p1.y) as f64;
|
let dy = (p2.y - p1.y) as f64;
|
||||||
let line_angle_deg = dy.atan2(dx).to_degrees();
|
let line_angle = dy.atan2(dx);
|
||||||
let tmp = entity.start_angle;
|
let tmp = entity.start_angle;
|
||||||
entity.start_angle = 2.0 * line_angle_deg - entity.end_angle;
|
entity.start_angle = 2.0 * line_angle - entity.end_angle;
|
||||||
entity.end_angle = 2.0 * line_angle_deg - tmp;
|
entity.end_angle = 2.0 * line_angle - tmp;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -49,11 +49,14 @@ pub fn explode_entity(entity: &EntityType, document: &CadDocument) -> Vec<Entity
|
|||||||
EntityType::Polyline2D(p) => explode_polyline2d(p),
|
EntityType::Polyline2D(p) => explode_polyline2d(p),
|
||||||
EntityType::Polyline(p) => explode_polyline(p),
|
EntityType::Polyline(p) => explode_polyline(p),
|
||||||
EntityType::Polyline3D(p) => explode_polyline3d(p),
|
EntityType::Polyline3D(p) => explode_polyline3d(p),
|
||||||
EntityType::Insert(ins) => ins
|
EntityType::Insert(ins) => {
|
||||||
.explode_from_document(document)
|
let is_mirrored = ins.x_scale() * ins.y_scale() < 0.0;
|
||||||
.into_iter()
|
ins.explode_from_document(document)
|
||||||
.map(normalize_insert_entity)
|
.into_iter()
|
||||||
.collect(),
|
.map(normalize_insert_entity)
|
||||||
|
.map(|sub| fix_mirrored_arc(sub, is_mirrored))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
EntityType::MLine(ml) => explode_mline(ml),
|
EntityType::MLine(ml) => explode_mline(ml),
|
||||||
EntityType::Dimension(dim) => explode_dimension(dim),
|
EntityType::Dimension(dim) => explode_dimension(dim),
|
||||||
_ => vec![],
|
_ => vec![],
|
||||||
@@ -140,10 +143,6 @@ fn explode_polyline2d(p: &Polyline2D) -> Vec<EntityType> {
|
|||||||
|
|
||||||
pub fn normalize_insert_entity(mut entity: EntityType) -> EntityType {
|
pub fn normalize_insert_entity(mut entity: EntityType) -> EntityType {
|
||||||
match &mut entity {
|
match &mut entity {
|
||||||
EntityType::Arc(arc) => {
|
|
||||||
arc.start_angle = arc.start_angle.to_degrees();
|
|
||||||
arc.end_angle = arc.end_angle.to_degrees();
|
|
||||||
}
|
|
||||||
EntityType::Ellipse(ell) => {
|
EntityType::Ellipse(ell) => {
|
||||||
let major_len = ell.major_axis_length();
|
let major_len = ell.major_axis_length();
|
||||||
let full_span = {
|
let full_span = {
|
||||||
@@ -170,14 +169,26 @@ pub fn normalize_insert_entity(mut entity: EntityType) -> EntityType {
|
|||||||
entity
|
entity
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn normalize_entity_for_block(mut entity: EntityType) -> EntityType {
|
pub fn normalize_entity_for_block(entity: EntityType) -> EntityType {
|
||||||
if let EntityType::Arc(arc) = &mut entity {
|
|
||||||
arc.start_angle = arc.start_angle.to_radians();
|
|
||||||
arc.end_angle = arc.end_angle.to_radians();
|
|
||||||
}
|
|
||||||
entity
|
entity
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Swap arc start/end angles when the INSERT that produced the entity was
|
||||||
|
/// mirrored (exactly one of x_scale / y_scale is negative). The acadrust
|
||||||
|
/// explode recalculates endpoint positions correctly but does not reverse the
|
||||||
|
/// sweep direction, so the arc would otherwise curve the wrong way.
|
||||||
|
pub fn fix_mirrored_arc(entity: EntityType, is_mirrored: bool) -> EntityType {
|
||||||
|
if !is_mirrored {
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
if let EntityType::Arc(mut arc) = entity {
|
||||||
|
std::mem::swap(&mut arc.start_angle, &mut arc.end_angle);
|
||||||
|
EntityType::Arc(arc)
|
||||||
|
} else {
|
||||||
|
entity
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn explode_lwpolyline(p: &LwPolyline) -> Vec<EntityType> {
|
fn explode_lwpolyline(p: &LwPolyline) -> Vec<EntityType> {
|
||||||
let n = p.vertices.len();
|
let n = p.vertices.len();
|
||||||
if n < 2 {
|
if n < 2 {
|
||||||
|
|||||||
@@ -518,11 +518,13 @@ impl Scene {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let EntityType::Insert(ins) = e {
|
if let EntityType::Insert(ins) = e {
|
||||||
|
let is_mirrored = ins.x_scale() * ins.y_scale() < 0.0;
|
||||||
return ins
|
return ins
|
||||||
.explode_from_document(&self.document)
|
.explode_from_document(&self.document)
|
||||||
.iter()
|
.iter()
|
||||||
.cloned()
|
.cloned()
|
||||||
.map(crate::modules::home::modify::explode::normalize_insert_entity)
|
.map(crate::modules::home::modify::explode::normalize_insert_entity)
|
||||||
|
.map(|sub| crate::modules::home::modify::explode::fix_mirrored_arc(sub, is_mirrored))
|
||||||
.flat_map(|sub| {
|
.flat_map(|sub| {
|
||||||
let (sub_color, sub_pattern_length, sub_pattern, sub_line_weight_px, sub_aci) =
|
let (sub_color, sub_pattern_length, sub_pattern, sub_line_weight_px, sub_aci) =
|
||||||
self.render_style(&sub);
|
self.render_style(&sub);
|
||||||
|
|||||||
Reference in new issue
Block a user