Random-Schraffur modellraum-verankert + Motiv-Editor fuer Custom-Linien

Random-Verankerung (Bugfix): die Streu-Striche haengen nicht mehr an der
Polygon-Bounding-Box, sondern an einem absoluten Modellraum-Gitter
(hashCell aus absoluten Zell-Indizes + hatch.seed). Beim Vergroessern der
Flaeche bleiben bestehende Striche stehen, am Rand kommen neue dazu, die
Dichte bleibt konstant, Verschieben wandert nicht; 'Neu wuerfeln' (neuer
Seed) verschiebt das ganze Feld. Alle Renderpfade ziehen aus derselben
Funktion. Verankerungs- und Verschiebe-Dichte-Test ergaenzt.

Motiv-Editor: neuer wiederverwendbarer MotifEditor (Punkte setzen/ziehen in
einer Einheitszelle, Live-Loop-Vorschau). LineStyle.kind 'custom' + motif
(points/length), additiv durchgereicht (analog zigzag) und in allen
Renderpfaden gekachelt (motifPoints, Verallgemeinerung von zigzagPoints).
ResourceManager-Umschalter Vollinie/Strich/Zickzack/Motiv, LineSwatch-
Vorschau. Insgesamt 5 neue Tests, 113 gruen.
This commit is contained in:
2026-07-04 01:09:00 +02:00
parent b4dd396768
commit 3d1fa40402
15 changed files with 714 additions and 58 deletions
+38
View File
@@ -292,6 +292,44 @@ export function LineSwatch({
strokeLinecap="round"
/>
);
} else if (style.kind === "custom" && style.motif && style.motif.points.length >= 2) {
// Custom-Motiv: die Einheitszelle (mm) wird entlang der Linie gekachelt
// (mm-Papier → px ·4, wie das Dash-Muster). Deckungsgleich zur motifPoints-
// Kachelung im Plan-Renderer, hier nur schematisch/px.
const PX_PER_MM = 4;
const motif = style.motif;
const cellPx = Math.max(2, motif.length * PX_PER_MM);
let d = "";
let started = false;
let lastX = -1;
let lastY = 0;
let x = x0;
let rep = 0;
outer: while (x <= x1 && rep < 400) {
const base = x0 + rep * cellPx;
for (const p of motif.points) {
const px2 = base + p.x * PX_PER_MM;
if (px2 > x1 + 0.5) break outer;
const py2 = midY - p.y * PX_PER_MM;
if (Math.abs(px2 - lastX) < 0.01 && Math.abs(py2 - lastY) < 0.01) continue;
d += `${started ? " L" : "M"} ${px2.toFixed(1)} ${py2.toFixed(1)}`;
started = true;
lastX = px2;
lastY = py2;
}
x = base + cellPx;
rep++;
}
path = (
<path
d={d || `M ${x0} ${midY} L ${x1} ${midY}`}
fill="none"
stroke="currentColor"
strokeWidth={px}
strokeLinejoin="round"
strokeLinecap="round"
/>
);
} else {
const dasharray = style.dash ? style.dash.map((d) => d * 4).join(" ") : undefined;
path = (