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

2 | |

3 | fn draw_part_a< |

4 | B: DrawingBackend, |

5 | Draw: FnMut(i32, (f64, f64)) -> Result<(), DrawingErrorKind<B::ErrorType>>, |

6 | >( |

7 | height: f64, |

8 | radius: u32, |

9 | mut draw: Draw, |

10 | ) -> Result<(), DrawingErrorKind<B::ErrorType>> { |

11 | let half_width = (radius as f64 * radius as f64 |

12 | - (radius as f64 - height) * (radius as f64 - height)) |

13 | .sqrt(); |

14 | |

15 | let x0 = (-half_width).ceil() as i32; |

16 | let x1 = half_width.floor() as i32; |

17 | |

18 | let y0 = (radius as f64 - height).ceil(); |

19 | |

20 | for x in x0..=x1 { |

21 | let y1 = (radius as f64 * radius as f64 - x as f64 * x as f64).sqrt(); |

22 | check_result!(draw(x, (y0, y1))); |

23 | } |

24 | |

25 | Ok(()) |

26 | } |

27 | |

28 | fn draw_part_b< |

29 | B: DrawingBackend, |

30 | Draw: FnMut(i32, (f64, f64)) -> Result<(), DrawingErrorKind<B::ErrorType>>, |

31 | >( |

32 | from: f64, |

33 | size: f64, |

34 | mut draw: Draw, |

35 | ) -> Result<(), DrawingErrorKind<B::ErrorType>> { |

36 | let from = from.floor(); |

37 | for x in (from - size).floor() as i32..=from as i32 { |

38 | check_result!(draw(x, (-x as f64, x as f64))); |

39 | } |

40 | Ok(()) |

41 | } |

42 | |

43 | fn draw_part_c< |

44 | B: DrawingBackend, |

45 | Draw: FnMut(i32, (f64, f64)) -> Result<(), DrawingErrorKind<B::ErrorType>>, |

46 | >( |

47 | r: i32, |

48 | r_limit: i32, |

49 | mut draw: Draw, |

50 | ) -> Result<(), DrawingErrorKind<B::ErrorType>> { |

51 | let half_size = r as f64 / (2f64).sqrt(); |

52 | |

53 | let (x0, x1) = ((-half_size).ceil() as i32, half_size.floor() as i32); |

54 | |

55 | for x in x0..x1 { |

56 | let outter_y0 = ((r_limit as f64) * (r_limit as f64) - x as f64 * x as f64).sqrt(); |

57 | let inner_y0 = r as f64 - 1.0; |

58 | let mut y1 = outter_y0.min(inner_y0); |

59 | let y0 = ((r as f64) * (r as f64) - x as f64 * x as f64).sqrt(); |

60 | |

61 | if y0 > y1 { |

62 | y1 = y0.ceil(); |

63 | if y1 >= r as f64 { |

64 | continue; |

65 | } |

66 | } |

67 | |

68 | check_result!(draw(x, (y0, y1))); |

69 | } |

70 | |

71 | for x in x1 + 1..r { |

72 | let outter_y0 = ((r_limit as f64) * (r_limit as f64) - x as f64 * x as f64).sqrt(); |

73 | let inner_y0 = r as f64 - 1.0; |

74 | let y0 = outter_y0.min(inner_y0); |

75 | let y1 = x as f64; |

76 | |

77 | if y1 < y0 { |

78 | check_result!(draw(x, (y0, y1 + 1.0))); |

79 | check_result!(draw(-x, (y0, y1 + 1.0))); |

80 | } |

81 | } |

82 | |

83 | Ok(()) |

84 | } |

85 | |

86 | fn draw_sweep_line<B: DrawingBackend, S: BackendStyle>( |

87 | b: &mut B, |

88 | style: &S, |

89 | (x0, y0): BackendCoord, |

90 | (dx, dy): (i32, i32), |

91 | p0: i32, |

92 | (s, e): (f64, f64), |

93 | ) -> Result<(), DrawingErrorKind<B::ErrorType>> { |

94 | let mut s = if dx < 0 || dy < 0 { -s } else { s }; |

95 | let mut e = if dx < 0 || dy < 0 { -e } else { e }; |

96 | if s > e { |

97 | std::mem::swap(&mut s, &mut e); |

98 | } |

99 | |

100 | let vs = s.ceil() - s; |

101 | let ve = e - e.floor(); |

102 | |

103 | if dx == 0 { |

104 | check_result!(b.draw_line( |

105 | (p0 + x0, s.ceil() as i32 + y0), |

106 | (p0 + x0, e.floor() as i32 + y0), |

107 | &style.color() |

108 | )); |

109 | check_result!(b.draw_pixel((p0 + x0, s.ceil() as i32 + y0 - 1), style.color().mix(vs))); |

110 | check_result!(b.draw_pixel((p0 + x0, e.floor() as i32 + y0 + 1), style.color().mix(ve))); |

111 | } else { |

112 | check_result!(b.draw_line( |

113 | (s.ceil() as i32 + x0, p0 + y0), |

114 | (e.floor() as i32 + x0, p0 + y0), |

115 | &style.color() |

116 | )); |

117 | check_result!(b.draw_pixel((s.ceil() as i32 + x0 - 1, p0 + y0), style.color().mix(vs))); |

118 | check_result!(b.draw_pixel((e.floor() as i32 + x0 + 1, p0 + y0), style.color().mix(ve))); |

119 | } |

120 | |

121 | Ok(()) |

122 | } |

