def run(args): saver = ReproSaver() saver.seed() R = 150 phi = 1.6180339 smaller = R / phi # golden ratio pts = [] pts.extend(circle_connections(1200, R, np.pi, -np.pi)) rays = draw_rays(0, 7 * R * phi, 0, np.pi, 30) rays.extend(draw_rays(2.5 * R, 7 * R * phi, np.pi / 60, np.pi - np.pi / 60, 29)) rays = shift(rays, np.array([1.7 * R, 0])) rays = mask_drawing(rays, circle((0, 0), 1.05 * R, N=50), invert=True) pts.extend(rays) # pts = mask_drawing(pts, circle((0, 0), R, N=50)) # pts.extend(circle_connections(230, R, np.pi * 0.3, 0)) # pts.extend(circle_connections(530, R, np.pi * 0.5, 0)) # pts.extend(circle_connections(650, R, np.pi * 0.65, np.pi * 0.50)) # pts.extend(circle_connections(700, R, np.pi * 0.95, np.pi * 0.72)) # for i in range(1, 18): # smooth_circle = circle_connections(360, R * 1.02**i, np.pi / 180, np.pi / 180) # pts.extend(drop_parts(smooth_circle, space=1, passes=4)) lines = to_vpype(pts) lines.crop(-2.3 * R - smaller, 2.3 * R + smaller, R + smaller, -R - smaller) # left, bottom, right, top pts = from_vpype_lines(lines) if not args.nosave: saver.add_svg(pts) document = vpype.Document(lines) # document.extend_page_size(None) with open('/tmp/signal_planet.svg', 'w') as fout: vpype.write_svg(fout, document) vpype_viewer.show(document) return 0
def run(args): saver = ReproSaver('star_results') saver.seed() if args.rnd: description = random_star() print(f"description: {description}") paths = construct_star(description) else: N_star = 12 R = 600 paths = [] start_fractions = np.linspace(0.4, 1, 25) end_fractions = reversed(start_fractions) for start_fraction, end_fraction in zip(start_fractions, end_fractions): start_r = R * start_fraction end_r = R * end_fraction paths.extend(star_line(N_star, start_r, end_r, 1)) # vlastovka start_fractions = np.geomspace(0.4, 0.65, 20) end_fractions = reversed(np.geomspace(0.3, 0.9, 20)) for start_fraction, end_fraction in zip(start_fractions, end_fractions): start_r = R * start_fraction end_r = R * end_fraction paths.extend(star_line(N_star, start_r, end_r, 1)) # stred start_fractions = rev_geomspace(0.1, 1, 30) end_fractions = start_fractions # end_fractions = np.linspace(0.1, 1, 30) for start_fraction, end_fraction in zip(start_fractions, end_fractions): start_r = R * start_fraction end_r = R * end_fraction paths.extend(star_line(N_star, start_r, end_r, 5, mirror=False)) # stred 2 start_fractions = rev_geomspace(0.13, 0.7, 20) end_fractions = start_fractions # end_fractions = np.linspace(0.1, 1, 30) for start_fraction, end_fraction in zip(start_fractions, end_fractions): start_r = R * start_fraction end_r = R * end_fraction paths.extend(star_line(N_star, start_r, end_r, 5, mirror=False)) paths.extend(star_line(N_star, R, R, 6, mirror=False)) lines = to_vpype(paths) if not args.nosave: saver.add_svg(paths) document = vpype.Document(lines) with open('/tmp/stars.svg', 'w') as fout: vpype.write_svg(fout, document) if not args.novis: vpype_viewer.show(document) return 0
def show( document: vp.Document, classic: bool, force: bool, show_pen_up: bool, show_points: bool, outline: bool, colorful: bool, show_axes: bool, show_grid: bool, hide_legend: bool, unit: str, ): """Display the geometry in an graphical user interface. By default, this command use a modern, hardware-accelerated viewer (currently in beta) with a preview mode (adjustable pen width and opacity) and interactive controls to adjust display options. This viewer requires OpenGL 3.3 support. The original, Matplotlib-based viewer is still available with the `--classic` option. The classic viewer does not have interactive controls for display options. Use the command-line options to customize the display. """ if not classic: mgl_ok = _test_mgl() if not mgl_ok and not force: classic = True logging.warning( "!!! show: ModernGL not available, reverting to classic mode.") elif not mgl_ok and force: logging.warning( "!!! show: ModernGL not available but forced to modern mode.") if classic: _show_mpl( document, show_axes, show_grid, show_pen_up, show_points, hide_legend, colorful, unit, ) else: view_mode = ViewMode.PREVIEW if outline or show_points: view_mode = ViewMode.OUTLINE if colorful: view_mode = ViewMode.OUTLINE_COLORFUL vpype_viewer.show(document, view_mode=view_mode, show_pen_up=show_pen_up, show_points=show_points) return document
def run(args): R = 150 smaller = R / 1.6180339 # golden ratio pts = [] pts.extend(circle_connections(230, R, np.pi * 0.3, 0)) pts.extend(circle_connections(530, R, np.pi * 0.5, 0)) pts.extend(circle_connections(650, R, np.pi * 0.65, np.pi * 0.50)) pts.extend(circle_connections(700, R, np.pi * 0.95, np.pi * 0.72)) for i in range(1, 18): smooth_circle = circle_connections(360, R * 1.02**i, np.pi / 180, np.pi / 180) pts.extend(drop_parts(smooth_circle, space=1, passes=4)) lines = to_vpype(pts) lines.crop(R - smaller, 0, R, -R) document = vpype.Document(lines) # document.extend_page_size(None) with open('/tmp/circles.svg', 'w') as fout: vpype.write_svg(fout, document) vpype_viewer.show(document) return 0
def run(args): saver = ReproSaver() saver.seed() if args.cfg is not None: saver.load_seeds(args.cfg) H, W = 210, 148 angle_range = 3 horizontal_lines = [] for i in range(1000): horizontal_lines.append( random_line(H, W, min_angle=np.radians(0 - angle_range / 2), max_angle=np.radians(0 + angle_range / 2))) vertical_lines = [] for i in range(1000): vertical_lines.append( random_line(H, W, min_angle=np.radians(90 - angle_range / 2), max_angle=np.radians(90 + angle_range / 2))) cross = draw_cross(H, W) horizontal, vertical = draw_cross_parts(H, W) margin = 10 (horizontal, vertical, cross) = resize_and_center([horizontal, vertical, cross], H, W, margin, margin, margin, margin) horizontal_lines = mask_drawing(horizontal_lines, horizontal) vertical_lines = mask_drawing(vertical_lines, vertical) horizontal_lines = jitter_length(horizontal_lines, 0, 5) vertical_lines = jitter_length(vertical_lines, 0, 5) vertical_lines = mask_drawing(vertical_lines, horizontal, invert=True) cross_lines = horizontal_lines cross_lines.extend(vertical_lines) cross_lines.extend(vertical_lines) # cross_lines.append(cross) bg_lines = [] for i in range(300): bg_lines.append(random_line(0.2 * H, W)) bg_lines = shift(bg_lines, (0, 0.8 * H)) bg_lines = mask_drawing(bg_lines, cross, invert=True) pts = [] pts.extend(cross_lines) pts = resize_and_center(pts, H, W, margin, margin, margin, margin) # pts.extend(bg_lines) lines = to_vpype(pts) document = vpype.Document(lines) vpype_viewer.show(document) if not args.nosave: saver.add_svg(pts) return 0
def effect(self): lc = vpype.LineCollection() # create a new array of LineStrings consisting of Points. We convert selected paths to polylines and grab their points elementsToWork = [] # we make an array of all collected nodes to get the boundingbox of that array. We need it to place the vpype converted stuff to the correct XY coordinates applyTransformAvailable = False # at first we apply external extension try: sys.path.append("..") # add parent directory to path to allow importing applytransform (vpype extension is encapsulated in sub directory) import applytransform applyTransformAvailable = True except Exception as e: # inkex.utils.debug(e) inkex.utils.debug("Calling 'Apply Transformations' extension failed. Maybe the extension is not installed. You can download it from official InkScape Gallery. Skipping this step") def flatten(node): path = node.path.to_superpath() bezier.cspsubdiv(path, self.options.flatness) newpath = [] for subpath in path: first = True for csp in subpath: cmd = 'L' if first: cmd = 'M' first = False newpath.append([cmd, [csp[1][0], csp[1][1]]]) node.path = newpath # flatten the node's path to linearize, split up the path to it's subpaths (break apart) and add all points to the vpype lines collection def convertPath(node, nodes = None): if nodes is None: nodes = [] if node.tag == inkex.addNS('path','svg'): nodes.append(node) if self.options.flattenbezier is True: flatten(node) raw = node.path.to_arrays() subPaths, prev = [], 0 for i in range(len(raw)): # Breaks compound paths into simple paths if raw[i][0] == 'M' and i != 0: subPaths.append(raw[prev:i]) prev = i subPaths.append(raw[prev:]) for subPath in subPaths: points = [] for csp in subPath: if len(csp[1]) > 0: #we need exactly two points per straight line segment points.append(Point(round(csp[1][0], self.options.decimals), round(csp[1][1], self.options.decimals))) if subPath[-1][0] == 'Z' or subPath[0][1] == subPath[-1][1]: #check if path is closed by Z or first pont == last point points.append(Point(round(subPath[0][1][0], self.options.decimals), round(subPath[0][1][1], self.options.decimals))) #if closed, we add the first point again lc.append(LineString(points)) children = node.getchildren() if children is not None: for child in children: convertPath(child, nodes) return nodes doc = None #create a vpype document ''' if 'paths' we process paths only. Objects like rectangles or strokes like polygon have to be converted before accessing them if 'layers' we can process all layers in the complete document ''' if self.options.input_handling == "paths": # getting the bounding box of the current selection. We use to calculate the offset XY from top-left corner of the canvas. This helps us placing back the elements input_bbox = None if self.options.apply_transformations is True and applyTransformAvailable is True: ''' we need to apply transfoms to the complete document even if there are only some single paths selected. If we apply it to selected nodes only the parent groups still might contain transforms. This messes with the coordinates and creates hardly controllable behaviour ''' applytransform.ApplyTransform().recursiveFuseTransform(self.document.getroot()) if len(self.svg.selected) == 0: elementsToWork = convertPath(self.document.getroot()) for element in elementsToWork: input_bbox += element.bounding_box() else: elementsToWork = None for element in self.svg.selected.values(): elementsToWork = convertPath(element, elementsToWork) #input_bbox = inkex.elements._selected.ElementList.bounding_box(self.svg.selected) # get BoundingBox for selection input_bbox = self.svg.selection.bounding_box() # get BoundingBox for selection if len(lc) == 0: inkex.errormsg('Selection appears to be empty or does not contain any valid svg:path nodes. Try to cast your objects to paths using CTRL + SHIFT + C or strokes to paths using CTRL + ALT+ C') return # find the first object in selection which has a style attribute (skips groups and other things which have no style) firstElementStyle = None for element in elementsToWork: if element.attrib.has_key('style'): firstElementStyle = element.get('style') doc = vpype.Document(page_size=(input_bbox.width + input_bbox.left, input_bbox.height + input_bbox.top)) #create new vpype document doc.add(lc, layer_id=None) # we add the lineCollection (converted selection) to the vpype document elif self.options.input_handling == "layers": doc = vpype.read_multilayer_svg(self.options.input_file, quantization = self.options.flatness, crop = False, simplify = self.options.simplify, parallel = self.options.parallel, default_width = self.document.getroot().get('width'), default_height = self.document.getroot().get('height')) for element in self.document.getroot().xpath("//svg:g", namespaces=inkex.NSS): #all groups/layers elementsToWork.append(element) tooling_length_before = doc.length() traveling_length_before = doc.pen_up_length() # build and execute the conversion command # the following code block is not intended to sum up the commands to build a series (pipe) of commands! ########################################## # Line Sorting if self.options.linesort is True: command = "linesort " if self.options.linesort_no_flip is True: command += " --no-flip" # Line Merging if self.options.linemerge is True: command = "linemerge --tolerance " + str(self.options.linemerge_tolerance) if self.options.linemerge_no_flip is True: command += " --no-flip" # Trimming if self.options.trim is True: command = "trim " + str(self.options.trim_x_margin) + " " + str(self.options.trim_y_margin) # Relooping if self.options.reloop is True: command = "reloop --tolerance " + str(self.options.reloop_tolerance) # Multipass if self.options.multipass is True: command = "multipass --count " + str(self.options.multipass_count) # Filter if self.options.filter is True: command = "filter --tolerance " + str(self.options.filter_tolerance) if self.options.filter_min_length_enabled is True: command += " --min-length " + str(self.options.filter_min_length) if self.options.filter_max_length_enabled is True: command += " --max-length " + str(self.options.filter_max_length) if self.options.filter_closed is True and self.options.filter_not_closed is False: command += " --closed" if self.options.filter_not_closed is True and self.options.filter_closed is False: command += " --not-closed" if self.options.filter_closed is False and \ self.options.filter_not_closed is False and \ self.options.filter_min_length_enabled is False and \ self.options.filter_max_length_enabled is False: inkex.errormsg('No filters to apply. Please select at least one filter.') return # Plugin Occult if self.options.plugin_occult is True: command = "occult --tolerance " + str(self.options.plugin_occult_tolerance) if self.options.plugin_occult_keepseparatelayer is True: command += " --keep-occulted" # Split All if self.options.splitall is True: command = " splitall" # Free Mode if self.options.freemode is True: command = "" if self.options.freemode_cmd1_enabled is True: command += " " + self.options.freemode_cmd1.strip() if self.options.freemode_cmd2_enabled is True: command += " " + self.options.freemode_cmd2.strip() if self.options.freemode_cmd3_enabled is True: command += " " + self.options.freemode_cmd3.strip() if self.options.freemode_cmd4_enabled is True: command += " " + self.options.freemode_cmd4.strip() if self.options.freemode_cmd5_enabled is True: command += " " + self.options.freemode_cmd5.strip() if self.options.freemode_cmd1_enabled is False and \ self.options.freemode_cmd2_enabled is False and \ self.options.freemode_cmd3_enabled is False and \ self.options.freemode_cmd4_enabled is False and \ self.options.freemode_cmd5_enabled is False: inkex.utils.debug("Warning: empty vpype pipeline. With this you are just getting read-write layerset/lineset.") else: if self.options.freemode_show_cmd is True: inkex.utils.debug("Your command pipe will be the following:") inkex.utils.debug(command) # inkex.utils.debug(command) try: doc = execute(command, doc) except Exception as e: inkex.utils.debug("Error in vpype:" + str(e)) return ########################################## tooling_length_after = doc.length() traveling_length_after = doc.pen_up_length() if tooling_length_before > 0: tooling_length_saving = (1.0 - tooling_length_after / tooling_length_before) * 100.0 else: tooling_length_saving = 0.0 if traveling_length_before > 0: traveling_length_saving = (1.0 - traveling_length_after / traveling_length_before) * 100.0 else: traveling_length_saving = 0.0 if self.options.output_stats is True: inkex.utils.debug('Total tooling length before vpype conversion: ' + str('{:0.2f}'.format(tooling_length_before)) + ' mm') inkex.utils.debug('Total traveling length before vpype conversion: ' + str('{:0.2f}'.format(traveling_length_before)) + ' mm') inkex.utils.debug('Total tooling length after vpype conversion: ' + str('{:0.2f}'.format(tooling_length_after)) + ' mm') inkex.utils.debug('Total traveling length after vpype conversion: ' + str('{:0.2f}'.format(traveling_length_after)) + ' mm') inkex.utils.debug('Total tooling length optimized: ' + str('{:0.2f}'.format(tooling_length_saving)) + ' %') inkex.utils.debug('Total traveling length optimized: ' + str('{:0.2f}'.format(traveling_length_saving)) + ' %') if tooling_length_after == 0: inkex.errormsg('No lines left after vpype conversion. Conversion result is empty. Cannot continue') return # show the vpype document visually if self.options.output_show: warnings.filterwarnings("ignore") # workaround to suppress annoying DeprecationWarning # vpype_viewer.show(doc, view_mode=ViewMode.PREVIEW, show_pen_up=self.options.output_trajectories, show_points=self.options.output_show_points, pen_width=0.1, pen_opacity=1.0, argv=None) vpype_viewer.show(doc, view_mode=ViewMode.PREVIEW, show_pen_up=self.options.output_trajectories, show_points=self.options.output_show_points, argv=None) # https://vpype.readthedocs.io/en/stable/api/vpype_viewer.ViewMode.html warnings.filterwarnings("default") # reset warning filter exit(0) #we leave the code loop because we only want to preview. We don't want to import the geometry # save the vpype document to new svg file and close it afterwards output_file = self.options.input_file + ".vpype.svg" output_fileIO = open(output_file, "w", encoding="utf-8") #vpype.write_svg(output_fileIO, doc, page_size=None, center=False, source_string='', layer_label_format='%d', show_pen_up=self.options.output_trajectories, color_mode='layer', single_path = True) vpype.write_svg(output_fileIO, doc, page_size=None, center=False, source_string='', layer_label_format='%d', show_pen_up=self.options.output_trajectories, color_mode='layer') #vpype.write_svg(output_fileIO, doc, page_size=(self.svg.unittouu(self.document.getroot().get('width')), self.svg.unittouu(self.document.getroot().get('height'))), center=False, source_string='', layer_label_format='%d', show_pen_up=self.options.output_trajectories, color_mode='layer') output_fileIO.close() # convert vpype polylines/lines/polygons to regular paths again. We need to use "--with-gui" to respond to "WARNING: ignoring verb FileSave - GUI required for this verb." if self.options.strokes_to_paths is True: cli_output = inkscape(output_file, "--with-gui", actions="EditSelectAllInAllLayers;EditUnlinkClone;ObjectToPath;FileSave;FileQuit") if len(cli_output) > 0: self.debug(_("Inkscape returned the following output when trying to run the vpype object to path back-conversion:")) self.debug(cli_output) # this does not work because line, polyline and polygon have no base class to execute replace_with #if self.options.strokes_to_paths is True: # for lineLayer in lineLayers: # for element in lineLayer: # element.replace_with(element.to_path_element()) # parse the SVG file try: stream = open(output_file, 'r') except FileNotFoundError as e: inkex.utils.debug("There was no SVG output generated by vpype. Cannot continue") exit(1) p = etree.XMLParser(huge_tree=True) import_doc = etree.parse(stream, parser=etree.XMLParser(huge_tree=True)) stream.close() # handle pen_up trajectories (travel lines) trajectoriesLayer = import_doc.getroot().xpath("//svg:g[@id='pen_up_trajectories']", namespaces=inkex.NSS) if self.options.output_trajectories is True: if len(trajectoriesLayer) > 0: trajectoriesLayer[0].set('style', 'stroke:#0000ff;stroke-width:{:0.2f}px;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;fill:none'.format(self.options.trajectories_stroke_width)) trajectoriesLayer[0].attrib.pop('stroke') # remove unneccesary stroke attribute trajectoriesLayer[0].attrib.pop('fill') # remove unneccesary fill attribute else: if len(trajectoriesLayer) > 0: trajectoriesLayer[0].delete() lineLayers = import_doc.getroot().xpath("//svg:g[not(@id='pen_up_trajectories')]", namespaces=inkex.NSS) #all layer except the pen_up trajectories layer if self.options.use_style_of_first_element is True and self.options.input_handling == "paths" and firstElementStyle is not None: # if we remove the fill property and use "Use style of first element in layer" the conversion will just crash with an unknown reason #declarations = firstElementStyle.split(';') #for i, decl in enumerate(declarations): # parts = decl.split(':', 2) # if len(parts) == 2: # (prop, val) = parts # prop = prop.strip().lower() # #if prop == 'fill': # # declarations[i] = prop + ':none' for lineLayer in lineLayers: #lineLayer.set('style', ';'.join(declarations)) lineLayer.set('style', firstElementStyle) lineLayer.attrib.pop('stroke') # remove unneccesary stroke attribute lineLayer.attrib.pop('fill') # remove unneccesary fill attribute else: for lineLayer in lineLayers: if lineLayer.attrib.has_key('stroke'): color = lineLayer.get('stroke') lineLayer.set('style', 'stroke:' + color + ';stroke-width:{:0.2f}px;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;fill:none'.format(self.options.lines_stroke_width)) lineLayer.attrib.pop('stroke') # remove unneccesary stroke attribute lineLayer.attrib.pop('fill') # remove unneccesary fill attribute import_viewBox = import_doc.getroot().get('viewBox').split(" ") self_viewBox = self.document.getroot().get('viewBox').split(" ") scaleX = self.svg.unittouu(self_viewBox[2]) / self.svg.unittouu(import_viewBox[2]) scaleY = self.svg.unittouu(self_viewBox[3]) / self.svg.unittouu(import_viewBox[3]) for element in import_doc.getroot().iter("{http://www.w3.org/2000/svg}g"): self.document.getroot().append(element) if self.options.input_handling == "layers": element.set('transform', 'scale(' + str(scaleX) + ',' + str(scaleY) + ')') #imported groups need to be transformed. Or they have wrong size. Reason: different viewBox sizes/units in namedview definitions if self.options.apply_transformations is True and applyTransformAvailable is True: #we apply the transforms directly after adding them applytransform.ApplyTransform().recursiveFuseTransform(element) # Delete the temporary file again because we do not need it anymore if os.path.exists(output_file): os.remove(output_file) # Remove selection objects to do a real replace with new objects from vpype document if self.options.keep_objects is False: for element in elementsToWork: element.delete()