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