Double-sided PCB support.

This commit is contained in:
Juan Pablo Caram 2014-02-05 19:36:47 -05:00
parent e930de1793
commit 6284156a0d
3 changed files with 708 additions and 9 deletions

View File

@ -1,3 +1,9 @@
############################################################
# Author: Juan Pablo Caram #
# Date: 2/5/2014 #
# caram.cl #
############################################################
import threading
from gi.repository import Gtk, Gdk, GLib, GObject
import simplejson as json
@ -255,7 +261,7 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
def plot(self, figure):
FlatCAMObj.plot(self, figure)
self.create_geometry()
#self.create_geometry()
if self.options["mergepolys"]:
geometry = self.solid_geometry
@ -334,7 +340,7 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
def plot(self, figure):
FlatCAMObj.plot(self, figure)
#self.setup_axes(figure)
self.create_geometry()
#self.create_geometry()
# Plot excellon
for geo in self.solid_geometry:
@ -572,6 +578,7 @@ class App:
self.setup_component_editor() # The "Selected" tab
#### DATA ####
self.clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
self.setup_obj_classes()
self.stuff = {} # FlatCAMObj's by name
self.mouse = None # Mouse coordinates over plot
@ -1229,10 +1236,196 @@ class App:
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 ##
########################################
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):
"""
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:
return
@ -1574,6 +1767,7 @@ class App:
:param widget: Ignored.
:return: None
"""
# TODO: Use Gerber.get_bounding_box(...)
gerber = self.get_current()
gerber.read_form()
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 ..."))
def obj_init(gerber_obj, app_obj):
assert isinstance(gerber_obj, FlatCAMGerber)
GLib.idle_add(lambda: app_obj.set_progress_bar(0.2, "Parsing ..."))
gerber_obj.parse_file(filename)
gerber_obj.create_geometry()
GLib.idle_add(lambda: app_obj.set_progress_bar(0.6, "Plotting ..."))
name = filename.split('/')[-1].split('\\')[-1]
@ -2126,6 +2322,7 @@ class App:
def obj_init(excellon_obj, app_obj):
GLib.idle_add(lambda: app_obj.set_progress_bar(0.2, "Parsing ..."))
excellon_obj.parse_file(filename)
excellon_obj.create_geometry()
GLib.idle_add(lambda: app_obj.set_progress_bar(0.6, "Plotting ..."))
name = filename.split('/')[-1].split('\\')[-1]
@ -2202,6 +2399,10 @@ class App:
by the Matplotlib backend and has been registered in ``self.__init__()``.
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
was clicked, the pixel coordinates and the axes coordinates.
:return: None
@ -2213,8 +2414,12 @@ class App:
print 'button=%d, x=%d, y=%d, xdata=%f, ydata=%f' % (
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:
self.plot_click_subscribers[subscriber](event)
self.clipboard.set_text("(%.4f, %.4f)" % (event.xdata, event.ydata), -1)
except Exception, e:
print "Outside plot!"

View File

@ -6,6 +6,11 @@
<property name="can_focus">False</property>
<property name="stock">gtk-open</property>
</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">
<property name="visible">True</property>
<property name="can_focus">False</property>
@ -46,6 +51,366 @@
<property name="can_focus">False</property>
<property name="stock">gtk-open</property>
</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">
<property name="can_focus">False</property>
<child>
@ -2267,6 +2632,30 @@ to application defaults.</property>
</child>
</object>
</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>
<object class="GtkMenuItem" id="menuitem4">
<property name="visible">True</property>
@ -3738,6 +4127,30 @@ to application defaults.</property>
<property name="tab_fill">False</property>
</packing>
</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>
<packing>
<property name="resize">False</property>

View File

@ -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 matplotlib.figure import Figure
import re
@ -16,6 +22,7 @@ from shapely.geometry.base import BaseGeometry
from descartes.patch import PolygonPatch
import simplejson as json
from matplotlib.pyplot import plot
class Geometry:
def __init__(self):
@ -202,9 +209,19 @@ class Gerber (Geometry):
* ``buffered_paths`` (list): List of (Shapely) polygons resulting from
*buffering* (or thickening) the ``paths`` with the aperture. These are
generated from ``paths`` in ``buffer_paths()``.
**USAGE**
"""
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
Geometry.__init__(self)
@ -457,8 +474,9 @@ class Gerber (Geometry):
Every stroke (linear or circular) has an aperture which gives
it thickness. Additionally, aperture strokes have non-zero area,
and regions naturally do as well.
:rtype : None
@return: None
:return: None
"""
# if len(self.buffered_paths) == 0:
# self.buffer_paths()
@ -470,6 +488,25 @@ class Gerber (Geometry):
[poly['polygon'] for poly in self.regions] +
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):
"""
@ -488,6 +525,11 @@ class Excellon(Geometry):
================ ====================================
"""
def __init__(self):
"""
The constructor takes no parameters.
:return: Excellon object.
:rtype: Excellon
"""
Geometry.__init__(self)
self.tools = {}
@ -1046,12 +1088,15 @@ def get_bounds(geometry_set):
print "Getting bounds of:", str(geometry_set)
for gs in geometry_set:
gxmin, gymin, gxmax, gymax = geometry_set[gs].bounds()
xmin = min([xmin, gxmin])
ymin = min([ymin, gymin])
xmax = max([xmax, gxmax])
ymax = max([ymax, gymax])
try:
gxmin, gymin, gxmax, gymax = geometry_set[gs].bounds()
xmin = min([xmin, gxmin])
ymin = min([ymin, gymin])
xmax = max([xmax, gxmax])
ymax = max([ymax, gymax])
except:
print "DEV WARNING: Tried to get bounds of empty geometry."
return [xmin, ymin, xmax, ymax]
@ -1125,6 +1170,7 @@ def find_polygon(poly_set, point):
return poly
return None
def to_dict(geo):
output = ''
if isinstance(geo, BaseGeometry):
@ -1134,6 +1180,7 @@ def to_dict(geo):
}
return geo
def dict2obj(d):
if '__class__' in d and '__inst__' in d:
# For now assume all classes are Shapely geometry.
@ -1141,6 +1188,40 @@ def dict2obj(d):
else:
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 ####################
def coord(gstr, digits, fraction):
"""