@@ -2,6 +2,16 @@ use super::{Drawable, PointCollection};
22use crate :: style:: { Color , ShapeStyle , SizeDesc } ;
33use plotters_backend:: { BackendCoord , DrawingBackend , DrawingErrorKind } ;
44
5+ #[ inline]
6+ fn to_i ( ( x, y) : ( f32 , f32 ) ) -> ( i32 , i32 ) {
7+ ( x. round ( ) as i32 , y. round ( ) as i32 )
8+ }
9+
10+ #[ inline]
11+ fn to_f ( ( x, y) : ( i32 , i32 ) ) -> ( f32 , f32 ) {
12+ ( x as f32 , y as f32 )
13+ }
14+
515/**
616An element representing a single pixel.
717
@@ -181,8 +191,6 @@ impl<I0: Iterator + Clone, Size: SizeDesc, DB: DrawingBackend> Drawable<DB>
181191 backend : & mut DB ,
182192 ps : ( u32 , u32 ) ,
183193 ) -> Result < ( ) , DrawingErrorKind < DB :: ErrorType > > {
184- let to_i = |( x, y) : ( f32 , f32 ) | ( x. round ( ) as i32 , y. round ( ) as i32 ) ;
185- let to_f = |( x, y) : ( i32 , i32 ) | ( x as f32 , y as f32 ) ;
186194 let mut start = match points. next ( ) {
187195 Some ( c) => to_f ( c) ,
188196 None => return Ok ( ( ) ) ,
@@ -264,6 +272,123 @@ fn test_dashed_path_element() {
264272 . expect ( "Drawing Failure" ) ;
265273}
266274
275+ /// An element of a series of connected lines in dot style for any markers.
276+ ///
277+ /// It's similar to [`PathElement`] but use a marker function to draw markers with spacing.
278+ pub struct DottedPathElement < I : Iterator + Clone , Size : SizeDesc , Marker > {
279+ points : I ,
280+ shift : Size ,
281+ spacing : Size ,
282+ func : Box < dyn Fn ( BackendCoord ) -> Marker > ,
283+ }
284+
285+ impl < I : Iterator + Clone , Size : SizeDesc , Marker > DottedPathElement < I , Size , Marker > {
286+ /// Create a new path
287+ /// - `points`: The iterator of the points
288+ /// - `shift`: The shift of the first marker
289+ /// - `spacing`: The spacing between markers
290+ /// - `func`: The marker function
291+ /// - returns the created element
292+ pub fn new < I0 , F > ( points : I0 , shift : Size , spacing : Size , func : F ) -> Self
293+ where
294+ I0 : IntoIterator < IntoIter = I > ,
295+ F : Fn ( BackendCoord ) -> Marker + ' static ,
296+ {
297+ Self {
298+ points : points. into_iter ( ) ,
299+ shift,
300+ spacing,
301+ func : Box :: new ( func) ,
302+ }
303+ }
304+ }
305+
306+ impl < ' a , I : Iterator + Clone , Size : SizeDesc , Marker > PointCollection < ' a , I :: Item >
307+ for & ' a DottedPathElement < I , Size , Marker >
308+ {
309+ type Point = I :: Item ;
310+ type IntoIter = I ;
311+ fn point_iter ( self ) -> Self :: IntoIter {
312+ self . points . clone ( )
313+ }
314+ }
315+
316+ impl < I0 , Size , DB , Marker > Drawable < DB > for DottedPathElement < I0 , Size , Marker >
317+ where
318+ I0 : Iterator + Clone ,
319+ Size : SizeDesc ,
320+ DB : DrawingBackend ,
321+ Marker : crate :: element:: IntoDynElement < ' static , DB , BackendCoord > ,
322+ {
323+ fn draw < I : Iterator < Item = BackendCoord > > (
324+ & self ,
325+ mut points : I ,
326+ backend : & mut DB ,
327+ ps : ( u32 , u32 ) ,
328+ ) -> Result < ( ) , DrawingErrorKind < DB :: ErrorType > > {
329+ let mut shift = self . shift . in_pixels ( & ps) . max ( 0 ) as f32 ;
330+ let mut start = match points. next ( ) {
331+ Some ( start_i) => {
332+ // Draw the first marker if no shift
333+ if shift == 0. {
334+ let mk = ( self . func ) ( start_i) . into_dyn ( ) ;
335+ mk. draw ( mk. point_iter ( ) . iter ( ) . copied ( ) , backend, ps) ?;
336+ }
337+ to_f ( start_i)
338+ }
339+ None => return Ok ( ( ) ) ,
340+ } ;
341+ let spacing = self . spacing . in_pixels ( & ps) . max ( 0 ) as f32 ;
342+ let mut dist = 0. ;
343+ for curr in points {
344+ let end = to_f ( curr) ;
345+ // Loop for spacing
346+ while start != end {
347+ let ( dx, dy) = ( end. 0 - start. 0 , end. 1 - start. 1 ) ;
348+ let d = dx. hypot ( dy) ;
349+ let spacing = if shift == 0. { spacing } else { shift } ;
350+ let left = spacing - dist;
351+ // Set next point to `start`
352+ if left < d {
353+ let t = left / d;
354+ start = ( start. 0 + dx * t, start. 1 + dy * t) ;
355+ dist += left;
356+ } else {
357+ start = end;
358+ dist += d;
359+ }
360+ // Draw if needed
361+ if spacing <= dist {
362+ let mk = ( self . func ) ( to_i ( start) ) . into_dyn ( ) ;
363+ mk. draw ( mk. point_iter ( ) . iter ( ) . copied ( ) , backend, ps) ?;
364+ shift = 0. ;
365+ dist = 0. ;
366+ }
367+ }
368+ }
369+ Ok ( ( ) )
370+ }
371+ }
372+
373+ #[ cfg( test) ]
374+ #[ test]
375+ fn test_dotted_path_element ( ) {
376+ use crate :: prelude:: * ;
377+ let da = crate :: create_mocked_drawing_area ( 300 , 300 , |m| {
378+ m. drop_check ( |b| {
379+ assert_eq ! ( b. num_draw_path_call, 0 ) ;
380+ assert_eq ! ( b. draw_count, 7 ) ;
381+ } ) ;
382+ } ) ;
383+ da. draw ( & DottedPathElement :: new (
384+ vec ! [ ( 100 , 100 ) , ( 105 , 105 ) , ( 150 , 150 ) ] ,
385+ 5 ,
386+ 10 ,
387+ |c| Circle :: new ( c, 5 , Into :: < ShapeStyle > :: into ( RED ) . filled ( ) ) ,
388+ ) )
389+ . expect ( "Drawing Failure" ) ;
390+ }
391+
267392/// A rectangle element
268393pub struct Rectangle < Coord > {
269394 points : [ Coord ; 2 ] ,
0 commit comments