Files
OneCAD-Draft/src/app/commands/xclip.rs
T
ramox81andClaude Opus 5.5 bd17e7e0d4 CLIP on raster images and viewports, IMAGECLIP, VPCLIP, clip depth rules
- CLIP continues as IMAGECLIP on a raster image and as VPCLIP on a layout
  viewport, with the reference prompts and stored data.
- Image clip boundaries sit on pixel centres, OFF only clears the display
  flag, and an inverted boundary cuts the image.
- VPCLIP polygon (Arc/Length/Close/Undo), clipping object and Delete; the
  viewport shrinks to the boundary's exact extents, view kept.
- XCLIP Clipdepth: Remove clears only the plane asked, a front plane behind
  the back one is rejected with the reference message.
- Attached and imported images carry the reference display flags.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-25 10:33:49 +03:00

100 lines
4.1 KiB
Rust

//! 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: codec::Handle) -> ([f64; 2], [f64; 2]) {
let document = &self.tabs[i].scene.document;
let Some(codec::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(codec::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(codec::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<codec::Handle>,
action: XclipAction,
) {
if let XclipAction::Depth { rejected: true, .. } = action {
self.command_line.push_error(crate::modules::insert::xclip::DEPTH_REJECTED);
}
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(codec::objects::ObjectType::SpatialFilter(filter)), Some(codec::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 = codec::entities::LwPolyline::from_points(
outline
.iter()
.map(|p| codec::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(codec::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();
}
}