def __init__(self, points): self._log = logging.getLogger('Surface') self.mapping = {} for point in points: self.mapping[point[0:2]] = point[2] self.points = self.mapping.keys() self.tree = KDTree(self.points) self.square_solver = MinSquares(self) # cache mls results self.mls_subdivisions = None self.mls_points = None
def calculate_mls_values(self, subdivisions, degree): if self.mls_subdivisions != subdivisions: self.distance_threshold = self.box_diag / float(subdivisions + 1) self.square_solver = MinSquares(self) self._log.debug(u"Calculating mls grid values...") self.mls_subdivisions = subdivisions self.mls_distances = zeros(((subdivisions+2)**3,), dtype='f') self.mls_normals = zeros(((subdivisions+2)**3,), dtype='3f') self.mls_points = zeros(((subdivisions+2)**3,), dtype='3f') for index, point in enumerate(self.iter_regular_xyz_grid_points(subdivisions)): distance, normal = self.square_solver.solveEquations(point, degree) self.mls_distances[index] = distance self.mls_normals[index] = normal self.mls_points[index] = point else: self._log.debug(u"Using cached mls grid values...")
def calculate_mls_values(self, subdivisions, degree, radius): if self.mls_subdivisions != subdivisions or self.mls_degree != degree: self._log.debug(u"Calculating mls grid values...") self.mls_subdivisions = subdivisions self.mls_degree = degree self.mls_radius = radius self.mls_distances = zeros(((subdivisions+2)**3,), dtype='f') self.mls_points = zeros(((subdivisions+2)**3,), dtype='3f') self.eps = self.box_diag / 100 #((subdivisions or 10) * 10.0) self._log.debug("Using radius %f, eps %f..." % (self.mls_radius, self.eps)) self.square_solver = MinSquares(self) for index, point in enumerate(self.iter_regular_xyz_grid_points(subdivisions)): distance = self.square_solver.get_point(point, degree) self.mls_distances[index] = distance self.mls_points[index] = point else: self._log.debug(u"Using cached mls grid values...")
class ImplicitSurface(object): def __init__(self, points, normals): self._log = logging.getLogger('ImplicitSurface') self.points = points self.normals = normals self.tree = cKDTree(self.points) # choose distance threshold and eps depending on bbox diameter min_point, max_point = self.get_bounding_box() self.box_diag = float(norm(max_point - min_point)) self.distance_threshold = 1 # initialized when needed self.eps = 1.0 # initialized when needed self.center = min_point + 0.5 * (max_point - min_point) self.square_solver = None # initialized when needed # cache mls results self.mls_subdivisions = None self.mls_distances = [] self.mls_points = [] self.mls_degree = None self.mls_radius = None self.cube_node = None @classmethod def from_file(cls, filename, invert_normals=False): logging.getLogger('SurfaceFactory').info(u"Reading surface from '%s'..." % filename) vertices = [] normals = [] for vertex, normal in openOff(filename).iter_vertices_and_normals(): vertices.append(vertex) normals.append(normal) vertices = array(vertices) normals = array(normals) # scale points scale_factor = 1.0/norm(max(vertices, 0) - min(vertices, 0)) vertices *= scale_factor # invert normals? if invert_normals: normals *= -1 return cls(points=vertices, normals=normals) def get_bounding_box(self, expansion_factor=0.2): """Returns the minumum and maximum corners of the bounding box. :Parameters: expansion_factor : float The factor by which to enlarge the bounding box. :return: (min_point, max_point) """ min_point = min(self.points, 0) max_point = max(self.points, 0) diff = max_point - min_point min_point -= diff*expansion_factor max_point += diff*expansion_factor return (min_point, max_point) def get_parameter_grid(self, subdivisions, degree, radius): self.calculate_mls_values(subdivisions, degree, radius) if self.mls_subdivisions: self._log.debug(u"Calculating colors...") colors = zeros(((subdivisions+2)**3,), dtype='4f') max_abs_distance = max([abs(min(self.mls_distances)), abs(max(self.mls_distances))]) for index, distance in enumerate(self.mls_distances): if distance < 0: colors[index] = (0.0, 0.0, 1.0, 0.0) else: colors[index] = (0.0, 1.0, 0.0, 0.0) colors[index] *= (1.0 - abs(float(distance)/max_abs_distance))**4 colors[:,3] = 1.0 else: self._log.debug(u"Using plain colors...") colors = None return PointCloudNode( name = 'parameter grid', points = [(x,y,z) for x,y,z in self.iter_regular_xyz_grid_points(subdivisions)], color = (0.0,1.0,0.0,0.6), colors = colors) def get_original_point_cloud(self): return PointCloudNode( name = 'point cloud', points = self.points, color = (1.0, 1.0, 1.0, 1.0)) def calculate_mls_values(self, subdivisions, degree, radius): if self.mls_subdivisions != subdivisions or self.mls_degree != degree: self._log.debug(u"Calculating mls grid values...") self.mls_subdivisions = subdivisions self.mls_degree = degree self.mls_radius = radius self.mls_distances = zeros(((subdivisions+2)**3,), dtype='f') self.mls_points = zeros(((subdivisions+2)**3,), dtype='3f') self.eps = self.box_diag / 100 #((subdivisions or 10) * 10.0) self._log.debug("Using radius %f, eps %f..." % (self.mls_radius, self.eps)) self.square_solver = MinSquares(self) for index, point in enumerate(self.iter_regular_xyz_grid_points(subdivisions)): distance = self.square_solver.get_point(point, degree) self.mls_distances[index] = distance self.mls_points[index] = point else: self._log.debug(u"Using cached mls grid values...") def query_distance(self, point): return self.square_solver.get_point(point, self.mls_degree) def query_normal(self, point): return self.square_solver.get_normal(point, self.mls_degree) def get_implicit_surface(self, subdivisions, degree, radius): if self.cube_node is None: self.calculate_mls_values(subdivisions, degree, radius) self.cube_node = MarchingCubeNode( name = "implicit_surface", surface = self, color = (1.0, 0.0, 0.0, 1.0), wireframe = False) return self.cube_node def get_halfedge_mesh(self, optimization_choice, value): if self.cube_node: halfedge_mesh = HalfEdgeMesh.from_triangles(self.cube_node) optimizer = HalfEdgeMeshOptimizer(halfedge_mesh, self) if optimization_choice == 0: optimizer.optimize_by_faces(value) elif optimization_choice == 1: optimizer.optimize_by_improvement(value) return halfedge_mesh.get_node("HalfEdge Mesh", color=array([1.0, 0.5, 0.0, 1.0])) else: return None def iter_regular_xyz_grid_points(self, subdivisions): min_point, max_point = self.get_bounding_box() for x in linspace(min_point[0], max_point[0], subdivisions+2): for y in linspace(min_point[1], max_point[1], subdivisions+2): for z in linspace(min_point[2], max_point[2], subdivisions+2): #self._log.debug(u"Yielding (%f, %f)..." % (x, y)) yield (x, y, z)
class ImplicitSurface(object): def __init__(self, points, normals): self._log = logging.getLogger('ImplicitSurface') self.points = points self.normals = normals self.tree = cKDTree(self.points) # choose distance threshold and eps depending on bbox diameter min_point, max_point = self.get_bounding_box() self.box_diag = norm(max_point - min_point) self.eps = self.box_diag * 0.1 self.center = min_point + 0.5 * (max_point - min_point) # cache mls results self.mls_subdivisions = None self.mls_distances = [] self.mls_normals = [] self.mls_points = [] @classmethod def from_file(cls, filename): logging.getLogger('SurfaceFactory').info(u"Reading surface from '%s'..." % filename) vertices = [] normals = [] for vertex, normal in openOff(filename).iter_vertices_and_normals(): vertices.append(vertex) normals.append(normal) return cls(points=vertices, normals=normals) def get_bounding_box(self, expansion_factor=0.2): """Returns the minumum and maximum corners of the bounding box. :Parameters: expansion_factor : float The factor by which to enlarge the bounding box. :return: (min_point, max_point) """ min_point = min(self.points, 0) max_point = max(self.points, 0) diff = max_point - min_point min_point -= diff*expansion_factor max_point += diff*expansion_factor return (min_point, max_point) def get_parameter_grid(self, subdivisions, degree=None): if degree: self.calculate_mls_values(subdivisions, degree) if self.mls_subdivisions: self._log.debug(u"Calculating colors...") colors = zeros(((subdivisions+2)**3,), dtype='4f') max_abs_distance = max([abs(min(self.mls_distances)), abs(max(self.mls_distances))]) for index, distance in enumerate(self.mls_distances): if distance < 0: colors[index] = (0.0, 0.0, 1.0, 0.0) else: colors[index] = (0.0, 1.0, 0.0, 0.0) colors[index] *= (1.0 - abs(float(distance)/max_abs_distance))**4 colors[index] += (0.0, 0.0, 0.0, 1.0) else: self._log.debug(u"Using plain colors...") colors = None return PointCloudNode( name = 'parameter grid', points = [(x,y,z) for x,y,z in self.iter_regular_xyz_grid_points(subdivisions)], color = (0.0,1.0,0.0,0.6), colors = colors) def get_original_point_cloud(self): return PointCloudNode( name = 'point cloud', points = self.points, color = (1.0, 1.0, 1.0, 1.0)) def calculate_mls_values(self, subdivisions, degree): if self.mls_subdivisions != subdivisions: self.distance_threshold = self.box_diag / float(subdivisions + 1) self.square_solver = MinSquares(self) self._log.debug(u"Calculating mls grid values...") self.mls_subdivisions = subdivisions self.mls_distances = zeros(((subdivisions+2)**3,), dtype='f') self.mls_normals = zeros(((subdivisions+2)**3,), dtype='3f') self.mls_points = zeros(((subdivisions+2)**3,), dtype='3f') for index, point in enumerate(self.iter_regular_xyz_grid_points(subdivisions)): distance, normal = self.square_solver.solveEquations(point, degree) self.mls_distances[index] = distance self.mls_normals[index] = normal self.mls_points[index] = point else: self._log.debug(u"Using cached mls grid values...") def get_implicit_surface(self, subdivisions, degree): self.calculate_mls_values(subdivisions, degree) return MarchingCubeNode( name = "implicit_surface", surface = self, color = (1.0, 0.0, 0.0, 1.0), wireframe = False) # TODO: perform marching cubes algorithm # return SurfaceNode( # name = 'mls surface', # width = subdivisions+2, # height = subdivisions+2, # points = self.mls_points, # normals= self.mls_normals, # color = (1.0,0.0,0.0,0.9)) def iter_regular_xyz_grid_points(self, subdivisions): min_point, max_point = self.get_bounding_box() for x in linspace(min_point[0], max_point[0], subdivisions+2): for y in linspace(min_point[1], max_point[1], subdivisions+2): for z in linspace(min_point[2], max_point[2], subdivisions+2): #self._log.debug(u"Yielding (%f, %f)..." % (x, y)) yield (x, y, z)
class Surface(object): def __init__(self, points): self._log = logging.getLogger('Surface') self.mapping = {} for point in points: self.mapping[point[0:2]] = point[2] self.points = self.mapping.keys() self.tree = KDTree(self.points) self.square_solver = MinSquares(self) # cache mls results self.mls_subdivisions = None self.mls_points = None @classmethod def from_file(cls, filename): logging.getLogger('SurfaceFactory').info(u"Reading surface from '%s'..." % filename) return cls(openOff(filename).iter_vertices()) def get_parameter_plane(self, subdivisions): return SurfaceNode( name = 'parameter plane', width = subdivisions+2, height = subdivisions+2, points = [(x,y,0) for x,y in self.iter_regular_xy_grid_points(subdivisions)], color = (1.0,1.0,1.0,0.9), wireframe = True) def get_original_point_cloud(self): return PointCloudNode( name = 'point cloud', points = self.mapping, color = (1.0, 1.0, 1.0, 0.9)) def get_mls_interpolated_surface(self, subdivisions): if self.mls_subdivisions != subdivisions: points_normals = [(x,y) + self.square_solver.solveEquations((x,y)) for x,y in self.iter_regular_xy_grid_points(subdivisions)] self.mls_points = [(x,y,z) for x,y,z,n in points_normals] self.mls_normals = [n for x,y,z,n in points_normals] self.mls_subdivisions = subdivisions return SurfaceNode( name = 'mls surface', width = subdivisions+2, height = subdivisions+2, points = self.mls_points, normals= self.mls_normals, color = (1.0,0.0,0.0,0.9)) def get_bezier_interpolated_surface(self, mls_subdivisions, bezier_factor): self._log.debug(u"Creating surface using grid %d and bezier factor %d" % (mls_subdivisions, bezier_factor)) if self.mls_subdivisions != mls_subdivisions: self.get_mls_interpolated_surface(mls_subdivisions) value_matrix = transpose(reshape(array(self.mls_points)[:,2], (mls_subdivisions+2, mls_subdivisions+2)), (1,0)) casteljau = Casteljau(value_matrix) points_normals = [(x,y) + casteljau.evaluate_point((x,y)) for x,y in self.iter_regular_xy_grid_points(mls_subdivisions*bezier_factor)] return SurfaceNode( name = 'bezier surface', width = mls_subdivisions*bezier_factor+2, height = mls_subdivisions*bezier_factor+2, points = [(x,y,z) for x,y,z,n in points_normals], normals= [n for x,y,z,n in points_normals], color = (0.0,1.0,0.0,0.5)) def iter_regular_xy_grid_points(self, subdivisions): min_point = min(self.points, 0) max_point = max(self.points, 0) for x in linspace(min_point[0], max_point[0], subdivisions+2): for y in linspace(min_point[1], max_point[1], subdivisions+2): #self._log.debug(u"Yielding (%f, %f)..." % (x, y)) yield (x, y)