Merge pull request #886 from ramox81/codex/complete-jogged-radius-dimension
Complete jogged radius dimension workflow
This commit is contained in:
commit
ba91095bcf
11 changed files with 747 additions and 85 deletions
|
|
@ -1864,6 +1864,12 @@ annotate =
|
||||||
.dimjogged-created-a-jogged-radius-dimension = DIMJOGGED: created a jogged radius dimension.
|
.dimjogged-created-a-jogged-radius-dimension = DIMJOGGED: created a jogged radius dimension.
|
||||||
.dimjogged-invalid-radius = DIMJOGGED: invalid radius.
|
.dimjogged-invalid-radius = DIMJOGGED: invalid radius.
|
||||||
.dimjogged-select-an-arc-or-circle-first = DIMJOGGED: select an arc or circle first.
|
.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-select-linear-or-aligned-dimension = DIMJOGLINE Select linear or aligned dimension:
|
||||||
.dimjogline-specify-jog-location = DIMJOGLINE Specify jog location:
|
.dimjogline-specify-jog-location = DIMJOGLINE Specify jog location:
|
||||||
.dimlinear-enter-dimension-text-blank-measured = DIMLINEAR Enter dimension text (blank = measured value):
|
.dimlinear-enter-dimension-text-blank-measured = DIMLINEAR Enter dimension text (blank = measured value):
|
||||||
|
|
@ -2667,6 +2673,7 @@ properties =
|
||||||
.island-detection-style = Island detection style
|
.island-detection-style = Island detection style
|
||||||
.italic = Italic
|
.italic = Italic
|
||||||
.jog-angle = Jog Angle
|
.jog-angle = Jog Angle
|
||||||
|
.jogged-radius = Jogged Radius
|
||||||
.jog-x = Jog X
|
.jog-x = Jog X
|
||||||
.jog-y = Jog Y
|
.jog-y = Jog Y
|
||||||
.jog-z = Jog Z
|
.jog-z = Jog Z
|
||||||
|
|
|
||||||
|
|
@ -1844,6 +1844,12 @@ annotate =
|
||||||
.dimjogged-created-a-jogged-radius-dimension = DIMJOGGED: kırıklı yarıçap ölçüsü oluşturuldu.
|
.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-invalid-radius = DIMJOGGED: yarıçap geçersiz.
|
||||||
.dimjogged-select-an-arc-or-circle-first = DIMJOGGED: önce bir yay veya daire seçin.
|
.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-select-linear-or-aligned-dimension = DIMJOGLINE Doğrusal veya hizalı ölçüyü seçin:
|
||||||
.dimjogline-specify-jog-location = DIMJOGLINE Kırılma konumunu belirtin:
|
.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):
|
.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
|
.island-detection-style = Ada algılama stili
|
||||||
.italic = İtalik
|
.italic = İtalik
|
||||||
.jog-angle = Kırılma Açısı
|
.jog-angle = Kırılma Açısı
|
||||||
|
.jogged-radius = Kırıklı Yarıçap
|
||||||
.jog-x = Kırılma X
|
.jog-x = Kırılma X
|
||||||
.jog-y = Kırılma Y
|
.jog-y = Kırılma Y
|
||||||
.jog-z = Kırılma Z
|
.jog-z = Kırılma Z
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,17 @@ impl OpenCADStudio {
|
||||||
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
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" => {
|
"DIMANGULAR" => {
|
||||||
use crate::modules::annotate::angular_dim::AngularDimensionCommand;
|
use crate::modules::annotate::angular_dim::AngularDimensionCommand;
|
||||||
let new_cmd = AngularDimensionCommand::new();
|
let new_cmd = AngularDimensionCommand::new();
|
||||||
|
|
@ -1293,49 +1304,6 @@ impl OpenCADStudio {
|
||||||
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
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
|
// ARCTEXT <text> — lay the text out as one Text entity per character
|
||||||
// along the selected arc, each rotated to follow the tangent.
|
// along the selected arc, each rotated to follow the tangent.
|
||||||
"ARCTEXT" => {
|
"ARCTEXT" => {
|
||||||
|
|
|
||||||
|
|
@ -1031,22 +1031,19 @@ impl OpenCADStudio {
|
||||||
if !dim_style_names.is_empty() {
|
if !dim_style_names.is_empty() {
|
||||||
// Current style is already shown as text in the geom section;
|
// Current style is already shown as text in the geom section;
|
||||||
// replace/upgrade it to a Choice if we have a list.
|
// replace/upgrade it to a Choice if we have a list.
|
||||||
if let Some(geom) = sections.last_mut() {
|
if let Some(prop) = sections
|
||||||
// Replace the style name with a choice.
|
.iter_mut()
|
||||||
if let Some(prop) =
|
.flat_map(|section| section.props.iter_mut())
|
||||||
geom.props.iter_mut().find(|p| p.field == "style_name")
|
.find(|property| property.field == "style_name")
|
||||||
{
|
{
|
||||||
let current = match &prop.value {
|
let current = match &prop.value {
|
||||||
crate::scene::model::object::PropValue::PlainText(s) => {
|
crate::scene::model::object::PropValue::PlainText(s) => s.clone(),
|
||||||
s.clone()
|
_ => String::new(),
|
||||||
}
|
};
|
||||||
_ => String::new(),
|
prop.value = crate::scene::model::object::PropValue::Choice {
|
||||||
};
|
selected: current,
|
||||||
prop.value = crate::scene::model::object::PropValue::Choice {
|
options: dim_style_names,
|
||||||
selected: current,
|
};
|
||||||
options: dim_style_names,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1066,6 +1063,16 @@ impl OpenCADStudio {
|
||||||
&self.tabs[i].scene.document,
|
&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;
|
// Prefer entity-level dimension-variable overrides;
|
||||||
// fall back to the assigned style values.
|
// fall back to the assigned style values.
|
||||||
use crate::entities::dim_override as dov;
|
use crate::entities::dim_override as dov;
|
||||||
|
|
|
||||||
|
|
@ -108,6 +108,52 @@ 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(),
|
||||||
|
),
|
||||||
|
ro(
|
||||||
|
t!("Measurement").as_ref(),
|
||||||
|
"measurement",
|
||||||
|
format!("{:.4}", d.measurement()),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
}];
|
||||||
|
}
|
||||||
let compact_linear = match dim {
|
let compact_linear = match dim {
|
||||||
Dimension::Linear(d) => Some((
|
Dimension::Linear(d) => Some((
|
||||||
&d.base,
|
&d.base,
|
||||||
|
|
@ -281,21 +327,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 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));
|
props.push(edit(t!("Leader 2 Z").as_ref(), "leader2_z", d.second_leader_point.z));
|
||||||
}
|
}
|
||||||
Dimension::LargeRadial(d) => {
|
Dimension::LargeRadial(_) => unreachable!("handled by compact large-radial properties"),
|
||||||
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()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
vec![PropSection {
|
vec![PropSection {
|
||||||
title: t!("Geometry").into_owned(),
|
title: t!("Geometry").into_owned(),
|
||||||
|
|
@ -1828,7 +1860,7 @@ pub fn style_sections(
|
||||||
let linetype = |code, inherited| {
|
let linetype = |code, inherited| {
|
||||||
linetype_name(document, ov::handle(data, code).unwrap_or(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 = if s.dimblk1.is_null() { s.dimblk } else { s.dimblk1 };
|
||||||
let inherited_name = if s.dimblk1.is_null() {
|
let inherited_name = if s.dimblk1.is_null() {
|
||||||
&s.dimblk_name
|
&s.dimblk_name
|
||||||
|
|
@ -2530,7 +2562,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 dimcen = real(ov::DIMCEN, s.dimcen);
|
||||||
let center_type = if dimcen > 1e-12 {
|
let center_type = if dimcen > 1e-12 {
|
||||||
"Center marks"
|
"Center marks"
|
||||||
|
|
@ -2573,8 +2608,18 @@ pub fn style_sections(
|
||||||
"dim_ext_line_ext",
|
"dim_ext_line_ext",
|
||||||
"dim_ext_line_offset",
|
"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(_)) {
|
let retained = if matches!(dimension, Dimension::Diameter(_)) {
|
||||||
DIAMETER_LINE_FIELDS
|
DIAMETER_LINE_FIELDS
|
||||||
|
} else if matches!(dimension, Dimension::LargeRadial(_)) {
|
||||||
|
LARGE_RADIAL_LINE_FIELDS
|
||||||
} else {
|
} else {
|
||||||
RADIUS_LINE_FIELDS
|
RADIUS_LINE_FIELDS
|
||||||
};
|
};
|
||||||
|
|
@ -3097,6 +3142,28 @@ fn tessellate_dimension_inner(
|
||||||
return wires;
|
return wires;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if let Dimension::LargeRadial(radial) = dim {
|
||||||
|
if let Some((local_radial, normal)) = large_radial_dimension_in_ocs(radial) {
|
||||||
|
let mut wires = tessellate_dimension_inner(
|
||||||
|
document,
|
||||||
|
handle,
|
||||||
|
&Dimension::LargeRadial(local_radial),
|
||||||
|
selected,
|
||||||
|
entity_color,
|
||||||
|
line_weight_px,
|
||||||
|
anno_scale,
|
||||||
|
selected_set,
|
||||||
|
active_viewport,
|
||||||
|
bg_color,
|
||||||
|
None,
|
||||||
|
world_per_pixel,
|
||||||
|
);
|
||||||
|
for wire in &mut wires {
|
||||||
|
map_wire_ocs_to_wcs(wire, normal);
|
||||||
|
}
|
||||||
|
return wires;
|
||||||
|
}
|
||||||
|
}
|
||||||
let name = handle.value().to_string();
|
let name = handle.value().to_string();
|
||||||
// (Baked-block fast path moved up into scene::tessellate_entity so the
|
// (Baked-block fast path moved up into scene::tessellate_entity so the
|
||||||
// recursive call goes through the LOD ladder, not the kernel path.)
|
// recursive call goes through the LOD ladder, not the kernel path.)
|
||||||
|
|
@ -3464,8 +3531,7 @@ fn tessellate_dimension_inner(
|
||||||
// DIMUPT governs interactive creation-time text placement; saved
|
// DIMUPT governs interactive creation-time text placement; saved
|
||||||
// geometry already carries the resulting position.
|
// geometry already carries the resulting position.
|
||||||
let _ = s.dimupt;
|
let _ = s.dimupt;
|
||||||
// DIMJOGANG is consumed by the jogged-radius path.
|
let _ = (s.dimarcsym, s.dimjogang);
|
||||||
let _ = s.dimjogang;
|
|
||||||
// DIMUNIT is the obsolete pre-R2000 linear unit format; DIMLUNIT
|
// DIMUNIT is the obsolete pre-R2000 linear unit format; DIMLUNIT
|
||||||
// supersedes it. Read but not honoured.
|
// supersedes it. Read but not honoured.
|
||||||
let _ = s.dimunit;
|
let _ = s.dimunit;
|
||||||
|
|
@ -4345,15 +4411,23 @@ fn dimension_geometry(
|
||||||
let override_center = lv(d.override_center);
|
let override_center = lv(d.override_center);
|
||||||
let (near, far) =
|
let (near, far) =
|
||||||
jogged_radial_break(chord, jog, override_center, d.jog_angle as f32);
|
jogged_radial_break(chord, jog, override_center, d.jog_angle as f32);
|
||||||
add_segment(&mut g.dim_lines, chord, near);
|
if !suppress.dim1 {
|
||||||
add_segment(&mut g.dim_lines, near, far);
|
add_segment(&mut g.dim_lines, chord, near);
|
||||||
add_segment(&mut g.dim_lines, far, override_center);
|
add_segment(&mut g.dim_lines, near, far);
|
||||||
|
add_segment(&mut g.dim_lines, far, override_center);
|
||||||
|
}
|
||||||
append_arrow(
|
append_arrow(
|
||||||
&mut g,
|
&mut g,
|
||||||
chord,
|
chord,
|
||||||
normalized_or(near - chord, Vec3::X),
|
normalized_or(near - chord, Vec3::X),
|
||||||
arrow1,
|
arrow1,
|
||||||
);
|
);
|
||||||
|
append_center_mark(
|
||||||
|
&mut g,
|
||||||
|
lv(d.definition_point),
|
||||||
|
params.dimcen,
|
||||||
|
(chord - lv(d.definition_point)).length(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
g
|
g
|
||||||
|
|
@ -4789,6 +4863,43 @@ pub(crate) fn arc_dimension_in_ocs(
|
||||||
Some((local, normal))
|
Some((local, normal))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn large_radial_dimension_in_ocs(
|
||||||
|
dimension: &DimensionLargeRadial,
|
||||||
|
) -> Option<(DimensionLargeRadial, Vector3)> {
|
||||||
|
let length = dimension.base.normal.length();
|
||||||
|
if !length.is_finite() || length <= 1.0e-12 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let normal = dimension.base.normal / length;
|
||||||
|
if (normal - Vector3::UNIT_Z).length() <= 1.0e-12 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let normal_tuple = (normal.x, normal.y, normal.z);
|
||||||
|
let to_ocs = |point: Vector3| {
|
||||||
|
let point = crate::scene::view::transform::wcs_point_to_ocs(
|
||||||
|
(point.x, point.y, point.z),
|
||||||
|
normal_tuple,
|
||||||
|
);
|
||||||
|
Vector3::new(point.0, point.1, point.2)
|
||||||
|
};
|
||||||
|
let mut local = dimension.clone();
|
||||||
|
local.definition_point = to_ocs(dimension.definition_point);
|
||||||
|
let elevation = local.definition_point.z;
|
||||||
|
let on_plane = |point: Vector3| {
|
||||||
|
let mut point = to_ocs(point);
|
||||||
|
point.z = elevation;
|
||||||
|
point
|
||||||
|
};
|
||||||
|
local.chord_point = on_plane(dimension.chord_point);
|
||||||
|
local.override_center = on_plane(dimension.override_center);
|
||||||
|
local.jog_point = on_plane(dimension.jog_point);
|
||||||
|
local.base.definition_point = local.definition_point;
|
||||||
|
local.base.text_middle_point = on_plane(dimension.base.text_middle_point);
|
||||||
|
local.base.insertion_point = on_plane(dimension.base.insertion_point);
|
||||||
|
local.base.normal = Vector3::UNIT_Z;
|
||||||
|
Some((local, normal))
|
||||||
|
}
|
||||||
|
|
||||||
fn map_wire_ocs_to_wcs(wire: &mut WireModel, normal: Vector3) {
|
fn map_wire_ocs_to_wcs(wire: &mut WireModel, normal: Vector3) {
|
||||||
let normal_tuple = (normal.x, normal.y, normal.z);
|
let normal_tuple = (normal.x, normal.y, normal.z);
|
||||||
let map = |x: f64, y: f64, z: f64| {
|
let map = |x: f64, y: f64, z: f64| {
|
||||||
|
|
|
||||||
|
|
@ -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: created a jogged radius dimension." => Some(("annotate", "dimjogged-created-a-jogged-radius-dimension")),
|
||||||
"DIMJOGGED: invalid radius." => Some(("annotate", "dimjogged-invalid-radius")),
|
"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: 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 Select linear or aligned dimension:" => Some(("annotate", "dimjogline-select-linear-or-aligned-dimension")),
|
||||||
"DIMJOGLINE Specify jog location:" => Some(("annotate", "dimjogline-specify-jog-location")),
|
"DIMJOGLINE Specify jog location:" => Some(("annotate", "dimjogline-specify-jog-location")),
|
||||||
"DIMJOGLINE: not yet implemented — nothing changed." => Some(("common", "dimjogline-not-yet-implemented-nothing-changed")),
|
"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")),
|
"JUSTIFYTEXT: updated {n} text object(s)." => Some(("annotate", "justifytext-updated-n-text-object-s")),
|
||||||
"Jog Angle" => Some(("properties", "jog-angle")),
|
"Jog Angle" => Some(("properties", "jog-angle")),
|
||||||
"Jog Line" => Some(("annotate", "jog-line")),
|
"Jog Line" => Some(("annotate", "jog-line")),
|
||||||
|
"Jogged Radius" => Some(("common", "jogged-radius")),
|
||||||
"Jog X" => Some(("properties", "jog-x")),
|
"Jog X" => Some(("properties", "jog-x")),
|
||||||
"Jog Y" => Some(("properties", "jog-y")),
|
"Jog Y" => Some(("properties", "jog-y")),
|
||||||
"Jog Z" => Some(("properties", "jog-z")),
|
"Jog Z" => Some(("properties", "jog-z")),
|
||||||
|
|
|
||||||
479
src/modules/annotate/jogged_radius_dim.rs
Normal file
479
src/modules/annotate/jogged_radius_dim.rs
Normal file
|
|
@ -0,0 +1,479 @@
|
||||||
|
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 display_scale = if defaults.annotative {
|
||||||
|
annotation_multiplier.max(f64::EPSILON)
|
||||||
|
} else {
|
||||||
|
defaults.scale
|
||||||
|
};
|
||||||
|
let editor_height = defaults.text_height * display_scale;
|
||||||
|
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"]
|
||||||
|
});
|
||||||
|
|
@ -16,6 +16,7 @@ pub mod dimspace;
|
||||||
pub mod dimtedit;
|
pub mod dimtedit;
|
||||||
pub mod leader_cmd;
|
pub mod leader_cmd;
|
||||||
pub mod linear_dim;
|
pub mod linear_dim;
|
||||||
|
pub mod jogged_radius_dim;
|
||||||
pub mod mleader_cmd;
|
pub mod mleader_cmd;
|
||||||
pub mod mleader_edit;
|
pub mod mleader_edit;
|
||||||
pub mod mtext;
|
pub mod mtext;
|
||||||
|
|
@ -109,6 +110,11 @@ impl CadModule for AnnotateModule {
|
||||||
radius_dim::tool().label,
|
radius_dim::tool().label,
|
||||||
radius_dim::tool().icon,
|
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().id,
|
||||||
diameter_dim::tool().label,
|
diameter_dim::tool().label,
|
||||||
|
|
|
||||||
|
|
@ -927,6 +927,24 @@ fn explode_dimension(dim: &Dimension, doc: &CadDocument) -> Vec<EntityType> {
|
||||||
.collect();
|
.collect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if let Dimension::LargeRadial(radial) = dim {
|
||||||
|
if let Some((local_radial, normal)) =
|
||||||
|
crate::entities::dimension::large_radial_dimension_in_ocs(radial)
|
||||||
|
{
|
||||||
|
let (x_axis, y_axis) = crate::scene::view::transform::ocs_axes((
|
||||||
|
normal.x, normal.y, normal.z,
|
||||||
|
));
|
||||||
|
let plane = WorkingPlane::new(
|
||||||
|
DVec3::ZERO,
|
||||||
|
DVec3::new(x_axis.0, x_axis.1, x_axis.2),
|
||||||
|
DVec3::new(y_axis.0, y_axis.1, y_axis.2),
|
||||||
|
);
|
||||||
|
return explode_dimension(&Dimension::LargeRadial(local_radial), doc)
|
||||||
|
.into_iter()
|
||||||
|
.map(|entity| plane.place_entity(entity))
|
||||||
|
.collect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let base = dim.base();
|
let base = dim.base();
|
||||||
let met = dim_metrics(dim, doc);
|
let met = dim_metrics(dim, doc);
|
||||||
|
|
@ -1235,9 +1253,11 @@ fn explode_dimension(dim: &Dimension, doc: &CadDocument) -> Vec<EntityType> {
|
||||||
d.override_center,
|
d.override_center,
|
||||||
d.jog_angle,
|
d.jog_angle,
|
||||||
);
|
);
|
||||||
result.push(make_seg(&d.chord_point, &near, &dim_c));
|
if !met.dimsd1 {
|
||||||
result.push(make_seg(&near, &far, &dim_c));
|
result.push(make_seg(&d.chord_point, &near, &dim_c));
|
||||||
result.push(make_seg(&far, &d.override_center, &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)
|
let len = ((near.x - d.chord_point.x).powi(2)
|
||||||
+ (near.y - d.chord_point.y).powi(2))
|
+ (near.y - d.chord_point.y).powi(2))
|
||||||
.sqrt()
|
.sqrt()
|
||||||
|
|
@ -1249,6 +1269,15 @@ fn explode_dimension(dim: &Dimension, doc: &CadDocument) -> Vec<EntityType> {
|
||||||
&met.arrow1,
|
&met.arrow1,
|
||||||
&dim_c,
|
&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,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ pub struct DimensionCreationDefaults {
|
||||||
pub text_height: f64,
|
pub text_height: f64,
|
||||||
pub gap: f64,
|
pub gap: f64,
|
||||||
pub arrow_size: f64,
|
pub arrow_size: f64,
|
||||||
|
pub jog_angle: f64,
|
||||||
pub scale: f64,
|
pub scale: f64,
|
||||||
pub annotative: bool,
|
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),
|
text_height: style.as_ref().map_or(2.5, |style| style.dimtxt),
|
||||||
gap: style.as_ref().map_or(0.625, |style| style.dimgap),
|
gap: style.as_ref().map_or(0.625, |style| style.dimgap),
|
||||||
arrow_size: style.as_ref().map_or(2.5, |style| style.dimasz),
|
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| {
|
scale: style.as_ref().map_or(1.0, |style| {
|
||||||
if style.annotative {
|
if style.annotative {
|
||||||
1.0
|
1.0
|
||||||
|
|
|
||||||
|
|
@ -559,6 +559,7 @@ pub(crate) fn radial_extension_points(
|
||||||
let target_point = match dimension_entity {
|
let target_point = match dimension_entity {
|
||||||
Dimension::Radius(radius) => radius.definition_point,
|
Dimension::Radius(radius) => radius.definition_point,
|
||||||
Dimension::Diameter(diameter) => diameter.angle_vertex,
|
Dimension::Diameter(diameter) => diameter.angle_vertex,
|
||||||
|
Dimension::LargeRadial(radial) => radial.chord_point,
|
||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
let association = document.objects.values().find_map(|object| {
|
let association = document.objects.values().find_map(|object| {
|
||||||
|
|
@ -655,6 +656,11 @@ impl Scene {
|
||||||
diameter.measurement() * 0.5,
|
diameter.measurement() * 0.5,
|
||||||
diameter.angle_vertex,
|
diameter.angle_vertex,
|
||||||
)),
|
)),
|
||||||
|
Dimension::LargeRadial(radial) => Some((
|
||||||
|
radial.definition_point,
|
||||||
|
radial.measurement(),
|
||||||
|
radial.chord_point,
|
||||||
|
)),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
if let Some((center, measured_radius, chord)) = radial_data {
|
if let Some((center, measured_radius, chord)) = radial_data {
|
||||||
|
|
@ -810,6 +816,11 @@ impl Scene {
|
||||||
diameter.measurement() * 0.5,
|
diameter.measurement() * 0.5,
|
||||||
diameter.angle_vertex,
|
diameter.angle_vertex,
|
||||||
)),
|
)),
|
||||||
|
Dimension::LargeRadial(radial) => Some((
|
||||||
|
radial.definition_point,
|
||||||
|
radial.measurement(),
|
||||||
|
radial.chord_point,
|
||||||
|
)),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
if let Some((center, radius, chord)) = radial_data {
|
if let Some((center, radius, chord)) = radial_data {
|
||||||
|
|
@ -1003,6 +1014,33 @@ impl Scene {
|
||||||
}
|
}
|
||||||
diameter.base.actual_measurement = diameter.measurement();
|
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) => {
|
Dimension::Ordinate(ordinate) => {
|
||||||
if let Some(feature) = resolved[0] {
|
if let Some(feature) = resolved[0] {
|
||||||
ordinate.feature_location = feature;
|
ordinate.feature_location = feature;
|
||||||
|
|
@ -1098,7 +1136,6 @@ impl Scene {
|
||||||
}
|
}
|
||||||
arc.base.actual_measurement = arc.measurement();
|
arc.base.actual_measurement = arc.measurement();
|
||||||
}
|
}
|
||||||
_ => continue,
|
|
||||||
}
|
}
|
||||||
dimension.base_mut().actual_measurement = dimension.measurement();
|
dimension.base_mut().actual_measurement = dimension.measurement();
|
||||||
refreshed.push((association.dimension, ChangeKind::Modified));
|
refreshed.push((association.dimension, ChangeKind::Modified));
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue