diff --git a/painter.py b/painter.py index 9974cf9..8db1e89 100644 --- a/painter.py +++ b/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)=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