129 lines
4.6 KiB
Python
129 lines
4.6 KiB
Python
|
import math
|
||
|
import sys
|
||
|
from kivy.graphics import Color,Line,Rectangle
|
||
|
|
||
|
from point import Point,l_infinity
|
||
|
from tools import isint_nonzero,sgn,in_interval
|
||
|
|
||
|
class Square():
|
||
|
# size
|
||
|
size=50
|
||
|
|
||
|
def __init__(self,x,y,**kwargs):
|
||
|
self.pos=Point(x,y)
|
||
|
self.color=kwargs.get("color",(0,0,1))
|
||
|
self.selected=False
|
||
|
|
||
|
# set position
|
||
|
def setpos(self,x,y):
|
||
|
self.pos.x=x
|
||
|
self.pos.y=y
|
||
|
|
||
|
def draw(self,**kwargs):
|
||
|
# set color
|
||
|
if not self.selected:
|
||
|
Color(*self.color)
|
||
|
else:
|
||
|
(r,g,b)=self.color
|
||
|
# darken selected
|
||
|
Color(r/2,g/2,b/2)
|
||
|
|
||
|
Rectangle(pos=((self.pos.x-0.5)*self.size,(self.pos.y-0.5)*self.size),size=(self.size,self.size))
|
||
|
|
||
|
# check whether a square at pos interacts with square
|
||
|
def check_interaction(self,pos):
|
||
|
return l_infinity(pos-self.pos)>=1
|
||
|
|
||
|
# check whether a square at position pos is touching self
|
||
|
def check_touch(self,pos):
|
||
|
# allow for error
|
||
|
if in_interval(l_infinity(pos-solf.pos),1-1e-11,1+1e-11):
|
||
|
return True
|
||
|
return False
|
||
|
|
||
|
# find position along a line that comes in contact with the line going through pos in direction v
|
||
|
def move_on_line_to_stick(self,pos,v):
|
||
|
# compute intersections with four lines making up square
|
||
|
intersections=[\
|
||
|
Point(self.pos.x+1/2,pos.y+v.y/v.x*(self.pos.x+1/2-pos.x)),\
|
||
|
Point(self.pos.x-1/2,pos.y+v.y/v.x*(self.pos.x-1/2-pos.x)),\
|
||
|
Point(pos.x+v.x/v.y*(self.pos.y+1/2-pos.y),self.pos.y+1/2),\
|
||
|
Point(pos.x+v.x/v.y*(self.pos.y-1/2-pos.y),self.pos.y-1/2)\
|
||
|
]
|
||
|
|
||
|
# compute closest one, on square
|
||
|
closest=None
|
||
|
dist=math.inf
|
||
|
for i in range(0,4):
|
||
|
# check that it is on square
|
||
|
if abs(intersections[i].x-self.pos.x)<=1/2+1e-11 and abs(intersections[i].y-self.pos.y)<=1/2+1e-11:
|
||
|
if (intersections[i]-pos)**2<dist:
|
||
|
closest=intersections[i]
|
||
|
dist=(intersections[i]-pos)**2
|
||
|
|
||
|
if closest==None:
|
||
|
print("error: cannot move particle at (",pos.x,",",pos.y,") to the boundary of (",self.pos.x,",",self.pos.y,")",file=sys.stderr)
|
||
|
exit(-1)
|
||
|
|
||
|
return closest
|
||
|
|
||
|
# move along edge of square
|
||
|
def move_along(self,newpos,pos):
|
||
|
rel=pos-self.pos
|
||
|
# check if the particle is stuck in the x direction
|
||
|
if isint_nonzero(rel.x):
|
||
|
# check y direction
|
||
|
if isint_nonzero(rel.y):
|
||
|
# in corner
|
||
|
if sgn(newpos.y-pos.y)==-sgn(rel.y):
|
||
|
# stuck in x direction
|
||
|
return self.move_stuck_x(newpos,pos)
|
||
|
elif sgn(newpos.x-pos.x)==-sgn(rel.x):
|
||
|
# stuck in y direction
|
||
|
return self.move_stuck_y(newpos,pos)
|
||
|
# stuck in both directions
|
||
|
return pos
|
||
|
else:
|
||
|
# stuck in x direction
|
||
|
return self.move_stuck_x(newpos,pos)
|
||
|
elif isint_nonzero(rel.y):
|
||
|
# stuck in y direction
|
||
|
return self.move_stuck_y(newpos,pos)
|
||
|
# this should never happen
|
||
|
else:
|
||
|
print("error: stuck particle has non-integer relative position: (",rel.x,",",rel.y,")",file=sys.stderr)
|
||
|
exit(-1)
|
||
|
# move when stuck in the x direction
|
||
|
def move_stuck_x(self,newpos,pos):
|
||
|
# only move in y direction
|
||
|
candidate=Point(pos.x,newpos.y)
|
||
|
# do not move past corners
|
||
|
rel=pos.y-self.pos.y
|
||
|
newrel=newpos.y-self.pos.y
|
||
|
if newpos.y>pos.y:
|
||
|
if rel<math.ceil(rel)-1e-11 and newrel>math.ceil(rel)+1e-11 and math.ceil(rel)!=0:
|
||
|
# stick to corner
|
||
|
candidate.y=math.ceil(rel)+self.pos.y
|
||
|
else:
|
||
|
if rel>math.floor(rel)+1e-11 and newrel<math.floor(rel)-1e-11 and math.floor(rel)!=0:
|
||
|
# stick to corner
|
||
|
candidate.y=math.floor(rel)+self.pos.y
|
||
|
return candidate
|
||
|
# move when stuck in the y direction
|
||
|
def move_stuck_y(self,newpos,pos):
|
||
|
# onlx move in x direction
|
||
|
candidate=Point(pos.x,newpos.x)
|
||
|
# do not move past corners
|
||
|
rel=pos.x-self.pos.x
|
||
|
newrel=newpos.x-self.pos.x
|
||
|
if newpos.x>pos.x:
|
||
|
if rel<math.ceil(rel)-1e-11 and newrel>math.ceil(rel)+1e-11 and math.ceil(rel)!=0:
|
||
|
# stick to corner
|
||
|
candidate.x=math.ceil(rel)+self.pos.x
|
||
|
else:
|
||
|
if rel>math.floor(rel)+1e-11 and newrel<math.floor(rel)-1e-11 and math.floor(rel)!=0:
|
||
|
# stick to corner
|
||
|
candidate.x=math.floor(rel)+self.pos.x
|
||
|
return candidate
|
||
|
|