forked from 1b-agent/opencadstudio-upstream
fix(annotate): align annotation ribbon workflows
This commit is contained in:
1 parent
fc1788df65
commit
6505b27818
26 files changed
+1921
-345
No files matched your search
@@ -1,13 +1,10 @@
|
||||
// DIMTEDIT command — reposition the text of an existing dimension.
|
||||
//
|
||||
// Workflow:
|
||||
// 1. Pick a dimension entity
|
||||
// 2. Click the new text position (entity data is injected by update.rs after pick)
|
||||
// DIMTEDIT — reposition, justify, home or rotate existing dimension text.
|
||||
|
||||
use acadrust::entities::{AttachmentPointType, Dimension};
|
||||
use acadrust::{EntityType, Handle};
|
||||
use glam::DVec3;
|
||||
|
||||
use crate::command::{CadCommand, CmdResult};
|
||||
use crate::command::{CadCommand, CmdOption, CmdResult, InputKind};
|
||||
use crate::modules::{IconKind, ModuleEvent, ToolDef};
|
||||
use crate::scene::model::wire_model::WireModel;
|
||||
use crate::t;
|
||||
@@ -29,18 +26,47 @@ enum Step {
|
||||
handle: Handle,
|
||||
entity: Option<EntityType>,
|
||||
},
|
||||
EnterAngle {
|
||||
handle: Handle,
|
||||
entity: Option<EntityType>,
|
||||
},
|
||||
}
|
||||
|
||||
pub struct DimTeditCommand {
|
||||
step: Step,
|
||||
picked_entity: Option<EntityType>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum Placement {
|
||||
Left,
|
||||
Right,
|
||||
Center,
|
||||
Home,
|
||||
}
|
||||
|
||||
impl DimTeditCommand {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
step: Step::PickDim,
|
||||
picked_entity: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn finish_placement(
|
||||
handle: Handle,
|
||||
entity: &mut Option<EntityType>,
|
||||
placement: Placement,
|
||||
) -> CmdResult {
|
||||
let Some(mut entity) = entity.take() else {
|
||||
return CmdResult::NeedPoint;
|
||||
};
|
||||
let EntityType::Dimension(dimension) = &mut entity else {
|
||||
return CmdResult::Cancel;
|
||||
};
|
||||
apply_placement(dimension, placement);
|
||||
CmdResult::UpdateEntityAndFinish { handle, entity }
|
||||
}
|
||||
}
|
||||
|
||||
impl CadCommand for DimTeditCommand {
|
||||
@@ -49,47 +75,123 @@ impl CadCommand for DimTeditCommand {
|
||||
}
|
||||
|
||||
fn prompt(&self) -> String {
|
||||
match &self.step {
|
||||
match self.step {
|
||||
Step::PickDim => t!("DIMTEDIT Select dimension:").into_owned(),
|
||||
Step::PickTextPos { .. } => {
|
||||
t!("DIMTEDIT Specify new location for dimension text:").into_owned()
|
||||
Step::PickTextPos { .. } => t!(
|
||||
"DIMTEDIT Specify new location for dimension text or [Left/Right/Center/Home/Angle]:"
|
||||
)
|
||||
.into_owned(),
|
||||
Step::EnterAngle { .. } => {
|
||||
t!("DIMTEDIT Specify angle for dimension text:").into_owned()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn options(&self) -> Vec<CmdOption> {
|
||||
if !matches!(self.step, Step::PickTextPos { .. }) {
|
||||
return Vec::new();
|
||||
}
|
||||
vec![
|
||||
CmdOption::new("Left", "LEFT"),
|
||||
CmdOption::new("Right", "RIGHT"),
|
||||
CmdOption::new("Center", "CENTER"),
|
||||
CmdOption::new("Home", "HOME"),
|
||||
CmdOption::new("Angle", "ANGLE"),
|
||||
]
|
||||
}
|
||||
|
||||
fn input_kind(&self) -> InputKind {
|
||||
match self.step {
|
||||
Step::EnterAngle { .. } => InputKind::SingleToken,
|
||||
Step::PickDim | Step::PickTextPos { .. } => InputKind::Point,
|
||||
}
|
||||
}
|
||||
|
||||
fn point_step_accepts_keywords(&self) -> bool {
|
||||
matches!(self.step, Step::PickTextPos { .. })
|
||||
}
|
||||
|
||||
fn needs_entity_pick(&self) -> bool {
|
||||
matches!(self.step, Step::PickDim)
|
||||
}
|
||||
|
||||
fn on_entity_pick(&mut self, handle: Handle, _pt: DVec3) -> CmdResult {
|
||||
fn inject_before_entity_pick(&self) -> bool {
|
||||
matches!(self.step, Step::PickDim)
|
||||
}
|
||||
|
||||
fn on_entity_pick(&mut self, handle: Handle, _point: DVec3) -> CmdResult {
|
||||
if handle.is_null() {
|
||||
return CmdResult::NeedPoint;
|
||||
}
|
||||
if !matches!(self.picked_entity.as_ref(), Some(EntityType::Dimension(_))) {
|
||||
self.picked_entity = None;
|
||||
return CmdResult::ReportError(
|
||||
t!("DIMTEDIT: select a dimension.").into_owned(),
|
||||
);
|
||||
}
|
||||
self.step = Step::PickTextPos {
|
||||
handle,
|
||||
entity: None,
|
||||
entity: self.picked_entity.take(),
|
||||
};
|
||||
CmdResult::NeedPoint
|
||||
}
|
||||
|
||||
fn on_point(&mut self, pt: DVec3) -> CmdResult {
|
||||
if let Step::PickTextPos { handle, entity } = &mut self.step {
|
||||
let h = *handle;
|
||||
if let Some(mut ent) = entity.take() {
|
||||
if let EntityType::Dimension(ref mut d) = ent {
|
||||
let new_pt = acadrust::types::Vector3::new(pt.x, pt.y, pt.z);
|
||||
d.base_mut().text_middle_point = new_pt;
|
||||
d.base_mut().insertion_point = new_pt;
|
||||
// Pin the text to this location, else the renderer recomputes
|
||||
// the style-default placement and the move is discarded.
|
||||
d.base_mut().text_user_positioned = true;
|
||||
fn on_text_input(&mut self, text: &str) -> Option<CmdResult> {
|
||||
let keyword = text.trim().to_ascii_uppercase();
|
||||
match &mut self.step {
|
||||
Step::PickTextPos { handle, entity } => match keyword.as_str() {
|
||||
"L" | "LEFT" => Some(Self::finish_placement(*handle, entity, Placement::Left)),
|
||||
"R" | "RIGHT" => {
|
||||
Some(Self::finish_placement(*handle, entity, Placement::Right))
|
||||
}
|
||||
return CmdResult::ReplaceEntity(h, vec![ent]);
|
||||
"C" | "CENTER" => {
|
||||
Some(Self::finish_placement(*handle, entity, Placement::Center))
|
||||
}
|
||||
"H" | "HOME" => Some(Self::finish_placement(*handle, entity, Placement::Home)),
|
||||
"A" | "ANGLE" => {
|
||||
let handle = *handle;
|
||||
let entity = entity.take();
|
||||
self.step = Step::EnterAngle { handle, entity };
|
||||
Some(CmdResult::NeedPoint)
|
||||
}
|
||||
_ => None,
|
||||
},
|
||||
Step::EnterAngle { handle, entity } => {
|
||||
let degrees = text.trim().parse::<f64>().ok()?;
|
||||
let Some(mut entity) = entity.take() else {
|
||||
return Some(CmdResult::NeedPoint);
|
||||
};
|
||||
let EntityType::Dimension(dimension) = &mut entity else {
|
||||
return Some(CmdResult::Cancel);
|
||||
};
|
||||
dimension.base_mut().text_rotation = degrees.to_radians();
|
||||
Some(CmdResult::UpdateEntityAndFinish {
|
||||
handle: *handle,
|
||||
entity,
|
||||
})
|
||||
}
|
||||
// No entity yet — wait for inject
|
||||
CmdResult::NeedPoint
|
||||
} else {
|
||||
CmdResult::NeedPoint
|
||||
Step::PickDim => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn on_point(&mut self, point: DVec3) -> CmdResult {
|
||||
let Step::PickTextPos { handle, entity } = &mut self.step else {
|
||||
return CmdResult::NeedPoint;
|
||||
};
|
||||
let Some(mut entity) = entity.take() else {
|
||||
return CmdResult::NeedPoint;
|
||||
};
|
||||
let EntityType::Dimension(dimension) = &mut entity else {
|
||||
return CmdResult::Cancel;
|
||||
};
|
||||
let point = acadrust::types::Vector3::new(point.x, point.y, point.z);
|
||||
let base = dimension.base_mut();
|
||||
base.text_middle_point = point;
|
||||
base.insertion_point = point;
|
||||
base.text_user_positioned = true;
|
||||
CmdResult::UpdateEntityAndFinish {
|
||||
handle: *handle,
|
||||
entity,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,63 +199,102 @@ impl CadCommand for DimTeditCommand {
|
||||
CmdResult::Cancel
|
||||
}
|
||||
|
||||
fn on_mouse_move(&mut self, pt: DVec3) -> Option<WireModel> { let pt = pt.as_vec3();
|
||||
fn on_mouse_move(&mut self, point: DVec3) -> Option<WireModel> {
|
||||
if !matches!(self.step, Step::PickTextPos { .. }) {
|
||||
return None;
|
||||
}
|
||||
let point = point.as_vec3();
|
||||
let d = 0.2_f32;
|
||||
Some(WireModel {
|
||||
bg_adapt: None,
|
||||
point_marker: None,
|
||||
taper_widths: Vec::new(),
|
||||
pattern_stations: Vec::new(),
|
||||
world_width: 0.0,
|
||||
depth_override: None,
|
||||
display_visible: true,
|
||||
plot_visible: true,
|
||||
fill_is_3d: false,
|
||||
fill_is_2d_solid: false,
|
||||
render_instance: None,
|
||||
pick_tris: Vec::new(),
|
||||
pick_tris_low: Vec::new(),
|
||||
dash_from_start: false,
|
||||
dash_align_end: None,
|
||||
text_verts: Vec::new(),
|
||||
name: "dimtedit_preview".into(),
|
||||
// Marker box in the XY drawing plane (Z is elevation, ~0). The old
|
||||
// box varied Z, so in the top-down view it collapsed to a flat line
|
||||
// instead of a square. (#150)
|
||||
points: vec![
|
||||
[pt.x - d, pt.y - d, pt.z],
|
||||
[pt.x + d, pt.y - d, pt.z],
|
||||
[pt.x + d, pt.y + d, pt.z],
|
||||
[pt.x - d, pt.y + d, pt.z],
|
||||
[pt.x - d, pt.y - d, pt.z],
|
||||
[point.x - d, point.y - d, point.z],
|
||||
[point.x + d, point.y - d, point.z],
|
||||
[point.x + d, point.y + d, point.z],
|
||||
[point.x - d, point.y + d, point.z],
|
||||
[point.x - d, point.y - d, point.z],
|
||||
],
|
||||
points_low: Vec::new(),
|
||||
color: WireModel::CYAN,
|
||||
selected: false,
|
||||
pattern_length: 0.0,
|
||||
pattern: [0.0; 8],
|
||||
line_weight_px: 1.0,
|
||||
snap_pts: vec![],
|
||||
tangent_geoms: vec![],
|
||||
aci: 0,
|
||||
key_vertices: vec![],
|
||||
aabb: WireModel::UNBOUNDED_AABB,
|
||||
plinegen: true,
|
||||
fill_tris: vec![],
|
||||
fill_tris_low: Vec::new(),
|
||||
..WireModel::default()
|
||||
})
|
||||
}
|
||||
|
||||
fn inject_picked_entity(&mut self, entity: EntityType) {
|
||||
if let Step::PickTextPos { entity: slot, .. } = &mut self.step {
|
||||
*slot = Some(entity);
|
||||
match &mut self.step {
|
||||
Step::PickTextPos { entity: slot, .. } | Step::EnterAngle { entity: slot, .. } => {
|
||||
*slot = Some(entity);
|
||||
}
|
||||
Step::PickDim => self.picked_entity = Some(entity),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn dimension_line_endpoints(dimension: &Dimension) -> Option<(DVec3, DVec3)> {
|
||||
let (first, second, definition, axis) = match dimension {
|
||||
Dimension::Linear(value) => (
|
||||
value.first_point,
|
||||
value.second_point,
|
||||
value.definition_point,
|
||||
DVec3::new(value.rotation.cos(), value.rotation.sin(), 0.0),
|
||||
),
|
||||
Dimension::Aligned(value) => {
|
||||
let delta = DVec3::new(
|
||||
value.second_point.x - value.first_point.x,
|
||||
value.second_point.y - value.first_point.y,
|
||||
0.0,
|
||||
);
|
||||
if delta.length_squared() <= 1.0e-18 {
|
||||
return None;
|
||||
}
|
||||
(
|
||||
value.first_point,
|
||||
value.second_point,
|
||||
value.definition_point,
|
||||
delta.normalize(),
|
||||
)
|
||||
}
|
||||
_ => return None,
|
||||
};
|
||||
let first = DVec3::new(first.x, first.y, first.z);
|
||||
let second = DVec3::new(second.x, second.y, second.z);
|
||||
let definition = DVec3::new(definition.x, definition.y, definition.z);
|
||||
let perpendicular = DVec3::new(-axis.y, axis.x, 0.0);
|
||||
let project = |point: DVec3| {
|
||||
point + perpendicular * (definition - point).dot(perpendicular)
|
||||
};
|
||||
let first = project(first);
|
||||
let second = project(second);
|
||||
if first.dot(axis) <= second.dot(axis) {
|
||||
Some((first, second))
|
||||
} else {
|
||||
Some((second, first))
|
||||
}
|
||||
}
|
||||
|
||||
// ── Autocomplete registry ─────────────────────────────────
|
||||
inventory::submit!(crate::command::CommandRegistration { names: &["DIMTED", "DIMTEDIT"] }); // DimTeditCommand
|
||||
fn apply_placement(dimension: &mut Dimension, placement: Placement) {
|
||||
if matches!(placement, Placement::Home) {
|
||||
let base = dimension.base_mut();
|
||||
base.text_middle_point = acadrust::types::Vector3::ZERO;
|
||||
base.insertion_point = acadrust::types::Vector3::ZERO;
|
||||
base.text_user_positioned = false;
|
||||
base.attachment_point = AttachmentPointType::MiddleCenter;
|
||||
return;
|
||||
}
|
||||
let Some((left, right)) = dimension_line_endpoints(dimension) else {
|
||||
return;
|
||||
};
|
||||
let (point, attachment) = match placement {
|
||||
Placement::Left => (left, AttachmentPointType::MiddleLeft),
|
||||
Placement::Right => (right, AttachmentPointType::MiddleRight),
|
||||
Placement::Center => ((left + right) * 0.5, AttachmentPointType::MiddleCenter),
|
||||
Placement::Home => unreachable!(),
|
||||
};
|
||||
let point = acadrust::types::Vector3::new(point.x, point.y, point.z);
|
||||
let base = dimension.base_mut();
|
||||
base.text_middle_point = point;
|
||||
base.insertion_point = point;
|
||||
base.text_user_positioned = true;
|
||||
base.attachment_point = attachment;
|
||||
}
|
||||
|
||||
inventory::submit!(crate::command::CommandRegistration { names: &["DIMTED", "DIMTEDIT"] });
|
||||
Reference in new issue
Block a user