#!/usr/bin/env python # Copyright 2014 Jo Pol # 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 3 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, see http://www.gnu.org/licenses/. ######################################################################### import inkex, cubicsuperpath, bezmisc __author__ = 'Jo Pol' __credits__ = ['Jo Pol','Mark Shafer'] __license__ = 'GPLv3' class ThreadStyle(inkex.Effect): """ Apply stroke style of selected path to connected paths """ def __init__(self): """ Constructor. """ # Call the base class constructor. inkex.Effect.__init__(self) self.OptionParser.add_option('-t', '--tolerance', action='store', type='float', dest='tolerance', default=0.05, help='tolerance (max. distance between segments)') self.OptionParser.add_option('-u', '--units', action = 'store', type = 'string', dest = 'units', default = 'mm', help = 'The units the measurements are in') self.OptionParser.add_option('-w', '--width', action='store', type='float', dest='width', default='1', help='thread width') self.OptionParser.add_option('-c', '--color', action='store', type='string', dest='color', default='#FF9999', help='thread color') def getUnittouu(self, param): " compatibility between inkscape 0.48 and 0.91 " try: return inkex.unittouu(param) except AttributeError: return self.unittouu(param) def startPoint(self, cubicSuperPath): """ returns the first point of a CubicSuperPath """ return cubicSuperPath[0][0][0] def endPoint(self, csp): """ returns the last point of a CubicSuperPath """ return csp[0][len(csp[0]) - 1][len(csp[0][1]) - 1] def isBezier(self, item): return item.tag == inkex.addNS('path', 'svg') and item.get(inkex.addNS('connector-curvature', 'inkscape')) def findCandidatesForStyleChange(self, skip): """ collect the document items that are a Bezier curve """ self.candidates = [] for item in self.document.getiterator(): if self.isBezier(item) and item != skip: csp = cubicsuperpath.parsePath(item.get('d')) s = self.startPoint(csp) e = self.endPoint(csp) self.candidates.append({'s':s, 'e':e, 'i':item}) def applyStyle(self, item): """ Change the style of the item and remove it form the candidates """ item['i'].attrib['style'] = self.style self.candidates.remove(item) def applyToAdjacent(self, point): while point != None: p = (point[0], point[1]) next = None for item in self.candidates: if bezmisc.pointdistance(p, (item['s'][0], item['s'][1])) < self.options.tolerance: self.applyStyle(item) next = item['e'] break elif bezmisc.pointdistance(p, (item['e'][0], item['e'][1])) < self.options.tolerance: self.applyStyle(item) next = item['s'] break point = next def getColorString(self, longColor, verbose=False): """ Convert the long into a #RRGGBB color value - verbose=true pops up value for us in defaults conversion back is A + B*256^1 + G*256^2 + R*256^3 """ if verbose: inkex.debug("%s ="%(longColor)) longColor = long(longColor) if longColor <0: longColor = long(longColor) & 0xFFFFFFFF hexColor = hex(longColor)[2:-3] hexColor = '#' + hexColor.rjust(6, '0').upper() if verbose: inkex.debug(" %s for color default value"%(hexColor)) return hexColor def effect(self): """ Effect behaviour. Overrides base class' method and draws something. """ self.options.color = self.getColorString(self.options.color) conversion = self.getUnittouu("1" + self.options.units) if len(self.selected) != 1: inkex.debug('no object selected, or more than one selected') return selected = self.selected.values()[0] if not self.isBezier(selected): inkex.debug('selected element is not a Bezier curve') return self.findCandidatesForStyleChange(selected) self.style = 'fill:none;stroke:{1};stroke-width:{0}'.format(self.options.width*conversion, self.options.color) csp = cubicsuperpath.parsePath(selected.get('d')) self.selected.values()[0].attrib['style'] = self.style self.applyToAdjacent(self.startPoint(csp)) self.applyToAdjacent(self.endPoint(csp)) # Create effect instance and apply it. if __name__ == '__main__': ThreadStyle().affect()