#!/usr/bin/env python 
'''
Copyright (C) 2007 Terry Brown, terry_n_brown@yahoo.com

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
'''
import inkex, simplepath, sys, copy
from math import degrees, atan2

class Edge3d(inkex.Effect):
    def __init__(self):
        inkex.Effect.__init__(self)
        opts = [('-a', '--angle', 'float', 'angle', 45.0,
                 'angle of illumination, clockwise, 45 = upper right'),
                ('-d', '--stddev', 'float', 'stddev', 5.0,
                 'stdDeviation for Gaussian Blur'),
                ('-H', '--blurheight', 'float', 'blurheight', 2.0,
                 'height for Gaussian Blur'),
                ('-W', '--blurwidth', 'float', 'blurwidth', 2.0,
                 'width for Gaussian Blur'),
                ('-s', '--shades', 'int', 'shades', 2,
                 'shades, 2 = black and white, 3 = black, grey, white, etc.'),
                ('-b', '--bw', 'inkbool', 'bw', False,
                 'black and white, create only the fully black and white wedges'),
                ('-p', '--thick', 'float', 'thick', 10.,
                 'stroke-width for path pieces'),
                ]
        for o in opts:
            self.OptionParser.add_option(o[0], o[1], action="store", type=o[2],
                                         dest=o[3], default=o[4], help=o[5])
        self.filtId = ''

    def angleBetween(self, start, end, angle):
        """Return true if angle (degrees, clockwise, 0 = up/north) is between
           angles start and end"""
        def f(x):
            """Maybe add 360 to x"""
            if x < 0: return x + 360.
            return x
        # rotate all inputs by start, => start = 0
        a = f(f(angle) - f(start))
        e = f(f(end) - f(start))
        return a < e

    def effectX(self):
        # size of a wedge for shade i, wedges come in pairs
        delta = 360. / self.options.shades / 2.

        for shade in range(0, self.options.shades):
            if self.options.bw and shade > 0 and shade < self.options.shades-1:
                continue
            self.start = [self.options.angle - delta * (shade+1)]
            self.end = [self.options.angle - delta * (shade)]
            self.start.append( self.options.angle + delta * (shade) )
            self.end.append( self.options.angle + delta * (shade+1) )
            self.makeShade(float(shade)/float(self.options.shades-1))

    def effect(self):
        """Check each internode to see if it's in one of the wedges
           for the current shade.  shade is a floating point 0-1 white-black"""
        # size of a wedge for shade i, wedges come in pairs
        delta = 360. / self.options.shades / 2.
        for id, node in self.selected.iteritems():
            if node.tag == inkex.addNS('path','svg'):
                d = node.get('d')
                p = simplepath.parsePath(d)
                g = None
                for shade in range(0, self.options.shades):
                    if (self.options.bw and shade > 0 and
                        shade < self.options.shades - 1):
                        continue
                    self.start = [self.options.angle - delta * (shade+1)]
                    self.end = [self.options.angle - delta * (shade)]
                    self.start.append( self.options.angle + delta * (shade) )
                    self.end.append( self.options.angle + delta * (shade+1) )
                    level=float(shade)/float(self.options.shades-1)
                    last = []
                    result = []
                    for cmd,params in p:
                        if cmd == 'Z':
                            last = []
                            continue
                        if last:
                            a = degrees(atan2(params[-2:][0] - last[0],
                                              params[-2:][1] - last[1]))
                            if (self.angleBetween(self.start[0], self.end[0], a) or
                                self.angleBetween(self.start[1], self.end[1], a)):
                                result.append(('M', last))
                                result.append((cmd, params))
                        last = params[-2:]
                    if result:
                        if not g:
                            g = self.getGroup(node)
                        nn = copy.deepcopy(node)
                        nn.set('d',simplepath.formatPath(result))
                        
                        col = 255 - int(255. * level)
                        a = 'fill:none;stroke:#%02x%02x%02x;stroke-opacity:1;stroke-width:10;%s' % ((col,)*3 + (self.filtId,))
                        nn.set('style',a)
                        g.append(nn)
        
    def getGroup(self, node):
        defs = self.document.getroot().xpath('//svg:defs',inkex.NSS)
        if defs:
            defs = defs[0]
            # make a clipped group, clip with clone of original, clipped group
            # include original and group of paths
            clip = inkex.etree.SubElement(defs,inkex.addNS('clipPath','svg'))
            clip.append(copy.deepcopy(node))
            clipId = self.uniqueId('clipPath')
            clip.set('id', clipId)
            clipG = inkex.etree.SubElement(node.getparent(),inkex.addNS('g','svg'))
            g = inkex.etree.SubElement(clipG,inkex.addNS('g','svg'))
            clipG.set('clip-path', 'url(#'+clipId+')')
            # make a blur filter reference by the style of each path
            filt = inkex.etree.SubElement(defs,inkex.addNS('filter','svg'))
            filtId = self.uniqueId('filter')
            self.filtId = 'filter:url(#%s);' % filtId
            for k, v in [('id', filtId), ('height', str(self.options.blurheight)),
                         ('width', str(self.options.blurwidth)),
                         ('x', '-0.5'), ('y', '-0.5')]:
                filt.set(k, v)
            fe = inkex.etree.SubElement(filt,inkex.addNS('feGaussianBlur','svg'))
            fe.set('stdDeviation', str(self.options.stddev))
        else:
            # can't find defs, just group paths
            g = inkex.etree.SubElement(node.getparent(),inkex.addNS('g','svg'))
            g.append(node)

        return g

e = Edge3d()
e.affect()
