1use super::{CoordTranslate, ReverseCoordTranslate};
2use crate::drawing::backend::{BackendCoord, DrawingBackend, DrawingErrorKind};
3use crate::style::ShapeStyle;
4
5use std::ops::Range;
6
7pub trait Ranged {
10 type ValueType;
12
13 fn map(&self, value: &Self::ValueType, limit: (i32, i32)) -> i32;
15
16 fn key_points(&self, max_points: usize) -> Vec<Self::ValueType>;
18
19 fn range(&self) -> Range<Self::ValueType>;
21
22 #[allow(clippy::range_plus_one)]
24 fn axis_pixel_range(&self, limit: (i32, i32)) -> Range<i32> {
25 if limit.0 < limit.1 {
26 limit.0..limit.1
27 } else {
28 (limit.1 + 1)..(limit.0 + 1)
29 }
30 }
31}
32
33pub trait ReversibleRanged: Ranged {
37 fn unmap(&self, input: i32, limit: (i32, i32)) -> Option<Self::ValueType>;
38}
39
40pub struct RangedCoord<X: Ranged, Y: Ranged> {
42 logic_x: X,
43 logic_y: Y,
44 back_x: (i32, i32),
45 back_y: (i32, i32),
46}
47
48impl<X: Ranged + Clone, Y: Ranged + Clone> Clone for RangedCoord<X, Y> {
49 fn clone(&self) -> Self {
50 Self {
51 logic_x: self.logic_x.clone(),
52 logic_y: self.logic_y.clone(),
53 back_x: self.back_x,
54 back_y: self.back_y,
55 }
56 }
57}
58
59impl<X: Ranged, Y: Ranged> RangedCoord<X, Y> {
60 pub fn new<IntoX: Into<X>, IntoY: Into<Y>>(
62 logic_x: IntoX,
63 logic_y: IntoY,
64 actual: (Range<i32>, Range<i32>),
65 ) -> Self {
66 Self {
67 logic_x: logic_x.into(),
68 logic_y: logic_y.into(),
69 back_x: (actual.0.start, actual.0.end),
70 back_y: (actual.1.start, actual.1.end),
71 }
72 }
73
74 pub fn draw_mesh<E, DrawMesh: FnMut(MeshLine<X, Y>) -> Result<(), E>>(
76 &self,
77 h_limit: usize,
78 v_limit: usize,
79 mut draw_mesh: DrawMesh,
80 ) -> Result<(), E> {
81 let (xkp, ykp) = (
82 self.logic_x.key_points(v_limit),
83 self.logic_y.key_points(h_limit),
84 );
85
86 for logic_x in xkp {
87 let x = self.logic_x.map(&logic_x, self.back_x);
88 draw_mesh(MeshLine::XMesh(
89 (x, self.back_y.0),
90 (x, self.back_y.1),
91 &logic_x,
92 ))?;
93 }
94
95 for logic_y in ykp {
96 let y = self.logic_y.map(&logic_y, self.back_y);
97 draw_mesh(MeshLine::YMesh(
98 (self.back_x.0, y),
99 (self.back_x.1, y),
100 &logic_y,
101 ))?;
102 }
103
104 Ok(())
105 }
106
107 pub fn get_x_range(&self) -> Range<X::ValueType> {
109 self.logic_x.range()
110 }
111
112 pub fn get_y_range(&self) -> Range<Y::ValueType> {
114 self.logic_y.range()
115 }
116
117 pub fn get_x_axis_pixel_range(&self) -> Range<i32> {
118 self.logic_x.axis_pixel_range(self.back_x)
119 }
120
121 pub fn get_y_axis_pixel_range(&self) -> Range<i32> {
122 self.logic_y.axis_pixel_range(self.back_y)
123 }
124
125 pub fn x_spec(&self) -> &X {
126 &self.logic_x
127 }
128
129 pub fn y_spec(&self) -> &Y {
130 &self.logic_y
131 }
132}
133
134impl<X: Ranged, Y: Ranged> CoordTranslate for RangedCoord<X, Y> {
135 type From = (X::ValueType, Y::ValueType);
136
137 fn translate(&self, from: &Self::From) -> BackendCoord {
138 (
139 self.logic_x.map(&from.0, self.back_x),
140 self.logic_y.map(&from.1, self.back_y),
141 )
142 }
143}
144
145impl<X: ReversibleRanged, Y: ReversibleRanged> ReverseCoordTranslate for RangedCoord<X, Y> {
146 fn reverse_translate(&self, input: BackendCoord) -> Option<Self::From> {
147 Some((
148 self.logic_x.unmap(input.0, self.back_x)?,
149 self.logic_y.unmap(input.1, self.back_y)?,
150 ))
151 }
152}
153
154pub enum MeshLine<'a, X: Ranged, Y: Ranged> {
156 XMesh(BackendCoord, BackendCoord, &'a X::ValueType),
157 YMesh(BackendCoord, BackendCoord, &'a Y::ValueType),
158}
159
160impl<'a, X: Ranged, Y: Ranged> MeshLine<'a, X, Y> {
161 pub fn draw<DB: DrawingBackend>(
163 &self,
164 backend: &mut DB,
165 style: &ShapeStyle,
166 ) -> Result<(), DrawingErrorKind<DB::ErrorType>> {
167 let (&left, &right) = match self {
168 MeshLine::XMesh(a, b, _) => (a, b),
169 MeshLine::YMesh(a, b, _) => (a, b),
170 };
171 backend.draw_line(left, right, &style.color)
172 }
173}
174
175pub trait DiscreteRanged
177where
178 Self: Ranged,
179{
180 type RangeParameter;
181
182 fn get_range_parameter(&self) -> Self::RangeParameter;
183
184 fn next_value(this: &Self::ValueType, param: &Self::RangeParameter) -> Self::ValueType;
186
187 fn previous_value(this: &Self::ValueType, param: &Self::RangeParameter) -> Self::ValueType;
189}
190
191pub trait AsRangedCoord: Sized {
193 type CoordDescType: Ranged<ValueType = Self::Value> + From<Self>;
194 type Value;
195}
196
197impl<T> AsRangedCoord for T
198where
199 T: Ranged,
200 Range<T::ValueType>: Into<T>,
201{
202 type CoordDescType = T;
203 type Value = T::ValueType;
204}
205
206pub struct CentricDiscreteRange<D: DiscreteRanged>(D)
211where
212 <D as Ranged>::ValueType: Eq;
213
214pub trait IntoCentric: AsRangedCoord
216where
217 Self::CoordDescType: DiscreteRanged,
218 <Self::CoordDescType as Ranged>::ValueType: Eq,
219{
220 fn into_centric(self) -> CentricDiscreteRange<Self::CoordDescType> {
222 CentricDiscreteRange(self.into())
223 }
224}
225
226impl<T: AsRangedCoord> IntoCentric for T
227where
228 T::CoordDescType: DiscreteRanged,
229 <Self::CoordDescType as Ranged>::ValueType: Eq,
230{
231}
232
233impl<D: DiscreteRanged + Clone> Clone for CentricDiscreteRange<D>
234where
235 <D as Ranged>::ValueType: Eq,
236{
237 fn clone(&self) -> Self {
238 Self(self.0.clone())
239 }
240}
241
242impl<D: DiscreteRanged> Ranged for CentricDiscreteRange<D>
243where
244 <D as Ranged>::ValueType: Eq,
245{
246 type ValueType = <D as Ranged>::ValueType;
247
248 fn map(&self, value: &Self::ValueType, limit: (i32, i32)) -> i32 {
249 let prev = <D as DiscreteRanged>::previous_value(&value, &self.0.get_range_parameter());
250 (self.0.map(&prev, limit) + self.0.map(value, limit)) / 2
251 }
252
253 fn key_points(&self, max_points: usize) -> Vec<Self::ValueType> {
254 self.0.key_points(max_points)
255 }
256
257 fn range(&self) -> Range<Self::ValueType> {
258 self.0.range()
259 }
260}
261
262impl<D: DiscreteRanged> DiscreteRanged for CentricDiscreteRange<D>
263where
264 <D as Ranged>::ValueType: Eq,
265{
266 type RangeParameter = <D as DiscreteRanged>::RangeParameter;
267 fn get_range_parameter(&self) -> Self::RangeParameter {
268 self.0.get_range_parameter()
269 }
270 fn next_value(this: &Self::ValueType, param: &Self::RangeParameter) -> Self::ValueType {
271 <D as DiscreteRanged>::next_value(this, param)
272 }
273
274 fn previous_value(this: &Self::ValueType, param: &Self::RangeParameter) -> Self::ValueType {
275 <D as DiscreteRanged>::previous_value(this, param)
276 }
277}
278
279impl<D: DiscreteRanged> AsRangedCoord for CentricDiscreteRange<D>
280where
281 <D as Ranged>::ValueType: Eq,
282{
283 type CoordDescType = Self;
284 type Value = <Self as Ranged>::ValueType;
285}
286
287pub struct PartialAxis<R: Ranged>(R, Range<R::ValueType>);
291
292pub trait IntoPartialAxis: AsRangedCoord {
294 fn partial_axis(
299 self,
300 axis_range: Range<<Self::CoordDescType as Ranged>::ValueType>,
301 ) -> PartialAxis<Self::CoordDescType> {
302 PartialAxis(self.into(), axis_range)
303 }
304}
305
306impl<R: AsRangedCoord> IntoPartialAxis for R {}
307
308impl<R: Ranged + Clone> Clone for PartialAxis<R>
309where
310 <R as Ranged>::ValueType: Clone,
311{
312 fn clone(&self) -> Self {
313 PartialAxis(self.0.clone(), self.1.clone())
314 }
315}
316
317impl<R: Ranged> Ranged for PartialAxis<R>
318where
319 R::ValueType: Clone,
320{
321 type ValueType = R::ValueType;
322
323 fn map(&self, value: &Self::ValueType, limit: (i32, i32)) -> i32 {
324 self.0.map(value, limit)
325 }
326
327 fn key_points(&self, max_points: usize) -> Vec<Self::ValueType> {
328 self.0.key_points(max_points)
329 }
330
331 fn range(&self) -> Range<Self::ValueType> {
332 self.0.range()
333 }
334
335 fn axis_pixel_range(&self, limit: (i32, i32)) -> Range<i32> {
336 let left = self.map(&self.1.start, limit);
337 let right = self.map(&self.1.end, limit);
338
339 left.min(right)..left.max(right)
340 }
341}
342
343impl<R: DiscreteRanged> DiscreteRanged for PartialAxis<R>
344where
345 R: Ranged,
346 <R as Ranged>::ValueType: Eq + Clone,
347{
348 type RangeParameter = <R as DiscreteRanged>::RangeParameter;
349 fn get_range_parameter(&self) -> Self::RangeParameter {
350 self.0.get_range_parameter()
351 }
352 fn next_value(this: &Self::ValueType, param: &Self::RangeParameter) -> Self::ValueType {
353 <R as DiscreteRanged>::next_value(this, param)
354 }
355
356 fn previous_value(this: &Self::ValueType, param: &Self::RangeParameter) -> Self::ValueType {
357 <R as DiscreteRanged>::previous_value(this, param)
358 }
359}
360
361impl<R: Ranged> AsRangedCoord for PartialAxis<R>
362where
363 <R as Ranged>::ValueType: Clone,
364{
365 type CoordDescType = Self;
366 type Value = <Self as Ranged>::ValueType;
367}
368
369pub fn make_partial_axis<T>(
377 axis_range: Range<T>,
378 part: Range<f64>,
379) -> Option<PartialAxis<<Range<T> as AsRangedCoord>::CoordDescType>>
380where
381 Range<T>: AsRangedCoord,
382 T: num_traits::NumCast + Clone,
383{
384 let left: f64 = num_traits::cast(axis_range.start.clone())?;
385 let right: f64 = num_traits::cast(axis_range.end.clone())?;
386
387 let full_range_size = (right - left) / (part.end - part.start);
388
389 let full_left = left - full_range_size * part.start;
390 let full_right = right + full_range_size * (1.0 - part.end);
391
392 let full_range: Range<T> = num_traits::cast(full_left)?..num_traits::cast(full_right)?;
393
394 let axis_range: <Range<T> as AsRangedCoord>::CoordDescType = axis_range.into();
395
396 Some(PartialAxis(full_range.into(), axis_range.range()))
397}