upxo.geoEntities.mulsline2d module

Multi-straight line 2D geometric entity module for UPXO.

This module provides multi-straight-line (polyline) representations in 2D space. It supports closed and open polylines with operations for node/line management, spatial queries, adjacent relationship detection, and geometric transformations.

Core Classes

  • MSline2d : Multi-straight-line in 2D (collection of connected line segments).

  • ring2d : Ring structure in 2D (to be further developed).

  • mulring2d : Multiple rings in 2D (to be further developed).

Key Features

  • Line chain construction from various input formats (node points, coordinates, existing lines).

  • Node and line enumeration, spacing, and connectivity checks.

  • Adjacency and spatial relationship detection between polylines.

  • Centroid and length calculations.

  • Open/closed topology management.

  • Node coordinate access and updates.

  • Spatial tree indexing for nearest-neighbor queries.

  • Point location finding within polyline node sequences.

  • Plotting and visualization support.

Applications

  • Non-conformal to conformal geometry conversion.

  • Hierarchical grain structure feature generation.

  • General geometry operations and polyline manipulation.

  • Boundary and edge representation in meshing and simulation.

Usage

from upxo.geoEntities.mulsline2d import MSline2d

Coordinate System

Standard 2D Cartesian with notation (X+, Y+) = (right, up):

Y+ | | | |

X- | X+ —–+—–+—–O—–+—–+—–





Y-

@author: Dr. Sunil Anandatheertha

Examples

Construct from line objects:
>>> from upxo.geoEntities.mulsline2d import MSline2d
>>> from upxo.geoEntities.sline2d import Sline2d
>>> lines = [
...     Sline2d(0, 0, 1, 1),
...     Sline2d(1, 1, 2, 2),
...     Sline2d(2, 2, 3, 1)
... ]
>>> msl = MSline2d.from_lines(lines, close=False)
>>> msl.nlines, msl.nnodes
(3, 4)
Construct from coordinates:
>>> coords = [(0, 0), (1, 1), (2, 2), (3, 1)]
>>> msl = MSline2d.by_coords(coords, close=True)
>>> msl.length
>>> msl.centroid
Query relationships:
>>> msl1 = MSline2d.by_coords([(0, 0), (1, 1)])
>>> msl2 = MSline2d.by_coords([(1, 1), (2, 2)])
>>> msl1.do_i_precede(msl2)  # Check if msl1 comes before msl2
True
Find closest nodes:
>>> from upxo.geoEntities.point2d import Point2d
>>> msl = MSline2d.by_coords([(0, 0), (2, 0), (4, 0)])
>>> closest = msl.find_closest_nodes(Point2d(1.5, 0.5))

See also

-, -, -

class upxo.geoEntities.mulsline2d.MSline2d(nodes=None, llist=None, closed=None)[source]

Bases: object

Multi-straight-line (polyline) entity in 2D for UPXO.

A connected chain of Sline2d line segments sharing end-nodes. Supports open and closed topologies.

lines

Constituent Sline2d segments.

Type:

list

nodes

Ordered endpoint nodes along the polyline.

Type:

list

features

Feature metadata associated with the polyline.

Type:

dict

closed

Whether the polyline is closed.

Type:

bool

Examples

from upxo.geoEntities.mulsline2d import MSline2d as msl2d
from upxo.geoEntities.sline2d import Sline2d as sl2d

e0 = sl2d(0.0, 0.0, 1.0, 1.0)
e1 = sl2d(1.0, 1.0, 1.5, 1.5)
e2 = sl2d(1.5, 1.5, 2.5, 2.5)
e3 = sl2d(2.5, 2.5, 4.0, 4.0)
e4 = sl2d(4.0, 4.0, 4.0, 6.0)
me = msl2d.from_lines([e0, e1, e2, e3, e4], close=False)
EPS_coord_coincide = 1e-08
lines
nodes
closed
features
classmethod from_lines(llist, close=True)[source]

Construct a multi-straight-line 2D from a list of Sline2d objects.

Parameters:
  • llist (list of Sline2d) – Ordered line segments forming the polyline chain.

  • close (bool, optional) – If True, append a closing segment from the last node back to the first node.

Returns:

Assembled polyline.

Return type:

MSline2d

Examples

from upxo.geoEntities.mulsline2d import MSline2d as msl2d
from upxo.geoEntities.sline2d import Sline2d as sl2d

