Example #1
0
    def __generate(self):
        logger.debug("Starting generate...")
        upper_curve = svgp.parse_path(self.upper_curve.d)
        upper_curve_start = (upper_curve.start.real, upper_curve.start.imag)
        upper_curve_end = (upper_curve.end.real, upper_curve.end.imag)

        lower_curve = svgp.parse_path(self.lower_curve.d)
        lower_curve_start = (lower_curve.start.real, lower_curve.start.imag)
        lower_curve_end = (lower_curve.end.real, lower_curve.end.imag)

        r1 = spatial.distance.euclidean(upper_curve_end, lower_curve_start) / 2
        r2 = spatial.distance.euclidean(lower_curve_end, upper_curve_start) / 2

        p = svgwrite.path.Path()
        p.push(self.upper_curve.d)

        p.push("A", r1, r1, 0, 1, 1, lower_curve_start[0],
               lower_curve_start[1])

        p.push(self.lower_curve.d_c)

        p.push("A", r2, r2, 0, 1, 1, upper_curve_start[0],
               upper_curve_start[1])

        p.push("Z")

        # Call to either tostring or to_xml() is needed to create the dict 'd' attribute.
        p.tostring()
        self.d = p.attribs['d']
Example #2
0
def plot_lengths(a):
    """

    :param a: [{'color':'','length':5}]
    :return: [{'color': 'red', 'path': Path()}]
    """
    # Turn left 90 degrees each time
    behavior_ref = ['h -', 'v ', 'h ', 'v -']
    path_store = []
    path_str = 'M50 20j'
    count = 0
    color = a[0].get('color', 'black')
    for obj in a:

        if obj['color'] != color:
            # print 'Changed colors from %s to %s' % (color, obj['color'])
            last_point = fix_coordinate(str(parse_path(path_str).point(1.0)))
            new_path_start = 'M%s' % strip_parens(last_point)
            res = {'color': color, 'path': parse_path(path_str)}
            path_store.append(res)  # Add the Path to the line_store
            path_str = new_path_start  # Start the new path
            color = obj['color']  # With the new color

        move = behavior_ref[count] + str(obj['length'])
        path_str = ' '.join([path_str, move])
        count = 0 if count == 3 else count + 1

    # Add the last entry
    path_store.append({'color': color, 'path': parse_path(path_str)})
    return path_store
Example #3
0
def generateInBetweens(poseA, poseB, steps):
    inv = Inventory()

    # make pairs
    pairs = []
    for key in ORDER:
        if key in poseA.inv and key in poseB.inv:
            partA = poseA.inv[key]
            partB = poseB.inv[key]

            if len(partA) != 1 or len(partB) != 1:
                print('Too many parts {0} - A: {1} B: {2}'.format(
                    key, partA.keys(), partB.keys()))
                continue

            pairs.append((key, partA.values()[0], partB.values()[0]))

    # If there are 3 steps, there are 4 gaps between start and finish
    # |------1------2------3------|
    gaps = steps + 1

    # process pairs
    for key, a, b in pairs:
        pathA = parse_path(a['d'])
        pathB = parse_path(b['d'])

        if len(pathA) != len(pathB):
            print('Unmatched segments {0} - A: {1} B: {2}'.format(
                key, pathA, pathB))
            continue

        for step in range(1, gaps):
            newPath = Path()
            for i in range(len(pathA)):
                segA = pathA[i]
                segB = pathB[i]

                if isinstance(segA, Line):
                    points = _deltaPoints([segA.start, segA.end],
                                          [segB.start, segB.end], step, gaps)
                    newPath.append(Line(*points))

                elif isinstance(segA, CubicBezier):
                    points = _deltaPoints(
                        [segA.start, segA.control1, segA.control2, segA.end],
                        [segB.start, segB.control1, segB.control2, segB.end],
                        step, gaps)
                    newPath.append(CubicBezier(*points))

            newPart = Part(newPath.d())
            newPart['x'] = int(_delta(a['x'], b['x'], step, gaps))
            newPart['y'] = int(_delta(a['y'], b['y'], step, gaps))
            newPart['z'] = int(_delta(a['z'], b['z'], step, gaps))

            inv.addPart(key, newPart)
            print(key, step, newPart)

    return inv
    def effect(self):
        if len(self.options.ids) < 2:
            inkex.errormsg(_("This extension requires two selected paths."))
            exit()

        obj = self.selected[self.options.ids[0]]
        envelope = self.selected[self.options.ids[1]]

        if obj.get(inkex.addNS('type', 'sodipodi')):
            inkex.errormsg(
                _("The first selected object is of type '%s'.\nTry using the procedure Path->Object to Path."
                  % obj.get(inkex.addNS('type', 'sodipodi'))))
            exit()

        if obj.tag == inkex.addNS('path', 'svg') or obj.tag == inkex.addNS(
                'g', 'svg'):
            if envelope.tag == inkex.addNS('path', 'svg'):
                absolute_envelope_path = svgpathtools.parse_path(
                    envelope.get("d")).d()
                coords_to_project = self.envelope2coords(
                    absolute_envelope_path)

                if obj.tag == inkex.addNS('path', 'svg'):
                    absolute_object_path = svgpathtools.parse_path(
                        obj.get("d")).d()
                if obj.tag == inkex.addNS('g', 'svg'):
                    absolute_object_path = ""
                    for p in obj.iterfind(
                            ".//{http://www.w3.org/2000/svg}path"):
                        absolute_object_path += svgpathtools.parse_path(
                            p.get("d")).d()

                #inkex.debug(absolute_object_path)
                new_path = projection(absolute_object_path, coords_to_project)
                attributes = {'d': new_path}
                new_element = inkex.etree.SubElement(
                    self.current_layer, inkex.addNS('path', 'svg'), attributes)

            else:
                if envelope.tag == inkex.addNS('g', 'svg'):
                    inkex.errormsg(
                        _("The second selected object is a group, not a path.\nTry using the procedure Object->Ungroup."
                          ))
                else:
                    inkex.errormsg(
                        _("The second selected object is not a path.\nTry using the procedure Path->Object to Path."
                          ))
                exit()
        else:
            inkex.errormsg(
                _("The first selected object is not a path.\nTry using the procedure Path->Object to Path."
                  ))
            exit()
Example #5
0
def detect_indeterminate_line(glyph_info, lines):
    top_line = glyph_info[1][0]
    bottom_line = glyph_info[1][1]
    glyph_path = glyph_info[0]
    glyph_box = glyph_path.bbox()
    glyph_min = glyph_box[0] - search_width
    glyph_max = glyph_box[1] + search_width

    if top_line != 1 or bottom_line != 2 or glyph_min < 250:
        return None, None

    def within_bounds(comparison_path, x_min, x_max):
        comp = comparison_path.bbox()
        left_check = x_min <= comp[0] <= x_max
        right_check = x_min <= comp[1] <= x_max
        overlap_check = comp[0] <= x_min and comp[1] >= x_max
        return left_check or right_check or overlap_check

    top_paths = lines[top_line].d().split("M")
    top_paths = filter(lambda x: len(x.strip()) > 0, top_paths)
    top_paths = map(lambda x: svgpathtools.parse_path(f"M{x}"), top_paths)
    top_paths = filter(lambda x: within_bounds(x, glyph_min, glyph_max),
                       top_paths)
    top_paths = list(top_paths)

    bottom_paths = lines[bottom_line].d().split("M")
    bottom_paths = filter(lambda x: len(x.strip()) > 0, bottom_paths)
    bottom_paths = map(lambda x: svgpathtools.parse_path(f"M{x}"),
                       bottom_paths)
    bottom_paths = filter(lambda x: within_bounds(x, glyph_min, glyph_max),
                          bottom_paths)
    bottom_paths = list(bottom_paths)

    def min_distance(undetermined_path, comparison_paths):
        minimum_distance = 999999999
        nearest_point = None
        for _path in comparison_paths:
            distance, t = path_distance(undetermined_path, _path)
            if distance < minimum_distance:
                minimum_distance = distance
                nearest_point = _path.point(t)

        return minimum_distance, nearest_point

    top_distance, nearest_top_point = min_distance(glyph_path, top_paths)
    bottom_distance, nearest_bottom_point = min_distance(
        glyph_path, bottom_paths)

    if top_distance < bottom_distance:
        return top_line, nearest_top_point
    else:
        return bottom_line, nearest_bottom_point
Example #6
0
    def load_curves_from_svg(self, file):
        svg_tree = ET.parse(file)
        svg_root = svg_tree.getroot()
        self.width = int(float(svg_root.attrib['viewBox'].split()[2]))
        self.height = int(float(svg_root.attrib['viewBox'].split()[3]))

        for xml_path in svg_root.iter('{http://www.w3.org/2000/svg}path'):

            path = parse_path(xml_path.attrib['d'])

            path_len = path.length(error=0.00001)

            path_points = []

            d = xml_path.attrib['d'][1:].split(',')

            num_points = int(path_len * self.point_density)
            for i in range(num_points):
                path_points.append(path.point(i / num_points))

            css_class = '.' + xml_path.attrib['class']
            style_str = svg_root[0].text
            splited_styles = style_str.split(';}')
            color = list(
                substr for substr in splited_styles
                if substr.find(css_class) != -1)[0].split('stroke:#')[1][:6]
            color = tuple(int(color[i:i + 2], 16)
                          for i in (4, 2, 0))  #FORMAT: (B, G, R)

            self.curves.append(Curve(path_points, color))
Example #7
0
def SVGPathToTrackPoints(fname, spacing=TRACK_SPACING):
    path = svgpathtools.parse_path(open(fname).read())

# input: svg based on a 100px / m image
    def next_t(path, t, dist):
        p = path.point(t)
        L = path.length()
        # t += 1.0 / np.abs(path.derivative(t))
        itr = 0
        while itr < 20:
            itr += 1
            p1 = path.point(t)
            err = np.abs(p1 - p) - dist
            d1 = path.derivative(t)
            if np.abs(err) < 1e-5:
                return t, p1, d1 / np.abs(d1)
            derr = np.abs(d1) * L
            # do a step in Newton's method (clipped because some of the
            # gradients in the curve are really small)
            t -= np.clip(err / derr, -1e-2, 1e-2)
            t = np.clip(t, 0, 1)
        return t, p, d1 / np.abs(d1)

    d0 = path.derivative(0)
    pts = [[path.point(0), d0 / np.abs(d0)]]
    t = 0
    while t < 1:
        t, p, d = next_t(path, t, spacing)
        pts.append([p, d])

    return pts
Example #8
0
def get_symbol_geometries(svg_paths):
    symbol_geometries = []

    for svg_path in svg_paths:
        # split svg path into it's continuous parts
        # split: without the M's in front, the first one is an empty string
        pathSegs_strs_split = svg_path.d().split("M")[1:]
        # with the M's back in front
        pathSegs_strs = ["M " + s for s in pathSegs_strs_split]

        cont_paths = [svgpathtools.parse_path(x) for x in pathSegs_strs]

        # 2 different cases are handled here for now: 0 holes, 1 hole

        if len(cont_paths) > 2:
            print("This case of nested polygons isn't handled yet")
            exit(1)

        symbol_geometry = []

        for cp in cont_paths:
            contour_points = get_points_continuous_path_part(cp)
            symbol_geometry.append(contour_points)

        symbol_geometries.append(symbol_geometry)

    return symbol_geometries
Example #9
0
def SVGPathToTrackPoints(fname, spacing=TRACK_SPACING):
    path = svgpathtools.parse_path(open(fname).read())

    # input: svg based on a 100px / m image
    def next_t(path, t, dist):
        p = path.point(t)
        L = path.length()
        # t += 1.0 / np.abs(path.derivative(t))
        itr = 0
        while itr < 20:
            itr += 1
            p1 = path.point(t)
            err = np.abs(p1 - p) - dist
            d1 = path.derivative(t)
            if np.abs(err) < 1e-5:
                return t, p1, d1 / np.abs(d1)
            derr = np.abs(d1) * L
            # do a step in Newton's method (clipped because some of the
            # gradients in the curve are really small)
            t -= np.clip(err / derr, -1e-2, 1e-2)
            t = np.clip(t, 0, 1)
        return t, p, d1 / np.abs(d1)

    d0 = path.derivative(0)
    pts = [[path.point(0), d0 / np.abs(d0)]]
    t = 0
    while t < 1:
        t, p, d = next_t(path, t, spacing)
        pts.append([p, d])

    return pts
def writeGcode(gcode_file, svg_path):
    gcodeString = ""

    doc = minidom.parse(svg_path)
    path_strings = [
        path.getAttribute('d') for path in doc.getElementsByTagName('path')
    ]
    doc.unlink()

    # print the line draw commands
    for path_string in path_strings:
        p = parse_path(path_string)
        for i in range(0, int(p.length()), 2):
            div = (i / p.length())
            point = p.point(div)
            x = point.real
            y = point.imag
            gcodeString += ("G00 X" + str(x) + " Y" + str(y)) + "\n"
        if (len(path_strings) > 1):
            gcodeString += "\nM00"

    gcodeString += "\nM30"

    f = open(gcode_file, "w")
    f.write(gcodeString)
    f.close()
Example #11
0
def displaySVGPaths(pathList,*colors): #creates and saves an svf file displaying the input paths
    show_closed_discont=True
    import svgwrite
    from svgpathtools import Path, Line, CubicBezier
