예제 #1
0
    def torsion(self, t, above=True):
        """  Evaluate the torsion for a 3D curve at specified point(s). The torsion is defined as

        .. math:: \\frac{(\\boldsymbol{v}\\times \\boldsymbol{a})\\cdot (d\\boldsymbol{a}/dt)}{|\\boldsymbol{v}\\times \\boldsymbol{a}|^2}

        :param t: Parametric coordinates in which to evaluate
        :type t: float or [float]
        :param bool above: Evaluation in the limit from above
        :return: Derivative array
        :rtype: numpy.array
        """
        if self.dimension == 2:
            # no torsion for 2D curves
            t = ensure_listlike(t)
            return np.zeros(len(t))
        elif self.dimension == 3:
            # only allow 3D curves
            pass
        else:
            raise ValueError('dimension must be 2 or 3')

        # compute derivative
        v = self.derivative(t, d=1, above=above)
        a = self.derivative(t, d=2, above=above)
        da = self.derivative(t, d=3, above=above)
        w = np.cross(v, a)

        if len(v.shape) == 1:  # single evaluation point
            magnitude = np.linalg.norm(w)
            nominator = np.dot(w, a)
        else:  # multiple evaluation points
            magnitude = np.apply_along_axis(np.linalg.norm, -1, w)
            nominator = np.array([np.dot(w1, da1) for (w1, da1) in zip(w, da)])

        return nominator / np.power(magnitude, 2)
예제 #2
0
    def torsion(self, t, above=True):
        """  Evaluate the torsion for a 3D curve at specified point(s). The torsion is defined as

        .. math:: \\frac{(\\boldsymbol{v}\\times \\boldsymbol{a})\\cdot (d\\boldsymbol{a}/dt)}{|\\boldsymbol{v}\\times \\boldsymbol{a}|^2}

        :param t: Parametric coordinates in which to evaluate
        :type t: float or [float]
        :param bool above: Evaluation in the limit from above
        :return: Derivative array
        :rtype: numpy.array
        """
        if self.dimension == 2:
            # no torsion for 2D curves
            t = ensure_listlike(t)
            return np.zeros(len(t))
        elif self.dimension == 3:
            # only allow 3D curves
            pass
        else:
            raise ValueError('dimension must be 2 or 3')

        # compute derivative
        v  = self.derivative(t, d=1, above=above)
        a  = self.derivative(t, d=2, above=above)
        da = self.derivative(t, d=3, above=above)
        w = np.cross(v,a)

        if len(v.shape) == 1: # single evaluation point
            magnitude = np.linalg.norm(w)
            nominator = np.dot(w, a)
        else:                 # multiple evaluation points
            magnitude = np.apply_along_axis(np.linalg.norm, -1, w)
            nominator = np.array([np.dot(w1,da1) for (w1,da1) in zip(w, da)])

        return nominator / np.power(magnitude, 2)
예제 #3
0
    def evaluate(self, t, d=0, from_right=True, sparse=False):
        """  Evaluate all basis functions in a given set of points.

        :param t: The parametric coordinate(s) in which to evaluate
        :type t: float or [float]
        :param int d: Number of derivatives to compute
        :param bool from_right: True if evaluation should be done in the limit
            from above
        :param bool sparse: True if computed matrix should be returned as sparse
        :return: A matrix *N[i,j]* of all basis functions *j* evaluated in all
            points *i*
        :rtype: numpy.array
        """
        # for single-value input, wrap it into a list so it don't crash on the loop below
        t = ensure_listlike(t)
        t = np.array(t, dtype=np.float64)
        basis_eval.snap(self.knots, t, state.knot_tolerance)

        if self.order <= d:  # requesting more derivatives than polymoial degree: return all zeros
            return np.zeros((len(t), self.num_functions()))

        (data, size) = basis_eval.evaluate(self.knots, self.order, t,
                                           self.periodic, state.knot_tolerance,
                                           d, from_right)

        N = csr_matrix(data, size)
        if not sparse:
            N = N.toarray()
        return N
예제 #4
0
def subdivide(objs, n):
    """Subdivide a list of objects by splitting them up along existing knot
    lines. The resulting partition will roughly the same number of elements on
    all pieces. By splitting along *n* lines, we generate *n* + 1 new blocks.

    The number of subdivisions can be a list of integers: one for each
    direction, or a single integer for uniform sudivision.

    :param objs: Objects to split
    :param n: Number of subdivisions to perform
    :type n: int or [int]
    :return: New objects
    :rtype: [:class:`splipy.SplineObject`]
    """
    pardim = objs[0].pardim  # 1 for curves, 2 for surfaces, 3 for volumes
    n = ensure_listlike(n, pardim)

    result = objs
    for d in range(pardim):
        # split all objects so far along direction d
        new_results = []
        for obj in result:
            splitting_points = [
                obj.knots(d)[i]
                for i in _splitvector(len(obj.knots(d)), n[d] + 1)
            ]
            new_results += obj.split(splitting_points[1:], d)

        # only keep the smallest pieces in our result list
        result = new_results

    return result
