def get_coors_in_ball(coors, centre, radius, inside=True): """ Return indices of coordinates inside or outside a ball given by centre and radius. Notes ----- All float comparisons are done using `<=` or `>=` operators, i.e. the points on the boundaries are taken into account. """ coors = nm.asarray(coors) centre = nm.asarray(centre) vec = coors - centre[None, :] if inside: out = nm.where(norm(vec) <= radius)[0] else: out = nm.where(norm(vec) >= radius)[0] return out
def get_face_areas(faces, coors): """ Get areas of planar convex faces in 2D and 3D. Parameters ---------- faces : array, shape (n, m) The indices of `n` faces with `m` vertices into `coors`. coors : array The coordinates of face vertices. Returns ------- areas : array The areas of the faces. """ faces = nm.asarray(faces) coors = nm.asarray(coors) n_v = faces.shape[1] if n_v == 3: aux = coors[faces] v1 = aux[:, 1, :] - aux[:, 0, :] v2 = aux[:, 2, :] - aux[:, 0, :] if coors.shape[1] == 3: areas = 0.5 * norm(nm.cross(v1, v2)) else: areas = 0.5 * nm.abs(nm.cross(v1, v2)) elif n_v == 4: areas1 = get_face_areas(faces[:, [0, 1, 2]], coors) areas2 = get_face_areas(faces[:, [0, 2, 3]], coors) areas = areas1 + areas2 else: raise ValueError('unsupported faces! (%d vertices)' % n_v) return areas
def get_simplex_circumcentres(coors, force_inside_eps=None): """ Compute the circumcentres of `n_s` simplices in 1D, 2D and 3D. Parameters ---------- coors : array The coordinates of the simplices with `n_v` vertices given in an array of shape `(n_s, n_v, dim)`, where `dim` is the space dimension and `2 <= n_v <= (dim + 1)`. force_inside_eps : float, optional If not None, move the circumcentres that are outside of their simplices or closer to their boundary then `force_inside_eps` so that they are inside the simplices at the distance given by `force_inside_eps`. It is ignored for edges. Returns ------- centres : array The circumcentre coordinates as an array of shape `(n_s, dim)`. """ n_s, n_v, dim = coors.shape assert_(2 <= n_v <= (dim + 1)) assert_(1 <= dim <= 3) if n_v == 2: # Edges. centres = 0.5 * nm.sum(coors, axis=1) else: if n_v == 3: # Triangles. a2 = norm(coors[:,1,:] - coors[:,2,:], squared=True) b2 = norm(coors[:,0,:] - coors[:,2,:], squared=True) c2 = norm(coors[:,0,:] - coors[:,1,:], squared=True) bar_coors = nm.c_[a2 * (-a2 + b2 + c2), b2 * (a2 - b2 + c2), c2 * (a2 + b2 - c2)] elif n_v == 4: # Tetrahedrons. a2 = norm(coors[:,2,:] - coors[:,1,:], squared=True) b2 = norm(coors[:,2,:] - coors[:,0,:], squared=True) c2 = norm(coors[:,1,:] - coors[:,0,:], squared=True) d2 = norm(coors[:,3,:] - coors[:,0,:], squared=True) e2 = norm(coors[:,3,:] - coors[:,1,:], squared=True) f2 = norm(coors[:,3,:] - coors[:,2,:], squared=True) bar_coors = nm.c_[(d2 * a2 * (f2 + e2 - a2) + b2 * e2 * (a2 + f2 - e2) + c2 * f2 * (e2 + a2 - f2) - 2 * a2 * e2 * f2), (e2 * b2 * (f2 + d2 - b2) + c2 * f2 * (d2 + b2 - f2) + a2 * d2 * (b2 + f2 - d2) - 2 * b2 * d2 * f2), (f2 * c2 * (e2 + d2 - c2) + b2 * e2 * (d2 + c2 - e2) + a2 * d2 * (c2 + e2 - d2) - 2 * c2 * e2 * d2), (d2 * a2 * (b2 + c2 - a2) + e2 * b2 * (c2 + a2 - b2) + f2 * c2 * (a2 + b2 - c2) - 2 * a2 * b2 * c2)] else: raise ValueError('unsupported simplex! (%d vertices)' % n_v) bar_coors /= nm.sum(bar_coors, axis=1)[:,None] if force_inside_eps is not None: bc = 1.0 / n_v limit = 0.9 * bc bar_centre = nm.array([bc] * n_v, dtype=nm.float64) eps = float(force_inside_eps) if eps > limit: output('force_inside_eps is too big, adjusting! (%e -> %e)' % (eps, limit)) eps = limit # Flag is True where the barycentre is closer to the simplex # boundary then eps, or outside of the simplex. mb = nm.min(bar_coors, axis=1) flag = nm.where(mb < eps)[0] # Move the bar_coors[flag] towards bar_centre so that it is # inside at the eps distance. mb = mb[flag] alpha = ((eps - mb) / (bar_centre[0] - mb))[:,None] bar_coors[flag] = (1.0 - alpha) * bar_coors[flag] \ + alpha * bar_centre[None,:] centres = transform_bar_to_space_coors(bar_coors, coors) return centres