#    from time import strftime
#    from re import sub
#    dwg = svgwrite.Drawing('temporary_displaySVGPaths%s.svg'%time_date, size= ("%spx"%(int(xmax-xmin+1)), "%spx"%(int(ymax-ymin+1))))
#    dwg = svgwrite.Drawing('temporary_displaySVGPaths%s.svg'%time_date)
    dwg = svgwrite.Drawing('temporary_displaySVGPaths.svg')
    dwg.add(dwg.rect(insert=(0, 0), size=('100%', '100%'), rx=None, ry=None, fill='white')) #add white background
    dc = 100/len(pathList)
    for i,p in enumerate(pathList):
        if isinstance(p,Path):
            startpt = p[0].start
            ps = path2str(p)
        elif isinstance(p,Line) or isinstance(p,CubicBezier):
            startpt = p.start
            ps = path2str(p)
        else:
            startpt = parse_path(p)[0].start
            ps = p
        if colors !=tuple():
            dwg.add(dwg.path(ps, stroke=colors[0][i], fill='none'))
        else:
            dwg.add(dwg.path(ps, stroke=svgwrite.rgb(0+dc, 0+dc, 16, '%'), fill='none'))
        if show_closed_discont and isApproxClosedPath(p):
            startpt = (startpt.real,startpt.imag)
            dwg.add(dwg.circle(startpt,1, stroke='gray', fill='gray'))
    dwg.save()
Example #12
0
    def snap(self, tree, threshold):
        def process(points):
            for i, p in enumerate(points):
                best, _, dist = tree.nearest_neighbor([p.real, p.imag])

                if dist < threshold:
                    points[i] = complex(best[0], best[1])
            return points

        path = parse_path(self['d'])
        newPath = Path()
        for seg in path:
            points = process([seg.start, seg.end])

            if isinstance(seg, Line):
                newSeg = Line(*points)
                newPath.append(newSeg)

            elif isinstance(seg, CubicBezier):
                newSeg = CubicBezier(points[0], seg.control1, seg.control2,
                                     points[1])
                newPath.append(newSeg)

        self['d'] = newPath.d()
        return self
Example #13
0
    def effect(self):
        namedView = self.document.getroot().find(
            inkex.addNS('namedview', 'sodipodi'))
        doc_units = namedView.get(inkex.addNS('document-units', 'inkscape'))
        #inkex.utils.debug("document unit is " + doc_units)
        self.options.threshold = self.svg.unittouu(
            str(self.options.threshold) + doc_units)
        unit_factor = 1.0 / self.svg.uutounit(1.0, self.options.unit)
        #inkex.utils.debug("unit_factor is " + str(unit_factor))

        if self.options.threshold == 0:
            return

        for path in self.document.xpath("//svg:path", namespaces=inkex.NSS):
            try:
                parsed_path = svgpathtools.parse_path(path.attrib["d"])

                #if not isclosedac(parsed_path):
                #    continue

                if self.options.measure == "area":
                    calc = parsed_path.area()
                    #inkex.utils.debug(calc) #print calculated area with document units
                    #inkex.utils.debug(str(self.options.threshold * (unit_factor * unit_factor))) #print threshold area with selected units
                    if calc < (self.options.threshold *
                               (unit_factor * unit_factor)):
                        path.getparent().remove(path)
                else:  #length
                    calc = parsed_path.length()
                    #inkex.utils.debug(calc) #print calculated area with document units
                    #inkex.utils.debug(str(self.options.threshold * (unit_factor * unit_factor))) #print threshold area with selected units
                    if calc < (self.options.threshold * unit_factor):
                        path.getparent().remove(path)
            except:
                pass
Example #14
0
def safe_parse_path(d):
    p = None
    try:
        p = parse_path(d)
    except:
        logger.debug('Parse d string {}... failed.'.format(d[:100]))
    return p
