commit cc55ac1987e59259eb814f6a4be83abaa9bd3fe1 Author: Ian Jauslin Date: Mon Sep 27 21:20:22 2021 -0400 Initial commit diff --git a/main.py b/main.py new file mode 100644 index 0000000..57b3d65 --- /dev/null +++ b/main.py @@ -0,0 +1,256 @@ +import math +from kivy.app import App +from kivy.uix.widget import Widget +from kivy.graphics import Color,Line,Rectangle +from kivy.config import Config + +# App class +class Jam_app(App): + # name of .kv file for main interface + #kv_file="jam.kv" + + def build(self): + parent=Widget() + self.cross_painter=Cross_painter() + parent.add_widget(self.cross_painter) + return parent + +# cross painter +class Cross_painter(Widget): + + def __init__(self,**kwargs): + + # list of crosses + self.crosses=[] + + # selected cross + self.selected=None + + # init Widget + super(Cross_painter,self).__init__(**kwargs) + + + # draw all crosses + def draw(self): + with self.canvas: + for cross in self.crosses: + cross.draw() + + + # respond to mouse down + def on_touch_down(self,touch): + # create new cross + if touch.button=="right": + if self.check_add((touch.x,touch.y)): + new=Cross((touch.x,touch.y)) + with self.canvas: + new.draw() + # add to list + self.crosses.append(new) + + # select cross + if touch.button=="left": + # unselect + if self.selected!=None: + self.selected.selected=False + + # find cross under touch + self.selected=self.find_cross((touch.x,touch.y)) + # select + if self.selected!=None: + self.selected.selected=True + + + # respond to drag + def on_touch_move(self,touch): + # only move on left click + if touch.button=="left" and self.selected!=None: + #self.selected.pos=self.check_move((touch.x,touch.y),self.selected) + self.selected.pos=(touch.x,touch.y) + # redraw + self.canvas.clear() + self.draw() + ## check move + #if self.check_move((touch.x,touch.y),self.selected): + # # move cross + # self.selected.pos=(touch.x,touch.y) + # # redraw + # self.canvas.clear() + # self.draw() + ## try to move just x + #elif self.check_move((touch.x,self.selected.pos[1]),self.selected): + # # move cross + # self.selected.pos=(touch.x,self.selected.pos[1]) + # # redraw + # self.canvas.clear() + # self.draw() + ## try to move just y + #elif self.check_move((self.selected.pos[0],touch.y),self.selected): + # # move cross + # self.selected.pos=(self.selected.pos[0],touch.y) + # # redraw + # self.canvas.clear() + # self.draw() + + + # find the cross at position pos + def find_cross(self,pos): + for cross in self.crosses: + if cross_distx(pos,cross.pos)<=cross.size/2 or cross_disty(pos,cross.pos)<=cross.size/2: + return cross + # none found + return None + + # check that a cross can move to new position + def check_move(self,newpos,cross): + for other in self.crosses: + # do not compare a cross to itself + if other!=cross: + ## find nearest points + #nearest1=( + #newpos[0]-other.size*1.5*sgn(newpos[0]-other.pos[0]), + #newpos[1]-other.size*0.5*sgn(newpos[1]-other.pos[1]) + #) + #nearest2=( + #newpos[0]-other.size*0.5*sgn(newpos[0]-other.pos[0]), + #newpos[1]-other.size*1.5*sgn(newpos[1]-other.pos[1]) + #) + #dist1_x=abs(nearest1[0]-other.pos[0])/cross.size + #dist1_y=abs(nearest1[1]-other.pos[1])/cross.size + #dist2_x=abs(nearest2[0]-other.pos[0])/cross.size + #dist2_y=abs(nearest2[1]-other.pos[1])/cross.size + #if dist1_x>dist1_y and dist1_x<1.5: + # if dist1_y<0.5: + # print(1) + # return self.check_move((other.pos[0]+3*sgn(newpos[0]-other.pos[0])*cross.size,newpos[1]),cross) + # elif dist1_y<1.5: + # print(2) + # return self.check_move((other.pos[0]+2*sgn(newpos[0]-other.pos[0])*cross.size,other.pos[1]+sgn(newpos[1]-other.pos[1])*cross.size),cross) + #elif dist2_x>dist2_y and dist2_x<1.5: + # if dist2_y<0.5: + # print(3) + # return self.check_move((newpos[0],other.pos[1]+2*sgn(newpos[1]-other.pos[1])*cross.size),cross) + #elif dist1_y>dist1_x and dist1_y<1.5: + # if dist1_x<0.5: + # print(4) + # return self.check_move((newpos[0],other.pos[1]+2*sgn(newpos[1]-other.pos[1])*cross.size),cross) + #elif dist2_y>dist2_x and dist2_y<1.5: + # if dist2_x<0.5: + # print(5) + # return self.check_move((newpos[0],other.pos[1]+3*sgn(newpos[1]-other.pos[1])*cross.size),cross) + # elif dist2_x<1.5: + # print(6) + # return self.check_move((other.pos[0]+sgn(newpos[0]-other.pos[0])*cross.size,other.pos[1]+2*sgn(newpos[1]-other.pos[1])*cross.size),cross) + + if self.check_interaction(newpos,other)==False: + if math.sqrt((newpos[1]-cross.pos[1])**2+(newpos[0]-cross.pos[0])**2) > cross.size: + + # angle between newpos and cross + theta=math.atan2(newpos[1]-cross.pos[1],newpos[0]-cross.pos[0]) + # distance between new position and other + R=cross_polar(theta) + print(R,theta,R*math.cos(theta),R*math.sin(theta)) + return (other.pos[0]-cross.size*R*math.cos(theta),other.pos[1]-cross.size*R*math.sin(theta)) + return self.check_move((other.pos[0]-cross.size*R*(newpos[0]-cross.pos[0]),other.pos[1]-cross.size*R*(newpos[1]-cross.pos[1])),cross) + else: + return cross.pos + return newpos + + # check that a cross can be added at position + def check_add(self,pos): + for cross in self.crosses: + if self.check_interaction(pos,cross)==False: + return False + return True + + # check whether a cross at pos interacts with cross + def check_interaction(self,pos,cross): + #if max(abs(pos[0]-cross.pos[0]),abs(pos[1]-cross.pos[1]))<=cross.size/2: + # return False + ## find nearest point + #if abs(pos[0]-cross.pos[0])>abs(pos[1]-cross.pos[1]): + # nearest=( + # pos[0]-cross.size*1.5*sgn(pos[0]-cross.pos[0]), + # pos[1]-cross.size*0.5*sgn(pos[1]-cross.pos[1]) + # ) + #else: + # nearest=( + # pos[0]-cross.size*0.5*sgn(pos[0]-cross.pos[0]), + # pos[1]-cross.size*1.5*sgn(pos[1]-cross.pos[1]) + # ) + #if cross_distx(nearest,cross.pos)=5 + +# cross +class Cross(): + # size of central square + size=50 + + def __init__(self,pos,**kwargs): + self.pos=pos + self.color=kwargs.get("color",(0,0,1)) + self.selected=False + + def draw(self): + # fill + #if not self.selected: + # Color(*(self.color)) + #else: + # Color(1,0,0) + Color(*(self.color)) + Rectangle(pos=(self.pos[0]-self.size*1.5,self.pos[1]-self.size*0.5),size=(3*self.size,self.size)) + Rectangle(pos=(self.pos[0]-self.size*0.5,self.pos[1]-self.size*1.5),size=(self.size,3*self.size)) + + # stroke + Color(1,1,1) + Line(points=( + *(self.pos[0]-self.size*0.5,self.pos[1]-self.size*0.5), + *(self.pos[0]-self.size*0.5,self.pos[1]-self.size*1.5), + *(self.pos[0]+self.size*0.5,self.pos[1]-self.size*1.5), + *(self.pos[0]+self.size*0.5,self.pos[1]-self.size*0.5), + *(self.pos[0]+self.size*1.5,self.pos[1]-self.size*0.5), + *(self.pos[0]+self.size*1.5,self.pos[1]+self.size*0.5), + *(self.pos[0]+self.size*0.5,self.pos[1]+self.size*0.5), + *(self.pos[0]+self.size*0.5,self.pos[1]+self.size*1.5), + *(self.pos[0]-self.size*0.5,self.pos[1]+self.size*1.5), + *(self.pos[0]-self.size*0.5,self.pos[1]+self.size*0.5), + *(self.pos[0]-self.size*1.5,self.pos[1]+self.size*0.5), + *(self.pos[0]-self.size*1.5,self.pos[1]-self.size*0.5), + *(self.pos[0]-self.size*0.5,self.pos[1]-self.size*0.5), + )) + +# L_infinity distance rescalled by 3 in the x direction +def cross_distx(x,y): + return max(abs(x[0]-y[0])/3,abs(x[1]-y[1])) +# L_infinity distance rescalled by 3 in the y direction +def cross_disty(x,y): + return max(abs(x[0]-y[0]),abs(x[1]-y[1])/3) + +# polar description of touching cross +def cross_polar(t): + # by symmetry, put angle in interval (-pi/4,pi/4), and take absolute value + tt=abs((t+math.pi/4)%(math.pi/2)-math.pi/4) + if tt=0: + return 1 + return -1 + + +# disable red circles on right click +Config.set('input', 'mouse', 'mouse,disable_multitouch') + +# run +if __name__ == '__main__': + Jam_app().run()