Associate hatch boundaries from internal points and selected sources

This commit is contained in:
ramox81 committed 2026-09-11 11:18:01 +03:00
1 parent 49d1125530
commit 1039f51abf
5 files changed
+74 -8

No files matched your search

+18 -1
View File
@@ -4462,7 +4462,16 @@ impl OpenCADStudio {
self.command_line.push_info("HATCHEDIT: hatch is already associative.");
self.tabs[i].active_cmd=None;
}else{
let command=crate::modules::draw::draw::hatchedit::HatcheditCommand::for_association(handle,name,scale,angle);
let plane=match self.tabs[i].scene.document.get_entity(handle) {
Some(acadrust::EntityType::Hatch(h))=>{
let storage=crate::entities::curve::ocs_plane(h.normal,h.elevation);
crate::command::WorkingPlane::new(glam::DVec3::from_array(storage.origin),glam::DVec3::from_array(storage.x_axis),glam::DVec3::from_array(storage.y_axis))
},
_=>self.tabs[i].ucs_xform().working_plane(),
};
let mut sources=self.tabs[i].scene.boundary_sources_on_plane(plane,1e-6);
sources.remove(&handle);
let command=crate::modules::draw::draw::hatchedit::HatcheditCommand::for_association(handle,name,scale,angle,plane,sources);
self.tabs[i].scene.deselect_all();
self.command_line.push_info(&command.prompt());
self.tabs[i].active_cmd=Some(Box::new(command));
@@ -4632,6 +4641,14 @@ impl OpenCADStudio {
.scene
.edit_hatch_boundary_handles(handle, &handles, false);
}
HatchEditOperation::AssociatePaths(paths) => {
if paths.is_empty()||paths.iter().any(|path|path.boundary_handles.is_empty()||path.boundary_handles.iter().any(|source|self.tabs[i].scene.document.get_entity(*source).is_none())) {
self.discard_last_undo_entry(i);
self.command_line.push_error("HATCHEDIT: associated boundary is no longer available.");
return Task::none();
}
self.tabs[i].scene.replace_hatch_association(handle,paths);
}
HatchEditOperation::AssociateBoundaries(handles) => {
let plane=match self.tabs[i].scene.document.get_entity(handle) {
Some(acadrust::EntityType::Hatch(h))=>{
+1
View File
@@ -27,6 +27,7 @@ pub enum HatchEditOperation {
RecreateBoundary { associate: bool, region: bool },
BeginAssociate,
AssociateBoundaries(Vec<Handle>),
AssociatePaths(Vec<acadrust::entities::BoundaryPath>),
DrawOrderBoundary { above: bool },
Separate,
AddBoundaries(Vec<Handle>),
+46 -6
View File
@@ -37,6 +37,9 @@ pub struct HatcheditCommand {
current_color: acadrust::types::Color,
current_transparency: acadrust::types::Transparency,
boundary_region: bool,
association_sources: Option<(crate::command::WorkingPlane,rustc_hash::FxHashMap<Handle,crate::scene::BoundarySource>)>,
association_paths: Vec<acadrust::entities::BoundaryPath>,
association_missed: bool,
}
impl HatcheditCommand {
@@ -54,6 +57,9 @@ impl HatcheditCommand {
current_color: acadrust::types::Color::ByLayer,
current_transparency: acadrust::types::Transparency::ByLayer,
boundary_region: false,
association_sources: None,
association_paths: Vec::new(),
association_missed: false,
}
}
@@ -82,6 +88,9 @@ impl HatcheditCommand {
current_color: acadrust::types::Color::ByLayer,
current_transparency: acadrust::types::Transparency::ByLayer,
boundary_region: false,
association_sources: None,
association_paths: Vec::new(),
association_missed: false,
}
}
@@ -104,9 +113,17 @@ impl HatcheditCommand {
})
}
pub fn for_association(handle:Handle,name:String,scale:f32,angle:f32)->Self {
pub fn for_association(handle:Handle,name:String,scale:f32,angle:f32,plane:crate::command::WorkingPlane,sources:rustc_hash::FxHashMap<Handle,crate::scene::BoundarySource>)->Self {
let mut command=Self::with_handle(handle,name,scale,angle,false);
command.input=Some("associate-select");command
command.input=Some("associate-point");command.association_sources=Some((plane,sources));command
}
fn add_association_rings(&mut self,rings:Vec<Vec<[f64;2]>>,sources:&rustc_hash::FxHashMap<Handle,crate::scene::BoundarySource>) {
let exterior=cadkernel::geom2d::ring_nesting_depths(&rings).into_iter().map(|depth|depth==0).collect::<Vec<_>>();
let paths=crate::scene::exact_hatch_paths(&rings,&exterior,sources,1e-6);
self.association_missed=paths.is_empty()||paths.len()!=rings.len()||paths.iter().any(|path|path.boundary_handles.is_empty());
if !self.association_missed {
for path in paths {if !self.association_paths.contains(&path){self.association_paths.push(path);}}
}
}
pub fn with_appearance(mut self,entity:Option<&acadrust::EntityType>,current_color:acadrust::types::Color,current_transparency:acadrust::types::Transparency)->Self {
self.source_appearance=entity.map(|e|{let c=e.common();(c.color,c.layer.clone(),c.transparency)});
@@ -162,6 +179,8 @@ impl CadCommand for HatcheditCommand {
"boundary-type"=>"Enter type of boundary object [Region/Polyline] <Polyline>:",
"boundary-associate"=>"Reassociate hatch with new boundary? [Yes/No] <No>:",
"associate-select"=>"Select boundary objects:",
"associate-point" if self.association_missed=>"No closed boundary found. Specify internal point or [Select objects]:",
"associate-point"=>"Specify internal point or [Select objects]:",
_=>"Specify value:",
}.into();
}
@@ -188,7 +207,13 @@ impl CadCommand for HatcheditCommand {
}
fn is_selection_gathering(&self)->bool {self.input==Some("associate-select")}
fn on_selection_complete(&mut self,handles:Vec<Handle>)->CmdResult {
self.boundary_selection=handles;CmdResult::NeedPoint
self.boundary_selection=handles.clone();
if let Some((_,sources))=&self.association_sources {
let sources=sources.iter().filter(|(handle,_)|handles.contains(handle)).map(|(handle,source)|(*handle,source.clone())).collect();
let rings=crate::scene::boundary_faces(&sources,1e-6);
self.add_association_rings(rings,&sources);
}
self.input=Some("associate-point");CmdResult::NeedPoint
}
fn on_entity_pick(&mut self, handle: Handle, _pt: DVec3) -> CmdResult {
@@ -211,6 +236,7 @@ impl CadCommand for HatcheditCommand {
}
fn options(&self) -> Vec<crate::command::CmdOption> {
if self.input==Some("associate-point") {return vec![crate::command::CmdOption::new("Select objects","S")];}
if self.input.is_some() {return Vec::new();}
if !matches!(self.step, HatcheditStep::EditOptions { .. }) {
return Vec::new();
@@ -238,6 +264,10 @@ impl CadCommand for HatcheditCommand {
use acadrust::types::{Color,Transparency};
let appearance=|color,layer,transparency|HatchEditOperation::Appearance{color,layer,transparency};
match input {
"associate-point"=>{
if matches!(keyword.as_str(),"S"|"SELECT"|"SELECT OBJECTS") {self.input=Some("associate-select");}
return Some(CmdResult::NeedPoint);
},
"color"=>{
if matches!(keyword.as_str(),"T"|"TRUECOLOR") {self.input=Some("truecolor");return Some(CmdResult::NeedPoint);}
let color=match keyword.as_str(){"."=>Some(self.current_color),"BYLAYER"=>Some(Color::ByLayer),"BYBLOCK"=>Some(Color::ByBlock),
@@ -403,7 +433,16 @@ impl CadCommand for HatcheditCommand {
Some(CmdResult::NeedPoint)
}
fn on_point(&mut self, _pt: DVec3) -> CmdResult {
fn on_point(&mut self, pt: DVec3) -> CmdResult {
if self.input==Some("associate-point") {
if let Some((plane,sources))=&self.association_sources {
let point=plane.to_local(pt);
let sources=sources.clone();
if let Some(rings)=crate::scene::model::presspull_model::selected_rings(&sources,[point.x,point.y]) {
self.add_association_rings(rings,&sources);
}else{self.association_missed=true;}
}
}
CmdResult::NeedPoint
}
fn on_enter(&mut self) -> CmdResult {
@@ -420,8 +459,9 @@ impl CadCommand for HatcheditCommand {
Some("angle")=>self.apply_result(self.update_operation()).unwrap_or(CmdResult::Cancel),
Some("boundary-type")=>{self.boundary_region=false;self.input=Some("boundary-associate");CmdResult::NeedPoint},
Some("boundary-associate")=>self.apply_result(HatchEditOperation::RecreateBoundary{associate:false,region:self.boundary_region}).unwrap_or(CmdResult::Cancel),
Some("associate-select")=>if self.boundary_selection.is_empty(){CmdResult::Cancel}else{
self.apply_result(HatchEditOperation::AssociateBoundaries(self.boundary_selection.clone())).unwrap_or(CmdResult::Cancel)
Some("associate-select")=>{self.input=Some("associate-point");CmdResult::NeedPoint},
Some("associate-point")=>if self.association_paths.is_empty(){CmdResult::Cancel}else{
self.apply_result(HatchEditOperation::AssociatePaths(self.association_paths.clone())).unwrap_or(CmdResult::Cancel)
},
_=>CmdResult::Cancel,
}
+8
View File
@@ -628,6 +628,14 @@ pub(crate) fn boundary_entities_from_sources(
}
impl Scene {
pub(crate) fn replace_hatch_association(&mut self,handle:Handle,paths:Vec<acadrust::entities::BoundaryPath>) {
let Some(EntityType::Hatch(hatch))=self.document.get_entity_mut(handle) else{return;};
hatch.paths=paths;
hatch.is_associative=true;
self.associative_hatch_source_cache.borrow_mut().take();
self.refresh_fill_model(handle);
}
pub(crate) fn edit_hatch_boundary_handles(
&mut self,
hatch_handle: Handle,
+1 -1
View File
@@ -141,7 +141,7 @@ fn boundary_sources(scene: &Scene, plane: WorkingPlane) -> FxHashMap<Handle, Bou
}).collect()
}
fn selected_rings(sources: &FxHashMap<Handle, BoundarySource>, point: [f64; 2]) -> Option<Vec<Vec<[f64; 2]>>> {
pub(crate) fn selected_rings(sources: &FxHashMap<Handle, BoundarySource>, point: [f64; 2]) -> Option<Vec<Vec<[f64; 2]>>> {
let rings = boundary_faces(sources, BOUNDARY_TOLERANCE);
let loops = boundary_loops(sources, &rings)?;
let tolerance = Tolerance::new(BOUNDARY_TOLERANCE);