def compute_hypothetical_errors(self, point_indeces): """ Computes the hypothetical errors of each point in the TIN, if they were to be removed. """ indices, indptr = self.dt.vertex_neighbor_vertices for pt_index in point_indeces: current_vertex = self.dt.points[pt_index] point = self.grid.get(current_vertex[0], current_vertex[1]) if point in self.grid.get_corner_set(): continue # Find neighboring vertices of the vertex at pt_index & create hypothetical triangulation neighbor_indeces = indptr[indices[pt_index]:indices[pt_index + 1]] neighbors = self.dt.points[neighbor_indeces] hypothetical_triangulation = Delaunay(neighbors) # Store neighbor point data in the point for later point_neighbors = set() for pt in neighbors: point_neighbors.add(self.grid.get(pt[0], pt[1])) point.neighbors = point_neighbors # Locate current point in new triangulation & compute error simplex = hypothetical_triangulation.find_simplex(current_vertex) triangle_pts = hypothetical_triangulation.points[ hypothetical_triangulation.simplices[simplex]] self.compute_point_error(point, triangle_pts)
def __init__(self, coords, values=None, neighbors=3, dist_transform=2): super().__init__(coords, values) # Construct a convex hull of passed coords. Cast coords to float32, otherwise cv2 may fail. cv2 is used since # QHull can't handle degenerate hulls. self.coords_hull = cv2.convexHull(self.coords.astype(np.float32), returnPoints=True) # Construct an IDW interpolator to use outside the constructed hull self.idw_interpolator = IDWInterpolator(coords, values, neighbors=neighbors, dist_transform=dist_transform) # Triangulate input points try: self.tri = Delaunay(self.coords, incremental=False) except QhullError: # Delaunay fails in case of linearly dependent coordinates. Create artificial points in the corners of # given coordinate grid in order for Delaunay to work with a full rank matrix. min_x, min_y = np.min(self.coords, axis=0) - 1 max_x, max_y = np.max(self.coords, axis=0) + 1 corner_coords = [(min_x, min_y), (min_x, max_y), (max_x, min_y), (max_x, max_y)] self.coords = np.concatenate([self.coords, corner_coords]) if self.values is not None: mean_values = np.mean(self.values, axis=0, keepdims=True, dtype=self.values.dtype) corner_values = np.repeat(mean_values, 4, axis=0) self.values = np.concatenate([self.values, corner_values]) self.tri = Delaunay(self.coords, incremental=False) # Perform the first auxiliary call of the tri for it to work properly in different processes. # Otherwise interpolation may fail if called in a pipeline with prefetch with mpc target. _ = self.tri.find_simplex((0, 0))
def _interp(orig_val: np.ndarray, orig_lat: np.ndarray, orig_lon: np.ndarray, lat: np.ndarray, lon: np.ndarray) -> np.ndarray: """ 格点插值 :param orig_val: 等距离文件中的格点数组 :param orig_lat: 等距离文件的lat数组 :param orig_lon: 等距离文件的lon数组 :param lat: 所需要的等经纬度的lat数组 :param lon: 所需要的等经纬度的lon数组 :return: """ if orig_val.ndim != 2: raise ValueError('interp only receive 2-dim variable') if lat.ndim == 1 or lon.ndim == 1: lon, lat = np.meshgrid(lon, lat) if _INTERP_SWITCH == 1: tri = Delaunay(np.asarray([orig_lon.ravel(), orig_lat.ravel()]).T) interpolator = LinearNDInterpolator(tri, orig_val.ravel()) val = interpolator((lon, lat)) elif _INTERP_SWITCH == 2: val = griddata( np.asarray([orig_lon.ravel(), orig_lat.ravel()]).T, orig_val.ravel(), (lon, lat)) else: raise ValueError('INTERP_SWITCH only can be 1 or 2') return val
def _add_boundary_points_and_construct_delaunay(self): """ This method add the corners of [0, 1]^dimension hypercube to the existing samples, which are used to construct a Delaunay Triangulation. """ from scipy.spatial.qhull import Delaunay self.mesh_vertices = self.training_points.copy() self.points_to_samplesU01 = np.arange(0, self.training_points.shape[0]) for i in range(np.shape(self.strata_object.voronoi.vertices)[0]): if any(np.logical_and(self.strata_object.voronoi.vertices[i, :] >= -1e-10, self.strata_object.voronoi.vertices[i, :] <= 1e-10)) or \ any(np.logical_and(self.strata_object.voronoi.vertices[i, :] >= 1 - 1e-10, self.strata_object.voronoi.vertices[i, :] <= 1 + 1e-10)): self.mesh_vertices = np.vstack([ self.mesh_vertices, self.strata_object.voronoi.vertices[i, :] ]) self.points_to_samplesU01 = np.hstack([ np.array([-1]), self.points_to_samplesU01, ]) # Define the simplex mesh to be used for gradient estimation and sampling self.mesh = Delaunay(self.mesh_vertices, furthest_site=False, incremental=True, qhull_options=None) self.points = getattr(self.mesh, 'points')
def GridInterpolation(pos, data, Xi, Yi): from scipy.spatial.qhull import Delaunay from scipy.interpolate import CloughTocher2DInterpolator import itertools extremes = np.array([pos.min(axis=0), pos.max(axis=0)]) diffs = extremes[1] - extremes[0] extremes[0] -= diffs extremes[1] += diffs eidx = np.array( list( itertools.product(*([[0] * (pos.shape[1] - 1) + [1]] * pos.shape[1])))) pidx = np.tile(np.arange(pos.shape[1])[np.newaxis], (len(eidx), 1)) n_extra = pidx.shape[0] outer_pts = extremes[eidx, pidx] pos = np.concatenate((pos, outer_pts)) tri = Delaunay(pos) data = np.concatenate((data, np.zeros(n_extra))) Interpolator = CloughTocher2DInterpolator(tri, data) args = [Xi, Yi] Zi = Interpolator(*args) return Zi
def create_tree(features, neighs=200): tri = Delaunay(features) tree = DistanceTree(features) for entries in tri.simplices: row = [] for entry in entries: row.append(features[entry].tolist()) dist1 = distance.euclidean(row[0], row[1]) dist2 = distance.euclidean(row[0], row[2]) dist3 = distance.euclidean(row[1], row[2]) edge1 = Edge(entries[0], entries[1], dist1, Vertex(row[0]), Vertex(row[1])) edge2 = Edge(entries[0], entries[2], dist2, Vertex(row[0]), Vertex(row[2])) edge3 = Edge(entries[1], entries[2], dist3, Vertex(row[1]), Vertex(row[2])) tree.add_edge(edge1) tree.add_edge(edge2) tree.add_edge(edge3) tree.calc_neighbours(neighs) tree.wasser_cost_calc() tree.sort_wasser() return tree
def single_surface(star_container=None, points=None): """ calculates triangulation of given set of points, if points are not given, star surface points are used. Returns set of triple indices of surface pints that make up given triangle :param star_container: StarContainer; :param points: np.array: :: numpy.array([[x1 y1 z1], [x2 y2 z2], ... [xN yN zN]]) :return: np.array(): :: numpy.array([[point_index1 point_index2 point_index3], [...], ... [...]]) """ if points is None: points = star_container.points triangulation = Delaunay(points) triangles_indices = triangulation.convex_hull return triangles_indices
def optimal_mu(self, error=None, k=1): """ Return the parametric points where new high-fidelity solutions have to be computed in order to globally reduce the estimated error. These points are the barycentric center of the region (simplex) with higher error. :param numpy.ndarray error: the estimated error evaluated for each snapshot; if error array is not passed, it is computed using :func:`loo_error` with the default function. Default value is None. :param int k: the number of optimal points to return. Default value is 1. :return: the optimal points :rtype: list(numpy.ndarray) """ if error is None: error = self.loo_error() mu = self.database.parameters tria = Delaunay(mu) error_on_simplex = np.array([ np.sum(error[smpx]) * self._simplex_volume(mu[smpx]) for smpx in tria.simplices ]) barycentric_point = [] for index in np.argpartition(error_on_simplex, -k)[-k:]: worst_tria_pts = mu[tria.simplices[index]] worst_tria_err = error[tria.simplices[index]] barycentric_point.append( np.average(worst_tria_pts, axis=0, weights=worst_tria_err)) return barycentric_point
def conv(points): "Indices of the convex hull." if len(points) <= 2: return points else: tri = Delaunay(np.array([(x.x, x.y) for x in points])) hul = list({v for x in tri.convex_hull for v in x}) return [points[i] for i in hul]
def generate_delaunay_triangulation(k): x = np.random.rand(k, 1) y = np.random.rand(k, 1) X = np.concatenate((x - 1, x - 1, x - 1, x, x, x, x + 1, x + 1, x + 1)) Y = np.concatenate((y - 1, y, y + 1, y - 1, y, y + 1, y - 1, y, y + 1)) points = np.dstack((X, Y)).reshape((-1, 2)) tri = Delaunay(points) return tri
def detached_system_surface(system, components_distance, points=None, component="all"): """ Calculates surface faces from the given component's points in case of detached or semi-contact system. :param system: elisa.binary_system.container.OrbitalPositionContainer; :param components_distance: float; :param points: numpy.array; :param component: str; :return: numpy.array; N x 3 array of vertices indices """ component_instance = getattr(system, component) if points is None: points = component_instance.points if not np.any(points): raise ValueError(f"{component} component, with class instance name {component_instance.name} do not " "contain any valid surface point to triangulate") # there is a problem with triangulation of near over-contact system, delaunay is not good with pointy surfaces critical_pot = system.primary.critical_surface_potential if component == 'primary' \ else system.secondary.critical_surface_potential potential = system.primary.surface_potential if component == 'primary' \ else system.secondary.surface_potential if potential - critical_pot > 0.01: logger.debug(f'triangulating surface of {component} component using standard method') triangulation = Delaunay(points) triangles_indices = triangulation.convex_hull else: logger.debug(f'surface of {component} component is near or at critical potential; therefore custom ' f'triangulation method for (near)critical potential surfaces will be used') # calculating closest point to the barycentre r_near = np.max(points[:, 0]) if component == 'primary' else np.min(points[:, 0]) # projection of component's far side surface into ``sphere`` with radius r1 points_to_transform = copy(points) if component == 'secondary': points_to_transform[:, 0] -= components_distance projected_points = \ r_near * points_to_transform / np.linalg.norm(points_to_transform, axis=1)[:, None] if component == 'secondary': projected_points[:, 0] += components_distance triangulation = Delaunay(projected_points) triangles_indices = triangulation.convex_hull return triangles_indices
class BaseDelaunayInterpolator(SpatialInterpolator): """A base class for interpolators built on top of Delaunay triangulation of data points. The class triangulates input `coords` and stores the result in `tri` attribute of the created instance. It also constructs an IDW interpolator which is used to perform extrapolation for coordinates lying outside the convex hull of data points. Each concrete subclass must implement `_interpolate_inside_hull` method. """ def __init__(self, coords, values=None, neighbors=3, dist_transform=2): super().__init__(coords, values) # Construct a convex hull of passed coords. Cast coords to float32, otherwise cv2 may fail. cv2 is used since # QHull can't handle degenerate hulls. self.coords_hull = cv2.convexHull(self.coords.astype(np.float32), returnPoints=True) # Construct an IDW interpolator to use outside the constructed hull self.idw_interpolator = IDWInterpolator(coords, values, neighbors=neighbors, dist_transform=dist_transform) # Triangulate input points try: self.tri = Delaunay(self.coords, incremental=False) except QhullError: # Delaunay fails in case of linearly dependent coordinates. Create artificial points in the corners of # given coordinate grid in order for Delaunay to work with a full rank matrix. min_x, min_y = np.min(self.coords, axis=0) - 1 max_x, max_y = np.max(self.coords, axis=0) + 1 corner_coords = [(min_x, min_y), (min_x, max_y), (max_x, min_y), (max_x, max_y)] self.coords = np.concatenate([self.coords, corner_coords]) if self.values is not None: mean_values = np.mean(self.values, axis=0, keepdims=True, dtype=self.values.dtype) corner_values = np.repeat(mean_values, 4, axis=0) self.values = np.concatenate([self.values, corner_values]) self.tri = Delaunay(self.coords, incremental=False) # Perform the first auxiliary call of the tri for it to work properly in different processes. # Otherwise interpolation may fail if called in a pipeline with prefetch with mpc target. _ = self.tri.find_simplex((0, 0)) def _is_in_hull(self, coords): """Check whether items in `coords` lie within the convex hull of data points.""" coords = coords.astype(np.float32) # Cast coords to float32 to match the type of points in the convex hull return np.array([cv2.pointPolygonTest(self.coords_hull, coord, measureDist=False) >= 0 for coord in coords]) def _interpolate_inside_hull(self, coords): """Perform interpolation for coordinates lying inside convex hull of data points. `coords` are guaranteed to be 2-dimensional with shape (n_coords, 2).""" _ = coords raise NotImplementedError def _interpolate(self, coords): """Perform interpolation at given `coords`. Falls back to an IDW interpolator for points lying outside the convex hull of data points. `coords` are guaranteed to be 2-dimensional with shape (n_coords, 2).""" inside_hull_mask = self._is_in_hull(coords) values = np.empty((len(coords), self.values.shape[1]), dtype=self.values.dtype) values[inside_hull_mask] = self._interpolate_inside_hull(coords[inside_hull_mask]) # pylint: disable-next=protected-access values[~inside_hull_mask] = self.idw_interpolator._interpolate(coords[~inside_hull_mask]) return values
def conv(points): "Indices of the convex hull." _points = points points = np.array([(x.m, x.b) for x in points]) if len(points) <= 2: hul = range(len(points)) else: tri = Delaunay(points) hul = list({v for x in tri.convex_hull for v in x}) return list(np.array(_points)[hul])
def alphaShape(coordinates: TCoordinates, alpha=0.5): """ Compute the `alpha shape`_ of a set of points. Based on `this implementation`_. In contrast to the standard definition of the parameter alpha here we normalize it by the mean edge size of the cluster. This results in similar "concavity properties" of the resulting shapes for different coordinate sets and a fixed alpha. .. _this implementation: https://sgillies.net/2012/10/13/the-fading-shape-of-alpha.html .. _alpha shape: https://en.wikipedia.org/wiki/Alpha_shape :param coordinates: a suitable iterable of 2-dimensional coordinates :param alpha: alpha value to influence the gooeyness of the border. Larger numbers don't fall inward as much as smaller numbers. :return: a shapely Polygon """ coordinates = extractCoordinatesArray(coordinates) edge_index_pairs = set() edge_vertex_pairs = [] graph = delaunayGraph(coordinates) mean_edge_size = graph.size(weight="weight") / graph.number_of_edges() def add_edge(edge_index_pair, edge_vertex_pair): """ Add a line between the i-th and j-th points, if not in the list already """ edge_index_pair = tuple(sorted(edge_index_pair)) if edge_index_pair in edge_index_pairs: # already added return edge_index_pairs.add(edge_index_pair) edge_vertex_pairs.append(edge_vertex_pair) tri = Delaunay(coordinates) for simplex in tri.simplices: vertices = tri.points[simplex] area = Polygon(vertices).area edges = combinations(vertices, 2) product_edges_lengths = 1 for vertex_1, vertex_2 in edges: product_edges_lengths *= euclidean(vertex_1, vertex_2) # this is the radius of the circumscribed circle of the triangle # see https://en.wikipedia.org/wiki/Circumscribed_circle#Triangles circum_r = product_edges_lengths / (4.0 * area) if circum_r < mean_edge_size / alpha: for index_pair in combinations(simplex, 2): add_edge(index_pair, tri.points[np.array(index_pair)]) remaining_edges = MultiLineString(edge_vertex_pairs) return unary_union(list(polygonize(remaining_edges)))
def draw_delaunay_triangulation(self): """ Draws the Delaunay triangulation of cell centers. """ points = [center.loc for center in self.centers] dual = Delaunay(points) simplices = [[dual.points[i] for i in simplex] for simplex in dual.simplices] for simplex in simplices: centroid = tuple((sum(simplex) / 3).astype(int))[::-1] polygon(self.window, IMAGE[centroid], simplex)
def _rebuild_hull(self): numpy_parameters = self._training_parameters.numpy() if numpy_parameters.shape[0] < numpy_parameters.shape[1] + 2: # Check we have enough points to build a Delaunay hull. return # We need to remove any 'flat' degrees of freedom (i.e any # parameters where all training data has the same value, such # as removing temperatures if all training points were measured # at the same temperature). self._flat_parameter_indices = numpy.argwhere( numpy.all(numpy_parameters == numpy_parameters[0, :], axis=0)) self._flat_parameter_values = numpy_parameters[ 0, self._flat_parameter_indices] index_mask = numpy.ones(numpy_parameters.shape[1], numpy.bool) index_mask[self._flat_parameter_indices] = 0 hull_parameters = numpy_parameters[:, index_mask] self._convex_hull = Delaunay(hull_parameters)
def invalidate(self, proj): try: from scipy.spatial.qhull import Delaunay except ImportError: print('DelaunayLayer needs scipy >= 0.12') raise self.painter = BatchPainter() x, y = proj.lonlat_to_screen(self.data['lon'], self.data['lat']) points = list(set(zip(x, y))) dela = Delaunay(points) edges = set() for tria in dela.vertices: edges.add((tria[0], tria[1])) edges.add((tria[1], tria[2])) edges.add((tria[2], tria[0])) allx0 = [] ally0 = [] allx1 = [] ally1 = [] colors = [] for a, b in edges: x0, y0 = dela.points[a] x1, y1 = dela.points[b] allx0.append(x0) ally0.append(y0) allx1.append(x1) ally1.append(y1) if self.line_color: colors.append(self.line_color) colors.append(self.line_color) elif self.cmap: l = math.sqrt((x0 - x1)**2 + (y0 - y1)**2) c = self.cmap.to_color(l, self.max_lenght, 'log') colors.append(c) colors.append(c) self.painter.lines(allx0, ally0, allx1, ally1, colors, width=self.line_width)
def delaunay_pos_graph(pos): """Compute a graph containing the Delaunay triangulation of the positons.""" # Duplicate the positions left and right # Needed for the triangulation to wrap around earth new_pos = pos.copy() duplicate_right = pos.copy() duplicate_right[:, 1] = duplicate_right[:, 1] + 360 new_pos = np.vstack((new_pos, duplicate_right)) duplicate_left = pos.copy() duplicate_left[:, 1] = duplicate_left[:, 1] - 360 new_pos = np.vstack((new_pos, duplicate_left)) def reconvert_node(node_num, n_ixps): """Convert from the position in the duplicate matrix to the original.""" return node_num - n_ixps * (node_num // n_ixps) # Make the Delaunay triangulation triangulation = Delaunay(new_pos) # Load the links from the triangles in a graph, with geographic distances n_pos = pos.shape[0] # 578 G = nx.Graph() G.add_nodes_from(np.arange(n_pos)) for cur_tri in triangulation.simplices: a1, b1, c1 = cur_tri a = reconvert_node(a1, n_pos) b = reconvert_node(b1, n_pos) c = reconvert_node(c1, n_pos) ab = great_circle(pos[a, :], pos[b, :]).km bc = great_circle(pos[b, :], pos[c, :]).km ca = great_circle(pos[c, :], pos[a, :]).km if not ab > 0: print(f"{a} - {b} is {ab} in {a1, b1, c1}") # raise ValueError(f"{a} - {b} is {ab} in {a1, b1, c1}") if not bc > 0: print(f"{b} - {c} is {bc} in {a1, b1, c1}") # raise ValueError(f"{b} - {c} is {bc} in {a1, b1, c1}") if not ca > 0: print(f"{c} - {a} is {ca} in {a1, b1, c1}") # raise ValueError(f"{c} - {a} is {ca} in {a1, b1, c1}") G.add_edge(a, b, length=ab) G.add_edge(b, c, length=bc) G.add_edge(c, a, length=ca) return G
def polygon_sample(vertices): """Uniform sampling of points inside a convex polygon. Non convex polygons will be treated as the input would be their convex hull. Steps of the algorithm 1) `Delaunay triangulation`_ to break the polygon into triangular mesh. 2) Draw random uniform triangle weighted by its area. 3) Draw random uniform sample from inside the triangle. .. _Delaunay triangulation: https://en.wikipedia.org/wiki/Delaunay_triangulation Args: vertices (numpy.ndarray): Array of polygon vertices. Shape of (n, 2). Yields: numpy.ndarray: Random point inside the polygon. References: - http://gis.stackexchange.com/questions/6412/generate-points-that-lie-inside-polygon Todo: - Algorithm for sampling non-convex polygons """ assert len(vertices.shape) == 2 assert vertices.shape[1] == 2 #np.random.seed(seed) delaunay = Delaunay(vertices) # Triangulation mesh = vertices[delaunay.simplices] # Triangle mesh # Weights for choosing random uniform triangle from the mesh. # Weight are normalized to values in interval [0, 1]. weights = triangle_area_cumsum(mesh) weights /= weights[-1] while True: x = np.random.random() # Random variable from interval [0, 1] i = np.searchsorted(weights, x) # Uniformly drawn random triangle a, b, c = mesh[i] #print(a) #print(b) #print(c) #yield random_sample_triangle(a, b, c) yield random_sample_triangle(a, b, c, seed)
def __init__(self, polygon): """Polygon to sample. :param polygon: Shapely polygon or numpy array of polygon vertices. """ if isinstance(polygon, Polygon): self.vertices = np.asarray(polygon.exterior) elif isinstance(polygon, np.ndarray): self.vertices = polygon else: raise Exception("") # Triangular mesh by Delaunay triangulation algorithm self.delaunay = Delaunay(self.vertices) self.mesh = self.vertices[self.delaunay.simplices] # Cumulative sum of areas of the triangles self.weights = triangle_area_cumsum(self.mesh) self.weights /= self.weights[-1] # Normalize values to interval [0, 1]
def __init__(self, pos, res=64): from scipy.spatial.qhull import Delaunay import itertools extremes = np.array([pos.min(axis=0), pos.max(axis=0)]) diffs = extremes[1] - extremes[0] extremes[0] -= diffs extremes[1] += diffs eidx = np.array( list( itertools.product(*([[0] * (pos.shape[1] - 1) + [1]] * pos.shape[1])))) pidx = np.tile(np.arange(pos.shape[1])[np.newaxis], (len(eidx), 1)) self.n_extra = pidx.shape[0] outer_pts = extremes[eidx, pidx] pos = np.concatenate((pos, outer_pts)) self.tri = Delaunay(pos) self.set_Grid()
def draw_voronoi_dual(self): """ Draws the Delaunay triangulation of cell centers. """ points = [center.loc for center in self.centers] points.append(self.color_center.loc) try: dual = Delaunay(points) except (QhullError, ValueError): #Either too few points or points are degenerate. return simplices = [[dual.points[i].astype(int) for i in simplex] for simplex in dual.simplices] if self.fill: for simplex in simplices: polygon(self.WINDOW, self.color(simplex, dual=True), simplex) if self.outline: for simplex in simplices: aalines(self.WINDOW, (255, 255, 255), True, simplex, 1)
def simplex_edge_difference(X): tri = Delaunay(X, qhull_options="QJ") simplices = [] for e in tri.simplices: diff = X[e[1:]] - X[e[0]] det = np.linalg.det(diff) if det > 1e-6: simplices.append(e) val = [] for triangle in simplices: dists = np.zeros(len(triangle)) for i in range(len(triangle)): a, b = triangle[i], triangle[(i + 1) % len(triangle)] dists[i] = np.linalg.norm(X[a] - X[b]) val.append(dists.max() - dists.min()) val = np.array(val) return val.mean()
if ( draw_polyhedra and len(connected_sites) > 3 and not connected_sites_not_drawn and not any(not_most_electro_negative) ): if explicitly_calculate_polyhedra_hull: try: # all_positions = [[0, 0, 0], [0, 0, 10], [0, 10, 0], [10, 0, 0]] # gives... # .convex_hull = [[2, 3, 0], [1, 3, 0], [1, 2, 0], [1, 2, 3]] # .vertex_neighbor_vertices = [1, 2, 3, 2, 3, 0, 1, 3, 0, 1, 2, 0] vertices_indices = Delaunay(all_positions).convex_hull except Exception as e: vertices_indices = [] vertices = [ all_positions[idx] for idx in chain.from_iterable(vertices_indices) ] polyhedron = [Surface(positions=vertices, color=site_color)] else: polyhedron = [Convex(positions=all_positions, color=site_color)] return Scene( self.species_string,
def get_site_scene( self, connected_sites: List[ConnectedSite] = None, # connected_site_metadata: None, # connected_sites_to_draw, connected_sites_not_drawn: List[ConnectedSite] = None, hide_incomplete_edges: bool = False, incomplete_edge_length_scale: Optional[float] = 1.0, connected_sites_colors: Optional[List[str]] = None, connected_sites_not_drawn_colors: Optional[List[str]] = None, origin: Optional[List[float]] = None, draw_polyhedra: bool = True, explicitly_calculate_polyhedra_hull: bool = False, bond_radius: float = 0.1, legend: Optional[Legend] = None, ) -> Scene: """ Args: connected_sites: connected_sites_not_drawn: hide_incomplete_edges: incomplete_edge_length_scale: connected_sites_colors: connected_sites_not_drawn_colors: origin: explicitly_calculate_polyhedra_hull: legend: Returns: """ atoms = [] bonds = [] polyhedron = [] legend = legend or Legend(self) # for disordered structures is_ordered = self.is_ordered phiStart, phiEnd = None, None occu_start = 0.0 position = self.coords.tolist() for idx, (sp, occu) in enumerate(self.species.items()): if isinstance(sp, DummySpecie): cube = Cubes(positions=[position], color=legend.get_color(sp, site=self), width=0.4) atoms.append(cube) else: color = legend.get_color(sp, site=self) radius = legend.get_radius(sp, site=self) # TODO: make optional/default to None # in disordered structures, we fractionally color-code spheres, # drawing a sphere segment from phi_end to phi_start # (think a sphere pie chart) if not is_ordered: phi_frac_end = occu_start + occu phi_frac_start = occu_start occu_start = phi_frac_end phiStart = phi_frac_start * np.pi * 2 phiEnd = phi_frac_end * np.pi * 2 name = str(sp) if occu != 1.0: name += " ({}% occupancy)".format(occu) name += f" ({position[0]:.3f}, {position[1]:.3f}, {position[2]:.3f})" sphere = Spheres( positions=[position], color=color, radius=radius, phiStart=phiStart, phiEnd=phiEnd, clickable=True, tooltip=name, ) atoms.append(sphere) if not is_ordered and not np.isclose(phiEnd, np.pi * 2): # if site occupancy doesn't sum to 100%, cap sphere sphere = Spheres( positions=[position], color="#ffffff", radius=self.properties["display_radius"][0], phiStart=phiEnd, phiEnd=np.pi * 2, ) atoms.append(sphere) if connected_sites: # TODO: more graceful solution here # if ambiguous (disordered), re-use last color used site_color = color # TODO: can cause a bug if all vertices almost co-planar # necessary to include center site in case it's outside polyhedra all_positions = [self.coords] for idx, connected_site in enumerate(connected_sites): connected_position = connected_site.site.coords bond_midpoint = np.add(position, connected_position) / 2 if connected_sites_colors: color = connected_sites_colors[idx] else: color = site_color cylinder = Cylinders( positionPairs=[[position, bond_midpoint.tolist()]], color=color, radius=bond_radius, ) bonds.append(cylinder) all_positions.append(connected_position.tolist()) if connected_sites_not_drawn and not hide_incomplete_edges: for idx, connected_site in enumerate(connected_sites_not_drawn): connected_position = connected_site.site.coords bond_midpoint = (incomplete_edge_length_scale * np.add(position, connected_position) / 2) if connected_sites_not_drawn_colors: color = connected_sites_not_drawn_colors[idx] else: color = site_color cylinder = Cylinders( positionPairs=[[position, bond_midpoint.tolist()]], color=color, radius=bond_radius, ) bonds.append(cylinder) all_positions.append(connected_position.tolist()) # ensure intersecting polyhedra are not shown, defaults to choose by electronegativity not_most_electro_negative = map( lambda x: (x.site.specie < self.specie) or (x.site.specie == self.specie), connected_sites, ) all_positions = [list(p) for p in all_positions] if (draw_polyhedra and len(connected_sites) > 3 and not connected_sites_not_drawn and not any(not_most_electro_negative)): if explicitly_calculate_polyhedra_hull: try: # all_positions = [[0, 0, 0], [0, 0, 10], [0, 10, 0], [10, 0, 0]] # gives... # .convex_hull = [[2, 3, 0], [1, 3, 0], [1, 2, 0], [1, 2, 3]] # .vertex_neighbor_vertices = [1, 2, 3, 2, 3, 0, 1, 3, 0, 1, 2, 0] vertices_indices = Delaunay(all_positions).convex_hull except Exception as e: vertices_indices = [] vertices = [ all_positions[idx] for idx in chain.from_iterable(vertices_indices) ] polyhedron = [Surface(positions=vertices, color=site_color)] else: polyhedron = [ Convex(positions=all_positions, color=site_color) ] return Scene( self.species_string, [ Scene("atoms", contents=atoms), Scene("bonds", contents=bonds), Scene("polyhedra", contents=polyhedron), ], origin=origin, )
import numpy as np import json from scipy.spatial.qhull import Delaunay with open("position.json", 'r') as w: original_data = json.load(w)[0]['Argentina'] points = np.zeros([10, 2]) for i in range(10): points[i, 0] = original_data[i]['x'] points[i, 1] = original_data[i]['y'] tri = Delaunay(points) import matplotlib.pyplot as plt plt.triplot(points[:, 0], points[:, 1], tri.simplices.copy()) plt.plot(points[:, 0], points[:, 1], 'o') plt.show()
def _is_inside_scaffold(scaffold_positions: np.ndarray, new_position: np.ndarray): hull = ConvexHull(scaffold_positions, incremental=False) vertices = scaffold_positions[hull.vertices] delaunay = Delaunay(vertices) return delaunay.find_simplex(new_position) >= 0
def cube_frac2cart(cvalues, v1, v2, v3, centre=(0., 0., 0.), min_voxels=None, max_voxels=1000000, interp='linear', make_cubic=False, bval=False): """convert a 3d cube of values, whose indexes relate to fractional coordinates of v1,v2,v3, into a cube of values in the cartesian basis (using a background value for coordinates outside the bounding box of v1,v2,v3) NB: there may be some edge effects for smaller cubes Properties ---------- values : array((N,M,L)) values in fractional basis v1 : array((3,)) v2 : array((3,)) v3 : array((3,)) centre : array((3,)) cartesian coordinates for centre of v1, v2, v3 min_voxels : int or None minimum number of voxels in returned cube. If None, compute base on input cube max_voxels : int or None maximum number of voxels in returned cube. If None, compute base on input cube interp : str interpolation mode; 'nearest' or 'linear' make_cubic: bool if True, ensure all final cartesian cube sides are of the same length bval: float background value to use outside the bounding box of the cube. If False, use numpy.nan Returns ------- B : array((P,Q,R)) where P,Q,R <= longest_side min_bounds : array((3,)) xmin,ymin,zmin max_bounds : array((3,)) xmax,ymax,zmax Example ------- >>> from pprint import pprint >>> import numpy as np >>> fcube = np.array( ... [[[1.,5.], ... [3.,7.]], ... [[2.,6.], ... [4.,8.]]]) ... >>> ncube, min_bound, max_bound = cube_frac2cart(fcube, [1.,0.,0.], [0.,1.,0.], [0.,0.,1.], min_voxels=30) >>> min_bound.tolist() [-0.5, -0.5, -0.5] >>> max_bound.tolist() [0.5, 0.5, 0.5] >>> pprint(ncube.round(1).tolist()) [[[1.0, 1.0, 3.0, 5.0], [1.0, 1.0, 3.0, 5.0], [2.0, 2.0, 4.0, 6.0], [3.0, 3.0, 5.0, 7.0]], [[1.0, 1.0, 3.0, 5.0], [1.0, 1.0, 3.0, 5.0], [2.0, 2.0, 4.0, 6.0], [3.0, 3.0, 5.0, 7.0]], [[1.5, 1.5, 3.5, 5.5], [1.5, 1.5, 3.5, 5.5], [2.5, 2.5, 4.5, 6.5], [3.5, 3.5, 5.5, 7.5]], [[2.0, 2.0, 4.0, 6.0], [2.0, 2.0, 4.0, 6.0], [3.0, 3.0, 5.0, 7.0], [4.0, 4.0, 6.0, 8.0]]] >>> ncube, min_bound, max_bound = cube_frac2cart(fcube, [2.,0.,0.], [0.,1.,0.], [0.,0.,1.], min_voxels=30) >>> min_bound.tolist() [-1.0, -0.5, -0.5] >>> max_bound.tolist() [1.0, 0.5, 0.5] >>> pprint(ncube.round(1).tolist()) [[[1.0, 1.7, 4.3], [1.3, 2.0, 4.7], [2.7, 3.3, 6.0]], [[1.0, 1.7, 4.3], [1.3, 2.0, 4.7], [2.7, 3.3, 6.0]], [[1.2, 1.8, 4.5], [1.5, 2.2, 4.8], [2.8, 3.5, 6.2]], [[1.5, 2.2, 4.8], [1.8, 2.5, 5.2], [3.2, 3.8, 6.5]], [[1.8, 2.5, 5.2], [2.2, 2.8, 5.5], [3.5, 4.2, 6.8]], [[2.0, 2.7, 5.3], [2.3, 3.0, 5.7], [3.7, 4.3, 7.0]]] >>> ncube, min_bound, max_bound = cube_frac2cart(fcube, [1.,0.,0.], [0.,2.,0.], [0.,0.,1.], min_voxels=30) >>> pprint(ncube.round(1).tolist()) [[[1.0, 1.7, 4.3], [1.0, 1.7, 4.3], [1.3, 2.0, 4.7], [2.0, 2.7, 5.3], [2.7, 3.3, 6.0], [3.0, 3.7, 6.3]], [[1.2, 1.8, 4.5], [1.2, 1.8, 4.5], [1.5, 2.2, 4.8], [2.2, 2.8, 5.5], [2.8, 3.5, 6.2], [3.2, 3.8, 6.5]], [[1.8, 2.5, 5.2], [1.8, 2.5, 5.2], [2.2, 2.8, 5.5], [2.8, 3.5, 6.2], [3.5, 4.2, 6.8], [3.8, 4.5, 7.2]]] >>> ncube, min_bound, max_bound = cube_frac2cart(fcube, [1.,0.,0.], [0.,1.,0.], [0.,0.,2.], min_voxels=30) >>> pprint(ncube.round(1).tolist()) [[[1.0, 1.0, 1.7, 3.0, 4.3, 5.0], [1.3, 1.3, 2.0, 3.3, 4.7, 5.3], [2.7, 2.7, 3.3, 4.7, 6.0, 6.7]], [[1.2, 1.2, 1.8, 3.2, 4.5, 5.2], [1.5, 1.5, 2.2, 3.5, 4.8, 5.5], [2.8, 2.8, 3.5, 4.8, 6.2, 6.8]], [[1.8, 1.8, 2.5, 3.8, 5.2, 5.8], [2.2, 2.2, 2.8, 4.2, 5.5, 6.2], [3.5, 3.5, 4.2, 5.5, 6.8, 7.5]]] >>> ncube, min_bound, max_bound = cube_frac2cart(fcube, [1.,0.,0.], [.7,.7,0.], [0.,0.,1.], min_voxels=30) >>> min_bound.tolist() [-0.85, -0.35, -0.5] >>> max_bound.tolist() [0.85, 0.35, 0.5] >>> pprint(ncube.round(1).tolist()) [[[1.0, 1.7, 4.3], [nan, nan, nan]], [[1.1, 1.7, 4.4], [nan, nan, nan]], [[1.6, 2.3, 5.0], [2.0, 2.7, 5.3]], [[2.0, 2.7, 5.3], [2.5, 3.2, 5.8]], [[nan, nan, nan], [3.0, 3.7, 6.3]], [[nan, nan, nan], [nan, nan, nan]]] >>> ncube, min_bound, max_bound = cube_frac2cart(fcube, [2.,0.,0.], [0.,1.,0.], [0.,0.,1.], min_voxels=30, make_cubic=True) >>> min_bound.tolist() [-1.0, -0.5, -0.5] >>> max_bound.tolist() [1.0, 1.5, 1.5] >>> pprint(ncube.round(1).tolist()) [[[1.0, 3.0, 5.0, nan], [2.0, 4.0, 6.0, nan], [3.0, 5.0, 7.0, nan], [nan, nan, nan, nan]], [[1.0, 3.0, 5.0, nan], [2.0, 4.0, 6.0, nan], [3.0, 5.0, 7.0, nan], [nan, nan, nan, nan]], [[1.5, 3.5, 5.5, nan], [2.5, 4.5, 6.5, nan], [3.5, 5.5, 7.5, nan], [nan, nan, nan, nan]], [[2.0, 4.0, 6.0, nan], [3.0, 5.0, 7.0, nan], [4.0, 6.0, 8.0, nan], [nan, nan, nan, nan]]] """ cvalues = np.asarray(cvalues, dtype=float) min_voxels = min_voxels if min_voxels is not None else 1 longest_side = max(cvalues.shape) if (min_voxels is not None) and (max_voxels is not None) and min_voxels > max_voxels: raise ValueError( "minimum dimension ({0}) must be less than or equal to maximum distance ({1})".format(min_voxels, max_voxels)) if min_voxels is not None: longest_side = max(longest_side, int(min_voxels ** (1 / 3.))) if max_voxels is not None: longest_side = min(longest_side, int(max_voxels ** (1 / 3.))) # convert to numpy arrays origin = np.asarray([0, 0, 0], dtype=float) v1 = np.asarray(v1) v2 = np.asarray(v2) v3 = np.asarray(v3) # -------------- # expand cube by one unit in all directions (for interpolation) cvalues = np.concatenate((np.array(cvalues[0], ndmin=3), cvalues, np.array(cvalues[-1], ndmin=3)), axis=0) start = np.transpose(np.array(cvalues[:, :, 0], ndmin=3), axes=[1, 2, 0]) end = np.transpose(np.array(cvalues[:, :, -1], ndmin=3), axes=[1, 2, 0]) cvalues = np.concatenate((start, cvalues, end), axis=2) start = np.transpose(np.array(cvalues[:, 0, :], ndmin=3), axes=[1, 0, 2]) end = np.transpose(np.array(cvalues[:, -1, :], ndmin=3), axes=[1, 0, 2]) cvalues = np.concatenate((start, cvalues, end), axis=1) # -------------- # -------------- # create fractional coordinate axes for cube f_axes = [] for i, v in enumerate([v1, v2, v3]): step = 1. / (cvalues.shape[i] - 2.) ax = np.linspace(0, 1 + step, cvalues.shape[i]) - step / 2. f_axes.append(ax) # -------------- # -------------- # get bounding box for cartesian vectors and compute its volume and extents bbox_pts = np.asarray([origin, v1, v2, v3, v1 + v2, v1 + v3, v1 + v2 + v3, v2 + v3]) hull = Delaunay(bbox_pts) bbox_x, bbox_y, bbox_z = bbox_pts.T xmin, xmax, ymin, ymax, zmin, zmax = (bbox_x.min(), bbox_x.max(), bbox_y.min(), bbox_y.max(), bbox_z.min(), bbox_z.max()) # l,r,bottom,top x_length = abs(xmin - xmax) y_length = abs(ymin - ymax) z_length = abs(zmin - zmax) if make_cubic: # min_bound, max_bound = min(xmin, ymin, zmin), max(xmax, ymax, zmin) max_length = max(x_length, y_length, z_length) xmax += max_length - (xmin + x_length) ymax += max_length - (ymin + y_length) zmax += max_length - (zmin + z_length) x_length = y_length = z_length = max_length # -------------- # -------------- # compute new cube size, in which the bounding box can fit xlen, ylen, zlen = 0, 0, 0 while xlen * ylen * zlen < min_voxels: if x_length == max([x_length, y_length, z_length]): xlen = longest_side ylen = int(longest_side * y_length / float(x_length)) zlen = int(longest_side * z_length / float(x_length)) elif y_length == max([x_length, y_length, z_length]): ylen = longest_side xlen = int(longest_side * x_length / float(y_length)) zlen = int(longest_side * z_length / float(y_length)) else: zlen = longest_side xlen = int(longest_side * x_length / float(z_length)) ylen = int(longest_side * y_length / float(z_length)) longest_side += 1 # -------------- # -------------- # create a new, initially empty cube new_array = np.full((xlen, ylen, zlen), bval if bval is not False else np.nan) # get the indexes for each voxel in cube xidx, yidx, zidx = np.meshgrid(range(new_array.shape[0]), range(new_array.shape[1]), range(new_array.shape[2])) xidx = xidx.flatten() yidx = yidx.flatten() zidx = zidx.flatten() xyzidx = np.concatenate((np.array(xidx, ndmin=2).T, np.array(yidx, ndmin=2).T, np.array(zidx, ndmin=2).T), axis=1) # -------------- # -------------- # get the cartesian coordinates for each voxel xyz = np.concatenate((np.array(xmin + (xyzidx[:, 0] * abs(xmin - xmax) / float(xlen)), ndmin=2).T, np.array(ymin + (xyzidx[:, 1] * abs(ymin - ymax) / float(ylen)), ndmin=2).T, np.array(zmin + (xyzidx[:, 2] * abs(zmin - zmax) / float(zlen)), ndmin=2).T), axis=1) # create a mask for filtering all cartesian coordinates which sit inside the bounding box inside_mask = hull.find_simplex(xyz) >= 0 # -------------- # -------------- # for all coordinates inside the bounding box, get their equivalent fractional position and set interpolated value basis_transform = np.linalg.inv(np.transpose([v1, v2, v3])) uvw = np.einsum('...jk,...k->...j', basis_transform, xyz[inside_mask]) mask_i, mask_j, mask_k = xyzidx[inside_mask][:, 0], xyzidx[inside_mask][:, 1], xyzidx[inside_mask][:, 2] new_array[mask_i, mask_j, mask_k] = interpn(f_axes, cvalues, uvw, bounds_error=True, method=interp) # -------------- mins = np.array((xmin, ymin, zmin)) - 0.5 * (v1 + v2 + v3) + np.array(centre) maxes = np.array((xmax, ymax, zmax)) - 0.5 * (v1 + v2 + v3) + np.array(centre) return new_array, mins, maxes
(dcoords, cv_icoords, cv_xcoords, icoords, xcoords, ccoord)) point_type = np.concatenate( (0 * np.ones(dcoords.shape[0]), 1 * np.ones(cv_icoords.shape[0]), 2 * np.ones(cv_xcoords.shape[0]), 3 * np.ones(icoords.shape[0]), 4 * np.ones(xcoords.shape[0]), 5 * np.ones(ccoord.shape[0]))) tcoords = np.vstack((dcoords, cv_icoords, cv_xcoords, icoords, ccoord)) point_type = np.concatenate( (0 * np.ones(dcoords.shape[0]), 1 * np.ones(cv_icoords.shape[0]), 2 * np.ones(cv_xcoords.shape[0]), 3 * np.ones(icoords.shape[0]), 5 * np.ones(ccoord.shape[0]))) tcoords = np.vstack((dcoords, ccoord)) point_type = np.concatenate( (0 * np.ones(dcoords.shape[0]), 5 * np.ones(ccoord.shape[0]))) trian = Delaunay(tcoords) df = pd.DataFrame(point_type[trian.simplices]) df.to_excel("delaunay.xlsx", index=False) modified_simplices = trian.simplices[np.sum( trian.simplices == trian.simplices.max(), axis=1) == 1] a = tl.DelaunaySingle(trian.points, trian.simplices, point_type, 2) #a = tl.DelaunaySingle(trian.points, trian.simplices,point_type,2) # import pdb; pdb.set_trace() #ncoords = np.vstack((dcoords, extra_coords_internal, extra_coords_external)) # a = tl.DelaunaySingle(, good_tetra, neighbors, x) # # #perfoming delaunay triangulation # # trian = Delaunay(dcoords)
def test_meshing(): """ meshing example demonstrates the use of multiplicity, and group.median """ #set up some random points, and get their delaunay triangulation points = np.random.random((20000,2))*2-1 points = points[np.linalg.norm(points,axis=1) < 1] from scipy.spatial.qhull import Delaunay d = Delaunay(points) tris = d.simplices #the operations provided in this module allow us to express potentially complex #computational geometry questions elegantly in numpy #Delaunay.neighbors could be used as well, #but the point is to express this in pure numpy, without additional library functionaoty edges = tris[:,[[0,1],[1,2],[2,0]]].reshape(-1,2) sorted_edges = np.where(edges[:,0:1]<edges[:,1:2], edges, edges[:,::-1]) #we test to see how often each edge occurs, or how many indicent simplices it has #this is a very general method of finding the boundary of any topology #and we can do so here with only one simple and readable command, multiplicity == 1 if backwards_compatible: boundary_edges = edges[multiplicity(sorted_edges, axis=0)==1] else: boundary_edges = edges[multiplicity(sorted_edges)==1] boundary_points = unique(boundary_edges) if False: print (boundary_edges) print (incidence(boundary_edges)) #create some random values on faces #we want to smooth them over the mesh to create a nice hilly landscape face_values = np.random.normal(size=d.nsimplex) #add some salt and pepper noise, to make our problem more interesting face_values[np.random.randint(d.nsimplex, size=10)] += 1000 #start with a median step, to remove salt-and-pepper noise #toggle to mean to see the effect of the median filter g = group_by(tris.flatten()) prestep = g.median if True else g.mean vertex_values = prestep(np.repeat(face_values, 3))[1] vertex_values[boundary_points] = 0 #actually, we can compute the mean without grouping tris_per_vert = g.count def scatter(x): r = np.zeros(d.npoints, x.dtype) for idx in tris.T: np.add.at(r, idx, x) return r / tris_per_vert def gather(x): return x[tris].mean(axis=1) #iterate a little for i in range(100): face_values = gather(vertex_values) vertex_values = scatter(face_values) vertex_values[boundary_points] = 0 #display our nicely rolling hills and their boundary import matplotlib.pyplot as plt x, y = points.T plt.tripcolor(x,y, triangles = tris, facecolors = face_values) plt.scatter(x[boundary_points], y[boundary_points]) plt.xlim(-1,1) plt.ylim(-1,1) plt.axis('equal') plt.show()