fix(hatch): anchor prebaked pattern origin near the geometry
Prebaked hatch fills anchored each pattern family at its authored
base point minus world_origin. On UTM-scale drawings that offset is
~1e6, which broke the fill two ways:
- the shader consumes x0/y0 as f32, so ~0.5 m quantization shredded
the pattern phase; and
- it evaluated the pattern that far from its origin, where the
multi-family aggregate patterns (AR-CONC, AR-SAND, GRAVEL, ...) —
effectively quasi-periodic — dissolve from their interlocking
stones into scattered dashes.
Boundaries were unaffected throughout; they ride the double-single
relative-to-eye path.
Fold the origin's offset from world_origin down to a small,
coherence-safe remainder on a `spacing * 64` grid, applied as one
common fold so every family keeps the relative phase that forms the
stones. Because the origin grip / Origin X/Y edit shifts every base
point by the same delta, the sub-grid remainder tracks it 1:1, so the
grip still moves the fill; a grid crossing needs a full `spacing * 64`
drag and is never hit by realistic alignment drags.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
dc7117f203
commit
0cd861cc76
1 changed files with 40 additions and 9 deletions
|
|
@ -1372,17 +1372,48 @@ impl Scene {
|
||||||
[0.0, 0.0]
|
[0.0, 0.0]
|
||||||
};
|
};
|
||||||
|
|
||||||
// Anchor each prebaked family at its stored pattern origin (per-line
|
// Anchor the prebaked families near the geometry, tracking the pattern
|
||||||
// `base_point`), expressed relative to `world_origin` so it shares the
|
// origin. The DWG base points sit at the pattern's *authored* origin,
|
||||||
// boundary's coordinate frame. Left at 0 the pattern tiles from the
|
// which on a UTM drawing can be ~1e6 units from the hatch. Handing the
|
||||||
// boundary centre instead: invisible for a solid infinite-line family
|
// shader `base - world_origin` (~1e6) is wrong two ways: it consumes
|
||||||
// but the wrong phase for dashed / offset patterns and for a user-moved
|
// `x0/y0` as f32 (so ~0.5 m quantization shreds the phase — boundaries
|
||||||
// origin (Origin X/Y property + origin grip).
|
// are unaffected, they ride the double-single relative-to-eye path), AND
|
||||||
|
// it evaluates the pattern that far from its origin. Multi-family
|
||||||
|
// aggregate patterns (AR-CONC, AR-SAND, GRAVEL, …) are effectively
|
||||||
|
// quasi-periodic: their families only cohere into the intended
|
||||||
|
// stones/grains near their shared origin and dissolve into scattered
|
||||||
|
// dashes at large offsets.
|
||||||
|
//
|
||||||
|
// So fold the origin's offset from `world_origin` down to a small,
|
||||||
|
// coherence-safe remainder on a grid of `spacing * 64` (many multiples
|
||||||
|
// of the pattern spacing — well inside the coherence range yet far
|
||||||
|
// larger than any realistic grip drag). Applying ONE common fold (from
|
||||||
|
// the reference line) preserves each family's relative phase, which is
|
||||||
|
// what forms the stones. Because the origin grip / Origin X/Y edit
|
||||||
|
// shifts every base point by the same delta, the sub-grid remainder
|
||||||
|
// tracks that delta 1:1, so the grip still moves the fill; a grid
|
||||||
|
// crossing (a whole `spacing * 64` drag) is never hit in practice.
|
||||||
if prebaked {
|
if prebaked {
|
||||||
if let model::hatch_model::HatchPattern::Pattern(fams) = &mut pattern {
|
if let model::hatch_model::HatchPattern::Pattern(fams) = &mut pattern {
|
||||||
for (fam, ln) in fams.iter_mut().zip(dxf.pattern.lines.iter()) {
|
if let Some(rb) = dxf.pattern.lines.first().map(|l| l.base_point) {
|
||||||
fam.x0 = (ln.base_point.x - world_origin[0]) as f32;
|
let spacing = dxf
|
||||||
fam.y0 = (ln.base_point.y - world_origin[1]) as f32;
|
.pattern
|
||||||
|
.lines
|
||||||
|
.iter()
|
||||||
|
.map(|l| l.offset.length())
|
||||||
|
.fold(0.0_f64, f64::max)
|
||||||
|
.max(1e-6);
|
||||||
|
let cell = spacing * 64.0;
|
||||||
|
let fold = |v: f64, o: f64| -> f64 {
|
||||||
|
let d = v - o;
|
||||||
|
d - (d / cell).round() * cell
|
||||||
|
};
|
||||||
|
let ox = fold(rb.x, world_origin[0]);
|
||||||
|
let oy = fold(rb.y, world_origin[1]);
|
||||||
|
for (fam, ln) in fams.iter_mut().zip(dxf.pattern.lines.iter()) {
|
||||||
|
fam.x0 = (ln.base_point.x - rb.x + ox) as f32;
|
||||||
|
fam.y0 = (ln.base_point.y - rb.y + oy) as f32;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue