figure: cells of a row can share one y range
This commit is contained in:
1 parent
73f610b136
commit
25c3381551
6 files changed
+214
-7
No files matched your search
@@ -434,6 +434,80 @@ bool Figure::adopt_member_changes(XLink& group) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void Figure::set_share_row_y(bool on) {
|
||||
share_row_y_ = on;
|
||||
row_y_.clear();
|
||||
for (u32 row = 0; row < cells_per_row_.size(); ++row) {
|
||||
if (cells_per_row_[row] < 2u) continue;
|
||||
for (u32 i = row_first_[row]; i < row_first_[row] + cells_per_row_[row]; ++i) {
|
||||
plots_[i].set_y_link_member(0u, on);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Figure::row_y_auto(u32 row) const noexcept {
|
||||
return row < row_y_.size() && row_y_[row].have ? row_y_[row].auto_scale : true;
|
||||
}
|
||||
|
||||
void Figure::sync_row_y() {
|
||||
if (!share_row_y_) return;
|
||||
row_y_.resize(cells_per_row_.size());
|
||||
for (u32 row = 0; row < cells_per_row_.size(); ++row) {
|
||||
const u32 n = cells_per_row_[row];
|
||||
if (n < 2u) continue;
|
||||
RowY& g = row_y_[row];
|
||||
const u32 first = row_first_[row];
|
||||
// Axes added after set_share_row_y() are covered here.
|
||||
for (u32 i = first; i < first + n; ++i) plots_[i].set_y_link_member(0u, true);
|
||||
if (!g.have) {
|
||||
g.pushed = plots_[first].y_axis(0u).range;
|
||||
g.auto_scale = plots_[first].y_axis(0u).auto_scale;
|
||||
}
|
||||
// What happened since the last push. A member's auto flag that differs from the group's
|
||||
// is a request (set_y_range clears it, auto_fit_y / set_y_auto_scale(true) sets it);
|
||||
// otherwise a member whose range differs from the pushed one was moved by a gesture.
|
||||
bool want_auto = g.auto_scale;
|
||||
Range target = g.pushed;
|
||||
bool flag_request = false;
|
||||
for (u32 i = first; i < first + n && !flag_request; ++i) {
|
||||
const Axis& a = plots_[i].y_axis(0u);
|
||||
if (a.auto_scale != g.auto_scale) {
|
||||
want_auto = a.auto_scale;
|
||||
if (!want_auto) target = a.range; // auto -> manual: the gesture's range
|
||||
flag_request = true;
|
||||
}
|
||||
}
|
||||
if (!flag_request && !want_auto) {
|
||||
for (u32 i = first; i < first + n; ++i) {
|
||||
const Range r = plots_[i].y_axis(0u).range;
|
||||
if (r.min != g.pushed.min || r.max != g.pushed.max) {
|
||||
target = r;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (want_auto) {
|
||||
Range bounds{};
|
||||
bool any = false;
|
||||
for (u32 i = first; i < first + n; ++i) {
|
||||
Range b{};
|
||||
if (!plots_[i].y_data_bounds(0u, b)) continue;
|
||||
bounds = any ? Range{std::min(bounds.min, b.min), std::max(bounds.max, b.max)} : b;
|
||||
any = true;
|
||||
}
|
||||
if (any && plots_[first].fit_y_bounds(0u, bounds)) target = plots_[first].y_axis(0u).range;
|
||||
// else: nothing to fit -- keep the range the group had
|
||||
}
|
||||
for (u32 i = first; i < first + n; ++i) {
|
||||
plots_[i].set_y_range(0u, target); // clears the flag ...
|
||||
plots_[i].set_y_auto_scale(0u, want_auto); // ... so restate it: every member mirrors the group
|
||||
}
|
||||
g.pushed = target;
|
||||
g.auto_scale = want_auto;
|
||||
g.have = true;
|
||||
}
|
||||
}
|
||||
|
||||
void Figure::sync_x_links() {
|
||||
for (XLink& group : links_) {
|
||||
if (group.rows.empty()) continue;
|
||||
@@ -581,6 +655,8 @@ const FigureLayout& Figure::layout() {
|
||||
// its transforms describe this frame's rects; and before the rows lay themselves
|
||||
// out below, so a following group's window is the range they lay out with.
|
||||
sync_x_links();
|
||||
// Before any `Plot::layout()`, where a plot would autoscale its own y axis.
|
||||
sync_row_y();
|
||||
// Every row is laid out against its own rect, so after this call each row's
|
||||
// chrome is current: `plot(i).layout()` reads back what the figure computed rather
|
||||
// than what the row last held. That is the contract `Renderer::draw(Figure&)`
|
||||
|
||||
+15
-1
@@ -819,6 +819,19 @@ void Plot::set_x_link_member(u32 axis, bool linked) noexcept {
|
||||
x_axes_[axis].link_member = linked;
|
||||
}
|
||||
|
||||
void Plot::set_y_link_member(u32 axis, bool linked) noexcept {
|
||||
if (axis >= y_axes_.size()) return;
|
||||
y_axes_[axis].link_member = linked;
|
||||
}
|
||||
|
||||
bool Plot::y_data_bounds(u32 axis, Range& out) const {
|
||||
return trace_bounds_union(/*x_side=*/false, axis, out);
|
||||
}
|
||||
|
||||
bool Plot::fit_y_bounds(u32 axis, Range bounds) {
|
||||
return axis < y_axes_.size() && fit_axis_range(y_axes_[axis], bounds);
|
||||
}
|
||||
|
||||
XFollow Plot::x_follow(u32 axis) const {
|
||||
return axis < x_axes_.size() ? x_axes_[axis].follow : XFollow::Off;
|
||||
}
|
||||
@@ -1001,7 +1014,8 @@ void Plot::autoscale_axes() {
|
||||
if (x_axes_[i].auto_scale && !follows_live(i)) fit_x_axis(i);
|
||||
}
|
||||
for (u32 i = 0; i < y_axes_.size(); ++i) {
|
||||
if (y_axes_[i].auto_scale) fit_y_axis(i);
|
||||
// A shared-y member is fitted by its figure, against every cell's traces.
|
||||
if (y_axes_[i].auto_scale && !y_axes_[i].link_member) fit_y_axis(i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user