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
def test_bspline_io(): def function(x): return np.sin(x[0] * 4) * np.cos(x[1] * 4) quad = np.array([[1., 3.5], [1., 2.], [2., 2.2], [2, 3.7]]) 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]]) surface = bs.Z_Surface(quad, surface_func) xy_map = [[0.1, 0.2, -2], [0.2, - 0.1, -1]] z_map = [2.0, 5.0] surface.transform(xy_map, z_map ) surf_io = bspline_io.bs_zsurface_write(surface) surf_new = bspline_io.bs_zsurface_read(surf_io) assert np.allclose(xy_map, surf_new.get_transform()[0]) assert np.allclose(z_map, surf_new.get_transform()[1]) assert np.allclose( surface.center(), surf_new.center()) surface.transform(xy_map) assert np.allclose(surface.center(), surf_new.center()) xy_map = [[0.2, 0.3, -3], [0.3, - 0.2, -2]] z_map = [3, 6] surface.transform(xy_map) surf_new.transform(xy_map) assert np.allclose(surface.center(), surf_new.center())
def bs_zsurface_read(z_surface_io): io = z_surface_io u_basis = bs.SplineBasis(io.u_degree, io.u_knots) v_basis = bs.SplineBasis(io.v_degree, io.v_knots) z_surf = bs.Surface((u_basis, v_basis), io.poles, io.rational) surf = bs.Z_Surface(io.orig_quad, z_surf) surf.transform(io.xy_map, io.z_map) return surf
def approx_chol(self): """ This function tries to approximate terrain data with B-Spline surface patches using Cholesky decomposition :param terrain_data: matrix of 3D terrain data :param quad: points defining quadrangle area (array) :param u_knots: array of u knots :param v_knots: array of v knots :param sparse: if sparse matrix is used :param filter_thresh: threshold of filter :return: B-Spline patch """ print('Transforming points to parametric space ...') start_time = time.time() points = self.grid_surf.points_xyz points_uv = self.grid_surf.xy_to_uv(points[:, 0:2]) # remove points far from unit square eps = 1.0e-15 cut_min = np.array([-eps, -eps]) cut_max = np.array([1 + eps, 1 + eps]) in_idx = np.all(np.logical_and(cut_min < points_uv, points_uv <= cut_max), axis=1) points_uv = points_uv[in_idx] points_z = points[in_idx, 2][:, None] # snap to unit square points_uv = np.maximum(points_uv, np.array([0.0, 0.0])) points_uv = np.minimum(points_uv, np.array([1.0, 1.0])) self.grid_uvz = np.concatenate((points_uv, points_z), axis=1) end_time = time.time() print('Computed in {0} seconds.'.format(end_time - start_time)) # Own computation of approximation print('Creating B matrix ...') start_time = time.time() b_mat, interval = self.build_ls_matrix() end_time = time.time() print('Computed in {0} seconds.'.format(end_time - start_time)) print('Creating A matrix ...') start_time = time.time() a_mat = self.build_sparse_reg_matrix() end_time = time.time() print('Computed in {0} seconds.'.format(end_time - start_time)) print('Computing B^T B matrix ...') start_time = time.time() bb_mat = b_mat.transpose() * b_mat end_time = time.time() print('Computed in {0} seconds.'.format(end_time - start_time)) bb_norm = scipy.sparse.linalg.svds(bb_mat, k=1, ncv=10, tol=1e-4, which='LM', v0=None, maxiter=300, return_singular_vectors=False) a_norm = scipy.sparse.linalg.svds(a_mat, k=1, ncv=10, tol=1e-4, which='LM', v0=None, maxiter=300, return_singular_vectors=False) c_mat = bb_mat + self.regularization_weight * (bb_norm[0] / a_norm[0]) * a_mat g_vec = self.grid_uvz[:, 2] b_vec = b_mat.transpose() * g_vec print('Computing Z coordinates ...') start_time = time.time() z_vec = scipy.sparse.linalg.spsolve(c_mat, b_vec) print(type(z_vec)) end_time = time.time() print('Computed in {0} seconds.'.format(end_time - start_time)) # print('Computing differences ...') # start_time = time.time() # diff = (numpy.matrix(b_mat * z_vec).transpose() - g_vec).tolist() # diff = [item[0] for item in diff] # end_time = time.time() # print('Computed in {0} seconds.'.format(end_time - start_time)) # Construct Z-Surface poles_z = z_vec.reshape(self.u_basis.size, self.v_basis.size, 1) surface_z = bs.Surface((self.u_basis, self.v_basis), poles_z) z_surf = bs.Z_Surface(self.grid_surf.quad, surface_z) return z_surf
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 B matrix ...') start_time = time.time() b_mat, interval = self._build_ls_matrix() 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('Scaling + B^T B ...') start_time = time.time() g_vec = self._z_quad_points[:] if self._weights is not None: W = scipy.sparse.diags(self._w_quad_points, 0) wg_vec = np.dot(W, g_vec) wb_mat = np.dot(W, b_mat) else: wg_vec = g_vec wb_mat = b_mat b_vec = b_mat.transpose().dot(wg_vec) bb_mat = b_mat.transpose().dot(wb_mat) end_time = time.time() logging.info('Computed in: {} s'.format(end_time - start_time)) logging.info('Computing A and B svds approximation ...') start_time = time.time() bb_norm = scipy.sparse.linalg.svds(bb_mat, k=1, ncv=10, tol=1e-2, which='LM', v0=None, maxiter=300, return_singular_vectors=False) a_norm = scipy.sparse.linalg.eigsh(a_mat, k=1, ncv=10, tol=1e-2, which='LM', maxiter=300, return_eigenvectors=False) c_mat = bb_mat + self.regularization_weight * (bb_norm[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, b_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() self.diff = b_mat.dot(z_vec) - g_vec self.error = max_diff = np.max(np.abs(self.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