Skip to content

Commit 20a0962

Browse files
committed
improve sequentialize
1 parent 29dd567 commit 20a0962

File tree

1 file changed

+89
-45
lines changed

1 file changed

+89
-45
lines changed

gustaf/utils/connec.py

Lines changed: 89 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@
99

1010
import numpy as np
1111

12+
try:
13+
import napf
14+
except ImportError:
15+
from gustaf.helpers.raise_if import ModuleImportRaiser
16+
17+
napf = ModuleImportRaiser("napf")
18+
1219
from gustaf import helpers, settings
1320
from gustaf.utils import arr
1421

@@ -562,45 +569,32 @@ def sorted_unique(connectivity, sorted_=False):
562569
)
563570

564571

565-
def _sequentialize_ordered_edges(outline_edges, return_edges=False):
572+
def _sequentialize_directed_edges(edges, start=None, return_edges=False):
566573
"""
567-
Organize outline edges, so that it describes a polygon.
568-
Edges are expected to be extracted using `gus.Mesh.edges`, from
569-
wind-consistent meshes.
570-
Could utilizes `scipy.sparse.coo_matrix`, alternatively.
571-
572-
Parameters
573-
-----------
574-
outline_edges: (n, 2) list-like
575-
return_edges: bool
576-
(Optional) Default is False. If set True, returns polygon as edges.
577-
578-
Returns
579-
--------
580-
polygons: list
581-
list of polygon vertices. Or edges iff return_edges is True.
582-
583-
Examples
584-
---------
585-
>>> m = gus.load_mesh("path_to_mesh_2d.stl")
586-
>>> polygon_edges = edges_to_polygon(m.edges()[m.single_edges()])
574+
Sequentialize directed edges.
587575
"""
588576
# we want to have an np array
589-
outline_edges = np.asanyarray(outline_edges)
577+
edges = np.asanyarray(edges)
590578

591579
# Build a lookup_array
592-
lookup_array = np.full(
593-
outline_edges.max() + 1, -1, dtype=settings.INT_DTYPE
594-
)
595-
lookup_array[outline_edges[:, 0]] = outline_edges[:, 1]
580+
lookup_array = np.full(edges.max() + 1, -1, dtype=settings.INT_DTYPE)
581+
lookup_array[edges[:, 0]] = edges[:, 1]
596582

597583
# select starting point - lowest index
598-
starting_point = int(outline_edges.min())
584+
starting_point = int(edges.min()) if start is None else int(start)
585+
599586
# initialize a set to keep track of processes vertices
600-
next_candidates = set(outline_edges[:, 0])
587+
next_candidates = set(edges[:, 0])
588+
# we want to keep track of single occurrences, as they are line start
589+
line_starts = set(np.where(np.bincount(edges.ravel()) == 1)[0])
590+
# for this to work, we can't have a line that starts at column 1.
591+
# so, we remove those.
592+
ls_col1 = set(np.where(np.bincount(edges[:, 1].ravel()) == 1)[0])
593+
line_starts.difference_update(ls_col1)
594+
601595
polygons = []
602596
is_polygon = []
603-
for _ in range(len(outline_edges)):
597+
for _ in range(len(edges)):
604598
# Get polygon - start with first two points
605599
polygon = [starting_point]
606600
polygon.append(lookup_array[polygon[-1]])
@@ -625,11 +619,17 @@ def _sequentialize_ordered_edges(outline_edges, return_edges=False):
625619
# check if we counted all the edges
626620
# if so, exit
627621
next_candidates.difference_update(polygons[-1])
628-
if len(next_candidates) == 0:
622+
line_starts.difference_update(polygons[-1])
623+
624+
if len(next_candidates) == len(line_starts) == 0:
629625
break
630626

631627
# let's try to find the next starting point
632-
starting_point = min(next_candidates)
628+
starting_point = None
629+
if len(line_starts) != 0:
630+
starting_point = min(line_starts)
631+
else:
632+
starting_point = min(next_candidates)
633633

634634
if not return_edges:
635635
return polygons, is_polygon
@@ -639,13 +639,14 @@ def _sequentialize_ordered_edges(outline_edges, return_edges=False):
639639
for p, is_p in zip(polygons, is_polygon):
640640
polygon_edges.append(sequence_to_edges(p, closed=is_p))
641641

642-
return polygon_edges
642+
return polygon_edges, is_polygon
643643

