upxo.geoEntities.mulsline3d module
Multi-straight-line 3D geometric entity module for UPXO.
Provides MSline3d, a collection of ordered Sline3d segments
representing an open or closed 3-D polyline. Supports construction from
line lists, node/length queries, spatial distance calculations, subdivision,
and node removal operations.
Applications
Non-conformal to conformal geometry conversion.
Hierarchical grain structure feature generation.
General 3-D polyline geometry operations.
Classes
- MSline3d
Ordered collection of
Sline3dsegments forming a 3-D polyline.
Coordinate system
Y+
| Z-
| /
| /
| /
| /
X- | / X+
-----------------O------------------
/|
/ |
/ |
/ |
/ |
/ |
Z+ Y-
Usage
from upxo.geoEntities.mulsline3d import MSline3d as msl3d
from upxo.geoEntities.sline3d import Sline3d as sl3d
@author: Dr. Sunil Anandatheertha
- class upxo.geoEntities.mulsline3d.MSline3d(llist)[source]
Bases:
objectOrdered collection of
Sline3dsegments forming a 3-D polyline.Wraps a list of connected
Sline3dobjects and provides aggregate geometric properties (total length, mean length, node positions) and editing operations (subdivision, node removal).- Usage
- -----
- ::
from upxo.geoEntities.mulsline3d import MSline3d as msl3d
Examples
from upxo.geoEntities.mulsline3d import MSline3d as msl3d from upxo.geoEntities.sline3d import Sline3d as sl3d e0 = sl3d(0.0, 0.0, 0.0, 1.0, 1.0, 1.0) e1 = sl3d(1.0, 1.0, 1.0, 1.5, 1.5, 0.0) e2 = sl3d(1.5, 1.5, 0.0, 2.5, 2.5, 3.0) e3 = sl3d(2.5, 2.5, 3.0, 4.0, 4.0, 3.5) e4 = sl3d(4.0, 4.0, 3.5, 4.0, 6.0, 3.5) me = msl3d([e0, e1, e2, e3, e4]) print(me.n, me.length)
- lines
- classmethod from_lines(llist, close=True)[source]
Construct a
MSline3dfrom a list ofSline3dobjects.- Parameters:
- Returns:
New multi-line with optional closing segment appended.
- Return type:
Examples
from upxo.geoEntities.mulsline3d import MSline3d as msl3d from upxo.geoEntities.sline3d import Sline3d as sl3d lines = [sl3d(0, 0, 0, 1, 1, 1), sl3d(1, 1, 1, 2, 2, 2)] me = msl3d.from_lines(lines, close=True) print(me.n)
- classmethod by_walk(var_l='constant', var_ang='constant', specs={'max_total_length': 10, 'mean_length': 1, 'min_total_length': 8, 'n': 5})[source]
Construct a multi-line by random walk with length/angle variation. Not yet implemented.
- property n
Number of line segments in the collection.
- property lengths
Lengths of individual segments in order.
Examples
me.lengths
- property length
Total length of all segments combined.
- Returns:
Sum of all individual segment lengths.
- Return type:
Examples
me.length
- property length_mean
Mean segment length.
- Returns:
total_length / n.- Return type:
Examples
me.length_mean
- property gradients
Gradient
[dx/dz, dy/dz]of every segment.
- property nodes
Unique list of nodes (start and end points) across all segments.
- Returns:
Unique
[x, y, z]node coordinates.- Return type:
numpy.ndarray, shape (M, 3)
- property mid_nodes
Midpoint coordinates of all segments.
Examples
from upxo.geoEntities.mulsline3d import MSline3d as msl3d from upxo.geoEntities.sline3d import Sline3d as sl3d lines = [sl3d(0.0, 0.0, 0.0, 1.0, 1.0, 1.0), sl3d(1.0, 1.0, 1.0, 1.5, 1.5, 0.0), sl3d(1.5, 1.5, 0.0, 2.5, 2.5, 3.0), sl3d(2.5, 2.5, 3.0, 4.0, 4.0, 3.5), sl3d(4.0, 4.0, 3.5, 4.0, 6.0, 3.5)] MULLINE = msl3d.from_lines(lines, close=True) print(MULLINE.mid_nodes)
- property line_ids
Memory id of each segment.
- distances_nodes(points)[source]
Compute distances from every node to each of the given points.
- Parameters:
points (numpy.ndarray, shape (M, 3)) – Query points to measure distances from.
- Returns:
Distance from each node (row) to each query point (column).
- Return type:
numpy.ndarray, shape (N_nodes, M)
Notes
Uses vectorised broadcasting:
points[:, np.newaxis]shapes to(M, 1, 3)so that subtraction withnodes(shape(N, 3)) yields(M, N, 3)differences, squared-summed over axis 2, then transposed to(N_nodes, M).Examples
from upxo.geoEntities.mulsline3d import MSline3d as msl3d from upxo.geoEntities.sline3d import Sline3d as sl3d import numpy as np lines = [sl3d(0.0, 0.0, 0.0, 1.0, 1.0, 1.0), sl3d(1.0, 1.0, 1.0, 1.5, 1.5, 0.0), sl3d(1.5, 1.5, 0.0, 2.5, 2.5, 3.0), sl3d(2.5, 2.5, 3.0, 4.0, 4.0, 3.5), sl3d(4.0, 4.0, 3.5, 4.0, 6.0, 3.5)] MULLINE = msl3d.from_lines(lines, close=True) points = np.random.random((2, 3)) MULLINE.distances_nodes(points)
- find_closest_nodes(point)[source]
Find the index (or indices) of the node(s) closest to
point.- Parameters:
point (array-like, shape (3,)) – Query coordinate
[x, y, z].- Returns:
Index (or indices) into
self.nodesof the nearest node(s).- Return type:
Examples
Example 1 — random query point:
from upxo.geoEntities.mulsline3d import MSline3d as msl3d from upxo.geoEntities.sline3d import Sline3d as sl3d import numpy as np lines = [sl3d(0.0, 0.0, 0.0, 1.0, 1.0, 1.0), sl3d(1.0, 1.0, 1.0, 1.5, 1.5, 0.0), sl3d(1.5, 1.5, 0.0, 2.5, 2.5, 3.0)] MULLINE = msl3d.from_lines(lines, close=True) point = np.random.random(3) * np.random.randint(10) MULLINE.find_closest_nodes(point)
Example 2 — midpoint of the first segment:
from upxo.geoEntities.mulsline3d import MSline3d as msl3d from upxo.geoEntities.sline3d import Sline3d as sl3d lines = [sl3d(0.0, 0.0, 0.0, 1.0, 1.0, 1.0), sl3d(1.0, 1.0, 1.0, 1.5, 1.5, 0.0), sl3d(1.5, 1.5, 0.0, 2.5, 2.5, 3.0)] MULLINE = msl3d.from_lines(lines, close=True) point = lines[0].mid MULLINE.find_closest_nodes(point)
- sub_divide(line_number=0, f=0.5)[source]
Sub-divide a single segment at a fractional position.
Replaces the segment at
line_numberwith two shorter segments split at the fractional positionf.- Parameters:
- Returns:
Modifies
self.linesin place.- Return type:
None
- Raises:
TypeError – If
line_numberis not an integer.ValueError – If
line_numberexceeds the number of segments.
Examples
from upxo.geoEntities.mulsline3d import MSline3d as msl3d from upxo.geoEntities.sline3d import Sline3d as sl3d e0 = sl3d(0.0, 0.0, 0.0, 1.0, 1.0, 1.0) e1 = sl3d(1.0, 1.0, 1.0, 1.5, 1.5, 0.0) e2 = sl3d(1.5, 1.5, 0.0, 2.5, 2.5, 3.0) e3 = sl3d(2.5, 2.5, 3.0, 4.0, 4.0, 3.5) e4 = sl3d(4.0, 4.0, 3.5, 4.0, 6.0, 3.5) me = msl3d.from_lines([e0, e1, e2, e3, e4], close=False) me.sub_divide(line_number=0, f=0.25) me.sub_divide(line_number=len(me.lines), f=0.25) me.sub_divide(line_number=3, f=0.50)
- remove_point_by_index(index=2, remove='previous_line')[source]
Remove a node by index, merging the adjacent segments.
- Parameters:
index (int, optional) – Zero-based index of the node (shared endpoint) to remove. Default is 2.
remove ({'previous_line', 'next_line', 'both'}, optional) –
How to handle the adjacent segments:
'previous_line'— extend the next segment back to the previous segment’s start point and delete the previous segment.'next_line'— extend the previous segment forward to the next segment’s end point and delete the next segment.'both'— replace both adjacent segments with a single new segment connecting the previous segment’s start to the next segment’s end.
Default is
'previous_line'.
- Returns:
Modifies
self.linesin place.- Return type:
None
Examples
Example 1 — remove previous segment:
from upxo.geoEntities.mulsline3d import MSline3d as msl3d from upxo.geoEntities.sline3d import Sline3d as sl3d lines = [sl3d(0.0, 0.0, 0.0, 1.0, 1.0, 1.0), sl3d(1.0, 1.0, 1.0, 1.5, 1.5, 0.0), sl3d(1.5, 1.5, 0.0, 2.5, 2.5, 3.0), sl3d(2.5, 2.5, 3.0, 4.0, 4.0, 3.5), sl3d(4.0, 4.0, 3.5, 4.0, 6.0, 3.5)] MULLINE = msl3d.from_lines(lines, close=True) MULLINE.remove_point_by_index(index=2, remove='previous_line') print(MULLINE.n)
Example 2 — remove next segment:
from upxo.geoEntities.mulsline3d import MSline3d as msl3d from upxo.geoEntities.sline3d import Sline3d as sl3d lines = [sl3d(0.0, 0.0, 0.0, 1.0, 1.0, 1.0), sl3d(1.0, 1.0, 1.0, 1.5, 1.5, 0.0), sl3d(1.5, 1.5, 0.0, 2.5, 2.5, 3.0), sl3d(2.5, 2.5, 3.0, 4.0, 4.0, 3.5), sl3d(4.0, 4.0, 3.5, 4.0, 6.0, 3.5)] MULLINE = msl3d.from_lines(lines, close=True) MULLINE.remove_point_by_index(index=2, remove='next_line')
Example 3 — replace both adjacent segments with a single new segment:
from upxo.geoEntities.mulsline3d import MSline3d as msl3d from upxo.geoEntities.sline3d import Sline3d as sl3d lines = [sl3d(0.0, 0.0, 0.0, 1.0, 1.0, 1.0), sl3d(1.0, 1.0, 1.0, 1.5, 1.5, 0.0), sl3d(1.5, 1.5, 0.0, 2.5, 2.5, 3.0), sl3d(2.5, 2.5, 3.0, 4.0, 4.0, 3.5), sl3d(4.0, 4.0, 3.5, 4.0, 6.0, 3.5)] MULLINE = msl3d.from_lines(lines, close=True) MULLINE.remove_point_by_index(index=2, remove='both')
- remove_point_by_location(location=(None, None, None), remove='previous_line')[source]
Remove a node nearest to
location, merging the adjacent segments.Finds the closest node to
locationusingfind_closest_nodes()and delegates toremove_point_by_index().- Parameters:
location (array-like, shape (3,), optional) – Target
[x, y, z]coordinate. The nearest node is found and removed. Default is(None, None, None).remove ({'previous_line', 'next_line', 'both'}, optional) – See
remove_point_by_index(). Default is'previous_line'.
- Returns:
Modifies
self.linesin place.- Return type:
None
Notes
When only 2 segments remain after removal and the second is a redundant closing segment (same endpoints as the first but reversed), the closing segment is automatically deleted.
Examples
Example 1 — remove by a known endpoint:
from upxo.geoEntities.mulsline3d import MSline3d as msl3d from upxo.geoEntities.sline3d import Sline3d as sl3d lines = [sl3d(0.0, 0.0, 0.0, 1.0, 1.0, 1.0), sl3d(1.0, 1.0, 1.0, 1.5, 1.5, 0.0), sl3d(1.5, 1.5, 0.0, 2.5, 2.5, 3.0)] MULLINE = msl3d.from_lines(lines, close=True) MULLINE.remove_point_by_location(location=lines[0].coord_i, remove='previous_line')
Example 2 — remove by a segment midpoint:
from upxo.geoEntities.mulsline3d import MSline3d as msl3d from upxo.geoEntities.sline3d import Sline3d as sl3d lines = [sl3d(0.0, 0.0, 0.0, 1.0, 1.0, 1.0), sl3d(1.0, 1.0, 1.0, 1.5, 1.5, 0.0), sl3d(1.5, 1.5, 0.0, 2.5, 2.5, 3.0)] MULLINE = msl3d.from_lines(lines, close=True) MULLINE.remove_point_by_location(location=lines[0].mid, remove='previous_line')
Example 3 — remove by a random location:
from upxo.geoEntities.mulsline3d import MSline3d as msl3d from upxo.geoEntities.sline3d import Sline3d as sl3d import numpy as np lines = [sl3d(0.0, 0.0, 0.0, 1.0, 1.0, 1.0), sl3d(1.0, 1.0, 1.0, 1.5, 1.5, 0.0), sl3d(1.5, 1.5, 0.0, 2.5, 2.5, 3.0)] MULLINE = msl3d.from_lines(lines, close=True) location = np.random.random(3) * np.random.randint(10) MULLINE.remove_point_by_location(location=location, remove='previous_line')
- x0
- y0
- z0
- x1
- y1
- z1
- f
- closed