Line.getSelfRect() (and therefore getClientRect(), Transformer selection bounds, and cache bounds) is wrong for a bezier Line with more than one cubic segment — it can collapse to a degenerate zero-height/zero-width box while the actual drawn curve is much larger.
Reproduction
const line = new Konva.Line({
bezier: true,
// 2 + 6*2 = 14 points: a starting point + two cubic segments.
points: [0, 50, 0, 150, 100, 150, 100, 50, 100, 50, 200, 150, 300, 50],
stroke: 'red',
});
layer.add(line);
layer.draw();
console.log(line.getSelfRect());
// {x: 0, y: 50, width: 300, height: 0} -- height 0!
The curve itself renders correctly (dips down to about y=125 in this example); getSelfRect() just doesn't know about it.
Root cause
function getBezierExtremaPoints(points) {
const axisPoints = [
[points[0], points[2], points[4], points[6]],
[points[1], points[3], points[5], points[7]],
];
...
}
This only ever reads points[0..7] — the first cubic segment — no matter how many segments the line actually has. _sceneFunc draws every points[n..n+7] group of 6 in a loop (context.bezierCurveTo(...) while n < length), so any segment after the first is invisible to the bounds calculation.
A second bug in the same function
While fixing the above I found the extrema-finding math also silently drops an axis when the cubic's derivative degenerates from quadratic to linear (a === 0, the discriminant branch's a coefficient) — which happens for a segment with symmetric control points, a fairly common shape. The if (a !== 0) guard skips that axis's extremum entirely instead of solving the resulting linear equation b*t + c = 0. This affects the original single-segment code too, not just multi-segment lines — you can reproduce it with a single symmetric segment: points: [0, 50, 0, 150, 100, 150, 100, 50].
I have a fix (rewrites getBezierExtremaPoints to iterate every segment and handles the degenerate case) + a regression test, verified against the real rendered pixels (not just the formula), and will open a PR against this issue.
Line.getSelfRect()(and thereforegetClientRect(), Transformer selection bounds, and cache bounds) is wrong for a bezierLinewith more than one cubic segment — it can collapse to a degenerate zero-height/zero-width box while the actual drawn curve is much larger.Reproduction
The curve itself renders correctly (dips down to about y=125 in this example);
getSelfRect()just doesn't know about it.Root cause
This only ever reads
points[0..7]— the first cubic segment — no matter how many segments the line actually has._sceneFuncdraws everypoints[n..n+7]group of 6 in a loop (context.bezierCurveTo(...)whilen < length), so any segment after the first is invisible to the bounds calculation.A second bug in the same function
While fixing the above I found the extrema-finding math also silently drops an axis when the cubic's derivative degenerates from quadratic to linear (
a === 0, the discriminant branch'sacoefficient) — which happens for a segment with symmetric control points, a fairly common shape. Theif (a !== 0)guard skips that axis's extremum entirely instead of solving the resulting linear equationb*t + c = 0. This affects the original single-segment code too, not just multi-segment lines — you can reproduce it with a single symmetric segment:points: [0, 50, 0, 150, 100, 150, 100, 50].I have a fix (rewrites
getBezierExtremaPointsto iterate every segment and handles the degenerate case) + a regression test, verified against the real rendered pixels (not just the formula), and will open a PR against this issue.