Compare commits

..

12 Commits

Author SHA1 Message Date
c51efebf95 Corrected error message 2024-12-05 11:56:44 -05:00
ea1ac6490a Fix write: color 2024-02-26 10:26:16 -05:00
3d681da551 Export other shapes 2024-02-26 10:04:57 -05:00
5864dc5c06 Coarse grain mouse input with lattice 2024-02-26 10:01:50 -05:00
a152af6bac Fix open empty file 2024-02-24 10:44:11 -05:00
121b8fe4b0 2squares 2024-02-22 17:19:52 -05:00
1cd4e0fdc6 Fix lattice_points for disk 2024-02-22 17:08:42 -05:00
a48457de29 Adjust color of voronoi depending on number of neighbors 2024-02-22 16:50:04 -05:00
4a6d2a1758 More visible Voronoi cells 2024-02-22 16:42:57 -05:00
29355191d6 Staircases 2024-02-22 16:41:45 -05:00
7e44418829 voronoi cells for disks 2024-02-22 16:31:10 -05:00
fd61a4620f Compute discrete Voronoi cell 2024-02-21 19:12:07 -05:00
7 changed files with 248 additions and 16 deletions

View File

@ -28,3 +28,21 @@
\fill[color=#1]#2++(0.5,0.5)--++(0,1)--++(-1,0)--++(0,-1)--++(-1,0)--++(0,-1)--++(1,0)--++(0,-1)--++(1,0)--++(0,1)--++(1,0)--++(0,1)--++(-1,0); \fill[color=#1]#2++(0.5,0.5)--++(0,1)--++(-1,0)--++(0,-1)--++(-1,0)--++(0,-1)--++(1,0)--++(0,-1)--++(1,0)--++(0,1)--++(1,0)--++(0,1)--++(-1,0);
\draw[color=black]#2++(0.5,0.5)--++(0,1)--++(-1,0)--++(0,-1)--++(-1,0)--++(0,-1)--++(1,0)--++(0,-1)--++(1,0)--++(0,1)--++(1,0)--++(0,1)--++(-1,0); \draw[color=black]#2++(0.5,0.5)--++(0,1)--++(-1,0)--++(0,-1)--++(-1,0)--++(0,-1)--++(1,0)--++(0,-1)--++(1,0)--++(0,1)--++(1,0)--++(0,1)--++(-1,0);
} }
% 3-staircase (color #1, position #2)
\def\staircase#1#2{
\fill[color=#1]#2++(-0.5,-0.5)--++(3,0)--++(0,1)--++(-1,0)--++(0,1)--++(-1,0)--++(0,1)--++(-1,0)--++(0,-3);
\draw[color=black]#2++(-0.5,-0.5)--++(3,0)--++(0,1)--++(-1,0)--++(0,1)--++(-1,0)--++(0,1)--++(-1,0)--++(0,-3);
}
% disk (color #1, position #2)
\def\disk#1#2{
\fill[color=#1]#2circle(2.5);
\draw[color=black]#2circle(2.5);
}
% square (color #1, position #2)
\def\square#1#2{
\fill[color=#1]#2++(-1,-1)--++(0,2)--++(2,0)--++(0,-2)--cycle;
\draw[color=black]#2++(-1,-1)--++(0,2)--++(2,0)--++(0,-2)--cycle;
}

View File

@ -20,7 +20,7 @@ import os.path
import filecheck import filecheck
import colors import colors
from polyomino import Cross,Disk from polyomino import Cross,Disk,Staircase,Square2
class Command_prompt(Label): class Command_prompt(Label):
@ -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:
@ -385,8 +388,12 @@ class Command_prompt(Label):
self.app.painter.shape=Cross self.app.painter.shape=Cross
elif argv[2]=="disk": elif argv[2]=="disk":
self.app.painter.shape=Disk self.app.painter.shape=Disk
elif argv[2]=="staircase":
self.app.painter.shape=Staircase
elif argv[2]=="2square":
self.app.painter.shape=Square2
else: else:
self.message="error: unrecognized shape '"+argv[2]+"'; supported shapes are cross|disk" self.message="error: unrecognized shape '"+argv[2]+"'; supported shapes are cross|disk|staircase|2square"
return return
# toggle grid # toggle grid
@ -409,6 +416,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:

View File

@ -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
@ -270,3 +282,16 @@ class Element_circle(Element):
def move_along(self,delta,element): def move_along(self,delta,element):
x=element.pos-self.pos+delta x=element.pos-self.pos+delta
return x/l_2(x)*(element.size+self.size)/2+self.pos-element.pos return x/l_2(x)*(element.size+self.size)/2+self.pos-element.pos
# 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)
for i in range(-dx,dx+1):
for j in range(-dx,dx+1):
if lattice.spacing*lattice.spacing*(i*i+j*j)<=self.size*self.size/4:
out.append(Point(self.pos.x+i*lattice.spacing,self.pos.y+j*lattice.spacing))
return out

View File

@ -61,10 +61,10 @@ def read_cli():
else: else:
openfile=arg openfile=arg
(ret,message)=filecheck.check_edit(openfile) (ret,message)=filecheck.check_edit(openfile)
preread_conf(openfile)
if ret<0: if ret<0:
print(message,file=sys.stderr) print(message,file=sys.stderr)
exit(-1) exit(-1)
preread_conf(openfile)
# read command line arguments from configuration file # read command line arguments from configuration file
def preread_conf(file): def preread_conf(file):
@ -72,8 +72,7 @@ def preread_conf(file):
try: try:
ff=open(file,"r") ff=open(file,"r")
except: except:
print("error: could not read file '"+file+"' (this should not happen and is probably a bug)", file=sys.stderr) return
exit(-1)
# counter # counter
i=0 i=0

View File

@ -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

View File

@ -18,10 +18,10 @@ 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,Staircase,Square2
from tools import remove_fromlist from tools import remove_fromlist
@ -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,55 @@ 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:
self.draw_voronoi_site(xx,yy,particle.color,self.is_in_voronoi(xx,yy,particle))
yy+=self.lattice.spacing
yy=pos.y-self.lattice.spacing
while self.pos_tocoord_y(yy)>0:
self.draw_voronoi_site(xx,yy,particle.color,self.is_in_voronoi(xx,yy,particle))
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:
self.draw_voronoi_site(xx,yy,particle.color,self.is_in_voronoi(xx,yy,particle))
yy+=self.lattice.spacing
yy=pos.y-self.lattice.spacing
while self.pos_tocoord_y(yy)>0:
self.draw_voronoi_site(xx,yy,particle.color,self.is_in_voronoi(xx,yy,particle))
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)
# count how many are in voronoi cell
count=1
# TODO: start with a particle that is close to x,y
for q in self.particles:
dd=self.lattice.distance_to_particle(x,y,q)
if q!=particle and dd<d_to_particle:
return 0
if dd==d_to_particle:
count+=1
return count
# draw a site in a Voronoi cell
def draw_voronoi_site(self,x,y,color,count):
if count==0:
return
Color(color[0],color[1],color[2],1-count*0.1)
Rectangle(pos=(self.pos_tocoord_x(x-0.5*self.lattice.spacing),self.pos_tocoord_y(y-0.5*self.lattice.spacing)),size=(self.base_size*self.lattice.spacing,self.base_size*self.lattice.spacing))
# respond to keyboard # respond to keyboard
def on_key_down(self, keyboard, keycode, text, modifiers): def on_key_down(self, keyboard, keycode, text, modifiers):
@ -260,6 +315,9 @@ class Painter(Widget):
# record relative position of click with respect to reference # record relative position of click with respect to reference
if self.undermouse!=None: if self.undermouse!=None:
self.offset=Point(touchx,touchy)-self.undermouse.elements[0].pos self.offset=Point(touchx,touchy)-self.undermouse.elements[0].pos
# snap to lattice
if self.lattice!=None:
self.offset=self.lattice.nearest(self.offset)
# no modifiers # no modifiers
if self.modifiers==[]: if self.modifiers==[]:
@ -306,13 +364,17 @@ class Painter(Widget):
# only respond to touch in drawing area # only respond to touch in drawing area
if self.collide_point(*touch.pos): if self.collide_point(*touch.pos):
# convert to logical # convert to logical
touchx=self.coord_topos_x(touch.x) touchc=Point(self.coord_topos_x(touch.x),self.coord_topos_y(touch.y))
touchy=self.coord_topos_y(touch.y)
# snap to lattice
if self.lattice!=None:
touchc=self.lattice.nearest(touchc)
# only move on left click # only move on left click
if touch.button=="left" and self.modifiers==[] and self.undermouse!=None: if touch.button=="left" and self.modifiers==[] and self.undermouse!=None:
# attempted move determined by the relative position to the relative position of click within self.undermouse # attempted move determined by the relative position to the relative position of click within self.undermouse
delta=self.adjust_move(Point(touchx,touchy)-(self.offset+self.undermouse.elements[0].pos),0) delta=self.adjust_move(touchc-(self.offset+self.undermouse.elements[0].pos),0)
# snap to lattice # snap to lattice
if self.lattice!=None: if self.lattice!=None:
@ -465,6 +527,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):
@ -472,10 +545,16 @@ class Painter(Widget):
# save state (particle shape, zoom, lattice) # save state (particle shape, zoom, lattice)
if self.shape==Cross: if self.shape==Cross:
ff.write("%shape=cross\n") ff.write("%shape=cross\n")
else: elif self.shape==Disk:
ff.write("%shape=disk\n") ff.write("%shape=disk\n")
elif self.shape==Staircase:
ff.write("%shape=staircase\n")
elif self.shape==Square2:
ff.write("%shape=2square\n")
else:
print("bug: unrecognized shape in write: '"+str(self.shape)+"'")
ff.write("%zoom={:1.1f}\n".format(self.base_size/50)) ff.write("%zoom={:1.1f}\n".format(self.base_size/50))
ff.write("%color={:d},{:d},{:d}\n".format(self.color[0],self.color[1],self.color[2])) ff.write("%color={:1.1f},{:1.1f},{:1.1f}\n".format(self.color[0],self.color[1],self.color[2]))
if self.lattice != None: if self.lattice != None:
ff.write("%lattice="+self.lattice.type+':'+str(self.lattice.spacing)+"\n") ff.write("%lattice="+self.lattice.type+':'+str(self.lattice.spacing)+"\n")
for particle in self.particles: for particle in self.particles:
@ -483,6 +562,10 @@ class Painter(Widget):
ff.write("{:d};".format(CROSS_INDEX)) ff.write("{:d};".format(CROSS_INDEX))
elif type(particle)==Disk: elif type(particle)==Disk:
ff.write("{:d};".format(DISK_INDEX)) ff.write("{:d};".format(DISK_INDEX))
elif type(particle)==Staircase:
ff.write("{:d};".format(STAIRCASE_INDEX))
elif type(particle)==Square2:
ff.write("{:d};".format(SQUARE2_INDEX))
ff.write("{:05.2f},{:05.2f};{:3.1f},{:3.1f},{:3.1f}\n".format(particle.elements[0].pos.x,particle.elements[0].pos.y,particle.color[0],particle.color[1],particle.color[2])) ff.write("{:05.2f},{:05.2f};{:3.1f},{:3.1f},{:3.1f}\n".format(particle.elements[0].pos.x,particle.elements[0].pos.y,particle.color[0],particle.color[1],particle.color[2]))
ff.close() ff.close()
@ -585,6 +668,10 @@ class Painter(Widget):
candidate=Cross(pos.x,pos.y,color=color) candidate=Cross(pos.x,pos.y,color=color)
elif particle_type==DISK_INDEX: elif particle_type==DISK_INDEX:
candidate=Disk(pos.x,pos.y,color=color) candidate=Disk(pos.x,pos.y,color=color)
elif particle_type==STAIRCASE_INDEX:
candidate=Staircase(pos.x,pos.y,color=color)
elif particle_type==SQUARE2_INDEX:
candidate=Square2(pos.x,pos.y,color=color)
else: else:
print("warning: ignoring line "+str(i)+" in file '"+file+"': unrecognized particle type: '"+entries[0]+"'",file=sys.stderr) print("warning: ignoring line "+str(i)+" in file '"+file+"': unrecognized particle type: '"+entries[0]+"'",file=sys.stderr)
continue continue
@ -621,6 +708,12 @@ class Painter(Widget):
for particle in self.particles: for particle in self.particles:
if type(particle)==Cross: if type(particle)==Cross:
ff.write("\cross{"+colors.closest_color(particle.color,colors.xcolor_names)+"}") ff.write("\cross{"+colors.closest_color(particle.color,colors.xcolor_names)+"}")
elif type(particle)==Disk:
ff.write("\disk{"+colors.closest_color(particle.color,colors.xcolor_names)+"}")
elif type(particle)==Staircase:
ff.write("\staircase{"+colors.closest_color(particle.color,colors.xcolor_names)+"}")
elif type(particle)==Square2:
ff.write("\square{"+colors.closest_color(particle.color,colors.xcolor_names)+"}")
ff.write("{{({:05.2f},{:05.2f})}};\n".format(particle.elements[0].pos.x-self.particles[0].elements[0].pos.x,particle.elements[0].pos.y-self.particles[0].elements[0].pos.y)) ff.write("{{({:05.2f},{:05.2f})}};\n".format(particle.elements[0].pos.x-self.particles[0].elements[0].pos.x,particle.elements[0].pos.y-self.particles[0].elements[0].pos.y))
ff.write("\\end{tikzpicture}\n") ff.write("\\end{tikzpicture}\n")
@ -638,5 +731,7 @@ class Painter(Widget):
# global variables (used like precompiler variables) # global variables (used like precompiler variables)
CROSS_INDEX=1 CROSS_INDEX=1
DISK_INDEX=2 DISK_INDEX=2
STAIRCASE_INDEX=3
SQUARE2_INDEX=4

View 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)
@ -120,3 +123,57 @@ class Cross(Polyomino):
class Disk(Polyomino): class Disk(Polyomino):
def __init__(self,x,y,**kwargs): def __init__(self,x,y,**kwargs):
super(Disk,self).__init__(**kwargs,elements=[Element_circle(x,y,size=kwargs.get("size",1.0))]) super(Disk,self).__init__(**kwargs,elements=[Element_circle(x,y,size=kwargs.get("size",1.0))])
# 3-staircase
class Staircase(Polyomino):
def __init__(self,x,y,**kwargs):
super(Staircase,self).__init__(**kwargs,elements=[\
Element_square(x,y+1,1,aspect=3),\
Element_square(x+1,y,1),\
Element_square(x+1,y+1,1),\
Element_square(x+2,y,1)\
])
# redefine stroke to avoid lines between touching elements
def stroke(self,painter):
# convert to graphical coordinates
coordx=painter.pos_tocoord_x(self.elements[0].pos.x)
coordy=painter.pos_tocoord_y(self.elements[0].pos.y)
Color(1,1,1)
Line(points=(
*(coordx-0.5*painter.base_size,coordy-1.5*painter.base_size),
*(coordx+2.5*painter.base_size,coordy-1.5*painter.base_size),
*(coordx+2.5*painter.base_size,coordy-0.5*painter.base_size),
*(coordx+1.5*painter.base_size,coordy-0.5*painter.base_size),
*(coordx+1.5*painter.base_size,coordy+0.5*painter.base_size),
*(coordx+0.5*painter.base_size,coordy+0.5*painter.base_size),
*(coordx+0.5*painter.base_size,coordy+1.5*painter.base_size),
*(coordx-0.5*painter.base_size,coordy+1.5*painter.base_size),
*(coordx-0.5*painter.base_size,coordy-1.5*painter.base_size),
))
# 2-square
class Square2(Polyomino):
def __init__(self,x,y,**kwargs):
super(Square2,self).__init__(**kwargs,elements=[\
Element_square(x,y,1),\
Element_square(x+1,y,1),\
Element_square(x,y+1,1),\
Element_square(x+1,y+1,1)\
])
# redefine stroke to avoid lines between touching elements
def stroke(self,painter):
# convert to graphical coordinates
coordx=painter.pos_tocoord_x(self.elements[0].pos.x)
coordy=painter.pos_tocoord_y(self.elements[0].pos.y)
Color(1,1,1)
Line(points=(
*(coordx-0.5*painter.base_size,coordy-0.5*painter.base_size),
*(coordx+1.5*painter.base_size,coordy-0.5*painter.base_size),
*(coordx+1.5*painter.base_size,coordy+1.5*painter.base_size),
*(coordx-0.5*painter.base_size,coordy+1.5*painter.base_size),
*(coordx-0.5*painter.base_size,coordy-0.5*painter.base_size),
))