Complete independent CHAMFEREDGE workflow

This commit is contained in:
ramox81 committed 2026-09-08 18:08:52 +03:00
1 parent febedad916
commit 4f22d998e7
13 files changed
+1098 -26

No files matched your search

+22
View File
@@ -733,6 +733,7 @@ impl OpenCADStudio {
&result,
CmdResult::Relaunch(..)
| CmdResult::Dispatch(..)
| CmdResult::SolidChamferEdges { .. }
| CmdResult::SolidSubtract { .. }
);
let task = self.apply_cmd_result_inner(result);
@@ -4115,6 +4116,27 @@ impl OpenCADStudio {
return task;
}
CmdResult::SolidChamferEdges {
handle,
edges,
base_face,
distance1,
distance2,
} => {
let task = self.solid_chamfer_edges(
handle,
&edges,
base_face,
distance1,
distance2,
);
self.tabs[i].active_cmd = None;
self.tabs[i].snap_result = None;
self.tabs[i].scene.clear_preview_wire();
self.restore_pre_cmd_tangent();
return task;
}
CmdResult::SolidSubtract {
bases,
cutters,
+85 -6
View File
@@ -17,6 +17,49 @@ fn selected_solid(app: &OpenCADStudio, tab: usize) -> Option<acadrust::Handle> {
}
}
fn selected_chamfer_body(app: &OpenCADStudio, tab: usize) -> Option<acadrust::Handle> {
let scene = &app.tabs.get(tab)?.scene;
let selected = scene.selected_handles_in_order();
let [handle] = selected.as_slice() else {
return None;
};
matches!(
scene.document.get_entity(*handle),
Some(acadrust::EntityType::Solid3D(_) | acadrust::EntityType::Surface(_))
)
.then_some(*handle)
}
fn chamfer_edge_sources(
app: &mut OpenCADStudio,
tab: usize,
) -> Vec<(acadrust::Handle, cadkernel::brep::Body)> {
let handles = app.tabs[tab]
.scene
.document
.entities()
.filter_map(|entity| {
matches!(
entity,
acadrust::EntityType::Solid3D(_) | acadrust::EntityType::Surface(_)
)
.then_some(entity.common().handle)
})
.collect::<Vec<_>>();
app.tabs[tab].scene.restore_solid_models(&handles);
handles
.into_iter()
.filter_map(|handle| {
app.tabs[tab]
.scene
.solid_models
.get(&handle)
.cloned()
.map(|body| (handle, body))
})
.collect()
}
impl OpenCADStudio {
pub(super) fn dispatch_dim(&mut self, cmd: &str, i: usize) -> Option<Task<Message>> {
match cmd {
@@ -1107,16 +1150,52 @@ impl OpenCADStudio {
}
}
"SOLIDCHAMFER" => {
use crate::modules::model::edge_cmd::{EdgeOperation, SolidEdgeCommand};
let command = SolidEdgeCommand::new(EdgeOperation::Chamfer, selected_solid(self, i));
"CHAMFEREDGE" | "SOLIDCHAMFER" => {
use crate::modules::model::chamferedge_cmd::ChamferEdgeCommand;
let target = selected_chamfer_body(self, i);
let header = &self.tabs[i].scene.document.header;
let distances = (header.chamfer_distance_a, header.chamfer_distance_b);
let bodies = chamfer_edge_sources(self, i);
let command = ChamferEdgeCommand::new(
target,
bodies,
crate::scene::WireModel::SELECTED,
distances,
);
let distances = command.distances();
self.command_line.push_output(
crate::tf!(
"Distance1 = {:.4}, Distance2 = {:.4}",
distances.0,
distances.1
)
.as_ref(),
);
self.command_line.push_info(&command.prompt());
self.tabs[i].active_cmd = Some(Box::new(command));
}
"CHAMFER" if selected_solid(self, i).is_some() => {
use crate::modules::model::edge_cmd::{EdgeOperation, SolidEdgeCommand};
let command = SolidEdgeCommand::new(EdgeOperation::Chamfer, selected_solid(self, i));
"CHAMFER" if selected_chamfer_body(self, i).is_some() => {
use crate::modules::model::chamferedge_cmd::ChamferEdgeCommand;
let target = selected_chamfer_body(self, i);
let header = &self.tabs[i].scene.document.header;
let distances = (header.chamfer_distance_a, header.chamfer_distance_b);
let bodies = chamfer_edge_sources(self, i);
let command = ChamferEdgeCommand::new(
target,
bodies,
crate::scene::WireModel::SELECTED,
distances,
);
let distances = command.distances();
self.command_line.push_output(
crate::tf!(
"Distance1 = {:.4}, Distance2 = {:.4}",
distances.0,
distances.1
)
.as_ref(),
);
self.command_line.push_info(&command.prompt());
self.tabs[i].active_cmd = Some(Box::new(command));
}
+170 -1
View File
@@ -7,7 +7,7 @@ use acadrust::{
objects::SolidHistoryOperation,
EntityType, Handle,
};
use cadkernel::brep::Body;
use cadkernel::brep::{Body, EdgeKey, FaceKey};
use iced::Task;
use std::collections::HashMap;
@@ -244,6 +244,29 @@ fn entity_with_boolean_body(mut source: EntityType, body: &Body) -> Option<Entit
Some(source)
}
fn entity_with_history_body(mut source: EntityType, body: &Body) -> Option<EntityType> {
let document = crate::scene::convert::acis_export::solid_to_sat(body)?;
let wires = solid_model::edge_wires(body);
match &mut source {
EntityType::Solid3D(entity) => {
entity.wires = wires;
entity.silhouettes.clear();
entity.set_sat_document(&document);
}
EntityType::Surface(entity) => {
entity.wires = wires;
entity.silhouettes.clear();
if entity.kind != SurfaceKind::Plane {
entity.kind = SurfaceKind::Generic;
entity.surface_data = SurfaceData::Generic;
}
entity.acis_data = AcisData::from_sat(&document.to_sat_string());
}
_ => return None,
}
Some(source)
}
enum IntersectBodyOutcome {
Area(Body),
Touching,
@@ -646,6 +669,152 @@ impl super::OpenCADStudio {
Task::none()
}
fn replace_edge_body_with_chamfer(
&mut self,
handle: Handle,
source: &Body,
result: Body,
edges: &[EdgeKey],
base_face: FaceKey,
distance1: f64,
distance2: f64,
) -> bool {
let i = self.active_tab;
let Some(display) = self.tabs[i]
.scene
.prepare_solid_model_display(handle, &result)
.filter(|display| display.0.complete)
else {
self.command_line.push_error(crate::t!("The result could not be displayed completely. The original edge body was retained.").as_ref());
return false;
};
let ordered_edges = source.edge_keys().collect::<Vec<_>>();
let Some(history_edges) = edges
.iter()
.map(|edge| {
ordered_edges
.iter()
.position(|candidate| candidate == edge)
.and_then(|ordinal| i32::try_from(ordinal).ok())
})
.collect::<Option<Vec<_>>>()
else {
self.command_line
.push_error(crate::t!("A selected edge no longer belongs to the body.").as_ref());
return false;
};
let Some(history_face) = source
.face_keys()
.position(|candidate| candidate == base_face)
.and_then(|ordinal| i32::try_from(ordinal).ok())
else {
self.command_line.push_error(
crate::t!("The selected base face no longer belongs to the body.").as_ref(),
);
return false;
};
self.push_undo_snapshot(i, "CHAMFEREDGE");
if self.tabs[i].scene.document.solid_history_graph(handle).is_none()
&& !self.tabs[i]
.scene
.create_solid_history(handle, solid_history::brep_op(source))
{
self.command_line
.push_error(crate::t!("The edge body history could not be created.").as_ref());
return false;
}
if !self.tabs[i].scene.append_solid_history(
handle,
solid_history::chamfer_op(history_edges, history_face, distance1, distance2),
) {
self.command_line
.push_error(crate::t!("The chamfer history could not be recorded.").as_ref());
return false;
}
let _ = self.tabs[i].scene.apply_solid_history_choice(
handle,
solid_history::PROP_HISTORY,
"Record",
);
let Some(entity) = self.tabs[i].scene.document.get_entity(handle).cloned() else {
return false;
};
let Some(entity) = entity_with_history_body(entity, &result) else {
return false;
};
if !self.tabs[i].scene.update_entity(entity) {
self.command_line
.push_error(crate::t!("The edge body could not be updated.").as_ref());
return false;
}
self.tabs[i]
.scene
.register_prepared_solid_model(handle, result, display);
self.tabs[i].scene.deselect_all();
self.tabs[i].scene.select_entity(handle, false);
self.tabs[i].dirty = true;
self.refresh_properties();
true
}
pub(super) fn solid_chamfer_edges(
&mut self,
handle: Handle,
edges: &[EdgeKey],
base_face: FaceKey,
distance1: f64,
distance2: f64,
) -> Task<Message> {
let i = self.active_tab;
if self.reject_locked_edit(i, handle) {
return Task::none();
}
if !matches!(
self.tabs[i].scene.document.get_entity(handle),
Some(EntityType::Solid3D(_) | EntityType::Surface(_))
) {
self.command_line
.push_error(crate::t!("Select a 3D solid or surface edge.").as_ref());
return Task::none();
}
self.tabs[i].scene.restore_solid_models(&[handle]);
let Some(body) = self.tabs[i].scene.solid_models.get(&handle).cloned() else {
self.command_line
.push_error(crate::t!("The edge body geometry could not be restored.").as_ref());
return Task::none();
};
let result = match cadkernel::brep::chamfer_edges(
&body,
edges,
base_face,
distance1,
distance2,
) {
Ok(result) => result,
Err(error) => {
self.command_line
.push_error(&format!("CHAMFEREDGE: {error}"));
return Task::none();
}
};
if self.replace_edge_body_with_chamfer(
handle,
&body,
result,
edges,
base_face,
distance1,
distance2,
) {
self.tabs[i].scene.document.header.chamfer_distance_a = distance1;
self.tabs[i].scene.document.header.chamfer_distance_b = distance2;
self.command_line
.push_output(crate::t!("CHAMFEREDGE: solid updated.").as_ref());
}
Task::none()
}
/// Intersect compatible selected solids, Regions, and coplanar Surfaces.
pub(super) fn solid_intersect(&mut self) -> Task<Message> {
let i = self.active_tab;
+8
View File
@@ -740,6 +740,14 @@ impl OpenCADStudio {
.as_mut()
.map(|c| c.on_preview_wires(cur))
.unwrap_or_default();
let preview_hidden = self.tabs[i]
.active_cmd
.as_ref()
.map(|command| command.preview_hidden_handles().to_vec())
.unwrap_or_default();
self.tabs[i]
.scene
.set_command_preview_hidden(&preview_hidden);
self.tabs[i].scene.set_preview_wires(previews);
}
}
+8
View File
@@ -2468,6 +2468,14 @@ impl OpenCADStudio {
previews.push(guide);
}
}
let preview_hidden = self.tabs[i]
.active_cmd
.as_ref()
.map(|command| command.preview_hidden_handles().to_vec())
.unwrap_or_default();
self.tabs[i]
.scene
.set_command_preview_hidden(&preview_hidden);
self.tabs[i].scene.set_preview_wires(previews);
} else {
self.tabs[i].snap_result = None;