Split element into their own file
This commit is contained in:
parent
5835c9003c
commit
c09600cd10
164
src/element.py
Normal file
164
src/element.py
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
## elements that polyominoes are made of
|
||||||
|
import math
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from point import Point,l_infinity
|
||||||
|
from tools import isint_nonzero,sgn,in_interval
|
||||||
|
|
||||||
|
# parent class of all elements
|
||||||
|
class Element():
|
||||||
|
def __init__(self,x,y,size,**kwargs):
|
||||||
|
self.pos=Point(x,y)
|
||||||
|
self.size=size
|
||||||
|
|
||||||
|
# set position
|
||||||
|
def setpos(self,x,y):
|
||||||
|
self.pos.x=x
|
||||||
|
self.pos.y=y
|
||||||
|
|
||||||
|
|
||||||
|
# override in each subclass
|
||||||
|
# check whether an element interacts with square
|
||||||
|
def check_interaction(self,element):
|
||||||
|
return False
|
||||||
|
|
||||||
|
# override in each subclass
|
||||||
|
# whether x is in the support of the element
|
||||||
|
def in_support(self,x):
|
||||||
|
return False
|
||||||
|
|
||||||
|
# override in each subclass
|
||||||
|
# check whether an element is touching self
|
||||||
|
def check_touch(self,element):
|
||||||
|
return False
|
||||||
|
|
||||||
|
# override in each subclass
|
||||||
|
# find position along a line that comes in contact with the line going through element.pos in direction v
|
||||||
|
def move_on_line_to_stick(self,element,v):
|
||||||
|
return Point(0,0)
|
||||||
|
|
||||||
|
# override in each subclass
|
||||||
|
# move along edge of element
|
||||||
|
def move_along(self,delta,element):
|
||||||
|
return element
|
||||||
|
|
||||||
|
|
||||||
|
# square elements
|
||||||
|
class Element_square(Element):
|
||||||
|
|
||||||
|
# check whether an element interacts with square
|
||||||
|
def check_interaction(self,element):
|
||||||
|
# allow for error
|
||||||
|
return l_infinity(element.pos-self.pos)<(self.size+element.size)/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
|
||||||
|
|
||||||
|
# check whether an element is touching self
|
||||||
|
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):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
# find position along a line that comes in contact with the line going through element.pos in direction v
|
||||||
|
def move_on_line_to_stick(self,element,v):
|
||||||
|
# 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)\
|
||||||
|
]
|
||||||
|
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))
|
||||||
|
]
|
||||||
|
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)\
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
print("error: move_on_line_to_stick called with v=0, please file a bug report with the developer",file=sys.stderr)
|
||||||
|
exit(-1)
|
||||||
|
|
||||||
|
# compute closest one, on square
|
||||||
|
closest=None
|
||||||
|
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 (intersections[i]-element.pos)**2<dist:
|
||||||
|
closest=intersections[i]
|
||||||
|
dist=(intersections[i]-element.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,") in direction (",v.x,",",v.y,")",file=sys.stderr)
|
||||||
|
exit(-1)
|
||||||
|
|
||||||
|
# return difference to pos
|
||||||
|
return closest-element.pos
|
||||||
|
|
||||||
|
# move along edge of square
|
||||||
|
def move_along(self,delta,element):
|
||||||
|
rel=element.pos-self.pos
|
||||||
|
# check if the particle is stuck in the x direction
|
||||||
|
if isint_nonzero(rel.x/((self.size+element.size)/2)):
|
||||||
|
# check y direction
|
||||||
|
if isint_nonzero(rel.y/((self.size+element.size)/2)):
|
||||||
|
# in corner
|
||||||
|
if sgn(delta.y)==-sgn(rel.y):
|
||||||
|
# stuck in x direction
|
||||||
|
return self.move_stuck_x(delta,element)
|
||||||
|
elif sgn(delta.x)==-sgn(rel.x):
|
||||||
|
# stuck in y direction
|
||||||
|
return self.move_stuck_y(delta,element)
|
||||||
|
# stuck in both directions
|
||||||
|
return element.pos
|
||||||
|
else:
|
||||||
|
# stuck in x direction
|
||||||
|
return self.move_stuck_x(delta,element)
|
||||||
|
elif isint_nonzero(rel.y/((self.size+element.size)/2)):
|
||||||
|
# stuck in y direction
|
||||||
|
return self.move_stuck_y(delta,element)
|
||||||
|
# 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,delta,element):
|
||||||
|
# only move in y direction
|
||||||
|
candidate=Point(0,delta.y)
|
||||||
|
# do not move past corners
|
||||||
|
rel=element.pos.y-self.pos.y
|
||||||
|
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:
|
||||||
|
# stick to corner
|
||||||
|
candidate.y=math.ceil(rel/((self.size+element.size)/2))*((self.size+element.size)/2)+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+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:
|
||||||
|
# stick to corner
|
||||||
|
candidate.y=math.floor(rel/((self.size+element.size)/2))*((self.size+element.size)/2)+self.pos.y-element.pos.y
|
||||||
|
return candidate
|
||||||
|
# move when stuck in the y direction
|
||||||
|
def move_stuck_y(self,delta,element):
|
||||||
|
# onlx move in x direction
|
||||||
|
candidate=Point(delta.x,0)
|
||||||
|
# do not move past corners
|
||||||
|
rel=element.pos.x-self.pos.x
|
||||||
|
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:
|
||||||
|
# stick to corner
|
||||||
|
candidate.x=math.ceil(rel/((self.size+element.size)/2))*((self.size+element.size)/2)+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+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:
|
||||||
|
# stick to corner
|
||||||
|
candidate.x=math.floor(rel/((self.size+element.size)/2))*((self.size+element.size)/2)+self.pos.x-element.pos.x
|
||||||
|
return candidate
|
||||||
|
|
@ -6,7 +6,6 @@ from kivy.graphics import Color,Line
|
|||||||
|
|
||||||
from point import Point
|
from point import Point
|
||||||
from polyomino import Cross
|
from polyomino import Cross
|
||||||
from polyomino import Square_element
|
|
||||||
|
|
||||||
from tools import remove_fromlist
|
from tools import remove_fromlist
|
||||||
|
|
||||||
@ -71,7 +70,7 @@ class Painter(Widget):
|
|||||||
|
|
||||||
# snap all existing particles to grid
|
# snap all existing particles to grid
|
||||||
for particle in self.particles:
|
for particle in self.particles:
|
||||||
delta=self.lattice.nearest_delta(particle.squares[0].pos)
|
delta=self.lattice.nearest_delta(particle.elements[0].pos)
|
||||||
if not self.check_interaction_any(particle,delta):
|
if not self.check_interaction_any(particle,delta):
|
||||||
particle.move(delta)
|
particle.move(delta)
|
||||||
|
|
||||||
@ -104,7 +103,7 @@ class Painter(Widget):
|
|||||||
# draw grids
|
# draw grids
|
||||||
for particle in self.particles:
|
for particle in self.particles:
|
||||||
if particle.grid>0:
|
if particle.grid>0:
|
||||||
self.draw_grid(particle.squares[0].pos,particle.grid)
|
self.draw_grid(particle.elements[0].pos,particle.grid)
|
||||||
|
|
||||||
for particle in self.particles:
|
for particle in self.particles:
|
||||||
particle.draw(self,alpha=0.5)
|
particle.draw(self,alpha=0.5)
|
||||||
@ -217,7 +216,7 @@ class Painter(Widget):
|
|||||||
new=Cross(touchx,touchy)
|
new=Cross(touchx,touchy)
|
||||||
# snap to lattice
|
# snap to lattice
|
||||||
if self.lattice!=None:
|
if self.lattice!=None:
|
||||||
new.move(self.lattice.nearest_delta(new.squares[0].pos))
|
new.move(self.lattice.nearest_delta(new.elements[0].pos))
|
||||||
|
|
||||||
if not self.check_interaction_any(new,Point(0,0)):
|
if not self.check_interaction_any(new,Point(0,0)):
|
||||||
# add to list
|
# add to list
|
||||||
@ -238,7 +237,7 @@ class Painter(Widget):
|
|||||||
|
|
||||||
# record relative position of click with respect to reference
|
# record relative position of click with respect to reference
|
||||||
if self.undermouse!=None:
|
if self.undermouse!=None:
|
||||||
self.offset=Point(touchx,touchy)-self.undermouse.squares[0].pos
|
self.offset=Point(touchx,touchy)-self.undermouse.elements[0].pos
|
||||||
|
|
||||||
# no modifiers
|
# no modifiers
|
||||||
if self.modifiers==[]:
|
if self.modifiers==[]:
|
||||||
@ -291,7 +290,7 @@ class Painter(Widget):
|
|||||||
# only move on left click
|
# only move on left click
|
||||||
if touch.button=="left" and self.modifiers==[] and self.undermouse!=None:
|
if touch.button=="left" and self.modifiers==[] and self.undermouse!=None:
|
||||||
# attempted move determined by the relative position to the relative position of click within self.undermouse
|
# attempted move determined by the relative position to the relative position of click within self.undermouse
|
||||||
delta=self.adjust_move(Point(touchx,touchy)-(self.offset+self.undermouse.squares[0].pos),0)
|
delta=self.adjust_move(Point(touchx,touchy)-(self.offset+self.undermouse.elements[0].pos),0)
|
||||||
|
|
||||||
# snap to lattice
|
# snap to lattice
|
||||||
if self.lattice!=None:
|
if self.lattice!=None:
|
||||||
@ -347,10 +346,10 @@ class Painter(Widget):
|
|||||||
# check whether a candidate particle element with any of the unselected particles
|
# check whether a candidate particle element with any of the unselected particles
|
||||||
def check_interaction_unselected_element(self,element,offset):
|
def check_interaction_unselected_element(self,element,offset):
|
||||||
for particle in self.unselected:
|
for particle in self.unselected:
|
||||||
for square in particle.squares:
|
for elt in particle.elements:
|
||||||
# add offset
|
# add offset
|
||||||
element.pos+=offset
|
element.pos+=offset
|
||||||
if square.check_interaction(element):
|
if elt.check_interaction(element):
|
||||||
# reset offset
|
# reset offset
|
||||||
element.pos-=offset
|
element.pos-=offset
|
||||||
return True
|
return True
|
||||||
@ -365,7 +364,7 @@ class Painter(Widget):
|
|||||||
# actual_delta is the smallest (componentwise) of all the computed delta's
|
# actual_delta is the smallest (componentwise) of all the computed delta's
|
||||||
actual_delta=Point(math.inf,math.inf)
|
actual_delta=Point(math.inf,math.inf)
|
||||||
for particle in self.selected:
|
for particle in self.selected:
|
||||||
for element in particle.squares:
|
for element in particle.elements:
|
||||||
# compute adjustment move due to unselected obstacles
|
# compute adjustment move due to unselected obstacles
|
||||||
adjusted_delta=self.adjust_move_element(delta,element,0)
|
adjusted_delta=self.adjust_move_element(delta,element,0)
|
||||||
# only keep the smallest delta's (in absolute value)
|
# only keep the smallest delta's (in absolute value)
|
||||||
@ -391,7 +390,7 @@ class Painter(Widget):
|
|||||||
# whether newpos is acceptable
|
# whether newpos is acceptable
|
||||||
accept_newpos=True
|
accept_newpos=True
|
||||||
for other in self.unselected:
|
for other in self.unselected:
|
||||||
for obstacle in other.squares:
|
for obstacle in other.elements:
|
||||||
# move would make element overlap with obstacle
|
# move would make element overlap with obstacle
|
||||||
element.pos+=delta
|
element.pos+=delta
|
||||||
if obstacle.check_interaction(element):
|
if obstacle.check_interaction(element):
|
||||||
@ -448,7 +447,7 @@ class Painter(Widget):
|
|||||||
for particle in self.particles:
|
for particle in self.particles:
|
||||||
if type(particle)==Cross:
|
if type(particle)==Cross:
|
||||||
ff.write("{:d};".format(CROSS_INDEX))
|
ff.write("{:d};".format(CROSS_INDEX))
|
||||||
ff.write("{:05.2f},{:05.2f};{:3.1f},{:3.1f},{:3.1f}\n".format(particle.squares[0].pos.x,particle.squares[0].pos.y,particle.color[0],particle.color[1],particle.color[2]))
|
ff.write("{:05.2f},{:05.2f};{:3.1f},{:3.1f},{:3.1f}\n".format(particle.elements[0].pos.x,particle.elements[0].pos.y,particle.color[0],particle.color[1],particle.color[2]))
|
||||||
ff.close()
|
ff.close()
|
||||||
|
|
||||||
# read configuration from file
|
# read configuration from file
|
||||||
@ -561,7 +560,7 @@ class Painter(Widget):
|
|||||||
for particle in self.particles:
|
for particle in self.particles:
|
||||||
if type(particle)==Cross:
|
if type(particle)==Cross:
|
||||||
ff.write("\cross{"+colors.closest_color(particle.color,colors.xcolor_names)+"}")
|
ff.write("\cross{"+colors.closest_color(particle.color,colors.xcolor_names)+"}")
|
||||||
ff.write("{{({:05.2f},{:05.2f})}};\n".format(particle.squares[0].pos.x-self.particles[0].squares[0].pos.x,particle.squares[0].pos.y-self.particles[0].squares[0].pos.y))
|
ff.write("{{({:05.2f},{:05.2f})}};\n".format(particle.elements[0].pos.x-self.particles[0].elements[0].pos.x,particle.elements[0].pos.y-self.particles[0].elements[0].pos.y))
|
||||||
|
|
||||||
ff.write("\\end{tikzpicture}\n")
|
ff.write("\\end{tikzpicture}\n")
|
||||||
ff.write("\\end{document}\n")
|
ff.write("\\end{document}\n")
|
||||||
|
194
src/polyomino.py
194
src/polyomino.py
@ -1,15 +1,14 @@
|
|||||||
import math
|
# a polyomino is a collection of elements, defined in elements.py
|
||||||
import sys
|
|
||||||
from kivy.graphics import Color,Line,Rectangle
|
from kivy.graphics import Color,Line,Rectangle
|
||||||
|
|
||||||
from point import Point,l_infinity
|
from point import l_infinity
|
||||||
from tools import isint_nonzero,sgn,in_interval
|
from element import Element_square
|
||||||
|
|
||||||
# parent class of all polyominos
|
# parent class of all polyominos
|
||||||
class Polyomino():
|
class Polyomino():
|
||||||
def __init__(self,**kwargs):
|
def __init__(self,**kwargs):
|
||||||
# square elements that make up the polyomino
|
# elements that make up the polyomino
|
||||||
self.squares=kwargs.get("squares",[])
|
self.elements=kwargs.get("elements",[])
|
||||||
|
|
||||||
self.color=kwargs.get("color",(0,0,1))
|
self.color=kwargs.get("color",(0,0,1))
|
||||||
self.selected=False
|
self.selected=False
|
||||||
@ -28,8 +27,8 @@ class Polyomino():
|
|||||||
# darken selected
|
# darken selected
|
||||||
Color(r/2,g/2,b/2,alpha)
|
Color(r/2,g/2,b/2,alpha)
|
||||||
|
|
||||||
for square in self.squares:
|
for element in self.elements:
|
||||||
Rectangle(pos=(painter.pos_tocoord_x(square.pos.x-0.5*square.size),painter.pos_tocoord_y(square.pos.y-0.5*square.size)),size=(square.size*painter.base_size,square.size*painter.base_size))
|
Rectangle(pos=(painter.pos_tocoord_x(element.pos.x-0.5*element.size),painter.pos_tocoord_y(element.pos.y-0.5*element.size)),size=(element.size*painter.base_size,element.size*painter.base_size))
|
||||||
|
|
||||||
# draw boundary
|
# draw boundary
|
||||||
self.stroke(painter)
|
self.stroke(painter)
|
||||||
@ -42,62 +41,62 @@ class Polyomino():
|
|||||||
|
|
||||||
# white
|
# white
|
||||||
Color(1,1,1)
|
Color(1,1,1)
|
||||||
for square in self.squares:
|
for element in self.elements:
|
||||||
Line(points=(
|
Line(points=(
|
||||||
*(coordx-0.5*square.size*painter.base_size,coordy-0.5*square.size*painter.base_size),
|
*(coordx-0.5*element.size*painter.base_size,coordy-0.5*element.size*painter.base_size),
|
||||||
*(coordx-0.5*square.size*painter.base_size,coordy+0.5*square.size*painter.base_size),
|
*(coordx-0.5*element.size*painter.base_size,coordy+0.5*element.size*painter.base_size),
|
||||||
*(coordx+0.5*square.size*painter.base_size,coordy+0.5*square.size*painter.base_size),
|
*(coordx+0.5*element.size*painter.base_size,coordy+0.5*element.size*painter.base_size),
|
||||||
*(coordx+0.5*square.size*painter.base_size,coordy-0.5*square.size*painter.base_size),
|
*(coordx+0.5*element.size*painter.base_size,coordy-0.5*element.size*painter.base_size),
|
||||||
*(coordx-0.5*square.size*painter.base_size,coordy-0.5*square.size*painter.base_size)
|
*(coordx-0.5*element.size*painter.base_size,coordy-0.5*element.size*painter.base_size)
|
||||||
))
|
))
|
||||||
|
|
||||||
# move by delta
|
# move by delta
|
||||||
def move(self,delta):
|
def move(self,delta):
|
||||||
for square in self.squares:
|
for element in self.elements:
|
||||||
square.pos+=delta
|
element.pos+=delta
|
||||||
|
|
||||||
# whether x is in the support of the polyomino
|
# whether x is in the support of the polyomino
|
||||||
def in_support(self,x):
|
def in_support(self,x):
|
||||||
for square in self.squares:
|
for element in self.elements:
|
||||||
if l_infinity(square.pos-x)<=1/2:
|
if element.in_support(x):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# check whether self interacts with candidate if candidate were moved by offset
|
# check whether self interacts with candidate if candidate were moved by offset
|
||||||
def check_interaction(self,candidate,offset):
|
def check_interaction(self,candidate,offset):
|
||||||
for square1 in self.squares:
|
for element1 in self.elements:
|
||||||
for square2 in candidate.squares:
|
for element2 in candidate.elements:
|
||||||
# add offset
|
# add offset
|
||||||
square2.pos+=offset
|
element2.pos+=offset
|
||||||
if square1.check_interaction(square2):
|
if element1.check_interaction(element2):
|
||||||
# reset offset
|
# reset offset
|
||||||
square2.pos-=offset
|
element2.pos-=offset
|
||||||
return True
|
return True
|
||||||
# reset offset
|
# reset offset
|
||||||
square2.pos-=offset
|
element2.pos-=offset
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# square
|
# square
|
||||||
class Square(Polyomino):
|
class Square(Polyomino):
|
||||||
def __init__(self,x,y,**kwargs):
|
def __init__(self,x,y,**kwargs):
|
||||||
super(Square,self).__init__(**kwargs,squares=[Square_element(x,y,size=kwargs.get("size",1.0))])
|
super(Square,self).__init__(**kwargs,elements=[Element_square(x,y,size=kwargs.get("size",1.0))])
|
||||||
|
|
||||||
# cross
|
# cross
|
||||||
class Cross(Polyomino):
|
class Cross(Polyomino):
|
||||||
def __init__(self,x,y,**kwargs):
|
def __init__(self,x,y,**kwargs):
|
||||||
super(Cross,self).__init__(**kwargs,squares=[\
|
super(Cross,self).__init__(**kwargs,elements=[\
|
||||||
Square_element(x,y,1),\
|
Element_square(x,y,1),\
|
||||||
Square_element(x+1,y,1),\
|
Element_square(x+1,y,1),\
|
||||||
Square_element(x-1,y,1),\
|
Element_square(x-1,y,1),\
|
||||||
Square_element(x,y+1,1),\
|
Element_square(x,y+1,1),\
|
||||||
Square_element(x,y-1,1)\
|
Element_square(x,y-1,1)\
|
||||||
])
|
])
|
||||||
|
|
||||||
# redefine stroke to avoid lines between touching squares
|
# redefine stroke to avoid lines between touching elements
|
||||||
def stroke(self,painter):
|
def stroke(self,painter):
|
||||||
# convert to graphical coordinates
|
# convert to graphical coordinates
|
||||||
coordx=painter.pos_tocoord_x(self.squares[0].pos.x)
|
coordx=painter.pos_tocoord_x(self.elements[0].pos.x)
|
||||||
coordy=painter.pos_tocoord_y(self.squares[0].pos.y)
|
coordy=painter.pos_tocoord_y(self.elements[0].pos.y)
|
||||||
|
|
||||||
Color(1,1,1)
|
Color(1,1,1)
|
||||||
Line(points=(
|
Line(points=(
|
||||||
@ -116,130 +115,3 @@ class Cross(Polyomino):
|
|||||||
*(coordx-0.5*painter.base_size,coordy-0.5*painter.base_size),
|
*(coordx-0.5*painter.base_size,coordy-0.5*painter.base_size),
|
||||||
))
|
))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# square building block of polyominos
|
|
||||||
class Square_element():
|
|
||||||
|
|
||||||
def __init__(self,x,y,size,**kwargs):
|
|
||||||
self.pos=Point(x,y)
|
|
||||||
self.size=size
|
|
||||||
|
|
||||||
# set position
|
|
||||||
def setpos(self,x,y):
|
|
||||||
self.pos.x=x
|
|
||||||
self.pos.y=y
|
|
||||||
|
|
||||||
|
|
||||||
# check whether an element interacts with square
|
|
||||||
def check_interaction(self,element):
|
|
||||||
# allow for error
|
|
||||||
return l_infinity(element.pos-self.pos)<(self.size+element.size)/2-1e-11
|
|
||||||
|
|
||||||
# check whether an element is touching self
|
|
||||||
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):
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
# find position along a line that comes in contact with the line going through element.pos in direction v
|
|
||||||
def move_on_line_to_stick(self,element,v):
|
|
||||||
# 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)\
|
|
||||||
]
|
|
||||||
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))
|
|
||||||
]
|
|
||||||
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)\
|
|
||||||
]
|
|
||||||
else:
|
|
||||||
print("error: move_on_line_to_stick called with v=0, please file a bug report with the developer",file=sys.stderr)
|
|
||||||
exit(-1)
|
|
||||||
|
|
||||||
# compute closest one, on square
|
|
||||||
closest=None
|
|
||||||
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 (intersections[i]-element.pos)**2<dist:
|
|
||||||
closest=intersections[i]
|
|
||||||
dist=(intersections[i]-element.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,") in direction (",v.x,",",v.y,")",file=sys.stderr)
|
|
||||||
exit(-1)
|
|
||||||
|
|
||||||
# return difference to pos
|
|
||||||
return closest-element.pos
|
|
||||||
|
|
||||||
# move along edge of square
|
|
||||||
def move_along(self,delta,element):
|
|
||||||
rel=element.pos-self.pos
|
|
||||||
# check if the particle is stuck in the x direction
|
|
||||||
if isint_nonzero(rel.x/((self.size+element.size)/2)):
|
|
||||||
# check y direction
|
|
||||||
if isint_nonzero(rel.y/((self.size+element.size)/2)):
|
|
||||||
# in corner
|
|
||||||
if sgn(delta.y)==-sgn(rel.y):
|
|
||||||
# stuck in x direction
|
|
||||||
return self.move_stuck_x(delta,element)
|
|
||||||
elif sgn(delta.x)==-sgn(rel.x):
|
|
||||||
# stuck in y direction
|
|
||||||
return self.move_stuck_y(delta,element)
|
|
||||||
# stuck in both directions
|
|
||||||
return element.pos
|
|
||||||
else:
|
|
||||||
# stuck in x direction
|
|
||||||
return self.move_stuck_x(delta,element)
|
|
||||||
elif isint_nonzero(rel.y/((self.size+element.size)/2)):
|
|
||||||
# stuck in y direction
|
|
||||||
return self.move_stuck_y(delta,element)
|
|
||||||
# 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,delta,element):
|
|
||||||
# only move in y direction
|
|
||||||
candidate=Point(0,delta.y)
|
|
||||||
# do not move past corners
|
|
||||||
rel=element.pos.y-self.pos.y
|
|
||||||
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:
|
|
||||||
# stick to corner
|
|
||||||
candidate.y=math.ceil(rel/((self.size+element.size)/2))*((self.size+element.size)/2)+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+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:
|
|
||||||
# stick to corner
|
|
||||||
candidate.y=math.floor(rel/((self.size+element.size)/2))*((self.size+element.size)/2)+self.pos.y-element.pos.y
|
|
||||||
return candidate
|
|
||||||
# move when stuck in the y direction
|
|
||||||
def move_stuck_y(self,delta,element):
|
|
||||||
# onlx move in x direction
|
|
||||||
candidate=Point(delta.x,0)
|
|
||||||
# do not move past corners
|
|
||||||
rel=element.pos.x-self.pos.x
|
|
||||||
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:
|
|
||||||
# stick to corner
|
|
||||||
candidate.x=math.ceil(rel/((self.size+element.size)/2))*((self.size+element.size)/2)+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+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:
|
|
||||||
# stick to corner
|
|
||||||
candidate.x=math.floor(rel/((self.size+element.size)/2))*((self.size+element.size)/2)+self.pos.x-element.pos.x
|
|
||||||
return candidate
|
|
||||||
|
|
||||||
|
@ -45,9 +45,9 @@ class Status_bar(Label):
|
|||||||
spaces=int(self.width/self.char_width)-len(self.raw_text)-13
|
spaces=int(self.width/self.char_width)-len(self.raw_text)-13
|
||||||
if spaces>0:
|
if spaces>0:
|
||||||
if self.app.painter.reference==None:
|
if self.app.painter.reference==None:
|
||||||
self.raw_text+=" "*spaces+"({:05.2f},{:05.2f})\n".format(self.app.painter.selected[0].squares[0].pos.x,self.app.painter.selected[0].squares[0].pos.y)
|
self.raw_text+=" "*spaces+"({:05.2f},{:05.2f})\n".format(self.app.painter.selected[0].elements[0].pos.x,self.app.painter.selected[0].elements[0].pos.y)
|
||||||
else:
|
else:
|
||||||
self.raw_text+=" "*spaces+"({:05.2f},{:05.2f})\n".format(self.app.painter.selected[0].squares[0].pos.x-self.app.painter.reference.squares[0].pos.x,self.app.painter.selected[0].squares[0].pos.y-self.app.painter.reference.squares[0].pos.y)
|
self.raw_text+=" "*spaces+"({:05.2f},{:05.2f})\n".format(self.app.painter.selected[0].elements[0].pos.x-self.app.painter.reference.elements[0].pos.x,self.app.painter.selected[0].elements[0].pos.y-self.app.painter.reference.elements[0].pos.y)
|
||||||
|
|
||||||
# do not wrap
|
# do not wrap
|
||||||
self.text=self.raw_text[:min(len(self.raw_text),int(self.width/self.char_width))]
|
self.text=self.raw_text[:min(len(self.raw_text),int(self.width/self.char_width))]
|
||||||
|
Loading…
Reference in New Issue
Block a user