forked from 1b-agent/opencadstudio-upstream
fix(block-editor): manage parameter and basepoint graph items
This commit is contained in:
1 parent
4fdf357a7e
commit
286bdf6426
2 files changed
+163
-14
No files matched your search
@@ -93,12 +93,31 @@ pub fn items(doc: &CadDocument, definition: Handle) -> Vec<Item> {
|
|||||||
p.parameter.definition_point,
|
p.parameter.definition_point,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
if let Some((handle, p)) = crate::scene::block_basepoint::parameter(doc, definition) {
|
let basepoint = crate::scene::block_basepoint::parameter(doc, definition)
|
||||||
result.push(single(
|
.map(|(handle, p)| (handle, p.parameter.definition_point))
|
||||||
|
.or_else(|| {
|
||||||
|
let name = doc
|
||||||
|
.block_records
|
||||||
|
.iter()
|
||||||
|
.find(|record| record.handle == definition)?
|
||||||
|
.name
|
||||||
|
.as_str();
|
||||||
|
Some((
|
||||||
|
definition,
|
||||||
|
crate::scene::render_graph::block_base_point(doc, name),
|
||||||
|
))
|
||||||
|
});
|
||||||
|
if let Some((handle, point)) = basepoint {
|
||||||
|
let label = point + Vector3::new(0.25, 0.25, 0.0);
|
||||||
|
let mut graphic = Graphic::default();
|
||||||
|
graphic.lines.push([point, label]);
|
||||||
|
graphic.labels.push((label, "Basepoint".into()));
|
||||||
|
result.push(Item {
|
||||||
handle,
|
handle,
|
||||||
"Basepoint".into(),
|
name: "Basepoint".into(),
|
||||||
p.parameter.definition_point,
|
graphic,
|
||||||
));
|
grips: vec![(Part::Point, point)],
|
||||||
|
});
|
||||||
}
|
}
|
||||||
if let Some(p) = crate::scene::block_visibility::parameter(doc, definition) {
|
if let Some(p) = crate::scene::block_visibility::parameter(doc, definition) {
|
||||||
result.push(single(p.handle, p.name.clone(), p.def_point));
|
result.push(single(p.handle, p.name.clone(), p.def_point));
|
||||||
@@ -178,13 +197,16 @@ pub fn move_part(
|
|||||||
if (at - old).length() < 1e-10 {
|
if (at - old).length() < 1e-10 {
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
let graph = graph(doc, definition)?.ok_or("The authoring graph is unavailable.")?;
|
|
||||||
let mut staged = doc.clone();
|
let mut staged = doc.clone();
|
||||||
if crate::scene::block_basepoint::parameter(doc, definition).is_some_and(|(h, _)| h == handle) {
|
if handle == definition
|
||||||
|
|| crate::scene::block_basepoint::parameter(doc, definition)
|
||||||
|
.is_some_and(|(h, _)| h == handle)
|
||||||
|
{
|
||||||
crate::scene::block_basepoint::create(&mut staged, definition, at)?;
|
crate::scene::block_basepoint::create(&mut staged, definition, at)?;
|
||||||
*doc = staged;
|
*doc = staged;
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
}
|
}
|
||||||
|
let graph = graph(doc, definition)?.ok_or("The authoring graph is unavailable.")?;
|
||||||
let mut moved_grips = vec![];
|
let mut moved_grips = vec![];
|
||||||
match staged
|
match staged
|
||||||
.objects
|
.objects
|
||||||
@@ -287,12 +309,8 @@ pub fn move_part(
|
|||||||
p.definition_label_point = at
|
p.definition_label_point = at
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DynamicBlockData::MoveAction(p) => {
|
DynamicBlockData::MoveAction(p) => p.action.display_location = at,
|
||||||
p.action.display_location = at
|
DynamicBlockData::ArrayAction(p) => p.action.display_location = at,
|
||||||
}
|
|
||||||
DynamicBlockData::ArrayAction(p) => {
|
|
||||||
p.action.display_location = at
|
|
||||||
}
|
|
||||||
DynamicBlockData::StretchAction(p) => p.action.display_location = at,
|
DynamicBlockData::StretchAction(p) => p.action.display_location = at,
|
||||||
DynamicBlockData::FlipAction(p) => p.action.display_location = at,
|
DynamicBlockData::FlipAction(p) => p.action.display_location = at,
|
||||||
DynamicBlockData::ScaleAction(p) | DynamicBlockData::RotateAction(p) => {
|
DynamicBlockData::ScaleAction(p) | DynamicBlockData::RotateAction(p) => {
|
||||||
@@ -412,6 +430,137 @@ pub fn move_part(
|
|||||||
*doc = staged;
|
*doc = staged;
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Remove an authoring parameter together with the graph expressions that it
|
||||||
|
/// owns. Action bindings are reciprocal graph edges, so they are detached from
|
||||||
|
/// both ends before the parameter nodes are removed.
|
||||||
|
pub fn remove_parameter(
|
||||||
|
doc: &mut CadDocument,
|
||||||
|
definition: Handle,
|
||||||
|
handle: Handle,
|
||||||
|
) -> Result<(), &'static str> {
|
||||||
|
let item = items(doc, definition)
|
||||||
|
.into_iter()
|
||||||
|
.find(|item| item.handle == handle)
|
||||||
|
.ok_or("The selected authoring parameter is no longer available.")?;
|
||||||
|
if handle == definition && crate::scene::block_basepoint::parameter(doc, definition).is_none() {
|
||||||
|
return Err("The block basepoint cannot be deleted.");
|
||||||
|
}
|
||||||
|
if matches!(
|
||||||
|
doc.objects.get(&handle),
|
||||||
|
Some(ObjectType::DynamicBlock(object))
|
||||||
|
if matches!(object.data, DynamicBlockData::MoveAction(_) | DynamicBlockData::ArrayAction(_) | DynamicBlockData::StretchAction(_) | DynamicBlockData::FlipAction(_) | DynamicBlockData::ScaleAction(_) | DynamicBlockData::RotateAction(_))
|
||||||
|
) || matches!(
|
||||||
|
doc.objects.get(&handle),
|
||||||
|
Some(ObjectType::BlockVisibilityParameter(_))
|
||||||
|
) {
|
||||||
|
return Err("Delete actions and visibility parameters from their dedicated editor.");
|
||||||
|
}
|
||||||
|
let graph_handle = graph(doc, definition)?.ok_or("The authoring graph is unavailable.")?;
|
||||||
|
let ObjectType::DynamicBlock(graph_object) = doc.objects.get(&graph_handle).unwrap() else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
let DynamicBlockData::EvaluationGraph(original) = &graph_object.data else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
let mut graph = original.clone();
|
||||||
|
let parameter_nodes = graph
|
||||||
|
.nodes
|
||||||
|
.iter()
|
||||||
|
.filter(|node| node.expression == handle)
|
||||||
|
.map(|node| node.id)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
if parameter_nodes.len() != 1 {
|
||||||
|
return Err("The selected parameter has an invalid graph node.");
|
||||||
|
}
|
||||||
|
let parameter_node = parameter_nodes[0];
|
||||||
|
let mut owned_nodes = vec![parameter_node];
|
||||||
|
// Native authoring groups have one grip edge (grip -> parameter) and up
|
||||||
|
// to two component edges (parameter -> grip location components). Classify
|
||||||
|
// endpoint objects as well as edge direction so unrelated graph edges
|
||||||
|
// cannot cause another parameter to be erased.
|
||||||
|
for edge in graph.edges.iter().filter(|edge| edge.next_id == 0) {
|
||||||
|
if edge.destination_node == parameter_node
|
||||||
|
&& graph.nodes.iter().find(|node| node.id == edge.source_node).is_some_and(|node| {
|
||||||
|
matches!(doc.objects.get(&node.expression), Some(ObjectType::DynamicBlock(object)) if matches!(object.data, DynamicBlockData::LinearGrip(_) | DynamicBlockData::PolarGrip(_) | DynamicBlockData::XYGrip(_) | DynamicBlockData::RotationGrip(_) | DynamicBlockData::AlignmentGrip(_) | DynamicBlockData::FlipGrip(_) | DynamicBlockData::LookupGrip(_) | DynamicBlockData::VisibilityGrip(_)))
|
||||||
|
})
|
||||||
|
{
|
||||||
|
owned_nodes.push(edge.source_node);
|
||||||
|
} else if edge.source_node == parameter_node
|
||||||
|
&& graph.nodes.iter().find(|node| node.id == edge.destination_node).is_some_and(|node| {
|
||||||
|
matches!(doc.objects.get(&node.expression), Some(ObjectType::DynamicBlock(object)) if matches!(object.data, DynamicBlockData::GripLocationComponent(_)))
|
||||||
|
})
|
||||||
|
{
|
||||||
|
owned_nodes.push(edge.destination_node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
owned_nodes.sort_unstable();
|
||||||
|
owned_nodes.dedup();
|
||||||
|
let mut edge_ids = graph
|
||||||
|
.edges
|
||||||
|
.iter()
|
||||||
|
.filter(|edge| {
|
||||||
|
owned_nodes.contains(&edge.source_node) || owned_nodes.contains(&edge.destination_node)
|
||||||
|
})
|
||||||
|
.map(|edge| edge.id)
|
||||||
|
.collect::<std::collections::BTreeSet<_>>();
|
||||||
|
// Action relationships use reciprocal next_id=4 edges. Remove their
|
||||||
|
// inverse too, while leaving the action's graph node and object intact.
|
||||||
|
let selected_edges = edge_ids.iter().copied().collect::<Vec<_>>();
|
||||||
|
for selected in selected_edges {
|
||||||
|
let Some(edge) = graph.edges.iter().find(|edge| edge.id == selected) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
if edge.next_id == 4 {
|
||||||
|
let inverse = graph
|
||||||
|
.edges
|
||||||
|
.iter()
|
||||||
|
.find(|candidate| candidate.id == edge.outgoing_edges[4]);
|
||||||
|
let Some(inverse) = inverse.filter(|inverse| {
|
||||||
|
inverse.next_id == 4
|
||||||
|
&& inverse.outgoing_edges[4] == edge.id
|
||||||
|
&& inverse.source_node == edge.destination_node
|
||||||
|
&& inverse.destination_node == edge.source_node
|
||||||
|
}) else {
|
||||||
|
return Err("The parameter has an invalid action binding; it was not deleted.");
|
||||||
|
};
|
||||||
|
edge_ids.insert(inverse.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for edge in edge_ids {
|
||||||
|
crate::scene::block_authoring::graph::remove(&mut graph, edge);
|
||||||
|
}
|
||||||
|
let removed_handles = owned_nodes
|
||||||
|
.iter()
|
||||||
|
.filter_map(|node_id| {
|
||||||
|
graph
|
||||||
|
.nodes
|
||||||
|
.iter()
|
||||||
|
.find(|node| node.id == *node_id)
|
||||||
|
.map(|node| node.expression)
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
graph.nodes.retain(|node| !owned_nodes.contains(&node.id));
|
||||||
|
if let Some(max) = graph.nodes.iter().map(|node| node.next_id).max() {
|
||||||
|
graph.first_node_id = max;
|
||||||
|
graph.first_node_id_copy = max;
|
||||||
|
} else {
|
||||||
|
graph.first_node_id = 0;
|
||||||
|
graph.first_node_id_copy = 0;
|
||||||
|
}
|
||||||
|
if let Some(ObjectType::DynamicBlock(object)) = doc.objects.get_mut(&graph_handle) {
|
||||||
|
object.data = DynamicBlockData::EvaluationGraph(graph);
|
||||||
|
}
|
||||||
|
for owned in removed_handles.into_iter().chain(std::iter::once(handle)) {
|
||||||
|
if owned != graph_handle {
|
||||||
|
doc.objects.remove(&owned);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
doc.block_visibility_params.remove(&handle);
|
||||||
|
let _ = item;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn label_offset(p: &BlockTwoPointParameter, at: Vector3) -> f64 {
|
fn label_offset(p: &BlockTwoPointParameter, at: Vector3) -> f64 {
|
||||||
let d = p.definition_end_point - p.definition_base_point;
|
let d = p.definition_end_point - p.definition_base_point;
|
||||||
let n = Vector3::new(-d.y, d.x, 0.) * (1.0 / d.x.hypot(d.y));
|
let n = Vector3::new(-d.y, d.x, 0.) * (1.0 / d.x.hypot(d.y));
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ pub(crate) fn valid_lists(g: &BlockEvaluationGraph, node: i32) -> bool {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove(g: &mut BlockEvaluationGraph, id: i32) {
|
pub(crate) fn remove(g: &mut BlockEvaluationGraph, id: i32) {
|
||||||
let edge = g.edges.iter().find(|e| e.id == id).unwrap().clone();
|
let edge = g.edges.iter().find(|e| e.id == id).unwrap().clone();
|
||||||
let [pi, ni, po, no, _] = edge.outgoing_edges;
|
let [pi, ni, po, no, _] = edge.outgoing_edges;
|
||||||
for (neighbor, field, value) in [(pi, 1, ni), (ni, 0, pi), (po, 3, no), (no, 2, po)] {
|
for (neighbor, field, value) in [(pi, 1, ni), (ni, 0, pi), (po, 3, no), (no, 2, po)] {
|
||||||
|
|||||||
Reference in new issue
Block a user