Allow square element to be a rectangle
This commit is contained in:
parent
dd5917cd45
commit
e5b6019606
@ -3,7 +3,7 @@ import math
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
from point import Point,l_infinity,l_2
|
from point import Point,l_infinity,l_2
|
||||||
from tools import isint_nonzero,sgn,in_interval
|
from tools import isint_nonzero,sgn,in_interval,ceil_grid,floor_grid
|
||||||
from kivy.graphics import Rectangle,Ellipse,Line
|
from kivy.graphics import Rectangle,Ellipse,Line
|
||||||
|
|
||||||
# parent class of all elements
|
# parent class of all elements
|
||||||
@ -55,11 +55,17 @@ class Element():
|
|||||||
return element
|
return element
|
||||||
|
|
||||||
|
|
||||||
# square elements
|
# rectangular element
|
||||||
|
# the size of the y component is specified by an aspect ratio: size_x=size, size_y=size*aspect
|
||||||
class Element_square(Element):
|
class Element_square(Element):
|
||||||
|
def __init__(self,x,y,size,**kwargs):
|
||||||
|
self.pos=Point(x,y)
|
||||||
|
self.size=size
|
||||||
|
self.aspect=kwargs.get("aspect",1.0)
|
||||||
|
|
||||||
# draw element
|
# draw element
|
||||||
def draw(self,painter):
|
def draw(self,painter):
|
||||||
Rectangle(pos=(painter.pos_tocoord_x(self.pos.x-0.5*self.size),painter.pos_tocoord_y(self.pos.y-0.5*self.size)),size=(self.size*painter.base_size,self.size*painter.base_size))
|
Rectangle(pos=(painter.pos_tocoord_x(self.pos.x-0.5*self.size),painter.pos_tocoord_y(self.pos.y-0.5*self.size*self.aspect)),size=(self.size*painter.base_size,self.size*self.aspect*painter.base_size))
|
||||||
|
|
||||||
# draw boundary
|
# draw boundary
|
||||||
def stroke(self,painter):
|
def stroke(self,painter):
|
||||||
@ -68,53 +74,55 @@ class Element_square(Element):
|
|||||||
coordy=painter.pos_tocoord_y(square.pos.y)
|
coordy=painter.pos_tocoord_y(square.pos.y)
|
||||||
|
|
||||||
Line(points=(
|
Line(points=(
|
||||||
*(coordx-0.5*self.size*painter.base_size,coordy-0.5*self.size*painter.base_size),
|
*(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*painter.base_size),
|
*(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*painter.base_size),
|
*(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*painter.base_size),
|
*(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*painter.base_size)
|
*(coordx-0.5*self.size*painter.base_size,coordy-0.5*self.size*self.aspect*painter.base_size)
|
||||||
))
|
))
|
||||||
|
|
||||||
# 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 square!
|
||||||
def check_interaction(self,element):
|
def check_interaction(self,element):
|
||||||
# allow for error
|
# allow for error
|
||||||
return l_infinity(element.pos-self.pos)<(self.size+element.size)/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
|
||||||
|
|
||||||
# whether x is in the support of the element
|
# whether x is in the support of the element
|
||||||
def in_support(self,x):
|
def in_support(self,x):
|
||||||
return l_infinity(self.pos-x)<=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 square!
|
||||||
def check_touch(self,element):
|
def check_touch(self,element):
|
||||||
# allow for error
|
# allow for error
|
||||||
if in_interval(l_infinity(element.pos-self.pos),(self.size+element.size)/2-1e-11,(self.size+element.size)/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):
|
||||||
return True
|
return True
|
||||||
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 square!
|
||||||
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_y=(self.size*self.aspect+element.size*element.aspect)/2
|
||||||
# compute intersections with four lines making up square
|
# compute intersections with four lines making up square
|
||||||
if v.x!=0:
|
if v.x!=0:
|
||||||
if v.y!=0:
|
if v.y!=0:
|
||||||
intersections=[\
|
intersections=[\
|
||||||
Point(self.pos.x+(self.size+element.size)/2,element.pos.y+v.y/v.x*(self.pos.x+(self.size+element.size)/2-element.pos.x)),\
|
Point(self.pos.x+size_x,element.pos.y+v.y/v.x*(self.pos.x+size_x-element.pos.x)),\
|
||||||
Point(self.pos.x-(self.size+element.size)/2,element.pos.y+v.y/v.x*(self.pos.x-(self.size+element.size)/2-element.pos.x)),\
|
Point(self.pos.x-size_x,element.pos.y+v.y/v.x*(self.pos.x-size_x-element.pos.x)),\
|
||||||
Point(element.pos.x+v.x/v.y*(self.pos.y+(self.size+element.size)/2-element.pos.y),self.pos.y+(self.size+element.size)/2),\
|
Point(element.pos.x+v.x/v.y*(self.pos.y+size_y-element.pos.y),self.pos.y+size_y),\
|
||||||
Point(element.pos.x+v.x/v.y*(self.pos.y-(self.size+element.size)/2-element.pos.y),self.pos.y-(self.size+element.size)/2)\
|
Point(element.pos.x+v.x/v.y*(self.pos.y-size_y-element.pos.y),self.pos.y-size_y)\
|
||||||
]
|
]
|
||||||
else:
|
else:
|
||||||
intersections=[\
|
intersections=[\
|
||||||
Point(self.pos.x+(self.size+element.size)/2,element.pos.y+v.y/v.x*(self.pos.x+(self.size+element.size)/2-element.pos.x)),\
|
Point(self.pos.x+size_x,element.pos.y),\
|
||||||
Point(self.pos.x-(self.size+element.size)/2,element.pos.y+v.y/v.x*(self.pos.x-(self.size+element.size)/2-element.pos.x))
|
Point(self.pos.x-size_x,element.pos.y)
|
||||||
]
|
]
|
||||||
else:
|
else:
|
||||||
if v.y!=0:
|
if v.y!=0:
|
||||||
intersections=[\
|
intersections=[\
|
||||||
Point(element.pos.x+v.x/v.y*(self.pos.y+(self.size+element.size)/2-element.pos.y),self.pos.y+(self.size+element.size)/2),\
|
Point(element.pos.x,self.pos.y+size_y),\
|
||||||
Point(element.pos.x+v.x/v.y*(self.pos.y-(self.size+element.size)/2-element.pos.y),self.pos.y-(self.size+element.size)/2)\
|
Point(element.pos.x,self.pos.y-size_y)\
|
||||||
]
|
]
|
||||||
else:
|
else:
|
||||||
print("error: move_on_line_to_stick called with v=0, please file a bug report with the developer",file=sys.stderr)
|
print("error: move_on_line_to_stick called with v=0, please file a bug report with the developer",file=sys.stderr)
|
||||||
@ -125,13 +133,13 @@ class Element_square(Element):
|
|||||||
dist=math.inf
|
dist=math.inf
|
||||||
for i in range(0,len(intersections)):
|
for i in range(0,len(intersections)):
|
||||||
# check that it is on square
|
# check that it is on square
|
||||||
if abs(intersections[i].x-self.pos.x)<=(self.size+element.size)/2+1e-11 and abs(intersections[i].y-self.pos.y)<=(self.size+element.size)/2+1e-11:
|
if abs(intersections[i].x-self.pos.x)<=size_x+1e-11 and abs(intersections[i].y-self.pos.y)<=size_y+1e-11:
|
||||||
if (intersections[i]-element.pos)**2<dist:
|
if (intersections[i]-element.pos)**2<dist:
|
||||||
closest=intersections[i]
|
closest=intersections[i]
|
||||||
dist=(intersections[i]-element.pos)**2
|
dist=(intersections[i]-element.pos)**2
|
||||||
|
|
||||||
if closest==None:
|
if closest==None:
|
||||||
print("error: cannot move particle at (",pos.x,",",pos.y,") to the boundary of (",self.pos.x,",",self.pos.y,") in direction (",v.x,",",v.y,")",file=sys.stderr)
|
print("error: cannot move particle at (",element.pos.x,",",element.pos.y,") to the boundary of (",self.pos.x,",",self.pos.y,") in direction (",v.x,",",v.y,")",file=sys.stderr)
|
||||||
exit(-1)
|
exit(-1)
|
||||||
|
|
||||||
# return difference to pos
|
# return difference to pos
|
||||||
@ -140,11 +148,13 @@ class Element_square(Element):
|
|||||||
# 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 square!
|
||||||
def move_along(self,delta,element):
|
def move_along(self,delta,element):
|
||||||
|
size_x=(self.size+element.size)/2
|
||||||
|
size_y=(self.size*self.aspect+element.size*element.aspect)/2
|
||||||
rel=element.pos-self.pos
|
rel=element.pos-self.pos
|
||||||
# check if the particle is stuck in the x direction
|
# check if the particle is stuck in the x direction
|
||||||
if isint_nonzero(rel.x/((self.size+element.size)/2)):
|
if isint_nonzero(rel.x/size_x):
|
||||||
# check y direction
|
# check y direction
|
||||||
if isint_nonzero(rel.y/((self.size+element.size)/2)):
|
if isint_nonzero(rel.y/size_y):
|
||||||
# in corner
|
# in corner
|
||||||
if sgn(delta.y)==-sgn(rel.y):
|
if sgn(delta.y)==-sgn(rel.y):
|
||||||
# stuck in x direction
|
# stuck in x direction
|
||||||
@ -157,7 +167,7 @@ class Element_square(Element):
|
|||||||
else:
|
else:
|
||||||
# stuck in x direction
|
# stuck in x direction
|
||||||
return self.move_stuck_x(delta,element)
|
return self.move_stuck_x(delta,element)
|
||||||
elif isint_nonzero(rel.y/((self.size+element.size)/2)):
|
elif isint_nonzero(rel.y/size_y):
|
||||||
# stuck in y direction
|
# stuck in y direction
|
||||||
return self.move_stuck_y(delta,element)
|
return self.move_stuck_y(delta,element)
|
||||||
# this should never happen
|
# this should never happen
|
||||||
@ -166,33 +176,35 @@ class Element_square(Element):
|
|||||||
exit(-1)
|
exit(-1)
|
||||||
# move when stuck in the x direction
|
# move when stuck in the x direction
|
||||||
def move_stuck_x(self,delta,element):
|
def move_stuck_x(self,delta,element):
|
||||||
|
size_y=(self.size*self.aspect+element.size*element.aspect)/2
|
||||||
# only move in y direction
|
# only move in y direction
|
||||||
candidate=Point(0,delta.y)
|
candidate=Point(0,delta.y)
|
||||||
# do not move past corners
|
# do not move past corners
|
||||||
rel=element.pos.y-self.pos.y
|
rel=element.pos.y-self.pos.y
|
||||||
if delta.y>0:
|
if delta.y>0:
|
||||||
if rel<math.ceil(rel/((self.size+element.size)/2))*((self.size+element.size)/2)-1e-11 and delta.y+rel>math.ceil(rel/((self.size+element.size)/2))*((self.size+element.size)/2)+1e-11 and math.ceil(rel/((self.size+element.size)/2))*((self.size+element.size)/2)!=0:
|
if rel<ceil_grid(rel,size_y)-1e-11 and delta.y+rel>ceil_grid(rel,size_y)+1e-11 and ceil_grid(rel,size_y)!=0:
|
||||||
# stick to corner
|
# stick to corner
|
||||||
candidate.y=math.ceil(rel/((self.size+element.size)/2))*((self.size+element.size)/2)+self.pos.y-element.pos.y
|
candidate.y=ceil_grid(rel,size_y)+self.pos.y-element.pos.y
|
||||||
else:
|
else:
|
||||||
if rel>math.floor(rel/((self.size+element.size)/2))*((self.size+element.size)/2)+1e-11 and delta.y+rel<math.floor(rel/((self.size+element.size)/2))*((self.size+element.size)/2)-1e-11 and math.floor(rel/((self.size+element.size)/2))*((self.size+element.size)/2)!=0:
|
if rel>floor_grid(rel,size_y)+1e-11 and delta.y+rel<floor_grid(rel,size_y)-1e-11 and floor_grid(rel,size_y)!=0:
|
||||||
# stick to corner
|
# stick to corner
|
||||||
candidate.y=math.floor(rel/((self.size+element.size)/2))*((self.size+element.size)/2)+self.pos.y-element.pos.y
|
candidate.y=floor_grid(rel,size_y)+self.pos.y-element.pos.y
|
||||||
return candidate
|
return candidate
|
||||||
# move when stuck in the y direction
|
# move when stuck in the y direction
|
||||||
def move_stuck_y(self,delta,element):
|
def move_stuck_y(self,delta,element):
|
||||||
|
size_x=(self.size+element.size)/2
|
||||||
# onlx move in x direction
|
# onlx move in x direction
|
||||||
candidate=Point(delta.x,0)
|
candidate=Point(delta.x,0)
|
||||||
# do not move past corners
|
# do not move past corners
|
||||||
rel=element.pos.x-self.pos.x
|
rel=element.pos.x-self.pos.x
|
||||||
if delta.x>0:
|
if delta.x>0:
|
||||||
if rel<math.ceil(rel/((self.size+element.size)/2))*((self.size+element.size)/2)-1e-11 and delta.x+rel>math.ceil(rel/((self.size+element.size)/2))*((self.size+element.size)/2)+1e-11 and math.ceil(rel/((self.size+element.size)/2))*((self.size+element.size)/2)!=0:
|
if rel<ceil_grid(rel,size_x)-1e-11 and delta.x+rel>ceil_grid(rel,size_x)+1e-11 and ceil_grid(rel,size_x)!=0:
|
||||||
# stick to corner
|
# stick to corner
|
||||||
candidate.x=math.ceil(rel/((self.size+element.size)/2))*((self.size+element.size)/2)+self.pos.x-element.pos.x
|
candidate.x=ceil_grid(rel,size_x)+self.pos.x-element.pos.x
|
||||||
else:
|
else:
|
||||||
if rel>math.floor(rel/((self.size+element.size)/2))*((self.size+element.size)/2)+1e-11 and delta.x+rel<math.floor(rel/((self.size+element.size)/2))*((self.size+element.size)/2)-1e-11 and math.floor(rel/((self.size+element.size)/2))*((self.size+element.size)/2)!=0:
|
if rel>floor_grid(rel,size_x)+1e-11 and delta.x+rel<floor_grid(rel,size_x)-1e-11 and floor_grid(rel,size_x)!=0:
|
||||||
# stick to corner
|
# stick to corner
|
||||||
candidate.x=math.floor(rel/((self.size+element.size)/2))*((self.size+element.size)/2)+self.pos.x-element.pos.x
|
candidate.x=floor_grid(rel,size_x)+self.pos.x-element.pos.x
|
||||||
return candidate
|
return candidate
|
||||||
|
|
||||||
|
|
||||||
|
@ -215,8 +215,8 @@ class Painter(Widget):
|
|||||||
|
|
||||||
# create new particle
|
# create new particle
|
||||||
if touch.button=="right":
|
if touch.button=="right":
|
||||||
#new=Cross(touchx,touchy)
|
new=Cross(touchx,touchy)
|
||||||
new=Disk(touchx,touchy)
|
#new=Disk(touchx,touchy)
|
||||||
# snap to lattice
|
# snap to lattice
|
||||||
if self.lattice!=None:
|
if self.lattice!=None:
|
||||||
new.move(self.lattice.nearest_delta(new.elements[0].pos))
|
new.move(self.lattice.nearest_delta(new.elements[0].pos))
|
||||||
@ -529,7 +529,7 @@ class Painter(Widget):
|
|||||||
continue
|
continue
|
||||||
if particle_type==CROSS_INDEX:
|
if particle_type==CROSS_INDEX:
|
||||||
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)
|
||||||
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)
|
||||||
|
@ -75,11 +75,9 @@ class Square(Polyomino):
|
|||||||
class Cross(Polyomino):
|
class Cross(Polyomino):
|
||||||
def __init__(self,x,y,**kwargs):
|
def __init__(self,x,y,**kwargs):
|
||||||
super(Cross,self).__init__(**kwargs,elements=[\
|
super(Cross,self).__init__(**kwargs,elements=[\
|
||||||
Element_square(x,y,1),\
|
Element_square(x,y,1,aspect=3),\
|
||||||
Element_square(x+1,y,1),\
|
Element_square(x+1,y,1),\
|
||||||
Element_square(x-1,y,1),\
|
Element_square(x-1,y,1)\
|
||||||
Element_square(x,y+1,1),\
|
|
||||||
Element_square(x,y-1,1)\
|
|
||||||
])
|
])
|
||||||
|
|
||||||
# redefine stroke to avoid lines between touching elements
|
# redefine stroke to avoid lines between touching elements
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import math
|
||||||
|
|
||||||
# sign function
|
# sign function
|
||||||
def sgn(x):
|
def sgn(x):
|
||||||
if x>=0:
|
if x>=0:
|
||||||
@ -20,3 +22,10 @@ def remove_fromlist(a,x):
|
|||||||
a[a.index(x)]=a[len(a)-1]
|
a[a.index(x)]=a[len(a)-1]
|
||||||
a=a[:len(a)-1]
|
a=a[:len(a)-1]
|
||||||
return a
|
return a
|
||||||
|
|
||||||
|
# snap to a grid: ceiling
|
||||||
|
def ceil_grid(x,size):
|
||||||
|
return math.ceil(x/size)*size
|
||||||
|
# snap to a grid: floor
|
||||||
|
def floor_grid(x,size):
|
||||||
|
return math.floor(x/size)*size
|
||||||
|
Loading…
Reference in New Issue
Block a user