forked from 1b-agent/opencadstudio-upstream
feat(sweep): associative swept surfaces in the drawing's network
Swept surfaces made with SURFACEASSOCIATIVITY on keep their profile and path and are written as a swept-surface action of the drawing's associative network, reading the profile and path and their scale and twist expressions. They follow edits of their sources. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
018ac455cb
commit
48c3c433a5
10 files changed
+627
-22
No files matched your search
@@ -1130,6 +1130,49 @@ impl Scene {
|
||||
rebuilt
|
||||
}
|
||||
|
||||
/// Associative swept surfaces follow edits of their profile or path:
|
||||
/// swept again from them with the options they were made with. A
|
||||
/// surface edited along with its sources keeps its own edit. Returns the
|
||||
/// surfaces rebuilt.
|
||||
pub(crate) fn refresh_associative_sweeps(&mut self, changes: &[(Handle, crate::scene::ChangeKind)]) -> Vec<(Handle, crate::scene::ChangeKind)> {
|
||||
use crate::scene::model::sweep_model;
|
||||
let changed = changes.iter()
|
||||
.filter(|(_, kind)| !matches!(kind, crate::scene::ChangeKind::Removed))
|
||||
.map(|(handle, _)| *handle)
|
||||
.collect::<rustc_hash::FxHashSet<_>>();
|
||||
if changed.is_empty() { return Vec::new(); }
|
||||
let linked = self.document.entities()
|
||||
.filter_map(|entity| {
|
||||
let handle = entity.common().handle;
|
||||
let (profile, path) = sweep_model::sweep_sources(entity)?;
|
||||
(!changed.contains(&handle) && (changed.contains(&profile) || changed.contains(&path)))
|
||||
.then_some((handle, profile, path))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let mut rebuilt = Vec::new();
|
||||
for (handle, profile, path) in linked {
|
||||
let (Some(surface), Some(profile), Some(path)) = (
|
||||
self.document.get_entity(handle),
|
||||
self.document.get_entity(profile),
|
||||
self.document.get_entity(path),
|
||||
) else { continue };
|
||||
let Some((record, body)) = sweep_model::resweep_from_sources(surface, profile, path) else { continue };
|
||||
let EntityType::Surface(fresh) = sweep_model::swept_surface_entity(&record) else { continue };
|
||||
let Some(document) = crate::scene::convert::acis_export::solid_to_sat(&body) else { continue };
|
||||
let Some(display) = self.prepare_solid_model_display(handle, &body) else { continue };
|
||||
let before = self.document.get_entity(handle).cloned().map(std::sync::Arc::new);
|
||||
self.record_undo_before(handle, before);
|
||||
if let Some(EntityType::Surface(surface)) = self.document.get_entity_mut(handle) {
|
||||
surface.acis_data = codec::entities::AcisData::from_sat(&document.to_sat_string());
|
||||
surface.surface_data = fresh.surface_data;
|
||||
surface.point_of_reference = fresh.point_of_reference;
|
||||
}
|
||||
self.register_prepared_solid_model(handle, body, display);
|
||||
rebuilt.push((handle, crate::scene::ChangeKind::Modified));
|
||||
}
|
||||
rebuilt
|
||||
}
|
||||
|
||||
/// Swept surfaces the reference keeps associative to a scale or twist
|
||||
/// expression (an associative swept surface action whose value parameter
|
||||
/// depends on an expression variable) are linked the same way here.
|
||||
@@ -1140,6 +1183,7 @@ impl Scene {
|
||||
_ => None,
|
||||
};
|
||||
let mut links = Vec::new();
|
||||
let mut sources = Vec::new();
|
||||
for item in self.document.objects.values() {
|
||||
let ObjectType::Associative(action) = item else { continue };
|
||||
let AssociativeData::Action(action) = &action.data else { continue };
|
||||
@@ -1160,6 +1204,32 @@ impl Scene {
|
||||
if expressions.iter().any(Option::is_some) {
|
||||
links.push((written.dependent_on, expressions));
|
||||
}
|
||||
// The profile and path the action reads.
|
||||
let source = |name: &str| {
|
||||
action.owned_parameters.iter().find_map(|parameter| {
|
||||
let Some(AssociativeData::PathActionParam(path)) = object(*parameter) else { return None };
|
||||
if path.compound.action_param.name != name { return None; }
|
||||
let Some(AssociativeData::EdgeActionParam(edge)) = object(*path.compound.parameters.first()?) else { return None };
|
||||
match object(edge.single_dependency.dependency) {
|
||||
Some(AssociativeData::Dependency(dependency)) => Some(dependency.dependent_on),
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
};
|
||||
if let (Some(profile), Some(path)) = (source("SweepProfile"), source("SweepPath")) {
|
||||
sources.push((written.dependent_on, profile, path));
|
||||
}
|
||||
}
|
||||
for (surface, profile, path) in sources {
|
||||
let Some(entity @ EntityType::Surface(value)) = self.document.get_entity(surface) else { continue };
|
||||
if crate::scene::model::sweep_model::sweep_sources(entity).is_some() { continue; }
|
||||
let codec::entities::SurfaceData::Swept { options, .. } = &value.surface_data else { continue };
|
||||
let linked = crate::command::SweepOptions {
|
||||
align: options.sweep_alignment_flags == 1,
|
||||
bank: options.bank,
|
||||
..crate::command::SweepOptions::default()
|
||||
};
|
||||
crate::scene::model::sweep_model::link_sweep_sources(&mut self.document, surface, profile, path, &linked);
|
||||
}
|
||||
for (handle, expressions) in links {
|
||||
let Some(entity) = self.document.get_entity(handle) else { continue };
|
||||
|
||||
Reference in new issue
Block a user