Example #15
0
    def __init__(self, **kwargs):
        super(byA_PDFGrid, self).__init__(self)
        self._onePDFSize = kwargs.get('PDFSize', None)
        if (self._onePDFSize is None):
            onePDFWidth = kwargs.get('PDFWidth', 21)
            onePDFHeight = kwargs.get('PDFHeight', 29.7)
            self._onePDFSize = [onePDFWidth, onePDFHeight]

        w = self._onePDFSize[0]
        h = self._onePDFSize[1]

        paths = list()
        rectpath = ("M0,0v{}v{}h{}h{}v{}v{}h{}z"
                    "".format(0.5 * h, 0.5 * h, 0.5 * w, 0.5 * w, -0.5 * h,
                              -0.5 * h, -0.5 * w))
        paths.append(rectpath)
        for id, x in enumerate([1, 1.1]):
            tickpath = ("M0,{}l{},{}M0,{}l{},{}M{},0l{},{}M{},{}0l{},{}"
                        "".format(x, x, -x, h - x, x, x, w - x, x, x, w - x, h,
                                  x, -x))
            paths.append(tickpath)
        parsedPath = parse_path(paths[0] + paths[1] + paths[2])
        self._onePDFSheetPath = byA_Path()
        for p in parsedPath:
            assert (isinstance(p, Line))
            p1 = byA_Point(c=p.start)
            p2 = byA_Point(c=p.end)
            self._onePDFSheetPath.append(byA_Line(P1=p1, P2=p2))
        self._allPDFSheetPaths = None
        self._freeze("byA_PDFGrid")
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 slide(which, dur, along):
    # Now we know that 'along' is being used as a motion path, rewrite the
    # path so that it starts at the origin.
    # XX: for debugging, might be useful to wrap it in a <g> with a transform
    # that reverses this, so it still appears in the same place in the
    # document?
    along_elem = GLOBAL_STATE.by_id[along]
    assert along_elem.tag == "{http://www.w3.org/2000/svg}path"
    path = svgpathtools.parse_path(along_elem.attrib["d"])
    new_path = path.translated(-path[0].start)
    assert new_path[0].start == 0 + 0j
    along_elem.attrib["d"] = new_path.d()
    # Then add the actual animateMotion tag
    return add_SMIL_tag(
        "animateMotion",
        E("mpath", href="#" + along),
        href="#" + which,
        dur=dur,
        # Easing
        calcMode="spline",
        keyPoints="0; 1",
        keyTimes="0; 1",
        keySplines="0.43 0.02 0.72 0.93",
        # Start from whereever it is right now, not the initial position
        additive="sum",
        # And after it arrives, it should stay there
        fill="freeze",
    )
Example #18
0
def get_verts_and_codes(svg_list):
    '''
    parse into x,y coordinates and output list of lists of coordinates

    '''
    lines = []
    Verts = []
    Codes = []
    for stroke_ind, stroke in enumerate(svg_list):
        x = []
        y = []
        parsed = parse_path(stroke)
        for i, p in enumerate(parsed):
            if i != len(parsed) - 1:  # last line segment
                x.append(p.start.real)
                y.append(p.start.imag)
            else:
                x.append(p.start.real)
                y.append(p.start.imag)
                x.append(p.end.real)
                y.append(p.end.imag)
        lines.append(zip(x, y))
        verts, codes = polyline_pathmaker(lines)
        Verts.append(verts)
        Codes.append(codes)
    return Verts, Codes
def writeGcode(gcode_file, svg_path, repetitions, fill):
    gcodeString = ""

    returnString = ""
    skip_paths = []

    if fill:
        try:
            skip_paths = toolPathSVG(svg_path, repetitions)
        except:
            returnString = "Couldn't fill svg. Try breaking it into multiple paths."
            print(returnString)
    # print(returnString)

    doc = minidom.parse(svg_path)
    path_strings = [
        path.getAttribute('d') for path in doc.getElementsByTagName('path')
    ]

    print(skip_paths)

    doc.unlink()

    # try :
    #     fillSvg(svg_path, path_strings[0])

    #     doc = minidom.parse(svg_path)
    #     path_strings = [path.getAttribute('d') for path
    #                     in doc.getElementsByTagName('path')]
    #     doc.unlink()
    # except :
    #     print("filling failed")

    vertices = []

    # print the line draw commands
    idx = 0
    for path_string in path_strings:
        p = parse_path(path_string)
        for i in range(0, int(p.length()), 2):
            div = (i / p.length())
            point = p.point(div)
            x = point.real
            y = point.imag
            v = (x, y)
            vertices.append(v)
            gcodeString += ("G00 X" + str(x) + " Y" + str(y)) + "\n"
        if len(path_strings) > 1:
            if (fill and idx in skip_paths) or not fill:
                gcodeString += "M00\n"
        idx += 1

    gcodeString += "\nM30"

    f = open(gcode_file, "w")
    f.write(gcodeString)
    f.close()

    return returnString
