Exemple #1
0
def readXML(ifile):
    """
    Read a list of Polygons from a XML file which was written with writeXML().
        
    :Arguments:
        - ofile: see above
    :Returns:
        list of Polygon objects
    """
    f, cl = getReadableObject(ifile)
    d = parseString(f.read())
    if cl: f.close()
    plist = []
    for pn in d.getElementsByTagName('polygon'):
        p = Polygon()
        plist.append(p)
        for sn in pn.childNodes:
            if not sn.nodeType == Node.ELEMENT_NODE:
                continue
            assert sn.tagName == 'contour'
            polist = []
            for pon in sn.childNodes:
                if not pon.nodeType == Node.ELEMENT_NODE:
                    continue
                polist.append((float(pon.getAttribute('x')),
                               float(pon.getAttribute('y'))))
            assert int(sn.getAttribute('points')) == len(polist)
            p.addContour(polist, int(sn.getAttribute('isHole')))
        assert int(pn.getAttribute('contours')) == len(p)
    return plist
Exemple #2
0
def readXML(ifile):
    """
    Read a list of Polygons from a XML file which was written with writeXML().
        
    :Arguments:
        - ofile: see above
    :Returns:
        list of Polygon objects
    """
    f, cl = getReadableObject(ifile)
    d = parseString(f.read())
    if cl: f.close()
    plist = []
    for pn in d.getElementsByTagName('polygon'):
        p = Polygon()
        plist.append(p)
        for sn in pn.childNodes:
            if not sn.nodeType == Node.ELEMENT_NODE:
                continue
            assert sn.tagName == 'contour'
            polist = []
            for pon in sn.childNodes:
                if not pon.nodeType == Node.ELEMENT_NODE:
                    continue
                polist.append((float(pon.getAttribute('x')), float(pon.getAttribute('y'))))
            assert int(sn.getAttribute('points')) == len(polist)
            p.addContour(polist, int(sn.getAttribute('isHole')))
        assert int(pn.getAttribute('contours')) == len(p)
    return plist
Exemple #3
0
def prunePoints(poly):
    """
    Returns a new Polygon which has exactly the same shape as p, but unneeded 
    points are removed. The new Polygon has no double points or points that are 
    exactly on a straight line.

    :Arguments:
        - p: Polygon
    :Returns:
        new Polygon
    """
    np = Polygon()
    for x in range(len(poly)): # loop over contours
        c = list(poly[x])
        c.insert(0, c[-1])
        c.append(c[1])
        # remove double points
        i = 1
        while (i < (len(c))):
            if c[i] == c[i-1]:
                del c[i]
            else:
                i += 1
        # remove points that are on a straight line
        n = []
        for i in range(1, len(c)-1):
            if __linVal(c[i-1:i+2]) != 0.0:
                n.append(c[i])
        if len(n) > 2:
            np.addContour(n, poly.isHole(x))
    return np
Exemple #4
0
def prunePoints(poly):
    """
    Returns a new Polygon which has exactly the same shape as p, but unneeded 
    points are removed. The new Polygon has no double points or points that are 
    exactly on a straight line.

    :Arguments:
        - p: Polygon
    :Returns:
        new Polygon
    """
    np = Polygon()
    for x in range(len(poly)): # loop over contours
        c = list(poly[x])
        c.insert(0, c[-1])
        c.append(c[1])
        # remove double points
        i = 1
        while (i < (len(c))):
            if c[i] == c[i-1]:
                del c[i]
            else:
                i += 1
        # remove points that are on a straight line
        n = []
        for i in range(1, len(c)-1):
            if __linVal(c[i-1:i+2]) != 0.0:
                n.append(c[i])
        if len(n) > 2:
            np.addContour(n, poly.isHole(x))
    return np
Exemple #5
0
def fillHoles(poly):
    """
    Returns the polygon p without any holes.

    :Arguments:
        - p: Polygon
    :Returns:
        new Polygon
    """
    n = Polygon()
    [n.addContour(poly[i]) for i in range(len(poly)) if poly.isSolid(i)]
    return n
Exemple #6
0
def fillHoles(poly):
    """
    Returns the polygon p without any holes.

    :Arguments:
        - p: Polygon
    :Returns:
        new Polygon
    """
    n = Polygon()
    [n.addContour(poly[i]) for i in range(len(poly)) if poly.isSolid(i)]
    return n
