feat(plot): the figure crosshair marks the hovered row, and the stroke is thin, dashed and configurable
This commit is contained in:
1 parent
cae672bc99
commit
eee7c34dd5
12 files changed
+166
-12
No files matched your search
@@ -449,6 +449,22 @@ void Figure::set_crosshair(f64 x, bool active) {
|
||||
for (Plot& row : plots_) row.set_crosshair(x, active);
|
||||
}
|
||||
|
||||
void Figure::set_crosshair(f64 x, u32 hovered_row, f64 y_in_row, bool active) {
|
||||
// The shared x first, on every row -- the same state the single-coordinate overload leaves,
|
||||
// so the vertical lines and every row's readout are placed by one rule whether or not the
|
||||
// pointer's row is known. A bad x is refused by the row call, which changes nothing, so the
|
||||
// y below must not run either or the hover would be half-placed.
|
||||
if (active && !std::isfinite(x)) return;
|
||||
for (Plot& row : plots_) row.set_crosshair(x, active);
|
||||
// Then the pointer row's own y: the *only* row that gets a horizontal line, because the y is
|
||||
// a value on that row's scale and no other row's. A row that does not exist (the pointer is in
|
||||
// the gap between rows) leaves every row vertical-only, which is the honest answer -- there is
|
||||
// no row under the pointer to mark -- and a non-finite y leaves the row vertical-only rather
|
||||
// than clearing a hover the pointer still has.
|
||||
if (!active || hovered_row >= plots_.size()) return;
|
||||
plots_[hovered_row].set_crosshair(x, y_in_row, true);
|
||||
}
|
||||
|
||||
void Figure::clear_crosshair() {
|
||||
for (Plot& row : plots_) row.clear_crosshair();
|
||||
}
|
||||
|
||||
+14
-1
@@ -1265,6 +1265,8 @@ void Plot::build_crosshair() {
|
||||
crosshair.active = crosshair_.active;
|
||||
crosshair.vertical = Rect{};
|
||||
crosshair.horizontal = Rect{};
|
||||
crosshair.stroke = 0.0f;
|
||||
crosshair.dash = Vec2{};
|
||||
crosshair.panel = Rect{};
|
||||
if (!crosshair_.active) {
|
||||
crosshair.rows.clear();
|
||||
@@ -1286,7 +1288,18 @@ void Plot::build_crosshair() {
|
||||
// rule: the box is where the line may be drawn, and Task 1's clip never has to trim one
|
||||
// that already fits. The lines are placed through the primary axis pair, like the grid and
|
||||
// a region's fill -- one crosshair is one column and one row of the data area.
|
||||
const f32 stroke = style_.device(style_.line_width_px);
|
||||
//
|
||||
// The stroke and the dash are resolved *here*, once, from the crosshair's own override where
|
||||
// it has one and the global style otherwise, and converted to device pixels through
|
||||
// `device()`. The boxes below and the renderer's strokes are then one set of numbers rather
|
||||
// than two that must be kept in step (spec §17): `stroke` is the device width, `dash` the
|
||||
// device (on, off) pair with `off <= 0` meaning solid.
|
||||
const CrosshairStyle& cs = crosshair_.style;
|
||||
const f32 stroke = style_.device(cs.width >= 0.0f ? cs.width : style_.crosshair_width);
|
||||
crosshair.stroke = stroke;
|
||||
crosshair.dash =
|
||||
Vec2{style_.device(cs.dash_on >= 0.0f ? cs.dash_on : style_.crosshair_dash_on),
|
||||
style_.device(cs.dash_off >= 0.0f ? cs.dash_off : style_.crosshair_dash_off)};
|
||||
const f32 half = 0.5f * stroke;
|
||||
if (crosshair_.vertical) {
|
||||
const f32 at =
|
||||
|
||||
Reference in new issue
Block a user