Compute discrete Voronoi cell
This commit is contained in:
parent
0070094b94
commit
fd61a4620f
@ -345,6 +345,9 @@ class Command_prompt(Label):
|
|||||||
elif argv[1]=="grid":
|
elif argv[1]=="grid":
|
||||||
self.run_set_grid(argv)
|
self.run_set_grid(argv)
|
||||||
return
|
return
|
||||||
|
elif argv[1]=="voronoi":
|
||||||
|
self.run_set_voronoi(argv)
|
||||||
|
return
|
||||||
elif argv[1]=="zoom":
|
elif argv[1]=="zoom":
|
||||||
self.run_set_zoom(argv)
|
self.run_set_zoom(argv)
|
||||||
else:
|
else:
|
||||||
@ -409,6 +412,19 @@ class Command_prompt(Label):
|
|||||||
return
|
return
|
||||||
self.app.painter.set_grid(mesh)
|
self.app.painter.set_grid(mesh)
|
||||||
|
|
||||||
|
# toggle Voronoi cells
|
||||||
|
def run_set_voronoi(self,argv):
|
||||||
|
if len(argv)==2:
|
||||||
|
# no argument: set to toggle
|
||||||
|
self.app.painter.set_voronoi(-1)
|
||||||
|
elif argv[2]=="on":
|
||||||
|
self.app.painter.set_voronoi(1)
|
||||||
|
elif argv[2]=="off":
|
||||||
|
self.app.painter.set_voronoi(0)
|
||||||
|
else:
|
||||||
|
self.message="error: unrecognized argument '"+argv[2]+"' -- usage 'set voronoi [on|off]'"
|
||||||
|
return
|
||||||
|
|
||||||
# set zoom level (changes size of elements)
|
# set zoom level (changes size of elements)
|
||||||
def run_set_zoom(self,argv):
|
def run_set_zoom(self,argv):
|
||||||
if len(argv)==2:
|
if len(argv)==2:
|
||||||
|
@ -95,8 +95,20 @@ class Element_square(Element):
|
|||||||
*(coordx-0.5*self.size*painter.base_size,coordy-0.5*self.size*self.aspect*painter.base_size)
|
*(coordx-0.5*self.size*painter.base_size,coordy-0.5*self.size*self.aspect*painter.base_size)
|
||||||
))
|
))
|
||||||
|
|
||||||
|
# for use with lattices
|
||||||
|
# list of lattice points covered by square
|
||||||
|
def lattice_points(self,lattice):
|
||||||
|
out=[]
|
||||||
|
dx=math.floor(0.5*self.size/lattice.spacing)
|
||||||
|
dy=math.floor(0.5*self.size*self.aspect/lattice.spacing)
|
||||||
|
for i in range(-dx,dx+1):
|
||||||
|
for j in range(-dy,dy+1):
|
||||||
|
out.append(Point(self.pos.x+i*lattice.spacing,self.pos.y+j*lattice.spacing))
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
# check whether an element interacts with square
|
# check whether an element interacts with square
|
||||||
# TODO: this only works if element is a square!
|
# TODO: this only works if element is a rectangle!
|
||||||
def check_interaction(self,element):
|
def check_interaction(self,element):
|
||||||
# allow for error
|
# allow for error
|
||||||
return max(abs(element.pos.x-self.pos.x)/(self.size+element.size),abs(element.pos.y-self.pos.y)/(self.size*self.aspect+element.size*element.aspect))<1/2-1e-11
|
return max(abs(element.pos.x-self.pos.x)/(self.size+element.size),abs(element.pos.y-self.pos.y)/(self.size*self.aspect+element.size*element.aspect))<1/2-1e-11
|
||||||
@ -106,7 +118,7 @@ class Element_square(Element):
|
|||||||
return max(abs(self.pos.x-x.x),abs(self.pos.y-x.y)/self.aspect)<=1/2
|
return max(abs(self.pos.x-x.x),abs(self.pos.y-x.y)/self.aspect)<=1/2
|
||||||
|
|
||||||
# check whether an element is touching self
|
# check whether an element is touching self
|
||||||
# TODO: this only works if element is a square!
|
# TODO: this only works if element is a rectangle!
|
||||||
def check_touch(self,element):
|
def check_touch(self,element):
|
||||||
# allow for error
|
# allow for error
|
||||||
if in_interval(max(abs(element.pos.x-self.pos.x)/(self.size+element.size),abs(element.pos.y-self.pos.y)/(self.size*self.aspect+element.size*element.aspect)),1/2-1e-11,1/2+1e-11):
|
if in_interval(max(abs(element.pos.x-self.pos.x)/(self.size+element.size),abs(element.pos.y-self.pos.y)/(self.size*self.aspect+element.size*element.aspect)),1/2-1e-11,1/2+1e-11):
|
||||||
@ -114,7 +126,7 @@ class Element_square(Element):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
# find position along a line that comes in contact with the line going through element.pos in direction v
|
# find position along a line that comes in contact with the line going through element.pos in direction v
|
||||||
# TODO: this only works if element is a square!
|
# TODO: this only works if element is a rectangle!
|
||||||
def move_on_line_to_stick(self,element,v):
|
def move_on_line_to_stick(self,element,v):
|
||||||
size_x=(self.size+element.size)/2
|
size_x=(self.size+element.size)/2
|
||||||
size_y=(self.size*self.aspect+element.size*element.aspect)/2
|
size_y=(self.size*self.aspect+element.size*element.aspect)/2
|
||||||
@ -160,7 +172,7 @@ class Element_square(Element):
|
|||||||
return closest-element.pos
|
return closest-element.pos
|
||||||
|
|
||||||
# move along edge of square
|
# move along edge of square
|
||||||
# TODO: this only works if element is a square!
|
# TODO: this only works if element is a rectangle!
|
||||||
def move_along(self,delta,element):
|
def move_along(self,delta,element):
|
||||||
size_x=(self.size+element.size)/2
|
size_x=(self.size+element.size)/2
|
||||||
size_y=(self.size*self.aspect+element.size*element.aspect)/2
|
size_y=(self.size*self.aspect+element.size*element.aspect)/2
|
||||||
|
@ -74,3 +74,21 @@ class Lattice_square(Lattice):
|
|||||||
except:
|
except:
|
||||||
return (None,"error: '"+spec+"' is not a valid specification for the square lattice: should be 'square[:spacing]'")
|
return (None,"error: '"+spec+"' is not a valid specification for the square lattice: should be 'square[:spacing]'")
|
||||||
|
|
||||||
|
# distance on the lattice between (x1,x2) and (y1,y2)
|
||||||
|
def distance(self, x1, x2, y1, y2):
|
||||||
|
return round((abs(x1-y1)+abs(x2-y2))/self.spacing)
|
||||||
|
|
||||||
|
# distance between a lattice site and a particle
|
||||||
|
def distance_to_particle(self, x1, x2, particle):
|
||||||
|
mindist=self.distance_to_element(x1, x2, particle.elements[0])
|
||||||
|
for i in range(1,len(particle.elements)):
|
||||||
|
mindist=min(self.distance_to_element(x1, x2, particle.elements[i]),mindist)
|
||||||
|
return mindist
|
||||||
|
# distance between a lattice site and an element
|
||||||
|
def distance_to_element(self, x1, x2, element):
|
||||||
|
pts=element.lattice_points(self)
|
||||||
|
mindist=self.distance(x1, x2, pts[0].x, pts[0].y)
|
||||||
|
for i in range(1,len(pts)):
|
||||||
|
mindist=min(self.distance(x1, x2, pts[i].x, pts[i].y),mindist)
|
||||||
|
return mindist
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ import sys
|
|||||||
import math
|
import math
|
||||||
from kivy.uix.widget import Widget
|
from kivy.uix.widget import Widget
|
||||||
from kivy.core.window import Window
|
from kivy.core.window import Window
|
||||||
from kivy.graphics import Color,Line
|
from kivy.graphics import Color,Line,Rectangle
|
||||||
|
|
||||||
from point import Point
|
from point import Point
|
||||||
from polyomino import Cross,Disk
|
from polyomino import Cross,Disk
|
||||||
@ -127,6 +127,12 @@ class Painter(Widget):
|
|||||||
if particle.grid>0:
|
if particle.grid>0:
|
||||||
self.draw_grid(particle.elements[0].pos,particle.grid)
|
self.draw_grid(particle.elements[0].pos,particle.grid)
|
||||||
|
|
||||||
|
# draw Voronoi cells
|
||||||
|
if self.lattice!=None:
|
||||||
|
for particle in self.particles:
|
||||||
|
if particle.voronoi>0:
|
||||||
|
self.draw_voronoi(particle)
|
||||||
|
|
||||||
for particle in self.particles:
|
for particle in self.particles:
|
||||||
particle.draw(self,alpha=0.5)
|
particle.draw(self,alpha=0.5)
|
||||||
|
|
||||||
@ -160,6 +166,53 @@ class Painter(Widget):
|
|||||||
Line(points=(0,self.pos_tocoord_y(yy),self.width,self.pos_tocoord_y(yy)))
|
Line(points=(0,self.pos_tocoord_y(yy),self.width,self.pos_tocoord_y(yy)))
|
||||||
yy-=mesh
|
yy-=mesh
|
||||||
|
|
||||||
|
# draw the discrete Voronoi cell of a particle
|
||||||
|
def draw_voronoi(self,particle):
|
||||||
|
# only works for lattices
|
||||||
|
if self.lattice!=None:
|
||||||
|
pos=particle.elements[0].pos
|
||||||
|
# loop over all points
|
||||||
|
xx=pos.x
|
||||||
|
while self.pos_tocoord_x(xx)<self.width:
|
||||||
|
yy=pos.y
|
||||||
|
while self.pos_tocoord_y(yy)<self.height:
|
||||||
|
if self.is_in_voronoi(xx,yy,particle):
|
||||||
|
self.draw_voronoi_site(xx,yy,particle.color)
|
||||||
|
yy+=self.lattice.spacing
|
||||||
|
yy=pos.y-self.lattice.spacing
|
||||||
|
while self.pos_tocoord_y(yy)>0:
|
||||||
|
if self.is_in_voronoi(xx,yy,particle):
|
||||||
|
self.draw_voronoi_site(xx,yy,particle.color)
|
||||||
|
yy-=self.lattice.spacing
|
||||||
|
xx+=self.lattice.spacing
|
||||||
|
xx=pos.x-self.lattice.spacing
|
||||||
|
while self.pos_tocoord_x(xx)>0:
|
||||||
|
yy=pos.y
|
||||||
|
while self.pos_tocoord_y(yy)<self.height:
|
||||||
|
if self.is_in_voronoi(xx,yy,particle):
|
||||||
|
self.draw_voronoi_site(xx,yy,particle.color)
|
||||||
|
yy+=self.lattice.spacing
|
||||||
|
yy=pos.y-self.lattice.spacing
|
||||||
|
while self.pos_tocoord_y(yy)>0:
|
||||||
|
if self.is_in_voronoi(xx,yy,particle):
|
||||||
|
self.draw_voronoi_site(xx,yy,particle.color)
|
||||||
|
yy-=self.lattice.spacing
|
||||||
|
xx-=self.lattice.spacing
|
||||||
|
# check whether a site is in the Voronoi cell of a particle
|
||||||
|
def is_in_voronoi(self,x,y,particle):
|
||||||
|
d_to_particle=self.lattice.distance_to_particle(x,y,particle)
|
||||||
|
# TODO: start with a particle that is close to x,y
|
||||||
|
for q in self.particles:
|
||||||
|
if q!=particle and self.lattice.distance_to_particle(x,y,q)<d_to_particle:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
# draw a site in a Voronoi cell
|
||||||
|
def draw_voronoi_site(self,x,y,color):
|
||||||
|
Color(color[0],color[1],color[2],0.25)
|
||||||
|
#Color(1,0,0,alpha=0.25)
|
||||||
|
Rectangle(pos=(self.pos_tocoord_x(x-0.5),self.pos_tocoord_y(y-0.5)),size=(self.base_size,self.base_size))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# respond to keyboard
|
# respond to keyboard
|
||||||
def on_key_down(self, keyboard, keycode, text, modifiers):
|
def on_key_down(self, keyboard, keycode, text, modifiers):
|
||||||
@ -465,6 +518,17 @@ class Painter(Widget):
|
|||||||
# redraw
|
# redraw
|
||||||
self.draw()
|
self.draw()
|
||||||
|
|
||||||
|
# set voronoi for selected particles
|
||||||
|
def set_voronoi(self, onoff):
|
||||||
|
for particle in self.selected:
|
||||||
|
if onoff==0:
|
||||||
|
particle.voronoi=False
|
||||||
|
elif onoff==1:
|
||||||
|
particle.voronoi=True
|
||||||
|
elif onoff==-1:
|
||||||
|
particle.voronoi=not particle.voronoi
|
||||||
|
# redraw
|
||||||
|
self.draw()
|
||||||
|
|
||||||
# write configuration to file
|
# write configuration to file
|
||||||
def write(self,file):
|
def write(self,file):
|
||||||
|
@ -29,6 +29,9 @@ class Polyomino():
|
|||||||
# mesh of background grid (no grid for mesh size 0)
|
# mesh of background grid (no grid for mesh size 0)
|
||||||
self.grid=kwargs.get("grid",0)
|
self.grid=kwargs.get("grid",0)
|
||||||
|
|
||||||
|
# draw Voronoi cell
|
||||||
|
self.voronoi=kwargs.get("voronoi",False)
|
||||||
|
|
||||||
# draw function
|
# draw function
|
||||||
def draw(self,painter,**kwargs):
|
def draw(self,painter,**kwargs):
|
||||||
alpha=kwargs.get("alpha",1)
|
alpha=kwargs.get("alpha",1)
|
||||||
|
Loading…
Reference in New Issue
Block a user