@@ -63,33 +63,97 @@ public override void OnUpdate()
6363 AngleFromEdges ( edges [ 0 ] , edges [ 1 ] ) ;
6464 }
6565
66+ protected override IEnumerable < MeshEdge > ConvertSelectionToCurrentType ( )
67+ {
68+ var selectedFaces = Selection . OfType < MeshFace > ( ) . ToHashSet ( ) ;
69+ var selectedVertices = Selection . OfType < MeshVertex > ( ) . ToHashSet ( ) ;
70+
71+ foreach ( var face in selectedFaces )
72+ {
73+ if ( ! face . IsValid ( ) )
74+ continue ;
75+
76+ var mesh = face . Component . Mesh ;
77+ var edges = mesh . GetFaceEdges ( face . Handle ) ;
78+
79+ foreach ( var edge in edges )
80+ {
81+ if ( edge . IsValid )
82+ yield return new MeshEdge ( face . Component , edge ) ;
83+ }
84+ }
85+
86+ var candidateEdges = new HashSet < MeshEdge > ( ) ;
87+
88+ foreach ( var vertex in selectedVertices )
89+ {
90+ if ( ! vertex . IsValid ( ) )
91+ continue ;
92+
93+ var mesh = vertex . Component . Mesh ;
94+ mesh . GetEdgesConnectedToVertex ( vertex . Handle , out var edges ) ;
95+
96+ foreach ( var edge in edges )
97+ {
98+ if ( edge . IsValid )
99+ candidateEdges . Add ( new MeshEdge ( vertex . Component , edge ) ) ;
100+ }
101+ }
102+
103+ foreach ( var edge in candidateEdges )
104+ {
105+ var mesh = edge . Component . Mesh ;
106+ mesh . GetEdgeVertices ( edge . Handle , out var vertexA , out var vertexB ) ;
107+
108+ bool bothVerticesSelected =
109+ selectedVertices . Contains ( new MeshVertex ( edge . Component , vertexA ) ) &&
110+ selectedVertices . Contains ( new MeshVertex ( edge . Component , vertexB ) ) ;
111+
112+ if ( bothVerticesSelected )
113+ {
114+ yield return edge ;
115+ }
116+ }
117+ }
118+
66119 private static void DrawOpenEdge ( MeshEdge edge )
67120 {
68- var hFace = edge . Component . Mesh . GetHalfEdgeFace ( edge . Handle ) ;
69- var spacing = 2.0f ;
121+ var mesh = edge . Component . Mesh ;
122+ var hFace = mesh . GetHalfEdgeFace ( edge . Handle ) ;
123+ var spacing = 1.5f ;
124+
70125 if ( ! hFace . IsValid )
71126 {
72- hFace = edge . Component . Mesh . GetHalfEdgeFace ( edge . Component . Mesh . GetOppositeHalfEdge ( edge . Handle ) ) ;
127+ hFace = mesh . GetHalfEdgeFace ( mesh . GetOppositeHalfEdge ( edge . Handle ) ) ;
73128 spacing *= - 1.0f ;
74129 }
75130
131+ if ( ! hFace . IsValid )
132+ return ;
133+
76134 var line = edge . Line ;
77135 var a = edge . Transform . PointToWorld ( line . Start ) ;
78136 var b = edge . Transform . PointToWorld ( line . End ) ;
79-
80- edge . Component . Mesh . ComputeFaceNormal ( hFace , out var normal ) ;
81- var direction = ( line . End - line . Start ) . Normal ;
82137 var length = a . Distance ( b ) ;
138+
139+ mesh . ComputeFaceNormal ( hFace , out var normal ) ;
140+ var direction = ( b - a ) . Normal ;
83141 var tangent = normal . Cross ( direction ) ;
84- var numHashes = length / 5.0f ;
85142
86- for ( int i = 0 ; i < numHashes ; ++ i )
143+ var cameraDistance = Gizmo . Camera . Position . Distance ( ( a + b ) * 0.5f ) ;
144+ var visualScale = ( cameraDistance * 0.008f ) . Clamp ( 0.05f , 3f ) ;
145+
146+ spacing *= visualScale ;
147+
148+ var hashSpacing = ( 2.5f * visualScale ) . Clamp ( 0.5f , 50f ) ;
149+ var numHashes = Math . Max ( 3 , ( int ) ( length / hashSpacing ) ) ;
150+
151+ for ( int i = 0 ; i < numHashes ; i ++ )
87152 {
88- var d = i / numHashes * length ;
89- var p = line . Start + direction * d ;
90- var a2 = edge . Transform . PointToWorld ( p ) ;
91- var b2 = edge . Transform . PointToWorld ( p + tangent * spacing ) ;
92- Gizmo . Draw . Line ( a2 , b2 ) ;
153+ var t = i / ( float ) ( numHashes - 1 ) ;
154+ var position = Vector3 . Lerp ( a , b , t ) ;
155+ var hashEnd = position + tangent * spacing ;
156+ Gizmo . Draw . Line ( position , hashEnd ) ;
93157 }
94158 }
95159
@@ -176,21 +240,116 @@ private static void AngleFromEdges( MeshEdge edge1, MeshEdge edge2 )
176240
177241 private void SelectEdgeLoop ( )
178242 {
179- var edge = MeshTrace . GetClosestEdge ( 8 ) ;
180- if ( ! edge . IsValid ( ) )
243+ var targetEdge = MeshTrace . GetClosestEdge ( 8 ) ;
244+ if ( ! targetEdge . IsValid ( ) )
245+ return ;
246+
247+ if ( Application . KeyboardModifiers . HasFlag ( KeyboardModifiers . Shift ) && TrySelectEdgePath ( targetEdge ) )
181248 return ;
182249
183250 if ( ! Application . KeyboardModifiers . HasFlag ( KeyboardModifiers . Shift ) )
184251 Selection . Clear ( ) ;
185252
186253 if ( ! Application . KeyboardModifiers . HasFlag ( KeyboardModifiers . Ctrl ) )
187254 {
188- edge . Component . Mesh . FindEdgeLoopForEdges ( [ edge . Handle ] , out var hEdges ) ;
255+ targetEdge . Component . Mesh . FindEdgeLoopForEdges ( [ targetEdge . Handle ] , out var hEdges ) ;
189256 foreach ( var hEdge in hEdges )
190- Selection . Add ( new MeshEdge ( edge . Component , hEdge ) ) ;
257+ Selection . Add ( new MeshEdge ( targetEdge . Component , hEdge ) ) ;
191258 }
192259 }
193260
261+ private bool TrySelectEdgePath ( MeshEdge targetEdge )
262+ {
263+ var selected = Selection . OfType < MeshEdge > ( )
264+ . Where ( e => e . IsValid ( ) && e . Component == targetEdge . Component )
265+ . ToList ( ) ;
266+
267+ if ( selected . Count == 0 || selected . Count > 2 )
268+ return false ;
269+
270+ var startEdge = selected . FirstOrDefault ( e =>
271+ e . Handle != targetEdge . Handle &&
272+ e . Handle != targetEdge . Component . Mesh . GetOppositeHalfEdge ( targetEdge . Handle )
273+ ) ;
274+
275+ if ( ! startEdge . IsValid ( ) )
276+ return false ;
277+
278+ var path = FindShortestEdgePath ( startEdge , targetEdge ) ;
279+ if ( path == null || path . Count == 0 )
280+ return false ;
281+
282+ foreach ( var edge in path . Where ( e => ! Selection . Contains ( e ) ) )
283+ Selection . Add ( edge ) ;
284+
285+ return true ;
286+ }
287+
288+ private List < MeshEdge > FindShortestEdgePath ( MeshEdge start , MeshEdge end )
289+ {
290+ if ( start . Component != end . Component )
291+ return null ;
292+
293+ var mesh = start . Component . Mesh ;
294+ var queue = new Queue < HalfEdgeHandle > ( ) ;
295+ var visited = new HashSet < HalfEdgeHandle > ( ) ;
296+ var parent = new Dictionary < HalfEdgeHandle , HalfEdgeHandle > ( ) ;
297+
298+ var startHandle = start . Handle . Index < mesh . GetOppositeHalfEdge ( start . Handle ) . Index ?
299+ start . Handle : mesh . GetOppositeHalfEdge ( start . Handle ) ;
300+ var endHandle = end . Handle . Index < mesh . GetOppositeHalfEdge ( end . Handle ) . Index ?
301+ end . Handle : mesh . GetOppositeHalfEdge ( end . Handle ) ;
302+
303+ queue . Enqueue ( startHandle ) ;
304+ visited . Add ( startHandle ) ;
305+
306+ while ( queue . Count > 0 )
307+ {
308+ var current = queue . Dequeue ( ) ;
309+
310+ if ( current == endHandle || mesh . GetOppositeHalfEdge ( current ) == endHandle )
311+ {
312+ var path = new List < MeshEdge > ( ) ;
313+ var step = current ;
314+
315+ while ( step . IsValid )
316+ {
317+ path . Add ( new MeshEdge ( start . Component , step ) ) ;
318+ if ( step == startHandle || mesh . GetOppositeHalfEdge ( step ) == startHandle )
319+ break ;
320+ if ( ! parent . TryGetValue ( step , out step ) )
321+ break ;
322+ }
323+
324+ path . Reverse ( ) ;
325+ return path ;
326+ }
327+
328+ mesh . GetEdgeVertices ( current , out var vertexA , out var vertexB ) ;
329+
330+ mesh . GetEdgesConnectedToVertex ( vertexA , out var edgesA ) ;
331+ mesh . GetEdgesConnectedToVertex ( vertexB , out var edgesB ) ;
332+
333+ foreach ( var neighbor in edgesA . Concat ( edgesB ) )
334+ {
335+ if ( neighbor . IsValid && ! visited . Contains ( neighbor ) )
336+ {
337+ var oppositeNeighbor = mesh . GetOppositeHalfEdge ( neighbor ) ;
338+ if ( ! visited . Contains ( oppositeNeighbor ) )
339+ {
340+ visited . Add ( neighbor ) ;
341+ visited . Add ( oppositeNeighbor ) ;
342+ parent [ neighbor ] = current ;
343+ parent [ oppositeNeighbor ] = current ;
344+ queue . Enqueue ( neighbor ) ;
345+ }
346+ }
347+ }
348+ }
349+
350+ return null ;
351+ }
352+
194353 private void SelectEdge ( )
195354 {
196355 var edge = MeshTrace . GetClosestEdge ( 8 ) ;
0 commit comments