upxo.geoEntities.mulpoint3d module
Multi-point 3D geometric entity module for UPXO.
Provides MPoint3d, a collection class for N 3-D points stored as a
single (N, 3) NumPy array. Supports construction from coordinate arrays,
separated x/y/z lists, regular grids, and other UPXO point collections;
rigid-body operations (translation, rotation); spatial queries (kd-tree,
nearest neighbours, distance computations); and surface-topology checks for
voxel-based meshes.
Classes
- MPoint3d
Collection of 3-D points backed by an
(N, 3)NumPy array.
Usage
from upxo.geoEntities.mulpoint3d import MPoint3d as mp3d
@author: Dr. Sunil Anandatheertha
- class upxo.geoEntities.mulpoint3d.MPoint3d(coords=None)[source]
Bases:
objectCollection of N 3-D points stored as a single
(N, 3)NumPy array.Provides construction class-methods, rigid-body operations (translation, rotation), spatial-query helpers (kd-tree, neighbour search, distance calculations), and surface-topology checks for voxel-based meshes.
- coords
Row-major array of 3-D coordinates: each row is
[x, y, z].- Type:
numpy.ndarray, shape (N, 3)
- tree
Spatial index, populated on demand by
maketree().- Type:
scipy.spatial.cKDTree or None
- pdist
Reference to
scipy.spatial.distance.pdistfor pairwise distances.- Type:
callable
- Standard coordinate format
- --------------------------
- ::
- coords = np.array([[0, 0, 0],
[1, 1, 1], [2, 3, 3], [4, 5, 6]])
- coords
- pdist
- add(toadd=None, operation='add')[source]
Add to or append coordinates in
self.coords.- Parameters:
toadd (scalar, list, or numpy.ndarray, optional) –
Value(s) to add or append. Accepted shapes / types:
scalar number — broadcast-added to every coordinate.
[x, y, z]— added to every row as a 3-element offset.[[x, y, z]]— same as above, single-row list.(N, 3)array — element-wise add; must matchself.n.(3, N)array (transposed) — transposed before adding.
When
operation='append', the same shapes are supported but the rows are appended instead of added.operation ({'add', 'append'}, optional) –
'add'modifies coordinates in place;'append'growsself.coordsby the supplied rows. Default is'add'.
- Returns:
Modifies
self.coordsin place.- Return type:
None
Examples
Example 1 — scalar broadcast addition:
from upxo.geoEntities.mulpoint3d import MPoint3d as mp3d mulpoint3d = mp3d.from_coords(np.random.random((10, 3))) mulpoint3d.add(toadd=10, operation='add')
Example 2 — 3-element offset vector:
from upxo.geoEntities.mulpoint3d import MPoint3d as mp3d mulpoint3d = mp3d.from_coords(np.random.random((10, 3))) mulpoint3d.add(toadd=[-10, 20, 0], operation='add')
Example 3 — element-wise
(N, 3)array addition:from upxo.geoEntities.mulpoint3d import MPoint3d as mp3d mulpoint3d = mp3d.from_coords(np.random.random((10, 3))) mulpoint3d.add(toadd=np.random.random((mulpoint3d.n, 3)), operation='add')
Example 4 — transposed
(3, N)array addition:from upxo.geoEntities.mulpoint3d import MPoint3d as mp3d mulpoint3d = mp3d.from_coords(np.random.random((10, 3))) mulpoint3d.add(toadd=np.random.random((mulpoint3d.n, 3)).T, operation='add')
Example 5 — append rows:
from upxo.geoEntities.mulpoint3d import MPoint3d as mp3d mulpoint3d = mp3d.from_coords(np.random.random((10, 3))) mulpoint3d.add(toadd=np.random.random((10, 3)), operation='append')
- classmethod from_coords(point_coords)[source]
Instantiate from an
(N, 3)array or list of coordinate triples.- Parameters:
point_coords (array-like, shape (N, 3)) – Each row is a 3-D coordinate
[x, y, z].- Returns:
New instance with
coordsset frompoint_coords.- Return type:
Examples
from upxo.geoEntities.mulpoint3d import MPoint3d as mp3d point_coords = np.array([[0, 0, 0], [1, 1, 1], [2, 3, 3], [4, 5, 6]]) MULPOINT3D = mp3d.from_coords(point_coords) print(MULPOINT3D.coords)
- classmethod from_x_y_z(x, y, z)[source]
Instantiate from separate x, y, and z coordinate arrays.
- Parameters:
- Returns:
New instance with
coordsof shape (N, 3).- Return type:
Examples
from upxo.geoEntities.mulpoint3d import MPoint3d as mp3d x, y, z = np.array([[0, 0, 0], [1, 1, 1], [2, 3, 3], [4, 5, 6]]).T MULPOINT3D = mp3d.from_x_y_z(x, y, z) print(MULPOINT3D.coords)
- classmethod from_xyz(xyz)[source]
Instantiate from a
(3, N)coordinate matrix.- Parameters:
xyz (numpy.ndarray, shape (3, N)) – Row 0 is x-coords, row 1 is y-coords, row 2 is z-coords.
- Returns:
New instance with
coordsof shape (N, 3) (transposed fromxyz).- Return type:
Examples
from upxo.geoEntities.mulpoint3d import MPoint3d as mp3d xyz = np.array([[0, 0, 0], [1, 1, 1], [2, 3, 3], [4, 5, 6]]).T MULPOINT3D = mp3d.from_xyz(xyz) print(MULPOINT3D.coords)
- classmethod from_mulpoint2d(mp2d, zloc=0.0)[source]
Construct from a
MPoint2dby appending a constant z-value. Not yet implemented.
- classmethod from_mulpoint3d(mulpoint3d=None, dxyz=[0.0, 0.0, 0.0], translate_ref=[0.0, 0.0, 0.0], rot=[0.0, 0.0, 0.0], rot_ref=[0.0, 0.0, 0.0], degree=True)[source]
Instantiate by applying rotation and translation to an existing
MPoint3d.- Parameters:
mulpoint3d (MPoint3d) – Source point collection to transform.
dxyz (list of float, optional) – Translation offsets
[dx, dy, dz]applied after rotation. Default is[0.0, 0.0, 0.0].translate_ref (list of float, optional) – Reference point for the translation step; the cloud is shifted so that
translate_refmaps to the origin before rotation. Default is[0.0, 0.0, 0.0].rot (list of float, optional) – Rotation angles
[rx, ry, rz]about the x, y, and z axes (CCW positive about positive axes). Default is[0.0, 0.0, 0.0].rot_ref (list of float, optional) – Centre of rotation in 3-D space. Default is
[0.0, 0.0, 0.0].degree (bool, optional) – If
True,rotvalues are interpreted as degrees; ifFalse, as radians. Default isTrue.
- Returns:
New instance with transformed coordinates.
- Return type:
Notes
Rotation is applied as successive Rx → Ry → Rz matrix multiplication about
rot_ref. Translation is applied last by centering ontranslate_refand addingdxyz. Refer to the examples for a concrete demonstration of each degree of freedom.Examples
Example 1 — no rotation, no translation (identity):
from upxo.geoEntities.mulpoint3d import MPoint3d as mp3d point_coords = np.array([[0, 0, 0], [1, 1, 1], [2, 2, 2], [3, 3, 3]]) mulpoint3d = mp3d.from_coords(point_coords) MULPOINT3D = mp3d.from_mulpoint3d(mulpoint3d=mulpoint3d, dxyz=[0.0, 0.0, 0.0], translate_ref=mulpoint3d.centroid, rot=[0.0, 0.0, 0.0], rot_ref=[0.0, 0.0, 0.0], degree=True) mulpoint3d.plot(MULPOINT3D.coords)
Example 2 — 45° rotation about x-axis, centred at origin:
from upxo.geoEntities.mulpoint3d import MPoint3d as mp3d point_coords = np.array([[0, 0, 0], [0, 1, 1], [0, 2, 2], [0, 3, 3]]) mulpoint3d = mp3d.from_coords(point_coords) MULPOINT3D = mp3d.from_mulpoint3d(mulpoint3d=mulpoint3d, dxyz=[0.0, 0.0, 0.0], translate_ref=mulpoint3d.centroid, rot=[45, 0.0, 0.0], rot_ref=[0.0, 0.0, 0.0], degree=True) mulpoint3d.plot(MULPOINT3D.coords)
Example 3 — 45° rotation about x-axis, non-origin rotation centre:
from upxo.geoEntities.mulpoint3d import MPoint3d as mp3d point_coords = np.array([[0, 0, 0], [0, 1, 1], [0, 2, 2], [0, 3, 3]]) mulpoint3d = mp3d.from_coords(point_coords) MULPOINT3D = mp3d.from_mulpoint3d(mulpoint3d=mulpoint3d, dxyz=[0.0, 0.0, 0.0], translate_ref=[0.0, 0.0, 0.0], rot=[45, 0.0, 0.0], rot_ref=[2.0, 0.0, 0.0], degree=True) mulpoint3d.plot(MULPOINT3D.coords)
Example 4 — rotation about x with centroid as both translate_ref and rot_ref:
from upxo.geoEntities.mulpoint3d import MPoint3d as mp3d point_coords = np.array([[0, 0, 0], [0, 1, 1], [0, 2, 2], [0, 3, 3]]) mulpoint3d = mp3d.from_coords(point_coords) MULPOINT3D = mp3d.from_mulpoint3d(mulpoint3d=mulpoint3d, dxyz=[0.0, 0.0, 0.0], translate_ref=mulpoint3d.centroid, rot=[45, 0.0, 0.0], rot_ref=[2.0, 0.0, 0.0], degree=True) mulpoint3d.plot(MULPOINT3D.coords)
Example 5 — rotation with rot_ref at centroid:
from upxo.geoEntities.mulpoint3d import MPoint3d as mp3d point_coords = np.array([[0, 0, 0], [0, 1, 1], [0, 2, 2], [0, 3, 3]]) mulpoint3d = mp3d.from_coords(point_coords) MULPOINT3D = mp3d.from_mulpoint3d(mulpoint3d=mulpoint3d, dxyz=[0.0, 0.0, 0.0], translate_ref=mulpoint3d.centroid, rot=[45, 0.0, 0.0], rot_ref=mulpoint3d.centroid, degree=True) mulpoint3d.plot(MULPOINT3D.coords)
Example 6 — combined rotation and x-translation:
from upxo.geoEntities.mulpoint3d import MPoint3d as mp3d point_coords = np.array([[0, 0, 0], [0, 1, 1], [0, 2, 2], [0, 3, 3]]) mulpoint3d = mp3d.from_coords(point_coords) MULPOINT3D = mp3d.from_mulpoint3d(mulpoint3d=mulpoint3d, dxyz=[1.0, 0.0, 0.0], translate_ref=mulpoint3d.centroid, rot=[45, 0.0, 0.0], rot_ref=mulpoint3d.centroid, degree=True) mulpoint3d.plot(MULPOINT3D.coords)
Example 7 — 3-axis translation, no rotation:
from upxo.geoEntities.mulpoint3d import MPoint3d as mp3d point_coords = np.array([[0, 0, 0], [0, 1, 1], [0, 2, 2], [0, 3, 3]]) mulpoint3d = mp3d.from_coords(point_coords) MULPOINT3D = mp3d.from_mulpoint3d(mulpoint3d=mulpoint3d, dxyz=[1.0, 1.0, -0.5], translate_ref=mulpoint3d.centroid, rot=[0, 0.0, 0.0], rot_ref=mulpoint3d.centroid, degree=True) mulpoint3d.plot(MULPOINT3D.coords)
- classmethod from_mulsline3d(msline3d)[source]
Construct from a
MSline3dendpoint collection. Not yet implemented.
- classmethod from_xyz_grid(xspec=[0, 1, 0.25], yspec=[0, 1, 0.25], zspec=[0, 1, 0.25], dxyz=[0.0, 0.0, 0.0], translate_ref=[0.0, 0.0, 0.0], rot=[0.0, 0.0, 0.0], rot_ref=[0.0, 0.0, 0.0], degree=True)[source]
Instantiate from a regular 3-D Cartesian grid with optional rigid-body transform.
Builds a meshgrid from the three axis specifications, flattens it to an
(N, 3)array, then delegates tofrom_mulpoint3d()to apply the requested rotation and translation.- Parameters:
xspec (list of float, optional) –
[xstart, xend, xincrement]for the x-axis grid. Default is[0, 1, 0.25].yspec (list of float, optional) –
[ystart, yend, yincrement]for the y-axis grid. Default is[0, 1, 0.25].zspec (list of float, optional) –
[zstart, zend, zincrement]for the z-axis grid. Default is[0, 1, 0.25].dxyz (list of float, optional) – Translation offsets
[dx, dy, dz]. Default is[0.0, 0.0, 0.0].translate_ref (list of float or str, optional) – Reference point for translation. Pass
'centroid'to use the grid centroid, or a[x, y, z]coordinate list. Default is[0.0, 0.0, 0.0].rot (list of float, optional) – Rotation angles
[rx, ry, rz]about x, y, z axes (CCW positive). Default is[0.0, 0.0, 0.0].rot_ref (list of float, optional) – Centre of rotation. Default is
[0.0, 0.0, 0.0].degree (bool, optional) – If
True,rotis in degrees; ifFalse, in radians. Default isTrue.
- Returns:
New instance containing the grid points after the rigid-body transform.
- Return type:
Examples
Example 1 — two grids, one base and one rotated by (5°, 5°, 5°):
from upxo.geoEntities.mulpoint3d import MPoint3d as mp3d xspec, yspec, zspec = [0, 1, 0.1], [0, 1, 0.1], [0, 1, 0.1] dxyz, translate_ref = [0.0, 0.0, 0.0], [0.0, 0.0, 0.0] mulpoint3d = mp3d.from_xyz_grid(xspec=xspec, yspec=yspec, zspec=zspec, dxyz=dxyz, translate_ref=translate_ref, rot=[0.0, 0.0, 0.0], rot_ref=[0.0, 0.0, 0.0], degree=True) MULPOINT3D = mp3d.from_xyz_grid(xspec=xspec, yspec=yspec, zspec=zspec, dxyz=dxyz, translate_ref=translate_ref, rot=[5.0, 5.0, 5.0], rot_ref=[0.0, 0.0, 0.0], degree=True) MULPOINT3D.plot(mulpoint3d.coords, primary_ms=50, secondary_ms=5)
- property n
Number of points in the collection.
- property centroid
Mean 3D coordinate of all points as a
(3,)array.
- property points
Return a list of
Point3dobjects built fromself.coords.
- property x
x-coordinates of all points as a 1-D array.
- property y
y-coordinates of all points as a 1-D array.
- property z
z-coordinates of all points as a 1-D array.
- property ckd_tree
Build and return a
cKDTreefor fast nearest-neighbour queries.
- squared_distances_to_point(point)[source]
Return squared Euclidean distances from all points to
point.- Parameters:
point (Point3d or array-like) – Target point. Validated via
val_point_and_get_coord.- Returns:
Squared distance from each point in
self.coordstopoint.- Return type:
numpy.ndarray, shape (N,)
- distances_to_point(point)[source]
Return Euclidean distances from all points to
point.- Parameters:
point (Point3d or array-like) – Target point.
- Returns:
Euclidean distance from each point in
self.coordstopoint.- Return type:
numpy.ndarray, shape (N,)
- squared_distance_to_centroid(points, validate_points=True, points_type='numpy')[source]
Compute squared distances from
self.centroidto a set of 3-D points.- Parameters:
points (list of Point3d or numpy.ndarray, shape (M, 3)) – Target points to measure from
self.centroid.validate_points (bool, optional) – When
Truethe input is validated and converted automatically. When confident thatpointsis an(M, 3)NumPy array, set toFalseto skip validation overhead. Default isTrue.points_type ({'numpy', 'upxo', 'shapely', 'coord', 'coord_pair'}, optional) – Type hint used only when
validate_points=False. Use'numpy'for plain NumPy arrays. Default is'numpy'.
- Returns:
Squared Euclidean distances from each target point to
self.centroid.- Return type:
numpy.ndarray, shape (M,)
Examples
Example 1 — validated UPXO point objects:
from upxo.geoEntities.mulpoint3d import MPoint3d MULPOINT3D = MPoint3d.from_coords(np.random.random((10, 3))) POINTS = make_p3d(2 + np.random.random((10, 3)), return_type='p3d') MULPOINT3D.squared_distance_to_centroid(POINTS, validate_points=True)
Example 2 — raw NumPy array, validation skipped:
POINTS = 2 + np.random.random((10, 3)) MULPOINT3D.squared_distance_to_centroid(POINTS, validate_points=False, points_type='numpy')
- distance_to_centroid(points, validate_points=True, points_type='numpy')[source]
Compute Euclidean distances from
self.centroidto a set of 3-D points.- Parameters:
points (list of Point3d or numpy.ndarray, shape (M, 3)) – Target points.
validate_points (bool, optional) – See
squared_distance_to_centroid(). Default isTrue.points_type (str, optional) – See
squared_distance_to_centroid(). Default is'numpy'.
- Returns:
Euclidean distances from each target point to
self.centroid.- Return type:
numpy.ndarray, shape (M,)
Examples
from upxo.geoEntities.mulpoint3d import MPoint3d MULPOINT3D = MPoint3d.from_coords(np.random.random((10, 3))) POINTS = 2 + np.random.random((10, 3)) MULPOINT3D.distance_to_centroid(POINTS, validate_points=False, points_type='numpy')
- maketree(treeType='ckdtree', saa=False, throw=False, balance=True)[source]
Build a spatial index tree over
self.coords.- Parameters:
treeType ({'ckdtree', 'kdtree'}, optional) – Type of spatial index. Currently only
'ckdtree'is implemented. Default is'ckdtree'.saa (bool, optional) – If
True, store the built tree onself.tree. Default isFalse.throw (bool, optional) – If
True, return the tree object. Default isFalse.balance (bool, optional) – Passed as
balanced_treetocKDTree. Default isTrue.
- Returns:
The built tree when
throw=True; otherwiseNone.- Return type:
scipy.spatial.cKDTree or None
Examples
from upxo.geoEntities.mulpoint3d import MPoint3d as mp3d mulpoint3d = mp3d.from_coords(np.random.random((25, 3))) tree = mulpoint3d.maketree(treeType='ckdtree', throw=True) print(tree.data.shape)
- get_self_distance_max()[source]
Return the maximum pairwise distance among all points in
self.coords.
- get_self_distance_min()[source]
Return the minimum pairwise distance among all points in
self.coords.
- find_first_order_neigh_CUBIC(coord, vox_size, return_indices=True, return_coords=True, return_input_coord=False, k=1.000001)[source]
Find first-order (face+edge+vertex) neighbours of a voxel on a cubic lattice.
A point
pinself.coordsis a first-order neighbour ofcoordif|p[d] - coord[d]| <= vox_sizefor all three dimensions d (i.e. it fits within a 3×3×3 cubic stencil centred atcoord). The tolerance multiplierkavoids floating-point boundary misclassification.- Parameters:
coord (array-like, shape (3,)) – Centre voxel coordinate. Must be a member of
self.coords.vox_size (float) – Voxel edge length; defines the stencil half-width.
return_indices (bool, optional) – Include neighbour indices into
self.coordsin the output. Default isTrue.return_coords (bool, optional) – Include neighbour coordinate arrays in the output. Default is
True.return_input_coord (bool, optional) – Append
coordto the return tuple. Default isFalse.k (float, optional) – Tolerance multiplier applied to
vox_sizeto avoid floating-point boundary misses. Default is1.000001.
- Returns:
Contents depend on the flag combination:
(return_indices=True, return_coords=False)→(indices,)or(indices, coord)(return_indices=False, return_coords=True)→(coords,)or(coords, coord)(return_indices=True, return_coords=True)→(indices, coords, coord)or(indices, coords)
- Return type:
Notes
Designed for cubic lattices only. A voxel
[x, y, z]is a first-order neighbour of[cx, cy, cz]when|x-cx| <= A,|y-cy| <= B,|z-cz| <= Cwhere A = B = C =vox_size. This includes up to 26 neighbours in a full 3×3×3 grid.Examples
from upxo.geoEntities.mulpoint3d import MPoint3d as mp3d vs = 0.1 xspec, yspec, zspec = [0, 1, vs], [0, 1, vs], [0, 1, vs] X, Y, Z = np.meshgrid(np.arange(xspec[0], xspec[1], xspec[2]), np.arange(yspec[0], yspec[1], yspec[2]), np.arange(zspec[0], zspec[1], zspec[2])) mp = mp3d.from_coords(np.vstack((X.ravel(), Y.ravel(), Z.ravel())).T) mp.find_first_order_neigh_CUBIC((0.5, 0.5, 0.5), vs)
- check_if_point_can_host_a_single_surface_CUBIC(coord, vs)[source]
Check whether a voxel can have a single non-self-intersecting surface through it.
Given that the 3×3×3 neighbourhood contains 27 voxels in various ON/OFF states, a single surface can pass through the centre voxel and all ON-state neighbours only when the number of ON-state neighbours is at most 4 (empirical threshold; equivalent to at most 5 points including the centre).
The
CUBICsuffix indicates this method is designed for cubic lattices only.- Parameters:
- Returns:
Trueif 2–4 same-state neighbours exist (a surface can be formed),Falseif outside that range,Noneifcoordis not found inself.coords.- Return type:
bool or None
Examples
from upxo.geoEntities.mulpoint3d import MPoint3d as mp3d vs = 0.1 xspec, yspec, zspec = [0, 1, vs], [0, 1, vs], [0, 1, vs] X, Y, Z = np.meshgrid(np.arange(xspec[0], xspec[1], xspec[2]), np.arange(yspec[0], yspec[1], yspec[2]), np.arange(zspec[0], zspec[1], zspec[2])) mp = mp3d.from_coords(np.vstack((X.ravel(), Y.ravel(), Z.ravel())).T) coords = mp.find_first_order_neigh_CUBIC((0.5, 0.5, 0.5), vs, return_indices=False, return_coords=True, return_input_coord=False)[0] coord = np.array([0.5, 0.5, 0.5]) coord_loc = np.argwhere(np.all(coords == coord, axis=1)).squeeze() rand_4_locs = np.sort(np.random.choice(range(coords.shape[0]), 4, replace=False)) points_5_locs = np.unique(np.hstack((coord_loc, rand_4_locs))) coords_ON_state = coords[points_5_locs] fig = plt.figure() ax = fig.add_subplot(111, projection='3d') ax.scatter(coords[:, 0], coords[:, 1], coords[:, 2], c='c', marker='o', alpha=0.1, s=200, edgecolors='black') ax.scatter(coords_ON_state[:, 0], coords_ON_state[:, 1], coords_ON_state[:, 2], c='b', marker='o', alpha=0.8, s=50, edgecolors='black') result = mp.check_if_point_can_host_a_single_surface_CUBIC(coord, vs) print("Can host single surface:", result)
- find_intersection_voxels_with_line(sl3d, cod)[source]
Find all voxels in
self.coordsthat intersect a 3-D line within a cut-off distance.
- tree