forked from 1b-agent/opencadstudio-upstream
Dimensional constraints: dynamic dimension persistence, lock mark, display variables, DIMCONSTRAINT
The dynamic dimension of a dimensional constraint is now linked in the drawing file (AcDbAssocDependency + AcDbAssocDimDependencyBody on the constraint node), reread on load, hidden by DCHIDE/DYNCONSTRAINTDISPLAY and never plotted; it carries a lock mark before its text. New CONSTRAINTNAMEFORMAT and DYNCONSTRAINTDISPLAY variables and the DIMCONSTRAINT option front end. A transformed entity whose own end points a driving dimension measures keeps only the first constraint point, so a scaled line returns to its dimension value. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
72fd3fd35b
commit
971abe64a4
12 files changed
+433
-24
No files matched your search
@@ -2,7 +2,8 @@
|
||||
|
||||
use acadrust::objects::{
|
||||
Assoc2dConstraintGroup, AssocAction, AssocActionDependency, AssocConstraintNode,
|
||||
AssocConstraintNodeData, AssocDependency, AssocEvalValue, AssocEvalVariant,
|
||||
AssocConstraintNodeData, AssocDependency, AssocDimDependencyBody, AssocEvalValue,
|
||||
AssocEvalVariant,
|
||||
AssocGeomDependency, AssocNetwork, AssocPersistentSubentId, AssocValueDependency,
|
||||
AssocVariable, AssociativeData, AssociativeObject, ObjectType,
|
||||
};
|
||||
@@ -1291,6 +1292,67 @@ impl Allocator<'_> {
|
||||
}
|
||||
handle
|
||||
}
|
||||
|
||||
/// The `AcDbAssocDependency` + `AcDbAssocDimDependencyBody` pair that
|
||||
/// ties a dimensional constraint to the dynamic dimension showing it.
|
||||
fn dimension_dependency(
|
||||
&mut self,
|
||||
group_handle: Handle,
|
||||
dimension: Handle,
|
||||
name: &str,
|
||||
dependency_id: i32,
|
||||
) -> Handle {
|
||||
let handle = self.document.allocate_handle();
|
||||
let body = self.insert_associative(
|
||||
handle,
|
||||
"ACDBASSOCDIMDEPENDENCYBODY",
|
||||
"AcDbAssocDimDependencyBody",
|
||||
AssociativeData::DimDependencyBody(AssocDimDependencyBody {
|
||||
dependency_body_version: 1,
|
||||
base_version: 1,
|
||||
name: name.to_string(),
|
||||
class_version: 1,
|
||||
}),
|
||||
);
|
||||
self.insert_associative_at(
|
||||
handle,
|
||||
group_handle,
|
||||
"ACDBASSOCDEPENDENCY",
|
||||
"AcDbAssocDependency",
|
||||
AssociativeData::Dependency(AssocDependency {
|
||||
class_version: 2,
|
||||
is_read_dependency: true,
|
||||
is_write_dependency: true,
|
||||
is_attached_to_object: true,
|
||||
is_delegating_to_owning_action: true,
|
||||
dependent_on: dimension,
|
||||
dependency_body: body,
|
||||
dependency_body_id: dependency_id,
|
||||
..Default::default()
|
||||
}),
|
||||
);
|
||||
// Earlier materializations' dependencies are gone by now; drop
|
||||
// their reactor entries before adding this one.
|
||||
let live: Vec<Handle> = self
|
||||
.document
|
||||
.get_entity(dimension)
|
||||
.map(|entity| {
|
||||
entity
|
||||
.common()
|
||||
.reactors
|
||||
.iter()
|
||||
.copied()
|
||||
.filter(|reactor| self.document.objects.contains_key(reactor))
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or_default();
|
||||
if let Some(entity) = self.document.get_entity_mut(dimension) {
|
||||
let reactors = &mut entity.common_mut().reactors;
|
||||
*reactors = live;
|
||||
reactors.push(handle);
|
||||
}
|
||||
handle
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the dependency on whole-geometry nodes. Implicit points retain a
|
||||
@@ -1355,6 +1417,27 @@ fn set_value_dependency(data: &mut AssocConstraintNodeData, handle: Handle) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Patches a dimensional-constraint node's `dimension_dependency` field.
|
||||
fn set_dimension_dependency(data: &mut AssocConstraintNodeData, handle: Handle) {
|
||||
match data {
|
||||
AssocConstraintNodeData::Distance {
|
||||
dimension_dependency,
|
||||
..
|
||||
}
|
||||
| AssocConstraintNodeData::Angle {
|
||||
dimension_dependency,
|
||||
..
|
||||
}
|
||||
| AssocConstraintNodeData::RadiusDiameter {
|
||||
dimension_dependency,
|
||||
..
|
||||
} => {
|
||||
*dimension_dependency = handle;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
/// Associative classes that must have real class numbers in DWG/DXF.
|
||||
const ASSOC_CLASSES: &[(&str, &str, i32)] = &[
|
||||
(
|
||||
@@ -1366,6 +1449,8 @@ const ASSOC_CLASSES: &[(&str, &str, i32)] = &[
|
||||
("ACDBASSOCVARIABLE", "AcDbAssocVariable", 45),
|
||||
("ACDBASSOCGEOMDEPENDENCY", "AcDbAssocGeomDependency", 29),
|
||||
("ACDBASSOCVALUEDEPENDENCY", "AcDbAssocValueDependency", 29),
|
||||
("ACDBASSOCDEPENDENCY", "AcDbAssocDependency", 29),
|
||||
("ACDBASSOCDIMDEPENDENCYBODY", "AcDbAssocDimDependencyBody", 45),
|
||||
];
|
||||
|
||||
fn ensure_associative_classes_registered(document: &mut CadDocument) {
|
||||
@@ -1806,6 +1891,19 @@ fn driving_value(
|
||||
}
|
||||
}
|
||||
|
||||
/// The dynamic dimension an `AcDbAssocDependency` ties to a dimensional
|
||||
/// constraint node.
|
||||
fn dependency_dimension(document: &CadDocument, dependency: Handle) -> Option<Handle> {
|
||||
let ObjectType::Associative(object) = document.objects.get(&dependency)? else {
|
||||
return None;
|
||||
};
|
||||
let AssociativeData::Dependency(dependency) = &object.data else {
|
||||
return None;
|
||||
};
|
||||
let target = dependency.dependent_on;
|
||||
matches!(document.get_entity(target)?, EntityType::Dimension(_)).then_some(target)
|
||||
}
|
||||
|
||||
fn owning_composite(data: &AssocConstraintNodeData) -> Option<i32> {
|
||||
match data {
|
||||
AssocConstraintNodeData::Geometrical { owner_id, .. }
|
||||
@@ -2250,7 +2348,25 @@ pub(super) fn native_constraint_set(
|
||||
{
|
||||
continue;
|
||||
}
|
||||
set.add(kind, targets, driving);
|
||||
let dimension = match &node.data {
|
||||
AssocConstraintNodeData::Angle {
|
||||
dimension_dependency,
|
||||
..
|
||||
}
|
||||
| AssocConstraintNodeData::Distance {
|
||||
dimension_dependency,
|
||||
..
|
||||
}
|
||||
| AssocConstraintNodeData::RadiusDiameter {
|
||||
dimension_dependency,
|
||||
..
|
||||
} => dependency_dimension(document, *dimension_dependency),
|
||||
_ => None,
|
||||
};
|
||||
let id = set.add(kind, targets, driving);
|
||||
if let Some(dimension) = dimension {
|
||||
set.dimensions.insert(id, dimension);
|
||||
}
|
||||
if let Some(constraint) = set.constraints.last_mut() {
|
||||
constraint.enabled = enabled;
|
||||
constraint.rigid_points = rigid_points;
|
||||
@@ -2429,15 +2545,21 @@ fn materialize_scope(
|
||||
|
||||
// Pass 1 (read-only): constraint-node shapes.
|
||||
let mut needs_value_dependency = Vec::new();
|
||||
let mut dimension_links = Vec::new();
|
||||
let (mut nodes, entities, referenced_entities) = {
|
||||
let mut builder = GroupBuilder::new(document);
|
||||
for constraint in &set.constraints {
|
||||
constraint_node(
|
||||
let node_id = constraint_node(
|
||||
&mut builder,
|
||||
document,
|
||||
constraint,
|
||||
&mut needs_value_dependency,
|
||||
);
|
||||
if let Some((node_id, dimension)) = node_id.zip(set.dimensions.get(&constraint.id)) {
|
||||
if document.get_entity(*dimension).is_some() {
|
||||
dimension_links.push((node_id, *dimension));
|
||||
}
|
||||
}
|
||||
}
|
||||
let GroupBuilder {
|
||||
nodes,
|
||||
@@ -2527,6 +2649,7 @@ fn materialize_scope(
|
||||
}
|
||||
|
||||
let mut group_dependencies = geometry_dependencies;
|
||||
let mut node_names: FxHashMap<i32, String> = FxHashMap::default();
|
||||
for (node_id, driving) in needs_value_dependency {
|
||||
let Some(driving) = driving else { continue };
|
||||
let Ok(resolved) = driving.resolve(parameters) else {
|
||||
@@ -2542,6 +2665,7 @@ fn materialize_scope(
|
||||
}
|
||||
DrivingValue::Literal(_) => (format!("d{node_id}"), resolved.to_string()),
|
||||
};
|
||||
node_names.insert(node_id, name.clone());
|
||||
let variable_handle = allocator.variable(network_handle, &name, &formula, resolved);
|
||||
let dependency_id = group_dependencies.len() as i32 + 1;
|
||||
let dep_handle =
|
||||
@@ -2551,6 +2675,18 @@ fn materialize_scope(
|
||||
set_value_dependency(&mut node.data, dep_handle);
|
||||
}
|
||||
}
|
||||
for (node_id, dimension) in dimension_links {
|
||||
let Some(name) = node_names.get(&node_id) else {
|
||||
continue;
|
||||
};
|
||||
let dependency_id = group_dependencies.len() as i32 + 1;
|
||||
let dep_handle =
|
||||
allocator.dimension_dependency(group_handle?, dimension, name, dependency_id);
|
||||
group_dependencies.push(dep_handle);
|
||||
if let Some(node) = nodes.iter_mut().find(|n| n.node_id == node_id) {
|
||||
set_dimension_dependency(&mut node.data, dep_handle);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(group_handle) = group_handle {
|
||||
let group = Assoc2dConstraintGroup {
|
||||
|
||||
Reference in new issue
Block a user