예제 #1
0
 def pre_calc_torsion_integral(self, resolution):
     t_min, t_max = self.get_u_bounds()
     ts = np.linspace(t_min, t_max, resolution)
     vectors = self.evaluate_array(ts)
     dvs = vectors[1:] - vectors[:-1]
     lengths = np.linalg.norm(dvs, axis=1)
     xs = np.insert(np.cumsum(lengths), 0, 0)
     ys = self.torsion_array(ts)
     self._torsion_integral = TrapezoidIntegral(ts, xs, ys)
     self._torsion_integral.calc()
예제 #2
0
class SvCurve(object):
    def __repr__(self):
        if hasattr(self, '__description__'):
            description = self.__description__
        else:
            description = self.__class__.__name__
        return "<{} curve>".format(description)

    def evaluate(self, t):
        raise Exception("not implemented!")

    def evaluate_array(self, ts):
        raise Exception("not implemented!")

    def get_tangent_delta(self, tangent_delta=None):
        if tangent_delta is None:
            if hasattr(self, 'tangent_delta'):
                h = self.tangent_delta
            else:
                h = DEFAULT_TANGENT_DELTA
        else:
            h = DEFAULT_TANGENT_DELTA
        return h

    def calc_length(self, t_min, t_max, resolution=50):
        ts = np.linspace(t_min, t_max, num=resolution)
        vectors = self.evaluate_array(ts)
        dvs = vectors[1:] - vectors[:-1]
        lengths = np.linalg.norm(dvs, axis=1)
        return np.sum(lengths)

    def tangent(self, t, tangent_delta=None):
        v = self.evaluate(t)
        h = self.get_tangent_delta(tangent_delta)
        v_h = self.evaluate(t + h)
        return (v_h - v) / h

    def tangent_array(self, ts, tangent_delta=None):
        vs = self.evaluate_array(ts)
        h = self.get_tangent_delta(tangent_delta)
        u_max = self.get_u_bounds()[1]
        bad_idxs = (ts + h) > u_max
        good_idxs = (ts + h) <= u_max
        ts_h = ts + h
        ts_h[bad_idxs] = (ts - h)[bad_idxs]

        vs_h = self.evaluate_array(ts_h)
        tangents_plus = (vs_h - vs) / h
        tangents_minus = (vs - vs_h) / h
        tangents_x = np.where(good_idxs, tangents_plus[:, 0],
                              tangents_minus[:, 0])
        tangents_y = np.where(good_idxs, tangents_plus[:, 1],
                              tangents_minus[:, 1])
        tangents_z = np.where(good_idxs, tangents_plus[:, 2],
                              tangents_minus[:, 2])
        tangents = np.stack((tangents_x, tangents_y, tangents_z)).T
        return tangents

    def second_derivative(self, t, tangent_delta=None):
        h = self.get_tangent_delta(tangent_delta)

        v0 = self.evaluate(t - h)
        v1 = self.evaluate(t)
        v2 = self.evaluate(t + h)
        return (v2 - 2 * v1 + v0) / (h * h)

    def second_derivative_array(self, ts, tangent_delta=None):
        h = self.get_tangent_delta(tangent_delta)
        v0s = self.evaluate_array(ts - h)
        v1s = self.evaluate_array(ts)
        v2s = self.evaluate_array(ts + h)
        return (v2s - 2 * v1s + v0s) / (h * h)

    def third_derivative(self, t, tangent_delta=None):
        return self.third_derivative_array(np.array([t]),
                                           tangent_delta=tangent_delta)[0]

    def third_derivative_array(self, ts, tangent_delta=None):
        h = self.get_tangent_delta(tangent_delta)
        v0s = self.evaluate_array(ts)
        v1s = self.evaluate_array(ts + h)
        v2s = self.evaluate_array(ts + 2 * h)
        v3s = self.evaluate_array(ts + 3 * h)
        return (-v0s + 3 * v1s - 3 * v2s + v3s) / (h * h * h)

    def derivatives_array(self, n, ts, tangent_delta=None):
        h = self.get_tangent_delta(tangent_delta)

        result = []
        N = len(ts)
        t_min, t_max = self.get_u_bounds()
        if n >= 1:
            first = self.tangent_array(ts, tangent_delta=h)
            result.append(first)

        if n >= 2:
            low = ts < t_min + h
            high = ts > t_max - h
            good = np.logical_and(np.logical_not(low), np.logical_not(high))

            points = np.empty((N, 3))
            minus_h = np.empty((N, 3))
            plus_h = np.empty((N, 3))

            if good.any():
                minus_h[good] = self.evaluate_array(ts[good] - h)
                points[good] = self.evaluate_array(ts[good])
                plus_h[good] = self.evaluate_array(ts[good] + h)

            if low.any():
                minus_h[low] = self.evaluate_array(ts[low])
                points[low] = self.evaluate_array(ts[low] + h)
                plus_h[low] = self.evaluate_array(ts[low] + 2 * h)

            if high.any():
                minus_h[high] = self.evaluate_array(ts[high] - 2 * h)
                points[high] = self.evaluate_array(ts[high] - h)
                plus_h[high] = self.evaluate_array(ts[high])

            second = (plus_h - 2 * points + minus_h) / (h * h)
            result.append(second)

        if n >= 3:
            v0s = points
            v1s = plus_h
            v2s = self.evaluate_array(ts + 2 * h)
            v3s = self.evaluate_array(ts + 3 * h)
            third = (-v0s + 3 * v1s - 3 * v2s + v3s) / (h * h * h)
            result.append(third)

        return result

    def main_normal(self, t, normalize=True, tangent_delta=None):
        h = self.get_tangent_delta(tangent_delta)

        tangent = self.tangent(t, tangent_delta=h)
        binormal = self.binormal(t, normalize, tangent_delta=h)
        v = np.cross(binormal, tangent)
        if normalize:
            v = v / np.linalg.norm(v)
        return v

    def binormal(self, t, normalize=True, tangent_delta=None):
        h = self.get_tangent_delta(tangent_delta)

        tangent = self.tangent(t, tangent_delta=h)
        second = self.second_derivative(t, tangent_delta=h)
        v = np.cross(tangent, second)
        if normalize:
            v = v / np.linalg.norm(v)
        return v

    def main_normal_array(self, ts, normalize=True, tangent_delta=None):
        h = self.get_tangent_delta(tangent_delta)

        tangents = self.tangent_array(ts, tangent_delta=h)
        binormals = self.binormal_array(ts, normalize, tangent_delta=h)
        v = np.cross(binormals, tangents)
        if normalize:
            norms = np.linalg.norm(v, axis=1, keepdims=True)
            nonzero = (norms > 0)[:, 0]
            v[nonzero] = v[nonzero] / norms[nonzero][:, 0][np.newaxis].T
        return v

    def binormal_array(self, ts, normalize=True, tangent_delta=None):
        h = self.get_tangent_delta(tangent_delta)

        tangents, seconds = self.derivatives_array(2, ts, tangent_delta=h)
        v = np.cross(tangents, seconds)
        if normalize:
            norms = np.linalg.norm(v, axis=1, keepdims=True)
            nonzero = (norms > 0)[:, 0]
            v[nonzero] = v[nonzero] / norms[nonzero][:, 0][np.newaxis].T
        return v

    def tangent_normal_binormal_array(self,
                                      ts,
                                      normalize=True,
                                      tangent_delta=None):
        h = self.get_tangent_delta(tangent_delta)

        tangents, seconds = self.derivatives_array(2, ts, tangent_delta=h)
        binormals = np.cross(tangents, seconds)
        if normalize:
            norms = np.linalg.norm(binormals, axis=1, keepdims=True)
            nonzero = (norms > 0)[:, 0]
            binormals[nonzero] = binormals[nonzero] / norms[nonzero][:, 0][
                np.newaxis].T
        normals = np.cross(binormals, tangents)
        if normalize:
            norms = np.linalg.norm(normals, axis=1, keepdims=True)
            nonzero = (norms > 0)[:, 0]
            normals[nonzero] = normals[nonzero] / norms[nonzero][:, 0][
                np.newaxis].T
        return tangents, normals, binormals

    def arbitrary_frame_array(self, ts, tangent_delta=None):
        h = self.get_tangent_delta(tangent_delta)

        normals = []
        binormals = []

        points = self.evaluate_array(ts)
        tangents = self.tangent_array(ts, tangent_delta=h)
        tangents /= np.linalg.norm(tangents, axis=1, keepdims=True)

        for i, t in enumerate(ts):
            tangent = tangents[i]
            normal = np.array(Vector(tangent).orthogonal())
            binormal = np.cross(tangent, normal)
            binormal /= np.linalg.norm(binormal)
            normals.append(normal)
            binormals.append(binormal)

        normals = np.array(normals)
        binormals = np.array(binormals)

        matrices_np = np.dstack((normals, binormals, tangents))
        matrices_np = np.transpose(matrices_np, axes=(0, 2, 1))
        matrices_np = np.linalg.inv(matrices_np)
        return matrices_np, normals, binormals

    def frame_by_plane_array(self, ts, plane_normal, tangent_delta=None):
        h = self.get_tangent_delta(tangent_delta)
        n = len(ts)
        tangents = self.tangent_array(ts, tangent_delta=h)
        tangents /= np.linalg.norm(tangents, axis=1, keepdims=True)
        plane_normals = np.tile(plane_normal[np.newaxis].T, n).T
        normals = np.cross(tangents, plane_normals)
        normals /= np.linalg.norm(normals, axis=1, keepdims=True)
        binormals = np.cross(tangents, normals)

        matrices_np = np.dstack((normals, binormals, tangents))
        matrices_np = np.transpose(matrices_np, axes=(0, 2, 1))
        matrices_np = np.linalg.inv(matrices_np)
        return matrices_np, normals, binormals

    FAIL = 'fail'
    ASIS = 'asis'
    RETURN_NONE = 'none'

    def frame_array(self, ts, on_zero_curvature=ASIS, tangent_delta=None):
        """
        input:
            * ts - np.array of shape (n,)
            * on_zero_curvature - what to do if the curve has zero curvature at one of T values.
              The supported options are:
              * SvCurve.FAIL: raise ZeroCurvatureException
              * SvCurve.RETURN_NONE: return None
              * SvCurve.ASIS: do not perform special check for this case, the
                algorithm will raise a general LinAlgError exception if it can't calculate the matrix.

        output: tuple:
            * matrices: np.array of shape (n, 3, 3)
            * normals: np.array of shape (n, 3)
            * binormals: np.array of shape (n, 3)
        """
        h = self.get_tangent_delta(tangent_delta)
        tangents, normals, binormals = self.tangent_normal_binormal_array(
            ts, tangent_delta=h)

        if on_zero_curvature != SvCurve.ASIS:
            zero_normal = np.linalg.norm(normals, axis=1) < 1e-6
            if zero_normal.any():
                if on_zero_curvature == SvCurve.FAIL:
                    raise ZeroCurvatureException(np.unique(ts[zero_normal]),
                                                 zero_normal)
                elif on_zero_curvature == SvCurve.RETURN_NONE:
                    return None

        tangents = tangents / np.linalg.norm(tangents, axis=1)[np.newaxis].T
        matrices_np = np.dstack((normals, binormals, tangents))
        matrices_np = np.transpose(matrices_np, axes=(0, 2, 1))
        try:
            matrices_np = np.linalg.inv(matrices_np)
            return matrices_np, normals, binormals
        except np.linalg.LinAlgError as e:
            error("Some of matrices are singular:")
            for i, m in enumerate(matrices_np):
                if abs(np.linalg.det(m) < 1e-5):
                    error("M[%s] (t = %s):\n%s", i, ts[i], m)
            raise e

    def zero_torsion_frame_array(self, ts, tangent_delta=None):
        """
        input: ts - np.array of shape (n,)
        output: tuple:
            * cumulative torsion - np.array of shape (n,) (rotation angles in radians)
            * matrices - np.array of shape (n, 3, 3)
        """
        if not hasattr(self, '_torsion_integral'):
            raise Exception(
                "pre_calc_torsion_integral() has to be called first")

        h = self.get_tangent_delta(tangent_delta)
        vectors = self.evaluate_array(ts)
        matrices_np, normals, binormals = self.frame_array(ts, tangent_delta=h)
        integral = self.torsion_integral(ts)
        new_matrices = []
        for matrix_np, point, angle in zip(matrices_np, vectors, integral):
            frenet_matrix = Matrix(matrix_np.tolist()).to_4x4()
            rotation_matrix = Matrix.Rotation(-angle, 4, 'Z')
            matrix = frenet_matrix @ rotation_matrix
            matrix.translation = Vector(point)
            new_matrices.append(matrix)

        return integral, new_matrices

    def curvature_array(self, ts, tangent_delta=None):
        h = self.get_tangent_delta(tangent_delta)
        tangents, seconds = self.derivatives_array(2, ts, tangent_delta=h)
        numerator = np.linalg.norm(np.cross(tangents, seconds), axis=1)
        tangents_norm = np.linalg.norm(tangents, axis=1)
        denominator = tangents_norm * tangents_norm * tangents_norm
        return numerator / denominator

    def torsion_array(self, ts, tangent_delta=None):
        h = self.get_tangent_delta(tangent_delta)

        tangents, seconds, thirds = self.derivatives_array(3,
                                                           ts,
                                                           tangent_delta=h)
        seconds_thirds = np.cross(seconds, thirds)
        numerator = (tangents * seconds_thirds).sum(axis=1)
        #numerator = np.apply_along_axis(lambda tangent: tangent.dot(seconds_thirds), 1, tangents)
        first_second = np.cross(tangents, seconds)
        denominator = np.linalg.norm(first_second, axis=1)
        return numerator / (denominator * denominator)

    def pre_calc_torsion_integral(self, resolution, tangent_delta=None):
        h = self.get_tangent_delta(tangent_delta)
        t_min, t_max = self.get_u_bounds()
        ts = np.linspace(t_min, t_max, resolution)
        vectors = self.evaluate_array(ts)
        dvs = vectors[1:] - vectors[:-1]
        lengths = np.linalg.norm(dvs, axis=1)
        xs = np.insert(np.cumsum(lengths), 0, 0)
        ys = self.torsion_array(ts, tangent_delta=h)
        self._torsion_integral = TrapezoidIntegral(ts, xs, ys)
        self._torsion_integral.calc()

    def torsion_integral(self, ts):
        return self._torsion_integral.evaluate_cubic(ts)

    def get_u_bounds(self):
        raise Exception("not implemented!")

    def get_degree(self):
        raise Exception(
            "`Get Degree' method is not applicable to curve of type `{}'".
            format(type(self)))

    def get_control_points(self):
        """
        Returns: np.array of shape (n, 3)
        """
        return np.array([])
