def align_to_node(self, ref_node, alignment, relative_scale): """ Aligns the node represented by self to a reference node according to the settings defined by the user :param ref_node: Reference node subclassed from SvgElement to which self is going to be aligned :param alignment: A 2-element string list defining the alignment :param relative_scale: Scaling of the new node relative to the scale of the reference node """ scale_transform = st.parseTransform("scale(%f)" % relative_scale) old_transform = ref_node.get_attrib('transform') composition = st.parseTransform(old_transform, scale_transform) # Account for vertical flipping of pstoedit nodes when recompiled via pdf2svg and vice versa composition = self._check_and_fix_transform(ref_node, composition) # keep alignment point of drawing intact, calculate required shift self.set_attrib('transform', st.formatTransform(composition)) x, y, w, h = ref_node.get_frame() new_x, new_y, new_w, new_h = self.get_frame() p_old = self._get_pos(x, y, w, h, alignment) p_new = self._get_pos(new_x, new_y, new_w, new_h, alignment) dx = p_old[0] - p_new[0] dy = p_old[1] - p_new[1] composition[0][2] += dx composition[1][2] += dy self.set_attrib('transform', st.formatTransform(composition)) self.set_attrib("jacobian_sqrt", str(self.get_jacobian_sqrt()), TEXTEXT_NS)
def check_text_on_path(svg, element, scale_x, scale_y): """Check whether to skip scaling a text put on a path.""" skip = False path = get_linked(svg, element.find(inkex.addNS('textPath', 'svg'))) if not is_in_defs(svg, path): if is_sibling(element, path): # skip common element scaling if both text and path are siblings skip = True # scale offset if 'transform' in element.attrib: mat = simpletransform.parseTransform(element.get('transform')) mat[0][2] *= scale_x mat[1][2] *= scale_y element.set('transform', simpletransform.formatTransform(mat)) # scale font size mat = simpletransform.parseTransform( 'scale({},{})'.format(scale_x, scale_y)) det = abs(mat[0][0]*mat[1][1] - mat[0][1]*mat[1][0]) descrim = math.sqrt(abs(det)) prop = 'font-size' # outer text sdict = simplestyle.parseStyle(element.get('style')) if prop in sdict: sdict[prop] = float(sdict[prop]) * descrim element.set('style', simplestyle.formatStyle(sdict)) # inner tspans for child in element.iterdescendants(): if child.tag == inkex.addNS('tspan', 'svg'): sdict = simplestyle.parseStyle(child.get('style')) if prop in sdict: sdict[prop] = float(sdict[prop]) * descrim child.set('style', simplestyle.formatStyle(sdict)) return skip
def _merge_transform(self, node, transform): """Propagate style and transform to remove inheritance Originally from https://github.com/nikitakit/svg2sif/blob/master/synfig_prepare.py#L370 """ # Compose the transformations if node.tag == addNS("svg", "svg") and node.get("viewBox"): vx, vy, vw, vh = [self._get_dimension(x) for x in node.get("viewBox").split()] dw = self._get_dimension(node.get("width", vw)) dh = self._get_dimension(node.get("height", vh)) t = ("translate(%f, %f) scale(%f, %f)" % (-vx, -vy, dw / vw, dh / vh)) this_transform = simpletransform.parseTransform( t, transform) this_transform = simpletransform.parseTransform( node.get("transform"), this_transform) del node.attrib["viewBox"] else: this_transform = simpletransform.parseTransform(node.get( "transform"), transform) # Set the node's transform attrib node.set("transform", simpletransform.formatTransform(this_transform))
def fixUses(self, node, index): ''' Fix <use> objects after transformation the objects their refer to ''' # Go deep first for i in node: self.fixUses(i, index) # Is it a <use> tag? if node.tag == inkex.addNS("use", "svg"): # Get reference href = node.get(inkex.addNS('href', 'xlink')) if href[0] == "#": # Did we apply any transforms to the referred node? if href[1:] in index: # Yes! thatTr = index[href[1:]] thisTr = SpeleoTransform.getTransform(node) invThatTr = SpeleoTransform.invertTransform(thatTr) # So, first transform of this <use> should be *reverting* that transform! node.set( "transform", simpletransform.formatTransform( simpletransform.composeTransform( thisTr, invThatTr)))
def _merge_transform(self, node, transform): """Propagate style and transform to remove inheritance Originally from https://github.com/nikitakit/svg2sif/blob/master/synfig_prepare.py#L370 """ # Compose the transformations if node.tag == addNS("svg", "svg") and node.get("viewBox"): vx, vy, vw, vh = [ self._get_dimension(x) for x in node.get("viewBox").split() ] dw = self._get_dimension(node.get("width", vw)) dh = self._get_dimension(node.get("height", vh)) t = ("translate(%f, %f) scale(%f, %f)" % (-vx, -vy, dw / vw, dh / vh)) this_transform = simpletransform.parseTransform(t, transform) this_transform = simpletransform.parseTransform( node.get("transform"), this_transform) del node.attrib["viewBox"] else: this_transform = simpletransform.parseTransform( node.get("transform"), transform) # Set the node's transform attrib node.set("transform", simpletransform.formatTransform(this_transform))
def _merge_clippath(self, node, clippathurl): if (clippathurl): node_transform = simpletransform.parseTransform( node.get("transform")) if (node_transform): # Clip-paths on nodes with a transform have the transform # applied to the clipPath as well, which we don't want. So, we # create new clipPath element with references to all existing # clippath subelements, but with the inverse transform applied inverse_node_transform = simpletransform.formatTransform( self._invert_transform(node_transform)) new_clippath = inkex.etree.SubElement( self.xpathSingle('//svg:defs'), 'clipPath', { 'clipPathUnits': 'userSpaceOnUse', 'id': self.uniqueId("clipPath") }) clippath = self.getElementById(clippathurl[5:-1]) for c in (clippath.iterchildren()): inkex.etree.SubElement( new_clippath, 'use', { inkex.addNS('href', 'xlink'): '#' + c.get("id"), 'transform': inverse_node_transform, 'id': self.uniqueId("use") }) # Set the clippathurl to be the one with the inverse transform clippathurl = "url(#" + new_clippath.get("id") + ")" # Reference the parent clip-path to keep clipping intersection # Find end of clip-path chain and add reference there node_clippathurl = node.get("clip-path") while (node_clippathurl): node = self.getElementById(node_clippathurl[5:-1]) node_clippathurl = node.get("clip-path") node.set("clip-path", clippathurl)
def _merge_clippath(self, node, clippathurl): if (clippathurl): node_transform = simpletransform.parseTransform( node.get("transform")) if (node_transform): # Clip-paths on nodes with a transform have the transform # applied to the clipPath as well, which we don't want. So, we # create new clipPath element with references to all existing # clippath subelements, but with the inverse transform applied inverse_node_transform = simpletransform.formatTransform( self._invert_transform(node_transform)) new_clippath = inkex.etree.SubElement( self.xpathSingle('//svg:defs'), 'clipPath', {'clipPathUnits': 'userSpaceOnUse', 'id': self.uniqueId("clipPath")}) clippath = self.getElementById(clippathurl[5:-1]) for c in (clippath.iterchildren()): inkex.etree.SubElement( new_clippath, 'use', {inkex.addNS('href', 'xlink'): '#' + c.get("id"), 'transform': inverse_node_transform, 'id': self.uniqueId("use")}) # Set the clippathurl to be the one with the inverse transform clippathurl = "url(#" + new_clippath.get("id") + ")" # Reference the parent clip-path to keep clipping intersection # Find end of clip-path chain and add reference there node_clippathurl = node.get("clip-path") while (node_clippathurl): node = self.getElementById(node_clippathurl[5:-1]) node_clippathurl = node.get("clip-path") node.set("clip-path", clippathurl)
def get_correction_transform(svg): transform = get_viewbox_transform(svg) # we need to correct for the viewbox transform = simpletransform.invertTransform(transform) transform = simpletransform.formatTransform(transform) return transform
def recursiveFuseTransform(self, node, transf=[[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]]): transf = composeTransform(transf, parseTransform(node.get("transform", None))) if 'transform' in node.attrib: del node.attrib['transform'] if 'style' in node.attrib: style = node.attrib.get('style') style = simplestyle.parseStyle(style) update = False if 'stroke-width' in style: try: stroke_width = self.unittouu(style.get('stroke-width').strip()) # pixelsnap ext assumes scaling is similar in x and y # and uses the x scale... # let's try to be a bit smarter stroke_width *= math.sqrt(transf[0][0]**2 + transf[1][1]**2) style['stroke-width'] = str(stroke_width) update = True except AttributeError: pass if update: style = simplestyle.formatStyle(style) node.attrib['style'] = style node = ApplyTransform.objectToPath(node) if 'd' in node.attrib: d = node.get('d') p = cubicsuperpath.parsePath(d) applyTransformToPath(transf, p) node.set('d', cubicsuperpath.formatPath(p)) elif node.tag in [inkex.addNS('polygon', 'svg'), inkex.addNS('polyline', 'svg')]: points = node.get('points') points = points.strip().split(' ') for k,p in enumerate(points): if ',' in p: p = p.split(',') p = [float(p[0]),float(p[1])] applyTransformToPoint(transf, p) p = [str(p[0]),str(p[1])] p = ','.join(p) points[k] = p points = ' '.join(points) node.set('points', points) elif node.tag in [inkex.addNS('rect', 'svg'), inkex.addNS('text', 'svg'), inkex.addNS('image', 'svg'), inkex.addNS('use', 'svg')]: node.set('transform', formatTransform(transf)) for child in node.getchildren(): self.recursiveFuseTransform(child, transf)
def effect(self): layer = self.get_current_layer() affine = inverse(i2d_affine(self, layer)) transform = simpletransform.formatTransform(affine) + \ ' translate' + str(self.view_center) g = Scalebar(self.options.scale, self.options.dpi, self.options.text, self.uutounit(1, 'px')).get_tree() g.set('transform', transform) layer.append(g)
def copy_interior(self, basename,status, newbasename,newstatus, dx): current = self.getElementById(basename+'-'+status) if current != None: new = copy.deepcopy(current) new.set('id', newbasename+'-'+newstatus) if new.attrib.has_key('transform'): mat = simpletransform.parseTransform(new.get('transform')) mat[0][2] = mat[0][2]+dx else: mat = [[1,0,dx],[0,1,0]] new.set('transform', simpletransform.formatTransform(mat)) current.getparent().append(new)
def recursiveFuseTransform(self, node, transf=[[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]]): transf = composeTransform(transf, parseTransform(node.get("transform", None))) if 'transform' in node.attrib: del node.attrib['transform'] node = self.objectToPath(node) if 'd' in node.attrib: d = node.get('d') p = cubicsuperpath.parsePath(d) applyTransformToPath(transf, p) node.set('d', cubicsuperpath.formatPath(p)) self.scaleStrokeWidth(node, transf) elif node.tag in [ inkex.addNS('polygon', 'svg'), inkex.addNS('polyline', 'svg') ]: points = node.get('points') points = points.strip().split(' ') for k, p in enumerate(points): if ',' in p: p = p.split(',') p = [float(p[0]), float(p[1])] applyTransformToPoint(transf, p) p = [str(p[0]), str(p[1])] p = ','.join(p) points[k] = p points = ' '.join(points) node.set('points', points) self.scaleStrokeWidth(node, transf) elif node.tag in [ inkex.addNS('rect', 'svg'), inkex.addNS('text', 'svg'), inkex.addNS('image', 'svg'), inkex.addNS('use', 'svg'), inkex.addNS('circle', 'svg') ]: node.set('transform', formatTransform(transf)) else: # e.g. <g style="..."> self.scaleStrokeWidth(node, transf) for child in node.getchildren(): self.recursiveFuseTransform(child, transf)
def get_correction_transform(self, node): # if we want to place our new nodes in the same group as this node, # then we'll need to factor in the effects of any transforms set on # the parents of this node. # we can ignore the transform on the node itself since it won't apply # to the objects we add transform = get_node_transform(node.getparent()) # now invert it, so that we can position our objects in absolute # coordinates transform = simpletransform.invertTransform(transform) return simpletransform.formatTransform(transform)
def check_use(svg, element, scale_x, scale_y): """Check whether to skip scaling an instanciated element (<use>).""" skip = False path = get_linked(svg, element) if not is_in_defs(svg, path): if is_sibling(element, path): skip = True # scale offset if 'transform' in element.attrib: mat = simpletransform.parseTransform(element.get('transform')) mat[0][2] *= scale_x mat[1][2] *= scale_y element.set('transform', simpletransform.formatTransform(mat)) return skip
def translateElement(self, node, x, y, relative=False): # Grab transform attribute if it exists. transform = node.get("transform", "") # Compute the nodes bounding box box = list(simpletransform.computeBBox([node])) pos_x = box[0] pos_y = box[2] # rotation center is not a breeze to calculate from the matrix, so thanks inkscape ;) origin_x = float(node.get(inkex.addNS("transform-center-x", "inkscape"), 0)) origin_y = float(node.get(inkex.addNS("transform-center-y", "inkscape"), 0)) origin_x = origin_x + (box[1] / 2) origin_y = (origin_y * -1) + (box[3] / 2) if transform == "": # If there is no transform attribute on the node we add one node.attrib["transform"] = "" # simpletransform returns a multi dim array of matrix values transform = simpletransform.parseTransform(transform) transformObject = self.normalizeMatrix(transform) inkex.debug(transformObject) # offset_x = (transform[0][2]-pos_x) # offset_y = (transform[1][2]-pos_y) offset_x = pos_x * -1 offset_y = pos_y * -1 inkex.debug([offset_x, offset_y]) transform = simpletransform.parseTransform( ("translate(" + str(offset_x) + " " + str(offset_y) + ")"), transform ) transformObject = self.normalizeMatrix(transform) inkex.debug(transformObject) inkex.debug(transform) if relative == False: matrix = simpletransform.formatTransform(transform) node.set("transform", matrix) inkex.debug(matrix) else: simpletransform.applyTransformToNode(transform, node)
def effect(self): """ Apply the transformation. If an element already has a transformation attribute, it will be combined with the transformation matrix for the requested conversion. """ if self.options.reverse == "true": conversion = "from_" + self.options.conversion else: conversion = "to_" + self.options.conversion if len(self.selected) == 0: inkex.errormsg( _("Please select an object to perform the " + "isometric projection transformation on.")) return # Default to the flat 2D to isometric top down view conversion if an # invalid identifier is passed. effect_matrix = self.transformations.get( conversion, self.transformations.get('to_top')) for id, node in self.selected.items(): bbox = computeBBox([node]) midpoint = self.getMidPoint(bbox, node) center_old = self.getTransformCenter(midpoint, node) transform = node.get("transform") # Combine our transformation matrix with any pre-existing # transform. matrix = parseTransform(transform, effect_matrix) # Compute the location of the transformation center after applying # the transformation matrix. center_new = center_old[:] applyTransformToPoint(matrix, center_new) applyTransformToPoint(matrix, midpoint) # Add a translation transformation that will move the object to # keep its transformation center in the same place. self.translateBetweenPoints(matrix, center_new, center_old) node.set('transform', formatTransform(matrix)) # Adjust the transformation center. self.moveTransformationCenter(node, midpoint, center_new)
def scanTree(self, node, sel, group, correctiveTransform): ''' Recursively look for <use>s referring to symbol sel Adds all these symbols to group 'group', applying transforms accordingly ''' # Avoid too much recursion if node == group: return # Do not go into invisible layers if self.isLayer(node): if self.hasDisplayNone(node): return # Recurse first for i in node: self.scanTree(i, sel, group, correctiveTransform) # See if it's the symbol we need href = node.get(inkex.addNS('href', 'xlink')) # Perhaps not a reference at all... if href == None: return # Is this the right symbol? if href == sel: # Move to our group if group <> None: # Get total transform of this symbol transform = SpeleoTransform.getTotalTransform(node) # Group it together with others group.append(node) # Reset this node transform node.set( "transform", simpletransform.formatTransform( simpletransform.composeTransform( correctiveTransform, transform))) # Did anyone order a replace? if self.options.replace: node.set(inkex.addNS('href', 'xlink'), '#' + self.options.replace)
def effect(self): """ Apply the transformation. If an element already has a transformation attribute, it will be combined with the transformation matrix for the requested conversion. """ if self.options.reverse == "true": conversion = "from_" + self.options.conversion else: conversion = "to_" + self.options.conversion if len(self.selected) == 0: inkex.errormsg(_("Please select an object to perform the " + "isometric projection transformation on.")) return # Default to the flat 2D to isometric top down view conversion if an # invalid identifier is passed. effect_matrix = self.transformations.get( conversion, self.transformations.get('to_top')) for id, node in self.selected.iteritems(): bbox = computeBBox([node]) midpoint = self.getMidPoint(bbox, node) center_old = self.getTransformCenter(midpoint, node) transform = node.get("transform") # Combine our transformation matrix with any pre-existing # transform. matrix = parseTransform(transform, effect_matrix) # Compute the location of the transformation center after applying # the transformation matrix. center_new = center_old[:] applyTransformToPoint(matrix, center_new) applyTransformToPoint(matrix, midpoint) # Add a translation transformation that will move the object to # keep its transformation center in the same place. self.translateBetweenPoints(matrix, center_new, center_old) node.set('transform', formatTransform(matrix)) # Adjust the transformation center. self.moveTransformationCenter(node, midpoint, center_new)
def transform(self, elem, setval=None, parent_transform=None): """ Gets this element's transform. Use setval=matrix to set this element's transform. You can only specify parent_transform when getting. """ transform = elem.attrib.get("transform", "").strip() if transform: transform = simpletransform.parseTransform(transform) else: transform = [[1, 0, 0], [0, 1, 0], [0, 0, 1]] if parent_transform: transform = simpletransform.composeTransform(parent_transform, transform) if setval: elem.attrib["transform"] = simpletransform.formatTransform(setval) else: return transform
def transform(self, elem, setval=None, parent_transform=None): """ Gets this element's transform. Use setval=matrix to set this element's transform. You can only specify parent_transform when getting. """ transform = elem.attrib.get('transform', '').strip() if transform: transform = simpletransform.parseTransform(transform) else: transform = [[1,0,0], [0,1,0], [0,0,1]] if parent_transform: transform = simpletransform.composeTransform(parent_transform, transform) if setval: elem.attrib['transform'] = simpletransform.formatTransform(setval) else: return transform
def recursiveFuseTransform(node, transf=[[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]]): transf = composeTransform(transf, parseTransform(node.get("transform", None))) if "transform" in node.attrib: del node.attrib["transform"] node = ApplyTransform.objectToPath(node) if "d" in node.attrib: d = node.get("d") p = cubicsuperpath.parsePath(d) applyTransformToPath(transf, p) node.set("d", cubicsuperpath.formatPath(p)) elif node.tag == inkex.addNS("rect", "svg"): node.set("transform", formatTransform(transf)) for child in node.getchildren(): ApplyTransform.recursiveFuseTransform(child, transf)
def recursiveFuseTransform(node, transf=[[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]]): transf = composeTransform(transf, parseTransform(node.get("transform", None))) if 'transform' in node.attrib: del node.attrib['transform'] node = ApplyTransform.objectToPath(node) if 'd' in node.attrib: d = node.get('d') p = cubicsuperpath.parsePath(d) applyTransformToPath(transf, p) node.set('d', cubicsuperpath.formatPath(p)) elif node.tag == inkex.addNS('rect', 'svg'): node.set('transform', formatTransform(transf)) for child in node.getchildren(): ApplyTransform.recursiveFuseTransform(child, transf)
def test_simpletransform(self): """Test simpletransform API""" import simpletransform self.assertEqual(simpletransform.parseTransform('scale(10)'), [[10, 0, 0], [0, 10, 0]]) self.assertEqual(simpletransform.parseTransform('translate(2,3)'), [[1, 0, 2], [0, 1, 3]]) self.assertEqual( simpletransform.parseTransform('translate(2,3) rotate(90)'), [approx([0, -1, 2]), approx([1, 0, 3])]) m = simpletransform.formatTransform([[0, -1, 2], [1, 0, 3]]) self.assertEqual(re.sub(r',', ' ', re.sub(r'\.0*\b', '', m)), 'matrix(0 1 -1 0 2 3)') self.assertEqual( simpletransform.invertTransform([[1, 0, 2], [0, 1, 3]]), [[1, 0, -2], [0, 1, -3]]) self.assertEqual( simpletransform.composeTransform([[1, 0, 2], [0, 1, 3]], [[0, -1, 0], [1, 0, 0]]), [[0, -1, 2], [1, 0, 3]]) pt = [4, 5] self.assertEqual( simpletransform.applyTransformToPoint([[0, -1, 2], [1, 0, 3]], pt), None) self.assertEqual(pt, [-3, 7]) self.assertEqual(simpletransform.boxunion([3, 5, 2, 4], [4, 6, 1, 3]), (3, 6, 1, 4)) self.assertEqual(simpletransform.cubicExtrema(1, 2, 3, 4), (1, 4)) # TODO need cubic superpath self.assertTrue(simpletransform.applyTransformToPath) self.assertTrue(simpletransform.roughBBox) self.assertTrue(simpletransform.refinedBBox) # TODO need node self.assertTrue(simpletransform.fuseTransform) self.assertTrue(simpletransform.composeParents) self.assertTrue(simpletransform.applyTransformToNode) self.assertTrue(simpletransform.computeBBox) self.assertTrue(simpletransform.computePointInNode)
def get_correction_transform(node, child=False): """Get a transform to apply to new siblings or children of this SVG node""" # if we want to place our new nodes in the same group/layer as this node, # then we'll need to factor in the effects of any transforms set on # the parents of this node. if child: transform = get_node_transform(node) else: # we can ignore the transform on the node itself since it won't apply # to the objects we add transform = get_node_transform(node.getparent()) # now invert it, so that we can position our objects in absolute # coordinates transform = simpletransform.invertTransform(transform) return simpletransform.formatTransform(transform)
def emit_inkscape(parent, stitches): transform = get_viewbox_transform(parent.getroottree().getroot()) # we need to correct for the viewbox transform = simpletransform.invertTransform(transform) transform = simpletransform.formatTransform(transform) for color, polyline in stitches_to_polylines(stitches): # dbg.write('polyline: %s %s\n' % (color, repr(polyline))) inkex.etree.SubElement( parent, inkex.addNS('polyline', 'svg'), { 'style': simplestyle.formatStyle( { 'stroke': color if color is not None else '#000000', 'stroke-width': "0.4", 'fill': 'none' }), 'points': " ".join(",".join(str(coord) for coord in point) for point in polyline), 'transform': transform })
def effect(self): # search for pattern layer triangle_layer = self.document.xpath('//svg:g[@id="triangle_layer"]', namespaces=inkex.NSS) if len(triangle_layer) == 0: # append pattern layer triangle_layer = self.createLayer("triangle_layer", "Triangle Boundary") self.document.getroot().append(triangle_layer) inkex.debug( "1. draw exactly one triangle in Triangle Boundary layer\n" "2. create a pattern composed of only paths\n" "3. select triangles that you want to apply pattern to\n" "4. enter pattern id (or default to use first pattern), then apply" ) else: triangle_layer = triangle_layer[0] # find triangle in the triangle_layer trngl_lyr_children = triangle_layer.getchildren() if not len(trngl_lyr_children) == 1: inkex.debug( "more or less than 1 element in Triangle Boundary layer: " + str(len(trngl_lyr_children))) return else: bndry_trngle = triangle_layer.getchildren()[0] # seems like a path if isPath(bndry_trngle): path_string = bndry_trngle.attrib[u'd'] svg_path = simplepath.parsePath(path_string) # verified to be triangle if pathIsTriangle(svg_path): self.bndry_trngle_verts = getTriangleVerts(svg_path) self.bndry_trngle_matrx = formMatrix( self.bndry_trngle_verts) # find pattern self.defs = self.document.xpath( '//svg:defs', namespaces=inkex.NSS)[0] # get first pattern is default if self.options.pattern_name == "default": patterns = self.defs.xpath('./svg:pattern', namespaces=inkex.NSS) # find pattern with name else: patterns = self.defs.xpath( './svg:pattern[@id="' + self.options.pattern_name + '"]', namespaces=inkex.NSS) if len(patterns) > 0: self.pattern = patterns[0] # calculate the size of the pattern so that the pattern repeats exactly once in the bounding triangle # get the union of all paths in pattern and boundary triangle pattern_trngle_union = self.pattern.xpath( './/svg:path', namespaces=inkex.NSS) pattern_trngle_union.append(bndry_trngle) # get bounding box of the union bb_x_min, bb_x_max, bb_y_min, bb_y_max = simpletransform.computeBBox( pattern_trngle_union) # calculate size of union pattern_width = bb_x_max - bb_x_min pattern_height = bb_y_max - bb_y_min # get scaling factor of pattern pattern_trnsfrm = simpletransform.parseTransform( self.pattern.attrib[u'patternTransform']) scale_x = pattern_trnsfrm[0][0] scale_y = pattern_trnsfrm[1][1] # set size of pattern self.pattern.attrib[u'width'] = str(pattern_width / scale_x) self.pattern.attrib[u'height'] = str( pattern_height / scale_y) else: inkex.debug("cannot find pattern") return else: inkex.debug( "the shape in triangle boundary layer is not a triangle" ) return for id, node in self.selected.iteritems(): if isPath(node): path_string = node.attrib[u'd'] svg_path = simplepath.parsePath(path_string) if pathIsTriangle(svg_path): trngle_verts = getTriangleVerts(svg_path) trngle_matrx = formMatrix(trngle_verts) # apply affine transform pattern_trnsform = three.multiply( trngle_matrx, three.getInverse(self.bndry_trngle_matrx)) # compose with initial transform of pattern initial_trnsform = simpletransform.parseTransform( self.pattern.attrib[u'patternTransform']) final_trnsform = simpletransform.composeTransform( pattern_trnsform, initial_trnsform) # if pattern for triangle exists, use it pattern_name = "pattern_for_" + str(id) existing_patterns = self.defs.xpath('./svg:pattern[@id="' + pattern_name + '"]', namespaces=inkex.NSS) if len(existing_patterns) > 0: pattern_transformed = existing_patterns[0] else: # create transformed pattern pattern_transformed = etree.Element("{%s}pattern" % inkex.NSS[u'svg']) # fill in the attributes for pattern pattern_transformed.attrib[u'id'] = "pattern_for_" + str( id) pattern_transformed.attrib[ "{%s}collect" % inkex.NSS[u'inkscape']] = "always" pattern_transformed.attrib[ "{%s}href" % inkex.NSS[u'xlink']] = '#' + self.pattern.attrib[u'id'] pattern_transformed.attrib[ u'patternTransform'] = simpletransform.formatTransform( final_trnsform) # append transformed pattern self.defs.append(pattern_transformed) # fill triangle with pattern trngle_styles = simplestyle.parseStyle( node.attrib[u'style']) trngle_styles[u'fill'] = u'url(#' + str( pattern_transformed.attrib[u'id']) + ')' node.attrib[u'style'] = simplestyle.formatStyle( trngle_styles) else: inkex.debug("not triangle")
def propagate_attribs(node, parent_style={}, parent_transform=[[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]]): """Propagate style and transform to remove inheritance""" # Don't enter non-graphical portions of the document if (node.tag == addNS("namedview", "sodipodi") or node.tag == addNS("defs", "svg") or node.tag == addNS("metadata", "svg") or node.tag == addNS("foreignObject", "svg")): return # Compose the transformations if node.tag == addNS("svg", "svg") and node.get("viewBox"): vx, vy, vw, vh = [ get_dimension(x) for x in node.get("viewBox").split() ] dw = get_dimension(node.get("width", vw)) dh = get_dimension(node.get("height", vh)) t = "translate(%f, %f) scale(%f, %f)" % (-vx, -vy, dw / vw, dh / vh) this_transform = simpletransform.parseTransform(t, parent_transform) this_transform = simpletransform.parseTransform( node.get("transform"), this_transform) del node.attrib["viewBox"] else: this_transform = simpletransform.parseTransform( node.get("transform"), parent_transform) # Compose the style attribs this_style = simplestyle.parseStyle(node.get("style", "")) remaining_style = {} # Style attributes that are not propagated non_propagated = ["filter" ] # Filters should remain on the topmost ancestor for key in non_propagated: if key in this_style.keys(): remaining_style[key] = this_style[key] del this_style[key] # Create a copy of the parent style, and merge this style into it parent_style_copy = parent_style.copy() parent_style_copy.update(this_style) this_style = parent_style_copy # Merge in any attributes outside of the style style_attribs = ["fill", "stroke"] for attrib in style_attribs: if node.get(attrib): this_style[attrib] = node.get(attrib) del node.attrib[attrib] if (node.tag == addNS("svg", "svg") or node.tag == addNS("g", "svg") or node.tag == addNS("a", "svg") or node.tag == addNS("switch", "svg")): # Leave only non-propagating style attributes if len(remaining_style) == 0: if "style" in node.keys(): del node.attrib["style"] else: node.set("style", simplestyle.formatStyle(remaining_style)) # Remove the transform attribute if "transform" in node.keys(): del node.attrib["transform"] # Continue propagating on subelements for c in node.iterchildren(): propagate_attribs(c, this_style, this_transform) else: # This element is not a container # Merge remaining_style into this_style this_style.update(remaining_style) # Set the element's style and transform attribs node.set("style", simplestyle.formatStyle(this_style)) node.set("transform", simpletransform.formatTransform(this_transform))
def effect(self) : if (self.options.create_bearing_gear and self.options.active_tab == '"help"' ): self.error( """English spport forum: http://www.cnc-club.ru/forum/viewforum.php?f=33 Russian support forum: http://cnc-club.ru/forum/viewtopic.php?f=15&t=287 """) return time_ = time.time() self.distance = self.options.distance * self.options.units self.options.linear_distance *= self.options.units self.options.gear_r *= self.options.units self.options.bearings_d *= self.options.units self.first_teeth = self.options.selected_puley_teeth self.second_teeth = self.options.generated_puley_teeth self.num = self.options.number_of_copies def add_circle(c,r, center_mark=False) : return ( "M%s,%s a %s,%s 0 1 1 %s,0 %s,%s 0 1 1 %s,0 z " % ( c[0]+r,c[1], r,r, -2*r, r,r, 2*r) + #c+r,c r r -2*r r r 2*r ( ("M %s,%s l -11,-11 22,22 -11,-11 -11,11, 22,-22" % (c[0],c[1])) if center_mark else "") ) c = list(self.view_center) d = "" d1 = "" if (self.options.create_bearing_gear and self.options.active_tab == '"linear_shaft"' ): r = (self.options.gear_r*2+self.options.bearings_d/2+100) for i in range(self.options.number_of_bearings): a = i*math.pi*2/self.options.number_of_bearings d += add_circle([c[0]+math.cos(a)*self.options.gear_r, c[1]+math.sin(a)*self.options.gear_r ], self.options.bearings_d/2) d1 += add_circle([c[0]+math.cos(a)*self.options.gear_r, -r+c[1]+math.sin(a)*self.options.gear_r ], self.options.bearings_d/2, True) d1 += add_circle([c[0], c[1]-r], self.options.gear_r, True) path = inkex.etree.SubElement( self.current_layer, inkex.addNS('path','svg'), {"d":d, "style": "stroke:#4d4d4d;fill:#ececec"} ) path1 = inkex.etree.SubElement( self.current_layer, inkex.addNS('path','svg'), {"d":d1, "style": "stroke:#4d4d4d;fill:#ececec"} ) csp = cubicsuperpath.parsePath(d) minx,miny,maxx,maxy = csp_true_bounds(csp) c1 = [ (maxx[0] + minx[0])/2, (maxy[1] + miny[1])/2 ] path.set(inkex.addNS('transform-center-x','inkscape'),str(-c1[0]+c[0])) path.set(inkex.addNS('transform-center-y','inkscape'),str(-c1[1]+c[1])) self.selected = {"1":path} if len(self.selected)!=1: self.error('Please select one and only one path!','error') path = self.selected.values()[0] if "d" not in path.keys() : self.error('Please select a "Path"!','error') csp = cubicsuperpath.parsePath(path.get("d")) center_group = inkex.etree.SubElement( path.getparent(), inkex.addNS('g','svg') ) group = inkex.etree.SubElement( path.getparent(), inkex.addNS('g','svg') ) attrib = path.attrib if "transform" in path.keys() : t = path.get('transform') t = simpletransform.parseTransform(t) simpletransform.applyTransformToPath(t,csp) path.set("transform", "") path.set('d', cubicsuperpath.formatPath(csp)) inkex.etree.SubElement( group, inkex.addNS('path','svg'), {"d": cubicsuperpath.formatPath(csp)}) self.error(path.get("transform")) # Will have to find center of bbox minx,miny,maxx,maxy = csp_true_bounds(csp) c1 = [ (maxx[0] + minx[0])/2, (maxy[1] + miny[1])/2 ] w,h = max(maxx[0]-minx[0], abs(c1[0]-minx[0]), abs(c1[0]-maxx[0]) ), max(maxy[1]-miny[1], abs(c1[1]-miny[1]), abs(c1[1]-maxy[1]) ) if inkex.addNS('transform-center-x','inkscape') in path.keys() : c1[0] += float(path.get(inkex.addNS('transform-center-x','inkscape'))) if inkex.addNS('transform-center-y','inkscape') in path.keys() : c1[1] -= float(path.get(inkex.addNS('transform-center-y','inkscape'))) c2 = [c1[0]-self.distance, c1[1]] def get_transform(c1,c2,a1,a2): [c1x,c1y], [c2x,c2y] = c1,c2 # move c1 to 0 transform = [ [1,0,-c1x], [0,1,-c1y] ] # Rotate to a1 to 0 transform = simpletransform.composeTransform([ [math.cos(a1), -math.sin(a1), 0], [math.sin(a1), math.cos(a1), 0] ], transform ) # Move c2 to 0 transform = simpletransform.composeTransform( [ [1,0,-c2x+c1x], [0,1,-c2y+c1y] ], transform) # Rotate to a2 to 0 transform = simpletransform.composeTransform( [ [math.cos(a2), -math.sin(a2), 0], [math.sin(a2), math.cos(a2), 0] ] , transform) # move c2 back to c2 transform = simpletransform.composeTransform([ [1,0,c2x], [0,1,c2y] ], transform) return transform if not self.options.active_tab == '"linear_shaft"' : # Radial gear if not self.options.variable_speed : for i in range(self.num): alpha = math.pi*2*(i)/self.num*self.options.rev alpha1 = alpha*self.second_teeth alpha2 = alpha*self.first_teeth transform = get_transform(c1,c2,alpha1,alpha2) # add path's clone attrib["transform"] = simpletransform.formatTransform(transform) inkex.etree.SubElement( group, inkex.addNS('path','svg'), attrib ) else : def get_k (csp, c1,d): c2 = [c1[0]-d, c1[1]] alpha2_ = [] for n in range(self.num): alpha1 = math.pi*2*(n)/self.num * self.second_teeth d_alpha1 = math.pi*2/self.num * self.second_teeth csp_ = [[[p[:] for p in point] for point in subpath] for subpath in csp ] transform = get_transform(c1,c2,alpha1,0) simpletransform.applyTransformToPath(transform, csp_) # r2 = distance to second gear's center [r2, i,j,t] = csp_to_point_distance(csp_, c2, dist_bounds = [0,1e100], tolerance=.001) r2 = math.sqrt(r2) p = csp_at_t(csp_[i][j-1],csp_[i][j],t) r1 = math.sqrt((p[0]-c1[0])**2 +(p[1]-c1[1])**2) # d_alpha2 = rotation speed factor if r2 == 0 : return 1e100, [] alpha2_.append(d_alpha1 * r1/r2) return math.pi*2 * self.first_teeth / sum(alpha2_), alpha2_ #get K, firs calculate aproximate turn and then get the K if not self.options.optimize_distance : K, alpha2_ = get_k(csp,c1,self.distance) else : #set min and max distances dists = [0., math.sqrt(w**2+h**2)*(1+self.second_teeth/self.first_teeth) ] first = True for i in range(optimize_distance_max_iters): d = (dists[0]+dists[1])/2 # if not first and not dists[0]<self.distance<dists[1] else self.distance first = False K, alpha2_ = get_k(csp,c1,d) if K>1 : dists = [dists[0], d] else: dists = [d, dists[1]] if abs(1.-K)< optimize_distance_tolerance : break #self.error(str((i,K,d))) self.distance = d c2 = [c1[0]-self.distance, c1[1]] # Now create the paths alpha2 = 0 for i in range(self.num): alpha = math.pi*2*(i)/self.num alpha1 = alpha*self.second_teeth alpha2 += alpha2_[i] * K transform = get_transform(c1,c2,alpha1,alpha2) # add path's clone attrib["transform"] = simpletransform.formatTransform(transform) inkex.etree.SubElement( group, inkex.addNS('path','svg'), attrib ) self.error("Optimized distance: %s. "%(self.distance/self.options.units)) self.error("Optimized parameter K: %s (the closer to 1.0 the better)."%K) self.error("Time elapsed %s seconds." %(time.time()-time_)) self.draw_pointer(c1, color = "#000080", width = 1, group = center_group ) self.draw_pointer(c2, color = "#000080", width = 1, group = center_group ) else : # Linear gear c1x,c1y = c1[0], c1[1] i = 0 self.distance = self.options.linear_distance if self.options.standatd_distance and self.options.create_bearing_gear: self.distance = self.options.gear_r+self.options.bearings_d/2 while i<=self.options.rev*self.num : a = math.pi*2*i/self.num d = self.distance * a # Move c to 0 transform = [ [1,0,-c1x], [0,1,-c1y] ] # Rotate to a transform = simpletransform.composeTransform( [ [math.cos(a), -math.sin(a), 0], [math.sin(a), math.cos(a), 0] ] , transform) # Move 0 to c + d transform = simpletransform.composeTransform( [ [1,0,c1x+d], [0,1,c1y] ], transform) attrib["transform"] = simpletransform.formatTransform(transform) inkex.etree.SubElement( group, inkex.addNS('path','svg'), attrib ) i += 1
def output(self): root = self.document.getroot() doc_width = self.unittouu(root.get('width')) doc_height = self.unittouu(root.get('height')) # load prefs from file th2pref_load_from_xml(root) th2pref.xyascenter = self.options.xyascenter self.r2d = [[1, 0, 0],[0, -1, doc_height]] viewBox = root.get('viewBox') if viewBox: m1 = parseViewBox(viewBox, doc_width, doc_height) self.r2d = simpletransform.composeTransform(self.r2d, m1) self.classes = {} stylenodes = self.document.xpath('//svg:style', namespaces=inkex.NSS) pattern = re.compile(r'\.(\w+)\s*\{(.*?)\}') for stylenode in stylenodes: if isinstance(stylenode.text, str): for x in pattern.finditer(stylenode.text): self.classes[x.group(1)] = simplestyle.parseStyle(x.group(2).strip()) print '''encoding utf-8 ##XTHERION## xth_me_area_adjust 0 0 %f %f ##XTHERION## xth_me_area_zoom_to 100 ''' % (doc_width * th2pref.basescale, doc_height * th2pref.basescale) # text on path if th2pref.textonpath: textpaths = self.document.xpath('//svg:textPath', namespaces=inkex.NSS) for node in textpaths: href = node.get(xlink_href).split('#', 1)[-1] options = {'text': self.get_point_text(node)} self.guess_text_scale(node, self.get_style(node.getparent()), options, self.i2d_affine(node)) self.textpath_dict[href] = options if self.options.images: images = self.document.xpath('//svg:image', namespaces=inkex.NSS) + \ self.document.xpath('//svg:g[@therion:type="xth_me_image_insert"]', namespaces=inkex.NSS) #for node in reversed(images): for node in images: params = [ self.unittouu(node.get('x', '0')), self.unittouu(node.get('y', '0')) ] mat = self.i2d_affine(node) href = node.get(xlink_href, '') if href == '': # xvi image (svg:g) options = parse_options(node.get(therion_options, '')) href = options.get('href', '') elif href.startswith('data:'): inkex.errormsg('Embedded images not supported!') continue paramsTrans = transformParams(mat, params) mat = simpletransform.composeTransform(mat, [[1,0,params[0]],[0,1,params[1]]]) w = node.get('width', '100%') h = node.get('height', '100%') if th2pref.image_inkscape: print '##INKSCAPE## image %s %s %s %s' % (w, h, simpletransform.formatTransform(mat), href) continue if href.startswith('file://'): href = href[7:] print '##XTHERION## xth_me_image_insert {%f 1 1.0} {%f 1} "%s" 0 {}' % \ (paramsTrans[0], paramsTrans[1], href) self.print_scrap_begin('scrap1', not self.options.lay2scr) if self.options.layers == 'current': layer = self.current_layer while layer.get(inkscape_groupmode, '') != "layer" and layer.getparent(): layer = layer.getparent() self.output_scrap(layer) else: layers = self.document.xpath('/svg:svg/svg:g[@inkscape:groupmode="layer"]', namespaces=inkex.NSS) if len(layers) == 0: inkex.errormsg("Document has no layers!\nFallback to single scrap") layers = [ root ] for layer in layers: if layer.get(therion_role) == 'none': continue if self.options.layers == 'visible': style = self.get_style(layer) if style.get('display') == 'none': continue self.output_scrap(layer) self.print_scrap_end(not self.options.lay2scr)
def compute_bbox(self, node, transform=True, use_cache=False): ''' Compute the bounding box of a element in its parent coordinate system, or in its own coordinate system if "transform" is False. Uses a cache to not compute the bounding box multiple times for elements like referenced symbols. Returns [xmin, xmax, ymin, ymax] Enhanced version of simpletransform.computeBBox() Warning: Evaluates "transform" attribute for symbol tags, which is wrong according to SVG spec, but matches Inkscape's behaviour. ''' import cubicsuperpath from simpletransform import boxunion, parseTransform, applyTransformToPath, formatTransform try: from simpletransform import refinedBBox except: from simpletransform import roughBBox as refinedBBox d = None recurse = False node_bbox = None if transform: transform = node.get('transform', '') else: transform = '' if use_cache and node in self.bbox_cache: node_bbox = self.bbox_cache[node] elif node.tag in [ svg_use, 'use' ]: x, y = float(node.get('x', 0)), float(node.get('y', 0)) refid = node.get(xlink_href) refnode = self.getElementById(refid[1:]) if refnode is None: return None if 'width' in node.attrib and 'height' in node.attrib and 'viewBox' in refnode.attrib: mat = parseViewBox(refnode.get('viewBox'), node.get('width'), node.get('height')) transform += ' ' + formatTransform(mat) refbbox = self.compute_bbox(refnode, True, True) if refbbox is not None: node_bbox = [refbbox[0] + x, refbbox[1] + x, refbbox[2] + y, refbbox[3] + y] elif node.get('d'): d = node.get('d') elif node.get('points'): d = 'M' + node.get('points') elif node.tag in [ svg_rect, 'rect', svg_image, 'image' ]: d = 'M' + node.get('x', '0') + ',' + node.get('y', '0') + \ 'h' + node.get('width') + 'v' + node.get('height') + \ 'h-' + node.get('width') elif node.tag in [ svg_line, 'line' ]: d = 'M' + node.get('x1') + ',' + node.get('y1') + \ ' ' + node.get('x2') + ',' + node.get('y2') elif node.tag in [ svg_circle, 'circle', svg_ellipse, 'ellipse' ]: rx = node.get('r') if rx is not None: ry = rx else: rx = node.get('rx') ry = node.get('ry') rx, ry = float(rx), float(ry) cx = float(node.get('cx', '0')) cy = float(node.get('cy', '0')) node_bbox = [cx - rx, cx + rx, cy - ry, cy + ry] ''' a = 0.555 d = 'M %f %f C' % (cx-rx, cy) + ' '.join('%f' % c for c in [ cx-rx, cy-ry*a, cx-rx*a, cy-ry, cx, cy-ry, cx+rx*a, cy-ry, cx+rx, cy-ry*a, cx+rx, cy, cx+rx, cy+ry*a, cx+rx*a, cy+ry, cx, cy+ry, cx-rx*a, cy+ry, cx-rx, cy+ry*a, cx-rx, cy, ]) ''' elif node.tag in [ svg_text, 'text', svg_tspan, 'tspan' ]: # very rough estimate of text bounding box x = node.get('x', '0').split() y = node.get('y', '0').split() if len(x) == 1 and len(y) > 1: x = x * len(y) elif len(y) == 1 and len(x) > 1: y = y * len(x) d = 'M' + ' '.join('%f' % self.unittouu(c) for xy in zip(x, y) for c in xy) recurse = True elif node.tag in [ svg_g, 'g', svg_symbol, 'symbol', svg_svg, 'svg' ]: recurse = True if d is not None: p = cubicsuperpath.parsePath(d) node_bbox = refinedBBox(p) if recurse: for child in node: child_bbox = self.compute_bbox(child, True, use_cache) node_bbox = boxunion(child_bbox, node_bbox) self.bbox_cache[node] = node_bbox if transform.strip() != '' and node_bbox != None: mat = parseTransform(transform) p = [[[ [node_bbox[0], node_bbox[2]], [node_bbox[0], node_bbox[3]], [node_bbox[1], node_bbox[2]], [node_bbox[1], node_bbox[3]]]]] applyTransformToPath(mat, p) x, y = zip(*p[0][0]) node_bbox = [min(x), max(x), min(y), max(y)] return node_bbox
def effect(self): if (self.options.create_bearing_gear and self.options.active_tab == '"help"'): self.error("""English spport forum: http://www.cnc-club.ru/forum/viewforum.php?f=33 Russian support forum: http://cnc-club.ru/forum/viewtopic.php?f=15&t=287 """) return time_ = time.time() self.distance = self.options.distance * self.options.units self.options.linear_distance *= self.options.units self.options.gear_r *= self.options.units self.options.bearings_d *= self.options.units self.first_teeth = self.options.selected_puley_teeth self.second_teeth = self.options.generated_puley_teeth self.num = self.options.number_of_copies def add_circle(c, r, center_mark=False): return ("M%s,%s a %s,%s 0 1 1 %s,0 %s,%s 0 1 1 %s,0 z " % (c[0] + r, c[1], r, r, -2 * r, r, r, 2 * r) + #c+r,c r r -2*r r r 2*r (("M %s,%s l -11,-11 22,22 -11,-11 -11,11, 22,-22" % (c[0], c[1])) if center_mark else "")) c = list(self.view_center) d = "" d1 = "" if (self.options.create_bearing_gear and self.options.active_tab == '"linear_shaft"'): r = (self.options.gear_r * 2 + self.options.bearings_d / 2 + 100) for i in range(self.options.number_of_bearings): a = i * math.pi * 2 / self.options.number_of_bearings d += add_circle([ c[0] + math.cos(a) * self.options.gear_r, c[1] + math.sin(a) * self.options.gear_r ], self.options.bearings_d / 2) d1 += add_circle([ c[0] + math.cos(a) * self.options.gear_r, -r + c[1] + math.sin(a) * self.options.gear_r ], self.options.bearings_d / 2, True) d1 += add_circle([c[0], c[1] - r], self.options.gear_r, True) path = inkex.etree.SubElement( self.current_layer, inkex.addNS('path', 'svg'), { "d": d, "style": "stroke:#4d4d4d;fill:#ececec" }) path1 = inkex.etree.SubElement( self.current_layer, inkex.addNS('path', 'svg'), { "d": d1, "style": "stroke:#4d4d4d;fill:#ececec" }) csp = cubicsuperpath.parsePath(d) minx, miny, maxx, maxy = csp_true_bounds(csp) c1 = [(maxx[0] + minx[0]) / 2, (maxy[1] + miny[1]) / 2] path.set(inkex.addNS('transform-center-x', 'inkscape'), str(-c1[0] + c[0])) path.set(inkex.addNS('transform-center-y', 'inkscape'), str(-c1[1] + c[1])) self.selected = {"1": path} if len(self.selected) != 1: self.error('Please select one and only one path!', 'error') path = self.selected.values()[0] if "d" not in path.keys(): self.error('Please select a "Path"!', 'error') csp = cubicsuperpath.parsePath(path.get("d")) center_group = inkex.etree.SubElement(path.getparent(), inkex.addNS('g', 'svg')) group = inkex.etree.SubElement(path.getparent(), inkex.addNS('g', 'svg')) attrib = path.attrib if "transform" in path.keys(): t = path.get('transform') t = simpletransform.parseTransform(t) simpletransform.applyTransformToPath(t, csp) path.set("transform", "") path.set('d', cubicsuperpath.formatPath(csp)) inkex.etree.SubElement(group, inkex.addNS('path', 'svg'), {"d": cubicsuperpath.formatPath(csp)}) self.error(path.get("transform")) # Will have to find center of bbox minx, miny, maxx, maxy = csp_true_bounds(csp) c1 = [(maxx[0] + minx[0]) / 2, (maxy[1] + miny[1]) / 2] w, h = max(maxx[0] - minx[0], abs(c1[0] - minx[0]), abs(c1[0] - maxx[0])), max(maxy[1] - miny[1], abs(c1[1] - miny[1]), abs(c1[1] - maxy[1])) if inkex.addNS('transform-center-x', 'inkscape') in path.keys(): c1[0] += float( path.get(inkex.addNS('transform-center-x', 'inkscape'))) if inkex.addNS('transform-center-y', 'inkscape') in path.keys(): c1[1] -= float( path.get(inkex.addNS('transform-center-y', 'inkscape'))) c2 = [c1[0] - self.distance, c1[1]] def get_transform(c1, c2, a1, a2): [c1x, c1y], [c2x, c2y] = c1, c2 # move c1 to 0 transform = [[1, 0, -c1x], [0, 1, -c1y]] # Rotate to a1 to 0 transform = simpletransform.composeTransform( [[math.cos(a1), -math.sin(a1), 0], [math.sin(a1), math.cos(a1), 0]], transform) # Move c2 to 0 transform = simpletransform.composeTransform( [[1, 0, -c2x + c1x], [0, 1, -c2y + c1y]], transform) # Rotate to a2 to 0 transform = simpletransform.composeTransform( [[math.cos(a2), -math.sin(a2), 0], [math.sin(a2), math.cos(a2), 0]], transform) # move c2 back to c2 transform = simpletransform.composeTransform( [[1, 0, c2x], [0, 1, c2y]], transform) return transform if not self.options.active_tab == '"linear_shaft"': # Radial gear if not self.options.variable_speed: for i in range(self.num): alpha = math.pi * 2 * (i) / self.num alpha1 = alpha * self.second_teeth alpha2 = alpha * self.first_teeth transform = get_transform(c1, c2, alpha1, alpha2) # add path's clone attrib["transform"] = simpletransform.formatTransform( transform) inkex.etree.SubElement(group, inkex.addNS('path', 'svg'), attrib) else: def get_k(csp, c1, d): c2 = [c1[0] - d, c1[1]] alpha2_ = [] for n in range(self.num): alpha1 = math.pi * 2 * ( n) / self.num * self.second_teeth d_alpha1 = math.pi * 2 / self.num * self.second_teeth csp_ = [[[p[:] for p in point] for point in subpath] for subpath in csp] transform = get_transform(c1, c2, alpha1, 0) simpletransform.applyTransformToPath(transform, csp_) # r2 = distance to second gear's center [r2, i, j, t] = csp_to_point_distance(csp_, c2, dist_bounds=[0, 1e100], tolerance=.001) r2 = math.sqrt(r2) p = csp_at_t(csp_[i][j - 1], csp_[i][j], t) r1 = math.sqrt((p[0] - c1[0])**2 + (p[1] - c1[1])**2) # d_alpha2 = rotation speed factor if r2 == 0: return 1e100, [] alpha2_.append(d_alpha1 * r1 / r2) return math.pi * 2 * self.first_teeth / sum( alpha2_), alpha2_ #get K, firs calculate aproximate turn and then get the K if not self.options.optimize_distance: K, alpha2_ = get_k(csp, c1, self.distance) else: #set min and max distances dists = [ 0., math.sqrt(w**2 + h**2) * (1 + self.second_teeth / self.first_teeth) ] first = True for i in range(optimize_distance_max_iters): d = ( dists[0] + dists[1] ) / 2 # if not first and not dists[0]<self.distance<dists[1] else self.distance first = False K, alpha2_ = get_k(csp, c1, d) if K > 1: dists = [dists[0], d] else: dists = [d, dists[1]] if abs(1. - K) < optimize_distance_tolerance: break #self.error(str((i,K,d))) self.distance = d c2 = [c1[0] - self.distance, c1[1]] # Now create the paths alpha2 = 0 for i in range(self.num): alpha = math.pi * 2 * (i) / self.num alpha1 = alpha * self.second_teeth alpha2 += alpha2_[i] * K transform = get_transform(c1, c2, alpha1, alpha2) # add path's clone attrib["transform"] = simpletransform.formatTransform( transform) inkex.etree.SubElement(group, inkex.addNS('path', 'svg'), attrib) self.error("Optimized distance: %s. " % (self.distance / self.options.units)) self.error( "Optimized parameter K: %s (the closer to 1.0 the better)." % K) self.error("Time elapsed %s seconds." % (time.time() - time_)) self.draw_pointer(c1, color="#000080", width=1, group=center_group) self.draw_pointer(c2, color="#000080", width=1, group=center_group) else: # Linear gear c1x, c1y = c1[0], c1[1] i = 0 self.distance = self.options.linear_distance if self.options.standatd_distance and self.options.create_bearing_gear: self.distance = self.options.gear_r + self.options.bearings_d / 2 while i <= self.options.rev * self.num: a = math.pi * 2 * i / self.num d = self.distance * a # Move c to 0 transform = [[1, 0, -c1x], [0, 1, -c1y]] # Rotate to a transform = simpletransform.composeTransform( [[math.cos(a), -math.sin(a), 0], [math.sin(a), math.cos(a), 0]], transform) # Move 0 to c + d transform = simpletransform.composeTransform( [[1, 0, c1x + d], [0, 1, c1y]], transform) attrib["transform"] = simpletransform.formatTransform( transform) inkex.etree.SubElement(group, inkex.addNS('path', 'svg'), attrib) i += 1
def set_transform(self, elem, matrix): """ Sets this element's transform value to the given matrix """ elem.attrib['transform'] = simpletransform.formatTransform(matrix)
def output(self): root = self.document.getroot() doc_width = self.unittouu(root.get('width') or '0') doc_height = self.unittouu(root.get('height') or '0') # load prefs from file th2pref_load_from_xml(root) th2pref.xyascenter = self.options.xyascenter self.r2d = [[1, 0, 0],[0, -1, doc_height]] viewBox = root.get('viewBox') if viewBox and doc_width and doc_height: m1 = parseViewBox(viewBox, doc_width, doc_height) self.r2d = simpletransform.composeTransform(self.r2d, m1) self.classes = {} stylenodes = self.document.xpath('//svg:style', namespaces=inkex.NSS) pattern = re.compile(r'\.(\w+)\s*\{(.*?)\}') for stylenode in stylenodes: if isinstance(stylenode.text, basestring): for x in pattern.finditer(stylenode.text): self.classes[x.group(1)] = simplestyle.parseStyle(x.group(2).strip()) print('encoding utf-8') if doc_width and doc_height: print('##XTHERION## xth_me_area_adjust 0 0 %f %f' % ( doc_width * th2pref.basescale, doc_height * th2pref.basescale)) print('##XTHERION## xth_me_area_zoom_to 100') # text on path if th2pref.textonpath: textpaths = self.document.xpath('//svg:textPath', namespaces=inkex.NSS) for node in textpaths: href = node.get(xlink_href).split('#', 1)[-1] options = {'text': self.get_point_text(node)} self.guess_text_scale(node, self.get_style(node.getparent()), options, self.i2d_affine(node)) self.textpath_dict[href] = options if self.options.images: images = self.document.xpath('//svg:image', namespaces=inkex.NSS) + \ self.document.xpath('//svg:g[@therion:type="xth_me_image_insert"]', namespaces=inkex.NSS) #for node in reversed(images): for node in images: params = [ self.unittouu(node.get('x', '0')), self.unittouu(node.get('y', '0')) ] mat = self.i2d_affine(node) href = node.get(xlink_href, '') XVIroot = '{}' if href == '': # xvi image (svg:g) options = parse_options(node.get(therion_options, '')) href = options.get('href', '') XVIroot = options.get('XVIroot', '{}') elif href.startswith('data:'): inkex.errormsg('Embedded images not supported!') continue paramsTrans = transformParams(mat, params) mat = simpletransform.composeTransform(mat, [[1,0,params[0]],[0,1,params[1]]]) w = node.get('width', '100%') h = node.get('height', '100%') if th2pref.image_inkscape: print('##INKSCAPE## image %s %s %s %s' % (w, h, simpletransform.formatTransform(mat), href)) continue if href.startswith('file://'): href = href[7:] print('##XTHERION## xth_me_image_insert {%f 1 1.0} {%f %s} "%s" 0 {}' % \ (paramsTrans[0], paramsTrans[1], XVIroot, href)) self.print_scrap_begin('scrap1', not self.options.lay2scr) if self.options.layers == 'current': layer = self.current_layer while layer.get(inkscape_groupmode, '') != "layer" and layer.getparent(): layer = layer.getparent() self.output_scrap(layer) else: layers = self.document.xpath('/svg:svg/svg:g[@inkscape:groupmode="layer"]', namespaces=inkex.NSS) if len(layers) == 0: inkex.errormsg("Document has no layers!\nFallback to single scrap") layers = [ root ] for layer in layers: if layer.get(therion_role) == 'none': continue if self.options.layers == 'visible': style = self.get_style(layer) if style.get('display') == 'none': continue self.output_scrap(layer) self.print_scrap_end(not self.options.lay2scr)
def compute_bbox(self, node, transform=True, use_cache=False): ''' Compute the bounding box of a element in its parent coordinate system, or in its own coordinate system if "transform" is False. Uses a cache to not compute the bounding box multiple times for elements like referenced symbols. Returns [xmin, xmax, ymin, ymax] Enhanced version of simpletransform.computeBBox() Warning: Evaluates "transform" attribute for symbol tags, which is wrong according to SVG spec, but matches Inkscape's behaviour. ''' import cubicsuperpath from simpletransform import boxunion, parseTransform, applyTransformToPath, formatTransform try: from simpletransform import refinedBBox except: from simpletransform import roughBBox as refinedBBox d = None recurse = False node_bbox = None if transform: transform = node.get('transform', '') else: transform = '' if use_cache and node in self.bbox_cache: node_bbox = self.bbox_cache[node] elif node.tag in [svg_use, 'use']: x, y = float(node.get('x', 0)), float(node.get('y', 0)) refid = node.get(xlink_href) refnode = self.getElementById(refid[1:]) if refnode is None: return None if 'width' in node.attrib and 'height' in node.attrib and 'viewBox' in refnode.attrib: mat = parseViewBox(refnode.get('viewBox'), node.get('width'), node.get('height')) transform += ' ' + formatTransform(mat) refbbox = self.compute_bbox(refnode, True, True) if refbbox is not None: node_bbox = [ refbbox[0] + x, refbbox[1] + x, refbbox[2] + y, refbbox[3] + y ] elif node.get('d'): d = node.get('d') elif node.get('points'): d = 'M' + node.get('points') elif node.tag in [svg_rect, 'rect', svg_image, 'image']: d = 'M' + node.get('x', '0') + ',' + node.get('y', '0') + \ 'h' + node.get('width') + 'v' + node.get('height') + \ 'h-' + node.get('width') elif node.tag in [svg_line, 'line']: d = 'M' + node.get('x1') + ',' + node.get('y1') + \ ' ' + node.get('x2') + ',' + node.get('y2') elif node.tag in [svg_circle, 'circle', svg_ellipse, 'ellipse']: rx = node.get('r') if rx is not None: ry = rx else: rx = node.get('rx') ry = node.get('ry') rx, ry = float(rx), float(ry) cx = float(node.get('cx', '0')) cy = float(node.get('cy', '0')) node_bbox = [cx - rx, cx + rx, cy - ry, cy + ry] ''' a = 0.555 d = 'M %f %f C' % (cx-rx, cy) + ' '.join('%f' % c for c in [ cx-rx, cy-ry*a, cx-rx*a, cy-ry, cx, cy-ry, cx+rx*a, cy-ry, cx+rx, cy-ry*a, cx+rx, cy, cx+rx, cy+ry*a, cx+rx*a, cy+ry, cx, cy+ry, cx-rx*a, cy+ry, cx-rx, cy+ry*a, cx-rx, cy, ]) ''' elif node.tag in [svg_text, 'text', svg_tspan, 'tspan']: # very rough estimate of text bounding box x = node.get('x', '0').split() y = node.get('y', '0').split() if len(x) == 1 and len(y) > 1: x = x * len(y) elif len(y) == 1 and len(x) > 1: y = y * len(x) d = 'M' + ' '.join('%f' % self.unittouu(c) for xy in zip(x, y) for c in xy) recurse = True elif node.tag in [svg_g, 'g', svg_symbol, 'symbol', svg_svg, 'svg']: recurse = True if d is not None: p = cubicsuperpath.parsePath(d) node_bbox = refinedBBox(p) if recurse: for child in node: child_bbox = self.compute_bbox(child, True, use_cache) node_bbox = boxunion(child_bbox, node_bbox) self.bbox_cache[node] = node_bbox if transform.strip() != '' and node_bbox != None: mat = parseTransform(transform) p = [[[[node_bbox[0], node_bbox[2]], [node_bbox[0], node_bbox[3]], [node_bbox[1], node_bbox[2]], [node_bbox[1], node_bbox[3]]]]] applyTransformToPath(mat, p) x, y = zip(*p[0][0]) node_bbox = [min(x), max(x), min(y), max(y)] return node_bbox
def propagate_attribs(node, parent_style={}, parent_transform=[[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]]): """Propagate style and transform to remove inheritance""" # Don't enter non-graphical portions of the document if (node.tag == addNS("namedview", "sodipodi") or node.tag == addNS("defs", "svg") or node.tag == addNS("metadata", "svg") or node.tag == addNS("foreignObject", "svg")): return # Compose the transformations if node.tag == addNS("svg", "svg") and node.get("viewBox"): vx, vy, vw, vh = [get_dimension(x) for x in node.get("viewBox").split()] dw = get_dimension(node.get("width", vw)) dh = get_dimension(node.get("height", vh)) t = "translate(%f, %f) scale(%f, %f)" % (-vx, -vy, dw/vw, dh/vh) this_transform = simpletransform.parseTransform(t, parent_transform) this_transform = simpletransform.parseTransform(node.get("transform"), this_transform) del node.attrib["viewBox"] else: this_transform = simpletransform.parseTransform(node.get("transform"), parent_transform) # Compose the style attribs this_style = simplestyle.parseStyle(node.get("style", "")) remaining_style = {} # Style attributes that are not propagated non_propagated = ["filter"] # Filters should remain on the topmost ancestor for key in non_propagated: if key in this_style.keys(): remaining_style[key] = this_style[key] del this_style[key] # Create a copy of the parent style, and merge this style into it parent_style_copy = parent_style.copy() parent_style_copy.update(this_style) this_style = parent_style_copy # Merge in any attributes outside of the style style_attribs = ["fill", "stroke"] for attrib in style_attribs: if node.get(attrib): this_style[attrib] = node.get(attrib) del node.attrib[attrib] if (node.tag == addNS("svg", "svg") or node.tag == addNS("g", "svg") or node.tag == addNS("a", "svg") or node.tag == addNS("switch", "svg")): # Leave only non-propagating style attributes if len(remaining_style) == 0: if "style" in node.keys(): del node.attrib["style"] else: node.set("style", simplestyle.formatStyle(remaining_style)) # Remove the transform attribute if "transform" in node.keys(): del node.attrib["transform"] # Continue propagating on subelements for c in node.iterchildren(): propagate_attribs(c, this_style, this_transform) else: # This element is not a container # Merge remaining_style into this_style this_style.update(remaining_style) # Set the element's style and transform attribs node.set("style", simplestyle.formatStyle(this_style)) node.set("transform", simpletransform.formatTransform(this_transform))
def copy_frame(self, basename,status, newbasename,newstatus): current = self.getElementById(basename+'-'+status+'-top') if current != None: new = copy.deepcopy(current) new.set('id', newbasename+'-'+newstatus+'-top') if new.attrib.has_key('transform'): mat = simpletransform.parseTransform(new.get('transform')) mat[1][2] = mat[1][2]-30 else: mat = [[1,0,0],[0,1,-30]] new.set('transform', simpletransform.formatTransform(mat)) current.getparent().append(new) current = self.getElementById(basename+'-'+status+'-bottom') if current != None: new = copy.deepcopy(current) new.set('id', newbasename+'-'+newstatus+'-bottom') if new.attrib.has_key('transform'): mat = simpletransform.parseTransform(new.get('transform')) mat[1][2] = mat[1][2]-30 else: mat = [[1,0,0],[0,1,-30]] new.set('transform', simpletransform.formatTransform(mat)) current.getparent().append(new) current = self.getElementById(basename+'-'+status+'-left') if current != None: new = copy.deepcopy(current) new.set('id', newbasename+'-'+newstatus+'-left') if new.attrib.has_key('transform'): mat = simpletransform.parseTransform(new.get('transform')) mat[1][2] = mat[1][2]-30 else: mat = [[1,0,0],[0,1,-30]] new.set('transform', simpletransform.formatTransform(mat)) current.getparent().append(new) current = self.getElementById(basename+'-'+status+'-right') if current != None: new = copy.deepcopy(current) new.set('id', newbasename+'-'+newstatus+'-right') if new.attrib.has_key('transform'): mat = simpletransform.parseTransform(new.get('transform')) mat[1][2] = mat[1][2]-30 else: mat = [[1,0,0],[0,1,-30]] new.set('transform', simpletransform.formatTransform(mat)) current.getparent().append(new) current = self.getElementById(basename+'-'+status+'-topleft') if current != None: new = copy.deepcopy(current) new.set('id', newbasename+'-'+newstatus+'-topleft') if new.attrib.has_key('transform'): mat = simpletransform.parseTransform(new.get('transform')) mat[1][2] = mat[1][2]-30 else: mat = [[1,0,0],[0,1,-30]] new.set('transform', simpletransform.formatTransform(mat)) current.getparent().append(new) current = self.getElementById(basename+'-'+status+'-topright') if current != None: new = copy.deepcopy(current) new.set('id', newbasename+'-'+newstatus+'-topright') if new.attrib.has_key('transform'): mat = simpletransform.parseTransform(new.get('transform')) mat[1][2] = mat[1][2]-30 else: mat = [[1,0,0],[0,1,-30]] new.set('transform', simpletransform.formatTransform(mat)) current.getparent().append(new) current = self.getElementById(basename+'-'+status+'-bottomleft') if current != None: new = copy.deepcopy(current) new.set('id', newbasename+'-'+newstatus+'-bottomleft') if new.attrib.has_key('transform'): mat = simpletransform.parseTransform(new.get('transform')) mat[1][2] = mat[1][2]-30 else: mat = [[1,0,0],[0,1,-30]] new.set('transform', simpletransform.formatTransform(mat)) current.getparent().append(new) current = self.getElementById(basename+'-'+status+'-bottomright') if current != None: new = copy.deepcopy(current) new.set('id', newbasename+'-'+newstatus+'-bottomright') if new.attrib.has_key('transform'): mat = simpletransform.parseTransform(new.get('transform')) mat[1][2] = mat[1][2]-30 else: mat = [[1,0,0],[0,1,-30]] new.set('transform', simpletransform.formatTransform(mat)) current.getparent().append(new)