Implement associative center mark workflow

This commit is contained in:
ramox81 committed 2026-08-21 12:05:18 +03:00
1 parent 0ae6355c2c
commit ff09fc7800
18 files changed
+936 -40

No files matched your search

+119 -2
View File
@@ -8,22 +8,26 @@
// `m = radius * 0.2`. Both lines are committed at once and the command ends.
use acadrust::types::Vector3;
use acadrust::entities::{CenterMarkAssociation, CenterMarkSource};
use acadrust::{EntityType, Handle, Line};
use glam::DVec3;
use crate::t;
use crate::command::{CadCommand, CmdResult};
use crate::modules::{IconKind, ModuleEvent, ToolDef};
use crate::scene::centerline::{
center_measure_is_relative, resolve_center_measure, CenterLineSettings,
};
// ── Ribbon definition ─────────────────────────────────────────────────────
#[allow(dead_code)] // ribbon definition ready for wiring; command works via the command line
pub fn tool() -> ToolDef {
ToolDef {
id: "DIMCENTER",
id: "CENTERMARK",
label: "Center Mark",
icon: IconKind::Svg(include_bytes!("../../../../assets/icons/line.svg")),
event: ModuleEvent::Command("DIMCENTER".to_string()),
event: ModuleEvent::Command("CENTERMARK".to_string()),
}
}
@@ -133,6 +137,119 @@ impl CadCommand for DimCenterCommand {
}
}
/// Associative centre-mark command. Each accepted pick commits one smart mark
/// and keeps the command active so further circular objects can be selected.
pub struct CenterMarkCommand {
picked: Option<EntityType>,
settings: CenterLineSettings,
}
impl CenterMarkCommand {
pub(crate) fn new(settings: CenterLineSettings) -> Self {
Self { picked: None, settings }
}
fn build_mark(
&self,
source: CenterMarkSource,
center: DVec3,
radius: f64,
x: DVec3,
y: DVec3,
) -> EntityType {
let diameter = radius * 2.0;
let association = CenterMarkAssociation {
source,
plane_origin: Vector3::new(center.x, center.y, center.z),
plane_x: Vector3::new(x.x, x.y, x.z),
plane_y: Vector3::new(y.x, y.y, y.z),
center: Vector3::new(center.x, center.y, center.z),
radius,
cross_size: resolve_center_measure(&self.settings.cross_size, diameter, 0.1),
cross_gap: resolve_center_measure(&self.settings.cross_gap, diameter, 0.05),
cross_size_relative: center_measure_is_relative(&self.settings.cross_size),
cross_gap_relative: center_measure_is_relative(&self.settings.cross_gap),
extension_length: self.settings.extension,
length_adjustments: [0.0; 4],
overshoots: [0.0; 4],
show_extensions: self.settings.mark_extensions,
associated: true,
};
let mut line = Line::from_points(
Vector3::new(center.x, center.y, center.z),
Vector3::new(center.x, center.y, center.z),
);
crate::scene::centermark::update_carrier(&mut line, &association);
EntityType::Line(line)
}
}
impl CadCommand for CenterMarkCommand {
fn name(&self) -> &'static str { "CENTERMARK" }
fn prompt(&self) -> String {
t!("CENTERMARK Select arc or circle <finish>:").into_owned()
}
fn needs_entity_pick(&self) -> bool { true }
fn inject_before_entity_pick(&self) -> bool { true }
fn inject_picked_entity(&mut self, entity: EntityType) {
self.picked = Some(entity);
}
fn on_entity_pick(&mut self, handle: Handle, point: DVec3) -> CmdResult {
if handle.is_null() {
return CmdResult::NeedPoint;
}
let Some((source, center, radius, x, y)) = self
.picked
.as_ref()
.and_then(|entity| crate::scene::centermark::picked_mark_source(entity, handle, point))
else {
return CmdResult::NeedPoint;
};
CmdResult::CommitEntity(self.build_mark(source, center, radius, x, y))
}
fn on_point(&mut self, _point: DVec3) -> CmdResult { CmdResult::NeedPoint }
fn on_enter(&mut self) -> CmdResult { CmdResult::Cancel }
fn on_escape(&mut self) -> CmdResult { CmdResult::Cancel }
}
pub struct CenterMarkReassociateCommand {
target: Handle,
picked: Option<EntityType>,
}
impl CenterMarkReassociateCommand {
pub fn new(target: Handle) -> Self {
Self { target, picked: None }
}
}
impl CadCommand for CenterMarkReassociateCommand {
fn name(&self) -> &'static str { "CENTERREASSOCIATE" }
fn prompt(&self) -> String {
t!("CENTERREASSOCIATE Select new arc or circle:").into_owned()
}
fn needs_entity_pick(&self) -> bool { true }
fn inject_before_entity_pick(&self) -> bool { true }
fn inject_picked_entity(&mut self, entity: EntityType) { self.picked = Some(entity); }
fn on_entity_pick(&mut self, source: Handle, point: DVec3) -> CmdResult {
let valid = self.picked.as_ref().and_then(|entity| {
crate::scene::centermark::picked_mark_source(entity, source, point)
}).is_some();
if !valid {
return CmdResult::NeedPoint;
}
CmdResult::ReassociateCenterMark { target: self.target, source, point }
}
fn on_point(&mut self, _point: DVec3) -> CmdResult { CmdResult::NeedPoint }
fn on_enter(&mut self) -> CmdResult { CmdResult::Cancel }
fn on_escape(&mut self) -> CmdResult { CmdResult::Cancel }
}
// ── Autocomplete registry ─────────────────────────────────
inventory::submit!(crate::command::CommandRegistration {
names: &["DIMCENTER", "DCE", "CENTERMARK"]
+21
View File
@@ -63,6 +63,27 @@ pub fn explode_polyline_segments(entity: &EntityType) -> Vec<EntityType> {
/// Returns an empty vec if the entity cannot be exploded.
pub fn explode_entity(entity: &EntityType, document: &CadDocument) -> Vec<EntityType> {
match entity {
EntityType::Line(line) => {
let Some(association) = acadrust::entities::CenterMarkAssociation::read(
&line.common.extended_data,
) else {
return vec![];
};
crate::scene::centermark::mark_segments(&association)
.into_iter()
.map(|segment| {
let mut common = line.common.clone();
common.handle = Handle::NULL;
acadrust::entities::CenterMarkAssociation::remove(&mut common.extended_data);
EntityType::Line(LineEnt {
common,
start: Vector3::new(segment[0].x, segment[0].y, segment[0].z),
end: Vector3::new(segment[1].x, segment[1].y, segment[1].z),
..LineEnt::new()
})
})
.collect()
}
EntityType::LwPolyline(p) => explode_lwpolyline(p),
EntityType::Polyline2D(p) => explode_polyline2d(p),
EntityType::Polyline(p) => explode_polyline(p),