gpui_base/plot/shape/
area.rs1use gpui::{Background, Bounds, Path, PathBuilder, Pixels, Point, Window, px};
4
5use crate::plot::{Curve, PathCache, ShapeKey, origin_point};
6
7#[allow(clippy::type_complexity)]
8pub struct Area<T> {
9 data: Vec<T>,
10 x: Box<dyn Fn(&T) -> Option<f32>>,
11 y0: Option<f32>,
12 y1: Box<dyn Fn(&T) -> Option<f32>>,
13 fill: Background,
14 stroke: Background,
15 curve: Curve,
16}
17
18impl<T> Default for Area<T> {
19 fn default() -> Self {
20 Self {
21 data: Vec::new(),
22 x: Box::new(|_| None),
23 y0: None,
24 y1: Box::new(|_| None),
25 fill: Default::default(),
26 stroke: Default::default(),
27 curve: Curve::default(),
28 }
29 }
30}
31
32impl<T> Area<T> {
33 pub fn new() -> Self {
34 Self::default()
35 }
36
37 pub fn data<I>(mut self, data: I) -> Self
39 where
40 I: IntoIterator<Item = T>,
41 {
42 self.data = data.into_iter().collect();
43 self
44 }
45
46 pub fn x<F>(mut self, x: F) -> Self
48 where
49 F: Fn(&T) -> Option<f32> + 'static,
50 {
51 self.x = Box::new(x);
52 self
53 }
54
55 pub fn y0(mut self, y0: f32) -> Self {
57 self.y0 = Some(y0);
58 self
59 }
60
61 pub fn y1<F>(mut self, y1: F) -> Self
63 where
64 F: Fn(&T) -> Option<f32> + 'static,
65 {
66 self.y1 = Box::new(y1);
67 self
68 }
69
70 pub fn fill(mut self, fill: impl Into<Background>) -> Self {
72 self.fill = fill.into();
73 self
74 }
75
76 pub fn stroke(mut self, stroke: impl Into<Background>) -> Self {
78 self.stroke = stroke.into();
79 self
80 }
81
82 pub fn curve(mut self, curve: Curve) -> Self {
85 self.curve = curve;
86 self
87 }
88
89 fn path(&self, bounds: &Bounds<Pixels>) -> (Option<Path<Pixels>>, Option<Path<Pixels>>) {
90 let origin = bounds.origin;
91 let mut area_builder = PathBuilder::fill();
92 let mut line_builder = PathBuilder::stroke(px(1.));
93
94 let mut points = vec![];
95
96 let mut first_x_tick = None;
97 let mut last_x_tick = None;
98 for (index, v) in self.data.iter().enumerate() {
99 if index == 0 {
100 first_x_tick = (self.x)(v);
101 }
102 if index == self.data.len() - 1 {
103 last_x_tick = (self.x)(v);
104 }
105 let x_tick = (self.x)(v);
106 let y_tick = (self.y1)(v);
107
108 if let (Some(x), Some(y)) = (x_tick, y_tick) {
109 let pos = origin_point(px(x), px(y), origin);
110
111 points.push(pos);
112 }
113 }
114
115 if points.is_empty() {
116 return (None, None);
117 }
118
119 if points.len() == 1 {
120 area_builder.move_to(points[0]);
121 line_builder.move_to(points[0]);
122 return (area_builder.build().ok(), line_builder.build().ok());
123 }
124
125 match self.curve {
126 Curve::Natural => {
127 area_builder.move_to(points[0]);
128 line_builder.move_to(points[0]);
129 let n = points.len();
130 for i in 0..n - 1 {
131 let p0 = if i == 0 { points[0] } else { points[i - 1] };
132 let p1 = points[i];
133 let p2 = points[i + 1];
134 let p3 = if i + 2 < n {
135 points[i + 2]
136 } else {
137 points[n - 1]
138 };
139
140 let c1 = Point::new(p1.x + (p2.x - p0.x) / 6.0, p1.y + (p2.y - p0.y) / 6.0);
142 let c2 = Point::new(p2.x - (p3.x - p1.x) / 6.0, p2.y - (p3.y - p1.y) / 6.0);
143
144 area_builder.cubic_bezier_to(p2, c1, c2);
145 line_builder.cubic_bezier_to(p2, c1, c2);
146 }
147 }
148 Curve::Linear => {
149 area_builder.move_to(points[0]);
150 line_builder.move_to(points[0]);
151 for p in &points[1..] {
152 area_builder.line_to(*p);
153 line_builder.line_to(*p);
154 }
155 }
156 Curve::StepAfter => {
157 area_builder.move_to(points[0]);
158 line_builder.move_to(points[0]);
159 for (i, p) in points.windows(2).enumerate() {
160 area_builder.line_to(Point::new(p[1].x, p[0].y));
161 line_builder.line_to(Point::new(p[1].x, p[0].y));
162 if i < points.len() - 2 {
164 area_builder.line_to(p[1]);
165 line_builder.line_to(p[1]);
166 }
167 }
168 }
169 }
170
171 if let (Some(first), Some(last), Some(y)) = (first_x_tick, last_x_tick, self.y0) {
173 area_builder.line_to(origin_point(px(last), px(y), bounds.origin));
174 area_builder.line_to(origin_point(px(first), px(y), bounds.origin));
175 area_builder.close();
176 }
177
178 (area_builder.build().ok(), line_builder.build().ok())
179 }
180
181 pub fn paint_cached(
186 &self,
187 bounds: &Bounds<Pixels>,
188 fill: &mut PathCache,
189 line: &mut PathCache,
190 window: &mut Window,
191 ) {
192 let mut key = ShapeKey::new((self.curve, self.y0.map(f32::to_bits)));
193 for v in self.data.iter() {
194 if let (Some(x), Some(y)) = ((self.x)(v), (self.y1)(v)) {
195 key.f32(x).f32(y);
196 }
197 }
198 let key = key.finish();
199 let local = Bounds::new(Point::default(), bounds.size);
200 let mut stroke_path = None;
203 let fill_path = fill.get(key, bounds.origin, || {
204 let (area, stroke) = self.path(&local);
205 stroke_path = Some(stroke);
206 area
207 });
208 let line_path = line.get(key, bounds.origin, || {
209 stroke_path.take().unwrap_or_else(|| self.path(&local).1)
210 });
211 if let Some(area) = fill_path {
212 window.paint_path(area, self.fill);
213 }
214 if let Some(line) = line_path {
215 window.paint_path(line, self.stroke);
216 }
217 }
218
219 pub fn paint(&self, bounds: &Bounds<Pixels>, window: &mut Window) {
221 let (area, line) = self.path(bounds);
222
223 if let Some(area) = area {
224 window.paint_path(area, self.fill);
225 }
226 if let Some(line) = line {
227 window.paint_path(line, self.stroke);
228 }
229 }
230}