1- use super :: path:: { Path , curve_type_at} ;
1+ use super :: path:: { CurveType , Path , curve_type_at} ;
22use core_types:: list:: List ;
33
44/// One path's reference to a pool point, naming where in the weave that point is used.
55#[ derive( Clone , Copy , Debug , Default , PartialEq , Eq ) ]
66pub struct Reference {
77 pub path : u32 ,
88 pub index : u32 ,
9+ /// The curve type of the path holding this reference. Its stride decides whether the reference names an anchor.
10+ pub curve_type : CurveType ,
11+ /// The segment ends radiating from this reference: none for a handle or a path's only reference, one at a path end, two along a path.
12+ pub spokes : u8 ,
13+ }
14+
15+ impl Reference {
16+ /// Whether this reference sits at an anchor slot of its path rather than a handle slot.
17+ pub fn is_anchor ( & self ) -> bool {
18+ ( self . index as usize ) . is_multiple_of ( self . curve_type . stride ( ) )
19+ }
920}
1021
1122/// Reverse index from each pool point to the references naming it.
@@ -39,11 +50,21 @@ impl Connectivity {
3950 let mut cursors = starts[ ..point_count] . to_vec ( ) ;
4051 let mut references = vec ! [ Reference :: default ( ) ; starts[ point_count] as usize ] ;
4152 for ( path_index, path) in paths. iter_element_values ( ) . enumerate ( ) {
53+ // Each path's curve type is read once here, so queries never look it up again
54+ let curve_type = curve_type_at ( paths, path_index) ;
55+ let last = path. 0 . len ( ) . saturating_sub ( 1 ) ;
56+
4257 for ( reference_index, point) in path. references ( ) . enumerate ( ) {
4358 let Some ( cursor) = cursors. get_mut ( point as usize ) else { continue } ;
59+
60+ let anchor = reference_index. is_multiple_of ( curve_type. stride ( ) ) ;
61+ let spokes = if anchor { u8:: from ( reference_index > 0 ) + u8:: from ( reference_index < last) } else { 0 } ;
62+
4463 references[ * cursor as usize ] = Reference {
4564 path : path_index as u32 ,
4665 index : reference_index as u32 ,
66+ curve_type,
67+ spokes,
4768 } ;
4869 * cursor += 1 ;
4970 }
@@ -66,27 +87,21 @@ impl Connectivity {
6687 !self . references ( point) . is_empty ( )
6788 }
6889
90+ /// Whether this point is a handle, named by paths only at handle slots.
91+ pub fn is_handle ( & self , point : u32 ) -> bool {
92+ let references = self . references ( point) ;
93+ !references. is_empty ( ) && !references. iter ( ) . any ( Reference :: is_anchor)
94+ }
95+
6996 /// The segment ends meeting at this point: 1 at a free end, 2 along a chain, 3 or more at a junction.
7097 /// Handle references contribute none, since only anchors have segments radiating from them.
71- pub fn spokes ( & self , point : u32 , paths : & List < Path > ) -> usize {
72- self . references ( point)
73- . iter ( )
74- . filter_map ( |reference| {
75- let path = paths. element ( reference. path as usize ) ?;
76- if !path. is_anchor_reference ( reference. index as usize , curve_type_at ( paths, reference. path as usize ) ) {
77- return None ;
78- }
79-
80- let last = path. 0 . len ( ) . saturating_sub ( 1 ) as u32 ;
81- Some ( usize:: from ( reference. index > 0 ) + usize:: from ( reference. index < last) )
82- } )
83- . sum ( )
98+ pub fn spokes ( & self , point : u32 ) -> usize {
99+ self . references ( point) . iter ( ) . map ( |reference| usize:: from ( reference. spokes ) ) . sum ( )
84100 }
85101}
86102
87103#[ cfg( test) ]
88104mod tests {
89- use super :: super :: path:: CurveType ;
90105 use super :: * ;
91106 use core_types:: list:: { ATTR_CURVE_TYPE , Item } ;
92107
@@ -101,21 +116,28 @@ mod tests {
101116 paths
102117 }
103118
119+ fn places ( connectivity : & Connectivity , point : u32 ) -> Vec < ( u32 , u32 ) > {
120+ connectivity. references ( point) . iter ( ) . map ( |reference| ( reference. path , reference. index ) ) . collect ( )
121+ }
122+
104123 // A closed circle crossed by a chord welded at two of its anchors, plus two points no path names
105124 #[ test]
106125 fn spokes_at_welds_and_free_points ( ) {
107126 let paths = paths ( & [ ( CurveType :: Cubic , & [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 0 ] ) , ( CurveType :: Linear , & [ 0 , 12 , 6 ] ) ] ) ;
108127 let connectivity = Connectivity :: build ( 15 , & paths) ;
109128
110129 // The seam contributes two spokes and the chord a third
111- assert_eq ! ( connectivity. spokes( 0 , & paths ) , 3 ) ;
112- assert_eq ! ( connectivity. spokes( 6 , & paths ) , 3 ) ;
130+ assert_eq ! ( connectivity. spokes( 0 ) , 3 ) ;
131+ assert_eq ! ( connectivity. spokes( 6 ) , 3 ) ;
113132
114- assert_eq ! ( connectivity. spokes( 3 , & paths ) , 2 ) ;
115- assert_eq ! ( connectivity. spokes( 12 , & paths ) , 2 ) ;
133+ assert_eq ! ( connectivity. spokes( 3 ) , 2 ) ;
134+ assert_eq ! ( connectivity. spokes( 12 ) , 2 ) ;
116135
117136 // Handles carry no segments, and unreferenced points are free
118- assert_eq ! ( connectivity. spokes( 1 , & paths) , 0 ) ;
137+ assert_eq ! ( connectivity. spokes( 1 ) , 0 ) ;
138+ assert ! ( connectivity. is_handle( 1 ) ) ;
139+ assert ! ( !connectivity. is_handle( 0 ) ) ;
140+ assert ! ( !connectivity. is_handle( 13 ) ) ;
119141 assert ! ( !connectivity. is_referenced( 13 ) ) ;
120142 assert ! ( connectivity. is_referenced( 12 ) ) ;
121143 }
@@ -125,9 +147,9 @@ mod tests {
125147 let paths = paths ( & [ ( CurveType :: Linear , & [ 0 , 1 , 2 ] ) , ( CurveType :: Linear , & [ 2 , 3 ] ) ] ) ;
126148 let connectivity = Connectivity :: build ( 4 , & paths) ;
127149
128- assert_eq ! ( connectivity . references ( 0 ) , [ Reference { path : 0 , index : 0 } ] ) ;
129- assert_eq ! ( connectivity . references ( 2 ) , [ Reference { path : 0 , index : 2 } , Reference { path : 1 , index : 0 } ] ) ;
130- assert_eq ! ( connectivity. spokes( 2 , & paths ) , 2 ) ;
150+ assert_eq ! ( places ( & connectivity , 0 ) , [ ( 0 , 0 ) ] ) ;
151+ assert_eq ! ( places ( & connectivity , 2 ) , [ ( 0 , 2 ) , ( 1 , 0 ) ] ) ;
152+ assert_eq ! ( connectivity. spokes( 2 ) , 2 ) ;
131153 }
132154
133155 // References falling anywhere outside the pool are ignored, including one exactly at its end
@@ -136,10 +158,10 @@ mod tests {
136158 let paths = paths ( & [ ( CurveType :: Linear , & [ 0 , 3 , 1 , 4 ] ) ] ) ;
137159 let connectivity = Connectivity :: build ( 3 , & paths) ;
138160
139- assert_eq ! ( connectivity . references ( 0 ) , [ Reference { path : 0 , index : 0 } ] ) ;
140- assert_eq ! ( connectivity . references ( 1 ) , [ Reference { path : 0 , index : 2 } ] ) ;
141- assert_eq ! ( connectivity. references ( 3 ) , [ ] ) ;
142- assert_eq ! ( connectivity. references ( 4 ) , [ ] ) ;
161+ assert_eq ! ( places ( & connectivity , 0 ) , [ ( 0 , 0 ) ] ) ;
162+ assert_eq ! ( places ( & connectivity , 1 ) , [ ( 0 , 2 ) ] ) ;
163+ assert ! ( places ( & connectivity, 3 ) . is_empty ( ) ) ;
164+ assert ! ( places ( & connectivity, 4 ) . is_empty ( ) ) ;
143165 }
144166
145167 // An open end has one spoke, which is what makes it a cap site
@@ -148,8 +170,8 @@ mod tests {
148170 let paths = paths ( & [ ( CurveType :: Linear , & [ 0 , 1 , 2 ] ) ] ) ;
149171 let connectivity = Connectivity :: build ( 3 , & paths) ;
150172
151- assert_eq ! ( connectivity. spokes( 0 , & paths ) , 1 ) ;
152- assert_eq ! ( connectivity. spokes( 2 , & paths ) , 1 ) ;
153- assert_eq ! ( connectivity. spokes( 1 , & paths ) , 2 ) ;
173+ assert_eq ! ( connectivity. spokes( 0 ) , 1 ) ;
174+ assert_eq ! ( connectivity. spokes( 2 ) , 1 ) ;
175+ assert_eq ! ( connectivity. spokes( 1 ) , 2 ) ;
154176 }
155177}
0 commit comments