Merge branch 'master' of https://github.com/skykooler/Lightningbeam
This commit is contained in:
commit
f0e2de1c2f
1
control
1
control
|
|
@ -7,3 +7,4 @@ Installed-size: 27000
|
|||
Depends: bash, python, python-imaging, imagemagick, libzzip-0-13, sox, python-numpy, mplayer
|
||||
Maintainer: skycooler@gmail.com
|
||||
Description: Lightningbeam is an open-source animated content creation tool.
|
||||
|
||||
|
|
|
|||
12
kt.py
12
kt.py
|
|
@ -9,16 +9,18 @@ from kivy.graphics import Color, Ellipse, Line
|
|||
|
||||
Builder.load_file("lightningbeam.kv")
|
||||
|
||||
class Lightningbeam(TabbedPanel):
|
||||
class LightningbeamPanel(TabbedPanel):
|
||||
|
||||
pass
|
||||
|
||||
class KivyCanvas(Widget):
|
||||
def on_touch_down(self, touch):
|
||||
print touch.button
|
||||
|
||||
class MyPaintApp(App):
|
||||
class LightningbeamApp(App):
|
||||
|
||||
def build(self):
|
||||
return Lightningbeam()
|
||||
|
||||
return LightningbeamPanel()
|
||||
|
||||
if __name__ == '__main__':
|
||||
MyPaintApp().run()
|
||||
LightningbeamApp().run()
|
||||
|
|
@ -1,12 +1,16 @@
|
|||
#:kivy 1.0
|
||||
#:import ActionScriptLexer pygments.lexers.ActionScriptLexer
|
||||
|
||||
<Lightningbeam>:
|
||||
<KivyCanvas>:
|
||||
|
||||
|
||||
<LightningbeamPanel>:
|
||||
do_default_tab: False
|
||||
TabbedPanelItem:
|
||||
text: 'Drawing'
|
||||
BoxLayout:
|
||||
orientation: "vertical"
|
||||
KivyCanvas:
|
||||
|
||||
TabbedPanelItem:
|
||||
text: 'Tools'
|
||||
|
|
@ -25,7 +29,7 @@
|
|||
Button:
|
||||
text: "Ellipse"
|
||||
Button:
|
||||
text: "Painbrush"
|
||||
text: "Paintbrush"
|
||||
TabbedPanelItem:
|
||||
text: 'ActionScript'
|
||||
CodeInput:
|
||||
|
|
|
|||
|
|
@ -111,6 +111,7 @@ def onMouseDownGroup(self, x, y,button=1,clicks=1):
|
|||
if svlgui.MODE in [" ", "s"]:
|
||||
if self.hitTest(x, y):
|
||||
self.clicked = True
|
||||
|
||||
elif svlgui.MODE in ["r", "e", "p"]:
|
||||
if svlgui.MODE=="r":
|
||||
# 'c' stands for 'current'
|
||||
|
|
@ -118,7 +119,26 @@ def onMouseDownGroup(self, x, y,button=1,clicks=1):
|
|||
elif svlgui.MODE=="e":
|
||||
self.cshape = ellipse(x, y, 0, 0)
|
||||
elif svlgui.MODE=="p":
|
||||
for i in self.lines:
|
||||
if abs(x-i.endpoint1.x)<10 and abs(y-i.endpoint1.y)<10:
|
||||
x, y = i.endpoint1.x, i.endpoint1.y
|
||||
break
|
||||
elif abs(x-i.endpoint2.x)<10 and abs(i.endpoint2.y)<10:
|
||||
x, y = i.endpoint2.x, i.endpoint2.y
|
||||
break
|
||||
self.cshape = shape(x, y)
|
||||
'''self.cshape = svlgui.Line(svlgui.Point(x, y),svlgui.Point(x,y))
|
||||
for i in self.lines:
|
||||
if abs(self.cshape.endpoint1.x-i.endpoint1.x)<10 and abs(self.cshape.endpoint1.y-i.endpoint1.y)<10:
|
||||
self.cshape.connection1 = i.endpoint1
|
||||
self.cshape.connection1.lines.add(self.cshape)
|
||||
break
|
||||
elif abs(self.cshape.endpoint1.x-i.endpoint2.x)<10 and abs(self.cshape.endpoint1.y-i.endpoint2.y)<10:
|
||||
self.cshape.connection1 = i.endpoint2
|
||||
self.cshape.connection1.lines.add(self.cshape)
|
||||
break
|
||||
self.lines.append(self.cshape)
|
||||
return'''
|
||||
#self.cshape.rotation = 5
|
||||
self.cshape.initx,self.cshape.inity = x, y
|
||||
self.add(self.cshape)
|
||||
|
|
@ -185,9 +205,50 @@ def onMouseUpGroup(self, x, y,button=1,clicks=1):
|
|||
undo_stack[-1] = undo_stack[-1].complete({"obj":cobj, "frame":self.activelayer.currentframe, "layer":self.activelayer})
|
||||
clear(redo_stack)
|
||||
elif svlgui.MODE=="p":
|
||||
print len(self.cshape.shapedata)
|
||||
prelen = len(self.cshape.shapedata)
|
||||
self.cshape.shapedata = misc_funcs.simplify_shape(self.cshape.shapedata, svlgui.PMODE.split()[-1],1)
|
||||
print len(self.cshape.shapedata)
|
||||
postlen = len(self.cshape.shapedata)
|
||||
print str((prelen-postlen)*100/prelen)+"% reduction: started at "+str(prelen)+" vertices, ended at "+str(postlen)+" vertices"
|
||||
if svlgui.PMODE.split()[-1]=="straight":
|
||||
lastline = None
|
||||
x, y = self.cshape.x, self.cshape.y
|
||||
for a, b in misc_funcs.pairwise(self.cshape.shapedata):
|
||||
l = svlgui.Line(svlgui.Point(a[1]+x,a[2]+y),svlgui.Point(b[1]+x,b[2]+y))
|
||||
if lastline:
|
||||
l.connection1 = lastline.endpoint2
|
||||
l.connection1.lines.add(l)
|
||||
lastline = l
|
||||
self.lines.append(l)
|
||||
self.delete(self.activelayer.frames[self.currentframe].objs[-1])
|
||||
for line in self.lines:
|
||||
for otherline in self.lines:
|
||||
if not otherline is line:
|
||||
if line.connection1 and otherline in line.connection1.lines: continue
|
||||
if line.connection2 and otherline in line.connection2.lines: continue
|
||||
inter = line.intersects(otherline)
|
||||
if inter:
|
||||
print "INTERSECTION"
|
||||
inter = svlgui.Point(*inter)
|
||||
l1 = svlgui.Line(line.endpoint1,inter,line.connection1,inter)
|
||||
l2 = svlgui.Line(line.endpoint2,inter,line.connection2,inter)
|
||||
l3 = svlgui.Line(otherline.endpoint1,inter,otherline.connection1,inter)
|
||||
l4 = svlgui.Line(otherline.endpoint2,inter,otherline.connection2,inter)
|
||||
inter.lines.add(l1)
|
||||
inter.lines.add(l2)
|
||||
inter.lines.add(l3)
|
||||
inter.lines.add(l4)
|
||||
self.lines[self.lines.index(line):self.lines.index(line)+1]=[l1,l2]
|
||||
self.lines[self.lines.index(otherline):self.lines.index(otherline)+1]=[l3,l4]
|
||||
break
|
||||
'''for i in self.lines:
|
||||
if abs(self.cshape.endpoint2.x-i.endpoint1.x)<10 and abs(self.cshape.endpoint2.y-i.endpoint1.y)<10:
|
||||
self.cshape.connection2 = i.endpoint1
|
||||
self.cshape.connection2.lines.add(self.cshape)
|
||||
break
|
||||
elif abs(self.cshape.endpoint2.x-i.endpoint2.x)<10 and abs(self.cshape.endpoint2.y-i.endpoint2.y)<10:
|
||||
self.cshape.connection2 = i.endpoint2
|
||||
self.cshape.connection2.lines.add(self.cshape)
|
||||
break'''
|
||||
self.cshape = None
|
||||
MainWindow.stage.draw()
|
||||
def onMouseUpObj(self, x, y,button=1,clicks=1):
|
||||
|
|
@ -247,6 +308,8 @@ def onMouseDragGroup(self, x, y,button=1,clicks=1):
|
|||
self.cshape.shapedata = [["M",x/2,0],["C",4*x/5,0,x,y/5,x,y/2],["C",x,4*y/5,4*x/5,y,x/2,y],["C",x/5,y,0,4*y/5,0,y/2],["C",0,y/5,x/5,0,x/2,0]]
|
||||
elif svlgui.MODE == "p":
|
||||
self.cshape.shapedata.append(["L",x-self.cshape.initx,y-self.cshape.inity])
|
||||
# self.cshape.endpoint2.x = x
|
||||
# self.cshape.endpoint2.y = y
|
||||
def onMouseDragObj(self, x, y,button=1,clicks=1):
|
||||
if svlgui.MODE==" ":
|
||||
self.x = x-self.initx
|
||||
|
|
@ -546,6 +609,8 @@ if svlgui.SYSTEM == "gtk":
|
|||
MainWindow = lightningbeam_windows.MainWindow()
|
||||
elif svlgui.SYSTEM=="osx":
|
||||
MainWindow = lightningbeam_windows.MainWindowOSX()
|
||||
elif svlgui.SYSTEM=="kivy":
|
||||
MainWindow = lightningbeam_windows.MainWindowKivy()
|
||||
elif svlgui.SYSTEM=="html":
|
||||
MainWindow = lightningbeam_windows.MainWindowHTML()
|
||||
elif svlgui.SYSTEM=="pyglet":
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import misc_funcs
|
|||
from misc_funcs import *
|
||||
|
||||
class MainWindow:
|
||||
''' GTK UI. Not currently used. '''
|
||||
def __init__(self):
|
||||
self.window = svlgui.Window("Lightningbeam")
|
||||
self.window.maximize()
|
||||
|
|
@ -134,6 +135,7 @@ class MainWindow:
|
|||
|
||||
|
||||
class MainWindowAndroid:
|
||||
''' Android UI. Not currently used. Will be replaced with Kivy. '''
|
||||
def __init__(self):
|
||||
class stagewrapper:
|
||||
def add(self, obj, x, y):
|
||||
|
|
@ -231,7 +233,8 @@ class MainWindowOSX:
|
|||
# self.toolbox.buttons[1][0]._int().enabled = False
|
||||
self.toolbox.buttons[3][0]._int().enabled = False
|
||||
self.toolbox.buttons[4][0]._int().enabled = False
|
||||
self.scriptwindow = svlgui.TextView(code=True)
|
||||
# self.scriptwindow = svlgui.TextView(code=True)
|
||||
self.scriptwindow = svlgui.TextView(code=False)
|
||||
self.paintgroup = svlgui.RadioGroup("Draw straight", "Draw smooth", "Draw as inked")
|
||||
def setmode(self):
|
||||
svlgui.PMODE = self.value
|
||||
|
|
@ -435,7 +438,10 @@ class MainWindowHTML:
|
|||
[self.stage,self.toolbox._int(),self.scriptwindow._int(),self.timelinebox._int()+2,0,"nsew", "hv"] )
|
||||
self.window.add(self.frame)
|
||||
|
||||
|
||||
class MainWindowKivy:
|
||||
def __init__(self):
|
||||
from kivy.lang import Builder
|
||||
Builder.load_file("lightningbeam.kv")
|
||||
|
||||
if __name__=="__main__":
|
||||
a = MainWindow()
|
||||
|
|
|
|||
|
|
@ -5,10 +5,12 @@
|
|||
|
||||
import svlgui
|
||||
from threading import Event, Thread
|
||||
from itertools import tee, izip
|
||||
import math
|
||||
import subprocess
|
||||
import re
|
||||
import os
|
||||
import sys
|
||||
|
||||
def select_any(self):
|
||||
svlgui.MODE = " "
|
||||
|
|
@ -96,8 +98,30 @@ def lastval(arr,index):
|
|||
return i
|
||||
|
||||
|
||||
def angle_to_point(point1, point2):
|
||||
deltaX = point2.x-point1.x
|
||||
deltaY = point2.y-point1.y
|
||||
angleInDegrees = math.atan2(-deltaY, deltaX) * 180 / math.pi
|
||||
if angleInDegrees<0: angleInDegrees = 360+angleInDegrees
|
||||
return angleInDegrees
|
||||
|
||||
def sqr(x) :
|
||||
return x * x
|
||||
def dist2(v, w):
|
||||
return sqr(v.x - w.x) + sqr(v.y - w.y)
|
||||
def distToSegmentSquared(p, v, w):
|
||||
l2 = dist2(v, w)
|
||||
if l2 == 0:
|
||||
return dist2(p, v)
|
||||
t = ((p.x - v.x) * (w.x - v.x) + (p.y - v.y) * (w.y - v.y)) / l2
|
||||
if t < 0:
|
||||
return dist2(p, v)
|
||||
if t > 1:
|
||||
return dist2(p, w)
|
||||
return dist2(p, svlgui.Point(x=(v.x+t*(w.x-v.x)), y=(v.y+t*(w.y-v.y))))
|
||||
|
||||
def distToSegment(p, v, w):
|
||||
return math.sqrt(distToSegmentSquared(p, v, w))
|
||||
|
||||
def catmullRom2bezier( points ) :
|
||||
#crp = points.split(/[,\s]/);
|
||||
|
|
@ -184,7 +208,6 @@ def simplify_shape(shape,mode,iterations):
|
|||
del shape[j]
|
||||
if mode=="smooth":
|
||||
shape = catmullRom2bezier([shape[0]]*2+shape+[shape[-1]])
|
||||
print shape
|
||||
|
||||
return shape#+nshape
|
||||
|
||||
|
|
@ -237,3 +260,34 @@ class RepeatTimer(Thread):
|
|||
|
||||
def cancel(self):
|
||||
self.finished.set()
|
||||
|
||||
|
||||
def pairwise(iterable):
|
||||
"s -> (s0,s1), (s1,s2), (s2, s3), ..."
|
||||
a, b = tee(iterable)
|
||||
next(b, None)
|
||||
return izip(a, b)
|
||||
|
||||
def hittest(linelist,x,y):
|
||||
hits = False
|
||||
def IsOnLeft(a, b, c):
|
||||
return Area2(a, b, c) > 0
|
||||
def IsOnRight(a, b, c):
|
||||
return Area2(a, b, c) < 0
|
||||
def IsCollinear(a, b, c):
|
||||
return Area2(a, b, c) == 0
|
||||
# calculates the triangle's size (formed by the "anchor" segment and additional point)
|
||||
def Area2(a, b, c):
|
||||
return (b[0]-a[0])*(c[1]-a[1])-(c[0]-a[0])*(b[1]-a[1])
|
||||
def intersects(a,b,c,d):
|
||||
return not (IsOnLeft(a,b,c) != IsOnRight(a,b,d))
|
||||
def ccw(a,b,c):
|
||||
return (c[1]-a[1])*(b[0]-a[0]) > (b[1]-a[1])*(c[0]-a[0])
|
||||
def intersect(a,b,c,d):
|
||||
return ccw(a,c,d) != ccw(b,c,d) and ccw(a,b,c) != ccw(a,b,d)
|
||||
for i in xrange(len(linelist)):
|
||||
hits = hits != intersect([linelist[i-1].endpoint1.x,linelist[i-1].endpoint1.y],
|
||||
[linelist[i].endpoint1.x,linelist[i].endpoint1.y],[x,y],[x,sys.maxint])
|
||||
print hits, x, y
|
||||
return hits
|
||||
|
||||
|
|
|
|||
442
svlgui.py
442
svlgui.py
|
|
@ -136,6 +136,8 @@ class Color (object):
|
|||
retval = "var "+self.val.split('/')[-1].replace(' ','_').replace('.','_')+" = new Image();\n"
|
||||
retval = retval+self.val.split('/')[-1].replace(' ','_').replace('.','_')+".src = \""+self.val.split("/")[-1]+"\";\n"
|
||||
return retval
|
||||
def print_json(self):
|
||||
return {'type':'Color','arguments':{'val':self.val}}
|
||||
def rgb2hex(r, g, b, a=1):
|
||||
r=hex(int(r*255)).split("x")[1].zfill(2)
|
||||
g=hex(int(g*255)).split("x")[1].zfill(2)
|
||||
|
|
@ -176,6 +178,8 @@ LINECOLOR = Color("#990099")
|
|||
FILLCOLOR = Color("#00FF00")
|
||||
TEXTCOLOR = Color("#000000")
|
||||
|
||||
LINEWIDTH = 2
|
||||
|
||||
#Magic. Detect platform and select appropriate toolkit. To be used throughout code.
|
||||
if sys.platform=="linux2":
|
||||
id = platform.machine()
|
||||
|
|
@ -201,6 +205,7 @@ if sys.platform=="linux2":
|
|||
import pickle
|
||||
import tarfile
|
||||
import tempfile
|
||||
|
||||
import GUI # Using PyGUI. Experimental.
|
||||
from GUI import Window as OSXWindow, Button as OSXButton, Image as OSXImage
|
||||
from GUI import Frame as OSXFrame, Color as OSXColor, Grid as OSXGrid, CheckBox as OSXCheckBox
|
||||
|
|
@ -218,7 +223,17 @@ if sys.platform=="linux2":
|
|||
from PIL import Image as PILImage
|
||||
except ImportError:
|
||||
import Image as PILImage
|
||||
SYSTEM="osx"
|
||||
from GUI.Geometry import offset_rect, rect_sized
|
||||
'''
|
||||
|
||||
from kivy.app import App # Using Kivy. Very experimental.
|
||||
from kivy.uix.widget import Widget
|
||||
from kivy.uix.codeinput import CodeInput
|
||||
from kivy.uix.tabbedpanel import TabbedPanel
|
||||
from kivy.uix.button import Button
|
||||
from kivy.graphics import Color, Ellipse, Line
|
||||
SYSTEM="kivy"'''
|
||||
|
||||
#If we can import this, we are in the install directory. Mangle media paths accordingly.
|
||||
try:
|
||||
|
|
@ -226,7 +241,6 @@ if sys.platform=="linux2":
|
|||
except:
|
||||
media_path = ""
|
||||
#app = GUI.application()
|
||||
SYSTEM="osx"
|
||||
TEMPDIR = "/tmp"
|
||||
FONT = u'Times New Roman'
|
||||
'''
|
||||
|
|
@ -377,6 +391,31 @@ if SYSTEM=="osx":
|
|||
|
||||
|
||||
app = Lightningbeam()
|
||||
elif SYSTEM=="kivy":
|
||||
class Lightningbeam(App):
|
||||
def build(self):
|
||||
return LightningbeamPanel()
|
||||
class LightningbeamPanel(TabbedPanel):
|
||||
pass
|
||||
class KivyCanvas(Widget):
|
||||
def draw(self):
|
||||
with self.canvas:
|
||||
for i in self.objs:
|
||||
try:
|
||||
i.draw(None)
|
||||
except:
|
||||
traceback.print_exc()
|
||||
def on_touch_down(self, touch):
|
||||
x, y = touch.x, touch.y
|
||||
try:
|
||||
try:
|
||||
for i in self.objs:
|
||||
i._onMouseDown(x,y,button=touch.button, clicks=(3 if touch.is_triple_click else (2 if touch.is_double_click else 1)))
|
||||
except ObjectDeletedError:
|
||||
return
|
||||
except:
|
||||
traceback.print_exc()
|
||||
self.draw()
|
||||
elif SYSTEM=="html":
|
||||
app = ""
|
||||
|
||||
|
|
@ -1151,6 +1190,9 @@ class Canvas(Widget):
|
|||
pass
|
||||
self.canvas = OSXCanvas(extent = (width, height), scrolling = 'hv')
|
||||
self.canvas.objs = self.objs
|
||||
elif SYSTEM=="kivy":
|
||||
|
||||
self.canvas = KivyCanvas()
|
||||
elif SYSTEM=="html":
|
||||
global ids
|
||||
while True:
|
||||
|
|
@ -1183,6 +1225,8 @@ class Canvas(Widget):
|
|||
def draw(self):
|
||||
if SYSTEM=="gtk":
|
||||
self.expose_event(self.canvas, "draw_event", self.objs)
|
||||
elif SYSTEM=="kivy":
|
||||
self.canvas.draw()
|
||||
elif SYSTEM in ["osx", "android"]:
|
||||
self.canvas.invalidate_rect((0,0,self.canvas.extent[0],self.canvas.extent[1]))
|
||||
elif SYSTEM=="html":
|
||||
|
|
@ -1190,6 +1234,8 @@ class Canvas(Widget):
|
|||
def is_focused(self):
|
||||
if SYSTEM=="osx":
|
||||
return self.canvas.is_target()
|
||||
else:
|
||||
return false
|
||||
def add(self, obj, x, y):
|
||||
obj.x = x
|
||||
obj.y = y
|
||||
|
|
@ -1491,6 +1537,8 @@ class Image(object):
|
|||
pass
|
||||
def print_sc(self):
|
||||
return ".png "+self.name+" \""+self.path+"\"\n"
|
||||
def print_json(self):
|
||||
return {'type':'Image','arguments':{'image':self.image,'x':self.x,'y':self.y,'animated':self.animated,'canvas':None,'htiles':self.htiles,'vtiles':self.vtiles,'skipl':false}}
|
||||
|
||||
class Shape (object):
|
||||
def __init__(self,x=0,y=0,rotation=0,fillcolor=None,linecolor=None):
|
||||
|
|
@ -1636,6 +1684,10 @@ class Shape (object):
|
|||
else:
|
||||
cr.stroke()
|
||||
cr.grestore()
|
||||
elif SYSTEM=="kivy":
|
||||
Color(1, 1, 0)
|
||||
d = 30.
|
||||
Ellipse(pos=(self.x - d / 2, self.y - d / 2), size=(d, d))
|
||||
elif SYSTEM=="html":
|
||||
tb = ""
|
||||
tb+="cr.save()\n"
|
||||
|
|
@ -1766,6 +1818,13 @@ class Shape (object):
|
|||
retval += self.name+".fill = \""+self.fillcolor.rgb+"\";\n"+self.name+".line = \""+self.linecolor.rgb+"\";\n"
|
||||
retval += self.name+".filled = "+str(self.filled).lower()+";\n"
|
||||
return retval
|
||||
def print_json(self):
|
||||
return {'type':'Shape','arguments':{'x':self.x,
|
||||
'y':self.y,
|
||||
'rotation':self.rotation,
|
||||
'linecolor':self.linecolor.print_json(),
|
||||
'fillcolor':self.fillcolor.print_json()},
|
||||
'properties':{'shapedata':self.shapedata}}
|
||||
|
||||
class Text (object):
|
||||
def __getstate__(self):
|
||||
|
|
@ -2359,6 +2418,7 @@ class Layer:
|
|||
print "#>>",i
|
||||
for j in self.frames[self.currentframe].objs:
|
||||
if j == i:
|
||||
print "Deleting",j
|
||||
del self.currentFrame()[self.currentFrame().index(j)]
|
||||
def add_frame(self,populate):
|
||||
if self.activeframe>len(self.frames):
|
||||
|
|
@ -2553,6 +2613,9 @@ class Group (object):
|
|||
self.tempgroup = None
|
||||
self.is_mc = False
|
||||
self.name = "g"+str(int(random.random()*10000))+str(SITER)
|
||||
self.lines = []
|
||||
self.fills = []
|
||||
self.activepoint = None
|
||||
if "onload" in kwargs:
|
||||
kwargs["onload"](self)
|
||||
def draw(self,cr=None,transform=None,rect=None):
|
||||
|
|
@ -2573,8 +2636,14 @@ class Group (object):
|
|||
cr.pencolor = Color([0,0,1]).pygui
|
||||
cr.stroke_rect([sorted([self.startx,self.cx])[0], sorted([self.starty,self.cy])[0], \
|
||||
sorted([self.startx,self.cx])[1], sorted([self.starty,self.cy])[1]])
|
||||
for i in self.fills:
|
||||
i.draw(cr, rect=rect)
|
||||
for i in self.lines:
|
||||
i.draw(cr, rect=rect)
|
||||
def add(self, *args):
|
||||
self.activelayer.add(*args)
|
||||
def delete(self, *args):
|
||||
self.activelayer.delete(*args)
|
||||
def add_frame(self, populate):
|
||||
self.activelayer.add_frame(populate)
|
||||
def add_layer(self, index):
|
||||
|
|
@ -2644,6 +2713,147 @@ class Group (object):
|
|||
self.tempgroup = None
|
||||
self.activelayer.currentselect = None
|
||||
self.startx, self.starty = x, y
|
||||
if MODE in " s":
|
||||
for i in self.lines:
|
||||
if abs(x-i.endpoint1.x)<10 and abs(y-i.endpoint1.y)<10:
|
||||
i.endpoint1.x = x
|
||||
i.endpoint1.y = y
|
||||
self.activepoint = i.endpoint1
|
||||
return
|
||||
elif abs(x-i.endpoint2.x)<10 and abs(y-i.endpoint2.y)<10:
|
||||
i.endpoint2.x = x
|
||||
i.endpoint2.y = y
|
||||
self.activepoint = i.endpoint2
|
||||
return
|
||||
elif MODE=="b":
|
||||
nlines = [i for i in self.lines]
|
||||
# First, remove all line segments that have at least one free endpoit, not coincident with any other segment.
|
||||
# Do that repeatedly until no such segment remains.
|
||||
for i in reversed(nlines):
|
||||
if not (i.endpoint1 in [j.endpoint1 for j in nlines if not j==i]+[j.endpoint2 for j in nlines if not j==i]):
|
||||
nlines.remove(i)
|
||||
elif not (i.endpoint2 in [j.endpoint1 for j in nlines if not j==i]+[j.endpoint2 for j in nlines if not j==i]):
|
||||
nlines.remove(i)
|
||||
|
||||
# Find the closest segment to the point.
|
||||
if nlines:
|
||||
mindist = sys.maxint
|
||||
point = Point(x, y)
|
||||
closestsegment = None
|
||||
for i in nlines:
|
||||
d = misc_funcs.distToSegment(point,i.endpoint1,i.endpoint2)
|
||||
if d<mindist:
|
||||
mindist = d
|
||||
closestsegment = i
|
||||
|
||||
print closestsegment
|
||||
|
||||
# Go to the endpoint and turn right. Repeat until you hit the closest segment again.
|
||||
if closestsegment:
|
||||
# Grab angles to closest segments endpoints, go to the counterclockwise-most one
|
||||
angle1 = misc_funcs.angle_to_point(point, closestsegment.endpoint1)
|
||||
angle2 = misc_funcs.angle_to_point(point, closestsegment.endpoint2)
|
||||
if (angle1<angle2 and angle2-angle1<180):
|
||||
startpoint = closestsegment.endpoint2
|
||||
sp = 2
|
||||
else:
|
||||
startpoint = closestsegment.endpoint1
|
||||
sp = 1
|
||||
linelist = [closestsegment]
|
||||
while True:
|
||||
# try:
|
||||
nextline = max([[closestsegment.angle(i),i] for i in startpoint.lines if not i==closestsegment])[1]
|
||||
# except:
|
||||
# break
|
||||
closestsegment = nextline
|
||||
startpoint = [None,closestsegment.endpoint1,closestsegment.endpoint2][sp]
|
||||
if not nextline in linelist:
|
||||
linelist.append(nextline)
|
||||
else:
|
||||
break
|
||||
|
||||
# Check if the polygon encloses the given point. If it is not, then we've found an "island"
|
||||
if misc_funcs.hittest(linelist,x,y):
|
||||
f = Fill()
|
||||
dic = {}
|
||||
for i in linelist:
|
||||
dic[i.endpoint1]=i
|
||||
|
||||
def walk(list_of_lines, starting_point):
|
||||
lookup_map = {}
|
||||
for i in list_of_lines:
|
||||
lookup_map[i.endpoint1]=i
|
||||
cur_point = starting_point
|
||||
visited_points = []
|
||||
print lookup_map
|
||||
while cur_point.endpoint2 in lookup_map and not cur_point in visited_points:
|
||||
visited_points.append(cur_point)
|
||||
cur_point = lookup_map[cur_point.endpoint2]
|
||||
yield cur_point
|
||||
|
||||
|
||||
# f.lines = linelist
|
||||
f.lines = [x for x in walk(linelist,linelist[0])]
|
||||
print f.lines
|
||||
self.fills.append(f)
|
||||
else:
|
||||
print "No hit"
|
||||
|
||||
|
||||
'''nlines = [i for i in self.lines]
|
||||
endsleft = True
|
||||
while endsleft:
|
||||
endsleft = False
|
||||
for i in reversed(nlines):
|
||||
if not (i.endpoint1 in [j.endpoint1 for j in nlines if not j==i]+[j.endpoint2 for j in nlines if not j==i]):
|
||||
nlines.remove(i)
|
||||
endsleft = True
|
||||
elif not (i.endpoint2 in [j.endpoint1 for j in nlines if not j==i]+[j.endpoint2 for j in nlines if not j==i]):
|
||||
nlines.remove(i)
|
||||
endsleft = True
|
||||
print "2728",nlines
|
||||
|
||||
if nlines:
|
||||
mindist = sys.maxint
|
||||
point = Point(x, y)
|
||||
closestsegment = None
|
||||
|
||||
for i in nlines:
|
||||
d = misc_funcs.distToSegment(point,i.endpoint1,i.endpoint2)
|
||||
if d<mindist:
|
||||
mindist = d
|
||||
closestsegment = i
|
||||
|
||||
f = Fill()
|
||||
f.lines = nlines
|
||||
self.fills.append(f)
|
||||
|
||||
if closestsegment:
|
||||
# Then go to endpoint of closestsegment counterclockwise from point
|
||||
angle1 = misc_funcs.angle_to_point(point, closestsegment.endpoint1)
|
||||
angle2 = misc_funcs.angle_to_point(point, closestsegment.endpoint2)
|
||||
if (angle1<angle2 and angle2-angle1<180):
|
||||
startpoint = closestsegment.endpoint2
|
||||
else:
|
||||
startpoint = closestsegment.endpoint1
|
||||
print startpoint.lines
|
||||
linelist = [closestsegment]
|
||||
# nextline = max([[closestsegment.angle(i),i] for i in startpoint.lines if not i in linelist])
|
||||
# print nextline
|
||||
# Then, follow clockwise-most segment leading off from said point
|
||||
while True:
|
||||
try:
|
||||
nextline = max([[closestsegment.angle(i),i] for i in startpoint.lines if not i==closestsegment])[1]
|
||||
except:
|
||||
break
|
||||
closestsegment = nextline
|
||||
if not nextline in linelist:
|
||||
linelist.append(nextline)
|
||||
else:
|
||||
break
|
||||
print "*****",linelist
|
||||
# Continue until closestsegment is reached. I _think_ this is inevitable.'''
|
||||
|
||||
self.selecting = True
|
||||
else:
|
||||
self.onMouseDown(self, x, y, button=button, clicks=clicks)
|
||||
|
|
@ -2658,10 +2868,35 @@ class Group (object):
|
|||
self.dragging = False
|
||||
self.selecting = False
|
||||
x, y = self.localtransform(x, y)
|
||||
# If we are in selection mode and the current level (i.e. what we're inside of) is this layer
|
||||
if self.activelayer.level and MODE in [" ", "s"]:
|
||||
# If we have a selection
|
||||
if self.activelayer.currentselect:
|
||||
self.activelayer.currentselect._onMouseUp(x, y, button=button, clicks=clicks)
|
||||
# If we have a point that we're dragging around (vector editing)
|
||||
elif self.activepoint:
|
||||
for i in self.lines:
|
||||
if abs(self.activepoint.x-i.endpoint1.x)<10 and abs(self.activepoint.y-i.endpoint1.y)<10:
|
||||
try:
|
||||
[j for j in self.lines if self.activepoint==j.endpoint1][0].assign(i.endpoint1, 1)
|
||||
except IndexError:
|
||||
try:
|
||||
[j for j in self.lines if self.activepoint==j.endpoint2][0].assign(i.endpoint1, 2)
|
||||
break
|
||||
except IndexError: pass
|
||||
if abs(self.activepoint.x-i.endpoint2.x)<10 and abs(self.activepoint.y-i.endpoint2.y)<10:
|
||||
try:
|
||||
[j for j in self.lines if self.activepoint==j.endpoint1][0].assign(i.endpoint2, 1)
|
||||
except IndexError:
|
||||
try:
|
||||
[j for j in self.lines if self.activepoint==j.endpoint2][0].assign(i.endpoint2, 2)
|
||||
break
|
||||
except IndexError: pass
|
||||
break
|
||||
self.activepoint = None
|
||||
# if neither of those, but we've dragged the mouse at least 4 pixels in either axis
|
||||
elif abs(self.startx-x)>4 or abs(self.starty-y)>4:
|
||||
# this should make a temporary group containing all the objects within the area we dragged
|
||||
objs = []
|
||||
for i in reversed(self.currentFrame()):
|
||||
if self.startx<i.x+i.minx<x or self.startx<i.x+i.maxx<x:
|
||||
|
|
@ -2695,6 +2930,9 @@ class Group (object):
|
|||
if self.activelayer.level and MODE in [" ", "s"]:
|
||||
if self.activelayer.currentselect:
|
||||
self.activelayer.currentselect._onMouseDrag(x, y, button=button)
|
||||
elif self.activepoint:
|
||||
self.activepoint.x = x
|
||||
self.activepoint.y = y
|
||||
else:
|
||||
self.onMouseDrag(self, x, y, button=button)
|
||||
def onMouseDrag(self, self1, x, y, button=1, clicks=1):
|
||||
|
|
@ -2732,6 +2970,17 @@ class Group (object):
|
|||
# retval+=i.print_sc(True, False)
|
||||
if not self.name=="_root":
|
||||
retval+=".sprite "+self.name+"\n"
|
||||
if self.fills:
|
||||
for i in self.fills:
|
||||
retval+=i.print_sc()
|
||||
retval+=".put fill_"+str(i.__hash__())+"\n"
|
||||
if self.lines:
|
||||
for i in self.lines:
|
||||
retval+=".outline "+self.name+"_line_"+str(i.__hash__())+"_outline:\n"
|
||||
retval+=" "+i.print_sc()
|
||||
retval+="\n.end\n"
|
||||
retval+=".filled "+self.name+"_line_"+str(i.__hash__())+" outline="+self.name+"_line_"+str(i.__hash__())+"_outline color="+i.linecolor.rgb+"\n"
|
||||
retval+=".put "+self.name+"_line_"+str(i.__hash__())+"\n"
|
||||
for i in xrange(self.maxframe()):
|
||||
for j in self.layers:
|
||||
if j.frames[i]:
|
||||
|
|
@ -2776,7 +3025,7 @@ class Group (object):
|
|||
class TemporaryGroup(Group):
|
||||
"""Created when selecting multiple items, for ease of use."""
|
||||
def __init__(self, *args, **kwargs):
|
||||
(TemporaryGroup, self).__init__(*args, **kwargs)
|
||||
super(TemporaryGroup, self).__init__(*args, **kwargs)
|
||||
# def draw(self, cr=None, transform=None, rect=None):
|
||||
# super(TemporaryGroup, self).draw(cr, transform, rect)
|
||||
# print self.x, self.activelayer.x
|
||||
|
|
@ -2834,6 +3083,195 @@ class TemporaryGroup(Group):
|
|||
elif key=="down_arrow":
|
||||
self1.y+=1
|
||||
|
||||
class Point(object):
|
||||
"""represents an x,y point, might store other data in the future"""
|
||||
def __init__(self, x, y):
|
||||
super(Point, self).__init__()
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.lines = set()
|
||||
def __repr__(self):
|
||||
return "<Point x="+str(self.x)+" y="+str(self.y)+">"
|
||||
|
||||
|
||||
class Line(object):
|
||||
"""Use Lines to build Shapes while allowing paintbucketing."""
|
||||
def __init__(self, endpoint1, endpoint2, connection1 = None, connection2 = None):
|
||||
super(Line, self).__init__()
|
||||
self.endpoint1 = endpoint1
|
||||
self.endpoint2 = endpoint2
|
||||
self.endpoint1.lines.add(self)
|
||||
self.endpoint2.lines.add(self)
|
||||
self.connection1 = connection1
|
||||
self.connection2 = connection2
|
||||
if self.connection1: self.connection1.lines.add(self)
|
||||
if self.connection2: self.connection2.lines.add(self)
|
||||
self.linecolor = LINECOLOR
|
||||
self.linewidth = LINEWIDTH
|
||||
def __repr__(self):
|
||||
return "<Line (endpoint1=("+str(self.endpoint1.x)+","+str(self.endpoint1.y)+"),endpoint2=("+str(self.endpoint2.x)+","+str(self.endpoint2.y)+"))>"
|
||||
def assign(self, point, which):
|
||||
if which==1:
|
||||
self.connection1 = point
|
||||
self.connection1.lines.add(self)
|
||||
elif which==2:
|
||||
self.connection2 = point
|
||||
self.connection2.lines.add(self)
|
||||
def angle(self, other):
|
||||
if self.endpoint1==other.endpoint1:
|
||||
x1 = self.endpoint2.x-self.endpoint1.x
|
||||
y1 = self.endpoint2.y-self.endpoint1.y
|
||||
x2 = other.endpoint2.x-other.endpoint1.x
|
||||
y2 = other.endpoint2.y-other.endpoint1.y
|
||||
elif self.endpoint2==other.endpoint1:
|
||||
x1 = self.endpoint1.x-self.endpoint2.x
|
||||
y1 = self.endpoint1.y-self.endpoint2.y
|
||||
x2 = other.endpoint2.x-other.endpoint1.x
|
||||
y2 = other.endpoint2.y-other.endpoint1.y
|
||||
elif self.endpoint1==other.endpoint2:
|
||||
x1 = self.endpoint2.x-self.endpoint1.x
|
||||
y1 = self.endpoint2.y-self.endpoint1.y
|
||||
x2 = other.endpoint1.x-other.endpoint2.x
|
||||
y2 = other.endpoint1.y-other.endpoint2.y
|
||||
elif self.endpoint2==other.endpoint2:
|
||||
x1 = self.endpoint1.x-self.endpoint2.x
|
||||
y1 = self.endpoint1.y-self.endpoint2.y
|
||||
x2 = other.endpoint1.x-other.endpoint2.x
|
||||
y2 = other.endpoint1.y-other.endpoint2.y
|
||||
dot = x1*x2+y1*y2
|
||||
mag1 = math.sqrt(x1**2+y1**2)
|
||||
mag2 = math.sqrt(x2**2+y2**2)
|
||||
angle = math.acos(dot/(mag1*mag2))/math.pi*180
|
||||
cz = x1*y2-y1*x2
|
||||
if cz>0: angle=360-angle
|
||||
return angle
|
||||
def intersects(self,other):
|
||||
'''def IsOnLeft(a,b,c):
|
||||
return Area2(a,b,c) > 0
|
||||
def IsOnRight(a,b,c):
|
||||
return Area2(a,b,c) < 0
|
||||
def IsCollinear(a,b,c):
|
||||
return Area2(a,b,c) == 0
|
||||
def Area2 (a,b,c):
|
||||
return (b.x - a.x) * (c.y - a.y) - (c.x - a.x) * (b.y - a.y)
|
||||
if (IsOnLeft(self.endpoint1,self.endpoint2,other.endpoint1) and IsOnRight(self.endpoint1,self.endpoint2,other.endpoint2))
|
||||
or (IsOnLeft(self.endpoint1,self.endpoint2,other.endpoint2) and IsOnRight(self.endpoint1,self.endpoint2,other.endpoint1):
|
||||
if (IsOnLeft(other.endpoint1,other.endpoint2,self.endpoint1) and IsOnRight(other.endpoint1,other.endpoint2,self.endpoint2))
|
||||
or (IsOnLeft(other.endpoint1,other.endpoint2,self.endpoint2) and IsOnRight(other.endpoint1,other.endpoint2,self.endpoint1):
|
||||
return True'''
|
||||
# Formula for line is y = mx + b
|
||||
try:
|
||||
sm = (self.endpoint1.y-self.endpoint2.y)/(self.endpoint1.x-self.endpoint1.y)
|
||||
om = (other.endpoint1.y-other.endpoint2.y)/(other.endpoint1.x-other.endpoint1.y)
|
||||
sb = self.endpoint1.y-sm*self.endpoint1.x
|
||||
ob = other.endpoint1.y-sm*other.endpoint1.x
|
||||
if sm == om: return False
|
||||
x = (ob-sb)/(sm-om)
|
||||
y = sm*x + sb
|
||||
if min(self.endpoint1.x,self.endpoint2.x)<x<max(self.endpoint1.x,self.endpoint2.x):
|
||||
return [x,y]
|
||||
else:
|
||||
return False
|
||||
except ZeroDivisionError:
|
||||
# One of the lines is vertical.
|
||||
# Formula for line in terms of y is x = my + b
|
||||
try:
|
||||
sm = (self.endpoint1.x-self.endpoint2.x)/(self.endpoint1.y-self.endpoint1.x)
|
||||
om = (other.endpoint1.x-other.endpoint2.x)/(other.endpoint1.y-other.endpoint1.x)
|
||||
sb = self.endpoint1.x-sm*self.endpoint1.y
|
||||
ob = other.endpoint1.x-sm*other.endpoint1.y
|
||||
if sm == om: return False
|
||||
y = (ob-sb)/(sm-om)
|
||||
x = sm*y + sb
|
||||
if min(self.endpoint1.y,self.endpoint2.y)<y<max(self.endpoint1.y,self.endpoint2.y):
|
||||
return [x,y]
|
||||
except ZeroDivisionError:
|
||||
# One of the lines is horizontal, too. Or one has zero length.
|
||||
# Logic this.
|
||||
return False
|
||||
|
||||
return False
|
||||
def draw(self, cr=None, transform=None, rect=None):
|
||||
if self.connection1:
|
||||
self.endpoint1 = self.connection1
|
||||
if self.connection2:
|
||||
self.endpoint2 = self.connection2
|
||||
if SYSTEM=="gtk":
|
||||
cr.save()
|
||||
cr.set_source(self.linecolor.cairo)
|
||||
cr.set_line_width(max(self.linewidth,1))
|
||||
cr.move_to(self.endpoint1.x,self.endpoint2.y)
|
||||
cr.line_to(self.endpoint2.x,self.endpoint2.y)
|
||||
cr.stroke()
|
||||
cr.restore()
|
||||
elif SYSTEM=="android":
|
||||
global tb
|
||||
tb+="cr.save()\n"
|
||||
tb+="cr.lineWidth = "+str(max(self.linewidth,1))+"\n"
|
||||
tb+="cr.moveTo("+str(self.endpoint1.x)+","+str(self.endpoint1.y)+")\n"
|
||||
tb+="cr.lineTo("+str(self.endpoint2.x)+","+str(self.endpoint2.y)+")\n"
|
||||
tb+="cr.stroke()\n"
|
||||
tb+="cr.restore()\n"
|
||||
elif SYSTEM=="osx":
|
||||
if USING_GL:
|
||||
cr.save()
|
||||
glColor3f(1.0,0.0,0.0)
|
||||
glBegin(GL_LINES)
|
||||
cr.x, cr.y = (self.endpoint1.x, self.endpoint1.y)
|
||||
glVertex2f(cr.x, -cr.y)
|
||||
point = (self.endpoint2.x, self.endpoint2.y)
|
||||
glVertex2f(point[0], -point[1]) # because OpenGL swaps y coords
|
||||
cr.x, cr.y = point
|
||||
glEnd()
|
||||
cr.restore()
|
||||
else:
|
||||
cr.gsave()
|
||||
cr.newpath()
|
||||
cr.pencolor = self.linecolor.pygui
|
||||
cr.pensize = max(self.linewidth,1)
|
||||
cr.moveto(self.endpoint1.x, self.endpoint1.y)
|
||||
cr.lineto(self.endpoint2.x,self.endpoint2.y)
|
||||
cr.stroke()
|
||||
cr.grestore()
|
||||
elif SYSTEM=="html":
|
||||
tb = ""
|
||||
tb+="cr.save()\n"
|
||||
tb+="cr.lineWidth = "+str(max(self.linewidth,1))+"\n"
|
||||
tb+="cr.moveTo("+str(self.endpoint1.x)+","+str(self.endpoint1.y)+")\n"
|
||||
tb+="cr.lineTo("+str(self.endpoint2.x)+","+str(self.endpoint2.y)+")\n"
|
||||
tb+="cr.stroke()\n"
|
||||
tb+="cr.restore()\n"
|
||||
jscommunicate(tb)
|
||||
def print_sc(self):
|
||||
return "M "+str(self.endpoint1.x)+" "+str(self.endpoint1.y)+" L "+str(self.endpoint2.x)+" "+str(self.endpoint2.y)
|
||||
|
||||
class Fill(object):
|
||||
"""Fills are Shapes without edges, built from Lines"""
|
||||
def __init__(self):
|
||||
super(Fill,self).__init__()
|
||||
self.lines = []
|
||||
self.fillcolor = FILLCOLOR
|
||||
def draw(self, cr=None, transform=None, rect=None):
|
||||
if SYSTEM=="osx":
|
||||
if USING_GL:
|
||||
pass
|
||||
else:
|
||||
cr.gsave()
|
||||
cr.newpath()
|
||||
cr.fillcolor = self.fillcolor.pygui
|
||||
cr.moveto(self.lines[0].endpoint1.x,self.lines[0].endpoint1.y)
|
||||
for i in self.lines:
|
||||
cr.lineto(i.endpoint2.x,i.endpoint2.y)
|
||||
cr.fill()
|
||||
cr.grestore()
|
||||
def print_sc(self):
|
||||
retval = ".outline fill_"+str(self.__hash__())+"_outline:\n"
|
||||
retval += "M "+str(self.lines[0].endpoint1.x)+" "+str(self.lines[0].endpoint1.y)
|
||||
for i in self.lines:
|
||||
retval += " L "+str(i.endpoint2.x)+" "+str(i.endpoint2.y)
|
||||
retval += "\n.end\n"
|
||||
retval += ".filled fill_"+str(self.__hash__())+" outline=fill_"+str(self.__hash__())+"_outline fill="+self.fillcolor.rgb+"\n"
|
||||
return retval
|
||||
|
||||
def set_cursor(curs, widget=None):
|
||||
if SYSTEM == "osx":
|
||||
|
|
|
|||
Loading…
Reference in New Issue