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_
def effect(self): self.dxf_insert_code('999', '"DXF R12 Output" (www.mydxf.blogspot.com)') self.dxf_add(r12_header) scale = 25.4 / 90.0 h = self.unittouu(self.getDocumentHeight()) path = '//svg:path' for node in self.document.getroot().xpath(path, namespaces=inkex.NSS): layer = node.getparent().get(inkex.addNS('label', 'inkscape')) if layer == None: layer = 'Layer 1' d = node.get('d') p = cubicsuperpath.parsePath(d) t = node.get('transform') if t != None: m = simpletransform.parseTransform(t) simpletransform.applyTransformToPath(m, p) m = [[scale, 0, 0], [0, -scale, h * scale]] simpletransform.applyTransformToPath(m, p) if re.search('drill$', layer, re.I) == None: #if layer == 'Brackets Drill': self.dxf_path_to_lines(layer, p) else: self.dxf_path_to_point(layer, p) self.dxf_add(r12_footer)
def plotPath(self, node, matTransform): """ Plot the path while applying the transformation defined by the matrix matTransform. """ filledPath = (simplestyle.parseStyle(node.get("style")).get("fill", "none") != "none") # Plan: Turn this path into a cubicsuperpath (list of beziers)... d = node.get("d") if len(simplepath.parsePath(d)) == 0: return p = cubicsuperpath.parsePath(d) # ... and apply the transformation to each point. simpletransform.applyTransformToPath(matTransform, p) # p is now a list of lists of cubic beziers [cp1, cp2, endp] # where the start-point is the last point of the previous segment. # For some reason the inkscape extensions uses csp[1] as the coordinates of each point, # but that makes it seem like they are using control point 2 as line points. # Maybe that is a side-effect of the CSP subdivion process? TODO realPoints = [] for sp in p: cspsubdiv.subdiv(sp, self.smoothness) for csp in sp: realPoints.append( (csp[1][0], csp[1][1]) ) self.rawObjects.append( (filledPath, realPoints) )
def point_generator(path, mat, flatness): rirCount = 0 if len(simplepath.parsePath(path)) == 0: return simple_path = simplepath.parsePath(path) startX,startY = float(simple_path[0][1][0]), float(simple_path[0][1][1]) command = simple_path[0][0][0] yield startX, startY, command p = cubicsuperpath.parsePath(path) if mat: simpletransform.applyTransformToPath(mat, p) commandPile = [] for sp in p: cspsubdiv.subdiv( sp, flatness) for csp in sp: ctrl_pt1 = csp[0] ctrl_pt2 = csp[1] end_pt = csp[2] command = csp[-1] commandPile.append(command) yield end_pt[0], end_pt[1], command rirCount += 1
def apply_transforms(path, node): transform = get_node_transform(node) # apply the combined transform to this node's path simpletransform.applyTransformToPath(transform, path) return path
def path_to_segments(node, smoothness=0.1): ''' Generator to convert a path node to an interator on segmented paths (bezier curves broken to approximated straights lines). ''' mat = simpletransform.composeParents(node, [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]]) d = node.get('d') if len(simplepath.parsePath(d)) == 0: return p = cubicsuperpath.parsePath(d) simpletransform.applyTransformToPath(mat, p) # p is now a list of lists of cubic beziers [ctrl p1, ctrl p2, endpoint] # where the start-point is the last point in the previous segment for sp in p: path = [] subdivideCubicPath(sp, smoothness) for csp in sp: path.append([csp[1][0], csp[1][1]]) yield path
def effect(self): paths = [] for id, node in self.selected.iteritems(): if node.tag == '{http://www.w3.org/2000/svg}path': paths.append(node) if len(paths) == 2: break else: sys.stderr.write('Need 2 paths selected\n') return pts = [cubicsuperpath.parsePath(paths[i].get('d')) for i in (0, 1)] for i in (0, 1): if 'transform' in paths[i].keys(): trans = paths[i].get('transform') trans = simpletransform.parseTransform(trans) simpletransform.applyTransformToPath(trans, pts[i]) verts = [] for i in range(0, min(map(len, pts))): comp = [] for j in range(0, min(len(pts[0][i]), len(pts[1][i]))): comp.append([pts[0][i][j][1][-2:], pts[1][i][j][1][-2:]]) verts.append(comp) if self.options.mode.lower() == 'lines': line = [] for comp in verts: for n, v in enumerate(comp): line += [('M', v[0])] line += [('L', v[1])] ele = inkex.etree.Element('{http://www.w3.org/2000/svg}path') paths[0].xpath('..')[0].append(ele) ele.set('d', simplepath.formatPath(line)) ele.set( 'style', 'fill:none;stroke:#000000;stroke-opacity:1;stroke-width:1;') elif self.options.mode.lower() == 'polygons': g = inkex.etree.Element('{http://www.w3.org/2000/svg}g') g.set( 'style', 'fill:#000000;stroke:#000000;fill-opacity:0.3;stroke-width:2;stroke-opacity:0.6;' ) paths[0].xpath('..')[0].append(g) for comp in verts: for n, v in enumerate(comp): nn = n + 1 if nn == len(comp): nn = 0 line = [] line += [('M', comp[n][0])] line += [('L', comp[n][1])] line += [('L', comp[nn][1])] line += [('L', comp[nn][0])] line += [('L', comp[n][0])] ele = inkex.etree.Element( '{http://www.w3.org/2000/svg}path') g.append(ele) ele.set('d', simplepath.formatPath(line))
def effect(self): if len(self.options.ids) < 2: inkex.errormsg(_("This extension requires two selected paths. \nThe second path must be exactly four nodes long.")) exit() #obj is selected second scale = self.unittouu('1px') # convert to document units obj = self.selected[self.options.ids[0]] trafo = self.selected[self.options.ids[1]] if obj.get(inkex.addNS('type','sodipodi')): inkex.errormsg(_("The first selected object is of type '%s'.\nTry using the procedure Path->Object to Path." % obj.get(inkex.addNS('type','sodipodi')))) exit() if obj.tag == inkex.addNS('path','svg') or obj.tag == inkex.addNS('g','svg'): if trafo.tag == inkex.addNS('path','svg'): #distil trafo into four node points mat = simpletransform.composeParents(trafo, [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]]) trafo = cubicsuperpath.parsePath(trafo.get('d')) if len(trafo[0]) < 4: inkex.errormsg(_("This extension requires that the second selected path be four nodes long.")) exit() simpletransform.applyTransformToPath(mat, trafo) trafo = [[Point(csp[1][0],csp[1][1]) for csp in subs] for subs in trafo][0][:4] #vectors pointing away from the trafo origin self.t1 = Segment(trafo[0],trafo[1]) self.t2 = Segment(trafo[1],trafo[2]) self.t3 = Segment(trafo[3],trafo[2]) self.t4 = Segment(trafo[0],trafo[3]) #query inkscape about the bounding box of obj self.q = {'x':0,'y':0,'width':0,'height':0} file = self.args[-1] id = self.options.ids[0] for query in self.q.keys(): if bsubprocess: p = Popen('inkscape --query-%s --query-id=%s "%s"' % (query,id,file), shell=True, stdout=PIPE, stderr=PIPE) rc = p.wait() self.q[query] = scale*float(p.stdout.read()) err = p.stderr.read() else: f,err = os.popen3('inkscape --query-%s --query-id=%s "%s"' % (query,id,file))[1:] self.q[query] = scale*float(f.read()) f.close() err.close() if obj.tag == inkex.addNS("path",'svg'): self.process_path(obj) if obj.tag == inkex.addNS("g",'svg'): self.process_group(obj) else: if trafo.tag == inkex.addNS('g','svg'): inkex.errormsg(_("The second selected object is a group, not a path.\nTry using the procedure Object->Ungroup.")) else: inkex.errormsg(_("The second selected object is not a path.\nTry using the procedure Path->Object to Path.")) exit() else: inkex.errormsg(_("The first selected object is not a path.\nTry using the procedure Path->Object to Path.")) exit()
def process_shape(self, node, mat): rgb = (0, 0, 0) # stroke color fillcolor = None # fill color stroke = 1 # pen width in printer pixels # Very NB : If the pen width is greater than 1 then the output will Not be a vector output ! style = node.get('style') if style: style = simplestyle.parseStyle(style) if style.has_key('stroke'): if style['stroke'] and style['stroke'] != 'none' and style[ 'stroke'][0:3] != 'url': rgb = simplestyle.parseColor(style['stroke']) if style.has_key('stroke-width'): stroke = self.unittouu( style['stroke-width']) / self.unittouu('1px') stroke = int(stroke * self.scale) if style.has_key('fill'): if style['fill'] and style['fill'] != 'none' and style['fill'][ 0:3] != 'url': fill = simplestyle.parseColor(style['fill']) fillcolor = fill[0] + 256 * fill[1] + 256 * 256 * fill[2] color = rgb[0] + 256 * rgb[1] + 256 * 256 * rgb[2] if node.tag == inkex.addNS('path', 'svg'): d = node.get('d') if not d: return p = cubicsuperpath.parsePath(d) elif node.tag == inkex.addNS('rect', 'svg'): x = float(node.get('x')) y = float(node.get('y')) width = float(node.get('width')) height = float(node.get('height')) p = [[[x, y], [x, y], [x, y]]] p.append([[x + width, y], [x + width, y], [x + width, y]]) p.append([[x + width, y + height], [x + width, y + height], [x + width, y + height]]) p.append([[x, y + height], [x, y + height], [x, y + height]]) p.append([[x, y], [x, y], [x, y]]) p = [p] else: return trans = node.get('transform') if trans: mat = simpletransform.composeTransform( mat, simpletransform.parseTransform(trans)) simpletransform.applyTransformToPath(mat, p) hPen = mygdi.CreatePen(0, stroke, color) mygdi.SelectObject(self.hDC, hPen) self.emit_path(p) if fillcolor is not None: brush = LOGBRUSH(0, fillcolor, 0) hBrush = mygdi.CreateBrushIndirect(addressof(brush)) mygdi.SelectObject(self.hDC, hBrush) mygdi.BeginPath(self.hDC) self.emit_path(p) mygdi.EndPath(self.hDC) mygdi.FillPath(self.hDC) return
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): # if self.options.mformat == '"presets"': # self.setPreset() # njj: hack some options # get number of digits # prec = int(self.options.precision) prec = 2 self.options.fontsize = 20 self.options.unit = 'mm' self.options.scale = 1 self.options.angle = 0 self.options.offset = -6 scale = self.unittouu('1px') # convert to document units # self.options.offset *= scale factor = 1.0 doc = self.document.getroot() if doc.get('viewBox'): (viewx, viewy, vieww, viewh) = re.sub(' +|, +|,', ' ', doc.get('viewBox')).strip().split(' ', 4) factor = self.unittouu(doc.get('width')) / float(vieww) if self.unittouu(doc.get('height')) / float(viewh) < factor: factor = self.unittouu(doc.get('height')) / float(viewh) factor /= self.unittouu('1px') self.options.fontsize /= factor factor *= scale / self.unittouu('1' + self.options.unit) # Gather paths debug(dir(self)) paths = self.document.findall('.//{0}'.format(nspath)) # Act on paths for node in paths: mat = simpletransform.composeParents( node, [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]]) p = cubicsuperpath.parsePath(node.get('d')) simpletransform.applyTransformToPath(mat, p) stotal = abs(measure.csparea(p) * factor * self.options.scale) self.group = inkex.etree.SubElement(node.getparent(), inkex.addNS('text', 'svg')) # Format the area as string resultstr = locale.format( "%(len)25." + str(prec) + "f", { 'len': round(stotal * factor * self.options.scale, prec) }).strip() # Fixed text, in the center of each path bbox = simpletransform.computeBBox([node]) tx = bbox[0] + (bbox[1] - bbox[0]) / 2.0 ty = bbox[2] + (bbox[3] - bbox[2]) / 2.0 anchor = 'middle' self.addTextWithTspan( self.group, tx, ty, resultstr + ' ' + self.options.unit + '^2', id, anchor, -int(self.options.angle), -self.options.offset + self.options.fontsize / 2)
def effect(self): """ This method is called first, and sets up the self.commands list for later output. """ svg = self.document.getroot() # find document width and height, used to scale down self.doc_width = inkex.unittouu(svg.get('width')) self.doc_height = inkex.unittouu(svg.get('height')) # add header self.commands.append("^DF;") self.commands.append("! 1;") self.commands.append("H;") self.commands.append("@ %d %d;" % (self.options.z_down, self.options.z_up)) self.commands.append("V {0};F {0};\n".format(self.options.feed_rate_moving)) self.commands.append("Z 0 0 %d;" % self.options.z_up) # mostly borrowed from hgpl_output.py lastX = 0 lastY = 0 # find paths in layers i = 0 layerPath = '//svg:g[@inkscape:groupmode="layer"]' for layer in svg.xpath(layerPath, namespaces=inkex.NSS): i += 1 nodePath = ('//svg:g[@inkscape:groupmode="layer"][%d]/descendant::svg:path') % i for node in svg.xpath(nodePath, namespaces=inkex.NSS): # these next lines added from this patch to fix the transformation issues - http://launchpadlibrarian.net/36269154/hpgl_output.py.patch # possibly also want to try this code: https://bugs.launchpad.net/inkscape/+bug/600472/+attachment/1475310/+files/hpgl_output.py transforms = node.xpath("./ancestor-or-self::svg:*[@transform]",namespaces=inkex.NSS) matrix = [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]] for parenttransform in transforms: newmatrix = simpletransform.parseTransform(parenttransform.get("transform")) matrix = simpletransform.composeTransform(matrix, newmatrix) d = node.get('d') if len(simplepath.parsePath(d)): p = cubicsuperpath.parsePath(d) simpletransform.applyTransformToPath(matrix, p) # this line is also from the transform-fixing patch mentioned above cspsubdiv.cspsubdiv(p, self.options.flat) for sp in p: first = True for csp in sp: if first: x, y = self.conv_coords(csp[1][0], self.doc_height - csp[1][1]) self.commands.append("Z %d %d %d;" % (x, y, self.options.z_up)) self.commands.append("V {0};F {0};".format(self.options.feed_rate_cutting)) first = False x, y = self.conv_coords(csp[1][0], self.doc_height - csp[1][1]) self.commands.append("Z %d %d %d;" % (x, y, self.options.z_down)) lastX = x lastY = y self.commands.append("V {0};F {0};".format(self.options.feed_rate_moving)) self.commands.append("Z %d %d %d;" % (lastX, lastY, self.options.z_up)) self.commands.append("Z 0 0 %d;" % self.options.z_up) self.commands.append("H;")
def process_shape(self, node, mat): rgb = (0, 0, 0) style = node.get('style') if style: style = simplestyle.parseStyle(style) if style.has_key('stroke'): if style['stroke'] and style['stroke'] != 'none' and style[ 'stroke'][0:3] != 'url': rgb = simplestyle.parseColor(style['stroke']) hsl = coloreffect.ColorEffect.rgb_to_hsl(coloreffect.ColorEffect(), rgb[0] / 255.0, rgb[1] / 255.0, rgb[2] / 255.0) self.closed = 0 # only for LWPOLYLINE self.color = 7 # default is black if hsl[2]: self.color = 1 + (int(6 * hsl[0] + 0.5) % 6) # use 6 hues if node.tag == inkex.addNS('path', 'svg'): d = node.get('d') if not d: return if (d[-1] == 'z' or d[-1] == 'Z'): self.closed = 1 p = cubicsuperpath.parsePath(d) elif node.tag == inkex.addNS('rect', 'svg'): self.closed = 1 x = float(node.get('x')) y = float(node.get('y')) width = float(node.get('width')) height = float(node.get('height')) p = [[[x, y], [x, y], [x, y]]] p.append([[x + width, y], [x + width, y], [x + width, y]]) p.append([[x + width, y + height], [x + width, y + height], [x + width, y + height]]) p.append([[x, y + height], [x, y + height], [x, y + height]]) p.append([[x, y], [x, y], [x, y]]) p = [p] else: return trans = node.get('transform') if trans: mat = simpletransform.composeTransform( mat, simpletransform.parseTransform(trans)) simpletransform.applyTransformToPath(mat, p) for sub in p: for i in range(len(sub) - 1): s = sub[i] e = sub[i + 1] if s[1] == s[2] and e[0] == e[1]: if (self.options.POLY == 'true'): self.LWPOLY_line([s[1], e[1]]) else: self.dxf_line([s[1], e[1]]) elif (self.options.ROBO == 'true'): self.ROBO_spline([s[1], s[2], e[0], e[1]]) else: self.dxf_spline([s[1], s[2], e[0], e[1]])
def load(self, node, mat): self.id = node.get('id') d = node.get('d') if len(simplepath.parsePath(d)) == 0: return p = cubicsuperpath.parsePath(d) applyTransformToPath(mat, p) # p is now a list of lists of cubic beziers [ctrl p1, ctrl p2, endpoint] # where the start-point is the last point in the previous segment self.points = [] self.paths = [] for sp in p: path = [] subdivide_cubic_path(sp, 0.2) # TODO: smoothness preference for csp in sp: point = [csp[1][0], csp[1][1]] if point not in self.points: self.points.append(point) path.append(self.points.index(point)) self.paths.append(path) # get color style = node.get('style') try: hexcolor = re.search('fill:#([0-9a-fA-f]{6})', style).group(1) rgb = [ int(hexcolor[0:2], 16), int(hexcolor[2:4], 16), int(hexcolor[4:6], 16) ] except (TypeError, AttributeError): rgb = None # get optional opacity try: opacity = float(re.search('(?:^|;)opacity:([0-9]*\.?[0-9]+)', style).group(1)) except (TypeError, AttributeError): opacity = 1.0 # get optional fill-opacity (of course there is more than one way to set opacity of paths...) try: fill_opacity = float(re.search('(?:^|;)fill-opacity:([0-9]*\.?[0-9]+)', style).group(1)) except (TypeError, AttributeError): fill_opacity = 1.0 if rgb: self.color = [ rgb[0] / 255.0, rgb[1] / 255.0, rgb[2] / 255.0, opacity * fill_opacity ] else: self.color = None
def effect(self): paths = [] for id, node in self.selected.iteritems(): if node.tag == '{http://www.w3.org/2000/svg}path': paths.append(node) if len(paths) == 2: break else: sys.stderr.write('Need 2 paths selected\n') return pts = [cubicsuperpath.parsePath(paths[i].get('d')) for i in (0,1)] for i in (0,1): if 'transform' in paths[i].keys(): trans = paths[i].get('transform') trans = simpletransform.parseTransform(trans) simpletransform.applyTransformToPath(trans, pts[i]) verts = [] for i in range(0, min(map(len, pts))): comp = [] for j in range(0, min(len(pts[0][i]), len(pts[1][i]))): comp.append([pts[0][i][j][1][-2:], pts[1][i][j][1][-2:]]) verts.append(comp) if self.options.mode.lower() == 'lines': line = [] for comp in verts: for n,v in enumerate(comp): line += [('M', v[0])] line += [('L', v[1])] ele = inkex.etree.Element('{http://www.w3.org/2000/svg}path') paths[0].xpath('..')[0].append(ele) ele.set('d', simplepath.formatPath(line)) ele.set('style', 'fill:none;stroke:#000000;stroke-opacity:1;stroke-width:1;') elif self.options.mode.lower() == 'polygons': g = inkex.etree.Element('{http://www.w3.org/2000/svg}g') g.set('style', 'fill:#000000;stroke:#000000;fill-opacity:0.3;stroke-width:2;stroke-opacity:0.6;') paths[0].xpath('..')[0].append(g) for comp in verts: for n,v in enumerate(comp): nn = n+1 if nn == len(comp): nn = 0 line = [] line += [('M', comp[n][0])] line += [('L', comp[n][1])] line += [('L', comp[nn][1])] line += [('L', comp[nn][0])] line += [('L', comp[n][0])] ele = inkex.etree.Element('{http://www.w3.org/2000/svg}path') g.append(ele) ele.set('d', simplepath.formatPath(line))
def effect(self): for id, node in self.selected.iteritems(): if node.tag == '{http://www.w3.org/2000/svg}path': path=node break else: sys.stderr.write('Need one path selected\n') return pts = cubicsuperpath.parsePath(path.get('d')) if 'transform' in path.keys(): trans = path.get('transform') trans = simpletransform.parseTransform(trans) simpletransform.applyTransformToPath(trans, pts[i]) gtext = inkex.etree.SubElement(path.xpath('..')[0], inkex.addNS('g','svg'), {} ) gdots = inkex.etree.SubElement(path.xpath('..')[0], inkex.addNS('g','svg'), {} ) size = 10 if 'style' in path.attrib: style=path.get('style') if style!='': styles=style.split(';') for i in range(len(styles)): if styles[i].startswith('stroke-width'): if ( styles[i].endswith('px') or styles[i].endswith('cm') ) : size=float(styles[i][len('stroke-width:'):-2])*2 else: size=float(styles[i][len('stroke-width:'):])*2 # if len(pts[0])>2: # size = math.sqrt(math.pow(pts[0][0][0][0]-pts[0][1][0][0],2)+math.pow(pts[0][0][0][1]-pts[0][1][0][1],2))/10 it = 0 for sub in pts: for p in sub: if it == 0: style = { 'stroke': 'none', 'fill': 'black' } x0 = p[0][0] y0 = p[0][1] circ_attribs = {'id':'pt0','style':simplestyle.formatStyle(style),'cx':str(x0), 'cy':str(y0),'r':str(size)} circle = inkex.etree.SubElement(gdots, inkex.addNS('circle','svg'), circ_attribs ) else: clone_attribs = { 'x':'0', 'y':'0', 'transform':'translate(%f,%f)' % (p[0][0]-x0,p[0][1]-y0) } clone = inkex.etree.SubElement(gdots, inkex.addNS('use','svg'), clone_attribs ) xlink = inkex.addNS('href','xlink') clone.set(xlink, '#pt0') text_style = { 'font-size':'%dpx' % (size*2.4), 'fill':'black', 'font-family':'DejaVu Sans', 'text-anchor':'middle' } text_attribs = { 'x':str(p[0][0]), 'y':str(p[0][1]-size*1.8), 'style':simplestyle.formatStyle(text_style) } text = inkex.etree.SubElement(gtext, inkex.addNS('text','svg'), text_attribs) tspan = inkex.etree.SubElement(text , 'tspan', {inkex.addNS('role','sodipodi'): 'line'}) tspan.text = '%d' % ( it+1 ) it+=1
def effect(self): paths = [] for id, node in self.selected.iteritems(): if node.tag == "{http://www.w3.org/2000/svg}path": paths.append(node) if len(paths) == 2: break else: sys.stderr.write("Need 2 paths selected\n") return pts = [cubicsuperpath.parsePath(paths[i].get("d")) for i in (0, 1)] for i in (0, 1): if "transform" in paths[i].keys(): trans = paths[i].get("transform") trans = simpletransform.parseTransform(trans) simpletransform.applyTransformToPath(trans, pts[i]) verts = [] for i in range(0, min(map(len, pts))): comp = [] for j in range(0, min(len(pts[0][i]), len(pts[1][i]))): comp.append([pts[0][i][j][1][-2:], pts[1][i][j][1][-2:]]) verts.append(comp) if self.options.mode.lower() == "lines": line = [] for comp in verts: for n, v in enumerate(comp): line += [("M", v[0])] line += [("L", v[1])] ele = inkex.etree.Element("{http://www.w3.org/2000/svg}path") paths[0].xpath("..")[0].append(ele) ele.set("d", simplepath.formatPath(line)) ele.set("style", "fill:none;stroke:#000000;stroke-opacity:1;stroke-width:1;") elif self.options.mode.lower() == "polygons": g = inkex.etree.Element("{http://www.w3.org/2000/svg}g") g.set("style", "fill:#000000;stroke:#000000;fill-opacity:0.3;stroke-width:2;stroke-opacity:0.6;") paths[0].xpath("..")[0].append(g) for comp in verts: for n, v in enumerate(comp): nn = n + 1 if nn == len(comp): nn = 0 line = [] line += [("M", comp[n][0])] line += [("L", comp[n][1])] line += [("L", comp[nn][1])] line += [("L", comp[nn][0])] line += [("L", comp[n][0])] ele = inkex.etree.Element("{http://www.w3.org/2000/svg}path") g.append(ele) ele.set("d", simplepath.formatPath(line))
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 process_shape(self, node, mat): rgb = (0,0,0) # stroke color fillcolor = None # fill color stroke = 1 # pen width in printer pixels # Very NB : If the pen width is greater than 1 then the output will Not be a vector output ! style = node.get('style') if style: style = simplestyle.parseStyle(style) if style.has_key('stroke'): if style['stroke'] and style['stroke'] != 'none' and style['stroke'][0:3] != 'url': rgb = simplestyle.parseColor(style['stroke']) if style.has_key('stroke-width'): stroke = self.unittouu(style['stroke-width']) stroke = int(stroke*self.scale) if style.has_key('fill'): if style['fill'] and style['fill'] != 'none' and style['fill'][0:3] != 'url': fill = simplestyle.parseColor(style['fill']) fillcolor = fill[0] + 256*fill[1] + 256*256*fill[2] color = rgb[0] + 256*rgb[1] + 256*256*rgb[2] if node.tag == inkex.addNS('path','svg'): d = node.get('d') if not d: return p = cubicsuperpath.parsePath(d) elif node.tag == inkex.addNS('rect','svg'): x = float(node.get('x')) y = float(node.get('y')) width = float(node.get('width')) height = float(node.get('height')) p = [[[x, y],[x, y],[x, y]]] p.append([[x + width, y],[x + width, y],[x + width, y]]) p.append([[x + width, y + height],[x + width, y + height],[x + width, y + height]]) p.append([[x, y + height],[x, y + height],[x, y + height]]) p.append([[x, y],[x, y],[x, y]]) p = [p] else: return trans = node.get('transform') if trans: mat = simpletransform.composeTransform(mat, simpletransform.parseTransform(trans)) simpletransform.applyTransformToPath(mat, p) hPen = mygdi.CreatePen(0, stroke, color) mygdi.SelectObject(self.hDC, hPen) self.emit_path(p) if fillcolor is not None: brush = LOGBRUSH(0, fillcolor, 0) hBrush = mygdi.CreateBrushIndirect(addressof(brush)) mygdi.SelectObject(self.hDC, hBrush) mygdi.BeginPath(self.hDC) self.emit_path(p) mygdi.EndPath(self.hDC) mygdi.FillPath(self.hDC) return
def process_shape(self, node, mat): rgb = (0,0,0) style = node.get('style') if style: style = simplestyle.parseStyle(style) if style.has_key('stroke'): if style['stroke'] and style['stroke'] != 'none' and style['stroke'][0:3] != 'url': rgb = simplestyle.parseColor(style['stroke']) hsl = coloreffect.ColorEffect.rgb_to_hsl(coloreffect.ColorEffect(),rgb[0]/255.0,rgb[1]/255.0,rgb[2]/255.0) self.closed = 0 # only for LWPOLYLINE self.color = 7 # default is black if hsl[2]: #self.color = 1 + (int(6*hsl[0] + 0.5) % 6) # use 6 hues self.color = 1 + (int(10*hsl[0] + 0.5) % 10) # use 6 hues if node.tag == inkex.addNS('path','svg'): d = node.get('d') if not d: return if (d[-1] == 'z' or d[-1] == 'Z'): self.closed = 1 p = cubicsuperpath.parsePath(d) elif node.tag == inkex.addNS('rect','svg'): self.closed = 1 x = float(node.get('x')) y = float(node.get('y')) width = float(node.get('width')) height = float(node.get('height')) p = [[[x, y],[x, y],[x, y]]] p.append([[x + width, y],[x + width, y],[x + width, y]]) p.append([[x + width, y + height],[x + width, y + height],[x + width, y + height]]) p.append([[x, y + height],[x, y + height],[x, y + height]]) p.append([[x, y],[x, y],[x, y]]) p = [p] else: return trans = node.get('transform') if trans: mat = simpletransform.composeTransform(mat, simpletransform.parseTransform(trans)) simpletransform.applyTransformToPath(mat, p) for sub in p: for i in range(len(sub)-1): s = sub[i] e = sub[i+1] if s[1] == s[2] and e[0] == e[1]: if (self.options.POLY == 'true'): self.LWPOLY_line([s[1],e[1]]) else: self.dxf_line([s[1],e[1]]) elif (self.options.ROBO == 'true'): self.ROBO_spline([s[1],s[2],e[0],e[1]]) else: self.dxf_spline([s[1],s[2],e[0],e[1]])
def process_shape(self, node, mat): readStrokeWidth = not self.options.ignoreStrokeWidth color = None # stroke color fillcolor = None # fill color stroke = 1 # pen width in printer pixels # Very NB : If the pen width is greater than 1 then the output will Not be a vector output ! node_style = node.get('style') if node_style: style = self.groupstyle.copy() style.update(simplestyle.parseStyle(node_style)) if style.has_key('stroke'): if style['stroke'] and style['stroke'] != 'none' and style[ 'stroke'][0:3] != 'url': rgb = simplestyle.parseColor(style['stroke']) color = rgb[0] + 256 * rgb[1] + 256 * 256 * rgb[2] if readStrokeWidth and style.has_key('stroke-width'): stroke = self.unittouu( style['stroke-width']) / self.unittouu('1px') stroke = int(stroke * self.scale) if style.has_key('fill'): if style['fill'] and style['fill'] != 'none' and style['fill'][ 0:3] != 'url': fill = simplestyle.parseColor(style['fill']) fillcolor = fill[0] + 256 * fill[1] + 256 * 256 * fill[2] if node.tag == inkex.addNS('path', 'svg'): d = node.get('d') if not d: self.not_converted.append(node.get('id')) return p = cubicsuperpath.parsePath(d) elif node.tag == inkex.addNS('rect', 'svg'): x = float(node.get('x')) y = float(node.get('y')) width = float(node.get('width')) height = float(node.get('height')) p = self.printer.rectangle_path(x, y, width, height) elif node.tag == inkex.addNS('defs', 'svg') or node.tag == inkex.addNS( 'metadata', 'svg'): # ignore svg:defs and svg:metadata return elif node.tag.startswith('{' + inkex.NSS['svg']) == False: # ignore non-SVG elements return else: self.not_converted.append(node.get('id')) return trans = node.get('transform') if trans: mat = simpletransform.composeTransform( mat, simpletransform.parseTransform(trans)) simpletransform.applyTransformToPath(mat, p) self.printer.draw_path(p, color, stroke, fillcolor)
def process_path(self,path): mat = simpletransform.composeParents(path, [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]]) d = path.get('d') p = cubicsuperpath.parsePath(d) simpletransform.applyTransformToPath(mat, p) for subs in p: for csp in subs: csp[0] = self.trafopoint(csp[0]) csp[1] = self.trafopoint(csp[1]) csp[2] = self.trafopoint(csp[2]) mat = simpletransform.invertTransform(mat) simpletransform.applyTransformToPath(mat, p) path.set('d',cubicsuperpath.formatPath(p))
def appendPolygonsFromPath(self, d, layer, style): fill = True if style.has_key('fill'): fill = style['fill'] if not fill or fill == 'none': return # ix.debug('appendPolygonsFromPath') path = cubicsuperpath.parsePath(d) cspsubdiv.cspsubdiv(path, self.options.flatness) path = listit(path) simpletransform.applyTransformToPath(self.currentTransform(), path) polygons = constructBridgedPolygonsFromPath(path) for polygon in polygons: self.appendPolygon(polygon, layer)
def processPaths(self, transformMatrix): path = '//svg:path' pm = pathmodifier.PathModifier() for node in self.document.getroot().xpath(path, namespaces=inkex.NSS): pm.objectToPath(node, True) d = node.get('d') p = cubicsuperpath.parsePath(d) t = node.get('transform') if t is not None: transformMatrix = composeTransform(transformMatrix, parseTransform(t)) applyTransformToPath(transformMatrix, p) yield [p, node]
def apply_transforms(path, node): # start with the identity transform transform = [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]] # combine this node's transform with all parent groups' transforms transform = simpletransform.composeParents(node, transform) # add in the transform implied by the viewBox viewbox_transform = get_viewbox_transform(node.getroottree().getroot()) transform = simpletransform.composeTransform(viewbox_transform, transform) # apply the combined transform to this node's path simpletransform.applyTransformToPath(transform, path) return path
def processPath(self, node, mat, pen): # process path path = node.get('d') if path: # parse and transform path path = cubicsuperpath.parsePath(path) simpletransform.applyTransformToPath(mat, path) cspsubdiv.cspsubdiv(path, self.flat) # path to HPGL commands oldPosX = 0.0 oldPosY = 0.0 for singlePath in path: cmd = 'PU' for singlePathPoint in singlePath: posX, posY = singlePathPoint[1] # check if point is repeating, if so, ignore if int(round(posX)) != int(round(oldPosX)) or int( round(posY)) != int(round(oldPosY)): self.processOffset(cmd, posX, posY, pen) cmd = 'PD' oldPosX = posX oldPosY = posY # perform overcut if self.overcut > 0.0 and not self.dryRun: # check if last and first points are the same, otherwise the path is not closed and no overcut can be performed if int(round(oldPosX)) == int(round( singlePath[0][1][0])) and int( round(oldPosY)) == int( round(singlePath[0][1][1])): overcutLength = 0 for singlePathPoint in singlePath: posX, posY = singlePathPoint[1] # check if point is repeating, if so, ignore if int(round(posX)) != int(round(oldPosX)) or int( round(posY)) != int(round(oldPosY)): overcutLength += self.getLength( oldPosX, oldPosY, posX, posY) if overcutLength >= self.overcut: newLength = self.changeLength( oldPosX, oldPosY, posX, posY, -(overcutLength - self.overcut)) self.processOffset(cmd, newLength[0], newLength[1], pen) break else: self.processOffset(cmd, posX, posY, pen) oldPosX = posX oldPosY = posY
def process_path(self, node, mat): d = node.get('d') if d: p = cubicsuperpath.parsePath(d) trans = node.get('transform') if trans: mat = simpletransform.composeTransform(mat, simpletransform.parseTransform(trans)) simpletransform.applyTransformToPath(mat, p) cspsubdiv.cspsubdiv(p, self.options.flat) for sp in p: first = True for csp in sp: cmd = 'PD' if first: cmd = 'PU' first = False self.hpgl.append('%s%d,%d;' % (cmd,csp[1][0],csp[1][1]))
def effect(self): """ Effect behaviour. """ # Get script's "--what" option value. fontsize = str(self.options.fontsize) + 'px' color = self.options.color font = self.options.font fontweight = self.options.fontweight replaced = self.options.replaced replacewith = self.options.replacewith angle = -int(self.options.angle) capitals = self.options.capitals if len(self.selected) == 0: inkex.errormsg(_("Please select some paths first.")) exit() for id, node in self.selected.iteritems(): mat = simpletransform.composeParents( node, [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]]) p = cubicsuperpath.parsePath(node.get('d')) simpletransform.applyTransformToPath(mat, p) self.group = inkex.etree.SubElement(node.getparent(), inkex.addNS('text', 'svg')) tx, ty = cspcofm(p) new = inkex.etree.SubElement( self.group, inkex.addNS('tspan', 'svg'), {inkex.addNS('role', 'sodipodi'): 'line'}) s = { 'text-align': 'center', 'vertical-align': 'bottom', 'text-anchor': 'middle', 'font-size': fontsize, 'font-weight': fontweight, 'font-style': 'normal', 'font-family': font, 'fill': color } new.set('style', simplestyle.formatStyle(s)) if capitals: id = id.upper() new.text = id.replace(replaced, replacewith) self.group.set('x', str(tx)) self.group.set('y', str(ty)) self.group.set('transform', 'rotate(%s, %s, %s)' % (angle, tx, ty))
def parse_path(self): # A CSP is a "cubic superpath". # # A "path" is a sequence of strung-together bezier curves. # # A "superpath" is a collection of paths that are all in one object. # # The "cubic" bit in "cubic superpath" is because the bezier curves # inkscape uses involve cubic polynomials. # # Each path is a collection of tuples, each of the form: # # (control_before, point, control_after) # # A bezier curve segment is defined by an endpoint, a control point, # a second control point, and a final endpoint. A path is a bunch of # bezier curves strung together. One could represent a path as a set # of four-tuples, but there would be redundancy because the ending # point of one bezier is the starting point of the next. Instead, a # path is a set of 3-tuples as shown above, and one must construct # each bezier curve by taking the appropriate endpoints and control # points. Bleh. It should be noted that a straight segment is # represented by having the control point on each end equal to that # end's point. # # In a path, each element in the 3-tuple is itself a tuple of (x, y). # Tuples all the way down. Hasn't anyone heard of using classes? path = self.path # start with the identity transform transform = [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]] # combine this node's transform with all parent groups' transforms transform = simpletransform.composeParents(self.node, transform) # add in the transform implied by the viewBox viewbox_transform = get_viewbox_transform( self.node.getroottree().getroot()) transform = simpletransform.composeTransform(viewbox_transform, transform) # apply the combined transform to this node's path simpletransform.applyTransformToPath(transform, path) return path
def process_shapes(self, shapelist, transform): """Convert the SVG shape elements to Lines and CubicBeziers, and apply object/layer transforms. Returns a list of tuples ((path-id, path), ...). """ cutpath_list = [] for node, layer_transform in shapelist: # Convert the shape element to a simplepath path = supereffect.convert_element_to_path(node) # Convert the simplepath to a 'cubicsuperpath' which is # just a list of cubic bezier curves. # This seems to be how most Inkscape plugins do things... # TODO: This is really an unnecessary step since # we could deal with SVG shapes directly... csp = cubicsuperpath.CubicSuperPath(path) # Apply the SVG element transform and it's layer transform to the # path segments so that we are working in absolute coordinates. # Transform SVG coordinates into cartesian (ie G code) coordinates # (flip the Y axis from upper left to lower left). # node_transform = simpletransform.parseTransform(node.get('transform')) # node_transform = simpletransform.composeTransform(node_transform, transform) # node_transform = simpletransform.composeTransform(node_transform, layer_transform) node_transform = simpletransform.composeTransform(transform, layer_transform) simpletransform.applyTransformToPath(node_transform, csp) # Convert cubic path segments to curves and lines # TODO: get bounding boxes and calculate offset if doc origin not used. cutpath = paths.Path(name=node.get('id')) for subcsp in csp: for i in range(1,len(subcsp)): p1 = geom.P(subcsp[i-1][1]) c1 = geom.P(subcsp[i-1][2]) p2 = geom.P(subcsp[i][0]) c2 = geom.P(subcsp[i][1]) if p1 == c1 and p2 == c2: segment = geom.Line(p1, p2) else: segment = geom.CubicBezier(p1, c1, p2, c2) cutpath.append(segment) cutpath_list.append(cutpath) return cutpath_list
def effect(self): # get number of digits prec = int(self.options.precision) scale = self.unittouu('1px') # convert to document units self.options.offset *= scale factor = 1.0 doc = self.document.getroot() if doc.get('viewBox'): [viewx, viewy, vieww, viewh] = doc.get('viewBox').split(' ') factor = self.unittouu(doc.get('width'))/float(vieww) if self.unittouu(doc.get('height'))/float(viewh) < factor: factor = self.unittouu(doc.get('height'))/float(viewh) factor /= self.unittouu('1px') self.options.fontsize /= factor # loop over all selected paths for id, node in self.selected.iteritems(): if node.tag == inkex.addNS('path','svg'): mat = simpletransform.composeParents(node, [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]]) p = cubicsuperpath.parsePath(node.get('d')) simpletransform.applyTransformToPath(mat, p) factor *= scale/self.unittouu('1'+self.options.unit) if self.options.type == "length": slengths, stotal = csplength(p) self.group = inkex.etree.SubElement(node.getparent(),inkex.addNS('text','svg')) elif self.options.type == "area": stotal = csparea(p)*factor*self.options.scale self.group = inkex.etree.SubElement(node.getparent(),inkex.addNS('text','svg')) else: xc, yc = cspcofm(p) self.group = inkex.etree.SubElement(node.getparent(),inkex.addNS('path','svg')) self.group.set('id', 'MassCenter_' + node.get('id')) self.addCross(self.group, xc, yc, scale) continue # Format the length as string lenstr = locale.format("%(len)25."+str(prec)+"f",{'len':round(stotal*factor*self.options.scale,prec)}).strip() if self.options.format == 'textonpath': if self.options.type == "length": self.addTextOnPath(self.group, 0, 0, lenstr+' '+self.options.unit, id, 'start', '50%', self.options.offset) else: self.addTextOnPath(self.group, 0, 0, lenstr+' '+self.options.unit+'^2', id, 'start', '0%', self.options.offset) else: if self.options.type == "length": self.addTextWithTspan(self.group, p[0][0][1][0], p[0][0][1][1], lenstr+' '+self.options.unit, id, 'start', -int(self.options.angle), self.options.offset + self.options.fontsize/2) else: self.addTextWithTspan(self.group, p[0][0][1][0], p[0][0][1][1], lenstr+' '+self.options.unit+'^2', id, 'start', -int(self.options.angle), -self.options.offset + self.options.fontsize/2)
def process_path(self, node, mat): d = node.get('d') if d: p = cubicsuperpath.parsePath(d) trans = node.get('transform') if trans: mat = simpletransform.composeTransform( mat, simpletransform.parseTransform(trans)) simpletransform.applyTransformToPath(mat, p) cspsubdiv.cspsubdiv(p, self.options.flat) for sp in p: first = True for csp in sp: cmd = 'PD' if first: cmd = 'PU' first = False self.hpgl.append('%s%d,%d;' % (cmd, csp[1][0], csp[1][1]))
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 plotPath(self, node, matTransform): """ Plot the path while applying the transformation defined by the matrix matTransform. """ filledPath = (simplestyle.parseStyle(node.get("style")).get("fill", "none") != "none") # Plan: Turn this path into a cubicsuperpath (list of beziers)... d = node.get("d") if len(simplepath.parsePath(d)) == 0: return p = cubicsuperpath.parsePath(d) # ... and apply the transformation to each point. simpletransform.applyTransformToPath(matTransform, p) # p is now a list of lists of cubic beziers [cp1, cp2, endp] # where the start-point is the last point of the previous segment. # For some reason the inkscape extensions uses csp[1] as the coordinates of each point, # but that makes it seem like they are using control point 2 as line points. # Maybe that is a side-effect of the CSP subdivion process? TODO realPoints = [] for sp in p: cspsubdiv.subdiv(sp, self.smoothness) for i in range(1, len(p)): pStart = [p[i][0][1][0], p[i][0][1][1]] pClose = [] pDist = -1 for k in range(len(p)): if (k == i): continue for j in range(len(p[k])): csp = p[k][j]; tDist = self.getDist(pStart, [csp[1][0], csp[1][1]]) if (pDist < 0) or (tDist < pDist): pDist = tDist pClose = [k, j] if pClose: #print "close " + repr(i) + " " + repr(pClose) + " " + repr(p[pClose[0]][pClose[1]]) p[pClose[0]][pClose[1]].append(i) self.genPoints(p, p[0], realPoints) self.rawObjects.append( (filledPath, realPoints) )
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_
def point_generator(path, mat, flatness): if len(simplepath.parsePath(path)) == 0: return simple_path = simplepath.parsePath(path) startX,startY = float(simple_path[0][1][0]), float(simple_path[0][1][1]) yield startX, startY p = cubicsuperpath.parsePath(path) if mat: simpletransform.applyTransformToPath(mat, p) for sp in p: cspsubdiv.subdiv( sp, flatness) for csp in sp: ctrl_pt1 = csp[0] ctrl_pt2 = csp[1] end_pt = csp[2] yield end_pt[0], end_pt[1],
def processPath(self, node, mat): # process path paths = node.get('d') if paths: # parse and transform path paths = cubicsuperpath.parsePath(paths) simpletransform.applyTransformToPath(mat, paths) cspsubdiv.cspsubdiv(paths, self.options.flat) # path to HPGL commands oldPosX = 0.0 oldPosY = 0.0 # TODO: Plot smallest parts first to avid plotter dragging parts of foil around (on text) for singlePath in paths: cmd = 'PU' for singlePathPoint in singlePath: posX, posY = singlePathPoint[1] # check if point is repeating, if so, ignore if int(round(posX)) != int(round(oldPosX)) or int(round(posY)) != int(round(oldPosY)): self.processOffset(cmd, posX, posY) cmd = 'PD' oldPosX = posX oldPosY = posY # perform overcut if self.options.useOvercut and not self.dryRun: # check if last and first points are the same, otherwise the path is not closed and no overcut can be performed if int(round(oldPosX)) == int(round(singlePath[0][1][0])) and int(round(oldPosY)) == int(round(singlePath[0][1][1])): overcutLength = 0 for singlePathPoint in singlePath: posX, posY = singlePathPoint[1] # check if point is repeating, if so, ignore if int(round(posX)) != int(round(oldPosX)) or int(round(posY)) != int(round(oldPosY)): overcutLength += self.getLength(oldPosX, oldPosY, posX, posY) if overcutLength >= self.options.overcut: newLength = self.changeLength(oldPosX, oldPosY, posX, posY, - (overcutLength - self.options.overcut)) self.processOffset(cmd, newLength[0], newLength[1]) break else: self.processOffset(cmd, posX, posY) oldPosX = posX oldPosY = posY
def processPath(self, node, mat, pen): # process path path = node.get('d') if path: # parse and transform path path = cubicsuperpath.parsePath(path) simpletransform.applyTransformToPath(mat, path) cspsubdiv.cspsubdiv(path, self.flat) # path to HPGL commands oldPosX = 0.0 oldPosY = 0.0 for singlePath in path: cmd = 'PU' for singlePathPoint in singlePath: posX, posY = singlePathPoint[1] # check if point is repeating, if so, ignore if int(round(posX)) != int(round(oldPosX)) or int(round(posY)) != int(round(oldPosY)): self.processOffset(cmd, posX, posY, pen) cmd = 'PD' oldPosX = posX oldPosY = posY # perform overcut if self.overcut > 0.0 and not self.dryRun: # check if last and first points are the same, otherwise the path is not closed and no overcut can be performed if int(round(oldPosX)) == int(round(singlePath[0][1][0])) and int(round(oldPosY)) == int(round(singlePath[0][1][1])): overcutLength = 0 for singlePathPoint in singlePath: posX, posY = singlePathPoint[1] # check if point is repeating, if so, ignore if int(round(posX)) != int(round(oldPosX)) or int(round(posY)) != int(round(oldPosY)): overcutLength += self.getLength(oldPosX, oldPosY, posX, posY) if overcutLength >= self.overcut: newLength = self.changeLength(oldPosX, oldPosY, posX, posY, - (overcutLength - self.overcut)) self.processOffset(cmd, newLength[0], newLength[1], pen) break else: self.processOffset(cmd, posX, posY, pen) oldPosX = posX oldPosY = posY
def parsePath(self, node, transforms, names): name = "" for n in names: name = n + "_" + name name = name + node.get("id") m2 = [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]] for t in transforms: m = simpletransform.parseTransform(t) m2 = simpletransform.composeTransform(m2, m) m = simpletransform.parseTransform(node.get("transform")) m2 = simpletransform.composeTransform(m2, m) color = self.get_color(node) path = simplepath.formatPath(simplepath.parsePath(node.get('d'))) subpaths = path.split('M') for i in range(1, len(subpaths)): subpaths[i] = 'M ' + rstrip(subpaths[i]) closed = subpaths[i][-1] in ['Z', 'z'] csp = cubicsuperpath.parsePath(subpaths[i]) simpletransform.applyTransformToPath(m2, csp) if closed: self.closed2curves(csp) else: self.opened2curves(csp) vertices = self.cast2spine(csp, closed) if len(vertices) >= 9 and closed or len(vertices) >= 6 and not closed: self.path2json(name + "_" + str(i), closed, color, vertices) else: inkex.debug("skipping " + name + "_" + str(i) + ": vertex count < 6 (" + str(len(vertices)) + ")")
def process_path(self, node, mat): rgb = (0, 0, 0) style = node.get('style') if style: style = simplestyle.parseStyle(style) if style.has_key('stroke'): if style['stroke'] and style['stroke'] != 'none': rgb = simplestyle.parseColor(style['stroke']) hsl = coloreffect.ColorEffect.rgb_to_hsl(coloreffect.ColorEffect(), rgb[0] / 255.0, rgb[1] / 255.0, rgb[2] / 255.0) self.color = 7 # default is black if hsl[2]: self.color = 1 + (int(6 * hsl[0] + 0.5) % 6) # use 6 hues d = node.get('d') if d: p = cubicsuperpath.parsePath(d) trans = node.get('transform') if trans: mat = simpletransform.composeTransform( mat, simpletransform.parseTransform(trans)) simpletransform.applyTransformToPath(mat, p) for sub in p: for i in range(len(sub) - 1): s = sub[i] e = sub[i + 1] if s[1] == s[2] and e[0] == e[1]: if (self.options.POLY == 'true'): self.LWPOLY_line([s[1], e[1]]) else: self.dxf_line([s[1], e[1]]) elif (self.options.ROBO == 'true'): self.ROBO_spline([s[1], s[2], e[0], e[1]]) else: self.dxf_spline([s[1], s[2], e[0], e[1]])
def effect(self): # get number of digits prec = int(self.options.precision) factor = 1.0 doc = self.document.getroot() if doc.get('viewBox'): [viewx, viewy, vieww, viewh] = doc.get('viewBox').split(' ') factor = float(doc.get('width'))/float(vieww) if float(doc.get('height'))/float(viewh) < factor: factor = float(doc.get('height'))/float(viewh) self.options.fontsize /= factor # loop over all selected paths for id, node in self.selected.iteritems(): if node.tag == inkex.addNS('path','svg'): self.group = inkex.etree.SubElement(node.getparent(),inkex.addNS('text','svg')) a =[] mat = simpletransform.composeParents(node, [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]]) p = cubicsuperpath.parsePath(node.get('d')) simpletransform.applyTransformToPath(mat, p) factor = factor/inkex.unittouu('1'+self.options.unit) if self.options.type == "length": slengths, stotal = csplength(p) else: stotal = csparea(p)*factor*self.options.scale # Format the length as string lenstr = locale.format("%(len)25."+str(prec)+"f",{'len':round(stotal*factor*self.options.scale,prec)}).strip() if self.options.format == 'textonpath': if self.options.type == "length": self.addTextOnPath(self.group, 0, 0, lenstr+' '+self.options.unit, id, 'start', '50%', self.options.offset) else: self.addTextOnPath(self.group, 0, 0, lenstr+' '+self.options.unit+'^2', id, 'start', '0%', self.options.offset) else: if self.options.type == "length": self.addTextWithTspan(self.group, p[0][0][1][0], p[0][0][1][1], lenstr+' '+self.options.unit, id, 'start', -int(self.options.angle), self.options.offset + self.options.fontsize/2) else: self.addTextWithTspan(self.group, p[0][0][1][0], p[0][0][1][1], lenstr+' '+self.options.unit+'^2', id, 'start', -int(self.options.angle), -self.options.offset + self.options.fontsize/2)
def compile_paths(node, trans): # Apply the object transform, along with the parent transformation mat = node.get('transform', None) if mat: mat = simpletransform.parseTransform(mat) trans = simpletransform.composeTransform(trans, mat) if node.tag == SVG_PATH_TAG: # This is a path object if (not node.get("d")): return [] csp = cubicsuperpath.parsePath(node.get("d")) if (trans): simpletransform.applyTransformToPath(trans, csp) return csp elif node.tag == SVG_GROUP_TAG: # This node is a group of other nodes path = [] for child in node.iterchildren(): path += compile_paths(child, trans) return path logger.write("skipping " + str(node.tag)) self.skipped += 1 return []
def effect(self): # Get number of digits scale = self.unittouu('1px') # Convert to document units factor = 1.0 doc = self.document.getroot() if doc.get('viewBox'): [viewx, viewy, vieww, viewh] = doc.get('viewBox').split(' ') factor = self.unittouu(doc.get('width'))/float(vieww) if self.unittouu(doc.get('height'))/float(viewh) < factor: factor = self.unittouu(doc.get('height'))/float(viewh) factor /= self.unittouu('1px') # Loop over all selected paths obj_lengths = [] obj_ids = [] obj_nodes = [] for id, node in self.selected.iteritems(): if node.tag == inkex.addNS('path','svg'): mat = simpletransform.composeParents(node, [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]]) p = cubicsuperpath.parsePath(node.get('d')) node.set('d', cubicsuperpath.formatPath(p)) simpletransform.applyTransformToPath(mat, p) factor *= scale/self.unittouu('1'+self.options.unit) if self.options.type == "length": slengths, stotal = csplength(p) # Save the path length and segment lengths in the document node.set('pathlength', str(stotal)) tmp = '' for slen in slengths[0][::-1]: tmp += str(slen) + ' ' tmp = tmp[:-1] # Remove the space at the end node.set('segmentlengths', tmp) obj_lengths += [stotal] obj_ids += [id] obj_nodes += [node] # Format the length as string id_min = 0 id_max = 1 # Set bigger and smaller object if obj_lengths[id_min] > obj_lengths[id_max]: id_min = 1 id_max = 0 # Get paths and collect pathlength and segmentlengths obj_lengths = [] obj_ids = [] obj_nodes = [] for id, node in self.selected.iteritems(): if node.tag == inkex.addNS('path','svg'): mat = simpletransform.composeParents(node, [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]]) p = cubicsuperpath.parsePath(node.get('d')) simpletransform.applyTransformToPath(mat, p) node.set('d', cubicsuperpath.formatPath(p)) factor *= scale/self.unittouu('1'+self.options.unit) if self.options.type == "length": slengths, stotal = csplength(p) # Save the path length and segment lengths in the document node.set('pathlength', str(stotal)) tmp = '' for slen in slengths[0][::-1]: tmp += str(slen) + ' ' tmp = tmp[:-1] # Remove the space at the end node.set('segmentlengths', tmp) obj_lengths += [stotal] obj_ids += [id] obj_nodes += [node] # Format the length as string points = [] # Apply the leaves method to both paths addLeaves(obj_nodes[id_min], self.options.pointsL, self.options.offsetL, self.options.slideL, doc) addLeaves(obj_nodes[id_max], self.options.pointsL, self.options.offsetL, self.options.slideL, doc)
def process_shape(self, node, mat, group_stroke = None): ################################# ### Determine the shape type ### ################################# try: i = node.tag.find('}') if i >= 0: tag_type = node.tag[i+1:] except: tag_type="" ############################################## ### Set a unique identifier for each shape ### ############################################## self.id_cnt=self.id_cnt+1 path_id = "ID%d"%(self.id_cnt) sw_flag = False changed = False ####################################### ### Handle references to CSS data ### ####################################### class_val = node.get('class') if class_val: css_data = "" for cv in class_val.split(' '): if css_data!="": css_data = self.CSS_values.get_css_value(tag_type,cv)+";"+css_data else: css_data = self.CSS_values.get_css_value(tag_type,cv) # Remove the reference to the CSS data del node.attrib['class'] # Check if a style entry already exists. If it does # append the the existing style data to the CSS data # otherwise create a new style entry. if node.get('style'): if css_data!="": css_data = css_data + ";" + node.get('style') node.set('style', css_data) else: node.set('style', css_data) style = node.get('style') self.Cut_Type[path_id]="raster" # Set default type to raster text_message_warning = "SVG File with Color Coded Text Outlines Found: (i.e. Blue: engrave/ Red: cut)" line1 = "SVG File with color coded text outlines found (i.e. Blue: engrave/ Red: cut)." line2 = "Automatic conversion to paths failed: Try upgrading to Inkscape .90 or later" line3 = "To convert manually in Inkscape: select the text then select \"Path\"-\"Object to Path\" in the menu bar." text_message_fatal = "%s\n\n%s\n\n%s" %(line1,line2,line3) ############################################## ### Handle 'style' data outside of style ### ############################################## stroke_outside = node.get('stroke') if not stroke_outside: stroke_outside = group_stroke if stroke_outside: stroke_width_outside = node.get('stroke-width') col = stroke_outside col= col.strip() if simplestyle.isColor(col): c=simplestyle.parseColor(col) (new_val,changed,k40_action)=self.colmod(c[0],c[1],c[2],path_id) else: new_val = col if changed: node.set('stroke',new_val) node.set('stroke-width',"0.0") node.set('k40_action', k40_action) sw_flag = True if sw_flag == True: if node.tag == inkex.addNS('text','svg') or node.tag == inkex.addNS('flowRoot','svg'): if (self.txt2paths==False): raise SVG_TEXT_EXCEPTION(text_message_warning) else: raise Exception(text_message_fatal) ################################################### ### Handle 'k40_action' data outside of style ### ################################################### if node.get('k40_action'): k40_action = node.get('k40_action') changed=True self.Cut_Type[path_id]=k40_action ############################################## ### Handle 'style' data ### ############################################## if style: declarations = style.split(';') i_sw = -1 sw_prop = 'stroke-width' for i,decl in enumerate(declarations): parts = decl.split(':', 2) if len(parts) == 2: (prop, col) = parts prop = prop.strip().lower() if prop == 'k40_action': changed = True self.Cut_Type[path_id]=col #if prop in color_props: if prop == sw_prop: i_sw = i if prop == 'stroke': col= col.strip() if simplestyle.isColor(col): c=simplestyle.parseColor(col) (new_val,changed,k40_action)=self.colmod(c[0],c[1],c[2],path_id) else: new_val = col if changed: declarations[i] = prop + ':' + new_val declarations.append('k40_action' + ':' + k40_action) sw_flag = True if sw_flag == True: if node.tag == inkex.addNS('text','svg') or node.tag == inkex.addNS('flowRoot','svg'): if (self.txt2paths==False): raise SVG_TEXT_EXCEPTION(text_message_warning) else: raise Exception(text_message_fatal) if i_sw != -1: declarations[i_sw] = sw_prop + ':' + "0.0" else: declarations.append(sw_prop + ':' + "0.0") node.set('style', ';'.join(declarations)) ############################################## ##################################################### ### If vector data was found save the path data ### ##################################################### if changed: if node.tag == inkex.addNS('path','svg'): d = node.get('d') if not d: return p = cubicsuperpath.parsePath(d) elif node.tag == inkex.addNS('rect','svg'): x = float(node.get('x')) y = float(node.get('y')) width = float(node.get('width')) height = float(node.get('height')) rx = 0.0 ry = 0.0 if node.get('rx'): rx=float(node.get('rx')) if node.get('ry'): ry=float(node.get('ry')) if max(rx,ry) > 0.0: if rx==0.0 or ry==0.0: rx = max(rx,ry) ry = rx Rxmax = abs(width)/2.0 Rymax = abs(height)/2.0 rx = min(rx,Rxmax) ry = min(ry,Rymax) L1 = "M %f,%f %f,%f " %(x+rx , y , x+width-rx , y ) C1 = "A %f,%f 0 0 1 %f,%f" %(rx , ry , x+width , y+ry ) L2 = "M %f,%f %f,%f " %(x+width , y+ry , x+width , y+height-ry) C2 = "A %f,%f 0 0 1 %f,%f" %(rx , ry , x+width-rx , y+height ) L3 = "M %f,%f %f,%f " %(x+width-rx , y+height , x+rx , y+height ) C3 = "A %f,%f 0 0 1 %f,%f" %(rx , ry , x , y+height-ry) L4 = "M %f,%f %f,%f " %(x , y+height-ry, x , y+ry ) C4 = "A %f,%f 0 0 1 %f,%f" %(rx , ry , x+rx , y ) d = L1 + C1 + L2 + C2 + L3 + C3 + L4 + C4 else: d = "M %f,%f %f,%f %f,%f %f,%f Z" %(x,y, x+width,y, x+width,y+height, x,y+height) p = cubicsuperpath.parsePath(d) elif node.tag == inkex.addNS('circle','svg'): cx = float(node.get('cx') ) cy = float(node.get('cy')) r = float(node.get('r')) d = "M %f,%f A %f,%f 0 0 1 %f,%f %f,%f 0 0 1 %f,%f %f,%f 0 0 1 %f,%f %f,%f 0 0 1 %f,%f Z" %(cx+r,cy, r,r,cx,cy+r, r,r,cx-r,cy, r,r,cx,cy-r, r,r,cx+r,cy) p = cubicsuperpath.parsePath(d) elif node.tag == inkex.addNS('ellipse','svg'): cx = float(node.get('cx')) cy = float(node.get('cy')) if node.get('r'): r = float(node.get('r')) rx = r ry = r if node.get('rx'): rx = float(node.get('rx')) if node.get('ry'): ry = float(node.get('ry')) d = "M %f,%f A %f,%f 0 0 1 %f,%f %f,%f 0 0 1 %f,%f %f,%f 0 0 1 %f,%f %f,%f 0 0 1 %f,%f Z" %(cx+rx,cy, rx,ry,cx,cy+ry, rx,ry,cx-rx,cy, rx,ry,cx,cy-ry, rx,ry,cx+rx,cy) p = cubicsuperpath.parsePath(d) elif (node.tag == inkex.addNS('polygon','svg')) or (node.tag == inkex.addNS('polyline','svg')): points = node.get('points') if not points: return points = points.replace(',', ' ') while points.find(' ') > -1: points = points.replace(' ', ' ') points = points.strip().split(" ") d = "M " for i in range(0,len(points),2): x = float(points[i]) y = float(points[i+1]) d = d + "%f,%f " %(x,y) #Close the loop if it is a ploygon if node.tag == inkex.addNS('polygon','svg'): d = d + "Z" p = cubicsuperpath.parsePath(d) elif node.tag == inkex.addNS('line','svg'): x1 = float(node.get('x1')) y1 = float(node.get('y1')) x2 = float(node.get('x2')) y2 = float(node.get('y2')) d = "M " d = "M %f,%f %f,%f" %(x1,y1,x2,y2) p = cubicsuperpath.parsePath(d) else: #print("something was ignored") #print(node.tag) return trans = node.get('transform') if trans: mat = simpletransform.composeTransform(mat, simpletransform.parseTransform(trans)) simpletransform.applyTransformToPath(mat, p) ########################################## ## Break Curves down into small lines ### ########################################## f = self.flatness is_flat = 0 while is_flat < 1: try: cspsubdiv.cspsubdiv(p, f) is_flat = 1 except IndexError: break except: f += 0.1 if f>2 : break #something has gone very wrong. ########################################## rgb=(0,0,0) for sub in p: for i in range(len(sub)-1): x1 = sub[i][1][0] y1 = sub[i][1][1] x2 = sub[i+1][1][0] y2 = sub[i+1][1][1] self.lines.append([x1,y1,x2,y2,rgb,path_id])
def addPathVertices( self, path, node=None, transform=None ): ''' Decompose the path data from an SVG element into individual subpaths, each starting with an absolute move-to (x, y) coordinate followed by one or more absolute line-to (x, y) coordinates. Each subpath is stored as a list of (x, y) coordinates, with the first entry understood to be a move-to coordinate and the rest line-to coordinates. A list is then made of all the subpath lists and then stored in the self.paths dictionary using the path's lxml.etree node pointer as the dictionary key. ''' if ( not path ) or ( len( path ) == 0 ): return # parsePath() may raise an exception. This is okay sp = simplepath.parsePath( path ) if ( not sp ) or ( len( sp ) == 0 ): return # Get a cubic super duper path p = cubicsuperpath.CubicSuperPath( sp ) if ( not p ) or ( len( p ) == 0 ): return # Apply any transformation if transform != None: simpletransform.applyTransformToPath( transform, p ) # Now traverse the simplified path subpaths = [] subpath_vertices = [] for sp in p: # We've started a new subpath # See if there is a prior subpath and whether we should keep it if len( subpath_vertices ): if distanceSquared( subpath_vertices[0], subpath_vertices[-1] ) < 1: # Keep the prior subpath: it appears to be a closed path subpaths.append( subpath_vertices ) subpath_vertices = [] subdivideCubicPath( sp, float( self.options.tolerance / 100 ) ) for csp in sp: # Add this vertex to the list of vetices subpath_vertices.append( csp[1] ) # Handle final subpath if len( subpath_vertices ): if distanceSquared( subpath_vertices[0], subpath_vertices[-1] ) < 1: # Path appears to be closed so let's keep it subpaths.append( subpath_vertices ) # Empty path? if len( subpaths ) == 0: return # And add this path to our dictionary of paths self.paths[node] = subpaths # And save the transform for this element in a dictionary keyed # by the element's lxml node pointer self.transforms[node] = transform
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 = {} for id in self.options.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: self.options.ids.remove(id) for i in range(1,len(self.options.ids)): start = copy.deepcopy(paths[self.options.ids[i-1]]) end = copy.deepcopy(paths[self.options.ids[i]]) sst = copy.deepcopy(styles[self.options.ids[i-1]]) est = copy.deepcopy(styles[self.options.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 getPathVertices(self, path, node=None, transform=None): ''' Decompose the path data from an SVG element into individual subpaths, each subpath consisting of absolute move to and line to coordinates. Place these coordinates into a list of polygon vertices. ''' if (not path) or (len(path) == 0): # Nothing to do return None # parsePath() may raise an exception. This is okay sp = simplepath.parsePath(path) if (not sp) or (len(sp) == 0): # Path must have been devoid of any real content return None # Get a cubic super path p = cubicsuperpath.CubicSuperPath(sp) if (not p) or (len(p) == 0): # Probably never happens, but... return None if transform: simpletransform.applyTransformToPath(transform, p) # Now traverse the cubic super path subpath_list = [] subpath_vertices = [] for sp in p: # We've started a new subpath # See if there is a prior subpath and whether we should keep it if len(subpath_vertices): subpath_list.append( [subpath_vertices, [sp_xmin, sp_xmax, sp_ymin, sp_ymax]]) subpath_vertices = [] subdivideCubicPath(sp, float(self.options.smoothness)) # Note the first point of the subpath first_point = sp[0][1] subpath_vertices.append(first_point) sp_xmin = first_point[0] sp_xmax = first_point[0] sp_ymin = first_point[1] sp_ymax = first_point[1] # See if the first and last points are identical # OpenSCAD doesn't mind if we duplicate the first and last # vertex, but our polygon in polygon algorithm may n = len(sp) last_point = sp[n - 1][1] if first_point[0] == last_point[0] and \ first_point[1] == last_point[1]: n = n - 1 # Traverse each point of the subpath for csp in sp[1:n]: # Append the vertex to our list of vertices pt = csp[1] subpath_vertices.append(pt) # Track the bounding box of this subpath if pt[0] < sp_xmin: sp_xmin = pt[0] elif pt[0] > sp_xmax: sp_xmax = pt[0] if pt[1] < sp_ymin: sp_ymin = pt[1] elif pt[1] > sp_ymax: sp_ymax = pt[1] # Track the bounding box of the overall drawing # This is used for centering the polygons in OpenSCAD around the # (x,y) origin if sp_xmin < self.xmin: self.xmin = sp_xmin if sp_xmax > self.xmax: self.xmax = sp_xmax if sp_ymin < self.ymin: self.ymin = sp_ymin if sp_ymax > self.ymax: self.ymax = sp_ymax # Handle the final subpath if len(subpath_vertices): subpath_list.append( [subpath_vertices, [sp_xmin, sp_xmax, sp_ymin, sp_ymax]]) if len(subpath_list) > 0: self.paths[node] = subpath_list
def effect(self): if self.options.mformat == '"presets"': self.setPreset() # get number of digits prec = int(self.options.precision) scale = self.unittouu('1px') # convert to document units self.options.offset *= scale factor = 1.0 doc = self.document.getroot() if doc.get('viewBox'): [viewx, viewy, vieww, viewh] = doc.get('viewBox').split(' ') factor = self.unittouu(doc.get('width'))/float(vieww) if self.unittouu(doc.get('height'))/float(viewh) < factor: factor = self.unittouu(doc.get('height'))/float(viewh) factor /= self.unittouu('1px') self.options.fontsize /= factor factor *= scale/self.unittouu('1'+self.options.unit) # loop over all selected paths for id, node in self.selected.iteritems(): if node.tag == inkex.addNS('path','svg'): mat = simpletransform.composeParents(node, [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]]) p = cubicsuperpath.parsePath(node.get('d')) simpletransform.applyTransformToPath(mat, p) if self.options.mtype == "length": slengths, stotal = csplength(p) self.group = inkex.etree.SubElement(node.getparent(),inkex.addNS('text','svg')) elif self.options.mtype == "area": stotal = abs(csparea(p)*factor*self.options.scale) self.group = inkex.etree.SubElement(node.getparent(),inkex.addNS('text','svg')) else: xc, yc = cspcofm(p) self.group = inkex.etree.SubElement(node.getparent(),inkex.addNS('path','svg')) self.group.set('id', 'MassCenter_' + node.get('id')) self.addCross(self.group, xc, yc, scale) continue # Format the length as string lenstr = locale.format("%(len)25."+str(prec)+"f",{'len':round(stotal*factor*self.options.scale,prec)}).strip() if self.options.mformat == '"textonpath"': startOffset = self.options.startOffset if startOffset == "custom": startOffset = str(self.options.startOffsetCustom) + '%' if self.options.mtype == "length": self.addTextOnPath(self.group, 0, 0, lenstr+' '+self.options.unit, id, self.options.anchor, startOffset, self.options.offset) else: self.addTextOnPath(self.group, 0, 0, lenstr+' '+self.options.unit+'^2', id, self.options.anchor, startOffset, self.options.offset) elif self.options.mformat == '"fixedtext"': if self.options.position == "mass": tx, ty = cspcofm(p) anchor = 'middle' elif self.options.position == "center": bbox = simpletransform.computeBBox([node]) tx = bbox[0] + (bbox[1] - bbox[0])/2.0 ty = bbox[2] + (bbox[3] - bbox[2])/2.0 anchor = 'middle' else: # default tx = p[0][0][1][0] ty = p[0][0][1][1] anchor = 'start' if self.options.mtype == "length": self.addTextWithTspan(self.group, tx, ty, lenstr+' '+self.options.unit, id, anchor, -int(self.options.angle), self.options.offset + self.options.fontsize/2) else: self.addTextWithTspan(self.group, tx, ty, lenstr+' '+self.options.unit+'^2', id, anchor, -int(self.options.angle), -self.options.offset + self.options.fontsize/2) else: # center of mass, no text pass
def process_shape(self, node, mat): rgb = (0,0,0) style = node.get('style') if style: style = simplestyle.parseStyle(style) if style.has_key('stroke'): if style['stroke'] and style['stroke'] != 'none' and style['stroke'][0:3] != 'url': rgb = simplestyle.parseColor(style['stroke']) hsl = coloreffect.ColorEffect.rgb_to_hsl(coloreffect.ColorEffect(),rgb[0]/255.0,rgb[1]/255.0,rgb[2]/255.0) self.color = 7 # default is black if hsl[2]: self.color = 1 + (int(6*hsl[0] + 0.5) % 6) # use 6 hues if node.tag == inkex.addNS('path','svg'): d = node.get('d') if not d: return p = cubicsuperpath.parsePath(d) elif node.tag == inkex.addNS('rect','svg'): x = float(node.get('x', 0)) y = float(node.get('y', 0)) width = float(node.get('width')) height = float(node.get('height')) d = "m %s,%s %s,%s %s,%s %s,%s z" % (x, y, width, 0, 0, height, -width, 0) p = cubicsuperpath.parsePath(d) elif node.tag == inkex.addNS('line','svg'): x1 = float(node.get('x1', 0)) x2 = float(node.get('x2', 0)) y1 = float(node.get('y1', 0)) y2 = float(node.get('y2', 0)) d = "M %s,%s L %s,%s" % (x1, y1, x2, y2) p = cubicsuperpath.parsePath(d) elif node.tag == inkex.addNS('circle','svg'): cx = float(node.get('cx', 0)) cy = float(node.get('cy', 0)) r = float(node.get('r')) d = "m %s,%s a %s,%s 0 0 1 %s,%s %s,%s 0 0 1 %s,%s z" % (cx + r, cy, r, r, -2*r, 0, r, r, 2*r, 0) p = cubicsuperpath.parsePath(d) elif node.tag == inkex.addNS('ellipse','svg'): cx = float(node.get('cx', 0)) cy = float(node.get('cy', 0)) rx = float(node.get('rx')) ry = float(node.get('ry')) d = "m %s,%s a %s,%s 0 0 1 %s,%s %s,%s 0 0 1 %s,%s z" % (cx + rx, cy, rx, ry, -2*rx, 0, rx, ry, 2*rx, 0) p = cubicsuperpath.parsePath(d) else: return trans = node.get('transform') if trans: mat = simpletransform.composeTransform(mat, simpletransform.parseTransform(trans)) simpletransform.applyTransformToPath(mat, p) for sub in p: for i in range(len(sub)-1): s = sub[i] e = sub[i+1] if s[1] == s[2] and e[0] == e[1]: if (self.options.POLY == 'true'): self.LWPOLY_line([s[1],e[1]]) else: self.dxf_line([s[1],e[1]]) elif (self.options.ROBO == 'true'): self.ROBO_spline([s[1],s[2],e[0],e[1]]) else: self.dxf_spline([s[1],s[2],e[0],e[1]])
def effect(self): if self.options.mformat == '"presets"': self.setPreset() # get number of digits prec = int(self.options.precision) scale = self.unittouu('1px') # convert to document units self.options.offset *= scale factor = 1.0 doc = self.document.getroot() if doc.get('viewBox'): [viewx, viewy, vieww, viewh] = doc.get('viewBox').split(' ') factor = self.unittouu(doc.get('width')) / float(vieww) if self.unittouu(doc.get('height')) / float(viewh) < factor: factor = self.unittouu(doc.get('height')) / float(viewh) factor /= self.unittouu('1px') self.options.fontsize /= factor factor *= scale / self.unittouu('1' + self.options.unit) # loop over all selected paths for id, node in self.selected.iteritems(): if node.tag == inkex.addNS('path', 'svg'): mat = simpletransform.composeParents( node, [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]]) p = cubicsuperpath.parsePath(node.get('d')) simpletransform.applyTransformToPath(mat, p) if self.options.mtype == "length": slengths, stotal = csplength(p) self.group = inkex.etree.SubElement( node.getparent(), inkex.addNS('text', 'svg')) elif self.options.mtype == "area": stotal = abs(csparea(p) * factor * self.options.scale) self.group = inkex.etree.SubElement( node.getparent(), inkex.addNS('text', 'svg')) else: xc, yc = cspcofm(p) self.group = inkex.etree.SubElement( node.getparent(), inkex.addNS('path', 'svg')) self.group.set('id', 'MassCenter_' + node.get('id')) self.addCross(self.group, xc, yc, scale) continue # Format the length as string lenstr = locale.format("%(len)25." + str(prec) + "f", { 'len': round(stotal * factor * self.options.scale, prec) }).strip() if self.options.mformat == '"textonpath"': startOffset = self.options.startOffset if startOffset == "custom": startOffset = str(self.options.startOffsetCustom) + '%' if self.options.mtype == "length": self.addTextOnPath(self.group, 0, 0, lenstr + ' ' + self.options.unit, id, self.options.anchor, startOffset, self.options.offset) else: self.addTextOnPath( self.group, 0, 0, lenstr + ' ' + self.options.unit + '^2', id, self.options.anchor, startOffset, self.options.offset) elif self.options.mformat == '"fixedtext"': if self.options.position == "mass": tx, ty = cspcofm(p) anchor = 'middle' elif self.options.position == "center": bbox = simpletransform.computeBBox([node]) tx = bbox[0] + (bbox[1] - bbox[0]) / 2.0 ty = bbox[2] + (bbox[3] - bbox[2]) / 2.0 anchor = 'middle' else: # default tx = p[0][0][1][0] ty = p[0][0][1][1] anchor = 'start' if self.options.mtype == "length": self.addTextWithTspan( self.group, tx, ty, lenstr + ' ' + self.options.unit, id, anchor, -int(self.options.angle), self.options.offset + self.options.fontsize / 2) else: self.addTextWithTspan( self.group, tx, ty, lenstr + ' ' + self.options.unit + '^2', id, anchor, -int(self.options.angle), -self.options.offset + self.options.fontsize / 2) else: # center of mass, no text pass
def effect(self): object2path.ObjectToPath.effect(self) self.dxf += self.dxf_add_codes([('999', 'Inkscape export via OpenSCAD DXF Export')]) self.dxf += dxf_templates.r14_header scale = 25.4/90.0 h = self.unittouu(self.document.getroot().xpath('@height',namespaces=inkex.NSS)[0]) path = '//svg:path' pm = pathmodifier.PathModifier() layers = [] dxf_body = '' for node in self.document.getroot().xpath(path,namespaces=inkex.NSS): pm.objectToPath(node, True) layer = node.getparent().get(inkex.addNS('label', 'inkscape')) if layer == None: layer = 'Layer 1' layer = layer.replace(' ', '_') if layer not in layers: layers.append(layer) self.layer_dims[layer] = { 'minX':None, 'minY':None, 'maxX':None, 'maxY':None } d = node.get('d') color = (0,0,0) style = node.get('style') if style: style = simplestyle.parseStyle(style) if style.has_key('stroke'): if style['stroke'] and style['stroke'] != 'none': color = simplestyle.parseColor(style['stroke']) p = cubicsuperpath.parsePath(d) t = node.get('transform') if t != None: m = simpletransform.parseTransform(t) simpletransform.applyTransformToPath(m,p) m = [[scale,0,0],[0,-scale,h*scale]] simpletransform.applyTransformToPath(m,p) dxf_body += self.dxf_path_to_lines(layer, p, color) self.dxf += self.dxf_add_codes([ ('0', 'TABLE'), ('2', 'LAYER'), ('5', '2'), ('330', '0'), ('100', 'AcDbSymbolTable'), # group code 70 tells a reader how many table records to expect (e.g. pre-allocate memory for). # It must be greater or equal to the actual number of records ('70', str(len(layers))) ]) # Add dimensions for total width and height dxf_dims = self.dxf_add_dimension('total_width', [self.global_dims['minX'], self.global_dims['maxX']]) dxf_dims += self.dxf_add_dimension('total_height', None, [self.global_dims['minY'], self.global_dims['maxY']]) for layer in layers: self.dxf += self.dxf_add_codes([ ('0', 'LAYER'), ('5', '10'), ('330', '2'), ('100', 'AcDbSymbolTableRecord'), ('100', 'AcDbLayerTableRecord'), ('2', layer), ('70', '0'), ('62', '7'), ('6', 'CONTINUOUS') ]) # Add dimensions for layer width and height dxf_dims += self.dxf_add_dimension(layer + '_width', [self.layer_dims[layer]['minX'], self.layer_dims[layer]['maxX']], None, layer) dxf_dims += self.dxf_add_dimension(layer + '_height', None, [self.layer_dims[layer]['minY'], self.layer_dims[layer]['maxY']], layer) self.dxf += self.dxf_add_codes([ ('0', 'ENDTAB'), ('0', 'ENDSEC') ]) self.dxf += dxf_templates.r14_style self.dxf += dxf_dims self.dxf += dxf_body self.dxf += dxf_templates.r14_footer
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