Exemple #7
0
def tile(poly, x=[], y=[], bb=None):
    """
    Returns a list of polygons which are tiles of p splitted at the border values 
    specified in x and y. If you already know the bounding box of p, you may give 
    it as argument bb (4-tuple) to speed up the calculation.

    :Arguments:
        - p: Polygon
        - x: list of floats
        - y: list of floats
        - optional bb: tuple of 4 floats
    :Returns:
        list of new Polygons
    """
    if not (x or y):
        return [poly] # nothin' to do
    bb = bb or poly.boundingBox()
    x = [bb[0]] + [i for i in x if bb[0] < i < bb[1]] + [bb[1]]
    y = [bb[2]] + [j for j in y if bb[2] < j < bb[3]] + [bb[3]]
    x.sort()
    y.sort()
    cutpoly = []
    for i in range(len(x)-1):
        for j in range(len(y)-1):
            cutpoly.append(Polygon(((x[i],y[j]),(x[i],y[j+1]),(x[i+1],y[j+1]),(x[i+1],y[j]))))
    tmp = [c & poly for c in cutpoly]
    return [p for p in tmp if p]
Exemple #8
0
def cloneGrid(poly, con, xl, yl, xstep, ystep):
    """
    Create a single new polygon with contours that are made from contour con from 
    polygon poly arranged in a xl-yl-grid with spacing xstep and ystep.

    :Arguments:
        - poly: Polygon
        - con: integer
        - xl: integer
        - yl: integer
        - xstep: float
        - ystep: float
    :Returns:
        new Polygon
    """
    p = Polygon(poly[con])
    for xi in range(xl):
        for yi in range(yl):
            p.cloneContour(0, xi*xstep, yi*ystep)
    return p
Exemple #9
0
def cloneGrid(poly, con, xl, yl, xstep, ystep):
    """
    Create a single new polygon with contours that are made from contour con from 
    polygon poly arranged in a xl-yl-grid with spacing xstep and ystep.

    :Arguments:
        - poly: Polygon
        - con: integer
        - xl: integer
        - yl: integer
        - xstep: float
        - ystep: float
    :Returns:
        new Polygon
    """
    p = Polygon(poly[con])
    for xi in range(xl):
        for yi in range(yl):
            p.cloneContour(0, xi*xstep, yi*ystep)
    return p
Exemple #10
0
def Rectangle(xl=1.0, yl=None):
    """
    Create a rectangular shape. If yl is not set, a square is created.

    :Arguments:
        - optional xl: float
        - optional yl: float
    :Returns:
        new Polygon
    """
    if yl is None: yl = xl
    return Polygon(((0.0, 0.0), (xl, 0.0), (xl, yl), (0.0, yl)))
Exemple #11
0
def decodeBinary(bin):
    """
    Create Polygon from a binary string created with encodeBinary(). If the string 
    is not valid, the whole thing may break!

    :Arguments:
        - s: string
    :Returns:
        new Polygon
    """
    nC, b = __unpack('!I', bin)
    p = Polygon()
    for i in range(nC[0]):
        x, b = __unpack('!l', b)
        if x[0] < 0:
            isHole = 1
            s = -2 * x[0]
        else:
            isHole = 0
            s = 2 * x[0]
        flat, b = __unpack('!%dd' % s, b)
        p.addContour(tuple(__couples(flat)), isHole)
    return p
Exemple #12
0
def decodeBinary(bin):
    """
    Create Polygon from a binary string created with encodeBinary(). If the string 
    is not valid, the whole thing may break!

    :Arguments:
        - s: string
    :Returns:
        new Polygon
    """
    nC, b = __unpack('!I', bin)
    p = Polygon()
    for i in range(nC[0]):
        x, b = __unpack('!l', b)
        if x[0] < 0:
            isHole = 1
            s = -2*x[0]
        else:
            isHole = 0
            s = 2*x[0]
        flat, b = __unpack('!%dd' % s, b)
        p.addContour(tuple(__couples(flat)), isHole)
    return p
Exemple #13
0
def Circle(radius=1.0, center=(0.0, 0.0), points=32):
    """
    Create a polygonal approximation of a circle.

    :Arguments:
        - optional radius: float
        - optional center: point (2-tuple of float)
        - optional points: integer
    :Returns:
        new Polygon
    """
    p = []
    for i in range(points):
        a = 2.0 * pi * float(i) / points
        p.append((center[0] + radius * sin(a), center[1] + radius * cos(a)))
    return Polygon(p)
Exemple #14
0
def Star(radius=1.0, center=(0.0, 0.0), beams=16, iradius=0.5):
    """
    Create a star shape, iradius is the inner and radius the outer radius.

    :Arguments:
        - optional radius: float
        - optional center: point (2-tuple of float)
        - optional beams: integer
        - optional iradius: float
    :Returns:
        new Polygon
    """
    p = []
    for i in range(beams):
        a = 2.0 * pi * float(i) / beams
        p.append((center[0] + radius * sin(a), center[1] + radius * cos(a)))
        b = 2.0 * pi * (float(i) + 0.5) / beams
        p.append((center[0] + iradius * sin(b), center[1] + iradius * cos(b)))
    return Polygon(p)
