From 6e74cc48d89b58d76679883831e934a9100c598c Mon Sep 17 00:00:00 2001 From: ramox81 <184937705+ramox81@users.noreply.github.com> Date: Fri, 11 Sep 2026 10:57:23 +0300 Subject: [PATCH] Recreate hatch boundaries as exact regions --- src/app/command_driver.rs | 18 +++++++++++++++--- src/command.rs | 2 +- src/modules/draw/draw/hatchedit.rs | 19 +++++++++++++------ src/scene/model/presspull_model.rs | 6 +++++- 4 files changed, 34 insertions(+), 11 deletions(-) diff --git a/src/app/command_driver.rs b/src/app/command_driver.rs index 2fa61e4f6..16be1a02c 100644 --- a/src/app/command_driver.rs +++ b/src/app/command_driver.rs @@ -4650,7 +4650,7 @@ impl OpenCADStudio { let valid_handles:Vec<_>=sources.keys().copied().collect(); self.tabs[i].scene.edit_hatch_boundary_handles(handle,&valid_handles,true); } - HatchEditOperation::RecreateBoundary { associate } => { + HatchEditOperation::RecreateBoundary { associate, region } => { let source = self.tabs[i].scene.document.get_entity(handle).cloned(); if let Some(acadrust::EntityType::Hatch(source)) = source { let storage = crate::entities::curve::ocs_plane( @@ -4662,8 +4662,20 @@ impl OpenCADStudio { glam::DVec3::from_array(storage.x_axis), glam::DVec3::from_array(storage.y_axis), ); - let rings = crate::scene::hatch_boundary_rings(&source); - let entities = crate::scene::boundary_entities(&rings, plane); + let entities = if region { + let entities = source.paths.iter().map(|path| { + let curves = path.edges.iter().map(crate::entities::hatch::edge_curve).collect::>>()?; + crate::scene::model::presspull_model::region_from_loops(&[curves], plane) + }).collect::>>(); + let Some(entities) = entities else { + self.command_line.push_error("HATCHEDIT: boundary cannot form a region."); + return Task::none(); + }; + entities + } else { + let rings = crate::scene::hatch_boundary_rings(&source); + crate::scene::boundary_entities(&rings, plane) + }; let mut handles = Vec::new(); for entity in entities { if let Some(boundary) = self.commit_entity_handle(entity) { diff --git a/src/command.rs b/src/command.rs index b3fbe6de8..b5167f0d7 100644 --- a/src/command.rs +++ b/src/command.rs @@ -24,7 +24,7 @@ pub enum HatchEditOperation { style: Option, annotative: Option, }, - RecreateBoundary { associate: bool }, + RecreateBoundary { associate: bool, region: bool }, BeginAssociate, AssociateBoundaries(Vec), DrawOrderBoundary { above: bool }, diff --git a/src/modules/draw/draw/hatchedit.rs b/src/modules/draw/draw/hatchedit.rs index 825b64ad3..807babf52 100644 --- a/src/modules/draw/draw/hatchedit.rs +++ b/src/modules/draw/draw/hatchedit.rs @@ -35,6 +35,7 @@ pub struct HatcheditCommand { boundary_selection: Vec, source_appearance: Option<(acadrust::types::Color,String,acadrust::types::Transparency)>, current_color: acadrust::types::Color, + boundary_region: bool, } impl HatcheditCommand { @@ -50,6 +51,7 @@ impl HatcheditCommand { boundary_selection: Vec::new(), source_appearance: None, current_color: acadrust::types::Color::ByLayer, + boundary_region: false, } } @@ -76,6 +78,7 @@ impl HatcheditCommand { boundary_selection: Vec::new(), source_appearance: None, current_color: acadrust::types::Color::ByLayer, + boundary_region: false, } } @@ -153,7 +156,7 @@ impl CadCommand for HatcheditCommand { "layer"=>"Specify layer or [. (for use current)]:", "transparency"=>"Specify transparency value (0-90) or ByLayer/ByBlock:", "draworder"=>"Enter draw order [do Not change/send to Back/bring to Front/send beHind boundary/bring in front of bounDary] :", - "boundary-type"=>"Enter type of boundary object [Polyline] :", + "boundary-type"=>"Enter type of boundary object [Region/Polyline] :", "boundary-associate"=>"Reassociate hatch with new boundary? [Yes/No] :", "associate-select"=>"Select boundary objects:", _=>"Specify value:", @@ -254,10 +257,14 @@ impl CadCommand for HatcheditCommand { "H"|"BEHIND"=>self.apply_result(HatchEditOperation::DrawOrderBoundary{above:false}), "D"=>self.apply_result(HatchEditOperation::DrawOrderBoundary{above:true}), "B"|"BACK"=>self.apply_result(HatchEditOperation::DrawOrderBack),"N"|"NOT"=>Some(CmdResult::Cancel),_=>Some(CmdResult::NeedPoint)}, - "boundary-type"=>if matches!(keyword.as_str(),"P"|"POLYLINE"){self.input=Some("boundary-associate");}, + "boundary-type"=>match keyword.as_str(){ + "P"|"POLYLINE"=>{self.boundary_region=false;self.input=Some("boundary-associate");}, + "R"|"REGION"=>{self.boundary_region=true;self.input=Some("boundary-associate");}, + _=>{}, + }, "boundary-associate"=>return match keyword.as_str(){ - "Y"|"YES"=>self.apply_result(HatchEditOperation::RecreateBoundary{associate:true}), - "N"|"NO"=>self.apply_result(HatchEditOperation::RecreateBoundary{associate:false}), + "Y"|"YES"=>self.apply_result(HatchEditOperation::RecreateBoundary{associate:true,region:self.boundary_region}), + "N"|"NO"=>self.apply_result(HatchEditOperation::RecreateBoundary{associate:false,region:self.boundary_region}), _=>Some(CmdResult::NeedPoint), }, "pattern"=>{ @@ -408,8 +415,8 @@ impl CadCommand for HatcheditCommand { } Some("scale")=>{self.input=Some("angle");CmdResult::NeedPoint} Some("angle")=>self.apply_result(self.update_operation()).unwrap_or(CmdResult::Cancel), - Some("boundary-type")=>{self.input=Some("boundary-associate");CmdResult::NeedPoint}, - Some("boundary-associate")=>self.apply_result(HatchEditOperation::RecreateBoundary{associate:false}).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) }, diff --git a/src/scene/model/presspull_model.rs b/src/scene/model/presspull_model.rs index 151b15e95..0218f03ab 100644 --- a/src/scene/model/presspull_model.rs +++ b/src/scene/model/presspull_model.rs @@ -179,8 +179,12 @@ fn boundary_region( sources: &FxHashMap, rings: &[Vec<[f64; 2]>], plane: WorkingPlane, ) -> Option { let loops = boundary_loops(sources, rings)?; + region_from_loops(&loops, plane) +} + +pub(crate) fn region_from_loops(loops: &[Vec], plane: WorkingPlane) -> Option { let kernel_plane = Plane::from_axes(plane.origin.to_array(), plane.x.to_array(), plane.y.to_array()); - let sheet = brep::planar_region(kernel_plane, &loops)?; + let sheet = brep::planar_region(kernel_plane, loops)?; let document = crate::scene::convert::acis_export::solid_to_sat(&sheet)?; let mut region = Region::new(); region.acis_data = AcisData::from_sat(&document.to_sat_string());