123 | |

124 | fn draw_annulus<B: DrawingBackend, S: BackendStyle>( |

125 | b: &mut B, |

126 | center: BackendCoord, |

127 | radius: (u32, u32), |

128 | style: &S, |

129 | ) -> Result<(), DrawingErrorKind<B::ErrorType>> { |

130 | let a0 = ((radius.0 - radius.1) as f64).min(radius.0 as f64 * (1.0 - 1.0 / (2f64).sqrt())); |

131 | let a1 = (radius.0 as f64 - a0 - radius.1 as f64).max(0.0); |

132 | |

133 | check_result!(draw_part_a::<B, _>(a0, radius.0, |p, r| draw_sweep_line( |

134 | b, |

135 | style, |

136 | center, |

137 | (0, 1), |

138 | p, |

139 | r |

140 | ))); |

141 | check_result!(draw_part_a::<B, _>(a0, radius.0, |p, r| draw_sweep_line( |

142 | b, |

143 | style, |

144 | center, |

145 | (0, -1), |

146 | p, |

147 | r |

148 | ))); |

149 | check_result!(draw_part_a::<B, _>(a0, radius.0, |p, r| draw_sweep_line( |

150 | b, |

151 | style, |

152 | center, |

153 | (1, 0), |

154 | p, |

155 | r |

156 | ))); |

157 | check_result!(draw_part_a::<B, _>(a0, radius.0, |p, r| draw_sweep_line( |

158 | b, |

159 | style, |

160 | center, |

161 | (-1, 0), |

162 | p, |

163 | r |

164 | ))); |

165 | |

166 | if a1 > 0.0 { |

167 | check_result!(draw_part_b::<B, _>( |

168 | radius.0 as f64 - a0, |

169 | a1.floor(), |

170 | |h, (f, t)| { |

171 | let h = h as i32; |

172 | let f = f as i32; |

173 | let t = t as i32; |

174 | check_result!(b.draw_line( |

175 | (center.0 + h, center.1 + f), |

176 | (center.0 + h, center.1 + t), |

177 | &style.color() |

178 | )); |

179 | check_result!(b.draw_line( |

180 | (center.0 - h, center.1 + f), |

181 | (center.0 - h, center.1 + t), |

182 | &style.color() |

183 | )); |

184 | |

185 | check_result!(b.draw_line( |

186 | (center.0 + f + 1, center.1 + h), |

187 | (center.0 + t - 1, center.1 + h), |

188 | &style.color() |

189 | )); |

190 | check_result!(b.draw_line( |

191 | (center.0 + f + 1, center.1 - h), |

192 | (center.0 + t - 1, center.1 - h), |

193 | &style.color() |

194 | )); |

195 | |

196 | Ok(()) |

197 | } |

198 | )); |

199 | } |

200 | |

201 | check_result!(draw_part_c::<B, _>( |

202 | radius.1 as i32, |

203 | radius.0 as i32, |

204 | |p, r| draw_sweep_line(b, style, center, (0, 1), p, r) |

205 | )); |

206 | check_result!(draw_part_c::<B, _>( |

207 | radius.1 as i32, |

208 | radius.0 as i32, |

209 | |p, r| draw_sweep_line(b, style, center, (0, -1), p, r) |

210 | )); |

211 | check_result!(draw_part_c::<B, _>( |

212 | radius.1 as i32, |

213 | radius.0 as i32, |

214 | |p, r| draw_sweep_line(b, style, center, (1, 0), p, r) |

215 | )); |

216 | check_result!(draw_part_c::<B, _>( |

217 | radius.1 as i32, |

218 | radius.0 as i32, |

219 | |p, r| draw_sweep_line(b, style, center, (-1, 0), p, r) |

220 | )); |

221 | |

222 | let d_inner = ((radius.1 as f64) / (2f64).sqrt()) as i32; |

223 | let d_outter = (((radius.0 as f64) / (2f64).sqrt()) as i32).min(radius.1 as i32 - 1); |

224 | let d_outter_actually = (radius.1 as i32).min( |

225 | (radius.0 as f64 * radius.0 as f64 - radius.1 as f64 * radius.1 as f64 / 2.0) |

226 | .sqrt() |

227 | .ceil() as i32, |

228 | ); |

229 | |

230 | check_result!(b.draw_line( |

231 | (center.0 - d_inner, center.1 - d_inner), |

232 | (center.0 - d_outter, center.1 - d_outter), |

233 | &style.color() |

234 | )); |

235 | check_result!(b.draw_line( |

236 | (center.0 + d_inner, center.1 - d_inner), |

237 | (center.0 + d_outter, center.1 - d_outter), |

238 | &style.color() |

239 | )); |

240 | check_result!(b.draw_line( |

241 | (center.0 - d_inner, center.1 + d_inner), |

242 | (center.0 - d_outter, center.1 + d_outter), |

243 | &style.color() |

244 | )); |

245 | check_result!(b.draw_line( |

246 | (center.0 + d_inner, center.1 + d_inner), |

247 | (center.0 + d_outter, center.1 + d_outter), |

248 | &style.color() |

249 | )); |

250 | |

251 | check_result!(b.draw_line( |

252 | (center.0 - d_inner, center.1 + d_inner), |

253 | (center.0 - d_outter_actually, center.1 + d_inner), |

254 | &style.color() |

255 | )); |

256 | check_result!(b.draw_line( |

257 | (center.0 + d_inner, center.1 - d_inner), |

258 | (center.0 + d_inner, center.1 - d_outter_actually), |

259 | &style.color() |

260 | )); |

261 | check_result!(b.draw_line( |

262 | (center.0 + d_inner, center.1 + d_inner), |

263 | (center.0 + d_inner, center.1 + d_outter_actually), |

264 | &style.color() |

265 | )); |

266 | check_result!(b.draw_line( |

267 | (center.0 + d_inner, center.1 + d_inner), |

268 | (center.0 + d_outter_actually, center.1 + d_inner), |

269 | &style.color() |

270 | )); |

271 | |

272 | Ok(()) |

273 | } |

274 | |

275 | pub fn draw_circle<B: DrawingBackend, S: BackendStyle>( |

276 | b: &mut B, |

277 | center: BackendCoord, |

278 | mut radius: u32, |

279 | style: &S, |

280 | mut fill: bool, |

281 | ) -> Result<(), DrawingErrorKind<B::ErrorType>> { |

282 | if style.color().alpha == 0.0 { |

283 | return Ok(()); |

284 | } |

285 | |

286 | if !fill && style.stroke_width() != 1 { |

287 | let inner_radius = radius - (style.stroke_width() / 2).min(radius); |

288 | radius += style.stroke_width() / 2; |

289 | if inner_radius > 0 { |

290 | return draw_annulus(b, center, (radius, inner_radius), style); |

291 | } else { |

292 | fill = true; |

293 | } |

294 | } |

295 | |

296 | let min = (f64::from(radius) * (1.0 - (2f64).sqrt() / 2.0)).ceil() as i32; |

297 | let max = (f64::from(radius) * (1.0 + (2f64).sqrt() / 2.0)).floor() as i32; |

298 | |

299 | let range = min..=max; |

300 | |

301 | let (up, down) = ( |

302 | range.start() + center.1 - radius as i32, |

303 | range.end() + center.1 - radius as i32, |

304 | ); |

305 | |

306 | for dy in range { |

307 | let dy = dy - radius as i32; |

308 | let y = center.1 + dy; |

309 | |

310 | let lx = (f64::from(radius) * f64::from(radius) |

311 | - (f64::from(dy) * f64::from(dy)).max(1e-5)) |

312 | .sqrt(); |

313 | |

314 | let left = center.0 - lx.floor() as i32; |

315 | let right = center.0 + lx.floor() as i32; |

316 | |

317 | let v = lx - lx.floor(); |

318 | |

319 | let x = center.0 + dy; |

320 | let top = center.1 - lx.floor() as i32; |

321 | let bottom = center.1 + lx.floor() as i32; |

322 | |

323 | if fill { |

324 | check_result!(b.draw_line((left, y), (right, y), &style.color())); |

325 | check_result!(b.draw_line((x, top), (x, up - 1), &style.color())); |

326 | check_result!(b.draw_line((x, down + 1), (x, bottom), &style.color())); |

327 | } else { |

328 | check_result!(b.draw_pixel((left, y), style.color().mix(1.0 - v))); |

329 | check_result!(b.draw_pixel((right, y), style.color().mix(1.0 - v))); |

330 | |

331 | check_result!(b.draw_pixel((x, top), style.color().mix(1.0 - v))); |

332 | check_result!(b.draw_pixel((x, bottom), style.color().mix(1.0 - v))); |

333 | } |

334 | |

335 | check_result!(b.draw_pixel((left - 1, y), style.color().mix(v))); |

336 | check_result!(b.draw_pixel((right + 1, y), style.color().mix(v))); |

337 | check_result!(b.draw_pixel((x, top - 1), style.color().mix(v))); |

338 | check_result!(b.draw_pixel((x, bottom + 1), style.color().mix(v))); |

339 | } |

340 | |

341 | Ok(()) |

342 | } |

343 |