fix(dimension): complete ordinate workflow

This commit is contained in:
ramox81 2026-08-25 17:10:55 +03:00
commit e34b67c709
9 changed files with 442 additions and 78 deletions

2
Cargo.lock generated
View file

@ -72,7 +72,7 @@ checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618"
[[package]]
name = "acadrust"
version = "0.4.1"
source = "git+https://git@github.com/HakanSeven12/cadcodec.git?rev=5b2ae66#5b2ae66d0bd7b0da2d13392d3c3332f8a39caa1f"
source = "git+https://git@github.com/HakanSeven12/cadcodec.git?rev=564859e#564859e77197fdb98193b2477115709e6821229b"
dependencies = [
"ahash 0.8.12",
"anyhow",

View file

@ -27,7 +27,7 @@ glam = { version = "0.33", features = ["bytemuck"] }
rfd = "0.17"
clap = { version = "4", features = ["derive"] }
env_logger = "0.11"
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "5b2ae66", features = ["serde"] }
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "564859e", features = ["serde"] }
cadkernel = { git = "https://github.com/HakanSeven12/cadkernel.git", rev = "b2b1d4b", features = ["acis", "offset"] }
dwg-thumbnailer = { path = "crates/dwg-thumbnailer" }
flate2 = "1"
@ -61,7 +61,7 @@ iced_core = { git = "https://github.com/iced-rs/iced.git", rev = "23604ff22ab0aa
iced_widget = { git = "https://github.com/iced-rs/iced.git", rev = "23604ff22ab0aad9e00b9327cb7b8546ed84db39" }
[patch."https://github.com/HakanSeven12/cadcodec.git"]
acadrust = { git = "https://git@github.com/HakanSeven12/cadcodec.git", rev = "5b2ae66" }
acadrust = { git = "https://git@github.com/HakanSeven12/cadcodec.git", rev = "564859e" }
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
ocs_plugin_api = { path = "crates/ocs_plugin_api", features = ["host"] }

View file

@ -1246,6 +1246,12 @@ impl OpenCADStudio {
.document
.header
.dimension_associativity;
let single_source_dimension = matches!(
&entity,
acadrust::EntityType::Dimension(
acadrust::entities::Dimension::Ordinate(_)
)
);
let pending = if association_mode == 0 {
let layer = self.tabs[i].active_layer.clone();
if layer != "0" || entity.as_entity().layer().is_empty() {
@ -1295,7 +1301,13 @@ impl OpenCADStudio {
if association_mode == 2 {
let sources = source.map_or_else(
|| self.tabs[i].scene.infer_dimension_sources(handle),
|source| [Some(source), Some(source)],
|source| {
if single_source_dimension {
[Some(source), None]
} else {
[Some(source), Some(source)]
}
},
);
self.tabs[i]
.scene
@ -3066,7 +3078,6 @@ impl OpenCADStudio {
stretched |= mv(&mut d.definition_point);
}
Dimension::Ordinate(d) => {
stretched |= mv(&mut d.definition_point);
stretched |= mv(&mut d.feature_location);
stretched |= mv(&mut d.leader_endpoint);
}

View file

@ -1154,10 +1154,17 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
.zip(self.tabs[i].selected_grips.iter())
.find(|(owner, g)| **owner == popup.handle && g.id == popup.grip_id)
{
// "Move with Leader" drags the whole multileader; the
// others move just the picked grip.
// The synthetic whole-object grip is specific to a
// multileader. Dimension text grips implement their
// own leader relationship and must keep their real ID.
let is_multileader = matches!(
self.tabs[i].scene.document.get_entity(popup.handle),
Some(acadrust::EntityType::MultiLeader(_))
);
let (grip_id, is_translate) =
if matches!(item.action, GripMenuAction::MoveWithLeader) {
if matches!(item.action, GripMenuAction::MoveWithLeader)
&& is_multileader
{
(crate::entities::multileader::MOVE_ALL_GRIP, true)
} else {
(popup.grip_id, g.is_midpoint)

View file

@ -1212,7 +1212,7 @@ pub enum CmdResult {
CommitEntitiesAndExit(Vec<EntityType>),
/// Commit an acadrust entity to the document and end the command.
CommitAndExit(EntityType),
/// Commit a linear/aligned dimension using the drawing's association mode.
/// Commit a dimension using the drawing's association mode.
/// Object-selection workflows may retain their source.
CommitDimension {
entity: EntityType,

View file

@ -124,6 +124,23 @@ fn properties(dim: &Dimension) -> Vec<PropSection> {
],
}];
}
if let Dimension::Ordinate(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_angle(
t!("Rotation").as_ref(),
"ordinate_rotation",
-d.base.horizontal_direction.to_degrees(),
),
],
}];
}
let mut props = base_props(dim.base());
match dim {
Dimension::Aligned(d) => {
@ -351,6 +368,40 @@ fn assign_deg(value: &str, target: &mut f64) -> bool {
}
fn apply_geom_prop(dim: &mut Dimension, field: &str, value: &str) {
let ordinate_auto_text = match dim {
Dimension::Ordinate(ordinate) if !ordinate.base.text_user_positioned => {
Some(dimension_text_pos_f64(dim, None, 2.5, 1.0))
}
_ => None,
};
if let Dimension::Ordinate(ordinate) = dim {
match field {
"ordinate_rotation" => {
if let Some(angle) = parse_f64(value) {
ordinate.base.horizontal_direction = -angle.to_radians();
ordinate.refresh_measurement();
}
return;
}
"text_x" | "text_y" => {
let old_text = ordinate_auto_text.unwrap_or(ordinate.base.text_middle_point);
let mut new_text = old_text;
let changed = if field == "text_x" {
assign_f64(value, &mut new_text.x)
} else {
assign_f64(value, &mut new_text.y)
};
if changed {
let delta = new_text - old_text;
ordinate.leader_endpoint = ordinate.leader_endpoint + delta;
ordinate.base.text_middle_point = new_text;
ordinate.base.text_user_positioned = true;
}
return;
}
_ => {}
}
}
if apply_base_prop(dim.base_mut(), field, value) {
return;
}
@ -709,6 +760,42 @@ fn apply_large_radial_fields(d: &mut DimensionLargeRadial, field: &str, value: &
}
fn apply_transform(dim: &mut Dimension, t: &EntityTransform) {
if matches!(dim, Dimension::Ordinate(_)) {
match t {
EntityTransform::Translate(delta) => dim.translate(Vector3::new(
delta.x, delta.y, delta.z,
)),
EntityTransform::Rotate {
center,
axis,
angle_rad,
} => crate::scene::view::transform::apply_standard_transform(
dim,
*center,
*axis,
*angle_rad,
),
EntityTransform::Scale { center, factor } => {
crate::scene::view::transform::apply_standard_scale(dim, *center, *factor)
}
EntityTransform::Mirror {
p1,
p2,
working_normal,
} => acadrust::Entity::apply_transform(
dim,
&crate::scene::view::transform::reflection_about_working_line(
*p1,
*p2,
*working_normal,
),
),
EntityTransform::Affine(transform) => {
acadrust::Entity::apply_transform(dim, transform)
}
}
return;
}
match t {
EntityTransform::Translate(d) => dim.translate(acadrust::types::Vector3::new(
d.x as f64, d.y as f64, d.z as f64,
@ -1193,10 +1280,9 @@ impl Grippable for Dimension {
center_grip(4, text),
],
Dimension::Ordinate(d) => vec![
square_grip(0, dv3(&d.definition_point)),
center_grip(1, dv3(&d.feature_location)),
center_grip(2, dv3(&d.leader_endpoint)),
center_grip(3, text),
square_grip(0, dv3(&d.feature_location)),
center_grip(1, dv3(&d.leader_endpoint)),
center_grip(2, text),
],
Dimension::Arc(d) => {
let mut grips = vec![
@ -1227,12 +1313,25 @@ impl Grippable for Dimension {
fn apply_grip(&mut self, grip_id: usize, apply: GripApply) {
if matches!(self, Dimension::Ordinate(_)) && grip_id == 2 {
let old_text = dimension_text_pos_f64(self, None, 2.5, 1.0);
let mut new_text = old_text;
apply_to_v3(&mut new_text, &apply);
let delta = new_text - old_text;
if let Dimension::Ordinate(d) = self {
d.leader_endpoint = d.leader_endpoint + delta;
d.base.text_middle_point = new_text;
d.base.text_user_positioned = true;
d.refresh_measurement();
}
return;
}
// Last grip always moves the text.
let text_grip = match self {
Dimension::Linear(_) | Dimension::Aligned(_) => 3,
Dimension::Radius(_) | Dimension::Diameter(_) => 2,
Dimension::Angular2Ln(_) | Dimension::Angular3Pt(_) => 4,
Dimension::Ordinate(_) => 3,
Dimension::Ordinate(_) => 2,
Dimension::Arc(d) => if d.has_leader { 6 } else { 4 },
Dimension::LargeRadial(_) => 4,
};
@ -1288,9 +1387,15 @@ impl Grippable for Dimension {
_ => {}
},
Dimension::Ordinate(d) => match grip_id {
0 => apply_to_v3(&mut d.definition_point, &apply),
1 => apply_to_v3(&mut d.feature_location, &apply),
2 => apply_to_v3(&mut d.leader_endpoint, &apply),
0 => apply_to_v3(&mut d.feature_location, &apply),
1 => {
let old = d.leader_endpoint;
apply_to_v3(&mut d.leader_endpoint, &apply);
if d.base.text_user_positioned {
d.base.text_middle_point =
d.base.text_middle_point + (d.leader_endpoint - old);
}
}
_ => {}
},
Dimension::Arc(d) => match grip_id {
@ -1326,7 +1431,7 @@ impl Grippable for Dimension {
Dimension::Linear(_) | Dimension::Aligned(_) => (2, 3),
Dimension::Radius(_) | Dimension::Diameter(_) => (1, 2),
Dimension::Angular2Ln(_) | Dimension::Angular3Pt(_) => (3, 4),
Dimension::Ordinate(_) => (0, 3),
Dimension::Ordinate(_) => (1, 2),
Dimension::Arc(d) => (3, if d.has_leader { 6 } else { 4 }),
Dimension::LargeRadial(_) => (3, 4),
};
@ -1365,6 +1470,11 @@ impl Grippable for Dimension {
action: GripMenuAction::Center,
},
]
} else if matches!(self, Dimension::Ordinate(_)) {
vec![GripMenuItem {
label: "Stretch",
action: GripMenuAction::Stretch,
}]
} else if grip_id == dim_line_grip {
vec![
GripMenuItem {
@ -1394,7 +1504,7 @@ impl Grippable for Dimension {
Dimension::Linear(_) | Dimension::Aligned(_) => (2, 3),
Dimension::Radius(_) | Dimension::Diameter(_) => (1, 2),
Dimension::Angular2Ln(_) | Dimension::Angular3Pt(_) => (3, 4),
Dimension::Ordinate(_) => (0, 3),
Dimension::Ordinate(_) => (1, 2),
Dimension::Arc(d) => (3, if d.has_leader { 6 } else { 4 }),
Dimension::LargeRadial(_) => (3, 4),
};
@ -1441,7 +1551,7 @@ impl Grippable for Dimension {
Dimension::Linear(_) | Dimension::Aligned(_) => 3,
Dimension::Radius(_) | Dimension::Diameter(_) => 2,
Dimension::Angular2Ln(_) | Dimension::Angular3Pt(_) => 4,
Dimension::Ordinate(_) => 3,
Dimension::Ordinate(_) => 2,
Dimension::Arc(d) => if d.has_leader { 6 } else { 4 },
Dimension::LargeRadial(_) => 4,
};
@ -1682,7 +1792,7 @@ pub fn style_sections(
_ => choice_value("None", &["None", "Background", "Color"]),
};
vec![
let mut sections = vec![
PropSection {
title: t!("Lines & Arrows").into_owned(),
props: vec![
@ -2312,7 +2422,52 @@ pub fn style_sections(
),
],
},
]
];
if matches!(dimension, Dimension::Ordinate(_)) {
for section in &mut sections {
section.props.retain(|property| match section.title.as_str() {
title if title == t!("Lines & Arrows").as_ref() => matches!(
property.field,
"dim_arrow_size"
| "dim_ext_line_lineweight"
| "dim_ext_line_fixed"
| "dim_ext_line_fixed_length"
| "dim_ext_line_color"
| "dim_ext_line_offset"
),
title if title == t!("Text").as_ref() => !matches!(
property.field,
"dim_text_outside_align" | "dim_text_pos_hor" | "dim_text_inside_align"
),
title if title == t!("Fit").as_ref() => matches!(
property.field,
"dim_text_movement" | "dim_scale_overall"
),
_ => true,
});
}
let dim_scale = if s.dimscale > 1e-6 { s.dimscale } else { 1.0 };
let text_position = dimension_text_pos_f64(
dimension,
Some(s),
real(ov::DIMTXT, s.dimtxt) * dim_scale,
dim_scale,
);
for section in &mut sections {
for property in &mut section.props {
match property.field {
"text_x" => {
property.value = PropValue::EditText(format!("{:.4}", text_position.x));
}
"text_y" => {
property.value = PropValue::EditText(format!("{:.4}", text_position.y));
}
_ => {}
}
}
}
}
sections
}
fn property(label: &str, field: &'static str, value: PropValue) -> Property {
@ -3640,16 +3795,23 @@ fn dimension_geometry(
);
}
Dimension::Ordinate(d) => {
add_segment(
&mut g.dim_lines,
lv(d.feature_location),
lv(d.definition_point),
);
add_segment(
&mut g.dim_lines,
lv(d.definition_point),
lv(d.leader_endpoint),
);
if !suppress.ext1 {
let fixed_length = params
.dimfxlon
.then_some(params.dimfxl.max(0.0) as f64);
let points = d.leader_polyline(
(params.arrow_len * 2.0) as f64,
params.dimexo as f64,
fixed_length,
);
for pair in points.windows(2) {
let start = lv(pair[0]);
let end = lv(pair[1]);
if (end - start).length_squared() > 1e-12 {
add_segment(&mut g.ext_lines, start, end);
}
}
}
}
Dimension::Arc(d) => {
append_angular_dimension(
@ -4061,7 +4223,6 @@ fn dimension_snap_pts(dim: &Dimension) -> Vec<(glam::DVec3, SnapHint)> {
node(d.definition_point),
],
Dimension::Ordinate(d) => vec![
node(d.definition_point),
node(d.feature_location),
node(d.leader_endpoint),
],
@ -4271,6 +4432,8 @@ fn dimension_text_rotation(dim: &Dimension, style: Option<&DimStyle>) -> f64 {
let outside = dimension_text_is_outside(dim, style);
if base.text_rotation.abs() > 1e-9 {
base.text_rotation
} else if matches!(dim, Dimension::Ordinate(_)) {
dimension_text_natural_rotation(dim)
} else if base.horizontal_direction.abs() > 1e-9 {
base.horizontal_direction
} else if (outside && dimtoh) || (!outside && dimtih) {
@ -4338,6 +4501,14 @@ fn dimension_text_natural_rotation(dim: &Dimension) -> f64 {
let dy = d.second_point.y - d.first_point.y;
dy.atan2(dx)
}
Dimension::Ordinate(d) => {
let axis_rotation = -d.base.horizontal_direction;
if d.is_ordinate_type_x {
axis_rotation + std::f64::consts::FRAC_PI_2
} else {
axis_rotation
}
}
_ => 0.0,
};
// Clamp to (-π/2, π/2] so text never appears upside-down.
@ -5119,6 +5290,26 @@ fn dimension_text_pos_f64(
dimtad,
)
}
Dimension::Ordinate(d) => {
let (x_axis, y_axis) = d.local_axes();
let text_axis = if d.is_ordinate_type_x { y_axis } else { x_axis };
let perpendicular = if d.is_ordinate_type_x { x_axis } else { y_axis };
let delta = d.leader_endpoint - d.feature_location;
let direction_sign = if delta.dot(&text_axis) < 0.0 { -1.0 } else { 1.0 };
let perpendicular_sign = if delta.dot(&perpendicular) < 0.0 {
-1.0
} else {
1.0
};
let vertical = match dimtad {
0 => dimtvp * text_height,
4 => -perp_off,
_ => perp_off,
};
d.leader_endpoint
+ text_axis * (direction_sign * (text_w * 0.5 + dimgap))
+ perpendicular * (perpendicular_sign * vertical)
}
_ => {
// Non-linear (radius / diameter / angular / ordinate): lift the
// natural mid point straight up by the style offset. A user-dragged

View file

@ -32,9 +32,22 @@ enum Step {
LeaderEndpoint { feature: DVec3 },
}
#[derive(Clone, Copy)]
enum DatumMode {
Automatic,
X,
Y,
}
pub struct OrdinateDimCommand {
step: Step,
plane: WorkingPlane,
datum_mode: DatumMode,
text_override: Option<String>,
awaiting_text: bool,
mtext_override: bool,
text_angle: Option<f64>,
awaiting_angle: bool,
}
impl OrdinateDimCommand {
@ -42,6 +55,12 @@ impl OrdinateDimCommand {
Self {
step: Step::FeaturePoint,
plane: WorkingPlane::default(),
datum_mode: DatumMode::Automatic,
text_override: None,
awaiting_text: false,
mtext_override: false,
text_angle: None,
awaiting_angle: false,
}
}
}
@ -56,9 +75,23 @@ impl CadCommand for OrdinateDimCommand {
}
fn prompt(&self) -> String {
if self.awaiting_text {
return if self.mtext_override {
t!("DIMORDINATE Enter formatted dimension text (blank = measured value):")
.into_owned()
} else {
t!("DIMORDINATE Enter dimension text (blank = measured value):").into_owned()
};
}
if self.awaiting_angle {
return t!("DIMORDINATE Specify text angle (degrees):").into_owned();
}
match self.step {
Step::FeaturePoint => t!("DIMORDINATE Specify feature location:").into_owned(),
Step::LeaderEndpoint { .. } => t!("DIMORDINATE Specify leader endpoint:").into_owned(),
Step::LeaderEndpoint { .. } => t!(
"DIMORDINATE Specify leader endpoint [Xdatum/Ydatum/Mtext/Text/Angle]:"
)
.into_owned(),
}
}
@ -71,26 +104,105 @@ impl CadCommand for OrdinateDimCommand {
Step::LeaderEndpoint { feature } => {
let feature = self.plane.to_local(feature);
let pt = self.plane.to_local(pt);
let is_x = is_x_type(feature, pt);
let elbow = ordinate_elbow(feature, pt, is_x);
let is_x = match self.datum_mode {
DatumMode::Automatic => is_x_type(feature, pt),
DatumMode::X => true,
DatumMode::Y => false,
};
let mut dim = DimensionOrdinate::new(v3(feature), v3(pt), is_x);
// The leader is an orthogonal L from the feature to the
// endpoint; store its elbow as the definition point. Without it
// the renderer draws feature → (0,0,0) → endpoint, kinking the
// leader through the world origin. The old code also worked in
// the wrong (XZ) plane, dropping the Y coordinate. (#150)
dim.definition_point = v3(elbow);
dim.base.definition_point = v3(elbow);
CmdResult::CommitAndExit(self.plane.place_entity(EntityType::Dimension(
Dimension::Ordinate(dim),
)))
crate::entities::dimension::set_dimension_text_override(
&mut dim.base,
self.text_override.clone(),
);
if let Some(angle) = self.text_angle {
dim.base.text_rotation = angle;
}
dim.refresh_measurement();
CmdResult::CommitDimension {
entity: self.plane.place_entity(EntityType::Dimension(
Dimension::Ordinate(dim),
)),
source: None,
}
}
}
}
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 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 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::LeaderEndpoint { .. }) {
return None;
}
match text.trim().to_uppercase().as_str() {
"X" | "XDATUM" => {
self.datum_mode = DatumMode::X;
Some(CmdResult::NeedPoint)
}
"Y" | "YDATUM" => {
self.datum_mode = DatumMode::Y;
Some(CmdResult::NeedPoint)
}
"T" | "TEXT" => {
self.mtext_override = false;
self.awaiting_text = true;
Some(CmdResult::NeedPoint)
}
"M" | "MTEXT" => {
self.mtext_override = true;
self.awaiting_text = true;
Some(CmdResult::NeedPoint)
}
"A" | "ANGLE" => {
self.awaiting_angle = true;
Some(CmdResult::NeedPoint)
}
_ => None,
}
}
fn on_mouse_move(&mut self, pt: DVec3) -> Option<WireModel> {
let feature = match self.step {
Step::LeaderEndpoint { feature } => feature,
@ -98,17 +210,24 @@ impl CadCommand for OrdinateDimCommand {
};
let feature = self.plane.to_local(feature);
let pt = self.plane.to_local(pt);
let is_x = is_x_type(feature, pt);
let elbow = ordinate_elbow(feature, pt, is_x);
let feature = self.plane.to_world(feature);
let elbow = self.plane.to_world(elbow);
let pt = self.plane.to_world(pt);
let is_x = match self.datum_mode {
DatumMode::Automatic => is_x_type(feature, pt),
DatumMode::X => true,
DatumMode::Y => false,
};
let dim = DimensionOrdinate::new(v3(feature), v3(pt), is_x);
let points = dim.leader_polyline(0.44, 0.0, None);
// Screen-only rubber band: downcast to f32 at the preview boundary.
Some(preview_wire(vec![
feature.as_vec3(),
elbow.as_vec3(),
pt.as_vec3(),
]))
Some(preview_wire(
points
.into_iter()
.map(|point| {
self.plane
.to_world(DVec3::new(point.x, point.y, point.z))
.as_vec3()
})
.collect(),
))
}
}
@ -122,17 +241,7 @@ fn v3(p: DVec3) -> Vector3 {
fn is_x_type(feature: DVec3, leader: DVec3) -> bool {
let dx = (leader.x - feature.x).abs();
let dy = (leader.y - feature.y).abs();
dy >= dx
}
/// Orthogonal elbow of the ordinate leader: an X-datum runs along Y from the
/// feature then jogs across in X; a Y-datum runs along X then jogs in Y.
fn ordinate_elbow(feature: DVec3, leader: DVec3, is_x: bool) -> DVec3 {
if is_x {
DVec3::new(feature.x, leader.y, feature.z)
} else {
DVec3::new(leader.x, feature.y, feature.z)
}
dy > dx
}
fn preview_wire(points: Vec<Vec3>) -> WireModel {

View file

@ -851,7 +851,20 @@ fn explode_dimension(dim: &Dimension, doc: &CadDocument) -> Vec<EntityType> {
// Per-element commons so a baked block keeps the style's colours/lineweights:
// extension lines (DIMCLRE/DIMLWE), dimension line + arrows + centre mark
// (DIMCLRD/DIMLWD), text (DIMCLRT).
let ext_c = dim_common(&base.common, met.dimclre, met.dimlwe);
let mut ext_c = dim_common(&base.common, met.dimclre, met.dimlwe);
let override_data = &base.common.extended_data;
if let Some(color) = crate::entities::dim_override::color(
override_data,
crate::entities::dim_override::DIMCLRE,
) {
ext_c.color = color;
}
if let Some(lineweight) = crate::entities::dim_override::int(
override_data,
crate::entities::dim_override::DIMLWE,
) {
ext_c.line_weight = acadrust::types::LineWeight::from_value(lineweight);
}
let dim_c = dim_common(&base.common, met.dimclrd, met.dimlwd);
let mut result: Vec<EntityType> = Vec::new();
@ -1041,8 +1054,33 @@ fn explode_dimension(dim: &Dimension, doc: &CadDocument) -> Vec<EntityType> {
));
}
Dimension::Ordinate(d) => {
result.push(make_seg(&d.feature_location, &d.definition_point, &dim_c));
result.push(make_seg(&d.definition_point, &d.leader_endpoint, &dim_c));
if !met.dimse1 {
use crate::entities::dim_override as ov;
let scale = ov::real(override_data, ov::DIMSCALE)
.filter(|value| *value > 1e-6)
.unwrap_or(1.0);
let dogleg = ov::real(override_data, ov::DIMASZ)
.unwrap_or(met.dimasz / scale)
* scale
* 2.0;
let offset = ov::real(override_data, ov::DIMEXO)
.unwrap_or(met.dimexo / scale)
* scale;
let fixed_on = ov::int(override_data, ov::DIMFXLON)
.map(|value| value != 0)
.unwrap_or(met.dimfxlon);
let fixed_length = fixed_on.then(|| {
ov::real(override_data, ov::DIMFXL)
.unwrap_or(met.dimfxl / scale)
* scale
});
let points = d.leader_polyline(dogleg, offset, fixed_length);
for pair in points.windows(2) {
if (pair[1] - pair[0]).length() > 1e-12 {
result.push(make_seg(&pair[0], &pair[1], &ext_c));
}
}
}
}
Dimension::Arc(d) => {
result.extend(angular_block_segs(

View file

@ -115,10 +115,11 @@ fn resolve_reference(scene: &Scene, reference: &AssocDimensionReference) -> Opti
.copied()
}
fn dimension_points(dimension: &Dimension) -> Option<[Vector3; 2]> {
fn dimension_points(dimension: &Dimension) -> Option<[Option<Vector3>; 2]> {
match dimension {
Dimension::Linear(linear) => Some([linear.first_point, linear.second_point]),
Dimension::Aligned(aligned) => Some([aligned.first_point, aligned.second_point]),
Dimension::Linear(linear) => Some([Some(linear.first_point), Some(linear.second_point)]),
Dimension::Aligned(aligned) => Some([Some(aligned.first_point), Some(aligned.second_point)]),
Dimension::Ordinate(ordinate) => Some([Some(ordinate.feature_location), None]),
_ => None,
}
}
@ -186,13 +187,14 @@ impl Scene {
return;
};
let resolved: [Option<(Handle, i32, f64, u8)>; 2] = std::array::from_fn(|index| {
let point = source_data[index]?;
let source = sources[index]?;
let entity = self.document.get_entity(source)?;
source_marker(entity, source_data[index]).map(|marker| {
source_marker(entity, point).map(|marker| {
(
source,
marker,
source_parameter(entity, source_data[index]),
source_parameter(entity, point),
if matches!(entity, EntityType::Circle(_)) {
10
} else {
@ -230,7 +232,7 @@ impl Scene {
marker,
parameter,
osnap_type,
source_data[index],
source_data[index].expect("resolved dimension point"),
));
}
}
@ -274,7 +276,7 @@ impl Scene {
return [None, None];
};
points.map(|point| {
self.document
point.and_then(|point| self.document
.entities()
.filter(|entity| entity.common().handle != dimension)
.filter_map(|entity| {
@ -283,7 +285,7 @@ impl Scene {
})
.filter(|(distance, _)| *distance <= 1e-16)
.min_by(|first, second| first.0.total_cmp(&second.0))
.map(|(_, handle)| handle)
.map(|(_, handle)| handle))
})
}
@ -353,6 +355,12 @@ impl Scene {
aligned.base.actual_measurement = aligned.measurement();
aligned.base.definition_point = aligned.definition_point;
}
Dimension::Ordinate(ordinate) => {
if let Some(feature) = first {
ordinate.feature_location = feature;
}
ordinate.refresh_measurement();
}
_ => continue,
}
refreshed.push((association.dimension, ChangeKind::Modified));