mirror of
https://github.com/PR0M3TH3AN/Artstr-Studio.git
synced 2026-10-27 08:40:39 +00:00
Pen tool: uniform-scale remap (fixes stroke distortion) + smaller handles
Two visual issues on top of the Phase D close-out commit:
1. **Stroke width varied along curves.** The pre-existing pipeline
stored nodes in 0-100 × 0-100 viewBox space and let the SVG
renderer pick `vbH = 100 * layer.h / layer.w` with
`preserveAspectRatio="none"`. For any non-square layer, the
0-100×0-100 node range got stretched non-uniformly into a 100×vbH
viewBox, distorting the path: strokes appeared wider where the
curve traveled more in one axis. Phase D's refit made this much
more likely because dragging anchors out of bounds routinely
produced non-square layers.
Fix in two places:
- finishPenPath: uniform-scale the bbox by `100/bbox.w` so y
range lands at exactly `100 * h/w = vbH`. The viewBox aspect
matches the layer aspect, no stretching.
- refitPenLayerIfNeeded: same uniform-scale invariant. Also
respects the layer's current vbH when deciding whether nodes
are "out of bounds" — was previously hard-coded at 100, which
wrongly fired refit on every drag in already-non-square layers.
2. **Anchor + handle glyphs were too prominent.** On a 1920px-wide
slide preview the old `dotR = 0.013 * unit` rendered ~25px
dots. Reduced to `0.0075 * unit` (~12px) and stroke from
`0.003 * unit` to `0.0018 * unit`. The in-progress draft
renderer matches.
Strokes now stay constant width all the way around a curve, and
the editing UI reads as overlay rather than as part of the artwork.
This commit is contained in:
1 parent
b825181356
commit
e5c06b7415
1 file changed
+42
-12
+42
-12
@@ -9999,23 +9999,39 @@
|
||||
if (p.x > maxX) maxX = p.x;
|
||||
if (p.y > maxY) maxY = p.y;
|
||||
}
|
||||
// No-op unless we're outside [0, 100] on any side. A bit of
|
||||
// tolerance to avoid micro-jiggle during normal in-bounds drags.
|
||||
// Layer viewBox is 0-100 × 0-vbH where vbH = 100*layer.h/layer.w
|
||||
// — the renderer's hard-coded formula. We want nodes to fit
|
||||
// x:[0,100] and y:[0,vbH], matching the layer's actual aspect
|
||||
// ratio after refit. The current viewBox-y bound is implied
|
||||
// by what the renderer would compute given new layer.w/h.
|
||||
//
|
||||
// No-op unless we're outside the current viewBox on any side
|
||||
// (with a small tolerance to avoid micro-jiggle during normal
|
||||
// in-bounds drags). The current viewBox y-max is `vbH` computed
|
||||
// from existing layer w/h.
|
||||
const currentVbH = layer.w > 0 ? 100 * layer.h / layer.w : 100;
|
||||
const EPS = 0.5;
|
||||
if (minX > -EPS && minY > -EPS && maxX < 100 + EPS && maxY < 100 + EPS) return;
|
||||
if (minX > -EPS && minY > -EPS && maxX < 100 + EPS && maxY < currentVbH + EPS) return;
|
||||
const newW = Math.max(EPS, maxX - minX);
|
||||
const newH = Math.max(EPS, maxY - minY);
|
||||
// Layer scales: in inches, layer.w grows in proportion to
|
||||
// newW vs current viewBox-x (always 100); layer.h grows in
|
||||
// proportion to newH vs the existing vbH.
|
||||
const sx = newW / 100;
|
||||
const sy = newH / 100;
|
||||
const sy = newH / currentVbH;
|
||||
// Layer position shifts in inches by the bbox offset scaled
|
||||
// to the existing layer w/h.
|
||||
layer.x = layer.x + (minX / 100) * layer.w;
|
||||
layer.y = layer.y + (minY / 100) * layer.h;
|
||||
layer.y = layer.y + (minY / currentVbH) * layer.h;
|
||||
layer.w = Math.max(0.01, layer.w * sx);
|
||||
layer.h = Math.max(0.01, layer.h * sy);
|
||||
// Uniform-scale remap so the new node range matches the new
|
||||
// viewBox aspect exactly — no stretching when the SVG paints
|
||||
// into the refitted layer rectangle.
|
||||
const remapScale = 100 / newW;
|
||||
const remap = (p) => ({
|
||||
x: (p.x - minX) / newW * 100,
|
||||
y: (p.y - minY) / newH * 100,
|
||||
x: (p.x - minX) * remapScale,
|
||||
y: (p.y - minY) * remapScale,
|
||||
});
|
||||
for (const n of nodes) {
|
||||
const r = remap(n);
|
||||
@@ -10113,8 +10129,13 @@
|
||||
clearPenDraftRender();
|
||||
const dims = currentCanvasDims();
|
||||
const unit = Math.max(dims.w, dims.h);
|
||||
const lineW = unit * 0.003;
|
||||
const dotR = unit * 0.013;
|
||||
// Smaller, less prominent anchors/handles — earlier sizes
|
||||
// (0.003 stroke, 0.013 anchor) rendered ~25px dots on a
|
||||
// 1920px-wide preview, which obscured short paths and made
|
||||
// the canvas feel cluttered. ~50% smaller reads as deliberate
|
||||
// editing UI without dominating.
|
||||
const lineW = unit * 0.0018;
|
||||
const dotR = unit * 0.0075;
|
||||
const nodes = layer.shape.pen.nodes;
|
||||
nodes.forEach((n, i) => {
|
||||
const a = penNodeToInch(layer, n);
|
||||
@@ -10469,7 +10490,14 @@
|
||||
if (n.hOut) allPts.push(n.hOut);
|
||||
}
|
||||
const bbox = pointsBbox(allPts);
|
||||
const remap = (pt) => ({ x: (pt.x - bbox.x) / bbox.w * 100, y: (pt.y - bbox.y) / bbox.h * 100 });
|
||||
// Uniform scaling: divide both x and y by bbox.w so nodes land
|
||||
// in 0-100 × 0-(100*h/w) — matching the renderer's vbH formula
|
||||
// (= 100 * layer.h / layer.w). Independent x/y scaling here
|
||||
// forces a 0-100×0-100 node range into a non-square viewBox
|
||||
// later, stretching curves and varying stroke widths around
|
||||
// the path. Uniform scaling keeps strokes a constant width.
|
||||
const scale = bbox.w > 0 ? 100 / bbox.w : 1;
|
||||
const remap = (pt) => ({ x: (pt.x - bbox.x) * scale, y: (pt.y - bbox.y) * scale });
|
||||
const normNodes = nodes.map(n => ({
|
||||
x: remap(n).x, y: remap(n).y,
|
||||
hIn: n.hIn ? remap(n.hIn) : null,
|
||||
@@ -10515,8 +10543,10 @@
|
||||
clearPenDraftRender();
|
||||
const dims = currentCanvasDims();
|
||||
const unit = Math.max(dims.w, dims.h);
|
||||
const lineW = unit * 0.004;
|
||||
const dotR = unit * 0.012;
|
||||
// Sized to match renderPenEditHandles so the in-progress
|
||||
// draw and the post-commit edit handles look consistent.
|
||||
const lineW = unit * 0.0025;
|
||||
const dotR = unit * 0.0075;
|
||||
|
||||
if (_penDraft.tool === 'pencil') {
|
||||
const pts = _penDraft.points;
|
||||
|
||||
Reference in new issue
Block a user