forked from 1b-agent/opencadstudio-upstream
fix(dimension): harden jogged radius workflow
This commit is contained in:
commit
bc750c6d76
71 files changed
+5834
-860
No files matched your search
@@ -16,6 +16,19 @@ use crate::command::DimensionAssociationSource;
|
||||
|
||||
use super::{ChangeKind, Scene};
|
||||
|
||||
pub(crate) const POLYLINE_ARC_CENTER_MARKER: i32 = -4;
|
||||
const POLYLINE_ARC_POINT_MARKER_BASE: i32 = -5;
|
||||
pub(crate) const ARC_DIMENSION_POINT_MARKER: i32 = -1;
|
||||
|
||||
pub(crate) fn polyline_arc_point_marker(segment: i32) -> i32 {
|
||||
POLYLINE_ARC_POINT_MARKER_BASE - segment.max(0)
|
||||
}
|
||||
|
||||
fn polyline_arc_segment_from_point_marker(marker: i32) -> Option<i32> {
|
||||
(marker <= POLYLINE_ARC_POINT_MARKER_BASE)
|
||||
.then_some(POLYLINE_ARC_POINT_MARKER_BASE - marker)
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub(crate) struct RadialSourceGeometry {
|
||||
pub plane: Plane,
|
||||
@@ -329,7 +342,21 @@ fn source_marker(entity: &EntityType, point: Vector3) -> Option<i32> {
|
||||
fn resolve_reference(scene: &Scene, reference: &AssocDimensionReference) -> Option<Vector3> {
|
||||
let source = *reference.xrefs.first()?;
|
||||
let entity = scene.document.get_entity(source)?;
|
||||
if reference.main_gs_marker == -4 {
|
||||
if reference.main_gs_marker == ARC_DIMENSION_POINT_MARKER {
|
||||
let radial = radial_source_for_marker(entity, 0)?;
|
||||
let sweep = positive_sweep(radial.start_angle, radial.end_angle);
|
||||
let angle = radial.start_angle + sweep * reference.osnap_distance.clamp(0.0, 1.0);
|
||||
return Some(radial.point_at_angle(angle));
|
||||
}
|
||||
if let Some(segment) =
|
||||
polyline_arc_segment_from_point_marker(reference.main_gs_marker)
|
||||
{
|
||||
let radial = radial_source_for_marker(entity, segment)?;
|
||||
let sweep = positive_sweep(radial.start_angle, radial.end_angle);
|
||||
let angle = radial.start_angle + sweep * reference.osnap_distance.clamp(0.0, 1.0);
|
||||
return Some(radial.point_at_angle(angle));
|
||||
}
|
||||
if reference.main_gs_marker == POLYLINE_ARC_CENTER_MARKER {
|
||||
let segment = reference.osnap_distance.round().max(0.0) as usize;
|
||||
return polyline_arc_center(entity, segment);
|
||||
}
|
||||
@@ -422,10 +449,82 @@ fn dimension_reference_points(dimension: &Dimension) -> Vec<Vector3> {
|
||||
angular.definition_point,
|
||||
],
|
||||
Dimension::Ordinate(ordinate) => vec![ordinate.feature_location],
|
||||
Dimension::Arc(arc) => vec![
|
||||
arc.center_point,
|
||||
arc.first_extension_point,
|
||||
arc.second_extension_point,
|
||||
],
|
||||
_ => Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn positive_sweep(start: f64, end: f64) -> f64 {
|
||||
let raw = end - start;
|
||||
let mut sweep = raw.rem_euclid(TAU);
|
||||
if sweep <= 1.0e-12 && raw.abs() > 1.0e-12 {
|
||||
sweep = TAU;
|
||||
}
|
||||
sweep
|
||||
}
|
||||
|
||||
fn signed_angle_delta(value: f64) -> f64 {
|
||||
(value + std::f64::consts::PI).rem_euclid(TAU) - std::f64::consts::PI
|
||||
}
|
||||
|
||||
fn angle_about_plane(
|
||||
plane: Plane,
|
||||
center: Vector3,
|
||||
point: Vector3,
|
||||
) -> f64 {
|
||||
let delta = [
|
||||
point.x - center.x,
|
||||
point.y - center.y,
|
||||
point.z - center.z,
|
||||
];
|
||||
let dot = |axis: [f64; 3]| {
|
||||
delta[0] * axis[0] + delta[1] * axis[1] + delta[2] * axis[2]
|
||||
};
|
||||
dot(plane.y_axis).atan2(dot(plane.x_axis))
|
||||
}
|
||||
|
||||
fn point_on_radial_circle(
|
||||
radial: RadialSourceGeometry,
|
||||
radius: f64,
|
||||
angle: f64,
|
||||
) -> Vector3 {
|
||||
vector3(radial.plane.point_at([
|
||||
radial.center[0] + radius * angle.cos(),
|
||||
radial.center[1] + radius * angle.sin(),
|
||||
]))
|
||||
}
|
||||
|
||||
fn plane_from_normal(origin: Vector3, normal: Vector3) -> Plane {
|
||||
let (x_axis, y_axis) = crate::scene::view::transform::ocs_axes((
|
||||
normal.x, normal.y, normal.z,
|
||||
));
|
||||
Plane::from_axes(
|
||||
[origin.x, origin.y, origin.z],
|
||||
[x_axis.0, x_axis.1, x_axis.2],
|
||||
[y_axis.0, y_axis.1, y_axis.2],
|
||||
)
|
||||
}
|
||||
|
||||
fn remap_plane_offset(
|
||||
old_plane: Plane,
|
||||
new_plane: Plane,
|
||||
old_origin: Vector3,
|
||||
old_point: Vector3,
|
||||
new_origin: Vector3,
|
||||
) -> Vector3 {
|
||||
let old_origin = old_plane.project(dpoint(old_origin)).unwrap_or([0.0; 2]);
|
||||
let old_point = old_plane.project(dpoint(old_point)).unwrap_or(old_origin);
|
||||
let new_origin_uv = new_plane.project(dpoint(new_origin)).unwrap_or([0.0; 2]);
|
||||
vector3(new_plane.point_at([
|
||||
new_origin_uv[0] + old_point[0] - old_origin[0],
|
||||
new_origin_uv[1] + old_point[1] - old_origin[1],
|
||||
]))
|
||||
}
|
||||
|
||||
pub(crate) fn dimension_is_associative(
|
||||
document: &acadrust::CadDocument,
|
||||
dimension: Handle,
|
||||
@@ -807,6 +906,18 @@ impl Scene {
|
||||
let radial = radial_source_for_marker(entity, reference.main_gs_marker)?;
|
||||
Some((radial, reference.osnap_distance))
|
||||
});
|
||||
let arc_source = association.references[0].first().and_then(|reference| {
|
||||
let source = *reference.xrefs.first()?;
|
||||
let entity = self.document.get_entity(source)?;
|
||||
let segment = match reference.main_gs_marker {
|
||||
-3 => 0,
|
||||
POLYLINE_ARC_CENTER_MARKER => {
|
||||
reference.osnap_distance.round().max(0.0) as i32
|
||||
}
|
||||
_ => return None,
|
||||
};
|
||||
radial_source_for_marker(entity, segment)
|
||||
});
|
||||
if radial_source.is_none() && resolved.iter().all(Option::is_none) {
|
||||
continue;
|
||||
}
|
||||
@@ -936,7 +1047,95 @@ impl Scene {
|
||||
}
|
||||
ordinate.refresh_measurement();
|
||||
}
|
||||
_ => continue,
|
||||
Dimension::Arc(arc) => {
|
||||
let Some(radial) = arc_source else {
|
||||
continue;
|
||||
};
|
||||
let center = resolved[0].unwrap_or_else(|| radial.center_world());
|
||||
let Some(first) = resolved[1] else {
|
||||
continue;
|
||||
};
|
||||
let Some(second) = resolved[2] else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let old_center = arc.center_point;
|
||||
let old_definition = arc.definition_point;
|
||||
let old_plane = plane_from_normal(old_center, arc.base.normal);
|
||||
let old_source_radius = old_center.distance(&arc.first_extension_point);
|
||||
let old_dim_radius = old_center.distance(&old_definition);
|
||||
let radial_offset = old_dim_radius - old_source_radius;
|
||||
let old_mid = arc.arc_start_parameter
|
||||
+ positive_sweep(
|
||||
arc.arc_start_parameter,
|
||||
arc.arc_end_parameter,
|
||||
) * 0.5;
|
||||
let old_definition_angle =
|
||||
angle_about_plane(old_plane, old_center, old_definition);
|
||||
let definition_angle_offset =
|
||||
signed_angle_delta(old_definition_angle - old_mid);
|
||||
|
||||
let start = radial.angle_at(dpoint(first));
|
||||
let end_at = radial.angle_at(dpoint(second));
|
||||
let sweep = positive_sweep(start, end_at);
|
||||
let end = start + sweep;
|
||||
let new_radius = center.distance(&first);
|
||||
if !new_radius.is_finite() || new_radius <= 1.0e-12 {
|
||||
continue;
|
||||
}
|
||||
let dim_radius = (new_radius + radial_offset).max(1.0e-9);
|
||||
let middle = start + sweep * 0.5;
|
||||
let definition = point_on_radial_circle(
|
||||
radial,
|
||||
dim_radius,
|
||||
middle + definition_angle_offset,
|
||||
);
|
||||
let text_middle = remap_plane_offset(
|
||||
old_plane,
|
||||
radial.plane,
|
||||
old_definition,
|
||||
arc.base.text_middle_point,
|
||||
definition,
|
||||
);
|
||||
let insertion = remap_plane_offset(
|
||||
old_plane,
|
||||
radial.plane,
|
||||
old_definition,
|
||||
arc.base.insertion_point,
|
||||
definition,
|
||||
);
|
||||
let second_leader = remap_plane_offset(
|
||||
old_plane,
|
||||
radial.plane,
|
||||
old_definition,
|
||||
arc.second_leader_point,
|
||||
definition,
|
||||
);
|
||||
|
||||
arc.center_point = center;
|
||||
arc.first_extension_point = first;
|
||||
arc.second_extension_point = second;
|
||||
arc.arc_start_parameter = start;
|
||||
arc.arc_end_parameter = end;
|
||||
arc.definition_point = definition;
|
||||
arc.base.definition_point = definition;
|
||||
if let Some(normal) = radial.plane.normal() {
|
||||
arc.base.normal = vector3(normal);
|
||||
}
|
||||
if arc.base.text_user_positioned {
|
||||
arc.base.text_middle_point = text_middle;
|
||||
arc.base.insertion_point = insertion;
|
||||
} else {
|
||||
arc.base.text_middle_point = definition;
|
||||
arc.base.insertion_point = definition;
|
||||
}
|
||||
if arc.has_leader {
|
||||
arc.first_leader_point =
|
||||
point_on_radial_circle(radial, dim_radius, middle);
|
||||
arc.second_leader_point = second_leader;
|
||||
}
|
||||
arc.base.actual_measurement = arc.measurement();
|
||||
}
|
||||
}
|
||||
dimension.base_mut().actual_measurement = dimension.measurement();
|
||||
refreshed.push((association.dimension, ChangeKind::Modified));
|
||||
|
||||
Reference in new issue
Block a user