forked from 1b-agent/opencadstudio-upstream
fix: correct hatch geometry workflow
This commit is contained in:
1 parent
f83037271c
commit
4bc02b6b4a
11 files changed
+401
-255
No files matched your search
+38
-35
@@ -1375,6 +1375,13 @@ impl OpenCADStudio {
|
||||
.collect::<Vec<_>>();
|
||||
sources.push(handles);
|
||||
}
|
||||
if let Some(paths) = hatch.boundary_paths.as_mut() {
|
||||
for (path, handles) in std::sync::Arc::make_mut(paths).iter_mut().zip(&sources)
|
||||
{
|
||||
path.boundary_handles = handles.clone();
|
||||
path.flags.set_external(!handles.is_empty());
|
||||
}
|
||||
}
|
||||
hatch.boundary_sources = Some(std::sync::Arc::new(sources));
|
||||
let layer = self.tabs[i].active_layer.clone();
|
||||
let new_handle =
|
||||
@@ -1619,10 +1626,7 @@ impl OpenCADStudio {
|
||||
.into_iter()
|
||||
.map(|e| self.tabs[i].scene.add_entity(e))
|
||||
.collect();
|
||||
// A replaced dimension carries edited geometry/text but still
|
||||
// names its old *D block; drop that stale block so the next save
|
||||
// re-bakes it — otherwise BricsCAD/ODA draw the pre-edit
|
||||
// graphics while OCS shows the edit. (#181)
|
||||
// Rebuild replaced dimensions from edited data.
|
||||
for &nh in &new_handles {
|
||||
if matches!(
|
||||
self.tabs[i].scene.document.get_entity(nh),
|
||||
@@ -1932,11 +1936,6 @@ impl OpenCADStudio {
|
||||
}
|
||||
// The command stays active after each apply so more targets
|
||||
// can keep being picked; Enter / Esc ends it (#362).
|
||||
// Special (type-specific) properties travel like AutoCAD's
|
||||
// Special Properties: each is captured from the source when it
|
||||
// carries it and applied only to destinations that support it
|
||||
// (#281). Text formatting crosses TEXT ↔ MTEXT (#361); the dim
|
||||
// style crosses Dimension / Leader / Tolerance.
|
||||
let src_clone = self.tabs[i].scene.document.get_entity(src).cloned();
|
||||
let src_common = src_clone.as_ref().map(|e| e.common().clone());
|
||||
let thickness = src_clone
|
||||
@@ -3337,6 +3336,7 @@ impl OpenCADStudio {
|
||||
if disassociate {
|
||||
for path in &mut hatch.paths {
|
||||
path.boundary_handles.clear();
|
||||
path.flags.set_external(false);
|
||||
}
|
||||
hatch.is_associative = false;
|
||||
}
|
||||
@@ -3378,21 +3378,19 @@ impl OpenCADStudio {
|
||||
.edit_hatch_boundary_handles(handle, &handles, false);
|
||||
}
|
||||
HatchEditOperation::RecreateBoundary => {
|
||||
let model = self.tabs[i].scene.hatches.get(&handle).cloned();
|
||||
if let Some(model) = model {
|
||||
let mut rings = vec![Vec::new()];
|
||||
for &[x, y] in model.boundary.iter() {
|
||||
if x.is_finite() && y.is_finite() {
|
||||
rings.last_mut().unwrap().push([
|
||||
model.world_origin[0] + x as f64,
|
||||
model.world_origin[1] + y as f64,
|
||||
]);
|
||||
} else if !rings.last().unwrap().is_empty() {
|
||||
rings.push(Vec::new());
|
||||
}
|
||||
}
|
||||
rings.retain(|ring| ring.len() >= 3);
|
||||
let entities = crate::scene::boundary_entities(&rings);
|
||||
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(
|
||||
source.normal,
|
||||
source.elevation,
|
||||
);
|
||||
let plane = crate::command::WorkingPlane::new(
|
||||
glam::DVec3::from_array(storage.origin),
|
||||
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 mut handles = Vec::new();
|
||||
for entity in entities {
|
||||
if let Some(boundary) = self.commit_entity_handle(entity) {
|
||||
@@ -3406,6 +3404,7 @@ impl OpenCADStudio {
|
||||
hatch.paths.iter_mut().zip(handles.iter().copied())
|
||||
{
|
||||
path.boundary_handles = vec![boundary];
|
||||
path.flags.set_external(true);
|
||||
}
|
||||
hatch.is_associative = !handles.is_empty();
|
||||
}
|
||||
@@ -3418,18 +3417,22 @@ impl OpenCADStudio {
|
||||
HatchEditOperation::Separate => {
|
||||
let source = self.tabs[i].scene.document.get_entity(handle).cloned();
|
||||
if let Some(acadrust::EntityType::Hatch(hatch)) = source {
|
||||
for path in hatch.paths.iter().cloned() {
|
||||
let mut separated = hatch.clone();
|
||||
separated.common.handle = acadrust::Handle::NULL;
|
||||
separated.paths = vec![path];
|
||||
separated.is_associative = separated.paths.iter().any(|path| {
|
||||
!path.boundary_handles.is_empty()
|
||||
});
|
||||
self.tabs[i]
|
||||
.scene
|
||||
.add_entity(acadrust::EntityType::Hatch(separated));
|
||||
let groups = crate::scene::separated_hatch_path_groups(&hatch);
|
||||
if groups.len() > 1 {
|
||||
for paths in groups {
|
||||
let mut separated = hatch.clone();
|
||||
separated.common.handle = acadrust::Handle::NULL;
|
||||
separated.paths = paths;
|
||||
separated.is_associative = separated
|
||||
.paths
|
||||
.iter()
|
||||
.any(|path| !path.boundary_handles.is_empty());
|
||||
self.tabs[i]
|
||||
.scene
|
||||
.add_entity(acadrust::EntityType::Hatch(separated));
|
||||
}
|
||||
self.tabs[i].scene.erase_entities(&[handle]);
|
||||
}
|
||||
self.tabs[i].scene.erase_entities(&[handle]);
|
||||
}
|
||||
}
|
||||
HatchEditOperation::DrawOrderFront
|
||||
|
||||
+35
-10
@@ -635,9 +635,25 @@ impl OpenCADStudio {
|
||||
|
||||
"HATCH" => {
|
||||
use crate::modules::draw::draw::hatch::HatchCommand;
|
||||
let working_plane = if self.tabs[i].editing_model_space() {
|
||||
self.tabs[i].ucs_xform().working_plane()
|
||||
} else {
|
||||
crate::command::WorkingPlane::default()
|
||||
};
|
||||
let normal = working_plane.z.normalize_or(glam::DVec3::Z);
|
||||
let elevation = working_plane.origin.dot(normal);
|
||||
let storage = crate::entities::curve::ocs_plane(
|
||||
acadrust::types::Vector3::new(normal.x, normal.y, normal.z),
|
||||
elevation,
|
||||
);
|
||||
let plane = crate::command::WorkingPlane::new(
|
||||
glam::DVec3::from_array(storage.origin),
|
||||
glam::DVec3::from_array(storage.x_axis),
|
||||
glam::DVec3::from_array(storage.y_axis),
|
||||
);
|
||||
let boundary_sources = self.tabs[i]
|
||||
.scene
|
||||
.boundary_sources_on_plane(crate::command::WorkingPlane::default(), 1.0e-6);
|
||||
.boundary_sources_on_plane(plane, 1.0e-6);
|
||||
let outlines = crate::scene::boundary_faces(&boundary_sources, 1.0e-6);
|
||||
let selected = self.tabs[i]
|
||||
.scene
|
||||
@@ -652,8 +668,13 @@ impl OpenCADStudio {
|
||||
let common = self.tabs[i].scene.document.get_entity(*handle)?.common();
|
||||
Some((model, common.color.clone(), common.transparency))
|
||||
});
|
||||
let new_cmd =
|
||||
HatchCommand::new(outlines, boundary_sources, selected, inherited);
|
||||
let new_cmd = HatchCommand::new(
|
||||
outlines,
|
||||
boundary_sources,
|
||||
selected,
|
||||
inherited,
|
||||
plane,
|
||||
);
|
||||
self.command_line.push_info(&new_cmd.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
||||
self.refresh_area_preview(i);
|
||||
@@ -666,21 +687,25 @@ impl OpenCADStudio {
|
||||
if sel.len() == 1 {
|
||||
let (h, _) = sel[0];
|
||||
if let Some(model) = self.tabs[i].scene.hatches.get(&h).cloned() {
|
||||
let annotative = self.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
.get_entity(h)
|
||||
.is_some_and(|entity| {
|
||||
let entity = self.tabs[i].scene.document.get_entity(h);
|
||||
let annotative = entity.is_some_and(|entity| {
|
||||
crate::scene::annotative::is_annotative(
|
||||
&self.tabs[i].scene.document,
|
||||
entity,
|
||||
)
|
||||
});
|
||||
let (scale, angle) = match entity {
|
||||
Some(acadrust::EntityType::Hatch(hatch)) => (
|
||||
hatch.pattern_scale as f32,
|
||||
hatch.pattern_angle.to_degrees() as f32,
|
||||
),
|
||||
_ => (model.scale, model.angle_offset.to_degrees()),
|
||||
};
|
||||
let cmd = HatcheditCommand::with_handle(
|
||||
h,
|
||||
model.name.clone(),
|
||||
model.scale,
|
||||
model.angle_offset.to_degrees(),
|
||||
scale,
|
||||
angle,
|
||||
annotative,
|
||||
);
|
||||
self.command_line.push_info(&cmd.prompt());
|
||||
|
||||
@@ -2057,9 +2057,7 @@ pub(super) fn aggregate_sections(
|
||||
for sections in all_sections {
|
||||
result = merge_sections(&result, §ions);
|
||||
}
|
||||
// Unlike ordinary common properties, cumulative area is an aggregate by
|
||||
// definition. Preserve the individual Area row's "varies" state while
|
||||
// summing every selected hatch's actual filled area (holes subtracted).
|
||||
// Sum the filled area while individual Area rows may still vary.
|
||||
if selected.len() > 1
|
||||
&& selected
|
||||
.iter()
|
||||
|
||||
@@ -3490,23 +3490,27 @@ impl OpenCADStudio {
|
||||
.unwrap_or(false)
|
||||
{
|
||||
if let Some(model) = self.tabs[i].scene.hatches.get(&handle).cloned() {
|
||||
let annotative = self.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
.get_entity(handle)
|
||||
.is_some_and(|entity| {
|
||||
let entity = self.tabs[i].scene.document.get_entity(handle);
|
||||
let annotative = entity.is_some_and(|entity| {
|
||||
crate::scene::annotative::is_annotative(
|
||||
&self.tabs[i].scene.document,
|
||||
entity,
|
||||
)
|
||||
});
|
||||
let (scale, angle) = match entity {
|
||||
Some(acadrust::EntityType::Hatch(hatch)) => (
|
||||
hatch.pattern_scale as f32,
|
||||
hatch.pattern_angle.to_degrees() as f32,
|
||||
),
|
||||
_ => (model.scale, model.angle_offset.to_degrees()),
|
||||
};
|
||||
use crate::command::CadCommand;
|
||||
use crate::modules::draw::draw::hatchedit::HatcheditCommand;
|
||||
let cmd: Box<dyn CadCommand> = Box::new(HatcheditCommand::with_handle(
|
||||
handle,
|
||||
model.name.clone(),
|
||||
model.scale,
|
||||
model.angle_offset.to_degrees(),
|
||||
scale,
|
||||
angle,
|
||||
annotative,
|
||||
));
|
||||
self.command_line.push_info(&cmd.prompt());
|
||||
|
||||
Reference in new issue
Block a user