Open jvolker opened 6 years ago
At this point, SVGs are fully converted to <path>
object using svgo
: svg.js#L16-L32
Apparently, the plugins selected are not enough to convert <circle>
to SVG <path>
.
I will investigate on this. In the meantime, try to use <path>
only SVG, or directly the built-in draw API
Also note that the firmware is currently not able to draw real curves, and instead use a subset of straight lines.
Also, note that like xy-plotter/xy-server
, a brand new version xy-plotter/xy
will soonish be released.
Thanks!
Also note that the firmware is currently not able to draw real curves, and instead use a subset of straight lines.
Good to know. Is that why circles (from the built-in API) are drawing quite slowly at the moment?
a brand new version xy-plotter/xy will soonish be released
Great news! Is the new version going to support curves as well?
I checked how other plotters deal with curve drawing, and the majority seems to use the same straight line curve approximation as the xy's firmware.
That means that the next version will certainly use the same approach, and draw small straight lines instead of real curves. IMHO this isn't an issue, but the speed could be one. You could try setting manually the speed before calling a circle command :
...
job.setSpeed(0.8).circle(x, y, radius)
job.resetSpeed()
...
Setting manually the speed will use a linear interpolation instead of the default eased one.
Thanks for investigating this.
I made some tests with different speeds but realized it made not much difference (see further down this post).
I then realized there is, of course, the option of setting the number of sides from default=100. My finding is that a circle of 2mm radius (which is sort of what I'm looking after right now) looks close to perfect with around 15-30 sides. I very roughly measured the time to draw each of the circles using the default eased speed.
100 sides = 5s 30 sides = 1s
There is a lot of room for optimization it seems. I assume the number of sides should be optimized depending on the circle's size.
I quickly looked into the Axidraw repo and found this:
Convert circles and ellipses to a path with two 180 degree arcs.
Not sure, but aren't those paths curves or multiple straight lines as well?
And in the wiki of StippleGen under "Saving a Stipple Drawing" it reads:
Properly tuned for making dots rapidly, the Eggbot can plot about four stipples per second
'Stipples' are actually circles. I wonder what sort of optimization they do? Maybe even drawing dots only if the circles are really small? Checking the corresponding repos might be worth it.
I've tried drawing a 2mm circle with the default 100 sides using setSpeed()
to 0.8
, 0.95
or even 1.0
and compared it to eased speed as well. There is not much difference I would say.
Drawing a circle with 30 sides using eased speed or 0.01
was smooth and relatively fast. But setting it anywhere around 0.8
or above made for a very jagged circle.
I have implemented this answer from Stack Overflow: How to render a circle with as few vertices as possible?
function circleOptimized(_cx, _cy, _radius, _deviation = 0.075) {
let th = Math.acos(2 * Math.pow((1 - _deviation / _radius), 2) - 1)
let verticesNumber = (_radius <= _deviation) ? 1 : Math.ceil(2 * Math.PI / th)
console.log(_radius.toFixed(1) + "mm radius >>> " + verticesNumber + " vertices")
let points = []
for (let i = 0; i < verticesNumber; i++) {
let angle = i / verticesNumber * 2 * Math.PI;
let point = [
_cx + _radius * Math.cos(angle),
_cy + _radius * Math.sin(angle)
]
points.push(point)
}
if (_radius > _deviation) points.push(points[0])
job.polygon(points)
}
Tested with a few circles:
let x = 20
let y = 70
circleOptimized(x, y, 0)
for (let r = 0.1; r < 3; r += 0.3) {
x += r * 2 + 2
circleOptimized(x, y, r)
}
for (let r = 4; r < 40; r *= 1.5) {
x += r * 1.7 + 2
circleOptimized(x, y, r)
}
Gives:
0.0mm radius >>> 1 vertices
0.1mm radius >>> 3 vertices
0.4mm radius >>> 6 vertices
0.7mm radius >>> 7 vertices
1.0mm radius >>> 9 vertices
1.3mm radius >>> 10 vertices
1.6mm radius >>> 11 vertices
1.9mm radius >>> 12 vertices
2.2mm radius >>> 12 vertices
2.5mm radius >>> 13 vertices
2.8mm radius >>> 14 vertices
4.0mm radius >>> 17 vertices
6.0mm radius >>> 20 vertices
9.0mm radius >>> 25 vertices
13.5mm radius >>> 30 vertices
20.3mm radius >>> 37 vertices
30.4mm radius >>> 45 vertices
At least the renderings seem to be okay. I'm not too sure about smaller circles in the physical drawing though. But this might be my pen holder, which is still not sitting super tight.
I think as an improvement depending on the radius, for bigger circles the drawing speed could go up quite a bit.
Your circleOptimized()
implementation seems really cool !
I think I will use it for v3.0.0 as a default for job.circle(cx, cy, radius, sides)
when no sides
argument are specified, instead of an arbitrary sides = 100
value.
Eventually, it might be a good idea to implement in the firmware a real curve drawing, but I can't find any existing solution.
I don't quite understand the approach used in the official axidraw repo you mentioned : they do convert <ellipse>
to <path>
with arc curves, but do not seem to use them as such : their PlanTrajectory()
method use segments.
They do however use a far more complex speed computation (see axidraw/axidraw.py#L1576-L1640), which could lead to better result.
I looked in Fogleman's version of axidraw, and it seems that he does draw the circle as straight lines too (axi/examples/circles.py#L5-L12).
Your circleOptimized() implementation seems really cool ! I think I will use it for v3.0.0 as a default for job.circle(cx, cy, radius, sides) when no sides argument are specified, instead of an arbitrary sides = 100 value.
I'm glad if it helps.
Eventually, it might be a good idea to implement in the firmware a real curve drawing, but I can't find any existing solution.
In the official XY-Plotter-2.0 repo, there seems to be arc drawing in the firmware. But in the end, it's all resolved to linear movements, and it doesn't really matter if that happens in the firmware or the software, does it?
I don't quite understand the approach used in the official axidraw repo you mentioned : they do convert
to with arc curves, but do not seem to use them as such : their PlanTrajectory() method use segments.
After converting
They do however use a far more complex speed computation (see axidraw/axidraw.py#L1576-L1640), which could lead to better result.
Indeed, that looks promising. Would be interesting to see the difference.
At this point, SVGs are fully converted to
object using svgo : svg.js#L16-L32 Apparently, the plugins selected are not enough to convert to SVG .
According to https://github.com/svg/svgo/pull/818 it requires svgo 1.0.0 to convert circles and ellipses to paths. This project currently uses svgo 0.7.0
Switching to the latest version 1.0.5 throws an error ⚠️ Warning: /path/to/svg-file.svg doesn't contain any valid points.
and the exported PNG is empty. Looks like more work needs to be done.
Also not sure if it might be a problem that flatten transforms (groups) is not implemented in svgo yet.
Maybe https://github.com/stadline/svg-flatten could help in the meantime?
My example from the beginning of this thread works with svg-flatten
using the following code:
const plotter = require('xy-plotter')()
const svgFlatten = require('svg-flatten')
const fs = require('fs');
let flattendSvgPath = 'tmp-flattened.svg'
// read SVG into string
let svgString = fs.readFileSync('circle-rect.svg', 'utf8')
// convert all objects to paths and write to disk
fs.writeFileSync(flattendSvgPath, svgFlatten(svgString).pathify().value());
const job = plotter.Job('svg')
job.svg(flattendSvgPath, {
x: 0, // x coordinate
y: 150, // y coordinate
width: 150, // custom width
height: 150, // custom height
// angle: 90, // angle in degrees
origin: [0, 1] // origin point of the transformation
})
const file = plotter.File()
file.export(job, path.join(__dirname, job.name + '.png'))
But in the rendered PNG the circles have clearly visible vertices:
The preview of tmp-flattened.svg
in macOS doesn't have that issue:
I tried to use const plotter = require('xy-plotter')({ decimals : 4 })
but it didn't help. I'm wondering where this comes from?
First of all, thanks a lot for creating this software!
I tried loading SVGs I created and exported from Affinity Designer. This is an example: The SVG file: circle-rect.svg.zip
Unfortunately, it seems that circles are not going to be picked up. The plots and exported PNGs do not contain any circles. Rectangles show up though:
Loading this SVG heart icon worked as well: https://thenounproject.com/search/?q=dsf&i=350276
Are there any certain specifications or limitations to the SVGs you can load?
Thanks!