move along cross

This commit is contained in:
Ian Jauslin 2021-09-28 23:36:07 -04:00
parent 6851e1ab32
commit a5caeec29a
2 changed files with 100 additions and 3 deletions

View File

@ -1,8 +1,10 @@
import math import math
import sys
from kivy.graphics import Color,Line,Rectangle from kivy.graphics import Color,Line,Rectangle
from kivy.uix.widget import Widget from kivy.uix.widget import Widget
from point import Point from point import Point
from tools import isint_nonzero,sgn
class Cross(): class Cross():
# size of central square # size of central square
@ -12,6 +14,7 @@ class Cross():
self.pos=Point(x,y) self.pos=Point(x,y)
self.color=kwargs.get("color",(0,0,1)) self.color=kwargs.get("color",(0,0,1))
self.selected=False self.selected=False
self.stuckto=None
# set position # set position
def setpos(self,x,y): def setpos(self,x,y):
@ -89,6 +92,87 @@ class Cross():
closest=self.move_on_line_to_stick_relative(Point(-x.x,x.y),Point(-v.x,v.y)) closest=self.move_on_line_to_stick_relative(Point(-x.x,x.y),Point(-v.x,v.y))
return Point(-closest.x,closest.y) return Point(-closest.x,closest.y)
# move along edge of cross while remaining stuck
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
# two types of corners: |x|_1=3 or |x|_1=4
if abs(rel.x)+abs(rel.y)<3.5:
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)
else:
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 self.check_interaction(candidate)==False or (rel<math.ceil(rel)-1e-11 and newrel>math.ceil(rel)+1e-11 and math.ceil(rel)!=0):
# in open corner
if rel>math.ceil(rel)-1e-11:
candidate.y=math.ceil(rel)+1+self.pos.y
else:
candidate.y=math.ceil(rel)+self.pos.y
else:
if self.check_interaction(candidate)==False or (rel>math.floor(rel)+1e-11 and newrel<math.floor(rel)-1e-11 and math.floor(rel)!=0):
# in open corner
if rel<math.floor(rel)+1e-11:
candidate.y=math.floor(rel)-1+self.pos.y
else:
candidate.y=math.floor(rel)+self.pos.y
return candidate
# move when stuck in the y direction
def move_stuck_y(self,newpos,pos):
# only move in x direction
candidate=Point(newpos.x,pos.y)
# do not move past corners
rel=pos.x-self.pos.x
newrel=newpos.x-self.pos.x
if newpos.x>pos.x:
if self.check_interaction(candidate)==False or (rel<math.ceil(rel)-1e-11 and newrel>math.ceil(rel)+1e-11 and math.ceil(rel)!=0):
# in open corner
if rel>math.ceil(rel)-1e-11:
candidate.x=math.ceil(rel)+1+self.pos.x
else:
candidate.x=math.ceil(rel)+self.pos.x
else:
if self.check_interaction(candidate)==False or (rel>math.floor(rel)+1e-11 and newrel<math.floor(rel)-1e-11 and math.floor(rel)!=0):
# in open corner
if rel<math.floor(rel)+1e-11:
candidate.x=math.floor(rel)-1+self.pos.x
else:
candidate.x=math.floor(rel)+self.pos.x
return candidate
# L_infinity distance rescalled by 3 in the x direction # L_infinity distance rescalled by 3 in the x direction
def cross_distx(x,y): def cross_distx(x,y):
@ -181,8 +265,16 @@ class Cross_painter(Widget):
if other!=cross: if other!=cross:
# move would make cross overlap with other # move would make cross overlap with other
if other.check_interaction(newpos)==False: if other.check_interaction(newpos)==False:
# check if cross is stuck to other
if cross.stuckto==other:
# move along other while remaining stuck
return other.move_along(newpos,cross.pos)
else:
# stick to the cross # stick to the cross
cross.stuckto=other
return other.move_on_line_to_stick(cross.pos,newpos-cross.pos) return other.move_on_line_to_stick(cross.pos,newpos-cross.pos)
# reset stuckto if move is allowed
cross.stuckto=None
return newpos return newpos
# check that a cross can be added at position # check that a cross can be added at position

View File

@ -1,6 +1,11 @@
# sign function # sign function
def sgn(x): def sgn(x):
if x>=0: if x>=0:
return 1 return 1
return -1 return -1
# check whether a number is an integer (with tolerance)
def isint_nonzero(x):
if abs(x)<1e-11:
return False
return abs(round(x)-x)<1e-11