Exemple #1
0
def bilinear_surface(vtxs, overhang=0.0):
    """
    Returns B-spline surface of a bilinear surface given by 4 corner points:
    uv coords:
    We retun also list of UV coordinates of the given points.
    :param vtxs: List of tuples (X,Y,Z)
    :return: ( Surface, vtxs_uv )
    """
    assert len(vtxs) == 4, "n vtx: {}".format(len(vtxs))
    vtxs = np.array(vtxs)
    if overhang > 0.0:
        dv = np.roll(vtxs, -1, axis=0) - vtxs
        dv *= overhang
        vtxs += np.roll(dv, 1, axis=0) - dv

    def mid(*idx):
        return np.mean(vtxs[list(idx)], axis=0)

    # v - direction v0 -> v2
    # u - direction v0 -> v1
    poles = [[vtxs[0], mid(0, 3), vtxs[3]],
             [mid(0, 1), mid(0, 1, 2, 3),
              mid(2, 3)], [vtxs[1], mid(1, 2), vtxs[2]]]
    knots = 3 * [0.0 - overhang] + 3 * [1.0 + overhang]
    basis = bs.SplineBasis(2, knots)
    surface = bs.Surface((basis, basis), poles)
    #vtxs_uv = [ (0, 0), (1, 0), (1, 1), (0, 1) ]
    return surface
Exemple #2
0
    def test_aabb(self):
        # function surface
        def function(x):
            return x[0] * (x[1] + 1.0) + 3.0

        poles = bs.make_function_grid(function, 4, 5)
        u_basis = bs.SplineBasis.make_equidistant(2, 2)
        v_basis = bs.SplineBasis.make_equidistant(2, 3)
        surface_func = bs.Surface((u_basis, v_basis), np.array(poles))
        box = surface_func.aabb()
        assert np.allclose(box, np.array([[0, 0, 3], [1, 1, 5]]))
Exemple #3
0
    def plot_extrude(self):

        # curve extruded to surface
        poles_yz = [[0., 0.], [1.0, 0.5], [2., -2.], [3., 1.]]
        poles_x = [0, 1, 2]
        poles = [[[x] + yz for yz in poles_yz] for x in poles_x]
        u_basis = bs.SplineBasis.make_equidistant(2, 1)
        v_basis = bs.SplineBasis.make_equidistant(2, 2)
        surface_extrude = bs.Surface((u_basis, v_basis), poles)
        plotting.plot_surface_3d(surface_extrude, poles=True)
        plotting.show()
Exemple #4
0
    def plot_function(self):
        fig = plt.figure()
        ax = fig.gca(projection='3d')

        # function surface
        def function(x):
            return math.sin(x[0]) * math.cos(x[1])

        poles = bs.make_function_grid(function, 4, 5)
        u_basis = bs.SplineBasis.make_equidistant(2, 2)
        v_basis = bs.SplineBasis.make_equidistant(2, 3)
        surface_func = bs.Surface((u_basis, v_basis), poles)
        plotting.plot_surface_3d(surface_func, poles=True)
        plotting.show()
Exemple #5
0
    def plotting_3d(self, plotting):
        # plotting 3d surfaces
        def function(x):
            return math.sin(x[0]*4) * math.cos(x[1]*4)

        poles = bs.make_function_grid(function, 4, 5)
        u_basis = bs.SplineBasis.make_equidistant(2, 2)
        v_basis = bs.SplineBasis.make_equidistant(2, 3)
        surface_func = bs.Surface( (u_basis, v_basis), poles[:,:, [2] ])

        #quad = np.array( [ [0, 0], [0, 0.5], [1, 0.1],  [1.1, 1.1] ]  )
        quad = np.array([[0, 0], [1, 0], [1, 1], [0, 1]])
        z_surf = bs.Z_Surface(quad, surface_func)
        full_surf = z_surf.make_full_surface()
        z_surf.transform(np.array([[1., 0, 0], [0, 1, 0]]), np.array([2.0, 0]) )
        plotting.plot_surface_3d(z_surf)
        plotting.plot_surface_3d(full_surf)

        plotting.show()