e0 = sl2d(0.0, 0.0, 1.0, 1.0)
e1 = sl2d(1.0, 1.0, 1.5, 1.5)
e2 = sl2d(1.5, 1.5, 2.5, 2.5)
e3 = sl2d(2.5, 2.5, 4.0, 4.0)
e4 = sl2d(4.0, 4.0, 4.0, 6.0)

me_closed = msl2d.from_lines([e0, e1, e2, e3, e4], close=True)
me_open   = msl2d.from_lines([e0, e1, e2, e3, e4], close=False)
classmethod by_nodes(nodes, close=True)[source]

Construct a multi-straight-line 2D from a list of Point2d nodes.

Parameters:
  • nodes (list of Point2d) – Ordered nodes of the polyline.

  • close (bool, optional) – If True, append a closing segment.

Returns:

Assembled polyline.

Return type:

MSline2d

Examples

from upxo.geoEntities.mulsline2d import MSline2d
from upxo.geoEntities.point2d import Point2d

nodes = [Point2d(0, 0), Point2d(1, 1), Point2d(2, 2),
         Point2d(3, 3), Point2d(5, 5)]
MSline2d.by_nodes(nodes).lines
classmethod by_coords(coords, close=True)[source]

Construct a multi-straight-line 2D from a sequence of (x, y) coordinates.

Parameters:
  • coords (array-like of shape (n, 2)) – Ordered (x, y) coordinate pairs.

  • close (bool, optional) – If True, append a closing segment.

Returns:

Assembled polyline.

Return type:

MSline2d

Examples

from upxo.geoEntities.mulsline2d import MSline2d
import numpy as np

coords = np.array([(0,0), (1,1), (2,2), (3,3), (5,5)])
msl = MSline2d.by_coords(coords)
msl.lines
msl.nodes
classmethod by_walk(var_l='constant', var_ang='constant', specs={'max_total_length': 10, 'mean_length': 1, 'min_total_length': 8, 'n': 5})[source]

Generate polyline by “walking” with variable step length/angle.

Parameters:
  • var_l (str, optional) – Length variation type (e.g., ‘constant’, ‘random’).

  • var_ang (str, optional) – Angle variation type (e.g., ‘constant’, ‘random’).

  • specs (dict, optional) – Specifications dict with keys: ‘n’ (count), ‘max_total_length’, ‘min_total_length’, ‘mean_length’.

Notes

To be developed.

property nlines

Return number of lines.

Returns:

Number of constituent line segments.

Return type:

int

property centroid

Compute and return centroid of the polyline.

Returns:

Mean coordinate (x, y) of all nodes.

Return type:

numpy.ndarray

property centroid_p2dl

Return centroid as p2d_leanest point object.

Returns:

Lightweight point object at polyline centroid.

Return type:

p2d_leanest

property length

Return total length of all lines in polyline.

Returns:

Sum of all individual line lengths.

Return type:

float

property lengths

Return individual line lengths as list.

Returns:

Lengths of each line in the polyline.

Return type:

list

property nnodes

Return number of polyline nodes.

Returns:

Node count, excluding the duplicate closing node when closed.

Return type:

int

property coords

Get all node coordinates as 2D array.

Returns:

Array of shape (n_nodes, 2) with (x, y) coordinates.

Return type:

numpy.ndarray

property tree

Build spatial KD-tree index for neighbor queries.

Returns:

KD-tree constructed from node coordinates.

Return type:

scipy.spatial.cKDTree

property length_mean

Return mean length of all line segments.

Returns:

Total length divided by number of lines.

Return type:

float

property gradients

Return gradient of every line.

Returns:

Gradient value for each constituent line.

Return type:

list

property get_nodes

Return unique nodes in the multi-straight-line object.

Returns:

Unique node coordinate rows.

Return type:

numpy.ndarray

get_node_coords()[source]

Extract and return all node coordinates as array.

Returns:

Array of shape (n_nodes, 2) with (x, y) coordinates of each node.

Return type:

numpy.ndarray

property mid_nodes

Return midpoint nodes of all lines.

Returns:

Mid-point of each line segment in the polyline.

Return type:

list

Examples

from upxo.geoEntities.mulsline2d import MSline2d as msl2d
from upxo.geoEntities.sline2d import Sline2d as sl2d

lines = [sl2d(0.0,0.0,1.0,1.0), sl2d(1.0,1.0,1.5,1.5),
         sl2d(1.5,1.5,2.5,2.5), sl2d(2.5,2.5,4.0,4.0),
         sl2d(4.0,4.0,4.0,6.0)]
