Esempio n. 1
0
def bezier_surface_to_chebyshev(bsurf):
    spline = bsurf.data.splines[0]
    mb = spline.point_count_u  #order_u
    nb = spline.point_count_v  #order_v
    if max(mb, nb) > 4:
        return []
    B = numpy.zeros((mb, nb, 3))
    for l, p in enumerate(spline.points):
        i = l % mb
        j = int((l - i) / mb)
        for k in range(3):
            B[i, j, k] = p.co[k]
    M = cheb.B2Cmatrix(mb)
    N = cheb.B2Cmatrix(nb)
    C = numpy.zeros((mb, nb, 3))
    for dim in range(3):
        """
        for i in range(mb):
            for j in range(nb):
                for k in range(nb):
                    MB = 0.0
                    for l in range(mb):
                        MB += M[i,l]*B[l,k,dim]
                    C[i,j,dim] += MB*N[j,k]
        """
        C[:, :, dim] = matmul(matmul(M, B[:, :, dim]), N.T)
    return C
Esempio n. 2
0
def minimum_area_OBB(xy):
    """
    returns (center, ranges, axes)
    """
    # get convex hull
    hull = quickhull2d(xy)
    nh = len(hull)

    # handle special cases
    if nh < 1:
        return (numpy.zeros(2), numpy.zeros(2), numpy.eye(2))
    elif nh == 1:
        return (xy[hull[0]], numpy.zeros(2), numpy.eye(2))
    elif nh == 2:
        center = 0.5 * numpy.sum(xy[hull], axis=0)
        vec = xy[hull[1]] - xy[hull[0]]
        ranges = numpy.array([0.5 * numpy.hypot(vec[0], vec[1]), 0])
        axes = rotation_matrix2d(-numpy.arctan2(vec[1], vec[0]))
        return (center, ranges, axes)

    xyh = xy[hull]
    area = 1e20
    for i in range(nh):
        # i-th edge of the convex hull
        vec = xyh[(i + 1) % nh] - xyh[i]

        # apply rotation that makes that edge parallel to the x-axis
        rot = rotation_matrix2d(numpy.arctan2(vec[1], vec[0]))
        xyrot = matmul(rot, xyh.T).T

        # xy ranges of the rotated convex hull
        mn = numpy.amin(xyrot, axis=0)
        mx = numpy.amax(xyrot, axis=0)
        ranges_tmp = mx - mn
        area_tmp = ranges_tmp[0] * ranges_tmp[1]

        if area_tmp < area:
            area = area_tmp
            # inverse rotation
            rot = rot.T
            center = matvecprod(rot, 0.5 * (mn + mx))
            if ranges_tmp[1] > ranges_tmp[0]:
                ranges = 0.5 * ranges_tmp[[1, 0]]
                axes = numpy.zeros((2, 2))
                axes[:, 0] = rot[:, 1]
                axes[:, 1] = -rot[:, 0]
            else:
                ranges = 0.5 * ranges_tmp
                axes = rot
    return (center, ranges, axes)
Esempio n. 3
0
def minimal_OBB(xy, critere='area', tol=0.0):
    """
    returns (center, ranges, axes)
    """
    # get convex hull
    hull = quickhull2d(xy)
    nh = len(hull)

    # handle special cases
    if nh < 1:
        return (numpy.zeros(2), numpy.zeros(2), numpy.eye(2))
    elif nh == 1:
        return (xy[hull[0]], numpy.zeros(2), numpy.eye(2))
    elif nh == 2:
        center = 0.5 * numpy.sum(xy[hull], axis=0)
        vec = xy[hull[1]] - xy[hull[0]]
        ranges = numpy.array([0.5 * numpy.hypot(vec[0], vec[1]), 0])
        axes = rotation_matrix2d(-numpy.arctan2(vec[1], vec[0]))
        return (center, ranges, axes)

    xyh = xy[hull]
    val = 1e20
    frac = 1.0 + tol
    rot = numpy.zeros((2, 2))
    for i in range(nh):
        # i-th edge of the convex hull
        vec = xyh[(i + 1) % nh] - xyh[i]

        # apply rotation that makes that edge parallel to the x-axis
        if True:
            rot = rotation_matrix2d(numpy.arctan2(vec[1], vec[0]))
        else:
            rot[:, 0] = vec
            rot[:, 1] = [vec[1], -vec[0]]
            rot = rot / numpy.hypot(vec[0], vec[1])
        xyrot = matmul(rot, xyh.T).T

        # xy ranges of the rotated convex hull
        mn = numpy.amin(xyrot, axis=0)
        mx = numpy.amax(xyrot, axis=0)
        ranges_tmp = mx - mn
        if critere == 'area':
            val_tmp = ranges_tmp[0] * ranges_tmp[1]
        elif critere == 'width':
            val_tmp = min(ranges_tmp)
        print('VAL_TMP =', val_tmp, ', VAL_TMP - VAL =', val_tmp - val)

        if val_tmp < val * frac:
            val = val_tmp
            # inverse rotation
            rot = rot.T
            center = matvecprod(rot, 0.5 * (mn + mx))
            if ranges_tmp[1] > ranges_tmp[0]:
                ranges = 0.5 * ranges_tmp[[1, 0]]
                axes = numpy.zeros((2, 2))
                axes[:, 0] = rot[:, 1]
                axes[:, 1] = -rot[:, 0]
            else:
                ranges = 0.5 * ranges_tmp
                axes = rot
    return (center, ranges, axes)