Example #20
0
def find_paths(doc,
               convert_circles_to_paths=True,
               convert_ellipses_to_paths=True,
               convert_lines_to_paths=True,
               convert_polylines_to_paths=True,
               convert_polygons_to_paths=True,
               convert_rectangles_to_paths=True):

    # Use minidom to extract path strings from input SVG
    pathnodes = doc.getElementsByTagName('path')
    paths = [dom2dict(el) for el in pathnodes]
    d_strings = [el['d'] for el in paths]

    # Use minidom to extract polyline strings from input SVG, convert to
    # path strings, add to list
    if convert_polylines_to_paths:
        plinnodes = doc.getElementsByTagName('polyline')
        plins = [dom2dict(el) for el in plinnodes]
        d_strings += [polyline2pathd(pl['points']) for pl in plins]
        pathnodes.extend(plinnodes)

    # Use minidom to extract polygon strings from input SVG, convert to
    # path strings, add to list
    if convert_polygons_to_paths:
        pgonnodes = doc.getElementsByTagName('polygon')
        pgons = [dom2dict(el) for el in pgonnodes]
        d_strings += [polygon2pathd(pg['points']) for pg in pgons]
        pathnodes.extend(pgonnodes)

    if convert_lines_to_paths:
        linenodes = doc.getElementsByTagName('line')
        lines = [dom2dict(el) for el in linenodes]
        d_strings += [
            ('M' + l['x1'] + ' ' + l['y1'] + 'L' + l['x2'] + ' ' + l['y2'])
            for l in lines
        ]
        pathnodes.extend(linenodes)

    if convert_ellipses_to_paths:
        ellipsenodes = doc.getElementsByTagName('ellipse')
        ellipses = [dom2dict(el) for el in ellipsenodes]
        d_strings += [ellipse2pathd(e) for e in ellipses]
        pathnodes.extend(ellipsenodes)

    if convert_circles_to_paths:
        circlenodes = doc.getElementsByTagName('circle')
        circles = [dom2dict(el) for el in circlenodes]
        d_strings += [ellipse2pathd(c) for c in circles]
        pathnodes.extend(circlenodes)

    if convert_rectangles_to_paths:
        rectanglenodes = doc.getElementsByTagName('rect')
        rectangles = [dom2dict(el) for el in rectanglenodes]
        d_strings += [rect2pathd(r) for r in rectangles]
        pathnodes.extend(rectanglenodes)

    path_list = [parse_path(d) for d in d_strings]
    return pathnodes, path_list
    def envelope2coords(self, path_envelope):
        pp_envelope = svgpathtools.parse_path(path_envelope)

        c0 = complex2tulpe(pp_envelope[0].start)
        c1 = complex2tulpe(pp_envelope[1].start)
        c2 = complex2tulpe(pp_envelope[2].start)
        c3 = complex2tulpe(pp_envelope[3].start)

        return [c0, c1, c2, c3]
def get_simple_paths(text):
    """

    :param text:
    :return:
    """
    sentence_lengths = get_sentence_lengths(text)
    path_str = plot_lengths(sentence_lengths)
    return parse_path(path_str)
Example #23
0
def transform_paths(stage_svg):
    """Inkscapes deep ungroup doesn't handle paths with inline matrix
    transforms well. Transform them here instead"""
    root = ET.fromstring(stage_svg.read_bytes(), parser=XML_PARSER)
    for path in root.findall(".//path[@transform]", root.nsmap):
        trans_matrix = parse_transform(path.attrib.pop("transform"))
        svg_path = parse_path(path.attrib["d"])
        path.attrib["d"] = transform(svg_path, trans_matrix).d()

    stage_svg.write_bytes(ET.tostring(root))
Example #24
0
    def __init__(self, p_id: str, p_attrs: Dict[str, str]):
        '''
        '''
        # the 'id' of a svg <path> definition
        self.p_id = p_id
        # and the attributes of the <path>
        self.p_attrs = p_attrs

        # the transformation of the svg_path_d to a svgpathtools 'path'
        self.svg_path = svgpathtools.parse_path(self.p_attrs['d'])
Example #25
0
def debug_overlay_path(page_offset, line_height, line):
    line_y = page_offset + (line * line_height)
    mid = page_offset + ((line - 0.5) * line_height)
    buffer = line_height / determinate_ratio

    top = page_offset if line == 1 else mid - buffer
    bottom = mid + (line_height / 2) if line == 15 else mid + buffer

    p = f"M 0,{top} L 345,{top} L 345,{bottom} L 0,{bottom} L 0,{top} M 0,{line_y} L 345,{line_y}"
    return svgpathtools.parse_path(p)