예제 #5
0
def subdivide(objs, n):
    """Subdivide a list of objects by splitting them up along existing knot
    lines. The resulting partition will roughly the same number of elements on
    all pieces. By splitting along *n* lines, we generate *n* + 1 new blocks.

    The number of subdivisions can be a list of integers: one for each
    direction, or a single integer for uniform sudivision.

    :param objs: Objects to split
    :param n: Number of subdivisions to perform
    :type n: int or [int]
    :return: New objects
    :rtype: [:class:`splipy.SplineObject`]
    """
    pardim = objs[0].pardim # 1 for curves, 2 for surfaces, 3 for volumes
    n = ensure_listlike(n, pardim)

    result = objs
    for d in range(pardim):
        # split all objects so far along direction d
        new_results = []
        for obj in result:
            splitting_points = [obj.knots(d)[i] for i in _splitvector(len(obj.knots(d)), n[d]+1)]
            new_results += obj.split(splitting_points[1:], d)

        # only keep the smallest pieces in our result list
        result = new_results

    return result
예제 #6
0
파일: Volume.py 프로젝트: yukoba/Splipy
    def rebuild(self, p, n):
        """  Creates an approximation to this volume by resampling it using
        uniform knot vectors of order *p* with *n* control points.

        :param (int) p: Tuple of polynomial discretization order in each direction
        :param (int) n: Tuple of number of control points in each direction
        :return: A new approximate volume
        :rtype: Volume
        """
        p = ensure_listlike(p, dups=3)
        n = ensure_listlike(n, dups=3)

        old_basis = [self.bases[0], self.bases[1], self.bases[2]]
        basis = []
        u = []
        N = []
        # establish uniform open knot vectors
        for i in range(3):
            knot = [0] * p[i] + list(range(
                1, n[i] - p[i] + 1)) + [n[i] - p[i] + 1] * p[i]
            basis.append(BSplineBasis(p[i], knot))

            # make these span the same parametric domain as the old ones
            basis[i].normalize()
            t0 = old_basis[i].start()
            t1 = old_basis[i].end()
            basis[i] *= (t1 - t0)
            basis[i] += t0

            # fetch evaluation points and evaluate basis functions
            u.append(basis[i].greville())
            N.append(basis[i].evaluate(u[i]))

        # find interpolation points as evaluation of existing volume
        x = self.evaluate(u[0], u[1], u[2])

        # solve interpolation problem
        cp = np.tensordot(np.linalg.inv(N[2]), x, axes=(1, 2))
        cp = np.tensordot(np.linalg.inv(N[1]), cp, axes=(1, 2))
        cp = np.tensordot(np.linalg.inv(N[0]), cp, axes=(1, 2))

        # re-order controlpoints so they match up with Volume constructor
        cp = cp.transpose((2, 1, 0, 3))
        cp = cp.reshape(n[0] * n[1] * n[2], cp.shape[3])

        # return new resampled curve
        return Volume(basis[0], basis[1], basis[2], cp)
예제 #7
0
파일: Volume.py 프로젝트: sintefmath/Splipy
    def rebuild(self, p, n):
        """Creates an approximation to this volume by resampling it using
        uniform knot vectors of order *p* with *n* control points.

        :param int p: Polynomial discretization order
        :param int n: Number of control points
        :return: A new approximate volume
        :rtype: Volume
        """
        p = ensure_listlike(p, dups=3)
        n = ensure_listlike(n, dups=3)

        old_basis = [self.bases[0], self.bases[1], self.bases[2]]
        basis = []
        u = []
        N = []
        # establish uniform open knot vectors
        for i in range(3):
            knot = [0] * p[i] + list(range(1, n[i] - p[i] + 1)) + [n[i] - p[i] + 1] * p[i]
            basis.append(BSplineBasis(p[i], knot))

            # make these span the same parametric domain as the old ones
            basis[i].normalize()
            t0 = old_basis[i].start()
            t1 = old_basis[i].end()
            basis[i] *= (t1 - t0)
            basis[i] += t0

            # fetch evaluation points and evaluate basis functions
            u.append(basis[i].greville())
            N.append(basis[i].evaluate(u[i]))

        # find interpolation points as evaluation of existing volume
        x = self.evaluate(u[0], u[1], u[2])

        # solve interpolation problem
        cp = np.tensordot(np.linalg.inv(N[2]), x, axes=(1, 2))
        cp = np.tensordot(np.linalg.inv(N[1]), cp, axes=(1, 2))
        cp = np.tensordot(np.linalg.inv(N[0]), cp, axes=(1, 2))

        # re-order controlpoints so they match up with Volume constructor
        cp = cp.transpose((2, 1, 0, 3))
        cp = cp.reshape(n[0] * n[1] * n[2], cp.shape[3])

        # return new resampled curve
        return Volume(basis[0], basis[1], basis[2], cp)
