Double-sided PCB support.
This commit is contained in:
parent
e930de1793
commit
6284156a0d
209
FlatCAM.py
209
FlatCAM.py
|
@ -1,3 +1,9 @@
|
||||||
|
############################################################
|
||||||
|
# Author: Juan Pablo Caram #
|
||||||
|
# Date: 2/5/2014 #
|
||||||
|
# caram.cl #
|
||||||
|
############################################################
|
||||||
|
|
||||||
import threading
|
import threading
|
||||||
from gi.repository import Gtk, Gdk, GLib, GObject
|
from gi.repository import Gtk, Gdk, GLib, GObject
|
||||||
import simplejson as json
|
import simplejson as json
|
||||||
|
@ -255,7 +261,7 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
|
||||||
def plot(self, figure):
|
def plot(self, figure):
|
||||||
FlatCAMObj.plot(self, figure)
|
FlatCAMObj.plot(self, figure)
|
||||||
|
|
||||||
self.create_geometry()
|
#self.create_geometry()
|
||||||
|
|
||||||
if self.options["mergepolys"]:
|
if self.options["mergepolys"]:
|
||||||
geometry = self.solid_geometry
|
geometry = self.solid_geometry
|
||||||
|
@ -334,7 +340,7 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
|
||||||
def plot(self, figure):
|
def plot(self, figure):
|
||||||
FlatCAMObj.plot(self, figure)
|
FlatCAMObj.plot(self, figure)
|
||||||
#self.setup_axes(figure)
|
#self.setup_axes(figure)
|
||||||
self.create_geometry()
|
#self.create_geometry()
|
||||||
|
|
||||||
# Plot excellon
|
# Plot excellon
|
||||||
for geo in self.solid_geometry:
|
for geo in self.solid_geometry:
|
||||||
|
@ -572,6 +578,7 @@ class App:
|
||||||
self.setup_component_editor() # The "Selected" tab
|
self.setup_component_editor() # The "Selected" tab
|
||||||
|
|
||||||
#### DATA ####
|
#### DATA ####
|
||||||
|
self.clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
|
||||||
self.setup_obj_classes()
|
self.setup_obj_classes()
|
||||||
self.stuff = {} # FlatCAMObj's by name
|
self.stuff = {} # FlatCAMObj's by name
|
||||||
self.mouse = None # Mouse coordinates over plot
|
self.mouse = None # Mouse coordinates over plot
|
||||||
|
@ -1229,10 +1236,196 @@ class App:
|
||||||
|
|
||||||
self.info("Project loaded from: " + filename)
|
self.info("Project loaded from: " + filename)
|
||||||
|
|
||||||
|
def populate_objects_combo(self, combo):
|
||||||
|
"""
|
||||||
|
Populates a Gtk.Comboboxtext with the list of the object in the project.
|
||||||
|
|
||||||
|
:param combo: Name or instance of the comboboxtext.
|
||||||
|
:type combo: str or Gtk.ComboBoxText
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
|
print "Populating combo!"
|
||||||
|
if type(combo) == str:
|
||||||
|
combo = self.builder.get_object(combo)
|
||||||
|
|
||||||
|
combo.remove_all()
|
||||||
|
for obj in self.stuff:
|
||||||
|
combo.append_text(obj)
|
||||||
|
|
||||||
########################################
|
########################################
|
||||||
## EVENT HANDLERS ##
|
## EVENT HANDLERS ##
|
||||||
########################################
|
########################################
|
||||||
|
def on_create_mirror(self, widget):
|
||||||
|
"""
|
||||||
|
Creates a mirror image of a Gerber object to be used as a bottom
|
||||||
|
copper layer.
|
||||||
|
|
||||||
|
:param widget: Ignored.
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Layer to mirror
|
||||||
|
gerb_name = self.builder.get_object("comboboxtext_bottomlayer").get_active_text()
|
||||||
|
gerb = self.stuff[gerb_name]
|
||||||
|
|
||||||
|
# For now, lets limit to Gerbers.
|
||||||
|
assert isinstance(gerb, FlatCAMGerber)
|
||||||
|
|
||||||
|
# Mirror axis "X" or "Y
|
||||||
|
axis = self.get_radio_value({"rb_mirror_x": "X",
|
||||||
|
"rb_mirror_y": "Y"})
|
||||||
|
mode = self.get_radio_value({"rb_mirror_box": "box",
|
||||||
|
"rb_mirror_point": "point"})
|
||||||
|
if mode == "point": # A single point defines the mirror axis
|
||||||
|
# TODO: Error handling
|
||||||
|
px, py = eval(self.point_entry.get_text())
|
||||||
|
else: # The axis is the line dividing the box in the middle
|
||||||
|
name = self.box_combo.get_active_text()
|
||||||
|
bb_obj = self.stuff[name]
|
||||||
|
xmin, ymin, xmax, ymax = bb_obj.bounds()
|
||||||
|
px = 0.5*(xmin+xmax)
|
||||||
|
py = 0.5*(ymin+ymax)
|
||||||
|
|
||||||
|
# Do the mirroring
|
||||||
|
xscale, yscale = {"X": (1.0, -1.0), "Y": (-1.0, 1.0)}[axis]
|
||||||
|
mirrored = affinity.scale(gerb.solid_geometry, xscale, yscale, origin=(px, py))
|
||||||
|
|
||||||
|
def obj_init(obj_inst, app_inst):
|
||||||
|
obj_inst.solid_geometry = mirrored
|
||||||
|
|
||||||
|
self.new_object("gerber", gerb.options["name"] + "_mirror", obj_init)
|
||||||
|
|
||||||
|
def on_create_aligndrill(self, widget):
|
||||||
|
"""
|
||||||
|
Creates alignment holes Excellon object. Creates mirror duplicates
|
||||||
|
of the specified holes around the specified axis.
|
||||||
|
|
||||||
|
:param widget: Ignored.
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
|
# Mirror axis. Same as in on_create_mirror.
|
||||||
|
axis = self.get_radio_value({"rb_mirror_x": "X",
|
||||||
|
"rb_mirror_y": "Y"})
|
||||||
|
# TODO: Error handling
|
||||||
|
mode = self.get_radio_value({"rb_mirror_box": "box",
|
||||||
|
"rb_mirror_point": "point"})
|
||||||
|
if mode == "point":
|
||||||
|
px, py = eval(self.point_entry.get_text())
|
||||||
|
else:
|
||||||
|
name = self.box_combo.get_active_text()
|
||||||
|
bb_obj = self.stuff[name]
|
||||||
|
xmin, ymin, xmax, ymax = bb_obj.bounds()
|
||||||
|
px = 0.5*(xmin+xmax)
|
||||||
|
py = 0.5*(ymin+ymax)
|
||||||
|
xscale, yscale = {"X": (1.0, -1.0), "Y": (-1.0, 1.0)}[axis]
|
||||||
|
|
||||||
|
# Tools
|
||||||
|
tools = {"1": self.get_eval("entry_dblsided_alignholediam")}
|
||||||
|
|
||||||
|
# Parse hole list
|
||||||
|
# TODO: Better parsing
|
||||||
|
holes = self.builder.get_object("entry_dblsided_alignholes").get_text()
|
||||||
|
holes = eval("[" + holes + "]")
|
||||||
|
drills = []
|
||||||
|
for hole in holes:
|
||||||
|
point = Point(hole)
|
||||||
|
point_mirror = affinity.scale(point, xscale, yscale, origin=(px, py))
|
||||||
|
drills.append({"point": point, "tool": "1"})
|
||||||
|
drills.append({"point": point_mirror, "tool": "1"})
|
||||||
|
|
||||||
|
def obj_init(obj_inst, app_inst):
|
||||||
|
obj_inst.tools = tools
|
||||||
|
obj_inst.drills = drills
|
||||||
|
obj_inst.create_geometry()
|
||||||
|
|
||||||
|
self.new_object("excellon", "Alignment Drills", obj_init)
|
||||||
|
|
||||||
|
|
||||||
|
def on_toggle_pointbox(self, widget):
|
||||||
|
"""
|
||||||
|
Callback for radio selection change between point and box in the
|
||||||
|
Double-sided PCB tool. Updates the UI accordingly.
|
||||||
|
|
||||||
|
:param widget: Ignored.
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Where the entry or combo go
|
||||||
|
box = self.builder.get_object("box_pointbox")
|
||||||
|
|
||||||
|
# Clear contents
|
||||||
|
children = box.get_children()
|
||||||
|
for child in children:
|
||||||
|
box.remove(child)
|
||||||
|
|
||||||
|
choice = self.get_radio_value({"rb_mirror_point": "point",
|
||||||
|
"rb_mirror_box": "box"})
|
||||||
|
|
||||||
|
if choice == "point":
|
||||||
|
self.point_entry = Gtk.Entry()
|
||||||
|
self.builder.get_object("box_pointbox").pack_start(self.point_entry,
|
||||||
|
False, False, 1)
|
||||||
|
self.point_entry.show()
|
||||||
|
else:
|
||||||
|
self.box_combo = Gtk.ComboBoxText()
|
||||||
|
self.builder.get_object("box_pointbox").pack_start(self.box_combo,
|
||||||
|
False, False, 1)
|
||||||
|
self.populate_objects_combo(self.box_combo)
|
||||||
|
self.box_combo.show()
|
||||||
|
|
||||||
|
|
||||||
|
def on_tools_doublesided(self, param):
|
||||||
|
"""
|
||||||
|
Callback for menu item Tools->Double Sided PCB Tool. Launches the
|
||||||
|
tool placing its UI in the "Tool" tab in the notebook.
|
||||||
|
|
||||||
|
:param param: Ignored.
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Were are we drawing the UI
|
||||||
|
box_tool = self.builder.get_object("box_tool")
|
||||||
|
|
||||||
|
# Remove anything else in the box
|
||||||
|
box_children = box_tool.get_children()
|
||||||
|
for child in box_children:
|
||||||
|
box_tool.remove(child)
|
||||||
|
|
||||||
|
# Get the UI
|
||||||
|
osw = self.builder.get_object("offscreenwindow_dblsided")
|
||||||
|
sw = self.builder.get_object("sw_dblsided")
|
||||||
|
osw.remove(sw)
|
||||||
|
vp = self.builder.get_object("vp_dblsided")
|
||||||
|
vp.override_background_color(Gtk.StateType.NORMAL, Gdk.RGBA(1, 1, 1, 1))
|
||||||
|
|
||||||
|
# Put in the UI
|
||||||
|
box_tool.pack_start(sw, True, True, 0)
|
||||||
|
|
||||||
|
# INITIALIZATION
|
||||||
|
# Populate combo box
|
||||||
|
self.populate_objects_combo("comboboxtext_bottomlayer")
|
||||||
|
|
||||||
|
# Point entry
|
||||||
|
self.point_entry = Gtk.Entry()
|
||||||
|
box = self.builder.get_object("box_pointbox")
|
||||||
|
for child in box.get_children():
|
||||||
|
box.remove(child)
|
||||||
|
box.pack_start(self.point_entry, False, False, 1)
|
||||||
|
|
||||||
|
# Show the "Tool" tab
|
||||||
|
self.notebook.set_current_page(3)
|
||||||
|
sw.show_all()
|
||||||
|
|
||||||
def on_toggle_units(self, widget):
|
def on_toggle_units(self, widget):
|
||||||
|
"""
|
||||||
|
Callback for the Units radio-button change in the Options tab.
|
||||||
|
Changes the application's default units or the current project's units.
|
||||||
|
If changing the project's units, the change propagates to all of
|
||||||
|
the objects in the project.
|
||||||
|
|
||||||
|
:param widget: Ignored.
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
if self.toggle_units_ignore:
|
if self.toggle_units_ignore:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -1574,6 +1767,7 @@ class App:
|
||||||
:param widget: Ignored.
|
:param widget: Ignored.
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
|
# TODO: Use Gerber.get_bounding_box(...)
|
||||||
gerber = self.get_current()
|
gerber = self.get_current()
|
||||||
gerber.read_form()
|
gerber.read_form()
|
||||||
name = self.selected_item_name + "_bbox"
|
name = self.selected_item_name + "_bbox"
|
||||||
|
@ -2094,8 +2288,10 @@ class App:
|
||||||
GLib.idle_add(lambda: app_obj.set_progress_bar(0.1, "Opening Gerber ..."))
|
GLib.idle_add(lambda: app_obj.set_progress_bar(0.1, "Opening Gerber ..."))
|
||||||
|
|
||||||
def obj_init(gerber_obj, app_obj):
|
def obj_init(gerber_obj, app_obj):
|
||||||
|
assert isinstance(gerber_obj, FlatCAMGerber)
|
||||||
GLib.idle_add(lambda: app_obj.set_progress_bar(0.2, "Parsing ..."))
|
GLib.idle_add(lambda: app_obj.set_progress_bar(0.2, "Parsing ..."))
|
||||||
gerber_obj.parse_file(filename)
|
gerber_obj.parse_file(filename)
|
||||||
|
gerber_obj.create_geometry()
|
||||||
GLib.idle_add(lambda: app_obj.set_progress_bar(0.6, "Plotting ..."))
|
GLib.idle_add(lambda: app_obj.set_progress_bar(0.6, "Plotting ..."))
|
||||||
|
|
||||||
name = filename.split('/')[-1].split('\\')[-1]
|
name = filename.split('/')[-1].split('\\')[-1]
|
||||||
|
@ -2126,6 +2322,7 @@ class App:
|
||||||
def obj_init(excellon_obj, app_obj):
|
def obj_init(excellon_obj, app_obj):
|
||||||
GLib.idle_add(lambda: app_obj.set_progress_bar(0.2, "Parsing ..."))
|
GLib.idle_add(lambda: app_obj.set_progress_bar(0.2, "Parsing ..."))
|
||||||
excellon_obj.parse_file(filename)
|
excellon_obj.parse_file(filename)
|
||||||
|
excellon_obj.create_geometry()
|
||||||
GLib.idle_add(lambda: app_obj.set_progress_bar(0.6, "Plotting ..."))
|
GLib.idle_add(lambda: app_obj.set_progress_bar(0.6, "Plotting ..."))
|
||||||
|
|
||||||
name = filename.split('/')[-1].split('\\')[-1]
|
name = filename.split('/')[-1].split('\\')[-1]
|
||||||
|
@ -2202,6 +2399,10 @@ class App:
|
||||||
by the Matplotlib backend and has been registered in ``self.__init__()``.
|
by the Matplotlib backend and has been registered in ``self.__init__()``.
|
||||||
For details, see: http://matplotlib.org/users/event_handling.html
|
For details, see: http://matplotlib.org/users/event_handling.html
|
||||||
|
|
||||||
|
Default actions are:
|
||||||
|
|
||||||
|
* Copy coordinates to clipboard. Ex.: (65.5473, -13.2679)
|
||||||
|
|
||||||
:param event: Contains information about the event, like which button
|
:param event: Contains information about the event, like which button
|
||||||
was clicked, the pixel coordinates and the axes coordinates.
|
was clicked, the pixel coordinates and the axes coordinates.
|
||||||
:return: None
|
:return: None
|
||||||
|
@ -2213,8 +2414,12 @@ class App:
|
||||||
print 'button=%d, x=%d, y=%d, xdata=%f, ydata=%f' % (
|
print 'button=%d, x=%d, y=%d, xdata=%f, ydata=%f' % (
|
||||||
event.button, event.x, event.y, event.xdata, event.ydata)
|
event.button, event.x, event.y, event.xdata, event.ydata)
|
||||||
|
|
||||||
|
# TODO: This custom subscription mechanism is probably not necessary.
|
||||||
for subscriber in self.plot_click_subscribers:
|
for subscriber in self.plot_click_subscribers:
|
||||||
self.plot_click_subscribers[subscriber](event)
|
self.plot_click_subscribers[subscriber](event)
|
||||||
|
|
||||||
|
self.clipboard.set_text("(%.4f, %.4f)" % (event.xdata, event.ydata), -1)
|
||||||
|
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
print "Outside plot!"
|
print "Outside plot!"
|
||||||
|
|
||||||
|
|
413
FlatCAM.ui
413
FlatCAM.ui
|
@ -6,6 +6,11 @@
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
<property name="stock">gtk-open</property>
|
<property name="stock">gtk-open</property>
|
||||||
</object>
|
</object>
|
||||||
|
<object class="GtkImage" id="image10">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="stock">gtk-page-setup</property>
|
||||||
|
</object>
|
||||||
<object class="GtkImage" id="image2">
|
<object class="GtkImage" id="image2">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
|
@ -46,6 +51,366 @@
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
<property name="stock">gtk-open</property>
|
<property name="stock">gtk-open</property>
|
||||||
</object>
|
</object>
|
||||||
|
<object class="GtkOffscreenWindow" id="offscreenwindow_dblsided">
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkScrolledWindow" id="sw_dblsided">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="hscrollbar_policy">never</property>
|
||||||
|
<property name="shadow_type">in</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkViewport" id="vp_dblsided">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox" id="box_dblsided">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="margin_left">5</property>
|
||||||
|
<property name="margin_right">5</property>
|
||||||
|
<property name="margin_top">5</property>
|
||||||
|
<property name="margin_bottom">5</property>
|
||||||
|
<property name="orientation">vertical</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="label53">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="margin_bottom">6</property>
|
||||||
|
<property name="ypad">3</property>
|
||||||
|
<property name="label" translatable="yes">Double-Sided PCB Tool</property>
|
||||||
|
<attributes>
|
||||||
|
<attribute name="weight" value="semibold"/>
|
||||||
|
</attributes>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkGrid" id="grid7">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="row_spacing">3</property>
|
||||||
|
<property name="column_spacing">3</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="label84">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="xalign">1</property>
|
||||||
|
<property name="xpad">3</property>
|
||||||
|
<property name="label" translatable="yes">Bottom Layer:</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">0</property>
|
||||||
|
<property name="top_attach">0</property>
|
||||||
|
<property name="width">1</property>
|
||||||
|
<property name="height">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkComboBoxText" id="comboboxtext_bottomlayer">
|
||||||
|
<property name="width_request">200</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="halign">start</property>
|
||||||
|
<property name="entry_text_column">0</property>
|
||||||
|
<property name="id_column">1</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">1</property>
|
||||||
|
<property name="top_attach">0</property>
|
||||||
|
<property name="width">1</property>
|
||||||
|
<property name="height">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="label85">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="xalign">1</property>
|
||||||
|
<property name="xpad">3</property>
|
||||||
|
<property name="label" translatable="yes">Mirror Axis:</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">0</property>
|
||||||
|
<property name="top_attach">1</property>
|
||||||
|
<property name="width">1</property>
|
||||||
|
<property name="height">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox" id="box24">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="spacing">10</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkRadioButton" id="rb_mirror_x">
|
||||||
|
<property name="label" translatable="yes">X</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">False</property>
|
||||||
|
<property name="xalign">0</property>
|
||||||
|
<property name="active">True</property>
|
||||||
|
<property name="draw_indicator">True</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkRadioButton" id="rb_mirror_y">
|
||||||
|
<property name="label" translatable="yes">Y</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">False</property>
|
||||||
|
<property name="xalign">0</property>
|
||||||
|
<property name="draw_indicator">True</property>
|
||||||
|
<property name="group">rb_mirror_x</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">1</property>
|
||||||
|
<property name="top_attach">1</property>
|
||||||
|
<property name="width">1</property>
|
||||||
|
<property name="height">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="label86">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="xalign">1</property>
|
||||||
|
<property name="xpad">3</property>
|
||||||
|
<property name="label" translatable="yes">Axis location:</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">0</property>
|
||||||
|
<property name="top_attach">2</property>
|
||||||
|
<property name="width">1</property>
|
||||||
|
<property name="height">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox" id="box25">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="spacing">10</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkRadioButton" id="rb_mirror_point">
|
||||||
|
<property name="label" translatable="yes">Point</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">False</property>
|
||||||
|
<property name="xalign">0</property>
|
||||||
|
<property name="active">True</property>
|
||||||
|
<property name="draw_indicator">True</property>
|
||||||
|
<signal name="toggled" handler="on_toggle_pointbox" swapped="no"/>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkRadioButton" id="rb_mirror_box">
|
||||||
|
<property name="label" translatable="yes">Box</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">False</property>
|
||||||
|
<property name="xalign">0</property>
|
||||||
|
<property name="draw_indicator">True</property>
|
||||||
|
<property name="group">rb_mirror_point</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">1</property>
|
||||||
|
<property name="top_attach">2</property>
|
||||||
|
<property name="width">1</property>
|
||||||
|
<property name="height">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="label87">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="xalign">1</property>
|
||||||
|
<property name="xpad">3</property>
|
||||||
|
<property name="label" translatable="yes">Point/Box:</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">0</property>
|
||||||
|
<property name="top_attach">3</property>
|
||||||
|
<property name="width">1</property>
|
||||||
|
<property name="height">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox" id="box_pointbox">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="orientation">vertical</property>
|
||||||
|
<child>
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">1</property>
|
||||||
|
<property name="top_attach">3</property>
|
||||||
|
<property name="width">1</property>
|
||||||
|
<property name="height">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="label89">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="xalign">1</property>
|
||||||
|
<property name="xpad">3</property>
|
||||||
|
<property name="label" translatable="yes">Algnmt holes:</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">0</property>
|
||||||
|
<property name="top_attach">4</property>
|
||||||
|
<property name="width">1</property>
|
||||||
|
<property name="height">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkEntry" id="entry_dblsided_alignholes">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="invisible_char">●</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">1</property>
|
||||||
|
<property name="top_attach">4</property>
|
||||||
|
<property name="width">1</property>
|
||||||
|
<property name="height">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="label90">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="xalign">1</property>
|
||||||
|
<property name="xpad">3</property>
|
||||||
|
<property name="label" translatable="yes">Drill diam.:</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">0</property>
|
||||||
|
<property name="top_attach">5</property>
|
||||||
|
<property name="width">1</property>
|
||||||
|
<property name="height">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkEntry" id="entry_dblsided_alignholediam">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="invisible_char">●</property>
|
||||||
|
<property name="invisible_char_set">True</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">1</property>
|
||||||
|
<property name="top_attach">5</property>
|
||||||
|
<property name="width">1</property>
|
||||||
|
<property name="height">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox" id="box27">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="halign">end</property>
|
||||||
|
<property name="margin_top">6</property>
|
||||||
|
<property name="margin_bottom">3</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButton" id="button19">
|
||||||
|
<property name="label" translatable="yes">Create Alignment Drill</property>
|
||||||
|
<property name="width_request">120</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">True</property>
|
||||||
|
<property name="halign">end</property>
|
||||||
|
<signal name="activate" handler="on_create_aligndrill" swapped="no"/>
|
||||||
|
<signal name="clicked" handler="on_create_aligndrill" swapped="no"/>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">False</property>
|
||||||
|
<property name="padding">4</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButton" id="button18">
|
||||||
|
<property name="label" translatable="yes">Create Mirror</property>
|
||||||
|
<property name="width_request">120</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">True</property>
|
||||||
|
<property name="halign">end</property>
|
||||||
|
<signal name="activate" handler="on_create_mirror" swapped="no"/>
|
||||||
|
<signal name="clicked" handler="on_create_mirror" swapped="no"/>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">False</property>
|
||||||
|
<property name="padding">4</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">2</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
<object class="GtkOffscreenWindow" id="offscrwindow_cncjob">
|
<object class="GtkOffscreenWindow" id="offscrwindow_cncjob">
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
<child>
|
<child>
|
||||||
|
@ -2267,6 +2632,30 @@ to application defaults.</property>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkMenuItem" id="menuitem11">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="label" translatable="yes">_Tools</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
<child type="submenu">
|
||||||
|
<object class="GtkMenu" id="menu6">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkImageMenuItem" id="imagemenuitem12">
|
||||||
|
<property name="label" translatable="yes">Double-Sided PCB Tool</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="image">image10</property>
|
||||||
|
<property name="use_stock">False</property>
|
||||||
|
<signal name="activate" handler="on_tools_doublesided" swapped="no"/>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkMenuItem" id="menuitem4">
|
<object class="GtkMenuItem" id="menuitem4">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
|
@ -3738,6 +4127,30 @@ to application defaults.</property>
|
||||||
<property name="tab_fill">False</property>
|
<property name="tab_fill">False</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox" id="box_tool">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="orientation">vertical</property>
|
||||||
|
<child>
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="position">3</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child type="tab">
|
||||||
|
<object class="GtkLabel" id="label88">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="label" translatable="yes">Tool</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="position">3</property>
|
||||||
|
<property name="tab_fill">False</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="resize">False</property>
|
<property name="resize">False</property>
|
||||||
|
|
93
camlib.py
93
camlib.py
|
@ -1,3 +1,9 @@
|
||||||
|
############################################################
|
||||||
|
# Author: Juan Pablo Caram #
|
||||||
|
# Date: 2/5/2014 #
|
||||||
|
# caram.cl #
|
||||||
|
############################################################
|
||||||
|
|
||||||
from numpy import arctan2, Inf, array, sqrt, pi, ceil, sin, cos
|
from numpy import arctan2, Inf, array, sqrt, pi, ceil, sin, cos
|
||||||
from matplotlib.figure import Figure
|
from matplotlib.figure import Figure
|
||||||
import re
|
import re
|
||||||
|
@ -16,6 +22,7 @@ from shapely.geometry.base import BaseGeometry
|
||||||
from descartes.patch import PolygonPatch
|
from descartes.patch import PolygonPatch
|
||||||
|
|
||||||
import simplejson as json
|
import simplejson as json
|
||||||
|
from matplotlib.pyplot import plot
|
||||||
|
|
||||||
class Geometry:
|
class Geometry:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
@ -202,9 +209,19 @@ class Gerber (Geometry):
|
||||||
* ``buffered_paths`` (list): List of (Shapely) polygons resulting from
|
* ``buffered_paths`` (list): List of (Shapely) polygons resulting from
|
||||||
*buffering* (or thickening) the ``paths`` with the aperture. These are
|
*buffering* (or thickening) the ``paths`` with the aperture. These are
|
||||||
generated from ``paths`` in ``buffer_paths()``.
|
generated from ``paths`` in ``buffer_paths()``.
|
||||||
|
|
||||||
|
**USAGE**
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
The constructor takes no parameters. Use ``gerber.parse_files()``
|
||||||
|
or ``gerber.parse_lines()`` to populate the object from Gerber source.
|
||||||
|
:return: Gerber object
|
||||||
|
:rtype: Gerber
|
||||||
|
"""
|
||||||
# Initialize parent
|
# Initialize parent
|
||||||
Geometry.__init__(self)
|
Geometry.__init__(self)
|
||||||
|
|
||||||
|
@ -457,8 +474,9 @@ class Gerber (Geometry):
|
||||||
Every stroke (linear or circular) has an aperture which gives
|
Every stroke (linear or circular) has an aperture which gives
|
||||||
it thickness. Additionally, aperture strokes have non-zero area,
|
it thickness. Additionally, aperture strokes have non-zero area,
|
||||||
and regions naturally do as well.
|
and regions naturally do as well.
|
||||||
|
|
||||||
:rtype : None
|
:rtype : None
|
||||||
@return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
# if len(self.buffered_paths) == 0:
|
# if len(self.buffered_paths) == 0:
|
||||||
# self.buffer_paths()
|
# self.buffer_paths()
|
||||||
|
@ -470,6 +488,25 @@ class Gerber (Geometry):
|
||||||
[poly['polygon'] for poly in self.regions] +
|
[poly['polygon'] for poly in self.regions] +
|
||||||
self.flash_geometry)
|
self.flash_geometry)
|
||||||
|
|
||||||
|
def get_bounding_box(self, margin=0.0, rounded=False):
|
||||||
|
"""
|
||||||
|
Creates and returns a rectangular polygon bounding at a distance of
|
||||||
|
margin from the object's ``solid_geometry``. If margin > 0, the polygon
|
||||||
|
can optionally have rounded corners of radius equal to margin.
|
||||||
|
|
||||||
|
:param margin: Distance to enlarge the rectangular bounding
|
||||||
|
box in both positive and negative, x and y axes.
|
||||||
|
:type margin: float
|
||||||
|
:param rounded: Wether or not to have rounded corners.
|
||||||
|
:type rounded: bool
|
||||||
|
:return: The bounding box.
|
||||||
|
:rtype: Shapely.Polygon
|
||||||
|
"""
|
||||||
|
bbox = self.solid_geometry.envelope.buffer(margin)
|
||||||
|
if not rounded:
|
||||||
|
bbox = bbox.envelope
|
||||||
|
return bbox
|
||||||
|
|
||||||
|
|
||||||
class Excellon(Geometry):
|
class Excellon(Geometry):
|
||||||
"""
|
"""
|
||||||
|
@ -488,6 +525,11 @@ class Excellon(Geometry):
|
||||||
================ ====================================
|
================ ====================================
|
||||||
"""
|
"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
The constructor takes no parameters.
|
||||||
|
:return: Excellon object.
|
||||||
|
:rtype: Excellon
|
||||||
|
"""
|
||||||
Geometry.__init__(self)
|
Geometry.__init__(self)
|
||||||
|
|
||||||
self.tools = {}
|
self.tools = {}
|
||||||
|
@ -1046,11 +1088,14 @@ def get_bounds(geometry_set):
|
||||||
|
|
||||||
print "Getting bounds of:", str(geometry_set)
|
print "Getting bounds of:", str(geometry_set)
|
||||||
for gs in geometry_set:
|
for gs in geometry_set:
|
||||||
gxmin, gymin, gxmax, gymax = geometry_set[gs].bounds()
|
try:
|
||||||
xmin = min([xmin, gxmin])
|
gxmin, gymin, gxmax, gymax = geometry_set[gs].bounds()
|
||||||
ymin = min([ymin, gymin])
|
xmin = min([xmin, gxmin])
|
||||||
xmax = max([xmax, gxmax])
|
ymin = min([ymin, gymin])
|
||||||
ymax = max([ymax, gymax])
|
xmax = max([xmax, gxmax])
|
||||||
|
ymax = max([ymax, gymax])
|
||||||
|
except:
|
||||||
|
print "DEV WARNING: Tried to get bounds of empty geometry."
|
||||||
|
|
||||||
return [xmin, ymin, xmax, ymax]
|
return [xmin, ymin, xmax, ymax]
|
||||||
|
|
||||||
|
@ -1125,6 +1170,7 @@ def find_polygon(poly_set, point):
|
||||||
return poly
|
return poly
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def to_dict(geo):
|
def to_dict(geo):
|
||||||
output = ''
|
output = ''
|
||||||
if isinstance(geo, BaseGeometry):
|
if isinstance(geo, BaseGeometry):
|
||||||
|
@ -1134,6 +1180,7 @@ def to_dict(geo):
|
||||||
}
|
}
|
||||||
return geo
|
return geo
|
||||||
|
|
||||||
|
|
||||||
def dict2obj(d):
|
def dict2obj(d):
|
||||||
if '__class__' in d and '__inst__' in d:
|
if '__class__' in d and '__inst__' in d:
|
||||||
# For now assume all classes are Shapely geometry.
|
# For now assume all classes are Shapely geometry.
|
||||||
|
@ -1141,6 +1188,40 @@ def dict2obj(d):
|
||||||
else:
|
else:
|
||||||
return d
|
return d
|
||||||
|
|
||||||
|
|
||||||
|
def plotg(geo):
|
||||||
|
try:
|
||||||
|
_ = iter(geo)
|
||||||
|
except:
|
||||||
|
geo = [geo]
|
||||||
|
|
||||||
|
for g in geo:
|
||||||
|
if type(g) == Polygon:
|
||||||
|
x, y = g.exterior.coords.xy
|
||||||
|
plot(x, y)
|
||||||
|
for ints in g.interiors:
|
||||||
|
x, y = ints.coords.xy
|
||||||
|
plot(x, y)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if type(g) == LineString or type(g) == LinearRing:
|
||||||
|
x, y = g.coords.xy
|
||||||
|
plot(x, y)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if type(g) == Point:
|
||||||
|
x, y = g.coords.xy
|
||||||
|
plot(x, y, 'o')
|
||||||
|
continue
|
||||||
|
|
||||||
|
try:
|
||||||
|
_ = iter(g)
|
||||||
|
plotg(g)
|
||||||
|
except:
|
||||||
|
print "Cannot plot:", str(type(g))
|
||||||
|
continue
|
||||||
|
|
||||||
|
|
||||||
############### cam.py ####################
|
############### cam.py ####################
|
||||||
def coord(gstr, digits, fraction):
|
def coord(gstr, digits, fraction):
|
||||||
"""
|
"""
|
||||||
|
|
Loading…
Reference in New Issue