Source code for pymunk.autogeometry

__docformat__ = "reStructuredText"

from ._chipmunk_cffi import lib, ffi
from .vec2d import Vec2d
from .bb import BB


def _to_chipmunk(polyline):
    l = len(polyline)
    _line = ffi.new("cpPolyline *", {"verts": l})
    _line.count = l
    _line.capacity = l
    _line.verts = polyline
    return _line

def _from_polyline_set(_set):
    lines = []
    for i in range(_set.count):
        line = []
        for j in range(_set.lines[i].count):
            line.append(Vec2d(_set.lines[i].verts[j]))
        lines.append(line)
    return lines
    
[docs]def is_closed(polyline): """Returns true if the first vertex is equal to the last. :Parameters: polyline : [(x,y)] or [`Vec2d`] Polyline to simplify. """ return bool(lib.cpPolylineIsClosed(_to_chipmunk(polyline)))
[docs]def simplify_curves(polyline, tolerance): """Returns a copy of a polyline simplified by using the Douglas-Peucker algorithm. This works very well on smooth or gently curved shapes, but not well on straight edged or angular shapes. :Parameters: polyline : [(x,y)] or [`Vec2d`] Polyline to simplify. tolerance : float A higher value means more error is tolerated. """ _line = lib.cpPolylineSimplifyCurves(_to_chipmunk(polyline), tolerance) simplified = [] for i in range(_line.count): simplified.append(Vec2d(_line.verts[i])) return simplified
[docs]def simplify_vertexes(polyline, tolerance): """Returns a copy of a polyline simplified by discarding "flat" vertexes. This works well on straigt edged or angular shapes, not as well on smooth shapes. :Parameters: polyline : [(x,y)] or [`Vec2d`] Polyline to simplify. tolerance : float A higher value means more error is tolerated. """ _line = lib.cpPolylineSimplifyVertexes(_to_chipmunk(polyline), tolerance) simplified = [] for i in range(_line.count): simplified.append(Vec2d(_line.verts[i])) return simplified
[docs]def to_convex_hull(polyline, tolerance): """Get the convex hull of a polyline as a looped polyline. :Parameters: polyline : [(x,y)] or [`Vec2d`] The polyline to generate the hull for. tolerance : float A higher value means more error is tolerated. """ _line = lib.cpPolylineToConvexHull(_to_chipmunk(polyline), tolerance) hull = [] for i in range(_line.count): hull.append(Vec2d(_line.verts[i])) return hull
[docs]def convex_decomposition(polyline, tolerance): """Get an approximate convex decomposition from a polyline. Returns a list of convex hulls that match the original shape to within tolerance. NOTE: If the input is a self intersecting polygon, the output might end up overly simplified. :Parameters: polyline : [(x,y)] or [`Vec2d`] The polyline to get the convex hulls for. tolerance : float A higher value means more error is tolerated. """ _line = _to_chipmunk(polyline) _set = lib.cpPolylineConvexDecomposition(_line, tolerance) return _from_polyline_set(_set)
import collections
[docs]class PolylineSet(collections.Sequence): """A set of Polylines. Mainly intended to be used for its `collect_segment()` function when generating geometry with the `march_soft()` and `march_hard()` functions. """
[docs] def __init__(self): def free(_set): lib.cpPolylineSetFree(_set, True) self._set = ffi.gc(lib.cpPolylineSetNew(), free)
[docs] def collect_segment(self, v0, v1): """Add a line segment to a polyline set. A segment will either start a new polyline, join two others, or add to or loop an existing polyline. This is mostly intended to be used as a callback directly from `march_soft()` or `march_hard()`. """ lib.cpPolylineSetCollectSegment(v0, v1, self._set)
def __len__(self): return self._set.count def __getitem__(self, key): if key >= self._set.count: raise IndexError line = [] for i in range(self._set.lines[key].count): line.append(Vec2d(self._set.lines[key].verts[i])) return line
[docs]def march_soft(bb, x_samples, y_samples, threshold, segment_func, sample_func): """Trace an anti-aliased contour of an image along a particular threshold. The given number of samples will be taken and spread across the bounding box area using the sampling function and context. :Parameters: bb : `BB` bounding box of the area to sample within. x_samples : int Number of samples in x y_samples : int Number of samples in y threshold : float A higher value means more error is tolerated segment_func : ``func(v0, v1)`` The segment function will be called for each segment detected that lies along the density contour for threshold. v0 and v1 are `Vec2d`. sample_func : ``func(point) -> float`` The sample function will be called for x_samples * y_samples spread across the bounding box area, and should return a float. point is a `Vec2d`. """ @ffi.callback("typedef void (*cpMarchSegmentFunc)" "(cpVect v0, cpVect v1, void *data)") def _seg_f(v0, v1, _data): segment_func(Vec2d(v0), Vec2d(v1)) @ffi.callback("typedef cpFloat (*cpMarchSampleFunc)" "(cpVect point, void *data)") def _sam_f(point, _data): return sample_func(Vec2d(point)) lib.cpMarchSoft(bb._bb[0], x_samples, y_samples, threshold, _seg_f, ffi.NULL, _sam_f, ffi.NULL)
[docs]def march_hard(bb, x_samples, y_samples, threshold, segment_func, sample_func): """Trace an aliased curve of an image along a particular threshold. The given number of samples will be taken and spread across the bounding box area using the sampling function and context. :Parameters: bb : `BB` bounding box of the area to sample within. x_samples : int Number of samples in x y_samples : int Number of samples in y threshold : float A higher value means more error is tolerated segment_func : ``func(v0, v1)`` The segment function will be called for each segment detected that lies along the density contour for threshold. v0 and v1 are `Vec2d`. sample_func : ``func(point) -> float`` The sample function will be called for x_samples * y_samples spread across the bounding box area, and should return a float. point is a `Vec2d`. """ @ffi.callback("typedef void (*cpMarchSegmentFunc)" "(cpVect v0, cpVect v1, void *data)") def _seg_f(v0, v1, _data): segment_func(Vec2d(v0), Vec2d(v1)) @ffi.callback("typedef cpFloat (*cpMarchSampleFunc)" "(cpVect point, void *data)") def _sam_f(point, _data): return sample_func(Vec2d(point)) lib.cpMarchHard(bb._bb[0], x_samples, y_samples, threshold, _seg_f, ffi.NULL, _sam_f, ffi.NULL)