Exemple #15
0
def convexHull(poly):
    """
    Returns a polygon which is the convex hull of p.

    :Arguments:
        - p: Polygon
    :Returns:
        new Polygon
    """
    points = list(pointList(poly, 0))
    points.sort()
    u = [points[0], points[1]]
    for p in points[2:]:
        u.append(p)
        while len(u) > 2 and __left(u[-3:]):
            del u[-2]
    points.reverse()
    l = [points[0], points[1]]
    for p in points[2:]:
        l.append(p)
        while len(l) > 2 and __left(l[-3:]):
            del l[-2]
    return Polygon(u+l[1:-1])
Exemple #16
0
def tileBSP(p):
    """
    This generator function returns tiles of a polygon. It will be much 
    more efficient for larger polygons and a large number of tiles than the 
    original tile() function. For a discussion see:
        http://dr-josiah.blogspot.com/2010/08/binary-space-partitions-and-you.html

    :Arguments:
        - p: Polygon
    :Returns:
        tiles of the Polygon p on the integer grid
    """
    _int = int
    _floor = floor

    work = [p]
    while work:
        # we'll use an explicit stack to ensure that degenerate polygons don't
        # blow the system recursion limit
        polygon = work.pop()

        # find out how many points are in each row/column of the grid
        xs = defaultdict(_int)
        ys = defaultdict(_int)
        for poly in polygon:
            for x,y in poly:
                xs[_int(_floor(x))] += 1
                ys[_int(_floor(y))] += 1

        # handle empty polygons gracefully
        if not xs:
            continue

        # handle top and right-edge border points
        mvx = max(max(x for x,y in poly) for poly in polygon)
        vx = _int(_floor(mvx))
        if len(xs) > 1 and mvx == vx:
            xs[vx-1] += xs.pop(vx, 0)
        mvy = max(max(y for x,y in poly) for poly in polygon)
        vy = _int(_floor(mvy))
        if len(ys) > 1 and mvy == vy:
            ys[vy-1] += ys.pop(vy, 0)

        # we've got a single grid, yield it
        if len(xs) == len(ys) == 1:
            yield polygon
            continue

        # find the split
        if len(xs) < 2:
            spx, countx = xs.items()[0]
            countx *= 3
        else:
            spx, countx = _find_split(xs)
        if len(ys) < 2:
            spy, county = ys.items()[0]
            county *= 3
        else:
            spy, county = _find_split(ys)

        # get the grid bounds for the split
        minx = min(xs)
        maxx = max(xs)
        miny = min(ys)
        maxy = max(ys)

        # actually split the polygon and put the results back on the work
        # stack
        if (countx < county and not _single_poly(polygon, 0, minx + 1.0)) or _single_poly(polygon, 1, miny + 1.0):
            work.append(polygon &
                Polygon([(minx, miny), (minx, maxy+1),
                         (spx, maxy+1), (spx, miny)]))
            work.append(polygon &
                Polygon([(spx, miny), (spx, maxy+1),
                         (maxx+1, maxy+1), (maxx+1, miny)]))
        else:
            work.append(polygon &
                Polygon([(minx, miny), (minx, spy),
                         (maxx+1, spy), (maxx+1, miny)]))
            work.append(polygon &
                Polygon([(minx, spy), (minx, maxy+1),
                         (maxx+1, maxy+1), (maxx+1, spy)]))

        # Always recurse on the smallest set, which is a trick to ensure that
        # the stack size is O(log n) .
        if work[-2].nPoints() < work[-1].nPoints():
            work.append(work.pop(-2))