Exemple #6
0
    def compute_approximation(self, **kwargs):
        """
        Compute approximation of the point set (given to constructor).
        Approximation parameters can be passed in through kwargs or set in the object before the call.
        :param quad: [(x1,y1), .. , (x4,y4)] Set vertices of different quad for the point set.
        :param nuv: (nu, nv) Set number of intervals of the resulting B-spline, in U and V direction
        :param regularization_wight: Default 0.001, is scaled by the max singular value of B.
        :return: B-Spline surface
        """

        self.quad = kwargs.get("quad", self.quad)
        self.nuv = kwargs.get("nuv", self.nuv)
        self.regularization_weight = kwargs.get("regularization_weight",
                                                self.regularization_weight)

        logging.info('Transforming points (n={}) ...'.format(self._n_points))
        start_time = time.time()
        if self.quad is None:
            self.compute_default_quad()
        if self.nuv is None:
            self.compute_default_nuv()

        # TODO: better logic, since this has to be recomputed only if quad is changed.
        self._compute_uv_points()

        logging.info("Using {} x {} B-spline approximation.".format(
            self.nuv[0], self.nuv[1]))
        self._u_basis = bs.SplineBasis.make_equidistant(2, self.nuv[0])
        self._v_basis = bs.SplineBasis.make_equidistant(2, self.nuv[1])

        end_time = time.time()
        logging.info('Computed in: {} s'.format(end_time - start_time))

        # Approximation itself
        logging.info(
            'Creating explicitly system of normal equations B^TBz=B^Tb ...')
        start_time = time.time()
        btb_mat, btwb_vec, point_loc = self._build_system_of_normal_equations()
        end_time = time.time()
        logging.info('Computed in: {} s'.format(end_time - start_time))

        logging.info('Creating A matrix ...')
        start_time = time.time()
        a_mat = self._build_sparse_reg_matrix()
        end_time = time.time()
        logging.info('Computed in: {} s'.format(end_time - start_time))

        logging.info('Computing A and B^TB svds approximation ...')
        start_time = time.time()
        bb_norm = scipy.sparse.linalg.eigsh(btb_mat,
                                            k=1,
                                            ncv=10,
                                            tol=1e-2,
                                            which='LM',
                                            maxiter=300,
                                            return_eigenvectors=False)
        a_norm = scipy.sparse.linalg.eigsh(a_mat,
                                           k=1,
                                           ncv=10,
                                           tol=1e-2,
                                           which='LM',
                                           maxiter=300,
                                           return_eigenvectors=False)
        a_min = scipy.sparse.linalg.eigsh(a_mat,
                                          k=1,
                                          ncv=10,
                                          tol=1e-2,
                                          which='SM',
                                          maxiter=300,
                                          return_eigenvectors=False)
        #c_mat = bb_mat + self.regularization_weight * (bb_norm[0] / a_norm[0]) * a_mat
        c_mat = btb_mat + self.regularization_weight * (bb_norm[0] * a_min[0] /
                                                        a_norm[0]) * a_mat
        end_time = time.time()
        logging.info('Computed in: {} s'.format(end_time - start_time))

        logging.info('Solving for Z coordinates ...')
        start_time = time.time()
        z_vec = scipy.sparse.linalg.spsolve(c_mat, btwb_vec)
        assert not np.isnan(
            np.sum(z_vec)), "Singular matrix for approximation."
        end_time = time.time()
        logging.info('Computed in: {} s'.format(end_time - start_time))

        logging.info('Computing error ...')
        start_time = time.time()

        diff = self._compute_errors(point_loc, z_vec)

        self.error = max_diff = np.max(diff)
        logging.info("Approximation error (max norm): {}".format(max_diff))
        end_time = time.time()
        logging.info('Computed in: {} s'.format(end_time - start_time))

        # Construct Z-Surface
        poles_z = z_vec.reshape(self._v_basis.size, self._u_basis.size).T
        #poles_z *= self.grid_surf.z_scale
        #poles_z += self.grid_surf.z_shift
        surface_z = bs.Surface((self._u_basis, self._v_basis), poles_z[:, :,
                                                                       None])
        self.surface = bs.Z_Surface(self.quad[0:3], surface_z)

        return self.surface
