56
56
RIBBON = "RIBBON"
57
57
58
58
59
+ def is_boundary (loop ):
60
+ return len (loop .link_loops ) == 0
61
+
62
+
59
63
class DirectedLoop :
60
64
def __init__ (self , loop , forward ):
61
65
self .loop = loop
@@ -75,7 +79,7 @@ def next_face_loop(self):
75
79
loop = loop .link_loop_next
76
80
else :
77
81
loop = loop .link_loop_prev
78
- if len (loop . link_loops ) != 0 :
82
+ if not is_boundary (loop ) :
79
83
break
80
84
return DirectedLoop (loop , forward )
81
85
@@ -261,17 +265,39 @@ def get_cached_vote(edge_index):
261
265
else :
262
266
return cached_votes .setdefault (edge_index , count_votes (edge_index ))
263
267
264
- v0 = randrange (len (bm .verts ))
265
- for e in bm .verts [v0 ].link_edges :
266
- color_edge (e , TWIST_CW )
268
+ # For each disconnected island of edges
269
+ while True :
270
+ uncolored = [i for i , color in enumerate (coloring ) if color is None ]
271
+ if not uncolored :
272
+ break
273
+
274
+ # Pick a random point
275
+ v0 = choice (bm .edges [choice (uncolored )].verts )
276
+
277
+ # Set initial coloring
278
+ for e in v0 .link_edges :
279
+ color_edge (e , TWIST_CW )
280
+
281
+ # Explore from frontier
282
+ while frontier :
283
+ # First clear out any boundaries from the frontier
284
+ while True :
285
+ found_boundaries = False
286
+ for e in list (frontier ):
287
+ edge = bm .edges [e ]
288
+ if is_boundary (edge .link_loops [0 ]):
289
+ color_edge (edge , IGNORE )
290
+ found_boundaries = True
291
+ if not found_boundaries :
292
+ break
293
+ # Color the best choice of edge
294
+ votes = {e : get_cached_vote (e ) for e in frontier }
295
+ m = max (max (v .cw , v .ccw ) for v in votes .values ())
296
+ best_edge , best_votes = choice ([(k , v ) for (k , v ) in votes .items () if v .cw == m or v .ccw == m ])
297
+ set_twist = TWIST_CW if best_votes .cw > best_votes .ccw else TWIST_CCW
298
+ color_edge (bm .edges [best_edge ], set_twist )
267
299
268
- # Color the best choice of edge
269
- while frontier :
270
- votes = {e : get_cached_vote (e ) for e in frontier }
271
- m = max (max (v .cw , v .ccw ) for v in votes .values ())
272
- best_edge , best_votes = choice ([(k , v ) for (k , v ) in votes .items () if v .cw == m or v .ccw == m ])
273
- set_twist = TWIST_CW if best_votes .cw > best_votes .ccw else TWIST_CCW
274
- color_edge (bm .edges [best_edge ], set_twist )
300
+ assert all (coloring ), "Failed to assign some twists when computing twill"
275
301
276
302
return coloring
277
303
@@ -283,6 +309,8 @@ def get_offset(weave_up, weave_down, twist, forward):
283
309
return weave_down if forward else weave_up
284
310
elif twist is STRAIGHT :
285
311
return (weave_down + weave_up ) / 2.0
312
+ else :
313
+ assert False , "Unexpected twist type " + twist
286
314
287
315
288
316
def lerp (v1 , v2 , t ):
@@ -482,7 +510,7 @@ def make_loop(d):
482
510
# Attempt to start a loop at each untouched loop in the entire mesh
483
511
for face in bm .faces :
484
512
for loop in face .loops :
485
- if len (loop . link_loops ) == 0 : continue
513
+ if is_boundary (loop ) : continue
486
514
if not loops_exited [loop ]: make_loop (DirectedLoop (loop , True ))
487
515
if not loops_entered [loop ]: make_loop (DirectedLoop (loop , False ))
488
516
0 commit comments