From a8359a98bf4f47b06fd2c7bdaea85a634d4b4247 Mon Sep 17 00:00:00 2001 From: Ian Jauslin Date: Wed, 24 Nov 2021 17:18:27 -0500 Subject: [PATCH] Adapt cross dynamics to square --- cross.py | 213 --------------------------------------------------- point.py | 4 + polyomino.py | 128 +++++++++++++++++++++++++++++++ tools.py | 4 + 4 files changed, 136 insertions(+), 213 deletions(-) delete mode 100644 cross.py create mode 100644 polyomino.py diff --git a/cross.py b/cross.py deleted file mode 100644 index 527a202..0000000 --- a/cross.py +++ /dev/null @@ -1,213 +0,0 @@ -import math -import sys -from kivy.graphics import Color,Line,Rectangle - -from point import Point -from tools import isint_nonzero,sgn - -class Cross(): - # size of central square - 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-1.5)*self.size,(self.pos.y-0.5)*self.size),size=(3*self.size,self.size)) - Rectangle(pos=((self.pos.x-0.5)*self.size,(self.pos.y-1.5)*self.size),size=(self.size,3*self.size)) - - # stroke - Color(1,1,1) - Line(points=( - *((self.pos.x-0.5)*self.size,(self.pos.y-0.5)*self.size), - *((self.pos.x-0.5)*self.size,(self.pos.y-1.5)*self.size), - *((self.pos.x+0.5)*self.size,(self.pos.y-1.5)*self.size), - *((self.pos.x+0.5)*self.size,(self.pos.y-0.5)*self.size), - *((self.pos.x+1.5)*self.size,(self.pos.y-0.5)*self.size), - *((self.pos.x+1.5)*self.size,(self.pos.y+0.5)*self.size), - *((self.pos.x+0.5)*self.size,(self.pos.y+0.5)*self.size), - *((self.pos.x+0.5)*self.size,(self.pos.y+1.5)*self.size), - *((self.pos.x-0.5)*self.size,(self.pos.y+1.5)*self.size), - *((self.pos.x-0.5)*self.size,(self.pos.y+0.5)*self.size), - *((self.pos.x-1.5)*self.size,(self.pos.y+0.5)*self.size), - *((self.pos.x-1.5)*self.size,(self.pos.y-0.5)*self.size), - *((self.pos.x-0.5)*self.size,(self.pos.y-0.5)*self.size), - )) - - # check whether a cross at pos interacts with cross - def check_interaction(self,pos): - # allow for error - return int(pos.x-self.pos.x+sgn(pos.x-self.pos.x)*1e-11)**2+int(pos.y-self.pos.y+sgn(pos.y-self.pos.y)*1e-11)**2>=5 - - # check whether a cross at position pos is touching self - def check_touch(self,pos): - rel=pos-self.pos - for i in [-3,-2,-1,1,2,3]: - # allow for error - if abs(rel.x-i)<1e-11 and abs(rel.y)<=4-abs(i)+1e-11 and abs(rel.y)>=3-abs(i)-1e-11: - return True - if abs(rel.y-i)<1e-11 and abs(rel.x)<=4-abs(i)+1e-11 and abs(rel.x)>=3-abs(i)-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): - # relative to cross - return self.move_on_line_to_stick_relative(pos-self.pos,v)+self.pos - def move_on_line_to_stick_relative(self,x,v): - - # if x is in the right quadrant - if abs(x.y)<=x.x: - # find all stuck positions on lines - stuck=[] - - # check intersections with vertical lines - if v.x!=0: - for i in range(1,4): - # candidate - y=Point(i,x.y+(i-x.x)*v.y/v.x) - # check that it is in the right range - if abs(y.y)<=4-i and abs(y.y)>=3-i: - stuck.append(y) - # check intersections with horizontal lines - if v.y!=0: - for i in [-3,-2,-1,1,2,3]: - # candidate - y=Point(x.x+(i-x.y)*v.x/v.y,i) - # check that it is in the right range - if y.x<=4-abs(i) and y.x>=3-abs(i): - stuck.append(y) - - return x.closest(stuck) - - # reflect other quadrants to the right one - # top quadrant - elif abs(x.x)<=x.y: - closest=self.move_on_line_to_stick_relative(Point(x.y,x.x),Point(v.y,v.x)) - return Point(closest.y,closest.x) - # bottom quadrant - elif abs(x.x)<=-x.y: - closest=self.move_on_line_to_stick_relative(Point(-x.y,x.x),Point(-v.y,v.x)) - return Point(closest.y,-closest.x) - # left quadrant - else: - closest=self.move_on_line_to_stick_relative(Point(-x.x,x.y),Point(-v.x,v.y)) - return Point(-closest.x,closest.y) - - # move along edge of cross - 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 (relmath.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 newrelpos.x: - if self.check_interaction(candidate)==False or (relmath.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=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)**2pos.y: + if relmath.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 newrelpos.x: + if relmath.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=a and x<=b