Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCampbell Barton <ideasman42@gmail.com>2011-07-10 21:27:57 +0400
committerCampbell Barton <ideasman42@gmail.com>2011-07-10 21:27:57 +0400
commit0507e7a5b8dd558e12c906dd398a6e0bd7f76911 (patch)
tree4a4b361f021d39ad6fb66d7002daa97ca60f2de9
parentce1d35c00fd42e5c78f98121abf12f39959da6b7 (diff)
added some more methods of evaluating the curve.
-rw-r--r--modules/curve_utils.py356
1 files changed, 281 insertions, 75 deletions
diff --git a/modules/curve_utils.py b/modules/curve_utils.py
index fba6489e..529ddd0c 100644
--- a/modules/curve_utils.py
+++ b/modules/curve_utils.py
@@ -92,10 +92,7 @@ def treat_points(points,
def solve_curvature(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,
+ from mathutils.geometry import (intersect_line_line,
)
p1_a = p1 + n1
@@ -126,7 +123,7 @@ def solve_curvature(p1, p2, n1, n2, fac, fallback):
def points_to_bezier(points_orig,
double_limit=0.0001,
kink_tolerance=0.25,
- bezier_tolerance=0.02, # error distance, scale dependant
+ bezier_tolerance=0.05, # error distance, scale dependant
subdiv=8,
angle_span=0.95, # 1.0 tries to evaluate splines of 180d
):
@@ -360,28 +357,75 @@ def points_to_bezier(points_orig,
self.points[0].is_joint, self.points[-1].is_joint = joint
self.calc_all()
+ # raise Exception("END")
- def bezier_solve_(self):
- """ Calculate bezier handles,
- assume the splines have been broken up.
-
- http://polymathprogrammer.com/
+ def intersect_line(self, l1, l2, reverse=False):
+ """ Spectial kind of intersection, works in 3d on the plane
+ defimed by the points normal and the line.
"""
- p1 = self.points[0]
- p2 = self.points[-1]
+ from mathutils.geometry import (intersect_point_line,
+ )
+
+ if reverse:
+ p_first = self.points[-1]
+ no = -self.points[-1].no
+ point_iter = reversed(self.points[:-1])
+ else:
+ p_first = self.points[0]
+ no = self.points[0].no
+ point_iter = self.points[1:]
- line_ix_p1 = self.points[len(self.points) // 3]
- line_ix_p2 = self.points[int((len(self.points) / 3) * 2)]
+ # calculate the line right angles to the line
+ bi_no = (no - no.project(l2 - l1)).normalized()
- u = 1 / 3
- v = 2 / 3
+ bi_l1 = p_first.co
+ bi_l2 = p_first.co + bi_no
- p0x, p0y, p0z = p1.co
- p1x, p1y, p1z = line_ix_p1.co
- p2x, p2y, p2z = line_ix_p2.co
- p3x, p3y, p3z = p2.co
-
+ for p_apex in point_iter:
+ ix, fac = intersect_point_line(p_apex.co, bi_l1, bi_l2)
+
+ if fac < 0.0001:
+
+ if reverse:
+ p_apex_other = p_apex.next
+ else:
+ p_apex_other = p_apex.prev
+
+ # find the exact point on the line between the apex and
+ # the middle
+ p_test_1 = intersect_point_line(p_apex.co,
+ l1,
+ l2)[0]
+ p_test_2 = intersect_point_line(p_apex_other.co,
+ l1,
+ l2)[0]
+
+ w1 = (p_test_1 - p_apex.co).length
+ w2 = (p_test_2 - p_apex_other.co).length
+
+ #assert(w1 + w2 != 0)
+ try:
+ fac = w1 / (w1 + w2)
+ except ZeroDivisionError:
+ fac = 0.5
+ assert(fac >= 0.0 and fac <= 1.0)
+
+ p_apex_co = p_apex.co.lerp(p_apex_other.co, fac)
+ p_apex_no = p_apex.no.lerp(p_apex_other.no, fac)
+ p_apex_no.normalize()
+
+ # visualize_line(p_mid.to_3d(), corner.to_3d())
+ # visualize_line(p_apex.co.to_3d(), p_apex_co.to_3d())
+
+ return p_apex_co, p_apex_no, p_apex
+
+ # intersection not found
+ return None, None, None
+
+
+ @staticmethod
+ def bez_solve(p0, p1, p2, p3, u, v):
ui = 1.0 - u
vi = 1.0 - v
u_p3 = u * u * u
@@ -389,14 +433,6 @@ def points_to_bezier(points_orig,
ui_p3 = ui * ui * ui
vi_p3 = vi * vi * vi
-
- # --- snip
-
- q1 = Vector()
- q2 = Vector()
-
- pos = Vector(), Vector(), Vector(), Vector()
-
a = 3.0 * ui * ui * u
b = 3.0 * ui * u * u
c = 3.0 * vi * vi * v
@@ -407,26 +443,117 @@ def points_to_bezier(points_orig,
assert(0)
return 0
- q1.x = p1x - (ui_p3 * p0x + u_p3 * p3x)
- q1.y = p1y - (ui_p3 * p0y + u_p3 * p3y)
- q1.z = p1z - (ui_p3 * p0z + u_p3 * p3z)
+ q1 = p1 - (ui_p3 * p0 + u_p3 * p3)
+ q2 = p2 - (vi_p3 * p0 + v_p3 * p3)
- q2.x = p2x - (vi_p3 * p0x + v_p3 * p3x)
- q2.y = p2y - (vi_p3 * p0y + v_p3 * p3y)
- q2.z = p2z - (vi_p3 * p0z + v_p3 * p3z)
+ return ((d * q1 - b * q2) / det,
+ (-c * q1 + a * q2) / det
+ )
- pos[1].x = (d * q1.x - b * q2.x) / det
- pos[1].y = (d * q1.y - b * q2.y) / det
- pos[1].z = (d * q1.z - b * q2.z) / det
+ def bezier_solve__math1(self):
+ """ Calculate bezier handles,
+ assume the splines have been broken up.
- pos[2].x = ((-c) * q1.x + a * q2.x) / det
- pos[2].y = ((-c) * q1.y + a * q2.y) / det
- pos[2].z = ((-c) * q1.z + a * q2.z) / det
+ http://polymathprogrammer.com/
+ """
- self.handle_left = pos[1]
- self.handle_right = pos[2]
+ def get(f, min=0.0, max=1.0):
+ f = (f * (max - min) + min)
+ return self.points[int((len(self.points) - 1) * f)].co
+
+
+ p1 = get(0.0)
+ p2 = get(1.0)
+ i1 = get(1/3)
+ i2 = get(2/3)
+
+ pos = __class__.bez_solve(p1, i1, i2, p2, 1.0 / 3.0, 2.0 / 3.0)
+ self.handle_left = self.points[0].co + (pos[0] - self.points[0].co)
+ self.handle_right = self.points[-1].co + (pos[1] - self.points[-1].co)
+
+ def bezier_solve__math2(self):
+
+ def get(f, min=0.0, max=1.0):
+ f = (f * (max - min) + min)
+ return self.points[int((len(self.points) - 1) * f)].co
+
+ p1 = get(0.0, min=0.0, max=0.5)
+ p2 = get(1.0, min=0.0, max=0.5)
+ i1 = get(1/3, min=0.0, max=0.5)
+ i2 = get(2/3, min=0.0, max=0.5)
+
+ pos_a = __class__.bez_solve(p1, i1, i2, p2, 1.0 / 3.0, 2.0 / 3.0)
+
+ p1 = get(0.0, min=0.5, max=1.0)
+ p2 = get(1.0, min=0.5, max=1.0)
+ i1 = get(1/3, min=0.5, max=1.0)
+ i2 = get(2/3, min=0.5, max=1.0)
+
+ pos_b = __class__.bez_solve(p1, i1, i2, p2, 1.0 / 3.0, 2.0 / 3.0)
+
+ self.handle_left = self.points[0].co + (pos_a[0] - self.points[0].co) * 2
+ self.handle_right = self.points[-1].co + (pos_b[1] - self.points[-1].co) * 2
+
+ def bezier_solve__inkscape(self):
+
+ # static void
+ # estimate_bi(Point bezier[4], unsigned const ei,
+ # Point const data[], double const u[], unsigned const len)
+ def estimate_bi(bezier, ei, data, u):
+
+ def B0(u): return ( ( 1.0 - u ) * ( 1.0 - u ) * ( 1.0 - u ) )
+ def B1(u): return ( 3 * u * ( 1.0 - u ) * ( 1.0 - u ) )
+ def B2(u): return ( 3 * u * u * ( 1.0 - u ) )
+ def B3(u): return ( u * u * u )
+
+ # assert( not (1 <= ei and ei <= 2))
+ oi = 3 - ei
+ num = [0.0, 0.0, 0.0]
+ den = 0.0
+
+ for i in range(len(data)):
+ ui = u[i];
+ b = [
+ B0(ui),
+ B1(ui),
+ B2(ui),
+ B3(ui)
+ ]
+
+ for d in range(3):
+ num[d] += (b[ei] * (b[0] * bezier[0][d] +
+ b[oi] * bezier[oi][d] +
+ b[3] * bezier[3][d] +
+ - data[i][d]))
+
+ den -= b[ei] * b[ei]
+
+ if den:
+ for d in range(3):
+ bezier[ei][d] = num[d] / den
+ else:
+ bezier[ei] = (oi * bezier[0] + ei * bezier[3]) / 3.0
+ bezier = [
+ self.points[0].co,
+ self.points[0].co.lerp(self.points[-1].co, 1/3),
+ self.points[0].co.lerp(self.points[-1].co, 2/3),
+ self.points[-1].co,
+ ]
+ data = [p.co for p in self.points]
+ u = [i / len(self.points) for i in range(len(self.points))]
+ estimate_bi(bezier, 1, data, u)
+ estimate_bi(bezier, 2, data, u)
+ estimate_bi(bezier, 1, data, u)
+ estimate_bi(bezier, 2, data, u)
+ estimate_bi(bezier, 1, data, u)
+ estimate_bi(bezier, 2, data, u)
+ estimate_bi(bezier, 1, data, u)
+ estimate_bi(bezier, 2, data, u)
+
+ self.handle_left = bezier[1]
+ self.handle_right = bezier[2]
- def bezier_solve(self):
+ def bezier_solve_ideasman42(self):
from mathutils.geometry import (intersect_point_line,
intersect_line_line,
)
@@ -449,41 +576,92 @@ def points_to_bezier(points_orig,
# visualize_line(p1.co, l1_co)
# visualize_line(p2.co, l2_co)
- # picking 1/2 and 2/3'rds works best
- line_ix_p1 = self.points[int(len(self.points) * (1.0 / 3.0))]
- line_ix_p1_co, line_ix_p1_no = line_ix_p1.co, line_ix_p1.no
- line_ix_p2 = self.points[int(len(self.points) * (2.0 / 3.0))]
- line_ix_p2_co, line_ix_p2_no = line_ix_p2.co, line_ix_p2.no
-
- # used to seek for the upper most point but this gives mostly
- # as good results
- p1_apex_co = self.points[int(len(self.points) * (1.0 / 3.0) * 0.75)].co
- p2_apex_co = self.points[int(len(self.points) * (1.0 - (1.0 / 3.0) * 0.75))].co
+ line_ix_p1_co, line_ix_p1_no, line_ix_p1 = \
+ self.intersect_line(p1.co,
+ l1_co,
+ )
+ line_ix_p2_co, line_ix_p2_no, line_ix_p2 = \
+ self.intersect_line(p2.co,
+ l2_co,
+ reverse=True,
+ )
+ if line_ix_p1_co is None:
+ line_ix_p1_co, line_ix_p1_no, line_ix_p1 = \
+ p1.next.co, p1.next.no, p1.next
+ if line_ix_p2_co is None:
+ line_ix_p2_co, line_ix_p2_no, line_ix_p2 = \
+ p2.prev.co, p2.prev.no, p2.prev
+
+ # vis_circle_object(line_ix_p1_co)
+ # vis_circle_object(line_ix_p2_co)
+
+ l1_max = 0.0
+ p1_apex_co = None
+ p = self.points[1]
+ while p and (not p.is_joint) and p != line_ix_p1:
+ ix = intersect_point_line(p.co, p1.co, l1_co)[0]
+ length = (ix - p.co).length
+ if length > l1_max:
+ l1_max = length
+ p1_apex_co = p.co
+ p = p.next
+
+ l2_max = 0.0
+ p2_apex_co = None
+ p = self.points[-2]
+ while p and (not p.is_joint) and p != line_ix_p2:
+ ix = intersect_point_line(p.co, p2.co, l2_co)[0]
+ length = (ix - p.co).length
+ if length > l2_max:
+ l2_max = length
+ p2_apex_co = p.co
+ p = p.prev
+
+ if p1_apex_co is None:
+ p1_apex_co = p1.next.co
+ if p2_apex_co is None:
+ p2_apex_co = p2.prev.co
l1_tan = (p1.no - p1.no.project(l1_no)).normalized()
l2_tan = -(p2.no - p2.no.project(l2_no)).normalized()
+ # values are good!
+ visualize_line(p1.co, p1.co + l1_tan)
+ visualize_line(p2.co, p2.co + l2_tan)
+
+ visualize_line(p1.co, p1.co + l1_no)
+ visualize_line(p2.co, p2.co + l2_no)
+
# calculate bias based on the position of the other point allong
# the tangent.
# first need to reflect the second normal for angle comparison
# first fist need the reflection normal
+
+ # angle between - 0 - 1
+ from math import pi
no_ref = p_vec.cross(p2.no).cross(p_vec).normalized()
l2_no_ref = p2.no.reflect(no_ref).normalized()
+ no_angle = p1.no.angle(l2_no_ref) / pi
del no_ref
- from math import pi
# This could be tweaked but seems to work well
- fac_fac = (p1.no.angle(l2_no_ref) / pi)
- fac_1 = p1.no.angle(line_ix_p1_co - p1.co) / pi
- fac_2 = (-p2.no).angle(line_ix_p2_co - p2.co) / pi
+ # fac_fac = 1.0
+
+ print("angle", "%.6f" % no_angle)
- # fac_1 = fac_2 = 0.0
- print(fac_1, fac_2)
- # why * 3 ? - it just gives best results
- h1_fac = ((p1.co - p1_apex_co).length / 0.75) * (1.0 + fac_1 * fac_fac * 3.0)
- h2_fac = ((p2.co - p2_apex_co).length / 0.75) * (1.0 + fac_2 * fac_fac * 3.0)
+ fac_1 = intersect_point_line(p2_apex_co,
+ p1.co,
+ p1.co + l1_tan * (p1.co - p1_apex_co).length,
+ )[1] * (1.0 + no_angle)
+ fac_2 = intersect_point_line(p1_apex_co,
+ p2.co,
+ p2.co + l2_tan * (p2.co - p2_apex_co).length,
+ )[1] * (1.0 + no_angle)
+
+ h1_fac = abs(fac_1)
+ h2_fac = abs(fac_2)
h1 = p1.co + (p1.no * h1_fac)
h2 = p2.co - (p2.no * h2_fac)
@@ -491,14 +669,17 @@ def points_to_bezier(points_orig,
self.handle_left = h1
self.handle_right = h2
- """
+ '''
visualize_line(p1.co, p1_apex_co)
visualize_line(p1_apex_co, p2_apex_co)
visualize_line(p2.co, p2_apex_co)
visualize_line(p1.co, p2.co)
- """
+ '''
+
+ def bezier_solve(self):
+ return self.bezier_solve__inkscape()
- def bezier_error(self, error_max=-1.0, test_count=16):
+ def bezier_error(self, error_max=-1.0, test_count=8):
from mathutils.geometry import interpolate_bezier
test_points = interpolate_bezier(self.points[0].co,
@@ -515,7 +696,6 @@ def points_to_bezier(points_orig,
# this is a rough method measuring the error but should be ok
# TODO. dont test against every single point.
for co in test_points:
- co = co
# initial values
co_best = self.points[0].co
@@ -752,11 +932,25 @@ def points_to_bezier(points_orig,
for s in curve.splines:
s.bezier_solve()
'''
+
+ '''
+ def angle_point(s):
+ a = 0.0
+ a_best = len(s.points) // 2
+ i = 1
+ for p in s.points[2:-2]:
+ if p.angle > a:
+ a = p.angle
+ a_best = i
+ i += 1
+ return a_best
+ '''
+
# or recursively subdivide...
curve.split_func_spline(lambda s:
- len(s.points) // 2
+ len(s.points) // 2 # angle_point(s)
if ((s.bezier_solve(),
- s.bezier_error(bezier_tolerance))[1]
+ s.bezier_error(bezier_tolerance))[1]
and (len(s.points)))
else -1,
recursive=True,
@@ -774,14 +968,26 @@ def points_to_bezier(points_orig,
if __name__ == "__main__":
- bpy.ops.wm.open_mainfile(filepath="/root/curve_test2.blend")
+ if 0:
+ bpy.ops.wm.open_mainfile(filepath="/root/curve_test3.blend")
+
+ for c in "Curve Curve.001 Curve.002 Curve.003 Curve.004 Curve.005".split():
+ print("---", c)
+ ob = bpy.data.objects[c]
+ points = [p.co.xyz for s in ob.data.splines for p in s.points]
+
+ print("points_to_bezier 1")
+ points_to_bezier(points)
+ print("points_to_bezier 2")
+ else:
+ bpy.ops.wm.open_mainfile(filepath="/root/curve_test2.blend")
- ob = bpy.data.objects["Curve"]
- points = [p.co.xyz for s in ob.data.splines for p in s.points]
+ ob = bpy.data.objects['Curve']
+ points = [p.co.xyz for s in ob.data.splines for p in s.points]
- print("points_to_bezier 1")
- points_to_bezier(points)
- print("points_to_bezier 2")
+ print("points_to_bezier 1")
+ points_to_bezier(points)
+ print("points_to_bezier 2")
bpy.ops.wm.save_as_mainfile(filepath="/root/curve_test_edit.blend",
copy=True)