mirror of
https://github.com/PR0M3TH3AN/Artstr-Studio.git
synced 2026-10-27 08:40:39 +00:00
Pen tool: write viewBox.h to match layer aspect for uniform strokes
The previous "uniform-scale remap" commit fixed node coordinates to land in 0-100 × 0-(100*h/w), but the renderer (buildShapeSvgInner path branch) reads shape.viewBox and computes sx = 100/vb.w, sy = vbH/vb.h. With shape.viewBox.h hardcoded to 100, sx ≠ sy for non-square layers and the path got stretched again — strokes distorted around curves after the path was committed, even though the in-progress draft (which renders inside the pen overlay, not the layer SVG) looked fine. Fix is the matching half: also write shape.viewBox.h = 100*lh/lw wherever we touch the pen data, so sx == sy and the renderer applies a uniform scale. - finishPenPath now sets viewBox.h = vbH from the bbox aspect. - regenPenLayerD (called on every edit drag + insert/delete/toggle) recomputes vbH from current layer.w/h before writing the new d string. This catches the rare case where layer.w/h change between refit calls without my refit running. - The "Make editable path" conversion remaps primitiveToPenNodes / parsePathDToNodes results (which always come in 0-100×0-100) into the layer's actual aspect space, and writes viewBox.h to match. Result: converting a rect → editable path on a non-square layer keeps its proportions, and the resulting pen path edits cleanly with uniform strokes. Strokes now stay a constant width all the way around closed shapes, even after the path is fully committed and re-rendered through the standard shape-layer pipeline.
This commit is contained in:
1 parent
e5c06b7415
commit
3831b39b2e
1 file changed
+36
-9
+36
-9
@@ -9925,8 +9925,15 @@
|
||||
}
|
||||
|
||||
function regenPenLayerD(layer) {
|
||||
// The renderer applies sx=100/vb.w, sy=vbH/vb.h; we want sx==sy.
|
||||
// So whenever we regenerate the d-string we also update the
|
||||
// stored viewBox.h to match the layer's current aspect, keeping
|
||||
// strokes uniform. Nodes are kept in 0-100 × 0-(100*h/w) space
|
||||
// by the uniform-scale remap in finishPenPath + refit.
|
||||
const vbH = layer.w > 0 ? 100 * layer.h / layer.w : 100;
|
||||
layer.shape = {
|
||||
...layer.shape,
|
||||
viewBox: { x: 0, y: 0, w: 100, h: vbH },
|
||||
d: penNodesToPathD(layer.shape.pen.nodes, layer.shape.pen.closed)
|
||||
};
|
||||
}
|
||||
@@ -10439,11 +10446,28 @@
|
||||
width: Math.max(0.5, Number(sh.strokeWidth) || 6), dash: 'solid' };
|
||||
layer.fill = { type: 'none' };
|
||||
}
|
||||
// primitiveToPenNodes / parsePathDToNodes return nodes in
|
||||
// 0-100 × 0-100. The renderer paints pen paths with sx=100/vb.w,
|
||||
// sy=vbH/vb.h, where vbH = 100*layer.h/layer.w — so for any
|
||||
// non-square layer this would stretch the path. Remap node y
|
||||
// coordinates to 0..vbH so the resulting viewBox is the layer's
|
||||
// actual aspect, giving uniform sx==sy at render time.
|
||||
const lw = Number(layer.w) > 0 ? Number(layer.w) : 1;
|
||||
const lh = Number(layer.h) > 0 ? Number(layer.h) : 1;
|
||||
const vbH = 100 * lh / lw;
|
||||
const yScale = vbH / 100;
|
||||
const scaledNodes = result.nodes.map(n => ({
|
||||
x: n.x,
|
||||
y: n.y * yScale,
|
||||
hIn: n.hIn ? { x: n.hIn.x, y: n.hIn.y * yScale } : null,
|
||||
hOut: n.hOut ? { x: n.hOut.x, y: n.hOut.y * yScale } : null,
|
||||
kind: n.kind || 'corner',
|
||||
}));
|
||||
layer.shape = {
|
||||
kind: 'path',
|
||||
viewBox: { x: 0, y: 0, w: 100, h: 100 },
|
||||
pen: { closed: result.closed, tool: 'pen', nodes: result.nodes },
|
||||
d: penNodesToPathD(result.nodes, result.closed)
|
||||
viewBox: { x: 0, y: 0, w: 100, h: vbH },
|
||||
pen: { closed: result.closed, tool: 'pen', nodes: scaledNodes },
|
||||
d: penNodesToPathD(scaledNodes, result.closed)
|
||||
};
|
||||
render();
|
||||
return { ok: true };
|
||||
@@ -10491,11 +10515,13 @@
|
||||
}
|
||||
const bbox = pointsBbox(allPts);
|
||||
// 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.
|
||||
// in 0-100 × 0-(100*h/w). Then store shape.viewBox.h to match,
|
||||
// so the renderer (buildShapeSvgInner path branch, sx = 100/vb.w,
|
||||
// sy = H/vb.h) ends up with sx == sy = uniform scaling. The
|
||||
// earlier hardcoded viewBox.h = 100 with bbox-rescaled nodes
|
||||
// forced the renderer to stretch the path vertically into a
|
||||
// non-square layer rectangle, varying stroke widths around
|
||||
// curves.
|
||||
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 => ({
|
||||
@@ -10503,6 +10529,7 @@
|
||||
hIn: n.hIn ? remap(n.hIn) : null,
|
||||
hOut: n.hOut ? remap(n.hOut) : null
|
||||
}));
|
||||
const vbH = bbox.w > 0 ? 100 * bbox.h / bbox.w : 100;
|
||||
const d = penNodesToPathD(normNodes, closed);
|
||||
pushHistory('draw pen path');
|
||||
const fill = (closed && _penToolOpts.penFill)
|
||||
@@ -10516,7 +10543,7 @@
|
||||
x: bbox.x, y: bbox.y, w: bbox.w, h: bbox.h,
|
||||
rotate: 0, opacity: 1, z: nextLayerZ(),
|
||||
shape: {
|
||||
kind: 'path', d, viewBox: { x: 0, y: 0, w: 100, h: 100 },
|
||||
kind: 'path', d, viewBox: { x: 0, y: 0, w: 100, h: vbH },
|
||||
pen: { closed: !!closed, tool: 'pen', nodes: normNodes }
|
||||
},
|
||||
fill,
|
||||
|
||||
Reference in new issue
Block a user