# Commits

committed 8bae6d2

polybezier - find_position works

# geoalg/polybezier.py

` `
` from .nurbs import Bezier4P`
` from .vector2d import Vec2, NULLVEC`
`-from .util import almost_equal_vectors, memodict`
`+from .util import almost_equal_vectors`
`+from ray import Ray2D`
` import math`
` `
` class PolyBezier(object):`
` `
`         :param int segments: segment count for each bezier part.`
`         """`
`-        points = []`
`+        points = [self.breakpoints[0]]`
`         for bezier in self.iter_bezier_parts():`
`-            points.extend(bezier.approximate(segments))`
`+            pts = bezier.approximate(segments)`
`+            next(pts) # skip first point, because equals last point of prev part`
`+            points.extend(pts)`
`         return points`
` `
`-    def find_position(self, p, epsilon=0.0001):`
`+    def find_position(self, p, segments=100):`
`         """ Find curve position near point p.`
` `
`         :param 2-tuple p: curve position`
`         """`
`-        # this algorithm works only with 'flat' curves, no loops or`
`-        # nearly self touching curves.`
`+        def interpolate(start, end):`
`+            if start > end:`
`+                start, end = end, start`
`+            ps = Vec2(self.get_point(start))`
`+            pe = Vec2(self.get_point(end))`
`+            ray1 = Ray2D(ps, pe)`
`+            ip =  ray1.intersect(ray1.normal_through(p))`
`+            a = ps.distance(ip)`
`+            b = pe.distance(ip)`
`+            return start + (end - start) * (a / (a + b))`
`+`
`+        def curve_distances():`
`+            delta = self.length / segments`
`+            for i in range(segments+1):`
`+                position = i * delta`
`+                curve_point = Vec2(self.get_point(position))`
`+                yield p.distance(curve_point), position`
` `
`         p = Vec2(p)`
`-`
`-        # caching of get_point() is save, because no changing of breakpoints`
`-        # or tangents within a search.`
`-        curve_point = memodict(self.get_point)`
`-`
`-        def is_close_enough(p2):`
`-            return p.distance(p2) <= epsilon`
`-`
`-        def search(s, e):`
`-            m = (s + e) / 2.0`
`-            pm = curve_point(m)`
`-            if is_close_enough(pm):`
`-                return m`
`-`
`-            if abs(e - s) < epsilon:`
`-                # requested point is beside the curve`
`-                return m`
`-`
`-            # recursive binary search`
`-            if p.distance(curve_point(s)) < p.distance(curve_point(e)):`
`-                return search(s, m)`
`-            else:`
`-                return search(m, e)`
`-`
`-        if is_close_enough(curve_point(0.)):`
`-            return 0.`
`-        elif is_close_enough(curve_point(self.length)):`
`-            return self.length`
`-        else:`
`-            return search(0., self.length)`
`+        start, end = sorted(curve_distances())[:2]`
`+        return interpolate(start[1], end[1])`
` `
`     def find_part(self, position):`
`         """ Find bezier curve part associated to curve position.`
`         bezier, t = self.find_part(position)`
`         # 0 <= t <= 1.`
`         return bezier.get_tangent(t)`
`-            `
`+`
` class Tangent(object):`
`     def __init__(self, left, right, align=True):`
`         self._left = Vec2(left)`

# polybezier_examples.py

`             dwg.add(dxf.line(p, p+tangent.right, color=4))`
` `
`     def add_curvepoint(p1):`
`-        cpos = bezier.find_position(p1)`
`+        cpos = bezier.find_position(p1, 300)`
`         p2 = bezier.get_point(cpos)`
`         for p in (p1, p2):`
`             dwg.add(dxf.circle(radius=.2, center=p, color=5))`
`     add_defpoints()`
`     add_tangents()`
`     add_curvepoint((22, 16))`
`+    add_curvepoint((7, 13))`
`+    add_curvepoint((35, 12))`
`     dwg.save()`
` `
`+def print_bezier_data(filename, bezier):`
`+    import csv`
`+    points = bezier.approximate(10)`
`+    rows = []`
`+    point_gen = iter(points)`
`+    prev_point = Vec2(next(point_gen))`
`+    dist_abs = 0.`
`+    num = 0`
`+    for p in point_gen:`
`+        p = Vec2(p)`
`+        distance = prev_point.distance(p)`
`+        dist_abs += distance`
`+        num += 1`
`+        rows.append((num, p.x, p.y, distance, dist_abs))`
`+        prev_point = p`
`+`
`+    with open(filename, 'wt') as fp:`
`+        writer = csv.writer(fp, dialect='excel')`
`+        writer.writerow(('num', 'x', 'y', 'dist-rel', 'dist-abs'))`
`+        for row in rows:`
`+            writer.writerow(row)`
` def main():`
`-    draw("polybezier.svg", PolyBezier(DEFPOINTS, START_ANGLE, END_ANGLE))`
`-    draw("polybezier_reversed.svg", PolyBezier(reversed(DEFPOINTS), END_ANGLE, START_ANGLE))`
`-    draw("bezier_circle.svg", bezier_circle())`
`+    #draw("polybezier.svg", PolyBezier(DEFPOINTS, START_ANGLE, END_ANGLE))`
`+    #draw("polybezier_reversed.svg", PolyBezier(reversed(DEFPOINTS), END_ANGLE, START_ANGLE))`
`+    #draw("bezier_circle.svg", bezier_circle())`
`     dxf_curve('polybezier.dxf', PolyBezier(DEFPOINTS, START_ANGLE, END_ANGLE))`
`+    #print_bezier_data('polybezier.csv', PolyBezier(DEFPOINTS, START_ANGLE, END_ANGLE))`
` `
` if __name__ == '__main__':`
`     main()`

# polybezier_sdl.py

`             pygame.draw.circle(surface, CURVE_COLOR, point2int(p), 2)`
` `
`     def marker_points(self):`
`-        for dist in range(int(self.bezier.length/30)):`
`-            yield self.bezier.get_point(dist*30)`
`+        t = self.bezier.length/40.`
`+        for i in range(41):`
`+            yield self.bezier.get_point(t*i)`
` `
`     def draw_points(self, surface, points):`
`         for p in  points:`