def mapPointsToMorph( axes, percentage, morphed, numPts ): # rename the axes for legibility yCubic0 = axes[1] yCubic1 = axes[3] xCubic0 = axes[0] xCubic1 = axes[2] # morph each point for i in range( 0, numPts ): x = i*2 y = i*2+1 # tween between the morphed y axes according to the x percentage tweenedY = tweenCubic( yCubic0, yCubic1, percentage[x] ) # get 2 points on the morphed x axes xSpot0 = pointOnCubic( xCubic0, percentage[x] ) xSpot1 = pointOnCubic( xCubic1, percentage[x] ) # create a transform that stretches the y axis tween between these 2 points yAnchor0 = [ tweenedY[0], tweenedY[1] ] yAnchor1 = [ tweenedY[6], tweenedY[7] ] xTransform = match( yAnchor0, yAnchor1, xSpot0, xSpot1 ) # map the y axis tween to the 2 points by applying the stretch transform for j in range(0,4): x2 = j*2 y2 = j*2+1 pointOnY = [tweenedY[x2],tweenedY[y2]] simpletransform.applyTransformToPoint( xTransform, pointOnY ) tweenedY[x2] = pointOnY[0] tweenedY[y2] = pointOnY[1] # get the point on the tweened and transformed y axis according to the y percentage morphedPoint = pointOnCubic( tweenedY, percentage[y] ) morphed[x] = morphedPoint[0] morphed[y] = morphedPoint[1]
def mapPointsToMorph(axes, percentage, morphed, numPts): # rename the axes for legibility yCubic0 = axes[1] yCubic1 = axes[3] xCubic0 = axes[0] xCubic1 = axes[2] # morph each point for i in range(0, numPts): x = i * 2 y = i * 2 + 1 # tween between the morphed y axes according to the x percentage tweenedY = tweenCubic(yCubic0, yCubic1, percentage[x]) # get 2 points on the morphed x axes xSpot0 = pointOnCubic(xCubic0, percentage[x]) xSpot1 = pointOnCubic(xCubic1, percentage[x]) # create a transform that stretches the y axis tween between these 2 points yAnchor0 = [tweenedY[0], tweenedY[1]] yAnchor1 = [tweenedY[6], tweenedY[7]] xTransform = match(yAnchor0, yAnchor1, xSpot0, xSpot1) # map the y axis tween to the 2 points by applying the stretch transform for j in range(0, 4): x2 = j * 2 y2 = j * 2 + 1 pointOnY = [tweenedY[x2], tweenedY[y2]] simpletransform.applyTransformToPoint(xTransform, pointOnY) tweenedY[x2] = pointOnY[0] tweenedY[y2] = pointOnY[1] # get the point on the tweened and transformed y axis according to the y percentage morphedPoint = pointOnCubic(tweenedY, percentage[y]) morphed[x] = morphedPoint[0] morphed[y] = morphedPoint[1]
def mapPathVertices( self, node ): steps2rads = math.pi / float( 1600 ) transform = self.transforms[node] if transform is None: invTransform = None else: invTransform = inverseTransform( transform ) newPath = '' for subpath in self.paths[node]: lastPoint = subpath[0] lastPoint[0] = self.cx + ( lastPoint[0] - self.cx ) / math.cos( ( lastPoint[1] - self.cy ) * steps2rads ) if invTransform != None: simpletransform.applyTransformToPoint( invTransform, lastPoint ) newPath += ' M %f,%f' % ( lastPoint[0], lastPoint[1] ) for point in subpath[1:]: x = self.cx + ( point[0] - self.cx ) / math.cos( ( point[1] - self.cy ) * steps2rads ) pt = [x, point[1] ] if invTransform != None: simpletransform.applyTransformToPoint( invTransform, pt ) newPath += ' l %f,%f' % ( pt[0] - lastPoint[0], pt[1] - lastPoint[1] ) lastPoint = pt self.paths[node] = newPath
def center(self): point = [float(self.node.get('x', 0)), float(self.node.get('y', 0))] transform = get_node_transform(self.node) applyTransformToPoint(transform, point) return point
def mapPathVertices(self, node): steps2rads = math.pi / float(1600) transform = self.transforms[node] if transform is None: inv_transform = None else: inv_transform = inverseTransform(transform) new_path = '' for subpath in self.paths[node]: last_point = subpath[0] last_point[0] = self.cx + (last_point[0] - self.cx) / math.cos( (last_point[1] - self.cy) * steps2rads) if inv_transform is not None: applyTransformToPoint(inv_transform, last_point) new_path += ' M {0:f},{1:f}'.format(last_point[0], last_point[1]) for point in subpath[1:]: x = self.cx + (point[0] - self.cx) / math.cos( (point[1] - self.cy) * steps2rads) pt = [x, point[1]] if inv_transform is not None: applyTransformToPoint(inv_transform, pt) new_path += ' l {0:f},{1:f}'.format(pt[0] - last_point[0], pt[1] - last_point[1]) last_point = pt self.paths[node] = new_path
def process_layer(self, layer_element, layer_name): #For each layer group, get each path. for element in layer_element.iter(tag_path): log("Found path: " + str(element.attrib['d'])) #Get the point transform at node svg_transforms = self.get_transform_list(element) #Parse path parsed_path = cubicsuperpath.parsePath(element.attrib['d']) #Convert into polyline cspsubdiv.cspsubdiv(parsed_path, self.resolution) #At this point, parsed_path contains a list of list of points (yes, I know) #so for each "path", each "subpath" we should get an array of points for subpath in parsed_path: log(" Subpath (%d points)" % len(subpath)) #Write footprint path begining self.out_file.write(polygon_header) for point in subpath: point = list(point[1]) for transform in svg_transforms: log("Applying transform: " + str(transform)) simpletransform.applyTransformToPoint(transform, point) log(" Point: " + str(point)) #transform point using self.transform matrix #write individual point self.out_file.write("(xy %f %f) " % (point[0], point[1])) self.out_file.write(polygon_footer.format(layer=layer_name))
def mapPathVertices(self, node): steps2rads = math.pi / float(1600) transform = self.transforms[node] if transform is None: invTransform = None else: invTransform = inverseTransform(transform) newPath = '' for subpath in self.paths[node]: lastPoint = subpath[0] lastPoint[0] = self.cx + (lastPoint[0] - self.cx) / math.cos( (lastPoint[1] - self.cy) * steps2rads) if invTransform != None: simpletransform.applyTransformToPoint(invTransform, lastPoint) newPath += ' M %f,%f' % (lastPoint[0], lastPoint[1]) for point in subpath[1:]: x = self.cx + (point[0] - self.cx) / math.cos( (point[1] - self.cy) * steps2rads) pt = [x, point[1]] if invTransform != None: simpletransform.applyTransformToPoint(invTransform, pt) newPath += ' l %f,%f' % (pt[0] - lastPoint[0], pt[1] - lastPoint[1]) lastPoint = pt self.paths[node] = newPath
def exportDrill(self, kicad_mod=False): x0 = 0 y0 = 0 mirror = 1.0 self.setInkscapeScaling() kicad_drill_string = "" i = 0 if kicad_mod: pad_template = "(pad {n} thru_hole circle (at {x} {y}) (size {d} {d}) (drill {d}) (layers *.Cu *.Mask))\n" else: pad_template = """ (module Wire_Pads:SolderWirePad_single_0-8mmDrill (layer F.Cu) (tedit 0) (tstamp 5ABD66D0) (at {x} {y}) (pad {n} thru_hole circle (at 0 0) (size {d} {d}) (drill {d}) (layers *.Cu *.Mask)) ) """ layerPath = '//svg:g[@inkscape:groupmode="layer"][@inkscape:label="Drill"]' for layer in self.document.getroot().xpath(layerPath, namespaces=inkex.NSS): layer_trans = layer.get('transform') if layer_trans: layer_m = simpletransform.parseTransform(layer_trans) else: layer_m = IDENTITY_MATRIX nodePath = 'descendant::svg:circle' count = 0 for node in layer.xpath(nodePath, namespaces=inkex.NSS): count = count + 1 cx = float(node.get('cx')) cy = float(node.get('cy')) radius = float(node.get('r')) drill_size = radius * 2 t = node.get('transform') pt = [cx, cy] if t: m = simpletransform.parseTransform(t) trans = simpletransform.composeTransform(layer_m, m) else: trans = layer_m simpletransform.applyTransformToPoint(trans,pt) padCoord = self.coordToKicad(pt) kicad_drill_string += pad_template.format(x=padCoord[0], y=padCoord[1], n=count, d=drill_size) return kicad_drill_string
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 center(self): point = [float(self.node.get('x', 0)), float(self.node.get('y', 0))] point = [(point[0] + (float(self.node.get('width', 0)) / 2)), (point[1] + (float(self.node.get('height', 0)) / 2))] transform = get_node_transform(self.node) applyTransformToPoint(transform, point) return point
def process_path(self, ipath, mat_x, mat_y, term1): mat = simpletransform.composeParents( ipath, [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]]) path_string = ipath.get('d') svg_path = simplepath.parsePath(path_string) paths = [] # for path in svg_path: # inkex.debug("svg_path: " + str(svg_path)) for path in svg_path: if path[0] == 'M' or path[0] == 'L': paths.append([path[0], [path[1]]]) elif path[0] == 'C' or path[0] == 'Q': pts = [] if path[0] == 'C': num = 3 else: num = 2 for i in range(0, num): pt = [path[1][2 * i], path[1][2 * i + 1]] pts.append(pt) paths.append([path[0], pts]) elif path[0] == 'Z': paths.append(['Z', []]) # inkex.debug("paths: " + str(paths) + "\n\n") mat = simpletransform.invertTransform(mat) # simpletransform.applyTransformToPath(mat, p) for path in paths: for pt in path[1]: simpletransform.applyTransformToPoint(mat, pt) # do transformation for path in paths: for pt in path[1]: self.project_point(pt, mat_x, mat_y, term1) # back to original form res_paths = [] for path in paths: if path[0] == 'C' or path[0] == 'Q': flat_pts = [] for pt in path[1]: flat_pts.append(pt[0]) flat_pts.append(pt[1]) res_paths.append([path[0], flat_pts]) elif path[0] == 'M' or path[0] == 'L': res_paths.append([path[0], path[1][0]]) elif path[0] == 'Z': res_paths.append(path) # inkex.debug("res_paths: " + str(res_paths)) res_svg_paths = simplepath.formatPath(res_paths) # inkex.debug("res_svg_paths: " + str(res_svg_paths)) ipath.set('d', res_svg_paths)
def to_point(point): """ Extracts a point in our format from a point in simplepath format """ transformed_point = [point[1][0], point[1][1]] simpletransform.applyTransformToPoint(self.current_transform(), transformed_point) return (self.to_mm(transformed_point[0]), self.to_mm(transformed_point[1]))
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 _readPath(self, item, flat, transform): p = cubicsuperpath.parsePath(item.get('d')) cspsubdiv.cspsubdiv(p, flat) subpaths = [] for sp in p: sps = [] subpaths.append(sps) for c0, c1, c2 in sp: pt = list(c2) simpletransform.applyTransformToPoint(transform, pt) sps.append(tuple(pt)) self[:] = mergePaths(sortPaths(subpaths))
def element_center (node) : if node.tag == inkex.addNS('path','svg') :#assume circle x=float(node.get(inkex.addNS("cx","sodipodi"))) y=float(node.get(inkex.addNS("cy","sodipodi"))) elif node.tag == inkex.addNS('rect','svg') : x=float(node.get("x"))+float(node.get("width"))/2. y=float(node.get("y"))+float(node.get("height"))/2. else : raise UserWarning("unrecognized node") pt=[x,y] if node.get("transform") is not None : mat=simpletransform.parseTransform(node.get("transform")) simpletransform.applyTransformToPoint(mat,pt) return pt
def element_center(node): if node.tag == inkex.addNS('path', 'svg'): #assume circle x = float(node.get(inkex.addNS("cx", "sodipodi"))) y = float(node.get(inkex.addNS("cy", "sodipodi"))) elif node.tag == inkex.addNS('rect', 'svg'): x = float(node.get("x")) + float(node.get("width")) / 2. y = float(node.get("y")) + float(node.get("height")) / 2. else: raise UserWarning("unrecognized node") pt = [x, y] if node.get("transform") is not None: mat = simpletransform.parseTransform(node.get("transform")) simpletransform.applyTransformToPoint(mat, pt) return pt
def draw(self, transform): """ Draws the working area :param transform: the transform to apply to points :type transform: a 2x3 matrix """ # Using a viewBox so that we can express all measures relative to it. We compute the # view box height when the width is 100 to keep the aspect ratio. We also compute the stroke # width so that it is 0.5 millimiters self.factor = 100.0 / self.dim_x self.view_box_height = self.factor * self.dim_y line_width = self.factor * 0.5 # inverting transformation, we know absolute coordinates inv_transform = simpletransform.invertTransform(transform) # tranforming the position of the box box_origin = [0, self.page_height - self.dim_y] simpletransform.applyTransformToPoint(inv_transform, box_origin) # transforming lengths (ignoring translation) box_dims = [self.dim_x, self.dim_y] length_inv_transform = [[ inv_transform[0][0], inv_transform[0][1], 0.0 ], [inv_transform[1][0], inv_transform[1][1], 0.0]] simpletransform.applyTransformToPoint(length_inv_transform, box_dims) self.area = inkex.etree.Element( "svg", { 'id': self.working_area_id, 'x': str(self.to_uu(box_origin[0])), 'y': str(self.to_uu(box_origin[1])), 'width': str(self.to_uu(box_dims[0])), 'height': str(self.to_uu(box_dims[1])), 'viewBox': "0 0 100 " + str(self.view_box_height), 'preserveAspectRatio': "none", 'style': "fill:none;stroke-width:{};stroke:rgb(0,0,0)".format( line_width) }) self.draw_rectangle() self.draw_cross() self.draw_text()
def element_size (node) : if node.tag == inkex.addNS('path','svg') :#assume circle rx=float(node.get(inkex.addNS("rx","sodipodi"))) ry=float(node.get(inkex.addNS("ry","sodipodi"))) elif node.tag == inkex.addNS('rect','svg') : rx=float(node.get("width"))/2. ry=float(node.get("height"))/2. else : raise UserWarning("unrecognized node") pt=[rx,ry] if node.get("transform") is not None : mat=simpletransform.parseTransform(node.get("transform")) mat[0][2]=0 mat[1][2]=0 simpletransform.applyTransformToPoint(mat,pt) return pt
def element_size(node): if node.tag == inkex.addNS('path', 'svg'): #assume circle rx = float(node.get(inkex.addNS("rx", "sodipodi"))) ry = float(node.get(inkex.addNS("ry", "sodipodi"))) elif node.tag == inkex.addNS('rect', 'svg'): rx = float(node.get("width")) / 2. ry = float(node.get("height")) / 2. else: raise UserWarning("unrecognized node") pt = [rx, ry] if node.get("transform") is not None: mat = simpletransform.parseTransform(node.get("transform")) mat[0][2] = 0 mat[1][2] = 0 simpletransform.applyTransformToPoint(mat, pt) return pt
def effect(self): """ Apply the transformation. If an element already has a transformation attribute, it will be combined with the transformation matrix for the requested conversion. """ if self.options.reverse == "true": conversion = "from_" + self.options.conversion else: conversion = "to_" + self.options.conversion if len(self.selected) == 0: inkex.errormsg( _("Please select an object to perform the " + "isometric projection transformation on.")) return # Default to the flat 2D to isometric top down view conversion if an # invalid identifier is passed. effect_matrix = self.transformations.get( conversion, self.transformations.get('to_top')) for id, node in self.selected.items(): bbox = computeBBox([node]) midpoint = self.getMidPoint(bbox, node) center_old = self.getTransformCenter(midpoint, node) transform = node.get("transform") # Combine our transformation matrix with any pre-existing # transform. matrix = parseTransform(transform, effect_matrix) # Compute the location of the transformation center after applying # the transformation matrix. center_new = center_old[:] applyTransformToPoint(matrix, center_new) applyTransformToPoint(matrix, midpoint) # Add a translation transformation that will move the object to # keep its transformation center in the same place. self.translateBetweenPoints(matrix, center_new, center_old) node.set('transform', formatTransform(matrix)) # Adjust the transformation center. self.moveTransformationCenter(node, midpoint, center_new)
def effect(self): """ Apply the transformation. If an element already has a transformation attribute, it will be combined with the transformation matrix for the requested conversion. """ if self.options.reverse == "true": conversion = "from_" + self.options.conversion else: conversion = "to_" + self.options.conversion if len(self.selected) == 0: inkex.errormsg(_("Please select an object to perform the " + "isometric projection transformation on.")) return # Default to the flat 2D to isometric top down view conversion if an # invalid identifier is passed. effect_matrix = self.transformations.get( conversion, self.transformations.get('to_top')) for id, node in self.selected.iteritems(): bbox = computeBBox([node]) midpoint = self.getMidPoint(bbox, node) center_old = self.getTransformCenter(midpoint, node) transform = node.get("transform") # Combine our transformation matrix with any pre-existing # transform. matrix = parseTransform(transform, effect_matrix) # Compute the location of the transformation center after applying # the transformation matrix. center_new = center_old[:] applyTransformToPoint(matrix, center_new) applyTransformToPoint(matrix, midpoint) # Add a translation transformation that will move the object to # keep its transformation center in the same place. self.translateBetweenPoints(matrix, center_new, center_old) node.set('transform', formatTransform(matrix)) # Adjust the transformation center. self.moveTransformationCenter(node, midpoint, center_new)
def dxf_arc_transform(self,m,cx,cy,rx,ry,abase,a0,a1): cp = [cx,cy] abaserads = math.radians(abase) if rx >= ry: abaserads = abaserads + math.pi rmaj = rx rmin = ry # major axis vector points left # angles in inkscape = cw from elipse local x axis; # angles in dxf ccw from major axis # major axis at Pi # so invert and offset by Pi a0 = math.pi - a0 a1 = math.pi - a1 else: abaserads = abaserads - math.pi / 2 rmaj = ry rmin = rx # major axis vector is up the page (-ve y in inkscape) # angles in inkscape = cw from elipse local x axis; # angles in dxf ccw from major axis # major axis at 3 * Pi / 2 # so invert and offset by 3 * Pi / 2 a0 = 3 * math.pi / 2 - a0 a1 = 3 * math.pi / 2 - a1 rmm = rmin / rmaj majaxisp = [cx + rmaj*cos(abaserads), cy + rmaj*sin(abaserads)] if ((a0 < 0) or (a1 < 0)): a0 = a0 + 2 * math.pi a1 = a1 + 2 * math.pi # apply transforms simpletransform.applyTransformToPoint(m,cp) simpletransform.applyTransformToPoint(m,majaxisp) # Maj axis is relative to centre... majaxisp[0] = majaxisp[0] - cp[0] majaxisp[1] = majaxisp[1] - cp[1] # reverse angles from inkscape cw to DXF ccw self.dxf_arc(cp,majaxisp,rmm,a1,a0)
def get_origin(svg): origin_command = global_command(svg, "origin") if origin_command: return origin_command.point else: # default: center of the canvas doc_size = list(get_doc_size(svg)) # convert the size from viewbox-relative to real-world pixels viewbox_transform = get_viewbox_transform(svg) simpletransform.applyTransformToPoint(simpletransform.invertTransform(viewbox_transform), doc_size) default = [doc_size[0] / 2.0, doc_size[1] / 2.0] simpletransform.applyTransformToPoint(viewbox_transform, default) default = Point(*default) return default
def _parse(self): self.label = self.node.get(INKSCAPE_LABEL, "") doc_size = list(get_doc_size(self.svg)) # convert the size from viewbox-relative to real-world pixels viewbox_transform = get_viewbox_transform(self.svg) simpletransform.applyTransformToPoint( simpletransform.invertTransform(viewbox_transform), doc_size) self.position = Point(*string_to_floats(self.node.get('position'))) # inkscape's Y axis is reversed from SVG's, and the guide is in inkscape coordinates self.position.y = doc_size[1] - self.position.y # This one baffles me. I think inkscape might have gotten the order of # their vector wrong? parts = string_to_floats(self.node.get('orientation')) self.direction = Point(parts[1], parts[0])
def __init__(self, document, options, size, scale): super(KiCadFootprintBuilder, self).__init__(document, options, size, scale) self.expression = [ MODULE, MODULE_NAME, [LAYER, 'F.Cu'], [TEDIT, timestamp()], [ATTR, 'smd'] ] T.foo() if options.description: self.expression.append([DESCR, options.description]) if options.tags > 0: self.expression.append([TAGS, options.tags]) font_size = 1 field_offset = 1.2 * font_size if options.ref_mode != 'none': hidden = options.ref_mode == 'hidden' p = [0.5 * self.size[0], 0] simpletransform.applyTransformToPoint(self.currentTransform(), p) p[1] -= field_offset self.appendField(FIELD_REFERENCE, 'REF**', p, 'F.SilkS', hidden, font_size) if options.value_mode != 'none': hidden = options.value_mode == 'hidden' value = '' if options.value_src == 'document': title_node = self.document.xpath('//dc:title', namespaces=ix.NSS)[0] if title_node is None or title_node.text is None or len( title_node.text) == 0: abort('Document Properties/Metadata/Title is missing') value = title_node.text elif options.value_src == 'custom': value = options.custom_value else: abort('Unhandled value-src') p = [0.5 * self.size[0], self.size[1]] simpletransform.applyTransformToPoint(self.currentTransform(), p) p[1] += field_offset self.appendField(FIELD_VALUE, value, p, 'F.SilkS', hidden, font_size)
def rect_bounding_box(rect, box=None): """Get the bounding box of an SVG rectangle. :param rect: The XML node defining the object. :param box: The existing :class:`bounds.BoundingBox` if available. :return: A :class:`bounds.BoundingBox` encompassing the object. """ # Get the position and dimension of the rectangle x = float(rect.get('x', 0)) y = float(rect.get('y', 0)) width = float(rect.get('width')) height = float(rect.get('height')) # Width and height can't be negative if width < 0: raise ValueError(_('Width of rect object cannot be negative.')) if height < 0: raise ValueError(_('Height of rect object cannot be negative.')) # Width or height of zero disables rendering if width == 0 or height == 0: return box # Create the four points bl = [x, y] br = [x + width, y] tr = [x + width, y + height] tl = [x, y + height] # Get the transform transform = rect.get('transform', None) if transform: transform = simpletransform.parseTransform(transform) simpletransform.applyTransformToPoint(transform, bl) simpletransform.applyTransformToPoint(transform, br) simpletransform.applyTransformToPoint(transform, tr) simpletransform.applyTransformToPoint(transform, tl) # Extend the box if box is None: box = BoundingBox(bl[0], bl[0], bl[1], bl[1]) else: box.extend(bl) box.extend(br) box.extend(tr) box.extend(tl) # And done. return box
def get_origin(svg): # The user can specify the embroidery origin by defining two guides # named "embroidery origin" that intersect. namedview = svg.find(inkex.addNS('namedview', 'sodipodi')) all_guides = namedview.findall(inkex.addNS('guide', 'sodipodi')) label_attribute = inkex.addNS('label', 'inkscape') guides = [ guide for guide in all_guides if guide.get(label_attribute, "").startswith("embroidery origin") ] # document size used below doc_size = list(get_doc_size(svg)) # convert the size from viewbox-relative to real-world pixels viewbox_transform = get_viewbox_transform(svg) simpletransform.applyTransformToPoint( simpletransform.invertTransform(viewbox_transform), doc_size) default = [doc_size[0] / 2.0, doc_size[1] / 2.0] simpletransform.applyTransformToPoint(viewbox_transform, default) default = Point(*default) if len(guides) < 2: return default # Find out where the guides intersect. Only pay attention to the first two. guides = guides[:2] lines = [] for guide in guides: # inkscape's Y axis is reversed from SVG's, and the guide is in inkscape coordinates position = Point(*_string_to_floats(guide.get('position'))) position.y = doc_size[1] - position.y # This one baffles me. I think inkscape might have gotten the order of # their vector wrong? parts = _string_to_floats(guide.get('orientation')) direction = Point(parts[1], parts[0]) # We have a theoretically infinite line defined by a point on the line # and a vector direction. Shapely can only deal in concrete line # segments, so we'll pick points really far in either direction on the # line and call it good enough. lines.append( shgeo.LineString((position + 100000 * direction, position - 100000 * direction))) intersection = lines[0].intersection(lines[1]) if isinstance(intersection, shgeo.Point): origin = [intersection.x, intersection.y] simpletransform.applyTransformToPoint(viewbox_transform, origin) return Point(*origin) else: # Either the two guides are the same line, or they're parallel. return default
def effect(self): x0 = self.options.xOrigin y0 = self.options.yOrigin mirror = 1.0 if self.options.mirror: mirror = -1.0 if self.document.getroot().get('height'): y0 -= float(self.document.getroot().get('height')) i = 0 layerPath = '//svg:g[@inkscape:groupmode="layer"]' for layer in self.document.getroot().xpath(layerPath, namespaces=inkex.NSS): i += 1 layer_trans = layer.get('transform') if layer_trans: layer_m = simpletransform.parseTransform(layer_trans) else: layer_m = identity_m nodePath = ('//svg:g[@inkscape:groupmode="layer"][%d]/descendant::svg:path') % i for node in self.document.getroot().xpath(nodePath, namespaces=inkex.NSS): d = node.get('d') p = simplepath.parsePath(d) if p: #sanity check if p[0][0] == 'M': pt = [p[0][1][0], p[0][1][1]] t = node.get('transform') if t: m = simpletransform.parseTransform(t) trans = simpletransform.composeTransform(layer_m, m) else: trans = layer_m simpletransform.applyTransformToPoint(trans,pt) self.x.append(str(pt[0]-x0)) self.y.append(str(pt[1]*mirror-y0))
def op_transform(self, layers, mtx, name="Transform", is_end=False): """Apply a matrix transformation to the given layers Keyword arguments: layers -- list of layers mtx -- transformation matrix name -- name of the Transform layer that is added is_end -- set to True if layers are at the end of a canvas Returns: list of layers """ if layers == []: return layers if mtx is None or mtx == [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]]: return layers src_tl = [100, 100] src_br = [200, 200] dest_tl = [100, 100] dest_tr = [200, 100] dest_br = [200, 200] dest_bl = [100, 200] simpletransform.applyTransformToPoint(mtx, dest_tl) simpletransform.applyTransformToPoint(mtx, dest_tr) simpletransform.applyTransformToPoint(mtx, dest_br) simpletransform.applyTransformToPoint(mtx, dest_bl) warp = self.create_layer( "warp", name, params={ "src_tl": self.coor_svg2sif(src_tl), "src_br": self.coor_svg2sif(src_br), "dest_tl": self.coor_svg2sif(dest_tl), "dest_tr": self.coor_svg2sif(dest_tr), "dest_br": self.coor_svg2sif(dest_br), "dest_bl": self.coor_svg2sif(dest_bl), }, ) if is_end: return layers + [warp] else: return self.op_encapsulate(layers + [warp])
def test_simpletransform(self): """Test simpletransform API""" import simpletransform self.assertEqual(simpletransform.parseTransform('scale(10)'), [[10, 0, 0], [0, 10, 0]]) self.assertEqual(simpletransform.parseTransform('translate(2,3)'), [[1, 0, 2], [0, 1, 3]]) self.assertEqual( simpletransform.parseTransform('translate(2,3) rotate(90)'), [approx([0, -1, 2]), approx([1, 0, 3])]) m = simpletransform.formatTransform([[0, -1, 2], [1, 0, 3]]) self.assertEqual(re.sub(r',', ' ', re.sub(r'\.0*\b', '', m)), 'matrix(0 1 -1 0 2 3)') self.assertEqual( simpletransform.invertTransform([[1, 0, 2], [0, 1, 3]]), [[1, 0, -2], [0, 1, -3]]) self.assertEqual( simpletransform.composeTransform([[1, 0, 2], [0, 1, 3]], [[0, -1, 0], [1, 0, 0]]), [[0, -1, 2], [1, 0, 3]]) pt = [4, 5] self.assertEqual( simpletransform.applyTransformToPoint([[0, -1, 2], [1, 0, 3]], pt), None) self.assertEqual(pt, [-3, 7]) self.assertEqual(simpletransform.boxunion([3, 5, 2, 4], [4, 6, 1, 3]), (3, 6, 1, 4)) self.assertEqual(simpletransform.cubicExtrema(1, 2, 3, 4), (1, 4)) # TODO need cubic superpath self.assertTrue(simpletransform.applyTransformToPath) self.assertTrue(simpletransform.roughBBox) self.assertTrue(simpletransform.refinedBBox) # TODO need node self.assertTrue(simpletransform.fuseTransform) self.assertTrue(simpletransform.composeParents) self.assertTrue(simpletransform.applyTransformToNode) self.assertTrue(simpletransform.computeBBox) self.assertTrue(simpletransform.computePointInNode)
def renderFoil(self, naca_num, size, twist=0.0): l = len(naca_num) if l == 4: pts = naca.naca4(naca_num, self.options.points, False, True) elif l == 5: pts = naca.naca5(naca_num, self.options.points, False, True) else: #sys.stderr.write("Naca number must be 4 or 5 digits\n") return None, None for i, pt in enumerate(pts): pts[i] = list(pt) upper = [ pts[0:self.options.points+1] ] lower = [ pts[self.options.points:] + [pts[0]] ] beam_x = self.options.beampos/100.0 n_up, pt_up = self.pointAt(upper[0], beam_x) n_low, pt_low = self.pointAt(lower[0], beam_x) beam_y = (pt_up[1] + pt_low[1]) / 2.0 trans = simpletransform.composeTransform( simpletransform.parseTransform("rotate(%f)" % (-1*twist)), [[size,0.0,-beam_x*size], [0.0,size,-beam_y*size]]) for pt in pts: simpletransform.applyTransformToPoint(trans, pt) for pt in pt_up, pt_low: simpletransform.applyTransformToPoint(trans, pt) if self.options.beamtype in [1,2,11,12]: # center beam if self.options.beamtype in [1,2]: # round beam = self.circle(self.options.beamwidth) else: # rectangular beam = self.rectangle(self.options.beamwidth, self.options.beamheight) if self.options.beamtype in [1, 3]: # from above upper = [ upper[0][:n_up] + [ pt_up ], [pt_up, beam[0]], beam, [ beam[-1], pt_up ], [pt_up] + upper[0][n_up :] ] else: # from below beam = self.upsidedown(beam) lower = [ lower[0][:n_low] + [ pt_low ], [pt_low, beam[0]], beam, [beam[-1], pt_low ], [pt_low] + lower[0][n_low:]] elif self.options.beamtype in [3,13]: if self.options.beamtype == 3: beamleft = self.halfcircle(self.options.beamwidth) else: beamleft = self.halfrectangle(self.options.beamwidth, self.options.beamheight) beamright = self.lefttoright(beamleft) beamleft.reverse() upper = [ upper[0][:n_up] + [ pt_up ], [pt_up, beamright[0]], beamright, [beamright[-1], pt_low], [pt_low, beamleft[0]], beamleft, [beamleft[-1], pt_up], [pt_up] + upper[0][n_up :] ] lower = [ lower[0][:n_low] + [ pt_low ], self.move(10,0, [pt_low] + lower[0][n_low:])] for i in xrange(4): upper[i] = self.move(10,0, upper[i]) # add connection below upper[4:4] = [ [upper[3][-1],upper[4][0]]] else: # Surface beam(s) if self.options.beamtype in [5, 7]: # Top beam pass if self.options.beamtype in [6, 7]: # Bottom beam pass if self.options.approach <= 2: # Start with left/leading eadge pts = lower + upper else: pts = upper + lower return pts
def point(self): pos = [float(self.node.get("x", 0)), float(self.node.get("y", 0))] transform = get_node_transform(self.node) simpletransform.applyTransformToPoint(transform, pos) return Point(*pos)
def convert(self, on_progress=None, on_progress_args=None, on_progress_kwargs=None): self.init_output_file() self.parse() options = self.options options['doc_root'] = self.document.getroot() # Get all Gcodetools data from the scene. self.calculate_conversion_matrix() self.collect_paths() for p in self.paths: #print "path", etree.tostring(p) pass def report_progress(on_progress, on_progress_args, on_progress_kwargs, done, total): if (total == 0): total = 1 progress = done / float(total) if on_progress is not None: if on_progress_args is None: on_progress_args = () if on_progress_kwargs is None: on_progress_kwargs = dict() on_progress_kwargs["_progress"] = progress on_progress(*on_progress_args, **on_progress_kwargs) self._log.info("processing %i layers" % len(self.layers)) # sum up itemAmount = 1 for layer in self.layers: if layer in self.paths: itemAmount += len(self.paths[layer]) if layer in self.images: itemAmount += len(self.images[layer]) processedItemCount = 0 report_progress(on_progress, on_progress_args, on_progress_kwargs, processedItemCount, itemAmount) with open(self._tempfile, 'a') as fh: # write comments to gcode gc_options_str = "; gc_nexgen gc_options: {}\n".format( self.gc_options) fh.write(gc_options_str) fh.write("; created:{}\n".format( time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime()))) gc_color_str = "; laser params: {}\n".format(self.colorParams) fh.write(gc_color_str) fh.write(self._get_gcode_header()) # images self._log.info('Raster conversion: %s' % self.options['engrave']) for layer in self.layers: if layer in self.images and self.options['engrave']: for imgNode in self.images[layer]: file_id = imgNode.get('data-serveurl', '') x = imgNode.get('x') y = imgNode.get('y') if x is None: x = "0" if y is None: y = "0" # pt units x = float(x) y = float(y) w = float(imgNode.get("width")) h = float(imgNode.get("height")) _upperLeft = [x, y] _lowerRight = [x + w, y + h] # apply svg transforms _mat = self._get_transforms(imgNode) simpletransform.applyTransformToPoint(_mat, _upperLeft) simpletransform.applyTransformToPoint( _mat, _lowerRight) ### original style with orientation points :( ... TODO # mm conversion upperLeft = self._transform(_upperLeft, layer, False) lowerRight = self._transform(_lowerRight, layer, False) w = abs(lowerRight[0] - upperLeft[0]) h = abs(lowerRight[1] - upperLeft[1]) # contrast = 1.0, sharpening = 1.0, beam_diameter = 0.25, # intensity_black = 1000, intensity_white = 0, speed_black = 30, speed_white = 500, # dithering = True, pierce_time = 500, material = "default" rasterParams = self.options['raster'] ip = ImageProcessor( output_filehandle=fh, contrast=rasterParams['contrast'], sharpening=rasterParams['sharpening'], beam_diameter=rasterParams['beam_diameter'], intensity_black=rasterParams['intensity_black'], intensity_white=rasterParams['intensity_white'], speed_black=rasterParams['speed_black'], speed_white=rasterParams['speed_white'], dithering=rasterParams['dithering'], pierce_time=rasterParams['pierce_time'], material="default") data = imgNode.get('href') if (data is None): data = imgNode.get(_add_ns('href', 'xlink')) if (data.startswith("data:")): ip.dataUrl_to_gcode(data, w, h, upperLeft[0], lowerRight[1], file_id) elif (data.startswith("http://")): ip.imgurl_to_gcode(data, w, h, upperLeft[0], lowerRight[1], file_id) else: self._log.error("Unable to parse img data", data) processedItemCount += 1 report_progress(on_progress, on_progress_args, on_progress_kwargs, processedItemCount, itemAmount) else: self._log.info("postponing non-image layer %s" % (layer.get('id'))) # paths self._log.info('Vector conversion: %s paths' % len(self.paths)) for layer in self.layers: if layer in self.paths: paths_by_color = dict() for path in self.paths[layer]: self._log.info( "path %s, %s, stroke: %s, fill: %s, mb:gc: %s" % (layer.get('id'), path.get('id'), path.get('stroke'), path.get('class'), path.get(_add_ns('gc', 'mb'))[:100])) # if path.get('stroke') is not None: #todo catch None stroke/fill earlier # stroke = path.get('stroke') # elif path.get('fill') is not None: # stroke = path.get('fill') # elif path.get('class') is not None: # stroke = path.get('class') # else: # stroke = 'default' #continue strokeInfo = self._get_stroke(path) #print('strokeInfo:', strokeInfo) if (strokeInfo['visible'] == False): continue stroke = strokeInfo['color'] if "d" not in path.keys(): self._log.error( "Warning: One or more paths don't have 'd' parameter" ) continue if stroke not in paths_by_color.keys( ) and stroke != 'default': paths_by_color[stroke] = [] d = path.get("d") if d != '': paths_by_color[stroke].append(path) # += path processedItemCount += 1 report_progress(on_progress, on_progress_args, on_progress_kwargs, processedItemCount, itemAmount) # curvesD = dict() #diction # for colorKey in paths_by_color.keys(): # if colorKey == 'none': # continue # curvesD[colorKey] = self._parse_curve(paths_by_color[colorKey], layer) #pierce_time = self.options['pierce_time'] layerId = layer.get('id') or '?' pathId = path.get('id') or '?' #for each color generate GCode #for colorKey in curvesD.keys(): for colorKey in paths_by_color.keys(): if colorKey == 'none': continue for path in paths_by_color[colorKey]: print('p', path) curveGCode = "" mbgc = path.get(_add_ns('gc', 'mb'), None) if (mbgc != None): curveGCode = self._use_embedded_gcode( mbgc, colorKey) else: d = path.get('d') csp = cubicsuperpath.parsePath(d) csp = self._apply_transforms(path, csp) curve = self._parse_curve(csp, layer) curveGCode = self._generate_gcode( curve, colorKey) settings = self.colorParams.get( colorKey, { 'intensity': -1, 'feedrate': -1, 'passes': 0, 'pierce_time': 0 }) fh.write("; Layer:" + layerId + ", outline of:" + pathId + ", stroke:" + colorKey + ', ' + str(settings) + "\n") for p in range(0, int(settings['passes'])): fh.write("; pass:%i/%s\n" % (p + 1, settings['passes'])) fh.write(curveGCode) fh.write(self._get_gcode_footer()) self.export_gcode()
def effect( self, hatchSpacing=10, crossHatch=False, hatchAngle=0, tolerance=20, minGap=5, ): ''' crossHatch : Generate a cross hatch pattern hatchAngle : Angle of inclination for hatch lines hatchSpacing : Spacing between hatch lines tolerance : Allowed deviation from original paths minGap : Minimum length of hatch segments and gaps ''' self.options = {} self.options['crossHatch'] = crossHatch self.options['hatchAngle'] = hatchAngle self.options['hatchSpacing'] = hatchSpacing * 90 / 25.4 self.options['tolerance'] = tolerance self.options['minGap'] = minGap # Viewbox handling self.handleViewBox() # Traverse the selected nodes self.recursivelyTraverseSvg(self.nodes, self.docTransform) # Build a grid of possible hatch lines self.makeHatchGrid(float(self.options['hatchAngle']), float(self.options['hatchSpacing']), True) if self.options['crossHatch']: self.makeHatchGrid(float(self.options['hatchAngle'] + 90.0), float(self.options['hatchSpacing']), False) # Now loop over our hatch lines looking for intersections for h in self.grid: interstices((h[0], h[1]), (h[2], h[3]), self.paths, self.hatches, self.minGap) # Target stroke width will be (doc width + doc height) / 2 / 1000 # stroke_width_target = ( self.docHeight + self.docWidth ) / 2000 stroke_width_target = 1 # Each hatch line stroke will be within an SVG object which may # be subject to transforms. So, on an object by object basis, # we need to transform our target width to a width suitable # for that object (so that after the object and its hatches are # transformed, the result has the desired width). # To aid in the process, we use a diagonal line segment of length # stroke_width_target. We then run this segment through an object's # inverse transform and see what the resulting length of the inversely # transformed segment is. We could, alternatively, look at the # x and y scaling factors in the transform and average them. s = stroke_width_target / math.sqrt(2) # Now, dump the hatch fills sorted by which document element # they correspond to. This is made easy by the fact that we # saved the information and used each element's lxml.etree node # pointer as the dictionary key under which to save the hatch # fills for that node. fillings = [] for key in self.hatches: path = '' direction = True if self.transforms.has_key(key): transform = inverseTransform(self.transforms[key]) # Determine the scaled stroke width for a hatch line # We produce a line segment of unit length, transform # its endpoints and then determine the length of the # resulting line segment. pt1 = [0, 0] pt2 = [s, s] simpletransform.applyTransformToPoint(transform, pt1) simpletransform.applyTransformToPoint(transform, pt2) dx = pt2[0] - pt1[0] dy = pt2[1] - pt1[1] stroke_width = math.sqrt(dx * dx + dy * dy) else: transform = None stroke_width = float(1.0) for segment in self.hatches[key]: style = simplestyle.parseStyle(key.get("style")) color = style["fill"] if len(segment) < 2: continue pt1 = segment[0] pt2 = segment[1] # Okay, we're going to put these hatch lines into the same # group as the element they hatch. That element is down # some chain of SVG elements, some of which may have # transforms attached. But, our hatch lines have been # computed assuming that those transforms have already # been applied (since we had to apply them so as to know # where this element is on the page relative to other # elements and their transforms). So, we need to invert # the transforms for this element and then either apply # that inverse transform here and now or set it in a # transform attribute of the <path> element. Having it # set in the path element seems a bit counterintuitive # after the fact (i.e., what's this tranform here for?). # So, we compute the inverse transform and apply it here. if transform != None: simpletransform.applyTransformToPoint(transform, pt1) simpletransform.applyTransformToPoint(transform, pt2) # Now generate the path data for the <path> if direction: # Go this direction path += 'M %f,%f l %f,%f ' % \ ( pt1[0], pt1[1], pt2[0] - pt1[0], pt2[1] - pt1[1] ) else: # Or go this direction path += 'M %f,%f l %f,%f ' % \ ( pt2[0], pt2[1], pt1[0] - pt2[0], pt1[1] - pt2[1] ) direction = not direction fillment = self.joinFillsWithNode(key, stroke_width, color, path[:-1]) fillment['id'] = key.get('id') fillings.append(fillment) #inkex.errormsg("Elapsed CPU time was %f" % (time.clock()-self.t0)) return fillings
def center(self, source_node): xmin, xmax, ymin, ymax = computeBBox([source_node]) point = [(xmax - ((xmax - xmin) / 2)), (ymax - ((ymax - ymin) / 2))] transform = get_node_transform(self.node) applyTransformToPoint(transform, point) return point
def effect(self): #{{{ Check that elements have been selected if len(self.options.ids) == 0: inkex.errormsg(_("Please select objects!")) return #}}} #{{{ Drawing styles linestyle = { 'stroke': '#000000', 'stroke-width': str(self.unittouu('1px')), 'fill': 'none' } facestyle = { 'stroke': '#000000', 'stroke-width':'0px',# str(self.unittouu('1px')), 'fill': 'none' } #}}} #{{{ Handle the transformation of the current group parentGroup = self.getParentNode(self.selected[self.options.ids[0]]) svg = self.document.getroot() children =svg.getchildren() fp=open("log.txt","w") img=None width_in_svg=1 height_in_svg=1 for child in children: if child.tag=="{http://www.w3.org/2000/svg}g": ccc=child.getchildren() for c in ccc: if c.tag=="{http://www.w3.org/2000/svg}image": href=c.attrib["{http://www.w3.org/1999/xlink}href"] fp.write(href) img = Image.open(href) width_in_svg=child.attrib['width'] height_in_svg=child.attrib['height'] elif child.tag=="{http://www.w3.org/2000/svg}image": href=child.attrib["{http://www.w3.org/1999/xlink}href"] width_in_svg=child.attrib['width'] height_in_svg=child.attrib['height'] if "file://" in href: href=href[7:] fp.write(href+"\n") img = Image.open(href).convert("RGB") width=-1 height=-1 if img!=None: imagesize = img.size width=img.size[0] height=img.size[1] fp.write("imageSize="+str(imagesize)) trans = self.getGlobalTransform(parentGroup) invtrans = None if trans: invtrans = self.invertTransform(trans) #}}} #{{{ Recovery of the selected objects pts = [] nodes = [] seeds = [] fp.write('num:'+str(len(self.options.ids))+'\n') for id in self.options.ids: node = self.selected[id] nodes.append(node) if(node.tag=="{http://www.w3.org/2000/svg}path"):#pathだった場合 #パスの頂点座標を取得 points = cubicsuperpath.parsePath(node.get('d')) fp.write(str(points)+"\n") for p in points[0]: pt=[p[1][0],p[1][1]] if trans: simpletransform.applyTransformToPoint(trans, pt) pts.append(Point(pt[0], pt[1])) seeds.append(Point(p[1][0], p[1][1])) else:#その他の図形の場合 bbox = simpletransform.computeBBox([node]) if bbox: cx = 0.5 * (bbox[0] + bbox[1]) cy = 0.5 * (bbox[2] + bbox[3]) pt = [cx, cy] if trans: simpletransform.applyTransformToPoint(trans, pt) pts.append(Point(pt[0], pt[1])) seeds.append(Point(cx, cy)) pts.sort() seeds.sort() fp.write("*******sorted!***********"+str(len(seeds))+"\n") #}}} #{{{ Creation of groups to store the result # Delaunay groupDelaunay = inkex.etree.SubElement(parentGroup, inkex.addNS('g', 'svg')) groupDelaunay.set(inkex.addNS('label', 'inkscape'), 'Delaunay') #}}} scale_x=float(width_in_svg)/float(width) scale_y=float(height_in_svg)/float(height) fp.write('width='+str(width)+', height='+str(height)+'\n') fp.write('scale_x='+str(scale_x)+', scale_y='+str(scale_y)+'\n') #{{{ Voronoi diagram generation triangles = voronoi.computeDelaunayTriangulation(seeds) for triangle in triangles: p1 = seeds[triangle[0]] p2 = seeds[triangle[1]] p3 = seeds[triangle[2]] cmds = [['M', [p1.x, p1.y]], ['L', [p2.x, p2.y]], ['L', [p3.x, p3.y]], ['Z', []]] path = inkex.etree.Element(inkex.addNS('path', 'svg')) path.set('d', simplepath.formatPath(cmds)) middleX=(p1.x+p2.x+p3.x)/3.0/scale_x middleY=(p1.y+p2.y+p3.y)/3.0/scale_y fp.write("x:"+str(middleX)+" y:"+str(middleY)+"\n") if img!=None and imagesize[0]>middleX and imagesize[1]>middleY and middleX>=0 and middleY>=0: r,g,b = img.getpixel((middleX,middleY)) facestyle["fill"]=simplestyle.formatColor3i(r,g,b) else: facestyle["fill"]="black" path.set('style', simplestyle.formatStyle(facestyle)) groupDelaunay.append(path) fp.close()
def path_bounding_box(path, box=None): """Compute the bounding box for an SVG path. :param path: The XML node defining the path. :param box: The existing :class:`bounds.BoundingBox` if available. :return: A :class:`bounds.BoundingBox` encompassing the path. SVG paths are a collection of various types of segments: * Straight lines * Quadratic Bézier curves * Cubic Bézier curves * Elliptical arcs This function splits the path into its segments, calculates the bounding box for each segment and combines them to get the bounding box of the path. If an existing bounding box is given in the ``box`` parameter, it is extended to encompass the path and returned. Otherwise, a new bounding box is created and returned. """ # Get the transform transform = path.get('transform', None) if transform: transform = simpletransform.parseTransform(transform) # Parse the path details. # Note that when parsing all path segments are converted to absolute # coordinates. It also converts H and V segments to L, S segments to C and # T segments to Q. parsed = simplepath.parsePath(path.get('d')) # Starting point current = parsed[0][1] if transform: simpletransform.applyTransformToPoint(transform, current) objbox = BoundingBox(current[0], current[0], current[1], current[1]) # Loop through each segment. for type,params in parsed[1:]: # End of path if type == 'Z': break # Line or move to elif type == 'L' or type == 'M': point = params if transform: simpletransform.applyTransformToPoint(transform, point) objbox.extend(point) current = point # Cubic Bézier curve elif type == 'C': p1 = params[0:2] p2 = params[2:4] p3 = params[4:6] if transform: simpletransform.applyTransformToPoint(transform, p1) simpletransform.applyTransformToPoint(transform, p2) simpletransform.applyTransformToPoint(transform, p3) objbox = cubic_bounding_box(current, p1, p2, p3, objbox) current = p3 # Quadratic Bézier curve elif type == 'Q': p1 = params[0:2] p2 = params[2:4] if transform: simpletransform.applyTransformToPoint(transform, p1) simpletransform.applyTransformToPoint(transform, p2) objbox = quadratic_bounding_box(current, p1, p2, objbox) current = p2 # Elliptical arc elif type == 'A': rx, ry, rotation, large_arc, sweep = params[0:5] end = params[5:7] if transform: simpletransform.applyTransformToPoint(transform, end) objbox = elliptical_arc_bounding_box(current, rx, ry, rotation, large_arc, sweep, end, objbox) current = end # Unknown segment type else: raise Exception(_('Unknown path segment type %s.' % type)) # Returnt the appropriate box if box is None: return objbox else: return box.combine(objbox)
def effect( self , hatchSpacing = 10, crossHatch = False, hatchAngle = 0, tolerance = 20, minGap = 5, ): ''' crossHatch : Generate a cross hatch pattern hatchAngle : Angle of inclination for hatch lines hatchSpacing : Spacing between hatch lines tolerance : Allowed deviation from original paths minGap : Minimum length of hatch segments and gaps ''' self.options = {} self.options['crossHatch'] = crossHatch self.options['hatchAngle'] = hatchAngle self.options['hatchSpacing'] = hatchSpacing * 90/25.4 self.options['tolerance'] = tolerance self.options['minGap'] = minGap # Viewbox handling self.handleViewBox() # Traverse the selected nodes self.recursivelyTraverseSvg( self.nodes, self.docTransform ) # Build a grid of possible hatch lines self.makeHatchGrid( float( self.options['hatchAngle'] ), float( self.options['hatchSpacing'] ), True ) if self.options['crossHatch']: self.makeHatchGrid( float( self.options['hatchAngle'] + 90.0 ), float( self.options['hatchSpacing'] ), False ) # Now loop over our hatch lines looking for intersections for h in self.grid: interstices( (h[0], h[1]), (h[2], h[3]), self.paths, self.hatches, self.minGap ) # Target stroke width will be (doc width + doc height) / 2 / 1000 # stroke_width_target = ( self.docHeight + self.docWidth ) / 2000 stroke_width_target = 1 # Each hatch line stroke will be within an SVG object which may # be subject to transforms. So, on an object by object basis, # we need to transform our target width to a width suitable # for that object (so that after the object and its hatches are # transformed, the result has the desired width). # To aid in the process, we use a diagonal line segment of length # stroke_width_target. We then run this segment through an object's # inverse transform and see what the resulting length of the inversely # transformed segment is. We could, alternatively, look at the # x and y scaling factors in the transform and average them. s = stroke_width_target / math.sqrt( 2 ) # Now, dump the hatch fills sorted by which document element # they correspond to. This is made easy by the fact that we # saved the information and used each element's lxml.etree node # pointer as the dictionary key under which to save the hatch # fills for that node. fillings = [] for key in self.hatches: path = '' direction = True if self.transforms.has_key( key ): transform = inverseTransform( self.transforms[key] ) # Determine the scaled stroke width for a hatch line # We produce a line segment of unit length, transform # its endpoints and then determine the length of the # resulting line segment. pt1 = [0, 0] pt2 = [s, s] simpletransform.applyTransformToPoint( transform, pt1 ) simpletransform.applyTransformToPoint( transform, pt2 ) dx = pt2[0] - pt1[0] dy = pt2[1] - pt1[1] stroke_width = math.sqrt( dx * dx + dy * dy ) else: transform = None stroke_width = float( 1.0 ) for segment in self.hatches[key]: style = simplestyle.parseStyle(key.get("style")) color = style["fill"] if len( segment ) < 2: continue pt1 = segment[0] pt2 = segment[1] # Okay, we're going to put these hatch lines into the same # group as the element they hatch. That element is down # some chain of SVG elements, some of which may have # transforms attached. But, our hatch lines have been # computed assuming that those transforms have already # been applied (since we had to apply them so as to know # where this element is on the page relative to other # elements and their transforms). So, we need to invert # the transforms for this element and then either apply # that inverse transform here and now or set it in a # transform attribute of the <path> element. Having it # set in the path element seems a bit counterintuitive # after the fact (i.e., what's this tranform here for?). # So, we compute the inverse transform and apply it here. if transform != None: simpletransform.applyTransformToPoint( transform, pt1 ) simpletransform.applyTransformToPoint( transform, pt2 ) # Now generate the path data for the <path> if direction: # Go this direction path += 'M %f,%f l %f,%f ' % \ ( pt1[0], pt1[1], pt2[0] - pt1[0], pt2[1] - pt1[1] ) else: # Or go this direction path += 'M %f,%f l %f,%f ' % \ ( pt2[0], pt2[1], pt1[0] - pt2[0], pt1[1] - pt2[1] ) direction = not direction fillment = self.joinFillsWithNode( key, stroke_width, color, path[:-1] ) fillment['id'] = key.get('id') fillings.append(fillment) #inkex.errormsg("Elapsed CPU time was %f" % (time.clock()-self.t0)) return fillings
def t(point): p = point[:] simpletransform.applyTransformToPoint(mat, p) return p
def applyTransformToRegions(mtx, regions): for poly in regions: for pt in poly: applyTransformToPoint(mtx, pt)
def exportEdgeCut(self, kicad_mod=False): x0 = 0 y0 = 0 mirror = 1.0 line_type = "fp_line" if kicad_mod else "gr_line" kicad_edgecut_string = "" i = 0 layerPath = '//svg:g[@inkscape:groupmode="layer"]' if (self.options.autoflatten): self.flatten_bezier() for layer in self.document.getroot().xpath(layerPath, namespaces=inkex.NSS): i += 1 label_attrib_name = "{%s}label" % layer.nsmap['inkscape'] if label_attrib_name not in layer.attrib: continue layer_name = (layer.attrib[label_attrib_name]) if layer_name != "Edge.Cuts": continue layer_trans = layer.get('transform') if layer_trans: layer_m = simpletransform.parseTransform(layer_trans) else: layer_m = IDENTITY_MATRIX nodePath = ( '//svg:g[@inkscape:groupmode="layer"][%d]/descendant::svg:path' ) % i for node in self.document.getroot().xpath(nodePath, namespaces=inkex.NSS): d = node.get('d') p = simplepath.parsePath(d) points = [] if p: #sanity check if p[0][0] == 'M': t = node.get('transform') if t: m = simpletransform.parseTransform(t) trans = simpletransform.composeTransform( layer_m, m) else: trans = layer_m for path in p: if path[0] != "Z": x = (path[1][0]) y = (path[1][1]) xy = [x, y] simpletransform.applyTransformToPoint( trans, xy) points.append( self.coordToKicad([(xy[0] - x0), xy[1] * mirror - y0])) points_count = len(points) points.append(points[0]) for x in range(0, points_count): kicad_edgecut_string = kicad_edgecut_string + ( "(%s (start %f %f) (end %f %f) (layer Edge.Cuts) (width 0.1))\n" % (line_type, points[x][0], points[x][1], points[x + 1][0], points[x + 1][1])) return kicad_edgecut_string
def effect(self): # Viewbox handling self.handleViewBox() # Build a list of the vertices for the document's graphical elements if self.options.ids: # Traverse the selected objects for id in self.options.ids: self.recursivelyTraverseSvg([self.selected[id]], self.docTransform) else: # Traverse the entire document self.recursivelyTraverseSvg(self.document.getroot(), self.docTransform) # Build a grid of possible hatch lines self.makeHatchGrid(float(self.options.hatchAngle), float(self.options.hatchSpacing), True) if self.options.crossHatch: self.makeHatchGrid(float(self.options.hatchAngle + 90.0), float(self.options.hatchSpacing), False) # Now loop over our hatch lines looking for intersections for h in self.grid: interstices((h[0], h[1]), (h[2], h[3]), self.paths, self.hatches, self.minGap) # Target stroke width will be (doc width + doc height) / 2 / 1000 # stroke_width_target = ( self.docHeight + self.docWidth ) / 2000 stroke_width_target = 1 # Each hatch line stroke will be within an SVG object which may # be subject to transforms. So, on an object by object basis, # we need to transform our target width to a width suitable # for that object (so that after the object and its hatches are # transformed, the result has the desired width). # To aid in the process, we use a diagonal line segment of length # stroke_width_target. We then run this segment through an object's # inverse transform and see what the resulting length of the inversely # transformed segment is. We could, alternatively, look at the # x and y scaling factors in the transform and average them. s = stroke_width_target / math.sqrt(2) # Now, dump the hatch fills sorted by which document element # they correspond to. This is made easy by the fact that we # saved the information and used each element's lxml.etree node # pointer as the dictionary key under which to save the hatch # fills for that node. for key in self.hatches: path = '' direction = True if self.transforms.has_key(key): transform = inverseTransform(self.transforms[key]) # Determine the scaled stroke width for a hatch line # We produce a line segment of unit length, transform # its endpoints and then determine the length of the # resulting line segment. pt1 = [0, 0] pt2 = [s, s] simpletransform.applyTransformToPoint(transform, pt1) simpletransform.applyTransformToPoint(transform, pt2) dx = pt2[0] - pt1[0] dy = pt2[1] - pt1[1] stroke_width = math.sqrt(dx * dx + dy * dy) else: transform = None stroke_width = float(1.0) for segment in self.hatches[key]: if len(segment) < 2: continue pt1 = segment[0] pt2 = segment[1] # Okay, we're going to put these hatch lines into the same # group as the element they hatch. That element is down # some chain of SVG elements, some of which may have # transforms attached. But, our hatch lines have been # computed assuming that those transforms have already # been applied (since we had to apply them so as to know # where this element is on the page relative to other # elements and their transforms). So, we need to invert # the transforms for this element and then either apply # that inverse transform here and now or set it in a # transform attribute of the <path> element. Having it # set in the path element seems a bit counterintuitive # after the fact (i.e., what's this tranform here for?). # So, we compute the inverse transform and apply it here. if transform != None: simpletransform.applyTransformToPoint(transform, pt1) simpletransform.applyTransformToPoint(transform, pt2) # Now generate the path data for the <path> if direction: # Go this direction path += 'M %f,%f l %f,%f ' % \ ( pt1[0], pt1[1], pt2[0] - pt1[0], pt2[1] - pt1[1] ) else: # Or go this direction path += 'M %f,%f l %f,%f ' % \ ( pt2[0], pt2[1], pt1[0] - pt2[0], pt1[1] - pt2[1] ) direction = not direction self.joinFillsWithNode(key, stroke_width, path[:-1])
def path_to_bline_list(path_d, nodetypes=None, mtx=[[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]]): """ Convert a path to a BLine List bline_list format: Vertex: [[tg1x, tg1y], [x,y], [tg2x, tg2y], split = T/F] Vertex list: [ vertex, vertex, vertex, ...] Bline: { "points" : vertex_list, "loop" : True / False } """ # Exit on empty paths if not path_d: return [] # Parse the path path = simplepath.parsePath(path_d) # Append (more than) enough c's to the nodetypes if nodetypes is None: nt = "" else: nt = nodetypes for _ in range(len(path)): nt += "c" # Create bline list # borrows code from cubicsuperpath.py # bline_list := [bline, bline, ...] # bline := { # "points":[vertex, vertex, ...], # "loop":True/False, # } bline_list = [] subpathstart = [] last = [] lastctrl = [] lastsplit = True for s in path: cmd, params = s if cmd != "M" and bline_list == []: raise MalformedSVGError, "Bad path data: path doesn't start with moveto, %s, %s" % (s, path) elif cmd == "M": # Add previous point to subpath if last: bline_list[-1]["points"].append([lastctrl[:], last[:], last[:], lastsplit]) # Start a new subpath bline_list.append({"nodetypes": "", "loop": False, "points": []}) # Save coordinates of this point subpathstart = params[:] last = params[:] lastctrl = params[:] lastsplit = False if nt[0] == "z" else True nt = nt[1:] elif cmd == "L": bline_list[-1]["points"].append([lastctrl[:], last[:], last[:], lastsplit]) last = params[:] lastctrl = params[:] lastsplit = False if nt[0] == "z" else True nt = nt[1:] elif cmd == "C": bline_list[-1]["points"].append([lastctrl[:], last[:], params[:2], lastsplit]) last = params[-2:] lastctrl = params[2:4] lastsplit = False if nt[0] == "z" else True nt = nt[1:] elif cmd == "Q": q0 = last[:] q1 = params[0:2] q2 = params[2:4] x0 = q0[0] x1 = 1.0 / 3 * q0[0] + 2.0 / 3 * q1[0] x2 = 2.0 / 3 * q1[0] + 1.0 / 3 * q2[0] x3 = q2[0] y0 = q0[1] y1 = 1.0 / 3 * q0[1] + 2.0 / 3 * q1[1] y2 = 2.0 / 3 * q1[1] + 1.0 / 3 * q2[1] y3 = q2[1] bline_list[-1]["points"].append([lastctrl[:], [x0, y0], [x1, y1], lastsplit]) last = [x3, y3] lastctrl = [x2, y2] lastsplit = False if nt[0] == "z" else True nt = nt[1:] elif cmd == "A": arcp = cubicsuperpath.ArcToPath(last[:], params[:]) arcp[0][0] = lastctrl[:] last = arcp[-1][1] lastctrl = arcp[-1][0] lastsplit = False if nt[0] == "z" else True nt = nt[1:] for el in arcp[:-1]: el.append(True) bline_list[-1]["points"].append(el) elif cmd == "Z": if len(bline_list[-1]["points"]) == 0: # If the path "loops" after only one point # e.g. "M 0 0 Z" bline_list[-1]["points"].append([lastctrl[:], last[:], last[:], False]) elif last == subpathstart: # If we are back to the original position # merge our tangent into the first point bline_list[-1]["points"][0][0] = lastctrl[:] else: # Otherwise draw a line to the starting point bline_list[-1]["points"].append([lastctrl[:], last[:], last[:], lastsplit]) # Clear the variables (no more points need to be added) last = [] lastctrl = [] lastsplit = True # Loop the subpath bline_list[-1]["loop"] = True # Append final superpoint, if needed if last: bline_list[-1]["points"].append([lastctrl[:], last[:], last[:], lastsplit]) # Apply the transformation if mtx != [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]]: for bline in bline_list: for vertex in bline["points"]: for pt in vertex: if type(pt) != bool: simpletransform.applyTransformToPoint(mtx, pt) return bline_list
def effect( self ): # Viewbox handling self.handleViewBox() # Build a list of the vertices for the document's graphical elements if self.options.ids: # Traverse the selected objects for id in self.options.ids: self.recursivelyTraverseSvg( [self.selected[id]], self.docTransform ) else: # Traverse the entire document self.recursivelyTraverseSvg( self.document.getroot(), self.docTransform ) # Build a grid of possible hatch lines self.makeHatchGrid( float( self.options.hatchAngle ), float( 90 / self.options.hatchSpacing), True ) if self.options.crossHatch: self.makeHatchGrid( float( self.options.hatchAngle + 90.0 ), float( 90 / self.options.hatchSpacing), False ) # Now loop over our hatch lines looking for intersections for h in self.grid: interstices( (h[0], h[1]), (h[2], h[3]), self.paths, self.hatches, self.minGap ) # Target stroke width will be (doc width + doc height) / 2 / 1000 # stroke_width_target = ( self.docHeight + self.docWidth ) / 2000 stroke_width_target = 1 # Each hatch line stroke will be within an SVG object which may # be subject to transforms. So, on an object by object basis, # we need to transform our target width to a width suitable # for that object (so that after the object and its hatches are # transformed, the result has the desired width). # To aid in the process, we use a diagonal line segment of length # stroke_width_target. We then run this segment through an object's # inverse transform and see what the resulting length of the inversely # transformed segment is. We could, alternatively, look at the # x and y scaling factors in the transform and average them. s = stroke_width_target / math.sqrt( 2 ) # Now, dump the hatch fills sorted by which document element # they correspond to. This is made easy by the fact that we # saved the information and used each element's lxml.etree node # pointer as the dictionary key under which to save the hatch # fills for that node. for key in self.hatches: path = '' direction = True if self.transforms.has_key( key ): transform = inverseTransform( self.transforms[key] ) # Determine the scaled stroke width for a hatch line # We produce a line segment of unit length, transform # its endpoints and then determine the length of the # resulting line segment. pt1 = [0, 0] pt2 = [s, s] simpletransform.applyTransformToPoint( transform, pt1 ) simpletransform.applyTransformToPoint( transform, pt2 ) dx = pt2[0] - pt1[0] dy = pt2[1] - pt1[1] stroke_width = math.sqrt( dx * dx + dy * dy ) else: transform = None stroke_width = float( 1.0 ) for segment in self.hatches[key]: if len( segment ) < 2: continue pt1 = segment[0] pt2 = segment[1] # Okay, we're going to put these hatch lines into the same # group as the element they hatch. That element is down # some chain of SVG elements, some of which may have # transforms attached. But, our hatch lines have been # computed assuming that those transforms have already # been applied (since we had to apply them so as to know # where this element is on the page relative to other # elements and their transforms). So, we need to invert # the transforms for this element and then either apply # that inverse transform here and now or set it in a # transform attribute of the <path> element. Having it # set in the path element seems a bit counterintuitive # after the fact (i.e., what's this tranform here for?). # So, we compute the inverse transform and apply it here. if transform != None: simpletransform.applyTransformToPoint( transform, pt1 ) simpletransform.applyTransformToPoint( transform, pt2 ) # Now generate the path data for the <path> if direction: # Go this direction path += 'M %f,%f l %f,%f ' % \ ( pt1[0], pt1[1], pt2[0] - pt1[0], pt2[1] - pt1[1] ) else: # Or go this direction path += 'M %f,%f l %f,%f ' % \ ( pt2[0], pt2[1], pt1[0] - pt2[0], pt1[1] - pt2[1] ) direction = not direction self.joinFillsWithNode( key, stroke_width, path[:-1] )
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: unit = self.getUnit(style.get('stroke-width').strip()) stroke_width = self.getVal(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) / 2) style['stroke-width'] = self.valWithUnit(stroke_width, unit) update = True 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 == inkex.addNS('polygon', 'svg'): points = node.get('points') points = points.strip().split(' ') for k, p in enumerate(points): p = p.split(',') unit = self.getUnit(p[0]) p = [self.getVal(p[0]), self.getVal(p[1])] applyTransformToPoint(transf, p) p = [ self.valWithUnit(p[0], unit), self.valWithUnit(p[1], unit) ] p = ','.join(p) points[k] = p points = ' '.join(points) node.set('points', points) # if there is cx, there is also cy if 'cx' in node.attrib: cx = node.get('cx') cy = node.get('cy') unit = self.getUnit(cx) pt = [self.getVal(cx), self.getVal(cy)] applyTransformToPoint(transf, pt) node.set('cx', self.valWithUnit(pt[0], unit)) node.set('cy', self.valWithUnit(pt[1], unit)) if 'r' in node.attrib: unit = self.getUnit(node.get('r')) r = self.getVal(node.get('r')) # this is a circle: is the scale uniform? if transf[0][0] == transf[1][1]: r *= abs(transf[0][0]) node.set('r', self.valWithUnit(r, unit)) else: # transform is not uniform: go from circle to ellipse # NOTE!!! Inkscape currently applies this particular transform as soon as we touch the object. # therefore rx and ry are both assigned to r, otherwise the scaling is applied two times! # this is kind of a bug of the implementation rx = r #*abs(transf[0][0]) ry = r #*abs(transf[1][1]) node.set('rx', self.valWithUnit(rx, unit)) node.set('ry', self.valWithUnit(ry, unit)) del node.attrib['r'] node.tag = inkex.addNS('ellipse', 'svg') if 'rx' in node.attrib: unit = self.getUnit(node.get('rx')) rx = self.getVal(node.get('rx')) * abs(transf[0][0]) ry = self.getVal(node.get('ry')) * abs(transf[1][1]) node.set('rx', self.valWithUnit(rx, unit)) node.set('ry', self.valWithUnit(ry, unit)) if 'x' in node.attrib: unit = self.getUnit(node.get('x')) x = self.getVal(node.get('x')) * transf[0][0] y = self.getVal(node.get('y')) * transf[1][1] node.set('x', self.valWithUnit(x, unit)) node.set('y', self.valWithUnit(y, unit)) if 'width' in node.attrib: unit = self.getUnit(node.get('width')) w = self.getVal(node.get('width')) * transf[0][0] h = self.getVal(node.get('height')) * transf[1][1] if w < 0: xUnit = self.getUnit(node.get('x')) x = self.getVal(node.get('x')) x += w w = -w node.set('x', self.valWithUnit(x, xUnit)) if h < 0: yUnit = self.getUnit(node.get('y')) y = self.getVal(node.get('y')) y += h h = -h node.set('y', self.valWithUnit(y, yUnit)) node.set('width', self.valWithUnit(w, unit)) node.set('height', self.valWithUnit(h, unit)) for child in node.getchildren(): self.recursiveFuseTransform(child, transf)
def effect(self): #{{{ Check that elements have been selected if len(self.options.ids) == 0: inkex.errormsg(_("Please select objects!")) return #}}} #{{{ Drawing styles linestyle = { 'stroke' : '#000000', 'stroke-width' : str(self.unittouu('1px')), 'fill' : 'none' } facestyle = { 'stroke' : '#ff0000', 'stroke-width' : str(self.unittouu('1px')), 'fill' : 'none' } #}}} #{{{ Handle the transformation of the current group parentGroup = self.getParentNode(self.selected[self.options.ids[0]]) trans = self.getGlobalTransform(parentGroup) invtrans = None if trans: invtrans = simpletransform.invertTransform(trans) #}}} #{{{ Recovery of the selected objects pts = [] nodes = [] seeds = [] for id in self.options.ids: node = self.selected[id] nodes.append(node) bbox = simpletransform.computeBBox([node]) if bbox: cx = 0.5*(bbox[0]+bbox[1]) cy = 0.5*(bbox[2]+bbox[3]) pt = [cx,cy] if trans: simpletransform.applyTransformToPoint(trans,pt) pts.append(Point(pt[0],pt[1])) seeds.append(Point(cx,cy)) #}}} #{{{ Creation of groups to store the result if self.options.diagramType != 'Delaunay': # Voronoi groupVoronoi = inkex.etree.SubElement(parentGroup,inkex.addNS('g','svg')) groupVoronoi.set(inkex.addNS('label', 'inkscape'), 'Voronoi') if invtrans: simpletransform.applyTransformToNode(invtrans,groupVoronoi) if self.options.diagramType != 'Voronoi': # Delaunay groupDelaunay = inkex.etree.SubElement(parentGroup,inkex.addNS('g','svg')) groupDelaunay.set(inkex.addNS('label', 'inkscape'), 'Delaunay') #}}} #{{{ Clipping box handling if self.options.diagramType != 'Delaunay': #Clipping bounding box creation gBbox = simpletransform.computeBBox(nodes) #Clipbox is the box to which the Voronoi diagram is restricted clipBox = () if self.options.clipBox == 'Page': svg = self.document.getroot() w = self.unittouu(svg.get('width')) h = self.unittouu(svg.get('height')) clipBox = (0,w,0,h) else: clipBox = (2*gBbox[0]-gBbox[1], 2*gBbox[1]-gBbox[0], 2*gBbox[2]-gBbox[3], 2*gBbox[3]-gBbox[2]) #Safebox adds points so that no Voronoi edge in clipBox is infinite safeBox = (2*clipBox[0]-clipBox[1], 2*clipBox[1]-clipBox[0], 2*clipBox[2]-clipBox[3], 2*clipBox[3]-clipBox[2]) pts.append(Point(safeBox[0],safeBox[2])) pts.append(Point(safeBox[1],safeBox[2])) pts.append(Point(safeBox[1],safeBox[3])) pts.append(Point(safeBox[0],safeBox[3])) if self.options.showClipBox: #Add the clip box to the drawing rect = inkex.etree.SubElement(groupVoronoi,inkex.addNS('rect','svg')) rect.set('x',str(clipBox[0])) rect.set('y',str(clipBox[2])) rect.set('width',str(clipBox[1]-clipBox[0])) rect.set('height',str(clipBox[3]-clipBox[2])) rect.set('style',simplestyle.formatStyle(linestyle)) #}}} #{{{ Voronoi diagram generation if self.options.diagramType != 'Delaunay': vertices,lines,edges = voronoi.computeVoronoiDiagram(pts) for edge in edges: line = edge[0] vindex1 = edge[1] vindex2 = edge[2] if (vindex1 <0) or (vindex2 <0): continue # infinite lines have no need to be handled in the clipped box else: segment = self.clipEdge(vertices,lines,edge,clipBox) #segment = [vertices[vindex1],vertices[vindex2]] # deactivate clipping if len(segment)>1: v1 = segment[0] v2 = segment[1] cmds = [['M',[v1[0],v1[1]]],['L',[v2[0],v2[1]]]] path = inkex.etree.Element(inkex.addNS('path','svg')) path.set('d',simplepath.formatPath(cmds)) path.set('style',simplestyle.formatStyle(linestyle)) groupVoronoi.append(path) if self.options.diagramType != 'Voronoi': triangles = voronoi.computeDelaunayTriangulation(seeds) for triangle in triangles: p1 = seeds[triangle[0]] p2 = seeds[triangle[1]] p3 = seeds[triangle[2]] cmds = [['M',[p1.x,p1.y]], ['L',[p2.x,p2.y]], ['L',[p3.x,p3.y]], ['Z',[]]] path = inkex.etree.Element(inkex.addNS('path','svg')) path.set('d',simplepath.formatPath(cmds)) path.set('style',simplestyle.formatStyle(facestyle)) groupDelaunay.append(path)
def process_shape(self, node, mat): nodetype = node.get(inkex.addNS("type","sodipodi")) if nodetype == "arc": # These are actually only used for checking the maths ui_arc = True ui_cx = float(node.get(inkex.addNS('cx','sodipodi'))) ui_cy = float(node.get(inkex.addNS('cy','sodipodi'))) ui_r = float(node.get(inkex.addNS('r','sodipodi'),0.0)) ui_rx = float(node.get(inkex.addNS('rx','sodipodi'),ui_r)) ui_ry = float(node.get(inkex.addNS('ry','sodipodi'),ui_r)) ui_a0 = float(node.get(inkex.addNS('start','sodipodi'),0)) ui_a1 = float(node.get(inkex.addNS('end','sodipodi'),2*math.pi)) else: ui_arc = False 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 trans = node.get('transform') if trans: mat = simpletransform.composeTransform(mat, simpletransform.parseTransform(trans)) if node.tag == inkex.addNS('path','svg'): d = node.get('d') if not d: inkex.errormsg("PATH DATA MISSING!") inkex.sys.exit() return # Filter out any eliptical arcs for special treatment: simplep = simplepath.parsePath(d) split = split_arc_nonarc(simplep) arc_simplep = split[1] simplep = split[0] if len(simplep)>0: if (simplep[-1][0] == 'z' or simplep[-1][0] == 'Z'): self.closed = 1 p = cubicsuperpath.CubicSuperPath(simplep) if (self.options.FLATTENBES): cspsubdiv.cspsubdiv(p, self.options.flat) np = [] for sp in p: first = True for csp in sp: cmd = 'L' if first: cmd = 'M' first = False np.append([cmd,[csp[1][0],csp[1][1]]]) p = cubicsuperpath.parsePath(simplepath.formatPath(np)) 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]]) # Now process any arc segments: if len(arc_simplep) > 0: # As our path is broken by arcs, we cannot have a closed polyline: self.closed = 0 i = 0 while i < len(arc_simplep): cmd, params = arc_simplep[i] if cmd == 'M': p0 = params[:] else: p1 = params[-2:] rx,ry,theta,largearc,sweep = params[0:5] e_params = convert_arc_abrxry0_to_crxry00102(p0,p1,rx,ry,theta,largearc==1,sweep==1) cx,cy = e_params[0] if (i<len(arc_simplep)-1): cmd2, params2 = arc_simplep[i+1] else: cmd2 = '-' if cmd2 == 'A' and params2[0:5] == params[0:5] and params2[-2:] == p0: # complete circle or ellipse a0 = 0 a1 = 2.0*math.pi i = i + 1 p1 = p0 else: a0 = e_params[4] a1 = e_params[5] p0 = p1 self.dxf_arc_transform(mat,cx,cy,rx,ry,theta,a0,a1) # check did we get our maths right? if ui_arc and ((abs(cx - ui_cx) > 0.05) or (abs(cy - ui_cy) > 0.05) or (abs(a0 - ui_a0)>0.1) or (abs(a1 - ui_a1)>0.1)): inkex.errormsg("WARNING, Maths failure. Stored attributes of arc and calculated values do not agree!:") inkex.errormsg("sodipodi:\tc=[%f,%f],r=[%f,%f],a0=%fpi,a1=%fpi" % (ui_cx,ui_cy,ui_rx,ui_ry,ui_a0/math.pi,ui_a1/math.pi)) inkex.errormsg("raw:\tc=[%f,%f],r=[%f,%f],a0=%fpi,a1=%fpi" % (cx,cy,rx,ry,a0/math.pi,a1/math.pi)) i = i+1 return elif node.tag in [ inkex.addNS('circle','svg'), 'circle', \ inkex.addNS('ellipse','svg'), 'ellipse' ]: cx = float(node.get('cx',0)) cy = float(node.get('cy',0)) if node.tag == inkex.addNS('circle','svg'): rx = float(node.get('r',0)) ry = rx else: rx = float(node.get('rx',0)) ry = float(node.get('ry',rx)) a0 = 0.0 a1 = 2*math.pi self.dxf_arc_transform(mat,cx,cy,rx,ry,0,a0,a1) return 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')) pt0 = [x,y] pt1 = [x + width, y] pt2 = [x + width, y + height] pt3 = [x, y + height] simpletransform.applyTransformToPoint(mat,pt0) simpletransform.applyTransformToPoint(mat,pt1) simpletransform.applyTransformToPoint(mat,pt2) simpletransform.applyTransformToPoint(mat,pt3) if (self.options.POLY == 'true'): self.LWPOLY_line([pt0,pt1]) self.LWPOLY_line([pt1,pt2]) self.LWPOLY_line([pt2,pt3]) self.LWPOLY_line([pt3,pt0]) else: self.dxf_line([pt0,pt1]) self.dxf_line([pt1,pt2]) self.dxf_line([pt2,pt3]) self.dxf_line([pt3,pt0]) return else: return