Exemple #17
0
    def writePDF(ofile, polylist, pagesize=None, linewidth=0, fill_color=None):
        """
    *This function is only available if the reportlab package is installed!*
    Write a the Polygons in polylist to a PDF file.

    :Arguments:
        - ofile: see above
        - polylist: sequence of Polygons
        - optional pagesize: 2-tuple of floats
        - optional linewidth: float
        - optional fill_color: color
    :Returns:
        ofile object
    """
        from reportlab.pdfgen import canvas
        from reportlab.lib.colors import red, green, blue, yellow, black, white
        if not pagesize:
            from reportlab.lib.pagesizes import A4
            pagesize = A4
        can = canvas.Canvas(ofile, pagesize=pagesize)
        can.setLineWidth(linewidth)
        pp = [Polygon(p) for p in polylist]  # use clones only
        bbs = [p.boundingBox() for p in pp]
        bbs2 = zip(*bbs)
        minx = min(bbs2[0])
        maxx = max(bbs2[1])
        miny = min(bbs2[2])
        maxy = max(bbs2[3])
        xdim = maxx - minx
        ydim = maxy - miny
        if not (xdim or ydim):
            raise Error("Polygons have no extent in one direction!")
        a = ydim / xdim
        width, height = pagesize
        if a > (height / width):
            width = height / a
        else:
            height = width * a
        npoly = len(pp)
        fill_color = __RingBuffer(fill_color or (red, green, blue, yellow))
        for i in range(npoly):
            p = pp[i]
            bb = bbs[i]
            p.warpToBox(width * (bb[0] - minx) / xdim,
                        width * (bb[1] - minx) / xdim,
                        height * (bb[2] - miny) / ydim,
                        height * (bb[3] - miny) / ydim)
        for poly in pp:
            solids = [poly[i] for i in range(len(poly)) if poly.isSolid(i)]
            can.setFillColor(fill_color())
            for c in solids:
                p = can.beginPath()
                p.moveTo(c[0][0], c[0][1])
                for i in range(1, len(c)):
                    p.lineTo(c[i][0], c[i][1])
                p.close()
                can.drawPath(p, stroke=1, fill=1)
            holes = [poly[i] for i in range(len(poly)) if poly.isHole(i)]
            can.setFillColor(white)
            for c in holes:
                p = can.beginPath()
                p.moveTo(c[0][0], c[0][1])
                for i in range(1, len(c)):
                    p.lineTo(c[i][0], c[i][1])
                p.close()
                can.drawPath(p, stroke=1, fill=1)
        can.showPage()
        can.save()
Exemple #18
0
def writeSVG(ofile,
             polylist,
             width=None,
             height=None,
             fill_color=None,
             fill_opacity=None,
             stroke_color=None,
             stroke_width=None):
    """
    Write a SVG representation of the Polygons in polylist, width and/or height 
    will be adapted if not given. fill_color, fill_opacity, stroke_color and 
    stroke_width can be sequences of the corresponding SVG style attributes to use.

    :Arguments:
        - ofile: see above
        - polylist: sequence of Polygons
        - optional width: float
        - optional height: height
        - optional fill_color: sequence of colors (3-tuples of floats: RGB)
        - optional fill_opacity: sequence of colors
        - optional stroke_color: sequence of colors
        - optional stroke_width: sequence of floats
    :Returns:
        ofile object
    """
    f, cl = getWritableObject(ofile)
    pp = [Polygon(p) for p in polylist]  # use clones only
    [p.flop(0.0) for p in pp]  # adopt to the SVG coordinate system
    bbs = [p.boundingBox() for p in pp]
    bbs2 = zip(*bbs)
    minx = min(bbs2[0])
    maxx = max(bbs2[1])
    miny = min(bbs2[2])
    maxy = max(bbs2[3])
    xdim = maxx - minx
    ydim = maxy - miny
    if not (xdim or ydim):
        raise Error("Polygons have no extent in one direction!")
    a = ydim / xdim
    if not width and not height:
        if a < 1.0:
            width = 300
        else:
            height = 300
    if width and not height:
        height = width * a
    if height and not width:
        width = height / a
    npoly = len(pp)
    fill_color = __RingBuffer(fill_color or ((255, 0, 0), (0, 255, 0),
                                             (0, 0, 255), (255, 255, 0)))
    fill_opacity = __RingBuffer(fill_opacity or (1.0, ))
    stroke_color = __RingBuffer(stroke_color or ((0, 0, 0), ))
    stroke_width = __RingBuffer(stroke_width or (1.0, ))
    s = [
        '<?xml version="1.0" encoding="iso-8859-1" standalone="no"?>',
        '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">',
        '<svg xmlns="http://www.w3.org/2000/svg" width="%d" height="%d">' %
        (width, height)
    ]
    for i in range(npoly):
        p = pp[i]
        bb = bbs[i]
        p.warpToBox(width * (bb[0] - minx) / xdim,
                    width * (bb[1] - minx) / xdim,
                    height * (bb[2] - miny) / ydim,
                    height * (bb[3] - miny) / ydim)
        subl = [
            '<path style="fill:rgb%s;fill-opacity:%s;fill-rule:evenodd;stroke:rgb%s;stroke-width:%s;" d="'
            % (fill_color(), fill_opacity(), stroke_color(), stroke_width())
        ]
        for c in p:
            subl.append('M %g, %g %s z ' %
                        (c[0][0], c[0][1], ' '.join([("L %g, %g" % (a, b))
                                                     for a, b in c[1:]])))
        subl.append('"/>')
        s.append(''.join(subl))
    s.append('</svg>')
    f.write('\n'.join(s))
    if cl: f.close()
    return f