Example #26
0
 def parseCCW(self):
     orig_path = parse_path(self.string)
     #fix degenerate segments here
     for i,seg in enumerate(orig_path):
         if abs(seg.start-seg.end) < 1:
             del orig_path[i]
             orig_path[i].start = orig_path[i-1].end
     if isCCW(orig_path,self.center):
         return orig_path
     else:
         return reversePath(orig_path)
Example #27
0
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)
Example #28
0
def splitSingleLine(start_end, unitLength, toPoint=False):
    '''
    return strings
    :param start_end:
    :param unitLength:
    :return:
    '''
    if UB.pointEquals(start_end[0], start_end[1]):
        return []
    pathStr = getStraightPath(start_end)
    path = parse_path(pathStr)
    try:
        l = path.length()
        if l > unitLength:
            paths = []
            t = unitLength / l
            ts = 0
            te = t
            for i in range(int(math.ceil(l / unitLength))):
                seg = Line(path.point(ts), path.point(te))
                p = Path(seg)
                if toPoint:
                    paths.append([
                        UB.getPointFromComplex(path.point(ts)),
                        UB.getPointFromComplex(path.point(te))
                    ])
                else:
                    paths.append(p.d())

                ts += t
                te += t
                te = min(1, te)
            if toPoint:
                paths.append([
                    UB.getPointFromComplex(path.point(te)),
                    UB.getPointFromComplex(path.point(1))
                ])
            else:
                paths.append(
                    getStraightPath([
                        UB.getPointFromComplex(path.point(te)),
                        UB.getPointFromComplex(path.point(1))
                    ]))
            return paths
        if toPoint:
            return [start_end]
        else:
            return [pathStr]
    except Exception as e:
        print(start_end, "something wrong with the splitLine", e)
        return []
Example #29
0
def writeDxf(svgName, dxfName, kerfWidth=None, chordHeight=None, globalRadius=None):
	svgRoot = xml.etree.ElementTree.parse(svgName).getroot()
	svgHeight = toFloat(svgRoot.attrib['height'])
	kerfWidth = 0.0 if kerfWidth is None else kerfWidth
	blueRects = []

	dxfDoc = ezdxf.new('R12')
	dxfSpace = dxfDoc.modelspace()
	for elem in svgRoot.iter():
		attr   = elem.attrib
		style  = toStyleDict(attr.get('style', ''))
		radius = attr.get('stroke-width', '1.0')
		radius = toFloat(style.get('stroke-width', radius))
		stroke = attr.get('stroke', '')
		stroke = style.get('stroke', stroke).lower()

		isRed    = stroke in ('#f00', '#ff0000', 'red')
		isGreen  = stroke in ('#0f0', '#00ff00', 'lime')
		isBlue   = stroke in ('#00f', '#0000ff', 'blue')
		isRect   = elem.tag == '{http://www.w3.org/2000/svg}rect'
		isPath   = elem.tag == '{http://www.w3.org/2000/svg}path'
		isCircle = elem.tag == '{http://www.w3.org/2000/svg}circle'

		if isBlue and isRect:
			w = toFloat(attr['width'])
			h = toFloat(attr['height'])
			x = toFloat(attr['x'])
			y = svgHeight - toFloat(attr['y']) - h
			blueRects.append((x, y, w, h, radius))

		if isRed or isGreen:
			offset = kerfWidth / (-2.0 if isRed else 2.0)

			if isPath:
				d = svgpathtools.parse_path(attr.get('d'))
				shape = ezdxf.math.Shape2d()
				radii = []
				for segment in d:
					x = segment.start.real
					y = svgHeight - segment.start.imag
					shape.append((x, y))
					radii.append(getRadius((x, y), globalRadius, radius, blueRects))
				writeRoundedPolygon(dxfSpace, shape, radii, offset, chordHeight)

			if isCircle:
				x = toFloat(attr['cx'])
				y = svgHeight - toFloat(attr['cy'])
				r = toFloat(attr['r'])
				writeCircle(dxfSpace, (x,y), r+offset, chordHeight)
	dxfDoc.saveas(dxfName)
Example #30
0
def convert_path(path, x0, xfactor, y0, yfactor, steps):
    p = svgpath.parse_path(path)
    res = []
    xlast = -np.inf
    for i in range(steps):
        t = i / (steps - 1)
        pt = p.point(t)
        x = (pt.real - x0) * xfactor
        y = (pt.imag - y0) * yfactor
        # only add points that advance forward on x axis
        if x > xlast:
            res.append((x, y))
            xlast = x
    return np.asarray(res, dtype="float32")
