[Bf-extensions-cvs] SVN commit: /data/svn/bf-extensions [2094] trunk/py/scripts/addons/modules/ curve_utils.py: wip commit, basic bezier evaluation working, but still need to rewrite some parts of this script.

Campbell Barton ideasman42 at gmail.com
Tue Jul 5 19:54:11 CEST 2011


Revision: 2094
          http://projects.blender.org/scm/viewvc.php?view=rev&root=bf-extensions&revision=2094
Author:   campbellbarton
Date:     2011-07-05 17:54:10 +0000 (Tue, 05 Jul 2011)
Log Message:
-----------
wip commit, basic bezier evaluation working, but still need to rewrite some parts of this script.

Modified Paths:
--------------
    trunk/py/scripts/addons/modules/curve_utils.py

Modified: trunk/py/scripts/addons/modules/curve_utils.py
===================================================================
--- trunk/py/scripts/addons/modules/curve_utils.py	2011-07-05 07:54:55 UTC (rev 2093)
+++ trunk/py/scripts/addons/modules/curve_utils.py	2011-07-05 17:54:10 UTC (rev 2094)
@@ -17,3 +17,830 @@
 # ##### END GPL LICENSE BLOCK #####
 
 # <pep8 compliant>
+
+import bpy
+
+def line_point_side_v2(l1, l2, pt):
+    return (((l1[0] - pt[0]) * (l2[1] - pt[1])) -
+            ((l2[0] - pt[0]) * (l1[1] - pt[1])))
+
+
+def shell_angle_to_dist(angle):
+    from math import cos
+    return 1.0 if (angle < 0.0001) else abs(1.0 / cos(angle))
+
+
+def vis_curve_object():
+    scene = bpy.data.scenes[0] # weak!
+    cu = bpy.data.curves.new(name="Line", type='CURVE')
+    ob = bpy.data.objects.new(name="Test", object_data=cu)
+    ob.layers = [True] * 20
+    base = scene.objects.link(ob)
+    return ob
+
+
+def vis_curve_spline(p1, h1, p2, h2):
+    ob = vis_curve_object()
+    spline = ob.data.splines.new(type='BEZIER')
+    spline.bezier_points.add(1)
+    spline.bezier_points[0].co = p1.to_3d()
+    spline.bezier_points[1].co = p2.to_3d()
+
+    spline.bezier_points[0].handle_right = h1.to_3d()
+    spline.bezier_points[1].handle_left = h2.to_3d()
+
+
+def vis_circle_object(co, rad=1.0):
+    import math
+    scene = bpy.data.scenes[0] # weak!
+    ob = bpy.data.objects.new(name="Circle", object_data=None)
+    ob.rotation_euler.x = math.pi / 2
+    ob.location = co.to_3d()
+    ob.empty_draw_size = rad
+    ob.layers = [True] * 20
+    base = scene.objects.link(ob)
+    return ob
+
+
+def visualize_line(p1, p2, p3=None, rad=None):
+    pair = p1.to_3d(), p2.to_3d()
+
+    ob = vis_curve_object()
+    spline = ob.data.splines.new(type='POLY')
+    spline.points.add(1)
+    for co, v in zip((pair), spline.points):
+        v.co.xyz = co
+    
+    if p3:
+        spline = ob.data.splines.new(type='POLY')
+        spline.points[0].co.xyz = p3.to_3d()
+        print(rad)
+        if rad is not None:
+            vis_circle_object(p3, rad)
+
+
+def treat_points(points,
+                 double_limit=0.0001,
+                 ):
+
+    # first remove doubles
+    tot_len = 0.0
+    if double_limit != 0.0:
+        i = len(points) - 1
+        while i > 0:
+            length = (points[i] - points[i - 1]).length
+            if length < double_limit:
+                del points[i]
+                if i >= len(points):
+                    i -= 1
+            else:
+                tot_len += length
+                i -= 1
+    return tot_len
+
+
+def solve_curvature_2d(p1, p2, n1, n2, fac, fallback):
+    """ Add a nice circular curvature on 
+    """
+    from mathutils import Vector
+    from mathutils.geometry import (barycentric_transform,
+                                    intersect_line_line,
+                                    intersect_point_line,
+                                    )
+
+    p1_a = p1 + n1
+    p2_a = p2 - n2
+
+    isect = intersect_line_line(p1.to_3d(),
+                                p1_a.to_3d(),
+                                p2.to_3d(),
+                                p2_a.to_3d(),
+                                )
+
+    if isect:
+        corner = isect[0]
+    else:
+        corner = None
+
+    if corner:
+        corner = corner.xy
+        p1_first_order = p1.lerp(corner, fac)
+        p2_first_order = corner.lerp(p2, fac)
+        co = p1_first_order.lerp(p2_first_order, fac)
+        
+        return co.xy
+    else:
+        # cant interpolate. just return interpolated value
+        return fallback.copy() # p1.lerp(p2, fac)
+
+
+def points_to_bezier(points_orig,
+                     double_limit=0.0001,
+                     kink_tolerance=0.25,
+                     bezier_tolerance=0.1,  # error distance, scale dependant
+                     subdiv=8,
+                     angle_span=0.95,  # 1.0 tries to evaluate splines of 180d
+                     ):
+
+    import math
+    from mathutils import Vector
+
+    class Point(object):
+        __slots__ = ("co",
+                     "angle",
+                     "no",
+                     "is_joint",
+                     "next",
+                     "prev",
+                     )
+
+        def __init__(self, co):
+            self.co = co
+            self.is_joint = False
+
+        def calc_angle(self):
+            if self.prev is None or self.next is None:
+                self.angle = 0.0
+            else:
+                va = self.co - self.prev.co
+                vb = self.next.co - self.co
+                self.angle = va.angle(vb, 0.0)
+                
+                # XXX 2D
+                if line_point_side_v2(self.prev.co,
+                                      self.co,
+                                      self.next.co,
+                                      ) < 0.0:
+
+                    self.angle = -self.angle
+
+        def angle_diff(self):
+            """ use for detecting joints, detect difference in angle from
+                surrounding points.
+            """
+            if self.prev is None or self.next is None:
+                return 0.0
+            else:
+                if (self.angle > self.prev.angle and
+                            self.angle > self.next.angle):
+                    return abs(self.angle - self.prev.angle) / math.pi
+                else:
+                    return 0.0
+        
+        def angle_filter(self):
+            tot = 1
+            a = self.angle
+            if self.prev:
+                tot += 1
+                a += self.prev.angle
+
+            if self.next:
+                tot += 1
+                a += self.next.angle
+            
+            a = a / tot
+            return 0.0 if abs(a) < 0.01 else a
+        
+        def calc_normal(self):
+            v1 = v2 = None
+            if self.prev and not self.prev.is_joint:
+                v1 = (self.co - self.prev.co).normalized()
+            if self.next and not self.next.is_joint:
+                v2 = (self.next.co - self.co).normalized()
+            
+            if v1 and v2:
+                self.no = (v1 + v2).normalized()
+            elif v1:
+                self.no = v1
+            elif v2:
+                self.no = v2
+            else:
+                print("Warning, assigning dummy normal")
+                self.no = Vector(0, 1)
+
+
+    class Spline(object):
+        __slots__ = ("points",
+                     "handle_left",
+                     "handle_right",
+                     "next",
+                     "prev",
+                     )
+
+        def __init__(self, points):
+            self.points = points
+
+        def link_points(self):
+
+            if hasattr(self.points[0], "prev"):
+                raise Exception("already linked")
+
+            p_prev = None
+            for p in self.points:
+                p.prev = p_prev
+                p_prev = p
+
+            p_prev = None
+            for p in reversed(self.points):
+                p.next = p_prev
+                p_prev = p
+
+        def split(self, i, is_joint=False):
+            prev = self.prev
+            next = self.next
+
+            if is_joint:
+                self.points[i].is_joint = True
+
+            # share a point
+            spline_a = Spline(self.points[:i + 1])
+            spline_b = Spline(self.points[i:])
+
+            # invalidate self, dont reuse!
+            self.points = None
+            
+            spline_a.next = spline_b
+            spline_b.prev = spline_a
+    
+            spline_a.prev = prev
+            spline_b.next = next
+            if prev:
+                prev.next = spline_a
+            if next:
+                next.prev = spline_b
+
+            return spline_a, spline_b
+
+        def calc_angle(self):
+            for p in self.points:
+                p.calc_angle()
+
+        def calc_normal(self):
+            for p in self.points:
+                p.calc_normal()
+
+        def calc_all(self):
+            self.link_points()
+            self.calc_angle()
+            self.calc_normal()
+
+        def total_angle(self):
+            return abs(sum((p.angle for p in self.points)))
+
+        def redistribute(self, segment_length, smooth=False):
+            if len(self.points) == 1:
+                return
+
+            from mathutils.geometry import intersect_line_sphere_2d
+
+            p_line = p = self.points[0]
+            points = [(p.co.copy(), p.co.copy())]
+            p = p.next
+
+            def point_add(co, p=None):
+                co = co.copy()
+                co_smooth = co.copy()
+
+                if smooth:
+                    if p is None:
+                        pass # works ok but no smoothing
+                    elif (p.prev.no - p.no).length < 0.001:
+                        pass # normals are too similar, paralelle
+                    elif (p.angle > 0.0) != (p.prev.angle > 0.0):
+                        pass
+                    else:
+                        # visualize_line(p.co, p.co + p.no)
+                        
+                        # this assumes co is on the line
+                        fac = ((p.prev.co - co).length /
+                               (p.prev.co - p.co).length)
+
+                        assert(fac >= 0.0 and fac <= 1.0)
+
+                        co_smooth = solve_curvature_2d(p.prev.co,
+                                                       p.co,
+                                                       p.prev.no,
+                                                       p.no,
+                                                       fac,
+                                                       co,
+                                                       )
+
+                points.append((co, co_smooth))
+
+            def point_step(p):
+                if p.is_joint or p.next is None:
+                    point_add(p.co)
+                    return None
+                else:
+                    return p.next
+
+            print("START")
+            while p:
+                # we want the first pont past the segment size
+                
+                #if p.is_joint:
+                #    vis_circle_object(p.co)
+
+                length = (points[-1][0] - p.co).length
+                
+                if abs(length - segment_length) < 0.00001:
+                    # close enough to be considered on the circle bounds
+                    point_add(p.co)
+                    p_line = p

@@ Diff output truncated at 10240 characters. @@


More information about the Bf-extensions-cvs mailing list