Esempio n. 4
0
def minimum_width_OBB(xy):
    """
    returns (center, ranges, axes)
    inspired by https://www.mathworks.com/matlabcentral/fileexchange/7844-geom2d,
    function 'orientedBox', by David Legland
    """
    # get convex hull
    hull = quickhull2d(xy)
    nh = len(hull)

    # handle special cases
    if nh < 1:
        return (numpy.zeros(2), numpy.zeros(2), numpy.eye(2))
    elif nh == 1:
        return (xy[hull[0]], numpy.zeros(2), numpy.eye(2))
    elif nh == 2:
        center = 0.5 * numpy.sum(xy[hull], axis=0)
        vec = xy[hull[1]] - xy[hull[0]]
        ranges = numpy.array([0.5 * numpy.hypot(vec[0], vec[1]), 0])
        axes = rotation_matrix2d(-numpy.arctan2(vec[1], vec[0]))
        return (center, ranges, axes)

    xyh = xy[hull]
    minWidth = 1e20
    rotatedAngle = 0
    minAngle = 0
    # indices of vertices in extreme y directions
    indA = numpy.argmin(xyh[:, 1])
    indB = numpy.argmax(xyh[:, 1])

    caliperA = numpy.array([1,
                            0])  # Caliper A points along the positive x-axis
    caliperB = numpy.array([-1,
                            0])  # Caliper B points along the negative x-axis

    # Find the direction with minimum width (rotating caliper algorithm)
    while rotatedAngle < numpy.pi:
        # compute the direction vectors corresponding to each edge
        indA2 = (indA + 1) % nh
        vectorA = xyh[indA2] - xyh[indA]

        indB2 = (indB + 1) % nh
        vectorB = xyh[indB2] - xyh[indB]

        # Determine the angle between each caliper and the next adjacent edge in the convex hull
        angleA = angle_between_vectors_2d(caliperA, vectorA)
        angleB = angle_between_vectors_2d(caliperB, vectorB)
        if angleA < 0: angleA += 2 * numpy.pi
        if angleB < 0: angleB += 2 * numpy.pi

        # Determine the smallest of these angles
        angleIncrement = min(angleA, angleB)

        # Rotate the calipers by the smallest angle
        caliperA = rotate_2d_vector(caliperA, angleIncrement)
        caliperB = rotate_2d_vector(caliperB, angleIncrement)

        rotatedAngle += angleIncrement

        # compute current width, and update opposite vertex
        if angleA < angleB:
            width = abs(distance_point_line(xyh[indB], xyh[indA], xyh[indA2]))
            indA = (indA + 1) % nh
        else:
            width = abs(distance_point_line(xyh[indA], xyh[indB], xyh[indB2]))
            indB = (indB + 1) % nh

        # update minimum width and corresponding angle if needed
        if width < minWidth:
            minWidth = width
            minAngle = rotatedAngle

    # OBB axes
    axes = rotation_matrix2d(minAngle)

    # OBB extents and center
    xyr = matmul(axes, xyh.T).T
    mn = numpy.amin(xyr, axis=0)
    mx = numpy.amax(xyr, axis=0)
    center = matvecprod(axes, 0.5 * (mn + mx))
    ranges = 0.5 * (mx - mn)

    return (center, ranges, axes)
def LL_patch_from_corners(arcs,
                          center,
                          mrg=0,
                          construction_steps=False,
                          critere='area'):
    m = len(arcs)

    # normalize corners onto unit sphere
    s = numpy.array([arc[0] - center for arc in arcs])
    r = numpy.sqrt(numpy.sum(s**2, axis=1))
    ravg = numpy.sum(r) / float(m)
    s = s / numpy.tile(r, (3, 1)).T

    # orthonormal basis
    R = complete_orthonormal_matrix(numpy.sum(s, axis=0), i=0).T

    # central projection onto plane tangent to unit sphere at point r1 = R[:,0]
    s_dot_r1 = s[:, 0] * R[0, 0] + s[:, 1] * R[1, 0] + s[:, 2] * R[2, 0]
    s_dot_r1 = numpy.sign(s_dot_r1) * numpy.maximum(1e-6,
                                                    numpy.absolute(s_dot_r1))
    inv_s_dot_r1 = 1. / s_dot_r1
    p = s * numpy.tile(inv_s_dot_r1, (3, 1)).T

    # coordinates in local frame (r2, r3)
    ab = lib_linalg.matmul(p, R[:, 1:3])
    if construction_steps:
        abverts = [(a, b, 0) for a, b in ab]
        obj = lbu.pydata_to_mesh(verts=abverts,
                                 faces=[],
                                 edges=[(i, (i + 1) % m) for i in range(m)],
                                 name='ab_points')
        obj.layers[3] = True
        obj.layers[0] = False

    # mimimum-area OBB
    ctr_ab, rng_ab, axes_ab = minimal_OBB(ab)
    if construction_steps:
        OBBverts = [((-1)**(i + 1), (-1)**(j + 1), 0) for j in range(2)
                    for i in range(2)]
        OBBedges = [(0, 1), (1, 3), (3, 2), (2, 0)]
        obj = lbu.pydata_to_mesh(verts=OBBverts,
                                 faces=[],
                                 edges=OBBedges,
                                 name='ab_OBB')
        obj.layers[3] = True
        obj.layers[0] = False
        obj.scale = (rng_ab[0], rng_ab[1], 1)
        obj.location = (ctr_ab[0], ctr_ab[1], 0)
        obj.rotation_euler[2] = numpy.arctan2(axes_ab[1, 0], axes_ab[0, 0])

    R[:, 1:3] = lib_linalg.matmul(R[:, 1:3], axes_ab)

    # xyz-coords in rotated frame
    s = numpy.empty((0, 3), dtype=float)
    for arc in arcs:
        s = numpy.vstack([
            s,
            lib_linalg.matmul((arc - numpy.tile(center, (len(arc), 1))) / ravg,
                              R)
        ])
    n = len(s)

    if construction_steps:
        obj = lbu.pydata_to_mesh(verts=s,
                                 faces=[],
                                 edges=[(i, (i + 1) % n) for i in range(n)],
                                 name='s_points')
        obj.layers[2] = True
        obj.layers[0] = False
        obj.location = center
        obj.scale = ravg * numpy.ones(3)
        obj.rotation_euler = Matrix(R).to_euler()

    # spherical coords: longitude t(heta), latitude l(ambda)
    tl = numpy.zeros((n, 2))
    tl[:, 0] = numpy.arctan2(s[:, 1], s[:, 0])
    tl[:, 1] = numpy.arcsin(s[:, 2])

    if construction_steps:
        tlverts = [(t / numpy.pi, l / numpy.pi, 0) for t, l in tl]
        obj = lbu.pydata_to_mesh(verts=tlverts,
                                 faces=[],
                                 edges=[(i, (i + 1) % n) for i in range(n)],
                                 name='tl_points')
        obj.layers[4] = True
        obj.layers[0] = False

    min_tl = numpy.amin(tl, axis=0)
    max_tl = numpy.amax(tl, axis=0)

    ctr_tl = 0.5 * (min_tl + max_tl)
    print('lambda_0/pi = ', ctr_tl[1] / numpy.pi)
    rng_tl = (1 + mrg) * 0.5 * (max_tl - min_tl)
    print('max |lambda|/pi = ',
          max(abs(ctr_tl[1] - rng_tl[1]), ctr_tl[1] + rng_tl[1]) / numpy.pi)

    if construction_steps:
        obj = lbu.pydata_to_mesh(verts=OBBverts,
                                 faces=[],
                                 edges=OBBedges,
                                 name='tl_OBB')
        obj.layers[4] = True
        obj.layers[0] = False
        obj.scale = (rng_tl[0] / numpy.pi, rng_tl[1] / numpy.pi, 1)
        obj.location = (ctr_tl[0] / numpy.pi, ctr_tl[1] / numpy.pi, 0)

    # uv-coords
    uv = (tl - numpy.tile(ctr_tl, (n, 1))) / numpy.tile(rng_tl, (n, 1))

    return (ravg, R, ctr_tl, rng_tl, uv)
