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 math
|
||||
from kivy.uix.widget import Widget
|
||||
from kivy.core.window import Window
|
||||
|
||||
@ -6,6 +7,8 @@ from point import Point
|
||||
from polyomino import Square
|
||||
from polyomino import Square_element
|
||||
|
||||
from tools import remove_fromlist
|
||||
|
||||
# painter class
|
||||
class Painter(Widget):
|
||||
|
||||
@ -19,6 +22,8 @@ class Painter(Widget):
|
||||
|
||||
# list of selected particles
|
||||
self.selected=[]
|
||||
# complement
|
||||
self.unselected=self.particles
|
||||
|
||||
# relative position of mouse when moving
|
||||
self.offset=Point(0,0)
|
||||
@ -85,6 +90,7 @@ class Painter(Widget):
|
||||
for sel in self.selected:
|
||||
sel.selected=False
|
||||
self.selected=[]
|
||||
self.unselected=self.particles
|
||||
|
||||
self.draw()
|
||||
|
||||
@ -99,11 +105,20 @@ class Painter(Widget):
|
||||
|
||||
# no modifiers
|
||||
if self.modifiers==[]:
|
||||
if self.undermouse==None or not self.undermouse in self.selected:
|
||||
if self.undermouse==None:
|
||||
# unselect all particles
|
||||
for sel in self.selected:
|
||||
sel.selected=False
|
||||
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
|
||||
@ -112,11 +127,14 @@ class Painter(Widget):
|
||||
if self.undermouse not in self.selected:
|
||||
self.selected.append(self.undermouse)
|
||||
self.undermouse.selected=True
|
||||
# remove from unselected
|
||||
self.unselected=remove_fromlist(self.unselected,self.undermouse)
|
||||
else:
|
||||
# remove
|
||||
self.selected[self.selected.index(self.undermouse)]=self.selected[len(self.selected)-1]
|
||||
self.selected=self.selected[:len(self.selected)-1]
|
||||
self.selected=remove_fromlist(self.selected,self.undermouse)
|
||||
self.undermouse.selected=False
|
||||
# add to unselected
|
||||
self.unselected.append(self.undermouse)
|
||||
|
||||
self.draw()
|
||||
|
||||
@ -127,42 +145,14 @@ class Painter(Widget):
|
||||
if self.collide_point(*touch.pos):
|
||||
# only move on left click
|
||||
if touch.button=="left" and self.modifiers==[] and self.undermouse!=None:
|
||||
# change in position
|
||||
delta=self.check_move(Point(touch.x/Square_element.size,touch.y/Square_element.size)-(self.offset+self.undermouse.squares[0].pos),self.undermouse)
|
||||
self.undermouse.move(delta)
|
||||
# multiple particles
|
||||
# TODO: group moves
|
||||
#else:
|
||||
# self.group_move(touch)
|
||||
# attempted move determined by the relative position to the relative position of click within self.undermouse
|
||||
delta=self.adjust_move(Point(touch.x/Square_element.size,touch.y/Square_element.size)-(self.offset+self.undermouse.squares[0].pos))
|
||||
for particle in self.selected:
|
||||
particle.move(delta)
|
||||
|
||||
# redraw
|
||||
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
|
||||
def find_particle(self,pos):
|
||||
@ -172,7 +162,7 @@ class Painter(Widget):
|
||||
# none found
|
||||
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):
|
||||
for particle in self.particles:
|
||||
# do not check interaction if candidate=particle
|
||||
@ -180,32 +170,77 @@ class Painter(Widget):
|
||||
return True
|
||||
return False
|
||||
|
||||
# check that a particle can move by delta, and return the closest allowed relative motion
|
||||
def check_move(self,delta,particle):
|
||||
# check whether shifting a list of particles by offset makes them interact with all particles
|
||||
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
|
||||
accept_newpos=True
|
||||
for other in self.particles:
|
||||
# do not compare a particle to itself
|
||||
if other!=particle:
|
||||
# move would make particle overlap with other
|
||||
if other.check_interaction(particle,delta):
|
||||
for other in self.unselected:
|
||||
for obstacle in other.squares:
|
||||
# move would make element overlap with obstacle
|
||||
if obstacle.check_interaction(element.pos+delta):
|
||||
accept_newpos=False
|
||||
# check if particle already touches other
|
||||
if other.check_touch(particle):
|
||||
# move along other while remaining stuck
|
||||
# TODO: this assumes other is a square
|
||||
newdelta=other.squares[0].move_along(delta,particle.squares[0].pos)
|
||||
# check if particle already touches obstacle
|
||||
if obstacle.check_touch(element.pos):
|
||||
# move along obstacle while remaining stuck
|
||||
newdelta=obstacle.move_along(delta,element.pos)
|
||||
else:
|
||||
newdelta=other.squares[0].move_on_line_to_stick(particle.squares[0].pos,delta)
|
||||
if not self.check_interaction_any(particle,newdelta):
|
||||
newdelta=obstacle.move_on_line_to_stick(element.pos,delta)
|
||||
if not self.check_interaction_unselected_element(element,newdelta):
|
||||
return newdelta
|
||||
if accept_newpos:
|
||||
return delta
|
||||
else:
|
||||
# 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
|
||||
def write(self,file):
|
||||
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.close()
|
||||
|
||||
# TODO adapt
|
||||
# read configuration from file
|
||||
def read(self,file):
|
||||
self.reset()
|
||||
|
@ -63,15 +63,6 @@ class Polyomino():
|
||||
return True
|
||||
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
|
||||
class Square(Polyomino):
|
||||
def __init__(self,x,y,**kwargs):
|
||||
|
Loading…
Reference in New Issue
Block a user