commit 1d75fccd12b81d3a4625b3288f1b02b8ce1a07c1 Author: Jon Nordby Date: Fri Jul 4 17:09:46 2014 +0200 Initial commit diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..13bbf1a --- /dev/null +++ b/Makefile @@ -0,0 +1,111 @@ +# The lines below extract out program names from the cmake cache +# This is used to automatically generate a documentation page. +C_ = $(shell sed -n -e 's/;/ /g' \ + -e 's/SOLVER_EXECUTABLES:STRING=//gp' \ + -e 's/PROGRAMS:STRING=//gp' \ + build/CMakeCache.txt) +C = $(addprefix bin/, $(C_)) + +Python_ = $(shell sed -n -e 's/;/ /g' \ + -e 's/PYs:STRING=//gp' \ + build/CMakeCache.txt) +Python = $(addprefix bin/, $(Python_)) + + +scripts_ = $(shell sed -n -e 's/;/ /g' \ + -e 's/SCRIPTS:STRING=//gp' \ + build/CMakeCache.txt) +scripts = $(addprefix bin/, $(scripts_)) + +GUIs_ = $(shell sed -n -e 's/;/ /g' \ + -e 's/GUIs:STRING=//gp' \ + build/CMakeCache.txt) +GUIs = $(addprefix bin/, $(GUIs_)) + +PWD := $(shell pwd) + +help: + @echo "Makefile options:" + @echo " make fab Compile all files and copy scripts from src to bin" + @echo " make doc Saves command names and docstrings into commands.html" + @echo " make zip Bundles relevant files in fab.zip" + @echo " make dist Copies files to Web directory" + @echo " make install Copies files to /usr/local/bin" + @echo " make clean Removes compiled executables and scripts from bin" + @echo " make wxpython2.9 Downloads, compiles, and installs wxpython 2.9.4.1" + @echo " (Linux only)" + +fab: + @echo "Building with CMake" + @mkdir -p build + + @cd build; \ + cmake ../src; \ + make -j4; \ + make install | sed "s@$(PWD)/src/../@@g" + +doc: commands.html +commands.html: fab + @# Dump all of the command names + @echo " Storing command names" + @echo "\n\n
\ncommands:" > commands.html
+	@for name in $(C) $(scripts) $(GUIs); do               \
+	    echo "   "$$name >> commands.html;                 \
+	done
+	  
+	@echo "" >> commands.html
+	
+	@# Dump command docstrings
+	@echo "	Storing command docstrings"
+	@for name in $(C) $(scripts) ; do                        \
+	   ./$$name >> commands.html;                           \
+	   echo "" >> commands.html;                            \
+	done
+
+
+zip: commands.html
+	rm -f fab_src.zip
+	rm -rf src/apps/dist
+	rm -rf src/apps/build
+	
+#	@echo "Copying revision number to kokopelli About panel"
+#	@if which hg &>/dev/null && hg summary &> /dev/null; \
+#	 then \
+#	    sed "s/CHANGESET = .*/CHANGESET = '`hg id --num`:`hg id --id`'/g" \
+#	    src/guis/koko/__init__.py > tmp; \
+#	    mv tmp src/guis/koko/__init__.py; \
+#	 fi
+	
+	zip -r fab_src.zip commands.html Makefile src
+	
+#	@sed "s/CHANGESET = .*/CHANGESET = None/g" \
+#	    src/guis/koko/__init__.py > tmp; \
+#	    mv tmp src/guis/koko/__init__.py; \
+
+dist: zip
+	cp fab_src.zip ../../Web/fab_src.zip
+	cp commands.html ../../Web/
+	sed -e "s/Snapshot from [^\)]*/Snapshot from `date '+%B %d, %Y, %I:%M%p'`/g" \
+	    ../../Web/downloads.html > ../../Web/_downloads.html
+	mv ../../Web/_downloads.html ../../Web/downloads.html
+
+install: fab
+	@echo "Installing executables and scripts to /usr/local/bin"
+	@if [ -e "/usr/local/bin/fab_send" ]; \
+	then \
+	    mv /usr/local/bin/fab_send /usr/local/bin/fab_send.old; \
+	fi
+	@cp -r bin/* /usr/local/bin/
+	@if [ -e "/usr/local/bin/fab_send.old" ]; \
+	then \
+	    mv /usr/local/bin/fab_send /usr/local/bin/fab_send.new; \
+	    mv /usr/local/bin/fab_send.old /usr/local/bin/fab_send; \
+	    echo "Note:"; \
+	    echo "   Pre-existing fab_send has not been overwritten, and"; \
+	    echo "   the new version of fab_send has been named fab_send.new"; \
+	fi
+
+clean:
+	@echo "Deleting build directory"
+	@rm -rf build
+	
diff --git a/commands.html b/commands.html
new file mode 100644
index 0000000..a080d5b
--- /dev/null
+++ b/commands.html
@@ -0,0 +1,447 @@
+
+
+
+commands:
+   bin/vol_gif
+   bin/vol_stl
+   bin/gif_info
+   bin/gif_png
+   bin/gif_stl
+   bin/stl_info
+   bin/stl_png
+   bin/stl_path
+   bin/png_size
+   bin/png_scale
+   bin/png_distances
+   bin/png_offset
+   bin/png_grb
+   bin/png_drl
+   bin/png_path
+   bin/png_halftone
+   bin/svg_path
+   bin/path_eps
+   bin/path_dxf
+   bin/path_png
+   bin/path_rml
+   bin/path_sbp
+   bin/path_g
+   bin/path_camm
+   bin/path_epi
+   bin/path_uni
+   bin/path_oms
+   bin/path_ord
+   bin/path_join
+   bin/path_array
+   bin/path_info
+   bin/path_time
+   bin/math_png
+   bin/math_dot
+   bin/math_stl
+   bin/math_svg
+   bin/cad_png
+   bin/cad_view
+   bin/path_view
+   bin/rml_move
+   bin/fab_send
+   bin/fab_update
+   bin/cad_math
+   bin/math_png_py
+   bin/math_stl_py
+   bin/png_tile
+   bin/eagle_png
+   bin/fab
+   bin/fab.html
+   bin/fabserver
+   bin/make_cad_png
+   bin/make_cad_eps
+   bin/make_cad_stl
+   bin/make_cad_camm
+   bin/make_cad_rml
+   bin/make_cad_epi
+   bin/make_cad_uni
+   bin/make_cad_sbp
+   bin/make_cad_g
+   bin/make_cad_ord
+   bin/make_cad_grb
+   bin/make_cad_drl
+   bin/make_math_camm
+   bin/make_math_epi
+   bin/make_math_g
+   bin/make_math_ord
+   bin/make_math_eps
+   bin/make_math_uni
+   bin/make_math_rml
+   bin/make_math_sbp
+   bin/make_math_grb
+   bin/make_math_drl
+   bin/make_png_png
+   bin/make_png_eps
+   bin/make_png_epi
+   bin/make_png_uni
+   bin/make_png_grb
+   bin/make_png_epi_halftone
+   bin/make_png_uni_halftone
+   bin/make_png_rml
+   bin/make_png_sbp
+   bin/make_png_ord
+   bin/make_png_camm
+   bin/make_png_plt
+   bin/make_png_g
+   bin/make_png_drl
+   bin/make_png_oms
+   bin/make_stl_png
+   bin/make_stl_rml
+   bin/make_stl_sbp
+   bin/make_stl_g
+   bin/make_svg_camm
+   bin/make_svg_epi
+   bin/make_svg_uni
+   bin/make_svg_oms
+   bin/make_svg_g
+   bin/make_svg_rml
+   bin/make_svg_sbp
+   bin/make_svg_ord
+   bin/make_png_snap
+   bin/make_cad_snap
+   bin/make_stl_snap
+   bin/make_svg_snap
+   bin/make_png_eps_halftone
+   bin/make_cad_dxf
+   bin/make_math_dxf
+   bin/make_math_stl
+   bin/make_png_dxf
+   bin/make_gif_stl
+   bin/cad_ui
+   bin/rml_send_gui
+
+command line: vol_gif in.vol out.gif nx ny nz [format [type [arg [size [dx dy dz [x0 y0 z0 [rx ry rz]]]]]]]
+   in.vol = input volume file
+   out.gif = output GIF file
+   nx,ny,nz = x,y,z input voxel number
+   format = 'f' for float 32, 'i' for uint16_t (default 'f')
+   type = 's' for section, 'h' for height (default 's')
+   arg = gamma for 's', threshold for 'h' (default 1)
+   size = mm per voxel (default 1)
+   dx,dy,dz = x,y,z output voxel number (default all)
+   x0,y0,z0 = x,y,z output voxel origin (default 0)
+   to be implemented: rx,ry,rz = view rotation angles (degrees; default 0)
+
+command line: vol_stl in.vol out.stl nx ny nz [format [threshold [size [points [angle]]]]]
+   in.vol = input VOL file
+   out.stl = output STL file
+   nx,ny,nz = x,y,z input voxel number
+   format = 'f' for float 32, 'i' for uint16_t (default 'f')
+   threshold: surface intensity threshold (0 = min, 1 = max, default 0.5))
+   size = voxel size (mm, default from file))
+   points = points to interpolate per point (default 0)
+   to be implemented: angle = minimum relative face angle to decimate vertices (default 0)
+
+command line: gif_info in.gif
+   in.gif = input GIF file
+
+command line: gif_png in.gif out.png [type [arg [points [size [rx ry rz]]]]]
+   in.gif = input gif file
+   out.png = output PNG file
+   type = 'z' of density, 'h' for height (default z)
+   arg = type argument
+      'z': gamma (default 1)
+      'h': threshold (0 = min, 1 = max, default 0.5)
+   points = points to interpolate per point (linear, default 0)
+   size = voxel size (mm, default from file))
+   to be implemented: rx,ry,rz = x,y,z rotation angles (degrees; default 0)
+
+command line: gif_stl in.gif out.stl [threshold [size [points [angle]]]]
+   in.gif = input GIF section file
+   out.stl = output STL file
+   threshold: surface intensity threshold (0 = min, 1 = max, default 0.5))
+   size = voxel size (mm, default from file))
+   points = points to interpolate per point (default 0)
+   to be implemented: angle = minimum relative face angle to decimate vertices (default 0)
+
+command line: stl_info in.stl
+   in.stl = input binary STL file
+
+command line: stl_png in.stl out.png [units [resolution [axis]]]
+   in.stl = input binary STL file
+   out.png = output PNG file
+   units = file units (optional, mm/unit, default 1)
+   resolution = image resolution (optional, pixels/mm, default 10)
+   axis = projection axis (optional, top or bottom, x|X|y|Y|z|Z, default z)
+
+command line: stl_path in.stl out.path [units [resolution]]]
+   in.stl = input binary STL file
+   out.png = output PNG file
+   units = file units (optional, mm/unit, default 1)
+   resolution = image resolution (optional, pixels/mm, default 10)
+
+command line: png_size in.png [dx [dy]]
+   in.png = input PNG file
+   dx = set width (optional, mm)
+   dy = set height (optional, mm)
+
+command line: png_scale in.png out.png low high
+   in.png = input PNG file
+   out.png = output PNG file
+   low = rescaled intensity minimum (0-1)
+   high = rescaled intensity maximum (0-1)
+
+command line: png_distances in.png out.png [intensity [distances]]
+   in.png = input PNG file
+   out.png = input PNG file
+   intensity = intensity level to slice (optional, 0-1, default 0.5)
+   distances = show distances (optional, 0/1, default 1)
+
+command line: png_offset in.png out.png [intensity [distance]]
+   in.png = input PNG file
+   out.png = input PNG file
+   intensity = intensity level to slice (optional, 0-1, default 0.5)
+   distance = distance to offset (optional, mm, default 0)
+
+command line: png_grb in.png out.grb
+   in.png = input PNG file
+   out.grb = output Gerber (RS-274X) file
+
+command line: png_drl in.png out.drl
+   in.png = input PNG file
+   out.drl = output Excellon file
+
+command line: png_path in.png out.path [error [offset_diameter [offset_number [offset_overlap [intensity_top [intensity_bottom [z_top [z_bottom [z_thickness [xz [yz [xy [type [clearance_length clearance_diameter]]]]]]]]]]]]]]
+   in.png = input PNG file
+   out.path = output path file
+   error = allowable vector fit deviation (optional, pixels, default 1.1)
+   offset_diameter = diameter to offset (optional, mm, default 0)
+   offset_number = number of contours to offset (optional, -1 to fill all, default 1)
+   offset_overlap = tool offset overlap fraction (optional, 0 (no overlap) - 1 (complete overlap, default 0.5))
+   intensity_top = top slice intensity (optional, 0-1, default 0.5)
+   intensity_bottom = bottom slice intensity (optional, 0-1, default intensity_top)
+   z_top = top slice z value (optional, mm, default 0)
+   z_bottom = bottom slice z value (optional, mm, default z_top)
+   z_thickness = slice z thickness (optional, mm, default z_top-z_bottom)
+   xz = xz finish (optional, 1=yes, default 0
+   yz = yz finish (optional, 1=yes, default 0
+   xy = xy path (optional, 1=yes, default 1
+   type = finish tool type (optional, f=flat end, b=ball end, default f
+   clearance_length = finish tool clearance length (optional, mm, 0 = no limit, default 0
+   clearance_diameter = finish tool clearance diameter (optional, mm, default offset_diameter
+
+command line: png_halftone in.png out.path [threshold [points [size [spacing [offset [invert]]]]]]
+   in.png = input PNG file
+   out.path = output path file
+   threshold = minimum spot radius (optional, pixels default 1)
+   points = points per spot (optional, default 8)
+   size = maximum spot size (optional, mm, default 1)
+   spacing = spot spacing (optional, 1 = size, default 1)
+   offset = row offset (optional, 1 = size, default 0.5)
+   offset = row offset (optional, 1 = size, default 0.5)
+   invert = invert image (0 = no (default), 1 = yes)
+
+command line: svg_path in.svg out.path [scale [points [resolution [zmin [zmax]]]]]
+   in.svg = input binary SVG file
+   out.path = output path file
+   scale = scale factor (optional, default 1.0)
+   points = points per curve segment (optional, default 25)
+   resolution = path x resolution (optional, default 10000)
+   zmin = path min intensity z (optional, mm, default 0)
+   zmax = path max intensity z (optional, mm, default zmin)
+
+command line: path_eps in.path out.eps [view]
+   in.path = input path file
+   out.eps= output PostScript file
+   view = view projection(s) (optional, z|3, default z)
+
+command line: path_dxf in.path out.dxf
+   in.path = input path file
+   out.dxf = output DXF file
+
+command line: path_png in.path out.png
+   in.path = input path file
+   out.png = output PNG file
+
+command line: path_rml in.path out.rml [speed [direction [jog [xmin ymin [zmin]]]]]
+   in.path = input path file
+   out.rml = output Roland Modela file
+   speed = cutting speed (optional, mm/s, default 4)
+   direction = machining direction (optional, 0 conventional/1 climb, default 1)
+   jog = jog height (optional, mm, default 1)
+   xmin = left position (optional, mm, default path value)
+   ymin = front position (optional, mm, default path value)
+   zmin = bottom position (optional, -mm, default path value)
+
+command line: path_sbp in.path out.sbp [direction [spindle_speed [xy_speed z_speed [xy_jog_speed z_jog_speed z_jog [units]]]]]]
+   in.path = input path file
+   out.sbp = output ShopBot file
+   direction = machining direction (optional, 0 conventional/1 climb, default 0)
+   spindle_speed = spindle speed (optional, if control installed, RPM, default 12000)
+   xy_speed = xy cutting speed (optional, mm/s, default 30)
+   z_speed = z cutting speed (optional, mm/s, default 30)
+   xy_jog_speed = xy jog speed (optional, mm/s, default 150)
+   z_jog_speed = z jog speed (optional, mm/s, default 150)
+   z_jog = z jog height (optional, mm, default 25)
+   units = mm per file unit (optional, default 25.4)
+
+command line: path_g in.path out.g [direction [z_jog [feed [z_feed [spindle [tool [coolant]]]]]]
+   in.path = input path file
+   out.g = output G-code file
+   direction = machining direction (optional, 0 conventional/1 climb, default 0)
+   z_jog = z jog height (optional, mm, default 25)
+   feed = feed rate (optional, mm/s, default 100)
+   z_feed = z plunge rate (optional, mm/s, default xy feed rate)
+   spindle = spindle speed (optional, RPM, default 5000)
+   tool = tool number (optional, default 1)
+   coolant = coolant on/off (optional, 0=off/1=on, default 1)
+
+command line: path_camm in.path out.camm [force [velocity [x y [location]]]]
+   in.path = input path file
+   out.camm = output Roland vinylcutter file
+   force = cutting force (optional, grams, default 45)
+   velocity = cutting speed (optional, cm/s, default 2)
+   x = origin x (optional, mm, default 0)
+   y = origin y (optional, mm, default 0)
+   location = origin location (optional, bottom left:l, bottom right:r, top left:L, top right:R, default l)
+
+command line: path_epi in.path out.epi [power [speed [focus [x y [ location [rate [max_power]]]]]]]
+   in.path = input path file
+   out.epi= output Epilog lasercutter file
+   power = percent power, for minimum z value (optional, 0-100, default 50)
+   speed = percent speed (optional, 0-100, default 50)
+   focus = autofocus (optional, 0=off | 1=on, default on)
+   x = origin x (optional, mm, default 0 = left side of bed)
+   y = origin y (optional, mm, default 0 = back side of bed, front positive)
+   location = origin location (optional, bottom left:l, bottom right:r, top left:L, top right:R, default l)
+   rate = pulse rate (optional, frequency, default 2500)
+   max_power = percent power, for maximum z value (optional, 0-100, default power)
+
+command line: path_uni in.path out.uni [power [speed [xmin ymin [rate [max_power]]]]]
+   in.path = input path file
+   out.uni= output Universal lasercutter file
+   power = percent power (optional, 0-100, default 100)
+   speed = percent speed (optional, 0-100, default 100)
+   xmin = left position (optional, mm, default path, 0 = left side of bed)
+   ymin = front position (optional, mm, default path, 0 = back, front positive)
+   rate = pulse rate (optional, frequency, default 500)
+   max_power = maximum power for maximum z value (optional, 0-100, default 100)
+
+command line: path_oms in.path out.oms [velocity [acceleration [period]]]
+   in.path = input path file
+   out.oms = output Resonetics excimer micromachining center file
+   velocity (default 0.1)
+   acceleration (default 5.0)
+   period (usec, default 10000)
+
+command line: path_ord in.path out.ord [lead [quality [xstart ystart]]]
+   in.path = input path file
+   out.ord = output Omax waterjet file
+   lead = lead in/out (optional, mm, default 2)
+   quality = cut quality (optional, default -3)
+   xstart,ystart = start position (optional, mm, default path start)
+
+command line: path_join in1.path in2.path out.path [dx [dy]]
+   in1.path = first input path file
+   in2.path = second input path file
+   out.path = joined output path file
+   dx = in1 horizontal offset (optional, mm, default 0)
+   dy = in1 vertical offset (optional, mm, default dx)
+
+command line: path_array in.path out.path nx ny [dx [dy]]
+   in.path = input path file
+   out.path = output path file
+   nx = number of horizonal array elements
+   ny = number of vertical array elements
+   dx = array element horizontal spacing (optional, mm, default 0)
+   dy = array element vertical spacing (optional, mm, default dx)
+
+command line: path_info in.path
+   in.path = input path file
+
+command line: path_time in.path move_speed [jog_height [jog_speed [plunge_speed]]]
+   in.path = input path file
+   move_speed = speed of path segments (mm/s)
+   jog_height = height between path segments (mm, optional, default 0)
+   jog_speed = speed between path segments (mm/s, optional, default move_speed)
+   plunge_speed = speed from jog to move  (mm/s, optional, default move_speed)
+
+command line: math_png in.math out.png [resolution [slices]]
+   in.math = input math string file
+   out.png = output PNG image
+   resolution = pixels per mm (optional, default 10)
+   slices = number of z slices (optional, default full)
+
+command line: math_dot in.math out.dot
+   in.math = input math string file
+   out.dot = output dot file
+
+command line: math_stl in.math out.stl [resolution [quality]]
+   in.math = input math string file
+   out.png = output PNG image
+   resolution = voxels per mm (optional, default 10)
+   quality = voxel interpolation level (default 8)
+
+command line: math_svg in.math out.svg [resolution [slices [error [quality]]]]
+   in.math = input math string file
+   out.png = output PNG image
+   resolution = voxels per mm (default: 10)
+   slices = z slices (defaults: 1 for 2D models, 10 for 3D models)
+   error = maximum decimation error (in mm^2)
+   quality = voxel interpolation level (default: 8)
+Note: output svgs are at 72 dpi.
+
+command line: cad_png in.cad [args]
+   in.cad = input .cad file
+   args = math_png arguments (optional)
+
+command line: cad_view in.cad [args]
+   in.cad = input .cad file
+   args = math_png arguments (optional)
+   image viewer =  eog
+
+command line: path_view in.path [view [viewer]]
+   in.path = input path file
+   view = view projection(s) (optional, z|3, default z)
+   viewer = PostScript viewer [default evince]
+
+command line: rml_move x y
+   x,y, = position to move to (mm)
+
+command line: fab_send [file]
+   file = file to send
+   file type commands:
+       {'.eps': 'inkscape "$file"', '.drl': 'gerbv "$file"', '.camm': 'printer=vinyl; lpr -P$printer "$file"', '.uni': 'port=/dev/lp0; cat "$file" > $port', '.epi': 'printer=laser; lprm -P$printer -; lpr -P$printer "$file"', '.sbp': 'gedit "$file"', '.rml': 'port=/dev/ttyUSB0; rml_send_gui "$file" $port', '.g': 'gedit "$file"', '.oms': 'gedit "$file"', '.dxf': 'gedit "$file"', '.stl': 'meshlab "$file"', '.plt': 'gedit "$file"', '.ord': 'gedit "$file"', '.grb': 'gerbv "$file"'}
+
+command line: fab_update [check|install]
+   check will inform you if a newer version of the fab modules is available.
+   install will install a newer version of the fab modules, if applicable.
+
+command line: cad_math in.cad out.math [args]
+   in.cad = input design file
+   out.math = output math string file
+   args = arguments to cad script
+          (delivered in sys.argv)
+
+command line: math_png_py in.math out.png [resolution [number [view [rx ry rx]]]]
+   in.math = input math string file
+   out.png = output PNG image
+   resolution = pixels per mm (optional, default 10)
+   number = number of z slices to evaluate (optional, default 1)
+   view = view projection(s) (optional, z|3, default z)
+   rx ry rz = 3D view angle (optional, degrees, default 70 0 20)
+[This command is deprecated; use math_png instead.]
+
+command line: math_stl in.math out.stl [resolution]
+   in.math = input math string file
+   out.stl = output STL image
+   resolution = pixels per mm (optional, default 1)
+
+command line: png_tile rows cols file1.png file2.png ...
+   rows = number of horizontal copies
+   cols = number of vertical copies
+   file1.png to fileN.png = files to tile
+
+command line: eagle_png [options] target.brd
+   target.brd = EAGLE brd file to render
+   The board outline should be a solid polygon on the 'milling' layer
+   Internal cutouts should be solid shapes on the 'holes' layer
+   
+   Valid options:
+       --resolution NUM : sets output image resolution
+       --doublesided : forces double-sided mode
+
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644
index 0000000..391c70a
--- /dev/null
+++ b/src/CMakeLists.txt
@@ -0,0 +1,10 @@
+cmake_minimum_required(VERSION 2.6)
+project(fabmod)
+
+set(CMAKE_BUILD_TYPE Release)
+
+add_subdirectory(core)
+add_subdirectory(solver)
+add_subdirectory(scripts)
+add_subdirectory(guis)
+add_subdirectory(py)
diff --git a/src/apps/README b/src/apps/README
new file mode 100644
index 0000000..05cf54f
--- /dev/null
+++ b/src/apps/README
@@ -0,0 +1,15 @@
+kokopelli:
+    This is a bundled version of kokopelli, an unusual CAD package.
+    It should be considered alpha software.  Use at your own risk.
+    
+Online:
+    kokopelli is usually distributed as part of the fab modules,
+    found online at http://kokompe.cba.mit.edu
+
+License:
+    kokopelli is freely available for for experimental and personal use;
+    license for commercial sale is available from MIT.
+
+Contact:
+    Matt Keeter
+    matt.keeter@cba.mit.edu
\ No newline at end of file
diff --git a/src/apps/bundle.py b/src/apps/bundle.py
new file mode 100755
index 0000000..630136f
--- /dev/null
+++ b/src/apps/bundle.py
@@ -0,0 +1,62 @@
+#!/usr/bin/env python
+"""
+This is a setup.py script generated by py2applet
+
+Usage:
+    python setup.py py2app
+"""
+
+from setuptools import setup
+import shutil
+import os
+import stat
+import subprocess
+import glob
+
+# Trick to make this run properly
+import sys
+sys.argv += ['py2app']
+
+subprocess.call(['make','fab'], cwd='../..')
+
+# Delete old build stuff
+try:    shutil.rmtree('build')
+except OSError: pass
+
+try:    shutil.rmtree('koko')
+except OSError: pass
+
+try:    os.remove('kokopelli.py')
+except OSError: pass
+
+# This is the pythons script that we're bundling into an application.
+shutil.copy('../../bin/kokopelli','kokopelli.py')
+shutil.copytree('../../bin/koko','koko')
+
+APP = ['kokopelli.py']
+DATA_FILES = glob.glob('../bin/koko/lib/*.py') + ['cba_icon.png']
+
+OPTIONS = {'argv_emulation': True,
+           'iconfile':'cba.icns'}
+
+# Run py2app to bundle everything.
+setup(
+    app=APP,
+    data_files=DATA_FILES,
+    options={'py2app': OPTIONS},
+    setup_requires=['py2app'],
+)
+
+# Copy libtree
+os.mkdir('dist/kokopelli.app/Contents/lib')
+shutil.copy('../../lib/libfab.dylib', 
+            'dist/kokopelli.app/Contents/lib/libfab.dylib')
+
+# Copy the readme and examples into the distribution directory, then zip it up
+shutil.copy('README','dist')
+shutil.copy('../../../../Web/examples.zip', 'dist/examples.zip')
+os.system('cd dist; unzip examples.zip; rm -rf __MACOSX;'+
+          'zip -r kokopelli.zip kokopelli.app README examples')
+shutil.rmtree('build')
+shutil.rmtree('koko')
+shutil.os.remove('kokopelli.py')
\ No newline at end of file
diff --git a/src/apps/cba.icns b/src/apps/cba.icns
new file mode 100644
index 0000000..79d6dcb
Binary files /dev/null and b/src/apps/cba.icns differ
diff --git a/src/apps/cba_icon.png b/src/apps/cba_icon.png
new file mode 100644
index 0000000..fbb930e
Binary files /dev/null and b/src/apps/cba_icon.png differ
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
new file mode 100644
index 0000000..b67dadf
--- /dev/null
+++ b/src/core/CMakeLists.txt
@@ -0,0 +1,28 @@
+cmake_minimum_required(VERSION 2.6)
+
+set(PROGRAMS vol_gif vol_stl gif_info gif_png gif_stl stl_info stl_png stl_path
+             png_size png_scale png_distances png_offset png_grb png_drl
+             png_path png_halftone svg_path
+             path_eps path_dxf path_png path_rml path_sbp path_g path_camm
+             path_epi path_uni path_oms path_ord
+             path_join path_array path_info path_time
+    CACHE STRING "Core program list")
+
+find_package(PNG REQUIRED)
+find_package(GIF REQUIRED)
+include_directories(${PNG_INCLUDE_DIR})
+include_directories(${GIF_INCLUDE_DIR})
+
+#set(CMAKE_C_FLAGS "-Wall -g")
+set(CMAKE_C_FLAGS "-Wall -O3")
+
+add_library(fabcore STATIC fab.c)
+
+foreach(program ${PROGRAMS})
+  add_executable(${program} ${program}.c)
+  target_link_libraries(${program} fabcore ${PNG_LIBRARY} ${GIF_LIBRARY} m)
+endforeach(program)
+
+if( ${CMAKE_PROJECT_NAME} MATCHES fabmod )
+  install(TARGETS ${PROGRAMS} DESTINATION ${PROJECT_SOURCE_DIR}/../bin)
+endif( ${CMAKE_PROJECT_NAME} MATCHES fabmod )
diff --git a/src/core/array_direction.c b/src/core/array_direction.c
new file mode 100644
index 0000000..65aeb76
--- /dev/null
+++ b/src/core/array_direction.c
@@ -0,0 +1,52 @@
+//
+// array_direction.c
+//    find array edge directions
+//    array_direction in.array out.array threshold
+//
+// Neil Gershenfeld
+// CBA MIT 7/30/10
+//
+// (c) Massachusetts Institute of Technology 2010
+// Permission granted for experimental and personal use;
+// license for commercial sale available from MIT.
+//
+// todo
+//    pipe I/O
+//    variable bit depth
+//
+
+#include "fab.h"
+
+int main(int argc, char **argv) {
+   //
+   // variables
+   //
+   struct fab_vars v;
+   init_vars(&v);
+   int threshold;
+   //
+   // command line args
+   //
+   if (argc != 4) {
+      printf("command line: array_direction in.array out.array threshold\n");
+      exit(-1);
+      }
+   //
+   // read array
+   //
+   fab_read_array(&v,argv[1]);
+   //
+   // threshold
+   //
+   sscanf(argv[3],"%d",&threshold);
+   fab_threshold(&v,threshold);
+   //
+   // set directions
+   //
+   fab_directions(&v);
+   //
+   // write array
+   //
+   fab_write_array(&v,argv[2]);
+   }
+
diff --git a/src/core/array_distances.c b/src/core/array_distances.c
new file mode 100644
index 0000000..ad65d2b
--- /dev/null
+++ b/src/core/array_distances.c
@@ -0,0 +1,71 @@
+//
+// array_distances.c
+//    find distances from edges
+//    array_distances in.array out.array threshold
+//
+// Neil Gershenfeld
+// CBA MIT 8/5/10
+//
+// (c) Massachusetts Institute of Technology 2010
+// Permission granted for experimental and personal use;
+// license for commercial sale available from MIT.
+//
+// todo
+//    pipe I/O
+//    variable bit depth
+//
+
+#include "fab.h"
+
+main(int argc, char **argv) {
+   //
+   // local vars
+   //
+   struct fab_vars v;
+   init_vars(&v);
+   int x,y,threshold;
+   float distance;
+   //
+   // command line args
+   //
+   if (argc != 4) {
+      printf("command line: array_distances in.array out.array threshold\n");
+      exit(-1);
+      }
+   //
+   // read array
+   //
+   fab_read_array(&v,argv[1]);
+   //
+   // threshold
+   //
+   sscanf(argv[3],"%d",&threshold);
+   fab_threshold(&v,threshold);
+   //
+   // find edges
+   //
+   fab_edges(&v);
+   //
+   // find edge distances
+   //
+   fab_distances(&v);
+   //
+   // put distances into array
+   //
+   for (y = 0; y < v.ny; ++y) {
+      for (x = 0; x < v.nx; ++x) {
+         if (v.xptr[y][x] != -1) {
+            distance = sqrt((v.xptr[y][x]-x)*(v.xptr[y][x]-x)
+               + (v.yptr[y][x]-y)*(v.yptr[y][x]-y));
+            v.array[y][x] = (int) distance;
+            }
+         else
+            v.array[y][x] = 128;
+         }
+      }
+   //
+   // write array
+   //
+   fab_write_array(&v,argv[2]);
+   }
+
diff --git a/src/core/array_edges.c b/src/core/array_edges.c
new file mode 100644
index 0000000..8080a28
--- /dev/null
+++ b/src/core/array_edges.c
@@ -0,0 +1,52 @@
+//
+// array_edges.c
+//    find edges in array
+//    array_edges in.array out.array threshold
+//
+// Neil Gershenfeld
+// CBA MIT 7/30/10
+//
+// (c) Massachusetts Institute of Technology 2010
+// Permission granted for experimental and personal use;
+// license for commercial sale available from MIT.
+//
+// todo
+//    pipe I/O
+//    variable bit depth
+//
+
+#include "fab.h"
+
+int main(int argc, char **argv) {
+   //
+   // variables
+   //
+   struct fab_vars v;
+   init_vars(&v);
+   int threshold;
+   //
+   // command line args
+   //
+   if (argc != 4) {
+      printf("command line: array_edges in.array out.array threshold\n");
+      exit(-1);
+      }
+   //
+   // read array
+   //
+   fab_read_array(&v,argv[1]);
+   //
+   // threshold
+   //
+   sscanf(argv[3],"%d",&threshold);
+   fab_threshold(&v,threshold);
+   //
+   // find edges
+   //
+   fab_edges(&v);
+   //
+   // write array
+   //
+   fab_write_array(&v,argv[2]);
+   }
+
diff --git a/src/core/array_info.c b/src/core/array_info.c
new file mode 100644
index 0000000..7a242e3
--- /dev/null
+++ b/src/core/array_info.c
@@ -0,0 +1,39 @@
+//
+// array_info.c
+//    read and print array info
+//
+// Neil Gershenfeld
+// CBA MIT 8/5/10
+//
+// (c) Massachusetts Institute of Technology 2010
+// Permission granted for experimental and personal use;
+// license for commercial sale available from MIT.
+//
+// todo
+//    pipe I/O
+//    variable bit depth
+//
+
+#include "fab.h"
+
+main(int argc, char **argv) {
+   //
+   // local vars
+   //
+   struct fab_vars v;
+   init_vars(&v);
+   int x,y,threshold;
+   float distance;
+   //
+   // command line args
+   //
+   if (argc != 2) {
+      printf("command line: array_info in.array\n");
+      exit(-1);
+      }
+   //
+   // read array
+   //
+   fab_read_array(&v,argv[1]);
+   }
+
diff --git a/src/core/array_offset.c b/src/core/array_offset.c
new file mode 100644
index 0000000..299ce1d
--- /dev/null
+++ b/src/core/array_offset.c
@@ -0,0 +1,67 @@
+//
+// array_offset.c
+//    offset array
+//    array_offset in.array out.array threshold distance
+//
+// Neil Gershenfeld
+// CBA MIT 7/30/10
+//
+// (c) Massachusetts Institute of Technology 2010
+// Permission granted for experimental and personal use;
+// license for commercial sale available from MIT.
+//
+// todo
+//    pipe I/O
+//    variable bit depth
+//
+
+#include "fab.h"
+
+main(int argc, char **argv) {
+   //
+   // local vars
+   //
+   struct fab_vars v;
+   init_vars(&v);
+   int x,y,threshold,count;
+   float distance;
+   //
+   // command line args
+   //
+   if (argc != 5) {
+      printf("command line: array_offset in.array out.array threshold distance\n");
+      exit(-1);
+      }
+   //
+   // read array
+   //
+   fab_read_array(&v,argv[1]);
+   //
+   // threshold
+   //
+   sscanf(argv[3],"%d",&threshold);
+   fab_threshold(&v,threshold);
+   //
+   // find edges
+   //
+   fab_edges(&v);
+   //
+   // find edge distances
+   //
+   fab_distances(&v);
+   //
+   // offset
+   //
+   distance = atof(argv[4]);
+   count = fab_offset(&v,distance);
+   printf("%d points remain\n",count);
+   //
+   // set edge directions
+   //
+   fab_directions(&v);
+   //
+   // write array
+   //
+   fab_write_array(&v,argv[2]);
+   }
+
diff --git a/src/core/array_png.c b/src/core/array_png.c
new file mode 100644
index 0000000..3d8f8bf
--- /dev/null
+++ b/src/core/array_png.c
@@ -0,0 +1,43 @@
+//
+// array_png.c
+//    converted array to a PNG image
+//    array_png.c in.array out.png
+//
+// Neil Gershenfeld
+// CBA MIT 7/30/10
+//
+// (c) Massachusetts Institute of Technology 2010
+// Permission granted for experimental and personal use;
+// license for commercial sale available from MIT.
+//
+// todo
+//    pipe I/O
+//    variable bit depth
+//    z layers
+//
+
+#include "fab.h"
+
+int main(int argc, char **argv) {
+   //
+   // variables
+   //
+   struct fab_vars v;
+   init_vars(&v);
+   //
+   // command line args
+   //
+   if (argc != 3) {
+      printf("command line: array_png.c in.array out.png\n");
+      exit(-1);
+      }
+   //
+   // read array
+   //
+   fab_read_array(&v,argv[1]);
+   //
+   // write PNG
+   //
+   fab_write_png(&v,argv[2]);
+   }
+
diff --git a/src/core/array_slice.c b/src/core/array_slice.c
new file mode 100644
index 0000000..100a1c7
--- /dev/null
+++ b/src/core/array_slice.c
@@ -0,0 +1,48 @@
+//
+// array_slice.c
+//    slice array
+//    array_slice in.array out.array threshold
+//
+// Neil Gershenfeld
+// CBA MIT 7/30/10
+//
+// (c) Massachusetts Institute of Technology 2010
+// Permission granted for experimental and personal use;
+// license for commercial sale available from MIT.
+//
+// todo
+//    pipe I/O
+//    variable bit depth
+//
+
+#include "fab.h"
+
+int main(int argc, char **argv) {
+   //
+   // variables
+   //
+   struct fab_vars v;
+   init_vars(&v);
+   int threshold;
+   //
+   // command line args
+   //
+   if (argc != 4) {
+      printf("command line: array_slice in.array out.array threshold\n");
+      exit(-1);
+      }
+   //
+   // read lattice
+   //
+   fab_read_array(&v,argv[1]);
+   //
+   // threshold
+   //
+   sscanf(argv[3],"%d",&threshold);
+   fab_threshold(&v,threshold);
+   //
+   // write state slice
+   //
+   fab_write_array(&v,argv[2]);
+   }
+
diff --git a/src/core/array_states_png.c b/src/core/array_states_png.c
new file mode 100644
index 0000000..22e6f69
--- /dev/null
+++ b/src/core/array_states_png.c
@@ -0,0 +1,47 @@
+//
+// array_states_png.c
+//    converted array states to a PNG image
+//    array_states png.c in.array out.png
+//
+// Neil Gershenfeld
+// CBA MIT 7/30/10
+//
+// (c) Massachusetts Institute of Technology 2010
+// Permission granted for experimental and personal use;
+// license for commercial sale available from MIT.
+//
+// todo
+//    pipe I/O
+//    variable bit depth
+//    z layers
+//
+
+#include "fab.h"
+
+int main(int argc, char **argv) {
+   //
+   // variables
+   //
+   struct fab_vars v;
+   init_vars(&v);
+   //
+   // command line args
+   //
+   if (argc != 3) {
+      printf("command line: array_states_png.c in.array out.png\n");
+      exit(-1);
+      }
+   //
+   // read array
+   //
+   fab_read_array(&v,argv[1]);
+   //
+   // shade states
+   //
+   fab_shade_states(&v);
+   //
+   // write PNG
+   //
+   fab_write_png(&v,argv[2]);
+   }
+
diff --git a/src/core/fab.c b/src/core/fab.c
new file mode 100644
index 0000000..043ed09
--- /dev/null
+++ b/src/core/fab.c
@@ -0,0 +1,2376 @@
+//
+// fab.c
+//    fab modules routines
+//
+// Neil Gershenfeld 9/8/13
+// (c) Massachusetts Institute of Technology 2013
+//
+// This work may be reproduced, modified, distributed,
+// performed, and displayed for any purpose, but must
+// acknowledge the fab modules project. Copyright is
+// retained and must be preserved. The work is provided
+// as is; no warranty is provided, and users accept all 
+// liability.
+//
+
+#include "fab.h"
+
+//
+// initialization
+//
+
+void init_vars(struct fab_vars *v) {
+   //
+   // fab_vars initialization
+   //
+   v->empty = 0;
+   v->interior = 1;
+   v->edge = (1 << 1);
+   v->north = (1 << 2);
+   v->west = (2 << 2);
+   v->east = (3 << 2);
+   v->south = (4 << 2);
+   v->stop = (5 << 2);
+   v->corner = (6 << 2);
+   v->corner2 = (7 << 2);
+   v->direction = (7 << 2);
+   v->g = 0;
+   v->h = 0;
+   v->dx = 0;
+   v->dy = 0;
+   v->dz = 0;
+   v->xmin = 0;
+   v->ymin = 0;
+   v->zmin = 0;
+   v->distances = 0;
+   v->starts= 0;
+   v->minimums= 0;
+   }
+
+void fab_limits(struct fab_vars *v, float *vertex) {
+	//
+	// update limits for vertex
+	//
+	int i;
+	for (i = 0; i < 3; ++i) {
+		if (vertex[i] > v->mesh->max[i])
+			v->mesh->max[i] = vertex[i];
+		if (vertex[i] < v->mesh->min[i])
+			v->mesh->min[i] = vertex[i];
+	}
+}
+
+//
+// input
+//
+
+void fab_read_stl(struct fab_vars *v, char *input_file_name) {
+   //
+   // read STL into triangle mesh
+   //
+	FILE *input_file;
+   char buf[80],*ptr;
+   uint32_t i,size,ret;
+   //
+   // read file
+   //
+   input_file = fopen(input_file_name, "rb");
+   if (input_file == 0) {
+      printf("fab.c: oops -- can't open %s\n",input_file_name);
+      exit(-1);
+      }
+   ret = fread(buf,80,1,input_file);
+   ptr = strstr(buf,"facet");
+   if (ptr != NULL) {
+      printf("fab.c: oops -- must be binary STL file\n");
+      exit(-1);
+      }
+   ret = fread(&size,4,1,input_file);
+	v->mesh = malloc(sizeof(struct fab_mesh_type));
+	v->mesh->triangle = malloc(sizeof(struct fab_mesh_triangle_type));
+   v->mesh->first = v->mesh->triangle;
+   v->mesh->last = v->mesh->triangle;
+   v->mesh->triangle->previous = v->mesh->triangle->next = 0;
+   ret = fread(&(v->mesh->triangle->normal),4,3,input_file);
+   ret = fread(&(v->mesh->triangle->v0),4,3,input_file);
+   ret = fread(&(v->mesh->triangle->v1),4,3,input_file);
+   ret = fread(&(v->mesh->triangle->v2),4,3,input_file);
+   ret = fread(&(v->mesh->triangle->attribute),2,1,input_file);
+   for (i = 1; i < size; ++i) {
+	   v->mesh->last = malloc(sizeof(struct fab_mesh_triangle_type));
+      v->mesh->last->previous = v->mesh->triangle;
+      v->mesh->triangle->next = v->mesh->last;
+      v->mesh->triangle = v->mesh->last;
+      v->mesh->triangle->previous = v->mesh->triangle->next = 0;
+      ret = fread(&(v->mesh->triangle->normal),4,3,input_file);
+      ret = fread(&(v->mesh->triangle->v0),4,3,input_file);
+      ret = fread(&(v->mesh->triangle->v1),4,3,input_file);
+      ret = fread(&(v->mesh->triangle->v2),4,3,input_file);
+      ret = fread(&(v->mesh->triangle->attribute),2,1,input_file);
+      }
+   fclose(input_file);
+   //
+   // check read
+   //
+   if (ret == 0) {
+      printf("fab.c: oops -- file read failed\n");
+      exit(-1);
+      }
+   v->mesh->min[0] = fab_big;
+   v->mesh->min[1] = fab_big;
+   v->mesh->min[2] = fab_big;
+   v->mesh->max[0] = -fab_big;
+   v->mesh->max[1] = -fab_big;
+   v->mesh->max[2] = -fab_big;
+   v->mesh->triangle = v->mesh->first;
+   while (v->mesh->triangle != 0) {
+      fab_limits(v, v->mesh->triangle->v0);
+      fab_limits(v, v->mesh->triangle->v1);
+      fab_limits(v, v->mesh->triangle->v2);
+      v->mesh->triangle = v->mesh->triangle->next;
+      }
+   //
+   // report and return
+   //
+   v->mesh->number = size;
+   printf("read %s\n",input_file_name);
+   printf("   number of triangles: %d\n",v->mesh->number);
+   printf("   xmin, xmax: %f %f\n",v->mesh->min[0],v->mesh->max[0]);
+   printf("   ymin, ymax: %f %f\n",v->mesh->min[1],v->mesh->max[1]);
+   printf("   zmin, zmax: %f %f\n",v->mesh->min[2],v->mesh->max[2]);
+   }
+
+void fab_read_png(struct fab_vars *v, char *input_file_name) {
+   //
+   // read PNG into fab_vars
+   //
+	FILE *input_file;
+   int y;
+   png_uint_32 res_x,res_y;
+   int unit_type;
+   //
+   // read PNG file
+   //
+   input_file = fopen(input_file_name, "rb");
+   if (input_file == 0) {
+      printf("fab.c: oops -- can't open %s\n",input_file_name);
+      exit(-1);
+      }
+   v->png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
+                                       NULL, NULL, NULL);
+   v->info_ptr = png_create_info_struct(v->png_ptr);
+   png_init_io(v->png_ptr, input_file);
+   png_read_info(v->png_ptr, v->info_ptr);
+   //
+   // get size
+   //
+   v->nx = png_get_image_width(v->png_ptr, v->info_ptr);
+   v->ny = png_get_image_height(v->png_ptr, v->info_ptr);
+   v->bit_depth = png_get_bit_depth(v->png_ptr, v->info_ptr);
+   //
+   // get units
+   //
+   png_get_pHYs(v->png_ptr, v->info_ptr, &res_x, &res_y, &unit_type);
+   if (unit_type == PNG_RESOLUTION_METER) {
+      v->dx = 1000 * v->nx / ((float) res_x);
+      v->dy = 1000 * v->ny / ((float) res_y);
+   }
+   else {
+      printf("fab.c: don't recognize PNG units, assuming 72/inch\n");
+      res_x = (72*1000.0)/(25.4);
+      res_y = (72*1000.0)/(25.4);
+      v->dx = 1000 * v->nx / ((float) res_x);
+      v->dy = 1000 * v->ny / ((float) res_y);
+      }
+   //
+   // get texts
+   //
+   png_textp text_ptr;
+   int num_text;
+   png_get_text(v->png_ptr, v->info_ptr, &text_ptr, &num_text);
+   int t;
+   float xmin=0,ymin=0,zmin=0,zmax=0;
+   for (t = 0; t < num_text; ++t) {
+      if (0 == strcmp(text_ptr[t].key, "zmax"))
+         sscanf(text_ptr[t].text, "%g", &zmax);
+      else if (0 == strcmp(text_ptr[t].key, "zmin"))
+         sscanf(text_ptr[t].text, "%g", &zmin);
+      else if (0 == strcmp(text_ptr[t].key, "ymin"))
+         sscanf(text_ptr[t].text, "%g", &ymin);
+      else if (0 == strcmp(text_ptr[t].key, "xmin"))
+         sscanf(text_ptr[t].text, "%g", &xmin);
+      }
+   //
+   // set pixels
+   //
+	v->row_pointers = (png_bytep*) malloc(sizeof(png_bytep) * v->ny);
+	for (y = 0; y < v->ny; ++y)
+		v->row_pointers[y] = (png_byte*) malloc(png_get_rowbytes(v->png_ptr,
+		                                                         v->info_ptr));
+	png_read_image(v->png_ptr, v->row_pointers);
+   //
+   // set variables
+   //
+   v->nz = 1;
+   if (zmax != zmin)
+      v->dz = zmax - zmin;
+   else
+      v->dz = 0;
+   v->xmin = xmin;
+   v->ymin = ymin;
+   v->zmin = zmin;
+   //
+   // allocate array
+   //
+   v->array = malloc(v->ny*sizeof(uint32_t *));
+   for (y = 0; y < v->ny; ++y)
+      v->array[y] = malloc(v->nx*sizeof(uint32_t));
+   //
+   // copy image to array
+   //
+   fab_png_array(v);
+   //
+   // close and return
+   //
+   fclose(input_file);
+   printf("read %s\n",input_file_name);
+   printf("   bit depth: %d\n",v->bit_depth);
+   printf("   x pixels: %d, y pixels: %d\n",v->nx,v->ny);
+   printf("   x pixels/m: %d, y pixels/m: %d\n",(int) res_x,(int) res_y);
+   if (v->dz != 0) {
+      printf("   dx: %f mm, dy: %f mm, dz: %f mm\n",v->dx,v->dy,v->dz);
+      }
+   else {
+      printf("   dx: %f mm, dy: %f mm\n",v->dx,v->dy);
+      }
+   }
+
+void fab_read_array(struct fab_vars *v, char *input_file_name) {
+   //
+   // read array
+   //
+   FILE *input_file;
+   int x,y,ret;
+   input_file = fopen(input_file_name,"rb");
+   //
+   // read vars
+   //
+   ret = fread(&v->nx,4,1,input_file);
+   ret = fread(&v->ny,4,1,input_file);
+   ret = fread(&v->nz,4,1,input_file);
+   ret = fread(&v->dx,4,1,input_file);
+   ret = fread(&v->dy,4,1,input_file);
+   ret = fread(&v->dz,4,1,input_file);
+   ret = fread(&v->xmin,4,1,input_file);
+   ret = fread(&v->ymin,4,1,input_file);
+   ret = fread(&v->zmin,4,1,input_file);
+   //
+   // allocate array
+   //
+   v->array = malloc(v->ny*sizeof(uint32_t *));
+   for (y = 0; y < v->ny; ++y)
+      v->array[y] = malloc(v->nx*sizeof(uint32_t));
+   //
+   // read array
+   //
+   for (y = 0; y < v->ny; ++y)
+      for (x = 0; x < v->nx; ++x)
+         ret = fread(&v->array[y][x],1,1,input_file);
+   //
+   // check read
+   //
+   if (ret == 0) {
+      printf("fab.c: oops -- file read failed\n");
+      exit(-1);
+      }
+   //
+   // print, close, and return
+   //
+   printf("read %s\n",input_file_name);
+   printf("   nx: %d, ny: %d, nz: %d\n",v->nx,v->ny,v->nz);
+   printf("   dx: %f, dy: %f, dz: %f\n",v->dx,v->dy,v->dz);
+   printf("   xmin: %f, ymin: %f, zmin: %f\n",v->xmin,v->ymin,v->zmin);
+   fclose(input_file);
+   }
+
+void fab_read_path(struct fab_vars *v, char *input_file_name) {
+   //
+   // read path from file
+   //
+	FILE *input_file;
+   int x,y,z,dof,nsegs=0,npts=0;
+   char line[255];
+   input_file = fopen(input_file_name,"r");
+   if (input_file == 0) {
+      printf("fab.c: oops -- can't open %s\n",input_file_name);
+      exit(-1);
+      }
+   printf("read %s\n",input_file_name);
+   //
+   // scan file
+   //
+   while (fgets(line,sizeof line,input_file) != NULL) {
+      if (0 == strncmp(line,"dof:",4)) {
+         sscanf(line,"dof: %d",&dof);
+         printf("   degrees of freedom: %d\n",dof);
+         fab_path_start(v,dof);
+         }
+      else if (0 == strncmp(line,"units:",6)) {
+         printf("   %s",line); // todo: parse and use units
+         }
+      else if (0 == strncmp(line,"nx ny:",6)) {
+         sscanf(line,"nx ny: %d %d",&v->nx,&v->ny);
+         printf("   nx: %d, ny: %d\n",v->nx,v->ny);
+         }
+      else if (0 == strncmp(line,"nx ny nz:",9)) {
+         sscanf(line,"nx ny nz: %d %d %d",&v->nx,&v->ny,&v->nz);
+         printf("   nx: %d, ny: %d, nz: %d\n",v->nx,v->ny,v->nz);
+         }
+      else if (0 == strncmp(line,"dx dy:",6)) {
+         sscanf(line,"dx dy: %lf %lf",&v->dx,&v->dy);
+         printf("   dx: %f, dy: %f\n",v->dx,v->dy);
+         }
+      else if (0 == strncmp(line,"dx dy dz:",9)) {
+         sscanf(line,"dx dy dz: %lf %lf %lf",&v->dx,&v->dy,&v->dz);
+         printf("   dx: %f, dy: %f, dz: %f\n",v->dx,v->dy,v->dz);
+         }
+      else if (0 == strncmp(line,"xmin ymin:",10)) {
+         sscanf(line,"xmin ymin: %lf %lf",&v->xmin,&v->ymin);
+         printf("   xmin: %f, ymin: %f\n",v->xmin,v->ymin);
+         }
+      else if (0 == strncmp(line,"xmin ymin zmin:",15)) {
+         sscanf(line,"xmin ymin zmin: %lf %lf %lf",&v->xmin,&v->ymin,&v->zmin);
+         printf("   xmin: %f, ymin: %f, zmin: %f\n",v->xmin,v->ymin,v->zmin);
+         }
+      else if (0 == strncmp(line,"path start:",11)) {
+         printf("   path start:\n");
+         }
+      else if (0 == strncmp(line,"segment start:",14)) {
+         fab_path_segment(v);
+         nsegs += 1;
+         while (fgets(line,sizeof line,input_file) != NULL) {
+            if (0 == strncmp(line,"segment end:",12)) {
+               break;
+               }
+            else {
+               fab_path_point(v);
+               npts += 1;
+               if (dof == 3) {
+                  sscanf(line,"%d %d %d",&x,&y,&z);
+                  fab_path_axis(v,x);
+                  fab_path_axis(v,y);
+                  fab_path_axis(v,z);
+                  }
+               else if (dof == 2) {
+                  sscanf(line,"%d %d",&x,&y);
+                  fab_path_axis(v,x);
+                  fab_path_axis(v,y);
+                  }
+               else {
+                  printf("fab.c: oops -- can't handle %d dof\n",dof);
+                  exit(-1);
+                  }
+               }
+            }
+         }
+      else if (0 == strncmp(line,"path end:",9)) {
+         printf("   segments: %d, points: %d\n",nsegs,npts);
+         break;
+         }
+      else {
+         printf("fab.c: oops -- can't parse %s\n",line);
+         exit(-1);
+         }
+      }
+   fclose(input_file);
+   //
+   // create array
+   //
+   v->array = malloc(v->ny*sizeof(uint32_t *));
+   for (y = 0; y < v->ny; ++y)
+      v->array[y] = malloc(v->nx*sizeof(uint32_t));
+   //
+   // return
+   //
+   return;
+   }
+
+//
+// output
+//
+
+void fab_write_png_K(struct fab_vars *v, char *output_file_name) {
+   //
+   // write grayscale PNG from fab_vars
+   //
+	FILE *output_file;
+   int x,y;
+   int num_text=0;
+   char xmins[100],ymins[100],zmins[100],zmaxs[100];
+   png_text text_ptr[4];
+   png_uint_32 res_x,res_y;
+   png_byte color_type;
+   png_byte bit_depth;
+   png_byte *ptr;
+   //
+   // open PNG file
+   //
+	output_file = fopen(output_file_name, "wb");
+	v->png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
+      NULL, NULL, NULL);
+	v->info_ptr = png_create_info_struct(v->png_ptr);
+	png_init_io(v->png_ptr, output_file);
+   //
+   // set vars
+   //
+   bit_depth = v->bit_depth;
+   color_type = PNG_COLOR_TYPE_GRAY; 
+	png_set_IHDR(v->png_ptr, v->info_ptr, v->nx, v->ny,
+	   bit_depth, color_type, PNG_INTERLACE_NONE,
+		PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+   res_x = 1000 * v->nx / v->dx;
+   res_y = 1000 * v->ny / v->dy;
+   png_set_pHYs(v->png_ptr, v->info_ptr, res_x, res_y, PNG_RESOLUTION_METER);
+   text_ptr[0].key = "xmin";
+   sprintf(xmins,"%f",v->xmin);
+   text_ptr[0].text = xmins;
+   text_ptr[0].compression = PNG_TEXT_COMPRESSION_NONE;
+   num_text += 1;
+   text_ptr[1].key = "ymin";
+   sprintf(ymins,"%f",v->ymin);
+   text_ptr[1].text = ymins;
+   text_ptr[1].compression = PNG_TEXT_COMPRESSION_NONE;
+   num_text += 1;
+   if (v->dz != 0) {
+      text_ptr[2].key = "zmin";
+      sprintf(zmins,"%f",v->zmin);
+      text_ptr[2].text = zmins;
+      text_ptr[2].compression = PNG_TEXT_COMPRESSION_NONE;
+      num_text += 1;
+      text_ptr[3].key = "zmax";
+      sprintf(zmaxs,"%f",(v->zmin + v->dz));
+      text_ptr[3].text = zmaxs;
+      text_ptr[3].compression = PNG_TEXT_COMPRESSION_NONE;
+      num_text += 1;
+      }
+   png_set_text(v->png_ptr, v->info_ptr, text_ptr, num_text);
+   png_write_info(v->png_ptr, v->info_ptr);
+   //
+   // allocate pixels
+   //
+	v->row_pointers = (png_bytep*) malloc(sizeof(png_bytep) * v->ny);
+	for (y = 0; y < v->ny; ++y)
+		v->row_pointers[y] = (png_byte*) malloc(png_get_rowbytes(v->png_ptr,
+		                                                         v->info_ptr));
+   //
+   // set pixels
+   //
+   if (bit_depth == 8) {
+      for (y = 0; y < v->ny; ++y)
+         for (x = 0; x < v->nx; ++x) {
+      		ptr = &(v->row_pointers[y][x]);
+      	   ptr[0] = v->array[y][x];
+      	   }
+         }
+   else if (bit_depth == 16) {
+      for (y = 0; y < v->ny; ++y)
+         for (x = 0; x < v->nx; ++x) {
+      		ptr = &(v->row_pointers[y][x*2]);
+      	   ptr[0] = (v->array[y][x] >> 8) & 255;
+      	   ptr[1] = v->array[y][x] & 255;
+      	   }
+         }
+   else {
+      printf("fab.c: oops -- don't recognize bit depth %d\n",v->bit_depth);
+      exit(-1);
+      }
+   //
+   // write, close, and return
+   //
+	png_write_image(v->png_ptr, v->row_pointers);
+	png_write_end(v->png_ptr, NULL);
+   fclose(output_file);
+   printf("write %s\n",output_file_name);
+   printf("   x pixels: %d, y pixels: %d\n",v->nx,v->ny);
+   printf("   x pixels/m: %d, y pixels/m: %d\n",(int) res_x,(int) res_y);
+   if (v->dz != 0) {
+      printf("   dx: %f mm, dy: %f mm, dz: %f mm\n",v->dx,v->dy,v->dz);
+      }
+   else {
+      printf("   dx: %f mm, dy: %f mm\n",v->dx,v->dy);
+      }
+   }
+
+void fab_write_array(struct fab_vars *v, char *output_file_name) {
+   //
+   // write array
+   //
+   FILE *output_file;
+   int x,y;
+   output_file = fopen(output_file_name,"wb");
+   fwrite(&v->nx,4,1,output_file);
+   fwrite(&v->ny,4,1,output_file);
+   fwrite(&v->nz,4,1,output_file);
+   fwrite(&v->dx,4,1,output_file);
+   fwrite(&v->dy,4,1,output_file);
+   fwrite(&v->dz,4,1,output_file);
+   fwrite(&v->xmin,4,1,output_file);
+   fwrite(&v->ymin,4,1,output_file);
+   fwrite(&v->zmin,4,1,output_file);
+   for (y = 0; y < v->ny; ++y)
+      for (x = 0; x < v->nx; ++x)
+         fwrite(&v->array[y][x],1,1,output_file);
+   fclose(output_file);
+   printf("write %s\n",output_file_name);
+   printf("   nx: %d, ny: %d, nz: %d\n",v->nx,v->ny,v->nz);
+   printf("   dx: %f, dy: %f, dz: %f\n",v->dx,v->dy,v->dz);
+   printf("   xmin: %f, ymin: %f, zmin: %f\n",v->xmin,v->ymin,v->zmin);
+   }
+
+void fab_write_path(struct fab_vars *v, char *output_file_name) {
+   //
+   // write path to file
+   //
+	FILE *output_file;
+   int i,nsegs=0,npts=0;
+   output_file = fopen(output_file_name,"w");
+   v->path->segment = v->path->first;
+   fprintf(output_file,"dof: %d\n",v->path->dof);
+   fprintf(output_file,"units: ");
+   for (i = 0; i < v->path->dof; ++i)
+      fprintf(output_file,"mm ");
+   fprintf(output_file,"\n");
+   if (v->path->dof == 2) {
+      fprintf(output_file,"nx ny: %d %d\n",v->nx,v->ny);
+      fprintf(output_file,"dx dy: %f %f\n",v->dx,v->dy);
+      fprintf(output_file,"xmin ymin: %f %f\n",v->xmin,v->ymin);
+      }
+   else if (v->path->dof > 2) {
+      fprintf(output_file,"nx ny nz: %d %d %d\n",v->nx,v->ny,v->nz);
+      fprintf(output_file,"dx dy dz: %f %f %f\n",v->dx,v->dy,v->dz);
+      fprintf(output_file,"xmin ymin zmin: %f %f %f\n",v->xmin,v->ymin,v->zmin);
+      }
+   fprintf(output_file,"path start:\n");
+   while (1) {
+      //
+      // follow segments
+      //
+      v->path->segment->point = v->path->segment->first;
+      fprintf(output_file,"segment start:\n");
+      nsegs += 1;
+      while (1) {
+         //
+         // follow points
+         //
+         v->path->segment->point->axis = v->path->segment->point->first;
+         while (1) {
+            //
+            // follow axes
+            //
+            fprintf(output_file,"%d ",v->path->segment->point->axis->value);
+            if (v->path->segment->point->axis->next == 0)
+               break;
+            v->path->segment->point->axis = v->path->segment->point->axis->next;
+            }
+         fprintf(output_file,"\n");
+         npts += 1;
+         if (v->path->segment->point->next == 0)
+            break;
+         v->path->segment->point = v->path->segment->point->next;
+         }
+      fprintf(output_file,"segment end:\n");
+      if (v->path->segment->next == 0)
+         break;
+      v->path->segment = v->path->segment->next;
+      }
+   fprintf(output_file,"path end:\n");
+   fclose(output_file);
+   printf("write %s\n",output_file_name);
+   printf("   degrees of freedom: %d\n",v->path->dof);
+   printf("   units: ");
+   for (i = 0; i < v->path->dof; ++i) {
+      printf("mm ");
+      }
+   printf("\n");
+   printf("   segments: %d, points: %d\n",nsegs,npts);
+   if (v->path->dof == 2) {
+      printf("   nx: %d, ny: %d\n",v->nx,v->ny);
+      printf("   dx: %f, dy: %f\n",v->dx,v->dy);
+      printf("   xmin: %f, ymin: %f\n",v->xmin,v->ymin);
+      }
+   else if (v->path->dof > 2) {
+      printf("   nx: %d, ny: %d, nz: %d\n",v->nx,v->ny,v->nz);
+      printf("   dx: %f, dy: %f, dz: %f\n",v->dx,v->dy,v->dz);
+      printf("   xmin: %f, ymin: %f, zmin: %f\n",v->xmin,v->ymin,v->zmin);
+      }
+   }
+
+//
+// shading
+//
+
+void fab_shade_states(struct fab_vars *v) {
+   //
+   // shade array states
+   //
+   int x,y;
+   for (y = 0; y < v->ny; ++y)
+      for (x = 0; x < v->nx; ++x) {
+         if ((v->array[y][x] & v->direction) == v->north)
+            v->array[y][x] = 200;
+         else if ((v->array[y][x] & v->direction) == v->east)
+            v->array[y][x] = 150;
+         else if ((v->array[y][x] & v->direction) == v->south)
+            v->array[y][x] = 100;
+         else if ((v->array[y][x] & v->direction) == v->west)
+            v->array[y][x] = 50;
+         else if ((v->array[y][x] & v->direction) == v->stop)
+            v->array[y][x] = 25;
+         else if (v->array[y][x] == v->empty)
+            v->array[y][x] = 0;
+         else if (v->array[y][x] & v->edge)
+            v->array[y][x] = 128;
+         else if (v->array[y][x] & v->interior)
+            v->array[y][x] = 255;
+         }
+   }
+
+void fab_shade_line(struct fab_vars *v, int x0, int y0, int x1, int y1, int intensity) {
+   //
+   // shade line into array
+   //
+   int x,y,dx,dy,step;
+   float slope;
+   dx = x1 - x0;
+   dy = y1 - y0;
+   x = x0;
+   y = y0;
+   if ((dx == 0) && (dy == 0)) {
+      if ((x0 > 0) && (x0 < v->nx) && (y0 > 0) && (y0 < v->ny))
+         v->array[y0][x0] = intensity;
+      }
+   else if (abs(dx) == 0) {
+      if (dy > 0)
+         step = 1;
+      else
+         step = -1;
+      do {
+         if ((x > 0) && (x < v->nx) && (y > 0) && (y < v->ny))
+            v->array[y][x] = intensity;
+         y += step;
+         } while (y != y1);
+      if ((x > 0) && (x < v->nx) && (y > 0) && (y < v->ny))
+         v->array[y][x] = intensity;
+      }
+   else if (abs(dy) == 0) {
+      if (dx > 0)
+         step = 1;
+      else
+         step = -1;
+      do {
+         if ((x > 0) && (x < v->nx) && (y > 0) && (y < v->ny))
+            v->array[y][x] = intensity;
+         x += step;
+         } while (x != x1);
+      if ((x > 0) && (x < v->nx) && (y > 0) && (y < v->ny))
+         v->array[y][x] = intensity;
+      }
+   else {
+      slope = ((float) dy)/((float) dx);
+      if (dx > 0)
+         step = 1;
+      else
+         step = -1;
+      do {
+         y = y0 + slope*(x-x0);
+         if ((x > 0) && (x < v->nx) && (y > 0) && (y < v->ny))
+            v->array[y][x] = intensity;
+         x += step;
+         } while (x != x1);
+      y = y0 + slope*(x-x0);
+      if ((x > 0) && (x < v->nx) && (y > 0) && (y < v->ny))
+         v->array[y][x] = intensity;
+      }
+   }
+
+void fab_shade_path(struct fab_vars *v) {
+   //
+   // shade path into array
+   //
+   int x,y,x0,y0,x1,y1,intensity;
+   //
+   // set up and clear array
+   //
+   v->bit_depth = 8;
+   intensity = pow(2,8) - 1;
+   for (y = 0; y < v->ny; ++y)
+      for (x = 0; x < v->nx; ++x)
+         v->array[y][x] = 0;
+   v->path->segment = v->path->first;
+   while (1) {
+      //
+      // follow segments
+      //
+      v->path->segment->point = v->path->segment->first;
+      x0 = v->path->segment->point->first->value;
+      y0 = v->path->segment->point->first->next->value;
+      while (1) {
+         //
+         // follow points
+         //
+         if (v->path->segment->point->next == 0)
+            break;
+         v->path->segment->point = v->path->segment->point->next;
+         x1 = v->path->segment->point->first->value;
+         y1 = v->path->segment->point->first->next->value;
+         fab_shade_line(v,x0,y0,x1,y1,intensity);
+         x0 = x1;
+         y0 = y1;
+         }
+      if (v->path->segment->next == 0)
+         break;
+      v->path->segment = v->path->segment->next;
+      }
+   }
+
+void fab_shade_path_displace(struct fab_vars *v) {
+   //
+   // shade path into array with z displacement
+   //
+   int x,y,x0,y0,z0,x1,y1,z1,nz,intensity;
+   float scale;
+   //
+   // check for thickness
+   //
+   if (v->dz == 0) {
+      //
+      // no, draw 2D
+      //
+      fab_shade_path(v);
+      return;
+      }
+   //
+   // yes, enlarge and clear array for displacements
+   //
+   nz = v->nx * v->dz / v->dx;
+   scale = ((float) nz) / v->nz;
+   v->nx += nz;
+   v->ny += nz;
+   v->dx += v->dz;
+   v->dy += v->dz;
+   v->bit_depth = 8;
+   v->array = malloc(v->ny*sizeof(uint32_t *));
+   for (y = 0; y < v->ny; ++y)
+      v->array[y] = malloc(v->nx*sizeof(uint32_t));
+   for (y = 0; y < v->ny; ++y)
+      for (x = 0; x < v->nx; ++x)
+         v->array[y][x] = 0;
+   //
+   // follow path
+   //
+   v->path->segment = v->path->first;
+   while (1) {
+      //
+      // follow segments
+      //
+      v->path->segment->point = v->path->segment->first;
+      z0 = nz - scale * v->path->segment->point->first->next->next->value;
+      y0 = z0 + v->path->segment->point->first->next->value;
+      x0 = z0 + v->path->segment->point->first->value;
+      while (1) {
+         //
+         // follow points
+         //
+         if (v->path->segment->point->next == 0)
+            break;
+         v->path->segment->point = v->path->segment->point->next;
+         z1 = nz - scale * v->path->segment->point->first->next->next->value;
+         y1 = z1 + v->path->segment->point->first->next->value;
+         x1 = z1 + v->path->segment->point->first->value;
+         intensity = (z0 + z1)/2;
+         fab_shade_line(v,x0,y0,x1,y1,intensity);
+         x0 = x1;
+         y0 = y1;
+         z0 = z1;
+         }
+      if (v->path->segment->next == 0)
+         break;
+      v->path->segment = v->path->segment->next;
+      }
+   }
+
+int fab_row(struct fab_vars *v, float units, int Y, float value) {
+    //
+    // return array row
+    //
+    value = 0.5f + (v->ny - 1) * units * (value - v->mesh->min[Y]) / v->dy;
+    return (value);
+}
+int fab_col(struct fab_vars *v, float units, int X, float value) {
+    //
+    // return array col
+    //
+    value = 0.5f + (v->nx - 1) * units * (value - v->mesh->min[X]) / v->dx;
+    return value;
+}
+
+int fab_height(struct fab_vars *v, int S, int Z, float value) {
+    //
+    // return array height
+    //
+    value = ((1-S)/2) * 65535 + S * 65535 * (value - v->mesh->min[Z]) / (v->mesh->max[Z] - v->mesh->min[Z]);
+    return value;
+}
+
+void fab_shade_triangle(struct fab_vars *v, float units, int S, int X, int Y, int Z) {
+    //
+    // shade triangle intro array
+    //
+    float slope;
+    int xmin,ymin,zmin,x1,y1,z1,x2,y2,z2;
+    int x20,z20,x21,z21,x10,z10,z;
+    int i,j;
+    int dir;
+    int temp;
+    #define fab_swap(a,b) temp=a; a=b; b=temp;
+    //
+    // find coords
+    //
+    xmin = fab_col(v, units, X, v->mesh->triangle->v0[X]);
+    ymin = fab_row(v, units, Y, v->mesh->triangle->v0[Y]);
+    zmin = fab_height(v, S, Z, v->mesh->triangle->v0[Z]);
+    x1 = fab_col(v, units, X, v->mesh->triangle->v1[X]);
+    y1 = fab_row(v, units, Y, v->mesh->triangle->v1[Y]);
+    z1 = fab_height(v, S, Z, v->mesh->triangle->v1[Z]);
+    x2 = fab_col(v, units, X, v->mesh->triangle->v2[X]);
+    y2 = fab_row(v, units, Y, v->mesh->triangle->v2[Y]);
+    z2 = fab_height(v, S, Z, v->mesh->triangle->v2[Z]);
+    //
+    // check normal if needs to be drawn
+    //
+    if ((S*((x1-xmin)*(y1-y2)-(x1-x2)*(y1-ymin))) >= 0)
+        return;
+    //
+    // sort heights
+    //
+    if (y1 > y2) {
+        fab_swap(x1,x2);
+        fab_swap(y1,y2);
+        fab_swap(z1,z2);
+    }
+    if (ymin > y1) {
+        fab_swap(xmin,x1);
+        fab_swap(ymin,y1);
+        fab_swap(zmin,z1);
+    }
+    if (y1 > y2) {
+        fab_swap(x1,x2);
+        fab_swap(y1,y2);
+        fab_swap(z1,z2);
+    }
+    //
+    // check orientation
+    //
+    if (x1 < (xmin+((x2-xmin)*(y1-ymin))/(y2-ymin)))
+        dir = 1;
+    else
+        dir = -1;
+    //
+    // draw
+    //
+    if (y2 != y1) {
+        for (i = y2; i >= y1; --i) {
+            x21 = x1 + ((x2-x1)*(i-y1))/(y2-y1);
+            z21 = z1 + ((z2-z1)*(i-y1))/(y2-y1);
+            x20 = xmin + ((x2-xmin)*(i-ymin))/(y2-ymin);
+            z20 = zmin + ((z2-zmin)*(i-ymin))/(y2-ymin);
+            if (x21 != x20)
+                slope = (z20-z21)/((float) (x20-x21));
+            else
+                slope = 0;
+            j = x21 - dir;
+            while (j != x20) {
+                j += dir;
+                z = z21 + slope*(j-x21);
+                if (z > v->array[v->ny-1-i][j])
+                    v->array[v->ny-1-i][j] = z;
+            }
+        }
+    }
+    if (y1 != ymin) {
+        for (i = y1; i >= ymin; --i) {
+            x10 = xmin + ((x1-xmin)*(i-ymin))/(y1-ymin);
+            z10 = zmin + ((z1-zmin)*(i-ymin))/(y1-ymin);
+            x20 = xmin + ((x2-xmin)*(i-ymin))/(y2-ymin);
+            z20 = zmin + ((z2-zmin)*(i-ymin))/(y2-ymin);
+            if (x10 != x20)
+                slope = (z20-z10)/((float) (x20-x10));
+            else
+                slope = 0;
+            j = x10 - dir;
+            while (j != x20) {
+                j += dir;
+                z = z10 + slope*(j-x10);
+                if (z > v->array[v->ny-1-i][j])
+                    v->array[v->ny-1-i][j] = z;
+            }
+        }
+    }
+}
+
+void fab_shade_mesh(struct fab_vars *v, float units, float resolution, char axis) {
+    //
+    // shade triangle mesh into 16-bit array
+    //
+    int X,Y,Z,S;
+    int i; 
+    //
+    // allocate array
+    //
+    if (axis == 'x') {
+      X = 1; Y = 2; Z = 0; S = 1;
+      }
+    else if (axis == 'X') {
+      X = 1; Y = 2; Z = 0; S = -1;
+      }
+    if (axis == 'y') {
+      X = 2; Y = 0; Z = 1; S = 1;
+      }
+    else if (axis == 'Y') {
+      X = 2; Y = 0; Z = 1; S = -1;
+      }
+    if (axis == 'z') {
+      X = 0; Y = 1; Z = 2; S = 1;
+      }
+    else if (axis == 'Z') {
+      X = 0; Y = 1; Z = 2; S = -1;
+      }
+    v->dx = units * (v->mesh->max[X]-v->mesh->min[X]);
+    v->dy = units * (v->mesh->max[Y]-v->mesh->min[Y]);
+    v->dz = units * (v->mesh->max[Z]-v->mesh->min[Z]);
+    v->nx = resolution * v->dx;
+    v->ny = resolution * v->dy;
+    v->nz = 1;
+    v->xmin = units * v->mesh->min[X];
+    v->ymin = units * v->mesh->min[Y];
+    v->zmin = units * v->mesh->min[Z];
+    v->bit_depth = 16;
+    //
+    // alloc and init
+    //
+    v->array = malloc(v->ny*sizeof(uint32_t *));
+    for (i = 0; i < v->ny; ++i) {
+      v->array[i] = malloc(v->nx*sizeof(uint32_t));
+      memset(v->array[i],'\0',v->nx*sizeof(uint32_t));
+      }
+    //
+    // draw triangles into array
+    //
+    v->mesh->triangle = v->mesh->first;
+    while (v->mesh->triangle != 0) {
+      fab_shade_triangle(v, units, S, X, Y, Z);
+      v->mesh->triangle = v->mesh->triangle->next;
+      }
+   }
+
+//
+// array operations
+//
+
+void fab_png_array(struct fab_vars *v) {
+   //
+   // copy PNG image to array
+   //
+   int intensity,value,x,y;
+   int bit, byte;
+   png_byte *ptr;
+   png_colorp palette;
+   png_color color;
+   //png_uint_32 num_palette;
+   int num_palette;
+   
+   if (png_get_bit_depth(v->png_ptr, v->info_ptr) == 1) {
+      if (png_get_color_type(v->png_ptr, v->info_ptr) == PNG_COLOR_TYPE_PALETTE)
+         for (y = 0; y < v->ny; ++y)
+            for (x = 0; x < v->nx; ++x) {
+               byte = x / 8;
+               bit = 7 - x % 8;
+               ptr = &(v->row_pointers[y][byte]);
+               value = (ptr[0] >> bit) & 1;
+               png_get_PLTE(v->png_ptr, v->info_ptr, &palette, &num_palette);
+               color = palette[value];
+               intensity = (color.red + color.green + color.blue)/3.0;
+         	   v->array[y][x] = intensity;
+         	   }
+      else if (png_get_color_type(v->png_ptr, v->info_ptr) == PNG_COLOR_TYPE_GRAY)
+         for (y = 0; y < v->ny; ++y)
+            for (x = 0; x < v->nx; ++x) {
+               byte = x / 8;
+               bit = 7 - x % 8;
+         		ptr = &(v->row_pointers[y][byte]);
+               value = (ptr[0] >> bit) & 1;
+         	   v->array[y][x] = value;
+         	   }
+      else {
+         printf("fab.c: oops 3 -- image type not supported\n");
+         exit(-1);
+         }
+      }
+   else if (png_get_bit_depth(v->png_ptr, v->info_ptr) == 8) {
+      if (png_get_color_type(v->png_ptr, v->info_ptr) == PNG_COLOR_TYPE_GRAY)
+         for (y = 0; y < v->ny; ++y)
+            for (x = 0; x < v->nx; ++x) {
+         		ptr = &(v->row_pointers[y][x*1]);
+         	   v->array[y][x] = ptr[0];
+         	   }
+      else if (png_get_color_type(v->png_ptr, v->info_ptr) == PNG_COLOR_TYPE_GRAY_ALPHA)
+         for (y = 0; y < v->ny; ++y)
+            for (x = 0; x < v->nx; ++x) {
+         		ptr = &(v->row_pointers[y][x*2]);
+         	   v->array[y][x] = (int) ((1.0-ptr[1]/255.0)*255 + (0.0+(ptr[1]/255.0))*ptr[0]);
+         	   }
+      else if (png_get_color_type(v->png_ptr, v->info_ptr) == PNG_COLOR_TYPE_RGB)
+         for (y = 0; y < v->ny; ++y)
+            for (x = 0; x < v->nx; ++x) {
+         		ptr = &(v->row_pointers[y][x*3]);
+         	   v->array[y][x] = (int) ((ptr[0] + ptr[1] + ptr[2])/3.0);
+         	   }
+      else if (png_get_color_type(v->png_ptr, v->info_ptr) == PNG_COLOR_TYPE_RGB_ALPHA)
+         for (y = 0; y < v->ny; ++y)
+            for (x = 0; x < v->nx; ++x) {
+         		ptr = &(v->row_pointers[y][x*4]);
+         	   v->array[y][x] = (int) ((1.0-ptr[3]/255.0)*255 + (0.0+(ptr[3]/255.0))*((ptr[0] + ptr[1] + ptr[2])/3.0));
+         	   }
+      else if (png_get_color_type(v->png_ptr, v->info_ptr) == PNG_COLOR_TYPE_PALETTE) {
+         for (y = 0; y < v->ny; ++y)
+            for (x = 0; x < v->nx; ++x) {
+         		value = v->row_pointers[y][x];
+               png_get_PLTE(v->png_ptr, v->info_ptr, &palette, &num_palette);
+               color = palette[value];
+               intensity = (color.red + color.green + color.blue)/3.0;
+         	   v->array[y][x] = intensity;
+         	   }
+         }
+      else {
+         printf("fab.c: oops -- image type not supported\n");
+         exit(-1);
+         }
+      }
+   else if (png_get_bit_depth(v->png_ptr, v->info_ptr) == 16) {
+      if (png_get_color_type(v->png_ptr, v->info_ptr) == PNG_COLOR_TYPE_GRAY)
+         for (y = 0; y < v->ny; ++y)
+            for (x = 0; x < v->nx; ++x) {
+         		ptr = &(v->row_pointers[y][x*2]);
+         	   v->array[y][x] = (ptr[0] << 8) + ptr[1];
+         	   }
+      else {
+         printf("fab.c: oops -- image type not supported\n");
+         exit(-1);
+         }
+      }
+   else {
+      printf("fab.c: oops -- image type not supported\n");
+      exit(-1);
+      }
+   }
+
+void fab_rescale(struct fab_vars *v, float min, float max) {
+   //
+   // rescale array intensity
+   //
+   int x,y,top;
+   float low,high;
+   //
+   // find limits
+   //
+   top = pow(2,v->bit_depth) - 1;
+   low = top;
+   high = 0;
+   for (y = 0; y < v->ny; ++y) {
+      for (x = 0; x < v->nx; ++x) {
+         if (v->array[y][x] > high)
+            high = v->array[y][x];
+         if (v->array[y][x] < low)
+            low = v->array[y][x];
+         }
+      }
+   //
+   // rescale
+   //
+   for (y = 0; y < v->ny; ++y) {
+      for (x = 0; x < v->nx; ++x) {
+         v->array[y][x] = top*min + top*(max-min) * (v->array[y][x] - low) / (high - low);
+         }
+      }
+   }
+
+void add_edge(struct fab_vars *v, int* count, int y, int x) {
+	//
+	// set cell to interior edge
+	//
+	v->array[y][x] = v->interior | v->edge;
+	++(*count);
+}
+
+int fab_edges(struct fab_vars *v) {
+   //
+   // find edges of thresholded array
+   //
+   int x,y,sum,count;
+
+   count = 0;
+   for (x = 1; x < (v->nx-1); ++x)
+      if (v->array[0][x] & v->interior) {
+         sum = (v->array[0][x-1] & v->interior)
+            + (v->array[0][x+1] & v->interior)
+            + (v->array[1][x] & v->interior);
+         if (sum < 3)
+            add_edge(v, &count, 0,x);
+         }
+      else if (v->array[v->ny-1][x] & v->interior) {
+         sum = (v->array[v->ny-1][x-1] & v->interior)
+            + (v->array[v->ny-1][x+1] & v->interior)
+            + (v->array[v->ny-2][x] & v->interior);
+         if (sum < 3)
+            add_edge(v, &count, v->ny-1,x);
+         }
+   for (y = 1; y < (v->ny-1); ++y) {
+      if (v->array[y][0] & v->interior) {
+         sum = (v->array[y-1][0] & v->interior)
+            + (v->array[y+1][0] & v->interior)
+            + (v->array[y][1] & v->interior);
+         if (sum < 3)
+            add_edge(v, &count, y,0);
+         }
+      if (v->array[y][v->nx-1] & v->interior) {
+         sum = (v->array[y-1][v->nx-1] & v->interior)
+            + (v->array[y+1][v->nx-1] & v->interior)
+            + (v->array[y][v->nx-2] & v->interior);
+         if (sum < 3)
+            add_edge(v, &count, y,v->nx-1);
+         }
+      for (x = 1; x < (v->nx-1); ++x) {
+         if (v->array[y][x] & v->interior) {
+            sum = (v->array[y-1][x] & v->interior)
+               + (v->array[y+1][x] & v->interior)
+               + (v->array[y][x-1] & v->interior)
+               + (v->array[y][x+1] & v->interior);
+            if (sum < 4)
+               add_edge(v, &count, y,x);
+            }
+         }
+      }
+   return count;
+   }
+
+void fab_threshold(struct fab_vars *v, float intensity) {
+   //
+   // threshold array
+   //
+   int x,y;
+   float threshold;
+   threshold = (pow(2,v->bit_depth) - 1.0) * intensity;
+   for (y = 0; y < v->ny; ++y)
+      for (x = 0; x < v->nx; ++x)
+         if (v->array[y][x] >= threshold)
+            v->array[y][x] = v->interior;
+         else
+            v->array[y][x] = v->empty;
+   }
+
+int fab_distances_distance(struct fab_vars *v, int x, int y, int i) {
+   return ((y-i)*(y-i) + (v->g[i][x])*(v->g[i][x]));
+   }
+
+int fab_distances_intersection(struct fab_vars *v, int x, int y0, int y1) {
+   return ((0.0+(v->g[y0][x])*(v->g[y0][x])-(v->g[y1][x])*(v->g[y1][x])+y0*y0-y1*y1)/(2.0*(y0-y1)));
+   }
+
+void fab_distances(struct fab_vars *v) {
+   //
+   // find Euclidean distance to interior in a thresholded array
+   //
+   int x,y,closest,segment,newstart;
+   double d;
+   //
+   // allocate arrays if needed
+   //
+   if (v->g == 0) {
+      v->g = malloc(v->ny*sizeof(uint32_t *));
+      for (y = 0; y < v->ny; ++y)
+         v->g[y] = malloc(v->nx*sizeof(uint32_t));
+      }
+   if (v->h == 0) {
+      v->h = malloc(v->ny*sizeof(uint32_t *));
+      for (y = 0; y < v->ny; ++y)
+         v->h[y] = malloc(v->nx*sizeof(uint32_t));
+      }
+   if (v->distances == 0) {
+      v->distances = malloc(v->ny*sizeof(uint32_t *));
+      for (y = 0; y < v->ny; ++y)
+         v->distances[y] = malloc(v->nx*sizeof(uint32_t));
+      }
+   if (v->starts == 0)
+      v->starts = malloc(v->ny*sizeof(uint32_t *));
+   if (v->minimums == 0)
+      v->minimums = malloc(v->ny*sizeof(uint32_t *));
+   //
+   // column scan
+   //  
+   for (y = 0; y < v->ny; ++y) {
+      //
+      // right pass
+      //
+      closest = -(v->nx);
+      for (x = 0; x < v->nx; ++x) {
+         if (v->array[y][x] == v->interior) {
+            v->g[y][x] = 0;
+            closest = x;
+            }
+         else
+            v->g[y][x] = (x-closest);
+         }
+      //
+      // left pass
+      //
+      closest = 2*(v->nx);
+      for (x = (v->nx - 1); x >= 0; --x) {
+         if (v->array[y][x] == v->interior)
+            closest = x;
+         else {
+            d = (closest-x);
+            if (d < v->g[y][x])
+               v->g[y][x] = d;
+            }
+         }
+      }
+   //
+   // row scan
+   //
+   for (x = 0; x < v->nx; ++x) {
+      printf("\b\b\b\b\b\b\b\b   %d",x);
+      segment = 0;
+      v->starts[0] = 0;
+      v->minimums[0] = 0;
+      //
+      // down 
+      //
+      for (y = 1; y < v->ny; ++y) {
+         while ((segment >= 0) &&
+               (fab_distances_distance(v, x, v->starts[segment],v->minimums[segment])
+               > fab_distances_distance(v, x, v->starts[segment],y)))
+            segment -= 1;
+         if (segment < 0) {
+            segment = 0;
+            v->minimums[0] = y;
+            }
+         else {
+            newstart = 1 + fab_distances_intersection(v, x, v->minimums[segment],y);
+            if (newstart < v->ny) {
+               segment += 1;
+               v->minimums[segment] = y;
+               v->starts[segment] = newstart;
+               }
+            }
+         }
+      //
+      // up 
+      //
+      for (y = (v->ny - 1); y >= 0; --y) {
+         v->distances[y][x] = sqrt(fab_distances_distance(v, x, y, v->minimums[segment]));
+         if (y == v->starts[segment])
+            segment -= 1;
+         }
+      }
+   printf("\n");
+   }
+
+void test_fab_distances(struct fab_vars *v) {
+   //
+   // find Euclidean distance to interior in a thresholded array
+   //
+   int x,y;
+   //
+   // allocate arrays if needed
+   //
+   if (v->ddx == 0) {
+      v->ddx = malloc(v->ny*sizeof(uint32_t *));
+      for (y = 0; y < v->ny; ++y)
+         v->ddx[y] = malloc(v->nx*sizeof(uint32_t));
+      }
+   if (v->ddy == 0) {
+      v->ddy = malloc(v->ny*sizeof(uint32_t *));
+      for (y = 0; y < v->ny; ++y)
+         v->ddy[y] = malloc(v->nx*sizeof(uint32_t));
+      }
+   if (v->distances == 0) {
+      v->distances = malloc(v->ny*sizeof(uint32_t *));
+      for (y = 0; y < v->ny; ++y)
+         v->distances[y] = malloc(v->nx*sizeof(uint32_t));
+      }
+   //
+   // column scan
+   //  
+   for (y = 0; y < v->ny; ++y) {
+      printf("\b\b\b\b\b\b\b\b\b\b   y %d",y);
+      //
+      // right pass
+      //
+      v->ddx[y][0] = -1;
+      v->ddy[y][0] = 0;
+      for (x = 1; x < v->nx; ++x) {
+         v->ddy[y][x] = 0;
+         if (v->array[y][x] == v->interior)
+            v->ddx[y][x] = 0;
+         else
+            v->ddx[y][x] = v->ddx[y][x-1] - 1;
+         }
+      //
+      // left pass
+      //
+      v->ddx[y][v->nx-1] = 1;
+      for (x = (v->nx - 2); x >= 0; --x) {
+         if (abs(v->ddx[y][x]) > (abs(v->ddx[y][x+1])) + 1)
+            v->ddx[y][x] = v->ddx[y][x+1] + 1;
+         //v->distances[y][x] = sqrt(v->ddx[y][x]*v->ddx[y][x]+v->ddy[y][x]*v->ddy[y][x]);
+         }
+      }
+   printf("\n");
+   //
+   // row scan
+   //
+   for (x = 0; x < v->nx; ++x) {
+      printf("\b\b\b\b\b\b\b\b\b\b   x %d",x);
+      //
+      // down 
+      //
+      v->ddx[0][x] = 0;
+      v->ddy[0][x] = -1;
+      for (y = 1; y < v->ny; ++y) {
+         if ((v->ddx[y][x]*v->ddx[y][x]) > (v->ddx[y-1][x]*v->ddx[y-1][x] + (v->ddy[y-1][x]-1)*(v->ddy[y-1][x]-1))) {
+            v->ddx[y][x] = v->ddx[y-1][x];
+            v->ddy[y][x] = v->ddy[y-1][x]-1;
+            }
+         v->distances[y][x] = sqrt(v->ddx[y][x]*v->ddx[y][x]+v->ddy[y][x]*v->ddy[y][x]);
+         }
+      //
+      // up 
+      //
+      /*
+      v->ddx[v->ny-1][x] = 0;
+      v->ddy[v->ny-1][x] = 1;
+      for (y = (v->ny - 2); y >= 0; --y) {
+         if ((v->ddx[y][x]*v->ddx[y][x]+v->ddy[y][x]*v->ddy[y][x]) > (v->ddx[y+1][x]*v->ddx[y+1][x] + (abs(v->ddy[y+1][x])+1)*(abs(v->ddy[y+1][x])+1))) {
+            v->ddx[y][x] = v->ddx[y+1][x];
+            v->ddy[y][x] = v->ddy[y+1][x]+1;
+            }
+         //v->distances[y][x] = sqrt(v->ddx[y][x]*v->ddx[y][x]+v->ddy[y][x]*v->ddy[y][x]);
+         }
+      */
+      }
+   printf("\n");
+   }
+
+int fab_offset(struct fab_vars *v, float distance) {
+   //
+   // use distances to offset array
+   //    real units, assumes square pixels
+   //    returns remaining points
+   //
+   int x,y,remaining_count;
+   float image_distance;
+   image_distance = distance * v->nx/v->dx;
+   //
+   // loop over array
+   //
+   remaining_count = 0;
+   for (y = 0; y < v->ny; ++y) {
+      for (x = 0; x < v->nx; ++x) {
+         if (v->distances[y][x] <= image_distance)
+            v->array[y][x] = v->interior;
+         else {
+            v->array[y][x] = v->empty;
+            remaining_count += 1;
+            }
+         }
+      }
+   return remaining_count;
+   }
+
+
+int state(struct fab_vars *v, int y, int x) {
+    int nw, nn, ne, ww, cc, ee, sw, ss, se, state;
+    //
+    // assemble state string
+    //   0 = empty
+    //   1 = interior
+    //   2 = boundary
+    //
+    if ((y == 0) | (x == 0))
+        nw = 2;
+    else
+        nw = v->array[y-1][x-1] & 1; 
+    if (y == 0)
+        nn = 2;
+    else
+        nn = v->array[y-1][x] & 1;
+    if ((y == 0) | (x == (v->nx-1)))
+        ne = 2;
+    else
+        ne = v->array[y-1][x+1] & 1;
+    if (x == 0)
+        ww = 2;
+    else
+        ww = v->array[y][x-1] & 1;
+    cc = v->array[y][x] & 1;
+    if (x == (v->nx-1))
+        ee = 2;
+    else
+        ee = v->array[y][x+1] & 1;
+    if ((y == (v->ny-1)) | (x == 0))
+        sw = 2;
+    else
+        sw = v->array[y+1][x-1] & 1;
+    if (y == (v->ny-1))
+        ss = 2;
+    else
+        ss = v->array[y+1][x] & 1;
+    if ((y == (v->ny-1)) | (x == (v->nx-1)))
+        se = 2;
+    else
+        se = v->array[y+1][x+1] & 1;
+    state =
+        (nw <<  0) + (nn <<  2) + (ne <<  4) +
+        (ww <<  6) + (cc <<  8) + (ee << 10) +
+        (sw << 12) + (ss << 14) + (se << 16);
+    return state;
+}
+
+void add_rule(struct fab_vars *v, int *table, int nw, int nn, int ne,
+    int ww, int cc, int ee,int sw,int ss, int se, int rule) {
+        //
+        // add a CA rule, with rotations
+        //
+        int state;
+        //
+        // add the rule
+        //
+        state =
+            (nw <<  0) + (nn <<  2) + (ne <<  4) +
+            (ww <<  6) + (cc <<  8) + (ee << 10) +
+            (sw << 12) + (ss << 14) + (se << 16);
+        table[state] = rule;
+        //
+        // rotate 90 degrees
+        //
+        state =
+            (sw <<  0) + (ww <<  2) + (nw <<  4) +
+            (ss <<  6) + (cc <<  8) + (nn << 10) +
+            (se << 12) + (ee << 14) + (ne << 16);
+        if (rule == v->east)
+            table[state] = v->south;
+        else if (rule == v->south)
+            table[state] = v->west;
+        else if (rule == v->west)
+            table[state] = v->north;
+        else if (rule == v->north)
+            table[state] = v->east;
+        else if (rule == v->corner)
+            table[state] = v->corner;
+        else if (rule == v->stop)
+            table[state] = v->stop;
+        //
+        // rotate 180 degrees
+        // 
+        state =
+            (se <<  0) + (ss <<  2) + (sw <<  4) +
+            (ee <<  6) + (cc <<  8) + (ww << 10) +
+            (ne << 12) + (nn << 14) + (nw << 16);
+        if (rule == v->east)
+            table[state] = v->west;
+        else if (rule == v->south)
+            table[state] = v->north;
+        else if (rule == v->west)
+            table[state] = v->east;
+        else if (rule == v->north)
+            table[state] = v->south;
+        else if (rule == v->corner)
+            table[state] = v->corner;
+        else if (rule == v->stop)
+            table[state] = v->stop;
+        //
+        // rotate 270 degrees
+        //
+        state =
+            (ne <<  0) + (ee <<  2) + (se <<  4) +
+            (nn <<  6) + (cc <<  8) + (ss << 10) +
+            (nw << 12) + (ww << 14) + (sw << 16);
+        if (rule == v->east)
+            table[state] = v->north;
+        else if (rule == v->south)
+            table[state] = v->east;
+        else if (rule == v->west)
+            table[state] = v->south;
+        else if (rule == v->north)
+            table[state] = v->west;
+        else if (rule == v->corner)
+            table[state] = v->corner;
+        else if (rule == v->stop)
+            table[state] = v->stop;
+}
+
+void fab_directions(struct fab_vars *v) {
+   //
+   // find directions around interior edges (right-hand rule)
+   //
+   int x,y,rule,dir,*table;
+   table = calloc((pow(2,18)), sizeof(int));
+
+   // 
+   // build rule table
+   //
+   // 0 = empty
+   // 1 = interior
+   // 2 = boundary 
+   //
+   // 1 0:
+   //
+   // 011
+   // 111
+   // 111
+   add_rule(v, table,0,1,1,1,1,1,1,1,1,v->north);
+   // 101
+   // 111
+   // 111
+   add_rule(v, table,1,0,1,1,1,1,1,1,1,v->east);
+   //
+   // 2 0's:
+   //
+   // 001
+   // 111
+   // 111
+   add_rule(v, table,0,0,1,1,1,1,1,1,1,v->east);
+   // 100
+   // 111
+   // 111
+   add_rule(v, table,1,0,0,1,1,1,1,1,1,v->east);
+   // 010
+   // 111
+   // 111
+   add_rule(v, table,0,1,0,1,1,1,1,1,1,v->east);
+   // 011
+   // 110
+   // 111
+   add_rule(v, table,0,1,1,1,1,0,1,1,1,v->south);
+   // 110
+   // 011
+   // 111
+   add_rule(v, table,1,1,0,0,1,1,1,1,1,v->east);
+   // 101
+   // 011
+   // 111
+   add_rule(v, table,1,0,1,0,1,1,1,1,1,v->east);
+   // 101
+   // 110
+   // 111
+   add_rule(v, table,1,0,1,1,1,0,1,1,1,v->south);
+   // 011
+   // 111
+   // 110
+   add_rule(v, table,0,1,1,1,1,1,1,1,0,v->corner);
+   // 011
+   // 111
+   // 101
+   add_rule(v, table,0,1,1,1,1,1,1,0,1,v->north);
+   // 110
+   // 111
+   // 101
+   add_rule(v, table,1,1,0,1,1,1,1,0,1,v->west);
+   // 101
+   // 111
+   // 110
+   add_rule(v, table,1,0,1,1,1,1,1,1,0,v->south);
+   // 101
+   // 111
+   // 011
+   add_rule(v, table,1,0,1,1,1,1,0,1,1,v->east);
+   //
+   // 3 0's:
+   //
+   // 001
+   // 011
+   // 111
+   add_rule(v, table,0,0,1,0,1,1,1,1,1,v->east);
+   // 010
+   // 011
+   // 111
+   add_rule(v, table,0,1,0,0,1,1,1,1,1,v->east);
+   // 010
+   // 110
+   // 111
+   add_rule(v, table,0,1,0,1,1,0,1,1,1,v->south);
+   // 010
+   // 111
+   // 011
+   add_rule(v, table,0,1,0,1,1,1,0,1,1,v->east);
+   // 010
+   // 111
+   // 110
+   add_rule(v, table,0,1,0,1,1,1,1,1,0,v->south);
+   // 110
+   // 011
+   // 011
+   add_rule(v, table,1,1,0,0,1,1,0,1,1,v->east);
+   // 011
+   // 110
+   // 110
+   add_rule(v, table,0,1,1,1,1,0,1,1,0,v->south);
+   // 101
+   // 011
+   // 011
+   add_rule(v, table,1,0,1,0,1,1,0,1,1,v->east);
+   // 101
+   // 110
+   // 110
+   add_rule(v, table,1,0,1,1,1,0,1,1,0,v->south);
+   // 011
+   // 011
+   // 011;
+   add_rule(v, table,0,1,1,0,1,1,0,1,1,v->north);
+   //
+   // 4 0's:
+   //
+   // 001
+   // 011
+   // 011
+   add_rule(v, table,0,0,1,0,1,1,0,1,1,v->east);
+   // 100
+   // 110
+   // 110
+   add_rule(v, table,1,0,0,1,1,0,1,1,0,v->south);
+   // 010
+   // 011
+   // 011
+   add_rule(v, table,0,1,0,0,1,1,0,1,1,v->east);
+   // 010
+   // 110
+   // 110
+   add_rule(v, table,0,1,0,1,1,0,1,1,0,v->south);
+   // 001
+   // 110
+   // 110
+   add_rule(v, table,0,0,1,1,1,0,1,1,0,v->south);
+   // 100
+   // 011
+   // 011
+   add_rule(v, table,1,0,0,0,1,1,0,1,1,v->east);
+   //
+   // 5 0's:
+   //
+   // 000 
+   // 011
+   // 011
+   add_rule(v, table,0,0,0,0,1,1,0,1,1,v->east);
+   //
+   // boundary states
+   //
+   // 200
+   // 211
+   // 211
+   add_rule(v, table,2,0,0,2,1,1,2,1,1,v->east);
+   // 201
+   // 211
+   // 211
+   add_rule(v, table,2,0,1,2,1,1,2,1,1,v->east);
+   // 210
+   // 211
+   // 211
+   add_rule(v, table,2,1,0,2,1,1,2,1,1,v->east);
+   // 002
+   // 112
+   // 112
+   add_rule(v, table,0,0,2,1,1,2,1,1,2,v->stop);
+   // 102
+   // 112
+   // 112
+   add_rule(v, table,1,0,2,1,1,2,1,1,2,v->stop);
+   // 002
+   // 112
+   // 102
+   add_rule(v, table,0,0,2,1,1,2,1,0,2,v->stop);
+   // 012
+   // 112
+   // 112
+   add_rule(v, table,0,1,2,1,1,2,1,1,2,v->stop);
+   // 012
+   // 112
+   // 102
+   add_rule(v, table,0,1,2,1,1,2,1,0,2,v->stop);
+   for (y = 0; y < v->ny; ++y) {
+      for (x = 0; x < v->nx; ++x) {
+         if (v->array[y][x] & v->interior) {
+            v->array[y][x] = v->interior;
+            rule = state(v,y,x);
+            dir = table[rule];
+            v->array[y][x] |= dir;
+            }
+         }
+      }
+
+   free(table); 
+   }
+
+//
+// mesh operations
+//
+
+void fab_mesh_path(struct fab_vars *v, float units, float resolution) {
+   //
+   // convert mesh to path
+   //
+
+   //
+   // set up path
+   //
+   fab_path_start(v,3);
+   v->dx = units * (v->mesh->max[0]-v->mesh->min[0]);
+   v->dy = units * (v->mesh->max[1]-v->mesh->min[1]);
+   v->dz = units * (v->mesh->max[2]-v->mesh->min[2]);
+   v->nx = resolution * v->dx;
+   v->ny = resolution * v->dy;
+   v->nz = resolution * v->dz;
+   v->xmin = units * v->mesh->min[0];
+   v->ymin = units * v->mesh->min[1];
+   v->zmin = units * v->mesh->min[2];
+   v->bit_depth = 16;
+   //
+   // loop over mesh
+   //
+   v->mesh->triangle = v->mesh->first;
+   while (v->mesh->triangle != 0) {
+      fab_path_segment(v);
+      fab_path_point(v);
+      fab_path_axis(v,(v->nx)*(v->mesh->triangle->v0[0] - v->mesh->min[0])/(v->dx));
+      fab_path_axis(v,(v->ny)*(v->mesh->max[1] - v->mesh->triangle->v0[1])/(v->dy));
+      fab_path_axis(v,(v->nz)*(v->mesh->triangle->v0[2] - v->mesh->min[2])/(v->dz));
+      fab_path_point(v);
+      fab_path_axis(v,(v->nx)*(v->mesh->triangle->v1[0] - v->mesh->min[0])/(v->dx));
+      fab_path_axis(v,(v->ny)*(v->mesh->max[1] - v->mesh->triangle->v1[1])/(v->dy));
+      fab_path_axis(v,(v->nz)*(v->mesh->triangle->v1[2] - v->mesh->min[2])/(v->dz));
+      fab_path_point(v);
+      fab_path_axis(v,(v->nx)*(v->mesh->triangle->v2[0] - v->mesh->min[0])/(v->dx));
+      fab_path_axis(v,(v->ny)*(v->mesh->max[1] - v->mesh->triangle->v2[1])/(v->dy));
+      fab_path_axis(v,(v->nz)*(v->mesh->triangle->v2[2] - v->mesh->min[2])/(v->dz));
+      fab_path_point(v);
+      fab_path_axis(v,(v->nx)*(v->mesh->triangle->v0[0] - v->mesh->min[0])/(v->dx));
+      fab_path_axis(v,(v->ny)*(v->mesh->max[1] - v->mesh->triangle->v0[1])/(v->dy));
+      fab_path_axis(v,(v->nz)*(v->mesh->triangle->v0[2] - v->mesh->min[2])/(v->dz));
+      v->mesh->triangle = v->mesh->triangle->next;
+      }
+   }
+
+//
+// path operations
+//
+
+void fab_path_start(struct fab_vars *v, int dof) {
+   //
+   // start a new path
+   //
+   v->path = malloc(sizeof(struct fab_path_type));
+   v->path->first = 0;
+   v->path->dof = dof;
+   }
+
+void fab_path_segment(struct fab_vars *v) {
+   //
+   // add a segment to the current path
+   //
+	v->path->last = malloc(sizeof(struct fab_path_segment_type));
+   v->path->last->previous = 0;
+   v->path->last->next = 0;
+   v->path->last->first = 0;
+   if (v->path->first == 0) {
+      //
+      // first segment
+      //
+      v->path->first = v->path->last;
+      v->path->segment = v->path->last;
+      }
+   else {
+      //
+      // not first segment
+      //
+      v->path->last->previous = v->path->segment;
+      v->path->segment->next = v->path->last;
+      v->path->segment = v->path->last;
+      }
+   }
+
+void fab_path_point(struct fab_vars *v) {
+   //
+   // add a point to the current path segment
+   //
+   v->path->segment->last =
+      malloc(sizeof(struct fab_path_point_type));
+   v->path->segment->last->previous = 0;
+   v->path->segment->last->next = 0;
+   v->path->segment->last->first = 0;
+   if (v->path->segment->first == 0) {
+      //
+      // first point
+      //
+      v->path->segment->first = v->path->segment->last;
+      v->path->segment->point = v->path->segment->last;
+      }
+   else {
+      //
+      // not first point
+      //
+      v->path->segment->last->previous = v->path->segment->point;
+      v->path->segment->point->next = v->path->segment->last;
+      v->path->segment->point = v->path->segment->last;
+      }
+   }
+
+void fab_path_axis(struct fab_vars *v, int value) {
+   //
+   // add an axis to the current path segment point
+   //
+   v->path->segment->point->last =
+      malloc(sizeof(struct fab_path_axis_type));
+   v->path->segment->point->last->previous = 0;
+   v->path->segment->point->last->next = 0;
+   v->path->segment->point->last->value = value;
+   if (v->path->segment->point->first == 0) {
+      //
+      // first axis
+      //
+      v->path->segment->point->first = v->path->segment->point->last;
+      v->path->segment->point->axis = v->path->segment->point->last;
+      }
+   else {
+      //
+      // not first axis
+      //
+      v->path->segment->point->last->previous = v->path->segment->point->axis;
+      v->path->segment->point->axis->next = v->path->segment->point->last;
+      v->path->segment->point->axis = v->path->segment->point->last;
+      }
+   }
+
+void fab_path_join(struct fab_vars *vin1, struct fab_vars *vin2, struct fab_vars *vout, float dx, float dy) {
+   //
+   // join paths, checking limits
+   //
+   float xf,yf,zf;
+   int xi,yi,zi;
+   float xmin,ymin,zmin,xmax,ymax,zmax,dpmm;
+   xmin = ((dx + vin1->xmin) < vin2->xmin) ? (dx + vin1->xmin) : vin2->xmin;
+   ymin = ((-dy + vin1->ymin) < vin2->ymin) ? (-dy + vin1->ymin) : vin2->ymin;
+   zmin = (vin1->zmin < vin2->zmin) ? vin1->zmin : vin2->zmin;
+   xmax = ((dx + vin1->xmin + vin1->dx) > (vin2->xmin + vin2->dx)) ? (dx + vin1->xmin + vin1->dx) : (vin2->xmin + vin2->dx);
+   ymax = ((-dy + vin1->ymin + vin1->dy) > (vin2->ymin + vin2->dy)) ? (-dy + vin1->ymin + vin1->dy) : (vin2->ymin + vin2->dy);
+   zmax = ((vin1->zmin + vin1->dz) > (vin2->zmin + vin2->dz)) ? (vin1->zmin + vin1->dz) : (vin2->zmin + vin2->dz);
+   dpmm = ((vin1->nx/vin1->dx) > (vin2->nx/vin2->dx)) ? (vin1->nx/vin1->dx) : (vin2->nx/vin2->dx);
+   vout->xmin = xmin;
+   vout->ymin = ymin;
+   vout->zmin = zmin;
+   vout->dx = (xmax-xmin);
+   vout->dy = (ymax-ymin);
+   vout->dz = (zmax-zmin);
+   vout->nx = dpmm * vout->dx;
+   vout->ny = dpmm * vout->dy;
+   vout->nz = dpmm * vout->dz;
+   fab_path_start(vout,vin1->path->dof);
+   //
+   // copy first path
+   //
+   vin1->path->segment = vin1->path->first;
+   while (1) {
+      //
+      // follow segments
+      //
+      fab_path_segment(vout);
+      vin1->path->segment->point = vin1->path->segment->first;
+      while (1) {
+         //
+         // follow points
+         //
+         fab_path_point(vout);
+         xf = dx + vin1->xmin + vin1->dx * vin1->path->segment->point->first->value / (vin1->nx - 1.0);
+         xi =  (vout->nx - 1) * (xf - vout->xmin) / vout->dx;
+         fab_path_axis(vout,xi);
+         yf = -dy + vin1->ymin + vin1->dy * vin1->path->segment->point->first->next->value / (vin1->ny - 1.0);
+         yi =  (vout->ny - 1) * (yf - vout->ymin) / vout->dy;
+         fab_path_axis(vout,yi);
+         if (vin1->path->dof == 3) {
+            if (vin1->nz == 1)
+               zi = 0;
+            else {
+               zf = vin1->zmin + vin1->dz * vin1->path->segment->point->first->next->next->value / (vin1->nz - 1.0);
+               zi = (vout->nz - 1) * (zf - vout->zmin) / vout->dz;
+               }
+            fab_path_axis(vout,zi);
+            }
+         if (vin1->path->segment->point->next == 0)
+            break;
+         vin1->path->segment->point = vin1->path->segment->point->next;
+         }
+      if (vin1->path->segment->next == 0)
+         break;
+      vin1->path->segment = vin1->path->segment->next;
+      }
+   //
+   // copy second path
+   //
+   vin2->path->segment = vin2->path->first;
+   while (1) {
+      //
+      // follow segments
+      //
+      fab_path_segment(vout);
+      vin2->path->segment->point = vin2->path->segment->first;
+      while (1) {
+         //
+         // follow points
+         //
+         fab_path_point(vout);
+         xf = vin2->xmin + vin2->dx * vin2->path->segment->point->first->value / (vin2->nx - 1.0);
+         xi = (vout->nx - 1) * (xf - vout->xmin) / vout->dx;
+         fab_path_axis(vout,xi);
+         yf = vin2->ymin + vin2->dy * vin2->path->segment->point->first->next->value / (vin2->ny - 1.0);
+         yi = (vout->ny - 1) * (yf - vout->ymin) / vout->dy;
+         fab_path_axis(vout,yi);
+         if (vin2->path->dof == 3) {
+            if (vin2->nz == 1)
+               zi = 0;
+            else {
+               zf = vin2->zmin + vin2->dz * vin2->path->segment->point->first->next->next->value / (vin2->nz - 1.0);
+               zi = (vout->nz - 1) * (zf - vout->zmin) / vout->dz;
+               }
+            fab_path_axis(vout,zi);
+            }
+         if (vin2->path->segment->point->next == 0)
+            break;
+         vin2->path->segment->point = vin2->path->segment->point->next;
+         }
+      if (vin2->path->segment->next == 0)
+         break;
+      vin2->path->segment = vin2->path->segment->next;
+      }
+   }
+
+void fab_path_cat(struct fab_vars *vin1, struct fab_vars *vin2, struct fab_vars *vout) {
+   //
+   // concatenate paths, copying limits
+   //
+   vout->xmin = vin1->xmin;
+   vout->ymin = vin1->ymin;
+   vout->zmin = vin1->zmin;
+   vout->dx = vin1->dx;
+   vout->dy = vin1->dy;
+   vout->dz = vin1->dz;
+   vout->nx = vin1->nx;
+   vout->ny = vin1->ny;
+   vout->nz = vin1->nz;
+   fab_path_start(vout,vin1->path->dof);
+   //
+   // copy first path
+   //
+   vin1->path->segment = vin1->path->first;
+   while (1) {
+      //
+      // follow segments
+      //
+      fab_path_segment(vout);
+      vin1->path->segment->point = vin1->path->segment->first;
+      while (1) {
+         //
+         // follow points
+         //
+         fab_path_point(vout);
+         fab_path_axis(vout,vin1->path->segment->point->first->value);
+         fab_path_axis(vout,vin1->path->segment->point->first->next->value);
+         if (vin1->path->dof > 2)
+            fab_path_axis(vout,vin1->path->segment->point->first->next->next->value);
+         if (vin1->path->segment->point->next == 0)
+            break;
+         vin1->path->segment->point = vin1->path->segment->point->next;
+         }
+      if (vin1->path->segment->next == 0)
+         break;
+      vin1->path->segment = vin1->path->segment->next;
+      }
+   //
+   // copy second path
+   //
+   vin2->path->segment = vin2->path->first;
+   while (1) {
+      //
+      // follow segments
+      //
+      fab_path_segment(vout);
+      vin2->path->segment->point = vin2->path->segment->first;
+      while (1) {
+         //
+         // follow points
+         //
+         fab_path_point(vout);
+         fab_path_axis(vout,vin2->path->segment->point->first->value);
+         fab_path_axis(vout,vin2->path->segment->point->first->next->value);
+         if (vin2->path->dof > 2)
+            fab_path_axis(vout,vin2->path->segment->point->first->next->next->value);
+         if (vin2->path->segment->point->next == 0)
+            break;
+         vin2->path->segment->point = vin2->path->segment->point->next;
+         }
+      if (vin2->path->segment->next == 0)
+         break;
+      vin2->path->segment = vin2->path->segment->next;
+      }
+   }
+
+void fab_path_append(struct fab_vars *vin, struct fab_vars *vout) {
+   //
+   // append vin to vout, copying limits
+   //
+   vin->path->segment = vin->path->first;
+   vout->path->segment = vout->path->last;
+   while (1) {
+      //
+      // follow segments
+      //
+      fab_path_segment(vout);
+      vin->path->segment->point = vin->path->segment->first;
+      while (1) {
+         //
+         // follow points
+         //
+         fab_path_point(vout);
+         fab_path_axis(vout,vin->path->segment->point->first->value);
+         fab_path_axis(vout,vin->path->segment->point->first->next->value);
+         if (vin->path->dof > 2)
+            fab_path_axis(vout,vin->path->segment->point->first->next->next->value);
+         if (vin->path->segment->point->next == 0)
+            break;
+         vin->path->segment->point = vin->path->segment->point->next;
+         }
+      if (vin->path->segment->next == 0)
+         break;
+      vin->path->segment = vin->path->segment->next;
+      }
+   }
+
+void fab_path_array(struct fab_vars *vin, struct fab_vars *vout, int nx, int ny, float dx, float dy) {
+   //
+   // array path
+   //
+   int i,j,x,y,z,dnx,dny;
+   dnx = dx * vin->nx / vin->dx;
+   dny = dy * vin->ny / vin->dy;
+   vout->dx = nx * vin->dx + (nx-1) * dx;
+   vout->dy = ny * vin->dy + (ny-1) * dy;
+   vout->dz = vin->dz;
+   vout->nx = nx * vin->nx + (nx-1) * dnx;
+   vout->ny = ny * vin->ny + (ny-1) * dny;
+   vout->nz = vin->nz;
+   vout->xmin = vin->xmin;
+   vout->ymin = vin->ymin;
+   vout->zmin = vin->zmin;
+   fab_path_start(vout,vin->path->dof);
+   for (i = 0; i < nx; ++i) {
+      for (j = 0; j < ny; ++j) {
+         vin->path->segment = vin->path->first;
+         while (1) {
+            //
+            // follow segments
+            //
+            fab_path_segment(vout);
+            vin->path->segment->point = vin->path->segment->first;
+            while (1) {
+               //
+               // follow points
+               //
+               fab_path_point(vout);
+               x = i*(vin->nx + dnx) + vin->path->segment->point->first->value;
+               fab_path_axis(vout,x);
+               y = j*(vin->ny + dny) + vin->path->segment->point->first->next->value;
+               fab_path_axis(vout,y);
+               if (vin->path->dof == 3) {
+                  z = vin->path->segment->point->first->next->next->value;
+                  fab_path_axis(vout,z);
+                  }
+               if (vin->path->segment->point->next == 0)
+                  break;
+               vin->path->segment->point = vin->path->segment->point->next;
+               }
+            if (vin->path->segment->next == 0)
+               break;
+            vin->path->segment = vin->path->segment->next;
+            }
+         }
+      }
+   }
+
+void fab_follow(struct fab_vars *v, float error, int y, int x, int z) {
+    //
+    // create path by following edge directions at x,y
+    //   error is allowable deviation before stroking (in pixel units)
+    //   z is layer height (ignored for 2D paths)
+    //
+    int xnew=0,ynew=0;
+    float xmean,ymean;
+    float dx,dy,nx,ny,d;
+    int xcur,ycur,xstart,ystart,xseg,yseg,xsum,ysum,nsum;
+    char last_direction;
+    //
+    // don't start on a corner state
+    //
+    if (((v->array[y][x] & v->direction) == v->corner)
+        || ((v->array[y][x] & v->direction) == v->corner2))
+        return;
+    //
+    // otherwise start segment
+    //
+    xstart = x;
+    ystart = y;
+    xcur = x;
+    ycur = y;
+    xseg = x;
+    yseg = y;
+    xsum = 0;
+    ysum = 0;
+    nsum = 0;
+    last_direction = '?';
+    fab_path_segment(v);
+    //
+    // add first point
+    //
+    fab_path_point(v);
+    fab_path_axis(v,xstart);
+    fab_path_axis(v,ystart);
+    if (v->path->dof > 2)
+       fab_path_axis(v,z);
+    while (1) {
+		//
+		// accumulate current point
+		//
+		xsum += xcur;
+		ysum += ycur;
+		nsum += 1;
+		xmean = xsum / ((float) nsum);
+		ymean = ysum / ((float) nsum);
+		dx = (xmean - xseg);
+		dy = (ymean - yseg);
+		d = sqrt(dx*dx + dy*dy);
+		nx = dy/d;
+		ny = -dx/d;
+		//
+		// follow current direction
+		//
+		if ((v->array[ycur][xcur] & v->direction) == v->north) {
+			xnew = xcur;
+			ynew = ycur - 1;
+			last_direction = 'n'; // remember direction
+			v->array[ycur][xcur] &= ~v->direction; // clear direction
+		}
+		else if ((v->array[ycur][xcur] & v->direction) == v->south) {
+			xnew = xcur;
+			ynew = ycur + 1;
+			last_direction = 's'; // remember direction
+			v->array[ycur][xcur] &= ~v->direction; // clear direction
+		}
+		else if ((v->array[ycur][xcur] & v->direction) == v->east) {
+			xnew = xcur + 1;
+			ynew = ycur;
+			last_direction = 'e'; // remember direction
+			v->array[ycur][xcur] &= ~v->direction; // clear direction
+		}
+		else if ((v->array[ycur][xcur] & v->direction) == v->west) {
+			xnew = xcur - 1;
+			ynew = ycur;
+			last_direction = 'w'; // remember direction
+			v->array[ycur][xcur] &= ~v->direction; // clear direction
+		}
+		else if ((v->array[ycur][xcur] & v->direction) == v->corner) {
+			if (last_direction == 'n') {
+				xnew = xcur - 1;
+				ynew = ycur;
+				last_direction = 'w';
+			}
+			else if (last_direction == 'e') {
+				xnew = xcur;
+				ynew = ycur - 1;
+				last_direction = 'n';
+			}
+			else if (last_direction == 's') {
+				xnew = xcur + 1;
+				ynew = ycur;
+				last_direction = 'e';
+			}
+			else if (last_direction == 'w') {
+				xnew = xcur;
+				ynew = ycur + 1;
+				last_direction = 's';
+			}
+			v->array[ycur][xcur] &= ~v->direction; // set direction to corner2
+			v->array[ycur][xcur] |= v->corner2;
+		}
+		else if ((v->array[ycur][xcur] & v->direction) == v->corner2) {
+			if (last_direction == 'n') {
+				xnew = xcur - 1;
+				ynew = ycur;
+				last_direction = 'w';
+			}
+			else if (last_direction == 'e') {
+				xnew = xcur;
+				ynew = ycur - 1;
+				last_direction = 'n';
+			}
+			else if (last_direction == 's') {
+				xnew = xcur + 1;
+				ynew = ycur;
+				last_direction = 'e';
+			}
+			else if (last_direction == 'w') {
+				xnew = xcur;
+				ynew = ycur + 1;
+				last_direction = 's';
+			}
+			v->array[ycur][xcur] &= ~v->direction; // clear direction
+		}
+		else if ((v->array[ycur][xcur] & v->direction) == 0) {
+			printf("fab.c: oops -- no direction\n");
+			exit(-1);
+		}
+		else {
+			printf("fab.c: oops -- unknown direction = %d\n",
+				v->array[ycur][xcur] & v->direction);
+			exit(-1);
+		}
+		//
+		// finish segment and return if back to start, or stop reached
+		//
+		if (((xnew == xstart) && (ynew == ystart)) ||
+			((v->array[ynew][xnew] & v->direction) == v->stop)) {
+				fab_path_point(v);
+				fab_path_axis(v,xnew);
+				fab_path_axis(v,ynew);
+            if (v->path->dof > 2)
+				   fab_path_axis(v,z);
+				break;
+		}
+		//
+		// check segment at new point
+		//
+		d = fabs(nx*(xnew-xseg) + ny*(ynew-yseg));
+		if (d >= error) {
+			//
+			// exceeds error, add current point to path
+			//
+			fab_path_point(v);
+			fab_path_axis(v,xcur);
+			fab_path_axis(v,ycur);
+         if (v->path->dof > 2)
+			   fab_path_axis(v,z);
+			xsum = 0;
+			ysum = 0;
+			nsum = 0;
+			xseg = xcur;
+			yseg = ycur;
+		}
+		//
+		// move to new point
+		//
+		xcur = xnew;
+		ycur = ynew;
+	}
+}
+
+void fab_vectorize(struct fab_vars *v, float error, int z) {
+   //
+   // vectorize xy edge directions
+   //    error is transverse distance from segment mean
+   //       in lattice units
+   //    z is layer height (ignored for 2D paths)
+   //
+   int x, y;
+   //
+   // find edge starts
+   //
+   for (x = 0; x < v->nx; ++x)
+      if ((v->array[0][x] & v->direction) == v->south)
+         fab_follow(v, error, 0,x, z);
+   for (y = 0; y < v->ny; ++y)
+      if ((v->array[y][v->nx-1] & v->direction) == v->west)
+         fab_follow(v, error, y,v->nx-1, z);
+   for (x = (v->nx - 1); x >= 0 ; --x)
+      if ((v->array[v->ny-1][x] & v->direction) == v->north)
+         fab_follow(v, error, v->ny-1,x, z);
+   for (y = (v->ny - 1); y >= 0; --y)
+      if ((v->array[y][0] & v->direction) == v->east)
+         fab_follow(v, error, y,0, z);
+   //
+   // find remaining interior paths
+   //
+   for (y = 1; y < (v->ny - 1); ++y) {
+      if ((y % 2) == 1) {
+         for (x = 1; x < (v->nx - 1); ++x) {
+            if (v->array[y][x] & v->direction) {
+               fab_follow(v,error,y,x,z);
+               }
+            }
+         }
+      else {
+         for (x = (v->nx - 2); x > 0; --x) {
+            if (v->array[y][x] & v->direction) {
+               fab_follow(v,error,y,x,z);
+               }
+            }
+         }
+      }
+   }
+
+void fab_halftone(struct fab_vars *v, float threshold, int points, float size, float spacing, float offset, int invert) {
+   //
+   // halftone array to path
+   //
+   int xmin,ymin,x,y,n,i,space,shift,top;
+   float pi,angle,r;
+   top = pow(2,v->bit_depth) - 1;
+   //
+   // start 2D path
+   //
+   fab_path_start(v,2);
+   //
+   // loop over spots
+   //
+   pi = 4.0*atan(1.0);
+   angle = 2.0*pi/(points-1.0);
+   n = v->nx * (0.5*size/v->dx);
+   space = spacing*2*n;
+   shift = 0;
+   for (ymin = n; ymin < (v->ny - n); ymin += space) {
+      for (xmin = n; xmin < (v->nx - n); xmin += space) {
+         r = n * (invert*top + (1-2*invert)* v->array[ymin][xmin]) / ((float) top);
+         //
+         // if spot radius larger than threshold, add to path
+         //
+         if (r >= threshold) {
+            fab_path_segment(v);
+            for (i = 0; i < points; ++i) {
+               x = shift + 0.5 + xmin + r*cos(i*angle);
+               y = 0.5 + ymin + r*sin(i*angle);
+               fab_path_point(v);
+               fab_path_axis(v,x);
+               fab_path_axis(v,y);
+               }
+            }
+         }
+      shift += offset * space;
+      if (shift >= space)
+         shift -= space;
+      }
+   }
+
diff --git a/src/core/fab.h b/src/core/fab.h
new file mode 100644
index 0000000..36dbf5e
--- /dev/null
+++ b/src/core/fab.h
@@ -0,0 +1,150 @@
+//
+// fab.h
+//    fab modules header
+//
+// Neil Gershenfeld 7/4/13
+// (c) Massachusetts Institute of Technology 2013
+//
+// This work may be reproduced, modified, distributed,
+// performed, and displayed for any purpose, but must
+// acknowledge the fab modules project. Copyright is
+// retained and must be preserved. The work is provided
+// as is; no warranty is provided, and users accept all 
+// liability.
+//
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#define fab_big 1e10
+
+//
+// data structures
+//
+
+struct fab_vars {
+   unsigned char empty;
+   unsigned char interior;
+   unsigned char edge;
+   unsigned char north;
+   unsigned char south;
+   unsigned char east;
+   unsigned char west;
+   unsigned char stop;
+   unsigned char corner;
+   unsigned char corner2;
+   unsigned char direction;
+   unsigned int nx,ny,nz;
+   unsigned int bit_depth;
+   unsigned int word_size;
+   double dx,dy,dz;
+   double xmin,ymin,zmin;
+   uint32_t **array;
+   uint32_t **distances,**g,**h; // for distance transform
+   uint32_t *starts,*minimums; // "
+   uint32_t **ddx,**ddy; // testing
+   struct fab_path_type *path;
+   struct fab_mesh_type *mesh;
+   png_bytep *row_pointers;
+   png_structp png_ptr;
+   png_infop info_ptr;
+   };
+
+struct fab_path_type {
+   struct fab_path_segment_type *first,*segment,*last;
+   int dof;
+   };
+
+struct fab_path_segment_type {
+   struct fab_path_segment_type *previous,*next;
+   struct fab_path_point_type *first,*point,*last;
+   };
+
+struct fab_path_point_type {
+   struct fab_path_point_type *previous,*next;
+   struct fab_path_axis_type *first,*axis,*last;
+   };
+
+struct fab_path_axis_type {
+   struct fab_path_axis_type *previous,*next;
+   int value;
+   };
+
+struct fab_mesh_type {
+   struct fab_mesh_triangle_type *first,*triangle,*last;
+   float min[3];
+   float max[3];
+   int number;
+   };
+
+struct fab_mesh_triangle_type {
+   struct fab_mesh_triangle_type *previous,*next;
+   float normal[3],v0[3],v1[3],v2[3];
+   int attribute;
+   };
+
+//
+// function prototypes
+//
+
+//
+// initialization
+//
+void init_vars(struct fab_vars *v);
+//
+// input
+//
+void fab_read_stl(struct fab_vars *v, char *input_file_name);
+//void fab_read_svg(struct fab_vars *v, char *input_file_name);
+void fab_read_png(struct fab_vars *v, char *input_file_name);
+void fab_read_array(struct fab_vars *v, char *input_file_name);
+void fab_read_path(struct fab_vars *v, char *input_file_name);
+//
+// output
+//
+void fab_write_png_K(struct fab_vars *v, char *output_file_name);
+void fab_write_array(struct fab_vars *v, char *output_file_name);
+void fab_write_path(struct fab_vars *v, char *output_file_name);
+//
+// shading
+//
+void fab_shade_states(struct fab_vars *v);
+void fab_shade_line(struct fab_vars *v, int x0, int y0, int x1, int y1, int intensity);
+void fab_shade_path(struct fab_vars *v);
+void fab_shade_path_displace(struct fab_vars *v);
+void fab_shade_mesh(struct fab_vars *v, float units, float resolution, char axis);
+void fab_shade_triangle(struct fab_vars *v, float units, int S, int X, int Y, int Z);
+//
+// array operations
+//
+void fab_png_array(struct fab_vars *v);
+void fab_rescale(struct fab_vars *v, float min, float max);
+int fab_edges(struct fab_vars *v);
+void fab_threshold(struct fab_vars *v, float threshold);
+void fab_distances(struct fab_vars *v);
+int fab_offset(struct fab_vars *v, float distance);
+void fab_directions(struct fab_vars *v);
+//
+// mesh operations
+//
+void fab_mesh_path(struct fab_vars *v, float units, float resolution);
+//
+// path operations
+//
+void fab_follow(struct fab_vars *v, float error, int y, int x, int z);
+void fab_path_start(struct fab_vars *v, int dof);
+void fab_path_segment(struct fab_vars *v);
+void fab_path_point(struct fab_vars *v);
+void fab_path_axis(struct fab_vars *v, int value);
+void fab_path_join(struct fab_vars *vin1, struct fab_vars *vin2, struct fab_vars *vout, float dx, float dy);
+void fab_path_cat(struct fab_vars *vin1, struct fab_vars *vin2, struct fab_vars *vout);
+void fab_path_append(struct fab_vars *vin1, struct fab_vars *vin2);
+void fab_path_array(struct fab_vars *vin, struct fab_vars *vout, int nx, int ny, float dx, float dy);
+void fab_vectorize(struct fab_vars *v, float error, int z);
+void fab_halftone(struct fab_vars *v, float threshold, int points, float size, float spacing, float offset, int invert);
diff --git a/src/core/gif_info.c b/src/core/gif_info.c
new file mode 100644
index 0000000..a7efaae
--- /dev/null
+++ b/src/core/gif_info.c
@@ -0,0 +1,120 @@
+//
+// gif_info.c
+//    report .gif info
+//
+// Neil Gershenfeld 3/24/14
+// (c) Massachusetts Institute of Technology 2013
+//
+// This work may be reproduced, modified, distributed,
+// performed, and displayed for any purpose, but must
+// acknowledge the fab modules project. Copyright is
+// retained and must be preserved. The work is provided
+// as is; no warranty is provided, and users accept all 
+// liability.
+//
+
+#define MAX_LINE 10000
+
+#include "fab.h"
+
+int main(int argc, char **argv) {
+   //
+   // local vars
+   //
+   GifFileType *GIFfile;
+   GifRecordType GIFtype;
+   GifByteType *GIFextension;
+   GifPixelType *GIFline;
+   int x,y,i,n,imin,imax;
+   int image_width,image_height,image_count,color_resolution,GIFcode,ret;
+   float voxel_size;
+   char comment[256];
+   struct fab_vars v;
+   init_vars(&v);
+   //
+   // command line args
+   //
+   if (!(argc == 2)) {
+      printf("command line: gif_info in.gif\n");
+      printf("   in.gif = input GIF file\n");
+      exit(-1);
+      }
+   voxel_size = -1;
+   image_width = -1;
+   image_height = -1;
+   image_count = -1;
+   //
+   // scan the file 
+   //
+   printf("read %s\n",argv[1]);
+   color_resolution = -1;
+   GIFfile = DGifOpenFileName(argv[1]);
+   if (GIFfile == NULL) {
+      printf("gif_info: oops -- can not open %s\n",argv[1]);
+      exit(-1);
+      }
+   GIFline = malloc(MAX_LINE*sizeof(GifPixelType));
+   imin = 256;
+   imax = 0;
+   do {
+      DGifGetRecordType(GIFfile,&GIFtype);
+      switch (GIFtype) {
+         case IMAGE_DESC_RECORD_TYPE:
+            DGifGetImageDesc(GIFfile);
+            image_width = GIFfile->SWidth;
+            image_height = GIFfile->SHeight;
+            image_count = GIFfile->ImageCount;
+            color_resolution = GIFfile->SColorResolution;
+            for (y = 0; y < GIFfile->SHeight; ++y) {
+               ret = DGifGetLine(GIFfile,GIFline,GIFfile->SWidth);
+               if (ret != GIF_OK) {
+                  printf("gif_info: oops -- error reading line\n");
+                  exit(-1);
+                  }
+               for (x = 0; x < GIFfile->SWidth; ++x) {
+                  if (GIFline[x] < imin) imin = GIFline[x];
+                  if (GIFline[x] > imax) imax = GIFline[x];
+                  }
+               }
+            break;
+         case EXTENSION_RECORD_TYPE:
+            DGifGetExtension(GIFfile,&GIFcode,&GIFextension);
+            if (GIFcode == COMMENT_EXT_FUNC_CODE) {
+               n = GIFextension[0];
+               for (i = 1; i <= n; ++i)
+                  comment[i-1] = GIFextension[i];
+               comment[n] = 0;
+               if (voxel_size == -1)
+                  sscanf(comment,"mm per pixel: %f;",&voxel_size);
+               }
+            while (GIFextension != NULL)
+               DGifGetExtensionNext(GIFfile,&GIFextension);
+            break;
+         case SCREEN_DESC_RECORD_TYPE:
+            DGifGetScreenDesc(GIFfile);
+            break;
+         case TERMINATE_RECORD_TYPE:
+            break;
+         case UNDEFINED_RECORD_TYPE:
+            printf("gif_info: oops -- undefined GIF record type\n");
+            exit(-1);
+            break;
+         }
+      } while (GIFtype != TERMINATE_RECORD_TYPE);
+   if (GIFfile == NULL) {
+      printf("gif_info: oops -- can not open %s\n",argv[1]);
+      exit(-1);
+      }
+   if (voxel_size == -1) {
+      voxel_size = 1.0;
+      printf("   no pixel size found, assuming 1 mm\n");
+      }
+   printf("   voxel size (mm): %f, color resolution (bits): %d\n",voxel_size,color_resolution);
+   printf("   intensity min: %d max: %d\n",imin,imax);
+   printf("   number of images: %d, image width %d, image height %d\n",image_count,image_width,image_height);
+   //
+   // exit
+   //
+   DGifCloseFile(GIFfile);
+   exit(0);
+   }
diff --git a/src/core/gif_png.c b/src/core/gif_png.c
new file mode 100644
index 0000000..e915c58
--- /dev/null
+++ b/src/core/gif_png.c
@@ -0,0 +1,346 @@
+//
+// gif_png.c
+//    .gif to .png
+//
+// Neil Gershenfeld 9/20/13
+// (c) Massachusetts Institute of Technology 2013
+//
+// This work may be reproduced, modified, distributed,
+// performed, and displayed for any purpose, but must
+// acknowledge the fab modules project. Copyright is
+// retained and must be preserved. The work is provided
+// as is; no warranty is provided, and users accept all 
+// liability.
+//
+
+#define MAX_LINE 10000
+#define BIG 1e10
+
+#include "fab.h"
+
+int main(int argc, char **argv) {
+   //
+   // local vars
+   //
+   GifFileType *GIFfile;
+   GifRecordType GIFtype;
+   GifByteType *GIFextension;
+   GifPixelType *GIFline;
+   uint32_t **lower_array,**upper_array;
+   double **image_array;
+   double f,fmin,fmax;
+   int x,y,z,h,i,j,k,n,p;
+   int imin,imax,image_width,image_height,image_count,color_resolution,GIFcode,ret;
+   float pixel_size,arg,threshold,rx,ry,rz;
+   char type,comment[256];
+   struct fab_vars v;
+   init_vars(&v);
+   //
+   // command line args
+   //
+   if (!((argc == 3) || (argc == 4) || (argc == 5) || (argc == 6) || (argc == 7) || (argc == 10))) {
+      printf("command line: gif_png in.gif out.png [type [arg [points [size [rx ry rz]]]]]\n");
+      printf("   in.gif = input gif file\n");
+      printf("   out.png = output PNG file\n");
+      printf("   type = 'z' of density, 'h' for height (default z)\n");
+      printf("   arg = type argument\n");
+      printf("      'z': gamma (default 1)\n");
+      printf("      'h': threshold (0 = min, 1 = max, default 0.5)\n");
+      printf("   points = points to interpolate per point (linear, default 0)\n");
+      printf("   size = voxel size (mm, default from file))\n");
+      printf("   to be implemented: rx,ry,rz = x,y,z rotation angles (degrees; default 0)\n");
+      exit(-1);
+      }
+   type = 'z';
+   p = 0;
+   rx = ry = rz = 0;
+   pixel_size = -1;
+   image_width = -1;
+   image_height = -1;
+   if (argc >= 4) {
+      sscanf(argv[3],"%c",&type);
+      if (!((type == 'z') || (type == 'h'))) {
+         printf("gif_png: oops -- type must be 'z' or 'h'\n");
+         exit(-1);
+         }
+      }
+   if (type == 'z') arg = 1.0;
+   else if (type == 'h') arg = 0.5;
+   if (argc >= 5)
+      sscanf(argv[4],"%f",&arg);
+   if (argc >= 6)
+      sscanf(argv[5],"%d",&p);
+   if (argc >= 7)
+      sscanf(argv[6],"%f",&pixel_size);
+   if (argc >= 10) {
+      sscanf(argv[7],"%f",&rx);
+      sscanf(argv[8],"%f",&ry);
+      sscanf(argv[9],"%f",&rz);
+      }
+   //
+   // scan the file 
+   //
+   printf("read %s\n",argv[1]);
+   imin = 256;
+   imax = 0;
+   color_resolution = -1;
+   GIFfile = DGifOpenFileName(argv[1]);
+   if (GIFfile == NULL) {
+      printf("gif_png: oops -- can not open %s\n",argv[1]);
+      exit(-1);
+      }
+   GIFline = malloc(MAX_LINE*sizeof(GifPixelType));
+   do {
+      DGifGetRecordType(GIFfile,&GIFtype);
+      switch (GIFtype) {
+         case IMAGE_DESC_RECORD_TYPE:
+            DGifGetImageDesc(GIFfile);
+            image_width = GIFfile->SWidth;
+            image_height = GIFfile->SHeight;
+            image_count = GIFfile->ImageCount;
+            color_resolution = GIFfile->SColorResolution;
+            for (y = 0; y < GIFfile->SHeight; ++y) {
+               ret = DGifGetLine(GIFfile,GIFline,GIFfile->SWidth);
+               if (ret != GIF_OK) {
+                  printf("gif_png: oops -- error reading line\n");
+                  exit(-1);
+                  }
+               for (x = 0; x < GIFfile->SWidth; ++x) {
+                  if (GIFline[x] < imin) imin = GIFline[x];
+                  if (GIFline[x] > imax) imax = GIFline[x];
+                  }
+               }
+            break;
+         case EXTENSION_RECORD_TYPE:
+            DGifGetExtension(GIFfile,&GIFcode,&GIFextension);
+            if (GIFcode == COMMENT_EXT_FUNC_CODE) {
+               n = GIFextension[0];
+               for (i = 1; i <= n; ++i)
+                  comment[i-1] = GIFextension[i];
+               comment[n] = 0;
+               if (pixel_size == -1)
+                  sscanf(comment,"mm per pixel: %f;",&pixel_size);
+               }
+            while (GIFextension != NULL)
+               DGifGetExtensionNext(GIFfile,&GIFextension);
+            break;
+         case SCREEN_DESC_RECORD_TYPE:
+            DGifGetScreenDesc(GIFfile);
+            break;
+         case TERMINATE_RECORD_TYPE:
+            break;
+         case UNDEFINED_RECORD_TYPE:
+            printf("gif_png: oops -- undefined GIF record type\n");
+            exit(-1);
+            break;
+         }
+      } while (GIFtype != TERMINATE_RECORD_TYPE);
+   if (GIFfile == NULL) {
+      printf("gif_png: oops -- can not open %s\n",argv[1]);
+      exit(-1);
+      }
+   if (pixel_size == -1) {
+      pixel_size = 1.0;
+      printf("   no pixel size found, assuming 1 mm\n");
+      }
+   printf("   pixel size (mm): %f, color resolution (bits): %d\n",pixel_size,color_resolution);
+   printf("   intensity min: %d max: %d\n",imin,imax);
+   printf("   number of images: %d, image width %d, image height %d\n",image_count,image_width,image_height);
+   //
+   // set threshold
+   //
+   if (type == 'h')
+      threshold = imin + arg*(imax-imin);
+   //
+   // check and set limits
+   //
+   v.nx = image_width + (image_width-1)*p;
+   v.ny = image_height + (image_height-1)*p;
+   v.nz = image_count + (image_count-1)*p;
+   v.dx = v.nx*pixel_size;
+   v.dy = v.ny*pixel_size;
+   v.dz = v.nz*pixel_size;
+   v.xmin = 0;
+   v.ymin = 0;
+   v.zmin = 0;
+   //
+   // allocate arrays
+   //
+   lower_array = malloc(image_height*sizeof(uint32_t *));
+   for (y = 0; y < image_height; ++y) {
+      lower_array[y] = malloc(image_width*sizeof(uint32_t));
+      for (x = 0; x < image_width; ++x)
+         lower_array[y][x] = 0;
+      }
+   upper_array = malloc(image_height*sizeof(uint32_t *));
+   for (y = 0; y < image_height; ++y) {
+      upper_array[y] = malloc(image_width*sizeof(uint32_t));
+      for (x = 0; x < image_width; ++x)
+         upper_array[y][x] = 0;
+      }
+   image_array = malloc(v.ny*sizeof(double *));
+   for (y = 0; y < v.ny; ++y) {
+      image_array[y] = malloc(v.nx*sizeof(double));
+      for (x = 0; x < v.nx; ++x)
+         image_array[y][x] = 0;
+      }
+   v.array = malloc(v.ny*sizeof(uint32_t *));
+   for (y = 0; y < v.ny; ++y) {
+      v.array[y] = malloc(v.nx*sizeof(uint32_t));
+      for (x = 0; x < v.nx; ++x)
+         v.array[y][x] = 0;
+      }
+   //
+   // read the file
+   //
+   DGifCloseFile(GIFfile);
+   GIFfile = DGifOpenFileName(argv[1]);
+   if (GIFfile == NULL) {
+      printf("gif_png: oops -- can not open %s\n",argv[1]);
+      exit(-1);
+      }
+   z = 0;
+   do {
+      DGifGetRecordType(GIFfile,&GIFtype);
+      switch (GIFtype) {
+         case IMAGE_DESC_RECORD_TYPE:
+            //
+            // read image
+            //
+            DGifGetImageDesc(GIFfile);
+            printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b   layer = %d",z);
+            if (z == 0) {
+               //
+               // read first layer
+               //
+               for (y = 0; y < image_height; ++y) {
+                  ret = DGifGetLine(GIFfile,GIFline,GIFfile->SWidth);
+                  if (ret != GIF_OK) {
+                     printf("gif_png: oops -- error reading first line\n");
+                     exit(-1);
+                     }
+                  for (x = 0; x < image_width; ++x)
+                     upper_array[y][x] = GIFline[x];
+                  }
+               }
+            else {
+               //
+               // read next layer
+               //
+               for (y = 0; y < image_height; ++y) {
+                  ret = DGifGetLine(GIFfile,GIFline,GIFfile->SWidth);
+                  if (ret != GIF_OK) {
+                     printf("gif_png: oops -- error reading line\n");
+                     exit(-1);
+                     }
+                  for (x = 0; x < image_width; ++x) {
+                     lower_array[y][x] = upper_array[y][x];
+                     upper_array[y][x] = GIFline[x];
+                     }
+                  }
+               if (p == 0) {
+                  //
+                  // no interpolation, loop over layer voxels
+                  //
+                  for (x = 0; x < (image_width-1); ++x) {
+                     for (y = 0; y < (image_height-1); ++y) {
+                        if (type == 'z') {
+                           image_array[y][x] += lower_array[y][x];
+                           }
+                        else if (type == 'h') {
+                           if ((lower_array[y][x] >= threshold) && (upper_array[y][x] < threshold))
+                              image_array[y][x] = z + (lower_array[y][x]-threshold)/(lower_array[y][x]-upper_array[y][x]);
+                           }
+                        }
+                     }
+                  }
+               else {
+                  //
+                  // yes interpolation, loop over layer sub-voxels
+                  //
+                  for (x = 0; x < (image_width-1); ++x) {
+                     for (y = 0; y < (image_height-1); ++y) {
+                        for (i = 0; i <= p; ++i) {
+                           for (j = 0; j <= p; ++j) {
+                              for (k = 0; k <= p; ++k) {
+                                 f = lower_array[y][x]*((p+1.0-i)/(p+1.0))*((p+1.0-j)/(p+1.0))*((p+1.0-k)/(p+1.0))
+                                   + lower_array[y][x+1]*((i)/(p+1.0))*((p+1.0-j)/(p+1.0))*((p+1.0-k)/(p+1.0))
+                                   + lower_array[y+1][x]*((p+1.0-i)/(p+1.0))*((j)/(p+1.0))*((p+1.0-k)/(p+1.0))
+                                   + lower_array[y+1][x+1]*((i)/(p+1.0))*((j)/(p+1.0))*((p+1.0-k)/(p+1.0))
+                                   + upper_array[y][x]*((p+1.0-i)/(p+1.0))*((p+1.0-j)/(p+1.0))*((k)/(p+1.0))
+                                   + upper_array[y][x+1]*((i)/(p+1.0))*((p+1.0-j)/(p+1.0))*((k)/(p+1.0))
+                                   + upper_array[y+1][x]*((p+1.0-i)/(p+1.0))*((j)/(p+1.0))*((k)/(p+1.0))
+                                   + upper_array[y+1][x+1]*((i)/(p+1.0))*((j)/(p+1.0))*((k)/(p+1.0));
+                                 if (type == 'z') {
+                                    image_array[(1+p)*y+j][(1+p)*x+i] += f;
+                                    }
+                                 else if (type == 'h') {
+                                    h = (1+p)*z+k;
+                                    if ((f > threshold) && (h > image_array[(1+p)*y+j][(1+p)*x+i]))
+                                       image_array[(1+p)*y+j][(1+p)*x+i] = h;
+                                    }
+                                 }
+                              }
+                           }
+                        }
+                     }
+                  }
+               }
+            z += 1;
+            break;
+         case EXTENSION_RECORD_TYPE:
+            DGifGetExtension(GIFfile,&GIFcode,&GIFextension);
+            while (GIFextension != NULL)
+               DGifGetExtensionNext(GIFfile,&GIFextension);
+            break;
+         case SCREEN_DESC_RECORD_TYPE:
+            DGifGetScreenDesc(GIFfile);
+            break;
+         case TERMINATE_RECORD_TYPE:
+            break;
+         case UNDEFINED_RECORD_TYPE:
+            printf("gif_png: oops -- undefined GIF record type\n");
+            exit(-1);
+            break;
+         }
+      } while (GIFtype != TERMINATE_RECORD_TYPE);
+   printf("\n");
+   //
+   // scale image and copy to PNG array
+   //
+   if (type == 'z') {
+      fmin = BIG;
+      fmax = 0;
+      for (x = 0; x < v.nx; ++x) {
+         for (y = 0; y < v.ny; ++y) {
+            if (image_array[y][x] > fmax) fmax = image_array[y][x];
+            if (image_array[y][x] < fmin) fmin = image_array[y][x];
+            }
+         }
+      if (arg == 1) {
+         for (x = 0; x < v.nx; ++x)
+            for (y = 0; y < v.ny; ++y)
+               v.array[y][x] = 65536*(image_array[y][x]-fmin)/(fmax-fmin);
+         }
+      else {
+         for (x = 0; x < v.nx; ++x)
+            for (y = 0; y < v.ny; ++y)
+               v.array[y][x] = 65536*pow((image_array[y][x]-fmin)/(fmax-fmin),arg);
+         }
+      }
+   else if (type == 'h') {
+      for (x = 0; x < v.nx; ++x)
+         for (y = 0; y < v.ny; ++y)
+            v.array[y][x] = 65536*image_array[y][x]/(v.nz-1.0);
+      }
+   //
+   // write PNG
+   //
+   v.bit_depth = 16;
+   fab_write_png_K(&v,argv[2]);
+   //
+   // exit
+   //
+   DGifCloseFile(GIFfile);
+   exit(0);
+   }
diff --git a/src/core/gif_stl.c b/src/core/gif_stl.c
new file mode 100644
index 0000000..d3795c8
--- /dev/null
+++ b/src/core/gif_stl.c
@@ -0,0 +1,717 @@
+//
+// gif_stl.c
+//    .gif to .stl
+//
+// Neil Gershenfeld 12/11/13
+// (c) Massachusetts Institute of Technology 2013
+//
+// This work may be reproduced, modified, distributed,
+// performed, and displayed for any purpose, but must
+// acknowledge the fab modules project. Copyright is
+// retained and must be preserved. The work is provided
+// as is; no warranty is provided, and users accept all 
+// liability.
+//
+
+#define MAX_LINE 10000
+
+#include "fab.h"
+
+/*
+vertices:
+   ---
+   6 7
+   4 5
+   ---
+   2 3
+   0 1
+   ---
+edges:
+   ---
+    k
+   l j
+    i
+   ---
+   h g
+   e f
+   ---
+    c
+   d b
+    a
+   ---
+*/
+
+//
+// rotate_x
+//   rotate rule around x and add
+//
+void rotate_x(char rules[255][20], int *index) {
+   int i,b[8];
+   int old_index = *index;
+   for (i = 0; i < 8; ++i) b[i] = (*index & (1 << i)) >> i;
+   *index = 
+      (b[4] << 0)
+     +(b[5] << 1)
+     +(b[0] << 2)
+     +(b[1] << 3)
+     +(b[6] << 4)
+     +(b[7] << 5)
+     +(b[2] << 6)
+     +(b[3] << 7);
+   for (i
+    = 0; i < strlen(rules[old_index]); ++i) {
+      switch (rules[old_index][i]) {
+         case 'a': rules[*index][i] = 'c'; break;
+         case 'b': rules[*index][i] = 'g'; break;
+         case 'c': rules[*index][i] = 'k'; break;
+         case 'd': rules[*index][i] = 'h'; break;
+         case 'e': rules[*index][i] = 'd'; break;
+         case 'f': rules[*index][i] = 'b'; break;
+         case 'g': rules[*index][i] = 'j'; break;
+         case 'h': rules[*index][i] = 'l'; break;
+         case 'i': rules[*index][i] = 'a'; break;
+         case 'j': rules[*index][i] = 'f'; break;
+         case 'k': rules[*index][i] = 'i'; break;
+         case 'l': rules[*index][i] = 'e'; break;
+         case ' ': rules[*index][i] = ' '; break;
+         }
+      }
+   }
+//
+// rotate_y
+//   rotate rule around y and add
+//
+void rotate_y(char rules[255][20], int *index) {
+   int i,b[8];
+   int old_index = *index;
+   for (i = 0; i < 8; ++i) b[i] = (*index & (1 << i)) >> i;
+   *index = 
+      (b[1] << 0)
+     +(b[5] << 1)
+     +(b[3] << 2)
+     +(b[7] << 3)
+     +(b[0] << 4)
+     +(b[4] << 5)
+     +(b[2] << 6)
+     +(b[6] << 7);
+   for (i = 0; i < strlen(rules[old_index]); ++i) {
+      switch (rules[old_index][i]) {
+         case 'a': rules[*index][i] = 'e'; break;
+         case 'b': rules[*index][i] = 'd'; break;
+         case 'c': rules[*index][i] = 'h'; break;
+         case 'd': rules[*index][i] = 'l'; break;
+         case 'e': rules[*index][i] = 'i'; break;
+         case 'f': rules[*index][i] = 'a'; break;
+         case 'g': rules[*index][i] = 'c'; break;
+         case 'h': rules[*index][i] = 'k'; break;
+         case 'i': rules[*index][i] = 'f'; break;
+         case 'j': rules[*index][i] = 'b'; break;
+         case 'k': rules[*index][i] = 'g'; break;
+         case 'l': rules[*index][i] = 'j'; break;
+         case ' ': rules[*index][i] = ' '; break;
+         }
+      }
+   }
+//
+// rotate_z
+//   rotate rule around z and add
+//
+void rotate_z(char rules[255][20], int *index) {
+   int i,b[8];
+   int old_index = *index;
+   for (i = 0; i < 8; ++i) b[i] = (*index & (1 << i)) >> i;
+   *index = 
+      (b[2] << 0)
+     +(b[0] << 1)
+     +(b[3] << 2)
+     +(b[1] << 3)
+     +(b[6] << 4)
+     +(b[4] << 5)
+     +(b[7] << 6)
+     +(b[5] << 7);
+   for (i = 0; i < strlen(rules[old_index]); ++i) {
+      switch (rules[old_index][i]) {
+         case 'a': rules[*index][i] = 'b'; break;
+         case 'b': rules[*index][i] = 'c'; break;
+         case 'c': rules[*index][i] = 'd'; break;
+         case 'd': rules[*index][i] = 'a'; break;
+         case 'e': rules[*index][i] = 'f'; break;
+         case 'f': rules[*index][i] = 'g'; break;
+         case 'g': rules[*index][i] = 'h'; break;
+         case 'h': rules[*index][i] = 'e'; break;
+         case 'i': rules[*index][i] = 'j'; break;
+         case 'j': rules[*index][i] = 'k'; break;
+         case 'k': rules[*index][i] = 'l'; break;
+         case 'l': rules[*index][i] = 'i'; break;
+         case ' ': rules[*index][i] = ' '; break;
+         }
+      }
+   }
+//
+// print_rules
+//    print the rule table
+//
+void print_rules(char rules[255][20]) {
+   int i,b;
+   printf("76543210\n");
+   for (i = 0; i < 256; ++i) {
+      for (b = 7; b >= 0; --b) {
+         printf("%d",(i & (1 << b)) >> b);
+         }
+      printf(" %d %s\n",i,rules[i]);
+      }
+   }
+// add_rules
+//    add a rule and its variants to the table
+//
+void add_rules(char rules[255][20], int index, char *edges) {
+   int i,j,k,l;
+   strcpy(rules[index],edges);
+   for (i=0; i<4; ++i) {
+      for (j=0; j<4; ++j) {
+         for (k=0; k<4; ++k) {
+            rotate_x(rules,&index);
+            }
+         rotate_y(rules,&index);
+         }
+      rotate_z(rules,&index);
+      }
+   }
+//
+// init_rules
+//    create the rule table
+//
+void init_rules(char rules[255][20]) {
+   int i,j;
+   for (i = 0; i < 256; ++i)
+      for (j = 0; j < 20; ++j)
+         rules[i][j] = 0;
+   add_rules(rules,0b00000000,""); // 0
+   add_rules(rules,0b11111111,""); // ~0
+   add_rules(rules,0b00000001,"eda"); // 1
+   add_rules(rules,0b11111110,"ade"); // ~1
+   add_rules(rules,0b00000011,"fed dbf"); // 2
+   add_rules(rules,0b11111100,"def fbd"); // ~2
+   add_rules(rules,0b00100001,"eda jif"); // 3
+   add_rules(rules,0b11011110,"ade fij"); // ~3
+   add_rules(rules,0b10000001,"eda gkj"); // 4
+   add_rules(rules,0b01111110,"ade jkg"); // ~4
+   add_rules(rules,0b00001110,"fhg fdh fad"); // 5
+   add_rules(rules,0b11110001,"ghf hdf daf"); // ~5
+   add_rules(rules,0b10000011,"fed fdb gkj"); // 6
+   add_rules(rules,0b01111100,"def bdf jkg"); // ~6
+   add_rules(rules,0b10010010,"bfa ile gkj"); // 7
+   add_rules(rules,0b01101101,"afb eli jkg"); // ~7
+   add_rules(rules,0b00001111,"ehg feg"); // 8
+   add_rules(rules,0b11110000,"ghe gef"); // ~8
+   add_rules(rules,0b01001101,"elk eka akg agb"); // 9
+   add_rules(rules,0b10110010,"kle ake gka bga"); // ~9
+   add_rules(rules,0b10011001,"ild ida ckj cjb"); // 10
+   add_rules(rules,0b01100110,"dli adi jkc bjc"); // ~10
+   add_rules(rules,0b10001101,"hkj hja hae ajb"); // 11
+   add_rules(rules,0b01110010,"jkh ajh eah bja"); // ~11
+   add_rules(rules,0b00011110,"ile hgf hfd dfa"); // 12
+   add_rules(rules,0b11100001,"eli fgh dfh afd"); // ~12
+   add_rules(rules,0b01101001,"eda bcg lkh jif"); // 13
+   add_rules(rules,0b10010110,"ade gcb hkl fij"); // ~13
+   add_rules(rules,0b01001110,"lkg lga lad agf"); // 14
+   add_rules(rules,0b10110001,"gkl agl dal fga"); // ~14
+   }
+//
+// vertex
+//    add a triangle vertex
+//
+void vertex(char c, int x, int y, int z, float voxel_size, float t, uint32_t *w, float *array) {
+   switch(c) {
+      case 'a':
+         array[0] = x+(w[0]-t)/(w[0]-((float) w[1]));
+         array[1] = y;
+         array[2] = z;
+         break;
+      case 'b':
+         array[0] = x+1;
+         array[1] = y+(w[1]-t)/(w[1]-((float) w[3]));
+         array[2] = z;
+         break;
+      case 'c':
+         array[0] = x+(w[2]-t)/(w[2]-((float) w[3]));
+         array[1] = y+1;
+         array[2] = z;
+         break;
+      case 'd':
+         array[0] = x;
+         array[1] = y+(w[0]-t)/(w[0]-((float) w[2]));
+         array[2] = z;
+         break;
+      case 'e':
+         array[0] = x;
+         array[1] = y;
+         array[2] = z+(w[0]-t)/(w[0]-((float) w[4]));
+         break;
+      case 'f':
+         array[0] = x+1;
+         array[1] = y;
+         array[2] = z+(w[1]-t)/(w[1]-((float) w[5]));
+         break;
+      case 'g':
+         array[0] = x+1;
+         array[1] = y+1;
+         array[2] = z+(w[3]-t)/(w[3]-((float) w[7]));
+         break;
+      case 'h':
+         array[0] = x;
+         array[1] = y+1;
+         array[2] = z+(w[2]-t)/(w[2]-((float) w[6]));
+         break;
+      case 'i':
+         array[0] = x+(w[4]-t)/(w[4]-((float) w[5]));
+         array[1] = y;
+         array[2] = z+1;
+         break;
+      case 'j':
+         array[0] = x+1;
+         array[1] = y+(w[5]-t)/(w[5]-((float) w[7]));
+         array[2] = z+1;
+         break;
+      case 'k':
+         array[0] = x+(w[6]-t)/(w[6]-((float) w[7]));
+         array[1] = y+1;
+         array[2] = z+1;
+         break;
+      case 'l':
+         array[0] = x;
+         array[1] = y+(w[4]-t)/(w[4]-((float) w[6]));
+         array[2] = z+1;
+         break;
+      }
+      array[0] = voxel_size*array[0];
+      array[1] = voxel_size*array[1];
+      array[2] = voxel_size*array[2];
+   }
+//
+// triangulate
+//    triangulate voxel at lattice site x,y,z with vertex weights w
+//
+void triangulate(int x, int y, int z, float voxel_size, float t, uint32_t *w, char rules[255][20], struct fab_vars *v) {
+   int i,index;
+   char c;
+   //
+   // set index code
+   //
+   index = ((w[0] >= t) ? 0 : 1)
+         + ((w[1] >= t) ? 0 : 2)
+         + ((w[2] >= t) ? 0 : 4)
+         + ((w[3] >= t) ? 0 : 8)
+         + ((w[4] >= t) ? 0 : 16)
+         + ((w[5] >= t) ? 0 : 32)
+         + ((w[6] >= t) ? 0 : 64)
+         + ((w[7] >= t) ? 0 : 128);
+   //
+   // add triangles for rule
+   //
+   i = 0;
+   while (1) {
+      //
+      // loop over rule chars
+      //
+      c = rules[index][i];
+      if (c == 0)
+         //
+         // end of rule
+         //
+         break;
+      else if (c == ' ') {
+         //
+         // space between rules
+         //
+         i += 1;
+         continue;
+         }
+      else {
+         //
+         // create triangle for rule
+         //
+         v->mesh->last = malloc(sizeof(struct fab_mesh_triangle_type));
+         v->mesh->last->previous = v->mesh->triangle;
+         v->mesh->triangle->next = v->mesh->last;
+         v->mesh->triangle = v->mesh->last;
+         v->mesh->triangle->next = 0;
+         v->mesh->triangle->attribute = 0;
+         v->mesh->triangle->normal[0] = v->mesh->triangle->normal[1] = v->mesh->triangle->normal[2] = 0;
+         //
+         // add vertices for rule
+         //
+         c = rules[index][i];
+         vertex(c,x,y,z,voxel_size,t,w,v->mesh->triangle->v0);
+         i += 1;
+         c = rules[index][i];
+         vertex(c,x,y,z,voxel_size,t,w,v->mesh->triangle->v1);
+         i += 1;
+         c = rules[index][i];
+         vertex(c,x,y,z,voxel_size,t,w,v->mesh->triangle->v2);
+         i += 1;
+         }
+      }
+   }
+
+void fab_write_stl(struct fab_vars *v, char *output_file_name) {
+   //
+   // write mesh as STL
+   //
+   FILE *output_file;
+   char buf[80];
+   uint32_t count;
+   //
+   // open output file
+   //
+   output_file = fopen(output_file_name, "wb");
+   if (output_file == 0) {
+      printf("fab.c: oops -- can't open %s\n",output_file_name);
+      exit(-1);
+      }
+   //
+   // write header
+   //
+   fwrite(buf,80,1,output_file);
+   //
+   // write count
+   //
+   count = 0;
+   v->mesh->triangle = v->mesh->first;
+   while (v->mesh->triangle->next != 0) {
+      v->mesh->triangle = v->mesh->triangle->next;
+      count += 1;
+      }
+   fwrite(&count,4,1,output_file);
+   //
+   // write triangles
+   //
+   v->mesh->triangle = v->mesh->first;
+   while (v->mesh->triangle->next != 0) {
+      v->mesh->triangle = v->mesh->triangle->next;
+      fwrite(v->mesh->triangle->normal,4,3,output_file);
+      fwrite(v->mesh->triangle->v0,4,3,output_file);
+      fwrite(v->mesh->triangle->v1,4,3,output_file);
+      fwrite(v->mesh->triangle->v2,4,3,output_file);
+      fwrite(&(v->mesh->triangle->attribute),2,1,output_file);
+      }
+   //
+   // return
+   //
+   printf("wrote %d triangles to %s\n",count,output_file_name);
+   fclose(output_file);
+   }
+
+float interp(int x,int y,int i,int j,int k,uint32_t **lower_array,uint32_t **upper_array,int p) {
+   //
+   // trilinear interpolation within a voxel
+   //
+   return (lower_array[y][x]*((p+1.0-i)/(p+1.0))*((p+1.0-j)/(p+1.0))*((p+1.0-k)/(p+1.0))
+  + lower_array[y][x+1]*((i)/(p+1.0))*((p+1.0-j)/(p+1.0))*((p+1.0-k)/(p+1.0))
+  + lower_array[y+1][x]*((p+1.0-i)/(p+1.0))*((j)/(p+1.0))*((p+1.0-k)/(p+1.0))
+  + lower_array[y+1][x+1]*((i)/(p+1.0))*((j)/(p+1.0))*((p+1.0-k)/(p+1.0))
+  + upper_array[y][x]*((p+1.0-i)/(p+1.0))*((p+1.0-j)/(p+1.0))*((k)/(p+1.0))
+  + upper_array[y][x+1]*((i)/(p+1.0))*((p+1.0-j)/(p+1.0))*((k)/(p+1.0))
+  + upper_array[y+1][x]*((p+1.0-i)/(p+1.0))*((j)/(p+1.0))*((k)/(p+1.0))
+  + upper_array[y+1][x+1]*((i)/(p+1.0))*((j)/(p+1.0))*((k)/(p+1.0)));
+  }
+
+int main(int argc, char **argv) {
+   //
+   // local vars
+   //
+   GifFileType *GIFfile;
+   GifRecordType GIFtype;
+   GifByteType *GIFextension;
+   GifPixelType *GIFline;
+   uint32_t w[8],**lower_array,**upper_array;
+   int x,y,z,i,j,k,n,p,imin,imax;
+   int image_width,image_height,image_count,color_resolution,GIFcode,ret;
+   float threshold,voxel_size;
+   char comment[256],rules[255][20];
+   struct fab_vars v;
+   init_vars(&v);
+   //
+   // command line args
+   //
+   if (!((argc == 3) || (argc == 4) || (argc == 5) || (argc == 6))) {
+      printf("command line: gif_stl in.gif out.stl [threshold [size [points [angle]]]]\n");
+      printf("   in.gif = input GIF section file\n");
+      printf("   out.stl = output STL file\n");
+      printf("   threshold: surface intensity threshold (0 = min, 1 = max, default 0.5))\n");
+      printf("   size = voxel size (mm, default from file))\n");
+      printf("   points = points to interpolate per point (default 0)\n");
+      printf("   to be implemented: angle = minimum relative face angle to decimate vertices (default 0)\n");
+      exit(-1);
+      }
+   p = 0;
+   threshold = 0.5;
+   voxel_size = -1;
+   image_width = -1;
+   image_height = -1;
+   image_count = -1;
+   if (argc >= 4)
+      sscanf(argv[3],"%f",&threshold);
+   if (argc >= 5)
+      sscanf(argv[4],"%f",&voxel_size);
+   if (argc >= 6)
+      sscanf(argv[5],"%d",&p);
+   //
+   // initialize the rule table
+   //
+   init_rules(rules);
+   //
+   // scan the file 
+   //
+   printf("read %s\n",argv[1]);
+   color_resolution = -1;
+   GIFfile = DGifOpenFileName(argv[1]);
+   if (GIFfile == NULL) {
+      printf("gif_stl: oops -- can not open %s\n",argv[1]);
+      exit(-1);
+      }
+   GIFline = malloc(MAX_LINE*sizeof(GifPixelType));
+   imin = 256;
+   imax = 0;
+   do {
+      DGifGetRecordType(GIFfile,&GIFtype);
+      switch (GIFtype) {
+         case IMAGE_DESC_RECORD_TYPE:
+            DGifGetImageDesc(GIFfile);
+            image_width = GIFfile->SWidth;
+            image_height = GIFfile->SHeight;
+            image_count = GIFfile->ImageCount;
+            color_resolution = GIFfile->SColorResolution;
+            for (y = 0; y < GIFfile->SHeight; ++y) {
+               ret = DGifGetLine(GIFfile,GIFline,GIFfile->SWidth);
+               if (ret != GIF_OK) {
+                  printf("gif_stl: oops -- error reading line\n");
+                  exit(-1);
+                  }
+               for (x = 0; x < GIFfile->SWidth; ++x) {
+                  if (GIFline[x] < imin) imin = GIFline[x];
+                  if (GIFline[x] > imax) imax = GIFline[x];
+                  }
+               }
+            break;
+         case EXTENSION_RECORD_TYPE:
+            DGifGetExtension(GIFfile,&GIFcode,&GIFextension);
+            if (GIFcode == COMMENT_EXT_FUNC_CODE) {
+               n = GIFextension[0];
+               for (i = 1; i <= n; ++i)
+                  comment[i-1] = GIFextension[i];
+               comment[n] = 0;
+               if (voxel_size == -1)
+                  sscanf(comment,"mm per pixel: %f;",&voxel_size);
+               }
+            while (GIFextension != NULL)
+               DGifGetExtensionNext(GIFfile,&GIFextension);
+            break;
+         case SCREEN_DESC_RECORD_TYPE:
+            DGifGetScreenDesc(GIFfile);
+            break;
+         case TERMINATE_RECORD_TYPE:
+            break;
+         case UNDEFINED_RECORD_TYPE:
+            printf("gif_stl: oops -- undefined GIF record type\n");
+            exit(-1);
+            break;
+         }
+      } while (GIFtype != TERMINATE_RECORD_TYPE);
+   if (GIFfile == NULL) {
+      printf("gif_stl: oops -- can not open %s\n",argv[1]);
+      exit(-1);
+      }
+   if (voxel_size == -1) {
+      voxel_size = 1.0;
+      printf("   no pixel size found, assuming 1 mm\n");
+      }
+   printf("   voxel size (mm): %f, color resolution (bits): %d\n",voxel_size,color_resolution);
+   printf("   intensity min: %d max: %d\n",imin,imax);
+   printf("   number of images: %d, image width %d, image height %d\n",image_count,image_width,image_height);
+   //
+   // set threshold
+   //
+   threshold = imin + threshold*(imax-imin);
+   //
+   // add empty border
+   //
+   image_width += 2;
+   image_height += 2;
+   image_count += 2;
+   //
+   // allocate arrays
+   //
+   lower_array = malloc(image_height*sizeof(uint32_t *));
+   for (y = 0; y < image_height; ++y) {
+      lower_array[y] = malloc(image_width*sizeof(uint32_t));
+      for (x = 0; x < image_width; ++x)
+         lower_array[y][x] = 0;
+      }
+   upper_array = malloc(image_height*sizeof(uint32_t *));
+   for (y = 0; y < image_height; ++y) {
+      upper_array[y] = malloc(image_width*sizeof(uint32_t));
+      for (x = 0; x < image_width; ++x)
+         upper_array[y][x] = 0;
+      }
+   //
+   // read the file
+   //
+   DGifCloseFile(GIFfile);
+   GIFfile = DGifOpenFileName(argv[1]);
+   if (GIFfile == NULL) {
+      printf("gif_stl: oops -- can not open %s\n",argv[1]);
+      exit(-1);
+      }
+   z = 0;
+   v.mesh = malloc(sizeof(struct fab_mesh_type));
+   v.mesh->triangle = malloc(sizeof(struct fab_mesh_triangle_type));
+   v.mesh->first = v.mesh->triangle;
+   v.mesh->last = v.mesh->triangle;
+   v.mesh->triangle->previous = v.mesh->triangle->next = 0;
+   do {
+      DGifGetRecordType(GIFfile,&GIFtype);
+      switch (GIFtype) {
+         case IMAGE_DESC_RECORD_TYPE:
+            //
+            // read image
+            //
+            DGifGetImageDesc(GIFfile);
+            printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b   layer = %d",z);
+            //
+            // read layer
+            //
+            for (y = 0; y < (image_height-2); ++y) {
+               ret = DGifGetLine(GIFfile,GIFline,GIFfile->SWidth);
+               if (ret != GIF_OK) {
+                  printf("gif_stl: oops -- error reading line\n");
+                  exit(-1);
+                  }
+               for (x = 0; x < (image_width-2); ++x) {
+                  lower_array[y+1][x+1] = upper_array[y+1][x+1];
+                  upper_array[y+1][x+1] = GIFline[x];
+                  }
+               }
+            if (p == 0) {
+               //
+               // no interpolation, loop over layer voxels
+               //
+               for (x = 0; x < (image_width-1); ++x) {
+                  for (y = 0; y < (image_height-1); ++y) {
+                     w[0] = lower_array[y][x];
+                     w[1] = lower_array[y][x+1];
+                     w[2] = lower_array[y+1][x];
+                     w[3] = lower_array[y+1][x+1];
+                     w[4] = upper_array[y][x];
+                     w[5] = upper_array[y][x+1];
+                     w[6] = upper_array[y+1][x];
+                     w[7] = upper_array[y+1][x+1];
+                     triangulate(x,y,z,voxel_size,threshold,w,rules,&v);
+                     }
+                  }
+               }
+            else {
+               //
+               // yes interpolation, loop over layer sub-voxels
+               //
+               for (x = 0; x < (image_width-1); ++x) {
+                  for (y = 0; y < (image_height-1); ++y) {
+                     for (i = 0; i <= p; ++i) {
+                        for (j = 0; j <= p; ++j) {
+                           for (k = 0; k <= p; ++k) {
+                              w[0] = interp(x,y,i,j,k,lower_array,upper_array,p);
+                              w[1] = interp(x,y,i+1,j,k,lower_array,upper_array,p);
+                              w[2] = interp(x,y,i,j+1,k,lower_array,upper_array,p);
+                              w[3] = interp(x,y,i+1,j+1,k,lower_array,upper_array,p);
+                              w[4] = interp(x,y,i,j,k+1,lower_array,upper_array,p);
+                              w[5] = interp(x,y,i+1,j,k+1,lower_array,upper_array,p);
+                              w[6] = interp(x,y,i,j+1,k+1,lower_array,upper_array,p);
+                              w[7] = interp(x,y,i+1,j+1,k+1,lower_array,upper_array,p);
+                              triangulate((1+p)*x+i,(1+p)*y+j,(1+p)*z+k,voxel_size,threshold,w,rules,&v);
+                              }
+                           }
+                        }
+                     }
+                  }
+               }
+            z += 1;
+            break;
+         case EXTENSION_RECORD_TYPE:
+            DGifGetExtension(GIFfile,&GIFcode,&GIFextension);
+            while (GIFextension != NULL)
+               DGifGetExtensionNext(GIFfile,&GIFextension);
+            break;
+         case SCREEN_DESC_RECORD_TYPE:
+            DGifGetScreenDesc(GIFfile);
+            break;
+         case TERMINATE_RECORD_TYPE:
+            break;
+         case UNDEFINED_RECORD_TYPE:
+            printf("gif_stl: oops -- undefined GIF record type\n");
+            exit(-1);
+            break;
+         }
+      } while (GIFtype != TERMINATE_RECORD_TYPE);
+   //
+   // add empty top layer
+   //
+   printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b   layer = %d",z);
+   for (y = 0; y < (image_height-2); ++y) {
+      for (x = 0; x < (image_width-2); ++x) {
+         lower_array[y+1][x+1] = upper_array[y+1][x+1];
+         upper_array[y+1][x+1] = 0;
+         }
+      }
+   if (p == 0) {
+      //
+      // no interpolation, loop over layer voxels
+      //
+      for (x = 0; x < (image_width-1); ++x) {
+         for (y = 0; y < (image_height-1); ++y) {
+            w[0] = lower_array[y][x];
+            w[1] = lower_array[y][x+1];
+            w[2] = lower_array[y+1][x];
+            w[3] = lower_array[y+1][x+1];
+            w[4] = upper_array[y][x];
+            w[5] = upper_array[y][x+1];
+            w[6] = upper_array[y+1][x];
+            w[7] = upper_array[y+1][x+1];
+            triangulate(x,y,z,voxel_size,threshold,w,rules,&v);
+            }
+         }
+      }
+   else {
+      //
+      // yes interpolation, loop over layer sub-voxels
+      //
+      for (x = 0; x < (image_width-1); ++x) {
+         for (y = 0; y < (image_height-1); ++y) {
+            for (i = 0; i <= p; ++i) {
+               for (j = 0; j <= p; ++j) {
+                  for (k = 0; k <= p; ++k) {
+                     w[0] = interp(x,y,i,j,k,lower_array,upper_array,p);
+                     w[1] = interp(x,y,i+1,j,k,lower_array,upper_array,p);
+                     w[2] = interp(x,y,i,j+1,k,lower_array,upper_array,p);
+                     w[3] = interp(x,y,i+1,j+1,k,lower_array,upper_array,p);
+                     w[4] = interp(x,y,i,j,k+1,lower_array,upper_array,p);
+                     w[5] = interp(x,y,i+1,j,k+1,lower_array,upper_array,p);
+                     w[6] = interp(x,y,i,j+1,k+1,lower_array,upper_array,p);
+                     w[7] = interp(x,y,i+1,j+1,k+1,lower_array,upper_array,p);
+                     triangulate((1+p)*x+i,(1+p)*y+j,(1+p)*z+k,voxel_size,threshold,w,rules,&v);
+                     }
+                  }
+               }
+            }
+         }
+      }
+   printf("\n");
+   //
+   // write STL
+   //
+   fab_write_stl(&v,argv[2]);
+   //
+   // exit
+   //
+   DGifCloseFile(GIFfile);
+   exit(0);
+   }
diff --git a/src/core/path_array.c b/src/core/path_array.c
new file mode 100644
index 0000000..4463400
--- /dev/null
+++ b/src/core/path_array.c
@@ -0,0 +1,69 @@
+//
+// path_array.c
+//    array path
+//
+// Neil Gershenfeld
+// CBA MIT 10/6/10
+//
+// (c) Massachusetts Institute of Technology 2010
+// Permission granted for experimental and personal use;
+// license for commercial sale available from MIT.
+//
+
+#include "fab.h"
+
+main(int argc, char **argv) {
+   //
+   // local vars
+   //
+   struct fab_vars vin;
+   init_vars(&vin);
+   struct fab_vars vout;
+   init_vars(&vout);
+   int nx,ny;
+   float dx,dy;
+   //
+   // command line args
+   //
+   if (!((argc == 5) || (argc == 6) || (argc == 7))) {
+      printf("command line: path_array in.path out.path nx ny [dx [dy]]\n");
+      printf("   in.path = input path file\n");
+      printf("   out.path = output path file\n");
+      printf("   nx = number of horizonal array elements\n");
+      printf("   ny = number of vertical array elements\n");
+      printf("   dx = array element horizontal spacing (optional, mm, default 0)\n");
+      printf("   dy = array element vertical spacing (optional, mm, default dx)\n");
+      exit(-1);
+      }
+   if (argc == 5) {
+      sscanf(argv[3],"%d",&nx);
+      sscanf(argv[4],"%d",&ny);
+      dx = 0;
+      dy = dx;
+      }
+   if (argc == 6) {
+      sscanf(argv[3],"%d",&nx);
+      sscanf(argv[4],"%d",&ny);
+      sscanf(argv[5],"%f",&dx);
+      dy = dx;
+      }
+   else if (argc == 7) {
+      sscanf(argv[3],"%d",&nx);
+      sscanf(argv[4],"%d",&ny);
+      sscanf(argv[5],"%f",&dx);
+      sscanf(argv[6],"%f",&dy);
+      }
+   //
+   // read path
+   //
+   fab_read_path(&vin,argv[1]);
+   //
+   // array path
+   //
+   fab_path_array(&vin,&vout,nx,ny,dx,dy);
+   //
+   // write path
+   //
+   fab_write_path(&vout,argv[2]);
+   }
+
diff --git a/src/core/path_camm.c b/src/core/path_camm.c
new file mode 100644
index 0000000..2da75fa
--- /dev/null
+++ b/src/core/path_camm.c
@@ -0,0 +1,128 @@
+//
+// path_camm.c
+//    convert path to Roland vinylcutter .camm
+//
+// Neil Gershenfeld
+// CBA MIT 9/1/10
+//
+// (c) Massachusetts Institute of Technology 2010
+// Permission granted for experimental and personal use;
+// license for commercial sale available from MIT.
+//
+
+#include "fab.h"
+
+void fab_write_camm(struct fab_vars *v, char *output_file_name, float force, float velocity, float ox, float oy, char loc) {
+   //
+   // write path to Roland vinylcutter file
+   //
+	FILE *output_file;
+   int x,y,nsegs=0,npts=0;
+   float scale,xoffset,yoffset;
+   output_file = fopen(output_file_name,"w");
+   fprintf(output_file,"PA;PA;!ST1;!FS%f;VS%f;\n",force,velocity);
+   scale = 40.0*v->dx/(v->nx-1.0); // 40/mm
+   if (loc == 'l') {
+      xoffset = 40.0*(ox);
+      yoffset = 40.0*(oy);
+      }
+   else if (loc == 'r') {
+      xoffset = 40.0*(ox - v->dx);
+      yoffset = 40.0*(oy);
+      }
+   else if (loc == 'L') {
+      xoffset = 40.0*(ox);
+      yoffset = 40.0*(oy - v->dy);
+      }
+   else if (loc == 'R') {
+      xoffset = 40.0*(ox - v->dx);
+      yoffset = 40.0*(oy - v->dy);
+      }
+   v->path->segment = v->path->last;
+   while (1) {
+      //
+      // follow segments
+      //
+      v->path->segment->point = v->path->segment->first;
+      x = xoffset + scale * v->path->segment->point->first->value;
+      y = yoffset + scale * (v->ny - v->path->segment->point->first->next->value);
+      fprintf(output_file,"PU%d,%d;\n",x,y); // move up to start point
+      fprintf(output_file,"PU%d,%d;\n",x,y); // hack: repeat in case comm dropped
+      fprintf(output_file,"PD%d,%d;\n",x,y); // move down
+      fprintf(output_file,"PD%d,%d;\n",x,y); // hack: repeat in case comm dropped
+      nsegs += 1;
+      while (1) {
+         //
+         // follow points
+         //
+         if (v->path->segment->point->next == 0)
+            break;
+         v->path->segment->point = v->path->segment->point->next;
+         x = xoffset + scale * v->path->segment->point->first->value;
+         y = yoffset + scale * (v->ny - v->path->segment->point->first->next->value);
+         fprintf(output_file,"PD%d,%d;\n",x,y); // move down
+         fprintf(output_file,"PD%d,%d;\n",x,y); // hack: repeat in case comm dropped
+         npts += 1;
+         }
+      //fprintf(output_file,"\n",x,y);
+      if (v->path->segment->previous == 0)
+         break;
+      v->path->segment = v->path->segment->previous;
+      }
+   fprintf(output_file,"PU0,0;\n"); // pen up to origin
+   fprintf(output_file,"PU0,0;\n"); // hack: repeat in case comm dropped
+   fclose(output_file);
+   printf("wrote %s\n",output_file_name);
+   printf("   segments: %d, points: %d\n",nsegs,npts);
+   }
+
+main(int argc, char **argv) {
+   //
+   // local vars
+   //
+   struct fab_vars v;
+   init_vars(&v);
+   float ox,oy,force,velocity;
+   char loc;
+   //
+   // command line args
+   //
+   if (!((argc == 3) || (argc == 4) || (argc == 5) || (argc == 7) || (argc == 8))) {
+      printf("command line: path_camm in.path out.camm [force [velocity [x y [location]]]]\n");
+      printf("   in.path = input path file\n");
+      printf("   out.camm = output Roland vinylcutter file\n");
+      printf("   force = cutting force (optional, grams, default 45)\n");
+      printf("   velocity = cutting speed (optional, cm/s, default 2)\n");
+      printf("   x = origin x (optional, mm, default 0)\n");
+      printf("   y = origin y (optional, mm, default 0)\n");
+      printf("   location = origin location (optional, bottom left:l, bottom right:r, top left:L, top right:R, default l)\n");
+      exit(-1);
+      }
+   force = 45;
+   velocity = 2;
+   ox = 0;
+   oy = 0;
+   loc = 'l';
+   if (argc >= 4) {
+      sscanf(argv[3],"%f",&force);
+      }
+   if (argc >= 5) {
+      sscanf(argv[4],"%f",&velocity);
+      }
+   if (argc >= 7) {
+      sscanf(argv[5],"%f",&ox);
+      sscanf(argv[6],"%f",&oy);
+      }
+   if (argc >= 8) {
+      sscanf(argv[7],"%c",&loc);
+      }
+   //
+   // read path
+   //
+   fab_read_path(&v,argv[1]);
+   //
+   // write .epi
+   //
+   fab_write_camm(&v,argv[2],force,velocity,ox,oy,loc);
+   }
+
diff --git a/src/core/path_dxf.c b/src/core/path_dxf.c
new file mode 100644
index 0000000..5635fa7
--- /dev/null
+++ b/src/core/path_dxf.c
@@ -0,0 +1,149 @@
+//
+// path_dxf.c
+//    convert path to DXF
+//    todo: 3D paths
+//
+// Neil Gershenfeld
+// CBA MIT 8/25/12
+//
+// (c) Massachusetts Institute of Technology 2012
+// Permission granted for experimental and personal use;
+// license for commercial sale available from MIT.
+//
+
+#include "fab.h"
+
+void fab_write_dxf(struct fab_vars *v, char *output_file_name) {
+   //
+   // write path to DXF file
+   //
+   int x,y,z;
+   float x0,y0,z0,x1,y1,z1;
+	FILE *output_file;
+   int nsegs=0, npts=0;
+   float scale,units;
+   units = 1/25.4;
+   scale = v->dx/(v->nx-1.0);
+   output_file = fopen(output_file_name,"w");
+   fprintf(output_file,"999\nDXF written by fab modules path_dxf\n");
+   fprintf(output_file,"0\nSECTION\n");
+   fprintf(output_file,"2\nHEADER\n");
+   fprintf(output_file,"9\n$ACADVER\n1\nAC1009\n");
+   fprintf(output_file,"9\n$EXTMIN\n");
+   fprintf(output_file,"10\n%f\n",units*v->xmin);
+   fprintf(output_file,"20\n%f\n",units*v->ymin);
+   if (v->path->dof == 3)
+      fprintf(output_file,"30\n%f\n",units*(v->zmin));
+   fprintf(output_file,"9\n$EXTMAX\n");
+   fprintf(output_file,"10\n%f\n",units*(v->xmin+v->dx));
+   fprintf(output_file,"20\n%f\n",units*(v->ymin+v->dy));
+   if (v->path->dof == 3)
+      fprintf(output_file,"30\n%f\n",units*(v->zmin+v->dz));
+   fprintf(output_file,"0\nENDSEC\n");
+   /*
+   fprintf(output_file,"0\nSECTION\n");
+   fprintf(output_file,"2\nTABLES\n");
+   fprintf(output_file,"0\nTABLE\n");
+   fprintf(output_file,"2\nLTYPE\n70\n1\n");
+   fprintf(output_file,"0\nLTYPE\n");
+   fprintf(output_file,"2\nCONTINUOUS\n");
+   fprintf(output_file,"70\n64\n3\n");
+   fprintf(output_file,"Solid line\n");
+   fprintf(output_file,"72\n65\n73\n0\n40\n0.000000\n");
+   fprintf(output_file,"0\nENDTAB\n");
+   fprintf(output_file,"0\nTABLE\n2\nLAYER\n70\n1\n");
+   fprintf(output_file,"0\nLAYER\n2\ndefault\n70\n64\n62\n7\n6\n");
+   fprintf(output_file,"CONTINUOUS\n0\nENDTAB\n");
+   fprintf(output_file,"0\nENDSEC\n");
+   fprintf(output_file,"0\nSECTION\n");
+   fprintf(output_file,"2\nBLOCKS\n");
+   fprintf(output_file,"0\nENDSEC\n");
+   */
+   fprintf(output_file,"0\nSECTION\n");
+   fprintf(output_file,"2\nENTITIES\n");
+   v->path->segment = v->path->first;
+   while (1) {
+      //
+      // follow segments
+      //
+      v->path->segment->point = v->path->segment->first;
+      x = v->path->segment->point->first->value;
+      y = v->ny - v->path->segment->point->first->next->value;
+      if (v->path->dof == 3)
+         z = v->path->segment->point->first->next->next->value;
+      x0 = units*(v->xmin+scale*x);
+      y0 = units*(v->ymin+scale*y);
+      if (v->path->dof == 3)
+         z0 = units*(v->zmin+scale*z);
+      nsegs += 1;
+      while (1) {
+         //
+         // follow points
+         //
+         if (v->path->segment->point->next == 0)
+            break;
+         v->path->segment->point = v->path->segment->point->next;
+         x = v->path->segment->point->first->value;
+         y = v->ny - v->path->segment->point->first->next->value;
+         if (v->path->dof == 3)
+            z = v->path->segment->point->first->next->next->value;
+         x1 = units*(v->xmin+scale*x);
+         y1 = units*(v->ymin+scale*y);
+         if (v->path->dof == 3)
+            z1 = units*(v->zmin+scale*z);
+         fprintf(output_file,"0\nLINE\n");
+         fprintf(output_file,"10\n%f\n",x0);
+         fprintf(output_file,"20\n%f\n",y0);
+         if (v->path->dof == 3)
+            fprintf(output_file,"30\n%f\n",z0);
+         fprintf(output_file,"11\n%f\n",x1);
+         fprintf(output_file,"21\n%f\n",y1);
+         if (v->path->dof == 3)
+            fprintf(output_file,"31\n%f\n",z1);
+         x0 = x1;
+         y0 = y1;
+         if (v->path->dof == 3)
+            z0 = z1;
+         npts += 1;
+         }
+      if (v->path->segment->next == 0)
+         break;
+      v->path->segment = v->path->segment->next;
+      }
+   fprintf(output_file,"0\nENDSEC\n");
+   fprintf(output_file,"0\nEOF\n");
+   fclose(output_file);
+   printf("write %s\n",output_file_name);
+   printf("   segments: %d, points: %d\n",nsegs,npts);
+   }
+
+main(int argc, char **argv) {
+   //
+   // local vars
+   //
+   struct fab_vars v;
+   init_vars(&v);
+   struct fab_vars vnew;
+   init_vars(&vnew);
+   char view;
+   int x,y,z,nz,dz;
+   float scale;
+   //
+   // command line args
+   //
+   if (!(argc == 3)) {
+      printf("command line: path_dxf in.path out.dxf\n");
+      printf("   in.path = input path file\n");
+      printf("   out.dxf = output DXF file\n");
+      exit(-1);
+      }
+   //
+   // read path
+   //
+   fab_read_path(&v,argv[1]);
+   //
+   // write dxf
+   //
+   fab_write_dxf(&v,argv[2]);
+   }
+
diff --git a/src/core/path_epi.c b/src/core/path_epi.c
new file mode 100644
index 0000000..5775678
--- /dev/null
+++ b/src/core/path_epi.c
@@ -0,0 +1,182 @@
+//
+// path_epi.c
+//    convert path to Epilog lasercutter .epi
+//
+// Neil Gershenfeld 8/18/13
+// (c) Massachusetts Institute of Technology 2013
+//
+// This work may be reproduced, modified, distributed,
+// performed, and displayed for any purpose, but must
+// acknowledge the fab modules project. Copyright is
+// retained and must be preserved. The work is provided
+// as is; no warranty is provided, and users accept all 
+// liability.
+//
+
+#include "fab.h"
+
+void fab_write_epi(struct fab_vars *v, char *output_file_name, int power, int speed, int focus, float ox, float oy, char loc, int rate, int max_power) {
+   //
+   // write path to Epilog lasercutter file
+   //
+	FILE *output_file;
+   int i,x,y,z,current_z,layer_power,nsegs=0,npts=0;
+   float scale,xoffset,yoffset;
+   output_file = fopen(output_file_name,"w");
+   scale = 600.0*v->dx/(25.4*(v->nx-1.0)); // 600 DPI
+   if (loc == 'l') {
+      xoffset = 600.0*(ox)/25.4;
+      yoffset = 600.0*(oy - v->dy)/25.4;
+      }
+   else if (loc == 'r') {
+      xoffset = 600.0*(ox - v->dx)/25.4;
+      yoffset = 600.0*(oy - v->dy)/25.4;
+      }
+   else if (loc == 'L') {
+      xoffset = 600.0*(ox)/25.4;
+      yoffset = 600.0*(oy)/25.4;
+      }
+   else if (loc == 'R') {
+      xoffset = 600.0*(ox - v->dx)/25.4;
+      yoffset = 600.0*(oy)/25.4;
+      }
+   if (focus == 0)
+      // 
+      // init with autofocus off
+      //
+      fprintf(output_file,"%%-12345X@PJL JOB NAME=%s\r\nE@PJL ENTER LANGUAGE=PCL\r\n&y0A&l0U&l0Z&u600D*p0X*p0Y*t600R*r0F&y50P&z50S*r6600T*r5100S*r1A*rC%%1BIN;XR%d;YP%d;ZS%d;\n",output_file_name,rate,power,speed);
+   else
+      //
+      // init with autofocus on
+      //
+      fprintf(output_file,"%%-12345X@PJL JOB NAME=%s\r\nE@PJL ENTER LANGUAGE=PCL\r\n&y1A&l0U&l0Z&u600D*p0X*p0Y*t600R*r0F&y50P&z50S*r6600T*r5100S*r1A*rC%%1BIN;XR%d;YP%d;ZS%d;\n",output_file_name,rate,power,speed);
+   current_z = 0;
+   fprintf(output_file,"YP%d;\n",power);
+   v->path->segment = v->path->last;
+   while (1) {
+      //
+      // follow segments in reverse order
+      //
+      v->path->segment->point = v->path->segment->first;
+      x = xoffset + scale * v->path->segment->point->first->value;
+      y = yoffset + scale * v->path->segment->point->first->next->value;
+      if (v->path->dof >= 3) {
+         z = v->path->segment->point->first->next->next->value;
+         if (z != current_z) {
+            layer_power = power + (max_power-power) * z / (v->nz - 1.0);
+            fprintf(output_file,"YP%d;\n",layer_power);
+            current_z = z;
+            }
+         }
+      if (x < 0) x = 0;
+      if (y < 0) y = 0;
+      fprintf(output_file,"PU%d,%d;",x,y);
+      nsegs += 1;
+      while (1) {
+         //
+         // follow points
+         //
+         if (v->path->segment->point->next == 0)
+            break;
+         v->path->segment->point = v->path->segment->point->next;
+         x = xoffset + scale * v->path->segment->point->first->value;
+         y = yoffset + scale * v->path->segment->point->first->next->value;
+         if (v->path->dof >= 3) {
+            z = v->path->segment->point->first->next->next->value;
+            if (z != current_z) {
+               layer_power = power + (max_power-power) * z / (v->nz - 1.0);
+               fprintf(output_file,"YP%d;\n",layer_power);
+               current_z = z;
+               }
+            }
+         if (x < 0) x = 0;
+         if (y < 0) y = 0;
+         fprintf(output_file,"PD%d,%d;",x,y);
+         npts += 1;
+         }
+      fprintf(output_file,"\n");
+      if (v->path->segment == v->path->first)
+         break;
+      v->path->segment = v->path->segment->previous;
+      }
+   fprintf(output_file,"%%0B%%1BPUE%%-12345X@PJL EOJ \r\n");
+   //
+   // end-of-file padding hack from Epilog print driver
+   //
+   for (i = 0; i < 10000; ++i)
+      fprintf(output_file," ");
+   // fprintf(output_file,"%c",26); // ^z
+   //
+   // close and return
+   //
+   fclose(output_file);
+   printf("wrote %s\n",output_file_name);
+   printf("   segments: %d, points: %d\n",nsegs,npts);
+   }
+
+int main(int argc, char **argv) {
+   //
+   // local vars
+   //
+   struct fab_vars v;
+   init_vars(&v);
+   int power,max_power,speed,focus,rate;
+   float ox,oy;
+   char loc;
+   //
+   // command line args
+   //
+   if (!((argc == 3) || (argc == 4) || (argc == 5) || (argc == 6) || (argc == 8) || (argc == 9) || (argc == 10) || (argc == 11))) {
+      printf("command line: path_epi in.path out.epi [power [speed [focus [x y [ location [rate [max_power]]]]]]]\n");
+      printf("   in.path = input path file\n");
+      printf("   out.epi= output Epilog lasercutter file\n");
+      printf("   power = percent power, for minimum z value (optional, 0-100, default 50)\n");
+      printf("   speed = percent speed (optional, 0-100, default 50)\n");
+      printf("   focus = autofocus (optional, 0=off | 1=on, default on)\n");
+      printf("   x = origin x (optional, mm, default 0 = left side of bed)\n");
+      printf("   y = origin y (optional, mm, default 0 = back side of bed, front positive)\n");
+      printf("   location = origin location (optional, bottom left:l, bottom right:r, top left:L, top right:R, default l)\n");
+      printf("   rate = pulse rate (optional, frequency, default 2500)\n");
+      printf("   max_power = percent power, for maximum z value (optional, 0-100, default power)\n");
+      exit(-1);
+      }
+   power = 50;
+   speed = 50;
+   focus = 1;
+   ox = 0;
+   oy = 0;
+   loc = 'l';
+   rate = 2500;
+   max_power = power;
+   if (argc >= 4) {
+      sscanf(argv[3],"%d",&power);
+      }
+   if (argc >= 5) {
+      sscanf(argv[4],"%d",&speed);
+      }
+   if (argc >= 6) {
+      sscanf(argv[5],"%d",&focus);
+      }
+   if (argc >= 8) {
+      sscanf(argv[6],"%f",&ox);
+      sscanf(argv[7],"%f",&oy);
+      }
+   if (argc >= 9) {
+      sscanf(argv[8],"%c",&loc);
+      }
+   if (argc >= 10) {
+      sscanf(argv[9],"%d",&rate);
+      }
+   if (argc >= 11) {
+      sscanf(argv[10],"%d",&max_power);
+      }
+   //
+   // read path
+   //
+   fab_read_path(&v,argv[1]);
+   //
+   // write .epi
+   //
+   fab_write_epi(&v,argv[2],power,speed,focus,ox,oy,loc,rate,max_power);
+   }
+
diff --git a/src/core/path_eps.c b/src/core/path_eps.c
new file mode 100644
index 0000000..11f421d
--- /dev/null
+++ b/src/core/path_eps.c
@@ -0,0 +1,182 @@
+//
+// path_eps.c
+//    convert .path to .eps
+//
+// Neil Gershenfeld 7/4/13
+// (c) Massachusetts Institute of Technology 2013
+//
+// This work may be reproduced, modified, distributed,
+// performed, and displayed for any purpose, but must
+// acknowledge the fab modules project. Copyright is
+// retained and must be preserved. The work is provided
+// as is; no warranty is provided, and users accept all 
+// liability.
+//
+
+#include "fab.h"
+
+void fab_write_eps(struct fab_vars *v, char *output_file_name) {
+   //
+   // write path to PostScript file
+   //
+   int x,y,z,current_z;
+   float margin = 0.5; // lower left margin, in inches
+   float gray;
+	FILE *output_file;
+   int nsegs=0, npts=0;
+   float scale;
+   output_file = fopen(output_file_name,"w");
+   fprintf(output_file,"%%! path_eps output\n");
+   fprintf(output_file,"%%%%BoundingBox: %f %f %f %f\n",72.0*margin,72.0*margin,
+      72.0*(margin+v->dx/25.4),72.0*(margin+v->dy/25.4));
+   fprintf(output_file,"/m {moveto} def\n");
+   fprintf(output_file,"/l {lineto} def\n");
+   fprintf(output_file,"/g {setgray} def\n");
+   fprintf(output_file,"/s {stroke} def\n");
+   fprintf(output_file,"72 72 scale\n");
+   fprintf(output_file,"%f %f translate\n",margin,margin);
+   fprintf(output_file,"1 setlinewidth\n");
+   scale = v->dx/(25.4*(v->nx-1.0));
+   fprintf(output_file,"%f %f scale\n",scale,scale);
+   current_z = 0;
+   fprintf(output_file,"0 g\n");
+   v->path->segment = v->path->first;
+   while (1) {
+      //
+      // follow segments
+      //
+      v->path->segment->point = v->path->segment->first;
+      x = v->path->segment->point->first->value;
+      y = v->ny - v->path->segment->point->first->next->value;
+      if (v->path->dof == 3) {
+         z = v->path->segment->point->first->next->next->value;
+         if (z != current_z) {
+            gray = 0.9 * z / (v->nz - 1.0);
+            fprintf(output_file,"%.3f g\n",gray);
+            current_z = z;
+            }
+         }
+      fprintf(output_file,"%d %d m\n",x,y);
+      nsegs += 1;
+      while (1) {
+         //
+         // follow points
+         //
+         if (v->path->segment->point->next == 0)
+            break;
+         v->path->segment->point = v->path->segment->point->next;
+         x = v->path->segment->point->first->value;
+         y = v->ny - v->path->segment->point->first->next->value;
+         fprintf(output_file,"%d %d l\n",x,y);
+         if (v->path->dof == 3) {
+            z = v->path->segment->point->first->next->next->value;
+            if (z != current_z) {
+               gray = 0.9 * 0.5 * (z + current_z) / (v->nz - 1.0);
+               fprintf(output_file,"s %.3f g %d %d m\n",gray,x,y);
+               current_z = z;
+               }
+            }
+         npts += 1;
+         }
+      fprintf(output_file,"s\n");
+      if (v->path->segment->next == 0)
+         break;
+      v->path->segment = v->path->segment->next;
+      }
+   //fprintf(output_file,"showpage\n");
+   fclose(output_file);
+   printf("write %s\n",output_file_name);
+   printf("   segments: %d, points: %d\n",nsegs,npts);
+   }
+
+main(int argc, char **argv) {
+   //
+   // local vars
+   //
+   struct fab_vars v;
+   init_vars(&v);
+   struct fab_vars vnew;
+   init_vars(&vnew);
+   char view;
+   int x,y,z,nz,dz;
+   float scale;
+   //
+   // command line args
+   //
+   if (!((argc == 3) || (argc == 4))) {
+      printf("command line: path_eps in.path out.eps [view]\n");
+      printf("   in.path = input path file\n");
+      printf("   out.eps= output PostScript file\n");
+      printf("   view = view projection(s) (optional, z|3, default z)\n");
+      exit(-1);
+      }
+   view = 'z';
+   if (argc == 4)
+      view = argv[3][0];
+   //
+   // read path
+   //
+   fab_read_path(&v,argv[1]);
+   //
+   // check view
+   //
+   if (view == 'z') {
+      //
+      // write eps
+      //
+      fab_write_eps(&v,argv[2]);
+      }
+   else if (view == '3') {
+      if (v.path->dof < 3) {
+         printf("path_eps: oops -- path not 3D\n");
+         exit(-1);
+         }
+      nz = v.nx * v.dz / v.dx;
+      scale = ((float) nz) / v.nz;
+      vnew.nx = v.nx + nz;
+      vnew.ny = v.ny + nz;
+      vnew.nz = v.nz;
+      vnew.dx = v.dx + v.dz;
+      vnew.dy = v.dy + v.dz;
+      vnew.dz = v.dz;
+      vnew.xmin = v.xmin;
+      vnew.ymin = v.ymin;
+      vnew.zmin = v.zmin;
+      fab_path_start(&vnew,v.path->dof);
+      //
+      // follow path
+      //
+      v.path->segment = v.path->first;
+      while (1) {
+         //
+         // follow segments
+         //
+         v.path->segment->point = v.path->segment->first;
+         fab_path_segment(&vnew);
+         while (1) {
+            //
+            // follow points
+            //
+            fab_path_point(&vnew);
+            z = v.path->segment->point->first->next->next->value;
+            dz = nz - scale*z;
+            y = dz + v.path->segment->point->first->next->value;
+            x = dz + v.path->segment->point->first->value;
+            fab_path_axis(&vnew,x);
+            fab_path_axis(&vnew,y);
+            fab_path_axis(&vnew,z);
+            if (v.path->segment->point->next == 0)
+               break;
+            v.path->segment->point = v.path->segment->point->next;
+            }
+         if (v.path->segment->next == 0)
+            break;
+         v.path->segment = v.path->segment->next;
+         }
+      //
+      // write eps
+      //
+      fab_write_eps(&vnew,argv[2]);
+      }
+   }
+
diff --git a/src/core/path_g.c b/src/core/path_g.c
new file mode 100644
index 0000000..631e8e9
--- /dev/null
+++ b/src/core/path_g.c
@@ -0,0 +1,273 @@
+//
+// path_g.c
+//    convert path to G codes
+//
+// Neil Gershenfeld, David Carr
+// CBA MIT 8/10/11
+//
+// (c) Massachusetts Institute of Technology 2011
+// Permission granted for experimental and personal use;
+// license for commercial sale available from MIT.
+//
+
+#include "fab.h"
+
+void fab_write_g(struct fab_vars *v, char *output_file_name, int direction, float z_jog, float feed_rate, float z_feed_rate, float spindle_speed, int tool, int coolant) {
+   //
+   // write path to G-code file
+   //
+	FILE *output_file;
+   int i,nsegs=0,npts=0;
+   float units,xscale,yscale,zscale,x,y,z,xoffset,yoffset,zoffset;
+   output_file = fopen(output_file_name,"w");
+   units = 1.0/25.4; // inches
+   xscale = units*v->dx/(v->nx-1.0);
+   yscale = units*v->dy/(v->ny-1.0);
+   if (v->nz > 1)
+      zscale = units*v->dz/v->nz;
+   else
+      zscale = 0;
+   xoffset = units*v->xmin;
+   yoffset = units*v->ymin;
+   zoffset = units*v->zmin;
+   z_jog = units*z_jog;
+   feed_rate = 60*units*feed_rate; // feed rate in inches per minute
+   z_feed_rate = 60*units*z_feed_rate;
+   //
+   // Write G code header
+   //
+   //fprintf(output_file, "; G-Code generated by MIT CBA fab modules\n");
+   fprintf(output_file, "%%\n"); // tape start
+   //fprintf(output_file, "; http://kokompe.cba.mit.edu/dist\n\n");
+   // Clear all state: XY plane, inch mode, cancel diameter compensation, cancel length offset
+   // coordinate system 1, cancel motion, non-incremental motion, feed/minute mode
+   fprintf(output_file,"G17\n");
+   fprintf(output_file,"G20\n");
+   fprintf(output_file,"G40\n");
+   fprintf(output_file,"G49\n");
+   fprintf(output_file,"G54\n");
+   fprintf(output_file,"G80\n");
+   fprintf(output_file,"G90\n");
+   fprintf(output_file,"G94\n");
+   fprintf(output_file,"T%dM06\n",tool); // tool selection, tool change
+   fprintf(output_file,"F%0.4f\n",feed_rate); // feed rate
+   fprintf(output_file,"S%0.4f\n",spindle_speed); // spindle speed
+   if (coolant == 1)
+      fprintf(output_file,"M08\n"); // coolant on
+   fprintf(output_file,"G00Z%0.4f\n",z_jog); // move up before starting spindle
+   fprintf(output_file,"M03\n"); // spindle on clockwise
+   fprintf(output_file,"G04 P1\n"); // give spindle 1 second to spin up
+   //
+   // follow segments in reverse order (mill boundaries last)
+   //
+   v->path->segment = v->path->last;
+   while (1) {
+      if (direction == 0)
+         //
+         // conventional
+         //
+         v->path->segment->point = v->path->segment->last;
+      else
+         //
+         // climb
+         //
+         v->path->segment->point = v->path->segment->first;
+      x = xoffset + xscale * v->path->segment->point->first->value;
+      y = yoffset + yscale * (v->ny - v->path->segment->point->first->next->value);
+      //
+      // move to first point
+      //
+      fprintf(output_file,"G00X%0.4fY%0.4fZ%0.4f\n",x,y,z_jog);
+      //
+      // move down
+      //
+      if (v->path->dof == 2) {
+	fprintf(output_file,"G01Z%0.4f F%0.4f\n",zoffset,z_feed_rate);
+	fprintf(output_file,"F%0.4f\n",feed_rate); //restore XY feed rate
+      }
+      else if (v->path->dof == 3) {
+         z = zoffset + zscale * v->path->segment->point->first->next->next->value;
+         fprintf(output_file,"G01Z%0.4f F%0.4f\n",z,z_feed_rate);
+	 fprintf(output_file,"F%0.4f\n",feed_rate);
+         }
+      else {
+         printf("path_g: path degrees of freedom must be 2 or 3\n");
+         exit(-1);
+         }
+      nsegs += 1;
+      while (1) {
+         //
+         // check if last point
+         //
+         if (direction == 0) {
+            //
+            // conventional
+            //
+            if (v->path->segment->point->previous == 0) {
+               fprintf(output_file,"Z%0.4f\n",z_jog);
+               break;
+               }
+            }
+         else {
+            //
+            // climb
+            //
+            if (v->path->segment->point->next == 0) {
+               fprintf(output_file,"Z%0.4f\n",z_jog);
+               break;
+               }
+            }
+         //
+         // move to next point
+         //
+         if (direction == 0)
+            //
+            // conventional
+            //
+            v->path->segment->point = v->path->segment->point->previous;
+         else
+            //
+            // climb
+            //
+            v->path->segment->point = v->path->segment->point->next;
+         x = xoffset + xscale * v->path->segment->point->first->value;
+         y = yoffset + yscale * (v->ny - v->path->segment->point->first->next->value);
+         if (v->path->dof == 2)
+            fprintf(output_file,"X%0.4fY%0.4f\n",x,y);
+         else if (v->path->dof == 3) {
+            z = zoffset + zscale * v->path->segment->point->first->next->next->value;
+            fprintf(output_file,"X%0.4fY%0.4fZ%0.4f\n",x,y,z);
+            }
+         else {
+            printf("path_g: path degrees of freedom must be 2 or 3\n");
+            exit(-1);
+            }
+         npts += 1;
+         }
+      //
+      // check for previous segment
+      //
+      if (v->path->segment->previous == 0)
+         break;
+      v->path->segment = v->path->segment->previous;
+      }
+   //
+   // close and return
+   //
+   fprintf(output_file,"G00Z%0.4f\n",z_jog); // move up before stopping spindle
+   fprintf(output_file,"M05\n"); // spindle stop
+   if (coolant == 1)
+      fprintf(output_file,"M09\n"); // coolant off
+   fprintf(output_file,"M30\n"); // program end and reset
+   fprintf(output_file, "%%\n"); // tape end
+   fclose(output_file);
+   printf("wrote %s\n",output_file_name);
+   printf("   segments: %d, points: %d\n",nsegs,npts);
+   }
+
+main(int argc, char **argv) {
+   //
+   // local vars
+   //
+   struct fab_vars v;
+   init_vars(&v);
+   float z_jog,feed_rate,z_feed_rate,spindle_speed;
+   int direction,tool,coolant;
+   //
+   // command line args
+   //
+   if (!((argc == 3) || (argc == 4) || (argc == 5) || (argc == 6) || (argc == 7) || (argc == 8) || (argc == 9) || (argc == 10))) {
+      printf("command line: path_g in.path out.g [direction [z_jog [feed [z_feed [spindle [tool [coolant]]]]]]\n");
+      printf("   in.path = input path file\n");
+      printf("   out.g = output G-code file\n");
+      printf("   direction = machining direction (optional, 0 conventional/1 climb, default 0)\n");
+      printf("   z_jog = z jog height (optional, mm, default 25)\n");
+      printf("   feed = feed rate (optional, mm/s, default 100)\n");
+      printf("   z_feed = z plunge rate (optional, mm/s, default xy feed rate)\n");
+      printf("   spindle = spindle speed (optional, RPM, default 5000)\n");
+      printf("   tool = tool number (optional, default 1)\n");
+      printf("   coolant = coolant on/off (optional, 0=off/1=on, default 1)\n");
+      exit(-1);
+      }
+   if (argc == 3) {
+      direction = 0;
+      z_jog = 25.0;
+      feed_rate = 5.0;
+      z_feed_rate = feed_rate;
+      spindle_speed = 5000.0;
+      tool = 1;
+      coolant = 1;
+      }
+   else if (argc == 4) {
+      sscanf(argv[3],"%d",&direction);
+      z_jog = 25.0;
+      feed_rate = 5.0;
+      z_feed_rate = feed_rate;
+      spindle_speed = 5000.0;
+      tool = 1;
+      coolant = 1;
+      }
+   else if (argc == 5) {
+      sscanf(argv[3],"%d",&direction);
+      sscanf(argv[4],"%f",&z_jog);
+      feed_rate = 5.0;
+      z_feed_rate = feed_rate;
+      spindle_speed = 5000.0;
+      tool = 1;
+      coolant = 1;
+      }
+   else if (argc == 6) {
+      sscanf(argv[3],"%d",&direction);
+      sscanf(argv[4],"%f",&z_jog);
+      sscanf(argv[5],"%f",&feed_rate);
+      z_feed_rate = feed_rate;
+      spindle_speed = 5000.0;
+      tool = 1;
+      coolant = 1;
+      }
+    else if (argc == 7) {
+      sscanf(argv[3],"%d",&direction);
+      sscanf(argv[4],"%f",&z_jog);
+      sscanf(argv[5],"%f",&feed_rate);
+      sscanf(argv[6],"%f",&z_feed_rate);
+      spindle_speed = 5000.0;
+      tool = 1;
+      coolant = 1;
+      }
+   else if (argc == 8) {
+      sscanf(argv[3],"%d",&direction);
+      sscanf(argv[4],"%f",&z_jog);
+      sscanf(argv[5],"%f",&feed_rate);
+      sscanf(argv[6],"%f",&z_feed_rate);
+      sscanf(argv[7],"%f",&spindle_speed);
+      tool = 1;
+      coolant = 1;
+      }
+   else if (argc == 9) {
+      sscanf(argv[3],"%d",&direction);
+      sscanf(argv[4],"%f",&z_jog);
+      sscanf(argv[5],"%f",&feed_rate);
+      sscanf(argv[6],"%f",&z_feed_rate);
+      sscanf(argv[7],"%f",&spindle_speed);
+      sscanf(argv[8],"%d",&tool);
+      coolant = 1;
+      }
+   else if (argc == 10) {
+      sscanf(argv[3],"%d",&direction);
+      sscanf(argv[4],"%f",&z_jog);
+      sscanf(argv[5],"%f",&feed_rate);
+      sscanf(argv[6],"%f",&z_feed_rate);
+      sscanf(argv[7],"%f",&spindle_speed);
+      sscanf(argv[8],"%d",&tool);
+      sscanf(argv[9],"%d",&coolant);
+      }
+   //
+   // read path
+   //
+   fab_read_path(&v,argv[1]);
+   //
+   // write G codes
+   //
+   fab_write_g(&v,argv[2],direction,z_jog,feed_rate,z_feed_rate,spindle_speed,tool,coolant);
+   }
+
diff --git a/src/core/path_info.c b/src/core/path_info.c
new file mode 100644
index 0000000..35679b5
--- /dev/null
+++ b/src/core/path_info.c
@@ -0,0 +1,41 @@
+//
+// path_info.c
+//    report .path info
+//
+// Neil Gershenfeld 10/3/13
+// (c) Massachusetts Institute of Technology 2013
+//
+// This work may be reproduced, modified, distributed,
+// performed, and displayed for any purpose, but must
+// acknowledge the fab modules project. Copyright is
+// retained and must be preserved. The work is provided
+// as is; no warranty is provided, and users accept all 
+// liability.
+//
+// todo
+//    gradient intensity
+//
+
+#include "fab.h"
+
+main(int argc, char **argv) {
+   //
+   // local vars
+   //
+   struct fab_vars v;
+   init_vars(&v);
+   float units,resolution;
+   char axis;
+   //
+   // command line args
+   //
+   if (argc != 2) {
+      printf("command line: path_info in.path\n");
+      printf("   in.path = input path file\n");
+      exit(-1);
+      }
+   //
+   // read path
+   //
+   fab_read_path(&v,argv[1]);
+   }
diff --git a/src/core/path_join.c b/src/core/path_join.c
new file mode 100644
index 0000000..d519dbf
--- /dev/null
+++ b/src/core/path_join.c
@@ -0,0 +1,64 @@
+//
+// path_join.c
+//    join paths
+//
+// Neil Gershenfeld
+// CBA MIT 11/6/10
+//
+// (c) Massachusetts Institute of Technology 2010
+// Permission granted for experimental and personal use;
+// license for commercial sale available from MIT.
+//
+
+#include "fab.h"
+
+main(int argc, char **argv) {
+   //
+   // local vars
+   //
+   struct fab_vars vin1;
+   init_vars(&vin1);
+   struct fab_vars vin2;
+   init_vars(&vin2);
+   struct fab_vars vout;
+   init_vars(&vout);
+   float dx,dy;
+   //
+   // command line args
+   //
+   if (!((argc == 4) || (argc == 5) || (argc == 6))) {
+      printf("command line: path_join in1.path in2.path out.path [dx [dy]]\n");
+      printf("   in1.path = first input path file\n");
+      printf("   in2.path = second input path file\n");
+      printf("   out.path = joined output path file\n");
+      printf("   dx = in1 horizontal offset (optional, mm, default 0)\n");
+      printf("   dy = in1 vertical offset (optional, mm, default dx)\n");
+      exit(-1);
+      }
+   if (argc == 4) {
+      dx = 0;
+      dy = dx;
+      }
+   if (argc == 5) {
+      sscanf(argv[4],"%f",&dx);
+      dy = dx;
+      }
+   else if (argc == 6) {
+      sscanf(argv[4],"%f",&dx);
+      sscanf(argv[5],"%f",&dy);
+      }
+   //
+   // read paths
+   //
+   fab_read_path(&vin1,argv[1]);
+   fab_read_path(&vin2,argv[2]);
+   //
+   // append path
+   //
+   fab_path_join(&vin1,&vin2,&vout,dx,dy);
+   //
+   // write path
+   //
+   fab_write_path(&vout,argv[3]);
+   }
+
diff --git a/src/core/path_oms.c b/src/core/path_oms.c
new file mode 100644
index 0000000..5dce2fb
--- /dev/null
+++ b/src/core/path_oms.c
@@ -0,0 +1,120 @@
+//
+// path_oms.c
+//    convert path to Resonetics excimer micromachining center .oms
+//
+// Neil Gershenfeld
+// CBA MIT 6/4/13
+//
+// (c) Massachusetts Institute of Technology 2013
+// Permission granted for experimental and personal use;
+// license for commercial sale available from MIT.
+//
+
+#include "fab.h"
+
+void fab_write_oms(struct fab_vars *v, char *output_file_name, float velocity, float acceleration, int period) {
+   //
+   // write path to Epilog lasercutter file
+   //
+	FILE *output_file;
+   float x,y,scale,xoffset,yoffset;
+   float slew_velocity = 1.0;
+   float slew_acceleration = 5.0;
+   int npts=0,nsegs=0;
+   int settle = 100;
+   scale = v->dx/(v->nx-1.0); // mm
+   xoffset = v->xmin;
+   yoffset = v->ymin;
+   output_file = fopen(output_file_name,"w");
+   fprintf(output_file,"AA LP0,0,0,0,0\n"); // set origin
+   fprintf(output_file,"PP%d\n",period); // set pulse period (in us)
+   v->path->segment = v->path->first;
+   while (1) {
+      //
+      // follow segments
+      //
+      v->path->segment->point = v->path->segment->first;
+      x = xoffset + scale * v->path->segment->point->first->value;
+      y = yoffset + scale * (v->ny - v->path->segment->point->first->next->value);
+      //fprintf(output_file,"VL%.1f,%.1f\n",slew_velocity,slew_velocity);
+      // redundantly include pulses/s in VL
+      fprintf(output_file,"VL%.1f,%.1f,,%.0f\n",slew_velocity,slew_velocity,1.0e6/period);
+      fprintf(output_file,"AC%.1f,%.1f\n",slew_acceleration,slew_acceleration);
+      fprintf(output_file,"MA%f,%f\n",x,y);
+      //fprintf(output_file,"VL%.1f,%.1f\n",velocity,velocity);
+      // redundantly include pulses/s in VL
+      fprintf(output_file,"VL%.1f,%.1f,,%.0f\n",velocity,velocity,1.0e6/period);
+      fprintf(output_file,"AC%.1f,%.1f\n",acceleration,acceleration);
+      fprintf(output_file,"WT%d\n",settle);
+      nsegs += 1;
+      while (1) {
+         //
+         // follow points
+         //
+         if (v->path->segment->point->next == 0)
+            break;
+         v->path->segment->point = v->path->segment->point->next;
+         x = xoffset + scale * v->path->segment->point->first->value;
+         y = yoffset + scale * (v->ny - v->path->segment->point->first->next->value);
+         fprintf(output_file,"CutAbs %f,%f\n",x,y);
+         npts += 1;
+         }
+      if (v->path->segment->next == 0)
+         break;
+      v->path->segment = v->path->segment->next;
+      }
+   fprintf(output_file,"END\n");
+   fclose(output_file);
+   printf("wrote %s\n",output_file_name);
+   printf("   segments: %d, points: %d\n",nsegs,npts);
+   }
+
+main(int argc, char **argv) {
+   //
+   // local vars
+   //
+   struct fab_vars v;
+   init_vars(&v);
+   float velocity, acceleration;
+   int period;
+   //
+   // command line args
+   //
+   if (!((argc == 3) || (argc == 4) || (argc == 5) || (argc == 6))) {
+      printf("command line: path_oms in.path out.oms [velocity [acceleration [period]]]\n");
+      printf("   in.path = input path file\n");
+      printf("   out.oms = output Resonetics excimer micromachining center file\n");
+      printf("   velocity (default 0.1)\n");
+      printf("   acceleration (default 5.0)\n");
+      printf("   period (usec, default 10000)\n");
+      exit(-1);
+      }
+   if (argc == 3) {
+      velocity = 0.1;
+      acceleration = 5.0;
+      period = 10000;
+      }
+   else if (argc == 4) {
+      sscanf(argv[3],"%f",&velocity);
+      acceleration = 5.0;
+      period = 10000;
+      }
+   else if (argc == 5) {
+      sscanf(argv[3],"%f",&velocity);
+      sscanf(argv[4],"%f",&acceleration);
+      period = 10000;
+      }
+   else if (argc == 6) {
+      sscanf(argv[3],"%f",&velocity);
+      sscanf(argv[4],"%f",&acceleration);
+      sscanf(argv[5],"%d",&period);
+      }
+   //
+   // read path
+   //
+   fab_read_path(&v,argv[1]);
+   //
+   // write .oms
+   //
+   fab_write_oms(&v,argv[2],velocity,acceleration,period);
+   }
diff --git a/src/core/path_ord.c b/src/core/path_ord.c
new file mode 100644
index 0000000..840b629
--- /dev/null
+++ b/src/core/path_ord.c
@@ -0,0 +1,164 @@
+//
+// path_ord.c
+//    convert path to Omax waterjet .ord
+//
+// Neil Gershenfeld
+// CBA MIT 10/5/10
+//
+// (c) Massachusetts Institute of Technology 2010
+// Permission granted for experimental and personal use;
+// license for commercial sale available from MIT.
+//
+
+#include "fab.h"
+#define nostart -10000
+
+void fab_write_ord(struct fab_vars *v, char *output_file_name, float lead, int quality, float xstart, float ystart) {
+   //
+   // write path to ShopBot file
+   //
+	FILE *output_file;
+   int i,nsegs=0,npts=0;
+   float units,xscale,yscale,x,y,xmin,ymin,x0,y0,x1,y1,xoffset,yoffset,xlead,ylead,dx,dy,norm_x,norm_y,norm;
+   output_file = fopen(output_file_name,"w");
+   units = 1.0/25.4; // inches
+   xscale = units*v->dx/(v->nx-1.0);
+   yscale = units*v->dy/(v->ny-1.0);
+   xoffset = units*v->xmin;
+   yoffset = units*v->ymin;
+   lead = units*lead;
+   //
+   // write start if supplied
+   //
+   if (xstart != nostart) {
+      xstart = units*xstart;
+      ystart = units*ystart;
+      fprintf(output_file,"%f, %f, 0, 0\n",xstart,ystart); // rapid traverse from lead-out
+      }
+   //
+   // follow segments in reverse order
+   //
+   v->path->segment = v->path->last;
+   while (1) {
+      //
+      // follow points in forward order
+      //
+      v->path->segment->point = v->path->segment->first;
+      //
+      // calculate and write lead-in perpendicular to segment start
+      //
+      x0 = xoffset + xscale * v->path->segment->point->first->value;
+      y0 = yoffset + yscale * (v->ny - v->path->segment->point->first->next->value);
+      x1 = xoffset + xscale * v->path->segment->point->next->first->value;
+      y1 = yoffset + yscale * (v->ny - v->path->segment->point->next->first->next->value);
+      dx = x1 - x0;
+      dy = y1 - y0;
+      norm_x = -dy;
+      norm_y = dx;
+      norm = sqrt(norm_x*norm_x + norm_y*norm_y);
+      norm_x = norm_x/norm;
+      norm_y = norm_y/norm;
+      xlead = x0 + norm_x*lead;
+      ylead = y0 + norm_y*lead;
+      fprintf(output_file,"%f, %f, 0, -9\n",xlead,ylead); // lead-in
+      fprintf(output_file,"%f, %f, 0, %d\n",x0,y0,quality); // first point
+      nsegs += 1;
+      while (1) {
+         if (v->path->segment->point->next == 0) {
+            //
+            // no next point in segment, write lead-out
+            //
+            x0 = xoffset + xscale * v->path->segment->point->previous->first->value;
+            y0 = yoffset + yscale * (v->ny - v->path->segment->point->previous->first->next->value);
+            x1 = xoffset + xscale * v->path->segment->point->first->value;
+            y1 = yoffset + yscale * (v->ny - v->path->segment->point->first->next->value);
+            dx = x1 - x0;
+            dy = y1 - y0;
+            norm_x = -dy;
+            norm_y = dx;
+            norm = sqrt(norm_x*norm_x + norm_y*norm_y);
+            norm_x = norm_x/norm;
+            norm_y = norm_y/norm;
+            xlead = x1 + norm_x*lead;
+            ylead = y1 + norm_y*lead;
+            fprintf(output_file,"%f, %f, 0, 0\n",xlead,ylead); // rapid traverse from lead-out
+            break;
+            }
+         v->path->segment->point = v->path->segment->point->next;
+         x = xoffset + xscale * v->path->segment->point->first->value;
+         y = yoffset + yscale * (v->ny - v->path->segment->point->first->next->value);
+         if (v->path->segment->point->next != 0)
+            fprintf(output_file,"%f, %f, 0, %d\n",x,y,quality);
+         else
+            fprintf(output_file,"%f, %f, 0, -9\n",x,y); // lead-out next
+         npts += 1;
+         }
+      //
+      // check for previous segment
+      //
+      if (v->path->segment->previous == 0)
+         break;
+      v->path->segment = v->path->segment->previous;
+      }
+   //
+   // close and return
+   //
+   fclose(output_file);
+   printf("wrote %s\n",output_file_name);
+   printf("   segments: %d, points: %d\n",nsegs,npts);
+   }
+
+main(int argc, char **argv) {
+   //
+   // local vars
+   //
+   struct fab_vars v;
+   init_vars(&v);
+   float lead,xstart,ystart;
+   int quality;
+   //
+   // command line args
+   //
+   if (!((argc == 3) || (argc == 4) || (argc == 5) || (argc == 7))) {
+      printf("command line: path_ord in.path out.ord [lead [quality [xstart ystart]]]\n");
+      printf("   in.path = input path file\n");
+      printf("   out.ord = output Omax waterjet file\n");
+      printf("   lead = lead in/out (optional, mm, default 2)\n");
+      printf("   quality = cut quality (optional, default -3)\n");
+      printf("   xstart,ystart = start position (optional, mm, default path start)\n");
+      exit(-1);
+      }
+   if (argc == 3) {
+      lead = 2.0;
+      quality = -3;
+      xstart = nostart;
+      ystart = nostart;
+      }
+   else if (argc == 4) {
+      sscanf(argv[3],"%f",&lead);
+      quality = -3;
+      xstart = nostart;
+      ystart = nostart;
+      }
+   else if (argc == 5) {
+      sscanf(argv[3],"%f",&lead);
+      sscanf(argv[4],"%d",&quality);
+      xstart = nostart;
+      ystart = nostart;
+      }
+   else if (argc == 7) {
+      sscanf(argv[3],"%f",&lead);
+      sscanf(argv[4],"%d",&quality);
+      sscanf(argv[5],"%f",&xstart);
+      sscanf(argv[6],"%f",&ystart);
+      }
+   //
+   // read path
+   //
+   fab_read_path(&v,argv[1]);
+   //
+   // write .ord
+   //
+   fab_write_ord(&v,argv[2],lead,quality,xstart,ystart);
+   }
+
diff --git a/src/core/path_png.c b/src/core/path_png.c
new file mode 100644
index 0000000..8071ab9
--- /dev/null
+++ b/src/core/path_png.c
@@ -0,0 +1,45 @@
+//
+// path_png.c
+//    convert path to PNG
+//
+// Neil Gershenfeld
+// CBA MIT 2/26/11
+//
+// (c) Massachusetts Institute of Technology 2010
+// Permission granted for experimental and personal use;
+// license for commercial sale available from MIT.
+//
+
+#include "fab.h"
+
+main(int argc, char **argv) {
+   //
+   // local vars
+   //
+   struct fab_vars v;
+   init_vars(&v);
+   int x,y,intensity,number,n,count,count_sum;
+   float distance,error,z;
+   //
+   // command line args
+   //
+   if (argc != 3) {
+      printf("command line: path_png in.path out.png\n");
+      printf("   in.path = input path file\n");
+      printf("   out.png = output PNG file\n");
+      exit(-1);
+      }
+   //
+   // read path
+   //
+   fab_read_path(&v,argv[1]);
+   //
+   // shade path with z displacement
+   //
+   fab_shade_path_displace(&v);
+   //
+   // write PNG
+   //
+   fab_write_png_K(&v,argv[2]);
+   }
+
diff --git a/src/core/path_rml.c b/src/core/path_rml.c
new file mode 100644
index 0000000..f479455
--- /dev/null
+++ b/src/core/path_rml.c
@@ -0,0 +1,196 @@
+//
+// path_rml.c
+//    convert path to Roland Modela .rml
+//
+// Neil Gershenfeld
+// CBA MIT 9/6/10
+//
+// (c) Massachusetts Institute of Technology 2010
+// Permission granted for experimental and personal use;
+// license for commercial sale available from MIT.
+//
+
+#include "fab.h"
+
+void fab_write_rml(struct fab_vars *v, char *output_file_name, float speed, int direction, float z_up) {
+   //
+   // write path to Roland Modela file
+   //
+	FILE *output_file;
+   int i,x,y,z,iz_down,iz_up,nsegs=0,npts=0;
+   float xscale,yscale,zscale,xoffset,yoffset,zoffset;
+   output_file = fopen(output_file_name,"w");
+   fprintf(output_file,"PA;PA;"); // plot absolute
+   fprintf(output_file,"VS%.1f;!VZ%.1f;",speed,speed);
+   xscale = 40.0*v->dx/(v->nx-1.0); // 40/mm
+   yscale = 40.0*v->dy/(v->ny-1.0); // 40/mm
+   if (v->nz > 1)
+      zscale = 40.0*v->dz/v->nz; // 40/mm
+   else
+      zscale = 0;
+   xoffset = 40.0*v->xmin;
+   yoffset = 40.0*v->ymin;
+   zoffset = 40.0*v->zmin;
+   iz_up = 40.0*z_up;
+   iz_down = zoffset;
+   fprintf(output_file,"!PZ%d,%d;",iz_down,iz_up); // set z down, jog 
+   fprintf(output_file,"!MC1;\n"); // turn motor on
+   //
+   // follow segments in reverse order (mill boundaries last)
+   //
+   v->path->segment = v->path->last;
+   while (1) {
+      if (direction == 0)
+         //
+         // conventional
+         //
+         v->path->segment->point = v->path->segment->last;
+      else
+         //
+         // climb
+         //
+         v->path->segment->point = v->path->segment->first;
+      x = xoffset + xscale * v->path->segment->point->first->value;
+      y = yoffset + yscale * (v->ny - v->path->segment->point->first->next->value);
+      //
+      // move up to first point
+      //
+      fprintf(output_file,"PU%d,%d;\n",x,y);
+      //
+      // move down
+      //
+      if (v->path->dof == 2) {
+         fprintf(output_file,"PD%d,%d;\n",x,y);
+         }
+      else if (v->path->dof == 3) {
+         z = zoffset + zscale * v->path->segment->point->first->next->next->value;
+         fprintf(output_file,"Z%d,%d,%d;\n",x,y,z);
+         }
+      else {
+         printf("path_rml: path degrees of freedom must be 2 or 3\n");
+         exit(-1);
+         }
+      nsegs += 1;
+      while (1) {
+         //
+         // check if last point
+         //
+         if (direction == 0) {
+            //
+            // conventional
+            //
+            if (v->path->segment->point->previous == 0) {
+               fprintf(output_file,"PU%d,%d;\n",x,y);
+               break;
+               }
+            }
+         else {
+            //
+            // climb
+            //
+            if (v->path->segment->point->next == 0) {
+               fprintf(output_file,"PU%d,%d;\n",x,y);
+               break;
+               }
+            }
+         //
+         // move to next point
+         //
+         if (direction == 0)
+            //
+            // conventional
+            //
+            v->path->segment->point = v->path->segment->point->previous;
+         else
+            //
+            // climb
+            //
+            v->path->segment->point = v->path->segment->point->next;
+         x = xoffset + xscale * v->path->segment->point->first->value;
+         y = yoffset + yscale * (v->ny - v->path->segment->point->first->next->value);
+         if (v->path->dof == 2) {
+            fprintf(output_file,"PD%d,%d;\n",x,y);
+            }
+         else if (v->path->dof == 3) {
+            z = zoffset + zscale * v->path->segment->point->first->next->next->value;
+            fprintf(output_file,"Z%d,%d,%d;\n",x,y,z);
+            }
+         else {
+            printf("path_rml: path degrees of freedom must be 2 or 3\n");
+            exit(-1);
+            }
+         npts += 1;
+         }
+      //
+      // check for previous segment
+      //
+      //fprintf(output_file,"\n",x,y);
+      if (v->path->segment->previous == 0)
+         break;
+      v->path->segment = v->path->segment->previous;
+      }
+   //
+   // pad end of file with motor off commands for Modela buffering bug
+   //
+   for (i = 0; i < 1000; ++i)
+      fprintf(output_file,"!MC0;");
+   //
+   // return to home
+   //
+   fprintf(output_file,"\nH;\n");
+   fclose(output_file);
+   printf("wrote %s\n",output_file_name);
+   printf("   segments: %d, points: %d\n",nsegs,npts);
+   }
+
+main(int argc, char **argv) {
+   //
+   // local vars
+   //
+   struct fab_vars v;
+   init_vars(&v);
+   float speed,z_up;
+   int direction;
+   //
+   // command line args
+   //
+   if (!((argc == 3) || (argc == 4)  || (argc == 5) || (argc == 6) || (argc == 7) || (argc == 8) || (argc == 9))) {
+      printf("command line: path_rml in.path out.rml [speed [direction [jog [xmin ymin [zmin]]]]]\n");
+      printf("   in.path = input path file\n");
+      printf("   out.rml = output Roland Modela file\n");
+      printf("   speed = cutting speed (optional, mm/s, default 4)\n");
+      printf("   direction = machining direction (optional, 0 conventional/1 climb, default 1)\n");
+      printf("   jog = jog height (optional, mm, default 1)\n");
+      printf("   xmin = left position (optional, mm, default path value)\n");
+      printf("   ymin = front position (optional, mm, default path value)\n");
+      printf("   zmin = bottom position (optional, -mm, default path value)\n");
+      exit(-1);
+      }
+   speed = 4;
+   direction = 1;
+   z_up = 1;
+   if (argc >= 4)
+      sscanf(argv[3],"%f",&speed);
+   if (argc >= 5)
+      sscanf(argv[4],"%d",&direction);
+   if (argc >= 6)
+      sscanf(argv[5],"%f",&z_up);
+   //
+   // read path
+   //
+   fab_read_path(&v,argv[1]);
+   //
+   // check origin
+   //
+   if (argc >= 7)
+      sscanf(argv[6],"%lf",&v.xmin);
+   if (argc >= 8)
+      sscanf(argv[7],"%lf",&v.ymin);
+   if (argc >= 9)
+      sscanf(argv[8],"%lf",&v.zmin);
+   //
+   // write .rml
+   //
+   fab_write_rml(&v,argv[2],speed,direction,z_up);
+   }
+
diff --git a/src/core/path_sbp.c b/src/core/path_sbp.c
new file mode 100644
index 0000000..1d9e8ac
--- /dev/null
+++ b/src/core/path_sbp.c
@@ -0,0 +1,238 @@
+//
+// path_sbp.c
+//    convert path to ShopBot .sbp
+//
+// Neil Gershenfeld
+// CBA MIT 10/1/10
+//
+// (c) Massachusetts Institute of Technology 2010
+// Permission granted for experimental and personal use;
+// license for commercial sale available from MIT.
+//
+
+#include "fab.h"
+
+void fab_write_sbp(struct fab_vars *v, char *output_file_name, int direction, int spindle_speed, float xy_speed, float z_speed, float xy_jog_speed, float z_jog_speed, float z_jog, float units) {
+   //
+   // write path to ShopBot file
+   //
+	FILE *output_file;
+   int i,nsegs=0,npts=0;
+   float xscale,yscale,zscale,x,y,z,xoffset,yoffset,zoffset;
+   output_file = fopen(output_file_name,"w");
+   fprintf(output_file,"SA\r\n"); // set to absolute distances
+   fprintf(output_file,"TR,%d,1\r\n",spindle_speed); // set spindle speed
+   fprintf(output_file,"SO,1,1\r\n"); // set output number 1 to on
+   fprintf(output_file,"pause,2\r\n"); // let spindle come up to speed
+   xscale = v->dx/(v->nx-1.0)/units;
+   yscale = v->dy/(v->ny-1.0)/units;
+   if (v->nz > 1)
+      zscale = v->dz/(units*v->nz);
+   else
+      zscale = 0;
+   xoffset = v->xmin/units;
+   yoffset = v->ymin/units;
+   zoffset = v->zmin/units;
+   xy_speed = xy_speed/units;
+   z_speed = z_speed/units;
+   xy_jog_speed = xy_jog_speed/units;
+   z_jog_speed = z_jog_speed/units;
+   z_jog = z_jog/units;
+   fprintf(output_file,"MS,%f,%f\r\n",xy_speed,z_speed); // set xy,z speed
+   fprintf(output_file,"JS,%f,%f\r\n",xy_jog_speed,z_jog_speed); // set jog xy,z speed
+   fprintf(output_file,"JZ,%f\r\n",z_jog); // move up
+   //
+   // follow segments in reverse order (mill boundaries last)
+   //
+   v->path->segment = v->path->last;
+   while (1) {
+      if (direction == 0)
+         //
+         // conventional
+         //
+         v->path->segment->point = v->path->segment->last;
+      else
+         //
+         // climb
+         //
+         v->path->segment->point = v->path->segment->first;
+      x = xoffset + xscale * v->path->segment->point->first->value;
+      y = yoffset + yscale * (v->ny - v->path->segment->point->first->next->value);
+      //
+      // move to first point
+      //
+      fprintf(output_file,"J2,%f,%f\r\n",x,y);
+      //
+      // move down
+      //
+      if (v->path->dof == 2)
+         fprintf(output_file,"MZ,%f\r\n",zoffset);
+      else if (v->path->dof == 3) {
+         z = zoffset + zscale * v->path->segment->point->first->next->next->value;
+         fprintf(output_file,"M3,%f,%f,%f\r\n",x,y,z);
+         }
+      else {
+         printf("path_sbp: path degrees of freedom must be 2 or 3\n");
+         exit(-1);
+         }
+      nsegs += 1;
+      while (1) {
+         //
+         // check if last point
+         //
+         if (direction == 0) {
+            //
+            // conventional
+            //
+            if (v->path->segment->point->previous == 0) {
+               fprintf(output_file,"MZ,%f\r\n",z_jog);
+               break;
+               }
+            }
+         else {
+            //
+            // climb
+            //
+            if (v->path->segment->point->next == 0) {
+               fprintf(output_file,"MZ,%f\r\n",z_jog);
+               break;
+               }
+            }
+         //
+         // move to next point
+         //
+         if (direction == 0)
+            //
+            // conventional
+            //
+            v->path->segment->point = v->path->segment->point->previous;
+         else
+            //
+            // climb
+            //
+            v->path->segment->point = v->path->segment->point->next;
+         x = xoffset + xscale * v->path->segment->point->first->value;
+         y = yoffset + yscale * (v->ny - v->path->segment->point->first->next->value);
+         if (v->path->dof == 2)
+            fprintf(output_file,"M2,%f,%f\r\n",x,y);
+         else if (v->path->dof == 3) {
+            z = zoffset + zscale * v->path->segment->point->first->next->next->value;
+            fprintf(output_file,"M3,%f,%f,%f\r\n",x,y,z);
+            }
+         else {
+            printf("path_sbp: path degrees of freedom must be 2 or 3\n");
+            exit(-1);
+            }
+         npts += 1;
+         }
+      //
+      // check for previous segment
+      //
+      if (v->path->segment->previous == 0)
+         break;
+      v->path->segment = v->path->segment->previous;
+      }
+   //
+   // close and return
+   //
+   fclose(output_file);
+   printf("wrote %s\n",output_file_name);
+   printf("   segments: %d, points: %d\n",nsegs,npts);
+   }
+
+int main(int argc, char **argv) {
+   //
+   // local vars
+   //
+   struct fab_vars v;
+   init_vars(&v);
+   float xy_speed, z_speed, xy_jog_speed, z_jog_speed, z_jog, units;
+   int direction,spindle_speed;
+   //
+   // command line args
+   //
+   if (!((argc == 3) || (argc == 4) || (argc == 5) || (argc == 7) || (argc == 10) || (argc == 11))) {
+      printf("command line: path_sbp in.path out.sbp [direction [spindle_speed [xy_speed z_speed [xy_jog_speed z_jog_speed z_jog [units]]]]]]\n");
+      printf("   in.path = input path file\n");
+      printf("   out.sbp = output ShopBot file\n");
+      printf("   direction = machining direction (optional, 0 conventional/1 climb, default 0)\n");
+      printf("   spindle_speed = spindle speed (optional, if control installed, RPM, default 12000)\n");
+      printf("   xy_speed = xy cutting speed (optional, mm/s, default 30)\n");
+      printf("   z_speed = z cutting speed (optional, mm/s, default 30)\n");
+      printf("   xy_jog_speed = xy jog speed (optional, mm/s, default 150)\n");
+      printf("   z_jog_speed = z jog speed (optional, mm/s, default 150)\n");
+      printf("   z_jog = z jog height (optional, mm, default 25)\n");
+      printf("   units = mm per file unit (optional, default 25.4)\n");
+      exit(-1);
+      }
+   if (argc == 3) {
+      direction = 0;
+      spindle_speed = 12000;
+      xy_speed = 30;
+      z_speed = 30;
+      xy_jog_speed = 150;
+      z_jog_speed = 150;
+      z_jog = 25;
+      units = 25.4;
+      }
+   else if (argc == 4) {
+      sscanf(argv[3],"%d",&direction);
+      spindle_speed = 12000;
+      xy_speed = 30;
+      z_speed = 30;
+      xy_jog_speed = 150;
+      z_jog_speed = 150;
+      z_jog = 25;
+      units = 25.4;
+      }
+   else if (argc == 5) {
+      sscanf(argv[3],"%d",&direction);
+      sscanf(argv[4],"%d",&spindle_speed);
+      xy_speed = 30;
+      z_speed = 30;
+      xy_jog_speed = 150;
+      z_jog_speed = 150;
+      z_jog = 25;
+      units = 25.4;
+      }
+   else if (argc == 7) {
+      sscanf(argv[3],"%d",&direction);
+      sscanf(argv[4],"%d",&spindle_speed);
+      sscanf(argv[5],"%f",&xy_speed);
+      sscanf(argv[6],"%f",&z_speed);
+      xy_jog_speed = 150;
+      z_jog_speed = 150;
+      z_jog = 25;
+      units = 25.4;
+      }
+   else if (argc == 10) {
+      sscanf(argv[3],"%d",&direction);
+      sscanf(argv[4],"%d",&spindle_speed);
+      sscanf(argv[5],"%f",&xy_speed);
+      sscanf(argv[6],"%f",&z_speed);
+      sscanf(argv[7],"%f",&xy_jog_speed);
+      sscanf(argv[8],"%f",&z_jog_speed);
+      sscanf(argv[9],"%f",&z_jog);
+      units = 25.4;
+      }
+   else if (argc == 11) {
+      sscanf(argv[3],"%d",&direction);
+      sscanf(argv[4],"%d",&spindle_speed);
+      sscanf(argv[5],"%f",&xy_speed);
+      sscanf(argv[6],"%f",&z_speed);
+      sscanf(argv[7],"%f",&xy_jog_speed);
+      sscanf(argv[8],"%f",&z_jog_speed);
+      sscanf(argv[9],"%f",&z_jog);
+      sscanf(argv[10],"%f",&units);
+      }
+   //
+   // read path
+   //
+   fab_read_path(&v,argv[1]);
+   //
+   // write .sbp
+   //
+   fab_write_sbp(&v,argv[2],direction,spindle_speed,xy_speed,z_speed,xy_jog_speed,z_jog_speed,z_jog,units);
+   return 0;
+   }
+
diff --git a/src/core/path_time.c b/src/core/path_time.c
new file mode 100644
index 0000000..cb8d7a6
--- /dev/null
+++ b/src/core/path_time.c
@@ -0,0 +1,174 @@
+//
+// path_time.c
+//    estimate path time
+//
+// Neil Gershenfeld 10/29/13
+// (c) Massachusetts Institute of Technology 2013
+//
+// This work may be reproduced, modified, distributed,
+// performed, and displayed for any purpose, but must
+// acknowledge the fab modules project. Copyright is
+// retained and must be preserved. The work is provided
+// as is; no warranty is provided, and users accept all 
+// liability.
+//
+
+#include "fab.h"
+
+void fab_find_time(struct fab_vars *v, float units, float move, float height, float jog, float plunge, float *time, int *segments, int *points) {
+   float xscale, yscale, zscale;
+   float xoffset, yoffset, zoffset;
+   float xold, yold, zold, xnew, ynew, znew;
+   if ((v->path->dof < 2) || (v->path->dof > 3)) {
+         printf("path_time: path degrees of freedom must be 2 or 3\n");
+         exit(-1);
+         }
+   xoffset = v->xmin/units;
+   yoffset = v->ymin/units;
+   xscale = v->dx/(v->nx-1.0)/units;
+   yscale = v->dy/(v->ny-1.0)/units;
+   if (v->path->dof == 3) {
+      zoffset = v->zmin/units;
+      if (v->nz > 1)
+         zscale = v->dz/(units*v->nz);
+      else
+         zscale = 0;
+      }
+   //
+   // start at origin
+   //
+   xold = 0;
+   yold = 0;
+   zold = 0;
+   *time = 0;
+   *segments = 0;
+   *points = 0;
+   //
+   // follow segments
+   //
+   v->path->segment = v->path->first;
+   while (1) {
+      *segments += 1;
+      //
+      // follow segment points
+      //
+      v->path->segment->point = v->path->segment->first;
+      *points += 1;
+      xnew = xoffset + xscale * v->path->segment->point->first->value;
+      ynew = yoffset + yscale * (v->ny - v->path->segment->point->first->next->value);
+      znew = height;
+      //
+      // jog to first point
+      //
+      *time += sqrt((xnew-xold)*(xnew-xold)+(ynew-yold)*(ynew-yold))/jog;
+      xold = xnew;
+      yold = ynew;
+      //
+      // plunge to first point
+      //
+      if (v->path->dof == 3) {
+         znew = zoffset + zscale * v->path->segment->point->first->next->next->value;
+         *time += sqrt((znew-zold)*(znew-zold))/plunge;
+         zold = znew;
+         }
+      while (1) {
+         //
+         // check if last point
+         //
+         if (v->path->segment->point->next == 0) {
+            znew = height;
+            *time += sqrt((znew-zold)*(znew-zold))/plunge;
+            zold = znew;
+            break;
+            }
+         //
+         // move to next point
+         //
+         v->path->segment->point = v->path->segment->point->next;
+         *points += 1;
+         xnew = xoffset + xscale * v->path->segment->point->first->value;
+         ynew = yoffset + yscale * (v->ny - v->path->segment->point->first->next->value);
+         if (v->path->dof == 2) {
+            *time += sqrt((xnew-xold)*(xnew-xold)+(ynew-yold)*(ynew-yold))/move;
+            xold = xnew;
+            yold = ynew;
+            }
+         else if (v->path->dof == 3) {
+            znew = zoffset + zscale * v->path->segment->point->first->next->next->value;
+            *time += sqrt((xnew-xold)*(xnew-xold)+(ynew-yold)*(ynew-yold)+(znew-zold)*(znew-zold))/move;
+            xold = xnew;
+            yold = ynew;
+            zold = znew;
+            }
+         }
+      //
+      // check for next segment
+      //
+      if (v->path->segment->next == 0)
+         break;
+      v->path->segment = v->path->segment->next;
+      *segments += 1;
+      }
+   //
+   // return
+   //
+   return;
+   }
+
+int main(int argc, char **argv) {
+   //
+   // local vars
+   //
+   struct fab_vars v;
+   init_vars(&v);
+   float units, move, height, jog, plunge, time;
+   int segments, points;
+   //
+   // command line args
+   //
+   if (!((argc == 3) || (argc == 4) || (argc == 5) || (argc == 6))) {
+      printf("command line: path_time in.path move_speed [jog_height [jog_speed [plunge_speed]]]\n");
+      printf("   in.path = input path file\n");
+      printf("   move_speed = speed of path segments (mm/s)\n");
+      printf("   jog_height = height between path segments (mm, optional, default 0)\n");
+      printf("   jog_speed = speed between path segments (mm/s, optional, default move_speed)\n");
+      printf("   plunge_speed = speed from jog to move  (mm/s, optional, default move_speed)\n");
+      exit(-1);
+      }
+   if (argc == 3) {
+      sscanf(argv[2],"%f",&move);
+      height = 0;
+      jog = move;
+      plunge = move;
+      }
+   else if (argc == 4) {
+      sscanf(argv[2],"%f",&move);
+      sscanf(argv[3],"%f",&height);
+      jog = move;
+      plunge = move;
+      }
+   else if (argc == 5) {
+      sscanf(argv[2],"%f",&move);
+      sscanf(argv[3],"%f",&height);
+      sscanf(argv[4],"%f",&jog);
+      plunge = move;
+      }
+   else if (argc == 6) {
+      sscanf(argv[2],"%f",&move);
+      sscanf(argv[3],"%f",&height);
+      sscanf(argv[4],"%f",&jog);
+      sscanf(argv[5],"%f",&plunge);
+      }
+   //
+   // read path
+   //
+   fab_read_path(&v,argv[1]);
+   //
+   // calculate time
+   //
+   fab_find_time(&v,1.0,move,height,jog,plunge,&time,&segments,&points);
+   //
+   // report and return
+   //
+   printf("path time: %.0f seconds, %.1f minutes, %.2f hours\n",time,time/60.0,time/(60.0*60.0));
+   }
diff --git a/src/core/path_uni.c b/src/core/path_uni.c
new file mode 100644
index 0000000..a7329b6
--- /dev/null
+++ b/src/core/path_uni.c
@@ -0,0 +1,166 @@
+//
+// path_uni.c
+//    convert path to Universal lasercutter .uni
+//
+// Neil Gershenfeld
+// CBA MIT 9/21/11
+//
+// (c) Massachusetts Institute of Technology 2011
+// Permission granted for experimental and personal use;
+// license for commercial sale available from MIT.
+//
+
+#include "fab.h"
+
+void fab_write_uni(struct fab_vars *v, char *output_file_name, int power, int max_power, int speed, int rate) {
+   //
+   // write path to Universal lasercutter file
+   //
+	FILE *output_file;
+   int x,y,z,current_z,layer_power,nsegs=0,npts=0;
+   unsigned char ppi,speed_hi,speed_lo,power_hi,power_lo;
+   float scale,xoffset,yoffset;
+   //
+   output_file = fopen(output_file_name,"w");
+   scale = 1000.0*v->dx/(25.4*(v->nx-1.0)); // 1000 DPI
+   xoffset = 1000.0*v->xmin/25.4;
+   yoffset = 1000.0*v->ymin/25.4;
+   fprintf(output_file,"Z"); // initialize
+   fprintf(output_file,"t%s~;",output_file_name); // title
+   fprintf(output_file,"IN;DF;PS0;DT~"); // initialize
+   ppi = rate/10;
+   fprintf(output_file,"s%c",ppi); // PPI
+   speed_hi = (648*speed)/256;
+   speed_lo = (648*speed)%256;
+   fprintf(output_file,"v%c%c",speed_hi,speed_lo); // speed
+   power_hi = (320*power)/256;
+   power_lo = (320*power)%256;
+   fprintf(output_file,"p%c%c",power_hi,power_lo); // power
+   fprintf(output_file,"a%c",2); // air assist on high
+   current_z = 0;
+   v->path->segment = v->path->last;
+   while (1) {
+      //
+      // follow segments in reverse order
+      //
+      v->path->segment->point = v->path->segment->first;
+      x = xoffset + scale * v->path->segment->point->first->value;
+      y = yoffset + scale * (v->ny - v->path->segment->point->first->next->value);
+      if (v->path->dof == 3) {
+         z = v->path->segment->point->first->next->next->value;
+         if (z != current_z) {
+            layer_power = power + (max_power-power) * z / (v->nz - 1.0);
+            power_hi = (320*layer_power)/256;
+            power_lo = (320*layer_power)%256;
+            fprintf(output_file,"p%c%c",power_hi,power_lo); // power
+            current_z = z;
+            }
+         }
+      fprintf(output_file,"PU;PA%d,%d;PD;",x,y);
+      nsegs += 1;
+      while (1) {
+         //
+         // follow points
+         //
+         if (v->path->segment->point->next == 0)
+            break;
+         v->path->segment->point = v->path->segment->point->next;
+         x = xoffset + scale * v->path->segment->point->first->value;
+         y = yoffset + scale * (v->ny - v->path->segment->point->first->next->value);
+         if (v->path->dof == 3) {
+            z = v->path->segment->point->first->next->next->value;
+            if (z != current_z) {
+               layer_power = power + (max_power-power) * z / (v->nz - 1.0);
+               fprintf(output_file,"YP%d;\n",layer_power);
+               current_z = z;
+               }
+            }
+         fprintf(output_file,"PA%d,%d;",x,y);
+         npts += 1;
+         }
+      fprintf(output_file,"\n",x,y);
+      if (v->path->segment == v->path->first)
+         break;
+      v->path->segment = v->path->segment->previous;
+      }
+   fprintf(output_file,"e"); // end of file
+   fclose(output_file);
+   printf("wrote %s\n",output_file_name);
+   printf("   segments: %d, points: %d\n",nsegs,npts);
+   }
+
+main(int argc, char **argv) {
+   //
+   // local vars
+   //
+   struct fab_vars v;
+   init_vars(&v);
+   int power,max_power,speed,focus,rate;
+   //
+   // command line args
+   //
+   if (!((argc == 3) || (argc == 4) || (argc == 5) || (argc == 7) || (argc == 8) || (argc == 9))) {
+      printf("command line: path_uni in.path out.uni [power [speed [xmin ymin [rate [max_power]]]]]\n");
+      printf("   in.path = input path file\n");
+      printf("   out.uni= output Universal lasercutter file\n");
+      printf("   power = percent power (optional, 0-100, default 100)\n");
+      printf("   speed = percent speed (optional, 0-100, default 100)\n");
+      printf("   xmin = left position (optional, mm, default path, 0 = left side of bed)\n");
+      printf("   ymin = front position (optional, mm, default path, 0 = back, front positive)\n");
+      printf("   rate = pulse rate (optional, frequency, default 500)\n");
+      printf("   max_power = maximum power for maximum z value (optional, 0-100, default 100)\n");
+      exit(-1);
+      }
+   if (argc == 3) {
+      power = 100;
+      speed = 100;
+      rate = 500;
+      max_power = 100;
+      }
+   else if (argc == 4) {
+      sscanf(argv[3],"%d",&power);
+      speed = 100;
+      rate = 500;
+      max_power = 100;
+      }
+   else if (argc == 5) {
+      sscanf(argv[3],"%d",&power);
+      sscanf(argv[4],"%d",&speed);
+      rate = 500;
+      max_power = 100;
+      }
+   else if (argc == 7) {
+      sscanf(argv[3],"%d",&power);
+      sscanf(argv[4],"%d",&speed);
+      rate = 500;
+      max_power = 100;
+      }
+   else if (argc == 8) {
+      sscanf(argv[3],"%d",&power);
+      sscanf(argv[4],"%d",&speed);
+      sscanf(argv[7],"%d",&rate);
+      max_power = 100;
+      }
+   else if (argc == 9) {
+      sscanf(argv[3],"%d",&power);
+      sscanf(argv[4],"%d",&speed);
+      sscanf(argv[7],"%d",&rate);
+      sscanf(argv[8],"%d",&max_power);
+      }
+   //
+   // read path
+   //
+   fab_read_path(&v,argv[1]);
+   //
+   // origin
+   //
+   if ((argc == 7) || (argc == 8) || (argc == 9)) {
+      sscanf(argv[5],"%lf",&v.xmin);
+      sscanf(argv[6],"%lf",&v.ymin);
+      }
+   //
+   // write .epi
+   //
+   fab_write_uni(&v,argv[2],power,max_power,speed,rate);
+   }
+
diff --git a/src/core/png_distances.c b/src/core/png_distances.c
new file mode 100644
index 0000000..882e437
--- /dev/null
+++ b/src/core/png_distances.c
@@ -0,0 +1,77 @@
+//
+// png_distances.c
+//    find distances from edges in PNG
+//
+// Neil Gershenfeld
+// CBA MIT 7/24/11
+//
+// (c) Massachusetts Institute of Technology 2011
+// Permission granted for experimental and personal use;
+// license for commercial sale available from MIT.
+//
+
+#include "fab.h"
+
+main(int argc, char **argv) {
+   //
+   // local vars
+   //
+   struct fab_vars v;
+   init_vars(&v);
+   int x,y,distances;
+   float intensity,distance;
+   //
+   // command line args
+   //
+   if (!((argc == 3) || (argc == 4) || (argc == 5))) {
+      printf("command line: png_distances in.png out.png [intensity [distances]]\n");
+      printf("   in.png = input PNG file\n");
+      printf("   out.png = input PNG file\n");
+      printf("   intensity = intensity level to slice (optional, 0-1, default 0.5)\n");
+      printf("   distances = show distances (optional, 0/1, default 1)\n");
+      exit(-1);
+      }
+   if (argc == 3) {
+      intensity = 0.5;
+      distances = 1;
+      }
+   else if (argc == 4) {
+      sscanf(argv[3],"%f",&intensity);
+      distances = 1;
+      }
+   else if (argc == 5) {
+      sscanf(argv[3],"%f",&intensity);
+      sscanf(argv[4],"%d",&distances);
+      }
+   //
+   // read PNG
+   //
+   fab_read_png(&v,argv[1]);
+   //
+   // threshold
+   //
+   fab_threshold(&v,intensity);
+   //
+   // find distances
+   //
+   if (distances == 1) {
+      printf("   find distances\n");
+      fab_distances(&v);
+      //
+      // copy distances to image array
+      //
+      for (y = 0; y < v.ny; ++y)
+         for (x = 0; x < v.nx; ++x)
+            v.array[y][x] = v.distances[y][x];
+      }
+   //
+   // scale 
+   //
+   v.bit_depth = 16;
+   fab_rescale(&v,0,1);
+   //
+   // write PNG
+   //
+   fab_write_png_K(&v,argv[2]);
+   }
+
diff --git a/src/core/png_drl.c b/src/core/png_drl.c
new file mode 100644
index 0000000..9796a55
--- /dev/null
+++ b/src/core/png_drl.c
@@ -0,0 +1,267 @@
+/*
+%  	Rewind and Stop
+X#Y# 	Move and Drill
+T# 	Tool Selection
+M30 	End of Program
+M00 	End of Program
+R#X#Y# 	Repeat Hole
+G05, G81 	Select Drill Mode
+G90 	Absolute Mode
+G91 	Incremental Mode
+G92 X#Y# 	Set Zero
+G93 X#Y# 	Set Zero
+M48 	Program Header to first "%"
+M72 	English-Imperial Mode
+*/
+/* 
+%	Rewind and Stop
+X#Y#	Move and Drill
+T#	Tool Selection
+M30	End of Program
+M00	End of Program
+M25	Beginning of Pattern
+M31	Beginning of Pattern
+M01	End of Pattern
+M02 X#Y#	Repeat Pattern
+R#M02X#Y#	Multiple Repeat Pattern
+M02 X#Y# M70	Swap Axis
+M02 X#Y# M80	Mirror Image X Axis
+M02 X#Y# M90	Mirror Image Y Axis
+M08	End of Step and Repeat
+N#	Block Sequence Number
+/	Block Delete
+R#X#Y#	Repeat Hole
+G05, G81	Select Drill Mode
+G04 X#	Variable Dwell (ignored)
+G90	Absolute Mode
+G91	Incremental Mode
+G92 X#Y#	Set Zero
+G93 X#Y#	Set Zero
+M48	Program Header to first "%"
+M47	Operator Message CRT Display
+M71	Metric Mode
+M72	English-Imperial Mode
+Snn	Spindle Speed (RPM)
+Fnn	Z axis feed speed (IPM)
+*/
+/*
+M48	      Program Header
+M72, LZ	   English measurement Leading 0's
+T03C0.038	T03 .0380"
+T04C0.125	T04 .1250"
+T06C0.046	T06 .0460"
+T12C0.063	T12 .0630"
+T16C0.250	T16 .2500"
+%	         Rewind and Stop (start of drill data)
+T03	      Drill COMMAND GET TOOL 3
+X00581Y0122	Drill location with TOOL 3
+T06	      Drill COMMAND GET TOOL 6
+X01657Y03295	Drill location with TOOL 6
+T12	      Drill COMMAND GET TOOL 12
+X00585Y03311	Drill location with TOOL 12
+T04	      Drill COMMAND GET TOOL 4
+X00873Y02691	Drill location with TOOL 4
+T16	      Drill COMMAND GET TOOL 16
+X00625Y02191	Drill location with TOOL 16
+T00	      Drill COMMAND UNLOAD TOOL
+M30	      End of Program
+*/
+//
+// png_drl.c
+//    PNG to Excellon
+//
+// Neil Gershenfeld
+// CBA MIT 11/22/12
+//
+// (c) Massachusetts Institute of Technology 2012
+// Permission granted for experimental and personal use;
+// license for commercial sale available from MIT.
+//
+
+#include "fab.h"
+
+#define MAX_HOLES 100000
+#define TOLERANCE 0.01 // hole drill match percent tolerance
+
+void fab_write_drl(struct fab_vars *v, char *output_file_name) {
+	FILE *output_file;
+   int x,y,edges,points,hole,holes,drill,drills,index[MAX_HOLES];
+   float units,scale;
+   float x0[MAX_HOLES],y0[MAX_HOLES],r[MAX_HOLES],d[MAX_HOLES];
+   output_file = fopen(output_file_name,"w");
+   //
+   // threshold array
+   //
+   fab_threshold(v,0.5);
+   //
+   // find edges
+   //
+   edges = fab_edges(v);
+   if (edges == 0) {
+      printf("png_drl: oops -- no edges\n");
+      exit(-1);
+      }
+   //
+   // set edge directions
+   //
+   fab_directions(v);
+   //
+   // follow edges
+   //
+   fab_path_start(v,2);
+   fab_vectorize(v,1,0);
+   //
+   //
+   //
+   units = 1.0/25.4; // inches
+   scale = units*v->dx/(v->nx-1.0);
+   //
+   // follow segments to find holes
+   //
+   v->path->segment = v->path->first;
+   hole = 0;
+   while (1) {
+      //
+      // find hole centers
+      //
+      v->path->segment->point = v->path->segment->first;
+      x = v->path->segment->point->first->value;
+      y = v->ny - v->path->segment->point->first->next->value;
+      x0[hole] = x;
+      y0[hole] = y;
+      points = 1;
+      while (1) {
+         if (v->path->segment->point->next == 0) {
+            x0[hole] = x0[hole] / points;
+            y0[hole] = y0[hole] / points;
+            break;
+            }
+         v->path->segment->point = v->path->segment->point->next;
+         x = v->path->segment->point->first->value;
+         y = v->ny - v->path->segment->point->first->next->value;
+         x0[hole] += x;
+         y0[hole] += y;
+         points += 1;
+         }
+      //
+      // find hole radii
+      //
+      v->path->segment->point = v->path->segment->first;
+      x = v->path->segment->point->first->value;
+      y = v->ny - v->path->segment->point->first->next->value;
+      r[hole] = sqrt((x-x0[hole])*(x-x0[hole])+(y-y0[hole])*(y-y0[hole]));
+      points = 1;
+      while (1) {
+         if (v->path->segment->point->next == 0) {
+            x0[hole] = scale * x0[hole];
+            y0[hole] = scale * y0[hole];
+            r[hole] = scale * r[hole] / points;
+            break;
+            }
+         v->path->segment->point = v->path->segment->point->next;
+         x = v->path->segment->point->first->value;
+         y = v->ny - v->path->segment->point->first->next->value;
+         r[hole] += sqrt((x-x0[hole])*(x-x0[hole])+(y-y0[hole])*(y-y0[hole]));
+         points += 1;
+         }
+      hole += 1;
+      if (v->path->segment->next == 0) {
+         break;
+         }
+      v->path->segment = v->path->segment->next;
+      }
+   holes = hole;
+   //
+   // find unique drills
+   //
+   drills = 0;
+   for (hole = 0; hole < holes; ++hole) {
+      drill = 0;
+      while (1) {
+         if (drill == drills) {
+            drills += 1;
+            d[drill] = 2*r[hole];
+            index[hole] = drill;
+            break;
+            }
+         if ((fabs(d[drill] - 2*r[hole])/d[drill]) < TOLERANCE) {
+            index[hole] = drill;
+            break;
+            }
+         drill += 1;
+         }
+      }
+   //
+   // write header
+   //
+   fprintf(output_file,"M48\n"); // program header
+   fprintf(output_file,"M72, LZ\n"); // inches, leading zeros
+   //
+   // write drills
+   //
+   for (drill = 0; drill < drills; ++drill) {
+      fprintf(output_file,"T%.2dC%.3f\n",(1+drill),d[drill]);
+      }
+   //
+   // write holes
+   //
+   fprintf(output_file,"%%\n"); // rewind and stop (start hole data)
+   for (hole = 0; hole < holes; ++hole) {
+      fprintf(output_file,"T%.2d\n",(1+index[hole])); // tool
+      x = 10000*x0[hole];
+      y = 10000*y0[hole];
+      fprintf(output_file,"X%.6dY%.6d\n",x,y); // location
+      }
+   /*
+      T06	      Drill COMMAND GET TOOL 6
+      X01657Y03295	Drill location with TOOL 6
+      T12	      Drill COMMAND GET TOOL 12
+      X00585Y03311	Drill location with TOOL 12
+      T04	      Drill COMMAND GET TOOL 4
+      X00873Y02691	Drill location with TOOL 4
+      T16	      Drill COMMAND GET TOOL 16
+      X00625Y02191	Drill location with TOOL 16
+      T00	      Drill COMMAND UNLOAD TOOL
+   */
+   //
+   // close file
+   //
+   fprintf(output_file,"M30\n"); // end of program
+   fclose(output_file);
+   printf("wrote %s\n",output_file_name);
+   printf("   holes: %d, drills: %d\n",holes,drills);
+   }
+
+main(int argc, char **argv) {
+   //
+   // local vars
+   //
+   struct fab_vars v;
+   init_vars(&v);
+   //
+   // command line args
+   //
+   if ((argc < 3) || (argc > 3)) {
+      printf("command line: png_drl in.png out.drl\n");
+      printf("   in.png = input PNG file\n");
+      printf("   out.drl = output Excellon file\n");
+      exit(-1);
+      }
+   //
+   // read PNG
+   //
+   fab_read_png(&v,argv[1]);
+   //
+   // copy image to array
+   //
+   fab_png_array(&v);
+   //
+   // write array to Excellon
+   //
+   fab_write_drl(&v,argv[2]);
+   //
+   // exit
+   //
+   exit(0);
+   }
+
diff --git a/src/core/png_ex.c b/src/core/png_ex.c
new file mode 100644
index 0000000..880dd4e
--- /dev/null
+++ b/src/core/png_ex.c
@@ -0,0 +1,66 @@
+#include 
+#include 
+#include 
+
+int x, y;
+int width, height;
+png_byte color_type;
+png_byte bit_depth;
+png_structp png_ptr;
+png_infop info_ptr;
+png_bytep * row_pointers;
+
+void read_png_file(char* file_name) {
+	FILE *fp = fopen(file_name, "rb");
+	png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
+      NULL,NULL,NULL);
+	info_ptr = png_create_info_struct(png_ptr);
+	png_init_io(png_ptr, fp);
+	png_read_info(png_ptr, info_ptr);
+	width = info_ptr->width;
+	height = info_ptr->height;
+	color_type = info_ptr->color_type;
+	bit_depth = info_ptr->bit_depth;
+   printf("width %d height %d color %d bit %d\n",
+      width,height,color_type,bit_depth);
+	row_pointers = (png_bytep*) malloc(sizeof(png_bytep) * height);
+	for (y=0; yrowbytes);
+	png_read_image(png_ptr, row_pointers);
+   fclose(fp);
+   }
+
+void write_png_file(char* file_name) {
+	FILE *fp = fopen(file_name, "wb");
+	png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
+      NULL,NULL,NULL);
+	info_ptr = png_create_info_struct(png_ptr);
+	png_init_io(png_ptr, fp);
+	png_set_IHDR(png_ptr, info_ptr, width, height,
+	   bit_depth, color_type, PNG_INTERLACE_NONE,
+		PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+	png_write_info(png_ptr, info_ptr);
+	png_write_image(png_ptr, row_pointers);
+	png_write_end(png_ptr, NULL);
+   fclose(fp);
+   }
+
+void process_file(void) {
+   png_byte *ptr;
+	for (y = 0; y < height; y++)
+		for (x = 0; x < width; x++) {
+			ptr = &(row_pointers[y][x*3]);
+			ptr[0] = 255;
+			ptr[1] = ptr[2];
+		   }
+   }
+
+int main(int argc, char **argv) {
+	if (argc != 3) {
+      printf("Usage: program_name  \n");
+      exit(0);
+      }
+	read_png_file(argv[1]);
+	process_file();
+	write_png_file(argv[2]);
+   }
diff --git a/src/core/png_grb.c b/src/core/png_grb.c
new file mode 100644
index 0000000..60312fc
--- /dev/null
+++ b/src/core/png_grb.c
@@ -0,0 +1,125 @@
+//
+// png_grb.c
+//    PNG to Gerber (RS-274X)
+//
+// Neil Gershenfeld
+// CBA MIT 11/22/12
+//
+// (c) Massachusetts Institute of Technology 2012
+// Permission granted for experimental and personal use;
+// license for commercial sale available from MIT.
+//
+
+#include "fab.h"
+
+void fab_write_grb(struct fab_vars *v, char *output_file_name) {
+	FILE *output_file;
+   int i,j,k,x,y,width,height;
+   float units,dx,dy,xmin,ymin;
+   output_file = fopen(output_file_name,"w");
+   //
+   // write header
+   //
+   fprintf(output_file,"%%FSLAX24Y24*%%\n"); // leading zeros omitted, absolute coordinates, 2.4
+   fprintf(output_file,"%%MOIN*%%\n"); // inches units
+   fprintf(output_file,"%%OFA0B0*%%\n"); // no offset
+   //
+   // write apertures
+   //
+   units = 1.0/25.4; // inches
+   dx = units*v->dx/(v->nx-1.0);
+   dy = units*v->dy/(v->ny-1.0);
+   xmin = units*v->xmin;
+   ymin = units*v->ymin;
+   for (i = 1; i <= 10; ++i) {
+      for (j = 1; j <= 10; ++j) {
+         fprintf(output_file,"%%ADD%dR,%.4fX%.4f*%%\n",i*10+j,j*dx,i*dy);
+         }
+      }
+   //
+   // write flashes
+   //
+   for (i = (v->ny-1); i >= 0; --i) {
+      for (j = 0; j < v->nx; ++j) {
+         //
+         // find next starting pixel
+         //
+         if (v->array[i][j] > 0) {
+            //
+            // find width
+            //
+            v->array[i][j] = 0;
+            width = 1;
+            while ((width < 10) && ((j+width) < v->nx)){
+               if (v->array[i][j+width] != 0) {
+                  v->array[i][j+width] = 0;
+                  width += 1;
+                  }
+               else
+                  break;
+               }
+            //
+            // find height
+            //
+            height = 1;
+            while ((height < 10) && ((i-height) >= 0)){
+               for (k = 0; k < width; ++k) {
+                  if (v->array[i-height][j+k] == 0)
+                     goto height_continue;
+                  }
+               for (k = 0; k < width; ++k)
+                  v->array[i-height][j+k] = 0;
+               height += 1;
+               }
+            height_continue:
+            //
+            // flash it
+            //
+            x = 10000*(xmin+dx*(j+width/2.0));
+            y = 10000*(ymin+dy*(v->ny-1-i+height/2.0));
+            fprintf(output_file,"D%d*\n",height*10+width);
+            fprintf(output_file,"X%dY%dD03*\n",x,y);
+            }
+         }
+      }
+   //
+   // close file
+   //
+   fprintf(output_file,"M02*\n"); // end of file
+   fclose(output_file);
+   printf("wrote %s\n",output_file_name);
+   }
+
+main(int argc, char **argv) {
+   //
+   // local vars
+   //
+   struct fab_vars v;
+   init_vars(&v);
+   //
+   // command line args
+   //
+   if ((argc < 3) || (argc > 3)) {
+      printf("command line: png_grb in.png out.grb\n");
+      printf("   in.png = input PNG file\n");
+      printf("   out.grb = output Gerber (RS-274X) file\n");
+      exit(-1);
+      }
+   //
+   // read PNG
+   //
+   fab_read_png(&v,argv[1]);
+   //
+   // copy image to array
+   //
+   fab_png_array(&v);
+   //
+   // write array to Gerber
+   //
+   fab_write_grb(&v,argv[2]);
+   //
+   // exit
+   //
+   exit(0);
+   }
+
diff --git a/src/core/png_halftone.c b/src/core/png_halftone.c
new file mode 100644
index 0000000..17b8b71
--- /dev/null
+++ b/src/core/png_halftone.c
@@ -0,0 +1,108 @@
+//
+// png_halftone.c
+//    halftone PNG to path
+//
+// Neil Gershenfeld
+// CBA MIT 9/9/10
+//
+// (c) Massachusetts Institute of Technology 2010
+// Permission granted for experimental and personal use;
+// license for commercial sale available from MIT.
+//
+
+#include "fab.h"
+
+main(int argc, char **argv) {
+   //
+   // local vars
+   //
+   struct fab_vars v;
+   init_vars(&v);
+   int points, invert;
+   float threshold, size, spacing, offset;
+   //
+   // command line args
+   //
+   if (!((argc == 3) || (argc == 4) || (argc == 5) || (argc == 6) || (argc == 7) || (argc == 8) || (argc == 9))) {
+      printf("command line: png_halftone in.png out.path [threshold [points [size [spacing [offset [invert]]]]]]\n");
+      printf("   in.png = input PNG file\n");
+      printf("   out.path = output path file\n");
+      printf("   threshold = minimum spot radius (optional, pixels default 1)\n");
+      printf("   points = points per spot (optional, default 8)\n");
+      printf("   size = maximum spot size (optional, mm, default 1)\n");
+      printf("   spacing = spot spacing (optional, 1 = size, default 1)\n");
+      printf("   offset = row offset (optional, 1 = size, default 0.5)\n");
+      printf("   offset = row offset (optional, 1 = size, default 0.5)\n");
+      printf("   invert = invert image (0 = no (default), 1 = yes)\n");
+      exit(-1);
+      }
+   if (argc == 3) {
+      threshold = 1.0;
+      points = 8;
+      size = 1.0;
+      spacing = 1.0;
+      offset = 0.5;
+      invert = 0;
+      }
+   else if (argc == 4) {
+      sscanf(argv[3],"%f",&threshold);
+      points = 8;
+      size = 1.0;
+      spacing = 1.0;
+      offset = 0.5;
+      invert = 0;
+      }
+   else if (argc == 5) {
+      sscanf(argv[3],"%f",&threshold);
+      sscanf(argv[4],"%d",&points);
+      size = 1.0;
+      spacing = 1.0;
+      offset = 0.5;
+      invert = 0;
+      }
+   else if (argc == 6) {
+      sscanf(argv[3],"%f",&threshold);
+      sscanf(argv[4],"%d",&points);
+      sscanf(argv[5],"%f",&size);
+      spacing = 1.0;
+      offset = 0.5;
+      invert = 0;
+      }
+   else if (argc == 7) {
+      sscanf(argv[3],"%f",&threshold);
+      sscanf(argv[4],"%d",&points);
+      sscanf(argv[5],"%f",&size);
+      sscanf(argv[6],"%f",&spacing);
+      offset = 0.5;
+      invert = 0;
+      }
+   else if (argc == 8) {
+      sscanf(argv[3],"%f",&threshold);
+      sscanf(argv[4],"%d",&points);
+      sscanf(argv[5],"%f",&size);
+      sscanf(argv[6],"%f",&spacing);
+      sscanf(argv[7],"%f",&offset);
+      invert = 0;
+      }
+   else if (argc == 9) {
+      sscanf(argv[3],"%f",&threshold);
+      sscanf(argv[4],"%d",&points);
+      sscanf(argv[5],"%f",&size);
+      sscanf(argv[6],"%f",&spacing);
+      sscanf(argv[7],"%f",&offset);
+      sscanf(argv[8],"%d",&invert);
+      }
+   //
+   // read PNG
+   //
+   fab_read_png(&v,argv[1]);
+   //
+   // halftone
+   //
+   fab_halftone(&v,threshold,points,size,spacing,offset,invert);
+   //
+   // write path
+   //
+   fab_write_path(&v,argv[2]);
+   }
+
diff --git a/src/core/png_offset.c b/src/core/png_offset.c
new file mode 100644
index 0000000..4dc78b7
--- /dev/null
+++ b/src/core/png_offset.c
@@ -0,0 +1,85 @@
+//
+// png_offset.c
+//    offset PNG
+//    png_offset in.png out.png threshold distance
+//
+// Neil Gershenfeld
+// CBA MIT 9/11/10
+//
+// (c) Massachusetts Institute of Technology 2010
+// Permission granted for experimental and personal use;
+// license for commercial sale available from MIT.
+//
+// todo
+//    pipe I/O
+//    variable bit depth
+//
+
+#include "fab.h"
+
+main(int argc, char **argv) {
+   //
+   // local vars
+   //
+   struct fab_vars v;
+   init_vars(&v);
+   int x,y,count;
+   float intensity,distance;
+   //
+   // command line args
+   //
+   if (!((argc == 3) || (argc == 4) || (argc == 5))) {
+      printf("command line: png_offset in.png out.png [intensity [distance]]\n");
+      printf("   in.png = input PNG file\n");
+      printf("   out.png = input PNG file\n");
+      printf("   intensity = intensity level to slice (optional, 0-1, default 0.5)\n");
+      printf("   distance = distance to offset (optional, mm, default 0)\n");
+      exit(-1);
+      }
+   if (argc == 3) {
+      intensity = 0.5;
+      distance = 0;
+      }
+   else if (argc == 4) {
+      sscanf(argv[3],"%f",&intensity);
+      distance = 0;
+      }
+   else if (argc == 5) {
+      sscanf(argv[3],"%f",&intensity);
+      sscanf(argv[4],"%f",&distance);
+      }
+   //
+   // read PNG
+   //
+   fab_read_png(&v,argv[1]);
+   //
+   // threshold
+   //
+   fab_threshold(&v,intensity);
+   //
+   // find edges
+   //
+   fab_edges(&v);
+   //
+   // find edge distances
+   //
+   fab_distances(&v);
+   //
+   // offset
+   //
+   count = fab_offset(&v,distance);
+   printf("png_offset: %d points remain\n",count);
+   //
+   // set edge directions
+   //
+   fab_directions(&v);
+   //
+   // shade states
+   //
+   fab_shade_states(&v);
+   //
+   // write PNG
+   //
+   fab_write_png_K(&v,argv[2]);
+   }
+
diff --git a/src/core/png_path.c b/src/core/png_path.c
new file mode 100644
index 0000000..4bfaca5
--- /dev/null
+++ b/src/core/png_path.c
@@ -0,0 +1,671 @@
+//
+// png_path.c
+//    slice, offset, and vectorize PNG to path
+//
+// Neil Gershenfeld 10/14/13
+// (c) Massachusetts Institute of Technology 2013
+//
+// This work may be reproduced, modified, distributed,
+// performed, and displayed for any purpose, but must
+// acknowledge the fab modules project. Copyright is
+// retained and must be preserved. The work is provided
+// as is; no warranty is provided, and users accept all 
+// liability.
+//
+// todo:
+
+#include "fab.h"
+
+int main(int argc, char **argv) {
+   //
+   // local vars
+   //
+   struct fab_vars v;
+   init_vars(&v);
+   struct fab_vars vxz;
+   init_vars(&vxz);
+   struct fab_vars vyz;
+   init_vars(&vyz);
+   int offset_number,offset_count,
+      layer,layer_number,nz,xz,yz,xy,
+      ix,ixmin,ixmax,idx,
+      iy,iymin,iymax,idy,
+      iz,ithreshold,
+      *tool_dx,*tool_dy,*tool_iz,tool_pt,tool_n,tool_ir,tool_id,
+      *clearance_dx,*clearance_dy,*clearance_iz,clearance_pt,clearance_n,clearance_ir,clearance_id,
+      remaining_count,remaining_count_sum,tool_collision;
+   float error,offset_diameter,offset_overlap,clearance_length,clearance_diameter,
+      intensity_top,intensity_bottom,intensity_layer,
+      z_top,z_bottom,z_thickness,z_layer,*z_list,distance;
+   char tool_type;
+   //
+   // command line args
+   //
+   if ((argc < 3) || (argc > 18)) {
+      printf("command line: png_path in.png out.path [error [offset_diameter [offset_number [offset_overlap [intensity_top [intensity_bottom [z_top [z_bottom [z_thickness [xz [yz [xy [type [clearance_length clearance_diameter]]]]]]]]]]]]]]\n");
+      printf("   in.png = input PNG file\n");
+      printf("   out.path = output path file\n");
+      printf("   error = allowable vector fit deviation (optional, pixels, default 1.1)\n");
+      printf("   offset_diameter = diameter to offset (optional, mm, default 0)\n");
+      printf("   offset_number = number of contours to offset (optional, -1 to fill all, default 1)\n");
+      printf("   offset_overlap = tool offset overlap fraction (optional, 0 (no overlap) - 1 (complete overlap, default 0.5))\n");
+      printf("   intensity_top = top slice intensity (optional, 0-1, default 0.5)\n");
+      printf("   intensity_bottom = bottom slice intensity (optional, 0-1, default intensity_top)\n");
+      printf("   z_top = top slice z value (optional, mm, default 0)\n");
+      printf("   z_bottom = bottom slice z value (optional, mm, default z_top)\n");
+      printf("   z_thickness = slice z thickness (optional, mm, default z_top-z_bottom)\n");
+      printf("   xz = xz finish (optional, 1=yes, default 0\n");
+      printf("   yz = yz finish (optional, 1=yes, default 0\n");
+      printf("   xy = xy path (optional, 1=yes, default 1\n");
+      printf("   type = finish tool type (optional, f=flat end, b=ball end, default f\n");
+      printf("   clearance_length = finish tool clearance length (optional, mm, 0 = no limit, default 0\n");
+      printf("   clearance_diameter = finish tool clearance diameter (optional, mm, default offset_diameter\n");
+      exit(-1);
+      }
+   error = 1.1; 
+   offset_diameter = 0;
+   offset_number = 1;
+   offset_overlap = 0.5;
+   intensity_top = 0.5;
+   intensity_bottom = intensity_top;
+   z_top = 0;
+   z_bottom = 0;
+   z_thickness = 0;
+   xz = 0;
+   yz = 0;
+   xy = 1;
+   tool_type = 'f';
+   if (argc >= 4) {
+      sscanf(argv[3],"%f",&error);
+      }
+   if (argc >= 5) {
+      sscanf(argv[4],"%f",&offset_diameter);
+      }
+   if (argc >= 6) {
+      sscanf(argv[5],"%d",&offset_number);
+      }
+   if (argc >= 7) {
+      sscanf(argv[6],"%f",&offset_overlap);
+      }
+   if (argc >= 8) {
+      sscanf(argv[7],"%f",&intensity_top);
+      intensity_bottom = intensity_top;
+      }
+   if (argc >= 9) {
+      sscanf(argv[8],"%f",&intensity_bottom);
+      }
+   if (argc >= 10) {
+      sscanf(argv[9],"%f",&z_top);
+      z_bottom = z_top;
+      }
+   if (argc >= 11) {
+      sscanf(argv[10],"%f",&z_bottom);
+      z_thickness = z_top - z_bottom;
+      }
+   if (argc >= 12) {
+      sscanf(argv[11],"%f",&z_thickness);
+      }
+   if (argc >= 13) {
+      sscanf(argv[12],"%d",&xz);
+      }
+   if (argc >= 14) {
+      sscanf(argv[13],"%d",&yz);
+      }
+   if (argc >= 15) {
+      sscanf(argv[14],"%d",&xy);
+      }
+   if (argc >= 16) {
+      sscanf(argv[15],"%c",&tool_type);
+      }
+   clearance_length = z_top - z_bottom;
+   clearance_diameter = offset_diameter;
+   if (argc >= 17) {
+      sscanf(argv[16],"%f",&clearance_length);
+      if (clearance_length == 0)
+         clearance_length = z_top - z_bottom;
+      }
+   if (argc >= 18) {
+      sscanf(argv[17],"%f",&clearance_diameter);
+      }
+   tool_collision = 0;
+   //
+   // read PNG
+   //
+   fab_read_png(&v,argv[1]);
+   //
+   // 2D path?
+   //
+   if (argc < 9) {
+      //
+      // start path
+      //
+      fab_path_start(&v,2);
+      printf("png_path: intensity %f\n",intensity_top);
+      //
+      // copy image to array
+      //
+      fab_png_array(&v);
+      //
+      // threshold array
+      //
+      printf("   threshold\n");
+      fab_threshold(&v,intensity_top);
+      //
+      // find edges
+      //
+      printf("   find edges\n");
+      remaining_count = fab_edges(&v);
+      if (remaining_count != 0) {
+         //
+         // find edge distances
+         //
+         printf("   find distances\n");
+         //test_fab_distances(&v);
+         fab_distances(&v);
+         //
+         // loop over contours
+         //
+         printf("   offset:\n");
+         offset_count = 0;
+         remaining_count_sum = 0;
+         do {
+            //
+            // offset
+            //
+            distance = offset_diameter/2.0 + offset_diameter*(1-offset_overlap)*offset_count;
+            remaining_count = fab_offset(&v,distance);
+            remaining_count_sum += remaining_count;
+            printf("      distance %f, %d exterior points remain\n",
+               distance,remaining_count);
+            if (remaining_count != 0) {
+               //
+               // set edge directions
+               //
+               fab_directions(&v);
+               //
+               // vectorize edge directions
+               //
+               fab_vectorize(&v,error,0);
+               }
+            ++offset_count;
+            } while ((offset_count != offset_number) && (remaining_count != 0));
+         }
+      else {
+         printf("path.c: oops -- no layer edges\n");
+         exit(-1);
+         }
+      if (remaining_count_sum == 0) {
+         printf("png_path: no offset edges\n");
+         exit(1);
+         }
+      }
+   //
+   // 3D path
+   //
+   else {
+      //
+      // start path
+      //
+      fab_path_start(&v,3);
+      //
+      // set path z bounds
+      //
+      v.zmin = z_bottom;
+      v.dz = (z_top - z_bottom);
+      if (v.dz == 0)
+         v.nz = 1;
+      else
+         v.nz = v.dz * v.nx / v.dx;
+      //
+      // find z layer values
+      //
+      if (xy == 1) {
+         if (v.dz == 0)
+            nz = 1;
+         else {
+            nz = 2 + (z_top-z_bottom)/z_thickness;
+            z_list = (float*) malloc(nz*sizeof(float));
+            z_layer = z_top;
+            nz = 0;
+            while (1) {
+               z_list[nz] = z_layer;
+               nz += 1;
+               if (z_layer == z_bottom)
+                  break;
+               z_layer -= z_thickness;
+               if (z_layer < z_bottom)
+                  z_layer = z_bottom;
+               }
+            }
+         }
+      //
+      // copy image to array for finish cuts
+      //
+      fab_png_array(&v);
+      //
+      // create tool shape offset list
+      //
+      tool_ir = v.nx*offset_diameter/(2*v.dx);
+      tool_id = 2*tool_ir;
+      tool_dx = (int*) malloc(tool_id*tool_id*sizeof(int));
+      tool_dy = (int*) malloc(tool_id*tool_id*sizeof(int));
+      tool_iz = (int*) malloc(tool_id*tool_id*sizeof(int));
+      tool_pt = 0;
+      tool_n = 0;
+      for (ix = 0; ix < tool_id; ++ix)
+         for (iy = 0; iy < tool_id; ++iy)
+            if (sqrt((ix-tool_ir)*(ix-tool_ir)+(iy-tool_ir)*(iy-tool_ir)) < tool_ir) {
+               tool_dx[tool_pt] = ix-tool_ir;
+               tool_dy[tool_pt] = iy-tool_ir;
+               if (tool_type == 'f')
+                  tool_iz[tool_pt] = 0;
+               else if (tool_type == 'b') {
+                  tool_iz[tool_pt] = pow(2,v.bit_depth)*(tool_ir - sqrt(tool_ir*tool_ir
+                     - ((ix-tool_ir)*(ix-tool_ir) + (iy-tool_ir)*(iy-tool_ir))))/v.nz;
+                  }
+               else {
+                  printf("path.c: oops -- unsupported tool type\n");
+                  exit(-1);
+                  }
+               tool_pt += 1;
+               tool_n = tool_pt;
+               }
+      //
+      // create tool clearance offset list
+      //
+      clearance_ir = v.nx*clearance_diameter/(2*v.dx);
+      clearance_id = 2*clearance_ir;
+      clearance_dx = (int*) malloc(clearance_id*clearance_id*sizeof(int));
+      clearance_dy = (int*) malloc(clearance_id*clearance_id*sizeof(int));
+      clearance_iz = (int*) malloc(clearance_id*clearance_id*sizeof(int));
+      clearance_pt = 0;
+      clearance_n = 0;
+      for (ix = 0; ix < clearance_id; ++ix)
+         for (iy = 0; iy < clearance_id; ++iy)
+            if (((sqrt((ix-clearance_ir)*(ix-clearance_ir)+(iy-clearance_ir)*(iy-clearance_ir))) >= tool_ir)
+             && ((sqrt((ix-clearance_ir)*(ix-clearance_ir)+(iy-clearance_ir)*(iy-clearance_ir))) < clearance_ir)) {
+               clearance_dx[clearance_pt] = ix-clearance_ir;
+               clearance_dy[clearance_pt] = iy-clearance_ir;
+               clearance_iz[clearance_pt] = (pow(2,v.bit_depth)-1)*clearance_length/v.dz;
+               clearance_pt += 1;
+               clearance_n = clearance_pt;
+               }
+      //
+      // check for xz cut
+      //
+      if (xz == 1) {
+         //
+         // allocate xz finish cut array
+         //
+         vxz.nx = v.nx;
+         vxz.ny = v.nz;
+         vxz.nz = 1;
+         vxz.dx = v.dx;
+         vxz.dy = v.dz;
+         vxz.dz = 0;
+         vxz.xmin = v.xmin;
+         vxz.ymin = v.zmin;
+         vxz.zmin = 0;
+         vxz.array = malloc(v.nz*sizeof(uint32_t *));
+         for (iz = 0; iz < v.nz; ++iz)
+            vxz.array[iz] = malloc(v.nx*sizeof(uint32_t));
+         //
+         // start xz path
+         //
+         fab_path_start(&vxz,3);
+         //
+         // y loop
+         //
+         iymin = v.ny*clearance_diameter/(2.0*v.dy);
+         iymax = (v.ny-1) - v.ny*clearance_diameter/(2.0*v.dy);
+         idy = v.ny*offset_diameter*(1-offset_overlap)/v.dy;
+         printf("   xz finish:\n");
+         for (iy = iymin; iy <= iymax; (iy += idy)) {
+            printf("      y=%d/%d\n",iy,iymax);
+            //
+            // x loop
+            //
+            for (ix = 0; ix < vxz.nx; ++ix) {
+               //
+               // set z threshold
+               //
+               if ((ix <= clearance_ir) | (ix >= (vxz.nx-clearance_ir))) 
+                  //
+                  // too close to edge, set max
+                  //
+                  ithreshold = pow(2,v.bit_depth);
+               else {
+                  //
+                  // offset for tool shape
+                  //
+                  ithreshold = 0;
+                  for (tool_pt = 0; tool_pt < tool_n; ++tool_pt) {
+                     if (v.array[iy+tool_dy[tool_pt]][ix+tool_dx[tool_pt]] > (ithreshold+tool_iz[tool_pt]))
+                        ithreshold = v.array[iy+tool_dy[tool_pt]][ix+tool_dx[tool_pt]] - tool_iz[tool_pt];
+                     }
+                  //
+                  // check clearance
+                  //
+                  for (clearance_pt = 0; clearance_pt < clearance_n; ++clearance_pt) {
+                     if (v.array[iy+clearance_dy[clearance_pt]][ix+clearance_dx[clearance_pt]] > (ithreshold+clearance_iz[clearance_pt])) {
+                        ithreshold = v.array[iy+clearance_dy[clearance_pt]][ix+clearance_dx[clearance_pt]] - clearance_iz[clearance_pt];
+                        tool_collision = 1;
+                        }
+                     }
+                  }
+               //
+               // convert from image to lattice units
+               //
+               ithreshold = 1+(v.nz-2.0)*ithreshold/(pow(2,v.bit_depth)-1.0); // +1 for bottom edge
+               //
+               // z (array y) loop
+               //
+               for (iz = 0; iz < vxz.ny; ++iz) {
+                  if (iz <= ithreshold)
+                     vxz.array[iz][ix] = v.interior;
+                  else
+                     vxz.array[iz][ix] = v.empty;
+                  }
+               }
+            //
+            // set edge directions
+            //
+            fab_directions(&vxz);
+            //
+            // vectorize edge directions
+            //
+            fab_vectorize(&vxz,error,iy);
+            }
+         //
+         // copy xz path
+         //
+         vxz.path->segment = vxz.path->first;
+         while (1) {
+            //
+            // follow segment forward
+            //
+            vxz.path->segment->point = vxz.path->segment->first;
+            fab_path_segment(&v);
+            while (1) {
+               //
+               // follow points
+               //
+               fab_path_point(&v);
+               vxz.path->segment->point->axis = vxz.path->segment->point->first;
+               ix = vxz.path->segment->point->axis->value;
+               vxz.path->segment->point->axis = vxz.path->segment->point->axis->next;
+               iy = vxz.path->segment->point->axis->value;
+               vxz.path->segment->point->axis = vxz.path->segment->point->axis->next;
+               iz = vxz.path->segment->point->axis->value;
+               fab_path_axis(&v,ix);
+               fab_path_axis(&v,iz);
+               fab_path_axis(&v,iy);
+               if (vxz.path->segment->point->next == 0)
+                  break;
+               vxz.path->segment->point = vxz.path->segment->point->next;
+               }
+            if (vxz.path->segment->next == 0)
+               break;
+            vxz.path->segment = vxz.path->segment->next;
+            //
+            // follow segment backwards
+            //
+            vxz.path->segment->point = vxz.path->segment->last;
+            fab_path_segment(&v);
+            while (1) {
+               //
+               // follow points
+               //
+               fab_path_point(&v);
+               vxz.path->segment->point->axis = vxz.path->segment->point->first;
+               ix = vxz.path->segment->point->axis->value;
+               vxz.path->segment->point->axis = vxz.path->segment->point->axis->next;
+               iy = vxz.path->segment->point->axis->value;
+               vxz.path->segment->point->axis = vxz.path->segment->point->axis->next;
+               iz = vxz.path->segment->point->axis->value;
+               fab_path_axis(&v,ix);
+               fab_path_axis(&v,iz);
+               fab_path_axis(&v,iy);
+               if (vxz.path->segment->point->previous == 0)
+                  break;
+               vxz.path->segment->point = vxz.path->segment->point->previous;
+               }
+            if (vxz.path->segment->next == 0)
+               break;
+            vxz.path->segment = vxz.path->segment->next;
+            }
+         }
+      //
+      // check for yz cut
+      //
+      if (yz == 1) {
+         //
+         // allocate yz finish cut array
+         //
+         vyz.nx = v.ny;
+         vyz.ny = v.nz;
+         vyz.nz = 1;
+         vyz.dx = v.dy;
+         vyz.dy = v.dz;
+         vyz.dz = 0;
+         vyz.xmin = v.ymin;
+         vyz.ymin = v.zmin;
+         vyz.zmin = 0;
+         vyz.array = malloc(v.nz*sizeof(uint32_t *));
+         for (iz = 0; iz < v.nz; ++iz)
+            vyz.array[iz] = malloc(v.ny*sizeof(uint32_t));
+         //
+         // start yz path
+         //
+         fab_path_start(&vyz,3);
+         //
+         // x loop
+         //
+         ixmin = v.nx*clearance_diameter/(2.0*v.dx);
+         ixmax = (v.nx-1) - v.nx*clearance_diameter/(2.0*v.dx);
+         idx = v.nx*offset_diameter*(1-offset_overlap)/v.dx;
+         printf("   yz finish:\n");
+         for (ix = ixmax; ix >= ixmin; (ix -= idx)) {
+            printf("      x=%d/%d\n",ix,ixmax);
+            //
+            // y (array x) loop
+            //
+            for (iy = 0; iy < vyz.nx; ++iy) {
+               //
+               // set z threshold
+               //
+               if ((iy <= clearance_ir) | (iy >= (vyz.nx-clearance_ir))) 
+                  //
+                  // too close to edge, set max
+                  //
+                  ithreshold = pow(2,v.bit_depth);
+               else {
+                  //
+                  // offset for tool shape
+                  //
+                  ithreshold = 0;
+                  for (tool_pt = 0; tool_pt < tool_n; ++tool_pt) {
+                     if (v.array[iy+tool_dy[tool_pt]][ix+tool_dx[tool_pt]] > (ithreshold+tool_iz[tool_pt]))
+                        ithreshold = v.array[iy+tool_dy[tool_pt]][ix+tool_dx[tool_pt]] - tool_iz[tool_pt];
+                     }
+                  //
+                  // check clearance
+                  //
+                  for (clearance_pt = 0; clearance_pt < clearance_n; ++clearance_pt) {
+                     if (v.array[iy+clearance_dy[clearance_pt]][ix+clearance_dx[clearance_pt]] > (ithreshold+clearance_iz[clearance_pt])) {
+                        ithreshold = v.array[iy+clearance_dy[clearance_pt]][ix+clearance_dx[clearance_pt]] - clearance_iz[clearance_pt];
+                        tool_collision = 1;
+                        }
+                     }
+                  }
+               //
+               // convert from image to lattice units
+               //
+               ithreshold = 1+(v.nz-2.0)*ithreshold/(pow(2,v.bit_depth)-1.0); // +1 for bottom edge
+               //
+               // z (array y) loop
+               //
+               for (iz = 0; iz < vyz.ny; ++iz) {
+                  if (iz <= ithreshold)
+                     vyz.array[iz][iy] = v.interior;
+                  else
+                     vyz.array[iz][iy] = v.empty;
+                  }
+               }
+            //
+            // set edge directions
+            //
+            fab_directions(&vyz);
+            //
+            // vectorize edge directions
+            //
+            fab_vectorize(&vyz,error,ix);
+            }
+         //
+         // copy yz path
+         //
+         vyz.path->segment = vyz.path->first;
+         while (1) {
+            //
+            // follow segment forwards
+            //
+            vyz.path->segment->point = vyz.path->segment->first;
+            fab_path_segment(&v);
+            while (1) {
+               //
+               // follow points
+               //
+               fab_path_point(&v);
+               vyz.path->segment->point->axis = vyz.path->segment->point->first;
+               ix = vyz.path->segment->point->axis->value;
+               vyz.path->segment->point->axis = vyz.path->segment->point->axis->next;
+               iy = vyz.path->segment->point->axis->value;
+               vyz.path->segment->point->axis = vyz.path->segment->point->axis->next;
+               iz = vyz.path->segment->point->axis->value;
+               fab_path_axis(&v,iz);
+               fab_path_axis(&v,ix);
+               fab_path_axis(&v,iy);
+               if (vyz.path->segment->point->next == 0)
+                  break;
+               vyz.path->segment->point = vyz.path->segment->point->next;
+               }
+            if (vyz.path->segment->next == 0)
+               break;
+            vyz.path->segment = vyz.path->segment->next;
+            //
+            // follow segment backwards
+            //
+            vyz.path->segment->point = vyz.path->segment->last;
+            fab_path_segment(&v);
+            while (1) {
+               //
+               // follow points
+               //
+               fab_path_point(&v);
+               vyz.path->segment->point->axis = vyz.path->segment->point->first;
+               ix = vyz.path->segment->point->axis->value;
+               vyz.path->segment->point->axis = vyz.path->segment->point->axis->next;
+               iy = vyz.path->segment->point->axis->value;
+               vyz.path->segment->point->axis = vyz.path->segment->point->axis->next;
+               iz = vyz.path->segment->point->axis->value;
+               fab_path_axis(&v,iz);
+               fab_path_axis(&v,ix);
+               fab_path_axis(&v,iy);
+               if (vyz.path->segment->point->previous == 0)
+                  break;
+               vyz.path->segment->point = vyz.path->segment->point->previous;
+               }
+            if (vyz.path->segment->next == 0)
+               break;
+            vyz.path->segment = vyz.path->segment->next;
+            }
+         }
+      //
+      // check for xy cut
+      //
+      if (xy == 1) {
+         //
+         // z loop
+         //
+         for (layer = (nz-1); layer >= 0; --layer) {
+            if (v.dz == 0) {
+               z_layer = z_top;
+               intensity_layer = intensity_top;
+               layer_number = 0;
+               }
+            else {
+               z_layer = z_list[layer];
+               intensity_layer = intensity_bottom + (intensity_top-intensity_bottom)*(z_layer-z_bottom)/(z_top-z_bottom);
+               layer_number = v.nz*(z_layer-z_bottom)/(z_top-z_bottom);
+               }
+            printf("png_path: intensity %f, z %f\n",intensity_layer,z_layer);
+            //
+            // copy image to array
+            //
+            fab_png_array(&v);
+            //
+            // threshold array
+            //
+            printf("   threshold\n");
+            fab_threshold(&v,intensity_layer);
+            //
+            // find edges
+            //
+            printf("   find edges\n");
+            remaining_count = fab_edges(&v);
+            if (remaining_count == 0) {
+               printf("      no layer edges\n");
+               continue;
+               }
+            //
+            // find edge distances
+            //
+            printf("   find distances\n");
+            //test_fab_distances(&v);
+            fab_distances(&v);
+            //
+            // loop over contours
+            //
+            printf("   offset:\n");
+            offset_count = 0;
+            remaining_count_sum = 0;
+            do {
+               //
+               // offset
+               //
+               distance = offset_diameter/2.0 + offset_diameter*(1-offset_overlap)*offset_count;
+               remaining_count = fab_offset(&v,distance);
+               remaining_count_sum += remaining_count;
+               printf("      distance %f, %d exterior points remain\n",
+                  distance,remaining_count);
+               if (remaining_count != 0) {
+                  //
+                  // set edge directions
+                  //
+                  fab_directions(&v);
+                  //
+                  // vectorize edge directions
+                  //
+                  fab_vectorize(&v,error,layer_number);
+                  }
+               ++offset_count;
+               } while ((offset_count != offset_number) && (remaining_count != 0));
+            }
+         if (remaining_count_sum == 0) {
+            printf("png_path: no offset edges\n");
+            exit(1);
+            }
+         }
+      }
+   if (tool_collision == 1)
+      printf("   tool collisions\n");
+   //
+   // write path
+   //
+   fab_write_path(&v,argv[2]);
+   //
+   // exit
+   //
+   exit(0);
+   }
+
diff --git a/src/core/png_scale.c b/src/core/png_scale.c
new file mode 100644
index 0000000..fde1d99
--- /dev/null
+++ b/src/core/png_scale.c
@@ -0,0 +1,48 @@
+//
+// png_scale.c
+//    rescale PNG intensity 
+//
+// Neil Gershenfeld
+// CBA MIT 1/22/11
+//
+// (c) Massachusetts Institute of Technology 2010
+// Permission granted for experimental and personal use;
+// license for commercial sale available from MIT.
+//
+
+#include "fab.h"
+
+main(int argc, char **argv) {
+   //
+   // local vars
+   //
+   struct fab_vars v;
+   init_vars(&v);
+   float min,max;
+   //
+   // command line args
+   //
+   if (argc != 5) {
+      printf("command line: png_scale in.png out.png low high\n");
+      printf("   in.png = input PNG file\n");
+      printf("   out.png = output PNG file\n");
+      printf("   low = rescaled intensity minimum (0-1)\n");
+      printf("   high = rescaled intensity maximum (0-1)\n");
+      exit(-1);
+      }
+   //
+   // read PNG
+   //
+   fab_read_png(&v,argv[1]);
+   //
+   // rescale
+   //
+   sscanf(argv[3],"%f",&min);
+   sscanf(argv[4],"%f",&max);
+   fab_rescale(&v,min,max);
+   //
+   // write PNG
+   //
+   fab_write_png_K(&v,argv[2]);
+   }
+
diff --git a/src/core/png_size.c b/src/core/png_size.c
new file mode 100644
index 0000000..c441b60
--- /dev/null
+++ b/src/core/png_size.c
@@ -0,0 +1,59 @@
+//
+// png_size.c
+//    display and edit PNG size
+//
+// Neil Gershenfeld
+// CBA MIT 8/28/10
+//
+// (c) Massachusetts Institute of Technology 2010
+// Permission granted for experimental and personal use;
+// license for commercial sale available from MIT.
+//
+// todo
+//    variable units
+//    pipe I/O
+//    variable bit depth
+//
+
+#include "fab.h"
+
+main(int argc, char **argv) {
+   //
+   // local vars
+   //
+   struct fab_vars v;
+   init_vars(&v);
+   float dx,dy;
+   //
+   // command line args
+   //
+   if (!((argc == 2) || (argc == 3) || (argc == 4))) {
+      printf("command line: png_size in.png [dx [dy]]\n");
+      printf("   in.png = input PNG file\n");
+      printf("   dx = set width (optional, mm)\n");
+      printf("   dy = set height (optional, mm)\n");
+      exit(-1);
+      }
+   //
+   // read image
+   //
+   fab_read_png(&v,argv[1]);
+   //
+   // rescale if size given
+   //
+   if (argc == 3) {
+      sscanf(argv[2],"%f",&dx);
+      dy = v.dy * dx / ((float) v.dx);
+      v.dx = dx;
+      v.dy = dy;
+      fab_write_png_K(&v,argv[1]);
+      }
+   else if (argc == 4) {
+      sscanf(argv[2],"%f",&dx);
+      sscanf(argv[3],"%f",&dy);
+      v.dx = dx;
+      v.dy = dy;
+      fab_write_png_K(&v,argv[1]);
+      }
+   }
+
diff --git a/src/core/stl_info.c b/src/core/stl_info.c
new file mode 100644
index 0000000..af7dbf8
--- /dev/null
+++ b/src/core/stl_info.c
@@ -0,0 +1,35 @@
+//
+// stl_info.c
+//    report .stl info
+//
+// Neil Gershenfeld
+// CBA MIT 3/6/11
+//
+// (c) Massachusetts Institute of Technology 2011
+// Permission granted for experimental and personal use;
+// license for commercial sale available from MIT.
+//
+
+#include "fab.h"
+
+main(int argc, char **argv) {
+   //
+   // local vars
+   //
+   struct fab_vars v;
+   init_vars(&v);
+   float units,resolution;
+   char axis;
+   //
+   // command line args
+   //
+   if (argc != 2) {
+      printf("command line: stl_info in.stl\n");
+      printf("   in.stl = input binary STL file\n");
+      exit(-1);
+      }
+   //
+   //  read .stl
+   //
+   fab_read_stl(&v,argv[1]);
+   }
diff --git a/src/core/stl_path.c b/src/core/stl_path.c
new file mode 100644
index 0000000..061d092
--- /dev/null
+++ b/src/core/stl_path.c
@@ -0,0 +1,64 @@
+//
+// stl_path.c
+//    convert .stl to .path 
+//
+// Neil Gershenfeld 10/4/13
+// (c) Massachusetts Institute of Technology 2013
+//
+// This work may be reproduced, modified, distributed,
+// performed, and displayed for any purpose, but must
+// acknowledge the fab modules project. Copyright is
+// retained and must be preserved. The work is provided
+// as is; no warranty is provided, and users accept all 
+// liability.
+//
+
+#include "fab.h"
+
+int main(int argc, char **argv) {
+   //
+   // local vars
+   //
+   struct fab_vars v;
+   init_vars(&v);
+   float units,resolution;
+   //
+   // command line args
+   //
+   if (!((argc == 3) || (argc == 4) || (argc == 5))) {
+      printf("command line: stl_path in.stl out.path [units [resolution]]]\n");
+      printf("   in.stl = input binary STL file\n");
+      printf("   out.png = output PNG file\n");
+      printf("   units = file units (optional, mm/unit, default 1)\n");
+      printf("   resolution = image resolution (optional, pixels/mm, default 10)\n");
+      exit(-1);
+      }
+   if (argc == 3) {
+      units = 1;
+      resolution = 10;
+      }
+   else if (argc == 4) {
+      sscanf(argv[3],"%f",&units);
+      resolution = 10;
+      }
+   else if (argc == 5) {
+      sscanf(argv[3],"%f",&units);
+      sscanf(argv[4],"%f",&resolution);
+      }
+   //
+   //  read .stl
+   //
+   fab_read_stl(&v,argv[1]);
+   //
+   // convert mesh to path
+   //
+   fab_mesh_path(&v,units,resolution);
+   //
+   //  write .path
+   //
+   fab_write_path(&v,argv[2]);
+   //
+   // return
+   //
+   return(0);
+   }
diff --git a/src/core/stl_png.c b/src/core/stl_png.c
new file mode 100644
index 0000000..8b8c9cb
--- /dev/null
+++ b/src/core/stl_png.c
@@ -0,0 +1,76 @@
+//
+// stl_png.c
+//    convert STL to PNG
+//
+// Neil Gershenfeld 10/4/13
+// (c) Massachusetts Institute of Technology 2013
+//
+// This work may be reproduced, modified, distributed,
+// performed, and displayed for any purpose, but must
+// acknowledge the fab modules project. Copyright is
+// retained and must be preserved. The work is provided
+// as is; no warranty is provided, and users accept all 
+// liability.
+//
+
+#include "fab.h"
+
+main(int argc, char **argv) {
+   //
+   // local vars
+   //
+   struct fab_vars v;
+   init_vars(&v);
+   float units,resolution;
+   char axis;
+   //
+   // command line args
+   //
+   if (!((argc == 3) || (argc == 4) || (argc == 5) || (argc == 6))) {
+      printf("command line: stl_png in.stl out.png [units [resolution [axis]]]\n");
+      printf("   in.stl = input binary STL file\n");
+      printf("   out.png = output PNG file\n");
+      printf("   units = file units (optional, mm/unit, default 1)\n");
+      printf("   resolution = image resolution (optional, pixels/mm, default 10)\n");
+      printf("   axis = projection axis (optional, top or bottom, x|X|y|Y|z|Z, default z)\n");
+      exit(-1);
+      }
+   if (argc == 3) {
+      units = 1;
+      resolution = 10;
+      axis = 'z';
+      }
+   else if (argc == 4) {
+      sscanf(argv[3],"%f",&units);
+      resolution = 10;
+      axis = 'z';
+      }
+   else if (argc == 5) {
+      sscanf(argv[3],"%f",&units);
+      sscanf(argv[4],"%f",&resolution);
+      axis = 'z';
+      }
+   else if (argc == 6) {
+      sscanf(argv[3],"%f",&units);
+      sscanf(argv[4],"%f",&resolution);
+      sscanf(argv[5],"%c",&axis);
+      }
+   //
+   //  read STL
+   //
+   fab_read_stl(&v,argv[1]);
+   //
+   // draw mesh into array
+   //
+   fab_shade_mesh(&v,units,resolution,axis);
+   //
+   // move origin to corner
+   //
+   v.xmin = 0;
+   v.ymin = 0;
+   v.zmin = 0;
+   //
+   //  write PNG
+   //
+   fab_write_png_K(&v,argv[2]);
+   }
diff --git a/src/core/svg_path.c b/src/core/svg_path.c
new file mode 100644
index 0000000..886a001
--- /dev/null
+++ b/src/core/svg_path.c
@@ -0,0 +1,1255 @@
+//
+// svg_path.c
+//    convert SVG to path
+//
+// Neil Gershenfeld 9/21/13
+// (c) Massachusetts Institute of Technology 2013
+//
+// This work may be reproduced, modified, distributed,
+// performed, and displayed for any purpose, but must
+// acknowledge the fab modules project. Copyright is
+// retained and must be preserved. The work is provided
+// as is; no warranty is provided, and users accept all 
+// liability.
+//
+// todo
+//    gradient intensity
+//
+
+#include "fab.h"
+
+#define SVG_MAX_FILE 1000000
+#define SVG_MAX_TRANSFORM 100
+
+double angle(double ux, double uy, double vx, double vy) {
+   double sign = (((ux*vy-uy*vx) > 0) ? 1 : -1);
+   return (sign*acos((ux*vx+uy*vy)/(sqrt(ux*ux+uy*uy)*sqrt(vx*vx+vy*vy))));
+   }
+   
+void parse_number_units(char *ptr, double *number, double *unit_scale, char *units) {
+   //
+   // parse quoted number and units
+   // return scale in mm/unit
+   // 
+   char *start,*end;
+   start = strstr(ptr,"\"");
+   end = strstr(start+1,"\"");
+   char c1,c2;
+   c1 = *(end-1);
+   c2 = *(end-2);
+   if ((((c2 >= '0') && (c2 <= '9')) || (c2 == '.'))
+   && (((c1 >= '0') && (c1 <= '9')) || (c1 == '.'))) {
+      *number = strtod(start+1,NULL);
+      strcpy(units,"px");
+      }
+   else {
+      units[0] = *(end-2);
+      units[1] = *(end-1);
+      units[2] = 0;
+      *(end-2) = ' ';
+      *(end-1) = ' ';
+      *number = strtod(start+1,NULL);
+      }
+   if (0 == strncmp(units,"px",2))
+      *unit_scale = 25.4*1.0/90.0; // Inkscape px default 90/inch
+   else if (0 == strncmp(units,"pt",2))
+      *unit_scale = 25.4*1.0/72.0;
+   else if (0 == strncmp(units,"in",2))
+      *unit_scale = 25.4;
+   else if (0 == strncmp(units,"mm",2))
+      *unit_scale = 1.0;
+   else if (0 == strncmp(units,"cm",2))
+      *unit_scale = 10.0;
+   else {
+      printf("svg_path: oops -- don't recognize unit %s\n",units);
+      exit(-1);
+      }
+   }
+
+void next_number(char **ptr, double *number) {
+   //
+   // return next number after pointer
+   //
+   char haystack[] = "0123456789.-+";
+   char *end;
+   //
+   // find start
+   //
+   while (1) {
+      if (strchr(haystack,**ptr) != NULL)
+         break;
+      *ptr += 1;
+      }
+   //
+   // find end
+   //
+   end = *ptr;
+   while (1) {
+      if (strchr(haystack,*end) == NULL)
+         break;
+      end += 1;
+      }
+   //
+   // move pointer to end and return number
+   //
+   *number = strtod(*ptr,&end);
+   *ptr = end;
+   }
+
+char next_element(char **ptr, char current_element) {
+   //
+   // return next path element after pointer
+   //
+   char number_haystack[] = "0123456789.-+";
+   char element_haystack[] = "mMlLhHvVcCsSaAzZ\"";
+   while (1) {
+      if (strchr(element_haystack,**ptr) != NULL)
+         return(**ptr);
+      else if (strchr(number_haystack,**ptr) != NULL)
+         return(current_element);
+      else
+         *ptr += 1;
+      }
+   }
+
+void clear_transform(int *transform) {
+   //
+   // pop current transform from stack
+   //
+   *transform -= 1;
+   }
+
+int hex_int(char chr) {
+   //
+   // return hex for char
+   //
+   if (chr == '0') return 0;
+   else if (chr == '1') return 1;
+   else if (chr == '2') return 2;
+   else if (chr == '3') return 3;
+   else if (chr == '4') return 4;
+   else if (chr == '5') return 5;
+   else if (chr == '6') return 6;
+   else if (chr == '7') return 7;
+   else if (chr == '8') return 8;
+   else if (chr == '9') return 9;
+   else if ((chr == 'a') || (chr == 'A')) return 10;
+   else if ((chr == 'b') || (chr == 'B')) return 11;
+   else if ((chr == 'c') || (chr == 'C')) return 12;
+   else if ((chr == 'd') || (chr == 'D')) return 13;
+   else if ((chr == 'e') || (chr == 'e')) return 14;
+   else if ((chr == 'f') || (chr == 'F')) return 15;
+   printf("svg_path: oops -- non-hex char\n");
+   exit(-1);
+   }
+
+void set_intensity(char *start, int *zn, struct fab_vars *v, int zsign) {
+   char *end,*ptr;
+   int r,g,b;
+   float i;
+   //
+   // set zn from element intensity
+   //
+   end = strstr(start+1,">");
+   ptr = strstr(start,"stroke:");
+   if ((ptr != NULL) && (ptr < end)) {
+      //
+      // stroke found, check for hex encoding
+      //
+      ptr = strstr(ptr,"#");
+      if ((ptr != NULL) && (ptr < end)) {
+         //
+         // hex encoding found, set intensity
+         //
+         r = 16*hex_int(ptr[1]) + hex_int(ptr[2]);
+         g = 16*hex_int(ptr[3]) + hex_int(ptr[4]);
+         b = 16*hex_int(ptr[5]) + hex_int(ptr[6]);
+         if (zsign == 1)
+            i = (r+g+b)/(255.0+255.0+255.0);
+         else
+            i = 1.0 - (r+g+b)/(255.0+255.0+255.0);
+         *zn = i * (v->nz - 1);
+         }
+      else {
+         //
+         // hex encoding not found, set to bottom
+         //
+         *zn = 0;
+         }
+      }
+   else {
+      //
+      // stroke not found, set to bottom
+      //
+      *zn = 0;
+      }
+   }
+
+void set_transform(char *start, int *transform, double transforms[SVG_MAX_TRANSFORM][6]) {
+   double a,b,c,d,e,f;
+   double pi = 4*atan(1.0);
+   char *end,*next,*ptr;
+   //
+   // push transform for current element onto stack
+   //
+   end = strstr(start+1,">");
+   next = strstr(start+1,"<");
+   if ((next != NULL) && (next < end))
+      end = next;
+   //
+   // check for transform
+   //
+   ptr = strstr(start,"transform");
+   if ((ptr != NULL) && (ptr < end)) {
+      //
+      // found, check for matrix
+      //
+      ptr = strstr(start,"matrix(");
+      if ((ptr != NULL) && (ptr < end)) {
+         //printf("trans: matrix\n");
+         next_number(&ptr,&a);
+         next_number(&ptr,&b);
+         next_number(&ptr,&c);
+         next_number(&ptr,&d);
+         next_number(&ptr,&e);
+         next_number(&ptr,&f);
+         transforms[*transform][0] = a;
+         transforms[*transform][1] = b;
+         transforms[*transform][2] = c;
+         transforms[*transform][3] = d;
+         transforms[*transform][4] = e;
+         transforms[*transform][5] = f;
+         *transform += 1;
+         return;
+         }
+      //
+      // check for translate
+      //
+      ptr = strstr(start,"translate(");
+      if ((ptr != NULL) && (ptr < end)) {
+         //printf("trans: translate\n");
+         next_number(&ptr,&e);
+         next_number(&ptr,&f);
+         transforms[*transform][0] = 1;
+         transforms[*transform][1] = 0;
+         transforms[*transform][2] = 0;
+         transforms[*transform][3] = 1;
+         transforms[*transform][4] = e;
+         transforms[*transform][5] = f;
+         *transform += 1;
+         return;
+         }
+      //
+      // check for scale
+      //
+      ptr = strstr(start,"scale(");
+      if ((ptr != NULL) && (ptr < end)) {
+         //printf("trans: scale\n");
+         next_number(&ptr,&a);
+         transforms[*transform][0] = a;
+         transforms[*transform][1] = 0;
+         transforms[*transform][2] = 0;
+         transforms[*transform][3] = a;
+         transforms[*transform][4] = 0;
+         transforms[*transform][5] = 0;
+         *transform += 1;
+         return;
+         }
+      //
+      // check for rotate
+      //
+      ptr = strstr(start,"rotate(");
+      if ((ptr != NULL) && (ptr < end)) {
+         //printf("trans: rotate\n");
+         next_number(&ptr,&a);
+         transforms[*transform][0] = cos(a*pi/180.0);
+         transforms[*transform][1] = sin(a*pi/180.0);
+         transforms[*transform][2] = -sin(a*pi/180.0);
+         transforms[*transform][3] = cos(a*pi/180.0);
+         transforms[*transform][4] = 0;
+         transforms[*transform][5] = 0;
+         *transform += 1;
+         return;
+         }
+      //
+      // check for skewX
+      //
+      ptr = strstr(start,"skewX(");
+      if ((ptr != NULL) && (ptr < end)) {
+         //printf("trans: skewX\n");
+         next_number(&ptr,&a);
+         transforms[*transform][0] = 1;
+         transforms[*transform][1] = 0;
+         transforms[*transform][2] = tan(a*pi/180.0);
+         transforms[*transform][3] = 1;
+         transforms[*transform][4] = 0;
+         transforms[*transform][5] = 0;
+         *transform += 1;
+         return;
+         }
+      //
+      // check for skewY
+      //
+      ptr = strstr(start,"skewY(");
+      if ((ptr != NULL) && (ptr < end)) {
+         //printf("trans: skewY\n");
+         next_number(&ptr,&a);
+         transforms[*transform][0] = 1;
+         transforms[*transform][1] = tan(a*pi/180.0);
+         transforms[*transform][2] = 0;
+         transforms[*transform][3] = 1;
+         transforms[*transform][4] = 0;
+         transforms[*transform][5] = 0;
+         *transform += 1;
+         return;
+         }
+      //
+      // didn't find transform
+      //
+      printf("svg_path: oops -- didn't find transform\n");
+      transforms[*transform][0] = 1;
+      transforms[*transform][1] = 0;
+      transforms[*transform][2] = 0;
+      transforms[*transform][3] = 1;
+      transforms[*transform][4] = 0;
+      transforms[*transform][5] = 0;
+      *transform += 1;
+      return;
+      }
+   else {
+      //
+      // transform not found, set unit transform
+      //
+      transforms[*transform][0] = 1;
+      transforms[*transform][1] = 0;
+      transforms[*transform][2] = 0;
+      transforms[*transform][3] = 1;
+      transforms[*transform][4] = 0;
+      transforms[*transform][5] = 0;
+      *transform += 1;
+      return;
+      }
+   }
+
+void path_point(int *xn, int *yn, double x, double y, int transform, double transforms[SVG_MAX_TRANSFORM][6],
+   double xnscale, double ynscale, double xnmid, double vxmid, double ynmid, double vymid) {
+   double xt,yt,xtemp,ytemp;
+   int t;
+   //
+   // return path point
+   //
+   xt = x;
+   yt = y;
+   for (t = (transform-1); t >= 0; --t) {
+      xtemp = transforms[t][0]*xt + transforms[t][2]*yt + transforms[t][4];
+      ytemp = transforms[t][1]*xt + transforms[t][3]*yt + transforms[t][5];
+      xt = xtemp;
+      yt = ytemp;
+      }
+   *xn = xnmid + xnscale*(xt - vxmid);
+   *yn = ynmid + ynscale*(yt - vymid);
+   }
+
+void fab_read_svg(struct fab_vars *v, char *input_file_name, float scale, int points, int resolution, float zmin, float zmax) {
+   //
+   // read SVG into fab_vars
+   //
+	FILE *input_file;
+   char buf[SVG_MAX_FILE];
+   char units[3];
+   char current_element,last_element;
+   int point,ret;
+   char *ptr,*start,*stop,*endptr;
+   int transform;
+   double transforms[SVG_MAX_TRANSFORM][6];
+   double unit_scale;
+   double xnscale,ynscale,xnmid,ynmid;
+   double vxmin,vymin,vxmid,vymid,vwidth,vheight,vaspect;
+   double width,height,aspect;
+   double x,y,z,x0,y0,x1,y1,x2,y2;
+   double rx,ry,theta,theta_0,theta_1,theta_diff,rotation,large_arc,sweep;
+   double phi,x1p,y1p,sign,cxp,cyp;
+   double ax,bx,cx,ay,by,cy,xt,yt,t,r;
+   double pi;
+   int xn,yn,zn;
+   int zsign;
+   int count;
+   pi = 4*atan(1.0);
+   //
+   // read SVG file
+   //
+   input_file = fopen(input_file_name, "rb");
+   if (input_file == 0) {
+      printf("svg_path.c: oops -- can't open %s\n",input_file_name);
+      exit(-1);
+      }
+   ret = fread(buf,1,SVG_MAX_FILE,input_file);
+   if (ret >= SVG_MAX_FILE) {
+      printf("svg_path: oops -- exceeded SVG buffer size\n");
+      exit(-1);
+      }
+   endptr = buf + ret;
+   fclose(input_file);
+   printf("read %d bytes from %s\n",ret,input_file_name);
+   //
+   // find SVG element
+   //
+   ptr = strstr(buf,"");
+   //
+   // check for width and height
+   //
+   start = strstr(ptr,"width=");
+   if ((start == NULL) || (start > stop)) {
+      printf("svg_path: oops -- no width\n");
+      exit(-1);
+      }
+   parse_number_units(start,&width,&unit_scale,units);
+   printf("   width %f %s\n",width,units);
+   start = strstr(ptr,"height=");
+   if ((start == NULL) || (start > stop)) {
+      printf("svg_path: oops -- no height\n");
+      exit(-1);
+      }
+   parse_number_units(start,&height,&unit_scale,units);
+   printf("   height %f %s\n",height,units);
+   //
+   // check for viewBox
+   //
+   start = strstr(ptr,"viewBox=");
+   if ((start != NULL) && (start < stop)) {
+      next_number(&start,&vxmin);
+      next_number(&start,&vymin);
+      next_number(&start,&vwidth);
+      next_number(&start,&vheight);
+      printf("   view x min %f\n",vxmin);
+      printf("   view y min %f\n",vymin);
+      printf("   view width %f\n",vwidth);
+      printf("   view height %f\n",vheight);
+      aspect = height/width;
+      vaspect = vheight/vwidth;
+      vxmid = vxmin + vwidth/2.0;
+      vymid = vymin + vheight/2.0;
+      // assume xMidYMid meet scaling
+      if (vaspect > aspect) {
+         ynscale = resolution*aspect/vheight;
+         xnscale = ynscale;
+         }
+      else {
+         xnscale = resolution/vwidth;
+         ynscale = xnscale;
+         }
+      xnmid = resolution/2.0;
+      ynmid = aspect*xnmid;
+      }
+   else {
+      vxmid = width/2.0;
+      vymid = height/2.0;
+      xnscale = resolution/width;
+      ynscale = xnscale;
+      xnmid = xnscale*width/2.0;
+      ynmid = ynscale*height/2.0;
+      }
+   //
+   // start path
+   //
+   v->nx = 2*xnmid;
+   v->ny = 2*ynmid;
+   v->dx = scale*width*unit_scale;
+   v->dy = scale*height*unit_scale;
+   v->xmin = 0;
+   v->ymin = 0;
+   zsign = 1;
+   if (v->path->dof > 2) {
+      if (zmin > zmax) {
+         z = zmin;
+         zmin = zmax;
+         zmax = z;
+         zsign = -1;
+         }
+      v->nz = 1+xnscale*(zmax-zmin);
+      v->dz = zmax-zmin;
+      v->zmin = zmin;
+      }
+   //
+   // find graphic elements
+   //
+   ptr = buf;
+   transform = 0;
+   do {
+      ptr += 1;
+      if (strncmp(ptr,"",2) == 0) {
+         //
+         // closing g, clear transform
+         //
+         clear_transform(&transform);
+         }
+      else if (strncmp(ptr,"",2) == 0)
+               //
+               // self-closing
+               //
+               break;
+            else if (strncmp(ptr,">",1) == 0) {
+               //
+               // not self-closing
+               //
+               while (1) {
+                  ptr += 1;
+                  if (strncmp(ptr,"",7) == 0)
+                     break;
+                  }
+               break;
+               }
+            }
+         printf("   svg_path: defs not yet implemented\n");
+         }
+      else if (strncmp(ptr,"",2) == 0)
+               //
+               // self-closing
+               //
+               break;
+            else if (strncmp(ptr,">",1) == 0) {
+               //
+               // not self-closing
+               //
+               while (1) {
+                  ptr += 1;
+                  if (strncmp(ptr,"",8) == 0)
+                     break;
+                  }
+               break;
+               }
+            }
+         printf("   svg_path: image not yet implemented\n");
+         }
+      else if (strncmp(ptr,"path->dof > 2)
+                  fab_path_axis(v,zn);
+               }
+            else if (current_element == 'M') {
+               //
+               // absolute moveto
+               //
+               next_number(&ptr,&x0);
+               next_number(&ptr,&y0);
+               //printf("  path M: %f %f\n",x0,y0);
+               current_element = 'L';
+               fab_path_segment(v);
+               fab_path_point(v);
+               path_point(&xn,&yn,x0,y0,transform,transforms,xnscale,ynscale,xnmid,vxmid,ynmid,vymid);
+               fab_path_axis(v,xn);
+               fab_path_axis(v,yn);
+               if (v->path->dof > 2)
+                  fab_path_axis(v,zn);
+               }
+            else if (current_element == 'l') {
+               //
+               // relative lineto
+               //
+               next_number(&ptr,&x);
+               next_number(&ptr,&y);
+               //printf("   path l: %f %f\n",x,y);
+               current_element = 'l';
+               fab_path_point(v);
+               path_point(&xn,&yn,x0+x,y0+y,transform,transforms,xnscale,ynscale,xnmid,vxmid,ynmid,vymid);
+               fab_path_axis(v,xn);
+               fab_path_axis(v,yn);
+               if (v->path->dof > 2)
+                  fab_path_axis(v,zn);
+               x0 = x0+x;
+               y0 = y0+y;
+               }
+            else if (current_element == 'L') {
+               //
+               // absolute lineto
+               //
+               next_number(&ptr,&x);
+               next_number(&ptr,&y);
+               //printf("   path L: %f %f\n",x,y);
+               current_element = 'L';
+               fab_path_point(v);
+               path_point(&xn,&yn,x,y,transform,transforms,xnscale,ynscale,xnmid,vxmid,ynmid,vymid);
+               fab_path_axis(v,xn);
+               fab_path_axis(v,yn);
+               if (v->path->dof > 2)
+                  fab_path_axis(v,zn);
+               x0 = x;
+               y0 = y;
+               }
+            else if (current_element == 'h') {
+               //
+               // relative horizontal
+               //
+               next_number(&ptr,&x);
+               //printf("   path h: %f\n",x);
+               current_element = 'j';
+               fab_path_point(v);
+               path_point(&xn,&yn,x0+x,y0,transform,transforms,xnscale,ynscale,xnmid,vxmid,ynmid,vymid);
+               fab_path_axis(v,xn);
+               fab_path_axis(v,yn);
+               if (v->path->dof > 2)
+                  fab_path_axis(v,zn);
+               x0 = x0+x;
+               }
+            else if (current_element == 'H') {
+               //
+               // absolute horizontal
+               //
+               next_number(&ptr,&x);
+               //printf("   path H: %f\n",x);
+               current_element = 'H';
+               fab_path_point(v);
+               path_point(&xn,&yn,x,y0,transform,transforms,xnscale,ynscale,xnmid,vxmid,ynmid,vymid);
+               fab_path_axis(v,xn);
+               fab_path_axis(v,yn);
+               if (v->path->dof > 2)
+                  fab_path_axis(v,zn);
+               x0 = x;
+               }
+            else if (current_element == 'v') {
+               //
+               // relative vertical
+               //
+               next_number(&ptr,&y);
+               //printf("   path v: %f\n",y);
+               current_element = 'v';
+               fab_path_point(v);
+               path_point(&xn,&yn,x0,y0+y,transform,transforms,xnscale,ynscale,xnmid,vxmid,ynmid,vymid);
+               fab_path_axis(v,xn);
+               fab_path_axis(v,yn);
+               if (v->path->dof > 2)
+                  fab_path_axis(v,zn);
+               y0 = y0+y;
+               }
+            else if (current_element == 'V') {
+               //
+               // absolute vertical
+               //
+               next_number(&ptr,&y);
+               //printf("   path V: %f\n",y);
+               current_element = 'V';
+               fab_path_point(v);
+               path_point(&xn,&yn,x0,y,transform,transforms,xnscale,ynscale,xnmid,vxmid,ynmid,vymid);
+               fab_path_axis(v,xn);
+               fab_path_axis(v,yn);
+               if (v->path->dof > 2)
+                  fab_path_axis(v,zn);
+               y0 = y;
+               }
+            else if (current_element == 'c') {
+               //
+               // relative curveto
+               //
+               next_number(&ptr,&x1);
+               x1 += x0;
+               next_number(&ptr,&y1);
+               y1 += y0;
+               next_number(&ptr,&x2);
+               x2 += x0;
+               next_number(&ptr,&y2);
+               y2 += y0;
+               next_number(&ptr,&x);
+               x += x0;
+               next_number(&ptr,&y);
+               y += y0;
+               //printf("   path c: %f %f %f %f %f %f\n",x1,y1,x2,y2,x,y);
+               current_element = 'c';
+               cx = 3 * (x1 - x0);
+               bx = 3 * (x2 - x1) - cx;
+               ax = x - x0 - cx - bx;
+               cy = 3 * (y1 - y0);
+               by = 3 * (y2 - y1) - cy;
+               ay = y - y0 - cy - by;
+               for (point = 0; point < points; ++point) {
+                  t = point / (points - 1.0);
+                  xt = ax*t*t*t + bx*t*t + cx*t + x0;
+                  yt = ay*t*t*t + by*t*t + cy*t + y0;
+                  fab_path_point(v);
+                  path_point(&xn,&yn,xt,yt,transform,transforms,xnscale,ynscale,xnmid,vxmid,ynmid,vymid);
+                  fab_path_axis(v,xn);
+                  fab_path_axis(v,yn);
+                  if (v->path->dof > 2)
+                     fab_path_axis(v,zn);
+                  }
+               x0 = x;
+               y0 = y;
+               }
+            else if (current_element == 'C') {
+               //
+               // absolute curveto
+               //
+               next_number(&ptr,&x1);
+               next_number(&ptr,&y1);
+               next_number(&ptr,&x2);
+               next_number(&ptr,&y2);
+               next_number(&ptr,&x);
+               next_number(&ptr,&y);
+               //printf(" path C: %f %f %f %f %f %f\n",x1,y1,x2,y2,x,y);
+               current_element = 'C';
+               cx = 3 * (x1 - x0);
+               bx = 3 * (x2 - x1) - cx;
+               ax = x - x0 - cx - bx;
+               cy = 3 * (y1 - y0);
+               by = 3 * (y2 - y1) - cy;
+               ay = y - y0 - cy - by;
+               for (point = 0; point < points; ++point) {
+                  t = point / (points - 1.0);
+                  xt = ax*t*t*t + bx*t*t + cx*t + x0;
+                  yt = ay*t*t*t + by*t*t + cy*t + y0;
+                  fab_path_point(v);
+                  path_point(&xn,&yn,xt,yt,transform,transforms,xnscale,ynscale,xnmid,vxmid,ynmid,vymid);
+                  fab_path_axis(v,xn);
+                  fab_path_axis(v,yn);
+                  if (v->path->dof > 2)
+                     fab_path_axis(v,zn);
+                  }
+               x0 = x;
+               y0 = y;
+               }
+            else if (current_element == 's') {
+               //
+               // relative smooth curveto
+               //
+               if ((last_element == 'c') || (last_element == 'C') || (last_element == 's') || (last_element == 'S')) {
+                  x1 = x0 + (x0-x2);
+                  y1 = y0 + (y0-y2);
+                  }
+               else {
+                  x1 = x0;
+                  y1 = y0;
+                  }
+               next_number(&ptr,&x2);
+               x2 += x0;
+               next_number(&ptr,&y2);
+               y2 += y0;
+               next_number(&ptr,&x);
+               x += x0;
+               next_number(&ptr,&y);
+               y += y0;
+               //printf("   path s: %f %f %f %f\n",x2,y2,x,y);
+               current_element = 'c';
+               cx = 3 * (x1 - x0);
+               bx = 3 * (x2 - x1) - cx;
+               ax = x - x0 - cx - bx;
+               cy = 3 * (y1 - y0);
+               by = 3 * (y2 - y1) - cy;
+               ay = y - y0 - cy - by;
+               for (point = 0; point < points; ++point) {
+                  t = point / (points - 1.0);
+                  xt = ax*t*t*t + bx*t*t + cx*t + x0;
+                  yt = ay*t*t*t + by*t*t + cy*t + y0;
+                  fab_path_point(v);
+                  path_point(&xn,&yn,xt,yt,transform,transforms,xnscale,ynscale,xnmid,vxmid,ynmid,vymid);
+                  fab_path_axis(v,xn);
+                  fab_path_axis(v,yn);
+                  if (v->path->dof > 2)
+                     fab_path_axis(v,zn);
+                  }
+               x0 = x;
+               y0 = y;
+               }
+            else if (current_element == 'S') {
+               //
+               // absolute smooth curveto
+               //
+               if ((last_element == 'c') || (last_element == 'C') || (last_element == 's') || (last_element == 'S')) {
+                  x1 = x0 + (x0-x2);
+                  y1 = y0 + (y0-y2);
+                  }
+               else {
+                  x1 = x0;
+                  y1 = y0;
+                  }
+               next_number(&ptr,&x2);
+               next_number(&ptr,&y2);
+               next_number(&ptr,&x);
+               next_number(&ptr,&y);
+               //printf(" path S: %f %f %f %f\n",x2,y2,x,y);
+               current_element = 'C';
+               cx = 3 * (x1 - x0);
+               bx = 3 * (x2 - x1) - cx;
+               ax = x - x0 - cx - bx;
+               cy = 3 * (y1 - y0);
+               by = 3 * (y2 - y1) - cy;
+               ay = y - y0 - cy - by;
+               for (point = 0; point < points; ++point) {
+                  t = point / (points - 1.0);
+                  xt = ax*t*t*t + bx*t*t + cx*t + x0;
+                  yt = ay*t*t*t + by*t*t + cy*t + y0;
+                  fab_path_point(v);
+                  path_point(&xn,&yn,xt,yt,transform,transforms,xnscale,ynscale,xnmid,vxmid,ynmid,vymid);
+                  fab_path_axis(v,xn);
+                  fab_path_axis(v,yn);
+                  if (v->path->dof > 2)
+                     fab_path_axis(v,zn);
+                  }
+               x0 = x;
+               y0 = y;
+               }
+            else if (current_element == 'a') {
+               //
+               // relative arc
+               //
+               next_number(&ptr,&rx);
+               next_number(&ptr,&ry);
+               next_number(&ptr,&rotation);
+               next_number(&ptr,&large_arc);
+               next_number(&ptr,&sweep);
+               next_number(&ptr,&x);
+               x += x0;
+               next_number(&ptr,&y);
+               y += y0;
+               //printf("   path a: %f %f %f %f %f %f %f\n",rx,ry,rotation,large_arc,sweep,x,y);
+               current_element = 'a';
+               phi = rotation*pi/180.0;
+               x1 = x0;
+               x2 = x;
+               y1 = y0;
+               y2 = y;
+               x1p = cos(phi)*(x1-x2)/2.0 + sin(phi)*(y1-y2)/2.0;
+               y1p = -sin(phi)*(x1-x2)/2.0 + cos(phi)*(y1-y2)/2.0;
+               sign = ((large_arc == sweep) ? -1 : 1);
+               if ((rx*rx*ry*ry - rx*rx*y1p*y1p - ry*ry*x1p*x1p) < 0) {
+                  cxp = 0;
+                  cyp = 0;
+                  }
+               else {
+                  cxp = sign * sqrt((rx*rx*ry*ry - rx*rx*y1p*y1p - ry*ry*x1p*x1p)/(rx*rx*y1p*y1p + ry*ry*x1p*x1p)) * rx*y1p/ry;
+                  cyp = -1 * sign * sqrt((rx*rx*ry*ry - rx*rx*y1p*y1p - ry*ry*x1p*x1p)/(rx*rx*y1p*y1p + ry*ry*x1p*x1p)) * ry*x1p/rx;
+                  }
+               cx = cos(phi)*cxp - sin(phi)*cyp + (x1+x2)/2.0;
+               cy = sin(phi)*cxp + cos(phi)*cyp + (y1+y2)/2.0;
+               theta_0 = angle(1,0,(x1p-cxp)/rx,(y1p-cyp)/ry);
+               theta_diff = angle ((x1p-cxp)/rx,(y1p-cyp)/ry,(-x1p-cxp)/rx,(-y1p-cyp)/ry);
+               theta_1 = theta_0 + theta_diff;
+               if (large_arc == 1) {
+                  if (sweep == 0)
+                     theta_1 -= 2*pi;
+                  else
+                     theta_1 += 2*pi;
+                  }
+               for (point = 0; point < points; ++point) {
+                  theta = theta_0 + (theta_1-theta_0) * point / (points - 1.0);
+                  xt = cx + rx*cos(theta);
+                  yt = cy + ry*sin(theta);
+                  fab_path_point(v);
+                  path_point(&xn,&yn,xt,yt,transform,transforms,xnscale,ynscale,xnmid,vxmid,ynmid,vymid);
+                  fab_path_axis(v,xn);
+                  fab_path_axis(v,yn);
+                  if (v->path->dof > 2)
+                     fab_path_axis(v,zn);
+                  }
+               x0 = x;
+               y0 = y;
+               }
+            else if (current_element == 'A') {
+               //
+               // absolute arc
+               //
+               next_number(&ptr,&rx);
+               next_number(&ptr,&ry);
+               next_number(&ptr,&rotation);
+               next_number(&ptr,&large_arc);
+               next_number(&ptr,&sweep);
+               next_number(&ptr,&x);
+               next_number(&ptr,&y);
+               //printf("   path A: %f %f %f %f %f %f %f\n",rx,ry,rotation,large_arc,sweep,x,y);
+               current_element = 'A';
+               phi = rotation*pi/180.0;
+               x1 = x0;
+               x2 = x;
+               y1 = y0;
+               y2 = y;
+               x1p = cos(phi)*(x1-x2)/2.0 + sin(phi)*(y1-y2)/2.0;
+               y1p = -sin(phi)*(x1-x2)/2.0 + cos(phi)*(y1-y2)/2.0;
+               sign = ((large_arc == sweep) ? -1 : 1);
+               if ((rx*rx*ry*ry - rx*rx*y1p*y1p - ry*ry*x1p*x1p) < 0) {
+                  cxp = 0;
+                  cyp = 0;
+                  }
+               else {
+                  cxp = sign * sqrt((rx*rx*ry*ry - rx*rx*y1p*y1p - ry*ry*x1p*x1p)/(rx*rx*y1p*y1p + ry*ry*x1p*x1p)) * rx*y1p/ry;
+                  cyp = -1 * sign * sqrt((rx*rx*ry*ry - rx*rx*y1p*y1p - ry*ry*x1p*x1p)/(rx*rx*y1p*y1p + ry*ry*x1p*x1p)) * ry*x1p/rx;
+                  }
+               cx = cos(phi)*cxp - sin(phi)*cyp + (x1+x2)/2.0;
+               cy = sin(phi)*cxp + cos(phi)*cyp + (y1+y2)/2.0;
+               theta_0 = angle(1,0,(x1p-cxp)/rx,(y1p-cyp)/ry);
+               theta_diff = angle ((x1p-cxp)/rx,(y1p-cyp)/ry,(-x1p-cxp)/rx,(-y1p-cyp)/ry);
+               theta_1 = theta_0 + theta_diff;
+               if (large_arc == 1) {
+                  if (sweep == 0)
+                     theta_1 -= 2*pi;
+                  else
+                     theta_1 += 2*pi;
+                  }
+               for (point = 0; point < points; ++point) {
+                  theta = theta_0 + (theta_1-theta_0) * point / (points - 1.0);
+                  xt = cx + rx*cos(theta);
+                  yt = cy + ry*sin(theta);
+                  fab_path_point(v);
+                  path_point(&xn,&yn,xt,yt,transform,transforms,xnscale,ynscale,xnmid,vxmid,ynmid,vymid);
+                  fab_path_axis(v,xn);
+                  fab_path_axis(v,yn);
+                  if (v->path->dof > 2)
+                     fab_path_axis(v,zn);
+                  }
+               x0 = x;
+               y0 = y;
+               }
+            else if ((current_element == 'z') || (current_element == 'Z')) {
+               //
+               // closepath
+               //
+               //printf("   path zZ\n");
+               fab_path_point(v);
+               fab_path_axis(v,v->path->segment->first->first->value);
+               fab_path_axis(v,v->path->segment->first->first->next->value);
+               if (v->path->dof > 2)
+                  fab_path_axis(v,zn);
+               current_element = 0;
+               ptr += 1;
+               }
+            } while (*ptr != '\"');
+         clear_transform(&transform);
+         }
+      else if (strncmp(ptr,"",2) != 0) ptr += 1; // read to end of element
+         //printf("   rect: %f %f %f %f\n",width,height,x,y);
+         fab_path_segment(v);
+            fab_path_point(v);
+               path_point(&xn,&yn,x,y,transform,transforms,xnscale,ynscale,xnmid,vxmid,ynmid,vymid);
+               fab_path_axis(v,xn);
+               fab_path_axis(v,yn);
+               if (v->path->dof > 2)
+                  fab_path_axis(v,zn);
+            fab_path_point(v);
+               path_point(&xn,&yn,x,y+height,transform,transforms,xnscale,ynscale,xnmid,vxmid,ynmid,vymid);
+               fab_path_axis(v,xn);
+               fab_path_axis(v,yn);
+               if (v->path->dof > 2)
+                  fab_path_axis(v,zn);
+            fab_path_point(v);
+               path_point(&xn,&yn,x+width,y+height,transform,transforms,xnscale,ynscale,xnmid,vxmid,ynmid,vymid);
+               fab_path_axis(v,xn);
+               fab_path_axis(v,yn);
+               if (v->path->dof > 2)
+                  fab_path_axis(v,zn);
+            fab_path_point(v);
+               path_point(&xn,&yn,x+width,y,transform,transforms,xnscale,ynscale,xnmid,vxmid,ynmid,vymid);
+               fab_path_axis(v,xn);
+               fab_path_axis(v,yn);
+               if (v->path->dof > 2)
+                  fab_path_axis(v,zn);
+            fab_path_point(v);
+               path_point(&xn,&yn,x,y,transform,transforms,xnscale,ynscale,xnmid,vxmid,ynmid,vymid);
+               fab_path_axis(v,xn);
+               fab_path_axis(v,yn);
+               if (v->path->dof > 2)
+                  fab_path_axis(v,zn);
+         clear_transform(&transform);
+         }
+      else if (strncmp(ptr,"",2) != 0) ptr += 1; // read to end of element
+         //printf("   circle: %f %f %f\n",cx,cy,r);
+         fab_path_segment(v);
+         for (point = 0; point < points; ++point) {
+            fab_path_point(v);
+            x = cx + r*cos(point*2*pi/(points-1.0));
+            y = cy + r*sin(point*2*pi/(points-1.0));
+            path_point(&xn,&yn,x,y,transform,transforms,xnscale,ynscale,xnmid,vxmid,ynmid,vymid);
+            fab_path_axis(v,xn);
+            fab_path_axis(v,yn);
+            if (v->path->dof > 2)
+               fab_path_axis(v,zn);
+            }
+         clear_transform(&transform);
+         }
+      else if (strncmp(ptr,"",2) != 0) ptr += 1; // read to end of element
+         printf("   svg_path: ellipse not yet implemented\n");
+         }
+      else if (strncmp(ptr,"",2) != 0) ptr += 1; // read to end of element
+         //printf("   line: %f %f %f %f\n",x1,y1,x2,y2);
+         fab_path_segment(v);
+         fab_path_point(v);
+         path_point(&xn,&yn,x1,y1,transform,transforms,xnscale,ynscale,xnmid,vxmid,ynmid,vymid);
+         fab_path_axis(v,xn);
+         fab_path_axis(v,yn);
+         if (v->path->dof > 2)
+            fab_path_axis(v,zn);
+         fab_path_point(v);
+         path_point(&xn,&yn,x2,y2,transform,transforms,xnscale,ynscale,xnmid,vxmid,ynmid,vymid);
+         fab_path_axis(v,xn);
+         fab_path_axis(v,yn);
+         if (v->path->dof > 2)
+            fab_path_axis(v,zn);
+         clear_transform(&transform);
+         }
+      else if (strncmp(ptr,"path->dof > 2)
+            fab_path_axis(v,zn);
+         while (1) {
+            //
+            // loop over remaining points 
+            //
+            next_number(&ptr,&x);
+            next_number(&ptr,&y);
+            fab_path_point(v);
+            path_point(&xn,&yn,x,y,transform,transforms,xnscale,ynscale,xnmid,vxmid,ynmid,vymid);
+            fab_path_axis(v,xn);
+            fab_path_axis(v,yn);
+            if (v->path->dof > 2)
+               fab_path_axis(v,zn);
+            while ((*ptr == ' ') || (*ptr == '"') || (*ptr == 13) || (*ptr == 10)) ptr += 1; // skip space
+            if (strncmp(ptr,"/>",2) == 0) break; // check for end
+            }
+         clear_transform(&transform);
+         }
+      else if (strncmp(ptr,"path->dof > 2)
+            fab_path_axis(v,zn);
+         while (1) {
+            //
+            // loop over remaining points 
+            //
+            next_number(&ptr,&x);
+            next_number(&ptr,&y);
+            fab_path_point(v);
+            path_point(&xn,&yn,x,y,transform,transforms,xnscale,ynscale,xnmid,vxmid,ynmid,vymid);
+            fab_path_axis(v,xn);
+            fab_path_axis(v,yn);
+            if (v->path->dof > 2)
+               fab_path_axis(v,zn);
+            while ((*ptr == ' ') || (*ptr == '"') || (*ptr == 13) || (*ptr == 10)) ptr += 1; // skip space
+            if (strncmp(ptr,"/>",2) == 0) break; // check for end
+            }
+         fab_path_point(v);
+         path_point(&xn,&yn,x0,y0,transform,transforms,xnscale,ynscale,xnmid,vxmid,ynmid,vymid);
+         fab_path_axis(v,xn);
+         fab_path_axis(v,yn);
+         if (v->path->dof > 2)
+            fab_path_axis(v,zn);
+         clear_transform(&transform);
+         }
+      else if (strncmp(ptr,"",6) != 0) ptr += 1; // read to end of element
+         printf("   svg_path: text not implemented\n");
+         }
+      } while ((endptr-ptr) > 1);
+   }
+
+int main(int argc, char **argv) {
+   //
+   // local vars
+   //
+   struct fab_vars v;
+   init_vars(&v);
+   int points,resolution;
+   float scale,zmin,zmax;
+   //
+   // command line args
+   // Largo al Factotum Canadian Brass
+   //
+   if ((argc != 3) && (argc != 4) && (argc != 5) && (argc != 6) && (argc != 7) && (argc != 8)) {
+      printf("command line: svg_path in.svg out.path [scale [points [resolution [zmin [zmax]]]]]\n");
+      printf("   in.svg = input binary SVG file\n");
+      printf("   out.path = output path file\n");
+      printf("   scale = scale factor (optional, default 1.0)\n");
+      printf("   points = points per curve segment (optional, default 25)\n");
+      printf("   resolution = path x resolution (optional, default 10000)\n");
+      printf("   zmin = path min intensity z (optional, mm, default 0)\n");
+      printf("   zmax = path max intensity z (optional, mm, default zmin)\n");
+      exit(-1);
+      }
+   if (argc == 3) {
+      scale = 1.0;
+      points = 25;
+      resolution = 10000;
+      zmin = 0;
+      zmax = 0;
+      }
+   else if (argc == 4) {
+      sscanf(argv[3],"%f",&scale);
+      points = 25;
+      resolution = 10000;
+      zmin = 0;
+      zmax = 0;
+      }
+   else if (argc == 5) {
+      sscanf(argv[3],"%f",&scale);
+      sscanf(argv[4],"%d",&points);
+      resolution = 10000;
+      zmin = 0;
+      zmax = 0;
+      }
+   else if (argc == 6) {
+      sscanf(argv[3],"%f",&scale);
+      sscanf(argv[4],"%d",&points);
+      sscanf(argv[5],"%d",&resolution);
+      zmin = 0;
+      zmax = 0;
+      }
+   else if (argc == 7) {
+      sscanf(argv[3],"%f",&scale);
+      sscanf(argv[4],"%d",&points);
+      sscanf(argv[5],"%d",&resolution);
+      sscanf(argv[6],"%f",&zmin);
+      zmax = zmin;
+      }
+   else if (argc == 8) {
+      sscanf(argv[3],"%f",&scale);
+      sscanf(argv[4],"%d",&points);
+      sscanf(argv[5],"%d",&resolution);
+      sscanf(argv[6],"%f",&zmin);
+      sscanf(argv[7],"%f",&zmax);
+      }
+   //
+   //  read SVG
+   //
+   if (argc <= 6)
+      fab_path_start(&v,2);
+   else
+      fab_path_start(&v,3);
+   fab_read_svg(&v,argv[1],scale,points,resolution,zmin,zmax);
+   //
+   // write path
+   //
+   fab_write_path(&v,argv[2]);
+   //
+   // return
+   //
+   return(0);
+   }
diff --git a/src/core/test.c b/src/core/test.c
new file mode 100644
index 0000000..5de547f
--- /dev/null
+++ b/src/core/test.c
@@ -0,0 +1,18 @@
+#include 
+#include 
+
+main() {
+   float X,Y,Z;
+   int val;
+   int True=1, False=0;
+   int i,j;
+   X = 0;
+   Y = 0;
+   Z = 0;
+   for (i = 0; i < 10000; ++ i) {
+      for (j = 0; j < 10000; ++j) {
+val = ((3296100*((False | ((((X-(1.47))-(-0.12)) >= (-0.035)) & (((X-(1.47))-(-0.12)) <= (0.035)) & (((Y-(1.59))-(0.15)) >= (-0.015)) & (((Y-(1.59))-(0.15)) <= (0.015)) & (((Z-(-0.005))-(0)) >= (0)) & (((Z-(-0.005))-(0)) <= (0))) | ((((X-(1.47))-(-0.155))*((X-(1.47))-(-0.155)) + ((Y-(1.59))-(0.15))*((Y-(1.59))-(0.15)) <= 0.015*0.015) & ((Z-(-0.005)) >= (0)) & ((Z-(-0.005)) <= (0))) | ((((X-(1.47))-(-0.12)) >= (-0.035)) & (((X-(1.47))-(-0.12)) <= (0.035)) & (((Y-(1.59))-(0.1)) >= (-0.015)) & (((Y-(1.59))-(0.1)) <= (0.015)) & (((Z-(-0.005))-(0)) >= (0)) & (((Z-(-0.005))-(0)) <= (0))) | ((((X-(1.47))-(-0.12)) >= (-0.035)) & (((X-(1.47))-(-0.12)) <= (0.035)) & (((Y-(1.59))-(0.05)) >= (-0.015)) & (((Y-(1.59))-(0.05)) <= (0.015)) & (((Z-(-0.005))-(0)) >= (0)) & (((Z-(-0.005))-(0)) <= (0))) | ((((X-(1.47))-(-0.12)) >= (-0.035)) & (((X-(1.47))-(-0.12)) <= (0.035)) & (((Y-(1.59))-(0)) >= (-0.015)) & (((Y-(1.59))-(0)) <= (0.015)) & (((Z-(-0.005))-(0)) >= (0)) & (((Z-(-0.005))-(0)) <= (0))) | ((((X-(1.47))-(-0.12)) >= (-0.035)) & (((X-(1.47))-(-0.12)) <= (0.035)) & (((Y-(1.59))-(-0.05)) >= (-0.015)) & (((Y-(1.59))-(-0.05)) <= (0.015)) & (((Z-(-0.005))-(0)) >= (0)) & (((Z-(-0.005))-(0)) <= (0))) | ((((X-(1.47))-(-0.12)) >= (-0.035)) & (((X-(1.47))-(-0.12)) <= (0.035)) & (((Y-(1.59))-(-0.1)) >= (-0.015)) & (((Y-(1.59))-(-0.1)) <= (0.015)) & (((Z-(-0.005))-(0)) >= (0)) & (((Z-(-0.005))-(0)) <= (0))) | ((((X-(1.47))-(-0.12)) >= (-0.035)) & (((X-(1.47))-(-0.12)) <= (0.035)) & (((Y-(1.59))-(-0.15)) >= (-0.015)) & (((Y-(1.59))-(-0.15)) <= (0.015)) & (((Z-(-0.005))-(0)) >= (0)) & (((Z-(-0.005))-(0)) <= (0))) | ((((X-(1.47))-(0.12)) >= (-0.035)) & (((X-(1.47))-(0.12)) <= (0.035)) & (((Y-(1.59))-(-0.15)) >= (-0.015)) & (((Y-(1.59))-(-0.15)) <= (0.015)) & (((Z-(-0.005))-(0)) >= (0)) & (((Z-(-0.005))-(0)) <= (0))) | ((((X-(1.47))-(0.12)) >= (-0.035)) & (((X-(1.47))-(0.12)) <= (0.035)) & (((Y-(1.59))-(-0.1)) >= (-0.015)) & (((Y-(1.59))-(-0.1)) <= (0.015)) & (((Z-(-0.005))-(0)) >= (0)) & (((Z-(-0.005))-(0)) <= (0))) | ((((X-(1.47))-(0.12)) >= (-0.035)) & (((X-(1.47))-(0.12)) <= (0.035)) & (((Y-(1.59))-(-0.05)) >= (-0.015)) & (((Y-(1.59))-(-0.05)) <= (0.015)) & (((Z-(-0.005))-(0)) >= (0)) & (((Z-(-0.005))-(0)) <= (0))) | ((((X-(1.47))-(0.12)) >= (-0.035)) & (((X-(1.47))-(0.12)) <= (0.035)) & (((Y-(1.59))-(0)) >= (-0.015)) & (((Y-(1.59))-(0)) <= (0.015)) & (((Z-(-0.005))-(0)) >= (0)) & (((Z-(-0.005))-(0)) <= (0))) | ((((X-(1.47))-(0.12)) >= (-0.035)) & (((X-(1.47))-(0.12)) <= (0.035)) & (((Y-(1.59))-(0.05)) >= (-0.015)) & (((Y-(1.59))-(0.05)) <= (0.015)) & (((Z-(-0.005))-(0)) >= (0)) & (((Z-(-0.005))-(0)) <= (0))) | ((((X-(1.47))-(0.12)) >= (-0.035)) & (((X-(1.47))-(0.12)) <= (0.035)) & (((Y-(1.59))-(0.1)) >= (-0.015)) & (((Y-(1.59))-(0.1)) <= (0.015)) & (((Z-(-0.005))-(0)) >= (0)) & (((Z-(-0.005))-(0)) <= (0))) | ((((X-(1.47))-(0.12)) >= (-0.035)) & (((X-(1.47))-(0.12)) <= (0.035)) & (((Y-(1.59))-(0.15)) >= (-0.015)) & (((Y-(1.59))-(0.15)) <= (0.015)) & (((Z-(-0.005))-(0)) >= (0)) & (((Z-(-0.005))-(0)) <= (0))) | ((((-(-(Y-(1.22))))-(0.107)) >= (-0.05)) & (((-(-(Y-(1.22))))-(0.107)) <= (0.05)) & (((-(X-(1.52)))-(-0.1)) >= (-0.025)) & (((-(X-(1.52)))-(-0.1)) <= (0.025)) & (((Z-(-0.005))-(0)) >= (0)) & (((Z-(-0.005))-(0)) <= (0))) | ((((-(-(Y-(1.22))))-(0.157))*((-(-(Y-(1.22))))-(0.157)) + ((-(X-(1.52)))-(-0.1))*((-(X-(1.52)))-(-0.1)) <= 0.025*0.025) & ((Z-(-0.005)) >= (0)) & ((Z-(-0.005)) <= (0))) | ((((-(-(Y-(1.22))))-(-0.107)) >= (-0.05)) & (((-(-(Y-(1.22))))-(-0.107)) <= (0.05)) & (((-(X-(1.52)))-(-0.1)) >= (-0.025)) & (((-(X-(1.52)))-(-0.1)) <= (0.025)) & (((Z-(-0.005))-(0)) >= (0)) & (((Z-(-0.005))-(0)) <= (0))) | ((((-(-(Y-(1.22))))-(0.107)) >= (-0.05)) & (((-(-(Y-(1.22))))-(0.107)) <= (0.05)) & (((-(X-(1.52)))-(0)) >= (-0.025)) & (((-(X-(1.52)))-(0)) <= (0.025)) & (((Z-(-0.005))-(0)) >= (0)) & (((Z-(-0.005))-(0)) <= (0))) | ((((-(-(Y-(1.22))))-(-0.107)) >= (-0.05)) & (((-(-(Y-(1.22))))-(-0.107)) <= (0.05)) & (((-(X-(1.52)))-(0)) >= (-0.025)) & (((-(X-(1.52)))-(0)) <= (0.025)) & (((Z-(-0.005))-(0)) >= (0)) & (((Z-(-0.005))-(0)) <= (0))) | ((((-(-(Y-(1.22))))-(0.107)) >= (-0.05)) & (((-(-(Y-(1.22))))-(0.107)) <= (0.05)) & (((-(X-(1.52)))-(0.1)) >= (-0.025)) & (((-(X-(1.52)))-(0.1)) <= (0.025)) & (((Z-(-0.005))-(0)) >= (0)) & (((Z-(-0.005))-(0)) <= (0))) | ((((-(-(Y-(1.22))))-(-0.107)) >= (-0.05)) & (((-(-(Y-(1.22))))-(-0.107)) <= (0.05)) & (((-(X-(1.52)))-(0.1)) >= (-0.025)) & (((-(X-(1.52)))-(0.1)) <= (0.025)) & (((Z-(-0.005))-(0)) >= (0)) & (((Z-(-0.005))-(0)) <= (0))) | ((X >= (1.582)) & (X <= (1.628)) & (Y >= (1.432)) & (Y <= (1.448)) & (Z >= (-0.005)) & (Z <= (-0.005))) | ((X >= (1.612)) & (X <= (1.628)) & (Y >= (1.319)) & (Y <= (1.448)) & (Z >= (-0.005)) & (Z <= (-0.005))) | ((X >= (1.512)) & (X <= (1.598)) & (Y >= (1.482)) & (Y <= (1.498)) & (Z >= (-0.005)) & (Z <= (-0.005))) | ((X >= (1.512)) & (X <= (1.528)) & (Y >= (1.319)) & (Y <= (1.498)) & (Z >= (-0.005)) & (Z <= (-0.005))) | ((X >= (1.342)) & (X <= (1.368)) & (Y >= (1.432)) & (Y <= (1.448)) & (Z >= (-0.005)) & (Z <= (-0.005))) | ((X >= (1.352)) & (X <= (1.368)) & (Y >= (1.192)) & (Y <= (1.448)) & (Z >= (-0.005)) & (Z <= (-0.005))) | ((X >= (1.352)) & (X <= (1.528)) & (Y >= (1.192)) & (Y <= (1.208)) & (Z >= (-0.005)) & (Z <= (-0.005))) | ((X >= (1.512)) & (X <= (1.528)) & (Y >= (1.105)) & (Y <= (1.208)) & (Z >= (-0.005)) & (Z <= (-0.005))) | ((X >= (1.342)) & (X <= (1.428)) & (Y >= (1.582)) & (Y <= (1.598)) & (Z >= (-0.005)) & (Z <= (-0.005))) | ((X >= (1.412)) & (X <= (1.428)) & (Y >= (1.319)) & (Y <= (1.598)) & (Z >= (-0.005)) & (Z <= (-0.005))) | ((X >= (1.462)) & (X <= (1.598)) & (Y >= (1.732)) & (Y <= (1.748)) & (Z >= (-0.005)) & (Z <= (-0.005))) | ((X >= (1.462)) & (X <= (1.478)) & (Y >= (1.232)) & (Y <= (1.748)) & (Z >= (-0.005)) & (Z <= (-0.005))) | ((X >= (1.462)) & (X <= (1.578)) & (Y >= (1.232)) & (Y <= (1.248)) & (Z >= (-0.005)) & (Z <= (-0.005))) | ((X >= (1.562)) & (X <= (1.578)) & (Y >= (1.025)) & (Y <= (1.248)) & (Z >= (-0.005)) & (Z <= (-0.005))) | ((X >= (1.412)) & (X <= (1.578)) & (Y >= (1.025)) & (Y <= (1.041)) & (Z >= (-0.005)) & (Z <= (-0.005))) | ((X >= (1.412)) & (X <= (1.428)) & (Y >= (1.025)) & (Y <= (1.121)) & (Z >= (-0.005)) & (Z <= (-0.005))) | ((((X-(1.78))-(0)) >= (-0.05)) & (((X-(1.78))-(0)) <= (0.05)) & (((Y-(1.59))-(0.25)) >= (-0.025)) & (((Y-(1.59))-(0.25)) <= (0.025)) & (((Z-(-0.005))-(0)) >= (0)) & (((Z-(-0.005))-(0)) <= (0))) | ((((X-(1.78))-(-0.05))*((X-(1.78))-(-0.05)) + ((Y-(1.59))-(0.25))*((Y-(1.59))-(0.25)) <= 0.025*0.025) & ((Z-(-0.005)) >= (0)) & ((Z-(-0.005)) <= (0))) | ((((X-(1.78))-(0)) >= (-0.05)) & (((X-(1.78))-(0)) <= (0.05)) & (((Y-(1.59))-(0.15)) >= (-0.025)) & (((Y-(1.59))-(0.15)) <= (0.025)) & (((Z-(-0.005))-(0)) >= (0)) & (((Z-(-0.005))-(0)) <= (0))) | ((((X-(1.78))-(0)) >= (-0.05)) & (((X-(1.78))-(0)) <= (0.05)) & (((Y-(1.59))-(0.05)) >= (-0.025)) & (((Y-(1.59))-(0.05)) <= (0.025)) & (((Z-(-0.005))-(0)) >= (0)) & (((Z-(-0.005))-(0)) <= (0))) | ((((X-(1.78))-(0)) >= (-0.05)) & (((X-(1.78))-(0)) <= (0.05)) & (((Y-(1.59))-(-0.05)) >= (-0.025)) & (((Y-(1.59))-(-0.05)) <= (0.025)) & (((Z-(-0.005))-(0)) >= (0)) & (((Z-(-0.005))-(0)) <= (0))) | ((((X-(1.78))-(0)) >= (-0.05)) & (((X-(1.78))-(0)) <= (0.05)) & (((Y-(1.59))-(-0.15)) >= (-0.025)) & (((Y-(1.59))-(-0.15)) <= (0.025)) & (((Z-(-0.005))-(0)) >= (0)) & (((Z-(-0.005))-(0)) <= (0))) | ((((X-(1.78))-(0)) >= (-0.05)) & (((X-(1.78))-(0)) <= (0.05)) & (((Y-(1.59))-(-0.25)) >= (-0.025)) & (((Y-(1.59))-(-0.25)) <= (0.025)) & (((Z-(-0.005))-(0)) >= (0)) & (((Z-(-0.005))-(0)) <= (0))) | ((X >= (1.612)) & (X <= (1.868)) & (Y >= (1.105)) & (Y <= (1.121)) & (Z >= (-0.005)) & (Z <= (-0.005))) | ((X >= (1.852)) & (X <= (1.868)) & (Y >= (1.105)) & (Y <= (1.648)) & (Z >= (-0.005)) & (Z <= (-0.005))) | ((X >= (1.772)) & (X <= (1.868)) & (Y >= (1.632)) & (Y <= (1.648)) & (Z >= (-0.005)) & (Z <= (-0.005))) | ((X >= (1.582)) & (X <= (1.698)) & (Y >= (1.682)) & (Y <= (1.698)) & (Z >= (-0.005)) & (Z <= (-0.005))) | ((X >= (1.682)) & (X <= (1.698)) & (Y >= (1.532)) & (Y <= (1.698)) & (Z >= (-0.005)) & (Z <= (-0.005))) | ((X >= (1.682)) & (X <= (1.788)) & (Y >= (1.532)) & (Y <= (1.548)) & (Z >= (-0.005)) & (Z <= (-0.005))) | ((X >= (1.582)) & (X <= (1.668)) & (Y >= (1.632)) & (Y <= (1.648)) & (Z >= (-0.005)) & (Z <= (-0.005))) | ((X >= (1.652)) & (X <= (1.668)) & (Y >= (1.432)) & (Y <= (1.648)) & (Z >= (-0.005)) & (Z <= (-0.005))) | ((X >= (1.652)) & (X <= (1.788)) & (Y >= (1.432)) & (Y <= (1.448)) & (Z >= (-0.005)) & (Z <= (-0.005))) | ((((-(Y-(1.698)))-(-0.053)) >= (-0.016)) & (((-(Y-(1.698)))-(-0.053)) <= (0.016)) & (((X-(1.15))-(0)) >= (-0.085)) & (((X-(1.15))-(0)) <= (0.085)) & (((Z-(-0.005))-(0)) >= (0)) & (((Z-(-0.005))-(0)) <= (0))) | ((((-(Y-(1.698)))-(0)) >= (-0.016)) & (((-(Y-(1.698)))-(0)) <= (0.016)) & (((X-(1.15))-(0)) >= (-0.085)) & (((X-(1.15))-(0)) <= (0.085)) & (((Z-(-0.005))-(0)) >= (0)) & (((Z-(-0.005))-(0)) <= (0))) | ((((-(Y-(1.698)))-(0.053)) >= (-0.016)) & (((-(Y-(1.698)))-(0.053)) <= (0.016)) & (((X-(1.15))-(0)) >= (-0.085)) & (((X-(1.15))-(0)) <= (0.085)) & (((Z-(-0.005))-(0)) >= (0)) & (((Z-(-0.005))-(0)) <= (0))) | ((X >= (1.262)) & (X <= (1.358)) & (Y >= (1.682)) & (Y <= (1.698)) & (Z >= (-0.005)) & (Z <= (-0.005))) | ((X >= (1.262)) & (X <= (1.278)) & (Y >= (1.682)) & (Y <= (1.759)) & (Z >= (-0.005)) & (Z <= (-0.005))) | ((X >= (1.142)) & (X <= (1.278)) & (Y >= (1.743)) & (Y <= (1.759)) & (Z >= (-0.005)) & (Z <= (-0.005))) | ((X >= (1.412)) & (X <= (1.428)) & (Y >= (1.025)) & (Y <= (1.121)) & (Z >= (-0.005)) & (Z <= (-0.005))) | ((X >= (1.022)) & (X <= (1.428)) & (Y >= (1.025)) & (Y <= (1.041)) & (Z >= (-0.005)) & (Z <= (-0.005))) | ((X >= (1.022)) & (X <= (1.038)) & (Y >= (1.025)) & (Y <= (1.706)) & (Z >= (-0.005)) & (Z <= (-0.005))) | ((X >= (1.022)) & (X <= (1.158)) & (Y >= (1.69)) & (Y <= (1.706)) & (Z >= (-0.005)) & (Z <= (-0.005))) | ((X >= (1.142)) & (X <= (1.358)) & (Y >= (1.632)) & (Y <= (1.648)) & (Z >= (-0.005)) & (Z <= (-0.005))) | ((X >= (1.142)) & (X <= (1.158)) & (Y >= (1.632)) & (Y <= (1.653)) & (Z >= (-0.005)) & (Z <= (-0.005))) | ((((X-(1.35))-(-0.06)) >= (-0.032)) & (((X-(1.35))-(-0.06)) <= (0.032)) & (((Y-(1.84))-(0)) >= (-0.034)) & (((Y-(1.84))-(0)) <= (0.034)) & (((Z-(-0.005))-(0)) >= (0)) & (((Z-(-0.005))-(0)) <= (0))) | ((((X-(1.35))-(0.06)) >= (-0.032)) & (((X-(1.35))-(0.06)) <= (0.032)) & (((Y-(1.84))-(0)) >= (-0.034)) & (((Y-(1.84))-(0)) <= (0.034)) & (((Z-(-0.005))-(0)) >= (0)) & (((Z-(-0.005))-(0)) <= (0))) | ((X >= (1.282)) & (X <= (1.358)) & (Y >= (1.832)) & (Y <= (1.848)) & (Z >= (-0.005)) & (Z <= (-0.005))) | ((X >= (1.342)) & (X <= (1.358)) & (Y >= (1.732)) & (Y <= (1.848)) & (Z >= (-0.005)) & (Z <= (-0.005))) | ((X >= (1.772)) & (X <= (1.868)) & (Y >= (1.632)) & (Y <= (1.648)) & (Z >= (-0.005)) & (Z <= (-0.005))) | ((X >= (1.852)) & (X <= (1.868)) & (Y >= (1.632)) & (Y <= (1.908)) & (Z >= (-0.005)) & (Z <= (-0.005))) | ((X >= (1.282)) & (X <= (1.868)) & (Y >= (1.892)) & (Y <= (1.908)) & (Z >= (-0.005)) & (Z <= (-0.005))) | ((X >= (1.282)) & (X <= (1.298)) & (Y >= (1.832)) & (Y <= (1.908)) & (Z >= (-0.005)) & (Z <= (-0.005))) | ((X >= (1.402)) & (X <= (1.428)) & (Y >= (1.832)) & (Y <= (1.848)) & (Z >= (-0.005)) & (Z <= (-0.005))) | ((X >= (1.412)) & (X <= (1.428)) & (Y >= (1.319)) & (Y <= (1.848)) & (Z >= (-0.005)) & (Z <= (-0.005))) | ((((X-(1.59))-(-0.06)) >= (-0.032)) & (((X-(1.59))-(-0.06)) <= (0.032)) & (((Y-(1.84))-(0)) >= (-0.034)) & (((Y-(1.84))-(0)) <= (0.034)) & (((Z-(-0.005))-(0)) >= (0)) & (((Z-(-0.005))-(0)) <= (0))) | ((((X-(1.59))-(0.06)) >= (-0.032)) & (((X-(1.59))-(0.06)) <= (0.032)) & (((Y-(1.84))-(0)) >= (-0.034)) & (((Y-(1.84))-(0)) <= (0.034)) & (((Z-(-0.005))-(0)) >= (0)) & (((Z-(-0.005))-(0)) <= (0))) | ((X >= (1.582)) & (X <= (1.658)) & (Y >= (1.732)) & (Y <= (1.748)) & (Z >= (-0.005)) & (Z <= (-0.005))) | ((X >= (1.642)) & (X <= (1.658)) & (Y >= (1.732)) & (Y <= (1.848)) & (Z >= (-0.005)) & (Z <= (-0.005))) | ((X >= (1.522)) & (X <= (1.538)) & (Y >= (1.832)) & (Y <= (1.908)) & (Z >= (-0.005)) & (Z <= (-0.005))) | ((X >= (1.642)) & (X <= (1.788)) & (Y >= (1.832)) & (Y <= (1.848)) & (Z >= (-0.005)) & (Z <= (-0.005))))!=0)) | False | (65280*((False | False | ((((((X-(1.47))-(0))-(-0.0455))-(0)) >= (0.0105)) & (((((X-(1.47))-(0))-(-0.0455))-(0)) <= (0.0175)) & (((((Y-(1.59))-(0.0525))-(0))-(-0.042)) >= (0)) & (((((Y-(1.59))-(0.0525))-(0))-(-0.042)) <= (0.042))) | ((((((X-(1.47))-(0))-(-0.0455))-(0)) >= (0.0056)) & (((((X-(1.47))-(0))-(-0.0455))-(0)) <= (0.0224)) & (((((Y-(1.59))-(0.0525))-(0))-(-0.042)) >= (0)) & (((((Y-(1.59))-(0.0525))-(0))-(-0.042)) <= (0.007))) | ((((((X-(1.47))-(0))-(-0.0455))-(0)) >= (0.0056)) & (((((X-(1.47))-(0))-(-0.0455))-(0)) <= (0.0224)) & (((((Y-(1.59))-(0.0525))-(0))-(-0.042)) >= (0.035)) & (((((Y-(1.59))-(0.0525))-(0))-(-0.042)) <= (0.042))) | ((((((((((X-(1.47))-(0))-(-0.0455))-(0.0315))-(0.014))*(((((X-(1.47))-(0))-(-0.0455))-(0.0315))-(0.014)) + (((((Y-(1.59))-(0.0525))-(0))-(-0.042))-(0.014))*(((((X-(1.47))-(0))-(-0.0455))-(0.0315))-(0.014))) <= 0.014*0.014) | (((((((X-(1.47))-(0))-(-0.0455))-(0.0315))-(0.014))*(((((X-(1.47))-(0))-(-0.0455))-(0.0315))-(0.014)) + (((((Y-(1.59))-(0.0525))-(0))-(-0.042))-(0.028))*(((((X-(1.47))-(0))-(-0.0455))-(0.0315))-(0.014))) <= 0.014*0.014) | ((((((X-(1.47))-(0))-(-0.0455))-(0.0315)) >= (0)) & (((((X-(1.47))-(0))-(-0.0455))-(0.0315)) <= (0.028)) & (((((Y-(1.59))-(0.0525))-(0))-(-0.042)) >= (0.01225)) & (((((Y-(1.59))-(0.0525))-(0))-(-0.042)) <= (0.02975)))) & ~((((((((X-(1.47))-(0))-(-0.0455))-(0.0315))-(0.014))*(((((X-(1.47))-(0))-(-0.0455))-(0.0315))-(0.014)) + (((((Y-(1.59))-(0.0525))-(0))-(-0.042))-(0.014))*(((((X-(1.47))-(0))-(-0.0455))-(0.0315))-(0.014))) <= 0.007*0.007))) & ~((((((((X-(1.47))-(0))-(-0.0455))-(0.0315))-(0.014))*(((((X-(1.47))-(0))-(-0.0455))-(0.0315))-(0.014)) + (((((Y-(1.59))-(0.0525))-(0))-(-0.042))-(0.028))*(((((X-(1.47))-(0))-(-0.0455))-(0.0315))-(0.014))) <= 0.007*0.007))) & ~(((((((X-(1.47))-(0))-(-0.0455))-(0.0315)) >= (0.007)) & (((((X-(1.47))-(0))-(-0.0455))-(0.0315)) <= (0.028)) & (((((Y-(1.59))-(0.0525))-(0))-(-0.042)) >= (0.014)) & (((((Y-(1.59))-(0.0525))-(0))-(-0.042)) <= (0.028)))) | ((((((((X-(1.47))-(0))-(-0.0455))-(0.063))-(0.004375)) >= (0.0105)) & ((((((X-(1.47))-(0))-(-0.0455))-(0.063))-(0.004375)) <= (0.0175)) & ((((((Y-(1.59))-(0.0525))-(0))-(-0.042))-(0)) >= (0)) & ((((((Y-(1.59))-(0.0525))-(0))-(-0.042))-(0)) <= (0.042))) | (((((((X-(1.47))-(0))-(-0.0455))-(0.063))-(0.004375)) >= (0)) & ((((((X-(1.47))-(0))-(-0.0455))-(0.063))-(0.004375)) <= (0.014)) & ((((((Y-(1.59))-(0.0525))-(0))-(-0.042))-(0)) >= (0.0245)) & ((((((Y-(1.59))-(0.0525))-(0))-(-0.042))-(0)) <= (0.042)))) & ~(((((((((X-(1.47))-(0))-(-0.0455))-(0.063))-(0.004375))-(0))*((((((X-(1.47))-(0))-(-0.0455))-(0.063))-(0.004375))-(0)) + ((((((Y-(1.59))-(0.0525))-(0))-(-0.042))-(0))-(0.042))*((((((X-(1.47))-(0))-(-0.0455))-(0.063))-(0.004375))-(0))) <= 0.0105*0.0105)) | False | (((((((((((X-(1.47))-(0))-(-0.0455))-(0))-(0.021))-(0))*((((((X-(1.47))-(0))-(-0.0455))-(0))-(0.021))-(0)) + ((((((Y-(1.59))-(0.0525))-(0))-(-0.105))-(0))-(0.0105))*((((((X-(1.47))-(0))-(-0.0455))-(0))-(0.021))-(0))) <= 0.0105*0.0105)) & ~(((((((((X-(1.47))-(0))-(-0.0455))-(0))-(0.021))-(0))*((((((X-(1.47))-(0))-(-0.0455))-(0))-(0.021))-(0)) + ((((((Y-(1.59))-(0.0525))-(0))-(-0.105))-(0))-(0.0105))*((((((X-(1.47))-(0))-(-0.0455))-(0))-(0.021))-(0))) <= 0.0035*0.0035))) & ~((((((((X-(1.47))-(0))-(-0.0455))-(0))-(0.021)) >= (-0.028)) & ((((((X-(1.47))-(0))-(-0.0455))-(0))-(0.021)) <= (0.028)) & ((((((Y-(1.59))-(0.0525))-(0))-(-0.105))-(0)) >= (0.0105)) & ((((((Y-(1.59))-(0.0525))-(0))-(-0.105))-(0)) <= (0.042))))) & ~((((((((X-(1.47))-(0))-(-0.0455))-(0))-(0.021)) >= (0)) & ((((((X-(1.47))-(0))-(-0.0455))-(0))-(0.021)) <= (0.028)) & ((((((Y-(1.59))-(0.0525))-(0))-(-0.105))-(0)) >= (-0.042)) & ((((((Y-(1.59))-(0.0525))-(0))-(-0.105))-(0)) <= (0.042)))) | ((((((X-(1.47))-(0))-(-0.0455))-(0)) >= (0.0105)) & (((((X-(1.47))-(0))-(-0.0455))-(0)) <= (0.0175)) & (((((Y-(1.59))-(0.0525))-(0))-(-0.105)) >= (0.007)) & (((((Y-(1.59))-(0.0525))-(0))-(-0.105)) <= (0.0315))) | ((((((X-(1.47))-(0))-(-0.0455))-(0)) >= (0.0056)) & (((((X-(1.47))-(0))-(-0.0455))-(0)) <= (0.0224)) & (((((Y-(1.59))-(0.0525))-(0))-(-0.105)) >= (0.0175)) & (((((Y-(1.59))-(0.0525))-(0))-(-0.105)) <= (0.0245))) | (((((((X-(1.47))-(0))-(-0.0455))-(0.0315)) >= (0.021)) & (((((X-(1.47))-(0))-(-0.0455))-(0.0315)) <= (0.028)) & (((((Y-(1.59))-(0.0525))-(0))-(-0.105)) >= (0)) & (((((Y-(1.59))-(0.0525))-(0))-(-0.105)) <= (0.042))) | (((((0.042)-(0.014))*(((((X-(1.47))-(0))-(-0.0455))-(0.0315))-(0))-((0.021)-(0))*(((((Y-(1.59))-(0.0525))-(0))-(-0.105))-(0.014))) >= 0) & ((((0.014)-(0.042))*(((((X-(1.47))-(0))-(-0.0455))-(0.0315))-(0.021))-((0.021)-(0.021))*(((((Y-(1.59))-(0.0525))-(0))-(-0.105))-(0.042))) >= 0) & ((((0.014)-(0.014))*(((((X-(1.47))-(0))-(-0.0455))-(0.0315))-(0.021))-((0)-(0.021))*(((((Y-(1.59))-(0.0525))-(0))-(-0.105))-(0.014))) >= 0))) & ~((((((0.0315)-(0.021))*(((((X-(1.47))-(0))-(-0.0455))-(0.0315))-(0.01225))-((0.021)-(0.01225))*(((((Y-(1.59))-(0.0525))-(0))-(-0.105))-(0.021))) >= 0) & ((((0.021)-(0.0315))*(((((X-(1.47))-(0))-(-0.0455))-(0.0315))-(0.021))-((0.021)-(0.021))*(((((Y-(1.59))-(0.0525))-(0))-(-0.105))-(0.0315))) >= 0) & ((((0.021)-(0.021))*(((((X-(1.47))-(0))-(-0.0455))-(0.0315))-(0.021))-((0.01225)-(0.021))*(((((Y-(1.59))-(0.0525))-(0))-(-0.105))-(0.021))) >= 0))) | (((((((X-(1.47))-(0))-(-0.0455))-(0.063)) >= (0.021)) & (((((X-(1.47))-(0))-(-0.0455))-(0.063)) <= (0.028)) & (((((Y-(1.59))-(0.0525))-(0))-(-0.105)) >= (0)) & (((((Y-(1.59))-(0.0525))-(0))-(-0.105)) <= (0.042))) | (((((0.042)-(0.014))*(((((X-(1.47))-(0))-(-0.0455))-(0.063))-(0))-((0.021)-(0))*(((((Y-(1.59))-(0.0525))-(0))-(-0.105))-(0.014))) >= 0) & ((((0.014)-(0.042))*(((((X-(1.47))-(0))-(-0.0455))-(0.063))-(0.021))-((0.021)-(0.021))*(((((Y-(1.59))-(0.0525))-(0))-(-0.105))-(0.042))) >= 0) & ((((0.014)-(0.014))*(((((X-(1.47))-(0))-(-0.0455))-(0.063))-(0.021))-((0)-(0.021))*(((((Y-(1.59))-(0.0525))-(0))-(-0.105))-(0.014))) >= 0))) & ~((((((0.0315)-(0.021))*(((((X-(1.47))-(0))-(-0.0455))-(0.063))-(0.01225))-((0.021)-(0.01225))*(((((Y-(1.59))-(0.0525))-(0))-(-0.105))-(0.021))) >= 0) & ((((0.021)-(0.0315))*(((((X-(1.47))-(0))-(-0.0455))-(0.063))-(0.021))-((0.021)-(0.021))*(((((Y-(1.59))-(0.0525))-(0))-(-0.105))-(0.0315))) >= 0) & ((((0.021)-(0.021))*(((((X-(1.47))-(0))-(-0.0455))-(0.063))-(0.021))-((0.01225)-(0.021))*(((((Y-(1.59))-(0.0525))-(0))-(-0.105))-(0.021))) >= 0))))!=0)) | (255*((False | False | ((((((0.0315)-(0.0315))*(((((X-(1.35))-(0))-(-0.034125))-(0))-(0))-((0.021)-(0))*(((((Y-(1.74))-(0.01575))-(0))-(-0.0315))-(0.0315))) >= 0) & ((((0)-(0.0315))*(((((X-(1.35))-(0))-(-0.034125))-(0))-(0.021))-((0.0105)-(0.021))*(((((Y-(1.74))-(0.01575))-(0))-(-0.0315))-(0.0315))) >= 0) & ((((0.0315)-(0))*(((((X-(1.35))-(0))-(-0.034125))-(0))-(0.0105))-((0)-(0.0105))*(((((Y-(1.74))-(0.01575))-(0))-(-0.0315))-(0))) >= 0))) & ~((((((0.04725)-(0.04725))*(((((X-(1.35))-(0))-(-0.034125))-(0))-(0))-((0.021)-(0))*(((((Y-(1.74))-(0.01575))-(0))-(-0.0315))-(0.04725))) >= 0) & ((((0.01575)-(0.04725))*(((((X-(1.35))-(0))-(-0.034125))-(0))-(0.021))-((0.0105)-(0.021))*(((((Y-(1.74))-(0.01575))-(0))-(-0.0315))-(0.04725))) >= 0) & ((((0.04725)-(0.01575))*(((((X-(1.35))-(0))-(-0.034125))-(0))-(0.0105))-((0)-(0.0105))*(((((Y-(1.74))-(0.01575))-(0))-(-0.0315))-(0.01575))) >= 0))) | ((((((((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.0105))*(((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.0105)) + (((((Y-(1.74))-(0.01575))-(0))-(-0.0315))-(0.0105))*(((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.0105))) <= 0.0105*0.0105) | (((((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.0105))*(((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.0105)) + (((((Y-(1.74))-(0.01575))-(0))-(-0.0315))-(0.021))*(((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.0105))) <= 0.0105*0.0105) | ((((((X-(1.35))-(0))-(-0.034125))-(0.023625)) >= (0)) & (((((X-(1.35))-(0))-(-0.034125))-(0.023625)) <= (0.021)) & (((((Y-(1.74))-(0.01575))-(0))-(-0.0315)) >= (0.0091875)) & (((((Y-(1.74))-(0.01575))-(0))-(-0.0315)) <= (0.0223125)))) & ~((((((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.0105))*(((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.0105)) + (((((Y-(1.74))-(0.01575))-(0))-(-0.0315))-(0.0105))*(((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.0105))) <= 0.00525*0.00525))) & ~((((((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.0105))*(((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.0105)) + (((((Y-(1.74))-(0.01575))-(0))-(-0.0315))-(0.021))*(((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.0105))) <= 0.00525*0.00525))) & ~(((((((X-(1.35))-(0))-(-0.034125))-(0.023625)) >= (0.00525)) & (((((X-(1.35))-(0))-(-0.034125))-(0.023625)) <= (0.021)) & (((((Y-(1.74))-(0.01575))-(0))-(-0.0315)) >= (0.0105)) & (((((Y-(1.74))-(0.01575))-(0))-(-0.0315)) <= (0.021)))) | ((((((((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0.0105))*(((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0.0105)) + (((((Y-(1.74))-(0.01575))-(0))-(-0.0315))-(0.0105))*(((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0.0105))) <= 0.0105*0.0105) | (((((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0.0105))*(((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0.0105)) + (((((Y-(1.74))-(0.01575))-(0))-(-0.0315))-(0.021))*(((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0.0105))) <= 0.0105*0.0105) | ((((((X-(1.35))-(0))-(-0.034125))-(0.04725)) >= (0)) & (((((X-(1.35))-(0))-(-0.034125))-(0.04725)) <= (0.021)) & (((((Y-(1.74))-(0.01575))-(0))-(-0.0315)) >= (0.0091875)) & (((((Y-(1.74))-(0.01575))-(0))-(-0.0315)) <= (0.0223125)))) & ~((((((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0.0105))*(((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0.0105)) + (((((Y-(1.74))-(0.01575))-(0))-(-0.0315))-(0.0105))*(((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0.0105))) <= 0.00525*0.00525))) & ~((((((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0.0105))*(((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0.0105)) + (((((Y-(1.74))-(0.01575))-(0))-(-0.0315))-(0.021))*(((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0.0105))) <= 0.00525*0.00525))) & ~(((((((X-(1.35))-(0))-(-0.034125))-(0.04725)) >= (0.00525)) & (((((X-(1.35))-(0))-(-0.034125))-(0.04725)) <= (0.021)) & (((((Y-(1.74))-(0.01575))-(0))-(-0.0315)) >= (0.0105)) & (((((Y-(1.74))-(0.01575))-(0))-(-0.0315)) <= (0.021)))))!=0)) | (255*((False | False | ((((((((X-(1.35))-(0))-(-0.034125))-(0)) >= (0)) & (((((X-(1.35))-(0))-(-0.034125))-(0)) <= (0.00525)) & (((((Y-(1.69))-(0.01575))-(0))-(-0.0315)) >= (0)) & (((((Y-(1.69))-(0.01575))-(0))-(-0.0315)) <= (0.0315))) | (((((((X-(1.35))-(0))-(-0.034125))-(0))-(0.0105))*(((((X-(1.35))-(0))-(-0.034125))-(0))-(0.0105)) + (((((Y-(1.69))-(0.01575))-(0))-(-0.0315))-(0.021))*(((((X-(1.35))-(0))-(-0.034125))-(0))-(0.0105))) <= 0.0105*0.0105) | ((((((X-(1.35))-(0))-(-0.034125))-(0)) >= (0)) & (((((X-(1.35))-(0))-(-0.034125))-(0)) <= (0.0105)) & (((((Y-(1.69))-(0.01575))-(0))-(-0.0315)) >= (0.0105)) & (((((Y-(1.69))-(0.01575))-(0))-(-0.0315)) <= (0.0315)))) & ~((((((((X-(1.35))-(0))-(-0.034125))-(0))-(0.0105))*(((((X-(1.35))-(0))-(-0.034125))-(0))-(0.0105)) + (((((Y-(1.69))-(0.01575))-(0))-(-0.0315))-(0.021))*(((((X-(1.35))-(0))-(-0.034125))-(0))-(0.0105))) <= 0.00525*0.00525))) & ~(((((((X-(1.35))-(0))-(-0.034125))-(0)) >= (0.00525)) & (((((X-(1.35))-(0))-(-0.034125))-(0)) <= (0.0105)) & (((((Y-(1.69))-(0.01575))-(0))-(-0.0315)) >= (0.01575)) & (((((Y-(1.69))-(0.01575))-(0))-(-0.0315)) <= (0.02625)))) | ((((((((((X-(1.35))-(0))-(-0.034125))-(0.023625)) >= (0)) & (((((X-(1.35))-(0))-(-0.034125))-(0.023625)) <= (0.013125)) & (((((Y-(1.69))-(0.01575))-(0))-(-0.0315)) >= (0)) & (((((Y-(1.69))-(0.01575))-(0))-(-0.0315)) <= (0.0315))) | (((((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.013125))*(((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.013125)) + (((((Y-(1.69))-(0.01575))-(0))-(-0.0315))-(0.007875))*(((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.013125))) <= 0.007875*0.007875) | (((((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.013125))*(((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.013125)) + (((((Y-(1.69))-(0.01575))-(0))-(-0.0315))-(0.023625))*(((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.013125))) <= 0.007875*0.007875)) & ~(((((((X-(1.35))-(0))-(-0.034125))-(0.023625)) >= (0.00525)) & (((((X-(1.35))-(0))-(-0.034125))-(0.023625)) <= (0.0105)) & (((((Y-(1.69))-(0.01575))-(0))-(-0.0315)) >= (0.018375)) & (((((Y-(1.69))-(0.01575))-(0))-(-0.0315)) <= (0.02625))))) & ~((((((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.0105))*(((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.0105)) + (((((Y-(1.69))-(0.01575))-(0))-(-0.0315))-(0.0223125))*(((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.0105))) <= 0.0039375*0.0039375))) & ~(((((((X-(1.35))-(0))-(-0.034125))-(0.023625)) >= (0.00525)) & (((((X-(1.35))-(0))-(-0.034125))-(0.023625)) <= (0.0105)) & (((((Y-(1.69))-(0.01575))-(0))-(-0.0315)) >= (0.00525)) & (((((Y-(1.69))-(0.01575))-(0))-(-0.0315)) <= (0.013125))))) & ~((((((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.0105))*(((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.0105)) + (((((Y-(1.69))-(0.01575))-(0))-(-0.0315))-(0.0091875))*(((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.0105))) <= 0.0039375*0.0039375)) | ((((((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0.0105))*(((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0.0105)) + (((0) + (((((Y-(1.69))-(0.01575))-(0))-(-0.0315))-(0))/(1.5))-(0.0105))*(((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0.0105))) <= 0.0105*0.0105)) & ~((((((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0.0105))*(((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0.0105)) + (((0) + (((((Y-(1.69))-(0.01575))-(0))-(-0.0315))-(0))/(1.5))-(0.0105))*(((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0.0105))) <= 0.00525*0.00525)))!=0)) | (255*((False | False | ((((((((X-(1.35))-(0))-(-0.034125))-(0)) >= (0)) & (((((X-(1.35))-(0))-(-0.034125))-(0)) <= (0.00525)) & (((((Y-(1.64))-(0.01575))-(0))-(-0.0315)) >= (0)) & (((((Y-(1.64))-(0.01575))-(0))-(-0.0315)) <= (0.0315))) | (((((((X-(1.35))-(0))-(-0.034125))-(0))-(0.0105))*(((((X-(1.35))-(0))-(-0.034125))-(0))-(0.0105)) + (((((Y-(1.64))-(0.01575))-(0))-(-0.0315))-(0.021))*(((((X-(1.35))-(0))-(-0.034125))-(0))-(0.0105))) <= 0.0105*0.0105) | ((((((X-(1.35))-(0))-(-0.034125))-(0)) >= (0)) & (((((X-(1.35))-(0))-(-0.034125))-(0)) <= (0.0105)) & (((((Y-(1.64))-(0.01575))-(0))-(-0.0315)) >= (0.0105)) & (((((Y-(1.64))-(0.01575))-(0))-(-0.0315)) <= (0.0315)))) & ~((((((((X-(1.35))-(0))-(-0.034125))-(0))-(0.0105))*(((((X-(1.35))-(0))-(-0.034125))-(0))-(0.0105)) + (((((Y-(1.64))-(0.01575))-(0))-(-0.0315))-(0.021))*(((((X-(1.35))-(0))-(-0.034125))-(0))-(0.0105))) <= 0.00525*0.00525))) & ~(((((((X-(1.35))-(0))-(-0.034125))-(0)) >= (0.00525)) & (((((X-(1.35))-(0))-(-0.034125))-(0)) <= (0.0105)) & (((((Y-(1.64))-(0.01575))-(0))-(-0.0315)) >= (0.01575)) & (((((Y-(1.64))-(0.01575))-(0))-(-0.0315)) <= (0.02625)))) | ((((((((((X-(1.35))-(0))-(-0.034125))-(0.023625)) >= (0)) & (((((X-(1.35))-(0))-(-0.034125))-(0.023625)) <= (0.013125)) & (((((Y-(1.64))-(0.01575))-(0))-(-0.0315)) >= (0)) & (((((Y-(1.64))-(0.01575))-(0))-(-0.0315)) <= (0.0315))) | (((((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.013125))*(((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.013125)) + (((((Y-(1.64))-(0.01575))-(0))-(-0.0315))-(0.007875))*(((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.013125))) <= 0.007875*0.007875) | (((((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.013125))*(((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.013125)) + (((((Y-(1.64))-(0.01575))-(0))-(-0.0315))-(0.023625))*(((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.013125))) <= 0.007875*0.007875)) & ~(((((((X-(1.35))-(0))-(-0.034125))-(0.023625)) >= (0.00525)) & (((((X-(1.35))-(0))-(-0.034125))-(0.023625)) <= (0.0105)) & (((((Y-(1.64))-(0.01575))-(0))-(-0.0315)) >= (0.018375)) & (((((Y-(1.64))-(0.01575))-(0))-(-0.0315)) <= (0.02625))))) & ~((((((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.0105))*(((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.0105)) + (((((Y-(1.64))-(0.01575))-(0))-(-0.0315))-(0.0223125))*(((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.0105))) <= 0.0039375*0.0039375))) & ~(((((((X-(1.35))-(0))-(-0.034125))-(0.023625)) >= (0.00525)) & (((((X-(1.35))-(0))-(-0.034125))-(0.023625)) <= (0.0105)) & (((((Y-(1.64))-(0.01575))-(0))-(-0.0315)) >= (0.00525)) & (((((Y-(1.64))-(0.01575))-(0))-(-0.0315)) <= (0.013125))))) & ~((((((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.0105))*(((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.0105)) + (((((Y-(1.64))-(0.01575))-(0))-(-0.0315))-(0.0091875))*(((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.0105))) <= 0.0039375*0.0039375)) | ((((((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0.00328125)) >= (0.007875)) & ((((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0.00328125)) <= (0.013125)) & ((((((Y-(1.64))-(0.01575))-(0))-(-0.0315))-(0)) >= (0)) & ((((((Y-(1.64))-(0.01575))-(0))-(-0.0315))-(0)) <= (0.0315))) | (((((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0.00328125)) >= (0)) & ((((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0.00328125)) <= (0.0105)) & ((((((Y-(1.64))-(0.01575))-(0))-(-0.0315))-(0)) >= (0.018375)) & ((((((Y-(1.64))-(0.01575))-(0))-(-0.0315))-(0)) <= (0.0315)))) & ~(((((((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0.00328125))-(0))*((((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0.00328125))-(0)) + ((((((Y-(1.64))-(0.01575))-(0))-(-0.0315))-(0))-(0.0315))*((((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0.00328125))-(0))) <= 0.007875*0.007875)))!=0)) | (255*((False | False | ((((((((X-(1.35))-(0))-(-0.034125))-(0)) >= (0)) & (((((X-(1.35))-(0))-(-0.034125))-(0)) <= (0.00525)) & (((((Y-(1.59))-(0.01575))-(0))-(-0.0315)) >= (0)) & (((((Y-(1.59))-(0.01575))-(0))-(-0.0315)) <= (0.0315))) | (((((((X-(1.35))-(0))-(-0.034125))-(0))-(0.0105))*(((((X-(1.35))-(0))-(-0.034125))-(0))-(0.0105)) + (((((Y-(1.59))-(0.01575))-(0))-(-0.0315))-(0.021))*(((((X-(1.35))-(0))-(-0.034125))-(0))-(0.0105))) <= 0.0105*0.0105) | ((((((X-(1.35))-(0))-(-0.034125))-(0)) >= (0)) & (((((X-(1.35))-(0))-(-0.034125))-(0)) <= (0.0105)) & (((((Y-(1.59))-(0.01575))-(0))-(-0.0315)) >= (0.0105)) & (((((Y-(1.59))-(0.01575))-(0))-(-0.0315)) <= (0.0315)))) & ~((((((((X-(1.35))-(0))-(-0.034125))-(0))-(0.0105))*(((((X-(1.35))-(0))-(-0.034125))-(0))-(0.0105)) + (((((Y-(1.59))-(0.01575))-(0))-(-0.0315))-(0.021))*(((((X-(1.35))-(0))-(-0.034125))-(0))-(0.0105))) <= 0.00525*0.00525))) & ~(((((((X-(1.35))-(0))-(-0.034125))-(0)) >= (0.00525)) & (((((X-(1.35))-(0))-(-0.034125))-(0)) <= (0.0105)) & (((((Y-(1.59))-(0.01575))-(0))-(-0.0315)) >= (0.01575)) & (((((Y-(1.59))-(0.01575))-(0))-(-0.0315)) <= (0.02625)))) | ((((((((((X-(1.35))-(0))-(-0.034125))-(0.023625)) >= (0)) & (((((X-(1.35))-(0))-(-0.034125))-(0.023625)) <= (0.013125)) & (((((Y-(1.59))-(0.01575))-(0))-(-0.0315)) >= (0)) & (((((Y-(1.59))-(0.01575))-(0))-(-0.0315)) <= (0.0315))) | (((((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.013125))*(((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.013125)) + (((((Y-(1.59))-(0.01575))-(0))-(-0.0315))-(0.007875))*(((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.013125))) <= 0.007875*0.007875) | (((((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.013125))*(((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.013125)) + (((((Y-(1.59))-(0.01575))-(0))-(-0.0315))-(0.023625))*(((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.013125))) <= 0.007875*0.007875)) & ~(((((((X-(1.35))-(0))-(-0.034125))-(0.023625)) >= (0.00525)) & (((((X-(1.35))-(0))-(-0.034125))-(0.023625)) <= (0.0105)) & (((((Y-(1.59))-(0.01575))-(0))-(-0.0315)) >= (0.018375)) & (((((Y-(1.59))-(0.01575))-(0))-(-0.0315)) <= (0.02625))))) & ~((((((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.0105))*(((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.0105)) + (((((Y-(1.59))-(0.01575))-(0))-(-0.0315))-(0.0223125))*(((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.0105))) <= 0.0039375*0.0039375))) & ~(((((((X-(1.35))-(0))-(-0.034125))-(0.023625)) >= (0.00525)) & (((((X-(1.35))-(0))-(-0.034125))-(0.023625)) <= (0.0105)) & (((((Y-(1.59))-(0.01575))-(0))-(-0.0315)) >= (0.00525)) & (((((Y-(1.59))-(0.01575))-(0))-(-0.0315)) <= (0.013125))))) & ~((((((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.0105))*(((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.0105)) + (((((Y-(1.59))-(0.01575))-(0))-(-0.0315))-(0.0091875))*(((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.0105))) <= 0.0039375*0.0039375)) | (((((((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0.0105))*(((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0.0105)) + (((0) + (((((Y-(1.59))-(0.01575))-(0))-(-0.0315))-(0))/(0.875))-(0.0105))*(((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0.0105))) <= 0.0105*0.0105)) & ~((((((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0.0105))*(((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0.0105)) + (((0) + (((((Y-(1.59))-(0.01575))-(0))-(-0.0315))-(0))/(0.875))-(0.0105))*(((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0.0105))) <= 0.00525*0.00525)) | (((((((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0))-(0.0105))*((((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0))-(0.0105)) + (((0) + ((((((Y-(1.59))-(0.01575))-(0))-(-0.0315))-(0.013125))-(0))/(0.875))-(0.0105))*((((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0))-(0.0105))) <= 0.0105*0.0105)) & ~(((((((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0))-(0.0105))*((((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0))-(0.0105)) + (((0) + ((((((Y-(1.59))-(0.01575))-(0))-(-0.0315))-(0.013125))-(0))/(0.875))-(0.0105))*((((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0))-(0.0105))) <= 0.00525*0.00525))) & ~(((((((X-(1.35))-(0))-(-0.034125))-(0.04725)) >= (0)) & (((((X-(1.35))-(0))-(-0.034125))-(0.04725)) <= (0.0105)) & (((((Y-(1.59))-(0.01575))-(0))-(-0.0315)) >= (0.007875)) & (((((Y-(1.59))-(0.01575))-(0))-(-0.0315)) <= (0.023625)))))!=0)) | (255*((False | False | ((((((((X-(1.35))-(0))-(-0.034125))-(0)) >= (0)) & (((((X-(1.35))-(0))-(-0.034125))-(0)) <= (0.00525)) & (((((Y-(1.54))-(0.01575))-(0))-(-0.0315)) >= (0)) & (((((Y-(1.54))-(0.01575))-(0))-(-0.0315)) <= (0.0315))) | (((((((X-(1.35))-(0))-(-0.034125))-(0))-(0.0105))*(((((X-(1.35))-(0))-(-0.034125))-(0))-(0.0105)) + (((((Y-(1.54))-(0.01575))-(0))-(-0.0315))-(0.021))*(((((X-(1.35))-(0))-(-0.034125))-(0))-(0.0105))) <= 0.0105*0.0105) | ((((((X-(1.35))-(0))-(-0.034125))-(0)) >= (0)) & (((((X-(1.35))-(0))-(-0.034125))-(0)) <= (0.0105)) & (((((Y-(1.54))-(0.01575))-(0))-(-0.0315)) >= (0.0105)) & (((((Y-(1.54))-(0.01575))-(0))-(-0.0315)) <= (0.0315)))) & ~((((((((X-(1.35))-(0))-(-0.034125))-(0))-(0.0105))*(((((X-(1.35))-(0))-(-0.034125))-(0))-(0.0105)) + (((((Y-(1.54))-(0.01575))-(0))-(-0.0315))-(0.021))*(((((X-(1.35))-(0))-(-0.034125))-(0))-(0.0105))) <= 0.00525*0.00525))) & ~(((((((X-(1.35))-(0))-(-0.034125))-(0)) >= (0.00525)) & (((((X-(1.35))-(0))-(-0.034125))-(0)) <= (0.0105)) & (((((Y-(1.54))-(0.01575))-(0))-(-0.0315)) >= (0.01575)) & (((((Y-(1.54))-(0.01575))-(0))-(-0.0315)) <= (0.02625)))) | ((((((((((X-(1.35))-(0))-(-0.034125))-(0.023625)) >= (0)) & (((((X-(1.35))-(0))-(-0.034125))-(0.023625)) <= (0.013125)) & (((((Y-(1.54))-(0.01575))-(0))-(-0.0315)) >= (0)) & (((((Y-(1.54))-(0.01575))-(0))-(-0.0315)) <= (0.0315))) | (((((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.013125))*(((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.013125)) + (((((Y-(1.54))-(0.01575))-(0))-(-0.0315))-(0.007875))*(((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.013125))) <= 0.007875*0.007875) | (((((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.013125))*(((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.013125)) + (((((Y-(1.54))-(0.01575))-(0))-(-0.0315))-(0.023625))*(((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.013125))) <= 0.007875*0.007875)) & ~(((((((X-(1.35))-(0))-(-0.034125))-(0.023625)) >= (0.00525)) & (((((X-(1.35))-(0))-(-0.034125))-(0.023625)) <= (0.0105)) & (((((Y-(1.54))-(0.01575))-(0))-(-0.0315)) >= (0.018375)) & (((((Y-(1.54))-(0.01575))-(0))-(-0.0315)) <= (0.02625))))) & ~((((((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.0105))*(((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.0105)) + (((((Y-(1.54))-(0.01575))-(0))-(-0.0315))-(0.0223125))*(((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.0105))) <= 0.0039375*0.0039375))) & ~(((((((X-(1.35))-(0))-(-0.034125))-(0.023625)) >= (0.00525)) & (((((X-(1.35))-(0))-(-0.034125))-(0.023625)) <= (0.0105)) & (((((Y-(1.54))-(0.01575))-(0))-(-0.0315)) >= (0.00525)) & (((((Y-(1.54))-(0.01575))-(0))-(-0.0315)) <= (0.013125))))) & ~((((((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.0105))*(((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.0105)) + (((((Y-(1.54))-(0.01575))-(0))-(-0.0315))-(0.0091875))*(((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.0105))) <= 0.0039375*0.0039375)) | (((((((((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0.0105))*(((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0.0105)) + (((((Y-(1.54))-(0.01575))-(0))-(-0.0315))-(0.021))*(((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0.0105))) <= 0.0105*0.0105)) & ~((((((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0.0105))*(((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0.0105)) + (((((Y-(1.54))-(0.01575))-(0))-(-0.0315))-(0.021))*(((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0.0105))) <= 0.00525*0.00525))) & ~(((((((X-(1.35))-(0))-(-0.034125))-(0.04725)) >= (0)) & (((((X-(1.35))-(0))-(-0.034125))-(0.04725)) <= (0.0105)) & (((((Y-(1.54))-(0.01575))-(0))-(-0.0315)) >= (0)) & (((((Y-(1.54))-(0.01575))-(0))-(-0.0315)) <= (0.021)))) | ((((((X-(1.35))-(0))-(-0.034125))-(0.04725)) >= (0)) & (((((X-(1.35))-(0))-(-0.034125))-(0.04725)) <= (0.021)) & (((((Y-(1.54))-(0.01575))-(0))-(-0.0315)) >= (0)) & (((((Y-(1.54))-(0.01575))-(0))-(-0.0315)) <= (0.021)))) & ~((((((0.021)-(0.00525))*(((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0))-((0)-(0))*(((((Y-(1.54))-(0.01575))-(0))-(-0.0315))-(0.00525))) >= 0) & ((((0.021)-(0.021))*(((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0))-((0.01575)-(0))*(((((Y-(1.54))-(0.01575))-(0))-(-0.0315))-(0.021))) >= 0) & ((((0.00525)-(0.021))*(((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0.01575))-((0)-(0.01575))*(((((Y-(1.54))-(0.01575))-(0))-(-0.0315))-(0.021))) >= 0)))) & ~((((((0.018375)-(0.00525))*(((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0.007875))-((0.021)-(0.007875))*(((((Y-(1.54))-(0.01575))-(0))-(-0.0315))-(0.00525))) >= 0) & ((((0.00525)-(0.018375))*(((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0.021))-((0.021)-(0.021))*(((((Y-(1.54))-(0.01575))-(0))-(-0.0315))-(0.018375))) >= 0) & ((((0.00525)-(0.00525))*(((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0.021))-((0.007875)-(0.021))*(((((Y-(1.54))-(0.01575))-(0))-(-0.0315))-(0.00525))) >= 0))))!=0)) | (255*((False | False | ((((((((X-(1.35))-(0))-(-0.034125))-(0)) >= (0)) & (((((X-(1.35))-(0))-(-0.034125))-(0)) <= (0.00525)) & (((((Y-(1.49))-(0.01575))-(0))-(-0.0315)) >= (0)) & (((((Y-(1.49))-(0.01575))-(0))-(-0.0315)) <= (0.0315))) | (((((((X-(1.35))-(0))-(-0.034125))-(0))-(0.0105))*(((((X-(1.35))-(0))-(-0.034125))-(0))-(0.0105)) + (((((Y-(1.49))-(0.01575))-(0))-(-0.0315))-(0.021))*(((((X-(1.35))-(0))-(-0.034125))-(0))-(0.0105))) <= 0.0105*0.0105) | ((((((X-(1.35))-(0))-(-0.034125))-(0)) >= (0)) & (((((X-(1.35))-(0))-(-0.034125))-(0)) <= (0.0105)) & (((((Y-(1.49))-(0.01575))-(0))-(-0.0315)) >= (0.0105)) & (((((Y-(1.49))-(0.01575))-(0))-(-0.0315)) <= (0.0315)))) & ~((((((((X-(1.35))-(0))-(-0.034125))-(0))-(0.0105))*(((((X-(1.35))-(0))-(-0.034125))-(0))-(0.0105)) + (((((Y-(1.49))-(0.01575))-(0))-(-0.0315))-(0.021))*(((((X-(1.35))-(0))-(-0.034125))-(0))-(0.0105))) <= 0.00525*0.00525))) & ~(((((((X-(1.35))-(0))-(-0.034125))-(0)) >= (0.00525)) & (((((X-(1.35))-(0))-(-0.034125))-(0)) <= (0.0105)) & (((((Y-(1.49))-(0.01575))-(0))-(-0.0315)) >= (0.01575)) & (((((Y-(1.49))-(0.01575))-(0))-(-0.0315)) <= (0.02625)))) | ((((((0.0315)-(0))*(((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0))-((0.0105)-(0))*(((((Y-(1.49))-(0.01575))-(0))-(-0.0315))-(0))) >= 0) & ((((0)-(0.0315))*(((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.0105))-((0.021)-(0.0105))*(((((Y-(1.49))-(0.01575))-(0))-(-0.0315))-(0.0315))) >= 0) & ((((0)-(0))*(((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.021))-((0)-(0.021))*(((((Y-(1.49))-(0.01575))-(0))-(-0.0315))-(0))) >= 0))) & ~(((((((0.018375)-(-0.013125))*(((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0))-((0.0105)-(0))*(((((Y-(1.49))-(0.01575))-(0))-(-0.0315))-(-0.013125))) >= 0) & ((((-0.013125)-(0.018375))*(((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.0105))-((0.021)-(0.0105))*(((((Y-(1.49))-(0.01575))-(0))-(-0.0315))-(0.018375))) >= 0) & ((((-0.013125)-(-0.013125))*(((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.021))-((0)-(0.021))*(((((Y-(1.49))-(0.01575))-(0))-(-0.0315))-(-0.013125))) >= 0))) & ~(((((((X-(1.35))-(0))-(-0.034125))-(0.023625)) >= (0)) & (((((X-(1.35))-(0))-(-0.034125))-(0.023625)) <= (0.021)) & (((((Y-(1.49))-(0.01575))-(0))-(-0.0315)) >= (0.00525)) & (((((Y-(1.49))-(0.01575))-(0))-(-0.0315)) <= (0.0105))))) | ((((((((X-(1.35))-(0))-(-0.034125))-(0.04725)) >= (0)) & (((((X-(1.35))-(0))-(-0.034125))-(0.04725)) <= (0.021)) & (((((Y-(1.49))-(0.01575))-(0))-(-0.0315)) >= (0)) & (((((Y-(1.49))-(0.01575))-(0))-(-0.0315)) <= (0.0315)))) & ~((((((0.02625)-(0))*(((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0))-((0)-(0))*(((((Y-(1.49))-(0.01575))-(0))-(-0.0315))-(0))) >= 0) & ((((0.02625)-(0.02625))*(((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0))-((0.01575)-(0))*(((((Y-(1.49))-(0.01575))-(0))-(-0.0315))-(0.02625))) >= 0) & ((((0)-(0.02625))*(((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0.01575))-((0)-(0.01575))*(((((Y-(1.49))-(0.01575))-(0))-(-0.0315))-(0.02625))) >= 0)))) & ~((((((0.02625)-(0))*(((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0.00525))-((0.021)-(0.00525))*(((((Y-(1.49))-(0.01575))-(0))-(-0.0315))-(0))) >= 0) & ((((0)-(0.02625))*(((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0.021))-((0.021)-(0.021))*(((((Y-(1.49))-(0.01575))-(0))-(-0.0315))-(0.02625))) >= 0) & ((((0)-(0))*(((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0.021))-((0.00525)-(0.021))*(((((Y-(1.49))-(0.01575))-(0))-(-0.0315))-(0))) >= 0))))!=0)) | (255*((False | False | ((((((((X-(1.35))-(0))-(-0.034125))-(0)) >= (0)) & (((((X-(1.35))-(0))-(-0.034125))-(0)) <= (0.00525)) & (((((Y-(1.44))-(0.01575))-(0))-(-0.0315)) >= (0)) & (((((Y-(1.44))-(0.01575))-(0))-(-0.0315)) <= (0.0315))) | (((((((X-(1.35))-(0))-(-0.034125))-(0))-(0.0105))*(((((X-(1.35))-(0))-(-0.034125))-(0))-(0.0105)) + (((((Y-(1.44))-(0.01575))-(0))-(-0.0315))-(0.021))*(((((X-(1.35))-(0))-(-0.034125))-(0))-(0.0105))) <= 0.0105*0.0105) | ((((((X-(1.35))-(0))-(-0.034125))-(0)) >= (0)) & (((((X-(1.35))-(0))-(-0.034125))-(0)) <= (0.0105)) & (((((Y-(1.44))-(0.01575))-(0))-(-0.0315)) >= (0.0105)) & (((((Y-(1.44))-(0.01575))-(0))-(-0.0315)) <= (0.0315)))) & ~((((((((X-(1.35))-(0))-(-0.034125))-(0))-(0.0105))*(((((X-(1.35))-(0))-(-0.034125))-(0))-(0.0105)) + (((((Y-(1.44))-(0.01575))-(0))-(-0.0315))-(0.021))*(((((X-(1.35))-(0))-(-0.034125))-(0))-(0.0105))) <= 0.00525*0.00525))) & ~(((((((X-(1.35))-(0))-(-0.034125))-(0)) >= (0.00525)) & (((((X-(1.35))-(0))-(-0.034125))-(0)) <= (0.0105)) & (((((Y-(1.44))-(0.01575))-(0))-(-0.0315)) >= (0.01575)) & (((((Y-(1.44))-(0.01575))-(0))-(-0.0315)) <= (0.02625)))) | ((((((0.0315)-(0))*(((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0))-((0.0105)-(0))*(((((Y-(1.44))-(0.01575))-(0))-(-0.0315))-(0))) >= 0) & ((((0)-(0.0315))*(((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.0105))-((0.021)-(0.0105))*(((((Y-(1.44))-(0.01575))-(0))-(-0.0315))-(0.0315))) >= 0) & ((((0)-(0))*(((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.021))-((0)-(0.021))*(((((Y-(1.44))-(0.01575))-(0))-(-0.0315))-(0))) >= 0))) & ~(((((((0.018375)-(-0.013125))*(((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0))-((0.0105)-(0))*(((((Y-(1.44))-(0.01575))-(0))-(-0.0315))-(-0.013125))) >= 0) & ((((-0.013125)-(0.018375))*(((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.0105))-((0.021)-(0.0105))*(((((Y-(1.44))-(0.01575))-(0))-(-0.0315))-(0.018375))) >= 0) & ((((-0.013125)-(-0.013125))*(((((X-(1.35))-(0))-(-0.034125))-(0.023625))-(0.021))-((0)-(0.021))*(((((Y-(1.44))-(0.01575))-(0))-(-0.0315))-(-0.013125))) >= 0))) & ~(((((((X-(1.35))-(0))-(-0.034125))-(0.023625)) >= (0)) & (((((X-(1.35))-(0))-(-0.034125))-(0.023625)) <= (0.021)) & (((((Y-(1.44))-(0.01575))-(0))-(-0.0315)) >= (0.00525)) & (((((Y-(1.44))-(0.01575))-(0))-(-0.0315)) <= (0.0105))))) | (((((((((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0.0105))*(((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0.0105)) + (((((Y-(1.44))-(0.01575))-(0))-(-0.0315))-(0.021))*(((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0.0105))) <= 0.0105*0.0105)) & ~((((((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0.0105))*(((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0.0105)) + (((((Y-(1.44))-(0.01575))-(0))-(-0.0315))-(0.021))*(((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0.0105))) <= 0.00525*0.00525))) & ~(((((((X-(1.35))-(0))-(-0.034125))-(0.04725)) >= (0)) & (((((X-(1.35))-(0))-(-0.034125))-(0.04725)) <= (0.021)) & (((((Y-(1.44))-(0.01575))-(0))-(-0.0315)) >= (0)) & (((((Y-(1.44))-(0.01575))-(0))-(-0.0315)) <= (0.021))))) & ~((((((0.01575)-(0.0315))*(((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0.021))-((0.021)-(0.021))*(((((Y-(1.44))-(0.01575))-(0))-(-0.0315))-(0.0315))) >= 0) & ((((0.01575)-(0.01575))*(((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0.021))-((0.0105)-(0.021))*(((((Y-(1.44))-(0.01575))-(0))-(-0.0315))-(0.01575))) >= 0) & ((((0.0315)-(0.01575))*(((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0.0105))-((0.021)-(0.0105))*(((((Y-(1.44))-(0.01575))-(0))-(-0.0315))-(0.01575))) >= 0))) | (((((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0.0105))*(((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0.0105)) + (((((Y-(1.44))-(0.01575))-(0))-(-0.0315))-(0.0105))*(((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0.0105))) <= 0.0105*0.0105)) & ~((((((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0.0105))*(((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0.0105)) + (((((Y-(1.44))-(0.01575))-(0))-(-0.0315))-(0.0105))*(((((X-(1.35))-(0))-(-0.034125))-(0.04725))-(0.0105))) <= 0.00525*0.00525)) | ((((((X-(1.35))-(0))-(-0.034125))-(0.04725)) >= (0)) & (((((X-(1.35))-(0))-(-0.034125))-(0.04725)) <= (0.00525)) & (((((Y-(1.44))-(0.01575))-(0))-(-0.0315)) >= (0.0105)) & (((((Y-(1.44))-(0.01575))-(0))-(-0.0315)) <= (0.021))))!=0)) | (255*((False | False | ((((((((X-(1.59))-(0))-(-0.034125))-(0)) >= (0)) & (((((X-(1.59))-(0))-(-0.034125))-(0)) <= (0.00525)) & (((((Y-(1.44))-(0.01575))-(0))-(-0.0315)) >= (0)) & (((((Y-(1.44))-(0.01575))-(0))-(-0.0315)) <= (0.0315))) | (((((((X-(1.59))-(0))-(-0.034125))-(0))-(0.0105))*(((((X-(1.59))-(0))-(-0.034125))-(0))-(0.0105)) + (((((Y-(1.44))-(0.01575))-(0))-(-0.0315))-(0.021))*(((((X-(1.59))-(0))-(-0.034125))-(0))-(0.0105))) <= 0.0105*0.0105) | ((((((X-(1.59))-(0))-(-0.034125))-(0)) >= (0)) & (((((X-(1.59))-(0))-(-0.034125))-(0)) <= (0.0105)) & (((((Y-(1.44))-(0.01575))-(0))-(-0.0315)) >= (0.0105)) & (((((Y-(1.44))-(0.01575))-(0))-(-0.0315)) <= (0.0315)))) & ~((((((((X-(1.59))-(0))-(-0.034125))-(0))-(0.0105))*(((((X-(1.59))-(0))-(-0.034125))-(0))-(0.0105)) + (((((Y-(1.44))-(0.01575))-(0))-(-0.0315))-(0.021))*(((((X-(1.59))-(0))-(-0.034125))-(0))-(0.0105))) <= 0.00525*0.00525))) & ~(((((((X-(1.59))-(0))-(-0.034125))-(0)) >= (0.00525)) & (((((X-(1.59))-(0))-(-0.034125))-(0)) <= (0.0105)) & (((((Y-(1.44))-(0.01575))-(0))-(-0.0315)) >= (0.01575)) & (((((Y-(1.44))-(0.01575))-(0))-(-0.0315)) <= (0.02625)))) | ((((((0.0315)-(0))*(((((X-(1.59))-(0))-(-0.034125))-(0.023625))-(0))-((0.0105)-(0))*(((((Y-(1.44))-(0.01575))-(0))-(-0.0315))-(0))) >= 0) & ((((0)-(0.0315))*(((((X-(1.59))-(0))-(-0.034125))-(0.023625))-(0.0105))-((0.021)-(0.0105))*(((((Y-(1.44))-(0.01575))-(0))-(-0.0315))-(0.0315))) >= 0) & ((((0)-(0))*(((((X-(1.59))-(0))-(-0.034125))-(0.023625))-(0.021))-((0)-(0.021))*(((((Y-(1.44))-(0.01575))-(0))-(-0.0315))-(0))) >= 0))) & ~(((((((0.018375)-(-0.013125))*(((((X-(1.59))-(0))-(-0.034125))-(0.023625))-(0))-((0.0105)-(0))*(((((Y-(1.44))-(0.01575))-(0))-(-0.0315))-(-0.013125))) >= 0) & ((((-0.013125)-(0.018375))*(((((X-(1.59))-(0))-(-0.034125))-(0.023625))-(0.0105))-((0.021)-(0.0105))*(((((Y-(1.44))-(0.01575))-(0))-(-0.0315))-(0.018375))) >= 0) & ((((-0.013125)-(-0.013125))*(((((X-(1.59))-(0))-(-0.034125))-(0.023625))-(0.021))-((0)-(0.021))*(((((Y-(1.44))-(0.01575))-(0))-(-0.0315))-(-0.013125))) >= 0))) & ~(((((((X-(1.59))-(0))-(-0.034125))-(0.023625)) >= (0)) & (((((X-(1.59))-(0))-(-0.034125))-(0.023625)) <= (0.021)) & (((((Y-(1.44))-(0.01575))-(0))-(-0.0315)) >= (0.00525)) & (((((Y-(1.44))-(0.01575))-(0))-(-0.0315)) <= (0.0105))))) | (((((((((X-(1.59))-(0))-(-0.034125))-(0.04725))-(0.0105))*(((((X-(1.59))-(0))-(-0.034125))-(0.04725))-(0.0105)) + (((((Y-(1.44))-(0.01575))-(0))-(-0.0315))-(0.0105))*(((((X-(1.59))-(0))-(-0.034125))-(0.04725))-(0.0105))) <= 0.0105*0.0105)) & ~((((((((X-(1.59))-(0))-(-0.034125))-(0.04725))-(0.0105))*(((((X-(1.59))-(0))-(-0.034125))-(0.04725))-(0.0105)) + (((((Y-(1.44))-(0.01575))-(0))-(-0.0315))-(0.0105))*(((((X-(1.59))-(0))-(-0.034125))-(0.04725))-(0.0105))) <= 0.00525*0.00525))) & ~(((((((X-(1.59))-(0))-(-0.034125))-(0.04725)) >= (0)) & (((((X-(1.59))-(0))-(-0.034125))-(0.04725)) <= (0.0105)) & (((((Y-(1.44))-(0.01575))-(0))-(-0.0315)) >= (0.0105)) & (((((Y-(1.44))-(0.01575))-(0))-(-0.0315)) <= (0.021)))) | ((((((X-(1.59))-(0))-(-0.034125))-(0.04725)) >= (0)) & (((((X-(1.59))-(0))-(-0.034125))-(0.04725)) <= (0.0105)) & (((((Y-(1.44))-(0.01575))-(0))-(-0.0315)) >= (0.01575)) & (((((Y-(1.44))-(0.01575))-(0))-(-0.0315)) <= (0.021))) | ((((((X-(1.59))-(0))-(-0.034125))-(0.04725)) >= (0)) & (((((X-(1.59))-(0))-(-0.034125))-(0.04725)) <= (0.00525)) & (((((Y-(1.44))-(0.01575))-(0))-(-0.0315)) >= (0.01575)) & (((((Y-(1.44))-(0.01575))-(0))-(-0.0315)) <= (0.0315))) | ((((((X-(1.59))-(0))-(-0.034125))-(0.04725)) >= (0)) & (((((X-(1.59))-(0))-(-0.034125))-(0.04725)) <= (0.021)) & (((((Y-(1.44))-(0.01575))-(0))-(-0.0315)) >= (0.02625)) & (((((Y-(1.44))-(0.01575))-(0))-(-0.0315)) <= (0.0315))))!=0)) | (255*((False | False | ((((((((X-(1.59))-(0))-(-0.034125))-(0)) >= (0)) & (((((X-(1.59))-(0))-(-0.034125))-(0)) <= (0.00525)) & (((((Y-(1.49))-(0.01575))-(0))-(-0.0315)) >= (0)) & (((((Y-(1.49))-(0.01575))-(0))-(-0.0315)) <= (0.0315))) | (((((((X-(1.59))-(0))-(-0.034125))-(0))-(0.0105))*(((((X-(1.59))-(0))-(-0.034125))-(0))-(0.0105)) + (((((Y-(1.49))-(0.01575))-(0))-(-0.0315))-(0.021))*(((((X-(1.59))-(0))-(-0.034125))-(0))-(0.0105))) <= 0.0105*0.0105) | ((((((X-(1.59))-(0))-(-0.034125))-(0)) >= (0)) & (((((X-(1.59))-(0))-(-0.034125))-(0)) <= (0.0105)) & (((((Y-(1.49))-(0.01575))-(0))-(-0.0315)) >= (0.0105)) & (((((Y-(1.49))-(0.01575))-(0))-(-0.0315)) <= (0.0315)))) & ~((((((((X-(1.59))-(0))-(-0.034125))-(0))-(0.0105))*(((((X-(1.59))-(0))-(-0.034125))-(0))-(0.0105)) + (((((Y-(1.49))-(0.01575))-(0))-(-0.0315))-(0.021))*(((((X-(1.59))-(0))-(-0.034125))-(0))-(0.0105))) <= 0.00525*0.00525))) & ~(((((((X-(1.59))-(0))-(-0.034125))-(0)) >= (0.00525)) & (((((X-(1.59))-(0))-(-0.034125))-(0)) <= (0.0105)) & (((((Y-(1.49))-(0.01575))-(0))-(-0.0315)) >= (0.01575)) & (((((Y-(1.49))-(0.01575))-(0))-(-0.0315)) <= (0.02625)))) | ((((((0.0315)-(0))*(((((X-(1.59))-(0))-(-0.034125))-(0.023625))-(0))-((0.0105)-(0))*(((((Y-(1.49))-(0.01575))-(0))-(-0.0315))-(0))) >= 0) & ((((0)-(0.0315))*(((((X-(1.59))-(0))-(-0.034125))-(0.023625))-(0.0105))-((0.021)-(0.0105))*(((((Y-(1.49))-(0.01575))-(0))-(-0.0315))-(0.0315))) >= 0) & ((((0)-(0))*(((((X-(1.59))-(0))-(-0.034125))-(0.023625))-(0.021))-((0)-(0.021))*(((((Y-(1.49))-(0.01575))-(0))-(-0.0315))-(0))) >= 0))) & ~(((((((0.018375)-(-0.013125))*(((((X-(1.59))-(0))-(-0.034125))-(0.023625))-(0))-((0.0105)-(0))*(((((Y-(1.49))-(0.01575))-(0))-(-0.0315))-(-0.013125))) >= 0) & ((((-0.013125)-(0.018375))*(((((X-(1.59))-(0))-(-0.034125))-(0.023625))-(0.0105))-((0.021)-(0.0105))*(((((Y-(1.49))-(0.01575))-(0))-(-0.0315))-(0.018375))) >= 0) & ((((-0.013125)-(-0.013125))*(((((X-(1.59))-(0))-(-0.034125))-(0.023625))-(0.021))-((0)-(0.021))*(((((Y-(1.49))-(0.01575))-(0))-(-0.0315))-(-0.013125))) >= 0))) & ~(((((((X-(1.59))-(0))-(-0.034125))-(0.023625)) >= (0)) & (((((X-(1.59))-(0))-(-0.034125))-(0.023625)) <= (0.021)) & (((((Y-(1.49))-(0.01575))-(0))-(-0.0315)) >= (0.00525)) & (((((Y-(1.49))-(0.01575))-(0))-(-0.0315)) <= (0.0105))))) | (((((((X-(1.59))-(0))-(-0.034125))-(0.04725)) >= (0.01575)) & (((((X-(1.59))-(0))-(-0.034125))-(0.04725)) <= (0.021)) & (((((Y-(1.49))-(0.01575))-(0))-(-0.0315)) >= (0)) & (((((Y-(1.49))-(0.01575))-(0))-(-0.0315)) <= (0.0315))) | (((((0.0315)-(0.0105))*(((((X-(1.59))-(0))-(-0.034125))-(0.04725))-(0))-((0.01575)-(0))*(((((Y-(1.49))-(0.01575))-(0))-(-0.0315))-(0.0105))) >= 0) & ((((0.0105)-(0.0315))*(((((X-(1.59))-(0))-(-0.034125))-(0.04725))-(0.01575))-((0.01575)-(0.01575))*(((((Y-(1.49))-(0.01575))-(0))-(-0.0315))-(0.0315))) >= 0) & ((((0.0105)-(0.0105))*(((((X-(1.59))-(0))-(-0.034125))-(0.04725))-(0.01575))-((0)-(0.01575))*(((((Y-(1.49))-(0.01575))-(0))-(-0.0315))-(0.0105))) >= 0))) & ~((((((0.023625)-(0.01575))*(((((X-(1.59))-(0))-(-0.034125))-(0.04725))-(0.0091875))-((0.01575)-(0.0091875))*(((((Y-(1.49))-(0.01575))-(0))-(-0.0315))-(0.01575))) >= 0) & ((((0.01575)-(0.023625))*(((((X-(1.59))-(0))-(-0.034125))-(0.04725))-(0.01575))-((0.01575)-(0.01575))*(((((Y-(1.49))-(0.01575))-(0))-(-0.0315))-(0.023625))) >= 0) & ((((0.01575)-(0.01575))*(((((X-(1.59))-(0))-(-0.034125))-(0.04725))-(0.01575))-((0.0091875)-(0.01575))*(((((Y-(1.49))-(0.01575))-(0))-(-0.0315))-(0.01575))) >= 0))))!=0)) | (255*((False | False | ((((((((X-(1.59))-(0))-(-0.034125))-(0)) >= (0)) & (((((X-(1.59))-(0))-(-0.034125))-(0)) <= (0.00525)) & (((((Y-(1.54))-(0.01575))-(0))-(-0.0315)) >= (0)) & (((((Y-(1.54))-(0.01575))-(0))-(-0.0315)) <= (0.0315))) | (((((((X-(1.59))-(0))-(-0.034125))-(0))-(0.0105))*(((((X-(1.59))-(0))-(-0.034125))-(0))-(0.0105)) + (((((Y-(1.54))-(0.01575))-(0))-(-0.0315))-(0.021))*(((((X-(1.59))-(0))-(-0.034125))-(0))-(0.0105))) <= 0.0105*0.0105) | ((((((X-(1.59))-(0))-(-0.034125))-(0)) >= (0)) & (((((X-(1.59))-(0))-(-0.034125))-(0)) <= (0.0105)) & (((((Y-(1.54))-(0.01575))-(0))-(-0.0315)) >= (0.0105)) & (((((Y-(1.54))-(0.01575))-(0))-(-0.0315)) <= (0.0315)))) & ~((((((((X-(1.59))-(0))-(-0.034125))-(0))-(0.0105))*(((((X-(1.59))-(0))-(-0.034125))-(0))-(0.0105)) + (((((Y-(1.54))-(0.01575))-(0))-(-0.0315))-(0.021))*(((((X-(1.59))-(0))-(-0.034125))-(0))-(0.0105))) <= 0.00525*0.00525))) & ~(((((((X-(1.59))-(0))-(-0.034125))-(0)) >= (0.00525)) & (((((X-(1.59))-(0))-(-0.034125))-(0)) <= (0.0105)) & (((((Y-(1.54))-(0.01575))-(0))-(-0.0315)) >= (0.01575)) & (((((Y-(1.54))-(0.01575))-(0))-(-0.0315)) <= (0.02625)))) | ((((((0.0315)-(0))*(((((X-(1.59))-(0))-(-0.034125))-(0.023625))-(0))-((0.0105)-(0))*(((((Y-(1.54))-(0.01575))-(0))-(-0.0315))-(0))) >= 0) & ((((0)-(0.0315))*(((((X-(1.59))-(0))-(-0.034125))-(0.023625))-(0.0105))-((0.021)-(0.0105))*(((((Y-(1.54))-(0.01575))-(0))-(-0.0315))-(0.0315))) >= 0) & ((((0)-(0))*(((((X-(1.59))-(0))-(-0.034125))-(0.023625))-(0.021))-((0)-(0.021))*(((((Y-(1.54))-(0.01575))-(0))-(-0.0315))-(0))) >= 0))) & ~(((((((0.018375)-(-0.013125))*(((((X-(1.59))-(0))-(-0.034125))-(0.023625))-(0))-((0.0105)-(0))*(((((Y-(1.54))-(0.01575))-(0))-(-0.0315))-(-0.013125))) >= 0) & ((((-0.013125)-(0.018375))*(((((X-(1.59))-(0))-(-0.034125))-(0.023625))-(0.0105))-((0.021)-(0.0105))*(((((Y-(1.54))-(0.01575))-(0))-(-0.0315))-(0.018375))) >= 0) & ((((-0.013125)-(-0.013125))*(((((X-(1.59))-(0))-(-0.034125))-(0.023625))-(0.021))-((0)-(0.021))*(((((Y-(1.54))-(0.01575))-(0))-(-0.0315))-(-0.013125))) >= 0))) & ~(((((((X-(1.59))-(0))-(-0.034125))-(0.023625)) >= (0)) & (((((X-(1.59))-(0))-(-0.034125))-(0.023625)) <= (0.021)) & (((((Y-(1.54))-(0.01575))-(0))-(-0.0315)) >= (0.00525)) & (((((Y-(1.54))-(0.01575))-(0))-(-0.0315)) <= (0.0105))))) | (((((((((X-(1.59))-(0))-(-0.034125))-(0.04725))-(0.0105))*(((((X-(1.59))-(0))-(-0.034125))-(0.04725))-(0.0105)) + (((0) + (((((Y-(1.54))-(0.01575))-(0))-(-0.0315))-(0))/(0.875))-(0.0105))*(((((X-(1.59))-(0))-(-0.034125))-(0.04725))-(0.0105))) <= 0.0105*0.0105)) & ~((((((((X-(1.59))-(0))-(-0.034125))-(0.04725))-(0.0105))*(((((X-(1.59))-(0))-(-0.034125))-(0.04725))-(0.0105)) + (((0) + (((((Y-(1.54))-(0.01575))-(0))-(-0.0315))-(0))/(0.875))-(0.0105))*(((((X-(1.59))-(0))-(-0.034125))-(0.04725))-(0.0105))) <= 0.00525*0.00525)) | (((((((((X-(1.59))-(0))-(-0.034125))-(0.04725))-(0))-(0.0105))*((((((X-(1.59))-(0))-(-0.034125))-(0.04725))-(0))-(0.0105)) + (((0) + ((((((Y-(1.54))-(0.01575))-(0))-(-0.0315))-(0.013125))-(0))/(0.875))-(0.0105))*((((((X-(1.59))-(0))-(-0.034125))-(0.04725))-(0))-(0.0105))) <= 0.0105*0.0105)) & ~(((((((((X-(1.59))-(0))-(-0.034125))-(0.04725))-(0))-(0.0105))*((((((X-(1.59))-(0))-(-0.034125))-(0.04725))-(0))-(0.0105)) + (((0) + ((((((Y-(1.54))-(0.01575))-(0))-(-0.0315))-(0.013125))-(0))/(0.875))-(0.0105))*((((((X-(1.59))-(0))-(-0.034125))-(0.04725))-(0))-(0.0105))) <= 0.00525*0.00525))) & ~(((((((X-(1.59))-(0))-(-0.034125))-(0.04725)) >= (0)) & (((((X-(1.59))-(0))-(-0.034125))-(0.04725)) <= (0.0105)) & (((((Y-(1.54))-(0.01575))-(0))-(-0.0315)) >= (0.007875)) & (((((Y-(1.54))-(0.01575))-(0))-(-0.0315)) <= (0.023625)))))!=0)) | (255*((False | False | ((((((((X-(1.59))-(0))-(-0.034125))-(0)) >= (0)) & (((((X-(1.59))-(0))-(-0.034125))-(0)) <= (0.00525)) & (((((Y-(1.59))-(0.01575))-(0))-(-0.0315)) >= (0)) & (((((Y-(1.59))-(0.01575))-(0))-(-0.0315)) <= (0.0315))) | (((((((X-(1.59))-(0))-(-0.034125))-(0))-(0.0105))*(((((X-(1.59))-(0))-(-0.034125))-(0))-(0.0105)) + (((((Y-(1.59))-(0.01575))-(0))-(-0.0315))-(0.021))*(((((X-(1.59))-(0))-(-0.034125))-(0))-(0.0105))) <= 0.0105*0.0105) | ((((((X-(1.59))-(0))-(-0.034125))-(0)) >= (0)) & (((((X-(1.59))-(0))-(-0.034125))-(0)) <= (0.0105)) & (((((Y-(1.59))-(0.01575))-(0))-(-0.0315)) >= (0.0105)) & (((((Y-(1.59))-(0.01575))-(0))-(-0.0315)) <= (0.0315)))) & ~((((((((X-(1.59))-(0))-(-0.034125))-(0))-(0.0105))*(((((X-(1.59))-(0))-(-0.034125))-(0))-(0.0105)) + (((((Y-(1.59))-(0.01575))-(0))-(-0.0315))-(0.021))*(((((X-(1.59))-(0))-(-0.034125))-(0))-(0.0105))) <= 0.00525*0.00525))) & ~(((((((X-(1.59))-(0))-(-0.034125))-(0)) >= (0.00525)) & (((((X-(1.59))-(0))-(-0.034125))-(0)) <= (0.0105)) & (((((Y-(1.59))-(0.01575))-(0))-(-0.0315)) >= (0.01575)) & (((((Y-(1.59))-(0.01575))-(0))-(-0.0315)) <= (0.02625)))) | ((((((0.0315)-(0))*(((((X-(1.59))-(0))-(-0.034125))-(0.023625))-(0))-((0.0105)-(0))*(((((Y-(1.59))-(0.01575))-(0))-(-0.0315))-(0))) >= 0) & ((((0)-(0.0315))*(((((X-(1.59))-(0))-(-0.034125))-(0.023625))-(0.0105))-((0.021)-(0.0105))*(((((Y-(1.59))-(0.01575))-(0))-(-0.0315))-(0.0315))) >= 0) & ((((0)-(0))*(((((X-(1.59))-(0))-(-0.034125))-(0.023625))-(0.021))-((0)-(0.021))*(((((Y-(1.59))-(0.01575))-(0))-(-0.0315))-(0))) >= 0))) & ~(((((((0.018375)-(-0.013125))*(((((X-(1.59))-(0))-(-0.034125))-(0.023625))-(0))-((0.0105)-(0))*(((((Y-(1.59))-(0.01575))-(0))-(-0.0315))-(-0.013125))) >= 0) & ((((-0.013125)-(0.018375))*(((((X-(1.59))-(0))-(-0.034125))-(0.023625))-(0.0105))-((0.021)-(0.0105))*(((((Y-(1.59))-(0.01575))-(0))-(-0.0315))-(0.018375))) >= 0) & ((((-0.013125)-(-0.013125))*(((((X-(1.59))-(0))-(-0.034125))-(0.023625))-(0.021))-((0)-(0.021))*(((((Y-(1.59))-(0.01575))-(0))-(-0.0315))-(-0.013125))) >= 0))) & ~(((((((X-(1.59))-(0))-(-0.034125))-(0.023625)) >= (0)) & (((((X-(1.59))-(0))-(-0.034125))-(0.023625)) <= (0.021)) & (((((Y-(1.59))-(0.01575))-(0))-(-0.0315)) >= (0.00525)) & (((((Y-(1.59))-(0.01575))-(0))-(-0.0315)) <= (0.0105))))) | (((((((((((X-(1.59))-(0))-(-0.034125))-(0.04725))-(0.0105))*(((((X-(1.59))-(0))-(-0.034125))-(0.04725))-(0.0105)) + (((((Y-(1.59))-(0.01575))-(0))-(-0.0315))-(0.021))*(((((X-(1.59))-(0))-(-0.034125))-(0.04725))-(0.0105))) <= 0.0105*0.0105)) & ~((((((((X-(1.59))-(0))-(-0.034125))-(0.04725))-(0.0105))*(((((X-(1.59))-(0))-(-0.034125))-(0.04725))-(0.0105)) + (((((Y-(1.59))-(0.01575))-(0))-(-0.0315))-(0.021))*(((((X-(1.59))-(0))-(-0.034125))-(0.04725))-(0.0105))) <= 0.00525*0.00525))) & ~(((((((X-(1.59))-(0))-(-0.034125))-(0.04725)) >= (0)) & (((((X-(1.59))-(0))-(-0.034125))-(0.04725)) <= (0.0105)) & (((((Y-(1.59))-(0.01575))-(0))-(-0.0315)) >= (0)) & (((((Y-(1.59))-(0.01575))-(0))-(-0.0315)) <= (0.021)))) | ((((((X-(1.59))-(0))-(-0.034125))-(0.04725)) >= (0)) & (((((X-(1.59))-(0))-(-0.034125))-(0.04725)) <= (0.021)) & (((((Y-(1.59))-(0.01575))-(0))-(-0.0315)) >= (0)) & (((((Y-(1.59))-(0.01575))-(0))-(-0.0315)) <= (0.021)))) & ~((((((0.021)-(0.00525))*(((((X-(1.59))-(0))-(-0.034125))-(0.04725))-(0))-((0)-(0))*(((((Y-(1.59))-(0.01575))-(0))-(-0.0315))-(0.00525))) >= 0) & ((((0.021)-(0.021))*(((((X-(1.59))-(0))-(-0.034125))-(0.04725))-(0))-((0.01575)-(0))*(((((Y-(1.59))-(0.01575))-(0))-(-0.0315))-(0.021))) >= 0) & ((((0.00525)-(0.021))*(((((X-(1.59))-(0))-(-0.034125))-(0.04725))-(0.01575))-((0)-(0.01575))*(((((Y-(1.59))-(0.01575))-(0))-(-0.0315))-(0.021))) >= 0)))) & ~((((((0.018375)-(0.00525))*(((((X-(1.59))-(0))-(-0.034125))-(0.04725))-(0.007875))-((0.021)-(0.007875))*(((((Y-(1.59))-(0.01575))-(0))-(-0.0315))-(0.00525))) >= 0) & ((((0.00525)-(0.018375))*(((((X-(1.59))-(0))-(-0.034125))-(0.04725))-(0.021))-((0.021)-(0.021))*(((((Y-(1.59))-(0.01575))-(0))-(-0.0315))-(0.018375))) >= 0) & ((((0.00525)-(0.00525))*(((((X-(1.59))-(0))-(-0.034125))-(0.04725))-(0.021))-((0.007875)-(0.021))*(((((Y-(1.59))-(0.01575))-(0))-(-0.0315))-(0.00525))) >= 0))))!=0)) | (255*((False | False | ((((((((X-(1.59))-(0))-(-0.034125))-(0)) >= (0)) & (((((X-(1.59))-(0))-(-0.034125))-(0)) <= (0.00525)) & (((((Y-(1.64))-(0.01575))-(0))-(-0.0315)) >= (0)) & (((((Y-(1.64))-(0.01575))-(0))-(-0.0315)) <= (0.0315))) | (((((((X-(1.59))-(0))-(-0.034125))-(0))-(0.0105))*(((((X-(1.59))-(0))-(-0.034125))-(0))-(0.0105)) + (((((Y-(1.64))-(0.01575))-(0))-(-0.0315))-(0.021))*(((((X-(1.59))-(0))-(-0.034125))-(0))-(0.0105))) <= 0.0105*0.0105) | ((((((X-(1.59))-(0))-(-0.034125))-(0)) >= (0)) & (((((X-(1.59))-(0))-(-0.034125))-(0)) <= (0.0105)) & (((((Y-(1.64))-(0.01575))-(0))-(-0.0315)) >= (0.0105)) & (((((Y-(1.64))-(0.01575))-(0))-(-0.0315)) <= (0.0315)))) & ~((((((((X-(1.59))-(0))-(-0.034125))-(0))-(0.0105))*(((((X-(1.59))-(0))-(-0.034125))-(0))-(0.0105)) + (((((Y-(1.64))-(0.01575))-(0))-(-0.0315))-(0.021))*(((((X-(1.59))-(0))-(-0.034125))-(0))-(0.0105))) <= 0.00525*0.00525))) & ~(((((((X-(1.59))-(0))-(-0.034125))-(0)) >= (0.00525)) & (((((X-(1.59))-(0))-(-0.034125))-(0)) <= (0.0105)) & (((((Y-(1.64))-(0.01575))-(0))-(-0.0315)) >= (0.01575)) & (((((Y-(1.64))-(0.01575))-(0))-(-0.0315)) <= (0.02625)))) | ((((((0.0315)-(0))*(((((X-(1.59))-(0))-(-0.034125))-(0.023625))-(0))-((0.0105)-(0))*(((((Y-(1.64))-(0.01575))-(0))-(-0.0315))-(0))) >= 0) & ((((0)-(0.0315))*(((((X-(1.59))-(0))-(-0.034125))-(0.023625))-(0.0105))-((0.021)-(0.0105))*(((((Y-(1.64))-(0.01575))-(0))-(-0.0315))-(0.0315))) >= 0) & ((((0)-(0))*(((((X-(1.59))-(0))-(-0.034125))-(0.023625))-(0.021))-((0)-(0.021))*(((((Y-(1.64))-(0.01575))-(0))-(-0.0315))-(0))) >= 0))) & ~(((((((0.018375)-(-0.013125))*(((((X-(1.59))-(0))-(-0.034125))-(0.023625))-(0))-((0.0105)-(0))*(((((Y-(1.64))-(0.01575))-(0))-(-0.0315))-(-0.013125))) >= 0) & ((((-0.013125)-(0.018375))*(((((X-(1.59))-(0))-(-0.034125))-(0.023625))-(0.0105))-((0.021)-(0.0105))*(((((Y-(1.64))-(0.01575))-(0))-(-0.0315))-(0.018375))) >= 0) & ((((-0.013125)-(-0.013125))*(((((X-(1.59))-(0))-(-0.034125))-(0.023625))-(0.021))-((0)-(0.021))*(((((Y-(1.64))-(0.01575))-(0))-(-0.0315))-(-0.013125))) >= 0))) & ~(((((((X-(1.59))-(0))-(-0.034125))-(0.023625)) >= (0)) & (((((X-(1.59))-(0))-(-0.034125))-(0.023625)) <= (0.021)) & (((((Y-(1.64))-(0.01575))-(0))-(-0.0315)) >= (0.00525)) & (((((Y-(1.64))-(0.01575))-(0))-(-0.0315)) <= (0.0105))))) | ((((((((X-(1.59))-(0))-(-0.034125))-(0.04725))-(0.00328125)) >= (0.007875)) & ((((((X-(1.59))-(0))-(-0.034125))-(0.04725))-(0.00328125)) <= (0.013125)) & ((((((Y-(1.64))-(0.01575))-(0))-(-0.0315))-(0)) >= (0)) & ((((((Y-(1.64))-(0.01575))-(0))-(-0.0315))-(0)) <= (0.0315))) | (((((((X-(1.59))-(0))-(-0.034125))-(0.04725))-(0.00328125)) >= (0)) & ((((((X-(1.59))-(0))-(-0.034125))-(0.04725))-(0.00328125)) <= (0.0105)) & ((((((Y-(1.64))-(0.01575))-(0))-(-0.0315))-(0)) >= (0.018375)) & ((((((Y-(1.64))-(0.01575))-(0))-(-0.0315))-(0)) <= (0.0315)))) & ~(((((((((X-(1.59))-(0))-(-0.034125))-(0.04725))-(0.00328125))-(0))*((((((X-(1.59))-(0))-(-0.034125))-(0.04725))-(0.00328125))-(0)) + ((((((Y-(1.64))-(0.01575))-(0))-(-0.0315))-(0))-(0.0315))*((((((X-(1.59))-(0))-(-0.034125))-(0.04725))-(0.00328125))-(0))) <= 0.007875*0.007875)))!=0)) | (255*((False | False | ((((((((X-(1.59))-(0))-(-0.034125))-(0)) >= (0)) & (((((X-(1.59))-(0))-(-0.034125))-(0)) <= (0.00525)) & (((((Y-(1.69))-(0.01575))-(0))-(-0.0315)) >= (0)) & (((((Y-(1.69))-(0.01575))-(0))-(-0.0315)) <= (0.0315))) | (((((((X-(1.59))-(0))-(-0.034125))-(0))-(0.0105))*(((((X-(1.59))-(0))-(-0.034125))-(0))-(0.0105)) + (((((Y-(1.69))-(0.01575))-(0))-(-0.0315))-(0.021))*(((((X-(1.59))-(0))-(-0.034125))-(0))-(0.0105))) <= 0.0105*0.0105) | ((((((X-(1.59))-(0))-(-0.034125))-(0)) >= (0)) & (((((X-(1.59))-(0))-(-0.034125))-(0)) <= (0.0105)) & (((((Y-(1.69))-(0.01575))-(0))-(-0.0315)) >= (0.0105)) & (((((Y-(1.69))-(0.01575))-(0))-(-0.0315)) <= (0.0315)))) & ~((((((((X-(1.59))-(0))-(-0.034125))-(0))-(0.0105))*(((((X-(1.59))-(0))-(-0.034125))-(0))-(0.0105)) + (((((Y-(1.69))-(0.01575))-(0))-(-0.0315))-(0.021))*(((((X-(1.59))-(0))-(-0.034125))-(0))-(0.0105))) <= 0.00525*0.00525))) & ~(((((((X-(1.59))-(0))-(-0.034125))-(0)) >= (0.00525)) & (((((X-(1.59))-(0))-(-0.034125))-(0)) <= (0.0105)) & (((((Y-(1.69))-(0.01575))-(0))-(-0.0315)) >= (0.01575)) & (((((Y-(1.69))-(0.01575))-(0))-(-0.0315)) <= (0.02625)))) | ((((((0.0315)-(0))*(((((X-(1.59))-(0))-(-0.034125))-(0.023625))-(0))-((0.0105)-(0))*(((((Y-(1.69))-(0.01575))-(0))-(-0.0315))-(0))) >= 0) & ((((0)-(0.0315))*(((((X-(1.59))-(0))-(-0.034125))-(0.023625))-(0.0105))-((0.021)-(0.0105))*(((((Y-(1.69))-(0.01575))-(0))-(-0.0315))-(0.0315))) >= 0) & ((((0)-(0))*(((((X-(1.59))-(0))-(-0.034125))-(0.023625))-(0.021))-((0)-(0.021))*(((((Y-(1.69))-(0.01575))-(0))-(-0.0315))-(0))) >= 0))) & ~(((((((0.018375)-(-0.013125))*(((((X-(1.59))-(0))-(-0.034125))-(0.023625))-(0))-((0.0105)-(0))*(((((Y-(1.69))-(0.01575))-(0))-(-0.0315))-(-0.013125))) >= 0) & ((((-0.013125)-(0.018375))*(((((X-(1.59))-(0))-(-0.034125))-(0.023625))-(0.0105))-((0.021)-(0.0105))*(((((Y-(1.69))-(0.01575))-(0))-(-0.0315))-(0.018375))) >= 0) & ((((-0.013125)-(-0.013125))*(((((X-(1.59))-(0))-(-0.034125))-(0.023625))-(0.021))-((0)-(0.021))*(((((Y-(1.69))-(0.01575))-(0))-(-0.0315))-(-0.013125))) >= 0))) & ~(((((((X-(1.59))-(0))-(-0.034125))-(0.023625)) >= (0)) & (((((X-(1.59))-(0))-(-0.034125))-(0.023625)) <= (0.021)) & (((((Y-(1.69))-(0.01575))-(0))-(-0.0315)) >= (0.00525)) & (((((Y-(1.69))-(0.01575))-(0))-(-0.0315)) <= (0.0105))))) | ((((((((X-(1.59))-(0))-(-0.034125))-(0.04725))-(0.0105))*(((((X-(1.59))-(0))-(-0.034125))-(0.04725))-(0.0105)) + (((0) + (((((Y-(1.69))-(0.01575))-(0))-(-0.0315))-(0))/(1.5))-(0.0105))*(((((X-(1.59))-(0))-(-0.034125))-(0.04725))-(0.0105))) <= 0.0105*0.0105)) & ~((((((((X-(1.59))-(0))-(-0.034125))-(0.04725))-(0.0105))*(((((X-(1.59))-(0))-(-0.034125))-(0.04725))-(0.0105)) + (((0) + (((((Y-(1.69))-(0.01575))-(0))-(-0.0315))-(0))/(1.5))-(0.0105))*(((((X-(1.59))-(0))-(-0.034125))-(0.04725))-(0.0105))) <= 0.00525*0.00525)))!=0)) | (255*((False | False | ((((((((((X-(1.59))-(0))-(-0.034125))-(0))-(0.0105))*(((((X-(1.59))-(0))-(-0.034125))-(0))-(0.0105)) + (((((Y-(1.74))-(0.01575))-(0))-(-0.0315))-(0.0105))*(((((X-(1.59))-(0))-(-0.034125))-(0))-(0.0105))) <= 0.0105*0.0105) | (((((((X-(1.59))-(0))-(-0.034125))-(0))-(0.0105))*(((((X-(1.59))-(0))-(-0.034125))-(0))-(0.0105)) + (((((Y-(1.74))-(0.01575))-(0))-(-0.0315))-(0.021))*(((((X-(1.59))-(0))-(-0.034125))-(0))-(0.0105))) <= 0.0105*0.0105) | ((((((X-(1.59))-(0))-(-0.034125))-(0)) >= (0)) & (((((X-(1.59))-(0))-(-0.034125))-(0)) <= (0.021)) & (((((Y-(1.74))-(0.01575))-(0))-(-0.0315)) >= (0.0105)) & (((((Y-(1.74))-(0.01575))-(0))-(-0.0315)) <= (0.021)))) & ~((((((((X-(1.59))-(0))-(-0.034125))-(0))-(0.0105))*(((((X-(1.59))-(0))-(-0.034125))-(0))-(0.0105)) + (((((Y-(1.74))-(0.01575))-(0))-(-0.0315))-(0.0105))*(((((X-(1.59))-(0))-(-0.034125))-(0))-(0.0105))) <= 0.00525*0.00525))) & ~((((((((X-(1.59))-(0))-(-0.034125))-(0))-(0.0105))*(((((X-(1.59))-(0))-(-0.034125))-(0))-(0.0105)) + (((((Y-(1.74))-(0.01575))-(0))-(-0.0315))-(0.021))*(((((X-(1.59))-(0))-(-0.034125))-(0))-(0.0105))) <= 0.00525*0.00525))) & ~(((((((X-(1.59))-(0))-(-0.034125))-(0)) >= (0.00525)) & (((((X-(1.59))-(0))-(-0.034125))-(0)) <= (0.021)) & (((((Y-(1.74))-(0.01575))-(0))-(-0.0315)) >= (0.0105)) & (((((Y-(1.74))-(0.01575))-(0))-(-0.0315)) <= (0.021)))) | ((((((X-(1.59))-(0))-(-0.034125))-(0)) >= (0.0105)) & (((((X-(1.59))-(0))-(-0.034125))-(0)) <= (0.021)) & (((((Y-(1.74))-(0.01575))-(0))-(-0.0315)) >= (0.0105)) & (((((Y-(1.74))-(0.01575))-(0))-(-0.0315)) <= (0.01575))) | ((((((((X-(1.59))-(0))-(-0.034125))-(0.023625)) >= (0)) & (((((X-(1.59))-(0))-(-0.034125))-(0.023625)) <= (0.021)) & (((((Y-(1.74))-(0.01575))-(0))-(-0.0315)) >= (0)) & (((((Y-(1.74))-(0.01575))-(0))-(-0.0315)) <= (0.0315)))) & ~((((((0.039375)-(0.039375))*(((((X-(1.59))-(0))-(-0.034125))-(0.023625))-(0.00525))-((0.01575)-(0.00525))*(((((Y-(1.74))-(0.01575))-(0))-(-0.0315))-(0.039375))) >= 0) & ((((0.007875)-(0.039375))*(((((X-(1.59))-(0))-(-0.034125))-(0.023625))-(0.01575))-((0.01575)-(0.01575))*(((((Y-(1.74))-(0.01575))-(0))-(-0.0315))-(0.039375))) >= 0) & ((((0.039375)-(0.007875))*(((((X-(1.59))-(0))-(-0.034125))-(0.023625))-(0.01575))-((0.00525)-(0.01575))*(((((Y-(1.74))-(0.01575))-(0))-(-0.0315))-(0.007875))) >= 0)))) & ~((((((0.023625)-(-0.007875))*(((((X-(1.59))-(0))-(-0.034125))-(0.023625))-(0.00525))-((0.00525)-(0.00525))*(((((Y-(1.74))-(0.01575))-(0))-(-0.0315))-(-0.007875))) >= 0) & ((((-0.007875)-(0.023625))*(((((X-(1.59))-(0))-(-0.034125))-(0.023625))-(0.00525))-((0.01575)-(0.00525))*(((((Y-(1.74))-(0.01575))-(0))-(-0.0315))-(0.023625))) >= 0) & ((((-0.007875)-(-0.007875))*(((((X-(1.59))-(0))-(-0.034125))-(0.023625))-(0.01575))-((0.00525)-(0.01575))*(((((Y-(1.74))-(0.01575))-(0))-(-0.0315))-(-0.007875))) >= 0))) | (((((((((X-(1.59))-(0))-(-0.034125))-(0.04725))-(0.00525))*(((((X-(1.59))-(0))-(-0.034125))-(0.04725))-(0.00525)) + (((0) + (((((Y-(1.74))-(0.01575))-(0))-(-0.0315))-(0))/(1.0))-(0.01575))*(((((X-(1.59))-(0))-(-0.034125))-(0.04725))-(0.00525))) <= 0.01575*0.01575)) & ~((((((((X-(1.59))-(0))-(-0.034125))-(0.04725))-(0.00525))*(((((X-(1.59))-(0))-(-0.034125))-(0.04725))-(0.00525)) + (((0) + (((((Y-(1.74))-(0.01575))-(0))-(-0.0315))-(0))/(1.0))-(0.01575))*(((((X-(1.59))-(0))-(-0.034125))-(0.04725))-(0.00525))) <= 0.0105*0.0105))) & ~(((((((X-(1.59))-(0))-(-0.034125))-(0.04725)) >= (-0.021)) & (((((X-(1.59))-(0))-(-0.034125))-(0.04725)) <= (0.00525)) & (((0) + (((((Y-(1.74))-(0.01575))-(0))-(-0.0315))-(0))/(1.0)) >= (0)) & (((0) + (((((Y-(1.74))-(0.01575))-(0))-(-0.0315))-(0))/(1.0)) <= (0.0315)))) | ((((((X-(1.59))-(0))-(-0.034125))-(0.04725)) >= (0)) & (((((X-(1.59))-(0))-(-0.034125))-(0.04725)) <= (0.00525)) & (((((Y-(1.74))-(0.01575))-(0))-(-0.0315)) >= (0)) & (((((Y-(1.74))-(0.01575))-(0))-(-0.0315)) <= (0.0315))))!=0)) | (65280*((False | False | (((((((((X-(1.52))-(0))-(-0.02975))-(0))-(0.014))*(((((X-(1.52))-(0))-(-0.02975))-(0))-(0.014)) + (((((Y-(1.22))-(0.0525))-(0))-(-0.042))-(0.014))*(((((X-(1.52))-(0))-(-0.02975))-(0))-(0.014))) <= 0.014*0.014)) & ~((((((((X-(1.52))-(0))-(-0.02975))-(0))-(0.014))*(((((X-(1.52))-(0))-(-0.02975))-(0))-(0.014)) + (((((Y-(1.22))-(0.0525))-(0))-(-0.042))-(0.014))*(((((X-(1.52))-(0))-(-0.02975))-(0))-(0.014))) <= 0.007*0.007))) & ~(((((((X-(1.52))-(0))-(-0.02975))-(0)) >= (0)) & (((((X-(1.52))-(0))-(-0.02975))-(0)) <= (0.028)) & (((((Y-(1.22))-(0.0525))-(0))-(-0.042)) >= (0.014)) & (((((Y-(1.22))-(0.0525))-(0))-(-0.042)) <= (0.042)))) | ((((((X-(1.52))-(0))-(-0.02975))-(0)) >= (0.021)) & (((((X-(1.52))-(0))-(-0.02975))-(0)) <= (0.028)) & (((((Y-(1.22))-(0.0525))-(0))-(-0.042)) >= (0.014)) & (((((Y-(1.22))-(0.0525))-(0))-(-0.042)) <= (0.042))) | ((((((((X-(1.52))-(0))-(-0.02975))-(0.0315))-(0.004375)) >= (0.0105)) & ((((((X-(1.52))-(0))-(-0.02975))-(0.0315))-(0.004375)) <= (0.0175)) & ((((((Y-(1.22))-(0.0525))-(0))-(-0.042))-(0)) >= (0)) & ((((((Y-(1.22))-(0.0525))-(0))-(-0.042))-(0)) <= (0.042))) | (((((((X-(1.52))-(0))-(-0.02975))-(0.0315))-(0.004375)) >= (0)) & ((((((X-(1.52))-(0))-(-0.02975))-(0.0315))-(0.004375)) <= (0.014)) & ((((((Y-(1.22))-(0.0525))-(0))-(-0.042))-(0)) >= (0.0245)) & ((((((Y-(1.22))-(0.0525))-(0))-(-0.042))-(0)) <= (0.042)))) & ~(((((((((X-(1.52))-(0))-(-0.02975))-(0.0315))-(0.004375))-(0))*((((((X-(1.52))-(0))-(-0.02975))-(0.0315))-(0.004375))-(0)) + ((((((Y-(1.22))-(0.0525))-(0))-(-0.042))-(0))-(0.042))*((((((X-(1.52))-(0))-(-0.02975))-(0.0315))-(0.004375))-(0))) <= 0.0105*0.0105)) | False | ((((((X-(1.52))-(0))-(-0.0455))-(0)) >= (0.0105)) & (((((X-(1.52))-(0))-(-0.0455))-(0)) <= (0.0175)) & (((((Y-(1.22))-(0.0525))-(0))-(-0.105)) >= (0)) & (((((Y-(1.22))-(0.0525))-(0))-(-0.105)) <= (0.042))) | ((((((X-(1.52))-(0))-(-0.0455))-(0)) >= (0.0056)) & (((((X-(1.52))-(0))-(-0.0455))-(0)) <= (0.0224)) & (((((Y-(1.22))-(0.0525))-(0))-(-0.105)) >= (0)) & (((((Y-(1.22))-(0.0525))-(0))-(-0.105)) <= (0.007))) | ((((((X-(1.52))-(0))-(-0.0455))-(0)) >= (0.0056)) & (((((X-(1.52))-(0))-(-0.0455))-(0)) <= (0.0224)) & (((((Y-(1.22))-(0.0525))-(0))-(-0.105)) >= (0.035)) & (((((Y-(1.22))-(0.0525))-(0))-(-0.105)) <= (0.042))) | ((((((X-(1.52))-(0))-(-0.0455))-(0.0315)) >= (0)) & (((((X-(1.52))-(0))-(-0.0455))-(0.0315)) <= (0.028)) & (((((Y-(1.22))-(0.0525))-(0))-(-0.105)) >= (0)) & (((((Y-(1.22))-(0.0525))-(0))-(-0.105)) <= (0.042))) | ((((((((X-(1.52))-(0))-(-0.0455))-(0.063)) >= (0)) & (((((X-(1.52))-(0))-(-0.0455))-(0.063)) <= (0.007)) & (((((Y-(1.22))-(0.0525))-(0))-(-0.105)) >= (0)) & (((((Y-(1.22))-(0.0525))-(0))-(-0.105)) <= (0.042))) | (((((((X-(1.52))-(0))-(-0.0455))-(0.063))-(0.014))*(((((X-(1.52))-(0))-(-0.0455))-(0.063))-(0.014)) + (((((Y-(1.22))-(0.0525))-(0))-(-0.105))-(0.028))*(((((X-(1.52))-(0))-(-0.0455))-(0.063))-(0.014))) <= 0.014*0.014) | ((((((X-(1.52))-(0))-(-0.0455))-(0.063)) >= (0)) & (((((X-(1.52))-(0))-(-0.0455))-(0.063)) <= (0.014)) & (((((Y-(1.22))-(0.0525))-(0))-(-0.105)) >= (0.014)) & (((((Y-(1.22))-(0.0525))-(0))-(-0.105)) <= (0.042)))) & ~((((((((X-(1.52))-(0))-(-0.0455))-(0.063))-(0.014))*(((((X-(1.52))-(0))-(-0.0455))-(0.063))-(0.014)) + (((((Y-(1.22))-(0.0525))-(0))-(-0.105))-(0.028))*(((((X-(1.52))-(0))-(-0.0455))-(0.063))-(0.014))) <= 0.007*0.007))) & ~(((((((X-(1.52))-(0))-(-0.0455))-(0.063)) >= (0.007)) & (((((X-(1.52))-(0))-(-0.0455))-(0.063)) <= (0.014)) & (((((Y-(1.22))-(0.0525))-(0))-(-0.105)) >= (0.021)) & (((((Y-(1.22))-(0.0525))-(0))-(-0.105)) <= (0.035)))))!=0)) | (255*((False | False | ((((((cos(-1.57079632679)*(X-(1.62))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.0459375))-(0)) >= (0)) & (((((cos(-1.57079632679)*(X-(1.62))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.0459375))-(0)) <= (0.021)) & (((((-sin(-1.57079632679)*(X-(1.62))+cos(-1.57079632679)*(Y-(1.327)))-(0.01575))-(0))-(-0.0315)) >= (0)) & (((((-sin(-1.57079632679)*(X-(1.62))+cos(-1.57079632679)*(Y-(1.327)))-(0.01575))-(0))-(-0.0315)) <= (0.0315))) | ((((((cos(-1.57079632679)*(X-(1.62))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.0459375))-(0.023625)) >= (0.007875)) & (((((cos(-1.57079632679)*(X-(1.62))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.0459375))-(0.023625)) <= (0.013125)) & (((((-sin(-1.57079632679)*(X-(1.62))+cos(-1.57079632679)*(Y-(1.327)))-(0.01575))-(0))-(-0.0315)) >= (0)) & (((((-sin(-1.57079632679)*(X-(1.62))+cos(-1.57079632679)*(Y-(1.327)))-(0.01575))-(0))-(-0.0315)) <= (0.0315))) | ((((((cos(-1.57079632679)*(X-(1.62))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.0459375))-(0.023625)) >= (0.0042)) & (((((cos(-1.57079632679)*(X-(1.62))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.0459375))-(0.023625)) <= (0.0168)) & (((((-sin(-1.57079632679)*(X-(1.62))+cos(-1.57079632679)*(Y-(1.327)))-(0.01575))-(0))-(-0.0315)) >= (0)) & (((((-sin(-1.57079632679)*(X-(1.62))+cos(-1.57079632679)*(Y-(1.327)))-(0.01575))-(0))-(-0.0315)) <= (0.00525))) | ((((((cos(-1.57079632679)*(X-(1.62))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.0459375))-(0.023625)) >= (0.0042)) & (((((cos(-1.57079632679)*(X-(1.62))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.0459375))-(0.023625)) <= (0.0168)) & (((((-sin(-1.57079632679)*(X-(1.62))+cos(-1.57079632679)*(Y-(1.327)))-(0.01575))-(0))-(-0.0315)) >= (0.02625)) & (((((-sin(-1.57079632679)*(X-(1.62))+cos(-1.57079632679)*(Y-(1.327)))-(0.01575))-(0))-(-0.0315)) <= (0.0315))) | ((((((cos(-1.57079632679)*(X-(1.62))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.0459375))-(0.04725)) >= (0)) & (((((cos(-1.57079632679)*(X-(1.62))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.0459375))-(0.04725)) <= (0.021)) & (((((-sin(-1.57079632679)*(X-(1.62))+cos(-1.57079632679)*(Y-(1.327)))-(0.01575))-(0))-(-0.0315)) >= (0)) & (((((-sin(-1.57079632679)*(X-(1.62))+cos(-1.57079632679)*(Y-(1.327)))-(0.01575))-(0))-(-0.0315)) <= (0.0315))) | ((((((((((cos(-1.57079632679)*(X-(1.62))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.0459375))-(0.070875))-(0.0105))*(((((cos(-1.57079632679)*(X-(1.62))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.0459375))-(0.070875))-(0.0105)) + (((((-sin(-1.57079632679)*(X-(1.62))+cos(-1.57079632679)*(Y-(1.327)))-(0.01575))-(0))-(-0.0315))-(0.0105))*(((((cos(-1.57079632679)*(X-(1.62))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.0459375))-(0.070875))-(0.0105))) <= 0.0105*0.0105) | (((((((cos(-1.57079632679)*(X-(1.62))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.0459375))-(0.070875))-(0.0105))*(((((cos(-1.57079632679)*(X-(1.62))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.0459375))-(0.070875))-(0.0105)) + (((((-sin(-1.57079632679)*(X-(1.62))+cos(-1.57079632679)*(Y-(1.327)))-(0.01575))-(0))-(-0.0315))-(0.021))*(((((cos(-1.57079632679)*(X-(1.62))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.0459375))-(0.070875))-(0.0105))) <= 0.0105*0.0105) | ((((((cos(-1.57079632679)*(X-(1.62))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.0459375))-(0.070875)) >= (0)) & (((((cos(-1.57079632679)*(X-(1.62))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.0459375))-(0.070875)) <= (0.021)) & (((((-sin(-1.57079632679)*(X-(1.62))+cos(-1.57079632679)*(Y-(1.327)))-(0.01575))-(0))-(-0.0315)) >= (0.0105)) & (((((-sin(-1.57079632679)*(X-(1.62))+cos(-1.57079632679)*(Y-(1.327)))-(0.01575))-(0))-(-0.0315)) <= (0.021)))) & ~((((((((cos(-1.57079632679)*(X-(1.62))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.0459375))-(0.070875))-(0.0105))*(((((cos(-1.57079632679)*(X-(1.62))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.0459375))-(0.070875))-(0.0105)) + (((((-sin(-1.57079632679)*(X-(1.62))+cos(-1.57079632679)*(Y-(1.327)))-(0.01575))-(0))-(-0.0315))-(0.0105))*(((((cos(-1.57079632679)*(X-(1.62))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.0459375))-(0.070875))-(0.0105))) <= 0.00525*0.00525))) & ~((((((((cos(-1.57079632679)*(X-(1.62))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.0459375))-(0.070875))-(0.0105))*(((((cos(-1.57079632679)*(X-(1.62))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.0459375))-(0.070875))-(0.0105)) + (((((-sin(-1.57079632679)*(X-(1.62))+cos(-1.57079632679)*(Y-(1.327)))-(0.01575))-(0))-(-0.0315))-(0.021))*(((((cos(-1.57079632679)*(X-(1.62))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.0459375))-(0.070875))-(0.0105))) <= 0.00525*0.00525))) & ~(((((((cos(-1.57079632679)*(X-(1.62))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.0459375))-(0.070875)) >= (0.00525)) & (((((cos(-1.57079632679)*(X-(1.62))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.0459375))-(0.070875)) <= (0.01575)) & (((((-sin(-1.57079632679)*(X-(1.62))+cos(-1.57079632679)*(Y-(1.327)))-(0.01575))-(0))-(-0.0315)) >= (0.0105)) & (((((-sin(-1.57079632679)*(X-(1.62))+cos(-1.57079632679)*(Y-(1.327)))-(0.01575))-(0))-(-0.0315)) <= (0.021)))))!=0)) | (255*((False | False | ((((((0.0315)-(0.0315))*(((((cos(-1.57079632679)*(X-(1.62))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.0105))-(0))-(0))-((0.021)-(0))*(((((-sin(-1.57079632679)*(X-(1.62))+cos(-1.57079632679)*(Y-(1.113)))-(0.01575))-(0))-(-0.0315))-(0.0315))) >= 0) & ((((0)-(0.0315))*(((((cos(-1.57079632679)*(X-(1.62))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.0105))-(0))-(0.021))-((0.0105)-(0.021))*(((((-sin(-1.57079632679)*(X-(1.62))+cos(-1.57079632679)*(Y-(1.113)))-(0.01575))-(0))-(-0.0315))-(0.0315))) >= 0) & ((((0.0315)-(0))*(((((cos(-1.57079632679)*(X-(1.62))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.0105))-(0))-(0.0105))-((0)-(0.0105))*(((((-sin(-1.57079632679)*(X-(1.62))+cos(-1.57079632679)*(Y-(1.113)))-(0.01575))-(0))-(-0.0315))-(0))) >= 0))) & ~((((((0.04725)-(0.04725))*(((((cos(-1.57079632679)*(X-(1.62))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.0105))-(0))-(0))-((0.021)-(0))*(((((-sin(-1.57079632679)*(X-(1.62))+cos(-1.57079632679)*(Y-(1.113)))-(0.01575))-(0))-(-0.0315))-(0.04725))) >= 0) & ((((0.01575)-(0.04725))*(((((cos(-1.57079632679)*(X-(1.62))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.0105))-(0))-(0.021))-((0.0105)-(0.021))*(((((-sin(-1.57079632679)*(X-(1.62))+cos(-1.57079632679)*(Y-(1.113)))-(0.01575))-(0))-(-0.0315))-(0.04725))) >= 0) & ((((0.04725)-(0.01575))*(((((cos(-1.57079632679)*(X-(1.62))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.0105))-(0))-(0.0105))-((0)-(0.0105))*(((((-sin(-1.57079632679)*(X-(1.62))+cos(-1.57079632679)*(Y-(1.113)))-(0.01575))-(0))-(-0.0315))-(0.01575))) >= 0))))!=0)) | (255*((False | False | ((((((cos(-1.57079632679)*(X-(1.52))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.034125))-(0)) >= (0)) & (((((cos(-1.57079632679)*(X-(1.52))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.034125))-(0)) <= (0.021)) & (((((-sin(-1.57079632679)*(X-(1.52))+cos(-1.57079632679)*(Y-(1.327)))-(0.01575))-(0))-(-0.0315)) >= (0)) & (((((-sin(-1.57079632679)*(X-(1.52))+cos(-1.57079632679)*(Y-(1.327)))-(0.01575))-(0))-(-0.0315)) <= (0.0315))) | ((((((((((cos(-1.57079632679)*(X-(1.52))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.034125))-(0.023625))-(0.0105))*(((((cos(-1.57079632679)*(X-(1.52))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.034125))-(0.023625))-(0.0105)) + (((((-sin(-1.57079632679)*(X-(1.52))+cos(-1.57079632679)*(Y-(1.327)))-(0.01575))-(0))-(-0.0315))-(0.0105))*(((((cos(-1.57079632679)*(X-(1.52))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.034125))-(0.023625))-(0.0105))) <= 0.0105*0.0105) | (((((((cos(-1.57079632679)*(X-(1.52))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.034125))-(0.023625))-(0.0105))*(((((cos(-1.57079632679)*(X-(1.52))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.034125))-(0.023625))-(0.0105)) + (((((-sin(-1.57079632679)*(X-(1.52))+cos(-1.57079632679)*(Y-(1.327)))-(0.01575))-(0))-(-0.0315))-(0.021))*(((((cos(-1.57079632679)*(X-(1.52))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.034125))-(0.023625))-(0.0105))) <= 0.0105*0.0105) | ((((((cos(-1.57079632679)*(X-(1.52))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.034125))-(0.023625)) >= (0)) & (((((cos(-1.57079632679)*(X-(1.52))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.034125))-(0.023625)) <= (0.021)) & (((((-sin(-1.57079632679)*(X-(1.52))+cos(-1.57079632679)*(Y-(1.327)))-(0.01575))-(0))-(-0.0315)) >= (0.0091875)) & (((((-sin(-1.57079632679)*(X-(1.52))+cos(-1.57079632679)*(Y-(1.327)))-(0.01575))-(0))-(-0.0315)) <= (0.0223125)))) & ~((((((((cos(-1.57079632679)*(X-(1.52))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.034125))-(0.023625))-(0.0105))*(((((cos(-1.57079632679)*(X-(1.52))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.034125))-(0.023625))-(0.0105)) + (((((-sin(-1.57079632679)*(X-(1.52))+cos(-1.57079632679)*(Y-(1.327)))-(0.01575))-(0))-(-0.0315))-(0.0105))*(((((cos(-1.57079632679)*(X-(1.52))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.034125))-(0.023625))-(0.0105))) <= 0.00525*0.00525))) & ~((((((((cos(-1.57079632679)*(X-(1.52))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.034125))-(0.023625))-(0.0105))*(((((cos(-1.57079632679)*(X-(1.52))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.034125))-(0.023625))-(0.0105)) + (((((-sin(-1.57079632679)*(X-(1.52))+cos(-1.57079632679)*(Y-(1.327)))-(0.01575))-(0))-(-0.0315))-(0.021))*(((((cos(-1.57079632679)*(X-(1.52))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.034125))-(0.023625))-(0.0105))) <= 0.00525*0.00525))) & ~(((((((cos(-1.57079632679)*(X-(1.52))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.034125))-(0.023625)) >= (0.00525)) & (((((cos(-1.57079632679)*(X-(1.52))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.034125))-(0.023625)) <= (0.021)) & (((((-sin(-1.57079632679)*(X-(1.52))+cos(-1.57079632679)*(Y-(1.327)))-(0.01575))-(0))-(-0.0315)) >= (0.0105)) & (((((-sin(-1.57079632679)*(X-(1.52))+cos(-1.57079632679)*(Y-(1.327)))-(0.01575))-(0))-(-0.0315)) <= (0.021)))) | (((((((((cos(-1.57079632679)*(X-(1.52))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.034125))-(0.04725)) >= (0)) & (((((cos(-1.57079632679)*(X-(1.52))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.034125))-(0.04725)) <= (0.021)) & (((((-sin(-1.57079632679)*(X-(1.52))+cos(-1.57079632679)*(Y-(1.327)))-(0.01575))-(0))-(-0.0315)) >= (0)) & (((((-sin(-1.57079632679)*(X-(1.52))+cos(-1.57079632679)*(Y-(1.327)))-(0.01575))-(0))-(-0.0315)) <= (0.0315)))) & ~((((((0.0315)-(0.0315))*(((((cos(-1.57079632679)*(X-(1.52))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.034125))-(0.04725))-(0.00525))-((0.015225)-(0.00525))*(((((-sin(-1.57079632679)*(X-(1.52))+cos(-1.57079632679)*(Y-(1.327)))-(0.01575))-(0))-(-0.0315))-(0.0315))) >= 0) & ((((0.018375)-(0.0315))*(((((cos(-1.57079632679)*(X-(1.52))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.034125))-(0.04725))-(0.015225))-((0.00525)-(0.015225))*(((((-sin(-1.57079632679)*(X-(1.52))+cos(-1.57079632679)*(Y-(1.327)))-(0.01575))-(0))-(-0.0315))-(0.0315))) >= 0) & ((((0.0315)-(0.018375))*(((((cos(-1.57079632679)*(X-(1.52))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.034125))-(0.04725))-(0.00525))-((0.00525)-(0.00525))*(((((-sin(-1.57079632679)*(X-(1.52))+cos(-1.57079632679)*(Y-(1.327)))-(0.01575))-(0))-(-0.0315))-(0.018375))) >= 0)))) & ~((((((0.01575)-(0))*(((((cos(-1.57079632679)*(X-(1.52))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.034125))-(0.04725))-(0.021))-((0.00945)-(0.021))*(((((-sin(-1.57079632679)*(X-(1.52))+cos(-1.57079632679)*(Y-(1.327)))-(0.01575))-(0))-(-0.0315))-(0))) >= 0) & ((((0.0315)-(0.01575))*(((((cos(-1.57079632679)*(X-(1.52))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.034125))-(0.04725))-(0.00945))-((0.021)-(0.00945))*(((((-sin(-1.57079632679)*(X-(1.52))+cos(-1.57079632679)*(Y-(1.327)))-(0.01575))-(0))-(-0.0315))-(0.01575))) >= 0) & ((((0)-(0.0315))*(((((cos(-1.57079632679)*(X-(1.52))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.034125))-(0.04725))-(0.021))-((0.021)-(0.021))*(((((-sin(-1.57079632679)*(X-(1.52))+cos(-1.57079632679)*(Y-(1.327)))-(0.01575))-(0))-(-0.0315))-(0.0315))) >= 0)))) & ~((((((0.013125)-(0))*(((((cos(-1.57079632679)*(X-(1.52))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.034125))-(0.04725))-(0.00525))-((0.00525)-(0.00525))*(((((-sin(-1.57079632679)*(X-(1.52))+cos(-1.57079632679)*(Y-(1.327)))-(0.01575))-(0))-(-0.0315))-(0))) >= 0) & ((((0)-(0.013125))*(((((cos(-1.57079632679)*(X-(1.52))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.034125))-(0.04725))-(0.00525))-((0.015225)-(0.00525))*(((((-sin(-1.57079632679)*(X-(1.52))+cos(-1.57079632679)*(Y-(1.327)))-(0.01575))-(0))-(-0.0315))-(0.013125))) >= 0) & ((((0)-(0))*(((((cos(-1.57079632679)*(X-(1.52))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.034125))-(0.04725))-(0.015225))-((0.00525)-(0.015225))*(((((-sin(-1.57079632679)*(X-(1.52))+cos(-1.57079632679)*(Y-(1.327)))-(0.01575))-(0))-(-0.0315))-(0))) >= 0))))!=0)) | (255*((False | False | ((((((cos(-1.57079632679)*(X-(1.52))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.0459375))-(0)) >= (0)) & (((((cos(-1.57079632679)*(X-(1.52))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.0459375))-(0)) <= (0.021)) & (((((-sin(-1.57079632679)*(X-(1.52))+cos(-1.57079632679)*(Y-(1.113)))-(0.01575))-(0))-(-0.0315)) >= (0)) & (((((-sin(-1.57079632679)*(X-(1.52))+cos(-1.57079632679)*(Y-(1.113)))-(0.01575))-(0))-(-0.0315)) <= (0.0315))) | ((((((((((cos(-1.57079632679)*(X-(1.52))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.0459375))-(0.023625))-(0.0105))*(((((cos(-1.57079632679)*(X-(1.52))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.0459375))-(0.023625))-(0.0105)) + (((((-sin(-1.57079632679)*(X-(1.52))+cos(-1.57079632679)*(Y-(1.113)))-(0.01575))-(0))-(-0.0315))-(0.0105))*(((((cos(-1.57079632679)*(X-(1.52))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.0459375))-(0.023625))-(0.0105))) <= 0.0105*0.0105) | (((((((cos(-1.57079632679)*(X-(1.52))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.0459375))-(0.023625))-(0.0105))*(((((cos(-1.57079632679)*(X-(1.52))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.0459375))-(0.023625))-(0.0105)) + (((((-sin(-1.57079632679)*(X-(1.52))+cos(-1.57079632679)*(Y-(1.113)))-(0.01575))-(0))-(-0.0315))-(0.021))*(((((cos(-1.57079632679)*(X-(1.52))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.0459375))-(0.023625))-(0.0105))) <= 0.0105*0.0105) | ((((((cos(-1.57079632679)*(X-(1.52))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.0459375))-(0.023625)) >= (0)) & (((((cos(-1.57079632679)*(X-(1.52))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.0459375))-(0.023625)) <= (0.021)) & (((((-sin(-1.57079632679)*(X-(1.52))+cos(-1.57079632679)*(Y-(1.113)))-(0.01575))-(0))-(-0.0315)) >= (0.0105)) & (((((-sin(-1.57079632679)*(X-(1.52))+cos(-1.57079632679)*(Y-(1.113)))-(0.01575))-(0))-(-0.0315)) <= (0.021)))) & ~((((((((cos(-1.57079632679)*(X-(1.52))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.0459375))-(0.023625))-(0.0105))*(((((cos(-1.57079632679)*(X-(1.52))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.0459375))-(0.023625))-(0.0105)) + (((((-sin(-1.57079632679)*(X-(1.52))+cos(-1.57079632679)*(Y-(1.113)))-(0.01575))-(0))-(-0.0315))-(0.0105))*(((((cos(-1.57079632679)*(X-(1.52))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.0459375))-(0.023625))-(0.0105))) <= 0.00525*0.00525))) & ~((((((((cos(-1.57079632679)*(X-(1.52))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.0459375))-(0.023625))-(0.0105))*(((((cos(-1.57079632679)*(X-(1.52))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.0459375))-(0.023625))-(0.0105)) + (((((-sin(-1.57079632679)*(X-(1.52))+cos(-1.57079632679)*(Y-(1.113)))-(0.01575))-(0))-(-0.0315))-(0.021))*(((((cos(-1.57079632679)*(X-(1.52))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.0459375))-(0.023625))-(0.0105))) <= 0.00525*0.00525))) & ~(((((((cos(-1.57079632679)*(X-(1.52))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.0459375))-(0.023625)) >= (0.00525)) & (((((cos(-1.57079632679)*(X-(1.52))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.0459375))-(0.023625)) <= (0.01575)) & (((((-sin(-1.57079632679)*(X-(1.52))+cos(-1.57079632679)*(Y-(1.113)))-(0.01575))-(0))-(-0.0315)) >= (0.0105)) & (((((-sin(-1.57079632679)*(X-(1.52))+cos(-1.57079632679)*(Y-(1.113)))-(0.01575))-(0))-(-0.0315)) <= (0.021)))) | ((((((cos(-1.57079632679)*(X-(1.52))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.0459375))-(0.04725)) >= (0)) & (((((cos(-1.57079632679)*(X-(1.52))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.0459375))-(0.04725)) <= (0.021)) & (((((-sin(-1.57079632679)*(X-(1.52))+cos(-1.57079632679)*(Y-(1.113)))-(0.01575))-(0))-(-0.0315)) >= (0)) & (((((-sin(-1.57079632679)*(X-(1.52))+cos(-1.57079632679)*(Y-(1.113)))-(0.01575))-(0))-(-0.0315)) <= (0.0315))) | ((((((cos(-1.57079632679)*(X-(1.52))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.0459375))-(0.070875)) >= (0.007875)) & (((((cos(-1.57079632679)*(X-(1.52))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.0459375))-(0.070875)) <= (0.013125)) & (((((-sin(-1.57079632679)*(X-(1.52))+cos(-1.57079632679)*(Y-(1.113)))-(0.01575))-(0))-(-0.0315)) >= (0)) & (((((-sin(-1.57079632679)*(X-(1.52))+cos(-1.57079632679)*(Y-(1.113)))-(0.01575))-(0))-(-0.0315)) <= (0.0315))) | ((((((cos(-1.57079632679)*(X-(1.52))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.0459375))-(0.070875)) >= (0.0042)) & (((((cos(-1.57079632679)*(X-(1.52))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.0459375))-(0.070875)) <= (0.0168)) & (((((-sin(-1.57079632679)*(X-(1.52))+cos(-1.57079632679)*(Y-(1.113)))-(0.01575))-(0))-(-0.0315)) >= (0)) & (((((-sin(-1.57079632679)*(X-(1.52))+cos(-1.57079632679)*(Y-(1.113)))-(0.01575))-(0))-(-0.0315)) <= (0.00525))) | ((((((cos(-1.57079632679)*(X-(1.52))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.0459375))-(0.070875)) >= (0.0042)) & (((((cos(-1.57079632679)*(X-(1.52))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.0459375))-(0.070875)) <= (0.0168)) & (((((-sin(-1.57079632679)*(X-(1.52))+cos(-1.57079632679)*(Y-(1.113)))-(0.01575))-(0))-(-0.0315)) >= (0.02625)) & (((((-sin(-1.57079632679)*(X-(1.52))+cos(-1.57079632679)*(Y-(1.113)))-(0.01575))-(0))-(-0.0315)) <= (0.0315))))!=0)) | (255*((False | False | ((((((((cos(-1.57079632679)*(X-(1.42))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.034125))-(0)) >= (0)) & (((((cos(-1.57079632679)*(X-(1.42))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.034125))-(0)) <= (0.00525)) & (((((-sin(-1.57079632679)*(X-(1.42))+cos(-1.57079632679)*(Y-(1.327)))-(0.01575))-(0))-(-0.0315)) >= (0)) & (((((-sin(-1.57079632679)*(X-(1.42))+cos(-1.57079632679)*(Y-(1.327)))-(0.01575))-(0))-(-0.0315)) <= (0.0315))) | (((((((cos(-1.57079632679)*(X-(1.42))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.034125))-(0))-(0.0105))*(((((cos(-1.57079632679)*(X-(1.42))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.034125))-(0))-(0.0105)) + (((((-sin(-1.57079632679)*(X-(1.42))+cos(-1.57079632679)*(Y-(1.327)))-(0.01575))-(0))-(-0.0315))-(0.021))*(((((cos(-1.57079632679)*(X-(1.42))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.034125))-(0))-(0.0105))) <= 0.0105*0.0105) | ((((((cos(-1.57079632679)*(X-(1.42))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.034125))-(0)) >= (0)) & (((((cos(-1.57079632679)*(X-(1.42))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.034125))-(0)) <= (0.0105)) & (((((-sin(-1.57079632679)*(X-(1.42))+cos(-1.57079632679)*(Y-(1.327)))-(0.01575))-(0))-(-0.0315)) >= (0.0105)) & (((((-sin(-1.57079632679)*(X-(1.42))+cos(-1.57079632679)*(Y-(1.327)))-(0.01575))-(0))-(-0.0315)) <= (0.0315)))) & ~((((((((cos(-1.57079632679)*(X-(1.42))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.034125))-(0))-(0.0105))*(((((cos(-1.57079632679)*(X-(1.42))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.034125))-(0))-(0.0105)) + (((((-sin(-1.57079632679)*(X-(1.42))+cos(-1.57079632679)*(Y-(1.327)))-(0.01575))-(0))-(-0.0315))-(0.021))*(((((cos(-1.57079632679)*(X-(1.42))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.034125))-(0))-(0.0105))) <= 0.00525*0.00525))) & ~(((((((cos(-1.57079632679)*(X-(1.42))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.034125))-(0)) >= (0.00525)) & (((((cos(-1.57079632679)*(X-(1.42))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.034125))-(0)) <= (0.0105)) & (((((-sin(-1.57079632679)*(X-(1.42))+cos(-1.57079632679)*(Y-(1.327)))-(0.01575))-(0))-(-0.0315)) >= (0.01575)) & (((((-sin(-1.57079632679)*(X-(1.42))+cos(-1.57079632679)*(Y-(1.327)))-(0.01575))-(0))-(-0.0315)) <= (0.02625)))) | (((((((0.0315)-(0))*(((((cos(-1.57079632679)*(X-(1.42))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.034125))-(0))-(0.00525))-((0.00525)-(0.00525))*(((((-sin(-1.57079632679)*(X-(1.42))+cos(-1.57079632679)*(Y-(1.327)))-(0.01575))-(0))-(-0.0315))-(0))) >= 0) & ((((0)-(0.0315))*(((((cos(-1.57079632679)*(X-(1.42))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.034125))-(0))-(0.00525))-((0.021)-(0.00525))*(((((-sin(-1.57079632679)*(X-(1.42))+cos(-1.57079632679)*(Y-(1.327)))-(0.01575))-(0))-(-0.0315))-(0.0315))) >= 0) & ((((0)-(0))*(((((cos(-1.57079632679)*(X-(1.42))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.034125))-(0))-(0.021))-((0.00525)-(0.021))*(((((-sin(-1.57079632679)*(X-(1.42))+cos(-1.57079632679)*(Y-(1.327)))-(0.01575))-(0))-(-0.0315))-(0))) >= 0))) & ~((((((0.021)-(-0.0105))*(((((cos(-1.57079632679)*(X-(1.42))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.034125))-(0))-(0.00525))-((0.00525)-(0.00525))*(((((-sin(-1.57079632679)*(X-(1.42))+cos(-1.57079632679)*(Y-(1.327)))-(0.01575))-(0))-(-0.0315))-(-0.0105))) >= 0) & ((((-0.0105)-(0.021))*(((((cos(-1.57079632679)*(X-(1.42))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.034125))-(0))-(0.00525))-((0.021)-(0.00525))*(((((-sin(-1.57079632679)*(X-(1.42))+cos(-1.57079632679)*(Y-(1.327)))-(0.01575))-(0))-(-0.0315))-(0.021))) >= 0) & ((((-0.0105)-(-0.0105))*(((((cos(-1.57079632679)*(X-(1.42))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.034125))-(0))-(0.021))-((0.00525)-(0.021))*(((((-sin(-1.57079632679)*(X-(1.42))+cos(-1.57079632679)*(Y-(1.327)))-(0.01575))-(0))-(-0.0315))-(-0.0105))) >= 0)))) & ~(((((((cos(-1.57079632679)*(X-(1.42))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.034125))-(0)) >= (0)) & (((((cos(-1.57079632679)*(X-(1.42))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.034125))-(0)) <= (0.021)) & (((((-sin(-1.57079632679)*(X-(1.42))+cos(-1.57079632679)*(Y-(1.327)))-(0.01575))-(0))-(-0.0315)) >= (0.0105)) & (((((-sin(-1.57079632679)*(X-(1.42))+cos(-1.57079632679)*(Y-(1.327)))-(0.01575))-(0))-(-0.0315)) <= (0.0315)))) | ((((((cos(-1.57079632679)*(X-(1.42))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.034125))-(0.023625)) >= (0)) & (((((cos(-1.57079632679)*(X-(1.42))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.034125))-(0.023625)) <= (0.021)) & (((((-sin(-1.57079632679)*(X-(1.42))+cos(-1.57079632679)*(Y-(1.327)))-(0.01575))-(0))-(-0.0315)) >= (0)) & (((((-sin(-1.57079632679)*(X-(1.42))+cos(-1.57079632679)*(Y-(1.327)))-(0.01575))-(0))-(-0.0315)) <= (0.0315))) | ((((((cos(-1.57079632679)*(X-(1.42))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.034125))-(0.04725)) >= (0.007875)) & (((((cos(-1.57079632679)*(X-(1.42))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.034125))-(0.04725)) <= (0.013125)) & (((((-sin(-1.57079632679)*(X-(1.42))+cos(-1.57079632679)*(Y-(1.327)))-(0.01575))-(0))-(-0.0315)) >= (0)) & (((((-sin(-1.57079632679)*(X-(1.42))+cos(-1.57079632679)*(Y-(1.327)))-(0.01575))-(0))-(-0.0315)) <= (0.0315))) | ((((((cos(-1.57079632679)*(X-(1.42))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.034125))-(0.04725)) >= (0)) & (((((cos(-1.57079632679)*(X-(1.42))+sin(-1.57079632679)*(Y-(1.327)))-(0))-(-0.034125))-(0.04725)) <= (0.021)) & (((((-sin(-1.57079632679)*(X-(1.42))+cos(-1.57079632679)*(Y-(1.327)))-(0.01575))-(0))-(-0.0315)) >= (0.02625)) & (((((-sin(-1.57079632679)*(X-(1.42))+cos(-1.57079632679)*(Y-(1.327)))-(0.01575))-(0))-(-0.0315)) <= (0.0315))))!=0)) | (255*((False | False | ((((((((((cos(-1.57079632679)*(X-(1.42))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.034125))-(0))-(0.0105))*(((((cos(-1.57079632679)*(X-(1.42))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.034125))-(0))-(0.0105)) + (((((-sin(-1.57079632679)*(X-(1.42))+cos(-1.57079632679)*(Y-(1.113)))-(0.01575))-(0))-(-0.0315))-(0.0105))*(((((cos(-1.57079632679)*(X-(1.42))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.034125))-(0))-(0.0105))) <= 0.0105*0.0105) | (((((((cos(-1.57079632679)*(X-(1.42))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.034125))-(0))-(0.0105))*(((((cos(-1.57079632679)*(X-(1.42))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.034125))-(0))-(0.0105)) + (((((-sin(-1.57079632679)*(X-(1.42))+cos(-1.57079632679)*(Y-(1.113)))-(0.01575))-(0))-(-0.0315))-(0.021))*(((((cos(-1.57079632679)*(X-(1.42))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.034125))-(0))-(0.0105))) <= 0.0105*0.0105) | ((((((cos(-1.57079632679)*(X-(1.42))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.034125))-(0)) >= (0)) & (((((cos(-1.57079632679)*(X-(1.42))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.034125))-(0)) <= (0.021)) & (((((-sin(-1.57079632679)*(X-(1.42))+cos(-1.57079632679)*(Y-(1.113)))-(0.01575))-(0))-(-0.0315)) >= (0.0105)) & (((((-sin(-1.57079632679)*(X-(1.42))+cos(-1.57079632679)*(Y-(1.113)))-(0.01575))-(0))-(-0.0315)) <= (0.021)))) & ~((((((((cos(-1.57079632679)*(X-(1.42))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.034125))-(0))-(0.0105))*(((((cos(-1.57079632679)*(X-(1.42))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.034125))-(0))-(0.0105)) + (((((-sin(-1.57079632679)*(X-(1.42))+cos(-1.57079632679)*(Y-(1.113)))-(0.01575))-(0))-(-0.0315))-(0.0105))*(((((cos(-1.57079632679)*(X-(1.42))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.034125))-(0))-(0.0105))) <= 0.00525*0.00525))) & ~((((((((cos(-1.57079632679)*(X-(1.42))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.034125))-(0))-(0.0105))*(((((cos(-1.57079632679)*(X-(1.42))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.034125))-(0))-(0.0105)) + (((((-sin(-1.57079632679)*(X-(1.42))+cos(-1.57079632679)*(Y-(1.113)))-(0.01575))-(0))-(-0.0315))-(0.021))*(((((cos(-1.57079632679)*(X-(1.42))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.034125))-(0))-(0.0105))) <= 0.00525*0.00525))) & ~(((((((cos(-1.57079632679)*(X-(1.42))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.034125))-(0)) >= (0.00525)) & (((((cos(-1.57079632679)*(X-(1.42))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.034125))-(0)) <= (0.021)) & (((((-sin(-1.57079632679)*(X-(1.42))+cos(-1.57079632679)*(Y-(1.113)))-(0.01575))-(0))-(-0.0315)) >= (0.0105)) & (((((-sin(-1.57079632679)*(X-(1.42))+cos(-1.57079632679)*(Y-(1.113)))-(0.01575))-(0))-(-0.0315)) <= (0.021)))) | ((((((cos(-1.57079632679)*(X-(1.42))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.034125))-(0)) >= (0.0105)) & (((((cos(-1.57079632679)*(X-(1.42))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.034125))-(0)) <= (0.021)) & (((((-sin(-1.57079632679)*(X-(1.42))+cos(-1.57079632679)*(Y-(1.113)))-(0.01575))-(0))-(-0.0315)) >= (0.0105)) & (((((-sin(-1.57079632679)*(X-(1.42))+cos(-1.57079632679)*(Y-(1.113)))-(0.01575))-(0))-(-0.0315)) <= (0.01575))) | ((((((((cos(-1.57079632679)*(X-(1.42))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.034125))-(0.023625)) >= (0)) & (((((cos(-1.57079632679)*(X-(1.42))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.034125))-(0.023625)) <= (0.021)) & (((((-sin(-1.57079632679)*(X-(1.42))+cos(-1.57079632679)*(Y-(1.113)))-(0.01575))-(0))-(-0.0315)) >= (0)) & (((((-sin(-1.57079632679)*(X-(1.42))+cos(-1.57079632679)*(Y-(1.113)))-(0.01575))-(0))-(-0.0315)) <= (0.0315)))) & ~((((((0.039375)-(0.039375))*(((((cos(-1.57079632679)*(X-(1.42))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.034125))-(0.023625))-(0.00525))-((0.01575)-(0.00525))*(((((-sin(-1.57079632679)*(X-(1.42))+cos(-1.57079632679)*(Y-(1.113)))-(0.01575))-(0))-(-0.0315))-(0.039375))) >= 0) & ((((0.007875)-(0.039375))*(((((cos(-1.57079632679)*(X-(1.42))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.034125))-(0.023625))-(0.01575))-((0.01575)-(0.01575))*(((((-sin(-1.57079632679)*(X-(1.42))+cos(-1.57079632679)*(Y-(1.113)))-(0.01575))-(0))-(-0.0315))-(0.039375))) >= 0) & ((((0.039375)-(0.007875))*(((((cos(-1.57079632679)*(X-(1.42))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.034125))-(0.023625))-(0.01575))-((0.00525)-(0.01575))*(((((-sin(-1.57079632679)*(X-(1.42))+cos(-1.57079632679)*(Y-(1.113)))-(0.01575))-(0))-(-0.0315))-(0.007875))) >= 0)))) & ~((((((0.023625)-(-0.007875))*(((((cos(-1.57079632679)*(X-(1.42))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.034125))-(0.023625))-(0.00525))-((0.00525)-(0.00525))*(((((-sin(-1.57079632679)*(X-(1.42))+cos(-1.57079632679)*(Y-(1.113)))-(0.01575))-(0))-(-0.0315))-(-0.007875))) >= 0) & ((((-0.007875)-(0.023625))*(((((cos(-1.57079632679)*(X-(1.42))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.034125))-(0.023625))-(0.00525))-((0.01575)-(0.00525))*(((((-sin(-1.57079632679)*(X-(1.42))+cos(-1.57079632679)*(Y-(1.113)))-(0.01575))-(0))-(-0.0315))-(0.023625))) >= 0) & ((((-0.007875)-(-0.007875))*(((((cos(-1.57079632679)*(X-(1.42))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.034125))-(0.023625))-(0.01575))-((0.00525)-(0.01575))*(((((-sin(-1.57079632679)*(X-(1.42))+cos(-1.57079632679)*(Y-(1.113)))-(0.01575))-(0))-(-0.0315))-(-0.007875))) >= 0))) | (((((((((cos(-1.57079632679)*(X-(1.42))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.034125))-(0.04725))-(0.00525))*(((((cos(-1.57079632679)*(X-(1.42))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.034125))-(0.04725))-(0.00525)) + (((0) + (((((-sin(-1.57079632679)*(X-(1.42))+cos(-1.57079632679)*(Y-(1.113)))-(0.01575))-(0))-(-0.0315))-(0))/(1.0))-(0.01575))*(((((cos(-1.57079632679)*(X-(1.42))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.034125))-(0.04725))-(0.00525))) <= 0.01575*0.01575)) & ~((((((((cos(-1.57079632679)*(X-(1.42))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.034125))-(0.04725))-(0.00525))*(((((cos(-1.57079632679)*(X-(1.42))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.034125))-(0.04725))-(0.00525)) + (((0) + (((((-sin(-1.57079632679)*(X-(1.42))+cos(-1.57079632679)*(Y-(1.113)))-(0.01575))-(0))-(-0.0315))-(0))/(1.0))-(0.01575))*(((((cos(-1.57079632679)*(X-(1.42))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.034125))-(0.04725))-(0.00525))) <= 0.0105*0.0105))) & ~(((((((cos(-1.57079632679)*(X-(1.42))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.034125))-(0.04725)) >= (-0.021)) & (((((cos(-1.57079632679)*(X-(1.42))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.034125))-(0.04725)) <= (0.00525)) & (((0) + (((((-sin(-1.57079632679)*(X-(1.42))+cos(-1.57079632679)*(Y-(1.113)))-(0.01575))-(0))-(-0.0315))-(0))/(1.0)) >= (0)) & (((0) + (((((-sin(-1.57079632679)*(X-(1.42))+cos(-1.57079632679)*(Y-(1.113)))-(0.01575))-(0))-(-0.0315))-(0))/(1.0)) <= (0.0315)))) | ((((((cos(-1.57079632679)*(X-(1.42))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.034125))-(0.04725)) >= (0)) & (((((cos(-1.57079632679)*(X-(1.42))+sin(-1.57079632679)*(Y-(1.113)))-(0))-(-0.034125))-(0.04725)) <= (0.00525)) & (((((-sin(-1.57079632679)*(X-(1.42))+cos(-1.57079632679)*(Y-(1.113)))-(0.01575))-(0))-(-0.0315)) >= (0)) & (((((-sin(-1.57079632679)*(X-(1.42))+cos(-1.57079632679)*(Y-(1.113)))-(0.01575))-(0))-(-0.0315)) <= (0.0315))))!=0)) | (65280*((False | False | (((((((((X-(1.78))-(0))-(-0.1085))-(0))-(0.014))*(((((X-(1.78))-(0))-(-0.1085))-(0))-(0.014)) + (((((Y-(1.59))-(0.021))-(0))-(-0.042))-(0.014))*(((((X-(1.78))-(0))-(-0.1085))-(0))-(0.014))) <= 0.014*0.014)) & ~((((((((X-(1.78))-(0))-(-0.1085))-(0))-(0.014))*(((((X-(1.78))-(0))-(-0.1085))-(0))-(0.014)) + (((((Y-(1.59))-(0.021))-(0))-(-0.042))-(0.014))*(((((X-(1.78))-(0))-(-0.1085))-(0))-(0.014))) <= 0.007*0.007))) & ~(((((((X-(1.78))-(0))-(-0.1085))-(0)) >= (0)) & (((((X-(1.78))-(0))-(-0.1085))-(0)) <= (0.028)) & (((((Y-(1.59))-(0.021))-(0))-(-0.042)) >= (0.014)) & (((((Y-(1.59))-(0.021))-(0))-(-0.042)) <= (0.042)))) | ((((((X-(1.78))-(0))-(-0.1085))-(0)) >= (0.021)) & (((((X-(1.78))-(0))-(-0.1085))-(0)) <= (0.028)) & (((((Y-(1.59))-(0.021))-(0))-(-0.042)) >= (0.014)) & (((((Y-(1.59))-(0.021))-(0))-(-0.042)) <= (0.042))) | (((((((((((X-(1.78))-(0))-(-0.1085))-(0.0315))-(0.014))*(((((X-(1.78))-(0))-(-0.1085))-(0.0315))-(0.014)) + (((((Y-(1.59))-(0.021))-(0))-(-0.042))-(0.028))*(((((X-(1.78))-(0))-(-0.1085))-(0.0315))-(0.014))) <= 0.014*0.014)) & ~((((((((X-(1.78))-(0))-(-0.1085))-(0.0315))-(0.014))*(((((X-(1.78))-(0))-(-0.1085))-(0.0315))-(0.014)) + (((((Y-(1.59))-(0.021))-(0))-(-0.042))-(0.028))*(((((X-(1.78))-(0))-(-0.1085))-(0.0315))-(0.014))) <= 0.007*0.007))) & ~(((((((X-(1.78))-(0))-(-0.1085))-(0.0315)) >= (0)) & (((((X-(1.78))-(0))-(-0.1085))-(0.0315)) <= (0.014)) & (((((Y-(1.59))-(0.021))-(0))-(-0.042)) >= (0)) & (((((Y-(1.59))-(0.021))-(0))-(-0.042)) <= (0.028)))) | ((((((X-(1.78))-(0))-(-0.1085))-(0.0315)) >= (0)) & (((((X-(1.78))-(0))-(-0.1085))-(0.0315)) <= (0.028)) & (((((Y-(1.59))-(0.021))-(0))-(-0.042)) >= (0)) & (((((Y-(1.59))-(0.021))-(0))-(-0.042)) <= (0.028)))) & ~((((((0.028)-(0.007))*(((((X-(1.78))-(0))-(-0.1085))-(0.0315))-(0))-((0)-(0))*(((((Y-(1.59))-(0.021))-(0))-(-0.042))-(0.007))) >= 0) & ((((0.028)-(0.028))*(((((X-(1.78))-(0))-(-0.1085))-(0.0315))-(0))-((0.021)-(0))*(((((Y-(1.59))-(0.021))-(0))-(-0.042))-(0.028))) >= 0) & ((((0.007)-(0.028))*(((((X-(1.78))-(0))-(-0.1085))-(0.0315))-(0.021))-((0)-(0.021))*(((((Y-(1.59))-(0.021))-(0))-(-0.042))-(0.028))) >= 0)))) & ~((((((0.0245)-(0.007))*(((((X-(1.78))-(0))-(-0.1085))-(0.0315))-(0.0105))-((0.028)-(0.0105))*(((((Y-(1.59))-(0.021))-(0))-(-0.042))-(0.007))) >= 0) & ((((0.007)-(0.0245))*(((((X-(1.78))-(0))-(-0.1085))-(0.0315))-(0.028))-((0.028)-(0.028))*(((((Y-(1.59))-(0.021))-(0))-(-0.042))-(0.0245))) >= 0) & ((((0.007)-(0.007))*(((((X-(1.78))-(0))-(-0.1085))-(0.0315))-(0.028))-((0.0105)-(0.028))*(((((Y-(1.59))-(0.021))-(0))-(-0.042))-(0.007))) >= 0))) | False | ((((((X-(1.78))-(0))-(-0.1085))-(0.0945)) >= (0)) & (((((X-(1.78))-(0))-(-0.1085))-(0.0945)) <= (0.007)) & (((((Y-(1.59))-(0.021))-(0))-(-0.042)) >= (0)) & (((((Y-(1.59))-(0.021))-(0))-(-0.042)) <= (0.042))) | ((((((X-(1.78))-(0))-(-0.1085))-(0.0945)) >= (0)) & (((((X-(1.78))-(0))-(-0.1085))-(0.0945)) <= (0.028)) & (((((Y-(1.59))-(0.021))-(0))-(-0.042)) >= (0.035)) & (((((Y-(1.59))-(0.021))-(0))-(-0.042)) <= (0.042))) | ((((((X-(1.78))-(0))-(-0.1085))-(0.0945)) >= (0)) & (((((X-(1.78))-(0))-(-0.1085))-(0.0945)) <= (0.0186666666667)) & (((((Y-(1.59))-(0.021))-(0))-(-0.042)) >= (0.0175)) & (((((Y-(1.59))-(0.021))-(0))-(-0.042)) <= (0.0245))) | ((((((X-(1.78))-(0))-(-0.1085))-(0.126)) >= (0.0105)) & (((((X-(1.78))-(0))-(-0.1085))-(0.126)) <= (0.0175)) & (((((Y-(1.59))-(0.021))-(0))-(-0.042)) >= (0)) & (((((Y-(1.59))-(0.021))-(0))-(-0.042)) <= (0.042))) | ((((((X-(1.78))-(0))-(-0.1085))-(0.126)) >= (0)) & (((((X-(1.78))-(0))-(-0.1085))-(0.126)) <= (0.028)) & (((((Y-(1.59))-(0.021))-(0))-(-0.042)) >= (0.035)) & (((((Y-(1.59))-(0.021))-(0))-(-0.042)) <= (0.042))) | (((((((((X-(1.78))-(0))-(-0.1085))-(0.1575))-(0.007))*(((((X-(1.78))-(0))-(-0.1085))-(0.1575))-(0.007)) + (((0) + (((((Y-(1.59))-(0.021))-(0))-(-0.042))-(0))/(1.0))-(0.021))*(((((X-(1.78))-(0))-(-0.1085))-(0.1575))-(0.007))) <= 0.021*0.021)) & ~((((((((X-(1.78))-(0))-(-0.1085))-(0.1575))-(0.007))*(((((X-(1.78))-(0))-(-0.1085))-(0.1575))-(0.007)) + (((0) + (((((Y-(1.59))-(0.021))-(0))-(-0.042))-(0))/(1.0))-(0.021))*(((((X-(1.78))-(0))-(-0.1085))-(0.1575))-(0.007))) <= 0.014*0.014))) & ~(((((((X-(1.78))-(0))-(-0.1085))-(0.1575)) >= (-0.028)) & (((((X-(1.78))-(0))-(-0.1085))-(0.1575)) <= (0.007)) & (((0) + (((((Y-(1.59))-(0.021))-(0))-(-0.042))-(0))/(1.0)) >= (0)) & (((0) + (((((Y-(1.59))-(0.021))-(0))-(-0.042))-(0))/(1.0)) <= (0.042)))) | ((((((X-(1.78))-(0))-(-0.1085))-(0.1575)) >= (0)) & (((((X-(1.78))-(0))-(-0.1085))-(0.1575)) <= (0.007)) & (((((Y-(1.59))-(0.021))-(0))-(-0.042)) >= (0)) & (((((Y-(1.59))-(0.021))-(0))-(-0.042)) <= (0.042))) | ((((((X-(1.78))-(0))-(-0.1085))-(0.189)) >= (0.0105)) & (((((X-(1.78))-(0))-(-0.1085))-(0.189)) <= (0.0175)) & (((((Y-(1.59))-(0.021))-(0))-(-0.042)) >= (0)) & (((((Y-(1.59))-(0.021))-(0))-(-0.042)) <= (0.042))) | ((((((X-(1.78))-(0))-(-0.1085))-(0.189)) >= (0.0056)) & (((((X-(1.78))-(0))-(-0.1085))-(0.189)) <= (0.0224)) & (((((Y-(1.59))-(0.021))-(0))-(-0.042)) >= (0)) & (((((Y-(1.59))-(0.021))-(0))-(-0.042)) <= (0.007))) | ((((((X-(1.78))-(0))-(-0.1085))-(0.189)) >= (0.0056)) & (((((X-(1.78))-(0))-(-0.1085))-(0.189)) <= (0.0224)) & (((((Y-(1.59))-(0.021))-(0))-(-0.042)) >= (0.035)) & (((((Y-(1.59))-(0.021))-(0))-(-0.042)) <= (0.042))))!=0)) | (255*((False | False | ((((((((((X-(1.78))-(0))-(-0.081375))-(0))-(0.0105))*(((((X-(1.78))-(0))-(-0.081375))-(0))-(0.0105)) + (((((Y-(1.84))-(0.01575))-(0))-(-0.0315))-(0.0105))*(((((X-(1.78))-(0))-(-0.081375))-(0))-(0.0105))) <= 0.0105*0.0105) | (((((((X-(1.78))-(0))-(-0.081375))-(0))-(0.0105))*(((((X-(1.78))-(0))-(-0.081375))-(0))-(0.0105)) + (((((Y-(1.84))-(0.01575))-(0))-(-0.0315))-(0.021))*(((((X-(1.78))-(0))-(-0.081375))-(0))-(0.0105))) <= 0.0105*0.0105) | ((((((X-(1.78))-(0))-(-0.081375))-(0)) >= (0)) & (((((X-(1.78))-(0))-(-0.081375))-(0)) <= (0.021)) & (((((Y-(1.84))-(0.01575))-(0))-(-0.0315)) >= (0.0105)) & (((((Y-(1.84))-(0.01575))-(0))-(-0.0315)) <= (0.021)))) & ~((((((((X-(1.78))-(0))-(-0.081375))-(0))-(0.0105))*(((((X-(1.78))-(0))-(-0.081375))-(0))-(0.0105)) + (((((Y-(1.84))-(0.01575))-(0))-(-0.0315))-(0.0105))*(((((X-(1.78))-(0))-(-0.081375))-(0))-(0.0105))) <= 0.00525*0.00525))) & ~((((((((X-(1.78))-(0))-(-0.081375))-(0))-(0.0105))*(((((X-(1.78))-(0))-(-0.081375))-(0))-(0.0105)) + (((((Y-(1.84))-(0.01575))-(0))-(-0.0315))-(0.021))*(((((X-(1.78))-(0))-(-0.081375))-(0))-(0.0105))) <= 0.00525*0.00525))) & ~(((((((X-(1.78))-(0))-(-0.081375))-(0)) >= (0.00525)) & (((((X-(1.78))-(0))-(-0.081375))-(0)) <= (0.021)) & (((((Y-(1.84))-(0.01575))-(0))-(-0.0315)) >= (0.0105)) & (((((Y-(1.84))-(0.01575))-(0))-(-0.0315)) <= (0.021)))) | ((((((X-(1.78))-(0))-(-0.081375))-(0)) >= (0.0105)) & (((((X-(1.78))-(0))-(-0.081375))-(0)) <= (0.021)) & (((((Y-(1.84))-(0.01575))-(0))-(-0.0315)) >= (0.0105)) & (((((Y-(1.84))-(0.01575))-(0))-(-0.0315)) <= (0.01575))) | False | ((((((X-(1.78))-(0))-(-0.081375))-(0.04725)) >= (0)) & (((((X-(1.78))-(0))-(-0.081375))-(0.04725)) <= (0.021)) & (((((Y-(1.84))-(0.01575))-(0))-(-0.0315)) >= (0)) & (((((Y-(1.84))-(0.01575))-(0))-(-0.0315)) <= (0.0315))) | ((((((((X-(1.78))-(0))-(-0.081375))-(0.070875))-(0.0105))*(((((X-(1.78))-(0))-(-0.081375))-(0.070875))-(0.0105)) + (((((Y-(1.84))-(0.01575))-(0))-(-0.0315))-(0.0105))*(((((X-(1.78))-(0))-(-0.081375))-(0.070875))-(0.0105))) <= 0.0105*0.0105)) & ~((((((((X-(1.78))-(0))-(-0.081375))-(0.070875))-(0.0105))*(((((X-(1.78))-(0))-(-0.081375))-(0.070875))-(0.0105)) + (((((Y-(1.84))-(0.01575))-(0))-(-0.0315))-(0.0105))*(((((X-(1.78))-(0))-(-0.081375))-(0.070875))-(0.0105))) <= 0.00525*0.00525)) | ((((((X-(1.78))-(0))-(-0.081375))-(0.070875)) >= (0)) & (((((X-(1.78))-(0))-(-0.081375))-(0.070875)) <= (0.00525)) & (((((Y-(1.84))-(0.01575))-(0))-(-0.0315)) >= (0)) & (((((Y-(1.84))-(0.01575))-(0))-(-0.0315)) <= (0.0315))) | ((((((X-(1.78))-(0))-(-0.081375))-(0.0945)) >= (0)) & (((((X-(1.78))-(0))-(-0.081375))-(0.0945)) <= (0.021)) & (((((Y-(1.84))-(0.01575))-(0))-(-0.0315)) >= (0)) & (((((Y-(1.84))-(0.01575))-(0))-(-0.0315)) <= (0.0315))) | ((((((((((X-(1.78))-(0))-(-0.081375))-(0.118125)) >= (0)) & (((((X-(1.78))-(0))-(-0.081375))-(0.118125)) <= (0.021)) & (((((Y-(1.84))-(0.01575))-(0))-(-0.0315)) >= (0)) & (((((Y-(1.84))-(0.01575))-(0))-(-0.0315)) <= (0.0315)))) & ~(((((((X-(1.78))-(0))-(-0.081375))-(0.118125)) >= (0.00525)) & (((((X-(1.78))-(0))-(-0.081375))-(0.118125)) <= (0.021)) & (((((Y-(1.84))-(0.01575))-(0))-(-0.0315)) >= (0.021)) & (((((Y-(1.84))-(0.01575))-(0))-(-0.0315)) <= (0.0315))))) & ~((((((0.021)-(0.021))*(((((X-(1.78))-(0))-(-0.081375))-(0.118125))-(0.00525))-((0.014175)-(0.00525))*(((((Y-(1.84))-(0.01575))-(0))-(-0.0315))-(0.021))) >= 0) & ((((0.013125)-(0.021))*(((((X-(1.78))-(0))-(-0.081375))-(0.118125))-(0.014175))-((0.00525)-(0.014175))*(((((Y-(1.84))-(0.01575))-(0))-(-0.0315))-(0.021))) >= 0) & ((((0.021)-(0.013125))*(((((X-(1.78))-(0))-(-0.081375))-(0.118125))-(0.00525))-((0.00525)-(0.00525))*(((((Y-(1.84))-(0.01575))-(0))-(-0.0315))-(0.013125))) >= 0)))) & ~((((((0.0105)-(0))*(((((X-(1.78))-(0))-(-0.081375))-(0.118125))-(0.021))-((0.00945)-(0.021))*(((((Y-(1.84))-(0.01575))-(0))-(-0.0315))-(0))) >= 0) & ((((0.021)-(0.0105))*(((((X-(1.78))-(0))-(-0.081375))-(0.118125))-(0.00945))-((0.021)-(0.00945))*(((((Y-(1.84))-(0.01575))-(0))-(-0.0315))-(0.0105))) >= 0) & ((((0)-(0.021))*(((((X-(1.78))-(0))-(-0.081375))-(0.118125))-(0.021))-((0.021)-(0.021))*(((((Y-(1.84))-(0.01575))-(0))-(-0.0315))-(0.021))) >= 0)))) & ~((((((0.007875)-(0))*(((((X-(1.78))-(0))-(-0.081375))-(0.118125))-(0.00525))-((0.00525)-(0.00525))*(((((Y-(1.84))-(0.01575))-(0))-(-0.0315))-(0))) >= 0) & ((((0)-(0.007875))*(((((X-(1.78))-(0))-(-0.081375))-(0.118125))-(0.00525))-((0.014175)-(0.00525))*(((((Y-(1.84))-(0.01575))-(0))-(-0.0315))-(0.007875))) >= 0) & ((((0)-(0))*(((((X-(1.78))-(0))-(-0.081375))-(0.118125))-(0.014175))-((0.00525)-(0.014175))*(((((Y-(1.84))-(0.01575))-(0))-(-0.0315))-(0))) >= 0))) | ((((((X-(1.78))-(0))-(-0.081375))-(0.14175)) >= (0)) & (((((X-(1.78))-(0))-(-0.081375))-(0.14175)) <= (0.021)) & (((((Y-(1.84))-(0.01575))-(0))-(-0.0315)) >= (0)) & (((((Y-(1.84))-(0.01575))-(0))-(-0.0315)) <= (0.0315))))!=0)) | (255*((False | False | ((((((((((X-(1.78))-(0))-(-0.034125))-(0))-(0.0105))*(((((X-(1.78))-(0))-(-0.034125))-(0))-(0.0105)) + (((((Y-(1.74))-(0.01575))-(0))-(-0.0315))-(0.0105))*(((((X-(1.78))-(0))-(-0.034125))-(0))-(0.0105))) <= 0.0105*0.0105) | (((((((X-(1.78))-(0))-(-0.034125))-(0))-(0.0105))*(((((X-(1.78))-(0))-(-0.034125))-(0))-(0.0105)) + (((((Y-(1.74))-(0.01575))-(0))-(-0.0315))-(0.021))*(((((X-(1.78))-(0))-(-0.034125))-(0))-(0.0105))) <= 0.0105*0.0105) | ((((((X-(1.78))-(0))-(-0.034125))-(0)) >= (0)) & (((((X-(1.78))-(0))-(-0.034125))-(0)) <= (0.021)) & (((((Y-(1.74))-(0.01575))-(0))-(-0.0315)) >= (0.0091875)) & (((((Y-(1.74))-(0.01575))-(0))-(-0.0315)) <= (0.0223125)))) & ~((((((((X-(1.78))-(0))-(-0.034125))-(0))-(0.0105))*(((((X-(1.78))-(0))-(-0.034125))-(0))-(0.0105)) + (((((Y-(1.74))-(0.01575))-(0))-(-0.0315))-(0.0105))*(((((X-(1.78))-(0))-(-0.034125))-(0))-(0.0105))) <= 0.00525*0.00525))) & ~((((((((X-(1.78))-(0))-(-0.034125))-(0))-(0.0105))*(((((X-(1.78))-(0))-(-0.034125))-(0))-(0.0105)) + (((((Y-(1.74))-(0.01575))-(0))-(-0.0315))-(0.021))*(((((X-(1.78))-(0))-(-0.034125))-(0))-(0.0105))) <= 0.00525*0.00525))) & ~(((((((X-(1.78))-(0))-(-0.034125))-(0)) >= (0.00525)) & (((((X-(1.78))-(0))-(-0.034125))-(0)) <= (0.021)) & (((((Y-(1.74))-(0.01575))-(0))-(-0.0315)) >= (0.0105)) & (((((Y-(1.74))-(0.01575))-(0))-(-0.0315)) <= (0.021)))) | ((((((X-(1.78))-(0))-(-0.034125))-(0.023625)) >= (0.007875)) & (((((X-(1.78))-(0))-(-0.034125))-(0.023625)) <= (0.013125)) & (((((Y-(1.74))-(0.01575))-(0))-(-0.0315)) >= (0)) & (((((Y-(1.74))-(0.01575))-(0))-(-0.0315)) <= (0.0315))) | ((((((X-(1.78))-(0))-(-0.034125))-(0.023625)) >= (0)) & (((((X-(1.78))-(0))-(-0.034125))-(0.023625)) <= (0.021)) & (((((Y-(1.74))-(0.01575))-(0))-(-0.0315)) >= (0.02625)) & (((((Y-(1.74))-(0.01575))-(0))-(-0.0315)) <= (0.0315))) | ((((((X-(1.78))-(0))-(-0.034125))-(0.04725)) >= (0)) & (((((X-(1.78))-(0))-(-0.034125))-(0.04725)) <= (0.021)) & (((((Y-(1.74))-(0.01575))-(0))-(-0.0315)) >= (0)) & (((((Y-(1.74))-(0.01575))-(0))-(-0.0315)) <= (0.0315))))!=0)) | (255*((False | False | ((((((0.0315)-(0.0315))*(((((X-(1.78))-(0))-(-0.034125))-(0))-(0))-((0.021)-(0))*(((((Y-(1.64))-(0.01575))-(0))-(-0.0315))-(0.0315))) >= 0) & ((((0)-(0.0315))*(((((X-(1.78))-(0))-(-0.034125))-(0))-(0.021))-((0.0105)-(0.021))*(((((Y-(1.64))-(0.01575))-(0))-(-0.0315))-(0.0315))) >= 0) & ((((0.0315)-(0))*(((((X-(1.78))-(0))-(-0.034125))-(0))-(0.0105))-((0)-(0.0105))*(((((Y-(1.64))-(0.01575))-(0))-(-0.0315))-(0))) >= 0))) & ~((((((0.04725)-(0.04725))*(((((X-(1.78))-(0))-(-0.034125))-(0))-(0))-((0.021)-(0))*(((((Y-(1.64))-(0.01575))-(0))-(-0.0315))-(0.04725))) >= 0) & ((((0.01575)-(0.04725))*(((((X-(1.78))-(0))-(-0.034125))-(0))-(0.021))-((0.0105)-(0.021))*(((((Y-(1.64))-(0.01575))-(0))-(-0.0315))-(0.04725))) >= 0) & ((((0.04725)-(0.01575))*(((((X-(1.78))-(0))-(-0.034125))-(0))-(0.0105))-((0)-(0.0105))*(((((Y-(1.64))-(0.01575))-(0))-(-0.0315))-(0.01575))) >= 0))) | ((((((((((X-(1.78))-(0))-(-0.034125))-(0.023625))-(0.0105))*(((((X-(1.78))-(0))-(-0.034125))-(0.023625))-(0.0105)) + (((((Y-(1.64))-(0.01575))-(0))-(-0.0315))-(0.0105))*(((((X-(1.78))-(0))-(-0.034125))-(0.023625))-(0.0105))) <= 0.0105*0.0105) | (((((((X-(1.78))-(0))-(-0.034125))-(0.023625))-(0.0105))*(((((X-(1.78))-(0))-(-0.034125))-(0.023625))-(0.0105)) + (((((Y-(1.64))-(0.01575))-(0))-(-0.0315))-(0.021))*(((((X-(1.78))-(0))-(-0.034125))-(0.023625))-(0.0105))) <= 0.0105*0.0105) | ((((((X-(1.78))-(0))-(-0.034125))-(0.023625)) >= (0)) & (((((X-(1.78))-(0))-(-0.034125))-(0.023625)) <= (0.021)) & (((((Y-(1.64))-(0.01575))-(0))-(-0.0315)) >= (0.0091875)) & (((((Y-(1.64))-(0.01575))-(0))-(-0.0315)) <= (0.0223125)))) & ~((((((((X-(1.78))-(0))-(-0.034125))-(0.023625))-(0.0105))*(((((X-(1.78))-(0))-(-0.034125))-(0.023625))-(0.0105)) + (((((Y-(1.64))-(0.01575))-(0))-(-0.0315))-(0.0105))*(((((X-(1.78))-(0))-(-0.034125))-(0.023625))-(0.0105))) <= 0.00525*0.00525))) & ~((((((((X-(1.78))-(0))-(-0.034125))-(0.023625))-(0.0105))*(((((X-(1.78))-(0))-(-0.034125))-(0.023625))-(0.0105)) + (((((Y-(1.64))-(0.01575))-(0))-(-0.0315))-(0.021))*(((((X-(1.78))-(0))-(-0.034125))-(0.023625))-(0.0105))) <= 0.00525*0.00525))) & ~(((((((X-(1.78))-(0))-(-0.034125))-(0.023625)) >= (0.00525)) & (((((X-(1.78))-(0))-(-0.034125))-(0.023625)) <= (0.021)) & (((((Y-(1.64))-(0.01575))-(0))-(-0.0315)) >= (0.0105)) & (((((Y-(1.64))-(0.01575))-(0))-(-0.0315)) <= (0.021)))) | ((((((((((X-(1.78))-(0))-(-0.034125))-(0.04725))-(0.0105))*(((((X-(1.78))-(0))-(-0.034125))-(0.04725))-(0.0105)) + (((((Y-(1.64))-(0.01575))-(0))-(-0.0315))-(0.0105))*(((((X-(1.78))-(0))-(-0.034125))-(0.04725))-(0.0105))) <= 0.0105*0.0105) | (((((((X-(1.78))-(0))-(-0.034125))-(0.04725))-(0.0105))*(((((X-(1.78))-(0))-(-0.034125))-(0.04725))-(0.0105)) + (((((Y-(1.64))-(0.01575))-(0))-(-0.0315))-(0.021))*(((((X-(1.78))-(0))-(-0.034125))-(0.04725))-(0.0105))) <= 0.0105*0.0105) | ((((((X-(1.78))-(0))-(-0.034125))-(0.04725)) >= (0)) & (((((X-(1.78))-(0))-(-0.034125))-(0.04725)) <= (0.021)) & (((((Y-(1.64))-(0.01575))-(0))-(-0.0315)) >= (0.0091875)) & (((((Y-(1.64))-(0.01575))-(0))-(-0.0315)) <= (0.0223125)))) & ~((((((((X-(1.78))-(0))-(-0.034125))-(0.04725))-(0.0105))*(((((X-(1.78))-(0))-(-0.034125))-(0.04725))-(0.0105)) + (((((Y-(1.64))-(0.01575))-(0))-(-0.0315))-(0.0105))*(((((X-(1.78))-(0))-(-0.034125))-(0.04725))-(0.0105))) <= 0.00525*0.00525))) & ~((((((((X-(1.78))-(0))-(-0.034125))-(0.04725))-(0.0105))*(((((X-(1.78))-(0))-(-0.034125))-(0.04725))-(0.0105)) + (((((Y-(1.64))-(0.01575))-(0))-(-0.0315))-(0.021))*(((((X-(1.78))-(0))-(-0.034125))-(0.04725))-(0.0105))) <= 0.00525*0.00525))) & ~(((((((X-(1.78))-(0))-(-0.034125))-(0.04725)) >= (0.00525)) & (((((X-(1.78))-(0))-(-0.034125))-(0.04725)) <= (0.021)) & (((((Y-(1.64))-(0.01575))-(0))-(-0.0315)) >= (0.0105)) & (((((Y-(1.64))-(0.01575))-(0))-(-0.0315)) <= (0.021)))))!=0)) | (255*((False | False | ((((((X-(1.78))-(0))-(-0.0223125))-(0)) >= (0.007875)) & (((((X-(1.78))-(0))-(-0.0223125))-(0)) <= (0.013125)) & (((((Y-(1.54))-(0.01575))-(0))-(-0.0315)) >= (0)) & (((((Y-(1.54))-(0.01575))-(0))-(-0.0315)) <= (0.0315))) | ((((((X-(1.78))-(0))-(-0.0223125))-(0)) >= (0)) & (((((X-(1.78))-(0))-(-0.0223125))-(0)) <= (0.021)) & (((((Y-(1.54))-(0.01575))-(0))-(-0.0315)) >= (0.02625)) & (((((Y-(1.54))-(0.01575))-(0))-(-0.0315)) <= (0.0315))) | ((((((X-(1.78))-(0))-(-0.0223125))-(0.023625)) >= (0)) & (((((X-(1.78))-(0))-(-0.0223125))-(0.023625)) <= (0.021)) & (((((Y-(1.54))-(0.01575))-(0))-(-0.0315)) >= (0)) & (((((Y-(1.54))-(0.01575))-(0))-(-0.0315)) <= (0.0315))))!=0)) | (255*((False | False | ((((((((X-(1.78))-(0))-(-0.0223125))-(0)) >= (0)) & (((((X-(1.78))-(0))-(-0.0223125))-(0)) <= (0.00525)) & (((((Y-(1.44))-(0.01575))-(0))-(-0.0315)) >= (0)) & (((((Y-(1.44))-(0.01575))-(0))-(-0.0315)) <= (0.0315))) | (((((((X-(1.78))-(0))-(-0.0223125))-(0))-(0.0105))*(((((X-(1.78))-(0))-(-0.0223125))-(0))-(0.0105)) + (((((Y-(1.44))-(0.01575))-(0))-(-0.0315))-(0.021))*(((((X-(1.78))-(0))-(-0.0223125))-(0))-(0.0105))) <= 0.0105*0.0105) | ((((((X-(1.78))-(0))-(-0.0223125))-(0)) >= (0)) & (((((X-(1.78))-(0))-(-0.0223125))-(0)) <= (0.0105)) & (((((Y-(1.44))-(0.01575))-(0))-(-0.0315)) >= (0.0105)) & (((((Y-(1.44))-(0.01575))-(0))-(-0.0315)) <= (0.0315)))) & ~((((((((X-(1.78))-(0))-(-0.0223125))-(0))-(0.0105))*(((((X-(1.78))-(0))-(-0.0223125))-(0))-(0.0105)) + (((((Y-(1.44))-(0.01575))-(0))-(-0.0315))-(0.021))*(((((X-(1.78))-(0))-(-0.0223125))-(0))-(0.0105))) <= 0.00525*0.00525))) & ~(((((((X-(1.78))-(0))-(-0.0223125))-(0)) >= (0.00525)) & (((((X-(1.78))-(0))-(-0.0223125))-(0)) <= (0.0105)) & (((((Y-(1.44))-(0.01575))-(0))-(-0.0315)) >= (0.01575)) & (((((Y-(1.44))-(0.01575))-(0))-(-0.0315)) <= (0.02625)))) | (((((((0.0315)-(0))*(((((X-(1.78))-(0))-(-0.0223125))-(0))-(0.00525))-((0.00525)-(0.00525))*(((((Y-(1.44))-(0.01575))-(0))-(-0.0315))-(0))) >= 0) & ((((0)-(0.0315))*(((((X-(1.78))-(0))-(-0.0223125))-(0))-(0.00525))-((0.021)-(0.00525))*(((((Y-(1.44))-(0.01575))-(0))-(-0.0315))-(0.0315))) >= 0) & ((((0)-(0))*(((((X-(1.78))-(0))-(-0.0223125))-(0))-(0.021))-((0.00525)-(0.021))*(((((Y-(1.44))-(0.01575))-(0))-(-0.0315))-(0))) >= 0))) & ~((((((0.021)-(-0.0105))*(((((X-(1.78))-(0))-(-0.0223125))-(0))-(0.00525))-((0.00525)-(0.00525))*(((((Y-(1.44))-(0.01575))-(0))-(-0.0315))-(-0.0105))) >= 0) & ((((-0.0105)-(0.021))*(((((X-(1.78))-(0))-(-0.0223125))-(0))-(0.00525))-((0.021)-(0.00525))*(((((Y-(1.44))-(0.01575))-(0))-(-0.0315))-(0.021))) >= 0) & ((((-0.0105)-(-0.0105))*(((((X-(1.78))-(0))-(-0.0223125))-(0))-(0.021))-((0.00525)-(0.021))*(((((Y-(1.44))-(0.01575))-(0))-(-0.0315))-(-0.0105))) >= 0)))) & ~(((((((X-(1.78))-(0))-(-0.0223125))-(0)) >= (0)) & (((((X-(1.78))-(0))-(-0.0223125))-(0)) <= (0.021)) & (((((Y-(1.44))-(0.01575))-(0))-(-0.0315)) >= (0.0105)) & (((((Y-(1.44))-(0.01575))-(0))-(-0.0315)) <= (0.0315)))) | ((((((X-(1.78))-(0))-(-0.0223125))-(0.023625)) >= (0)) & (((((X-(1.78))-(0))-(-0.0223125))-(0.023625)) <= (0.021)) & (((((Y-(1.44))-(0.01575))-(0))-(-0.0315)) >= (0)) & (((((Y-(1.44))-(0.01575))-(0))-(-0.0315)) <= (0.0315))))!=0)) | (255*((False | False | ((((((((X-(1.78))-(0))-(-0.034125))-(0)) >= (0)) & (((((X-(1.78))-(0))-(-0.034125))-(0)) <= (0.00525)) & (((((Y-(1.34))-(0.01575))-(0))-(-0.0315)) >= (0)) & (((((Y-(1.34))-(0.01575))-(0))-(-0.0315)) <= (0.0315))) | (((((((X-(1.78))-(0))-(-0.034125))-(0))-(0.0105))*(((((X-(1.78))-(0))-(-0.034125))-(0))-(0.0105)) + (((((Y-(1.34))-(0.01575))-(0))-(-0.0315))-(0.021))*(((((X-(1.78))-(0))-(-0.034125))-(0))-(0.0105))) <= 0.0105*0.0105) | ((((((X-(1.78))-(0))-(-0.034125))-(0)) >= (0)) & (((((X-(1.78))-(0))-(-0.034125))-(0)) <= (0.0105)) & (((((Y-(1.34))-(0.01575))-(0))-(-0.0315)) >= (0.0105)) & (((((Y-(1.34))-(0.01575))-(0))-(-0.0315)) <= (0.0315)))) & ~((((((((X-(1.78))-(0))-(-0.034125))-(0))-(0.0105))*(((((X-(1.78))-(0))-(-0.034125))-(0))-(0.0105)) + (((((Y-(1.34))-(0.01575))-(0))-(-0.0315))-(0.021))*(((((X-(1.78))-(0))-(-0.034125))-(0))-(0.0105))) <= 0.00525*0.00525))) & ~(((((((X-(1.78))-(0))-(-0.034125))-(0)) >= (0.00525)) & (((((X-(1.78))-(0))-(-0.034125))-(0)) <= (0.0105)) & (((((Y-(1.34))-(0.01575))-(0))-(-0.0315)) >= (0.01575)) & (((((Y-(1.34))-(0.01575))-(0))-(-0.0315)) <= (0.02625)))) | (((((((0.0315)-(0))*(((((X-(1.78))-(0))-(-0.034125))-(0))-(0.00525))-((0.00525)-(0.00525))*(((((Y-(1.34))-(0.01575))-(0))-(-0.0315))-(0))) >= 0) & ((((0)-(0.0315))*(((((X-(1.78))-(0))-(-0.034125))-(0))-(0.00525))-((0.021)-(0.00525))*(((((Y-(1.34))-(0.01575))-(0))-(-0.0315))-(0.0315))) >= 0) & ((((0)-(0))*(((((X-(1.78))-(0))-(-0.034125))-(0))-(0.021))-((0.00525)-(0.021))*(((((Y-(1.34))-(0.01575))-(0))-(-0.0315))-(0))) >= 0))) & ~((((((0.021)-(-0.0105))*(((((X-(1.78))-(0))-(-0.034125))-(0))-(0.00525))-((0.00525)-(0.00525))*(((((Y-(1.34))-(0.01575))-(0))-(-0.0315))-(-0.0105))) >= 0) & ((((-0.0105)-(0.021))*(((((X-(1.78))-(0))-(-0.034125))-(0))-(0.00525))-((0.021)-(0.00525))*(((((Y-(1.34))-(0.01575))-(0))-(-0.0315))-(0.021))) >= 0) & ((((-0.0105)-(-0.0105))*(((((X-(1.78))-(0))-(-0.034125))-(0))-(0.021))-((0.00525)-(0.021))*(((((Y-(1.34))-(0.01575))-(0))-(-0.0315))-(-0.0105))) >= 0)))) & ~(((((((X-(1.78))-(0))-(-0.034125))-(0)) >= (0)) & (((((X-(1.78))-(0))-(-0.034125))-(0)) <= (0.021)) & (((((Y-(1.34))-(0.01575))-(0))-(-0.0315)) >= (0.0105)) & (((((Y-(1.34))-(0.01575))-(0))-(-0.0315)) <= (0.0315)))) | ((((((X-(1.78))-(0))-(-0.034125))-(0.023625)) >= (0.007875)) & (((((X-(1.78))-(0))-(-0.034125))-(0.023625)) <= (0.013125)) & (((((Y-(1.34))-(0.01575))-(0))-(-0.0315)) >= (0)) & (((((Y-(1.34))-(0.01575))-(0))-(-0.0315)) <= (0.0315))) | ((((((X-(1.78))-(0))-(-0.034125))-(0.023625)) >= (0)) & (((((X-(1.78))-(0))-(-0.034125))-(0.023625)) <= (0.021)) & (((((Y-(1.34))-(0.01575))-(0))-(-0.0315)) >= (0.02625)) & (((((Y-(1.34))-(0.01575))-(0))-(-0.0315)) <= (0.0315))) | ((((((X-(1.78))-(0))-(-0.034125))-(0.04725)) >= (0)) & (((((X-(1.78))-(0))-(-0.034125))-(0.04725)) <= (0.021)) & (((((Y-(1.34))-(0.01575))-(0))-(-0.0315)) >= (0)) & (((((Y-(1.34))-(0.01575))-(0))-(-0.0315)) <= (0.0315))))!=0)) | (65280*((False | False | ((((((((((X-(1.15))-(0))-(-0.077))-(0)) >= (0)) & (((((X-(1.15))-(0))-(-0.077))-(0)) <= (0.028)) & (((((Y-(1.698))-(0.0525))-(0))-(-0.042)) >= (0)) & (((((Y-(1.698))-(0.0525))-(0))-(-0.042)) <= (0.042)))) & ~((((((0.042)-(0))*(((((X-(1.15))-(0))-(-0.077))-(0))-(0))-((0)-(0))*(((((Y-(1.698))-(0.0525))-(0))-(-0.042))-(0))) >= 0) & ((((0.021)-(0.042))*(((((X-(1.15))-(0))-(-0.077))-(0))-(0))-((0.0091)-(0))*(((((Y-(1.698))-(0.0525))-(0))-(-0.042))-(0.042))) >= 0) & ((((0)-(0.021))*(((((X-(1.15))-(0))-(-0.077))-(0))-(0.0091))-((0)-(0.0091))*(((((Y-(1.698))-(0.0525))-(0))-(-0.042))-(0.021))) >= 0)))) & ~((((((0.021)-(0))*(((((X-(1.15))-(0))-(-0.077))-(0))-(0.028))-((0.0189)-(0.028))*(((((Y-(1.698))-(0.0525))-(0))-(-0.042))-(0))) >= 0) & ((((0.042)-(0.021))*(((((X-(1.15))-(0))-(-0.077))-(0))-(0.0189))-((0.028)-(0.0189))*(((((Y-(1.698))-(0.0525))-(0))-(-0.042))-(0.021))) >= 0) & ((((0)-(0.042))*(((((X-(1.15))-(0))-(-0.077))-(0))-(0.028))-((0.028)-(0.028))*(((((Y-(1.698))-(0.0525))-(0))-(-0.042))-(0.042))) >= 0)))) & ~((((((0.042)-(0.042))*(((((X-(1.15))-(0))-(-0.077))-(0))-(0.007))-((0.021)-(0.007))*(((((Y-(1.698))-(0.0525))-(0))-(-0.042))-(0.042))) >= 0) & ((((0.0259)-(0.042))*(((((X-(1.15))-(0))-(-0.077))-(0))-(0.021))-((0.014)-(0.021))*(((((Y-(1.698))-(0.0525))-(0))-(-0.042))-(0.042))) >= 0) & ((((0.042)-(0.0259))*(((((X-(1.15))-(0))-(-0.077))-(0))-(0.014))-((0.007)-(0.014))*(((((Y-(1.698))-(0.0525))-(0))-(-0.042))-(0.0259))) >= 0)))) & ~((((((0.0161)-(0))*(((((X-(1.15))-(0))-(-0.077))-(0))-(0.007))-((0.014)-(0.007))*(((((Y-(1.698))-(0.0525))-(0))-(-0.042))-(0))) >= 0) & ((((0)-(0.0161))*(((((X-(1.15))-(0))-(-0.077))-(0))-(0.014))-((0.021)-(0.014))*(((((Y-(1.698))-(0.0525))-(0))-(-0.042))-(0.0161))) >= 0) & ((((0)-(0))*(((((X-(1.15))-(0))-(-0.077))-(0))-(0.021))-((0.007)-(0.021))*(((((Y-(1.698))-(0.0525))-(0))-(-0.042))-(0))) >= 0))) | ((((((X-(1.15))-(0))-(-0.077))-(0.0315)) >= (0.0105)) & (((((X-(1.15))-(0))-(-0.077))-(0.0315)) <= (0.0175)) & (((((Y-(1.698))-(0.0525))-(0))-(-0.042)) >= (0)) & (((((Y-(1.698))-(0.0525))-(0))-(-0.042)) <= (0.042))) | ((((((X-(1.15))-(0))-(-0.077))-(0.0315)) >= (0)) & (((((X-(1.15))-(0))-(-0.077))-(0.0315)) <= (0.028)) & (((((Y-(1.698))-(0.0525))-(0))-(-0.042)) >= (0.035)) & (((((Y-(1.698))-(0.0525))-(0))-(-0.042)) <= (0.042))) | ((((((0.042)-(0))*(((((X-(1.15))-(0))-(-0.077))-(0.063))-(0))-((0.014)-(0))*(((((Y-(1.698))-(0.0525))-(0))-(-0.042))-(0))) >= 0) & ((((0)-(0.042))*(((((X-(1.15))-(0))-(-0.077))-(0.063))-(0.014))-((0.028)-(0.014))*(((((Y-(1.698))-(0.0525))-(0))-(-0.042))-(0.042))) >= 0) & ((((0)-(0))*(((((X-(1.15))-(0))-(-0.077))-(0.063))-(0.028))-((0)-(0.028))*(((((Y-(1.698))-(0.0525))-(0))-(-0.042))-(0))) >= 0))) & ~(((((((0.0245)-(-0.0175))*(((((X-(1.15))-(0))-(-0.077))-(0.063))-(0))-((0.014)-(0))*(((((Y-(1.698))-(0.0525))-(0))-(-0.042))-(-0.0175))) >= 0) & ((((-0.0175)-(0.0245))*(((((X-(1.15))-(0))-(-0.077))-(0.063))-(0.014))-((0.028)-(0.014))*(((((Y-(1.698))-(0.0525))-(0))-(-0.042))-(0.0245))) >= 0) & ((((-0.0175)-(-0.0175))*(((((X-(1.15))-(0))-(-0.077))-(0.063))-(0.028))-((0)-(0.028))*(((((Y-(1.698))-(0.0525))-(0))-(-0.042))-(-0.0175))) >= 0))) & ~(((((((X-(1.15))-(0))-(-0.077))-(0.063)) >= (0)) & (((((X-(1.15))-(0))-(-0.077))-(0.063)) <= (0.028)) & (((((Y-(1.698))-(0.0525))-(0))-(-0.042)) >= (0.007)) & (((((Y-(1.698))-(0.0525))-(0))-(-0.042)) <= (0.014))))) | ((((((X-(1.15))-(0))-(-0.077))-(0.0945)) >= (0)) & (((((X-(1.15))-(0))-(-0.077))-(0.0945)) <= (0.007)) & (((((Y-(1.698))-(0.0525))-(0))-(-0.042)) >= (0)) & (((((Y-(1.698))-(0.0525))-(0))-(-0.042)) <= (0.042))) | ((((((X-(1.15))-(0))-(-0.077))-(0.0945)) >= (0)) & (((((X-(1.15))-(0))-(-0.077))-(0.0945)) <= (0.028)) & (((((Y-(1.698))-(0.0525))-(0))-(-0.042)) >= (0)) & (((((Y-(1.698))-(0.0525))-(0))-(-0.042)) <= (0.007))) | ((((((((X-(1.15))-(0))-(-0.077))-(0.126))-(0.004375)) >= (0.0105)) & ((((((X-(1.15))-(0))-(-0.077))-(0.126))-(0.004375)) <= (0.0175)) & ((((((Y-(1.698))-(0.0525))-(0))-(-0.042))-(0)) >= (0)) & ((((((Y-(1.698))-(0.0525))-(0))-(-0.042))-(0)) <= (0.042))) | (((((((X-(1.15))-(0))-(-0.077))-(0.126))-(0.004375)) >= (0)) & ((((((X-(1.15))-(0))-(-0.077))-(0.126))-(0.004375)) <= (0.014)) & ((((((Y-(1.698))-(0.0525))-(0))-(-0.042))-(0)) >= (0.0245)) & ((((((Y-(1.698))-(0.0525))-(0))-(-0.042))-(0)) <= (0.042)))) & ~(((((((((X-(1.15))-(0))-(-0.077))-(0.126))-(0.004375))-(0))*((((((X-(1.15))-(0))-(-0.077))-(0.126))-(0.004375))-(0)) + ((((((Y-(1.698))-(0.0525))-(0))-(-0.042))-(0))-(0.042))*((((((X-(1.15))-(0))-(-0.077))-(0.126))-(0.004375))-(0))) <= 0.0105*0.0105)) | False | (((((((((((X-(1.15))-(0))-(-0.09275))-(0))-(0.014))*(((((X-(1.15))-(0))-(-0.09275))-(0))-(0.014)) + (((((Y-(1.698))-(0.0525))-(0))-(-0.105))-(0.028))*(((((X-(1.15))-(0))-(-0.09275))-(0))-(0.014))) <= 0.014*0.014)) & ~((((((((X-(1.15))-(0))-(-0.09275))-(0))-(0.014))*(((((X-(1.15))-(0))-(-0.09275))-(0))-(0.014)) + (((((Y-(1.698))-(0.0525))-(0))-(-0.105))-(0.028))*(((((X-(1.15))-(0))-(-0.09275))-(0))-(0.014))) <= 0.007*0.007))) & ~(((((((X-(1.15))-(0))-(-0.09275))-(0)) >= (0)) & (((((X-(1.15))-(0))-(-0.09275))-(0)) <= (0.014)) & (((((Y-(1.698))-(0.0525))-(0))-(-0.105)) >= (0)) & (((((Y-(1.698))-(0.0525))-(0))-(-0.105)) <= (0.028)))) | ((((((X-(1.15))-(0))-(-0.09275))-(0)) >= (0)) & (((((X-(1.15))-(0))-(-0.09275))-(0)) <= (0.028)) & (((((Y-(1.698))-(0.0525))-(0))-(-0.105)) >= (0)) & (((((Y-(1.698))-(0.0525))-(0))-(-0.105)) <= (0.028)))) & ~((((((0.028)-(0.007))*(((((X-(1.15))-(0))-(-0.09275))-(0))-(0))-((0)-(0))*(((((Y-(1.698))-(0.0525))-(0))-(-0.105))-(0.007))) >= 0) & ((((0.028)-(0.028))*(((((X-(1.15))-(0))-(-0.09275))-(0))-(0))-((0.021)-(0))*(((((Y-(1.698))-(0.0525))-(0))-(-0.105))-(0.028))) >= 0) & ((((0.007)-(0.028))*(((((X-(1.15))-(0))-(-0.09275))-(0))-(0.021))-((0)-(0.021))*(((((Y-(1.698))-(0.0525))-(0))-(-0.105))-(0.028))) >= 0)))) & ~((((((0.0245)-(0.007))*(((((X-(1.15))-(0))-(-0.09275))-(0))-(0.0105))-((0.028)-(0.0105))*(((((Y-(1.698))-(0.0525))-(0))-(-0.105))-(0.007))) >= 0) & ((((0.007)-(0.0245))*(((((X-(1.15))-(0))-(-0.09275))-(0))-(0.028))-((0.028)-(0.028))*(((((Y-(1.698))-(0.0525))-(0))-(-0.105))-(0.0245))) >= 0) & ((((0.007)-(0.007))*(((((X-(1.15))-(0))-(-0.09275))-(0))-(0.028))-((0.0105)-(0.028))*(((((Y-(1.698))-(0.0525))-(0))-(-0.105))-(0.007))) >= 0))) | ((((((((X-(1.15))-(0))-(-0.09275))-(0.0315))-(0.014))*(((((X-(1.15))-(0))-(-0.09275))-(0.0315))-(0.014)) + (((0) + (((((Y-(1.698))-(0.0525))-(0))-(-0.105))-(0))/(1.5))-(0.014))*(((((X-(1.15))-(0))-(-0.09275))-(0.0315))-(0.014))) <= 0.014*0.014)) & ~((((((((X-(1.15))-(0))-(-0.09275))-(0.0315))-(0.014))*(((((X-(1.15))-(0))-(-0.09275))-(0.0315))-(0.014)) + (((0) + (((((Y-(1.698))-(0.0525))-(0))-(-0.105))-(0))/(1.5))-(0.014))*(((((X-(1.15))-(0))-(-0.09275))-(0.0315))-(0.014))) <= 0.007*0.007)) | False | ((((((X-(1.15))-(0))-(-0.09275))-(0.0945)) >= (0)) & (((((X-(1.15))-(0))-(-0.09275))-(0.0945)) <= (0.028)) & (((((Y-(1.698))-(0.0525))-(0))-(-0.105)) >= (0)) & (((((Y-(1.698))-(0.0525))-(0))-(-0.105)) <= (0.042))) | ((((((X-(1.15))-(0))-(-0.09275))-(0.126)) >= (0)) & (((((X-(1.15))-(0))-(-0.09275))-(0.126)) <= (0.028)) & (((((Y-(1.698))-(0.0525))-(0))-(-0.105)) >= (0)) & (((((Y-(1.698))-(0.0525))-(0))-(-0.105)) <= (0.042))) | ((((((X-(1.15))-(0))-(-0.09275))-(0.1575)) >= (0)) & (((((X-(1.15))-(0))-(-0.09275))-(0.1575)) <= (0.028)) & (((((Y-(1.698))-(0.0525))-(0))-(-0.105)) >= (0)) & (((((Y-(1.698))-(0.0525))-(0))-(-0.105)) <= (0.042))))!=0)) | (65280*((False | False | ((((((((X-(1.35))-(0))-(-0.02975))-(0)) >= (0)) & (((((X-(1.35))-(0))-(-0.02975))-(0)) <= (0.007)) & (((((Y-(1.84))-(0.0525))-(0))-(-0.042)) >= (0)) & (((((Y-(1.84))-(0.0525))-(0))-(-0.042)) <= (0.042))) | (((((((X-(1.35))-(0))-(-0.02975))-(0))-(0.014))*(((((X-(1.35))-(0))-(-0.02975))-(0))-(0.014)) + (((((Y-(1.84))-(0.0525))-(0))-(-0.042))-(0.028))*(((((X-(1.35))-(0))-(-0.02975))-(0))-(0.014))) <= 0.014*0.014) | ((((((X-(1.35))-(0))-(-0.02975))-(0)) >= (0)) & (((((X-(1.35))-(0))-(-0.02975))-(0)) <= (0.014)) & (((((Y-(1.84))-(0.0525))-(0))-(-0.042)) >= (0.014)) & (((((Y-(1.84))-(0.0525))-(0))-(-0.042)) <= (0.042)))) & ~((((((((X-(1.35))-(0))-(-0.02975))-(0))-(0.014))*(((((X-(1.35))-(0))-(-0.02975))-(0))-(0.014)) + (((((Y-(1.84))-(0.0525))-(0))-(-0.042))-(0.028))*(((((X-(1.35))-(0))-(-0.02975))-(0))-(0.014))) <= 0.007*0.007))) & ~(((((((X-(1.35))-(0))-(-0.02975))-(0)) >= (0.007)) & (((((X-(1.35))-(0))-(-0.02975))-(0)) <= (0.014)) & (((((Y-(1.84))-(0.0525))-(0))-(-0.042)) >= (0.021)) & (((((Y-(1.84))-(0.0525))-(0))-(-0.042)) <= (0.035)))) | (((((((0.042)-(0))*(((((X-(1.35))-(0))-(-0.02975))-(0))-(0.007))-((0.007)-(0.007))*(((((Y-(1.84))-(0.0525))-(0))-(-0.042))-(0))) >= 0) & ((((0)-(0.042))*(((((X-(1.35))-(0))-(-0.02975))-(0))-(0.007))-((0.028)-(0.007))*(((((Y-(1.84))-(0.0525))-(0))-(-0.042))-(0.042))) >= 0) & ((((0)-(0))*(((((X-(1.35))-(0))-(-0.02975))-(0))-(0.028))-((0.007)-(0.028))*(((((Y-(1.84))-(0.0525))-(0))-(-0.042))-(0))) >= 0))) & ~((((((0.028)-(-0.014))*(((((X-(1.35))-(0))-(-0.02975))-(0))-(0.007))-((0.007)-(0.007))*(((((Y-(1.84))-(0.0525))-(0))-(-0.042))-(-0.014))) >= 0) & ((((-0.014)-(0.028))*(((((X-(1.35))-(0))-(-0.02975))-(0))-(0.007))-((0.028)-(0.007))*(((((Y-(1.84))-(0.0525))-(0))-(-0.042))-(0.028))) >= 0) & ((((-0.014)-(-0.014))*(((((X-(1.35))-(0))-(-0.02975))-(0))-(0.028))-((0.007)-(0.028))*(((((Y-(1.84))-(0.0525))-(0))-(-0.042))-(-0.014))) >= 0)))) & ~(((((((X-(1.35))-(0))-(-0.02975))-(0)) >= (0)) & (((((X-(1.35))-(0))-(-0.02975))-(0)) <= (0.028)) & (((((Y-(1.84))-(0.0525))-(0))-(-0.042)) >= (0.014)) & (((((Y-(1.84))-(0.0525))-(0))-(-0.042)) <= (0.042)))) | ((((((((X-(1.35))-(0))-(-0.02975))-(0.0315))-(0.004375)) >= (0.0105)) & ((((((X-(1.35))-(0))-(-0.02975))-(0.0315))-(0.004375)) <= (0.0175)) & ((((((Y-(1.84))-(0.0525))-(0))-(-0.042))-(0)) >= (0)) & ((((((Y-(1.84))-(0.0525))-(0))-(-0.042))-(0)) <= (0.042))) | (((((((X-(1.35))-(0))-(-0.02975))-(0.0315))-(0.004375)) >= (0)) & ((((((X-(1.35))-(0))-(-0.02975))-(0.0315))-(0.004375)) <= (0.014)) & ((((((Y-(1.84))-(0.0525))-(0))-(-0.042))-(0)) >= (0.0245)) & ((((((Y-(1.84))-(0.0525))-(0))-(-0.042))-(0)) <= (0.042)))) & ~(((((((((X-(1.35))-(0))-(-0.02975))-(0.0315))-(0.004375))-(0))*((((((X-(1.35))-(0))-(-0.02975))-(0.0315))-(0.004375))-(0)) + ((((((Y-(1.84))-(0.0525))-(0))-(-0.042))-(0))-(0.042))*((((((X-(1.35))-(0))-(-0.02975))-(0.0315))-(0.004375))-(0))) <= 0.0105*0.0105)) | False | ((((((((X-(1.35))-(0))-(-0.0455))-(0))-(0.004375)) >= (0.0105)) & ((((((X-(1.35))-(0))-(-0.0455))-(0))-(0.004375)) <= (0.0175)) & ((((((Y-(1.84))-(0.0525))-(0))-(-0.105))-(0)) >= (0)) & ((((((Y-(1.84))-(0.0525))-(0))-(-0.105))-(0)) <= (0.042))) | (((((((X-(1.35))-(0))-(-0.0455))-(0))-(0.004375)) >= (0)) & ((((((X-(1.35))-(0))-(-0.0455))-(0))-(0.004375)) <= (0.014)) & ((((((Y-(1.84))-(0.0525))-(0))-(-0.105))-(0)) >= (0.0245)) & ((((((Y-(1.84))-(0.0525))-(0))-(-0.105))-(0)) <= (0.042)))) & ~(((((((((X-(1.35))-(0))-(-0.0455))-(0))-(0.004375))-(0))*((((((X-(1.35))-(0))-(-0.0455))-(0))-(0.004375))-(0)) + ((((((Y-(1.84))-(0.0525))-(0))-(-0.105))-(0))-(0.042))*((((((X-(1.35))-(0))-(-0.0455))-(0))-(0.004375))-(0))) <= 0.0105*0.0105)) | ((((((((X-(1.35))-(0))-(-0.0455))-(0.0315))-(0.014))*(((((X-(1.35))-(0))-(-0.0455))-(0.0315))-(0.014)) + (((0) + (((((Y-(1.84))-(0.0525))-(0))-(-0.105))-(0))/(1.5))-(0.014))*(((((X-(1.35))-(0))-(-0.0455))-(0.0315))-(0.014))) <= 0.014*0.014)) & ~((((((((X-(1.35))-(0))-(-0.0455))-(0.0315))-(0.014))*(((((X-(1.35))-(0))-(-0.0455))-(0.0315))-(0.014)) + (((0) + (((((Y-(1.84))-(0.0525))-(0))-(-0.105))-(0))/(1.5))-(0.014))*(((((X-(1.35))-(0))-(-0.0455))-(0.0315))-(0.014))) <= 0.007*0.007)) | ((((((((((X-(1.35))-(0))-(-0.0455))-(0.063)) >= (0)) & (((((X-(1.35))-(0))-(-0.0455))-(0.063)) <= (0.028)) & (((((Y-(1.84))-(0.0525))-(0))-(-0.105)) >= (0)) & (((((Y-(1.84))-(0.0525))-(0))-(-0.105)) <= (0.042)))) & ~(((((((X-(1.35))-(0))-(-0.0455))-(0.063)) >= (0.007)) & (((((X-(1.35))-(0))-(-0.0455))-(0.063)) <= (0.028)) & (((((Y-(1.84))-(0.0525))-(0))-(-0.105)) >= (0.028)) & (((((Y-(1.84))-(0.0525))-(0))-(-0.105)) <= (0.042))))) & ~((((((0.028)-(0.028))*(((((X-(1.35))-(0))-(-0.0455))-(0.063))-(0.007))-((0.0189)-(0.007))*(((((Y-(1.84))-(0.0525))-(0))-(-0.105))-(0.028))) >= 0) & ((((0.0175)-(0.028))*(((((X-(1.35))-(0))-(-0.0455))-(0.063))-(0.0189))-((0.007)-(0.0189))*(((((Y-(1.84))-(0.0525))-(0))-(-0.105))-(0.028))) >= 0) & ((((0.028)-(0.0175))*(((((X-(1.35))-(0))-(-0.0455))-(0.063))-(0.007))-((0.007)-(0.007))*(((((Y-(1.84))-(0.0525))-(0))-(-0.105))-(0.0175))) >= 0)))) & ~((((((0.014)-(0))*(((((X-(1.35))-(0))-(-0.0455))-(0.063))-(0.028))-((0.0126)-(0.028))*(((((Y-(1.84))-(0.0525))-(0))-(-0.105))-(0))) >= 0) & ((((0.028)-(0.014))*(((((X-(1.35))-(0))-(-0.0455))-(0.063))-(0.0126))-((0.028)-(0.0126))*(((((Y-(1.84))-(0.0525))-(0))-(-0.105))-(0.014))) >= 0) & ((((0)-(0.028))*(((((X-(1.35))-(0))-(-0.0455))-(0.063))-(0.028))-((0.028)-(0.028))*(((((Y-(1.84))-(0.0525))-(0))-(-0.105))-(0.028))) >= 0)))) & ~((((((0.0105)-(0))*(((((X-(1.35))-(0))-(-0.0455))-(0.063))-(0.007))-((0.007)-(0.007))*(((((Y-(1.84))-(0.0525))-(0))-(-0.105))-(0))) >= 0) & ((((0)-(0.0105))*(((((X-(1.35))-(0))-(-0.0455))-(0.063))-(0.007))-((0.0189)-(0.007))*(((((Y-(1.84))-(0.0525))-(0))-(-0.105))-(0.0105))) >= 0) & ((((0)-(0))*(((((X-(1.35))-(0))-(-0.0455))-(0.063))-(0.0189))-((0.007)-(0.0189))*(((((Y-(1.84))-(0.0525))-(0))-(-0.105))-(0))) >= 0))))!=0)) | (65280*((False | False | ((((((((((X-(1.59))-(0))-(-0.02975))-(0))-(0.014))*(((((X-(1.59))-(0))-(-0.02975))-(0))-(0.014)) + (((((Y-(1.84))-(0.0525))-(0))-(-0.042))-(0.014))*(((((X-(1.59))-(0))-(-0.02975))-(0))-(0.014))) <= 0.014*0.014) | (((((((X-(1.59))-(0))-(-0.02975))-(0))-(0.014))*(((((X-(1.59))-(0))-(-0.02975))-(0))-(0.014)) + (((((Y-(1.84))-(0.0525))-(0))-(-0.042))-(0.028))*(((((X-(1.59))-(0))-(-0.02975))-(0))-(0.014))) <= 0.014*0.014) | ((((((X-(1.59))-(0))-(-0.02975))-(0)) >= (0)) & (((((X-(1.59))-(0))-(-0.02975))-(0)) <= (0.028)) & (((((Y-(1.84))-(0.0525))-(0))-(-0.042)) >= (0.01225)) & (((((Y-(1.84))-(0.0525))-(0))-(-0.042)) <= (0.02975)))) & ~((((((((X-(1.59))-(0))-(-0.02975))-(0))-(0.014))*(((((X-(1.59))-(0))-(-0.02975))-(0))-(0.014)) + (((((Y-(1.84))-(0.0525))-(0))-(-0.042))-(0.014))*(((((X-(1.59))-(0))-(-0.02975))-(0))-(0.014))) <= 0.007*0.007))) & ~((((((((X-(1.59))-(0))-(-0.02975))-(0))-(0.014))*(((((X-(1.59))-(0))-(-0.02975))-(0))-(0.014)) + (((((Y-(1.84))-(0.0525))-(0))-(-0.042))-(0.028))*(((((X-(1.59))-(0))-(-0.02975))-(0))-(0.014))) <= 0.007*0.007))) & ~(((((((X-(1.59))-(0))-(-0.02975))-(0)) >= (0.007)) & (((((X-(1.59))-(0))-(-0.02975))-(0)) <= (0.028)) & (((((Y-(1.84))-(0.0525))-(0))-(-0.042)) >= (0.014)) & (((((Y-(1.84))-(0.0525))-(0))-(-0.042)) <= (0.028)))) | ((((((((X-(1.59))-(0))-(-0.02975))-(0.0315))-(0.004375)) >= (0.0105)) & ((((((X-(1.59))-(0))-(-0.02975))-(0.0315))-(0.004375)) <= (0.0175)) & ((((((Y-(1.84))-(0.0525))-(0))-(-0.042))-(0)) >= (0)) & ((((((Y-(1.84))-(0.0525))-(0))-(-0.042))-(0)) <= (0.042))) | (((((((X-(1.59))-(0))-(-0.02975))-(0.0315))-(0.004375)) >= (0)) & ((((((X-(1.59))-(0))-(-0.02975))-(0.0315))-(0.004375)) <= (0.014)) & ((((((Y-(1.84))-(0.0525))-(0))-(-0.042))-(0)) >= (0.0245)) & ((((((Y-(1.84))-(0.0525))-(0))-(-0.042))-(0)) <= (0.042)))) & ~(((((((((X-(1.59))-(0))-(-0.02975))-(0.0315))-(0.004375))-(0))*((((((X-(1.59))-(0))-(-0.02975))-(0.0315))-(0.004375))-(0)) + ((((((Y-(1.84))-(0.0525))-(0))-(-0.042))-(0))-(0.042))*((((((X-(1.59))-(0))-(-0.02975))-(0.0315))-(0.004375))-(0))) <= 0.0105*0.0105)) | False | ((((((((X-(1.59))-(0))-(-0.0455))-(0))-(0.004375)) >= (0.0105)) & ((((((X-(1.59))-(0))-(-0.0455))-(0))-(0.004375)) <= (0.0175)) & ((((((Y-(1.84))-(0.0525))-(0))-(-0.105))-(0)) >= (0)) & ((((((Y-(1.84))-(0.0525))-(0))-(-0.105))-(0)) <= (0.042))) | (((((((X-(1.59))-(0))-(-0.0455))-(0))-(0.004375)) >= (0)) & ((((((X-(1.59))-(0))-(-0.0455))-(0))-(0.004375)) <= (0.014)) & ((((((Y-(1.84))-(0.0525))-(0))-(-0.105))-(0)) >= (0.0245)) & ((((((Y-(1.84))-(0.0525))-(0))-(-0.105))-(0)) <= (0.042)))) & ~(((((((((X-(1.59))-(0))-(-0.0455))-(0))-(0.004375))-(0))*((((((X-(1.59))-(0))-(-0.0455))-(0))-(0.004375))-(0)) + ((((((Y-(1.84))-(0.0525))-(0))-(-0.105))-(0))-(0.042))*((((((X-(1.59))-(0))-(-0.0455))-(0))-(0.004375))-(0))) <= 0.0105*0.0105)) | (((((((((X-(1.59))-(0))-(-0.0455))-(0.0315))-(0.014))*(((((X-(1.59))-(0))-(-0.0455))-(0.0315))-(0.014)) + (((((Y-(1.84))-(0.0525))-(0))-(-0.105))-(0.014))*(((((X-(1.59))-(0))-(-0.0455))-(0.0315))-(0.014))) <= 0.014*0.014)) & ~((((((((X-(1.59))-(0))-(-0.0455))-(0.0315))-(0.014))*(((((X-(1.59))-(0))-(-0.0455))-(0.0315))-(0.014)) + (((((Y-(1.84))-(0.0525))-(0))-(-0.105))-(0.014))*(((((X-(1.59))-(0))-(-0.0455))-(0.0315))-(0.014))) <= 0.007*0.007))) & ~(((((((X-(1.59))-(0))-(-0.0455))-(0.0315)) >= (0)) & (((((X-(1.59))-(0))-(-0.0455))-(0.0315)) <= (0.028)) & (((((Y-(1.84))-(0.0525))-(0))-(-0.105)) >= (0.014)) & (((((Y-(1.84))-(0.0525))-(0))-(-0.105)) <= (0.042)))) | ((((((X-(1.59))-(0))-(-0.0455))-(0.0315)) >= (0)) & (((((X-(1.59))-(0))-(-0.0455))-(0.0315)) <= (0.007)) & (((((Y-(1.84))-(0.0525))-(0))-(-0.105)) >= (0.014)) & (((((Y-(1.84))-(0.0525))-(0))-(-0.105)) <= (0.028))) | ((((((X-(1.59))-(0))-(-0.0455))-(0.0315)) >= (0.021)) & (((((X-(1.59))-(0))-(-0.0455))-(0.0315)) <= (0.028)) & (((((Y-(1.84))-(0.0525))-(0))-(-0.105)) >= (0)) & (((((Y-(1.84))-(0.0525))-(0))-(-0.105)) <= (0.028))) | ((((((X-(1.59))-(0))-(-0.0455))-(0.063)) >= (0)) & (((((X-(1.59))-(0))-(-0.0455))-(0.063)) <= (0.007)) & (((((Y-(1.84))-(0.0525))-(0))-(-0.105)) >= (0)) & (((((Y-(1.84))-(0.0525))-(0))-(-0.105)) <= (0.042))) | ((((((X-(1.59))-(0))-(-0.0455))-(0.063)) >= (0)) & (((((X-(1.59))-(0))-(-0.0455))-(0.063)) <= (0.028)) & (((((Y-(1.84))-(0.0525))-(0))-(-0.105)) >= (0.035)) & (((((Y-(1.84))-(0.0525))-(0))-(-0.105)) <= (0.042))) | ((((((X-(1.59))-(0))-(-0.0455))-(0.063)) >= (0)) & (((((X-(1.59))-(0))-(-0.0455))-(0.063)) <= (0.0186666666667)) & (((((Y-(1.84))-(0.0525))-(0))-(-0.105)) >= (0.0175)) & (((((Y-(1.84))-(0.0525))-(0))-(-0.105)) <= (0.0245))))!=0)) | (16777215*(((True) & ~(((X >= (1)) & (X <= (2)) & (Y >= (1)) & (Y <= (1.93)))))!=0)));
+      }
+   }
+      printf("%d %d %d\n",i,j,val);
+   }
diff --git a/src/core/vol_gif.c b/src/core/vol_gif.c
new file mode 100644
index 0000000..c06ba71
--- /dev/null
+++ b/src/core/vol_gif.c
@@ -0,0 +1,265 @@
+//
+// vol_gif.c
+//    .vol to .gif
+//
+// Neil Gershenfeld 11/29/13
+// (c) Massachusetts Institute of Technology 2013
+//
+// This work may be reproduced, modified, distributed,
+// performed, and displayed for any purpose, but must
+// acknowledge the fab modules project. Copyright is
+// retained and must be preserved. The work is provided
+// as is; no warranty is provided, and users accept all 
+// liability.
+//
+
+/*
+gcc -O3 vol_gif.c -o vol_gif -D_FILE_OFFSET_BITS=64 -Wall -lm -lgif
+*/
+
+#include "fab.h"
+
+void read_voxel_f(FILE *input_file,float *f,int x,int y,int z,int *xi,int *yi,int *zi,int ix,int iy,int iz) {
+   int r,match=0;
+   while (1) {
+      if ((x == *xi) && (y == *yi) && (z == *zi)) {
+         match = 1;
+         }
+      r = fread(f,sizeof(*f),1,input_file);
+      if (r == 0) {
+         printf("vol_gif: oops -- not enough points in file\n");
+         exit(-1);
+         }
+      *xi += 1;
+      if (*xi == ix) {
+         *xi = 0;
+         *yi += 1;
+         if (*yi == iy) {
+            *yi = 0;
+            *zi += 1;
+            }
+         }
+      if (match == 1)
+         return;
+      }
+   }
+
+void read_voxel_i(FILE *input_file,uint16_t *i,int x,int y,int z,int *xi,int *yi,int *zi,int ix,int iy,int iz) {
+   int r,match=0;
+   while (1) {
+      if ((x == *xi) && (y == *yi) && (z == *zi)) {
+         match = 1;
+         }
+      r = fread(i,sizeof(*i),1,input_file);
+      if (r == 0) {
+         printf("vol_gif: oops -- not enough points in file\n");
+         exit(-1);
+         }
+      *xi += 1;
+      if (*xi == ix) {
+         *xi = 0;
+         *yi += 1;
+         if (*yi == iy) {
+            *yi = 0;
+            *zi += 1;
+            }
+         }
+      if (match == 1)
+         return;
+      }
+   }
+
+int main(int argc, char **argv) {
+   FILE *input_file;
+   float arg,size,rx,ry,rz;
+   float f,fmin=1e10,fmax=-1e10;
+   uint16_t i;
+   uint64_t count;
+   int xi,yi,zi,xo,yo,zo,nx,ny,nz,dx,dy,dz,x0,y0,z0,h;
+   int **image;
+   char format,type,comment[256];
+   GifFileType *GIFfile;
+   ColorMapObject *GIFcmap;
+   GifPixelType *GIFline;
+   //
+   // command line args
+   //
+   if (!((argc == 6) || (argc == 7)  || (argc == 8) || (argc == 9) || (argc == 10) || (argc == 13) || (argc == 16) || (argc == 19))) {
+      printf("command line: vol_gif in.vol out.gif nx ny nz [format [type [arg [size [dx dy dz [x0 y0 z0 [rx ry rz]]]]]]]\n");
+      printf("   in.vol = input volume file\n");
+      printf("   out.gif = output GIF file\n");
+      printf("   nx,ny,nz = x,y,z input voxel number\n");
+      printf("   format = 'f' for float 32, 'i' for uint16_t (default 'f')\n");
+      printf("   type = 's' for section, 'h' for height (default 's')\n");
+      printf("   arg = gamma for 's', threshold for 'h' (default 1)\n");
+      printf("   size = mm per voxel (default 1)\n");
+      printf("   dx,dy,dz = x,y,z output voxel number (default all)\n");
+      printf("   x0,y0,z0 = x,y,z output voxel origin (default 0)\n");
+      printf("   to be implemented: rx,ry,rz = view rotation angles (degrees; default 0)\n");
+      exit(-1);
+      }
+   format = 'f';
+   type = 's';
+   arg = 1;
+   size = 1.0;
+   rx = ry = rz = 0;
+   sscanf(argv[3],"%d",&nx);
+   sscanf(argv[4],"%d",&ny);
+   sscanf(argv[5],"%d",&nz);
+   dx = nx; dy = ny; dz = nz;
+   x0 = y0 = z0 = 0;
+   if (argc >= 7) {
+      sscanf(argv[6],"%c",&format);
+      if (!((format == 'f') || (format == 'i'))) {
+         printf("vol_gif: oops -- format must be 'f' or 'i'\n");
+         exit(-1);
+         }
+      }
+   if (argc >= 8) {
+      sscanf(argv[7],"%c",&type);
+      if (!((type == 's') || (type == 'h'))) {
+         printf("vol_gif: oops -- type must be 's' or 'h'\n");
+         exit(-1);
+         }
+      }
+   if (argc >= 9) {
+      sscanf(argv[8],"%f",&arg);
+      }
+   if (argc >= 10) {
+      sscanf(argv[9],"%f",&size);
+      }
+   if (argc >= 13) {
+      sscanf(argv[10],"%d",&x0);
+      sscanf(argv[11],"%d",&y0);
+      sscanf(argv[12],"%d",&z0);
+      }
+   if (argc >= 16) {
+      sscanf(argv[13],"%d",&dx);
+      sscanf(argv[14],"%d",&dy);
+      sscanf(argv[15],"%d",&dz);
+      }
+   if (argc >= 19) {
+      sscanf(argv[16],"%f",&rx);
+      sscanf(argv[17],"%f",&ry);
+      sscanf(argv[18],"%f",&rz);
+      }
+   //
+   // check and find limits
+   //
+   input_file = fopen(argv[1],"rb");
+   if (input_file == NULL) {
+      printf("vol_gif: oops -- can not open %s\n",argv[1]);
+      exit(-1);
+      }
+   if (((x0 + dx) > nx) || ((y0 + dy) > ny) || ((z0 + dz) > nz)) {
+      printf("vol_gif: oops -- region too large\n");
+      exit(-1);
+      }
+   printf("read %s\n",argv[1]);
+   if (format == 'f') {
+      count = 0;
+      while (fread(&f,sizeof(f),1,input_file) != 0) {
+         if (f > fmax) fmax = f;
+         if (f < fmin) fmin = f;
+         count += 1;
+         }
+      }
+   else if (format == 'i') {
+      count = 0;
+      while (fread(&i,sizeof(i),1,input_file) != 0) {
+         if (i > fmax) fmax = i;
+         if (i < fmin) fmin = i;
+         count += 1;
+         }
+      }
+   printf("   %" PRIu64 " points, min %f, max %f\n",count,fmin,fmax);
+   printf("   nx ny nz: %d %d %d\n",nx,ny,nz);
+   rewind(input_file);
+   //
+   // set up color map
+   //
+   GIFcmap = MakeMapObject(256, NULL);
+   for (i = 0; i < 256; i++) {
+      GIFcmap->Colors[i].Red = i;
+      GIFcmap->Colors[i].Green = i;
+      GIFcmap->Colors[i].Blue = i;
+      }
+   //
+   // open GIF file
+   //
+   printf("write %s\n",argv[2]);
+   GIFfile = EGifOpenFileName(argv[2], 0);
+   EGifPutScreenDesc(GIFfile,dx,dy,8,0,GIFcmap);
+   unsigned char loop_count[] = {1,0,0};
+   EGifPutExtensionFirst(GIFfile, APPLICATION_EXT_FUNC_CODE, 11, "NETSCAPE2.0");
+   EGifPutExtensionLast(GIFfile, APPLICATION_EXT_FUNC_CODE, 3, loop_count);
+   unsigned char delay_count[5] = { 
+      0, // no transparency
+      0, // delay time
+      0, // delay time
+      0 // transparent index not used
+      };
+   //
+   // allocate image
+   //
+   image = malloc(dy*sizeof(int *));
+   for (yo = 0; yo < dy; ++yo) {
+      image[yo] = malloc(dx*sizeof(int));
+      for (xo = 0; xo < dx; ++xo)
+         image[yo][xo] = 0;
+      }
+   GIFline = malloc(dx*sizeof(GifPixelType));
+   //
+   // scan file
+   //
+   xi = yi = zi = 0;
+   for (zo = 0; zo < dz; ++zo) {
+      printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b   layer = %d",zo);
+      EGifPutExtension(GIFfile,GRAPHICS_EXT_FUNC_CODE,4,delay_count);
+      EGifPutImageDesc(GIFfile,0,0,dx,dy,0,NULL);
+      //
+      // read layer
+      //
+      for (yo = 0; yo < dy; ++yo) {
+         for (xo = 0; xo < dx; ++xo) {
+            if (format == 'f') {
+               read_voxel_f(input_file,&f,xo+x0,yo+y0,zo+z0,&xi,&yi,&zi,nx,ny,nz);
+               if (type == 'h') {
+                  h = 255*zo/(nz-1.0);
+                  if ((h > image[yo][xo]) && (f > arg))
+                     image[yo][xo] = h;
+                  GIFline[xo] = image[yo][xo]*(nz-1.0)/zo;
+                  }
+               else if (type == 's') {
+                  GIFline[xo] = 255*pow((f-fmin)/(fmax-fmin),arg);
+                  }
+               }
+            else if (format == 'i') {
+               read_voxel_i(input_file,&i,xo+x0,yo+y0,zo+z0,&xi,&yi,&zi,nx,ny,nz);
+               if (type == 'h') {
+                  h = 255*zo/(nz-1.0);
+                  if ((h > image[yo][xo]) && (i > arg))
+                     image[yo][xo] = h;
+                  GIFline[xo] = image[yo][xo]*(nz-1.0)/zo;
+                  }
+               else if (type == 's') {
+                  GIFline[xo] = 255*pow((i-fmin)/(fmax-fmin),arg);
+                  }
+               }
+            }
+         EGifPutLine(GIFfile,GIFline,dx);
+         }
+      }
+   printf("\n");
+   //
+   // put mm per pixel in comment
+   //
+   sprintf(comment,"mm per pixel: %f;",size);
+   EGifPutComment(GIFfile,comment);
+   //
+   // exit
+   //
+   fclose(input_file);
+   EGifCloseFile(GIFfile);
+   exit(0);
+   }
diff --git a/src/core/vol_stl.c b/src/core/vol_stl.c
new file mode 100644
index 0000000..c8a7b1b
--- /dev/null
+++ b/src/core/vol_stl.c
@@ -0,0 +1,667 @@
+//
+// vol_stl.c
+//    .vol to .stl
+//
+// Neil Gershenfeld 5/21/14
+// (c) Massachusetts Institute of Technology 2014
+//
+// This work may be reproduced, modified, distributed,
+// performed, and displayed for any purpose, but must
+// acknowledge the fab modules project. Copyright is
+// retained and must be preserved. The work is provided
+// as is; no warranty is provided, and users accept all 
+// liability.
+//
+
+#define MAX_LINE 10000
+
+#include "fab.h"
+
+/*
+vertices:
+   ---
+   6 7
+   4 5
+   ---
+   2 3
+   0 1
+   ---
+edges:
+   ---
+    k
+   l j
+    i
+   ---
+   h g
+   e f
+   ---
+    c
+   d b
+    a
+   ---
+*/
+
+//
+// rotate_x
+//   rotate rule around x and add
+//
+void rotate_x(char rules[255][20], int *index) {
+   int i,b[8];
+   int old_index = *index;
+   for (i = 0; i < 8; ++i) b[i] = (*index & (1 << i)) >> i;
+   *index = 
+      (b[4] << 0)
+     +(b[5] << 1)
+     +(b[0] << 2)
+     +(b[1] << 3)
+     +(b[6] << 4)
+     +(b[7] << 5)
+     +(b[2] << 6)
+     +(b[3] << 7);
+   for (i
+    = 0; i < strlen(rules[old_index]); ++i) {
+      switch (rules[old_index][i]) {
+         case 'a': rules[*index][i] = 'c'; break;
+         case 'b': rules[*index][i] = 'g'; break;
+         case 'c': rules[*index][i] = 'k'; break;
+         case 'd': rules[*index][i] = 'h'; break;
+         case 'e': rules[*index][i] = 'd'; break;
+         case 'f': rules[*index][i] = 'b'; break;
+         case 'g': rules[*index][i] = 'j'; break;
+         case 'h': rules[*index][i] = 'l'; break;
+         case 'i': rules[*index][i] = 'a'; break;
+         case 'j': rules[*index][i] = 'f'; break;
+         case 'k': rules[*index][i] = 'i'; break;
+         case 'l': rules[*index][i] = 'e'; break;
+         case ' ': rules[*index][i] = ' '; break;
+         }
+      }
+   }
+//
+// rotate_y
+//   rotate rule around y and add
+//
+void rotate_y(char rules[255][20], int *index) {
+   int i,b[8];
+   int old_index = *index;
+   for (i = 0; i < 8; ++i) b[i] = (*index & (1 << i)) >> i;
+   *index = 
+      (b[1] << 0)
+     +(b[5] << 1)
+     +(b[3] << 2)
+     +(b[7] << 3)
+     +(b[0] << 4)
+     +(b[4] << 5)
+     +(b[2] << 6)
+     +(b[6] << 7);
+   for (i = 0; i < strlen(rules[old_index]); ++i) {
+      switch (rules[old_index][i]) {
+         case 'a': rules[*index][i] = 'e'; break;
+         case 'b': rules[*index][i] = 'd'; break;
+         case 'c': rules[*index][i] = 'h'; break;
+         case 'd': rules[*index][i] = 'l'; break;
+         case 'e': rules[*index][i] = 'i'; break;
+         case 'f': rules[*index][i] = 'a'; break;
+         case 'g': rules[*index][i] = 'c'; break;
+         case 'h': rules[*index][i] = 'k'; break;
+         case 'i': rules[*index][i] = 'f'; break;
+         case 'j': rules[*index][i] = 'b'; break;
+         case 'k': rules[*index][i] = 'g'; break;
+         case 'l': rules[*index][i] = 'j'; break;
+         case ' ': rules[*index][i] = ' '; break;
+         }
+      }
+   }
+//
+// rotate_z
+//   rotate rule around z and add
+//
+void rotate_z(char rules[255][20], int *index) {
+   int i,b[8];
+   int old_index = *index;
+   for (i = 0; i < 8; ++i) b[i] = (*index & (1 << i)) >> i;
+   *index = 
+      (b[2] << 0)
+     +(b[0] << 1)
+     +(b[3] << 2)
+     +(b[1] << 3)
+     +(b[6] << 4)
+     +(b[4] << 5)
+     +(b[7] << 6)
+     +(b[5] << 7);
+   for (i = 0; i < strlen(rules[old_index]); ++i) {
+      switch (rules[old_index][i]) {
+         case 'a': rules[*index][i] = 'b'; break;
+         case 'b': rules[*index][i] = 'c'; break;
+         case 'c': rules[*index][i] = 'd'; break;
+         case 'd': rules[*index][i] = 'a'; break;
+         case 'e': rules[*index][i] = 'f'; break;
+         case 'f': rules[*index][i] = 'g'; break;
+         case 'g': rules[*index][i] = 'h'; break;
+         case 'h': rules[*index][i] = 'e'; break;
+         case 'i': rules[*index][i] = 'j'; break;
+         case 'j': rules[*index][i] = 'k'; break;
+         case 'k': rules[*index][i] = 'l'; break;
+         case 'l': rules[*index][i] = 'i'; break;
+         case ' ': rules[*index][i] = ' '; break;
+         }
+      }
+   }
+//
+// print_rules
+//    print the rule table
+//
+void print_rules(char rules[255][20]) {
+   int i,b;
+   printf("76543210\n");
+   for (i = 0; i < 256; ++i) {
+      for (b = 7; b >= 0; --b) {
+         printf("%d",(i & (1 << b)) >> b);
+         }
+      printf(" %d %s\n",i,rules[i]);
+      }
+   }
+// add_rules
+//    add a rule and its variants to the table
+//
+void add_rules(char rules[255][20], int index, char *edges) {
+   int i,j,k,l;
+   strcpy(rules[index],edges);
+   for (i=0; i<4; ++i) {
+      for (j=0; j<4; ++j) {
+         for (k=0; k<4; ++k) {
+            rotate_x(rules,&index);
+            }
+         rotate_y(rules,&index);
+         }
+      rotate_z(rules,&index);
+      }
+   }
+//
+// init_rules
+//    create the rule table
+//
+void init_rules(char rules[255][20]) {
+   int i,j;
+   for (i = 0; i < 256; ++i)
+      for (j = 0; j < 20; ++j)
+         rules[i][j] = 0;
+   add_rules(rules,0b00000000,""); // 0
+   add_rules(rules,0b11111111,""); // ~0
+   add_rules(rules,0b00000001,"eda"); // 1
+   add_rules(rules,0b11111110,"ade"); // ~1
+   add_rules(rules,0b00000011,"fed dbf"); // 2
+   add_rules(rules,0b11111100,"def fbd"); // ~2
+   add_rules(rules,0b00100001,"eda jif"); // 3
+   add_rules(rules,0b11011110,"ade fij"); // ~3
+   add_rules(rules,0b10000001,"eda gkj"); // 4
+   add_rules(rules,0b01111110,"ade jkg"); // ~4
+   add_rules(rules,0b00001110,"fhg fdh fad"); // 5
+   add_rules(rules,0b11110001,"ghf hdf daf"); // ~5
+   add_rules(rules,0b10000011,"fed fdb gkj"); // 6
+   add_rules(rules,0b01111100,"def bdf jkg"); // ~6
+   add_rules(rules,0b10010010,"bfa ile gkj"); // 7
+   add_rules(rules,0b01101101,"afb eli jkg"); // ~7
+   add_rules(rules,0b00001111,"ehg feg"); // 8
+   add_rules(rules,0b11110000,"ghe gef"); // ~8
+   add_rules(rules,0b01001101,"elk eka akg agb"); // 9
+   add_rules(rules,0b10110010,"kle ake gka bga"); // ~9
+   add_rules(rules,0b10011001,"ild ida ckj cjb"); // 10
+   add_rules(rules,0b01100110,"dli adi jkc bjc"); // ~10
+   add_rules(rules,0b10001101,"hkj hja hae ajb"); // 11
+   add_rules(rules,0b01110010,"jkh ajh eah bja"); // ~11
+   add_rules(rules,0b00011110,"ile hgf hfd dfa"); // 12
+   add_rules(rules,0b11100001,"eli fgh dfh afd"); // ~12
+   add_rules(rules,0b01101001,"eda bcg lkh jif"); // 13
+   add_rules(rules,0b10010110,"ade gcb hkl fij"); // ~13
+   add_rules(rules,0b01001110,"lkg lga lad agf"); // 14
+   add_rules(rules,0b10110001,"gkl agl dal fga"); // ~14
+   }
+//
+// vertex
+//    add a triangle vertex
+//
+void vertex(char c, int x, int y, int z, float voxel_size, float t, float *w, float *array) {
+   switch(c) {
+      case 'a':
+         array[0] = x+(w[0]-t)/(w[0]-w[1]);
+         array[1] = y;
+         array[2] = z;
+         break;
+      case 'b':
+         array[0] = x+1;
+         array[1] = y+(w[1]-t)/(w[1]-w[3]);
+         array[2] = z;
+         break;
+      case 'c':
+         array[0] = x+(w[2]-t)/(w[2]-w[3]);
+         array[1] = y+1;
+         array[2] = z;
+         break;
+      case 'd':
+         array[0] = x;
+         array[1] = y+(w[0]-t)/(w[0]-w[2]);
+         array[2] = z;
+         break;
+      case 'e':
+         array[0] = x;
+         array[1] = y;
+         array[2] = z+(w[0]-t)/(w[0]-w[4]);
+         break;
+      case 'f':
+         array[0] = x+1;
+         array[1] = y;
+         array[2] = z+(w[1]-t)/(w[1]-w[5]);
+         break;
+      case 'g':
+         array[0] = x+1;
+         array[1] = y+1;
+         array[2] = z+(w[3]-t)/(w[3]-w[7]);
+         break;
+      case 'h':
+         array[0] = x;
+         array[1] = y+1;
+         array[2] = z+(w[2]-t)/(w[2]-w[6]);
+         break;
+      case 'i':
+         array[0] = x+(w[4]-t)/(w[4]-w[5]);
+         array[1] = y;
+         array[2] = z+1;
+         break;
+      case 'j':
+         array[0] = x+1;
+         array[1] = y+(w[5]-t)/(w[5]-w[7]);
+         array[2] = z+1;
+         break;
+      case 'k':
+         array[0] = x+(w[6]-t)/(w[6]-w[7]);
+         array[1] = y+1;
+         array[2] = z+1;
+         break;
+      case 'l':
+         array[0] = x;
+         array[1] = y+(w[4]-t)/(w[4]-w[6]);
+         array[2] = z+1;
+         break;
+      }
+      array[0] = voxel_size*array[0];
+      array[1] = voxel_size*array[1];
+      array[2] = voxel_size*array[2];
+   }
+//
+// triangulate
+//    triangulate voxel at lattice site x,y,z with vertex weights w
+//
+void triangulate(int x, int y, int z, float voxel_size, float t, float *w, char rules[255][20], struct fab_vars *v) {
+   int i,index;
+   char c;
+   //
+   // set index code
+   //
+   index = ((w[0] < t) ? 0 : 1)
+         + ((w[1] < t) ? 0 : 2)
+         + ((w[2] < t) ? 0 : 4)
+         + ((w[3] < t) ? 0 : 8)
+         + ((w[4] < t) ? 0 : 16)
+         + ((w[5] < t) ? 0 : 32)
+         + ((w[6] < t) ? 0 : 64)
+         + ((w[7] < t) ? 0 : 128);
+   //
+   // add triangles for rule
+   //
+   i = 0;
+   while (1) {
+      //
+      // loop over rule chars
+      //
+      c = rules[index][i];
+      if (c == 0)
+         //
+         // end of rule
+         //
+         break;
+      else if (c == ' ') {
+         //
+         // space between rules
+         //
+         i += 1;
+         continue;
+         }
+      else {
+         //
+         // create triangle for rule
+         //
+         v->mesh->last = malloc(sizeof(struct fab_mesh_triangle_type));
+         v->mesh->last->previous = v->mesh->triangle;
+         v->mesh->triangle->next = v->mesh->last;
+         v->mesh->triangle = v->mesh->last;
+         v->mesh->triangle->next = 0;
+         v->mesh->triangle->attribute = 0;
+         v->mesh->triangle->normal[0] = v->mesh->triangle->normal[1] = v->mesh->triangle->normal[2] = 0;
+         //
+         // add vertices for rule
+         //
+         c = rules[index][i];
+         vertex(c,x,y,z,voxel_size,t,w,v->mesh->triangle->v0);
+         i += 1;
+         c = rules[index][i];
+         vertex(c,x,y,z,voxel_size,t,w,v->mesh->triangle->v1);
+         i += 1;
+         c = rules[index][i];
+         vertex(c,x,y,z,voxel_size,t,w,v->mesh->triangle->v2);
+         i += 1;
+         }
+      }
+   }
+
+void fab_write_stl(struct fab_vars *v, char *output_file_name) {
+   //
+   // write mesh as STL
+   //
+   FILE *output_file;
+   char buf[80];
+   uint32_t count;
+   //
+   // open output file
+   //
+   output_file = fopen(output_file_name, "wb");
+   if (output_file == 0) {
+      printf("fab.c: oops -- can't open %s\n",output_file_name);
+      exit(-1);
+      }
+   //
+   // write header
+   //
+   fwrite(buf,80,1,output_file);
+   //
+   // write count
+   //
+   count = 0;
+   v->mesh->triangle = v->mesh->first;
+   while (v->mesh->triangle->next != 0) {
+      v->mesh->triangle = v->mesh->triangle->next;
+      count += 1;
+      }
+   fwrite(&count,4,1,output_file);
+   //
+   // write triangles
+   //
+   v->mesh->triangle = v->mesh->first;
+   while (v->mesh->triangle->next != 0) {
+      v->mesh->triangle = v->mesh->triangle->next;
+      fwrite(v->mesh->triangle->normal,4,3,output_file);
+      fwrite(v->mesh->triangle->v0,4,3,output_file);
+      fwrite(v->mesh->triangle->v1,4,3,output_file);
+      fwrite(v->mesh->triangle->v2,4,3,output_file);
+      fwrite(&(v->mesh->triangle->attribute),2,1,output_file);
+      }
+   //
+   // return
+   //
+   printf("wrote %d triangles to %s\n",count,output_file_name);
+   fclose(output_file);
+   }
+
+float interp(int x,int y,int i,int j,int k,float **lower_array,float **upper_array,int p) {
+   //
+   // trilinear interpolation within a voxel
+   //
+   return (lower_array[y][x]*((p+1.0-i)/(p+1.0))*((p+1.0-j)/(p+1.0))*((p+1.0-k)/(p+1.0))
+  + lower_array[y][x+1]*((i)/(p+1.0))*((p+1.0-j)/(p+1.0))*((p+1.0-k)/(p+1.0))
+  + lower_array[y+1][x]*((p+1.0-i)/(p+1.0))*((j)/(p+1.0))*((p+1.0-k)/(p+1.0))
+  + lower_array[y+1][x+1]*((i)/(p+1.0))*((j)/(p+1.0))*((p+1.0-k)/(p+1.0))
+  + upper_array[y][x]*((p+1.0-i)/(p+1.0))*((p+1.0-j)/(p+1.0))*((k)/(p+1.0))
+  + upper_array[y][x+1]*((i)/(p+1.0))*((p+1.0-j)/(p+1.0))*((k)/(p+1.0))
+  + upper_array[y+1][x]*((p+1.0-i)/(p+1.0))*((j)/(p+1.0))*((k)/(p+1.0))
+  + upper_array[y+1][x+1]*((i)/(p+1.0))*((j)/(p+1.0))*((k)/(p+1.0)));
+  }
+
+int main(int argc, char **argv) {
+   //
+   // local vars
+   //
+   FILE *input_file;
+   uint64_t count;
+   uint16_t itype;
+   int x,y,z,i,j,k,n,p,imin,imax,nx,ny,nz;
+   int image_width,image_height,color_resolution,ret;
+   float w[8],**lower_array,**upper_array;
+   float threshold,voxel_size;
+   float ftype,fmin=1e10,fmax=-1e10;
+   char format,comment[256],rules[255][20];
+   struct fab_vars v;
+   init_vars(&v);
+   //
+   // command line args
+   //
+   if (!((argc == 6) || (argc == 7) || (argc == 8) || (argc == 9) || (argc == 10))) {
+      printf("command line: vol_stl in.vol out.stl nx ny nz [format [threshold [size [points [angle]]]]]\n");
+      printf("   in.vol = input VOL file\n");
+      printf("   out.stl = output STL file\n");
+      printf("   nx,ny,nz = x,y,z input voxel number\n");
+      printf("   format = 'f' for float 32, 'i' for uint16_t (default 'f')\n");
+      printf("   threshold: surface intensity threshold (0 = min, 1 = max, default 0.5))\n");
+      printf("   size = voxel size (mm, default from file))\n");
+      printf("   points = points to interpolate per point (default 0)\n");
+      printf("   to be implemented: angle = minimum relative face angle to decimate vertices (default 0)\n");
+      exit(-1);
+      }
+   format = 'f';
+   p = 0;
+   threshold = 0.5;
+   voxel_size = -1;
+   image_width = -1;
+   image_height = -1;
+   sscanf(argv[3],"%d",&nx);
+   sscanf(argv[4],"%d",&ny);
+   sscanf(argv[5],"%d",&nz);
+   if (argc >= 7) {
+      sscanf(argv[6],"%c",&format);
+      if (!((format == 'f') || (format == 'i'))) {
+         printf("vol_gif: oops -- format must be 'f' or 'i'\n");
+         exit(-1);
+         }
+      }
+   if (argc >= 8)
+      sscanf(argv[7],"%f",&threshold);
+   if (argc >= 9)
+      sscanf(argv[8],"%f",&voxel_size);
+   if (argc >= 10)
+      sscanf(argv[9],"%d",&p);
+   //
+   // initialize the rule table
+   //
+   init_rules(rules);
+   //
+   // check and find limits
+   //
+   input_file = fopen(argv[1],"rb");
+   if (input_file == NULL) {
+      printf("vol_stl: oops -- can not open %s\n",argv[1]);
+      exit(-1);
+      }
+   printf("read %s\n",argv[1]);
+   if (format == 'f') {
+      count = 0;
+      while (fread(&ftype,sizeof(ftype),1,input_file) != 0) {
+         if (ftype > fmax) fmax = ftype;
+         if (ftype < fmin) fmin = ftype;
+         count += 1;
+         }
+      }
+   else if (format == 'i') {
+      count = 0;
+      while (fread(&itype,sizeof(itype),1,input_file) != 0) {
+         if (itype > fmax) fmax = itype;
+         if (itype < fmin) fmin = itype;
+         count += 1;
+         }
+      }
+   if (nx*ny*nz != count) {
+      printf("vol_stl: oops -- file size doesn't match pixel number\n");
+      exit(-1);
+      }
+   printf("   %" PRIu64 " points, min %f, max %f\n",count,fmin,fmax);
+   printf("   nx ny nz: %d %d %d\n",nx,ny,nz);
+   rewind(input_file);
+   //
+   // scan the file 
+   //
+   printf("read %s\n",argv[1]);
+   //
+   // set threshold
+   //
+   threshold = fmin + threshold*(fmax-fmin);
+   //
+   // add empty border
+   //
+   image_width = nx+2;
+   image_height = ny+2;
+   //
+   // allocate arrays
+   //
+   lower_array = malloc(image_height*sizeof(float *));
+   for (y = 0; y < image_height; ++y) {
+      lower_array[y] = malloc(image_width*sizeof(float));
+      for (x = 0; x < image_width; ++x)
+         lower_array[y][x] = 0;
+      }
+   upper_array = malloc(image_height*sizeof(float *));
+   for (y = 0; y < image_height; ++y) {
+      upper_array[y] = malloc(image_width*sizeof(float));
+      for (x = 0; x < image_width; ++x)
+         upper_array[y][x] = 0;
+      }
+   //
+   // read the file
+   //
+   z = 0;
+   v.mesh = malloc(sizeof(struct fab_mesh_type));
+   v.mesh->triangle = malloc(sizeof(struct fab_mesh_triangle_type));
+   v.mesh->first = v.mesh->triangle;
+   v.mesh->last = v.mesh->triangle;
+   v.mesh->triangle->previous = v.mesh->triangle->next = 0;
+   for (z = 0; z < nz; ++z) {
+      //
+      // read layer
+      //
+      printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b   layer = %d",z);
+      for (y = 0; y < ny; ++y) {
+         for (x = 0; x < nx; ++x) {
+            lower_array[y+1][x+1] = upper_array[y+1][x+1];
+            if (format == 'f') {
+               fread(&ftype,sizeof(ftype),1,input_file);
+               upper_array[y+1][x+1] = ftype;
+               }
+            else if (format == 'i') {
+               fread(&itype,sizeof(itype),1,input_file);
+               upper_array[y+1][x+1] = itype;
+               }
+            }
+         }
+      if (p == 0) {
+         //
+         // no interpolation, loop over layer voxels
+         //
+         for (x = 0; x < (image_width-1); ++x) {
+            for (y = 0; y < (image_height-1); ++y) {
+               w[0] = lower_array[y][x];
+               w[1] = lower_array[y][x+1];
+               w[2] = lower_array[y+1][x];
+               w[3] = lower_array[y+1][x+1];
+               w[4] = upper_array[y][x];
+               w[5] = upper_array[y][x+1];
+               w[6] = upper_array[y+1][x];
+               w[7] = upper_array[y+1][x+1];
+               triangulate(x,y,z,voxel_size,threshold,w,rules,&v);
+               }
+            }
+         }
+      else {
+         //
+         // yes interpolation, loop over layer sub-voxels
+         //
+         for (x = 0; x < (image_width-1); ++x) {
+            for (y = 0; y < (image_height-1); ++y) {
+               for (i = 0; i <= p; ++i) {
+                  for (j = 0; j <= p; ++j) {
+                     for (k = 0; k <= p; ++k) {
+                        w[0] = interp(x,y,i,j,k,lower_array,upper_array,p);
+                        w[1] = interp(x,y,i+1,j,k,lower_array,upper_array,p);
+                        w[2] = interp(x,y,i,j+1,k,lower_array,upper_array,p);
+                        w[3] = interp(x,y,i+1,j+1,k,lower_array,upper_array,p);
+                        w[4] = interp(x,y,i,j,k+1,lower_array,upper_array,p);
+                        w[5] = interp(x,y,i+1,j,k+1,lower_array,upper_array,p);
+                        w[6] = interp(x,y,i,j+1,k+1,lower_array,upper_array,p);
+                        w[7] = interp(x,y,i+1,j+1,k+1,lower_array,upper_array,p);
+                        triangulate((1+p)*x+i,(1+p)*y+j,(1+p)*z+k,voxel_size,threshold,w,rules,&v);
+                        }
+                     }
+                  }
+               }
+            }
+         }
+      }
+   //
+   // add empty top layer
+   //
+   printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b   layer = %d",z);
+   for (y = 0; y < (image_height-2); ++y) {
+      for (x = 0; x < (image_width-2); ++x) {
+         lower_array[y+1][x+1] = upper_array[y+1][x+1];
+         upper_array[y+1][x+1] = 0;
+         }
+      }
+   if (p == 0) {
+      //
+      // no interpolation, loop over layer voxels
+      //
+      for (x = 0; x < (image_width-1); ++x) {
+         for (y = 0; y < (image_height-1); ++y) {
+            w[0] = lower_array[y][x];
+            w[1] = lower_array[y][x+1];
+            w[2] = lower_array[y+1][x];
+            w[3] = lower_array[y+1][x+1];
+            w[4] = upper_array[y][x];
+            w[5] = upper_array[y][x+1];
+            w[6] = upper_array[y+1][x];
+            w[7] = upper_array[y+1][x+1];
+            triangulate(x,y,z,voxel_size,threshold,w,rules,&v);
+            }
+         }
+      }
+   else {
+      //
+      // yes interpolation, loop over layer sub-voxels
+      //
+      for (x = 0; x < (image_width-1); ++x) {
+         for (y = 0; y < (image_height-1); ++y) {
+            for (i = 0; i <= p; ++i) {
+               for (j = 0; j <= p; ++j) {
+                  for (k = 0; k <= p; ++k) {
+                     w[0] = interp(x,y,i,j,k,lower_array,upper_array,p);
+                     w[1] = interp(x,y,i+1,j,k,lower_array,upper_array,p);
+                     w[2] = interp(x,y,i,j+1,k,lower_array,upper_array,p);
+                     w[3] = interp(x,y,i+1,j+1,k,lower_array,upper_array,p);
+                     w[4] = interp(x,y,i,j,k+1,lower_array,upper_array,p);
+                     w[5] = interp(x,y,i+1,j,k+1,lower_array,upper_array,p);
+                     w[6] = interp(x,y,i,j+1,k+1,lower_array,upper_array,p);
+                     w[7] = interp(x,y,i+1,j+1,k+1,lower_array,upper_array,p);
+                     triangulate((1+p)*x+i,(1+p)*y+j,(1+p)*z+k,voxel_size,threshold,w,rules,&v);
+                     }
+                  }
+               }
+            }
+         }
+      }
+   printf("\n");
+   //
+   // write STL
+   //
+   fab_write_stl(&v,argv[2]);
+   //
+   // exit
+   //
+   fclose(input_file);
+   exit(0);
+   }
diff --git a/src/guis/CMakeLists.txt b/src/guis/CMakeLists.txt
new file mode 100644
index 0000000..f6ab3c6
--- /dev/null
+++ b/src/guis/CMakeLists.txt
@@ -0,0 +1,26 @@
+cmake_minimum_required(VERSION 2.6)
+
+set(GUIs fab fab.html fabserver
+         make_cad_png make_cad_eps make_cad_stl make_cad_camm make_cad_rml
+         make_cad_epi make_cad_uni make_cad_sbp make_cad_g make_cad_ord
+         make_cad_grb make_cad_drl
+         make_math_camm make_math_epi make_math_g make_math_ord make_math_eps
+         make_math_uni make_math_rml make_math_sbp make_math_grb
+         make_math_drl
+         make_png_png make_png_eps make_png_epi make_png_uni make_png_grb
+         make_png_epi_halftone make_png_uni_halftone make_png_rml make_png_sbp
+         make_png_ord make_png_camm make_png_plt make_png_g make_png_drl
+         make_png_oms
+         make_stl_png make_stl_rml make_stl_sbp make_stl_g
+         make_svg_camm make_svg_epi make_svg_uni make_svg_oms
+         make_svg_g make_svg_rml make_svg_sbp make_svg_ord make_png_snap
+         make_cad_snap make_stl_snap make_svg_snap make_png_eps_halftone
+         make_cad_dxf make_math_dxf make_math_stl make_png_dxf
+         make_gif_stl
+         cad_ui
+         rml_send_gui
+    CACHE STRING "GUI script list")
+
+if( ${CMAKE_PROJECT_NAME} MATCHES fabmod )
+  install(PROGRAMS ${GUIs} DESTINATION ${PROJECT_SOURCE_DIR}/../bin)
+endif( ${CMAKE_PROJECT_NAME} MATCHES fabmod )
diff --git a/src/guis/cad_ui b/src/guis/cad_ui
new file mode 100755
index 0000000..1d55c8a
--- /dev/null
+++ b/src/guis/cad_ui
@@ -0,0 +1,1406 @@
+#!/usr/bin/env python
+
+import wx
+import wx.py
+import wx.stc
+
+import os, sys
+import subprocess
+import inspect
+import re
+
+from datetime import datetime
+
+SHOW_SCROLL = True
+
+# Special case to set up the search path if we're bundled into
+# an application (Mac OS X only)
+if '.app' in sys.argv[0]:
+    sys.path.append('')
+    APP_MODE = True
+    BUNDLED = True
+else:
+    APP_MODE = False
+    BUNDLED = False
+
+import cad_shapes
+import cad_text
+
+from Queue import Queue, Empty
+import threading
+
+full_image_size = 480
+small_image_size = 2 * full_image_size / 3
+
+image_size = full_image_size
+
+text_width = 480
+border = 3
+
+VERSION = '0.25'
+
+DARK_THEME = {
+'txt': [(wx.stc.STC_STYLE_DEFAULT,    '#000000', '#000000'),
+        (wx.stc.STC_STYLE_LINENUMBER, '#303030', '#c8c8c8'),
+        (wx.stc.STC_P_CHARACTER,      '#000000', '#ff73fd'),
+        (wx.stc.STC_P_CLASSNAME,      '#000000', '#96cbfe'),
+        (wx.stc.STC_P_COMMENTBLOCK,   '#000000', '#7f7f7f'),
+        (wx.stc.STC_P_COMMENTLINE,    '#000000', '#a8ff60'),
+        (wx.stc.STC_P_DEFAULT,        '#000000', '#ffffff'),
+        (wx.stc.STC_P_DEFNAME,        '#000000', '#96cbfe'),
+        (wx.stc.STC_P_IDENTIFIER,     '#000000', '#ffffff'),
+        (wx.stc.STC_P_NUMBER,         '#000000', '#ffffff'),
+        (wx.stc.STC_P_OPERATOR,       '#000000', '#ffffff'),
+        (wx.stc.STC_P_STRING,         '#000000', '#ff73fd'),
+        (wx.stc.STC_P_STRINGEOL,      '#000000', '#ffffff'),
+        (wx.stc.STC_P_TRIPLE,         '#000000', '#ff6c60'),
+        (wx.stc.STC_P_TRIPLEDOUBLE,   '#000000', '#96cbfe'),
+        (wx.stc.STC_P_WORD,           '#000000', '#b5dcff')],
+'background': '#252525',
+'foreground': '#c8c8c8'
+}
+
+LIGHT_THEME = {
+'txt': [(wx.stc.STC_STYLE_DEFAULT,    '#ffffff', '#ffffff'),
+        (wx.stc.STC_STYLE_LINENUMBER, '#c0c0c0', '#000000'),
+        (wx.stc.STC_P_CHARACTER,      '#ffffff', '#7f007f'),
+        (wx.stc.STC_P_CLASSNAME,      '#ffffff', '#0000ff'),
+        (wx.stc.STC_P_COMMENTBLOCK,   '#ffffff', '#7f7f7f'),
+        (wx.stc.STC_P_COMMENTLINE,    '#ffffff', '#007f00'),
+        (wx.stc.STC_P_DEFAULT,        '#ffffff', '#000000'),
+        (wx.stc.STC_P_DEFNAME,        '#ffffff', '#007f7f'),
+        (wx.stc.STC_P_IDENTIFIER,     '#ffffff', '#000000'),
+        (wx.stc.STC_P_NUMBER,         '#ffffff', '#000000'),
+        (wx.stc.STC_P_OPERATOR,       '#ffffff', '#000000'),
+        (wx.stc.STC_P_STRING,         '#ffffff', '#7f007f'),
+        (wx.stc.STC_P_STRINGEOL,      '#ffffff', '#000000'),
+        (wx.stc.STC_P_TRIPLE,         '#ffffff', '#7f0000'),
+        (wx.stc.STC_P_TRIPLEDOUBLE,   '#ffffff', '#000051'),
+        (wx.stc.STC_P_WORD,           '#ffffff', '#00007f')],
+'background': '#e0e0e0',
+'foreground': '#222222'
+}
+    
+def ApplyTheme(theme, txt, backgrounds, foregrounds):
+    for t in txt:
+        t.apply_theme(theme['txt'])
+    for b in backgrounds:
+        b(theme['background'])
+    for f in foregrounds:
+        f(theme['foreground'])
+    return theme
+
+class CadEditor(wx.py.editwindow.EditWindow):
+    def __init__(self, parent, size, style):
+        wx.py.editwindow.EditWindow.__init__(self, parent,
+                                             size=size, style=style)
+    
+    def apply_theme(self, theme):
+        for s in theme:
+            self.StyleSetBackground(s[0], s[1])
+            self.StyleSetForeground(s[0], s[2])
+
+################################################################################
+# LibraryFrame
+#   A simple read-only frame that displays the text from a given file.
+################################################################################
+class LibraryFrame(wx.Frame):
+    '''A simple text frame to display the contents of a standard libraries.'''
+    def __init__(self, title, filename, theme):
+        wx.Frame.__init__(self, None, title = title)
+
+        # Create text pane.
+        self.txt = CadEditor(self, size=(text_width, text_width),
+                                               style=wx.NO_BORDER)
+        if not SHOW_SCROLL:
+            self.txt.SetUseHorizontalScrollBar(False)
+            dummyScroll = wx.ScrollBar(self)
+            dummyScroll.Hide()
+            self.txt.SetVScrollBar(dummyScroll)
+        self.txt.SetCaretLineVisible(0)
+        
+        self.txt.ClearAll()
+        with open(filename.replace('pyc','py'), 'r') as f:
+            self.txt.SetText(f.read())
+        self.txt.SetSelection(0, 0)            
+        self.txt.SetReadOnly(True)
+        
+        ApplyTheme(theme, [self.txt], [self.SetBackgroundColour], [])
+
+        self.sizer = wx.BoxSizer(wx.VERTICAL)
+        self.sizer.Add(self.txt, 1, wx.EXPAND | wx.RIGHT | wx.TOP | wx.BOTTOM,
+                       border=border * 4)
+        self.SetSizerAndFit(self.sizer)
+        self.Show()
+
+################################################################################
+# Cadvars
+#   A class that represents a set of cad variables loaded from a .math file
+################################################################################
+class CadVars:
+   #
+   # cad variables
+   #
+   def __init__(self):
+      self.xmin = 0 # minimum x value to render
+      self.xmax = 0 # maximum x value to render
+      self.ymin = 0 # minimum y value to render
+      self.ymax = 0 # maximum y value to render
+      self.zmin = 0 # minimum z value to render
+      self.zmax = 0 # maximum z value to render
+      self.layers = [] # optional number of layers to render
+      self.function = '' # cad function
+      self.labels = [] # display labels
+      self.mm_per_unit = 1.0 # file units
+      self.type = '' # math string type
+
+################################################################################
+# ResolutionDialog
+#   A simple dialog box that lets the user set the resolution at which
+#   a cad file will be rendered to png.
+################################################################################
+class ResolutionDialog(wx.Dialog):
+    def __init__(self, parent, res):
+        wx.Dialog.__init__(self, parent = parent, title = 'Export')
+        self.value = wx.TextCtrl(self, -1, style=wx.TE_PROCESS_ENTER)
+        self.value.Bind(wx.EVT_TEXT, self.LimitToNumbers)
+        self.value.Bind(wx.EVT_TEXT_ENTER, self.Done)
+        self.value.ChangeValue(str(int(res)))
+        vbox = wx.BoxSizer(wx.VERTICAL)
+        
+        hbox = wx.BoxSizer(wx.HORIZONTAL)
+        hbox.Add(self.value, flag = wx.ALL, border=10)
+        okButton = wx.Button(self, label='OK')
+        okButton.Bind(wx.EVT_BUTTON, self.Done)
+        hbox.Add(okButton, flag = wx.ALL, border=10)
+        
+        vbox.Add(wx.StaticText(self, -1, 'Resolution (pixels/mm):'),
+                 flag = wx.LEFT | wx.TOP, border = 10)
+        vbox.Add(hbox)
+        
+        self.SetSizerAndFit(vbox)
+
+################################################################################
+
+    def LimitToNumbers(self, event):
+        value = self.value.GetValue()
+        i = self.value.GetInsertionPoint()
+        if value[i-1] in '0123456789.':
+            return
+        self.value.ChangeValue(value.replace(value[i-1],''))
+        self.value.SetInsertionPoint(i - 1)
+
+################################################################################
+
+    def Done(self, event):
+        self.EndModal(wx.ID_OK)
+        self.result = self.value.GetValue()
+        self.Destroy()
+
+##############################################################################
+
+class CadUIFrame(wx.Frame):
+    def __init__(self, Parent, title = 'cad_ui'):
+        wx.Frame.__init__(self, None, title = title)
+        
+        self.parent = Parent
+        self.backgrounds = [self.SetBackgroundColour]
+        self.foregrounds = []
+
+        imgpanel = self.CreateImagePanel()
+
+        self.CreateTextBox(textCallback      = Parent.OnText,
+                           isSavedCallback   = Parent.IsSaved,
+                           isUnsavedCallback = Parent.IsUnsaved)
+
+        self.CreateMenus(newCallback    = Parent.LoadTemplate,
+                         openCallback   = Parent.OnOpen,
+                         reloadCallback = Parent.ReloadFile,
+                         saveCallback   = Parent.OnSave,
+                         saveasCallback = Parent.OnSaveAs,
+                         exitCallback   = Parent.OnExit,
+                         renderCallback = Parent.Render,
+                         exportCallback = Parent.Export)
+                         
+        self.ArrangeGUI(self.txt, imgpanel)
+        self.ApplyTheme(DARK_THEME)
+        
+        self.Bind(wx.EVT_IDLE, lambda e: Parent.monitor_threads())
+
+################################################################################
+
+    def CreateTextBox(self, textCallback, isSavedCallback, isUnsavedCallback):
+        self.txt = CadEditor(self, size=(text_width, 0), style=wx.NO_BORDER)
+        # Set the margins on the text window:
+        #   2 with margin numbers
+        #   1 with symbols to indicate compile errors
+        #   3 just to add a bit of space
+        self.txt.SetMarginWidth(2, 16)
+        self.txt.SetMarginType(2,wx.stc.STC_MARGIN_NUMBER)
+        
+        self.txt.SetMarginWidth(1, 16)
+        self.txt.SetMarginType(1, wx.stc.STC_MARGIN_SYMBOL)
+        self.txt.MarkerDefine(0, wx.stc.STC_MARK_SHORTARROW, 'black','red')
+
+        self.txt.SetMarginWidth(3, 4)
+
+        self.txt.SetEOLMode(wx.stc.STC_EOL_LF)
+#        self.txt.SetViewEOL(True)
+
+        # Hide the scroll bars for visual cleanliness
+        if not SHOW_SCROLL:
+            self.txt.SetUseHorizontalScrollBar(False)
+            dummyScroll = wx.ScrollBar(self)
+            dummyScroll.Hide()
+            self.txt.SetVScrollBar(dummyScroll)
+
+        # Make the caret grey so that it works with both themes.
+        self.txt.SetCaretForeground('#888888')
+
+        # Bind callbacks to text events
+        self.txt.Bind(wx.stc.EVT_STC_CHANGE, textCallback)
+        self.txt.SetModEventMask(wx.stc.STC_MODEVENTMASKALL &                         
+                                ~wx.stc.STC_MOD_CHANGEMARKER &
+                                ~wx.stc.STC_MOD_CHANGESTYLE)
+        self.txt.Bind(wx.stc.EVT_STC_SAVEPOINTLEFT,    isUnsavedCallback)
+        self.txt.Bind(wx.stc.EVT_STC_SAVEPOINTREACHED, isSavedCallback)
+        
+        self.txt.Bind(wx.EVT_KEY_DOWN, self.HandleKey)
+        self.Bind(wx.EVT_KEY_DOWN, self.HandleKey)
+        self.txt.Bind(wx.EVT_KEY_UP, self.HandleKey)
+        self.txt.Bind(wx.EVT_KEY_UP, self.HandleKey)
+
+################################################################################
+
+    def CreateImagePanel(self):
+        # Create a nested panel to contain the image and a colorful border
+        centeringPanel = wx.Panel(self)
+        imgpanel = wx.Panel(centeringPanel)
+        
+        empty = wx.EmptyImage(image_size, image_size)
+        image = wx.StaticBitmap(imgpanel, -1, wx.BitmapFromImage(empty))
+        
+        imgsizer = wx.BoxSizer()
+        imgsizer.Add(image, 0, wx.ALL,
+                          border=border)
+        imgpanel.SetSizer(imgsizer)
+        
+        self.SetBorder = lambda color: [imgpanel.SetBackgroundColour(color),
+                                        imgpanel.Refresh()]
+
+        self.SetImage = lambda bitmap: [image.SetBitmap(bitmap),
+                                        self.Layout()]
+
+        # Create text pane.
+        self.outputTxt = CadEditor(centeringPanel, size=(image_size + border * 12,
+                                                         image_size/4),
+                                   style=wx.NO_BORDER)
+        if not SHOW_SCROLL:
+            self.outputTxt.SetUseHorizontalScrollBar(False)
+            dummyScroll = wx.ScrollBar(self)
+            dummyScroll.Hide()
+            self.outputTxt.SetVScrollBar(dummyScroll)
+        self.outputTxt.SetReadOnly(True)
+        self.outputTxt.SetCaretLineVisible(0)
+        self.outputTxt.Hide()
+        
+        # Create nested horizontal and vertical centering sizers
+        vcenter = wx.BoxSizer(wx.VERTICAL)
+        vcenter.Add((0,0), 1)
+        vcenter.Add(imgpanel, 0, wx.LEFT | wx.RIGHT | wx.CENTER, border = 5 * border)
+        statusText = wx.StaticText(centeringPanel)
+        vcenter.Add(statusText, 0, wx.LEFT, border = 5 * border)
+        vcenter.Add((0,0), 1)
+        vcenter.Add(self.outputTxt, 25, wx.TOP | wx.EXPAND, border = 5 * border)
+        
+        hcenter = wx.BoxSizer(wx.HORIZONTAL)
+        hcenter.Add((0,0), 1)
+        hcenter.Add(vcenter, 0, wx.CENTER | wx.EXPAND),
+        hcenter.Add((0,0), 1)
+        centeringPanel.SetSizer(hcenter)
+         
+        self.SetStatus = lambda h: statusText.SetLabel(h)
+
+        self.backgrounds += [imgpanel.SetBackgroundColour,
+		             centeringPanel.SetBackgroundColour]
+        self.foregrounds += [statusText.SetForegroundColour]
+        
+        return centeringPanel
+
+################################################################################
+
+    def CreateMenus(self,                  newCallback = None,
+                    openCallback   = None, reloadCallback = None,
+                    saveCallback   = None, saveasCallback = None,
+                    exitCallback   = None, renderCallback = None,
+                    exportCallback = None):
+                    
+        menu = wx.Menu()
+        
+        new = menu.Append(wx.ID_NEW, 'New\tCtrl+N', 'Start a new cad file')
+        self.Bind(wx.EVT_MENU, newCallback, new)
+        
+        about = menu.Append(wx.ID_ABOUT, 'About',
+                            'Display information about cad_ui')
+        self.Bind(wx.EVT_MENU, self.AboutBox, about)
+                              
+        open = menu.Append(wx.ID_OPEN, 'Open\tCtrl+O', "Open a cad file")
+        self.Bind(wx.EVT_MENU, openCallback, open)
+        
+        reload = menu.Append(-1, 'Reload\tCtrl+R','Reload the current file')
+        self.Bind(wx.EVT_MENU, reloadCallback, reload)
+        
+        save = menu.Append(wx.ID_SAVE, 'Save\tCtrl+S', "Save the current file")
+        self.Bind(wx.EVT_MENU, saveCallback, save)
+        
+        saveas = menu.Append(wx.ID_SAVEAS, 'Save As\tCtrl+Shift+S', "Save the current file")
+        self.Bind(wx.EVT_MENU, saveasCallback, saveas)
+        
+        exit = menu.Append(wx.ID_EXIT,'Exit\tCtrl+Q',"Terminate the program")
+        self.Bind(wx.EVT_MENU, exitCallback, exit)
+        self.Bind(wx.EVT_CLOSE, exitCallback)
+        
+        settingsMenu = wx.Menu()
+        self.Bind(wx.EVT_MENU, renderCallback,
+                  settingsMenu.Append(-1, 'Render\tCtrl+Enter',
+                  "Render the current .cad file to the preview pane")
+                  )
+        self.auto = settingsMenu.AppendCheckItem(-1, 'Auto-render',
+            "Re-render whenever the cad file changes")
+        self.Bind(wx.EVT_MENU, renderCallback, self.auto)
+        self.auto.Check(True)
+
+        # Menu to change view options
+        viewMenu = wx.Menu()
+        self.fullScreen = viewMenu.AppendCheckItem(-1, 'Full screen\tCtrl+Shift+F',
+            'Expand window and activate full screen mode.')
+        self.Bind(wx.EVT_MENU, lambda e: self.ShowFullScreen(e.Checked()),
+                  self.fullScreen)
+        output = viewMenu.AppendCheckItem(-1, 'Show errors\tCtrl+E',
+                                          'Show errors messages in a separate pane.')
+        self.Bind(wx.EVT_MENU, self.ShowOutput, output)
+        
+        themeMenu = wx.Menu()
+        viewMenu.AppendSubMenu(themeMenu, 'Theme')
+        
+        light = themeMenu.AppendRadioItem(-1, 'Light')
+        self.Bind(wx.EVT_MENU, lambda e: self.ApplyTheme(LIGHT_THEME), light)
+
+        dark = themeMenu.AppendRadioItem(-1, 'Dark')
+        self.Bind(wx.EVT_MENU, lambda e: self.ApplyTheme(DARK_THEME), dark)
+
+        # Default theme is dark
+        dark.Check(True)
+
+        libraryMenu = wx.Menu()
+        show_cad_shapes = lambda e: LibraryFrame('cad_shapes',
+                                                 cad_shapes.__file__,
+                                                 self.theme)
+        self.Bind(wx.EVT_MENU, show_cad_shapes,
+                  libraryMenu.Append(-1, 'cad_shapes','View the standard shapes library'))
+                  
+        show_cad_text = lambda e: LibraryFrame('cad_text',
+                                               cad_text.__file__,
+                                               self.theme)
+        self.Bind(wx.EVT_MENU, show_cad_text,
+                  libraryMenu.Append(-1, 'cad_text','View the standard text library'))
+
+        exportMenu = wx.Menu()
+        self.Bind(wx.EVT_MENU, lambda e: exportCallback('math'),
+                  exportMenu.Append(-1, '.math','Export to .math file'))
+        self.Bind(wx.EVT_MENU, lambda e: exportCallback('png'),
+                  exportMenu.Append(-1, '.png','Export to image file'))
+        self.Bind(wx.EVT_MENU, lambda e: exportCallback('svg'),
+                  exportMenu.Append(-1, '.svg','Export to svg file'))
+        self.Bind(wx.EVT_MENU, lambda e: exportCallback('stl'),
+                  exportMenu.Append(-1, '.stl','Export to stl file'))
+        self.Bind(wx.EVT_MENU, lambda e: exportCallback('dot'),
+                  exportMenu.Append(-1, '.dot','Export to dot / Graphviz file'))
+                  
+        menuBar = wx.MenuBar()
+        menuBar.Append(menu, 'File')
+        menuBar.Append(viewMenu, 'View')
+        menuBar.Append(settingsMenu, 'Options')
+        menuBar.Append(libraryMenu, 'Libraries')
+        menuBar.Append(exportMenu, 'Export')
+
+        self.Bind(wx.EVT_MENU_HIGHLIGHT, self.OnMenuHighlight)
+        self.Bind(wx.EVT_MENU_CLOSE, self.OnMenuClose)
+        self.Bind(wx.EVT_MENU_OPEN, self.OnMenuOpen)
+        
+        self.SetMenuBar(menuBar)
+        
+################################################################################
+
+    def ArrangeGUI(self, txt, img):
+        self.sizer = wx.FlexGridSizer(rows = 3, cols = 2)
+        self.sizer.AddGrowableCol(0, 1)
+        self.sizer.AddGrowableRow(1, 1)
+        
+        self.sizer.Add((0, 0)) # Add a dummy widget to the top left corner
+        
+        # Create and add version text
+        versionText = wx.StaticText(self, -1, 'cad_ui %s' % VERSION)
+        self.foregrounds += [versionText.SetForegroundColour]        
+        
+        self.sizer.Add(versionText, 0, wx.ALIGN_RIGHT)
+        self.sizer.Add(txt, 0, wx.EXPAND)
+        self.sizer.Add(img, 0, wx.EXPAND)
+        
+        # Create and add syntax hint
+        syntaxHint = wx.StaticText(self)
+        self.foregrounds += [syntaxHint.SetForegroundColour]
+        
+        self.sizer.Add(syntaxHint)
+        self.SetHint = lambda h: syntaxHint.SetLabel(h)
+        
+        # Fit everything into the sizer
+        self.SetSizerAndFit(self.sizer)
+
+################################################################################
+
+    def ApplyTheme(self, theme):
+        self.theme = ApplyTheme(theme, [self.txt, self.outputTxt],
+                                self.backgrounds, self.foregrounds)
+
+################################################################################
+
+    def HandleKey(self, e):
+        try:
+            control = wx.WXK_RAW_CONTROL
+        except:
+            control = wx.WXK_CONTROL
+
+        if e.GetKeyCode() == wx.WXK_ESCAPE and \
+           e.GetEventType() == wx.EVT_KEY_DOWN.typeId:
+            self.ShowFullScreen(False)
+            self.fullScreen.Check(False)
+            
+        # Highlighting is not available in the revised solver.
+#         elif e.GetKeyCode() == control and \
+#              e.GetEventType() == wx.EVT_KEY_DOWN.typeId and\
+#              self.parent.cad.type == "Boolean":
+#             self.parent.Render(highlight = True)
+#         elif e.GetKeyCode() == control and \
+#              e.GetEventType() == wx.EVT_KEY_UP.typeId and\
+#              self.parent.cad.type == "Highlight":
+#              self.parent.Render()         
+             
+        else:
+            if e.GetEventType() == wx.EVT_KEY_DOWN:
+                self.SetHint('')
+            e.Skip()
+###############################################################################
+    def ShowOutput(self, e):
+        global image_size
+        if type(e) is bool:
+            show = e
+        else:
+            show = e.Checked()
+        if e.Checked():
+            self.outputTxt.Show()
+            if image_size != small_image_size:
+                image_size = small_image_size
+                empty = wx.EmptyImage(image_size, image_size)
+                self.SetImage(wx.BitmapFromImage(empty))
+                self.parent.Render()
+        else:
+            self.outputTxt.Hide()
+            if image_size != full_image_size:
+                image_size = full_image_size
+                empty = wx.EmptyImage(image_size, image_size)
+                self.SetImage(wx.BitmapFromImage(empty))
+                self.parent.Render()
+
+        self.Layout()
+
+###############################################################################
+                                    
+    def AboutBox(self, e = None):
+        info = wx.AboutDialogInfo()
+        info.SetName("cad_ui")
+        if APP_MODE:
+            info.SetIcon(wx.Icon('cba_icon.png', wx.BITMAP_TYPE_PNG))
+        info.SetVersion(VERSION)
+        info.SetDescription('A design tool for .cad files.')
+        info.SetWebSite('http://kokompe.cba.mit.edu')
+        info.SetCopyright('(C) 2012 Matthew Keeter')
+        wx.AboutBox(info)
+
+###############################################################################
+
+    def OnMenuHighlight(self, event):
+        # Show how to get menu item info from this event handler
+        id = event.GetMenuId()
+        item = self.GetMenuBar().FindItemById(id)
+        if not item or not item.GetHelp():
+            self.SetHint('')
+        else:
+            self.SetHint(item.GetHelp())
+#        print "highlight"
+    
+    def OnMenuClose(self, event):
+#        print "close"
+        self.SetHint('')
+    
+    def OnMenuOpen(self, event):
+#        print "Open"
+        pass
+         
+###############################################################################
+
+    def SetText(self, text):
+        self.txt.ClearAll()
+        self.txt.SetText(text)
+        self.txt.SetSelection(0, 0)
+
+    def SetOutput(self, text):
+        self.outputTxt.SetReadOnly(False)
+        self.outputTxt.ClearAll()
+        self.outputTxt.SetText(text)
+        self.outputTxt.SetSelection(0, 0)
+        self.outputTxt.SetReadOnly(True)
+
+###############################################################################
+
+    def GetText(self):
+        return self.txt.GetText()
+
+###############################################################################
+
+    def BindTimer(self, callback, time = 10):
+        self.timer = wx.Timer(self, -1)
+        self.Bind(wx.EVT_TIMER, callback, self.timer)
+        self.timer.Start(time)
+
+###############################################################################
+
+    def MarkError(self, line):
+        self.txt.MarkerAdd(line, 0)
+    
+###############################################################################
+
+    def ClearError(self):
+        self.txt.MarkerDeleteAll(0)
+
+###############################################################################
+
+    def SyntaxHelper(self):
+        self.SetHint('')
+        
+        c = self.txt.GetCurLine()
+        before = c[0][:c[1] + 1]
+        if not before:
+            return
+        
+        # Try to grab a token being typed.
+        i = len(before) - 1        
+        while i >= 0 and before[i:].replace('_','a').isalnum():
+            i -= 1
+        token = before[i+1:]
+        
+        matches = []
+        for op in dir(cad_shapes):
+            if token and op.startswith(token) \
+                     and callable(eval("cad_shapes." + op)):
+                matches += [(op, 'cad_shapes.')]
+        for op in dir(cad_text):
+            if token and op.startswith(token) \
+                     and callable(eval("cad_text." + op)):
+                matches += [(op, 'cad_text.')]
+        
+        # Otherwise, try to get the name of the outermost function
+        if not matches:
+            openParens = len(before) - 1
+            depth = 0
+            while openParens >= 0:
+                if before[openParens] == '(':
+                    if depth == 0:
+                        break
+                    else:
+                        depth -= 1
+                elif before[openParens] == ')':
+                    depth += 1
+                openParens -= 1
+                
+            if openParens == -1:
+                return
+            before = before[:openParens]
+    
+            # Extract the name
+            i = len(before) - 1        
+            while i >= 0 and before[i:].replace('_','a').isalnum():
+                i -= 1
+            token = before[i+1:]
+
+            for op in dir(cad_shapes):
+                if token and op.startswith(token) \
+                         and callable(eval("cad_shapes." + op)):
+                    matches += [(op, 'cad_shapes.')]
+            for op in dir(cad_text):
+                if token and op.startswith(token) \
+                         and callable(eval("cad_text." + op)):
+                    matches += [(op, 'cad_text.')]
+                    
+            if not matches:
+                return
+
+        # Pick the match with the largest fraction of matching characters
+        best_score = 0
+        match = ''
+        print matches
+        for m in matches:
+            score = min(len(m), len(token)) / float(max(len(m), len(token)))
+            if  score > best_score:
+                best_score = score
+                match = m
+        try:
+            args = inspect.getargspec(eval(match[1] + match[0])).args
+            self.SetHint(match[0] + '(' + ', '.join(args) + ')')
+        except TypeError:
+            pass
+
+###############################################################################
+# CadUIApp
+#   The class that actually runs everything.
+###############################################################################
+
+class CadUIApp(wx.App):
+    def OnInit(self):
+        self.frame = CadUIFrame(self)
+        self.frame.Show()
+        self.image = wx.EmptyImage(image_size, image_size)
+        
+        self.threads = []
+        self.export = False
+        
+        self.saved = True
+        
+        # Either load a file from the first command-line argument, or open
+        # a template
+        if len(sys.argv) != 2:
+            self.LoadTemplate()
+        else:
+            try:
+                if len(sys.argv) == 2:
+                    if os.path.isabs(sys.argv[1]):
+                        filename = sys.argv[1]
+                    else:
+                        filename = os.path.join(os.getcwd(), sys.argv[1])
+                    print filename
+                    self.SetFilename(os.path.split(filename)[0],
+                                     os.path.split(filename)[1])
+                self.LoadFile()
+            except:
+                self.LoadTemplate()
+                self.SetFilename(os.path.split(filename)[0],
+                                 os.path.split(filename)[1])
+                self.OnSave()
+        
+        self.cad = CadVars()
+        self.resolution = 0
+        
+        return True
+    
+    def LoadTemplate(self, e = None):
+        if not self.WarnChanges():
+            return
+            
+        self.SetFilename('','')
+        
+        self.frame.SetText('''from cad_shapes import *
+
+# Render boundaries
+cad.xmin = -1
+cad.xmax = 1
+cad.ymin = -1
+cad.ymax = 1
+cad.mm_per_unit = 25.4 # inch units
+
+cad.function = circle(0, 0, 0.5)''')
+        self.frame.txt.SetSavePoint()
+        self.IsSaved()
+    
+    def IsSaved(self, e = None):
+        self.saved = True
+        self.UpdateTitle()
+    
+    def IsUnsaved(self, e = None):
+        self.saved = False
+        self.UpdateTitle()
+    
+    
+    def OnSave(self, e = None):
+        if self.cadFilePath:
+            with open(self.cadFilePath, 'w') as f:
+                f.write(self.frame.txt.GetText())
+            self.IsSaved()
+            self.frame.txt.SetSavePoint()
+        else:
+            self.OnSaveAs()
+        
+    def OnSaveAs(self, e = None):
+        directory = self.cadFileDir if self.cadFileDir else os.getcwd()
+        dlg = wx.FileDialog(self.frame, "Choose a file",
+                            directory, '', '*.*', wx.SAVE)
+        if dlg.ShowModal() == wx.ID_OK:
+            filename = dlg.GetFilename()
+            # Automatically append a .cad extension to the filename
+            if filename[-4:] != '.cad':
+                filename += '.cad'
+            self.SetFilename(dlg.GetDirectory(), filename)
+            self.OnSave()
+        dlg.Destroy()
+    
+    def Export(self, target):
+        # Pick png/svg/stl resolution
+        if target in ['png', 'svg', 'stl']:
+            dlg = ResolutionDialog(self.frame, self.resolution)
+            result = dlg.ShowModal()
+            dlg.Destroy()
+            if result == wx.ID_OK:
+                resolution = dlg.result
+            else:
+                return
+            
+        # Pick new filename
+        directory = self.cadFileDir if self.cadFileDir else os.getcwd()
+        dlg = wx.FileDialog(self.frame, '', directory,
+                            '', '*.*',wx.SAVE)
+        if dlg.ShowModal() == wx.ID_OK:
+            filename = dlg.GetFilename()
+            directory = dlg.GetDirectory()    
+            path = os.path.join(directory, filename)
+            
+            if target == 'math':
+                self.start_thread(self.export_math, (path,))
+            elif target == 'dot':
+                self.start_thread(self.export_dot, (path,))
+            elif target == 'png':
+                self.start_thread(self.export_png, (resolution,path))
+            elif target == 'svg':
+                self.start_thread(self.export_svg, (resolution,path))
+            elif target == 'stl':
+                self.start_thread(self.export_stl, (resolution,path))
+            
+        dlg.Destroy()
+    
+    def LoadImage(self, imageName):
+        '''Loads an image from a file and puts it into the UI.'''
+        self.image = wx.Image(imageName)
+        self.bitmap = wx.BitmapFromImage(self.image)
+            
+        self.frame.SetImage(self.bitmap)
+        
+    
+    def SetFilename(self, directory, name):
+        '''Given a directory and filename, set internal variables that describe
+           our output/input targets.  Also, clear state variables to mark that this
+           is the first time we've run this file.
+        '''
+        self.cadFileDir = directory
+        self.cadFilePath = os.path.join(self.cadFileDir, name)
+        
+        self.cad = CadVars()
+        
+    def OnOpen(self, e = None):
+        '''Callback for the open file dialog box.  Saves the name
+           of the opened file and generates .math and .png names.'''
+
+        if not self.WarnChanges():
+            return
+
+        directory = self.cadFileDir if self.cadFileDir else os.getcwd()
+        dlg = wx.FileDialog(self.frame, "Choose a file",
+                            directory, '', '*.*', wx.OPEN)
+        result = dlg.ShowModal()
+        dlg.Destroy()
+        if result != wx.ID_OK:
+            return
+        
+        self.SetFilename(dlg.GetDirectory(), dlg.GetFilename())
+        self.LoadFile()
+    
+    def ReloadFile(self, e = None):
+        if self.cadFilePath == '':
+            return
+            
+        if not self.WarnChanges():
+            return
+        
+        self.LoadFile(reset = False)
+    
+    def LoadFile(self, reset = True):
+        with open(self.cadFilePath, 'r') as f:
+            text = f.read()
+
+        if reset:
+            self.frame.auto.Check(True)
+        
+        self.frame.SetText(text)
+        self.frame.txt.SetSavePoint()
+
+        self.IsSaved()
+
+    def UpdateTitle(self):
+        if not self.cadFilePath:
+            s = 'cad_ui:  [Untitled]'
+        else:
+            s = 'cad_ui:  ' + self.cadFilePath
+
+        if self.saved:
+            self.frame.SetTitle(s)
+        else:
+            self.frame.SetTitle(s + '*')
+    
+    def OnText(self, event):
+        self.frame.SyntaxHelper()
+            
+        # If auto-render is enabled, then render the image
+        # (unless we're currently exporting something)
+        if self.frame.auto.IsChecked() and not self.export:
+            self.Render()
+        else:
+            self.frame.SetBorder((100, 100, 100))
+
+    def WarnChanges(self):
+        '''Check to see if the user is ok with abandoning unsaved changes.
+           Returns True if we should proceed.'''
+        if self.saved:
+            return True
+
+        dlg = wx.MessageDialog(None, "All unsaved changes will be lost.",
+                               "Warning:",
+                               wx.OK | wx.CANCEL | wx.ICON_EXCLAMATION)
+        result = dlg.ShowModal()
+        dlg.Destroy()
+        return result == wx.ID_OK
+   
+    def OnExit(self, event = None):
+        if event is None:
+            return
+        if self.WarnChanges():
+            self.frame.Destroy()
+
+    def GetCadVars(self):
+        self.cad = CadVars()
+
+        f = open('_cad_ui_tmp.math')
+        for line in f:
+            if 'format:' in line:
+                self.cad.type = line[8:]
+            elif 'mm per unit:' in line:
+                self.cad.mm_per_unit = float(line[12:])
+            elif 'dx dy dz:' in line:
+                [dx, dy, dz] = [float(v) for v in line[10:].split(' ')]
+            elif 'xmin ymin zmin:' in line:
+                [self.cad.xmin,
+                 self.cad.ymin,
+                 self.cad.zmin] = [float(v) for v in line[16:].split(' ')]
+
+        self.cad.xmax = self.cad.xmin + dx
+        self.cad.ymax = self.cad.ymin + dy
+        self.cad.zmax = self.cad.zmin + dz
+        f.close()
+   
+    def get_resolution(self, size = None):
+        '''Calculates the desired resolution based on the .math file.'''
+        if not size:
+            size = image_size
+        self.GetCadVars()
+
+        dx = self.cad.xmax - self.cad.xmin
+        dy = self.cad.ymax - self.cad.ymin
+        side = max(dx, dy)
+        self.resolution = size / (self.cad.mm_per_unit * side)
+        return self.resolution
+
+################################################################################
+    
+    def start_thread(self, callable, args=()):
+        e = threading.Event()
+        t = threading.Thread(target=callable, args=args, kwargs={'event':e})
+        t.daemon = True
+        t.start()
+        self.threads += [(t, e)]
+    
+    def monitor_threads(self):
+        ''' Monitor the list of active threads, joining those that are dead.
+        
+            This function runs in the wx IDLE callback, so it is continuously
+            checking in the background.
+        '''
+        dead_threads = filter(lambda (thread, event): not thread.is_alive(),
+                              self.threads)
+                              
+        for thread, event in dead_threads:
+            thread.join()
+        
+        self.threads = filter(lambda t: t not in dead_threads, self.threads)
+        
+    def stop_threads(self):
+        ''' Tells all threads to stop at their earliest convenience.
+        '''
+        for thread, event in self.threads:
+            event.set()
+        print ''
+################################################################################
+
+    def export_math(self, filename, event = threading.Event()):
+        '''Exports a .math file.
+        
+           Updates the UI in case of success.
+        '''
+        self.export = True
+        
+        print "####    Exporting .math file    ####"
+        wx.CallAfter(self.frame.ClearError)
+        wx.CallAfter(self.frame.SetBorder, (255, 165, 0))
+        wx.CallAfter(self.frame.SetOutput, "\n\tNo errors")
+        wx.CallAfter(self.frame.SetStatus,
+                     "Converting to math string (.math export)")
+        cm = self.cad_math(filename, event)
+
+        if cm is True: # Program succeeded
+            wx.CallAfter(self.frame.SetBorder, (0, 255, 0))
+            wx.CallAfter(self.frame.SetStatus,
+                         ".math export complete")
+        self.export = False
+
+################################################################################
+
+    def export_png(self, resolution, filename, event = threading.Event()):
+        '''Exports a .png file.
+        
+           Updates the UI in case of success.
+        '''
+        self.export = True
+        
+        print "####    Exporting .png file     ####"
+        wx.CallAfter(self.frame.ClearError)
+        wx.CallAfter(self.frame.SetBorder, (255, 165, 0))
+        wx.CallAfter(self.frame.SetOutput, "\n\tNo errors")
+        wx.CallAfter(self.frame.SetStatus,
+                     "Converting to math string (.png export)")
+        
+        # Save math file with default filename
+        if not self.cad_math(event=event):
+            self.export = False
+            return
+                
+        # Call math_png
+        mp = self.math_png(resolution, filename, event, incremental = False)
+        
+        if mp is True:
+            wx.CallAfter(self.frame.SetBorder, (0, 255, 0))
+            wx.CallAfter(self.frame.SetStatus,
+                         ".png export complete") 
+        self.export = False
+
+################################################################################
+
+    def export_stl(self, resolution, filename, event = threading.Event()):
+        '''Exports a .stl file.
+        
+           Updates the UI in case of success.
+        '''
+        self.export = True
+        
+        print "####    Exporting .stl file     ####"
+        wx.CallAfter(self.frame.ClearError)
+        wx.CallAfter(self.frame.SetBorder, (255, 165, 0))
+        wx.CallAfter(self.frame.SetOutput, "\n\tNo errors")
+        wx.CallAfter(self.frame.SetStatus,
+                     "Converting to math string (.stl export)")
+        
+        # Save math file with default filename
+        if not self.cad_math(event=event):
+            self.export = False
+            return
+                
+        # Call math_stl
+        mp = self.math_stl(resolution, filename, event)
+        
+        if mp is True:
+            wx.CallAfter(self.frame.SetBorder, (0, 255, 0))
+            wx.CallAfter(self.frame.SetStatus,
+                         ".stl export complete") 
+                         
+        self.export = False
+                         
+################################################################################
+
+    def export_svg(self, resolution, filename, event = threading.Event()):
+        '''Exports a .svg file.
+        
+           Updates the UI in case of success.
+        '''
+        self.export = True
+        
+        print "####    Exporting .svg file     ####"
+        wx.CallAfter(self.frame.ClearError)
+        wx.CallAfter(self.frame.SetBorder, (255, 165, 0))
+        wx.CallAfter(self.frame.SetOutput, "\n\tNo errors")
+        wx.CallAfter(self.frame.SetStatus,
+                     "Converting to math string (.svg export)")
+        
+        # Save math file with default filename
+        if not self.cad_math(event=event):
+            self.export = False
+            return
+                
+        # Call math_svg
+        mp = self.math_svg(resolution, filename, event)
+        
+        if mp is True:
+            wx.CallAfter(self.frame.SetBorder, (0, 255, 0))
+            wx.CallAfter(self.frame.SetStatus,
+                         ".svg export complete") 
+                         
+        self.export = False
+################################################################################
+
+    def export_dot(self, filename, event = threading.Event()):
+        '''Exports a .dot file.
+        
+           Updates the UI in case of success.
+        '''
+        self.export = True
+        
+        print "####    Exporting .dot file     ####"
+        wx.CallAfter(self.frame.ClearError)
+        wx.CallAfter(self.frame.SetBorder, (255, 165, 0))
+        wx.CallAfter(self.frame.SetOutput, "\n\tNo errors")
+        wx.CallAfter(self.frame.SetStatus,
+                     "Converting to math string (.dot export)")
+        
+        # Save math file with default filename
+        if not self.cad_math(event=event):
+            self.export = False
+            return
+                
+        # Call math_dot
+        mp = self.math_dot(filename, event)
+        
+        if mp is True:
+            wx.CallAfter(self.frame.SetBorder, (0, 255, 0))
+            wx.CallAfter(self.frame.SetStatus,
+                         ".dot export complete") 
+        self.export = False
+################################################################################
+
+    def render(self, event = threading.Event()):
+        ''' Renders an image and loads it in the display panel.
+        
+            This function should be called in an independent thread.
+        '''
+        now = datetime.now()
+        print "####       Rendering image      ####"
+        wx.CallAfter(self.frame.ClearError)
+        wx.CallAfter(self.frame.SetBorder, (255, 165, 0))
+        wx.CallAfter(self.frame.SetOutput, "\n\tNo errors")
+        wx.CallAfter(self.frame.SetStatus,
+                     "Converting to math string")
+        
+        # Save math file with default filename
+        if not self.cad_math(event=event):
+            return
+    
+        # Extract the resolution from the .math file
+        resolution = self.get_resolution()
+        
+        # Call math_png.  If it fails, then return early.
+        if self.math_png(resolution, event=event) is not True:
+            return
+            
+        wx.CallAfter(self.frame.SetBorder, (0, 255, 0))
+        wx.CallAfter(self.LoadImage, '_cad_ui_tmp.png')
+        wx.CallAfter(os.remove, '_cad_ui_tmp.png')
+        wx.CallAfter(self.frame.SetStatus, "")
+        print "Time taken: ", datetime.now() - now
+        
+################################################################################
+
+    def cad_math(self, filename = None, event = threading.Event()):
+        '''Runs cad_math.  This should be started in a separate thread.
+        
+           Returns None if interrupted, False if failed, True if success.
+           In case of failure, updates UI accordingly.
+           
+           Deletes the source .cad file on success.
+           
+        '''
+    
+        # Dump the contents of the text box into a .cad file.
+        with open('_cad_ui_tmp.cad', 'w') as cad_file:
+        
+            # To maintain backwards compatibility, we need to include
+            # this pair of libraries
+            cad_file.write('from string import *\nfrom math import *\n')
+            
+            # To allow for included files, we modify the python system path
+            if self.cadFileDir:
+                cad_file.write('import sys\nsys.path.append("%s")\n' %
+                               self.cadFileDir)
+        
+            # Check to see if we should abort before writing the file
+            if event.is_set(): return
+                                       
+            cad_file.write(self.frame.GetText())
+        
+        # If we are exporting with a particular file name, then the filename
+        # field should be populated; otherwise, use the default name.
+        if filename is None:
+            filename = '_cad_ui_tmp.math'
+        
+        if BUNDLED:
+            command = ['../MacOS/python', 'cad_math',
+                       '_cad_ui_tmp.cad',target]
+        else:
+            command = ['cad_math', '_cad_ui_tmp.cad', filename]
+        
+        # Last chance to abort before starting the subprocess
+        if event.is_set(): return
+        
+        # Start running the process
+        print '>>  ' + ' '.join(command)
+        process = subprocess.Popen(command, stderr = subprocess.PIPE)
+
+        # Wait for the process to terminate or for the stop event to be set    
+        while process.poll() is None:
+            if event.is_set():
+                process.terminate()
+                return
+    
+        # If something went wrong, try to parse the error messages
+        # and update the GUI accordingly
+        if process.returncode != 0:
+        
+            # Read the text of the error messages
+            errors = process.stderr.readlines()
+            errors = errors[0] + ''.join(errors[3:])
+            
+            # Go through the set of errors and modify line numbers to match
+            # the displayed file (since we may be adding lines to the beginning)
+            line_offset = (4 if self.cadFileDir else 2)
+            for m in re.findall(r'line (\d+)', errors):
+                error_line = int(m) - line_offset
+                errors = errors.replace('line %s' % m, 'line_ %i' % error_line)
+            errors = errors.replace('line_','line')
+            
+            # One more chance to abort before we update the GUI
+            if event.is_set(): return
+            
+            # Write out the errors in the text box
+            wx.CallAfter(self.frame.SetOutput, errors)
+            
+            # Make the image border red
+            wx.CallAfter(self.frame.SetBorder, (255, 0, 0))
+            
+            # Update the status line
+            try:
+                wx.CallAfter(self.frame.MarkError, error_line - 1)
+                wx.CallAfter(self.frame.SetStatus,
+                             "cad_math failed (line %i)" % error_line)
+            except NameError:
+                wx.CallAfter(self.frame.SetStatus, "cad_math failed")
+                
+            return False
+        
+        # If everything worked, then we return delete the temporary file
+        # and return True   
+        os.remove('_cad_ui_tmp.cad')
+        return True
+
+################################################################################
+
+    def math_xyz(self, program, filename, resolution=None,
+                 event=threading.Event(), args = [],
+                 monitor = None):
+        
+        ''' Generic function to run math_png, math_svg, math_stl, etc.
+        
+        '''
+        
+        if BUNDLED:
+            xyz_path = './%s' % program
+        else:
+            xyz_path = program
+        
+        if monitor is None:
+            monitor = self.progress_bar
+            minotaur = "RAWR!"
+        
+        if event.is_set(): return
+        
+        command = [xyz_path] + args + ['_cad_ui_tmp.math', filename]
+        if resolution: command += [str(resolution)]
+        
+        print '>>  ' + ' '.join(command)
+        process = subprocess.Popen(command, stdout=subprocess.PIPE,
+                                   stderr=subprocess.PIPE)
+        
+        monitor(process, event)
+        success = (process.returncode == 0)
+        errors = process.stderr.read()
+        
+        # One more chance to abort before updating the GUI
+        if event.is_set(): return
+                
+        if not success:
+            wx.CallAfter(self.frame.SetOutput, errors)
+            wx.CallAfter(self.frame.SetBorder, (255, 0, 0))
+            wx.CallAfter(self.frame.SetStatus, "%s failed" % program)
+            return False
+        
+        # If everything worked, then we delete the temporary file
+        # and return True
+        os.remove('_cad_ui_tmp.math')
+        return True
+    
+################################################################################
+
+    def math_png(self, resolution, filename = None, event = threading.Event(),
+                 incremental=None):
+    
+        # If we aren't saving to a particular file, use the default
+        if filename is None:
+            filename = '_cad_ui_tmp.png'
+            
+        if incremental is None and not 'Linux' in os.uname():
+            incremental = not(self.cad.zmax - self.cad.zmin > 0)
+        
+        return self.math_xyz('math_png', filename, resolution=resolution,
+                             event=event,
+                             args = ['--incremental'] if incremental else [],
+                             monitor = self.incremental)
+
+################################################################################
+    
+    def math_stl(self, resolution, filename, event = threading.Event()):
+    
+        return self.math_xyz('math_stl', filename,
+                             resolution=resolution, event=event)
+        
+################################################################################
+    
+    def math_svg(self, resolution, filename, event = threading.Event()):
+    
+        return self.math_xyz('math_svg', filename,
+                             resolution=resolution, event=event)
+
+################################################################################
+    
+    def math_dot(self, filename, event = threading.Event()):
+    
+        return self.math_xyz('math_dot', filename, event=event)
+   
+################################################################################
+
+    def progress_bar(self, process, event):
+        ''' Waits for a process to finish, drawing a progress bar.
+        
+            Halts when the progress finishes or the event is set.
+            
+        '''
+        
+        line = ''
+        while process.poll() is None:
+            if event.is_set():
+                process.terminate()
+                return
+                
+            c = process.stdout.read(1)
+            if c == '\n' or c == '\r':
+                print repr(line)
+                if '[|' in line:
+                    line = line[4:]
+                    percent = (line.count('|') * 100) / (len(line) - 2)
+                    if percent < 100:
+                        wx.CallAfter(self.frame.SetStatus,
+                                     "Rendering (%i%%)" % percent)
+                    else:
+                        wx.CallAfter(self.frame.SetStatus,
+                                     "Writing output file")
+                else:
+                    print line
+                line = ''
+            else:
+                line = line + c
+
+################################################################################
+
+    def incremental(self, process, event):
+        ''' Waits for a process to finish, drawing a progress bar and
+            incremental render blobs.
+        
+            Halts when the progress finishes or the event is set.
+            
+        '''
+        
+        try:
+            dc = wx.MemoryDC(self.bitmap)
+            dc.SetPen(wx.TRANSPARENT_PEN)
+        except:
+            dc = None
+        
+        Y_MAX = (self.cad.ymax - self.cad.ymin) * self.cad.mm_per_unit*self.resolution
+
+        line = ''
+
+        if 'Linux' in os.uname():
+            regex = None
+        else:
+            regex = re.compile(
+                r'([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+) (.)'
+            )
+        
+        while process.poll() is None:
+            if event.is_set():
+                process.terminate()
+                return
+                
+            c = process.stdout.read(1)
+            if c == '\n' or c == '\r':
+                if '[|' in line:
+                    line = line[4:]
+                    percent = (line.count('|') * 100) / (len(line) - 2)
+                    if percent < 100:
+                        wx.CallAfter(self.frame.SetStatus,
+                                     "Rendering (%i%%)" % percent)
+                    else:
+                        wx.CallAfter(self.frame.SetStatus,
+                                     "Writing output file")
+                elif regex and regex.match(line):
+                    xmin, xmax, ymin, ymax, color = regex.match(line).groups()
+                    xmin, xmax, ymin, ymax = map(int, (xmin, xmax, ymin, ymax))
+                    color = ord(color)
+                    if dc:
+                        dc.SetBrush(wx.Brush(wx.Colour(color, color, color)))
+                        dc.DrawRectangle(xmin, Y_MAX - ymax,
+                                         xmax-xmin, ymax-ymin)
+                        wx.CallAfter(self.frame.SetImage, self.bitmap)
+                else:
+                    print line
+                line = ''
+            else:
+                line = line + c
+   
+################################################################################
+    
+    def Render(self, e = None):
+        '''Attempt to render the .cad file into an image.'''
+
+        # If this is an event callback, make sure that the box is checked.
+        if e and not e.Checked():
+            return
+        
+        # Tell all of the existing threads to stop (politely)
+        self.stop_threads()
+        
+        # Start up a new thread to render and load the image.
+        self.start_thread(self.render)
+        
+wx.Log.EnableLogging(False)
+app = CadUIApp()
+app.MainLoop()
diff --git a/src/guis/fab b/src/guis/fab
new file mode 100755
index 0000000..6b7e8c4
--- /dev/null
+++ b/src/guis/fab
@@ -0,0 +1,170 @@
+#!/usr/bin/env python
+#
+# fab
+#    GUI wrapper wrapper
+#
+# Neil Gershenfeld
+# CBA MIT
+date = "10/4/13"
+#
+# (c) Massachusetts Institute of Technology 2012
+# Permission granted for experimental and personal use;
+# license for commercial sale available from MIT.
+#
+# imports
+#
+import wx, sys, os, os.path
+from fab_mods import set_workflows
+#
+# defaults
+#
+input_file = '""'
+size = '400'
+#
+# command line
+#
+print "command line: fab [input_file [size]]"
+print "   input_file = input file (optional)"
+print "   size = image panel size (optional)"
+#
+# start wx
+#
+app = wx.App()
+#
+# set up frame
+#
+frame = wx.Frame(None, -1, 'fab')
+frame_sizer = wx.GridBagSizer(10,10)
+frame.SetSizer(frame_sizer)
+bold_font = wx.Font(10,wx.DEFAULT,wx.NORMAL,wx.BOLD)
+#
+# quit routine
+#
+def quit(event):
+   sys.exit()
+#
+# labels
+#
+format_text = wx.StaticText(frame,label="from format:")
+format_text.SetFont(bold_font)
+frame_sizer.Add(format_text,(0,0),flag=(wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER_HORIZONTAL))
+#
+process_text = wx.StaticText(frame,label="to process:")
+process_text.SetFont(bold_font)
+frame_sizer.Add(process_text,(0,1),flag=(wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER_HORIZONTAL))
+#
+program_text = wx.StaticText(frame,label="with program:")
+program_text.SetFont(bold_font)
+frame_sizer.Add(program_text,(0,2),flag=(wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER_HORIZONTAL))
+#
+# menu event handler
+#
+def handler(event):
+   selected_format = frame.formats.GetValue()
+   selected_process = frame.processes.GetValue()
+   if ((selected_format == "format") | (selected_process == "process")):
+      return
+   key = selected_format + ' : ' + selected_process
+   if workflows.has_key(key):
+      frame.program = workflows[key]
+      frame.programs.SetLabel(frame.program)
+      frame.Layout()
+      frame.Fit()
+   else:
+      frame.program = ""
+      frame.programs.SetLabel("not defined")
+      frame.Layout()
+      frame.Fit()
+#
+# program call
+#
+frame.program = ""
+def call_program(event):
+   if (frame.program != ""):
+      command = frame.program + ' ' + input_file + ' ' + size_control.GetValue() + '&'
+      print command
+      os.system(command)
+#
+# menus
+#
+frame.formats = wx.ComboBox(frame,value='format',style=wx.CB_READONLY)
+frame.formats.Bind(wx.EVT_COMBOBOX,handler)
+frame_sizer.Add(frame.formats,(1,0))
+#
+frame.processes = wx.ComboBox(frame,value='process',style=wx.CB_READONLY)
+frame.processes.Bind(wx.EVT_COMBOBOX,handler)
+frame_sizer.Add(frame.processes,(1,1))
+#
+frame.programs = wx.Button(frame,label='program')
+frame.programs.Bind(wx.EVT_BUTTON,call_program)
+frame_sizer.Add(frame.programs,(1,2),flag=(wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER_HORIZONTAL))
+#
+# set workflows
+#
+workflows = {}
+formats = []
+set_workflows(frame,formats,workflows)
+#
+# controls
+#
+control_panel = wx.Panel(frame)
+control_sizer = wx.GridBagSizer(10,10)
+control_panel.SetSizer(control_sizer)
+#
+control_sizer.Add(wx.StaticText(control_panel,label=' GUI size (pixels):'),(0,0),flag=(wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL))
+#
+size_control = wx.TextCtrl(control_panel,-1,size)
+control_sizer.Add(size_control,(0,1),flag=(wx.ALIGN_LEFT))
+#
+control_sizer.Add(wx.StaticText(control_panel,label="fab modules version: "+date),(0,2),flag=(wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL))
+#
+d = 4 
+w = 1 
+l = 6*d+2*w
+def logo_paint(event):
+   dc = wx.PaintDC(logo_panel)
+   dc.SetBrush(wx.Brush('white'))
+   dc.SetPen(wx.Pen('white', 0))
+   dc.DrawRectangleRect((0, 0, l, l))
+   dc.SetBrush(wx.Brush('red'))
+   dc.DrawCircle(d,d,d)
+   dc.DrawCircle(3*d+w,3*d+w,d)
+   dc.SetBrush(wx.Brush('blue'))
+   dc.DrawRectangleRect((2*d+w,0,2*d,2*d))
+   dc.DrawRectangleRect((4*d+2*w,0,2*d,2*d))
+   dc.DrawRectangleRect((0,2*d+w,2*d,2*d))
+   dc.DrawRectangleRect((4*d+2*w,2*d+w,2*d,2*d))
+   dc.DrawRectangleRect((0,4*d+2*w,2*d,2*d))
+   dc.DrawRectangleRect((2*d+w,4*d+2*w,2*d,2*d))
+   dc.DrawRectangleRect((4*d+2*w,4*d+2*w,2*d,2*d))
+logo_panel = wx.Panel(control_panel,size=(l,l))
+logo_panel.Bind(wx.EVT_PAINT,logo_paint)
+control_sizer.Add(logo_panel,(0,3))
+#
+control_quit = wx.Button(control_panel,label='quit')
+control_quit.Bind(wx.EVT_BUTTON,quit)
+control_sizer.Add(control_quit,(0,4))
+#
+control_sizer.Add(wx.StaticText(control_panel,label=" "),(0,5),flag=(wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL))
+#
+control_panel.Fit()
+frame_sizer.Add(control_panel,(2,0),span=(1,3),flag=wx.ALIGN_CENTER_HORIZONTAL)
+#
+# fit and show frame
+#
+frame.Fit()
+frame.Show()
+#
+# process command line
+#
+if (len(sys.argv) > 1):
+   input_file = sys.argv[1]
+   ext = os.path.splitext(input_file)[1]
+   if (formats.count(ext) != 0):
+      frame.formats.SetSelection(formats.index(ext))
+if (len(sys.argv) > 2):
+   size = sys.argv[2]
+#
+# start mainloop
+#
+app.MainLoop()
diff --git a/src/guis/fab.html b/src/guis/fab.html
new file mode 100644
index 0000000..ded0ac8
--- /dev/null
+++ b/src/guis/fab.html
@@ -0,0 +1,6539 @@
+
+
+
+
+   
+      
+         
+      
+   
+   
+   
+   
+   
+
+
+
+ + + diff --git a/src/guis/fabserver b/src/guis/fabserver new file mode 100755 index 0000000..4832a24 --- /dev/null +++ b/src/guis/fabserver @@ -0,0 +1,369 @@ +#!/usr/bin/env python +# +# fabserver.py +# command line syntax: python fabserver.py port +# +# Neil Gershenfeld +# CBA MIT 12/6/12 +# +# (c) Massachusetts Institute of Technology 2012 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# + +from socket import * +from string import * +from select import * +from math import * +import sys +import subprocess +import os +import glob + +MAX_PACKET = 4096 +MAX_RESPONSE = 10000000 +PACKET_TIMEOUT = 0.01 + +SERVER_ADDRESS = "127.0.0.1" + +def send_image(msg): + client_socket.sendall( + 'HTTP/1.1 200 OK\n' + +'Content-Length: %d\n'%len(msg) + +'Content-Type: image/jpeg\n' + +'\n' + +msg + ) + +def send_text(text): + client_socket.sendall( + 'HTTP/1.1 200 OK\n' + +'Content-Type: text/html\n' + +'\n' + +'\n' + +text + ) + +def parse(): + global mode, units, dx, dy, dz, xmin, ymin, zmin, xrot, yrot, zrot, resolution, slices, expression + # + # parse math string + # + start = 0 + start = 1+find(response,"{",start) + end = find(response,"}",start) + mode = response[start:end] + start = 1+find(response,"{",end) + end = find(response,"}",start) + units = response[start:end] + start = 1+find(response,"{",end) + end = find(response,"}",start) + dx = response[start:end] + start = 1+find(response,"{",end) + end = find(response,"}",start) + dy = response[start:end] + start = 1+find(response,"{",end) + end = find(response,"}",start) + dz = response[start:end] + start = 1+find(response,"{",end) + end = find(response,"}",start) + xmin = response[start:end] + start = 1+find(response,"{",end) + end = find(response,"}",start) + ymin = response[start:end] + start = 1+find(response,"{",end) + end = find(response,"}",start) + zmin = response[start:end] + start = 1+find(response,"{",end) + end = find(response,"}",start) + xrot = float(response[start:end]) + start = 1+find(response,"{",end) + end = find(response,"}",start) + yrot = float(response[start:end]) + start = 1+find(response,"{",end) + end = find(response,"}",start) + zrot = float(response[start:end]) + start = 1+find(response,"{",end) + end = find(response,"}",start) + resolution = response[start:end] + start = 1+find(response,"{",end) + end = find(response,"}",start) + slices = response[start:end] + start = 1+find(response,"{",end) + end = find(response,"}",start) + expression = response[start:end] + +def render(): + # + # render math string, xy view + # + math_file = open("fab_render_xy.math","w") + math_file.write("format: "+mode+"\n") + math_file.write("mm per unit: "+units+"\n") + math_file.write("dx dy dz: "+dx+" "+dy+" "+dz+"\n") + math_file.write("xmin ymin zmin: "+xmin+" "+ymin+" "+zmin+"\n") + math_file.write("expression: "+expression+"\n") + math_file.close() + os.system("math_png fab_render_xy.math fab_render_xy.png "+resolution+" "+slices) + os.system("rm fab_render_xy.math") + if (float(dz) != 0): + # + # xz view + # + math_file = open("fab_render_xz.math","w") + math_file.write("format: "+mode+"\n") + math_file.write("mm per unit: "+units+"\n") + math_file.write("dx dy dz: "+dx+" "+dz+" "+dy+"\n") + math_file.write("xmin ymin zmin: "+xmin+" "+zmin+" "+ymin+"\n") + math_file.write("expression: "+expression+"\n") + new_expression = replace(expression,'Y','#') + new_expression = replace(new_expression,'Z','Y') + new_expression = replace(new_expression,'#','Z') + math_file.write("expression: "+new_expression+"\n") + math_file.close() + os.system("math_png fab_render_xz.math fab_render_xz.png "+resolution+" "+slices) + os.system("rm fab_render_xz.math") + # + # zy view + # + math_file = open("fab_render_zy.math","w") + math_file.write("format: "+mode+"\n") + math_file.write("mm per unit: "+units+"\n") + math_file.write("dx dy dz: "+dz+" "+dy+" "+dx+"\n") + math_file.write("xmin ymin zmin: "+zmin+" "+ymin+" "+xmin+"\n") + new_expression = replace(expression,'X','#') + new_expression = replace(new_expression,'Z','((%f)-X)'%(float(dz)+2*float(zmin))) + new_expression = replace(new_expression,'#','Z') + math_file.write("expression: "+new_expression+"\n") + math_file.close() + os.system("math_png fab_render_zy.math fab_render_zy.png "+resolution+" "+slices) + os.system("rm fab_render_zy.math") + # + # xyz view + # + math_file = open("fab_render_xyz.math","w") + math_file.write("format: "+mode+"\n") + math_file.write("mm per unit: "+units+"\n") + math_file.write("dx dy dz: "+"2"+" "+"2"+" "+"2"+"\n") + math_file.write("xmin ymin zmin: "+"-1"+" "+"-1"+" "+"-1"+"\n") + # + # view scaling + # + -1 + 1 + new_expression = replace(expression,'X','(('+xmin+')+'+dx+'*(X+1)/2)') + new_expression = replace(new_expression,'Y','(('+ymin+')+'+dy+'*(Y+1)/2)') + new_expression = replace(new_expression,'Z','(('+zmin+')+'+dz+'*(Z+1)/2)') + #new_expression = replace(expression,'X','(X+('+xmin+')+1)') + #new_expression = replace(new_expression,'Y','(Y+('+ymin+')+1)') + #new_expression = replace(new_expression,'Z','(Z+('+zmin+')+1)') + # + # z rotation + # + new_expression = replace(new_expression,'X','(('+str(cos(zrot))+')*X+('+str(-sin(zrot))+')*ytemp)') + new_expression = replace(new_expression,'Y','(('+str(sin(zrot))+')*X+('+str(cos(zrot))+')*Y)') + new_expression = replace(new_expression,'ytemp','Y') + # + # y rotation (not used in GUI) + # + # + # x rotation + # + new_expression = replace(new_expression,'Y','(('+str(cos(xrot))+')*Y+('+str(sin(xrot))+')*ztemp)') + new_expression = replace(new_expression,'Z','(('+str(-sin(xrot))+')*Y+('+str(cos(xrot))+')*Z)') + new_expression = replace(new_expression,'ztemp','Z') + # + # write and evaluate + # + math_file.write("expression: "+new_expression+"\n") + math_file.close() + os.system("math_png fab_render_xyz.math fab_render_xyz.png "+resolution+" "+slices) + os.system("rm fab_render_xyz.math") + #send_text("render") + +# +# get command line arguments +# +if (len(sys.argv) != 2): + print "command line syntax: python fabserver.py port" + sys.exit() +server_port = sys.argv[1] +# +# start listening on server port +# +try: + server_socket = socket(AF_INET, SOCK_STREAM) + server_socket.bind((SERVER_ADDRESS,int(server_port))) + server_socket.listen(5) + print "listening on port "+server_port +except: + print "error: couldn't open socket" + sys.exit() + +# +# start main loop +# +while 1: + # + # blocking select call to wait for a connection + # + [read_ready, write_ready, error_ready] = select([server_socket],[],[]) + if (read_ready != []): + # + # child process, non-blocking select call to receive packet + # + (client_socket, client_address) = server_socket.accept() + response = "" + while 1: + [read_ready, write_ready, error_ready] = select([client_socket],[],[],PACKET_TIMEOUT) + if (read_ready != []): + response += client_socket.recv(MAX_PACKET) + else: + break + # + # process response + # + #print response + if (find(response,"GET / ") == 0): + # + # root request, send fab.html + # + file = open(os.path.join(os.path.dirname(sys.argv[0]), 'fab.html'), + 'rb') + msg = file.read() + file.close() + send_text(msg) + elif (find(response,"GET /fab_render_xy.png") == 0): + try: + file = open('fab_render_xy.png','rb') + os.system("rm fab_render_xy.png") + msg = file.read() + file.close() + send_image(msg) + except IOError: + print "oops" + elif (find(response,"GET /fab_render_xz.png") == 0): + try: + file = open('fab_render_xz.png','rb') + os.system("rm fab_render_xz.png") + msg = file.read() + file.close() + send_image(msg) + except IOError: + print "oops" + elif (find(response,"GET /fab_render_zy.png") == 0): + try: + file = open('fab_render_zy.png','rb') + os.system("rm fab_render_zy.png") + msg = file.read() + file.close() + send_image(msg) + except IOError: + print "oops" + elif (find(response,"GET /fab_render_xyz.png") == 0): + try: + file = open('fab_render_xyz.png','rb') + os.system("rm fab_render_xyz.png") + msg = file.read() + file.close() + send_image(msg) + except IOError: + print "oops" + elif (find(response,"GET /fab.html") == 0): + file = open('fab.html','rb') + msg = file.read() + file.close() + send_text(msg) + elif (find(response,"GET /quit") == 0): + break + elif (find(response,"POST /list_files") == 0): + files = glob.glob('*.fab') + file_names = ','.join(files) + cwd = os.getcwd() + dirs = filter(os.path.isdir, os.listdir('.')) + dirs_names = ','.join(dirs) + send_text(file_names+';'+cwd+';'+dirs_names) + elif (find(response,"POST /cd") == 0): + start = 1+find(response,"{") + end = find(response,"}",start) + dir_name = response[start:end] + os.chdir(dir_name) + files = glob.glob('*.fab') + file_names = ','.join(files) + cwd = os.getcwd() + dirs = filter(os.path.isdir, os.listdir('.')) + dirs_names = ','.join(dirs) + send_text(file_names+';'+cwd+';'+dirs_names) + elif (find(response,"POST /delete") == 0): + start = 1+find(response,"{") + end = find(response,"}",start) + file_name = response[start:end] + os.remove(file_name) + files = glob.glob('*.fab') + file_names = ','.join(files) + cwd = os.getcwd() + dirs = filter(os.path.isdir, os.listdir('.')) + dirs_names = ','.join(dirs) + send_text(file_names+';'+cwd+';'+dirs_names) + elif (find(response,"POST /open") == 0): + start = 1+find(response,"{") + end = find(response,"}",start) + file_name = response[start:end] + print "file"+file_name+"name" + try: + file = open(file_name,'rb') + msg = file.read() + file.close() + send_text(msg) + except IOError: + print "oops" + elif (find(response,"POST /render") == 0): + # + # render + # + parse() + render() + elif (find(response,"POST /fabricate") == 0): + # + # fabricate + # + parse() + math_file = open("fab_render_xy.math","w") + math_file.write("format: "+mode+"\n") + math_file.write("mm per unit: "+units+"\n") + math_file.write("dx dy dz: "+dx+" "+dy+" "+dz+"\n") + math_file.write("xmin ymin zmin: "+xmin+" "+ymin+" "+zmin+"\n") + math_file.write("expression: "+expression+"\n") + math_file.close() + os.system("fab fab_render_xy.math") + os.system("rm fab_render_xy.math") + elif (find(response,"POST /save_graph") == 0): + start = 17+find(response,"POST /save_graph") + end = find(response,"/",start) + file_name = response[start:end] + file = open(file_name,"w") + start = find(response,"graph start:") + file.writelines(response[start:]) + file.close() + client_socket.send("saved "+file_name) + elif (find(response,"POST /save_math") == 0): + start = 16+find(response,"POST /save_math") + end = find(response,"/",start) + file_name = response[start:end] + parse() + math_file = open(file_name,"w") + math_file.write("format: "+mode+"\n") + math_file.write("mm per unit: "+units+"\n") + math_file.write("dx dy dz: "+dx+" "+dy+" "+dz+"\n") + math_file.write("xmin ymin zmin: "+xmin+" "+ymin+" "+zmin+"\n") + math_file.write("expression: "+expression+"\n") + math_file.close() + client_socket.send("saved "+file_name) + # + # close connection socket and exit + # + client_socket.close() +# +# close server socket and exit +# +server_socket.close() diff --git a/src/guis/make_cad_camm b/src/guis/make_cad_camm new file mode 100755 index 0000000..dd99999 --- /dev/null +++ b/src/guis/make_cad_camm @@ -0,0 +1,59 @@ +#!/usr/bin/env python +# +# make_cad_camm +# .cad to .camm GUI wrapper +# +# Neil Gershenfeld +# CBA MIT 3/6/11 +# +# (c) Massachusetts Institute of Technology 2011 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,sys +from fab_set import fab_frame +from panel_control import control_panel +from panel_cad import cad_panel +from panel_cad_png import cad_png_panel +from panel_png_path import png_path_panel +from panel_path_camm import path_camm_panel +# +# command line +# +print "command line: make_cad_camm [input_file [size]]" +print " input_file = input .cad file (optional)" +print " size = image panel size (optional)" +# +# start wx +# +app = wx.App() +# +# add panels to frame +# +frame = fab_frame("make_cad_camm",sys.argv) +frame.size = frame.size/1.25 +frame.control_panel = control_panel(frame) +frame.sizer.Add(frame.control_panel,(0,0),span=(1,4),flag=wx.ALIGN_CENTER_HORIZONTAL) +frame.cad_panel = cad_panel(frame) +frame.sizer.Add(frame.cad_panel,(1,0)) +frame.png_panel = cad_png_panel(frame) +frame.sizer.Add(frame.png_panel,(1,1)) +frame.path_panel = png_path_panel(frame) +frame.sizer.Add(frame.path_panel,(1,2)) +frame.camm_panel = path_camm_panel(frame) +frame.sizer.Add(frame.camm_panel,(1,3)) +# +# defaults +# +frame.set_cad_camm() +# +# fit and show frame +# +frame.Fit() +frame.Show() +# +# start mainloop +# +app.MainLoop() diff --git a/src/guis/make_cad_drl b/src/guis/make_cad_drl new file mode 100755 index 0000000..6df91f4 --- /dev/null +++ b/src/guis/make_cad_drl @@ -0,0 +1,55 @@ +#!/usr/bin/env python +# +# make_cad_drl +# .cad to .drl GUI wrapper +# +# Neil Gershenfeld +# CBA MIT 11/25/12 +# +# (c) Massachusetts Institute of Technology 2012 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,sys +from fab_set import fab_frame +from panel_control import control_panel +from panel_cad import cad_panel +from panel_cad_png import cad_png_panel +from panel_png_drl import png_drl_panel +# +# command line +# +print "command line: make_cad_drl [input_file [size]]" +print " input_file = input .cad file (optional)" +print " size = image panel size (optional)" +# +# start wx +# +app = wx.App() +# +# add panels to frame +# +frame = fab_frame("make_cad_drl",sys.argv) +frame.control_panel = control_panel(frame) +frame.sizer.Add(frame.control_panel,(0,0),span=(1,3),flag=wx.ALIGN_CENTER_HORIZONTAL) +frame.cad_panel = cad_panel(frame) +frame.sizer.Add(frame.cad_panel,(1,0)) +frame.png_panel = cad_png_panel(frame) +frame.sizer.Add(frame.png_panel,(1,1)) +frame.drl_panel = png_drl_panel(frame) +frame.sizer.Add(frame.drl_panel,(1,2)) +# +# defaults +# +#frame.set_cad_drl() +# +# fit and show frame +# +frame.Fit() +frame.Show() +# +# start mainloop +# +app.MainLoop() diff --git a/src/guis/make_cad_dxf b/src/guis/make_cad_dxf new file mode 100755 index 0000000..8014ec9 --- /dev/null +++ b/src/guis/make_cad_dxf @@ -0,0 +1,58 @@ +#!/usr/bin/env python +# +# make_cad_dxf +# .cad to .dxf GUI wrapper +# +# Neil Gershenfeld +# CBA MIT 8/25/12 +# +# (c) Massachusetts Institute of Technology 2012 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,sys +from fab_set import fab_frame +from panel_control import control_panel +from panel_cad import cad_panel +from panel_cad_png import cad_png_panel +from panel_png_path import png_path_panel +from panel_path_dxf import path_dxf_panel +# +# command line +# +print "command line: make_cad_dxf [input_file [size]]" +print " input_file = input .cad file (optional)" +print " size = image panel size (optional)" +# +# start wx +# +app = wx.App() +# +# add panels to frame +# +frame = fab_frame("make_cad_dxf",sys.argv) +frame.control_panel = control_panel(frame) +frame.sizer.Add(frame.control_panel,(0,0),span=(1,4),flag=wx.ALIGN_CENTER_HORIZONTAL) +frame.cad_panel = cad_panel(frame) +frame.sizer.Add(frame.cad_panel,(1,0)) +frame.png_panel = cad_png_panel(frame) +frame.sizer.Add(frame.png_panel,(1,1)) +frame.path_panel = png_path_panel(frame) +frame.sizer.Add(frame.path_panel,(1,2)) +frame.dxf_panel = path_dxf_panel(frame) +frame.sizer.Add(frame.dxf_panel,(1,3)) +# +# defaults +# +#frame.set_cad_dxf() +# +# fit and show frame +# +frame.Fit() +frame.Show() +# +# start mainloop +# +app.MainLoop() diff --git a/src/guis/make_cad_epi b/src/guis/make_cad_epi new file mode 100755 index 0000000..341c566 --- /dev/null +++ b/src/guis/make_cad_epi @@ -0,0 +1,58 @@ +#!/usr/bin/env python +# +# make_cad_epi +# .cad to .epi GUI wrapper +# +# Neil Gershenfeld +# CBA MIT 3/6/11 +# +# (c) Massachusetts Institute of Technology 2011 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,sys +from fab_set import fab_frame +from panel_control import control_panel +from panel_cad import cad_panel +from panel_cad_png import cad_png_panel +from panel_png_path import png_path_panel +from panel_path_epi import path_epi_panel +# +# command line +# +print "command line: make_cad_epi [input_file [size]]" +print " input_file = input .cad file (optional)" +print " size = image panel size (optional)" +# +# start wx +# +app = wx.App() +# +# add panels to frame +# +frame = fab_frame("make_cad_epi",sys.argv) +frame.control_panel = control_panel(frame) +frame.sizer.Add(frame.control_panel,(0,0),span=(1,4),flag=wx.ALIGN_CENTER_HORIZONTAL) +frame.cad_panel = cad_panel(frame) +frame.sizer.Add(frame.cad_panel,(1,0)) +frame.cad_png_panel = cad_png_panel(frame) +frame.sizer.Add(frame.cad_png_panel,(1,1)) +frame.png_path_panel = png_path_panel(frame) +frame.sizer.Add(frame.png_path_panel,(1,2)) +frame.epi_panel = path_epi_panel(frame) +frame.sizer.Add(frame.epi_panel,(1,3)) +# +# defaults +# +frame.set_cad_epi() +# +# fit and show frame +# +frame.Fit() +frame.Show() +# +# start mainloop +# +app.MainLoop() diff --git a/src/guis/make_cad_eps b/src/guis/make_cad_eps new file mode 100755 index 0000000..4e4448d --- /dev/null +++ b/src/guis/make_cad_eps @@ -0,0 +1,61 @@ +#!/usr/bin/env python +# +# make_cad_eps +# .cad to .eps GUI wrapper +# +# Neil Gershenfeld 7/4/13 +# (c) Massachusetts Institute of Technology 2013 +# +# This work may be reproduced, modified, distributed, +# performed, and displayed for any purpose, but must +# acknowledge the fab modules project. Copyright is +# retained and must be preserved. The work is provided +# as is; no warranty is provided, and users accept all +# liability. +# +# imports +# +import wx,sys +from fab_set import fab_frame +from panel_control import control_panel +from panel_cad import cad_panel +from panel_cad_png import cad_png_panel +from panel_png_path import png_path_panel +from panel_path_eps import path_eps_panel +# +# command line +# +print "command line: make_cad_eps [input_file [size]]" +print " input_file = input .cad file (optional)" +print " size = image panel size (optional)" +# +# start wx +# +app = wx.App() +# +# add panels to frame +# +frame = fab_frame("make_cad_eps",sys.argv) +frame.control_panel = control_panel(frame) +frame.sizer.Add(frame.control_panel,(0,0),span=(1,4),flag=wx.ALIGN_CENTER_HORIZONTAL) +frame.cad_panel = cad_panel(frame) +frame.sizer.Add(frame.cad_panel,(1,0)) +frame.cad_png_panel = cad_png_panel(frame) +frame.sizer.Add(frame.cad_png_panel,(1,1)) +frame.png_path_panel = png_path_panel(frame) +frame.sizer.Add(frame.png_path_panel,(1,2)) +frame.eps_panel = path_eps_panel(frame) +frame.sizer.Add(frame.eps_panel,(1,3)) +# +# defaults +# +frame.set_cad_eps() +# +# fit and show frame +# +frame.Fit() +frame.Show() +# +# start mainloop +# +app.MainLoop() diff --git a/src/guis/make_cad_g b/src/guis/make_cad_g new file mode 100755 index 0000000..ee6aae3 --- /dev/null +++ b/src/guis/make_cad_g @@ -0,0 +1,58 @@ +#!/usr/bin/env python +# +# make_cad_g +# .cad to .g GUI wrapper +# +# Neil Gershenfeld +# CBA MIT 3/6/11 +# +# (c) Massachusetts Institute of Technology 2011 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,sys +from fab_set import fab_frame +from panel_control import control_panel +from panel_cad import cad_panel +from panel_cad_png import cad_png_panel +from panel_png_path import png_path_panel +from panel_path_g import path_g_panel +# +# command line +# +print "command line: make_cad_g [input_file [size]]" +print " input_file = input .cad file (optional)" +print " size = image panel size (optional)" +# +# start wx +# +app = wx.App() +# +# add panels to frame +# +frame = fab_frame("make_cad_g",sys.argv) +frame.control_panel = control_panel(frame) +frame.sizer.Add(frame.control_panel,(0,0),span=(1,4),flag=wx.ALIGN_CENTER_HORIZONTAL) +frame.cad_panel = cad_panel(frame) +frame.sizer.Add(frame.cad_panel,(1,0)) +frame.cad_png_panel = cad_png_panel(frame) +frame.sizer.Add(frame.cad_png_panel,(1,1)) +frame.png_path_panel = png_path_panel(frame) +frame.sizer.Add(frame.png_path_panel,(1,2)) +frame.g_panel = path_g_panel(frame) +frame.sizer.Add(frame.g_panel,(1,3)) +# +# defaults +# +frame.set_cad_g() +# +# fit and show frame +# +frame.Fit() +frame.Show() +# +# start mainloop +# +app.MainLoop() diff --git a/src/guis/make_cad_grb b/src/guis/make_cad_grb new file mode 100755 index 0000000..a22bee3 --- /dev/null +++ b/src/guis/make_cad_grb @@ -0,0 +1,55 @@ +#!/usr/bin/env python +# +# make_cad_grb +# .cad to .grb GUI wrapper +# +# Neil Gershenfeld +# CBA MIT 11/24/12 +# +# (c) Massachusetts Institute of Technology 2012 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,sys +from fab_set import fab_frame +from panel_control import control_panel +from panel_cad import cad_panel +from panel_cad_png import cad_png_panel +from panel_png_grb import png_grb_panel +# +# command line +# +print "command line: make_cad_grb [input_file [size]]" +print " input_file = input .cad file (optional)" +print " size = image panel size (optional)" +# +# start wx +# +app = wx.App() +# +# add panels to frame +# +frame = fab_frame("make_cad_grb",sys.argv) +frame.control_panel = control_panel(frame) +frame.sizer.Add(frame.control_panel,(0,0),span=(1,3),flag=wx.ALIGN_CENTER_HORIZONTAL) +frame.cad_panel = cad_panel(frame) +frame.sizer.Add(frame.cad_panel,(1,0)) +frame.png_panel = cad_png_panel(frame) +frame.sizer.Add(frame.png_panel,(1,1)) +frame.grb_panel = png_grb_panel(frame) +frame.sizer.Add(frame.grb_panel,(1,2)) +# +# defaults +# +#frame.set_cad_grb() +# +# fit and show frame +# +frame.Fit() +frame.Show() +# +# start mainloop +# +app.MainLoop() diff --git a/src/guis/make_cad_ord b/src/guis/make_cad_ord new file mode 100755 index 0000000..258e8ea --- /dev/null +++ b/src/guis/make_cad_ord @@ -0,0 +1,58 @@ +#!/usr/bin/env python +# +# make_cad_ord +# .cad to .ord GUI wrapper +# +# Neil Gershenfeld +# CBA MIT 3/6/11 +# +# (c) Massachusetts Institute of Technology 2011 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,sys +from fab_set import fab_frame +from panel_control import control_panel +from panel_cad import cad_panel +from panel_cad_png import cad_png_panel +from panel_png_path import png_path_panel +from panel_path_ord import path_ord_panel +# +# command line +# +print "command line: make_cad_ord [input_file [size]]" +print " input_file = input .cad file (optional)" +print " size = image panel size (optional)" +# +# start wx +# +app = wx.App() +# +# add panels to frame +# +frame = fab_frame("make_cad_ord",sys.argv) +frame.control_panel = control_panel(frame) +frame.sizer.Add(frame.control_panel,(0,0),span=(1,4),flag=wx.ALIGN_CENTER_HORIZONTAL) +frame.cad_panel = cad_panel(frame) +frame.sizer.Add(frame.cad_panel,(1,0)) +frame.png_panel = cad_png_panel(frame) +frame.sizer.Add(frame.png_panel,(1,1)) +frame.path_panel = png_path_panel(frame) +frame.sizer.Add(frame.path_panel,(1,2)) +frame.ord_panel = path_ord_panel(frame) +frame.sizer.Add(frame.ord_panel,(1,3)) +# +# defaults +# +frame.set_cad_ord() +# +# fit and show frame +# +frame.Fit() +frame.Show() +# +# start mainloop +# +app.MainLoop() diff --git a/src/guis/make_cad_png b/src/guis/make_cad_png new file mode 100755 index 0000000..53525f4 --- /dev/null +++ b/src/guis/make_cad_png @@ -0,0 +1,56 @@ +#!/usr/bin/env python +# +# make_cad_png +# .cad to .png GUI wrapper +# +# Neil Gershenfeld +# CBA MIT 2/18/11 +# +# (c) Massachusetts Institute of Technology 2011 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,sys +from fab_set import fab_frame +from panel_control import control_panel +from panel_cad import cad_panel +from panel_cad_png import cad_png_panel +# +# command line +# +print "command line: make_cad_png [input_file [size]]" +print " input_file = input .cad file (optional)" +print " size = image panel size (optional)" +# +# start wx +# +app = wx.App() +# +# add panels to frame +# +frame = fab_frame("make_cad_png",sys.argv) +frame.control_panel = control_panel(frame) +frame.sizer.Add(frame.control_panel,(0,0),span=(1,2),flag=wx.ALIGN_CENTER_HORIZONTAL) +frame.cad_panel = cad_panel(frame) +frame.sizer.Add(frame.cad_panel,(1,0)) +frame.cad_png_panel = cad_png_panel(frame) +frame.sizer.Add(frame.cad_png_panel,(1,1)) +# +# defaults +# +frame.defaults = {} +frame.control_panel.defaults.Append('preview') +frame.defaults["preview"] = "self.png_panel.resolution.SetValue('10')" +frame.control_panel.defaults.Append('render') +frame.defaults["render"] = "self.png_panel.resolution.SetValue('50')" +# +# fit and show frame +# +frame.Fit() +frame.Show() +# +# start mainloop +# +app.MainLoop() diff --git a/src/guis/make_cad_rml b/src/guis/make_cad_rml new file mode 100755 index 0000000..3c96c58 --- /dev/null +++ b/src/guis/make_cad_rml @@ -0,0 +1,58 @@ +#!/usr/bin/env python +# +# make_cad_rml +# .cad to .rml GUI wrapper +# +# Neil Gershenfeld +# CBA MIT 2/19/11 +# +# (c) Massachusetts Institute of Technology 2011 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,sys +from fab_set import fab_frame +from panel_control import control_panel +from panel_cad import cad_panel +from panel_cad_png import cad_png_panel +from panel_png_path import png_path_panel +from panel_path_rml import path_rml_panel +# +# command line +# +print "command line: make_cad_rml [input_file [size]]" +print " input_file = input .cad file (optional)" +print " size = image panel size (optional)" +# +# start wx +# +app = wx.App() +# +# add panels to frame +# +frame = fab_frame("make_cad_rml",sys.argv) +frame.control_panel = control_panel(frame) +frame.sizer.Add(frame.control_panel,(0,0),span=(1,4),flag=wx.ALIGN_CENTER_HORIZONTAL) +frame.cad_panel = cad_panel(frame) +frame.sizer.Add(frame.cad_panel,(1,0)) +frame.cad_png_panel = cad_png_panel(frame) +frame.sizer.Add(frame.cad_png_panel,(1,1)) +frame.png_path_panel = png_path_panel(frame) +frame.sizer.Add(frame.png_path_panel,(1,2)) +frame.rml_panel = path_rml_panel(frame) +frame.sizer.Add(frame.rml_panel,(1,3)) +# +# defaults +# +frame.set_cad_rml() +# +# fit and show frame +# +frame.Fit() +frame.Show() +# +# start mainloop +# +app.MainLoop() diff --git a/src/guis/make_cad_sbp b/src/guis/make_cad_sbp new file mode 100755 index 0000000..f9cc0f9 --- /dev/null +++ b/src/guis/make_cad_sbp @@ -0,0 +1,58 @@ +#!/usr/bin/env python +# +# make_cad_sbp +# .cad to .sbp GUI wrapper +# +# Neil Gershenfeld +# CBA MIT 3/6/11 +# +# (c) Massachusetts Institute of Technology 2011 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,sys +from fab_set import fab_frame +from panel_control import control_panel +from panel_cad import cad_panel +from panel_cad_png import cad_png_panel +from panel_png_path import png_path_panel +from panel_path_sbp import path_sbp_panel +# +# command line +# +print "command line: make_cad_sbp [input_file [size]]" +print " input_file = input .cad file (optional)" +print " size = image panel size (optional)" +# +# start wx +# +app = wx.App() +# +# add panels to frame +# +frame = fab_frame("make_cad_sbp",sys.argv) +frame.control_panel = control_panel(frame) +frame.sizer.Add(frame.control_panel,(0,0),span=(1,4),flag=wx.ALIGN_CENTER_HORIZONTAL) +frame.cad_panel = cad_panel(frame) +frame.sizer.Add(frame.cad_panel,(1,0)) +frame.cad_png_panel = cad_png_panel(frame) +frame.sizer.Add(frame.cad_png_panel,(1,1)) +frame.png_path_panel = png_path_panel(frame) +frame.sizer.Add(frame.png_path_panel,(1,2)) +frame.sbp_panel = path_sbp_panel(frame) +frame.sizer.Add(frame.sbp_panel,(1,3)) +# +# defaults +# +frame.set_cad_sbp() +# +# fit and show frame +# +frame.Fit() +frame.Show() +# +# start mainloop +# +app.MainLoop() diff --git a/src/guis/make_cad_snap b/src/guis/make_cad_snap new file mode 100755 index 0000000..7ea072b --- /dev/null +++ b/src/guis/make_cad_snap @@ -0,0 +1,58 @@ +#!/usr/bin/env python +# +# make_cad_snap +# .cad to MTP Snap GUI wrapper +# +# Neil Gershenfeld +# CBA MIT 11/24/11 +# +# (c) Massachusetts Institute of Technology 2011 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,sys +from fab_set import fab_frame +from panel_control import control_panel +from panel_cad import cad_panel +from panel_cad_png import cad_png_panel +from panel_png_path import png_path_panel +from panel_path_snap import path_snap_panel +# +# command line +# +print "command line: make_cad_snap [input_file [size]]" +print " input_file = input .cad file (optional)" +print " size = image panel size (optional)" +# +# start wx +# +app = wx.App() +# +# add panels to frame +# +frame = fab_frame("make_cad_snap",sys.argv) +frame.control_panel = control_panel(frame) +frame.sizer.Add(frame.control_panel,(0,0),span=(1,4),flag=wx.ALIGN_CENTER_HORIZONTAL) +frame.cad_panel = cad_panel(frame) +frame.sizer.Add(frame.cad_panel,(1,0)) +frame.png_panel = cad_png_panel(frame) +frame.sizer.Add(frame.png_panel,(1,1)) +frame.path_panel = png_path_panel(frame) +frame.sizer.Add(frame.path_panel,(1,2)) +frame.snap_panel = path_snap_panel(frame) +frame.sizer.Add(frame.snap_panel,(1,3)) +# +# defaults +# +#frame.set_cad_rml() +# +# fit and show frame +# +frame.Fit() +frame.Show() +# +# start mainloop +# +app.MainLoop() diff --git a/src/guis/make_cad_stl b/src/guis/make_cad_stl new file mode 100755 index 0000000..5d8ae92 --- /dev/null +++ b/src/guis/make_cad_stl @@ -0,0 +1,52 @@ +#!/usr/bin/env python +# +# make_cad_stl +# .cad to .stl GUI wrapper +# +# Neil Gershenfeld +# CBA MIT 3/22/11 +# +# (c) Massachusetts Institute of Technology 2011 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,sys +from fab_set import fab_frame +from panel_control import control_panel +from panel_cad import cad_panel +from panel_cad_stl import cad_stl_panel +# +# command line +# +print "command line: make_cad_stl [input_file [size]]" +print " input_file = input .cad file (optional)" +print " size = image panel size (optional)" +# +# start wx +# +app = wx.App() +# +# add panels to frame +# +frame = fab_frame("make_cad_stl",sys.argv) +frame.control_panel = control_panel(frame) +frame.sizer.Add(frame.control_panel,(0,0),span=(1,2),flag=wx.ALIGN_CENTER_HORIZONTAL) +frame.cad_panel = cad_panel(frame) +frame.sizer.Add(frame.cad_panel,(1,0)) +frame.stl_panel = cad_stl_panel(frame) +frame.sizer.Add(frame.stl_panel,(1,1)) +# +# defaults +# +frame.defaults = {} +# +# fit and show frame +# +frame.Fit() +frame.Show() +# +# start mainloop +# +app.MainLoop() diff --git a/src/guis/make_cad_uni b/src/guis/make_cad_uni new file mode 100755 index 0000000..50ab51b --- /dev/null +++ b/src/guis/make_cad_uni @@ -0,0 +1,58 @@ +#!/usr/bin/env python +# +# make_cad_uni +# .cad to .uni GUI wrapper +# +# Neil Gershenfeld +# CBA MIT 3/6/11 +# +# (c) Massachusetts Institute of Technology 2011 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,sys +from fab_set import fab_frame +from panel_control import control_panel +from panel_cad import cad_panel +from panel_cad_png import cad_png_panel +from panel_png_path import png_path_panel +from panel_path_uni import path_uni_panel +# +# command line +# +print "command line: make_cad_uni [input_file [size]]" +print " input_file = input .cad file (optional)" +print " size = image panel size (optional)" +# +# start wx +# +app = wx.App() +# +# add panels to frame +# +frame = fab_frame("make_cad_uni",sys.argv) +frame.control_panel = control_panel(frame) +frame.sizer.Add(frame.control_panel,(0,0),span=(1,4),flag=wx.ALIGN_CENTER_HORIZONTAL) +frame.cad_panel = cad_panel(frame) +frame.sizer.Add(frame.cad_panel,(1,0)) +frame.png_panel = cad_png_panel(frame) +frame.sizer.Add(frame.png_panel,(1,1)) +frame.path_panel = png_path_panel(frame) +frame.sizer.Add(frame.path_panel,(1,2)) +frame.uni_panel = path_uni_panel(frame) +frame.sizer.Add(frame.uni_panel,(1,3)) +# +# defaults +# +frame.set_cad_uni() +# +# fit and show frame +# +frame.Fit() +frame.Show() +# +# start mainloop +# +app.MainLoop() diff --git a/src/guis/make_gif_stl b/src/guis/make_gif_stl new file mode 100644 index 0000000..24beb9c --- /dev/null +++ b/src/guis/make_gif_stl @@ -0,0 +1,52 @@ +#!/usr/bin/env python +# +# make_gif_stl +# .gif to .stl GUI wrapper +# +# Neil Gershenfeld +# CBA MIT 3/25/14 +# +# (c) Massachusetts Institute of Technology 2011 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,sys +from fab_set import fab_frame +from panel_control import control_panel +from panel_gif import gif_panel +from panel_gif_stl import gif_stl_panel +# +# command line +# +print "command line: make_gif_stl [input_file [size]]" +print " input_file = input .gif file (optional)" +print " size = image panel size (optional)" +# +# start wx +# +app = wx.App() +# +# add panels to frame +# +frame = fab_frame("make_gif_stl",sys.argv) +frame.control_panel = control_panel(frame) +frame.sizer.Add(frame.control_panel,(0,0),span=(1,2),flag=wx.ALIGN_CENTER_HORIZONTAL) +frame.gif_panel = gif_panel(frame) +frame.sizer.Add(frame.gif_panel,(1,0)) +frame.stl_panel = gif_stl_panel(frame) +frame.sizer.Add(frame.stl_panel,(1,1)) +# +# defaults +# +frame.defaults = {} +# +# fit and show frame +# +frame.Fit() +frame.Show() +# +# start mainloop +# +app.MainLoop() diff --git a/src/guis/make_math_camm b/src/guis/make_math_camm new file mode 100755 index 0000000..366d295 --- /dev/null +++ b/src/guis/make_math_camm @@ -0,0 +1,58 @@ +#!/usr/bin/env python +# +# make_math_camm +# .math to .camm GUI wrapper +# +# Neil Gershenfeld +# CBA MIT 4/25/12 +# +# (c) Massachusetts Institute of Technology 2012 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,sys +from fab_set import fab_frame +from panel_control import control_panel +from panel_math import math_panel +from panel_math_png import math_png_panel +from panel_png_path import png_path_panel +from panel_path_camm import path_camm_panel +# +# command line +# +print "command line: make_math_camm [input_file [size]]" +print " input_file = input .math file (optional)" +print " size = image panel size (optional)" +# +# start wx +# +app = wx.App() +# +# add panels to frame +# +frame = fab_frame("make_math_camm",sys.argv) +frame.control_panel = control_panel(frame) +frame.sizer.Add(frame.control_panel,(0,0),span=(1,4),flag=wx.ALIGN_CENTER_HORIZONTAL) +frame.math_panel = math_panel(frame) +frame.sizer.Add(frame.math_panel,(1,0)) +frame.png_panel = math_png_panel(frame) +frame.sizer.Add(frame.png_panel,(1,1)) +frame.path_panel = png_path_panel(frame) +frame.sizer.Add(frame.path_panel,(1,2)) +frame.camm_panel = path_camm_panel(frame) +frame.sizer.Add(frame.camm_panel,(1,3)) +# +# defaults +# +frame.set_math_camm() +# +# fit and show frame +# +frame.Fit() +frame.Show() +# +# start mainloop +# +app.MainLoop() diff --git a/src/guis/make_math_drl b/src/guis/make_math_drl new file mode 100755 index 0000000..7d3c0f8 --- /dev/null +++ b/src/guis/make_math_drl @@ -0,0 +1,55 @@ +#!/usr/bin/env python +# +# make_math_drl +# .math to .drl GUI wrapper +# +# Neil Gershenfeld +# CBA MIT 11/24/12 +# +# (c) Massachusetts Institute of Technology 2012 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,sys +from fab_set import fab_frame +from panel_control import control_panel +from panel_math import math_panel +from panel_math_png import math_png_panel +from panel_png_drl import png_drl_panel +# +# command line +# +print "command line: make_math_drl [input_file [size]]" +print " input_file = input .math file (optional)" +print " size = image panel size (optional)" +# +# start wx +# +app = wx.App() +# +# add panels to frame +# +frame = fab_frame("make_math_drl",sys.argv) +frame.control_panel = control_panel(frame) +frame.sizer.Add(frame.control_panel,(0,0),span=(1,4),flag=wx.ALIGN_CENTER_HORIZONTAL) +frame.math_panel = math_panel(frame) +frame.sizer.Add(frame.math_panel,(1,0)) +frame.png_panel = math_png_panel(frame) +frame.sizer.Add(frame.png_panel,(1,1)) +frame.drl_panel = png_drl_panel(frame) +frame.sizer.Add(frame.drl_panel,(1,2)) +# +# defaults +# +#frame.set_math_drl() +# +# fit and show frame +# +frame.Fit() +frame.Show() +# +# start mainloop +# +app.MainLoop() diff --git a/src/guis/make_math_dxf b/src/guis/make_math_dxf new file mode 100755 index 0000000..e4c101c --- /dev/null +++ b/src/guis/make_math_dxf @@ -0,0 +1,58 @@ +#!/usr/bin/env python +# +# make_math_dxf +# .math to .dxf GUI wrapper +# +# Neil Gershenfeld +# CBA MIT 8/25/12 +# +# (c) Massachusetts Institute of Technology 2012 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,sys +from fab_set import fab_frame +from panel_control import control_panel +from panel_math import math_panel +from panel_math_png import math_png_panel +from panel_png_path import png_path_panel +from panel_path_dxf import path_dxf_panel +# +# command line +# +print "command line: make_math_dxf [input_file [size]]" +print " input_file = input .math file (optional)" +print " size = image panel size (optional)" +# +# start wx +# +app = wx.App() +# +# add panels to frame +# +frame = fab_frame("make_math_dxf",sys.argv) +frame.control_panel = control_panel(frame) +frame.sizer.Add(frame.control_panel,(0,0),span=(1,4),flag=wx.ALIGN_CENTER_HORIZONTAL) +frame.math_panel = math_panel(frame) +frame.sizer.Add(frame.math_panel,(1,0)) +frame.png_panel = math_png_panel(frame) +frame.sizer.Add(frame.png_panel,(1,1)) +frame.path_panel = png_path_panel(frame) +frame.sizer.Add(frame.path_panel,(1,2)) +frame.dxf_panel = path_dxf_panel(frame) +frame.sizer.Add(frame.dxf_panel,(1,3)) +# +# defaults +# +#frame.set_math_dxf() +# +# fit and show frame +# +frame.Fit() +frame.Show() +# +# start mainloop +# +app.MainLoop() diff --git a/src/guis/make_math_epi b/src/guis/make_math_epi new file mode 100755 index 0000000..58543a4 --- /dev/null +++ b/src/guis/make_math_epi @@ -0,0 +1,58 @@ +#!/usr/bin/env python +# +# make_math_epi +# .math to .epi GUI wrapper +# +# Neil Gershenfeld +# CBA MIT 4/25/12 +# +# (c) Massachusetts Institute of Technology 2012 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,sys +from fab_set import fab_frame +from panel_control import control_panel +from panel_math import math_panel +from panel_math_png import math_png_panel +from panel_png_path import png_path_panel +from panel_path_epi import path_epi_panel +# +# command line +# +print "command line: make_math_epi [input_file [size]]" +print " input_file = input .math file (optional)" +print " size = image panel size (optional)" +# +# start wx +# +app = wx.App() +# +# add panels to frame +# +frame = fab_frame("make_math_epi",sys.argv) +frame.control_panel = control_panel(frame) +frame.sizer.Add(frame.control_panel,(0,0),span=(1,4),flag=wx.ALIGN_CENTER_HORIZONTAL) +frame.math_panel = math_panel(frame) +frame.sizer.Add(frame.math_panel,(1,0)) +frame.math_png_panel = math_png_panel(frame) +frame.sizer.Add(frame.math_png_panel,(1,1)) +frame.path_panel = png_path_panel(frame) +frame.sizer.Add(frame.path_panel,(1,2)) +frame.epi_panel = path_epi_panel(frame) +frame.sizer.Add(frame.epi_panel,(1,3)) +# +# defaults +# +frame.set_math_epi() +# +# fit and show frame +# +frame.Fit() +frame.Show() +# +# start mainloop +# +app.MainLoop() diff --git a/src/guis/make_math_eps b/src/guis/make_math_eps new file mode 100755 index 0000000..c8fd32c --- /dev/null +++ b/src/guis/make_math_eps @@ -0,0 +1,61 @@ +#!/usr/bin/env python +# +# make_math_eps +# .math to .eps GUI wrapper +# +# Neil Gershenfeld 7/4/13 +# (c) Massachusetts Institute of Technology 2013 +# +# This work may be reproduced, modified, distributed, +# performed, and displayed for any purpose, but must +# acknowledge the fab modules project. Copyright is +# retained and must be preserved. The work is provided +# as is; no warranty is provided, and users accept all +# liability. +# +# imports +# +import wx,sys +from fab_set import fab_frame +from panel_control import control_panel +from panel_math import math_panel +from panel_math_png import math_png_panel +from panel_png_path import png_path_panel +from panel_path_eps import path_eps_panel +# +# command line +# +print "command line: make_math_eps [input_file [size]]" +print " input_file = input .math file (optional)" +print " size = image panel size (optional)" +# +# start wx +# +app = wx.App() +# +# add panels to frame +# +frame = fab_frame("make_math_eps",sys.argv) +frame.control_panel = control_panel(frame) +frame.sizer.Add(frame.control_panel,(0,0),span=(1,4),flag=wx.ALIGN_CENTER_HORIZONTAL) +frame.math_panel = math_panel(frame) +frame.sizer.Add(frame.math_panel,(1,0)) +frame.png_panel = math_png_panel(frame) +frame.sizer.Add(frame.png_panel,(1,1)) +frame.path_panel = png_path_panel(frame) +frame.sizer.Add(frame.path_panel,(1,2)) +frame.eps_panel = path_eps_panel(frame) +frame.sizer.Add(frame.eps_panel,(1,3)) +# +# defaults +# +frame.set_math_eps() +# +# fit and show frame +# +frame.Fit() +frame.Show() +# +# start mainloop +# +app.MainLoop() diff --git a/src/guis/make_math_g b/src/guis/make_math_g new file mode 100755 index 0000000..5030bcd --- /dev/null +++ b/src/guis/make_math_g @@ -0,0 +1,58 @@ +#!/usr/bin/env python +# +# make_math_g +# .math to .g GUI wrapper +# +# Neil Gershenfeld +# CBA MIT 4/25/12 +# +# (c) Massachusetts Institute of Technology 2012 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,sys +from fab_set import fab_frame +from panel_control import control_panel +from panel_math import math_panel +from panel_math_png import math_png_panel +from panel_png_path import png_path_panel +from panel_path_g import path_g_panel +# +# command line +# +print "command line: make_math_g [input_file [size]]" +print " input_file = input .math file (optional)" +print " size = image panel size (optional)" +# +# start wx +# +app = wx.App() +# +# add panels to frame +# +frame = fab_frame("make_math_g",sys.argv) +frame.control_panel = control_panel(frame) +frame.sizer.Add(frame.control_panel,(0,0),span=(1,4),flag=wx.ALIGN_CENTER_HORIZONTAL) +frame.math_panel = math_panel(frame) +frame.sizer.Add(frame.math_panel,(1,0)) +frame.math_png_panel = math_png_panel(frame) +frame.sizer.Add(frame.math_png_panel,(1,1)) +frame.png_path_panel = png_path_panel(frame) +frame.sizer.Add(frame.png_path_panel,(1,2)) +frame.g_panel = path_g_panel(frame) +frame.sizer.Add(frame.g_panel,(1,3)) +# +# defaults +# +frame.set_math_g() +# +# fit and show frame +# +frame.Fit() +frame.Show() +# +# start mainloop +# +app.MainLoop() diff --git a/src/guis/make_math_grb b/src/guis/make_math_grb new file mode 100755 index 0000000..39d3d92 --- /dev/null +++ b/src/guis/make_math_grb @@ -0,0 +1,55 @@ +#!/usr/bin/env python +# +# make_math_grb +# .math to .grb GUI wrapper +# +# Neil Gershenfeld +# CBA MIT 11/24/12 +# +# (c) Massachusetts Institute of Technology 2012 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,sys +from fab_set import fab_frame +from panel_control import control_panel +from panel_math import math_panel +from panel_math_png import math_png_panel +from panel_png_grb import png_grb_panel +# +# command line +# +print "command line: make_math_grb [input_file [size]]" +print " input_file = input .math file (optional)" +print " size = image panel size (optional)" +# +# start wx +# +app = wx.App() +# +# add panels to frame +# +frame = fab_frame("make_math_rml",sys.argv) +frame.control_panel = control_panel(frame) +frame.sizer.Add(frame.control_panel,(0,0),span=(1,4),flag=wx.ALIGN_CENTER_HORIZONTAL) +frame.math_panel = math_panel(frame) +frame.sizer.Add(frame.math_panel,(1,0)) +frame.png_panel = math_png_panel(frame) +frame.sizer.Add(frame.png_panel,(1,1)) +frame.grb_panel = png_grb_panel(frame) +frame.sizer.Add(frame.grb_panel,(1,2)) +# +# defaults +# +#frame.set_math_grb() +# +# fit and show frame +# +frame.Fit() +frame.Show() +# +# start mainloop +# +app.MainLoop() diff --git a/src/guis/make_math_ord b/src/guis/make_math_ord new file mode 100755 index 0000000..aa6b34d --- /dev/null +++ b/src/guis/make_math_ord @@ -0,0 +1,58 @@ +#!/usr/bin/env python +# +# make_math_ord +# .math to .ord GUI wrapper +# +# Neil Gershenfeld +# CBA MIT 4/25/12 +# +# (c) Massachusetts Institute of Technology 2012 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,sys +from fab_set import fab_frame +from panel_control import control_panel +from panel_math import math_panel +from panel_math_png import math_png_panel +from panel_png_path import png_path_panel +from panel_path_ord import path_ord_panel +# +# command line +# +print "command line: make_math_ord [input_file [size]]" +print " input_file = input .math file (optional)" +print " size = image panel size (optional)" +# +# start wx +# +app = wx.App() +# +# add panels to frame +# +frame = fab_frame("make_math_ord",sys.argv) +frame.control_panel = control_panel(frame) +frame.sizer.Add(frame.control_panel,(0,0),span=(1,4),flag=wx.ALIGN_CENTER_HORIZONTAL) +frame.math_panel = math_panel(frame) +frame.sizer.Add(frame.math_panel,(1,0)) +frame.png_panel = math_png_panel(frame) +frame.sizer.Add(frame.png_panel,(1,1)) +frame.path_panel = png_path_panel(frame) +frame.sizer.Add(frame.path_panel,(1,2)) +frame.ord_panel = path_ord_panel(frame) +frame.sizer.Add(frame.ord_panel,(1,3)) +# +# defaults +# +frame.set_math_ord() +# +# fit and show frame +# +frame.Fit() +frame.Show() +# +# start mainloop +# +app.MainLoop() diff --git a/src/guis/make_math_rml b/src/guis/make_math_rml new file mode 100755 index 0000000..6d94a0c --- /dev/null +++ b/src/guis/make_math_rml @@ -0,0 +1,58 @@ +#!/usr/bin/env python +# +# make_math_rml +# .math to .rml GUI wrapper +# +# Neil Gershenfeld +# CBA MIT 4/25/12 +# +# (c) Massachusetts Institute of Technology 2012 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,sys +from fab_set import fab_frame +from panel_control import control_panel +from panel_math import math_panel +from panel_math_png import math_png_panel +from panel_png_path import png_path_panel +from panel_path_rml import path_rml_panel +# +# command line +# +print "command line: make_math_rml [input_file [size]]" +print " input_file = input .math file (optional)" +print " size = image panel size (optional)" +# +# start wx +# +app = wx.App() +# +# add panels to frame +# +frame = fab_frame("make_math_rml",sys.argv) +frame.control_panel = control_panel(frame) +frame.sizer.Add(frame.control_panel,(0,0),span=(1,4),flag=wx.ALIGN_CENTER_HORIZONTAL) +frame.math_panel = math_panel(frame) +frame.sizer.Add(frame.math_panel,(1,0)) +frame.math_png_panel = math_png_panel(frame) +frame.sizer.Add(frame.math_png_panel,(1,1)) +frame.png_path_panel = png_path_panel(frame) +frame.sizer.Add(frame.png_path_panel,(1,2)) +frame.rml_panel = path_rml_panel(frame) +frame.sizer.Add(frame.rml_panel,(1,3)) +# +# defaults +# +frame.set_math_rml() +# +# fit and show frame +# +frame.Fit() +frame.Show() +# +# start mainloop +# +app.MainLoop() diff --git a/src/guis/make_math_sbp b/src/guis/make_math_sbp new file mode 100755 index 0000000..306969a --- /dev/null +++ b/src/guis/make_math_sbp @@ -0,0 +1,58 @@ +#!/usr/bin/env python +# +# make_math_sbp +# .math to .sbp GUI wrapper +# +# Neil Gershenfeld +# CBA MIT 4/25/12 +# +# (c) Massachusetts Institute of Technology 2012 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,sys +from fab_set import fab_frame +from panel_control import control_panel +from panel_math import math_panel +from panel_math_png import math_png_panel +from panel_png_path import png_path_panel +from panel_path_sbp import path_sbp_panel +# +# command line +# +print "command line: make_math_sbp [input_file [size]]" +print " input_file = input .math file (optional)" +print " size = image panel size (optional)" +# +# start wx +# +app = wx.App() +# +# add panels to frame +# +frame = fab_frame("make_math_sbp",sys.argv) +frame.control_panel = control_panel(frame) +frame.sizer.Add(frame.control_panel,(0,0),span=(1,4),flag=wx.ALIGN_CENTER_HORIZONTAL) +frame.math_panel = math_panel(frame) +frame.sizer.Add(frame.math_panel,(1,0)) +frame.math_png_panel = math_png_panel(frame) +frame.sizer.Add(frame.math_png_panel,(1,1)) +frame.png_path_panel = png_path_panel(frame) +frame.sizer.Add(frame.png_path_panel,(1,2)) +frame.sbp_panel = path_sbp_panel(frame) +frame.sizer.Add(frame.sbp_panel,(1,3)) +# +# defaults +# +frame.set_math_sbp() +# +# fit and show frame +# +frame.Fit() +frame.Show() +# +# start mainloop +# +app.MainLoop() diff --git a/src/guis/make_math_stl b/src/guis/make_math_stl new file mode 100755 index 0000000..4f472ef --- /dev/null +++ b/src/guis/make_math_stl @@ -0,0 +1,52 @@ +#!/usr/bin/env python +# +# make_math_stl +# .math to .stl GUI wrapper +# +# Neil Gershenfeld +# CBA MIT 9/25/12 +# +# (c) Massachusetts Institute of Technology 2012 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,sys +from fab_set import fab_frame +from panel_control import control_panel +from panel_math import math_panel +from panel_math_stl import math_stl_panel +# +# command line +# +print "command line: make_math_stl [input_file [size]]" +print " input_file = input .math file (optional)" +print " size = image panel size (optional)" +# +# start wx +# +app = wx.App() +# +# add panels to frame +# +frame = fab_frame("make_math_stl",sys.argv) +frame.control_panel = control_panel(frame) +frame.sizer.Add(frame.control_panel,(0,0),span=(1,2),flag=wx.ALIGN_CENTER_HORIZONTAL) +frame.math_panel = math_panel(frame) +frame.sizer.Add(frame.math_panel,(1,0)) +frame.stl_panel = math_stl_panel(frame) +frame.sizer.Add(frame.stl_panel,(1,1)) +# +# defaults +# +frame.defaults = {} +# +# fit and show frame +# +frame.Fit() +frame.Show() +# +# start mainloop +# +app.MainLoop() diff --git a/src/guis/make_math_uni b/src/guis/make_math_uni new file mode 100755 index 0000000..aad7b87 --- /dev/null +++ b/src/guis/make_math_uni @@ -0,0 +1,58 @@ +#!/usr/bin/env python +# +# make_math_uni +# .math to .uni GUI wrapper +# +# Neil Gershenfeld +# CBA MIT 4/25/12 +# +# (c) Massachusetts Institute of Technology 2012 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,sys +from fab_set import fab_frame +from panel_control import control_panel +from panel_math import math_panel +from panel_math_png import math_png_panel +from panel_png_path import png_path_panel +from panel_path_uni import path_uni_panel +# +# command line +# +print "command line: make_math_uni [input_file [size]]" +print " input_file = input .math file (optional)" +print " size = image panel size (optional)" +# +# start wx +# +app = wx.App() +# +# add panels to frame +# +frame = fab_frame("make_math_uni",sys.argv) +frame.control_panel = control_panel(frame) +frame.sizer.Add(frame.control_panel,(0,0),span=(1,4),flag=wx.ALIGN_CENTER_HORIZONTAL) +frame.math_panel = math_panel(frame) +frame.sizer.Add(frame.math_panel,(1,0)) +frame.png_panel = math_png_panel(frame) +frame.sizer.Add(frame.png_panel,(1,1)) +frame.path_panel = png_path_panel(frame) +frame.sizer.Add(frame.path_panel,(1,2)) +frame.uni_panel = path_uni_panel(frame) +frame.sizer.Add(frame.uni_panel,(1,3)) +# +# defaults +# +frame.set_math_uni() +# +# fit and show frame +# +frame.Fit() +frame.Show() +# +# start mainloop +# +app.MainLoop() diff --git a/src/guis/make_png_camm b/src/guis/make_png_camm new file mode 100755 index 0000000..6df9e0d --- /dev/null +++ b/src/guis/make_png_camm @@ -0,0 +1,55 @@ +#!/usr/bin/env python +# +# make_png_camm +# .png to .camm GUI wrapper +# +# Neil Gershenfeld +# CBA MIT 2/19/11 +# +# (c) Massachusetts Institute of Technology 2011 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,sys +from fab_set import fab_frame +from panel_control import control_panel +from panel_png import png_panel +from panel_png_path import png_path_panel +from panel_path_camm import path_camm_panel +# +# command line +# +print "command line: make_png_camm [input_file [size]]" +print " input_file = input .camm file (optional)" +print " size = image panel size (optional)" +# +# start wx +# +app = wx.App() +# +# add panels to frame +# +frame = fab_frame("make_png_camm",sys.argv) +frame.control_panel = control_panel(frame) +frame.sizer.Add(frame.control_panel,(0,0),span=(1,3),flag=wx.ALIGN_CENTER_HORIZONTAL) +frame.camm_panel = path_camm_panel(frame) +frame.sizer.Add(frame.camm_panel,(1,2)) +frame.png_path_panel = png_path_panel(frame) +frame.sizer.Add(frame.png_path_panel,(1,1)) +frame.png_panel = png_panel(frame) +frame.sizer.Add(frame.png_panel,(1,0)) +# +# defaults +# +frame.set_png_camm() +# +# fit and show frame +# +frame.Fit() +frame.Show() +# +# start mainloop +# +app.MainLoop() diff --git a/src/guis/make_png_drl b/src/guis/make_png_drl new file mode 100755 index 0000000..b10543b --- /dev/null +++ b/src/guis/make_png_drl @@ -0,0 +1,52 @@ +#!/usr/bin/env python +# +# make_png_grb +# .png to .grb GUI wrapper +# +# Neil Gershenfeld +# CBA MIT 11/23/12 +# +# (c) Massachusetts Institute of Technology 2012 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,sys +from fab_set import fab_frame +from panel_control import control_panel +from panel_png import png_panel +from panel_png_drl import png_drl_panel +# +# command line +# +print "command line: make_png_drl [input_file [size]]" +print " input_file = input .png file (optional)" +print " size = image panel size (optional)" +# +# start wx +# +app = wx.App() +# +# add panels to frame +# +frame = fab_frame("make_png_drl",sys.argv) +frame.control_panel = control_panel(frame) +frame.sizer.Add(frame.control_panel,(0,0),span=(1,2),flag=wx.ALIGN_CENTER_HORIZONTAL) +frame.drl_panel = png_drl_panel(frame) +frame.sizer.Add(frame.drl_panel,(1,1)) +frame.png_panel = png_panel(frame) +frame.sizer.Add(frame.png_panel,(1,0)) +# +# set defaults +# +#frame.set_png_drl() +# +# fit and show frame +# +frame.Fit() +frame.Show() +# +# start mainloop +# +app.MainLoop() diff --git a/src/guis/make_png_dxf b/src/guis/make_png_dxf new file mode 100755 index 0000000..f27fa95 --- /dev/null +++ b/src/guis/make_png_dxf @@ -0,0 +1,55 @@ +#!/usr/bin/env python +# +# make_png_dxf +# .png to .dxf GUI wrapper +# +# Neil Gershenfeld +# CBA MIT 2/19/12 +# +# (c) Massachusetts Institute of Technology 2011 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,sys +from fab_set import fab_frame +from panel_control import control_panel +from panel_png import png_panel +from panel_png_path import png_path_panel +from panel_path_dxf import path_dxf_panel +# +# command line +# +print "command line: make_png_dxf [input_file [size]]" +print " input_file = input .dxf file (optional)" +print " size = image panel size (optional)" +# +# start wx +# +app = wx.App() +# +# add panels to frame +# +frame = fab_frame("make_png_dxf",sys.argv) +frame.control_panel = control_panel(frame) +frame.sizer.Add(frame.control_panel,(0,0),span=(1,3),flag=wx.ALIGN_CENTER_HORIZONTAL) +frame.dxf_panel = path_dxf_panel(frame) +frame.sizer.Add(frame.dxf_panel,(1,2)) +frame.path_panel = png_path_panel(frame) +frame.sizer.Add(frame.path_panel,(1,1)) +frame.png_panel = png_panel(frame) +frame.sizer.Add(frame.png_panel,(1,0)) +# +# defaults +# +#frame.set_png_dxf() +# +# fit and show frame +# +frame.Fit() +frame.Show() +# +# start mainloop +# +app.MainLoop() diff --git a/src/guis/make_png_epi b/src/guis/make_png_epi new file mode 100755 index 0000000..44884e3 --- /dev/null +++ b/src/guis/make_png_epi @@ -0,0 +1,55 @@ +#!/usr/bin/env python +# +# make_png_epi +# .png to .epi GUI wrapper +# +# Neil Gershenfeld +# CBA MIT 1/24/11 +# +# (c) Massachusetts Institute of Technology 2011 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,sys +from fab_set import fab_frame +from panel_control import control_panel +from panel_png import png_panel +from panel_png_path import png_path_panel +from panel_path_epi import path_epi_panel +# +# command line +# +print "command line: make_png_epi [input_file [size]]" +print " input_file = input .png file (optional)" +print " size = image panel size (optional)" +# +# start wx +# +app = wx.App() +# +# add panels to frame +# +frame = fab_frame("make_png_epi",sys.argv) +frame.control_panel = control_panel(frame) +frame.sizer.Add(frame.control_panel,(0,0),span=(1,3),flag=wx.ALIGN_CENTER_HORIZONTAL) +frame.epi_panel = path_epi_panel(frame) +frame.sizer.Add(frame.epi_panel,(1,2)) +frame.path_panel = png_path_panel(frame) +frame.sizer.Add(frame.path_panel,(1,1)) +frame.png_panel = png_panel(frame) +frame.sizer.Add(frame.png_panel,(1,0)) +# +# set defaults +# +frame.set_png_epi() +# +# fit and show frame +# +frame.Fit() +frame.Show() +# +# start mainloop +# +app.MainLoop() diff --git a/src/guis/make_png_epi_halftone b/src/guis/make_png_epi_halftone new file mode 100755 index 0000000..70ffec8 --- /dev/null +++ b/src/guis/make_png_epi_halftone @@ -0,0 +1,55 @@ +#!/usr/bin/env python +# +# make_png_epi_halftone +# .png to .epi halftone GUI wrapper +# +# Neil Gershenfeld +# CBA MIT 3/12/11 +# +# (c) Massachusetts Institute of Technology 2011 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,sys +from fab_set import fab_frame +from panel_control import control_panel +from panel_png import png_panel +from panel_png_path_halftone import png_path_halftone_panel +from panel_path_epi import path_epi_panel +# +# command line +# +print "command line: make_png_epi_halftone [input_file [size]]" +print " input_file = input .png file (optional)" +print " size = image panel size (optional)" +# +# start wx +# +app = wx.App() +# +# add panels to frame +# +frame = fab_frame("make_png_epi_halftone",sys.argv) +frame.control_panel = control_panel(frame) +frame.sizer.Add(frame.control_panel,(0,0),span=(1,3),flag=wx.ALIGN_CENTER_HORIZONTAL) +frame.epi_panel = path_epi_panel(frame) +frame.sizer.Add(frame.epi_panel,(1,2)) +frame.path_panel = png_path_halftone_panel(frame) +frame.sizer.Add(frame.path_panel,(1,1)) +frame.png_panel = png_panel(frame) +frame.sizer.Add(frame.png_panel,(1,0)) +# +# defaults +# +frame.set_png_epi_halftone() +# +# fit and show frame +# +frame.Fit() +frame.Show() +# +# start mainloop +# +app.MainLoop() diff --git a/src/guis/make_png_eps b/src/guis/make_png_eps new file mode 100755 index 0000000..8f45dca --- /dev/null +++ b/src/guis/make_png_eps @@ -0,0 +1,58 @@ +#!/usr/bin/env python +# +# make_png_eps +# .png to .eps GUI wrapper +# +# Neil Gershenfeld 7/4/13 +# (c) Massachusetts Institute of Technology 2013 +# +# This work may be reproduced, modified, distributed, +# performed, and displayed for any purpose, but must +# acknowledge the fab modules project. Copyright is +# retained and must be preserved. The work is provided +# as is; no warranty is provided, and users accept all +# liability. +# +# imports +# +import wx,sys +from fab_set import fab_frame +from panel_control import control_panel +from panel_png import png_panel +from panel_png_path import png_path_panel +from panel_path_eps import path_eps_panel +# +# command line +# +print "command line: make_png_eps [input_file [size]]" +print " input_file = input .eps file (optional)" +print " size = image panel size (optional)" +# +# start wx +# +app = wx.App() +# +# add panels to frame +# +frame = fab_frame("make_png_eps",sys.argv) +frame.control_panel = control_panel(frame) +frame.sizer.Add(frame.control_panel,(0,0),span=(1,3),flag=wx.ALIGN_CENTER_HORIZONTAL) +frame.eps_panel = path_eps_panel(frame) +frame.sizer.Add(frame.eps_panel,(1,2)) +frame.path_panel = png_path_panel(frame) +frame.sizer.Add(frame.path_panel,(1,1)) +frame.png_panel = png_panel(frame) +frame.sizer.Add(frame.png_panel,(1,0)) +# +# defaults +# +frame.set_png_eps() +# +# fit and show frame +# +frame.Fit() +frame.Show() +# +# start mainloop +# +app.MainLoop() diff --git a/src/guis/make_png_eps_halftone b/src/guis/make_png_eps_halftone new file mode 100755 index 0000000..d39a1e2 --- /dev/null +++ b/src/guis/make_png_eps_halftone @@ -0,0 +1,58 @@ +#!/usr/bin/env python +# +# make_png_eps_halftone +# .png to .eps halftone GUI wrapper +# +# Neil Gershenfeld 7/4/13 +# (c) Massachusetts Institute of Technology 2013 +# +# This work may be reproduced, modified, distributed, +# performed, and displayed for any purpose, but must +# acknowledge the fab modules project. Copyright is +# retained and must be preserved. The work is provided +# as is; no warranty is provided, and users accept all +# liability. +# +# imports +# +import wx,sys +from fab_set import fab_frame +from panel_control import control_panel +from panel_png import png_panel +from panel_png_path_halftone import png_path_halftone_panel +from panel_path_eps import path_eps_panel +# +# command line +# +print "command line: make_png_eps_halftone [input_file [size]]" +print " input_file = input .png file (optional)" +print " size = image panel size (optional)" +# +# start wx +# +app = wx.App() +# +# add panels to frame +# +frame = fab_frame("make_png_eps_halftone",sys.argv) +frame.control_panel = control_panel(frame) +frame.sizer.Add(frame.control_panel,(0,0),span=(1,3),flag=wx.ALIGN_CENTER_HORIZONTAL) +frame.eps_panel = path_eps_panel(frame) +frame.sizer.Add(frame.eps_panel,(1,2)) +frame.path_panel = png_path_halftone_panel(frame) +frame.sizer.Add(frame.path_panel,(1,1)) +frame.png_panel = png_panel(frame) +frame.sizer.Add(frame.png_panel,(1,0)) +# +# defaults +# +#frame.set_png_eps_halftone() +# +# fit and show frame +# +frame.Fit() +frame.Show() +# +# start mainloop +# +app.MainLoop() diff --git a/src/guis/make_png_g b/src/guis/make_png_g new file mode 100755 index 0000000..881bc94 --- /dev/null +++ b/src/guis/make_png_g @@ -0,0 +1,55 @@ +#!/usr/bin/env python +# +# make_png_g +# .png to .g GUI wrapper +# +# Neil Gershenfeld +# CBA MIT 2/19/11 +# +# (c) Massachusetts Institute of Technology 2011 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,sys +from fab_set import fab_frame +from panel_control import control_panel +from panel_png import png_panel +from panel_png_path import png_path_panel +from panel_path_g import path_g_panel +# +# command line +# +print "command line: make_png_g [input_file [size]]" +print " input_file = input .g file (optional)" +print " size = image panel size (optional)" +# +# start wx +# +app = wx.App() +# +# add panels to frame +# +frame = fab_frame("make_png_g",sys.argv) +frame.control_panel = control_panel(frame) +frame.sizer.Add(frame.control_panel,(0,0),span=(1,3),flag=wx.ALIGN_CENTER_HORIZONTAL) +frame.g_panel = path_g_panel(frame) +frame.sizer.Add(frame.g_panel,(1,2)) +frame.png_path_panel = png_path_panel(frame) +frame.sizer.Add(frame.png_path_panel,(1,1)) +frame.png_panel = png_panel(frame) +frame.sizer.Add(frame.png_panel,(1,0)) +# +# defaults +# +frame.set_png_g() +# +# fit and show frame +# +frame.Fit() +frame.Show() +# +# start mainloop +# +app.MainLoop() diff --git a/src/guis/make_png_grb b/src/guis/make_png_grb new file mode 100755 index 0000000..f19c614 --- /dev/null +++ b/src/guis/make_png_grb @@ -0,0 +1,52 @@ +#!/usr/bin/env python +# +# make_png_grb +# .png to .grb GUI wrapper +# +# Neil Gershenfeld +# CBA MIT 11/23/12 +# +# (c) Massachusetts Institute of Technology 2012 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,sys +from fab_set import fab_frame +from panel_control import control_panel +from panel_png import png_panel +from panel_png_grb import png_grb_panel +# +# command line +# +print "command line: make_png_grb [input_file [size]]" +print " input_file = input .png file (optional)" +print " size = image panel size (optional)" +# +# start wx +# +app = wx.App() +# +# add panels to frame +# +frame = fab_frame("make_png_grb",sys.argv) +frame.control_panel = control_panel(frame) +frame.sizer.Add(frame.control_panel,(0,0),span=(1,2),flag=wx.ALIGN_CENTER_HORIZONTAL) +frame.grb_panel = png_grb_panel(frame) +frame.sizer.Add(frame.grb_panel,(1,1)) +frame.png_panel = png_panel(frame) +frame.sizer.Add(frame.png_panel,(1,0)) +# +# set defaults +# +#frame.set_png_grb() +# +# fit and show frame +# +frame.Fit() +frame.Show() +# +# start mainloop +# +app.MainLoop() diff --git a/src/guis/make_png_oms b/src/guis/make_png_oms new file mode 100755 index 0000000..ed90da3 --- /dev/null +++ b/src/guis/make_png_oms @@ -0,0 +1,55 @@ +#!/usr/bin/env python +# +# make_png_oms +# .png to .oms GUI wrapper +# +# Neil Gershenfeld +# CBA MIT 5/25/13 +# +# (c) Massachusetts Institute of Technology 2013 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,sys +from fab_set import fab_frame +from panel_control import control_panel +from panel_png import png_panel +from panel_png_path import png_path_panel +from panel_path_oms import path_oms_panel +# +# command line +# +print "command line: make_png_oms [input_file [size]]" +print " input_file = input .png file (optional)" +print " size = image panel size (optional)" +# +# start wx +# +app = wx.App() +# +# add panels to frame +# +frame = fab_frame("make_png_oms",sys.argv) +frame.control_panel = control_panel(frame) +frame.sizer.Add(frame.control_panel,(0,0),span=(1,3),flag=wx.ALIGN_CENTER_HORIZONTAL) +frame.oms_panel = path_oms_panel(frame) +frame.sizer.Add(frame.oms_panel,(1,2)) +frame.path_panel = png_path_panel(frame) +frame.sizer.Add(frame.path_panel,(1,1)) +frame.png_panel = png_panel(frame) +frame.sizer.Add(frame.png_panel,(1,0)) +# +# set defaults +# +frame.set_png_oms() +# +# fit and show frame +# +frame.Fit() +frame.Show() +# +# start mainloop +# +app.MainLoop() diff --git a/src/guis/make_png_ord b/src/guis/make_png_ord new file mode 100755 index 0000000..6c956eb --- /dev/null +++ b/src/guis/make_png_ord @@ -0,0 +1,55 @@ +#!/usr/bin/env python +# +# make_png_ord +# .png to .ord GUI wrapper +# +# Neil Gershenfeld +# CBA MIT 2/19/11 +# +# (c) Massachusetts Institute of Technology 2011 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,sys +from fab_set import fab_frame +from panel_control import control_panel +from panel_png import png_panel +from panel_png_path import png_path_panel +from panel_path_ord import path_ord_panel +# +# command line +# +print "command line: make_png_ord [input_file [size]]" +print " input_file = input .ord file (optional)" +print " size = image panel size (optional)" +# +# start wx +# +app = wx.App() +# +# add panels to frame +# +frame = fab_frame("make_png_ord",sys.argv) +frame.control_panel = control_panel(frame) +frame.sizer.Add(frame.control_panel,(0,0),span=(1,3),flag=wx.ALIGN_CENTER_HORIZONTAL) +frame.ord_panel = path_ord_panel(frame) +frame.sizer.Add(frame.ord_panel,(1,2)) +frame.path_panel = png_path_panel(frame) +frame.sizer.Add(frame.path_panel,(1,1)) +frame.png_panel = png_panel(frame) +frame.sizer.Add(frame.png_panel,(1,0)) +# +# defaults +# +frame.set_png_ord() +# +# fit and show frame +# +frame.Fit() +frame.Show() +# +# start mainloop +# +app.MainLoop() diff --git a/src/guis/make_png_plt b/src/guis/make_png_plt new file mode 100755 index 0000000..2729705 --- /dev/null +++ b/src/guis/make_png_plt @@ -0,0 +1,55 @@ +#!/usr/bin/env python +# +# make_png_plt +# .png to .plt GUI wrapper +# +# Neil Gershenfeld +# CBA MIT 2/19/11 +# +# (c) Massachusetts Institute of Technology 2011 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,sys +from fab_set import fab_frame +from panel_control import control_panel +from panel_png import png_panel +from panel_png_path import png_path_panel +from panel_path_plt import path_plt_panel +# +# command line +# +print "command line: make_png_plt [input_file [size]]" +print " input_file = input .plt file (optional)" +print " size = image panel size (optional)" +# +# start wx +# +app = wx.App() +# +# add panels to frame +# +frame = fab_frame("make_png_plt",sys.argv) +frame.control_panel = control_panel(frame) +frame.sizer.Add(frame.control_panel,(0,0),span=(1,3),flag=wx.ALIGN_CENTER_HORIZONTAL) +frame.plt_panel = path_plt_panel(frame) +frame.sizer.Add(frame.plt_panel,(1,2)) +frame.path_panel = png_path_panel(frame) +frame.sizer.Add(frame.path_panel,(1,1)) +frame.png_panel = png_panel(frame) +frame.sizer.Add(frame.png_panel,(1,0)) +# +# defaults +# +frame.set_png_plt() +# +# fit and show frame +# +frame.Fit() +frame.Show() +# +# start mainloop +# +app.MainLoop() diff --git a/src/guis/make_png_png b/src/guis/make_png_png new file mode 100755 index 0000000..b29550e --- /dev/null +++ b/src/guis/make_png_png @@ -0,0 +1,52 @@ +#!/usr/bin/env python +# +# make_png_png +# .png to .png GUI wrapper +# +# Neil Gershenfeld +# CBA MIT 2/19/11 +# +# (c) Massachusetts Institute of Technology 2011 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,sys +from fab_set import fab_frame +from panel_control import control_panel +from panel_png import png_panel +from panel_png_png import png_png_panel +# +# command line +# +print "command line: make_png_png [input_file [size]]" +print " input_file = input .png file (optional)" +print " size = image panel size (optional)" +# +# start wx +# +app = wx.App() +# +# add panels to frame +# +frame = fab_frame("make_png_png",sys.argv) +frame.control_panel = control_panel(frame) +frame.sizer.Add(frame.control_panel,(0,0),span=(1,3),flag=wx.ALIGN_CENTER_HORIZONTAL) +frame.png_png_panel = png_png_panel(frame) +frame.sizer.Add(frame.png_png_panel,(1,1)) +frame.png_panel = png_panel(frame) +frame.sizer.Add(frame.png_panel,(1,0)) +# +# defaults +# +frame.defaults = {} +# +# fit and show frame +# +frame.Fit() +frame.Show() +# +# start mainloop +# +app.MainLoop() diff --git a/src/guis/make_png_rml b/src/guis/make_png_rml new file mode 100755 index 0000000..39ba4f9 --- /dev/null +++ b/src/guis/make_png_rml @@ -0,0 +1,55 @@ +#!/usr/bin/env python +# +# make_png_rml +# .png to .rml GUI wrapper +# +# Neil Gershenfeld +# CBA MIT 2/19/11 +# +# (c) Massachusetts Institute of Technology 2011 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,sys +from fab_set import fab_frame +from panel_control import control_panel +from panel_png import png_panel +from panel_png_path import png_path_panel +from panel_path_rml import path_rml_panel +# +# command line +# +print "command line: make_png_rml [input_file [size]]" +print " input_file = input .rml file (optional)" +print " size = image panel size (optional)" +# +# start wx +# +app = wx.App() +# +# add panels to frame +# +frame = fab_frame("make_png_rml",sys.argv) +frame.control_panel = control_panel(frame) +frame.sizer.Add(frame.control_panel,(0,0),span=(1,3),flag=wx.ALIGN_CENTER_HORIZONTAL) +frame.rml_panel = path_rml_panel(frame) +frame.sizer.Add(frame.rml_panel,(1,2)) +frame.png_path_panel = png_path_panel(frame) +frame.sizer.Add(frame.png_path_panel,(1,1)) +frame.png_panel = png_panel(frame) +frame.sizer.Add(frame.png_panel,(1,0)) +# +# defaults +# +frame.set_png_rml() +# +# fit and show frame +# +frame.Fit() +frame.Show() +# +# start mainloop +# +app.MainLoop() diff --git a/src/guis/make_png_sbp b/src/guis/make_png_sbp new file mode 100755 index 0000000..3095eea --- /dev/null +++ b/src/guis/make_png_sbp @@ -0,0 +1,55 @@ +#!/usr/bin/env python +# +# make_png_sbp +# .png to .sbp GUI wrapper +# +# Neil Gershenfeld +# CBA MIT 2/19/11 +# +# (c) Massachusetts Institute of Technology 2011 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,sys +from fab_set import fab_frame +from panel_control import control_panel +from panel_png import png_panel +from panel_png_path import png_path_panel +from panel_path_sbp import path_sbp_panel +# +# command line +# +print "command line: make_png_sbp [input_file [size]]" +print " input_file = input .sbp file (optional)" +print " size = image panel size (optional)" +# +# start wx +# +app = wx.App() +# +# add panels to frame +# +frame = fab_frame("make_png_sbp",sys.argv) +frame.control_panel = control_panel(frame) +frame.sizer.Add(frame.control_panel,(0,0),span=(1,3),flag=wx.ALIGN_CENTER_HORIZONTAL) +frame.sbp_panel = path_sbp_panel(frame) +frame.sizer.Add(frame.sbp_panel,(1,2)) +frame.png_path_panel = png_path_panel(frame) +frame.sizer.Add(frame.png_path_panel,(1,1)) +frame.png_panel = png_panel(frame) +frame.sizer.Add(frame.png_panel,(1,0)) +# +# defaults +# +frame.set_png_sbp() +# +# fit and show frame +# +frame.Fit() +frame.Show() +# +# start mainloop +# +app.MainLoop() diff --git a/src/guis/make_png_snap b/src/guis/make_png_snap new file mode 100755 index 0000000..b0d8787 --- /dev/null +++ b/src/guis/make_png_snap @@ -0,0 +1,55 @@ +#!/usr/bin/env python +# +# make_png_snap +# .png to MTM Snap GUI wrapper +# +# Neil Gershenfeld +# CBA MIT 11/24/11 +# +# (c) Massachusetts Institute of Technology 2011 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,sys +from fab_set import fab_frame +from panel_control import control_panel +from panel_png import png_panel +from panel_png_path import png_path_panel +from panel_path_snap import path_snap_panel +# +# command line +# +print "command line: make_png_snap [input_file [size]]" +print " input_file = input .png file (optional)" +print " size = image panel size (optional)" +# +# start wx +# +app = wx.App() +# +# add panels to frame +# +frame = fab_frame("make_png_snap",sys.argv) +frame.control_panel = control_panel(frame) +frame.sizer.Add(frame.control_panel,(0,0),span=(1,3),flag=wx.ALIGN_CENTER_HORIZONTAL) +frame.snap_panel = path_snap_panel(frame) +frame.sizer.Add(frame.snap_panel,(1,2)) +frame.path_panel = png_path_panel(frame) +frame.sizer.Add(frame.path_panel,(1,1)) +frame.png_panel = png_panel(frame) +frame.sizer.Add(frame.png_panel,(1,0)) +# +# defaults +# +#frame.set_png_snap() +# +# fit and show frame +# +frame.Fit() +frame.Show() +# +# start mainloop +# +app.MainLoop() diff --git a/src/guis/make_png_uni b/src/guis/make_png_uni new file mode 100755 index 0000000..e12e072 --- /dev/null +++ b/src/guis/make_png_uni @@ -0,0 +1,55 @@ +#!/usr/bin/env python +# +# make_png_uni +# .png to .uni GUI wrapper +# +# Neil Gershenfeld +# CBA MIT 1/24/11 +# +# (c) Massachusetts Institute of Technology 2011 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,sys +from fab_set import fab_frame +from panel_control import control_panel +from panel_png import png_panel +from panel_png_path import png_path_panel +from panel_path_uni import path_uni_panel +# +# command line +# +print "command line: make_png_uni [input_file [size]]" +print " input_file = input .png file (optional)" +print " size = image panel size (optional)" +# +# start wx +# +app = wx.App() +# +# add panels to frame +# +frame = fab_frame("make_png_uni",sys.argv) +frame.control_panel = control_panel(frame) +frame.sizer.Add(frame.control_panel,(0,0),span=(1,3),flag=wx.ALIGN_CENTER_HORIZONTAL) +frame.uni_panel = path_uni_panel(frame) +frame.sizer.Add(frame.uni_panel,(1,2)) +frame.path_panel = png_path_panel(frame) +frame.sizer.Add(frame.path_panel,(1,1)) +frame.png_panel = png_panel(frame) +frame.sizer.Add(frame.png_panel,(1,0)) +# +# defaults +# +frame.set_png_uni() +# +# fit and show frame +# +frame.Fit() +frame.Show() +# +# start mainloop +# +app.MainLoop() diff --git a/src/guis/make_png_uni_halftone b/src/guis/make_png_uni_halftone new file mode 100755 index 0000000..b955eb3 --- /dev/null +++ b/src/guis/make_png_uni_halftone @@ -0,0 +1,55 @@ +#!/usr/bin/env python +# +# make_png_uni_halftone +# .png to .uni halftone GUI wrapper +# +# Neil Gershenfeld +# CBA MIT 3/12/11 +# +# (c) Massachusetts Institute of Technology 2011 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,sys +from fab_set import fab_frame +from panel_control import control_panel +from panel_png import png_panel +from panel_png_path_halftone import png_path_halftone_panel +from panel_path_uni import path_uni_panel +# +# command line +# +print "command line: make_png_uni_halftone [input_file [size]]" +print " input_file = input .png file (optional)" +print " size = image panel size (optional)" +# +# start wx +# +app = wx.App() +# +# add panels to frame +# +frame = fab_frame("make_png_uni_halftone",sys.argv) +frame.control_panel = control_panel(frame) +frame.sizer.Add(frame.control_panel,(0,0),span=(1,3),flag=wx.ALIGN_CENTER_HORIZONTAL) +frame.uni_panel = path_uni_panel(frame) +frame.sizer.Add(frame.uni_panel,(1,2)) +frame.path_panel = png_path_halftone_panel(frame) +frame.sizer.Add(frame.path_panel,(1,1)) +frame.png_panel = png_panel(frame) +frame.sizer.Add(frame.png_panel,(1,0)) +# +# defaults +# +frame.set_png_uni_halftone() +# +# fit and show frame +# +frame.Fit() +frame.Show() +# +# start mainloop +# +app.MainLoop() diff --git a/src/guis/make_stl_g b/src/guis/make_stl_g new file mode 100755 index 0000000..7e21fc0 --- /dev/null +++ b/src/guis/make_stl_g @@ -0,0 +1,58 @@ +#!/usr/bin/env python +# +# make_stl_g +# .stl to .g GUI wrapper +# +# Neil Gershenfeld +# CBA MIT 3/6/11 +# +# (c) Massachusetts Institute of Technology 2011 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,sys +from fab_set import fab_frame +from panel_control import control_panel +from panel_stl import stl_panel +from panel_stl_png import stl_png_panel +from panel_png_path import png_path_panel +from panel_path_g import path_g_panel +# +# command line +# +print "command line: make_stl_g [input_file [size]]" +print " input_file = input .stl file (optional)" +print " size = image panel size (optional)" +# +# start wx +# +app = wx.App() +# +# add panels to frame +# +frame = fab_frame("make_stl_g",sys.argv) +frame.control_panel = control_panel(frame) +frame.sizer.Add(frame.control_panel,(0,0),span=(1,4),flag=wx.ALIGN_CENTER_HORIZONTAL) +frame.g_panel = path_g_panel(frame) +frame.sizer.Add(frame.g_panel,(1,3)) +frame.png_path_panel = png_path_panel(frame) +frame.sizer.Add(frame.png_path_panel,(1,2)) +frame.stl_png_panel = stl_png_panel(frame) +frame.sizer.Add(frame.stl_png_panel,(1,1)) +frame.stl_panel = stl_panel(frame) +frame.sizer.Add(frame.stl_panel,(1,0)) +# +# defaults +# +frame.set_stl_g() +# +# fit and show frame +# +frame.Fit() +frame.Show() +# +# start mainloop +# +app.MainLoop() diff --git a/src/guis/make_stl_png b/src/guis/make_stl_png new file mode 100755 index 0000000..8eed289 --- /dev/null +++ b/src/guis/make_stl_png @@ -0,0 +1,64 @@ +#!/usr/bin/env python +# +# make_stl_png +# .stl to .png GUI wrapper +# +# Neil Gershenfeld +# CBA MIT 3/6/11 +# +# (c) Massachusetts Institute of Technology 2011 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,sys +from fab_set import fab_frame +from panel_control import control_panel +from panel_stl import stl_panel +from panel_stl_png import stl_png_panel +# +# command line +# +print "command line: make_stl_png [input_file [size]]" +print " input_file = input .stl file (optional)" +print " size = image panel size (optional)" +# +# start wx +# +app = wx.App() +# +# add panels to frame +# +frame = fab_frame("make_stl_png",sys.argv) +frame.control_panel = control_panel(frame) +frame.sizer.Add(frame.control_panel,(0,0),span=(1,2),flag=wx.ALIGN_CENTER_HORIZONTAL) +frame.stl_panel = stl_panel(frame) +frame.sizer.Add(frame.stl_panel,(1,0)) +frame.stl_png_panel = stl_png_panel(frame) +frame.sizer.Add(frame.stl_png_panel,(1,1)) +# +# defaults +# +frame.defaults = {} +frame.control_panel.defaults.Append('inches') +frame.defaults["inches"] = "\ +self.stl_png_panel.units.SetValue('25.4');\ +self.stl_png_panel.resolution.SetValue('25');" +frame.control_panel.defaults.Append('mm') +frame.defaults["mm"] = "\ +self.stl_png_panel.units.SetValue('1');\ +self.stl_png_panel.resolution.SetValue('25');" +frame.control_panel.defaults.Append('cm') +frame.defaults["cm"] = "\ +self.stl_png_panel.units.SetValue('10');\ +self.stl_png_panel.resolution.SetValue('25');" +# +# fit and show frame +# +frame.Fit() +frame.Show() +# +# start mainloop +# +app.MainLoop() diff --git a/src/guis/make_stl_rml b/src/guis/make_stl_rml new file mode 100755 index 0000000..113e795 --- /dev/null +++ b/src/guis/make_stl_rml @@ -0,0 +1,58 @@ +#!/usr/bin/env python +# +# make_stl_rml +# .stl to .rml GUI wrapper +# +# Neil Gershenfeld +# CBA MIT 3/6/11 +# +# (c) Massachusetts Institute of Technology 2011 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,sys +from fab_set import fab_frame +from panel_control import control_panel +from panel_stl import stl_panel +from panel_stl_png import stl_png_panel +from panel_png_path import png_path_panel +from panel_path_rml import path_rml_panel +# +# command line +# +print "command line: make_stl_rml [input_file [size]]" +print " input_file = input .stl file (optional)" +print " size = image panel size (optional)" +# +# start wx +# +app = wx.App() +# +# add panels to frame +# +frame = fab_frame("make_stl_rml",sys.argv) +frame.control_panel = control_panel(frame) +frame.sizer.Add(frame.control_panel,(0,0),span=(1,4),flag=wx.ALIGN_CENTER_HORIZONTAL) +frame.rml_panel = path_rml_panel(frame) +frame.sizer.Add(frame.rml_panel,(1,3)) +frame.png_path_panel = png_path_panel(frame) +frame.sizer.Add(frame.png_path_panel,(1,2)) +frame.stl_png_panel = stl_png_panel(frame) +frame.sizer.Add(frame.stl_png_panel,(1,1)) +frame.stl_panel = stl_panel(frame) +frame.sizer.Add(frame.stl_panel,(1,0)) +# +# defaults +# +frame.set_stl_rml() +# +# fit and show frame +# +frame.Fit() +frame.Show() +# +# start mainloop +# +app.MainLoop() diff --git a/src/guis/make_stl_sbp b/src/guis/make_stl_sbp new file mode 100755 index 0000000..6853a15 --- /dev/null +++ b/src/guis/make_stl_sbp @@ -0,0 +1,58 @@ +#!/usr/bin/env python +# +# make_stl_sbp +# .stl to .sbp GUI wrapper +# +# Neil Gershenfeld +# CBA MIT 3/6/11 +# +# (c) Massachusetts Institute of Technology 2011 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,sys +from fab_set import fab_frame +from panel_control import control_panel +from panel_stl import stl_panel +from panel_stl_png import stl_png_panel +from panel_png_path import png_path_panel +from panel_path_sbp import path_sbp_panel +# +# command line +# +print "command line: make_stl_sbp [input_file [size]]" +print " input_file = input .stl file (optional)" +print " size = image panel size (optional)" +# +# start wx +# +app = wx.App() +# +# add panels to frame +# +frame = fab_frame("make_stl_sbp",sys.argv) +frame.control_panel = control_panel(frame) +frame.sizer.Add(frame.control_panel,(0,0),span=(1,4),flag=wx.ALIGN_CENTER_HORIZONTAL) +frame.sbp_panel = path_sbp_panel(frame) +frame.sizer.Add(frame.sbp_panel,(1,3)) +frame.png_path_panel = png_path_panel(frame) +frame.sizer.Add(frame.png_path_panel,(1,2)) +frame.stl_png_panel = stl_png_panel(frame) +frame.sizer.Add(frame.stl_png_panel,(1,1)) +frame.stl_panel = stl_panel(frame) +frame.sizer.Add(frame.stl_panel,(1,0)) +# +# defaults +# +frame.set_stl_sbp() +# +# fit and show frame +# +frame.Fit() +frame.Show() +# +# start mainloop +# +app.MainLoop() diff --git a/src/guis/make_stl_snap b/src/guis/make_stl_snap new file mode 100755 index 0000000..aedccc6 --- /dev/null +++ b/src/guis/make_stl_snap @@ -0,0 +1,58 @@ +#!/usr/bin/env python +# +# make_stl_snap +# .stl to MTM Snap GUI wrapper +# +# Neil Gershenfeld +# CBA MIT 11/24/11 +# +# (c) Massachusetts Institute of Technology 2011 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,sys +from fab_set import fab_frame +from panel_control import control_panel +from panel_stl import stl_panel +from panel_stl_png import stl_png_panel +from panel_png_path import png_path_panel +from panel_path_snap import path_snap_panel +# +# command line +# +print "command line: make_stl_snap [input_file [size]]" +print " input_file = input .stl file (optional)" +print " size = image panel size (optional)" +# +# start wx +# +app = wx.App() +# +# add panels to frame +# +frame = fab_frame("make_stl_snap",sys.argv) +frame.control_panel = control_panel(frame) +frame.sizer.Add(frame.control_panel,(0,0),span=(1,4),flag=wx.ALIGN_CENTER_HORIZONTAL) +frame.snap_panel = path_snap_panel(frame) +frame.sizer.Add(frame.snap_panel,(1,3)) +frame.path_panel = png_path_panel(frame) +frame.sizer.Add(frame.path_panel,(1,2)) +frame.stl_png_panel = stl_png_panel(frame) +frame.sizer.Add(frame.stl_png_panel,(1,1)) +frame.stl_panel = stl_panel(frame) +frame.sizer.Add(frame.stl_panel,(1,0)) +# +# defaults +# +#frame.set_stl_snap() +# +# fit and show frame +# +frame.Fit() +frame.Show() +# +# start mainloop +# +app.MainLoop() diff --git a/src/guis/make_svg_camm b/src/guis/make_svg_camm new file mode 100755 index 0000000..18d3e7f --- /dev/null +++ b/src/guis/make_svg_camm @@ -0,0 +1,55 @@ +#!/usr/bin/env python +# +# make_svg_camm +# .svg to .camm GUI wrapper +# +# Neil Gershenfeld +# CBA MIT 7/13/11 +# +# (c) Massachusetts Institute of Technology 2011 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,sys +from fab_set import fab_frame +from panel_control import control_panel +from panel_svg import svg_panel +from panel_svg_path import svg_path_panel +from panel_path_camm import path_camm_panel +# +# command line +# +print "command line: make_svg_camm [input_file [size]]" +print " input_file = input .svg file (optional)" +print " size = image panel size (optional)" +# +# start wx +# +app = wx.App() +# +# add panels to frame +# +frame = fab_frame("make_svg_camm",sys.argv) +frame.control_panel = control_panel(frame) +frame.sizer.Add(frame.control_panel,(0,0),span=(1,4),flag=wx.ALIGN_CENTER_HORIZONTAL) +frame.svg_panel = svg_panel(frame) +frame.sizer.Add(frame.svg_panel,(1,0)) +frame.path_panel = svg_path_panel(frame) +frame.sizer.Add(frame.path_panel,(1,1)) +frame.camm_panel = path_camm_panel(frame) +frame.sizer.Add(frame.camm_panel,(1,2)) +# +# defaults +# +frame.set_svg_camm() +# +# fit and show frame +# +frame.Fit() +frame.Show() +# +# start mainloop +# +app.MainLoop() diff --git a/src/guis/make_svg_epi b/src/guis/make_svg_epi new file mode 100755 index 0000000..9227dcc --- /dev/null +++ b/src/guis/make_svg_epi @@ -0,0 +1,55 @@ +#!/usr/bin/env python +# +# make_svg_epi +# .svg to .epi GUI wrapper +# +# Neil Gershenfeld +# CBA MIT 7/19/11 +# +# (c) Massachusetts Institute of Technology 2011 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,sys +from fab_set import fab_frame +from panel_control import control_panel +from panel_svg import svg_panel +from panel_svg_path import svg_path_panel +from panel_path_epi import path_epi_panel +# +# command line +# +print "command line: make_svg_epi [input_file [size]]" +print " input_file = input .svg file (optional)" +print " size = image panel size (optional)" +# +# start wx +# +app = wx.App() +# +# add panels to frame +# +frame = fab_frame("make_svg_epi",sys.argv) +frame.control_panel = control_panel(frame) +frame.sizer.Add(frame.control_panel,(0,0),span=(1,4),flag=wx.ALIGN_CENTER_HORIZONTAL) +frame.svg_panel = svg_panel(frame) +frame.sizer.Add(frame.svg_panel,(1,0)) +frame.path_panel = svg_path_panel(frame) +frame.sizer.Add(frame.path_panel,(1,1)) +frame.epi_panel = path_epi_panel(frame) +frame.sizer.Add(frame.epi_panel,(1,2)) +# +# defaults +# +frame.set_svg_epi() +# +# fit and show frame +# +frame.Fit() +frame.Show() +# +# start mainloop +# +app.MainLoop() diff --git a/src/guis/make_svg_g b/src/guis/make_svg_g new file mode 100755 index 0000000..7d5fb57 --- /dev/null +++ b/src/guis/make_svg_g @@ -0,0 +1,55 @@ +#!/usr/bin/env python +# +# make_svg_g +# .svg to .g GUI wrapper +# +# Neil Gershenfeld +# CBA MIT 7/19/11 +# +# (c) Massachusetts Institute of Technology 2011 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,sys +from fab_set import fab_frame +from panel_control import control_panel +from panel_svg import svg_panel +from panel_svg_path import svg_path_panel +from panel_path_g import path_g_panel +# +# command line +# +print "command line: make_svg_g [input_file [size]]" +print " input_file = input .svg file (optional)" +print " size = image panel size (optional)" +# +# start wx +# +app = wx.App() +# +# add panels to frame +# +frame = fab_frame("make_svg_g",sys.argv) +frame.control_panel = control_panel(frame) +frame.sizer.Add(frame.control_panel,(0,0),span=(1,4),flag=wx.ALIGN_CENTER_HORIZONTAL) +frame.svg_panel = svg_panel(frame) +frame.sizer.Add(frame.svg_panel,(1,0)) +frame.svg_path_panel = svg_path_panel(frame) +frame.sizer.Add(frame.svg_path_panel,(1,1)) +frame.g_panel = path_g_panel(frame) +frame.sizer.Add(frame.g_panel,(1,2)) +# +# defaults +# +frame.set_svg_g() +# +# fit and show frame +# +frame.Fit() +frame.Show() +# +# start mainloop +# +app.MainLoop() diff --git a/src/guis/make_svg_oms b/src/guis/make_svg_oms new file mode 100755 index 0000000..42a3eae --- /dev/null +++ b/src/guis/make_svg_oms @@ -0,0 +1,55 @@ +#!/usr/bin/env python +# +# make_svg_oms +# .svg to .oms GUI wrapper +# +# Neil Gershenfeld +# CBA MIT 5/25/13 +# +# (c) Massachusetts Institute of Technology 2013 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,sys +from fab_set import fab_frame +from panel_control import control_panel +from panel_svg import svg_panel +from panel_svg_path import svg_path_panel +from panel_path_oms import path_oms_panel +# +# command line +# +print "command line: make_svg_oms [input_file [size]]" +print " input_file = input .svg file (optional)" +print " size = image panel size (optional)" +# +# start wx +# +app = wx.App() +# +# add panels to frame +# +frame = fab_frame("make_svg_oms",sys.argv) +frame.control_panel = control_panel(frame) +frame.sizer.Add(frame.control_panel,(0,0),span=(1,4),flag=wx.ALIGN_CENTER_HORIZONTAL) +frame.svg_panel = svg_panel(frame) +frame.sizer.Add(frame.svg_panel,(1,0)) +frame.path_panel = svg_path_panel(frame) +frame.sizer.Add(frame.path_panel,(1,1)) +frame.oms_panel = path_oms_panel(frame) +frame.sizer.Add(frame.oms_panel,(1,2)) +# +# defaults +# +frame.set_svg_oms() +# +# fit and show frame +# +frame.Fit() +frame.Show() +# +# start mainloop +# +app.MainLoop() diff --git a/src/guis/make_svg_ord b/src/guis/make_svg_ord new file mode 100755 index 0000000..fcde224 --- /dev/null +++ b/src/guis/make_svg_ord @@ -0,0 +1,55 @@ +#!/usr/bin/env python +# +# make_svg_ord +# .svg to .ord GUI wrapper +# +# Neil Gershenfeld +# CBA MIT 7/24/11 +# +# (c) Massachusetts Institute of Technology 2011 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,sys +from fab_set import fab_frame +from panel_control import control_panel +from panel_svg import svg_panel +from panel_svg_path import svg_path_panel +from panel_path_ord import path_ord_panel +# +# command line +# +print "command line: make_svg_ord [input_file [size]]" +print " input_file = input .svg file (optional)" +print " size = image panel size (optional)" +# +# start wx +# +app = wx.App() +# +# add panels to frame +# +frame = fab_frame("make_svg_ord",sys.argv) +frame.control_panel = control_panel(frame) +frame.sizer.Add(frame.control_panel,(0,0),span=(1,4),flag=wx.ALIGN_CENTER_HORIZONTAL) +frame.svg_panel = svg_panel(frame) +frame.sizer.Add(frame.svg_panel,(1,0)) +frame.path_panel = svg_path_panel(frame) +frame.sizer.Add(frame.path_panel,(1,1)) +frame.ord_panel = path_ord_panel(frame) +frame.sizer.Add(frame.ord_panel,(1,2)) +# +# defaults +# +frame.set_svg_ord() +# +# fit and show frame +# +frame.Fit() +frame.Show() +# +# start mainloop +# +app.MainLoop() diff --git a/src/guis/make_svg_rml b/src/guis/make_svg_rml new file mode 100755 index 0000000..f3e1d1c --- /dev/null +++ b/src/guis/make_svg_rml @@ -0,0 +1,55 @@ +#!/usr/bin/env python +# +# make_svg_rml +# .svg to .rml GUI wrapper +# +# Neil Gershenfeld +# CBA MIT 7/24/11 +# +# (c) Massachusetts Institute of Technology 2011 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,sys +from fab_set import fab_frame +from panel_control import control_panel +from panel_svg import svg_panel +from panel_svg_path import svg_path_panel +from panel_path_rml import path_rml_panel +# +# command line +# +print "command line: make_svg_rml [input_file [size]]" +print " input_file = input .svg file (optional)" +print " size = image panel size (optional)" +# +# start wx +# +app = wx.App() +# +# add panels to frame +# +frame = fab_frame("make_svg_rml",sys.argv) +frame.control_panel = control_panel(frame) +frame.sizer.Add(frame.control_panel,(0,0),span=(1,4),flag=wx.ALIGN_CENTER_HORIZONTAL) +frame.svg_panel = svg_panel(frame) +frame.sizer.Add(frame.svg_panel,(1,0)) +frame.svg_path_panel = svg_path_panel(frame) +frame.sizer.Add(frame.svg_path_panel,(1,1)) +frame.rml_panel = path_rml_panel(frame) +frame.sizer.Add(frame.rml_panel,(1,2)) +# +# defaults +# +frame.set_svg_rml() +# +# fit and show frame +# +frame.Fit() +frame.Show() +# +# start mainloop +# +app.MainLoop() diff --git a/src/guis/make_svg_sbp b/src/guis/make_svg_sbp new file mode 100755 index 0000000..d70eaa4 --- /dev/null +++ b/src/guis/make_svg_sbp @@ -0,0 +1,55 @@ +#!/usr/bin/env python +# +# make_svg_sbp +# .svg to .sbp GUI wrapper +# +# Neil Gershenfeld +# CBA MIT 7/24/11 +# +# (c) Massachusetts Institute of Technology 2011 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,sys +from fab_set import fab_frame +from panel_control import control_panel +from panel_svg import svg_panel +from panel_svg_path import svg_path_panel +from panel_path_sbp import path_sbp_panel +# +# command line +# +print "command line: make_svg_sbp [input_file [size]]" +print " input_file = input .svg file (optional)" +print " size = image panel size (optional)" +# +# start wx +# +app = wx.App() +# +# add panels to frame +# +frame = fab_frame("make_svg_sbp",sys.argv) +frame.control_panel = control_panel(frame) +frame.sizer.Add(frame.control_panel,(0,0),span=(1,4),flag=wx.ALIGN_CENTER_HORIZONTAL) +frame.svg_panel = svg_panel(frame) +frame.sizer.Add(frame.svg_panel,(1,0)) +frame.svg_path_panel = svg_path_panel(frame) +frame.sizer.Add(frame.svg_path_panel,(1,1)) +frame.sbp_panel = path_sbp_panel(frame) +frame.sizer.Add(frame.sbp_panel,(1,2)) +# +# defaults +# +frame.set_svg_sbp() +# +# fit and show frame +# +frame.Fit() +frame.Show() +# +# start mainloop +# +app.MainLoop() diff --git a/src/guis/make_svg_snap b/src/guis/make_svg_snap new file mode 100755 index 0000000..a3802dc --- /dev/null +++ b/src/guis/make_svg_snap @@ -0,0 +1,55 @@ +#!/usr/bin/env python +# +# make_svg_snap +# .svg to MTM Snap GUI wrapper +# +# Neil Gershenfeld +# CBA MIT 11/24/11 +# +# (c) Massachusetts Institute of Technology 2011 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,sys +from fab_set import fab_frame +from panel_control import control_panel +from panel_svg import svg_panel +from panel_svg_path import svg_path_panel +from panel_path_snap import path_snap_panel +# +# command line +# +print "command line: make_svg_snap [input_file [size]]" +print " input_file = input .svg file (optional)" +print " size = image panel size (optional)" +# +# start wx +# +app = wx.App() +# +# add panels to frame +# +frame = fab_frame("make_svg_snap",sys.argv) +frame.control_panel = control_panel(frame) +frame.sizer.Add(frame.control_panel,(0,0),span=(1,4),flag=wx.ALIGN_CENTER_HORIZONTAL) +frame.svg_panel = svg_panel(frame) +frame.sizer.Add(frame.svg_panel,(1,0)) +frame.path_panel = svg_path_panel(frame) +frame.sizer.Add(frame.path_panel,(1,1)) +frame.snap_panel = path_snap_panel(frame) +frame.sizer.Add(frame.snap_panel,(1,2)) +# +# defaults +# +#frame.set_svg_snap() +# +# fit and show frame +# +frame.Fit() +frame.Show() +# +# start mainloop +# +app.MainLoop() diff --git a/src/guis/make_svg_uni b/src/guis/make_svg_uni new file mode 100755 index 0000000..4dd6f3c --- /dev/null +++ b/src/guis/make_svg_uni @@ -0,0 +1,55 @@ +#!/usr/bin/env python +# +# make_svg_uni +# .svg to .uni GUI wrapper +# +# Neil Gershenfeld +# CBA MIT 7/19/11 +# +# (c) Massachusetts Institute of Technology 2011 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,sys +from fab_set import fab_frame +from panel_control import control_panel +from panel_svg import svg_panel +from panel_svg_path import svg_path_panel +from panel_path_uni import path_uni_panel +# +# command line +# +print "command line: make_svg_uni [input_file [size]]" +print " input_file = input .svg file (optional)" +print " size = image panel size (optional)" +# +# start wx +# +app = wx.App() +# +# add panels to frame +# +frame = fab_frame("make_svg_camm",sys.argv) +frame.control_panel = control_panel(frame) +frame.sizer.Add(frame.control_panel,(0,0),span=(1,4),flag=wx.ALIGN_CENTER_HORIZONTAL) +frame.svg_panel = svg_panel(frame) +frame.sizer.Add(frame.svg_panel,(1,0)) +frame.path_panel = svg_path_panel(frame) +frame.sizer.Add(frame.path_panel,(1,1)) +frame.uni_panel = path_uni_panel(frame) +frame.sizer.Add(frame.uni_panel,(1,2)) +# +# defaults +# +frame.set_svg_uni() +# +# fit and show frame +# +frame.Fit() +frame.Show() +# +# start mainloop +# +app.MainLoop() diff --git a/src/guis/rml_send_gui b/src/guis/rml_send_gui new file mode 100755 index 0000000..52d8774 --- /dev/null +++ b/src/guis/rml_send_gui @@ -0,0 +1,524 @@ +#!/usr/bin/env python +# +# rml_send_gui +# graphical interface for sending jobs to the Roland Modela +# +# Brian Mayton +# MIT 2011-2014 +# +# (c) Massachusetts Institute of Technology 2011-2014 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. + +# imports +from __future__ import with_statement +import serial +import sys +import wx +import threading +import time +import math + +# global constants +RML_UNITS=40.0 +SPEED_TRAVERSE = 15.0 + +# utility functions +def dist(x1, y1, z1, x2, y2, z2): + return math.sqrt( + pow(x1-x2, 2.0) + + pow(y1-y2, 2.0) + + pow(z1-z2, 2.0) + ) + +class RMLSender: + """This class implements the parsing of RML files and sending to the + Modela.""" + + def __init__(self, port="/dev/ttyUSB0"): + self.serial = serial.Serial(port, baudrate=9600, rtscts=True, timeout=0) + self.cmds = [] + + self.xr=[0,1]; self.yr=[0,1]; self.zr=[0,1] + self.paths = [] + self.segments_done = [] + self.traverses = [] + self.traverses_done = [] + + self.speed_feed = 15.0 + self.speed_plunge = 5.0 + + self.total_distance = 1.0 + self.distance_milled = 0.0 + + self.total_time = 1.0 + self.time_remaining = 1.0 + self.time_start = None + + self.current_cmd = "" + self.cur_cmd_start = time.time() + self.cur_cmd_duration = 0.0 + + self.running = False + self.thread = threading.Thread(target=self.thread_fn) + self.should_stop = threading.Event() + self.done = threading.Event() + self.aborted = threading.Event() + self.lock = threading.Lock() + + def load_file(self, filename): + self.cmds = [] + f = open(filename, "r") + data = f.read() + f.close() + self.cmds = data.split(";") + self.calculate_metrics() + + def calculate_metrics(self): + paths = [] + traverses = [] + cur_path = [] + xmin, ymin, zmin = 99999999, 999999999, 999999999 + xmax, ymax, zmax = 0, 0, 0 + xpos, ypos, zpos = 0, 0, 0 + zup, zdown = 0, 0 + speeds, speedz = 0.0, 0.0 + total_distance = 0.0 + total_time = 0.0 + in_path = False + for cmd in self.cmds: + cmd=cmd.strip() + try: + if cmd[:3] == "!PZ": + params = cmd[3:].split(',') + if len(params) < 2: + params = cmd[3:].split(' ') + zup = int(params[1]) + zdown = int(params[0]) + print "pen: %d up, %d down" % (zup, zdown) + elif cmd[:2] == "VS": + params = cmd[2:].split(',') + if len(params) < 2: + params = cmd[2:].split(' ') + speeds = float(params[0]) + print "xy speed: %f mm/s" % (speeds) + elif cmd[:3] == "!VZ": + params = cmd[3:].split(',') + if len(params) < 2: + params = cmd[3:].split(' ') + speedz = float(params[0]) + print "z speed: %f mm/s" % (speedz) + elif cmd[:2] == "PU": + params = cmd[2:].split(',') + if len(params) < 2: + params = cmd[2:].split(' ') + if len(params) < 2: + continue + x = int(params[0]) + y = int(params[1]) + z = zup + d = dist(xpos, ypos, zpos, x, y, z) + total_distance += d + total_time += d / RML_UNITS / SPEED_TRAVERSE + traverses.append([(xpos, ypos, zpos), (x, y, z)]) + xpos = x; ypos = y; zpos = z; + xmax = max(x, xmax); ymax = max(y, ymax); zmax = max(z, zmax) + xmin = min(x, xmin); ymin = min(y, ymin); zmin = min(z, zmin) + if len(cur_path) > 0: + paths.append(cur_path) + cur_path = [] + elif cmd[:1] == "Z": + params = cmd[1:].split(',') + if len(params) < 2: + params = cmd[1:].split(' ') + x = int(params[0]) + y = int(params[1]) + z = int(params[2]) + dist_xy = math.hypot(xpos-x, ypos-y) / RML_UNITS + dist_z = float(zpos-z) / RML_UNITS + time_xy = dist_xy / speeds + time_z = dist_z / speedz + total_time += max(time_xy, time_z) + total_distance += dist(xpos, ypos, zpos, x, y, z) + + xpos = x; ypos = y; zpos = z; + xmax = max(x, xmax); ymax = max(y, ymax); zmax = max(z, zmax) + xmin = min(x, xmin); ymin = min(y, ymin); zmin = min(z, zmin) + cur_path.append((x, y, z)) + except: + print "ignoring: %s" % cmd + pass + self.paths = paths + self.traverses = traverses + self.speed_feed = speeds + self.speed_plunge = speedz + self.xr = (xmin, xmax) + self.yr = (ymin, ymax) + self.zr = (zmin, zmax) + self.total_distance = total_distance + if self.total_distance == 0: self.total_distance = 1.0 + self.total_time = total_time + if self.total_time == 0: self.total_time = 1.0 + self.time_remaining = total_time + + def start(self): + self.running = True + self.time_start = time.time() + self.thread.start() + + def abort(self): + if self.running and not self.done.isSet(): + self.should_stop.set() + + def thread_fn(self): + xmax, ymax, zmax = 0, 0, 0 + xpos, ypos, zpos = 0, 0, 0 + zup, zdown = 0, 0 + speeds, speedz = 0.0, 0.0 + with self.lock: + cmds = self.cmds + for cmd in cmds: + cmd = cmd.strip() + if self.should_stop.isSet(): + cmd="PA;PA;!VZ10;!PZ0,100;PU0,0;PD0,0;!MC0;" + self.serial.write(cmd) + self.serial.close() + self.aborted.set() + return + cmd=cmd.strip() + with self.lock: + self.current_cmd = cmd + self.cur_cmd_start = time.time() + self.cur_cmd_duration = 0.0 + while (self.serial.getDSR() != True): + time.sleep(0.001) + self.serial.write(cmd) + try: + if cmd[:3] == "!PZ": + params = cmd[3:].split(',') + if len(params) < 2: + params = cmd[3:].split(' ') + zup = int(params[1]) + zdown = int(params[0]) + elif cmd[:2] == "VS": + params = cmd[2:].split(',') + if len(params) < 2: + params = cmd[2:].split(' ') + speeds = float(params[0]) + with self.lock: + self.speed_feed = speeds + elif cmd[:3] == "!VZ": + params = cmd[3:].split(',') + if len(params) < 2: + params = cmd[3:].split(' ') + speedz = float(params[0]) + with self.lock: + self.speed_plunge = speedz + elif cmd[:2] == "PU": + params = cmd[2:].split(',') + if len(params) < 2: + params = cmd[2:].split(' ') + if len(params) < 2: + continue + x = int(params[0]) + y = int(params[1]) + z = zup + d = dist(xpos, ypos, zpos, x, y, z) + t = d / RML_UNITS / SPEED_TRAVERSE + with self.lock: + self.cur_cmd_duration = t + self.time_remaining -= t + self.distance_milled += d + self.traverses_done.append(((xpos, ypos, zpos), (x, y, z))) + xpos = x; ypos = y; zpos = z; + elif cmd[:1] == "Z": + params = cmd[1:].split(',') + if len(params) < 2: + params = cmd[1:].split(' ') + x = int(params[0]) + y = int(params[1]) + z = int(params[2]) + dist_xy = math.hypot(xpos-x, ypos-y) / RML_UNITS + dist_z = float(zpos-z) / RML_UNITS + time_xy = dist_xy / speeds + time_z = dist_z / speedz + t = max(time_xy, time_z) + with self.lock: + self.cur_cmd_duration = t + self.time_remaining -= t + self.distance_milled += dist(xpos, ypos, zpos, x, y, z) + self.segments_done.append(((xpos, ypos, zpos), (x, y, z))) + xpos = x; ypos = y; zpos = z; + time.sleep(self.cur_cmd_duration) + except: + print "ignoring: %s" % cmd + self.done.set() + + +class RMLSenderGUI(RMLSender): + """This class implements the GUI.""" + + def __init__(self, port="/dev/ttyUSB0"): + RMLSender.__init__(self, port) + self.lines_path = [] + self.lines_traverse = [] + self.lines_seg_done = [] + self.lines_traverse_done = [] + self.n_segs_done = 0 + self.n_traverse_done = 0 + self.distance = 0.0 + self.frame = wx.Frame(None, -1, "Modela Output", size=(640,480)) + top_vbox = wx.BoxSizer(wx.VERTICAL) + + self.frame.SetSizer(top_vbox) + + upper_panel = wx.Panel(self.frame) + upper_panel_sizer = wx.BoxSizer(wx.HORIZONTAL) + upper_panel.SetSizer(upper_panel_sizer) + + self.panel = wx.Panel(upper_panel) + self.gui_xsize, self.gui_ysize = self.panel.GetSize() + self.gui_xoff, self.gui_yoff = 0, 0 + self.gui_scale = 0.5 + self.panel.Bind(wx.EVT_PAINT, self.on_paint) + self.panel.Bind(wx.EVT_SIZE, self.on_size) + upper_panel_sizer.Add(self.panel, 1, wx.EXPAND | wx.ALL, border=4) + + right_panel = wx.Panel(upper_panel, size=(200, 0)) + #right_panel.SetBackgroundColour('gray') + right_panel_sizer = wx.BoxSizer(wx.VERTICAL) + right_panel.SetSizer(right_panel_sizer) + upper_panel_sizer.Add(right_panel, 0, wx.EXPAND) + + origin_label = wx.StaticText(right_panel, label="Origin:") + right_panel_sizer.Add(origin_label, 0, + wx.EXPAND | wx.ALIGN_LEFT | wx.LEFT | wx.TOP, border=4) + bf = origin_label.GetFont() + bf.SetWeight(wx.BOLD) + origin_label.SetFont(bf) + + self.origin_label_x = wx.StaticText(right_panel, label="x: ") + self.origin_label_y = wx.StaticText(right_panel, label="y: ") + right_panel_sizer.Add(self.origin_label_x, 0, + wx.EXPAND | wx.ALIGN_LEFT | wx.LEFT, border=4) + right_panel_sizer.Add(self.origin_label_y, 0, + wx.EXPAND | wx.ALIGN_LEFT | wx.LEFT | wx.BOTTOM, border=4) + + size_label = wx.StaticText(right_panel, label="Size:") + size_label.SetFont(bf) + right_panel_sizer.Add(size_label, 0, + wx.EXPAND | wx.ALIGN_LEFT | wx.LEFT | wx.TOP, border=4) + self.size_label_x = wx.StaticText(right_panel, label="x: ") + self.size_label_y = wx.StaticText(right_panel, label="y: ") + right_panel_sizer.Add(self.size_label_x, 0, + wx.EXPAND | wx.ALIGN_LEFT | wx.LEFT, border=4) + right_panel_sizer.Add(self.size_label_y, 0, + wx.EXPAND | wx.ALIGN_LEFT | wx.LEFT | wx.BOTTOM, border=4) + + size_label = wx.StaticText(right_panel, label="Speeds:") + size_label.SetFont(bf) + right_panel_sizer.Add(size_label, 0, + wx.EXPAND | wx.ALIGN_LEFT | wx.LEFT | wx.TOP, border=4) + self.plunge_label = wx.StaticText(right_panel, label="plunge: ") + self.feed_label = wx.StaticText(right_panel, label="feed: ") + right_panel_sizer.Add(self.plunge_label, 0, + wx.EXPAND | wx.ALIGN_LEFT | wx.LEFT, border=4) + right_panel_sizer.Add(self.feed_label, 0, + wx.EXPAND | wx.ALIGN_LEFT | wx.LEFT | wx.BOTTOM, border=4) + + size_label = wx.StaticText(right_panel, label="Time:") + size_label.SetFont(bf) + right_panel_sizer.Add(size_label, 0, + wx.EXPAND | wx.ALIGN_LEFT | wx.LEFT | wx.TOP, border=4) + self.elapsed_label = wx.StaticText(right_panel, label="elapsed: ") + self.remaining_label = wx.StaticText(right_panel, label="remaining: ") + right_panel_sizer.Add(self.elapsed_label, 0, + wx.EXPAND | wx.ALIGN_LEFT | wx.LEFT, border=4) + right_panel_sizer.Add(self.remaining_label, 0, + wx.EXPAND | wx.ALIGN_LEFT | wx.LEFT | wx.BOTTOM, border=4) + + filler_panel = wx.Panel(right_panel) + right_panel_sizer.Add(filler_panel, 1, wx.EXPAND) + + self.start_button = wx.Button(right_panel, label="Begin Milling") + right_panel_sizer.Add(self.start_button, 0, wx.EXPAND | wx.ALL, border=4) + self.start_button.Bind(wx.EVT_BUTTON, self.on_startstop) + + top_vbox.Add(upper_panel, 1, wx.EXPAND) + + self.gauge = wx.Gauge(self.frame, range=100, size=(0,32)) + + top_vbox.Add(self.gauge, 0, wx.EXPAND) + + self.status_label = wx.StaticText(self.frame, label="Ready.") + top_vbox.Add(self.status_label, 0, wx.EXPAND) + + self.frame.Bind(wx.EVT_CLOSE, self.on_close) + + self.frame.Show() + + def on_close(self, event): + self.abort() + sys.exit(0) + + def transform_point(self, point): + xsize, ysize = self.gui_xsize, self.gui_ysize + xoff, yoff = self.gui_xoff, self.gui_yoff + scale = self.gui_scale + x, y, z = point + x = (x - self.xr[0]) * scale + xoff + y = ysize - (y - self.yr[0]) * scale + yoff + return (x, y, z) + + def update(self): + self.lines_path = [] + self.lines_traverse = [] + self.lines_seg_done = [] + self.lines_traverse_done = [] + self.gui_xsize, self.gui_ysize = self.panel.GetSize() + path_xsize = self.xr[1] - self.xr[0] + path_ysize = self.yr[1] - self.yr[0] + scalex = float(self.gui_xsize) / path_xsize + scaley = float(self.gui_ysize) / path_ysize + if scalex < scaley: + self.gui_scale = scalex + self.gui_yoff = -1* (self.gui_ysize - (path_ysize * scalex)) / 2 + self.gui_xoff = 0 + else: + self.gui_scale = scaley + self.gui_xoff = (self.gui_xsize - (path_xsize * scaley)) / 2 + self.gui_yoff = 0 + + self.origin_label_x.SetLabel("x: %02.01f mm" % (self.xr[0] / RML_UNITS)) + self.origin_label_y.SetLabel("y: %02.01f mm" % (self.yr[0] / RML_UNITS)) + self.size_label_x.SetLabel("x: %02.01f mm" % (path_xsize / RML_UNITS)) + self.size_label_y.SetLabel("y: %02.01f mm" % (path_ysize / RML_UNITS)) + self.plunge_label.SetLabel("plunge: %01.01f mm/s" % (self.speed_plunge)) + self.feed_label.SetLabel("feed: %01.01f mm/s" % (self.speed_feed)) + self.remaining_label.SetLabel("remaining: %02d:%02d s" % (int(self.time_remaining)/60, + int(self.time_remaining) % 60)) + + for path in self.paths: + for i in xrange(len(path)-1): + p1 = self.transform_point(path[i]) + p2 = self.transform_point(path[i+1]) + line = (p1[0], p1[1], p2[0], p2[1]) + self.lines_path.append(line) + for tr in self.traverses: + p1 = self.transform_point(tr[0]) + p2 = self.transform_point(tr[1]) + line = (p1[0], p1[1], p2[0], p2[1]) + self.lines_traverse.append(line) + for seg in self.segments_done: + p1 = self.transform_point(seg[0]) + p2 = self.transform_point(seg[1]) + line = (p1[0], p1[1], p2[0], p2[1]) + self.lines_seg_done.append(line) + for tr in self.traverses_done: + p1 = self.transform_point(tr[0]) + p2 = self.transform_point(tr[1]) + line = (p1[0], p1[1], p2[0], p2[1]) + self.lines_traverse_done.append(line) + self.panel.Refresh() + + def on_paint(self, event): + dc = wx.PaintDC(self.panel) + dc.Clear() + dc.SetPen(wx.Pen('gray', 1)) + dc.DrawLineList(self.lines_traverse) + dc.SetPen(wx.Pen('blue', 1)) + dc.DrawLineList(self.lines_path) + dc.SetPen(wx.Pen('gray', 2)) + dc.DrawLineList(self.lines_traverse_done) + dc.SetPen(wx.Pen('red', 2)) + dc.DrawLineList(self.lines_seg_done) + + def new_segs_check(self, event): + dc = wx.ClientDC(self.panel) + with self.lock: + dc.SetPen(wx.Pen('red', 2)) + for i in xrange(self.n_segs_done, len(self.segments_done)): + seg = self.segments_done[i] + p1 = self.transform_point(seg[0]) + p2 = self.transform_point(seg[1]) + dc.DrawLine(p1[0], p1[1], p2[0], p2[1]) + self.lines_seg_done.append((p1[0], p1[1], p2[0], p2[1])) + self.n_segs_done = len(self.segments_done) + dc.SetPen(wx.Pen('gray', 2)) + for i in xrange(self.n_traverse_done, len(self.traverses_done)): + seg = self.traverses_done[i] + p1 = self.transform_point(seg[0]) + p2 = self.transform_point(seg[1]) + dc.DrawLine(p1[0], p1[1], p2[0], p2[1]) + self.lines_traverse_done.append((p1[0], p1[1], p2[0], p2[1])) + self.n_traverse_done = len(self.traverses_done) + + perc_done = int(round(100.0 * (self.distance_milled / self.total_distance))) + self.gauge.SetValue(perc_done) + self.plunge_label.SetLabel("plunge: %01.01f mm/s" % (self.speed_plunge)) + self.feed_label.SetLabel("feed: %01.01f mm/s" % (self.speed_feed)) + elapsed = time.time() - self.time_start + self.elapsed_label.SetLabel("elapsed: %02d:%02d s" % + (int(elapsed)/60, int(elapsed) % 60)) + self.remaining_label.SetLabel("remaining: %02d:%02d s" % + (int(self.time_remaining)/60, int(self.time_remaining) % 60)) + cmd_elapsed = time.time() - self.cur_cmd_start + cmd_rem = self.cur_cmd_duration - cmd_elapsed + self.status_label.SetLabel("Executing: %s (%d:%02d)" % (self.current_cmd, + cmd_rem / 60, cmd_rem % 60)) + if self.done.isSet(): + self.timer.Stop() + self.start_button.SetLabel("Exit") + self.status_label.SetLabel("Finished.") + elif self.should_stop.isSet(): + self.status_label.SetLabel("Aborting; wait for Modela to stop or switch to view mode and hold up/down buttons to clear buffer") + if self.aborted.isSet(): + sys.exit(0) + + + + def on_size(self, event): + self.update() + + def on_startstop(self, event): + if not self.running: + self.start_button.SetLabel("Abort") + self.start() + self.timer = wx.Timer(self.frame) + self.frame.Bind(wx.EVT_TIMER, self.new_segs_check, self.timer) + self.timer.Start(100) + elif self.done.isSet(): + sys.exit(0) + else: + self.abort() + + def load_file(self, filename): + RMLSender.load_file(self,filename) + self.update() + +if __name__ == '__main__': + if len(sys.argv) < 2: + print "usage: %s file.rml [/dev/ttyUSB0]" % sys.argv[0] + sys.exit(1) + + filename = sys.argv[1] + port = "/dev/ttyUSB0" + if len(sys.argv) > 2: + port = sys.argv[2] + + app = wx.PySimpleApp() + + f = open(filename) + data = f.read() + f.close() + + if len(data) < 150: + sender = RMLSender(port) + sender.load_file(filename) + sender.start() + sender.thread.join() + else: + sender = RMLSenderGUI(port) + sender.load_file(filename) + app.MainLoop() + diff --git a/src/py/CMakeLists.txt b/src/py/CMakeLists.txt new file mode 100644 index 0000000..0d57871 --- /dev/null +++ b/src/py/CMakeLists.txt @@ -0,0 +1,21 @@ +#cmake_minimum_required(VERSION 2.6) + +set(PYs fab_mods.py fab_set.py panel_control.py + cad_shapes.py cad_text.py math_string.py + panel_cad.py panel_cad_png.py panel_cad_stl.py + panel_math.py panel_math_png.py panel_math_stl.py + panel_png.py panel_png_png.py panel_png_path.py + panel_png_path_halftone.py panel_png_grb.py + panel_png_drl.py panel_path.py + panel_path_rml.py panel_path_plt.py panel_path_oms.py + panel_path_sbp.py panel_path_ord.py panel_path_camm.py + panel_path_epi.py panel_path_uni.py panel_path_g.py + panel_path_eps.py panel_path_snap.py panel_path_dxf.py + panel_stl.py panel_stl_png.py + panel_svg.py panel_svg_path.py + panel_gif.py panel_gif_stl.py + CACHE STRING "Python script list") + +if( ${CMAKE_PROJECT_NAME} MATCHES fabmod ) + install(FILES ${PYs} DESTINATION ${PROJECT_SOURCE_DIR}/../bin) +endif( ${CMAKE_PROJECT_NAME} MATCHES fabmod ) diff --git a/src/py/cad_shapes.py b/src/py/cad_shapes.py new file mode 100644 index 0000000..f47882b --- /dev/null +++ b/src/py/cad_shapes.py @@ -0,0 +1,497 @@ +# cad_shapes.py +# Standard library of shapes and operations + +# Assembled by Matt Keeter (with code from Neil Gershenfeld) +# matt.keeter@cba.mit.edu + +# kokompe.cba.mit.edu + +############################################################################### + +# 2D shapes: +# circle(x0, y0, r) +# rectangle(x0, x1, y0, y1) +# right_triangle(x0, y0, l) +# triangle(x0, y0, x1, y1, x2, y2) [clockwise order] +# polygon(x, y, r, n) +# tab(x, y, width, height, angle = 0, chamfer = 0.2) + +# 3D shapes: +# extrusion(part, z0, z1) +# cylinder(x0, y0, z0, z1, r) +# sphere(x0, y0, z0, r) +# cube(x0, x1, y0, y1, z0, z1) +# cone(x0, y0, z0, z1, r0) +# pyramid(x0, x1, y0, y1, z0, z1) + +# Logic operations: +# add(part1, part2) +# subtract(part1, part2) +# intersect(part1, part2) +# invert(part) + +# Translation: +# move(part, dx, dy, dz = 0) + +# Rotation: +# rotate(part, angle) +# rotate_90(part) +# rotate_180(part) +# rotate_270(part) +# rotate_x(part, angle) +# rotate_y(part, angle) +# rotate_z(part, angle) + +# Reflection: +# reflect_x(part,x0 = 0) +# reflect_y(part,y0 = 0) +# reflect_z(part,z0 = 0) +# reflect_xy(part) +# reflect_xz(part) +# reflect_yz(part) + +# Scaling: +# scale_x(part, x0, sx) +# scale_y(part, y0, sy) +# scale_z(part, z0, sz) +# scale_xy(part, x0, y0, sxy) +# scale_xyz(part, x0, y0, z0, sxyz) + +# Distortion: +# attract(part, value, x0, y0, z0=0) + +# Coscaling: +# coscale_x_y(part, x0, y0, y1, angle0, angle1, amplitude, offset) +# coscale_x_z(part, x0, z0, z1, angle0, angle1, amplitude, offset) +# coscale_xy_z(part, x0, y0, z0, z1, angle0, angle1, amplitude, offset) + +# Tapering: +# taper_x_y(part, x0, y0, y1, s0, s1) +# taper_x_z(part, x0, z0, z1, s0, s1) +# taper_xy_z(part, x0, y0, z0, z1, s0, s1) + +# Shearing: +# shear_x_y(part, y0, y1, dx0, dx1) +# shear_x_z(part, z0, z1, dx0, dx1) +# coshear_x_z(part, z0, z1, angle0, angle1, amplitude, offset) + +# Color: +# color(color,part) +# +# Colors are implemented as an integer bit-field: +# R = bits 0 through 7 +# G = bits 8 through 15 +# B = bits 16 through 23 +# +# The following colors are pre-defined: +# red, green, blue, gray, white, teal, pink, +# yellow, brown, navy, black + +############################################################################### +from math_string import MString + +from math import pi, sin, cos +import re + +############################################################################### +# 2D Shapes +############################################################################### +def circle(x0, y0, r): + return move(MString("(pow(X,2) + pow(Y,2) <= %f)" % (r*r)), x0, y0) + +def rectangle(x0, x1, y0, y1): + return MString("((X >= %f) && (X <= %f) && " % (x0, x1) + + "(Y >= %f) && (Y <= %f))" % (y0, y1)) + +def right_triangle(x, y, L): + tri = MString("(X < -Y) && (X > -{0}) && (Y > -{0})".format(L)) + return move(tri, x + L, y + L) + +def triangle(x0, y0, x1, y1, x2, y2): # points in clockwise order + tri = ("((((({y1})-({y0}))*(X-({x0}))-(({x1})-({x0}))*(Y-({y0}))) >= 0) && " + \ + "(((({y2})-({y1}))*(X-({x1}))-(({x2})-({x1}))*(Y-({y1}))) >= 0) && " + \ + "(((({y0})-({y2}))*(X-({x2}))-(({x0})-({x2}))*(Y-({y2}))) >= 0))").format( + x0 = x0, y0 = y0, x1 = x1, y1 = y1, x2 = x2, y2 = y2) + return MString(tri) + +def polygon(x, y, r, n): + if n <= 2: + return '0' + part = circle(0, 0, r) + cutoff = 'Y > -%f' % (cos(pi/n) * r) + for i in range(n): + part = intersect(part, rotate(cutoff, i*360./n)) + return move(MString(part), x, y) + + +def tab(x, y, width, height, angle = 0, chamfer = 0.2): + tab = rectangle(-width/2, width/2, 0, height) + cutout = triangle(width/2 - chamfer*height, height, + width/2, height, + width/2, height - chamfer*height) + cutout = add(cutout, reflect_x(cutout)) + tab = subtract(tab, cutout) + + tab = rotate(tab, angle) + tab = move(tab, x, y) + return MString(tab) + +def slot(x, y, width, height, angle = 0, chamfer = 0.2): + slot = rectangle(-width/2, width/2, -height, 0) + inset = triangle(width/2, 0, + width/2 + height * chamfer, 0, + width/2, -chamfer*height) + inset = add(inset, reflect_x(inset)) + slot = add(slot, inset) + + slot = rotate(slot, angle) + slot = move(slot, x, y) + return MString(slot) + +############################################################################### +# 3D Shapes +############################################################################### +def extrusion(part, z0, z1): + return MString("((%s) && (Z >= %f) && (Z <= %f))" % (part, z0, z1)) + +def cylinder(x0, y0, z0, z1, r): + return extrusion(circle(x0, y0, r), z0, z1) + +def sphere(x0, y0, z0, r): + return move("(pow(X,2) + pow(Y,2) + pow(Z,2) <= %f)" % (r * r), x0, y0, z0) + +def cube(x0, x1, y0, y1, z0, z1): + return MString("((X >= %f) && (X <= %f) && " % (x0, x1) + + " (Y >= %f) && (Y <= %f) && " % (y0, y1) +\ + " (Z >= %f) && (Z <= %f))" % (z0, z1)) + +def cone(x0, y0, z0, z1, r0): + cyl = cylinder(x0, y0, z0, z1, r0) + return taper_xy_z(cyl, x0, y0, z0, z1, 1.0, 0.0) + +def pyramid(x0, x1, y0, y1, z0, z1): + c = cube(x0, x1, y0, y1, z0, z1) + return taper_xy_z(c, (x0+x1)/2., (y0+y1)/2., z0, z1, 1.0, 0.0) + +############################################################################### +# Logic Operations +############################################################################### +def add(part1, part2): + return MString("(%s) || (%s)" % (part1, part2)) + +def subtract(part1, part2): + return MString("(%s) && !(%s)" % (part1, part2)) + +def intersect(part1, part2): + return MString("(%s) && (%s)" % (part1, part2)) + +def invert(part): + return MString("!(%s)" % part) + +############################################################################### +# Translation +############################################################################### +def move(part,dx,dy,dz = 0): + if dx: + part = part.replace('X','(X-%f)' % dx) + if dy: + part = part.replace('Y','(Y-%f)' % dy) + if dz: + part = part.replace('Z','(Z-%f)' % dz) + return part + +############################################################################### +# Rotation +############################################################################### +def rotate(part, angle): + if angle == 90: + return rotate_90(part) + elif angle == 180: + return rotate_180(part) + elif angle == 270: + return rotate_270(part) + elif angle == 0: + return part + + angle = str(angle*pi/180) + + # find all already-rotated points and increment that + # rotation instead of stacking more and more trig + # functions + xrot_regex = re.compile(r'\( cos\(([\-\d.]+)\)\*X\+sin\(\1\)\*Y\)') + yrot_regex = re.compile(r'\(-sin\(([\-\d.]+)\)\*X\+cos\(\1\)\*Y\)') + + def update_angle_x(match, angle): + match = match.groups() + angle = str(float(angle) + float(match[0])) + return '(cos({0})*x+sin({0})*y)'.format(angle) + + def update_angle_y(match, angle): + match = match.groups() + angle = str(float(angle) + float(match[0])) + return '(-sin({0})*x+cos({0})*y)'.format(angle) + + part = xrot_regex.sub(lambda x: update_angle_x(x, angle), str(part)) + part = yrot_regex.sub(lambda x: update_angle_y(x, angle), str(part)) + + # replace all bare X, Y as before + # move all upper to lower case + part = part.replace('X','(cos({0})*X+sin({0})*y)'.format(angle)) + part = part.replace('Y','(-sin({0})*X+cos({0})*y)'.format(angle)) + part = part.replace('y','Y') + part = part.replace('x','X') + + return MString(part) + +def rotate_90(part): + part = reflect_y(part) + part = reflect_xy(part) + return part + +def rotate_180(part): + part = rotate_90(part) + part = rotate_90(part) + return part + +def rotate_270(part): + part = rotate_90(part) + part = rotate_90(part) + part = rotate_90(part) + return part + +def rotate_x(part, angle): + angle = angle*pi/180 + part = part.replace('Y','(cos(angle)*Y+sin(angle)*z)') + part = part.replace('Z','(-sin(angle)*Y+cos(angle)*z)') + part = part.replace('z','Z') + part = part.replace('angle',str(angle)) + return part + +def rotate_y(part, angle): + angle = angle*pi/180 + part = part.replace('X','(cos(angle)*X+sin(angle)*z)') + part = part.replace('Z','(-sin(angle)*X+cos(angle)*z)') + part = part.replace('z','Z') + part = part.replace('angle',str(angle)) + return part + +def rotate_z(part, angle): + return rotate(part, angle) +############################################################################### +# Reflection +############################################################################### +def reflect_x(part, x0 = 0): + return part.replace('X','(%f-X)' % x0) + +def reflect_y(part, y0 = 0): + return part.replace('Y','(%f-Y)' % y0) + +def reflect_z(part,z0): + return part.replace('Z','(%f-Z)' % z0) + return part + +def reflect_xy(part): + part = part.replace('X','temp') + part = part.replace('Y','X') + part = part.replace('temp','Y') + return part + +def reflect_xz(part): + part = part.replace('X','temp') + part = part.replace('Z','X') + part = part.replace('temp','Z') + return part + +def reflect_yz(part): + part = part.replace('Y','temp') + part = part.replace('Z','Y') + part = part.replace('temp','Z') + return part +############################################################################### +# Scaling +############################################################################### +def scale_x(part, x0, sx): + part = part.replace('X','((x0) + (X-(x0))/(sx))') + part = part.replace('x0',str(x0)) + part = part.replace('sx',str(sx)) + return part + +def scale_y(part, y0, sy): + part = part.replace('Y','((y0) + (Y-(y0))/(sy))') + part = part.replace('y0',str(y0)) + part = part.replace('sy',str(sy)) + return part + +def scale_z(part, z0, sz): + part = part.replace('Z','((z0) + (Z-(z0))/(sz))') + part = part.replace('z0',str(z0)) + part = part.replace('sz',str(sz)) + return part + +def scale_xy(part, x0, y0, sxy): + part = part.replace('X','((x0) + (X-(x0))/(sxy))') + part = part.replace('Y','((y0) + (Y-(y0))/(sxy))') + part = part.replace('x0',str(x0)) + part = part.replace('y0',str(y0)) + part = part.replace('sxy',str(sxy)) + return part + +def scale_xyz(part, x0, y0, z0, sxyz): + part = part.replace('X','((x0) + (X-(x0))/(sxyz))') + part = part.replace('Y','((y0) + (Y-(y0))/(sxyz))') + part = part.replace('Z','((z0) + (Z-(z0))/(sxyz))') + part = part.replace('x0',str(x0)) + part = part.replace('y0',str(y0)) + part = part.replace('z0',str(z0)) + part = part.replace('sxyz',str(sxyz)) + return part + +############################################################################### +# Distortion: +############################################################################### + +def attract(part, radius, x0, y0, z0=0): + part = part.replace('X', + ('({x0}+(X-{x0})*(1+{r}*exp(-pow(X-{x0},2)+'+ + 'pow(yt-{y0},2)+pow(zt-{z0},2)/{r})))').format( + x0=x0,y0=y0,z0=z0,r=radius)) + part = part.replace('Y', + ('({y0}+(Y-{y0})*(1+{r}*exp(-pow(xt-{x0},2)+'+ + 'pow(Y-{y0},2)+pow(zt-{z0},2)/{r})))').format( + x0=x0,y0=y0,z0=z0,r=radius)) + part = part.replace('Z', + ('({z0}+(Z-{z0})*(1+{r}*exp(-pow(xt-{x0},2)+'+ + 'pow(yt-{y0},2)+pow(Z-{z0},2)/{r})))').format( + x0=x0,y0=y0,z0=z0,r=radius)) + part = part.replace('xt','X') + part = part.replace('yt','Y') + part = part.replace('zt','Z') + return part + +############################################################################### +# Coscaling +############################################################################### + +def coscale_x_y(part, x0, y0, y1, angle0, angle1, amplitude, offset): + phase0 = pi*angle0/180. + phase1 = pi*angle1/180. + part = part.replace('X','((x0) + (X-(x0))/((offset) + (amplitude)*cos((phase0) + ((phase1)-(phase0))*(Y-(y0))/((y1)-(y0)))))') + part = part.replace('x0',str(x0)) + part = part.replace('y0',str(y0)) + part = part.replace('y1',str(y1)) + part = part.replace('phase0',str(phase0)) + part = part.replace('phase1',str(phase1)) + part = part.replace('amplitude',str(amplitude)) + part = part.replace('offset',str(offset)) + return part + +def coscale_x_z(part, x0, z0, z1, angle0, angle1, amplitude, offset): + phase0 = pi*angle0/180. + phase1 = pi*angle1/180. + part = part.replace('X','((x0) + (X-(x0))/((offset) + (amplitude)*cos((phase0) + ((phase1)-(phase0))*(Z-(z0))/((z1)-(z0)))))') + part = part.replace('x0',str(x0)) + part = part.replace('z0',str(z0)) + part = part.replace('z1',str(z1)) + part = part.replace('phase0',str(phase0)) + part = part.replace('phase1',str(phase1)) + part = part.replace('amplitude',str(amplitude)) + part = part.replace('offset',str(offset)) + return part + +def coscale_xy_z(part, x0, y0, z0, z1, angle0, angle1, amplitude, offset): + phase0 = pi*angle0/180. + phase1 = pi*angle1/180. + part = part.replace('X','((x0) + (X-(x0))/((offset) + (amplitude)*cos((phase0) + ((phase1)-(phase0))*(Z-(z0))/((z1)-(z0)))))') + part = part.replace('Y','((y0) + (Y-(y0))/((offset) + (amplitude)*cos((phase0) + ((phase1)-(phase0))*(Z-(z0))/((z1)-(z0)))))') + part = part.replace('x0',str(x0)) + part = part.replace('y0',str(y0)) + part = part.replace('z0',str(z0)) + part = part.replace('z1',str(z1)) + part = part.replace('phase0',str(phase0)) + part = part.replace('phase1',str(phase1)) + part = part.replace('amplitude',str(amplitude)) + part = part.replace('offset',str(offset)) + return part + +############################################################################### +# Tapering +############################################################################### +def taper_x_y(part, x0, y0, y1, s0, s1): + part = part.replace('X','((x0) + (X-(x0))*((y1)-(y0))/((s1)*(Y-(y0)) + (s0)*((y1)-Y)))') + part = part.replace('x0',str(x0)) + part = part.replace('y0',str(y0)) + part = part.replace('y1',str(y1)) + part = part.replace('s0',str(s0)) + part = part.replace('s1',str(s1)) + return part + +def taper_x_z(part, x0, z0, z1, s0, s1): + part = part.replace('X','((x0) + (X-(x0))*((z1)-(z0))/((s1)*(Z-(z0)) + (s0)*((z1)-Z)))') + part = part.replace('x0',str(x0)) + part = part.replace('z0',str(z0)) + part = part.replace('z1',str(z1)) + part = part.replace('s0',str(s0)) + part = part.replace('s1',str(s1)) + return part + +def taper_xy_z(part, x0, y0, z0, z1, s0, s1): + part = part.replace('X','((x0) + (X-(x0))*((z1)-(z0))/((s1)*(Z-(z0)) + (s0)*((z1)-Z)))') + part = part.replace('Y','((y0) + (Y-(y0))*((z1)-(z0))/((s1)*(Z-(z0)) + (s0)*((z1)-Z)))') + part = part.replace('x0',str(x0)) + part = part.replace('y0',str(y0)) + part = part.replace('z0',str(z0)) + part = part.replace('z1',str(z1)) + part = part.replace('s0',str(s0)) + part = part.replace('s1',str(s1)) + return part + +############################################################################### +# Shearing +############################################################################### +def shear_x_y(part, y0, y1, dx0, dx1): + part = part.replace('X','(X - (dx0) - ((dx1)-(dx0))*(Y-(y0))/((y1)-(y0)))') + part = part.replace('y0',str(y0)) + part = part.replace('y1',str(y1)) + part = part.replace('dx0',str(dx0)) + part = part.replace('dx1',str(dx1)) + return part + +def shear_x_z(part, z0, z1, dx0, dx1): + part = part.replace('X','(X - (dx0) - ((dx1)-(dx0))*(Z-(z0))/((z1)-(z0)))') + part = part.replace('z0',str(z0)) + part = part.replace('z1',str(z1)) + part = part.replace('dx0',str(dx0)) + part = part.replace('dx1',str(dx1)) + return part + +def coshear_x_z(part, z0, z1, angle0, angle1, amplitude, offset): + phase0 = pi*angle0/180. + phase1 = pi*angle1/180. + part = part.replace('X','(X - (offset) - (amplitude)*cos((phase0) + ((phase1)-(phase0))*(Z-(z0))/((z1)-(z0))))') + part = part.replace('z0',str(z0)) + part = part.replace('z1',str(z1)) + part = part.replace('phase0',str(phase0)) + part = part.replace('phase1',str(phase1)) + part = part.replace('amplitude',str(amplitude)) + part = part.replace('offset',str(offset)) + return part + + +############################################################################### +# Color +############################################################################### +red = (225 << 0) +green = (225 << 8) +blue = (225 << 16) +gray = (128 << 16) + (128 << 8) + (128 << 0) +white = (255 << 16) + (255 << 8) + (255 << 0) +teal = (255 << 16) + (255 << 8) +pink = (255 << 16) + (255 << 0) +yellow = (255 << 8) + (255 << 0) +brown = (45 << 16) + (82 << 8) + (145 << 0) +navy = (128 << 16) + (0 << 8) + (0 << 0) +tan = (60 << 16) + (90 << 8) + (125 << 0) +black = 0 + +def color(color, part): + return MString('(%s * (%s))' % (color, part)) \ No newline at end of file diff --git a/src/py/cad_text.py b/src/py/cad_text.py new file mode 100644 index 0000000..703c7d4 --- /dev/null +++ b/src/py/cad_text.py @@ -0,0 +1,541 @@ +# cad_text.py +# Tools to generate text in the cad format. + +# Assembled by Matt Keeter (with code from Neil Gershenfeld) +# matt.keeter@cba.mit.edu + +# kokompe.cba.mit.edu + +############################################################################### + +# Usage: +# text(text, x, y, height = None, align = 'CC'): +# text is the desired string +# x, y are the position +# height is the glyph height. Defaults to the current font height. +# align sets horizontal (C/L/R) and vertical (C/T/B) alignment + +# set_font(height) +# Pre-renders a set of font glyphs for a given glyph height. + +# When text() is called, it uses the precalculated glyph set if the height is +# correct; otherwise, it renders glyphs of the appropriate height. +# +# Prerendering glyphs of the appropriate size with set_font() can save time +# that would be spent regenerating the font with every call to text(). + +############################################################################### + +from cad_shapes import * + +def make_glyphs(height = 1.0, width = None, line = None): + if not width: + width = 2*height/3 + if not line: + line = height / 6 + + glyphs = {} + + shape = triangle(0, 0, width/2, height, width, 0) + shape = subtract(shape, triangle(line, 0, width/2, height - line*2, width - line, 0)) + shape = add(shape, rectangle(line, width - line, height/4 - line/2, height/4 + line/2)) + glyphs['A'] = shape + + shape = circle(width/2,width/2,width/2) + shape = subtract(shape,circle(width/2,width/2,width/2 - line)) + shape = add(shape,rectangle(width-line,width,0,height/3)) + glyphs['a'] = shape + + shape = rectangle(0, 2*line, 0, height/2 + line/2) + shape = add(shape, circle(2*line, height/4 + line/4, height/4 + line/4)) + shape = subtract(shape, circle(2*line, height/4 + line/4, height/4 - 3*line/4)) + shape = subtract(shape, rectangle(line, 2*line, line, height/2 - line/2)) + shape = add(shape, move(shape,0, height/2 - line/2)) + glyphs['B'] = shape + + shape = circle(width/2,width/2,width/2) + shape = subtract(shape,circle(width/2,width/2,width/2 - line)) + shape = add(shape,rectangle(0,line,0,height)) + glyphs['b'] = shape + + shape = circle(width/2,width/2,width/2) + shape = add(shape,circle(width/2,height-width/2,width/2)) + shape = add(shape,rectangle(0,width,width/2, height-width/2)) + shape = subtract(shape,circle(width/2,width/2,width/2 - line)) + shape = subtract(shape,circle(width/2,height -width/2,width/2 - line)) + shape = subtract(shape,rectangle(line,width,width/2, height-width/2)) + glyphs['C'] = shape + + shape = circle(width/2,width/2,width/2) + shape = subtract(shape,circle(width/2,width/2,width/2 - line)) + shape = subtract(shape,rectangle(width/2,width,width/2-1/(2*line),width/2+1/(2*line))) + glyphs['c'] = shape + + shape = circle(line,height/2,height/2) + shape = subtract(shape,circle(line,height/2,height/2 - line)) + shape = subtract(shape,rectangle(line-height/2,line,0,height)) + shape = add(shape,rectangle(0,line,0,height)) + glyphs['D'] = shape + + shape = rectangle(width-line,width,0,height) + shape = add(shape,circle(width/2,width/2,width/2)) + shape = subtract(shape,circle(width/2,width/2,width/2-line)) + glyphs['d'] = shape + + shape = rectangle(0,line,0,height) + shape = add(shape,rectangle(0,width,height-line,height)) + shape = add(shape,rectangle(0,2*width/3,height/2-line/2,height/2+line/2)) + shape = add(shape,rectangle(0,width,0,line)) + glyphs['E'] = shape + + shape = circle(width/2,width/2,width/2) + shape = subtract(shape,circle(width/2,width/2,width/2-line)) + shape = subtract(shape,triangle(width,0,width/2,width/2-line/2,width,width/2-line/2)) + shape = add(shape,rectangle(line/2,width-line/2,width/2-line/2,width/2+line/2)) + glyphs['e'] = shape + + shape = rectangle(0,line,0,height) + shape = add(shape,rectangle(0,width,height-line,height)) + shape = add(shape,rectangle(0,2*width/3,height/2-line/2,height/2+line/2)) + glyphs['F'] = shape + + shape = circle(width-line/2,height-width/2,width/2) + shape = subtract(shape,circle(width-line/2,height-width/2,width/2-line)) + shape = subtract(shape,rectangle(0,width-line/2,0,height-width/2)) + shape = subtract(shape,rectangle(width-line/2,2*width,0,height)) + shape = add(shape,rectangle(width/2-line/2,width/2+line/2,0,height-width/2)) + shape = add(shape,rectangle(width/5,4*width/5,height/2-line/2,height/2+line/2)) + glyphs['f'] = shape + + shape = circle(width/2,width/2,width/2) + shape = add(shape,circle(width/2,height-width/2,width/2)) + shape = add(shape,rectangle(0,width,width/2, height-width/2)) + shape = subtract(shape,circle(width/2,width/2,width/2 - line)) + shape = subtract(shape,circle(width/2,height -width/2,width/2 - line)) + shape = subtract(shape,rectangle(line,width,width/2, height-width/2)) + shape = add(shape, rectangle(width/2, width, width/2, width/2 + line)) + glyphs['G'] = shape + + shape = circle(width/2, 0, width/2) + shape = subtract(shape, circle(width/2, 0, width/2 - line)) + shape = subtract(shape, rectangle(0, width, 0, height)) + shape = add(shape, circle(width/2,width/2,width/2)) + shape = subtract(shape,circle(width/2,width/2,width/2-line)) + shape = add(shape,rectangle(width-line,width,0,width)) + glyphs['g'] = shape + + shape = rectangle(0,line,0,height) + shape = add(shape,rectangle(width-line,width,0,height)) + shape = add(shape,rectangle(0,width,height/2-line/2,height/2+line/2)) + glyphs['H'] = shape + + shape = circle(width/2,width/2,width/2) + shape = subtract(shape,circle(width/2,width/2,width/2-line)) + shape = subtract(shape,rectangle(0,width,0,width/2)) + shape = add(shape,rectangle(0,line,0,height)) + shape = add(shape,rectangle(width-line,width,0,width/2)) + glyphs['h'] = shape + + shape = rectangle(width/2-line/2,width/2+line/2,0,height) + shape = add(shape,rectangle(width/5,4*width/5,0,line)) + shape = add(shape,rectangle(width/5,4*width/5,height-line,height)) + glyphs['I'] = shape + + shape = rectangle(width/2-line/2,width/2+line/2,0,height/2) + shape = add(shape,circle(width/2,3*height/4,3*line/5)) + glyphs['i'] = shape + + shape = circle(width/2,width/2,width/2) + shape = subtract(shape,circle(width/2,width/2,width/2-line)) + shape = subtract(shape,rectangle(0,width,width/2,height)) + shape = add(shape,rectangle(width-line,width,width/2,height)) + glyphs['J'] = shape + + shape = circle(width/2, 0, width/2) + shape = subtract(shape, circle(width/2, 0, width/2 - line)) + shape = subtract(shape, rectangle(0, width, 0, height)) + shape = add(shape,rectangle(width-line,width,0,height/2)) + shape = add(shape,circle(width-line/2,3*height/4,3*line/5)) + glyphs['j'] = shape + + shape = rectangle(0,width,0,height) + shape = subtract(shape,triangle(line,height,width-line,height,line,height/2+line)) + shape = subtract(shape,triangle(width,0,1.5*line,height/2,width,height)) + shape = subtract(shape,triangle(line,0,line,height/2-line,width-line,0)) + glyphs['K'] = shape + + shape = rectangle(0,3*width/4,0,height) + shape = subtract(shape,triangle(line,height,width-line,height,line,height/3+line)) + shape = subtract(shape,triangle(3*width/4,0,1.5*line,height/3,width,height)) + shape = subtract(shape,triangle(line,0,line,height/2.5-line,3*width/4-line,0)) + shape = subtract(shape, rectangle(line, width, 3*height/4, height)) + glyphs['k'] = shape + + shape = rectangle(0,line,0,height) + shape = add(shape,rectangle(0,width,0,line)) + glyphs['L'] = shape + + shape = rectangle(width/2-line/2,width/2+line/2,0,height) + glyphs['l'] = shape + + shape = rectangle(0,width,0,height) + shape = subtract(shape,triangle(line,0,line,height/2,width/2-line/3,0)) + shape = subtract(shape,triangle(line,height,width-line,height,width/2,height/4)) + shape = subtract(shape,triangle(width/2+line/3,0,width-line,height/2,width-line,0)) + glyphs['M'] = shape + + shape = circle(width/4 + line/4, width/2, width/4 + line/4) + shape = subtract(shape, circle(width/4 + line/4, width/2, width/4 - 3*line/4)) + shape = subtract(shape, rectangle(0, width, 0, width/2)) + shape = add(shape, move(shape, width/2 - line/2, 0)) + shape = add(shape, rectangle(0, line, 0, width/2 + line)) + shape = add(shape, rectangle(width/2-line/2, width/2+line/2, 0, width/2)) + shape = add(shape, rectangle(width-line, width, 0, width/2)) + glyphs['m'] = shape + + shape = rectangle(0,width,0,height) + shape = subtract(shape,triangle(4*line/3,height,width-line,height,width-line,height/4)) + shape = subtract(shape,triangle(line,0,line,3*height/4,width - 4*line/3,0)) + glyphs['N'] = shape + + shape = circle(width/2,width/2,width/2) + shape = subtract(shape,circle(width/2,width/2,width/2-line)) + shape = subtract(shape,rectangle(0,width,0,width/2)) + shape = add(shape,rectangle(0,line,0,width)) + shape = add(shape,rectangle(width-line,width,0,width/2)) + glyphs['n'] = shape + + shape = circle(width/2,width/2,width/2) + shape = add(shape,circle(width/2,height-width/2,width/2)) + shape = add(shape,rectangle(0,width,width/2, height-width/2)) + shape = subtract(shape,circle(width/2,width/2,width/2 - line)) + shape = subtract(shape,circle(width/2,height -width/2,width/2 - line)) + shape = subtract(shape,rectangle(line,width-line,width/2, height-width/2)) + glyphs['O'] = shape + + shape = circle(width/2,width/2,width/2) + shape = subtract(shape,circle(width/2,width/2,width/2-line)) + glyphs['o'] = shape + + # P scales a bit strangely + shape = rectangle(0,line,0,height) + shape = add(shape,circle(width/2,height-width/2,width/2)) + shape = add(shape,rectangle(0,width/2,height-width,height)) + shape = subtract(shape,circle(width/2,height-width/2,width/2-line)) + shape = subtract(shape,rectangle(line,width/2,height-width+line,height-line)) + glyphs['P'] = shape + + shape = circle(width/2,width/2,width/2) + shape = subtract(shape,circle(width/2,width/2,width/2-line)) + shape = add(shape,rectangle(0,line,-height/3,width)) + glyphs['p'] = shape + + shape = glyphs['O'] + shape = add(shape,move(rotate(rectangle(-line/2,line/2,-width/4,width/4),30),3*width/4,width/4)) + glyphs['Q'] = shape + + shape = circle(width/2,width/2,width/2) + shape = subtract(shape,circle(width/2,width/2,width/2-line)) + shape = add(shape,rectangle(width-line,width,-height/3,width)) + glyphs['q'] = shape + + shape = triangle(line,0,line,height,width,0) + shape = subtract(shape,triangle(line,0,line,height-line*2,width-line,0)) + shape = subtract(shape,rectangle(0,width,height/3 + line/2,height)) + shape = add(glyphs['P'],shape) + glyphs['R'] = shape + + shape = circle(width,0,width) + shape = subtract(shape,circle(width,0,width-line)) + shape = subtract(shape,rectangle(.8*width,2*width,-width,width)) + shape = subtract(shape,rectangle(0,width,-width,0)) + shape = add(shape,rectangle(0,line,0,width)) + glyphs['r'] = shape + + shape = circle(width/2,width/2,width/2) + shape = subtract(shape,circle(width/2,width/2,width/2-line)) + shape = subtract(shape,rectangle(0,width/2,width/2,width)) + shape = add(shape,move(reflect_y(reflect_x(shape,width),width),0,width-line)) + shape = scale_y(shape,0,height/(2*width-line)) + glyphs['S'] = shape + + w = width/3 + shape = circle(w,w,w) + shape = subtract(shape,circle(w,w,w-line*0.9)) + shape = subtract(shape,rectangle(0,w,w,2*w)) + shape = add(shape,move(reflect_y(reflect_x(shape,2*w),2*w),0,2*w-line*.9)) + shape = scale_y(shape,0,(2*height/3)/(4*w-line*.9)) + shape = move(shape,(width/2)-w,0) + glyphs['s'] = shape + + shape = rectangle(width/2-line/2,width/2+line/2,0,height) + shape = add(shape,rectangle(0,width,height-line,height)) + glyphs['T'] = shape + + shape = circle(0,3*width/8,3*width/8) + shape = subtract(shape,circle(0,3*width/8,3*width/8-line)) + shape = subtract(shape,rectangle(-width,width,3*width/8,height)) + shape = subtract(shape,rectangle(0,width,-height,height)) + shape = move(shape,width/2-line/2+3*width/8,0) + shape = add(shape,rectangle(width/2-line/2,width/2+line/2,width/4,3*height/4)) + shape = add(shape,rectangle(width/5,4*width/5,height/2-line/2,height/2+line/2)) + glyphs['t'] = shape + + shape = circle(width/2,width/2,width/2) + shape = subtract(shape,circle(width/2,width/2,width/2-line)) + shape = subtract(shape,rectangle(0,width,width/2,height)) + shape = add(shape,rectangle(0,line,width/2,height)) + shape = add(shape,rectangle(width-line,width,width/2,height)) + glyphs['U'] = shape + + shape = circle(width/2,width/2,width/2) + shape = subtract(shape,circle(width/2,width/2,width/2-line)) + shape = subtract(shape,rectangle(0,width,width/2,height)) + shape = add(shape,rectangle(0,line,width/2,2*height/3)) + shape = add(shape,rectangle(width-line,width,0,2*height/3)) + glyphs['u'] = shape + + shape = triangle(0,height,width,height,width/2,0) + shape = subtract(shape,triangle(0,height+3*line,width,height+3*line,width/2,3*line)) + glyphs['V'] = shape + + w = 2*height/3.0 + shape = triangle(0,w,width,w,width/2,0) + shape = subtract(shape,triangle(0,w+2*line,width,w+2*line,width/2,2*line)) + glyphs['v'] = shape + + shape = triangle(0,height,width,height,width/2,0) + shape = add(shape,move(shape,.6*width,0)) + cutout = triangle(0,height+4*line,width,height+4*line,width/2,4*line) + cutout = add(cutout,move(cutout,.6*width,0)) + shape = subtract(shape,cutout) + shape = scale_x(shape,0,1/1.6) + glyphs['W'] = shape + + glyphs['w'] = scale_y(glyphs['W'],0,width/height) + + shape = rectangle(0,width,0,height) + shape = subtract(shape,triangle(0,0,0,height,width/2-.7*line,height/2)) + shape = subtract(shape,triangle(width,0,width/2+.7*line,height/2,width,height)) + shape = subtract(shape,triangle(line,height,width-line,height,width/2,height/2+line)) + shape = subtract(shape,triangle(line,0,width/2,height/2-line,width-line,0)) + glyphs['X'] = shape + + glyphs['x'] = scale_y(glyphs['X'], 0, width/height) + + shape = rectangle(0,width,height/2,height) + shape = subtract(shape,triangle(0,height/2,0,height,width/2-line/2,height/2)) + shape = subtract(shape,triangle(width/2+line/2,height/2,width,height,width,height/2)) + shape = subtract(shape,triangle(1.1*line,height,width-1.1*line,height,width/2,height/2+1.1*line)) + shape = add(shape,rectangle(width/2-line/2,width/2+line/2,0,height/2)) + glyphs['Y'] = shape + + + shape = rectangle(0,width,-height/3,width) + shape = subtract(shape,triangle(0,-height/3,0,width,width/2-line*0.8,0)) + shape = subtract(shape,triangle(1.1*line,width,width-1.1*line,width,width/2-width*0.05,1.6*line)) + shape = subtract(shape,triangle(width/3,-height/3,width,width,width,-height/3)) + glyphs['y'] = shape + + shape = rectangle(0,width,0,height) + shape = subtract(shape,triangle(0,line,0,height-line,width-1.4*line,height-line)) + shape = subtract(shape,triangle(1.4*line,line,width,height-line,width,line)) + glyphs['Z'] = shape + + w = 2*height/3 + shape = rectangle(0,width,0,w) + shape = subtract(shape,triangle(0,line,0,w-line,width-1.6*line,w-line)) + shape = subtract(shape,triangle(width,line,1.6*line,line,width,w-line)) + glyphs['z'] = shape + + shape = circle(width/2,width/2,width/2) + shape = subtract(shape,circle(width/2,width/2,width/2-0.8*line)) + shape = scale_y(shape,0,height/width) + glyphs['0'] = shape + + shape = rectangle(width/2-line/2,width/2+line/2,0,height) + w = width/2-line/2 + cutout = circle(0,height,w) + shape = add(shape,rectangle(0,width/2,height-w-line,height)) + shape = subtract(shape,cutout) + shape = move(shape,(width/2+line/2)/4,0) + glyphs['1'] = shape + + shape = circle(width/2,height-width/2,width/2) + shape = subtract(shape,circle(width/2,height-width/2,width/2-line)) + shape = subtract(shape,rectangle(0,width/2,0,height-width/2)) + shape = add(shape,rectangle(0,width,0,height-width/2)) + shape = subtract(shape,triangle(0,line,0,height-width/2,width-line,height-width/2)) + shape = subtract(shape,triangle(1.5*line,line,width,height-width/2-.5*line,width,line)) + glyphs['2'] = shape + + shape = circle(width/2,width/2,width/2) + shape = subtract(shape,circle(width/2,width/2,width/2-line)) + shape = scale_y(shape,0,(height/2+line/2)/width) + shape = add(shape,move(shape,0,height/2-line/2)) + shape = subtract(shape,rectangle(0,width/2,height/4,3*height/4)) + glyphs['3'] = shape + + shape = rectangle(width-line,width,0,height) + shape = add(shape,triangle(0,height/3,width-line,height,width-line,height/3)) + shape = subtract(shape,triangle(width/2*line,height/3+line,width-line,height-1.5*line,width-line,height/3+line)) + glyphs['4'] = shape + + ######## + + shape = circle(width/2,width/2,width/2) + shape = subtract(shape,circle(width/2,width/2,width/2-line)) + shape = subtract(shape,rectangle(0,width/2,width/2,width)) + shape = add(shape,rectangle(0,width/2,width-line,width)) + shape = add(shape,rectangle(0,line,width-line,height)) + shape = add(shape,rectangle(0,width,height-line,height)) + glyphs['5'] = shape + + shape = circle(width/2,height-width/2,width/2) + shape = subtract(shape,circle(width/2,height-width/2,width/2-line)) + shape = subtract(shape,rectangle(0,width,0,height-width/2)) + shape = subtract(shape,triangle(width,height,width,height/2,width/2,height/2)) + shape = add(shape,circle(width/2,width/2,width/2)) + shape = subtract(shape,circle(width/2,width/2,width/2-line)) + shape = add(shape,rectangle(0,line,width/2,height-width/2)) + glyphs['6'] = shape + + shape = rectangle(0,width,0,height) + shape = subtract(shape,triangle(0,0,0,height-line,width-line,height-line)) + shape = subtract(shape,triangle(line,0,width,height-line,width,0)) + glyphs['7'] = shape + + shape = circle(width/2,width/2,width/2) + shape = subtract(shape,circle(width/2,width/2,width/2-line)) + shape = scale_y(shape,0,(height/2+line/2)/width) + shape = add(shape,move(shape,0,height/2-line/2)) + glyphs['8'] = shape + + shape = circle(width/2,width/2,width/2) + shape = subtract(shape,circle(width/2,width/2,width/2-line)) + shape = subtract(shape,rectangle(0,width,width/2,height)) + shape = subtract(shape,triangle(0,0,0,height/2,width/2,height/2)) + shape = add(shape,circle(width/2,height-width/2,width/2)) + shape = subtract(shape,circle(width/2,height-width/2,width/2-line)) + shape = add(shape,rectangle(width-line,width,width/2,height-width/2)) + glyphs['9'] = shape + + w = width/2 + shape = circle(w,w,w) + shape = subtract(shape,circle(w,w,w-line)) + shape = subtract(shape,rectangle(w,width,0,height)) + shape = scale_y(shape,0,height/width) + shape = move(shape,w/2,0) + glyphs['('] = shape + + shape = reflect_x(glyphs['('],width) + glyphs[')'] = shape + + glyphs[' '] = '0' + + shape = rectangle(width/2-width/3,width/2+width/3,height/2-line/2,height/2+line/2) + shape = add(shape,rectangle(width/2-line/2,width/2+line/2,height/2-width/3,height/2+width/3)) + glyphs['+'] = shape + + shape = rectangle(width/2-width/3,width/2+width/3,height/2-line/2,height/2+line/2) + glyphs['-'] = shape + + shape = circle(width/2,line,3*line/4) + glyphs['.'] = shape + + shape = rectangle(0,width,0,height) + shape = subtract(shape,triangle(4*line/5,0,width,height-4*line/5,width,0)) + shape = subtract(shape,triangle(0,4*line/5,0,height,width-4*line/5,height)) + glyphs['/'] = shape + + # + # to be done + # + # glyphs['~'] = False + # glyphs['!'] = False + # glyphs['@'] = False + # glyphs['#'] = False + # glyphs['$'] = False + # glyphs['%'] = False + # glyphs['^'] = False + # glyphs['&'] = False + # glyphs['&'] = False + # glyphs['_'] = False + # glyphs['='] = False + # glyphs['['] = False + # glyphs['{'] = False + # glyphs[']'] = False + # glyphs['}'] = False + # glyphs[';'] = False + # glyphs[':'] = False + # glyphs["'"] = False + # glyphs['"'] = False + # glyphs[','] = False + # glyphs['<'] = False + # glyphs['>'] = False + # glyphs['?'] = False + + return glyphs, width, height + +def set_font(height): + global glyphs, glyph_width, glyph_height + glyphs, glyph_width, glyph_height = make_glyphs(height) + +def text(text, x, y, height = None, align = 'CC'): + dx = 0 + dy = 0 + + text_shape = '0' + + if height is None: + height = glyph_height + + if height == glyph_height: + width = glyph_width + glyphs_ = glyphs + else: + glyphs_, width, height = make_glyphs(height) + + + for line in text.split('\n'): + line_shape = '0' + for chr in line: + # Only draw characters that we have glyphs for + if not chr in glyphs_.keys(): + print "Unknown character:", chr + continue + + line_shape = add(line_shape, move(glyphs_[chr], dx, dy)) + + dx += 3*width/2 + + # Horizontally align this line of text + dx -= 3*width/2 + if (align[0] == 'L'): + text_shape = add(text_shape, line_shape) + elif (align[0] == 'C'): + text_shape = add(text_shape, move(line_shape, -dx / 2, 0)) + elif (align[0] == 'R'): + text_shape = add(text_shape, move(line_shape, -dx, 0)) + else: + print "Invalid horizontal alignment character." + + dx = 0 + dy -= 3*height/2 + + dy += 3*height/2 + + # Vertically align the text + if (align[1] == 'T'): + pass + elif (align[1] == 'C'): + text_shape = move(text_shape, 0, -dy/2) + elif (align[1] == 'B'): + text_shape = move(text_shape, 0, -dy) + else: + print "Invalid vertical alignment character." + + text_shape = move(text_shape, x, y) + return text_shape + +set_font(1.0) \ No newline at end of file diff --git a/src/py/fab_mods.py b/src/py/fab_mods.py new file mode 100644 index 0000000..a0ea104 --- /dev/null +++ b/src/py/fab_mods.py @@ -0,0 +1,126 @@ +# +# fab_mods.py +# fab module definitions +# +# Neil Gershenfeld 9/1/13 +# (c) Massachusetts Institute of Technology 2013 +# +# This work may be reproduced, modified, distributed, +# performed, and displayed for any purpose, but must +# acknowledge the fab modules project. Copyright is +# retained and must be preserved. The work is provided +# as is; no warranty is provided, and users accept all +# liability. +# +# imports +# +import wx,sys,os +# +# set workflows +# +def set_workflows(frame,formats,workflows): + frame.formats.Append("image (.png)") + formats.append(".png") + frame.formats.Append("volume (.gif)") + formats.append(".gif") + frame.formats.Append("mesh (.stl)") + formats.append(".stl") + frame.formats.Append("drawing (.svg)") + formats.append(".svg") + frame.formats.Append("program (.cad)") + formats.append(".cad") + frame.formats.Append("expression (.math)") + formats.append(".math") + # + frame.processes.Append("image (.png)") + workflows["image (.png) : image (.png)"] = "make_png_png" + workflows["program (.cad) : image (.png)"] = "make_cad_png" + workflows["mesh (.stl) : image (.png)"] = "make_stl_png" + # + frame.processes.Append("Encapsulated PostScript (.eps)") + workflows["image (.png) : Encapsulated PostScript (.eps)"] = "make_png_eps" + workflows["program (.cad) : Encapsulated PostScript (.eps)"] = "make_cad_eps" + workflows["expression (.math) : Encapsulated PostScript (.eps)"] = "make_math_eps" + frame.processes.Append("PostScript halftone (.eps)") + workflows["image (.png) : PostScript halftone (.eps)"] = "make_png_eps_halftone" + # + frame.processes.Append("DXF (.dxf)") + workflows["image (.png) : DXF (.dxf)"] = "make_png_dxf" + workflows["program (.cad) : DXF (.dxf)"] = "make_cad_dxf" + workflows["expression (.math) : DXF (.dxf)"] = "make_math_dxf" + # + frame.processes.Append("Gerber (.grb)") + workflows["image (.png) : Gerber (.grb)"] = "make_png_grb" + workflows["program (.cad) : Gerber (.grb)"] = "make_cad_grb" + workflows["expression (.math) : Gerber (.grb)"] = "make_math_grb" + # + frame.processes.Append("Excellon (.drl)") + workflows["image (.png) : Excellon (.drl)"] = "make_png_drl" + workflows["program (.cad) : Excellon (.drl)"] = "make_cad_drl" + workflows["expression (.math) : Excellon (.drl)"] = "make_math_drl" + # + frame.processes.Append("Epilog lasercutter (.epi)") + workflows["image (.png) : Epilog lasercutter (.epi)"] = "make_png_epi" + workflows["program (.cad) : Epilog lasercutter (.epi)"] = "make_cad_epi" + workflows["expression (.math) : Epilog lasercutter (.epi)"] = "make_math_epi" + workflows["drawing (.svg) : Epilog lasercutter (.epi)"] = "make_svg_epi" + frame.processes.Append("Epilog halftone (.epi)") + workflows["image (.png) : Epilog halftone (.epi)"] = "make_png_epi_halftone" + # + frame.processes.Append("Universal lasercutter (.uni)") + workflows["image (.png) : Universal lasercutter (.uni)"] = "make_png_uni" + workflows["program (.cad) : Universal lasercutter (.uni)"] = "make_cad_uni" + workflows["expression (.math) : Universal lasercutter (.uni)"] = "make_math_uni" + workflows["drawing (.svg) : Universal lasercutter (.uni)"] = "make_svg_uni" + # + frame.processes.Append("Universal halftone (.uni)") + workflows["image (.png) : Universal halftone (.uni)"] = "make_png_uni_halftone" + # + frame.processes.Append("Resonetics excimer (.oms)") + workflows["image (.png) : Resonetics excimer (.oms)"] = "make_png_oms" + workflows["drawing (.svg) : Resonetics excimer (.oms)"] = "make_svg_oms" + # + frame.processes.Append("Omax waterjet (.ord)") + workflows["image (.png) : Omax waterjet (.ord)"] = "make_png_ord" + workflows["program (.cad) : Omax waterjet (.ord)"] = "make_cad_ord" + workflows["expression (.math) : Omax waterjet (.ord)"] = "make_math_ord" + workflows["drawing (.svg) : Omax waterjet (.ord)"] = "make_svg_ord" + # + frame.processes.Append("mesh (.stl)") + workflows["volume (.gif) : mesh (.stl)"] = "make_gif_stl" + workflows["program (.cad) : mesh (.stl)"] = "make_cad_stl" + workflows["expression (.math) : mesh (.stl)"] = "make_math_stl" + # + frame.processes.Append("Roland vinylcutter (.camm)") + workflows["image (.png) : Roland vinylcutter (.camm)"] = "make_png_camm" + workflows["program (.cad) : Roland vinylcutter (.camm)"] = "make_cad_camm" + workflows["expression (.math) : Roland vinylcutter (.camm)"] = "make_math_camm" + workflows["drawing (.svg) : Roland vinylcutter (.camm)"] = "make_svg_camm" + # + frame.processes.Append("Roland Modela (.rml)") + workflows["image (.png) : Roland Modela (.rml)"] = "make_png_rml" + workflows["program (.cad) : Roland Modela (.rml)"] = "make_cad_rml" + workflows["expression (.math) : Roland Modela (.rml)"] = "make_math_rml" + workflows["mesh (.stl) : Roland Modela (.rml)"] = "make_stl_rml" + workflows["drawing (.svg) : Roland Modela (.rml)"] = "make_svg_rml" + workflows["image (.png) : Roland Modela (.rml)"] = "make_png_rml" + # + frame.processes.Append("G-codes (.g)") + workflows["image (.png) : G-codes (.g)"] = "make_png_g" + workflows["program (.cad) : G-codes (.g)"] = "make_cad_g" + workflows["expression (.math) : G-codes (.g)"] = "make_math_g" + workflows["mesh (.stl) : G-codes (.g)"] = "make_stl_g" + workflows["drawing (.svg) : G-codes (.g)"] = "make_svg_g" + # + frame.processes.Append("ShopBot (.sbp)") + workflows["image (.png) : ShopBot (.sbp)"] = "make_png_sbp" + workflows["program (.cad) : ShopBot (.sbp)"] = "make_cad_sbp" + workflows["expression (.math) : ShopBot (.sbp)"] = "make_math_sbp" + workflows["mesh (.stl) : ShopBot (.sbp)"] = "make_stl_sbp" + workflows["drawing (.svg) : ShopBot (.sbp)"] = "make_svg_sbp" + # + frame.processes.Append("MTM Snap") + workflows["image (.png) : MTM Snap"] = "make_png_snap" + workflows["program (.cad) : MTM Snap"] = "make_cad_snap" + workflows["mesh (.stl) : MTM Snap"] = "make_stl_snap" + workflows["drawing (.svg) : MTM Snap"] = "make_svg_snap" diff --git a/src/py/fab_set.py b/src/py/fab_set.py new file mode 100644 index 0000000..1161a08 --- /dev/null +++ b/src/py/fab_set.py @@ -0,0 +1,1299 @@ +# +# fab_set.py +# fab modules frame and set workflow defaults +# +# Neil Gershenfeld 9/1/13 +# (c) Massachusetts Institute of Technology 2013 +# +# This work may be reproduced, modified, distributed, +# performed, and displayed for any purpose, but must +# acknowledge the fab modules project. Copyright is +# retained and must be preserved. The work is provided +# as is; no warranty is provided, and users accept all +# liability. +# +# imports +# +import wx,sys,os +# +# frame class +# +class fab_frame(wx.Frame): + # + # set .png .epi defaults + # + def set_png_epi(self): + self.defaults = {} + self.control_panel.defaults.Append('cardboard') + self.defaults["cardboard"]\ + = "self.png_path_panel.diameter_rough.SetValue('0.25');\ + self.png_path_panel.error_rough.SetValue('1.5');\ + self.png_path_panel.diameter_finish.SetValue('0.25');\ + self.png_path_panel.clearance_diameter_finish.SetValue('0.25');\ + self.png_path_panel.error_finish.SetValue('1.5');\ + self.png_path_panel.diameter_2D.SetValue('0.25');\ + self.png_path_panel.error_2D.SetValue('1.5');\ + self.epi_panel.power_2D.SetValue('25');\ + self.epi_panel.speed_2D.SetValue('75');\ + self.epi_panel.min_power_3D.SetValue('5');\ + self.epi_panel.max_power_3D.SetValue('25');\ + self.epi_panel.speed_3D.SetValue('75');" + self.control_panel.defaults.Append('acrylic') + self.defaults["acrylic"]\ + = "self.png_path_panel.diameter_rough.SetValue('0.25');\ + self.png_path_panel.error_rough.SetValue('1.5');\ + self.png_path_panel.diameter_finish.SetValue('0.25');\ + self.png_path_panel.clearance_diameter_finish.SetValue('0.25');\ + self.png_path_panel.error_finish.SetValue('1.5');\ + self.png_path_panel.diameter_2D.SetValue('0.25');\ + self.png_path_panel.error_2D.SetValue('1.5');\ + self.epi_panel.power_2D.SetValue('75');\ + self.epi_panel.speed_2D.SetValue('25');\ + self.epi_panel.min_power_3D.SetValue('25');\ + self.epi_panel.max_power_3D.SetValue('75');\ + self.epi_panel.speed_3D.SetValue('25');" + # + # set .png .epi halftone defaults + # + def set_png_epi_halftone(self): + self.defaults = {} + self.control_panel.defaults.Append('cardboard') + self.defaults["cardboard"]\ + = "self.epi_panel.power_2D.SetValue('25');\ + self.epi_panel.speed_2D.SetValue('75');" + self.control_panel.defaults.Append('acrylic') + self.defaults["acrylic"]\ + = "self.epi_panel.power_2D.SetValue('75');\ + self.epi_panel.speed_2D.SetValue('25');" + # + # set .cad .epi defaults + # + def set_cad_epi(self): + self.defaults = {} + self.control_panel.defaults.Append('cardboard') + self.defaults["cardboard"]\ + = "self.cad_png_panel.resolution.SetValue('10');\ + self.png_path_panel.diameter_rough.SetValue('0.25');\ + self.png_path_panel.error_rough.SetValue('1.5');\ + self.png_path_panel.diameter_finish.SetValue('0.25');\ + self.png_path_panel.clearance_diameter_finish.SetValue('0.25');\ + self.png_path_panel.error_finish.SetValue('1.5');\ + self.png_path_panel.diameter_2D.SetValue('0.25');\ + self.png_path_panel.error_2D.SetValue('1.5');\ + self.epi_panel.power_2D.SetValue('25');\ + self.epi_panel.speed_2D.SetValue('75');\ + self.epi_panel.min_power_3D.SetValue('5');\ + self.epi_panel.max_power_3D.SetValue('25');\ + self.epi_panel.speed_3D.SetValue('75');" + self.control_panel.defaults.Append('acrylic') + self.defaults["acrylic"]\ + = "self.cad_png_panel.resolution.SetValue('10');\ + self.png_path_panel.diameter_rough.SetValue('0.25');\ + self.png_path_panel.error_rough.SetValue('1.5');\ + self.png_path_panel.diameter_finish.SetValue('0.25');\ + self.png_path_panel.clearance_diameter_finish.SetValue('0.25');\ + self.png_path_panel.error_finish.SetValue('1.5');\ + self.png_path_panel.diameter_2D.SetValue('0.25');\ + self.png_path_panel.error_2D.SetValue('1.5');\ + self.epi_panel.power_2D.SetValue('75');\ + self.epi_panel.speed_2D.SetValue('25');\ + self.epi_panel.min_power_3D.SetValue('25');\ + self.epi_panel.max_power_3D.SetValue('75');\ + self.epi_panel.speed_3D.SetValue('25');" + # + # set .math .epi defaults + # + def set_math_epi(self): + self.defaults = {} + self.control_panel.defaults.Append('cardboard') + self.defaults["cardboard"]\ + = "self.png_panel.resolution.SetValue('10');\ + self.png_path_panel.diameter_rough.SetValue('0.25');\ + self.png_path_panel.error_rough.SetValue('1.5');\ + self.png_path_panel.diameter_finish.SetValue('0.25');\ + self.png_path_panel.clearance_diameter_finish.SetValue('0.25');\ + self.png_path_panel.error_finish.SetValue('1.5');\ + self.png_path_panel.diameter_2D.SetValue('0.25');\ + self.png_path_panel.error_2D.SetValue('1.5');\ + self.epi_panel.power_2D.SetValue('25');\ + self.epi_panel.speed_2D.SetValue('75');\ + self.epi_panel.min_power_3D.SetValue('5');\ + self.epi_panel.max_power_3D.SetValue('25');\ + self.epi_panel.speed_3D.SetValue('75');" + self.control_panel.defaults.Append('acrylic') + self.defaults["acrylic"]\ + = "self.png_panel.resolution.SetValue('10');\ + self.png_path_panel.diameter_rough.SetValue('0.25');\ + self.png_path_panel.error_rough.SetValue('1.5');\ + self.png_path_panel.diameter_finish.SetValue('0.25');\ + self.png_path_panel.clearance_diameter_finish.SetValue('0.25');\ + self.png_path_panel.error_finish.SetValue('1.5');\ + self.png_path_panel.diameter_2D.SetValue('0.25');\ + self.png_path_panel.error_2D.SetValue('1.5');\ + self.epi_panel.power_2D.SetValue('75');\ + self.epi_panel.speed_2D.SetValue('25');\ + self.epi_panel.min_power_3D.SetValue('25');\ + self.epi_panel.max_power_3D.SetValue('75');\ + self.epi_panel.speed_3D.SetValue('25');" + # + # set .svg .epi defaults + # + def set_svg_epi(self): + self.defaults = {} + self.control_panel.defaults.Append('cardboard') + self.defaults["cardboard"]\ + = "self.epi_panel.power_2D.SetValue('25');\ + self.epi_panel.speed_2D.SetValue('75');\ + self.epi_panel.min_power_3D.SetValue('5');\ + self.epi_panel.max_power_3D.SetValue('25');\ + self.epi_panel.speed_3D.SetValue('75');" + self.control_panel.defaults.Append('acrylic') + self.defaults["acrylic"]\ + = "self.epi_panel.power_2D.SetValue('75');\ + self.epi_panel.speed_2D.SetValue('25');\ + self.epi_panel.min_power_3D.SetValue('25');\ + self.epi_panel.max_power_3D.SetValue('75');\ + self.epi_panel.speed_3D.SetValue('25');" + # + # set .png .uni defaults + # + def set_png_uni(self): + self.defaults = {} + self.control_panel.defaults.Append('cardboard') + self.defaults["cardboard"]\ + = "self.png_path_panel.diameter.SetValue('0.25');\ + self.png_path_panel.error.SetValue('1.5');\ + self.uni_panel.power.SetValue('25');\ + self.uni_panel.speed.SetValue('75');" + self.control_panel.defaults.Append('acrylic') + self.defaults["acrylic"]\ + = "self.png_path_panel.diameter.SetValue('0.25');\ + self.png_path_panel.error.SetValue('1.5');\ + self.uni_panel.power.SetValue('75');\ + self.uni_panel.speed.SetValue('25');" + # + # set .png .uni halftone defaults + # + def set_png_uni_halftone(self): + self.defaults = {} + self.control_panel.defaults.Append('cardboard') + self.defaults["cardboard"]\ + = "self.uni_panel.power.SetValue('25');\ + self.uni_panel.speed.SetValue('75');" + self.control_panel.defaults.Append('acrylic') + self.defaults["acrylic"]\ + = "self.uni_panel.power.SetValue('75');\ + self.uni_panel.speed.SetValue('25');" + # + # set .cad .uni defaults + # + def set_cad_uni(self): + self.defaults = {} + self.control_panel.defaults.Append('cardboard') + self.defaults["cardboard"]\ + = "self.png_panel.resolution.SetValue('10');\ + self.png_path_panel.diameter.SetValue('0.25');\ + self.png_path_panel.error.SetValue('1.5');\ + self.uni_panel.power.SetValue('25');\ + self.uni_panel.speed.SetValue('75');" + self.control_panel.defaults.Append('acrylic') + self.defaults["acrylic"]\ + = "self.png_panel.resolution.SetValue('10');\ + self.png_path_panel.diameter.SetValue('0.25');\ + self.png_path_panel.error.SetValue('1.5');\ + self.uni_panel.power.SetValue('75');\ + self.uni_panel.speed.SetValue('25');" + # + # set .math .uni defaults + # + def set_math_uni(self): + self.defaults = {} + self.control_panel.defaults.Append('cardboard') + self.defaults["cardboard"]\ + = "self.png_panel.resolution.SetValue('10');\ + self.png_path_panel.diameter.SetValue('0.25');\ + self.png_path_panel.error.SetValue('1.5');\ + self.uni_panel.power.SetValue('25');\ + self.uni_panel.speed.SetValue('75');" + self.control_panel.defaults.Append('acrylic') + self.defaults["acrylic"]\ + = "self.png_panel.resolution.SetValue('10');\ + self.png_path_panel.diameter.SetValue('0.25');\ + self.png_path_panel.error.SetValue('1.5');\ + self.uni_panel.power.SetValue('75');\ + self.uni_panel.speed.SetValue('25');" + # + # set .svg .uni defaults + # + def set_svg_uni(self): + self.defaults = {} + self.control_panel.defaults.Append('cardboard') + self.defaults["cardboard"]\ + = "self.uni_panel.power.SetValue('25');\ + self.uni_panel.speed.SetValue('75');" + self.control_panel.defaults.Append('acrylic') + self.defaults["acrylic"]\ + = "self.uni_panel.power.SetValue('75');\ + self.uni_panel.speed.SetValue('25');" + # + # set .png .rml defaults + # + def set_png_rml(self): + self.defaults = {} + self.control_panel.defaults.Append('mill traces (1/64)') + self.defaults["mill traces (1/64)"]\ + = "self.png_path_panel.diameter_plane.SetValue('0.4');\ + self.png_path_panel.number_plane.SetValue('4');\ + self.png_path_panel.z_plane.SetValue('-0.1');\ + self.rml_panel.button.Hide();\ + self.rml_panel.speed.SetValue('4');\ + self.rml_panel.zjog.SetValue('1.0');\ + self.path_type = '3D plane';\ + self.update_panels();" + self.control_panel.defaults.Append('mill traces (0.010)') + self.defaults["mill traces (0.010)"]\ + = "self.png_path_panel.diameter_plane.SetValue('0.254');\ + self.png_path_panel.number_plane.SetValue('1');\ + self.png_path_panel.z_plane.SetValue('-0.1');\ + self.rml_panel.button.Hide();\ + self.rml_panel.speed.SetValue('2');\ + self.rml_panel.zjog.SetValue('1.0');\ + self.path_type = '3D plane';\ + self.update_panels();" + self.control_panel.defaults.Append('cut out board (1/32)') + self.defaults["cut out board (1/32)"]\ + = "self.png_path_panel.diameter_rough.SetValue('0.79');\ + self.png_path_panel.number_rough.SetValue('1');\ + self.png_path_panel.itop_rough.SetValue('0.5');\ + self.png_path_panel.ibot_rough.SetValue('0.5');\ + self.png_path_panel.ztop_rough.SetValue('-0.6');\ + self.png_path_panel.zbot_rough.SetValue('-1.7');\ + self.png_path_panel.zstep_rough.SetValue('0.6');\ + self.rml_panel.button.Hide();\ + self.rml_panel.speed.SetValue('4');\ + self.rml_panel.zjog.SetValue('1.0');\ + self.path_type = '3D rough';\ + self.update_panels();" + self.control_panel.defaults.Append('wax rough cut (1/8)') + self.defaults["wax rough cut (1/8)"]\ + = "self.png_path_panel.diameter_rough.SetValue('3.175');\ + self.png_path_panel.number_rough.SetValue('-1');\ + self.png_path_panel.overlap_rough.SetValue('0.25');\ + self.png_path_panel.error_rough.SetValue('1.5');\ + self.png_path_panel.zstep_rough.SetValue('1');\ + self.png_path_panel.ibot_rough.SetValue('0');\ + self.png_path_panel.itop_rough.SetValue('1');\ + self.png_path_panel.zbot_rough.SetValue('-10');\ + self.png_path_panel.ztop_rough.SetValue('0');\ + self.rml_panel.button.Hide();\ + self.rml_panel.speed.SetValue('20');\ + self.path_type = '3D rough';\ + self.update_panels();" + self.control_panel.defaults.Append('wax finish cut (1/8)') + self.defaults["wax finish cut (1/8)"]\ + = "self.png_path_panel.diameter_finish.SetValue('3.175');\ + self.png_path_panel.clearance_diameter_finish.SetValue('3.175');\ + self.png_path_panel.overlap_finish.SetValue('0.5');\ + self.png_path_panel.error_finish.SetValue('1.5');\ + self.png_path_panel.xz_finish.SetValue(True);\ + self.png_path_panel.yz_finish.SetValue(True);\ + self.png_path_panel.ibot_finish.SetValue('0');\ + self.png_path_panel.itop_finish.SetValue('1');\ + self.png_path_panel.zbot_finish.SetValue('-10');\ + self.png_path_panel.ztop_finish.SetValue('0');\ + self.rml_panel.button.Hide();\ + self.rml_panel.speed.SetValue('20');\ + self.path_type = '3D finish';\ + self.update_panels();" + # + # set .cad .rml defaults + # + def set_cad_rml(self): + self.defaults = {} + self.control_panel.defaults.Append('mill traces (1/64)') + self.defaults["mill traces (1/64)"]\ + = "self.cad_png_panel.resolution.SetValue('50');\ + self.png_path_panel.diameter_plane.SetValue('0.4');\ + self.png_path_panel.number_plane.SetValue('4');\ + self.png_path_panel.z_plane.SetValue('-0.1');\ + self.rml_panel.button.Hide();\ + self.rml_panel.speed.SetValue('4');\ + self.rml_panel.zjog.SetValue('1.0');\ + self.path_type = '3D plane';\ + self.update_panels();" + self.control_panel.defaults.Append('mill traces (0.010)') + self.defaults["mill traces (0.010)"]\ + = "self.cad_png_panel.resolution.SetValue('50');\ + self.png_path_panel.diameter_plane.SetValue('0.254');\ + self.png_path_panel.number_plane.SetValue('1');\ + self.png_path_panel.z_plane.SetValue('-0.1');\ + self.rml_panel.button.Hide();\ + self.rml_panel.speed.SetValue('2');\ + self.rml_panel.zjog.SetValue('1.0');\ + self.path_type = '3D plane';\ + self.update_panels();" + self.control_panel.defaults.Append('cut out board (1/32)') + self.defaults["cut out board (1/32)"]\ + = "self.cad_png_panel.resolution.SetValue('50');\ + self.png_path_panel.diameter_rough.SetValue('0.79');\ + self.png_path_panel.number_rough.SetValue('1');\ + self.png_path_panel.itop_rough.SetValue('0.5');\ + self.png_path_panel.ibot_rough.SetValue('0.5');\ + self.png_path_panel.ztop_rough.SetValue('-0.6');\ + self.png_path_panel.zbot_rough.SetValue('-1.7');\ + self.png_path_panel.zstep_rough.SetValue('0.6');\ + self.rml_panel.button.Hide();\ + self.rml_panel.speed.SetValue('4');\ + self.rml_panel.zjog.SetValue('1.0');\ + self.path_type = '3D rough';\ + self.update_panels();" + self.control_panel.defaults.Append('wax rough cut (1/8)') + self.defaults["wax rough cut (1/8)"]\ + = "self.cad_png_panel.resolution.SetValue('25');\ + self.png_path_panel.diameter_rough.SetValue('3.175');\ + self.png_path_panel.number_rough.SetValue('-1');\ + self.png_path_panel.overlap_rough.SetValue('0.25');\ + self.png_path_panel.error_rough.SetValue('1.5');\ + self.png_path_panel.zstep_rough.SetValue('1');\ + self.png_path_panel.ibot_rough.SetValue('0');\ + self.png_path_panel.itop_rough.SetValue('1');\ + self.png_path_panel.zbot_rough.SetValue('');\ + self.png_path_panel.ztop_rough.SetValue('');\ + self.rml_panel.button.Hide();\ + self.rml_panel.speed.SetValue('20');\ + self.path_type = '3D rough';\ + self.update_panels();" + self.control_panel.defaults.Append('wax finish cut (1/8)') + self.defaults["wax finish cut (1/8)"]\ + = "self.cad_png_panel.resolution.SetValue('25');\ + self.png_path_panel.diameter_finish.SetValue('3.175');\ + self.png_path_panel.clearance_diameter_finish.SetValue('3.175');\ + self.png_path_panel.overlap_finish.SetValue('0.5');\ + self.png_path_panel.error_finish.SetValue('1.5');\ + self.png_path_panel.xz_finish.SetValue(True);\ + self.png_path_panel.yz_finish.SetValue(True);\ + self.png_path_panel.ibot_finish.SetValue('0');\ + self.png_path_panel.itop_finish.SetValue('1');\ + self.png_path_panel.zbot_finish.SetValue('');\ + self.png_path_panel.ztop_finish.SetValue('');\ + self.rml_panel.button.Hide();\ + self.rml_panel.speed.SetValue('20');\ + self.path_type = '3D finish';\ + self.update_panels();" + # + # set .math .rml defaults + # + def set_math_rml(self): + self.defaults = {} + self.control_panel.defaults.Append('mill traces (1/64)') + self.defaults["mill traces (1/64)"]\ + = "self.png_panel.resolution.SetValue('50');\ + self.png_path_panel.diameter.SetValue('0.4');\ + self.png_path_panel.number.SetValue('4');\ + self.png_path_panel.z.SetValue('-0.1');\ + self.png_path_panel.frame3_ztop.SetValue('');\ + self.png_path_panel.frame3_zbot.SetValue('');\ + self.png_path_panel.frame3_zstep.SetValue('');\ + self.png_path_panel.frame3_xy.SetValue(False);\ + self.png_path_panel.frame3_xz.SetValue(False);\ + self.png_path_panel.frame3_yz.SetValue(False);\ + self.rml_panel.speed.SetValue('4');\ + self.rml_panel.zjog.SetValue('1.0');" + self.control_panel.defaults.Append('mill traces (0.010)') + self.defaults["mill traces (0.010)"]\ + = "self.png_path_panel.diameter.SetValue('0.254');\ + self.png_path_panel.number.SetValue('1');\ + self.png_path_panel.z.SetValue('-0.1');\ + self.png_path_panel.frame3_ztop.SetValue('');\ + self.png_path_panel.frame3_zbot.SetValue('');\ + self.png_path_panel.frame3_zstep.SetValue('');\ + self.png_path_panel.frame3_xy.SetValue(False);\ + self.png_path_panel.frame3_xz.SetValue(False);\ + self.png_path_panel.frame3_yz.SetValue(False);\ + self.rml_panel.speed.SetValue('2');\ + self.rml_panel.zjog.SetValue('1.0');" + self.control_panel.defaults.Append('cut out board (1/32)') + self.defaults["cut out board (1/32)"]\ + = "self.png_panel.resolution.SetValue('50');\ + self.png_path_panel.diameter.SetValue('0.79');\ + self.png_path_panel.number.SetValue('1');\ + self.png_path_panel.frame3_itop.SetValue('0.5');\ + self.png_path_panel.frame3_ibot.SetValue('0.5');\ + self.png_path_panel.frame3_ztop.SetValue('-0.6');\ + self.png_path_panel.frame3_zbot.SetValue('-1.7');\ + self.png_path_panel.frame3_zstep.SetValue('0.6');\ + self.png_path_panel.frame3_xy.SetValue(True);\ + self.png_path_panel.frame3_xz.SetValue(False);\ + self.png_path_panel.frame3_yz.SetValue(False);\ + self.rml_panel.speed.SetValue('4');\ + self.rml_panel.zjog.SetValue('1.0');" + self.control_panel.defaults.Append('wax rough cut (1/8)') + self.defaults["wax rough cut (1/8)"]\ + = "self.png_panel.resolution.SetValue('25');\ + self.png_path_panel.diameter.SetValue('3.175');\ + self.png_path_panel.number.SetValue('-1');\ + self.png_path_panel.overlap.SetValue('0.25');\ + self.png_path_panel.error.SetValue('1.5');\ + self.png_path_panel.frame3_xy.SetValue(True);\ + self.png_path_panel.frame3_xz.SetValue(False);\ + self.png_path_panel.frame3_yz.SetValue(False);\ + self.png_path_panel.frame3_zstep.SetValue('0.5');\ + self.rml_panel.speed.SetValue('20');" + self.control_panel.defaults.Append('wax finish cut (1/8)') + self.defaults["wax finish cut (1/8)"]\ + = "self.png_panel.resolution.SetValue('25');\ + self.png_path_panel.diameter.SetValue('3.175');\ + self.png_path_panel.number.SetValue('1');\ + self.png_path_panel.overlap.SetValue('0.5');\ + self.png_path_panel.error.SetValue('1.5');\ + self.png_path_panel.frame3_xy.SetValue(False);\ + self.png_path_panel.frame3_xz.SetValue(True);\ + self.png_path_panel.frame3_yz.SetValue(True);\ + self.rml_panel.speed.SetValue('20');" + # + # set .stl .rml defaults + # + def set_stl_rml(self): + self.defaults = {} + self.control_panel.defaults.Append('inches, 1/8, wax, rough') + self.defaults["inches, 1/8, wax, rough"]\ + = "self.png_path_panel.diameter_rough.SetValue('3.175');\ + self.png_path_panel.number_rough.SetValue('-1');\ + self.png_path_panel.overlap_rough.SetValue('0.25');\ + self.png_path_panel.error_rough.SetValue('1.5');\ + self.png_path_panel.zstep_rough.SetValue('1');\ + self.png_path_panel.ibot_rough.SetValue('0');\ + self.png_path_panel.itop_rough.SetValue('1');\ + self.png_path_panel.zbot_rough.SetValue('');\ + self.png_path_panel.ztop_rough.SetValue('');\ + self.stl_png_panel.resolution.SetValue('25');\ + self.rml_panel.button.Hide();\ + self.rml_panel.speed.SetValue('20');\ + self.units = 25.4;\ + self.path_type='3D rough';\ + self.update_panels();" + self.control_panel.defaults.Append('inches, 1/8, wax, finish') + self.defaults["inches, 1/8, wax, finish"]\ + = "self.png_path_panel.diameter_finish.SetValue('3.175');\ + self.png_path_panel.clearance_diameter_finish.SetValue('3.175');\ + self.png_path_panel.overlap_finish.SetValue('0.5');\ + self.png_path_panel.error_finish.SetValue('1.5');\ + self.png_path_panel.xz_finish.SetValue(True);\ + self.png_path_panel.yz_finish.SetValue(True);\ + self.png_path_panel.ibot_finish.SetValue('0');\ + self.png_path_panel.itop_finish.SetValue('1');\ + self.png_path_panel.zbot_finish.SetValue('');\ + self.png_path_panel.ztop_finish.SetValue('');\ + self.stl_png_panel.resolution.SetValue('25');\ + self.rml_panel.button.Hide();\ + self.rml_panel.speed.SetValue('20');\ + self.units = 25.4;\ + self.path_type='3D finish';\ + self.update_panels();" + self.control_panel.defaults.Append('mm, 1/8, wax, rough') + self.defaults["mm, 1/8, wax, rough"]\ + = "self.png_path_panel.diameter_rough.SetValue('3.175');\ + self.png_path_panel.number_rough.SetValue('-1');\ + self.png_path_panel.overlap_rough.SetValue('0.25');\ + self.png_path_panel.error_rough.SetValue('1.5');\ + self.png_path_panel.zstep_rough.SetValue('1');\ + self.png_path_panel.ibot_rough.SetValue('0');\ + self.png_path_panel.itop_rough.SetValue('1');\ + self.png_path_panel.zbot_rough.SetValue('');\ + self.png_path_panel.ztop_rough.SetValue('');\ + self.stl_png_panel.resolution.SetValue('25');\ + self.rml_panel.button.Hide();\ + self.rml_panel.speed.SetValue('20');\ + self.units = 1;\ + self.path_type='3D rough';\ + self.update_panels();" + self.control_panel.defaults.Append('mm, 1/8, wax, finish') + self.defaults["mm, 1/8, wax, finish"]\ + = "self.png_path_panel.diameter_finish.SetValue('3.175');\ + self.png_path_panel.clearance_diameter_finish.SetValue('3.175');\ + self.png_path_panel.overlap_finish.SetValue('0.5');\ + self.png_path_panel.error_finish.SetValue('1.5');\ + self.png_path_panel.xz_finish.SetValue(True);\ + self.png_path_panel.yz_finish.SetValue(True);\ + self.png_path_panel.ibot_finish.SetValue('0');\ + self.png_path_panel.itop_finish.SetValue('1');\ + self.png_path_panel.zbot_finish.SetValue('');\ + self.png_path_panel.ztop_finish.SetValue('');\ + self.stl_png_panel.resolution.SetValue('25');\ + self.rml_panel.button.Hide();\ + self.rml_panel.speed.SetValue('20');\ + self.units = 1;\ + self.path_type='3D finish';\ + self.update_panels();" + # + # set .svg .rml defaults + # + def set_svg_rml(self): + self.defaults = {} + self.control_panel.defaults.Append('wax') + self.defaults["wax"]\ + = "self.rml_panel.speed.SetValue('20');" + # + # set .png .sbp defaults + # + def set_png_sbp(self): + self.defaults = {} + self.control_panel.defaults.Append('wax rough cut (1/8)') + self.defaults["wax rough cut (1/8)"]\ + = "self.png_path_panel.diameter_rough.SetValue('3.175');\ + self.png_path_panel.number_rough.SetValue('-1');\ + self.png_path_panel.overlap_rough.SetValue('0.25');\ + self.png_path_panel.error_rough.SetValue('1.5');\ + self.png_path_panel.zstep_rough.SetValue('1');\ + self.png_path_panel.ibot_rough.SetValue('0');\ + self.png_path_panel.itop_rough.SetValue('1');\ + self.png_path_panel.zbot_rough.SetValue('-10');\ + self.png_path_panel.ztop_rough.SetValue('0');\ + self.sbp_panel.button.Hide();\ + self.sbp_panel.speed.SetValue('20');\ + self.path_type = '3D rough';\ + self.update_panels();" + self.control_panel.defaults.Append('wax finish cut (1/8)') + self.defaults["wax finish cut (1/8)"]\ + = "self.png_path_panel.diameter_finish.SetValue('3.175');\ + self.png_path_panel.clearance_diameter_finish.SetValue('3.175');\ + self.png_path_panel.overlap_finish.SetValue('0.5');\ + self.png_path_panel.error_finish.SetValue('1.5');\ + self.png_path_panel.xz_finish.SetValue(True);\ + self.png_path_panel.yz_finish.SetValue(True);\ + self.png_path_panel.ibot_finish.SetValue('0');\ + self.png_path_panel.itop_finish.SetValue('1');\ + self.png_path_panel.zbot_finish.SetValue('-10');\ + self.png_path_panel.ztop_finish.SetValue('0');\ + self.sbp_panel.button.Hide();\ + self.sbp_panel.speed.SetValue('20');\ + self.path_type = '3D finish';\ + self.update_panels();" + # + # set .png .plt defaults + # + def set_png_plt(self): + self.defaults = {} + # + # set .cad .sbp defaults + # + def set_cad_sbp(self): + self.defaults = {} + self.control_panel.defaults.Append('wax rough cut (1/8)') + self.defaults["wax rough cut (1/8)"]\ + = "self.cad_png_panel.resolution.SetValue('10');\ + self.png_path_panel.diameter_rough.SetValue('3.175');\ + self.png_path_panel.number_rough.SetValue('-1');\ + self.png_path_panel.overlap_rough.SetValue('0.25');\ + self.png_path_panel.error_rough.SetValue('1.5');\ + self.png_path_panel.zstep_rough.SetValue('1');\ + self.png_path_panel.ibot_rough.SetValue('0');\ + self.png_path_panel.itop_rough.SetValue('1');\ + self.png_path_panel.zbot_rough.SetValue('');\ + self.png_path_panel.ztop_rough.SetValue('');\ + self.sbp_panel.button.Hide();\ + self.sbp_panel.speed.SetValue('20');\ + self.units = 25.4;\ + self.path_type='3D rough';\ + self.update_panels();" + self.control_panel.defaults.Append('wax finish cut (1/8)') + self.defaults["wax finish cut (1/8)"]\ + = "self.cad_png_panel.resolution.SetValue('10');\ + self.png_path_panel.diameter_finish.SetValue('3.175');\ + self.png_path_panel.clearance_diameter_finish.SetValue('3.175');\ + self.png_path_panel.overlap_finish.SetValue('0.5');\ + self.png_path_panel.error_finish.SetValue('1.5');\ + self.png_path_panel.xz_finish.SetValue(True);\ + self.png_path_panel.yz_finish.SetValue(True);\ + self.png_path_panel.ibot_finish.SetValue('0');\ + self.png_path_panel.itop_finish.SetValue('1');\ + self.png_path_panel.zbot_finish.SetValue('');\ + self.png_path_panel.ztop_finish.SetValue('');\ + self.sbp_panel.button.Hide();\ + self.sbp_panel.speed.SetValue('20');\ + self.units = 25.4;\ + self.path_type='3D finish';\ + self.update_panels();" + # + # set .math .sbp defaults + # + def set_math_sbp(self): + self.defaults = {} + self.control_panel.defaults.Append('wax rough cut (1/8)') + self.defaults["wax rough cut (1/8)"]\ + = "self.math_png_panel.resolution.SetValue('10');\ + self.png_path_panel.diameter_rough.SetValue('3.175');\ + self.png_path_panel.number_rough.SetValue('-1');\ + self.png_path_panel.overlap_rough.SetValue('0.25');\ + self.png_path_panel.error_rough.SetValue('1.5');\ + self.png_path_panel.zstep_rough.SetValue('1');\ + self.png_path_panel.ibot_rough.SetValue('0');\ + self.png_path_panel.itop_rough.SetValue('1');\ + self.png_path_panel.zbot_rough.SetValue('');\ + self.png_path_panel.ztop_rough.SetValue('');\ + self.sbp_panel.button.Hide();\ + self.sbp_panel.speed.SetValue('20');\ + self.units = 25.4;\ + self.path_type='3D rough';\ + self.update_panels();" + self.control_panel.defaults.Append('wax finish cut (1/8)') + self.defaults["wax finish cut (1/8)"]\ + = "self.math_png_panel.resolution.SetValue('10');\ + self.png_path_panel.diameter_finish.SetValue('3.175');\ + self.png_path_panel.clearance_diameter_finish.SetValue('3.175');\ + self.png_path_panel.overlap_finish.SetValue('0.5');\ + self.png_path_panel.error_finish.SetValue('1.5');\ + self.png_path_panel.xz_finish.SetValue(True);\ + self.png_path_panel.yz_finish.SetValue(True);\ + self.png_path_panel.ibot_finish.SetValue('0');\ + self.png_path_panel.itop_finish.SetValue('1');\ + self.png_path_panel.zbot_finish.SetValue('');\ + self.png_path_panel.ztop_finish.SetValue('');\ + self.sbp_panel.button.Hide();\ + self.sbp_panel.speed.SetValue('20');\ + self.units = 25.4;\ + self.path_type='3D finish';\ + self.update_panels();" + # + # set .stl .sbp defaults + # + def set_stl_sbp(self): + self.defaults = {} + self.control_panel.defaults.Append('inches, 1/8, wax, rough') + self.defaults["inches, 1/8, wax, rough"]\ + = "self.png_path_panel.diameter_rough.SetValue('3.175');\ + self.png_path_panel.number_rough.SetValue('-1');\ + self.png_path_panel.overlap_rough.SetValue('0.25');\ + self.png_path_panel.error_rough.SetValue('1.5');\ + self.png_path_panel.zstep_rough.SetValue('1');\ + self.png_path_panel.ibot_rough.SetValue('0');\ + self.png_path_panel.itop_rough.SetValue('1');\ + self.png_path_panel.zbot_rough.SetValue('');\ + self.png_path_panel.ztop_rough.SetValue('');\ + self.stl_png_panel.resolution.SetValue('25');\ + self.sbp_panel.button.Hide();\ + self.sbp_panel.speed.SetValue('20');\ + self.units = 25.4;\ + self.path_type='3D rough';\ + self.update_panels();" + self.control_panel.defaults.Append('inches, 1/8, wax, finish') + self.defaults["inches, 1/8, wax, finish"]\ + = "self.png_path_panel.diameter_finish.SetValue('3.175');\ + self.png_path_panel.clearance_diameter_finish.SetValue('3.175');\ + self.png_path_panel.overlap_finish.SetValue('0.5');\ + self.png_path_panel.error_finish.SetValue('1.5');\ + self.png_path_panel.xz_finish.SetValue(True);\ + self.png_path_panel.yz_finish.SetValue(True);\ + self.png_path_panel.ibot_finish.SetValue('0');\ + self.png_path_panel.itop_finish.SetValue('1');\ + self.png_path_panel.zbot_finish.SetValue('');\ + self.png_path_panel.ztop_finish.SetValue('');\ + self.stl_png_panel.resolution.SetValue('25');\ + self.sbp_panel.button.Hide();\ + self.sbp_panel.speed.SetValue('20');\ + self.units = 25.4;\ + self.path_type='3D finish';\ + self.update_panels();" + self.control_panel.defaults.Append('mm, 1/8, wax, rough') + self.defaults["mm, 1/8, wax, rough"]\ + = "self.png_path_panel.diameter_rough.SetValue('3.175');\ + self.png_path_panel.number_rough.SetValue('-1');\ + self.png_path_panel.overlap_rough.SetValue('0.25');\ + self.png_path_panel.error_rough.SetValue('1.5');\ + self.png_path_panel.zstep_rough.SetValue('1');\ + self.png_path_panel.ibot_rough.SetValue('0');\ + self.png_path_panel.itop_rough.SetValue('1');\ + self.png_path_panel.zbot_rough.SetValue('');\ + self.png_path_panel.ztop_rough.SetValue('');\ + self.stl_png_panel.resolution.SetValue('25');\ + self.sbp_panel.button.Hide();\ + self.sbp_panel.speed.SetValue('20');\ + self.units = 1;\ + self.path_type='3D rough';\ + self.update_panels();" + self.control_panel.defaults.Append('mm, 1/8, wax, finish') + self.defaults["mm, 1/8, wax, finish"]\ + = "self.png_path_panel.diameter_finish.SetValue('3.175');\ + self.png_path_panel.clearance_diameter_finish.SetValue('3.175');\ + self.png_path_panel.overlap_finish.SetValue('0.5');\ + self.png_path_panel.error_finish.SetValue('1.5');\ + self.png_path_panel.xz_finish.SetValue(True);\ + self.png_path_panel.yz_finish.SetValue(True);\ + self.png_path_panel.ibot_finish.SetValue('0');\ + self.png_path_panel.itop_finish.SetValue('1');\ + self.png_path_panel.zbot_finish.SetValue('');\ + self.png_path_panel.ztop_finish.SetValue('');\ + self.stl_png_panel.resolution.SetValue('25');\ + self.sbp_panel.button.Hide();\ + self.sbp_panel.speed.SetValue('20');\ + self.units = 1;\ + self.path_type='3D finish';\ + self.update_panels();" + # + # set .svg .sbp defaults + # + def set_svg_sbp(self): + self.defaults = {} + self.control_panel.defaults.Append('wax') + self.defaults["wax"]\ + = "self.sbp_panel.speed.SetValue('20');\ + self.sbp_panel.button.Hide();\ + self.svg_path_panel.zmin.SetValue('-10.0');\ + self.svg_path_panel.zmax.SetValue('0.0');\ + self.path_type='3D';\ + self.update_panels();" + # + # set .png .g defaults + # + def set_png_g(self): + self.defaults = {} + self.control_panel.defaults.Append('mill traces (1/64)') + self.defaults["mill traces (1/64)"]\ + = "self.png_path_panel.diameter_plane.SetValue('0.4');\ + self.png_path_panel.number_plane.SetValue('4');\ + self.png_path_panel.z_plane.SetValue('-0.1');\ + self.g_panel.button.Hide();\ + self.g_panel.speed.SetValue('4');\ + self.path_type = '3D plane';\ + self.update_panels();" + self.control_panel.defaults.Append('cut out board (1/32)') + self.defaults["cut out board (1/32)"]\ + = "self.png_path_panel.diameter_rough.SetValue('0.79');\ + self.png_path_panel.number_rough.SetValue('1');\ + self.png_path_panel.itop_rough.SetValue('0.5');\ + self.png_path_panel.ibot_rough.SetValue('0.5');\ + self.png_path_panel.ztop_rough.SetValue('-0.6');\ + self.png_path_panel.zbot_rough.SetValue('-1.7');\ + self.png_path_panel.zstep_rough.SetValue('0.6');\ + self.g_panel.button.Hide();\ + self.g_panel.speed.SetValue('4');\ + self.path_type = '3D rough';\ + self.update_panels();" + self.control_panel.defaults.Append('wax rough cut (1/8)') + self.defaults["wax rough cut (1/8)"]\ + = "self.png_path_panel.diameter_rough.SetValue('3.175');\ + self.png_path_panel.number_rough.SetValue('-1');\ + self.png_path_panel.overlap_rough.SetValue('0.25');\ + self.png_path_panel.error_rough.SetValue('1.5');\ + self.png_path_panel.zstep_rough.SetValue('1');\ + self.png_path_panel.ibot_rough.SetValue('0');\ + self.png_path_panel.itop_rough.SetValue('1');\ + self.png_path_panel.zbot_rough.SetValue('-10');\ + self.png_path_panel.ztop_rough.SetValue('0');\ + self.g_panel.button.Hide();\ + self.g_panel.speed.SetValue('20');\ + self.path_type = '3D rough';\ + self.update_panels();" + self.control_panel.defaults.Append('wax finish cut (1/8)') + self.defaults["wax finish cut (1/8)"]\ + = "self.png_path_panel.diameter_finish.SetValue('3.175');\ + self.png_path_panel.clearance_diameter_finish.SetValue('3.175');\ + self.png_path_panel.overlap_finish.SetValue('0.5');\ + self.png_path_panel.error_finish.SetValue('1.5');\ + self.png_path_panel.xz_finish.SetValue(True);\ + self.png_path_panel.yz_finish.SetValue(True);\ + self.png_path_panel.ibot_finish.SetValue('0');\ + self.png_path_panel.itop_finish.SetValue('1');\ + self.png_path_panel.zbot_finish.SetValue('-10');\ + self.png_path_panel.ztop_finish.SetValue('0');\ + self.g_panel.button.Hide();\ + self.g_panel.speed.SetValue('20');\ + self.path_type = '3D finish';\ + self.update_panels();" + # + # set .cad .g defaults + # + def set_cad_g(self): + self.defaults = {} + self.control_panel.defaults.Append('mill traces (1/64)') + self.defaults["mill traces (1/64)"]\ + = "self.cad_png_panel.resolution.SetValue('50');\ + self.png_path_panel.diameter_plane.SetValue('0.4');\ + self.png_path_panel.number_plane.SetValue('4');\ + self.png_path_panel.z_plane.SetValue('-0.1');\ + self.g_panel.button.Hide();\ + self.g_panel.speed.SetValue('4');\ + self.path_type = '3D plane';\ + self.update_panels();" + self.control_panel.defaults.Append('mill traces (0.010)') + self.defaults["mill traces (0.010)"]\ + = "self.cad_png_panel.resolution.SetValue('50');\ + self.png_path_panel.diameter_plane.SetValue('0.254');\ + self.png_path_panel.number_plane.SetValue('1');\ + self.png_path_panel.z_plane.SetValue('-0.1');\ + self.g_panel.button.Hide();\ + self.g_panel.speed.SetValue('4');\ + self.path_type = '3D plane';\ + self.update_panels();" + self.control_panel.defaults.Append('cut out board (1/32)') + self.defaults["cut out board (1/32)"]\ + = "self.cad_png_panel.resolution.SetValue('50');\ + self.png_path_panel.diameter_rough.SetValue('0.79');\ + self.png_path_panel.number_rough.SetValue('1');\ + self.png_path_panel.itop_rough.SetValue('0.5');\ + self.png_path_panel.ibot_rough.SetValue('0.5');\ + self.png_path_panel.ztop_rough.SetValue('-0.6');\ + self.png_path_panel.zbot_rough.SetValue('-1.7');\ + self.png_path_panel.zstep_rough.SetValue('0.6');\ + self.g_panel.button.Hide();\ + self.g_panel.speed.SetValue('4');\ + self.path_type = '3D rough';\ + self.update_panels();" + self.control_panel.defaults.Append('wax rough cut (1/8)') + self.defaults["wax rough cut (1/8)"]\ + = "self.cad_png_panel.resolution.SetValue('25');\ + self.png_path_panel.diameter_rough.SetValue('3.175');\ + self.png_path_panel.number_rough.SetValue('-1');\ + self.png_path_panel.overlap_rough.SetValue('0.25');\ + self.png_path_panel.error_rough.SetValue('1.5');\ + self.png_path_panel.zstep_rough.SetValue('1');\ + self.png_path_panel.ibot_rough.SetValue('0');\ + self.png_path_panel.itop_rough.SetValue('1');\ + self.png_path_panel.zbot_rough.SetValue('');\ + self.png_path_panel.ztop_rough.SetValue('');\ + self.g_panel.button.Hide();\ + self.g_panel.speed.SetValue('4');\ + self.path_type = '3D rough';\ + self.update_panels();" + self.control_panel.defaults.Append('wax finish cut (1/8)') + self.defaults["wax finish cut (1/8)"]\ + = "self.cad_png_panel.resolution.SetValue('25');\ + self.png_path_panel.diameter_finish.SetValue('3.175');\ + self.png_path_panel.clearance_diameter_finish.SetValue('3.175');\ + self.png_path_panel.overlap_finish.SetValue('0.5');\ + self.png_path_panel.error_finish.SetValue('1.5');\ + self.png_path_panel.xz_finish.SetValue(True);\ + self.png_path_panel.yz_finish.SetValue(True);\ + self.png_path_panel.ibot_finish.SetValue('0');\ + self.png_path_panel.itop_finish.SetValue('1');\ + self.png_path_panel.zbot_finish.SetValue('');\ + self.png_path_panel.ztop_finish.SetValue('');\ + self.g_panel.button.Hide();\ + self.g_panel.speed.SetValue('4');\ + self.path_type = '3D finish';\ + self.update_panels();" + # + # set .math .g defaults + # + def set_math_g(self): + self.defaults = {} + self.control_panel.defaults.Append('wax rough cut (1/8)') + self.defaults["wax rough cut (1/8)"]\ + = "self.math_png_panel.resolution.SetValue('10');\ + self.png_path_panel.diameter_rough.SetValue('3.175');\ + self.png_path_panel.number_rough.SetValue('-1');\ + self.png_path_panel.overlap_rough.SetValue('0.25');\ + self.png_path_panel.error_rough.SetValue('1.5');\ + self.png_path_panel.zstep_rough.SetValue('1');\ + self.png_path_panel.ibot_rough.SetValue('0');\ + self.png_path_panel.itop_rough.SetValue('1');\ + self.png_path_panel.zbot_rough.SetValue('');\ + self.png_path_panel.ztop_rough.SetValue('');\ + self.g_panel.button.Hide();\ + self.g_panel.speed.SetValue('4');\ + self.units = 25.4;\ + self.path_type='3D rough';\ + self.update_panels();" + self.control_panel.defaults.Append('wax finish cut (1/8)') + self.defaults["wax finish cut (1/8)"]\ + = "self.math_png_panel.resolution.SetValue('10');\ + self.png_path_panel.diameter_finish.SetValue('3.175');\ + self.png_path_panel.clearance_diameter_finish.SetValue('3.175');\ + self.png_path_panel.overlap_finish.SetValue('0.5');\ + self.png_path_panel.error_finish.SetValue('1.5');\ + self.png_path_panel.xz_finish.SetValue(True);\ + self.png_path_panel.yz_finish.SetValue(True);\ + self.png_path_panel.ibot_finish.SetValue('0');\ + self.png_path_panel.itop_finish.SetValue('1');\ + self.png_path_panel.zbot_finish.SetValue('');\ + self.png_path_panel.ztop_finish.SetValue('');\ + self.g_panel.button.Hide();\ + self.g_panel.speed.SetValue('4');\ + self.units = 25.4;\ + self.path_type='3D finish';\ + self.update_panels();" + # + # set .stl .g defaults + # + def set_stl_g(self): + self.defaults = {} + self.control_panel.defaults.Append('inches, 1/8, wax, rough') + self.defaults["inches, 1/8, wax, rough"]\ + = "self.png_path_panel.diameter_rough.SetValue('3.175');\ + self.png_path_panel.number_rough.SetValue('-1');\ + self.png_path_panel.overlap_rough.SetValue('0.25');\ + self.png_path_panel.error_rough.SetValue('1.5');\ + self.png_path_panel.zstep_rough.SetValue('1');\ + self.png_path_panel.ibot_rough.SetValue('0');\ + self.png_path_panel.itop_rough.SetValue('1');\ + self.png_path_panel.zbot_rough.SetValue('');\ + self.png_path_panel.ztop_rough.SetValue('');\ + self.stl_png_panel.resolution.SetValue('25');\ + self.g_panel.button.Hide();\ + self.g_panel.speed.SetValue('20');\ + self.units = 25.4;\ + self.path_type = '3D rough';\ + self.update_panels();" + self.control_panel.defaults.Append('inches, 1/8, wax, finish') + self.defaults["inches, 1/8, wax, finish"]\ + = "self.png_path_panel.diameter_finish.SetValue('3.175');\ + self.png_path_panel.clearance_diameter_finish.SetValue('3.175');\ + self.png_path_panel.overlap_finish.SetValue('0.5');\ + self.png_path_panel.error_finish.SetValue('1.5');\ + self.png_path_panel.xz_finish.SetValue(True);\ + self.png_path_panel.yz_finish.SetValue(True);\ + self.png_path_panel.ibot_finish.SetValue('0');\ + self.png_path_panel.itop_finish.SetValue('1');\ + self.png_path_panel.zbot_finish.SetValue('');\ + self.png_path_panel.ztop_finish.SetValue('');\ + self.stl_png_panel.resolution.SetValue('25');\ + self.g_panel.button.Hide();\ + self.g_panel.speed.SetValue('20');\ + self.units = 25.4;\ + self.path_type='3D finish';\ + self.update_panels();" + self.control_panel.defaults.Append('mm, 1/8, wax, rough') + self.defaults["mm, 1/8, wax, rough"]\ + = "self.png_path_panel.diameter_rough.SetValue('3.175');\ + self.png_path_panel.number_rough.SetValue('-1');\ + self.png_path_panel.overlap_rough.SetValue('0.25');\ + self.png_path_panel.error_rough.SetValue('1.5');\ + self.png_path_panel.zstep_rough.SetValue('1');\ + self.png_path_panel.ibot_rough.SetValue('0');\ + self.png_path_panel.itop_rough.SetValue('1');\ + self.png_path_panel.zbot_rough.SetValue('');\ + self.png_path_panel.ztop_rough.SetValue('');\ + self.stl_png_panel.units.SetValue('1');\ + self.stl_png_panel.resolution.SetValue('25');\ + self.g_panel.button.Hide();\ + self.g_panel.speed.SetValue('20');\ + self.units = 1;\ + self.path_type='3D rough';\ + self.update_panels();" + self.control_panel.defaults.Append('mm, 1/8, wax, finish') + self.defaults["mm, 1/8, wax, finish"]\ + = "self.png_path_panel.diameter_finish.SetValue('3.175');\ + self.png_path_panel.clearance_diameter_finish.SetValue('3.175');\ + self.png_path_panel.overlap_finish.SetValue('0.5');\ + self.png_path_panel.error_finish.SetValue('1.5');\ + self.png_path_panel.xz_finish.SetValue(True);\ + self.png_path_panel.yz_finish.SetValue(True);\ + self.png_path_panel.ibot_finish.SetValue('0');\ + self.png_path_panel.itop_finish.SetValue('1');\ + self.png_path_panel.zbot_finish.SetValue('');\ + self.png_path_panel.ztop_finish.SetValue('');\ + self.stl_png_panel.units.SetValue('1');\ + self.stl_png_panel.resolution.SetValue('25');\ + self.g_panel.button.Hide();\ + self.g_panel.speed.SetValue('20');\ + self.units = 1;\ + self.path_type='3D finish';\ + self.update_panels();" + # + # set .svg .g defaults + # + def set_svg_g(self): + self.defaults = {} + self.control_panel.defaults.Append('wax') + self.defaults["wax"]\ + = "self.g_panel.speed.SetValue('20');" + # + # set .png .eps defaults + # + def set_png_eps(self): + self.defaults = {} + self.control_panel.defaults.Append('outline') + self.defaults["outline"]\ + = "self.png_path_panel.error_plane.SetValue('1');\ + self.png_path_panel.diameter_plane.SetValue('0');\ + self.png_path_panel.set_path_type('3D plane');\ + self.png_path_panel.number_plane.SetValue('1');" + self.control_panel.defaults.Append('mill traces (1/64)') + self.defaults["mill traces (1/64)"]\ + = "self.png_path_panel.error_plane.SetValue('1');\ + self.png_path_panel.diameter_plane.SetValue('0.4');\ + self.png_path_panel.set_path_type('3D plane');\ + self.png_path_panel.number_plane.SetValue('4');" + self.control_panel.defaults.Append('cut out board (1/32)') + self.defaults["cut out board (1/32)"]\ + = "self.png_path_panel.error_plane.SetValue('1');\ + self.png_path_panel.diameter_plane.SetValue('0.8');\ + self.png_path_panel.set_path_type('3D plane');\ + self.png_path_panel.number_plane.SetValue('1');" + # + # set .cad .eps defaults + # + def set_cad_eps(self): + self.defaults = {} + self.control_panel.defaults.Append('outline') + self.defaults["outline"]\ + = "self.cad_png_panel.resolution.SetValue('50');\ + self.png_path_panel.error_plane.SetValue('1');\ + self.png_path_panel.diameter_plane.SetValue('0');\ + self.png_path_panel.number_plane.SetValue('1');\ + self.path_type = '3D plane';\ + self.update_panels();" + # + # set .math .eps defaults + # + def set_math_eps(self): + self.defaults = {} + self.control_panel.defaults.Append('outline') + self.defaults["outline"]\ + = "self.png_panel.resolution.SetValue('50');\ + self.png_path_panel.error.SetValue('1');\ + self.png_path_panel.diameter.SetValue('0');\ + self.png_path_panel.number.SetValue('1');" + self.control_panel.defaults.Append('mill traces (1/64)') + self.defaults["mill traces (1/64)"]\ + = "self.png_panel.resolution.SetValue('50');\ + self.png_path_panel.error.SetValue('1');\ + self.png_path_panel.diameter.SetValue('0.4');\ + self.png_path_panel.number.SetValue('4');" + self.control_panel.defaults.Append('cut out board (1/32)') + self.defaults["cut out board (1/32)"]\ + = "self.png_panel.resolution.SetValue('50');\ + self.png_path_panel.error.SetValue('1');\ + self.png_path_panel.diameter.SetValue('0.8');\ + self.png_path_panel.number.SetValue('1');" + # + # set .svg .eps defaults + # + def set_svg_eps(self): + self.defaults = {} + self.control_panel.defaults.Append('outline') + self.defaults["outline"]\ + = "self.png_panel.resolution.SetValue('50');\ + self.png_path_panel.error.SetValue('1');\ + self.png_path_panel.diameter.SetValue('0');\ + self.png_path_panel.number.SetValue('1');" + self.control_panel.defaults.Append('mill traces (1/64)') + self.defaults["mill traces (1/64)"]\ + = "self.png_panel.resolution.SetValue('50');\ + self.png_path_panel.error.SetValue('1');\ + self.png_path_panel.diameter.SetValue('0.4');\ + self.png_path_panel.number.SetValue('4');" + self.control_panel.defaults.Append('cut out board (1/32)') + self.defaults["cut out board (1/32)"]\ + = "self.png_panel.resolution.SetValue('50');\ + self.png_path_panel.error.SetValue('1');\ + self.png_path_panel.diameter.SetValue('0.8');\ + self.png_path_panel.number.SetValue('1');" + # + # set .png .camm defaults + # + def set_png_camm(self): + self.defaults = {} + self.control_panel.defaults.Append('vinyl') + self.defaults["vinyl"]\ + = "self.png_path_panel.error_2D.SetValue('1.5');\ + self.png_path_panel.diameter_2D.SetValue('0.25');\ + self.png_path_panel.number_2D.SetValue('1');\ + self.camm_panel.force.SetValue('45');\ + self.camm_panel.velocity.SetValue('5');\ + self.path_type = '2D';\ + self.update_panels();" + self.control_panel.defaults.Append('copper') + self.defaults["copper"]\ + = "self.png_path_panel.error_2D.SetValue('1.5');\ + self.png_path_panel.diameter_2D.SetValue('0.25');\ + self.png_path_panel.number_2D.SetValue('1');\ + self.camm_panel.force.SetValue('55');\ + self.camm_panel.velocity.SetValue('2.5');\ + self.path_type = '2D';\ + self.update_panels();" + self.control_panel.defaults.Append('epoxy') + self.defaults["epoxy"]\ + = "self.png_path_panel.error_2D.SetValue('1.5');\ + self.png_path_panel.diameter_2D.SetValue('0.25');\ + self.png_path_panel.number_2D.SetValue('1');\ + self.camm_panel.force.SetValue('90');\ + self.camm_panel.velocity.SetValue('2.5');\ + self.path_type = '2D';\ + self.update_panels();" + # + # set .cad .camm defaults + # + def set_cad_camm(self): + self.defaults = {} + self.control_panel.defaults.Append('vinyl') + self.defaults["vinyl"]\ + = "self.png_path_panel.error_2D.SetValue('1.5');\ + self.png_path_panel.diameter_2D.SetValue('.25');\ + self.png_path_panel.number_2D.SetValue('1');\ + self.png_panel.resolution.SetValue('10');\ + self.camm_panel.force.SetValue('45');\ + self.camm_panel.velocity.SetValue('5');\ + self.path_type = '2D';\ + self.update_panels();" + self.control_panel.defaults.Append('copper') + self.defaults["copper"]\ + = "self.png_path_panel.error_2D.SetValue('1.5');\ + self.png_path_panel.diameter_2D.SetValue('.25');\ + self.png_path_panel.number_2D.SetValue('1');\ + self.png_panel.resolution.SetValue('10');\ + self.camm_panel.force.SetValue('55');\ + self.camm_panel.velocity.SetValue('2.5');\ + self.path_type = '2D';\ + self.update_panels();" + self.control_panel.defaults.Append('epoxy') + self.defaults["epoxy"]\ + = "self.png_path_panel.error_2D.SetValue('1.5');\ + self.png_path_panel.diameter_2D.SetValue('.25');\ + self.png_path_panel.number_2D.SetValue('1');\ + self.png_panel.resolution.SetValue('10');\ + self.camm_panel.force.SetValue('90');\ + self.camm_panel.velocity.SetValue('2.5');\ + self.path_type = '2D';\ + self.update_panels();" + # + # set .math .camm defaults + # + def set_math_camm(self): + self.defaults = {} + self.control_panel.defaults.Append('vinyl') + self.defaults["vinyl"]\ + = "self.png_path_panel.error_2D.SetValue('1.5');\ + self.png_path_panel.diameter_2D.SetValue('.25');\ + self.png_path_panel.number_2D.SetValue('1');\ + self.png_panel.resolution.SetValue('10');\ + self.camm_panel.force.SetValue('45');\ + self.camm_panel.velocity.SetValue('5');\ + self.path_type = '2D';\ + self.update_panels();" + self.control_panel.defaults.Append('copper') + self.defaults["copper"]\ + = "self.png_path_panel.error_2D.SetValue('1.5');\ + self.png_path_panel.diameter_2D.SetValue('.25');\ + self.png_path_panel.number_2D.SetValue('1');\ + self.png_panel.resolution.SetValue('10');\ + self.camm_panel.force.SetValue('55');\ + self.camm_panel.velocity.SetValue('2.5');\ + self.path_type = '2D';\ + self.update_panels();" + self.control_panel.defaults.Append('epoxy') + self.defaults["epoxy"]\ + = "self.png_path_panel.error_2D.SetValue('1.5');\ + self.png_path_panel.diameter_2D.SetValue('.25');\ + self.png_path_panel.number_2D.SetValue('1');\ + self.png_panel.resolution.SetValue('10');\ + self.camm_panel.force.SetValue('90');\ + self.camm_panel.velocity.SetValue('2.5');\ + self.path_type = '2D';\ + self.update_panels();" + # + # set .svg .camm defaults + # + def set_svg_camm(self): + self.defaults = {} + self.control_panel.defaults.Append('vinyl') + self.defaults["vinyl"]\ + = "self.camm_panel.force.SetValue('45');\ + self.camm_panel.velocity.SetValue('5');\ + self.path_type = '2D';\ + self.update_panels();" + self.control_panel.defaults.Append('copper') + self.defaults["copper"]\ + = "self.camm_panel.force.SetValue('55');\ + self.camm_panel.velocity.SetValue('2.5');\ + self.path_type = '2D';\ + self.update_panels();" + self.control_panel.defaults.Append('epoxy') + self.defaults["epoxy"]\ + = "self.camm_panel.force.SetValue('90');\ + self.camm_panel.velocity.SetValue('2.5');\ + self.path_type = '2D';\ + self.update_panels();" + # + # set .png .ord defaults + # + def set_png_ord(self): + self.defaults = {} + # + # set .cad .ord defaults + # + def set_cad_ord(self): + self.defaults = {} + # + # set .math .ord defaults + # + def set_math_ord(self): + self.defaults = {} + # + # set .svg .ord defaults + # + def set_svg_ord(self): + self.defaults = {} + # + # set .png .oms defaults + # + def set_png_oms(self): + self.defaults = {} + # + # set .svg .oms defaults + # + def set_svg_oms(self): + self.defaults = {} + # + # init + # + def __init__(self,title,argv): + self.tmp = "fab_mod_" # default temporary file prefix + # + # set default panel size from screen size + # + (screen_x,screen_y) = wx.DisplaySize() + self.size = screen_y/2.1 + # + # frame + # + wx.Frame.__init__(self,None,title=title) + self.sizer = wx.GridBagSizer(10,10) + self.SetSizer(self.sizer) + # + # resize event + # + self.Bind(wx.EVT_SIZE,self.resize_handler) + # + # arguments + # + self.rootname = "" + self.filename = "" + self.basename = "" + if (len(argv) > 1): + if (argv[1] != '""'): + self.filename = sys.argv[1] + self.basename = os.path.basename(self.filename) + if (len(argv) > 2): + self.size = int(sys.argv[2]) + # + # defaults handler + # + def defaults_handler(self,event): + value = self.control_panel.defaults.GetValue() + string = self.defaults[value] + exec(string) + # + # resize handler + # + def resize_handler(self,event): + (sizex,sizey) = self.GetSize() + self.update_sizes(sizex,sizey) + # + # update children size + # + def update_sizes(self,sizex,sizey): + for child in self.GetChildren(): + if hasattr(child,"update_size"): + child.update_size(sizex,sizey) + self.Layout() + self.Fit() + # + # update child panels + # + def update_panels(self): + for child in self.GetChildren(): + if hasattr(child,"update_panel"): + child.update_panel() + self.Layout() + self.Fit() diff --git a/src/py/math_string.py b/src/py/math_string.py new file mode 100644 index 0000000..f68f49e --- /dev/null +++ b/src/py/math_string.py @@ -0,0 +1,57 @@ +class MString: + def __init__(self, string): + if string.__class__ is MString: + self.s = string.s + else: + self.s = string + + def __repr__(self): + return 'MString(%s)' % self.s + + def __str__(self): + return self.s + + def __eq__(self, other): + if other.__class__ is not MString: + raise TypeError('Expected MString, got object of %s' + % type(other)) + return self.s == other.s + + def __neq__(self, other): + return not self.__eq__(other) + + def __len__(self): + return len(self.s) + + def __hash__(self): + return self.s.__hash__() + + def __or__(self, other): + return MString("(%s) || (%s)" % (self.s, other)) + + def __ror__(self, other): + return MString("(%s) || (%s)" % (other, self.s)) + + def __and__(self, other): + return MString("(%s) && (%s)" % (self.s, other)) + + def __rand__(self, other): + return MString("(%s) && (%s)" % (other, self.s)) + + def __invert__(self): + return MString("!(%s)" % (self.s)) + + def __add__(self, other): + return self | other + + def __radd__(self, other): + return other | self + + def __sub__(self, other): + return self & ~MString(other) + + def __rsub__(self, other): + return other & ~MString(self) + + def replace(self, target, substitution): + return MString(self.s.replace(target, substitution)) \ No newline at end of file diff --git a/src/py/panel_cad.py b/src/py/panel_cad.py new file mode 100644 index 0000000..0414307 --- /dev/null +++ b/src/py/panel_cad.py @@ -0,0 +1,104 @@ +# +# panel_cad.py +# read and edit .cad +# +# Neil Gershenfeld +# CBA MIT 2/18/11 +# +# (c) Massachusetts Institute of Technology 2011 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,string,os,sys +# +# panel class +# +class cad_panel(wx.Panel): + def __init__(self,parent): + self.parent = parent + # + # load file + # + def load_file(event): + if (self.parent.basename == ""): + return + pos = string.find(self.parent.basename,".cad") + if (pos == -1): + pos = string.find(self.parent.basename,".CAD") + if (pos == -1): + print 'cad_panel: oops -- must be .cad' + sys.exit() + self.parent.rootname = self.parent.basename[:pos] + cad_file = open(self.parent.filename,'r') + cad_string = cad_file.read() + cad_file.close() + self.text.SetValue(cad_string) + # + # select file + # + def select_file(event): + dialog = wx.FileDialog(self, "Choose a file", os.getcwd(), "", "*.cad", wx.OPEN) + if (dialog.ShowModal() == wx.ID_OK): + self.parent.filename = dialog.GetPath() + self.parent.basename = os.path.basename(self.parent.filename) + pos = string.find(self.parent.basename,".cad") + if (pos == -1): + print 'cad_panel: oops -- must be .cad' + sys.exit() + else: + self.parent.rootname = self.parent.basename[:pos] + cad_file = open(self.parent.filename,'r') + cad_string = cad_file.read() + cad_file.close() + self.text.SetValue(cad_string) + # + # save file + # + def save_file(event): + result = wx.SaveFileSelector('.cad','.cad',self.parent.filename) + if (result != ''): + cad_file = open(result,'w') + cad_file.write(self.text.GetValue()) + cad_file.close() + # + # panel + # + wx.Panel.__init__(self,parent) + self.sizer = wx.GridBagSizer(10,10) + self.SetSizer(self.sizer) + # + # label + # + label = wx.StaticText(self,label='from: cad') + bold_font = wx.Font(10,wx.DEFAULT,wx.NORMAL,wx.BOLD) + label.SetFont(bold_font) + self.sizer.Add(label,(0,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + # text + # + self.text = wx.TextCtrl(self,-1,'',size=(self.parent.size,self.parent.size),style=wx.TE_MULTILINE) + self.sizer.Add(self.text,(1,0),flag=(wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL)) + # + # controls + # + load = wx.Button(self,label='load .cad') + load.Bind(wx.EVT_BUTTON,select_file) + self.sizer.Add(load,(2,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + rload = wx.Button(self,label='reload .cad') + rload.Bind(wx.EVT_BUTTON,load_file) + self.sizer.Add(rload,(3,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + save = wx.Button(self,label='save .cad') + save.Bind(wx.EVT_BUTTON,save_file) + self.sizer.Add(save,(4,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + # file + # + load_file(0) + # + # fit + # + self.Fit() diff --git a/src/py/panel_cad_png.py b/src/py/panel_cad_png.py new file mode 100644 index 0000000..11c16ab --- /dev/null +++ b/src/py/panel_cad_png.py @@ -0,0 +1,149 @@ +# +# panel_cad_png.py +# make .png from .cad +# +# Neil Gershenfeld +# CBA MIT 4/18/11 +# +# (c) Massachusetts Institute of Technology 2011 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,os,string +# +# panel class +# +class cad_png_panel(wx.Panel): + def __init__(self,parent): + self.parent = parent + # + # get png info + # + def png_info(name): + temp_name = self.parent.tmp+'png_info' + command = 'png_size '+'\"'+name+'\"'+' > '+'\"'+temp_name+'\"' + os.system(command) + output_file = open(temp_name,'r') + output = output_file.read() + output_file.close() + command = 'rm '+'\"'+temp_name+'\"' + os.system(command) + return output + # + # make png + # + def make_png(event): + if (self.parent.rootname == ''): + return + # + # generate .math file + # + tmp_cad_file = self.parent.tmp+self.parent.rootname+'.cad' + cad_file = open(tmp_cad_file,'w') + cad_file.write(self.parent.cad_panel.text.GetValue()) + cad_file.close() + cad_info_name = self.parent.tmp+'cad_info' + math_file = self.parent.tmp+self.parent.rootname+'.math' + command = 'cad_math '+'\"'+tmp_cad_file+'\"'+' '+'\"'+math_file+'\"'+' > '+'\"'+cad_info_name+'\"' + print command + os.system(command) + cad_info_file = open(cad_info_name,'r') + cad_info = cad_info_file.read() + cad_info_file.close() + start = string.find(cad_info,"write") + cad_info = cad_info[start:] + start = 7+string.find(cad_info,'units:') + end = string.find(cad_info,'\n',start) + units = float(cad_info[start:end]) + start = 4+string.find(cad_info,'dz:') + end = string.find(cad_info,'\n',start) + dz = float(cad_info[start:end]) + start = 6+string.find(cad_info,'zmin:') + end = string.find(cad_info,'\n',start) + zmin = float(cad_info[start:end]) + start = 8+string.find(cad_info,'layers:') + resolution = self.parent.cad_png_panel.resolution.GetValue() + if (start > 7): + end = string.find(cad_info,'\n',start) + number = int(cad_info[start:end]) + elif (dz > 0): + number = int(dz*units*float(resolution)) + else: + number = 1 + parent.zmin = zmin + parent.zmax = zmin+dz + parent.units = units + # + # generate .png file + # + self.parent.png_file = self.parent.tmp+self.parent.rootname+'.png' + command = 'math_png '+'\"'+math_file+'\"'+' '+'\"'+self.parent.png_file+'\"'+' '+resolution+' '+str(number) + print command + os.system(command) + # + # update image + # + info = png_info(self.parent.png_file) + self.info.SetLabel(cad_info+info) + png_image = wx.Image(self.parent.png_file) + (nx,ny) = png_image.GetSize() + ratio = float(ny)/float(nx) + if (ratio > 1): + self.parent.ysize = self.parent.size + self.parent.xsize = self.parent.size/ratio + else: + self.parent.ysize = self.parent.size*ratio + self.parent.xsize = self.parent.size + wx.Image.Rescale(png_image,self.parent.xsize,self.parent.ysize) + png_bitmap = png_image.ConvertToBitmap() + self.bitmap.SetBitmap(png_bitmap) + self.bitmap.Show() + self.parent.Layout() + self.parent.Fit() + # + # update panels + # + parent.update_panels() + # + # panel + # + wx.Panel.__init__(self,parent) + self.sizer = wx.GridBagSizer(10,10) + self.SetSizer(self.sizer) + # + # label + # + label = wx.StaticText(self,label='to: png') + bold_font = wx.Font(10,wx.DEFAULT,wx.NORMAL,wx.BOLD) + label.SetFont(bold_font) + self.sizer.Add(label,(0,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + # image + # + image = wx.ArtProvider.GetBitmap(wx.ART_QUESTION, wx.ART_OTHER, (self.parent.size,self.parent.size)) + self.bitmap = wx.StaticBitmap(self,-1,image) + self.sizer.Add(self.bitmap,(1,0),flag=(wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL)) + self.bitmap.Hide() + # + # controls + # + make = wx.Button(self,label='make .png') + make.Bind(wx.EVT_BUTTON,make_png) + self.sizer.Add(make,(2,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + res_panel = wx.Panel(self) + res_panel_sizer = wx.GridBagSizer(10,10) + res_panel.SetSizer(res_panel_sizer) + res_panel_sizer.Add(wx.StaticText(res_panel,label='resolution (pixels/mm):'),(0,0),flag=wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) + self.resolution = wx.TextCtrl(res_panel,-1,'1') + res_panel_sizer.Add(self.resolution,(0,1),flag=wx.ALIGN_LEFT) + self.sizer.Add(res_panel,(3,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + self.info = wx.StaticText(self,label="") + self.sizer.Add(self.info,(5,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + # fit + # + self.Fit() diff --git a/src/py/panel_cad_stl.py b/src/py/panel_cad_stl.py new file mode 100644 index 0000000..0d56622 --- /dev/null +++ b/src/py/panel_cad_stl.py @@ -0,0 +1,124 @@ +# +# panel_cad_stl.py +# make .stl from .cad +# +# Neil Gershenfeld +# CBA MIT 3/22/11 +# +# (c) Massachusetts Institute of Technology 2011 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,os,string +# +# panel class +# +class cad_stl_panel(wx.Panel): + def __init__(self,parent): + self.parent = parent + # + # get stl info + # + def stl_info(name): + temp_name = self.parent.tmp+'stl_info' + command = 'stl_info '+'\"'+name+'\"'+' > '+'\"'+temp_name+'\"' + os.system(command) + output_file = open(temp_name,'r') + output = output_file.read() + output_file.close() + command = 'rm '+'\"'+temp_name+'\"' + os.system(command) + return output + # + # make stl + # + def make_stl(event): + if (self.parent.rootname == ''): + return + tmp_cad_file = self.parent.tmp+self.parent.rootname+'.cad' + cad_file = open(tmp_cad_file,'w') + cad_file.write(self.parent.cad_panel.text.GetValue()) + cad_file.close() + cad_info_name = self.parent.tmp+'cad_info' + math_file = self.parent.tmp+self.parent.rootname+'.math' + command = 'cad_math '+'\"'+tmp_cad_file+'\"'+' '+'\"'+math_file+'\"'+' > '+'\"'+cad_info_name+'\"' + print command + os.system(command) + cad_info_file = open(cad_info_name,'r') + cad_info = cad_info_file.read() + cad_info_file.close() + start = string.find(cad_info,"write") + cad_info = cad_info[start:] + start = 7+string.find(cad_info,'units:') + end = string.find(cad_info,'\n',start) + units = float(cad_info[start:end]) + start = 4+string.find(cad_info,'dz:') + end = string.find(cad_info,'\n',start) + dz = float(cad_info[start:end]) + start = 6+string.find(cad_info,'zmin:') + end = string.find(cad_info,'\n',start) + zmin = float(cad_info[start:end]) + parent.zmin = zmin + parent.zmax = zmin+dz + parent.units = units + self.parent.stl_file = self.parent.tmp+self.parent.rootname+'.stl' + resolution = self.parent.stl_panel.resolution.GetValue() + command = 'math_stl '+'\"'+math_file+'\"'+' '+'\"'+self.parent.stl_file+'\"'+' '+resolution + print command + os.system(command) + info = stl_info(self.parent.stl_file) + self.info.SetLabel(cad_info+info) + self.button.Show() + self.parent.Layout() + self.parent.Fit() + # + # send + # + def fab_send(event): + command = 'fab_send '+'\"'+self.parent.stl_file+'\"' + print command + os.system(command) + # + # panel + # + wx.Panel.__init__(self,parent) + self.sizer = wx.GridBagSizer(10,10) + self.SetSizer(self.sizer) + # + # label + # + label = wx.StaticText(self,label='to: stl') + bold_font = wx.Font(10,wx.DEFAULT,wx.NORMAL,wx.BOLD) + label.SetFont(bold_font) + self.sizer.Add(label,(0,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + # send + # + self.button = wx.Button(self,label='send it!') + self.button.Bind(wx.EVT_BUTTON,fab_send) + self.button.SetFont(bold_font) + self.sizer.Add(self.button,(1,0),flag=(wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL)) + self.button.Hide() + # + # controls + # + make = wx.Button(self,label='make .stl') + make.Bind(wx.EVT_BUTTON,make_stl) + self.sizer.Add(make,(2,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + res_panel = wx.Panel(self) + res_panel_sizer = wx.GridBagSizer(10,10) + res_panel.SetSizer(res_panel_sizer) + res_panel_sizer.Add(wx.StaticText(res_panel,label='resolution (pixels/mm):'),(0,0),flag=wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) + self.resolution = wx.TextCtrl(res_panel,-1,'1') + res_panel_sizer.Add(self.resolution,(0,1),flag=wx.ALIGN_LEFT) + self.sizer.Add(res_panel,(3,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + self.info = wx.StaticText(self,label="") + self.sizer.Add(self.info,(5,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + # fit + # + self.Fit() diff --git a/src/py/panel_control.py b/src/py/panel_control.py new file mode 100644 index 0000000..b1c8bea --- /dev/null +++ b/src/py/panel_control.py @@ -0,0 +1,68 @@ +# +# panel_control.py +# control panel +# +# Neil Gershenfeld +# CBA MIT 3/6/11 +# +# (c) Massachusetts Institute of Technology 2011 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,string,os,sys +# +# panel class +# +class control_panel(wx.Panel): + def __init__(self,parent): + self.parent = parent + wx.Panel.__init__(self,parent) + self.sizer = wx.GridBagSizer(10,10) + self.SetSizer(self.sizer) + # + # quit + # + def quits(event): + command = 'ls '+'\"'+self.parent.tmp+'\"'+'*; rm '+'\"'+self.parent.tmp+'\"'+'*' + print command + os.system(command) + sys.exit() + # + # controls + # + self.defaults = wx.ComboBox(self,value='defaults',style=wx.CB_READONLY) + self.defaults.Bind(wx.EVT_COMBOBOX,self.parent.defaults_handler) + self.sizer.Add(self.defaults,(0,0)) + # + d = 4 + w = 1 + l = 6*d+2*w + def logo_paint(event): + dc = wx.PaintDC(self.logo_panel) + dc.SetBrush(wx.Brush('white')) + dc.SetPen(wx.Pen('white', 0)) + dc.DrawRectangleRect((0, 0, l, l)) + dc.SetBrush(wx.Brush('red')) + dc.DrawCircle(d,d,d) + dc.DrawCircle(3*d+w,3*d+w,d) + dc.SetBrush(wx.Brush('blue')) + dc.DrawRectangleRect((2*d+w,0,2*d,2*d)) + dc.DrawRectangleRect((4*d+2*w,0,2*d,2*d)) + dc.DrawRectangleRect((0,2*d+w,2*d,2*d)) + dc.DrawRectangleRect((4*d+2*w,2*d+w,2*d,2*d)) + dc.DrawRectangleRect((0,4*d+2*w,2*d,2*d)) + dc.DrawRectangleRect((2*d+w,4*d+2*w,2*d,2*d)) + dc.DrawRectangleRect((4*d+2*w,4*d+2*w,2*d,2*d)) + self.logo_panel = wx.Panel(self,size=(l,l)) + self.logo_panel.Bind(wx.EVT_PAINT,logo_paint) + self.sizer.Add(self.logo_panel,(0,1)) + # + quit = wx.Button(self,label='quit') + quit.Bind(wx.EVT_BUTTON,quits) + self.sizer.Add(quit,(0,2)) + # + # fit + # + self.Fit() diff --git a/src/py/panel_gif.py b/src/py/panel_gif.py new file mode 100644 index 0000000..b4f5e99 --- /dev/null +++ b/src/py/panel_gif.py @@ -0,0 +1,153 @@ +# +# panel_gif.py +# read .gif +# +# Neil Gershenfeld +# CBA MIT 3/25/14 +# +# (c) Massachusetts Institute of Technology 2011 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,string,os,sys +from panel_path import path_panel +# +# panel class +# +class gif_panel(wx.Panel): + def __init__(self,parent): + self.parent = parent + self.parent.stl_file = '' + # + # get stl info + # + def stl_info(name): + # + # construct command + # + temp_name = self.parent.tmp+'stl_info' + command = 'stl_info '+'\"'+name+'\"'+' > '+'\"'+temp_name+'\"' + os.system(command) + output_file = open(temp_name,'r') + output = output_file.read() + output_file.close() + print output + if (string.find(output,'must be binary') != -1): + sys.exit(-1) + command = 'rm '+'\"'+temp_name+'\"' + os.system(command) + # + # read limits + # + start = 6+string.find(output,'xmax:') + space = string.find(output,' ',start) + end = string.find(output,'ymin',space)-4 + self.parent.xmin = float(output[start:space]) + self.parent.xmax = float(output[1+space:end]) + start = 6+string.find(output,'ymax:') + space = string.find(output,' ',start) + end = string.find(output,'zmin',space)-4 + self.parent.ymin = float(output[start:space]) + self.parent.ymax = float(output[1+space:end]) + start = 6+string.find(output,'zmax:') + space = string.find(output,' ',start) + self.parent.zmin = float(output[start:space]) + self.parent.zmax = float(output[space+1:-1]) + # + # move origin to top corner + # + # + self.parent.xmax = self.parent.xmax - self.parent.xmin + self.parent.xmin = 0 + self.parent.ymax = self.parent.ymax - self.parent.ymin + self.parent.ymin = 0 + self.parent.zmin = self.parent.zmin - self.parent.zmax + self.parent.zmax = 0 + # + return output + # + # load file + # + def load_file(event): + # + # get file name + # + if (self.parent.basename == ""): + return + pos = string.find(self.parent.basename,".stl") + if (pos == -1): + pos = string.find(self.parent.basename,".STL") + if (pos == -1): + print 'stl_panel: oops -- must be .stl' + sys.exit() + self.parent.rootname = self.parent.basename[:pos] + self.parent.stl_file = self.parent.filename + # + # get file info + # + info = stl_info(self.parent.filename) + self.info.SetLabel(info) + temp_name = self.parent.tmp+'stl.path' + # + # draw + # + if (self.path_viewer.view_type != "none"): + units = 1.0 + resolution = 100 + command = 'stl_path '+'\"'+self.parent.stl_file+'\"'+' '+'\"'+temp_name+'\"'+' '+str(units)+' '+str(resolution) + print command + ret = os.system(command) + if (ret == 0): + self.path_viewer.draw(temp_name) + # + # fit + # + self.parent.Layout() + self.parent.Fit() + # + # select file + # + def select_file(event): + dialog = wx.FileDialog(self, "Choose a file", os.getcwd(), "", "*.stl;*.STL", wx.OPEN) + if (dialog.ShowModal() == wx.ID_OK): + self.parent.filename = dialog.GetPath() + self.parent.basename = os.path.basename(self.parent.filename) + load_file(0) + # + # panel + # + wx.Panel.__init__(self,parent) + self.sizer = wx.GridBagSizer(10,10) + self.SetSizer(self.sizer) + # + # label + # + label = wx.StaticText(self,label='from: gif') + bold_font = wx.Font(10,wx.DEFAULT,wx.NORMAL,wx.BOLD) + label.SetFont(bold_font) + self.sizer.Add(label,(0,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + # image + # + self.path_viewer = path_panel(self) + self.sizer.Add(self.path_viewer,(1,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + self.path_viewer.view_type = "segments" + # + # controls + # + load = wx.Button(self,label='load .stl') + load.Bind(wx.EVT_BUTTON,select_file) + self.sizer.Add(load,(2,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + self.info = wx.StaticText(self,label="") + self.sizer.Add(self.info,(3,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + # file + # + load_file(0) + # + # fit + # + self.Fit() diff --git a/src/py/panel_gif_stl.py b/src/py/panel_gif_stl.py new file mode 100644 index 0000000..a1ae387 --- /dev/null +++ b/src/py/panel_gif_stl.py @@ -0,0 +1,137 @@ +# +# panel_gif_stl.py +# make .stl from .gif +# +# Neil Gershenfeld +# CBA MIT 3/25/14 +# +# (c) Massachusetts Institute of Technology 2012 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,os,string +# +# panel class +# +class gif_stl_panel(wx.Panel): + def __init__(self,parent): + self.parent = parent + # + # get stl info + # + def stl_info(name): + temp_name = self.parent.tmp+'stl_info' + command = 'stl_info '+'\"'+name+'\"'+' > '+'\"'+temp_name+'\"' + os.system(command) + output_file = open(temp_name,'r') + output = output_file.read() + output_file.close() + command = 'rm '+'\"'+temp_name+'\"' + os.system(command) + return output + # + # make stl + # + def make_stl(event): + if (self.parent.rootname == ''): + return + + math_text = self.parent.math_panel.text.GetValue() + tmp_math_file = self.parent.tmp+self.parent.rootname+'.math' + math_file = open(tmp_math_file,'w') + math_file.write(math_text) + math_file.close() + start = string.find(math_text,"dx dy dz:") + if (start == -1): + start = string.find(math_text,"dx dy:") + if (start == -1): + print "panel_math_png: oops, can't find bounding box" + return + dz = 0 + else: + end = string.find(math_text,"\n",start) + array = [float(s) for s in math_text[(start+10):end].split()] + dz = array[2] + start = string.find(math_text,"xmin ymin zmin:") + if (start == -1): + start = string.find(math_text,"xmin ymin:") + if (start == -1): + print "panel_math_png: oops, can't find origin" + return + zmin = 0 + else: + end = string.find(math_text,"\n",start) + array = [float(s) for s in math_text[(start+15):end].split()] + zmin = array[2] + start = string.find(math_text,"mm per unit:") + if (start == -1): + print "panel_math_png: oops, can't find units" + return + end = string.find(math_text,"\n",start) + units = float(math_text[(start+12):end]) + print units + parent.zmin = zmin + parent.zmax = zmin+dz + parent.units = units + + self.parent.stl_file = self.parent.tmp+self.parent.rootname+'.stl' + resolution = self.parent.stl_panel.resolution.GetValue() + command = 'math_stl '+'\"'+tmp_math_file+'\"'+' '+'\"'+self.parent.stl_file+'\"'+' '+resolution + print command + os.system(command) + info = stl_info(self.parent.stl_file) + self.info.SetLabel(info) + self.button.Show() + self.parent.Layout() + self.parent.Fit() + # + # send + # + def fab_send(event): + command = 'fab_send '+'\"'+self.parent.stl_file+'\"' + print command + os.system(command) + # + # panel + # + wx.Panel.__init__(self,parent) + self.sizer = wx.GridBagSizer(10,10) + self.SetSizer(self.sizer) + # + # label + # + label = wx.StaticText(self,label='to: stl') + bold_font = wx.Font(10,wx.DEFAULT,wx.NORMAL,wx.BOLD) + label.SetFont(bold_font) + self.sizer.Add(label,(0,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + # send + # + self.button = wx.Button(self,label='send it!') + self.button.Bind(wx.EVT_BUTTON,fab_send) + self.button.SetFont(bold_font) + self.sizer.Add(self.button,(1,0),flag=(wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL)) + self.button.Hide() + # + # controls + # + make = wx.Button(self,label='make .stl') + make.Bind(wx.EVT_BUTTON,make_stl) + self.sizer.Add(make,(2,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + res_panel = wx.Panel(self) + res_panel_sizer = wx.GridBagSizer(10,10) + res_panel.SetSizer(res_panel_sizer) + res_panel_sizer.Add(wx.StaticText(res_panel,label='resolution (pixels/mm):'),(0,0),flag=wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) + self.resolution = wx.TextCtrl(res_panel,-1,'1') + res_panel_sizer.Add(self.resolution,(0,1),flag=wx.ALIGN_LEFT) + self.sizer.Add(res_panel,(3,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + self.info = wx.StaticText(self,label="") + self.sizer.Add(self.info,(5,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + # fit + # + self.Fit() diff --git a/src/py/panel_math.py b/src/py/panel_math.py new file mode 100644 index 0000000..4ea5d12 --- /dev/null +++ b/src/py/panel_math.py @@ -0,0 +1,104 @@ +# +# panel_math.py +# read and edit .math +# +# Neil Gershenfeld +# CBA MIT 4/25/12 +# +# (c) Massachusetts Institute of Technology 2012 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,string,os,sys +# +# panel class +# +class math_panel(wx.Panel): + def __init__(self,parent): + self.parent = parent + # + # load file + # + def load_file(event): + if (self.parent.basename == ""): + return + pos = string.find(self.parent.basename,".math") + if (pos == -1): + pos = string.find(self.parent.basename,".MATH") + if (pos == -1): + print 'math_panel: oops -- must be .math' + sys.exit() + self.parent.rootname = self.parent.basename[:pos] + math_file = open(self.parent.filename,'r') + math_string = math_file.read() + math_file.close() + self.text.SetValue(math_string) + # + # select file + # + def select_file(event): + dialog = wx.FileDialog(self, "Choose a file", os.getcwd(), "", "*.math", wx.OPEN) + if (dialog.ShowModal() == wx.ID_OK): + self.parent.filename = dialog.GetPath() + self.parent.basename = os.path.basename(self.parent.filename) + pos = string.find(self.parent.basename,".math") + if (pos == -1): + print 'math_panel: oops -- must be .math' + sys.exit() + else: + self.parent.rootname = self.parent.basename[:pos] + math_file = open(self.parent.filename,'r') + math_string = math_file.read() + math_file.close() + self.text.SetValue(math_string) + # + # save file + # + def save_file(event): + result = wx.SaveFileSelector('.math','.math',self.parent.filename) + if (result != ''): + math_file = open(result,'w') + math_file.write(self.text.GetValue()) + math_file.close() + # + # panel + # + wx.Panel.__init__(self,parent) + self.sizer = wx.GridBagSizer(10,10) + self.SetSizer(self.sizer) + # + # label + # + label = wx.StaticText(self,label='from: math') + bold_font = wx.Font(10,wx.DEFAULT,wx.NORMAL,wx.BOLD) + label.SetFont(bold_font) + self.sizer.Add(label,(0,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + # text + # + self.text = wx.TextCtrl(self,-1,'',size=(self.parent.size,self.parent.size),style=wx.TE_MULTILINE) + self.sizer.Add(self.text,(1,0),flag=(wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL)) + # + # controls + # + load = wx.Button(self,label='load .math') + load.Bind(wx.EVT_BUTTON,select_file) + self.sizer.Add(load,(2,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + rload = wx.Button(self,label='reload .math') + rload.Bind(wx.EVT_BUTTON,load_file) + self.sizer.Add(rload,(3,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + save = wx.Button(self,label='save .math') + save.Bind(wx.EVT_BUTTON,save_file) + self.sizer.Add(save,(4,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + # file + # + load_file(0) + # + # fit + # + self.Fit() diff --git a/src/py/panel_math_png.py b/src/py/panel_math_png.py new file mode 100644 index 0000000..8797a7a --- /dev/null +++ b/src/py/panel_math_png.py @@ -0,0 +1,143 @@ +# +# panel_math_png.py +# make .png from .math +# +# Neil Gershenfeld +# CBA MIT 5/19/12 +# +# (c) Massachusetts Institute of Technology 2012 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,os,string +# +# panel class +# +class math_png_panel(wx.Panel): + def __init__(self,parent): + self.parent = parent + # + # get png info + # + def png_info(name): + temp_name = self.parent.tmp+'png_info' + command = 'png_size '+'\"'+name+'\"'+' > '+'\"'+temp_name+'\"' + os.system(command) + output_file = open(temp_name,'r') + output = output_file.read() + output_file.close() + command = 'rm '+'\"'+temp_name+'\"' + os.system(command) + return output + # + # make png + # + def make_png(event): + if (self.parent.rootname == ''): + return + math_text = self.parent.math_panel.text.GetValue() + tmp_math_file = self.parent.tmp+self.parent.rootname+'.math' + math_file = open(tmp_math_file,'w') + math_file.write(math_text) + math_file.close() + start = string.find(math_text,"dx dy dz:") + if (start == -1): + start = string.find(math_text,"dx dy:") + if (start == -1): + print "panel_math_png: oops, can't find bounding box" + return + dz = 0 + else: + end = string.find(math_text,"\n",start) + array = [float(s) for s in math_text[(start+10):end].split()] + dz = array[2] + start = string.find(math_text,"xmin ymin zmin:") + if (start == -1): + start = string.find(math_text,"xmin ymin:") + if (start == -1): + print "panel_math_png: oops, can't find origin" + return + zmin = 0 + else: + end = string.find(math_text,"\n",start) + array = [float(s) for s in math_text[(start+15):end].split()] + zmin = array[2] + start = string.find(math_text,"mm per unit:") + if (start == -1): + print "panel_math_png: oops, can't find units" + return + end = string.find(math_text,"\n",start) + units = float(math_text[(start+12):end]) + print units + parent.zmin = zmin + parent.zmax = zmin+dz + parent.units = units + resolution = self.parent.math_png_panel.resolution.GetValue() + self.parent.png_file = self.parent.tmp+self.parent.rootname+'.png' + command = 'math_png '+'\"'+tmp_math_file+'\"'+' '+'\"'+self.parent.png_file+'\"'+' '+resolution + print command + os.system(command) + info = png_info(self.parent.png_file) + self.info.SetLabel(info) + png_image = wx.Image(self.parent.png_file) + (nx,ny) = png_image.GetSize() + ratio = float(ny)/float(nx) + if (ratio > 1): + self.parent.ysize = self.parent.size + self.parent.xsize = self.parent.size/ratio + else: + self.parent.ysize = self.parent.size*ratio + self.parent.xsize = self.parent.size + wx.Image.Rescale(png_image,self.parent.xsize,self.parent.ysize) + png_bitmap = png_image.ConvertToBitmap() + self.bitmap.SetBitmap(png_bitmap) + self.bitmap.Show() + self.parent.Layout() + self.parent.Fit() + # + # update panels + # + parent.update_panels() + # + # panel + # + wx.Panel.__init__(self,parent) + self.sizer = wx.GridBagSizer(10,10) + self.SetSizer(self.sizer) + # + # label + # + label = wx.StaticText(self,label='to: png') + bold_font = wx.Font(10,wx.DEFAULT,wx.NORMAL,wx.BOLD) + label.SetFont(bold_font) + self.sizer.Add(label,(0,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + # image + # + image = wx.ArtProvider.GetBitmap(wx.ART_QUESTION, wx.ART_OTHER, (self.parent.size,self.parent.size)) + self.bitmap = wx.StaticBitmap(self,-1,image) + self.sizer.Add(self.bitmap,(1,0),flag=(wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL)) + self.bitmap.Hide() + # + # controls + # + make = wx.Button(self,label='make .png') + make.Bind(wx.EVT_BUTTON,make_png) + self.sizer.Add(make,(2,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + res_panel = wx.Panel(self) + res_panel_sizer = wx.GridBagSizer(10,10) + res_panel.SetSizer(res_panel_sizer) + res_panel_sizer.Add(wx.StaticText(res_panel,label='resolution (pixels/mm):'),(0,0),flag=wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) + self.resolution = wx.TextCtrl(res_panel,-1,'10') + res_panel_sizer.Add(self.resolution,(0,1),flag=wx.ALIGN_LEFT) + self.sizer.Add(res_panel,(3,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + self.info = wx.StaticText(self,label="") + self.sizer.Add(self.info,(5,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + # fit + # + self.Fit() diff --git a/src/py/panel_math_stl.py b/src/py/panel_math_stl.py new file mode 100644 index 0000000..ba2933c --- /dev/null +++ b/src/py/panel_math_stl.py @@ -0,0 +1,137 @@ +# +# panel_math_stl.py +# make .stl from .math +# +# Neil Gershenfeld +# CBA MIT 9/25/12 +# +# (c) Massachusetts Institute of Technology 2012 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,os,string +# +# panel class +# +class math_stl_panel(wx.Panel): + def __init__(self,parent): + self.parent = parent + # + # get stl info + # + def stl_info(name): + temp_name = self.parent.tmp+'stl_info' + command = 'stl_info '+'\"'+name+'\"'+' > '+'\"'+temp_name+'\"' + os.system(command) + output_file = open(temp_name,'r') + output = output_file.read() + output_file.close() + command = 'rm '+'\"'+temp_name+'\"' + os.system(command) + return output + # + # make stl + # + def make_stl(event): + if (self.parent.rootname == ''): + return + + math_text = self.parent.math_panel.text.GetValue() + tmp_math_file = self.parent.tmp+self.parent.rootname+'.math' + math_file = open(tmp_math_file,'w') + math_file.write(math_text) + math_file.close() + start = string.find(math_text,"dx dy dz:") + if (start == -1): + start = string.find(math_text,"dx dy:") + if (start == -1): + print "panel_math_png: oops, can't find bounding box" + return + dz = 0 + else: + end = string.find(math_text,"\n",start) + array = [float(s) for s in math_text[(start+10):end].split()] + dz = array[2] + start = string.find(math_text,"xmin ymin zmin:") + if (start == -1): + start = string.find(math_text,"xmin ymin:") + if (start == -1): + print "panel_math_png: oops, can't find origin" + return + zmin = 0 + else: + end = string.find(math_text,"\n",start) + array = [float(s) for s in math_text[(start+15):end].split()] + zmin = array[2] + start = string.find(math_text,"mm per unit:") + if (start == -1): + print "panel_math_png: oops, can't find units" + return + end = string.find(math_text,"\n",start) + units = float(math_text[(start+12):end]) + print units + parent.zmin = zmin + parent.zmax = zmin+dz + parent.units = units + + self.parent.stl_file = self.parent.tmp+self.parent.rootname+'.stl' + resolution = self.parent.stl_panel.resolution.GetValue() + command = 'math_stl '+'\"'+tmp_math_file+'\"'+' '+'\"'+self.parent.stl_file+'\"'+' '+resolution + print command + os.system(command) + info = stl_info(self.parent.stl_file) + self.info.SetLabel(info) + self.button.Show() + self.parent.Layout() + self.parent.Fit() + # + # send + # + def fab_send(event): + command = 'fab_send '+'\"'+self.parent.stl_file+'\"' + print command + os.system(command) + # + # panel + # + wx.Panel.__init__(self,parent) + self.sizer = wx.GridBagSizer(10,10) + self.SetSizer(self.sizer) + # + # label + # + label = wx.StaticText(self,label='to: stl') + bold_font = wx.Font(10,wx.DEFAULT,wx.NORMAL,wx.BOLD) + label.SetFont(bold_font) + self.sizer.Add(label,(0,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + # send + # + self.button = wx.Button(self,label='send it!') + self.button.Bind(wx.EVT_BUTTON,fab_send) + self.button.SetFont(bold_font) + self.sizer.Add(self.button,(1,0),flag=(wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL)) + self.button.Hide() + # + # controls + # + make = wx.Button(self,label='make .stl') + make.Bind(wx.EVT_BUTTON,make_stl) + self.sizer.Add(make,(2,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + res_panel = wx.Panel(self) + res_panel_sizer = wx.GridBagSizer(10,10) + res_panel.SetSizer(res_panel_sizer) + res_panel_sizer.Add(wx.StaticText(res_panel,label='resolution (pixels/mm):'),(0,0),flag=wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) + self.resolution = wx.TextCtrl(res_panel,-1,'1') + res_panel_sizer.Add(self.resolution,(0,1),flag=wx.ALIGN_LEFT) + self.sizer.Add(res_panel,(3,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + self.info = wx.StaticText(self,label="") + self.sizer.Add(self.info,(5,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + # fit + # + self.Fit() diff --git a/src/py/panel_path.py b/src/py/panel_path.py new file mode 100644 index 0000000..3cfac2c --- /dev/null +++ b/src/py/panel_path.py @@ -0,0 +1,411 @@ +# +# panel_path.py +# .path viewer +# +# Neil Gershenfeld 10/14/13 +# (c) Massachusetts Institute of Technology 2013 +# +# This work may be reproduced, modified, distributed, +# performed, and displayed for any purpose, but must +# acknowledge the fab modules project. Copyright is +# retained and must be preserved. The work is provided +# as is; no warranty is provided, and users accept all +# liability. +# +# imports +# +import wx,math + +# +# panel class +# +class path_panel(wx.Panel): + # + # constructor + # + def __init__(self,parent): + self.parent = parent + class path_object(object): + def __init__(self): + self.segments = [] + self.path = path_object() + self.scale = 1.0 + self.zx = 0 + self.zy = 0 + self.px = 0 + self.py = 0 + self.ox = 0 + self.oy = 0 + self.sx = [] + self.sy = [] + self.rx = [] + self.ry = [] + self.theta_x = 0 + self.theta_z = 0 + # + # drawing panel + # + wx.Panel.__init__(self,parent) + self.sizer = wx.GridBagSizer(10,10) + self.SetSizer(self.sizer) + self.panel = wx.Panel(self,size=(self.parent.parent.size,self.parent.parent.size)) + self.panel.SetBackgroundColour('white') + self.panel.Bind(wx.EVT_PAINT,self.paint) + self.panel.Bind(wx.EVT_MOUSEWHEEL,self.mouse_wheel) + self.panel.Bind(wx.EVT_LEFT_DOWN,self.mouse_left_down) + self.panel.Bind(wx.EVT_LEFT_UP,self.mouse_left_up) + self.panel.Bind(wx.EVT_MOTION,self.mouse_move) + self.panel.Bind(wx.EVT_RIGHT_DOWN,self.mouse_right_down) + self.panel.Bind(wx.EVT_RIGHT_UP,self.mouse_right_up) + self.sizer.Add(self.panel,(0,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + # controls + # + control_panel = wx.Panel(self) + control_sizer = wx.GridBagSizer(10,10) + control_panel.SetSizer(control_sizer) + self.message = wx.StaticText(control_panel,label='') + control_sizer.Add(self.message,(0,0),flag=(wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL)) + # + reset_button = wx.Button(control_panel,label='reset view') + reset_button.Bind(wx.EVT_BUTTON,self.reset_view) + control_sizer.Add(reset_button,(0,1),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + self.view_types = ["none","segments","segments+directions","segments+directions+connections"] + self.view_type_control = wx.ComboBox(control_panel,size=(100,-1),value="view type",choices=self.view_types,style=wx.CB_READONLY) + self.view_type = "segments+directions+connections" + self.view_type_control.Bind(wx.EVT_COMBOBOX,self.view_type_handler) + control_sizer.Add(self.view_type_control,(0,2)) + # + self.sizer.Add(control_panel,(1,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + # view type handler + # + def view_type_handler(self,event): + self.view_type = self.view_types[event.GetSelection()] + self.panel.Refresh() + # + # read path + # + def read_path(self,path_file_name): + class path_object(object): + def __init__(self): + self.segments = [] + path = path_object() + path_file = open(path_file_name,'r') + while 1: + line = path_file.readline() + if (line == ''): + break + elif (0 == line.find("dof:")): + path.dof = int(line.split()[1]) + elif (0 == line.find("units:")): + if (path.dof >= 2): + path.ux = line.split()[1] + path.uy = line.split()[2] + if (path.dof > 2): + path.uz = line.split()[3] + elif (0 == line.find("nx ny")): + if (path.dof == 2): + path.nx = int(line.split()[2]) + path.ny = int(line.split()[3]) + if (path.dof > 2): + path.nx = int(line.split()[3]) + path.ny = int(line.split()[4]) + path.nz = int(line.split()[5]) + elif (0 == line.find("dx dy")): + if (path.dof == 2): + path.dx = float(line.split()[2]) + path.dy = float(line.split()[3]) + if (path.dof > 2): + path.dx = float(line.split()[3]) + path.dy = float(line.split()[4]) + path.dz = float(line.split()[5]) + elif (0 == line.find("xmin ymin")): + if (path.dof == 2): + path.xmin = float(line.split()[2]) + path.ymin = float(line.split()[3]) + if (path.dof > 2): + path.xmin = float(line.split()[3]) + path.ymin = float(line.split()[4]) + path.zmin = float(line.split()[5]) + elif (0 == line.find("segment start:")): + path.segments.append([]) + while 1: + line = path_file.readline() + if (0 == line.find("segment end:")): + break + if (path.dof == 2): + x = float(line.split()[0]) + y = float(line.split()[1]) + path.segments[-1].append([x,y]) + if (path.dof == 3): + x = float(line.split()[0]) + y = float(line.split()[1]) + z = float(line.split()[2]) + path.segments[-1].append([x,y,z]) + if (path.dof == 2): + self.message.SetLabel('left: pan, scroll: zoom') + elif (path.dof > 2): + if (path.dz > 0): + self.message.SetLabel('left: pan, scroll: zoom, right: rotate') + else: + self.message.SetLabel('left: pan, scroll: zoom') + self.parent.Layout() + self.parent.Fit() + path_file.close() + return path + # + # draw path + # + def draw(self,path_file): + self.path = self.read_path(path_file) + self.panel.Refresh() + # + # rotate point + # + def rotate(self,x,y,z): + x -= self.path.nx/2.0 + y -= self.path.ny/2.0 + z -= self.path.nz/2.0 + angle = self.theta_z + xtemp = math.cos(angle)*x - math.sin(angle)*y + ytemp = math.sin(angle)*x + math.cos(angle)*y + x = xtemp + y = ytemp + angle = self.theta_x + ytemp = math.cos(angle)*y - math.sin(angle)*z + ztemp = math.sin(angle)*y + math.cos(angle)*z + y = ytemp + z = ztemp + x += self.path.nx/2.0 + y += self.path.ny/2.0 + z += self.path.nz/2.0 + return(x,y,z) + # + # scale and draw line + # + def draw_line(self,p0,p1,color,shading='off',end='none'): + # + # set aspect ratio + # + size = self.parent.parent.size + ratio = float(self.path.ny)/float(self.path.nx) + if (ratio > 1): + ysize = size + xsize = size/ratio + else: + ysize = size*ratio + xsize = size + # + # get points + # + if (len(p0) >= 2): + x0 = p0[0] + x1 = p1[0] + y0 = p0[1] + y1 = p1[1] + if (len(p0) >= 3): + z0 = p0[2] + z1 = p1[2] + # + # set 3D shading + # + if (self.path.dof > 2): + if ((self.path.nz > 1) & (shading == 'z')): + i = 0.8*((z0+z1)/2.0)/(self.path.nz-1.0) + color = (255.0*i+color[0]*(1.0-i),255.0*i+color[1]*(1.0-i),255.0*i+color[2]*(1.0-i)) + # + # rotate + # + if (self.path.dof > 2): + if (self.path.nz > 1): + (x0,y0,z0) = self.rotate(x0,y0,z0) + (x1,y1,z1) = self.rotate(x1,y1,z1) + # + # pan + # + if (self.sx != []): + self.px = self.mx - self.sx + if (self.sy != []): + self.py = self.my - self.sy + # + # zoom + # + xi0 = self.ox + self.px + self.zx + self.scale*(xsize*(x0/(self.path.nx-1.0))-self.zx) + xi1 = self.ox + self.px + self.zx + self.scale*(xsize*(x1/(self.path.nx-1.0))-self.zx) + yi0 = self.oy + self.py + self.zy + self.scale*(ysize*(y0/(self.path.ny-1.0))-self.zy) + yi1 = self.oy + self.py + self.zy + self.scale*(ysize*(y1/(self.path.ny-1.0))-self.zy) + # + # draw line + # + self.dc.SetPen(wx.Pen(color,1)) + self.dc.DrawLine(xi0,yi0,xi1,yi1) + # + # draw arrow head + # + if (end == "arrow"): + d = math.sqrt((xi1-xi0)*(xi1-xi0)+(yi1-yi0)*(yi1-yi0)) + if (d > 0): + dx = 4*(xi1-xi0)/d + dy = 4*(yi1-yi0)/d + self.dc.DrawLine(xi1,yi1,xi1-dx+dy,yi1-dy-dx) + self.dc.DrawLine(xi1,yi1,xi1-dx-dy,yi1-dy+dx) + self.dc.DrawLine(xi1-dx+dy,yi1-dy-dx,xi1-dx-dy,yi1-dy+dx) + # + # real to panel coordinates + # + def coords(self,p): + if (len(p) == 2): + ix = self.path.nx*(p[0]-self.path.xmin)/self.path.dx + iy = self.path.nx*(self.path.ymin+self.path.dy-p[1])/self.path.dx + return([ix,iy]) + elif (len(p) >= 3): + ix = self.path.nx*(p[0]-self.path.xmin)/self.path.dx + iy = self.path.nx*(self.path.ymin+self.path.dy-p[1])/self.path.dx + iz = self.path.nx*(p[2]-self.path.zmin)/self.path.dx + return([ix,iy,iz]) + # + # paint panel + # + def paint(self,event): + self.dc = wx.PaintDC(self.panel) + # + # return if no path + # + if (len(self.path.segments) == 0): + return + # + # return if view none + # + if (self.view_type == "none"): + return + # + # draw axes + # + if (self.path.dof == 2): + self.draw_line(self.coords([0,0]),self.coords([self.path.dx/10.0,0]),(0,255,0),end="arrow") + self.draw_line(self.coords([0,0]),self.coords([0,self.path.dx/10.0]),(0,255,255),end="arrow") + elif (self.path.dof >= 3): + self.draw_line(self.coords([0,0,0]),self.coords([self.path.dx/10.0,0,0]),(0,255,0),end="arrow") + self.draw_line(self.coords([0,0,0]),self.coords([0,self.path.dx/10.0,0]),(0,255,255),end="arrow") + self.draw_line(self.coords([0,0,0]),self.coords([0,0,self.path.dx/10.0]),(255,0,255),end="arrow") + # + # loop over segments + # + if (self.view_type == "segments"): + for segment in range(len(self.path.segments)): + s0 = self.path.segments[segment] + # + # loop over points + # + for point in range(len(s0)-1): + self.draw_line(s0[point],s0[point+1],(0,0,255),shading='z',end="none") + elif (self.view_type == "segments+directions"): + for segment in range(len(self.path.segments)): + s0 = self.path.segments[segment] + # + # loop over points + # + for point in range(len(s0)-1): + self.draw_line(s0[point],s0[point+1],(0,0,255),shading='z',end="arrow") + elif (self.view_type == "segments+directions+connections"): + for segment in range(len(self.path.segments)): + s0 = self.path.segments[segment] + # + # loop over points + # + for point in range(len(s0)-1): + self.draw_line(s0[point],s0[point+1],(0,0,255),shading='z',end="arrow") + # + # draw path to next segment + # + if (segment < (len(self.path.segments)-1)): + s1 = self.path.segments[segment+1] + self.draw_line(s0[-1],s1[0],(255,0,0),shading='z',end="arrow") + # + # mouse move: pan + # + def mouse_move(self,event): + self.mx = event.GetX() + self.my = event.GetY() + size = self.parent.parent.size + if (self.sx != []): + # + # panning + # + self.panel.Refresh() + if (self.rx != []): + # + # rotating + # + self.theta_z = self.theta_zs + math.pi*(self.mx-self.rx)/(size-1.0) + self.theta_x = self.theta_xs + math.pi*(self.ry-self.my)/(size-1.0) + self.panel.Refresh() + # + # mouse left down: start pan + # + def mouse_left_down(self,event): + self.sx = event.GetX() + self.sy = event.GetY() + # + # mouse left up: stop pan + # + def mouse_left_up(self,event): + self.sx = [] + self.sy = [] + self.ox = self.ox + self.px + self.oy = self.oy + self.py + self.px = 0 + self.py = 0 + # + # mouse right down: start rotate + # + def mouse_right_down(self,event): + self.rx = event.GetX() + self.ry = event.GetY() + self.theta_xs = self.theta_x + self.theta_zs = self.theta_z + # + # mouse right up: stop rotate + # + def mouse_right_up(self,event): + self.rx = [] + self.ry = [] + # + # mouse wheel: zoom + # + def mouse_wheel(self,event): + mx = (event.GetX() - self.ox - self.zx)/self.scale + self.zx + my = (event.GetY() - self.oy - self.zy)/self.scale + self.zy + self.ox = self.ox + (self.scale - 1.0)*(mx - self.zx) + self.oy = self.oy + (self.scale - 1.0)*(my - self.zy) + self.zx = mx + self.zy = my + if (event.GetWheelRotation() > 0): + self.scale *= 1.1 + else: + self.scale *= 0.9 + self.panel.Refresh() + # + # reset view + # + def reset_view(self,event): + self.scale = 1.0 + self.zx = 0 + self.zy = 0 + self.px = 0 + self.py = 0 + self.ox = 0 + self.oy = 0 + self.sx = [] + self.sy = [] + self.theta_x = 0 + self.theta_z = 0 + self.panel.Refresh() + # + # parent call to update size + # + def update_size(self,sizex,sizey): + self.Layout() + self.Fit() diff --git a/src/py/panel_path_camm.py b/src/py/panel_path_camm.py new file mode 100644 index 0000000..ae0301e --- /dev/null +++ b/src/py/panel_path_camm.py @@ -0,0 +1,120 @@ +# +# panel_path_camm.py +# make .camm from .path +# +# Neil Gershenfeld +# CBA MIT 2/19/11 +# +# (c) Massachusetts Institute of Technology 2011 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,os +# +# panel class +# +class path_camm_panel(wx.Panel): + def __init__(self,parent): + self.parent = parent + self.parent.path_file = '' + # + # make file + # + def make_file(event): + if (self.parent.path_file == ''): + print 'panel_path_camm: oops -- need path file' + return + self.parent.camm_file = self.parent.tmp+self.parent.rootname+'.camm' + force = self.force.GetValue() + velocity = self.velocity.GetValue() + ox = self.origin_x.GetValue() + oy = self.origin_y.GetValue() + if self.top_left.GetValue(): + loc = 'L' + elif self.top_right.GetValue(): + loc = 'R' + elif self.bottom_left.GetValue(): + loc = 'l' + elif self.bottom_right.GetValue(): + loc = 'r' + command = 'path_camm '+'\"'+self.parent.path_file+'\"'+' '+'\"'+self.parent.camm_file+'\"'+' '+force+' '+velocity+' '+ox+' '+oy+' '+loc + print command + os.system(command) + self.button.Show() + self.parent.Layout() + self.parent.Fit() + # + # send + # + def fab_send(event): + command = 'fab_send '+'\"'+self.parent.camm_file+'\"' + print command + os.system(command) + # + # panel + # + wx.Panel.__init__(self,parent) + self.sizer = wx.GridBagSizer(10,10) + self.SetSizer(self.sizer) + # + # label + # + label = wx.StaticText(self,label='to: camm') + bold_font = wx.Font(10,wx.DEFAULT,wx.NORMAL,wx.BOLD) + label.SetFont(bold_font) + self.sizer.Add(label,(0,0),span=(1,2),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + # send + # + self.button = wx.Button(self,label='send it!') + self.button.Bind(wx.EVT_BUTTON,fab_send) + self.button.SetFont(bold_font) + self.sizer.Add(self.button,(1,0),span=(1,2),flag=(wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL)) + self.button.Hide() + # + # controls + # + make = wx.Button(self,label='make .camm') + make.Bind(wx.EVT_BUTTON,make_file) + self.sizer.Add(make,(2,0),span=(1,2),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + force_panel = wx.Panel(self) + force_sizer = wx.GridBagSizer(10,10) + force_panel.SetSizer(force_sizer) + force_sizer.Add(wx.StaticText(force_panel,label='force (g)'),(0,0),flag=(wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)) + self.force = wx.TextCtrl(force_panel,-1,'45') + force_sizer.Add(self.force,(0,1),flag=wx.ALIGN_LEFT) + # + force_sizer.Add(wx.StaticText(force_panel,label='velocity (cm/2)'),(1,0),flag=(wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)) + self.velocity = wx.TextCtrl(force_panel,-1,'2') + force_sizer.Add(self.velocity,(1,1),flag=wx.ALIGN_LEFT) + self.sizer.Add(force_panel,(3,0),span=(1,2),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + origin_panel = wx.Panel(self) + origin_sizer = wx.GridBagSizer(10,10) + origin_panel.SetSizer(origin_sizer) + self.top_left = wx.RadioButton(origin_panel,-1,'left',(10,10),style=wx.RB_GROUP) + origin_sizer.Add(self.top_left,(0,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + origin_sizer.Add(wx.StaticText(origin_panel,label='top'),(0,1),flag=(wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL)) + self.top_right = wx.RadioButton(origin_panel,-1,'right',(10,10)) + origin_sizer.Add(self.top_right,(0,2),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + self.origin_x = wx.TextCtrl(origin_panel,-1,'0') + origin_sizer.Add(self.origin_x,(1,0),flag=wx.ALIGN_RIGHT) + origin_sizer.Add(wx.StaticText(origin_panel,label='x origin (mm) y'),(1,1),flag=(wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL)) + self.origin_y = wx.TextCtrl(origin_panel,-1,'0') + origin_sizer.Add(self.origin_y,(1,2),flag=wx.ALIGN_LEFT) + # + self.bottom_left = wx.RadioButton(origin_panel,-1,'left',(10,10)) + origin_sizer.Add(self.bottom_left,(2,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + self.bottom_left.SetValue(True) + origin_sizer.Add(wx.StaticText(origin_panel,label='bottom'),(2,1),flag=(wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL)) + self.bottom_right = wx.RadioButton(origin_panel,-1,'right',(10,10)) + origin_sizer.Add(self.bottom_right,(2,2),flag=wx.ALIGN_CENTER_HORIZONTAL) + self.sizer.Add(origin_panel,(4,0),span=(1,2),flag=(wx.ALIGN_CENTER_HORIZONTAL)) + # + # fit + # + self.Fit() diff --git a/src/py/panel_path_dxf.py b/src/py/panel_path_dxf.py new file mode 100644 index 0000000..17c1e6b --- /dev/null +++ b/src/py/panel_path_dxf.py @@ -0,0 +1,73 @@ +# +# panel_path_dxf.py +# make .dxf from .path +# +# Neil Gershenfeld +# CBA MIT 8/25/12 +# +# (c) Massachusetts Institute of Technology 2012 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,os +# +# panel class +# +class path_dxf_panel(wx.Panel): + def __init__(self,parent): + self.parent = parent + self.parent.path_file = '' + # + # make dxf + # + def make_dxf(event): + if (self.parent.path_file == ''): + print 'panel_path_dxf: oops -- need path file' + return + self.parent.dxf_file = self.parent.tmp+self.parent.rootname+'.dxf' + command = 'path_dxf '+'\"'+self.parent.path_file+'\"'+' '+'\"'+self.parent.dxf_file+'\"' + print command + os.system(command) + self.button.Show() + self.parent.Layout() + self.parent.Fit() + # + # send + # + def fab_send(event): + command = 'fab_send '+'\"'+self.parent.dxf_file+'\"' + print command + os.system(command) + # + # panel + # + wx.Panel.__init__(self,parent) + self.sizer = wx.GridBagSizer(10,10) + self.SetSizer(self.sizer) + # + # label + # + label = wx.StaticText(self,label='to: dxf') + bold_font = wx.Font(10,wx.DEFAULT,wx.NORMAL,wx.BOLD) + label.SetFont(bold_font) + self.sizer.Add(label,(0,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + # send + # + self.button = wx.Button(self,label='send it!') + self.button.Bind(wx.EVT_BUTTON,fab_send) + self.button.SetFont(bold_font) + self.sizer.Add(self.button,(1,0),flag=(wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL)) + self.button.Hide() + # + # controls + # + make = wx.Button(self,label='make .dxf') + make.Bind(wx.EVT_BUTTON,make_dxf) + self.sizer.Add(make,(2,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + # fit + # + self.Fit() diff --git a/src/py/panel_path_epi.py b/src/py/panel_path_epi.py new file mode 100644 index 0000000..c277291 --- /dev/null +++ b/src/py/panel_path_epi.py @@ -0,0 +1,248 @@ +# +# panel_path_epi.py +# make .epi from .path +# +# Neil Gershenfeld 8/19/13 +# (c) Massachusetts Institute of Technology 2013 +# +# This work may be reproduced, modified, distributed, +# performed, and displayed for any purpose, but must +# acknowledge the fab modules project. Copyright is +# retained and must be preserved. The work is provided +# as is; no warranty is provided, and users accept all +# liability. +# + +# +# imports +# +import wx,os,string +# +# panel class +# +class path_epi_panel(wx.Panel): + def __init__(self,parent): + self.parent = parent + self.parent.path_file = '' + # + # make epi + # + def make_epi(event): + if (self.parent.path_file == ''): + print 'panel_path_epi: oops -- need path file' + return + self.parent.epi_file = self.parent.tmp+self.parent.rootname+'.epi' + if (string.find(self.parent.path_type,"2D") != -1): + power = self.power_2D.GetValue() + if (self.focus_2D.GetValue()): + focus = '1' + else: + focus = '0' + ox = self.origin_x_2D.GetValue() + oy = self.origin_y_2D.GetValue() + if self.top_left_2D.GetValue(): + loc = 'L' + elif self.top_right_2D.GetValue(): + loc = 'R' + elif self.bottom_left_2D.GetValue(): + loc = 'l' + elif self.bottom_right_2D.GetValue(): + loc = 'r' + speed = self.speed_2D.GetValue() + rate = self.rate_2D.GetValue() + command = 'path_epi '+'\"'+self.parent.path_file+'\"'+' '+'\"'+self.parent.epi_file+'\"'+' '+power+' '+speed+' '+focus+' '+ox+' '+oy+' '+loc+' '+' '+rate + print command + os.system(command) + self.button.Show() + self.parent.Layout() + self.parent.Fit() + elif (string.find(self.parent.path_type,"3D") != -1): + min_power = self.min_power_3D.GetValue() + max_power = self.max_power_3D.GetValue() + if (self.focus_3D.GetValue()): + focus = '1' + else: + focus = '0' + ox = self.origin_x_3D.GetValue() + oy = self.origin_y_3D.GetValue() + if self.top_left_3D.GetValue(): + loc = 'L' + elif self.top_right_3D.GetValue(): + loc = 'R' + elif self.bottom_left_3D.GetValue(): + loc = 'l' + elif self.bottom_right_3D.GetValue(): + loc = 'r' + speed = self.speed_3D.GetValue() + rate = self.rate_3D.GetValue() + command = 'path_epi '+'\"'+self.parent.path_file+'\"'+' '+'\"'+self.parent.epi_file+'\"'+' '+min_power+' '+speed+' '+focus+' '+ox+' '+oy+' '+loc+' '+' '+rate+' '+max_power + print command + os.system(command) + self.button.Show() + self.parent.Layout() + self.parent.Fit() + else: + print "panel_path_epi: oops -- don't recognize path type" + return + # + # send + # + def fab_send(event): + command = 'fab_send '+'\"'+self.parent.epi_file+'\"' + print command + os.system(command) + # + # panel + # + wx.Panel.__init__(self,parent) + self.sizer = wx.GridBagSizer(10,10) + self.SetSizer(self.sizer) + # + # label + # + label = wx.StaticText(self,label='to: epi') + bold_font = wx.Font(10,wx.DEFAULT,wx.NORMAL,wx.BOLD) + label.SetFont(bold_font) + self.sizer.Add(label,(0,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + # send + # + self.button = wx.Button(self,label='send it!') + self.button.Bind(wx.EVT_BUTTON,fab_send) + self.button.SetFont(bold_font) + self.sizer.Add(self.button,(1,0),flag=(wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL)) + self.button.Hide() + # + # controls + # + make = wx.Button(self,label='make .epi') + make.Bind(wx.EVT_BUTTON,make_epi) + self.sizer.Add(make,(2,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + # 2D panel + # + self.panel_2D = wx.Panel(self) + sizer_2D = wx.GridBagSizer(10,10) + self.panel_2D.SetSizer(sizer_2D) + # + self.focus_2D = wx.CheckBox(self.panel_2D,-1,'autofocus') + sizer_2D.Add(self.focus_2D,(0,0),span=(1,2),flag=(wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL)) + # + sizer_2D.Add(wx.StaticText(self.panel_2D,label='power (%)'),(1,0),span=(1,2),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + self.power_2D = wx.TextCtrl(self.panel_2D,-1,'25') + sizer_2D.Add(self.power_2D,(2,0),span=(1,2),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + set_panel_2D = wx.Panel(self.panel_2D) + set_sizer_2D = wx.GridBagSizer(10,10) + set_panel_2D.SetSizer(set_sizer_2D) + # + set_sizer_2D.Add(wx.StaticText(set_panel_2D,label='speed (%)'),(0,0),flag=wx.ALIGN_RIGHT) + set_sizer_2D.Add(wx.StaticText(set_panel_2D,label='rate'),(0,1),flag=wx.ALIGN_LEFT) + # + self.speed_2D = wx.TextCtrl(set_panel_2D,-1,'75') + set_sizer_2D.Add(self.speed_2D,(1,0),flag=wx.ALIGN_RIGHT) + self.rate_2D = wx.TextCtrl(set_panel_2D,-1,'500') + set_sizer_2D.Add(self.rate_2D,(1,1),flag=wx.ALIGN_LEFT) + # + sizer_2D.Add(set_panel_2D,(3,0),span=(1,2),flag=(wx.ALIGN_CENTER_HORIZONTAL)) + # + origin_panel_2D = wx.Panel(self.panel_2D) + origin_sizer_2D = wx.GridBagSizer(10,10) + origin_panel_2D.SetSizer(origin_sizer_2D) + # + self.top_left_2D = wx.RadioButton(origin_panel_2D,-1,'left',(10,10),style=wx.RB_GROUP) + origin_sizer_2D.Add(self.top_left_2D,(0,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + origin_sizer_2D.Add(wx.StaticText(origin_panel_2D,label='top'),(0,1),flag=(wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL)) + self.top_right_2D = wx.RadioButton(origin_panel_2D,-1,'right',(10,10)) + origin_sizer_2D.Add(self.top_right_2D,(0,2),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + self.origin_x_2D = wx.TextCtrl(origin_panel_2D,-1,'10') + origin_sizer_2D.Add(self.origin_x_2D,(1,0),flag=wx.ALIGN_RIGHT) + origin_sizer_2D.Add(wx.StaticText(origin_panel_2D,label='x origin (mm) y'),(1,1),flag=(wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL)) + self.origin_y_2D = wx.TextCtrl(origin_panel_2D,-1,'10') + origin_sizer_2D.Add(self.origin_y_2D,(1,2),flag=wx.ALIGN_LEFT) + # + self.bottom_left_2D = wx.RadioButton(origin_panel_2D,-1,'left',(10,10)) + origin_sizer_2D.Add(self.bottom_left_2D,(2,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + origin_sizer_2D.Add(wx.StaticText(origin_panel_2D,label='bottom'),(2,1),flag=(wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL)) + self.bottom_right_2D = wx.RadioButton(origin_panel_2D,-1,'right',(10,10)) + # + origin_sizer_2D.Add(self.bottom_right_2D,(2,2),flag=wx.ALIGN_CENTER_HORIZONTAL) + sizer_2D.Add(origin_panel_2D,(4,0),span=(1,2),flag=(wx.ALIGN_CENTER_HORIZONTAL)) + # + self.sizer.Add(self.panel_2D,(3,0),flag=(wx.ALIGN_CENTER_HORIZONTAL)) + self.path_type = "2D" + # + # 3D panel + # + self.panel_3D = wx.Panel(self) + sizer_3D = wx.GridBagSizer(10,10) + self.panel_3D.SetSizer(sizer_3D) + # + self.focus_3D = wx.CheckBox(self.panel_3D,-1,'autofocus') + sizer_3D.Add(self.focus_3D,(0,0),span=(1,2),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + set_panel_3D = wx.Panel(self.panel_3D) + set_sizer_3D = wx.GridBagSizer(10,10) + set_panel_3D.SetSizer(set_sizer_3D) + # + set_sizer_3D.Add(wx.StaticText(set_panel_3D,label='min z power (%)'),(0,0),flag=wx.ALIGN_RIGHT) + set_sizer_3D.Add(wx.StaticText(set_panel_3D,label='max z power (%)'),(0,1),flag=wx.ALIGN_LEFT) + # + self.min_power_3D = wx.TextCtrl(set_panel_3D,-1,'25') + set_sizer_3D.Add(self.min_power_3D,(1,0),flag=wx.ALIGN_RIGHT) + self.max_power_3D = wx.TextCtrl(set_panel_3D,-1,'25') + set_sizer_3D.Add(self.max_power_3D,(1,1),flag=wx.ALIGN_LEFT) + # + set_sizer_3D.Add(wx.StaticText(set_panel_3D,label='speed (%)'),(2,0),flag=wx.ALIGN_RIGHT) + set_sizer_3D.Add(wx.StaticText(set_panel_3D,label='rate'),(2,1),flag=wx.ALIGN_LEFT) + # + self.speed_3D = wx.TextCtrl(set_panel_3D,-1,'75') + set_sizer_3D.Add(self.speed_3D,(3,0),flag=wx.ALIGN_RIGHT) + self.rate_3D = wx.TextCtrl(set_panel_3D,-1,'500') + set_sizer_3D.Add(self.rate_3D,(3,1),flag=wx.ALIGN_LEFT) + # + sizer_3D.Add(set_panel_3D,(1,0),span=(1,2),flag=(wx.ALIGN_CENTER_HORIZONTAL)) + # + origin_panel_3D = wx.Panel(self.panel_3D) + origin_sizer_3D = wx.GridBagSizer(10,10) + origin_panel_3D.SetSizer(origin_sizer_3D) + # + self.top_left_3D = wx.RadioButton(origin_panel_3D,-1,'left',(10,10),style=wx.RB_GROUP) + origin_sizer_3D.Add(self.top_left_3D,(0,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + origin_sizer_3D.Add(wx.StaticText(origin_panel_3D,label='top'),(0,1),flag=(wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL)) + self.top_right_3D = wx.RadioButton(origin_panel_3D,-1,'right',(10,10)) + origin_sizer_3D.Add(self.top_right_3D,(0,2),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + self.origin_x_3D = wx.TextCtrl(origin_panel_3D,-1,'10') + origin_sizer_3D.Add(self.origin_x_3D,(1,0),flag=wx.ALIGN_RIGHT) + origin_sizer_3D.Add(wx.StaticText(origin_panel_3D,label='x origin (mm) y'),(1,1),flag=(wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL)) + self.origin_y_3D = wx.TextCtrl(origin_panel_3D,-1,'10') + origin_sizer_3D.Add(self.origin_y_3D,(1,2),flag=wx.ALIGN_LEFT) + # + self.bottom_left_3D = wx.RadioButton(origin_panel_3D,-1,'left',(10,10)) + origin_sizer_3D.Add(self.bottom_left_3D,(2,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + origin_sizer_3D.Add(wx.StaticText(origin_panel_3D,label='bottom'),(2,1),flag=(wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL)) + self.bottom_right_3D = wx.RadioButton(origin_panel_3D,-1,'right',(10,10)) + origin_sizer_3D.Add(self.bottom_right_3D,(2,2),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + sizer_3D.Add(origin_panel_3D,(2,0),span=(1,2),flag=(wx.ALIGN_CENTER_HORIZONTAL)) + # + self.panel_3D.Hide() + # + # parent call to update panel + # + def update_panel(self): + if (string.find(self.parent.path_type,"3D") != -1): + self.sizer.Detach(self.panel_2D) + self.panel_2D.Hide() + self.sizer.Add(self.panel_3D,(3,0),flag=(wx.ALIGN_CENTER_HORIZONTAL)) + self.panel_3D.Show() + elif (string.find(self.parent.path_type,"2D") != -1): + self.sizer.Detach(self.panel_3D) + self.panel_3D.Hide() + self.sizer.Add(self.panel_2D,(3,0),flag=(wx.ALIGN_CENTER_HORIZONTAL)) + self.panel_2D.Show() + self.Layout() + self.Fit() diff --git a/src/py/panel_path_eps.py b/src/py/panel_path_eps.py new file mode 100644 index 0000000..8e46d42 --- /dev/null +++ b/src/py/panel_path_eps.py @@ -0,0 +1,76 @@ +# +# panel_path_eps.py +# make .eps from .path +# +# Neil Gershenfeld 7/4/13 +# (c) Massachusetts Institute of Technology 2013 +# +# This work may be reproduced, modified, distributed, +# performed, and displayed for any purpose, but must +# acknowledge the fab modules project. Copyright is +# retained and must be preserved. The work is provided +# as is; no warranty is provided, and users accept all +# liability. +# +# imports +# +import wx,os +# +# panel class +# +class path_eps_panel(wx.Panel): + def __init__(self,parent): + self.parent = parent + self.parent.path_file = '' + # + # make eps + # + def make_eps(event): + if (self.parent.path_file == ''): + print 'make_png_eps: oops -- need path file' + return + self.parent.eps_file = self.parent.tmp+self.parent.rootname+'.eps' + command = 'path_eps '+'\"'+self.parent.path_file+'\"'+' '+'\"'+self.parent.eps_file+'\"' + print command + os.system(command) + self.button.Show() + self.parent.Layout() + self.parent.Fit() + # + # send + # + def fab_send(event): + command = 'fab_send '+'\"'+self.parent.eps_file+'\"' + print command + os.system(command) + # + # panel + # + wx.Panel.__init__(self,parent) + self.sizer = wx.GridBagSizer(10,10) + self.SetSizer(self.sizer) + # + # label + # + label = wx.StaticText(self,label='to: eps') + bold_font = wx.Font(10,wx.DEFAULT,wx.NORMAL,wx.BOLD) + label.SetFont(bold_font) + self.sizer.Add(label,(0,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + # send + # + self.button = wx.Button(self,label='send it!') + self.button.Bind(wx.EVT_BUTTON,fab_send) + self.button.SetFont(bold_font) + self.sizer.Add(self.button,(1,0),flag=(wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL)) + self.button.Hide() + # + # controls + # + make = wx.Button(self,label='make .eps') + make.Bind(wx.EVT_BUTTON,make_eps) + self.sizer.Add(make,(2,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + # fit + # + self.Fit() diff --git a/src/py/panel_path_g.py b/src/py/panel_path_g.py new file mode 100644 index 0000000..7315a37 --- /dev/null +++ b/src/py/panel_path_g.py @@ -0,0 +1,167 @@ +# +# panel_path_g.py +# make .g from .path +# +# Neil Gershenfeld +# CBA MIT 2/26/11 +# +# (c) Massachusetts Institute of Technology 2011 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,os,string +# +# panel class +# +class path_g_panel(wx.Panel): + def __init__(self,parent): + self.parent = parent + self.parent.path_file = '' + # + # make file + # + def make_file(event): + if (self.parent.path_file == ''): + print 'panel_path_g: oops -- need path file' + return + self.parent.g_file = self.parent.tmp+self.parent.rootname+'.g' + if (self.conv.GetValue()): + direction = '0' + else: + direction = '1' + height = self.height.GetValue() + plunge = self.plunge.GetValue() + speed = self.speed.GetValue() + spindle = self.spindle.GetValue() + tool = self.tool.GetValue() + if (self.on.GetValue()): + coolant = '1' + else: + coolant = '0' + command = 'path_g '+'\"'+self.parent.path_file+'\"'+' '+'\"'+self.parent.g_file+'\"'+' '+direction+' '+height+' '+speed+' '+plunge+' '+spindle+' '+tool+' '+coolant + print command + os.system(command) + temp_name = self.parent.tmp+'path_info' + command = 'path_time '+'\"'+self.parent.path_file+'\"'+' '+speed+' '+height+' > '+'\"'+temp_name+'\"' + print command + os.system(command) + output_file = open(temp_name,'r') + output = output_file.read() + output_file.close() + command = 'rm '+'\"'+temp_name+'\"' + start = 11+string.find(output,'path time: ') + end = 5+string.find(output,'hours',start) + times = output[start:end] + self.info.SetLabel(times) + os.system(command) + self.button.Show() + self.parent.Layout() + self.parent.Fit() + # + # send + # + def fab_send(event): + command = 'fab_send '+'\"'+self.parent.g_file+'\"' + print command + os.system(command) + # + # panel + # + wx.Panel.__init__(self,parent) + self.sizer = wx.GridBagSizer(10,10) + self.SetSizer(self.sizer) + # + # label + # + label = wx.StaticText(self,label='to: g') + bold_font = wx.Font(10,wx.DEFAULT,wx.NORMAL,wx.BOLD) + label.SetFont(bold_font) + self.sizer.Add(label,(0,0),span=(1,2),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + # send + # + self.button = wx.Button(self,label='send it!') + self.button.Bind(wx.EVT_BUTTON,fab_send) + self.button.SetFont(bold_font) + self.sizer.Add(self.button,(1,0),span=(1,2),flag=(wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL)) + self.button.Hide() + # + # info + # + self.info = wx.StaticText(self,label="") + self.sizer.Add(self.info,(2,0),span=(1,2),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + # controls + # + make = wx.Button(self,label='make .g') + make.Bind(wx.EVT_BUTTON,make_file) + self.sizer.Add(make,(3,0),span=(1,2),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + dir_panel = wx.Panel(self) + dir_sizer = wx.GridBagSizer(10,10) + dir_panel.SetSizer(dir_sizer) + self.conv = wx.RadioButton(dir_panel,label='conventional',style=wx.RB_GROUP) + dir_sizer.Add(self.conv,(0,0)) + self.climb = wx.RadioButton(dir_panel,label='climb') + dir_sizer.Add(self.climb,(0,1)) + self.sizer.Add(dir_panel,(4,0),span=(1,2),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + row5_panel = wx.Panel(self) + row5_sizer = wx.GridBagSizer(10,10) + row5_panel.SetSizer(row5_sizer) + row5_sizer.Add(wx.StaticText(row5_panel,label=' cut speed (mm/s)'),(0,0),flag=(wx.ALIGN_RIGHT)) + row5_sizer.Add(wx.StaticText(row5_panel,label='plunge speed (mm/s)'),(0,1),flag=(wx.ALIGN_LEFT)) + self.sizer.Add(row5_panel,(5,0),span=(1,2),flag=(wx.ALIGN_CENTER_HORIZONTAL)) + # + row6_panel = wx.Panel(self) + row6_sizer = wx.GridBagSizer(10,10) + row6_panel.SetSizer(row6_sizer) + self.speed = wx.TextCtrl(row6_panel,-1,'5') + row6_sizer.Add(self.speed,(0,0),flag=(wx.ALIGN_RIGHT)) + self.plunge = wx.TextCtrl(row6_panel,-1,'2.5') + row6_sizer.Add(self.plunge,(0,1),flag=(wx.ALIGN_LEFT)) + self.sizer.Add(row6_panel,(6,0),span=(1,2),flag=(wx.ALIGN_CENTER_HORIZONTAL)) + # + row7_panel = wx.Panel(self) + row7_sizer = wx.GridBagSizer(10,10) + row7_panel.SetSizer(row7_sizer) + row7_sizer.Add(wx.StaticText(row7_panel,label='spindle speed (RPM)'),(0,0),flag=(wx.ALIGN_RIGHT)) + row7_sizer.Add(wx.StaticText(row7_panel,label='jog height (mm) '),(0,1),flag=(wx.ALIGN_LEFT)) + self.sizer.Add(row7_panel,(7,0),span=(1,2),flag=(wx.ALIGN_CENTER_HORIZONTAL)) + # + row8_panel = wx.Panel(self) + row8_sizer = wx.GridBagSizer(10,10) + row8_panel.SetSizer(row8_sizer) + self.spindle = wx.TextCtrl(row8_panel,-1,'10000') + row8_sizer.Add(self.spindle,(0,0),flag=(wx.ALIGN_RIGHT)) + self.height = wx.TextCtrl(row8_panel,-1,'5') + row8_sizer.Add(self.height,(0,1),flag=(wx.ALIGN_LEFT)) + self.sizer.Add(row8_panel,(8,0),span=(1,2),flag=(wx.ALIGN_CENTER_HORIZONTAL)) + # + row9_panel = wx.Panel(self) + row9_sizer = wx.GridBagSizer(10,10) + row9_panel.SetSizer(row9_sizer) + row9_sizer.Add(wx.StaticText(row9_panel,label='tool number'),(0,0),flag=(wx.ALIGN_RIGHT)) + row9_sizer.Add(wx.StaticText(row9_panel,label='coolant '),(0,1),flag=(wx.ALIGN_LEFT)) + self.sizer.Add(row9_panel,(9,0),span=(1,2),flag=(wx.ALIGN_CENTER_HORIZONTAL)) + # + row10_panel = wx.Panel(self) + row10_sizer = wx.GridBagSizer(10,10) + row10_panel.SetSizer(row10_sizer) + self.tool = wx.TextCtrl(row10_panel,-1,'1') + row10_sizer.Add(self.tool,(0,0),flag=(wx.ALIGN_RIGHT)) + coolant_panel = wx.Panel(row10_panel) + coolant_sizer = wx.GridBagSizer(10,10) + coolant_panel.SetSizer(coolant_sizer) + self.on = wx.RadioButton(coolant_panel,label='on',style=wx.RB_GROUP) + coolant_sizer.Add(self.on,(0,0)) + self.off = wx.RadioButton(coolant_panel,label='no') + coolant_sizer.Add(self.off,(0,1)) + row10_sizer.Add(coolant_panel,(0,1),flag=wx.ALIGN_LEFT) + self.sizer.Add(row10_panel,(10,0),span=(1,2),flag=(wx.ALIGN_CENTER_HORIZONTAL)) + # + # fit + # + self.Fit() diff --git a/src/py/panel_path_oms.py b/src/py/panel_path_oms.py new file mode 100644 index 0000000..1f7b577 --- /dev/null +++ b/src/py/panel_path_oms.py @@ -0,0 +1,113 @@ +# +# panel_path_oms.py +# make .oms from .path +# +# Neil Gershenfeld +# CBA MIT 5/25/13 +# +# (c) Massachusetts Institute of Technology 2013 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,os +# +# panel class +# +class path_oms_panel(wx.Panel): + def __init__(self,parent): + self.parent = parent + self.parent.path_file = '' + # + # make oms + # + def make_oms(event): + if (self.parent.path_file == ''): + print 'panel_path_oms: oops -- need path file' + return + self.parent.oms_file = self.parent.tmp+self.parent.rootname+'.oms' + velocity = self.velocity.GetValue() + accel = self.accel.GetValue() + period = self.period.GetValue() + command = 'path_oms '+'\"'+self.parent.path_file+'\"'+' '+'\"'+self.parent.oms_file+'\"'+' '+velocity+' '+accel+' '+period + print command + os.system(command) + self.button.SetMaxSize((self.parent.xsize,self.parent.ysize)) + self.button.SetMinSize((self.parent.xsize,self.parent.ysize)) + self.button.Show() + self.parent.Layout() + self.parent.Fit() + # + # send + # + def fab_send(event): + command = 'fab_send '+'\"'+self.parent.oms_file+'\"' + print command + os.system(command) + # + # panel + # + wx.Panel.__init__(self,parent) + self.sizer = wx.GridBagSizer(10,10) + self.SetSizer(self.sizer) + # + # label + # + label = wx.StaticText(self,label='to: oms') + bold_font = wx.Font(10,wx.DEFAULT,wx.NORMAL,wx.BOLD) + label.SetFont(bold_font) + self.sizer.Add(label,(0,0),span=(1,2),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + # send + # + self.button = wx.Button(self,label='send it!') + self.button.Bind(wx.EVT_BUTTON,fab_send) + self.button.SetFont(bold_font) + self.sizer.Add(self.button,(1,0),span=(1,2),flag=(wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL)) + self.button.Hide() + # + # controls + # + make = wx.Button(self,label='make .oms') + make.Bind(wx.EVT_BUTTON,make_oms) + self.sizer.Add(make,(2,0),span=(1,2),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + row3_panel = wx.Panel(self) + row3_sizer = wx.GridBagSizer(10,10) + row3_panel.SetSizer(row3_sizer) + row3_sizer.Add(wx.StaticText(row3_panel,label='velocity'),(0,0),flag=(wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT)) + self.sizer.Add(row3_panel,(3,0),span=(1,2),flag=(wx.ALIGN_CENTER_HORIZONTAL)) + # + row4_panel = wx.Panel(self) + row4_sizer = wx.GridBagSizer(10,10) + row4_panel.SetSizer(row4_sizer) + self.velocity = wx.TextCtrl(row4_panel,-1,'0.1') + row4_sizer.Add(self.velocity,(0,0)) + self.sizer.Add(row4_panel,(4,0),span=(1,2),flag=(wx.ALIGN_CENTER_HORIZONTAL)) + # + row5_panel = wx.Panel(self) + row5_sizer = wx.GridBagSizer(10,10) + row5_panel.SetSizer(row5_sizer) + row5_sizer.Add(wx.StaticText(row5_panel,label='acceleration'),(0,0),flag=(wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT)) + self.sizer.Add(row5_panel,(5,0),span=(1,2),flag=(wx.ALIGN_CENTER_HORIZONTAL)) + # + row6_panel = wx.Panel(self) + row6_sizer = wx.GridBagSizer(10,10) + row6_panel.SetSizer(row6_sizer) + self.accel = wx.TextCtrl(row6_panel,-1,'5.0') + row6_sizer.Add(self.accel,(0,0)) + self.sizer.Add(row6_panel,(6,0),span=(1,2),flag=(wx.ALIGN_CENTER_HORIZONTAL)) + # + row7_panel = wx.Panel(self) + row7_sizer = wx.GridBagSizer(10,10) + row7_panel.SetSizer(row7_sizer) + row7_sizer.Add(wx.StaticText(row7_panel,label='period'),(0,0),flag=(wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT)) + self.sizer.Add(row7_panel,(7,0),span=(1,2),flag=(wx.ALIGN_CENTER_HORIZONTAL)) + # + row8_panel = wx.Panel(self) + row8_sizer = wx.GridBagSizer(10,10) + row8_panel.SetSizer(row8_sizer) + self.period = wx.TextCtrl(row8_panel,-1,'10000') + row8_sizer.Add(self.period,(0,0)) + self.sizer.Add(row8_panel,(8,0),span=(1,2),flag=(wx.ALIGN_CENTER_HORIZONTAL)) diff --git a/src/py/panel_path_ord.py b/src/py/panel_path_ord.py new file mode 100644 index 0000000..f3b6fa8 --- /dev/null +++ b/src/py/panel_path_ord.py @@ -0,0 +1,91 @@ +# +# panel_path_ord.py +# make .ord from .path +# +# Neil Gershenfeld +# CBA MIT 2/26/11 +# +# (c) Massachusetts Institute of Technology 2011 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,os +# +# panel class +# +class path_ord_panel(wx.Panel): + def __init__(self,parent): + self.parent = parent + self.parent.path_file = '' + # + # make file + # + def make_file(event): + if (self.parent.path_file == ''): + print 'panel_path_ord: oops -- need path file' + return + self.parent.ord_file = self.parent.tmp+self.parent.rootname+'.ord' + lead = self.lead.GetValue() + quality = self.quality.GetValue() + command = 'path_ord '+'\"'+self.parent.path_file+'\"'+' '+'\"'+self.parent.ord_file+'\"'+' '+lead+' '+quality + print command + os.system(command) + self.button.Show() + self.parent.Layout() + self.parent.Fit() + # + # send + # + def fab_send(event): + command = 'fab_send '+'\"'+self.parent.ord_file+'\"' + print command + os.system(command) + # + # panel + # + wx.Panel.__init__(self,parent) + self.sizer = wx.GridBagSizer(10,10) + self.SetSizer(self.sizer) + # + # label + # + label = wx.StaticText(self,label='to: ord') + bold_font = wx.Font(10,wx.DEFAULT,wx.NORMAL,wx.BOLD) + label.SetFont(bold_font) + self.sizer.Add(label,(0,0),span=(1,2),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + # send + # + self.button = wx.Button(self,label='send it!') + self.button.Bind(wx.EVT_BUTTON,fab_send) + self.button.SetFont(bold_font) + self.sizer.Add(self.button,(1,0),span=(1,2),flag=(wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL)) + self.button.Hide() + # + # controls + # + make = wx.Button(self,label='make .ord') + make.Bind(wx.EVT_BUTTON,make_file) + self.sizer.Add(make,(2,0),span=(1,2),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + row3_panel = wx.Panel(self) + row3_sizer = wx.GridBagSizer(10,10) + row3_panel.SetSizer(row3_sizer) + row3_sizer.Add(wx.StaticText(row3_panel,label='lead in/out (mm)'),(0,0),flag=(wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT)) + row3_sizer.Add(wx.StaticText(row3_panel,label='cut quality '),(0,1),flag=(wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT)) + self.sizer.Add(row3_panel,(3,0),span=(1,2),flag=(wx.ALIGN_CENTER_HORIZONTAL)) + # + row4_panel = wx.Panel(self) + row4_sizer = wx.GridBagSizer(10,10) + row4_panel.SetSizer(row4_sizer) + self.lead= wx.TextCtrl(row4_panel,-1,'2') + row4_sizer.Add(self.lead,(0,0)) + self.quality= wx.TextCtrl(row4_panel,-1,'-3') + row4_sizer.Add(self.quality,(0,1)) + self.sizer.Add(row4_panel,(4,0),span=(1,2),flag=(wx.ALIGN_CENTER_HORIZONTAL)) + # + # fit + # + self.Fit() diff --git a/src/py/panel_path_plt.py b/src/py/panel_path_plt.py new file mode 100755 index 0000000..30a374e --- /dev/null +++ b/src/py/panel_path_plt.py @@ -0,0 +1,121 @@ +# +# panel_path_plt.py +# make .plt from .path +# +# by Kenny Cheung CBA MIT 21Aug2012 +# from panel_path_plt.py Neil Gershenfeld CBA MIT 19Feb2011 +# +# imports +# +import wx,os +# +# panel class +# +class path_plt_panel(wx.Panel): + def __init__(self,parent): + self.parent = parent + self.parent.path_file = '' + # + # make file + # + def make_file(event): + if (self.parent.path_file == ''): + print 'panel_path_plt: oops -- need path file' + return + self.parent.plt_file = self.parent.tmp+self.parent.rootname+'.plt' + if (self.conv.GetValue()): + direction = '0' + else: + direction = '1' + self.parent.plt_file = self.parent.tmp+self.parent.rootname+'.plt' + spindle = self.spindle.GetValue() + speed = self.speed.GetValue() + jog = self.jog.GetValue() + height = self.height.GetValue() + command = 'path_plt '+'\"'+self.parent.path_file+'\"'+' '+'\"'+self.parent.plt_file+'\"'+' '+direction+' '+spindle+' '+speed+' '+speed+' '+jog+' '+jog+' '+height + print command + os.system(command) + self.button.SetMaxSize((self.parent.xsize,self.parent.ysize)) + self.button.SetMinSize((self.parent.xsize,self.parent.ysize)) + self.button.Show() + self.parent.Layout() + self.parent.Fit() + # + # send + # + def fab_send(event): + command = 'fab_send '+'\"'+self.parent.plt_file+'\"' + print command + os.system(command) + # + # panel + # + wx.Panel.__init__(self,parent) + self.sizer = wx.GridBagSizer(10,10) + self.SetSizer(self.sizer) + # + # label + # + label = wx.StaticText(self,label='to: plt') + bold_font = wx.Font(10,wx.DEFAULT,wx.NORMAL,wx.BOLD) + label.SetFont(bold_font) + self.sizer.Add(label,(0,0),span=(1,2),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + # send + # + self.button = wx.Button(self,label='send it!') + self.button.Bind(wx.EVT_BUTTON,fab_send) + self.button.SetFont(bold_font) + self.sizer.Add(self.button,(1,0),span=(1,2),flag=(wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL)) + self.button.Hide() + # + # controls + # + make = wx.Button(self,label='make .plt') + make.Bind(wx.EVT_BUTTON,make_file) + self.sizer.Add(make,(2,0),span=(1,2),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + dir_panel = wx.Panel(self) + dir_sizer = wx.GridBagSizer(10,10) + dir_panel.SetSizer(dir_sizer) + self.conv = wx.RadioButton(dir_panel,label='conventional',style=wx.RB_GROUP) + dir_sizer.Add(self.conv,(0,0)) + self.climb = wx.RadioButton(dir_panel,label='climb') + dir_sizer.Add(self.climb,(0,1)) + self.sizer.Add(dir_panel,(3,0),span=(1,2),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + row4_panel = wx.Panel(self) + row4_sizer = wx.GridBagSizer(10,10) + row4_panel.SetSizer(row4_sizer) + row4_sizer.Add(wx.StaticText(row4_panel,label='cut speed (mm/s)'),(0,0),flag=(wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT)) + row4_sizer.Add(wx.StaticText(row4_panel,label='jog speed (mm/s)'),(0,1),flag=(wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT)) + self.sizer.Add(row4_panel,(4,0),span=(1,2),flag=(wx.ALIGN_CENTER_HORIZONTAL)) + # + row5_panel = wx.Panel(self) + row5_sizer = wx.GridBagSizer(10,10) + row5_panel.SetSizer(row5_sizer) + self.speed = wx.TextCtrl(row5_panel,-1,'15') + row5_sizer.Add(self.speed,(0,0)) + self.jog = wx.TextCtrl(row5_panel,-1,'75') + row5_sizer.Add(self.jog,(0,1)) + self.sizer.Add(row5_panel,(5,0),span=(1,2),flag=(wx.ALIGN_CENTER_HORIZONTAL)) + # + row6_panel = wx.Panel(self) + row6_sizer = wx.GridBagSizer(10,10) + row6_panel.SetSizer(row6_sizer) + row6_sizer.Add(wx.StaticText(row6_panel,label='spindle speed (RPM)'),(0,0),flag=(wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT)) + row6_sizer.Add(wx.StaticText(row6_panel,label='jog height (mm) '),(0,1),flag=(wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT)) + self.sizer.Add(row6_panel,(6,0),span=(1,2),flag=(wx.ALIGN_CENTER_HORIZONTAL)) + # + row7_panel = wx.Panel(self) + row7_sizer = wx.GridBagSizer(10,10) + row7_panel.SetSizer(row7_sizer) + self.spindle = wx.TextCtrl(row7_panel,-1,'10000') + row7_sizer.Add(self.spindle,(0,0)) + self.height = wx.TextCtrl(row7_panel,-1,'5') + row7_sizer.Add(self.height,(0,1)) + self.sizer.Add(row7_panel,(7,0),span=(1,2),flag=(wx.ALIGN_CENTER_HORIZONTAL)) + # + # fit + # + self.Fit() diff --git a/src/py/panel_path_rml.py b/src/py/panel_path_rml.py new file mode 100644 index 0000000..8dbd4f0 --- /dev/null +++ b/src/py/panel_path_rml.py @@ -0,0 +1,140 @@ +# +# panel_path_rml.py +# make .rml from .path +# +# Neil Gershenfeld +# CBA MIT 2/19/11 +# +# (c) Massachusetts Institute of Technology 2011 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,os,string +# +# panel class +# +class path_rml_panel(wx.Panel): + def __init__(self,parent): + self.parent = parent + self.parent.path_file = '' + # + # make rml + # + def make_rml(event): + if (self.parent.path_file == ''): + print 'panel_path_rml: oops -- need path file' + return + self.parent.rml_file = self.parent.tmp+self.parent.rootname+'.rml' + speed = self.speed.GetValue() + direction = '1' + zjog = self.zjog.GetValue() + xmin = self.xmin.GetValue() + ymin = self.ymin.GetValue() + command = 'path_rml '+'\"'+self.parent.path_file+'\"'+' '+'\"'+self.parent.rml_file+'\"'+' '+speed+' '+direction+' '+zjog+' '+xmin+' '+ymin + print command + os.system(command) + temp_name = self.parent.tmp+'path_info' + command = 'path_time '+'\"'+self.parent.path_file+'\"'+' '+speed+' '+zjog+' > '+'\"'+temp_name+'\"' + print command + os.system(command) + output_file = open(temp_name,'r') + output = output_file.read() + output_file.close() + command = 'rm '+'\"'+temp_name+'\"' + start = 11+string.find(output,'path time: ') + end = 5+string.find(output,'hours',start) + times = output[start:end] + self.info.SetLabel(times) + self.button.Show() + self.parent.Layout() + self.parent.Fit() + # + # rml move + # + def move_rml(event): + xmin = self.xmin.GetValue() + ymin = self.ymin.GetValue() + command = 'rml_move '+xmin+' '+ymin + print command + os.system(command) + # + # send + # + def fab_send(event): + command = 'fab_send '+'\"'+self.parent.rml_file+'\"' + print command + os.system(command) + # + # panel + # + wx.Panel.__init__(self,parent) + self.sizer = wx.GridBagSizer(10,10) + self.SetSizer(self.sizer) + # + # label + # + label = wx.StaticText(self,label='to: rml') + bold_font = wx.Font(10,wx.DEFAULT,wx.NORMAL,wx.BOLD) + label.SetFont(bold_font) + self.sizer.Add(label,(0,0),span=(1,2),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + # send + # + self.button = wx.Button(self,label='send it!') + self.button.Bind(wx.EVT_BUTTON,fab_send) + self.button.SetFont(bold_font) + self.sizer.Add(self.button,(1,0),span=(1,2),flag=(wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL)) + self.button.Hide() + # + # info + # + self.info = wx.StaticText(self,label="") + self.sizer.Add(self.info,(2,0),span=(1,2),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + # controls + # + make = wx.Button(self,label='make .rml') + make.Bind(wx.EVT_BUTTON,make_rml) + self.sizer.Add(make,(3,0),span=(1,2),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + row4_panel = wx.Panel(self) + row4_sizer = wx.GridBagSizer(10,10) + row4_panel.SetSizer(row4_sizer) + row4_sizer.Add(wx.StaticText(row4_panel,label='speed (mm/s)'),(0,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + row4_sizer.Add(wx.StaticText(row4_panel,label='jog (mm) '),(0,1),flag=wx.ALIGN_CENTER_HORIZONTAL) + self.sizer.Add(row4_panel,(4,0),span=(1,2),flag=(wx.ALIGN_CENTER_HORIZONTAL)) + # + row5_panel = wx.Panel(self) + row5_sizer = wx.GridBagSizer(10,10) + row5_panel.SetSizer(row5_sizer) + self.speed = wx.TextCtrl(row5_panel,-1,'4') + row5_sizer.Add(self.speed,(0,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + self.zjog = wx.TextCtrl(row5_panel,-1,'1') + row5_sizer.Add(self.zjog,(0,1),flag=wx.ALIGN_CENTER_HORIZONTAL) + self.sizer.Add(row5_panel,(5,0),span=(1,2),flag=(wx.ALIGN_CENTER_HORIZONTAL)) + # + row6_panel = wx.Panel(self) + row6_sizer = wx.GridBagSizer(10,10) + row6_panel.SetSizer(row6_sizer) + row6_sizer.Add(wx.StaticText(row6_panel,label='xmin (mm)'),(0,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + row6_sizer.Add(wx.StaticText(row6_panel,label='ymin (mm)'),(0,1),flag=wx.ALIGN_CENTER_HORIZONTAL) + self.sizer.Add(row6_panel,(6,0),span=(1,2),flag=(wx.ALIGN_CENTER_HORIZONTAL)) + # + row7_panel = wx.Panel(self) + row7_sizer = wx.GridBagSizer(10,10) + row7_panel.SetSizer(row7_sizer) + self.xmin = wx.TextCtrl(row7_panel,-1,'20') + row7_sizer.Add(self.xmin,(0,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + self.ymin = wx.TextCtrl(row7_panel,-1,'20') + row7_sizer.Add(self.ymin,(0,1),flag=wx.ALIGN_CENTER_HORIZONTAL) + self.sizer.Add(row7_panel,(7,0),span=(1,2),flag=(wx.ALIGN_CENTER_HORIZONTAL)) + # + move = wx.Button(self,label='move to xmin,ymin') + move.Bind(wx.EVT_BUTTON,move_rml) + self.sizer.Add(move,(8,0),span=(1,2),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + # fit + # + self.Fit() diff --git a/src/py/panel_path_sbp.py b/src/py/panel_path_sbp.py new file mode 100644 index 0000000..3ce34bf --- /dev/null +++ b/src/py/panel_path_sbp.py @@ -0,0 +1,154 @@ +# +# panel_path_sbp.py +# make .sbp from .path +# +# Neil Gershenfeld +# CBA MIT 3/23/13 +# +# (c) Massachusetts Institute of Technology 2011 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,os,string +# +# panel class +# +class path_sbp_panel(wx.Panel): + def __init__(self,parent): + self.parent = parent + self.parent.path_file = '' + # + # make file + # + def make_file(event): + if (self.parent.path_file == ''): + print 'panel_path_sbp: oops -- need path file' + return + self.parent.sbp_file = self.parent.tmp+self.parent.rootname+'.sbp' + if (self.conv.GetValue()): + direction = '0' + else: + direction = '1' + if (self.inches.GetValue()): + units = '25.4' + else: + units = '1' + self.parent.sbp_file = self.parent.tmp+self.parent.rootname+'.sbp' + spindle = self.spindle.GetValue() + speed = self.speed.GetValue() + jog = self.jog.GetValue() + height = self.height.GetValue() + command = 'path_sbp '+'\"'+self.parent.path_file+'\"'+' '+'\"'+self.parent.sbp_file+'\"'+' '+direction+' '+spindle+' '+speed+' '+speed+' '+jog+' '+jog+' '+height+' '+units + print command + os.system(command) + temp_name = self.parent.tmp+'path_info' + command = 'path_time '+'\"'+self.parent.path_file+'\"'+' '+speed+' '+height+' '+jog+' > '+'\"'+temp_name+'\"' + print command + os.system(command) + output_file = open(temp_name,'r') + output = output_file.read() + output_file.close() + command = 'rm '+'\"'+temp_name+'\"' + start = 11+string.find(output,'path time: ') + end = 5+string.find(output,'hours',start) + times = output[start:end] + self.info.SetLabel(times) + self.button.Show() + self.parent.Layout() + self.parent.Fit() + # + # send + # + def fab_send(event): + command = 'fab_send '+'\"'+self.parent.sbp_file+'\"' + print command + os.system(command) + # + # panel + # + wx.Panel.__init__(self,parent) + self.sizer = wx.GridBagSizer(10,10) + self.SetSizer(self.sizer) + # + # label + # + label = wx.StaticText(self,label='to: sbp') + bold_font = wx.Font(10,wx.DEFAULT,wx.NORMAL,wx.BOLD) + label.SetFont(bold_font) + self.sizer.Add(label,(0,0),span=(1,2),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + # send + # + self.button = wx.Button(self,label='send it!') + self.button.Bind(wx.EVT_BUTTON,fab_send) + self.button.SetFont(bold_font) + self.sizer.Add(self.button,(1,0),span=(1,2),flag=(wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL)) + self.button.Hide() + # + # info + # + self.info = wx.StaticText(self,label="") + self.sizer.Add(self.info,(2,0),span=(1,2),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + # controls + # + make = wx.Button(self,label='make .sbp') + make.Bind(wx.EVT_BUTTON,make_file) + self.sizer.Add(make,(3,0),span=(1,2),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + units_panel = wx.Panel(self) + units_sizer = wx.GridBagSizer(10,10) + units_panel.SetSizer(units_sizer) + units_sizer.Add(wx.StaticText(units_panel,label='file units:'),(0,0),flag=(wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT)) + self.inches = wx.RadioButton(units_panel,label='inches',style=wx.RB_GROUP) + units_sizer.Add(self.inches,(0,1)) + self.mm = wx.RadioButton(units_panel,label='mm') + units_sizer.Add(self.mm,(0,2)) + self.sizer.Add(units_panel,(4,0),span=(1,2),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + dir_panel = wx.Panel(self) + dir_sizer = wx.GridBagSizer(10,10) + dir_panel.SetSizer(dir_sizer) + self.conv = wx.RadioButton(dir_panel,label='conventional',style=wx.RB_GROUP) + dir_sizer.Add(self.conv,(0,0)) + self.climb = wx.RadioButton(dir_panel,label='climb') + dir_sizer.Add(self.climb,(0,1)) + self.sizer.Add(dir_panel,(5,0),span=(1,2),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + row6_panel = wx.Panel(self) + row6_sizer = wx.GridBagSizer(10,10) + row6_panel.SetSizer(row6_sizer) + row6_sizer.Add(wx.StaticText(row6_panel,label='cut speed (mm/s)'),(0,0),flag=(wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT)) + row6_sizer.Add(wx.StaticText(row6_panel,label='jog speed (mm/s)'),(0,1),flag=(wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT)) + self.sizer.Add(row6_panel,(6,0),span=(1,2),flag=(wx.ALIGN_CENTER_HORIZONTAL)) + # + row7_panel = wx.Panel(self) + row7_sizer = wx.GridBagSizer(10,10) + row7_panel.SetSizer(row7_sizer) + self.speed = wx.TextCtrl(row7_panel,-1,'15') + row7_sizer.Add(self.speed,(0,0)) + self.jog = wx.TextCtrl(row7_panel,-1,'75') + row7_sizer.Add(self.jog,(0,1)) + self.sizer.Add(row7_panel,(7,0),span=(1,2),flag=(wx.ALIGN_CENTER_HORIZONTAL)) + # + row8_panel = wx.Panel(self) + row8_sizer = wx.GridBagSizer(10,10) + row8_panel.SetSizer(row8_sizer) + row8_sizer.Add(wx.StaticText(row8_panel,label='spindle speed (RPM)'),(0,0),flag=(wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT)) + row8_sizer.Add(wx.StaticText(row8_panel,label='jog height (mm) '),(0,1),flag=(wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT)) + self.sizer.Add(row8_panel,(8,0),span=(1,2),flag=(wx.ALIGN_CENTER_HORIZONTAL)) + # + row9_panel = wx.Panel(self) + row9_sizer = wx.GridBagSizer(10,10) + row9_panel.SetSizer(row9_sizer) + self.spindle = wx.TextCtrl(row9_panel,-1,'10000') + row9_sizer.Add(self.spindle,(0,0)) + self.height = wx.TextCtrl(row9_panel,-1,'5') + row9_sizer.Add(self.height,(0,1)) + self.sizer.Add(row9_panel,(9,0),span=(1,2),flag=(wx.ALIGN_CENTER_HORIZONTAL)) + # + # fit + # + self.Fit() diff --git a/src/py/panel_path_snap.py b/src/py/panel_path_snap.py new file mode 100644 index 0000000..181fc7b --- /dev/null +++ b/src/py/panel_path_snap.py @@ -0,0 +1,633 @@ +# +# panel_path_snap.py +# MTM Snap virtual machine +# +# Neil Gershenfeld +# CBA MIT 12/13/11 +# +# (c) Massachusetts Institute of Technology 2011 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# todo +# process text entry +# acceleration +# job times +# +# imports +# +import wx,os,serial,time +from math import * +# +# machine definitions +# +mm_per_xstep = 0.00635 # stepper step size (400 steps/rev, 10 revs/in) +mm_per_ystep = 0.00635 # stepper step size '' +mm_per_zstep = 0.00635 # stepper step size '' +mm_per_jog_step = 0.1 # jog step size +char_delay = 0.001 # delay between command chars in seconds +# +# global variables +# +xstep = ystep = zstep = 0 # stepper position (step units) +spindle_state = 0 # on/off +move_state = 0 # on/off +# +# panel class +# +class path_snap_panel(wx.Panel): + def __init__(self,parent): + self.parent = parent + self.parent.path_file = '' + # + # start/stop spindle + # + def spindle(event): + global spindle_state + spindle_state = 1 - spindle_state + if (spindle_state == 1): + spindle_button.SetLabel('OFF') + else: + spindle_button.SetLabel('on') + # + # start/stop job + # + def start(event,start_line=5): + global move_state,ser + move_state = 1 - move_state + if (move_state == 1): + if (self.parent.path_file == ''): + print "panel_path_snap: oops -- need path file" + move_state = 0 + return + start_button.SetLabel('STOP') + pause_button.SetLabel('pause') + ser = serial.Serial(port.GetValue(),115200) + ser.setDTR() + start = time.time() + send_job(start_line) + end = time.time() + ser.close() + nsegment = float(segment.GetValue()) + print "%f sec, %f sec/segment"%((end-start),(end-start)/nsegment) + else: + start_button.SetLabel('start') + # + # send job + # + def send_job(start_line): + global move_state + global nx,ny,nz,dx,dy,dz,x0,y0,z0 + # + # read path + # + path_file = open(self.parent.path_file,'r') + lines = path_file.readlines() + segment_label.SetLabel("/"+str(len(lines)-1)) + dof = int(lines[0].split()[0]) + if (dof != 3): + print "panel_path_snap: oops -- need a 3D path" + return + units = lines[1] # currently ignored, mm assumed + nx = int(lines[2].split()[0]) + ny = int(lines[2].split()[1]) + nz = int(lines[2].split()[2]) + dx = float(lines[3].split()[0]) + dy = float(lines[3].split()[1]) + dz = float(lines[3].split()[2]) + x0 = float(lines[4].split()[0]) + y0 = float(lines[4].split()[1]) + z0 = float(lines[4].split()[2]) + path_file.close() + # + # get GUI values + # + speed_move = float(move_speed.GetValue()) + speed_jog = float(jog_speed.GetValue()) + height_jog = float(jog_height.GetValue()) + # + # loop over path + # + (xold,yold,zold) = ([],[],[]) + for i in range(start_line,len(lines)): + # + # check for GUI events + # + wx.Yield() + # + # quit if stopped + # + if (move_state == 0): + return + # + # read next line + # + line = lines[i].split() + # + # start of new segment? + # + if (line[0] == '.'): + # + # yes, clear old point and continue + # + (xold,yold,zold) = ([],[],[]) + continue + # + # no, go to next point + # + x = int(line[0]) + y = int(line[1]) + z = int(line[2]) + # + # first point in segment? + # + if (xold == []): + # + # yes, jog there and move down + # + move_z_abs_mm(height_jog,speed_jog) + move_xy_abs_path(x,y,speed_jog) + move_z_abs_path(z,speed_move) + else: + # + # no, move there + # + move_xyz_abs_path(x,y,z,speed_move) + # + # save old point + # + (xold,yold,zold) = (x,y,z) + # + # update segment counter and continue + # + segment.SetValue(str(i)) + # + # finish + # + move_state = 0 + start_button.SetLabel('start') + segment_label.SetLabel("segment") + # + # pause + # + def pause(event): + global move_state + print "pause" + if (move_state == 1): + move_state = 0 + start_button.SetLabel("start") + pause_button.SetLabel("CONTINUE") + elif (pause_button.GetLabel() == "CONTINUE"): + start(0,int(segment.GetValue())) + # + # write command to bus and read acknowledgement + # + def bus_send(command): + global ser + for i in range(len(command)): + print "%d: %c = %d"%(i,command[i],ord(command[i])) + ser.write(command[i]) + time.sleep(char_delay) + ack = ser.read() + # + # send z, stepper step units, ms time units + # + def send_z(dz,dt): + command = "" + if (dz >= 0): + zcmd = 'Z' + else: + zcmd = 'z' + dz1 = (abs(dz) >> 8) & 255 + dz0 = abs(dz) & 255 + command += zcmd+chr(2)+chr(dz1)+chr(dz0) + t1 = (abs(dt) >> 8) & 255 + t0 = abs(dt) & 255 + command += 'T'+chr(2)+chr(t1)+chr(t0) + command += 'G' + bus_send(command) + # + # send xy, stepper step units, ms time units + # + def send_xy(dx,dy,dt): + command = "" + if (dx >= 0): + xcmd = 'X' + else: + xcmd = 'x' + dx1 = (abs(dx) >> 8) & 255 + dx0 = abs(dx) & 255 + command += xcmd+chr(2)+chr(dx1)+chr(dx0) + if (dy >= 0): + ycmd = 'Y' + else: + ycmd = 'y' + dy1 = (abs(dy) >> 8) & 255 + dy0 = abs(dy) & 255 + command += ycmd+chr(2)+chr(dy1)+chr(dy0) + t1 = (abs(dt) >> 8) & 255 + t0 = abs(dt) & 255 + command += 'T'+chr(2)+chr(t1)+chr(t0) + command += 'G' + bus_send(command) + # + # send xyz, stepper step units, ms time units + # + def send_xyz(dx,dy,dz,dt): + command = "" + if (dx >= 0): + xcmd = 'X' + else: + xcmd = 'x' + dx1 = (abs(dx) >> 8) & 255 + dx0 = abs(dx) & 255 + command += xcmd+chr(2)+chr(dx1)+chr(dx0) + if (dy >= 0): + ycmd = 'Y' + else: + ycmd = 'y' + dy1 = (abs(dy) >> 8) & 255 + dy0 = abs(dy) & 255 + command += ycmd+chr(2)+chr(dy1)+chr(dy0) + if (dz >= 0): + zcmd = 'Z' + else: + zcmd = 'z' + dz1 = (abs(dz) >> 8) & 255 + dz0 = abs(dz) & 255 + command += zcmd+chr(2)+chr(dz1)+chr(dz0) + t1 = (abs(dt) >> 8) & 255 + t0 = abs(dt) & 255 + command += 'T'+chr(2)+chr(t1)+chr(t0) + command += 'G' + bus_send(command) + # + # move xy absolute, path units + # + def move_xy_abs_path(xi,yi,speed): + global xstep,ystep + global mm_per_xstep,mm_per_ystep + global nx,ny,nz,dx,dy,dz,x0,y0 + if (nx != 1): + x = x0 + dx*xi/(nx-1.0) + else: + x = x0 + if (ny != 1): + y = y0 + dy*yi/(ny-1.0) + else: + y = y0 + xpos.SetValue("%.3f"%x) + ypos.SetValue("%.3f"%y) + deltax = x - xstep*mm_per_xstep + deltay = y - ystep*mm_per_ystep + dxstep = int(deltax/mm_per_xstep) + xstep += dxstep + dystep = int(deltay/mm_per_ystep) + ystep += dystep + distance = sqrt(deltax*deltax+deltay*deltay) + dt = int(1000* distance / speed) + send_xy(dxstep,dystep,dt) + # + # move xyz absolute, path units + # + def move_xyz_abs_path(xi,yi,zi,speed): + global xstep,ystep,zstep + global mm_per_xstep,mm_per_ystep,mm_per_zstep + global nx,ny,nz,dx,dy,dz,x0,y0,z0 + if (nx != 1): + x = x0 + dx*xi/(nx-1.0) + else: + x = x0 + if (ny != 1): + y = y0 + dy*yi/(ny-1.0) + else: + y = y0 + if (nz != 1): + z = z0 + dz*zi/(nz-1.0) + else: + z = z0 + xpos.SetValue("%.3f"%x) + ypos.SetValue("%.3f"%y) + zpos.SetValue("%.3f"%z) + deltax = x - xstep*mm_per_xstep + deltay = y - ystep*mm_per_ystep + deltaz = z - zstep*mm_per_zstep + dxstep = int(deltax/mm_per_xstep) + xstep += dxstep + dystep = int(deltay/mm_per_ystep) + ystep += dystep + dzstep = int(deltaz/mm_per_zstep) + zstep += dzstep + distance = sqrt(deltax*deltax+deltay*deltay+deltaz*deltaz) + dt = int(1000* distance / speed) + send_xyz(dxstep,dystep,dzstep,dt) + # + # move z absolute, path units + # + def move_z_abs_path(z,speed): + global zstep + global mm_per_zstep + global nz,dz,z0 + if (nz != 1): + z = z0 + dz*zi/(nz-1.0) + else: + z = z0 + zpos.SetValue("%.3f"%z) + deltaz = z - zstep*mm_per_zstep + dzstep = int(deltaz/mm_per_zstep) + zstep += dzstep + distance = sqrt(deltaz*deltaz) + dt = int(1000* distance / speed) + send_z(dzstep,dt) + # + # move z absolute, mm units + # + def move_z_abs_mm(z,speed): + global zstep + global mm_per_zstep + zpos.SetValue("%.3f"%z) + deltaz = z - zstep*mm_per_zstep + dzstep = int(deltaz/mm_per_zstep) + zstep += dzstep + distance = sqrt(deltaz*deltaz) + dt = int(1000* distance / speed) + send_z(dzstep,dt) + # + # move xyz relative, mm units + # + def move_xyz_rel_mm(dx,dy,dz,speed): + global xstep,ystep,zstep + global mm_per_xstep,mm_per_ystep,mm_per_zstep + dxstep = int(dx / mm_per_xstep) + xstep += dxstep + xpos.SetValue("%.3f"%(xstep*mm_per_xstep)) + dystep = int(dy / mm_per_xstep) + ystep += dystep + ypos.SetValue("%.3f"%(ystep*mm_per_xstep)) + dzstep = int(dz / mm_per_xstep) + zstep += dzstep + zpos.SetValue("%.3f"%(zstep*mm_per_xstep)) + distance = sqrt(dx*dx+dy*dy+dz*dz) + dt = int(1000* distance / speed) + send_xyz(dxstep,dystep,dzstep,dt) + # + # move left mouse down + # + def left_down(event): + global move_state + move_state = 1 + left_button.SetLabel('LEFT') + while 1: + wx.Yield() + if (move_state == 0): + return + move_xyz_rel_mm(-mm_per_jog_step,0,0,float(jog_speed.GetValue())) + # + # move left mouse up + # + def left_up(event): + global move_state + move_state = 0 + left_button.SetLabel('left') + # + # move right mouse down + # + def right_down(event): + global move_state + move_state = 1 + right_button.SetLabel('RIGHT') + while 1: + wx.Yield() + if (move_state == 0): + return + move_xyz_rel_mm(mm_per_jog_step,0,0,float(jog_speed.GetValue())) + # + # move right mouse up + # + def right_up(event): + global move_state + move_state = 0 + right_button.SetLabel('right') + # + # move forward mouse down + # + def forward_down(event): + global move_state + move_state = 1 + forward_button.SetLabel('FORWARD') + while 1: + wx.Yield() + if (move_state == 0): + return + move_xyz_rel_mm(0,-mm_per_jog_step,0,float(jog_speed.GetValue())) + # + # move forward mouse up + # + def forward_up(event): + global move_state + move_state = 0 + forward_button.SetLabel('forward') + # + # move back mouse down + # + def back_down(event): + global move_state + move_state = 1 + back_button.SetLabel('BACK') + while 1: + wx.Yield() + if (move_state == 0): + return + move_xyz_rel_mm(0,mm_per_jog_step,0,float(jog_speed.GetValue())) + # + # move back mouse up + # + def back_up(event): + global move_state + move_state = 0 + back_button.SetLabel('back') + # + # move up mouse down + # + def up_down(event): + global move_state + move_state = 1 + up_button.SetLabel('UP') + while 1: + wx.Yield() + if (move_state == 0): + return + move_xyz_rel_mm(0,0,mm_per_jog_step,float(jog_speed.GetValue())) + # + # move up mouse up + # + def up_up(event): + global move_state + move_state = 0 + up_button.SetLabel('up') + # + # move down mouse down + # + def down_down(event): + global move_state + move_state = 1 + down_button.SetLabel('DOWN') + while 1: + wx.Yield() + if (move_state == 0): + return + move_xyz_rel_mm(0,0,-mm_per_jog_step,float(jog_speed.GetValue())) + # + # move down mouse up + # + def down_up(event): + global move_state + move_state = 0 + down_button.SetLabel('down') + # + # zero x + # + def zero_x(event): + global xstep + xstep = 0 + xpos.SetValue("0.000") + # + # zero y + # + def zero_y(event): + global ystep + ystep = 0 + ypos.SetValue("0.000") + # + # zero z + # + def zero_z(event): + global zstep + zstep = 0 + zpos.SetValue("0.000") + # + # zero xyz + # + def zero_xyz(event): + global xstep,ystep,zstep + xstep = 0 + xpos.SetValue("0.000") + ystep = 0 + ypos.SetValue("0.000") + zstep = 0 + zpos.SetValue("0.000") + # + # home + # + def home(event): + speed_jog = float(jog_speed.GetValue()) + height_jog = float(jog_height.GetValue()) + move_z_abs_mm(height_jog,speed_jog) + move_xy_abs_path(0,0,speed_jog) + # + # panel + # + wx.Panel.__init__(self,parent) + self.sizer = wx.GridBagSizer(10,10) + self.SetSizer(self.sizer) + # + # label + # + label = wx.StaticText(self,label='to: MTM Snap') + bold_font = wx.Font(10,wx.DEFAULT,wx.NORMAL,wx.BOLD) + label.SetFont(bold_font) + self.sizer.Add(label,(0,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + # controls + # + mtm_panel = wx.Panel(self) + mtm_sizer = wx.GridBagSizer(10,10) + mtm_panel.SetSizer(mtm_sizer) + # + mtm_sizer.Add(wx.StaticText(mtm_panel,label='x y z (mm)'),(0,0),flag=wx.ALIGN_RIGHT) + xpos = wx.TextCtrl(mtm_panel,-1,'0.000') + mtm_sizer.Add(xpos,(0,1),flag=wx.ALIGN_CENTER_HORIZONTAL) + ypos = wx.TextCtrl(mtm_panel,-1,'0.000') + mtm_sizer.Add(ypos,(0,2),flag=wx.ALIGN_CENTER_HORIZONTAL) + zpos = wx.TextCtrl(mtm_panel,-1,'0.000') + mtm_sizer.Add(zpos,(0,3),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + zero_xyz_button = wx.Button(mtm_panel,label='zero xyz') + zero_xyz_button.Bind(wx.EVT_BUTTON,zero_xyz) + mtm_sizer.Add(zero_xyz_button,(1,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + zero_x_button = wx.Button(mtm_panel,label='zero x') + zero_x_button.Bind(wx.EVT_BUTTON,zero_x) + mtm_sizer.Add(zero_x_button,(1,1),flag=wx.ALIGN_CENTER_HORIZONTAL) + zero_y_button = wx.Button(mtm_panel,label='zero y') + zero_y_button.Bind(wx.EVT_BUTTON,zero_y) + mtm_sizer.Add(zero_y_button,(1,2),flag=wx.ALIGN_CENTER_HORIZONTAL) + zero_z_button = wx.Button(mtm_panel,label='zero z') + zero_z_button.Bind(wx.EVT_BUTTON,zero_z) + mtm_sizer.Add(zero_z_button,(1,3),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + home_button = wx.Button(mtm_panel,label='home') + home_button.Bind(wx.EVT_BUTTON,home) + mtm_sizer.Add(home_button,(2,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + left_button = wx.Button(mtm_panel,label='left') + left_button.Bind(wx.EVT_LEFT_DOWN,left_down) + left_button.Bind(wx.EVT_LEFT_UP,left_up) + mtm_sizer.Add(left_button,(3,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + back_button = wx.Button(mtm_panel,label='back') + back_button.Bind(wx.EVT_LEFT_DOWN,back_down) + back_button.Bind(wx.EVT_LEFT_UP,back_up) + mtm_sizer.Add(back_button,(2,1),flag=wx.ALIGN_CENTER_HORIZONTAL) + mtm_sizer.Add(wx.StaticText(mtm_panel,label='jog'),(3,1),flag=(wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER)) + forward_button = wx.Button(mtm_panel,label='forward') + forward_button.Bind(wx.EVT_LEFT_DOWN,forward_down) + forward_button.Bind(wx.EVT_LEFT_UP,forward_up) + mtm_sizer.Add(forward_button,(4,1),flag=wx.ALIGN_CENTER_HORIZONTAL) + right_button = wx.Button(mtm_panel,label='right') + right_button.Bind(wx.EVT_LEFT_DOWN,right_down) + right_button.Bind(wx.EVT_LEFT_UP,right_up) + mtm_sizer.Add(right_button,(3,2),flag=wx.ALIGN_CENTER_HORIZONTAL) + up_button = wx.Button(mtm_panel,label='up') + up_button.Bind(wx.EVT_LEFT_DOWN,up_down) + up_button.Bind(wx.EVT_LEFT_UP,up_up) + mtm_sizer.Add(up_button,(2,3),flag=wx.ALIGN_CENTER_HORIZONTAL) + down_button = wx.Button(mtm_panel,label='down') + down_button.Bind(wx.EVT_LEFT_DOWN,down_down) + down_button.Bind(wx.EVT_LEFT_UP,down_up) + mtm_sizer.Add(down_button,(3,3),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + mtm_sizer.Add(wx.StaticText(mtm_panel,label='speed (mm/s)'),(4,0),flag=(wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_BOTTOM)) + move_speed = wx.TextCtrl(mtm_panel,-1,'1') + mtm_sizer.Add(move_speed,(5,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + mtm_sizer.Add(wx.StaticText(mtm_panel,label='jog speed (mm/s) z (mm)'),(4,2),span=(1,2),flag=(wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_BOTTOM)) + jog_speed = wx.TextCtrl(mtm_panel,-1,'10') + mtm_sizer.Add(jog_speed,(5,2),flag=wx.ALIGN_CENTER_HORIZONTAL) + jog_height = wx.TextCtrl(mtm_panel,-1,'1') + mtm_sizer.Add(jog_height,(5,3),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + mtm_sizer.Add(wx.StaticText(mtm_panel,label='spindle'),(6,0),flag=(wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)) + spindle_button = wx.Button(mtm_panel,label='on') + spindle_button.Bind(wx.EVT_BUTTON,spindle) + mtm_sizer.Add(spindle_button,(6,1),flag=wx.ALIGN_CENTER_HORIZONTAL) + mtm_sizer.Add(wx.StaticText(mtm_panel,label='speed (rpm)'),(6,2),flag=(wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)) + spindle_speed = wx.TextCtrl(mtm_panel,-1,'10000') + mtm_sizer.Add(spindle_speed,(6,3),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + start_button = wx.Button(mtm_panel,label='start') + start_button.Bind(wx.EVT_BUTTON,start) + mtm_sizer.Add(start_button,(7,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + pause_button = wx.Button(mtm_panel,label='pause') + pause_button.Bind(wx.EVT_BUTTON,pause) + mtm_sizer.Add(pause_button,(7,1),flag=wx.ALIGN_CENTER_HORIZONTAL) + segment = wx.TextCtrl(mtm_panel,-1,'0') + mtm_sizer.Add(segment,(7,2),flag=wx.ALIGN_CENTER_HORIZONTAL) + segment_label = wx.StaticText(mtm_panel,label='segment') + mtm_sizer.Add(segment_label,(7,3),flag=(wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL)) + # + mtm_sizer.Add(wx.StaticText(mtm_panel,label='port'),(8,0),flag=(wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)) + port = wx.TextCtrl(mtm_panel,-1,'/dev/ttyUSB0') + mtm_sizer.Add(port,(8,1),span=(1,2),flag=wx.ALIGN_CENTER_HORIZONTAL|wx.EXPAND) + # + self.sizer.Add(mtm_panel,(1,0),flag=(wx.ALIGN_CENTER_HORIZONTAL)) + # + # fit + # + self.Fit() diff --git a/src/py/panel_path_snap_apa.py b/src/py/panel_path_snap_apa.py new file mode 100644 index 0000000..cef85e5 --- /dev/null +++ b/src/py/panel_path_snap_apa.py @@ -0,0 +1,574 @@ +# +# panel_path_snap.py +# MTM Snap virtual machine +# +# Neil Gershenfeld +# CBA MIT 11/28/11 +# nmp update dec 3 2011: mm_per_step +# +# (c) Massachusetts Institute of Technology 2011 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# todo +# APA out +# APA send +# process text entry +# acceleration +# job times +# +# imports +# +import wx,os +from math import * +import time +# +# machine definitions +# +mm_per_xstep = 0.00635 # stepper step size (400 steps/rev, 10 revs/in) +mm_per_ystep = 0.00635 # stepper step size '' +mm_per_zstep = 0.00635 # stepper step size '' +mm_per_jog_step = 0.00635 # jog step size +x_path = "" # APA path to x axis +y_path = "0" # APA path to y axis +z_path = "1" # APA path to z axis +spindle = "11" # APA path to spindle +# +# global variables +# +xstep = ystep = zstep = 0 # stepper position (step units) +spindle_state = 0 # on/off +move_state = 0 # on/off +# +# panel class +# +class path_snap_panel(wx.Panel): + def __init__(self,parent): + self.parent = parent + self.parent.path_file = '' + # + # start/stop spindle + # + def spindle(event): + global spindle_state + spindle_state = 1 - spindle_state + if (spindle_state == 1): + spindle_button.SetLabel('OFF') + else: + spindle_button.SetLabel('on') + # + # start/stop job + # + def start(event,start_line=5): + global move_state + move_state = 1 - move_state + if (move_state == 1): + if (self.parent.path_file == ''): + print "panel_path_snap: oops -- need path file" + move_state = 0 + return + start_button.SetLabel('STOP') + pause_button.SetLabel('pause') + start = time.time() + send_job(start_line) + end = time.time() + nsegment = float(segment.GetValue()) + print "%f sec, %f sec/segment"%((end-start),(end-start)/nsegment) + else: + start_button.SetLabel('start') + # + # send job + # + def send_job(start_line): + global move_state + global nx,ny,nz,dx,dy,dz,x0,y0,z0 + # + # read path + # + path_file = open(self.parent.path_file,'r') + lines = path_file.readlines() + segment_label.SetLabel("/"+str(len(lines)-1)) + dof = int(lines[0].split()[0]) + if (dof != 3): + print "panel_path_snap: oops -- need a 3D path" + return + units = lines[1] # currently ignored, mm assumed + nx = int(lines[2].split()[0]) + ny = int(lines[2].split()[1]) + nz = int(lines[2].split()[2]) + dx = float(lines[3].split()[0]) + dy = float(lines[3].split()[1]) + dz = float(lines[3].split()[2]) + x0 = float(lines[4].split()[0]) + y0 = float(lines[4].split()[1]) + z0 = float(lines[4].split()[2]) + path_file.close() + # + # get GUI values + # + speed_move = float(move_speed.GetValue()) + speed_jog = float(jog_speed.GetValue()) + height_jog = float(jog_height.GetValue()) + # + # loop over path + # + for i in range(start_line,len(lines)): + # + # check for GUI events + # + wx.Yield() + # + # quit if stopped + # + if (move_state == 0): + return + # + # read next line + # + line = lines[i].split() + # + # start of new segment? + # + if (line[0] == '.'): + # + # yes, clear old point and continue + # + (xold,yold,zold) = ([],[],[]) + continue + # + # no, go to next point + # + x = int(line[0]) + y = int(line[1]) + z = int(line[2]) + # + # first point in segment? + # + if (xold == []): + # + # yes, jog there and move down + # + move_z_abs_mm(height_jog,speed_jog) + move_xy_abs_path(x,y,speed_jog) + move_z_abs_path(z,speed_move) + else: + # + # no, move there + # + move_xyz_abs_path(x,y,z,speed_move) + # + # save old point + # + (xold,yold,zold) = (x,y,z) + # + # update segment counter and continue + # + segment.SetValue(str(i)) + # + # finish + # + move_state = 0 + start_button.SetLabel('start') + segment_label.SetLabel("segment") + # + # pause + # + def pause(event): + global move_state + print "pause" + if (move_state == 1): + move_state = 0 + start_button.SetLabel("start") + pause_button.SetLabel("CONTINUE") + elif (pause_button.GetLabel() == "CONTINUE"): + start(0,int(segment.GetValue())) + # + # APA send z, stepper step units, ms time units + # + def apa_send_z(dz,time): + command = "apa_send {^"+z_path+"|"+str(dz)+","+str(time)+"}" + #print command + os.system('echo "'+command+'" > /dev/null') + # os.system(command) + # + # APA send xy, stepper step units, ms time units + # + def apa_send_xy(dx,dy,time): + command = "apa_send {^"+x_path+"|"+str(dx)+","+str(time)+"}{^"+y_path+"|"+str(dy)+","+str(time)+"}" + #print command + os.system('echo "'+command+'" > /dev/null') + # os.system(command) + # + # APA send xyz, stepper step units, ms time units + # + def apa_send_xyz(dx,dy,dz,time): + command = "apa_send {^"+x_path+"|"+str(dx)+","+str(time)+"}{^"+y_path+"|"+str(dy)+","+str(time)+"}{^"+z_path+"|"+str(dz)+","+str(time)+"}" + #print command + os.system('echo "'+command+'" > /dev/null') + # os.system(command) + # + # move xy absolute, path units + # + def move_xy_abs_path(xi,yi,speed): + global xstep,ystep + global mm_per_xstep,mm_per_ystep + global nx,ny,nz,dx,dy,dz,x0,y0 + if (nx != 1): + x = x0 + dx*xi/(nx-1.0) + else: + x = x0 + if (ny != 1): + y = y0 + dy*yi/(ny-1.0) + else: + y = y0 + xpos.SetValue("%.3f"%x) + ypos.SetValue("%.3f"%y) + deltax = x - xstep*mm_per_xstep + deltay = y - ystep*mm_per_ystep + dxstep = int(deltax/mm_per_xstep) + xstep += dxstep + dystep = int(deltay/mm_per_ystep) + ystep += dystep + distance = sqrt(deltax*deltax+deltay*deltay) + time = int(1000* distance / speed) + apa_send_xy(dxstep,dystep,time) + # + # move xyz absolute, path units + # + def move_xyz_abs_path(xi,yi,zi,speed): + global xstep,ystep,zstep + global mm_per_xstep,mm_per_ystep,mm_per_zstep + global nx,ny,nz,dx,dy,dz,x0,y0,z0 + if (nx != 1): + x = x0 + dx*xi/(nx-1.0) + else: + x = x0 + if (ny != 1): + y = y0 + dy*yi/(ny-1.0) + else: + y = y0 + if (nz != 1): + z = z0 + dz*zi/(nz-1.0) + else: + z = z0 + xpos.SetValue("%.3f"%x) + ypos.SetValue("%.3f"%y) + zpos.SetValue("%.3f"%z) + deltax = x - xstep*mm_per_xstep + deltay = y - ystep*mm_per_ystep + deltaz = z - zstep*mm_per_zstep + dxstep = int(deltax/mm_per_xstep) + xstep += dxstep + dystep = int(deltay/mm_per_ystep) + ystep += dystep + dzstep = int(deltaz/mm_per_zstep) + zstep += dzstep + distance = sqrt(deltax*deltax+deltay*deltay+deltaz*deltaz) + time = int(1000* distance / speed) + apa_send_xyz(dxstep,dystep,dzstep,time) + # + # move z absolute, path units + # + def move_z_abs_path(z,speed): + global zstep + global mm_per_zstep + global nz,dz,z0 + if (nz != 1): + z = z0 + dz*zi/(nz-1.0) + else: + z = z0 + zpos.SetValue("%.3f"%z) + deltaz = z - zstep*mm_per_zstep + dzstep = int(deltaz/mm_per_zstep) + zstep += dzstep + distance = sqrt(deltaz*deltaz) + time = int(1000* distance / speed) + apa_send_z(dzstep,time) + # + # move z absolute, mm units + # + def move_z_abs_mm(z,speed): + global zstep + global mm_per_zstep + zpos.SetValue("%.3f"%z) + deltaz = z - zstep*mm_per_zstep + dzstep = int(deltaz/mm_per_zstep) + zstep += dzstep + distance = sqrt(deltaz*deltaz) + time = int(1000* distance / speed) + apa_send_z(dzstep,time) + # + # move xyz relative, mm units + # + def move_xyz_rel_mm(dx,dy,dz,speed): + global xstep,ystep,zstep + global mm_per_xstep,mm_per_ystep,mm_per_zstep + dxstep = int(dx / mm_per_xstep) + xstep += dxstep + xpos.SetValue("%.3f"%(xstep*mm_per_xstep)) + dystep = int(dy / mm_per_xstep) + ystep += dystep + ypos.SetValue("%.3f"%(ystep*mm_per_xstep)) + dzstep = int(dz / mm_per_xstep) + zstep += dzstep + zpos.SetValue("%.3f"%(zstep*mm_per_xstep)) + distance = sqrt(dx*dx+dy*dy+dz*dz) + time = int(1000* distance / speed) + apa_send_xyz(dxstep,dystep,dzstep,time) + # + # move left mouse down + # + def left_down(event): + global move_state + move_state = 1 + left_button.SetLabel('LEFT') + while 1: + wx.Yield() + if (move_state == 0): + return + move_xyz_rel_mm(-mm_per_jog_step,0,0,float(jog_speed.GetValue())) + # + # move left mouse up + # + def left_up(event): + global move_state + move_state = 0 + left_button.SetLabel('left') + # + # move right mouse down + # + def right_down(event): + global move_state + move_state = 1 + right_button.SetLabel('RIGHT') + while 1: + wx.Yield() + if (move_state == 0): + return + move_xyz_rel_mm(mm_per_jog_step,0,0,float(jog_speed.GetValue())) + # + # move right mouse up + # + def right_up(event): + global move_state + move_state = 0 + right_button.SetLabel('right') + # + # move forward mouse down + # + def forward_down(event): + global move_state + move_state = 1 + forward_button.SetLabel('FORWARD') + while 1: + wx.Yield() + if (move_state == 0): + return + move_xyz_rel_mm(0,-mm_per_jog_step,0,float(jog_speed.GetValue())) + # + # move forward mouse up + # + def forward_up(event): + global move_state + move_state = 0 + forward_button.SetLabel('forward') + # + # move back mouse down + # + def back_down(event): + global move_state + move_state = 1 + back_button.SetLabel('BACK') + while 1: + wx.Yield() + if (move_state == 0): + return + move_xyz_rel_mm(0,mm_per_jog_step,0,float(jog_speed.GetValue())) + # + # move back mouse up + # + def back_up(event): + global move_state + move_state = 0 + back_button.SetLabel('back') + # + # move up mouse down + # + def up_down(event): + global move_state + move_state = 1 + up_button.SetLabel('UP') + while 1: + wx.Yield() + if (move_state == 0): + return + move_xyz_rel_mm(0,0,mm_per_jog_step,float(jog_speed.GetValue())) + # + # move up mouse up + # + def up_up(event): + global move_state + move_state = 0 + up_button.SetLabel('up') + # + # move down mouse down + # + def down_down(event): + global move_state + move_state = 1 + down_button.SetLabel('DOWN') + while 1: + wx.Yield() + if (move_state == 0): + return + move_xyz_rel_mm(0,0,-mm_per_jog_step,float(jog_speed.GetValue())) + # + # move down mouse up + # + def down_up(event): + global move_state + move_state = 0 + down_button.SetLabel('down') + # + # zero x + # + def zero_x(event): + global xstep + xstep = 0 + xpos.SetValue("0.000") + # + # zero y + # + def zero_y(event): + global ystep + ystep = 0 + ypos.SetValue("0.000") + # + # zero z + # + def zero_z(event): + global zstep + zstep = 0 + zpos.SetValue("0.000") + # + # zero xyz + # + def zero_xyz(event): + global xstep,ystep,zstep + xstep = 0 + xpos.SetValue("0.000") + ystep = 0 + ypos.SetValue("0.000") + zstep = 0 + zpos.SetValue("0.000") + # + # home + # + def home(event): + speed_jog = float(jog_speed.GetValue()) + height_jog = float(jog_height.GetValue()) + move_z_abs_mm(height_jog,speed_jog) + move_xy_abs_path(0,0,speed_jog) + # + # panel + # + wx.Panel.__init__(self,parent) + self.sizer = wx.GridBagSizer(10,10) + self.SetSizer(self.sizer) + # + # label + # + label = wx.StaticText(self,label='to: MTM Snap') + bold_font = wx.Font(10,wx.DEFAULT,wx.NORMAL,wx.BOLD) + label.SetFont(bold_font) + self.sizer.Add(label,(0,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + # controls + # + mtm_panel = wx.Panel(self) + mtm_sizer = wx.GridBagSizer(10,10) + mtm_panel.SetSizer(mtm_sizer) + # + mtm_sizer.Add(wx.StaticText(mtm_panel,label='x y z (mm)'),(0,0),flag=wx.ALIGN_RIGHT) + xpos = wx.TextCtrl(mtm_panel,-1,'0.000') + mtm_sizer.Add(xpos,(0,1),flag=wx.ALIGN_CENTER_HORIZONTAL) + ypos = wx.TextCtrl(mtm_panel,-1,'0.000') + mtm_sizer.Add(ypos,(0,2),flag=wx.ALIGN_CENTER_HORIZONTAL) + zpos = wx.TextCtrl(mtm_panel,-1,'0.000') + mtm_sizer.Add(zpos,(0,3),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + zero_xyz_button = wx.Button(mtm_panel,label='zero xyz') + zero_xyz_button.Bind(wx.EVT_BUTTON,zero_xyz) + mtm_sizer.Add(zero_xyz_button,(1,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + zero_x_button = wx.Button(mtm_panel,label='zero x') + zero_x_button.Bind(wx.EVT_BUTTON,zero_x) + mtm_sizer.Add(zero_x_button,(1,1),flag=wx.ALIGN_CENTER_HORIZONTAL) + zero_y_button = wx.Button(mtm_panel,label='zero y') + zero_y_button.Bind(wx.EVT_BUTTON,zero_y) + mtm_sizer.Add(zero_y_button,(1,2),flag=wx.ALIGN_CENTER_HORIZONTAL) + zero_z_button = wx.Button(mtm_panel,label='zero z') + zero_z_button.Bind(wx.EVT_BUTTON,zero_z) + mtm_sizer.Add(zero_z_button,(1,3),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + home_button = wx.Button(mtm_panel,label='home') + home_button.Bind(wx.EVT_BUTTON,home) + mtm_sizer.Add(home_button,(2,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + left_button = wx.Button(mtm_panel,label='left') + left_button.Bind(wx.EVT_LEFT_DOWN,left_down) + left_button.Bind(wx.EVT_LEFT_UP,left_up) + mtm_sizer.Add(left_button,(3,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + back_button = wx.Button(mtm_panel,label='back') + back_button.Bind(wx.EVT_LEFT_DOWN,back_down) + back_button.Bind(wx.EVT_LEFT_UP,back_up) + mtm_sizer.Add(back_button,(2,1),flag=wx.ALIGN_CENTER_HORIZONTAL) + mtm_sizer.Add(wx.StaticText(mtm_panel,label='jog'),(3,1),flag=(wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER)) + forward_button = wx.Button(mtm_panel,label='forward') + forward_button.Bind(wx.EVT_LEFT_DOWN,forward_down) + forward_button.Bind(wx.EVT_LEFT_UP,forward_up) + mtm_sizer.Add(forward_button,(4,1),flag=wx.ALIGN_CENTER_HORIZONTAL) + right_button = wx.Button(mtm_panel,label='right') + right_button.Bind(wx.EVT_LEFT_DOWN,right_down) + right_button.Bind(wx.EVT_LEFT_UP,right_up) + mtm_sizer.Add(right_button,(3,2),flag=wx.ALIGN_CENTER_HORIZONTAL) + up_button = wx.Button(mtm_panel,label='up') + up_button.Bind(wx.EVT_LEFT_DOWN,up_down) + up_button.Bind(wx.EVT_LEFT_UP,up_up) + mtm_sizer.Add(up_button,(2,3),flag=wx.ALIGN_CENTER_HORIZONTAL) + down_button = wx.Button(mtm_panel,label='down') + down_button.Bind(wx.EVT_LEFT_DOWN,down_down) + down_button.Bind(wx.EVT_LEFT_UP,down_up) + mtm_sizer.Add(down_button,(3,3),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + mtm_sizer.Add(wx.StaticText(mtm_panel,label='speed (mm/s)'),(4,0),flag=(wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_BOTTOM)) + move_speed = wx.TextCtrl(mtm_panel,-1,'1') + mtm_sizer.Add(move_speed,(5,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + mtm_sizer.Add(wx.StaticText(mtm_panel,label='jog speed (mm/s) z (mm)'),(4,2),span=(1,2),flag=(wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_BOTTOM)) + jog_speed = wx.TextCtrl(mtm_panel,-1,'10') + mtm_sizer.Add(jog_speed,(5,2),flag=wx.ALIGN_CENTER_HORIZONTAL) + jog_height = wx.TextCtrl(mtm_panel,-1,'1') + mtm_sizer.Add(jog_height,(5,3),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + mtm_sizer.Add(wx.StaticText(mtm_panel,label='spindle'),(6,0),flag=(wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)) + spindle_button = wx.Button(mtm_panel,label='on') + spindle_button.Bind(wx.EVT_BUTTON,spindle) + mtm_sizer.Add(spindle_button,(6,1),flag=wx.ALIGN_CENTER_HORIZONTAL) + mtm_sizer.Add(wx.StaticText(mtm_panel,label='speed (rpm)'),(6,2),flag=(wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)) + spindle_speed = wx.TextCtrl(mtm_panel,-1,'10000') + mtm_sizer.Add(spindle_speed,(6,3),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + start_button = wx.Button(mtm_panel,label='start') + start_button.Bind(wx.EVT_BUTTON,start) + mtm_sizer.Add(start_button,(7,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + pause_button = wx.Button(mtm_panel,label='pause') + pause_button.Bind(wx.EVT_BUTTON,pause) + mtm_sizer.Add(pause_button,(7,1),flag=wx.ALIGN_CENTER_HORIZONTAL) + segment = wx.TextCtrl(mtm_panel,-1,'0') + mtm_sizer.Add(segment,(7,2),flag=wx.ALIGN_CENTER_HORIZONTAL) + segment_label = wx.StaticText(mtm_panel,label='segment') + mtm_sizer.Add(segment_label,(7,3),flag=(wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL)) + # + self.sizer.Add(mtm_panel,(1,0),flag=(wx.ALIGN_CENTER_HORIZONTAL)) + # + # fit + # + self.Fit() diff --git a/src/py/panel_path_uni.py b/src/py/panel_path_uni.py new file mode 100644 index 0000000..899b711 --- /dev/null +++ b/src/py/panel_path_uni.py @@ -0,0 +1,123 @@ +# +# panel_path_uni.py +# make .uni from .path +# +# Neil Gershenfeld +# CBA MIT 3/10/11 +# +# (c) Massachusetts Institute of Technology 2011 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,os +# +# panel class +# +class path_uni_panel(wx.Panel): + def __init__(self,parent): + self.parent = parent + self.parent.path_file = '' + # + # make uni + # + def make_uni(event): + if (self.parent.path_file == ''): + print 'panel_path_uni: oops -- need path file' + return + self.parent.uni_file = self.parent.tmp+self.parent.rootname+'.uni' + power = self.power.GetValue() + speed = self.speed.GetValue() + xmin = self.xmin.GetValue() + ymin = self.ymin.GetValue() + rate = self.rate.GetValue() + maxpower = self.maxpower.GetValue() + command = 'path_uni '+'\"'+self.parent.path_file+'\"'+' '+'\"'+self.parent.uni_file+'\"'+' '+power+' '+speed+' '+xmin+' '+ymin+' '+' '+rate+' '+maxpower + print command + os.system(command) + self.button.Show() + self.parent.Layout() + self.parent.Fit() + # + # send + # + def fab_send(event): + command = 'fab_send '+'\"'+self.parent.uni_file+'\"' + print command + os.system(command) + # + # panel + # + wx.Panel.__init__(self,parent) + self.sizer = wx.GridBagSizer(10,10) + self.SetSizer(self.sizer) + # + # label + # + label = wx.StaticText(self,label='to: uni') + bold_font = wx.Font(10,wx.DEFAULT,wx.NORMAL,wx.BOLD) + label.SetFont(bold_font) + self.sizer.Add(label,(0,0),span=(1,2),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + # send + # + self.button = wx.Button(self,label='send it!') + self.button.Bind(wx.EVT_BUTTON,fab_send) + self.button.SetFont(bold_font) + self.sizer.Add(self.button,(1,0),span=(1,2),flag=(wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL)) + self.button.Hide() + # + # controls + # + make = wx.Button(self,label='make .uni') + make.Bind(wx.EVT_BUTTON,make_uni) + self.sizer.Add(make,(2,0),span=(1,2),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + row3_panel = wx.Panel(self) + row3_sizer = wx.GridBagSizer(10,10) + row3_panel.SetSizer(row3_sizer) + row3_sizer.Add(wx.StaticText(row3_panel,label='2D power (%)'),(0,0),flag=(wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT)) + row3_sizer.Add(wx.StaticText(row3_panel,label='speed (%) '),(0,1),flag=(wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT)) + self.sizer.Add(row3_panel,(3,0),span=(1,2),flag=(wx.ALIGN_CENTER_HORIZONTAL)) + # + row4_panel = wx.Panel(self) + row4_sizer = wx.GridBagSizer(10,10) + row4_panel.SetSizer(row4_sizer) + self.power = wx.TextCtrl(row4_panel,-1,'25') + row4_sizer.Add(self.power,(0,0)) + self.speed = wx.TextCtrl(row4_panel,-1,'75') + row4_sizer.Add(self.speed,(0,1)) + self.sizer.Add(row4_panel,(4,0),span=(1,2),flag=(wx.ALIGN_CENTER_HORIZONTAL)) + # + row5_panel = wx.Panel(self) + row5_sizer = wx.GridBagSizer(10,10) + row5_panel.SetSizer(row5_sizer) + row5_sizer.Add(wx.StaticText(row5_panel,label='xmin (mm)'),(0,0),flag=(wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT)) + row5_sizer.Add(wx.StaticText(row5_panel,label='ymin (mm)'),(0,1),flag=(wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT)) + self.sizer.Add(row5_panel,(5,0),span=(1,2),flag=(wx.ALIGN_CENTER_HORIZONTAL)) + # + row6_panel = wx.Panel(self) + row6_sizer = wx.GridBagSizer(10,10) + row6_panel.SetSizer(row6_sizer) + self.xmin = wx.TextCtrl(row6_panel,-1,'0') + row6_sizer.Add(self.xmin,(0,0)) + self.ymin = wx.TextCtrl(row6_panel,-1,'0') + row6_sizer.Add(self.ymin,(0,1)) + self.sizer.Add(row6_panel,(6,0),span=(1,2),flag=(wx.ALIGN_CENTER_HORIZONTAL)) + # + row7_panel = wx.Panel(self) + row7_sizer = wx.GridBagSizer(10,10) + row7_panel.SetSizer(row7_sizer) + row7_sizer.Add(wx.StaticText(row7_panel,label='3D power (%)'),(0,0),flag=(wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT)) + row7_sizer.Add(wx.StaticText(row7_panel,label='rate '),(0,1),flag=(wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT)) + self.sizer.Add(row7_panel,(7,0),span=(1,2),flag=(wx.ALIGN_CENTER_HORIZONTAL)) + # + row8_panel = wx.Panel(self) + row8_sizer = wx.GridBagSizer(10,10) + row8_panel.SetSizer(row8_sizer) + self.maxpower = wx.TextCtrl(row8_panel,-1,'100') + row8_sizer.Add(self.maxpower,(0,0)) + self.rate = wx.TextCtrl(row8_panel,-1,'500') + row8_sizer.Add(self.rate,(0,1)) + self.sizer.Add(row8_panel,(8,0),span=(1,2),flag=(wx.ALIGN_CENTER_HORIZONTAL)) diff --git a/src/py/panel_png.py b/src/py/panel_png.py new file mode 100644 index 0000000..292acf5 --- /dev/null +++ b/src/py/panel_png.py @@ -0,0 +1,242 @@ +# +# panel_png.py +# read .png +# +# Neil Gershenfeld +# CBA MIT 2/19/11 +# +# (c) Massachusetts Institute of Technology 2011 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,string,os,sys +# +# panel class +# +class png_panel(wx.Panel): + def __init__(self,parent): + self.parent = parent + # + # get png info + # + def png_info(name): + temp_name = self.parent.tmp+'png_dimension' + command = 'png_size '+'\"'+name+'\"'+' > '+'\"'+temp_name+'\"' + os.system(command) + output_file = open(temp_name,'r') + output = output_file.read() + command = 'rm '+'\"'+temp_name+'\"' + os.system(command) + print output + self.parent.units = 1 + self.parent.xmin = 0 + start = string.find(output,"dx:") + end = string.find(output,"mm",start) + self.parent.xmax = float(output[start+4:end]) + self.parent.ymin = 0 + start = string.find(output,"dy:") + end = string.find(output,"mm",start) + self.parent.ymax = float(output[start+4:end]) + if "dz:" in output: + self.parent.zmin = 0 + start = string.find(output,"dz:") + end = string.find(output,"mm",start) + self.parent.zmax = float(output[start+4:end]) + return output + # + # file open dialog + # + def file_dialog(event): + dialog = wx.FileDialog(self, "Choose a file", os.getcwd(), "", "*.png", wx.OPEN) + if (dialog.ShowModal() == wx.ID_OK): + self.parent.filename = dialog.GetPath() + self.parent.basename = os.path.basename(self.parent.filename) + load_file(0) + # + # load file + # + def load_file(event): + if (self.parent.basename == ""): + return + pos = string.find(self.parent.basename,".png") + if (pos == -1): + pos = string.find(self.parent.basename,".PNG") + if (pos == -1): + print 'png_panel: oops -- must be .png' + sys.exit() + self.parent.rootname = self.parent.basename[:pos] + self.parent.png_file = self.parent.filename + info = png_info(self.parent.filename) + self.info.SetLabel(info) + png_image = wx.Image(self.parent.filename) + (nx,ny) = png_image.GetSize() + pixels = "size: (%d,%d)"%(nx,ny) + ratio = float(ny)/float(nx) + if (ratio > 1): + self.parent.ysize = self.parent.size + self.parent.xsize = int(self.parent.size/ratio) + else: + self.parent.ysize = int(self.parent.size*ratio) + self.parent.xsize = self.parent.size + wx.Image.Rescale(png_image,self.parent.xsize,self.parent.ysize) + png_bitmap = png_image.ConvertToBitmap() + self.bitmap.SetBitmap(png_bitmap) + self.bitmap.Show() + self.parent.Layout() + self.parent.Fit() + # + # select file + # + def select_file(event): + dialog = wx.FileDialog(self, "Choose a file", os.getcwd(), "", "*.cad", wx.OPEN) + if (dialog.ShowModal() == wx.ID_OK): + self.parent.filename = dialog.GetPath() + self.parent.basename = os.path.basename(self.parent.filename) + pos = string.find(self.parent.basename,".cad") + if (pos == -1): + print 'cad_panel: oops -- must be .cad' + sys.exit() + else: + self.parent.rootname = self.parent.basename[:pos] + cad_file = open(self.parent.filename,'r') + cad_string = cad_file.read() + cad_file.close() + self.text.SetValue(cad_string) + # + # invert + # + def invert_dialog(event): + command = 'png_scale '+'\"'+self.parent.png_file+'\" \"'+self.parent.png_file+'\" 1 0' + print command + os.system(command) + load_file(0) + # + # resize + # + def resize_dialog(event): + resize.dx = self.parent.xmax + resize.dy = self.parent.ymax + resize_width.SetValue(str(resize.dx)) + resize_height.SetValue(str(resize.dy)) + resize.Show() + def resize_dialog_cancel(event): + resize.Hide() + def resize_dialog_resize(event): + width = resize_width.GetValue() + height = resize_height.GetValue() + print width,height + command = 'png_size '+'\"'+self.parent.png_file+'\" '+width+' '+height + print command + os.system(command) + load_file(0) + resize.Hide() + def resize_width_handler(event): + if resize.prop.GetValue(): + dx = float(resize_width.GetValue()) + dy = dx * resize.dy/resize.dx + resize_height.Unbind(wx.EVT_TEXT) + resize_height.SetValue(str(dy)) + resize_height.Bind(wx.EVT_TEXT,resize_height_handler) + def resize_height_handler(event): + if resize.prop.GetValue(): + dy = float(resize_height.GetValue()) + dx = dy * resize.dx/resize.dy + resize_width.Unbind(wx.EVT_TEXT) + resize_width.SetValue(str(dx)) + resize_width.Bind(wx.EVT_TEXT,resize_width_handler) + # + resize = wx.Frame(None, -1, 'resize') + resize_sizer = wx.GridBagSizer(10,10) + resize.SetSizer(resize_sizer) + # + resize_sizer.Add(wx.StaticText(resize,label='width (mm):'),(0,0),flag=(wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT)) + resize_width = wx.TextCtrl(resize,-1,'0') + resize_width.Bind(wx.EVT_TEXT,resize_width_handler) + resize_sizer.Add(resize_width,(0,1)) + # + resize_sizer.Add(wx.StaticText(resize,label='height (mm):'),(0,2),flag=(wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT)) + resize_height = wx.TextCtrl(resize,-1,'0') + resize_height.Bind(wx.EVT_TEXT,resize_height_handler) + resize_sizer.Add(resize_height,(0,3)) + # + resize.prop = wx.CheckBox(resize,label='proportional',style=wx.RB_GROUP) + resize.prop.SetValue(True) + resize_sizer.Add(resize.prop,(1,1)) + # + resize_resize = wx.Button(resize,label='resize') + resize_resize.Bind(wx.EVT_BUTTON,resize_dialog_resize) + resize_sizer.Add(resize_resize,(1,2),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + resize_cancel = wx.Button(resize,label='cancel') + resize_cancel.Bind(wx.EVT_BUTTON,resize_dialog_cancel) + resize_sizer.Add(resize_cancel,(1,3),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + resize.Fit() + # + # panel + # + wx.Panel.__init__(self,parent) + self.sizer = wx.GridBagSizer(10,10) + self.SetSizer(self.sizer) + # + # label + # + label = wx.StaticText(self,label='from: png') + bold_font = wx.Font(10,wx.DEFAULT,wx.NORMAL,wx.BOLD) + label.SetFont(bold_font) + self.sizer.Add(label,(0,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + # image + # + self.bitmap = wx.StaticBitmap(self) + self.sizer.Add(self.bitmap,(1,0),flag=(wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL)) + self.bitmap.Hide() + def mouse_move(event): + x = self.parent.xmin + (self.parent.xmax-self.parent.xmin)*event.GetX()/float(self.parent.xsize) + y = self.parent.ymin + (self.parent.ymax-self.parent.ymin)*(self.parent.ysize-event.GetY())/float(self.parent.ysize) + self.position.SetLabel("x: %.3f mm y: %.3f mm"%(x,y)) + self.Layout() + self.Fit() + self.bitmap.Bind(wx.EVT_MOTION,mouse_move) + # + # controls + # + control_panel = wx.Panel(self) + control_sizer = wx.GridBagSizer(10,10) + control_panel.SetSizer(control_sizer) + # + button = wx.Button(control_panel,label='load .png') + button.Bind(wx.EVT_BUTTON,file_dialog) + control_sizer.Add(button,(0,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + button = wx.Button(control_panel,label='resize .png') + button.Bind(wx.EVT_BUTTON,resize_dialog) + control_sizer.Add(button,(0,1),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + button = wx.Button(control_panel,label='invert .png') + button.Bind(wx.EVT_BUTTON,invert_dialog) + control_sizer.Add(button,(0,2),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + self.sizer.Add(control_panel,(2,0),flag=(wx.ALIGN_CENTER_HORIZONTAL)) + # + self.position= wx.StaticText(self,label="") + self.sizer.Add(self.position,(3,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + self.info= wx.StaticText(self,label="") + self.sizer.Add(self.info,(4,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + # file + # + load_file(0) + # + # fit + # + self.Fit() + # + # parent call to update size + # + def update_size(self,sizex,sizey): + self.Layout() + self.Fit() diff --git a/src/py/panel_png_drl.py b/src/py/panel_png_drl.py new file mode 100644 index 0000000..d049fe9 --- /dev/null +++ b/src/py/panel_png_drl.py @@ -0,0 +1,75 @@ +# +# panel_png_drl.py +# make .drl from .png +# +# Neil Gershenfeld +# CBA MIT 11/25/12 +# +# (c) Massachusetts Institute of Technology 2012 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,os +# +# panel class +# +class png_drl_panel(wx.Panel): + def __init__(self,parent): + self.parent = parent + self.parent.path_file = '' + # + # make drl + # + def make_drl(event): + if (self.parent.png_file == ''): + print 'make_png_drl: oops -- need .png file' + return + self.parent.drl_file = self.parent.tmp+self.parent.rootname+'.drl' + command = 'png_drl '+'\"'+self.parent.png_file+'\"'+' '+'\"'+self.parent.drl_file+'\"' + print command + os.system(command) + self.button.SetMaxSize((self.parent.xsize,self.parent.ysize)) + self.button.SetMinSize((self.parent.xsize,self.parent.ysize)) + self.button.Show() + self.parent.Layout() + self.parent.Fit() + # + # send + # + def fab_send(event): + command = 'fab_send '+'\"'+self.parent.drl_file+'\"' + print command + os.system(command) + # + # panel + # + wx.Panel.__init__(self,parent) + self.sizer = wx.GridBagSizer(10,10) + self.SetSizer(self.sizer) + # + # label + # + label = wx.StaticText(self,label='to: Excellon') + bold_font = wx.Font(10,wx.DEFAULT,wx.NORMAL,wx.BOLD) + label.SetFont(bold_font) + self.sizer.Add(label,(0,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + # send + # + self.button = wx.Button(self,label='send it!') + self.button.Bind(wx.EVT_BUTTON,fab_send) + self.button.SetFont(bold_font) + self.sizer.Add(self.button,(1,0),flag=(wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL)) + self.button.Hide() + # + # controls + # + make = wx.Button(self,label='make .drl') + make.Bind(wx.EVT_BUTTON,make_drl) + self.sizer.Add(make,(2,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + # fit + # + self.Fit() diff --git a/src/py/panel_png_grb.py b/src/py/panel_png_grb.py new file mode 100644 index 0000000..9b6485c --- /dev/null +++ b/src/py/panel_png_grb.py @@ -0,0 +1,75 @@ +# +# panel_png_grb.py +# make .grb from .png +# +# Neil Gershenfeld +# CBA MIT 11/23/12 +# +# (c) Massachusetts Institute of Technology 2012 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,os +# +# panel class +# +class png_grb_panel(wx.Panel): + def __init__(self,parent): + self.parent = parent + self.parent.path_file = '' + # + # make grb + # + def make_grb(event): + if (self.parent.png_file == ''): + print 'make_png_grb: oops -- need .png file' + return + self.parent.grb_file = self.parent.tmp+self.parent.rootname+'.grb' + command = 'png_grb '+'\"'+self.parent.png_file+'\"'+' '+'\"'+self.parent.grb_file+'\"' + print command + os.system(command) + self.button.SetMaxSize((self.parent.xsize,self.parent.ysize)) + self.button.SetMinSize((self.parent.xsize,self.parent.ysize)) + self.button.Show() + self.parent.Layout() + self.parent.Fit() + # + # send + # + def fab_send(event): + command = 'fab_send '+'\"'+self.parent.grb_file+'\"' + print command + os.system(command) + # + # panel + # + wx.Panel.__init__(self,parent) + self.sizer = wx.GridBagSizer(10,10) + self.SetSizer(self.sizer) + # + # label + # + label = wx.StaticText(self,label='to: Gerber') + bold_font = wx.Font(10,wx.DEFAULT,wx.NORMAL,wx.BOLD) + label.SetFont(bold_font) + self.sizer.Add(label,(0,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + # send + # + self.button = wx.Button(self,label='send it!') + self.button.Bind(wx.EVT_BUTTON,fab_send) + self.button.SetFont(bold_font) + self.sizer.Add(self.button,(1,0),flag=(wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL)) + self.button.Hide() + # + # controls + # + make = wx.Button(self,label='make .grb') + make.Bind(wx.EVT_BUTTON,make_grb) + self.sizer.Add(make,(2,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + # fit + # + self.Fit() diff --git a/src/py/panel_png_path.py b/src/py/panel_png_path.py new file mode 100644 index 0000000..498c707 --- /dev/null +++ b/src/py/panel_png_path.py @@ -0,0 +1,346 @@ +# +# panel_png_path.py +# make .path from .png +# +# Neil Gershenfeld 9/3/13 +# (c) Massachusetts Institute of Technology 2013 +# +# This work may be reproduced, modified, distributed, +# performed, and displayed for any purpose, but must +# acknowledge the fab modules project. Copyright is +# retained and must be preserved. The work is provided +# as is; no warranty is provided, and users accept all +# liability. +# +# imports +# +import wx,os,string +from panel_path import path_panel +# +# panel class +# +class png_path_panel(wx.Panel): + # + # constructor + # + def __init__(self,parent): + self.parent = parent + self.parent.png_file = '' + self.parent.zmin = '' + self.parent.zmax = '' + self.parent.units = 1 + # + # make path + # + def make_path(event): + if (self.parent.png_file == ''): + print "png_path: oops -- must make .png first" + return + self.parent.path_file = self.parent.tmp+self.parent.rootname+'.path' + path_png = self.parent.tmp+self.parent.rootname+'.path.png' + selection = self.path_type.GetValue() + if (selection == "2D"): + diameter = self.diameter_2D.GetValue() + number = self.number_2D.GetValue() + overlap = self.overlap_2D.GetValue() + error = self.error_2D.GetValue() + intensity = self.intensity_2D.GetValue() + command = 'png_path '+'\"'+self.parent.png_file+'\"'+' '+'\"'+self.parent.path_file+'\"'+' '+error+' '+diameter+' '+number+' '+overlap+' '+intensity + elif (selection == "3D plane"): + diameter = self.diameter_plane.GetValue() + number = self.number_plane.GetValue() + overlap = self.overlap_plane.GetValue() + error = self.error_plane.GetValue() + intensity = self.intensity_plane.GetValue() + z = self.z_plane.GetValue() + command = 'png_path '+'\"'+self.parent.png_file+'\"'+' '+'\"'+self.parent.path_file+'\"'+' '+error+' '+diameter+' '+number+' '+overlap+' '+intensity+' '+intensity+' '+z + elif (selection == "3D rough"): + diameter = self.diameter_rough.GetValue() + number = self.number_rough.GetValue() + overlap = self.overlap_rough.GetValue() + error = self.error_rough.GetValue() + itop = self.itop_rough.GetValue() + ibot = self.ibot_rough.GetValue() + ztop = self.ztop_rough.GetValue() + zbot = self.zbot_rough.GetValue() + zstep = self.zstep_rough.GetValue() + command = 'png_path '+'\"'+self.parent.png_file+'\"'+' '+'\"'+self.parent.path_file+'\"'+' '+error+' '+diameter+' '+number+' '+overlap+' '+itop+' '+ibot+' '+ztop+' '+zbot+' '+zstep + elif (selection == "3D finish"): + diameter = self.diameter_finish.GetValue() + overlap = self.overlap_finish.GetValue() + error = self.error_finish.GetValue() + itop = self.itop_finish.GetValue() + ibot = self.ibot_finish.GetValue() + ztop = self.ztop_finish.GetValue() + zbot = self.zbot_finish.GetValue() + clearance_length = self.clearance_length_finish.GetValue() + clearance_diameter = self.clearance_diameter_finish.GetValue() + number = '0' + zstep = '0' + if self.xz_finish.GetValue(): + xz = "1" + else: + xz = "0" + if self.yz_finish.GetValue(): + yz = "1" + else: + yz = "0" + xy = "0" + if self.flat_end_finish.GetValue(): + tool_type = 'f' + elif self.ball_end_finish.GetValue(): + tool_type = 'b' + command = 'png_path '+'\"'+self.parent.png_file+'\"'+' '+'\"'+self.parent.path_file+'\"'+' '+error+' '+diameter+' '+number+' '+overlap+' '+itop+' '+ibot+' '+ztop+' '+zbot+' '+zstep+' '+xz+' '+yz+' '+xy+' '+tool_type+' '+clearance_length+' '+clearance_diameter + print command + ret = os.system(command) + if (ret == 0): + self.path_viewer.draw(self.parent.path_file) + #self.parent.Layout() + #self.parent.Fit() + # + # panel + # + wx.Panel.__init__(self,parent) + self.sizer = wx.GridBagSizer(10,10) + self.SetSizer(self.sizer) + # + # label + # + label = wx.StaticText(self,label='to: path') + bold_font = wx.Font(10,wx.DEFAULT,wx.NORMAL,wx.BOLD) + label.SetFont(bold_font) + self.sizer.Add(label,(0,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + # image + # + self.path_viewer = path_panel(self) + self.sizer.Add(self.path_viewer,(1,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + # controls + # + make_panel = wx.Panel(self) + make_sizer = wx.GridBagSizer(10,10) + make_panel.SetSizer(make_sizer) + make_button = wx.Button(make_panel,label='make .path') + make_button.Bind(wx.EVT_BUTTON,make_path) + make_sizer.Add(make_button,(0,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + make_sizer.Add(wx.StaticText(make_panel,label='type:'),(0,1),flag=(wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)) + self.path_types = ["2D","3D plane","3D rough","3D finish"] + self.path_type = wx.ComboBox(make_panel,size=(100,-1),value="2D",choices=self.path_types,style=wx.CB_READONLY) + self.parent.path_type = "2D" + self.path_type.Bind(wx.EVT_COMBOBOX,self.path_type_handler) + make_sizer.Add(self.path_type,(0,2),flag=wx.ALIGN_LEFT) + self.sizer.Add(make_panel,(2,0),flag=(wx.ALIGN_CENTER_HORIZONTAL)) + # + # 2D panel + # + panel_2D = wx.Panel(self) + sizer_2D = wx.GridBagSizer(10,10) + panel_2D.SetSizer(sizer_2D) + # + sizer_2D.Add(wx.StaticText(panel_2D,label='diameter (mm)'),(0,0),flag=(wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)) + self.diameter_2D = wx.TextCtrl(panel_2D,-1,'0.25') + sizer_2D.Add(self.diameter_2D,(0,1),flag=(wx.ALIGN_LEFT)) + self.number_2D = wx.TextCtrl(panel_2D,-1,'1') + sizer_2D.Add(self.number_2D,(0,2),flag=(wx.ALIGN_RIGHT)) + sizer_2D.Add(wx.StaticText(panel_2D,label='offsets (-1 to fill)'),(0,3),flag=(wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL)) + # + sizer_2D.Add(wx.StaticText(panel_2D,label='overlap (0-1)'),(1,0),flag=(wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)) + self.overlap_2D = wx.TextCtrl(panel_2D,-1,'0.5') + sizer_2D.Add(self.overlap_2D,(1,1),flag=(wx.ALIGN_RIGHT)) + self.error_2D = wx.TextCtrl(panel_2D,-1,'1.1') + sizer_2D.Add(self.error_2D,(1,2),flag=(wx.ALIGN_RIGHT)) + sizer_2D.Add(wx.StaticText(panel_2D,label='error (pixels)'),(1,3),flag=(wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL)) + # + sizer_2D.Add(wx.StaticText(panel_2D,label='intensity (0-1)'),(2,1),flag=(wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)) + self.intensity_2D = wx.TextCtrl(panel_2D,-1,'0.5') + sizer_2D.Add(self.intensity_2D,(2,2),flag=(wx.ALIGN_RIGHT)) + # + self.sizer.Add(panel_2D,(3,0),flag=(wx.ALIGN_CENTER_HORIZONTAL)) + self.type_panel = panel_2D + self.type_panels = [panel_2D] + # + # 3D plane panel + # + panel_3D_plane = wx.Panel(self) + sizer_3D_plane = wx.GridBagSizer(10,10) + panel_3D_plane.SetSizer(sizer_3D_plane) + # + sizer_3D_plane.Add(wx.StaticText(panel_3D_plane,label=' diameter (mm)'),(0,0),flag=(wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)) + self.diameter_plane = wx.TextCtrl(panel_3D_plane,-1,'0.25') + sizer_3D_plane.Add(self.diameter_plane,(0,1),flag=(wx.ALIGN_LEFT)) + self.number_plane = wx.TextCtrl(panel_3D_plane,-1,'1') + sizer_3D_plane.Add(self.number_plane,(0,2),flag=(wx.ALIGN_RIGHT)) + sizer_3D_plane.Add(wx.StaticText(panel_3D_plane,label='offsets (-1 to fill)'),(0,3),flag=(wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL)) + # + sizer_3D_plane.Add(wx.StaticText(panel_3D_plane,label='overlap (0-1)'),(1,0),flag=(wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)) + self.overlap_plane = wx.TextCtrl(panel_3D_plane,-1,'0.5') + sizer_3D_plane.Add(self.overlap_plane,(1,1),flag=(wx.ALIGN_LEFT)) + self.error_plane = wx.TextCtrl(panel_3D_plane,-1,'1.1') + sizer_3D_plane.Add(self.error_plane,(1,2),flag=(wx.ALIGN_RIGHT)) + sizer_3D_plane.Add(wx.StaticText(panel_3D_plane,label='error (pixels)'),(1,3),flag=(wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL)) + # + sizer_3D_plane.Add(wx.StaticText(panel_3D_plane,label='intensity (0-1) '),(2,0),flag=(wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)) + self.intensity_plane = wx.TextCtrl(panel_3D_plane,-1,'0.5') + sizer_3D_plane.Add(self.intensity_plane,(2,1),flag=(wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL)) + self.z_plane = wx.TextCtrl(panel_3D_plane,-1,'0') + sizer_3D_plane.Add(self.z_plane,(2,2),flag=(wx.ALIGN_RIGHT)) + sizer_3D_plane.Add(wx.StaticText(panel_3D_plane,label='z (mm)'),(2,3),flag=(wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL)) + # + panel_3D_plane.Hide() + self.type_panels.append(panel_3D_plane) + # + # 3D rough panel + # + panel_3D_rough = wx.Panel(self) + sizer_3D_rough = wx.GridBagSizer(10,10) + panel_3D_rough.SetSizer(sizer_3D_rough) + # + sizer_3D_rough.Add(wx.StaticText(panel_3D_rough,label='diameter (mm)'),(0,0),flag=(wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)) + self.diameter_rough = wx.TextCtrl(panel_3D_rough,-1,'0.25') + sizer_3D_rough.Add(self.diameter_rough,(0,1),flag=(wx.ALIGN_LEFT)) + self.number_rough = wx.TextCtrl(panel_3D_rough,-1,'-1') + sizer_3D_rough.Add(self.number_rough,(0,2),flag=(wx.ALIGN_RIGHT)) + sizer_3D_rough.Add(wx.StaticText(panel_3D_rough,label='offsets (-1 to fill)'),(0,3),flag=(wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL)) + # + sizer_3D_rough.Add(wx.StaticText(panel_3D_rough,label='overlap (0-1)'),(1,0),flag=(wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)) + self.overlap_rough = wx.TextCtrl(panel_3D_rough,-1,'0.5') + sizer_3D_rough.Add(self.overlap_rough,(1,1),flag=(wx.ALIGN_LEFT)) + self.error_rough = wx.TextCtrl(panel_3D_rough,-1,'1.1') + sizer_3D_rough.Add(self.error_rough,(1,2),flag=(wx.ALIGN_RIGHT)) + sizer_3D_rough.Add(wx.StaticText(panel_3D_rough,label='error (pixels)'),(1,3),flag=(wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL)) + # + sizer_3D_rough.Add(wx.StaticText(panel_3D_rough,label='top intensity (0-1)'),(2,0),flag=(wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)) + self.itop_rough = wx.TextCtrl(panel_3D_rough,-1,'1') + sizer_3D_rough.Add(self.itop_rough,(2,1),flag=(wx.ALIGN_LEFT)) + self.ztop_rough = wx.TextCtrl(panel_3D_rough,-1,'0') + sizer_3D_rough.Add(self.ztop_rough,(2,2),flag=(wx.ALIGN_RIGHT)) + sizer_3D_rough.Add(wx.StaticText(panel_3D_rough,label='top z (mm)'),(2,3),flag=(wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL)) + # + sizer_3D_rough.Add(wx.StaticText(panel_3D_rough,label='bot intensity (0-1)'),(3,0),flag=(wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)) + self.ibot_rough = wx.TextCtrl(panel_3D_rough,-1,'0') + sizer_3D_rough.Add(self.ibot_rough,(3,1),flag=(wx.ALIGN_LEFT)) + self.zbot_rough = wx.TextCtrl(panel_3D_rough,-1,'') + sizer_3D_rough.Add(self.zbot_rough,(3,2),flag=(wx.ALIGN_RIGHT)) + sizer_3D_rough.Add(wx.StaticText(panel_3D_rough,label='bot z (mm)'),(3,3),flag=(wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL)) + # + sizer_3D_rough.Add(wx.StaticText(panel_3D_rough,label='cut depth (mm)'),(4,1),flag=(wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL)) + self.zstep_rough = wx.TextCtrl(panel_3D_rough,-1,'1') + sizer_3D_rough.Add(self.zstep_rough,(4,2),flag=(wx.ALIGN_RIGHT)) + # + panel_3D_rough.Hide() + self.type_panels.append(panel_3D_rough) + # + # 3D finish panel + # + panel_3D_finish = wx.Panel(self) + sizer_3D_finish = wx.GridBagSizer(10,10) + panel_3D_finish.SetSizer(sizer_3D_finish) + # + sizer_3D_finish.Add(wx.StaticText(panel_3D_finish,label='tool diameter (mm)'),(0,1),flag=(wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)) + self.diameter_finish = wx.TextCtrl(panel_3D_finish,-1,'0.25') + sizer_3D_finish.Add(self.diameter_finish,(0,2),flag=(wx.ALIGN_LEFT)) + # + sizer_3D_finish.Add(wx.StaticText(panel_3D_finish,label='overlap (0-1)'),(1,0),flag=(wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)) + self.overlap_finish = wx.TextCtrl(panel_3D_finish,-1,'0.5') + sizer_3D_finish.Add(self.overlap_finish,(1,1),flag=(wx.ALIGN_LEFT)) + self.error_finish = wx.TextCtrl(panel_3D_finish,-1,'1.1') + sizer_3D_finish.Add(self.error_finish,(1,2),flag=(wx.ALIGN_RIGHT)) + sizer_3D_finish.Add(wx.StaticText(panel_3D_finish,label='error (pixels)'),(1,3),flag=(wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL)) + # + sizer_3D_finish.Add(wx.StaticText(panel_3D_finish,label='top intensity (0-1)'),(2,0),flag=(wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)) + self.itop_finish = wx.TextCtrl(panel_3D_finish,-1,'1') + sizer_3D_finish.Add(self.itop_finish,(2,1),flag=(wx.ALIGN_LEFT)) + self.ztop_finish = wx.TextCtrl(panel_3D_finish,-1,'0') + sizer_3D_finish.Add(self.ztop_finish,(2,2),flag=(wx.ALIGN_RIGHT)) + sizer_3D_finish.Add(wx.StaticText(panel_3D_finish,label='top z (mm)'),(2,3),flag=(wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL)) + # + sizer_3D_finish.Add(wx.StaticText(panel_3D_finish,label='bot intensity (0-1)'),(3,0),flag=(wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)) + self.ibot_finish = wx.TextCtrl(panel_3D_finish,-1,'0') + sizer_3D_finish.Add(self.ibot_finish,(3,1),flag=(wx.ALIGN_LEFT)) + self.zbot_finish = wx.TextCtrl(panel_3D_finish,-1,'') + sizer_3D_finish.Add(self.zbot_finish,(3,2),flag=(wx.ALIGN_RIGHT)) + sizer_3D_finish.Add(wx.StaticText(panel_3D_finish,label='bot z (mm)'),(3,3),flag=(wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL)) + # + sizer_3D_finish.Add(wx.StaticText(panel_3D_finish,label='clearance length (mm)'),(4,0),flag=(wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)) + self.clearance_length_finish = wx.TextCtrl(panel_3D_finish,-1,'0') + sizer_3D_finish.Add(self.clearance_length_finish,(4,1),flag=(wx.ALIGN_LEFT)) + self.clearance_diameter_finish = wx.TextCtrl(panel_3D_finish,-1,'0') + sizer_3D_finish.Add(self.clearance_diameter_finish,(4,2),flag=(wx.ALIGN_RIGHT)) + sizer_3D_finish.Add(wx.StaticText(panel_3D_finish,label='clearance diameter (mm)'),(4,3),flag=(wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL)) + # + panel_3D_finish_4 = wx.Panel(panel_3D_finish) + sizer_3D_finish_4 = wx.GridBagSizer(10,10) + panel_3D_finish_4.SetSizer(sizer_3D_finish_4) + sizer_3D_finish_4.Add(wx.StaticText(panel_3D_finish_4,label='direction:'),(0,0),flag=(wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)) + self.xz_finish = wx.CheckBox(panel_3D_finish_4,-1,'xz',(10,10)) + self.xz_finish.SetValue(True) + sizer_3D_finish_4.Add(self.xz_finish,(0,1),flag=wx.ALIGN_CENTER_HORIZONTAL) + self.yz_finish = wx.CheckBox(panel_3D_finish_4,-1,'yz',(10,10)) + self.yz_finish.SetValue(True) + sizer_3D_finish_4.Add(self.yz_finish,(0,2),flag=wx.ALIGN_CENTER_HORIZONTAL) + sizer_3D_finish_4.Add(wx.StaticText(panel_3D_finish_4,label='type:'),(0,3),flag=(wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)) + self.flat_end_finish = wx.RadioButton(panel_3D_finish_4,-1,'flat end',(10,10),style=wx.RB_GROUP) + sizer_3D_finish_4.Add(self.flat_end_finish,(0,4),flag=wx.ALIGN_CENTER_HORIZONTAL) + self.ball_end_finish = wx.RadioButton(panel_3D_finish_4,-1,'ball end',(10,10)) + #self.ball_end_finish.Enable(False) + sizer_3D_finish_4.Add(self.ball_end_finish,(0,5),flag=wx.ALIGN_CENTER_HORIZONTAL) + sizer_3D_finish.Add(panel_3D_finish_4,(5,0),span=(1,4),flag=(wx.ALIGN_CENTER_HORIZONTAL)) + # + panel_3D_finish.Hide() + self.type_panels.append(panel_3D_finish) + # + # fit + # + self.Fit() + # + # + # path type handler + # + def path_type_handler(self,event): + self.parent.path_type = self.path_types[event.GetSelection()] + self.parent.update_panels() + # + # parent call to update panel + # + def update_panel(self): + index = self.path_types.index(self.parent.path_type) + self.path_type.SetSelection(index) + self.sizer.Detach(self.type_panel) + self.type_panel.Hide() + self.sizer.Add(self.type_panels[index],(3,0),flag=(wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_TOP)) + self.type_panels[index].Show() + self.type_panel = self.type_panels[index] + # + # check for 3D info in PNG + # + if (self.parent.png_file != ''): + temp_name = self.parent.tmp+'png_info' + command = 'png_size '+'\"'+self.parent.png_file+'\"'+' > '+'\"'+temp_name+'\"' + os.system(command) + output_file = open(temp_name,'r') + info = output_file.read() + command = 'rm '+'\"'+temp_name+'\"' + os.system(command) + start = 3+string.find(info,'dz:') + if (start > 2): + # + # 3D info found, update + # + end = string.find(info,'mm',start)-1 + self.parent.zmin = -float(info[start:end])/self.parent.units + self.parent.zmax = 0 + if (self.parent.path_type == "3D rough"): + self.ztop_rough.SetValue(str(self.parent.units*self.parent.zmax)) + self.zbot_rough.SetValue(str(self.parent.units*self.parent.zmin)) + elif (self.parent.path_type == "3D finish"): + self.ztop_finish.SetValue(str(self.parent.units*self.parent.zmax)) + self.zbot_finish.SetValue(str(self.parent.units*self.parent.zmin)) + self.Layout() + self.Fit() + # + # parent call to update size + # + def update_size(self,sizex,sizey): + self.Layout() + self.Fit() diff --git a/src/py/panel_png_path_halftone.py b/src/py/panel_png_path_halftone.py new file mode 100644 index 0000000..323121a --- /dev/null +++ b/src/py/panel_png_path_halftone.py @@ -0,0 +1,121 @@ +# +# panel_png_path_halftone.py +# make .path from .png halftone +# +# Neil Gershenfeld +# CBA MIT 3/12/11 +# +# (c) Massachusetts Institute of Technology 2011 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,os +# +# panel class +# +class png_path_halftone_panel(wx.Panel): + def __init__(self,parent): + self.parent = parent + self.parent.png_file = '' + # + # make path + # + def make_path(event): + if (self.parent.png_file == ''): + print "png_path: oops -- must make .png first" + return + self.parent.path_file = self.parent.tmp+self.parent.rootname+'.path' + path_png = self.parent.tmp+self.parent.rootname+'.path.png' + threshold = self.threshold.GetValue() + points = self.points.GetValue() + spot_size = self.size.GetValue() + spacing = self.spacing.GetValue() + offset = self.offset.GetValue() + invert = self.invert.GetValue() + if (invert == False): + invert = '0' + else: + invert = '1' + command = 'png_halftone '+'\"'+self.parent.filename+'\"'+' '+'\"'+self.parent.path_file+'\"'+' '+threshold+' '+points+' '+spot_size+' '+spacing+' '+offset+' '+invert + print command + os.system(command) + command = 'path_png '+'\"'+self.parent.path_file+'\"'+' '+'\"'+path_png+'\"' + print command + os.system(command) + command = 'png_scale '+'\"'+path_png+'\"'+' '+'\"'+path_png+'\"'+' 1 0' + print command + os.system(command) + path_image = wx.Image(path_png) + path_image = wx.Image.Blur(path_image,1) + (nx,ny) = path_image.GetSize() + ratio = float(ny)/float(nx) + if (ratio > 1): + self.parent.ysize = self.parent.size + self.parent.xsize = self.parent.size/ratio + else: + self.parent.ysize = self.parent.size*ratio + self.parent.xsize = self.parent.size + wx.Image.Rescale(path_image,self.parent.xsize,self.parent.ysize,quality=wx.IMAGE_QUALITY_HIGH) + path_bitmap = path_image.ConvertToBitmap() + self.bitmap.SetBitmap(path_bitmap) + self.bitmap.Show() + self.parent.Layout() + self.parent.Fit() + # + # panel + # + wx.Panel.__init__(self,parent) + self.sizer = wx.GridBagSizer(10,10) + self.SetSizer(self.sizer) + # + # label + # + label = wx.StaticText(self,label='to: halftone path') + bold_font = wx.Font(10,wx.DEFAULT,wx.NORMAL,wx.BOLD) + label.SetFont(bold_font) + self.sizer.Add(label,(0,0),span=(1,4),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + # image + # + image = wx.ArtProvider.GetBitmap(wx.ART_QUESTION, wx.ART_OTHER, (self.parent.size,self.parent.size)) + self.bitmap = wx.StaticBitmap(self,-1,image) + self.sizer.Add(self.bitmap,(1,0),span=(1,4),flag=(wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL)) + self.bitmap.Hide() + # + # controls + # + make = wx.Button(self,label='make .path') + make.Bind(wx.EVT_BUTTON,make_path) + self.sizer.Add(make,(2,0),span=(1,4),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + self.sizer.Add(wx.StaticText(self,label='minimum spot radius (pixels):'),(3,0),flag=(wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT)) + self.threshold = wx.TextCtrl(self,-1,'1') + self.sizer.Add(self.threshold,(3,1)) + # + self.sizer.Add(wx.StaticText(self,label='points per spot:'),(3,2),flag=(wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT)) + self.points = wx.TextCtrl(self,-1,'8') + self.sizer.Add(self.points,(3,3)) + # + self.sizer.Add(wx.StaticText(self,label='max spot size (mm):'),(4,0),flag=(wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT)) + self.size = wx.TextCtrl(self,-1,'1') + self.sizer.Add(self.size,(4,1)) + # + self.sizer.Add(wx.StaticText(self,label='spot spacing (1 = spot size):'),(4,2),flag=(wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT)) + self.spacing = wx.TextCtrl(self,-1,'1') + self.sizer.Add(self.spacing,(4,3)) + # + self.sizer.Add(wx.StaticText(self,label='row offset (1 = spot size):'),(5,0),flag=(wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT)) + self.offset = wx.TextCtrl(self,-1,'0.5') + self.sizer.Add(self.offset,(5,1)) + # + self.invert = wx.CheckBox(self,-1,"invert image") + self.invert.SetValue(False) + self.sizer.Add(self.invert,(5,2)) + # + self.parent.path_type = "2D" + # + # fit + # + self.Fit() diff --git a/src/py/panel_png_png.py b/src/py/panel_png_png.py new file mode 100644 index 0000000..0b26670 --- /dev/null +++ b/src/py/panel_png_png.py @@ -0,0 +1,100 @@ +# +# panel_png_png.py +# make .pngfrom .png +# +# Neil Gershenfeld +# CBA MIT 7/24/11 +# +# (c) Massachusetts Institute of Technology 2011 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,os +# +# panel class +# +class png_png_panel(wx.Panel): + def __init__(self,parent): + self.parent = parent + self.parent.png_file = '' + # + # make png + # + def make_path(event): + if (self.parent.png_file == ''): + print "png_png: oops -- must make .png first" + return + png_png_file = self.parent.tmp+self.parent.rootname+'.png' + threshold = self.threshold.GetValue() + if self.distances.GetValue(): + distances = '1' + else: + distances = '0' + command = 'png_distances '+'\"'+self.parent.png_file+'\"'+' '+'\"'+png_png_file+'\"'+' '+threshold+' '+distances + print command + os.system(command) + png_image = wx.Image(png_png_file) + png_image = wx.Image.Blur(png_image,1) + (nx,ny) = png_image.GetSize() + ratio = float(ny)/float(nx) + if (ratio > 1): + self.parent.ysize = self.parent.size + self.parent.xsize = self.parent.size/ratio + else: + self.parent.ysize = self.parent.size*ratio + self.parent.xsize = self.parent.size + wx.Image.Rescale(png_image,self.parent.xsize,self.parent.ysize,quality=wx.IMAGE_QUALITY_HIGH) + path_bitmap = png_image.ConvertToBitmap() + self.bitmap.SetBitmap(path_bitmap) + self.bitmap.Show() + self.parent.Layout() + self.parent.Fit() + # + # panel + # + wx.Panel.__init__(self,parent) + self.sizer = wx.GridBagSizer(10,10) + self.SetSizer(self.sizer) + # + # label + # + label = wx.StaticText(self,label='to: png') + bold_font = wx.Font(10,wx.DEFAULT,wx.NORMAL,wx.BOLD) + label.SetFont(bold_font) + self.sizer.Add(label,(0,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + # image + # + image = wx.ArtProvider.GetBitmap(wx.ART_QUESTION, wx.ART_OTHER, (self.parent.size,self.parent.size)) + self.bitmap = wx.StaticBitmap(self,-1,image) + self.sizer.Add(self.bitmap,(1,0),flag=(wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL)) + self.bitmap.Hide() + # + # controls + # + make = wx.Button(self,label='make .png') + make.Bind(wx.EVT_BUTTON,make_path) + self.sizer.Add(make,(2,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + threshold_panel = wx.Panel(self) + threshold_sizer = wx.GridBagSizer(10,10) + threshold_panel.SetSizer(threshold_sizer) + threshold_sizer.Add(wx.StaticText(threshold_panel,label='threshold (0-1):'),(0,0),flag=(wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT)) + self.threshold = wx.TextCtrl(threshold_panel,-1,'0.5') + threshold_sizer.Add(self.threshold,(0,1)) + self.sizer.Add(threshold_panel,(3,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + type_panel = wx.Panel(self) + type_sizer = wx.GridBagSizer(10,10) + type_panel.SetSizer(type_sizer) + self.interior = wx.RadioButton(type_panel,label='interior',style=wx.RB_GROUP) + type_sizer.Add(self.interior,(0,0)) + self.distances = wx.RadioButton(type_panel,label='distances') + type_sizer.Add(self.distances,(0,1)) + self.sizer.Add(type_panel,(4,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + # fit + # + self.Fit() diff --git a/src/py/panel_stl.py b/src/py/panel_stl.py new file mode 100644 index 0000000..80b01e7 --- /dev/null +++ b/src/py/panel_stl.py @@ -0,0 +1,153 @@ +# +# panel_stl.py +# read .stl +# +# Neil Gershenfeld +# CBA MIT 3/6/11 +# +# (c) Massachusetts Institute of Technology 2011 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,string,os,sys +from panel_path import path_panel +# +# panel class +# +class stl_panel(wx.Panel): + def __init__(self,parent): + self.parent = parent + self.parent.stl_file = '' + # + # get stl info + # + def stl_info(name): + # + # construct command + # + temp_name = self.parent.tmp+'stl_info' + command = 'stl_info '+'\"'+name+'\"'+' > '+'\"'+temp_name+'\"' + os.system(command) + output_file = open(temp_name,'r') + output = output_file.read() + output_file.close() + print output + if (string.find(output,'must be binary') != -1): + sys.exit(-1) + command = 'rm '+'\"'+temp_name+'\"' + os.system(command) + # + # read limits + # + start = 6+string.find(output,'xmax:') + space = string.find(output,' ',start) + end = string.find(output,'ymin',space)-4 + self.parent.xmin = float(output[start:space]) + self.parent.xmax = float(output[1+space:end]) + start = 6+string.find(output,'ymax:') + space = string.find(output,' ',start) + end = string.find(output,'zmin',space)-4 + self.parent.ymin = float(output[start:space]) + self.parent.ymax = float(output[1+space:end]) + start = 6+string.find(output,'zmax:') + space = string.find(output,' ',start) + self.parent.zmin = float(output[start:space]) + self.parent.zmax = float(output[space+1:-1]) + # + # move origin to top corner + # + # + self.parent.xmax = self.parent.xmax - self.parent.xmin + self.parent.xmin = 0 + self.parent.ymax = self.parent.ymax - self.parent.ymin + self.parent.ymin = 0 + self.parent.zmin = self.parent.zmin - self.parent.zmax + self.parent.zmax = 0 + # + return output + # + # load file + # + def load_file(event): + # + # get file name + # + if (self.parent.basename == ""): + return + pos = string.find(self.parent.basename,".stl") + if (pos == -1): + pos = string.find(self.parent.basename,".STL") + if (pos == -1): + print 'stl_panel: oops -- must be .stl' + sys.exit() + self.parent.rootname = self.parent.basename[:pos] + self.parent.stl_file = self.parent.filename + # + # get file info + # + info = stl_info(self.parent.filename) + self.info.SetLabel(info) + temp_name = self.parent.tmp+'stl.path' + # + # draw + # + if (self.path_viewer.view_type != "none"): + units = 1.0 + resolution = 100 + command = 'stl_path '+'\"'+self.parent.stl_file+'\"'+' '+'\"'+temp_name+'\"'+' '+str(units)+' '+str(resolution) + print command + ret = os.system(command) + if (ret == 0): + self.path_viewer.draw(temp_name) + # + # fit + # + self.parent.Layout() + self.parent.Fit() + # + # select file + # + def select_file(event): + dialog = wx.FileDialog(self, "Choose a file", os.getcwd(), "", "*.stl;*.STL", wx.OPEN) + if (dialog.ShowModal() == wx.ID_OK): + self.parent.filename = dialog.GetPath() + self.parent.basename = os.path.basename(self.parent.filename) + load_file(0) + # + # panel + # + wx.Panel.__init__(self,parent) + self.sizer = wx.GridBagSizer(10,10) + self.SetSizer(self.sizer) + # + # label + # + label = wx.StaticText(self,label='from: stl') + bold_font = wx.Font(10,wx.DEFAULT,wx.NORMAL,wx.BOLD) + label.SetFont(bold_font) + self.sizer.Add(label,(0,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + # image + # + self.path_viewer = path_panel(self) + self.sizer.Add(self.path_viewer,(1,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + self.path_viewer.view_type = "segments" + # + # controls + # + load = wx.Button(self,label='load .stl') + load.Bind(wx.EVT_BUTTON,select_file) + self.sizer.Add(load,(2,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + self.info = wx.StaticText(self,label="") + self.sizer.Add(self.info,(3,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + # file + # + load_file(0) + # + # fit + # + self.Fit() diff --git a/src/py/panel_stl_png.py b/src/py/panel_stl_png.py new file mode 100644 index 0000000..ad92d7b --- /dev/null +++ b/src/py/panel_stl_png.py @@ -0,0 +1,210 @@ +# +# panel_stl_png.py +# make .png from .stl +# +# Neil Gershenfeld +# CBA MIT 3/6/11 +# +# (c) Massachusetts Institute of Technology 2012 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,os,string +# +# panel class +# +class stl_png_panel(wx.Panel): + def __init__(self,parent): + self.parent = parent + self.parent.png_file = '' + # + # get png info + # + def png_info(name): + temp_name = self.parent.tmp+'png_info' + command = 'png_size '+'\"'+name+'\"'+' > '+'\"'+temp_name+'\"' + os.system(command) + output_file = open(temp_name,'r') + output = output_file.read() + command = 'rm '+'\"'+temp_name+'\"' + os.system(command) + return output + # + # make png + # + def make_png(event): + if (self.parent.stl_file == ''): + print "panel_stl_png: oops -- must read .stl first" + return + # + # construct command + # + self.parent.png_file = self.parent.tmp+self.parent.rootname+'.png' + units = self.units.GetValue() + self.parent.units = float(units) + resolution = self.resolution.GetValue() + if (self.x.GetValue()): + axis = 'x' + elif (self.X.GetValue()): + axis = 'X' + elif (self.y.GetValue()): + axis = 'y' + self.parent.zmin = 1234 + elif (self.Y.GetValue()): + axis = 'Y' + elif (self.z.GetValue()): + axis = 'z' + elif (self.Z.GetValue()): + axis = 'Z' + command = 'stl_png '+'\"'+self.parent.stl_file+'\"'+' '+'\"'+self.parent.png_file+'\"'+' '+units+' '+resolution+' '+axis + # + # generate PNG + # + print command + os.system(command) + # + # get image info + # + info = png_info(self.parent.png_file) + self.info.SetLabel(info) + start = 3+string.find(info,'dx:') + end = string.find(info,'mm',start)-1 + self.parent.xmin = 0 + self.parent.xmax = float(info[start:end])/self.parent.units + start = 3+string.find(info,'dy:') + end = string.find(info,'mm',start)-1 + self.parent.ymin = 0 + self.parent.ymax = float(info[start:end])/self.parent.units + start = 3+string.find(info,'dz:') + end = string.find(info,'mm',start)-1 + self.parent.zmin = -float(info[start:end])/self.parent.units + self.parent.zmax = 0 + # + # update panels + # + parent.update_panels() + # + # show image + # + png_image = wx.Image(self.parent.png_file) + (nx,ny) = png_image.GetSize() + ratio = float(ny)/float(nx) + if (ratio > 1): + self.parent.ysize = self.parent.size + self.parent.xsize = self.parent.size/ratio + else: + self.parent.ysize = self.parent.size*ratio + self.parent.xsize = self.parent.size + wx.Image.Rescale(png_image,self.parent.xsize,self.parent.ysize) + png_bitmap = png_image.ConvertToBitmap() + self.bitmap.SetBitmap(png_bitmap) + self.bitmap.Show() + self.parent.Layout() + self.parent.Fit() + # + # panel + # + wx.Panel.__init__(self,parent) + self.sizer = wx.GridBagSizer(10,10) + self.SetSizer(self.sizer) + # + # label + # + label = wx.StaticText(self,label='to: png') + bold_font = wx.Font(10,wx.DEFAULT,wx.NORMAL,wx.BOLD) + label.SetFont(bold_font) + self.sizer.Add(label,(0,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + # image + # + image = wx.ArtProvider.GetBitmap(wx.ART_QUESTION, wx.ART_OTHER, (self.parent.size,self.parent.size)) + self.bitmap = wx.StaticBitmap(self,-1,image) + self.sizer.Add(self.bitmap,(1,0),flag=(wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL)) + self.bitmap.Hide() + def mouse_move(event): + if (self.x.GetValue()): + y = self.parent.units*(self.parent.ymin + (self.parent.ymax-self.parent.ymin)*event.GetX()/float(self.parent.xsize)) + z = self.parent.units*(self.parent.zmin + (self.parent.zmax-self.parent.zmin)*(self.parent.ysize-event.GetY())/float(self.parent.ysize)) + self.position.SetLabel("y: %.3f mm z: %.3f mm"%(y,z)) + elif (self.X.GetValue()): + y = self.parent.units*(self.parent.ymin + (self.parent.ymax-self.parent.ymin)*event.GetX()/float(self.parent.xsize)) + z = self.parent.units*(self.parent.zmin + (self.parent.zmax-self.parent.zmin)*(self.parent.ysize-event.GetY())/float(self.parent.ysize)) + self.position.SetLabel("y: %.3f mm z: %.3f mm"%(y,z)) + elif (self.y.GetValue()): + z = self.parent.units*(self.parent.zmin + (self.parent.zmax-self.parent.zmin)*event.GetX()/float(self.parent.xsize)) + x = self.parent.units*(self.parent.xmin + (self.parent.xmax-self.parent.xmin)*(self.parent.ysize-event.GetY())/float(self.parent.ysize)) + self.position.SetLabel("x: %.3f mm z: %.3f mm"%(x,z)) + elif (self.Y.GetValue()): + z = self.parent.units*(self.parent.zmin + (self.parent.zmax-self.parent.zmin)*(self.parent.xsize-event.GetX())/float(self.parent.xsize)) + x = self.parent.units*(self.parent.ymin + (self.parent.ymax-self.parent.ymin)*(self.parent.ysize-event.GetY())/float(self.parent.ysize)) + self.position.SetLabel("x: %.3f mm z: %.3f mm"%(x,z)) + elif (self.z.GetValue()): + x = self.parent.units*(self.parent.xmin + (self.parent.xmax-self.parent.xmin)*event.GetX()/float(self.parent.xsize)) + y = self.parent.units*(self.parent.ymin + (self.parent.ymax-self.parent.ymin)*(self.parent.ysize-event.GetY())/float(self.parent.ysize)) + self.position.SetLabel("x: %.3f mm y: %.3f mm"%(x,y)) + elif (self.Z.GetValue()): + x = self.parent.units*(self.parent.xmin + (self.parent.xmax-self.parent.xmin)*(self.parent.xsize-event.GetX())/float(self.parent.xsize)) + y = self.parent.units*(self.parent.ymin + (self.parent.ymax-self.parent.ymin)*(self.parent.ysize-event.GetY())/float(self.parent.ysize)) + self.position.SetLabel("x: %.3f mm y: %.3f mm"%(x,y)) + self.Layout() + self.Fit() + self.bitmap.Bind(wx.EVT_MOTION,mouse_move) + # + # controls + # + make = wx.Button(self,label='make .png') + make.Bind(wx.EVT_BUTTON,make_png) + self.sizer.Add(make,(2,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + units_panel = wx.Panel(self) + units_panel_sizer = wx.GridBagSizer(10,10) + units_panel.SetSizer(units_panel_sizer) + units_panel_sizer.Add(wx.StaticText(units_panel,label='stl units (mm/unit):'),(0,0),flag=wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) + self.units = wx.TextCtrl(units_panel,-1,'1') + units_panel_sizer.Add(self.units,(0,1),flag=wx.ALIGN_LEFT) + self.sizer.Add(units_panel,(3,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + res_panel = wx.Panel(self) + res_panel_sizer = wx.GridBagSizer(10,10) + res_panel.SetSizer(res_panel_sizer) + res_panel_sizer.Add(wx.StaticText(res_panel,label='png resolution (pixels/mm):'),(0,0),flag=wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) + self.resolution = wx.TextCtrl(res_panel,-1,'25') + res_panel_sizer.Add(self.resolution,(0,1),flag=wx.ALIGN_LEFT) + self.sizer.Add(res_panel,(4,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + axis_panel = wx.Panel(self) + axis_sizer = wx.GridBagSizer(10,10) + axis_panel.SetSizer(axis_sizer) + axis_sizer.Add(wx.StaticText(axis_panel,label='side:'),(0,0),flag=wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) + self.x = wx.RadioButton(axis_panel,label='x',style=wx.RB_GROUP) + axis_sizer.Add(self.x,(0,1)) + self.X = wx.RadioButton(axis_panel,label='-x') + axis_sizer.Add(self.X,(0,2)) + self.y = wx.RadioButton(axis_panel,label='y') + axis_sizer.Add(self.y,(0,3)) + self.Y = wx.RadioButton(axis_panel,label='-y') + axis_sizer.Add(self.Y,(0,4)) + self.z = wx.RadioButton(axis_panel,label='z') + axis_sizer.Add(self.z,(0,5)) + self.Z = wx.RadioButton(axis_panel,label='-z') + axis_sizer.Add(self.Z,(0,6)) + self.sizer.Add(axis_panel,(5,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + self.z.SetValue(True) + # + self.position= wx.StaticText(self,label="") + self.sizer.Add(self.position,(6,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + self.info = wx.StaticText(self,label="") + self.sizer.Add(self.info,(7,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + # fit + # + self.Fit() + # + # parent call to update panel for workflow + # + def update_panel(self): + self.units.SetValue(str(self.parent.units)) + diff --git a/src/py/panel_svg.py b/src/py/panel_svg.py new file mode 100644 index 0000000..531e03b --- /dev/null +++ b/src/py/panel_svg.py @@ -0,0 +1,104 @@ +# +# panel_svg.py +# read and edit .svg +# +# Neil Gershenfeld +# CBA MIT 7/13/11 +# +# (c) Massachusetts Institute of Technology 2011 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# imports +# +import wx,string,os,sys +# +# panel class +# +class svg_panel(wx.Panel): + def __init__(self,parent): + self.parent = parent + # + # load file + # + def load_file(event): + if (self.parent.basename == ""): + return + pos = string.find(self.parent.basename,".svg") + if (pos == -1): + pos = string.find(self.parent.basename,".SVG") + if (pos == -1): + print 'svg_panel: oops -- must be .svg' + sys.exit() + self.parent.rootname = self.parent.basename[:pos] + svg_file = open(self.parent.filename,'r') + svg_string = svg_file.read() + svg_file.close() + self.text.SetValue(svg_string) + # + # select file + # + def select_file(event): + dialog = wx.FileDialog(self, "Choose a file", os.getcwd(), "", "*.svg", wx.OPEN) + if (dialog.ShowModal() == wx.ID_OK): + self.parent.filename = dialog.GetPath() + self.parent.basename = os.path.basename(self.parent.filename) + pos = string.find(self.parent.basename,".svg") + if (pos == -1): + print 'svg_panel: oops -- must be .svg' + sys.exit() + else: + self.parent.rootname = self.parent.basename[:pos] + svg_file = open(self.parent.filename,'r') + svg_string = svg_file.read() + svg_file.close() + self.text.SetValue(svg_string) + # + # save file + # + def save_file(event): + result = wx.SaveFileSelector('.svg','.svg',self.parent.filename) + if (result != ''): + svg_file = open(result,'w') + svg_file.write(self.text.GetValue()) + svg_file.close() + # + # panel + # + wx.Panel.__init__(self,parent) + self.sizer = wx.GridBagSizer(10,10) + self.SetSizer(self.sizer) + # + # label + # + label = wx.StaticText(self,label='from: svg') + bold_font = wx.Font(10,wx.DEFAULT,wx.NORMAL,wx.BOLD) + label.SetFont(bold_font) + self.sizer.Add(label,(0,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + # text + # + self.text = wx.TextCtrl(self,-1,'',size=(self.parent.size,self.parent.size),style=wx.TE_MULTILINE) + self.sizer.Add(self.text,(1,0),flag=(wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL)) + # + # controls + # + load = wx.Button(self,label='load .svg') + load.Bind(wx.EVT_BUTTON,select_file) + self.sizer.Add(load,(2,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + rload = wx.Button(self,label='reload .svg') + rload.Bind(wx.EVT_BUTTON,load_file) + self.sizer.Add(rload,(3,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + save = wx.Button(self,label='save .svg') + save.Bind(wx.EVT_BUTTON,save_file) + self.sizer.Add(save,(4,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + # file + # + load_file(0) + # + # fit + # + self.Fit() diff --git a/src/py/panel_svg_path.py b/src/py/panel_svg_path.py new file mode 100644 index 0000000..9f8a1e6 --- /dev/null +++ b/src/py/panel_svg_path.py @@ -0,0 +1,189 @@ +# +# panel_svg_path.py +# make .path from .svg +# +# Neil Gershenfeld 9/8/13 +# (c) Massachusetts Institute of Technology 2013 +# +# This work may be reproduced, modified, distributed, +# performed, and displayed for any purpose, but must +# acknowledge the fab modules project. Copyright is +# retained and must be preserved. The work is provided +# as is; no warranty is provided, and users accept all +# liability. +# + +# +# imports +# +import wx,os,string +from panel_path import path_panel +# +# panel class +# +class svg_path_panel(wx.Panel): + def __init__(self,parent): + self.parent = parent + self.parent.zmin = 0 + self.parent.zmax = 0 + self.parent.units = 1 + # + # make path + # + def make_path(event): + if (self.parent.rootname == ''): + return + tmp_svg_file = self.parent.tmp+self.parent.rootname+'.svg' + svg_file = open(tmp_svg_file,'w') + svg_file.write(self.parent.svg_panel.text.GetValue()) + svg_file.close() + self.parent.path_file = self.parent.tmp+self.parent.rootname+'.path' + path_png = self.parent.tmp+self.parent.rootname+'.path.png' + if (self.path_type.GetValue() == '3D'): + scale = self.scale_3D.GetValue() + points = self.points_3D.GetValue() + resolution = self.resolution_3D.GetValue() + zmin = self.zmin.GetValue() + zmax = self.zmax.GetValue() + command = 'svg_path '+'\"'+tmp_svg_file+'\"'+' '+'\"'+self.parent.path_file+'\"'+' '+scale+' '+points+' '+resolution+' '+zmin+' '+zmax + print command + os.system(command) + temp_name = self.parent.tmp+'path_info' + command = 'path_info '+'\"'+self.parent.path_file+'\"'+' > '+'\"'+temp_name+'\"' + os.system(command) + output_file = open(temp_name,'r') + output = output_file.read() + output_file.close() + command = 'rm '+'\"'+temp_name+'\"' + os.system(command) + self.info_3D.SetLabel(output) + elif (self.path_type.GetValue() == '2D'): + scale = self.scale_2D.GetValue() + points = self.points_2D.GetValue() + resolution = self.resolution_2D.GetValue() + command = 'svg_path '+'\"'+tmp_svg_file+'\"'+' '+'\"'+self.parent.path_file+'\"'+' '+scale+' '+points+' '+resolution + print command + os.system(command) + temp_name = self.parent.tmp+'path_info' + command = 'path_info '+'\"'+self.parent.path_file+'\"'+' > '+'\"'+temp_name+'\"' + os.system(command) + output_file = open(temp_name,'r') + output = output_file.read() + output_file.close() + command = 'rm '+'\"'+temp_name+'\"' + os.system(command) + self.info_2D.SetLabel(output) + self.path_viewer.draw(self.parent.path_file) + self.parent.Layout() + self.parent.Fit() + # + # panel + # + wx.Panel.__init__(self,parent) + self.sizer = wx.GridBagSizer(10,10) + self.SetSizer(self.sizer) + # + # label + # + label = wx.StaticText(self,label='to: path') + bold_font = wx.Font(10,wx.DEFAULT,wx.NORMAL,wx.BOLD) + label.SetFont(bold_font) + self.sizer.Add(label,(0,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + # image + # + self.path_viewer = path_panel(self) + self.sizer.Add(self.path_viewer,(1,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + # + # controls + # + make_panel = wx.Panel(self) + make_sizer = wx.GridBagSizer(10,10) + make_panel.SetSizer(make_sizer) + make_button = wx.Button(make_panel,label='make .path') + make_button.Bind(wx.EVT_BUTTON,make_path) + make_sizer.Add(make_button,(0,0),flag=wx.ALIGN_CENTER_HORIZONTAL) + make_sizer.Add(wx.StaticText(make_panel,label='type:'),(0,1),flag=(wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)) + self.path_types = ["2D","3D"] + self.path_type = wx.ComboBox(make_panel,size=(100,-1),value="2D",choices=self.path_types,style=wx.CB_READONLY) + self.parent.path_type = "2D" + self.path_type.Bind(wx.EVT_COMBOBOX,self.path_type_handler) + make_sizer.Add(self.path_type,(0,2),flag=wx.ALIGN_LEFT) + self.sizer.Add(make_panel,(2,0),flag=(wx.ALIGN_CENTER_HORIZONTAL)) + # + # 2D panel + # + panel_2D = wx.Panel(self) + sizer_2D = wx.GridBagSizer(10,10) + panel_2D.SetSizer(sizer_2D) + # + sizer_2D.Add(wx.StaticText(panel_2D,label='scale factor'),(0,1),flag=(wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT)) + self.scale_2D = wx.TextCtrl(panel_2D,-1,'1.0') + sizer_2D.Add(self.scale_2D,(0,2),flag=wx.ALIGN_LEFT) + # + sizer_2D.Add(wx.StaticText(panel_2D,label='curve points'),(1,0),flag=(wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT)) + self.points_2D = wx.TextCtrl(panel_2D,-1,'10') + sizer_2D.Add(self.points_2D,(1,1),flag=wx.ALIGN_LEFT) + self.resolution_2D = wx.TextCtrl(panel_2D,-1,'10000') + sizer_2D.Add(self.resolution_2D,(1,2),flag=wx.ALIGN_RIGHT) + sizer_2D.Add(wx.StaticText(panel_2D,label='path resolution'),(1,3),flag=(wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT)) + # + self.info_2D = wx.StaticText(panel_2D,label="") + sizer_2D.Add(self.info_2D,(2,0),span=(1,4),flag=(wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER_HORIZONTAL)) + # + self.sizer.Add(panel_2D,(3,0),flag=(wx.ALIGN_CENTER_HORIZONTAL)) + self.type_panel = panel_2D + self.type_panels = [panel_2D] + # + # 3D panel + # + panel_3D = wx.Panel(self) + sizer_3D = wx.GridBagSizer(10,10) + panel_3D.SetSizer(sizer_3D) + # + sizer_3D.Add(wx.StaticText(panel_3D,label='scale factor'),(0,1),flag=(wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT)) + self.scale_3D = wx.TextCtrl(panel_3D,-1,'1.0') + sizer_3D.Add(self.scale_3D,(0,2),flag=wx.ALIGN_LEFT) + # + sizer_3D.Add(wx.StaticText(panel_3D,label='curve points'),(1,0),flag=(wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT)) + self.points_3D = wx.TextCtrl(panel_3D,-1,'10') + sizer_3D.Add(self.points_3D,(1,1),flag=wx.ALIGN_LEFT) + self.resolution_3D = wx.TextCtrl(panel_3D,-1,'10000') + sizer_3D.Add(self.resolution_3D,(1,2),flag=wx.ALIGN_RIGHT) + sizer_3D.Add(wx.StaticText(panel_3D,label='path resolution'),(1,3),flag=(wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT)) + # + sizer_3D.Add(wx.StaticText(panel_3D,label='min intensity z (mm)'),(2,0),flag=(wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT)) + self.zmin = wx.TextCtrl(panel_3D,-1,'0') + sizer_3D.Add(self.zmin,(2,1),flag=wx.ALIGN_LEFT) + self.zmax = wx.TextCtrl(panel_3D,-1,'0') + sizer_3D.Add(self.zmax,(2,2),flag=wx.ALIGN_RIGHT) + sizer_3D.Add(wx.StaticText(panel_3D,label='max intensity z (mm)'),(2,3),flag=(wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT)) + # + self.info_3D = wx.StaticText(panel_3D,label="") + sizer_3D.Add(self.info_3D,(3,0),span=(1,4),flag=(wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER_HORIZONTAL)) + # + panel_3D.Hide() + self.type_panels.append(panel_3D) + # + # fit + # + self.Fit() + # + # path type handler + # + def path_type_handler(self,event): + self.parent.path_type = self.path_types[event.GetSelection()] + self.parent.update_panels() + # + # parent call to update panel + # + def update_panel(self): + index = self.path_types.index(self.parent.path_type) + self.path_type.SetSelection(index) + self.sizer.Detach(self.type_panel) + self.type_panel.Hide() + self.sizer.Add(self.type_panels[index],(3,0),flag=(wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_TOP)) + self.type_panels[index].Show() + self.type_panel = self.type_panels[index] + self.Layout() + self.Fit() diff --git a/src/py/png_l.py b/src/py/png_l.py new file mode 100755 index 0000000..32f9b16 --- /dev/null +++ b/src/py/png_l.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python +# +# png_l.py +# converted a PNG image to a grayscale integer lattice +# png_l input.png output.l +# +# Neil Gershenfeld +# CBA MIT 7/2/10 +# +# (c) Massachusetts Institute of Technology 2010 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# todo +# C image library port +# pipe I/O +# variable bit depth +# z layers +# + +import sys, struct, Image +from numpy import * + +# +# read command line +# + +if len(sys.argv) == 3: + infile_name = sys.argv[1] + outfile_name = sys.argv[2] +else: + print "command line: png_l input.png output.l" + sys.exit() + + +# +# read image +# + +image = Image.open(infile_name) +(nx,ny) = image.size +nz = 1 +info = image.info +if ('dpi' in info): + (xdpi,ydpi) = info['dpi'] +else: + (xdpi,ydpi) = (1,1) +print "nx:",nx,"ny:",ny,"xdpi:",xdpi,"ydpi",ydpi + +# +# convert to grayscale +# + +image = image.convert("L") + +# +# set variables +# + +x0 = 0 +y0 = 0 +dx = nx/float(xdpi) +dy = ny/float(ydpi) +nz = 1 +z0 = 0 +dz = 0 +bytes_per_pixel = 1 + +# +# construct file buffer +# + +buf = struct.pack('I',nx) +buf += struct.pack('I',ny) +buf += struct.pack('I',nz) +buf += struct.pack('f',dx) +buf += struct.pack('f',dy) +buf += struct.pack('f',dz) +buf += struct.pack('f',x0) +buf += struct.pack('f',y0) +buf += struct.pack('f',z0) +buf += struct.pack('I',bytes_per_pixel) + +for y in range(ny): + for x in range(nx): + buf += struct.pack('B',image.getpixel((x,y))) + +# +# write to file +# + +outfile = open(outfile_name,"wb") +outfile.write(buf) +outfile.close() diff --git a/src/scripts/CMakeLists.txt b/src/scripts/CMakeLists.txt new file mode 100644 index 0000000..9f5492c --- /dev/null +++ b/src/scripts/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 2.6) + +set(SCRIPTS cad_png cad_view path_view rml_move + fab_send fab_update cad_math math_png_py math_stl_py + png_tile eagle_png + + CACHE STRING "Script file list") + +if( ${CMAKE_PROJECT_NAME} MATCHES fabmod ) + install(PROGRAMS ${SCRIPTS} DESTINATION ${PROJECT_SOURCE_DIR}/../bin) +endif( ${CMAKE_PROJECT_NAME} MATCHES fabmod ) diff --git a/src/scripts/cad_math b/src/scripts/cad_math new file mode 100755 index 0000000..07cd904 --- /dev/null +++ b/src/scripts/cad_math @@ -0,0 +1,92 @@ +#!/usr/bin/env python +# +# cad_math +# convert .cad to .math +# +# Neil Gershenfeld +# CBA MIT 4/18/11 +# +# (c) Massachusetts Institute of Technology 2010 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# + +import sys + +# Some older files need these imported +from math import * +from string import * + +class cad_variables: + # + # cad variables + # + def __init__(self): + self.xmin = 0 # minimum x value to render + self.xmax = 0 # maximum x value to render + self.ymin = 0 # minimum y value to render + self.ymax = 0 # maximum y value to render + self.zmin = 0 # minimum z value to render + self.zmax = 0 # maximum z value to render + self.layers = [] # optional number of layers to render + self.function = '' # cad function + self.labels = [] # display labels + self.mm_per_unit = 1.0 # file units + self.type = 'Boolean' # math string type + +cad = cad_variables() + +# +# command line args +# + +if (len(sys.argv) < 3): + print "command line: cad_math in.cad out.math [args]" + print " in.cad = input design file" + print " out.math = output math string file" + print " args = arguments to cad script" + print " (delivered in sys.argv)" + sys.exit() + + +# This line allows python to find other modules in the same folder +# as a .cad file. +sys.path.append('/'.join(sys.argv[1].split('/')[:-1])) + +# This allows for multi-file cad documents (e.g. a bunch of individual +# parts that all look to a set of common constants) + +# read & evaluate .cad file +input_file_name = sys.argv[1] +output_file_name = sys.argv[2] + +sys.argv = [input_file_name] + sys.argv[3:] +with open(input_file_name, 'r') as f: + exec(f.read()) + +print "read "+input_file_name + +# +# write .math file +# + +output_file = open(output_file_name,'wb') +output_file.write("format: %s\n" % cad.type) +output_file.write("mm per unit: %f\n" % cad.mm_per_unit) +output_file.write("dx dy dz: %f %f %f\n" % (cad.xmax-cad.xmin, + cad.ymax-cad.ymin, + cad.zmax-cad.zmin)) +output_file.write("xmin ymin zmin: %f %f %f\n" % (cad.xmin, + cad.ymin, + cad.zmin)) +output_file.write("expression: %s" % cad.function) + +print "write "+output_file_name +print " type: "+cad.type +print " units:",cad.mm_per_unit +print " dx: %f, dy: %f, dz: %f"%(cad.xmax-cad.xmin,cad.ymax-cad.ymin,cad.zmax-cad.zmin) +print " xmin: %f, ymin: %f, zmin: %f"%(cad.xmin,cad.ymin,cad.zmin) +if (cad.layers != []): + print " layers: %d"%cad.layers + diff --git a/src/scripts/cad_path b/src/scripts/cad_path new file mode 100755 index 0000000..bf86c6a --- /dev/null +++ b/src/scripts/cad_path @@ -0,0 +1,97 @@ +#!/usr/bin/env python +# +# cad_path +# .cad to .path +# +# Neil Gershenfeld +# CBA MIT 11/6/10 +# +# (c) Massachusetts Institute of Technology 2010 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# + +import sys, os, string + +# +# command line args +# + +if ((len(sys.argv) < 3) | (len(sys.argv) > 11)): + print "command line: cad_path in.cad out.path [resolution [error [offset_diameter [offset_number [offset_overlap [z_thickness [z_top [z_bottom]]]]]]]]" + print " in.cad = input .cad file" + print " out.path = output .path file" + print " resolution = pixels per mm (optional, default 10)" + print " error = allowable vector fit deviation (optional, pixels, default 1.1)" + print " offset_diameter = diameter to offset (optional, mm, default 0)" + print " offset_number = number of contours to offset (optional, -1 to fill all, default 1)" + print " offset_overlap = tool offset overlap fraction (optional, 0 (no overlap) - 1 (complete overlap, default 0.5))" + print " z_thickness = slice z thickness (optional, mm, default in.cad value)" + print " z_top = top slice z value (optional, mm, default in.cad value)" + print " z_bottom = bottom slice z value (optional, mm, default in.cad value)" + sys.exit() + +resolution = "10" +if (len(sys.argv) > 3): + resolution = sys.argv[3] +error = "1.1" +if (len(sys.argv) > 4): + error = sys.argv[4] +offset_diameter = "0" +if (len(sys.argv) > 5): + offset_diameter = sys.argv[5] +offset_numer = "1" +if (len(sys.argv) > 6): + offset_number = sys.argv[6] +offset_overlap = ".5" +if (len(sys.argv) > 7): + offset_overlap = sys.argv[7] + +# +# convert to .math +# + +cad_file_name = sys.argv[1] +math_file_name = (os.path.splitext(cad_file_name)[0])+".math" +os.system("cad_math "+cad_file_name+" "+math_file_name) + +# +# get dims from .math +# + +math_file = open(math_file_name,'r') +file_type = math_file.readline() +units = float(math_file.readline()) +(dx,dy,dz) = string.split(math_file.readline()) +dx = units*float(dx) +dy = units*float(dy) +dz = units*float(dz) +(xmin,ymin,zmin) = string.split(math_file.readline()) +xmin = units*float(xmin) +ymin = units*float(ymin) +zmin = units*float(zmin) +math_file.close() + +z_thickness = dz +if (len(sys.argv) > 8): + z_thickness = float(sys.argv[8]) +z_top = zmin + dz +if (len(sys.argv) > 9): + z_top = float(sys.argv[9]) +z_bottom = zmin +if (len(sys.argv) > 10): + z_bottom = float(sys.argv[10]) + +# +# make image +# + +png_file_name = (os.path.splitext(cad_file_name)[0])+".png" +nz = int(1 + (z_top-z_bottom)/z_thickness) +os.system("math_png %s %s %s %d"%(math_file_name,png_file_name,resolution,nz)) + +# +# make path +# + +os.system("png_path %s %s %s %s %s %s 1 0 %f %f %f"%(png_file_name,sys.argv[2],error,offset_diameter,offset_number,offset_overlap,z_top,z_bottom,z_thickness)) diff --git a/src/scripts/cad_png b/src/scripts/cad_png new file mode 100755 index 0000000..e15b391 --- /dev/null +++ b/src/scripts/cad_png @@ -0,0 +1,34 @@ +#!/bin/bash +# +# cad_png +# convert .cad to PNG +# +# Neil Gershenfeld +# CBA MIT 10/27/10 +# +# (c) Massachusetts Institute of Technology 2010 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# + +# +# check command line +# +if [ $# -eq 0 ]; then + echo "command line: cad_png in.cad [args]" + echo " in.cad = input .cad file" + echo " args = math_png arguments (optional)" + exit + fi + +# +# convert to math string +# +name=$1 +cad_math $name ${name%.*}.math + +# +# convert to PNG +# +shift +math_png ${name%.*}.math ${name%.*}.png $@ diff --git a/src/scripts/cad_view b/src/scripts/cad_view new file mode 100755 index 0000000..9313782 --- /dev/null +++ b/src/scripts/cad_view @@ -0,0 +1,42 @@ +#!/bin/bash +# +# cad_view +# view .cad +# +# Neil Gershenfeld +# CBA MIT 10/27/10 +# +# (c) Massachusetts Institute of Technology 2010 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# + +viewer="eog" + +# +# check command line +# +if [ $# -eq 0 ]; then + echo "command line: cad_view in.cad [args]" + echo " in.cad = input .cad file" + echo " args = math_png arguments (optional)" + echo " image viewer = " $viewer + exit + fi + +# +# convert to math string +# +name=$1 +cad_math $name ${name%.*}.math + +# +# convert to PNG +# +shift +math_png ${name%.*}.math ${name%.*}.png $@ + +# +# view +# +$viewer ${name%.*}.png diff --git a/src/scripts/eagle_png b/src/scripts/eagle_png new file mode 100755 index 0000000..aecab39 --- /dev/null +++ b/src/scripts/eagle_png @@ -0,0 +1,185 @@ +#!/usr/bin/env python + +import os +import sys +import platform +import glob +import subprocess +import hashlib + +def find_eagle(): + if platform.uname()[0] == 'Darwin': + try: + eagle_dir = glob.glob('/Applications/EAGLE*')[-1] + except IndexError: + sys.stderr.write("Error: EAGLE not found.\n") + sys.exit(1) + + return eagle_dir + '/EAGLE.app/Contents/MacOS/EAGLE' + else: + if subprocess.call(['which','eagle'], + stdout = open(os.devnull, 'w')): + sys.stderr.write("Error: EAGLE not found.\n") + sys.exit(1) + return 'eagle' + +def create_images(name, resolution = 1500): + for img in ['top','bottom','cutout','holes','vias']: + file = '%s.%s.png' % (name, img) + if os.path.isfile(file): + os.remove(file) + + script = ''' +ratsnest; write; +set palette black; window; +display none top vias pads; +export image '{name}.top.png' monochrome {resolution}; +display none bottom vias pads; +export image '{name}.bottom.png' monochrome {resolution}; +display none milling; +export image '{name}.cutout.png' monochrome {resolution}; +display none holes; +export image '{name}.holes.png' monochrome {resolution}; +display none vias pads; +export image '{name}.vias.png' monochrome {resolution}; +quit'''.format(name = name, resolution = resolution) + subprocess.call([find_eagle(), '-C', script, name + '.brd']) + +def md5(filename): + with open(filename,'rb') as f: + m = hashlib.md5() + for chunk in iter(lambda: f.read(m.block_size*128), ''): + m.update(chunk) + return m.digest() + +def clean_up(name): + preserve = ['top','bottom','cutout'] + for img in ['top','bottom','cutout','holes','vias']: + file = '%s.%s.png' % (name, img) + file_ = '%s.%s_.png' % (name, img) + if os.path.isfile(file) and img not in preserve: + os.remove(file) + if os.path.isfile(file_): + os.remove(file_) + +def print_help(): + print """command line: eagle_png [options] target.brd + target.brd = EAGLE brd file to render + The board outline should be a solid polygon on the 'milling' layer + Internal cutouts should be solid shapes on the 'holes' layer + + Valid options: + --resolution NUM : sets output image resolution + --doublesided : forces double-sided mode""" + sys.exit(1) + +if __name__ == '__main__': + if len(sys.argv) == 1: + print_help() + sys.exit(1) + + # Parse arguments + sys.argv = sys.argv[1:] + resolution = 1500 + force_doublesided = False + + while sys.argv: + if sys.argv[0] == '--resolution': + try: + resolution = sys.argv[1] + sys.argv = sys.argv[2:] + except IndexError: + sys.stderr.write("Error: No resolution provided.\n") + sys.exit(1) + try: + resolution = int(resolution) + except ValueError: + sys.stderr.write("Error: Invalid resolution.\n") + sys.exit(1) + + elif sys.argv[0] == '--doublesided': + force_doublesided = True + sys.argv = sys.argv[1:] + + elif len(sys.argv) == 1: + break + else: + sys.stderr.write("Error: No filename provided.\n") + sys.exit(1) + + name = sys.argv[0].replace('.brd','') + if not os.path.isfile(name+'.brd'): + sys.stderr.write("Error: .brd file does not exist.\n") + sys.exit(1) + + vias = name + '.vias.png' + cutout = name + '.cutout.png' + top = name + '.top.png' + bottom = name + '.bottom.png' + holes = name + '.holes.png' + + print "Rendering images." + create_images(name, resolution) + + # Check to make sure that imagemagick is installed. + if subprocess.call(['which','convert'], stdout = open(os.devnull, 'w')): + sys.stderr.write("""Error: 'convert' not found. +ImageMagick command-line tools must be installed to use eagle_png.""") + sys.exit(1) + + print "Processing images." + + # The following command is a set of ImageMagick instructions that + # combine all of the images. + + # The following steps take place: + # - Perform a white flood fill on the vias image, starting in the upper + # left corner. This makes the via image a set of black holes on + # a uniform white background + # - Multiply the vias and cutout images, to cut the via holes from + # the cutout region. + # - Invert the cutout image. + # - Lighten the top and bottom traces with the inverted cutout. This + # ensures that we don't waste time milling traces in regions that + # will be cut out of the PCB. + # - Subtract the holes image from the original cutout image + # - Save this combined cutout image + + command = [ 'convert', + vias, '-fill', 'white', '-draw', 'color 0,0 floodfill', + cutout, '-compose', 'Darken', '-composite', + '-compose','Lighten', + '(', + '+clone', + '-negate' + ] + + # If this is a two-sided board, then process the bottom layer + if md5(bottom) != md5(vias) or force_doublesided: + command += [ + '(', + '+clone', bottom, '-composite', + '-flop', '-write', bottom, '+delete', + ')' + ] + else: + os.remove(bottom) + + # Process the top layer + command += [ + top, '-composite', '-write', top, + '+delete', + ')', + holes, '-compose', 'Minus_Src', '-composite', cutout + ] + + # Execute this whole mess + subprocess.call(command) + + os.remove(vias) + os.remove(holes) + + if bottom in command: + print "Generated %s, %s, %s." % (top, bottom, cutout) + else: + print "Generated %s, %s." % (top, cutout) diff --git a/src/scripts/fab_send b/src/scripts/fab_send new file mode 100755 index 0000000..84b15d5 --- /dev/null +++ b/src/scripts/fab_send @@ -0,0 +1,72 @@ +#!/usr/bin/env python +# +# fab_send +# fab modules file send and configuration +# +# Neil Gershenfeld +# CBA MIT 9/16/11 +# +# (c) Massachusetts Institute of Technology 2011 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# + +# +# file type commands +# + +commands = { + '.eps': 'inkscape "$file"', + '.grb': 'gerbv "$file"', + '.drl': 'gerbv "$file"', + '.dxf': 'gedit "$file"', + '.epi': 'printer=laser; lprm -P$printer -; lpr -P$printer "$file"', + '.uni': 'port=/dev/lp0; cat "$file" > $port', + '.stl': 'meshlab "$file"', + '.ord': 'gedit "$file"', + '.camm': 'printer=vinyl; lpr -P$printer "$file"', + '.rml': 'port=/dev/ttyUSB0; rml_send_gui "$file" $port', + '.g': 'gedit "$file"', + '.sbp': 'gedit "$file"', + '.plt': 'gedit "$file"', + '.oms': 'gedit "$file"', + } + +# +# imports +# + +import sys,os + +# +# command line +# + +if (len(sys.argv) == 1): + print "command line: fab_send [file]" + print " file = file to send" + print " file type commands:" + print " ",commands + sys.exit() + +# +# get command +# + +filename = sys.argv[1] + +filetype = os.path.splitext(filename)[-1] + +if (not commands.has_key(filetype)): + print "fab_send:",filetype,"not defined" + sys.exit() + +command = 'file="'+filename+'"; '+commands[filetype] + +# +# execute +# + +print command +os.system(command) + diff --git a/src/scripts/fab_update b/src/scripts/fab_update new file mode 100755 index 0000000..6ea08e6 --- /dev/null +++ b/src/scripts/fab_update @@ -0,0 +1,90 @@ +#!/usr/bin/env python + +import datetime +import os +import re +import subprocess +import shutil +import sys +import urllib2 + +src_zip = os.path.join(os.getenv('HOME'), '__fab_src__.zip') +src_dir = os.path.join(os.getenv('HOME'), '__fab_src__') + +# Check when this script was installed +my_date = datetime.datetime.fromtimestamp(os.path.getmtime(sys.argv[0])) + +def get_date(): + '''Extracts the snapshot date from the kokompe downloads page.''' + html = urllib2.urlopen('http://kokompe.cba.mit.edu/downloads.html').read() + date = re.search('Snapshot from ([^\)]*)', html).group(1) + return datetime.datetime.strptime(date, '%B %d, %Y, %I:%M%p') + +def download(): + '''Downloads fab_src.zip, saving it in the user's home directory.''' + if os.path.isfile(src_zip): + print '''Error: Working file ~/__fab_src__.zip already exists. +Please delete it then rerun fab_update.''' + sys.exit(1) + zip = urllib2.urlopen('http://kokompe.cba.mit.edu/fab_src.zip') + open(src_zip,'wb').write(zip.read()) + +def unzip(): + '''Unzips fab_src.zip then removes the zip file.''' + if os.path.isdir(src_dir): + print '''Error: Working directory ~/__fab_src__ already exists. +Please delete it then rerun fab_update''' + sys.exit(1) + os.mkdir(src_dir) + os.chdir(src_dir) + subprocess.call(['unzip', src_zip]) + os.remove(src_zip) + +def print_help(): + print '''command line: fab_update [check|install] + check will inform you if a newer version of the fab modules is available. + install will install a newer version of the fab modules, if applicable.''' + +if __name__ == '__main__': + if len(sys.argv) == 1: + print_help() + sys.exit(1) + + # Check the date on the web to figure out if it is newer. + web_date = get_date() + date_info = ''' Your version was installed %s + Online version is dated %s''' % (my_date.strftime('%B %d, %Y, %I:%M%p'), + web_date.strftime('%B %d, %Y, %I:%M%p')) + outdated = web_date > my_date + + # Check to see if the web version is newer than our version. + if sys.argv[1] == 'check': + web_date = get_date() + if outdated: + print 'Newer version is available:\n%s' % date_info + else: + print 'No newer version available:\n%s' % date_info + + # Download & install if the web version is newer than this version + elif sys.argv[1] == 'install': + web_date = get_date() + if not outdated: + print 'No newer version available:\n%s' % date_info + sys.exit(0) + print "Downloading source" + download() + print "Unzipping source" + unzip() + print "Installing" + if subprocess.call(['make','fab']) != 0: + print 'Build failed. Please make sure your dependancies are up-to-date.' + shutil.rmtree(src_dir) + sys.exit(1) + subprocess.call(['make','install']) + shutil.rmtree(src_dir) + + # Invalid option + else: + print 'Invalid option "%s"' % ' '.join(sys.argv[1:]) + print_help() + sys.exit(1) diff --git a/src/scripts/math_png_py b/src/scripts/math_png_py new file mode 100755 index 0000000..623ba7a --- /dev/null +++ b/src/scripts/math_png_py @@ -0,0 +1,656 @@ +#!/usr/bin/env python +# +# math_png +# compile and evaluate .math to PNG +# +# Neil Gershenfeld +# CBA MIT 4/18/11 +# +# (c) Massachusetts Institute of Technology 2010 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# todo +# rotated view +# + +import sys +import string +import Image +import os +from numpy import * + +# +# command line args +# + +if (not((len(sys.argv) == 3) | (len(sys.argv) == 4) | (len(sys.argv) == 5) | (len(sys.argv) == 6)| (len(sys.argv) == 9))): + print "command line: math_png_py in.math out.png [resolution [number [view [rx ry rx]]]]" + print " in.math = input math string file" + print " out.png = output PNG image" + print " resolution = pixels per mm (optional, default 10)" + print " number = number of z slices to evaluate (optional, default 1)" + print " view = view projection(s) (optional, z|3, default z)" + print " rx ry rz = 3D view angle (optional, degrees, default 70 0 20)" + print "[This command is deprecated; use math_png instead.]" + sys.exit() + +resolution = "10" +if (len(sys.argv) > 3): + resolution = sys.argv[3] + +number = "1" +if (len(sys.argv) > 4): + number = sys.argv[4] + +view = "z" +if (len(sys.argv) > 5): + view = sys.argv[5] + +rx = "70" +ry = "10" +rz = "20" +if (len(sys.argv) > 6): + rx = sys.argv[6] + ry = sys.argv[7] + rz = sys.argv[8] +# +# read .math file +# + +input_file_name = sys.argv[1] +output_file_name = sys.argv[2] +input_file = open(input_file_name,'r') +file_type = string.split(input_file.readline(), ': ')[1][:-1] +units = float(string.split(input_file.readline(), ': ')[1][:-1]) +dx,dy,dz = string.split(input_file.readline())[3:] +xmin,ymin,zmin = string.split(input_file.readline())[3:] +math_string = input_file.readline()[1][:-1] +input_file.close() +print "read "+input_file_name +print " math string type: "+file_type +print " ",units,"mm per unit" +print " dx: "+dx+", dy: "+dy+", dz: "+dz +print " xmin: "+xmin+", ymin: "+ymin+", zmin: "+zmin + +# +# write evaluation program +# + +program = """// +// math_png_eval.c +// math string to PNG evaluation generated by math_png +// +// Neil Gershenfeld +// CBA MIT 3/6/11 +// +// (c) Massachusetts Institute of Technology 2010 +// Permission granted for experimental and personal use; +// license for commercial sale available from MIT. +// + +#include +#include +#include +#include +#include +#include + +// +// data structures +// + +struct fab_vars { + unsigned char empty; + unsigned char interior; + unsigned char edge; + unsigned char north; + unsigned char south; + unsigned char east; + unsigned char west; + unsigned char stop; + unsigned char corner; + unsigned char corner2; + unsigned char direction; + unsigned int nx,ny,nz; + unsigned int bit_depth; + unsigned int word_size; + double dx,dy,dz; + double xmin,ymin,zmin; + uint8_t **red,**green,**blue; + uint16_t **intensity; + short int **xptr, **yptr; + struct fab_path_type *path; + struct fab_mesh_type *mesh; + png_bytep *row_pointers; + png_structp png_ptr; + png_infop info_ptr; + }; + +void fab_write_png_K16(struct fab_vars *v, char *output_file_name) { + // + // write 16-bit grayscale PNG from fab_vars + // + FILE *output_file; + int x,y; + png_uint_32 res_x,res_y; + int unit_type; + png_byte color_type; + png_byte bit_depth; + png_byte *ptr; + // + // open PNG file + // + output_file = fopen(output_file_name, "wb"); + v->png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, + NULL, NULL, NULL); + v->info_ptr = png_create_info_struct(v->png_ptr); + png_init_io(v->png_ptr, output_file); + // + // set vars + // + bit_depth = 16; + color_type = PNG_COLOR_TYPE_GRAY; + png_set_IHDR(v->png_ptr, v->info_ptr, v->nx, v->ny, + bit_depth, color_type, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + res_x = 1000 * v->nx / v->dx; + res_y = 1000 * v->ny / v->dy; + png_set_pHYs(v->png_ptr, v->info_ptr, res_x, res_y, PNG_RESOLUTION_METER); + png_write_info(v->png_ptr, v->info_ptr); + // + // allocate pixels + // + v->row_pointers = (png_bytep*) malloc(sizeof(png_bytep) * v->ny); + for (y = 0; y < v->ny; ++y) + v->row_pointers[y] = (png_byte*) malloc(v->info_ptr->rowbytes); + // + // set pixels + // + for (y = 0; y < v->ny; ++y) + for (x = 0; x < v->nx; ++x) { + ptr = &(v->row_pointers[y][x*2]); + ptr[0] = (v->intensity[y][x] >> 8) & 255; + ptr[1] = v->intensity[y][x] & 255; + } + // + // write, close, and return + // + png_write_image(v->png_ptr, v->row_pointers); + png_write_end(v->png_ptr, NULL); + fclose(output_file); + printf("write %s\\n",output_file_name); + printf(" x pixels: %d, y pixels: %d\\n",v->nx,v->ny); + printf(" x pixels/m: %d, y pixels/m: %d\\n",(int) res_x,(int) res_y); + printf(" dx: %f mm, dy: %f mm\\n",v->dx,v->dy); + } + +void fab_write_png_RGB24(struct fab_vars *v, char *output_file_name) { + // + // write 24-bit RGB PNG from fab_vars + // + FILE *output_file; + int x,y; + png_uint_32 res_x,res_y; + int unit_type; + png_byte color_type; + png_byte bit_depth; + png_byte *ptr; + // + // open PNG file + // + output_file = fopen(output_file_name, "wb"); + v->png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, + NULL, NULL, NULL); + v->info_ptr = png_create_info_struct(v->png_ptr); + png_init_io(v->png_ptr, output_file); + // + // set vars + // + bit_depth = 8; + color_type = PNG_COLOR_TYPE_RGB; + png_set_IHDR(v->png_ptr, v->info_ptr, v->nx, v->ny, + bit_depth, color_type, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + res_x = 1000 * v->nx / v->dx; + res_y = 1000 * v->ny / v->dy; + png_set_pHYs(v->png_ptr, v->info_ptr, res_x, res_y, PNG_RESOLUTION_METER); + png_write_info(v->png_ptr, v->info_ptr); + // + // allocate pixels + // + v->row_pointers = (png_bytep*) malloc(sizeof(png_bytep) * v->ny); + for (y = 0; y < v->ny; ++y) + v->row_pointers[y] = (png_byte*) malloc(v->info_ptr->rowbytes); + // + // set pixels + // + for (y = 0; y < v->ny; ++y) + for (x = 0; x < v->nx; ++x) { + ptr = &(v->row_pointers[y][x*3]); + ptr[0] = v->red[y][x]; + ptr[1] = v->green[y][x]; + ptr[2] = v->blue[y][x]; + } + // + // write, close, and return + // + png_write_image(v->png_ptr, v->row_pointers); + png_write_end(v->png_ptr, NULL); + fclose(output_file); + printf("write %s\\n",output_file_name); + printf(" x pixels: %d, y pixels: %d\\n",v->nx,v->ny); + printf(" x pixels/m: %d, y pixels/m: %d\\n",(int) res_x,(int) res_y); + printf(" dx: %f mm, dy: %f mm\\n",v->dx,v->dy); + } + +struct fab_vars init_vars() { + // + // fab_vars constructor + // + struct fab_vars v; + // + v.empty = 0; + v.interior = 1; + v.edge = (1 << 1); + v.north = (1 << 2); + v.west = (2 << 2); + v.east = (3 << 2); + v.south = (4 << 2); + v.stop = (5 << 2); + v.corner = (6 << 2); + v.corner2 = (7 << 2); + v.direction = (7 << 2); + // + return v; + } + +""" +if (view == 'z'): + program +=""" +main(int argc, char **argv) { + // + // local vars + // + struct fab_vars v = init_vars(&v); + double units = 25.4; + double X,Y,Z; + v.dx = units*"""+dx+"""; + v.dy = units*"""+dy+"""; + v.dz = units*"""+dz+"""; + v.xmin = units*"""+xmin+"""; + v.ymin = units*"""+ymin+"""; + v.zmin = units*"""+zmin+"""; + double resolution="""+resolution+"""; + v.nx = resolution*v.dx; + v.ny = resolution*v.dy; + v.nz = """+number+"""; + int x,y,z; + uint32_t val; + uint8_t byte; + float scale; + // + // create arrays + // + if (0 == strcmp(\""""+file_type+"""\","Boolean")) { + v.intensity = malloc(v.ny*sizeof(uint16_t *)); + for (y = 0; y < v.ny; ++y) { + v.intensity[y] = (uint16_t*) malloc(v.nx*sizeof(uint16_t)); + } + } + else if (0 == strcmp(\""""+file_type+"""\","RGB")) { + v.red = malloc(v.ny*sizeof(uint16_t *)); + v.green = malloc(v.ny*sizeof(uint16_t *)); + v.blue = malloc(v.ny*sizeof(uint16_t *)); + for (y = 0; y < v.ny; ++y) { + v.red[y] = (uint8_t*) malloc(v.nx*sizeof(uint8_t)); + v.green[y] = (uint8_t*) malloc(v.nx*sizeof(uint8_t)); + v.blue[y] = (uint8_t*) malloc(v.nx*sizeof(uint8_t)); + } + } + else { + printf("math_png: oops -- file type not recognized\\n"); + exit(-1); + } + // + // evaluate + // + for (z = 0; z < v.nz; ++z) { + if (v.nz == 1) { + Z = v.zmin/units; + if (0 == strcmp(\""""+file_type+"""\","Boolean")) + scale = 65535; + else if (0 == strcmp(\""""+file_type+"""\","RGB")) + scale = 1; + } + else { + Z = (v.zmin + v.dz*z/(v.nz-1.0))/units; + if (0 == strcmp(\""""+file_type+"""\","Boolean")) + scale = 65535 * z/(v.nz-1.0); + else if (0 == strcmp(\""""+file_type+"""\","RGB")) + scale = z/(v.nz-1.0); + } + printf(" Z = %f\\n",Z); + for (y = 0; y < v.ny; ++y) { + Y = (v.ymin + v.dy*(v.ny-y-1)/(v.ny-1.0))/units; + for (x = 0; x < v.nx; ++x) { + X = (v.xmin + v.dx*x/(v.nx-1.0))/units; + val = """+math_string+"""; + if (0 == strcmp(\""""+file_type+"""\","Boolean")) { + val = scale*val; + if (val > v.intensity[y][x]) + v.intensity[y][x] = val; + } + else if (0 == strcmp(\""""+file_type+"""\","RGB")) { + byte = scale * (val & 255); + if (val > v.red[y][x]) + v.red[y][x] = byte; + byte = scale * ((val >> 8) & 255); + if (val > v.green[y][x]) + v.green[y][x] = byte; + byte = scale * ((val >> 16) & 255); + if (val > v.blue[y][x]) + v.blue[y][x] = byte; + } + } + } + } + // + // write PNG + // + if (0 == strcmp(\""""+file_type+"""\","Boolean")) + fab_write_png_K16(&v,\""""+output_file_name+"""\"); + else if (0 == strcmp(\""""+file_type+"""\","RGB")) + fab_write_png_RGB24(&v,\""""+output_file_name+"""\"); + } +""" +elif (view == 'r'): + program +=""" +main(int argc, char **argv) { + // + // to be completed + // + // local vars + // + struct fab_vars v = init_vars(&v); + double units = 25.4; + double X,Y,Z,X0,Y0,Z0,X1,Y1,Z1; + v.dx = units*"""+dx+"""; + v.dy = units*"""+dy+"""; + v.dz = units*"""+dz+"""; + v.xmin = units*"""+xmin+"""; + v.ymin = units*"""+ymin+"""; + v.zmin = units*"""+zmin+"""; + double resolution="""+resolution+"""; + int nx = resolution*v.dx; + int ny = resolution*v.dy; + int nz = resolution*v.dz; + v.nx = resolution*(v.dx); + v.ny = resolution*(v.dy); + v.nz = """+number+"""; + int x,y,z; + uint32_t val; + int scale; + double pi = 3.14159; + double rx = pi*"""+rx+"""/180.0; + double ry = pi*"""+ry+"""/180.0; + double rz = pi*"""+rz+"""/180.0; + // + // create RGB array + // + v.rgb = malloc(v.ny*sizeof(uint32_t *)); + for (y = 0; y < v.ny; ++y) + v.rgb[y] = (uint32_t*) malloc(2*v.nx*sizeof(uint32_t)); + // + // evaluate xyz + // + for (z = 0; z < v.nz; ++z) { + if (v.nz == 1) { + Z0 = v.zmin/units; + scale = 1; + } + else { + Z0 = (v.zmin + v.dz*z/(v.nz-1.0))/units; + scale = 255 * z/(v.nz-1.0); + } + if (0 == strcmp(\""""+file_type+"""\","Boolean")) + scale = scale + (scale << 8) + (scale << 16); + printf(" Z = %f\\n",Z0); + for (y = 0; y < v.ny; ++y) { + Y0 = (v.ymin + v.dy*(v.ny-y-1)/(v.ny-1.0))/units; + for (x = 0; x < v.nx; ++x) { + X0 = (v.xmin + v.dx*x/(v.nx-1.0))/units; + X1 = X0; + Y1 = cos(rx)*Y0 - sin(rx)*Z0; + Z1 = sin(rx)*Y0 + cos(rx)*Z0; + X = cos(rz)*X1 - sin(rz)*Y1; + Y = sin(rz)*X1 + cos(rz)*Y1; + Z = Z1; + val = """+math_string+"""; + val = scale*val; + if (val > v.rgb[y][x]) { + v.rgb[y][x] = val; + } + } + } + } + // + // write PNG + // + fab_write_png(&v,\""""+output_file_name+"""\"); + } +""" +elif (view == '3'): + program +=""" +main(int argc, char **argv) { + // + // local vars + // + struct fab_vars v = init_vars(&v); + double units = 25.4; + double X,Y,Z,X0,Y0,Z0,X1,Y1,Z1; + v.dx = units*"""+dx+"""; + v.dy = units*"""+dy+"""; + v.dz = units*"""+dz+"""; + v.xmin = units*"""+xmin+"""; + v.ymin = units*"""+ymin+"""; + v.zmin = units*"""+zmin+"""; + double resolution="""+resolution+"""; + int nx = resolution*v.dx; + int ny = resolution*v.dy; + int nz = resolution*v.dz; + int border = nx/25; + v.nx = border + nx + nz; + v.ny = border + ny + nz; + v.nz = """+number+"""; + int x,y,z; + uint32_t val; + uint8_t byte; + float scale; + // + // create arrays + // + if (0 == strcmp(\""""+file_type+"""\","Boolean")) { + v.intensity = malloc(v.ny*sizeof(uint16_t *)); + for (y = 0; y < v.ny; ++y) { + v.intensity[y] = (uint16_t*) malloc(v.nx*sizeof(uint16_t)); + } + } + else if (0 == strcmp(\""""+file_type+"""\","RGB")) { + v.red = malloc(v.ny*sizeof(uint16_t *)); + v.green = malloc(v.ny*sizeof(uint16_t *)); + v.blue = malloc(v.ny*sizeof(uint16_t *)); + for (y = 0; y < v.ny; ++y) { + v.red[y] = (uint8_t*) malloc(v.nx*sizeof(uint8_t)); + v.green[y] = (uint8_t*) malloc(v.nx*sizeof(uint8_t)); + v.blue[y] = (uint8_t*) malloc(v.nx*sizeof(uint8_t)); + } + } + else { + printf("math_png: oops -- file type not recognized\\n"); + exit(-1); + } + // + // evaluate xy + // + for (z = 0; z < v.nz; ++z) { + if (v.nz == 1) { + Z = v.zmin/units; + if (0 == strcmp(\""""+file_type+"""\","Boolean")) + scale = 65535; + else if (0 == strcmp(\""""+file_type+"""\","RGB")) + scale = 1; + } + else { + Z = (v.zmin + v.dz*z/(v.nz-1.0))/units; + if (0 == strcmp(\""""+file_type+"""\","Boolean")) + scale = 65535 * z/(v.nz-1.0); + else if (0 == strcmp(\""""+file_type+"""\","RGB")) + scale = z/(v.nz-1.0); + } + printf(" Z = %f\\n",Z); + for (y = 0; y < ny; ++y) { + Y = (v.ymin + v.dy*(ny-y-1)/(ny-1.0))/units; + for (x = 0; x < nx; ++x) { + X = (v.xmin + v.dx*x/(nx-1.0))/units; + val = """+math_string+"""; + if (0 == strcmp(\""""+file_type+"""\","Boolean")) { + val = scale*val; + if (val > v.intensity[y][x]) + v.intensity[y][x] = val; + } + else if (0 == strcmp(\""""+file_type+"""\","RGB")) { + byte = scale * (val & 255); + if (val > v.red[y][x]) + v.red[y][x] = byte; + byte = scale * ((val >> 8) & 255); + if (val > v.green[y][x]) + v.green[y][x] = byte; + byte = scale * ((val >> 16) & 255); + if (val > v.blue[y][x]) + v.blue[y][x] = byte; + } + } + } + } + // + // evaluate xz + // + for (y = 0; y < v.nz; ++y) { + if (v.nz == 1) { + Y = v.ymin/units; + if (0 == strcmp(\""""+file_type+"""\","Boolean")) + scale = 65535; + else if (0 == strcmp(\""""+file_type+"""\","RGB")) + scale = 1; + } + else { + Y = (v.ymin + v.dy*(v.nz-y-1)/(v.nz-1.0))/units; + if (0 == strcmp(\""""+file_type+"""\","Boolean")) + scale = 65535 * y/(v.nz-1.0); + else if (0 == strcmp(\""""+file_type+"""\","RGB")) + scale = y/(v.nz-1.0); + } + printf(" Y = %f\\n",Y); + for (z = 0; z < nz; ++z) { + Z = (v.zmin + v.dz*(nz-z-1)/(nz-1.0))/units; + for (x = 0; x < nx; ++x) { + X = (v.xmin + v.dx*x/(nx-1.0))/units; + val = """+math_string+"""; + if (0 == strcmp(\""""+file_type+"""\","Boolean")) { + val = scale*val; + if (val > v.intensity[ny+border+z][x]) + v.intensity[ny+border+z][x] = val; + } + else if (0 == strcmp(\""""+file_type+"""\","RGB")) { + byte = scale * (val & 255); + if (val > v.red[y][x]) + v.red[y][x] = byte; + byte = scale * ((val >> 8) & 255); + if (val > v.green[y][x]) + v.green[y][x] = byte; + byte = scale * ((val >> 16) & 255); + if (val > v.blue[y][x]) + v.blue[y][x] = byte; + } + } + } + } + // + // evaluate zy + // + for (x = 0; x < v.nz; ++x) { + if (v.nz == 1) { + X = v.xmin/units; + if (0 == strcmp(\""""+file_type+"""\","Boolean")) + scale = 65535; + else if (0 == strcmp(\""""+file_type+"""\","RGB")) + scale = 1; + } + else { + X = (v.xmin + v.dx*x/(v.nz-1.0))/units; + if (0 == strcmp(\""""+file_type+"""\","Boolean")) + scale = 65535 * x/(v.nz-1.0); + else if (0 == strcmp(\""""+file_type+"""\","RGB")) + scale = x/(v.nz-1.0); + } + printf(" X = %f\\n",X); + for (y = 0; y < ny; ++y) { + Y = (v.ymin + v.dy*(ny-y-1)/(ny-1.0))/units; + for (z = 0; z < nz; ++z) { + Z = (v.zmin + v.dz*(nz-z-1)/(nz-1.0))/units; + val = """+math_string+"""; + if (0 == strcmp(\""""+file_type+"""\","Boolean")) { + val = scale*val; + if (val > v.intensity[y][nx+border+z]) + v.intensity[y][nx+border+z] = val; + } + else if (0 == strcmp(\""""+file_type+"""\","RGB")) { + byte = scale * (val & 255); + if (val > v.red[y][x]) + v.red[y][x] = byte; + byte = scale * ((val >> 8) & 255); + if (val > v.green[y][x]) + v.green[y][x] = byte; + byte = scale * ((val >> 16) & 255); + if (val > v.blue[y][x]) + v.blue[y][x] = byte; + } + } + } + } + // + // write PNG + // + if (0 == strcmp(\""""+file_type+"""\","Boolean")) + fab_write_png_K16(&v,\""""+output_file_name+"""\"); + else if (0 == strcmp(\""""+file_type+"""\","RGB")) + fab_write_png_RGB24(&v,\""""+output_file_name+"""\"); + } +""" +else: + print "view "+view+" not recognized" + sys.exit() + +eval_file = open("math_png_eval.c",'w') +eval_file.write(program) +eval_file.close() + +# +# compile +# + +print "compile" +if os.uname()[0] == 'Darwin': + os.system("gcc -O math_png_eval.c -o math_png_eval -lm -I/usr/X11/include -L/usr/X11/lib -lpng") +else: + os.system("gcc -O math_png_eval.c -o math_png_eval -lm -lpng") + +# +# execute +# + +print "execute" +os.system("./math_png_eval") +#os.system("rm ./math_png_eval.c") +os.system("rm ./math_png_eval") diff --git a/src/scripts/math_stl_py b/src/scripts/math_stl_py new file mode 100755 index 0000000..3eb04e5 --- /dev/null +++ b/src/scripts/math_stl_py @@ -0,0 +1,465 @@ +#!/usr/bin/env python +# +# math_stl +# compile and evaluate .math to STL +# +# todo +# run-length code +# prune triangulation +# +# Neil Gershenfeld +# CBA MIT 10/29/11 +# +# (c) Massachusetts Institute of Technology 2011 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# +# + +import sys +import string +import Image +import os +from numpy import * + +# +# command line args +# + +if (not((len(sys.argv) == 3) | (len(sys.argv) == 4))): + print "command line: math_stl in.math out.stl [resolution]" + print " in.math = input math string file" + print " out.stl = output STL image" + print " resolution = pixels per mm (optional, default 1)" + sys.exit() + +resolution = "1" +if (len(sys.argv) == 4): + resolution = sys.argv[3] + +# +# read .math file +# + +input_file_name = sys.argv[1] +output_file_name = sys.argv[2] +input_file = open(input_file_name,'r') +(file_type,) = string.split(input_file.readline()) +(units,) = string.split(input_file.readline()) +units = float(units) +(dx,dy,dz) = string.split(input_file.readline()) +(xmin,ymin,zmin) = string.split(input_file.readline()) +math_string = input_file.readline() +input_file.close() +print "read "+input_file_name +print " math string type: "+file_type +print " ",units,"mm per unit" +print " dx: "+dx+", dy: "+dy+", dz: "+dz +print " xmin: "+xmin+", ymin: "+ymin+", zmin: "+zmin + +# +# write evaluation program +# + +program = """// +// math_stl_eval.c +// math string to STL evaluation generated by math_stl +// +// Neil Gershenfeld +// CBA MIT 10/29/11 +// +// (c) Massachusetts Institute of Technology 2011 +// Permission granted for experimental and personal use; +// license for commercial sale available from MIT. +// + +#include +#include +#include +#include +#include + +// +// data structures +// + +struct fab_vars { + unsigned char empty; + unsigned char interior; + unsigned char edge; + unsigned char north; + unsigned char south; + unsigned char east; + unsigned char west; + unsigned char stop; + unsigned char corner; + unsigned char corner2; + unsigned char direction; + unsigned int nx,ny,nz; + unsigned int bit_depth; + unsigned int word_size; + double dx,dy,dz; + double xmin,ymin,zmin; + unsigned char **array; + uint32_t **rgb0,**rgb1; + short int **xptr, **yptr; + struct fab_path_type *path; + struct fab_mesh_type *mesh; + png_bytep *row_pointers; + png_structp png_ptr; + png_infop info_ptr; + }; + +struct fab_vars init_vars() { + // + // fab_vars constructor + // + struct fab_vars v; + // + v.empty = 0; + v.interior = 1; + v.edge = (1 << 1); + v.north = (1 << 2); + v.west = (2 << 2); + v.east = (3 << 2); + v.south = (4 << 2); + v.stop = (5 << 2); + v.corner = (6 << 2); + v.corner2 = (7 << 2); + v.direction = (7 << 2); + // + return v; + } + +main(int argc, char **argv) { + // + // local vars + // + struct fab_vars v = init_vars(&v); + double units = 25.4; + double X,Y,Z; + double dx,dy,dz; + v.dx = units*"""+dx+"""; + v.dy = units*"""+dy+"""; + v.dz = units*"""+dz+"""; + v.xmin = units*"""+xmin+"""; + v.ymin = units*"""+ymin+"""; + v.zmin = units*"""+zmin+"""; + double resolution="""+resolution+"""; + v.nx = resolution*v.dx; + v.ny = resolution*v.dy; + v.nz = resolution*v.dz; + int x,y,z; + uint32_t val; + // + // create RGB arrays + // + v.rgb0 = malloc(v.ny*sizeof(uint32_t *)); + for (y = 0; y < v.ny; ++y) + v.rgb0[y] = (uint32_t*) malloc(v.nx*sizeof(uint32_t)); + v.rgb1 = malloc(v.ny*sizeof(uint32_t *)); + for (y = 0; y < v.ny; ++y) + v.rgb1[y] = (uint32_t*) malloc(v.nx*sizeof(uint32_t)); + // + // create triangle array + // + uint32_t triangle_count=0; + struct triangle_type { + int v0x,v0y,v0z; + int v1x,v1y,v1z; + int v2x,v2y,v2z; + struct triangle_type *next; + }; + struct triangle_type *t,*tstart,*tnew; + t = malloc(sizeof(struct triangle_type)); + tstart = t; + // + // loop over layers + // + for (z = 0; z < v.nz; ++z) { + Z = (v.zmin + v.dz*z/(v.nz-1.0))/units; + printf(" Z = %f\\n",Z); + // + // evaluate new layer + // + for (y = 0; y < v.ny; ++y) { + Y = (v.ymin + v.dy*(v.ny-y-1)/(v.ny-1.0))/units; + for (x = 0; x < v.nx; ++x) { + X = (v.xmin + v.dx*x/(v.nx-1.0))/units; + v.rgb0[y][x] = v.rgb1[y][x]; + v.rgb1[y][x] = """+math_string+"""; + } + } + // + // check voxel faces + // + for (y = 0; y < (v.ny-1); ++y) { + for (x = 0; x < (v.nx-1); ++x) { + // + // left + // + if ((v.rgb0[y][x] != 0) && (v.rgb0[y+1][x] != 0) && (v.rgb1[y][x] != 0) && (v.rgb1[y+1][x] != 0)) { + if ((v.rgb0[y][x+1] == 0) || (v.rgb0[y+1][x+1] == 0) || (v.rgb1[y][x+1] == 0) || (v.rgb1[y+1][x+1] == 0)) { + triangle_count += 1; + (*t).v0x = x; + (*t).v0y = y; + (*t).v0z = z; + (*t).v1x = x; + (*t).v1y = y; + (*t).v1z = z+1; + (*t).v2x = x; + (*t).v2y = y+1; + (*t).v2z = z+1; + tnew = malloc(sizeof(struct triangle_type)); + (*t).next = tnew; + t = tnew; + triangle_count += 1; + (*t).v0x = x; + (*t).v0y = y; + (*t).v0z = z; + (*t).v1x = x; + (*t).v1y = y+1; + (*t).v1z = z+1; + (*t).v2x = x; + (*t).v2y = y+1; + (*t).v2z = z; + tnew = malloc(sizeof(struct triangle_type)); + (*t).next = tnew; + t = tnew; + } + } + // + // right + // + if ((v.rgb0[y][x+1] != 0) && (v.rgb0[y+1][x+1] != 0) && (v.rgb1[y][x+1] != 0) && (v.rgb1[y+1][x+1] != 0)) { + if ((v.rgb0[y][x] == 0) || (v.rgb0[y+1][x] == 0) || (v.rgb1[y][x] == 0) || (v.rgb1[y+1][x] == 0)) { + triangle_count += 1; + (*t).v0x = x+1; + (*t).v0y = y; + (*t).v0z = z; + (*t).v1x = x+1; + (*t).v1y = y+1; + (*t).v1z = z+1; + (*t).v2x = x+1; + (*t).v2y = y; + (*t).v2z = z+1; + tnew = malloc(sizeof(struct triangle_type)); + (*t).next = tnew; + t = tnew; + triangle_count += 1; + (*t).v0x = x+1; + (*t).v0y = y; + (*t).v0z = z; + (*t).v1x = x+1; + (*t).v1y = y+1; + (*t).v1z = z; + (*t).v2x = x+1; + (*t).v2y = y+1; + (*t).v2z = z+1; + tnew = malloc(sizeof(struct triangle_type)); + (*t).next = tnew; + t = tnew; + } + } + // + // front + // + if ((v.rgb0[y][x] != 0) && (v.rgb0[y][x+1] != 0) && (v.rgb1[y][x] != 0) && (v.rgb1[y][x+1] != 0)) { + if ((v.rgb0[y+1][x] == 0) || (v.rgb0[y+1][x+1] == 0) || (v.rgb1[y+1][x] == 0) || (v.rgb1[y+1][x+1] == 0)) { + triangle_count += 1; + (*t).v0x = x; + (*t).v0y = y; + (*t).v0z = z; + (*t).v1x = x+1; + (*t).v1y = y; + (*t).v1z = z; + (*t).v2x = x+1; + (*t).v2y = y; + (*t).v2z = z+1; + tnew = malloc(sizeof(struct triangle_type)); + (*t).next = tnew; + t = tnew; + triangle_count += 1; + (*t).v0x = x; + (*t).v0y = y; + (*t).v0z = z; + (*t).v1x = x+1; + (*t).v1y = y; + (*t).v1z = z+1; + (*t).v2x = x; + (*t).v2y = y; + (*t).v2z = z+1; + tnew = malloc(sizeof(struct triangle_type)); + (*t).next = tnew; + t = tnew; + } + } + // + // back + // + if ((v.rgb0[y+1][x] != 0) && (v.rgb0[y+1][x+1] != 0) && (v.rgb1[y+1][x] != 0) && (v.rgb1[y+1][x+1] != 0)) { + if ((v.rgb0[y][x] == 0) || (v.rgb0[y][x+1] == 0) || (v.rgb1[y][x] == 0) || (v.rgb1[y][x+1] == 0)) { + triangle_count += 1; + (*t).v0x = x; + (*t).v0y = y+1; + (*t).v0z = z; + (*t).v1x = x+1; + (*t).v1y = y+1; + (*t).v1z = z; + (*t).v2x = x+1; + (*t).v2y = y+1; + (*t).v2z = z+1; + tnew = malloc(sizeof(struct triangle_type)); + (*t).next = tnew; + t = tnew; + triangle_count += 1; + (*t).v0x = x; + (*t).v0y = y+1; + (*t).v0z = z; + (*t).v1x = x+1; + (*t).v1y = y+1; + (*t).v1z = z+1; + (*t).v2x = x; + (*t).v2y = y+1; + (*t).v2z = z+1; + tnew = malloc(sizeof(struct triangle_type)); + (*t).next = tnew; + t = tnew; + } + } + // + // top + // + if ((v.rgb0[y][x] != 0) && (v.rgb0[y][x+1] != 0) && (v.rgb0[y+1][x] != 0) && (v.rgb0[y+1][x+1] != 0)) { + if ((v.rgb1[y][x] == 0) || (v.rgb1[y][x+1] == 0) || (v.rgb1[y+1][x] == 0) || (v.rgb1[y+1][x+1] == 0)) { + triangle_count += 1; + (*t).v0x = x; + (*t).v0y = y; + (*t).v0z = z; + (*t).v1x = x+1; + (*t).v1y = y+1; + (*t).v1z = z; + (*t).v2x = x; + (*t).v2y = y+1; + (*t).v2z = z; + tnew = malloc(sizeof(struct triangle_type)); + (*t).next = tnew; + t = tnew; + triangle_count += 1; + (*t).v0x = x; + (*t).v0y = y; + (*t).v0z = z; + (*t).v1x = x+1; + (*t).v1y = y; + (*t).v1z = z; + (*t).v2x = x+1; + (*t).v2y = y+1; + (*t).v2z = z; + tnew = malloc(sizeof(struct triangle_type)); + (*t).next = tnew; + t = tnew; + } + } + // + // bottom + // + if ((v.rgb1[y][x] != 0) && (v.rgb1[y][x+1] != 0) && (v.rgb1[y+1][x] != 0) && (v.rgb1[y+1][x+1] != 0)) { + if ((v.rgb0[y][x] == 0) || (v.rgb0[y][x+1] == 0) || (v.rgb0[y+1][x] == 0) || (v.rgb0[y+1][x+1] == 0)) { + triangle_count += 1; + (*t).v0x = x; + (*t).v0y = y; + (*t).v0z = z+1; + (*t).v1x = x+1; + (*t).v1y = y+1; + (*t).v1z = z+1; + (*t).v2x = x+1; + (*t).v2y = y; + (*t).v2z = z+1; + tnew = malloc(sizeof(struct triangle_type)); + (*t).next = tnew; + t = tnew; + triangle_count += 1; + (*t).v0x = x; + (*t).v0y = y; + (*t).v0z = z+1; + (*t).v1x = x; + (*t).v1y = y+1; + (*t).v1z = z+1; + (*t).v2x = x+1; + (*t).v2y = y+1; + (*t).v2z = z+1; + tnew = malloc(sizeof(struct triangle_type)); + (*t).next = tnew; + t = tnew; + } + } + } + } + } + // + // write STL + // + char header[80]; + uint16_t attribute; + int i; + t = tstart; + float v0x,v0y,v0z; + float v1x,v1y,v1z; + float v2x,v2y,v2z; + FILE *output_file; + output_file = fopen(\""""+output_file_name+"""\","wb"); + fwrite(header,1,80,output_file); + fwrite(&triangle_count,4,1,output_file); + float zero = 0; + for (i = 0; i < triangle_count; ++i) { + fwrite(&zero,4,1,output_file); + fwrite(&zero,4,1,output_file); + fwrite(&zero,4,1,output_file); + v0x = (v.xmin + v.dx*((*t).v0x)/(v.nx-1.0))/units; + fwrite(&v0x,4,1,output_file); + v0y = (v.ymin + v.dy*((*t).v0y)/(v.ny-1.0))/units; + fwrite(&v0y,4,1,output_file); + v0z = (v.zmin + v.dz*((*t).v0z)/(v.nz-1.0))/units; + fwrite(&v0z,4,1,output_file); + v1x = (v.xmin + v.dx*((*t).v1x)/(v.nx-1.0))/units; + fwrite(&v1x,4,1,output_file); + v1y = (v.ymin + v.dy*((*t).v1y)/(v.ny-1.0))/units; + fwrite(&v1y,4,1,output_file); + v1z = (v.zmin + v.dz*((*t).v1z)/(v.nz-1.0))/units; + fwrite(&v1z,4,1,output_file); + v2x = (v.xmin + v.dx*((*t).v2x)/(v.nx-1.0))/units; + fwrite(&v2x,4,1,output_file); + v2y = (v.ymin + v.dy*((*t).v2y)/(v.ny-1.0))/units; + fwrite(&v2y,4,1,output_file); + v2z = (v.zmin + v.dz*((*t).v2z)/(v.nz-1.0))/units; + fwrite(&v2z,4,1,output_file); + fwrite(&attribute,2,1,output_file); + t = (*t).next; + } + fclose(output_file); + } +""" + +eval_file = open("math_stl_eval.c",'w') +eval_file.write(program) +eval_file.close() + +# +# compile +# + +print "compile" +if os.uname()[0] == 'Darwin': + os.system("gcc -O math_stl_eval.c -o math_stl_eval -lm -I/usr/X11/include -L/usr/X11/lib -lpng") +else: + os.system("gcc -O math_stl_eval.c -o math_stl_eval -lm -lpng") + + +# +# execute +# + +print "execute" +os.system("./math_stl_eval") +os.system("rm ./math_stl_eval.c") +os.system("rm ./math_stl_eval") diff --git a/src/scripts/path_view b/src/scripts/path_view new file mode 100755 index 0000000..e35d1ed --- /dev/null +++ b/src/scripts/path_view @@ -0,0 +1,43 @@ +#!/bin/bash +# +# path_view +# view path file +# +# Neil Gershenfeld +# CBA MIT 9/18/10 +# +# (c) Massachusetts Institute of Technology 2010 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# + +# +# check command line +# +if [ $# -eq 0 ] || [ $# -gt 3 ]; then + echo "command line: path_view in.path [view [viewer]]" + echo " in.path = input path file" + echo " view = view projection(s) (optional, z|3, default z)" + echo " viewer = PostScript viewer [default evince]" + exit + fi +# +# get command line arguments +# +view="z" +if [ $# -eq 2 ] + then + view=$2 + fi +# +viewer="evince" +if [ $# -eq 3 ] + then + viewer=$3 + fi +# +# view file +# +path_ps $1 $1.ps $view +$viewer $1.ps +rm $1.ps diff --git a/src/scripts/png_tile b/src/scripts/png_tile new file mode 100755 index 0000000..6ffe2e2 --- /dev/null +++ b/src/scripts/png_tile @@ -0,0 +1,23 @@ +#!/usr/bin/env python + +import sys +import subprocess + +if len(sys.argv) < 3: + print "command line: png_tile rows cols file1.png file2.png ..." + print " rows = number of horizontal copies" + print " cols = number of vertical copies" + print " file1.png to fileN.png = files to tile" + sys.exit(1) + +rows = int(sys.argv[1]) +cols = int(sys.argv[2]) + +for file in sys.argv[3:]: + target = file.replace('.png','.tiled.png') + print "Processing %s:\n\tTiling vertically" % file + command = ['convert'] + [file]*rows + ['-append'] + [target] + subprocess.call(command) + print "\tTiling horizontally" + command = ['convert'] + [target]*cols + ['+append'] + [target] + subprocess.call(command) \ No newline at end of file diff --git a/src/scripts/rml_move b/src/scripts/rml_move new file mode 100755 index 0000000..24965f4 --- /dev/null +++ b/src/scripts/rml_move @@ -0,0 +1,33 @@ +#!/bin/bash +# +# rml_move +# move Roland Modela +# +# Neil Gershenfeld +# CBA MIT 9/18/10 +# +# (c) Massachusetts Institute of Technology 2010 +# Permission granted for experimental and personal use; +# license for commercial sale available from MIT. +# + +# +# check command line +# + +if [ $# -eq 0 ] || [ $# -gt 2 ]; then + echo command line: rml_move x y + echo " x,y, = position to move to (mm)" + exit + fi + +# +# move +# + +x=$(echo "(40.0*$1)/1" | bc) # 40/mm +y=$(echo "(40.0*$2)/1" | bc) +echo "PA;PA;!VZ10;!PZ0,100;PU $x $y;PD $x $y;!MC0;" > rml_move.rml +cat rml_move.rml +fab_send rml_move.rml +rm rml_move.rml diff --git a/src/solver/CMakeLists.txt b/src/solver/CMakeLists.txt new file mode 100755 index 0000000..4db7efc --- /dev/null +++ b/src/solver/CMakeLists.txt @@ -0,0 +1,47 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.6) + +find_package(Boost REQUIRED COMPONENTS thread system) +include_directories(${Boost_INCLUDE_DIR}) + +find_package(PNG REQUIRED) +include_directories(${PNG_INCLUDE_DIR}) + +set(CMAKE_CXX_FLAGS "-Wall -O3") +#set(CMAKE_CXX_FLAGS "-Wall -g") + +ADD_LIBRARY(MathTree + parser.cpp math_tree.cpp region.cpp + numeric_nodes.cpp logic_nodes.cpp + translator_nodes.cpp color_nodes.cpp + fabvars.cpp node.cpp opcodes.cpp geometry.cpp + progress_bar.cpp task_buffer.cpp solver.cpp) + +ADD_EXECUTABLE(math_png math_png.cpp imagesolver.cpp) +ADD_EXECUTABLE(math_ray math_ray.cpp raycaster.cpp) +ADD_EXECUTABLE(math_dot math_dot.cpp) +ADD_EXECUTABLE(math_stl math_stl.cpp trisolver.cpp) +ADD_EXECUTABLE(math_svg math_svg.cpp edgesolver.cpp) +ADD_EXECUTABLE(math_stats math_stats.cpp volsolver.cpp) + +SET(LIBRARIES + MathTree ${PNG_LIBRARY} + ${Boost_THREAD_LIBRARY} ${Boost_SYSTEM_LIBRARY} pthread) + +TARGET_LINK_LIBRARIES (math_png ${LIBRARIES}) +TARGET_LINK_LIBRARIES (math_ray ${LIBRARIES}) +TARGET_LINK_LIBRARIES (math_dot ${LIBRARIES}) +TARGET_LINK_LIBRARIES (math_stl ${LIBRARIES}) +TARGET_LINK_LIBRARIES (math_svg ${LIBRARIES}) +TARGET_LINK_LIBRARIES (math_stats ${LIBRARIES}) + +if( ${CMAKE_PROJECT_NAME} MATCHES fabmod ) + install(TARGETS math_png DESTINATION ${PROJECT_SOURCE_DIR}/../bin) + install(TARGETS math_ray DESTINATION ${PROJECT_SOURCE_DIR}/../bin) + install(TARGETS math_dot DESTINATION ${PROJECT_SOURCE_DIR}/../bin) + install(TARGETS math_stl DESTINATION ${PROJECT_SOURCE_DIR}/../bin) + install(TARGETS math_svg DESTINATION ${PROJECT_SOURCE_DIR}/../bin) + install(TARGETS math_stats DESTINATION ${PROJECT_SOURCE_DIR}/../bin) +endif( ${CMAKE_PROJECT_NAME} MATCHES fabmod ) + +set(SOLVER_EXECUTABLES math_png math_dot math_stl math_svg + CACHE STRING "Revised solver executables") diff --git a/src/solver/Makefile b/src/solver/Makefile new file mode 100644 index 0000000..ab6f445 --- /dev/null +++ b/src/solver/Makefile @@ -0,0 +1,40 @@ +CFLAGS = -O3 +#CFLAGS = -g + +LDFLAGS = -lpng -lboost_thread -lboost_system + +UNAME := $(shell uname) +ifeq ($(UNAME), Darwin) + # Look in the X11 folder for system default libpng + LDFLAGS += -L/usr/X11/lib + CFLAGS += -I/usr/X11/include + + # Check for MacPorts installation, if found look there for libraries + OPT := $(shell test -e /opt/local/lib; echo $$?) + ifeq ($(OPT), 0) + LDFLAGS += -L/opt/local/lib + endif +endif + +CPP = converter.cpp parser.cpp solver.cpp \ + fabtools.cpp numeric_tree.cpp rect.cpp tree.cpp \ + logic_tree.cpp opcodes.cpp roots.cpp lattice.cpp \ + solver_tp.cpp +HPP = converter.hpp logic_tree.hpp parser.hpp switches.hpp \ + fab_interval.hpp numeric_nodes.hpp rect.hpp tree.hpp \ + fabtools.hpp numeric_tree.hpp roots.hpp \ + logic_node.hpp opcodes.hpp solver.hpp lattice.hpp \ + solver_tp.hpp +OBJS = $(CPP:.cpp=.o) + +all: math_png + +math_png: main.cpp $(OBJS) + g++ $(CFLAGS) $(OBJS) main.cpp -o math_png $(LDFLAGS) + +%.o: %.cpp $(HPP) + g++ $(CFLAGS) -c $< + +clean: + @echo " Removing solver files" + @rm -f $(OBJS) math_png diff --git a/src/solver/color_nodes.cpp b/src/solver/color_nodes.cpp new file mode 100644 index 0000000..0d49c8a --- /dev/null +++ b/src/solver/color_nodes.cpp @@ -0,0 +1,78 @@ +#include "color_nodes.hpp" + +using namespace std; +//////////////////////////////////////////////////////////////////////////////// +ColorAnd::ColorAnd() + : BinaryNode(COLOR_AND) +{ + // Nothing to do here. +} + +void ColorAnd::eval(const float X, const float Y, const float Z) +{ + result_color = left->result_color & right->result_color; +} + +void ColorAnd::eval(const FabInterval& X, + const FabInterval& Y, + const FabInterval& Z) +{ + // Black ANDed with anything remains black. + if (left->result_color == 0 || right->result_color == 0) + result_color = 0; + else if (left->result_color == -1 || right->result_color == -1) + result_color = -1; + else + result_color = left->result_color & right->result_color; + marked = (result_color != -1); +} + +//////////////////////////////////////////////////////////////////////////////// +ColorOr::ColorOr() + : BinaryNode(COLOR_OR) +{ + // Nothing to do here. +} + +void ColorOr::eval(const float X, const float Y, const float Z) +{ + result_color = left->result_color | right->result_color; +} + +void ColorOr::eval(const FabInterval& X, + const FabInterval& Y, + const FabInterval& Z) +{ + // Pure white OR-ed with anything remains white. + const int WHITE = 255 + (255 << 8) + (255 << 16); + if (left->result_color == WHITE || right->result_color == WHITE) + result_color = WHITE; + else if (left->result_color == -1 || right->result_color == -1) + result_color = -1; + else + result_color = left->result_color | right->result_color; + marked = (result_color != -1); +} + +//////////////////////////////////////////////////////////////////////////////// +ColorNot::ColorNot() + : UnaryNode(COLOR_NOT) +{ + // Nothing to do here. +} + +void ColorNot::eval(const float X, const float Y, const float Z) +{ + result_color = ~child->result_color; +} + +void ColorNot::eval(const FabInterval& X, + const FabInterval& Y, + const FabInterval& Z) +{ + if (child->result_color == -1) + result_color = -1; + else + result_color = ~child->result_color; + marked = (result_color != -1); +} \ No newline at end of file diff --git a/src/solver/color_nodes.hpp b/src/solver/color_nodes.hpp new file mode 100644 index 0000000..3fcd41b --- /dev/null +++ b/src/solver/color_nodes.hpp @@ -0,0 +1,13 @@ +#ifndef COLOR_NODES_H +#define COLOR_NODES_H + +#include "node.hpp" +#include "node_macro.hpp" + +// This header file defines nodes that take in colors +// as inputs. +NODE(ColorAnd, BinaryNode); +NODE(ColorOr, BinaryNode); +NODE(ColorNot, UnaryNode); + +#endif \ No newline at end of file diff --git a/src/solver/edgesolver.cpp b/src/solver/edgesolver.cpp new file mode 100644 index 0000000..6426ef3 --- /dev/null +++ b/src/solver/edgesolver.cpp @@ -0,0 +1,179 @@ +#include "edgesolver.hpp" +#include "math_tree.hpp" +#include "node.hpp" + +#include "switches.hpp" + + +#include +#include + +using namespace std; +using boost::logic::tribool; +using boost::logic::indeterminate; +using boost::thread; + +const Vec3f OFFSETS[] = { + Vec3f(0, 0), Vec3f(1, 0), Vec3f(1, 1), Vec3f(0, 1) +}; + +const int EDGE_MAP[16][2][2] = { + {{-1,-1}, {-1,-1}}, // ---- + {{ 0, 1}, { 0, 3}}, // ---0 + {{ 1, 2}, { 1, 0}}, // --1- + {{ 1, 2}, { 0, 3}}, // --10 + {{ 2, 3}, { 2, 1}}, // -2-- + {{-2,-2}, {-2,-2}}, // -2-0 + {{ 2, 3}, { 1, 0}}, // -21- + {{ 2, 3}, { 0, 3}}, // -210 + + {{ 3, 0}, { 3, 2}}, // 3--- + {{ 0, 1}, { 3, 2}}, // 3--0 + {{-2,-2}, {-2,-2}}, // 3-1- + {{ 1, 2}, { 3, 2}}, // 3-10 + {{ 3, 0}, { 2, 1}}, // 32-- + {{ 0, 1}, { 2, 1}}, // 32-0 + {{ 3, 0}, { 1, 0}}, // 321- + {{-1,-1}, {-1,-1}} // 3210 +}; + + +/////////////////////////////////////////////////////////////////////////////// + +EdgeSolver::EdgeSolver(FabVars& v) + : Solver(v), paths(v.decimation_error) +{ + // Nothing to do here. +} + +EdgeSolver::EdgeSolver(MathTree* tree, FabVars& v) + : Solver(tree, v), paths(v.decimation_error) +{ + // Nothing to do here. +} + +void EdgeSolver::save() +{ + v.add_paths(paths); +} + +// Evaluate a single region, either with point-by-point evaluation or +// interval math + recursion. Operates in a single thread and spawns +// no children. +void EdgeSolver::evaluate_region(Region r) +{ + // For sufficiently small fractions of the space, do a + // point-by-point evaluation rather than recursing. + if (r.volume == 1) { + evaluate_voxel(r); + v.pb.update(r.volume); + return; + } + + // Convert from pixel regions to intervals + FabInterval X = v.x(r.imin, r.imax); + FabInterval Y = v.y(r.jmin, r.jmax); + FabInterval Z = v.z(r.kmin, r.kmax); + + tree->eval(X, Y, Z); + + // If the result was unambiguous, then we don't care since it + // is either entirely inside or outside the image. + bool result = false; + if (v.mode == SOLVE_BOOL) + result = !indeterminate(tree->root->result_bool); + else if (v.mode == SOLVE_REAL) + result = !indeterminate(tree->root->result_interval < FabInterval(0)); + + if (result) { + v.pb.update(r.volume); + return; + } + + // Split the region and recurse + list subregions = r.split(); + +#if PRUNE_TREE + tree->push(); +#endif + + list::iterator it; + for (it = subregions.begin(); it != subregions.end(); ++it) + evaluate_region(*it); + +#if PRUNE_TREE + tree->pop(); +#endif +} + +/////////////////////////////////////////////////////////////////////////////// + +// Evaluate a single rectangle, using interpolation to smooth the edges. +void EdgeSolver::evaluate_voxel(Region r) +{ + Vec3f corner(r.imin, r.jmin, r.kmin); + Vec3f v1, v2; // edge vertices + + Vec3f vertices[4]; + for (int i = 0; i < 4; ++i) + vertices[i] = corner + OFFSETS[i]; + + int lookup = 0; + for (int i = 3; i >= 0; --i) { + lookup <<= 1; + Vec3f pos = vertices[i]; + + if (point_cache.find(pos) == point_cache.end()) { + tree->eval(v.x(pos.x), v.y(pos.y), v.z(pos.z)); + + if (v.mode == SOLVE_BOOL) + point_cache[pos] = tree->root->result_bool; + else if (v.mode == SOLVE_REAL) + point_cache[pos] = tree->root->result_float < 0; + } + + if (point_cache[pos]) + lookup++; + } + + if (EDGE_MAP[lookup][0][0] == -1) + return; + if (EDGE_MAP[lookup][0][0] == -2) + return; + + v1 = interpolate(vertices[EDGE_MAP[lookup][0][0]], + vertices[EDGE_MAP[lookup][0][1]]); + v2 = interpolate(vertices[EDGE_MAP[lookup][1][0]], + vertices[EDGE_MAP[lookup][1][1]]); + paths += Path(v1, v2); + +} + +// Interpolates between a full and empty point, using caching to +// reduce the number of lookups required. +Vec3f EdgeSolver::interpolate(Vec3f filled, Vec3f empty) +{ + std::map::iterator it; + it = edge_cache.find(Edge(filled, empty)); + if (it != edge_cache.end()) + return it->second; + + float step_size = 0.25; + float interp = 0.5; + Vec3f offset = empty - filled; + + for (int i = 0; i < v.quality; ++i) { + Vec3f pos = filled + offset * interp; + + tree->eval(v.x(pos.x), v.y(pos.y), v.z(pos.z)); + if ((v.mode == SOLVE_BOOL && tree->root->result_bool) || + (v.mode == SOLVE_REAL && tree->root->result_float < 0)) + interp += step_size; + else + interp -= step_size; + step_size /= 2; + } + edge_cache[Edge(filled, empty)] = filled + offset * interp; + + return filled + offset * interp; +} \ No newline at end of file diff --git a/src/solver/edgesolver.hpp b/src/solver/edgesolver.hpp new file mode 100644 index 0000000..be4dd29 --- /dev/null +++ b/src/solver/edgesolver.hpp @@ -0,0 +1,62 @@ +#ifndef EDGESOLVER_H +#define EDGESOLVER_H + +#include +#include + +#include "solver.hpp" +#include "geometry.hpp" + +class EdgeSolver : public Solver +{ +public: + /* EdgeSolver(FabVars& v) + * EdgeSolver(MathTree* tree, FabVars& v) + * + * Constructs an EdgeSolver instance. + */ + EdgeSolver(FabVars& v); + EdgeSolver(MathTree* tree, FabVars& v); + + virtual ~EdgeSolver() { /* Nothing to do here */ } + + /* virtual void evaluate_region(Region R) + * + * Evaluate a given region recursively, saving results wherever is + * appropriate. + */ + virtual void evaluate_region(Region R); + + /* void evaluate_voxel(Region R) + * + * Evaluate a single unit cube. + */ + void evaluate_voxel(Region R); + + /* virtual void save() + * + * Saves the results of our calculation by copying paths into our + * reference to FabVars v. + */ + virtual void save(); + + /* Vec3f interpolate(Vec3f filled, Vec3f empty) + * + * Interpolates between a filled and an empty point using binary search. + * Values are saved in a cache, and the cache is checked before the + * search is run. + */ + Vec3f interpolate(Vec3f filled, Vec3f empty); + +private: + // True / False cached values + std::map point_cache; + + // Interpolation cached values + std::map edge_cache; + + // Saved paths + PathSet paths; + +}; +#endif \ No newline at end of file diff --git a/src/solver/fab_interval.hpp b/src/solver/fab_interval.hpp new file mode 100644 index 0000000..f6867d9 --- /dev/null +++ b/src/solver/fab_interval.hpp @@ -0,0 +1,101 @@ +#ifndef FAB_INTERVAL_H +#define FAB_INTERVAL_H + +#include + +#include +#include + +// We are using a specific type of Boost interval, which we define +// in this unreasonably long typedef. +typedef boost::numeric::interval + >, + boost::numeric::interval_lib::checking_base + > +> FabInterval; + +// Interval comparisons should be done with tribool logic. +#include +using namespace boost::numeric::interval_lib::compare::tribool; + +/* interval atan2(interval y, interval x) + * + * Computes atan2 of two intervals. + */ +template +boost::numeric::interval atan2( + const boost::numeric::interval& y, + const boost::numeric::interval& x) +{ + + // Do the 9 atan2 cases + // Interval could be entirely within a quadrant (4 cases) + // could span an axis without containing the origin (4 cases) + // or could contain the origin (1 case) + + // For each case, when we draw the rectangle representing the + // domain of atan2 on the plane, the range of possible angles is + // spanned by the angles from the origin to two of the corners of + // the rectangle. (These corners are enumerated below, from drawing + // the picture.) + + if (x.lower() > 0) { // To the right of the y axis + if (y.lower() > 0) // 1st quadrant + return boost::numeric::interval(atan2(y.lower(), x.upper()), atan2(y.upper(), x.lower())); + else if (y.upper() < 0) // 4th quadrant + return boost::numeric::interval(atan2(y.lower(), x.lower()), atan2(y.upper(), x.upper())); + else // straddling the positive x axis + return boost::numeric::interval(atan2(y.lower(), x.lower()), atan2(y.upper(), x.lower())); + } else if (x.upper() < 0) { // To the left of the y axis + + if (y.lower() > 0) // 2nd quadrant + return boost::numeric::interval(atan2(y.upper(), x.upper()), atan2(y.lower(), x.lower())); + else if (y.upper() < 0) // 3rd quadrant + return boost::numeric::interval(atan2(y.upper(),x.lower()), atan2(y.lower(),x.upper())); + else // straddling the negative x axis + // branch cut --- all we can say is -pi to pi + return boost::numeric::interval(-M_PI, M_PI); + } + else { // Straddling the y axis + if (y.lower() > 0) // straddling the positive y axis + return boost::numeric::interval(atan2(y.lower(),x.upper()), atan2(y.lower(),x.lower())); + else if (y.upper() < 0) // straddling the negative y axis + return boost::numeric::interval(atan2(y.upper(),x.lower()), atan2(y.upper(),x.upper())); + else // interval contains the origin + // all we can say is -pi to pi + return boost::numeric::interval(-M_PI, M_PI); + } +} + +/* interval sgn(interval x) + * + * Returns the sign of the input interval, which is either -1, 1, 0, or + * interval(-1, 1) + */ +template +boost::numeric::interval sgn( + const boost::numeric::interval& x) +{ + if (x.lower() > 0) + return 1; + else if (x.upper() < 0) + return -1; + else if (x.lower() == 0 and x.upper() == 0) + return 0; + else + return boost::numeric::interval(-1, 1); +} + +/* ostream& operator<<(ostream& o, interval& i) + * + * Prints an interval + */ +template +std::ostream& operator<<(std::ostream& o, const boost::numeric::interval& i) +{ + return o << '[' << i.lower() << ' ' << i.upper() << ']'; +} +#endif \ No newline at end of file diff --git a/src/solver/fabvars.cpp b/src/solver/fabvars.cpp new file mode 100644 index 0000000..814f881 --- /dev/null +++ b/src/solver/fabvars.cpp @@ -0,0 +1,570 @@ +#include +#include +#include + +#include + +#include "fabvars.hpp" +#include "region.hpp" + +using namespace std; +FabVars::FabVars(output_mode o, int argc, char** argv) + : ni(-1), nj(-1), nk(-1), + min_volume(-1), min_area(-1), + xmin(0), ymin(0), zmin(0), + dx(0), dy(0), dz(0), + pixels_per_mm(10), mm_per_unit(-1), quality(8), + decimation_error(1), + mode(SOLVE_BOOL), output(o), projection(false), + infile_name(""), outfile_name(""), + red(NULL), green(NULL), blue(NULL), intensity(NULL), + volume(0) +{ + + infile_name = argv[0]; + + if (output != OUTPUT_STATS) + outfile_name = argv[1]; + + pixels_per_mm = 10; + + // The different programs have different input argument orders. + if (output == OUTPUT_PNG) { + if (argc >= 3) + pixels_per_mm = atof(argv[2]); + if (argc >= 4) + nk = atoi(argv[3]); + } + else if (output == OUTPUT_STL) { + if (argc >= 3) + pixels_per_mm = atof(argv[2]); + if (argc >= 4) + quality = atoi(argv[3]); + } + else if (output == OUTPUT_SVG) { + if (argc >= 3) + pixels_per_mm = atof(argv[2]); + if (argc >= 4) + nk = atoi(argv[3]); + if (argc >= 5) + decimation_error = atof(argv[4]); + if (argc >= 6) + quality = atoi(argv[7]); + } + else if (output == OUTPUT_STATS) { + if (argc >= 2) + pixels_per_mm = atof(argv[1]); + } + // Load data from the input file + load(); +} + +FabVars::~FabVars() +{ + if (mode == SOLVE_RGB && red && green && blue) { + for (int y = 0; y < nj; ++y) { + delete [] red[y]; + delete [] green[y]; + delete [] blue[y]; + } + delete [] red; + red = NULL; + delete [] green; + green = NULL; + delete [] blue; + blue = NULL; + } else if ((mode == SOLVE_BOOL || mode == SOLVE_REAL) && intensity) { + for (int y = 0; y < nj; ++y) + delete [] intensity[y]; + delete [] intensity; + intensity = NULL; + } +} + +void FabVars::load() +{ + if (infile_name == "") { + cerr << "No input file provided!" << endl; + exit(1); + } + + fstream input; + + // Open the input file + input.open(infile_name.c_str(), ios::in); + if (!input.good()) { + cerr << "Failed to open input file.\n"; + exit(1); + } + + // Read and parse the file + string line; + while (getline(input, line)) + if (line.find("format:") != string::npos) + if (line.find("Boolean") != string::npos) + mode = SOLVE_BOOL; + else if (line.find("RGB") != string::npos) + mode = SOLVE_RGB; + else if (line.find("Real") != string::npos) + mode = SOLVE_REAL; + else { + cerr << "Input math string must be Boolean, RGB, or Real, not " + << line.substr(8,string::npos) << "." << endl; + exit(4); + } + else if (line.find("mm per unit:") != string::npos) + sscanf(line.substr(12,string::npos).c_str(),"%lf", + &mm_per_unit); + else if (line.find("dx dy dz:") != string::npos) + sscanf(line.substr(9,string::npos).c_str(),"%lf %lf %lf", + &dx, &dy, &dz); + else if (line.find("xmin ymin zmin:") != string::npos) + sscanf(line.substr(15,string::npos).c_str(),"%lf %lf %lf", + &xmin, &ymin, &zmin); + else if (line.find("expression: ") != string::npos) + math_string = line.substr(12,string::npos); + + ni = dx * mm_per_unit * pixels_per_mm; + nj = dy * mm_per_unit * pixels_per_mm; + // Default Z slices: 10 for SVG output, equivalent resolution + // for other output modes. + if (nk == -1) { + if (output == OUTPUT_SVG) + nk = dz ? 10 : 1; + else + nk = dz * mm_per_unit * pixels_per_mm; + } + + // Make sure that these are all non-zero. + ni = ni ? ni : 1; + nj = nj ? nj : 1; + nk = nk ? nk : 1; + pb.full = uint64_t(ni)*uint64_t(nj)*uint64_t(nk); + + // Pick a minimum volume below which we won't do + // octree recursion. + min_volume = 64; + min_area = 8; + + // Pick a stroke size for drawing SVGs + stroke = min(ni, nj) / (pixels_per_mm * 1000.); + + // Convert decimation error in mm^2 into something that can + // directly be compared with a simplified Heron's formula output. + decimation_error = pow(decimation_error*4, 2); + paths.decimation_error = decimation_error; + + // Everything below this point is allocating memory for the images, + // so return early if we have a different output format. + if (output != OUTPUT_PNG) + return; + + // Allocate memory for the image. + if (mode == SOLVE_RGB) { + red = new uint8_t*[nj]; + green = new uint8_t*[nj]; + blue = new uint8_t*[nj]; + for (int y = 0; y < nj; ++y) { + red[y] = new uint8_t[ni]; + green[y] = new uint8_t[ni]; + blue[y] = new uint8_t[ni]; + for (int x = 0; x < ni; ++x) + { + red[y][x] = 0; + green[y][x] = 0; + blue[y][x] = 0; + } + } + } else { + intensity = new uint16_t*[nj]; + for (int y = 0; y < nj; ++y) { + intensity[y] = new uint16_t[ni]; + for (int x = 0; x < ni; ++x) + intensity[y][x] = 0; + } + } +} + +//////////////////////////////////////////////////////////////////////////////// + +float FabVars::x(float i) const +{ + return xmin + dx*i / float(ni); +} + +float FabVars::y(float j) const +{ + return ymin + dy*j / float(nj); +} + +float FabVars::z(float k) const +{ + return zmin + dz*k / float(nk); +} + +//////////////////////////////////////////////////////////////////////////////// + +FabInterval FabVars::x(float imin, float imax) const +{ + return FabInterval(xmin + dx * imin / float(ni), + xmin + dx * imax / float(ni)); +} + +FabInterval FabVars::y(float jmin, float jmax) const +{ + return FabInterval(ymin + dy * jmin / float(nj), + ymin + dy * jmax / float(nj)); +} + +FabInterval FabVars::z(float kmin, float kmax) const +{ + return FabInterval(zmin + dz * kmin / float(nk), + zmin + dz * kmax / float(nk)); +} + +float FabVars::scale(unsigned int k) const +{ + if (nk == 1) { + if (mode == SOLVE_BOOL || mode == SOLVE_REAL) + return 65535; + else + return 1; + } + + return k/float(nk - 1) * + ((mode == SOLVE_BOOL || mode == SOLVE_REAL) ? 65535 : 1); +} + +//////////////////////////////////////////////////////////////////////////////// + +int FabVars::k(float z) const +{ + return (z - zmin) * float(nk) / dz; +} + +//////////////////////////////////////////////////////////////////////////////// + +void FabVars::fill(Region r) +{ + float s = scale(r.kmax - 1); + for (int i = r.imin; i < r.imax; ++i) + for (int j = r.jmin; j < r.jmax; ++j) + if (s > intensity[nj - j - 1][i]) + intensity[nj - j - 1][i] = s; +} + +void FabVars::fill(Region r, unsigned char R, unsigned char G, unsigned char B) +{ + float s = scale(r.kmax); + for (int j = r.jmin; j < r.jmax; ++j) + for (int i = r.imin; i < r.imax; ++i) { + if (R * s > red[nj - j - 1][i]) + red[nj - j - 1][i] = R * s; + if (G * s > green[nj - j - 1][i]) + green[nj - j - 1][i] = G * s; + if (B * s > blue[nj - j - 1][i]) + blue[nj - j - 1][i] = B * s; + } +} + +//////////////////////////////////////////////////////////////////////////////// + +void FabVars::add_triangles(std::list tris) +{ + geometry_lock.lock(); + triangles.splice(triangles.begin(), tris); + geometry_lock.unlock(); +} + +void FabVars::add_paths(const PathSet& p) +{ + geometry_lock.lock(); + + list::const_iterator it; + for (it = p.begin(); it != p.end(); ++it) + paths += *it; + + geometry_lock.unlock(); +} + +void FabVars::add_volume(const uint64_t v) +{ + geometry_lock.lock(); + volume += v; + geometry_lock.unlock(); +} +//////////////////////////////////////////////////////////////////////////////// + +void FabVars::write_png() +{ + if (mode == SOLVE_BOOL || mode == SOLVE_REAL) + fab_write_png_K16(this, outfile_name.c_str()); + else if (mode == SOLVE_RGB) + fab_write_png_RGB24(this, outfile_name.c_str()); +} + +void FabVars::write_stl() +{ + fstream stl_out; + stl_out.open(outfile_name.c_str(), fstream::trunc | fstream::out); + + // The first 80 characters are undefined, so let's leave an informative message! + stl_out << "This is a binary STL file created by math_stl. Learn more at kokompe.cba.mit.edu"; + stl_out << " "; + + uint32_t stl_faces = 0; + + list::iterator it; + for (it = triangles.begin(); it != triangles.end(); ++it) + { + // Write the normal as all zeros + for (int i = 0; i < 12; ++i) + stl_out << char(0); + + // Extract vertices from the list + Vec3f v1 = it->v1; + Vec3f v2 = it->v2; + Vec3f v3 = it->v3; + + // Convert into world coordinates + v1.x = x(v1.x); + v1.y = y(v1.y); + v1.z = z(v1.z); + // Write out each of the floats, byte by byte + for (int i = 0; i < 4; ++i) + stl_out << ((char*)&v1.x)[i]; + for (int i = 0; i < 4; ++i) + stl_out << ((char*)&v1.y)[i]; + for (int i = 0; i < 4; ++i) + stl_out << ((char*)&v1.z)[i]; + + v2.x = x(v2.x); + v2.y = y(v2.y); + v2.z = z(v2.z); + for (int i = 0; i < 4; ++i) + stl_out << ((char*)&v2.x)[i]; + for (int i = 0; i < 4; ++i) + stl_out << ((char*)&v2.y)[i]; + for (int i = 0; i < 4; ++i) + stl_out << ((char*)&v2.z)[i]; + + v3.x = x(v3.x); + v3.y = y(v3.y); + v3.z = z(v3.z); + for (int i = 0; i < 4; ++i) + stl_out << ((char*)&v3.x)[i]; + for (int i = 0; i < 4; ++i) + stl_out << ((char*)&v3.y)[i]; + for (int i = 0; i < 4; ++i) + stl_out << ((char*)&v3.z)[i]; + + stl_out << char(0) << char(0); + + stl_faces++; + } + + stl_out.seekp(80); + for (int i = 0; i < 4; ++i) + stl_out << ((char*)&stl_faces)[i]; + + stl_out.close(); +} + +void FabVars::write_svg() +{ + float scale = pixels_per_mm * 0.352778; // 72 dpi + + fstream svg_out; + svg_out.open(outfile_name.c_str(), fstream::trunc | fstream::out); + svg_out << "\n" + << "\n" + << "\n" + << "\n" + << "\n"; + + list::iterator it; + for (it = paths.begin(); it != paths.end(); ++it) { + svg_out << " front().x / scale + << ' ' << (nj - it->front().y - 1) / scale; + Path::iterator p = it->begin(); + Path::iterator path_end = it->end(); + bool loop = (it->front() == it->back()); + if (loop) + path_end--; + while (++p != path_end) + svg_out << " L" << p->x / scale + << ' ' << (nj - p->y - 1) / scale; + if (loop) + svg_out << " Z"; + svg_out << "\"/>\n"; + } + + svg_out << "\n"; + svg_out.close(); +} +// Based on code from math_png +// with attribution +// Neil Gershenfeld +// CBA MIT 3/6/11 +// +// (c) Massachusetts Institute of Technology 2010 +// Permission granted for experimental and personal use; +// license for commercial sale available from MIT. +// + +void fab_write_png_K16(FabVars *v, const char* output_file_name) { + // + // write 16-bit grayscale PNG from FabVars + // + FILE *output_file; + int x,y; + png_uint_32 res_x,res_y; + png_byte color_type; + png_byte bit_depth; + png_byte *ptr; + // + // open PNG file + // + output_file = fopen(output_file_name, "wb"); + png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, + NULL, NULL, NULL); + png_infop info_ptr = png_create_info_struct(png_ptr); + png_init_io(png_ptr, output_file); + // + // set vars + // + bit_depth = 16; + color_type = PNG_COLOR_TYPE_GRAY; + png_set_IHDR(png_ptr, info_ptr, v->ni, v->nj, + bit_depth, color_type, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + res_x = 1000 * v->ni / (v->dx * v->mm_per_unit); + res_y = 1000 * v->nj / (v->dy * v->mm_per_unit); + png_set_pHYs(png_ptr, info_ptr, res_x, res_y, PNG_RESOLUTION_METER); + + png_text text[2]; + + char zmin[10]; + snprintf(zmin, 10, "%g", v->zmin*v->mm_per_unit); + text[0].compression = PNG_TEXT_COMPRESSION_NONE; + text[0].key = (char*)"zmin"; + text[0].text = zmin; + + char zmax[10]; + snprintf(zmax, 10, "%g", (v->zmin+v->dz)*v->mm_per_unit); + text[1].compression = PNG_TEXT_COMPRESSION_NONE; + text[1].key = (char*)"zmax"; + text[1].text = zmax; + png_set_text(png_ptr, info_ptr, text, 2); + + png_write_info(png_ptr, info_ptr); + // + // allocate pixels + // + png_bytep* row_pointers = new png_bytep[v->nj]; + for (y = 0; y < v->nj; ++y) + row_pointers[y] = new png_byte[png_get_rowbytes(png_ptr, info_ptr)]; + + // + // set pixels + // + for (y = 0; y < v->nj; ++y) + for (x = 0; x < v->ni; ++x) { + ptr = &(row_pointers[y][x*2]); + ptr[0] = (v->intensity[y][x] >> 8) & 255; + ptr[1] = v->intensity[y][x] & 255; + } + // + // write, close, and return + // + png_write_image(png_ptr, row_pointers); + png_write_end(png_ptr, NULL); + fclose(output_file); + png_destroy_write_struct(&png_ptr, &info_ptr); + + cout << "write " << output_file_name + << "\n x pixels: " << v->ni << ", y pixels: " << v->nj + << "\n x pixels/m: " << res_x << ", y pixels/m: " << res_y + << "\n dx: " << v->dx << " mm, dy: " << v->dy <<" mm" << endl; + + for (y = 0; y < v->nj; ++y) + delete [] row_pointers[y]; + delete [] row_pointers; +} + +void fab_write_png_RGB24(FabVars *v, const char *output_file_name) { + // + // write 24-bit RGB PNG from FabVars + // + FILE *output_file; + int x,y; + png_uint_32 res_x,res_y; + + png_byte color_type; + png_byte bit_depth; + png_byte *ptr; + // + // open PNG file + // + output_file = fopen(output_file_name, "wb"); + png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, + NULL, NULL, NULL); + png_infop info_ptr = png_create_info_struct(png_ptr); + png_init_io(png_ptr, output_file); + // + // set vars + // + bit_depth = 8; + color_type = PNG_COLOR_TYPE_RGB; + png_set_IHDR(png_ptr, info_ptr, v->ni, v->nj, + bit_depth, color_type, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + res_x = 1000 * v->ni / (v->dx * v->mm_per_unit); + res_y = 1000 * v->nj / (v->dy * v->mm_per_unit); + png_set_pHYs(png_ptr, info_ptr, res_x, res_y, PNG_RESOLUTION_METER); + png_write_info(png_ptr, info_ptr); + // + // allocate pixels + // + png_bytep* row_pointers = new png_bytep[v->nj]; + for (y = 0; y < v->nj; ++y) + row_pointers[y] = new png_byte[png_get_rowbytes(png_ptr, info_ptr)]; + // + // set pixels + // + for (y = 0; y < v->nj; ++y) + for (x = 0; x < v->ni; ++x) { + ptr = &(row_pointers[y][x*3]); + ptr[0] = v->red[y][x]; + ptr[1] = v->green[y][x]; + ptr[2] = v->blue[y][x]; + } + // + // write, close, and return + // + png_write_image(png_ptr, row_pointers); + png_write_end(png_ptr, NULL); + fclose(output_file); + png_destroy_write_struct(&png_ptr, &info_ptr); + + cout << "write " << output_file_name + << "\n x pixels: " << v->ni << ", y pixels: " << v->nj + << "\n x pixels/m: " << res_x << ", y pixels/m: " << res_y + << "\n dx: " << v->dx << " mm, dy: " << v->dy <<" mm" << endl; + + for (y = 0; y < v->nj; ++y) + delete [] row_pointers[y]; + delete [] row_pointers; +} + diff --git a/src/solver/fabvars.hpp b/src/solver/fabvars.hpp new file mode 100644 index 0000000..5ea3564 --- /dev/null +++ b/src/solver/fabvars.hpp @@ -0,0 +1,205 @@ +#ifndef FABTOOLS_H +#define FABTOOLS_H + +#include +#include +#include + +#include +#include + +#include + +#include "fab_interval.hpp" +#include "geometry.hpp" +#include "region.hpp" +#include "progress_bar.hpp" + +enum solver_mode { SOLVE_BOOL, SOLVE_RGB, SOLVE_REAL }; +enum output_mode { OUTPUT_PNG, OUTPUT_STL, + OUTPUT_SVG, OUTPUT_STATS, OUTPUT_NONE }; + +typedef struct FabVars { + /* FabVars(output_mode o) + * + * Constructor, taking in an output mode. + */ + FabVars(output_mode o, int argc, char** argv); + + /* ~FabVars() + * + * Destructor. Deletes pixel arrays if they have been allocated. + */ + ~FabVars(); + + /* void load() + * + * Imports the provided .math file and stores relevant values. + */ + void load(); + + + /* void fill(Region r) + void fill(Region r, unsigned char R, unsigned char G, unsigned char B) + * + * Fills a region of the image with either solid white (with the first + * signature) or with a provided color. + */ + void fill(Region r); + void fill(Region r, unsigned char R, unsigned char G, unsigned char B); + + + /* void add_triangles(std::list tris) + * + * Adds a list of triangles to the stored triangle list by splicing + * the lists together. + */ + void add_triangles(std::list tris); + + /* void add_paths(const PathSet& p) + * + * Adds a set of paths to the stored set of paths, automatically + * decimating contiguous paths. + */ + void add_paths(const PathSet& p); + + /* void add_volume(const int v) + * + * Increments the saved volume count. + */ + void add_volume(const uint64_t v); + + /* void write_png() + * + * Writes out a png (either greyscale or RGB) based on data in either + * intensity or red, green, blue arrays. + */ + void write_png(); + + + /* void write_stl() + * + * Writes out an stl file from data stored in triangles. + */ + void write_stl(); + + + /* void write_stl() + * + * Writes out an svg file from data in paths. + */ + void write_svg(); + + + + /* float x(float i) const + float y(float j) const + float z(float k) const + * + * Converts from lattice coordinates to real coordinates. + */ + float x(float i) const; + float y(float j) const; + float z(float k) const; + + + /* float x(float i) const + float y(float j) const + float z(float k) const + * + * Converts from lattice intervals to real intervals. + */ + FabInterval x(float imin, float imax) const; + FabInterval y(float jmin, float jmax) const; + FabInterval z(float kmin, float kmax) const; + + + /* int k(float z) const + * + * Converts from z coordinate to pixel grid value k + */ + int k(float z) const; + + /* float scale(unsigned int k) const + * + * Returns pixel brightness at a given lattice height. + * For greyscale images, this is between 0 and 65535; for RGB + * it is between 0 and 1. + */ + float scale(unsigned int k) const; + + // Size of the lattice grid, in pixels/voxels + int ni,nj,nk; + + // Minimum volume below which octree recursion won't occur. + unsigned min_volume; + // Minimum area below which quadtree recursion won't occur. + int min_area; + + // Position and size of the lattice in cad units + double xmin,ymin,zmin; + double dx,dy,dz; + + // Scale factors + double pixels_per_mm; + double mm_per_unit; + + // Interpolation count for stl, svg + int quality; + + // SVG stroke + float stroke; + + // SVG decimation error (in pixels^2) + float decimation_error; + + // Color, boolean, or real (distance metric) + solver_mode mode; + + // png, svg, stl, or none + output_mode output; + + // 2D or 3D evaluation style? + bool projection; + + // Input and output file names + std::string infile_name; + std::string outfile_name; + + // The math string of interest + std::string math_string; + + // Pixel arrays for output images + uint8_t **red,**green,**blue; + uint16_t **intensity; + + // Mutex to lock geometry entities + boost::mutex geometry_lock; + + // Saved set of triangles + std::list triangles; + + // Filled solid volume + uint64_t volume; + + // Saved set of paths + PathSet paths; + + // A progress bar that displays progress in ASCII + ProgressBar pb; + +} FabVars; + +/* void fab_write_png_K16(FabVars* v, const char* output_file_name) + * + * Outputs a 16-bit PNG based on v.intensity + */ +void fab_write_png_K16(FabVars* v, const char* output_file_name); + +/* void fab_write_png_RGB24(FabVars* v, const char* output_file_name) + * + * Outputs a 24-bit PNG based on v.{red, green, blue} + */ +void fab_write_png_RGB24(FabVars* v, const char* output_file_name); + +#endif diff --git a/src/solver/geometry.cpp b/src/solver/geometry.cpp new file mode 100644 index 0000000..42b3785 --- /dev/null +++ b/src/solver/geometry.cpp @@ -0,0 +1,260 @@ +#include + +#include "geometry.hpp" + +using namespace std; + +Vec3f::Vec3f() + : x(0), y(0), z(0) +{ /* Nothing to do here */ } + +Vec3f::Vec3f(float x, float y) + : x(x), y(y), z(0) +{ + // Nothing to do here. +} + +Vec3f::Vec3f(float x, float y, float z) + : x(x), y(y), z(z) +{ + // Nothing to do here. +} + +Vec3f Vec3f::operator+(const Vec3f& rhs) const +{ + return Vec3f(x + rhs.x, y + rhs.y, z + rhs.z); +} + +Vec3f Vec3f::operator-(const Vec3f& rhs) const +{ + return Vec3f(x - rhs.x, y - rhs.y, z - rhs.z); +} + +Vec3f Vec3f::operator*(const float rhs) const +{ + return Vec3f(x*rhs, y*rhs, z*rhs); +} + +Vec3f Vec3f::operator/(const float rhs) const +{ + return Vec3f(x/rhs, y/rhs, z/rhs); +} + +bool Vec3f::operator<(const Vec3f& rhs) const +{ + if (x != rhs.x) + return x < rhs.x; + if (y != rhs.y) + return y < rhs.y; + return z < rhs.z; +} + +bool Vec3f::operator==(const Vec3f& rhs) const +{ + return x == rhs.x && y == rhs.y && z == rhs.z; +} + +bool Vec3f::operator!=(const Vec3f& rhs) const +{ + return x != rhs.x || y != rhs.y || z != rhs.z; +} + +float Vec3f::len() const +{ + return sqrt(x*x + y*y + z*z); +} + +Vec3f Vec3f::norm() const +{ + float len = sqrt(x*x + y*y + z*z); + return Vec3f(x / len, y / len, z / len); +} + +Vec3f Vec3f::rotate90() const +{ + return Vec3f(-y, x); +} + +float Vec3f::dot(const Vec3f& rhs) const +{ + return x*rhs.x + y*rhs.y + z*rhs.z; +} + +std::ostream& operator<<(std::ostream& o, const Vec3f& v) +{ + o << '(' << v.x << ", " << v.y << ", " << v.z << ')'; + return o; +} + +//////////////////////////////////////////////////////////////////////////////// + +Triangle::Triangle(Vec3f v1, Vec3f v2, Vec3f v3) + : v1(v1), v2(v2), v3(v3) +{ /* Nothing to do here */ } + +bool Triangle::operator<(const Triangle& rhs) const +{ + if (v1 != rhs.v1) + return v1 < rhs.v1; + if (v2 != rhs.v2) + return v2 < rhs.v2; + return v3 < rhs.v3; +} + +//////////////////////////////////////////////////////////////////////////////// + +Edge::Edge(Vec3f v1, Vec3f v2) + : v1(v1), v2(v2) +{ /* Nothing to do here */ } + +bool Edge::operator<(const Edge& rhs) const +{ + if (v1 != rhs.v1) + return v1 < rhs.v1; + return v2 < rhs.v2; +} + +//////////////////////////////////////////////////////////////////////////////// +Path::Path() + : std::list() +{ /* Nothing to do here */ } + + +Path::Path(Vec3f v1, Vec3f v2) + : std::list() +{ + this->push_back(v1); + this->push_back(v2); +} + +bool Path::operator<(const Path& rhs) const +{ + Path::const_iterator it1 = this->begin(); + Path::const_iterator it2 = rhs.begin(); + + while (true) { + if (it2 == rhs.end()) + return false; + if (it1 == this->end()) + return true; + + if (*it1 != *it2) + return *it1 < *it2; + + ++it1; + ++it2; + } +} + +//////////////////////////////////////////////////////////////////////////////// + +PathSet& PathSet::operator+=(Path rhs) +{ + Vec3f start = rhs.front(); + Vec3f end = rhs.back(); + + // Check to see if we can weld the end of an old path to the start of this + // new path. + std::map::iterator>::iterator it; + it = endings.find(start); + + if (it != endings.end()) { + // Find the existing path that we're going to join + list::iterator target = it->second; + + // Remove the existing path from the look-up maps, since + // it's about to be modified + beginnings.erase(target->front()); + endings.erase(target->back()); + + // Check to see if we should merge this new segment. + // + // Ascii art rendition: + // B + // -----> + // ^ / + // A | / C + // | / + // |/ + // + // A is the last segment of the existing path + // B is the first segment of the path being appended + // C is the path that we will get if we merge the two. + // + // Decimation depends on the area of the triangle ABC. + Path::const_iterator it = target->end(); + Vec3f a = *(--it); + a = a - *(--it); + Vec3f c = *it; + + it = rhs.begin(); + Vec3f b = *(it++); + b = b - *(it); + c = c - *it; + + float A = a.len(); + float B = b.len(); + float C = c.len(); + float area = (A+B+C)*(B+C-A)*(A+C-B)*(A+B-C); + + // Merge segments if the area of the resulting triangle + // is sufficiently small. + if (area < decimation_error) + target->pop_back(); + + // We remove the front node (since it is the end of one path + // and the start of the other), then splice the paths together. + rhs.pop_front(); + rhs.splice(rhs.begin(), *target); + + paths.erase(target); + + start = rhs.front(); + } + + // Check to see if we can weld the end of this new path to the start + // of an old path. + it = beginnings.find(end); + if (it != beginnings.end()) { + list::iterator target = it->second; + beginnings.erase(target->front()); + endings.erase(target->back()); + + // Check to see if we should merge this new segment. + // We calculate the area of the triangle that will be deleted + // if we decimate, then compare it to the variable decimation_error. + Path::const_iterator it = target->begin(); + Vec3f a = *(it); + a = a - *(++it); + Vec3f c = *it; + + it = rhs.end(); + Vec3f b = *(--it); + b = b - *(--it); + c = c - *it; + + float A = a.len(); + float B = b.len(); + float C = c.len(); + float area = (A+B+C)*(B+C-A)*(A+C-B)*(A+B-C); + + // Merge segments if the area of the resulting triangle + // is sufficiently small. + if (area < decimation_error) + target->pop_front(); + + rhs.pop_back(); + rhs.splice(rhs.end(), *target); + + beginnings.erase(end); + paths.erase(target); + + end = rhs.back(); + } + + paths.push_front(rhs); + + beginnings[start] = paths.begin(); + endings[end] = paths.begin(); + return *this; +} \ No newline at end of file diff --git a/src/solver/geometry.hpp b/src/solver/geometry.hpp new file mode 100644 index 0000000..347a9fc --- /dev/null +++ b/src/solver/geometry.hpp @@ -0,0 +1,167 @@ +#ifndef GEOMETRY +#define GEOMETRY + +#include +#include +#include + +// Lightweight 3D vector with overloaded operations +class Vec3f +{ +public: + + /* Vec3f() + Vec3f(float x, float y) + Vec3f(float x, float y, float z) + + * Constructors. Unspecified variables are set to 0. + */ + Vec3f(); + Vec3f(float x, float y); + Vec3f(float x, float y, float z); + + + /* Vec3f operator+(const Vec3f& rhs) const + Vec3f operator-(const Vec3f& rhs) const + Vec3f operator*(const float rhs) const + Vec3f operator/(const float rhs) const + + * Overloaded arithmetic operators. + */ + Vec3f operator+(const Vec3f& rhs) const; + Vec3f operator-(const Vec3f& rhs) const; + Vec3f operator*(const float rhs) const; + Vec3f operator/(const float rhs) const; + + /* bool operator<(const Vec3f& rhs) const + bool operator==(const Vec3f& rhs) const + bool operator!=(const Vec3f& rhs) const + + * Overloaded logical operators. + */ + bool operator<(const Vec3f& rhs) const; + bool operator==(const Vec3f& rhs) const; + bool operator!=(const Vec3f& rhs) const; + + /* Vec3f norm() const + * + * Calculates a normalized (unit) vector. + */ + Vec3f norm() const; + + /* float len() const + * + * Calculates the vector's length. + */ + float len() const; + + /* Vec3f rotate90() const + * + * Returns the vector rotated by 90 degrees about the z axis. + */ + Vec3f rotate90() const; + + + /* float dot(const Vec3f& rhs) const + * + * Calculates the dot product of two vectors. + */ + float dot(const Vec3f& rhs) const; + + // Output operator. + friend std::ostream& operator<<(std::ostream& o, const Vec3f& v); + + float x, y, z; +}; + +// Output operator. +std::ostream& operator<<(std::ostream& o, const Vec3f& v); + +//////////////////////////////////////////////////////////////////////////////// + +// Structure to hold a triangle (three Vec3fs) +typedef struct Triangle { + Triangle(Vec3f v1, Vec3f v2, Vec3f v3); + bool operator<(const Triangle& rhs) const; + + Vec3f v1, v2, v3; +} Triangle; + +//////////////////////////////////////////////////////////////////////////////// + +// Structure to hold an edge (two Vec3fs) +typedef struct Edge { + Edge(Vec3f v1, Vec3f v2); + bool operator<(const Edge& rhs) const; + + Vec3f v1, v2; +} Edge; + +//////////////////////////////////////////////////////////////////////////////// + +// A path is a list of vectors overloaded with a comparison operator. +class Path : public std::list +{ +public: + Path(); + Path(Vec3f v1, Vec3f v2); + + bool operator<(const Path& rhs) const; +}; + +//////////////////////////////////////////////////////////////////////////////// + +// A set of paths. From the outside, it looks like a list, but it also +// supports the += operator and performs decimation on appended paths. +class PathSet +{ +public: + /* PathSet() + * + * Default constructor. + */ + PathSet() : decimation_error(1) { /* Nothing to do here */ } + + /* PathSet(float de) + * + * Constructor which sets decimation_error. + */ + PathSet(float de) : decimation_error(de) { /* Nothing to do here */ } + + /* PathSet& operator+=(Path p) + * + * Appends a path to the set, with automatic path decimation: + * If the path's start or end coincides with another path's end + * or start, then it may merge them depending on the resulting + * decimation error. + */ + PathSet& operator+=(Path p); + + /* std::list::iterator begin() + std::list::iterator end() + std::list::const_iterator begin() + std::list::const_iterator end() + * + * Various iterators, which allows us to look like a list + * to outside users. + */ + std::list::iterator begin() { return paths.begin(); } + std::list::iterator end() { return paths.end(); } + std::list::const_iterator begin() const { return paths.begin(); } + std::list::const_iterator end() const { return paths.end(); } + + // List of active paths + std::list paths; + + // Maximum allowed decimation error + float decimation_error; + +private: + // Maps points to paths for which they are the start. + std::map::iterator> beginnings; + + // Maps points to paths for which they are the end. + std::map::iterator> endings; +}; + +#endif \ No newline at end of file diff --git a/src/solver/imagesolver.cpp b/src/solver/imagesolver.cpp new file mode 100644 index 0000000..64fe7df --- /dev/null +++ b/src/solver/imagesolver.cpp @@ -0,0 +1,210 @@ +#include "imagesolver.hpp" +#include "math_tree.hpp" +#include "node.hpp" +#include "task_buffer.hpp" + +#include "switches.hpp" + +using namespace std; +using boost::logic::tribool; +using boost::thread; + +ImageSolver::ImageSolver(FabVars& v) + : Solver(v) +{ + // Nothing to do here. +} + +ImageSolver::ImageSolver(MathTree* tree, FabVars& v) + : Solver(tree, v) +{ + // Nothing to do here. +} + +/////////////////////////////////////////////////////////////////////////////// + +// Evaluate a single region, either with point-by-point evaluation or +// interval math + recursion. Operates in a single thread and spawns +// no children. +void ImageSolver::evaluate_region(Region r) +{ + // For sufficiently small fractions of the space, do a + // point-by-point evaluation rather than recursing. + if (r.volume <= v.min_volume) { + evaluate_points(r); + v.pb.update(r.volume); + return; + } + + // Convert from pixel regions to intervals + FabInterval X = v.x(r.imin, r.imax); + FabInterval Y = v.y(r.jmin, r.jmax); + FabInterval Z = v.z(r.kmin, r.kmax); + + tree->eval(X, Y, Z); + + // If the result was unambiguous, then fill in that part + // of the image, then return. + if (v.mode == SOLVE_BOOL) { + tribool result = tree->root->result_bool; + if (result) + v.fill(r); + if (!indeterminate(result)) { + v.pb.update(r.volume); + return; + } + } else if (v.mode == SOLVE_REAL) { + tribool result = tree->root->result_interval <= FabInterval(0); + + if (result) + v.fill(r); + if (!indeterminate(result)) { + v.pb.update(r.volume); + return; + } + } else if (v.mode == SOLVE_RGB) { + int result = tree->root->result_color; + if (result != -1) { + v.fill(r, result & 255, + (result >> 8) & 255, + (result >> 16) & 255); + v.pb.update(r.volume); + return; + } + } + + // Split the region and recurse + list subregions = r.split(); + +#if CULL_Z + if (v.nk > 1) + cull_regions(subregions); + if (subregions.size() == 0) + return; +#endif + +#if PRUNE_TREE + tree->push(); +#endif + + list::iterator it; + +/* + // Implementing a full-on thread pool seems to slow it down + Region mine = subregions.front(); + subregions.pop_front(); + for (it = subregions.begin(); it != subregions.end(); ++it) + if (!task_buffer->add(*it, tree)) + evaluate_region(*it); + evaluate_region(mine); + + tree->wait_for_clones(); +*/ + + for (it = subregions.begin(); it != subregions.end(); ++it) + evaluate_region(*it); + + +#if PRUNE_TREE + tree->pop(); +#endif +} + +/////////////////////////////////////////////////////////////////////////////// + +// Evalutes a region full of points, one at a time. +void ImageSolver::evaluate_points(Region r) +{ + for (int k = r.kmax - 1; k >= r.kmin; --k) + { + + // Calculate Z coordinate and height-map scaling. + float Z = v.z(k); + float scale = v.scale(k); + + for (int i = r.imin; i < r.imax; ++i) + { // X loop + float X = v.x(i); + + for (int j = r.jmin; j < r.jmax; ++j) + { // Y loop + float Y = v.y(j); + + // If we can't brighten the image, skip this point. + if ((v.mode == SOLVE_BOOL || v.mode == SOLVE_REAL) + && scale <= v.intensity[v.nj - j - 1][i]) + continue; + + // Evaluate tree + tree->eval(X, Y, Z); + + // Fill in greyscale image + if ((v.mode == SOLVE_BOOL && tree->root->result_bool) || + (v.mode == SOLVE_REAL && tree->root->result_float <= 0)) + { + v.intensity[v.nj - j - 1][i] = scale; + } + // Fill in color image + else if (v.mode == SOLVE_RGB) { + int result = tree->root->result_color; + + // Extract colors from bit-field + unsigned char r = (result & 255) * scale, + g = ((result >> 8) & 255) * scale, + b = ((result >> 16) & 255) * scale; + + // Only brighten the image. + if (r > v.red[v.nj - j - 1][i]) + v.red[v.nj - j - 1][i] = r; + if (g > v.green[v.nj - j - 1][i]) + v.green[v.nj - j - 1][i] = g; + if (b > v.blue[v.nj - j - 1][i]) + v.blue[v.nj - j - 1][i] = b; + } + + } // Y loop + } // X loop + } // Z loop +} + +/////////////////////////////////////////////////////////////////////////////// + +// Removes any regions that cannot change the image. +void ImageSolver::cull_regions(list& subregions) +{ + list::iterator it = subregions.begin(); + + if (v.mode == SOLVE_BOOL || v.mode == SOLVE_REAL) { + while (it != subregions.end()) { + int scale = v.scale(it->kmax - 1); + bool cull = true; + for (int i = it->imin; i < it->imax && cull; ++i) + for (int j = it->jmin; j < it->jmax && cull; ++j) + if (v.intensity[v.nj - j - 1][i] < scale) + cull = false; + if (cull) { + v.pb.update(it->volume); + it = subregions.erase(it); + } + else + ++it; + } + } else if (v.mode == SOLVE_RGB) { + while (it != subregions.end()) { + int scale = v.scale(it->kmax - 1) * 255; + bool cull = true; + for (int i = it->imin; i < it->imax && cull; ++i) + for (int j = it->jmin; j < it->jmax && cull; ++j) + if (scale > v.red[v.nj - j - 1][i] || + scale > v.green[v.nj - j - 1][i] || + scale > v.blue[v.nj - j - 1][i]) + cull = false; + if (cull) { + v.pb.update(it->volume); + it = subregions.erase(it); + } else + ++it; + } + } +} + diff --git a/src/solver/imagesolver.hpp b/src/solver/imagesolver.hpp new file mode 100644 index 0000000..7fdb55f --- /dev/null +++ b/src/solver/imagesolver.hpp @@ -0,0 +1,48 @@ +#ifndef IMAGESOLVER_H +#define IMAGESOLVER_H + +#include + +#include "solver.hpp" + +class ImageSolver : public Solver +{ +public: + /* ImageSolver(FabVars& v) + * ImageSolver(MathTree* tree, FabVars& v) + * + * Constructs an ImageSolver instance. + */ + ImageSolver(FabVars& v); + ImageSolver(MathTree* tree, FabVars& v); + virtual ~ImageSolver() { /* Nothing to do here */ } + + /* virtual void evaluate_region(Region R) + * + * Evaluate a given region recursively, saving results wherever is + * appropriate. + */ + virtual void evaluate_region(Region R); + + /* void evaluate_points(Region R) + * + * Evaluate a region in space + */ + void evaluate_points(Region R); + + /* void cull_regions(list& subregions) + * + * Deletes a regions that can no longer affect the output + * image (i.e. anything that is darker than the existing value + * in that location). + */ + void cull_regions(std::list& subregions); + + /* virtual void save() + * + * Nothing needs to happen here, since evaluate_region and + * evalue_points dump data into the image. + */ + void save() { /* Nothing to do here */ } +}; +#endif \ No newline at end of file diff --git a/src/solver/logic_nodes.cpp b/src/solver/logic_nodes.cpp new file mode 100644 index 0000000..a87a2ea --- /dev/null +++ b/src/solver/logic_nodes.cpp @@ -0,0 +1,187 @@ +#include "logic_nodes.hpp" + +using boost::logic::indeterminate; +using namespace boost::numeric::interval_lib; + +//////////////////////////////////////////////////////////////////////////////// +LogicAnd::LogicAnd() + : BinaryNode(OP_AND) +{ + // Nothing to do here +} + +void LogicAnd::eval(const float X, const float Y, const float Z) +{ + result_bool = left->result_bool && right->result_bool; +} + +void LogicAnd::eval(const FabInterval& X, + const FabInterval& Y, + const FabInterval& Z) +{ + result_bool = left->result_bool && right->result_bool; + marked = !indeterminate(result_bool); +} + +//////////////////////////////////////////////////////////////////////////////// + +LogicOr::LogicOr() + : BinaryNode(OP_OR) +{ + // Nothing to do here +} + +void LogicOr::eval(const float X, const float Y, const float Z) +{ + result_bool = left->result_bool || right->result_bool; +} + +void LogicOr::eval(const FabInterval& X, + const FabInterval& Y, + const FabInterval& Z) +{ + result_bool = left->result_bool || right->result_bool; + marked = !indeterminate(result_bool); +} + +//////////////////////////////////////////////////////////////////////////////// + +LogicNeq::LogicNeq() + : BinaryNode(OP_NEQ) +{ + // Nothing to do here +} + +void LogicNeq::eval(const float X, const float Y, const float Z) +{ + result_bool = left->result_bool != right->result_bool; +} + +void LogicNeq::eval(const FabInterval& X, + const FabInterval& Y, + const FabInterval& Z) +{ + result_bool = left->result_bool != right->result_bool; + marked = !indeterminate(result_bool); +} + +//////////////////////////////////////////////////////////////////////////////// + +LogicNot::LogicNot() + : UnaryNode(OP_NOT) +{ + // Nothing to do here. +} + +void LogicNot::eval(const float X, const float Y, const float Z) +{ + result_bool = !child->result_bool; +} + +void LogicNot::eval(const FabInterval& X, + const FabInterval& Y, + const FabInterval& Z) +{ + result_bool = !child->result_bool; + marked = !indeterminate(result_bool); +} + +//////////////////////////////////////////////////////////////////////////////// + +TransitionLt::TransitionLt() + : BinaryNode(OP_LT) +{ + // Nothing to do here +} + +void TransitionLt::eval(const float X, const float Y, const float Z) +{ + result_bool = left->result_float < right->result_float; +} + +void TransitionLt::eval(const FabInterval& X, + const FabInterval& Y, + const FabInterval& Z) +{ + try { + result_bool = left->result_interval < right->result_interval; + } catch (comparison_error& e) { + result_bool = indeterminate; + } + marked = !indeterminate(result_bool); +} + +//////////////////////////////////////////////////////////////////////////////// + +TransitionLeq::TransitionLeq() + : BinaryNode(OP_LEQ) +{ + // Nothing to do here +} + +void TransitionLeq::eval(const float X, const float Y, const float Z) +{ + result_bool = left->result_float <= right->result_float; +} + +void TransitionLeq::eval(const FabInterval& X, + const FabInterval& Y, + const FabInterval& Z) +{ + try { + result_bool = left->result_interval <= right->result_interval; + } catch (comparison_error& e) { + result_bool = indeterminate; + } + marked = !indeterminate(result_bool); +} + +//////////////////////////////////////////////////////////////////////////////// + +TransitionGt::TransitionGt() + : BinaryNode(OP_GT) +{ + // Nothing to do here +} + +void TransitionGt::eval(const float X, const float Y, const float Z) +{ + result_bool = left->result_float > right->result_float; +} + +void TransitionGt::eval(const FabInterval& X, + const FabInterval& Y, + const FabInterval& Z) +{ + try { + result_bool = left->result_interval > right->result_interval; + } catch (comparison_error& e) { + result_bool = indeterminate; + } + marked = !indeterminate(result_bool); +} + +//////////////////////////////////////////////////////////////////////////////// + +TransitionGeq::TransitionGeq() + : BinaryNode(OP_GEQ) +{ + // Nothing to do here +} + +void TransitionGeq::eval(const float X, const float Y, const float Z) +{ + result_bool = left->result_float >= right->result_float; +} + +void TransitionGeq::eval(const FabInterval& X, + const FabInterval& Y, + const FabInterval& Z) +{ + try { + result_bool = left->result_interval >= right->result_interval; + } catch (comparison_error& e) { + result_bool = indeterminate; + } + marked = !indeterminate(result_bool); +} \ No newline at end of file diff --git a/src/solver/logic_nodes.hpp b/src/solver/logic_nodes.hpp new file mode 100644 index 0000000..e9fbf87 --- /dev/null +++ b/src/solver/logic_nodes.hpp @@ -0,0 +1,16 @@ +#ifndef LOGIC_NODES_H +#define LOGIC_NODES_H + +#include "node.hpp" +#include "node_macro.hpp" + +NODE(LogicAnd, BinaryNode); +NODE(LogicOr, BinaryNode); +NODE(LogicNeq, BinaryNode); +NODE(LogicNot, UnaryNode); + +NODE(TransitionLt, BinaryNode); +NODE(TransitionLeq, BinaryNode); +NODE(TransitionGt, BinaryNode); +NODE(TransitionGeq, BinaryNode); +#endif \ No newline at end of file diff --git a/src/solver/math_dot.cpp b/src/solver/math_dot.cpp new file mode 100644 index 0000000..772585a --- /dev/null +++ b/src/solver/math_dot.cpp @@ -0,0 +1,35 @@ +#include + +#include "fabvars.hpp" +#include "parser.hpp" +#include "math_tree.hpp" + +using namespace std; + +void print_help() +{ + cout << "command line: math_dot in.math out.dot\n" + << " in.math = input math string file\n" + << " out.dot = output dot file\n"; +} + +int main(int argc, char** argv) +{ + if (argc < 3) { + print_help(); + exit(1); + } + + argv++; argc--; // Remove executable name from argv + FabVars v(OUTPUT_NONE, argc, argv); + + Parser p; + MathTree* tree = p.parse(v.math_string, v.mode); + if (!tree) + return 1; + + tree->export_dot(v.outfile_name); + + delete tree; + return 0; +} \ No newline at end of file diff --git a/src/solver/math_png.cpp b/src/solver/math_png.cpp new file mode 100644 index 0000000..bda8ad8 --- /dev/null +++ b/src/solver/math_png.cpp @@ -0,0 +1,64 @@ +#include +#include + +#include "fabvars.hpp" +#include "imagesolver.hpp" +#include "math_tree.hpp" +#include "node.hpp" +#include "parser.hpp" +#include "thread_manager.hpp" + +#include "switches.hpp" + +using namespace std; + + +void print_help() +{ + cout << "command line: math_png in.math out.png [resolution [slices]]\n" + << " in.math = input math string file\n" + << " out.png = output PNG image\n" + << " resolution = pixels per mm (optional, default 10)\n" + << " slices = number of z slices (optional, default full)\n"; +} + + +int main(int argc, char** argv) +{ + if (argc < 3) { + print_help(); + exit(1); + } + + argv++; argc--; // Remove executable name from argv + FabVars v(OUTPUT_PNG, argc, argv); + + Parser p; + MathTree* tree = p.parse(v.math_string, v.mode); + if (!tree) + return 1; + + cout << "Nodes in tree: " << tree->node_count() << endl + << "\t" << tree->constant_count() << " constants\n" + << "Tree depth: " << tree->root->get_weight() << endl + << "Evaluating (region size = " << v.ni << " x " << v.nj + << " x " << v.nk << ")" + << endl; + +#if MULTITHREADED + ThreadManager tm(v); + tm.evaluate(tree); +#else + ImageSolver s(tree, v); + s.evaluate_region(Region(v)); +#endif + + cout << "\n"; + v.write_png(); + +#if MULTITHREADED + delete tree; +#endif + + return 0; +} \ No newline at end of file diff --git a/src/solver/math_ray.cpp b/src/solver/math_ray.cpp new file mode 100644 index 0000000..77f56e3 --- /dev/null +++ b/src/solver/math_ray.cpp @@ -0,0 +1,69 @@ +#include +#include + +#include "fabvars.hpp" +#include "raycaster.hpp" +#include "math_tree.hpp" +#include "node.hpp" +#include "parser.hpp" +#include "thread_manager.hpp" + +#include "switches.hpp" + +using namespace std; + + +void print_help() +{ + cout << "command line: math_ray in.math out.png [resolution]\n" + << " in.math = input math string file\n" + << " out.png = output PNG image\n" + << " resolution = pixels per mm (optional, default 10)\n"; +} + + +int main(int argc, char** argv) +{ + if (argc < 3) { + print_help(); + exit(1); + } + + argv++; argc--; // Remove executable name from argv + FabVars v(OUTPUT_PNG, argc, argv); + v.projection = true; + + if (v.mode != SOLVE_REAL) { + cerr << "Error: math_ray only works on Real math strings." << endl; + exit(4); + } + + Parser p; + MathTree* tree = p.parse(v.math_string, v.mode); + if (!tree) + return 1; + + cout << "Nodes in tree: " << tree->node_count() << endl + << "\t" << tree->constant_count() << " constants\n" + << "Tree depth: " << tree->root->get_weight() << endl + << "Evaluating (region size = " << v.ni << " x " << v.nj + << " x " << v.nk << ")" + << endl; + +#if MULTITHREADED + ThreadManager tm(v); + tm.evaluate(tree); +#else + Raycaster s(tree, v); + s.evaluate_region(Region(v)); +#endif + + cout << "\n"; + v.write_png(); + +#if MULTITHREADED + delete tree; +#endif + + return 0; +} \ No newline at end of file diff --git a/src/solver/math_stats.cpp b/src/solver/math_stats.cpp new file mode 100644 index 0000000..f0d271e --- /dev/null +++ b/src/solver/math_stats.cpp @@ -0,0 +1,63 @@ +#include +#include +#include + +#include "fabvars.hpp" +#include "volsolver.hpp" +#include "math_tree.hpp" +#include "node.hpp" +#include "parser.hpp" +#include "thread_manager.hpp" + +#include "switches.hpp" + +using namespace std; + + +void print_help() +{ + cout << "command line: math_stats in.math [resolution]\n" + << " in.math = input math string file\n" + << " resolution = pixels per mm (optional, default 10)\n"; +} + + +int main(int argc, char** argv) +{ + if (argc < 1) { + print_help(); + exit(1); + } + + argv++; argc--; // Remove executable name from argv + FabVars v(OUTPUT_STATS, argc, argv); + + Parser p; + MathTree* tree = p.parse(v.math_string, v.mode); + if (!tree) + return 1; + + cout << "Nodes in tree: " << tree->node_count() << endl + << "\t" << tree->constant_count() << " constants\n" + << "Tree depth: " << tree->root->get_weight() << endl + << "Evaluating (region size = " << v.ni << " x " << v.nj + << " x " << v.nk << ")" + << endl; + +#if MULTITHREADED + ThreadManager tm(v); + tm.evaluate(tree); +#else + VolSolver s(tree, v); + s.evaluate_region(Region(v)); +#endif + + cout << "\n"; + cout << "Filled volume = " << v.volume/pow(v.pixels_per_mm,3) << " mm^3" << endl; + +#if MULTITHREADED + delete tree; +#endif + + return 0; +} \ No newline at end of file diff --git a/src/solver/math_stl.cpp b/src/solver/math_stl.cpp new file mode 100644 index 0000000..fd370b1 --- /dev/null +++ b/src/solver/math_stl.cpp @@ -0,0 +1,64 @@ +#include + +#include "fabvars.hpp" +#include "math_tree.hpp" +#include "node.hpp" +#include "parser.hpp" +#include "thread_manager.hpp" +#include "trisolver.hpp" + +#include "switches.hpp" + +using namespace std; + + +void print_help() +{ + cout << "command line: math_stl in.math out.stl [resolution [quality]]\n" + << " in.math = input math string file\n" + << " out.png = output PNG image\n" + << " resolution = voxels per mm (optional, default 10)\n" + << " quality = voxel interpolation level (default 8)\n"; +} + + +int main(int argc, char** argv) +{ + if (argc < 3) { + print_help(); + exit(1); + } + + argv++; argc--; // Remove executable name from argv + FabVars v(OUTPUT_STL, argc, argv); + + if (v.mode != SOLVE_BOOL && v.mode != SOLVE_REAL) { + cerr << "Error: math_stl only works on Boolean or Real math strings." << endl; + exit(4); + } + + Parser p; + MathTree* tree = p.parse(v.math_string, v.mode); + if (!tree) + return 1; + + cout << "Nodes in tree: " << tree->node_count() << endl + << "Tree depth: " << tree->root->get_weight() << endl + << "Evaluating (region size = " << v.ni << " x " << v.nj + << " x " << v.nk << ")" + << endl; +#if MULTITHREADED + ThreadManager tm(v); + tm.evaluate(tree); +#else + TriSolver s(tree, v); + s.evaluate_region(Region(v)); + s.save(); +#endif + + cout << "\nWriting STL..." << endl; + v.write_stl(); + + delete tree; + return 0; +} \ No newline at end of file diff --git a/src/solver/math_svg.cpp b/src/solver/math_svg.cpp new file mode 100644 index 0000000..30392f7 --- /dev/null +++ b/src/solver/math_svg.cpp @@ -0,0 +1,66 @@ +#include + +#include "fabvars.hpp" +#include "edgesolver.hpp" +#include "math_tree.hpp" +#include "parser.hpp" +#include "node.hpp" +#include "thread_manager.hpp" + +#include "switches.hpp" + +using namespace std; + +void print_help() +{ + cout << "command line: math_svg in.math out.svg [resolution [slices [error [quality]]]]\n" + << " in.math = input math string file\n" + << " out.png = output PNG image\n" + << " resolution = voxels per mm (default: 10)\n" + << " slices = z slices (defaults: 1 for 2D models, 10 for 3D models)\n" + << " error = maximum decimation error (in mm^2)\n" + << " quality = voxel interpolation level (default: 8)\n" + << "Note: output svgs are at 72 dpi." << endl; +} + + +int main(int argc, char** argv) +{ + if (argc < 3) { + print_help(); + exit(1); + } + + argv++; argc--; // Remove executable name from argv + FabVars v(OUTPUT_SVG, argc, argv); + + if (v.mode != SOLVE_BOOL && v.mode != SOLVE_REAL) { + cerr << "Error: math_svg only works on Boolean or Real math strings." << endl; + exit(4); + } + + Parser p; + MathTree* tree = p.parse(v.math_string, v.mode); + if (!tree) + return 1; + + cout << "Nodes in tree: " << tree->node_count() << endl + << "Tree depth: " << tree->root->get_weight() << endl + << "Evaluating (region size = " << v.ni << " x " << v.nj + << " x " << v.nk << ")" + << endl; +#if MULTITHREADED + ThreadManager tm(v); + tm.evaluate(tree); +#else + EdgeSolver s(tree, v); + s.evaluate_region(Region(v)); + s.save(); +#endif + + cout << "\nWriting SVG." << endl; + v.write_svg(); + + delete tree; + return 0; +} \ No newline at end of file diff --git a/src/solver/math_tree.cpp b/src/solver/math_tree.cpp new file mode 100644 index 0000000..5f57edb --- /dev/null +++ b/src/solver/math_tree.cpp @@ -0,0 +1,243 @@ +#include + +#include "math_tree.hpp" +#include "node.hpp" +#include "switches.hpp" + +using namespace std; + +typedef boost::lock_guard locker; + +MathTree::MathTree() + : root(NULL), levels(NULL), active_nodes(NULL), dNodes(NULL), parent(NULL) +{ + // Nothing to do here +} + +MathTree::~MathTree() +{ + // If this tree is a clone, then notify the parent (because the parent will be + // waiting for this tree to be deleted before it can delete itself). + if (parent) { + locker lock(parent->mutex); + parent->done = true; + parent->condition.notify_one(); + } + + // Make sure that all children have been deleted. + wait_for_clones(); + + // Delete all nodes stored in the tree, from top to bottom (to prevent + // issues due to reference subtraction) + if (levels) + for (int i = num_levels - 1; i >= 0; --i) { + for (int j = 0; j < active_nodes[i]; ++j) + delete levels[i][j]; + delete [] levels[i]; + } + + for (MathTreeIter it = constants.begin(); it != constants.end(); ++it) + delete *it; + + delete [] levels; + delete [] active_nodes; + delete [] dNodes; +} + +MathTree* MathTree::clone() +{ + MathTree* m = new MathTree(); +// cout << "MathTree::clone() is cloning " << this << " to " << m << endl; + m->num_levels = num_levels; + m->levels = new Node**[num_levels]; + m->active_nodes = new int[num_levels]; + m->dNodes = new list[num_levels]; + + // Clone all of the active nodes. + // Since constants aren't changing, we don't need to clone them. + for (int i = 0; i < num_levels; ++i) { + m->active_nodes[i] = active_nodes[i]; + m->levels[i] = new Node*[active_nodes[i]]; + for (int j = 0; j < active_nodes[i]; ++j) + m->levels[i][j] = levels[i][j]->clone(); + } + + // If the entire tree is constant, then point back to this + // original tree's root. + if (num_levels) + m->root = m->levels[num_levels - 1][0]; + else + m->root = root; + + clones.push_back(new ThreadComm()); + m->parent = clones.back(); + + return m; +} + +void MathTree::wait_for_clones() +{ + + // Go through the list, waiting for each of the children. + list::iterator it = clones.begin(); + while (it != clones.end()) + { + { + boost::unique_lock lock((**it).mutex); + while (!(**it).done) + (**it).condition.wait(lock); + } + delete *it; + it = clones.erase(it); + } +} + +void MathTree::add(Node* n) +{ + if (!n) + return; + else if (n->marked) { + constants.push_back(n); + } else { + unsigned int L = n->get_weight(); + if (L + 1 > level_list.size()) + level_list.resize(L + 1); + level_list[L].push_back(n); + } +} + +// Converts the friendly, easy-to use C++ STL data structures into +// fast C-style arrays of pointers. +void MathTree::pack() +{ + num_levels = level_list.size(); + levels = new Node**[num_levels]; + active_nodes = new int[num_levels]; + dNodes = new list[num_levels]; + + for (int i = 0; i < num_levels; ++i) + { + active_nodes[i] = level_list[i].size(); + levels[i] = new Node*[active_nodes[i]]; + int j = 0; + while (level_list[i].size()) { + levels[i][j++] = level_list[i].front(); + level_list[i].pop_front(); + } + } +} + +void MathTree::set_root(Node* r) +{ + root = r; +} + +int MathTree::node_count() const +{ + int total = 0; + for(int i = 0; i < num_levels; ++i) + total += active_nodes[i]; + return total; +} + +int MathTree::constant_count() const +{ + return constants.size(); +} + + +// Evaluate a single point +void MathTree::eval(const float X, + const float Y, + const float Z) +{ + for (int i = 0; i < num_levels; ++i) + for (int j = 0; j < active_nodes[i]; ++j) + levels[i][j]->eval(X, Y, Z); +} + +// Evaluate an interval region +void MathTree::eval(const FabInterval& X, + const FabInterval& Y, + const FabInterval& Z) +{ + for (int i = 0; i < num_levels; ++i) + for (int j = 0; j < active_nodes[i]; ++j) + levels[i][j]->eval(X, Y, Z); +} + +void MathTree::push() +{ + // Pass downwards through the tree. If a node is cached + // (meaning it won't change upon further recursion), then + // tell its children that they are no longer being watched. + + // If all of a node's parents are no longer watching it, then + // remove that node from the tree as well (and inform its children) + + // Keep track of how many nodes were removed from the tree + // in the dNodes[i] stack. + + for (int i = num_levels - 2; i >= 0; --i) { + dNodes[i].push_back(0); + for (int j = 0; j < active_nodes[i]; ++j) { + if (levels[i][j]->cacheable()) + { + levels[i][j]->deactivate(); + swap(levels[i][j--], levels[i][--active_nodes[i]]); + dNodes[i].back()++; + } + } + } +} + +void MathTree::pop() +{ + // Increase the number of active nodes in the arrays so that + // previous cached nodes are now evaluated. + + for (int i = 0; i < num_levels - 1; ++i) { + + // Activate each of the previously disabled nodes. + for (int n = 0; n < dNodes[i].back(); ++n) { + levels[i][active_nodes[i]++]->activate(); + } + dNodes[i].pop_back(); + } +} + +ostream& operator<<(ostream& o, const MathTree& t) +{ + o << "Constants (" << t.constants.size() << " items)\n"; + MathTreeConstIter it; + for (it = t.constants.begin(); it != t.constants.end(); ++it) + o << '\t' << **it << '\n'; + o << endl; + + for (int i = 0; i < t.num_levels; ++i) + { + o << "Level " << i << " (" << t.active_nodes[i] << " items)\n"; + for (int j = 0; j < t.active_nodes[i]; ++j) + o << '\t' << *t.levels[i][j] << '\n'; + o << endl; + } + return o; +} + +void MathTree::export_dot(string filename) const +{ + ofstream out; + out.open(filename.c_str()); + out << "digraph math {\n" + << "node [rank = min, fontsize = 14, fontname = Arial]\n"; + MathTreeConstIter it; + for (it = constants.begin(); it != constants.end(); ++it) + (**it).dot(out); + + for (int i = 0; i < num_levels; ++i) + for (int j = 0; j < active_nodes[i]; ++j) + levels[i][j]->dot(out); + + out << "}\n"; + out.close(); +} diff --git a/src/solver/math_tree.hpp b/src/solver/math_tree.hpp new file mode 100644 index 0000000..422913b --- /dev/null +++ b/src/solver/math_tree.hpp @@ -0,0 +1,177 @@ +#ifndef MATH_TREE_H +#define MATH_TREE_H + +#include +#include +#include + +#include "fab_interval.hpp" +#include "region.hpp" + +// Forward declaration of node. +class Node; + + + +// This class stores a math expression tree. +class MathTree +{ +public: + /* MathTree() + * + * Simple constructor initializes pointers to NULL and nothing else. + */ + MathTree(); + + + /* ~MathTree() + * + * Destructor deletes all stored nodes. + */ + ~MathTree(); + + + /* MathTree* clone() + * + * Clones the MathTree. Constants and cached values are not cloned, + * so anything that references them will point back to this tree. + * + * Creates a ThreadComm object so that the child can notify us on + * destruction. + */ + MathTree* clone(); + + + /* void wait_for_clones() + * + * Waits for all clones to be deleted before returning. + */ + void wait_for_clones(); + + + /* void add(Node* n) + * + * Adds a node to the tree, based on the node's weight. + * The node is added to the list level_list if it is unmarked, + * otherwise it is added to the list constants. + * + * Note that pack() must be called before the tree can be evaluated. + */ + void add(Node* n); + + /* void pack() + * + * Converts the STL lists into C-style arrays for fast access. + */ + void pack(); + + /* void set_root(Node* r) + * + * Sets the root of the tree. + */ + void set_root(Node* r); + + /* int node_count() const + * + * Counts the number of active nodes in the tree. + */ + int node_count() const; + + /* int constant_count() const + * + * Counts the number of constant nodes in the tree. + */ + int constant_count() const; + + + /* int depth() const + * + * Returns the tree's depth. + */ + int depth() const { return num_levels; } + + /* void eval(X, Y, Z) + * + * Evaluates every active node in the tree, from leaves to root. + * After an evaluate, each node will have correct results stored in + * its result_{float,interval,bool,color} variables. Each node may + * also be marked if it can be ignored upon further recursion. + */ + void eval(const float X, + const float Y, + const float Z); + void eval(const FabInterval& X, + const FabInterval& Y, + const FabInterval& Z); + + /* void push() + * + * Traveling downward through the tree, deactivating nodes that are no + * longer relevant (either because they will not change values upon + * deeper recursion, or because no one is referencing them anymore). + * + * Deactivated nodes are moved to the back of their list, and the number + * of active nodes for that level is decreased by one. + * + * This function keeps track of how many nodes have been deactivated on + * each tree level in the variable dNodes. + */ + void push(); + + /* void pop() + * + * Traverse through the tree, re-activating nodes that had been + * deactivated by an earlier push(). + */ + void pop(); + + /* void export_dot(std::string filename) const + * + * Exports the tree to a dot/graphviz file with a given filename. + */ + void export_dot(std::string filename) const; + + // Output helper function + friend std::ostream& operator<<(std::ostream& o, const MathTree& t); + + // Root of the tree (stores the overall tree's results) + Node* root; + +private: + // Nice STL vectors and lists to store nodes while we're building the tree + std::vector< std::list > level_list; + std::list constants; + + // Fast C-style array for when we're actually using the tree. + Node*** levels; + + // Keeps track of how many nodes are active on each level + int* active_nodes; + + // Total number of levels in the tree + int num_levels; + + // Array of stacks storing the number of nodes that have been deactivated + // in each push() operation. Each level has an independent stack + // (implemented as a list) + std::list* dNodes; + + + typedef struct ThreadComm { + ThreadComm() : done(false) {} + bool done; + boost::mutex mutex; + boost::condition_variable condition; + } ThreadData; + + std::list clones; + ThreadComm* parent; + +}; + +// Helpful typedefs +typedef std::list::iterator MathTreeIter; +typedef std::list::const_iterator MathTreeConstIter; + +std::ostream& operator<<(std::ostream& o, const MathTree& t); +#endif \ No newline at end of file diff --git a/src/solver/node.cpp b/src/solver/node.cpp new file mode 100644 index 0000000..e8626e5 --- /dev/null +++ b/src/solver/node.cpp @@ -0,0 +1,307 @@ +#include "node.hpp" +#include "switches.hpp" + +#include "numeric_nodes.hpp" +#include "logic_nodes.hpp" +#include "translator_nodes.hpp" +#include "color_nodes.hpp" + +using namespace std; +using boost::logic::indeterminate; + +Node::Node(opcode operation) + : result_float(0), result_interval(0), + result_bool(indeterminate), result_color(0), + marked(false), operation(operation), + ref_count(0), weight(0), + clone_address(this) +{ + // Nothing to do here. +} + +Node::~Node() +{ + // Nothing to do here +} + +Node* Node::make(opcode operation) +{ + switch (operation) + { + case OP_AND: return new LogicAnd(); + case OP_OR: return new LogicOr(); + case OP_NEQ: return new LogicNeq(); + case OP_NOT: return new LogicNot(); + case OP_LT: return new TransitionLt(); + case OP_LEQ: return new TransitionLeq(); + case OP_GT: return new TransitionGt(); + case OP_GEQ: return new TransitionGeq(); + case OP_ABS: return new NumericAbs(); + case OP_COS: return new NumericCos(); + case OP_SIN: return new NumericSin(); + case OP_ACOS: return new NumericACos(); + case OP_ASIN: return new NumericASin(); + case OP_ATAN: return new NumericATan(); + case OP_SQRT: return new NumericSqrt(); + case OP_NEGATIVE: return new NumericNeg(); + case OP_EXP: return new NumericExp(); + case OP_SGN: return new NumericSgn(); + case OP_PLUS: return new NumericPlus(); + case OP_MINUS: return new NumericMinus(); + case OP_MULT: return new NumericMult(); + case OP_DIV: return new NumericDiv(); + case OP_ATAN2: return new NumericATan2(); + case OP_POW: return new NumericPow(); + case OP_MIN: return new NumericMin(); + case OP_MAX: return new NumericMax(); + case NUM_CONST: return new NumericConst(0); + case VAR_X: return new VarX(); + case VAR_Y: return new VarY(); + case VAR_Z: return new VarZ(); + case COLOR_AND: return new ColorAnd(); + case COLOR_OR: return new ColorOr(); + case COLOR_NOT: return new ColorNot(); + case NUM2BOOL: return new NumToBool(); + case BOOL2NUM: return new BoolToNum(); + case NUM2COLOR: return new NumToColor(); + case BOOL2COLOR: return new BoolToColor(); + default: + cerr << "Unknown clone target " << operation << endl; + exit(1); + } +} + +void Node::deactivate() +{ + // If we clone this node, then we want to be pointing back here + // (not to a previous clone address). + clone_address = this; +} + +void Node::activate() +{ + // Nothing to do here +} + +void Node::dot(ostream& o) const +{ + o << "\"p" << this << "\" [shape = " << dot_shape(operation) + << ", color = " << dot_color(operation) << ", fontsize = 24, label = \"" + << dot_label(operation) << "\""; + if (marked) + o << ", style=\"dotted\""; + o << "]\n"; +} + +ostream& operator<<(ostream& o, const Node& t) +{ + t.print(o); + return o; +} +//////////////////////////////////////////////////////////////////////////////// + +UnaryNode::UnaryNode(opcode operation) + : Node(operation), child(NULL) +{ + // Nothing to do here. +} + +Node* UnaryNode::clone() +{ + clone_address = make(operation); + static_cast(clone_address)-> + set_child(child->get_clone_address()); + return clone_address; +} + + +void UnaryNode::deactivate() +{ + Node::deactivate(); + child->sub_ref(); +} + +void UnaryNode::activate() +{ + child->add_ref(); + Node::activate(); +} + +bool UnaryNode::operator==(const Node& rhs) +{ + if (operation != rhs.op() ) + return false; + return *child == *static_cast(rhs).child; +} + +void UnaryNode::print(ostream& o) const +{ + o << operation; + if (child) { + o << '('; + child->print(o); + o << ')'; + } +} + +void UnaryNode::dot(ostream& o) const +{ + Node::dot(o); + + o << "p" << this << "->p" << child + << " [color = " << dot_arrow(operation) << "]\n"; +} + +void UnaryNode::set_child(Node* child_) +{ + child = child_; + child->add_ref(); + marked = child->marked; + if (marked) { + // Evaluate for both floats and intervals to ensure that + // result_float and result_interval are both populated + eval(0,0,0); + eval(FabInterval(0),FabInterval(0),FabInterval(0)); + } else { + weight = child->get_weight() + 1; + } +} + +bool UnaryNode::null_children() const +{ + return child == NULL; +} + +//////////////////////////////////////////////////////////////////////////////// + +BinaryNode::BinaryNode(opcode operation) + : Node(operation), left(NULL), right(NULL) +{ + // Nothing to do here. +} + +Node* BinaryNode::clone() +{ + clone_address = make(operation); + static_cast(clone_address)-> + set_children(left->get_clone_address(), + right->get_clone_address()); + return clone_address; +} + +void BinaryNode::deactivate() +{ + Node::deactivate(); + left->sub_ref(); + right->sub_ref(); +} + +void BinaryNode::activate() +{ + left->add_ref(); + right->add_ref(); + Node::activate(); +} + +bool BinaryNode::operator==(const Node& rhs) +{ + if (operation != rhs.op() ) + return false; + return *left == *static_cast(rhs).left && + *right == *static_cast(rhs).right; +} + +void BinaryNode::print(ostream& o) const +{ + if (operation == OP_ATAN2 || operation == OP_POW || + operation == OP_MAX || operation == OP_MIN) + o << operation; + if (left){ + o << '('; + left->print(o); + } + + if (operation == OP_ATAN2 || operation == OP_POW || + operation == OP_MAX || operation == OP_MIN) { + if (left && right) + o << ", "; + } + else + o << operation; + + if (right) { + right->print(o); + o << ')'; + } +} + +void BinaryNode::dot(ostream& o) const +{ + Node::dot(o); + + o << "p" << this << "->p" << left + << " [color = " << dot_arrow(operation) << "]\n"; + o << "p" << this << "->p" << right + << " [color = " << dot_arrow(operation) << "]\n"; +} + +void BinaryNode::set_children(Node* left_, Node* right_) +{ + left = left_; + left->add_ref(); + right = right_; + right->add_ref(); + marked = left->marked && right->marked; + if (marked) { + // Evaluate for both floats and intervals to ensure that + // result_float and result_interval are both populated + eval(0,0,0); + eval(FabInterval(0),FabInterval(0),FabInterval(0)); + } else { + weight = max(left->get_weight() + 1, right->get_weight() + 1); + } + +} + +bool BinaryNode::null_children() const +{ + return left == NULL || right == NULL; +} + +//////////////////////////////////////////////////////////////////////////////// + +NonaryNode::NonaryNode(opcode operation) + : Node(operation) +{ + // Nothing to do here. +} + +Node* NonaryNode::clone() +{ + clone_address = make(operation); + return clone_address; +} + +void NonaryNode::deactivate() { + Node::deactivate(); +} + +void NonaryNode::activate() +{ + Node::activate(); +} + +bool NonaryNode::operator==(const Node& rhs) +{ + return operation == rhs.op(); +} + +void NonaryNode::print(ostream& o) const +{ + o << operation; +} + +bool NonaryNode::null_children() const +{ + return false; +} \ No newline at end of file diff --git a/src/solver/node.hpp b/src/solver/node.hpp new file mode 100644 index 0000000..a074a9f --- /dev/null +++ b/src/solver/node.hpp @@ -0,0 +1,221 @@ +#ifndef NODE_H +#define NODE_H + +#include + +#include + +#include "opcodes.hpp" +#include "fab_interval.hpp" +#include "region.hpp" + +class Node +{ +public: + // Constructor and destructor. Nothing unusual here. + Node(opcode operation); + virtual ~Node(); + + /* virtual Node* clone() = 0 + * + * Clones a node (non-recursively). + * Refers to the clone_address member of children, which + * must be populated with a valid value. + */ + virtual Node* clone() = 0; + + + /* static Node* make(opcode op) + * + * Creates a new node with the provided opcode. + * + * Used by clone() to duplicate an existing node. + */ + static Node* make(opcode op); + + + /* virtual void eval(X, Y, Z) = 0 + * + * Evaluates the node, storing results in the local + * result_{float,interval,bool,color} variables (depending on + * node output type and whether we're solving for intervals or + * floats). Nodes may also become marked if they can be ignored + * upon deeper recursion. + */ + virtual void eval(const float X, + const float Y, + const float Z) = 0; + virtual void eval(const FabInterval& X, + const FabInterval& Y, + const FabInterval& Z) = 0; + + + /* int add_ref() + * int sub_ref() + * + * Increments and decrements to the node's reference count, returning + * the updated value. + */ + int add_ref() { return ++ref_count; } + int sub_ref() { return --ref_count; } + + + /* virtual void deactivate() + * virtual void activate() + * + * Turns a node on or off. Turning a node off entails: + * Setting 'marked = true' + * Subtracting a reference from all children. + * + * Turning a node on is the opposite. + */ + virtual void deactivate(); + virtual void activate(); + + + /* int get_weight() + * + * Return the node's weight (i.e. distance from the bottom of the + * tree). Constants and variables have weight 0, everything else + * has weight of max(children's weights) + 1. + */ + int get_weight() const { return weight; } + + + /* opcode op() const + * + * Return this node's opcode. + */ + opcode op() const { return operation; } + + + /* bool ignored() const + * + * Returns true if no other nodes are watching this node + * (i.e. ref_count == 0). + */ + bool ignored() const { return ref_count == 0; } + + + /* bool cacheable() const + * + * Returns true if no other nodes are watching this node + * (i.e. ref_count == 0) or if this node is marked. + */ + bool cacheable() const { return marked || ref_count == 0; } + + /* Node* get_clone_address() + * + * Returns the address of this node's youngest clone. + */ + Node* get_clone_address() const { return clone_address; } + + + /* virtual bool operator==(const Node& rhs) = 0 + * + * Recursively compare two nodes, going down the + * tree as far as needed to check equality. + */ + virtual bool operator==(const Node& rhs) = 0; + + + /* virtual void print(std::ostream& o) const + * + * Function defined by derived classes to print a node + * and its children in human-readable (hah!) form. + */ + virtual void print(std::ostream& o) const = 0; + + + /* virtual void dot(std::ostream& o) const + * + * Function defined by derived classes to print a node + * and its children in graphviz format. + */ + virtual void dot(std::ostream& o) const; + + + /* virtual bool null_children const + * + * Returns true if the node's children are null, false otherwise. + */ + virtual bool null_children() const=0; + + // Results are stored locally and then looked up by parents + float result_float; + FabInterval result_interval; + boost::tribool result_bool; + int result_color; + + // Nodes are marked if they should to be moved to the cache (with + // a math_tree push operation). + bool marked; + +protected: + const opcode operation; + int ref_count; + + int weight; + Node* clone_address; +}; + +std::ostream& operator<<(std::ostream& o, const Node& t); + +//////////////////////////////////////////////////////////////////////////////// +class UnaryNode : public Node +{ +public: + UnaryNode(opcode operation); + virtual ~UnaryNode() {/*Nothing to do here*/}; + Node* clone(); + + void deactivate(); + void activate(); + + bool operator==(const Node& rhs); + void print(std::ostream& o) const; + void dot(std::ostream& o) const; + + void set_child(Node* child); + virtual bool null_children() const; +protected: + Node* child; +}; +//////////////////////////////////////////////////////////////////////////////// +class BinaryNode : public Node +{ +public: + BinaryNode(opcode operation); + virtual ~BinaryNode() {/*Nothing to do here*/}; + Node* clone(); + + void deactivate(); + void activate(); + + bool operator==(const Node& rhs); + void print(std::ostream& o) const; + void dot(std::ostream& o) const; + + void set_children(Node* left, Node* right); + virtual bool null_children() const; +protected: + Node* left; + Node* right; +}; +//////////////////////////////////////////////////////////////////////////////// +class NonaryNode : public Node +{ +public: + NonaryNode(opcode operation); + virtual ~NonaryNode() {/*Nothing to do here*/}; + Node* clone(); + + void deactivate(); + void activate(); + + bool operator==(const Node& rhs); + void print(std::ostream& o) const; + virtual bool null_children() const; +}; + +#endif diff --git a/src/solver/node_macro.hpp b/src/solver/node_macro.hpp new file mode 100644 index 0000000..8798a41 --- /dev/null +++ b/src/solver/node_macro.hpp @@ -0,0 +1,34 @@ +#ifndef NODE_MACRO_H +#define NODE_MACRO_H + +// To save text, we use the following macro to define classes in bulk. +#define NODE(NAME, TYPE) \ +class NAME : public TYPE \ +{ \ +public: \ + NAME(); \ + virtual ~NAME() {/*Nothing to do here*/}; \ + void eval(const float X, \ + const float Y, \ + const float Z); \ + void eval(const FabInterval& X, \ + const FabInterval& Y, \ + const FabInterval& Z); \ +} + +#define TRANSLATOR(NAME, TYPE) \ +class NAME : public TYPE \ +{ \ +public: \ + NAME(); \ + NAME(Node* c); \ + virtual ~NAME() {/*Nothing to do here*/}; \ + void eval(const float X, \ + const float Y, \ + const float Z); \ + void eval(const FabInterval& X, \ + const FabInterval& Y, \ + const FabInterval& Z); \ +} + +#endif \ No newline at end of file diff --git a/src/solver/numeric_nodes.cpp b/src/solver/numeric_nodes.cpp new file mode 100644 index 0000000..663f9ea --- /dev/null +++ b/src/solver/numeric_nodes.cpp @@ -0,0 +1,559 @@ +#include "numeric_nodes.hpp" +#include "switches.hpp" + +using namespace std; + +using boost::logic::indeterminate; +using namespace boost::numeric::interval_lib; + +//////////////////////////////////////////////////////////////////////////////// + +NumericAbs::NumericAbs() + : UnaryNode(OP_ABS) +{ + // Nothing to do here. +} + +void NumericAbs::eval(const float X, const float Y, const float Z) +{ + result_float = abs(child->result_float); +} + +void NumericAbs::eval(const FabInterval& X, + const FabInterval& Y, + const FabInterval& Z) +{ + result_interval = abs(child->result_interval); +} + +//////////////////////////////////////////////////////////////////////////////// + +NumericCos::NumericCos() + : UnaryNode(OP_COS) +{ + // Nothing to do here. +} + +void NumericCos::eval(const float X, const float Y, const float Z) +{ + result_float = cos(child->result_float); +} + +void NumericCos::eval(const FabInterval& X, + const FabInterval& Y, + const FabInterval& Z) +{ + result_interval = cos(child->result_interval); +} + +//////////////////////////////////////////////////////////////////////////////// + +NumericSin::NumericSin() + : UnaryNode(OP_SIN) +{ + // Nothing to do here. +} + +void NumericSin::eval(const float X, const float Y, const float Z) +{ + result_float = sin(child->result_float); +} + +void NumericSin::eval(const FabInterval& X, + const FabInterval& Y, + const FabInterval& Z) +{ + result_interval = sin(child->result_interval); +} + +//////////////////////////////////////////////////////////////////////////////// + +NumericACos::NumericACos() + : UnaryNode(OP_ACOS) +{ + // Nothing to do here. +} + +void NumericACos::eval(const float X, const float Y, const float Z) +{ + result_float = acos(child->result_float); +} + +void NumericACos::eval(const FabInterval& X, + const FabInterval& Y, + const FabInterval& Z) +{ + result_interval = acos(child->result_interval); +} + +//////////////////////////////////////////////////////////////////////////////// + +NumericASin::NumericASin() + : UnaryNode(OP_ASIN) +{ + // Nothing to do here. +} + +void NumericASin::eval(const float X, const float Y, const float Z) +{ + result_float = asin(child->result_float); +} + +void NumericASin::eval(const FabInterval& X, + const FabInterval& Y, + const FabInterval& Z) +{ + result_interval = asin(child->result_interval); +} + +//////////////////////////////////////////////////////////////////////////////// + +NumericATan::NumericATan() + : UnaryNode(OP_ATAN) +{ + // Nothing to do here. +} + +void NumericATan::eval(const float X, const float Y, const float Z) +{ + result_float = atan(child->result_float); +} + +void NumericATan::eval(const FabInterval& X, + const FabInterval& Y, + const FabInterval& Z) +{ + result_interval = atan(child->result_interval); +} + +//////////////////////////////////////////////////////////////////////////////// + +NumericSqrt::NumericSqrt() + : UnaryNode(OP_SQRT) +{ + // Nothing to do here. +} + +void NumericSqrt::eval(const float X, const float Y, const float Z) +{ + result_float = sqrt(child->result_float); +} + +void NumericSqrt::eval(const FabInterval& X, + const FabInterval& Y, + const FabInterval& Z) +{ + result_interval = sqrt(child->result_interval); +} + +//////////////////////////////////////////////////////////////////////////////// + +NumericNeg::NumericNeg() + : UnaryNode(OP_NEGATIVE) +{ + // Nothing to do here. +} + +void NumericNeg::eval(const float X, const float Y, const float Z) +{ + result_float = -child->result_float; +} + +void NumericNeg::eval(const FabInterval& X, + const FabInterval& Y, + const FabInterval& Z) +{ + result_interval = -child->result_interval; +} + +//////////////////////////////////////////////////////////////////////////////// + +NumericExp::NumericExp() + : UnaryNode(OP_EXP) +{ + // Nothing to do here. +} + +void NumericExp::eval(const float X, const float Y, const float Z) +{ + result_float = exp(child->result_float); +} + +void NumericExp::eval(const FabInterval& X, + const FabInterval& Y, + const FabInterval& Z) +{ + result_interval = exp(child->result_interval); +} + +//////////////////////////////////////////////////////////////////////////////// + +NumericSgn::NumericSgn() + : UnaryNode(OP_SGN) +{ + // Nothing to do here. +} + +void NumericSgn::eval(const float X, const float Y, const float Z) +{ + if (child->result_float > 0) + result_float = 1; + else if (child->result_float < 0) + result_float = -1; + else + result_float = 0; +} + +void NumericSgn::eval(const FabInterval& X, + const FabInterval& Y, + const FabInterval& Z) +{ + result_interval = sgn(child->result_interval); +} +//////////////////////////////////////////////////////////////////////////////// + +NumericPlus::NumericPlus() + : BinaryNode(OP_PLUS) +{ + // Nothing to do here. +} + +void NumericPlus::eval(const float X, const float Y, const float Z) +{ + result_float = left->result_float + right->result_float; +} + +void NumericPlus::eval(const FabInterval& X, + const FabInterval& Y, + const FabInterval& Z) +{ + result_interval = left->result_interval + right->result_interval; +} + +//////////////////////////////////////////////////////////////////////////////// + +NumericMinus::NumericMinus() + : BinaryNode(OP_MINUS) +{ + // Nothing to do here. +} + +void NumericMinus::eval(const float X, const float Y, const float Z) +{ + result_float = left->result_float - right->result_float; +} + +void NumericMinus::eval(const FabInterval& X, + const FabInterval& Y, + const FabInterval& Z) +{ + result_interval = left->result_interval - right->result_interval; +} + +//////////////////////////////////////////////////////////////////////////////// + +NumericMult::NumericMult() + : BinaryNode(OP_MULT) +{ + // Nothing to do here. +} + +void NumericMult::eval(const float X, const float Y, const float Z) +{ + result_float = left->result_float * right->result_float; +} + +void NumericMult::eval(const FabInterval& X, + const FabInterval& Y, + const FabInterval& Z) +{ + result_interval = left->result_interval * right->result_interval; +} + +//////////////////////////////////////////////////////////////////////////////// + +NumericDiv::NumericDiv() + : BinaryNode(OP_DIV) +{ + // Nothing to do here. +} + +void NumericDiv::eval(const float X, const float Y, const float Z) +{ + result_float = left->result_float / right->result_float; +} + +void NumericDiv::eval(const FabInterval& X, + const FabInterval& Y, + const FabInterval& Z) +{ + result_interval = left->result_interval / right->result_interval; +} + +//////////////////////////////////////////////////////////////////////////////// + +NumericATan2::NumericATan2() + : BinaryNode(OP_ATAN2) +{ + // Nothing to do here. +} + +void NumericATan2::eval(const float X, const float Y, const float Z) +{ + result_float = atan2(left->result_float, right->result_float); +} + +void NumericATan2::eval(const FabInterval& X, + const FabInterval& Y, + const FabInterval& Z) +{ + result_interval = atan2(left->result_interval, right->result_interval); +} + +//////////////////////////////////////////////////////////////////////////////// + +NumericPow::NumericPow() + : BinaryNode(OP_POW) +{ + // Nothing to do here. +} + +void NumericPow::eval(const float X, const float Y, const float Z) +{ + result_float = pow(left->result_float, right->result_float); +} + +void NumericPow::eval(const FabInterval& X, + const FabInterval& Y, + const FabInterval& Z) +{ + result_interval = pow(left->result_interval, right->result_interval.lower()); +} + +//////////////////////////////////////////////////////////////////////////////// + +NumericMin::NumericMin() + : BinaryNode(OP_MIN), left_cached(false), right_cached(false) +{ + // Nothing to do here. +} + +void NumericMin::eval(const float X, const float Y, const float Z) +{ + result_float = min(left->result_float, right->result_float); +} + +void NumericMin::eval(const FabInterval& X, + const FabInterval& Y, + const FabInterval& Z) +{ + result_interval = min(left->result_interval, right->result_interval); + +#if MINMAX_PRUNE + if (right->result_interval < left->result_interval) { + if (!left_cached) { + left_cached = true; + left->sub_ref(); + } + + // In case this node gets deactivated out, save a float result + left->result_float = left->result_interval.lower(); + + } else if (left_cached) { + left_cached = false; + left->add_ref(); + } + + if (left->result_interval < right->result_interval) { + if (!right_cached) { + right->sub_ref(); + right_cached = true; + } + + // In case this node gets deactivated, save a float result + right->result_float = right->result_interval.lower(); + + } else if (right_cached) { + right_cached = false; + right->add_ref(); + } +#endif + +} + +void NumericMin::deactivate() +{ + Node::deactivate(); + if (!left_cached) + left->sub_ref(); + if (!right_cached) + right->sub_ref(); +} + +void NumericMin::activate() +{ + if (!left_cached) + left->add_ref(); + if (!right_cached) + right->add_ref(); + Node::activate(); +} + +//////////////////////////////////////////////////////////////////////////////// + +NumericMax::NumericMax() + : BinaryNode(OP_MAX), left_cached(false), right_cached(false) +{ + // Nothing to do here. +} + +void NumericMax::eval(const float X, const float Y, const float Z) +{ + result_float = max(left->result_float, right->result_float); +} + +void NumericMax::eval(const FabInterval& X, + const FabInterval& Y, + const FabInterval& Z) +{ + result_interval = max(left->result_interval, right->result_interval); + +#if MINMAX_PRUNE + if (right->result_interval > left->result_interval) { + if (!left_cached) { + left->sub_ref(); + left_cached = true; + } + + // In case this node gets deactivated out, save a float result + left->result_float = left->result_interval.lower(); + + } else if (left_cached) { + left->add_ref(); + left_cached = false; + } + + if (left->result_interval > right->result_interval) { + if (!right_cached) { + right->sub_ref(); + right_cached = true; + } + + // In case this node gets deactivated, save a float result + right->result_float = right->result_interval.lower(); + + } else if (right_cached) { + right->add_ref(); + right_cached = false; + } +#endif + +} + + +void NumericMax::activate() +{ + if (!left_cached) + left->add_ref(); + if (!right_cached) + right->add_ref(); + Node::activate(); +} + +void NumericMax::deactivate() +{ + Node::deactivate(); + if (!left_cached) + left->sub_ref(); + if (!right_cached) + right->sub_ref(); +} + +//////////////////////////////////////////////////////////////////////////////// +VarX::VarX() + : NonaryNode(VAR_X) +{ + // Nothing to do here. +} + +void VarX::eval(const float X, const float Y, const float Z) +{ + result_float = X; +} + +void VarX::eval(const FabInterval& X, + const FabInterval& Y, + const FabInterval& Z) +{ + result_interval = X; +} + +//////////////////////////////////////////////////////////////////////////////// + +VarY::VarY() + : NonaryNode(VAR_Y) +{ + // Nothing to do here. +} + +void VarY::eval(const float X, const float Y, const float Z) +{ + result_float = Y; +} + +void VarY::eval(const FabInterval& X, + const FabInterval& Y, + const FabInterval& Z) +{ + result_interval = Y; +} + +//////////////////////////////////////////////////////////////////////////////// + +VarZ::VarZ() + : NonaryNode(VAR_Z) +{ + // Nothing to do here. +} + +void VarZ::eval(const float X, const float Y, const float Z) +{ + result_float = Z; +} + +void VarZ::eval(const FabInterval& X, + const FabInterval& Y, + const FabInterval& Z) +{ + result_interval = Z; +} + +//////////////////////////////////////////////////////////////////////////////// + +NumericConst::NumericConst(float value) + : NonaryNode(NUM_CONST) +{ + marked = true; + + result_float = value; + result_interval = value; +} + +bool NumericConst::operator==(const Node& rhs) +{ + if (operation != rhs.op()) + return false; + return result_float == rhs.result_float; +} + +void NumericConst::print(ostream& o) const +{ + o << result_float; +} + +void NumericConst::dot(ostream& o) const +{ + o << "\"p" << this << "\" [shape = " << dot_shape(operation) + << ", color = " << dot_color(operation) << ", fontsize = 24, label = \"" + << result_float << "\", style=\"dotted\"]\n"; +} \ No newline at end of file diff --git a/src/solver/numeric_nodes.hpp b/src/solver/numeric_nodes.hpp new file mode 100644 index 0000000..701c243 --- /dev/null +++ b/src/solver/numeric_nodes.hpp @@ -0,0 +1,98 @@ +#ifndef NUMERIC_NODES_H +#define NUMERIC_NODES_H + +#include "node.hpp" +#include "node_macro.hpp" + +// This header file defines nodes with numeric inputs and outputs. + +NODE(NumericAbs, UnaryNode); +NODE(NumericCos, UnaryNode); +NODE(NumericSin, UnaryNode); +NODE(NumericACos, UnaryNode); +NODE(NumericASin, UnaryNode); +NODE(NumericATan, UnaryNode); +NODE(NumericSqrt, UnaryNode); +NODE(NumericNeg, UnaryNode); +NODE(NumericExp, UnaryNode); +NODE(NumericSgn, UnaryNode); + +NODE(NumericPlus, BinaryNode); +NODE(NumericMinus, BinaryNode); +NODE(NumericMult, BinaryNode); +NODE(NumericDiv, BinaryNode); +NODE(NumericATan2, BinaryNode); +NODE(NumericPow, BinaryNode); + + +// These two nodes have special forms with internal variables to keep track of +// selective child deactivation. +class NumericMin : public BinaryNode +{ +public: + NumericMin(); + virtual ~NumericMin() {/*Nothing to do here*/}; + void eval(const float X, + const float Y, + const float Z); + void eval(const FabInterval& X, + const FabInterval& Y, + const FabInterval& Z); + void activate(); + void deactivate(); + + +private: + bool left_cached; + bool right_cached; +}; + + +class NumericMax : public BinaryNode +{ +public: + NumericMax(); + virtual ~NumericMax() {/*Nothing to do here*/}; + void eval(const float X, + const float Y, + const float Z); + void eval(const FabInterval& X, + const FabInterval& Y, + const FabInterval& Z); + + void activate(); + void deactivate(); + +private: + bool left_cached; + bool right_cached; +}; + + + +NODE(VarX, NonaryNode); +NODE(VarY, NonaryNode); +NODE(VarZ, NonaryNode); + +// Numerical constants are a special case, because the constructor +// has a unique signature. +class NumericConst : public NonaryNode +{ +public: + NumericConst(float value); + virtual ~NumericConst() { /* Nothing to do here */ }; + virtual bool operator==(const Node& rhs); + + void eval(const float X, + const float Y, + const float Z) { /* Nothing to do here */ }; + void eval(const FabInterval& X, + const FabInterval& Y, + const FabInterval& Z) { /* Nothing to do here */ }; + void eval(const Region& r); + + void print(std::ostream& o) const; + void dot(std::ostream& o) const; +}; + +#endif diff --git a/src/solver/opcodes.cpp b/src/solver/opcodes.cpp new file mode 100644 index 0000000..e07f599 --- /dev/null +++ b/src/solver/opcodes.cpp @@ -0,0 +1,157 @@ +#include +#include + +#include "opcodes.hpp" +#include "parser.hpp" + +using namespace std; + +//////////////////////////////////////////////////////////////////////////////// + +ostream& operator<<(ostream& o, const opcode& op) +{ + switch(op) + { + case OP_AND: + o << " && "; return o; + case OP_OR: + o << " || "; return o; + case OP_NOT: + o << "!"; return o; + case OP_LT: + o << "<"; return o; + case OP_LEQ: + o << "<="; return o; + case OP_GT: + o << ">"; return o; + case OP_GEQ: + o << ">="; return o; + case OP_NEQ: + o << "!="; return o; + case OP_ABS: + o << "abs"; return o; + case OP_COS: + o << "cos"; return o; + case OP_SIN: + o << "sin"; return o; + case OP_ACOS: + o << "acos"; return o; + case OP_ASIN: + o << "asin"; return o; + case OP_ATAN: + o << "atan"; return o; + case OP_SQRT: + o << "sqrt"; return o; + case OP_NEGATIVE: + o << "-"; return o; + case OP_EXP: + o << "exp"; return o; + case OP_SGN: + o << "sgn"; return o; + case OP_PLUS: + o << "+"; return o; + case OP_MINUS: + o << "-"; return o; + case OP_MULT: + o << "*"; return o; + case OP_DIV: + o << "/"; return o; + case OP_ATAN2: + o << "atan2"; return o; + case OP_POW: + o << "pow"; return o; + case OP_MIN: + o << "min"; return o; + case OP_MAX: + o << "max"; return o; + case NUM_CONST: + return o; + case VAR_X: + o << "X"; return o; + case VAR_Y: + o << "Y"; return o; + case VAR_Z: + o << "Z"; return o; + case COLOR_AND: + o << "&"; return o; + case COLOR_OR: + o << "|"; return o; + case COLOR_NOT: + o << "~"; return o; + case BOOL2NUM: + o << "bool2num"; return o; + case NUM2BOOL: + o << "num2bool"; return o; + case BOOL2COLOR: + o << "bool2color"; return o; + case NUM2COLOR: + o << "num2color"; return o; + default: + o << "???"; return o; + } + return o; +} + + +//////////////////////////////////////////////////////////////////////////////// + +string dot_color(opcode op) +{ + if (op == VAR_X || op == VAR_Y || op == VAR_Z) + return "red"; + else if (op == NUM_CONST) + return "orangered"; + + switch (get_output(op)) { + case Parser::IO_NUM: + return "goldenrod"; + case Parser::IO_BOOL: + return get_input(op) == Parser::IO_NUM ? "green" : "dodgerblue"; + case Parser::IO_COLOR: + return "palevioletred2"; + default: + return "black"; + } +} + +string dot_arrow(opcode op) +{ + switch (get_input(op)) { + case Parser::IO_NUM: + return "darkgoldenrod"; + case Parser::IO_BOOL: + return "dodgerblue4"; + case Parser::IO_COLOR: + return "palevioletred4"; + default: + return "grey"; + } +} + +string dot_shape(opcode op) +{ + if (op == NUM_CONST) + return "oval"; + else if (get_token_type(op) == Parser::TOKEN_FUNC) + return "rectangle"; + else + return "square"; +} + +string dot_label(opcode op) +{ + stringstream ss; + + switch (op) { + case OP_NEGATIVE: + case OP_MINUS: + return "−"; + case OP_MULT: + return "×"; + default: + ss << op; + } + + return ss.str(); +} + diff --git a/src/solver/opcodes.hpp b/src/solver/opcodes.hpp new file mode 100644 index 0000000..17b2146 --- /dev/null +++ b/src/solver/opcodes.hpp @@ -0,0 +1,60 @@ +#ifndef OPCODES_H +#define OPCODES_H + +#include +#include + +// opcode defines which operator a node is performing. +enum opcode { + // Logical operations + OP_AND = 0, OP_OR, OP_NEQ, OP_NOT, + + // Transition operations + OP_LT, OP_LEQ, OP_GT, OP_GEQ, + + // Numerical unary operations + OP_ABS, OP_COS, OP_SIN, OP_ACOS, OP_ASIN, OP_ATAN, + OP_SQRT, OP_NEGATIVE, OP_EXP, OP_SGN, + + // Numeric binary operations + OP_PLUS, OP_MINUS, OP_MULT, OP_DIV, OP_ATAN2, OP_POW, + OP_MIN, OP_MAX, + + // Variables and constants + NUM_CONST, VAR_X, VAR_Y, VAR_Z, + + // Color operations + COLOR_AND, COLOR_OR, COLOR_NOT, + + // Translator nodes + BOOL2NUM, NUM2BOOL, BOOL2COLOR, NUM2COLOR, + + NOP_UNKNOWN, + LAST_OP +}; +std::ostream& operator<<(std::ostream& o, const opcode& i); + +// Overarching node types +enum node_type { + NODE_NUM, + NODE_TRANS, + NODE_LOGIC, + NODE_COLOR, + NODE_UNKNOWN +}; + + + +/* std::string dot_color(opcode op) + std::string dot_arrow(opcode op) + std::string dot_shape(opcode op) + std::string dot_label(opcode op) + * + * Returns various format strings for graphviz/dot file output. + */ +std::string dot_color(opcode op); +std::string dot_arrow(opcode op); +std::string dot_shape(opcode op); +std::string dot_label(opcode op); + +#endif \ No newline at end of file diff --git a/src/solver/parser.cpp b/src/solver/parser.cpp new file mode 100644 index 0000000..e2fe441 --- /dev/null +++ b/src/solver/parser.cpp @@ -0,0 +1,1248 @@ +#include +#include +#include + +#include "logic_nodes.hpp" +#include "numeric_nodes.hpp" +#include "translator_nodes.hpp" +#include "color_nodes.hpp" + +#include "parser.hpp" +#include "opcodes.hpp" +#include "switches.hpp" + +using namespace std; + +Parser::parse_token::parse_token() + : n(NULL), num_args(0), precedence(100), + ttype(TOKEN_EMPTY), input(IO_NONE), output(IO_NONE) + { + // Nothing to do here. + } + +Parser::parse_token::parse_token(token_type type) + : n(NULL), num_args(0), precedence(100), + ttype(type), input(IO_NONE), output(IO_NONE) + { + // Nothing to do here. + } + +Parser::parse_token::parse_token(Node* n) + : n(n), + num_args(get_argcount(n->op())), + precedence(get_precedence(n->op())), + ttype(get_token_type(n->op())), + input(get_input(n->op())), + output(get_output(n->op())), + associativity(get_associativity(n->op())) + { + // Nothing to do here. + } + + +ostream& operator<<(ostream& o, const Parser::token_type& t) +{ + switch (t) { + case Parser::TOKEN_NUM: + o << "TOKEN_NUM"; return o; + case Parser::TOKEN_FUNC: + o << "TOKEN_FUNC"; return o; + case Parser::TOKEN_OP: + o << "TOKEN_OP"; return o; + case Parser::TOKEN_LPARENS: + o << "TOKEN_LPARENS"; return o; + case Parser::TOKEN_ARGSEP: + o << "TOKEN_ARGSEP"; return o; + case Parser::TOKEN_RPARENS: + o << "TOKEN_RPARENS"; return o; + case Parser::TOKEN_ERROR: + o << "TOKEN_ERROR"; return o; + case Parser::TOKEN_EMPTY: + default: + o << "TOKEN_EMPTY"; return o; + } +} + +ostream& operator<<(ostream& o, const Parser::io_type& t) +{ + switch (t) { + case Parser::IO_NUM: + o << "IO_NUM"; return o; + case Parser::IO_BOOL: + o << "IO_BOOL"; return o; + case Parser::IO_COLOR: + o << "IO_COLOR"; return o; + case Parser::IO_NONE: + default: + o << "IO_NONE"; return o; + } +} + + +Parser::Parser() + : math_string(NULL), start(NULL), unary_subtraction(true), map_state(0), + newX(NULL), newY(NULL), newZ(NULL) +{ + // Stores plain old X, Y, and Z as the current + // X, Y, and Z coordinates. + currentX.push_front(new VarX()); + cache_node(currentX.front()); + + currentY.push_front(new VarY()); + cache_node(currentY.front()); + + currentZ.push_front(new VarZ()); + cache_node(currentZ.front()); +} + +void Parser::wrap_argument(parse_token& arg, io_type desired) +{ + if (!arg.n || arg.output == desired) + return; + + // Transform numerical nodes into logical nodes. + if (desired == IO_BOOL && arg.output == IO_NUM) { + arg.n = new NumToBool(arg.n); + arg.output = IO_BOOL; + + // Transform booleans into numbers + } else if (desired == IO_NUM && arg.output == IO_BOOL) { + arg.n = new BoolToNum(arg.n); + arg.output = IO_NUM; + + // Transform numbers into colors + } else if (desired == IO_COLOR && arg.output == IO_NUM) { + arg.n = new NumToColor(arg.n); + arg.output = IO_COLOR; + + // Transform booleans into colors + } else if (desired == IO_COLOR && arg.output == IO_BOOL) { + arg.n = new BoolToColor(arg.n); + arg.output = IO_COLOR; + + // If we can't wrap the operator, then return false. + } else { + cerr << "Error: node " << *(arg.n) << " has output of type " + << arg.output << ", which cannot be converted to " + << desired << endl; + exit(1); + } + + cache_node(arg.n); +} + + +void print_list(list L, ostream& o) +{ + list::iterator it; + int limit = 10; + for (it = L.begin(); it != L.end(); ++it) { + if (it->ttype == Parser::TOKEN_LPARENS) + o << "\t(\n"; + else if (it->ttype == Parser::TOKEN_RPARENS) + o << "\t)\n"; + else if (it->ttype == Parser::TOKEN_ARGSEP) + o << "\t,\n"; + else if (it->n) + o << "\t" << *(it->n) << "\n"; + if (--limit == 0) + return; + } +} + + +void print_parse(list output, + list operators, + ostream& o) +{ + for(int i = 0; i < 80; ++i) + o << '-'; + o << endl; + o << "Output stack:\n"; + print_list(output, o); + o << "Operators stack:\n"; + print_list(operators, o); + o << endl; +} + +void print_parse(list output, + list operators) +{ + print_parse(output, operators, cout); +} + + +// Stores a node in the cache, uniquifying if COMBINE_NODES is enabled. +void Parser::cache_node(Node*& node) +{ + // If this is a dummy node of some kind (e.g. parenthesis, + // argument separator), then don't do anything. + if (!node) + return; + + opcode op = node->op(); + unsigned weight = node->get_weight(); + + // If we don't have a cache line for this weight, then expand the cache, + // filling new lines with a vector of node lists. + if (weight+1 > node_cache.size()) + node_cache.resize(weight+1, vector >(LAST_OP)); + +#if COMBINE_NODES + list::iterator it; + Node* match = NULL; + + // Look for matches + for (it = node_cache[weight][op].begin(); + it != node_cache[weight][op].end(); + ++it) { + if (**it == *(node)) { + match = *it; + break; + } + } + + // If we found a match, delete the old node and replace it with + // the matching node. + if (match) { + if (node != match) { + delete node; + node = match; + } + } else { + node_cache[weight][op].push_front(node); + } +#else + node_cache[weight][op].push_front(node); +#endif +} + + +// Go through the node cache and delete nodes with no references +void Parser::remove_ignored() +{ + list::iterator it; + bool keep_going; + + do { + keep_going = false; + + // Iterate through each list in the node cache + for (unsigned weight = 0; weight < node_cache.size(); ++weight) { + for (int op = 0; op < LAST_OP; ++op) { + it = node_cache[weight][op].begin(); + + // Iterate through each node in this cache + while (it != node_cache[weight][op].end()) { + + // If the node has no one watching it, then + // deactivate and delete it + if ((**it).ignored()) { + (**it).deactivate(); + delete *it; + it = node_cache[weight][op].erase(it); + keep_going = true; + } else { + ++it; + } + } + } // end of loop through operators + } // end of loop through weights + + } while (keep_going); +} + + +// Copies the entire cache to a MathTree +MathTree* Parser::cache_to_tree(Node* root) +{ + MathTree* tree = new MathTree(); + list::iterator it; + for (unsigned weight = 0; weight < node_cache.size(); ++weight) + for (int op = 0; op < LAST_OP; ++op) + for (it = node_cache[weight][op].begin(); + it != node_cache[weight][op].end(); ++it) + tree->add(*it); + tree->pack(); + tree->set_root(root); + cout << "Done." << endl; + return tree; +} + + +void Parser::convert_colors(parse_token& oper, + parse_token& lh_arg, + parse_token& rh_arg) +{ + // If we find a boolean multiplied by a numerical value, then + // we treat it as a color creation. + if (oper.n->op() == OP_MULT && + ((rh_arg.output == IO_NUM && lh_arg.output == IO_BOOL) || + (lh_arg.output == IO_NUM && rh_arg.output == IO_BOOL)) ) + { + delete oper.n; + oper.n = new ColorAnd(); + oper.input = IO_COLOR; + oper.output = IO_COLOR; + } + // Logical OR on colors -> bitwise or + if (oper.n->op() == OP_OR && (rh_arg.output == IO_COLOR || + lh_arg.output == IO_COLOR)) + { + delete oper.n; + oper.n = new ColorOr(); + oper.input = IO_COLOR; + oper.output = IO_COLOR; + } + // Logical AND on colors -> bitwise and + if (oper.n->op() == OP_AND && (rh_arg.output == IO_COLOR || + lh_arg.output == IO_COLOR)) + { + delete oper.n; + oper.n = new ColorAnd(); + oper.input = IO_COLOR; + oper.output = IO_COLOR; + } + // Logical not on colors -> bitwise not + if (oper.n->op() == OP_NOT && rh_arg.output == IO_COLOR) + { + delete oper.n; + oper.n = new ColorNot(); + oper.input = IO_COLOR; + oper.output = IO_COLOR; + } +} + + +bool Parser::simplify(parse_token& oper, + parse_token& lh_arg, + parse_token& rh_arg) +{ + // We're only going to simplify numeric nodes to keep life easy. + if (oper.input != IO_NUM) + return false; + + opcode op = oper.n->op(); + + if (op == OP_PLUS) { + // 0 + X => X + if (lh_arg.n->marked && lh_arg.n->result_float == 0) { + delete oper.n; + oper.n = rh_arg.n; + return true; + } + // X + 0 => X + if (rh_arg.n->marked && rh_arg.n->result_float == 0) { + delete oper.n; + oper.n = lh_arg.n; + return true; + } + } else if (op == OP_MINUS) { + // X - 0 => X + if (rh_arg.n->marked && rh_arg.n->result_float == 0) { + delete oper.n; + oper.n = lh_arg.n; + return true; + } + } else if (op == OP_MULT) { + // X * 0 => 0 + if (rh_arg.n->marked && rh_arg.n->result_float == 0) { + delete oper.n; + oper.n = rh_arg.n; + return true; + } + // 0 * X => 0 + if (lh_arg.n->marked && lh_arg.n->result_float == 0) { + delete oper.n; + oper.n = lh_arg.n; + return true; + } + // X * 1 => X + if (rh_arg.n->marked && rh_arg.n->result_float == 1) { + delete oper.n; + oper.n = lh_arg.n; + return true; + } + // 1 * X => X + if (lh_arg.n->marked && lh_arg.n->result_float == 1) { + delete oper.n; + oper.n = rh_arg.n; + return true; + } + } + + return false; +} + + +// operator_to_output +// +// Find out how many arguments the operation wants to take from +// the output stack +// Take them off the stack and make them the operator's children +// Put the new operation on the output stack. +void Parser::operator_to_output(parse_token& oper) +{ + if (oper.ttype == TOKEN_LPARENS || oper.ttype == TOKEN_MAP_START) + return; + + // If this node's children have already been populated, then + // we can skip the song and dance about pulling them from the + // stack. + if (oper.n && oper.n->null_children() == false) { + cache_node(oper.n); + output.push_front(oper); + return; + } + + parse_token rh_arg; + parse_token lh_arg; + + // Handle the first argument + if (oper.num_args >= 1) { + if (output.empty()) { + cerr << "Parse failed:\n\t'" << *(oper.n) + << "' failed to acquire its first operand." << endl; + exit(1); + } + rh_arg = output.front(); + output.pop_front(); + } + + // Handle the second argument + if (oper.num_args >= 2) { + if (output.empty()) { + cerr << "Parse failed:\n\t'" << *(oper.n) + << "' failed to acquire its second operand." << endl; + exit(1); + } + lh_arg = output.front(); + output.pop_front(); + } + + // Set of special cases to handle colors correctly. + convert_colors(oper, lh_arg, rh_arg); + + // If the argument outputs are of a different type then + // the operator's inputs, wrap them in a lightweight + // wrapper classes to do the conversion. + wrap_argument(lh_arg, oper.input); + wrap_argument(rh_arg, oper.input); + + // Simplify common arithmetic expressions (e.g. X + 0 => X) + bool simplified = false; +#if SIMPLIFY_TREE + simplified = simplify(oper, lh_arg, rh_arg); +#endif + + // If this node has been simplified, then we don't need to worry + // about assigning children or caching (since the node is already + // cached). + if (!simplified) { + if (oper.num_args == 1) + static_cast(oper.n)->set_child(rh_arg.n); + else if (oper.num_args == 2) + static_cast(oper.n)->set_children(lh_arg.n, + rh_arg.n); + cache_node(oper.n); + } + + output.push_front(oper); +} + + +// operator_to_stack +// +// Add an operator to the operator stack, while checking if precedence +// requires removing other operators from the top of the stack. +void Parser::operator_to_stack(parse_token& o1) +{ + while (!operators.empty()) + { + parse_token o2 = operators.front(); + if ((o1.associativity == 'l' && o1.precedence >= o2.precedence) || + (o1.associativity == 'r' && o1.precedence > o2.precedence)) + { + operators.pop_front(); + operator_to_output(o2); + } else + break; + } + operators.push_front(o1); +} + + +bool str_match(const char* s1, const char* s2) +{ + do + if (!*s1 || *s1++ != *s2++) + return false; + while (*s2); + + return true; +} + +Parser::parse_token Parser::next_token() +{ + bool next_unary_subtraction = true; + + parse_token result; + + if (*start == '(') { + start += 1; + result = TOKEN_LPARENS; + } + else if (str_match(start, "{")) { + start += 1; + result = TOKEN_MAP_START; + } + else if (str_match(start, "X:")) { + start +=2; + result = TOKEN_MAP_DEF; + result.n = currentX.front(); + } + else if (str_match(start, "Y:")) { + start +=2; + result = TOKEN_MAP_DEF; + result.n = currentY.front(); + } + else if (str_match(start, "Z:")) { + start +=2; + result = TOKEN_MAP_DEF; + result.n = currentZ.front(); + } + else if (str_match(start, ";")) { + start += 1; + result = TOKEN_MAP_SEPARATOR; + } + else if (str_match(start, "}")) { + start += 1; + result = TOKEN_MAP_END; + } + else if (*start == ')') { + start += 1; + next_unary_subtraction = false; + result = TOKEN_RPARENS; + } else if (*start == 'X' || *start == 'x') { + start += 1; + next_unary_subtraction = false; + result = currentX.front(); + result.ttype = TOKEN_NUM; + } else if (*start == 'Y' || *start == 'y') { + start += 1; + next_unary_subtraction = false; + result = currentY.front(); + result.ttype = TOKEN_NUM; + } else if (*start == 'Z' || *start == 'z') { + start += 1; + next_unary_subtraction = false; + result = currentZ.front(); + result.ttype = TOKEN_NUM; + } + else if (*start == '-') { + start += 1; + if (unary_subtraction) { + result = new NumericNeg(); + } else { + result = new NumericMinus(); + } + } + else if (*start == ',') { + start += 1; + result = TOKEN_ARGSEP; + } + else if (*start == '*') { + start += 1; + result = new NumericMult(); + } + else if (*start == '+') { + start += 1; + result = new NumericPlus(); + } + else if (*start == '/') { + start += 1; + result = new NumericDiv(); + } + else if (str_match(start, "atan2")) { + start += 5; + result = new NumericATan2(); + } + else if (str_match(start, "pow")) { + start += 3; + result = new NumericPow(); + } + else if (str_match(start, "min")) { + start += 3; + result = new NumericMin(); + } + else if (str_match(start, "max")) { + start += 3; + result = new NumericMax(); + } + else if (str_match(start, "exp")) { + start += 3; + result = new NumericExp(); + } + else if (str_match(start, "sgn")) { + start += 3; + result = new NumericSgn(); + } + else if (str_match(start, "abs")) { + start += 3; + result = new NumericAbs(); + } + else if (str_match(start, "cos")) { + start += 3; + result = new NumericCos(); + } + else if (str_match(start, "sin")) { + start += 3; + result = new NumericSin(); + } + else if (str_match(start, "acos")) { + start += 4; + result = new NumericACos(); + } + else if (str_match(start, "asin")) { + start += 4; + result = new NumericASin(); + } + else if (str_match(start, "atan")) { + start += 4; + result = new NumericATan(); + } + else if (str_match(start, "sqrt")) { + start += 4; + result = new NumericSqrt(); + } + else if (str_match(start, "<=")) { + start += 2; + result = new TransitionLeq(); + } + else if (str_match(start, "<")) { + start += 1; + result = new TransitionLt(); + } + else if (str_match(start, ">=")) { + start += 2; + result = new TransitionGeq(); + } + else if (str_match(start, ">")) { + start += 1; + result = new TransitionGt(); + } + else if (str_match(start, "!=")) { + start += 2; + result = new LogicNeq(); + } + else if (*start == '!' || *start == '~') { + start += 1; + result = new LogicNot(); + } + else if (str_match(start, "&&")) + { + start += 2; + result = new LogicAnd(); + } + else if (*start == '&') + { + start += 1; + result = new LogicAnd(); + } + else if (str_match(start, "||")) + { + start += 2; + result = new LogicOr(); + } + else if (*start == '|') + { + start += 1; + result = new LogicOr(); + } + else if (str_match(start, "pi")) + { + start += 2; + result = new NumericConst(3.14159265358979323846); + } + else if ((*start >= '0' && *start <= '9') || *start == '.') + { + + // Accumulated value + float v = 0; + + // Divided value once we're after the decimal place + double divider = 0; + do { + if (*start == '.') + divider = 10; + else if (divider) { + v += float(*start - '0') / divider; + divider *= 10; + } else { + v = v * 10 + (*start - '0'); + } + + start++; + } while ((*start >= '0' && *start <= '9') || *start == '.'); + + // Check for scientific notation. + if (*start == 'e') { + int e = 0; + bool neg_exp = false; + start++; + if (*start == '-') { + neg_exp = true; + start++; + } else if (*start == '+') { + neg_exp = false; + start++; + } + do { + e = e * 10 + (*start++ - '0'); + } while (*start >= '0' && *start <= '9'); + // Negate the exponent if needed. + e = e * (neg_exp ? -1 : 1); + v = v * pow(10.0, e); + } + + next_unary_subtraction = false; + result = new NumericConst(v); + } + else if (*start != ' ') + { + cerr << "Warning: Unknown token at '"; + if (string(start).size() > 10) + cerr << string(start, start + 10) << "...'" << endl; + else + cerr << string(start) << "'" << endl; + start++; + return parse_token(); + } + + // Zoom along until we're staring at something useful. + while (*start == ' ') + start++; + + unary_subtraction = next_unary_subtraction; + + return result; +} + + +MathTree* Parser::parse(string input, solver_mode& mode) +{ + parse_token current; + + cout << "Parsing... "; + cout.flush(); + + // Convert into a c-style string + math_string = input.c_str(); + start = math_string; + + unary_subtraction = true; + + while (*start) + { + current = next_token(); + + // If there was a parse failure, then return. + if (current.ttype == TOKEN_ERROR) + return NULL; + + // If this token was empty, then continue. + if (current.ttype == TOKEN_EMPTY) + continue; + + if (current.ttype == TOKEN_NUM) + operator_to_output(current); + + else if (current.ttype == TOKEN_FUNC) + operators.push_front(current); + + else if (current.ttype == TOKEN_MAP_START) { + + if (map_state) { + cerr << "Parse error: badly nested map statement." + << endl; + exit(1); + } + + // If they are using curly braces without any map commands, then + // push trivial maps to the stacks. + if (*start && *(start+1) == ':') { + map_state = '?'; + } else { + currentX.push_front(currentX.front()); + currentY.push_front(currentY.front()); + currentZ.push_front(currentZ.front()); + } + + operators.push_front(current); + } + + else if (current.ttype == TOKEN_MAP_DEF) { + // When we get a colon, it better be within in the context of + // a map operation (and it should follow X,Y, or Z) + + if (current.n == currentX.front()) { + if (newX || map_state != '?') { + cerr << "Parse error: Repeated X mapping." << endl; + exit(1); + } + map_state = 'X'; + } else if (current.n == currentY.front()) { + if (newY || map_state != '?') { + cerr << "Parse error: Repeated Y mapping." << endl; + exit(1); + } + map_state = 'Y'; + } else if (current.n == currentZ.front()) { + if (newZ || map_state != '?') { + cerr << "Parse error: Repeated Z mapping." << endl; + exit(1); + } + map_state = 'Z'; + } + } + + + else if (current.ttype == TOKEN_ARGSEP) + { + if (operators.empty()) { + cerr << "Parse error: Misplaced argument separator." + << endl; + exit(1); + } + while (operators.front().ttype != TOKEN_LPARENS) + { + operator_to_output(operators.front()); + operators.pop_front(); + if (operators.empty()) { + cerr << "Parse error: Misplaced argument separator." + << endl; + exit(1); + } + } + } + + + else if (current.ttype == TOKEN_MAP_SEPARATOR) { + + if (!map_state) { + cerr << "Parse error: Misplaced map argument separator." + << endl; + exit(1); + } + + if (operators.empty()) { + cerr << "Parse error: Misplaced map argument separator." + << endl; + exit(1); + } + while (operators.front().ttype != TOKEN_MAP_START) + { + operator_to_output(operators.front()); + operators.pop_front(); + if (operators.empty()) { + cerr << "Parse error: Misplaced map argument separator." + << endl; + exit(1); + } + } + + // If we're in the middle of a map operation, save the + // new mapped value for X, Y, or Z. + if (map_state == 'X') { + newX = output.front().n; + output.pop_front(); + } else if (map_state == 'Y') { + newY = output.front().n; + output.pop_front(); + } else if (map_state == 'Z') { + newZ = output.front().n; + output.pop_front(); + } + + // Check to see if another map operation is coming down the line. + if (*start && *(start+1) == ':') { + map_state = '?'; + } + + // If not, then apply the saved maps for X, Y, and Z. + else { + if (newX) + currentX.push_front(newX); + else + currentX.push_front(currentX.front()); + + if (newY) + currentY.push_front(newY); + else + currentY.push_front(currentY.front()); + + if (newZ) + currentZ.push_front(newZ); + else + currentZ.push_front(currentZ.front()); + + newX = NULL; + newY = NULL; + newZ = NULL; + + map_state = 0; + } + } + + + + else if (current.ttype == TOKEN_OP) + operator_to_stack(current); + + else if (current.ttype == TOKEN_LPARENS) + operators.push_front(current); + + else if (current.ttype == TOKEN_RPARENS) + { + if (operators.empty()) { + cerr << "Parse error: mismatched parentheses." << endl; + exit(1); + } + while (operators.front().ttype != TOKEN_LPARENS) + { + operator_to_output(operators.front()); + operators.pop_front(); + if (operators.empty()) { + cerr << "Parse error: mismatched parentheses." << endl; + exit(1); + } + } + operators.pop_front(); + + // If there's a function on top of the stack, then it belongs + // with the set of parentheses. + if (!operators.empty() && operators.front().ttype == TOKEN_FUNC) { + operator_to_output(operators.front()); + operators.pop_front(); + } + } + + else if (current.ttype == TOKEN_MAP_END) + { + if (operators.empty()) { + cerr << "Parse error: mismatched curly braces." << endl; + exit(1); + } + while (operators.front().ttype != TOKEN_MAP_START) + { + operator_to_output(operators.front()); + operators.pop_front(); + if (operators.empty()) { + cerr << "Parse error: mismatched curly braces." << endl; + exit(1); + } + } + operators.pop_front(); + + currentX.pop_front(); + currentY.pop_front(); + currentZ.pop_front(); + } + } + + // Finish the parse by popping operators off the stack. + while (!operators.empty()) + { + operator_to_output(operators.front()); + operators.pop_front(); + } + + if (!output.size()) + { + cerr << "Parse failed:\n\tEmpty math tree." << endl; + return NULL; + } else if (output.size() > 1) { + cerr << "Parse failed:\n\tInvalid math string." << endl; + return NULL; + } + + // Make sure that the root's output type is correct; otherwise + // return NULL. + if (mode == SOLVE_BOOL) + wrap_argument(output.front(), IO_BOOL); + else if (mode == SOLVE_RGB) + wrap_argument(output.front(), IO_COLOR); + else if (mode == SOLVE_REAL) { + wrap_argument(output.front(), IO_NUM); + } + + // Add a watcher to the top node of the tree (so that it doesn't get + // auto-deleted) + Node* root = output.front().n; + root->add_ref(); + + // Delete any ignored nodes (that were optimized out of the tree) + remove_ignored(); + + // Convert the cache into the tree. + return cache_to_tree(root); +} + +//////////////////////////////////////////////////////////////////////////////// +// Functions below this point are used to extract parsing information from node +// opcodes. +//////////////////////////////////////////////////////////////////////////////// + +int get_precedence(opcode op) +{ + switch (op) + { + case OP_NOT: + case OP_NEGATIVE: + case COLOR_NOT: + return 1; + case OP_MULT: + case OP_DIV: + return 2; + case OP_PLUS: + case OP_MINUS: + return 3; + case OP_LT: + case OP_GT: + case OP_LEQ: + case OP_GEQ: + case OP_NEQ: + return 4; + case OP_AND: + case COLOR_AND: + return 5; + case OP_OR: + case COLOR_OR: + return 6; + default: + return 100; + } +} + +//////////////////////////////////////////////////////////////////////////////// + +// get_node_type +// +// From an opcode, infer the type of the node. +// (e.g. cosine implies a numerical node) +node_type get_node_type(opcode op) +{ + switch (op) + { + case OP_ABS: + case OP_COS: + case OP_SIN: + case OP_ACOS: + case OP_ASIN: + case OP_ATAN: + case OP_SQRT: + case OP_NEGATIVE: + case OP_EXP: + case OP_SGN: + case OP_PLUS: + case OP_MINUS: + case OP_MULT: + case OP_DIV: + case OP_ATAN2: + case OP_POW: + case OP_MIN: + case OP_MAX: + case VAR_X: + case VAR_Y: + case VAR_Z: + case NUM_CONST: + return NODE_NUM; + case OP_AND: + case OP_OR: + case OP_NOT: + case OP_NEQ: + return NODE_LOGIC; + case OP_LT: + case OP_LEQ: + case OP_GT: + case OP_GEQ: + return NODE_TRANS; + case COLOR_AND: + case COLOR_OR: + case COLOR_NOT: + return NODE_COLOR; + default: + cerr << "Error: unknown operator " << op << " in get_node_type." << endl; + exit(1); + } +} + +//////////////////////////////////////////////////////////////////////////////// + +// get_token_type +// +// From an opcode, infer the type of the token. +// (e.g. cosine implies a function node) +Parser::token_type get_token_type(opcode op) +{ + switch (op) + { + case OP_ABS: + case OP_COS: + case OP_SIN: + case OP_ACOS: + case OP_ASIN: + case OP_ATAN: + case OP_SQRT: + case OP_ATAN2: + case OP_POW: + case OP_MIN: + case OP_MAX: + case OP_EXP: + case OP_SGN: + case NUM2BOOL: + case BOOL2NUM: + case NUM2COLOR: + case BOOL2COLOR: + return Parser::TOKEN_FUNC; + case OP_NEGATIVE: + case OP_PLUS: + case OP_MINUS: + case OP_MULT: + case OP_DIV: + case OP_AND: + case OP_OR: + case OP_NOT: + case OP_LT: + case OP_LEQ: + case OP_GT: + case OP_GEQ: + case OP_NEQ: + case COLOR_OR: + case COLOR_AND: + case COLOR_NOT: + return Parser::TOKEN_OP; + case VAR_X: + case VAR_Y: + case VAR_Z: + case NUM_CONST: + return Parser::TOKEN_NUM; + default: + cerr << "Error: unknown operator " << op << " in get_token_type." << endl; + exit(1); + } +} + +//////////////////////////////////////////////////////////////////////////////// + +Parser::io_type get_output(opcode op) +{ + // Special case for translator nodes + switch (op) { + case BOOL2NUM: + return Parser::IO_NUM; + case NUM2BOOL: + return Parser::IO_BOOL; + case BOOL2COLOR: + case NUM2COLOR: + return Parser::IO_COLOR; + default: + ; // Continue to the rest of the function + } + + node_type ntype = get_node_type(op); + switch (ntype) + { + case NODE_NUM: + return Parser::IO_NUM; + case NODE_TRANS: + case NODE_LOGIC: + return Parser::IO_BOOL; + case NODE_COLOR: + return Parser::IO_COLOR; + default: + return Parser::IO_NONE; + } +} + +//////////////////////////////////////////////////////////////////////////////// + +Parser::io_type get_input(opcode op) +{ + // Special case for translator nodes + switch (op) { + case BOOL2NUM: + case BOOL2COLOR: + return Parser::IO_BOOL; + case NUM2BOOL: + case NUM2COLOR: + return Parser::IO_NUM; + default: + ; // Continue to the rest of the function + } + + node_type ntype = get_node_type(op); + switch (ntype) + { + case NODE_NUM: + case NODE_TRANS: + return Parser::IO_NUM; + case NODE_LOGIC: + return Parser::IO_BOOL; + case NODE_COLOR: + return Parser::IO_COLOR; + default: + return Parser::IO_NONE; + } +} + +//////////////////////////////////////////////////////////////////////////////// + +// get_argcount +// +// From an opcode, infer the number of arguments that a node will want +// to take (e.g. cosine wants one, atan2 wants 2) +int get_argcount(opcode op) +{ + switch (op) + { + case NUM_CONST: + case VAR_X: + case VAR_Y: + case VAR_Z: + return 0; + case OP_NEGATIVE: + case OP_EXP: + case OP_SGN: + case OP_ABS: + case OP_COS: + case OP_SIN: + case OP_ACOS: + case OP_ASIN: + case OP_ATAN: + case OP_SQRT: + case OP_NOT: + case COLOR_NOT: + return 1; + default: + return 2; + } +} + +//////////////////////////////////////////////////////////////////////////////// + +// get_associativity +// +// From an opcode, return the associativity of the operation. +char get_associativity(opcode op) +{ + switch (op) + { + case OP_NEGATIVE: + case OP_NOT: + case COLOR_NOT: + return 'r'; + default: + return 'l'; + } +} \ No newline at end of file diff --git a/src/solver/parser.hpp b/src/solver/parser.hpp new file mode 100644 index 0000000..60a39c1 --- /dev/null +++ b/src/solver/parser.hpp @@ -0,0 +1,272 @@ +#ifndef PARSE_H +#define PARSE_H + +#include +#include + +#include "opcodes.hpp" +#include "math_tree.hpp" +#include "fabvars.hpp" + +// This class parses a .math file, converting it into a MathTree. +class Parser +{ +public: + Parser(); + + // Tokens are divided into a set of types + enum token_type { + TOKEN_NUM, + TOKEN_FUNC, + TOKEN_OP, + TOKEN_LPARENS, + TOKEN_ARGSEP, + TOKEN_RPARENS, + + TOKEN_MAP_START, + TOKEN_MAP_DEF, TOKEN_MAP_SEPARATOR, + TOKEN_MAP_END, + + TOKEN_EMPTY, + TOKEN_ERROR + }; + + // Input and output types for parse tokens. + enum io_type { + IO_NUM, + IO_BOOL, + IO_COLOR, + IO_NONE + }; + + // Small structure to hold a single parse token + typedef struct parse_token + { + /* parse_token() + * + * Token constructor. Creates an empty token. + */ + parse_token(); + + + /* parse_token(token_type type) + * + * Token constructor. Creates an null token of + * the given type. + */ + parse_token(token_type type); + + /* parse_token(Node* n) + * + * Token constructor. Creates an token based on + * a Node object, using helper functions to look up + * the node's parameters (e.g. number of arguments) + */ + parse_token(Node* n); + + // Every token points to a Node + Node* n; + + // How many arguments will this token want to pull + // off the stack? + int num_args; + + // This token's precedence (lower is more important) + int precedence; + + // The type of this token (function, number, operation, etc) + token_type ttype; + + // Token's input and output data types + io_type input; + io_type output; + + // 'l' or 'r' for left or right associative + char associativity; + + } parse_token; + + + /* MathTree* parse(std::string input, solver_mode& mode) + * + * Takes in a string of math and solver mode, and returns an + * appropriate MathTree. + */ + MathTree* parse(std::string input, solver_mode& mode); + +private: + /* parse_token next_token() + * + * Extracts and returns the next token from the input string. + */ + parse_token next_token(); + + + /* void operator_to_stack(parse_token& o1) + * + * Puts an operator node on the top of the operator stack. + * This may require popping other nodes off the operator stack + * depending on their precedence. + */ + void operator_to_stack(parse_token& o1); + + + /* void operator_to_output(parse_token& oper) + * + * Puts an operator node on the top of the output stack. + * This may involve pulling arguments from the output stack + * and assigning them as the operator's children. + */ + void operator_to_output(parse_token& oper); + + + /* void cache_node(Node*& node) + * + * Stores a node in the cache. If COMBINE_NODES is enabled, then + * this function uniquifies nodes in the cache. + */ + void cache_node(Node*& node); + + + /* void remove_ignored() + * + * Traverses the node cache, deleting any items with no references. + * This is needed if the SIMPLIFY_TREE is turned on, because nodes + * are optimized out of the tree after they are cached. + */ + void remove_ignored(); + + + /* MathTree* cache_to_tree() + * + * Copies the set of cached nodes into a MathTree and + * returns the tree. + */ + MathTree* cache_to_tree(Node* root); + + + /* bool simplify(parse_token& oper, + parse_token& lh_arg, + parse_token& rh_arg) + * + * Simplifies an expression by replacing an operation with one of its + * operands if appropriate (e.g. X + 0 -> X). + * + * Returns true if the operation was simplified, false otherwise. + */ + bool simplify(parse_token& oper, + parse_token& lh_arg, + parse_token& rh_arg); + + + /* void convert_colors(parse_token& oper, + parse_token& lh_arg, + parse_token& rh_arg); + * + * Replaces logical operations with bitwise operations where + * appropriate. This occurs in a few situations: + * boolean * number -> boolean & number, which will be + * wrapped into color(boolean) & color(number) by + * wrap_argument + * color && color -> color & color + * color || color -> color | color + * !color -> ~color + */ + void convert_colors(parse_token& oper, + parse_token& lh_arg, + parse_token& rh_arg); + + + /* void wrap_argument(parse_token& arg, io_type desired) + * + * Coerces the output type of the provided parse token into the + * desired output types, using translator nodes if needed. + */ + void wrap_argument(parse_token& arg, io_type desired); + + // The math string in question + const char* math_string; + + // Our current position in the math string (used in tokenizer) + const char* start; + + // State variable to keep track of whether the next subtraction is + // unary (e.g. 4 * -1 vs. 4 - 1) + bool unary_subtraction; + + // Stack of output tokens + std::list output; + + // Stack of operator tokens + std::list operators; + + // Cached nodes + std::vector > > node_cache; + + // Stacks for X, Y, and Z (to add support from "map" operation) + std::list currentX; + std::list currentY; + std::list currentZ; + + // Store where we are in a map operation. + char map_state; + + // New nodes (for partway through a map operation) + Node* newX; + Node* newY; + Node* newZ; +}; + +// Print operator for token type enum +std::ostream& operator<<(std::ostream& o, const Parser::token_type& t); + +// Print operator for io type enum +std::ostream& operator<<(std::ostream& o, const Parser::io_type& t); + + +/* int get_precedence(opcode op) + * + * Returns the precedence of an operation. Lower values are equivalent + * to a higher precedence. + */ +int get_precedence(opcode op); + + +/* Parser::token_type get_token_type(opcode op) + * + * From an opcode, return a token type for the parser's use. + */ +Parser::token_type get_token_type(opcode op); + + +/* node_type get_node_type(opcode op) + * + * From an opcode, return a node type. + */ +node_type get_node_type(opcode op); + + +/* Parser::io_type get_input(opcode op) + Parser::io_type get_output(opcode op) + * + * Find a node's preferred input and output formats. + */ +Parser::io_type get_input(opcode op); +Parser::io_type get_output(opcode op); + + +/* int get_argcount(opcode op) + * + * Returns the number of arguments required by a node. + */ +int get_argcount(opcode op); + + +/* char get_associativity(opcode op) + * + * Returns 'l' or 'r' for left or right associative operators. + */ +char get_associativity(opcode op); + + +#endif \ No newline at end of file diff --git a/src/solver/progress_bar.cpp b/src/solver/progress_bar.cpp new file mode 100644 index 0000000..786f45a --- /dev/null +++ b/src/solver/progress_bar.cpp @@ -0,0 +1,36 @@ +#include +#include "progress_bar.hpp" + +using namespace std; + +ProgressBar::ProgressBar() + : full(0), progress(0), next_tick(1), bar_length(40) +{ + // Nothing to do here +} + +bool ProgressBar::update(uint64_t delta) +{ + if (!lock.try_lock()) + return false; + progress += delta; + lock.unlock(); + + if (!cout_lock.try_lock()) + return true; + + while (next_tick <= (progress*bar_length) / full) { + cout << "\r ["; + for(unsigned i = 0; i < next_tick; ++i) + cout << '|'; + for(int i = next_tick; i < bar_length; ++i) + cout << ' '; + cout << ']'; + + cout << flush; + + next_tick += 1; + } + cout_lock.unlock(); + return true; +} \ No newline at end of file diff --git a/src/solver/progress_bar.hpp b/src/solver/progress_bar.hpp new file mode 100644 index 0000000..128493b --- /dev/null +++ b/src/solver/progress_bar.hpp @@ -0,0 +1,44 @@ +#ifndef PROGRESS_H +#define PROGRESS_H + +#include +#include + +// This class draws a progress bar on the screen at regular intervals +class ProgressBar +{ +public: + /* ProgressBar() + * + * Simple constructor. + */ + ProgressBar(); + + /* bool update(unsigned long delta) + * + * Informs the progress bar that another delta units have been + * evaluated. The progress bar may or may not redraw at this point. + * + * Returns true if the update was accepted, false otherwise. + */ + bool update(uint64_t delta); + + + // The number of units in the full render + uint64_t full; + + // The number of units that have been solved + uint64_t progress; + + // The next significant redraw point + unsigned next_tick; + + // Total length of the ASCII bar + int bar_length; + + // Mutex to synchronize disparate threads + boost::mutex lock; + boost::mutex cout_lock; +}; + +#endif \ No newline at end of file diff --git a/src/solver/raycaster.cpp b/src/solver/raycaster.cpp new file mode 100644 index 0000000..41f4f79 --- /dev/null +++ b/src/solver/raycaster.cpp @@ -0,0 +1,99 @@ +#include "raycaster.hpp" +#include "math_tree.hpp" +#include "node.hpp" +#include "task_buffer.hpp" + +#include "switches.hpp" + +using namespace std; +using boost::logic::tribool; +using boost::thread; + +Raycaster::Raycaster(FabVars& v) + : Solver(v) +{ + // Nothing to do here. +} + +Raycaster::Raycaster(MathTree* tree, FabVars& v) + : Solver(tree, v) +{ + // Nothing to do here. +} + +/////////////////////////////////////////////////////////////////////////////// + +// Evaluate a single region, either with point-by-point evaluation or +// interval math + recursion. Operates in a single thread and spawns +// no children. +void Raycaster::evaluate_region(Region r) +{ + + FabInterval X = v.x(r.imin, r.imax-1); + FabInterval Y = v.y(r.jmin, r.jmax-1); + FabInterval Z = v.z(r.kmin, r.kmax-1); + + tree->eval(X, Y, Z); + + // If the result was unambiguous, then fill in that part + // of the image, then return. + tribool result = tree->root->result_interval <= FabInterval(0); + + if (result) v.fill(r); + if (!indeterminate(result)) return; + + // Use Newton's method on the distance metric to find + // an upper bound on the front of the object. + + // If we've hit voxel level, use floating-point evaluation + // when evaluating Newton's method. + if (r.ni * r.nj <= 1) { + float x = v.x(r.imin); + float y = v.y(r.jmin); + + float prev_k = r.kmax; + float k = prev_k; + do { + prev_k = k; + + tree->eval(x, y, v.z(k)); + float d1 = tree->root->result_float; + + tree->eval(x, y, v.z(k - 1)); + float d2 = tree->root->result_float; + + float slope = d1 - d2; + k = k - d1 / slope; + + } while (abs(k - prev_k) > 0.5); + + r.kmax = k; + v.fill(r); + + return; + } + + + // Otherwise, use interval evaluation on the xy region. + float prev_k = r.kmax; + float k = prev_k; + do { + prev_k = k; + + tree->eval(X, Y, v.z(k)); + float d1 = tree->root->result_interval.lower(); + + tree->eval(X, Y, v.z(k - 1)); + float d2 = tree->root->result_interval.lower(); + + float slope = d1 - d2; + k = k - d1 / slope; + } while (abs(k - prev_k) > 0.5); + r.kmax = k; + + list subregions = r.split_xy(2); + list::iterator it; + + for (it = subregions.begin(); it != subregions.end(); ++it) + evaluate_region(*it); +} \ No newline at end of file diff --git a/src/solver/raycaster.hpp b/src/solver/raycaster.hpp new file mode 100644 index 0000000..fb9468a --- /dev/null +++ b/src/solver/raycaster.hpp @@ -0,0 +1,34 @@ +#ifndef RAYCASTER_H +#define RAYCASTER_H + +#include + +#include "solver.hpp" + +class Raycaster : public Solver +{ +public: + /* Raycaster(FabVars& v) + * Raycaster(MathTree* tree, FabVars& v) + * + * Constructs an Raycaster instance. + */ + Raycaster(FabVars& v); + Raycaster(MathTree* tree, FabVars& v); + virtual ~Raycaster() { /* Nothing to do here */ } + + /* virtual void evaluate_region(Region R) + * + * Evaluate a given region recursively, saving results wherever is + * appropriate. + */ + virtual void evaluate_region(Region R); + + /* virtual void save() + * + * Nothing needs to happen here, since evaluate_region dumps data + * directly into the image matrix. + */ + void save() { /* Nothing to do here */ } +}; +#endif \ No newline at end of file diff --git a/src/solver/region.cpp b/src/solver/region.cpp new file mode 100644 index 0000000..e800bb8 --- /dev/null +++ b/src/solver/region.cpp @@ -0,0 +1,106 @@ +#include + +#include "region.hpp" +#include "fabvars.hpp" + +using namespace std; + +Region::Region() + : imin(0), jmin(0), kmin(0), + imax(0), jmax(0), kmax(0), + ni(0), nj(0), nk(0), + volume(0) +{ + // Nothing to do here +} + +Region::Region(const FabVars& v) + : imin(0), jmin(0), kmin(0), + imax(v.ni), jmax(v.nj), kmax(v.nk), + ni(v.ni), nj(v.nj), nk(v.nk), + volume(ni * nj * nk) +{ + // Nothing to do here +} + +Region::Region(unsigned int imin, unsigned int jmin, unsigned int kmin, + unsigned int imax, unsigned int jmax, unsigned int kmax) + : imin(imin), jmin(jmin), kmin(kmin), + imax(imax), jmax(jmax), kmax(kmax), + ni(imax - imin), nj(jmax - jmin), nk(kmax - kmin), + volume(ni * nj * nk) +{ + // Nothing to do here. +} + +list Region::split() const +{ + return split(2); +} + +list Region::split(int isplit, int jsplit, int ksplit) const +{ + isplit = isplit > ni ? ni : isplit; + jsplit = jsplit > nj ? nj : jsplit; + ksplit = ksplit > nk ? nk : ksplit; + + list L; + for (int k = ksplit - 1; k >= 0; --k) + for (int i = 0; i < isplit; ++i) + for (int j = 0; j < jsplit; ++j) + L.push_back(Region(imin + (i*ni) / isplit, + jmin + (j*nj) / jsplit, + kmin + (k*nk) / ksplit, + imin + ((i+1)*ni) / isplit, + jmin + ((j+1)*nj) / jsplit, + kmin + ((k+1)*nk) / ksplit)); + return L; +} + +// Split into a given number of segments (must be power of two), +// prioritizing larger dimensions +list Region::split(int count) const +{ + int i_score = 0; + int j_score = 0; + int k_score = 0; + + while ((1 << i_score) * (1 << j_score) * (1 << k_score) < count) { + float di = ni / float(1 << i_score); + float dj = nj / float(1 << j_score); + float dk = nk / float(1 << k_score); + if (di >= dj && di >= dk) + i_score++; + else if (di >= dk) + j_score++; + else + k_score++; + } + + return split(1< Region::split_xy(int count) const +{ + int i_score = 0; + int j_score = 0; + + while ((1 << i_score) * (1 << j_score) < count) { + float di = ni / float(1 << i_score); + float dj = nj / float(1 << j_score); + if (di >= dj) + i_score++; + else + j_score++; + } + + return split(1< +#include + +// Forward declarations +struct FabVars; + +// A note on terminology: +// x, y, z refer to coordinates in space. +// i, j, k refer to pixel discretized coordinates + +// Small structure to hold a 3D lattice region. +typedef struct Region { + + /* Region() + * + * Constructs a empty region. + */ + Region(); + + /* Region(const FabVars& v) + * + * Constructs a region that fills the entire space defined in v. + */ + Region(const FabVars& v); + + + /* Region(unsigned int imin, unsigned int jmin, unsigned int kmin, + unsigned int imax, unsigned int jmax, unsigned int kmax) + + * Constructs a region from the provided bounds. + */ + Region(unsigned int imin, unsigned int jmin, unsigned int kmin, + unsigned int imax, unsigned int jmax, unsigned int kmax); + + + /* std::list split() const + * + * Splits a region in half all three axes, returning a list of + * the resulting subregions. + */ + std::list split() const; + + /* std::list split(int count) const + * + * Splits a region into a provided number of subregions, returning + * a list of the resulting subregions. The split is biased towards + * the larger axes of the initial region. + */ + std::list split(int count) const; + std::list split_xy(int count) const; + + /* std::list split(int isplit, int jsplit, int ksplit) const + * + * Splits a region with a specific number of subregions per axis. + */ + std::list split(int isplit, int jsplit, int ksplit) const; + + // Boundaries of the region + int imin, jmin, kmin, imax, jmax, kmax; + + // Dimensions of the region + int ni, nj, nk; + + // Volume of the region + uint64_t volume; + +} Region; + +/* std::ostream& operator<<(std::ostream& o, const Region& r) + * + * Prints a region. + */ +std::ostream& operator<<(std::ostream& o, const Region& r); + +#endif \ No newline at end of file diff --git a/src/solver/solver.cpp b/src/solver/solver.cpp new file mode 100644 index 0000000..dec6a3b --- /dev/null +++ b/src/solver/solver.cpp @@ -0,0 +1,36 @@ +// This is an implementation file for the solver.hpp + +// It should not be compiled on its own (since a template needs to +// be instantiated with a particular class). + +#include "solver.hpp" + +#include "switches.hpp" +#include "task_buffer.hpp" + + +using namespace std; +using boost::logic::tribool; +using boost::thread; + +void Solver::run() +{ + Task task = task_buffer->next(); + task_buffer->hello(); + + while (task.region.volume != 0) { + if (task.tree) { + delete tree; + tree = task.tree; + } + evaluate_region(task.region); + task = task_buffer->next(); + } + + save(); +} + +void Solver::set_buffer(TaskBuffer* b) +{ + task_buffer = b; +} \ No newline at end of file diff --git a/src/solver/solver.hpp b/src/solver/solver.hpp new file mode 100644 index 0000000..da67a84 --- /dev/null +++ b/src/solver/solver.hpp @@ -0,0 +1,75 @@ +#ifndef SOLVER_H +#define SOLVER_H + +#include + +#include "fabvars.hpp" +#include "region.hpp" +#include "math_tree.hpp" + +// Forward declaration +class TaskBuffer; + +// Generic solver class. +class Solver +{ +public: + + /* Solver(MathTree* tree, FabVars& v) + * Solver(FabVars& v) + + * Constructs a solver with a given tree (or NULL) and reference to the + * global FabVars instance. + */ + Solver(MathTree* tree, FabVars& v) + : tree(tree), task_buffer(NULL), v(v) {} + Solver(FabVars& v) + : tree(NULL), task_buffer(NULL), v(v) {} + + + virtual ~Solver() { delete tree; } + + + /* virtual void evaluate_region(Region R) = 0 + * + * Evaluate a given region recursively, saving results wherever is + * appropriate. + */ + virtual void evaluate_region(Region R) = 0; + + + /* virtual void save() + * + * Saves the results of a solver instance's calculation. + * Typically, this will involve copying locally saved results into + * the solver's FabVars variable. + */ + virtual void save() = 0; + + + /* void run() + * + * Runs an instance of the solver from the task buffer. + */ + void run(); + + + /* void set_buffer(TaskBuffer* b) + * + * Sets the queue from which the solver will operate. + */ + void set_buffer(TaskBuffer* b); + +protected: + // The instance of the MathTree associated with this solver. + MathTree* tree; + + // For a thread-pool style solver, this object gives us tasks. + TaskBuffer* task_buffer; + + // A reference to the system's FabVars, where results are stored. + FabVars& v; +}; + +#endif + \ No newline at end of file diff --git a/src/solver/switches.hpp b/src/solver/switches.hpp new file mode 100644 index 0000000..9ae1aa2 --- /dev/null +++ b/src/solver/switches.hpp @@ -0,0 +1,15 @@ +#ifndef SWITCHES_H +#define SWITCHES_H + +// This header defines compile-time switches that turn on +// or off various optimizations. Optimizations are on when +// a switch is 1 and off when a switch is 0. + +#define COMBINE_NODES 1 +#define PRUNE_TREE 1 +#define MINMAX_PRUNE 1 +#define MULTITHREADED 1 +#define SIMPLIFY_TREE 1 +#define CULL_Z 1 + +#endif \ No newline at end of file diff --git a/src/solver/task_buffer.cpp b/src/solver/task_buffer.cpp new file mode 100644 index 0000000..3ec3526 --- /dev/null +++ b/src/solver/task_buffer.cpp @@ -0,0 +1,158 @@ +#include "math_tree.hpp" +#include "task_buffer.hpp" + +#include +using namespace std; + +typedef boost::lock_guard locker; + +Task::Task() + : tree(NULL) +{ + // Nothing to do here +} + +Task::Task(Region region) + : region(region), tree(NULL) +{ + // Nothing to do here. +} + +Task::Task(Region region, MathTree* tree) + : region(region), tree(tree) +{ + // Nothing to do here +} + +TaskSlot::TaskSlot() + : ready(false) +{ + // Nothing to do here +} + + +TaskBuffer::TaskBuffer(unsigned size) + : size(size), add_position(0), read_position(0), + active_threads(0), unassigned_tasks(0), barrier(size) +{ + tasks = new TaskSlot[size]; +} + + +TaskBuffer::~TaskBuffer() +{ + delete [] tasks; +} + + +bool TaskBuffer::full() const +{ + return (unassigned_tasks >= size - active_threads); +} + +// Add a task to the buffer and notify the corresponding thread. +bool TaskBuffer::add(Region region) +{ + return add(region, NULL); +} + +bool TaskBuffer::add(Region region, MathTree* tree) +{ + + { // Check to see if a thread is available for this + // new unassigned task. + locker lock(master_mutex); + if (unassigned_tasks >= size - active_threads) + return false; + unassigned_tasks++; + } + + + unsigned pos; + { // Figure out which task slot we're about to fill + locker lock(add_mutex); + pos = add_position; + + // Increment the add position in the buffer + add_position = (add_position + 1) % size; + } + + { // Fill the task slot + locker lock(tasks[pos].mutex); + if (tree) + tasks[pos].task = Task(region, tree->clone()); + else + tasks[pos].task = Task(region); + tasks[pos].ready = true; +// cout << "Added new task with region " << region << ' ' << tasks[pos].task.tree << endl; + } + // Notify anyone that has been waiting on this slot. + tasks[pos].condition.notify_one(); + + return true; +} + + +// Each thread should call this function once before taking +// data from the task buffer. +void TaskBuffer::hello() +{ + { + locker lock(master_mutex); + active_threads++; + } + barrier.wait(); +} + + +// Acquire the next task from the buffer. If there is nothing left, then +// fill the buffer with null tasks to halt execution. +Task TaskBuffer::next() +{ + unsigned pos; + { // Figure out the slot from which we'll read + locker lock(read_mutex); + pos = read_position; + read_position = (read_position + 1) % size; + } + + { // Mark that this thread is inactive + locker lock(master_mutex); + active_threads--; + + // If all threads are inactive and there are no unassigned + // tasks, then we're done with evaluation. finish() fills the buffer + // with null tasks to signal the end of execution. + if (active_threads == 0 && unassigned_tasks == 0) + finish(); + } + + Task t; + { // Wait until this task slot is filled, then claim the task. + boost::unique_lock lock(tasks[pos].mutex); + while (!tasks[pos].ready) + tasks[pos].condition.wait(lock); + t = tasks[pos].task; + tasks[pos].ready = false; + } + + { // Note that this thread is now active. + locker lock(master_mutex); + unassigned_tasks--; + active_threads++; + } + + return t; +} + + +// Populate the task buffer with null tasks, to signal the end of the program. +void TaskBuffer::finish() +{ + for (unsigned i = 0; i < size; ++i) { + locker lock(tasks[i].mutex); + tasks[i].task = Task(); + tasks[i].ready = true; + tasks[i].condition.notify_one(); + } +} diff --git a/src/solver/task_buffer.hpp b/src/solver/task_buffer.hpp new file mode 100644 index 0000000..af42674 --- /dev/null +++ b/src/solver/task_buffer.hpp @@ -0,0 +1,109 @@ +#ifndef TASK_BUFFER_H +#define TASK_BUFFER_H + +#include + +#include "region.hpp" + +class MathTree; + +// Simple structure containing a task to be solved. +typedef struct Task { + Task(); + Task(Region region); + Task(Region region, MathTree* tree); + + Region region; + MathTree* tree; +} Task; + + +// Slot to store a task and associated metadata in the ring buffer. +typedef struct TaskSlot { + TaskSlot(); + + Task task; + bool ready; + boost::mutex mutex; + boost::condition_variable condition; +} TaskSlot; + + +class TaskBuffer +{ +public: + /* TaskBuffer(unsigned size) + * + * Create a ring buffer of the appropriate size. + */ + TaskBuffer(unsigned size); + + + /* ~TaskBuffer() + * + * Destructor for the task buffer. + */ + ~TaskBuffer(); + + + /* bool full() const + * + * Checks to see if the task buffer is full in a thread-unsafe + * way. A further check should be run in a thread-safe manner. + */ + bool full() const; + + /* bool add(Region region, MathTree* tree) + * + * Adds a task to the latest available slot, with the provided + * region and a clone of the provided tree. Increments + * add_position and unassigned_tasks. + * + * If all slots are full, returns false; otherwise returns true. + */ + bool add(Region region); + bool add(Region region, MathTree* tree); + + + /* void hello() + * + * Each active thread should call this function once so that the + * task buffer has a count of how many active threads are running. + */ + void hello(); + + /* Task next() + * + * Acquires the next task from the list. If there are no tasks + * and no active threads, fill the buffer with null tasks to halt + * the process + * + * On start, decrements active_threads; when a task is acquired, + * increment active_threads and decrement unassigned_tasks. + * + * Increments read_position. + */ + Task next(); + + const unsigned size; + +private: + + void finish(); + + boost::mutex add_mutex; + unsigned add_position; + + boost::mutex read_mutex; + unsigned read_position; + + TaskSlot* tasks; + + boost::mutex master_mutex; + unsigned active_threads; + unsigned unassigned_tasks; + + boost::barrier barrier; +}; + +#endif \ No newline at end of file diff --git a/src/solver/thread_manager.cpp b/src/solver/thread_manager.cpp new file mode 100644 index 0000000..33ced7a --- /dev/null +++ b/src/solver/thread_manager.cpp @@ -0,0 +1,87 @@ +// This is an implementation file for the ThreadManager + +#include "math_tree.hpp" +#include "switches.hpp" +#include "thread_manager.hpp" +#include "solver.hpp" + +using namespace std; +using boost::logic::tribool; +using boost::thread; + +/////////////////////////////////////////////////////////////////////////////// + +template +ThreadManager::ThreadManager(FabVars& v) + : v(v), task_buffer(thread::hardware_concurrency()) +{ + // Nothing to do here. +} + +/////////////////////////////////////////////////////////////////////////////// + +// Evaluates an entire math tree on the full spatial region. +template +void ThreadManager::evaluate(MathTree* tree) +{ + list regions; + if (v.projection) + regions = Region(v).split_xy(task_buffer.size); + else + regions = Region(v).split(task_buffer.size); + + // Create a task for each region + list::iterator it; + for (it = regions.begin(); it != regions.end(); ++it) { +// cout << "Added region " << *it << endl; + if (!task_buffer.add(*it, tree)) { + cout << "Failed to add initial task to task buffer!" << endl; + exit(1); + } + } + + // Create a new thread for all but one of the tasks + for (unsigned i = 1; i < task_buffer.size; ++i) + make_new_thread(); + + // This thread gets a task as well. + SOLVER_TYPE* s = new SOLVER_TYPE(v); + s->set_buffer(&task_buffer); + s->run(); + delete s; + + wait_for_threads(); +} + +/////////////////////////////////////////////////////////////////////////////// + +template +void ThreadManager::make_new_thread() +{ + SOLVER_TYPE* s = new SOLVER_TYPE(v); + s->set_buffer(&task_buffer); + thread* newThread = new thread(&Solver::run, s); + + threads.push_back(make_pair(newThread, s)); +} + +/////////////////////////////////////////////////////////////////////////////// + +template +void ThreadManager::wait_for_threads() +{ + while (!threads.empty()) + { + typename ThreadList::iterator it = threads.begin(); + while(it != threads.end()) + if (it->first->timed_join(boost::system_time())) + { + delete it->first; + delete it->second; + + it = threads.erase(it); + } else { + ++it; + } + } +} \ No newline at end of file diff --git a/src/solver/thread_manager.hpp b/src/solver/thread_manager.hpp new file mode 100644 index 0000000..20d0d78 --- /dev/null +++ b/src/solver/thread_manager.hpp @@ -0,0 +1,53 @@ +#ifndef THREAD_MANAGER_H +#define THREAD_MANAGER_H + +#include "task_buffer.hpp" + +template +class ThreadManager +{ +public: + + /* ThreadManager(FabVars& v) + * + * Constructor for ThreadManager. + */ + ThreadManager(FabVars& v); + + + /* void evaluate(MathTree* tree, FabVars& v) + * + * This function evaluates the math tree on the full region defined in v, writing + * solver-dependent output to v. + */ + void evaluate(MathTree* tree); + + + /* static void make_new_thread() + * + * This function creates a new thread, in which a solver instance calls + * Solver::run() + */ + void make_new_thread(); + + + /* void wait_for_threads() + * + * Waits until all of the threads are finished. As each thread finishes, + * this function instructs it to saves its results, then deletes it. + */ + void wait_for_threads(); + +private: + FabVars& v; + + typedef std::list > ThreadList; + ThreadList threads; + + TaskBuffer task_buffer; +}; + +// Templated implementation file +#include "thread_manager.cpp" + +#endif \ No newline at end of file diff --git a/src/solver/translator_nodes.cpp b/src/solver/translator_nodes.cpp new file mode 100644 index 0000000..70aa93b --- /dev/null +++ b/src/solver/translator_nodes.cpp @@ -0,0 +1,139 @@ +#include + +#include "translator_nodes.hpp" + +using namespace std; +using boost::logic::indeterminate; + +BoolToNum::BoolToNum() + : UnaryNode(BOOL2NUM) +{ + // Nothing to do here. +} + + +BoolToNum::BoolToNum(Node* c) + : UnaryNode(BOOL2NUM) +{ + set_child(c); +} + +void BoolToNum::eval(const float X, const float Y, const float Z) +{ + if (child->result_bool) + result_float = 1; + else + result_float = 0; +} + +void BoolToNum::eval(const FabInterval& X, + const FabInterval& Y, + const FabInterval& Z) +{ + if (child->result_bool) + result_interval = 1; + else if (!child->result_bool) + result_interval = 0; + else + result_interval = FabInterval(0, 1); +} + +//////////////////////////////////////////////////////////////////////////////// + +NumToBool::NumToBool() + : UnaryNode(NUM2BOOL) +{ + // Nothing to do here. +} + + +NumToBool::NumToBool(Node* c) + : UnaryNode(NUM2BOOL) +{ + set_child(c); +} + +void NumToBool::eval(const float X, const float Y, const float Z) +{ + if (child->result_float) + result_bool = true; + else + result_bool = false; +} + +void NumToBool::eval(const FabInterval& X, + const FabInterval& Y, + const FabInterval& Z) +{ + if (child->result_interval.lower() == 0 && + child->result_interval.upper() == 0) + result_bool = false; + else if (child->result_interval.upper() < 0 || + child->result_interval.lower() > 0) + result_bool = true; + else + result_bool = indeterminate; +} + +/////////////////////////////////////////////////////////////////////////////// + +BoolToColor::BoolToColor() + : UnaryNode(BOOL2COLOR) +{ + // Nothing to do here. +} + +BoolToColor::BoolToColor(Node* c) + : UnaryNode(BOOL2COLOR) +{ + set_child(c); +} + +void BoolToColor::eval(const float X, const float Y, const float Z) +{ + if (child->result_bool) + result_color = 255 + (255 << 8) + (255 << 16); + else + result_color = 0; +} + +void BoolToColor::eval(const FabInterval& X, + const FabInterval& Y, + const FabInterval& Z) +{ + if (child->result_bool) + result_color = 255 + (255 << 8) + (255 << 16); + else if (!child->result_bool) + result_color = 0; + else + result_color = -1; +} + +/////////////////////////////////////////////////////////////////////////////// + +NumToColor::NumToColor() + : UnaryNode(NUM2COLOR) +{ + // Nothing to do here. +} + +NumToColor::NumToColor(Node* c) + : UnaryNode(NUM2COLOR) +{ + set_child(c); +} + +void NumToColor::eval(const float X, const float Y, const float Z) +{ + result_color = int(child->result_float); +} + +void NumToColor::eval(const FabInterval& X, + const FabInterval& Y, + const FabInterval& Z) +{ + if (child->result_interval.lower() == child->result_interval.upper()) + result_color = int(child->result_interval.lower()); + else + result_color = -1; +} \ No newline at end of file diff --git a/src/solver/translator_nodes.hpp b/src/solver/translator_nodes.hpp new file mode 100644 index 0000000..72c18d6 --- /dev/null +++ b/src/solver/translator_nodes.hpp @@ -0,0 +1,14 @@ +#ifndef TRANSLATOR_NODES +#define TRANSLATOR_NODES + +#include "node.hpp" +#include "node_macro.hpp" + +// This header file defines nodes that take in inputs of +// one type and return outputs of another type. +TRANSLATOR(BoolToNum, UnaryNode); +TRANSLATOR(NumToBool, UnaryNode); +TRANSLATOR(BoolToColor, UnaryNode); +TRANSLATOR(NumToColor, UnaryNode); + +#endif \ No newline at end of file diff --git a/src/solver/trisolver.cpp b/src/solver/trisolver.cpp new file mode 100644 index 0000000..b648127 --- /dev/null +++ b/src/solver/trisolver.cpp @@ -0,0 +1,202 @@ +#include "trisolver.hpp" +#include "math_tree.hpp" +#include "node.hpp" + +#include "switches.hpp" + +#include +#include + +using namespace std; +using boost::logic::tribool; +using boost::thread; + +const Vec3f VERTEX_LOOP[] = { + Vec3f(1, 1, 0), Vec3f(1, 0, 0), Vec3f(1, 0, 1), + Vec3f(0, 0, 1), Vec3f(0, 1, 1), Vec3f(0, 1, 0), + Vec3f(1, 1, 0) +}; + +// Based on which vertices are filled, this map tells you which +// edges to interpolate between when forming zero, one, or two +// triangles. +// (filled vertex is first in the pair) +const int EDGE_MAP[16][2][3][2] = { + {{{-1,-1}, {-1,-1}, {-1,-1}}, {{-1,-1}, {-1,-1}, {-1,-1}}}, // ---- + {{{ 0, 2}, { 0, 1}, { 0, 3}}, {{-1,-1}, {-1,-1}, {-1,-1}}}, // ---0 + {{{ 1, 0}, { 1, 2}, { 1, 3}}, {{-1,-1}, {-1,-1}, {-1,-1}}}, // --1- + {{{ 1, 2}, { 1, 3}, { 0, 3}}, {{ 0, 3}, { 0, 2}, { 1, 2}}}, // --10 + {{{ 2, 0}, { 2, 3}, { 2, 1}}, {{-1,-1}, {-1,-1}, {-1,-1}}}, // -2-- + {{{ 0, 3}, { 2, 3}, { 2, 1}}, {{ 2, 1}, { 0, 1}, { 0, 3}}}, // -2-0 + {{{ 1, 0}, { 2, 0}, { 2, 3}}, {{ 2, 3}, { 1, 3}, { 1, 0}}}, // -21- + {{{ 2, 3}, { 1, 3}, { 0, 3}}, {{-1,-1}, {-1,-1}, {-1,-1}}}, // -210 + + {{{ 3, 0}, { 3, 1}, { 3, 2}}, {{-1,-1}, {-1,-1}, {-1,-1}}}, // 3--- + {{{ 3, 2}, { 0, 2}, { 0, 1}}, {{ 0, 1}, { 3, 1}, { 3, 2}}}, // 3--0 + {{{ 1, 2}, { 3, 2}, { 3, 0}}, {{ 3, 0}, { 1, 0}, { 1, 2}}}, // 3-1- + {{{ 1, 2}, { 3, 2}, { 0, 2}}, {{-1,-1}, {-1,-1}, {-1,-1}}}, // 3-10 + {{{ 3, 0}, { 3, 1}, { 2, 1}}, {{ 2, 1}, { 2, 0}, { 3, 0}}}, // 32-- + {{{ 3, 1}, { 2, 1}, { 0, 1}}, {{-1,-1}, {-1,-1}, {-1,-1}}}, // 32-0 + {{{ 3, 0}, { 1, 0}, { 2, 0}}, {{-1,-1}, {-1,-1}, {-1,-1}}}, // 321- + {{{-1,-1}, {-1,-1}, {-1,-1}}, {{-1,-1}, {-1,-1}, {-1,-1}}}, // 3210 +}; + + +/////////////////////////////////////////////////////////////////////////////// + +TriSolver::TriSolver(FabVars& v) + : Solver(v) +{ + // Nothing to do here. +} + +TriSolver::TriSolver(MathTree* tree, FabVars& v) + : Solver(tree, v) +{ + // Nothing to do here. +} + +void TriSolver::save() +{ + v.add_triangles(triangles); +} + +// Evaluate a single region, either with point-by-point evaluation or +// interval math + recursion. Operates in a single thread and spawns +// no children. +void TriSolver::evaluate_region(Region r) +{ + // For sufficiently small fractions of the space, do a + // point-by-point evaluation rather than recursing. + if (r.volume == 1) { + evaluate_voxel(r); + v.pb.update(r.volume); + return; + } + + // Convert from pixel regions to intervals + FabInterval X = v.x(r.imin, r.imax); + FabInterval Y = v.y(r.jmin, r.jmax); + FabInterval Z = v.z(r.kmin, r.kmax); + + tree->eval(X, Y, Z); + + // If the result was unambiguous, then fill in that part + // of the image, then return. + tribool result; + if (v.mode == SOLVE_BOOL) + result = tree->root->result_bool; + else if (v.mode == SOLVE_REAL) + result = tree->root->result_interval < FabInterval(0,0); + + if (!indeterminate(result)) { + v.pb.update(r.volume); + return; + } + + // Split the region and recurse + list subregions = r.split(); + +#if PRUNE_TREE + tree->push(); +#endif + + list::iterator it; + for (it = subregions.begin(); it != subregions.end(); ++it) + evaluate_region(*it); + +#if PRUNE_TREE + tree->pop(); +#endif +} + +/////////////////////////////////////////////////////////////////////////////// + +void TriSolver::evaluate_voxel(Region r) +{ + Vec3f corner(r.imin, r.jmin, r.kmin); + Vec3f v1, v2, v3; // triangle vertices + + for (int t = 0; t < 6; ++t) { + // Look up the vertex locations for this tetrahedron + Vec3f vertices[] = {corner, corner + Vec3f(1, 1, 1), + corner + VERTEX_LOOP[t], corner + VERTEX_LOOP[t+1]}; + + // Check which vertices are filled to see how we need to triangulate. + unsigned char lookup = 0; + for (int vertex = 3; vertex >= 0; --vertex) { + lookup <<= 1; + Vec3f pos = vertices[vertex]; + + std::map::iterator it = point_cache.find(pos); + if (it == point_cache.end()) { + tree->eval(v.x(pos.x), v.y(pos.y), v.z(pos.z)); + + if (v.mode == SOLVE_BOOL) + point_cache[pos] = tree->root->result_bool; + else if (v.mode == SOLVE_REAL) + point_cache[pos] = tree->root->result_float < 0; + + if (point_cache[pos]) + lookup++; + } else if (it->second) + lookup++; + } + + // If the triangle map is empty, continue. + if (EDGE_MAP[lookup][0][0][0] == -1) + continue; + + v1 = interpolate(vertices[EDGE_MAP[lookup][0][0][0]], + vertices[EDGE_MAP[lookup][0][0][1]]); + v2 = interpolate(vertices[EDGE_MAP[lookup][0][1][0]], + vertices[EDGE_MAP[lookup][0][1][1]]); + v3 = interpolate(vertices[EDGE_MAP[lookup][0][2][0]], + vertices[EDGE_MAP[lookup][0][2][1]]); + triangles.push_back(Triangle(v1, v2, v3)); + + + // If there was only one triangle, skip the second face + if (EDGE_MAP[lookup][1][0][0] == -1) + continue; + + v1 = interpolate(vertices[EDGE_MAP[lookup][1][0][0]], + vertices[EDGE_MAP[lookup][1][0][1]]); + v2 = interpolate(vertices[EDGE_MAP[lookup][1][1][0]], + vertices[EDGE_MAP[lookup][1][1][1]]); + v3 = interpolate(vertices[EDGE_MAP[lookup][1][2][0]], + vertices[EDGE_MAP[lookup][1][2][1]]); + + triangles.push_back(Triangle(v1, v2, v3)); + } + +} + +// Interpolate between a filled and empty point using binary search +// (after checking in a cache). +Vec3f TriSolver::interpolate(Vec3f filled, Vec3f empty) +{ + std::map::iterator it; + it = edge_cache.find(Edge(filled, empty)); + if (it != edge_cache.end()) + return it->second; + + float step_size = 0.25; + float interp = 0.5; + Vec3f offset = empty - filled; + + for (int i = 0; i < v.quality; ++i) { + Vec3f pos = filled + offset * interp; + + tree->eval(v.x(pos.x), v.y(pos.y), v.z(pos.z)); + if ((v.mode == SOLVE_BOOL && tree->root->result_bool) || + (v.mode == SOLVE_REAL && tree->root->result_float < 0)) + interp += step_size; + else + interp -= step_size; + step_size /= 2; + } + edge_cache[Edge(filled, empty)] = filled + offset * interp; + + return filled + offset * interp; +} \ No newline at end of file diff --git a/src/solver/trisolver.hpp b/src/solver/trisolver.hpp new file mode 100644 index 0000000..566ad6a --- /dev/null +++ b/src/solver/trisolver.hpp @@ -0,0 +1,64 @@ +#ifndef TRISOLVER_H +#define TRISOLVER_H + +#include +#include + +#include "solver.hpp" +#include "geometry.hpp" + +class TriSolver : public Solver +{ +public: + /* TriSolver(FabVars& v) + * TriSolver(MathTree* tree, FabVars& v) + * + * Constructs an TriSolver instance. + */ + TriSolver(FabVars& v); + TriSolver(MathTree* tree, FabVars& v); + virtual ~TriSolver() { /* Nothing to do here */ } + + /* virtual void evaluate_region(Region R) + * + * Evaluate a given region recursively, saving results wherever is + * appropriate. + */ + virtual void evaluate_region(Region R); + + + /* void evaluate_voxel(Region R) + * + * Evaluate a single unit cube. + */ + void evaluate_voxel(Region R); + + + /* virtual void save() + * + * Saves the results of our calculation by copying paths into our + * reference to FabVars v. + */ + virtual void save(); + + + /* Vec3f interpolate(Vec3f filled, Vec3f empty) + * + * Interpolates between a filled and an empty point using binary search. + * Values are saved in a cache, and the cache is checked before the + * search is run. + */ + Vec3f interpolate(Vec3f filled, Vec3f empty); + +private: + // MathTree evaluation cached values + std::map point_cache; + + // Saved edges interpolation values + std::map edge_cache; + + // Saved triangles + std::list triangles; + +}; +#endif \ No newline at end of file diff --git a/src/solver/volsolver.cpp b/src/solver/volsolver.cpp new file mode 100644 index 0000000..28617fa --- /dev/null +++ b/src/solver/volsolver.cpp @@ -0,0 +1,115 @@ +#include "volsolver.hpp" +#include "math_tree.hpp" +#include "node.hpp" + +#include "switches.hpp" + +#include +#include + +using namespace std; +using boost::logic::tribool; +using boost::thread; + +/////////////////////////////////////////////////////////////////////////////// + +VolSolver::VolSolver(FabVars& v) + : Solver(v) +{ + // Nothing to do here. +} + +VolSolver::VolSolver(MathTree* tree, FabVars& v) + : Solver(tree, v) +{ + // Nothing to do here. +} + +void VolSolver::save() +{ + v.add_volume(volume); +} + +// Evaluate a single region, either with point-by-point evaluation or +// interval math + recursion. Operates in a single thread and spawns +// no children. +void VolSolver::evaluate_region(Region r) +{ + // For sufficiently small fractions of the space, do a + // point-by-point evaluation rather than recursing. + if (r.volume <= v.min_volume) { + evaluate_points(r); + v.pb.update(r.volume); + return; + } + + // Convert from pixel regions to intervals + FabInterval X = v.x(r.imin, r.imax); + FabInterval Y = v.y(r.jmin, r.jmax); + FabInterval Z = v.z(r.kmin, r.kmax); + + tree->eval(X, Y, Z); + + // If the result was unambiguous, then fill in that part + // of the image, then return. + tribool result; + if (v.mode == SOLVE_BOOL) + result = tree->root->result_bool; + else if (v.mode == SOLVE_REAL) + result = tree->root->result_interval < FabInterval(0,0); + + if (result) + volume += r.volume; + + if (!indeterminate(result)) { + v.pb.update(r.volume); + return; + } + + // Split the region and recurse + list subregions = r.split(); + +#if PRUNE_TREE + tree->push(); +#endif + + list::iterator it; + for (it = subregions.begin(); it != subregions.end(); ++it) + evaluate_region(*it); + +#if PRUNE_TREE + tree->pop(); +#endif +} + +/////////////////////////////////////////////////////////////////////////////// +void VolSolver::evaluate_points(Region r) +{ + for (int k = r.kmax - 1; k >= r.kmin; --k) + { + + // Calculate Z coordinate and height-map scaling. + float Z = v.z(k); + + for (int i = r.imin; i < r.imax; ++i) + { // X loop + float X = v.x(i); + + for (int j = r.jmin; j < r.jmax; ++j) + { // Y loop + float Y = v.y(j); + + // Evaluate tree + tree->eval(X, Y, Z); + + // Fill in greyscale image + if ((v.mode == SOLVE_BOOL && tree->root->result_bool) || + (v.mode == SOLVE_REAL && tree->root->result_float <= 0)) + { + volume++; + } + + } // Y loop + } // X loop + } // Z loop +} \ No newline at end of file diff --git a/src/solver/volsolver.hpp b/src/solver/volsolver.hpp new file mode 100644 index 0000000..cad3985 --- /dev/null +++ b/src/solver/volsolver.hpp @@ -0,0 +1,51 @@ +#ifndef VolSolver_H +#define VolSolver_H + +#include +#include +#include + +#include "solver.hpp" +#include "geometry.hpp" + +class VolSolver : public Solver +{ +public: + /* VolSolver(FabVars& v) + * VolSolver(MathTree* tree, FabVars& v) + * + * Constructs an VolSolver instance. + */ + VolSolver(FabVars& v); + VolSolver(MathTree* tree, FabVars& v); + virtual ~VolSolver() { /* Nothing to do here */ } + + /* virtual void evaluate_region(Region R) + * + * Evaluate a given region recursively, saving results wherever is + * appropriate. + */ + virtual void evaluate_region(Region R); + + + /* void evaluate_points(Region R) + * + * Evaluate a set of points in a region + */ + void evaluate_points(Region r); + + + /* virtual void save() + * + * Saves the results of our calculation by copying paths into our + * reference to FabVars v. + */ + virtual void save(); + + +private: + // Saved volume + uint64_t volume; + +}; +#endif \ No newline at end of file