예제 #8
0
    def derivative(self, t, d=1, above=True, tensor=True):
        """  Evaluate the derivative of the curve at the given parametric values.

        This function returns an *n* × *dim* array, where *n* is the number of
        evaluation points, and *dim* is the physical dimension of the curve.

        If there is only one evaluation point, a vector of length *dim* is
        returned instead.

        :param t: Parametric coordinates in which to evaluate
        :type t: float or [float]
        :param int d: Number of derivatives to compute
        :param bool above: Evaluation in the limit from above
        :param bool tensor: Not used in this method
        :return: Derivative array
        :rtype: numpy.array
        """
        if not is_singleton(d):
            d = d[0]
        if not self.rational or d < 2 or d > 3:
            return super(Curve, self).derivative(t,
                                                 d=d,
                                                 above=above,
                                                 tensor=tensor)

        t = ensure_listlike(t)
        result = np.zeros((len(t), self.dimension))

        d2 = np.array(self.bases[0].evaluate(t, 2, above) @ self.controlpoints)
        d1 = np.array(self.bases[0].evaluate(t, 1, above) @ self.controlpoints)
        d0 = np.array(self.bases[0].evaluate(t) @ self.controlpoints)
        W = d0[:, -1]  # W(t)
        W1 = d1[:, -1]  # W'(t)
        W2 = d2[:, -1]  # W''(t)
        if d == 2:
            for i in range(self.dimension):
                result[:, i] = (d2[:, i] * W * W - 2 * W1 *
                                (d1[:, i] * W - d0[:, i] * W1) -
                                d0[:, i] * W2 * W) / W / W / W
        if d == 3:
            d3 = np.array(
                self.bases[0].evaluate(t, 3, above) @ self.controlpoints)
            W3 = d3[:, -1]  # W'''(t)
            W6 = W * W * W * W * W * W  # W^6
            for i in range(self.dimension):
                H = d1[:, i] * W - d0[:, i] * W1
                H1 = d2[:, i] * W - d0[:, i] * W2
                H2 = d3[:, i] * W + d2[:, i] * W1 - d1[:, i] * W2 - d0[:,
                                                                       i] * W3
                G = H1 * W - 2 * H * W1
                G1 = H2 * W - 2 * H * W2 - H1 * W1
                result[:, i] = (G1 * W - 3 * G * W1) / W / W / W / W

        if result.shape[
                0] == 1:  # in case of single value input t, return vector instead of matrix
            result = np.array(result[0, :]).reshape(self.dimension)

        return result
예제 #9
0
    def write_surface(self, surface, n=None):
        # choose evaluation points as one of three cases:
        #   1. specified with input
        #   2. linear splines, only picks knots
        #   3. general splines choose 2*order-1 per knot span
        if n != None:
            n = ensure_listlike(n, 2)

        if n != None:
            u = np.linspace(surface.start(0), surface.end(0), n[0])
        elif surface.order(0) == 2:
            u = surface.knots(0)
        else:
            knots = surface.knots(0)
            p = surface.order(0)
            u = [
                np.linspace(k0, k1, 2 * p - 3, endpoint=False)
                for (k0, k1) in zip(knots[:-1], knots[1:])
            ]
            u = [point for element in u for point in element] + knots
            u = np.sort(u)

        if n != None:
            v = np.linspace(surface.start(1), surface.end(1), n[1])
        elif surface.order(1) == 2:
            v = surface.knots(1)
        else:
            knots = surface.knots(1)
            p = surface.order(1)
            v = [
                np.linspace(k0, k1, 2 * p - 3, endpoint=False)
                for (k0, k1) in zip(knots[:-1], knots[1:])
            ]
            v = [point for element in v for point in element] + knots
            v = np.sort(v)

        # perform evaluation and make sure that we have 3 components (in case of 2D geometries)
        x = surface(u, v)
        if x.shape[2] != 3:
            x.resize((x.shape[0], x.shape[1], 3))

        # compute tiny quad pieces
        faces = [[x[i, j], x[i, j + 1], x[i + 1, j + 1], x[i + 1, j]]
                 for i in range(x.shape[0] - 1) for j in range(x.shape[1] - 1)]

        self.writer.add_faces(faces)
예제 #10
0
    def derivative(self, t, d=1, above=True, tensor=True):
        """  Evaluate the derivative of the curve at the given parametric values.

        This function returns an *n* × *dim* array, where *n* is the number of
        evaluation points, and *dim* is the physical dimension of the curve.

        If there is only one evaluation point, a vector of length *dim* is
        returned instead.

        :param t: Parametric coordinates in which to evaluate
        :type t: float or [float]
        :param int d: Number of derivatives to compute
        :param bool above: Evaluation in the limit from above
        :param bool tensor: Not used in this method
        :return: Derivative array
        :rtype: numpy.array
        """
        if not self.rational or d != 2:
            return super(Curve, self).derivative(t,
                                                 d=d,
                                                 above=above,
                                                 tensor=tensor)

        t = ensure_listlike(t)
        dN = self.bases[0].evaluate(t, d, above)
        result = np.array(dN * self.controlpoints)

        d2 = result
        d1 = np.array(self.bases[0].evaluate(t, 1, above) * self.controlpoints)
        d0 = np.array(self.bases[0].evaluate(t) * self.controlpoints)
        W = d0[:, -1]  # W(t)
        W1 = d1[:, -1]  # W'(t)
        W2 = d2[:, -1]  # W''(t)
        for i in range(self.dimension):
            result[:, i] = (d2[:, i] * W * W - 2 * W1 *
                            (d1[:, i] * W - d0[:, i] * W1) -
                            d0[:, i] * W2 * W) / W / W / W

        result = np.delete(result, self.dimension,
                           1)  # remove the weight column

        if result.shape[
                0] == 1:  # in case of single value input t, return vector instead of matrix
            result = np.array(result[0, :]).reshape(self.dimension)

        return result
