Open
Bug 1507603
Opened 7 years ago
Updated 1 year ago
SVGPathElement.getPointAtLength() returns incorrect results depending on precision of path string
Categories
(Core :: SVG, defect, P3)
Tracking
()
UNCONFIRMED
People
(Reporter: noah, Unassigned)
Details
Attachments
(3 files)
User Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36
Steps to reproduce:
Reduced test case: https://gist.github.com/veltman/2c0a4f45ce7f7c9464e79e60849ed3df
Calling getPointAtLength() along an SVG path element with float coordinates produces incorrect results for some path strings in Firefox. Consider the following two path strings:
Broken string (five decimal places): "M216,139L228.16667,155C240.33333,171,264.66667,203,326.5,233.33333"
Rounded to three places: "M216,139L228.167,155C240.333,171,264.667,203,326.5,233.333"
In Chrome 70.0.3538.102, getTotalLength() returns roughly the same value 147.099 for both.
In Firefox 63.0.1, getTotalLength() returns significantly different values: 145.821 for the broken string, 147.092 for the rounded string.
In Chrome 70.0.3538.102, calls to getPointAtLength() for any length return points that lie on the path.
In Firefox 63.0.1, calls to getPointAtLength() return the correct points for the rounded string, but results for the broken string are far from the original path.
Screenshot of the discrepancy produced in the above gist attached.
Actual results:
SVGPathElement.getPointAtLength() produces points that do not lie along the path element, and SVGPathElement.getTotalLength() produces different results with small changes in precision.
Expected results:
SVGPathElement.getPointAtLength() should produce points that lie along the path element, and SVGPathElement.getTotalLength() should produce similar results with small changes in precision.
Updated•7 years ago
|
Component: Untriaged → Graphics
Product: Firefox → Core
Updated•7 years ago
|
Priority: -- → P3
Comment 2•5 years ago
|
||
I've stumbled over the same problem. Since I've already created a small example before finding this existing bug report, I'm sharing it here. (It's similar to Noah's example, but does not require third-party code.)
<div id="user-agent"></div>
<svg id="svg" width="200" height="300" viewBox="0 0 2 3">
<path id="path"
d="M 1,2.5 c -0.3523,-0.9 -0.3523,-2.022 -0.3523,-2.1"
style="stroke: grey; stroke-width: 0.01; fill: none;"
/>
</svg>
<script>
document.getElementById("user-agent").innerText = navigator.userAgent
const svg = document.getElementById("svg");
const path = document.getElementById("path");
const len = path.getTotalLength()
for (let lambda = 0; lambda <= 1; lambda += 1/16) {
const point = path.getPointAtLength(lambda * len);
const mark = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
mark.setAttribute("cx", point.x);
mark.setAttribute("cy", point.y);
mark.setAttribute("r", 0.015);
svg.append(mark);
}
</script>
I would expect the black points to appear on the slightly bent grey stroke, but they appear on the straight line between the first and last point of the path.
Note: The problem appears only with certain coordinate values and usually disappears for slightly different ones.
Comment 3•5 years ago
|
||
Here's the output produced by Firefox and Chrome with the example code from my previous comment.
Updated•3 years ago
|
Severity: normal → S3
Comment 4•2 years ago
|
||
Comment 5•1 year ago
|
||
Can confirm this is still a problem, and here's an additional example:
It seems like only the last line segment is affected for some reason.
<script>
const svgEl = document.getElementById("tracing-svg")!;
const tracePath = (path: SVGPathElement) => {
const parent = path.parentElement;
const strokeWidth = parseFloat(getComputedStyle(path).strokeWidth);
const length = path.getTotalLength();
path.style.strokeDasharray = `${length} ${length}`;
path.style.strokeDashoffset = `${length}`;
path.style.transition = "stroke-dashoffset 2s ease-in-out";
path.style.strokeDashoffset = "0";
for (let i = 0; i < length; i += strokeWidth / 5) {
const point = path.getPointAtLength(i);
const dot = document.createElementNS(
"http://www.w3.org/2000/svg",
"circle",
);
dot.setAttribute("cx", point.x.toString());
dot.setAttribute("cy", point.y.toString());
dot.setAttribute("r", (strokeWidth / 2).toString());
dot.style.fill = "green"; //path.style.stroke;
parent?.appendChild(dot);
}
};
tracePath(svgEl.querySelector("path") as SVGPathElement);
</script>
<svg
id="tracing-svg"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 32 32"
fill="none"
preserveAspectRatio="xMinYMin meet"
>
<path
d="M 2.5,4.9
C 3.4,7.4 4.3,9.8 5.9,10.1 7.4,10.4 9.7,8.7 9.9,7.5 10.1,6.2 8.3,5.5 7.6,6.8 7,8.1 7.6,11.4 8.9,11.5 10.3,11.5 12.5,8.2 13.6,7.7 14.8,7.1 14.9,9.2 15,11.3"
style="stroke: #349797; stroke-width: 1; fill: none; stroke-linecap: round"
></path>
</svg>
You need to log in
before you can comment on or make changes to this bug.
Description
•