diff --git a/src/scene/centerline.rs b/src/scene/centerline.rs
index 2438506a7..ecb735cd4 100644
--- a/src/scene/centerline.rs
+++ b/src/scene/centerline.rs
@@ -435,3 +435,15 @@ pub(crate) fn center_measure_is_relative(specification: &str) -> bool {
let text = specification.trim();
text.eq_ignore_ascii_case("ByLineType") || text.ends_with(['x', 'X'])
}
+
+/// Whether `entity` carries a centerline association.
+///
+/// Used to keep the drawing-wide scan off the edit path: only an entity that
+/// actually carries one can make the scan necessary.
+pub(crate) fn entity_has_center_association(entity: &EntityType) -> bool {
+ let EntityType::Line(line) = entity else {
+ return false;
+ };
+ CenterLineAssociation::read(&line.common.extended_data)
+ .is_some_and(|association| association.associated)
+}
diff --git a/src/scene/centermark.rs b/src/scene/centermark.rs
index fd719775b..979ed0741 100644
--- a/src/scene/centermark.rs
+++ b/src/scene/centermark.rs
@@ -473,3 +473,15 @@ impl Scene {
candidates.len()
}
}
+
+/// Whether `entity` carries a center mark association.
+///
+/// Used to keep the drawing-wide scan off the edit path: only an entity that
+/// actually carries one can make the scan necessary.
+pub(crate) fn entity_has_center_association(entity: &EntityType) -> bool {
+ let EntityType::Line(line) = entity else {
+ return false;
+ };
+ CenterMarkAssociation::read(&line.common.extended_data)
+ .is_some_and(|association| association.associated)
+}
diff --git a/src/scene/mod.rs b/src/scene/mod.rs
index fff1285b1..71847384e 100644
--- a/src/scene/mod.rs
+++ b/src/scene/mod.rs
@@ -1859,6 +1859,15 @@ pub struct Scene {
/// Boundary source → associative hatch handles. Source edits reuse this
/// index instead of scanning the document during every drag step.
associative_hatch_source_cache: RefCell