forked from 1b-agent/opencadstudio-upstream
SWEEP: sweep from the picked path end; kernel run faces and placed ends
- An open path is swept from its end nearest the pick point (in plan), as the reference does; the record keeps the original path and the placed frame marks the start. - Kernel from ramox81/cadkernel 1df75f5: one face per path run for twisted, scaled and spline sweeps, placed records swept from the placed end, end caps without pcurves. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
41d93b6f54
commit
0c3ca96822
4 files changed
+56
-1
No files matched your search
@@ -181,3 +181,7 @@ harness = false
|
||||
[[test]]
|
||||
name = "integration"
|
||||
path = "tests/integration.rs"
|
||||
|
||||
[patch."https://github.com/HakanSeven12/opencadkernel.git"]
|
||||
opencadkernel = { git = "https://github.com/ramox81/cadkernel.git", rev = "1df75f5" }
|
||||
opencadkernel-constraints = { git = "https://github.com/ramox81/cadkernel.git", rev = "1df75f5" }
|
||||
@@ -1356,6 +1356,9 @@ pub struct SweepOptions {
|
||||
pub scale: f64,
|
||||
/// Total twist along the path, in radians.
|
||||
pub twist_angle: f64,
|
||||
/// Where the path was picked: an open path is swept from its end nearest
|
||||
/// this point (seen from above), as the reference does.
|
||||
pub path_pick: Option<DVec3>,
|
||||
}
|
||||
|
||||
impl Default for SweepOptions {
|
||||
@@ -1366,6 +1369,7 @@ impl Default for SweepOptions {
|
||||
base_point: None,
|
||||
scale: 1.0,
|
||||
twist_angle: 0.0,
|
||||
path_pick: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1822,7 +1822,7 @@ impl CadCommand for SweepCommand {
|
||||
self.step == SweepStep::PickPath
|
||||
}
|
||||
|
||||
fn on_entity_pick(&mut self, handle: Handle, _pt: DVec3) -> CmdResult {
|
||||
fn on_entity_pick(&mut self, handle: Handle, pt: DVec3) -> CmdResult {
|
||||
if self.step != SweepStep::PickPath || handle.is_null() || self.contains_profile(handle) {
|
||||
self.injected_path = None;
|
||||
return CmdResult::NeedPoint;
|
||||
@@ -1835,6 +1835,7 @@ impl CadCommand for SweepCommand {
|
||||
let Some(path) = path.filter(crate::scene::model::sweep_model::is_sweep_path) else {
|
||||
return CmdResult::NeedPoint;
|
||||
};
|
||||
self.options.path_pick = pt.is_finite().then_some(pt);
|
||||
if (self.options.scale - 1.0).abs() > 1e-12 {
|
||||
if let Some(message) = crate::scene::model::sweep_model::scaled_sweep_path_refusal(&path) {
|
||||
return CmdResult::ReportMeasurement(format!(
|
||||
|
||||
@@ -112,7 +112,53 @@ pub fn sweep_selection_options(profiles: &[EntityType], mut options: SweepOption
|
||||
Some(options)
|
||||
}
|
||||
|
||||
/// The path traversed the other way when it was picked nearer its end
|
||||
/// (measured in plan, as the reference measures the pick), else `None`.
|
||||
fn reversed_toward_pick(path: &EntityType, pick: glam::DVec3) -> Option<EntityType> {
|
||||
let (start, end) = match path {
|
||||
EntityType::Polyline3D(value) if !value.is_closed() => {
|
||||
let p = |v: &codec::entities::Vertex3DPolyline| glam::DVec3::new(v.position.x, v.position.y, v.position.z);
|
||||
(p(value.vertices.first()?), p(value.vertices.last()?))
|
||||
}
|
||||
EntityType::Spline(value) if !value.flags.closed => {
|
||||
let points = if value.control_points.is_empty() { &value.fit_points } else { &value.control_points };
|
||||
let p = |v: &codec::types::Vector3| glam::DVec3::new(v.x, v.y, v.z);
|
||||
(p(points.first()?), p(points.last()?))
|
||||
}
|
||||
_ => {
|
||||
let planar = crate::entities::curve::entity_curve(path)?;
|
||||
if planar.curve.is_closed() { return None; }
|
||||
let at = |t: f64| glam::DVec3::from_array(planar.plane.point_at(planar.curve.point_at(t)));
|
||||
(at(0.0), at(1.0))
|
||||
}
|
||||
};
|
||||
let flat = |p: glam::DVec3| p.truncate().distance(pick.truncate());
|
||||
if flat(end) >= flat(start) { return None; }
|
||||
match path {
|
||||
// An arc has one direction; reversed it is a one-segment polyline.
|
||||
EntityType::Arc(arc) => {
|
||||
let mut polyline = codec::LwPolyline::new();
|
||||
let sweep = (arc.end_angle - arc.start_angle).rem_euclid(std::f64::consts::TAU);
|
||||
let at = |angle: f64| codec::types::Vector2::new(arc.center.x + arc.radius * angle.cos(), arc.center.y + arc.radius * angle.sin());
|
||||
let mut first = codec::entities::LwVertex::new(at(arc.end_angle));
|
||||
first.bulge = -(sweep / 4.0).tan();
|
||||
polyline.vertices = vec![first, codec::entities::LwVertex::new(at(arc.start_angle))];
|
||||
polyline.normal = arc.normal;
|
||||
polyline.elevation = arc.center.z;
|
||||
Some(EntityType::LwPolyline(polyline))
|
||||
}
|
||||
_ => crate::modules::draw::modify::reverse::ReverseCommand::reversed(path),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sweep_record(profile: &EntityType, path: &EntityType, options: SweepOptions) -> Option<SolidHistorySweep> {
|
||||
// Placed from the picked end; the record keeps the original path, and
|
||||
// the placed frame tells which end the sweep starts from.
|
||||
if let Some(reversed) = options.path_pick.and_then(|pick| reversed_toward_pick(path, pick)) {
|
||||
let mut record = sweep_record(profile, &reversed, SweepOptions { path_pick: None, ..options })?;
|
||||
record.path_entity = Some(embedded_sweep_path(path)?);
|
||||
return Some(record);
|
||||
}
|
||||
let (sweep_entity, sweep_entity_transform) = embedded_sweep_profile(profile)?;
|
||||
let (plane, wires, _) = kernel::acis::sweep_profile_geometry(&sweep_entity, sweep_entity_transform).ok()?;
|
||||
let base_point = match options.base_point {
|
||||
|
||||
Reference in new issue
Block a user