#!/usr/bin/env python ''' shapes.py Copyright (C) 2015 Paco Garcia, www.arakne.es This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ----------------------- ''' # standard library import locale, os, sys, tempfile, webbrowser, math import inkex, simplestyle, simpletransform def info(s, newLine="\n"): sys.stderr.write(s.encode("UTF-8") + newLine) def tern(condition,val1,val2): return val1 if condition else val2 class XY: """A class for creating Inkscape SVG Effects""" def __init__(self, *args, **kwargs): self.co=[0.0,0.0] lArgs=len(args) if lArgs>0: if lArgs==1: if type(args[0]==XY): self.co=args[0].co else: self.co=[args[0],args[0]] if lArgs>1: self.co=[args[0],args[1]] def __add__(self,xy): co=[self.co[0] + xy.co[0],self.co[1] + xy.co[1]] self.co = co return self def __sub__(self,xy): self.co=[self.co[0] - xy.co[0], self.co[1] - xy.co[1]] return self def __eq__(self,xy): return (self.co[0] == xy.x and self.co[1] == xy.y) def sub(self,xy): #self.co=[self.co[0] - xy.co[0],self.co[1] - xy.co[1]] self.__sub__(xy) return self def mul(self,xy): if type(xy)==XY: co=[self.co[0] * xy.co[0],self.co[1] * xy.co[1]] else: co=[self.co[0] * xy,self.co[1] * xy] self.co = co return self def div(self,xy): if type(xy)==XY: co=[self.co[0] / xy.co[0],self.co[1] / xy.co[1]] else: co=[self.co[0] / xy, self.co[1] / xy] self.co = co return self def vlength(self): return math.sqrt((self.co[0]*self.co[0])+(self.co[1]*self.co[1])) def rot(self,ang): x,y,sa,ca= (self.co[0], self.co[1], math.sin(ang), math.cos(ang)) self.co=[ca * x - sa * y, sa * x + ca * y] return self def Rot(self,p,r): self.co=[math.cos(r)*p,math.sin(r)*p] return self def rotate(self,rot,cX=0.0,cY=0.0): px = cX + (self.x-cX) * math.cos(rot) - (self.y-cY)*math.sin(rot) py = cY + (self.x-cX) * math.sin(rot) + (self.y-cY)*math.cos(rot) self.co = [px,py] return self def rotateD(self,rot,cX=0.0,cY=0.0): self.rotate(math.radians(rot),cX,cY) return self def VDist(self,V2): tmp = XY(self.co[0],self.co[1]) tmp = tmp.sub(V2) return tmp.vlength() def st(self): return str(self.co[0])+','+str(self.co[1]) @property def x(self): return self.co[0] @property def y(self): return self.co[1] def hipo(self,xy): return math.sqrt( math.pow(self.x-xy.x,2) + math.pow(self.y-xy.y,2) ) def angBetween2Lines(self,p1,p2): # pC punto comun return math.atan2(self.y - p1.y, self.x - p1.x) - math.atan2(self.y - p2.y, self.x - p2.x) def getAngle(self,b): return math.atan2(b.y - self.y, b.x - self.x) def getAngleD(self,b): return math.degrees(math.atan2(b.y - self.y, b.x - self.x)) # ________________________________________________________________ # ________________________________________________________________ # ________________________________________________________________ class bezpnt(object): def __init__(self,pfixed=None,pprev=None,pnext=None): if isinstance(pfixed, list): self.fixed = XY(pfixed[0],pfixed[1]) else: self.fixed = pfixed if isinstance(pprev, list): self.prev = XY(pprev[0],pprev[1]) else: self.prev = pprev if isinstance(pnext, list): self.next = XY(pnext[0],pnext[1]) else: self.next = pnext return def translate(self,x,y): self.fixed + XY(x,y) if self.prev!=None:self.prev + XY(x,y) if self.next!=None:self.next + XY(x,y) return self def scale(self,x=1.0,y=1.0): self.fixed.scale(x,y) if self.prev!=None:self.prev.scale(x,y) if self.next!=None:self.next.scale(x,y) return self def rotate(self,rot,cX=0.0,cY=0.0): self.fixed.rotate(rot,cX,cY) if self.prev!=None:self.prev.rotate(rot,cX,cY) if self.next!=None:self.next.rotate(rot,cX,cY) return self def skew(self,rotx,roty,cX=0.0,cY=0.0): self.fixed.skew(rotx,roty,cX,cY) if self.prev!=None:self.prev.skew(rotx,roty,cX,cY) if self.next!=None:self.next.skew(rotx,roty,cX,cY) return self def copy(self,bez2): try: self.fixed=XY().copy(bez2.fixed) self.prev = None if bez2.prev == None else XY().copy(bez2.prev) self.next = None if bez2.next == None else XY().copy(bez2.next) except Exception,e: gimp.message(str(e)) return self def arrXY(self): pts=[] if self.prev == None: pts+=self.fixed.arrXY(1) else: pts+=self.prev.arrXY(1) pts+=self.fixed.arrXY(1) if self.next==None: pts+=self.fixed.arrXY(1) else: pts+=self.next.arrXY(1) return pts def Prev(self): p = self.prev if p==None: p=self.fixed return p def Next(self): p = self.next if p==None: p=self.fixed return p def Fixed(self): return self.fixed def flip(self): p=self.prev n=self.next self.prev=n self.next=p def createSmallArcBez(r, a1, a2,rot): a = (a2 - a1) * 0.5 p4 = XY(r * math.cos(a), r * math.sin(a)) p1 = XY(p4.x, -p4.y) k = 0.5522847498 f = k * math.tan(a) p2 = XY(p1.x + f * p4.y, p1.y + f * p4.x) p3 = XY(p2.x,-p2.y) ar = a + a1 P1 = XY(r * math.cos(a1), r * math.sin(a1)).rotate(rot) P2 = XY(p2.x, p2.y).rotate(ar).rotate(rot) P3 = XY(p3.x, p3.y).rotate(ar).rotate(rot) P4 = XY(r * math.cos(a2),r * math.sin(a2)).rotate(rot) B1=bezpnt(P1,None,P2) B2=bezpnt(P4,P3) return [B1,B2] def createArcBez(rad, sAng, eAng): EPSILON = 0.0000000001 bezs =[] if eAng < sAng: eAng += 360.0 sAng = math.radians(sAng) eAng = math.radians(eAng) rot = sAng sAng = math.radians(0) eAng = eAng - rot pi2 = math.pi * 2 sAng, eAng = (sAng % pi2, eAng % pi2) pi_2 = math.pi * 0.5 sign = 1 if (sAng < eAng) else -1 a1 = sAng totAng = min(math.pi * 2, abs(eAng - sAng)) while (totAng > EPSILON): a2 = a1 + sign * min(totAng, pi_2) bezs.extend(createSmallArcBez(rad, a1, a2,rot)) totAng = totAng - abs(a2 - a1) a1 = a2 return bezs def bezs2XYList(arc1,transform = None): pnts=[] bezs=[] for aa in arc1: if aa.prev != None: bezs.append(XY(aa.prev)) bezs.append(XY(aa.fixed)) if aa.next!=None: bezs.append(XY(aa.next)) for i in range(len(bezs)): v = bezs[i] if transform: v = v + transform if i == 0: pnts.append(v) else: v2=pnts[-1] if (v2.x != v.x or v2.y != v.y): pnts.append(XY(v)) a=len(pnts) return pnts def XYList(lst, rot = 0.0, add = None): verts=[] for nn in range(len(lst)): v = lst[nn] if rot != 0.0: v = v.rotate(rot) if add: v = v + add verts.append([v.x,v.y]) return verts def XYListSt(lst, rot = 0.0, add = None): D2 = "" for nn in range(len(lst)): v = lst[nn] if rot != 0.0: v = v.rotate(rot) if add: v = v + add D2 += "%s%s " % (tern(nn==1,"C",""), v.st()) return D2 # circle by quadrants, A: 0>90, B: 90>180, C: 180>270, D: 270>360 def circQ(p,r,abcd="ABCD",inverse=0,xtra=None): aa = r * 0.551915024494 parts={ 'A':[XY(0,-r),XY(aa,-r), XY(r, -aa),XY(r,0)], 'B':[XY(r,0), XY(r, aa), XY(aa, r),XY(0,r)], 'C':[XY(0,r), XY(-aa,r), XY(-r, aa),XY(-r,0)], 'D':[XY(-r,0),XY(-r,-aa),XY(-aa,-r),XY(0,-r)]} #pA = parts[abcd[0]] pA = [XY(p)+N for N in parts[abcd[0]]] for aa in abcd[1:]: pA = pA + [XY(p)+N for N in parts[aa][1:]] if inverse==1: pA.reverse() listA = XYList(pA) if xtra: for n in xtra: listA[n].extend(xtra[n]) return listA def circleInCircle(c1,r1,c2,r2): d = c1.hipo(c2) return tern((r1 > (d + r2)),True,False) # 243