@@ -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,123 @@ 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
+ 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
+
267
392
/// A rectangle element
268
393
pub struct Rectangle < Coord > {
269
394
points : [ Coord ; 2 ] ,
0 commit comments