def save_points_as_svg_handler(points, size, image_number): # filename like file:///home/gdshen/Pictures/00000.jpg filename = os.path.join(project_path, svg_dir, image_number + '.svg') paths = [] for i in range(size): start_point_x = points.property(i).property('startPoint').property( 'X').toInt() start_point_y = points.property(i).property('startPoint').property( 'Y').toInt() control_point_x = points.property(i).property( 'controlPoint').property('X').toInt() control_point_y = points.property(i).property( 'controlPoint').property('Y').toInt() target_point_x = points.property(i).property( 'targetPoint').property('X').toInt() target_point_y = points.property(i).property( 'targetPoint').property('Y').toInt() print(start_point_x, start_point_y, control_point_x, control_point_y, target_point_x, target_point_y) paths.append( Path( QuadraticBezier(complex(start_point_x, start_point_y), complex(control_point_x, control_point_y), complex(target_point_x, target_point_y)))) wsvg(paths=paths, filename=filename)
def save_subplot(path_buffer, output_folder, num): """Take all the paths in the buffer and save them to a new file""" logging.info(f"Saving sublot {num}") paths_to_save, attributes_to_save = tuple(zip(*path_buffer)) svgpathtools.wsvg( paths_to_save, filename=os.path.join(output_folder, f"svg/{num}.svg"), )
def write(self, filename): wsvg(self.paths, attributes=self.attributes, svg_attributes=self.svg_attributes, filename=filename) return self
def saveSVG(self, fn): pathsCK = self.paths['ck'] pathsLL0 = self.paths['ll0'] def getSegment(path): return CubicBezier(complex(path['p0'][0], path['p0'][1]), complex(path['c0'][0], path['c0'][1]), complex(path['c1'][0], path['c1'][1]), complex(path['p1'][0], path['p1'][1])) # C/K ck = Path(getSegment(pathsCK[0]), getSegment(pathsCK[1]), getSegment(pathsCK[2]), getSegment(pathsCK[3])) # L/L0 ll0 = Path(getSegment(pathsLL0[0])) paths = [ck, ll0] pathAttributes = { "stroke-width": self.width, "stroke": "#000", "fill": "#fff" } svg_attributes = {"viewBox": "0 0 600 600", "x": "0px", "y": "0px"} attributes = [pathAttributes, pathAttributes] wsvg(paths, attributes=attributes, svg_attributes=svg_attributes, filename=fn)
def render_svg_color(paths, stroke_colors, stroke_width=4, stroke_linecap='round', fill_mode='none', viewbox=[0, 0, 300, 300], base_dir='./', out_dir='svg_images', out_fname='tmp.svg'): ''' see docs for wsvg: https://www.pydoc.io/pypi/svgpathtools-1.3.3/autoapi/paths2svg/index.html?highlight=wsvg#paths2svg.wsvg wsvg(paths=None, colors=None, filename=join, stroke_widths=None, nodes=None, node_colors=None, node_radii=None, openinbrowser=False, timestamp=False, margin_size=0.1, mindim=600, dimensions=None, viewbox=None, text=None, text_path=None, font_size=None, attributes=None, svg_attributes=None) ''' ## render out to svg file print('Rendering out to {}'.format(os.path.join(out_dir, out_fname))) if not os.path.exists(os.path.join(base_dir, out_dir)): os.makedirs(os.path.join(base_dir, out_dir)) wsvg(paths, attributes=[{ 'stroke-width': stroke_width, 'stroke-linecap': stroke_linecap, 'stroke': color, 'fill': fill_mode } for color in stroke_colors], viewbox=viewbox, filename=os.path.join(base_dir, out_dir, out_fname))
def pattern_to_svg(pattern, filename): if isinstance(filename, str) or isinstance(filename, unicode): output_file = open(filename, "wb") else: output_file = filename paths = [] colors = [] scale_factor = 0.1 # scale from cm to mm from pes for block in pattern.blocks: block_paths = [] last_stitch = None for stitch in block.stitches: if "JUMP" in stitch.tags: last_stitch = stitch continue if last_stitch is None: last_stitch = stitch continue block_paths.append( Line(start=last_stitch.complex * scale_factor, end=stitch.complex * scale_factor)) last_stitch = stitch if len(block_paths) > 0: colors.append(block.tuple_color) paths.append(Path(*block_paths)) dims = overall_bbox(paths) mindim = max(dims[1] - dims[0], dims[3] - dims[2]) print("in pattern to svg, overallbbox", overall_bbox(paths)) if len(paths) == 0: print("warning: pattern did not generate stitches") return wsvg(paths, colors, filename=output_file, mindim=mindim)
def compact(source, dest, px): paths, attributes, svg_attributes = svg2paths2(source) if len(paths) == 0: return # Get global bbox bboxes = numpy.array([ # min_x, max_x, min_y, max_y path.bbox() for path in paths ]) min_x, _____, min_y, _____ = numpy.min(bboxes, 0) _____, max_x, _____, max_y = numpy.max(bboxes, 0) min_x = math.floor(min_x) max_x = math.ceil(max_x) min_y = math.floor(min_y) max_y = math.ceil(max_y) # Bulk translate translated = [path.translated(complex(-min_x + px, 0)) for path in paths] wsvg(paths=translated, attributes=attributes, svg_attributes=svg_attributes, filename=dest)
def convert_file(args): print(args) input_file = args.infile output_file = args.outfile min_radius = args.min_radius max_dist = args.max_dist if input_file == "" or output_file == "": logging.error("Input or output file names are missing") raise ValueError("Input and output files are needed") logging.info("input file {} output file {} min radius {}".format(input_file, output_file, min_radius)) file_name, file_extension = os.path.splitext(output_file) if file_extension == '.svg': new_paths, svg_attributes = convert_to_svg(input_file, max_dist, min_radius) for idx, path in enumerate(new_paths): logging.info('=== new path {}'.format(idx)) for jdx, segment in enumerate(path): logging.info(" === segment {}".format(jdx)) logging.info(segment) spt.wsvg(new_paths, svg_attributes=svg_attributes, filename=output_file) elif file_extension == '.dxf': raise ValueError("convert to dxf not yet implemented.") # doc = convert_to_dxf(input_file, max_dist, min_radius) # doc.saveas(output_file) logging.info("End")
def pngtosvg( data, filename, crosshatch, numlines ): newpaths = [] for y in range(0,data.height): for x in range(0,data.width): if (data.pixels[data.width*y + x][1] != 0): newpaths = newpaths + svgbox( [x,y], crosshatch, data.pixels[data.width*y + x][0], numlines) wsvg( newpaths, filename=filename.split('.')[0] + '.svg')
def visualize_pen_transits(paths, svg_file): # We will construct a new image by storing (path, attribute) # pairs in this list. parts = list() last_end = None for i, path in enumerate(paths): start = path.start end = path.end # Generate a color based on how far along in the plot we are. frac = i / (len(paths) - 1) color = generate_color(frac, 1.0, 1.0) if last_end is not None: # If this isn't our first path, add a line between the end of # the last path and the start of this one. parts.append( ( svgpathtools.Line(last_end, start), { "stroke": "black", "fill": "none", }, ) ) last_end = end # Also draw a faded, colorized version of this path. parts.append((path, {"stroke": color, "fill": "none", "opacity": "0.5"})) new_paths, new_attrs = zip(*parts) svgpathtools.wsvg(new_paths, attributes=new_attrs, filename=svg_file)
def export_portrait(self, filename): wsvg(self.portrait.get_paths(), attributes=self.portrait.get_attributes({ 'skin': rgb2hex(self.skin_color), 'body': rgb2hex(change_value(self.skin_color, -0.1)) }), filename=filename)
def write(self, filename): colors = [] paths = [] paths, attrs = zip(*self.paths) assert len(self.paths) != 0 svgpathtools.wsvg(paths, svg_attributes=self.svg_attrs, attributes=attrs, filename=filename)
def run_optimizer( input_file, output_file, vis_output, runtime, greedy, noopt, merge_paths, origin_x, origin_y, ): paths = load_paths(input_file) initial_cost = cost_of_route(paths) print("Initial cost: {}".format(initial_cost)) if noopt: route = paths else: path_graph = PathGraph(paths, origin=origin_x + (origin_y * 1j)) greedy_solution = list(greedy_walk(path_graph)) greedy_route = get_route_from_solution(greedy_solution, path_graph) greedy_cost = cost_of_route(greedy_route) print("Cost after greedy optimization: {}".format(greedy_cost)) if not greedy: vrp_solution = vrp_solver(path_graph, greedy_solution, runtime) vrp_route = get_route_from_solution(vrp_solution, path_graph) vrp_cost = cost_of_route(vrp_route) print( "Cost after VRP optimization: {} " "(may be slightly off due to numeric precision)".format(vrp_cost) ) route = vrp_route else: route = greedy_route if output_file is None: output_file = splitext(input_file)[0] + "-optimized.svg" if merge_paths is not False: if merge_paths is None: threshold = DEFAULT_MERGE_THRESHOLD else: threshold = merge_paths print("Routes before merging: {}".format(len(route))) route = join_close_paths(route, threshold) print("Routes after merging: {}".format(len(route))) print("Writing results to {}".format(output_file)) wsvg(route, filename=output_file) if vis_output is not None: print("Writing visualization to {}".format(vis_output)) visualize_pen_transits(route, vis_output)
def save_subplot(path_buffer, num): """Take all the paths in the buffer and save them to a new file""" print(f"Saving sublot {num}") paths_to_save, attributes_to_save = tuple(zip(*path_buffer)) attributes_to_save = add_fill(attributes_to_save) svgpathtools.wsvg( paths_to_save, attributes=attributes_to_save, filename=os.path.join(output_folder, f"{num}.svg"), )
def compoundPath2simplePath(srcDir, desDir): svgFiles = sorted(glob.glob(srcDir + "*.svg")) svg_attribute = {u'height': u'256px', u'width': u'256px'} for svgFileName in svgFiles: svgFile = get_path_strings(svgFileName) svgMList = svgFile[0].split('M') svgMList = svgMList[1:] svgMList = ['M' + MEle for MEle in svgMList] svgPathList = [parse_path(MEle) for MEle in svgMList] wsvg(svgPathList, svg_attributes=svg_attribute, filename=desDir + '/' + svgFileName)
def generate_face(): height = 24.5 folder = './input' eyepaths = get_paths_from_directory(folder + '/eyes/', 'EYES') nosepaths = get_paths_from_directory(folder + '/nose/', 'NOSE') mouthpaths = get_paths_from_directory(folder + '/mouth/', 'MOUTH') facepaths = align_components(eyepaths, nosepaths, mouthpaths, height) filename = 'output/' + uuid.uuid4().hex + '.svg' wsvg(facepaths, dimensions=('275mm', '195mm'), stroke_widths=[1]*2000, filename=filename, viewbox="0 0 27.5 19.5") return filename
def save(self, filename): pathAttributes = { "stroke-width": self.width, "stroke": "#000", "fill": "#fff" } svg_attributes = {"viewBox": "0 0 600 600", "x": "0px", "y": "0px"} attributes = [pathAttributes, pathAttributes] wsvg(self.paths, attributes=attributes, svg_attributes=svg_attributes, filename=filename)
def json2svg(fname): path = [] pts = [(0, 0)] data = json.load(open(fname, 'r')) for x_step, y_step, up in data: x, y = pts[-1] x_next, y_next = x + x_step, y + y_step if not up: line = Line(x + y * 1j, x_next + y_next * 1j) path.append(line) pts.append((x_next, y_next)) paths = [Path(*path)] out = '{}.svg'.format(fname.rsplit('.', 1)[0]) wsvg(paths, filename=out) return out
def segment_svg(svg_file, output_file, segment_size=8): paths, _ = svgpathtools.svg2paths(svg_file) lines = [] for path in paths: for cp in path.continuous_subpaths(): last_pos = None segments = int(cp.length() / segment_size) for frac in np.linspace(0., 1., segments): pos = cp.point(frac) if last_pos: lines.append(svgpathtools.Line(last_pos, pos)) last_pos = pos svgpathtools.wsvg(lines, filename=output_file)
def run_optimizer(input_file, output_file, vis_output, noopt, merge_paths): paths, attributes, svg_attributes = svg2paths(input_file, return_svg_attributes=True) initial_cost = cost_of_route(paths) print('Initial cost: {}'.format(initial_cost)) if noopt: route = paths else: path_graph = PathGraph(paths) greedy_solution = list(greedy_walk(path_graph)) greedy_route = get_route_from_solution(greedy_solution, path_graph) greedy_cost = cost_of_route(greedy_route) print('Cost after greedy optimization: {}'.format(greedy_cost)) route = greedy_route if merge_paths is not False: if merge_paths is None: threshold = DEFAULT_MERGE_THRESHOLD else: threshold = merge_paths print('Routes before merging: {}'.format(len(route))) route = join_close_paths(route, threshold) print('Routes after merging: {}'.format(len(route))) if output_file is None: output_file = splitext(input_file)[0] + '-optimized.svg' print('Writing results to {}'.format(output_file)) svg_attributes["debug"] = False wsvg( route, filename=output_file, # margin_size, # dimensions, # viewbox, # attributes = attributes, svg_attributes=svg_attributes, ) if vis_output is not None: print('Writing visualization to {}'.format(vis_output)) visualize_pen_transits(route, vis_output)
def generate_svg(i, m1, m2, n1): sf = superformula(m1, m2, n1) polar_pts = [(ang, sf(ang)) for ang in np.arange(0, 2*math.pi, 0.1)] carte_pts = [pol2cart(ang, rho) for ang, rho in polar_pts] # so the figure closes carte_pts.append(carte_pts[0]) path = [] for (x1, y1), (x2, y2) in zip(carte_pts, carte_pts[1:]): seg = Line(x1+y1*1j, x2+y2*1j) path.append(seg) paths = [Path(*path)] fname = 'generated/{}.svg'.format(i) wsvg(paths, filename=fname) return fname
def displaySVGPaths_transects(ring_list, data_transects, transect_angles, skipped_angle_indices, fn=None): if not fn: filename = opt.output_directory + ring_list[0].svgname else: filename = fn transectPaths = [] for tran_index in range(len(data_transects)): tran_path = Path() for seg_index in range(len(data_transects[tran_index]) - 1): start_pt = data_transects[tran_index][seg_index] end_pt = data_transects[tran_index][seg_index + 1] tran_path.append(Line(start_pt,end_pt)) transectPaths.append(tran_path) ringPaths = [r.path for r in ring_list] ringColors = [r.color for r in ring_list] pathList = ringPaths + transectPaths colors = ringColors + ['black']*len(transectPaths) transect_nodes = [item for sublist in data_transects for item in sublist] # flatten data_transects nodes = transect_nodes + [ring_list[0].center] node_colors = ['purple']*len(transect_nodes) + ['blue'] text = ['%.3f' % theta for idx, theta in enumerate(transect_angles) if idx not in skipped_angle_indices] text += ['skipped %.3f' % transect_angles[idx] for idx in skipped_angle_indices] text_path = [] for tr in data_transects: end = tr[-1] last_seg = Line(tr[-2], tr[-1]) u = last_seg.unit_tangent(1) text_path.append(Path(Line(end + 10*u, end + 100*u))) # handle skipped transects bdry_ring = max(ring_list, key=lambda ring: ring.maxR) bdry_length = bdry_ring.path.length() for idx in skipped_angle_indices: s = bdry_length * transect_angles[idx] T = inv_arclength(bdry_ring.path, s) u = bdry_ring.path.normal(T) end = bdry_ring.path.point(T) text_path.append(Line(end + 10*u, end + 100*u)) wsvg(pathList, colors, nodes=nodes, node_colors=node_colors, text=text, text_path=text_path, filename=filename+'_transects.svg')
def toolPathSVG(svgPath, repetitions): paths, attributes = svg2paths(svgPath) offset_distances = [] mult = 3 for k in range(1, 1 + repetitions, 2): if k > 1: mult = 2 offset_distances.append(-mult * k) # offset_distances = [-3*k for k in range(1, 1 + repetitions,2)] attrs = [] allPaths = [] idx = 0 for path in paths: offset_paths = [] idx += 1 for distances in offset_distances: newcurve = offset_curve(path, distances) # _, attributes = newcurve. # if newcurve.length() < 230 : # break # newcurve = parse_path(newcurve.d()) # print(type(newcurve)) if newcurve.area() < path.area() / 50: # attrs.append(idx) break offset_paths.append(newcurve) idx += 1 allPaths.append(path) for of in offset_paths: allPaths.append(of) attrs.append(idx - 1) # shownPath = paths + offset_paths shownPath = allPaths wsvg(shownPath, filename=svgPath) return attrs
def convert_font(): cwd = pathlib.Path.cwd() for root, dirs, files in os.walk('{}/exports'.format(cwd)): for name in files: source = os.path.join(root, name) target = 'fonts/{}.min.svg'.format(name.split('.')[0]) print(source) try: paths, attr = svg2paths(source) except Exception as e: print(e) continue try: xmin, xmax, ymin, ymax = bounding_box(paths) except Exception as e: print(e) continue dx = xmax - xmin dy = ymax - ymin viewbox = '{} {} {} {}'.format(xmin, ymin, dx, dy) attr = {'viewBox': viewbox, 'preserveAspectRatio': 'xMidYMid meet'} wsvg(paths=paths, svg_attributes=attr, filename=source) doc = SaxDocument(source) d = doc.get_pathd_and_matrix()[0] g = Group() dwg = svgwrite.Drawing(target) dwg.viewbox(minx=xmin, miny=ymin, width=dx, height=dy) dwg.add(g) g.scale(sx=1, sy=-1) g.translate(tx=0, ty=-dy - ymin * 2) g.add(dwg.path(d)) dwg.save() generate_pdf(target)
def save_svgpathtools(self, filename): lines = [] for x, y, _ in self.lines: for i in range(len(x) - 1): lines.append( svg.Line(x[i] + y[i] * -1j, x[i + 1] + y[i + 1] * -1j)) line_colors = [ color for x, _, color in self.lines for _ in range(len(x)) ] text_path = [] for x, y, _, _, _ in self.texts: for i in range(len(x)): text_path.append( svg.Line(x[i] + y[i] * -1j, x[i] + 1.0 + y[i] * -1j)) text = [text for x, _, text, _, _ in self.texts for _ in range(len(x))] svg.wsvg(lines, line_colors, stroke_widths=[0.01] * len(lines), text=text, text_path=text_path, font_size=[0.1] * len(text), filename=filename)
def main(filename: str): paths, attributes, svg_attributes = svgpathtools.svg2paths( filename, return_svg_attributes=True) print("svg2paths ->", svg_attributes) # Let's print out the first path object and the color it was in the SVG # We'll see it is composed of two CubicBezier objects and, in the SVG file it # came from, it was red for k, path in enumerate(paths): attribs = attributes[k] path_id = attribs.get('id', None) print("============= path %s =================" % path_id) print(path) print(attribs) # and output svgpathtools.wsvg(paths, attributes=attributes, svg_attributes=svg_attributes, filename="out.svg")
def json2svg(jsonfile, outfile=None): with open(jsonfile) as f: obj = json.load(f) svg_attributes = make_svg_attributes(obj['imagePath']) attributes = [] paths = [] for shape in obj['shapes']: path = get_path(shape['points']) attr = make_attributes(shape['label']) paths.append(path) attributes.append(attr) paths.reverse() attributes.reverse() outfile = outfile or os.path.splitext(jsonfile)[0] + '.svg' svg.wsvg( paths=paths, filename=outfile, attributes=attributes, svg_attributes=svg_attributes, ) print('Wrote to {}'.format(outfile))
def add_circle(input_file, output_file): """ Adds a circle around the base icon. This will have the effect to invert the mask. """ paths, attributes, svg_attributes = svg2paths2(input_file) if len(paths) > 1: raise NotImplementedError("Can't handle more than one path for now") path = paths[0] # Scale path down new_path = scale_path_to_bbox(path, (6, 6, 42, 42)) # Add circle new_path.append(Arc(24 + 2j, 22 + 22j, 180, 0, 1, 24 + 46j)) new_path.append(Arc(24 + 46j, 22 + 22j, 180, 0, 1, 24 + 2j)) wsvg( [new_path], attributes=attributes, svg_attributes=svg_attributes, filename=output_file, )
def extract_base_icon(input_file, output_file): """Also scales the shape to use more space in the drawing""" center = 24 + 24j paths, attributes, svg_attributes = svg2paths2(input_file) if len(paths) > 1: raise NotImplementedError("Can't handle more than one path for now") path = paths[0] subpaths = path.continuous_subpaths() outer_shape = max(subpaths, key=lambda x: bbox_to_barea(x.bbox())) subpaths.remove(outer_shape) # Generate a new path with the remaining shapes new_path = Path() for subpath in subpaths: append_subpath(new_path, subpath) # Scale path up new_path = scale_path_to_bbox(new_path, (2, 2, 46, 46)) wsvg( [new_path], attributes=attributes, svg_attributes=svg_attributes, filename=output_file, )
def save_svg(self, filename): lines = [ svg.Line(start[0] + start[1] * 1j, end[0] + end[1] * 1j) for [start, end], _ in self.lines ] line_colors = [color for [_, _], color in self.lines] nodes = [point[0] + point[1] * 1j for point, _ in self.nodes] node_colors = [color for _, color in self.nodes] text_path = [ svg.Line(coordinate[0] + coordinate[1] * 1j, coordinate[0] + 100.0 + coordinate[1] * 1j) for coordinate, _, _, _ in self.texts ] text = [text for _, text, _, _ in self.texts] svg.wsvg(lines, line_colors, stroke_widths=[1.0] * len(lines), nodes=nodes, node_colors=node_colors, node_radii=[2.5] * len(nodes), text=text, text_path=text_path, font_size=[5] * len(text), filename=filename)
def render_svg(svg, width=None, height=None): drawing = wsvg( paths=svg.paths, attributes=svg.attributes, paths2Drawing=True, ) fd = StringIO() drawing.write(fd) fo = BytesIO() svg2png(bytestring=fd.getvalue(), write_to=fo, output_width=width, output_height=height) fo.seek(0) return imread(fo, format="png")
def invTransect(T, sorted_ring_list, warnifnotunique=True): """Finds a transect that ends at T. In the case there are more than one, if warnifnotunique=True, user will be warned, but this may slow down transect generation. Output: list of tuples (pt, ring_idx, seg_idx, t)""" cur_ring = sorted_ring_list[-1] cur_idx = len(sorted_ring_list) - 2 init_t,init_seg = pathT2tseg(cur_ring.path,T) init_seg_idx = cur_ring.path.index(init_seg) transect_info = [(cur_ring.point(T), len(sorted_ring_list) - 1, init_seg_idx, init_t)] cur_pt = transect_info[-1][0] while cur_idx > 0: # #DEBUG # if cur_pt == (53.13478144019948+284.79773905194884j): # bla=1 # #end of debug # Find all rings this transect segment could be coming from test_rings = [] r_idx = cur_idx - 1 while r_idx >= 0: r = sorted_ring_list[r_idx] test_rings.append((r_idx, r)) if r.path.isclosed(): break r_idx -= 1 test_ring_results = [] for r_idx, test_ring in test_rings: args = (cur_pt, test_ring.path, cur_ring) inward_segt_list = isPointOutwardOfPath(*args, justone=False) for seg_idx, t in inward_segt_list: test_ring_results.append((r_idx, seg_idx, t)) # # if the user asked for that # if len(inward_segt_list) > 1 and warnifnotunique: # warn("The transect ending at T=%s is likely not unique." % T) # sort choices by distance to cur_pt def dist(res_): r_idx_, seg_idx_, t_ = res_ new_pt_ = sorted_ring_list[r_idx_].path[seg_idx_].point(t_) return abs(cur_pt - new_pt_) sorted_results = sorted(test_ring_results, key=dist) # Find the closest result such that the transect does not go through # any other rings on it's way to cur_pt for res in sorted_results: wr_idx, wseg_idx, wt = res new_pt = sorted_ring_list[wr_idx].path[wseg_idx].point(wt) tr_line = Line(new_pt, cur_pt) winner = not any(r.path.intersect(tr_line) for ri, r in test_rings if ri != wr_idx) if winner: break else: if opt.skip_transects_that_dont_exist: bdry_ring = sorted_ring_list[-1] s_rel = bdry_ring.path.length(T1=T) / bdry_ring.path.length() from os import path as os_path fn = sorted_ring_list[0].svgname + "_partial_transect_%s.svg" % s_rel fn = os_path.join(opt.output_directory, fn) wsvg([r.path for r in sorted_ring_list], nodes=[tr[0] for tr in transect_info], filename=fn) warn("\nNo transect exists ending at relative arc " "length %s. An svg displaying this partial transect has" "been saved to:\n%s\n" % (s_rel, fn)) return [] elif opt.accept_transect_crossings: wr_idx, wseg_idx, wt = sorted_results[0] else: disvg([r.path for r in sorted_ring_list], nodes=[tr[0] for tr in transect_info]) # DEBUG line bdry_ring = sorted_ring_list[-1] s_rel = bdry_ring.path.length(T1=T) / bdry_ring.path.length() raise Exception("No transect exists ending at relative arc " "length %s." % s_rel) # Record the closest choice transect_info.append((sorted_ring_list[wr_idx].path[wseg_idx].point(wt), cur_idx, wseg_idx, wt)) cur_ring = sorted_ring_list[wr_idx] cur_pt = transect_info[-1][0] cur_idx = wr_idx #Erroneous Termination if cur_idx < 0 and sorted_ring_list.index(cur_ring) != 0: disvg([r.path for r in sorted_ring_list], nodes=[tr[0] for tr in transect_info]) # DEBUG line bdry_ring = sorted_ring_list[-1] s_rel = bdry_ring.path.length(T1=T) / bdry_ring.path.length() raise Exception("Something went wrong finding inverse transect at " "relative arc length %s." % s_rel) return transect_info
def fix_svg(ring_list, center, svgfile): # Discard inappropriately short rings from options4rings import appropriate_ring_length_minimum opt.basic_output_on.dprint("\nChecking for inappropriately short " "rings...",'nr') tmp_len = len(ring_list) short_rings = [idx for idx, ring in enumerate(ring_list) if ring.path.length() < appropriate_ring_length_minimum] opt.basic_output_on.dprint("Done (%s inappropriately short rings " "found)."%len(short_rings)) if short_rings: if opt.create_svg_highlighting_inappropriately_short_rings: opt.basic_output_on.dprint("\nCreating svg highlighting " "inappropriately short rings...",'nr') paths = [parse_path(r.string) for r in ring_list] colors = [r.color for r in ring_list] nodes = [ring_list[idx].path.point(0.5) for idx in short_rings] center_line = [Line(center-1,center+1)] tmp = svgfile[0:len(svgfile)-4] + "_short-rings.svg" shortrings_svg_filename = os_path.join(opt.output_directory, tmp) disvg(paths + [center_line], colors + [opt.colordict['center']], nodes=nodes, filename=shortrings_svg_filename) args = appropriate_ring_length_minimum, shortrings_svg_filename mes = ("Done. SVG created highlighting short rings by placing a " "node at each short ring's midpoint. Note: since these " "rings are all under {} pixels in length, they may be hard " "to see and may even be completely covered by the node. " "SVG file saved to:\n{}").format(*args) opt.basic_output_on.dprint(mes) if opt.dont_remove_closed_inappropriately_short_rings: shortest_ring_length = min([r.path.length() for r in [ring_list[k] for k in short_rings if ring_list[k].isClosed()]]) open_short_rings = [idx for idx in short_rings if not ring_list[idx].isClosed()] num_short_and_closed = len(short_rings)-len(open_short_rings) if num_short_and_closed: sug_tol = (opt.tol_isNear * shortest_ring_length / opt.appropriate_ring_length_minimum) warn("{} inappropriately short closed rings detected (and not " "removed as " "dont_remove_closed_inappropriately_short_rings = True). " " You should probably decrease tol_isNear to something " "less than {} and restart this file." "".format(num_short_and_closed, sug_tol)) short_rings = open_short_rings if opt.remove_inappropriately_short_rings: opt.basic_output_on.dprint("\nRemoving inappropriately short " "rings...",'nr') ring_list = [ring for idx,ring in enumerate(ring_list) if idx not in short_rings] opt.basic_output_on.dprint("Done (%s inappropriately short rings " "removed)."%(tmp_len - len(ring_list))) else: warn("{} inappropriately short rings were found, but " "remove_inappropriately_short_rings is set to False." "".format(len(ring_list))) print("") # Remove very short segments from rings def _remove_seg(path, _seg_idx, _newjoint): _new_path = [x for x in path] pathisclosed = path[-1].end == path[0].start # stretch next segment if _seg_idx != len(path) - 1 or pathisclosed: old_bpoints = _new_path[(_seg_idx + 1) % len(path)].bpoints() new_bpoints = (_newjoint,) + old_bpoints[1:] _new_path[(_seg_idx + 1) % len(path)] = bezier_segment(*new_bpoints) # stretch previous segment if _seg_idx != 0 or pathisclosed: old_bpoints = _new_path[(_seg_idx - 1) % len(path)].bpoints() new_bpoints = old_bpoints[:-1] + (_newjoint,) _new_path[(_seg_idx - 1) % len(path)] = bezier_segment(*new_bpoints) # delete the path to be removed del _new_path[_seg_idx] return _new_path if opt.min_relative_segment_length: for r_idx, r in enumerate(ring_list): min_seg_length = r.path.length() * opt.min_relative_segment_length new_path = [s for s in r.path] its = 0 flag = False while its < len(r.path): its += 1 for seg_idx, seg in enumerate(new_path): if seg.length() < min_seg_length: flag = True if seg == new_path[-1] and not r.path.isclosed(): newjoint = seg.end elif seg == new_path[0].start and not r.path.isclosed(): newjoint = seg.start else: newjoint = seg.point(0.5) new_path = _remove_seg(new_path, seg_idx, newjoint) break else: break if flag: ring_list[r_idx].path = Path(*new_path) # Close approximately closed rings for r in ring_list: r.fixClosure() # Palette check from svg2rings import palette_check ring_list = palette_check(ring_list) # Check for and fix inconsistencies in closedness of rings from svg2rings import closedness_consistency_check ring_list = closedness_consistency_check(ring_list) # Remove self-intersections in open rings if opt.remove_self_intersections: rsi_start_time = current_time() fixable_count = 0 print("Checking for self-intersections...") bad_rings = [] for r_idx, r in enumerate(ring_list): if r.path.end == r.path.start: continue first_half = r.path.cropped(0, 0.4) second_half = r.path.cropped(0.6, 1) middle_peice = r.path.cropped(0.4, 0.6) inters = first_half.intersect(second_half) if inters: if len(inters) > 1: Ts = [info1[0] for info1, info2 in inters] bad_rings.append((r_idx, Ts)) continue else: fixable_count += 1 T1, seg1, t1 = inters[0][0] T2, seg2, t2 = inters[0][1] if not opt.force_remove_self_intersections: print("Self-intersection detected!") greenpart = first_half.cropped(0, T1) redpart = second_half.cropped(T2, 1) new_path = [seg for seg in first_half.cropped(T1, 1)] new_path += [seg for seg in middle_peice] new_path += [seg for seg in second_half.cropped(0, T2)] new_path = Path(*new_path) if opt.force_remove_self_intersections: dec = True else: print("Should I remove the red and green sections?") disvg([greenpart, new_path, redpart], ['green', 'blue', 'red'], nodes=[seg1.point(t1)]) dec = inputyn() if dec: r.path = new_path print("Path cropped.") else: print("OK... I hope things work out for you.") if bad_rings: paths = [r.path for r in ring_list] colors = [r.color for r in ring_list] center_line = Line(center-1, center+1) nodes = [] for r_idx, Ts in bad_rings: for T in Ts: nodes.append(ring_list[r_idx].path.point(T)) colors[r_idx] = opt.colordict['safe2'] node_colors = [opt.colordict['safe1']] * len(nodes) tmp = svgfile[0:len(svgfile)-4] + "_SelfIntersections.svg" fixed_svg_filename = os_path.join(opt.output_directory, tmp) disvg(paths + [center_line], colors + [opt.colordict['center']], nodes=nodes, node_colors=node_colors, filename=fixed_svg_filename) tmp_mes = ( "Some rings contained multiple self-intersections, you better " "take a look. They must be fixed manually (in Inkscape or " "Adobe Illustrator). An svg has been output highlighting the " "rings which must be fixed manually (and the points where the " "self-intersections occur). Fix the highlighted rings and " "replace your old svg with the fixed one (the colors/circles " "used to highlight the intersections will be fixed/removed " "automatically).\n Output svg saved to:\n" "{}".format(fixed_svg_filename)) raise Exception(tmp_mes) et = format_time(current_time()-rsi_start_time) print("Done fixing self-intersections ({} detected in {})." "".format(fixable_count, et)) # Check that all rings are smooth (search for kinks and round them) if opt.smooth_rings: print("Smoothing paths...") bad_rings = [] for r_idx, r in enumerate(ring_list): args = (r.path, opt.maxjointsize, opt.tightness, True) r.path = smoothed_path(*args) still_kinky_list = kinks(r.path) if still_kinky_list: bad_rings.append((r_idx, still_kinky_list)) # If unremovable kinks exist, tell user to remove them manually if opt.ignore_unremovable_kinks or not bad_rings: opt.rings_may_contain_unremoved_kinks = False else: paths = [r.path for r in ring_list] colors = [r.color for r in ring_list] center_line = Line(center-1, center+1) nodes = [] for r_idx, kink_indices in bad_rings: for idx in kink_indices: kink = ring_list[r_idx].path[idx].start nodes.append(kink) colors[r_idx] = opt.colordict['safe2'] node_colors = [opt.colordict['safe1']] * len(nodes) tmp = svgfile[0:len(svgfile)-4] + "_kinks.svg" fixed_svg_filename = os_path.join(opt.output_directory, tmp) disvg(paths + [center_line], colors + [opt.colordict['center']], nodes=nodes, node_colors=node_colors, filename=fixed_svg_filename) raise Exception("Some rings contained kinks which could not be " "removed automatically. " "They must be fixed manually (in inkscape or " "adobe illustrator). An svg has been output " "highlighting the rings which must be fixed " "manually (and the points where the " "kinks occur). Fix the highlighted " "rings and replace your old svg with the fixed " "one (the colors/circles used to highlight the " "kinks will be fixed/removed automatically).\n" "Output svg saved to:\n" "%s" % fixed_svg_filename) print("Done smoothing paths.") # Check for overlapping ends in open rings if opt.check4overlappingends: print("Checking for overlapping ends (that do not intersect)...") bad_rings = [] for r_idx, r in enumerate(ring_list): if r.path.isclosed(): continue startpt = r.path.start endpt = r.path.end path_wo_start = r.path.cropped(.1, 1) path_wo_end = r.path.cropped(0, .9) start_is_outwards = isPointOutwardOfPath(startpt, path_wo_start) end_is_outwards = isPointOutwardOfPath(endpt, path_wo_end) if start_is_outwards: bad_rings.append((r_idx, 0, start_is_outwards)) if end_is_outwards: bad_rings.append((r_idx, 1, end_is_outwards)) if bad_rings: paths = [r.path for r in ring_list] colors = [r.color for r in ring_list] center_line = Line(center-1, center+1) for r_idx, endbin, segts in bad_rings: colors[r_idx] = opt.colordict['safe2'] # indicator lines indicator_lines = [] for r_idx, endbin, segts in bad_rings: bad_path = ring_list[r_idx].path endpt = bad_path.point(endbin) for bad_seg_idx, bad_t in segts: bad_pt = bad_path[bad_seg_idx].point(bad_t) indicator_lines.append(Line(bad_pt, endpt)) indicator_cols = [opt.colordict['safe1']] * len(indicator_lines) tmp = svgfile[0:len(svgfile)-4] + "_OverlappingEnds.svg" fixed_svg_filename = os_path.join(opt.output_directory, tmp) disvg(paths + [center_line] + indicator_lines, colors + [opt.colordict['center']] + indicator_cols, filename=fixed_svg_filename) bad_ring_count = len(set(x[0] for x in bad_rings)) tmp_mes = ( "Detected {} rings with overlapping (but not intersecting) " "ends. They must be fixed manually (e.g. in Inkscape or " "Adobe Illustrator). An svg has been output highlighting the " "rings which must be fixed manually. Fix the highlighted " "rings, remove the,indicator lines added, and replace your " "old svg with the fixed one (the colors used to highlight the " "intersections will be fixed automatically).\nIf the " "indicator lines do not appear to be normal to the ring, this " "is possibly caused by a very short path segment. In this " "case, you may want to try increasing " "min_relative_segment_length in options and running again.\n" "Output svg saved to:\n" "{}".format(bad_ring_count, fixed_svg_filename)) raise Exception(tmp_mes) print("Done checking for overlapping ends.") # Trim paths with high curvature (i.e. curly) ends if opt.remove_curly_ends: print("Trimming high curvature ends...") for ring in ring_list: if ring.isClosed(): continue # 90 degree turn in distance of opt.tol_isNear tol_curvature = 2**.5 / opt.tol_isNear #####Tolerance # Find any points within tol_isNear of start and end that have # curvature equal to tol_curvature, later we'll crop them off from svgpathtools import real, imag from svgpathtools.polytools import polyroots01 def icurvature(seg, kappa): """returns a list of t-values such that 0 <= t<= 1 and seg.curvature(t) = kappa.""" z = seg.poly() x, y = real(z), imag(z) dx, dy = x.deriv(), y.deriv() ddx, ddy = dx.deriv(), dy.deriv() p = kappa**2*(dx**2 + dy**2)**3 - (dx*ddy - ddx*dy)**2 return polyroots01(p) # For first segment startseg = ring.path[0] ts = icurvature(startseg, tol_curvature) ts = [t for t in ts if startseg.length(t1=t) < opt.tol_isNear] if ts: T0 = ring.path.t2T(0, max(ts)) else: T0 = 0 # For last segment endseg = ring.path[-1] ts = icurvature(endseg, tol_curvature) ts = [t for t in ts if endseg.length(t0=t) < opt.tol_isNear] if ts: T1 = ring.path.t2T(-1, min(ts)) else: T1 = 1 # crop (if necessary) if T0 != 0 or T1 != 1: ring.path = ring.path.cropped(T0, T1) print("Done trimming.") # Check that there are no rings end outside the boundary ring (note # intersection removal in next step makes this sufficient) print("Checking for rings outside boundary ring...") boundary_ring = max([r for r in ring_list if r.isClosed()], key=lambda rgn: rgn.maxR) outside_mark_indices = [] for idx, r in enumerate(ring_list): if r is not boundary_ring: pt_outside_bdry = center + 2*boundary_ring.maxR if not ptInsideClosedPath(r.path[0].start, pt_outside_bdry, boundary_ring.path): outside_mark_indices.append(idx) if outside_mark_indices: ring_list = [r for i,r in enumerate(ring_list) if i not in outside_mark_indices] warn("%s paths were found outside the boundary path and will be " "ignored." % len(outside_mark_indices)) print("Done removing rings outside of boundary ring.") # Remove intersections (between distinct rings) if opt.rings_may_contain_intersections: print("Removing intersections (between distinct rings)...") from noIntersections4rings import remove_intersections_from_rings opt.basic_output_on.dprint("Now attempting to find and remove all " "intersections from rings (this will take a " "long time)...") intersection_removal_start_time = current_time() ring_list, intersection_count, overlappingClosedRingPairs = \ remove_intersections_from_rings(ring_list) if not overlappingClosedRingPairs: tot_ov_time = format_time(current_time() - intersection_removal_start_time) opt.basic_output_on.dprint("Done (in just %s). Found and removed %s " "intersections." % (tot_ov_time, intersection_count)) else: # fixed_paths = [parse_path(r.string) for r in ring_list] fixed_paths = [r.path for r in ring_list] fixed_colors = [r.color for r in ring_list] center_line = Line(center-1, center+1) nodes = [] for i, j in overlappingClosedRingPairs: fixed_colors[i] = opt.colordict['safe1'] fixed_colors[j] = opt.colordict['safe2'] inters = pathXpathIntersections(ring_list[i].path,ring_list[j].path) nodes += [inter[0].point(inter[2]) for inter in inters] tmp = svgfile[0:len(svgfile)-4] + "_ClosedRingsOverlap.svg" fixed_svg_filename = os_path.join(opt.output_directory, tmp) disvg(fixed_paths + [center_line], fixed_colors + [opt.colordict['center']], nodes=nodes, filename=fixed_svg_filename) raise Exception("Found %s pair(s) over overlapping closed rings. " "They must be fixed manually (in inkscape or " "adobe illustrator). An svg has been output " "highlighting the rings which must be separated " "manually (and the points where they intersect). " "Fix the highlighted rings and replace your old " "svg with the fixed one (the colors/circles used " "to highlight the intersections will be " "fixed/removed automatically).\n" "Output svg saved to:\n" "%s" % (len(overlappingClosedRingPairs), fixed_svg_filename)) # Output a fixed SVG that is (hopefully) how this SVG would be if humans # were perfect from options4rings import create_fixed_svg if create_fixed_svg: opt.basic_output_on.dprint("Now creating a fixed svg file...", 'nr') fixed_paths = [r.path for r in ring_list] fixed_colors = [r.color for r in ring_list] center_line = Line(center - 1, center + 1) tmp = svgfile[0:len(svgfile)-4] + "_fixed.svg" fixed_svg_filename = os_path.join(opt.output_directory, tmp) wsvg(fixed_paths + [center_line], fixed_colors + [opt.colordict['center']], filename=fixed_svg_filename) opt.basic_output_on.dprint("Done. SVG file saved to:\n" "%s" % fixed_svg_filename)
def find_ring_areas(sorted_ring_list, center, svgfile): # This codeblock creates a one pixel by one pixel square Ring object to # act as the core - it is recorded in CP. note: perimeter should be found # as a path and treated at a ring already csd = centerSquare(center) csd_path = parse_path(csd) if not isCCW(csd_path, center): csd_path = reversePath(csd_path) # path_string, color, brooke_tag, center center_square = Ring(csd, colordict['center'], 'not recorded', Radius(center), csd_path) # Converts the sorted_ring_list into a CP_Boolset of # complete rings each containing their IRs completeRing_CPB = CP_BoolSet() innerRing = center_square innerRing_index = -1 for ring_index, ring in enumerate(sorted_ring_list): # when next closed ring found create CompleteRing object, # then set all inbetween rings to be IRs if ring.isClosed(): completeRing_CPB.append(CompleteRing(innerRing, ring)) for inc_ring in sorted_ring_list[innerRing_index+1:ring_index]: ir = IncompleteRing(inc_ring) ir.set_inner(innerRing) ir.set_outer(ring) completeRing_CPB.cpUpdate(CompleteRing(ir.innerCR_ring, ir.outerCR_ring, ir)) innerRing = ring innerRing_index = ring_index # Check (once again) that the last sort-suggested # boundary is closed and correctly colored bdry_ring = sorted_ring_list[-1] if bdry_ring.color != colordict['boundary'] or not bdry_ring.isClosed(): ###DEBUG Why is this necessary? Isn't this fixed earlier? if sorted_ring_list[-1] == max(sorted_ring_list, key=lambda r: r.maxR): sorted_ring_list[-1].color = colordict['boundary'] else: raise Exception("Last ring in sorted sorted_ring_list was not " "closed... this should be outer perimeter.") # identify the center square created earlier as the core completeRing_CPB[0].isCore = True basic_output_on.dprint("All complete_ring objects created and all " "incomple_ring objects created (and stored inside " "the appropriate complete_ring object).") # complete the incomplete rings CP_start_time = start_time_ring_completion = current_time() for count,cp in enumerate(completeRing_CPB): if count: CP_start_time = current_time() try: cp.completeIncompleteRings() except: if outputTroubledCPs: paths = ([cp.inner.path, cp.outer.path] + [ir.ring.path for ir in cp.ir_boolset] + [sorted_ring_list[-1].path]) path_colors = ([cp.inner.color, cp.outer.color] + [ir.ring.color for ir in cp.ir_boolset] + [colordict['boundary']]) center_line = Line(cp.inner.center-1,cp.inner.center+1) svgname = os_path.join(output_directory_debug,"trouble_"+svgfile) disvg(paths,path_colors,lines=[center_line],filename=svgname) print("Simplified SVG created containing troublesome section " "(troublesome incomplete ring colored {}) and saved " "to:\n{}".format(colordict['safe1'], svgname)) raise mes = ("{}/{} complete rings finished. This CP = {} | Total ET = {}" "".format(count + 1, len(completeRing_CPB), format_time(current_time()-CP_start_time), format_time(current_time()-start_time_ring_completion))) showCurrentFilesProgress.dprint(mes) outputFile = os_path.join(output_directory, svgfile + '_completeRing_info.csv') with open(outputFile, "wt") as out_file: out_file.write("complete ring index, type, # of IRs contained, minR, " "maxR, aveR, area, area Ignoring IRs\n") cp_index = 0 for cp in completeRing_CPB: cp_index += 1 out_file.write(cp.info(cp_index,colordict) + '\n') # Create SVG showing areas (i.e. showing completed paths) if create_SVG_showing_area_paths: basic_output_on.dprint("Attempting to create SVG showing completed " "paths used for area computation...", 'nr') svgpaths = [] svgcolors = [] for cp in completeRing_CPB: svgpaths.append(cp.inner.path) svgcolors.append(cp.inner.color) for ir in cp.ir_boolset: svgpaths.append(ir.completed_path) svgcolors.append(ir.ring.color) if cp.outer.color == colordict['boundary']: svgpaths.append(cp.outer.path) svgcolors.append(cp.outer.color) tmp = svgfile[0:len(svgfile)-4] + "_area_paths" + ".svg" svgname = os_path.join(output_directory_debug, tmp) wsvg(svgpaths, svgcolors, filename=svgname) basic_output_on.dprint("Done.")