Example #31
0
def process_svg(fname, step_size=0.2, preview=False):
    strokes = []
    title, _ = os.path.basename(fname).rsplit('.', 1)
    svg = etree.parse(fname).getroot()

    # convert to absolute points
    for ch in svg.findall('.//{}path'.format(ns)):
        data = ch.attrib['d']
        path = parse_path(data)
        for segment in path:
            points = [to_coord(segment.point(x)) for x in np.arange(0, 1 + step_size, step_size)]

            # merge strokes that are essentially one stroke
            if strokes and approx_eq(strokes[-1][-1], points[0]):
                strokes[-1].extend(points[1:])
            else:
                strokes.append(points)

    # draw it out
    if preview:
        im = Image.new('RGB', (2000, 2000))
        draw = ImageDraw.Draw(im)
        for stroke in strokes:
            for start, end in zip(stroke, stroke[1:]):
                if start == end:
                    continue
                draw.line([start, end], fill=(255, 255, 255))
        im.save('preview/{}.png'.format(title), 'PNG')

    # convert to offsets and end-of-stroke
    # where 1 -> the last point in the stroke
    data = []
    while strokes:
        stroke = strokes.pop(0)
        for start, end in zip(stroke, stroke[1:]):
            if start == end:
                continue
            sx, sy = start
            ex, ey = end
            delta_x, delta_y = ex - sx, ey - sy
            data.append((delta_x, delta_y, 0))
        if strokes:
            stroke_end = stroke[-1]
            stroke_start = strokes[0][0]
            sx, sy = stroke_end
            ex, ey = stroke_start
            delta_x, delta_y = ex - sx, ey - sy
            data.append((delta_x, delta_y, 1))
    return data
Example #32
0
    def showBoundingBoxes(self):
        style = {
            'stroke-width': 2,
            'stroke': '#0000ff',
            'fill': 'none',
            'opacity': .5
        }

        for key, part in self.inventory:
            path = parse_path(part['d'])
            x0, x1, y0, y1 = path.bbox()
            s = 'M{0},{1} L{0},{3} L{2},{3} L{2},{1} Z'.format(x0, y0, x1, y1)
            self._add(s, style)

        return self
 def replace_point(self, ptbefore, ptafter):
     pathstr = self._g.elements[0].tostring()
     pathstr = pathstr.split('"')[3]
     pathbefore = parse_path(pathstr)
     pathafter = Path()
     for i in pathbefore:
         if isinstance(i, Line):
             if (isclose(i.start.real, ptbefore._x)
                     and isclose(i.start.imag, ptbefore._y)):
                 i.start = ptafter._x + 1j * ptafter._y
             if (isclose(i.end.real, ptbefore._x)
                     and isclose(i.end.imag, ptbefore._y)):
                 i.end = ptafter._x + 1j * ptafter._y
         pathafter.append(i)
     self.add(svgwrite.path.Path(d=pathafter.d()))
Example #34
0
    def hardComplete(self, tol_closure=opt.tol_isApproxClosedPath):
        self.trimAndAddTransectsBeforeCompletion()
        meatOf_connecting_path = self.findMiddleOfConnectingPath()  ###this is a Path object
        self.addSegsToCP(meatOf_connecting_path)
        cp_start,cp_end = self.completed_path[0].start, self.completed_path[-1].end

        #check newly finished connecting_path is closed
        if abs(cp_start - cp_end) >= tol_closure:
            raise Exception("Connecting path should be finished but is not closed.")

        #test for weird .point() bug where .point(1) != end
        if (abs(cp_start - self.completed_path.point(0)) >= tol_closure or
            abs(cp_end - self.completed_path.point(1)) >= tol_closure):
            self.completed_path = parse_path(path2str(self.completed_path))
            raise Exception("weird .point() bug where .point(1) != end... I just added this check in on 3-5-15, so maybe if this happens it doesn't even matter.  Try removing this code-block... or change svgpathtools.Path.point() method to return one or the other.")
Example #35
0
def svg2pathlist(SVGfileLocation):
    doc = minidom.parse(SVGfileLocation)  # parseString also exists
    # Use minidom to extract path strings from input SVG
    path_strings = [p.getAttribute('d') 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')) 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' 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
    path_strings += ['M' + p.getAttribute('x1') + ' ' + p.getAttribute(
        'y1') + 'L' + p.getAttribute('x2') + ' ' + p.getAttribute('y2') for p
                     in doc.getElementsByTagName('line')]
    doc.unlink()
    return [parse_path(ps) for ps in path_strings]
Example #36
0
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
Example #37
0
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.")
Example #38
0
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)