Exemple #7
0
 def make_z_surf(self, func, quad):
     poles = bs.make_function_grid(func, 4, 5)
     u_basis = bs.SplineBasis.make_equidistant(2, 2)
     v_basis = bs.SplineBasis.make_equidistant(2, 3)
     surface_func = bs.Surface((u_basis, v_basis), poles[:, :, [2]])
     return bs.Z_Surface(quad, surface_func)
Exemple #8
0
    def plot_extrude(self):
        #fig1 = plt.figure()

        #ax1 = fig1.gca(projection='3d')



        def function(x):
            return math.sin(x[0]*4) * math.cos(x[1] * 4)

        def function2(x):
            return math.cos(x[0]*4) * math.sin(x[1] * 4)

        def function3(x):
            return (-x[0] + x[1] + 4 + 3 + math.cos(3 * x[0]))

        def function4(x):
            return (2 * x[0] - x[1] + 3 + math.cos(3 * x[0]))

        u1_int = 4
        v1_int = 4
        u2_int = 4
        v2_int = 4

        u_basis = bs.SplineBasis.make_equidistant(2, u1_int) #10
        v_basis = bs.SplineBasis.make_equidistant(2, v1_int) #15
        u2_basis = bs.SplineBasis.make_equidistant(2, u2_int) #10
        v2_basis = bs.SplineBasis.make_equidistant(2, v2_int) #15
        poles = bs.make_function_grid(function, u1_int + 2, v1_int + 2) #12, 17
        surface_extrude = bs.Surface((u_basis, v_basis), poles)

        myplot = bp.Plotting((bp.PlottingPlotly()))
        #myplot.plot_surface_3d(surface_extrude, poles = False)
        poles2 = bs.make_function_grid(function2,  u2_int + 2, v2_int + 2) #12, 17
        surface_extrude2 = bs.Surface((u2_basis, v2_basis), poles2)
        #myplot.plot_surface_3d(surface_extrude2, poles=False)

        m = 100
        fc = np.zeros([m * m, 3])
        fc2 = np.empty([m * m, 3])
        a = 5
        b = 7
        #print(fc)

        for i in range(m):
            for j in range(m):
                #print([i,j])
                x = i / m * a
                y = j / m * b
                z = function3([x, y])
                z2 = function4([x, y])
                fc[i + j * m, :] = [x, y, z]
                fc2[i + j * m, :] = [x, y, z2]

        #print(fc)

        #gs = bs.GridSurface(fc.reshape(-1, 3))
        #gs.transform(xy_mat, z_mat)
        #approx = bsa.SurfaceApprox.approx_from_grid_surface(gs)




        approx = bsa.SurfaceApprox(fc)
        approx2 = bsa.SurfaceApprox(fc2)
        surfz = approx.compute_approximation(nuv=np.array([11, 26]))
        surfz2 = approx2.compute_approximation(nuv=np.array([20, 16]))
        #surfz = approx.compute_approximation(nuv=np.array([3, 5]))
        #surfz2 = approx2.compute_approximation(nuv=np.array([2, 4]))
        surfzf = surfz.make_full_surface()
        surfzf2 = surfz2.make_full_surface()


        myplot.plot_surface_3d(surfzf, poles=False)
        myplot.plot_surface_3d(surfzf2, poles=False)

        #return surface_extrude, surface_extrude2, myplot
        return surfzf, surfzf2, myplot
