1use ecolor::Color32;
2use egui::{FontId, PointerButton, Response, Sense, Ui};
3use emath::{Align2, Pos2, Rect, RectTransform, Vec2};
4use epaint::{CornerRadiusF32, CubicBezierShape, PathShape, PathStroke, RectShape, Shape, Stroke};
5use eyre::WrapErr;
6use ftr_parser::types::{Transaction, TxGenerator};
7use itertools::Itertools;
8use num::bigint::{ToBigInt, ToBigUint};
9use num::{BigInt, BigUint, ToPrimitive, Zero};
10use rayon::prelude::{IntoParallelRefIterator, ParallelBridge, ParallelIterator};
11use std::cmp::Ordering;
12use std::collections::HashMap;
13use std::f32::consts::PI;
14use surfer_translation_types::{
15 SubFieldFlatTranslationResult, TranslatedValue, ValueKind, VariableInfo, VariableValue,
16};
17use tracing::{error, warn};
18
19use crate::CachedDrawData::TransactionDrawData;
20use crate::analog_renderer::{AnalogDrawingCommand, variable_analog_draw_commands};
21use crate::clock_highlighting::draw_clock_edge_marks;
22use crate::config::SurferTheme;
23use crate::data_container::DataContainer;
24use crate::displayed_item::{
25 AnalogSettings, DisplayedFieldRef, DisplayedItemRef, DisplayedVariable,
26};
27use crate::tooltips::handle_transaction_tooltip;
28use crate::transaction_container::{TransactionRef, TransactionStreamRef};
29use crate::translation::{TranslationResultExt, TranslatorList, ValueKindExt, VariableInfoExt};
30use crate::view::{DrawConfig, DrawingContext, ItemDrawingInfo};
31use crate::wave_container::{QueryResult, VariableRefExt};
32use crate::wave_data::WaveData;
33use crate::{
34 CachedDrawData, CachedTransactionDrawData, CachedWaveDrawData, Message, SystemState,
35 displayed_item::DisplayedItem,
36};
37
38#[derive(Clone, Copy)]
40enum DinotraceDrawingStyle {
41 Normal,
42 AllZeros,
43 AllOnes,
44}
45
46impl DinotraceDrawingStyle {
47 fn from_value(val: &VariableValue, num_bits: Option<u32>) -> Self {
48 match val {
49 VariableValue::BigUint(u) if u.is_zero() => DinotraceDrawingStyle::AllZeros,
50 VariableValue::BigUint(u)
51 if num_bits.is_some_and(|bits| u.count_ones() == u64::from(bits)) =>
52 {
53 DinotraceDrawingStyle::AllOnes
54 }
55 VariableValue::BigUint(_) => DinotraceDrawingStyle::Normal,
56 VariableValue::String(_) => DinotraceDrawingStyle::Normal,
57 }
58 }
59}
60
61pub struct DrawnRegion {
62 pub inner: Option<TranslatedValue>,
63 force_anti_alias: bool,
67 dinotrace_style: DinotraceDrawingStyle,
68}
69
70pub enum DrawingCommands {
71 Digital(DigitalDrawingCommands),
72 Analog(AnalogDrawingCommands),
73}
74
75pub enum AnalogDrawingCommands {
76 Loading,
78 Ready {
80 viewport_min: f64,
82 viewport_max: f64,
83 global_min: f64,
85 global_max: f64,
86 values: Vec<AnalogDrawingCommand>,
88 min_valid_pixel: f32,
90 max_valid_pixel: f32,
92 analog_settings: AnalogSettings,
93 },
94}
95#[derive(Clone, PartialEq, Debug)]
96pub enum DigitalDrawingType {
97 Bool,
98 Clock,
99 Event,
100 Vector,
101}
102
103impl From<&VariableInfo> for DigitalDrawingType {
104 fn from(info: &VariableInfo) -> Self {
105 match info {
106 VariableInfo::Bool => DigitalDrawingType::Bool,
107 VariableInfo::Clock => DigitalDrawingType::Clock,
108 VariableInfo::Event => DigitalDrawingType::Event,
109 _ => DigitalDrawingType::Vector,
110 }
111 }
112}
113pub struct DigitalDrawingCommands {
116 pub drawing_type: DigitalDrawingType,
117 pub values: Vec<(f32, DrawnRegion)>,
118}
119
120impl DigitalDrawingCommands {
121 #[must_use]
122 pub fn new_from_variable_info(info: &VariableInfo) -> Self {
123 DigitalDrawingCommands {
124 drawing_type: DigitalDrawingType::from(info),
125 values: vec![],
126 }
127 }
128
129 pub fn push(&mut self, val: (f32, DrawnRegion)) {
130 self.values.push(val);
131 }
132}
133
134pub struct TxDrawingCommands {
135 min: Pos2,
136 max: Pos2,
137 gen_ref: TransactionStreamRef, }
139
140pub(crate) struct VariableDrawCommands {
141 pub(crate) clock_edges: Vec<f32>,
142 pub(crate) display_id: DisplayedItemRef,
143 pub(crate) local_commands: HashMap<Vec<String>, DrawingCommands>,
144 pub(crate) local_msgs: Vec<Message>,
145}
146
147#[allow(clippy::too_many_arguments)]
150fn variable_draw_commands(
151 displayed_variable: &DisplayedVariable,
152 display_id: DisplayedItemRef,
153 timestamps: &[(f32, num::BigUint)],
154 waves: &WaveData,
155 translators: &TranslatorList,
156 view_width: f32,
157 viewport_idx: usize,
158 use_dinotrace_style: bool,
159) -> Option<VariableDrawCommands> {
160 let wave_container = waves.inner.as_waves()?;
161
162 let signal_id = wave_container
163 .signal_id(&displayed_variable.variable_ref)
164 .ok()?;
165 if !wave_container.is_signal_loaded(&signal_id) {
166 return None;
167 }
168
169 let meta = match wave_container
170 .variable_meta(&displayed_variable.variable_ref)
171 .context("failed to get variable meta")
172 {
173 Ok(meta) => meta,
174 Err(e) => {
175 warn!("{e:#?}");
176 return None;
177 }
178 };
179
180 let displayed_field_ref: DisplayedFieldRef = display_id.into();
181 let translator = waves.variable_translator_with_meta(&displayed_field_ref, translators, &meta);
182 let info = translator.variable_info(&meta).unwrap();
183
184 let is_analog_mode = displayed_variable.analog.is_some();
185 let is_bool = matches!(
186 info,
187 VariableInfo::Bool | VariableInfo::Clock | VariableInfo::Event
188 );
189
190 if is_analog_mode && !is_bool {
191 variable_analog_draw_commands(
192 displayed_variable,
193 display_id,
194 waves,
195 translators,
196 view_width,
197 viewport_idx,
198 )
199 } else {
200 variable_digital_draw_commands(
201 displayed_variable,
202 display_id,
203 timestamps,
204 waves,
205 translators,
206 wave_container,
207 &meta,
208 translator,
209 &info,
210 view_width,
211 viewport_idx,
212 use_dinotrace_style,
213 )
214 }
215}
216
217#[allow(clippy::too_many_arguments)]
219fn variable_digital_draw_commands(
220 displayed_variable: &DisplayedVariable,
221 display_id: DisplayedItemRef,
222 timestamps: &[(f32, num::BigUint)],
223 waves: &WaveData,
224 translators: &TranslatorList,
225 wave_container: &crate::wave_container::WaveContainer,
226 meta: &crate::wave_container::VariableMeta,
227 translator: &crate::translation::DynTranslator,
228 info: &VariableInfo,
229 view_width: f32,
230 viewport_idx: usize,
231 use_dinotrace_style: bool,
232) -> Option<VariableDrawCommands> {
233 let mut clock_edges = vec![];
234 let mut local_msgs = vec![];
235 let displayed_field_ref: DisplayedFieldRef = display_id.into();
236 let num_timestamps = waves.safe_num_timestamps();
237
238 let mut local_commands: HashMap<Vec<String>, DigitalDrawingCommands> = HashMap::new();
239
240 let mut prev_values = HashMap::new();
241
242 let end_pixel = timestamps.iter().last().map(|t| t.0).unwrap_or_default();
245 let start_pixel = timestamps.get(1).map(|t| t.0).unwrap_or_default();
248
249 let mut next_change = timestamps.first().map(|t| t.0).unwrap_or_default();
251 for ((_, prev_time), (pixel, time)) in timestamps.iter().zip(timestamps.iter().skip(1)) {
252 let is_last_timestep = pixel == &end_pixel;
253 let is_first_timestep = pixel == &start_pixel;
254
255 if *pixel < next_change && !is_first_timestep && !is_last_timestep {
256 continue;
257 }
258
259 let query_result = wave_container.query_variable(&displayed_variable.variable_ref, time);
260 next_change = match &query_result {
261 Ok(Some(QueryResult {
262 next: Some(timestamp),
263 ..
264 })) => waves.viewports[viewport_idx].pixel_from_time(
265 ×tamp.to_bigint().unwrap(),
266 view_width,
267 &num_timestamps,
268 ),
269 Ok(_) => timestamps.last().map(|t| t.0).unwrap_or_default(),
272 _ => timestamps.first().map(|t| t.0).unwrap_or_default(),
275 };
276
277 let (change_time, val) = match query_result {
278 Ok(Some(QueryResult {
279 current: Some((change_time, val)),
280 ..
281 })) => (change_time, val),
282 Ok(Some(QueryResult { current: None, .. }) | None) => continue,
283 Err(e) => {
284 error!("Variable query error {e:#?}");
285 continue;
286 }
287 };
288
289 if &change_time < prev_time && !is_first_timestep && !is_last_timestep {
292 continue;
293 }
294
295 let translation_result = match translator.translate(meta, &val) {
296 Ok(result) => result,
297 Err(e) => {
298 error!(
299 "{translator_name} for {variable_name} failed. Disabling:",
300 translator_name = translator.name(),
301 variable_name = displayed_variable.variable_ref.full_path_string()
302 );
303 error!("{e:#}");
304 local_msgs.push(Message::ResetVariableFormat(displayed_field_ref));
305 return None;
306 }
307 };
308
309 let fields = translation_result.format_flat(
310 &displayed_variable.format,
311 &displayed_variable.field_formats,
312 translators,
313 );
314
315 let dinotrace_style = if use_dinotrace_style {
316 DinotraceDrawingStyle::from_value(&val, meta.num_bits)
317 } else {
318 DinotraceDrawingStyle::Normal
319 };
320
321 for SubFieldFlatTranslationResult { names, value } in fields {
322 let entry = local_commands.entry(names.clone()).or_insert_with(|| {
323 DigitalDrawingCommands::new_from_variable_info(info.get_subinfo(&names))
324 });
325
326 let prev = prev_values.get(&names);
327
328 let anti_alias = &change_time > prev_time
334 && names.is_empty()
335 && wave_container.wants_anti_aliasing();
336 let new_value = prev != Some(&value);
337
338 if new_value || is_last_timestep || anti_alias {
340 prev_values
341 .entry(names.clone())
342 .or_insert(value.clone())
343 .clone_from(&value);
344
345 if entry.drawing_type == DigitalDrawingType::Clock {
346 match value.as_ref().map(|result| result.value.as_str()) {
347 Some("1") => {
348 if !is_last_timestep && !is_first_timestep {
349 clock_edges.push(*pixel);
350 }
351 }
352 Some(_) => {}
353 None => {}
354 }
355 }
356
357 entry.push((
358 *pixel,
359 DrawnRegion {
360 inner: value,
361 force_anti_alias: anti_alias && !new_value,
362 dinotrace_style,
363 },
364 ));
365 }
366 }
367 }
368 Some(VariableDrawCommands {
369 clock_edges,
370 display_id,
371 local_commands: local_commands
372 .into_iter()
373 .map(|(k, v)| (k, DrawingCommands::Digital(v)))
374 .collect(),
375 local_msgs,
376 })
377}
378
379impl SystemState {
380 pub fn invalidate_draw_commands(&mut self) {
381 if let Some(waves) = &self.user.waves {
382 for viewport in 0..waves.viewports.len() {
383 self.draw_data.borrow_mut()[viewport] = None;
384 }
385 }
386 }
387
388 pub fn generate_draw_commands(
389 &self,
390 cfg: &DrawConfig,
391 frame_width: f32,
392 msgs: &mut Vec<Message>,
393 viewport_idx: usize,
394 ) {
395 #[cfg(feature = "performance_plot")]
396 self.timing.borrow_mut().start("Generate draw commands");
397 if let Some(waves) = &self.user.waves {
398 let draw_data = match waves.inner {
399 DataContainer::Waves(_) => {
400 self.generate_wave_draw_commands(waves, cfg, frame_width, msgs, viewport_idx)
401 }
402 DataContainer::Transactions(_) => self.generate_transaction_draw_commands(
403 waves,
404 cfg,
405 frame_width,
406 msgs,
407 viewport_idx,
408 ),
409 DataContainer::Empty => None,
410 };
411 self.draw_data.borrow_mut()[viewport_idx] = draw_data;
412 }
413 #[cfg(feature = "performance_plot")]
414 self.timing.borrow_mut().end("Generate draw commands");
415 }
416
417 fn generate_wave_draw_commands(
418 &self,
419 waves: &WaveData,
420 cfg: &DrawConfig,
421 frame_width: f32,
422 msgs: &mut Vec<Message>,
423 viewport_idx: usize,
424 ) -> Option<CachedDrawData> {
425 let mut draw_commands = HashMap::new();
426
427 let num_timestamps = waves.safe_num_timestamps();
428 let max_time = num_timestamps.to_f64().unwrap_or(f64::MAX);
429 let mut clock_edges = vec![];
430 let mut timestamps = (-cfg.extra_draw_width..(frame_width as i32 + cfg.extra_draw_width))
433 .par_bridge()
434 .filter_map(|x| {
435 let time = waves.viewports[viewport_idx]
436 .as_absolute_time(f64::from(x), frame_width, &num_timestamps)
437 .0;
438 if time < 0. || time > max_time {
439 None
440 } else {
441 Some((x as f32, time.to_biguint().unwrap_or_default()))
442 }
443 })
444 .collect::<Vec<_>>();
445 timestamps.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal));
446
447 let use_dinotrace_style = self.use_dinotrace_style();
448 let translators = &self.translators;
449 let commands = waves
450 .items_tree
451 .iter_visible()
452 .map(|node| (node.item_ref, waves.displayed_items.get(&node.item_ref)))
453 .filter_map(|(id, item)| match item {
454 Some(DisplayedItem::Variable(variable_ref)) => Some((id, variable_ref)),
455 _ => None,
456 })
457 .collect::<Vec<_>>()
458 .par_iter()
459 .cloned()
460 .filter_map(|(id, displayed_variable)| {
463 variable_draw_commands(
464 displayed_variable,
465 id,
466 ×tamps,
467 waves,
468 translators,
469 frame_width,
470 viewport_idx,
471 use_dinotrace_style,
472 )
473 })
474 .collect::<Vec<_>>();
475
476 for VariableDrawCommands {
477 clock_edges: mut new_clock_edges,
478 display_id,
479 local_commands,
480 mut local_msgs,
481 } in commands
482 {
483 msgs.append(&mut local_msgs);
484 for (field, val) in local_commands {
485 draw_commands.insert(
486 DisplayedFieldRef {
487 item: display_id,
488 field,
489 },
490 val,
491 );
492 }
493 clock_edges.append(&mut new_clock_edges);
494 }
495
496 let ticks =
497 self.get_ticks_for_viewport_idx(waves, viewport_idx, frame_width, cfg.text_size);
498
499 Some(CachedDrawData::WaveDrawData(CachedWaveDrawData {
500 draw_commands,
501 clock_edges,
502 ticks,
503 }))
504 }
505
506 fn generate_transaction_draw_commands(
507 &self,
508 waves: &WaveData,
509 cfg: &DrawConfig,
510 frame_width: f32,
511 msgs: &mut Vec<Message>,
512 viewport_idx: usize,
513 ) -> Option<CachedDrawData> {
514 let mut draw_commands = HashMap::new();
515 let mut stream_to_displayed_txs = HashMap::new();
516 let mut inc_relation_tx_ids = vec![];
517 let mut out_relation_tx_ids = vec![];
518
519 let (focused_tx_ref, old_focused_tx) = &waves.focused_transaction;
520 let mut new_focused_tx: Option<&Transaction> = None;
521
522 let viewport = waves.viewports[viewport_idx];
523 let num_timestamps = waves.safe_num_timestamps();
524
525 let displayed_streams = waves
526 .items_tree
527 .iter_visible()
528 .map(|node| node.item_ref)
529 .collect::<Vec<_>>()
530 .par_iter()
531 .map(|id| waves.displayed_items.get(id))
532 .filter_map(|item| match item {
533 Some(DisplayedItem::Stream(stream_ref)) => Some(stream_ref),
534 _ => None,
535 })
536 .collect::<Vec<_>>();
537
538 let first_visible_timestamp = viewport
539 .curr_left
540 .absolute(&num_timestamps)
541 .0
542 .to_biguint()
543 .unwrap_or(BigUint::ZERO);
544
545 for displayed_stream in displayed_streams {
546 let tx_stream_ref = &displayed_stream.transaction_stream_ref;
547
548 let mut generators: Vec<&TxGenerator> = vec![];
549 let mut displayed_transactions = vec![];
550
551 if tx_stream_ref.is_stream() {
552 let stream = waves
553 .inner
554 .as_transactions()
555 .unwrap()
556 .get_stream(tx_stream_ref.stream_id)
557 .unwrap();
558
559 for gen_id in &stream.generators {
560 generators.push(
561 waves
562 .inner
563 .as_transactions()
564 .unwrap()
565 .get_generator(*gen_id)
566 .unwrap(),
567 );
568 }
569 } else {
570 generators.push(
571 waves
572 .inner
573 .as_transactions()
574 .unwrap()
575 .get_generator(tx_stream_ref.gen_id.unwrap())
576 .unwrap(),
577 );
578 }
579
580 for generator in &generators {
581 let first_visible_transaction_index =
583 match generator.transactions.binary_search_by_key(
584 &first_visible_timestamp,
585 ftr_parser::types::Transaction::get_end_time,
586 ) {
587 Ok(i) => i,
588 Err(i) => i,
589 }
590 .saturating_sub(1);
591 let transactions = generator
592 .transactions
593 .iter()
594 .skip(first_visible_transaction_index);
595
596 let mut last_px = f32::NAN;
597
598 for tx in transactions {
599 let start_time = tx.get_start_time();
600 let end_time = tx.get_end_time();
601 let curr_tx_id = tx.get_tx_id();
602
603 if start_time.to_f64().unwrap()
605 > viewport.curr_right.absolute(&num_timestamps).0
606 {
607 break;
608 }
609
610 if let Some(focused_tx_ref) = focused_tx_ref
611 && curr_tx_id == focused_tx_ref.id
612 {
613 new_focused_tx = Some(tx);
614 }
615
616 let min_px = viewport.pixel_from_time(
617 &start_time.to_bigint().unwrap(),
618 frame_width - 1.,
619 &num_timestamps,
620 );
621 let max_px = viewport.pixel_from_time(
622 &end_time.to_bigint().unwrap(),
623 frame_width - 1.,
624 &num_timestamps,
625 );
626
627 if (min_px == max_px) && (min_px == last_px) {
629 last_px = max_px;
630 continue;
631 }
632 last_px = max_px;
633
634 displayed_transactions.push(TransactionRef { id: curr_tx_id });
635 let min = Pos2::new(min_px, cfg.line_height * tx.row as f32 + 4.0);
636 let max = Pos2::new(max_px, cfg.line_height * (tx.row + 1) as f32 - 4.0);
637
638 let tx_ref = TransactionRef { id: curr_tx_id };
639 draw_commands.insert(
640 tx_ref,
641 TxDrawingCommands {
642 min,
643 max,
644 gen_ref: TransactionStreamRef::new_gen(
645 tx_stream_ref.stream_id,
646 generator.id,
647 generator.name.clone(),
648 ),
649 },
650 );
651 }
652 }
653 stream_to_displayed_txs.insert(tx_stream_ref.clone(), displayed_transactions);
654 }
655
656 if let Some(focused_tx) = new_focused_tx {
657 for rel in &focused_tx.inc_relations {
658 inc_relation_tx_ids.push(TransactionRef {
659 id: rel.source_tx_id,
660 });
661 }
662 for rel in &focused_tx.out_relations {
663 out_relation_tx_ids.push(TransactionRef { id: rel.sink_tx_id });
664 }
665 if old_focused_tx.is_none() || Some(focused_tx) != old_focused_tx.as_ref() {
666 msgs.push(Message::FocusTransaction(
667 focused_tx_ref.clone(),
668 Some(focused_tx.clone()),
669 ));
670 }
671 }
672
673 Some(TransactionDrawData(CachedTransactionDrawData {
674 draw_commands,
675 stream_to_displayed_txs,
676 inc_relation_tx_ids,
677 out_relation_tx_ids,
678 }))
679 }
680
681 fn transform_pos(
683 &self,
684 to_screen: RectTransform,
685 p: Pos2,
686 ui: &Ui,
687 consider_timeline: bool,
688 ) -> Pos2 {
689 to_screen
690 .inverse()
691 .transform_pos(if consider_timeline && self.show_default_timeline() {
692 Pos2 {
693 x: p.x,
694 y: p.y - ui.text_style_height(&egui::TextStyle::Body),
695 }
696 } else {
697 p
698 })
699 }
700
701 pub fn draw_items(
702 &mut self,
703 egui_ctx: &egui::Context,
704 msgs: &mut Vec<Message>,
705 ui: &mut Ui,
706 viewport_idx: usize,
707 ) {
708 let Some(waves) = &self.user.waves else {
709 return;
710 };
711
712 let (response, mut painter) =
713 ui.allocate_painter(ui.available_size(), Sense::click_and_drag());
714
715 let frame_size = response.rect.size();
716 let frame_height = frame_size.y;
717 let frame_width = frame_size.x;
718
719 if frame_width < 1. || frame_height < 1. {
720 return;
721 }
722
723 let cfg = match waves.inner {
724 DataContainer::Waves(_) => DrawConfig::new(
725 frame_height,
726 self.user.config.layout.waveforms_line_height,
727 self.user.config.layout.waveforms_text_size,
728 ),
729 DataContainer::Transactions(_) => DrawConfig::new(
730 frame_height,
731 self.user.config.layout.transactions_line_height,
732 self.user.config.layout.waveforms_text_size,
733 ),
734 DataContainer::Empty => return,
735 };
736 if self.draw_data.borrow()[viewport_idx].is_none()
738 || Some(response.rect) != *self.last_canvas_rect.borrow()
739 {
740 self.generate_draw_commands(&cfg, frame_width, msgs, viewport_idx);
741 *self.last_canvas_rect.borrow_mut() = Some(response.rect);
742 }
743
744 let container_rect = Rect::from_min_size(Pos2::ZERO, frame_size);
745 let to_screen = RectTransform::from_to(container_rect, response.rect);
746 let pointer_pos_global = ui.input(|i| i.pointer.interact_pos());
747 let pointer_pos_canvas =
748 pointer_pos_global.map(|p| self.transform_pos(to_screen, p, ui, true));
749 let pointer_pos_mouse_gesture =
750 pointer_pos_global.map(|p| self.transform_pos(to_screen, p, ui, false));
751 let num_timestamps = waves.safe_num_timestamps();
752
753 if ui.ui_contains_pointer() {
754 let pointer_pos = pointer_pos_global.unwrap();
755 let scroll_delta = ui.input(|i| i.smooth_scroll_delta);
756 let mouse_ptr_pos = to_screen.inverse().transform_pos(pointer_pos);
757 if scroll_delta != Vec2::ZERO {
758 msgs.push(Message::CanvasScroll {
759 delta: ui.input(|i| i.smooth_scroll_delta),
760 viewport_idx,
761 });
762 }
763
764 if ui.input(egui::InputState::zoom_delta) != 1. {
765 let mouse_ptr = Some(waves.viewports[viewport_idx].as_time_bigint(
766 mouse_ptr_pos.x,
767 frame_width,
768 &num_timestamps,
769 ));
770
771 msgs.push(Message::CanvasZoom {
772 mouse_ptr,
773 delta: ui.input(egui::InputState::zoom_delta),
774 viewport_idx,
775 });
776 }
777 }
778
779 ui.input(|i| {
780 let touch = i.any_touches() && i.multi_touch().is_none();
782 let right_mouse = i.pointer.button_down(PointerButton::Secondary);
783 if touch || right_mouse {
784 msgs.push(Message::CanvasScroll {
785 delta: Vec2 {
786 x: i.pointer.delta().y,
787 y: i.pointer.delta().x,
788 },
789 viewport_idx: 0,
790 });
791 }
792 });
793
794 let modifiers = egui_ctx.input(|i| i.modifiers);
795 if !modifiers.command
797 && ((response.dragged_by(PointerButton::Primary) && !self.do_measure(&modifiers))
798 || response.clicked_by(PointerButton::Primary))
799 && let Some(snap_point) =
800 self.snap_to_edge(pointer_pos_canvas, waves, frame_width, viewport_idx)
801 {
802 msgs.push(Message::CursorSet(snap_point));
803 }
804
805 painter.rect_filled(
807 response.rect,
808 CornerRadiusF32::ZERO,
809 self.user.config.theme.canvas_colors.background,
810 );
811
812 if response.drag_started_by(PointerButton::Middle)
814 || modifiers.command && response.drag_started_by(PointerButton::Primary)
815 {
816 msgs.push(Message::SetMouseGestureDragStart(
817 ui.input(|i| i.pointer.press_origin())
818 .map(|p| self.transform_pos(to_screen, p, ui, false)),
819 ));
820 }
821
822 if response.drag_started_by(PointerButton::Primary) && self.do_measure(&modifiers) {
824 msgs.push(Message::SetMeasureDragStart(
825 ui.input(|i| i.pointer.press_origin())
826 .map(|p| self.transform_pos(to_screen, p, ui, false)),
827 ));
828 }
829
830 let mut ctx = DrawingContext {
831 painter: &mut painter,
832 cfg: &cfg,
833 to_screen: &|x, y| to_screen.transform_pos(Pos2::new(x, y)),
834 theme: &self.user.config.theme,
835 };
836
837 let gap = ui.spacing().item_spacing.y * 0.5;
838 let y_zero = to_screen.transform_pos(Pos2::ZERO).y;
842 for (item_count, drawing_info) in waves
843 .drawing_infos
844 .iter()
845 .sorted_by_key(|o| o.top() as i32)
846 .enumerate()
847 {
848 let background_color =
850 self.get_background_color(waves, drawing_info.vidx(), item_count);
851
852 self.draw_background(
853 drawing_info,
854 y_zero,
855 &ctx,
856 gap,
857 frame_width,
858 background_color,
859 );
860 }
861
862 #[cfg(feature = "performance_plot")]
863 self.timing.borrow_mut().start("Wave drawing");
864
865 match &self.draw_data.borrow()[viewport_idx] {
866 Some(CachedDrawData::WaveDrawData(draw_data)) => {
867 self.draw_wave_data(waves, draw_data, frame_width, &mut ctx);
868 }
869 Some(CachedDrawData::TransactionDrawData(draw_data)) => {
870 self.draw_transaction_data(
871 waves,
872 draw_data,
873 viewport_idx,
874 frame_width,
875 ui,
876 msgs,
877 &mut ctx,
878 );
879 }
880 None => {}
881 }
882 #[cfg(feature = "performance_plot")]
883 self.timing.borrow_mut().end("Wave drawing");
884
885 waves.draw_graphics(
886 &mut ctx,
887 frame_size,
888 &waves.viewports[viewport_idx],
889 &self.user.config.theme,
890 );
891
892 waves.draw_cursor(
893 &self.user.config.theme,
894 &mut ctx,
895 frame_size,
896 &waves.viewports[viewport_idx],
897 );
898
899 waves.draw_markers(
900 &self.user.config.theme,
901 &mut ctx,
902 frame_size,
903 &waves.viewports[viewport_idx],
904 );
905
906 self.draw_marker_boxes(
907 waves,
908 &mut ctx,
909 frame_width,
910 gap,
911 &waves.viewports[viewport_idx],
912 y_zero,
913 );
914
915 if self.show_default_timeline() {
916 let rect = Rect {
917 min: Pos2 { x: 0.0, y: y_zero },
918 max: Pos2 {
919 x: response.rect.max.x,
920 y: y_zero + ui.text_style_height(&egui::TextStyle::Body),
921 },
922 };
923 ctx.painter
924 .rect_filled(rect, 0.0, self.user.config.theme.canvas_colors.background);
925 self.draw_default_timeline(waves, &ctx, viewport_idx, frame_width);
926 }
927
928 self.draw_mouse_gesture_widget(
929 egui_ctx,
930 waves,
931 pointer_pos_mouse_gesture,
932 &response,
933 msgs,
934 &mut ctx,
935 viewport_idx,
936 );
937
938 self.draw_measure_widget(
939 egui_ctx,
940 waves,
941 pointer_pos_mouse_gesture,
942 &response,
943 msgs,
944 &mut ctx,
945 viewport_idx,
946 );
947 self.handle_canvas_context_menu(&response, waves, to_screen, &mut ctx, msgs, viewport_idx);
948 }
949
950 fn draw_wave_data(
951 &self,
952 waves: &WaveData,
953 draw_data: &CachedWaveDrawData,
954 frame_width: f32,
955 ctx: &mut DrawingContext,
956 ) {
957 let clock_edges = &draw_data.clock_edges;
958 let draw_commands = &draw_data.draw_commands;
959 let draw_clock_edges = match clock_edges.as_slice() {
960 [] => false,
961 [_single] => true,
962 [first, second, ..] => second - first > 20.,
963 };
964 let draw_clock_rising_marker =
965 draw_clock_edges && self.user.config.theme.clock_rising_marker;
966 let ticks = &draw_data.ticks;
967 if !ticks.is_empty() && self.show_ticks() {
968 let stroke = Stroke::from(&self.user.config.theme.ticks.style);
969
970 for (_, x) in ticks {
971 waves.draw_tick_line(*x, ctx, &stroke);
972 }
973 }
974
975 if draw_clock_edges {
976 draw_clock_edge_marks(
977 clock_edges,
978 ctx,
979 &self.user.config,
980 self.clock_highlight_type(),
981 );
982 }
983 let zero_y = (ctx.to_screen)(0., 0.).y;
984 for (item_count, drawing_info) in waves
985 .drawing_infos
986 .iter()
987 .sorted_by_key(|o| o.top() as i32)
988 .enumerate()
989 {
990 let y_offset = drawing_info.top() - zero_y;
994
995 let displayed_item = waves
996 .items_tree
997 .get_visible(drawing_info.vidx())
998 .and_then(|node| waves.displayed_items.get(&node.item_ref));
999 let color = displayed_item
1000 .and_then(super::displayed_item::DisplayedItem::color)
1001 .and_then(|color| self.user.config.theme.get_color(color));
1002
1003 match drawing_info {
1004 ItemDrawingInfo::Variable(variable_info) => {
1005 if let Some(commands) = draw_commands.get(&variable_info.displayed_field_ref) {
1006 let height_scaling_factor = displayed_item.map_or(
1007 1.0,
1008 super::displayed_item::DisplayedItem::height_scaling_factor,
1009 );
1010
1011 let color = color.unwrap_or_else(|| {
1012 if let Some(DisplayedItem::Variable(variable)) = displayed_item {
1013 waves
1014 .inner
1015 .as_waves()
1016 .and_then(|w| w.variable_meta(&variable.variable_ref).ok())
1017 .and_then(|meta| {
1018 if meta.is_event() {
1019 Some(self.user.config.theme.variable_event)
1020 } else if meta.is_parameter() {
1021 Some(self.user.config.theme.variable_parameter)
1022 } else {
1023 None
1024 }
1025 })
1026 .unwrap_or(self.user.config.theme.variable_default)
1027 } else {
1028 self.user.config.theme.variable_default
1029 }
1030 });
1031 match commands {
1032 DrawingCommands::Digital(digital_commands) => {
1033 match digital_commands.drawing_type {
1034 DigitalDrawingType::Bool | DigitalDrawingType::Clock => {
1035 let draw_clock = (digital_commands.drawing_type
1036 == DigitalDrawingType::Clock)
1037 && draw_clock_rising_marker;
1038 let draw_background = self.fill_high_values();
1039 for (old, new) in digital_commands
1040 .values
1041 .iter()
1042 .zip(digital_commands.values.iter().skip(1))
1043 {
1044 self.draw_bool_transition(
1045 (old, new),
1046 new.1.force_anti_alias,
1047 color,
1048 y_offset,
1049 height_scaling_factor,
1050 draw_clock,
1051 draw_background,
1052 ctx,
1053 );
1054 }
1055 }
1056 DigitalDrawingType::Event => {
1057 for event in &digital_commands.values {
1058 self.draw_event(
1059 event,
1060 color,
1061 y_offset,
1062 height_scaling_factor,
1063 ctx,
1064 );
1065 }
1066 }
1067 DigitalDrawingType::Vector => {
1068 let background_color = self.get_background_color(
1070 waves,
1071 drawing_info.vidx(),
1072 item_count,
1073 );
1074
1075 let text_color = self
1076 .user
1077 .config
1078 .theme
1079 .get_best_text_color(background_color);
1080
1081 for (old, new) in digital_commands
1082 .values
1083 .iter()
1084 .zip(digital_commands.values.iter().skip(1))
1085 {
1086 self.draw_region(
1087 (old, new),
1088 color,
1089 y_offset,
1090 height_scaling_factor,
1091 ctx,
1092 text_color,
1093 );
1094 }
1095 }
1096 }
1097 }
1098 DrawingCommands::Analog(analog_commands) => {
1099 crate::analog_renderer::draw_analog(
1100 analog_commands,
1101 color,
1102 y_offset,
1103 height_scaling_factor,
1104 frame_width,
1105 ctx,
1106 );
1107 }
1108 }
1109 }
1110 }
1111 ItemDrawingInfo::Divider(_) => {}
1112 ItemDrawingInfo::Marker(_) => {}
1113 ItemDrawingInfo::TimeLine(_) => {
1114 let text_color = color.unwrap_or(
1115 self.user
1117 .config
1118 .theme
1119 .get_best_text_color(self.get_background_color(
1120 waves,
1121 drawing_info.vidx(),
1122 item_count,
1123 )),
1124 );
1125 waves.draw_ticks(text_color, ticks, ctx, y_offset, Align2::CENTER_TOP);
1126 }
1127 ItemDrawingInfo::Stream(_) => {}
1128 ItemDrawingInfo::Group(_) => {}
1129 ItemDrawingInfo::Placeholder(_) => {}
1130 }
1131 }
1132 }
1133
1134 #[allow(clippy::too_many_arguments)]
1135 fn draw_transaction_data(
1136 &self,
1137 waves: &WaveData,
1138 draw_data: &CachedTransactionDrawData,
1139 viewport_idx: usize,
1140 frame_width: f32,
1141 ui: &mut Ui,
1142 msgs: &mut Vec<Message>,
1143 ctx: &mut DrawingContext,
1144 ) {
1145 let draw_commands = &draw_data.draw_commands;
1146 let stream_to_displayed_txs = &draw_data.stream_to_displayed_txs;
1147 let inc_relation_tx_ids = &draw_data.inc_relation_tx_ids;
1148 let out_relation_tx_ids = &draw_data.out_relation_tx_ids;
1149
1150 let mut inc_relation_starts = vec![];
1151 let mut out_relation_starts = vec![];
1152 let mut focused_transaction_start: Option<Pos2> = None;
1153
1154 let ticks =
1155 self.get_ticks_for_viewport_idx(waves, viewport_idx, frame_width, ctx.cfg.text_size);
1156
1157 if !ticks.is_empty() && self.show_ticks() {
1158 let stroke = Stroke::from(&self.user.config.theme.ticks.style);
1159
1160 for (_, x) in &ticks {
1161 waves.draw_tick_line(*x, ctx, &stroke);
1162 }
1163 }
1164
1165 let border_stroke = Stroke::new(
1167 self.user.config.theme.linewidth,
1168 self.user.config.theme.foreground,
1169 );
1170
1171 let zero_y = (ctx.to_screen)(0., 0.).y;
1172 for (item_count, drawing_info) in waves
1173 .drawing_infos
1174 .iter()
1175 .sorted_by_key(|o| o.top() as i32)
1176 .enumerate()
1177 {
1178 let y_offset = drawing_info.top() - zero_y;
1179
1180 let displayed_item = waves
1181 .items_tree
1182 .get_visible(drawing_info.vidx())
1183 .and_then(|node| waves.displayed_items.get(&node.item_ref));
1184 let color = displayed_item
1185 .and_then(super::displayed_item::DisplayedItem::color)
1186 .and_then(|color| self.user.config.theme.get_color(color));
1187 let tx_color = color.unwrap_or(self.user.config.theme.transaction_default);
1188
1189 match drawing_info {
1190 ItemDrawingInfo::Stream(stream) => {
1191 if let Some(tx_refs) =
1192 stream_to_displayed_txs.get(&stream.transaction_stream_ref)
1193 {
1194 for tx_ref in tx_refs {
1195 if let Some(tx_draw_command) = draw_commands.get(tx_ref) {
1196 let mut min = tx_draw_command.min;
1197 let mut max = tx_draw_command.max;
1198
1199 min.x = min.x.max(0.);
1200 max.x = max.x.min(frame_width - 1.);
1201
1202 let min = (ctx.to_screen)(min.x, y_offset + min.y);
1203 let max = (ctx.to_screen)(max.x, y_offset + max.y);
1204
1205 let start = Pos2::new(min.x, f32::midpoint(min.y, max.y));
1206
1207 let is_transaction_focused = waves
1208 .focused_transaction
1209 .0
1210 .as_ref()
1211 .is_some_and(|t| t == tx_ref);
1212
1213 if inc_relation_tx_ids.contains(tx_ref) {
1214 inc_relation_starts.push(start);
1215 } else if out_relation_tx_ids.contains(tx_ref) {
1216 out_relation_starts.push(start);
1217 } else if is_transaction_focused {
1218 focused_transaction_start = Some(start);
1219 }
1220
1221 let transaction_rect = Rect { min, max };
1222 if (max.x - min.x) > 1.0 {
1223 let mut response =
1224 ui.allocate_rect(transaction_rect, Sense::click());
1225
1226 response = handle_transaction_tooltip(
1227 response,
1228 waves,
1229 &tx_draw_command.gen_ref,
1230 tx_ref,
1231 );
1232
1233 if response.clicked() {
1234 msgs.push(Message::FocusTransaction(
1235 Some(tx_ref.clone()),
1236 None,
1237 ));
1238 }
1239
1240 let tx_fill_color = if is_transaction_focused {
1241 Color32::from_rgb(
1243 255 - tx_color.r(),
1244 255 - tx_color.g(),
1245 255 - tx_color.b(),
1246 )
1247 } else {
1248 tx_color
1249 };
1250
1251 let stroke =
1252 Stroke::new(1.5, tx_fill_color.gamma_multiply(1.2));
1253 ctx.painter.rect(
1254 transaction_rect,
1255 CornerRadiusF32::same(5.0),
1256 tx_fill_color,
1257 stroke,
1258 epaint::StrokeKind::Middle,
1259 );
1260 } else {
1261 let tx_fill_color = tx_color.gamma_multiply(1.2);
1262
1263 let stroke = Stroke::new(1.5, tx_fill_color);
1264 ctx.painter.rect(
1265 transaction_rect,
1266 CornerRadiusF32::ZERO,
1267 tx_fill_color,
1268 stroke,
1269 epaint::StrokeKind::Middle,
1270 );
1271 }
1272 }
1273 }
1274 ctx.painter.hline(
1275 0.0..=((ctx.to_screen)(frame_width, 0.0).x),
1276 drawing_info.bottom(),
1277 border_stroke,
1278 );
1279 }
1280 }
1281 ItemDrawingInfo::TimeLine(_) => {
1282 let text_color = color.unwrap_or(
1283 self.user
1285 .config
1286 .theme
1287 .get_best_text_color(self.get_background_color(
1288 waves,
1289 drawing_info.vidx(),
1290 item_count,
1291 )),
1292 );
1293 waves.draw_ticks(text_color, &ticks, ctx, y_offset, Align2::CENTER_TOP);
1294 }
1295 ItemDrawingInfo::Variable(_) => {}
1296 ItemDrawingInfo::Divider(_) => {}
1297 ItemDrawingInfo::Marker(_) => {}
1298 ItemDrawingInfo::Group(_) => {}
1299 ItemDrawingInfo::Placeholder(_) => {}
1300 }
1301 }
1302
1303 if let Some(focused_pos) = focused_transaction_start {
1305 let path_stroke = PathStroke::from(&ctx.theme.relation_arrow.style);
1306 for start_pos in inc_relation_starts {
1307 self.draw_arrow(start_pos, focused_pos, ctx, &path_stroke);
1308 }
1309
1310 for end_pos in out_relation_starts {
1311 self.draw_arrow(focused_pos, end_pos, ctx, &path_stroke);
1312 }
1313 }
1314 }
1315
1316 fn draw_region(
1317 &self,
1318 ((old_x, prev_region), (new_x, _)): (&(f32, DrawnRegion), &(f32, DrawnRegion)),
1319 user_color: Color32,
1320 offset: f32,
1321 height_scaling_factor: f32,
1322 ctx: &mut DrawingContext,
1323 text_color: Color32,
1324 ) {
1325 if let Some(prev_result) = &prev_region.inner {
1326 let color = prev_result.kind.color(user_color, ctx.theme);
1327 let transition_width = (new_x - old_x).min(ctx.theme.vector_transition_width);
1328
1329 let trace_coords =
1330 |x, y| (ctx.to_screen)(x, y * ctx.cfg.line_height * height_scaling_factor + offset);
1331
1332 let points = vec![
1333 trace_coords(*old_x, 0.5),
1334 trace_coords(old_x + transition_width / 2., 0.0),
1335 trace_coords(new_x - transition_width / 2., 0.0),
1336 trace_coords(*new_x, 0.5),
1337 trace_coords(new_x - transition_width / 2., 1.0),
1338 trace_coords(old_x + transition_width / 2., 1.0),
1339 trace_coords(*old_x, 0.5),
1340 ];
1341
1342 if self.user.config.theme.wide_opacity != 0.0 {
1343 ctx.painter.add(PathShape::convex_polygon(
1346 points.clone(),
1347 color.gamma_multiply(self.user.config.theme.wide_opacity),
1348 PathStroke::NONE,
1349 ));
1350 }
1351 match prev_region.dinotrace_style {
1352 DinotraceDrawingStyle::Normal => {
1353 let stroke = Stroke {
1354 color,
1355 width: self.user.config.theme.linewidth,
1356 };
1357
1358 ctx.painter.add(PathShape::line(points, stroke));
1359 }
1360 DinotraceDrawingStyle::AllOnes => {
1361 let stroke_thick = Stroke {
1362 color,
1363 width: self.user.config.theme.thick_linewidth,
1364 };
1365 let stroke = Stroke {
1366 color,
1367 width: self.user.config.theme.linewidth,
1368 };
1369 ctx.painter
1370 .add(PathShape::line(points[0..4].to_vec(), stroke_thick));
1371 ctx.painter
1372 .add(PathShape::line(points[3..7].to_vec(), stroke));
1373 }
1374 DinotraceDrawingStyle::AllZeros => {
1375 let stroke_thick = Stroke {
1376 color,
1377 width: self.user.config.theme.thick_linewidth,
1378 };
1379 ctx.painter
1380 .add(PathShape::line(points[3..7].to_vec(), stroke_thick));
1381 }
1382 }
1383
1384 let text_size = ctx.cfg.text_size;
1385 let char_width = text_size * (20. / 31.);
1386
1387 let text_area = (new_x - old_x) - transition_width;
1388 let num_chars = (text_area / char_width).floor() as usize;
1389 let fits_text = num_chars >= 1;
1390
1391 if fits_text {
1392 let content = if prev_result.value.len() > num_chars {
1393 prev_result
1394 .value
1395 .chars()
1396 .take(num_chars - 1)
1397 .chain(['…'])
1398 .collect::<String>()
1399 } else {
1400 prev_result.value.to_string()
1401 };
1402
1403 ctx.painter.text(
1404 trace_coords(*old_x + transition_width, 0.5),
1405 Align2::LEFT_CENTER,
1406 content,
1407 FontId::monospace(text_size),
1408 text_color,
1409 );
1410 }
1411 }
1412 }
1413
1414 #[allow(clippy::too_many_arguments)]
1415 fn draw_bool_transition(
1416 &self,
1417 ((old_x, prev_region), (new_x, new_region)): (&(f32, DrawnRegion), &(f32, DrawnRegion)),
1418 force_anti_alias: bool,
1419 color: Color32,
1420 offset: f32,
1421 height_scaling_factor: f32,
1422 draw_clock_marker: bool,
1423 draw_background: bool,
1424 ctx: &mut DrawingContext,
1425 ) {
1426 if let (Some(prev_result), Some(new_result)) = (&prev_region.inner, &new_region.inner) {
1427 let trace_coords =
1428 |x, y| (ctx.to_screen)(x, y * ctx.cfg.line_height * height_scaling_factor + offset);
1429
1430 let (old_height, old_color, old_bg) = prev_result.value.bool_drawing_spec(
1431 color,
1432 &self.user.config.theme,
1433 prev_result.kind,
1434 );
1435 let (new_height, _, _) =
1436 new_result
1437 .value
1438 .bool_drawing_spec(color, &self.user.config.theme, new_result.kind);
1439
1440 if let (Some(old_bg), true) = (old_bg, draw_background) {
1441 ctx.painter.add(RectShape::new(
1442 Rect {
1443 min: (ctx.to_screen)(*old_x, offset),
1444 max: (ctx.to_screen)(
1445 *new_x,
1446 offset
1447 + ctx.cfg.line_height * height_scaling_factor
1448 + ctx.theme.linewidth / 2.,
1449 ),
1450 },
1451 CornerRadiusF32::ZERO,
1452 old_bg,
1453 Stroke::NONE,
1454 epaint::StrokeKind::Middle,
1455 ));
1456 }
1457
1458 let stroke = Stroke {
1459 color: old_color,
1460 width: self.user.config.theme.linewidth,
1461 };
1462
1463 if force_anti_alias {
1464 ctx.painter.add(PathShape::line(
1465 vec![trace_coords(*new_x, 0.0), trace_coords(*new_x, 1.0)],
1466 stroke,
1467 ));
1468 }
1469
1470 ctx.painter.add(PathShape::line(
1471 vec![
1472 trace_coords(*old_x, 1. - old_height),
1473 trace_coords(*new_x, 1. - old_height),
1474 trace_coords(*new_x, 1. - new_height),
1475 ],
1476 stroke,
1477 ));
1478
1479 if draw_clock_marker && (old_height < new_height) {
1480 ctx.painter.add(PathShape::convex_polygon(
1481 vec![
1482 trace_coords(*new_x - 2.5, 0.6),
1483 trace_coords(*new_x, 0.4),
1484 trace_coords(*new_x + 2.5, 0.6),
1485 ],
1486 old_color,
1487 stroke,
1488 ));
1489 }
1490 }
1491 }
1492
1493 fn draw_event(
1494 &self,
1495 (x, prev_region): &(f32, DrawnRegion),
1496 color: Color32,
1497 offset: f32,
1498 height_scaling_factor: f32,
1499 ctx: &mut DrawingContext,
1500 ) {
1501 if prev_region.inner.is_some() {
1502 let trace_coords =
1503 |x, y| (ctx.to_screen)(x, y * ctx.cfg.line_height * height_scaling_factor + offset);
1504
1505 let stroke = Stroke {
1506 color,
1507 width: self.user.config.theme.linewidth,
1508 };
1509
1510 let top = trace_coords(*x, 0.0);
1513 ctx.painter
1514 .add(PathShape::line(vec![top, trace_coords(*x, 1.0)], stroke));
1515
1516 ctx.painter.add(PathShape::convex_polygon(
1517 vec![
1518 trace_coords(*x - 2.5, 0.2),
1519 top,
1520 trace_coords(*x + 2.5, 0.2),
1521 ],
1522 color,
1523 stroke,
1524 ));
1525 }
1526 }
1527
1528 fn draw_arrow(&self, start: Pos2, end: Pos2, ctx: &DrawingContext, stroke: &PathStroke) {
1530 let x_diff = (end.x - start.x).max(100.);
1531 let scaled_x_diff = 0.4 * x_diff;
1532
1533 let anchor1 = Pos2 {
1534 x: start.x + scaled_x_diff,
1535 y: start.y,
1536 };
1537 let anchor2 = Pos2 {
1538 x: end.x - scaled_x_diff,
1539 y: end.y,
1540 };
1541
1542 ctx.painter.add(Shape::CubicBezier(CubicBezierShape {
1543 points: [start, anchor1, anchor2, end],
1544 closed: false,
1545 fill: Default::default(),
1546 stroke: stroke.clone(),
1547 }));
1548
1549 self.draw_arrowheads(anchor2, end, ctx, stroke);
1550 }
1551
1552 fn draw_arrowheads(
1555 &self,
1556 vec_start: Pos2,
1557 vec_tip: Pos2,
1558 ctx: &DrawingContext,
1559 stroke: &PathStroke,
1560 ) {
1561 let head_length = ctx.theme.relation_arrow.head_length;
1562
1563 let vec_x = vec_tip.x - vec_start.x;
1564 let vec_y = vec_tip.y - vec_start.y;
1565
1566 let alpha = (2. * PI / 360.) * ctx.theme.relation_arrow.head_angle;
1567
1568 let vec_angled_x = vec_x * alpha.cos() + vec_y * alpha.sin();
1570 let vec_angled_y = -vec_x * alpha.sin() + vec_y * alpha.cos();
1571
1572 let vec_angled_x = (1. / (vec_angled_y - vec_angled_x).abs()) * vec_angled_x * head_length;
1574 let vec_angled_y = (1. / (vec_angled_y - vec_angled_x).abs()) * vec_angled_y * head_length;
1575
1576 let arrowhead_left_x = vec_tip.x - vec_angled_x;
1577 let arrowhead_left_y = vec_tip.y - vec_angled_y;
1578
1579 let arrowhead_right_x = vec_tip.x + vec_angled_y;
1580 let arrowhead_right_y = vec_tip.y - vec_angled_x;
1581
1582 ctx.painter.add(PathShape::line(
1583 vec![
1584 Pos2::new(arrowhead_right_x, arrowhead_right_y),
1585 vec_tip,
1586 Pos2::new(arrowhead_left_x, arrowhead_left_y),
1587 ],
1588 stroke.clone(),
1589 ));
1590 }
1591
1592 fn handle_canvas_context_menu(
1593 &self,
1594 response: &Response,
1595 waves: &WaveData,
1596 to_screen: RectTransform,
1597 ctx: &mut DrawingContext,
1598 msgs: &mut Vec<Message>,
1599 viewport_idx: usize,
1600 ) {
1601 let frame_size = response.rect.size();
1602 response.context_menu(|ui| {
1603 let offset = f32::from(ui.spacing().menu_margin.left);
1604 let top_left = to_screen.inverse().transform_rect(ui.min_rect()).left_top()
1605 - Pos2 {
1606 x: offset,
1607 y: offset,
1608 };
1609
1610 let snap_pos =
1611 self.snap_to_edge(Some(top_left.to_pos2()), waves, frame_size.x, viewport_idx);
1612
1613 if let Some(time) = snap_pos {
1614 self.draw_line(&time, ctx, frame_size, viewport_idx, waves);
1615 ui.menu_button("Set marker", |ui| {
1616 for id in waves.markers.keys().sorted() {
1617 ui.button(format!("{id}")).clicked().then(|| {
1618 msgs.push(Message::SetMarker {
1619 id: *id,
1620 time: time.clone(),
1621 });
1622 });
1623 }
1624 if waves.can_add_marker() {
1626 ui.button("New").clicked().then(|| {
1627 msgs.push(Message::AddMarker {
1628 time,
1629 name: None,
1630 move_focus: true,
1631 });
1632 });
1633 }
1634 });
1635 }
1636 });
1637 }
1638
1639 fn snap_to_edge(
1644 &self,
1645 pointer_pos_canvas: Option<Pos2>,
1646 waves: &WaveData,
1647 frame_width: f32,
1648 viewport_idx: usize,
1649 ) -> Option<BigInt> {
1650 let pos = pointer_pos_canvas?;
1651 let viewport = &waves.viewports[viewport_idx];
1652 let num_timestamps = waves.safe_num_timestamps();
1653 let timestamp = viewport.as_time_bigint(pos.x, frame_width, &num_timestamps);
1654 if let Some(utimestamp) = timestamp.to_biguint()
1655 && let Some(vidx) = waves.get_item_at_y(pos.y)
1656 && let Some(node) = waves.items_tree.get_visible(vidx)
1657 && let Some(DisplayedItem::Variable(variable)) =
1658 &waves.displayed_items.get(&node.item_ref)
1659 && let Ok(Some(res)) = waves
1660 .inner
1661 .as_waves()
1662 .unwrap()
1663 .query_variable(&variable.variable_ref, &utimestamp)
1664 {
1665 let prev_time = &res
1666 .current
1667 .and_then(|v| v.0.to_bigint())
1668 .unwrap_or(BigInt::ZERO);
1669 let next_time = &res
1670 .next
1671 .unwrap_or_default()
1672 .to_bigint()
1673 .unwrap_or(BigInt::ZERO);
1674 let prev = viewport.pixel_from_time(prev_time, frame_width, &num_timestamps);
1675 let next = viewport.pixel_from_time(next_time, frame_width, &num_timestamps);
1676 if (prev - pos.x).abs() < (next - pos.x).abs() {
1677 if (prev - pos.x).abs() <= self.user.config.snap_distance {
1678 return Some(prev_time.clone());
1679 }
1680 } else if (next - pos.x).abs() <= self.user.config.snap_distance {
1681 return Some(next_time.clone());
1682 }
1683 }
1684 Some(timestamp)
1685 }
1686
1687 pub fn draw_line(
1688 &self,
1689 time: &BigInt,
1690 ctx: &mut DrawingContext,
1691 size: Vec2,
1692 viewport_idx: usize,
1693 waves: &WaveData,
1694 ) {
1695 let x = waves.viewports[viewport_idx].pixel_from_time(
1696 time,
1697 size.x,
1698 &waves.safe_num_timestamps(),
1699 );
1700
1701 ctx.painter.line_segment(
1702 [(ctx.to_screen)(x, 0.), (ctx.to_screen)(x, size.y)],
1703 self.user.config.theme.cursor.clone(),
1704 );
1705 }
1706}
1707
1708impl WaveData {}
1709
1710trait VariableExt {
1711 fn bool_drawing_spec(
1712 &self,
1713 user_color: Color32,
1714 theme: &SurferTheme,
1715 value_kind: ValueKind,
1716 ) -> (f32, Color32, Option<Color32>);
1717}
1718
1719impl VariableExt for String {
1720 fn bool_drawing_spec(
1722 &self,
1723 user_color: Color32,
1724 theme: &SurferTheme,
1725 value_kind: ValueKind,
1726 ) -> (f32, Color32, Option<Color32>) {
1727 let color = value_kind.color(user_color, theme);
1728 let (height, background) = match (value_kind, self) {
1729 (
1730 ValueKind::HighImp
1731 | ValueKind::Undef
1732 | ValueKind::DontCare
1733 | ValueKind::Warn
1734 | ValueKind::Error
1735 | ValueKind::Custom(_),
1736 _,
1737 ) => (0.5, None),
1738 (ValueKind::Weak, other) => {
1739 if other.to_lowercase() == "l" {
1740 (0., None)
1741 } else {
1742 (1., Some(color.gamma_multiply(theme.waveform_opacity)))
1743 }
1744 }
1745 (ValueKind::Normal, other) => {
1746 if other == "0" {
1747 (0., None)
1748 } else {
1749 (1., Some(color.gamma_multiply(theme.waveform_opacity)))
1750 }
1751 }
1752 (ValueKind::Event, _) => (1., Some(color.gamma_multiply(theme.waveform_opacity))),
1753 };
1754 (height, color, background)
1755 }
1756}