예제 #3
0
class SvCurve(object):
    def __repr__(self):
        if hasattr(self, '__description__'):
            description = self.__description__
        else:
            description = self.__class__.__name__
        return "<{} curve>".format(description)

    def evaluate(self, t):
        raise Exception("not implemented!")

    def evaluate_array(self, ts):
        raise Exception("not implemented!")

    def calc_length(self, t_min, t_max, resolution=50):
        ts = np.linspace(t_min, t_max, num=resolution)
        vectors = self.evaluate_array(ts)
        dvs = vectors[1:] - vectors[:-1]
        lengths = np.linalg.norm(dvs, axis=1)
        return np.sum(lengths)

    def tangent(self, t):
        v = self.evaluate(t)
        h = self.tangent_delta
        v_h = self.evaluate(t + h)
        return (v_h - v) / h

    def tangent_array(self, ts):
        vs = self.evaluate_array(ts)
        h = self.tangent_delta
        u_max = self.get_u_bounds()[1]
        bad_idxs = (ts + h) > u_max
        good_idxs = (ts + h) <= u_max
        ts_h = ts + h
        ts_h[bad_idxs] = (ts - h)[bad_idxs]

        vs_h = self.evaluate_array(ts_h)
        tangents_plus = (vs_h - vs) / h
        tangents_minus = (vs - vs_h) / h
        tangents_x = np.where(good_idxs, tangents_plus[:, 0],
                              tangents_minus[:, 0])
        tangents_y = np.where(good_idxs, tangents_plus[:, 1],
                              tangents_minus[:, 1])
        tangents_z = np.where(good_idxs, tangents_plus[:, 2],
                              tangents_minus[:, 2])
        tangents = np.stack((tangents_x, tangents_y, tangents_z)).T
        return tangents

    def second_derivative(self, t):
        if hasattr(self, 'tangent_delta'):
            h = self.tangent_delta
        else:
            h = 0.001
        v0 = self.evaluate(t - h)
        v1 = self.evaluate(t)
        v2 = self.evaluate(t + h)
        return (v2 - 2 * v1 + v0) / (h * h)

    def second_derivative_array(self, ts):
        h = 0.001
        v0s = self.evaluate_array(ts - h)
        v1s = self.evaluate_array(ts)
        v2s = self.evaluate_array(ts + h)
        return (v2s - 2 * v1s + v0s) / (h * h)

    def third_derivative_array(self, ts):
        h = 0.001
        v0s = self.evaluate_array(ts)
        v1s = self.evaluate_array(ts + h)
        v2s = self.evaluate_array(ts + 2 * h)
        v3s = self.evaluate_array(ts + 3 * h)
        return (-v0s + 3 * v1s - 3 * v2s + v3s) / (h * h * h)

    def derivatives_array(self, n, ts):
        result = []
        if n >= 1:
            first = self.tangent_array(ts)
            result.append(first)

        h = 0.001
        if n >= 2:
            minus_h = self.evaluate_array(ts - h)
            points = self.evaluate_array(ts)
            plus_h = self.evaluate_array(ts + h)
            second = (plus_h - 2 * points + minus_h) / (h * h)
            result.append(second)

        if n >= 3:
            v0s = points
            v1s = plus_h
            v2s = self.evaluate_array(ts + 2 * h)
            v3s = self.evaluate_array(ts + 3 * h)
            third = (-v0s + 3 * v1s - 3 * v2s + v3s) / (h * h * h)
            result.append(third)

        return result

    def main_normal(self, t, normalize=True):
        tangent = self.tangent(t)
        binormal = self.binormal(t, normalize)
        v = np.cross(binormal, tangent)
        if normalize:
            v = v / np.linalg.norm(v)
        return v

    def binormal(self, t, normalize=True):
        tangent = self.tangent(t)
        second = self.second_derivative(t)
        v = np.cross(tangent, second)
        if normalize:
            v = v / np.linalg.norm(v)
        return v

    def main_normal_array(self, ts, normalize=True):
        tangents = self.tangent_array(ts)
        binormals = self.binormal_array(ts, normalize)
        v = np.cross(binormals, tangents)
        if normalize:
            norms = np.linalg.norm(v, axis=1, keepdims=True)
            nonzero = (norms > 0)[:, 0]
            v[nonzero] = v[nonzero] / norms[nonzero][:, 0][np.newaxis].T
        return v

    def binormal_array(self, ts, normalize=True):
        tangents, seconds = self.derivatives_array(2, ts)
        v = np.cross(tangents, seconds)
        if normalize:
            norms = np.linalg.norm(v, axis=1, keepdims=True)
            nonzero = (norms > 0)[:, 0]
            v[nonzero] = v[nonzero] / norms[nonzero][:, 0][np.newaxis].T
        return v

    def tangent_normal_binormal_array(self, ts, normalize=True):
        tangents, seconds = self.derivatives_array(2, ts)
        binormals = np.cross(tangents, seconds)
        if normalize:
            norms = np.linalg.norm(binormals, axis=1, keepdims=True)
            nonzero = (norms > 0)[:, 0]
            binormals[nonzero] = binormals[nonzero] / norms[nonzero][:, 0][
                np.newaxis].T
        normals = np.cross(binormals, tangents)
        if normalize:
            norms = np.linalg.norm(normals, axis=1, keepdims=True)
            nonzero = (norms > 0)[:, 0]
            normals[nonzero] = normals[nonzero] / norms[nonzero][:, 0][
                np.newaxis].T
        return tangents, normals, binormals

    def frame_array(self, ts):
        tangents, normals, binormals = self.tangent_normal_binormal_array(ts)
        tangents = tangents / np.linalg.norm(tangents, axis=1)[np.newaxis].T
        matrices_np = np.dstack((normals, binormals, tangents))
        matrices_np = np.transpose(matrices_np, axes=(0, 2, 1))
        try:
            matrices_np = np.linalg.inv(matrices_np)
            return matrices_np, normals, binormals
        except np.linalg.LinAlgError as e:
            error("Some of matrices are singular:")
            for m in matrices_np:
                error("M:\n%s", m)
            raise e

    def curvature_array(self, ts):
        tangents, seconds = self.derivatives_array(2, ts)
        numerator = np.linalg.norm(np.cross(tangents, seconds), axis=1)
        tangents_norm = np.linalg.norm(tangents, axis=1)
        denominator = tangents_norm * tangents_norm * tangents_norm
        return numerator / denominator

    def torsion_array(self, ts):
        tangents, seconds, thirds = self.derivatives_array(3, ts)
        seconds_thirds = np.cross(seconds, thirds)
        numerator = (tangents * seconds_thirds).sum(axis=1)
        #numerator = np.apply_along_axis(lambda tangent: tangent.dot(seconds_thirds), 1, tangents)
        first_second = np.cross(tangents, seconds)
        denominator = np.linalg.norm(first_second, axis=1)
        return numerator / (denominator * denominator)

    def pre_calc_torsion_integral(self, resolution):
        t_min, t_max = self.get_u_bounds()
        ts = np.linspace(t_min, t_max, resolution)
        vectors = self.evaluate_array(ts)
        dvs = vectors[1:] - vectors[:-1]
        lengths = np.linalg.norm(dvs, axis=1)
        xs = np.insert(np.cumsum(lengths), 0, 0)
        ys = self.torsion_array(ts)
        self._torsion_integral = TrapezoidIntegral(ts, xs, ys)
        self._torsion_integral.calc()

    def torsion_integral(self, ts):
        return self._torsion_integral.evaluate_cubic(ts)

    def get_u_bounds(self):
        raise Exception("not implemented!")