Exemple #9
0
    def compute_adaptive_approximation(self, **kwargs):
        """
        Approximate the point set (given to the constructor) by a B-spline surface.
        The knot vectors in u and V direction are adaptively refined until a prescribed tolerance is reached.
        In order to prevent overfitting we regularize by penalizing the gradients of the constructed surface.
        The regularization parameter is automatically tuned to balance the approximation error |Z - surf(b)|
        and the regularization |grad surf(b)|_L2. Alternatively the cross-validation method can be applied.

        Compute approximation of the point set .
        Approximation parameters can be passed in through kwargs or set in the object before the call.
        :param quad: [(x1,y1), .. , (x4,y4)] Set vertices of different quad for the point set.
        :param nuv: (nu, nv) Set number of intervals of the resulting B-spline, in U and V direction
        :param max_iters: determines number refinement steps of the knot vectors , default (20)
        :param solver:
            'spsolve' (default) use the sparse direct solver scipy.sparse.linalg.spsolve ,
            'cg' use conjugate gradient solver scipy.sparse.linalg.cg
        :param adapt_type: 
            Adaptivity type to use. Denoting 'z(x,y)' the surface value and (x_i, y_i, z_i) given points:
            'absolute' (default) refine patches where |z(x_i, y_i) - z_i|_inf > max_diff 
            'std_dev' If the total L2 error is greater then 'std_dev', refine 'max_part' fraction of the rows/columns with highest L2 error contibution.
        :param max_diff: infinite norm tolerance for the 'absolute' refinement
        :param max_part: fraction of the raws/columns to be refined (1.0 is maximum) for the 'std_dev' refinement
        :param std_dev: Standard deviance of the Z components of the input points, or equivalently L2 norm tolerance. Used in 'std_dev' refinement method. achieved
        :param input_data_reduction: Determine regularization parameter using the cross-validation. Fit only to the random fraction 'input_data_reducion' 
             and use the remaining data for the cross-validation.
        :return: B-Spline surface

        Two refinement algorithms:
        
        absolute norm based adaptivity
         maximum norm is evaluated on every patch, if it holds: patch maximum norm > max_diff (param)
         then both of the knot vectors ("u" AND "v") are refined in corresponding intervals
         finished: number of iteration achieved max_iters (param) OR maximum norm on every patch < max_diff (param)

        standard deviation based adaptivity
         Euclidean norms of the errors are computed with respect u,v knot intervals
         even iteration: max_part (param) ratio of the "u" knot intervals involving the largest norm are refined
         odd iteration: max_part (param) ratio of the "v" knot intervals involving the largest norm are refined
         finished: number of iteration achieved max_iters (param) OR standard deviation < std_dev (param)
        """

        self.quad = kwargs.get("quad", self.quad)
        self.nuv = kwargs.get("nuv", self.nuv)
        self.solver = kwargs.get("solver", "spsolve")  # cg
        self.max_iters = kwargs.get("max_iters", 20)  #
        self.adapt_type = kwargs.get("adapt_type", "absolute")  # "std_dev"
        self.max_diff = kwargs.get("max_diff",
                                   10.0)  # for absolute based adaptivity
        self.max_part = kwargs.get(
            "max_part", 0.2)  # for standard deviation based adaptivity
        self.std_dev = kwargs.get(
            "std_dev", 1.0)  # for standard deviation based adaptivity
        self.input_data_reduction = kwargs.get("input_data_reduction", 1.0)

        logging.info('Transforming points (n={}) ...'.format(self._n_points))
        start_time = time.time()
        if self.quad is None:
            self.compute_default_quad()
        if self.nuv is None:
            self.compute_default_nuv()

        # TODO: better logic, since this has to be recomputed only if quad is changed.
        self._compute_uv_points()

        ###
        #self._w_quad_points = np.ones(len(self._w_quad_points))
        if self.input_data_reduction != 1.0:
            n = len(self._uv_quad_points)
            lsp = np.linspace(0, n - 1, n, dtype=int)
            red_lsp = np.random.choice(
                lsp, int(np.ceil(n * self.input_data_reduction)))
            compl_lsp = np.setxor1d(lsp, red_lsp)
            self._w_quad_points = np.ones(
                n)  # set all the weights equal to 1!!!
            self._w_quad_points[compl_lsp] = np.zeros(len(compl_lsp))
        ###

        logging.info("Using {} x {} B-spline approximation.".format(
            self.nuv[0], self.nuv[1]))
        self._u_basis = bs.SplineBasis.make_equidistant(2, self.nuv[0])
        self._v_basis = bs.SplineBasis.make_equidistant(2, self.nuv[1])

        end_time = time.time()
        logging.info('Computed in: {} s'.format(end_time - start_time))

        n_course = 1
        iters = -1
        while n_course != 0:  ### Adaptivity loop
            iters += 1

            if iters > 0:
                if (iters % 2) == 0:
                    if np.sum(ref_vec_u) > 0:
                        u_knot_new = self._refine_knots(
                            self._u_basis.knots, ref_vec_u)
                        self._u_basis = bs.SplineBasis.make_from_knots(
                            2, u_knot_new)
                else:
                    if np.sum(ref_vec_v) > 0:
                        v_knot_new = self._refine_knots(
                            self._v_basis.knots, ref_vec_v)
                        self._v_basis = bs.SplineBasis.make_from_knots(
                            2, v_knot_new)

            # Approximation itself
            logging.info(
                'Creating explicitly system of normal equations B^TBz=B^Tb ...'
            )
            start_time = time.time()
            self._locate_points()
            btb_mat, btwb_vec, avg_vec = self._build_system_of_normal_equations(
            )
            end_time = time.time()
            logging.info('Computed in: {} s'.format(end_time - start_time))

            logging.info('Creating A matrix ...')
            start_time = time.time()
            a_mat = self._build_sparse_reg_matrix()
            end_time = time.time()
            logging.info('Computed in: {} s'.format(end_time - start_time))

            logging.info('Computing A and B^TB svds approximation ...')
            start_time = time.time()
            if iters == 0:
                bb_norm = scipy.sparse.linalg.eigsh(btb_mat,
                                                    k=1,
                                                    ncv=10,
                                                    tol=1e-2,
                                                    which='LM',
                                                    maxiter=300,
                                                    return_eigenvectors=False)
                a_norm = scipy.sparse.linalg.eigsh(a_mat,
                                                   k=1,
                                                   ncv=10,
                                                   tol=1e-2,
                                                   which='LM',
                                                   maxiter=300,
                                                   return_eigenvectors=False)
                reg_coef = bb_norm[0] / a_norm[0]

            c_mat = btb_mat + reg_coef * a_mat
            end_time = time.time()
            logging.info('Computed in: {} s'.format(end_time - start_time))

            logging.info('Solving for Z coordinates ...')
            start_time = time.time()
            z_vec = self._solve_system(c_mat, btwb_vec, avg_vec)

            assert not np.isnan(
                np.sum(z_vec)), "Singular matrix for approximation."
            end_time = time.time()
            logging.info('Computed in: {} s'.format(end_time - start_time))

            logging.info('Computing error ...')
            start_time = time.time()

            diff, diff_mat_max, err_mat_eucl2, std_dev = self._compute_errors(
                z_vec)
            end_time = time.time()
            logging.info('Computed in: {} s'.format(end_time - start_time))

            if self.input_data_reduction != 1.0:
                diff_red = diff * self._w_quad_points  # make sense only fow w_i in set(0,1)

            ref_vec_u, ref_vec_v = self._refine_patches(
                diff_mat_max, err_mat_eucl2, std_dev, self.adapt_type)

            # Regularization coefficient
            print("L2 diff: ", diff.dot(diff))
            print("A2 diff: ", z_vec.dot(a_mat.dot(z_vec)))
            #reg_coef = diff.dot(diff) / z_vec.dot(a_mat.dot(z_vec)) # shoud be replaced by a more stable formula

            print("reg_coef =", reg_coef)
            print("iteration =", iters)
            print("\nL2_diff =", std_dev)
            print("\nmax_diff =", np.max(diff))
            print("area =", self._u_basis.n_intervals, 'x',
                  self._v_basis.n_intervals, "(n_patches =",
                  self._u_basis.n_intervals * self._v_basis.n_intervals, ")")
            if self.input_data_reduction != 1.0:
                logging.info("Efficient points ratio: {}".format(
                    self.input_data_reduction))
                logging.info(
                    "Ratio of the errors (efficient/complete): {}".format(
                        np.linalg.norm(diff_red) / np.linalg.norm(diff)))
            n_course = sum(ref_vec_u) + sum(ref_vec_v)
            if np.logical_or(n_course == 0, iters == self.max_iters):
                break

            self.error = max_diff = np.max(diff)
            logging.info("Approximation error (max norm): {}".format(max_diff))
            logging.info("Standard deviation: {}".format(std_dev))

        end_time = time.time()
        logging.info('Computed in: {} s'.format(end_time - start_time))

        # Construct Z-Surface
        poles_z = z_vec.reshape(self._v_basis.size, self._u_basis.size).T
        #poles_z *= self.grid_surf.z_scale
        #poles_z += self.grid_surf.z_shift
        surface_z = bs.Surface((self._u_basis, self._v_basis), poles_z[:, :,
                                                                       None])
        self.surface = bs.Z_Surface(self.quad[0:3], surface_z)

        return self.surface