From e5b60196066d7fae9ed0efa2c1e1c7c8ccc7c8a5 Mon Sep 17 00:00:00 2001 From: Ian Jauslin Date: Wed, 28 Sep 2022 18:42:06 -0400 Subject: [PATCH] Allow square element to be a rectangle --- src/element.py | 76 ++++++++++++++++++++++++++++-------------------- src/painter.py | 6 ++-- src/polyomino.py | 6 ++-- src/tools.py | 9 ++++++ 4 files changed, 58 insertions(+), 39 deletions(-) diff --git a/src/element.py b/src/element.py index b004318..a5ce7ea 100644 --- a/src/element.py +++ b/src/element.py @@ -3,7 +3,7 @@ import math import sys 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 # parent class of all elements @@ -55,11 +55,17 @@ class 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): + def __init__(self,x,y,size,**kwargs): + self.pos=Point(x,y) + self.size=size + self.aspect=kwargs.get("aspect",1.0) + # draw element 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 def stroke(self,painter): @@ -68,53 +74,55 @@ class Element_square(Element): coordy=painter.pos_tocoord_y(square.pos.y) 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*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*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*self.aspect*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*self.aspect*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 # TODO: this only works if element is a square! def check_interaction(self,element): # 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 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 # TODO: this only works if element is a square! def check_touch(self,element): # 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 False # 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! 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 if v.x!=0: if v.y!=0: 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-(self.size+element.size)/2,element.pos.y+v.y/v.x*(self.pos.x-(self.size+element.size)/2-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-(self.size+element.size)/2-element.pos.y),self.pos.y-(self.size+element.size)/2)\ + 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-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+size_y-element.pos.y),self.pos.y+size_y),\ + Point(element.pos.x+v.x/v.y*(self.pos.y-size_y-element.pos.y),self.pos.y-size_y)\ ] else: 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-(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-size_x,element.pos.y) ] else: if v.y!=0: 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+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,self.pos.y-size_y)\ ] else: 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 for i in range(0,len(intersections)): # 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)**20: - if relmath.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 relceil_grid(rel,size_y)+1e-11 and ceil_grid(rel,size_y)!=0: # 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: - if rel>math.floor(rel/((self.size+element.size)/2))*((self.size+element.size)/2)+1e-11 and delta.y+relfloor_grid(rel,size_y)+1e-11 and delta.y+rel0: - if relmath.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 relceil_grid(rel,size_x)+1e-11 and ceil_grid(rel,size_x)!=0: # 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: - if rel>math.floor(rel/((self.size+element.size)/2))*((self.size+element.size)/2)+1e-11 and delta.x+relfloor_grid(rel,size_x)+1e-11 and delta.x+rel=0: @@ -20,3 +22,10 @@ def remove_fromlist(a,x): a[a.index(x)]=a[len(a)-1] a=a[:len(a)-1] 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