diff --git a/xo-imgui/example/ex4/DrawState.cpp b/xo-imgui/example/ex4/DrawState.cpp index b7a839a1..fe835c03 100644 --- a/xo-imgui/example/ex4/DrawState.cpp +++ b/xo-imgui/example/ex4/DrawState.cpp @@ -3,6 +3,7 @@ #include "DrawState.hpp" #include "AnimateGcCopyCb.hpp" #include "GcStatistics.hpp" +#include "xo/imgui/ImScale.hpp" #include "xo/indentlog/scope.hpp" using xo::gc::GcStatisticsHistory; @@ -415,18 +416,27 @@ DrawState::draw_gc_history(const GcStateDescription & gcstate, { scope log(XO_DEBUG(debug_flag)); + if (gc_history.empty()) + return; + float lm = 10; float tm = 25; + float rm = 0; + float bm = 0; + + ImRect chart_rect = bounding_rect.within_margin(ImRect(lm, tm, rm, bm)); /* we're going to make a bar chart */ /* x_scale,y_scale in GC units (i.e. bytes) */ size_t x_scale = gc_history.capacity(); - size_t yplus_scale = 0; - size_t yminus_scale = 0; + /* in bytes. +ve values for bytes survivingK */ + size_t yplus_range = 0; + /* in bytes. -ve values for bytes collected */ + size_t yminus_range = 0; - float display_w = bounding_rect.width() - lm; - float display_h = bounding_rect.height() - tm; + float display_w = chart_rect.width(); + float display_h = chart_rect.height(); /* 1st loop: figure out max y scale */ for (const GcStatisticsHistoryItem & stats : gc_history) { @@ -440,20 +450,34 @@ DrawState::draw_gc_history(const GcStateDescription & gcstate, size_t g1z = stats.garbage1_z_; size_t gNz = stats.garbageN_z_; - if (yplus_scale < sz + pz + psz) - yplus_scale = sz + pz + psz; + if (yplus_range < sz + pz + psz) + yplus_range = sz + pz + psz; - if (yminus_scale < g0z + g1z + gNz) - yminus_scale = g0z + g1z + gNz; + if (yminus_range < g0z + g1z + gNz) + yminus_range = g0z + g1z + gNz; } else { ; } } - /* y-coord of x-axis */ - float y_zero = bounding_rect.y_lo() + tm + (display_h * yplus_scale) / (yplus_scale + yminus_scale); + float y_range = yplus_range + yminus_range; - float y_scale = yplus_scale + yminus_scale; + /* native->screen conversion in y direction */ + ImScale y_scale{ImVec2(0.0 - yminus_range, chart_rect.y_hi()), ImVec2(yplus_range, chart_rect.y_lo())}; + + /* screen y-coord of x-axis */ + float y_zero = y_scale(0); + + if ((y_zero < chart_rect.y_lo()) || (y_zero > chart_rect.y_hi())) { + throw std::runtime_error(tostr("expected y_zero within chart range [y_lo,y_hi]", + xtag("y_lo", chart_rect.y_lo()), + xtag("y_zero", y_zero), + xtag("y_hi", chart_rect.y_hi()))); + + } + + assert(y_zero >= chart_rect.y_lo()); + assert(y_zero <= chart_rect.y_hi()); /* width of 1 bar in screen coords */ constexpr float c_min_bar_w = 5.0; @@ -468,25 +492,25 @@ DrawState::draw_gc_history(const GcStateDescription & gcstate, if ((gen == stats.upto_) || (gen == generation::tenured)) { /* - * ys_lo +--+ - * | | survive_z (survived 1st GC) - * | | - * yp_lo +--+ - * | | promote_z (sruvived 2nd GC) - * | | - * ypsz_lo +--+ - * | | persist_z (survived 3+ GCs) - * | | - * y_zero +--+ - * | | gN (killed on 3+ GC) - * | | - * ygN_hi +--+ - * | | g1 (killed on 2nd GC) - * | | - * yg1_hi +--+ - * | | g0 (killed on 1st GC) - * | | - * yg0_hi +--+ + * ys_span.y +--+ + * | | survive_z (survived 1st GC) + * | | + * yp_span.y +--+ + * | | promote_z (sruvived 2nd GC) + * | | + * ypsz_span.y +--+ + * | | persist_z (survived 3+ GCs) + * | | + * y_zero +--+ ---------------------------------------- x-axis + * | | gN (killed on 3+ GC) + * | | + * ygN_span.x +--+ + * | | g1 (killed on 2nd GC) + * | | + * yg1_span.x +--+ + * | | g0 (killed on 1st GC) + * | | + * yg0_span.x +--+ */ /* x-coordinates of bar */ @@ -494,74 +518,85 @@ DrawState::draw_gc_history(const GcStateDescription & gcstate, float x_hi = x_lo + bar_w - 1; ImVec2 x_span{x_lo, x_hi}; - /* y-coordinates of persist bar (survived 3+ GCs) */ - float ypsz_lo = (y_zero - - (display_h * stats.persist_z_ / y_scale)); + /* screen y-coordinates of persist bar (survived 3+ GCs) */ + ImVec2 ypsz_span = y_scale.map_span(0, stats.persist_z_); + //float ypsz_lo = (y_zero - (display_h * stats.persist_z_ / y_range)); { write_gc_history_bar("##persist", gc_history_headline::persist, stats, - ImRect::from_xy_span(x_span, ImVec2(ypsz_lo, y_zero)), + ImRect::from_xy_span(x_span, ypsz_span), draw_list); } + /* screen y-coordinates of promote bar (survived 2nd GC) */ + ImVec2 yp_span = y_scale.map_span(stats.persist_z_, + stats.persist_z_ + stats.promote_z_); /* y-coordinates of promote bar (survived 2nd GC) */ - float yp_hi = ypsz_lo; - float yp_lo = (yp_hi - - (display_h * stats.promote_z_ / y_scale)); + //float yp_hi = ypsz_lo; + //float yp_lo = (yp_hi - (display_h * stats.promote_z_ / y_range)); { write_gc_history_bar("##promote", gc_history_headline::promote, stats, - ImRect::from_xy_span(x_span, ImVec2(yp_lo, yp_hi)), + ImRect::from_xy_span(x_span, yp_span), draw_list); } + /* screen y-coordinates of surive bar (survived 1st GC) */ + ImVec2 ys_span = y_scale.map_span(stats.persist_z_ + stats.promote_z_, + stats.persist_z_ + stats.promote_z_ + stats.survive_z_); /* y-coordinates of survivor bar (survived 1st GC) */ - float ys_hi = yp_lo; - float ys_lo = (ys_hi - (display_h * stats.survive_z_ / y_scale)); + //float ys_hi = yp_lo; + //float ys_lo = (ys_hi - (display_h * stats.survive_z_ / y_range)); { write_gc_history_bar("##survivor", gc_history_headline::survive, stats, - ImRect::from_xy_span(x_span, ImVec2(ys_lo, ys_hi)), + ImRect::from_xy_span(x_span, ys_span), draw_list); } // ----------------------------------------------------------- + /* screen y-coordinates of garbageN bar (killed on 3+ GC) */ + ImVec2 ygN_span = y_scale.map_span(0.0 - stats.garbageN_z_, 0.0); /* y-coordinates of garbageN bar (killed on 3+ GC) */ - float ygN_lo = y_zero; - float ygN_hi = (y_zero - + (display_h * stats.garbageN_z_ / y_scale)); + //float ygN_lo = y_zero; + //float ygN_hi = (y_zero + (display_h * stats.garbageN_z_ / y_range)); { write_gc_history_bar("##garbageN", gc_history_headline::garbageN, stats, - ImRect::from_xy_span(x_span, ImVec2(ygN_lo, ygN_hi)), + ImRect::from_xy_span(x_span, ygN_span), draw_list); } /* y-coordinates of garbage1 bar (killed on 2nd GC) */ - float yg1_lo = ygN_hi; - float yg1_hi = (yg1_lo - + (display_h * stats.garbage1_z_ / y_scale)); + ImVec2 yg1_span = y_scale.map_span(0.0 - stats.garbageN_z_ - stats.garbage1_z_, + 0.0 - stats.garbageN_z_); + /* y-coordinates of garbage1 bar (killed on 2nd GC) */ + //float yg1_lo = ygN_hi; + //float yg1_hi = (yg1_lo + (display_h * stats.garbage1_z_ / y_range)); { write_gc_history_bar("##garbage1", gc_history_headline::garbage1, stats, - ImRect(ImVec2(x_lo, yg1_lo), ImVec2(x_hi, yg1_hi)), + ImRect::from_xy_span(x_span, yg1_span), draw_list); } /* y-coordinates of garbage0 bar (killed on 1st GC) */ - float yg0_lo = yg1_hi; - float yg0_hi = (yg0_lo - + (display_h * stats.garbage0_z_ / y_scale)); + ImVec2 yg0_span = y_scale.map_span(0.0 - stats.garbageN_z_ - stats.garbage1_z_ - stats.garbage0_z_, + 0.0 - stats.garbageN_z_ - stats.garbage1_z_); + + /* y-coordinates of garbage0 bar (killed on 1st GC) */ + //float yg0_lo = yg1_hi; + //float yg0_hi = (yg0_lo + (display_h * stats.garbage0_z_ / y_range)); { write_gc_history_bar("##garbage0", gc_history_headline::garbage0, stats, - ImRect(ImVec2(x_lo, yg0_lo), ImVec2(x_hi, yg0_hi)), + ImRect::from_xy_span(x_span, yg0_span), draw_list); } } else { diff --git a/xo-imgui/include/xo/imgui/ImScale.hpp b/xo-imgui/include/xo/imgui/ImScale.hpp new file mode 100644 index 00000000..98e8fb2e --- /dev/null +++ b/xo-imgui/include/xo/imgui/ImScale.hpp @@ -0,0 +1,44 @@ +/* ImScale.hpp */ + +#pragma once + +#include "imgui.h" + +/** Linear scale for transforming between two coordinate systems. + * f(x) = m.(x - x0) + y0 + * inv(f)(y) = x0 + (y - y0) / m + **/ +class ImScale { +public: + ImScale(ImVec2 ref, float m) : ref_{ref}, scale_{m} {} + /** derive scale f from two reference points @p ref0 and @p ref1. + * Promise: + * @pre + * f(ref0.x) -> ref0.y + * f(ref1.x) -> ref1.y + * @endpre + **/ + ImScale(ImVec2 ref0, ImVec2 ref1) : ref_{ref0}, scale_{(ref1.y - ref0.y) / (ref1.x - ref0.x)} {} + + ImVec2 ref() const { return ref_; } + float scale() const { return scale_; } + + /** evaluate scale f(x) at @p x **/ + constexpr float at(float x) const { return scale_ * (x - ref_.x) + ref_.y; } + /** evaluate inverse inv(f)(y) at @p y **/ + constexpr float inverse_at(float y) const { return 1.0 / scale_ * (y - ref_.y) + ref_.x; } + + /** construct inverse function inv(f) **/ + ImScale inverse() const { return ImScale(ImVec2(ref_.y, ref_.x), 1.0 / scale_); } + + ImVec2 map_span(ImVec2 x_span) const { return ImVec2(at(x_span.x), at(x_span.y)); } + ImVec2 map_span(float x1, float x2) const { return map_span(ImVec2(x1, x2)); } + + constexpr float operator()(float x) const { return at(x); } + +private: + /** fixed point {x0, f(x0)} **/ + ImVec2 ref_ = ImVec2{0.0, 0.0}; + /** scale. f(x) - f(x0) = m * (x - x0) **/ + float scale_ = 1.0; +};