Search This Blog

Saturday, August 17, 2013

Java app to draw SVG files in prep for sending to CNC machine

I'm in the midst of writing the control program for an Ardunio powered CNC machine.

I've decided I'll use SVG format to drive the CNC machine from the PC.

I can generate SVG files from other pictures with this online free tool:
http://www.autotracer.org/

This tool allows you to view and edit them, and draw my own directly.
http://svg-edit.googlecode.com/svn/branches/2.6/editor/svg-editor.html

Here is the spec on the SVG file format
http://www.w3.org/TR/2011/REC-SVG11-20110816/paths.html#PathDataMovetoCommands

This is the SVG for a little graphic with some letters and a square I made.

<?xml version="1.0" standalone="yes"?>
<svg width="161" height="150">
<path style="fill:#ffffff; stroke:none;" d="M0 0L0 150L161 150L161 0L0 0z"/>
<path style="fill:#010101; stroke:none;" d="M1 1L1 109L117 109L117 1L1 1z"/>
<path style="fill:#ffffff; stroke:none;" d="M3 3L3 107L115 107L115 85C87.676 91.5053 87.5147 47.4563 115 54L115 3L3 3z"/>
<path style="fill:#010101; stroke:none;" d="M41 43L41 85L49 85L49 69C54.56 69 61.8856 70.2342 66.9568 67.5432C74.8268 63.3671 75.3453 49.6136 67.956 44.7423C61.5192 40.4989 48.4628 43 41 43M80 43L80 50L88 50L88 43L80 43z"/>
<path style="fill:#ffffff; stroke:none;" d="M49 50L49 62C66.8383 61.9882 66.8383 50.0118 49 50z"/>
<path style="fill:#010101; stroke:none;" d="M80 54L80 85L88 85L88 54L80 54z"/>
<path style="fill:#ffffff; stroke:none;" d="M107.043 59.9707C98.9687 61.792 101.299 81.8813 109.794 79.1489C117.684 76.611 116.364 57.8683 107.043 59.9707z"/>
</svg>


The task at hand now is to make a little graphics sub module for the Java control program to read in an SVG file, interpret the commands line by line and render it.  I'll both draw it on the screen in Java as a dry run, and format and send the move commands at the same time to the Arduinos controlling stepper motors.

This builds on stuff in these previous posts.

The major pieces are
-Open and read in svg files
-Parse them line by line to pull out the graphics commands
-Draw the vectors/curves on the screen
-Write out the CNC machine commands to the serial interface.  That program is already written but needs to be integrated.

Having some compatibility problems with the various svg editors.
I like the files autotracer.org makes, but svg-edit.googlecode.com can't read them.  GIMP will import and display them just fine.  internet explorer doesn't like them either, and displays a blank page.    The files from svg-edit.googlecode.com have a lot of complex commands in them.
Figured out that I want to just use the path tool in svg-edit.googlecode.com.  The format is a bit different though.

svg-edit gives me this:
 <path id="svg_11" d="m367,98l121,8l3,71l33,-118" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke="#ff0000" fill="#ffffff"/>

autotracer.org gives me this:
<path style="fill:#c1bfb3; stroke:none;" d="M119 16L120 17L119 16z"/>

They differ as to whether they have commas, absolute or relative moves, order of arguments, etc.
Played around a bit, and svg-edit seems to make files that gimp renders better.

Pushing forward, I'll try to interpret both styles.  

Wrote the file handling and parsing code in Java, now I need to write the graphics so I can draw a picture on the screen to see what the cnc thinks it's going to draw.  That is the best way to see if I've translated the file properly too.

This took days and days to get right.  Starting with some simple pictures of curves drawn in svg-edit through some vectorized complex pictures I eventually got this to work!  I had some trouble with the relative curve commands and decoding the curve commands and the compound commands.   The code gets over complicated and handling the absolute and relative syntax from the different svg file generators took a while to sort out.


The program opens an svg file, and then parses the commands into path lists of points.  Curves are expanded into lists of points for the CNC machine to draw.  Java graphics draw the lists of points so I can see if it looks right.

