Compute discrete Voronoi cell
This commit is contained in:
		| @@ -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 | ||||||
|   | |||||||
| @@ -73,4 +73,22 @@ class Lattice_square(Lattice): | |||||||
|             return (Lattice_square(spacing=spacing),"") |             return (Lattice_square(spacing=spacing),"") | ||||||
|         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) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user