Move all selected
This commit is contained in:
parent
bf365bc725
commit
7ea7b56556
138
painter.py
138
painter.py
@ -1,4 +1,5 @@
|
|||||||
import sys
|
import sys
|
||||||
|
import math
|
||||||
from kivy.uix.widget import Widget
|
from kivy.uix.widget import Widget
|
||||||
from kivy.core.window import Window
|
from kivy.core.window import Window
|
||||||
|
|
||||||
@ -6,6 +7,8 @@ from point import Point
|
|||||||
from polyomino import Square
|
from polyomino import Square
|
||||||
from polyomino import Square_element
|
from polyomino import Square_element
|
||||||
|
|
||||||
|
from tools import remove_fromlist
|
||||||
|
|
||||||
# painter class
|
# painter class
|
||||||
class Painter(Widget):
|
class Painter(Widget):
|
||||||
|
|
||||||
@ -19,6 +22,8 @@ class Painter(Widget):
|
|||||||
|
|
||||||
# list of selected particles
|
# list of selected particles
|
||||||
self.selected=[]
|
self.selected=[]
|
||||||
|
# complement
|
||||||
|
self.unselected=self.particles
|
||||||
|
|
||||||
# relative position of mouse when moving
|
# relative position of mouse when moving
|
||||||
self.offset=Point(0,0)
|
self.offset=Point(0,0)
|
||||||
@ -85,6 +90,7 @@ class Painter(Widget):
|
|||||||
for sel in self.selected:
|
for sel in self.selected:
|
||||||
sel.selected=False
|
sel.selected=False
|
||||||
self.selected=[]
|
self.selected=[]
|
||||||
|
self.unselected=self.particles
|
||||||
|
|
||||||
self.draw()
|
self.draw()
|
||||||
|
|
||||||
@ -99,11 +105,20 @@ class Painter(Widget):
|
|||||||
|
|
||||||
# no modifiers
|
# no modifiers
|
||||||
if self.modifiers==[]:
|
if self.modifiers==[]:
|
||||||
if self.undermouse==None or not self.undermouse in self.selected:
|
if self.undermouse==None:
|
||||||
# unselect all particles
|
# unselect all particles
|
||||||
for sel in self.selected:
|
for sel in self.selected:
|
||||||
sel.selected=False
|
sel.selected=False
|
||||||
self.selected=[]
|
self.selected=[]
|
||||||
|
self.unselected=self.particles
|
||||||
|
# select undermouse
|
||||||
|
elif not self.undermouse in self.selected:
|
||||||
|
for sel in self.selected:
|
||||||
|
sel.selected=False
|
||||||
|
self.selected=[self.undermouse]
|
||||||
|
self.unselected=self.particles.copy()
|
||||||
|
self.unselected=remove_fromlist(self.unselected,self.undermouse)
|
||||||
|
self.undermouse.selected=True
|
||||||
|
|
||||||
|
|
||||||
# shift-click
|
# shift-click
|
||||||
@ -112,11 +127,14 @@ class Painter(Widget):
|
|||||||
if self.undermouse not in self.selected:
|
if self.undermouse not in self.selected:
|
||||||
self.selected.append(self.undermouse)
|
self.selected.append(self.undermouse)
|
||||||
self.undermouse.selected=True
|
self.undermouse.selected=True
|
||||||
|
# remove from unselected
|
||||||
|
self.unselected=remove_fromlist(self.unselected,self.undermouse)
|
||||||
else:
|
else:
|
||||||
# remove
|
# remove
|
||||||
self.selected[self.selected.index(self.undermouse)]=self.selected[len(self.selected)-1]
|
self.selected=remove_fromlist(self.selected,self.undermouse)
|
||||||
self.selected=self.selected[:len(self.selected)-1]
|
|
||||||
self.undermouse.selected=False
|
self.undermouse.selected=False
|
||||||
|
# add to unselected
|
||||||
|
self.unselected.append(self.undermouse)
|
||||||
|
|
||||||
self.draw()
|
self.draw()
|
||||||
|
|
||||||
@ -127,42 +145,14 @@ class Painter(Widget):
|
|||||||
if self.collide_point(*touch.pos):
|
if self.collide_point(*touch.pos):
|
||||||
# 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:
|
||||||
# change in position
|
# attempted move determined by the relative position to the relative position of click within self.undermouse
|
||||||
delta=self.check_move(Point(touch.x/Square_element.size,touch.y/Square_element.size)-(self.offset+self.undermouse.squares[0].pos),self.undermouse)
|
delta=self.adjust_move(Point(touch.x/Square_element.size,touch.y/Square_element.size)-(self.offset+self.undermouse.squares[0].pos))
|
||||||
self.undermouse.move(delta)
|
for particle in self.selected:
|
||||||
# multiple particles
|
particle.move(delta)
|
||||||
# TODO: group moves
|
|
||||||
#else:
|
|
||||||
# self.group_move(touch)
|
|
||||||
|
|
||||||
# redraw
|
# redraw
|
||||||
self.draw()
|
self.draw()
|
||||||
|
|
||||||
## move all selected particles
|
|
||||||
#def group_move(self,touch):
|
|
||||||
# # save position of undermouse (to use it after it has been reset)
|
|
||||||
# relative_position=self.undermouse.pos
|
|
||||||
|
|
||||||
# # determine order in which to move
|
|
||||||
# # direction of motion
|
|
||||||
# direction=Point(touch.x/Square_element.size,touch.y/Square_element.size)-self.undermouse.pos
|
|
||||||
# # sort according to scalar product with direction
|
|
||||||
# self.selected.sort(key=(lambda particle: direction.dot(particle.pos-self.undermouse.pos)),reverse=True)
|
|
||||||
|
|
||||||
# # move
|
|
||||||
# for particle in self.selected:
|
|
||||||
# particle.pos=self.check_move(Point(touch.x/Square_element.size-relative_position.x+particle.pos.x,touch.y/Square_element.size-relative_position.y+particle.pos.y),particle)
|
|
||||||
#
|
|
||||||
# ## new position of undermouse
|
|
||||||
# #self.undermouse.pos=self.check_move(Point(touch.x/Square_element.size,touch.y/Square_element.size),self.undermouse)
|
|
||||||
# ## move other particles by the same amount as undermouse
|
|
||||||
# #for particle in self.selected:
|
|
||||||
# # if particle!=self.undermouse:
|
|
||||||
# # particle.pos=self.check_move(Point(self.undermouse.pos.x-relative_position.x+particle.pos.x,self.undermouse.pos.y-relative_position.y+particle.pos.y),particle)
|
|
||||||
# #
|
|
||||||
# #for particle in self.selected:
|
|
||||||
# # particle.pos=self.check_move(Point(touch.x/Square_element.size-relative_position.x+particle.pos.x,touch.y/Square_element.size-relative_position.y+particle.pos.y),particle)
|
|
||||||
|
|
||||||
|
|
||||||
# find the particle at position pos
|
# find the particle at position pos
|
||||||
def find_particle(self,pos):
|
def find_particle(self,pos):
|
||||||
@ -172,7 +162,7 @@ class Painter(Widget):
|
|||||||
# none found
|
# none found
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# check whether a position intersects with any of the particles
|
# check whether a candidate particle intersects with any of the particles
|
||||||
def check_interaction_any(self,candidate,offset):
|
def check_interaction_any(self,candidate,offset):
|
||||||
for particle in self.particles:
|
for particle in self.particles:
|
||||||
# do not check interaction if candidate=particle
|
# do not check interaction if candidate=particle
|
||||||
@ -180,32 +170,77 @@ class Painter(Widget):
|
|||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# check that a particle can move by delta, and return the closest allowed relative motion
|
# check whether shifting a list of particles by offset makes them interact with all particles
|
||||||
def check_move(self,delta,particle):
|
def check_interaction_list(self,array,offset):
|
||||||
|
for candidate in array:
|
||||||
|
if self.check_interaction_any(candidate,offset):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
# check whether shifting a list of particles by offset makes them interact with the unselected particles
|
||||||
|
def check_interaction_unselected_list(self,array,offset):
|
||||||
|
for candidate in array:
|
||||||
|
for particle in self.unselected:
|
||||||
|
if particle.check_interaction(candidate,offset):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
# check whether a candidate particle element with any of the unselected particles
|
||||||
|
def check_interaction_unselected_element(self,element,offset):
|
||||||
|
for particle in self.unselected:
|
||||||
|
for square in particle.squares:
|
||||||
|
if square.check_interaction(element.pos+offset):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
# try to move all selected particles by delta, adjust if needed to avoid overlap with unselected particles
|
||||||
|
# we only track whether these elements collide with unselected particles, not with each other
|
||||||
|
def adjust_move(self,delta):
|
||||||
|
# actual_delta is the smallest (componentwise) of all the computed delta's
|
||||||
|
actual_delta=Point(math.inf,math.inf)
|
||||||
|
for particle in self.selected:
|
||||||
|
for element in particle.squares:
|
||||||
|
# compute adjustment move due to unselected obstacles
|
||||||
|
adjusted_delta=self.adjust_move_element(delta,element)
|
||||||
|
# only keep the smallest delta's (in absolute value)
|
||||||
|
if abs(adjusted_delta.x)<abs(actual_delta.x):
|
||||||
|
actual_delta.x=adjusted_delta.x
|
||||||
|
if abs(adjusted_delta.y)<abs(actual_delta.y):
|
||||||
|
actual_delta.y=adjusted_delta.y
|
||||||
|
# try to move by actual_delta
|
||||||
|
if not self.check_interaction_unselected_list(self.selected,actual_delta):
|
||||||
|
return actual_delta
|
||||||
|
else:
|
||||||
|
# cannot move particles at all, try again
|
||||||
|
return self.adjust_move(actual_delta)
|
||||||
|
|
||||||
|
|
||||||
|
# trying to move a single element by delta, adjust if needed to avoid overlap with unselected particles
|
||||||
|
def adjust_move_element(self,delta,element):
|
||||||
# whether newpos is acceptable
|
# whether newpos is acceptable
|
||||||
accept_newpos=True
|
accept_newpos=True
|
||||||
for other in self.particles:
|
for other in self.unselected:
|
||||||
# do not compare a particle to itself
|
for obstacle in other.squares:
|
||||||
if other!=particle:
|
# move would make element overlap with obstacle
|
||||||
# move would make particle overlap with other
|
if obstacle.check_interaction(element.pos+delta):
|
||||||
if other.check_interaction(particle,delta):
|
|
||||||
accept_newpos=False
|
accept_newpos=False
|
||||||
# check if particle already touches other
|
# check if particle already touches obstacle
|
||||||
if other.check_touch(particle):
|
if obstacle.check_touch(element.pos):
|
||||||
# move along other while remaining stuck
|
# move along obstacle while remaining stuck
|
||||||
# TODO: this assumes other is a square
|
newdelta=obstacle.move_along(delta,element.pos)
|
||||||
newdelta=other.squares[0].move_along(delta,particle.squares[0].pos)
|
|
||||||
else:
|
else:
|
||||||
newdelta=other.squares[0].move_on_line_to_stick(particle.squares[0].pos,delta)
|
newdelta=obstacle.move_on_line_to_stick(element.pos,delta)
|
||||||
if not self.check_interaction_any(particle,newdelta):
|
if not self.check_interaction_unselected_element(element,newdelta):
|
||||||
return newdelta
|
return newdelta
|
||||||
if accept_newpos:
|
if accept_newpos:
|
||||||
return delta
|
return delta
|
||||||
else:
|
else:
|
||||||
# cannot move particle at all, try again
|
# cannot move particle at all, try again
|
||||||
return self.check_move(newdelta,particle)
|
return self.adjust_move_element(newdelta,element)
|
||||||
|
|
||||||
|
|
||||||
|
# TODO adapt
|
||||||
# write configuration to file
|
# write configuration to file
|
||||||
def write(self,file):
|
def write(self,file):
|
||||||
ff=open(file,"w")
|
ff=open(file,"w")
|
||||||
@ -213,6 +248,7 @@ class Painter(Widget):
|
|||||||
ff.write("{:05.2f},{:05.2f};{:3.1f},{:3.1f},{:3.1f}\n".format(particle.pos.x,particle.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.pos.x,particle.pos.y,particle.color[0],particle.color[1],particle.color[2]))
|
||||||
ff.close()
|
ff.close()
|
||||||
|
|
||||||
|
# TODO adapt
|
||||||
# read configuration from file
|
# read configuration from file
|
||||||
def read(self,file):
|
def read(self,file):
|
||||||
self.reset()
|
self.reset()
|
||||||
|
@ -63,15 +63,6 @@ class Polyomino():
|
|||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# check whether self touches candidate
|
|
||||||
def check_touch(self,candidate):
|
|
||||||
for square1 in self.squares:
|
|
||||||
for square2 in candidate.squares:
|
|
||||||
if square1.check_touch(square2.pos):
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
# square
|
# square
|
||||||
class Square(Polyomino):
|
class Square(Polyomino):
|
||||||
def __init__(self,x,y,**kwargs):
|
def __init__(self,x,y,**kwargs):
|
||||||
|
7
tools.py
7
tools.py
@ -13,3 +13,10 @@ def isint_nonzero(x):
|
|||||||
# check that a number is in an interval
|
# check that a number is in an interval
|
||||||
def in_interval(x,a,b):
|
def in_interval(x,a,b):
|
||||||
return x>=a and x<=b
|
return x>=a and x<=b
|
||||||
|
|
||||||
|
# remove x from list a
|
||||||
|
def remove_fromlist(a,x):
|
||||||
|
if x in a:
|
||||||
|
a[a.index(x)]=a[len(a)-1]
|
||||||
|
a=a[:len(a)-1]
|
||||||
|
return a
|
||||||
|
Loading…
Reference in New Issue
Block a user