예제 #11
0
파일: Curve.py 프로젝트: sintefmath/Splipy
    def derivative(self, t, d=1, above=True):
        """derivative(u, [d=1])

        Evaluate the derivative of the curve at the given parametric values.

        This function returns an *n* × *dim* array, where *n* is the number of
        evaluation points, and *dim* is the physical dimension of the curve.

        If there is only one evaluation point, a vector of length *dim* is
        returned instead.

        :param u: Parametric coordinates in which to evaluate
        :type u: float or [float]
        :param int d: Number of derivatives to compute
        :param bool above: Evaluation in the limit from above
        :return: Derivative array
        :rtype: numpy.array
        """
        if not self.rational or d != 2:
            return super(Curve, self).derivative(t, d=d, above=above)

        t = ensure_listlike(t)
        dN = self.bases[0].evaluate(t, d, above)
        result = np.array(dN * self.controlpoints)

        d2 = result
        d1 = np.array(self.bases[0].evaluate(t, 1, above) * self.controlpoints)
        d0 = np.array(self.bases[0].evaluate(t) * self.controlpoints)
        W = d0[:, -1]  # W(t)
        W1 = d1[:, -1]  # W'(t)
        W2 = d2[:, -1]  # W''(t)
        for i in range(self.dimension):
            result[:, i] = (d2[:, i] * W * W - 2 * W1 * (d1[:, i] * W - d0[:, i] * W1) - d0[:, i] * W2 * W) / W / W / W

        result = np.delete(result, self.dimension, 1)  # remove the weight column

        if result.shape[0] == 1:  # in case of single value input t, return vector instead of matrix
            result = np.array(result[0, :]).reshape(self.dimension)

        return result
예제 #12
0
파일: stl.py 프로젝트: sintefmath/Splipy
    def write_surface(self, surface, n=None):
        # choose evaluation points as one of three cases:
        #   1. specified with input
        #   2. linear splines, only picks knots
        #   3. general splines choose 2*order-1 per knot span
        if n != None:
            n = ensure_listlike(n,2)

        if n != None:
            u = np.linspace(surface.start(0), surface.end(0), n[0])
        elif surface.order(0) == 2:
            u = surface.knots(0)
        else:
            knots = surface.knots(0)
            p = surface.order(0)
            u = [np.linspace(k0,k1, 2*p-3, endpoint=False) for (k0,k1) in zip(knots[:-1], knots[1:])]
            u = [point for element in u for point in element] + knots
            u = np.sort(u)

        if n != None:
            v = np.linspace(surface.start(1), surface.end(1), n[1])
        elif surface.order(1) == 2:
            v = surface.knots(1)
        else:
            knots = surface.knots(1)
            p = surface.order(1)
            v = [np.linspace(k0,k1, 2*p-3, endpoint=False) for (k0,k1) in zip(knots[:-1], knots[1:])]
            v = [point for element in v for point in element] + knots
            v = np.sort(v)

        # perform evaluation and make sure that we have 3 components (in case of 2D geometries)
        x = surface(u,v)
        if x.shape[2] != 3:
            x.resize((x.shape[0],x.shape[1],3))

        # compute tiny quad pieces 
        faces = [[x[i,j], x[i,j+1], x[i+1,j+1], x[i+1,j]] for i in range(x.shape[0]-1) for j in range(x.shape[1]-1)]

        self.writer.add_faces(faces)
예제 #13
0
파일: Curve.py 프로젝트: sintefmath/Splipy
    def evaluate(self, *params):
        """evaluate(u, v, ...)

        Evaluate the object at given parametric values.

        This function returns an *n1* × *n2* × ... × *dim* array, where *ni* is
        the number of evaluation points in direction *i*, and *dim* is the
        physical dimension of the object.

        If there is only one evaluation point, a vector of length *dim* is
        returned instead.

        :param u,v,...: Parametric coordinates in which to evaluate
        :type u,v,...: float or [float]
        :return: Geometry coordinates
        :rtype: numpy.array
        """
        squeeze = is_singleton(params[0])
        params = [ensure_listlike(p) for p in params]

        self._validate_domain(*params)

        # Evaluate the derivatives of the corresponding bases at the corresponding points
        # and build the result array
        N = self.bases[0].evaluate(params[0], sparse=True)
        result = N * self.controlpoints

        # For rational objects, we divide out the weights, which are stored in the
        # last coordinate
        if self.rational:
            for i in range(self.dimension):
                result[..., i] /= result[..., -1]
            result = np.delete(result, self.dimension, -1)

        # Squeeze the singleton dimensions if we only have one point
        if squeeze:
            result = result.reshape(self.dimension)

        return result
