feat(plot): horizontal rulers alongside the vertical cursors
This commit is contained in:
1 parent
2d819c4228
commit
63f9655ae1
11 files changed
+514
-29
No files matched your search
+51
-10
@@ -920,7 +920,9 @@ void Renderer::draw_traces(const Plot& plot, const PlotLayout& layout) {
|
||||
}
|
||||
|
||||
void Renderer::draw_cursors(const Plot& plot, const PlotLayout& layout) {
|
||||
const Range x_range = plot.x_range(0);
|
||||
const Range y_range = plot.y_range(0);
|
||||
const bool log_x = plot.x_axis(0).log;
|
||||
const bool log_y = plot.y_axis(0).log;
|
||||
// The handles the input layer last found under the pointer: a marker hovered or
|
||||
// being dragged is drawn in the style's cursor colour and as a *tab*, so the cue
|
||||
@@ -933,20 +935,59 @@ void Renderer::draw_cursors(const Plot& plot, const PlotLayout& layout) {
|
||||
if (!cursor.visible) continue;
|
||||
const u64 id = static_cast<u64>(i);
|
||||
const bool on = id == hot.cursor || id == hot.grabbed_cursor;
|
||||
const Transform transform = plot.x_transform(cursor.axis_x);
|
||||
const f32 px = static_cast<f32>(transform.to_screen_x(cursor.x));
|
||||
// A cursor scrolled out of the visible x range is off the plot: drawing it
|
||||
// would put a marker over the label strip.
|
||||
if (px < layout.axes.x || px >= layout.axes.right()) continue;
|
||||
// The two orientations differ in exactly two things: which coordinate places the
|
||||
// stroke (and which axis maps the span it is drawn along), and where the label is
|
||||
// written. Everything else -- the skip at the far edge of the axes, the highlight, the
|
||||
// text path -- is this one loop's, which is the point of one type rather than two.
|
||||
const bool horizontal = cursor.orientation == CursorOrientation::Horizontal;
|
||||
const f32 at = plot.cursor_screen_coordinate(i);
|
||||
// A cursor at the *far* edge of its own range is off the plot rather than on it: its
|
||||
// stroke straddles that edge by construction, so drawing it would put half a marker
|
||||
// over the label strip beside it. The rule reads the same on both axes -- a marker
|
||||
// scrolled past the right of its x range, a ruler dragged to the bottom of its y range
|
||||
// -- and the axes clip would hold the ink in anyway, so this is about not drawing
|
||||
// something that is a half-stroke and a half-nothing.
|
||||
if (horizontal ? (at < layout.axes.y || at >= layout.axes.bottom())
|
||||
: (at < layout.axes.x || at >= layout.axes.right())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
stage_segment(cursor.x, y_range.min, cursor.x, y_range.max, plot.x_axis(cursor.axis_x).log,
|
||||
log_y);
|
||||
flush_lines(mapping(plot, cursor.axis_x, 0),
|
||||
if (horizontal) {
|
||||
stage_segment(x_range.min, cursor.y, x_range.max, cursor.y, log_x,
|
||||
plot.y_axis(cursor.axis_y).log);
|
||||
} else {
|
||||
stage_segment(cursor.x, y_range.min, cursor.x, y_range.max, plot.x_axis(cursor.axis_x).log,
|
||||
log_y);
|
||||
}
|
||||
flush_lines(horizontal ? mapping(plot, 0, cursor.axis_y) : mapping(plot, cursor.axis_x, 0),
|
||||
on ? style_.cursor : cursor.color,
|
||||
style_.device(on ? style_.handle_width_px : style_.line_width_px));
|
||||
if (!cursor.label.empty()) {
|
||||
stage_text({px + style_.device(3.0f), layout.axes.y + style_.device(2.0f)}, cursor.label.c_str());
|
||||
draw_cursor_label(plot, layout, cursor, at);
|
||||
}
|
||||
}
|
||||
|
||||
void Renderer::draw_cursor_label(const Plot& plot, const PlotLayout& layout, const Cursor& cursor,
|
||||
f32 at) {
|
||||
const f32 gap = style_.device(3.0f);
|
||||
if (cursor.orientation == CursorOrientation::Horizontal) {
|
||||
// A ruler's text is the value it is *for*, written the way the y axis writes its own
|
||||
// tick labels, so a value read off a ruler reads exactly like the tick beside it. The
|
||||
// caller's caption, when there is one, is staged first on the same line, so neither the
|
||||
// caption nor the value has to be dropped -- and both stay one text run's worth of
|
||||
// glyphs, since the pen advances per glyph either way.
|
||||
const Vec2 pen{layout.axes.x + gap, at + style_.device(2.0f)};
|
||||
char value[32];
|
||||
plot.y_axis(cursor.axis_y).format_value(cursor.y, value, sizeof value);
|
||||
if (cursor.label.empty()) {
|
||||
stage_text(pen, value);
|
||||
return;
|
||||
}
|
||||
stage_text(pen, cursor.label.c_str());
|
||||
stage_text({pen.x + font_backend_.measure(cursor.label.c_str()) + gap, pen.y}, value);
|
||||
return;
|
||||
}
|
||||
if (!cursor.label.empty()) {
|
||||
stage_text({at + gap, layout.axes.y + style_.device(2.0f)}, cursor.label.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user