diff --git a/src/command_prompt.py b/src/command_prompt.py index 9c5569d..54712a5 100644 --- a/src/command_prompt.py +++ b/src/command_prompt.py @@ -345,6 +345,9 @@ class Command_prompt(Label): elif argv[1]=="grid": self.run_set_grid(argv) return + elif argv[1]=="voronoi": + self.run_set_voronoi(argv) + return elif argv[1]=="zoom": self.run_set_zoom(argv) else: @@ -409,6 +412,19 @@ class Command_prompt(Label): return 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) def run_set_zoom(self,argv): if len(argv)==2: diff --git a/src/element.py b/src/element.py index 04d2070..816e907 100644 --- a/src/element.py +++ b/src/element.py @@ -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) )) + # 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 - # TODO: this only works if element is a square! + # TODO: this only works if element is a rectangle! def check_interaction(self,element): # 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 @@ -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 # 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): # 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): @@ -114,7 +126,7 @@ class Element_square(Element): return False # 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): size_x=(self.size+element.size)/2 size_y=(self.size*self.aspect+element.size*element.aspect)/2 @@ -160,7 +172,7 @@ class Element_square(Element): return closest-element.pos # 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): size_x=(self.size+element.size)/2 size_y=(self.size*self.aspect+element.size*element.aspect)/2 diff --git a/src/lattice.py b/src/lattice.py index f241255..5c8eb09 100644 --- a/src/lattice.py +++ b/src/lattice.py @@ -73,4 +73,22 @@ class Lattice_square(Lattice): return (Lattice_square(spacing=spacing),"") except: 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 diff --git a/src/painter.py b/src/painter.py index 94b5b4a..cae39cb 100644 --- a/src/painter.py +++ b/src/painter.py @@ -18,7 +18,7 @@ import sys import math from kivy.uix.widget import Widget from kivy.core.window import Window -from kivy.graphics import Color,Line +from kivy.graphics import Color,Line,Rectangle from point import Point from polyomino import Cross,Disk @@ -127,6 +127,12 @@ class Painter(Widget): if particle.grid>0: 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: 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))) 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)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)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)