def LL_patch_from_arcs(planes, corners, center, nsample=10):
    m = len(planes)

    # normalize corners onto unit sphere
    if False:
        xyz_sample = corners
    else:
        xyz_sample = []
        for i, (tng, occ) in enumerate(planes):
            ci = corners[i]
            cj = corners[(i - 1) % m]
            #
            r1 = cj - occ
            r2 = ci - occ
            r190d = numpy.cross(r1, tng)
            a = numpy.arctan2(r2.dot(r190d), r2.dot(r1)) % (2 * numpy.pi)
            mij = occ + r1 * numpy.cos(0.5 * a) + r190d * numpy.sin(0.5 * a)
            xyz_sample.append(cj)
            xyz_sample.append(mij)

    s = numpy.array([xyz - center for xyz in xyz_sample])
    r = numpy.sqrt(numpy.sum(s**2, axis=1))
    ravg = numpy.sum(r) / float(len(s))
    s = s / numpy.tile(r, (3, 1)).T
    print('s = ')
    print(s)

    # orthonormal basis
    B = complete_orthonormal_matrix(numpy.sum(s, axis=0), i=0).T
    print('Btmp =')
    print(B)

    # central projection onto plane tangent to unit sphere at point r1 = B[:,0]
    s_dot_r1 = s[:, 0] * B[0, 0] + s[:, 1] * B[1, 0] + s[:, 2] * B[2, 0]
    s_dot_r1 = numpy.sign(s_dot_r1) * numpy.maximum(1e-6,
                                                    numpy.absolute(s_dot_r1))
    inv_s_dot_r1 = 1. / s_dot_r1
    p = s * numpy.tile(inv_s_dot_r1, (3, 1)).T
    print('p =')
    print(p)

    # coordinates in local frame (r2, r3)
    ab = lib_linalg.matmul(p, B[:, 1:3])
    print('p_2d =')
    print(ab)

    # mimimum-area OBB
    if True:
        ctr_ab, rng_ab, axes_ab = minimal_OBB(ab)
    else:
        ctr_ab, rng_ab, axes_ab = covariance_ellipse(ab)
    print('ctr_ab =')
    print(ctr_ab)
    print('rng_ab =')
    print(rng_ab)
    print('area =')
    print(rng_ab[0] * rng_ab[1])
    print('axes_ab =')
    print(axes_ab)

    # final rotation matrix
    B[:, 1:3] = lib_linalg.matmul(B[:, 1:3], axes_ab)
    numpy.savetxt(pth_test + 'rotation_matrix.dat', B)

    # longitude-latitude extrema
    min_lon = numpy.pi
    max_lon = -numpy.pi
    min_lat = 0.5 * numpy.pi
    max_lat = -0.5 * numpy.pi
    for i, (tng, occ) in enumerate(planes):
        ci = corners[i]
        cj = corners[(i - 1) % m]
        #
        fid = open(pth_test + 'arc_' + str(i) + '.dat', 'w')
        for vec in [center, occ, tng, cj, ci]:
            fid.write('%s %s %s\n' % (vec[0], vec[1], vec[2]))
        fid.close()
        #
        r1 = cj - occ
        r2 = ci - occ
        r190d = numpy.cross(r1, tng)
        a = numpy.arctan2(numpy.dot(r2, r190d), numpy.dot(r2, r1))
        if a < 0: a += 2 * numpy.pi
        # latitude
        min_lati, max_lati = extrema_latitude(occ - center, tng, r1, r190d, a,
                                              B[:, 2])
        min_lat = min(min_lat, min_lati)
        max_lat = max(max_lat, max_lati)
        # longitude
        min_loni, max_loni = extrema_longitude(occ - center, tng, r1, r190d, a,
                                               B)
        min_lon = min(min_lon, min_loni)
        max_lon = max(max_lon, max_loni)

    print('%s < lon/PI < %s' % (min_lon / numpy.pi, max_lon / numpy.pi))
    print('%s < lat/PI < %s' % (min_lat / numpy.pi, max_lat / numpy.pi))
    ctr_tl = 0.5 * numpy.array([max_lon + min_lon, max_lat + min_lat])
    rng_tl = 0.5 * numpy.array([max_lon - min_lon, max_lat - min_lat])

    return (ravg, B, ctr_tl, rng_tl)