def realistic_stitch(start, end): """Generate a stitch vector path given a start and end point.""" end = Point(*end) start = Point(*start) stitch_length = (end - start).length() stitch_center = Point((end.x + start.x) / 2.0, (end[1] + start[1]) / 2.0) stitch_direction = (end - start) stitch_angle = math.atan2(stitch_direction.y, stitch_direction.x) * (180 / pi) stitch_length = max(0, stitch_length - 0.2 * PIXELS_PER_MM) # create the path by filling in the length in the template path = inkex.Path(stitch_path % stitch_length).to_arrays() # rotate the path to match the stitch rotation_center_x = -stitch_length / 2.0 rotation_center_y = stitch_height / 2.0 path = inkex.Path(path).rotate(stitch_angle, (rotation_center_x, rotation_center_y)) # move the path to the location of the stitch path = inkex.Path(path).translate(stitch_center.x - rotation_center_x, stitch_center.y - rotation_center_y) return str(path)
def mesh_to_grid(corners, hlines, vlines): """Construct mesh grid with CSP paths.""" rows = len(corners) - 1 cols = len(corners[0]) - 1 gridline_csps = [] # horizontal path = 'M {},{}'.format(*corners[0][0]) for edge_path in hlines[0]: path = ' '.join([path, edge_path]) gridline_csps.append(inkex.Path(path).to_superpath()) for i in range(1, rows + 1): path = 'M {},{}'.format(*corners[i][-1]) for edge_path in reversed(hlines[i]): path = ' '.join([path, edge_path]) gridline_csps.append(inkex.Path(path).to_superpath()) # vertical path = 'M {},{}'.format(*corners[-1][0]) for edge_path in reversed(vlines[0]): path = ' '.join([path, edge_path]) gridline_csps.append(inkex.Path(path).to_superpath()) for j in range(1, cols + 1): path = 'M {},{}'.format(*corners[0][j]) for edge_path in vlines[j]: path = ' '.join([path, edge_path]) gridline_csps.append(inkex.Path(path).to_superpath()) return gridline_csps
def drawCrossHairs(self, layer, x, y, style = None, rad = 10): # create crosshair group group = etree.SubElement(layer, inkex.addNS('g','svg')) # add circle (actually diamond...) if style: circle = etree.SubElement(group,inkex.addNS('path','svg')) circle.set('style', style) circleLine = [ ['M',[x-rad/2,y]], ['L',[x,y-rad/2]], ['L',[x+rad/2,y]], ['L',[x,y+rad/2]], ['Z',[]] ] circle.set('d', str(inkex.Path(circleLine))) # add crosshairs crosshairX = etree.SubElement(group,inkex.addNS('path','svg')) crosshairY = etree.SubElement(group,inkex.addNS('path','svg')) chxLine = [['M',[x,y-rad]],['L',[x,y+rad]]] chyLine = [['M',[x-rad,y]],['L',[x+rad,y]]] crosshairX.set('d', str(inkex.Path(chxLine))) crosshairX.set('style', 'stroke:#CC0000;stroke-width:1.0px;') crosshairY.set('d', str(inkex.Path(chyLine))) crosshairY.set('style', 'stroke:#CC0000;stroke-width:1.0px;')
def effect(self): zoom = self.svg.unittouu(str(self.options.zoom) + 'px') if self.options.randomize: imagelist = generate_random_string(self.options.text, zoom) else: tokens = tokenize(self.options.text) imagelist = randomize_input_string(tokens, zoom) image = layoutstring(imagelist, zoom) if image: s = {'stroke': 'none', 'fill': '#000000'} new = inkex.PathElement(style=str(inkex.Style(s)), d=str(inkex.Path(image))) layer = self.svg.get_current_layer() layer.append(new) # compensate preserved transforms of parent layer if layer.getparent() is not None: mat = ( self.svg.get_current_layer().transform * inkex.Transform([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]])).matrix new.transform *= -inkex.Transform(mat)
def flatten_bezier(self): layerPath = '//svg:g[@inkscape:groupmode="layer"][@inkscape:label="Edge.Cuts"]' for layer in self.document.getroot().xpath(layerPath, namespaces=inkex.NSS): nodePath = 'descendant::svg:path' for node in layer.xpath(nodePath, namespaces=inkex.NSS): if node.tag == inkex.addNS('path','svg'): d = node.get('d') p = inkex.Path(d).to_superpath() bezier.cspsubdiv(p, 0.01) 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]]]) node.set('d', str(inkex.Path(np)))
def fuse_subpaths(path_node): """Fuse subpaths of a path. Should only be used on unstroked paths""" path = path_node.path.to_arrays() if len(path) == 0: return i = 0 initial_point = [path[i][1][-2], path[i][1][-1]] prev_end = initial_point[:] return_stack = [] while i < len(path): # Remove any terminators: they are redundant if path[i][0] == "Z": path.remove(["Z", []]) continue if path[i][0] == 'V': prev_end[0] = path[i][1][0] i += 1 continue elif path[i][0] == 'H': prev_end[1] = path[i][1][0] i += 1 continue elif path[1][0] != 'M' or i == 0: prev_end = path[i][1][-2:] i += 1 continue # This element begins a new path - it should be a moveto assert (path[i][0] == 'M') # Swap it for a lineto path[i][0] = 'L' # If the old subpath has not been closed yet, close it if prev_end != initial_point: path.insert(i, ['L', initial_point]) i += 1 # Set the initial point of this subpath initial_point = path[i][1][-2:] # Append this point to the return stack return_stack.append(initial_point) # end while # Now pop the entire return stack while return_stack: el = ['L', return_stack.pop()] path.insert(i, el) i += 1 path_d = str(inkex.Path(path)) path_node.set("d", path_d)
def plot_beam(self, beam: List[Ray], node: inkex.ShapeElement) -> None: path = inkex.Path() if len(beam) > 0: path += [Move(beam[0].origin[0], beam[0].origin[1])] for ray in beam: p1 = ray.origin + ray.travel * ray.direction path += [Line(p1[0], p1[1])] element = self._beam_layer.add(inkex.PathElement()) # Need to convert to path to get the correct style for inkex.Use element.style = node.to_path_element().style element.path = path
def process_path(self, element, matrix): """Apply the transformation to the selected path""" point = element.path.to_absolute().transform( element.composed_transform()).to_superpath() for subs in point: for csp in subs: csp[0] = self.project_point(csp[0], matrix) csp[1] = self.project_point(csp[1], matrix) csp[2] = self.project_point(csp[2], matrix) element.path = inkex.Path(point).transform( -element.composed_transform())
def plot_beam(beam: List[Tuple[Ray, float]], node: inkex.ShapeElement) -> None: path = inkex.Path() if len(beam) > 0: path += [Move(beam[0][0].origin[0], beam[0][0].origin[1])] for ray, t in beam: p1 = ray.origin + t * ray.direction path += [Line(p1[0], p1[1])] element = node.getparent().add(inkex.PathElement()) element.style = node.get("style") element.path = path.transform(-node.composed_transform())
def plot_beam(self, beam: List[Tuple[Ray, float]], node: inkex.ShapeElement) -> None: path = inkex.Path() if len(beam) > 0: path += [Move(beam[0][0].origin[0], beam[0][0].origin[1])] for ray, t in beam: p1 = ray.origin + t * ray.direction path += [Line(p1[0], p1[1])] svg = self.document.getroot() element = svg.add(inkex.PathElement()) element.style = node.get("style") element.path = path
def parsePath(self, node, transforms, names): name = "" for n in names: name = n + "_" + name name = name + node.get("id") m2 = [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]] for t in transforms: m = inkex.Transform(t).matrix m2 = inkex.Transform(m2) * inkex.Transform(m) m = inkex.Transform(node.get("transform")).matrix m2 = inkex.Transform(m2) * inkex.Transform(m) color = self.get_color(node) path = str(inkex.Path(inkex.Path(node.get('d')).to_arrays())) subpaths = path.split('M') for i in range(1, len(subpaths)): subpaths[i] = 'M ' + str.rstrip(subpaths[i]) closed = subpaths[i][-1] in ['Z', 'z'] csp = inkex.Path(subpaths[i]).to_arrays() csp = inkex.Path(csp).transform(m2).to_arrays() if closed: self.closed2curves(csp) else: self.opened2curves(csp) vertices = self.cast2spine(csp, closed) if len(vertices) >= 9 and closed or len( vertices) >= 6 and not closed: self.path2json(name + "_" + str(i), closed, color, vertices) else: inkex.debug("skipping " + name + "_" + str(i) + ": vertex count < 6 (" + str(len(vertices)) + ")")
def mesh_to_outline(corners, hlines, vlines): """Construct mesh outline as CSP path.""" outline_csps = [] path = 'M {},{}'.format(*corners[0][0]) for edge_path in hlines[0]: path = ' '.join([path, edge_path]) for edge_path in vlines[-1]: path = ' '.join([path, edge_path]) for edge_path in reversed(hlines[-1]): path = ' '.join([path, edge_path]) for edge_path in reversed(vlines[0]): path = ' '.join([path, edge_path]) outline_csps.append(inkex.Path(path).to_superpath()) return outline_csps
def draw_poly(pts, face, st, name, parent): """Draw polygone""" style = {'stroke': '#000000', 'stroke-width': str(st.th), 'stroke-linejoin': st.linejoin, 'stroke-opacity': st.s_opac, 'fill': st.fill, 'fill-opacity': st.f_opac} path = inkex.Path() for facet in face: if not path: # for first point path.append(Move(pts[facet - 1][0], -pts[facet - 1][1])) else: path.append(Line(pts[facet - 1][0], -pts[facet - 1][1])) path.close() poly = parent.add(inkex.PathElement()) poly.label = name poly.style = style poly.path = path
def effect(self): clippingLineSegments = None pathTag = inkex.addNS('path', 'svg') groupTag = inkex.addNS('g', 'svg') self.error_messages = [] for id in self.options.ids: # the selection, top-down node = self.svg.selected[id] if node.tag == pathTag: path = self.fixVHbehaviour(node) if clippingLineSegments is None: # first path is the clipper #(clippingLineSegments, errors) = self.simplepathToLineSegments(node.path.to_arrays()) (clippingLineSegments, errors) = self.simplepathToLineSegments(path) self.error_messages.extend( ['{}: {}'.format(id, err) for err in errors]) else: # do all the work! #segmentsToClip, errors = self.simplepathToLineSegments(node.path.to_arrays()) segmentsToClip, errors = self.simplepathToLineSegments( path) self.error_messages.extend( ['{}: {}'.format(id, err) for err in errors]) clippedSegments = self.clipLineSegments( segmentsToClip, clippingLineSegments) if len(clippedSegments) != 0: path = str( inkex.Path( self.linesgmentsToSimplePath(clippedSegments))) node.set('d', path) else: # don't put back an empty path(?) could perhaps put move, move? inkex.errormsg( 'Object {} clipped to nothing, will not be updated.' .format(node.get('id'))) elif node.tag == groupTag: # we don't look inside groups for paths inkex.errormsg( 'Group object {} will be ignored. Please ungroup before running the script.' .format(id)) else: # something else inkex.errormsg( 'Object {} is not of type path ({}), and will be ignored. Current type "{}".' .format(id, pathTag, node.tag)) for error in self.error_messages: inkex.errormsg(error)
def flip(sp, cb, param): # print('flip before +' + str(sp)) p = inkex.Path(sp) cb(p, param) del sp[:] prev = Vector2d() prev_prev = Vector2d() first = Vector2d() for i, seg in enumerate(p): if i == 0: first = seg.end_point(first, prev) cps = [] for cp in seg.control_points(first, prev, prev_prev): prev_prev = prev prev = cp cps.extend(cp) sp.append([seg.letter, cps])
def draw(stack): # draw a character based on a tree stack state = stack.pop(0) # print state, image, width, height = load_path(font + syntax[state][0]) # load the image if stack[0] != "[": # terminal stack element if len(syntax[state]) == 1: # this state is a terminal node return image, width, height else: substack = generate(state) # generate random substack return draw(substack) # draw random substack else: # inkex.debug("[") stack.pop(0) images = [] # list of daughter images nodes = [] # list of daughter names while stack[0] != "]": # for all nodes in stack newstate = stack[0] # the new state newimage, width, height = draw(stack) # draw the daughter state if newimage: tfimage = mxfm(newimage, width, height, stack) # maybe transform daughter state images.append([tfimage, width, height]) # list of daughter images nodes.append(newstate) # list of daughter nodes else: # inkex.debug(("recurse on",newstate,"failed")) # this should never happen return None, 0, 0 rule = findrule(state, nodes) # find the rule for this subtree for i in range(0, len(images)): currimg, width, height = images[i] if currimg: # box = inkex.Path(currimg).bounding_box() dx = rule[i][1] * units dy = rule[i][2] * units # newbox = ((box[0]+dx),(box[1]+dy),(box[2]+dx),(box[3]+dy)) currimg = (inkex.Path(currimg).translate(dx, dy)).to_arrays() image = combinePaths(image, currimg) stack.pop(0) return image, width, height
def createRoundedRect(parent, bbox, corner_radii): x, y, w, h = bbox tl, tr, br, bl = corner_radii pathEl = etree.SubElement(parent, inkex.addNS("path", "svg")) d = [] if tl > 0: d.append(["M", [x + tl, y]]) else: d.append(["M", [x, y]]) if tr > 0: d.append(["L", [x + w - tr, y]]) d.append(["Q", [x + w, y, x + w, y + tr]]) else: d.append(["L", [x + w, y]]) if br > 0: d.append(["L", [x + w, y + h - br]]) d.append(["Q", [x + w, y + h, x + w - br, y + h]]) else: d.append(["L", [x + w, y + h]]) if bl > 0: d.append(["L", [x + bl, y + h]]) d.append(["Q", [x, y + h, x, y + h - bl]]) else: d.append(["L", [x, y + h]]) if tl > 0: d.append(["L", [x, y + tl]]) d.append(["Q", [x, y, x + tl, y]]) else: d.append(["L", [x, y]]) d.append(["z", []]) pathEl.set("d", str(inkex.Path(d))) return pathEl
def snap_path_pos(self, elem, parent_transform=None): path = elem.original_path.to_arrays() transform = elem.transform * Transform(parent_transform) bbox = elem.bounding_box() min_xy, max_xy = bbox.minimum, bbox.maximum fractional_offset = min_xy[0] - round(min_xy[0]), min_xy[1] - round( min_xy[1]) - self.document_offset fractional_offset = transform_dimensions(transform, fractional_offset[0], fractional_offset[1], inverse=True) for i in range(len(path)): self.transform_path_node(-Transform(translate=fractional_offset), path, i) path = str(inkex.Path(path)) if elem.get('inkscape:original-d'): elem.set('inkscape:original-d', path) else: elem.set('d', path)
def layoutstring(imagelist, zoom): # layout string of letter-images using optical kerning kernlist = [] length = zoom for entry in imagelist: if entry == " ": # leaving room for " " space characters length = length + (zoom * render_alphabetsoup_config.space) else: image, width, height = entry length = length + width + zoom # add letter length to overall length kernlist.append(optikern( image, width, zoom)) # append kerning data for this image workspace = None position = zoom for i in range(0, len(kernlist)): while imagelist[i] == " ": position = position + (zoom * render_alphabetsoup_config.space) imagelist.pop(i) image, width, height = imagelist[i] # set the kerning if i == 0: kern = 0 # for first image, kerning is zero else: kerncompare = [] # kerning comparison array for j in range(0, len(kernlist[i][0])): kerncompare.append(kernlist[i][0][j] + kernlist[i - 1][1][j]) kern = min(kerncompare) position = position - kern # move position back by kern amount thisimage = copy.deepcopy(image) thisimage = (inkex.Path(thisimage).translate(position, 0)).to_arrays() workspace = combinePaths(workspace, thisimage) position = position + width + zoom # advance position by letter width return workspace
def makeface(last, segment, facegroup, delx, dely): """translate path segment along vector""" elem = facegroup.add(inkex.PathElement()) npt = segment.translate([delx, dely]) # reverse direction of path segment if isinstance(segment, Curve): rev = Curve(npt.x3, npt.y3, npt.x2, npt.y2, last[0] + delx, last[1] + dely) elif isinstance(segment, Line): rev = Line(last[0] + delx, last[1] + dely) else: raise RuntimeError("Unexpected segment type {}".format( type(segment))) elem.path = inkex.Path([ Move(last[0], last[1]), segment, npt.to_line(Vector2d()), rev, ZoneClose(), ])
def mesh_corners(meshgradient): """Return list of mesh patch corners, patch paths.""" rows = len(meshgradient) cols = len(meshgradient[0]) # first corner of mesh gradient corner_x = float(meshgradient.get('x', '0.0')) corner_y = float(meshgradient.get('y', '0.0')) # init corner and meshpatch lists corners = [[None for _ in range(cols + 1)] for _ in range(rows + 1)] corners[0][0] = [corner_x, corner_y] meshpatch_csps = [] for meshrow in range(rows): for meshpatch in range(cols): # get start point for current meshpatch edges if meshrow == 0: first_corner = corners[meshrow][meshpatch] if meshrow > 0: first_corner = corners[meshrow][meshpatch + 1] # parse path of meshpatch edges path = 'M {},{}'.format(*first_corner) for edge in meshgradient[meshrow][meshpatch]: path = ' '.join([path, edge.get('path')]) csp = inkex.Path(path).to_superpath() # update corner list with current meshpatch if meshrow == 0: corners[meshrow][meshpatch + 1] = csp[0][1][1] corners[meshrow + 1][meshpatch + 1] = csp[0][2][1] if meshpatch == 0: corners[meshrow + 1][meshpatch] = csp[0][3][1] if meshrow > 0: corners[meshrow][meshpatch + 1] = csp[0][0][1] corners[meshrow + 1][meshpatch + 1] = csp[0][1][1] if meshpatch == 0: corners[meshrow + 1][meshpatch] = csp[0][2][1] # append to list of meshpatch csp meshpatch_csps.append(csp) return corners, meshpatch_csps
def mesh_to_faces(corners, hlines, vlines): """Construct mesh faces with CSP paths.""" rows = len(corners) - 1 cols = len(corners[0]) - 1 face_csps = [] for row in range(rows): for col in range(cols): # init new face face = [] # init edge paths edge_t = hlines[row][col] edge_b = hlines[row + 1][col] edge_l = vlines[col][row] edge_r = vlines[col + 1][row] # top edge, first if row == 0: path = 'M {},{}'.format(*corners[row][col]) path = ' '.join([path, edge_t]) face.append(inkex.Path(path).to_superpath()[0]) else: path = 'M {},{}'.format(*corners[row][col + 1]) path = ' '.join([path, edge_t]) face.append(reverse_path(inkex.Path(path).to_superpath())[0]) # right edge path = 'M {},{}'.format(*corners[row][col + 1]) path = ' '.join([path, edge_r]) join_path(face, -1, inkex.Path(path).to_superpath(), 0) # bottom edge path = 'M {},{}'.format(*corners[row + 1][col + 1]) path = ' '.join([path, edge_b]) join_path(face, -1, inkex.Path(path).to_superpath(), 0) # left edge if col == 0: path = 'M {},{}'.format(*corners[row + 1][col]) path = ' '.join([path, edge_l]) join_path(face, -1, inkex.Path(path).to_superpath(), 0) else: path = 'M {},{}'.format(*corners[row][col]) path = ' '.join([path, edge_l]) join_path(face, -1, reverse_path(inkex.Path(path).to_superpath()), 0) # append face to output list face_csps.append(face) return face_csps
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 = Transform(layer_trans).matrix 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 = inkex.Path(d).to_arrays() points = [] if p: #sanity check if p[0][0] == 'M': t = node.get('transform') if t: m = Transform(t).matrix trans = (Transform(layer_m)*Transform(m)).matrix else: trans = layer_m for path in p: if path[0] != "Z": x = (path[1][0]) y = (path[1][1]) xy = Transform(trans).apply_to_point([x,y]) 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 dxf_path_to_point(self, layer, p): bbox = inkex.Path(p).bounding_box() or inkex.BoundingBox(0, 0) x, y = bbox.center self.dxf_point(layer, x, y)
def effect(self): paths = [] for node in self.svg.selection.filter(inkex.PathElement): paths.append(node) if len(paths) < 2: raise inkex.AbortExtension("Need at least 2 paths selected") for path in paths: path.apply_transform() pts = [node.path.to_superpath() for node in paths] for n1 in range(0, len(paths)): for n2 in range(n1 + 1, len(paths)): verts = [] for i in range(0, min(map(len, pts))): comp = [] for j in range(0, min(len(pts[n1][i]), len(pts[n2][i]))): comp.append([pts[n1][i][j][1][-2:], pts[n2][i][j][1][-2:]]) verts.append(comp) if self.options.mode.lower() == 'lines': line = [] for comp in verts: for n, v in enumerate(comp): line += [('M', v[0])] line += [('L', v[1])] ele = inkex.PathElement() paths[0].xpath('..')[0].append(ele) ele.set('d', str(inkex.Path(line))) style = { 'fill': 'none', 'stroke': '#000000', 'stroke-opacity': 1, 'stroke-width': self.svg.unittouu('1px'), } ele.set('style', str(inkex.Style(style))) elif self.options.mode.lower() == 'polygons': g = inkex.Group() style = { 'fill': '#000000', 'fill-opacity': 0.3, 'stroke': '#000000', 'stroke-opacity': 0.6, 'stroke-width': self.svg.unittouu('2px'), } g.set('style', str(inkex.Style(style))) paths[0].xpath('..')[0].append(g) for comp in verts: for n, v in enumerate(comp): nn = n + 1 if nn == len(comp): nn = 0 line = [] line += [('M', comp[n][0])] line += [('L', comp[n][1])] line += [('L', comp[nn][1])] line += [('L', comp[nn][0])] line += [('L', comp[n][0])] ele = inkex.PathElement() g.append(ele) ele.set('d', str(inkex.Path(line)))
def generate(self): opts = self.options d = self.to_document_units(opts.diameter, opts.diameter_unit) f = self.to_document_units(opts.focal_length, opts.focal_length_unit) e = self.to_document_units(opts.edge_thickness, opts.edge_thickness_unit) optical_index = opts.optical_index lens_path = [] if opts.lens_type == 'plano_con': # Radius of curvature from Lensmaker's equation roc = (optical_index - 1) * abs(f) if 2 * roc < d: inkex.utils.errormsg( "Focal length is too short or diameter is too large.") return None elif (roc**2 - (d / 2)**2)**.5 - roc < -e and f < 0: inkex.utils.errormsg("Edge thickness is too small.") return None else: sweep = 1 if f < 0 else 0 # see arc_to_path in inkex/paths.py for description of # parameters lens_path = arc_to_path([-d / 2, 0], [roc, roc, 0., 0, sweep, +d / 2, 0]) lens_path += [ [[+d / 2, 0], [+d / 2, 0], [+d / 2, -e]], [[+d / 2, -e], [+d / 2, -e], [-d / 2, -e]], [[+d / 2, -e], [-d / 2, -e], [-d / 2, +e]], ] # no need to close the path correctly as it's done after elif opts.lens_type == 'bi_con': roc = (optical_index - 1) * abs(f) \ * (1 + (1 - e / f / optical_index) ** .5) if 2 * roc < d: inkex.utils.errormsg( "Focal length is too short or diameter is too large.") return None elif (roc**2 - (d / 2)**2)**.5 - roc < -e / 2 and f < 0: inkex.utils.errormsg("Edge thickness is too small.") return None else: sweep = 1 if f < 0 else 0 lens_path = arc_to_path([-d / 2, 0], [roc, roc, 0., 0, sweep, +d / 2, 0]) lens_path += [ [[+d / 2, 0], [+d / 2, 0], [+d / 2, -e]], [[+d / 2, -e], [+d / 2, -e], [+d / 2, -e]], ] lens_path += arc_to_path([+d / 2, -e], [roc, roc, 0., 0, sweep, -d / 2, -e]) lens_path += [ [[-d / 2, -e], [-d / 2, -e], [-d / 2, 0]], [[-d / 2, -e], [-d / 2, 0], [-d / 2, 0]], ] lens = inkex.PathElement() lens.style = self.style closed_path = inkex.Path(inkex.CubicSuperPath([lens_path])) closed_path.close() lens.path = closed_path.transform(Transform('rotate(90)')) set_description(lens, f"optics:glass:{optical_index}") yield lens
def effect(self): # Check that elements have been selected if not self.svg.selected: inkex.errormsg(_("Please select objects!")) return linestyle = { 'stroke': '#000000', 'stroke-width': str(self.svg.unittouu('1px')), 'fill': 'none', 'stroke-linecap': 'round', 'stroke-linejoin': 'round' } facestyle = { 'stroke': '#000000', 'stroke-width': str(self.svg.unittouu('1px')), 'fill': 'none', 'stroke-linecap': 'round', 'stroke-linejoin': 'round' } parent_group = self.svg.selection.first().getparent() trans = parent_group.composed_transform() invtrans = None if trans: invtrans = -trans # Recovery of the selected objects pts = [] nodes = [] seeds = [] fills = [] for node in self.svg.selected.values(): nodes.append(node) bbox = node.bounding_box() if bbox: center_x, center_y = bbox.center point = [center_x, center_y] if trans: point = trans.apply_to_point(point) pts.append(Point(*point)) if self.options.delaunayFillOptions != "delaunay-no-fill": fills.append(node.style.get('fill', 'none')) seeds.append(Point(center_x, center_y)) # Creation of groups to store the result if self.options.diagramType != 'Delaunay': # Voronoi group_voronoi = parent_group.add(Group()) group_voronoi.set('inkscape:label', 'Voronoi') if invtrans: group_voronoi.transform *= invtrans if self.options.diagramType != 'Voronoi': # Delaunay group_delaunay = parent_group.add(Group()) group_delaunay.set('inkscape:label', 'Delaunay') # Clipping box handling if self.options.diagramType != 'Delaunay': # Clipping bounding box creation group_bbox = sum([node.bounding_box() for node in nodes], None) # Clipbox is the box to which the Voronoi diagram is restricted if self.options.clip_box == 'Page': svg = self.document.getroot() width = self.svg.unittouu(svg.get('width')) height = self.svg.unittouu(svg.get('height')) clip_box = (0, width, 0, height) else: clip_box = (group_bbox.left, group_bbox.right, group_bbox.top, group_bbox.bottom) # Safebox adds points so that no Voronoi edge in clip_box is infinite safe_box = (2 * clip_box[0] - clip_box[1], 2 * clip_box[1] - clip_box[0], 2 * clip_box[2] - clip_box[3], 2 * clip_box[3] - clip_box[2]) pts.append(Point(safe_box[0], safe_box[2])) pts.append(Point(safe_box[1], safe_box[2])) pts.append(Point(safe_box[1], safe_box[3])) pts.append(Point(safe_box[0], safe_box[3])) if self.options.showClipBox: # Add the clip box to the drawing rect = group_voronoi.add(Rectangle()) rect.set('x', str(clip_box[0])) rect.set('y', str(clip_box[2])) rect.set('width', str(clip_box[1] - clip_box[0])) rect.set('height', str(clip_box[3] - clip_box[2])) rect.style = linestyle # Voronoi diagram generation if self.options.diagramType != 'Delaunay': vertices, lines, edges = voronoi.computeVoronoiDiagram(pts) for edge in edges: vindex1, vindex2 = edge[1:] if (vindex1 < 0) or (vindex2 < 0): continue # infinite lines have no need to be handled in the clipped box else: segment = self.clip_edge(vertices, lines, edge, clip_box) # segment = [vertices[vindex1],vertices[vindex2]] # deactivate clipping if len(segment) > 1: x1, y1 = segment[0] x2, y2 = segment[1] cmds = [['M', [x1, y1]], ['L', [x2, y2]]] path = group_voronoi.add(PathElement()) path.set('d', str(inkex.Path(cmds))) path.style = linestyle if self.options.diagramType != 'Voronoi': triangles = voronoi.computeDelaunayTriangulation(seeds) i = 0 if self.options.delaunayFillOptions == "delaunay-fill": random.seed("inkscape") for triangle in triangles: pt1 = seeds[triangle[0]] pt2 = seeds[triangle[1]] pt3 = seeds[triangle[2]] cmds = [['M', [pt1.x, pt1.y]], ['L', [pt2.x, pt2.y]], ['L', [pt3.x, pt3.y]], ['Z', []]] if self.options.delaunayFillOptions == "delaunay-fill" \ or self.options.delaunayFillOptions == "delaunay-fill-random": facestyle = { 'stroke': fills[triangle[random.randrange(0, 2)]], 'stroke-width': str(self.svg.unittouu('0.005px')), 'fill': fills[triangle[random.randrange(0, 2)]], 'stroke-linecap': 'round', 'stroke-linejoin': 'round' } path = group_delaunay.add(PathElement()) path.set('d', str(inkex.Path(cmds))) path.style = facestyle i += 1
def addPathToInkscape(self, path, parent, color): elem = parent.add(inkex.PathElement()) elem.style = {'stroke': color, 'stroke-width': 2, 'fill': 'none'} elem.path = inkex.Path(path)
def validation_warnings(self): repr_point = next(inkex.Path(self.parse_path()).end_points) yield PatternWarning(repr_point)
def draw_crop_scale(stack, zoom): # draw, crop and scale letter image image, width, height = draw(stack) bbox = inkex.Path(image).bounding_box() image = (inkex.Path(image).translate(-bbox.x.minimum, 0)).to_arrays() image = (inkex.Path(image).scale(zoom / units, zoom / units)).to_arrays() return image, bbox.width, bbox.height