Things I learned:
  • c commands have all the points relative to the starting point, not the last point in the list.
  • m is absolute if first in line, relative if later in line
  • if m shows up mid line, need to lift the pen and start a new line. at that point.  Also true of M
The command parsing is ugly code, I have to go character by character through the svg commands and interpret them.  A big case statement and while loops flattened out.  uggh.  But it works.

The code at this point is posted at
https://code.google.com/p/arduino-java-xyzcnc/downloads/list

Here is the steps to make a vector image file



First I just used power point to make some text.  This could come from anywhere.


Then I cut and pasted the words into gimp and saved it as a .png graphics file.   Again the png/gif/etc could come from any source

Then I went to autotracer.org and uploaded the file.  Hit send file and then download the resulting .svg file from the top.
Then I used my Java program to open the .svg file, filter and plot it to the screen. W00t!  It worked!!!  Now I have a vector file that I can send to the CNC machine.


I'm finding that svg-edit doesn't like the autotracer.org files if opened directly.  Also svg-edit works better in internet explorer than chrome (where is pretty broken).   If you hit the <svg> button and hand paste the .svg file from autotracer.org into svg-edit, it seems to work.  Not sure what the syntax issue is, something in the headers.

Here i show editing the svg file:

Open the file in internet explorer, svg-edit.  Did this by pasting the previous svg into the text window.  Edited it and moved some words.
Wrote out a new svg file.  This time the commands are all in relative syntax. 
It works!  Here it is in the Java program.

notes for my to do list.
  • Need to fix Java graphics repaint - DONE (added overide of paint to redo the drawing)
  • Get rid of all the system.out.println's for debug - DONE
  • need to make background non-white because some lines are disappearing.  - DONE
  • Also need to smarten up the calculation for how many segments I linearize the curves - DONE
Now to combine it with the CNC control java program...

This post is in progress....

Friday, August 2, 2013

Java app to draw SVG Bezier curves

I'm in the midst of writing the control program for an Ardunio powered CNC machine.

I've decided I'll use SVG format to drive the CNC machine from the PC.

I can generate SVG files from other pictures with this online free tool:
http://www.autotracer.org/

This tool allows you to view and edit them, and draw my own directly.
http://svg-edit.googlecode.com/svn/branches/2.6/editor/svg-editor.html

Here is the spec on the SVG file format
http://www.w3.org/TR/2011/REC-SVG11-20110816/paths.html#PathDataMovetoCommands

This is a little graphic with some letters and a square I made.

<?xml version="1.0" standalone="yes"?>
<svg width="161" height="150">
<path style="fill:#ffffff; stroke:none;" d="M0 0L0 150L161 150L161 0L0 0z"/>
<path style="fill:#010101; stroke:none;" d="M1 1L1 109L117 109L117 1L1 1z"/>
<path style="fill:#ffffff; stroke:none;" d="M3 3L3 107L115 107L115 85C87.676 91.5053 87.5147 47.4563 115 54L115 3L3 3z"/>
<path style="fill:#010101; stroke:none;" d="M41 43L41 85L49 85L49 69C54.56 69 61.8856 70.2342 66.9568 67.5432C74.8268 63.3671 75.3453 49.6136 67.956 44.7423C61.5192 40.4989 48.4628 43 41 43M80 43L80 50L88 50L88 43L80 43z"/>
<path style="fill:#ffffff; stroke:none;" d="M49 50L49 62C66.8383 61.9882 66.8383 50.0118 49 50z"/>
<path style="fill:#010101; stroke:none;" d="M80 54L80 85L88 85L88 54L80 54z"/>
<path style="fill:#ffffff; stroke:none;" d="M107.043 59.9707C98.9687 61.792 101.299 81.8813 109.794 79.1489C117.684 76.611 116.364 57.8683 107.043 59.9707z"/>
</svg>

I need to be able to interpret the Bezier curve commands and calculate the points to drive the Arduino to.

Some References:




The last one has the code at the end that I ended up starting with.

First I want to make the Java program to compute all the points, and draw them.
Here is the initial implementation of the code at the end of this post



It worked and drew a curve!   Now I need to draw curves with two control points that are entered, and control the number of points in the curve.   Looks like those functions are here.

A little fooling around and I figured out the functions and made a GUI to draw the curves and the control points