예제 #14
0
    def evaluate(self, *params):
        """  Evaluate the object at given parametric values.

        This function returns an *n1* × *n2* × ... × *dim* array, where *ni* is
        the number of evaluation points in direction *i*, and *dim* is the
        physical dimension of the object.

        If there is only one evaluation point, a vector of length *dim* is
        returned instead.

        :param u,v,...: Parametric coordinates in which to evaluate
        :type u,v,...: float or [float]
        :return: Geometry coordinates
        :rtype: numpy.array
        """
        squeeze = is_singleton(params[0])
        params = [ensure_listlike(p) for p in params]

        self._validate_domain(*params)

        # Evaluate the derivatives of the corresponding bases at the corresponding points
        # and build the result array
        N = self.bases[0].evaluate(params[0], sparse=True)
        result = N @ self.controlpoints

        # For rational objects, we divide out the weights, which are stored in the
        # last coordinate
        if self.rational:
            for i in range(self.dimension):
                result[..., i] /= result[..., -1]
            result = np.delete(result, self.dimension, -1)

        # Squeeze the singleton dimensions if we only have one point
        if squeeze:
            result = result.reshape(self.dimension)

        return result
예제 #15
0
    def evaluate_old(self, t, d=0, from_right=True, sparse=False):
        """  Evaluate all basis functions in a given set of points.
        :param t: The parametric coordinate(s) in which to evaluate
        :type t: float or [float]
        :param int d: Number of derivatives to compute
        :param bool from_right: True if evaluation should be done in the limit
            from above
        :param bool sparse: True if computed matrix should be returned as sparse
        :return: A matrix *N[i,j]* of all basis functions *j* evaluated in all
            points *i*
        :rtype: numpy.array
        """
        # for single-value input, wrap it into a list so it don't crash on the loop below
        t = ensure_listlike(t)
        self.snap(t)

        p = self.order  # knot vector order
        n_all = len(
            self.knots) - p  # number of basis functions (without periodicity)
        n = len(self.knots) - p - (
            self.periodic + 1)  # number of basis functions (with periodicity)
        m = len(t)
        data = np.zeros(m * p)
        indices = np.zeros(m * p, dtype='int32')
        indptr = np.array(range(0, m * p + 1, p), dtype='int32')
        if p <= d:  # requesting more derivatives than polymoial degree: return all zeros
            return np.zeros((m, n))
        if self.periodic >= 0:
            t = copy.deepcopy(t)
            # Wrap periodic evaluation into domain
            for i in range(len(t)):
                if t[i] < self.start() or t[i] > self.end():
                    t[i] = (t[i] - self.start()) % (
                        self.end() - self.start()) + self.start()
        for i in range(len(t)):
            right = from_right
            evalT = t[i]
            # Special-case the endpoint, so the user doesn't need to
            if abs(t[i] - self.end()) < state.knot_tolerance:
                right = False
            # Skip non-periodic evaluation points outside the domain
            if t[i] < self.start() or t[i] > self.end():
                continue

            # mu = index of last non-zero basis function
            if right:
                mu = bisect_right(self.knots, evalT)
            else:
                mu = bisect_left(self.knots, evalT)
            mu = min(mu, n_all)

            M = np.zeros(
                p)  # temp storage to keep all the function evaluations
            M[-1] = 1  # the last entry is a dummy-zero which is never used
            for q in range(1, p - d):
                for j in range(p - q - 1, p):
                    k = mu - p + j  # 'i'-index in global knot vector (ref Hughes book pg.21)
                    if j != p - q - 1:
                        M[j] = M[j] * float(evalT - self.knots[k]) / (
                            self.knots[k + q] - self.knots[k])

                    if j != p - 1:
                        M[j] = M[j] + M[j + 1] * float(
                            self.knots[k + q + 1] - evalT) / (
                                self.knots[k + q + 1] - self.knots[k + 1])

            for q in range(p - d, p):
                for j in range(p - q - 1, p):
                    k = mu - p + j  # 'i'-index in global knot vector (ref Hughes book pg.21)
                    if j != p - q - 1:
                        M[j] = M[j] * float(q) / (self.knots[k + q] -
                                                  self.knots[k])
                    if j != p - 1:
                        M[j] = M[j] - M[j + 1] * float(q) / (
                            self.knots[k + q + 1] - self.knots[k + 1])

            data[i * p:(i + 1) * p] = M
            indices[i * p:(i + 1) * p] = np.arange(mu - p, mu) % n

        N = csr_matrix((data, indices, indptr), (m, n))
        if not sparse:
            N = N.toarray()
        return N
