1use crate::geom::{Point, ScreenPoint, ScreenRect};
7use crate::transform::Transform;
8use crate::view::Viewport;
9use gpui::Rgba;
10
11#[derive(Debug, Clone, Copy, PartialEq)]
15pub struct LineStyle {
16 pub color: Rgba,
18 pub width: f32,
20}
21
22impl Default for LineStyle {
23 fn default() -> Self {
24 Self {
25 color: Rgba {
26 r: 0.0,
27 g: 0.0,
28 b: 0.0,
29 a: 1.0,
30 },
31 width: 1.0,
32 }
33 }
34}
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq)]
38pub enum MarkerShape {
39 Circle,
41 Square,
43 Cross,
45}
46
47#[derive(Debug, Clone, Copy, PartialEq)]
51pub struct MarkerStyle {
52 pub color: Rgba,
54 pub size: f32,
56 pub shape: MarkerShape,
58}
59
60impl Default for MarkerStyle {
61 fn default() -> Self {
62 Self {
63 color: Rgba {
64 r: 0.0,
65 g: 0.0,
66 b: 0.0,
67 a: 1.0,
68 },
69 size: 4.0,
70 shape: MarkerShape::Circle,
71 }
72 }
73}
74
75#[derive(Debug, Clone, Copy, PartialEq)]
77pub(crate) struct RectStyle {
78 pub fill: Rgba,
80 pub stroke: Rgba,
82 pub stroke_width: f32,
84}
85
86impl Default for RectStyle {
87 fn default() -> Self {
88 Self {
89 fill: Rgba {
90 r: 0.0,
91 g: 0.0,
92 b: 0.0,
93 a: 0.0,
94 },
95 stroke: Rgba {
96 r: 0.0,
97 g: 0.0,
98 b: 0.0,
99 a: 1.0,
100 },
101 stroke_width: 1.0,
102 }
103 }
104}
105
106#[derive(Debug, Clone, PartialEq)]
108pub(crate) struct TextStyle {
109 pub color: Rgba,
111 pub size: f32,
113}
114
115impl Default for TextStyle {
116 fn default() -> Self {
117 Self {
118 color: Rgba {
119 r: 0.0,
120 g: 0.0,
121 b: 0.0,
122 a: 1.0,
123 },
124 size: 12.0,
125 }
126 }
127}
128
129#[derive(Debug, Clone, Copy, PartialEq)]
131pub(crate) struct LineSegment {
132 pub start: ScreenPoint,
134 pub end: ScreenPoint,
136}
137
138impl LineSegment {
139 pub(crate) fn new(start: ScreenPoint, end: ScreenPoint) -> Self {
141 Self { start, end }
142 }
143}
144
145#[derive(Debug, Clone)]
147pub(crate) enum RenderCommand {
148 ClipRect(ScreenRect),
150 ClipEnd,
152 LineSegments {
154 segments: Vec<LineSegment>,
156 style: LineStyle,
158 },
159 Points {
161 points: Vec<ScreenPoint>,
163 style: MarkerStyle,
165 },
166 Rect {
168 rect: ScreenRect,
170 style: RectStyle,
172 },
173 Text {
175 position: ScreenPoint,
177 text: String,
179 style: TextStyle,
181 },
182}
183
184#[derive(Debug, Default, Clone)]
186pub(crate) struct RenderList {
187 commands: Vec<RenderCommand>,
188}
189
190impl RenderList {
191 pub(crate) fn new() -> Self {
193 Self::default()
194 }
195
196 pub(crate) fn push(&mut self, command: RenderCommand) {
198 self.commands.push(command);
199 }
200
201 pub(crate) fn commands(&self) -> &[RenderCommand] {
203 &self.commands
204 }
205}
206
207#[derive(Debug, Clone, PartialEq)]
209pub(crate) struct RenderCacheKey {
210 pub viewport: Viewport,
212 pub size: (u32, u32),
214 pub generation: u64,
216}
217
218pub(crate) fn build_line_segments(
220 points: &[Point],
221 transform: &Transform,
222 clip: ScreenRect,
223 out: &mut Vec<LineSegment>,
224) {
225 out.clear();
226 if points.len() < 2 {
227 return;
228 }
229 for window in points.windows(2) {
230 let Some(start) = transform.data_to_screen(window[0]) else {
231 continue;
232 };
233 let Some(end) = transform.data_to_screen(window[1]) else {
234 continue;
235 };
236 if let Some((clipped_start, clipped_end)) = clip_segment(start, end, clip) {
237 out.push(LineSegment::new(clipped_start, clipped_end));
238 }
239 }
240}
241
242pub(crate) fn build_scatter_points(
244 points: &[Point],
245 transform: &Transform,
246 clip: ScreenRect,
247 out: &mut Vec<ScreenPoint>,
248) {
249 out.clear();
250 for point in points {
251 let Some(screen) = transform.data_to_screen(*point) else {
252 continue;
253 };
254 if screen.x >= clip.min.x
255 && screen.x <= clip.max.x
256 && screen.y >= clip.min.y
257 && screen.y <= clip.max.y
258 {
259 out.push(screen);
260 }
261 }
262}
263
264fn clip_segment(
265 mut start: ScreenPoint,
266 mut end: ScreenPoint,
267 rect: ScreenRect,
268) -> Option<(ScreenPoint, ScreenPoint)> {
269 const LEFT: u8 = 1;
270 const RIGHT: u8 = 2;
271 const TOP: u8 = 4;
272 const BOTTOM: u8 = 8;
273
274 let mut out_start = region_code(start, rect, LEFT, RIGHT, TOP, BOTTOM);
275 let mut out_end = region_code(end, rect, LEFT, RIGHT, TOP, BOTTOM);
276
277 loop {
278 if (out_start | out_end) == 0 {
279 return Some((start, end));
280 }
281 if (out_start & out_end) != 0 {
282 return None;
283 }
284
285 let out_code = if out_start != 0 { out_start } else { out_end };
286 let (mut x, mut y) = (0.0_f32, 0.0_f32);
287
288 if (out_code & TOP) != 0 {
289 x = start.x + (end.x - start.x) * (rect.min.y - start.y) / (end.y - start.y);
290 y = rect.min.y;
291 } else if (out_code & BOTTOM) != 0 {
292 x = start.x + (end.x - start.x) * (rect.max.y - start.y) / (end.y - start.y);
293 y = rect.max.y;
294 } else if (out_code & RIGHT) != 0 {
295 y = start.y + (end.y - start.y) * (rect.max.x - start.x) / (end.x - start.x);
296 x = rect.max.x;
297 } else if (out_code & LEFT) != 0 {
298 y = start.y + (end.y - start.y) * (rect.min.x - start.x) / (end.x - start.x);
299 x = rect.min.x;
300 }
301
302 let new_point = ScreenPoint::new(x, y);
303 if out_code == out_start {
304 start = new_point;
305 out_start = region_code(start, rect, LEFT, RIGHT, TOP, BOTTOM);
306 } else {
307 end = new_point;
308 out_end = region_code(end, rect, LEFT, RIGHT, TOP, BOTTOM);
309 }
310 }
311}
312
313fn region_code(
314 point: ScreenPoint,
315 rect: ScreenRect,
316 left: u8,
317 right: u8,
318 top: u8,
319 bottom: u8,
320) -> u8 {
321 let mut code = 0;
322 if point.x < rect.min.x {
323 code |= left;
324 } else if point.x > rect.max.x {
325 code |= right;
326 }
327 if point.y < rect.min.y {
328 code |= top;
329 } else if point.y > rect.max.y {
330 code |= bottom;
331 }
332 code
333}
334
335#[cfg(test)]
336mod tests {
337 use super::*;
338 use crate::geom::Point;
339 use crate::view::Range;
340 use crate::view::Viewport;
341
342 #[test]
343 fn clip_segment_inside() {
344 let rect = ScreenRect::new(ScreenPoint::new(0.0, 0.0), ScreenPoint::new(10.0, 10.0));
345 let start = ScreenPoint::new(2.0, 2.0);
346 let end = ScreenPoint::new(8.0, 8.0);
347 let clipped = clip_segment(start, end, rect).expect("segment should clip");
348 assert_eq!(clipped.0, start);
349 assert_eq!(clipped.1, end);
350 }
351
352 #[test]
353 fn build_segments_with_transform() {
354 let viewport = Viewport::new(Range::new(0.0, 1.0), Range::new(0.0, 1.0));
355 let rect = ScreenRect::new(ScreenPoint::new(0.0, 0.0), ScreenPoint::new(10.0, 10.0));
356 let transform = Transform::new(viewport, rect).expect("valid transform");
357 let points = [Point::new(0.0, 0.0), Point::new(1.0, 1.0)];
358 let mut out = Vec::new();
359 build_line_segments(&points, &transform, rect, &mut out);
360 assert_eq!(out.len(), 1);
361 }
362}