MULLINE = msl2d.from_lines(lines, close=True)
MULLINE.mid_nodes
MULLINE.centroid_p2dl
property line_ids

Return memory id of each line.

Returns:

Python id values for all constituent lines.

Return type:

list of int

flip(saa=True, throw=False)[source]

Reverse the order of lines in the polyline.

Parameters:
  • saa (bool, optional) – If True, modify self in place (save-and-apply). If False, operate on a deep copy.

  • throw (bool, optional) – If True, return the (possibly copied) result.

Returns:

Flipped polyline if throw is True, else None.

Return type:

MSline2d or None

Examples

from upxo.geoEntities.mulsline2d import MSline2d as msl2d
from upxo.geoEntities.sline2d import Sline2d as sl2d

lines = [sl2d(0.0,0.0,1.0,1.0), sl2d(1.0,1.0,1.5,1.5),
         sl2d(1.5,1.5,2.5,2.5), sl2d(2.5,2.5,4.0,4.0),
         sl2d(4.0,4.0,4.0,6.0)]
MULLINE = msl2d.from_lines(lines, close=True)
MULLINE.flip()
MULLINE.lines
MULLINE.nodes
do_i_precede(multisline2d)[source]

Check if self spatially precedes the input multisline2d.

Parameters:

multisline2d (MSline2d) – Candidate successor polyline.

Returns:

  • i_precede (bool) – True if self immediately precedes multisline2d.

  • flip_needed (bool) – True if multisline2d must be flipped to achieve nodal continuity with self.

do_i_proceed(multisline2d)[source]

Check if self spatially comes after the input multisline2d.

Parameters:

multisline2d (MSline2d) – Candidate predecessor polyline.

Returns:

  • i_proceed (bool) – True if self immediately succeeds multisline2d.

  • flip_needed (bool) – True if multisline2d must be flipped to achieve nodal continuity with self.

is_adjacent(multisline2d)[source]

Check spatial adjacency with another multiline.

Parameters:

multisline2d (MSline2d) – Another multi-straight-line object to test.

Returns:

(is_adjacent_bool, left_relation, right_relation) tuple.

Return type:

tuple

Notes

Checks if multiline is immediately preceded or succeeded by input.

find_spatially_next_multisline2d(multislines2d)[source]

From a list of multisline2d objects, multislines2d, find the ones which come spatially immediately after self.

Parameters:

multislines2d (list of MSline2d) – Candidate successor polylines.

Returns:

do_i_precede results for each candidate.

Return type:

list

has_coord(coord, return_flags=False)[source]

Check if coordinate exists in polyline node sequence.

Parameters:
  • coord (array-like) – 2D coordinate (x, y) to search for.

  • return_flags (bool, optional) – If True, return boolean flags array.

Returns:

If return_flags=False: boolean indicating existence. If return_flags=True: (existence_bool, flags_array) tuple.

Return type:

bool or (bool, numpy.ndarray)

find_coord_location(coord)[source]

Find index location of coordinate in node sequence.

Parameters:

coord (array-like) – 2D coordinate (x, y) to search for.

Returns:

Index array if coordinate found, None if not found.

Return type:

numpy.ndarray or None

extract_nodes()[source]

Extract all unique nodes from polyline lines.

Returns:

List of Point2d nodes (including closing node if closed).

Return type:

list

update_nodes()[source]

Update internal node list from current line definitions.

Notes

Reconstructs self.nodes from line start/end points, respecting open/closed topology.

close(reclose=False)[source]

Close the self multi-straight line object.

Parameters:

reclose (bool, optional) – If True, append a closing line even when already closed.

Returns:

Updates self.lines in place.

Return type:

None

Examples

unclose()[source]

Remove the closing line.

Returns:

Deletes the last line from self.lines.

Return type:

None

distances_nodes(points)[source]

Compute distances from every node to each given point.

Parameters:

points (numpy.ndarray of shape (m, 2)) – Query points.

Returns:

Euclidean distance from each node to each query point.

Return type:

numpy.ndarray of shape (n_nodes, m)

Notes

Uses vectorised broadcasting: points[:, np.newaxis] is broadcast against the node array so all pairwise distances are computed in one operation.

Examples

from upxo.geoEntities.mulsline2d import MSline2d as msl2d
from upxo.geoEntities.sline2d import Sline2d as sl2d
import numpy as np

lines = [sl2d(0.0,0.0,1.0,1.0), sl2d(1.0,1.0,1.5,1.5),
         sl2d(1.5,1.5,2.5,2.5), sl2d(2.5,2.5,4.0,4.0),
         sl2d(4.0,4.0,4.0,6.0)]
