1 | use crate::{BackendCoord, BackendStyle, DrawingBackend, DrawingErrorKind}; |
---|---|

2 | |

3 | pub fn draw_line<DB: DrawingBackend, S: BackendStyle>( |

4 | back: &mut DB, |

5 | mut from: BackendCoord, |

6 | mut to: BackendCoord, |

7 | style: &S, |

8 | ) -> Result<(), DrawingErrorKind<DB::ErrorType>> { |

9 | if style.color().alpha == 0.0 || style.stroke_width() == 0 { |

10 | return Ok(()); |

11 | } |

12 | |

13 | if style.stroke_width() != 1 { |

14 | // If the line is wider than 1px, then we need to make it a polygon |

15 | let v = (i64::from(to.0 - from.0), i64::from(to.1 - from.1)); |

16 | let l = ((v.0 * v.0 + v.1 * v.1) as f64).sqrt(); |

17 | |

18 | if l < 1e-5 { |

19 | return Ok(()); |

20 | } |

21 | |

22 | let v = (v.0 as f64 / l, v.1 as f64 / l); |

23 | |

24 | let r = f64::from(style.stroke_width()) / 2.0; |

25 | let mut trans = [(v.1 * r, -v.0 * r), (-v.1 * r, v.0 * r)]; |

26 | let mut vertices = vec![]; |

27 | |

28 | for point in [from, to].iter() { |

29 | for t in trans.iter() { |

30 | vertices.push(( |

31 | (f64::from(point.0) + t.0) as i32, |

32 | (f64::from(point.1) + t.1) as i32, |

33 | )) |

34 | } |

35 | |

36 | trans.swap(0, 1); |

37 | } |

38 | |

39 | return back.fill_polygon(vertices, style); |

40 | } |

41 | |

42 | if from.0 == to.0 { |

43 | if from.1 > to.1 { |

44 | std::mem::swap(&mut from, &mut to); |

45 | } |

46 | for y in from.1..=to.1 { |

47 | check_result!(back.draw_pixel((from.0, y), style.color())); |

48 | } |

49 | return Ok(()); |

50 | } |

51 | |

52 | if from.1 == to.1 { |

53 | if from.0 > to.0 { |

54 | std::mem::swap(&mut from, &mut to); |

55 | } |

56 | for x in from.0..=to.0 { |

57 | check_result!(back.draw_pixel((x, from.1), style.color())); |

58 | } |

59 | return Ok(()); |

60 | } |

61 | |

62 | let steep = (from.0 - to.0).abs() < (from.1 - to.1).abs(); |

63 | |

64 | if steep { |

65 | from = (from.1, from.0); |

66 | to = (to.1, to.0); |

67 | } |

68 | |

69 | let (from, to) = if from.0 > to.0 { |

70 | (to, from) |

71 | } else { |

72 | (from, to) |

73 | }; |

74 | |

75 | let mut size_limit = back.get_size(); |

76 | |

77 | if steep { |

78 | size_limit = (size_limit.1, size_limit.0); |

79 | } |

80 | |

81 | let grad = f64::from(to.1 - from.1) / f64::from(to.0 - from.0); |

82 | |

83 | let mut put_pixel = |(x, y): BackendCoord, b: f64| { |

84 | if steep { |

85 | back.draw_pixel((y, x), style.color().mix(b)) |

86 | } else { |

87 | back.draw_pixel((x, y), style.color().mix(b)) |

88 | } |

89 | }; |

90 | |

91 | let y_step_limit = |

92 | (f64::from(to.1.min(size_limit.1 as i32 - 1).max(0) - from.1) / grad).floor() as i32; |

93 | |

94 | let batch_start = (f64::from(from.1.min(size_limit.1 as i32 - 2).max(0) - from.1) / grad) |

95 | .abs() |

96 | .ceil() as i32 |

97 | + from.0; |

98 | |

99 | let batch_limit = |

100 | to.0.min(size_limit.0 as i32 - 2) |

101 | .min(from.0 + y_step_limit - 1); |

102 | |

103 | let mut y = f64::from(from.1) + f64::from(batch_start - from.0) * grad; |

104 | |

105 | for x in batch_start..=batch_limit { |

106 | check_result!(put_pixel((x, y as i32), 1.0 + y.floor() - y)); |

107 | check_result!(put_pixel((x, y as i32 + 1), y - y.floor())); |

108 | |

109 | y += grad; |

110 | } |

111 | |

112 | if to.0 > batch_limit && y < f64::from(to.1) { |

113 | let x = batch_limit as i32 + 1; |

114 | if 1.0 + y.floor() - y > 1e-5 { |

115 | check_result!(put_pixel((x, y as i32), 1.0 + y.floor() - y)); |

116 | } |

117 | if y - y.floor() > 1e-5 && y + 1.0 < f64::from(to.1) { |

118 | check_result!(put_pixel((x, y as i32 + 1), y - y.floor())); |

119 | } |

120 | } |

121 | |

122 | Ok(()) |

123 | } |

124 |