From b465034bc34a94a92bd0287abd98209a9e8621c1 Mon Sep 17 00:00:00 2001 From: Kolan Sh Date: Tue, 28 Nov 2017 21:41:25 +0300 Subject: [PATCH 1/7] Several cursors methods added. Make rel_zoom_{x,y}_m{in,ax} readonly properties. --- src/Chart.vala | 317 +++++++++++++++++++++++++++++++++++--------- src/Point.vala | 2 +- src/Series.vala | 4 +- test/ChartTest.vala | 110 ++++++++++++--- 4 files changed, 352 insertions(+), 81 deletions(-) diff --git a/src/Chart.vala b/src/Chart.vala index 909846f..51d62c3 100644 --- a/src/Chart.vala +++ b/src/Chart.vala @@ -87,17 +87,21 @@ namespace Gtk.CairoChart { } } - double rel_zoom_x_min = 0.0; - double rel_zoom_x_max = 1.0; - double rel_zoom_y_min = 0.0; - double rel_zoom_y_max = 1.0; + double _rel_zoom_x_min = 0.0; + double _rel_zoom_x_max = 1.0; + double _rel_zoom_y_min = 0.0; + double _rel_zoom_y_max = 1.0; + public double rel_zoom_x_min { get { return _rel_zoom_x_min; } default = 0.0; } + public double rel_zoom_x_max { get { return _rel_zoom_x_max; } default = 1.0; } + public double rel_zoom_y_min { get { return _rel_zoom_y_min; } default = 0.0; } + public double rel_zoom_y_max { get { return _rel_zoom_y_max; } default = 1.0; } int zoom_first_show = 0; public virtual void zoom_in (double x0, double y0, double x1, double y1) { for (var si = 0, max_i = series.length; si < max_i; ++si) { var s = series[si]; - if (!s.zoom_show) continue; + if (!s.show) continue; var real_x0 = get_real_x (s, x0); var real_x1 = get_real_x (s, x1); var real_y0 = get_real_y (s, y0); @@ -105,7 +109,7 @@ namespace Gtk.CairoChart { // if selected square does not intersect with the series's square if ( real_x1 <= s.axis_x.zoom_min || real_x0 >= s.axis_x.zoom_max || real_y0 <= s.axis_y.zoom_min || real_y1 >= s.axis_y.zoom_max) { - s.zoom_show = false; + s.show = false; continue; } if (real_x0 >= s.axis_x.zoom_min) { @@ -136,24 +140,24 @@ namespace Gtk.CairoChart { zoom_first_show = 0; for (var si = 0, max_i = series.length; si < max_i; ++si) - if (series[si].zoom_show) { + if (series[si].show) { zoom_first_show = si; break; } - var new_rel_zoom_x_min = rel_zoom_x_min + (x0 - plot_area_x_min) / (plot_area_x_max - plot_area_x_min) * (rel_zoom_x_max - rel_zoom_x_min); - var new_rel_zoom_x_max = rel_zoom_x_min + (x1 - plot_area_x_min) / (plot_area_x_max - plot_area_x_min) * (rel_zoom_x_max - rel_zoom_x_min); - var new_rel_zoom_y_min = rel_zoom_y_min + (y0 - plot_area_y_min) / (plot_area_y_max - plot_area_y_min) * (rel_zoom_y_max - rel_zoom_y_min); - var new_rel_zoom_y_max = rel_zoom_y_min + (y1 - plot_area_y_min) / (plot_area_y_max - plot_area_y_min) * (rel_zoom_y_max - rel_zoom_y_min); - rel_zoom_x_min = new_rel_zoom_x_min; - rel_zoom_x_max = new_rel_zoom_x_max; - rel_zoom_y_min = new_rel_zoom_y_min; - rel_zoom_y_max = new_rel_zoom_y_max; + var new_rel_zoom_x_min = _rel_zoom_x_min + (x0 - plot_area_x_min) / (plot_area_x_max - plot_area_x_min) * (_rel_zoom_x_max - _rel_zoom_x_min); + var new_rel_zoom_x_max = _rel_zoom_x_min + (x1 - plot_area_x_min) / (plot_area_x_max - plot_area_x_min) * (_rel_zoom_x_max - _rel_zoom_x_min); + var new_rel_zoom_y_min = _rel_zoom_y_min + (y0 - plot_area_y_min) / (plot_area_y_max - plot_area_y_min) * (_rel_zoom_y_max - _rel_zoom_y_min); + var new_rel_zoom_y_max = _rel_zoom_y_min + (y1 - plot_area_y_min) / (plot_area_y_max - plot_area_y_min) * (_rel_zoom_y_max - _rel_zoom_y_min); + _rel_zoom_x_min = new_rel_zoom_x_min; + _rel_zoom_x_max = new_rel_zoom_x_max; + _rel_zoom_y_min = new_rel_zoom_y_min; + _rel_zoom_y_max = new_rel_zoom_y_max; } public virtual void zoom_out () { foreach (var s in series) { - s.zoom_show = true; + s.show = true; s.axis_x.zoom_min = s.axis_x.min; s.axis_x.zoom_max = s.axis_x.max; s.axis_y.zoom_min = s.axis_y.min; @@ -163,10 +167,10 @@ namespace Gtk.CairoChart { s.place.zoom_y_low = s.place.y_low; s.place.zoom_y_high = s.place.y_high; } - rel_zoom_x_min = 0; - rel_zoom_x_max = 1; - rel_zoom_y_min = 0; - rel_zoom_y_max = 1; + _rel_zoom_x_min = 0; + _rel_zoom_x_max = 1; + _rel_zoom_y_min = 0; + _rel_zoom_y_max = 1; zoom_first_show = 0; } @@ -174,7 +178,7 @@ namespace Gtk.CairoChart { public virtual void move (double delta_x, double delta_y) { delta_x /= plot_area_x_max - plot_area_x_min; delta_x *= - 1.0; delta_y /= plot_area_y_max - plot_area_y_min; delta_y *= - 1.0; - var rzxmin = rel_zoom_x_min, rzxmax = rel_zoom_x_max, rzymin = rel_zoom_y_min, rzymax = rel_zoom_y_max; + var rzxmin = _rel_zoom_x_min, rzxmax = _rel_zoom_x_max, rzymin = _rel_zoom_y_min, rzymax = _rel_zoom_y_max; zoom_out(); //draw(); // TODO: optimize here delta_x *= plot_area_x_max - plot_area_x_min; @@ -355,7 +359,7 @@ namespace Gtk.CairoChart { foreach (var s in series) { - if (!s.zoom_show) continue; + if (!s.show) continue; // carry switch (legend.position) { @@ -554,7 +558,7 @@ namespace Gtk.CairoChart { int nzoom_series_show = 0; for (var si = series.length - 1; si >=0; --si) { var s = series[si]; - if (!s.zoom_show) continue; + if (!s.show) continue; ++nzoom_series_show; if ( s.axis_x.position != series[0].axis_x.position || s.axis_x.zoom_min != series[0].axis_x.zoom_min @@ -575,7 +579,7 @@ namespace Gtk.CairoChart { // Join and calc X-axes for (var si = series.length - 1, nskip = 0; si >=0; --si) { var s = series[si]; - if (!s.zoom_show) continue; + if (!s.show) continue; if (nskip != 0) {--nskip; continue;} double max_rec_width = 0; double max_rec_height = 0; calc_axis_rec_sizes (s.axis_x, out max_rec_width, out max_rec_height, true); @@ -585,11 +589,11 @@ namespace Gtk.CairoChart { // join relative x-axes with non-intersect places for (int sj = si - 1; sj >= 0; --sj) { var s2 = series[sj]; - if (!s2.zoom_show) continue; + if (!s2.show) continue; bool has_intersection = false; for (int sk = si; sk > sj; --sk) { var s3 = series[sk]; - if (!s3.zoom_show) continue; + if (!s3.show) continue; if (are_intersect(s2.place.zoom_x_low, s2.place.zoom_x_high, s3.place.zoom_x_low, s3.place.zoom_x_high) || s2.axis_x.position != s3.axis_x.position || s2.axis_x.type != s3.axis_x.type) { @@ -623,7 +627,7 @@ namespace Gtk.CairoChart { // Join and calc Y-axes for (var si = series.length - 1, nskip = 0; si >=0; --si) { var s = series[si]; - if (!s.zoom_show) continue; + if (!s.show) continue; if (nskip != 0) {--nskip; continue;} double max_rec_width = 0; double max_rec_height = 0; calc_axis_rec_sizes (s.axis_y, out max_rec_width, out max_rec_height, false); @@ -633,11 +637,11 @@ namespace Gtk.CairoChart { // join relative x-axes with non-intersect places for (int sj = si - 1; sj >= 0; --sj) { var s2 = series[sj]; - if (!s2.zoom_show) continue; + if (!s2.show) continue; bool has_intersection = false; for (int sk = si; sk > sj; --sk) { var s3 = series[sk]; - if (!s3.zoom_show) continue; + if (!s3.show) continue; if (are_intersect(s2.place.zoom_y_low, s2.place.zoom_y_high, s3.place.zoom_y_low, s3.place.zoom_y_high) || s2.axis_y.position != s3.axis_y.position || s2.axis_x.type != s3.axis_x.type) { @@ -678,7 +682,7 @@ namespace Gtk.CairoChart { protected virtual void draw_horizontal_axis () { for (var si = series.length - 1, nskip = 0; si >=0; --si) { var s = series[si]; - if (!s.zoom_show) continue; + if (!s.show) continue; if (common_x_axes && si != zoom_first_show) continue; // 1. Detect max record width/height by axis_rec_npoints equally selected points using format. double max_rec_width, max_rec_height; @@ -829,11 +833,11 @@ namespace Gtk.CairoChart { // join relative x-axes with non-intersect places for (int sj = si - 1; sj >= 0; --sj) { var s2 = series[sj]; - if (!s2.zoom_show) continue; + if (!s2.show) continue; bool has_intersection = false; for (int sk = si; sk > sj; --sk) { var s3 = series[sk]; - if (!s3.zoom_show) continue; + if (!s3.show) continue; if (are_intersect(s2.place.zoom_x_low, s2.place.zoom_x_high, s3.place.zoom_x_low, s3.place.zoom_x_high) || s2.axis_x.position != s3.axis_x.position || s2.axis_x.type != s3.axis_x.type) { @@ -869,7 +873,7 @@ namespace Gtk.CairoChart { protected virtual void draw_vertical_axis () { for (var si = series.length - 1, nskip = 0; si >=0; --si) { var s = series[si]; - if (!s.zoom_show) continue; + if (!s.show) continue; if (common_y_axes && si != zoom_first_show) continue; // 1. Detect max record width/height by axis_rec_npoints equally selected points using format. double max_rec_width, max_rec_height; @@ -972,11 +976,11 @@ namespace Gtk.CairoChart { // join relative x-axes with non-intersect places for (int sj = si - 1; sj >= 0; --sj) { var s2 = series[sj]; - if (!s2.zoom_show) continue; + if (!s2.show) continue; bool has_intersection = false; for (int sk = si; sk > sj; --sk) { var s3 = series[sk]; - if (!s3.zoom_show) continue; + if (!s3.show) continue; if (are_intersect(s2.place.zoom_y_low, s2.place.zoom_y_high, s3.place.zoom_y_low, s3.place.zoom_y_high) || s2.axis_y.position != s3.axis_y.position) { has_intersection = true; @@ -1027,16 +1031,21 @@ namespace Gtk.CairoChart { return plot_area_y_max - (plot_area_y_max - plot_area_y_min) * (s.place.zoom_y_low + (y - s.axis_y.zoom_min) / (s.axis_y.zoom_max - s.axis_y.zoom_min) * (s.place.zoom_y_high - s.place.zoom_y_low)); } + protected virtual Point get_scr_point (Series s, Point p) { + return Point (get_scr_x(s, p.x), get_scr_y(s, p.y)); + } protected virtual Float128 get_real_x (Series s, double scr_x) { return s.axis_x.zoom_min + ((scr_x - plot_area_x_min) / (plot_area_x_max - plot_area_x_min) - s.place.zoom_x_low) * (s.axis_x.zoom_max - s.axis_x.zoom_min) / (s.place.zoom_x_high - s.place.zoom_x_low); } - protected virtual Float128 get_real_y (Series s, double scr_y) { return s.axis_y.zoom_min + ((plot_area_y_max - scr_y) / (plot_area_y_max - plot_area_y_min) - s.place.zoom_y_low) * (s.axis_y.zoom_max - s.axis_y.zoom_min) / (s.place.zoom_y_high - s.place.zoom_y_low); } + protected virtual Point get_real_point (Series s, Point p) { + return Point (get_real_x(s, p.x), get_real_y(s, p.y)); + } protected virtual bool point_in_rect (Point p, double x0, double x1, double y0, double y1) { if ( (x0 <= p.x <= x1 || x1 <= p.x <= x0) @@ -1072,7 +1081,7 @@ namespace Gtk.CairoChart { } delegate int PointComparator(Point a, Point b); - void sort_points(Point[] points, PointComparator compare) { + void sort_points_delegate(Point[] points, PointComparator compare) { for(var i = 0; i < points.length; ++i) { for(var j = i + 1; j < points.length; ++j) { if(compare(points[i], points[j]) > 0) { @@ -1121,28 +1130,33 @@ namespace Gtk.CairoChart { return false; } + protected virtual Point[] sort_points (Series s) { + var points = s.points.copy(); + switch(s.sort) { + case Series.Sort.BY_X: + sort_points_delegate(points, (a, b) => { + if (a.x < b.x) return -1; + if (a.x > b.x) return 1; + return 0; + }); + break; + case Series.Sort.BY_Y: + sort_points_delegate(points, (a, b) => { + if (a.y < b.y) return -1; + if (a.y > b.y) return 1; + return 0; + }); + break; + } + return points; + } + protected virtual void draw_series () { for (var si = 0; si < series.length; ++si) { var s = series[si]; - if (!s.zoom_show) continue; + if (!s.show) continue; if (s.points.length == 0) continue; - var points = s.points.copy(); - switch(s.sort) { - case Series.Sort.BY_X: - sort_points(points, (a, b) => { - if (a.x < b.x) return -1; - if (a.x > b.x) return 1; - return 0; - }); - break; - case Series.Sort.BY_Y: - sort_points(points, (a, b) => { - if (a.y < b.y) return -1; - if (a.y > b.y) return 1; - return 0; - }); - break; - } + var points = sort_points(s); set_line_style(s.line_style); // draw series line for (int i = 1; i < points.length; ++i) { @@ -1164,8 +1178,193 @@ namespace Gtk.CairoChart { } } + protected List cursors = new List (); + protected Point active_cursor = Point (); + protected bool is_cursor_active = false; + + public virtual void set_active_cursor (double x, double y, bool remove = false) { + active_cursor = Point (scr2rel_x(x), scr2rel_y(y)); + is_cursor_active = true; + } + + public virtual void add_active_cursor () { + cursors.append (active_cursor); + is_cursor_active = false; + } + + public enum CursorOrientation { + VERTICAL = 0, // default + HORIZONTAL + } + + public CursorOrientation cursors_orientation = CursorOrientation.VERTICAL; + + public double cursor_max_distance = 32; + + public virtual void remove_active_cursor () { + if (cursors.length() == 0) return; + var distance = width * width; + uint rm_indx = 0; + uint i = 0; + foreach (var c in cursors) { + double d = distance; + switch (cursors_orientation) { + case CursorOrientation.VERTICAL: + d = (c.x - active_cursor.x).abs(); + break; + case CursorOrientation.HORIZONTAL: + d = (c.y - active_cursor.y).abs(); + break; + default: + break; + } + if (distance > d) { + distance = d; + rm_indx = i; + } + ++i; + } + if (distance < cursor_max_distance) + cursors.delete_link(cursors.nth(rm_indx)); + } + + // TODO: + protected virtual bool get_cursor_limits (Series s, Point cursor, out Point low, out Point high) { + var points = sort_points (s); + bool ret = false; + low.x = plot_area_x_min; + low.y = plot_area_y_min; + high.x = plot_area_x_max; + high.y = plot_area_y_max; + for (var i = 0; i + 1 < points.length; ++i) { + switch (cursors_orientation) { + case CursorOrientation.VERTICAL: + Float128 y = 0.0; + if (vcross(get_scr_point(s, s.points[i]), get_scr_point(s, s.points[i+1]), cursor.x, plot_area_y_min, plot_area_y_max, out y)) { + if (y < low.y) low.y = y; + if (y > high.y) high.y = y; + ret = true; + } + break; + case CursorOrientation.HORIZONTAL: + Float128 x = 0.0; + if (hcross(get_scr_point(s, s.points[i]), get_scr_point(s, s.points[i+1]), cursor.y, plot_area_x_min, plot_area_x_max, out x)) { + if (x < low.x) low.x = x; + if (x > high.x) high.x = x; + ret = true; + } + break; + } + } + + if (common_x_axes) { + switch (s.axis_x.position) { + case Axis.Position.LOW: low.y = plot_area_y_max + s.axis_x.font_indent; break; + case Axis.Position.HIGH: high.y = plot_area_y_min - s.axis_x.font_indent; break; + case Axis.Position.BOTH: + low.y = plot_area_y_max + s.axis_x.font_indent; + high.y = plot_area_y_min - s.axis_x.font_indent; + break; + } + } + if (common_y_axes) { + switch (s.axis_y.position) { + case Axis.Position.LOW: low.x = plot_area_x_min - s.axis_y.font_indent; break; + case Axis.Position.HIGH: high.x = plot_area_x_max + s.axis_y.font_indent; break; + case Axis.Position.BOTH: + low.x = plot_area_x_min - s.axis_y.font_indent; + high.x = plot_area_x_max + s.axis_y.font_indent; + break; + } + } + + return false; + } + + protected virtual Float128 scr2rel_x (Float128 x) { + return (x - plot_area_x_min) / (plot_area_x_max - plot_area_x_min); + } + protected virtual Float128 scr2rel_y (Float128 y) { + return (y - plot_area_y_min) / (plot_area_y_max - plot_area_y_min); + } + protected virtual Point scr2rel_point (Point p) { + return Point (scr2rel_x(p.x), scr2rel_y(p.y)); + } + + protected virtual Float128 rel2scr_x(Float128 x) { + return plot_area_x_min + (plot_area_x_max - plot_area_x_min) * x; + } + + protected virtual Float128 rel2scr_y(Float128 y) { + return plot_area_y_max - (plot_area_y_max - plot_area_y_min) * y; + } + + protected virtual Point rel2scr_point (Point p) { + return Point (rel2scr_x(p.x), rel2scr_y(p.y)); + } + + public LineStyle cursor_line_style = LineStyle(); + // TODO: protected virtual void draw_cursors () { + if (series.length == 0) return; + + var all_cursors = cursors.copy(); + all_cursors.append(active_cursor); + + foreach (var c in all_cursors) { + switch (cursors_orientation) { + case CursorOrientation.VERTICAL: + if (c.x <= rel_zoom_x_min || c.x >= rel_zoom_x_max) continue; break; + case CursorOrientation.HORIZONTAL: + if (c.y <= rel_zoom_y_min || c.y >= rel_zoom_y_max) continue; break; + } + + var low = Point(plot_area_x_max, plot_area_y_min); // low and high + var high = Point(plot_area_x_min, plot_area_y_max); // points of the cursor + foreach (var s in series) { + var l = Point(), h = Point(); + if (get_cursor_limits (s, c, out l, out h)) { + if (l.x < low.x) low.x = l.x; + if (l.y < low.y) low.y = l.y; + if (h.x > high.x) high.x = h.x; + if (h.y > high.y) high.y = h.y; + } + } + + switch (cursors_orientation) { + case CursorOrientation.VERTICAL: + // TODO: draw cursor line + set_line_style(cursor_line_style); + context.move_to (rel2scr_x(c.x), low.y); + context.line_to (rel2scr_x(c.x), high.y); + context.stroke(); + + // TODO: show values + if (common_x_axes) + // TODO: show only Y value + ; + else + // TODO: show [X;Y] + ; + break; + case CursorOrientation.HORIZONTAL: + // TODO: draw cursor line + set_line_style(cursor_line_style); + context.move_to (low.x, rel2scr_y(c.y)); + context.line_to (high.x, rel2scr_y(c.y)); + context.stroke(); + + // TODO: show values + if (common_y_axes) + // TODO: show only X value + ; + else + // TODO: show [X;Y] + ; + break; + } + } } public Chart copy () { @@ -1193,10 +1392,10 @@ namespace Gtk.CairoChart { chart.plot_area_x_min = this.plot_area_x_min; chart.plot_area_y_max = this.plot_area_y_max; chart.plot_area_y_min = this.plot_area_y_min; - chart.rel_zoom_x_min = this.rel_zoom_x_min; - chart.rel_zoom_x_max = this.rel_zoom_x_max; - chart.rel_zoom_y_min = this.rel_zoom_y_min; - chart.rel_zoom_y_max = this.rel_zoom_y_max; + chart._rel_zoom_x_min = this._rel_zoom_x_min; + chart._rel_zoom_x_max = this._rel_zoom_x_max; + chart._rel_zoom_y_min = this._rel_zoom_y_min; + chart._rel_zoom_y_max = this._rel_zoom_y_max; chart.selection_style = this.selection_style; chart.show_legend = this.show_legend; chart.title = this.title.copy().copy(); diff --git a/src/Point.vala b/src/Point.vala index 8710a75..30bee7b 100644 --- a/src/Point.vala +++ b/src/Point.vala @@ -3,7 +3,7 @@ namespace Gtk.CairoChart { Float128 x; Float128 y; - public Point (Float128 x, Float128 y) { + public Point (Float128 x = 0.0, Float128 y = 0.0) { this.x = x; this.y = y; } } diff --git a/src/Series.vala b/src/Series.vala index 2d4bc89..d4c37ad 100644 --- a/src/Series.vala +++ b/src/Series.vala @@ -49,7 +49,7 @@ namespace Gtk.CairoChart { default = Color (0.0, 0.0, 0.0, 1.0); } - public bool zoom_show = true; + public bool show = true; public Series copy () { var series = new Series (); @@ -63,7 +63,7 @@ namespace Gtk.CairoChart { series.points = this.points.copy(); series.sort = this.sort; series.title = this.title.copy(); - series.zoom_show = this.zoom_show; + series.show = this.show; return series; } diff --git a/test/ChartTest.vala b/test/ChartTest.vala index 11c88a7..0008949 100644 --- a/test/ChartTest.vala +++ b/test/ChartTest.vala @@ -203,6 +203,13 @@ bool point_in_chart (Chart chart, double x, double y) { return true; } +enum MouseState { + FREE = 0, // default + DRAW_SELECTION, + MOVING_CHART, + CURSOR_SELECTION +} + int main (string[] args) { init (ref args); @@ -346,9 +353,40 @@ int main (string[] args) { } }); - bool draw_selection = false; + +/* var radio_button5 = new RadioButton.with_label (null, "Labels"); + var radio_button6 = new RadioButton.with_label_from_widget (radio_button5, "Cursors"); + radio_button5.toggled.connect ((button) => { + // TODO: set labels + if (button.get_active()) { + da.queue_draw_area(0, 0, da.get_allocated_width(), da.get_allocated_height()); + } + }); + radio_button6.toggled.connect ((button) => { + // TODO: set cursors + if (button.get_active()) { + da.queue_draw_area(0, 0, da.get_allocated_width(), da.get_allocated_height()); + } + });*/ + + var radio_button7 = new RadioButton.with_label (null, "Vertical Cursors"); + var radio_button8 = new RadioButton.with_label_from_widget (radio_button7, "Horizontal Cursors"); + radio_button7.toggled.connect ((button) => { + if (button.get_active()) { + chart.cursors_orientation = Chart.CursorOrientation.VERTICAL; + da.queue_draw_area(0, 0, da.get_allocated_width(), da.get_allocated_height()); + } + }); + radio_button8.toggled.connect ((button) => { + if (button.get_active()) { + chart.cursors_orientation = Chart.CursorOrientation.HORIZONTAL; + da.queue_draw_area(0, 0, da.get_allocated_width(), da.get_allocated_height()); + } + }); + + MouseState mouse_state = MouseState.FREE; + double sel_x0 = 0, sel_x1 = 0, sel_y0 = 0, sel_y1 = 0; - bool moving_chart = false; double mov_x0 = 0, mov_y0 = 0; da.draw.connect((context) => { @@ -363,7 +401,7 @@ int main (string[] args) { /*var ret = */chart.draw(); // user's post draw operations here... - if (draw_selection) { + if (mouse_state == MouseState.DRAW_SELECTION) { context.set_source_rgba (0.5, 0.5, 0.5, 0.7); context.set_line_join(Cairo.LineJoin.MITER); context.set_line_cap(Cairo.LineCap.ROUND); @@ -381,18 +419,30 @@ int main (string[] args) { da.button_press_event.connect((event) => { if (!point_in_chart(chart, event.x, event.y)) return true; - if (event.button == 2) { - draw_selection = true; + switch (event.button) { + case 1: // start cursor position selection + if ((event.state & Gdk.ModifierType.SHIFT_MASK) != 0) { // remove cursor + chart.set_active_cursor (event.x, event.y, true); + } else { // add cursor + chart.set_active_cursor (event.x, event.y); + } + da.queue_draw_area(0, 0, da.get_allocated_width(), da.get_allocated_height()); + mouse_state = MouseState.CURSOR_SELECTION; + break; + + case 2: // start zoom area selection sel_x0 = sel_x1 = event.x; sel_y0 = sel_y1 = event.y; da.queue_draw_area(0, 0, da.get_allocated_width(), da.get_allocated_height()); - } + mouse_state = MouseState.DRAW_SELECTION; + break; - if (event.button == 3) { - moving_chart = true; + case 3: // start moving mov_x0 = event.x; mov_y0 = event.y; da.queue_draw_area(0, 0, da.get_allocated_width(), da.get_allocated_height()); + mouse_state = MouseState.MOVING_CHART; + break; } return true; // return ret; @@ -401,9 +451,20 @@ int main (string[] args) { if (!point_in_chart(chart, event.x, event.y)) return true; - //var ret = chart.button_release_event(event); - if (event.button == 2) { - draw_selection = false; + switch (event.button) { + case 1: // start cursor position selection + if ((event.state & Gdk.ModifierType.SHIFT_MASK) != 0) { // remove cursor + chart.remove_active_cursor (); + da.queue_draw_area(0, 0, da.get_allocated_width(), da.get_allocated_height()); + mouse_state = MouseState.FREE; + } else { // add cursor + chart.add_active_cursor (); + da.queue_draw_area(0, 0, da.get_allocated_width(), da.get_allocated_height()); + mouse_state = MouseState.FREE; + } + break; + + case 2: sel_x1 = event.x; sel_y1 = event.y; if (sel_x1 > sel_x0 && sel_y1 > sel_y0) @@ -411,11 +472,13 @@ int main (string[] args) { else chart.zoom_out (); da.queue_draw_area(0, 0, da.get_allocated_width(), da.get_allocated_height()); - } + mouse_state = MouseState.FREE; + break; - if (event.button == 3) { - moving_chart = false; + case 3: da.queue_draw_area(0, 0, da.get_allocated_width(), da.get_allocated_height()); + mouse_state = MouseState.FREE; + break; } return true; // return ret; @@ -423,20 +486,25 @@ int main (string[] args) { da.motion_notify_event.connect((event) => { if (!point_in_chart(chart, event.x, event.y)) return true; - //var ret = chart.motion_notify_event(event); - - if (draw_selection) { + switch (mouse_state) { + case MouseState.DRAW_SELECTION: sel_x1 = event.x; sel_y1 = event.y; da.queue_draw_area(0, 0, da.get_allocated_width(), da.get_allocated_height()); - } + break; - if (moving_chart) { + case MouseState.MOVING_CHART: var delta_x = event.x - mov_x0, delta_y = event.y - mov_y0; chart.move (delta_x, delta_y); mov_x0 = event.x; mov_y0 = event.y; da.queue_draw_area(0, 0, da.get_allocated_width(), da.get_allocated_height()); + break; + + case MouseState.CURSOR_SELECTION: + chart.set_active_cursor (event.x, event.y, true); + da.queue_draw_area(0, 0, da.get_allocated_width(), da.get_allocated_height()); + break; } return true; // return ret; @@ -462,6 +530,10 @@ int main (string[] args) { vbox2.pack_start(radio_button2, false, false, 0); vbox2.pack_start(radio_button3, false, false, 0); vbox2.pack_start(radio_button4, false, false, 0); + //vbox2.pack_start(radio_button5, false, false, 0); + //vbox2.pack_start(radio_button6, false, false, 0); + vbox2.pack_start(radio_button7, false, false, 0); + vbox2.pack_start(radio_button8, false, false, 0); var hbox = new Box(Orientation.HORIZONTAL, 0); hbox.pack_start(da, true, true, 0); From 22761fa04f940dc95aa37429a9f744337258db10 Mon Sep 17 00:00:00 2001 From: Kolan Sh Date: Tue, 28 Nov 2017 21:51:58 +0300 Subject: [PATCH 2/7] Fixes in get_cursor_limits() and other methods. --- src/Chart.vala | 214 +++++++++++++++++++++++++++----------------- src/LineStyle.vala | 9 +- src/Series.vala | 4 +- test/ChartTest.vala | 32 +++++-- 4 files changed, 162 insertions(+), 97 deletions(-) diff --git a/src/Chart.vala b/src/Chart.vala index 51d62c3..c2575ba 100644 --- a/src/Chart.vala +++ b/src/Chart.vala @@ -101,7 +101,7 @@ namespace Gtk.CairoChart { public virtual void zoom_in (double x0, double y0, double x1, double y1) { for (var si = 0, max_i = series.length; si < max_i; ++si) { var s = series[si]; - if (!s.show) continue; + if (!s.zoom_show) continue; var real_x0 = get_real_x (s, x0); var real_x1 = get_real_x (s, x1); var real_y0 = get_real_y (s, y0); @@ -109,7 +109,7 @@ namespace Gtk.CairoChart { // if selected square does not intersect with the series's square if ( real_x1 <= s.axis_x.zoom_min || real_x0 >= s.axis_x.zoom_max || real_y0 <= s.axis_y.zoom_min || real_y1 >= s.axis_y.zoom_max) { - s.show = false; + s.zoom_show = false; continue; } if (real_x0 >= s.axis_x.zoom_min) { @@ -140,7 +140,7 @@ namespace Gtk.CairoChart { zoom_first_show = 0; for (var si = 0, max_i = series.length; si < max_i; ++si) - if (series[si].show) { + if (series[si].zoom_show) { zoom_first_show = si; break; } @@ -157,7 +157,7 @@ namespace Gtk.CairoChart { public virtual void zoom_out () { foreach (var s in series) { - s.show = true; + s.zoom_show = true; s.axis_x.zoom_min = s.axis_x.min; s.axis_x.zoom_max = s.axis_x.max; s.axis_y.zoom_min = s.axis_y.min; @@ -359,7 +359,7 @@ namespace Gtk.CairoChart { foreach (var s in series) { - if (!s.show) continue; + if (!s.zoom_show) continue; // carry switch (legend.position) { @@ -558,7 +558,7 @@ namespace Gtk.CairoChart { int nzoom_series_show = 0; for (var si = series.length - 1; si >=0; --si) { var s = series[si]; - if (!s.show) continue; + if (!s.zoom_show) continue; ++nzoom_series_show; if ( s.axis_x.position != series[0].axis_x.position || s.axis_x.zoom_min != series[0].axis_x.zoom_min @@ -579,7 +579,7 @@ namespace Gtk.CairoChart { // Join and calc X-axes for (var si = series.length - 1, nskip = 0; si >=0; --si) { var s = series[si]; - if (!s.show) continue; + if (!s.zoom_show) continue; if (nskip != 0) {--nskip; continue;} double max_rec_width = 0; double max_rec_height = 0; calc_axis_rec_sizes (s.axis_x, out max_rec_width, out max_rec_height, true); @@ -589,11 +589,11 @@ namespace Gtk.CairoChart { // join relative x-axes with non-intersect places for (int sj = si - 1; sj >= 0; --sj) { var s2 = series[sj]; - if (!s2.show) continue; + if (!s2.zoom_show) continue; bool has_intersection = false; for (int sk = si; sk > sj; --sk) { var s3 = series[sk]; - if (!s3.show) continue; + if (!s3.zoom_show) continue; if (are_intersect(s2.place.zoom_x_low, s2.place.zoom_x_high, s3.place.zoom_x_low, s3.place.zoom_x_high) || s2.axis_x.position != s3.axis_x.position || s2.axis_x.type != s3.axis_x.type) { @@ -627,7 +627,7 @@ namespace Gtk.CairoChart { // Join and calc Y-axes for (var si = series.length - 1, nskip = 0; si >=0; --si) { var s = series[si]; - if (!s.show) continue; + if (!s.zoom_show) continue; if (nskip != 0) {--nskip; continue;} double max_rec_width = 0; double max_rec_height = 0; calc_axis_rec_sizes (s.axis_y, out max_rec_width, out max_rec_height, false); @@ -637,11 +637,11 @@ namespace Gtk.CairoChart { // join relative x-axes with non-intersect places for (int sj = si - 1; sj >= 0; --sj) { var s2 = series[sj]; - if (!s2.show) continue; + if (!s2.zoom_show) continue; bool has_intersection = false; for (int sk = si; sk > sj; --sk) { var s3 = series[sk]; - if (!s3.show) continue; + if (!s3.zoom_show) continue; if (are_intersect(s2.place.zoom_y_low, s2.place.zoom_y_high, s3.place.zoom_y_low, s3.place.zoom_y_high) || s2.axis_y.position != s3.axis_y.position || s2.axis_x.type != s3.axis_x.type) { @@ -682,7 +682,7 @@ namespace Gtk.CairoChart { protected virtual void draw_horizontal_axis () { for (var si = series.length - 1, nskip = 0; si >=0; --si) { var s = series[si]; - if (!s.show) continue; + if (!s.zoom_show) continue; if (common_x_axes && si != zoom_first_show) continue; // 1. Detect max record width/height by axis_rec_npoints equally selected points using format. double max_rec_width, max_rec_height; @@ -833,11 +833,11 @@ namespace Gtk.CairoChart { // join relative x-axes with non-intersect places for (int sj = si - 1; sj >= 0; --sj) { var s2 = series[sj]; - if (!s2.show) continue; + if (!s2.zoom_show) continue; bool has_intersection = false; for (int sk = si; sk > sj; --sk) { var s3 = series[sk]; - if (!s3.show) continue; + if (!s3.zoom_show) continue; if (are_intersect(s2.place.zoom_x_low, s2.place.zoom_x_high, s3.place.zoom_x_low, s3.place.zoom_x_high) || s2.axis_x.position != s3.axis_x.position || s2.axis_x.type != s3.axis_x.type) { @@ -873,7 +873,7 @@ namespace Gtk.CairoChart { protected virtual void draw_vertical_axis () { for (var si = series.length - 1, nskip = 0; si >=0; --si) { var s = series[si]; - if (!s.show) continue; + if (!s.zoom_show) continue; if (common_y_axes && si != zoom_first_show) continue; // 1. Detect max record width/height by axis_rec_npoints equally selected points using format. double max_rec_width, max_rec_height; @@ -976,11 +976,11 @@ namespace Gtk.CairoChart { // join relative x-axes with non-intersect places for (int sj = si - 1; sj >= 0; --sj) { var s2 = series[sj]; - if (!s2.show) continue; + if (!s2.zoom_show) continue; bool has_intersection = false; for (int sk = si; sk > sj; --sk) { var s3 = series[sk]; - if (!s3.show) continue; + if (!s3.zoom_show) continue; if (are_intersect(s2.place.zoom_y_low, s2.place.zoom_y_high, s3.place.zoom_y_low, s3.place.zoom_y_high) || s2.axis_y.position != s3.axis_y.position) { has_intersection = true; @@ -1047,9 +1047,32 @@ namespace Gtk.CairoChart { return Point (get_real_x(s, p.x), get_real_y(s, p.y)); } + protected virtual bool x_in_range (double x, double x0, double x1) { + if (x0 <= x <= x1 || x1 <= x <= x0) + return true; + return false; + } + + protected virtual bool y_in_range (double y, double y0, double y1) { + if (y0 <= y <= y1 || y1 <= y <= y0) + return true; + return false; + } + + protected virtual bool x_in_plot_area (double x) { + if (x_in_range(x, plot_area_x_min, plot_area_x_max)) + return true; + return false; + } + + protected virtual bool y_in_plot_area (double y) { + if (y_in_range(y, plot_area_y_min, plot_area_y_max)) + return true; + return false; + } + protected virtual bool point_in_rect (Point p, double x0, double x1, double y0, double y1) { - if ( (x0 <= p.x <= x1 || x1 <= p.x <= x0) - && (y0 <= p.y <= y1 || y1 <= p.y <= y0)) + if (x_in_range(p.x, x0, x1) && y_in_range(p.y, y0, y1)) return true; return false; } @@ -1130,9 +1153,9 @@ namespace Gtk.CairoChart { return false; } - protected virtual Point[] sort_points (Series s) { + protected virtual Point[] sort_points (Series s, Series.Sort sort) { var points = s.points.copy(); - switch(s.sort) { + switch(sort) { case Series.Sort.BY_X: sort_points_delegate(points, (a, b) => { if (a.x < b.x) return -1; @@ -1154,9 +1177,9 @@ namespace Gtk.CairoChart { protected virtual void draw_series () { for (var si = 0; si < series.length; ++si) { var s = series[si]; - if (!s.show) continue; + if (!s.zoom_show) continue; if (s.points.length == 0) continue; - var points = sort_points(s); + var points = sort_points(s, s.sort); set_line_style(s.line_style); // draw series line for (int i = 1; i < points.length; ++i) { @@ -1184,7 +1207,7 @@ namespace Gtk.CairoChart { public virtual void set_active_cursor (double x, double y, bool remove = false) { active_cursor = Point (scr2rel_x(x), scr2rel_y(y)); - is_cursor_active = true; + is_cursor_active = ! remove; } public virtual void add_active_cursor () { @@ -1199,7 +1222,7 @@ namespace Gtk.CairoChart { public CursorOrientation cursors_orientation = CursorOrientation.VERTICAL; - public double cursor_max_distance = 32; + public double cursor_max_rm_distance = 32; public virtual void remove_active_cursor () { if (cursors.length() == 0) return; @@ -1210,47 +1233,81 @@ namespace Gtk.CairoChart { double d = distance; switch (cursors_orientation) { case CursorOrientation.VERTICAL: - d = (c.x - active_cursor.x).abs(); + d = (rel2scr_x(c.x) - rel2scr_x(active_cursor.x)).abs(); break; case CursorOrientation.HORIZONTAL: - d = (c.y - active_cursor.y).abs(); + d = (rel2scr_y(c.y) - rel2scr_y(active_cursor.y)).abs(); break; default: break; } - if (distance > d) { + if (d < distance) { distance = d; rm_indx = i; } ++i; } - if (distance < cursor_max_distance) + if (distance < cursor_max_rm_distance) cursors.delete_link(cursors.nth(rm_indx)); + is_cursor_active = false; } - // TODO: + protected virtual Float128 scr2rel_x (Float128 x) { + return _rel_zoom_x_min + (x - plot_area_x_min) / (plot_area_x_max - plot_area_x_min) * (_rel_zoom_x_max - _rel_zoom_x_min); + } + protected virtual Float128 scr2rel_y (Float128 y) { + return _rel_zoom_y_max - (plot_area_y_max - y) / (plot_area_y_max - plot_area_y_min) * (_rel_zoom_y_max - _rel_zoom_y_min); + } + protected virtual Point scr2rel_point (Point p) { + return Point (scr2rel_x(p.x), scr2rel_y(p.y)); + } + + protected virtual Float128 rel2scr_x(Float128 x) { + return plot_area_x_min + (plot_area_x_max - plot_area_x_min) * (x - _rel_zoom_x_min) / (_rel_zoom_x_max - _rel_zoom_x_min); + } + + protected virtual Float128 rel2scr_y(Float128 y) { + return plot_area_y_min + (plot_area_y_max - plot_area_y_min) * (y - _rel_zoom_y_min) / (_rel_zoom_y_max - _rel_zoom_y_min); + } + + protected virtual Point rel2scr_point (Point p) { + return Point (rel2scr_x(p.x), rel2scr_y(p.y)); + } + + public LineStyle cursor_line_style = LineStyle(Color(0.2, 0.2, 0.2, 0.8)); + protected virtual bool get_cursor_limits (Series s, Point cursor, out Point low, out Point high) { - var points = sort_points (s); bool ret = false; - low.x = plot_area_x_min; - low.y = plot_area_y_min; - high.x = plot_area_x_max; - high.y = plot_area_y_max; + low.x = plot_area_x_max; + low.y = plot_area_y_max; + high.x = plot_area_x_min; + high.y = plot_area_y_min; + Point[] points = {}; + switch (cursors_orientation) { + case CursorOrientation.VERTICAL: + points = sort_points (s, s.sort); + break; + case CursorOrientation.HORIZONTAL: + points = sort_points (s, s.sort); + break; + } for (var i = 0; i + 1 < points.length; ++i) { switch (cursors_orientation) { case CursorOrientation.VERTICAL: Float128 y = 0.0; - if (vcross(get_scr_point(s, s.points[i]), get_scr_point(s, s.points[i+1]), cursor.x, plot_area_y_min, plot_area_y_max, out y)) { - if (y < low.y) low.y = y; - if (y > high.y) high.y = y; + if (vcross(get_scr_point(s, points[i]), get_scr_point(s, points[i+1]), rel2scr_x(cursor.x), + plot_area_y_min, plot_area_y_max, out y)) { + if (y < low.y /*&& y_in_plot_area(y)*/) low.y = y; + if (y > high.y /*&& y_in_plot_area(y)*/) high.y = y; ret = true; } break; case CursorOrientation.HORIZONTAL: Float128 x = 0.0; - if (hcross(get_scr_point(s, s.points[i]), get_scr_point(s, s.points[i+1]), cursor.y, plot_area_x_min, plot_area_x_max, out x)) { - if (x < low.x) low.x = x; - if (x > high.x) high.x = x; + if (hcross(get_scr_point(s, points[i]), get_scr_point(s, points[i+1]), + plot_area_x_min, plot_area_x_max, rel2scr_y(cursor.y), out x)) { + if (x < low.x /*&& x_in_plot_area(x)*/) low.x = x; + if (x > high.x /*&& x_in_plot_area(x)*/) high.x = x; ret = true; } break; @@ -1259,13 +1316,14 @@ namespace Gtk.CairoChart { if (common_x_axes) { switch (s.axis_x.position) { - case Axis.Position.LOW: low.y = plot_area_y_max + s.axis_x.font_indent; break; - case Axis.Position.HIGH: high.y = plot_area_y_min - s.axis_x.font_indent; break; + case Axis.Position.LOW: high.y = plot_area_y_max + s.axis_x.font_indent; break; + case Axis.Position.HIGH: low.y = plot_area_y_min - s.axis_x.font_indent; break; case Axis.Position.BOTH: - low.y = plot_area_y_max + s.axis_x.font_indent; - high.y = plot_area_y_min - s.axis_x.font_indent; + high.y = plot_area_y_max + s.axis_x.font_indent; + low.y = plot_area_y_min - s.axis_x.font_indent; break; } + ret = true; } if (common_y_axes) { switch (s.axis_y.position) { @@ -1276,53 +1334,31 @@ namespace Gtk.CairoChart { high.x = plot_area_x_max + s.axis_y.font_indent; break; } + ret = true; } - return false; + return ret; } - protected virtual Float128 scr2rel_x (Float128 x) { - return (x - plot_area_x_min) / (plot_area_x_max - plot_area_x_min); - } - protected virtual Float128 scr2rel_y (Float128 y) { - return (y - plot_area_y_min) / (plot_area_y_max - plot_area_y_min); - } - protected virtual Point scr2rel_point (Point p) { - return Point (scr2rel_x(p.x), scr2rel_y(p.y)); - } - - protected virtual Float128 rel2scr_x(Float128 x) { - return plot_area_x_min + (plot_area_x_max - plot_area_x_min) * x; - } - - protected virtual Float128 rel2scr_y(Float128 y) { - return plot_area_y_max - (plot_area_y_max - plot_area_y_min) * y; - } - - protected virtual Point rel2scr_point (Point p) { - return Point (rel2scr_x(p.x), rel2scr_y(p.y)); - } - - public LineStyle cursor_line_style = LineStyle(); - - // TODO: protected virtual void draw_cursors () { if (series.length == 0) return; - var all_cursors = cursors.copy(); - all_cursors.append(active_cursor); + var all_cursors = cursors.copy_deep ((src) => { return src; }); + if (is_cursor_active) + all_cursors.append(active_cursor); foreach (var c in all_cursors) { switch (cursors_orientation) { case CursorOrientation.VERTICAL: - if (c.x <= rel_zoom_x_min || c.x >= rel_zoom_x_max) continue; break; + if (c.x <= _rel_zoom_x_min || c.x >= _rel_zoom_x_max) continue; break; case CursorOrientation.HORIZONTAL: - if (c.y <= rel_zoom_y_min || c.y >= rel_zoom_y_max) continue; break; + if (c.y <= _rel_zoom_y_min || c.y >= _rel_zoom_y_max) continue; break; } - var low = Point(plot_area_x_max, plot_area_y_min); // low and high - var high = Point(plot_area_x_min, plot_area_y_max); // points of the cursor + var low = Point(plot_area_x_max, plot_area_y_max); // low and high + var high = Point(plot_area_x_min, plot_area_y_min); // points of the cursor foreach (var s in series) { + if (!s.zoom_show) continue; var l = Point(), h = Point(); if (get_cursor_limits (s, c, out l, out h)) { if (l.x < low.x) low.x = l.x; @@ -1334,7 +1370,7 @@ namespace Gtk.CairoChart { switch (cursors_orientation) { case CursorOrientation.VERTICAL: - // TODO: draw cursor line + if (low.y > high.y) continue; set_line_style(cursor_line_style); context.move_to (rel2scr_x(c.x), low.y); context.line_to (rel2scr_x(c.x), high.y); @@ -1342,14 +1378,14 @@ namespace Gtk.CairoChart { // TODO: show values if (common_x_axes) - // TODO: show only Y value + // show only Y value ; else - // TODO: show [X;Y] + // show [X;Y] ; break; case CursorOrientation.HORIZONTAL: - // TODO: draw cursor line + if (low.x > high.x) continue; set_line_style(cursor_line_style); context.move_to (low.x, rel2scr_y(c.y)); context.line_to (high.x, rel2scr_y(c.y)); @@ -1357,10 +1393,10 @@ namespace Gtk.CairoChart { // TODO: show values if (common_y_axes) - // TODO: show only X value + // show only X value ; else - // TODO: show [X;Y] + // show [X;Y] ; break; } @@ -1369,6 +1405,7 @@ namespace Gtk.CairoChart { public Chart copy () { var chart = new Chart (); + chart.active_cursor = this.active_cursor; chart.axis_rec_npoints = this.axis_rec_npoints; chart.bg_color = this.bg_color; chart.border_color = this.border_color; @@ -1379,7 +1416,12 @@ namespace Gtk.CairoChart { chart.cur_x_min = this.cur_x_min; chart.cur_y_max = this.cur_y_max; chart.cur_y_min = this.cur_y_min; + chart.cursor_line_style = this.cursor_line_style; + chart.cursor_max_rm_distance = this.cursor_max_rm_distance; + chart.cursors = this.cursors.copy(); + chart.cursors_orientation = this.cursors_orientation; chart.height = this.height; + chart.is_cursor_active = this.is_cursor_active; chart.legend = this.legend.copy(); chart.legend_height = this.legend_height; chart.legend_line_length = this.legend_line_length; @@ -1397,12 +1439,16 @@ namespace Gtk.CairoChart { chart._rel_zoom_y_min = this._rel_zoom_y_min; chart._rel_zoom_y_max = this._rel_zoom_y_max; chart.selection_style = this.selection_style; + chart.series = this.series.copy(); chart.show_legend = this.show_legend; chart.title = this.title.copy().copy(); chart.title_height = this.title_height; chart.title_vindent = this.title_vindent; chart.title_width = this.title_width; chart.width = this.width; + chart.x_min = this.x_min; + chart.y_min = this.y_min; + chart.zoom_first_show = this.zoom_first_show; return chart; } } diff --git a/src/LineStyle.vala b/src/LineStyle.vala index 0efad62..4a58d1e 100644 --- a/src/LineStyle.vala +++ b/src/LineStyle.vala @@ -7,11 +7,12 @@ namespace Gtk.CairoChart { double dash_offset; Color color; - public LineStyle (double width = 1, - Cairo.LineJoin line_join = Cairo.LineJoin.MITER, - Cairo.LineCap line_cap = Cairo.LineCap.ROUND, + public LineStyle (Color color = Color(), + double width = 1, double[]? dashes = null, double dash_offset = 0, - Color color = Color()) { + Cairo.LineJoin line_join = Cairo.LineJoin.MITER, + Cairo.LineCap line_cap = Cairo.LineCap.ROUND + ) { this.width = width; this.line_join = line_join; this.line_cap = line_cap; diff --git a/src/Series.vala b/src/Series.vala index d4c37ad..2d4bc89 100644 --- a/src/Series.vala +++ b/src/Series.vala @@ -49,7 +49,7 @@ namespace Gtk.CairoChart { default = Color (0.0, 0.0, 0.0, 1.0); } - public bool show = true; + public bool zoom_show = true; public Series copy () { var series = new Series (); @@ -63,7 +63,7 @@ namespace Gtk.CairoChart { series.points = this.points.copy(); series.sort = this.sort; series.title = this.title.copy(); - series.show = this.show; + series.zoom_show = this.zoom_show; return series; } diff --git a/test/ChartTest.vala b/test/ChartTest.vala index 0008949..33916a9 100644 --- a/test/ChartTest.vala +++ b/test/ChartTest.vala @@ -251,6 +251,8 @@ int main (string[] args) { var radio_button2 = new RadioButton.with_label (radio_button1.get_group(), "Right Legend"); var radio_button3 = new RadioButton.with_label_from_widget (radio_button1, "Left Legend"); var radio_button4 = new RadioButton.with_label_from_widget (radio_button1, "Bottom Legend"); + var radio_button7 = new RadioButton.with_label (null, "Vertical Cursors"); + var radio_button8 = new RadioButton.with_label_from_widget (radio_button7, "Horizontal Cursors"); button1.clicked.connect (() => { chart = chart1; da.queue_draw_area(0, 0, da.get_allocated_width(), da.get_allocated_height()); @@ -261,6 +263,10 @@ int main (string[] args) { case Legend.Position.BOTTOM: radio_button4.set_active(true); break; default: break; } + switch (chart.cursors_orientation) { + case Chart.CursorOrientation.VERTICAL: radio_button7.set_active(true); break; + case Chart.CursorOrientation.HORIZONTAL: radio_button8.set_active(true); break; + } }); button2.clicked.connect (() => { chart = chart2; da.queue_draw_area(0, 0, da.get_allocated_width(), da.get_allocated_height()); @@ -271,6 +277,10 @@ int main (string[] args) { case Legend.Position.BOTTOM: radio_button4.set_active(true); break; default: break; } + switch (chart.cursors_orientation) { + case Chart.CursorOrientation.VERTICAL: radio_button7.set_active(true); break; + case Chart.CursorOrientation.HORIZONTAL: radio_button8.set_active(true); break; + } }); button3.clicked.connect (() => { chart = chart3; da.queue_draw_area(0, 0, da.get_allocated_width(), da.get_allocated_height()); @@ -281,6 +291,10 @@ int main (string[] args) { case Legend.Position.BOTTOM: radio_button4.set_active(true); break; default: break; } + switch (chart.cursors_orientation) { + case Chart.CursorOrientation.VERTICAL: radio_button7.set_active(true); break; + case Chart.CursorOrientation.HORIZONTAL: radio_button8.set_active(true); break; + } }); button4.clicked.connect (() => { chart = chart4; da.queue_draw_area(0, 0, da.get_allocated_width(), da.get_allocated_height()); @@ -291,6 +305,10 @@ int main (string[] args) { case Legend.Position.BOTTOM: radio_button4.set_active(true); break; default: break; } + switch (chart.cursors_orientation) { + case Chart.CursorOrientation.VERTICAL: radio_button7.set_active(true); break; + case Chart.CursorOrientation.HORIZONTAL: radio_button8.set_active(true); break; + } }); button5.clicked.connect (() => { for (var i = 0; i < chart.series.length; ++i) { @@ -369,8 +387,6 @@ int main (string[] args) { } });*/ - var radio_button7 = new RadioButton.with_label (null, "Vertical Cursors"); - var radio_button8 = new RadioButton.with_label_from_widget (radio_button7, "Horizontal Cursors"); radio_button7.toggled.connect ((button) => { if (button.get_active()) { chart.cursors_orientation = Chart.CursorOrientation.VERTICAL; @@ -423,11 +439,13 @@ int main (string[] args) { case 1: // start cursor position selection if ((event.state & Gdk.ModifierType.SHIFT_MASK) != 0) { // remove cursor chart.set_active_cursor (event.x, event.y, true); + chart.remove_active_cursor(); + mouse_state = MouseState.FREE; } else { // add cursor chart.set_active_cursor (event.x, event.y); + mouse_state = MouseState.CURSOR_SELECTION; } da.queue_draw_area(0, 0, da.get_allocated_width(), da.get_allocated_height()); - mouse_state = MouseState.CURSOR_SELECTION; break; case 2: // start zoom area selection @@ -454,9 +472,9 @@ int main (string[] args) { switch (event.button) { case 1: // start cursor position selection if ((event.state & Gdk.ModifierType.SHIFT_MASK) != 0) { // remove cursor - chart.remove_active_cursor (); - da.queue_draw_area(0, 0, da.get_allocated_width(), da.get_allocated_height()); - mouse_state = MouseState.FREE; + //chart.remove_active_cursor (); + //da.queue_draw_area(0, 0, da.get_allocated_width(), da.get_allocated_height()); + //mouse_state = MouseState.FREE; } else { // add cursor chart.add_active_cursor (); da.queue_draw_area(0, 0, da.get_allocated_width(), da.get_allocated_height()); @@ -502,7 +520,7 @@ int main (string[] args) { break; case MouseState.CURSOR_SELECTION: - chart.set_active_cursor (event.x, event.y, true); + chart.set_active_cursor (event.x, event.y); da.queue_draw_area(0, 0, da.get_allocated_width(), da.get_allocated_height()); break; } From dc18d83414d4c851396ec886d5dc2f7c9cc23f0c Mon Sep 17 00:00:00 2001 From: Kolan Sh Date: Fri, 1 Dec 2017 16:25:07 +0300 Subject: [PATCH 3/7] CursorCrossings[] added. --- src/Chart.vala | 184 +++++++++++++++++++++++++++++-------------------- 1 file changed, 110 insertions(+), 74 deletions(-) diff --git a/src/Chart.vala b/src/Chart.vala index c2575ba..aa154c2 100644 --- a/src/Chart.vala +++ b/src/Chart.vala @@ -55,6 +55,8 @@ namespace Gtk.CairoChart { set_vertical_axes_titles (); + get_cursors_crossings(); + calc_plot_area (); // Calculate plot area draw_horizontal_axis (); @@ -1276,78 +1278,31 @@ namespace Gtk.CairoChart { public LineStyle cursor_line_style = LineStyle(Color(0.2, 0.2, 0.2, 0.8)); - protected virtual bool get_cursor_limits (Series s, Point cursor, out Point low, out Point high) { - bool ret = false; - low.x = plot_area_x_max; - low.y = plot_area_y_max; - high.x = plot_area_x_min; - high.y = plot_area_y_min; - Point[] points = {}; - switch (cursors_orientation) { - case CursorOrientation.VERTICAL: - points = sort_points (s, s.sort); - break; - case CursorOrientation.HORIZONTAL: - points = sort_points (s, s.sort); - break; - } - for (var i = 0; i + 1 < points.length; ++i) { - switch (cursors_orientation) { - case CursorOrientation.VERTICAL: - Float128 y = 0.0; - if (vcross(get_scr_point(s, points[i]), get_scr_point(s, points[i+1]), rel2scr_x(cursor.x), - plot_area_y_min, plot_area_y_max, out y)) { - if (y < low.y /*&& y_in_plot_area(y)*/) low.y = y; - if (y > high.y /*&& y_in_plot_area(y)*/) high.y = y; - ret = true; - } - break; - case CursorOrientation.HORIZONTAL: - Float128 x = 0.0; - if (hcross(get_scr_point(s, points[i]), get_scr_point(s, points[i+1]), - plot_area_x_min, plot_area_x_max, rel2scr_y(cursor.y), out x)) { - if (x < low.x /*&& x_in_plot_area(x)*/) low.x = x; - if (x > high.x /*&& x_in_plot_area(x)*/) high.x = x; - ret = true; - } - break; - } - } - - if (common_x_axes) { - switch (s.axis_x.position) { - case Axis.Position.LOW: high.y = plot_area_y_max + s.axis_x.font_indent; break; - case Axis.Position.HIGH: low.y = plot_area_y_min - s.axis_x.font_indent; break; - case Axis.Position.BOTH: - high.y = plot_area_y_max + s.axis_x.font_indent; - low.y = plot_area_y_min - s.axis_x.font_indent; - break; - } - ret = true; - } - if (common_y_axes) { - switch (s.axis_y.position) { - case Axis.Position.LOW: low.x = plot_area_x_min - s.axis_y.font_indent; break; - case Axis.Position.HIGH: high.x = plot_area_x_max + s.axis_y.font_indent; break; - case Axis.Position.BOTH: - low.x = plot_area_x_min - s.axis_y.font_indent; - high.x = plot_area_x_max + s.axis_y.font_indent; - break; - } - ret = true; - } - - return ret; + protected struct CursorCross { + uint series_index; + Point point; + } + protected struct CursorCrossings { + uint cursor_index; + CursorCross[] crossings; } - protected virtual void draw_cursors () { - if (series.length == 0) return; + protected CursorCrossings[] cursors_crossings = {}; + protected List get_all_cursors () { var all_cursors = cursors.copy_deep ((src) => { return src; }); if (is_cursor_active) all_cursors.append(active_cursor); + return all_cursors; + } - foreach (var c in all_cursors) { + protected void get_cursors_crossings () { + var all_cursors = get_all_cursors(); + + CursorCrossings[] local_cursor_crossings = {}; + + for (var ci = 0, max_ci = all_cursors.length(); ci < max_ci; ++ci) { + var c = all_cursors.nth_data(ci); switch (cursors_orientation) { case CursorOrientation.VERTICAL: if (c.x <= _rel_zoom_x_min || c.x >= _rel_zoom_x_max) continue; break; @@ -1355,23 +1310,103 @@ namespace Gtk.CairoChart { if (c.y <= _rel_zoom_y_min || c.y >= _rel_zoom_y_max) continue; break; } - var low = Point(plot_area_x_max, plot_area_y_max); // low and high - var high = Point(plot_area_x_min, plot_area_y_min); // points of the cursor - foreach (var s in series) { + CursorCross[] crossings = {}; + for (var si = 0, max_si = series.length; si < max_si; ++si) { + var s = series[si]; if (!s.zoom_show) continue; - var l = Point(), h = Point(); - if (get_cursor_limits (s, c, out l, out h)) { - if (l.x < low.x) low.x = l.x; - if (l.y < low.y) low.y = l.y; - if (h.x > high.x) high.x = h.x; - if (h.y > high.y) high.y = h.y; + + Point[] points = {}; + switch (cursors_orientation) { + case CursorOrientation.VERTICAL: + points = sort_points (s, s.sort); + break; + case CursorOrientation.HORIZONTAL: + points = sort_points (s, s.sort); + break; + } + + for (var i = 0; i + 1 < points.length; ++i) { + switch (cursors_orientation) { + case CursorOrientation.VERTICAL: + Float128 y = 0.0; + if (vcross(get_scr_point(s, points[i]), get_scr_point(s, points[i+1]), rel2scr_x(c.x), + plot_area_y_min, plot_area_y_max, out y)) { + CursorCross cc = {si, Point(get_real_x(s, rel2scr_x(c.x)), get_real_y(s, y))}; + crossings += cc; +//stdout.printf("si = %d, rel2scr_x(c.x) = %f, y = %f\n", si, rel2scr_x(c.x), y); + } + break; + case CursorOrientation.HORIZONTAL: + Float128 x = 0.0; + if (hcross(get_scr_point(s, points[i]), get_scr_point(s, points[i+1]), + plot_area_x_min, plot_area_x_max, rel2scr_y(c.y), out x)) { + CursorCross cc = {si, Point(get_real_x(s, x), get_real_y(s, rel2scr_y(c.y)))}; + crossings += cc; + } + break; + } } } + if (crossings.length != 0) { + CursorCrossings ccs = {ci, crossings}; + local_cursor_crossings += ccs; + } + } + cursors_crossings = local_cursor_crossings; +//if (cursors_crossings.length != 0) +//stdout.printf("cursors_crossings[0].crossings.length = %d\n", cursors_crossings[0].crossings.length); + } + + protected virtual void draw_cursors () { + if (series.length == 0) return; + + var all_cursors = get_all_cursors(); + + for (var cci = 0, max_cci = cursors_crossings.length; cci < max_cci; ++cci) { + var low = Point(plot_area_x_max, plot_area_y_max); // low and high + var high = Point(plot_area_x_min, plot_area_y_min); // points of the cursor + var ccs = cursors_crossings[cci].crossings; + for (var ci = 0, max_ci = ccs.length; ci < max_ci; ++ci) { + var si = ccs[ci].series_index; + var s = series[si]; + var p = ccs[ci].point; + var scrx = get_scr_x(s, p.x); + var scry = get_scr_y(s, p.y); + if (scrx < low.x) low.x = scrx; +//stdout.printf("low.y = %f, high.y = %f\n", low.y, high.y); + if (scry < low.y) low.y = scry; + if (scrx > high.x) high.x = scrx; + if (scry > high.y) high.y = scry; + + if (common_x_axes) { + switch (s.axis_x.position) { + case Axis.Position.LOW: high.y = plot_area_y_max + s.axis_x.font_indent; break; + case Axis.Position.HIGH: low.y = plot_area_y_min - s.axis_x.font_indent; break; + case Axis.Position.BOTH: + high.y = plot_area_y_max + s.axis_x.font_indent; + low.y = plot_area_y_min - s.axis_x.font_indent; + break; + } + } + if (common_y_axes) { + switch (s.axis_y.position) { + case Axis.Position.LOW: low.x = plot_area_x_min - s.axis_y.font_indent; break; + case Axis.Position.HIGH: high.x = plot_area_x_max + s.axis_y.font_indent; break; + case Axis.Position.BOTH: + low.x = plot_area_x_min - s.axis_y.font_indent; + high.x = plot_area_x_max + s.axis_y.font_indent; + break; + } + } + } + + var c = all_cursors.nth_data(cursors_crossings[cci].cursor_index); switch (cursors_orientation) { case CursorOrientation.VERTICAL: if (low.y > high.y) continue; set_line_style(cursor_line_style); +//stdout.printf("rel2scr_x(c.x) = %f, low.y = %f\n", rel2scr_x(c.x), low.y); context.move_to (rel2scr_x(c.x), low.y); context.line_to (rel2scr_x(c.x), high.y); context.stroke(); @@ -1419,6 +1454,7 @@ namespace Gtk.CairoChart { chart.cursor_line_style = this.cursor_line_style; chart.cursor_max_rm_distance = this.cursor_max_rm_distance; chart.cursors = this.cursors.copy(); + chart.cursors_crossings = this.cursors_crossings.copy(); // no deep copying for .crossings chart.cursors_orientation = this.cursors_orientation; chart.height = this.height; chart.is_cursor_active = this.is_cursor_active; From 4e89e2682e7a9de9b64c684db15e74c7a57a574c Mon Sep 17 00:00:00 2001 From: Kolan Sh Date: Sun, 3 Dec 2017 16:03:51 +0300 Subject: [PATCH 4/7] Cursors: Move common axex. --- src/Chart.vala | 44 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/src/Chart.vala b/src/Chart.vala index aa154c2..178e574 100644 --- a/src/Chart.vala +++ b/src/Chart.vala @@ -665,12 +665,30 @@ namespace Gtk.CairoChart { } } + // 4.2. Cursor values for common X axis + if (common_x_axes && si == zoom_first_show && cursors_orientation == CursorOrientation.VERTICAL && cursors_crossings.length != 0) { + switch (s.axis_x.position) { + case Axis.Position.LOW: plot_area_y_max -= max_rec_height + s.axis_x.font_indent; break; + case Axis.Position.HIGH: plot_area_y_min += max_rec_height + s.axis_x.font_indent; break; + case Axis.Position.BOTH: break; + } + } + + // 4.2. Cursor values for common Y axis + if (common_y_axes && si == zoom_first_show && cursors_orientation == CursorOrientation.HORIZONTAL && cursors_crossings.length != 0) { + switch (s.axis_y.position) { + case Axis.Position.LOW: plot_area_x_min += max_rec_width + s.axis_y.font_indent; break; + case Axis.Position.HIGH: plot_area_x_max -= max_rec_width + s.axis_y.font_indent; break; + case Axis.Position.BOTH: break; + } + } + if (!common_y_axes || si == zoom_first_show) switch (s.axis_y.position) { - case Axis.Position.LOW: plot_area_x_min += max_rec_width + max_font_indent + max_axis_font_width; break; - case Axis.Position.HIGH: plot_area_x_max -= max_rec_width + max_font_indent + max_axis_font_width; break; - case Axis.Position.BOTH: break; - default: break; + case Axis.Position.LOW: plot_area_x_min += max_rec_width + max_font_indent + max_axis_font_width; break; + case Axis.Position.HIGH: plot_area_x_max -= max_rec_width + max_font_indent + max_axis_font_width; break; + case Axis.Position.BOTH: break; + default: break; } } } @@ -710,6 +728,15 @@ namespace Gtk.CairoChart { } if (x_min < s.axis_x.zoom_min) x_min += step; + // 4.2. Cursor values for common X axis + if (common_x_axes && cursors_orientation == CursorOrientation.VERTICAL && cursors_crossings.length != 0) { + switch (s.axis_x.position) { + case Axis.Position.LOW: cur_y_max -= max_rec_height + s.axis_x.font_indent; break; + case Axis.Position.HIGH: cur_y_min += max_rec_height + s.axis_x.font_indent; break; + case Axis.Position.BOTH: break; + } + } + // 4.5. Draw Axis title if (s.axis_x.title.text != "") switch (s.axis_x.position) { @@ -901,6 +928,15 @@ namespace Gtk.CairoChart { } if (y_min < s.axis_y.zoom_min) y_min += step; + // 4.2. Cursor values for common Y axis + if (common_y_axes && cursors_orientation == CursorOrientation.HORIZONTAL && cursors_crossings.length != 0) { + switch (s.axis_y.position) { + case Axis.Position.LOW: cur_x_min += max_rec_width + s.axis_y.font_indent; break; + case Axis.Position.HIGH: cur_x_max -= max_rec_width + s.axis_y.font_indent; break; + case Axis.Position.BOTH: break; + } + } + // 4.5. Draw Axis title if (s.axis_y.title.text != "") switch (s.axis_y.position) { From 7a1fd945327784006cefad5ab1bbe22e2a5b81d5 Mon Sep 17 00:00:00 2001 From: Kolan Sh Date: Mon, 4 Dec 2017 16:07:59 +0300 Subject: [PATCH 5/7] Switch-case alignments. --- src/Chart.vala | 574 ++++++++++++++++++++++---------------------- test/ChartTest.vala | 7 +- 2 files changed, 285 insertions(+), 296 deletions(-) diff --git a/src/Chart.vala b/src/Chart.vala index 178e574..2501b17 100644 --- a/src/Chart.vala +++ b/src/Chart.vala @@ -268,9 +268,6 @@ namespace Gtk.CairoChart { x0 = width - legend_width; y0 = (height - legend_height) / 2; break; - - default: - break; } set_source_rgba(legend.bg_color); context.rectangle (x0, y0, legend_width, legend_height); @@ -294,47 +291,43 @@ namespace Gtk.CairoChart { double x, double y) { context.move_to (x, y); switch (marker_type) { - case Series.MarkerType.SQUARE: - context.rectangle (x - marker_size / 2, y - marker_size / 2, - marker_size, marker_size); - context.fill(); - break; + case Series.MarkerType.SQUARE: + context.rectangle (x - marker_size / 2, y - marker_size / 2, + marker_size, marker_size); + context.fill(); + break; - case Series.MarkerType.CIRCLE: - context.arc (x, y, marker_size / 2, 0, 2*Math.PI); - context.fill(); - break; + case Series.MarkerType.CIRCLE: + context.arc (x, y, marker_size / 2, 0, 2*Math.PI); + context.fill(); + break; - case Series.MarkerType.TRIANGLE: - context.move_to (x - marker_size / 2, y - marker_size / 2); - context.line_to (x + marker_size / 2, y - marker_size / 2); - context.line_to (x, y + marker_size / 2); - context.line_to (x - marker_size / 2, y - marker_size / 2); - context.fill(); - break; + case Series.MarkerType.TRIANGLE: + context.move_to (x - marker_size / 2, y - marker_size / 2); + context.line_to (x + marker_size / 2, y - marker_size / 2); + context.line_to (x, y + marker_size / 2); + context.line_to (x - marker_size / 2, y - marker_size / 2); + context.fill(); + break; - case Series.MarkerType.PRICLE_SQUARE: - context.rectangle (x - marker_size / 2, y - marker_size / 2, - marker_size, marker_size); - context.stroke(); - break; + case Series.MarkerType.PRICLE_SQUARE: + context.rectangle (x - marker_size / 2, y - marker_size / 2, + marker_size, marker_size); + context.stroke(); + break; - case Series.MarkerType.PRICLE_CIRCLE: - context.arc (x, y, marker_size / 2, 0, 2*Math.PI); - context.stroke(); - break; + case Series.MarkerType.PRICLE_CIRCLE: + context.arc (x, y, marker_size / 2, 0, 2*Math.PI); + context.stroke(); + break; - case Series.MarkerType.PRICLE_TRIANGLE: - context.move_to (x - marker_size / 2, y - marker_size / 2); - context.line_to (x + marker_size / 2, y - marker_size / 2); - context.line_to (x, y + marker_size / 2); - context.line_to (x - marker_size / 2, y - marker_size / 2); - context.stroke(); - break; - - case Series.MarkerType.NONE: - default: - break; + case Series.MarkerType.PRICLE_TRIANGLE: + context.move_to (x - marker_size / 2, y - marker_size / 2); + context.line_to (x + marker_size / 2, y - marker_size / 2); + context.line_to (x, y + marker_size / 2); + context.line_to (x - marker_size / 2, y - marker_size / 2); + context.stroke(); + break; } } @@ -348,15 +341,15 @@ namespace Gtk.CairoChart { // prepare switch (process_type) { - case LegendProcessType.CALC: - legend_width = 0.0; - legend_height = 0.0; - max_font_heights = {}; - heights_idx = 0; - break; - case LegendProcessType.DRAW: - draw_legend_rect(out legend_x0, out legend_y0); - break; + case LegendProcessType.CALC: + legend_width = 0.0; + legend_height = 0.0; + max_font_heights = {}; + heights_idx = 0; + break; + case LegendProcessType.DRAW: + draw_legend_rect(out legend_x0, out legend_y0); + break; } foreach (var s in series) { @@ -365,43 +358,43 @@ namespace Gtk.CairoChart { // carry switch (legend.position) { - case Legend.Position.TOP: - case Legend.Position.BOTTOM: - var ser_title_width = s.title.get_width(context) + legend_line_length; - if (leg_width_sum + (leg_width_sum == 0 ? 0 : legend_text_hspace) + ser_title_width > width) { // carry - leg_height_sum += max_font_h; - switch (process_type) { - case LegendProcessType.CALC: - max_font_heights += max_font_h; - legend_width = double.max(legend_width, leg_width_sum); - break; - case LegendProcessType.DRAW: - heights_idx++; - break; - } - leg_width_sum = 0.0; - max_font_h = 0; + case Legend.Position.TOP: + case Legend.Position.BOTTOM: + var ser_title_width = s.title.get_width(context) + legend_line_length; + if (leg_width_sum + (leg_width_sum == 0 ? 0 : legend_text_hspace) + ser_title_width > width) { // carry + leg_height_sum += max_font_h; + switch (process_type) { + case LegendProcessType.CALC: + max_font_heights += max_font_h; + legend_width = double.max(legend_width, leg_width_sum); + break; + case LegendProcessType.DRAW: + heights_idx++; + break; } - break; + leg_width_sum = 0.0; + max_font_h = 0; + } + break; } switch (process_type) { - case LegendProcessType.DRAW: - var x = legend_x0 + leg_width_sum + (leg_width_sum == 0.0 ? 0.0 : legend_text_hspace); - var y = legend_y0 + leg_height_sum + max_font_heights[heights_idx]; + case LegendProcessType.DRAW: + var x = legend_x0 + leg_width_sum + (leg_width_sum == 0.0 ? 0.0 : legend_text_hspace); + var y = legend_y0 + leg_height_sum + max_font_heights[heights_idx]; - // series title - context.move_to (x + legend_line_length - s.title.get_x_bearing(context), y); - set_source_rgba (s.title.color); - show_text(s.title); + // series title + context.move_to (x + legend_line_length - s.title.get_x_bearing(context), y); + set_source_rgba (s.title.color); + show_text(s.title); - // series line style - context.move_to (x, y - s.title.get_height(context) / 2); - set_line_style(s.line_style); - context.rel_line_to (legend_line_length, 0); - context.stroke(); - draw_marker_at_pos (s.marker_type, x + legend_line_length / 2, y - s.title.get_height(context) / 2); - break; + // series line style + context.move_to (x, y - s.title.get_height(context) / 2); + set_line_style(s.line_style); + context.rel_line_to (legend_line_length, 0); + context.stroke(); + draw_marker_at_pos (s.marker_type, x + legend_line_length / 2, y - s.title.get_height(context) / 2); + break; } switch (legend.position) { @@ -415,13 +408,13 @@ namespace Gtk.CairoChart { case Legend.Position.LEFT: case Legend.Position.RIGHT: switch (process_type) { - case LegendProcessType.CALC: - max_font_heights += s.title.get_height(context) + (leg_height_sum != 0 ? legend_text_vspace : 0); - legend_width = double.max (legend_width, s.title.get_width(context) + legend_line_length); - break; - case LegendProcessType.DRAW: - heights_idx++; - break; + case LegendProcessType.CALC: + max_font_heights += s.title.get_height(context) + (leg_height_sum != 0 ? legend_text_vspace : 0); + legend_width = double.max (legend_width, s.title.get_width(context) + legend_line_length); + break; + case LegendProcessType.DRAW: + heights_idx++; + break; } leg_height_sum += s.title.get_height(context) + (leg_height_sum != 0 ? legend_text_vspace : 0); break; @@ -430,38 +423,38 @@ namespace Gtk.CairoChart { // TOP, BOTTOM switch (legend.position) { - case Legend.Position.TOP: - case Legend.Position.BOTTOM: - if (leg_width_sum != 0) { - leg_height_sum += max_font_h; - switch (process_type) { - case LegendProcessType.CALC: - max_font_heights += max_font_h; - legend_width = double.max(legend_width, leg_width_sum); - break; - } + case Legend.Position.TOP: + case Legend.Position.BOTTOM: + if (leg_width_sum != 0) { + leg_height_sum += max_font_h; + switch (process_type) { + case LegendProcessType.CALC: + max_font_heights += max_font_h; + legend_width = double.max(legend_width, leg_width_sum); + break; } - break; + } + break; } switch (process_type) { - case LegendProcessType.CALC: - legend_height = leg_height_sum; - switch (legend.position) { - case Legend.Position.TOP: - cur_y_min += legend_height; - break; - case Legend.Position.BOTTOM: - cur_y_max -= legend_height; - break; - case Legend.Position.LEFT: - cur_x_min += legend_width; - break; - case Legend.Position.RIGHT: - cur_x_max -= legend_width; - break; - } - break; + case LegendProcessType.CALC: + legend_height = leg_height_sum; + switch (legend.position) { + case Legend.Position.TOP: + cur_y_min += legend_height; + break; + case Legend.Position.BOTTOM: + cur_y_max -= legend_height; + break; + case Legend.Position.LEFT: + cur_x_min += legend_width; + break; + case Legend.Position.RIGHT: + cur_x_max -= legend_width; + break; + } + break; } } @@ -502,8 +495,6 @@ namespace Gtk.CairoChart { } max_rec_height = double.max (max_rec_height, h); break; - default: - break; } } } @@ -617,12 +608,18 @@ namespace Gtk.CairoChart { } } + // for 4.2. Cursor values for common X axis + if (common_x_axes && si == zoom_first_show && cursors_orientation == CursorOrientation.VERTICAL && cursors_crossings.length != 0) { + switch (s.axis_x.position) { + case Axis.Position.LOW: plot_area_y_max -= max_rec_height + s.axis_x.font_indent; break; + case Axis.Position.HIGH: plot_area_y_min += max_rec_height + s.axis_x.font_indent; break; + } + } + if (!common_x_axes || si == zoom_first_show) switch (s.axis_x.position) { case Axis.Position.LOW: plot_area_y_max -= max_rec_height + max_font_indent + max_axis_font_height; break; case Axis.Position.HIGH: plot_area_y_min += max_rec_height + max_font_indent + max_axis_font_height; break; - case Axis.Position.BOTH: break; - default: break; } } @@ -665,30 +662,18 @@ namespace Gtk.CairoChart { } } - // 4.2. Cursor values for common X axis - if (common_x_axes && si == zoom_first_show && cursors_orientation == CursorOrientation.VERTICAL && cursors_crossings.length != 0) { - switch (s.axis_x.position) { - case Axis.Position.LOW: plot_area_y_max -= max_rec_height + s.axis_x.font_indent; break; - case Axis.Position.HIGH: plot_area_y_min += max_rec_height + s.axis_x.font_indent; break; - case Axis.Position.BOTH: break; - } - } - - // 4.2. Cursor values for common Y axis + // for 4.2. Cursor values for common Y axis if (common_y_axes && si == zoom_first_show && cursors_orientation == CursorOrientation.HORIZONTAL && cursors_crossings.length != 0) { switch (s.axis_y.position) { - case Axis.Position.LOW: plot_area_x_min += max_rec_width + s.axis_y.font_indent; break; - case Axis.Position.HIGH: plot_area_x_max -= max_rec_width + s.axis_y.font_indent; break; - case Axis.Position.BOTH: break; + case Axis.Position.LOW: plot_area_x_min += max_rec_width + s.axis_y.font_indent; break; + case Axis.Position.HIGH: plot_area_x_max -= max_rec_width + s.axis_y.font_indent; break; } } if (!common_y_axes || si == zoom_first_show) switch (s.axis_y.position) { - case Axis.Position.LOW: plot_area_x_min += max_rec_width + max_font_indent + max_axis_font_width; break; - case Axis.Position.HIGH: plot_area_x_max -= max_rec_width + max_font_indent + max_axis_font_width; break; - case Axis.Position.BOTH: break; - default: break; + case Axis.Position.LOW: plot_area_x_min += max_rec_width + max_font_indent + max_axis_font_width; break; + case Axis.Position.HIGH: plot_area_x_max -= max_rec_width + max_font_indent + max_axis_font_width; break; } } } @@ -699,11 +684,22 @@ namespace Gtk.CairoChart { return false; } + protected virtual double compact_rec_x_pos (Series s, Float128 x, Text text) { + return get_scr_x(s, x) - text.get_width(context) / 2.0 - text.get_x_bearing(context) + - text.get_width(context) * (x - (s.axis_x.zoom_min + s.axis_x.zoom_max) / 2.0) / (s.axis_x.zoom_max - s.axis_x.zoom_min); + } + + protected virtual double compact_rec_y_pos (Series s, Float128 y, Text text) { + return get_scr_y(s, y) + text.get_height(context) / 2.0 + + text.get_height(context) * (y - (s.axis_y.zoom_min + s.axis_y.zoom_max) / 2.0) / (s.axis_y.zoom_max - s.axis_y.zoom_min); + } + protected virtual void draw_horizontal_axis () { for (var si = series.length - 1, nskip = 0; si >=0; --si) { var s = series[si]; if (!s.zoom_show) continue; if (common_x_axes && si != zoom_first_show) continue; + // 1. Detect max record width/height by axis_rec_npoints equally selected points using format. double max_rec_width, max_rec_height; calc_axis_rec_sizes (s.axis_x, out max_rec_width, out max_rec_height, true); @@ -731,34 +727,24 @@ namespace Gtk.CairoChart { // 4.2. Cursor values for common X axis if (common_x_axes && cursors_orientation == CursorOrientation.VERTICAL && cursors_crossings.length != 0) { switch (s.axis_x.position) { - case Axis.Position.LOW: cur_y_max -= max_rec_height + s.axis_x.font_indent; break; - case Axis.Position.HIGH: cur_y_min += max_rec_height + s.axis_x.font_indent; break; - case Axis.Position.BOTH: break; + case Axis.Position.LOW: cur_y_max -= max_rec_height + s.axis_x.font_indent; break; + case Axis.Position.HIGH: cur_y_min += max_rec_height + s.axis_x.font_indent; break; } } // 4.5. Draw Axis title - if (s.axis_x.title.text != "") + if (s.axis_x.title.text != "") { + var scr_x = plot_area_x_min + (plot_area_x_max - plot_area_x_min) * (s.place.zoom_x_low + s.place.zoom_x_high) / 2.0; + double scr_y = 0.0; switch (s.axis_x.position) { - case Axis.Position.LOW: - var scr_x = plot_area_x_min + (plot_area_x_max - plot_area_x_min) * (s.place.zoom_x_low + s.place.zoom_x_high) / 2.0; - var scr_y = cur_y_max - s.axis_x.font_indent; - context.move_to(scr_x - s.axis_x.title.get_width(context) / 2.0, scr_y); - set_source_rgba(s.axis_x.color); - if (common_x_axes) set_source_rgba(Color(0,0,0,1)); - show_text(s.axis_x.title); - break; - case Axis.Position.HIGH: - var scr_x = plot_area_x_min + (plot_area_x_max - plot_area_x_min) * (s.place.zoom_x_low + s.place.zoom_x_high) / 2.0; - var scr_y = cur_y_min + s.axis_x.font_indent + s.axis_x.title.get_height(context); - context.move_to(scr_x - s.axis_x.title.get_width(context) / 2.0, scr_y); - set_source_rgba(s.axis_x.color); - if (common_x_axes) set_source_rgba(Color(0,0,0,1)); - show_text(s.axis_x.title); - break; - case Axis.Position.BOTH: - break; + case Axis.Position.LOW: scr_y = cur_y_max - s.axis_x.font_indent; break; + case Axis.Position.HIGH: scr_y = cur_y_min + s.axis_x.font_indent + s.axis_x.title.get_height(context); break; } + context.move_to(scr_x - s.axis_x.title.get_width(context) / 2.0, scr_y); + set_source_rgba(s.axis_x.color); + if (common_x_axes) set_source_rgba(Color(0,0,0,1)); + show_text(s.axis_x.title); + } // 5. Draw records, update cur_{x,y}_{min,max}. for (Float128 x = x_min, x_max = s.axis_x.zoom_max; point_belong (x, x_min, x_max); x += step) { @@ -776,37 +762,27 @@ namespace Gtk.CairoChart { ("%."+(s.axis_x.dsec_signs.to_string())+"Lf").printf((LongDouble)(x - (int64)x)).offset(1); time_text = dt.format(s.axis_x.time_format) + dsec_str; break; - default: - break; } - var scr_x = plot_area_x_min + (plot_area_x_max - plot_area_x_min) - * (s.place.zoom_x_low + (s.place.zoom_x_high - s.place.zoom_x_low) / (s.axis_x.zoom_max - s.axis_x.zoom_min) * (x - s.axis_x.zoom_min)); + var scr_x = get_scr_x (s, x); var text_t = new Text(text, s.axis_x.font_style, s.axis_x.color); switch (s.axis_x.position) { case Axis.Position.LOW: var print_y = cur_y_max - s.axis_x.font_indent - (s.axis_x.title.text == "" ? 0 : s.axis_x.title.get_height(context) + s.axis_x.font_indent); + var print_x = compact_rec_x_pos (s, x, text_t); + context.move_to (print_x, print_y); switch (s.axis_x.type) { case Axis.Type.NUMBERS: - var print_x = scr_x - text_t.get_width(context) / 2.0 - text_t.get_x_bearing(context) - - text_t.get_width(context) * (x - (s.axis_x.zoom_min + s.axis_x.zoom_max) / 2.0) / (s.axis_x.zoom_max - s.axis_x.zoom_min); - context.move_to (print_x, print_y); show_text(text_t); break; case Axis.Type.DATE_TIME: - var print_x = scr_x - text_t.get_width(context) / 2.0 - text_t.get_x_bearing(context) - - text_t.get_width(context) * (x - (s.axis_x.zoom_min + s.axis_x.zoom_max) / 2.0) / (s.axis_x.zoom_max - s.axis_x.zoom_min); - context.move_to (print_x, print_y); if (s.axis_x.date_format != "") show_text(text_t); var time_text_t = new Text(time_text, s.axis_x.font_style, s.axis_x.color); - print_x = scr_x - time_text_t.get_width(context) / 2.0 - time_text_t.get_x_bearing(context) - - time_text_t.get_width(context) * (x - (s.axis_x.zoom_min + s.axis_x.zoom_max) / 2.0) / (s.axis_x.zoom_max - s.axis_x.zoom_min); + print_x = compact_rec_x_pos (s, x, time_text_t); context.move_to (print_x, print_y - (s.axis_x.date_format == "" ? 0 : text_t.get_height(context) + s.axis_x.font_indent)); if (s.axis_x.time_format != "") show_text(time_text_t); break; - default: - break; } - // 6. Draw grid lines to the s.place.zoom_y_high. + // 6. Draw grid lines to the s.place.zoom_y_low. var line_style = s.grid.line_style; if (common_x_axes) line_style.color = Color(0, 0, 0, 0.5); set_line_style(line_style); @@ -819,26 +795,20 @@ namespace Gtk.CairoChart { break; case Axis.Position.HIGH: var print_y = cur_y_min + max_rec_height + s.axis_x.font_indent + (s.axis_x.title.text == "" ? 0 : s.axis_x.title.get_height(context) + s.axis_x.font_indent); + var print_x = compact_rec_x_pos (s, x, text_t); + context.move_to (print_x, print_y); + switch (s.axis_x.type) { case Axis.Type.NUMBERS: - var print_x = scr_x - text_t.get_width(context) / 2.0 - text_t.get_x_bearing(context) - - text_t.get_width(context) * (x - (s.axis_x.zoom_min + s.axis_x.zoom_max) / 2.0) / (s.axis_x.zoom_max - s.axis_x.zoom_min); - context.move_to (print_x, print_y); show_text(text_t); break; case Axis.Type.DATE_TIME: - var print_x = scr_x - text_t.get_width(context) / 2.0 - text_t.get_x_bearing(context) - - text_t.get_width(context) * (x - (s.axis_x.zoom_min + s.axis_x.zoom_max) / 2.0) / (s.axis_x.zoom_max - s.axis_x.zoom_min); - context.move_to (print_x, print_y); if (s.axis_x.date_format != "") show_text(text_t); var time_text_t = new Text(time_text, s.axis_x.font_style, s.axis_x.color); - print_x = scr_x - time_text_t.get_width(context) / 2.0 - time_text_t.get_x_bearing(context) - - time_text_t.get_width(context) * (x - (s.axis_x.zoom_min + s.axis_x.zoom_max) / 2.0) / (s.axis_x.zoom_max - s.axis_x.zoom_min); + print_x = compact_rec_x_pos (s, x, time_text_t); context.move_to (print_x, print_y - (s.axis_x.date_format == "" ? 0 : text_t.get_height(context) + s.axis_x.font_indent)); if (s.axis_x.time_format != "") show_text(time_text_t); break; - default: - break; } // 6. Draw grid lines to the s.place.zoom_y_high. var line_style = s.grid.line_style; @@ -851,10 +821,6 @@ namespace Gtk.CairoChart { else context.line_to (scr_x, double.max (y, plot_area_y_max - (plot_area_y_max - plot_area_y_min) * s.place.zoom_y_low)); break; - case Axis.Position.BOTH: - break; - default: - break; } context.stroke (); } @@ -892,9 +858,6 @@ namespace Gtk.CairoChart { cur_y_min += max_rec_height + s.axis_x.font_indent + (s.axis_x.title.text == "" ? 0 : s.axis_x.title.get_height(context) + s.axis_x.font_indent); break; - case Axis.Position.BOTH: - break; - default: break; } } } @@ -931,51 +894,43 @@ namespace Gtk.CairoChart { // 4.2. Cursor values for common Y axis if (common_y_axes && cursors_orientation == CursorOrientation.HORIZONTAL && cursors_crossings.length != 0) { switch (s.axis_y.position) { - case Axis.Position.LOW: cur_x_min += max_rec_width + s.axis_y.font_indent; break; - case Axis.Position.HIGH: cur_x_max -= max_rec_width + s.axis_y.font_indent; break; - case Axis.Position.BOTH: break; + case Axis.Position.LOW: cur_x_min += max_rec_width + s.axis_y.font_indent; break; + case Axis.Position.HIGH: cur_x_max -= max_rec_width + s.axis_y.font_indent; break; } } // 4.5. Draw Axis title - if (s.axis_y.title.text != "") + if (s.axis_y.title.text != "") { + var scr_y = plot_area_y_max - (plot_area_y_max - plot_area_y_min) * (s.place.zoom_y_low + s.place.zoom_y_high) / 2.0; switch (s.axis_y.position) { case Axis.Position.LOW: - var scr_y = plot_area_y_max - (plot_area_y_max - plot_area_y_min) * (s.place.zoom_y_low + s.place.zoom_y_high) / 2.0; var scr_x = cur_x_min + s.axis_y.font_indent + s.axis_y.title.get_width(context); context.move_to(scr_x, scr_y + s.axis_y.title.get_height(context) / 2.0); - set_source_rgba(s.axis_y.color); - if (common_y_axes) set_source_rgba(Color(0,0,0,1)); - show_text(s.axis_y.title); break; case Axis.Position.HIGH: - var scr_y = plot_area_y_max - (plot_area_y_max - plot_area_y_min) * (s.place.zoom_y_low + s.place.zoom_y_high) / 2.0; var scr_x = cur_x_max - s.axis_y.font_indent; context.move_to(scr_x, scr_y + s.axis_y.title.get_height(context) / 2.0); - set_source_rgba(s.axis_y.color); - if (common_y_axes) set_source_rgba(Color(0,0,0,1)); - show_text(s.axis_y.title); - break; - case Axis.Position.BOTH: break; } + set_source_rgba(s.axis_y.color); + if (common_y_axes) set_source_rgba(Color(0,0,0,1)); + show_text(s.axis_y.title); + } // 5. Draw records, update cur_{x,y}_{min,max}. for (Float128 y = y_min, y_max = s.axis_y.zoom_max; point_belong (y, y_min, y_max); y += step) { if (common_y_axes) set_source_rgba(Color(0,0,0,1)); else set_source_rgba(s.axis_y.color); var text = s.axis_y.format.printf((LongDouble)y); - var scr_y = plot_area_y_max - (plot_area_y_max - plot_area_y_min) - * (s.place.zoom_y_low + (s.place.zoom_y_high - s.place.zoom_y_low) / (s.axis_y.zoom_max - s.axis_y.zoom_min) * (y - s.axis_y.zoom_min)); + var scr_y = get_scr_y (s, y); var text_t = new Text(text, s.axis_y.font_style, s.axis_y.color); switch (s.axis_y.position) { case Axis.Position.LOW: context.move_to (cur_x_min + max_rec_width - (new Text(text)).get_width(context) + s.axis_y.font_indent - text_t.get_x_bearing(context) + (s.axis_y.title.text == "" ? 0 : s.axis_y.title.get_width(context) + s.axis_y.font_indent), - scr_y + (new Text(text)).get_height(context) / 2.0 - + text_t.get_height(context) * (y - (s.axis_y.zoom_min + s.axis_y.zoom_max) / 2.0) / (s.axis_y.zoom_max - s.axis_y.zoom_min)); + compact_rec_y_pos (s, y, new Text(text))); show_text(text_t); - // 6. Draw grid lines to the s.place.zoom_y_high. + // 6. Draw grid lines to the s.place.zoom_x_low. var line_style = s.grid.line_style; if (common_y_axes) line_style.color = Color(0, 0, 0, 0.5); set_line_style(line_style); @@ -989,10 +944,9 @@ namespace Gtk.CairoChart { case Axis.Position.HIGH: context.move_to (cur_x_max - (new Text(text)).get_width(context) - s.axis_y.font_indent - text_t.get_x_bearing(context) - (s.axis_y.title.text == "" ? 0 : s.axis_y.title.get_width(context) + s.axis_y.font_indent), - scr_y + (new Text(text)).get_height(context) / 2.0 - + text_t.get_height(context) * (y - (s.axis_y.zoom_min + s.axis_y.zoom_max) / 2.0) / (s.axis_y.zoom_max - s.axis_y.zoom_min)); + compact_rec_y_pos (s, y, new Text(text))); show_text(text_t); - // 6. Draw grid lines to the s.place.zoom_y_high. + // 6. Draw grid lines to the s.place.zoom_x_high. var line_style = s.grid.line_style; if (common_y_axes) line_style.color = Color(0, 0, 0, 0.5); set_line_style(line_style); @@ -1003,10 +957,6 @@ namespace Gtk.CairoChart { else context.line_to (double.min (x, plot_area_x_min + (plot_area_x_max - plot_area_x_min) * s.place.zoom_x_low), scr_y); break; - case Axis.Position.BOTH: - break; - default: - break; } context.stroke (); } @@ -1034,7 +984,6 @@ namespace Gtk.CairoChart { if (nskip != 0) {--nskip; continue;} - switch (s.axis_y.position) { case Axis.Position.LOW: cur_x_min += max_rec_width + s.axis_y.font_indent @@ -1042,9 +991,6 @@ namespace Gtk.CairoChart { case Axis.Position.HIGH: cur_x_max -= max_rec_width + s.axis_y.font_indent + (s.axis_y.title.text == "" ? 0 : s.axis_y.title.get_width(context) + s.axis_y.font_indent); break; - case Axis.Position.BOTH: - break; - default: break; } } } @@ -1276,8 +1222,6 @@ namespace Gtk.CairoChart { case CursorOrientation.HORIZONTAL: d = (rel2scr_y(c.y) - rel2scr_y(active_cursor.y)).abs(); break; - default: - break; } if (d < distance) { distance = d; @@ -1340,10 +1284,10 @@ namespace Gtk.CairoChart { for (var ci = 0, max_ci = all_cursors.length(); ci < max_ci; ++ci) { var c = all_cursors.nth_data(ci); switch (cursors_orientation) { - case CursorOrientation.VERTICAL: - if (c.x <= _rel_zoom_x_min || c.x >= _rel_zoom_x_max) continue; break; - case CursorOrientation.HORIZONTAL: - if (c.y <= _rel_zoom_y_min || c.y >= _rel_zoom_y_max) continue; break; + case CursorOrientation.VERTICAL: + if (c.x <= _rel_zoom_x_min || c.x >= _rel_zoom_x_max) continue; break; + case CursorOrientation.HORIZONTAL: + if (c.y <= _rel_zoom_y_min || c.y >= _rel_zoom_y_max) continue; break; } CursorCross[] crossings = {}; @@ -1353,33 +1297,32 @@ namespace Gtk.CairoChart { Point[] points = {}; switch (cursors_orientation) { - case CursorOrientation.VERTICAL: - points = sort_points (s, s.sort); - break; - case CursorOrientation.HORIZONTAL: - points = sort_points (s, s.sort); - break; + case CursorOrientation.VERTICAL: + points = sort_points (s, s.sort); + break; + case CursorOrientation.HORIZONTAL: + points = sort_points (s, s.sort); + break; } for (var i = 0; i + 1 < points.length; ++i) { switch (cursors_orientation) { - case CursorOrientation.VERTICAL: - Float128 y = 0.0; - if (vcross(get_scr_point(s, points[i]), get_scr_point(s, points[i+1]), rel2scr_x(c.x), - plot_area_y_min, plot_area_y_max, out y)) { - CursorCross cc = {si, Point(get_real_x(s, rel2scr_x(c.x)), get_real_y(s, y))}; - crossings += cc; -//stdout.printf("si = %d, rel2scr_x(c.x) = %f, y = %f\n", si, rel2scr_x(c.x), y); - } - break; - case CursorOrientation.HORIZONTAL: - Float128 x = 0.0; - if (hcross(get_scr_point(s, points[i]), get_scr_point(s, points[i+1]), - plot_area_x_min, plot_area_x_max, rel2scr_y(c.y), out x)) { - CursorCross cc = {si, Point(get_real_x(s, x), get_real_y(s, rel2scr_y(c.y)))}; - crossings += cc; - } - break; + case CursorOrientation.VERTICAL: + Float128 y = 0.0; + if (vcross(get_scr_point(s, points[i]), get_scr_point(s, points[i+1]), rel2scr_x(c.x), + plot_area_y_min, plot_area_y_max, out y)) { + CursorCross cc = {si, Point(get_real_x(s, rel2scr_x(c.x)), get_real_y(s, y))}; + crossings += cc; + } + break; + case CursorOrientation.HORIZONTAL: + Float128 x = 0.0; + if (hcross(get_scr_point(s, points[i]), get_scr_point(s, points[i+1]), + plot_area_x_min, plot_area_x_max, rel2scr_y(c.y), out x)) { + CursorCross cc = {si, Point(get_real_x(s, x), get_real_y(s, rel2scr_y(c.y)))}; + crossings += cc; + } + break; } } } @@ -1389,8 +1332,6 @@ namespace Gtk.CairoChart { } } cursors_crossings = local_cursor_crossings; -//if (cursors_crossings.length != 0) -//stdout.printf("cursors_crossings[0].crossings.length = %d\n", cursors_crossings[0].crossings.length); } protected virtual void draw_cursors () { @@ -1409,67 +1350,116 @@ namespace Gtk.CairoChart { var scrx = get_scr_x(s, p.x); var scry = get_scr_y(s, p.y); if (scrx < low.x) low.x = scrx; -//stdout.printf("low.y = %f, high.y = %f\n", low.y, high.y); if (scry < low.y) low.y = scry; if (scrx > high.x) high.x = scrx; if (scry > high.y) high.y = scry; if (common_x_axes) { switch (s.axis_x.position) { - case Axis.Position.LOW: high.y = plot_area_y_max + s.axis_x.font_indent; break; - case Axis.Position.HIGH: low.y = plot_area_y_min - s.axis_x.font_indent; break; - case Axis.Position.BOTH: - high.y = plot_area_y_max + s.axis_x.font_indent; - low.y = plot_area_y_min - s.axis_x.font_indent; - break; + case Axis.Position.LOW: high.y = plot_area_y_max + s.axis_x.font_indent; break; + case Axis.Position.HIGH: low.y = plot_area_y_min - s.axis_x.font_indent; break; + case Axis.Position.BOTH: + high.y = plot_area_y_max + s.axis_x.font_indent; + low.y = plot_area_y_min - s.axis_x.font_indent; + break; } } if (common_y_axes) { switch (s.axis_y.position) { - case Axis.Position.LOW: low.x = plot_area_x_min - s.axis_y.font_indent; break; - case Axis.Position.HIGH: high.x = plot_area_x_max + s.axis_y.font_indent; break; - case Axis.Position.BOTH: - low.x = plot_area_x_min - s.axis_y.font_indent; - high.x = plot_area_x_max + s.axis_y.font_indent; - break; + case Axis.Position.LOW: low.x = plot_area_x_min - s.axis_y.font_indent; break; + case Axis.Position.HIGH: high.x = plot_area_x_max + s.axis_y.font_indent; break; + case Axis.Position.BOTH: + low.x = plot_area_x_min - s.axis_y.font_indent; + high.x = plot_area_x_max + s.axis_y.font_indent; + break; } } } var c = all_cursors.nth_data(cursors_crossings[cci].cursor_index); + var s = series[zoom_first_show]; switch (cursors_orientation) { - case CursorOrientation.VERTICAL: - if (low.y > high.y) continue; - set_line_style(cursor_line_style); -//stdout.printf("rel2scr_x(c.x) = %f, low.y = %f\n", rel2scr_x(c.x), low.y); - context.move_to (rel2scr_x(c.x), low.y); - context.line_to (rel2scr_x(c.x), high.y); - context.stroke(); + case CursorOrientation.VERTICAL: + if (low.y > high.y) continue; + set_line_style(cursor_line_style); + context.move_to (rel2scr_x(c.x), low.y); + context.line_to (rel2scr_x(c.x), high.y); + context.stroke(); - // TODO: show values - if (common_x_axes) - // show only Y value - ; - else - // show [X;Y] - ; - break; - case CursorOrientation.HORIZONTAL: - if (low.x > high.x) continue; - set_line_style(cursor_line_style); - context.move_to (low.x, rel2scr_y(c.y)); - context.line_to (high.x, rel2scr_y(c.y)); - context.stroke(); + // TODO: show values + if (common_x_axes) { + // show common X value + var x = get_real_x(s, rel2scr_x(c.x)); + string text = "", time_text = ""; + switch (s.axis_x.type) { + case Axis.Type.NUMBERS: + text = s.axis_x.format.printf((LongDouble)x); + break; + case Axis.Type.DATE_TIME: + var dt = new DateTime.from_unix_utc((int64)x); + text = dt.format(s.axis_x.date_format); + var dsec_str = + ("%."+(s.axis_x.dsec_signs.to_string())+"Lf").printf((LongDouble)(x - (int64)x)).offset(1); + time_text = dt.format(s.axis_x.time_format) + dsec_str; + break; + default: + break; + } + var text_t = new Text(text, s.axis_x.font_style, s.axis_x.color); + var time_text_t = new Text(time_text, s.axis_x.font_style, s.axis_x.color); + var print_y = 0.0; + switch (s.axis_x.position) { + case Axis.Position.LOW: print_y = y_min + height - s.axis_x.font_indent + - (legend.position == Legend.Position.BOTTOM ? legend_height : 0); + break; + case Axis.Position.HIGH: print_y = y_min + title_height + s.axis_x.font_indent + + (legend.position == Legend.Position.TOP ? legend_height : 0) + + (s.axis_x.date_format == "" ? 0 : text_t.get_height(context)) + + (s.axis_x.time_format == "" ? 0 : time_text_t.get_height(context)) + + (s.axis_x.date_format == "" || s.axis_x.time_format == "" ? 0 : s.axis_x.font_indent); + break; + } + var print_x = compact_rec_x_pos (s, x, text_t); + context.move_to (print_x, print_y); - // TODO: show values - if (common_y_axes) - // show only X value - ; - else - // show [X;Y] - ; - break; + switch (s.axis_x.type) { + case Axis.Type.NUMBERS: + show_text(text_t); + break; + case Axis.Type.DATE_TIME: + if (s.axis_x.date_format != "") show_text(text_t); + print_x = compact_rec_x_pos (s, x, time_text_t); + context.move_to (print_x, print_y - (s.axis_x.date_format == "" ? 0 : text_t.get_height(context) + s.axis_x.font_indent)); + if (s.axis_x.time_format != "") show_text(time_text_t); + break; + } + + context.stroke (); + + // show only Y value + ; + } else { + // show [X;Y] + } + break; + case CursorOrientation.HORIZONTAL: + if (low.x > high.x) continue; + set_line_style(cursor_line_style); + context.move_to (low.x, rel2scr_y(c.y)); + context.line_to (high.x, rel2scr_y(c.y)); + context.stroke(); + + // TODO: show values + if (common_y_axes) { + // show common Y value + + // show only X value + ; + } else + // show [X;Y] + ; + break; } } } diff --git a/test/ChartTest.vala b/test/ChartTest.vala index 33916a9..1f51ca3 100644 --- a/test/ChartTest.vala +++ b/test/ChartTest.vala @@ -83,6 +83,9 @@ void plot_chart2 (Chart chart) { s3.axis_x.title = new Text("All Series: Axis X."); s3.axis_y.title = new Text("Series 3: Axis Y."); + //s1.axis_x.position = s2.axis_x.position = s3.axis_x.position = Axis.Position.HIGH; + //s1.axis_x.type = s2.axis_x.type = s3.axis_x.type = Axis.Type.DATE_TIME; + chart.series = { s1, s2, s3 }; } @@ -261,7 +264,6 @@ int main (string[] args) { case Legend.Position.RIGHT: radio_button2.set_active(true); break; case Legend.Position.LEFT: radio_button3.set_active(true); break; case Legend.Position.BOTTOM: radio_button4.set_active(true); break; - default: break; } switch (chart.cursors_orientation) { case Chart.CursorOrientation.VERTICAL: radio_button7.set_active(true); break; @@ -275,7 +277,6 @@ int main (string[] args) { case Legend.Position.RIGHT: radio_button2.set_active(true); break; case Legend.Position.LEFT: radio_button3.set_active(true); break; case Legend.Position.BOTTOM: radio_button4.set_active(true); break; - default: break; } switch (chart.cursors_orientation) { case Chart.CursorOrientation.VERTICAL: radio_button7.set_active(true); break; @@ -289,7 +290,6 @@ int main (string[] args) { case Legend.Position.RIGHT: radio_button2.set_active(true); break; case Legend.Position.LEFT: radio_button3.set_active(true); break; case Legend.Position.BOTTOM: radio_button4.set_active(true); break; - default: break; } switch (chart.cursors_orientation) { case Chart.CursorOrientation.VERTICAL: radio_button7.set_active(true); break; @@ -303,7 +303,6 @@ int main (string[] args) { case Legend.Position.RIGHT: radio_button2.set_active(true); break; case Legend.Position.LEFT: radio_button4.set_active(true); break; case Legend.Position.BOTTOM: radio_button4.set_active(true); break; - default: break; } switch (chart.cursors_orientation) { case Chart.CursorOrientation.VERTICAL: radio_button7.set_active(true); break; From f4b89194d61d38716715a0c062f7aca107227cc3 Mon Sep 17 00:00:00 2001 From: Kolan Sh Date: Thu, 7 Dec 2017 04:21:27 +0300 Subject: [PATCH 6/7] Fix cursors value on common axes. --- src/Chart.vala | 33 +++++++++++++++++++++++++++++---- test/ChartTest.vala | 2 ++ 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/src/Chart.vala b/src/Chart.vala index 2501b17..3a3a866 100644 --- a/src/Chart.vala +++ b/src/Chart.vala @@ -1414,10 +1414,17 @@ namespace Gtk.CairoChart { - (legend.position == Legend.Position.BOTTOM ? legend_height : 0); break; case Axis.Position.HIGH: print_y = y_min + title_height + s.axis_x.font_indent - + (legend.position == Legend.Position.TOP ? legend_height : 0) - + (s.axis_x.date_format == "" ? 0 : text_t.get_height(context)) - + (s.axis_x.time_format == "" ? 0 : time_text_t.get_height(context)) - + (s.axis_x.date_format == "" || s.axis_x.time_format == "" ? 0 : s.axis_x.font_indent); + + (legend.position == Legend.Position.TOP ? legend_height : 0); + switch (s.axis_x.type) { + case Axis.Type.NUMBERS: + print_y += text_t.get_height(context); + break; + case Axis.Type.DATE_TIME: + print_y += (s.axis_x.date_format == "" ? 0 : text_t.get_height(context)) + + (s.axis_x.time_format == "" ? 0 : time_text_t.get_height(context)) + + (s.axis_x.date_format == "" || s.axis_x.time_format == "" ? 0 : s.axis_x.font_indent); + break; + } break; } var print_x = compact_rec_x_pos (s, x, text_t); @@ -1453,6 +1460,24 @@ namespace Gtk.CairoChart { // TODO: show values if (common_y_axes) { // show common Y value + var y = get_real_y(s, rel2scr_y(c.y)); + var text_t = new Text(s.axis_y.format.printf((LongDouble)y)); + var print_y = compact_rec_y_pos (s, y, text_t); + var print_x = 0.0; + switch (s.axis_y.position) { + case Axis.Position.LOW: + print_x = x_min + s.axis_y.font_indent + + (legend.position == Legend.Position.LEFT ? legend_width : 0); + break; + case Axis.Position.HIGH: + print_x = x_min + width - text_t.get_width(context) - s.axis_y.font_indent + - (legend.position == Legend.Position.RIGHT ? legend_width : 0); + break; + } + context.move_to (print_x, print_y); + show_text(text_t); + + context.stroke (); // show only X value ; diff --git a/test/ChartTest.vala b/test/ChartTest.vala index 1f51ca3..6ef3eec 100644 --- a/test/ChartTest.vala +++ b/test/ChartTest.vala @@ -131,6 +131,8 @@ void plot_chart3 (Chart chart) { s3.axis_x.title = new Text("Series 3: Axis X."); s3.axis_y.title = new Text("Series 3: Axis Y."); + //s1.axis_y.position = s2.axis_y.position = s3.axis_y.position = Axis.Position.LOW; + chart.series = { s1, s2, s3 }; } From c676f7eea0c30cd66be4290c24e4f91583733ccd Mon Sep 17 00:00:00 2001 From: Kolan Sh Date: Sun, 10 Dec 2017 03:42:29 +0300 Subject: [PATCH 7/7] Show cursors crossings values. --- src/Chart.vala | 233 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 183 insertions(+), 50 deletions(-) diff --git a/src/Chart.vala b/src/Chart.vala index 3a3a866..f3f7ab4 100644 --- a/src/Chart.vala +++ b/src/Chart.vala @@ -463,12 +463,12 @@ namespace Gtk.CairoChart { process_legend (LegendProcessType.DRAW); } - int axis_rec_npoints = 128; + protected int axis_rec_npoints = 128; protected virtual void calc_axis_rec_sizes (Axis axis, out double max_rec_width, out double max_rec_height, bool is_horizontal = true) { max_rec_width = max_rec_height = 0; for (var i = 0; i < axis_rec_npoints; ++i) { - Float128 x = axis.zoom_min + (axis.zoom_max - axis.zoom_min) / axis_rec_npoints * i; + Float128 x = (int64)(axis.zoom_min + (axis.zoom_max - axis.zoom_min) / axis_rec_npoints * i) + 1.0/3.0; switch (axis.type) { case Axis.Type.NUMBERS: var text = new Text (axis.format.printf((LongDouble)x) + (is_horizontal ? "_" : "")); @@ -477,18 +477,19 @@ namespace Gtk.CairoChart { max_rec_height = double.max (max_rec_height, text.get_height(context)); break; case Axis.Type.DATE_TIME: - var dt = new DateTime.from_unix_utc((int64)x); + string date, time; + format_date_time(axis, x, out date, out time); + var text = new Text(""); var h = 0.0; if (axis.date_format != "") { - text = new Text (dt.format(axis.date_format) + (is_horizontal ? "_" : "")); + text = new Text (date + (is_horizontal ? "_" : "")); text.style = axis.font_style; max_rec_width = double.max (max_rec_width, text.get_width(context)); h = text.get_height(context); } if (axis.time_format != "") { - var dsec_str = ("%."+(axis.dsec_signs.to_string())+"f").printf(1.0/3.0).offset(1); - text = new Text (dt.format(axis.time_format) + (is_horizontal ? "_" : "") + dsec_str); + text = new Text (time + (is_horizontal ? "_" : "")); text.style = axis.font_style; max_rec_width = double.max (max_rec_width, text.get_width(context)); h += text.get_height(context); @@ -541,14 +542,14 @@ namespace Gtk.CairoChart { } protected virtual void calc_plot_area () { - plot_area_x_min = cur_x_min + legend.indent; - plot_area_x_max = cur_x_max - legend.indent; - plot_area_y_min = cur_y_min + legend.indent; - plot_area_y_max = cur_y_max - legend.indent; + plot_area_x_min = cur_x_min + legend.indent; + plot_area_x_max = cur_x_max - legend.indent; + plot_area_y_min = cur_y_min + legend.indent; + plot_area_y_max = cur_y_max - legend.indent; - // Check for common axes - common_x_axes = common_y_axes = true; - int nzoom_series_show = 0; + // Check for common axes + common_x_axes = common_y_axes = true; + int nzoom_series_show = 0; for (var si = series.length - 1; si >=0; --si) { var s = series[si]; if (!s.zoom_show) continue; @@ -694,6 +695,15 @@ namespace Gtk.CairoChart { + text.get_height(context) * (y - (s.axis_y.zoom_min + s.axis_y.zoom_max) / 2.0) / (s.axis_y.zoom_max - s.axis_y.zoom_min); } + protected virtual void format_date_time (Axis axis, Float128 x, out string date, out string time) { + date = time = ""; + var dt = new DateTime.from_unix_utc((int64)x); + date = dt.format(axis.date_format); + var dsec_str = + ("%."+(axis.dsec_signs.to_string())+"Lf").printf((LongDouble)(x - (int64)x)).offset(1); + time = dt.format(axis.time_format) + dsec_str; + } + protected virtual void draw_horizontal_axis () { for (var si = series.length - 1, nskip = 0; si >=0; --si) { var s = series[si]; @@ -756,11 +766,7 @@ namespace Gtk.CairoChart { text = s.axis_x.format.printf((LongDouble)x); break; case Axis.Type.DATE_TIME: - var dt = new DateTime.from_unix_utc((int64)x); - text = dt.format(s.axis_x.date_format); - var dsec_str = - ("%."+(s.axis_x.dsec_signs.to_string())+"Lf").printf((LongDouble)(x - (int64)x)).offset(1); - time_text = dt.format(s.axis_x.time_format) + dsec_str; + format_date_time(s.axis_x, x, out text, out time_text); break; } var scr_x = get_scr_x (s, x); @@ -1261,6 +1267,13 @@ namespace Gtk.CairoChart { protected struct CursorCross { uint series_index; Point point; + Point size; + bool show_x; + bool show_date; + bool show_time; + bool show_y; + Point scr_point; + Point scr_value_point; } protected struct CursorCrossings { uint cursor_index; @@ -1311,16 +1324,24 @@ namespace Gtk.CairoChart { Float128 y = 0.0; if (vcross(get_scr_point(s, points[i]), get_scr_point(s, points[i+1]), rel2scr_x(c.x), plot_area_y_min, plot_area_y_max, out y)) { - CursorCross cc = {si, Point(get_real_x(s, rel2scr_x(c.x)), get_real_y(s, y))}; - crossings += cc; + var point = Point(get_real_x(s, rel2scr_x(c.x)), get_real_y(s, y)); + Point size; bool show_x, show_date, show_time, show_y; + cross_what_to_show(s, out show_x, out show_time, out show_date, out show_y); + calc_cross_sizes (s, point, out size, show_x, show_time, show_date, show_y); + CursorCross cc = {si, point, size, show_x, show_date, show_time, show_y}; + crossings += cc; } break; case CursorOrientation.HORIZONTAL: Float128 x = 0.0; if (hcross(get_scr_point(s, points[i]), get_scr_point(s, points[i+1]), plot_area_x_min, plot_area_x_max, rel2scr_y(c.y), out x)) { - CursorCross cc = {si, Point(get_real_x(s, x), get_real_y(s, rel2scr_y(c.y)))}; - crossings += cc; + var point = Point(get_real_x(s, x), get_real_y(s, rel2scr_y(c.y))); + Point size; bool show_x, show_date, show_time, show_y; + cross_what_to_show(s, out show_x, out show_time, out show_date, out show_y); + calc_cross_sizes (s, point, out size, show_x, show_time, show_date, show_y); + CursorCross cc = {si, point, size, show_x, show_date, show_time, show_y}; + crossings += cc; } break; } @@ -1334,15 +1355,79 @@ namespace Gtk.CairoChart { cursors_crossings = local_cursor_crossings; } + protected virtual void calc_cursors_value_positions () { + for (var ccsi = 0, max_ccsi = cursors_crossings.length; ccsi < max_ccsi; ++ccsi) { + for (var cci = 0, max_cci = cursors_crossings[ccsi].crossings.length; cci < max_cci; ++cci) { + // TODO: Ticket #142: find smart algorithm of cursors values placements + unowned CursorCross[] cr = cursors_crossings[ccsi].crossings; + cr[cci].scr_point = get_scr_point (series[cr[cci].series_index], cr[cci].point); + var d_max = double.max (cr[cci].size.x / 1.5, cr[cci].size.y / 1.5); + cr[cci].scr_value_point = Point (cr[cci].scr_point.x + d_max, cr[cci].scr_point.y - d_max); + } + } + } + + protected virtual void cross_what_to_show (Series s, out bool show_x, out bool show_time, + out bool show_date, out bool show_y) { + show_x = show_time = show_date = show_y = false; + switch (cursors_orientation) { + case CursorOrientation.VERTICAL: + show_y = true; + if (!common_x_axes) + switch (s.axis_x.type) { + case Axis.Type.NUMBERS: show_x = true; break; + case Axis.Type.DATE_TIME: + if (s.axis_x.date_format != "") show_date = true; + if (s.axis_x.time_format != "") show_time = true; + break; + } + break; + case CursorOrientation.HORIZONTAL: + if (!common_y_axes) show_y = true; + switch (s.axis_x.type) { + case Axis.Type.NUMBERS: show_x = true; break; + case Axis.Type.DATE_TIME: + if (s.axis_x.date_format != "") show_date = true; + if (s.axis_x.time_format != "") show_time = true; + break; + } + break; + } + } + + protected virtual void calc_cross_sizes (Series s, Point p, out Point size, + bool show_x = false, bool show_time = false, + bool show_date = false, bool show_y = false) { + if (show_x == show_time == show_date == show_y == false) + cross_what_to_show(s, out show_x, out show_time, out show_date, out show_y); + size = Point (); + double x_w = 0.0, x_h = 0.0, y_w = 0.0, y_h = 0.0; + string date, time; + format_date_time(s.axis_x, p.x, out date, out time); + var date_t = new Text (date, s.axis_x.font_style, s.axis_x.color); + var time_t = new Text (time, s.axis_x.font_style, s.axis_x.color); + var x_t = new Text (s.axis_x.format.printf((LongDouble)p.x), s.axis_x.font_style, s.axis_x.color); + var y_t = new Text (s.axis_y.format.printf((LongDouble)p.y), s.axis_y.font_style, s.axis_y.color); + double h_x = 0.0, h_y = 0.0; + if (show_x) { size.x = x_t.get_width(context); h_x = x_t.get_height(context); } + if (show_date) { size.x = date_t.get_width(context); h_x = date_t.get_height(context); } + if (show_time) { size.x = double.max(size.x, time_t.get_width(context)); h_x += time_t.get_height(context); } + if (show_y) { size.x += y_t.get_width(context); h_y = y_t.get_height(context); } + if ((show_x || show_date || show_time) && show_y) size.x += double.max(s.axis_x.font_indent, s.axis_y.font_indent); + if (show_date && show_time) h_x += s.axis_x.font_indent; + size.y = double.max (h_x, h_y); + } + protected virtual void draw_cursors () { if (series.length == 0) return; var all_cursors = get_all_cursors(); + calc_cursors_value_positions(); for (var cci = 0, max_cci = cursors_crossings.length; cci < max_cci; ++cci) { var low = Point(plot_area_x_max, plot_area_y_max); // low and high var high = Point(plot_area_x_min, plot_area_y_min); // points of the cursor - var ccs = cursors_crossings[cci].crossings; + unowned CursorCross[] ccs = cursors_crossings[cci].crossings; for (var ci = 0, max_ci = ccs.length; ci < max_ci; ++ci) { var si = ccs[ci].series_index; var s = series[si]; @@ -1374,10 +1459,14 @@ namespace Gtk.CairoChart { break; } } + + set_line_style(cursor_line_style); + context.move_to (ccs[ci].scr_point.x, ccs[ci].scr_point.y); + context.line_to (ccs[ci].scr_value_point.x, ccs[ci].scr_value_point.y); + context.stroke (); } var c = all_cursors.nth_data(cursors_crossings[cci].cursor_index); - var s = series[zoom_first_show]; switch (cursors_orientation) { case CursorOrientation.VERTICAL: @@ -1387,9 +1476,9 @@ namespace Gtk.CairoChart { context.line_to (rel2scr_x(c.x), high.y); context.stroke(); - // TODO: show values + // show common X value if (common_x_axes) { - // show common X value + var s = series[zoom_first_show]; var x = get_real_x(s, rel2scr_x(c.x)); string text = "", time_text = ""; switch (s.axis_x.type) { @@ -1397,11 +1486,7 @@ namespace Gtk.CairoChart { text = s.axis_x.format.printf((LongDouble)x); break; case Axis.Type.DATE_TIME: - var dt = new DateTime.from_unix_utc((int64)x); - text = dt.format(s.axis_x.date_format); - var dsec_str = - ("%."+(s.axis_x.dsec_signs.to_string())+"Lf").printf((LongDouble)(x - (int64)x)).offset(1); - time_text = dt.format(s.axis_x.time_format) + dsec_str; + format_date_time(s.axis_x, x, out text, out time_text); break; default: break; @@ -1411,19 +1496,19 @@ namespace Gtk.CairoChart { var print_y = 0.0; switch (s.axis_x.position) { case Axis.Position.LOW: print_y = y_min + height - s.axis_x.font_indent - - (legend.position == Legend.Position.BOTTOM ? legend_height : 0); + - (legend.position == Legend.Position.BOTTOM ? legend_height : 0); break; case Axis.Position.HIGH: print_y = y_min + title_height + s.axis_x.font_indent - + (legend.position == Legend.Position.TOP ? legend_height : 0); + + (legend.position == Legend.Position.TOP ? legend_height : 0); switch (s.axis_x.type) { case Axis.Type.NUMBERS: print_y += text_t.get_height(context); break; case Axis.Type.DATE_TIME: print_y += (s.axis_x.date_format == "" ? 0 : text_t.get_height(context)) - + (s.axis_x.time_format == "" ? 0 : time_text_t.get_height(context)) - + (s.axis_x.date_format == "" || s.axis_x.time_format == "" ? 0 : s.axis_x.font_indent); - break; + + (s.axis_x.time_format == "" ? 0 : time_text_t.get_height(context)) + + (s.axis_x.date_format == "" || s.axis_x.time_format == "" ? 0 : s.axis_x.font_indent); + break; } break; } @@ -1443,11 +1528,6 @@ namespace Gtk.CairoChart { } context.stroke (); - - // show only Y value - ; - } else { - // show [X;Y] } break; case CursorOrientation.HORIZONTAL: @@ -1457,9 +1537,9 @@ namespace Gtk.CairoChart { context.line_to (high.x, rel2scr_y(c.y)); context.stroke(); - // TODO: show values + // show common Y value if (common_y_axes) { - // show common Y value + var s = series[zoom_first_show]; var y = get_real_y(s, rel2scr_y(c.y)); var text_t = new Text(s.axis_y.format.printf((LongDouble)y)); var print_y = compact_rec_y_pos (s, y, text_t); @@ -1478,14 +1558,67 @@ namespace Gtk.CairoChart { show_text(text_t); context.stroke (); - - // show only X value - ; - } else - // show [X;Y] - ; + } break; } + + // show value (X, Y or [X;Y]) + for (var ci = 0, max_ci = ccs.length; ci < max_ci; ++ci) { + var si = ccs[ci].series_index; + var s = series[si]; + var point = ccs[ci].point; + var size = ccs[ci].size; + var svp = ccs[ci].scr_value_point; + var rp = get_real_point(s, rel2scr_point(point)); + var show_x = ccs[ci].show_x; + var show_date = ccs[ci].show_date; + var show_time = ccs[ci].show_time; + var show_y = ccs[ci].show_y; + + set_source_rgba(bg_color); + context.rectangle (svp.x - size.x / 2, svp.y - size.y / 2, size.x, size.y); + context.fill(); + + if (show_x) { + set_source_rgba(s.axis_x.color); + var text_t = new Text(s.axis_x.format.printf((LongDouble)point.x)); + context.move_to (svp.x - size.x / 2, svp.y + text_t.get_height(context) / 2); + show_text(text_t); + context.stroke(); + } + + if (show_time) { + set_source_rgba(s.axis_x.color); + string date = "", time = ""; + format_date_time(s.axis_x, point.x, out date, out time); + var text_t = new Text(time); + var y = svp.y + text_t.get_height(context) / 2; + if (show_date) y -= text_t.get_height(context) / 2 + s.axis_x.font_indent / 2; + context.move_to (svp.x - size.x / 2, y); + show_text(text_t); + context.stroke(); + } + + if (show_date) { + set_source_rgba(s.axis_x.color); + string date = "", time = ""; + format_date_time(s.axis_x, point.x, out date, out time); + var text_t = new Text(date); + var y = svp.y + text_t.get_height(context) / 2; + if (show_time) y += text_t.get_height(context) / 2 + s.axis_x.font_indent / 2; + context.move_to (svp.x - size.x / 2, y); + show_text(text_t); + context.stroke(); + } + + if (show_y) { + set_source_rgba(s.axis_y.color); + var text_t = new Text(s.axis_y.format.printf((LongDouble)point.y)); + context.move_to (svp.x + size.x / 2 - text_t.get_width(context), svp.y + text_t.get_height(context) / 2); + show_text(text_t); + context.stroke(); + } + } } } @@ -1528,7 +1661,7 @@ namespace Gtk.CairoChart { chart.selection_style = this.selection_style; chart.series = this.series.copy(); chart.show_legend = this.show_legend; - chart.title = this.title.copy().copy(); + chart.title = this.title.copy(); chart.title_height = this.title_height; chart.title_vindent = this.title_vindent; chart.title_width = this.title_width;