MULLINE = msl2d.from_lines(lines, close=True)
points = np.random.random((2, 2))
MULLINE.distances_nodes(points)
find_closest_nodes(point)[source]

Find the closest node index (or indices) to a query point.

Parameters:

point (array-like of shape (2,)) – Query point coordinates.

Returns:

Index of the closest node, or a list of indices if multiple nodes are equidistant.

Return type:

int or list of int

Examples

Example 1 — random query point:

from upxo.geoEntities.mulsline2d import MSline2d as msl2d
from upxo.geoEntities.sline2d import Sline2d as sl2d
import numpy as np

lines = [sl2d(0.0,0.0,1.0,1.0), sl2d(1.0,1.0,1.5,1.5),
         sl2d(1.5,1.5,2.5,2.5)]
MULLINE = msl2d.from_lines(lines, close=True)
point = np.random.random(2) * np.random.randint(10)
MULLINE.find_closest_nodes(point)

Example 2 — midpoint of first line:

from upxo.geoEntities.mulsline2d import MSline2d as msl2d
from upxo.geoEntities.sline2d import Sline2d as sl2d

lines = [sl2d(0.0,0.0,1.0,1.0), sl2d(1.0,1.0,1.5,1.5),
         sl2d(1.5,1.5,2.5,2.5)]
MULLINE = msl2d.from_lines(lines, close=True)
point = lines[0].mid
MULLINE.find_closest_nodes(point)
add_nodes(nodes)[source]

Insert new nodes into the polyline by splitting existing lines.

Parameters:

nodes (list of Point2d) – New nodes to insert. Each node is located on an existing line segment and splits it.

Notes

If a node does not lie on any line segment it is silently ignored. The internal node list is updated after each insertion.

Examples

Example 1 — insert two nodes on a 5-segment polyline:

from upxo.geoEntities.mulsline2d import MSline2d as msl2d
from upxo.geoEntities.sline2d import Sline2d as sl2d
from upxo.geoEntities.point2d import Point2d

lines = [sl2d(0.0,0.0,1.0,1.0), sl2d(1.0,1.0,1.5,1.5),
         sl2d(1.5,1.5,2.5,2.5), sl2d(2.5,2.5,4.0,4.0),
         sl2d(4.0,4.0,4.0,6.0)]
mulline = msl2d.from_lines(lines, close=True)
mulline.add_nodes([Point2d(0.5, 0.5), Point2d(2.0, 2.0)])
mulline.lines

Example 2 — insert three nodes on a 6-segment polyline:

from upxo.geoEntities.mulsline2d import MSline2d as msl2d
from upxo.geoEntities.sline2d import Sline2d as sl2d
from upxo.geoEntities.point2d import Point2d

lines = [sl2d(0.0,0.0,1.0,1.0), sl2d(1.0,1.0,1.5,1.5),
         sl2d(1.5,1.5,2.5,2.5), sl2d(2.5,2.5,4.0,4.0),
         sl2d(4.0,4.0,5.0,4.0), sl2d(5.0,4.0,5.0,0.0)]
mulline = msl2d.from_lines(lines, close=True)
mulline.add_nodes([Point2d(0.5, 0.5), Point2d(2.0, 2.0),
                   Point2d(1.75, 0)])
mulline.lines
splice_nodes_and_lines(method='points', points=None, perform_checks=True)[source]

Splice/interpolate nodes and lines at specified points.

Parameters:
  • method (str, optional) – Method for splicing. Options include ‘points’ (default).

  • points (list, optional) – Points at which to splice.

  • perform_checks (bool, optional) – Whether to perform validation checks.

Notes

To be developed.

roll(roll_distance)[source]

Roll/rotate line order within the polyline chain.

Parameters:

roll_distance (int) – Number of positions to roll lines.

Notes

Updates internal node list after rolling. Useful for changing polyline starting point while preserving connectivity.

sub_divide(line_number=0, f=0.5)[source]

Sub-divide a single line in self.lines at a fractional position.

Parameters:
  • line_number (int, optional) – Index in self.lines to subdivide (zero-based).

  • f (float, optional) – Fractional position along the target line in (0, 1) where the split is made.

Examples

from upxo.geoEntities.mulsline2d import MSline2d as msl2d
from upxo.geoEntities.sline2d import Sline2d as sl2d

