def effect(self): # Check we have enough objects selected count = len(self.selected) if count < 2: inkex.errormsg(_("Please select at least two objects.")) # The step in scale between each object xstep = (self.options.xfinish - self.options.xstart)/(count - 1) if self.options.ysame: ystep = xstep else: ystep = (self.options.yfinish - self.options.ystart)/(count - 1) # Starting scales xscale = self.options.xstart if self.options.ysame: yscale = xscale else: yscale = self.options.ystart # Sort selected objects by z order, lowest first id_list = self.selected.keys() id_list = pathmodifier.zSort(self.document.getroot(), id_list) # Scale each object for id in id_list: # No scaling actually happening if xscale == 1.0 and yscale == 1.0: xscale += xstep yscale += ystep continue # Get node and current transformations node = self.selected[id] transform = node.get('transform') # Scale the object as desired if transform: if xscale == yscale: transform += ' scale(%f)' % (xscale) else: transform += ' scale(%f, %f)' % (xscale, yscale) else: if xscale == yscale: transform = 'scale(%f)' % (xscale) else: transform = 'scale(%f, %f)' % (xscale, yscale) node.set('transform', transform) # Change the scale ready for the next object xscale += xstep yscale += ystep
def effect(self): # Check we have enough objects selected count = len(self.selected) if count < 2: inkex.errormsg(_("Please select at least two objects.")) # The step in scale between each object xstep = (self.options.xfinish - self.options.xstart) / (count - 1) if self.options.ysame: ystep = xstep else: ystep = (self.options.yfinish - self.options.ystart) / (count - 1) # Starting scales xscale = self.options.xstart if self.options.ysame: yscale = xscale else: yscale = self.options.ystart # Sort selected objects by z order, lowest first id_list = self.selected.keys() id_list = pathmodifier.zSort(self.document.getroot(), id_list) # Scale each object for id in id_list: # No scaling actually happening if xscale == 1.0 and yscale == 1.0: xscale += xstep yscale += ystep continue # Get node and current transformations node = self.selected[id] transform = node.get('transform') # Scale the object as desired if transform: if xscale == yscale: transform += ' scale(%f)' % (xscale) else: transform += ' scale(%f, %f)' % (xscale, yscale) else: if xscale == yscale: transform = 'scale(%f)' % (xscale) else: transform = 'scale(%f, %f)' % (xscale, yscale) node.set('transform', transform) # Change the scale ready for the next object xscale += xstep yscale += ystep
def prepareSelectionList(self): idList = self.options.ids idList = pathmodifier.zSort(self.document.getroot(), idList) id = idList[-1] self.patterns = {id: self.selected[id]} if self.options.duplicate: self.patterns = self.duplicateNodes(self.patterns) self.expandGroupsUnlinkClones(self.patterns, True, True) self.objectsToPaths(self.patterns) del self.selected[id] self.skeletons = self.selected self.expandGroupsUnlinkClones(self.skeletons, True, False) self.objectsToPaths(self.skeletons)
def prepareSelectionList(self): idList=self.options.ids idList=pathmodifier.zSort(self.document.getroot(),idList) id = idList[-1] self.patterns={id:self.selected[id]} if self.options.duplicate: self.patterns=self.duplicateNodes(self.patterns) self.expandGroupsUnlinkClones(self.patterns, True, True) self.objectsToPaths(self.patterns) del self.selected[id] self.skeletons=self.selected self.expandGroupsUnlinkClones(self.skeletons, True, False) self.objectsToPaths(self.skeletons)
def restack_z_order(self): parentnode = None objects = [] if len(self.selected) == 1: firstobject = self.selected[self.options.ids[0]] if firstobject.tag == inkex.addNS('g', 'svg'): parentnode = firstobject for child in parentnode.iterchildren(reversed=False): objects.append(child) else: parentnode = self.current_layer for id_ in zSort(self.document.getroot(), self.selected.keys()): objects.append(self.selected[id_]) if self.options.zsort == "rev": objects.reverse() elif self.options.zsort == "rand": random.shuffle(objects) if parentnode is not None: for item in objects: parentnode.append(item)
def alignAlongPath(self): ###convert object(line) to path? or determine wether object is path or not #This is used by object arrangement algorithm xNotY = True ascending = True if self.options.detect == 'left': xNotY = True ascending = True elif self.options.detect == 'right': xNotY = True ascending = False elif self.options.detect == 'top': xNotY = False ascending = True elif self.options.detect == 'bottom': xNotY = False ascending = False #Sorting selection to determine lowest (path) zOrderedList = pathmodifier.zSort(self.document.getroot(), self.options.ids) #Compute centers self.centers = self.computeCenters(zOrderedList[1:], self.options.placetext) if len(self.centers) > 0: #Ordering objects that should be placed on path self.orderObjects(xNotY, ascending) #Computing offsets from path's start point which are similar to distances between objects offsets = self.computeOffsets(self.options.usedistance, self.options.distance, self.options.distbetween) #Computing where objects should be placed orderedObjListPositions = self.computePointsXY( zOrderedList[0], offsets, self.options.offset) #Traslating and rotating objects if orderedObjListPositions: self.translateObjects(orderedObjListPositions, self.options.orient) else: inkex.debug("Nothing was changed.")
def getTotElements(self): self.tot_el = 0 self.collection = None if len( self.selected ) == 0: return False if len( self.selected ) > 1: # multiple selection if self.options.zsort: sorted_ids = zSort(self.document.getroot(),self.selected.keys()) else: sorted_ids = self.options.ids self.collection = list(sorted_ids) for i in sorted_ids: path = '//*[@id="%s"]' % i self.collection[self.tot_el] = self.document.xpath(path, namespaces=inkex.NSS)[0] self.tot_el += 1 else: # must be a group self.collection = self.selected[ self.options.ids[0] ] for i in self.collection: self.tot_el += 1
def effect(self): # get user-entered params x_scale = self.options.x_scale y_scale = self.options.y_scale t_start = self.options.t_start t_end = self.options.t_end n_steps = self.options.n_steps fps = self.options.fps dt = self.options.dt x_eqn = self.options.x_eqn y_eqn = self.options.y_eqn x_size_eqn = self.options.x_size_eqn y_size_eqn = self.options.y_size_eqn theta_eqn = self.options.theta_eqn # get doc root svg = self.document.getroot() doc_w = self.unittouu(svg.get('width')) doc_h = self.unittouu(svg.get('height')) # get selected items and validate selected = pathmodifier.zSort(self.document.getroot(), self.selected.keys()) if not selected: inkex.errormsg( 'Exactly two objects must be selected: a rect and a template. See "help" for details.' ) return elif len(selected) != 2: inkex.errormsg( 'Exactly two objects must be selected: a rect and a template. See "help" for details.' ) return # rect rect = self.selected[selected[0]] if not rect.tag.endswith('rect'): inkex.errormsg('Bottom object must be rect. See "help" for usage.') return # object obj = self.selected[selected[1]] if not (obj.tag.endswith('path') or obj.tag.endswith('g')): inkex.errormsg( 'Template object must be path or group of paths. See "help" for usage.' ) return if obj.tag.endswith('g'): children = obj.getchildren() if not all([ch.tag.endswith('path') for ch in children]): msg = 'All elements of group must be paths, but they are: ' msg += ', '.join(['{}'.format(ch) for ch in children]) inkex.errormsg(msg) return objs = children is_group = True else: objs = [obj] is_group = False # get rect params w = float(rect.get('width')) h = float(rect.get('height')) x_rect = float(rect.get('x')) y_rect = float(rect.get('y')) # lower left corner x_0 = x_rect y_0 = y_rect + h # get object path(s) obj_ps = [simplepath.parsePath(obj_.get('d')) for obj_ in objs] n_segs = [len(obj_p_) for obj_p_ in obj_ps] obj_p = sum(obj_ps, []) # compute travel parameters if not n_steps: # compute dt if dt == 0: dt = 1. / fps ts = np.arange(t_start, t_end, dt) else: ts = np.linspace(t_start, t_end, n_steps) # compute xs, ys, stretches, and rotations in arbitrary coordinates xs = np.nan * np.zeros(len(ts)) ys = np.nan * np.zeros(len(ts)) x_sizes = np.nan * np.zeros(len(ts)) y_sizes = np.nan * np.zeros(len(ts)) thetas = np.nan * np.zeros(len(ts)) for ctr, t in enumerate(ts): xs[ctr] = eval(x_eqn) ys[ctr] = eval(y_eqn) x_sizes[ctr] = eval(x_size_eqn) y_sizes[ctr] = eval(y_size_eqn) thetas[ctr] = eval(theta_eqn) * pi / 180 # ensure no Infs if np.any(np.isinf(xs)): raise Exception('Inf detected in x(t), please remove.') return if np.any(np.isinf(ys)): raise Exception('Inf detected in y(t), please remove.') return if np.any(np.isinf(x_sizes)): raise Exception('Inf detected in x_size(t), please remove.') return if np.any(np.isinf(y_sizes)): raise Exception('Inf detected in y_size(t), please remove.') return if np.any(np.isinf(thetas)): raise Exception('Inf detected in theta(t), please remove.') return # convert to screen coordinates xs *= (w / x_scale) xs += x_0 ys *= (-h / y_scale) # neg sign to invert y for inkscape screen ys += y_0 # get obj center b_box = simpletransform.refinedBBox( cubicsuperpath.CubicSuperPath(obj_p)) c_x = 0.5 * (b_box[0] + b_box[1]) c_y = 0.5 * (b_box[2] + b_box[3]) # get rotation anchor if any([k.endswith('transform-center-x') for k in obj.keys()]): k_r_x = [ k for k in obj.keys() if k.endswith('transform-center-x') ][0] k_r_y = [ k for k in obj.keys() if k.endswith('transform-center-y') ][0] r_x = c_x + float(obj.get(k_r_x)) r_y = c_y - float(obj.get(k_r_y)) else: r_x, r_y = c_x, c_y paths = [] # compute new paths for x, y, x_size, y_size, theta in zip(xs, ys, x_sizes, y_sizes, thetas): path = deepcopy(obj_p) # move to origin simplepath.translatePath(path, -x_0, -y_0) # move rotation anchor accordingly r_x_1 = r_x - x_0 r_y_1 = r_y - y_0 # scale simplepath.scalePath(path, x_size, y_size) # scale rotation anchor accordingly r_x_2 = r_x_1 * x_size r_y_2 = r_y_1 * y_size # move to final location simplepath.translatePath(path, x, y) # move rotation anchor accordingly r_x_3 = r_x_2 + x r_y_3 = r_y_2 + y # rotate simplepath.rotatePath(path, -theta, cx=r_x_3, cy=r_y_3) paths.append(path) parent = self.current_layer group = inkex.etree.SubElement(parent, inkex.addNS('g', 'svg'), {}) for path in paths: if is_group: group_ = inkex.etree.SubElement(group, inkex.addNS('g', 'svg'), {}) path_components = split(path, n_segs) for path_component, child in zip(path_components, children): attribs = {k: child.get(k) for k in child.keys()} attribs['d'] = simplepath.formatPath(path_component) child_copy = inkex.etree.SubElement( group_, child.tag, attribs) else: attribs = {k: obj.get(k) for k in obj.keys()} attribs['d'] = simplepath.formatPath(path) obj_copy = inkex.etree.SubElement(group, obj.tag, attribs)
def effect(self): exponent = self.options.exponent if exponent >= 0: exponent = 1.0 + exponent else: exponent = 1.0 / (1.0 - exponent) steps = [1.0 / (self.options.steps + 1.0)] for i in range(self.options.steps - 1): steps.append(steps[0] + steps[-1]) steps = [step**exponent for step in steps] paths = {} styles = {} if self.options.zsort: # work around selection order swapping with Live Preview sorted_ids = pathmodifier.zSort(self.document.getroot(), self.selected.keys()) else: # use selection order (default) sorted_ids = self.options.ids for id in sorted_ids: node = self.selected[id] if node.tag == inkex.addNS('path', 'svg'): paths[id] = cubicsuperpath.parsePath(node.get('d')) styles[id] = simplestyle.parseStyle(node.get('style')) trans = node.get('transform') if trans: simpletransform.applyTransformToPath( simpletransform.parseTransform(trans), paths[id]) else: sorted_ids.remove(id) for i in range(1, len(sorted_ids)): start = copy.deepcopy(paths[sorted_ids[i - 1]]) end = copy.deepcopy(paths[sorted_ids[i]]) sst = copy.deepcopy(styles[sorted_ids[i - 1]]) est = copy.deepcopy(styles[sorted_ids[i]]) basestyle = copy.deepcopy(sst) if basestyle.has_key('stroke-width'): basestyle['stroke-width'] = self.tweenstyleunit( 'stroke-width', sst, est, 0) #prepare for experimental style tweening if self.options.style: dostroke = True dofill = True styledefaults = { 'opacity': '1.0', 'stroke-opacity': '1.0', 'fill-opacity': '1.0', 'stroke-width': '1.0', 'stroke': 'none', 'fill': 'none' } for key in styledefaults.keys(): sst.setdefault(key, styledefaults[key]) est.setdefault(key, styledefaults[key]) isnotplain = lambda x: not (x == 'none' or x[:1] == '#') if isnotplain(sst['stroke']) or isnotplain( est['stroke']) or (sst['stroke'] == 'none' and est['stroke'] == 'none'): dostroke = False if isnotplain(sst['fill']) or isnotplain( est['fill']) or (sst['fill'] == 'none' and est['fill'] == 'none'): dofill = False if dostroke: if sst['stroke'] == 'none': sst['stroke-width'] = '0.0' sst['stroke-opacity'] = '0.0' sst['stroke'] = est['stroke'] elif est['stroke'] == 'none': est['stroke-width'] = '0.0' est['stroke-opacity'] = '0.0' est['stroke'] = sst['stroke'] if dofill: if sst['fill'] == 'none': sst['fill-opacity'] = '0.0' sst['fill'] = est['fill'] elif est['fill'] == 'none': est['fill-opacity'] = '0.0' est['fill'] = sst['fill'] if self.options.method == 2: #subdivide both paths into segments of relatively equal lengths slengths, stotal = csplength(start) elengths, etotal = csplength(end) lengths = {} t = 0 for sp in slengths: for l in sp: t += l / stotal lengths.setdefault(t, 0) lengths[t] += 1 t = 0 for sp in elengths: for l in sp: t += l / etotal lengths.setdefault(t, 0) lengths[t] += -1 sadd = [k for (k, v) in lengths.iteritems() if v < 0] sadd.sort() eadd = [k for (k, v) in lengths.iteritems() if v > 0] eadd.sort() t = 0 s = [[]] for sp in slengths: if not start[0]: s.append(start.pop(0)) s[-1].append(start[0].pop(0)) for l in sp: pt = t t += l / stotal if sadd and t > sadd[0]: while sadd and sadd[0] < t: nt = (sadd[0] - pt) / (t - pt) bezes = cspbezsplitatlength( s[-1][-1][:], start[0][0][:], nt) s[-1][-1:] = bezes[:2] start[0][0] = bezes[2] pt = sadd.pop(0) s[-1].append(start[0].pop(0)) t = 0 e = [[]] for sp in elengths: if not end[0]: e.append(end.pop(0)) e[-1].append(end[0].pop(0)) for l in sp: pt = t t += l / etotal if eadd and t > eadd[0]: while eadd and eadd[0] < t: nt = (eadd[0] - pt) / (t - pt) bezes = cspbezsplitatlength( e[-1][-1][:], end[0][0][:], nt) e[-1][-1:] = bezes[:2] end[0][0] = bezes[2] pt = eadd.pop(0) e[-1].append(end[0].pop(0)) start = s[:] end = e[:] else: #which path has fewer segments? lengthdiff = numsegs(start) - numsegs(end) #swap shortest first if lengthdiff > 0: start, end = end, start #subdivide the shorter path for x in range(abs(lengthdiff)): maxlen = 0 subpath = 0 segment = 0 for y in range(len(start)): for z in range(1, len(start[y])): leng = bezlenapprx(start[y][z - 1], start[y][z]) if leng > maxlen: maxlen = leng subpath = y segment = z sp1, sp2 = start[subpath][segment - 1:segment + 1] start[subpath][segment - 1:segment + 1] = cspbezsplit( sp1, sp2) #if swapped, swap them back if lengthdiff > 0: start, end = end, start #break paths so that corresponding subpaths have an equal number of segments s = [[]] e = [[]] while start and end: if start[0] and end[0]: s[-1].append(start[0].pop(0)) e[-1].append(end[0].pop(0)) elif end[0]: s.append(start.pop(0)) e[-1].append(end[0][0]) e.append([end[0].pop(0)]) elif start[0]: e.append(end.pop(0)) s[-1].append(start[0][0]) s.append([start[0].pop(0)]) else: s.append(start.pop(0)) e.append(end.pop(0)) if self.options.dup: steps = [0] + steps + [1] #create an interpolated path for each interval group = inkex.etree.SubElement(self.current_layer, inkex.addNS('g', 'svg')) for time in steps: interp = [] #process subpaths for ssp, esp in zip(s, e): if not (ssp or esp): break interp.append([]) #process superpoints for sp, ep in zip(ssp, esp): if not (sp or ep): break interp[-1].append([]) #process points for p1, p2 in zip(sp, ep): if not (sp or ep): break interp[-1][-1].append(interppoints(p1, p2, time)) #remove final subpath if empty. if not interp[-1]: del interp[-1] #basic style tweening if self.options.style: basestyle['opacity'] = tweenstylefloat( 'opacity', sst, est, time) if dostroke: basestyle['stroke-opacity'] = tweenstylefloat( 'stroke-opacity', sst, est, time) basestyle['stroke-width'] = self.tweenstyleunit( 'stroke-width', sst, est, time) basestyle['stroke'] = tweenstylecolor( 'stroke', sst, est, time) if dofill: basestyle['fill-opacity'] = tweenstylefloat( 'fill-opacity', sst, est, time) basestyle['fill'] = tweenstylecolor( 'fill', sst, est, time) attribs = { 'style': simplestyle.formatStyle(basestyle), 'd': cubicsuperpath.formatPath(interp) } new = inkex.etree.SubElement(group, inkex.addNS('path', 'svg'), attribs)
def effect(self): if len(self.options.ids) < 1 and len(self.options.ids) > 1: inkex.errormsg("This extension requires only one selected paths.") return #liste des chemins, preparation idList = self.options.ids idList = pathmodifier.zSort(self.document.getroot(), idList) id = idList[-1] idpoint = id + '-' + str(random.randint( 1, 99)) #id du paterns creer a partir du chemin selectionner idpointMark = id + '-' + str(random.randint(1, 99)) for id, node in self.selected.iteritems(): if node.tag == inkex.addNS('path', 'svg'): style = simplestyle.parseStyle( node.get('style')) #je recupere l'ancien style style['stroke'] = '#00ff00' #je modifie la valeur if self.options.autoMask == True: style['display'] = 'none' node.set('style', simplestyle.formatStyle(style)) #j'applique la modifi #gestion du skelete (le chemin selectionner) self.skeletons = self.selected self.expandGroupsUnlinkClones(self.skeletons, True, False) self.objectsToPaths(self.skeletons) for skelnode in self.skeletons.itervalues( ): #calcul de la longeur du chemin self.curSekeleton = cubicsuperpath.parsePath( skelnode.get('d')) for comp in self.curSekeleton: self.skelcomp, self.lengths = linearize(comp) longeur = sum(self.lengths) distance = self.unittouu(self.options.space) taille = self.unittouu(self.options.diamlong) MaxCopies = max(1, int(round((longeur + distance) / distance))) NbCopies = self.options.nrepeat #nombre de copie desirer a integrer dans les choix a modifier pour ne pas depasser les valeurs maxi if NbCopies > MaxCopies: NbCopies = MaxCopies #on limitte le nombre de copie au maxi possible sur le chemin if self.options.autoRepeat: #gestion du calcul auto NbCopies = MaxCopies if self.options.autoOffset: #gestion du decallage automatique tOffset = ((longeur - (NbCopies - 1) * distance) / 2) - taille / 2 else: tOffset = self.unittouu(self.options.toffset) #gestion du paterns labelpoint = 'Point: ' + idpoint + ' Nbr:' + str( NbCopies) + ' longueur:' + str( round(self.uutounit(longeur, 'mm'), 2)) + 'mm' addDot(self, idpoint, labelpoint, self.options.diamlong, self.options.typePoint, 0) #creation du cercle de base self.patterns = { idpoint: self.getElementById(idpoint) } #ajout du point dans le paterns de base bbox = simpletransform.computeBBox(self.patterns.values()) #liste des chemins, fin de preparation if distance < 0.01: exit( _("The total length of the pattern is too small :\nPlease choose a larger object or set 'Space between copies' > 0" )) for id, node in self.patterns.iteritems(): if node.tag == inkex.addNS('path', 'svg') or node.tag == 'path': d = node.get('d') p0 = cubicsuperpath.parsePath(d) newp = [] for skelnode in self.skeletons.itervalues(): self.curSekeleton = cubicsuperpath.parsePath( skelnode.get('d')) for comp in self.curSekeleton: p = copy.deepcopy(p0) self.skelcomp, self.lengths = linearize(comp) #!!!!>----> TODO: really test if path is closed! end point==start point is not enough! self.skelcompIsClosed = ( self.skelcomp[0] == self.skelcomp[-1]) xoffset = self.skelcomp[0][0] - bbox[ 0] + tOffset yoffset = self.skelcomp[0][1] - (bbox[2] + bbox[3]) / 2 if self.options.textInfos: addText(self, xoffset, yoffset, labelpoint) width = distance * NbCopies if not self.skelcompIsClosed: width -= distance new = [] for sub in p: #creation du nombre de patern for i in range(0, NbCopies, 1): new.append( copy.deepcopy(sub) ) #realise une copie de sub pour chaque nouveau element du patern offset(sub, distance, 0) p = new for sub in p: offset(sub, xoffset, yoffset) for sub in p: #une fois tous creer, on les mets en place for ctlpt in sub: #pose le patern sur le chemin self.applyDiffeo( ctlpt[1], (ctlpt[0], ctlpt[2])) newp += p node.set('d', cubicsuperpath.formatPath(newp)) else: inkex.errormsg( "This extension need a path, not groups.") if self.options.autoMark: if self.options.typeMark == "markFraction": Fraction = self.options.nrepeat2 #en mode fraction 1= au debut et a la fin, 2= un demi, 3= 1/3 etc distance = (width) / Fraction #distance inter point NbrMark = max(1, int(round((width + distance) / distance))) infos = " Marquage 1/" + str(Fraction) couleur = '#ff0000' else: Repeat = self.options.nrepeat2 #en mode fraction 1= au debut et a la fin, 2= un demi, 3= 1/3 etc NbrMark = max(1, int(round((NbCopies / Repeat)))) distance = distance * Repeat #distance inter point infos = " Marquage tous les " + str(Repeat) + " points" couleur = '#ffaa00' labelMark = "Mark: " + idpoint + infos addMark(self, 0, 0, idpointMark, labelMark, self.options.diamlong, couleur) self.patternsMark = { idpointMark: self.getElementById(idpointMark) } #ajout du point dans le paterns de base bbox = simpletransform.computeBBox(self.patternsMark.values()) #liste des chemins, fin de preparation if distance < 0.01: exit( _("The total length of the pattern is too small :\nPlease choose a larger object or set 'Space between copies' > 0" )) for id, node in self.patternsMark.iteritems(): if node.tag == inkex.addNS('path', 'svg') or node.tag == 'path': d = node.get('d') p0 = cubicsuperpath.parsePath(d) newp = [] for skelnode in self.skeletons.itervalues(): self.curSekeleton = cubicsuperpath.parsePath( skelnode.get('d')) for comp in self.curSekeleton: p = copy.deepcopy(p0) self.skelcomp, self.lengths = linearize(comp) #!!!!>----> TODO: really test if path is closed! end point==start point is not enough! self.skelcompIsClosed = ( self.skelcomp[0] == self.skelcomp[-1]) # a tester si les point au dessus sont utilisable pour positionner les autres a upoi ressemble skelcomp ?? xoffset = self.skelcomp[0][0] - bbox[ 0] + tOffset + taille / 2 yoffset = self.skelcomp[0][1] - (bbox[2] + bbox[3]) / 2 width = distance * NbrMark if not self.skelcompIsClosed: width -= distance new = [] for sub in p: #creation du nombre de patern for i in range(0, NbrMark, 1): new.append( copy.deepcopy(sub) ) #realise une copie de sub pour chaque nouveau element du patern offset(sub, distance, 0) p = new for sub in p: offset(sub, xoffset, yoffset) for sub in p: #une fois tous creer, on les mets en place for ctlpt in sub: #pose le patern sur le chemin self.applyDiffeo(ctlpt[1], (ctlpt[0], ctlpt[2])) newp += p node.set('d', cubicsuperpath.formatPath(newp)) else: inkex.errormsg("This extension need a path, not groups.")
def effect(self): if len(self.options.ids) < 1 and len(self.options.ids) > 1: inkex.errormsg( _("This extension requires only one selected paths.")) return #liste des chemins, preparation idList = self.options.ids idList = pathmodifier.zSort(self.document.getroot(), idList) id = idList[-1] idpoint = id + '-' + str(random.randint( 1, 99)) #id du paterns creer a partir du chemin selectionner for id, node in self.selected.iteritems(): style = simplestyle.parseStyle( node.get('style')) #je recupere l'ancien style style['stroke'] = '#00ff00' #je modifie la valeur node.set('style', simplestyle.formatStyle(style)) #j'applique la modifi #gestion du skelte (le chemin selectionner) self.skeletons = self.selected self.expandGroupsUnlinkClones(self.skeletons, True, False) self.objectsToPaths(self.skeletons) for skelnode in self.skeletons.itervalues( ): #calcul de la longeur du chemin self.curSekeleton = cubicsuperpath.parsePath(skelnode.get('d')) for comp in self.curSekeleton: self.skelcomp, self.lengths = linearize(comp) longeur = sum(self.lengths) distance = self.unittouu(self.options.space) taille = self.unittouu(self.options.diamlong) if self.options.autoRepeat: #gestion du calcul auto nbrRepeat = int(round((longeur) / distance)) else: nbrRepeat = self.options.nrepeat if self.options.autoOffset: #gestion du decallage automatique tOffset = ((longeur - (nbrRepeat - 1) * distance) / 2) - taille / 2 else: tOffset = self.unittouu(self.options.toffset) #gestion du paterns labelpoint = 'Point:' + self.options.diamlong + ' Ecart:' + self.options.space + ' Decallage:' + str( round(self.uutounit(tOffset, 'mm'), 2)) + 'mm' + ' Nbr:' + str(nbrRepeat) + ' longueur:' + str( round(self.uutounit(longeur, 'mm'), 2)) + 'mm' addDot(self, idpoint, labelpoint, self.options.diamlong, self.options.typePoint) #creation du cercle de base self.patterns = { idpoint: self.getElementById(idpoint) } #ajout du point dans le paterns de base bbox = simpletransform.computeBBox(self.patterns.values()) #liste des chemins, fin de preparation if distance < 0.01: exit( _("The total length of the pattern is too small :\nPlease choose a larger object or set 'Space between copies' > 0" )) for id, node in self.patterns.iteritems(): if node.tag == inkex.addNS('path', 'svg') or node.tag == 'path': d = node.get('d') p0 = cubicsuperpath.parsePath(d) newp = [] for skelnode in self.skeletons.itervalues(): self.curSekeleton = cubicsuperpath.parsePath( skelnode.get('d')) for comp in self.curSekeleton: p = copy.deepcopy(p0) self.skelcomp, self.lengths = linearize(comp) #!!!!>----> TODO: really test if path is closed! end point==start point is not enough! self.skelcompIsClosed = ( self.skelcomp[0] == self.skelcomp[-1]) length = sum(self.lengths) xoffset = self.skelcomp[0][0] - bbox[0] + tOffset yoffset = self.skelcomp[0][1] - (bbox[2] + bbox[3]) / 2 if self.options.textInfos: addText(self, xoffset, yoffset, labelpoint) MaxCopies = max( 1, int(round((length + distance) / distance))) NbCopies = nbrRepeat #nombre de copie desirer a intergrer dans les choix a modifier pour ne pas depasser les valeurs maxi if NbCopies > MaxCopies: NbCopies = MaxCopies #on limitte le nombre de copie au maxi possible sur le chemin width = distance * NbCopies if not self.skelcompIsClosed: width -= distance new = [] for sub in p: #creation du nombre de patern for i in range(0, NbCopies, 1): new.append( copy.deepcopy(sub) ) #realise une copie de sub pour chaque nouveau element du patern offset(sub, distance, 0) p = new for sub in p: offset(sub, xoffset, yoffset) for sub in p: #une fois tous creer, on les mets en place for ctlpt in sub: #pose le patern sur le chemin self.applyDiffeo(ctlpt[1], (ctlpt[0], ctlpt[2])) newp += p node.set('d', cubicsuperpath.formatPath(newp))
def effect(self): exponent = self.options.exponent if exponent>= 0: exponent = 1.0 + exponent else: exponent = 1.0/(1.0 - exponent) steps = [1.0/(self.options.steps + 1.0)] for i in range(self.options.steps - 1): steps.append(steps[0] + steps[-1]) steps = [step**exponent for step in steps] paths = {} styles = {} if self.options.zsort: # work around selection order swapping with Live Preview sorted_ids = pathmodifier.zSort(self.document.getroot(),self.selected.keys()) else: # use selection order (default) sorted_ids = self.options.ids for id in sorted_ids: node = self.selected[id] if node.tag ==inkex.addNS('path','svg'): paths[id] = cubicsuperpath.parsePath(node.get('d')) styles[id] = simplestyle.parseStyle(node.get('style')) trans = node.get('transform') if trans: simpletransform.applyTransformToPath(simpletransform.parseTransform(trans), paths[id]) else: sorted_ids.remove(id) for i in range(1,len(sorted_ids)): start = copy.deepcopy(paths[sorted_ids[i-1]]) end = copy.deepcopy(paths[sorted_ids[i]]) sst = copy.deepcopy(styles[sorted_ids[i-1]]) est = copy.deepcopy(styles[sorted_ids[i]]) basestyle = copy.deepcopy(sst) if basestyle.has_key('stroke-width'): basestyle['stroke-width'] = self.tweenstyleunit('stroke-width',sst,est,0) #prepare for experimental style tweening if self.options.style: dostroke = True dofill = True styledefaults = {'opacity':'1.0', 'stroke-opacity':'1.0', 'fill-opacity':'1.0', 'stroke-width':'1.0', 'stroke':'none', 'fill':'none'} for key in styledefaults.keys(): sst.setdefault(key,styledefaults[key]) est.setdefault(key,styledefaults[key]) isnotplain = lambda x: not (x=='none' or x[:1]=='#') if isnotplain(sst['stroke']) or isnotplain(est['stroke']) or (sst['stroke']=='none' and est['stroke']=='none'): dostroke = False if isnotplain(sst['fill']) or isnotplain(est['fill']) or (sst['fill']=='none' and est['fill']=='none'): dofill = False if dostroke: if sst['stroke']=='none': sst['stroke-width'] = '0.0' sst['stroke-opacity'] = '0.0' sst['stroke'] = est['stroke'] elif est['stroke']=='none': est['stroke-width'] = '0.0' est['stroke-opacity'] = '0.0' est['stroke'] = sst['stroke'] if dofill: if sst['fill']=='none': sst['fill-opacity'] = '0.0' sst['fill'] = est['fill'] elif est['fill']=='none': est['fill-opacity'] = '0.0' est['fill'] = sst['fill'] if self.options.method == 2: #subdivide both paths into segments of relatively equal lengths slengths, stotal = csplength(start) elengths, etotal = csplength(end) lengths = {} t = 0 for sp in slengths: for l in sp: t += l / stotal lengths.setdefault(t,0) lengths[t] += 1 t = 0 for sp in elengths: for l in sp: t += l / etotal lengths.setdefault(t,0) lengths[t] += -1 sadd = [k for (k,v) in lengths.iteritems() if v < 0] sadd.sort() eadd = [k for (k,v) in lengths.iteritems() if v > 0] eadd.sort() t = 0 s = [[]] for sp in slengths: if not start[0]: s.append(start.pop(0)) s[-1].append(start[0].pop(0)) for l in sp: pt = t t += l / stotal if sadd and t > sadd[0]: while sadd and sadd[0] < t: nt = (sadd[0] - pt) / (t - pt) bezes = cspbezsplitatlength(s[-1][-1][:],start[0][0][:], nt) s[-1][-1:] = bezes[:2] start[0][0] = bezes[2] pt = sadd.pop(0) s[-1].append(start[0].pop(0)) t = 0 e = [[]] for sp in elengths: if not end[0]: e.append(end.pop(0)) e[-1].append(end[0].pop(0)) for l in sp: pt = t t += l / etotal if eadd and t > eadd[0]: while eadd and eadd[0] < t: nt = (eadd[0] - pt) / (t - pt) bezes = cspbezsplitatlength(e[-1][-1][:],end[0][0][:], nt) e[-1][-1:] = bezes[:2] end[0][0] = bezes[2] pt = eadd.pop(0) e[-1].append(end[0].pop(0)) start = s[:] end = e[:] else: #which path has fewer segments? lengthdiff = numsegs(start) - numsegs(end) #swap shortest first if lengthdiff > 0: start, end = end, start #subdivide the shorter path for x in range(abs(lengthdiff)): maxlen = 0 subpath = 0 segment = 0 for y in range(len(start)): for z in range(1, len(start[y])): leng = bezlenapprx(start[y][z-1], start[y][z]) if leng > maxlen: maxlen = leng subpath = y segment = z sp1, sp2 = start[subpath][segment - 1:segment + 1] start[subpath][segment - 1:segment + 1] = cspbezsplit(sp1, sp2) #if swapped, swap them back if lengthdiff > 0: start, end = end, start #break paths so that corresponding subpaths have an equal number of segments s = [[]] e = [[]] while start and end: if start[0] and end[0]: s[-1].append(start[0].pop(0)) e[-1].append(end[0].pop(0)) elif end[0]: s.append(start.pop(0)) e[-1].append(end[0][0]) e.append([end[0].pop(0)]) elif start[0]: e.append(end.pop(0)) s[-1].append(start[0][0]) s.append([start[0].pop(0)]) else: s.append(start.pop(0)) e.append(end.pop(0)) if self.options.dup: steps = [0] + steps + [1] #create an interpolated path for each interval group = inkex.etree.SubElement(self.current_layer,inkex.addNS('g','svg')) for time in steps: interp = [] #process subpaths for ssp,esp in zip(s, e): if not (ssp or esp): break interp.append([]) #process superpoints for sp,ep in zip(ssp, esp): if not (sp or ep): break interp[-1].append([]) #process points for p1,p2 in zip(sp, ep): if not (sp or ep): break interp[-1][-1].append(interppoints(p1, p2, time)) #remove final subpath if empty. if not interp[-1]: del interp[-1] #basic style tweening if self.options.style: basestyle['opacity'] = tweenstylefloat('opacity',sst,est,time) if dostroke: basestyle['stroke-opacity'] = tweenstylefloat('stroke-opacity',sst,est,time) basestyle['stroke-width'] = self.tweenstyleunit('stroke-width',sst,est,time) basestyle['stroke'] = tweenstylecolor('stroke',sst,est,time) if dofill: basestyle['fill-opacity'] = tweenstylefloat('fill-opacity',sst,est,time) basestyle['fill'] = tweenstylecolor('fill',sst,est,time) attribs = {'style':simplestyle.formatStyle(basestyle),'d':cubicsuperpath.formatPath(interp)} new = inkex.etree.SubElement(group,inkex.addNS('path','svg'), attribs)