fix(hatch): apply pattern property edits

Closes #476
This commit is contained in:
Hakan Seven committed 2026-07-27 16:44:31 +03:00
1 parent ecb29767d4
commit 759f2316aa
3 files changed
+118 -14

No files matched your search

+28 -1
View File
@@ -1279,7 +1279,34 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
if let Some(acadrust::EntityType::Hatch(dxf)) =
self.tabs[i].scene.document.get_entity_mut(handle)
{
dxf.pattern = hatch_patterns::build_dxf_pattern(entry);
let old_origin = dxf.pattern.lines.first().map(|line| {
(line.base_point.x, line.base_point.y)
});
let mut pattern = hatch_patterns::build_dxf_pattern(entry);
// Stored pattern lines are final world-space
// geometry. Preserve the selected hatch's scale,
// angle and origin when replacing the catalog
// pattern.
crate::entities::hatch::scale_pattern_geometry(
&mut pattern,
dxf.pattern_scale,
);
crate::entities::hatch::rotate_pattern_geometry(
&mut pattern,
dxf.pattern_angle,
);
if let (Some((old_x, old_y)), Some(new_origin)) =
(old_origin, pattern.lines.first())
{
let dx = old_x - new_origin.base_point.x;
let dy = old_y - new_origin.base_point.y;
crate::entities::hatch::translate_pattern_geometry(
&mut pattern,
dx,
dy,
);
}
dxf.pattern = pattern;
dxf.is_solid = matches!(
entry.gpu,
crate::scene::model::hatch_model::HatchPattern::Solid
+80 -12
View File
@@ -99,6 +99,67 @@ fn boundary_centroid(h: &Hatch) -> Option<(f64, f64)> {
(n > 0.0).then(|| (sx / n, sy / n))
}
/// Scale the stored, final hatch-pattern geometry around the pattern origin.
/// Pattern lines loaded from DXF/DWG are rendered prebaked, so their metadata
/// scale is not applied again by the renderer.
pub(crate) fn scale_pattern_geometry(
pattern: &mut acadrust::entities::HatchPattern,
factor: f64,
) {
let (origin_x, origin_y) = pattern
.lines
.first()
.map(|line| (line.base_point.x, line.base_point.y))
.unwrap_or((0.0, 0.0));
for line in pattern.lines.iter_mut() {
line.base_point.x = origin_x + (line.base_point.x - origin_x) * factor;
line.base_point.y = origin_y + (line.base_point.y - origin_y) * factor;
line.offset.x *= factor;
line.offset.y *= factor;
for dash in line.dash_lengths.iter_mut() {
*dash *= factor;
}
}
}
/// Rotate the stored, final hatch-pattern geometry around the pattern origin.
/// This keeps the line angle, base point and world-space offset in sync.
pub(crate) fn rotate_pattern_geometry(
pattern: &mut acadrust::entities::HatchPattern,
angle: f64,
) {
let (sin, cos) = angle.sin_cos();
let (origin_x, origin_y) = pattern
.lines
.first()
.map(|line| (line.base_point.x, line.base_point.y))
.unwrap_or((0.0, 0.0));
for line in pattern.lines.iter_mut() {
line.angle += angle;
let (x, y) = (
line.base_point.x - origin_x,
line.base_point.y - origin_y,
);
line.base_point.x = origin_x + x * cos - y * sin;
line.base_point.y = origin_y + x * sin + y * cos;
let (x, y) = (line.offset.x, line.offset.y);
line.offset.x = x * cos - y * sin;
line.offset.y = x * sin + y * cos;
}
}
/// Move every stored line by the same amount, preserving the pattern phase.
pub(crate) fn translate_pattern_geometry(
pattern: &mut acadrust::entities::HatchPattern,
dx: f64,
dy: f64,
) {
for line in pattern.lines.iter_mut() {
line.base_point.x += dx;
line.base_point.y += dy;
}
}
/// Read the hatch background colour from its `HATCHBACKGROUNDCOLOR` extended
/// data (group 1071, packed true colour: high byte = colour method, low three =
/// RGB). `None` when unset or not a true-colour value.
@@ -483,8 +544,16 @@ fn apply_geom_prop(h: &mut Hatch, field: &str, value: &str) {
return;
};
match field {
"pattern_angle" if h.gradient_color.enabled => h.gradient_color.angle = v.to_radians(),
"pattern_angle" => h.pattern_angle = v.to_radians(),
"pattern_angle" if h.gradient_color.enabled => {
h.gradient_color.angle = v.to_radians();
h.pattern_angle = v.to_radians();
}
"pattern_angle" => {
let angle = v.to_radians();
let delta = angle - h.pattern_angle;
rotate_pattern_geometry(&mut h.pattern, delta);
h.pattern_angle = angle;
}
// The stored pattern lines are the FINAL rendered geometry (offsets,
// base points and dashes in world units — see the prebaked path in
// hatch_model_from_dxf), so changing the scale must rescale them by
@@ -493,15 +562,7 @@ fn apply_geom_prop(h: &mut Hatch, field: &str, value: &str) {
let old = h.pattern_scale;
if old > 1e-12 {
let k = v / old;
for line in h.pattern.lines.iter_mut() {
line.base_point.x *= k;
line.base_point.y *= k;
line.offset.x *= k;
line.offset.y *= k;
for d in line.dash_lengths.iter_mut() {
*d *= k;
}
}
scale_pattern_geometry(&mut h.pattern, k);
}
h.pattern_scale = v;
}
@@ -817,10 +878,17 @@ impl Grippable for Hatch {
use crate::scene::model::object::GripMenuAction as A;
match action {
A::HatchAngle => {
self.pattern_angle = value.to_radians();
let angle = value.to_radians();
let delta = angle - self.pattern_angle;
rotate_pattern_geometry(&mut self.pattern, delta);
self.pattern_angle = angle;
}
A::HatchScale => {
if value > 0.0 {
if self.pattern_scale > 1e-12 {
let factor = value / self.pattern_scale;
scale_pattern_geometry(&mut self.pattern, factor);
}
self.pattern_scale = value;
}
}
+10 -1
View File
@@ -870,9 +870,18 @@ impl PropertiesPanel {
} else {
Some(current.to_string())
};
// iced hides `selected` while the combo has focus and shows its
// placeholder instead. Use the real pattern name for a single hatch;
// a fixed VARIES placeholder made a correct ANSI31 value look mixed as
// soon as the dropdown opened (#476).
let placeholder = if current == VARIES_LABEL {
VARIES_LABEL
} else {
current
};
let combo = combo_box(
&self.hatch_pattern_combo,
VARIES_LABEL,
placeholder,
selected.as_ref(),
Message::PropHatchPatternChanged,
)