Complete jogged radius dimension workflow

This commit is contained in:
ramox81 committed 2026-08-26 17:17:55 +03:00
1 parent bbd58eb9f2
commit 5a9894fd3e
11 files changed
+664 -85

No files matched your search

+7
View File
@@ -1864,6 +1864,12 @@ annotate =
.dimjogged-created-a-jogged-radius-dimension = DIMJOGGED: created a jogged radius dimension.
.dimjogged-invalid-radius = DIMJOGGED: invalid radius.
.dimjogged-select-an-arc-or-circle-first = DIMJOGGED: select an arc or circle first.
.dimjogged-enter-dimension-text-blank-measured = DIMJOGGED Enter dimension text (blank = measured value):
.dimjogged-select-arc-circle-or-polyline-arc = DIMJOGGED Select arc, circle, or polyline arc:
.dimjogged-specify-center-location-override = DIMJOGGED Specify center location override:
.dimjogged-specify-dimension-line-location-text = DIMJOGGED Specify dimension line location [Mtext/Text/Angle]:
.dimjogged-specify-jog-location = DIMJOGGED Specify jog location:
.dimjogged-specify-text-angle-degrees = DIMJOGGED Specify text angle (degrees):
.dimjogline-select-linear-or-aligned-dimension = DIMJOGLINE Select linear or aligned dimension:
.dimjogline-specify-jog-location = DIMJOGLINE Specify jog location:
.dimlinear-enter-dimension-text-blank-measured = DIMLINEAR Enter dimension text (blank = measured value):
@@ -2667,6 +2673,7 @@ properties =
.island-detection-style = Island detection style
.italic = Italic
.jog-angle = Jog Angle
.jogged-radius = Jogged Radius
.jog-x = Jog X
.jog-y = Jog Y
.jog-z = Jog Z
+7
View File
@@ -1844,6 +1844,12 @@ annotate =
.dimjogged-created-a-jogged-radius-dimension = DIMJOGGED: kırıklı yarıçap ölçüsü oluşturuldu.
.dimjogged-invalid-radius = DIMJOGGED: yarıçap geçersiz.
.dimjogged-select-an-arc-or-circle-first = DIMJOGGED: önce bir yay veya daire seçin.
.dimjogged-enter-dimension-text-blank-measured = DIMJOGGED Ölçü metnini girin (boş = ölçülen değer):
.dimjogged-select-arc-circle-or-polyline-arc = DIMJOGGED Yay, daire veya çoklu çizgi yayı seçin:
.dimjogged-specify-center-location-override = DIMJOGGED Geçersiz kılınmış merkez konumunu belirtin:
.dimjogged-specify-dimension-line-location-text = DIMJOGGED Ölçü çizgisinin konumunu belirtin [Çok satırlı metin/Metin/Açı]:
.dimjogged-specify-jog-location = DIMJOGGED Kırılma konumunu belirtin:
.dimjogged-specify-text-angle-degrees = DIMJOGGED Metin açısını belirtin (derece):
.dimjogline-select-linear-or-aligned-dimension = DIMJOGLINE Doğrusal veya hizalı ölçüyü seçin:
.dimjogline-specify-jog-location = DIMJOGLINE Kırılma konumunu belirtin:
.dimlinear-enter-dimension-text-blank-measured = DIMLINEAR Ölçü metnini girin (boş = ölçülen değer):
@@ -2606,6 +2612,7 @@ properties =
.island-detection-style = Ada algılama stili
.italic = İtalik
.jog-angle = Kırılma Açısı
.jogged-radius = Kırıklı Yarıçap
.jog-x = Kırılma X
.jog-y = Kırılma Y
.jog-z = Kırılma Z
+11 -43
View File
@@ -48,6 +48,17 @@ impl OpenCADStudio {
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
}
"DIMJOGGED" | "DIMJOG" => {
use crate::modules::annotate::jogged_radius_dim::JoggedRadiusDimensionCommand;
let defaults = crate::scene::creation_style::current_dimension_defaults(
&self.tabs[i].scene.document,
);
let multiplier = self.tabs[i].scene.creation_annotation_multiplier();
let new_cmd = JoggedRadiusDimensionCommand::new(defaults, multiplier);
self.command_line.push_info(&new_cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
}
"DIMANGULAR" => {
use crate::modules::annotate::angular_dim::AngularDimensionCommand;
let new_cmd = AngularDimensionCommand::new();
@@ -1289,49 +1300,6 @@ impl OpenCADStudio {
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
}
// DIMJOGGED <DJO> — create a jogged radius dimension on the selected
// arc/circle: a standard radius dim carrying an OCS_JOGGED XData marker
// that the geometry generator renders with a foreshortened zig-zag.
"DIMJOGGED" | "DIMJOG" => {
use acadrust::entities::{Dimension, DimensionRadius};
use acadrust::types::Vector3;
use acadrust::xdata::{ExtendedDataRecord, XDataValue};
let found =
self.tabs[i]
.scene
.selected_entities()
.iter()
.find_map(|(_, e)| match e {
acadrust::EntityType::Arc(a) => Some((a.center, a.radius)),
acadrust::EntityType::Circle(c) => Some((c.center, c.radius)),
_ => None,
});
let Some((center, radius)) = found else {
self.command_line
.push_error(crate::t!("DIMJOGGED: select an arc or circle first.").as_ref());
return None;
};
if radius <= 0.0 {
self.command_line.push_error(crate::t!("DIMJOGGED: invalid radius.").as_ref());
return None;
}
let k = std::f64::consts::FRAC_1_SQRT_2;
let arc_point =
Vector3::new(center.x + radius * k, center.y + radius * k, center.z);
let mut dim = DimensionRadius::new(center, arc_point);
dim.base.common.layer = self.tabs[i].active_layer.clone();
let mut rec = ExtendedDataRecord::new("OCS_JOGGED");
rec.add_value(XDataValue::String("1".to_string()));
dim.base.common.extended_data.add_record(rec);
self.push_undo_snapshot(i, "DIMJOGGED");
self.tabs[i]
.scene
.add_entity(acadrust::EntityType::Dimension(Dimension::Radius(dim)));
self.tabs[i].dirty = true;
self.command_line
.push_output(crate::t!("DIMJOGGED: created a jogged radius dimension.").as_ref());
}
// ARCTEXT <text> — lay the text out as one Text entity per character
// along the selected arc, each rotated to follow the tangent.
"ARCTEXT" => {
+23 -16
View File
@@ -1030,22 +1030,19 @@ impl OpenCADStudio {
if !dim_style_names.is_empty() {
// Current style is already shown as text in the geom section;
// replace/upgrade it to a Choice if we have a list.
if let Some(geom) = sections.last_mut() {
// Replace the style name with a choice.
if let Some(prop) =
geom.props.iter_mut().find(|p| p.field == "style_name")
{
let current = match &prop.value {
crate::scene::model::object::PropValue::PlainText(s) => {
s.clone()
}
_ => String::new(),
};
prop.value = crate::scene::model::object::PropValue::Choice {
selected: current,
options: dim_style_names,
};
}
if let Some(prop) = sections
.iter_mut()
.flat_map(|section| section.props.iter_mut())
.find(|property| property.field == "style_name")
{
let current = match &prop.value {
crate::scene::model::object::PropValue::PlainText(s) => s.clone(),
_ => String::new(),
};
prop.value = crate::scene::model::object::PropValue::Choice {
selected: current,
options: dim_style_names,
};
}
}
@@ -1065,6 +1062,16 @@ impl OpenCADStudio {
&self.tabs[i].scene.document,
));
if matches!(d, acadrust::entities::Dimension::LargeRadial(_)) {
if let Some(index) = sections
.iter()
.position(|section| section.title == t!("Misc").as_ref())
{
let misc = sections.remove(index);
sections.push(misc);
}
}
// Prefer entity-level dimension-variable overrides;
// fall back to the assigned style values.
use crate::entities::dim_override as dov;
+71 -23
View File
@@ -108,6 +108,47 @@ fn properties(dim: &Dimension) -> Vec<PropSection> {
],
}];
}
if let Dimension::LargeRadial(d) = dim {
return vec![PropSection {
title: t!("Misc").into_owned(),
props: vec![
Property {
label: t!("Dimension style").into_owned(),
field: "style_name",
value: PropValue::PlainText(d.base.style_name.clone()),
},
edit(t!("Center X").as_ref(), "definition_x", d.definition_point.x),
edit(t!("Center Y").as_ref(), "definition_y", d.definition_point.y),
edit(t!("Center Z").as_ref(), "definition_z", d.definition_point.z),
edit(t!("Chord X").as_ref(), "chord_x", d.chord_point.x),
edit(t!("Chord Y").as_ref(), "chord_y", d.chord_point.y),
edit(t!("Chord Z").as_ref(), "chord_z", d.chord_point.z),
edit(
t!("Override Center X").as_ref(),
"override_x",
d.override_center.x,
),
edit(
t!("Override Center Y").as_ref(),
"override_y",
d.override_center.y,
),
edit(
t!("Override Center Z").as_ref(),
"override_z",
d.override_center.z,
),
edit(t!("Jog X").as_ref(), "jog_x", d.jog_point.x),
edit(t!("Jog Y").as_ref(), "jog_y", d.jog_point.y),
edit(t!("Jog Z").as_ref(), "jog_z", d.jog_point.z),
edit_angle(
t!("Jog Angle").as_ref(),
"jog_angle",
d.jog_angle.to_degrees(),
),
],
}];
}
let compact_linear = match dim {
Dimension::Linear(d) => Some((
&d.base,
@@ -271,21 +312,7 @@ fn properties(dim: &Dimension) -> Vec<PropSection> {
props.push(edit(t!("Leader 2 Y").as_ref(), "leader2_y", d.second_leader_point.y));
props.push(edit(t!("Leader 2 Z").as_ref(), "leader2_z", d.second_leader_point.z));
}
Dimension::LargeRadial(d) => {
props.push(edit(t!("Center X").as_ref(), "definition_x", d.definition_point.x));
props.push(edit(t!("Center Y").as_ref(), "definition_y", d.definition_point.y));
props.push(edit(t!("Center Z").as_ref(), "definition_z", d.definition_point.z));
props.push(edit(t!("Chord X").as_ref(), "chord_x", d.chord_point.x));
props.push(edit(t!("Chord Y").as_ref(), "chord_y", d.chord_point.y));
props.push(edit(t!("Chord Z").as_ref(), "chord_z", d.chord_point.z));
props.push(edit(t!("Override Center X").as_ref(), "override_x", d.override_center.x));
props.push(edit(t!("Override Center Y").as_ref(), "override_y", d.override_center.y));
props.push(edit(t!("Override Center Z").as_ref(), "override_z", d.override_center.z));
props.push(edit(t!("Jog X").as_ref(), "jog_x", d.jog_point.x));
props.push(edit(t!("Jog Y").as_ref(), "jog_y", d.jog_point.y));
props.push(edit(t!("Jog Z").as_ref(), "jog_z", d.jog_point.z));
props.push(edit_angle(t!("Jog Angle").as_ref(), "jog_angle", d.jog_angle.to_degrees()));
}
Dimension::LargeRadial(_) => unreachable!("handled by compact large-radial properties"),
}
vec![PropSection {
title: t!("Geometry").into_owned(),
@@ -1818,7 +1845,7 @@ pub fn style_sections(
let linetype = |code, inherited| {
linetype_name(document, ov::handle(data, code).unwrap_or(inherited))
};
let arrow_1 = if matches!(dimension, Dimension::Radius(_)) {
let arrow_1 = if matches!(dimension, Dimension::Radius(_) | Dimension::LargeRadial(_)) {
let inherited = if s.dimblk1.is_null() { s.dimblk } else { s.dimblk1 };
let inherited_name = if s.dimblk1.is_null() {
&s.dimblk_name
@@ -2520,7 +2547,10 @@ pub fn style_sections(
],
},
];
if matches!(dimension, Dimension::Radius(_) | Dimension::Diameter(_)) {
if matches!(
dimension,
Dimension::Radius(_) | Dimension::Diameter(_) | Dimension::LargeRadial(_)
) {
let dimcen = real(ov::DIMCEN, s.dimcen);
let center_type = if dimcen > 1e-12 {
"Center marks"
@@ -2563,8 +2593,18 @@ pub fn style_sections(
"dim_ext_line_ext",
"dim_ext_line_offset",
];
const LARGE_RADIAL_LINE_FIELDS: &[&str] = &[
"dim_arrowhead_1",
"dim_arrow_size",
"dim_line_lineweight",
"dim_line_1",
"dim_line_color",
"dim_linetype",
];
let retained = if matches!(dimension, Dimension::Diameter(_)) {
DIAMETER_LINE_FIELDS
} else if matches!(dimension, Dimension::LargeRadial(_)) {
LARGE_RADIAL_LINE_FIELDS
} else {
RADIUS_LINE_FIELDS
};
@@ -3405,9 +3445,9 @@ fn tessellate_dimension_inner(
// DIMUPT governs interactive creation-time text placement; saved
// geometry already carries the resulting position.
let _ = s.dimupt;
// DIMARCSYM only applies to arc-length dims; DIMJOGANG only to
// jogged-radius dims. We don't ship those Dimension variants yet,
// so the values are read for round-trip but not drawn.
// Per-entity arc-symbol and jog-angle values are consumed by their
// native dimension variants; the resolved style values remain the
// creation defaults.
let _ = (s.dimarcsym, s.dimjogang);
// DIMUNIT is the obsolete pre-R2000 linear unit format; DIMLUNIT
// supersedes it. Read but not honoured.
@@ -4199,15 +4239,23 @@ fn dimension_geometry(
let override_center = lv(d.override_center);
let (near, far) =
jogged_radial_break(chord, jog, override_center, d.jog_angle as f32);
add_segment(&mut g.dim_lines, chord, near);
add_segment(&mut g.dim_lines, near, far);
add_segment(&mut g.dim_lines, far, override_center);
if !suppress.dim1 {
add_segment(&mut g.dim_lines, chord, near);
add_segment(&mut g.dim_lines, near, far);
add_segment(&mut g.dim_lines, far, override_center);
}
append_arrow(
&mut g,
chord,
normalized_or(near - chord, Vec3::X),
arrow1,
);
append_center_mark(
&mut g,
lv(d.definition_point),
params.dimcen,
(chord - lv(d.definition_point)).length(),
);
}
}
g
+7
View File
@@ -767,6 +767,12 @@ pub(super) fn message_attribute(source: &str) -> Option<(&'static str, &'static
"DIMJOGGED: created a jogged radius dimension." => Some(("annotate", "dimjogged-created-a-jogged-radius-dimension")),
"DIMJOGGED: invalid radius." => Some(("annotate", "dimjogged-invalid-radius")),
"DIMJOGGED: select an arc or circle first." => Some(("annotate", "dimjogged-select-an-arc-or-circle-first")),
"DIMJOGGED Enter dimension text (blank = measured value):" => Some(("annotate", "dimjogged-enter-dimension-text-blank-measured")),
"DIMJOGGED Select arc, circle, or polyline arc:" => Some(("annotate", "dimjogged-select-arc-circle-or-polyline-arc")),
"DIMJOGGED Specify center location override:" => Some(("annotate", "dimjogged-specify-center-location-override")),
"DIMJOGGED Specify dimension line location [Mtext/Text/Angle]:" => Some(("annotate", "dimjogged-specify-dimension-line-location-text")),
"DIMJOGGED Specify jog location:" => Some(("annotate", "dimjogged-specify-jog-location")),
"DIMJOGGED Specify text angle (degrees):" => Some(("annotate", "dimjogged-specify-text-angle-degrees")),
"DIMJOGLINE Select linear or aligned dimension:" => Some(("annotate", "dimjogline-select-linear-or-aligned-dimension")),
"DIMJOGLINE Specify jog location:" => Some(("annotate", "dimjogline-specify-jog-location")),
"DIMJOGLINE: not yet implemented — nothing changed." => Some(("common", "dimjogline-not-yet-implemented-nothing-changed")),
@@ -1298,6 +1304,7 @@ pub(super) fn message_attribute(source: &str) -> Option<(&'static str, &'static
"JUSTIFYTEXT: updated {n} text object(s)." => Some(("annotate", "justifytext-updated-n-text-object-s")),
"Jog Angle" => Some(("properties", "jog-angle")),
"Jog Line" => Some(("annotate", "jog-line")),
"Jogged Radius" => Some(("common", "jogged-radius")),
"Jog X" => Some(("properties", "jog-x")),
"Jog Y" => Some(("properties", "jog-y")),
"Jog Z" => Some(("properties", "jog-z")),
+476
View File
@@ -0,0 +1,476 @@
use acadrust::entities::{Dimension, DimensionLargeRadial};
use acadrust::types::{Handle, Vector3};
use acadrust::EntityType;
use glam::{DVec3, Vec3};
use crate::command::{
CadCommand, CmdOption, CmdResult, DimensionAssociationInput, DimensionAssociationSource,
WorkingPlane,
};
use crate::modules::{IconKind, ModuleEvent, ToolDef};
use crate::scene::creation_style::DimensionCreationDefaults;
use crate::scene::dimension_assoc::RadialSourceGeometry;
use crate::scene::model::wire_model::WireModel;
use crate::t;
pub const ICON: IconKind = IconKind::Svg(include_bytes!("../../../assets/icons/dim_jog.svg"));
pub fn tool() -> ToolDef {
ToolDef {
id: "DIMJOGGED",
label: "Jogged Radius",
icon: ICON,
event: ModuleEvent::Command("DIMJOGGED".to_string()),
}
}
#[derive(Clone, Copy)]
enum Step {
SelectObject,
OverrideCenter(RadialSourceGeometry),
DimLine {
source: RadialSourceGeometry,
override_center: DVec3,
},
Jog {
source: RadialSourceGeometry,
override_center: DVec3,
chord: DVec3,
text_position: DVec3,
},
}
pub struct JoggedRadiusDimensionCommand {
step: Step,
defaults: DimensionCreationDefaults,
editor_height: f64,
text_override: Option<String>,
text_angle: Option<f64>,
awaiting_text: bool,
awaiting_angle: bool,
picked_entity: Option<EntityType>,
source_handle: Option<Handle>,
}
impl JoggedRadiusDimensionCommand {
pub fn new(defaults: DimensionCreationDefaults, annotation_multiplier: f64) -> Self {
let editor_height = defaults.text_height
* defaults.scale
* annotation_multiplier.max(f64::EPSILON);
Self {
step: Step::SelectObject,
defaults,
editor_height,
text_override: None,
text_angle: None,
awaiting_text: false,
awaiting_angle: false,
picked_entity: None,
source_handle: None,
}
}
fn editor_anchor(&self) -> DVec3 {
match self.step {
Step::SelectObject => DVec3::ZERO,
Step::OverrideCenter(source) => dvec(source.point_at_angle(source.start_angle)),
Step::DimLine {
override_center, ..
}
| Step::Jog {
override_center, ..
} => override_center,
}
}
fn commit_dimension(
&self,
source: RadialSourceGeometry,
override_center_world: DVec3,
chord_world: DVec3,
text_world: DVec3,
jog_world: DVec3,
) -> CmdResult {
let plane = source_plane(source);
let center = plane.to_local(dvec(source.center_world()));
let chord = plane.to_local(chord_world);
let override_center = plane.to_local(override_center_world);
let jog = project_jog(override_center, chord, plane.to_local(jog_world));
let text_position = plane.to_local(text_world);
let mut dimension = DimensionLargeRadial::default();
dimension.definition_point = v3(center);
dimension.base.definition_point = v3(center);
dimension.chord_point = v3(chord);
dimension.override_center = v3(override_center);
dimension.jog_point = v3(jog);
dimension.jog_angle = self.defaults.jog_angle;
dimension.base.style_name.clone_from(&self.defaults.style_name);
dimension.base.text_middle_point = v3(text_position);
dimension.base.insertion_point = v3(text_position);
dimension.base.text_user_positioned = true;
dimension.base.actual_measurement = dimension.measurement();
crate::entities::dimension::set_dimension_text_override(
&mut dimension.base,
self.text_override.clone(),
);
if let Some(angle) = self.text_angle {
dimension.base.text_rotation = angle;
}
let association = self.source_handle.map_or_else(
|| DimensionAssociationInput::Explicit(Vec::new()),
|handle| {
DimensionAssociationInput::Explicit(vec![Some(
DimensionAssociationSource::explicit(
handle,
source.marker,
source.angle_at(chord_world.to_array()),
),
)])
},
);
CmdResult::CommitDimension {
entity: plane.place_entity(EntityType::Dimension(Dimension::LargeRadial(
dimension,
))),
association,
}
}
}
impl CadCommand for JoggedRadiusDimensionCommand {
fn set_working_plane(&mut self, _plane: WorkingPlane) {}
fn name(&self) -> &'static str {
"DIMJOGGED"
}
fn prompt(&self) -> String {
if self.awaiting_text {
return t!("DIMJOGGED Enter dimension text (blank = measured value):").into_owned();
}
if self.awaiting_angle {
return t!("DIMJOGGED Specify text angle (degrees):").into_owned();
}
match self.step {
Step::SelectObject => {
t!("DIMJOGGED Select arc, circle, or polyline arc:").into_owned()
}
Step::OverrideCenter(_) => {
t!("DIMJOGGED Specify center location override:").into_owned()
}
Step::DimLine { .. } => t!(
"DIMJOGGED Specify dimension line location [Mtext/Text/Angle]:"
)
.into_owned(),
Step::Jog { .. } => t!("DIMJOGGED Specify jog location:").into_owned(),
}
}
fn on_point(&mut self, point: DVec3) -> CmdResult {
match self.step {
Step::SelectObject => CmdResult::NeedPoint,
Step::OverrideCenter(source) => {
self.step = Step::DimLine {
source,
override_center: point,
};
CmdResult::NeedPoint
}
Step::DimLine {
source,
override_center,
} => {
self.step = Step::Jog {
source,
override_center,
chord: dvec(source.chord_at(point.to_array())),
text_position: point,
};
CmdResult::NeedPoint
}
Step::Jog {
source,
override_center,
chord,
text_position,
} => self.commit_dimension(
source,
override_center,
chord,
text_position,
point,
),
}
}
fn on_enter(&mut self) -> CmdResult {
if self.awaiting_text {
self.awaiting_text = false;
return CmdResult::NeedPoint;
}
if self.awaiting_angle {
self.awaiting_angle = false;
return CmdResult::NeedPoint;
}
CmdResult::Cancel
}
fn on_escape(&mut self) -> CmdResult {
CmdResult::Cancel
}
fn wants_text_input(&self) -> bool {
true
}
fn point_step_accepts_keywords(&self) -> bool {
!self.awaiting_text && !self.awaiting_angle
}
fn wants_text_with_spaces(&self) -> bool {
self.awaiting_text
}
fn options(&self) -> Vec<CmdOption> {
if matches!(self.step, Step::DimLine { .. })
&& !self.awaiting_text
&& !self.awaiting_angle
{
vec![
CmdOption::new("MText", "MTEXT"),
CmdOption::new("Text", "TEXT"),
CmdOption::new("Angle", "ANGLE"),
]
} else {
Vec::new()
}
}
fn on_text_input(&mut self, text: &str) -> Option<CmdResult> {
if self.awaiting_text {
let value = text.trim();
self.text_override = if value.is_empty() || value == "<>" {
None
} else {
Some(value.to_string())
};
self.awaiting_text = false;
return Some(CmdResult::NeedPoint);
}
if self.awaiting_angle {
let value = text.trim();
self.text_angle = if value.is_empty() {
None
} else {
crate::entities::common::parse_typed_angle(value)
};
self.awaiting_angle = false;
return Some(CmdResult::NeedPoint);
}
if !matches!(self.step, Step::DimLine { .. }) {
return None;
}
match text.trim().to_ascii_uppercase().as_str() {
"T" | "TEXT" => {
self.awaiting_text = true;
Some(CmdResult::NeedPoint)
}
"M" | "MTEXT" => Some(CmdResult::SuspendForMTextInput {
pos: self.editor_anchor(),
initial: self.text_override.clone().unwrap_or_default(),
height: self.editor_height,
}),
"A" | "ANGLE" => {
self.awaiting_angle = true;
Some(CmdResult::NeedPoint)
}
_ => None,
}
}
fn on_editor_text(&mut self, value: String) {
let value = value.trim();
self.text_override = if value.is_empty() || value == "<>" {
None
} else {
Some(value.to_string())
};
}
fn on_editor_closed(&mut self, _committed: bool) -> CmdResult {
CmdResult::NeedPoint
}
fn needs_entity_pick(&self) -> bool {
matches!(self.step, Step::SelectObject)
}
fn entity_pick_highlights_hover(&self) -> bool {
true
}
fn inject_before_entity_pick(&self) -> bool {
true
}
fn inject_picked_entity(&mut self, entity: EntityType) {
self.picked_entity = Some(entity);
}
fn on_entity_pick(&mut self, handle: Handle, point: DVec3) -> CmdResult {
let Some(entity) = self.picked_entity.take() else {
return CmdResult::NeedPoint;
};
let Some(source) = crate::scene::dimension_assoc::radial_source_at(
&entity,
Vector3::new(point.x, point.y, point.z),
) else {
return CmdResult::NeedPoint;
};
if !source.radius.is_finite() || source.radius <= 1e-12 {
return CmdResult::NeedPoint;
}
self.source_handle = Some(handle);
self.step = Step::OverrideCenter(source);
CmdResult::NeedPoint
}
fn on_mouse_move(&mut self, point: DVec3) -> Option<WireModel> {
match self.step {
Step::SelectObject => None,
Step::OverrideCenter(source) => Some(preview_wire(vec![
dvec(source.center_world()).as_vec3(),
point.as_vec3(),
])),
Step::DimLine {
source,
override_center,
} => {
let chord = dvec(source.chord_at(point.to_array()));
Some(preview_wire(vec![
override_center.as_vec3(),
chord.as_vec3(),
Vec3::splat(f32::NAN),
chord.as_vec3(),
point.as_vec3(),
]))
}
Step::Jog {
source,
override_center,
chord,
..
} => {
let plane = source_plane(source);
let chord_local = plane.to_local(chord);
let override_local = plane.to_local(override_center);
let jog_local = project_jog(override_local, chord_local, plane.to_local(point));
let (near, far) = jog_break(
chord_local,
jog_local,
override_local,
self.defaults.jog_angle,
);
Some(preview_wire(
[chord_local, near, far, override_local]
.into_iter()
.map(|position| plane.to_world(position).as_vec3())
.collect(),
))
}
}
}
}
fn source_plane(source: RadialSourceGeometry) -> WorkingPlane {
WorkingPlane::new(
DVec3::from_array(source.plane.origin),
DVec3::from_array(source.plane.x_axis),
DVec3::from_array(source.plane.y_axis),
)
}
fn project_jog(override_center: DVec3, chord: DVec3, point: DVec3) -> DVec3 {
let line = chord - override_center;
let length_squared = line.length_squared();
if length_squared <= 1e-18 {
return override_center;
}
let factor = ((point - override_center).dot(line) / length_squared).clamp(0.0, 1.0);
override_center + line * factor
}
fn jog_break(
chord: DVec3,
jog: DVec3,
override_center: DVec3,
jog_angle: f64,
) -> (DVec3, DVec3) {
let radial = (chord - override_center).normalize_or_zero();
let (sin, cos) = jog_angle.sin_cos();
let transverse = DVec3::new(
radial.x * cos - radial.y * sin,
radial.x * sin + radial.y * cos,
0.0,
)
.normalize_or_zero();
let half = ((chord - override_center).length() * 0.04).max(1e-3);
let first = jog - transverse * half;
let second = jog + transverse * half;
if chord.distance_squared(first) <= chord.distance_squared(second) {
(first, second)
} else {
(second, first)
}
}
fn v3(point: DVec3) -> Vector3 {
Vector3::new(point.x, point.y, point.z)
}
fn dvec(point: Vector3) -> DVec3 {
DVec3::new(point.x, point.y, point.z)
}
fn preview_wire(points: Vec<Vec3>) -> WireModel {
WireModel {
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: "dimjogged_preview".to_string(),
points: points.into_iter().map(|point| point.to_array()).collect(),
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::new(),
tangent_geoms: Vec::new(),
aci: 0,
key_vertices: Vec::new(),
aabb: WireModel::UNBOUNDED_AABB,
plinegen: true,
fill_tris: Vec::new(),
fill_tris_low: Vec::new(),
}
}
inventory::submit!(crate::command::CommandRegistration {
names: &["DIMJOGGED", "DIMJOG"]
});
+6
View File
@@ -15,6 +15,7 @@ pub mod dimspace;
pub mod dimtedit;
pub mod leader_cmd;
pub mod linear_dim;
pub mod jogged_radius_dim;
pub mod mleader_cmd;
pub mod mleader_edit;
pub mod mtext;
@@ -103,6 +104,11 @@ impl CadModule for AnnotateModule {
radius_dim::tool().label,
radius_dim::tool().icon,
),
(
jogged_radius_dim::tool().id,
jogged_radius_dim::tool().label,
jogged_radius_dim::tool().icon,
),
(
diameter_dim::tool().id,
diameter_dim::tool().label,
+14 -3
View File
@@ -1218,9 +1218,11 @@ fn explode_dimension(dim: &Dimension, doc: &CadDocument) -> Vec<EntityType> {
d.override_center,
d.jog_angle,
);
result.push(make_seg(&d.chord_point, &near, &dim_c));
result.push(make_seg(&near, &far, &dim_c));
result.push(make_seg(&far, &d.override_center, &dim_c));
if !met.dimsd1 {
result.push(make_seg(&d.chord_point, &near, &dim_c));
result.push(make_seg(&near, &far, &dim_c));
result.push(make_seg(&far, &d.override_center, &dim_c));
}
let len = ((near.x - d.chord_point.x).powi(2)
+ (near.y - d.chord_point.y).powi(2))
.sqrt()
@@ -1232,6 +1234,15 @@ fn explode_dimension(dim: &Dimension, doc: &CadDocument) -> Vec<EntityType> {
&met.arrow1,
&dim_c,
));
let radius = ((d.definition_point.x - d.chord_point.x).powi(2)
+ (d.definition_point.y - d.chord_point.y).powi(2))
.sqrt();
result.extend(dim_center_mark(
d.definition_point,
met.dimcen,
radius,
&dim_c,
));
}
}
+4
View File
@@ -18,6 +18,7 @@ pub struct DimensionCreationDefaults {
pub text_height: f64,
pub gap: f64,
pub arrow_size: f64,
pub jog_angle: f64,
pub scale: f64,
pub annotative: bool,
}
@@ -92,6 +93,9 @@ pub fn current_dimension_defaults(doc: &CadDocument) -> DimensionCreationDefault
text_height: style.as_ref().map_or(2.5, |style| style.dimtxt),
gap: style.as_ref().map_or(0.625, |style| style.dimgap),
arrow_size: style.as_ref().map_or(2.5, |style| style.dimasz),
jog_angle: style
.as_ref()
.map_or(std::f64::consts::FRAC_PI_4, |style| style.dimjogang),
scale: style.as_ref().map_or(1.0, |style| {
if style.annotative {
1.0
+38
View File
@@ -460,6 +460,7 @@ pub(crate) fn radial_extension_points(
let target_point = match dimension_entity {
Dimension::Radius(radius) => radius.definition_point,
Dimension::Diameter(diameter) => diameter.angle_vertex,
Dimension::LargeRadial(radial) => radial.chord_point,
_ => return None,
};
let association = document.objects.values().find_map(|object| {
@@ -556,6 +557,11 @@ impl Scene {
diameter.measurement() * 0.5,
diameter.angle_vertex,
)),
Dimension::LargeRadial(radial) => Some((
radial.definition_point,
radial.measurement(),
radial.chord_point,
)),
_ => None,
};
if let Some((center, measured_radius, chord)) = radial_data {
@@ -711,6 +717,11 @@ impl Scene {
diameter.measurement() * 0.5,
diameter.angle_vertex,
)),
Dimension::LargeRadial(radial) => Some((
radial.definition_point,
radial.measurement(),
radial.chord_point,
)),
_ => None,
};
if let Some((center, radius, chord)) = radial_data {
@@ -892,6 +903,33 @@ impl Scene {
}
diameter.base.actual_measurement = diameter.measurement();
}
Dimension::LargeRadial(radial) => {
let Some((source, angle)) = radial_source else {
continue;
};
let old_center = radial.definition_point;
let new_center = source.center_world();
let new_chord = source.point_at_angle(angle);
let center_delta = Vector3::new(
new_center.x - old_center.x,
new_center.y - old_center.y,
new_center.z - old_center.z,
);
radial.definition_point = new_center;
radial.base.definition_point = new_center;
radial.chord_point = new_chord;
if point_distance_squared(center_delta, Vector3::default()) > 1e-18 {
radial.override_center = radial.override_center + center_delta;
radial.jog_point = radial.jog_point + center_delta;
if radial.base.text_user_positioned {
radial.base.text_middle_point =
radial.base.text_middle_point + center_delta;
radial.base.insertion_point =
radial.base.insertion_point + center_delta;
}
}
radial.base.actual_measurement = radial.measurement();
}
Dimension::Ordinate(ordinate) => {
if let Some(feature) = resolved[0] {
ordinate.feature_location = feature;