def cubic_bezier_t_of_s_dynamic(p0,p1,p2,p3, initial_step = 50):
    '''
    returns a dictionary mapping of arclen values ot t values
    approximated at steps along the curve.  Dumber method than
    the decastelejue subdivision.
    '''
    s_t_map = {}
    s_t_map[0] = 0
    
    pi0 = p0
    cumul_length = 0
    
    iters = 0
    dt = 1/initial_step      
    t = dt
    while t < 1  and iters < 1000:
        iters += 1
        
        weights = cubic_bezier_weights(t)
        pi1 = cubic_bezier_blend_weights(p0, p1, p2, p3, weights)    
        cumul_length += (pi1 - pi0).length
        
        
        v_num = (pi1 - pi0).length/dt
        v_cls = cubic_bezier_derivative(p0, p1, p2, p3, t).length
        s_t_map[cumul_length] = t
             
        pi0 = pi1
        dt *= v_cls/v_num
        t += dt
        
        if iters == 1000:
            print('maxed iters')
    
    #take care of the last point
    weights = cubic_bezier_weights(1)
    pi1 = cubic_bezier_blend_weights(p0, p1, p2, p3, weights)    
    cumul_length += (pi1 - pi0).length
    s_t_map[cumul_length] = 1 
            
    dprint('initial dt %f, final dt %f' % (1/initial_step, dt), l=4)
    return s_t_map
Exemple #2
0
def cubic_bezier_t_of_s_dynamic(p0, p1, p2, p3, initial_step=50):
    '''
    returns a dictionary mapping of arclen values ot t values
    approximated at steps along the curve.  Dumber method than
    the decastelejue subdivision.
    '''
    s_t_map = {}
    s_t_map[0] = 0

    pi0 = p0
    cumul_length = 0

    iters = 0
    dt = 1 / initial_step
    t = dt
    while t < 1 and iters < 1000:
        iters += 1

        weights = cubic_bezier_weights(t)
        pi1 = cubic_bezier_blend_weights(p0, p1, p2, p3, weights)
        cumul_length += (pi1 - pi0).length

        v_num = (pi1 - pi0).length / dt
        v_cls = cubic_bezier_derivative(p0, p1, p2, p3, t).length
        s_t_map[cumul_length] = t

        pi0 = pi1
        dt *= v_cls / v_num
        t += dt

        if iters == 1000:
            print('maxed iters')

    #take care of the last point
    weights = cubic_bezier_weights(1)
    pi1 = cubic_bezier_blend_weights(p0, p1, p2, p3, weights)
    cumul_length += (pi1 - pi0).length
    s_t_map[cumul_length] = 1

    dprint('initial dt %f, final dt %f' % (1 / initial_step, dt), l=4)
    return s_t_map
def cubic_bezier_fit_points(l_co, error_scale, depth=0, t0=0, t3=1, allow_split=True, force_split=False):
    '''
    fits cubic bezier to given points
    returns list of tuples of (t0,t3,p0,p1,p2,p3)
    that best fits the given points l_co
    where t0 and t3 are the passed-in t0 and t3
    and p0,p1,p2,p3 are the control points of bezier
    '''
    assert l_co
    if len(l_co)<3:
        p0,p3 = l_co[0],l_co[-1]
        p12 = (p0+p3)/2
        return [(t0,t3,p0,p12,p12,p3)]
    l_d  = [0] + [(v0-v1).length for v0,v1 in zip(l_co[:-1],l_co[1:])]
    l_ad = [s for d,s in common_utilities.iter_running_sum(l_d)]
    dist = sum(l_d)
    if dist <= 0:
        print('cubic_bezier_fit_points: returning []')
        return [] #[(t0,t3,l_co[0],l_co[0],l_co[0],l_co[0])]
    l_t  = [ad/dist for ad in l_ad]
    
    ex,x0,x1,x2,x3 = cubic_bezier_fit_value([co[0] for co in l_co], l_t)
    ey,y0,y1,y2,y3 = cubic_bezier_fit_value([co[1] for co in l_co], l_t)
    ez,z0,z1,z2,z3 = cubic_bezier_fit_value([co[2] for co in l_co], l_t)
    tot_error = ex+ey+ez
    dprint('total error = %f (%f)' % (tot_error,error_scale), l=4)
    
    if not force_split:
        if tot_error < error_scale or depth == 4 or len(l_co)<=15 or not allow_split:
            p0,p1,p2,p3 = Vector((x0,y0,z0)),Vector((x1,y1,z1)),Vector((x2,y2,z2)),Vector((x3,y3,z3))
            return [(t0,t3,p0,p1,p2,p3)]
    
    # too much error in fit.  split sequence in two, and fit each sub-sequence
    
    # find a good split point
    ind_split = -1
    mindot = 1.0
    for ind in range(5,len(l_co)-5):
        if l_t[ind] < 0.4: continue
        if l_t[ind] > 0.6: break
        #if l_ad[ind] < 0.1: continue
        #if l_ad[ind] > dist-0.1: break
        
        v0 = l_co[ind-4]
        v1 = l_co[ind+0]
        v2 = l_co[ind+4]
        d0 = (v1-v0).normalized()
        d1 = (v2-v1).normalized()
        dot01 = d0.dot(d1)
        if ind_split==-1 or dot01 < mindot:
            ind_split = ind
            mindot = dot01
    
    if ind_split == -1:
        # did not find a good splitting point!
        p0,p1,p2,p3 = Vector((x0,y0,z0)),Vector((x1,y1,z1)),Vector((x2,y2,z2)),Vector((x3,y3,z3))
        return [(t0,t3,p0,p1,p2,p3)]
    
    l_co_left  = l_co[:ind_split]
    l_co_right = l_co[ind_split:]
    tsplit = ind_split / (len(l_co)-1)
    return cubic_bezier_fit_points(l_co_left, error_scale, depth=depth+1, t0=t0, t3=tsplit) + cubic_bezier_fit_points(l_co_right, error_scale, depth=depth+1, t0=tsplit, t3=t3)