lines = [sl2d(0.0,0.0,1.0,1.0), sl2d(1.0,1.0,1.5,1.5),
         sl2d(1.5,1.5,2.5,2.5), sl2d(2.5,2.5,4.0,4.0),
         sl2d(4.0,4.0,4.0,6.0)]
me = msl2d.from_lines(lines, close=False)

me.sub_divide(line_number=0, f=0.25)
me.sub_divide(line_number=3, f=0.50)

for i in range(5):
    me.sub_divide(line_number=i, f=0.50)
me.lines
remove_point_by_index(index=2, remove='previous_line')[source]

Remove a node at the given index by merging its adjacent line segments.

Parameters:
  • index (int, optional) – Zero-based node index to remove.

  • remove ({'previous_line', 'next_line', 'both'}) – Which adjacent line to delete: 'previous_line' — extend the next line backward; 'next_line' — extend the previous line forward; 'both' — replace both adjacent lines with a new direct line.

Examples

Example 1 — remove via previous line:

from upxo.geoEntities.mulsline2d import MSline2d as msl2d
from upxo.geoEntities.sline2d import Sline2d as sl2d

lines = [sl2d(0.0,0.0,1.0,1.0), sl2d(1.0,1.0,1.5,1.5),
         sl2d(1.5,1.5,2.5,2.5), sl2d(2.5,2.5,4.0,4.0),
         sl2d(4.0,4.0,4.0,6.0)]
MULLINE = msl2d.from_lines(lines, close=True)
MULLINE.remove_point_by_index(index=2, remove='previous_line')
MULLINE.lines

Example 2 — remove via next line:

from upxo.geoEntities.mulsline2d import MSline2d as msl2d
from upxo.geoEntities.sline2d import Sline2d as sl2d

lines = [sl2d(0.0,0.0,1.0,1.0), sl2d(1.0,1.0,1.5,1.5),
         sl2d(1.5,1.5,2.5,2.5), sl2d(2.5,2.5,4.0,4.0),
         sl2d(4.0,4.0,4.0,6.0)]
MULLINE = msl2d.from_lines(lines, close=True)
MULLINE.remove_point_by_index(index=2, remove='next_line')
MULLINE.lines

Example 3 — remove both adjacent lines:

from upxo.geoEntities.mulsline2d import MSline2d as msl2d
from upxo.geoEntities.sline2d import Sline2d as sl2d

lines = [sl2d(0.0,0.0,1.0,1.0), sl2d(1.0,1.0,1.5,1.5),
         sl2d(1.5,1.5,2.5,2.5), sl2d(2.5,2.5,4.0,4.0),
         sl2d(4.0,4.0,4.0,6.0)]
MULLINE = msl2d.from_lines(lines, close=True)
MULLINE.remove_point_by_index(index=2, remove='both')
MULLINE.lines
remove_point_by_location(location=(None, None, None), remove='previous_line')[source]

Remove the node closest to a given location.

Parameters:
  • location (array-like of shape (2,)) – Query coordinates used to find the closest node.

  • remove ({'previous_line', 'next_line', 'both'}) – Passed through to remove_point_by_index().

Notes

If multiple nodes tie for closest, all are removed recursively. When only 2 lines remain and the closing line is redundant, the closing line is automatically deleted.

Examples

Example 1 — remove by endpoint coordinate:

from upxo.geoEntities.mulsline2d import MSline2d as msl2d
from upxo.geoEntities.sline2d import Sline2d as sl2d

lines = [sl2d(0.0,0.0,1.0,1.0), sl2d(1.0,1.0,1.5,1.5),
         sl2d(1.5,1.5,2.5,2.5), sl2d(2.5,2.5,4.0,4.0),
         sl2d(4.0,4.0,4.0,6.0)]
MULLINE = msl2d.from_lines(lines, close=True)
MULLINE.remove_point_by_location(location=lines[0].coord_i,
                                 remove='previous_line')
MULLINE.lines

Example 2 — remove by midpoint coordinate:

from upxo.geoEntities.mulsline2d import MSline2d as msl2d
from upxo.geoEntities.sline2d import Sline2d as sl2d

lines = [sl2d(0.0,0.0,1.0,1.0), sl2d(1.0,1.0,1.5,1.5),
         sl2d(1.5,1.5,2.5,2.5), sl2d(2.5,2.5,4.0,4.0),
         sl2d(4.0,4.0,4.0,6.0)]
MULLINE = msl2d.from_lines(lines, close=True)
MULLINE.remove_point_by_location(location=lines[0].mid,
                                 remove='previous_line')
MULLINE.lines

Example 3 — remove by random location:

