Excellon parser bug fixed, improved Gerber parse, added icons, added some tooltips.
This commit is contained in:
131
FlatCAM.py
131
FlatCAM.py
@@ -11,6 +11,7 @@ import threading
|
|||||||
# TODO: Bundle together. This is just for debugging.
|
# TODO: Bundle together. This is just for debugging.
|
||||||
from gi.repository import Gtk
|
from gi.repository import Gtk
|
||||||
from gi.repository import Gdk
|
from gi.repository import Gdk
|
||||||
|
from gi.repository import GdkPixbuf
|
||||||
from gi.repository import GLib
|
from gi.repository import GLib
|
||||||
from gi.repository import GObject
|
from gi.repository import GObject
|
||||||
import simplejson as json
|
import simplejson as json
|
||||||
@@ -23,6 +24,7 @@ from matplotlib.backends.backend_gtk3agg import FigureCanvasGTK3Agg as FigureCan
|
|||||||
|
|
||||||
from camlib import *
|
from camlib import *
|
||||||
import sys
|
import sys
|
||||||
|
import urllib
|
||||||
|
|
||||||
|
|
||||||
########################################
|
########################################
|
||||||
@@ -129,7 +131,7 @@ class FlatCAMObj:
|
|||||||
|
|
||||||
def set_form_item(self, option):
|
def set_form_item(self, option):
|
||||||
"""
|
"""
|
||||||
Copies the specified options to the UI form.
|
Copies the specified option to the UI form.
|
||||||
|
|
||||||
:param option: Name of the option (Key in ``self.options``).
|
:param option: Name of the option (Key in ``self.options``).
|
||||||
:type option: str
|
:type option: str
|
||||||
@@ -150,6 +152,13 @@ class FlatCAMObj:
|
|||||||
print "Unknown kind of form item:", fkind
|
print "Unknown kind of form item:", fkind
|
||||||
|
|
||||||
def read_form_item(self, option):
|
def read_form_item(self, option):
|
||||||
|
"""
|
||||||
|
Reads the specified option from the UI form into ``self.options``.
|
||||||
|
|
||||||
|
:param option: Name of the option.
|
||||||
|
:type option: str
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
fkind = self.form_kinds[option]
|
fkind = self.form_kinds[option]
|
||||||
fname = fkind + "_" + self.kind + "_" + option
|
fname = fkind + "_" + self.kind + "_" + option
|
||||||
|
|
||||||
@@ -175,25 +184,23 @@ class FlatCAMObj:
|
|||||||
# Creates the axes if necessary and sets them up.
|
# Creates the axes if necessary and sets them up.
|
||||||
self.setup_axes(figure)
|
self.setup_axes(figure)
|
||||||
|
|
||||||
# Clear axes.
|
|
||||||
# self.axes.cla()
|
|
||||||
# return
|
|
||||||
|
|
||||||
def serialize(self):
|
def serialize(self):
|
||||||
"""
|
"""
|
||||||
Returns a representation of the object as a dictionary so
|
Returns a representation of the object as a dictionary so
|
||||||
it can be later exported as JSON. Override this method.
|
it can be later exported as JSON. Override this method.
|
||||||
@return: Dictionary representing the object
|
|
||||||
@rtype: dict
|
:return: Dictionary representing the object
|
||||||
|
:rtype: dict
|
||||||
"""
|
"""
|
||||||
return
|
return
|
||||||
|
|
||||||
def deserialize(self, obj_dict):
|
def deserialize(self, obj_dict):
|
||||||
"""
|
"""
|
||||||
Re-builds an object from its serialized version.
|
Re-builds an object from its serialized version.
|
||||||
@param obj_dict: Dictionary representing a FlatCAMObj
|
|
||||||
@type obj_dict: dict
|
:param obj_dict: Dictionary representing a FlatCAMObj
|
||||||
@return None
|
:type obj_dict: dict
|
||||||
|
:return None
|
||||||
"""
|
"""
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -257,6 +264,7 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
|
|||||||
:return: None
|
:return: None
|
||||||
:rtype: None
|
:rtype: None
|
||||||
"""
|
"""
|
||||||
|
|
||||||
factor = Gerber.convert_units(self, units)
|
factor = Gerber.convert_units(self, units)
|
||||||
|
|
||||||
self.options['isotooldia'] *= factor
|
self.options['isotooldia'] *= factor
|
||||||
@@ -317,7 +325,7 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
|
|||||||
|
|
||||||
class FlatCAMExcellon(FlatCAMObj, Excellon):
|
class FlatCAMExcellon(FlatCAMObj, Excellon):
|
||||||
"""
|
"""
|
||||||
Represents Excellon code.
|
Represents Excellon/Drill code.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name):
|
def __init__(self, name):
|
||||||
@@ -367,7 +375,12 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
|
|||||||
if not self.options["plot"]:
|
if not self.options["plot"]:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Plot excellon
|
try:
|
||||||
|
_ = iter(self.solid_geometry)
|
||||||
|
except TypeError:
|
||||||
|
self.solid_geometry = [self.solid_geometry]
|
||||||
|
|
||||||
|
# Plot excellon (All polygons?)
|
||||||
for geo in self.solid_geometry:
|
for geo in self.solid_geometry:
|
||||||
x, y = geo.exterior.coords.xy
|
x, y = geo.exterior.coords.xy
|
||||||
self.axes.plot(x, y, 'r-')
|
self.axes.plot(x, y, 'r-')
|
||||||
@@ -446,6 +459,7 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
|
|||||||
print "FlatCAMCNCjob.convert_units()"
|
print "FlatCAMCNCjob.convert_units()"
|
||||||
self.options["tooldia"] *= factor
|
self.options["tooldia"] *= factor
|
||||||
|
|
||||||
|
|
||||||
class FlatCAMGeometry(FlatCAMObj, Geometry):
|
class FlatCAMGeometry(FlatCAMObj, Geometry):
|
||||||
"""
|
"""
|
||||||
Geometric object not associated with a specific
|
Geometric object not associated with a specific
|
||||||
@@ -596,12 +610,11 @@ class App:
|
|||||||
# Needed to interact with the GUI from other threads.
|
# Needed to interact with the GUI from other threads.
|
||||||
GObject.threads_init()
|
GObject.threads_init()
|
||||||
|
|
||||||
## GUI ##
|
#### GUI ####
|
||||||
self.gladefile = "FlatCAM.ui"
|
self.gladefile = "FlatCAM.ui"
|
||||||
self.builder = Gtk.Builder()
|
self.builder = Gtk.Builder()
|
||||||
self.builder.add_from_file(self.gladefile)
|
self.builder.add_from_file(self.gladefile)
|
||||||
self.window = self.builder.get_object("window1")
|
self.window = self.builder.get_object("window1")
|
||||||
self.window.set_title("FlatCAM - Alpha 1 UNSTABLE - Check for updates!")
|
|
||||||
self.position_label = self.builder.get_object("label3")
|
self.position_label = self.builder.get_object("label3")
|
||||||
self.grid = self.builder.get_object("grid1")
|
self.grid = self.builder.get_object("grid1")
|
||||||
self.notebook = self.builder.get_object("notebook1")
|
self.notebook = self.builder.get_object("notebook1")
|
||||||
@@ -613,22 +626,23 @@ class App:
|
|||||||
# White (transparent) background on the "Options" tab.
|
# White (transparent) background on the "Options" tab.
|
||||||
self.builder.get_object("vp_options").override_background_color(Gtk.StateType.NORMAL,
|
self.builder.get_object("vp_options").override_background_color(Gtk.StateType.NORMAL,
|
||||||
Gdk.RGBA(1, 1, 1, 1))
|
Gdk.RGBA(1, 1, 1, 1))
|
||||||
|
|
||||||
# Combo box to choose between project and application options.
|
# Combo box to choose between project and application options.
|
||||||
self.combo_options = self.builder.get_object("combo_options")
|
self.combo_options = self.builder.get_object("combo_options")
|
||||||
self.combo_options.set_active(1)
|
self.combo_options.set_active(1)
|
||||||
|
|
||||||
## Event handling ##
|
self.setup_project_list() # The "Project" tab
|
||||||
|
self.setup_component_editor() # The "Selected" tab
|
||||||
|
|
||||||
|
#### Event handling ####
|
||||||
self.builder.connect_signals(self)
|
self.builder.connect_signals(self)
|
||||||
|
|
||||||
## Make plot area ##
|
#### Make plot area ####
|
||||||
self.figure = None
|
self.figure = None
|
||||||
self.axes = None
|
self.axes = None
|
||||||
self.canvas = None
|
self.canvas = None
|
||||||
self.setup_plot()
|
self.setup_plot()
|
||||||
|
|
||||||
self.setup_project_list() # The "Project" tab
|
self.setup_tooltips()
|
||||||
self.setup_component_editor() # The "Selected" tab
|
|
||||||
|
|
||||||
#### DATA ####
|
#### DATA ####
|
||||||
self.clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
|
self.clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
|
||||||
@@ -663,8 +677,6 @@ class App:
|
|||||||
self.radios_inv = {"units": {"IN": "rb_inch", "MM": "rb_mm"},
|
self.radios_inv = {"units": {"IN": "rb_inch", "MM": "rb_mm"},
|
||||||
"gerber_gaps": {"tb": "rb_app_2tb", "lr": "rb_app_2lr", "4": "rb_app_4"}}
|
"gerber_gaps": {"tb": "rb_app_2tb", "lr": "rb_app_2lr", "4": "rb_app_4"}}
|
||||||
|
|
||||||
# self.combos = []
|
|
||||||
|
|
||||||
# Options for each kind of FlatCAMObj.
|
# Options for each kind of FlatCAMObj.
|
||||||
# Example: 'gerber_plot': 'cb'. The widget name would be: 'cb_app_gerber_plot'
|
# Example: 'gerber_plot': 'cb'. The widget name would be: 'cb_app_gerber_plot'
|
||||||
for FlatCAMClass in [FlatCAMExcellon, FlatCAMGeometry, FlatCAMGerber, FlatCAMCNCjob]:
|
for FlatCAMClass in [FlatCAMExcellon, FlatCAMGeometry, FlatCAMGerber, FlatCAMCNCjob]:
|
||||||
@@ -677,22 +689,34 @@ class App:
|
|||||||
|
|
||||||
self.plot_click_subscribers = {}
|
self.plot_click_subscribers = {}
|
||||||
|
|
||||||
# Initialization
|
#### Initialization ####
|
||||||
self.load_defaults()
|
self.load_defaults()
|
||||||
self.options.update(self.defaults) # Copy app defaults to project options
|
self.options.update(self.defaults) # Copy app defaults to project options
|
||||||
self.options2form() # Populate the app defaults form
|
self.options2form() # Populate the app defaults form
|
||||||
self.units_label.set_text("[" + self.options["units"] + "]")
|
self.units_label.set_text("[" + self.options["units"] + "]")
|
||||||
|
|
||||||
# For debugging only
|
#### Check for updates ####
|
||||||
|
self.version = 1
|
||||||
|
t1 = threading.Thread(target=self.versionCheck)
|
||||||
|
t1.daemon = True
|
||||||
|
t1.start()
|
||||||
|
|
||||||
|
#### For debugging only ###
|
||||||
def someThreadFunc(self):
|
def someThreadFunc(self):
|
||||||
print "Hello World!"
|
print "Hello World!"
|
||||||
|
|
||||||
t = threading.Thread(target=someThreadFunc, args=(self,))
|
t = threading.Thread(target=someThreadFunc, args=(self,))
|
||||||
|
t.daemon = True
|
||||||
t.start()
|
t.start()
|
||||||
|
|
||||||
########################################
|
########################################
|
||||||
## START ##
|
## START ##
|
||||||
########################################
|
########################################
|
||||||
|
self.icon256 = GdkPixbuf.Pixbuf.new_from_file('share/flatcam_icon256.png')
|
||||||
|
self.icon48 = GdkPixbuf.Pixbuf.new_from_file('share/flatcam_icon48.png')
|
||||||
|
self.icon16 = GdkPixbuf.Pixbuf.new_from_file('share/flatcam_icon16.png')
|
||||||
|
Gtk.Window.set_default_icon_list([self.icon16, self.icon48, self.icon256])
|
||||||
|
self.window.set_title("FlatCAM - Alpha 2 UNSTABLE - Check for updates!")
|
||||||
self.window.set_default_size(900, 600)
|
self.window.set_default_size(900, 600)
|
||||||
self.window.show_all()
|
self.window.show_all()
|
||||||
|
|
||||||
@@ -1309,6 +1333,67 @@ class App:
|
|||||||
for obj in self.stuff:
|
for obj in self.stuff:
|
||||||
combo.append_text(obj)
|
combo.append_text(obj)
|
||||||
|
|
||||||
|
def versionCheck(self):
|
||||||
|
"""
|
||||||
|
Checks for the latest version of the program. Alerts the
|
||||||
|
user if theirs is outdated. This method is meant to be run
|
||||||
|
in a saeparate thread.
|
||||||
|
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
f = urllib.urlopen("http://caram.cl/flatcam/VERSION") # TODO: Hardcoded.
|
||||||
|
except:
|
||||||
|
GLib.idle_add(lambda: self.info("ERROR trying to check for latest version."))
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
data = json.load(f)
|
||||||
|
except:
|
||||||
|
GLib.idle_add(lambda: self.info("ERROR trying to check for latest version."))
|
||||||
|
f.close()
|
||||||
|
return
|
||||||
|
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
if self.version >= data["version"]:
|
||||||
|
GLib.idle_add(lambda: self.info("FlatCAM is up to date!"))
|
||||||
|
return
|
||||||
|
|
||||||
|
label = Gtk.Label("There is a newer version of FlatCAM\n" +
|
||||||
|
"available for download:\n\n" +
|
||||||
|
data["name"] + "\n\n" + data["message"])
|
||||||
|
dialog = Gtk.Dialog("Newer Version Available", self.window, 0,
|
||||||
|
(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
|
||||||
|
Gtk.STOCK_OK, Gtk.ResponseType.OK))
|
||||||
|
dialog.set_default_size(150, 100)
|
||||||
|
dialog.set_modal(True)
|
||||||
|
box = dialog.get_content_area()
|
||||||
|
box.set_border_width(10)
|
||||||
|
box.add(label)
|
||||||
|
|
||||||
|
def do_dialog():
|
||||||
|
dialog.show_all()
|
||||||
|
response = dialog.run()
|
||||||
|
dialog.destroy()
|
||||||
|
|
||||||
|
GLib.idle_add(lambda: do_dialog())
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
def setup_tooltips(self):
|
||||||
|
tooltips = {
|
||||||
|
"cb_gerber_plot": "Plot this object on the main window.",
|
||||||
|
"cb_gerber_mergepolys": "Show overlapping polygons as single.",
|
||||||
|
"cb_gerber_solid": "Paint inside polygons.",
|
||||||
|
"cb_gerber_multicolored": "Draw polygons with different polygons.",
|
||||||
|
"button1": ""
|
||||||
|
}
|
||||||
|
|
||||||
|
for widget in tooltips:
|
||||||
|
self.builder.get_object(widget).set_tooltip_markup(tooltips[widget])
|
||||||
|
|
||||||
########################################
|
########################################
|
||||||
## EVENT HANDLERS ##
|
## EVENT HANDLERS ##
|
||||||
########################################
|
########################################
|
||||||
|
|||||||
30
FlatCAM.ui
30
FlatCAM.ui
@@ -3045,7 +3045,8 @@ to application defaults.</property>
|
|||||||
<object class="GtkToolButton" id="zoomfit_toolbutton">
|
<object class="GtkToolButton" id="zoomfit_toolbutton">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
<property name="tooltip_text" translatable="yes">Zoom Fit</property>
|
<property name="tooltip_markup" translatable="yes">Zoom Fit.
|
||||||
|
(Click on plot and hit <b>1</b>)</property>
|
||||||
<property name="label" translatable="yes">Fit</property>
|
<property name="label" translatable="yes">Fit</property>
|
||||||
<property name="use_underline">True</property>
|
<property name="use_underline">True</property>
|
||||||
<property name="stock_id">gtk-zoom-100</property>
|
<property name="stock_id">gtk-zoom-100</property>
|
||||||
@@ -3060,7 +3061,9 @@ to application defaults.</property>
|
|||||||
<object class="GtkToolButton" id="zoomin_toolbutton">
|
<object class="GtkToolButton" id="zoomin_toolbutton">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
<property name="tooltip_text" translatable="yes">Zoom+</property>
|
<property name="tooltip_markup" translatable="yes">Zoom in.
|
||||||
|
(Click on plot and hit <b>3</b>
|
||||||
|
to zoom around a point)</property>
|
||||||
<property name="label" translatable="yes">Zoom+</property>
|
<property name="label" translatable="yes">Zoom+</property>
|
||||||
<property name="use_underline">True</property>
|
<property name="use_underline">True</property>
|
||||||
<property name="stock_id">gtk-zoom-in</property>
|
<property name="stock_id">gtk-zoom-in</property>
|
||||||
@@ -3075,7 +3078,9 @@ to application defaults.</property>
|
|||||||
<object class="GtkToolButton" id="zoomout_toolbutton">
|
<object class="GtkToolButton" id="zoomout_toolbutton">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
<property name="tooltip_text" translatable="yes">Zoom-</property>
|
<property name="tooltip_markup" translatable="yes">Zoom Out.
|
||||||
|
(Click on plot and hit <b>2</b>
|
||||||
|
to zoom around a point)</property>
|
||||||
<property name="label" translatable="yes">Zoom-</property>
|
<property name="label" translatable="yes">Zoom-</property>
|
||||||
<property name="use_underline">True</property>
|
<property name="use_underline">True</property>
|
||||||
<property name="stock_id">gtk-zoom-out</property>
|
<property name="stock_id">gtk-zoom-out</property>
|
||||||
@@ -3090,6 +3095,7 @@ to application defaults.</property>
|
|||||||
<object class="GtkToolButton" id="Clear">
|
<object class="GtkToolButton" id="Clear">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
|
<property name="tooltip_markup" translatable="yes">Clear Plot</property>
|
||||||
<property name="label" translatable="yes">Clear Plots</property>
|
<property name="label" translatable="yes">Clear Plots</property>
|
||||||
<property name="use_underline">True</property>
|
<property name="use_underline">True</property>
|
||||||
<property name="stock_id">gtk-stop</property>
|
<property name="stock_id">gtk-stop</property>
|
||||||
@@ -3104,6 +3110,7 @@ to application defaults.</property>
|
|||||||
<object class="GtkToolButton" id="toolbutton1">
|
<object class="GtkToolButton" id="toolbutton1">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
|
<property name="tooltip_markup" translatable="yes">Re-plot all</property>
|
||||||
<property name="label" translatable="yes">Re-plot</property>
|
<property name="label" translatable="yes">Re-plot</property>
|
||||||
<property name="use_underline">True</property>
|
<property name="use_underline">True</property>
|
||||||
<property name="stock_id">gtk-redo</property>
|
<property name="stock_id">gtk-redo</property>
|
||||||
@@ -3118,6 +3125,8 @@ to application defaults.</property>
|
|||||||
<object class="GtkToolButton" id="toolbutton2">
|
<object class="GtkToolButton" id="toolbutton2">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
|
<property name="tooltip_markup" translatable="yes">Delete selected
|
||||||
|
object.</property>
|
||||||
<property name="label" translatable="yes">Delete Object</property>
|
<property name="label" translatable="yes">Delete Object</property>
|
||||||
<property name="use_underline">True</property>
|
<property name="use_underline">True</property>
|
||||||
<property name="stock_id">gtk-delete</property>
|
<property name="stock_id">gtk-delete</property>
|
||||||
@@ -3165,6 +3174,7 @@ to application defaults.</property>
|
|||||||
<object class="GtkLabel" id="label1">
|
<object class="GtkLabel" id="label1">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
|
<property name="tooltip_markup" translatable="yes">Objects in the project.</property>
|
||||||
<property name="label" translatable="yes">Project</property>
|
<property name="label" translatable="yes">Project</property>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
@@ -3190,6 +3200,8 @@ to application defaults.</property>
|
|||||||
<object class="GtkLabel" id="label2">
|
<object class="GtkLabel" id="label2">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
|
<property name="tooltip_markup" translatable="yes">Options and action
|
||||||
|
for the current object.</property>
|
||||||
<property name="label" translatable="yes">Selected</property>
|
<property name="label" translatable="yes">Selected</property>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
@@ -3229,6 +3241,15 @@ to application defaults.</property>
|
|||||||
<object class="GtkComboBoxText" id="combo_options">
|
<object class="GtkComboBoxText" id="combo_options">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
|
<property name="tooltip_markup" translatable="yes">Application defaults get transfered
|
||||||
|
to every new project. Project options
|
||||||
|
get inherited by new project objects.
|
||||||
|
|
||||||
|
<b>Save</b> application defaults
|
||||||
|
by choosing <i>File + Save defaults</i>.
|
||||||
|
|
||||||
|
Project obtions are saved with the
|
||||||
|
project.</property>
|
||||||
<property name="margin_left">10</property>
|
<property name="margin_left">10</property>
|
||||||
<property name="margin_right">10</property>
|
<property name="margin_right">10</property>
|
||||||
<property name="margin_top">5</property>
|
<property name="margin_top">5</property>
|
||||||
@@ -4444,6 +4465,8 @@ to application defaults.</property>
|
|||||||
<object class="GtkLabel" id="label5">
|
<object class="GtkLabel" id="label5">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
|
<property name="tooltip_markup" translatable="yes">Project and application
|
||||||
|
defaults.</property>
|
||||||
<property name="label" translatable="yes">Options</property>
|
<property name="label" translatable="yes">Options</property>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
@@ -4468,6 +4491,7 @@ to application defaults.</property>
|
|||||||
<object class="GtkLabel" id="label88">
|
<object class="GtkLabel" id="label88">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
|
<property name="tooltip_markup" translatable="yes">Active tool</property>
|
||||||
<property name="label" translatable="yes">Tool</property>
|
<property name="label" translatable="yes">Tool</property>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
|
|||||||
400
camlib.py
400
camlib.py
@@ -348,6 +348,9 @@ class Gerber (Geometry):
|
|||||||
# LP - Level polarity
|
# LP - Level polarity
|
||||||
self.lpol_re = re.compile(r'^%LP([DC])\*%$')
|
self.lpol_re = re.compile(r'^%LP([DC])\*%$')
|
||||||
|
|
||||||
|
# TODO: This is bad.
|
||||||
|
self.steps_per_circ = 40
|
||||||
|
|
||||||
def scale(self, factor):
|
def scale(self, factor):
|
||||||
"""
|
"""
|
||||||
Scales the objects' geometry on the XY plane by a given factor.
|
Scales the objects' geometry on the XY plane by a given factor.
|
||||||
@@ -453,8 +456,12 @@ class Gerber (Geometry):
|
|||||||
|
|
||||||
self.buffered_paths = []
|
self.buffered_paths = []
|
||||||
for path in self.paths:
|
for path in self.paths:
|
||||||
width = self.apertures[path["aperture"]]["size"]
|
try:
|
||||||
self.buffered_paths.append(path["linestring"].buffer(width/2))
|
width = self.apertures[path["aperture"]]["size"]
|
||||||
|
self.buffered_paths.append(path["linestring"].buffer(width/2))
|
||||||
|
except KeyError:
|
||||||
|
print "ERROR: Failed to buffer path: ", path
|
||||||
|
print "Apertures: ", self.apertures
|
||||||
|
|
||||||
def aperture_parse(self, gline):
|
def aperture_parse(self, gline):
|
||||||
"""
|
"""
|
||||||
@@ -523,7 +530,7 @@ class Gerber (Geometry):
|
|||||||
:rtype: None
|
:rtype: None
|
||||||
"""
|
"""
|
||||||
|
|
||||||
path = [] # Coordinates of the current path
|
path = [] # Coordinates of the current path, each is [x, y]
|
||||||
|
|
||||||
last_path_aperture = None
|
last_path_aperture = None
|
||||||
current_aperture = None
|
current_aperture = None
|
||||||
@@ -540,11 +547,25 @@ class Gerber (Geometry):
|
|||||||
current_x = None
|
current_x = None
|
||||||
current_y = None
|
current_y = None
|
||||||
|
|
||||||
for gline in glines:
|
# How to interprest circular interpolation: SINGLE or MULTI
|
||||||
|
quadrant_mode = None
|
||||||
|
|
||||||
# Linear interpolation plus flashes
|
line_num = 0
|
||||||
|
for gline in glines:
|
||||||
|
line_num += 1
|
||||||
|
|
||||||
|
## G01 - Linear interpolation plus flashes
|
||||||
|
# Operation code (D0x) missing is deprecated... oh well I will support it.
|
||||||
match = self.lin_re.search(gline)
|
match = self.lin_re.search(gline)
|
||||||
if match:
|
if match:
|
||||||
|
# Dxx alone? Will ignore for now.
|
||||||
|
if match.group(1) is None and match.group(2) is None and match.group(3) is None:
|
||||||
|
try:
|
||||||
|
current_operation_code = int(match.group(4))
|
||||||
|
except:
|
||||||
|
pass # A line with just * will match too.
|
||||||
|
continue
|
||||||
|
|
||||||
# Parse coordinates
|
# Parse coordinates
|
||||||
if match.group(2) is not None:
|
if match.group(2) is not None:
|
||||||
current_x = parse_gerber_number(match.group(2), self.frac_digits)
|
current_x = parse_gerber_number(match.group(2), self.frac_digits)
|
||||||
@@ -553,69 +574,141 @@ class Gerber (Geometry):
|
|||||||
|
|
||||||
# Parse operation code
|
# Parse operation code
|
||||||
if match.group(4) is not None:
|
if match.group(4) is not None:
|
||||||
current_operation_code = match.group(4)
|
current_operation_code = int(match.group(4))
|
||||||
|
|
||||||
# Pen down: add segment
|
# Pen down: add segment
|
||||||
if current_operation_code == '1':
|
if current_operation_code == 1:
|
||||||
path.append([current_x, current_y])
|
path.append([current_x, current_y])
|
||||||
last_path_aperture = current_aperture
|
last_path_aperture = current_aperture
|
||||||
|
|
||||||
# Pen up: finish path
|
# Pen up: finish path
|
||||||
elif current_operation_code == '2':
|
elif current_operation_code == 2:
|
||||||
if len(path) > 1:
|
if len(path) > 1:
|
||||||
|
if last_path_aperture is None:
|
||||||
|
print "Warning: No aperture defined for curent path. (%d)" % line_num
|
||||||
self.paths.append({"linestring": LineString(path),
|
self.paths.append({"linestring": LineString(path),
|
||||||
"aperture": last_path_aperture})
|
"aperture": last_path_aperture})
|
||||||
path = [[current_x, current_y]]
|
path = [[current_x, current_y]] # Start new path
|
||||||
|
|
||||||
# Flash
|
# Flash
|
||||||
elif current_operation_code == '3':
|
elif current_operation_code == 3:
|
||||||
self.flashes.append({"loc": [current_x, current_y],
|
self.flashes.append({"loc": [current_x, current_y],
|
||||||
"aperture": current_aperture})
|
"aperture": current_aperture})
|
||||||
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# if gline.find("D01*") != -1: # pen down
|
## G02/3 - Circular interpolation
|
||||||
# path.append(parse_gerber_coords(gline, self.int_digits, self.frac_digits))
|
# 2-clockwise, 3-counterclockwise
|
||||||
# last_path_aperture = current_aperture
|
match = self.circ_re.search(gline)
|
||||||
# continue
|
if match:
|
||||||
#
|
|
||||||
# if gline.find("D02*") != -1: # pen up
|
|
||||||
# if len(path) > 1:
|
|
||||||
# # Path completed, create shapely LineString
|
|
||||||
# self.paths.append({"linestring": LineString(path),
|
|
||||||
# "aperture": last_path_aperture})
|
|
||||||
# path = [parse_gerber_coords(gline, self.int_digits, self.frac_digits)]
|
|
||||||
# continue
|
|
||||||
#
|
|
||||||
# indexd3 = gline.find("D03*")
|
|
||||||
# if indexd3 > 0: # Flash
|
|
||||||
# self.flashes.append({"loc": parse_gerber_coords(gline, self.int_digits, self.frac_digits),
|
|
||||||
# "aperture": current_aperture})
|
|
||||||
# continue
|
|
||||||
# if indexd3 == 0: # Flash?
|
|
||||||
# print "WARNING: Uninplemented flash style:", gline
|
|
||||||
# continue
|
|
||||||
|
|
||||||
# End region
|
mode, x, y, i, j, d = match.groups()
|
||||||
if self.regionoff_re.search(gline):
|
try:
|
||||||
# Only one path defines region?
|
x = parse_gerber_number(x, self.frac_digits)
|
||||||
self.regions.append({"polygon": Polygon(path),
|
except:
|
||||||
"aperture": last_path_aperture})
|
x = current_x
|
||||||
path = []
|
try:
|
||||||
|
y = parse_gerber_number(y, self.frac_digits)
|
||||||
|
except:
|
||||||
|
y = current_y
|
||||||
|
try:
|
||||||
|
i = parse_gerber_number(i, self.frac_digits)
|
||||||
|
except:
|
||||||
|
i = 0
|
||||||
|
try:
|
||||||
|
j = parse_gerber_number(j, self.frac_digits)
|
||||||
|
except:
|
||||||
|
j = 0
|
||||||
|
|
||||||
|
if quadrant_mode is None:
|
||||||
|
print "ERROR: Found arc without preceding quadrant specification G74 or G75. (%d)" % line_num
|
||||||
|
print gline
|
||||||
|
continue
|
||||||
|
|
||||||
|
if mode is None and current_interpolation_mode not in [2, 3]:
|
||||||
|
print "ERROR: Found arc without circular interpolation mode defined. (%d)" % line_num
|
||||||
|
print gline
|
||||||
|
continue
|
||||||
|
elif mode is not None:
|
||||||
|
current_interpolation_mode = int(mode)
|
||||||
|
|
||||||
|
# Set operation code if provided
|
||||||
|
if d is not None:
|
||||||
|
current_operation_code = int(d)
|
||||||
|
|
||||||
|
# Nothing created! Pen Up.
|
||||||
|
if current_operation_code == 2:
|
||||||
|
print "Warning: Arc with D2. (%d)" % line_num
|
||||||
|
if len(path) > 1:
|
||||||
|
if last_path_aperture is None:
|
||||||
|
print "Warning: No aperture defined for curent path. (%d)" % line_num
|
||||||
|
self.paths.append({"linestring": LineString(path),
|
||||||
|
"aperture": last_path_aperture})
|
||||||
|
current_x = x
|
||||||
|
current_y = y
|
||||||
|
path = [[current_x, current_y]] # Start new path
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Flash should not happen here
|
||||||
|
if current_operation_code == 3:
|
||||||
|
print "ERROR: Trying to flash within arc. (%d)" % line_num
|
||||||
|
continue
|
||||||
|
|
||||||
|
if quadrant_mode == 'MULTI':
|
||||||
|
center = [i + current_x, j + current_y]
|
||||||
|
radius = sqrt(i**2 + j**2)
|
||||||
|
start = arctan2(-j, -i)
|
||||||
|
stop = arctan2(-center[1] + y, -center[0] + x)
|
||||||
|
arcdir = [None, None, "cw", "ccw"]
|
||||||
|
this_arc = arc(center, radius, start, stop,
|
||||||
|
arcdir[current_interpolation_mode],
|
||||||
|
self.steps_per_circ)
|
||||||
|
|
||||||
|
# Last point in path is current point
|
||||||
|
current_x = this_arc[-1][0]
|
||||||
|
current_y = this_arc[-1][1]
|
||||||
|
|
||||||
|
# Append
|
||||||
|
path += this_arc
|
||||||
|
|
||||||
|
last_path_aperture = current_aperture
|
||||||
|
|
||||||
|
continue
|
||||||
|
|
||||||
|
if quadrant_mode == 'SINGLE':
|
||||||
|
print "Warning: Single quadrant arc are not implemented yet. (%d)" % line_num
|
||||||
|
|
||||||
|
## G74/75* - Single or multiple quadrant arcs
|
||||||
|
match = self.quad_re.search(gline)
|
||||||
|
if match:
|
||||||
|
if match.group(1) == '4':
|
||||||
|
quadrant_mode = 'SINGLE'
|
||||||
|
else:
|
||||||
|
quadrant_mode = 'MULTI'
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# if gline.find("G37*") != -1: # end region
|
## G37* - End region
|
||||||
# # Only one path defines region?
|
if self.regionoff_re.search(gline):
|
||||||
# self.regions.append({"polygon": Polygon(path),
|
# Only one path defines region?
|
||||||
# "aperture": last_path_aperture})
|
if len(path) < 3:
|
||||||
# path = []
|
print "ERROR: Path contains less than 3 points:"
|
||||||
# continue
|
print path
|
||||||
|
print "Line (%d): " % line_num, gline
|
||||||
|
path = []
|
||||||
|
continue
|
||||||
|
|
||||||
|
# For regions we may ignore an aperture that is None
|
||||||
|
self.regions.append({"polygon": Polygon(path),
|
||||||
|
"aperture": last_path_aperture})
|
||||||
|
#path = []
|
||||||
|
path = [[current_x, current_y]] # Start new path
|
||||||
|
continue
|
||||||
|
|
||||||
if gline.find("%ADD") != -1: # aperture definition
|
if gline.find("%ADD") != -1: # aperture definition
|
||||||
self.aperture_parse(gline) # adds element to apertures
|
self.aperture_parse(gline) # adds element to apertures
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Interpolation mode change
|
## G01/2/3* - Interpolation mode change
|
||||||
# Can occur along with coordinates and operation code but
|
# Can occur along with coordinates and operation code but
|
||||||
# sometimes by itself (handled here).
|
# sometimes by itself (handled here).
|
||||||
# Example: G01*
|
# Example: G01*
|
||||||
@@ -624,22 +717,15 @@ class Gerber (Geometry):
|
|||||||
current_interpolation_mode = int(match.group(1))
|
current_interpolation_mode = int(match.group(1))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Tool/aperture change
|
## Tool/aperture change
|
||||||
# Example: D12*
|
# Example: D12*
|
||||||
match = self.tool_re.search(gline)
|
match = self.tool_re.search(gline)
|
||||||
if match:
|
if match:
|
||||||
current_aperture = match.group(1)
|
current_aperture = match.group(1)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# indexstar = gline.find("*")
|
## Number format
|
||||||
# if gline.find("D") == 0: # Aperture change
|
# Example: %FSLAX24Y24*%
|
||||||
# current_aperture = gline[1:indexstar]
|
|
||||||
# continue
|
|
||||||
# if gline.find("G54D") == 0: # Aperture change (deprecated)
|
|
||||||
# current_aperture = gline[4:indexstar]
|
|
||||||
# continue
|
|
||||||
|
|
||||||
# Number format
|
|
||||||
# TODO: This is ignoring most of the format. Implement the rest.
|
# TODO: This is ignoring most of the format. Implement the rest.
|
||||||
match = self.fmt_re.search(gline)
|
match = self.fmt_re.search(gline)
|
||||||
if match:
|
if match:
|
||||||
@@ -647,30 +733,20 @@ class Gerber (Geometry):
|
|||||||
self.frac_digits = int(match.group(4))
|
self.frac_digits = int(match.group(4))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# if gline.find("%FS") != -1: # Format statement
|
## Mode (IN/MM)
|
||||||
# indexx = gline.find("X")
|
# Example: %MOIN*%
|
||||||
# self.int_digits = int(gline[indexx + 1])
|
|
||||||
# self.frac_digits = int(gline[indexx + 2])
|
|
||||||
# continue
|
|
||||||
|
|
||||||
# Mode (IN/MM)
|
|
||||||
match = self.mode_re.search(gline)
|
match = self.mode_re.search(gline)
|
||||||
if match:
|
if match:
|
||||||
self.units = match.group(1)
|
self.units = match.group(1)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
print "WARNING: Line ignored:", gline
|
print "WARNING: Line ignored (%d):" % line_num, gline
|
||||||
|
|
||||||
if len(path) > 1:
|
if len(path) > 1:
|
||||||
# EOF, create shapely LineString if something still in path
|
# EOF, create shapely LineString if something still in path
|
||||||
self.paths.append({"linestring": LineString(path),
|
self.paths.append({"linestring": LineString(path),
|
||||||
"aperture": last_path_aperture})
|
"aperture": last_path_aperture})
|
||||||
|
|
||||||
# if len(path) > 1:
|
|
||||||
# # EOF, create shapely LineString if something still in path
|
|
||||||
# self.paths.append({"linestring": LineString(path),
|
|
||||||
# "aperture": current_aperture})
|
|
||||||
|
|
||||||
def do_flashes(self):
|
def do_flashes(self):
|
||||||
"""
|
"""
|
||||||
Creates geometry for Gerber flashes (aperture on a single point).
|
Creates geometry for Gerber flashes (aperture on a single point).
|
||||||
@@ -778,6 +854,7 @@ class Excellon(Geometry):
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
"""
|
"""
|
||||||
The constructor takes no parameters.
|
The constructor takes no parameters.
|
||||||
|
|
||||||
:return: Excellon object.
|
:return: Excellon object.
|
||||||
:rtype: Excellon
|
:rtype: Excellon
|
||||||
"""
|
"""
|
||||||
@@ -795,8 +872,74 @@ class Excellon(Geometry):
|
|||||||
# Always append to it because it carries contents
|
# Always append to it because it carries contents
|
||||||
# from Geometry.
|
# from Geometry.
|
||||||
self.ser_attrs += ['tools', 'drills', 'zeros']
|
self.ser_attrs += ['tools', 'drills', 'zeros']
|
||||||
|
|
||||||
|
#### Patterns ####
|
||||||
|
# Regex basics:
|
||||||
|
# ^ - beginning
|
||||||
|
# $ - end
|
||||||
|
# *: 0 or more, +: 1 or more, ?: 0 or 1
|
||||||
|
|
||||||
|
# M48 - Beggining of Part Program Header
|
||||||
|
self.hbegin_re = re.compile(r'^M48$')
|
||||||
|
|
||||||
|
# M95 or % - End of Part Program Header
|
||||||
|
# NOTE: % has different meaning in the body
|
||||||
|
self.hend_re = re.compile(r'^(?:M95|%)$')
|
||||||
|
|
||||||
|
# FMAT Excellon format
|
||||||
|
self.fmat_re = re.compile(r'^FMAT,([12])$')
|
||||||
|
|
||||||
|
# Number format and units
|
||||||
|
# INCH uses 6 digits
|
||||||
|
# METRIC uses 5/6
|
||||||
|
self.units_re = re.compile(r'^(INCH|METRIC)(?:,([TL])Z)?$')
|
||||||
|
|
||||||
|
# Tool definition/parameters (?= is look-ahead
|
||||||
|
# NOTE: This might be an overkill!
|
||||||
|
self.toolset_re = re.compile(r'^T(0?\d|\d\d)(?=.*C(\d*\.?\d*))?' +
|
||||||
|
r'(?=.*F(\d*\.?\d*))?(?=.*S(\d*\.?\d*))?' +
|
||||||
|
r'(?=.*B(\d*\.?\d*))?(?=.*H(\d*\.?\d*))?' +
|
||||||
|
r'(?=.*Z(-?\d*\.?\d*))?[CFSBHT]')
|
||||||
|
|
||||||
|
# Tool select
|
||||||
|
# Can have additional data after tool number but
|
||||||
|
# is ignored if present in the header.
|
||||||
|
# Warning: This will match toolset_re too.
|
||||||
|
self.toolsel_re = re.compile(r'^T((?:\d\d)|(?:\d))')
|
||||||
|
|
||||||
|
# Comment
|
||||||
|
self.comm_re = re.compile(r'^;(.*)$')
|
||||||
|
|
||||||
|
# Absolute/Incremental G90/G91
|
||||||
|
self.absinc_re = re.compile(r'^G9([01])$')
|
||||||
|
|
||||||
|
# Modes of operation
|
||||||
|
# 1-linear, 2-circCW, 3-cirCCW, 4-vardwell, 5-Drill
|
||||||
|
self.modes_re = re.compile(r'^G0([012345])')
|
||||||
|
|
||||||
|
# Measuring mode
|
||||||
|
# 1-metric, 2-inch
|
||||||
|
self.meas_re = re.compile(r'^M7([12])$')
|
||||||
|
|
||||||
|
# Coordinates
|
||||||
|
self.xcoord_re = re.compile(r'^X(\d*\.?\d*)(?:Y\d*\.?\d*)?$')
|
||||||
|
self.ycoord_re = re.compile(r'^(?:X\d*\.?\d*)?Y(\d*\.?\d*)$')
|
||||||
|
|
||||||
|
# R - Repeat hole (# times, X offset, Y offset)
|
||||||
|
self.rep_re = re.compile(r'^R(\d+)(?=.*[XY])+(?:X(\d*\.?\d*))?(?:Y(\d*\.?\d*))?$')
|
||||||
|
|
||||||
|
# Various stop/pause commands
|
||||||
|
self.stop_re = re.compile(r'^((G04)|(M09)|(M06)|(M00)|(M30))')
|
||||||
|
|
||||||
def parse_file(self, filename):
|
def parse_file(self, filename):
|
||||||
|
"""
|
||||||
|
Reads the specified file as array of lines as
|
||||||
|
passes it to ``parse_lines()``.
|
||||||
|
|
||||||
|
:param filename: The file to be read and parsed.
|
||||||
|
:type filename: str
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
efile = open(filename, 'r')
|
efile = open(filename, 'r')
|
||||||
estr = efile.readlines()
|
estr = efile.readlines()
|
||||||
efile.close()
|
efile.close()
|
||||||
@@ -805,71 +948,79 @@ class Excellon(Geometry):
|
|||||||
def parse_lines(self, elines):
|
def parse_lines(self, elines):
|
||||||
"""
|
"""
|
||||||
Main Excellon parser.
|
Main Excellon parser.
|
||||||
|
|
||||||
|
:param elines: List of strings, each being a line of Excellon code.
|
||||||
|
:type elines: list
|
||||||
|
:return: None
|
||||||
"""
|
"""
|
||||||
units_re = re.compile(r'^(INCH|METRIC)(?:,([TL])Z)?$')
|
|
||||||
|
|
||||||
current_tool = ""
|
current_tool = ""
|
||||||
|
in_header = False
|
||||||
|
|
||||||
for eline in elines:
|
for eline in elines:
|
||||||
|
|
||||||
## Tool definitions ##
|
## Header Begin/End ##
|
||||||
# TODO: Verify all this
|
if self.hbegin_re.search(eline):
|
||||||
indexT = eline.find("T")
|
in_header = True
|
||||||
indexC = eline.find("C")
|
|
||||||
indexF = eline.find("F")
|
|
||||||
# Type 1
|
|
||||||
if indexT != -1 and indexC > indexT and indexF > indexC:
|
|
||||||
tool = eline[1:indexC]
|
|
||||||
spec = eline[indexC+1:indexF]
|
|
||||||
self.tools[tool] = float(spec)
|
|
||||||
continue
|
|
||||||
# Type 2
|
|
||||||
# TODO: Is this inches?
|
|
||||||
#indexsp = eline.find(" ")
|
|
||||||
#indexin = eline.find("in")
|
|
||||||
#if indexT != -1 and indexsp > indexT and indexin > indexsp:
|
|
||||||
# tool = eline[1:indexsp]
|
|
||||||
# spec = eline[indexsp+1:indexin]
|
|
||||||
# self.tools[tool] = spec
|
|
||||||
# continue
|
|
||||||
# Type 3
|
|
||||||
if indexT != -1 and indexC > indexT:
|
|
||||||
tool = eline[1:indexC]
|
|
||||||
spec = eline[indexC+1:-1]
|
|
||||||
self.tools[tool] = float(spec)
|
|
||||||
continue
|
|
||||||
|
|
||||||
## Tool change
|
|
||||||
if indexT == 0:
|
|
||||||
current_tool = eline[1:-1]
|
|
||||||
continue
|
|
||||||
|
|
||||||
## Drill
|
|
||||||
indexx = eline.find("X")
|
|
||||||
indexy = eline.find("Y")
|
|
||||||
if indexx != -1 and indexy != -1:
|
|
||||||
x = float(int(eline[indexx+1:indexy])/10000.0)
|
|
||||||
y = float(int(eline[indexy+1:-1])/10000.0)
|
|
||||||
self.drills.append({'point': Point((x, y)), 'tool': current_tool})
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Units and number format
|
if self.hend_re.search(eline):
|
||||||
match = units_re.match(eline)
|
in_header = False
|
||||||
if match:
|
continue
|
||||||
self.zeros = match.group(2) # "T" or "L"
|
|
||||||
self.units = {"INCH": "IN", "METRIC": "MM"}[match.group(1)]
|
#### Body ####
|
||||||
|
if not in_header:
|
||||||
|
|
||||||
|
## Tool change ##
|
||||||
|
match = self.toolsel_re.search(eline)
|
||||||
|
if match:
|
||||||
|
current_tool = str(int(match.group(1)))
|
||||||
|
continue
|
||||||
|
|
||||||
|
## Drill ##
|
||||||
|
indexx = eline.find("X")
|
||||||
|
indexy = eline.find("Y")
|
||||||
|
if indexx != -1 and indexy != -1:
|
||||||
|
x = float(int(eline[indexx+1:indexy])/10000.0)
|
||||||
|
y = float(int(eline[indexy+1:-1])/10000.0)
|
||||||
|
self.drills.append({'point': Point((x, y)), 'tool': current_tool})
|
||||||
|
continue
|
||||||
|
|
||||||
|
#### Header ####
|
||||||
|
if in_header:
|
||||||
|
|
||||||
|
## Tool definitions ##
|
||||||
|
match = self.toolset_re.search(eline)
|
||||||
|
if match:
|
||||||
|
name = str(int(match.group(1)))
|
||||||
|
spec = {
|
||||||
|
"C": float(match.group(2)),
|
||||||
|
# "F": float(match.group(3)),
|
||||||
|
# "S": float(match.group(4)),
|
||||||
|
# "B": float(match.group(5)),
|
||||||
|
# "H": float(match.group(6)),
|
||||||
|
# "Z": float(match.group(7))
|
||||||
|
}
|
||||||
|
self.tools[name] = spec
|
||||||
|
continue
|
||||||
|
|
||||||
|
## Units and number format ##
|
||||||
|
match = self.units_re.match(eline)
|
||||||
|
if match:
|
||||||
|
self.zeros = match.group(2) # "T" or "L"
|
||||||
|
self.units = {"INCH": "IN", "METRIC": "MM"}[match.group(1)]
|
||||||
|
continue
|
||||||
|
|
||||||
print "WARNING: Line ignored:", eline
|
print "WARNING: Line ignored:", eline
|
||||||
|
|
||||||
def create_geometry(self):
|
def create_geometry(self):
|
||||||
self.solid_geometry = []
|
self.solid_geometry = []
|
||||||
sizes = {}
|
|
||||||
for tool in self.tools:
|
|
||||||
sizes[tool] = float(self.tools[tool])
|
|
||||||
for drill in self.drills:
|
for drill in self.drills:
|
||||||
poly = Point(drill['point']).buffer(sizes[drill['tool']]/2.0)
|
poly = Point(drill['point']).buffer(self.tools[drill['tool']]["C"]/2.0)
|
||||||
self.solid_geometry.append(poly)
|
self.solid_geometry.append(poly)
|
||||||
self.solid_geometry = cascaded_union(self.solid_geometry)
|
|
||||||
|
#self.solid_geometry = cascaded_union(self.solid_geometry)
|
||||||
|
|
||||||
def scale(self, factor):
|
def scale(self, factor):
|
||||||
"""
|
"""
|
||||||
@@ -910,7 +1061,9 @@ class Excellon(Geometry):
|
|||||||
|
|
||||||
# Tools
|
# Tools
|
||||||
for tname in self.tools:
|
for tname in self.tools:
|
||||||
self.tools[tname] *= factor
|
self.tools[tname]["C"] *= factor
|
||||||
|
|
||||||
|
self.create_geometry()
|
||||||
|
|
||||||
return factor
|
return factor
|
||||||
|
|
||||||
@@ -1236,8 +1389,7 @@ class CNCjob(Geometry):
|
|||||||
arcdir = [None, None, "cw", "ccw"]
|
arcdir = [None, None, "cw", "ccw"]
|
||||||
if current['G'] in [0, 1]: # line
|
if current['G'] in [0, 1]: # line
|
||||||
path.append((x, y))
|
path.append((x, y))
|
||||||
# geometry.append({'geom': LineString([(current['X'], current['Y']),
|
|
||||||
# (x, y)]), 'kind': kind})
|
|
||||||
if current['G'] in [2, 3]: # arc
|
if current['G'] in [2, 3]: # arc
|
||||||
center = [gobj['I'] + current['X'], gobj['J'] + current['Y']]
|
center = [gobj['I'] + current['X'], gobj['J'] + current['Y']]
|
||||||
radius = sqrt(gobj['I']**2 + gobj['J']**2)
|
radius = sqrt(gobj['I']**2 + gobj['J']**2)
|
||||||
@@ -1246,10 +1398,6 @@ class CNCjob(Geometry):
|
|||||||
path += arc(center, radius, start, stop,
|
path += arc(center, radius, start, stop,
|
||||||
arcdir[current['G']],
|
arcdir[current['G']],
|
||||||
self.steps_per_circ)
|
self.steps_per_circ)
|
||||||
# geometry.append({'geom': arc(center, radius, start, stop,
|
|
||||||
# arcdir[current['G']],
|
|
||||||
# self.steps_per_circ),
|
|
||||||
# 'kind': kind})
|
|
||||||
|
|
||||||
# Update current instruction
|
# Update current instruction
|
||||||
for code in gobj:
|
for code in gobj:
|
||||||
|
|||||||
BIN
share/flatcam_icon16.png
Normal file
BIN
share/flatcam_icon16.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 336 B |
BIN
share/flatcam_icon256.png
Normal file
BIN
share/flatcam_icon256.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.8 KiB |
BIN
share/flatcam_icon32.png
Normal file
BIN
share/flatcam_icon32.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 603 B |
BIN
share/flatcam_icon48.png
Normal file
BIN
share/flatcam_icon48.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 833 B |
Reference in New Issue
Block a user