Outline does not work for some shapes
The library fails to outline this shape:
It is represented by the following SVG path: M 120 0 A 100 100 0 0 1 110.86554718017578 45.92201232910156 L 92.38794708251953 38.26835250854492 A 80 80 0 0 1 38.26828384399414 92.38797760009766 L 45.92204666137695 110.86552429199219 A 100 100 0 0 1 0.00000548346952200518 120 L 0.000009139115718426183 200 A 220 220 0 0 0 76.53670501708984 184.77589416503906 L 84.19052124023438 203.25340270996094 A 240 240 0 0 0 203.2534942626953 84.19031524658203 L 184.7759246826172 76.53665924072266 A 220 220 0 0 0 200 0Z
But the very similar shape is outlined successfully:
Path: M 120 0 A 100 100 0 0 1 112.76310729980469 41.04243469238281 L 93.96925354003906 34.20203399658203 A 80 80 0 0 1 49.999942779541016 86.60258483886719 L 59.99997329711914 103.92306518554688 A 100 100 0 0 1 20.83777618408203 118.17693328857422 L 34.72963333129883 196.96156311035156 A 220 220 0 0 0 99.99992370605469 173.2051544189453 L 110.0000228881836 190.52554321289062 A 240 240 0 0 0 206.73239135742188 75.24442291259766 L 187.93850708007812 68.40404510498047 A 220 220 0 0 0 200 0Z
The difference between these two shapes is that in the non-working example the straight lines form an angle of 90 degrees, in the working - 80 degrees.
Here is the code I use to create outline:
const makerjs = require("makerjs");
let notworking = 'M 120 0 A 100 100 0 0 1 110.86554718017578 45.92201232910156 L 92.38794708251953 38.26835250854492 A 80 80 0 0 1 38.26828384399414 92.38797760009766 L 45.92204666137695 110.86552429199219 A 100 100 0 0 1 0.00000548346952200518 120 L 0.000009139115718426183 200 A 220 220 0 0 0 76.53670501708984 184.77589416503906 L 84.19052124023438 203.25340270996094 A 240 240 0 0 0 203.2534942626953 84.19031524658203 L 184.7759246826172 76.53665924072266 A 220 220 0 0 0 200 0Z';
let working = 'M 120 0 A 100 100 0 0 1 112.76310729980469 41.04243469238281 L 93.96925354003906 34.20203399658203 A 80 80 0 0 1 49.999942779541016 86.60258483886719 L 59.99997329711914 103.92306518554688 A 100 100 0 0 1 20.83777618408203 118.17693328857422 L 34.72963333129883 196.96156311035156 A 220 220 0 0 0 99.99992370605469 173.2051544189453 L 110.0000228881836 190.52554321289062 A 240 240 0 0 0 206.73239135742188 75.24442291259766 L 187.93850708007812 68.40404510498047 A 220 220 0 0 0 200 0Z';
let input = makerjs.importer.fromSVGPathData(notworking, { bezierAccuracy: 0.001 });
let result = makerjs.model.outline(input, 5, 1);
makerjs.model.simplify(result);
let outlinePath = makerjs.exporter.toSVGPathData(result, false, [0, 0]);
console.log('Not working:\n', {input, result, outlinePath});
input = makerjs.importer.fromSVGPathData(working, { bezierAccuracy: 0.001 });
result = makerjs.model.outline(input, 5, 1);
makerjs.model.simplify(result);
outlinePath = makerjs.exporter.toSVGPathData(result, false, [0, 0]);
console.log('Working:\n', {input, result, outlinePath});
I have investigated this further and discovered that outlining is not working because of this dot:
It is represented by this command in failing SVG path: L 0.000009139115718426183 200
If I change it to L 0 200 then outline works.
Where did the original SVG come from? That’s a lot of precision for an SVG file. Most applications apply rounding.
I thought that rounding is needed only to save file size. In my case file size is not an issue. That SVG is generated by me. This precision comes after I split an arc segment into n equal parts. My code is similar to this:
let part = arc.getTotalLength() / n;
let pos = 0;
let path = '';
for (let i = 0; i < n; i++) {
let {x, y} = arc.getPointAtLength(pos);
path += ` L ${x} ${y} `;
// some other code
// ...
pos += part;
}
The issue is not in SVG, this command works as expected: makerjs.importer.fromSVGPathData(path, { bezierAccuracy: 0.1 });
The issue is that I have to round numbers before I pass them to makerjs models. Can't find in documentation that I suppose to do this.
And the case of issue is not in arcs. Here is the simplified square path for which the outline is failing:
const makerjs = require("makerjs");
function getOutline(path, size) {
let input = makerjs.importer.fromSVGPathData(path);
let result = makerjs.model.outline(input, size, 1);
makerjs.model.simplify(result);
return makerjs.exporter.toSVGPathData(result, false, [0, 0]);
}
let testOk = 'M 0 0 L 50.000009 0 L 50 50 L 0 50 Z';
let testUndefined = 'M 0 0 L 50.0000009 0 L 50 50 L 0 50 Z';
console.log({ path: testOk, outline: getOutline(testOk, 5) });
console.log({ path: testUndefined, outline: getOutline(testUndefined, 5) });
Well, yes and no. Obviously, the library internals are having issues with precision. And your examples show that precision is causing issues with representing paths.
I think the last example with the square is really spot on. Hopefully, there’s a way to work around this.