@@ -112,7 +112,8 @@ where
112112 CourseError : From < <P as TryFrom < GeoPoint > >:: Error > ,
113113{
114114 options : CourseOptions ,
115- courses : Vec < CourseBuilderImpl < P > > ,
115+ course_builders : Vec < CourseBuilder > ,
116+ segmented_courses : Vec < SegmentedCourseBuilder < P > > ,
116117 waypoints : Vec < Waypoint < P > > ,
117118}
118119
@@ -126,18 +127,19 @@ where
126127 pub fn new ( options : CourseOptions ) -> Self {
127128 Self {
128129 options,
129- courses : Vec :: new ( ) ,
130+ course_builders : Vec :: new ( ) ,
131+ segmented_courses : Vec :: new ( ) ,
130132 waypoints : Vec :: new ( ) ,
131133 }
132134 }
133135
134- pub fn add_course ( & mut self ) -> & mut CourseBuilderImpl < P > {
135- self . courses . push ( CourseBuilderImpl :: new ( ) ) ;
136+ pub fn add_course ( & mut self ) -> & mut CourseBuilder {
137+ self . course_builders . push ( CourseBuilder :: new ( ) ) ;
136138 self . last_course_mut ( ) . unwrap ( )
137139 }
138140
139- pub fn last_course_mut ( & mut self ) -> Result < & mut CourseBuilderImpl < P > > {
140- match self . courses . last_mut ( ) {
141+ pub fn last_course_mut ( & mut self ) -> Result < & mut CourseBuilder > {
142+ match self . course_builders . last_mut ( ) {
141143 Some ( course) => Ok ( course) ,
142144 None => Err ( CourseError :: MissingCourse ) ,
143145 }
@@ -149,17 +151,21 @@ where
149151 }
150152
151153 pub fn num_courses ( & self ) -> usize {
152- self . courses . len ( )
154+ self . course_builders . len ( )
153155 }
154156
157+ /// Build the courses.
158+ ///
159+ /// The geodesic calculations happen in here.
155160 pub fn build ( mut self ) -> Result < CourseSet > {
156- let mut courses = vec ! [ ] ;
157- for course_builder in & mut self . courses {
158- course_builder. calculate_segments ( ) ?;
159- }
161+ let mut courses = Vec :: new ( ) ;
162+ self . segmented_courses = std:: mem:: take ( & mut self . course_builders )
163+ . into_iter ( )
164+ . map ( |c| c. segment ( ) )
165+ . collect :: < Result < Vec < _ > > > ( ) ?;
160166 self . process_waypoints ( ) ?;
161- for course_builder in self . courses {
162- courses. push ( course_builder . build ( ) ?) ;
167+ for segmented_course in self . segmented_courses {
168+ courses. push ( segmented_course . build ( ) ?) ;
163169 }
164170 Ok ( CourseSet { courses } )
165171 }
@@ -183,13 +189,13 @@ where
183189 } ) )
184190 }
185191
186- fn process_waypoint (
192+ fn process_single_waypoint (
187193 waypoint : & Waypoint < P > ,
188- course : & CourseBuilderImpl < P > ,
194+ course : & SegmentedCourseBuilder < P > ,
189195 threshold : Meter < f64 > ,
190196 ) -> Result < Vec < NearIntercept > > {
191197 let mut slns = Vec :: new ( ) ;
192- for ( segment, start_distance) in course. segments . as_ref ( ) . unwrap ( ) {
198+ for ( segment, start_distance) in & course. segments_and_distances {
193199 slns. push ( <Self as SolveIntercept < P > >:: solve_intercept (
194200 segment,
195201 waypoint,
@@ -221,11 +227,14 @@ where
221227
222228 #[ tracing:: instrument( level = "debug" , name = "process_waypoints" , skip_all) ]
223229 fn process_waypoints ( & mut self ) -> Result < ( ) > {
224- for course in & mut self . courses {
230+ for segmented_course in & mut self . segmented_courses {
225231 let waypoints_and_intercepts = iter_work ! ( & self . waypoints)
226232 . map ( |waypoint| {
227- let near_intercepts =
228- Self :: process_waypoint ( waypoint, course, self . options . threshold ) ?;
233+ let near_intercepts = Self :: process_single_waypoint (
234+ waypoint,
235+ segmented_course,
236+ self . options . threshold ,
237+ ) ?;
229238 Ok ( ( waypoint, near_intercepts) )
230239 } )
231240 . collect :: < Result < Vec < _ > > > ( ) ?;
@@ -241,23 +250,27 @@ where
241250 . unwrap ( )
242251 } ) ;
243252 Self :: add_course_point (
244- & mut course . course_points ,
253+ & mut segmented_course . course_points ,
245254 & near_sorted[ 0 ] ,
246255 waypoint,
247256 ) ;
248257 }
249258
250259 InterceptStrategy :: First => {
251260 Self :: add_course_point (
252- & mut course . course_points ,
261+ & mut segmented_course . course_points ,
253262 & near_intercepts[ 0 ] ,
254263 waypoint,
255264 ) ;
256265 }
257266
258267 InterceptStrategy :: All => {
259268 for sln in near_intercepts {
260- Self :: add_course_point ( & mut course. course_points , sln, waypoint) ;
269+ Self :: add_course_point (
270+ & mut segmented_course. course_points ,
271+ sln,
272+ waypoint,
273+ ) ;
261274 }
262275 }
263276 }
@@ -409,53 +422,59 @@ impl Course {
409422 }
410423}
411424
412- pub struct CourseBuilderImpl < P >
413- where
414- P : HasGeoPoint + Send + Sync ,
415- < P as TryFrom < GeoPoint > > :: Error : Send ,
416- CourseError : From < < P as TryFrom < GeoPoint > > :: Error > ,
417- {
425+ /// Builder for [`Course`].
426+ ///
427+ /// Used as part of [`CourseSetBuilderImpl`] to allow composing a course.
428+ /// Within that context, obtain a new instance with
429+ /// [`CourseSetBuilderImpl::add_course`].
430+ pub struct CourseBuilder {
418431 route_points : Vec < GeoPoint > ,
419- segments : Option < Vec < ( GeoSegment < P > , Meter < f64 > ) > > ,
420432 name : Option < String > ,
421- course_points : Vec < CoursePoint > ,
422433 num_repeated_points_skipped : usize ,
423434}
424435
425436#[ allow( clippy:: new_without_default) ]
426- impl < P > CourseBuilderImpl < P >
427- where
428- P : HasGeoPoint + Send + Sync ,
429- <P as TryFrom < GeoPoint > >:: Error : Send ,
430- CourseError : From < <P as TryFrom < GeoPoint > >:: Error > ,
431- {
437+ impl CourseBuilder {
432438 fn new ( ) -> Self {
433439 Self {
434440 route_points : Vec :: new ( ) ,
435- segments : None ,
436441 name : None ,
437- course_points : Vec :: new ( ) ,
438442 num_repeated_points_skipped : 0 ,
439443 }
440444 }
441445
446+ /// Set the course's name.
442447 pub fn with_name ( & mut self , name : String ) -> & mut Self {
443448 self . name = Some ( name) ;
444449 self
445450 }
446451
447- pub fn with_route_point ( & mut self , point : GeoPoint ) -> Result < & mut Self > {
452+ /// Add a route point to the course.
453+ ///
454+ /// Adds GPX route points (or equivalently, track points) to the course in
455+ /// order of traversal.
456+ pub fn with_route_point ( & mut self , point : GeoPoint ) -> & mut Self {
448457 if let Some ( prev) = self . route_points . last ( ) {
449458 if * prev == point {
450459 self . num_repeated_points_skipped += 1 ;
451- return Ok ( self ) ;
460+ return self ;
452461 }
453462 }
454463 self . route_points . push ( point) ;
455- Ok ( self )
464+ self
456465 }
457466
458- fn calculate_segments ( & mut self ) -> Result < ( ) > {
467+ /// Segment the course.
468+ ///
469+ /// Does the initial geodesic calculations of solving the indirect problem
470+ /// between adjacent points, and lifting points into instances the type
471+ /// parameter `P` (such as [`XyzPoint`]).
472+ fn segment < P > ( self ) -> Result < SegmentedCourseBuilder < P > >
473+ where
474+ P : HasGeoPoint + Send + Sync ,
475+ <P as TryFrom < GeoPoint > >:: Error : Send ,
476+ CourseError : From < <P as TryFrom < GeoPoint > >:: Error > ,
477+ {
459478 let ps = iter_work ! ( self . route_points)
460479 . map ( |p| P :: try_from ( * p) )
461480 . collect :: < std:: result:: Result < Vec < _ > , _ > > ( ) ?;
@@ -476,29 +495,58 @@ where
476495 } )
477496 . collect :: < Vec < _ > > ( ) ;
478497
479- self . segments = Some ( segments_and_distances) ;
480- Ok ( ( ) )
498+ Ok ( SegmentedCourseBuilder {
499+ route_points : self . route_points ,
500+ segments_and_distances,
501+ name : self . name ,
502+ course_points : Vec :: new ( ) ,
503+ num_repeated_points_skipped : self . num_repeated_points_skipped ,
504+ } )
481505 }
506+ }
482507
508+ /// A segmented [`Course`] builder.
509+ ///
510+ /// This represents an intermediate stage of building a [`CourseBuilder`]: The
511+ /// initial work of processing route points into geodesic segments along with
512+ /// distance information, as well as possibly [`XyPoint`] values, has been done.
513+ ///
514+ /// This builder is used internally within [`CourseSetBuilderImpl`] to gather
515+ /// course points.
516+ struct SegmentedCourseBuilder < P >
517+ where
518+ P : HasGeoPoint + Send + Sync ,
519+ <P as TryFrom < GeoPoint > >:: Error : Send ,
520+ CourseError : From < <P as TryFrom < GeoPoint > >:: Error > ,
521+ {
522+ route_points : Vec < GeoPoint > ,
523+ segments_and_distances : Vec < ( GeoSegment < P > , Meter < f64 > ) > ,
524+ name : Option < String > ,
525+ course_points : Vec < CoursePoint > ,
526+ num_repeated_points_skipped : usize ,
527+ }
528+
529+ impl < P > SegmentedCourseBuilder < P >
530+ where
531+ P : HasGeoPoint + Send + Sync ,
532+ <P as TryFrom < GeoPoint > >:: Error : Send ,
533+ CourseError : From < <P as TryFrom < GeoPoint > >:: Error > ,
534+ {
483535 fn build ( mut self ) -> Result < Course > {
484- if self . segments . is_none ( ) {
485- self . calculate_segments ( ) ?;
486- }
487536 match & self . name {
488537 Some ( name) => info ! ( "Building course {}" , name) ,
489538 None => info ! ( "Building untitled course" ) ,
490539 }
491540 let mut records = Vec :: new ( ) ;
492- let segments = self . segments . as_ref ( ) . unwrap ( ) ;
493- let num_segments = segments. len ( ) ;
494- let total_distance = match segments. last ( ) {
541+ let num_segments = self . segments_and_distances . len ( ) ;
542+ let total_distance = match self . segments_and_distances . last ( ) {
495543 Some ( ( s, start_distance) ) => * start_distance + s. geo_length ,
496544 None => 0.0 * M ,
497545 } ;
498- for ( segment, start_distance) in segments {
546+ for ( segment, start_distance) in self . segments_and_distances {
499547 records. push ( Record {
500548 point : * segment. start . geo ( ) ,
501- cumulative_distance : * start_distance,
549+ cumulative_distance : start_distance,
502550 } )
503551 }
504552 if let Some ( rp) = self . route_points . last ( ) {
@@ -586,30 +634,25 @@ mod tests {
586634 use dimensioned:: si:: { M , Meter } ;
587635
588636 use crate :: course:: {
589- CourseBuilderImpl , CourseSetBuilder , InterceptSolution , NearIntercept , Waypoint ,
637+ CourseBuilder , CourseSetBuilder , InterceptSolution , NearIntercept , Waypoint ,
590638 } ;
591639 use crate :: fit:: CoursePointType ;
592640 use crate :: types:: { GeoAndXyzPoint , GeoPoint } ;
593641 use crate :: { CourseOptions , geo_point, geo_points} ;
594642
595- #[ cfg( feature = "floor" ) ]
596- type CourseBuilder = CourseBuilderImpl < GeoAndXyzPoint > ;
597-
598- #[ cfg( not( feature = "floor" ) ) ]
599- type CourseBuilder = CourseBuilderImpl < GeoPoint > ;
600-
601643 #[ test]
602644 fn test_course_builder_empty ( ) -> Result < ( ) > {
603- let course = CourseBuilder :: new ( ) . build ( ) ?;
645+ let course = CourseBuilder :: new ( ) . segment :: < GeoAndXyzPoint > ( ) ? . build ( ) ?;
604646 assert_eq ! ( course. records, vec![ ] ) ;
605647 Ok ( ( ) )
606648 }
607649
608650 #[ test]
609651 fn test_course_builder_single_point ( ) -> Result < ( ) > {
610652 let mut builder = CourseBuilder :: new ( ) ;
611- builder. with_route_point ( geo_point ! ( 1.0 , 2.0 ) ) ? ;
653+ builder. with_route_point ( geo_point ! ( 1.0 , 2.0 ) ) ;
612654 let record_points = builder
655+ . segment :: < GeoAndXyzPoint > ( ) ?
613656 . build ( ) ?
614657 . records
615658 . iter ( )
@@ -626,9 +669,10 @@ mod tests {
626669 fn test_course_builder_two_points ( ) -> Result < ( ) > {
627670 let mut builder = CourseBuilder :: new ( ) ;
628671 builder
629- . with_route_point ( geo_point ! ( 1.0 , 2.0 ) ) ?
630- . with_route_point ( geo_point ! ( 1.1 , 2.2 ) ) ? ;
672+ . with_route_point ( geo_point ! ( 1.0 , 2.0 ) )
673+ . with_route_point ( geo_point ! ( 1.1 , 2.2 ) ) ;
631674 let record_points = builder
675+ . segment :: < GeoAndXyzPoint > ( ) ?
632676 . build ( ) ?
633677 . records
634678 . iter ( )
@@ -645,15 +689,15 @@ mod tests {
645689 fn test_repeated_points ( ) -> Result < ( ) > {
646690 let mut builder = CourseBuilder :: new ( ) ;
647691 builder
648- . with_route_point ( geo_point ! ( 1.0 , 2.0 ) ) ?
649- . with_route_point ( geo_point ! ( 1.0 , 2.0 ) ) ?
650- . with_route_point ( geo_point ! ( 1.1 , 2.2 ) ) ?
651- . with_route_point ( geo_point ! ( 1.1 , 2.2 ) ) ?
652- . with_route_point ( geo_point ! ( 1.2 , 2.1 ) ) ?
653- . with_route_point ( geo_point ! ( 1.1 , 2.2 ) ) ?
654- . with_route_point ( geo_point ! ( 1.1 , 2.2 ) ) ? ;
655-
656- let course = builder. build ( ) ?;
692+ . with_route_point ( geo_point ! ( 1.0 , 2.0 ) )
693+ . with_route_point ( geo_point ! ( 1.0 , 2.0 ) )
694+ . with_route_point ( geo_point ! ( 1.1 , 2.2 ) )
695+ . with_route_point ( geo_point ! ( 1.1 , 2.2 ) )
696+ . with_route_point ( geo_point ! ( 1.2 , 2.1 ) )
697+ . with_route_point ( geo_point ! ( 1.1 , 2.2 ) )
698+ . with_route_point ( geo_point ! ( 1.1 , 2.2 ) ) ;
699+
700+ let course = builder. segment :: < GeoAndXyzPoint > ( ) ? . build ( ) ?;
657701 let record_points = course. records . iter ( ) . map ( |r| r. point ) . collect :: < Vec < _ > > ( ) ;
658702
659703 let expected_points = geo_points ! [ ( 1.0 , 2.0 ) , ( 1.1 , 2.2 ) , ( 1.2 , 2.1 ) , ( 1.1 , 2.2 ) ] ;
@@ -667,9 +711,9 @@ mod tests {
667711 let mut builder = CourseSetBuilder :: new ( CourseOptions :: default ( ) ) ;
668712 builder
669713 . add_course ( )
670- . with_route_point ( geo_point ! ( 35.5252717091331 , -101.2856451853322 ) ) ?
671- . with_route_point ( geo_point ! ( 36.05200980326534 , -90.02610043506964 ) ) ?
672- . with_route_point ( geo_point ! ( 38.13369722302025 , -78.51238236506529 ) ) ? ;
714+ . with_route_point ( geo_point ! ( 35.5252717091331 , -101.2856451853322 ) )
715+ . with_route_point ( geo_point ! ( 36.05200980326534 , -90.02610043506964 ) )
716+ . with_route_point ( geo_point ! ( 38.13369722302025 , -78.51238236506529 ) ) ;
673717
674718 builder. add_waypoint ( Waypoint {
675719 name : "MyWaypoint" . to_owned ( ) ,
0 commit comments