diff --git a/interface/bezier.js b/interface/bezier.js new file mode 100644 index 0000000..c1f5282 --- /dev/null +++ b/interface/bezier.js @@ -0,0 +1,75 @@ +// bezier to line conversion script +// taken from https://github.com/evomotors/BezierCurvesJS +// written by evomotors (see https://github.com/M4GNV5/EggEsp/issues/1#issuecomment-488673199) + +var FactorialLookup = [1.0, 1.0, 2.0, 6.0, 24.0, 120.0, 720.0, 5040.0, 40320.0, 362880.0, + 3628800.0, 39916800.0, 479001600.0, 6227020800.0, 87178291200.0, + 1307674368000.0, 20922789888000.0, 355687428096000.0, 6402373705728000.0, + 121645100408832000.0, 2432902008176640000.0, 51090942171709440000.0, + 1124000727777607680000.0, 25852016738884976640000.0, 620448401733239439360000.0, + 15511210043330985984000000.0, 403291461126605635584000000.0, 10888869450418352160768000000.0, + 304888344611713860501504000000.0, 8841761993739701954543616000000.0, 265252859812191058636308480000000.0, + 8222838654177922817725562880000000.0, 263130836933693530167218012160000000.0] + +// Calculate points on curve +function GetBezierPoints(b, cpts) +{ + var npts = (b.length) / 2; + var p = []; + + var icount = 0; + var t = 0; + var step = 1.0 / (cpts - 1); + + for (var i1 = 0; i1 != cpts; i1++) + { + if ((1.0 - t) < 5e-6) + t = 1.0; + + var jcount = 0; + p[icount] = 0.0; + p[icount + 1] = 0.0; + for (var i = 0; i != npts; i++) + { + var basis = Bernstein(npts - 1, i, t); + p[icount] += basis * b[jcount]; + p[icount + 1] += basis * b[jcount + 1]; + jcount = jcount + 2; + } + + if(isNaN(p[icount]) || isNaN(p[icount + 1])) + debugger; + + icount += 2; + t += step; + } + + return p; +} + +// Calculates Bernstein basis +function Bernstein(n, i, t) +{ + var ti; + var tni; + + if (t == 0.0 && i == 0) + ti = 1.0; + else + ti = Math.pow(t, i); + + if (n == i && t == 1.0) + tni = 1.0; + else + tni = Math.pow((1 - t), (n - i)); + + return (Factorial(n) / (Factorial(i) * Factorial(n - i))) * ti * tni; +} + +function Factorial(n) +{ + if(n >= FactorialLookup.length) + return Number.MAX_SAFE_INTEGER; + else + return FactorialLookup[n]; +} \ No newline at end of file diff --git a/interface/index.html b/interface/index.html index 7c7c76f..ca59d04 100644 --- a/interface/index.html +++ b/interface/index.html @@ -12,8 +12,9 @@
- +
+ diff --git a/interface/svg2gcode.js b/interface/svg2gcode.js index a0a8912..3682222 100644 --- a/interface/svg2gcode.js +++ b/interface/svg2gcode.js @@ -70,51 +70,8 @@ function path2gcode(transform, path) let y = 0; let wasRelative = false; - while(split.length > 0) + function addOp(op) { - let curr = split.shift(); - let op; - if(xyOps.hasOwnProperty(curr)) - { - op = { - op: xyOps[curr], - x: parseFloat(split.shift()), - y: parseFloat(split.shift()) - }; - } - else if(xy0Ops.hasOwnProperty(curr)) - { - let xy0Op = xy0Ops[curr]; - op = {op: xy0Op.op}; - op[xy0Op.value] = parseFloat(split.shift()); - - if(isRelative(op.op)) - op[xy0Op.unchanged] = 0; - else - op[xy0Op.unchanged] = xy0Op.unchanged == "x" ? x : y; - } - else if(curr == "z" || curr == "Z") - { - op = { - op: OP_LINE, - x: startX, - y: startY - }; - } - //TODO other ops (see https://svgwg.org/specs/paths/#PathElement) - else if(!isNaN(curr)) //assume L/l by default - { - op = { - op: wasRelative ? OP_LINE_REL : OP_LINE, - x: parseFloat(curr), - y: parseFloat(split.shift()) - }; - } - else - { - throw "Invalid/Unsupported svg path character " + curr; - } - wasRelative = isRelative(op.op); if(ret.length == 0 && wasRelative) @@ -146,10 +103,88 @@ function path2gcode(transform, path) applyTransformation(transform, op); if(isNaN(op.x) || isNaN(op.y)) - throw "Oops, something went wrong!"; + throw new Error("Oops, something went wrong!"); ret.push(op); } + while(split.length > 0) + { + let curr = split.shift(); + if(xyOps.hasOwnProperty(curr)) + { + addOp({ + op: xyOps[curr], + x: parseFloat(split.shift()), + y: parseFloat(split.shift()) + }); + } + else if(xy0Ops.hasOwnProperty(curr)) + { + let xy0Op = xy0Ops[curr]; + while(!isNaN(split[0])) + { + let op = {op: xy0Op.op}; + op[xy0Op.value] = parseFloat(split.shift()); + + if(isRelative(op.op)) + op[xy0Op.unchanged] = 0; + else + op[xy0Op.unchanged] = xy0Op.unchanged == "x" ? x : y; + + addOp(op); + } + } + else if(curr == "z" || curr == "Z") + { + addOp({ + op: OP_LINE, + x: startX, + y: startY + }); + } + else if(curr == "c" || curr == "C") + { + let points; + var endIndex = split.findIndex(x => isNaN(x)); + if(endIndex == -1) + { + points = split; + split = []; + } + else + { + points = split.splice(0, endIndex); + } + + points = points.map(x => parseFloat(x)); + + //TODO what is a good value for the second argument? Should we let the user define this? + points = GetBezierPoints(points, 20); + + for(var i = 0; i < points.length; i += 2) + { + addOp({ + op: OP_LINE, + x: points[i], + y: points[i + 1] + }); + } + } + //TODO other ops (see https://svgwg.org/specs/paths/#PathElement) + else if(!isNaN(curr)) //assume L/l by default + { + addOp({ + op: wasRelative ? OP_LINE_REL : OP_LINE, + x: parseFloat(curr), + y: parseFloat(split.shift()) + }); + } + else + { + throw "Invalid/Unsupported svg path character " + curr; + } + } + return ret; }