fix: solve perpendicular placement in kernel

This commit is contained in:
Hakan Seven committed 2026-09-17 17:08:49 +03:00
1 parent b546f9b161
commit b6478b3b89
5 files changed
+61 -88

No files matched your search

Generated
+2 -2
View File
@@ -921,7 +921,7 @@ checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04"
[[package]]
name = "cadkernel"
version = "0.1.0"
source = "git+https://github.com/HakanSeven12/cadkernel.git?rev=35aa7a5#35aa7a541ab7c020cd23f43978373337523e25df"
source = "git+https://github.com/HakanSeven12/cadkernel.git?rev=7e43598#7e435984d9ba32999d2471930c18449a2ab58d67"
dependencies = [
"acadrust",
"cavalier_contours",
@@ -932,7 +932,7 @@ dependencies = [
[[package]]
name = "cadkernel-constraints"
version = "0.1.0"
source = "git+https://github.com/HakanSeven12/cadkernel.git?rev=35aa7a5#35aa7a541ab7c020cd23f43978373337523e25df"
source = "git+https://github.com/HakanSeven12/cadkernel.git?rev=7e43598#7e435984d9ba32999d2471930c18449a2ab58d67"
dependencies = [
"nalgebra",
]
+2 -2
View File
@@ -34,8 +34,8 @@ clap = { version = "4", features = ["derive", "string"] }
env_logger = "0.11"
log = "0.4"
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "8a28c21", features = ["serde"] }
cadkernel = { git = "https://github.com/HakanSeven12/cadkernel.git", rev = "35aa7a5", features = ["acis", "offset"] }
cadkernel-constraints = { git = "https://github.com/HakanSeven12/cadkernel.git", rev = "35aa7a5" }
cadkernel = { git = "https://github.com/HakanSeven12/cadkernel.git", rev = "7e43598", features = ["acis", "offset"] }
cadkernel-constraints = { git = "https://github.com/HakanSeven12/cadkernel.git", rev = "7e43598" }
dwg-thumbnailer = { path = "crates/dwg-thumbnailer" }
flate2 = "1"
image = { version = "0.25", default-features = false, features = ["png", "jpeg", "bmp", "tiff"] }
+35 -57
View File
@@ -3266,62 +3266,6 @@ impl OpenCADStudio {
self.tabs[i]
.scene
.record_undo_parametric_constraints_before(scope, constraints_before);
if first.entity != second.entity {
use crate::scene::parametric_constraints::directional_reference_endpoints;
let fixed_axis = self.tabs[i]
.scene
.document
.get_entity(first.entity)
.and_then(|entity| directional_reference_endpoints(entity, first));
let moving_axis = self.tabs[i]
.scene
.document
.get_entity(second.entity)
.and_then(|entity| directional_reference_endpoints(entity, second));
if let (Some([fixed_start, fixed_end]), Some([moving_start, moving_end])) =
(fixed_axis, moving_axis)
{
let fixed_direction = fixed_end - fixed_start;
let moving_direction = moving_end - moving_start;
if fixed_direction.length_squared() > 1.0e-24
&& moving_direction.length_squared() > 1.0e-24
{
let fixed_angle = fixed_direction.y.atan2(fixed_direction.x);
let moving_angle = moving_direction.y.atan2(moving_direction.x);
let normalize = |angle: f64| {
(angle + std::f64::consts::PI)
.rem_euclid(std::f64::consts::TAU)
- std::f64::consts::PI
};
let positive = normalize(
fixed_angle + std::f64::consts::FRAC_PI_2 - moving_angle,
);
let negative = normalize(
fixed_angle - std::f64::consts::FRAC_PI_2 - moving_angle,
);
let angle_rad = if positive.abs() <= negative.abs() {
positive
} else {
negative
};
if angle_rad.abs() > 1.0e-12 {
self.tabs[i].scene.transform_entities(
&[second.entity],
&crate::command::EntityTransform::Rotate {
center: glam::DVec3::new(
moving_start.x,
moving_start.y,
moving_start.z,
),
axis: glam::DVec3::Z,
angle_rad,
},
);
}
}
}
}
let id = self.tabs[i].scene.parametric_constraint_set_mut(scope).add(
ConstraintKind::Perpendicular,
refs,
@@ -3336,7 +3280,7 @@ impl OpenCADStudio {
.into_iter()
.map(|handle| (handle, crate::scene::ChangeKind::Modified))
.collect::<Vec<_>>();
self.tabs[i].scene.bump_entities_with_parametric_policy(
self.tabs[i].scene.bump_entities_with_initial_parametric_policy(
&changes,
&[first_fixed, second_start],
true,
@@ -8506,6 +8450,40 @@ mod parametric_constraint_undo_tests {
);
}
#[test]
fn perpendicular_initial_solve_rotates_parallel_second_line_in_kernel() {
let mut app = OpenCADStudio::new_for_test();
let _ = app.automation_op(r#"{"op":"new"}"#);
let first = add_line(&mut app, 0.0, 0.0, 10.0, 0.0);
let second = add_line(&mut app, 20.0, 0.0, 30.0, 0.0);
let _ = app.apply_cmd_result(CmdResult::AddPerpendicularConstraint {
first: ParametricRef::whole(first),
second: ParametricRef::whole(second),
first_fixed: ParametricRef::whole(first),
second_start: ParametricRef::point(second, 0),
label: "Perpendicular constraint",
});
let line = |handle| match app.tabs[app.active_tab].scene.document.get_entity(handle) {
Some(acadrust::EntityType::Line(line)) => line.clone(),
other => panic!("expected a Line, got {other:?}"),
};
let fixed = line(first);
let moving = line(second);
assert_eq!(fixed.start, acadrust::types::Vector3::new(0.0, 0.0, 0.0));
assert_eq!(fixed.end, acadrust::types::Vector3::new(10.0, 0.0, 0.0));
assert!((moving.start - acadrust::types::Vector3::new(20.0, 0.0, 0.0)).length() < 1.0e-9);
assert!((moving.length() - 10.0).abs() < 1.0e-7);
assert!(
(fixed.end - fixed.start)
.dot(&(moving.end - moving.start))
.abs()
< 1.0e-7,
"fixed={fixed:?}, moving={moving:?}"
);
}
/// Drawing commands must not create parametric constraints implicitly.
#[test]
fn drawing_a_line_onto_an_existing_endpoint_does_not_add_a_constraint() {
+22
View File
@@ -234,4 +234,26 @@ mod tests {
assert_eq!(first_fixed, ParametricRef::whole(first));
assert_eq!(second_start, ParametricRef::point(second, 0));
}
#[test]
fn preselection_uses_the_first_straight_polyline_segment() {
let handle = Handle::new(11);
let mut polyline = acadrust::entities::LwPolyline::new();
polyline.vertices = vec![
acadrust::entities::LwVertex::with_bulge(
acadrust::types::Vector2::new(0.0, 0.0),
1.0,
),
acadrust::entities::LwVertex::from_coords(4.0, 0.0),
acadrust::entities::LwVertex::from_coords(8.0, 0.0),
];
let pick = PerpendicularConstraintCommand::preselected_reference(
&EntityType::LwPolyline(polyline),
handle,
)
.expect("straight segment");
assert_eq!(pick.reference, ParametricRef::segment(handle, 1));
assert_eq!(pick.start_reference, ParametricRef::point(handle, 1));
}
}
-27
View File
@@ -186,33 +186,6 @@ pub(crate) fn directional_axis_endpoints(
}
}
/// Finite endpoints used to orient a line-like constraint reference.
pub(crate) fn directional_reference_endpoints(
entity: &acadrust::EntityType,
reference: ParametricRef,
) -> Option<[Vector3; 2]> {
if reference.directional_axis().is_some() {
return directional_axis_endpoints(entity, reference);
}
match entity {
acadrust::EntityType::Line(line) if reference.marker.is_none() => {
Some([line.start, line.end])
}
acadrust::EntityType::LwPolyline(_) | acadrust::EntityType::Polyline2D(_) => {
let index = reference.segment_index()?;
let points = super::dimension_assoc::source_points(entity);
let start = *points.get(index)?;
let end = if let Some(end) = points.get(index + 1) {
*end
} else {
*points.first()?
};
Some([start, end])
}
_ => None,
}
}
/// Grabbed points are exact kernel inputs; the solver anchors the remaining
/// endpoint coordinates according to the line's directional constraints.
pub(crate) fn grip_solve_anchor_refs(