@@ -2,6 +2,16 @@ use super::{Drawable, PointCollection};
2
2
use crate :: style:: { Color , ShapeStyle , SizeDesc } ;
3
3
use plotters_backend:: { BackendCoord , DrawingBackend , DrawingErrorKind } ;
4
4
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
+
5
15
/**
6
16
An element representing a single pixel.
7
17
@@ -181,8 +191,6 @@ impl<I0: Iterator + Clone, Size: SizeDesc, DB: DrawingBackend> Drawable<DB>
181
191
backend : & mut DB ,
182
192
ps : ( u32 , u32 ) ,
183
193
) -> 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 ) ;
186
194
let mut start = match points. next ( ) {
187
195
Some ( c) => to_f ( c) ,
188
196
None => return Ok ( ( ) ) ,
@@ -264,6 +272,120 @@ fn test_dashed_path_element() {
264
272
. expect ( "Drawing Failure" ) ;
265
273
}
266
274
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
+ spacing : Size ,
281
+ func : Box < dyn Fn ( BackendCoord ) -> Marker > ,
282
+ }
283
+
284
+ impl < I : Iterator + Clone , Size : SizeDesc , Marker > DottedPathElement < I , Size , Marker > {
285
+ /// Create a new path
286
+ /// - `points`: The iterator of the points
287
+ /// - `spacing`: The spacing between markers
288
+ /// - `func`: The marker function
289
+ /// - returns the created element
290
+ pub fn new < I0 , F > ( points : I0 , spacing : Size , func : F ) -> Self
291
+ where
292
+ I0 : IntoIterator < IntoIter = I > ,
293
+ F : Fn ( BackendCoord ) -> Marker + ' static ,
294
+ {
295
+ Self {
296
+ points : points. into_iter ( ) ,
297
+ spacing,
298
+ func : Box :: new ( func) ,
299
+ }
300
+ }
301
+ }
302
+
303
+ impl < ' a , I : Iterator + Clone , Size : SizeDesc , Marker > PointCollection < ' a , I :: Item >
304
+ for & ' a DottedPathElement < I , Size , Marker >
305
+ {
306
+ type Point = I :: Item ;
307
+ type IntoIter = I ;
308
+ fn point_iter ( self ) -> Self :: IntoIter {
309
+ self . points . clone ( )
310
+ }
311
+ }
312
+
313
+ impl < I0 , Size , DB , Marker > Drawable < DB > for DottedPathElement < I0 , Size , Marker >
314
+ where
315
+ I0 : Iterator + Clone ,
316
+ Size : SizeDesc ,
317
+ DB : DrawingBackend ,
318
+ Marker : crate :: element:: IntoDynElement < ' static , DB , BackendCoord > ,
319
+ {
320
+ fn draw < I : Iterator < Item = BackendCoord > > (
321
+ & self ,
322
+ mut points : I ,
323
+ backend : & mut DB ,
324
+ ps : ( u32 , u32 ) ,
325
+ ) -> Result < ( ) , DrawingErrorKind < DB :: ErrorType > > {
326
+ let mut start = match points. next ( ) {
327
+ Some ( start_i) => {
328
+ ( self . func ) ( start_i)
329
+ . into_dyn ( )
330
+ . draw ( std:: iter:: once ( start_i) , backend, ps) ?;
331
+ to_f ( start_i)
332
+ }
333
+ None => return Ok ( ( ) ) ,
334
+ } ;
335
+ let spacing = self . spacing . in_pixels ( & ps) . max ( 0 ) as f32 ;
336
+ if spacing == 0. {
337
+ return Ok ( ( ) ) ;
338
+ }
339
+ let mut dist = 0. ;
340
+ for curr in points {
341
+ let end = to_f ( curr) ;
342
+ // Loop for spacing
343
+ while start != end {
344
+ let ( dx, dy) = ( end. 0 - start. 0 , end. 1 - start. 1 ) ;
345
+ let d = dx. hypot ( dy) ;
346
+ let size = spacing;
347
+ let left = size - dist;
348
+ // Set next point to `start`
349
+ if left < d {
350
+ let t = left / d;
351
+ start = ( start. 0 + dx * t, start. 1 + dy * t) ;
352
+ dist += left;
353
+ } else {
354
+ start = end;
355
+ dist += d;
356
+ }
357
+ // Draw if needed
358
+ if size <= dist {
359
+ let start_i = to_i ( start) ;
360
+ ( self . func ) ( start_i)
361
+ . into_dyn ( )
362
+ . draw ( std:: iter:: once ( start_i) , backend, ps) ?;
363
+ dist = 0. ;
364
+ }
365
+ }
366
+ }
367
+ Ok ( ( ) )
368
+ }
369
+ }
370
+
371
+ #[ cfg( test) ]
372
+ #[ test]
373
+ fn test_dotted_path_element ( ) {
374
+ use crate :: prelude:: * ;
375
+ let da = crate :: create_mocked_drawing_area ( 300 , 300 , |m| {
376
+ m. drop_check ( |b| {
377
+ assert_eq ! ( b. num_draw_path_call, 0 ) ;
378
+ assert_eq ! ( b. draw_count, 8 ) ;
379
+ } ) ;
380
+ } ) ;
381
+ da. draw ( & DottedPathElement :: new (
382
+ vec ! [ ( 100 , 100 ) , ( 105 , 105 ) , ( 150 , 150 ) ] ,
383
+ 10 ,
384
+ |c| Circle :: new ( c, 5 , Into :: < ShapeStyle > :: into ( RED ) . filled ( ) ) ,
385
+ ) )
386
+ . expect ( "Drawing Failure" ) ;
387
+ }
388
+
267
389
/// A rectangle element
268
390
pub struct Rectangle < Coord > {
269
391
points : [ Coord ; 2 ] ,
0 commit comments