예제 #16
0
파일: grdecl.py 프로젝트: opnumten/Splipy
    def texture(self, p, ngeom, ntexture, method='full', irange=[None,None], jrange=[None,None]):
        # Set the dimensions of geometry and texture map
        # ngeom    = np.floor(self.n / (p-1))
        # ntexture = np.floor(self.n * n)
        # ngeom    = ngeom.astype(np.int32)
        # ntexture = ntexture.astype(np.int32)
        ngeom    = ensure_listlike(ngeom, 3)
        ntexture = ensure_listlike(ntexture, 3)
        p        = ensure_listlike(p, 3)


        # Create the geometry
        ngx, ngy, ngz = ngeom
        b1 = BSplineBasis(p[0], [0]*(p[0]-1) + [i/ngx for i in range(ngx+1)] + [1]*(p[0]-1))
        b2 = BSplineBasis(p[1], [0]*(p[1]-1) + [i/ngy for i in range(ngy+1)] + [1]*(p[1]-1))
        b3 = BSplineBasis(p[2], [0]*(p[2]-1) + [i/ngz for i in range(ngz+1)] + [1]*(p[2]-1))

        l2_fit = surface_factory.least_square_fit
        vol = self.get_c0_mesh()

        i = slice(irange[0], irange[1], None)
        j = slice(jrange[0], jrange[1], None)
        # special case number of evaluation points for full domain
        if irange[1] == None: irange[1] = vol.shape[0]
        if jrange[1] == None: jrange[1] = vol.shape[1]
        if irange[0] == None: irange[0] = 0
        if jrange[0] == None: jrange[0] = 0

        nu = np.diff(irange)
        nv = np.diff(jrange)
        nw = vol.shape[2]

        u = np.linspace(0, 1, nu)
        v = np.linspace(0, 1, nv)
        w = np.linspace(0, 1, nw)

        crvs = []
        crvs.append(curve_factory.polygon(vol[i          ,jrange[0]  , 0,:].squeeze()))
        crvs.append(curve_factory.polygon(vol[i          ,jrange[0]  ,-1,:].squeeze()))
        crvs.append(curve_factory.polygon(vol[i          ,jrange[1]-1, 0,:].squeeze()))
        crvs.append(curve_factory.polygon(vol[i          ,jrange[1]-1,-1,:].squeeze()))
        crvs.append(curve_factory.polygon(vol[irange[0]  ,j          , 0,:].squeeze()))
        crvs.append(curve_factory.polygon(vol[irange[0]  ,j          ,-1,:].squeeze()))
        crvs.append(curve_factory.polygon(vol[irange[1]-1,j          , 0,:].squeeze()))
        crvs.append(curve_factory.polygon(vol[irange[1]-1,j          ,-1,:].squeeze()))
        crvs.append(curve_factory.polygon(vol[irange[0]  ,jrange[0]  , :,:].squeeze()))
        crvs.append(curve_factory.polygon(vol[irange[0]  ,jrange[1]-1, :,:].squeeze()))
        crvs.append(curve_factory.polygon(vol[irange[1]-1,jrange[0]  , :,:].squeeze()))
        crvs.append(curve_factory.polygon(vol[irange[1]-1,jrange[1]-1, :,:].squeeze()))
#        with G2('curves.g2') as myfile:
#            myfile.write(crvs)
#        print('Written curve.g2')


        if method == 'full':
            bottom = l2_fit(vol[i,          j,          0,:].squeeze(), [b1, b2], [u, v])
            top    = l2_fit(vol[i,          j,         -1,:].squeeze(), [b1, b2], [u, v])
            left   = l2_fit(vol[irange[0]  ,j,          :,:].squeeze(), [b2, b3], [v, w])
            right  = l2_fit(vol[irange[1]-1,j,          :,:].squeeze(), [b2, b3], [v, w])
            front  = l2_fit(vol[i,          jrange[0],  :,:].squeeze(), [b1, b3], [u, w])
            back   = l2_fit(vol[i,          jrange[1]-1,:,:].squeeze(), [b1, b3], [u, w])
            volume = volume_factory.edge_surfaces([left, right, front, back, bottom, top])

        elif method == 'z':
            bottom = l2_fit(vol[i,j, 0,:].squeeze(), [b1, b2], [u, v])
            top    = l2_fit(vol[i,j,-1,:].squeeze(), [b1, b2], [u, v])
            volume = volume_factory.edge_surfaces([bottom, top])
            volume.set_order(*p)
            volume.refine(ngz - 1, direction='w')

        volume.reverse(direction=2)

        # Point-to-cell mapping
        # TODO: Optimize more
        eps = 1e-2
        u = [np.linspace(eps, 1-eps, n) for n in ntexture]
        points = volume(*u).reshape(-1, 3)
        cellids = np.zeros(points.shape[:-1], dtype=int)
        cell = None
        nx, ny, nz = self.n
        for ptid, point in enumerate(tqdm(points, desc='Inverse mapping')):
            i, j, k = cell = self.raw.cell_at(point) # , guess=cell)
            cellid = i*ny*nz + j*nz + k
            cellids[ptid] = cellid

        cellids = cellids.reshape(tuple(ntexture))

        all_textures = {}
        for name in self.attribute:
            data = self.attribute[name][cellids]

            # TODO: This flattens the image if it happens to be 3D (or higher...)
            # do we need a way to communicate the structure back to the caller?
            # data = data.reshape(-1, data.shape[-1])

            # TODO: This normalizes the image,
            # but we need a way to communicate the ranges back to the caller
            # a, b = min(data.flat), max(data.flat)
            # data = ((data - a) / (b - a) * 255).astype(np.uint8)

            all_textures[name] = data
        all_textures['cellids'] = cellids

        return volume, all_textures
