def fillSvg(svg_path, svg_d, width=750, height=750): p = parse_path(svg_d) intersections = [] for i in range(height, 0, -5): newline = Line(complex(0, i), complex(100000, i)) intersect = p.intersect(newline) # print(intersect) indiv_sections = [] if (intersect): for (T1, seg1, t1), (T2, seg2, t2) in intersect: point = p.point(T1) if point: point_tuple = (point.real, point.imag) # print(point_tuple) indiv_sections.append(point_tuple) # p.append(newline) print(indiv_sections) pairs = list( zip(indiv_sections, indiv_sections[1:] + indiv_sections[:1])) del pairs[1::2] for pair in pairs: x0 = pair[0][0] x1 = pair[1][0] y = pair[0][1] # print("( "+ x0 + ", " + y + "), (" + x1 + ", " + y + ")") betweenLine = Line(complex(x0, y), complex(x1, y)) p.append(betweenLine) disvg(p)
def _report_unfixable_kinks(_path,_kink_list): mes = ("\n%s kinks have been detected at that cannot be smoothed.\n" "To ignore these kinks and fix all others, run this function " "again with the second argument 'ignore_unfixable_kinks=True' " "The locations of the unfixable kinks are at the beginnings of " "segments: %s" % (len(_kink_list), _kink_list)) disvg(_path, nodes=[_path[idx].start for idx in _kink_list]) raise Exception(mes)
def display2rings4user(green_index,red_index,ring_list,mode=None): from options4rings import try_to_open_svgs_in_browser, colordict filename = 'temporary_4manualSorting.svg' save_location = os_path.join(getcwd(),filename) center = ring_list[0].center boundary_ring = max(ring_list,key=lambda r: r.maxR) green='#00FF00';red='#FF0000';bdry_col=colordict['boundary'] if mode==None or mode=='b': disp_paths = [ring_list[green_index].path,ring_list[red_index].path] disp_path_colors = [green,red] #['green', 'red'] disp_paths+= [ring_list[index].path for index in xrange(len(ring_list)) if index not in [green_index,red_index]] disp_path_colors += ['black']*(len(ring_list)-2) elif mode=='g': #just display green ring (and boundary and center) if boundary_ring == ring_list[green_index]: disp_paths = [boundary_ring.path] disp_path_colors = [green] else: disp_paths = [ring_list[green_index].path, boundary_ring.path] disp_path_colors = [green,'black'] elif mode=='r': #just display green ring (and boundary and center) if boundary_ring == ring_list[red_index]: disp_paths = [boundary_ring.path] disp_path_colors = [red] else: disp_paths = [ring_list[red_index].path, boundary_ring.path]; disp_path_colors = [red,'black'] elif mode=='b+': if boundary_ring == ring_list[green_index] or boundary_ring == ring_list[red_index]: disp_paths = [ring_list[green_index].path,ring_list[red_index].path] disp_path_colors = [green,red] else: disp_paths = [ring_list[green_index].path,ring_list[red_index].path,boundary_ring.path] disp_path_colors = [green,red,bdry_col] elif mode=='rb': assert boundary_ring != ring_list[green_index] and boundary_ring != ring_list[red_index] disp_paths = [ring_list[green_index].path,ring_list[red_index].path,boundary_ring.path] disp_path_colors = [ring_list[green_index].color, ring_list[red_index].color, bdry_col] else: Exception("There is no such setting, 'mode=%s'."%mode) if mode=='db': disvg(disp_paths + [Line(center-1, center+1)], disp_path_colors + [colordict['center']], filename=filename, openinbrowser=try_to_open_svgs_in_browser) else: disvg(disp_paths, disp_path_colors, nodes=[center], node_colors=[colordict['center']], filename=filename, openinbrowser=try_to_open_svgs_in_browser) print('SVG displaying rings at question saved (temporarily) as:\n' + save_location)
def dis(paths,colors=None,nodes=None,node_colors=None,node_radii=None, lines=None,line_colors=None, filename=os_path.join(getcwd(),'temporary_displaySVGPaths.svg'), openInBrowser=True,stroke_width=opt.stroke_width_default, margin_size=0.1): """This is the same as disvg, but with openInBrowser=True by default""" if lines and paths: stroke_widths = [stroke_width] * len(paths + lines) elif paths: stroke_widths = [stroke_width] * len(paths) elif lines: stroke_widths = [stroke_width] * len(lines) else: stroke_widths = None disvg(paths + lines, colors + line_colors, nodes=nodes, node_colors=node_colors, node_radii=node_radii, filename=filename, openinbrowser=openInBrowser, stroke_widths=stroke_widths, margin_size=margin_size)
def translate_path_demo(path, tol=5, min_length=5): max_depth = 0 done = False while not done: print max_depth # List of Line objects in the current path current_path = [] for seg in path: if isinstance(seg, svgpathtools.Line): current_path.append(seg) else: # Bezier curves and arcs. approx_path, done = approximate_with_line_segs( seg, tol, min_length, 0, max_depth) current_path += approx_path # save an svg svgpathtools.disvg([path] + current_path, filename=("bez_demo_" + str(max_depth) + ".svg")) # TODO: add some nodes - different color for the last ones generated? max_depth += 1
def normalvf(paths, cols, l=None, sw=.1): """This will display the normal vector field for the collections of curves, `paths` (and red disks on singular points), which can be useful when checking an svg for issues with orientation, smoothness, curvature, etc.""" if not l: l = min(p.length() for p in paths) / 20 print('l = ', l) normals = [] singular_pts = [] for p in paths: for t in np.linspace(0, 1, 100): try: normals.append(Line(p.point(t), p.point(t) + l * p.normal(t))) except: singular_pts.append(p.point(t)) paths2disp = paths + normals try: colors = [att['stroke'] for att in cols] + ['purple'] * len(normals) except: colors = cols + ['purple'] * len(normals) disvg(paths2disp, colors, nodes=singular_pts, stroke_widths=[sw] * len(paths2disp))
def color_map(geo_code_dict, df, team_and_color): #recolor and create output svg output = open("output.svg", "w") output.write( "<!DOCTYPE html><html><body><svg width=\"543\" height=\"228\">") #fill in primary color i = 0 for code in geo_code_dict: geo_code_dict[code]['stroke'] = "#000000" geo_code_dict[code]['stroke-width'] = "0.5" color = team_and_color[df['Favorite Team'][i]] #change color saturation according to percentage #rgb_color = webcolors.hex_to_rgb(team_and_color[df['Favorite Team'][i]]) #hsv_color = colorsys.rgb_to_hsv(rgb_color[0],rgb_color[1],rgb_color[2]) #lst = list(hsv_color) #lst[1] = lst[1] * ((df['Percentage'][i])/100) #hsv_color = tuple(lst) #rgb_color = hsv_to_rgb(hsv_color[0],hsv_color[1],hsv_color[2]) #lst = list(rgb_color) #lst[0] = int(rgb_color[0]) #lst[1] = int(rgb_color[1]) #lst[2] = int(rgb_color[2]) #rgb_color = tuple(lst) #color = webcolors.rgb_to_hex(rgb_color) geo_code_dict[code]['fill'] = color output.write("<path d=\"%s\" stroke=\"%s\" stroke-width=\"%s\" fill=\"%s\"></path>" % \ (geo_code_dict[code]['d'], geo_code_dict[code]['stroke'], geo_code_dict[code]['stroke-width'], geo_code_dict[code]['fill'])) i = i + 1 output.write("</svg></body></html>") output.close() #output final svg map paths, attributes = svg2paths('output.svg') disvg(paths, filename='output.svg', attributes=attributes)
def wsvg(paths=None, colors=None, filename=os.path.join(os.getcwd(), 'disvg_output.svg'), 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, svgwrite_debug=False, paths2Drawing=False): #NB: this code is originally from <https://github.com/mathandy/svgpathtools>. # Thanks tho @mathandy """Convenience function; identical to disvg() except that openinbrowser=False by default. See disvg() docstring for more info.""" return disvg(paths, colors=colors, filename=filename, stroke_widths=stroke_widths, nodes=nodes, node_colors=node_colors, node_radii=node_radii, openinbrowser=openinbrowser, timestamp=timestamp, margin_size=margin_size, mindim=mindim, dimensions=dimensions, viewbox=viewbox, text=text, text_path=text_path, font_size=font_size, attributes=attributes, svg_attributes=svg_attributes, svgwrite_debug=svgwrite_debug, paths2Drawing=paths2Drawing)
import weirdvector as wv from svgpathtools import Path, Line, wsvg, disvg s = [0, 0.5, 0.8, 0.3, 1] s = wv.fractalize_mult(s,s) s = wv.fractalize_mult(s,s) path = [Line(100+100j, 200+200j)] for i in range(len(s)): path.push(Line(i*100+s[i]*2000j, (i+1)*100+s[i]*2000j) disvg(path, filename="svgtest.svg")
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)
try: paths, attributes = svgpathtools.svg2paths(args.filename) except FileNotFoundError as e: print(e) exit(1) def invert_y(c): # I feel like there should be a better way return numpy.real(c) - numpy.imag(c) * 1j if args.showsvg: color_list = 'krgbcym' color_list *= int(len(paths) / len(color_list)) + 1 color_list = color_list[:len(paths)] svgpathtools.disvg(paths, color_list) exit(0) try: if not args.test: robot = Root(args.name) else: robot = Turtle() robot.wait_for_connect() time.sleep(1) segments = numpy.linspace(0, 1, args.approximate + 1) for path in paths: for element in path: if args.verbose:
def generate_unsorted_transects(ring_list, center): from options4rings import basic_output_on, warnings_output_on, N_transects, unsorted_transect_debug_output_folder, unsorted_transect_debug_on, colordict from misc4rings import transect_from_angle, normalLineAt_t_toInnerSeg_intersects_withOuter from andysSVGpathTools import pathlistXlineIntersections from andysmod import Timer import operator from random import uniform #Find outer boundary ring for r in ring_list: if r.color == colordict['boundary']: boundary_ring = r break else: warnings_output_on.dprint("[Warning:] Having trouble finding outer boundary - it should be color %s. Will now search for a ring of a similar color and if one is found, will use that.\n"%colordict['boundary']) from misc4rings import closestColor for r in ring_list: if colordict['boundary'] == closestColor(r.color,colordict): boundary_ring = r basic_output_on.dprint("Found a ring of color %s, using that one."%r.color) break else: warnings_output_on.dprint("[Warning:] Outer boundary could not be found by color (or similar color). This is possibly caused by the outer boundary ring not being closed - in this case you'd be able to see a (possibly quite small) gap between it's startpoint and endpoint. Using the ring of greatest maximum radius as the boundary ring (and hoping if there is a gap none of the transects hit it).\n") keyfcn = lambda x: x.maxR boundary_ring = max(ring_list,key=keyfcn) #Find transects from time import time as current_time from andysmod import format_time tr_gen_start_time = current_time() data = [] data_indices = [] angles = [] for dummy_index in range(N_transects): #dummy_index only used to create loop #estimate time remaining if dummy_index != 0: total_elapsed_time = current_time() - tr_gen_start_time estimated_time_remaining = (N_transects - dummy_index)*total_elapsed_time/dummy_index timer_str = 'Transect %s of %s || Est. Remaining Time = %s || Elapsed Time = %s'%(dummy_index+1,N_transects,format_time(estimated_time_remaining),format_time(total_elapsed_time)) overwrite_progress = True else: timer_str = 'transect %s of %s'%(dummy_index+1,N_transects) overwrite_progress = False print('') #generate current transect with Timer(timer_str, overwrite=overwrite_progress): if unsorted_transect_debug_on: print('') test_angle = uniform(0, 1) # test_angle = 0.408 angles.append(test_angle) transect = [center] transect_rings = ['core'] unused_ring_indices = range(len(ring_list)) #used to keep track of which rings I've used and thus don't need to be checked in the future # Find first transect segment (from core/center) # normal line to use to find intersections (from center to boundary ring) nl2bdry, seg_outer, t_outer = transect_from_angle(test_angle, center, boundary_ring.path, 'debug') #make normal line a little longer nl2bdry = Line(nl2bdry.start, nl2bdry.start + 1.5*(nl2bdry.end-nl2bdry.start)) tmp = pathlistXlineIntersections(nl2bdry, [ring_list[i].path for i in unused_ring_indices]) (tl,path_index,seg,tp) = min(tmp, key=operator.itemgetter(0)) #(tl,path_index,seg,tp) transect.append(nl2bdry.point(tl)) transect_rings.append(unused_ring_indices[path_index]) del unused_ring_indices[path_index] #now for the rest of the transect num_rings_checked = 0 while (ring_list[transect_rings[-1]] != boundary_ring and num_rings_checked < len(ring_list)): # < is correct, already did first num_rings_checked += 1 inner_path = ring_list[transect_rings[-1]].path inner_t = tp inner_seg = seg # normal line to use to find intersections (from center to boundary ring) nl2bdry, seg_outer, t_outer = normalLineAt_t_toInnerSeg_intersects_withOuter(inner_t, inner_seg, boundary_ring.path, center, 'debug') # make normal line a little longer nl2bdry = Line(nl2bdry.start,nl2bdry.start + 1.5*(nl2bdry.end-nl2bdry.start)) normal_line_intersections = pathlistXlineIntersections(nl2bdry, [ring_list[i].path for i in unused_ring_indices]) try: # (tl,path_index,seg,tp) tl, path_index, seg, tp = min(normal_line_intersections, key=operator.itemgetter(0)) except ValueError: raise if unsorted_transect_debug_on: from andysmod import format001 inner_path_index = transect_rings[-1] used_ring_paths = [r.path for i,r in enumerate(ring_list) if i not in unused_ring_indices+[inner_path_index]] used_ring_colors = ['black']*len(used_ring_paths) unused_ring_paths = [ring_list[i].path for i in unused_ring_indices] unused_ring_colors = [ring_list[i].color for i in unused_ring_indices] transect_so_far = Path(*[Line(transect[i-1],transect[i]) for i in range(1,len(transect))]) paths = used_ring_paths + unused_ring_paths + [transect_so_far] +[inner_path] + [nl2bdry] colors = used_ring_colors + unused_ring_colors + ['green']+['blue'] + ['black'] nodes_so_far = transect[1:-1] potential_nodes = [nl2bdry.point(tltmp) for (tltmp,path_indextmp,segtmp,tptmp) in normal_line_intersections] nodes = nodes_so_far + potential_nodes node_colors = ['red']*len(nodes_so_far) + ['purple']*len(potential_nodes) save_name = unsorted_transect_debug_output_folder+'unsorted_transect_debug_%s.svg'%format001(3,len(transect)) disvg(paths,colors,nodes=nodes,node_colors=node_colors,center=center,filename=save_name,openInBrowser=False) print("Done with %s out of (at most) %s transect segments"%(len(transect),len(ring_list))) transect.append(nl2bdry.point(tl)) transect_rings.append(unused_ring_indices[path_index]) del unused_ring_indices[path_index] data.append(transect) data_indices.append(transect_rings) return data, data_indices, angles
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 completeIncompleteRings(self): """This fcn takes each ir included in self and makes a closed path to use for its area computation.""" # make sure ir_boolset is sorted (by sort index found in topological sort) self.sortIRs() if len(self.ir_boolset) == 0: return # iterate through IRs to complete them one by one, inner-most to outer-most for i,ir in enumerate(self.ir_boolset): # try all more-inner IRs (and the inner CR) starting with # most-outer among them - this is for finding transects. # Note: #poten[j] = ir_boolset[j-1].ring for j=1,...,i potential_rings = [self.inner] + [x.ring for x in self.ir_boolset[0:i]] # Find transects from the endpoints of ir to the next most-Outer # of the more-inner acceptable rings # Note: the ir's are sorted, but need to make sure each transect # is well-defined (i.e. check the potential connecting ir does in # fact travel "below" that endpoint) # Note: findTransects fcn used below will return # (False, False, False, False) for any transects already found nextRing2Try_index = i # note this is right, not i-1 cause potential rings has self.inner at beginning while not (ir.transect0found and ir.transect1found) and nextRing2Try_index >= 0: nextRing2Try = potential_rings[nextRing2Try_index] if nextRing2Try_index == 0: irORcr_2Try = self else: irORcr_2Try = self.ir_boolset[nextRing2Try_index-1] tmp = ir.findTransects2endpointsFromInnerPath_normal(irORcr_2Try, nextRing2Try.path) (irORcr0, tL0, seg0, t0), (irORcr1, tL1, seg1, t1) = tmp # check if nextRing2Try is has a transect going to the # startpoint, if so we're done with this endpoint if tL0 != False: # ring.path.point(T0) is where the transect, tL, meets # this ring T0 = segt2PathT(irORcr0.ORring.path,seg0,t0) # record up-ladder on the ring the transect connects to # (and t-val it connects at) irORcr0.up_ladders.append((ir,T0)) # record startpoint down-ladder on this ir and (and t-val # on connecting ring it connects at) ir.down_ladder0 = (irORcr0,T0) if not ir.transect0found: ir.transect0found = True else: ir.transect0fails.append(irORcr_2Try) # check if nextRing2Try has a transect going to the endpoint, # if so we're done with this endpoint if tL1 != False: # ring.path.point(T0) is where the transect, tL, meets # this ring T1 = segt2PathT(irORcr1.ORring.path, seg1, t1) # record up-ladder on the ring the transect connects to # (and t-val it connects at) irORcr1.up_ladders.append((ir, T1)) # record startpoint down_ladder on this ir and (and t-val # on connecting ring it connects at) ir.down_ladder1 = (irORcr1, T1) if not ir.transect1found: ir.transect1found = True else: ir.transect1fails.append(irORcr_2Try) # unsuccessful while-loop termination conditions if (nextRing2Try_index == 0 and not (ir.transect0found and ir.transect1found)): printPath(ir.ring.path) print(i) colors = ['blue']*(len(self.ir_boolset)+2) + ['red'] paths2disp = ([self.inner.path] + [x.ring.path for x in self.ir_boolset] + [self.outer.path] + [ir.ring.path]) disvg(paths2disp, colors) raise Exception("Acceptable more-inner ring could not be " "found.") else: nextRing2Try_index -= 1 # Now that all up/down ladders are set in this CR, iterate through IRs # again and create completed_path for each for ir in self.ir_boolset: try: ir.hardComplete() except: from options4rings import colordict # highlight ir in output SVG containing troubled section # (see area4rings) ir.ring.color = colordict['safe1'] raise
from svgpathtools import svg2paths, disvg, path paths, attrs = svg2paths("../imgs/indiana_map.svg") disvg(paths, stroke_widths=[5])
def svg2rings(SVGfileLocation): global already_warned_having_trouble_extracting_ring_colors already_warned_having_trouble_extracting_ring_colors = False def getStroke(elem): #get 'stroke' attribute fom xml object troubleFlag=False stroke = elem.getAttribute('stroke') #sometimes this works if stroke=='': style = elem.getAttribute('style') hexstart = style.find('stroke') if hexstart==-1: troubleFlag=True else: temp = style[hexstart:] try: stroke = re.search(re.compile('\#[a-fA-F0-9]*'),temp).group() except: troubleFlag=True stroke='' if troubleFlag: global already_warned_having_trouble_extracting_ring_colors if not already_warned_having_trouble_extracting_ring_colors: already_warned_having_trouble_extracting_ring_colors = True opt.warnings_output_on.dprint("Warning: Having trouble extracting hex colors from svg. Hopefully this will not matter as the palette check will fix the colors.") return stroke.upper() example_center = r'<line fill="none" stroke="#0000FF" stroke-width="0.15" x1="246.143" y1="380.017" x2="246.765" y2="380.856"/>' doc = minidom.parse(SVGfileLocation) # parseString also exists #Find the center counter = 0 centerFound = False for elem in doc.getElementsByTagName('line'): if getStroke(elem) == colordict['center']: center = 0.5*float(elem.getAttribute('x1'))+0.5*float(elem.getAttribute('x2')) + 0.5*float(elem.getAttribute('y1'))*1j +0.5*float(elem.getAttribute('y2'))*1j rad = Radius(center) centerFound = True break else: counter += 1 if counter>0 and not centerFound: opt.warnings_output_on.dprint("[Warning:] No line objects in the svg were found matching the center color (%s). Now searching for lines of a color closer to center color than other colors."%counter) for elem in doc.getElementsByTagName('line'): if closestColor(getStroke(elem),colordict) == colordict['center']: center = 0.5*float(elem.getAttribute('x1'))+0.5*float(elem.getAttribute('x2')) + 0.5*float(elem.getAttribute('y1'))*1j +0.5*float(elem.getAttribute('y2'))*1j rad = Radius(center) centerFound = True counter -=1 break if counter>0: #center found but counter>0 opt.warnings_output_on.dprint("[Warning:] There are %s disconnected lines in this SVG not matching the center color. They will be ignored."%counter) try: center.real #test if center exists (should be a complex number object) except: try: if counter == 0: #Is there a path with the center color? for elem in doc.getElementsByTagName('path')+doc.getElementsByTagName('polyline')+doc.getElementsByTagName('polygon'): if getStroke(elem) == colordict['center']: if elem in doc.getElementsByTagName('path'): obtype = 'path'; pathstr = elem.getAttribute('d') elif elem in doc.getElementsByTagName('polyline'): obtype = 'polyline'; pathstr = polylineStr2pathStr(elem.getAttribute('points')) else: obtype = 'polygon'; pathstr = polylineStr2pathStr(elem.getAttribute('points')) + 'z' centerpath = parse_path(pathstr) start,end = centerpath.point(0.25),centerpath.point(0.75) x1,x2,y1,y2 = start.real,end.real,start.imag,end.imag newelem = r'<line fill="none" stroke="%s" stroke-width="0.05" stroke-miterlimit="10" x1="%s" y1="%s" x2="%s" y2="%s"/>'%(colordict['center'],x1,y1,x2,y2) raise Exception("Center of sample should be marked by line of color %s, but no lines are present in svg. There is a %s with the center color, however. Open the svg file in a text editor and you should be able to find '%s' somewhere... replace it with '%s'"%(colordict['center'],obtype,elem,newelem)) else: for elem in doc.getElementsByTagName('path')+doc.getElementsByTagName('polyline')+doc.getElementsByTagName('polygon'): if closestColor(getStroke(elem),colordict) == colordict['center']: if elem in doc.getElementsByTagName('path'): obtype = 'path'; pathstr = elem.getAttribute('d') elif elem in doc.getElementsByTagName('polyline'): obtype = 'polyline'; pathstr = polylineStr2pathStr(elem.getAttribute('points')) else: obtype = 'polygon'; pathstr = polylineStr2pathStr(elem.getAttribute('points')) + 'z' centerpath = parse_path(pathstr) start,end = centerpath.point(0.25),centerpath.point(0.75) x1,x2,y1,y2 = start.real,end.real,start.imag,end.imag newelem = r'<line fill="none" stroke="%s" stroke-width="0.05" stroke-miterlimit="10" x1="%s" y1="%s" x2="%s" y2="%s"/>'%(colordict['center'],x1,y1,x2,y2) raise Exception("Center of sample should be marked by line of color %s, but no lines are present in svg. There is a path with color close to the center color, however. Open the svg file in a text editor and you should be able to find '%s' somewhere... replace it with '%s'"%(colordict['center'],obtype,elem,newelem)) else: raise Exception('Center of sample should be marked by line of color %s, but no lines are present in svg. There were no paths or polylines or polygons of a similar color either. Looks like you did not mark the center. Open your svg in a text editor and search for something that looks like (with different x1, x2, y1, y2 values) \n%s\n'%(colordict['center'],example_center)) except: raise Exception('No center found searching line element with (color) stroke = %s. Open your svg in a text editor and search for something that looks like (with different x1, x2, y1, y2 values) \n%s\n'%(colordict['center'],example_center)) #Use minidom to extract path strings from input SVG opt.basic_output_on.dprint("Extracting path_strings from SVG... ",'nr') path_strings = [(p.getAttribute('d'),getStroke(p),p.parentNode.getAttribute('id'),p.toxml()) for p in doc.getElementsByTagName('path')] #Use minidom to extract polyline strings from input SVG, convert to path strings, add to list path_strings += [(polylineStr2pathStr(p.getAttribute('points')),getStroke(p),p.parentNode.getAttribute('id'),p.toxml()) for p in doc.getElementsByTagName('polyline')] #Use minidom to extract polygon strings from input SVG, convert to path strings, add to list path_strings += [(polylineStr2pathStr(p.getAttribute('points'))+'z',getStroke(p),p.parentNode.getAttribute('id'),p.toxml()) for p in doc.getElementsByTagName('polygon')] #currently choosing to ignore line objects (assuming... all lines are fixes for non-overlapping mergers?) ##Use minidom to extract line strings from input SVG, convert to path strings, and add them to list #line_strings = [('M' + p.getAttribute('x1') + ' ' +p.getAttribute('y1') + 'L'+p.getAttribute('x2') + ' ' + p.getAttribute('y2'),getStroke(p), p.parentNode.getAttribute('id')) for p in doc.getElementsByTagName('line')] doc.unlink() opt.basic_output_on.dprint("Done.") # #(first attempt to) Check for stray points, if any found, delete them # i=0 # count_popped_points = 0 # while i < len(path_strings): # if path_strings[i][0].count(',')<2: # path_strings.pop(i) # count_popped_points+=1 # opt.full_output_on.dprint("Removed a stray point: path_string[%s][0] = %s"%(i,path_strings[i][0])) # i +=1 # opt.basic_output_on.dprint("Removed %s stray points in path_string stage. Continuing..."%count_popped_points) #Convert path_strings to ring objects opt.basic_output_on.dprint("Converting path strings to Ring objects. This could take a minute... ",'nr') path2ring_start_time = current_time() ring_list = [] paths_of_unknown_orientation = [] for i in range(len(path_strings)): orig_path = parse_path(path_strings[i][0]) try: ### DEBUG ONLY (REMOVE ALL OF TRY/EXCEPT) orig_path[0] except: if len(path_strings[i][0].split(','))<3: opt.full_output_on.dprint("Found (and skipped) single point path: %s"%path_strings[i][0]) continue else: raise #fix degenerate segments here for index,seg in enumerate(orig_path): if abs(seg.start-seg.end) < 1: old_end = seg.end old_start = seg.start opt.full_output_on.dprint("Found degenerate seg in path %s: %s"%(i,seg)) del orig_path[index] if index == len(orig_path): #deleted last path orig_path[-1].end = old_end elif index == 0: orig_path[0].start=old_start else: orig_path[index].start = orig_path[index-1].end opt.full_output_on.dprint("Deleted above degenerate segment and fixed gap.") #check for doubled over segments nostupidsfound = False while not nostupidsfound and len(orig_path)>1: for indst in range(len(orig_path)-1): if (orig_path[indst] == orig_path[indst+1] or orig_path[indst] == orig_path[indst+1].reversed()): del orig_path[indst+1] opt.warnings_output_on.dprint("[Warning:] "+"stupidsfound"*50) # raise Exception() #you should remove this Exception and everything will run smoothly else: nostupidsfound = True #Now fix the orientation if path is not CCW (w.r.t. center) try: path_is_ccw = isCCW(orig_path,center) except: if opt.manually_fix_orientations: print("\n[Manually Fix Orientations:] As currently drawn, the " "path starts at the green node/segment and ends at the " "red (if you don't see one of these nodes, it's likely " "cause the path is very short and thus they are on top " "of each other). Does the path in " "'temporary_4manualOrientation.svg' appear to be drawn " "in a clockwise fashion?") if len(orig_path) == 1: disp_paths = [orig_path] disp_path_colors = ['blue'] elif len(orig_path) == 2: disp_paths = [Path(orig_path[0]),Path(orig_path[1])] disp_path_colors = ['green','red'] elif len(orig_path) > 2: disp_paths = [Path(orig_path[0]),Path(orig_path[1:-1]),Path(orig_path[-1])] disp_path_colors = ['green','blue','red'] else: raise Exception("This path is empty... this should never happen. Tell Andy.") for ring in ring_list: disp_paths.append(ring.path) disp_path_colors.append('black') nodes = [orig_path[0].start,orig_path[-1].end]+[center] node_colors = ['green','red']+[colordict['center']] disvg(disp_paths, disp_path_colors, nodes=nodes, node_colors=node_colors, filename='temporary_4manualOrientation.svg') path_is_ccw = askUserOrientation() #svg display reverses orientation so a respose of 'yes' means path is actually ccw and thus sets path_is_ccw = True if path_is_ccw == 'remove': print("OK, this path will be ignored... moving onto the rest.") continue # raise Exception("The manually_fix_orientations feature is not yet setup. If you need this, ask Andy; it shouldn't take him long.") else: path_is_ccw = opt.when_orientation_cannot_be_determined_assume_CCW paths_of_unknown_orientation.append(path_strings[i]) if not path_is_ccw: path2record = orig_path.reversed() opt.full_output_on.dprint("Path %s was not oriented CCW, but is now."%i) else: path2record = orig_path ring_list.append(Ring(path_strings[i][0],path_strings[i][1],path_strings[i][2],rad,path2record,xml=path_strings[i][3])) opt.full_output_on.dprint("Ring %s ok"%i) if len(paths_of_unknown_orientation)>0: from andysmod import ifelse fashion = ifelse(opt.when_orientation_cannot_be_determined_assume_CCW,'Counterclockwise','Clockwise') ccw_warning = "[Warning:] Unable to determine orientation of %s paths. This is likely because some paths in this sample are far from being convex. I assumed that these paths were traced in a %s fashion (to change this assumption, set 'when_orientation_cannot_be_determined_assume_CCW = %s' in options. If this assumption is false, either the program will crash or the transect will be visibly messed up in the output 'xxx_transects.svg' (where xxx is the input svg's filename sans extension)."%(len(paths_of_unknown_orientation),fashion,not opt.when_orientation_cannot_be_determined_assume_CCW) opt.warnings_output_on.dprint(ccw_warning) if len(paths_of_unknown_orientation)>1: opt.warnings_output_on.dprint("If think you were not consistent tracing in either CCW or CW fashion (or don't get good output from this file) then set 'manually_fix_orientations = True' in options.") #done extracting rings from svg opt.basic_output_on.dprint("Done (in %s)."%format_time(current_time()-path2ring_start_time)) opt.basic_output_on.dprint("Completed extracting rings from SVG. %s rings detected."%len(ring_list)) return center, ring_list