644644

645-
def _sequentialize_edges(edges, return_edges=False):
646-
""" """
647-
import napf
648-
import numpy as np
645+
def _sequentialize_edges(edges, start=None, return_edges=False):
646+
"""
647+
sequentialize undirected edges. No overlaps are allowed, for now.
648+
"""
649+
edges = np.asanyarray(edges)
649650

650651
# only applicable to closed polygons and open lines
651652
# not for arbitrarily connected edges
@@ -673,16 +674,19 @@ def _sequentialize_edges(edges, return_edges=False):
673674
# radius search size
674675
r = 0.1
675676

676-
current_id = 0
677-
start_value = int(e.a[0])
677+
current_id = np.argmin(e.a) if start is None else start
678+
start_value = int(e.a[current_id])
678679
other_col = e.b
679680

680681
polygons = []
681682
is_polygon = []
682683
for _ in range(len(edges)):
683684
polygon = [start_value, other_col[current_id]]
684685

685-
while polygon[0] != polygon[-1]:
686+
# while polygon[0] != polygon[-1]:
687+
for _ in range(len(next_candidates)):
688+
if polygon[0] == polygon[-1]:
689+
break
686690
hits = []
687691
# search for ids
688692
a_ids = t.a.radius_search([[polygon[-1]]], r, True)[0][0]
@@ -743,33 +747,73 @@ def _sequentialize_edges(edges, return_edges=False):
743747
# leftover candidates
744748
start_value = None
745749
if len(line_starts) != 0:
746-
start_value = line_starts[0]
750+
start_value = min(line_starts)
747751
else:
748-
start_value = next_candidates[0]
752+
start_value = min(next_candidates)
749753

750754
# adjust state values
751-
a_ids = t.a.radius_search([[polygon[-1]]], r, True)[0][0]
755+
a_ids = t.a.radius_search([[start_value]], r, True)[0][0]
752756
if len(a_ids) != 0:
753757
current_id = a_ids[0]
754758
other_col = e.b
755759
continue
756-
b_ids = t.b.radius_search([[polygon[-1]]], r, True)[0][0]
760+
b_ids = t.b.radius_search([[start_value]], r, True)[0][0]
757761
if len(b_ids) != 0:
758762
current_id = b_ids[0]
759763
other_col = e.a
760764
continue
761765

762766
raise RuntimeError(
763767
"Something went wrong. Please report this issue to "
764-
"github.com/tataratat/gustaf/issues]"
768+
"github.com/tataratat/gustaf/issues"
765769
)
766770

767771
if not return_edges:
768-
return polygons
772+
return polygons, is_polygon
769773

770774
else:
771775
polygon_edges = []
772776
for p, is_p in zip(polygons, is_polygon):
773777
polygon_edges.append(sequence_to_edges(p, closed=is_p))
774778

775779
return polygon_edges
780+
781+
782+
def sequentialize_edges(edges, start=None, return_edges=False, directed=False):
783+
"""
784+
Organize edge connectivities to describe polygon or a line.
785+
This supports edges that describes separated/individual polygons and lines.
786+
In other words, it doesn't support edges of overlapping vertices.
787+
788+
Parameters
789+
-----------
790+
edges: (n, 2) list-like
791+
start: int
792+
(Optional) Specify starting point. It will take minimum index otherwise.
793+
return_edges: bool
794+
(Optional) Default is False. If set True, returns sequences as edges.
795+
directed: bool
796+
(Optional) Default is False. Set True, if given edges are directed.
797+
It should return the result faster.
798+
799+
Returns
800+
--------
801+
sequences: list
802+
list of vertex ids. Or edges iff return_edges is True.
803+
is_polygon: list
804+
Tells if the sequence is a polygon or a line.
805+
806+
Examples
807+
---------
808+
>>> e = gus.Edges(vertices, edges)
809+
>>> ordered_sequence, is_polygon = sequentialize_edges(e.edges)
810+
811+
>>> f = gus.Faces(vertices, faces)
812+
>>> ordered_sequence, is_polygon = sequentialize_edges(
813+
... f.edges()[f.single_edges()]
814+
... )
815+
"""
816+
if directed:
817+
return _sequentialize_directed_edges(edges, start, return_edges)
818+
else:
819+
return _sequentialize_edges(edges, start, return_edges)

0 commit comments

Comments
 (0)