From: Soeren Apel Date: Fri, 5 Oct 2018 21:52:12 +0000 (+0200) Subject: View: Refine snapping algorithm X-Git-Url: https://sigrok.org/gitaction?a=commitdiff_plain;h=e0544801de2dda0b2e353eff04b0b23a40b85167;p=pulseview.git View: Refine snapping algorithm We now also choose the best match depending on edge density. --- diff --git a/pv/data/logicsegment.cpp b/pv/data/logicsegment.cpp index 32a7adb2..2f69e02d 100644 --- a/pv/data/logicsegment.cpp +++ b/pv/data/logicsegment.cpp @@ -375,6 +375,7 @@ void LogicSegment::get_surrounding_edges(vector &dest, // use a get_subsampled_edges() implementation that searches backwards vector* edges = new vector; + // Get all edges to the left of origin_sample get_subsampled_edges(*edges, 0, origin_sample, min_length, sig_index, false); // If we don't specify "first only", the first and last edge are the states @@ -390,6 +391,7 @@ void LogicSegment::get_surrounding_edges(vector &dest, dest.push_back(edges->back()); edges->clear(); + // Get first edge to the right of origin_sample get_subsampled_edges(*edges, origin_sample, sample_count_, min_length, sig_index, true); // "first only" is specified, so nothing needs to be dismissed diff --git a/pv/views/trace/view.cpp b/pv/views/trace/view.cpp index e68c16a8..625722d4 100644 --- a/pv/views/trace/view.cpp +++ b/pv/views/trace/view.cpp @@ -862,79 +862,103 @@ const QPoint& View::hover_point() const int64_t View::get_nearest_level_change(const QPoint &p) { + // Is snapping disabled? if (snap_distance_ == 0) return -1; - shared_ptr signal = signal_under_mouse_cursor_; + struct entry_t { + entry_t(shared_ptr s) : + signal(s), delta(numeric_limits::max()), sample(-1), is_dense(false) {} + shared_ptr signal; + int64_t delta; + int64_t sample; + bool is_dense; + }; - vector nearest_edges; - int64_t nearest_sample = -1; + vector list; - if (signal) { - // Determine nearest edge from specific signal + // Create list of signals to consider + if (signal_under_mouse_cursor_) + list.emplace_back(signal_under_mouse_cursor_); + else + for (shared_ptr s : signals_) { + if (!s->enabled()) + continue; + + list.emplace_back(s); + } + // Get data for listed signals + for (entry_t &e : list) { // Calculate sample number from cursor position - const double samples_per_pixel = signal->base()->get_samplerate() * scale(); + const double samples_per_pixel = e.signal->base()->get_samplerate() * scale(); const int64_t x_offset = offset().convert_to() / scale(); const int64_t sample_num = max(((x_offset + p.x()) * samples_per_pixel), 0.0); - nearest_edges = signal->get_nearest_level_changes(sample_num); + vector edges = + e.signal->get_nearest_level_changes(sample_num); - if (nearest_edges.size() != 2) - return -1; + if (edges.empty()) + continue; - // We received absolute sample numbers, make them relative - const int64_t left_sample_delta = sample_num - nearest_edges.front().first; - const int64_t right_sample_delta = nearest_edges.back().first - sample_num - 1; + // Check first edge + const int64_t first_sample_delta = abs(sample_num - edges.front().first); + const int64_t first_delta = first_sample_delta / samples_per_pixel; + e.delta = first_delta; + e.sample = edges.front().first; - const int64_t left_delta = left_sample_delta / samples_per_pixel; - const int64_t right_delta = right_sample_delta / samples_per_pixel; + // Check second edge if available + if (edges.size() == 2) { + // Note: -1 because this is usually the right edge and sample points are left-aligned + const int64_t second_sample_delta = abs(sample_num - edges.back().first - 1); + const int64_t second_delta = second_sample_delta / samples_per_pixel; - // Only use closest left or right edge if they're close to the cursor - if ((left_delta < right_delta) && (left_delta <= snap_distance_)) - nearest_sample = nearest_edges.front().first; - if ((left_delta >= right_delta) && (right_delta <= snap_distance_)) - nearest_sample = nearest_edges.back().first; - } else { - // Determine nearest edge from all signals - - int64_t nearest_delta = numeric_limits::max(); - for (shared_ptr s : signals_) { - if (!s->enabled()) - continue; + // If both edges are too close, we mark this signal as being dense + if ((first_delta + second_delta) <= snap_distance_) + e.is_dense = true; - // Calculate sample number from cursor position - const double samples_per_pixel = s->base()->get_samplerate() * scale(); - const int64_t x_offset = offset().convert_to() / scale(); - const int64_t sample_num = max(((x_offset + p.x()) * samples_per_pixel), 0.0); + if (second_delta < first_delta) { + e.delta = second_delta; + e.sample = edges.back().first; + } + } + } - vector edges = - s->get_nearest_level_changes(sample_num); + // Look for the best match: non-dense first, then dense + entry_t *match = nullptr; - if (edges.size() != 2) - continue; + for (entry_t &e : list) { + if (e.delta > snap_distance_ || e.is_dense) + continue; - // We received absolute sample numbers, make them relative - const int64_t left_sample_delta = sample_num - edges.front().first; - const int64_t right_sample_delta = edges.back().first - sample_num - 1; + if (match) { + if (e.delta < match->delta) + match = &e; + } else + match = &e; + } - const int64_t left_delta = left_sample_delta / samples_per_pixel; - const int64_t right_delta = right_sample_delta / samples_per_pixel; + if (!match) { + for (entry_t &e : list) { + if (!e.is_dense) + continue; - if ((left_delta < nearest_delta) || (right_delta < nearest_delta)) { - nearest_delta = min(left_delta, right_delta); + if (match) { + if (e.delta < match->delta) + match = &e; + } else + match = &e; + } + } - if (nearest_delta <= snap_distance_) - nearest_sample = (nearest_delta == left_delta) ? - edges.front().first : edges.back().first; + if (match) { + // Somewhat ugly hack to make TimeItem::drag_by() work + signal_under_mouse_cursor_ = match->signal; - // Somewhat ugly hack to make TimeItem::drag_by() work - signal_under_mouse_cursor_ = s; - } - } + return match->sample; } - return nearest_sample; + return -1; } void View::restack_all_trace_tree_items()