-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathvoronoi.py
190 lines (169 loc) · 7.24 KB
/
voronoi.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
'''Functions for creating voronoi regions'''
from PIL import Image, ImageDraw
import sys
def tuple1st(tup):
return tup[0]
def lineFrmPnts(p1,p2):
"""Returns slope and y-intercept"""
slpe = slope(p1,p2)
intercept = p2[1]-(slpe*p2[0])
return slpe,intercept
def slope(p1,p2):
if(p1 == p2):
return 0
elif p2[0]-p1[0] == 0:
return sys.maxsize
else:
return (p2[1]-p1[1])/(p2[0]-p1[0])
def pntSlpe(pnt,slope):
"""Returns y-intercept of line"""
return (pnt[1]-slope*pnt[0])
def invSlpe(slp):
try:
return -1/slp
except ZeroDivisionError:
sys.exit('Do not enter equidistant points. This results in an infinite slope')
def midpoint(p1,p2):
return ((p1[0]+p2[0])/2,(p1[1]+p2[1])/2)
def distance(p1,p2):
return ((p2[1] - p1[1])**2 + (p2[0] - p1[0])**2)**.5
def getX(ele):
return ele[0][0]
def sortAdjs(adjList):
srted = sorted(adjList,key = getX)
return srted
def Y2X(y,b,m):
return (y-b)/m
def checkAllDistances(p1,p2,line,points):
correctDist = (distance(p1,line) + distance(p2,line))/2
for i,point in enumerate(points):
if( correctDist > distance(line,point)#if closer to another point
and point != p1#and that point not orig pnt or clsst you're checking
and point != p2): #against
return False #because they ran into vertex[0]'s area
return True
def insideImg(point,dims):
#if 0 <= x <= maxX and 0 <= y <= maxY
return (point[0] <= dims[0] and point[0] >= 0
and point[1] <= dims[1] and point[1] >= 0)
def changeCoord(pnt,dims,orig,clsst,slope,intercept,dir,points):
'''Returns two points, first and last valid points of line in direction dir'''
x,y = pnt
validx1 = 0
validx2 = 0
validy1 = 0
validy2 = 0
i = 0
while(insideImg((x,y),dims)#start checking if pnt in frame, not valid
and not(checkAllDistances(orig,clsst,(x,y),points))):
x += 1*dir
y= slope*x + intercept
while (insideImg((x,y),dims)#if in frame and valid
and checkAllDistances(orig,clsst,(x,y),points)):
if i == 0:#first valid point
validx1,validy1 = x,y
i += 1
x += 1*dir
y= slope*x + intercept
#validx2,validy2 = x - 1*dir,slope*(x - 1*dir) + intercept#last valid point
validx2,validy2 = x,y#last invalid point
return ((validx1,validy1),(validx2,validy2))
def incDrawLines(points,dims):
'''Given list of point tuples and dims of image,
return image with voronoi lines drawn on it'''
img = Image.new('RGBA', dims, color = 'white')
draw = ImageDraw.Draw(img)
regions = [list() for point in points]
for i, point in enumerate(points):
draw.point(point,fill = 'red')
for otherPoint in points:
if point == otherPoint:
continue
midpnt = midpoint(point,otherPoint)
x,y = midpnt
#slope and intercept for line bet midoint and orig vertex
slope,intercept = lineFrmPnts(point,midpnt)
#slope = round(slope,1)
slope = invSlpe(slope)#get normal to slope
intercept = pntSlpe(midpnt,slope)#get y-int for normal to slope
pnt1Pos,pnt2Pos = changeCoord((x,y),dims,point,otherPoint,slope,intercept,1,points)
pnt1Neg,pnt2Neg = changeCoord((x,y),dims,point,otherPoint,slope,intercept,-1,points)
pnt1Pos = (round(pnt1Pos[0]),round(pnt1Pos[1]))
pnt2Pos = (round(pnt2Pos[0]),round(pnt2Pos[1]))
pnt1Neg = (round(pnt1Neg[0]),round(pnt1Neg[1]))
pnt2Neg = (round(pnt2Neg[0]),round(pnt2Neg[1]))
if(pnt1Pos != pnt2Pos and pnt1Neg != pnt2Neg):
if(pnt1Pos != (0,0) and pnt2Pos != (0,0)):
draw.line([pnt1Pos,pnt2Pos],fill = 'black', width = 1)
regions[i].append((pnt1Pos,pnt2Pos))
if(pnt1Neg != (0,0) and pnt2Neg != (0,0)):
draw.line([pnt1Neg,pnt2Neg],fill = 'black', width = 1)
regions[i].append((pnt1Neg,pnt2Neg))
return img,regions
def createCollage(imgList,regionList,points):
'''Returns new image'''
#imgList: list of PIL image objects
#imgList size should be equal to regionList size
#each img in imgList needs to be of same size
#regionList: list of region lists
#will have same size as points
#each region in regionList defines 1 region
#obtain regionList from 2nd returned value of incDrawLines
#points: list of user specified points; [(x1,y1),(x2,y2),...]
#All points should fall within specified dimensions
#NOTE: when creating images and regions, use the same dimensions for both
#this ensures the regions allign and fill up the given images
if len(imgList) != len(regionList) or len(regionList) != len(points):
raise ValueError('imgList or regionList or points not of same size',
len(imgList),len(regionList),len(points))
finalImg = imgList[0]
for i,img in enumerate(imgList):
imgTemp = outlineRegion(img,regionList[i],points[i])
finalImg = Image.alpha_composite(finalImg,imgTemp)
return finalImg
def outlineRegion(img,region,point):
'''Outlines given region on given img, returns new image'''
#img: PIL img class,should be the img you want region
#to be cut out of
#all imgs used for given final img should be of same size
#does not alter image, returns new image
#region: list of tuples, tuples consist of start and end point
#[((x1,y1),(x2,y2)),...]
#point: coresponding voronoi point to the defined region
imgCopy = img.copy()
draw = ImageDraw.Draw(imgCopy)
dims = imgCopy.size
for line in region:
slope,intercept = lineFrmPnts(line[0],line[1])
offset = min(line[0][0],line[1][0])
interval = abs(line[0][0] - line[1][0])
y = slope*point[0] + intercept
if y > point[1]:#need to erase downwards
if slope < 0:#need to erase to the right
for i in range(-1,interval+1):
x = i + offset
y = slope*x + intercept
draw.rectangle([(x+1,y+1),(dims[0],dims[1])],fill = (0,0,0,0))
elif slope > 0:#erase to the Left
for i in range(-1,interval+1):
x = i + offset
y = slope*x + intercept
draw.rectangle([(x-1,y+1),(0,dims[1])],fill = (0,0,0,0))
else: #erase left and right (slope = 0)
y = intercept
draw.rectangle([(0,intercept),(dims[0],dims[1])],fill = (0,0,0,0))
else:#erase upwards
if slope < 0:#erase to the left
for i in range(-1,interval+1):
x = i + offset
y = slope*x + intercept
draw.rectangle([(x-1,y-1),(0,0)],fill = (0,0,0,0))
elif slope > 0:#erase to the right
for i in range(-1,interval+1):
x = i + offset
y = slope*x + intercept
draw.rectangle([(x+1,y-1),(dims[0],0)],fill = (0,0,0,0))
else: #erase left and right(slope = 0)
y = intercept
draw.rectangle([(0,intercept),(dims[0],0)],fill = (0,0,0,0))
return imgCopy