from upxo.geoEntities.mulsline2d import MSline2d as msl2d
from upxo.geoEntities.sline2d import Sline2d as sl2d
import numpy as np

lines = [sl2d(0.0,0.0,1.0,1.0), sl2d(1.0,1.0,1.5,1.5),
         sl2d(1.5,1.5,2.5,2.5), sl2d(2.5,2.5,4.0,4.0),
         sl2d(4.0,4.0,4.0,6.0)]
MULLINE = msl2d.from_lines(lines, close=True)
location = np.random.random(2) * np.random.randint(10)
MULLINE.remove_point_by_location(location=location,
                                 remove='previous_line')
MULLINE.lines
plot(ax=None, connect_ends=False)[source]

Plot polyline on matplotlib axes.

Parameters:
  • ax (matplotlib.axes.Axes, optional) – Axes object to plot on. If None, creates new figure/axes.

  • connect_ends (bool, optional) – If True, draw line connecting polyline start and end points.

Returns:

Axes object with plotted polyline.

Return type:

matplotlib.axes.Axes

Examples

>>> msl = MSline2d.by_coords([(0, 0), (1, 1), (2, 0)])
>>> ax = msl.plot()
check_overlaping_points(tolerance=1e-08)[source]

Check for overlapping/duplicate nodes within tolerance.

Parameters:

tolerance (float, optional) – Maximum distance for considering points duplicates. Default: 1E-8.

Returns:

True if overlapping points found, False otherwise.

Return type:

bool

check_overlaping_lines()[source]

Check for overlapping line segments in polyline.

Returns:

True if overlapping lines detected, None if to be completed.

Return type:

bool or None

Notes

To be developed. Current implementation collects midpoints and gradients but does not perform overlap checks.

clone()[source]

Return an independent copy of the node and line containers.

The node and line objects are shared with the original. That is safe for smooth(), which replaces nodes and lines with new lists and new interior points rather than mutating them, and it is far cheaper than copy.deepcopy.

smooth(max_smooth_level=2)[source]

Smooth the polyline by replacing node coordinates with local means.

Parameters:

max_smooth_level (int, optional) – Number of smoothing passes applied via mean_coordinates().

Notes

Terminal nodes (first and last) are preserved. If fewer than 3 nodes are present, no smoothing is performed.

Examples

from upxo.geoEntities.mulsline2d import MSline2d as msl2d
from upxo.geoEntities.point2d import Point2d

nodes = [Point2d(0.0, 0.0), Point2d(1.0, 0.0),
         Point2d(1.0, 1.0), Point2d(2.5, 2.0),
         Point2d(4.0, 2.0), Point2d(4.0, 6.0),
         Point2d(4.0, 8.0), Point2d(2.0, 8.0)]
ml = msl2d.by_nodes(nodes, close=False)
ax = ml.plot()
ml.smooth(max_smooth_level=3)
ax.plot(ml.coords[:, 0], ml.coords[:, 1])
class upxo.geoEntities.mulsline2d.ring2d(segments=None, segids=None, segflips=None)[source]

Bases: object

Ring structure in 2D—closed polyline representation from multiple segments.

This class manages a collection of connected MSline2d (multi-straight-line) segments that form a closed ring/loop structure. It handles segment ordering, flipping, spatial continuity checks, and polygon conversion.

segments

Collection of MSline2d objects forming the ring.

Type:

list

segids

Segment identifiers.

Type:

list

segflips

Boolean flags indicating if segments were flipped for continuity.

Type:

list

nsegs

Number of segments in the ring.

Type:

int

closed

Whether ring is topologically closed.

Type:

bool

conn0

First-order connectivity status (start-end closure).

Type:

bool

conn1

Second-order connectivity status (inter-segment continuity).

Type:

dict

Notes

ring2d is in active development. Closure enforcement, reordering, and polygon generation are implemented; polygon meshing is not yet available.

Name collision: upxo.geoEntities.ring2d.ring2d is a separate, unrelated, currently-unused class (built on muledge2d rather than MSline2d segments). Mind the module path when importing.

Examples

from upxo.geoEntities.mulsline2d import MSline2d, ring2d
from upxo.geoEntities.sline2d import Sline2d as sl2d

msl1 = MSline2d.from_lines(
    [sl2d(0.0,0.0,1.0,1.0), sl2d(1.0,1.0,1.5,1.5),
     sl2d(1.5,1.5,2.5,2.5)], close=False)