예제 #17
0
    def derivative(self, u, v, d=(1, 1), above=True, tensor=True):
        """  Evaluate the derivative of the surface at the given parametric values.

        This function returns an *n* × *m* x *dim* array, where *n* is the number of
        evaluation points in u, *m* is the number of evaluation points in v, and
        *dim* is the physical dimension of the curve.

        If there is only one evaluation point, a vector of length *dim* is
        returned instead.

        :param u: Parametric coordinate(s) in the first direction
        :type u: float or [float]
        :param v: Parametric coordinate(s) in the second direction
        :type v: float or [float]
        :param int d: Number of derivatives to compute in each direction
        :type d: [int]
        :param (bool) above: Evaluation in the limit from above
        :param tensor: Whether to evaluate on a tensor product grid
        :type tensor: bool
        :return: Derivative array *X[i,j,k]* of component *xk* evaluated at *(u[i], v[j])*
        :rtype: numpy.array
        """

        squeeze = all(is_singleton(t) for t in [u, v])
        derivs = ensure_listlike(d, self.pardim)
        if not self.rational or np.sum(derivs) < 2 or np.sum(derivs) > 3:
            return super(Surface, self).derivative(u,
                                                   v,
                                                   d=derivs,
                                                   above=above,
                                                   tensor=tensor)

        u = ensure_listlike(u)
        v = ensure_listlike(v)
        result = np.zeros((len(u), len(v), self.dimension))
        # dNus = [self.bases[0].evaluate(u, d, above) for d in range(derivs[0]+1)]
        # dNvs = [self.bases[1].evaluate(v, d, above) for d in range(derivs[1]+1)]
        dNus = [
            self.bases[0].evaluate(u, d, above)
            for d in range(np.sum(derivs) + 1)
        ]
        dNvs = [
            self.bases[1].evaluate(v, d, above)
            for d in range(np.sum(derivs) + 1)
        ]

        d0ud0v = evaluate([dNus[0], dNvs[0]], self.controlpoints, tensor)
        d1ud0v = evaluate([dNus[1], dNvs[0]], self.controlpoints, tensor)
        d0ud1v = evaluate([dNus[0], dNvs[1]], self.controlpoints, tensor)
        d1ud1v = evaluate([dNus[1], dNvs[1]], self.controlpoints, tensor)
        d2ud0v = evaluate([dNus[2], dNvs[0]], self.controlpoints, tensor)
        d0ud2v = evaluate([dNus[0], dNvs[2]], self.controlpoints, tensor)
        W = d0ud0v[:, :, -1]
        dWdu = d1ud0v[:, :, -1]
        dWdv = d0ud1v[:, :, -1]
        d2Wduv = d1ud1v[:, :, -1]
        d2Wdu = d2ud0v[:, :, -1]
        d2Wdv = d0ud2v[:, :, -1]

        for i in range(self.dimension):
            H1 = d1ud0v[:, :, i] * W - d0ud0v[:, :, i] * dWdu
            H2 = d0ud1v[:, :, i] * W - d0ud0v[:, :, i] * dWdv
            dH1du = d2ud0v[:, :, i] * W - d0ud0v[:, :, i] * d2Wdu
            dH1dv = d1ud1v[:, :,
                           i] * W + d1ud0v[:, :,
                                           i] * dWdv - d0ud1v[:, :,
                                                              i] * dWdu - d0ud0v[:, :,
                                                                                 i] * d2Wduv
            dH2du = d1ud1v[:, :,
                           i] * W + d0ud1v[:, :,
                                           i] * dWdu - d1ud0v[:, :,
                                                              i] * dWdv - d0ud0v[:, :,
                                                                                 i] * d2Wduv
            dH2dv = d0ud2v[:, :, i] * W - d0ud0v[:, :, i] * d2Wdv
            G1 = dH1du * W - 2 * H1 * dWdu
            G2 = dH2dv * W - 2 * H2 * dWdv
            if derivs == (1, 0):
                result[:, :, i] = H1 / W / W
            elif derivs == (0, 1):
                result[:, :, i] = H2 / W / W
            elif derivs == (1, 1):
                result[:, :, i] = (dH1dv * W - 2 * H1 * dWdv) / W / W / W
            elif derivs == (2, 0):
                result[:, :, i] = G1 / W / W / W
            elif derivs == (0, 2):
                result[:, :, i] = G2 / W / W / W
            if np.sum(derivs) > 2:
                d2ud1v = evaluate([dNus[2], dNvs[1]], self.controlpoints,
                                  tensor)
                d1ud2v = evaluate([dNus[1], dNvs[2]], self.controlpoints,
                                  tensor)
                d3ud0v = evaluate([dNus[3], dNvs[0]], self.controlpoints,
                                  tensor)
                d0ud3v = evaluate([dNus[0], dNvs[3]], self.controlpoints,
                                  tensor)
                d3Wdu = d3ud0v[:, :, -1]
                d3Wdv = d0ud3v[:, :, -1]
                d3Wduuv = d2ud1v[:, :, -1]
                d3Wduvv = d1ud2v[:, :, -1]
                d2H1du = d3ud0v[:, :,
                                i] * W + d2ud0v[:, :,
                                                i] * dWdu - d1ud0v[:, :,
                                                                   i] * d2Wdu - d0ud0v[:, :,
                                                                                       i] * d3Wdu
                d2H1duv = d2ud1v[:, :,
                                 i] * W + d2ud0v[:, :,
                                                 i] * dWdv - d0ud1v[:, :,
                                                                    i] * d2Wdu - d0ud0v[:, :,
                                                                                        i] * d3Wduuv
                d2H2dv = d0ud3v[:, :,
                                i] * W + d0ud2v[:, :,
                                                i] * dWdv - d0ud1v[:, :,
                                                                   i] * d2Wdv - d0ud0v[:, :,
                                                                                       i] * d3Wdv
                d2H2duv = d1ud2v[:, :,
                                 i] * W + d0ud2v[:, :,
                                                 i] * dWdu - d1ud0v[:, :,
                                                                    i] * d2Wdv - d0ud0v[:, :,
                                                                                        i] * d3Wduvv
                dG1du = d2H1du * W + dH1du * dWdu - 2 * dH1du * dWdu - 2 * H1 * d2Wdu
                dG1dv = d2H1duv * W + dH1du * dWdv - 2 * dH1dv * dWdu - 2 * H1 * d2Wduv
                dG2du = d2H2duv * W + dH2dv * dWdu - 2 * dH2du * dWdv - 2 * H2 * d2Wduv
                dG2dv = d2H2dv * W + dH2dv * dWdv - 2 * dH2dv * dWdv - 2 * H2 * d2Wdv

                if derivs == (3, 0):
                    result[:, :,
                           i] = (dG1du * W - 3 * G1 * dWdu) / W / W / W / W
                elif derivs == (0, 3):
                    result[:, :,
                           i] = (dG2dv * W - 3 * G2 * dWdv) / W / W / W / W
                elif derivs == (2, 1):
                    result[:, :,
                           i] = (dG1dv * W - 3 * G1 * dWdv) / W / W / W / W
                elif derivs == (1, 2):
                    result[:, :,
                           i] = (dG2du * W - 3 * G2 * dWdu) / W / W / W / W

        # Squeeze the singleton dimensions if we only have one point
        if squeeze:
            result = result.reshape(self.dimension)

        return result
