Permalink
Browse files

It's alive!

  • Loading branch information...
0 parents commit 3e0e0cb8d28e927be55b5499fa390818c78dc223 @martymcguire committed Jan 30, 2011
Showing with 649 additions and 0 deletions.
  1. +17 −0 LICENSE
  2. +58 −0 README.md
  3. +1 −0 src/.gitignore
  4. +27 −0 src/unicorn.inx
  5. +78 −0 src/unicorn.py
  6. 0 src/unicorn/__init__.py
  7. +83 −0 src/unicorn/context.py
  8. +88 −0 src/unicorn/entities.py
  9. +297 −0 src/unicorn/svg_parser.py
17 LICENSE
@@ -0,0 +1,17 @@
+#
+# Copyright (c) 2011 MakerBot Industries
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
@@ -0,0 +1,58 @@
+MakerBot Unicorn G-Code Output for Inkscape
+===========================================
+
+This is an Inkscape extension that allows you to save your Inkscape drawings as
+G-Code files suitable for plotting with the [MakerBot Unicorn Pen Plotter](http://store.makerbot.com/makerbot-unicorn-pen-plotter-kit.html).
+
+Author: [Marty McGuire](http://github.com/martymcguire)
+
+Website: [http://github.com/martymcguire/inkscape-unicorn](http://github.com/martymcguire/inkscape-unicorn)
+
+Credits
+=======
+
+* Marty McGuire pulled this all together into an Inkscape extension.
+* [Scribbles](https://github.com/makerbot/Makerbot/tree/master/Unicorn/Scribbles%20Scripts) is the original DXF-to-Unicorn Python script.
+* [The Egg-Bot Driver for Inkscape](http://code.google.com/p/eggbotcode/) provided inspiration and good examples for working with Inkscape's extensions API.
+
+Install
+=======
+
+Copy the contents of `src/` to your Inkscape `extensions/` folder.
+
+Typical locations include:
+
+* OS X - `/Applications/Inkscape.app/Contents/Resources/extensions`
+* Linux - `/usr/share/inkscape/extensions`
+* Windows - `C:\Program Files\Inkscape\share\extensions`
+
+Usage
+=====
+
+* Size and locate your image appropriately:
+ * The CupCake CNC build platform size is 100mm x 100mm.
+ * Setting units to **mm** in Inkscape makes it easy to size your drawing.
+ * The extension will automatically attempt to center everything.
+* Convert all text to paths:
+ * Select all text objects.
+ * Choose **Path | Object to Path**.
+* Save as G-Code:
+ * **File | Save a Copy**.
+ * Select **MakerBot Unicorn G-Code (\*.gcode)**.
+ * Save your file.
+* Preview
+ * For OS X, [Pleasant3D](http://www.pleasantsoftware.com/developer/pleasant3d/index.shtml) is great for this.
+ * For other operating systems... I don't know!
+* Print!
+ * Open your `.gcode` file in [ReplicatorG](http://replicat.org/)
+ * Set up your Unicorn and pen.
+ * Center your build platform.
+ * Click the **Build** button!
+
+TODOs
+=====
+
+* Add support for [mifga's pen registration and page-changing G-Code](http://www.thingiverse.com/thing:4262).
+* Parameterize smoothness for curve approximation.
+* Use native curve G-Codes instead of converting to paths?
+* Include example templates?
@@ -0,0 +1 @@
+*.pyc
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
+ <_name>MakerBot Unicorn G-Code Output</_name>
+ <id>com.makerbot.unicorn.gcode</id>
+ <dependency type="extension">org.inkscape.output.svg.inkscape</dependency>
+ <dependency type="executable" location="extensions">unicorn.py</dependency>
+ <dependency type="executable" location="extensions">inkex.py</dependency>
+ <param name="pen-up-angle" type="float" min="0.0" max="180.0" _gui-text="Pen Up Angle">50</param>
+ <param name="pen-down-angle" type="float" min="0.0" max="180.0" _gui-text="Pen Down Angle">30</param>
+ <param name="start-delay" type="float" min="0.0" max="1000.0" _gui-text="Delay after pen-down command before movement in milliseconds.">50.0</param>
+ <param name="stop-delay" type="float" min="0.0" max="1000.0" _gui-text="Delay after pen-up command before movement in milliseconds.">150.0</param>
+ <param name="xy-feedrate" type="float" min="100.0" max="5000.0" _gui-text="XY axes feedrate in mm/min.">3500.0</param>
+ <param name="z-feedrate" type="float" min="0.0" max="1000.0" _gui-text="Z-axis feedrate in mm/min.">150.0</param>
+ <param name="z-height" type="float" min="0.0" max="110.0" _gui-text="Z-axis print height in mm.">0.0</param>
+ <param name="finished-height" type="float" min="0.0" max="110.0" _gui-text="Z-axis height after printing in mm.">15.0</param>
+
+ <output>
+ <extension>.gcode</extension>
+ <mimetype>application/x-gcode</mimetype>
+ <_filetypename>MakerBot Unicorn G-Code (*.gcode)</_filetypename>
+ <_filetypetooltip>Toolpath for the MakerBot Unicorn Pen Plotter</_filetypetooltip>
+ <dataloss>true</dataloss>
+ </output>
+ <script>
+ <command reldir="extensions" interpreter="python">unicorn.py</command>
+ </script>
+</inkscape-extension>
@@ -0,0 +1,78 @@
+#!/usr/bin/env python
+'''
+Copyright (c) 2010 MakerBot Industries
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+'''
+import sys,os
+import inkex
+from math import *
+import getopt
+from unicorn.context import GCodeContext
+from unicorn.svg_parser import SvgParser
+
+class MyEffect(inkex.Effect):
+ def __init__(self):
+ inkex.Effect.__init__(self)
+ self.OptionParser.add_option("--pen-up-angle",
+ action="store", type="float",
+ dest="pen_up_angle", default="50.0",
+ help="Pen Up Angle")
+ self.OptionParser.add_option("--pen-down-angle",
+ action="store", type="float",
+ dest="pen_down_angle", default="30.0",
+ help="Pen Down Angle")
+ self.OptionParser.add_option("--start-delay",
+ action="store", type="float",
+ dest="start_delay", default="50.0",
+ help="Delay after pen down command before movement in milliseconds")
+ self.OptionParser.add_option("--stop-delay",
+ action="store", type="float",
+ dest="stop_delay", default="150.0",
+ help="Delay after pen up command before movement in milliseconds")
+ self.OptionParser.add_option("--xy-feedrate",
+ action="store", type="float",
+ dest="xy_feedrate", default="3500.0",
+ help="XY axes feedrate in mm/min")
+ self.OptionParser.add_option("--z-feedrate",
+ action="store", type="float",
+ dest="z_feedrate", default="150.0",
+ help="Z axis feedrate in mm/min")
+ self.OptionParser.add_option("--z-height",
+ action="store", type="float",
+ dest="z_height", default="0.0",
+ help="Z axis print height in mm")
+ self.OptionParser.add_option("--finished-height",
+ action="store", type="float",
+ dest="finished_height", default="15.0",
+ help="Z axis height after printing in mm")
+
+ def output(self):
+ self.context.generate()
+
+ def effect(self):
+ self.context = GCodeContext(self.options.xy_feedrate, self.options.z_feedrate,
+ self.options.start_delay, self.options.stop_delay,
+ self.options.pen_up_angle, self.options.pen_down_angle,
+ self.options.z_height, self.options.finished_height,
+ self.svg_file)
+ parser = SvgParser(self.document.getroot())
+ parser.parse()
+ for entity in parser.entities:
+ entity.get_gcode(self.context)
+
+if __name__ == '__main__': #pragma: no cover
+ e = MyEffect()
+ e.affect()
No changes.
@@ -0,0 +1,83 @@
+from math import *
+import sys
+
+class GCodeContext:
+ def __init__(self, xy_feedrate, z_feedrate, start_delay, stop_delay, pen_up_angle, pen_down_angle, z_height, finished_height, file):
+ self.xy_feedrate = xy_feedrate
+ self.z_feedrate = z_feedrate
+ self.start_delay = start_delay
+ self.stop_delay = stop_delay
+ self.pen_up_angle = pen_up_angle
+ self.pen_down_angle = pen_down_angle
+ self.z_height = z_height
+ self.finished_height = finished_height
+ self.file = file
+
+ self.drawing = False
+ self.last = None
+
+ self.preamble = [
+ "(Scribbled version of %s @ %.2f)" % (self.file, self.xy_feedrate),
+ "( %s )" % " ".join(sys.argv),
+ "G21 (metric ftw)",
+ "G90 (absolute mode)",
+ "G92 X0 Y0 Z0 (zero all axes)",
+ "G92 Z%0.2F F%0.2F (go to printing level)" % (self.z_height, self.z_feedrate),
+ ""
+ ]
+
+ self.postscript = [
+ "",
+ "(end of print job)",
+ "M300 S%0.2F (pen up)" % self.pen_up_angle,
+ "G4 P%d (wait %dms)" % (self.stop_delay, self.stop_delay),
+ "M300 S255 (turn off servo)",
+ "G1 X0 Y0 F%0.2F" % self.xy_feedrate,
+ # FIXME - parameterize z finished height
+ "G1 Z%0.2F F%0.2F (go up to finished level)" % (self.finished_height, self.z_feedrate),
+ "G1 X0 Y0 F%0.2F (go back to center)" % self.z_feedrate,
+ "M18 (drives off)",
+ ]
+
+ self.codes = []
+
+ def generate(self):
+ for codeset in [self.preamble, self.codes, self.postscript]:
+ for line in codeset:
+ print line
+
+ def start(self):
+ self.codes.append("M300 S%0.2F (pen down)" % self.pen_down_angle)
+ self.codes.append("G4 P%d (wait %dms)" % (self.start_delay, self.start_delay))
+ self.drawing = True
+
+ def stop(self):
+ self.codes.append("M300 S%0.2F (pen up)" % self.pen_up_angle)
+ self.codes.append("G4 P%d (wait %dms)" % (self.stop_delay, self.stop_delay))
+ self.drawing = False
+
+ def go_to_point(self, x, y, stop=False):
+ if self.last == (x,y):
+ return
+ if stop:
+ return
+ else:
+ if self.drawing:
+ self.codes.append("M300 S%0.2F (pen up)" % self.pen_up_angle)
+ self.codes.append("G4 P%d (wait %dms)" % (self.stop_delay, self.stop_delay))
+ self.drawing = False
+ self.codes.append("G1 X%.2f Y%.2f F%.2f" % (x,y, self.xy_feedrate))
+ self.last = (x,y)
+
+ def draw_to_point(self, x, y, stop=False):
+ if self.last == (x,y):
+ return
+ if stop:
+ return
+ else:
+ if self.drawing == False:
+ self.codes.append("M300 S%0.2F (pen down)" % self.pen_up_angle)
+ self.codes.append("G4 P%d (wait %dms)" % (self.start_delay, self.start_delay))
+ self.drawing = True
+ self.codes.append("G1 X%0.2f Y%0.2f F%0.2f" % (x,y, self.xy_feedrate))
+ self.last = (x,y)
@@ -0,0 +1,88 @@
+from math import cos, sin, radians
+import pprint
+
+class Entity:
+ def get_gcode(self,context):
+ #raise NotImplementedError()
+ return "NIE"
+
+class Line(Entity):
+ def __str__(self):
+ return "Line from [%.2f, %.2f] to [%.2f, %.2f]" % (self.start[0], self.start[1], self.end[0], self.end[1])
+ def get_gcode(self,context):
+ "Emit gcode for drawing line"
+ context.codes.append("(" + str(self) + ")")
+ context.go_to_point(self.start[0],self.start[1])
+ context.draw_to_point(self.end[0],self.end[1])
+ context.codes.append("")
+
+class Circle(Entity):
+ def __str__(self):
+ return "Circle at [%.2f,%.2f], radius %.2f" % (self.center[0], self.center[1], self.radius)
+ def get_gcode(self,context):
+ "Emit gcode for drawing arc"
+ start = (self.center[0] - self.radius, self.center[1])
+ arc_code = "G3 I%.2f J0 F%.2f" % (self.radius, context.xy_feedrate)
+
+ context.codes.append("(" + str(self) + ")")
+ context.go_to_point(start[0],start[1])
+ context.start()
+ context.codes.append(arc_code)
+ context.stop()
+ context.codes.append("")
+
+class Arc(Entity):
+ def __str__(self):
+ return "Arc at [%.2f, %.2f], radius %.2f, from %.2f to %.2f" % (self.center[0], self.center[1], self.radius, self.start_angle, self.end_angle)
+
+ def find_point(self,proportion):
+ "Find point at the given proportion along the arc."
+ delta = self.end_angle - self.start_angle
+ angle = self.start_angle + delta*proportion
+
+ return (self.center[0] + self.radius*cos(angle), self.center[1] + self.radius*sin(angle))
+
+ def get_gcode(self,context):
+ "Emit gcode for drawing arc"
+ start = self.find_point(0)
+ end = self.find_point(1)
+ delta = self.end_angle - self.start_angle
+
+ if (delta < 0):
+ arc_code = "G3"
+ else:
+ arc_code = "G3"
+ arc_code = arc_code + " X%.2f Y%.2f I%.2f J%.2f F%.2f" % (end[0], end[1], self.center[0] - start[0], self.center[1] - start[1], context.xy_feedrate)
+
+ context.codes.append("(" + str(self) + ")")
+ context.go_to_point(start[0],start[1])
+ context.last = end
+ context.start()
+ context.codes.append(arc_code)
+ context.stop()
+ context.codes.append("")
+
+class Ellipse(Entity):
+ #NOT YET IMPLEMENTED
+ def __str__(self):
+ return "Ellipse at [%.2f, %.2f], major [%.2f, %.2f], minor/major %.2f" + " start %.2f end %.2f" % \
+ (self.center[0], self.center[1], self.major[0], self.major[1], self.minor_to_major, self.start_param, self.end_param)
+
+class PolyLine(Entity):
+ def __str__(self):
+ return "Polyline consisting of %d segments." % len(self.segments)
+
+ def get_gcode(self,context):
+ "Emit gcode for drawing polyline"
+ for points in self.segments:
+ start = points[0]
+
+ context.codes.append("(" + str(self) + ")")
+ context.go_to_point(start[0],start[1])
+ context.start()
+ for point in points[1:]:
+ context.draw_to_point(point[0],point[1])
+ context.last = point
+ context.stop()
+ context.codes.append("")
+
Oops, something went wrong.

0 comments on commit 3e0e0cb

Please sign in to comment.