Posted the source code and jar file here:
https://code.google.com/p/arduino-java-xyzcnc/downloads/list
https://code.google.com/p/arduino-java-xyzcnc/downloads/detail?name=BezierDraw.jar

This is the guts of the routine:

package bezierdraw;

import java.awt.geom.Point2D;

public class BezierCurve {
    
    Point2D.Double[] cp;
    int numberOfPoints;
    Point2D.Double[] curvePoints;

    public BezierCurve(Point2D.Double[] cp, int numPoints) {
        this.cp = cp;
        numberOfPoints = numPoints;
        curvePoints = new Point2D.Double[numPoints];
        computeBezier();
    }

    protected Point2D.Double[] getCurvePoints() {
        return curvePoints;
    }

    private void computeBezier() {
        double dt = 1.0 / (numberOfPoints - 1);

        for(int j = 0; j < numberOfPoints; j++)
            curvePoints[j] = getCurvePoint(j*dt);
    }
    
    private Point2D.Double getCurvePoint(double t)
    {
        Point2D.Double result = new Point2D.Double();

        /* calculate the polynomial coefficients */
        double cx = 3.0 * (cp[1].x - cp[0].x);
        double bx = 3.0 * (cp[2].x - cp[1].x) - cx;
        double ax = cp[3].x - cp[0].x - cx - bx;

        double cy = 3.0 * (cp[1].y - cp[0].y);
        double by = 3.0 * (cp[2].y - cp[1].y) - cy;
        double ay = cp[3].y - cp[0].y - cy - by;

        /* calculate the curve point at parameter value t */
        double tSquared = t * t;
        double tCubed = tSquared * t;

        result.x = (ax * tCubed) + (bx * tSquared) + (cx * t) + cp[0].x;
        result.y = (ay * tCubed) + (by * tSquared) + (cy * t) + cp[0].y;

        return result;
    }
    
}

private void drawBezierPath(Point2D.Double p1, Point2D.Double cp1, Point2D.Double cp2, Point2D.Double p2, int numPoints) {
        Point2D.Double[] points = { p1, cp1, cp2, p2 };
        BezierCurve curve = new BezierCurve(points, numPoints);
        Point2D.Double[] p = curve.getCurvePoints();
        path = new GeneralPath();
        for(int j = 0; j < p.length; j++) {
            if(j == 0)
                path.moveTo((float)p[j].x, (float)p[j].y);
            else
                path.lineTo((float)p[j].x, (float)p[j].y);
        }
        g2d.setColor(Color.red);
        g2d.draw(path);

    }



That is the basic C function.  Now for the S function. The shorthand curve notation is a bit confusing.
http://stackoverflow.com/questions/5287559/calculating-control-points-for-a-shorthand-smooth-svg-path-bezier-curve

From the SVG spec
CommandNameParametersDescription
C(absolute)
c (relative)
curveto(x1 y1 x2 y2 x y)+Draws a cubic Bézier curve from the current point to (x,y) using (x1,y1) as the control point at the beginning of the curve and (x2,y2) as the control point at the end of the curve. C (uppercase) indicates that absolute coordinates will follow; c (lowercase) indicates that relative coordinates will follow. Multiple sets of coordinates may be specified to draw a polybézier. At the end of the command, the new current point becomes the final (x,y) coordinate pair used in the polybézier.
S(absolute)
s (relative)
shorthand/smooth curveto(x2 y2 x y)+Draws a cubic Bézier curve from the current point to (x,y). The first control point is assumed to be the reflection of the second control point on the previous command relative to the current point. (If there is no previous command or if the previous command was not an C, c, S or s, assume the first control point is coincident with the current point.) (x2,y2) is the second control point (i.e., the control point at the end of the curve). S (uppercase) indicates that absolute coordinates will follow; s (lowercase) indicates that relative coordinates will follow. Multiple sets of coordinates may be specified to draw a polybézier. At the end of the command, the new current point becomes the final (x,y) coordinate pair used in the polybézier.

I am only doing a cubic curve, and there are quadratic and elliptical types.  Need to see if I need to implement the others or if I can get by with just implementing the Bezier cubic.