예제 #18
0
    def evaluate(self, t, d=0, from_right=True, sparse=False):
        """evaluate(t, [d=0], [from_right=True])

        Evaluate all basis functions in a given set of points.

        :param t: The parametric coordinate(s) in which to evaluate
        :type t: float or [float]
        :param int d: Number of derivatives to compute
        :param bool from_right: True if evaluation should be done in the limit
            from above
        :param bool sparse: True if computed matrix should be returned as sparse
        :return: A matrix *N[i,j]* of all basis functions *j* evaluated in all
            points *i*
        :rtype: numpy.array
        """
        # for single-value input, wrap it into a list so it don't crash on the loop below
        t = ensure_listlike(t)

        p = self.order  # knot vector order
        n_all = len(self.knots) - p  # number of basis functions (without periodicity)
        n = len(self.knots) - p - (self.periodic+1)  # number of basis functions (with periodicity)
        m = len(t)
        data    = np.zeros(m*p)
        indices = np.zeros(m*p)
        indptr  = range(0,m*p+1,p)
        if p <= d: # requesting more derivatives than polymoial degree: return all zeros
            return np.matrix(np.zeros((m,n)))
        if self.periodic >= 0:
            t = copy.deepcopy(t)
            # Wrap periodic evaluation into domain
            for i in range(len(t)):
                if t[i] < self.start() or t[i] > self.end():
                    t[i] = (t[i] - self.start()) % (self.end() - self.start()) + self.start()
        for i in range(len(t)):
            right = from_right
            evalT = t[i]
            # Special-case the endpoint, so the user doesn't need to
            if abs(t[i] - self.end()) < state.knot_tolerance:
                right = False
            # Skip non-periodic evaluation points outside the domain
            if t[i] < self.start() or t[i] > self.end():
                continue

            # mu = index of last non-zero basis function
            if right:
                mu = bisect_right(self.knots, evalT)
            else:
                mu = bisect_left(self.knots, evalT)
            mu = min(mu, n_all)

            M = np.zeros(p)  # temp storage to keep all the function evaluations
            M[-1] = 1  # the last entry is a dummy-zero which is never used
            for q in range(1, p-d):
                for j in range(p - q - 1, p):
                    k = mu - p + j  # 'i'-index in global knot vector (ref Hughes book pg.21)
                    if j != p-q-1:
                        M[j] = M[j] * float(evalT - self.knots[k]) / (self.knots[k + q] - self.knots[k])
                                    
                    if j != p-1:
                        M[j] = M[j] + M[j + 1] * float(self.knots[k + q + 1] - evalT) / (self.knots[k + q + 1] - self.knots[k + 1])
                                            
                                    
            for q in range(p-d, p):
                for j in range(p - q - 1, p):
                    k = mu - p + j  # 'i'-index in global knot vector (ref Hughes book pg.21)
                    if j != p-q-1:
                        M[j] = M[j] * float(q) / (self.knots[k + q] - self.knots[k])
                    if j != p-1:
                        M[j] = M[j] - M[j + 1] * float(q) / (self.knots[k + q + 1] - self.knots[k + 1])
                                                             

            data[i*p:(i+1)*p]    = M
            indices[i*p:(i+1)*p] = np.arange(mu-p, mu) % n

        N = csr_matrix((data, indices, indptr), (m,n))
        if not sparse:
            N = N.todense()
        return N