forked from 1b-agent/opencadstudio-upstream
Merge pull request #1475 from ramox81:codex/xclip-command
This commit is contained in:
commit
58671b191c
10 files changed
+948
-16
No files matched your search
@@ -479,6 +479,42 @@ impl OpenCADStudio {
|
||||
StepInput::SelectionComplete(kept)
|
||||
}
|
||||
|
||||
/// Keeps only block references in a completed selection when the active
|
||||
/// command asks for that (XCLIP), deselecting the rest and saying how many
|
||||
/// were ineligible.
|
||||
fn filter_block_reference_selection(&mut self, input: StepInput) -> StepInput {
|
||||
let StepInput::SelectionComplete(handles) = input else {
|
||||
return input;
|
||||
};
|
||||
let i = self.active_tab;
|
||||
if !self.tabs[i]
|
||||
.active_cmd
|
||||
.as_ref()
|
||||
.is_some_and(|command| command.selection_keeps_block_references())
|
||||
{
|
||||
return StepInput::SelectionComplete(handles);
|
||||
}
|
||||
let (kept, dropped): (Vec<Handle>, Vec<Handle>) = handles.into_iter().partition(|h| {
|
||||
matches!(
|
||||
self.tabs[i].scene.document.get_entity(*h),
|
||||
Some(acadrust::EntityType::Insert(_))
|
||||
)
|
||||
});
|
||||
for handle in &dropped {
|
||||
self.tabs[i].scene.deselect_entity(*handle);
|
||||
}
|
||||
if !dropped.is_empty() {
|
||||
let count = dropped.len();
|
||||
self.command_line.push_info(&if count == 1 {
|
||||
"1 was ineligible for clipping.".to_string()
|
||||
} else {
|
||||
format!("{count} were ineligible for clipping.")
|
||||
});
|
||||
self.refresh_properties();
|
||||
}
|
||||
StepInput::SelectionComplete(kept)
|
||||
}
|
||||
|
||||
/// `feed_command`, also reporting whether the step actually took the input.
|
||||
/// Only a `Text` token can come back unclaimed (`on_text_input` returning
|
||||
/// `None`), which is what lets the caller read it as something else — a
|
||||
@@ -488,6 +524,18 @@ impl OpenCADStudio {
|
||||
// the rest leave the selection and the count of what went is reported,
|
||||
// as the reference does while gathering.
|
||||
let input = self.filter_associative_dimension_selection(input);
|
||||
let input = self.filter_block_reference_selection(input);
|
||||
// XCLIP / CLIP learn which chosen references carry a clip already.
|
||||
{
|
||||
let i = self.active_tab;
|
||||
let tab = &mut self.tabs[i];
|
||||
if let Some(command) = tab.active_cmd.as_mut() {
|
||||
let document = &tab.scene.document;
|
||||
command.inject_clipped(&|h| {
|
||||
crate::scene::pick::xclip::filter_handle(document, h).is_some()
|
||||
});
|
||||
}
|
||||
}
|
||||
// Selection keywords (P / PREVIOUS, L / LAST) consume the token
|
||||
// before it reaches the command (#426).
|
||||
if let StepInput::Text(s) = &input {
|
||||
@@ -3892,6 +3940,12 @@ impl OpenCADStudio {
|
||||
}
|
||||
self.refresh_properties();
|
||||
}
|
||||
CmdResult::XClip { inserts, action } => {
|
||||
self.tabs[i].scene.clear_preview_wire();
|
||||
self.tabs[i].active_cmd = None;
|
||||
self.tabs[i].snap_result = None;
|
||||
self.apply_xclip(i, inserts, action);
|
||||
}
|
||||
CmdResult::OpenPdfImportSettings => self.open_pdf_import_settings(),
|
||||
CmdResult::PdfImportFile(import) => {
|
||||
self.tabs[i].scene.clear_preview_wire();
|
||||
|
||||
@@ -715,9 +715,39 @@ impl OpenCADStudio {
|
||||
Err(_) => self.command_line.push_error(&format!("{path} not found.")),
|
||||
}
|
||||
}
|
||||
"PDFCLIP" | "CLIP" => {
|
||||
"PDFCLIP" => {
|
||||
use crate::command::CadCommand;
|
||||
let command = crate::modules::insert::pdf_clip::PdfClipCommand::new(cmd == "CLIP");
|
||||
let command = crate::modules::insert::pdf_clip::PdfClipCommand::new();
|
||||
self.command_line.push_info(&command.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(command));
|
||||
}
|
||||
"CLIP" => {
|
||||
use crate::command::CadCommand;
|
||||
let command = crate::modules::insert::xclip::ClipCommand::new();
|
||||
self.command_line.push_info(&command.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(command));
|
||||
}
|
||||
// XCLIP takes block references chosen beforehand; others are
|
||||
// left out.
|
||||
"XCLIP" => {
|
||||
use crate::command::CadCommand;
|
||||
use crate::modules::insert::xclip::XclipCommand;
|
||||
let inserts: Vec<acadrust::Handle> = self.tabs[i]
|
||||
.scene
|
||||
.selected_entities()
|
||||
.iter()
|
||||
.filter(|(_, e)| matches!(e, acadrust::EntityType::Insert(_)))
|
||||
.map(|(h, _)| *h)
|
||||
.collect();
|
||||
let command = if inserts.is_empty() {
|
||||
XclipCommand::new()
|
||||
} else {
|
||||
let clipped = inserts.iter().any(|h| {
|
||||
crate::scene::pick::xclip::filter_handle(&self.tabs[i].scene.document, *h)
|
||||
.is_some()
|
||||
});
|
||||
XclipCommand::for_inserts(inserts, clipped)
|
||||
};
|
||||
self.command_line.push_info(&command.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(command));
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ mod layers;
|
||||
pub(crate) mod pdf_import;
|
||||
mod pdf_underlay;
|
||||
mod pdf_dialogs;
|
||||
mod xclip;
|
||||
mod plotvars;
|
||||
mod styleprops;
|
||||
mod view;
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
//! XCLIP's edits on block references, as one undo step.
|
||||
|
||||
use super::*;
|
||||
use crate::modules::insert::xclip::XclipAction;
|
||||
use crate::scene::pick::xclip as clip;
|
||||
|
||||
impl OpenCADStudio {
|
||||
/// A block reference's extents in WCS: its block's objects through the
|
||||
/// insert transform.
|
||||
fn insert_extents(&self, i: usize, insert: acadrust::Handle) -> ([f64; 2], [f64; 2]) {
|
||||
let document = &self.tabs[i].scene.document;
|
||||
let Some(acadrust::EntityType::Insert(ins)) = document.get_entity(insert) else {
|
||||
return ([0.0; 2], [0.0; 2]);
|
||||
};
|
||||
let xform = ins.get_transform();
|
||||
let (mut lo, mut hi) = ([f64::MAX; 2], [f64::MIN; 2]);
|
||||
for e in document.entities_in_block(&ins.block_name) {
|
||||
let (a, b) = crate::scene::convert::tess::entity_bounds_in(document, e);
|
||||
for x in [a[0], b[0]] {
|
||||
for y in [a[1], b[1]] {
|
||||
let w = xform.apply(acadrust::types::Vector3::new(x, y, a[2]));
|
||||
lo = [lo[0].min(w.x), lo[1].min(w.y)];
|
||||
hi = [hi[0].max(w.x), hi[1].max(w.y)];
|
||||
}
|
||||
}
|
||||
}
|
||||
if lo[0] > hi[0] {
|
||||
let p = xform.apply(acadrust::types::Vector3::new(0.0, 0.0, 0.0));
|
||||
return ([p.x, p.y], [p.x, p.y]);
|
||||
}
|
||||
(lo, hi)
|
||||
}
|
||||
|
||||
pub(in crate::app) fn apply_xclip(
|
||||
&mut self,
|
||||
i: usize,
|
||||
inserts: Vec<acadrust::Handle>,
|
||||
action: XclipAction,
|
||||
) {
|
||||
self.push_undo_snapshot(i, "XCLIP");
|
||||
let extents: Vec<_> = inserts.iter().map(|h| self.insert_extents(i, *h)).collect();
|
||||
let current_layer = self.tabs[i].scene.document.header.current_layer_name.clone();
|
||||
let scene = &mut self.tabs[i].scene;
|
||||
let mut polylines = Vec::new();
|
||||
for (n, insert) in inserts.iter().enumerate() {
|
||||
let doc = &mut scene.document;
|
||||
match &action {
|
||||
XclipAction::Enable(on) => {
|
||||
clip::set_insert_clip_enabled(doc, *insert, *on);
|
||||
}
|
||||
XclipAction::Delete => {
|
||||
clip::remove_insert_clip(doc, *insert);
|
||||
}
|
||||
XclipAction::Depth { front, back } => {
|
||||
clip::set_insert_clip_depth(doc, *insert, *front, *back);
|
||||
}
|
||||
XclipAction::New { boundary, inverted } => {
|
||||
clip::set_insert_clip(doc, *insert, boundary, *inverted, extents[n]);
|
||||
}
|
||||
XclipAction::Polyline => {
|
||||
let Some(spatial) = clip::filter_handle(doc, *insert) else {
|
||||
continue;
|
||||
};
|
||||
let (Some(acadrust::objects::ObjectType::SpatialFilter(filter)), Some(acadrust::EntityType::Insert(ins))) =
|
||||
(doc.objects.get(&spatial), doc.get_entity(*insert))
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
let outline = clip::clip_outline_world(doc, filter, &ins.get_transform());
|
||||
let mut pl = acadrust::entities::LwPolyline::from_points(
|
||||
outline
|
||||
.iter()
|
||||
.map(|p| acadrust::types::Vector2::new(p[0], p[1]))
|
||||
.collect(),
|
||||
);
|
||||
pl.is_closed = true;
|
||||
if !current_layer.is_empty() {
|
||||
pl.common.layer = current_layer.clone();
|
||||
}
|
||||
polylines.push(acadrust::EntityType::LwPolyline(pl));
|
||||
}
|
||||
}
|
||||
scene.reseed_derived_caches(*insert);
|
||||
}
|
||||
for pl in polylines {
|
||||
scene.add_entity(pl);
|
||||
}
|
||||
let changes: Vec<_> = inserts
|
||||
.iter()
|
||||
.map(|h| (*h, crate::scene::ChangeKind::Modified))
|
||||
.collect();
|
||||
scene.bump_entities(&changes);
|
||||
self.tabs[i].dirty = true;
|
||||
self.refresh_properties();
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user