Exemple #4
0
def cubic_bezier_fit_points(l_co,
                            error_scale,
                            depth=0,
                            t0=0,
                            t3=1,
                            allow_split=True,
                            force_split=False):
    '''
    fits cubic bezier to given points
    returns list of tuples of (t0,t3,p0,p1,p2,p3)
    that best fits the given points l_co
    where t0 and t3 are the passed-in t0 and t3
    and p0,p1,p2,p3 are the control points of bezier
    '''
    assert l_co
    if len(l_co) < 3:
        p0, p3 = l_co[0], l_co[-1]
        p12 = (p0 + p3) / 2
        return [(t0, t3, p0, p12, p12, p3)]
    l_d = [0] + [(v0 - v1).length for v0, v1 in zip(l_co[:-1], l_co[1:])]
    l_ad = [s for d, s in common_utilities.iter_running_sum(l_d)]
    dist = sum(l_d)
    if dist <= 0:
        print('cubic_bezier_fit_points: returning []')
        return []  #[(t0,t3,l_co[0],l_co[0],l_co[0],l_co[0])]
    l_t = [ad / dist for ad in l_ad]

    ex, x0, x1, x2, x3 = cubic_bezier_fit_value([co[0] for co in l_co], l_t)
    ey, y0, y1, y2, y3 = cubic_bezier_fit_value([co[1] for co in l_co], l_t)
    ez, z0, z1, z2, z3 = cubic_bezier_fit_value([co[2] for co in l_co], l_t)
    tot_error = ex + ey + ez
    dprint('total error = %f (%f)' % (tot_error, error_scale), l=4)

    if not force_split:
        if tot_error < error_scale or depth == 4 or len(
                l_co) <= 15 or not allow_split:
            p0, p1, p2, p3 = Vector((x0, y0, z0)), Vector(
                (x1, y1, z1)), Vector((x2, y2, z2)), Vector((x3, y3, z3))
            return [(t0, t3, p0, p1, p2, p3)]

    # too much error in fit.  split sequence in two, and fit each sub-sequence

    # find a good split point
    ind_split = -1
    mindot = 1.0
    for ind in range(5, len(l_co) - 5):
        if l_t[ind] < 0.4: continue
        if l_t[ind] > 0.6: break
        #if l_ad[ind] < 0.1: continue
        #if l_ad[ind] > dist-0.1: break

        v0 = l_co[ind - 4]
        v1 = l_co[ind + 0]
        v2 = l_co[ind + 4]
        d0 = (v1 - v0).normalized()
        d1 = (v2 - v1).normalized()
        dot01 = d0.dot(d1)
        if ind_split == -1 or dot01 < mindot:
            ind_split = ind
            mindot = dot01

    if ind_split == -1:
        # did not find a good splitting point!
        p0, p1, p2, p3 = Vector((x0, y0, z0)), Vector((x1, y1, z1)), Vector(
            (x2, y2, z2)), Vector((x3, y3, z3))
        return [(t0, t3, p0, p1, p2, p3)]

    l_co_left = l_co[:ind_split]
    l_co_right = l_co[ind_split:]
    tsplit = ind_split / (len(l_co) - 1)
    return cubic_bezier_fit_points(
        l_co_left, error_scale, depth=depth + 1, t0=t0,
        t3=tsplit) + cubic_bezier_fit_points(
            l_co_right, error_scale, depth=depth + 1, t0=tsplit, t3=t3)