parent
ecb29767d4
commit
759f2316aa
3 changed files with 118 additions and 14 deletions
|
|
@ -1279,7 +1279,34 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
|
||||||
if let Some(acadrust::EntityType::Hatch(dxf)) =
|
if let Some(acadrust::EntityType::Hatch(dxf)) =
|
||||||
self.tabs[i].scene.document.get_entity_mut(handle)
|
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!(
|
dxf.is_solid = matches!(
|
||||||
entry.gpu,
|
entry.gpu,
|
||||||
crate::scene::model::hatch_model::HatchPattern::Solid
|
crate::scene::model::hatch_model::HatchPattern::Solid
|
||||||
|
|
|
||||||
|
|
@ -99,6 +99,67 @@ fn boundary_centroid(h: &Hatch) -> Option<(f64, f64)> {
|
||||||
(n > 0.0).then(|| (sx / n, sy / n))
|
(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
|
/// Read the hatch background colour from its `HATCHBACKGROUNDCOLOR` extended
|
||||||
/// data (group 1071, packed true colour: high byte = colour method, low three =
|
/// data (group 1071, packed true colour: high byte = colour method, low three =
|
||||||
/// RGB). `None` when unset or not a true-colour value.
|
/// 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;
|
return;
|
||||||
};
|
};
|
||||||
match field {
|
match field {
|
||||||
"pattern_angle" if h.gradient_color.enabled => h.gradient_color.angle = v.to_radians(),
|
"pattern_angle" if h.gradient_color.enabled => {
|
||||||
"pattern_angle" => h.pattern_angle = v.to_radians(),
|
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,
|
// The stored pattern lines are the FINAL rendered geometry (offsets,
|
||||||
// base points and dashes in world units — see the prebaked path in
|
// base points and dashes in world units — see the prebaked path in
|
||||||
// hatch_model_from_dxf), so changing the scale must rescale them by
|
// 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;
|
let old = h.pattern_scale;
|
||||||
if old > 1e-12 {
|
if old > 1e-12 {
|
||||||
let k = v / old;
|
let k = v / old;
|
||||||
for line in h.pattern.lines.iter_mut() {
|
scale_pattern_geometry(&mut h.pattern, k);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
h.pattern_scale = v;
|
h.pattern_scale = v;
|
||||||
}
|
}
|
||||||
|
|
@ -817,10 +878,17 @@ impl Grippable for Hatch {
|
||||||
use crate::scene::model::object::GripMenuAction as A;
|
use crate::scene::model::object::GripMenuAction as A;
|
||||||
match action {
|
match action {
|
||||||
A::HatchAngle => {
|
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 => {
|
A::HatchScale => {
|
||||||
if value > 0.0 {
|
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;
|
self.pattern_scale = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -870,9 +870,18 @@ impl PropertiesPanel {
|
||||||
} else {
|
} else {
|
||||||
Some(current.to_string())
|
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(
|
let combo = combo_box(
|
||||||
&self.hatch_pattern_combo,
|
&self.hatch_pattern_combo,
|
||||||
VARIES_LABEL,
|
placeholder,
|
||||||
selected.as_ref(),
|
selected.as_ref(),
|
||||||
Message::PropHatchPatternChanged,
|
Message::PropHatchPatternChanged,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue