1use ecolor::Color32;
2use emath::{Align, Align2, Vec2};
3use epaint::{CubicBezierShape, FontId, Shape, Stroke};
4use num::BigInt;
5use serde::{Deserialize, Serialize};
6
7use crate::{
8 config::SurferTheme, displayed_item::DisplayedItemRef, view::DrawingContext,
9 viewport::Viewport, wave_data::WaveData,
10};
11
12#[derive(Serialize, Deserialize, Debug)]
13pub enum Direction {
14 North,
15 East,
16 South,
17 West,
18}
19
20impl Direction {
21 #[must_use]
22 pub fn as_vector(&self) -> Vec2 {
23 match self {
24 Direction::North => Vec2::new(0., -1.),
25 Direction::East => Vec2::new(-1., 0.),
26 Direction::South => Vec2::new(0., 1.),
27 Direction::West => Vec2::new(1., 0.),
28 }
29 }
30}
31
32#[derive(Serialize, Deserialize, Debug)]
33pub enum Anchor {
34 Top,
35 Center,
36 Bottom,
37}
38
39#[derive(Serialize, Deserialize, Debug)]
40pub struct GraphicsY {
41 pub item: DisplayedItemRef,
42 pub anchor: Anchor,
43}
44
45#[derive(Serialize, Deserialize, Debug)]
47pub struct GrPoint {
48 pub x: BigInt,
50 pub y: GraphicsY,
51}
52
53#[derive(Serialize, Deserialize, PartialEq, PartialOrd, Eq, Ord, Hash, Debug)]
54pub struct GraphicId(pub usize);
55
56#[derive(Serialize, Deserialize, Debug)]
57pub enum Graphic {
58 TextArrow {
59 from: (GrPoint, Direction),
60 to: (GrPoint, Direction),
61 text: String,
62 },
63 Text {
64 pos: (GrPoint, Direction),
65 text: String,
66 },
67}
68
69impl WaveData {
70 fn get_item_y(&self, y: &GraphicsY) -> Option<f32> {
73 self.items_tree
74 .iter_visible()
75 .zip(&self.drawing_infos)
76 .find(|(node, _info)| node.item_ref == y.item)
77 .map(|(_, info)| match y.anchor {
78 Anchor::Top => info.top(),
79 Anchor::Center => info.top() + (info.bottom() - info.top()) / 2.,
80 Anchor::Bottom => info.bottom(),
81 })
82 .map(|point| point - self.top_item_draw_offset)
83 }
84
85 pub(crate) fn draw_graphics(
86 &self,
87 ctx: &mut DrawingContext,
88 viewport: &Viewport,
89 theme: &SurferTheme,
90 ) {
91 let color = theme.variable_dontcare;
92 let num_timestamps = self.safe_num_timestamps();
93 for g in self.graphics.values() {
94 match g {
95 Graphic::TextArrow {
96 from: (from_point, from_dir),
97 to: (to_point, to_dir),
98 text,
99 } => {
100 let from_x = viewport.pixel_from_time(
101 &from_point.x,
102 ctx.cfg.canvas_width,
103 &num_timestamps,
104 );
105 let from_y = self.get_item_y(&from_point.y);
106
107 let to_x = viewport.pixel_from_time(
108 &to_point.x,
109 ctx.cfg.canvas_width,
110 &num_timestamps,
111 );
112 let to_y = self.get_item_y(&to_point.y);
113
114 if let (Some(from_y), Some(to_y)) = (from_y, to_y) {
115 let from_dir = from_dir.as_vector() * 30.;
116 let to_dir_vec = to_dir.as_vector() * 30.;
117 let shape = Shape::CubicBezier(CubicBezierShape {
118 points: [
119 (ctx.to_screen)(from_x, from_y),
120 (ctx.to_screen)(from_x + from_dir.x, from_y + from_dir.y),
121 (ctx.to_screen)(to_x + to_dir_vec.x, to_y + to_dir_vec.y),
122 (ctx.to_screen)(to_x, to_y),
123 ],
124 closed: false,
125 fill: Color32::TRANSPARENT,
126 stroke: Stroke { width: 3., color }.into(),
127 });
128 ctx.painter.add(shape);
129
130 ctx.painter.text(
131 (ctx.to_screen)(to_x, to_y),
132 match to_dir {
133 Direction::North => Align2([Align::Center, Align::TOP]),
134 Direction::East => Align2([Align::LEFT, Align::Center]),
135 Direction::South => Align2([Align::Center, Align::BOTTOM]),
136 Direction::West => Align2([Align::RIGHT, Align::Center]),
137 },
138 text,
139 FontId::monospace(15.),
140 color,
141 );
142 }
143 }
144 Graphic::Text {
145 pos: (pos, dir),
146 text,
147 } => {
148 let to_x =
149 viewport.pixel_from_time(&pos.x, ctx.cfg.canvas_width, &num_timestamps);
150 let to_y = self.get_item_y(&pos.y);
151 if let Some(to_y) = to_y {
152 ctx.painter.text(
153 (ctx.to_screen)(to_x, to_y),
154 match dir {
155 Direction::North => Align2([Align::Center, Align::TOP]),
156 Direction::East => Align2([Align::LEFT, Align::Center]),
157 Direction::South => Align2([Align::Center, Align::BOTTOM]),
158 Direction::West => Align2([Align::RIGHT, Align::Center]),
159 },
160 text,
161 FontId::monospace(15.),
162 color,
163 );
164 }
165 }
166 }
167 }
168 }
169}