Created
July 24, 2011 12:52
-
-
Save kevinjardine/1102587 to your computer and use it in GitHub Desktop.
A universal tiler and polygon decompositor
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# To use this, you will want to change the location of the saved data | |
# at least and possibly the functions called below (at the end of the file). | |
# You first need to install the Python Shapely library for manipulating polygons: | |
# http://trac.gispython.org/lab/wiki/Shapely | |
from math import pi, sin, cos, atan2 | |
import shapely.geometry | |
import sys, copy | |
def position_opposite(large_order,small_order): | |
sym_lookup = {} | |
s0 = large_order/small_order | |
#opposites only make sense if s0 is even | |
if ((s0 % 2) == 0): | |
for i in range(0,s0): | |
if i % 2 == 0: | |
sym_lookup[i] = i | |
else: | |
sym_lookup[i] = (i+(s0/2)-1) % s0 | |
else: | |
sym_lookup = position_sequential(large_order,small_order) | |
return sym_lookup | |
def position_6(large_order,small_order): | |
#return [3, 2, 1, 0, 5, 4] | |
return [4,5,0,1,2,3] | |
def position_sequential(large_order,small_order): | |
sym_lookup = {} | |
s0 = large_order/small_order | |
for i in range(0,s0): | |
sym_lookup[i] = i | |
return sym_lookup | |
def position_alternating(large_order,small_order): | |
sym_lookup = {} | |
s0 = large_order/small_order | |
for i in range(0,s0/2): | |
sym_lookup[i] = 2*i | |
for i in range(0,s0/2): | |
sym_lookup[i+s0/2] = 2*i+1 | |
return sym_lookup | |
class Tiling(object): | |
"""Stores and manipulates a tile patch""" | |
def __init__(self): | |
self.pc = 0 | |
self.polygons = [] | |
self.ec = 0 | |
self.edges = [] | |
self.vc = 0 | |
self.vertices = [] | |
self.spc = 0 | |
self.shapely_polygons = [] | |
self.stack = [] | |
def add_to_shapely_polygons(self,pe): | |
poly = [] | |
for e in pe: | |
poly.append(e[0]) | |
self.shapely_polygons.append(shapely.geometry.Polygon(poly)) | |
self.spc += 1 | |
def get_vertex_list(self,v0,e0,p0): | |
error = False | |
e = self.edges[e0] | |
v = self.vertices[v0] | |
poly = [[v.x,v.y]] | |
v1 = e.v1 | |
if v1 == v0: | |
v1 = e.v0 | |
i = 0 | |
# cycle around the vertices until we return to v0 | |
while v1 != v0: | |
if i > len(self.vertices): | |
error = True | |
break | |
v = self.vertices[v1] | |
poly.append([v.x,v.y]) | |
e0 = self.get_other_edge2(v1,e0,p0) | |
v1 = self.get_other_vertex(v1,e0) | |
i += 1 | |
if error: | |
print "cannot compute vertex list" | |
sys.exit() | |
else: | |
return poly | |
def get_edge_list(self,v0,e0,p0): | |
error = False | |
e = self.edges[e0] | |
poly = [e0] | |
v1 = e.v1 | |
if v0 == v1: | |
v1 = e.v0 | |
i = 0 | |
# cycle around the vertices until we return to v0 | |
while v1 != v0: | |
e0 = self.get_other_edge2(v1,e0,p0) | |
if e0 == -1 or i > len(self.vertices): | |
error = True | |
break | |
v1 = self.get_other_vertex(v1,e0) | |
poly.append(e0) | |
i += 1 | |
if error: | |
print "cannot compute edge list" | |
sys.exit() | |
else: | |
return poly | |
def convert_to_shapely_polygon(self,p,r=True): | |
e0 = self.polygons[p].edges[0] | |
e = self.edges[e0] | |
v0 = e.v0 | |
poly = self.get_vertex_list(v0,e0,p) | |
if poly == False or len(poly) < 3: | |
#sanity checks | |
return False | |
else: | |
return shapely.geometry.Polygon(poly) | |
def add_to_shapely_polygons2(self,poly): | |
self.shapely_polygons.append(shapely.geometry.Polygon(poly)) | |
self.spc += 1 | |
def polygon_to_svg(self,p): | |
if self.shapely_polygons[p] == False: | |
return '' | |
points = [] | |
for x,y in list(self.shapely_polygons[p].exterior.coords): | |
points.append(str(20*x+500)+","+str(20*y+500)) | |
r = '<polygon style="stroke:#000000;stroke-width:1;fill:#F0F0F0" points="' | |
r += " ".join(points) | |
r += '"/>'+"\n" | |
return r | |
def labels_to_svg(self): | |
r = '' | |
for v in self.vertices: | |
r += '<text x="'+str(20*v.x+500) | |
r += '" y="'+str(20*v.y+500) | |
r += '">('+str(v.num)+")</text>\n" | |
for e in self.edges: | |
vx = (0.5)*(self.vertices[e.v0].x+self.vertices[e.v1].x) | |
vy = (0.5)*(self.vertices[e.v0].y+self.vertices[e.v1].y) | |
r += '<text x="'+str(20*vx+500) | |
r += '" y="'+str(20*vy+500) | |
r += '">'+str(e.num)+"</text>\n" | |
return r | |
# TODO: change the data structures to make this more efficient to compute? | |
def get_other_edge2(self,v0,e0,p0): | |
for e in self.polygons[p0].edges: | |
if e != e0 and (self.edges[e].v0 == v0 or self.edges[e].v1 == v0): | |
return e | |
return -1 | |
def add_to_vertex_sweep(self,v,e,p): | |
vertex = self.vertices[v] | |
if v in []: | |
print "@@@************ adding to sweeps", v, e, p | |
print "current sweep_e", vertex.sweep_e | |
print "current sweep_p", vertex.sweep_p | |
e_prev = -1 | |
p_prev = -1 | |
e2 = -1 | |
p2 = -1 | |
if len(vertex.sweep_e) <= 1: | |
# the sweep direction is not yet established | |
if p not in vertex.sweep_p: | |
vertex.sweep_p.append(p) | |
if e not in vertex.sweep_e: | |
vertex.sweep_e.append(e) | |
else: | |
# need to add only in the sweep direction | |
if vertex.sweep_e[-1] == e: | |
if p not in vertex.sweep_p: | |
vertex.sweep_p.append(p) | |
elif vertex.sweep_e[0] == e: | |
if p not in vertex.sweep_p: | |
vertex.sweep_p = [p] + vertex.sweep_p | |
else: | |
# try the polygon method | |
if vertex.sweep_p[-1] == p: | |
if e not in vertex.sweep_e: | |
vertex.sweep_e.append(e) | |
elif vertex.sweep_p[0] == p: | |
if e not in vertex.sweep_e: | |
vertex.sweep_e = [e] + vertex.sweep_e | |
else: | |
#try the other edge method | |
#TODO: should the get_other_edge code be here? | |
e2 = self.get_other_edge2(v,e,p) | |
if vertex.sweep_e[-1] == e2: | |
if p not in vertex.sweep_p: | |
vertex.sweep_p.append(p) | |
elif vertex.sweep_e[0] == e2: | |
if p not in vertex.sweep_p: | |
vertex.sweep_p = [p] + vertex.sweep_p | |
else: | |
# try the other polygon method | |
p2 = self.get_other_polygon(e,p) | |
if vertex.sweep_p[-1] == p2: | |
if p not in vertex.sweep_p: | |
vertex.sweep_p.append(p) | |
if e not in vertex.sweep_e: | |
vertex.sweep_e.append(e) | |
elif vertex.sweep_p[0] == p2: | |
if p not in vertex.sweep_p: | |
vertex.sweep_p = [p] + vertex.sweep_p | |
if e not in vertex.sweep_e: | |
vertex.sweep_e = [e] + vertex.sweep_e | |
else: | |
# try the previous polygon method | |
p_prev = self.get_previous_polygon(v,e,p) | |
if vertex.sweep_p[-1] == p_prev: | |
if p not in vertex.sweep_p: | |
vertex.sweep_p.append(p) | |
if e not in vertex.sweep_e: | |
vertex.sweep_e.append(e) | |
elif vertex.sweep_p[0] == p_prev: | |
if p not in vertex.sweep_p: | |
vertex.sweep_p = [p] + vertex.sweep_p | |
if e not in vertex.sweep_e: | |
vertex.sweep_e = [e] + vertex.sweep_e | |
else: | |
# try the previous edge method | |
e_prev = self.get_other_edge2(v,e,p_prev) | |
p_prev = self.get_previous_polygon(v,e,p) | |
if vertex.sweep_p[-1] == p_prev: | |
if p not in vertex.sweep_p: | |
vertex.sweep_p.append(p) | |
if e not in vertex.sweep_e: | |
vertex.sweep_e.append(e) | |
elif vertex.sweep_p[0] == p_prev: | |
if p not in vertex.sweep_p: | |
vertex.sweep_p = [p] + vertex.sweep_p | |
if e not in vertex.sweep_e: | |
vertex.sweep_e = [e] + vertex.sweep_e | |
else: | |
pass | |
def add_to_sweeps(self,edge_ids,e0,p0): | |
for e in edge_ids: | |
if e != e0 and e != -1: | |
self.add_to_vertex_sweep(self.edges[e].v0,e,p0) | |
self.add_to_vertex_sweep(self.edges[e].v1,e,p0) | |
def get_other_polygon(self,e,p): | |
plist = self.polygons_for([e]) | |
if len(plist) < 2: | |
#print "warning: cannot find both polygons for", e, p | |
return plist[0] | |
if plist[0] == p: | |
return plist[1] | |
else: | |
return plist[0] | |
def get_previous_polygon(self,v,e0,p): | |
elist = self.polygons[p].edges | |
for e in elist: | |
if e != e0 and (self.edges[e].v0 == v or self.edges[e].v1 == v): | |
return self.get_other_polygon(e,p) | |
return -1 | |
def get_previous_vertex(self,v0,e0): | |
for p in self.polygons: | |
elist = p.edges | |
if e0 in elist: | |
for e in elist: | |
if e != e0: | |
if self.edges[e].v0 == v0: | |
return self.edges[e].v1 | |
elif self.edges[e].v1 == v0: | |
return self.edges[e].v0 | |
return -1 | |
def create_polygon(self,e0,p,poly_edges): | |
#TODO: try to simplify this | |
#In particular, some of the new_vertex calls are redundant | |
edge_ids = self.get_edge_ids(poly_edges) | |
self.add_to_sweeps(edge_ids,e0,p) | |
vl = [] | |
for i in range(0,len(edge_ids)): | |
e = edge_ids[i] | |
if e == -1: | |
# need a new edge | |
vfi = self.new_vertex(poly_edges[i][0]) | |
vsi = self.new_vertex(poly_edges[i][1]) | |
e = self.new_edge(vfi,vsi) | |
else: | |
vfi = self.edges[e].v0 | |
vsi = self.edges[e].v1 | |
if e != e0: | |
self.add_to_polygon(p,e) | |
self.add_to_vertex_sweep(vfi,e,p) | |
self.add_to_vertex_sweep(vsi,e,p) | |
for pe in poly_edges: | |
vi = self.new_vertex(pe[0]) | |
if vi not in vl: | |
vl.append(vi) | |
return vl | |
def get_edge_ids(self,poly_edges): | |
edge_ids = [] | |
for pe in poly_edges: | |
e = self.get_edge_from_poly_edge(pe) | |
edge_ids.append(e) | |
return edge_ids | |
def add_to_polygon(self,p,e): | |
self.polygons[p].edges.append(e) | |
def remove_from_polygon(self,p,e): | |
v0 = self.edges[e].v0 | |
v1 = self.edges[e].v1 | |
self.polygons[p].edges.remove(e) | |
#search vertex list looking for duplicates | |
# TODO: find something more efficient? | |
def new_vertex(self,v): | |
for vi in range(0,self.vc): | |
if not different_vertex(v,self.vertices[vi]): | |
return vi | |
v = Vertex(v[0],v[1]) | |
v.num = self.vc | |
self.vertices.append(v) | |
self.vc += 1 | |
return v.num | |
def new_edge(self,v0,v1): | |
for ei in range(0,self.ec): | |
if not different_edge(v0,v1,self.edges[ei]): | |
return ei | |
e = Edge(v0,v1) | |
e.num = self.ec | |
self.edges.append(e) | |
self.ec += 1 | |
return e.num | |
def new_polygon(self,e0,ptype,make_internal=True): | |
p = Polygon(e0,ptype,make_internal) | |
p.num = self.pc | |
self.polygons.append(p) | |
self.pc += 1 | |
return p.num | |
def get_edge_from_poly_edge(self,pe): | |
for ei in range(0,self.ec): | |
e = self.edges[ei] | |
v0 = [self.vertices[e.v0].x, self.vertices[e.v0].y] | |
v1 = [self.vertices[e.v1].x, self.vertices[e.v1].y] | |
t0 = equal_vertex(v0,pe[0]) and equal_vertex(v1,pe[1]) | |
t1 = equal_vertex(v0,pe[1]) and equal_vertex(v1,pe[0]) | |
if t0 or t1: | |
return ei | |
return -1 | |
def start_polygon(self,n): | |
v0 = self.new_vertex([-0.5,0.0]) | |
v1 = self.new_vertex([0.5,0.0]) | |
e = self.new_edge(v1,v0) | |
ab,external,le = self.get_leading_edge2(v0,0) | |
self.complete_at_vertex(v0,[n],external,1,[],[]) | |
def star_around(self,a): | |
v0 = self.new_vertex([-0.5,0.0]) | |
v1 = self.new_vertex([0.5,0.0]) | |
e = self.new_edge(v0,v1) | |
n = int(2*pi/a + 0.5) | |
for i in range(0,n): | |
p = compute_rhombus(v1,e,a) | |
e = get_other_edge(v1,e,p) | |
def edges_for(self,v): | |
ev = [] | |
for e in self.edges: | |
if (e.v0 == v) or (e.v1 == v): | |
ev.append(e.num) | |
return ev | |
# elist is a list of edges | |
def polygons_for(self,elist): | |
pe = [] | |
for p in self.polygons: | |
for e in elist: | |
if e in p.edges: | |
pe.append(p.num) | |
break | |
return pe | |
def compute_circle_gap_at_vertex(self,v): | |
at = 0 | |
vertex = self.vertices[v] | |
for p in vertex.sweep_p: | |
g = vertex.sweep_lu[p] | |
if g > 0: | |
ng = (pi / g) * (g-2) | |
#ng = d[g]['p'] | |
else: | |
ng = -g | |
at += ng | |
return 2*pi - at | |
def get_poly_edge_from_edge(self,e): | |
v0 = self.vertices[e[0]] | |
v1 = self.vertices[e[1]] | |
return [[v0.x,v0.y],[v1.x,v1.y]] | |
def validate_sweep(self,v,species,rspecies): | |
if len(species) == 0: | |
# an empty species turns off validation | |
return True | |
# search for a solution to the sweep | |
sweep = self.get_sweep(v) | |
# if the sweep is complete, backstep to make sure that this is a legal vertex | |
if abs(self.compute_circle_gap_at_vertex(v)) < 0.0001: | |
sweep = sweep[1:] | |
#print "in validate_sweep, checking sweep", convert_sweep(sweep) | |
for cspecies in [species, rspecies]: | |
for i in range(0,len(cspecies)): | |
#print "against vertex type", convert_sweep(cspecies[i]) | |
pl = get_plist_from_sweep(sweep,cspecies[i]) | |
if len(pl) != 0: | |
return True | |
return False | |
def find_overlapping_polygons(self,pl): | |
overlaps = [] | |
p0 = [] | |
for e in pl: | |
p0.append(e[0]) | |
poly = shapely.geometry.Polygon(p0) | |
i = 0 | |
for p in self.shapely_polygons: | |
try: | |
if poly.intersects(p): | |
pintersect = poly.intersection(p) | |
if (type(pintersect) is shapely.geometry.polygon.Polygon) and pintersect.area > 0.01: | |
#print "overlap detected", i, p,poly,pintersect,pintersect.area | |
overlaps.append(i) | |
except: | |
print "overlap intersect function error" | |
return [] | |
i += 1 | |
return overlaps | |
def punch(self,v,e,n): | |
external = [self.edges[e].v0,self.edges[e].v1] | |
vraw = [self.vertices[v].x,self.vertices[v].y] | |
e0 = self.get_poly_edge_from_edge(external) | |
parity = -1 | |
if n > 0: | |
r = compute_polygon(vraw,e0,n,parity) | |
else: | |
r = compute_rhombus(vraw,e0,-n,parity) | |
# only do something if the new polygon overlaps exactly one | |
# other polygon | |
overlaps = self.find_overlapping_polygons(r[0]) | |
if len(overlaps) == 0: | |
print "Error: no overlap in punch" | |
sys.exit() | |
elif len(overlaps) > 1: | |
print "Error: multiple overlaps in punch" | |
sys.exit() | |
else : | |
ps = t.polygons_for([e]) | |
if len(ps) > 0: | |
p0 = ps[0] | |
else: | |
print "Error in punch: cannot find existing polygon for edge ", e | |
sys.exit() | |
poly_edges = r[0] | |
root_edge = r[1] | |
a = r[2] | |
e0 = self.get_edge_from_poly_edge(root_edge) | |
p = self.new_polygon(e0,n) | |
vl = self.create_polygon(e0,p,poly_edges) | |
# add the sweep angles to the vertices | |
# TODO: this code is wrong for punches | |
if n > 0: | |
for vi in vl: | |
vertex = self.vertices[vi] | |
vertex.sweep_lu[p] = n | |
vertex.sweep_plu[p] = n | |
else: | |
# rhombuses have two angles, so the sweep code is a bit more complex | |
if v == vl[0] or v == vl[2]: | |
ang = [a,pi-a,a,pi-a] | |
else: | |
ang = [pi-a,a,pi-a,a] | |
for i in range(0,len(vl)): | |
vertex = self.vertices[vl[i]] | |
vertex.sweep_lu[p] = -ang[i] | |
if ang[i] > (pi/2): | |
vertex.sweep_plu[p] = int(2*pi/(pi-ang[i])+0.5) | |
else: | |
vertex.sweep_plu[p] = -int((2*pi/ang[i])+0.5) | |
self.add_to_shapely_polygons(poly_edges) | |
esp = self.polygons[p].edges | |
esp0 = copy.deepcopy(self.polygons[p0].edges) | |
for e0 in esp: | |
if e0 in esp0: | |
self.remove_from_polygon(p0,e0) | |
else: | |
self.polygons[p0].edges.append(e0) | |
self.shapely_polygons[p0] = self.convert_to_shapely_polygon(p0) | |
return True | |
def split(self,v0,e0,p0): | |
#take the edges from the end of e0 to the beginning of e1 | |
# copy it and translate it to the beginning of e0 | |
e1 = self.find_equal_slope_edge(v0,e0,p0) | |
edges = self.get_edge_list(v0,e0,p0) | |
if edges == False: | |
return False | |
es = [] | |
for e in edges: | |
es.append(e) | |
if e == e1: | |
break | |
v = self.vertices[v0] | |
v0x = v.x | |
v0y = v.y | |
v1 = self.get_other_vertex(v0,e0) | |
v = self.vertices[v1] | |
v1x = v.x | |
v1y = v.y | |
vx = v0x - v1x | |
vy = v0y - v1y | |
es2 = copy.deepcopy(es) | |
for e1 in es[1:-1]: | |
e = self.edges[e1] | |
pe1 = self.get_poly_edge_from_edge([e.v0,e.v1]) | |
nv0 = self.new_vertex([pe1[0][0]+vx,pe1[0][1]+vy]) | |
nv1 = self.new_vertex([pe1[1][0]+vx,pe1[1][1]+vy]) | |
e = self.new_edge(nv0,nv1) | |
if e not in es2: | |
es2.append(e) | |
self.add_to_polygon(p0,e) | |
# make a new polygon out of the edges | |
# sets ptype to 1 to signify a worm | |
p = self.new_polygon(es2[0],1) | |
for e in es2[1:]: | |
self.add_to_polygon(p,e) | |
s = self.convert_to_shapely_polygon(p) | |
self.shapely_polygons.append(s) | |
self.spc += 1 | |
#remove edges from original polygon | |
for e in es: | |
self.remove_from_polygon(p0,e) | |
self.shapely_polygons[p0] = self.convert_to_shapely_polygon(p0) | |
def clip_all(self,p0): | |
"""clip worm completely into rhombs""" | |
n = len(self.polygons[p0].edges) | |
while n > 4: | |
self.clip(p0) | |
n = len(self.polygons[p0].edges) | |
def clip(self,p0): | |
num_edges = len(self.edges) | |
es = copy.deepcopy(self.polygons[p0].edges) | |
v0 = self.edges[es[0]].v0 | |
vs = self.get_vertex_list(v0,es[0],p0) | |
n = len(es)/2 | |
v0 = self.edges[es[1]].v0 | |
v1 = self.edges[es[n+1]].v0 | |
e3 = self.new_edge(v0,v1) | |
if e3 < num_edges: | |
#sanity check | |
#oops, try the other vertices | |
v0 = self.edges[es[1]].v1 | |
v1 = self.edges[es[n+1]].v1 | |
e3 = self.new_edge(v0,v1) | |
# make a new worm out of the edges | |
p = self.new_polygon(es[0],1) | |
self.remove_from_polygon(p0,es[0]) | |
self.add_to_polygon(p,es[1]) | |
self.remove_from_polygon(p0,es[1]) | |
self.add_to_polygon(p,e3) | |
self.add_to_polygon(p,es[n+1]) | |
self.remove_from_polygon(p0,es[n+1]) | |
self.polygons[p0].edges = [e3] + self.polygons[p0].edges | |
s = self.convert_to_shapely_polygon(p) | |
self.shapely_polygons.append(s) | |
self.shapely_polygons[p0] = self.convert_to_shapely_polygon(p0) | |
def find_equal_slope_edge(self,v0,e0,p0): | |
es = self.get_edge_list(v0,e0,p0) | |
vs = self.get_vertex_list(v0,e0,p0) | |
if vs == False or len(vs) < 3: | |
# sanity check | |
return None | |
v0 = vs[0] | |
v1 = vs[1] | |
vax = v1[0] | |
vay = v1[1] | |
dx0 = vax - v0[0] | |
dy0 = vay - v0[1] | |
i = 1 | |
for v in vs[2:]: | |
dx = vax - v[0] | |
dy = vay - v[1] | |
if abs(dx) < 0.0001 and abs(dx0) < 0.0001: | |
return es[i] | |
elif abs(dx-dx0) < 0.0001 and abs(dy-dy0) < 0.0001: | |
return es[i] | |
vax = v[0] | |
vay = v[1] | |
i += 1 | |
dx = v0[0] - vax | |
dy = v0[1] - vay | |
if abs(dx) < 0.0001 and abs(dx0) < 0.0001: | |
return es[i] | |
elif abs(dx-dx0) < 0.0001 and abs(dy-dy0) < 0.0001: | |
return es[i] | |
return None | |
def rhombic_triangle(self,m): | |
sa = 1.0/(3*m) | |
self.start_polygon(-pi*sa) | |
stn = -1 | |
for i in range(2,m): | |
stn = stn + i - 1 | |
stn2 = 2*stn | |
a = -pi*i*sa | |
self.extend_edge(stn+i-1,stn2+1,[a]) | |
for n in range(0,i-2): | |
self.extend_edge(stn+n,stn2+2*(n+1),[a]) | |
self.extend_edge(stn+2*i-1,stn2+2*(i-1),[a]) | |
i = m | |
stn = stn + i - 1 | |
stn2 = 2*stn | |
self.extend_edge(stn+i-1,stn2+1,[3]) | |
for n in range(0,i-2): | |
self.extend_edge(stn+n,stn2+2*(n+1),[3]) | |
self.extend_edge(stn+2*i-1,stn2+2*(i-1),[3]) | |
def fan(self,m): | |
sa = 1.0/(m) | |
self.start_polygon(-pi*sa) | |
stn = -1 | |
for i in range(2,m): | |
stn = stn + i - 1 | |
stn2 = 2*stn | |
a = -pi*i*sa | |
self.extend_edge(stn+i-1,stn2+1,[a]) | |
for n in range(0,i-2): | |
self.extend_edge(stn+n,stn2+2*(n+1),[a]) | |
self.extend_edge(stn+2*i-1,stn2+2*(i-1),[a]) | |
def universal_tiler_tu(self,n,decomp_polygon): | |
self.rhombic_triangle(n) | |
tri = copy.deepcopy(self) | |
v0 = (n*(n-1) + 4*n)/2 | |
e0 = (2*n*(n-1) + 6*n)/2 -1 | |
inc = len(self.edges) - n | |
for i in range(0,5): | |
self.merge(v0,e0+i*inc,tri,v0,e0-1) | |
incv = v0 - n | |
for i in range(0,5): | |
if i == 0: | |
extra = 0 | |
else: | |
extra = 1 | |
self.merge(v0-1+i*incv+extra, inc-1+i*inc+extra,tri,v0-1,inc-2) | |
self.merge(0, inc-1+5*inc,tri,v0-1,inc-2) | |
self.merge(0,2*(inc-1+5*inc),decomp_polygon,0,0) | |
self.merge(v0-1,14*n+7*n*(n-1)-2,decomp_polygon,0,0) | |
def get_decompose_data(self,v0,e0,p0): | |
es0 = self.get_edge_list(v0,e0,p0) | |
if len(es0) < 2: | |
#sanity check | |
print "unable to get decompose data" | |
sys.exit() | |
es0a = es0[1] | |
v0a = self.get_other_vertex(v0,es0a) | |
es0b = es0[-1] | |
v0b = self.get_other_vertex(v0,es0b) | |
return [[v0a,es0a,-1],[v0b,es0b,1]] | |
# given adjacent edges, find a common vertex | |
def get_common_vertex(self,e0,e1): | |
e0 = self.edges[e0] | |
e1 = self.edges[e1] | |
if e0.v0 == e1.v0: | |
return e0.v0 | |
elif e0.v0 == e1.v1: | |
return e0.v0 | |
elif e0.v1 == e1.v0: | |
return e0.v1 | |
elif e0.v1 == e1.v1: | |
return e0.v1 | |
else: | |
return False | |
def decompose_zonagon(self,p0,as_worms=True): | |
n = len(self.polygons[p0].edges) | |
if n % 2 == 1: | |
#sanity check, this is not a zonagon! | |
return | |
for i in range(0,(n/2)-2): | |
e0 = self.polygons[p0].edges[0] | |
v0 = self.edges[e0].v0 | |
es = self.get_edge_list(v0,e0,p0) | |
va = self.get_common_vertex(es[0],es[1]) | |
d0 = self.get_decompose_data(va, es[0],p0) | |
self.split(d0[0][0],d0[0][1],p0) | |
p1 = self.pc - 1 | |
p = self.polygons[p1] | |
if as_worms: | |
p.ptype = 1 | |
else: | |
self.clip_all(p1) | |
def get_sym_edge_index(self,i,symclasses): | |
for j in range(0,len(symclasses)): | |
if i == symclasses[j]: | |
return j | |
return False | |
def get_split_data(self,v0,e0,p0,p1,do_extra_inc=False): | |
es0 = self.get_edge_list(v0,e0,p0) | |
n = len(es0) | |
extra_inc = 0 | |
if n % 2 == 0: | |
m = (n/2)-1 | |
if do_extra_inc: | |
extra_inc = 1 | |
else: | |
m = n/2 | |
es1 = self.polygons[p1].edges | |
found = False | |
# seek along the p0 edges starting with e0 | |
# until we find an edge that is in p1 | |
for i in range(1,n): | |
if es0[i] in es1: | |
found = True | |
break | |
if not found: | |
#sanity check | |
print "Error: could not find edge in get_split_data" | |
sys.exit() | |
i0 = i | |
# now try the same thing but in the other direction | |
for i in range(1,n): | |
if es0[n-i] in es1: | |
found = True | |
break | |
if not found: | |
#sanity check | |
print "Error: could not find edge in get_split_data" | |
sys.exit() | |
i1 = i | |
# we have found our split edges in both directions, | |
# so return the results | |
if i0 > 0: | |
i0b = i0 - 1 | |
else: | |
i0b = n - 1 | |
if i0 < n - 1: | |
i0a = i0 + 1 | |
else: | |
i0a = 0 | |
if i1 > 0: | |
i1a = i1 - 1 | |
else: | |
i1a = n - 1 | |
if i1 < n - 1: | |
i1b = i1 + 1 | |
else: | |
i1b = 0 | |
va = self.get_common_vertex(es0[i0a],es0[i0]) | |
r0 = [va,es0[i0],m-i0+1,es0[i0a]-es0[i0]] | |
vb = self.get_common_vertex(es0[n-i1a-1],es0[n-i1-1]) | |
r1 = [vb,es0[n-i1],m-i1+1+extra_inc,es0[i1]-es0[i1b]] | |
return [r0,r1] | |
def decompose_polygon(self,p0,n,small_order,position_function=position_opposite,eliminate_sym_classes=True): | |
# By default, this function tries to arrange the | |
# small order regular polygons in rings consisting of rhombs | |
# and two regular polygons of opposite symmetry classes. | |
# If the large order polygon has an even number of sides, | |
# this ensures that the polygon inside each ring is | |
# a zonagon and so can optionally be decomposed into rhombs. | |
if n == 0: | |
return | |
large_order = len(self.polygons[p0].edges) | |
s0 = large_order/small_order | |
sym_lookup = position_function(large_order,small_order) | |
if small_order % 2 == 0: | |
if eliminate_sym_classes and (large_order == n*small_order): | |
m1 = n - 1 | |
else: | |
m1 = n | |
else: | |
if large_order == n*small_order: | |
m1 = n - 1 | |
else: | |
m1 = n | |
print sym_lookup | |
for i in range(0,m1): | |
vs,es,symclasses = self.report(p0,large_order,small_order) | |
qi0 = self.get_sym_edge_index(sym_lookup[i],symclasses) | |
e0 = self.polygons[p0].edges[qi0] | |
v0 = self.edges[e0].v0 | |
if qi0 == 0: | |
qi1 = len(es)-1 | |
else: | |
qi1 = qi0 - 1 | |
va = self.get_common_vertex(es[qi0],es[qi1]) | |
self.punch(va, es[qi0],small_order) | |
p1 = self.pc - 1 | |
r = self.get_split_data(va,es[qi0],p1,p0,eliminate_sym_classes) | |
for k in range(0,2): | |
s,f,m1,inc = r[k] | |
for p in range(0,m1): | |
self.split(s+p*inc,f+p*inc,p0) | |
self.report(p0,large_order,small_order) | |
def clip_all_worms(self): | |
# clip the worms into rhombs | |
num_polygons = self.pc | |
for p in range(1,num_polygons): | |
poly = self.polygons[p] | |
if len(poly.edges) > 4 and poly.ptype == 1: | |
self.clip_all(p) | |
# a specialized function that creates and decomposes a regular polygon | |
# with 6n sides where n is odd. | |
def decompose6_odd(self,n): | |
#self.start_polygon(6*n) | |
m = n/2 | |
self.punch(0,0,n) | |
p0 = self.pc - 1 | |
self.punch(3*n+1,3*n,n) | |
p1 = p0 + 1 | |
d0 = self.get_decompose_data(0,0,p0) | |
d1 = self.get_decompose_data(3*n+1,3*n,p1) | |
d = [d0[0],d1[0],d1[1],d0[1]] | |
for i in range(0,4): | |
s,f,inc = d[i] | |
for p in range(0,m): | |
self.split(s+p*inc,f+p*inc,0) | |
self.report(0,6*n,n) | |
e0 = self.polygons[0].edges[0] | |
v0 = self.edges[e0].v0 | |
es = self.get_edge_list(v0,e0,0) | |
va = self.get_common_vertex(es[n-1],es[n]) | |
vb = self.get_common_vertex(es[3*n-1],es[3*n]) | |
self.punch(va,es[n-1],n) | |
p0 = self.pc - 1 | |
self.punch(vb,es[3*n-1],n) | |
p1 = p0 + 1 | |
d0 = self.get_decompose_data(va,es[n-1],p0) | |
d1 = self.get_decompose_data(vb,es[3*n-1],p1) | |
d = [d0[1],d1[1],d0[0],d1[0]] | |
for i in range(0,4): | |
s,f,inc = d[i] | |
for p in range(0,m): | |
self.split(s+p*inc,f+p*inc,0) | |
self.report(0,6*n,n) | |
e0 = self.polygons[0].edges[0] | |
v0 = self.edges[e0].v0 | |
es = self.get_edge_list(v0,e0,0) | |
va = self.get_common_vertex(es[n+m],es[n+m+1]) | |
self.punch(va, es[n+m],n) | |
p0 = self.pc - 1 | |
d0 = self.get_decompose_data(va, es[n+m],p0) | |
d = [d0[0],d0[1]] | |
for i in range(0,2): | |
s,f,inc = d[i] | |
for p in range(0,m): | |
self.split(s+p*inc,f+p*inc,0) | |
def complete_at_vertex(self,v,alist,external,parity,species,rspecies): | |
success = True | |
vraw = [self.vertices[v].x,self.vertices[v].y] | |
e0 = self.get_poly_edge_from_edge(external) | |
results = [] | |
new_polygons = [] | |
for n in alist: | |
if self.insufficient_gap(v,n): | |
success = False | |
break | |
if n > 0: | |
r = compute_polygon(vraw,e0,n,parity) | |
else: | |
r = compute_rhombus(vraw,e0,-n,parity) | |
if overlaps_polygons(r[0], self.shapely_polygons+new_polygons): | |
if v not in []: | |
success = False | |
break | |
e0 = get_other_edge(vraw,e0,r[0]) | |
if len(e0) <= 1: | |
success = True | |
break | |
#so far, so good | |
results.append([n,r]) | |
# add to temporary shapely list | |
spoly = [] | |
for e in r[0]: | |
spoly.append(e[0]) | |
new_polygons.append(shapely.geometry.Polygon(spoly)) | |
if success: | |
# it is possible to add all the polygons in the list without overlap, | |
# so now we will attempt to add them to the tiling | |
#save state | |
state = [self.vc,self.ec,self.pc] | |
for poly in results: | |
n = poly[0] | |
r = poly[1] | |
poly_edges = r[0] | |
root_edge = r[1] | |
a = r[2] | |
e0 = self.get_edge_from_poly_edge(root_edge) | |
p = self.new_polygon(e0,n) | |
vl = self.create_polygon(e0,p,poly_edges) | |
# now attempt to add the sweep angles to the vertices | |
if n > 0: | |
for vi in vl: | |
vertex = self.vertices[vi] | |
vertex.sweep_lu[p] = n | |
vertex.sweep_plu[p] = n | |
if not self.validate_sweep(vi,species,rspecies): | |
success = False | |
vb = vi | |
break | |
else: | |
# rhombuses have two angles, so the sweep code is a bit more complex | |
if v == vl[0] or v == vl[2]: | |
ang = [a,pi-a,a,pi-a] | |
else: | |
ang = [pi-a,a,pi-a,a] | |
for i in range(0,len(vl)): | |
vertex = self.vertices[vl[i]] | |
vertex.sweep_lu[p] = -ang[i] | |
if ang[i] > (pi/2): | |
vertex.sweep_plu[p] = int(2*pi/(pi-ang[i])+0.5) | |
else: | |
vertex.sweep_plu[p] = -int((2*pi/ang[i])+0.5) | |
if not self.validate_sweep(vl[i],species,rspecies): | |
success = False | |
vb = vl[i] | |
break | |
if success: | |
self.add_to_shapely_polygons(poly_edges) | |
else: | |
# backtrack | |
sb = self.get_sweep(vb) | |
self.vc,self.ec,self.pc = state | |
self.vertices = self.vertices[:self.vc] | |
self.edges = self.edges[:self.ec] | |
self.polygons = self.polygons[:self.pc] | |
self.shapely_polygons = self.shapely_polygons[:self.pc] | |
self.clean_sweeps() | |
break | |
return success | |
def get_external_edges(self,v): | |
ev = [] | |
for e in self.edges: | |
if (e.v0 == v) or (e.v1 == v): | |
if not e.internal: | |
ev.append(e.num) | |
if len(ev) < 3: | |
return ev | |
# We have too many edges so need to determine which | |
# ones are really external. If an edge belongs to | |
# two polygons, it is internal. | |
ep = {} | |
for p in self.polygons: | |
for ei in ev: | |
if ei in p.edges: | |
if ei not in ep: | |
ep[ei] = [] | |
ep[ei].append(p.num) | |
ee = [] | |
for ei in ev: | |
if ei in ep: | |
if len(ep[ei]) == 2: | |
# TODO: make the next line work with backtracking | |
#edges[ei].internal = True | |
pass | |
else: | |
ee.append(ei) | |
return ee | |
def get_leading_edge2(self,v,gap): | |
ee = self.get_external_edges(v) | |
if len(ee) == 0: | |
return [0,[],-1] | |
elif len(ee) == 1: | |
ne = self.edges[ee[0]] | |
nv0 = self.vertices[ne.v0] | |
nv1 = self.vertices[ne.v1] | |
return [0,[ne.v1,ne.v0],ne.num] | |
e0 = self.edges[ee[0]] | |
e1 = self.edges[ee[1]] | |
if e0.v0 == v: | |
v00 = self.vertices[e0.v1] | |
v01 = self.vertices[v] | |
else: | |
v00 = self.vertices[e0.v0] | |
v01 = self.vertices[v] | |
if e1.v0 == v: | |
v11 = self.vertices[e1.v1] | |
v10 = self.vertices[v] | |
else: | |
v11 = self.vertices[e1.v0] | |
v10 = self.vertices[v] | |
if abs(gap-pi) <= 0.001: | |
# need to resolve ambiguity | |
v2 = self.get_previous_vertex(v,ee[0]) | |
vx = self.vertices[v2].x | |
vy = self.vertices[v2].y | |
ab = angle_between([v00.x-v01.x,v00.y-v01.y],[vx-v10.x,vy-v10.y]) | |
if ab > 0: | |
return [ab,[v00.num,v],e0.num] | |
else: | |
return [ab,[v,v00.num],e0.num] | |
else: | |
ab = angle_between([v00.x-v01.x,v00.y-v01.y],[v11.x-v10.x,v11.y-v10.y]) | |
if abs(abs(ab) - gap) < 0.001: | |
if ab > 0: | |
return [ab,[v,v00.num],e0.num] | |
else: | |
return [ab,[v00.num,v],e0.num] | |
else: | |
if ab < 0: | |
return [ab,[v,v00.num],e0.num] | |
else: | |
return [ab,[v00.num,v],e0.num] | |
def insufficient_gap(self,v,n): | |
if n < 0: | |
ang = -n | |
else: | |
ang = (pi / n) * (n-2) | |
#ang = d[n]['p'] | |
gap = self.compute_circle_gap_at_vertex(v) | |
return ang - gap > 0.001 | |
# make sure that the proposed tiling is legal | |
# at both ends of an edge | |
def verify_external(self,v,e): | |
if self.edges[e].internal: | |
return False | |
v2 = self.get_other_vertex(v,e) | |
gap2 = self.compute_circle_gap_at_vertex(v2) | |
return gap2 > 0 | |
def dump_polygon(self,p): | |
print p, str(len(self.polygons[p].edges)) + "-gon" | |
for e in self.polygons[p].edges: | |
v0 = self.vertices[edges[e].v0] | |
v1 = self.vertices[edges[e].v1] | |
print "Edge[",e,"]:",self.edges[e].internal,self.edges[e].v0,[v0.x,v0.y],self.edges[e].v1,[v1.x,v1.y] | |
def get_other_vertex(self,v,e): | |
edge = self.edges[e] | |
if edge.v0 != v: | |
return edge.v0 | |
else: | |
return edge.v1 | |
def get_sweep(self,v): | |
sweep = [] | |
for p in self.vertices[v].sweep_p: | |
sweep.append(self.vertices[v].sweep_lu[p]) | |
return sweep | |
def get_psweep(self,v): | |
sweep = [] | |
for p in self.vertices[v].sweep_p: | |
sweep.append(self.vertices[v].sweep_plu[p]) | |
return sweep | |
def extend_edge(self,v,e,plist): | |
external = [self.edges[e].v0,self.edges[e].v1] | |
if not self.complete_at_vertex(v,plist,external,1,[],[]): | |
print "unable to complete vertex",v,"in extend_edge" | |
def add_to_vertices(self,sweep,plist): | |
for vi in range(0,self.vc): | |
self.add_to_vertex(vi,sweep,plist) | |
def clean_sweeps(self): | |
for v in self.vertices: | |
new_e = [] | |
vertex = self.vertices[v.num] | |
for e in vertex.sweep_e: | |
if e < self.ec: | |
new_e.append(e) | |
vertex.sweep_e = new_e | |
new_p = [] | |
for p in vertex.sweep_p: | |
if p < self.pc: | |
new_p.append(p) | |
else: | |
if p in vertex.sweep_lu: | |
del vertex.sweep_lu[p] | |
vertex.sweep_p = new_p | |
def clean_sweeps2(self,current_vc,todo): | |
for v in self.vertices: | |
new_e = [] | |
vertex = self.vertices[v.num] | |
for e in vertex.sweep_e: | |
if e < self.ec: | |
new_e.append(e) | |
vertex.sweep_e = new_e | |
new_p = [] | |
for p in vertex.sweep_p: | |
if p < self.pc: | |
new_p.append(p) | |
else: | |
if p in vertex.sweep_lu: | |
del vertex.sweep_lu[p] | |
if len(vertex.sweep_p) != len(new_p): | |
todo.append(v.num) | |
vertex.sweep_p = new_p | |
todo2 = [] | |
for i in todo: | |
if i < vc and i not in todo2: | |
todo2.append(i) | |
return todo2 | |
def insert_slope_class(self,n,dx,dy,ec): | |
ec2 = [] | |
found = False | |
for [dx1,dy1,nums] in ec: | |
if abs(dx1) < 0.0001 and abs(dx) < 0.0001: | |
ec2.append([dx1,dy1,nums + [n]]) | |
found = True | |
elif abs(dx) >= 0.0001 and abs(dx1) >= 0.0001: | |
if abs((dy/dx)-(dy1/dx1)) < 0.0001: | |
ec2.append([dx1,dy1,nums + [n]]) | |
found = True | |
else: | |
ec2.append([dx1,dy1,nums]) | |
else: | |
ec2.append([dx1,dy1,nums]) | |
if not found: | |
ec2.append([dx,dy,[n]]) | |
return ec2 | |
def get_angle_data(self,large_order,small_order,v0,vs): | |
if large_order % 2 == 0: | |
r = large_order/(2*pi) | |
else: | |
r = large_order/pi | |
n = large_order/small_order | |
v0 = self.vertices[v0] | |
v0 = [v0.x,v0.y] | |
va = v0 | |
ec = [] | |
angles = [] | |
symclasses = [] | |
i = 0 | |
for v in vs[1:]: | |
dx = v[0] - va[0] | |
dy = v[1] - va[1] | |
a = r*atan2(dy,dx) | |
angles.append(a) | |
if a > 0: | |
s = int(a+0.5) % n | |
else: | |
s = int(a-0.5) % n | |
symclasses.append(s) | |
ec = self.insert_slope_class(i,dx,dy,ec) | |
va = v | |
i += 1 | |
dx = v0[0] - va[0] | |
dy = v0[1] - va[1] | |
a = r*atan2(dy,dx) | |
angles.append(a) | |
if a > 0: | |
s = int(a+0.5) % n | |
else: | |
s = int(a-0.5) % n | |
symclasses.append(s) | |
ec = self.insert_slope_class(i,dx,dy,ec) | |
return [angles, symclasses,ec] | |
def get_data(self,p0,large_order,small_order): | |
e0 = self.polygons[p0].edges[0] | |
v0 = self.edges[e0].v0 | |
es = self.get_edge_list(v0,e0,p0) | |
vs = self.get_vertex_list(v0,e0,p0) | |
[angles, symclasses,ec] = self.get_angle_data(large_order,small_order,v0,vs) | |
return [vs,es,symclasses] | |
def report(self,p0,large_order,small_order): | |
"""A version of get_data which prints out the information as well""" | |
print "****** Polygon report ******" | |
print "Polygon",p0,"has",len(self.polygons[p0].edges),"edges" | |
e0 = self.polygons[p0].edges[0] | |
v0 = self.edges[e0].v0 | |
es = self.get_edge_list(v0,e0,p0) | |
print "edge list",es | |
vs = self.get_vertex_list(v0,e0,p0) | |
print "vertex list",vs | |
[angles, symclasses,ec] = self.get_angle_data(large_order,small_order,v0,vs) | |
print "with angles",angles | |
print "with symclasses",symclasses | |
print "and",len(ec),"slope classes",ec | |
return [vs,es,symclasses] | |
def merge(self,v0,e0,t1,v1,e1): | |
theta = self.get_merge_rotate(v0,e0,t1,v1,e1) | |
vxi,vyi = self.get_merge_translate(v0,e0,t1,v1,e1) | |
for p in t1.polygons: | |
self.merge_polygon(v0,t1,p,vxi,vyi,theta) | |
def get_merge_translate(self,v0,e0,t1,v1,e1): | |
v0x = self.vertices[v0].x | |
v0y = self.vertices[v0].y | |
v1x = t1.vertices[v1].x | |
v1y = t1.vertices[v1].y | |
return (v0x-v1x), (v0y-v1y) | |
def get_merge_rotate(self,v0,e0,t1,v1,e1): | |
vxi,vyi = self.get_merge_translate(v0,e0,t1,v1,e1) | |
# get the other vertices | |
v0other = self.get_other_vertex(v0,e0) | |
v1other = t1.get_other_vertex(v1,e1) | |
#translate the other vertices (mapping v0 onto (0,0)) | |
v0x = self.vertices[v0other].x - self.vertices[v0].x | |
v0y = self.vertices[v0other].y - self.vertices[v0].y | |
v1x = t1.vertices[v1other].x + vxi - self.vertices[v0].x | |
v1y = t1.vertices[v1other].y + vyi - self.vertices[v0].y | |
#return the angle between the other vertices | |
return angle_between([v1x,v1y],[v0x,v0y]) | |
def merge_polygon(self,v0,t1,p,vxi,vyi,theta): | |
# get the vertices and map them into the new coordinates | |
# we first use vxi, vyi to translate t2 coordinates into self | |
# coordinates, then rotate the new polygons around | |
# v0x, voy by theta radians | |
v0x = self.vertices[v0].x | |
v0y = self.vertices[v0].y | |
edges = [] | |
for ei in p.edges: | |
v0 = t1.vertices[t1.edges[ei].v0] | |
v1 = t1.vertices[t1.edges[ei].v1] | |
x0,y0 = rotate([v0.x+vxi-v0x,v0.y+vyi-v0y],theta) | |
x1,y1 = rotate([v1.x+vxi-v0x,v1.y+vyi-v0y],theta) | |
v0n = self.new_vertex([x0+v0x,y0+v0y]) | |
v1n = self.new_vertex([x1+v0x,y1+v0y]) | |
edges.append(self.new_edge(v0n,v1n)) | |
pedges = [] | |
for x,y in list(t1.shapely_polygons[p.num].exterior.coords): | |
x0,y0 = rotate([x+vxi-v0x,y+vyi-v0y],theta) | |
pedges.append([x0+v0x,y0+v0y]) | |
# we have the edges, so create the new polygon | |
p0 = self.new_polygon(edges[0],p.ptype,False) | |
for ei in edges[1:]: | |
self.add_to_polygon(p0,ei) | |
self.add_to_shapely_polygons2(pedges) | |
def toSVG(self,include_labels=False): | |
xml = """<?xml version="1.0" encoding="UTF-8" standalone="no"?> | |
<svg width="1500" height="1500" | |
xmlns:dc="http://purl.org/dc/elements/1.1/" | |
xmlns:cc="http://web.resource.org/cc/" | |
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" | |
xmlns:svg="http://www.w3.org/2000/svg" | |
xmlns="http://www.w3.org/2000/svg"> | |
> | |
<defs> | |
<style type="text/css"> | |
<![CDATA[ | |
text { | |
font-family: Arial, sans-serif; | |
font-size:2; | |
background-color:#ffff00; | |
fill:#ff0000; | |
} | |
]]> | |
</style> | |
</defs> | |
""" | |
for p in range(0,self.pc): | |
xml += self.polygon_to_svg(p) | |
if include_labels: | |
xml += self.labels_to_svg() | |
xml += "</svg>" | |
return xml | |
class Vertex(object): | |
"""Vertex class with public x, y, num and sweep attributes """ | |
def __init__(self,x,y): | |
self.x = x | |
self.y = y | |
self.num = 0 | |
self.sweep = [] | |
self.sweep_e = [] | |
self.sweep_p = [] | |
self.sweep_lu = {} | |
self.sweep_plu = {} | |
self.parity = 0 | |
class Edge(object): | |
"""Edge class with public v0, v1, num and internal attributes """ | |
def __init__(self,v0,v1,internal=False): | |
self.v0 = v0 | |
self.v1 = v1 | |
self.num = 0 | |
self.internal = False | |
class Polygon(object): | |
"""Polygon class with num, edges list and polygon/rhombus type """ | |
def __init__(self,e0,ptype,make_internal=True): | |
self.edges = [e0] | |
self.ptype = ptype | |
self.num = 0 | |
class LineSegment(object): | |
"""line segment is an object with four values x1, y1, x2,y2""" | |
def __init__(self,x1,y1,x2,y2): | |
self.x1 = x1 | |
self.y1 = y1 | |
self.x2 = x2 | |
self.y2 = y2 | |
def compute_polygon(vs,e0,n,parity=1): | |
if n < 0: | |
return compute_rhombus(vs,e0,n,parity) | |
if parity == 1: | |
v0 = e0[1] | |
v1 = e0[0] | |
else: | |
v0 = e0[0] | |
v1 = e0[1] | |
a = pi/n | |
#compute the edges | |
r = 1/(2*sin(a)) | |
r_internal = r*cos(a) | |
# compute edge angle | |
ea = atan2(v1[1]-v0[1],v1[0]-v0[0]) | |
phi = a - ea | |
x = 0.5 | |
y = -r_internal | |
xc, yc = rotate([x,y],ea) | |
centre = [xc+v0[0], yc+v0[1]] | |
poly_edges = get_polygon_edges(r,centre,phi,n) | |
return [poly_edges,e0,a] | |
#compute a new rhombus given an edge and an angle | |
# and add it to the end of the polygon list | |
# rewrite and add correct sweep bit plus fix angles | |
def compute_rhombus(vs,e0,a,parity=1): | |
if parity == 1: | |
v0 = e0[0] | |
v1 = e0[1] | |
else: | |
v0 = e0[1] | |
v1 = e0[0] | |
# the following trick generates the correct rhombus shape based | |
# upon the vertex ordering (vertex vs should always have angle a) | |
if equal_vertex(vs,v0): | |
flip = True | |
a2 = a | |
else: | |
flip = True | |
a2 = pi - a | |
# compute edge angle | |
ea = atan2(v1[1]-v0[1],v1[0]-v0[0]) | |
h = cos(0.5*(pi-a2)) | |
w = sin(0.5*(pi-a2)) | |
vlist = [[-w,0],[0,-h],[w,0],[0,h]] | |
rot = [] | |
for v in vlist: | |
x0 = v[0]+w | |
y0 = v[1] | |
r = rotate([x0,y0],ea+(a2/2)) | |
x1 = r[0]+v0[0] | |
y1 = r[1]+v0[1] | |
rot.append([x1,y1]) | |
if flip: | |
pe = [[rot[1],rot[0]],[rot[2],rot[1]],[rot[3],rot[2]],[rot[0],rot[3]]] | |
else: | |
pe = [[rot[0],rot[1]],[rot[1],rot[2]],[rot[2],rot[3]],[rot[3],rot[0]]] | |
return [pe,e0,a] | |
def overlaps_polygons(pl,sp,ignore=False): | |
p0 = [] | |
for e in pl: | |
p0.append(e[0]) | |
poly = shapely.geometry.Polygon(p0) | |
i = 0 | |
for p in sp: | |
try: | |
if poly.intersects(p): | |
pintersect = poly.intersection(p) | |
if (type(pintersect) is shapely.geometry.polygon.Polygon) and pintersect.area > 0.01: | |
return True | |
except: | |
print "overlap intersect function error" | |
return True | |
i += 1 | |
return False | |
# compute a new regular polygon given an edge and an order | |
# and add it to the end of the polygon list | |
def rotate(v,theta): | |
xc = cos(theta) * v[0] - sin(theta) * v[1] | |
yc = sin(theta) * v[0] + cos(theta) * v[1] | |
return xc, yc | |
def get_polygon_edges(r,centre,phi,n): | |
pe = [] | |
p0 = [centre[0]+r*sin(phi),centre[1]+ r*cos(phi)] | |
for i in range(1,n): | |
p1 = [centre[0]+r*sin(phi+(2*i*pi/n)), centre[1]+ r*cos(phi+(2*i*pi/n))] | |
pe.append([p0,p1]) | |
p0 = p1 | |
pe.append([p1,pe[0][0]]) | |
return pe | |
def different_vertex(v0,v1): | |
return (abs(v0[0] - v1.x) > 0.0001) or (abs(v0[1] - v1.y) > 0.0001) | |
def equal_vertex(v0,v1): | |
return (abs(v0[0] - v1[0]) < 0.0001) and (abs(v0[1] - v1[1]) < 0.0001) | |
def different_edge(v0,v1,e): | |
return (v0 != e.v0 or v1 != e.v1) and (v0 != e.v1 or v1 != e.v0) | |
def angle_between(v0,v1): | |
return atan2(v0[0]*v1[1] - v0[1]*v1[0], v0[0]*v1[0]+v0[1]*v1[1]) | |
# edges are equal if the vertices are equal in either order | |
def equal_poly_edges(e,et): | |
t0 = equal_vertex(e[0],et[0]) and equal_vertex(e[1],et[1]) | |
t1 = equal_vertex(e[0],et[1]) and equal_vertex(e[1],et[0]) | |
return t0 or t1 | |
def convert_sweep(s): | |
s2 = [] | |
for a in s: | |
if a > 0: | |
s2.append("p"+str(a)) | |
else: | |
s2.append("r"+str(-180*a/pi)) | |
return s2 | |
def rh(n,m=2): | |
if n > 0: | |
return -m*pi/n | |
else: | |
n = -n | |
return (-(n-m)*pi/n) | |
def get_plist_from_sweep(sweep,sp): | |
lsp = len(sp) | |
for i in range(0,lsp): | |
sp0 = i | |
sp1 = i | |
spi = i | |
plist = [] | |
success = True | |
for a in sweep: | |
if abs(sp[spi] - a) < 0.0001: | |
spi += 1 | |
if spi >= lsp: | |
spi = 0 | |
sp1 = spi | |
else: | |
success = False | |
break | |
if success: | |
break | |
if success: | |
if sp1 <= sp0: | |
return sp[sp1:sp0] | |
else: | |
return sp[sp1:]+sp[:sp0] | |
else: | |
return [] | |
def equal_sweep(s0,s1): | |
if len(s0) == len(s1): | |
for i in range(0,len(s0)): | |
if abs(s0[i]-s1[i]) > 0.0001: | |
return False | |
return True | |
return False | |
# v - raw vertex | |
# et - raw root edge | |
# poly_edges - list of raw polygon edges | |
def get_other_edge(v,et,poly_edges): | |
for e in poly_edges: | |
if not equal_poly_edges(e,et) and (equal_vertex(e[0],v) or equal_vertex(e[1],v)): | |
return e | |
return [] | |
### You will want to change the code after this point | |
# hendecagon tiling example | |
# You can change n below to generate a translational unit | |
# for a tiling containing any n-gon | |
print "Computing parts for translational unit." | |
print "Decomposing 6n polygon." | |
n = 11 | |
t = Tiling() | |
t.start_polygon(6*n) | |
#t.decompose6_odd(n) | |
t.decompose_polygon(0,6,n,position_6) | |
#t.decompose_zonagon(0) | |
t.clip_all_worms() | |
decomp = copy.deepcopy(t) | |
print "Computing translational unit. This may take some time." | |
t = Tiling() | |
t.universal_tiler_tu(n,decomp) | |
print t.pc, "polygons" | |
xml = t.toSVG() | |
# You need to change where the results are saved. | |
fd = open("d:/tess/output.svg","w") | |
fd.write(xml) | |
fd.close() | |
print "Tiling output completed." |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment