def effect(self): #References: Minimum Requirements for Creating a DXF File of a 3D Model By Paul Bourke # NURB Curves: A Guide for the Uninitiated By Philip J. Schneider # The NURBS Book By Les Piegl and Wayne Tiller (Springer, 1995) self.dxf_add("999\nDXF created by Inkscape\n") self.dxf_add(dxf_templates.r14_header) scale = 25.4/90.0 h = inkex.unittouu(self.document.getroot().xpath('@height', namespaces=inkex.NSS)[0]) path = '//svg:path' for node in self.document.getroot().xpath(path, namespaces=inkex.NSS): d = node.get('d') sim = simplepath.parsePath(d) if len(sim): simplepath.scalePath(sim,scale,-scale) simplepath.translatePath(sim,0,h*scale) p = cubicsuperpath.CubicSuperPath(sim) 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]: 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]]) if self.options.ROBO == 'true': self.ROBO_output() self.LWPOLY_output() self.dxf_add(dxf_templates.r14_footer)
def addPathVertices(self, path, node=None, transform=None, clone_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 # 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 # Get a cubic super path p = cubicsuperpath.CubicSuperPath(sp) if (not p) or (len(p) == 0): # Probably never happens, but... return # Now traverse the cubic super path subpath_list = [] subpath_vertices = [] for sp in p: if len(subpath_vertices): # There's a prior subpath: see if it is closed and should be saved if distanceSquared(subpath_vertices[0], subpath_vertices[-1]) < 1: # Keep the prior subpath: it appears to be a closed path subpath_list.append(subpath_vertices) subpath_vertices = [] subdivideCubicPath(sp, 0.2) for csp in sp: # Add this vertex to the list of vertices 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 subpath_list.append(subpath_vertices) # Empty path? if not subpath_list: return # Store the list of subpaths in a dictionary keyed off of the path's node pointer self.paths[node] = subpath_list self.paths_clone_transform[node] = clone_transform
def parsePath(d): ''' Parse line and replace quadratic bezier segments and arcs by cubic bezier segments. ''' import simplepath p = simplepath.parsePath(d) if any(cmd not in 'MLCZ' for (cmd, params) in p): import cubicsuperpath csp = cubicsuperpath.CubicSuperPath(p) p = cubicsuperpath.unCubicSuperPath(csp) return p
def getNodeDimensions(self, node): try: nodeX = float(node.attrib["x"]) nodeY = float(node.attrib["y"]) nodeWidth = float(node.attrib["width"]) nodeHeight = float(node.attrib["height"]) except Exception: try: (nodeMinX, nodeMaxX, nodeMinY, nodeMaxY) = \ simpletransform.roughBBox(node.attrib["d"]) except Exception: (nodeMinX, nodeMaxX, nodeMinY, nodeMaxY) = \ simpletransform.roughBBox( cubicsuperpath.CubicSuperPath( simplepath.parsePath(node.attrib["d"]) ) ) nodeX = nodeMinX nodeY = nodeMinY nodeWidth = nodeMaxX - nodeMinX nodeHeight = nodeMaxY - nodeMinY return nodeX, nodeY, nodeWidth, nodeHeight
def morphPath(path, axes): bounds = simpletransform.roughBBox(cubicsuperpath.CubicSuperPath(path)) newPath = [] current = [0.0, 0.0] start = [0.0, 0.0] for cmd, params in path: segmentType = cmd points = params if segmentType == "M": start[0] = points[0] start[1] = points[1] segmentType = convertSegmentToCubic(current, segmentType, points, start) percentages = [0.0] * len(points) morphed = [0.0] * len(points) numPts = getNumPts(segmentType) normalizePoints(bounds, points, percentages, numPts) mapPointsToMorph(axes, percentages, morphed, numPts) addSegment(newPath, segmentType, morphed) if len(points) >= 2: current[0] = points[len(points) - 2] current[1] = points[len(points) - 1] return newPath
def effect(self): #References: Minimum Requirements for Creating a DXF File of a 3D Model By Paul Bourke # NURB Curves: A Guide for the Uninitiated By Philip J. Schneider self.dxf_add("999\nDXF created by Inkscape\n0\nSECTION\n2\nENTITIES") scale = 25.4/90.0 h = inkex.unittouu(inkex.xml.xpath.Evaluate('/svg/@height',self.document)[0].value) path = '//path' for node in inkex.xml.xpath.Evaluate(path,self.document): d = node.attributes.getNamedItem('d').value sim = simplepath.parsePath(d) simplepath.scalePath(sim,scale,-scale) simplepath.translatePath(sim,0,h*scale) p = cubicsuperpath.CubicSuperPath(sim) 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]: self.dxf_line([s[1],e[1]]) else: self.dxf_spline([s[1],s[2],e[0],e[1]]) self.dxf_add("\n0\nENDSEC\n0\nEOF\n")
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 getPathVertices(self, path, node=None, transform=None, smoothness=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. The result is appended to self.paths as a two-element tuple of the form (node, path_list). This preserves the native ordering of the SVG file as much as possible, while still making all attributes if the node available when processing the path list. ''' if not smoothness: smoothness = self.smoothness # self.smoothness is deprecated. if (not path) or (len(path) == 0): # Nothing to do return None if node is not None: path = self.styleDasharray(path, node) # 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 = [] self.subdivideCubicPath(sp, float(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] n = len(sp) # 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.append((node, subpath_list))
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'] / 10)) 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): # get user-entered params x_scale = self.options.x_scale y_scale = self.options.y_scale t_start = self.options.t_start t_end = self.options.t_end n_steps = self.options.n_steps fps = self.options.fps dt = self.options.dt x_eqn = self.options.x_eqn y_eqn = self.options.y_eqn x_size_eqn = self.options.x_size_eqn y_size_eqn = self.options.y_size_eqn theta_eqn = self.options.theta_eqn # get doc root svg = self.document.getroot() doc_w = self.unittouu(svg.get('width')) doc_h = self.unittouu(svg.get('height')) # get selected items and validate selected = pathmodifier.zSort(self.document.getroot(), self.selected.keys()) if not selected: inkex.errormsg( 'Exactly two objects must be selected: a rect and a template. See "help" for details.' ) return elif len(selected) != 2: inkex.errormsg( 'Exactly two objects must be selected: a rect and a template. See "help" for details.' ) return # rect rect = self.selected[selected[0]] if not rect.tag.endswith('rect'): inkex.errormsg('Bottom object must be rect. See "help" for usage.') return # object obj = self.selected[selected[1]] if not (obj.tag.endswith('path') or obj.tag.endswith('g')): inkex.errormsg( 'Template object must be path or group of paths. See "help" for usage.' ) return if obj.tag.endswith('g'): children = obj.getchildren() if not all([ch.tag.endswith('path') for ch in children]): msg = 'All elements of group must be paths, but they are: ' msg += ', '.join(['{}'.format(ch) for ch in children]) inkex.errormsg(msg) return objs = children is_group = True else: objs = [obj] is_group = False # get rect params w = float(rect.get('width')) h = float(rect.get('height')) x_rect = float(rect.get('x')) y_rect = float(rect.get('y')) # lower left corner x_0 = x_rect y_0 = y_rect + h # get object path(s) obj_ps = [simplepath.parsePath(obj_.get('d')) for obj_ in objs] n_segs = [len(obj_p_) for obj_p_ in obj_ps] obj_p = sum(obj_ps, []) # compute travel parameters if not n_steps: # compute dt if dt == 0: dt = 1. / fps ts = np.arange(t_start, t_end, dt) else: ts = np.linspace(t_start, t_end, n_steps) # compute xs, ys, stretches, and rotations in arbitrary coordinates xs = np.nan * np.zeros(len(ts)) ys = np.nan * np.zeros(len(ts)) x_sizes = np.nan * np.zeros(len(ts)) y_sizes = np.nan * np.zeros(len(ts)) thetas = np.nan * np.zeros(len(ts)) for ctr, t in enumerate(ts): xs[ctr] = eval(x_eqn) ys[ctr] = eval(y_eqn) x_sizes[ctr] = eval(x_size_eqn) y_sizes[ctr] = eval(y_size_eqn) thetas[ctr] = eval(theta_eqn) * pi / 180 # ensure no Infs if np.any(np.isinf(xs)): raise Exception('Inf detected in x(t), please remove.') return if np.any(np.isinf(ys)): raise Exception('Inf detected in y(t), please remove.') return if np.any(np.isinf(x_sizes)): raise Exception('Inf detected in x_size(t), please remove.') return if np.any(np.isinf(y_sizes)): raise Exception('Inf detected in y_size(t), please remove.') return if np.any(np.isinf(thetas)): raise Exception('Inf detected in theta(t), please remove.') return # convert to screen coordinates xs *= (w / x_scale) xs += x_0 ys *= (-h / y_scale) # neg sign to invert y for inkscape screen ys += y_0 # get obj center b_box = simpletransform.refinedBBox( cubicsuperpath.CubicSuperPath(obj_p)) c_x = 0.5 * (b_box[0] + b_box[1]) c_y = 0.5 * (b_box[2] + b_box[3]) # get rotation anchor if any([k.endswith('transform-center-x') for k in obj.keys()]): k_r_x = [ k for k in obj.keys() if k.endswith('transform-center-x') ][0] k_r_y = [ k for k in obj.keys() if k.endswith('transform-center-y') ][0] r_x = c_x + float(obj.get(k_r_x)) r_y = c_y - float(obj.get(k_r_y)) else: r_x, r_y = c_x, c_y paths = [] # compute new paths for x, y, x_size, y_size, theta in zip(xs, ys, x_sizes, y_sizes, thetas): path = deepcopy(obj_p) # move to origin simplepath.translatePath(path, -x_0, -y_0) # move rotation anchor accordingly r_x_1 = r_x - x_0 r_y_1 = r_y - y_0 # scale simplepath.scalePath(path, x_size, y_size) # scale rotation anchor accordingly r_x_2 = r_x_1 * x_size r_y_2 = r_y_1 * y_size # move to final location simplepath.translatePath(path, x, y) # move rotation anchor accordingly r_x_3 = r_x_2 + x r_y_3 = r_y_2 + y # rotate simplepath.rotatePath(path, -theta, cx=r_x_3, cy=r_y_3) paths.append(path) parent = self.current_layer group = inkex.etree.SubElement(parent, inkex.addNS('g', 'svg'), {}) for path in paths: if is_group: group_ = inkex.etree.SubElement(group, inkex.addNS('g', 'svg'), {}) path_components = split(path, n_segs) for path_component, child in zip(path_components, children): attribs = {k: child.get(k) for k in child.keys()} attribs['d'] = simplepath.formatPath(path_component) child_copy = inkex.etree.SubElement( group_, child.tag, attribs) else: attribs = {k: obj.get(k) for k in obj.keys()} attribs['d'] = simplepath.formatPath(path) obj_copy = inkex.etree.SubElement(group, obj.tag, attribs)
def boundingBox(self): csp = cubicsuperpath.CubicSuperPath(self.data) self.bbox = list(simpletransform.roughBBox(csp)) return list(simpletransform.roughBBox(csp)) # [minx,maxx,miny,maxy]
def getPathVertices(self, path, node=None, transform=None, find_bbox=False): ''' 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: if len(subpath_vertices): subpath_list.append(subpath_vertices) subpath_vertices = [] last_csp = None subdivideCubicPath(sp, float(self.options.smoothness)) for csp in sp: if (last_csp != None) and (math.fabs(csp[1][1] - last_csp[1]) > self.options.maxDy): dy = (csp[1][1] - last_csp[1]) dx = (csp[1][0] - last_csp[0]) nsteps = math.ceil(math.fabs(dy / self.options.maxDy)) for n in range(1, int(1 + nsteps)): s = n / nsteps subpath_vertices.append( [last_csp[0] + s * dx, last_csp[1] + s * dy]) else: # Add this vertex to the list of vetices subpath_vertices.append(csp[1]) last_csp = csp[1] if find_bbox: if last_csp[0] < self.xmin: self.xmin = last_csp[0] elif last_csp[0] > self.xmax: self.xmax = last_csp[0] # Handle final subpath if len(subpath_vertices): subpath_list.append(subpath_vertices) if len(subpath_list) > 0: self.paths[node] = subpath_list self.transforms[node] = transform