forked from 1b-agent/opencadstudio-upstream
style: apply cargo fmt across workspace
This commit is contained in:
1 parent
85710e5454
commit
9cf9ff2c5c
531 files changed
+57740
-33906
No files matched your search
+310
-103
@@ -23,44 +23,85 @@ pub struct JoinCommand {
|
||||
handles: Vec<Handle>,
|
||||
}
|
||||
impl JoinCommand {
|
||||
pub fn new() -> Self { Self { source: None, picked: None, handles: Vec::new() } }
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
source: None,
|
||||
picked: None,
|
||||
handles: Vec::new(),
|
||||
}
|
||||
}
|
||||
pub fn with_source(mut self, handle: Handle, entity: EntityType) -> Self {
|
||||
self.picked = Some(entity); self.on_entity_pick(handle, DVec3::ZERO); self
|
||||
self.picked = Some(entity);
|
||||
self.on_entity_pick(handle, DVec3::ZERO);
|
||||
self
|
||||
}
|
||||
fn has_arc_source(&self) -> bool {
|
||||
matches!(&self.source, Some((_, EntityType::Arc(_))))
|
||||
}
|
||||
fn has_arc_source(&self) -> bool { matches!(&self.source, Some((_, EntityType::Arc(_)))) }
|
||||
}
|
||||
impl CadCommand for JoinCommand {
|
||||
fn name(&self) -> &'static str { "JOIN" }
|
||||
fn name(&self) -> &'static str {
|
||||
"JOIN"
|
||||
}
|
||||
fn prompt(&self) -> String {
|
||||
match self.source.as_ref().map(|(_, entity)| entity) {
|
||||
None => "JOIN Select source object:".into(),
|
||||
Some(EntityType::Line(_)) => "JOIN Select lines to join to source (Enter to apply):".into(),
|
||||
Some(EntityType::Arc(_)) => "JOIN Select arcs to join to source or [cLose] (Enter to apply):".into(),
|
||||
Some(EntityType::Spline(_)) => "JOIN Select open curves to join to source (Enter to apply):".into(),
|
||||
Some(EntityType::Line(_)) => {
|
||||
"JOIN Select lines to join to source (Enter to apply):".into()
|
||||
}
|
||||
Some(EntityType::Arc(_)) => {
|
||||
"JOIN Select arcs to join to source or [cLose] (Enter to apply):".into()
|
||||
}
|
||||
Some(EntityType::Spline(_)) => {
|
||||
"JOIN Select open curves to join to source (Enter to apply):".into()
|
||||
}
|
||||
_ => "JOIN Select objects to join to source (Enter to apply):".into(),
|
||||
}
|
||||
}
|
||||
fn options(&self) -> Vec<crate::command::CmdOption> {
|
||||
if matches!(&self.source,Some((_,EntityType::Arc(_)))) { vec![crate::command::CmdOption::new("Close","L")] } else {Vec::new()}
|
||||
if matches!(&self.source, Some((_, EntityType::Arc(_)))) {
|
||||
vec![crate::command::CmdOption::new("Close", "L")]
|
||||
} else {
|
||||
Vec::new()
|
||||
}
|
||||
}
|
||||
fn wants_text_input(&self) -> bool { self.has_arc_source() }
|
||||
fn point_step_accepts_keywords(&self) -> bool { self.has_arc_source() }
|
||||
fn on_text_input(&mut self,text:&str) -> Option<CmdResult> {
|
||||
if matches!(text.trim().to_ascii_uppercase().as_str(),"L"|"CLOSE") {
|
||||
if let Some((handle,EntityType::Arc(arc)))=&self.source {
|
||||
let mut circle=codec::entities::Circle::new();
|
||||
circle.common=arc.common.clone();circle.common.handle=Handle::NULL;
|
||||
circle.center=arc.center.clone();circle.normal=arc.normal.clone();circle.radius=arc.radius;circle.thickness=arc.thickness;
|
||||
return Some(CmdResult::ReplaceMany(vec![(*handle,vec![EntityType::Circle(circle)])],Vec::new()));
|
||||
fn wants_text_input(&self) -> bool {
|
||||
self.has_arc_source()
|
||||
}
|
||||
fn point_step_accepts_keywords(&self) -> bool {
|
||||
self.has_arc_source()
|
||||
}
|
||||
fn on_text_input(&mut self, text: &str) -> Option<CmdResult> {
|
||||
if matches!(text.trim().to_ascii_uppercase().as_str(), "L" | "CLOSE") {
|
||||
if let Some((handle, EntityType::Arc(arc))) = &self.source {
|
||||
let mut circle = codec::entities::Circle::new();
|
||||
circle.common = arc.common.clone();
|
||||
circle.common.handle = Handle::NULL;
|
||||
circle.center = arc.center.clone();
|
||||
circle.normal = arc.normal.clone();
|
||||
circle.radius = arc.radius;
|
||||
circle.thickness = arc.thickness;
|
||||
return Some(CmdResult::ReplaceMany(
|
||||
vec![(*handle, vec![EntityType::Circle(circle)])],
|
||||
Vec::new(),
|
||||
));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
fn needs_entity_pick(&self) -> bool { self.source.is_none() }
|
||||
fn inject_before_entity_pick(&self) -> bool { true }
|
||||
fn inject_picked_entity(&mut self, entity: EntityType) { self.picked = Some(entity); }
|
||||
fn needs_entity_pick(&self) -> bool {
|
||||
self.source.is_none()
|
||||
}
|
||||
fn inject_before_entity_pick(&self) -> bool {
|
||||
true
|
||||
}
|
||||
fn inject_picked_entity(&mut self, entity: EntityType) {
|
||||
self.picked = Some(entity);
|
||||
}
|
||||
fn on_entity_pick(&mut self, handle: Handle, _: DVec3) -> CmdResult {
|
||||
if handle.is_null() { return CmdResult::NeedPoint; }
|
||||
if handle.is_null() {
|
||||
return CmdResult::NeedPoint;
|
||||
}
|
||||
if let Some(entity) = self.picked.take() {
|
||||
let supported = match &entity {
|
||||
EntityType::Line(_) | EntityType::Arc(_) => true,
|
||||
@@ -69,94 +110,198 @@ impl CadCommand for JoinCommand {
|
||||
EntityType::Spline(p) => !p.flags.closed && !p.flags.periodic,
|
||||
_ => false,
|
||||
};
|
||||
if supported { self.source = Some((handle, entity)); }
|
||||
if supported {
|
||||
self.source = Some((handle, entity));
|
||||
}
|
||||
}
|
||||
CmdResult::NeedPoint
|
||||
}
|
||||
fn is_selection_gathering(&self) -> bool { self.source.is_some() }
|
||||
fn is_selection_gathering(&self) -> bool {
|
||||
self.source.is_some()
|
||||
}
|
||||
fn on_selection_complete(&mut self, handles: Vec<Handle>) -> CmdResult {
|
||||
self.handles = handles.into_iter().filter(|handle| self.source.as_ref().is_none_or(|(source,_)| handle != source)).collect(); CmdResult::NeedPoint
|
||||
self.handles = handles
|
||||
.into_iter()
|
||||
.filter(|handle| {
|
||||
self.source
|
||||
.as_ref()
|
||||
.is_none_or(|(source, _)| handle != source)
|
||||
})
|
||||
.collect();
|
||||
CmdResult::NeedPoint
|
||||
}
|
||||
fn on_point(&mut self, _: DVec3) -> CmdResult {
|
||||
CmdResult::NeedPoint
|
||||
}
|
||||
fn on_point(&mut self, _: DVec3) -> CmdResult { CmdResult::NeedPoint }
|
||||
fn on_enter(&mut self) -> CmdResult {
|
||||
let Some((source, _)) = self.source.as_ref() else { return CmdResult::Cancel; };
|
||||
if self.handles.is_empty() { return CmdResult::Cancel; }
|
||||
CmdResult::JoinToSource { source: *source, handles: self.handles.clone() }
|
||||
let Some((source, _)) = self.source.as_ref() else {
|
||||
return CmdResult::Cancel;
|
||||
};
|
||||
if self.handles.is_empty() {
|
||||
return CmdResult::Cancel;
|
||||
}
|
||||
CmdResult::JoinToSource {
|
||||
source: *source,
|
||||
handles: self.handles.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Join compatible candidates while preserving the source identity and style.
|
||||
pub fn join_to_source(source: &EntityType, candidates: &[(Handle, &EntityType)]) -> Option<(EntityType, Vec<Handle>)> {
|
||||
let mut result = source.clone(); let mut consumed = Vec::new();
|
||||
pub fn join_to_source(
|
||||
source: &EntityType,
|
||||
candidates: &[(Handle, &EntityType)],
|
||||
) -> Option<(EntityType, Vec<Handle>)> {
|
||||
let mut result = source.clone();
|
||||
let mut consumed = Vec::new();
|
||||
loop {
|
||||
let mut progress = false;
|
||||
for (handle, candidate) in candidates {
|
||||
if consumed.contains(handle) { continue; }
|
||||
if consumed.contains(handle) {
|
||||
continue;
|
||||
}
|
||||
let next = match (&result, *candidate) {
|
||||
(EntityType::Line(a), EntityType::Line(b)) => {
|
||||
let point = |p: &Vector3| [p.x,p.y,p.z];
|
||||
kernel::space::source_join::join_collinear_lines([point(&a.start),point(&a.end)],[point(&b.start),point(&b.end)],JOIN_EPS).map(|span| {
|
||||
let mut line = a.clone(); line.start = Vector3::new(span[0][0],span[0][1],span[0][2]); line.end = Vector3::new(span[1][0],span[1][1],span[1][2]); EntityType::Line(line)
|
||||
let point = |p: &Vector3| [p.x, p.y, p.z];
|
||||
kernel::space::source_join::join_collinear_lines(
|
||||
[point(&a.start), point(&a.end)],
|
||||
[point(&b.start), point(&b.end)],
|
||||
JOIN_EPS,
|
||||
)
|
||||
.map(|span| {
|
||||
let mut line = a.clone();
|
||||
line.start = Vector3::new(span[0][0], span[0][1], span[0][2]);
|
||||
line.end = Vector3::new(span[1][0], span[1][1], span[1][2]);
|
||||
EntityType::Line(line)
|
||||
})
|
||||
}
|
||||
(EntityType::Arc(a), EntityType::Arc(b)) => {
|
||||
let data = |a: &codec::entities::Arc| ([a.center.x,a.center.y,a.center.z],[a.normal.x,a.normal.y,a.normal.z],a.radius,[a.start_angle,a.end_angle]);
|
||||
kernel::space::source_join::join_cocircular_arcs(data(a),data(b),JOIN_EPS).map(|span| {
|
||||
if span[1]-span[0] >= std::f64::consts::TAU {
|
||||
let mut circle = codec::entities::Circle::new(); circle.common = a.common.clone(); circle.center = a.center.clone(); circle.normal = a.normal.clone(); circle.radius = a.radius; circle.thickness = a.thickness; EntityType::Circle(circle)
|
||||
} else { let mut arc = a.clone(); arc.start_angle = span[0]; arc.end_angle = span[1].rem_euclid(std::f64::consts::TAU); EntityType::Arc(arc) }
|
||||
})
|
||||
let data = |a: &codec::entities::Arc| {
|
||||
(
|
||||
[a.center.x, a.center.y, a.center.z],
|
||||
[a.normal.x, a.normal.y, a.normal.z],
|
||||
a.radius,
|
||||
[a.start_angle, a.end_angle],
|
||||
)
|
||||
};
|
||||
kernel::space::source_join::join_cocircular_arcs(data(a), data(b), JOIN_EPS)
|
||||
.map(|span| {
|
||||
if span[1] - span[0] >= std::f64::consts::TAU {
|
||||
let mut circle = codec::entities::Circle::new();
|
||||
circle.common = a.common.clone();
|
||||
circle.center = a.center.clone();
|
||||
circle.normal = a.normal.clone();
|
||||
circle.radius = a.radius;
|
||||
circle.thickness = a.thickness;
|
||||
EntityType::Circle(circle)
|
||||
} else {
|
||||
let mut arc = a.clone();
|
||||
arc.start_angle = span[0];
|
||||
arc.end_angle = span[1].rem_euclid(std::f64::consts::TAU);
|
||||
EntityType::Arc(arc)
|
||||
}
|
||||
})
|
||||
}
|
||||
(EntityType::Spline(source), candidate) => {
|
||||
let curve = |s:&codec::entities::Spline| {
|
||||
if s.flags.closed || s.flags.periodic {return None;}
|
||||
let curve = |s: &codec::entities::Spline| {
|
||||
if s.flags.closed || s.flags.periodic {
|
||||
return None;
|
||||
}
|
||||
if s.control_points.is_empty() && s.fit_points.len() >= 2 {
|
||||
use kernel::space::{NurbsCurve3, Parameterization};
|
||||
let points: Vec<_> = s.fit_points.iter().map(|point| [point.x,point.y,point.z]).collect();
|
||||
let points: Vec<_> = s
|
||||
.fit_points
|
||||
.iter()
|
||||
.map(|point| [point.x, point.y, point.z])
|
||||
.collect();
|
||||
let parameterization = match s.knot_parameterization {
|
||||
1 => Parameterization::Centripetal, 2 => Parameterization::Uniform, _ => Parameterization::Chord,
|
||||
1 => Parameterization::Centripetal,
|
||||
2 => Parameterization::Uniform,
|
||||
_ => Parameterization::Chord,
|
||||
};
|
||||
let tangent = |point: &Vector3| (point.x != 0.0 || point.y != 0.0 || point.z != 0.0)
|
||||
.then_some([point.x,point.y,point.z]);
|
||||
return NurbsCurve3::interpolate_fit(&points,tangent(&s.begin_tangent),tangent(&s.end_tangent),parameterization)?
|
||||
.compact_knots(s.control_tolerance.max(1e-9));
|
||||
let tangent = |point: &Vector3| {
|
||||
(point.x != 0.0 || point.y != 0.0 || point.z != 0.0)
|
||||
.then_some([point.x, point.y, point.z])
|
||||
};
|
||||
return NurbsCurve3::interpolate_fit(
|
||||
&points,
|
||||
tangent(&s.begin_tangent),
|
||||
tangent(&s.end_tangent),
|
||||
parameterization,
|
||||
)?
|
||||
.compact_knots(s.control_tolerance.max(1e-9));
|
||||
}
|
||||
let weights=if s.weights.is_empty(){vec![1.0;s.control_points.len()]}else{s.weights.clone()};
|
||||
kernel::space::NurbsCurve3::new_strict(s.degree as usize,s.control_points.iter().map(|p|[p.x,p.y,p.z]).collect(),s.knots.clone(),weights)
|
||||
let weights = if s.weights.is_empty() {
|
||||
vec![1.0; s.control_points.len()]
|
||||
} else {
|
||||
s.weights.clone()
|
||||
};
|
||||
kernel::space::NurbsCurve3::new_strict(
|
||||
s.degree as usize,
|
||||
s.control_points.iter().map(|p| [p.x, p.y, p.z]).collect(),
|
||||
s.knots.clone(),
|
||||
weights,
|
||||
)
|
||||
};
|
||||
curve(source).and_then(|a| {
|
||||
let b=match candidate {
|
||||
EntityType::Spline(s)=>curve(s),
|
||||
EntityType::Line(line)=>kernel::space::source_join::line_as_nurbs([[line.start.x,line.start.y,line.start.z],[line.end.x,line.end.y,line.end.z]],a.degree()),
|
||||
_=>None,
|
||||
let b = match candidate {
|
||||
EntityType::Spline(s) => curve(s),
|
||||
EntityType::Line(line) => kernel::space::source_join::line_as_nurbs(
|
||||
[
|
||||
[line.start.x, line.start.y, line.start.z],
|
||||
[line.end.x, line.end.y, line.end.z],
|
||||
],
|
||||
a.degree(),
|
||||
),
|
||||
_ => None,
|
||||
}?;
|
||||
let joined=kernel::space::source_join::join_nurbs_curves(&a,&b,JOIN_EPS)?;
|
||||
let mut spline=source.clone();
|
||||
spline.degree=joined.degree() as i32;
|
||||
spline.control_points=joined.control_points().iter().map(|p|Vector3::new(p[0],p[1],p[2])).collect();
|
||||
spline.knots=joined.knots().to_vec();spline.weights=joined.weights().to_vec();
|
||||
spline.fit_points.clear();spline.flags.rational=joined.is_rational();
|
||||
spline.dwg_flags1 &= !1;spline.dxf_flags &= !(32 | 1024);
|
||||
spline.flags.planar=kernel::space::are_coplanar(joined.control_points(),&[]);
|
||||
spline.begin_tangent=Vector3::new(0.0,0.0,0.0);spline.end_tangent=Vector3::new(0.0,0.0,0.0);
|
||||
let joined =
|
||||
kernel::space::source_join::join_nurbs_curves(&a, &b, JOIN_EPS)?;
|
||||
let mut spline = source.clone();
|
||||
spline.degree = joined.degree() as i32;
|
||||
spline.control_points = joined
|
||||
.control_points()
|
||||
.iter()
|
||||
.map(|p| Vector3::new(p[0], p[1], p[2]))
|
||||
.collect();
|
||||
spline.knots = joined.knots().to_vec();
|
||||
spline.weights = joined.weights().to_vec();
|
||||
spline.fit_points.clear();
|
||||
spline.flags.rational = joined.is_rational();
|
||||
spline.dwg_flags1 &= !1;
|
||||
spline.dxf_flags &= !(32 | 1024);
|
||||
spline.flags.planar =
|
||||
kernel::space::are_coplanar(joined.control_points(), &[]);
|
||||
spline.begin_tangent = Vector3::new(0.0, 0.0, 0.0);
|
||||
spline.end_tangent = Vector3::new(0.0, 0.0, 0.0);
|
||||
Some(EntityType::Spline(spline))
|
||||
})
|
||||
}
|
||||
(EntityType::LwPolyline(_) | EntityType::Polyline2D(_), _) => {
|
||||
join_entities(&[(result.common().handle,&result),(*handle,*candidate)]).and_then(|(_,mut entities)| {
|
||||
let entity = entities.pop()?;
|
||||
if matches!(&entity,EntityType::Line(_)) { super::pedit::convert_to_polyline(&entity) } else { Some(entity) }
|
||||
})
|
||||
join_entities(&[(result.common().handle, &result), (*handle, *candidate)])
|
||||
.and_then(|(_, mut entities)| {
|
||||
let entity = entities.pop()?;
|
||||
if matches!(&entity, EntityType::Line(_)) {
|
||||
super::pedit::convert_to_polyline(&entity)
|
||||
} else {
|
||||
Some(entity)
|
||||
}
|
||||
})
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
if let Some(mut entity) = next {
|
||||
*entity.common_mut() = source.common().clone(); result = entity; consumed.push(*handle); progress = true;
|
||||
*entity.common_mut() = source.common().clone();
|
||||
result = entity;
|
||||
consumed.push(*handle);
|
||||
progress = true;
|
||||
}
|
||||
}
|
||||
if !progress { break; }
|
||||
if !progress {
|
||||
break;
|
||||
}
|
||||
}
|
||||
(!consumed.is_empty()).then_some((result,consumed))
|
||||
(!consumed.is_empty()).then_some((result, consumed))
|
||||
}
|
||||
|
||||
/// Endpoint-match tolerance (model units). Segments split from a shared
|
||||
@@ -178,7 +323,7 @@ impl Seg {
|
||||
fn flip(&mut self) {
|
||||
std::mem::swap(&mut self.a, &mut self.b);
|
||||
self.bulge = -self.bulge;
|
||||
self.widths = self.widths.map(|(start,end)| (end,start));
|
||||
self.widths = self.widths.map(|(start, end)| (end, start));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,7 +393,11 @@ fn segs_of(e: &EntityType) -> Option<Vec<Seg>> {
|
||||
a: DVec3::new(w[0].location.x, w[0].location.y, z),
|
||||
b: DVec3::new(w[1].location.x, w[1].location.y, z),
|
||||
bulge: w[0].bulge,
|
||||
widths: Some(if p.constant_width != 0.0 { (p.constant_width,p.constant_width) } else { (w[0].start_width,w[0].end_width) }),
|
||||
widths: Some(if p.constant_width != 0.0 {
|
||||
(p.constant_width, p.constant_width)
|
||||
} else {
|
||||
(w[0].start_width, w[0].end_width)
|
||||
}),
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
@@ -272,11 +421,22 @@ fn segs_of(e: &EntityType) -> Option<Vec<Seg>> {
|
||||
.vertices
|
||||
.windows(2)
|
||||
.enumerate()
|
||||
.map(|(index,w)| Seg {
|
||||
.map(|(index, w)| Seg {
|
||||
a: DVec3::new(w[0].position[0], w[0].position[1], z),
|
||||
b: DVec3::new(w[1].position[0], w[1].position[1], z),
|
||||
bulge: w[0].bulge,
|
||||
widths: Some((if p.vertices[index].start_width == 0.0 {p.start_width} else {p.vertices[index].start_width}, if p.vertices[index].end_width == 0.0 {p.end_width} else {p.vertices[index].end_width})),
|
||||
widths: Some((
|
||||
if p.vertices[index].start_width == 0.0 {
|
||||
p.start_width
|
||||
} else {
|
||||
p.vertices[index].start_width
|
||||
},
|
||||
if p.vertices[index].end_width == 0.0 {
|
||||
p.end_width
|
||||
} else {
|
||||
p.vertices[index].end_width
|
||||
},
|
||||
)),
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
@@ -307,10 +467,16 @@ pub fn join_entities(entities: &[(Handle, &EntityType)]) -> Option<(Vec<Handle>,
|
||||
|
||||
let (mut chain, closed) = stitch(segs)?;
|
||||
// Newly appended line/arc spans inherit the adjoining polyline width.
|
||||
let mut width = chain.iter().find_map(|segment| segment.widths.map(|pair| pair.0)).unwrap_or(0.0);
|
||||
let mut width = chain
|
||||
.iter()
|
||||
.find_map(|segment| segment.widths.map(|pair| pair.0))
|
||||
.unwrap_or(0.0);
|
||||
for segment in &mut chain {
|
||||
if let Some((_,end)) = segment.widths { width = end; }
|
||||
else { segment.widths = Some((width,width)); }
|
||||
if let Some((_, end)) = segment.widths {
|
||||
width = end;
|
||||
} else {
|
||||
segment.widths = Some((width, width));
|
||||
}
|
||||
}
|
||||
|
||||
// Ordered vertices, each tagged with the bulge of the segment that
|
||||
@@ -326,7 +492,17 @@ pub fn join_entities(entities: &[(Handle, &EntityType)]) -> Option<(Vec<Handle>,
|
||||
let planar = verts.iter().all(|(p, _)| (p.z - z0).abs() <= JOIN_EPS);
|
||||
|
||||
// An open run of collinear straight segments collapses back to one Line.
|
||||
if !closed && !has_arc && chain.iter().all(|segment| segment.widths == Some((0.0,0.0))) && !matches!(entities[0].1, EntityType::LwPolyline(_) | EntityType::Polyline2D(_)) && is_collinear(&verts) {
|
||||
if !closed
|
||||
&& !has_arc
|
||||
&& chain
|
||||
.iter()
|
||||
.all(|segment| segment.widths == Some((0.0, 0.0)))
|
||||
&& !matches!(
|
||||
entities[0].1,
|
||||
EntityType::LwPolyline(_) | EntityType::Polyline2D(_)
|
||||
)
|
||||
&& is_collinear(&verts)
|
||||
{
|
||||
let mut line = codec::entities::Line::new();
|
||||
line.common = common;
|
||||
line.common.handle = Handle::NULL;
|
||||
@@ -343,16 +519,26 @@ pub fn join_entities(entities: &[(Handle, &EntityType)]) -> Option<(Vec<Handle>,
|
||||
}
|
||||
let flipped = thickness != 0.0 && normal.z < 0.0;
|
||||
let lw_verts: Vec<codec::entities::LwVertex> = verts
|
||||
.iter().enumerate()
|
||||
.map(|(index,(p, bulge))| {
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, (p, bulge))| {
|
||||
let mut v = codec::entities::LwVertex::new(Vector2::new(
|
||||
if flipped { -p.x } else { p.x },
|
||||
p.y,
|
||||
));
|
||||
v.bulge = if flipped { -*bulge } else { *bulge };
|
||||
let (start,end) = chain.get(index).and_then(|segment| segment.widths)
|
||||
.unwrap_or_else(|| {let width=chain.last().and_then(|segment| segment.widths).map_or(0.0,|pair|pair.1);(width,width)});
|
||||
v.start_width = start; v.end_width = end;
|
||||
let (start, end) = chain
|
||||
.get(index)
|
||||
.and_then(|segment| segment.widths)
|
||||
.unwrap_or_else(|| {
|
||||
let width = chain
|
||||
.last()
|
||||
.and_then(|segment| segment.widths)
|
||||
.map_or(0.0, |pair| pair.1);
|
||||
(width, width)
|
||||
});
|
||||
v.start_width = start;
|
||||
v.end_width = end;
|
||||
v
|
||||
})
|
||||
.collect();
|
||||
@@ -368,21 +554,45 @@ pub fn join_entities(entities: &[(Handle, &EntityType)]) -> Option<(Vec<Handle>,
|
||||
}
|
||||
if let EntityType::Polyline2D(source) = entities[0].1 {
|
||||
let mut polyline = source.clone();
|
||||
let input = crate::entities::curve::ocs_plane(pl.normal.clone(),pl.elevation);
|
||||
let output = crate::entities::curve::ocs_plane(source.normal.clone(),source.elevation);
|
||||
polyline.vertices = pl.vertices.iter().map(|vertex| {
|
||||
let point = output.project(input.point_at([vertex.location.x,vertex.location.y]))?;
|
||||
let mut result = codec::entities::polyline::Vertex2D::new(Vector3::new(point[0],point[1],source.elevation));
|
||||
result.bulge = if kernel::space::Vec3::from(input.normal()?).dot(kernel::space::Vec3::from(output.normal()?)) < 0.0 {-vertex.bulge} else {vertex.bulge}; result.start_width = vertex.start_width; result.end_width = vertex.end_width;
|
||||
Some(result)
|
||||
}).collect::<Option<Vec<_>>>()?;
|
||||
polyline.start_width = 0.0; polyline.end_width = 0.0;
|
||||
let input = crate::entities::curve::ocs_plane(pl.normal.clone(), pl.elevation);
|
||||
let output = crate::entities::curve::ocs_plane(source.normal.clone(), source.elevation);
|
||||
polyline.vertices = pl
|
||||
.vertices
|
||||
.iter()
|
||||
.map(|vertex| {
|
||||
let point =
|
||||
output.project(input.point_at([vertex.location.x, vertex.location.y]))?;
|
||||
let mut result = codec::entities::polyline::Vertex2D::new(Vector3::new(
|
||||
point[0],
|
||||
point[1],
|
||||
source.elevation,
|
||||
));
|
||||
result.bulge = if kernel::space::Vec3::from(input.normal()?)
|
||||
.dot(kernel::space::Vec3::from(output.normal()?))
|
||||
< 0.0
|
||||
{
|
||||
-vertex.bulge
|
||||
} else {
|
||||
vertex.bulge
|
||||
};
|
||||
result.start_width = vertex.start_width;
|
||||
result.end_width = vertex.end_width;
|
||||
Some(result)
|
||||
})
|
||||
.collect::<Option<Vec<_>>>()?;
|
||||
polyline.start_width = 0.0;
|
||||
polyline.end_width = 0.0;
|
||||
polyline.flags.set_closed(pl.is_closed);
|
||||
return Some((handles,vec![EntityType::Polyline2D(polyline)]));
|
||||
return Some((handles, vec![EntityType::Polyline2D(polyline)]));
|
||||
}
|
||||
if let EntityType::LwPolyline(source) = entities[0].1 {
|
||||
pl.plinegen = source.plinegen;
|
||||
if source.constant_width != 0.0 && pl.vertices.iter().all(|vertex| vertex.start_width == source.constant_width && vertex.end_width == source.constant_width) {
|
||||
if source.constant_width != 0.0
|
||||
&& pl.vertices.iter().all(|vertex| {
|
||||
vertex.start_width == source.constant_width
|
||||
&& vertex.end_width == source.constant_width
|
||||
})
|
||||
{
|
||||
pl.constant_width = source.constant_width;
|
||||
}
|
||||
}
|
||||
@@ -452,8 +662,8 @@ fn stitch(mut segs: Vec<Seg>) -> Option<(Vec<Seg>, bool)> {
|
||||
if !segs.is_empty() {
|
||||
return None; // disconnected or branched selection
|
||||
}
|
||||
let closed = chain.len() >= 2
|
||||
&& chain.first().unwrap().a.distance(chain.last().unwrap().b) <= JOIN_EPS;
|
||||
let closed =
|
||||
chain.len() >= 2 && chain.first().unwrap().a.distance(chain.last().unwrap().b) <= JOIN_EPS;
|
||||
Some((chain, closed))
|
||||
}
|
||||
|
||||
@@ -557,10 +767,7 @@ mod join_tests {
|
||||
let mut source = codec::entities::Spline::new();
|
||||
source.common.handle = source_handle;
|
||||
source.degree = 1;
|
||||
source.control_points = vec![
|
||||
Vector3::new(0.0, 0.0, 0.0),
|
||||
Vector3::new(1.0, 0.0, 0.0),
|
||||
];
|
||||
source.control_points = vec![Vector3::new(0.0, 0.0, 0.0), Vector3::new(1.0, 0.0, 0.0)];
|
||||
source.knots = vec![0.0, 0.0, 1.0, 1.0];
|
||||
source.fit_points = vec![Vector3::new(0.0, 0.0, 0.0)];
|
||||
source.dwg_flags1 = 1;
|
||||
@@ -603,7 +810,8 @@ mod join_tests {
|
||||
let mut command = JoinCommand::new().with_source(handle, EntityType::Arc(arc));
|
||||
assert!(command.input_kind().wants_text());
|
||||
assert!(command.point_step_accepts_keywords());
|
||||
let Some(CmdResult::ReplaceMany(mut replacements, _)) = command.on_text_input("Close") else {
|
||||
let Some(CmdResult::ReplaceMany(mut replacements, _)) = command.on_text_input("Close")
|
||||
else {
|
||||
panic!("expected the arc to close");
|
||||
};
|
||||
let Some(EntityType::Circle(circle)) = replacements[0].1.pop() else {
|
||||
@@ -630,6 +838,5 @@ fn is_collinear(verts: &[(DVec3, f64)]) -> bool {
|
||||
.all(|w| (w[1].0 - w[0].0).cross(dir).length() <= 1e-6)
|
||||
}
|
||||
|
||||
|
||||
// ── Autocomplete registry ─────────────────────────────────
|
||||
inventory::submit!(crate::command::CommandRegistration { names: &["JOIN"] }); // JoinCommand
|
||||
inventory::submit!(crate::command::CommandRegistration { names: &["JOIN"] }); // JoinCommand
|
||||
Reference in new issue
Block a user