Closes #130: Zoom added.

This commit is contained in:
Kolan Sh 2017-08-22 11:54:18 +03:00
parent 8c2d84c129
commit 0e1a746639
5 changed files with 341 additions and 144 deletions

View File

@ -3,8 +3,20 @@ namespace Gtk.CairoChart {
// then draw separate axis for each/all series
// or specify series name near the axis
public class Axis {
public Float128 min = 0;
public Float128 max = 1;
Float128 _min = 0;
Float128 _max = 0;
public Float128 min {
get { return _min; }
set { _min = zoom_min = value; }
default = 0;
}
public Float128 max {
get { return _max; }
set { _max = zoom_max = value; }
default = 1;
}
public Float128 zoom_min = 0;
public Float128 zoom_max = 1;
public Text title = new Text ("");
public enum Type {
NUMBERS = 0,

View File

@ -14,7 +14,19 @@ namespace Gtk.CairoChart {
public Legend legend = new Legend ();
public Series[] series = {};
Series[] _series = {};
public Series[] series {
get { return _series; }
set {
_series = value.copy();
zoom_series = value.copy();
for (var i = 0; i < value.length; ++i) {
_series[i] = value[i].copy();
zoom_series[i] = value[i].copy();
}
}
}
public Series[] zoom_series = {};
protected LineStyle selection_style = LineStyle ();
@ -91,6 +103,74 @@ namespace Gtk.CairoChart {
}
}
Series[] rm_series_by_idx (Series[] series, int idx) {
Series[] new_series = series.copy();
for (var i = idx + 1; i < series.length; ++i)
new_series[i - 1] = series[i];
new_series.length--;
return new_series;
}
public virtual void zoom_in (double x0, double y0, double x1, double y1) {
for (var i = 0, max_i = zoom_series.length; i < max_i; ++i) {
var s = zoom_series[i];
var real_x0 = get_real_x (s, x0);
var real_x1 = get_real_x (s, x1);
var real_y0 = get_real_y (s, y0);
var real_y1 = get_real_y (s, y1);
// if selected square does not intersect with the zoom_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) {
zoom_series = rm_series_by_idx (zoom_series, i);
--i;
--max_i;
continue;
}
if (real_x0 >= s.axis_x.zoom_min) {
s.axis_x.zoom_min = real_x0;
s.place.zoom_x_low = 0.0;
} else {
s.place.zoom_x_low = (s.axis_x.zoom_min - real_x0) / (real_x1 - real_x0);
}
if (real_x1 <= s.axis_x.zoom_max) {
s.axis_x.zoom_max = real_x1;
s.place.zoom_x_high = 1.0;
} else {
s.place.zoom_x_high = (s.axis_x.zoom_max - real_x0) / (real_x1 - real_x0);
}
if (real_y1 >= s.axis_y.zoom_min) {
s.axis_y.zoom_min = real_y1;
s.place.zoom_y_low = 0.0;
} else {
s.place.zoom_y_low = (s.axis_y.zoom_min - real_y1) / (real_y0 - real_y1);
}
if (real_y0 <= s.axis_y.zoom_max) {
s.axis_y.zoom_max = real_y0;
s.place.zoom_y_high = 1.0;
} else {
s.place.zoom_y_high = (s.axis_y.zoom_max - real_y1) / (real_y0 - real_y1);
}
}
}
public virtual void zoom_out () {
zoom_series = _series.copy();
for (var i = 0; i < _series.length; ++i) {
_series[i] = _series[i].copy();
zoom_series[i] = _series[i].copy();
}
foreach (var s in zoom_series) {
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;
s.axis_y.zoom_max = s.axis_y.max;
s.place.zoom_x_low = s.place.x_low;
s.place.zoom_x_high = s.place.x_high;
s.place.zoom_y_low = s.place.y_low;
s.place.zoom_y_high = s.place.y_high;
}
}
protected double title_width = 0.0;
protected double title_height = 0.0;
@ -249,7 +329,7 @@ namespace Gtk.CairoChart {
break;
}
foreach (var s in series) {
foreach (var s in zoom_series) {
// carry
switch (legend.position) {
@ -278,12 +358,12 @@ namespace Gtk.CairoChart {
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
// zoom_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
// zoom_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);
@ -363,7 +443,7 @@ namespace Gtk.CairoChart {
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.min + (axis.max - axis.min) / axis_rec_npoints * i;
Float128 x = axis.zoom_min + (axis.zoom_max - axis.zoom_min) / axis_rec_npoints * i;
switch (axis.type) {
case Axis.Type.NUMBERS:
var text = new Text (axis.format.printf((LongDouble)x) + (is_horizontal ? "_" : ""));
@ -407,7 +487,6 @@ namespace Gtk.CairoChart {
if (step / 5 > aver_step) step /= 5;
while (step / 2 > aver_step) step /= 2;
} else if (aver_step > 0) {
//stdout.printf("aver_step = %Lf\n", aver_step);
while (step / 10 > aver_step) step /= 10;
if (step / 5 > aver_step) step /= 5;
while (step / 2 > aver_step) step /= 2;
@ -432,8 +511,8 @@ namespace Gtk.CairoChart {
}
protected virtual void set_vertical_axes_titles () {
for (var i = 0; i < series.length; ++i) {
var s = series[i];
for (var i = 0; i < zoom_series.length; ++i) {
var s = zoom_series[i];
s.axis_y.title.style.orientation = FontOrient.VERTICAL;
}
}
@ -446,28 +525,28 @@ namespace Gtk.CairoChart {
// Check for common axes
common_x_axes = common_y_axes = true;
for (int si = series.length - 1; si >=0; --si) {
var s = series[si];
if ( s.axis_x.position != series[0].axis_x.position
|| s.axis_x.min != series[0].axis_x.min
|| s.axis_x.max != series[0].axis_x.max
|| s.place.x_low != series[0].place.x_low
|| s.place.x_high != series[0].place.x_high
|| s.axis_x.type != series[0].axis_x.type)
for (int si = zoom_series.length - 1; si >=0; --si) {
var s = zoom_series[si];
if ( s.axis_x.position != zoom_series[0].axis_x.position
|| s.axis_x.zoom_min != zoom_series[0].axis_x.zoom_min
|| s.axis_x.zoom_max != zoom_series[0].axis_x.zoom_max
|| s.place.zoom_x_low != zoom_series[0].place.zoom_x_low
|| s.place.zoom_x_high != zoom_series[0].place.zoom_x_high
|| s.axis_x.type != zoom_series[0].axis_x.type)
common_x_axes = false;
if ( s.axis_y.position != series[0].axis_y.position
|| s.axis_y.min != series[0].axis_y.min
|| s.axis_y.max != series[0].axis_y.max
|| s.place.y_low != series[0].place.y_low
|| s.place.y_high != series[0].place.y_high)
if ( s.axis_y.position != zoom_series[0].axis_y.position
|| s.axis_y.zoom_min != zoom_series[0].axis_y.zoom_min
|| s.axis_y.zoom_max != zoom_series[0].axis_y.zoom_max
|| s.place.zoom_y_low != zoom_series[0].place.zoom_y_low
|| s.place.zoom_y_high != zoom_series[0].place.zoom_y_high)
common_y_axes = false;
}
if (series.length == 1) common_x_axes = common_y_axes = false;
if (zoom_series.length == 1) common_x_axes = common_y_axes = false;
// Join and calc X-axes
for (int si = series.length - 1, nskip = 0; si >=0; --si) {
for (int si = zoom_series.length - 1, nskip = 0; si >=0; --si) {
if (nskip != 0) {--nskip; continue;}
var s = series[si];
var s = zoom_series[si];
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);
var max_font_indent = s.axis_x.font_indent;
@ -475,11 +554,11 @@ namespace Gtk.CairoChart {
// join relative x-axes with non-intersect places
for (int sj = si - 1; sj >= 0; --sj) {
var s2 = series[sj];
var s2 = zoom_series[sj];
bool has_intersection = false;
for (int sk = si; sk > sj; --sk) {
var s3 = series[sk];
if (are_intersect(s2.place.x_low, s2.place.x_high, s3.place.x_low, s3.place.x_high)
var s3 = zoom_series[sk];
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) {
has_intersection = true;
@ -510,9 +589,9 @@ namespace Gtk.CairoChart {
}
// Join and calc Y-axes
for (int si = series.length - 1, nskip = 0; si >=0; --si) {
for (int si = zoom_series.length - 1, nskip = 0; si >=0; --si) {
if (nskip != 0) {--nskip; continue;}
var s = series[si];
var s = zoom_series[si];
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);
var max_font_indent = s.axis_y.font_indent;
@ -520,11 +599,11 @@ namespace Gtk.CairoChart {
// join relative x-axes with non-intersect places
for (int sj = si - 1; sj >= 0; --sj) {
var s2 = series[sj];
var s2 = zoom_series[sj];
bool has_intersection = false;
for (int sk = si; sk > sj; --sk) {
var s3 = series[sk];
if (are_intersect(s2.place.y_low, s2.place.y_high, s3.place.y_low, s3.place.y_high)
var s3 = zoom_series[sk];
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) {
has_intersection = true;
@ -562,38 +641,38 @@ namespace Gtk.CairoChart {
}
protected virtual void draw_horizontal_axis () {
for (int si = series.length - 1, nskip = 0; si >=0; --si) {
for (int si = zoom_series.length - 1, nskip = 0; si >=0; --si) {
if (common_x_axes && si != 0) continue;
var s = series[si];
var s = zoom_series[si];
// 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);
// 2. Calculate maximal available number of records, take into account the space width.
long max_nrecs = (long) ((plot_area_x_max - plot_area_x_min) * (s.place.x_high - s.place.x_low) / max_rec_width);
long max_nrecs = (long) ((plot_area_x_max - plot_area_x_min) * (s.place.zoom_x_high - s.place.zoom_x_low) / max_rec_width);
// 3. Calculate grid step.
Float128 step = calc_round_step ((s.axis_x.max - s.axis_x.min) / max_nrecs, s.axis_x.type == Axis.Type.DATE_TIME);
if (step > s.axis_x.max - s.axis_x.min)
step = s.axis_x.max - s.axis_x.min;
Float128 step = calc_round_step ((s.axis_x.zoom_max - s.axis_x.zoom_min) / max_nrecs, s.axis_x.type == Axis.Type.DATE_TIME);
if (step > s.axis_x.zoom_max - s.axis_x.zoom_min)
step = s.axis_x.zoom_max - s.axis_x.zoom_min;
// 4. Calculate x_min (s.axis_x.min / step, round, multiply on step, add step if < s.axis_x.min).
// 4. Calculate x_min (s.axis_x.zoom_min / step, round, multiply on step, add step if < s.axis_x.zoom_min).
Float128 x_min = 0.0;
if (step >= 1) {
int64 x_min_nsteps = (int64) (s.axis_x.min / step);
int64 x_min_nsteps = (int64) (s.axis_x.zoom_min / step);
x_min = x_min_nsteps * step;
} else {
int64 round_axis_x_min = (int64)s.axis_x.min;
int64 x_min_nsteps = (int64) ((s.axis_x.min - round_axis_x_min) / step);
int64 round_axis_x_min = (int64)s.axis_x.zoom_min;
int64 x_min_nsteps = (int64) ((s.axis_x.zoom_min - round_axis_x_min) / step);
x_min = round_axis_x_min + x_min_nsteps * step;
}
if (x_min < s.axis_x.min) x_min += step;
if (x_min < s.axis_x.zoom_min) x_min += step;
// 4.5. Draw Axis title
if (s.axis_x.title.text != "")
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.x_low + s.place.x_high) / 2.0;
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);
@ -601,7 +680,7 @@ namespace Gtk.CairoChart {
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.x_low + s.place.x_high) / 2.0;
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);
@ -613,7 +692,7 @@ namespace Gtk.CairoChart {
}
// 5. Draw records, update cur_{x,y}_{min,max}.
for (Float128 x = x_min, x_max = s.axis_x.max; point_belong (x, x_min, x_max); x += step) {
for (Float128 x = x_min, x_max = s.axis_x.zoom_max; point_belong (x, x_min, x_max); x += step) {
if (common_x_axes) set_source_rgba(Color(0,0,0,1));
else set_source_rgba(s.axis_x.color);
string text = "", time_text = "";
@ -632,7 +711,7 @@ namespace Gtk.CairoChart {
break;
}
var scr_x = plot_area_x_min + (plot_area_x_max - plot_area_x_min)
* (s.place.x_low + (s.place.x_high - s.place.x_low) / (s.axis_x.max - s.axis_x.min) * (x - s.axis_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 text_t = new Text(text, s.axis_x.font_style, s.axis_x.color);
switch (s.axis_x.position) {
case Axis.Position.LOW:
@ -640,25 +719,25 @@ namespace Gtk.CairoChart {
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.min + s.axis_x.max) / 2.0) / (s.axis_x.max - s.axis_x.min);
- 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.min + s.axis_x.max) / 2.0) / (s.axis_x.max - s.axis_x.min);
- 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.min + s.axis_x.max) / 2.0) / (s.axis_x.max - s.axis_x.min);
- 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);
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.y_high.
// 6. Draw grid lines to the s.place.zoom_y_high.
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);
@ -667,32 +746,32 @@ namespace Gtk.CairoChart {
if (common_x_axes)
context.line_to (scr_x, plot_area_y_min);
else
context.line_to (scr_x, double.min (y, plot_area_y_max - (plot_area_y_max - plot_area_y_min) * s.place.y_high));
context.line_to (scr_x, double.min (y, plot_area_y_max - (plot_area_y_max - plot_area_y_min) * s.place.zoom_y_high));
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);
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.min + s.axis_x.max) / 2.0) / (s.axis_x.max - s.axis_x.min);
- 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.min + s.axis_x.max) / 2.0) / (s.axis_x.max - s.axis_x.min);
- 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.min + s.axis_x.max) / 2.0) / (s.axis_x.max - s.axis_x.min);
- 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);
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.y_high.
// 6. Draw grid lines to the s.place.zoom_y_high.
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);
@ -701,7 +780,7 @@ namespace Gtk.CairoChart {
if (common_x_axes)
context.line_to (scr_x, plot_area_y_max);
else
context.line_to (scr_x, double.max (y, plot_area_y_max - (plot_area_y_max - plot_area_y_min) * s.place.y_low));
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;
@ -713,11 +792,11 @@ namespace Gtk.CairoChart {
// join relative x-axes with non-intersect places
for (int sj = si - 1; sj >= 0; --sj) {
var s2 = series[sj];
var s2 = zoom_series[sj];
bool has_intersection = false;
for (int sk = si; sk > sj; --sk) {
var s3 = series[sk];
if (are_intersect(s2.place.x_low, s2.place.x_high, s3.place.x_low, s3.place.x_high)
var s3 = zoom_series[sk];
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) {
has_intersection = true;
@ -750,38 +829,38 @@ namespace Gtk.CairoChart {
}
protected virtual void draw_vertical_axis () {
for (int si = series.length - 1, nskip = 0; si >=0; --si) {
for (int si = zoom_series.length - 1, nskip = 0; si >=0; --si) {
if (common_y_axes && si != 0) continue;
var s = series[si];
var s = zoom_series[si];
// 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_y, out max_rec_width, out max_rec_height, false);
// 2. Calculate maximal available number of records, take into account the space width.
long max_nrecs = (long) ((plot_area_y_max - plot_area_y_min) * (s.place.y_high - s.place.y_low) / max_rec_height);
long max_nrecs = (long) ((plot_area_y_max - plot_area_y_min) * (s.place.zoom_y_high - s.place.zoom_y_low) / max_rec_height);
// 3. Calculate grid step.
Float128 step = calc_round_step ((s.axis_y.max - s.axis_y.min) / max_nrecs);
if (step > s.axis_y.max - s.axis_y.min)
step = s.axis_y.max - s.axis_y.min;
Float128 step = calc_round_step ((s.axis_y.zoom_max - s.axis_y.zoom_min) / max_nrecs);
if (step > s.axis_y.zoom_max - s.axis_y.zoom_min)
step = s.axis_y.zoom_max - s.axis_y.zoom_min;
// 4. Calculate y_min (s.axis_y.min / step, round, multiply on step, add step if < s.axis_y.min).
// 4. Calculate y_min (s.axis_y.zoom_min / step, round, multiply on step, add step if < s.axis_y.zoom_min).
Float128 y_min = 0.0;
if (step >= 1) {
int64 y_min_nsteps = (int64) (s.axis_y.min / step);
int64 y_min_nsteps = (int64) (s.axis_y.zoom_min / step);
y_min = y_min_nsteps * step;
} else {
int64 round_axis_y_min = (int64)s.axis_y.min;
int64 y_min_nsteps = (int64) ((s.axis_y.min - round_axis_y_min) / step);
int64 round_axis_y_min = (int64)s.axis_y.zoom_min;
int64 y_min_nsteps = (int64) ((s.axis_y.zoom_min - round_axis_y_min) / step);
y_min = round_axis_y_min + y_min_nsteps * step;
}
if (y_min < s.axis_y.min) y_min += step;
if (y_min < s.axis_y.zoom_min) y_min += step;
// 4.5. Draw Axis title
if (s.axis_y.title.text != "")
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.y_low + s.place.y_high) / 2.0;
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);
@ -789,7 +868,7 @@ namespace Gtk.CairoChart {
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.y_low + s.place.y_high) / 2.0;
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);
@ -801,21 +880,21 @@ namespace Gtk.CairoChart {
}
// 5. Draw records, update cur_{x,y}_{min,max}.
for (Float128 y = y_min, y_max = s.axis_y.max; point_belong (y, y_min, y_max); y += step) {
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.y_low + (s.place.y_high - s.place.y_low) / (s.axis_y.max - s.axis_y.min) * (y - s.axis_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 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.min + s.axis_y.max) / 2.0) / (s.axis_y.max - s.axis_y.min));
+ 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));
show_text(text_t);
// 6. Draw grid lines to the s.place.y_high.
// 6. Draw grid lines to the s.place.zoom_y_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);
@ -824,15 +903,15 @@ namespace Gtk.CairoChart {
if (common_y_axes)
context.line_to (plot_area_x_max, scr_y);
else
context.line_to (double.max (x, plot_area_x_min + (plot_area_x_max - plot_area_x_min) * s.place.x_high), scr_y);
context.line_to (double.max (x, plot_area_x_min + (plot_area_x_max - plot_area_x_min) * s.place.zoom_x_high), scr_y);
break;
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.min + s.axis_y.max) / 2.0) / (s.axis_y.max - s.axis_y.min));
+ 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));
show_text(text_t);
// 6. Draw grid lines to the s.place.y_high.
// 6. Draw grid lines to the s.place.zoom_y_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);
@ -841,7 +920,7 @@ namespace Gtk.CairoChart {
if (common_y_axes)
context.line_to (plot_area_x_min, scr_y);
else
context.line_to (double.min (x, plot_area_x_min + (plot_area_x_max - plot_area_x_min) * s.place.x_low), scr_y);
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;
@ -853,11 +932,11 @@ namespace Gtk.CairoChart {
// join relative x-axes with non-intersect places
for (int sj = si - 1; sj >= 0; --sj) {
var s2 = series[sj];
var s2 = zoom_series[sj];
bool has_intersection = false;
for (int sk = si; sk > sj; --sk) {
var s3 = series[sk];
if (are_intersect(s2.place.y_low, s2.place.y_high, s3.place.y_low, s3.place.y_high)
var s3 = zoom_series[sk];
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;
break;
@ -899,13 +978,23 @@ namespace Gtk.CairoChart {
}
protected virtual double get_scr_x (Series s, Float128 x) {
return plot_area_x_min + (plot_area_x_max - plot_area_x_min) * (s.place.x_low + (x - s.axis_x.min)
/ (s.axis_x.max - s.axis_x.min) * (s.place.x_high - s.place.x_low));
return plot_area_x_min + (plot_area_x_max - plot_area_x_min) * (s.place.zoom_x_low + (x - s.axis_x.zoom_min)
/ (s.axis_x.zoom_max - s.axis_x.zoom_min) * (s.place.zoom_x_high - s.place.zoom_x_low));
}
protected virtual double get_scr_y (Series s, Float128 y) {
return plot_area_y_max - (plot_area_y_max - plot_area_y_min) * (s.place.y_low + (y - s.axis_y.min)
/ (s.axis_y.max - s.axis_y.min) * (s.place.y_high - s.place.y_low));
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 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 bool point_in_rect (Point p, double x0, double x1, double y0, double y1) {
@ -992,8 +1081,8 @@ namespace Gtk.CairoChart {
}
protected virtual void draw_series () {
for (int si = 0; si < series.length; ++si) {
var s = series[si];
for (int si = 0; si < zoom_series.length; ++si) {
var s = zoom_series[si];
if (s.points.length == 0) continue;
var points = s.points.copy();
switch(s.sort) {
@ -1013,7 +1102,7 @@ namespace Gtk.CairoChart {
break;
}
set_line_style(s.line_style);
// draw series line
// draw zoom_series line
for (int i = 1; i < points.length; ++i) {
Point c, d;
if (cut_line (Point(get_scr_x(s, points[i - 1].x), get_scr_y(s, points[i - 1].y)),
@ -1063,7 +1152,7 @@ namespace Gtk.CairoChart {
chart.plot_area_y_max = this.plot_area_y_max;
chart.plot_area_y_min = this.plot_area_y_min;
chart.selection_style = this.selection_style;
chart.series = this.series.copy();
chart.zoom_series = this.zoom_series.copy();
chart.show_legend = this.show_legend;
chart.title = this.title.copy().copy();
chart.title_height = this.title_height;

View File

@ -1,15 +1,52 @@
namespace Gtk.CairoChart {
public struct Place {
double x_low;
double x_high;
double y_low;
double y_high;
public class Place {
double _x_low = 0;
double _x_high = 0;
double _y_low = 0;
double _y_high = 0;
public double x_low {
get { return _x_low; }
set { _x_low = zoom_x_low = value; }
default = 0;
}
public double x_high {
get { return _x_high; }
set { _x_high = zoom_x_high = value; }
default = 0;
}
public double y_low {
get { return _y_low; }
set { _y_low = zoom_y_low = value; }
default = 0;
}
public double y_high {
get { return _y_high; }
set { _y_high = zoom_y_high = value; }
default = 0;
}
public double zoom_x_low = 0;
public double zoom_x_high = 1;
public double zoom_y_low = 0;
public double zoom_y_high = 1;
public Place (double x_low = 0, double x_high = 0, double y_low = 0, double y_high = 0) {
public Place copy () {
var place = new Place ();
place.x_low = this.x_low;
place.x_high = this.x_high;
place.y_low = this.y_low;
place.y_high = this.y_high;
return place;
}
public Place (double x_low = 0, double x_high = 1, double y_low = 0, double y_high = 1) {
this.x_low = x_low;
this.x_high = x_high;
this.y_low = y_low;
this.y_high = y_high;
zoom_x_low = x_low;
zoom_x_high = x_high;
zoom_y_low = y_low;
zoom_y_high = y_high;
}
}
}

View File

@ -25,7 +25,7 @@ namespace Gtk.CairoChart {
PRICLE_TRIANGLE
}
public Place place = Place();
public Place place = new Place();
public Text title = new Text ();
public MarkerType marker_type = MarkerType.SQUARE;
@ -59,7 +59,7 @@ namespace Gtk.CairoChart {
series.grid = this.grid.copy ();
series.line_style = this.line_style;
series.marker_type = this.marker_type;
series.place = this.place;
series.place = this.place.copy();
series.points = this.points.copy();
series.sort = this.sort;
series.title = this.title.copy();

View File

@ -5,14 +5,14 @@ void plot_chart1 (Chart chart) {
var s2 = new Series ();
var s3 = new Series ();
s1.title = new Text("Series 1"); s1.color = new Color (1, 0, 0);
s1.points = {new Point(0, 0), new Point(2, 1), new Point(1, 3)};
s1.title = new Text("Series 1"); s1.color = Color (1, 0, 0);
s1.points = {Point(0, 0), Point(2, 1), Point(1, 3)};
s1.axis_x.position = Axis.Position.HIGH;
s1.axis_x.format = "%.3Lf";
s2.title = new Text("Series 2"); s2.color = new Color (0, 1, 0);
s2.points = {new Point(5, -3), new Point(25, -18), new Point(-11, 173)};
s3.title = new Text("Series 3"); s3.color = new Color (0, 0, 1);
s3.points = {new Point(9, 17), new Point(2, 10), new Point(122, 31)};
s2.title = new Text("Series 2"); s2.color = Color (0, 1, 0);
s2.points = {Point(5, -3), Point(25, -18), Point(-11, 173)};
s3.title = new Text("Series 3"); s3.color = Color (0, 0, 1);
s3.points = {Point(9, 17), Point(2, 10), Point(122, 31)};
s3.axis_y.position = Axis.Position.HIGH;
s1.axis_x.min = 0; s1.axis_x.max = 2;
@ -48,14 +48,14 @@ void plot_chart2 (Chart chart) {
var s2 = new Series ();
var s3 = new Series ();
s1.title = new Text("Series 1"); s1.color = new Color (1, 0, 0);
s1.points = {new Point(-12, 0), new Point(2, 1), new Point(20, 3)};
s1.title = new Text("Series 1"); s1.color = Color (1, 0, 0);
s1.points = {Point(-12, 0), Point(2, 1), Point(20, 3)};
s2.axis_y.position = Axis.Position.HIGH;
s1.axis_x.format = "%.3Lf";
s2.title = new Text("Series 2"); s2.color = new Color (0, 1, 0);
s2.points = {new Point(5, -3), new Point(25, -18), new Point(-11, 173)};
s3.title = new Text("Series 3"); s3.color = new Color (0, 0, 1);
s3.points = {new Point(9, 17), new Point(2, 10), new Point(-15, 31)};
s2.title = new Text("Series 2"); s2.color = Color (0, 1, 0);
s2.points = {Point(5, -3), Point(25, -18), Point(-11, 173)};
s3.title = new Text("Series 3"); s3.color = Color (0, 0, 1);
s3.points = {Point(9, 17), Point(2, 10), Point(-15, 31)};
s3.axis_y.position = Axis.Position.HIGH;
s1.axis_x.min = -15; s1.axis_x.max = 30;
@ -91,16 +91,16 @@ void plot_chart3 (Chart chart) {
var s2 = new Series ();
var s3 = new Series ();
s1.title = new Text("Series 1"); s1.color = new Color (1, 0, 0);
s1.points = {new Point(0, 70), new Point(2, 155), new Point(1, -3)};
s1.title = new Text("Series 1"); s1.color = Color (1, 0, 0);
s1.points = {Point(0, 70), Point(2, 155), Point(1, -3)};
s1.axis_x.position = Axis.Position.HIGH;
s1.axis_y.position = Axis.Position.HIGH;
s1.axis_x.format = "%.3Lf";
s2.title = new Text("Series 2"); s2.color = new Color (0, 1, 0);
s2.points = {new Point(5, -3), new Point(25, -18), new Point(-11, 173)};
s2.title = new Text("Series 2"); s2.color = Color (0, 1, 0);
s2.points = {Point(5, -3), Point(25, -18), Point(-11, 173)};
s2.axis_y.position = Axis.Position.HIGH;
s3.title = new Text("Series 3"); s3.color = new Color (0, 0, 1);
s3.points = {new Point(9, -17), new Point(2, 10), new Point(122, 31)};
s3.title = new Text("Series 3"); s3.color = Color (0, 0, 1);
s3.points = {Point(9, -17), Point(2, 10), Point(122, 31)};
s3.axis_y.position = Axis.Position.HIGH;
s1.axis_x.min = 0; s1.axis_x.max = 2;
@ -145,18 +145,18 @@ void plot_chart4 (Chart chart) {
var now = new DateTime.now_local().to_unix();
var high = (uint64) (253000000000L);
s1.title = new Text("Series 1"); s1.color = new Color (1, 0, 0);
s1.points = {new Point(now, 70), new Point(now - 100000, 155), new Point(now + 100000, 30)};
s1.title = new Text("Series 1"); s1.color = Color (1, 0, 0);
s1.points = {Point(now, 70), Point(now - 100000, 155), Point(now + 100000, 30)};
s1.axis_x.position = Axis.Position.HIGH;
s1.axis_y.position = Axis.Position.HIGH;
s2.title = new Text("Series 2"); s2.color = new Color (0, 1, 0);
s2.points = {new Point(5, -3), new Point(25, -18), new Point(-11, 173)};
s2.title = new Text("Series 2"); s2.color = Color (0, 1, 0);
s2.points = {Point(5, -3), Point(25, -18), Point(-11, 173)};
s2.axis_y.position = Axis.Position.HIGH;
s3.title = new Text("Series 3"); s3.color = new Color (0, 0, 1);
s3.points = {new Point(high - 2 + 0.73, -17), new Point(high - 1 + 0.234, 10), new Point(high + 1 + 0.411, 31)};
s3.title = new Text("Series 3"); s3.color = Color (0, 0, 1);
s3.points = {Point(high - 2 + 0.73, -17), Point(high - 1 + 0.234, 10), Point(high + 1 + 0.411, 31)};
s3.axis_y.position = Axis.Position.HIGH;
s4.title = new Text("Series 4"); s4.color = new Color (0.5, 0.3, 0.9);
s4.points = {new Point(high + 0.005, -19.05), new Point(high + 0.0051, 28), new Point(high + 0.0052, 55), new Point(high + 0.0053, 44)};
s4.title = new Text("Series 4"); s4.color = Color (0.5, 0.3, 0.9);
s4.points = {Point(high + 0.005, -19.05), Point(high + 0.0051, 28), Point(high + 0.0052, 55), Point(high + 0.0053, 44)};
s4.axis_y.position = Axis.Position.HIGH;
s1.axis_x.min = now - 100000; s1.axis_x.max = now + 100000;
@ -195,6 +195,14 @@ void plot_chart4 (Chart chart) {
chart.series = { s1, s2, s3, s4 };
}
bool point_in_chart (Chart chart, double x, double y) {
if (x < chart.plot_area_x_min) return false;
if (x > chart.plot_area_x_max) return false;
if (y < chart.plot_area_y_min) return false;
if (y > chart.plot_area_y_max) return false;
return true;
}
int main (string[] args) {
init (ref args);
@ -326,44 +334,95 @@ int main (string[] args) {
}
});
bool draw_selection = false;
double sel_x0 = 0, sel_x1 = 0, sel_y0 = 0, sel_y1 = 0;
da.draw.connect((context) => {
// user's pre draw operations here...
chart.context = context;
var ret = chart.draw();
/*var ret = */chart.draw();
// user's post draw operations here...
return ret;
if (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);
context.set_line_width(1);
//context.set_dash(null, 0);
context.rectangle (sel_x0, sel_y0, sel_x1 - sel_x0, sel_y1 - sel_y0);
//context.fill();
context.stroke();
}
return true;//ret;
});
da.queue_draw_area(0, 0, da.get_allocated_width(), da.get_allocated_height());
da.button_release_event.connect((event) => {
// user's pre button_release_event operations here...
var ret = true;//chart.button_release_event(event);
// user's post button_release_event operations here...
return ret;
});
da.button_press_event.connect((event) => {
// user's pre button_press_event operations here...
var ret = true;//chart.button_press_event(event);
//stdout.puts("pre_press\n");
if (event.button == 2 && point_in_chart(chart, event.x, event.y)) {
draw_selection = true;
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());
}
// user's post button_press_event operations here...
return ret;
//stdout.puts("post_press\n");
return true; // return ret;
});
da.button_release_event.connect((event) => {
// user's pre button_release_event operations here...
//stdout.puts("pre_release\n");
//var ret = chart.button_release_event(event);
if (event.button == 2) {
draw_selection = false;
sel_x1 = event.x;
sel_y1 = event.y;
if (sel_x1 > sel_x0 && sel_y1 > sel_y0)
chart.zoom_in (sel_x0, sel_y0, sel_x1, sel_y1);
else
chart.zoom_out ();
da.queue_draw_area(0, 0, da.get_allocated_width(), da.get_allocated_height());
}
// user's post button_release_event operations here...
//stdout.puts("post_release\n");
return true; // return ret;
});
da.motion_notify_event.connect((event) => {
// user's pre motion_notify_event operations here...
var ret = true;//chart.motion_notify_event(event);
//stdout.puts("pre_motion\n");
//var ret = chart.motion_notify_event(event);
// user's post motion_notify_event operations here...
return ret;
//stdout.puts("post_motion\n");
if (draw_selection && point_in_chart(chart, event.x, event.y)) {
sel_x1 = event.x;
sel_y1 = event.y;
da.queue_draw_area(0, 0, da.get_allocated_width(), da.get_allocated_height());
}
return true; // return ret;
});
da.add_events(Gdk.EventMask.SCROLL_MASK);
da.scroll_event.connect((event) => {
// user's pre scroll_notify_event operations here...
stdout.puts("pre_scroll\n");
//stdout.puts("pre_scroll\n");
var ret = true;//chart.scroll_notify_event(event);
//var ret = chart.scroll_notify_event(event);
// user's post scroll_notify_event operations here...
stdout.puts("post_scroll\n");
//stdout.puts("post_scroll\n");
return ret;
return true; // return ret;
});
var vbox2 = new Box(Orientation.VERTICAL, 0);