9
9
10
10
import numpy as np
11
11
12
+ try :
13
+ import napf
14
+ except ImportError :
15
+ from gustaf .helpers .raise_if import ModuleImportRaiser
16
+
17
+ napf = ModuleImportRaiser ("napf" )
18
+
12
19
from gustaf import helpers , settings
13
20
from gustaf .utils import arr
14
21
@@ -562,45 +569,32 @@ def sorted_unique(connectivity, sorted_=False):
562
569
)
563
570
564
571
565
- def _sequentialize_ordered_edges ( outline_edges , return_edges = False ):
572
+ def _sequentialize_directed_edges ( edges , start = None , return_edges = False ):
566
573
"""
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.
587
575
"""
588
576
# we want to have an np array
589
- outline_edges = np .asanyarray (outline_edges )
577
+ edges = np .asanyarray (edges )
590
578
591
579
# 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 ]
596
582
597
583
# 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
+
599
586
# 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
+
601
595
polygons = []
602
596
is_polygon = []
603
- for _ in range (len (outline_edges )):
597
+ for _ in range (len (edges )):
604
598
# Get polygon - start with first two points
605
599
polygon = [starting_point ]
606
600
polygon .append (lookup_array [polygon [- 1 ]])
@@ -625,11 +619,17 @@ def _sequentialize_ordered_edges(outline_edges, return_edges=False):
625
619
# check if we counted all the edges
626
620
# if so, exit
627
621
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 :
629
625
break
630
626
631
627
# 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 )
633
633
634
634
if not return_edges :
635
635
return polygons , is_polygon
@@ -639,13 +639,14 @@ def _sequentialize_ordered_edges(outline_edges, return_edges=False):
639
639
for p , is_p in zip (polygons , is_polygon ):
640
640
polygon_edges .append (sequence_to_edges (p , closed = is_p ))
641
641
642
- return polygon_edges
642
+ return polygon_edges , is_polygon
643
643
644
644
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 )
649
650
650
651
# only applicable to closed polygons and open lines
651
652
# not for arbitrarily connected edges
@@ -673,16 +674,19 @@ def _sequentialize_edges(edges, return_edges=False):
673
674
# radius search size
674
675
r = 0.1
675
676
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 ])
678
679
other_col = e .b
679
680
680
681
polygons = []
681
682
is_polygon = []
682
683
for _ in range (len (edges )):
683
684
polygon = [start_value , other_col [current_id ]]
684
685
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
686
690
hits = []
687
691
# search for ids
688
692
a_ids = t .a .radius_search ([[polygon [- 1 ]]], r , True )[0 ][0 ]
@@ -743,33 +747,73 @@ def _sequentialize_edges(edges, return_edges=False):
743
747
# leftover candidates
744
748
start_value = None
745
749
if len (line_starts ) != 0 :
746
- start_value = line_starts [ 0 ]
750
+ start_value = min ( line_starts )
747
751
else :
748
- start_value = next_candidates [ 0 ]
752
+ start_value = min ( next_candidates )
749
753
750
754
# 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 ]
752
756
if len (a_ids ) != 0 :
753
757
current_id = a_ids [0 ]
754
758
other_col = e .b
755
759
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 ]
757
761
if len (b_ids ) != 0 :
758
762
current_id = b_ids [0 ]
759
763
other_col = e .a
760
764
continue
761
765
762
766
raise RuntimeError (
763
767
"Something went wrong. Please report this issue to "
764
- "github.com/tataratat/gustaf/issues] "
768
+ "github.com/tataratat/gustaf/issues"
765
769
)
766
770
767
771
if not return_edges :
768
- return polygons
772
+ return polygons , is_polygon
769
773
770
774
else :
771
775
polygon_edges = []
772
776
for p , is_p in zip (polygons , is_polygon ):
773
777
polygon_edges .append (sequence_to_edges (p , closed = is_p ))
774
778
775
779
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