msl2 = MSline2d.from_lines(
    [sl2d(2.5,2.5,4.0,4.0), sl2d(4.0,4.0,4.0,6.0)], close=False)
msl3 = MSline2d.from_lines(
    [sl2d(4.0,6.0,4.0,8.0), sl2d(4.0,8.0,10.0,10.0)], close=False)
msl4 = MSline2d.from_lines(
    [sl2d(0,0,20,10), sl2d(20,10,10,10)], close=False)

R = ring2d([msl1, msl2, msl3, msl4])
R.assess_spatial_continuity()
EPS_coord_coincide = 1e-08
clone(seg_clones)[source]

Return a copy of this ring that uses cloned segments.

Parameters:

seg_clones (dict) – {id(original segment): cloned segment}. Segments missing from it are cloned and added, so a segment shared by several rings is cloned once and stays shared among the copies.

Notes

coords, conn0 and conn1 are not populated anywhere in the current geometrification/meshing pipeline before a ring is cloned, so this has had no observed effect; they are still copied by value rather than by reference so that connectivity1() or set_coords() called later on the original or the clone cannot silently corrupt the other, matching the “independent copy” this method promises.

translate(dx, dy, seen=None)[source]

Translate every point of this ring by (dx, dy), in place.

Walks every Sline2d endpoint by object identity – junction points and consecutive-line nodes are shared across segments – so a point shared by several segments/lines is moved exactly once, then resyncs each Sline2d’s cached x0/y0/x1/y1 to match.

Parameters:

seen (set, optional) – {id(point): ...} of points already translated. Pass the same set across several translate calls (e.g. one per sibling ring in the same cluster) when their rings may share Point2d objects at a common wall, so a shared point is not translated once per ring that references it.

translated_copy(dx, dy, seg_copies)[source]

Return a copy of this ring shifted by (dx, dy) that shares no point or line with it; the original is not changed.

MSline2d.clone shares its point objects with the original, so clone followed by translate() would move the original too.

Parameters:

seg_copies (dict) – {id(original segment): its translated copy}. Pass one dict for rings that may share walls, so a wall shared by several rings is copied once and stays shared among the copies.

segments
segids
segflips
nsegs

Proceed continuities, flips = self.assess_spatial_continuity(segments) if not all(continuities):

if self.assess_possibility_of_continuity(segments):

self.get_continuity_enforcement_indices() self.set_spatial_continuity(enforce_indices)

else:

raise ValueError(‘Invalid segments morphology passed.’)

Type:

overlaps = self.assess_segment_point_overlaps(segments) # False

add_segment_unsafe(segment)[source]

Add segment to ring without validation.

Parameters:

segment (MSline2d) – Segment to append (no continuity checks).

add_segid(segid)[source]

Add segment identifier to tracking list.

Parameters:

segid (hashable) – Segment identifier to track.

add_segflip(segflip)[source]

Add segment flip status flag.

Parameters:

segflip (bool) – True if segment was flipped, False otherwise.

check_closed()[source]

Check if ring is topologically closed.

Returns:

True if end node of last segment connects to start node of first segment (within EPS_coord_coincide tolerance).

Return type:

bool

close()[source]

Enforce topological closure by connecting end to start.

Notes

Adds first segment’s start node as last segment’s end node and updates internal node list.

connectivity0(flip_if_possible=True)[source]

Assess closure of 1st & last segment with option to close if possible.

Parameters:

flip_if_possible (bool, optional) – Whether to flip the last segment when doing so would close the ring.

Returns:

  • closed (bool) – True if the ring is already closed or has been closed.

  • last_seg_flipped (bool) – True if the last segment was flipped to achieve closure.

  • flip_possible (bool) – True when closure is possible by flipping the last segment.

connectivity1()[source]

Assess closure of all intermediate segments.

Returns:

Updates self.conn1 with pairwise segment-continuity flags.

Return type:

None

assess_segment_point_overlaps(line_check=False)[source]

Check all segments for duplicate/overlapping nodes.

Parameters:

line_check (bool, optional) – If True, also check for overlapping lines (not yet operational).

Returns:

Boolean list for each segment indicating overlap presence.

Return type:

list

assess_reorder_requirement()[source]

Check if reordering or flipping of segments is needed.

Notes

To be developed. Currently evaluates closure status and potential flips needed to enforce continuity.

set_coords()[source]

Build and cache coordinate array from all segments.

Notes

Combines node coordinates of all segments, skipping duplicate nodes at segment junctions.

get_coords()[source]

Extract and return full coordinate array from ring.

Returns:

Coordinates of all ring nodes.

Return type:

numpy.ndarray

property centroid

Compute centroid of the ring shape.

Returns:

Mean (x, y) coordinate of all ring nodes.

Return type:

numpy.ndarray

create_coords_from_segments(force_close=False)[source]

Build coordinate array from all segments, respecting flips.

Parameters:

force_close (bool, optional) – If True, append first coordinate to end to ensure closure.

Returns:

Coordinates from all segments, concatenated in order.

Return type:

numpy.ndarray

force_close_coordinates(coord, assess_first=True)[source]

Ensure coordinate array is closed (end connects to start).

Parameters:
  • coord (numpy.ndarray) – Input coordinate array.

  • assess_first (bool, optional) – If True, check if already closed before forcing.

Returns:

Closed coordinate array.

Return type:

numpy.ndarray

Notes

Unsafe. Not intended for user. Uses EPS_coord_coincide tolerance.

create_polygon_from_segments()[source]

Convert ring to Shapely Polygon object.

Returns:

Polygon representation of the ring.

Return type:

shapely.geometry.Polygon

create_polygon_from_coords()[source]

Create Shapely Polygon from coordinates.

Returns:

Polygon from extracted ring coordinates.

Return type:

shapely.geometry.Polygon

property area

Compute area enclosed by ring polygon.

Returns:

Area of ring polygon.

Return type:

float

property perimeter

Compute perimeter (boundary length) of ring.

Returns:

Total perimeter of ring polygon.

Return type:

float

property is_closed

Check if ring is topologically closed.

Returns:

True if ring forms a closed loop.

Return type:

bool

property nsegments

Return number of segments in ring.

Returns:

Count of constituent MSline2d segments.

Return type:

int

property ncoords

Return total number of nodes in ring.

Returns:

Count of unique coordinate nodes.

Return type:

int

property tree

Build spatial KD-tree index for ring coordinates.

Returns:

KD-tree for neighbor/distance queries on ring nodes.

Return type:

scipy.spatial.cKDTree

assess_spatial_continuity()[source]

From list of multisline2d, multislines2d, do all (i+1)^th multisline2d follow i^th multisline2d? with or without the need for flips.

Returns:

  • continuity (bool) – True when all segments form a chain, irrespective of whether flips are needed.

  • flip_needed (bool) – True if any multi-line object needs flipping to ensure spatial continuity.

  • i_precede_chain (list) – do_i_precede results for every i:i+1 pair.

plot_segs(plot_centroid=False, centroid_text='', plot_coord_order=False, visualize_flip_req=False)[source]

Visualize ring segments with optional annotations.

Parameters:
  • plot_centroid (bool, optional) – If True, plot ring centroid marker and label.

  • centroid_text (str, optional) – Label text for centroid.

  • plot_coord_order (bool, optional) – If True, plot coordinate connectivity with dashed line.

  • visualize_flip_req (bool, optional) – If True, use dashed lines for flipped segments.

Returns:

Axes object with plotted segments (color-coded).

Return type:

matplotlib.axes.Axes

get_coords_newdef()[source]

Extract coordinates in new definition format.

Returns:

(segment_coords_list, combined_coords) where: - segment_coords_list: List of coordinate arrays per segment - combined_coords: Concatenated coordinate array from all segments

Return type:

tuple

coords
closed
conn0
conn1
class upxo.geoEntities.mulsline2d.mulring2d(rings)[source]

Bases: object

Collection of 2D ring objects with shared interface bookkeeping.

Parameters:

rings (list) – Ring objects to be grouped into the multi-ring container.

rings

Individual ring objects supplied at construction.

Type:

list

jp

Junction-point storage placeholder.

Type:

object

ip

Interface-point storage placeholder.

Type:

object

Notes

The implementation is currently a placeholder for future multi-ring workflows. Existing notes below describe the intended data model and are retained for development continuity.

rings
build_points_list()[source]

Build a flat list of all points from the ring collection.

Raises:

NotImplementedError – Always raised because point-list construction is not implemented.

set_coords()[source]

Set coordinate arrays from the ring collection.

Raises:

NotImplementedError – Always raised because multi-ring coordinate caching is not implemented.

jp
ip
export_abaqus_for_meshing()[source]

Export the geometry in Abaqus mesh-input format.

Raises:

NotImplementedError – Always raised because Abaqus export is not implemented.

mesh()[source]

Generate a mesh for the ring collection.

Raises:

NotImplementedError – Always raised because multi-ring meshing is not implemented.