forked from 1b-agent/opencadstudio-upstream
feat(cad): integrate standard codec coverage
Render, edit, query, select, and explode newly decoded standard entity variants. Pin acadifc de8d6b5 and preserve None color semantics.
This commit is contained in:
1 parent
34d0c377e2
commit
731df4a725
19 files changed
+484
-72
No files matched your search
Generated
+1
-1
@@ -75,7 +75,7 @@ checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618"
|
||||
[[package]]
|
||||
name = "acadrust"
|
||||
version = "0.4.0"
|
||||
source = "git+https://github.com/OpenAEC-Foundation/acadifc.git?rev=8cc4793#8cc479342635fe16694e17226670d800cb3a1dfe"
|
||||
source = "git+https://github.com/OpenAEC-Foundation/acadifc.git?rev=de8d6b5#de8d6b5e262468fa8292c0bb7ecf4fbcb042d709"
|
||||
dependencies = [
|
||||
"ahash 0.8.12",
|
||||
"anyhow",
|
||||
|
||||
+1
-1
@@ -91,7 +91,7 @@ windows-sys = { version = "0.61", features = ["Win32_UI_Shell", "Win32_UI_Window
|
||||
|
||||
[patch.crates-io]
|
||||
# Track the verified DWG round-trip, I/O, and unified PERF fixes.
|
||||
acadrust = { git = "https://github.com/OpenAEC-Foundation/acadifc.git", rev = "8cc4793" }
|
||||
acadrust = { git = "https://github.com/OpenAEC-Foundation/acadifc.git", rev = "de8d6b5" }
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||
# Native enables the plugin host runtime (out-of-process plugins).
|
||||
|
||||
@@ -1750,6 +1750,22 @@ impl OpenCADStudio {
|
||||
stretched |= mv(&mut d.feature_location);
|
||||
stretched |= mv(&mut d.leader_endpoint);
|
||||
}
|
||||
Dimension::Arc(d) => {
|
||||
stretched |= mv(&mut d.definition_point);
|
||||
stretched |= mv(&mut d.first_extension_point);
|
||||
stretched |= mv(&mut d.second_extension_point);
|
||||
stretched |= mv(&mut d.center_point);
|
||||
if d.has_leader {
|
||||
stretched |= mv(&mut d.first_leader_point);
|
||||
stretched |= mv(&mut d.second_leader_point);
|
||||
}
|
||||
}
|
||||
Dimension::LargeRadial(d) => {
|
||||
stretched |= mv(&mut d.definition_point);
|
||||
stretched |= mv(&mut d.chord_point);
|
||||
stretched |= mv(&mut d.override_center);
|
||||
stretched |= mv(&mut d.jog_point);
|
||||
}
|
||||
}
|
||||
// Pinned text follows too; the zero sentinel means
|
||||
// "auto placement" and must not be captured by a
|
||||
|
||||
@@ -1323,6 +1323,8 @@ fn add_selected_verb(entity: &acadrust::EntityType) -> Option<&'static str> {
|
||||
DimensionType::Diameter => "DIMDIAMETER",
|
||||
DimensionType::Radius => "DIMRADIUS",
|
||||
DimensionType::Ordinate => "DIMORDINATE",
|
||||
DimensionType::ArcLength => "DIMARC",
|
||||
DimensionType::LargeRadial => "DIMJOGGED",
|
||||
}
|
||||
}
|
||||
_ => return None,
|
||||
|
||||
@@ -383,6 +383,7 @@ pub(super) fn entity_type_key(entity: &acadrust::EntityType) -> String {
|
||||
Light(_) => "light",
|
||||
SectionSymbol(_) => "sectionsymbol",
|
||||
ViewBorder(_) => "viewborder",
|
||||
Extended(entity) => return entity.class_name().to_ascii_lowercase(),
|
||||
Seqend(_) => "seqend",
|
||||
Unknown(_) => "unknown",
|
||||
}
|
||||
|
||||
@@ -1117,7 +1117,7 @@ const QSELECT_ANY_PROP: &str = "(Any property)";
|
||||
/// fields (Start X, Length, Radius, …) so type-specific filtering works.
|
||||
pub(super) fn qselect_overlay<'a>(
|
||||
state: &'a crate::app::QSelectState,
|
||||
types: &[&'static str],
|
||||
types: &[String],
|
||||
properties: &[(String, String)],
|
||||
) -> Element<'a, Message> {
|
||||
use iced::widget::{checkbox, pick_list};
|
||||
@@ -1165,7 +1165,7 @@ pub(super) fn qselect_overlay<'a>(
|
||||
};
|
||||
|
||||
let mut type_options: Vec<String> = vec![QSELECT_ANY_TYPE.to_string()];
|
||||
type_options.extend(types.iter().map(|s| (*s).to_string()));
|
||||
type_options.extend(types.iter().cloned());
|
||||
|
||||
let mut prop_options: Vec<crate::app::QSelectPropertyChoice> =
|
||||
vec![crate::app::QSelectPropertyChoice {
|
||||
@@ -1337,4 +1337,3 @@ pub(super) fn qselect_overlay<'a>(
|
||||
|
||||
stack![catcher, centered].into()
|
||||
}
|
||||
|
||||
+287
-8
@@ -1,6 +1,7 @@
|
||||
use acadrust::entities::{
|
||||
Dimension, DimensionAligned, DimensionAngular2Ln, DimensionAngular3Pt, DimensionBase,
|
||||
DimensionDiameter, DimensionLinear, DimensionOrdinate, DimensionRadius,
|
||||
Dimension, DimensionAligned, DimensionAngular2Ln, DimensionAngular3Pt, DimensionArc,
|
||||
DimensionBase, DimensionDiameter, DimensionLargeRadial, DimensionLinear, DimensionOrdinate,
|
||||
DimensionRadius,
|
||||
};
|
||||
use acadrust::Entity;
|
||||
use glam::{DVec3, Vec3};
|
||||
@@ -128,6 +129,47 @@ fn properties(dim: &Dimension) -> Vec<PropSection> {
|
||||
if d.is_ordinate_type_x { "X" } else { "Y" },
|
||||
));
|
||||
}
|
||||
Dimension::Arc(d) => {
|
||||
props.extend(angular_props(
|
||||
d.center_point,
|
||||
d.first_extension_point,
|
||||
d.second_extension_point,
|
||||
d.definition_point,
|
||||
));
|
||||
props.push(edit(
|
||||
"Arc Start (deg)",
|
||||
"arc_start_parameter",
|
||||
d.arc_start_parameter.to_degrees(),
|
||||
));
|
||||
props.push(edit(
|
||||
"Arc End (deg)",
|
||||
"arc_end_parameter",
|
||||
d.arc_end_parameter.to_degrees(),
|
||||
));
|
||||
props.push(ro("Partial", "is_partial", d.is_partial.to_string()));
|
||||
props.push(ro("Has Leader", "has_leader", d.has_leader.to_string()));
|
||||
props.push(edit("Leader 1 X", "leader1_x", d.first_leader_point.x));
|
||||
props.push(edit("Leader 1 Y", "leader1_y", d.first_leader_point.y));
|
||||
props.push(edit("Leader 1 Z", "leader1_z", d.first_leader_point.z));
|
||||
props.push(edit("Leader 2 X", "leader2_x", d.second_leader_point.x));
|
||||
props.push(edit("Leader 2 Y", "leader2_y", d.second_leader_point.y));
|
||||
props.push(edit("Leader 2 Z", "leader2_z", d.second_leader_point.z));
|
||||
}
|
||||
Dimension::LargeRadial(d) => {
|
||||
props.push(edit("Center X", "definition_x", d.definition_point.x));
|
||||
props.push(edit("Center Y", "definition_y", d.definition_point.y));
|
||||
props.push(edit("Center Z", "definition_z", d.definition_point.z));
|
||||
props.push(edit("Chord X", "chord_x", d.chord_point.x));
|
||||
props.push(edit("Chord Y", "chord_y", d.chord_point.y));
|
||||
props.push(edit("Chord Z", "chord_z", d.chord_point.z));
|
||||
props.push(edit("Override Center X", "override_x", d.override_center.x));
|
||||
props.push(edit("Override Center Y", "override_y", d.override_center.y));
|
||||
props.push(edit("Override Center Z", "override_z", d.override_center.z));
|
||||
props.push(edit("Jog X", "jog_x", d.jog_point.x));
|
||||
props.push(edit("Jog Y", "jog_y", d.jog_point.y));
|
||||
props.push(edit("Jog Z", "jog_z", d.jog_point.z));
|
||||
props.push(edit_angle("Jog Angle", "jog_angle", d.jog_angle.to_degrees()));
|
||||
}
|
||||
}
|
||||
vec![PropSection {
|
||||
title: "Geometry".into(),
|
||||
@@ -259,6 +301,8 @@ fn apply_geom_prop(dim: &mut Dimension, field: &str, value: &str) {
|
||||
Dimension::Angular2Ln(d) => apply_angular2_fields(d, field, value),
|
||||
Dimension::Angular3Pt(d) => apply_angular3_fields(d, field, value),
|
||||
Dimension::Ordinate(d) => apply_ordinate_fields(d, field, value),
|
||||
Dimension::Arc(d) => apply_arc_fields(d, field, value),
|
||||
Dimension::LargeRadial(d) => apply_large_radial_fields(d, field, value),
|
||||
}
|
||||
dim.base_mut().actual_measurement = dim.measurement();
|
||||
}
|
||||
@@ -495,6 +539,89 @@ fn apply_ordinate_fields(d: &mut DimensionOrdinate, field: &str, value: &str) {
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_arc_fields(d: &mut DimensionArc, field: &str, value: &str) {
|
||||
apply_angular_common(
|
||||
&mut d.center_point,
|
||||
&mut d.first_extension_point,
|
||||
&mut d.second_extension_point,
|
||||
&mut d.definition_point,
|
||||
field,
|
||||
value,
|
||||
);
|
||||
match field {
|
||||
"arc_start_parameter" => {
|
||||
let _ = assign_deg(value, &mut d.arc_start_parameter);
|
||||
}
|
||||
"arc_end_parameter" => {
|
||||
let _ = assign_deg(value, &mut d.arc_end_parameter);
|
||||
}
|
||||
"leader1_x" => {
|
||||
let _ = assign_f64(value, &mut d.first_leader_point.x);
|
||||
}
|
||||
"leader1_y" => {
|
||||
let _ = assign_f64(value, &mut d.first_leader_point.y);
|
||||
}
|
||||
"leader1_z" => {
|
||||
let _ = assign_f64(value, &mut d.first_leader_point.z);
|
||||
}
|
||||
"leader2_x" => {
|
||||
let _ = assign_f64(value, &mut d.second_leader_point.x);
|
||||
}
|
||||
"leader2_y" => {
|
||||
let _ = assign_f64(value, &mut d.second_leader_point.y);
|
||||
}
|
||||
"leader2_z" => {
|
||||
let _ = assign_f64(value, &mut d.second_leader_point.z);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_large_radial_fields(d: &mut DimensionLargeRadial, field: &str, value: &str) {
|
||||
match field {
|
||||
"definition_x" => {
|
||||
let _ = assign_f64(value, &mut d.definition_point.x);
|
||||
}
|
||||
"definition_y" => {
|
||||
let _ = assign_f64(value, &mut d.definition_point.y);
|
||||
}
|
||||
"definition_z" => {
|
||||
let _ = assign_f64(value, &mut d.definition_point.z);
|
||||
}
|
||||
"chord_x" => {
|
||||
let _ = assign_f64(value, &mut d.chord_point.x);
|
||||
}
|
||||
"chord_y" => {
|
||||
let _ = assign_f64(value, &mut d.chord_point.y);
|
||||
}
|
||||
"chord_z" => {
|
||||
let _ = assign_f64(value, &mut d.chord_point.z);
|
||||
}
|
||||
"override_x" => {
|
||||
let _ = assign_f64(value, &mut d.override_center.x);
|
||||
}
|
||||
"override_y" => {
|
||||
let _ = assign_f64(value, &mut d.override_center.y);
|
||||
}
|
||||
"override_z" => {
|
||||
let _ = assign_f64(value, &mut d.override_center.z);
|
||||
}
|
||||
"jog_x" => {
|
||||
let _ = assign_f64(value, &mut d.jog_point.x);
|
||||
}
|
||||
"jog_y" => {
|
||||
let _ = assign_f64(value, &mut d.jog_point.y);
|
||||
}
|
||||
"jog_z" => {
|
||||
let _ = assign_f64(value, &mut d.jog_point.z);
|
||||
}
|
||||
"jog_angle" => {
|
||||
let _ = assign_deg(value, &mut d.jog_angle);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_transform(dim: &mut Dimension, t: &EntityTransform) {
|
||||
match t {
|
||||
EntityTransform::Translate(d) => dim.translate(acadrust::types::Vector3::new(
|
||||
@@ -556,6 +683,22 @@ where
|
||||
f(&mut d.feature_location);
|
||||
f(&mut d.leader_endpoint);
|
||||
}
|
||||
Dimension::Arc(d) => {
|
||||
f(&mut d.definition_point);
|
||||
f(&mut d.first_extension_point);
|
||||
f(&mut d.second_extension_point);
|
||||
f(&mut d.center_point);
|
||||
if d.has_leader {
|
||||
f(&mut d.first_leader_point);
|
||||
f(&mut d.second_leader_point);
|
||||
}
|
||||
}
|
||||
Dimension::LargeRadial(d) => {
|
||||
f(&mut d.definition_point);
|
||||
f(&mut d.chord_point);
|
||||
f(&mut d.override_center);
|
||||
f(&mut d.jog_point);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -677,6 +820,29 @@ impl Grippable for Dimension {
|
||||
center_grip(2, dv3(&d.leader_endpoint)),
|
||||
center_grip(3, text),
|
||||
],
|
||||
Dimension::Arc(d) => {
|
||||
let mut grips = vec![
|
||||
square_grip(0, dv3(&d.center_point)),
|
||||
center_grip(1, dv3(&d.first_extension_point)),
|
||||
center_grip(2, dv3(&d.second_extension_point)),
|
||||
center_grip(3, dv3(&d.definition_point)),
|
||||
];
|
||||
if d.has_leader {
|
||||
grips.push(center_grip(4, dv3(&d.first_leader_point)));
|
||||
grips.push(center_grip(5, dv3(&d.second_leader_point)));
|
||||
grips.push(center_grip(6, text));
|
||||
} else {
|
||||
grips.push(center_grip(4, text));
|
||||
}
|
||||
grips
|
||||
}
|
||||
Dimension::LargeRadial(d) => vec![
|
||||
square_grip(0, dv3(&d.definition_point)),
|
||||
center_grip(1, dv3(&d.chord_point)),
|
||||
center_grip(2, dv3(&d.override_center)),
|
||||
center_grip(3, dv3(&d.jog_point)),
|
||||
center_grip(4, text),
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -687,6 +853,8 @@ impl Grippable for Dimension {
|
||||
Dimension::Radius(_) | Dimension::Diameter(_) => 2,
|
||||
Dimension::Angular2Ln(_) | Dimension::Angular3Pt(_) => 4,
|
||||
Dimension::Ordinate(_) => 3,
|
||||
Dimension::Arc(d) => if d.has_leader { 6 } else { 4 },
|
||||
Dimension::LargeRadial(_) => 4,
|
||||
};
|
||||
if grip_id == text_grip {
|
||||
apply_to_v3(&mut self.base_mut().text_middle_point, &apply);
|
||||
@@ -739,6 +907,22 @@ impl Grippable for Dimension {
|
||||
2 => apply_to_v3(&mut d.leader_endpoint, &apply),
|
||||
_ => {}
|
||||
},
|
||||
Dimension::Arc(d) => match grip_id {
|
||||
0 => apply_to_v3(&mut d.center_point, &apply),
|
||||
1 => apply_to_v3(&mut d.first_extension_point, &apply),
|
||||
2 => apply_to_v3(&mut d.second_extension_point, &apply),
|
||||
3 => apply_to_v3(&mut d.definition_point, &apply),
|
||||
4 => apply_to_v3(&mut d.first_leader_point, &apply),
|
||||
5 => apply_to_v3(&mut d.second_leader_point, &apply),
|
||||
_ => {}
|
||||
},
|
||||
Dimension::LargeRadial(d) => match grip_id {
|
||||
0 => apply_to_v3(&mut d.definition_point, &apply),
|
||||
1 => apply_to_v3(&mut d.chord_point, &apply),
|
||||
2 => apply_to_v3(&mut d.override_center, &apply),
|
||||
3 => apply_to_v3(&mut d.jog_point, &apply),
|
||||
_ => {}
|
||||
},
|
||||
}
|
||||
self.base_mut().actual_measurement = self.measurement();
|
||||
}
|
||||
@@ -750,6 +934,8 @@ impl Grippable for Dimension {
|
||||
Dimension::Radius(_) | Dimension::Diameter(_) => (1, 2),
|
||||
Dimension::Angular2Ln(_) | Dimension::Angular3Pt(_) => (3, 4),
|
||||
Dimension::Ordinate(_) => (0, 3),
|
||||
Dimension::Arc(d) => (3, if d.has_leader { 6 } else { 4 }),
|
||||
Dimension::LargeRadial(_) => (3, 4),
|
||||
};
|
||||
if grip_id == text_grip {
|
||||
vec![
|
||||
@@ -816,6 +1002,8 @@ impl Grippable for Dimension {
|
||||
Dimension::Radius(_) | Dimension::Diameter(_) => (1, 2),
|
||||
Dimension::Angular2Ln(_) | Dimension::Angular3Pt(_) => (3, 4),
|
||||
Dimension::Ordinate(_) => (0, 3),
|
||||
Dimension::Arc(d) => (3, if d.has_leader { 6 } else { 4 }),
|
||||
Dimension::LargeRadial(_) => (3, 4),
|
||||
};
|
||||
match action {
|
||||
A::ResetText if grip_id == text_grip => {
|
||||
@@ -1860,6 +2048,7 @@ fn dimension_geometry(
|
||||
lv(d.dimension_arc),
|
||||
arrow1,
|
||||
arrow2,
|
||||
None,
|
||||
);
|
||||
}
|
||||
Dimension::Angular3Pt(d) => {
|
||||
@@ -1871,6 +2060,7 @@ fn dimension_geometry(
|
||||
lv(d.definition_point),
|
||||
arrow1,
|
||||
arrow2,
|
||||
None,
|
||||
);
|
||||
}
|
||||
Dimension::Ordinate(d) => {
|
||||
@@ -1885,10 +2075,74 @@ fn dimension_geometry(
|
||||
lv(d.leader_endpoint),
|
||||
);
|
||||
}
|
||||
Dimension::Arc(d) => {
|
||||
append_angular_dimension(
|
||||
&mut g,
|
||||
lv(d.center_point),
|
||||
lv(d.first_extension_point),
|
||||
lv(d.second_extension_point),
|
||||
lv(d.definition_point),
|
||||
arrow1,
|
||||
arrow2,
|
||||
d.is_partial.then_some((
|
||||
d.arc_start_parameter as f32,
|
||||
d.arc_end_parameter as f32,
|
||||
)),
|
||||
);
|
||||
if d.has_leader {
|
||||
add_segment(
|
||||
&mut g.dim_lines,
|
||||
lv(d.first_leader_point),
|
||||
lv(d.second_leader_point),
|
||||
);
|
||||
}
|
||||
}
|
||||
Dimension::LargeRadial(d) => {
|
||||
let chord = lv(d.chord_point);
|
||||
let jog = lv(d.jog_point);
|
||||
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);
|
||||
append_arrow(
|
||||
&mut g,
|
||||
chord,
|
||||
normalized_or(near - chord, Vec3::X),
|
||||
arrow1,
|
||||
);
|
||||
}
|
||||
}
|
||||
g
|
||||
}
|
||||
|
||||
fn jogged_radial_break(
|
||||
chord: Vec3,
|
||||
jog: Vec3,
|
||||
override_center: Vec3,
|
||||
jog_angle: f32,
|
||||
) -> (Vec3, Vec3) {
|
||||
let radial = normalized_or(chord - override_center, Vec3::X);
|
||||
let (sin, cos) = jog_angle.sin_cos();
|
||||
let transverse = normalized_or(
|
||||
Vec3::new(
|
||||
radial.x * cos - radial.y * sin,
|
||||
radial.x * sin + radial.y * cos,
|
||||
0.0,
|
||||
),
|
||||
Vec3::Y,
|
||||
);
|
||||
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 append_linear_dimension(
|
||||
g: &mut DimGeom,
|
||||
first: Vec3,
|
||||
@@ -2058,6 +2312,7 @@ fn append_angular_dimension(
|
||||
arc_point: Vec3,
|
||||
arrow1: &ArrowKind,
|
||||
arrow2: &ArrowKind,
|
||||
explicit_sweep: Option<(f32, f32)>,
|
||||
) {
|
||||
let radius = vertex.distance(arc_point);
|
||||
if radius <= 1e-6 {
|
||||
@@ -2067,18 +2322,19 @@ fn append_angular_dimension(
|
||||
}
|
||||
// Extension lines run from each measured point out to the dimension arc so
|
||||
// there is no gap between a ray and its arc endpoint. (#181 / DIM-027)
|
||||
let dir1 = normalized_or(first - vertex, Vec3::X);
|
||||
let dir2 = normalized_or(second - vertex, Vec3::X);
|
||||
let measured_start = (first.y - vertex.y).atan2(first.x - vertex.x);
|
||||
let measured_end = (second.y - vertex.y).atan2(second.x - vertex.x);
|
||||
let (start, mut end) = explicit_sweep.unwrap_or((measured_start, measured_end));
|
||||
let dir1 = Vec3::new(start.cos(), start.sin(), 0.0);
|
||||
let dir2 = Vec3::new(end.cos(), end.sin(), 0.0);
|
||||
add_segment(&mut g.ext_lines, first, vertex + dir1 * radius);
|
||||
add_segment(&mut g.ext_lines, second, vertex + dir2 * radius);
|
||||
|
||||
let start = (first.y - vertex.y).atan2(first.x - vertex.x);
|
||||
let mut end = (second.y - vertex.y).atan2(second.x - vertex.x);
|
||||
let mut delta = end - start;
|
||||
while delta <= 0.0 {
|
||||
delta += std::f32::consts::TAU;
|
||||
}
|
||||
if delta > std::f32::consts::PI {
|
||||
if explicit_sweep.is_none() && delta > std::f32::consts::PI {
|
||||
end -= std::f32::consts::TAU;
|
||||
delta = end - start;
|
||||
}
|
||||
@@ -2142,6 +2398,25 @@ fn dimension_snap_pts(dim: &Dimension) -> Vec<(glam::DVec3, SnapHint)> {
|
||||
node(d.feature_location),
|
||||
node(d.leader_endpoint),
|
||||
],
|
||||
Dimension::Arc(d) => {
|
||||
let mut points = vec![
|
||||
node(d.center_point),
|
||||
node(d.first_extension_point),
|
||||
node(d.second_extension_point),
|
||||
node(d.definition_point),
|
||||
];
|
||||
if d.has_leader {
|
||||
points.push(node(d.first_leader_point));
|
||||
points.push(node(d.second_leader_point));
|
||||
}
|
||||
points
|
||||
}
|
||||
Dimension::LargeRadial(d) => vec![
|
||||
node(d.definition_point),
|
||||
node(d.chord_point),
|
||||
node(d.override_center),
|
||||
node(d.jog_point),
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2383,7 +2658,7 @@ fn dimension_text_parts(
|
||||
} else {
|
||||
let v = format_linear_value(dim.measurement(), style);
|
||||
match dim {
|
||||
Dimension::Radius(_) => format!("R{}", v),
|
||||
Dimension::Radius(_) | Dimension::LargeRadial(_) => format!("R{}", v),
|
||||
Dimension::Diameter(_) => format!("Ø{}", v),
|
||||
_ => v,
|
||||
}
|
||||
@@ -2858,6 +3133,8 @@ fn dimension_text_position(dim: &Dimension) -> Vec3 {
|
||||
Dimension::Angular2Ln(d) => lv(d.dimension_arc),
|
||||
Dimension::Angular3Pt(d) => lv(d.definition_point),
|
||||
Dimension::Ordinate(d) => lv(d.leader_endpoint),
|
||||
Dimension::Arc(d) => lv(d.definition_point),
|
||||
Dimension::LargeRadial(d) => lv(d.jog_point),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3037,6 +3314,8 @@ fn dimension_text_pos_f64(
|
||||
Dimension::Angular2Ln(d) => d.dimension_arc,
|
||||
Dimension::Angular3Pt(d) => d.definition_point,
|
||||
Dimension::Ordinate(d) => d.leader_endpoint,
|
||||
Dimension::Arc(d) => d.definition_point,
|
||||
Dimension::LargeRadial(d) => d.jog_point,
|
||||
_ => base.text_middle_point,
|
||||
};
|
||||
Vector3::new(mid.x, mid.y + perp_off * perp_sign_default(), mid.z)
|
||||
|
||||
@@ -57,6 +57,7 @@ pub fn ui_name(e: &EntityType) -> &'static str {
|
||||
EntityType::Light(_) => "Light",
|
||||
EntityType::SectionSymbol(_) => "Section Symbol",
|
||||
EntityType::ViewBorder(_) => "View Border",
|
||||
EntityType::Extended(_) => "Extended Entity",
|
||||
EntityType::Seqend(_) => "Seqend",
|
||||
EntityType::Unknown(_) => "Unknown",
|
||||
}
|
||||
@@ -68,6 +69,9 @@ pub fn ui_name(e: &EntityType) -> &'static str {
|
||||
/// defers to [`ui_name`]. Falls back to "Unknown" for a class we couldn't name
|
||||
/// (the numeric `DWG_TYPE_<n>` placeholder).
|
||||
pub fn ui_name_or_class(e: &EntityType) -> String {
|
||||
if let EntityType::Extended(entity) = e {
|
||||
return entity.class_name().to_string();
|
||||
}
|
||||
if let EntityType::Unknown(u) = e {
|
||||
let n = u.dxf_name.trim();
|
||||
if !n.is_empty() && !n.starts_with("DWG_TYPE_") {
|
||||
|
||||
@@ -97,7 +97,7 @@ pub trait TextContent {
|
||||
/// Human-readable variant name used by Quick Select / Select Similar
|
||||
/// filtering. Stable across releases — the strings here are the canonical
|
||||
/// type identifiers throughout the UI.
|
||||
pub fn entity_type_name(et: &EntityType) -> &'static str {
|
||||
pub fn entity_type_name(et: &EntityType) -> &str {
|
||||
match et {
|
||||
EntityType::Point(_) => "Point",
|
||||
EntityType::Line(_) => "Line",
|
||||
@@ -145,6 +145,7 @@ pub fn entity_type_name(et: &EntityType) -> &'static str {
|
||||
EntityType::Light(_) => "Light",
|
||||
EntityType::SectionSymbol(_) => "SectionSymbol",
|
||||
EntityType::ViewBorder(_) => "ViewBorder",
|
||||
EntityType::Extended(entity) => entity.class_name(),
|
||||
EntityType::Unknown(_) => "Unknown",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -527,20 +527,6 @@ pub fn dropped_on_save_count(
|
||||
.dwg_source_version
|
||||
.is_some_and(|source| source != target_version)
|
||||
}
|
||||
acadrust::EntityType::SectionSymbol(entity) => {
|
||||
is_dxf
|
||||
|| entity.raw_dwg_data.is_none()
|
||||
|| entity
|
||||
.dwg_source_version
|
||||
.is_some_and(|source| source != target_version)
|
||||
}
|
||||
acadrust::EntityType::ViewBorder(entity) => {
|
||||
is_dxf
|
||||
|| entity.raw_dwg_data.is_none()
|
||||
|| entity
|
||||
.dwg_source_version
|
||||
.is_some_and(|source| source != target_version)
|
||||
}
|
||||
_ => false,
|
||||
};
|
||||
if dropped {
|
||||
|
||||
@@ -502,12 +502,23 @@ fn dim_arc_segs(
|
||||
while sweep <= -PI {
|
||||
sweep += 2.0 * PI;
|
||||
}
|
||||
dim_arc_segs_with_sweep(center, radius, a1, sweep, common)
|
||||
}
|
||||
|
||||
fn dim_arc_segs_with_sweep(
|
||||
center: Vector3,
|
||||
radius: f64,
|
||||
start: f64,
|
||||
sweep: f64,
|
||||
common: &acadrust::entities::EntityCommon,
|
||||
) -> Vec<EntityType> {
|
||||
use std::f64::consts::PI;
|
||||
let steps = 12usize.max((sweep.abs() / (PI / 36.0)).ceil() as usize);
|
||||
let pt = |a: f64| Vector3::new(center.x + radius * a.cos(), center.y + radius * a.sin(), center.z);
|
||||
let mut out = Vec::new();
|
||||
let mut prev = pt(a1);
|
||||
let mut prev = pt(start);
|
||||
for i in 1..=steps {
|
||||
let cur = pt(a1 + sweep * (i as f64 / steps as f64));
|
||||
let cur = pt(start + sweep * (i as f64 / steps as f64));
|
||||
out.push(dim_seg(prev, cur, common));
|
||||
prev = cur;
|
||||
}
|
||||
@@ -631,10 +642,12 @@ fn angular_block_segs(
|
||||
met: &DimMetrics,
|
||||
ext_c: &acadrust::entities::EntityCommon,
|
||||
dim_c: &acadrust::entities::EntityCommon,
|
||||
explicit_sweep: Option<(f64, f64)>,
|
||||
) -> Vec<EntityType> {
|
||||
use std::f64::consts::PI;
|
||||
let a1 = (p1.y - vertex.y).atan2(p1.x - vertex.x);
|
||||
let a2 = (p2.y - vertex.y).atan2(p2.x - vertex.x);
|
||||
let measured_a1 = (p1.y - vertex.y).atan2(p1.x - vertex.x);
|
||||
let measured_a2 = (p2.y - vertex.y).atan2(p2.x - vertex.x);
|
||||
let (a1, a2) = explicit_sweep.unwrap_or((measured_a1, measured_a2));
|
||||
let radius = ((arc_loc.x - vertex.x).powi(2) + (arc_loc.y - vertex.y).powi(2)).sqrt();
|
||||
let mut out = Vec::new();
|
||||
if radius < 1e-9 {
|
||||
@@ -647,14 +660,29 @@ fn angular_block_segs(
|
||||
let e2 = Vector3::new(vertex.x + a2.cos() * radius, vertex.y + a2.sin() * radius, vertex.z);
|
||||
out.push(dim_seg(p1, e1, ext_c));
|
||||
out.push(dim_seg(p2, e2, ext_c));
|
||||
out.extend(dim_arc_segs(vertex, radius, a1, a2, dim_c));
|
||||
// Terminators tangent to the arc at each end, pointing along the sweep.
|
||||
let mut sweep = a2 - a1;
|
||||
while sweep > PI {
|
||||
sweep -= 2.0 * PI;
|
||||
if explicit_sweep.is_some() {
|
||||
while sweep <= 0.0 {
|
||||
sweep += 2.0 * PI;
|
||||
}
|
||||
out.extend(dim_arc_segs_with_sweep(
|
||||
vertex,
|
||||
radius,
|
||||
a1,
|
||||
sweep,
|
||||
dim_c,
|
||||
));
|
||||
} else {
|
||||
out.extend(dim_arc_segs(vertex, radius, a1, a2, dim_c));
|
||||
}
|
||||
while sweep <= -PI {
|
||||
sweep += 2.0 * PI;
|
||||
// Terminators tangent to the arc at each end, pointing along the sweep.
|
||||
if explicit_sweep.is_none() {
|
||||
while sweep > PI {
|
||||
sweep -= 2.0 * PI;
|
||||
}
|
||||
while sweep <= -PI {
|
||||
sweep += 2.0 * PI;
|
||||
}
|
||||
}
|
||||
let sgn = if sweep >= 0.0 { 1.0 } else { -1.0 };
|
||||
out.extend(dim_terminator(e1, -a1.sin() * sgn, a1.cos() * sgn, &met.arrow1, dim_c));
|
||||
@@ -662,6 +690,49 @@ fn angular_block_segs(
|
||||
out
|
||||
}
|
||||
|
||||
fn jogged_radial_break(
|
||||
chord: Vector3,
|
||||
jog: Vector3,
|
||||
override_center: Vector3,
|
||||
jog_angle: f64,
|
||||
) -> (Vector3, Vector3) {
|
||||
let radial = norm2(
|
||||
chord.x - override_center.x,
|
||||
chord.y - override_center.y,
|
||||
1.0,
|
||||
0.0,
|
||||
);
|
||||
let (sin, cos) = jog_angle.sin_cos();
|
||||
let transverse = (
|
||||
radial.0 * cos - radial.1 * sin,
|
||||
radial.0 * sin + radial.1 * cos,
|
||||
);
|
||||
let length = ((chord.x - override_center.x).powi(2)
|
||||
+ (chord.y - override_center.y).powi(2))
|
||||
.sqrt();
|
||||
let half = (length * 0.04).max(1e-6);
|
||||
let first = Vector3::new(
|
||||
jog.x - transverse.0 * half,
|
||||
jog.y - transverse.1 * half,
|
||||
jog.z,
|
||||
);
|
||||
let second = Vector3::new(
|
||||
jog.x + transverse.0 * half,
|
||||
jog.y + transverse.1 * half,
|
||||
jog.z,
|
||||
);
|
||||
let distance_squared = |point: Vector3| {
|
||||
(point.x - chord.x).powi(2)
|
||||
+ (point.y - chord.y).powi(2)
|
||||
+ (point.z - chord.z).powi(2)
|
||||
};
|
||||
if distance_squared(first) <= distance_squared(second) {
|
||||
(first, second)
|
||||
} else {
|
||||
(second, first)
|
||||
}
|
||||
}
|
||||
|
||||
/// The text anchor for a radial leader: the saved text middle point when set,
|
||||
/// else the midpoint of `a` and `b` — mirroring the live `dimension_text_position`.
|
||||
fn dim_text_anchor(
|
||||
@@ -902,6 +973,7 @@ fn explode_dimension(dim: &Dimension, doc: &CadDocument) -> Vec<EntityType> {
|
||||
&met,
|
||||
&ext_c,
|
||||
&dim_c,
|
||||
None,
|
||||
));
|
||||
}
|
||||
Dimension::Angular3Pt(d) => {
|
||||
@@ -913,12 +985,57 @@ fn explode_dimension(dim: &Dimension, doc: &CadDocument) -> Vec<EntityType> {
|
||||
&met,
|
||||
&ext_c,
|
||||
&dim_c,
|
||||
None,
|
||||
));
|
||||
}
|
||||
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));
|
||||
}
|
||||
Dimension::Arc(d) => {
|
||||
result.extend(angular_block_segs(
|
||||
d.center_point,
|
||||
d.first_extension_point,
|
||||
d.second_extension_point,
|
||||
d.definition_point,
|
||||
&met,
|
||||
&ext_c,
|
||||
&dim_c,
|
||||
d.is_partial.then_some((
|
||||
d.arc_start_parameter,
|
||||
d.arc_end_parameter,
|
||||
)),
|
||||
));
|
||||
if d.has_leader {
|
||||
result.push(make_seg(
|
||||
&d.first_leader_point,
|
||||
&d.second_leader_point,
|
||||
&dim_c,
|
||||
));
|
||||
}
|
||||
}
|
||||
Dimension::LargeRadial(d) => {
|
||||
let (near, far) = jogged_radial_break(
|
||||
d.chord_point,
|
||||
d.jog_point,
|
||||
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));
|
||||
let len = ((near.x - d.chord_point.x).powi(2)
|
||||
+ (near.y - d.chord_point.y).powi(2))
|
||||
.sqrt()
|
||||
.max(1e-12);
|
||||
result.extend(dim_terminator(
|
||||
d.chord_point,
|
||||
(near.x - d.chord_point.x) / len,
|
||||
(near.y - d.chord_point.y) / len,
|
||||
&met.arrow1,
|
||||
&dim_c,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Measurement text is the live render's own Text/MText entity (value,
|
||||
@@ -963,6 +1080,8 @@ fn sync_dimension_base_points_and_collect_pending(doc: &mut CadDocument) -> Vec<
|
||||
Dimension::Diameter(value) => Some(value.definition_point),
|
||||
Dimension::Ordinate(value) => Some(value.definition_point),
|
||||
Dimension::Angular3Pt(value) => Some(value.definition_point),
|
||||
Dimension::Arc(value) => Some(value.definition_point),
|
||||
Dimension::LargeRadial(value) => Some(value.definition_point),
|
||||
Dimension::Angular2Ln(_) => None,
|
||||
};
|
||||
let base = dimension.base();
|
||||
|
||||
+25
-27
@@ -62,33 +62,31 @@ impl Scene {
|
||||
let eye_dir = cam.rotation * glam::Vec3::Z;
|
||||
let height = cam.ortho_size() * 2.0;
|
||||
let width = height; // caller can adjust; rough square
|
||||
acadrust::tables::View {
|
||||
handle: acadrust::types::Handle::NULL,
|
||||
name: name.to_string(),
|
||||
center: Vector3 {
|
||||
x: cam.target.x as f64,
|
||||
y: cam.target.y as f64,
|
||||
z: 0.0,
|
||||
},
|
||||
target: Vector3 {
|
||||
x: cam.target.x as f64,
|
||||
y: cam.target.y as f64,
|
||||
z: cam.target.z as f64,
|
||||
},
|
||||
direction: Vector3 {
|
||||
x: eye_dir.x as f64,
|
||||
y: eye_dir.y as f64,
|
||||
z: eye_dir.z as f64,
|
||||
},
|
||||
height: height as f64,
|
||||
width: width as f64,
|
||||
lens_length: 50.0,
|
||||
front_clip: 0.0,
|
||||
back_clip: 0.0,
|
||||
twist_angle: 0.0,
|
||||
// OCS saves an orthographic model view, not a perspective camera.
|
||||
perspective: false,
|
||||
}
|
||||
let mut view = acadrust::tables::View::new(name);
|
||||
view.center = Vector3 {
|
||||
x: cam.target.x as f64,
|
||||
y: cam.target.y as f64,
|
||||
z: 0.0,
|
||||
};
|
||||
view.target = Vector3 {
|
||||
x: cam.target.x as f64,
|
||||
y: cam.target.y as f64,
|
||||
z: cam.target.z as f64,
|
||||
};
|
||||
view.direction = Vector3 {
|
||||
x: eye_dir.x as f64,
|
||||
y: eye_dir.y as f64,
|
||||
z: eye_dir.z as f64,
|
||||
};
|
||||
view.height = height as f64;
|
||||
view.width = width as f64;
|
||||
view.lens_length = 50.0;
|
||||
view.front_clip = 0.0;
|
||||
view.back_clip = 0.0;
|
||||
view.twist_angle = 0.0;
|
||||
// OCS saves an orthographic model view, not a perspective camera.
|
||||
view.perspective = false;
|
||||
view
|
||||
}
|
||||
|
||||
/// Zoom the model-space camera in/out by a percentage.
|
||||
|
||||
@@ -37,7 +37,7 @@ fn section_arrow_dir_from_views(
|
||||
s: &acadrust::entities::SectionSymbol,
|
||||
) -> Option<[f64; 2]> {
|
||||
use acadrust::types::Handle as AHandle;
|
||||
if s.view_rep_handle == 0 {
|
||||
if s.view_rep_handle.is_null() {
|
||||
return None;
|
||||
}
|
||||
// A view's active viewport, via its border entity among the ViewRep refs.
|
||||
@@ -47,7 +47,7 @@ fn section_arrow_dir_from_views(
|
||||
_ => None,
|
||||
}
|
||||
};
|
||||
let parent_vr = AHandle::from(s.view_rep_handle);
|
||||
let parent_vr = s.view_rep_handle;
|
||||
let parent_refs = document.view_rep_refs.get(&parent_vr)?;
|
||||
let parent_vp_h = parent_refs.iter().find_map(border_vp)?;
|
||||
let sect_vr = parent_refs
|
||||
|
||||
@@ -298,7 +298,8 @@ impl ImageModel {
|
||||
/// the frame is degenerate or nothing in the blob decodes, so the caller
|
||||
/// falls back to the frame placeholder.
|
||||
pub fn from_ole2frame(ole: &acadrust::entities::Ole2Frame) -> Option<Self> {
|
||||
let (pixels, width, height) = super::ole_pres::decode(&ole.binary_data)?;
|
||||
let payload = ole.encoded_payload();
|
||||
let (pixels, width, height) = super::ole_pres::decode(&payload)?;
|
||||
|
||||
// Frame rectangle in WCS. `upper_left`/`lower_right` name the diagonal;
|
||||
// normalise to left/right/top/bottom so the bitmap sits upright.
|
||||
|
||||
@@ -55,7 +55,7 @@ impl Scene {
|
||||
if self.selected.is_empty() {
|
||||
return 0;
|
||||
}
|
||||
let pairs: rustc_hash::FxHashSet<(&'static str, String)> = self
|
||||
let pairs: rustc_hash::FxHashSet<(&str, String)> = self
|
||||
.selected
|
||||
.iter()
|
||||
.filter_map(|h| self.document.get_entity(*h))
|
||||
@@ -182,13 +182,13 @@ impl Scene {
|
||||
/// Returns the sorted set of entity-type names present in the active
|
||||
/// layout. Used to populate the Quick Select "Object type" dropdown
|
||||
/// with only the types that actually exist in the drawing.
|
||||
pub fn entity_type_names_in_layout(&self) -> Vec<&'static str> {
|
||||
pub fn entity_type_names_in_layout(&self) -> Vec<String> {
|
||||
use crate::entities::traits::entity_type_name;
|
||||
let mut names: std::collections::BTreeSet<&'static str> =
|
||||
let mut names: std::collections::BTreeSet<String> =
|
||||
std::collections::BTreeSet::new();
|
||||
for h in self.current_layout_entity_handles() {
|
||||
if let Some(e) = self.document.get_entity(h) {
|
||||
names.insert(entity_type_name(e));
|
||||
names.insert(entity_type_name(e).to_string());
|
||||
}
|
||||
}
|
||||
names.into_iter().collect()
|
||||
@@ -311,6 +311,7 @@ impl Scene {
|
||||
use acadrust::types::Color;
|
||||
match c {
|
||||
Color::ByLayer => "ByLayer".to_string(),
|
||||
Color::None => "None".to_string(),
|
||||
Color::ByBlock => "ByBlock".to_string(),
|
||||
Color::Index(i) => i.to_string(),
|
||||
Color::Rgb { r, g, b } => format!("{},{},{}", r, g, b),
|
||||
|
||||
@@ -45,6 +45,7 @@ pub fn color_to_aci_string(c: AcadColor) -> String {
|
||||
match c {
|
||||
AcadColor::ByBlock => "0".to_string(),
|
||||
AcadColor::ByLayer => "256".to_string(),
|
||||
AcadColor::None => "257".to_string(),
|
||||
AcadColor::Index(i) => i.to_string(),
|
||||
AcadColor::Rgb { .. } => "256".to_string(),
|
||||
}
|
||||
@@ -55,6 +56,7 @@ pub fn aci_string_to_color(s: &str) -> AcadColor {
|
||||
match s.trim().parse::<i16>().unwrap_or(256) {
|
||||
0 => AcadColor::ByBlock,
|
||||
256 => AcadColor::ByLayer,
|
||||
257 => AcadColor::None,
|
||||
n if (1..=255).contains(&n) => AcadColor::Index(n as u8),
|
||||
_ => AcadColor::ByLayer,
|
||||
}
|
||||
|
||||
@@ -1356,6 +1356,7 @@ fn picker_text_btn(label: &str, msg: Message) -> Element<'_, Message> {
|
||||
/// Returns an (iced::Color swatch_bg, display_label) pair for an AcadColor.
|
||||
pub fn acad_color_display(c: AcadColor) -> (Color, &'static str) {
|
||||
match c {
|
||||
AcadColor::None => (Color::TRANSPARENT, "None"),
|
||||
AcadColor::ByLayer => (
|
||||
Color {
|
||||
r: 0.35,
|
||||
|
||||
@@ -38,6 +38,7 @@ pub fn view_window<'a>(
|
||||
.map(|(idx, e)| {
|
||||
let color_str = match &e.color {
|
||||
acadrust::types::Color::ByLayer => "ByLayer".into(),
|
||||
acadrust::types::Color::None => "None".into(),
|
||||
acadrust::types::Color::ByBlock => "ByBlock".into(),
|
||||
acadrust::types::Color::Index(i) => format!("ACI {i}"),
|
||||
acadrust::types::Color::Rgb { r, g, b } => format!("#{r:02X}{g:02X}{b:02X}"),
|
||||
|
||||
@@ -116,6 +116,7 @@ const COLOR_OPTIONS: &[&str] = &[
|
||||
pub fn color_label(c: AcadColor) -> String {
|
||||
match c {
|
||||
AcadColor::ByLayer => "ByLayer".into(),
|
||||
AcadColor::None => "None".into(),
|
||||
AcadColor::ByBlock => "ByBlock".into(),
|
||||
AcadColor::Index(1) => "Red".into(),
|
||||
AcadColor::Index(2) => "Yellow".into(),
|
||||
|
||||
Reference in new issue
Block a user