forked from 1b-agent/opencadstudio-upstream
fix(dimstyle): preview respects per-end arrowheads
DimensionPreview drew identical generic triangles regardless of dimblk/dimblk1/dimblk2. It now resolves each end through the shared arrow_from_block_name mapping (widened to pub(crate)) and draws per-end glyphs, keeping DIMTSZ tick precedence. Tests: arrow_name_mapping_covers_preview_kinds pins the shared contract; 2 preview_arrow_tests pin per-end resolution (RED first). Full suite: 1989 passed / 0 failed / 25 ignored.
This commit is contained in:
1 parent
50e20a91c4
commit
29392de267
2 files changed
+186
-15
No files matched your search
@@ -2781,7 +2781,10 @@ pub(crate) fn arrow_from_block_with_deferred_hatch(
|
||||
.unwrap_or_else(|| arrow_from_block_name(None, dimasz))
|
||||
}
|
||||
|
||||
fn arrow_from_block_name(name: Option<&str>, dimasz: f32) -> ArrowKind {
|
||||
/// Canonical block-name → arrow-kind mapping shared by the renderer and the
|
||||
/// dimstyle preview so both agree on what a named arrowhead looks like.
|
||||
/// Unknown / empty names fall back to the standard filled triangle.
|
||||
pub(crate) fn arrow_from_block_name(name: Option<&str>, dimasz: f32) -> ArrowKind {
|
||||
name.and_then(|name| builtin_arrow_from_block_name(name, dimasz))
|
||||
.unwrap_or(ArrowKind::Triangle {
|
||||
size: dimasz,
|
||||
@@ -3453,6 +3456,36 @@ pub(crate) fn normalized_or(v: Vec3, fallback: Vec3) -> Vec3 {
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn arrow_name_mapping_covers_preview_kinds() {
|
||||
// Pins the shared name→kind contract the dimstyle preview relies on.
|
||||
assert!(matches!(
|
||||
arrow_from_block_name(Some("OPEN"), 1.0),
|
||||
ArrowKind::Open { .. }
|
||||
));
|
||||
assert!(matches!(
|
||||
arrow_from_block_name(Some("DOT"), 1.0),
|
||||
ArrowKind::Dot { filled: true, .. }
|
||||
));
|
||||
assert!(matches!(
|
||||
arrow_from_block_name(Some("ARCHTICK"), 1.0),
|
||||
ArrowKind::Tick { .. }
|
||||
));
|
||||
assert!(matches!(
|
||||
arrow_from_block_name(Some("NONE"), 1.0),
|
||||
ArrowKind::None
|
||||
));
|
||||
// Unknown / empty names fall back to the standard filled triangle.
|
||||
assert!(matches!(
|
||||
arrow_from_block_name(Some("NOPE"), 1.0),
|
||||
ArrowKind::Triangle { filled: true, .. }
|
||||
));
|
||||
assert!(matches!(
|
||||
arrow_from_block_name(None, 1.0),
|
||||
ArrowKind::Triangle { filled: true, .. }
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_split_mixed_polyline_straight_lines_have_taper_widths() {
|
||||
let tg_line = TangentGeom::Line {
|
||||
|
||||
+152
-14
@@ -1,6 +1,7 @@
|
||||
//! Dimension Style Manager window — fills the entire OS window.
|
||||
|
||||
use crate::app::{ColorPickTarget, DsField, Message};
|
||||
use crate::scene::convert::tessellate::{arrow_from_block_name, ArrowKind};
|
||||
use iced::widget::{
|
||||
button, canvas, checkbox, column, container, row, scrollable, text, text_input, Space,
|
||||
};
|
||||
@@ -171,11 +172,32 @@ struct DimensionPreview {
|
||||
dim2: bool,
|
||||
tick: bool,
|
||||
arrow_size: f32,
|
||||
arrow1: ArrowKind,
|
||||
arrow2: ArrowKind,
|
||||
text_above: bool,
|
||||
basic: bool,
|
||||
text: String,
|
||||
}
|
||||
|
||||
/// Resolve the staged arrowhead block names to per-end preview kinds: the
|
||||
/// shared `dimblk` when Dimsah is off, the per-end blocks when on — the same
|
||||
/// selection the renderer makes. `dimasz` is the preview arrow size so
|
||||
/// size-scaled kinds (SMALL, DOTSMALL) keep their proportions.
|
||||
fn preview_arrow_kinds(
|
||||
dimsah: bool,
|
||||
dimblk: &str,
|
||||
dimblk1: &str,
|
||||
dimblk2: &str,
|
||||
dimasz: f32,
|
||||
) -> (ArrowKind, ArrowKind) {
|
||||
let kind = |name: &str| arrow_from_block_name(Some(name), dimasz);
|
||||
if dimsah {
|
||||
(kind(dimblk1), kind(dimblk2))
|
||||
} else {
|
||||
(kind(dimblk), kind(dimblk))
|
||||
}
|
||||
}
|
||||
|
||||
impl canvas::Program<Message> for DimensionPreview {
|
||||
type State = ();
|
||||
|
||||
@@ -218,23 +240,107 @@ impl canvas::Program<Message> for DimensionPreview {
|
||||
}
|
||||
|
||||
let size = self.arrow_size.clamp(5.0, 14.0);
|
||||
for (tip_x, sign) in [(x1, 1.0_f32), (x2, -1.0_f32)] {
|
||||
// Per-end arrowheads: each end draws its own resolved kind so split
|
||||
// (Dimsah) configurations read correctly. A global DIMTSZ tick still
|
||||
// overrides both ends, matching AutoCAD precedence.
|
||||
let tick_path = |tip_x: f32, tick_size: f32| {
|
||||
canvas::Path::line(
|
||||
Point::new(tip_x - tick_size * 0.55, y + tick_size * 0.75),
|
||||
Point::new(tip_x + tick_size * 0.55, y - tick_size * 0.75),
|
||||
)
|
||||
};
|
||||
for (kind, tip_x, sign) in [(&self.arrow1, x1, 1.0_f32), (&self.arrow2, x2, -1.0_f32)] {
|
||||
if self.tick {
|
||||
frame.stroke(
|
||||
&line(
|
||||
Point::new(tip_x - size * 0.55, y + size * 0.75),
|
||||
Point::new(tip_x + size * 0.55, y - size * 0.75),
|
||||
),
|
||||
&tick_path(tip_x, size),
|
||||
canvas::Stroke::default().with_color(guide).with_width(1.6),
|
||||
);
|
||||
} else {
|
||||
let arrow = canvas::Path::new(|path| {
|
||||
path.move_to(Point::new(tip_x, y));
|
||||
path.line_to(Point::new(tip_x + sign * size, y - size * 0.42));
|
||||
path.line_to(Point::new(tip_x + sign * size, y + size * 0.42));
|
||||
path.close();
|
||||
});
|
||||
frame.fill(&arrow, guide);
|
||||
continue;
|
||||
}
|
||||
match kind {
|
||||
ArrowKind::None => {}
|
||||
ArrowKind::Triangle { size, filled, size_mul } => {
|
||||
let s = size * size_mul;
|
||||
let arrow = canvas::Path::new(|path| {
|
||||
path.move_to(Point::new(tip_x, y));
|
||||
path.line_to(Point::new(tip_x + sign * s, y - s * 0.42));
|
||||
path.line_to(Point::new(tip_x + sign * s, y + s * 0.42));
|
||||
path.close();
|
||||
});
|
||||
if *filled {
|
||||
frame.fill(&arrow, guide);
|
||||
} else {
|
||||
frame.stroke(&arrow, canvas::Stroke::default().with_color(guide).with_width(1.4));
|
||||
}
|
||||
}
|
||||
ArrowKind::Open { size, half_angle } => {
|
||||
let (sin, cos) = half_angle.sin_cos();
|
||||
for side in [-1.0_f32, 1.0_f32] {
|
||||
frame.stroke(
|
||||
&line(
|
||||
Point::new(tip_x, y),
|
||||
Point::new(tip_x + sign * size * cos, y + side * size * sin),
|
||||
),
|
||||
canvas::Stroke::default().with_color(guide).with_width(1.4),
|
||||
);
|
||||
}
|
||||
}
|
||||
ArrowKind::Dot { size, filled } => {
|
||||
let dot = canvas::Path::circle(Point::new(tip_x + sign * size * 0.5, y), size * 0.5);
|
||||
if *filled {
|
||||
frame.fill(&dot, guide);
|
||||
} else {
|
||||
frame.stroke(&dot, canvas::Stroke::default().with_color(guide).with_width(1.4));
|
||||
}
|
||||
}
|
||||
ArrowKind::Tick { size } => {
|
||||
frame.stroke(
|
||||
&tick_path(tip_x, *size),
|
||||
canvas::Stroke::default().with_color(guide).with_width(1.6),
|
||||
);
|
||||
}
|
||||
ArrowKind::Box_ { size, filled } => {
|
||||
let half = size * 0.45;
|
||||
let square = canvas::Path::rectangle(
|
||||
Point::new(tip_x + sign * half - half, y - half),
|
||||
iced::Size::new(half * 2.0, half * 2.0),
|
||||
);
|
||||
if *filled {
|
||||
frame.fill(&square, guide);
|
||||
} else {
|
||||
frame.stroke(&square, canvas::Stroke::default().with_color(guide).with_width(1.4));
|
||||
}
|
||||
}
|
||||
// Datum/Origin have dedicated CAD geometry; the preview keeps
|
||||
// the schematic triangle/dot respectively.
|
||||
ArrowKind::Datum { size, filled } => {
|
||||
let arrow = canvas::Path::new(|path| {
|
||||
path.move_to(Point::new(tip_x, y));
|
||||
path.line_to(Point::new(tip_x + sign * size, y - size * 0.42));
|
||||
path.line_to(Point::new(tip_x + sign * size, y + size * 0.42));
|
||||
path.close();
|
||||
});
|
||||
if *filled {
|
||||
frame.fill(&arrow, guide);
|
||||
} else {
|
||||
frame.stroke(&arrow, canvas::Stroke::default().with_color(guide).with_width(1.4));
|
||||
}
|
||||
}
|
||||
ArrowKind::Origin { size } => {
|
||||
let dot = canvas::Path::circle(Point::new(tip_x + sign * size * 0.5, y), size * 0.4);
|
||||
frame.stroke(&dot, canvas::Stroke::default().with_color(guide).with_width(1.4));
|
||||
}
|
||||
// Custom DWG block geometry cannot render in the schematic
|
||||
// preview; fall back to the standard filled triangle.
|
||||
ArrowKind::Custom { .. } => {
|
||||
let arrow = canvas::Path::new(|path| {
|
||||
path.move_to(Point::new(tip_x, y));
|
||||
path.line_to(Point::new(tip_x + sign * size, y - size * 0.42));
|
||||
path.line_to(Point::new(tip_x + sign * size, y + size * 0.42));
|
||||
path.close();
|
||||
});
|
||||
frame.fill(&arrow, guide);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1127,13 +1233,23 @@ pub fn view_window<'a>(
|
||||
preview_text.push_str(&format!(" +{} −{}", vals.dimtp.trim(), vals.dimtm.trim()));
|
||||
}
|
||||
}
|
||||
let arrow_size = vals.dimasz.trim().parse::<f32>().unwrap_or(1.0).abs() * 5.0 + 5.0;
|
||||
let (arrow1, arrow2) = preview_arrow_kinds(
|
||||
vals.dimsah,
|
||||
&vals.dimblk_name,
|
||||
&vals.dimblk1_name,
|
||||
&vals.dimblk2_name,
|
||||
arrow_size.clamp(5.0, 14.0),
|
||||
);
|
||||
let preview = canvas(DimensionPreview {
|
||||
ext1: !vals.dimse1,
|
||||
ext2: !vals.dimse2,
|
||||
dim1: !vals.dimsd1,
|
||||
dim2: !vals.dimsd2,
|
||||
tick: vals.dimtsz.trim().parse::<f32>().unwrap_or(0.0) > 0.0,
|
||||
arrow_size: vals.dimasz.trim().parse::<f32>().unwrap_or(1.0).abs() * 5.0 + 5.0,
|
||||
arrow_size,
|
||||
arrow1,
|
||||
arrow2,
|
||||
text_above: vals.dimtad.trim() != "0",
|
||||
basic: vals.dimgap.trim().starts_with('-'),
|
||||
text: preview_text,
|
||||
@@ -1231,3 +1347,25 @@ pub fn view_window<'a>(
|
||||
editor: right_panel.into(),
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod preview_arrow_tests {
|
||||
// Preview must resolve each end independently: shared block when Dimsah
|
||||
// is off, per-end blocks when on.
|
||||
use super::preview_arrow_kinds;
|
||||
use crate::scene::convert::tessellate::ArrowKind;
|
||||
|
||||
#[test]
|
||||
fn shared_block_feeds_both_ends_when_dimsah_off() {
|
||||
let (first, second) = preview_arrow_kinds(false, "OPEN", "DOT", "NONE", 10.0);
|
||||
assert!(matches!(first, ArrowKind::Open { .. }));
|
||||
assert!(matches!(second, ArrowKind::Open { .. }));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn per_end_blocks_feed_each_end_when_dimsah_on() {
|
||||
let (first, second) = preview_arrow_kinds(true, "OPEN", "DOT", "NONE", 10.0);
|
||||
assert!(matches!(first, ArrowKind::Dot { .. }));
|
||||
assert!(matches!(second, ArrowKind::None));
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user