Merge branch '#132_refactoring' into develop
This commit is contained in:
commit
6bb5fcce57
|
@ -0,0 +1,128 @@
|
|||
namespace CairoChart {
|
||||
|
||||
/**
|
||||
* Area rectangle.
|
||||
*/
|
||||
public class Area {
|
||||
|
||||
/**
|
||||
* Left bound.
|
||||
*/
|
||||
public double x0 = 0;
|
||||
|
||||
/**
|
||||
* Top bound.
|
||||
*/
|
||||
public double x1 = 1;
|
||||
|
||||
/**
|
||||
* Right bound.
|
||||
*/
|
||||
public double y0 = 0;
|
||||
|
||||
/**
|
||||
* Bottom bound.
|
||||
*/
|
||||
public double y1 = 1;
|
||||
|
||||
/**
|
||||
* ``Area`` width.
|
||||
*/
|
||||
public virtual double width {
|
||||
get {
|
||||
return x1 - x0;
|
||||
}
|
||||
set {
|
||||
x1 = x0 + value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ``Area`` height.
|
||||
*/
|
||||
public virtual double height {
|
||||
get {
|
||||
return y1 - y0;
|
||||
}
|
||||
set {
|
||||
y1 = y0 + value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new ``Area``.
|
||||
*/
|
||||
public Area () { }
|
||||
|
||||
/**
|
||||
* Constructs a new ``Area`` by other ``Area``.
|
||||
* @param area ``Area`` instance.
|
||||
*/
|
||||
public Area.with_area (Area area) {
|
||||
this.x0 = area.x0;
|
||||
this.y0 = area.y0;
|
||||
this.x1 = area.x1;
|
||||
this.y1 = area.y1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new ``Area`` with absolute coordinates.
|
||||
* @param x0 left bound.
|
||||
* @param y0 top bound.
|
||||
* @param x1 right bound.
|
||||
* @param y1 bottom bound.
|
||||
*/
|
||||
public Area.with_abs (double x0, double y0,
|
||||
double x1, double y1) {
|
||||
this.x0 = x0;
|
||||
this.y0 = y0;
|
||||
this.x1 = x1;
|
||||
this.y1 = y1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new ``Area`` by 2 points.
|
||||
* @param p0 top left position.
|
||||
* @param p1 bottom right position.
|
||||
*/
|
||||
public Area.with_points (Point p0, Point p1) {
|
||||
this.x0 = p0.x;
|
||||
this.y0 = p0.y;
|
||||
this.x1 = p1.x;
|
||||
this.y1 = p1.y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new ``Area`` with relative coordinates.
|
||||
* @param x0 left bound.
|
||||
* @param y0 top bound.
|
||||
* @param width ``Area`` width.
|
||||
* @param height ``Area`` height.
|
||||
*/
|
||||
public Area.with_rel (double x0, double y0,
|
||||
double width, double height) {
|
||||
this.x0 = x0;
|
||||
this.y0 = y0;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new ``Area`` by ``Cairo.Rectangle``.
|
||||
* @param rectangle ``Cairo.Rectangle`` instance.
|
||||
*/
|
||||
public Area.with_rectangle (Cairo.Rectangle rectangle) {
|
||||
this.x0 = rectangle.x;
|
||||
this.y0 = rectangle.y;
|
||||
this.width = rectangle.width;
|
||||
this.height = rectangle.height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a copy of the ``Chart``.
|
||||
*/
|
||||
public virtual Area copy () {
|
||||
return new Area.with_area(this);
|
||||
}
|
||||
}
|
||||
}
|
566
src/Axis.vala
566
src/Axis.vala
|
@ -1,46 +1,102 @@
|
|||
namespace Gtk.CairoChart {
|
||||
// If one of axis:title or axis:min/max are different
|
||||
// then draw separate axis for each/all series
|
||||
// or specify series name near the axis
|
||||
namespace CairoChart {
|
||||
|
||||
/**
|
||||
* ``Chart`` axis.
|
||||
*/
|
||||
public class Axis {
|
||||
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,
|
||||
DATE_TIME
|
||||
}
|
||||
public enum ScaleType {
|
||||
LINEAR = 0, // default
|
||||
// LOGARITHMIC, // TODO
|
||||
// etc
|
||||
}
|
||||
public Type type;
|
||||
public ScaleType scale_type;
|
||||
|
||||
protected unowned Chart chart;
|
||||
protected unowned Series ser;
|
||||
protected string _format = "%.2Lf";
|
||||
protected string _date_format = "%Y.%m.%d";
|
||||
protected string _time_format = "%H:%M:%S";
|
||||
protected int _dsec_signs = 2; // 2 signs = centiseconds
|
||||
protected bool is_x;
|
||||
|
||||
/**
|
||||
* ``Axis`` title.
|
||||
*/
|
||||
public Text title;
|
||||
|
||||
/**
|
||||
* ``Axis`` range/limits.
|
||||
*/
|
||||
public Range range = new Range();
|
||||
|
||||
/**
|
||||
* ``Axis`` relative range/limits.
|
||||
*/
|
||||
public Range place = new Range();
|
||||
|
||||
/**
|
||||
* ``Axis`` position.
|
||||
*/
|
||||
public enum Position {
|
||||
/**
|
||||
* Bottom/Left ``Axis``.
|
||||
*/
|
||||
LOW = 0,
|
||||
|
||||
/**
|
||||
* Top/Right ``Axis``.
|
||||
*/
|
||||
HIGH = 1,
|
||||
|
||||
/**
|
||||
* 2 ``Axes``.
|
||||
*/
|
||||
BOTH = 2
|
||||
}
|
||||
|
||||
/**
|
||||
* Position.
|
||||
*/
|
||||
public Position position = Position.LOW;
|
||||
|
||||
string _format = "%.2Lf";
|
||||
string _date_format = "%Y.%m.%d";
|
||||
string _time_format = "%H:%M:%S";
|
||||
int _dsec_signs = 2; // 2 signs = centiseconds
|
||||
public string format {
|
||||
/**
|
||||
* Data type.
|
||||
*/
|
||||
public enum DType {
|
||||
/**
|
||||
* Float128 numbers.
|
||||
*/
|
||||
NUMBERS = 0,
|
||||
|
||||
/**
|
||||
* Date/Time.
|
||||
*/
|
||||
DATE_TIME
|
||||
}
|
||||
|
||||
/**
|
||||
* Data type.
|
||||
*/
|
||||
public DType dtype;
|
||||
|
||||
/**
|
||||
* ``Axis`` scale type.
|
||||
*/
|
||||
public enum Scale {
|
||||
/**
|
||||
* Linear scale.
|
||||
*/
|
||||
LINEAR = 0,
|
||||
|
||||
/**
|
||||
* Logarithmic scale.
|
||||
*/
|
||||
// LOGARITHMIC,
|
||||
}
|
||||
|
||||
/**
|
||||
* Scale type.
|
||||
*/
|
||||
public Scale scale;
|
||||
|
||||
/**
|
||||
* Float128 numbers print string format.
|
||||
*/
|
||||
public virtual string format {
|
||||
get { return _format; }
|
||||
set {
|
||||
// TODO: check format
|
||||
|
@ -48,7 +104,11 @@ namespace Gtk.CairoChart {
|
|||
}
|
||||
default = "%.2Lf";
|
||||
}
|
||||
public string date_format {
|
||||
|
||||
/**
|
||||
* Date print string format.
|
||||
*/
|
||||
public virtual string date_format {
|
||||
get { return _date_format; }
|
||||
set {
|
||||
// TODO: check format
|
||||
|
@ -56,7 +116,11 @@ namespace Gtk.CairoChart {
|
|||
}
|
||||
default = "%Y.%m.%d";
|
||||
}
|
||||
public string time_format {
|
||||
|
||||
/**
|
||||
* Time print string format.
|
||||
*/
|
||||
public virtual string time_format {
|
||||
get { return _time_format; }
|
||||
set {
|
||||
// TODO: check format
|
||||
|
@ -64,7 +128,13 @@ namespace Gtk.CairoChart {
|
|||
}
|
||||
default = "%H:%M:%S";
|
||||
}
|
||||
public int dsec_signs {
|
||||
|
||||
/**
|
||||
* Number of second's signs after point.
|
||||
*
|
||||
* 2 signs means centiseconds, 3 signs means milliseconds, etc...
|
||||
*/
|
||||
public virtual int dsec_signs {
|
||||
get { return _dsec_signs; }
|
||||
set {
|
||||
// TODO: check format
|
||||
|
@ -72,30 +142,422 @@ namespace Gtk.CairoChart {
|
|||
}
|
||||
default = 2;
|
||||
}
|
||||
public FontStyle font_style = FontStyle ();
|
||||
public Color color = Color ();
|
||||
public LineStyle line_style = LineStyle ();
|
||||
public double font_indent = 5;
|
||||
|
||||
public Axis copy () {
|
||||
var axis = new Axis ();
|
||||
/**
|
||||
* ``Axis`` Font style.
|
||||
*/
|
||||
public Font font = new Font ();
|
||||
|
||||
/**
|
||||
* ``Axis`` color.
|
||||
*/
|
||||
public Color color = Color ();
|
||||
|
||||
/**
|
||||
* ``Axis`` line style.
|
||||
*/
|
||||
public LineStyle grid_style = LineStyle (Color(), 1, {2, 3});
|
||||
|
||||
/**
|
||||
* Number of equally placed points to evaluate records sizes.
|
||||
*/
|
||||
public int nrecords = 128;
|
||||
|
||||
/**
|
||||
* Constructs a new ``Axis``.
|
||||
* @param chart ``Chart`` instance.
|
||||
* @param ser ``Series`` instance.
|
||||
* @param is_x is X-axis or not (Y-axis otherwise).
|
||||
*/
|
||||
public Axis (Chart chart, Series ser, bool is_x) {
|
||||
this.chart = chart;
|
||||
this.ser = ser;
|
||||
this.is_x = is_x;
|
||||
title = new Text (chart, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a copy of the ``Axis``.
|
||||
*/
|
||||
public virtual Axis copy () {
|
||||
var axis = new Axis (chart, ser, is_x);
|
||||
axis._date_format = this._date_format;
|
||||
axis._dsec_signs = this._dsec_signs;
|
||||
axis._format = this._format;
|
||||
axis._time_format = this._time_format;
|
||||
axis.color = this.color;
|
||||
axis.font_indent = this.font_indent;
|
||||
axis.font_style = this.font_style;
|
||||
axis.line_style = this.line_style;
|
||||
axis.max = this.max;
|
||||
axis.min = this.min;
|
||||
axis.font = this.font.copy();
|
||||
axis.grid_style = this.grid_style;
|
||||
axis.range = this.range.copy();
|
||||
axis.place = this.place.copy();
|
||||
axis.position = this.position;
|
||||
axis.scale_type = this.scale_type;
|
||||
axis.scale = this.scale;
|
||||
axis.title = this.title.copy();
|
||||
axis.type = this.type;
|
||||
axis.dtype = this.dtype;
|
||||
axis.nrecords = this.nrecords;
|
||||
return axis;
|
||||
}
|
||||
|
||||
public Axis () {}
|
||||
/**
|
||||
* Gets screen position by real ``Axis`` value.
|
||||
* @param axis_value real ``Axis`` value.
|
||||
*/
|
||||
public virtual double scr_pos (Float128 axis_value) {
|
||||
return is_x ? chart.plarea.x0 + chart.plarea.width * (place.zmin + (axis_value - range.zmin) / range.zrange * place.zrange)
|
||||
: chart.plarea.y0 + chart.plarea.height * (1 - (place.zmin + (axis_value - range.zmin) / range.zrange * place.zrange));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets real ``Axis`` value by screen position.
|
||||
* @param scr_pos screen position.
|
||||
*/
|
||||
public virtual Float128 axis_val (double scr_pos) {
|
||||
return is_x ? range.zmin + ((scr_pos - chart.plarea.x0) / chart.plarea.width - place.zmin) * range.zrange / place.zrange
|
||||
: range.zmin + ((chart.plarea.y1 - scr_pos) / chart.plarea.height - place.zmin) * range.zrange / place.zrange;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints date/time to strings with a current formats.
|
||||
* @param date returns formatted date string.
|
||||
* @param time returns formatted time string.
|
||||
*/
|
||||
public virtual void print_dt (Float128 x, out string date, out string time) {
|
||||
date = time = "";
|
||||
var dt = new DateTime.from_unix_utc((int64)x);
|
||||
date = dt.format(date_format);
|
||||
var dsec_str =
|
||||
("%."+(dsec_signs.to_string())+"Lf").printf((LongDouble)(x - (int64)x)).offset(1);
|
||||
if (time_format != "")
|
||||
time = dt.format(time_format) + dsec_str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets compact placement position on the screen.
|
||||
* @param axis_value real ``Axis`` value.
|
||||
* @param text to place on the screen.
|
||||
*/
|
||||
public virtual double compact_rec_pos (Float128 axis_value, Text text) {
|
||||
return is_x ? scr_pos(axis_value) - text.width / 2 - text.width * (axis_value - (range.zmin + range.zmax) / 2) / range.zrange
|
||||
: scr_pos(axis_value) + text.height / 2 + text.height * (axis_value - (range.zmin + range.zmax) / 2) / range.zrange;
|
||||
}
|
||||
|
||||
/**
|
||||
* Zooms out ``Axis``.
|
||||
*/
|
||||
public virtual void zoom_out () {
|
||||
range.zoom_out();
|
||||
place.zoom_out();
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws horizontal axis.
|
||||
* @param nskip number of series to skip printing.
|
||||
*/
|
||||
public virtual void draw (ref int nskip) {
|
||||
if (!ser.zoom_show) return;
|
||||
|
||||
var si = Math.find_arr<Series>(chart.series, ser);
|
||||
if (si == -1) return;
|
||||
|
||||
if ((is_x && chart.joint_x || !is_x && chart.joint_y) && si != chart.zoom_1st_idx) return;
|
||||
|
||||
// 1. Detect max record width/height by axis.nrecords equally selected points using format.
|
||||
double max_rec_width, max_rec_height;
|
||||
calc_rec_sizes (this, out max_rec_width, out max_rec_height, is_x);
|
||||
|
||||
// 2. Calculate maximal available number of records, take into account the space width.
|
||||
long max_nrecs = (long) (is_x ? chart.plarea.width * place.zrange / max_rec_width
|
||||
: chart.plarea.height * place.zrange / max_rec_height);
|
||||
|
||||
// 3. Calculate grid step.
|
||||
Float128 step = Math.calc_round_step (range.zrange / max_nrecs, dtype == DType.DATE_TIME);
|
||||
if (step > range.zrange)
|
||||
step = range.zrange;
|
||||
|
||||
// 4. Calculate min (range.zmin / step, round, multiply on step, add step if < range.zmin).
|
||||
Float128 min = 0;
|
||||
if (step >= 1) {
|
||||
int64 min_nsteps = (int64) (range.zmin / step);
|
||||
min = min_nsteps * step;
|
||||
} else {
|
||||
int64 round_axis_min = (int64)range.zmin;
|
||||
int64 min_nsteps = (int64) ((range.zmin - round_axis_min) / step);
|
||||
min = round_axis_min + min_nsteps * step;
|
||||
}
|
||||
if (min < range.zmin) min += step;
|
||||
|
||||
// 4.2. Cursor values for joint axes
|
||||
if (chart.cursors.has_crossings)
|
||||
if ( is_x && chart.joint_x && chart.cursors.style.orientation == Cursors.Orientation.VERTICAL
|
||||
|| !is_x && chart.joint_y && chart.cursors.style.orientation == Cursors.Orientation.HORIZONTAL) {
|
||||
var tmp = 0.0;
|
||||
if (is_x) tmp = max_rec_height + font.vspacing;
|
||||
else tmp = max_rec_width + font.hspacing;
|
||||
switch (position) {
|
||||
case Position.LOW:
|
||||
if (is_x) chart.evarea.y1 -= tmp;
|
||||
else chart.evarea.x0 += tmp;
|
||||
break;
|
||||
case Position.HIGH:
|
||||
if (is_x) chart.evarea.y0 += tmp;
|
||||
else chart.evarea.x1 -= tmp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 4.5. Draw Axis title
|
||||
if (title.text != "") {
|
||||
if (is_x) {
|
||||
var scr_x = chart.plarea.x0 + chart.plarea.width * (place.zmin + place.zmax) / 2;
|
||||
var scr_y = 0.0;
|
||||
switch (position) {
|
||||
case Position.LOW: scr_y = chart.evarea.y1 - font.vspacing; break;
|
||||
case Position.HIGH: scr_y = chart.evarea.y0 + font.vspacing + title.height; break;
|
||||
}
|
||||
chart.ctx.move_to(scr_x - title.width / 2, scr_y);
|
||||
chart.color = color;
|
||||
} else {
|
||||
var scr_y = chart.plarea.y0 + chart.plarea.height * (1 - (place.zmin + place.zmax) / 2);
|
||||
switch (position) {
|
||||
case Position.LOW:
|
||||
var scr_x = chart.evarea.x0 + font.hspacing + title.width;
|
||||
chart.ctx.move_to(scr_x, scr_y + title.height / 2);
|
||||
break;
|
||||
case Position.HIGH:
|
||||
var scr_x = chart.evarea.x1 - font.hspacing;
|
||||
chart.ctx.move_to(scr_x, scr_y + title.height / 2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (is_x && chart.joint_x || !is_x && chart.joint_y)
|
||||
chart.color = chart.joint_color;
|
||||
else
|
||||
chart.color = color;
|
||||
title.show();
|
||||
}
|
||||
|
||||
if (is_x) draw_recs (min, step, max_rec_height);
|
||||
else draw_recs (min, step, max_rec_width);
|
||||
|
||||
chart.ctx.stroke ();
|
||||
|
||||
var tmp1 = 0.0, tmp2 = 0.0, tmp3 = 0.0, tmp4 = 0.0;
|
||||
join_rel_axes (si, false, ref tmp1, ref tmp2, ref tmp3, ref tmp4, ref nskip);
|
||||
|
||||
if (nskip != 0) {--nskip; return;}
|
||||
|
||||
var tmp = 0.0;
|
||||
if (is_x) tmp = max_rec_height + font.vspacing + (title.text == "" ? 0 : title.height + font.vspacing);
|
||||
else tmp = max_rec_width + font.hspacing + (title.text == "" ? 0 : title.width + font.hspacing);
|
||||
switch (position) {
|
||||
case Position.LOW:
|
||||
if (is_x) chart.evarea.y1 -= tmp;
|
||||
else chart.evarea.x0 += tmp;
|
||||
break;
|
||||
case Position.HIGH:
|
||||
if (is_x) chart.evarea.y0 += tmp;
|
||||
else chart.evarea.x1 -= tmp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Joins equal axes.
|
||||
* @param nskip returns number of series to skip printing.
|
||||
*/
|
||||
public virtual void join_axes (ref int nskip) {
|
||||
Axis axis = this;
|
||||
if (!ser.zoom_show) return;
|
||||
if (nskip != 0) {--nskip; return;}
|
||||
var max_rec_width = 0.0, max_rec_height = 0.0;
|
||||
calc_rec_sizes (axis, out max_rec_width, out max_rec_height, is_x);
|
||||
var max_font_spacing = is_x ? axis.font.vspacing : axis.font.hspacing;
|
||||
var max_axis_font_width = axis.title.text == "" ? 0 : axis.title.width + axis.font.hspacing;
|
||||
var max_axis_font_height = axis.title.text == "" ? 0 : axis.title.height + axis.font.vspacing;
|
||||
|
||||
var si = Math.find_arr<Series>(chart.series, ser);
|
||||
if (si == -1) return;
|
||||
|
||||
if (is_x)
|
||||
join_rel_axes (si, true, ref max_rec_width, ref max_rec_height, ref max_font_spacing, ref max_axis_font_height, ref nskip);
|
||||
else
|
||||
join_rel_axes (si, true, ref max_rec_width, ref max_rec_height, ref max_font_spacing, ref max_axis_font_width, ref nskip);
|
||||
|
||||
// for 4.2. Cursor values for joint X axis
|
||||
if (si == chart.zoom_1st_idx && chart.cursors.has_crossings) {
|
||||
switch (chart.cursors.style.orientation) {
|
||||
case Cursors.Orientation.VERTICAL:
|
||||
if (is_x && chart.joint_x) {
|
||||
var tmp = max_rec_height + axis.font.vspacing;
|
||||
switch (axis.position) {
|
||||
case Position.LOW: chart.plarea.y1 -= tmp; break;
|
||||
case Position.HIGH: chart.plarea.y0 += tmp; break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Cursors.Orientation.HORIZONTAL:
|
||||
if (!is_x && chart.joint_y) {
|
||||
var tmp = max_rec_width + font.hspacing;
|
||||
switch (position) {
|
||||
case Position.LOW: chart.plarea.x0 += tmp; break;
|
||||
case Position.HIGH: chart.plarea.x1 -= tmp; break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (is_x && (!chart.joint_x || si == chart.zoom_1st_idx)) {
|
||||
var tmp = max_rec_height + max_font_spacing + max_axis_font_height;
|
||||
switch (axis.position) {
|
||||
case Position.LOW: chart.plarea.y1 -= tmp; break;
|
||||
case Position.HIGH: chart.plarea.y0 += tmp; break;
|
||||
}
|
||||
}
|
||||
if (!is_x && (!chart.joint_y || si == chart.zoom_1st_idx)) {
|
||||
var tmp = max_rec_width + max_font_spacing + max_axis_font_width;
|
||||
switch (position) {
|
||||
case Position.LOW: chart.plarea.x0 += tmp; break;
|
||||
case Position.HIGH: chart.plarea.x1 -= tmp; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void calc_rec_sizes (Axis axis, out double max_rec_width, out double max_rec_height, bool horizontal = true) {
|
||||
max_rec_width = max_rec_height = 0;
|
||||
for (var i = 0; i < axis.nrecords; ++i) {
|
||||
Float128 x = (int64)(axis.range.zmin + axis.range.zrange / axis.nrecords * i) + 1/3.0;
|
||||
switch (axis.dtype) {
|
||||
case DType.NUMBERS:
|
||||
var text = new Text (chart, axis.format.printf((LongDouble)x) + (horizontal ? "_" : ""), axis.font);
|
||||
max_rec_width = double.max (max_rec_width, text.width);
|
||||
max_rec_height = double.max (max_rec_height, text.height);
|
||||
break;
|
||||
case DType.DATE_TIME:
|
||||
string date, time;
|
||||
axis.print_dt(x, out date, out time);
|
||||
|
||||
var h = 0.0;
|
||||
if (axis.date_format != "") {
|
||||
var text = new Text (chart, date + (horizontal ? "_" : ""), axis.font);
|
||||
max_rec_width = double.max (max_rec_width, text.width);
|
||||
h = text.height;
|
||||
}
|
||||
if (axis.time_format != "") {
|
||||
var text = new Text (chart, time + (horizontal ? "_" : ""), axis.font);
|
||||
max_rec_width = double.max (max_rec_width, text.width);
|
||||
h += text.height;
|
||||
}
|
||||
max_rec_height = double.max (max_rec_height, h);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void join_rel_axes (int si,
|
||||
bool calc_max_values,
|
||||
ref double max_rec_width,
|
||||
ref double max_rec_height,
|
||||
ref double max_font_spacing,
|
||||
ref double max_axis_font_size,
|
||||
ref int nskip) {
|
||||
for (int sj = si - 1; sj >= 0; --sj) {
|
||||
var s2 = chart.series[sj];
|
||||
if (!s2.zoom_show) continue;
|
||||
bool has_intersection = false;
|
||||
Axis a2 = s2.axis_x; if (!is_x) a2 = s2.axis_y;
|
||||
for (int sk = si; sk > sj; --sk) {
|
||||
var s3 = chart.series[sk];
|
||||
if (!s3.zoom_show) continue;
|
||||
Axis a3 = s3.axis_x; if (!is_x) a3 = s3.axis_y;
|
||||
if (Math.coord_cross(a2.place.zmin, a2.place.zmax, a3.place.zmin, a3.place.zmax)
|
||||
|| a2.position != a3.position || a2.dtype != a3.dtype) {
|
||||
has_intersection = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!has_intersection) {
|
||||
if (calc_max_values) {
|
||||
var tmp_max_rec_width = 0.0, tmp_max_rec_height = 0.0;
|
||||
calc_rec_sizes (a2, out tmp_max_rec_width, out tmp_max_rec_height, is_x);
|
||||
max_rec_width = double.max (max_rec_width, tmp_max_rec_width);
|
||||
max_rec_height = double.max (max_rec_height, tmp_max_rec_height);
|
||||
max_font_spacing = double.max (max_font_spacing, is_x ? a2.font.vspacing : a2.font.hspacing);
|
||||
max_axis_font_size = double.max (max_axis_font_size,
|
||||
a2.title.text == "" ? 0
|
||||
: is_x ? a2.title.height + font.vspacing
|
||||
: a2.title.width + font.hspacing);
|
||||
}
|
||||
++nskip;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void draw_recs (Float128 min, Float128 step, double max_rec_size) {
|
||||
for (Float128 v = min; Math.point_belong (v, min, range.zmax); v += step) {
|
||||
if (is_x && chart.joint_x || !is_x && chart.joint_y) {
|
||||
chart.color = chart.joint_color;
|
||||
ser.axis_x.grid_style.color = Color(0, 0, 0, 0.5);
|
||||
ser.axis_y.grid_style.color = Color(0, 0, 0, 0.5);
|
||||
} else
|
||||
chart.color = color;
|
||||
string text = "", time_text = ""; var time_text_t = new Text(chart); var crpt = 0.0;
|
||||
switch (dtype) {
|
||||
case DType.NUMBERS: text = format.printf((LongDouble)v); break;
|
||||
case DType.DATE_TIME:
|
||||
print_dt(v, out text, out time_text);
|
||||
time_text_t = new Text(chart, time_text, font, color);
|
||||
crpt = compact_rec_pos (v, time_text_t);
|
||||
break;
|
||||
}
|
||||
var scr_v = scr_pos (v);
|
||||
var text_t = new Text(chart, text, font, color);
|
||||
var tthv = title.text == "" ? font.vspacing : title.height + 2 * font.vspacing;
|
||||
var ttwh = title.text == "" ? font.hspacing : title.width + 2 * font.hspacing;
|
||||
var dtf = (date_format == "" ? 0 : text_t.height + font.vspacing);
|
||||
var py0 = chart.plarea.y0, ey0 = chart.evarea.y0 + max_rec_size + tthv, ey1 = chart.evarea.y1 - tthv;
|
||||
var px0 = chart.plarea.x0, ex0 = chart.evarea.x0 + max_rec_size + ttwh, ex1 = chart.evarea.x1 - ttwh;
|
||||
var ph = chart.plarea.height, pw = chart.plarea.width;
|
||||
if (is_x) {
|
||||
var crp = compact_rec_pos (v, text_t);
|
||||
switch (position) {
|
||||
case Position.LOW:
|
||||
chart.ctx.move_to (crp, ey1); text_t.show();
|
||||
chart.ctx.move_to (crpt, ey1 - dtf); time_text_t.show();
|
||||
ser.axis_x.grid_style.apply(chart);
|
||||
double y = ey1 - max_rec_size; chart.ctx.move_to (scr_v, y);
|
||||
if (chart.joint_x) chart.ctx.line_to (scr_v, py0);
|
||||
else chart.ctx.line_to (scr_v, double.min (y, py0 + ph * (1 - ser.axis_y.place.zmax)));
|
||||
break;
|
||||
case Position.HIGH:
|
||||
chart.ctx.move_to (crp, ey0); text_t.show();
|
||||
chart.ctx.move_to (crpt, ey0 - dtf); time_text_t.show();
|
||||
ser.axis_x.grid_style.apply(chart); chart.ctx.move_to (scr_v, ey0);
|
||||
if (chart.joint_x) chart.ctx.line_to (scr_v, chart.plarea.y1);
|
||||
else chart.ctx.line_to (scr_v, double.max (ey0, py0 + ph * (1 - ser.axis_y.place.zmin)));
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
var crp = compact_rec_pos (v, text_t);
|
||||
switch (position) {
|
||||
case Position.LOW:
|
||||
chart.ctx.move_to (ex0 - text_t.width, crp);
|
||||
text_t.show(); ser.axis_y.grid_style.apply(chart);
|
||||
chart.ctx.move_to (ex0, scr_v);
|
||||
if (chart.joint_y) chart.ctx.line_to (chart.plarea.x1, scr_v);
|
||||
else chart.ctx.line_to (double.max (ex0, px0 + pw * ser.axis_x.place.zmax), scr_v);
|
||||
break;
|
||||
case Position.HIGH:
|
||||
chart.ctx.move_to (ex1 - text_t.width, crp);
|
||||
text_t.show(); ser.axis_y.grid_style.apply(chart);
|
||||
double x = ex1 - max_rec_size; chart.ctx.move_to (x, scr_v);
|
||||
if (chart.joint_y) chart.ctx.line_to (px0, scr_v);
|
||||
else chart.ctx.line_to (double.min (x, px0 + pw * ser.axis_x.place.zmin), scr_v);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
1956
src/Chart.vala
1956
src/Chart.vala
File diff suppressed because it is too large
Load Diff
|
@ -1,12 +1,46 @@
|
|||
namespace Gtk.CairoChart {
|
||||
namespace CairoChart {
|
||||
|
||||
/**
|
||||
* R/G/B/A Color.
|
||||
*/
|
||||
public struct Color {
|
||||
|
||||
/**
|
||||
* Red component.
|
||||
*/
|
||||
double red;
|
||||
|
||||
/**
|
||||
* Green component.
|
||||
*/
|
||||
double green;
|
||||
|
||||
/**
|
||||
* Blue component.
|
||||
*/
|
||||
double blue;
|
||||
|
||||
/**
|
||||
* Alpha component.
|
||||
*/
|
||||
double alpha;
|
||||
|
||||
public Color (double red = 0.0, double green = 0.0, double blue = 0.0, double alpha = 1.0) {
|
||||
this.red = red; this.green = green; this.blue = blue; this.alpha = alpha;
|
||||
/**
|
||||
* Constructs a new ``Color``.
|
||||
* @param red red component.
|
||||
* @param green green component.
|
||||
* @param blue blue component.
|
||||
* @param alpha alpha component.
|
||||
*/
|
||||
public Color (double red = 0,
|
||||
double green = 0,
|
||||
double blue = 0,
|
||||
double alpha = 1
|
||||
) {
|
||||
this.red = red;
|
||||
this.green = green;
|
||||
this.blue = blue;
|
||||
this.alpha = alpha;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,543 @@
|
|||
namespace CairoChart {
|
||||
|
||||
/**
|
||||
* {@link Chart} cursors.
|
||||
*/
|
||||
public class Cursors {
|
||||
|
||||
protected unowned Chart chart;
|
||||
protected List<Point?> list = new List<Point?> ();
|
||||
protected Point active_cursor = Point(); // { get; protected set; default = Point128 (); }
|
||||
protected bool is_cursor_active = false; // { get; protected set; default = false; }
|
||||
protected Crossings[] crossings = {};
|
||||
|
||||
/**
|
||||
* ``Cursors`` lines orientation.
|
||||
*/
|
||||
protected enum Orientation {
|
||||
/**
|
||||
* Vertical cursors.
|
||||
*/
|
||||
VERTICAL,
|
||||
|
||||
/**
|
||||
* Horizontal cursors.
|
||||
*/
|
||||
HORIZONTAL
|
||||
}
|
||||
|
||||
/**
|
||||
* ``Cursors`` style.
|
||||
*/
|
||||
public struct Style {
|
||||
/**
|
||||
* ``Cursors`` lines orientation.
|
||||
*/
|
||||
public Orientation orientation;
|
||||
|
||||
/**
|
||||
* Maximum distance between mouse and cursor to remove it.
|
||||
*/
|
||||
public double select_distance;
|
||||
|
||||
/**
|
||||
* ``Cursors`` line style.
|
||||
*/
|
||||
public LineStyle line_style;
|
||||
|
||||
/**
|
||||
* Constructs a new ``Style``.
|
||||
*/
|
||||
public Style () {
|
||||
orientation = Orientation.VERTICAL;
|
||||
select_distance = 32;
|
||||
line_style = LineStyle(Color(0.2, 0.2, 0.2, 0.8));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cursor style.
|
||||
*/
|
||||
public Style style = Style();
|
||||
|
||||
/**
|
||||
* Has crossings.
|
||||
*/
|
||||
public bool has_crossings { get { return crossings.length != 0; } protected set {} }
|
||||
|
||||
/**
|
||||
* Constructs a new ``Chart``.
|
||||
* @param chart ``Chart`` instance.
|
||||
*/
|
||||
public Cursors (Chart chart) {
|
||||
this.chart = chart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a copy of the ``Cursors``.
|
||||
*/
|
||||
public Cursors copy () {
|
||||
var c = new Cursors (chart);
|
||||
c.list = list.copy();
|
||||
c.active_cursor = active_cursor;
|
||||
c.is_cursor_active = is_cursor_active;
|
||||
c.style = style;
|
||||
c.crossings = crossings;
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets active cursor.
|
||||
* @param p ``Cursor`` position.
|
||||
* @param remove select for removing or not.
|
||||
*/
|
||||
public virtual void set_active (Point p, bool remove = false) {
|
||||
active_cursor.x = chart.zoom.x0 + (p.x - chart.plarea.x0) / chart.plarea.width * chart.zoom.width;
|
||||
active_cursor.y = chart.zoom.y1 - (chart.plarea.y1 - p.y) / chart.plarea.height * chart.zoom.height;
|
||||
is_cursor_active = ! remove;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds active cursor.
|
||||
*/
|
||||
public virtual void add_active () {
|
||||
list.append (active_cursor);
|
||||
is_cursor_active = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes active cursor.
|
||||
*/
|
||||
public virtual void remove_active () {
|
||||
if (list.length() == 0) return;
|
||||
var distance = 1024.0 * 1024;//width * width;
|
||||
uint rm_indx = 0;
|
||||
uint i = 0;
|
||||
foreach (var c in list) {
|
||||
double d = distance;
|
||||
switch (style.orientation) {
|
||||
case Orientation.VERTICAL: d = (rel2scr_x(c.x) - rel2scr_x(active_cursor.x)).abs(); break;
|
||||
case Orientation.HORIZONTAL: d = (rel2scr_y(c.y) - rel2scr_y(active_cursor.y)).abs(); break;
|
||||
}
|
||||
if (d < distance) {
|
||||
distance = d;
|
||||
rm_indx = i;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
if (distance < style.select_distance)
|
||||
list.delete_link(list.nth(rm_indx));
|
||||
is_cursor_active = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets delta between 2 cursors values.
|
||||
* @param delta returns delta value.
|
||||
*/
|
||||
public bool get_delta (out Float128 delta) {
|
||||
delta = 0;
|
||||
if (chart.series.length == 0) return false;
|
||||
if (list.length() + (is_cursor_active ? 1 : 0) != 2) return false;
|
||||
if (chart.joint_x && style.orientation == Orientation.VERTICAL) {
|
||||
Float128 val1 = chart.series[chart.zoom_1st_idx].axis_x.axis_val(rel2scr_x(list.nth_data(0).x));
|
||||
Float128 val2 = 0;
|
||||
if (is_cursor_active)
|
||||
val2 = chart.series[chart.zoom_1st_idx].axis_x.axis_val(rel2scr_x(active_cursor.x));
|
||||
else
|
||||
val2 = chart.series[chart.zoom_1st_idx].axis_x.axis_val(rel2scr_x(list.nth_data(1).x));
|
||||
if (val2 > val1)
|
||||
delta = val2 - val1;
|
||||
else
|
||||
delta = val1 - val2;
|
||||
return true;
|
||||
}
|
||||
if (chart.joint_y && style.orientation == Orientation.HORIZONTAL) {
|
||||
Float128 val1 = chart.series[chart.zoom_1st_idx].axis_y.axis_val(rel2scr_y(list.nth_data(0).y));
|
||||
Float128 val2 = 0;
|
||||
if (is_cursor_active)
|
||||
val2 = chart.series[chart.zoom_1st_idx].axis_y.axis_val(rel2scr_y(active_cursor.y));
|
||||
else
|
||||
val2 = chart.series[chart.zoom_1st_idx].axis_y.axis_val(rel2scr_y(list.nth_data(1).y));
|
||||
if (val2 > val1)
|
||||
delta = val2 - val1;
|
||||
else
|
||||
delta = val1 - val2;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets delta formatted string.
|
||||
*/
|
||||
public string get_delta_str () {
|
||||
Float128 delta = 0;
|
||||
if (!get_delta(out delta)) return "";
|
||||
var str = "";
|
||||
var s = chart.series[chart.zoom_1st_idx];
|
||||
if (chart.joint_x)
|
||||
switch (s.axis_x.dtype) {
|
||||
case Axis.DType.NUMBERS:
|
||||
str = s.axis_x.format.printf((LongDouble)delta);
|
||||
break;
|
||||
case Axis.DType.DATE_TIME:
|
||||
var date = "", time = "";
|
||||
int64 days = (int64)(delta / 24 / 3600);
|
||||
s.axis_x.print_dt(delta, out date, out time);
|
||||
str = days.to_string() + " + " + time;
|
||||
break;
|
||||
}
|
||||
if (chart.joint_y) {
|
||||
str = s.axis_y.format.printf((LongDouble)delta);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws cursors.
|
||||
*/
|
||||
public virtual void draw () {
|
||||
if (chart.series.length == 0) return;
|
||||
|
||||
var all_cursors = get_all_cursors();
|
||||
calc_cursors_value_positions();
|
||||
|
||||
for (var cci = 0, max_cci = crossings.length; cci < max_cci; ++cci) {
|
||||
var low = Point128(chart.plarea.x1, chart.plarea.y1); // low and high
|
||||
var high = Point128(chart.plarea.x0, chart.plarea.y0); // points of the cursor
|
||||
unowned Cross[] ccs = crossings[cci].crossings;
|
||||
style.line_style.apply(chart);
|
||||
for (var ci = 0, max_ci = ccs.length; ci < max_ci; ++ci) {
|
||||
var si = ccs[ci].series_index;
|
||||
var s = chart.series[si];
|
||||
var p = ccs[ci].point;
|
||||
var scrp = s.scr_pnt(p);
|
||||
if (scrp.x < low.x) low.x = scrp.x;
|
||||
if (scrp.y < low.y) low.y = scrp.y;
|
||||
if (scrp.x > high.x) high.x = scrp.x;
|
||||
if (scrp.y > high.y) high.y = scrp.y;
|
||||
|
||||
if (chart.joint_x) {
|
||||
switch (s.axis_x.position) {
|
||||
case Axis.Position.LOW: high.y = chart.plarea.y1 + s.axis_x.font.vspacing; break;
|
||||
case Axis.Position.HIGH: low.y = chart.plarea.y0 - s.axis_x.font.vspacing; break;
|
||||
case Axis.Position.BOTH:
|
||||
high.y = chart.plarea.y1 + s.axis_x.font.vspacing;
|
||||
low.y = chart.plarea.y0 - s.axis_x.font.vspacing;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (chart.joint_y) {
|
||||
switch (s.axis_y.position) {
|
||||
case Axis.Position.LOW: low.x = chart.plarea.x0 - s.axis_y.font.hspacing; break;
|
||||
case Axis.Position.HIGH: high.x = chart.plarea.x1 + s.axis_y.font.hspacing; break;
|
||||
case Axis.Position.BOTH:
|
||||
low.x = chart.plarea.x0 - s.axis_y.font.hspacing;
|
||||
high.x = chart.plarea.x1 + s.axis_y.font.hspacing;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
chart.ctx.move_to (ccs[ci].scr_point.x, ccs[ci].scr_point.y);
|
||||
chart.ctx.line_to (ccs[ci].scr_value_point.x, ccs[ci].scr_value_point.y);
|
||||
}
|
||||
|
||||
var c = all_cursors.nth_data(crossings[cci].cursor_index);
|
||||
|
||||
switch (style.orientation) {
|
||||
case Orientation.VERTICAL:
|
||||
if (low.y > high.y) continue;
|
||||
chart.ctx.move_to (rel2scr_x(c.x), low.y);
|
||||
chart.ctx.line_to (rel2scr_x(c.x), high.y);
|
||||
|
||||
// show joint X value
|
||||
if (chart.joint_x) {
|
||||
var s = chart.series[chart.zoom_1st_idx];
|
||||
var x = s.axis_x.axis_val(rel2scr_x(c.x));
|
||||
string text = "", time_text = "";
|
||||
switch (s.axis_x.dtype) {
|
||||
case Axis.DType.NUMBERS: text = s.axis_x.format.printf((LongDouble)x); break;
|
||||
case Axis.DType.DATE_TIME: s.axis_x.print_dt(x, out text, out time_text); break;
|
||||
}
|
||||
var text_t = new Text(chart, text, s.axis_x.font, s.axis_x.color);
|
||||
var time_text_t = new Text(chart, time_text, s.axis_x.font, s.axis_x.color);
|
||||
var print_y = 0.0;
|
||||
switch (s.axis_x.position) {
|
||||
case Axis.Position.LOW: print_y = chart.area.y1 - s.axis_x.font.vspacing
|
||||
- (chart.legend.position == Legend.Position.BOTTOM ? chart.legend.height : 0);
|
||||
break;
|
||||
case Axis.Position.HIGH:
|
||||
var title_height = chart.title.height + (chart.legend.position == Legend.Position.TOP ?
|
||||
chart.title.font.vspacing * 2 : chart.title.font.vspacing);
|
||||
print_y = chart.area.y0 + title_height + s.axis_x.font.vspacing
|
||||
+ (chart.legend.position == Legend.Position.TOP ? chart.legend.height : 0);
|
||||
switch (s.axis_x.dtype) {
|
||||
case Axis.DType.NUMBERS:
|
||||
print_y += text_t.height;
|
||||
break;
|
||||
case Axis.DType.DATE_TIME:
|
||||
print_y += (s.axis_x.date_format == "" ? 0 : text_t.height)
|
||||
+ (s.axis_x.time_format == "" ? 0 : time_text_t.height)
|
||||
+ (s.axis_x.date_format == "" || s.axis_x.time_format == "" ? 0 : s.axis_x.font.vspacing);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
var print_x = s.axis_x.compact_rec_pos (x, text_t);
|
||||
chart.ctx.move_to (print_x, print_y);
|
||||
|
||||
switch (s.axis_x.dtype) {
|
||||
case Axis.DType.NUMBERS:
|
||||
text_t.show();
|
||||
break;
|
||||
case Axis.DType.DATE_TIME:
|
||||
if (s.axis_x.date_format != "") text_t.show();
|
||||
print_x = s.axis_x.compact_rec_pos (x, time_text_t);
|
||||
chart.ctx.move_to (print_x, print_y - (s.axis_x.date_format == "" ? 0 : text_t.height + s.axis_x.font.vspacing));
|
||||
if (s.axis_x.time_format != "") time_text_t.show();
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Orientation.HORIZONTAL:
|
||||
if (low.x > high.x) continue;
|
||||
chart.ctx.move_to (low.x, rel2scr_y(c.y));
|
||||
chart.ctx.line_to (high.x, rel2scr_y(c.y));
|
||||
|
||||
// show joint Y value
|
||||
if (chart.joint_y) {
|
||||
var s = chart.series[chart.zoom_1st_idx];
|
||||
var y = s.axis_y.axis_val(rel2scr_y(c.y));
|
||||
var text_t = new Text(chart, s.axis_y.format.printf((LongDouble)y, s.axis_y.font));
|
||||
var print_y = s.axis_y.compact_rec_pos (y, text_t);
|
||||
var print_x = 0.0;
|
||||
switch (s.axis_y.position) {
|
||||
case Axis.Position.LOW:
|
||||
print_x = chart.area.x0 + s.axis_y.font.hspacing
|
||||
+ (chart.legend.position == Legend.Position.LEFT ? chart.legend.width : 0);
|
||||
break;
|
||||
case Axis.Position.HIGH:
|
||||
print_x = chart.area.x1 - text_t.width - s.axis_y.font.hspacing
|
||||
- (chart.legend.position == Legend.Position.RIGHT ? chart.legend.width : 0);
|
||||
break;
|
||||
}
|
||||
chart.ctx.move_to (print_x, print_y);
|
||||
text_t.show();
|
||||
}
|
||||
break;
|
||||
}
|
||||
chart.ctx.stroke ();
|
||||
|
||||
// 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 = chart.series[si];
|
||||
var point = ccs[ci].point;
|
||||
var size = ccs[ci].size;
|
||||
var svp = ccs[ci].scr_value_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;
|
||||
|
||||
chart.color = chart.bg_color;
|
||||
chart.ctx.rectangle (svp.x - size.x / 2, svp.y - size.y / 2, size.x, size.y);
|
||||
chart.ctx.fill();
|
||||
|
||||
if (show_x) {
|
||||
chart.color = s.axis_x.color;
|
||||
var text_t = new Text(chart, s.axis_x.format.printf((LongDouble)point.x), s.axis_x.font);
|
||||
chart.ctx.move_to (svp.x - size.x / 2, svp.y + text_t.height / 2);
|
||||
if (chart.joint_x) chart.color = chart.joint_color;
|
||||
text_t.show();
|
||||
}
|
||||
|
||||
if (show_time) {
|
||||
chart.color = s.axis_x.color;
|
||||
string date = "", time = "";
|
||||
s.axis_x.print_dt(point.x, out date, out time);
|
||||
var text_t = new Text(chart, time, s.axis_x.font);
|
||||
var y = svp.y + text_t.height / 2;
|
||||
if (show_date) y -= text_t.height / 2 + s.axis_x.font.vspacing / 2;
|
||||
chart.ctx.move_to (svp.x - size.x / 2, y);
|
||||
if (chart.joint_x) chart.color = chart.joint_color;
|
||||
text_t.show();
|
||||
}
|
||||
|
||||
if (show_date) {
|
||||
chart.color = s.axis_x.color;
|
||||
string date = "", time = "";
|
||||
s.axis_x.print_dt(point.x, out date, out time);
|
||||
var text_t = new Text(chart, date, s.axis_x.font);
|
||||
var y = svp.y + text_t.height / 2;
|
||||
if (show_time) y += text_t.height / 2 + s.axis_x.font.vspacing / 2;
|
||||
chart.ctx.move_to (svp.x - size.x / 2, y);
|
||||
if (chart.joint_x) chart.color = chart.joint_color;
|
||||
text_t.show();
|
||||
}
|
||||
|
||||
if (show_y) {
|
||||
chart.color = s.axis_y.color;
|
||||
var text_t = new Text(chart, s.axis_y.format.printf((LongDouble)point.y), s.axis_y.font);
|
||||
chart.ctx.move_to (svp.x + size.x / 2 - text_t.width, svp.y + text_t.height / 2);
|
||||
if (chart.joint_y) chart.color = chart.joint_color;
|
||||
text_t.show();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates crossings.
|
||||
*/
|
||||
public void eval_crossings () {
|
||||
var all_cursors = get_all_cursors();
|
||||
|
||||
Crossings[] local_cursor_crossings = {};
|
||||
|
||||
for (var ci = 0, max_ci = all_cursors.length(); ci < max_ci; ++ci) {
|
||||
var c = all_cursors.nth_data(ci);
|
||||
switch (style.orientation) {
|
||||
case Orientation.VERTICAL: if (c.x <= chart.zoom.x0 || c.x >= chart.zoom.x1) continue; break;
|
||||
case Orientation.HORIZONTAL: if (c.y <= chart.zoom.y0 || c.y >= chart.zoom.y1) continue; break;
|
||||
}
|
||||
|
||||
Cross[] crossings = {};
|
||||
for (var si = 0, max_si = chart.series.length; si < max_si; ++si) {
|
||||
var s = chart.series[si];
|
||||
if (!s.zoom_show) continue;
|
||||
|
||||
var points = Math.sort_points (s, s.sort);
|
||||
|
||||
for (var i = 0; i + 1 < points.length; ++i) {
|
||||
switch (style.orientation) {
|
||||
case Orientation.VERTICAL:
|
||||
Float128 y = 0;
|
||||
if (Math.vcross(s.scr_pnt(points[i]), s.scr_pnt(points[i+1]), rel2scr_x(c.x),
|
||||
chart.plarea.y0, chart.plarea.y1, out y)) {
|
||||
var point = Point128(s.axis_x.axis_val(rel2scr_x(c.x)), s.axis_y.axis_val(y));
|
||||
Point128 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);
|
||||
Cross cc = {si, point, size, show_x, show_date, show_time, show_y};
|
||||
crossings += cc;
|
||||
}
|
||||
break;
|
||||
case Orientation.HORIZONTAL:
|
||||
Float128 x = 0;
|
||||
if (Math.hcross(s.scr_pnt(points[i]), s.scr_pnt(points[i+1]),
|
||||
chart.plarea.x0, chart.plarea.x1, rel2scr_y(c.y), out x)) {
|
||||
var point = Point128(s.axis_x.axis_val(x), s.axis_y.axis_val(rel2scr_y(c.y)));
|
||||
Point128 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);
|
||||
Cross cc = {si, point, size, show_x, show_date, show_time, show_y};
|
||||
crossings += cc;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (crossings.length != 0) {
|
||||
Crossings ccs = {ci, crossings};
|
||||
local_cursor_crossings += ccs;
|
||||
}
|
||||
}
|
||||
crossings = local_cursor_crossings;
|
||||
}
|
||||
|
||||
protected struct Cross {
|
||||
uint series_index;
|
||||
Point128 point;
|
||||
Point128 size;
|
||||
bool show_x;
|
||||
bool show_date;
|
||||
bool show_time;
|
||||
bool show_y;
|
||||
Point scr_point;
|
||||
Point scr_value_point;
|
||||
}
|
||||
|
||||
protected struct Crossings {
|
||||
uint cursor_index;
|
||||
Cross[] crossings;
|
||||
}
|
||||
|
||||
protected virtual Float128 rel2scr_x(Float128 x) {
|
||||
return chart.plarea.x0 + chart.plarea.width * (x - chart.zoom.x0) / chart.zoom.width;
|
||||
}
|
||||
|
||||
protected virtual Float128 rel2scr_y(Float128 y) {
|
||||
return chart.plarea.y0 + chart.plarea.height * (y - chart.zoom.y0) / chart.zoom.height;
|
||||
}
|
||||
|
||||
protected List<Point?> get_all_cursors () {
|
||||
var all_cursors = list.copy_deep ((src) => { return src; });
|
||||
if (is_cursor_active)
|
||||
all_cursors.append(active_cursor);
|
||||
return all_cursors;
|
||||
}
|
||||
|
||||
protected virtual void calc_cursors_value_positions () {
|
||||
for (var ccsi = 0, max_ccsi = crossings.length; ccsi < max_ccsi; ++ccsi) {
|
||||
for (var cci = 0, max_cci = crossings[ccsi].crossings.length; cci < max_cci; ++cci) {
|
||||
// TODO: Ticket #142: find smart algorithm of cursors values placements
|
||||
unowned Cross[] cr = crossings[ccsi].crossings;
|
||||
cr[cci].scr_point = chart.series[cr[cci].series_index].scr_pnt (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 (style.orientation) {
|
||||
case Orientation.VERTICAL:
|
||||
show_y = true;
|
||||
if (!chart.joint_x)
|
||||
switch (s.axis_x.dtype) {
|
||||
case Axis.DType.NUMBERS: show_x = true; break;
|
||||
case Axis.DType.DATE_TIME:
|
||||
if (s.axis_x.date_format != "") show_date = true;
|
||||
if (s.axis_x.time_format != "") show_time = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Orientation.HORIZONTAL:
|
||||
if (!chart.joint_y) show_y = true;
|
||||
switch (s.axis_x.dtype) {
|
||||
case Axis.DType.NUMBERS: show_x = true; break;
|
||||
case Axis.DType.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, Point128 p, out Point128 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 = Point128 ();
|
||||
string date, time;
|
||||
s.axis_x.print_dt(p.x, out date, out time);
|
||||
var date_t = new Text(chart, date, s.axis_x.font, s.axis_x.color);
|
||||
var time_t = new Text(chart, time, s.axis_x.font, s.axis_x.color);
|
||||
var x_t = new Text(chart, s.axis_x.format.printf((LongDouble)p.x), s.axis_x.font, s.axis_x.color);
|
||||
var y_t = new Text(chart, s.axis_y.format.printf((LongDouble)p.y), s.axis_y.font, s.axis_y.color);
|
||||
var h_x = 0.0, h_y = 0.0;
|
||||
if (show_x) { size.x = x_t.width; h_x = x_t.height; }
|
||||
if (show_date) { size.x = date_t.width; h_x = date_t.height; }
|
||||
if (show_time) { size.x = double.max(size.x, time_t.width); h_x += time_t.height; }
|
||||
if (show_y) { size.x += y_t.width; h_y = y_t.height; }
|
||||
if ((show_x || show_date || show_time) && show_y) size.x += double.max(s.axis_x.font.hspacing, s.axis_y.font.hspacing);
|
||||
if (show_date && show_time) h_x += s.axis_x.font.hspacing;
|
||||
size.y = double.max (h_x, h_y);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
namespace CairoChart {
|
||||
|
||||
/**
|
||||
* ``Font`` style.
|
||||
*/
|
||||
[Compact]
|
||||
public class Font : Object {
|
||||
|
||||
/**
|
||||
* A font family name, encoded in UTF-8.
|
||||
*/
|
||||
public virtual string family { get; set; }
|
||||
|
||||
/**
|
||||
* The new font size, in user space units.
|
||||
*/
|
||||
public virtual double size { get; set; }
|
||||
|
||||
/**
|
||||
* The slant for the font.
|
||||
*/
|
||||
public virtual Cairo.FontSlant slant { get; set; }
|
||||
|
||||
/**
|
||||
* The weight for the font.
|
||||
*/
|
||||
public virtual Cairo.FontWeight weight { get; set; }
|
||||
|
||||
/**
|
||||
* Font/Text orientation.
|
||||
*/
|
||||
public virtual Gtk.Orientation orient { get; set; }
|
||||
|
||||
/**
|
||||
* Vertical spacing.
|
||||
*/
|
||||
public double vspacing = 4;
|
||||
|
||||
/**
|
||||
* Horizontal spacing.
|
||||
*/
|
||||
public double hspacing = 4;
|
||||
|
||||
/**
|
||||
* Both vertical & horizontal spacing (set only).
|
||||
*/
|
||||
public virtual double spacing {
|
||||
protected get {
|
||||
return 0;
|
||||
}
|
||||
set {
|
||||
vspacing = hspacing = value;
|
||||
}
|
||||
default = 4;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new ``Font``.
|
||||
* @param family a font family name, encoded in UTF-8.
|
||||
* @param size the new font size, in user space units.
|
||||
* @param slant the slant for the font.
|
||||
* @param weight the weight for the font.
|
||||
* @param orient font/text orientation.
|
||||
*/
|
||||
public Font (string family = "Sans",
|
||||
double size = 10,
|
||||
Cairo.FontSlant slant = Cairo.FontSlant.NORMAL,
|
||||
Cairo.FontWeight weight = Cairo.FontWeight.NORMAL,
|
||||
Gtk.Orientation orient = Gtk.Orientation.HORIZONTAL,
|
||||
double vspacing = 4,
|
||||
double hspacing = 4
|
||||
) {
|
||||
this.family = family;
|
||||
this.size = size;
|
||||
this.slant = slant;
|
||||
this.weight = weight;
|
||||
this.orient = orient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a copy of the ``Font``.
|
||||
*/
|
||||
public virtual Font copy () {
|
||||
var f = new Font(family, size, slant, weight, orient);
|
||||
f.vspacing = vspacing;
|
||||
f.hspacing = hspacing;
|
||||
return f;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
namespace Gtk.CairoChart {
|
||||
public enum FontOrient {
|
||||
HORIZONTAL = 0,
|
||||
VERTICAL
|
||||
}
|
||||
|
||||
public struct FontStyle {
|
||||
string family;
|
||||
Cairo.FontSlant slant;
|
||||
Cairo.FontWeight weight;
|
||||
|
||||
FontOrient orientation;
|
||||
double size;
|
||||
|
||||
public FontStyle (string family = "Sans",
|
||||
Cairo.FontSlant slant = Cairo.FontSlant.NORMAL,
|
||||
Cairo.FontWeight weight = Cairo.FontWeight.NORMAL,
|
||||
double size = 10,
|
||||
FontOrient orientation = FontOrient.HORIZONTAL) {
|
||||
this.family = family;
|
||||
this.slant = slant;
|
||||
this.weight = weight;
|
||||
this.size = size;
|
||||
this.orientation = orientation;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
namespace Gtk.CairoChart {
|
||||
public class Grid {
|
||||
/*public enum GridType {
|
||||
PRICK_LINE = 0, // default
|
||||
LINE
|
||||
}*/
|
||||
public Color color = Color (0, 0, 0, 0.1);
|
||||
|
||||
public LineStyle line_style = LineStyle ();
|
||||
|
||||
public Grid copy () {
|
||||
var grid = new Grid ();
|
||||
grid.color = this.color;
|
||||
grid.line_style = this.line_style;
|
||||
return grid;
|
||||
}
|
||||
|
||||
public Grid () {
|
||||
line_style.dashes = {2, 3};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
namespace CairoChart {
|
||||
|
||||
/**
|
||||
* ``LabelStyle`` Style.
|
||||
*/
|
||||
public struct LabelStyle {
|
||||
|
||||
/**
|
||||
* Font style.
|
||||
*/
|
||||
Font font;
|
||||
|
||||
/**
|
||||
* Frame line style.
|
||||
*/
|
||||
LineStyle frame_line_style;
|
||||
|
||||
/**
|
||||
* Background color.
|
||||
*/
|
||||
Color bg_color;
|
||||
|
||||
/**
|
||||
* Frame/border color.
|
||||
*/
|
||||
Color frame_color;
|
||||
}
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
namespace Gtk.CairoChart {
|
||||
public struct LabelStyle {
|
||||
FontStyle font_style;
|
||||
LineStyle frame_line_style;
|
||||
Color bg_color;
|
||||
Color frame_color;
|
||||
}
|
||||
}
|
273
src/Legend.vala
273
src/Legend.vala
|
@ -1,28 +1,275 @@
|
|||
namespace Gtk.CairoChart {
|
||||
namespace CairoChart {
|
||||
|
||||
/**
|
||||
* {@link Chart} ``Legend``.
|
||||
*/
|
||||
public class Legend {
|
||||
|
||||
protected unowned Chart chart;
|
||||
protected double [] max_font_heights;
|
||||
|
||||
/**
|
||||
* Show legend?
|
||||
*/
|
||||
public bool show = true;
|
||||
|
||||
/**
|
||||
* ``Legend`` position.
|
||||
*/
|
||||
public enum Position {
|
||||
TOP = 0, // default
|
||||
/**
|
||||
* Top position.
|
||||
*/
|
||||
TOP = 0,
|
||||
|
||||
/**
|
||||
* Left position.
|
||||
*/
|
||||
LEFT,
|
||||
|
||||
/**
|
||||
* Right position.
|
||||
*/
|
||||
RIGHT,
|
||||
|
||||
/**
|
||||
* Bottom position.
|
||||
*/
|
||||
BOTTOM
|
||||
}
|
||||
public Position position = Position.TOP;
|
||||
public FontStyle font_style = FontStyle();
|
||||
public Color bg_color = Color(1, 1, 1);
|
||||
public LineStyle border_style = LineStyle ();
|
||||
public double indent = 5;
|
||||
|
||||
public Legend copy () {
|
||||
var legend = new Legend ();
|
||||
/**
|
||||
* Position.
|
||||
*/
|
||||
public Position position = Position.TOP;
|
||||
|
||||
/**
|
||||
* ``Legend`` background color.
|
||||
*/
|
||||
public Color bg_color = Color(1, 1, 1);
|
||||
|
||||
/**
|
||||
* Border line style.
|
||||
*/
|
||||
public LineStyle border_style = LineStyle ();
|
||||
|
||||
/**
|
||||
* Both vertical & horizontal spacing.
|
||||
*/
|
||||
public double spacing = 5;
|
||||
|
||||
/**
|
||||
* ``Legend`` width.
|
||||
*/
|
||||
public double width { get; protected set; }
|
||||
|
||||
/**
|
||||
* ``Legend`` height.
|
||||
*/
|
||||
public double height { get; protected set; }
|
||||
|
||||
/**
|
||||
* {@link Series} line length.
|
||||
*/
|
||||
public double line_length = 30;
|
||||
|
||||
/**
|
||||
* Constructs a new ``Legend``.
|
||||
* @param chart ``Chart`` instance.
|
||||
*/
|
||||
public Legend (Chart chart) {
|
||||
this.chart = chart;
|
||||
border_style.color = Color (0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a copy of the ``Legend``.
|
||||
*/
|
||||
public virtual Legend copy () {
|
||||
var legend = new Legend (chart);
|
||||
legend.position = this.position;
|
||||
legend.font_style = this.font_style;
|
||||
legend.bg_color = this.bg_color;
|
||||
legend.indent = this.indent;
|
||||
legend.spacing = this.spacing;
|
||||
legend.height = this.height;
|
||||
legend.line_length = this.line_length;
|
||||
legend.width = this.width;
|
||||
legend.show = this.show;
|
||||
legend.max_font_heights = this.max_font_heights;
|
||||
return legend;
|
||||
}
|
||||
|
||||
public Legend () {
|
||||
border_style.color = Color (0, 0, 0, 0.3);
|
||||
/**
|
||||
* Draws the ``Legend``.
|
||||
*/
|
||||
public virtual void draw () {
|
||||
if (!show) return;
|
||||
process (ProcessType.CALC);
|
||||
process (ProcessType.DRAW);
|
||||
}
|
||||
|
||||
protected virtual void draw_rect (out double x0, out double y0) {
|
||||
x0 = y0 = 0;
|
||||
if (chart.ctx != null) {
|
||||
switch (position) {
|
||||
case Position.TOP:
|
||||
x0 = (chart.area.width - width) / 2;
|
||||
var title_height = chart.title.height + (chart.legend.position == Position.TOP ?
|
||||
chart.title.font.vspacing * 2 : chart.title.font.vspacing);
|
||||
y0 = title_height;
|
||||
break;
|
||||
|
||||
case Position.BOTTOM:
|
||||
x0 = (chart.area.width - width) / 2;
|
||||
y0 = chart.area.height - height;
|
||||
break;
|
||||
|
||||
case Position.LEFT:
|
||||
x0 = 0;
|
||||
y0 = (chart.area.height - height) / 2;
|
||||
break;
|
||||
|
||||
case Position.RIGHT:
|
||||
x0 = chart.area.width - width;
|
||||
y0 = (chart.area.height - height) / 2;
|
||||
break;
|
||||
}
|
||||
chart.color = bg_color;
|
||||
chart.ctx.rectangle (x0, y0, width, height);
|
||||
chart.ctx.fill();
|
||||
border_style.apply(chart);
|
||||
chart.ctx.move_to (x0, y0);
|
||||
chart.ctx.rel_line_to (width, 0);
|
||||
chart.ctx.rel_line_to (0, height);
|
||||
chart.ctx.rel_line_to (-width, 0);
|
||||
chart.ctx.rel_line_to (0, -height);
|
||||
chart.ctx.stroke ();
|
||||
}
|
||||
}
|
||||
|
||||
protected enum ProcessType {
|
||||
CALC = 0, // default
|
||||
DRAW
|
||||
}
|
||||
|
||||
protected virtual void process (ProcessType process_type) {
|
||||
var legend_x0 = 0.0, legend_y0 = 0.0;
|
||||
var heights_idx = 0;
|
||||
var leg_width_sum = 0.0;
|
||||
var leg_height_sum = 0.0;
|
||||
var max_font_h = 0.0;
|
||||
|
||||
double [] mfh = max_font_heights;
|
||||
|
||||
// prepare
|
||||
switch (process_type) {
|
||||
case ProcessType.CALC:
|
||||
width = 0;
|
||||
height = 0;
|
||||
mfh = {};
|
||||
heights_idx = 0;
|
||||
break;
|
||||
case ProcessType.DRAW:
|
||||
draw_rect(out legend_x0, out legend_y0);
|
||||
break;
|
||||
}
|
||||
|
||||
foreach (var s in chart.series) {
|
||||
if (!s.zoom_show) continue;
|
||||
|
||||
// carry
|
||||
switch (position) {
|
||||
case Position.TOP:
|
||||
case Position.BOTTOM:
|
||||
var ser_title_width = s.title.width + line_length;
|
||||
if (leg_width_sum + (leg_width_sum == 0 ? 0 : s.title.font.hspacing) + ser_title_width > chart.area.width) { // carry
|
||||
leg_height_sum += max_font_h;
|
||||
switch (process_type) {
|
||||
case ProcessType.CALC:
|
||||
mfh += max_font_h;
|
||||
width = double.max(width, leg_width_sum);
|
||||
break;
|
||||
case ProcessType.DRAW:
|
||||
heights_idx++;
|
||||
break;
|
||||
}
|
||||
leg_width_sum = 0;
|
||||
max_font_h = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
switch (process_type) {
|
||||
case ProcessType.DRAW:
|
||||
var x = legend_x0 + leg_width_sum + (leg_width_sum == 0 ? 0 : s.title.font.hspacing);
|
||||
var y = legend_y0 + leg_height_sum + mfh[heights_idx] / 2 + s.title.height / 2;
|
||||
|
||||
// series title
|
||||
chart.ctx.move_to (x + line_length, y);
|
||||
chart.color = s.title.color;
|
||||
s.title.show();
|
||||
|
||||
// series line style
|
||||
chart.ctx.move_to (x, y - s.title.height / 2);
|
||||
s.line_style.apply(chart);
|
||||
chart.ctx.rel_line_to (line_length, 0);
|
||||
chart.ctx.stroke();
|
||||
s.marker.draw_at_pos (Point(x + line_length / 2, y - s.title.height / 2));
|
||||
break;
|
||||
}
|
||||
|
||||
switch (position) {
|
||||
case Position.TOP:
|
||||
case Position.BOTTOM:
|
||||
var ser_title_width = s.title.width + line_length;
|
||||
leg_width_sum += (leg_width_sum == 0 ? 0 : s.title.font.hspacing) + ser_title_width;
|
||||
max_font_h = double.max (max_font_h, s.title.height) + (leg_height_sum != 0 ? s.title.font.vspacing : 0);
|
||||
break;
|
||||
|
||||
case Position.LEFT:
|
||||
case Position.RIGHT:
|
||||
switch (process_type) {
|
||||
case ProcessType.CALC:
|
||||
mfh += s.title.height + (leg_height_sum != 0 ? s.title.font.vspacing : 0);
|
||||
width = double.max (width, s.title.width + line_length);
|
||||
break;
|
||||
case ProcessType.DRAW:
|
||||
heights_idx++;
|
||||
break;
|
||||
}
|
||||
leg_height_sum += s.title.height + (leg_height_sum != 0 ? s.title.font.vspacing : 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// TOP, BOTTOM
|
||||
switch (position) {
|
||||
case Position.TOP:
|
||||
case Position.BOTTOM:
|
||||
if (leg_width_sum != 0) {
|
||||
leg_height_sum += max_font_h;
|
||||
switch (process_type) {
|
||||
case ProcessType.CALC:
|
||||
mfh += max_font_h;
|
||||
width = double.max(width, leg_width_sum);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
switch (process_type) {
|
||||
case ProcessType.CALC:
|
||||
height = leg_height_sum;
|
||||
switch (position) {
|
||||
case Position.TOP: chart.evarea.y0 += height; break;
|
||||
case Position.BOTTOM: chart.evarea.y1 -= height; break;
|
||||
case Position.LEFT: chart.evarea.x0 += width; break;
|
||||
case Position.RIGHT: chart.evarea.x1 -= width; break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
max_font_heights = mfh;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
namespace CairoChart {
|
||||
|
||||
/**
|
||||
* Line Style.
|
||||
*/
|
||||
public struct LineStyle {
|
||||
|
||||
/**
|
||||
* Line color.
|
||||
*/
|
||||
Color color;
|
||||
|
||||
/**
|
||||
* A line width.
|
||||
*/
|
||||
double width;
|
||||
|
||||
/**
|
||||
* An array specifying alternate lengths of on and off stroke portions.
|
||||
*/
|
||||
double[]? dashes;
|
||||
|
||||
/**
|
||||
* An offset into the dash pattern at which the stroke should start.
|
||||
*/
|
||||
double dash_offset;
|
||||
/**
|
||||
* A line join style.
|
||||
*/
|
||||
Cairo.LineJoin join;
|
||||
|
||||
/**
|
||||
* A line cap style.
|
||||
*/
|
||||
Cairo.LineCap cap;
|
||||
|
||||
/**
|
||||
* Constructs a new ``LineStyle``.
|
||||
* @param color line color.
|
||||
* @param width a line width.
|
||||
* @param dashes an array specifying alternate lengths of on and off stroke portions.
|
||||
* @param dash_offset an offset into the dash pattern at which the stroke should start.
|
||||
* @param join a line join style.
|
||||
* @param cap a line cap style.
|
||||
*/
|
||||
public LineStyle (Color color = Color(),
|
||||
double width = 1,
|
||||
double[]? dashes = null,
|
||||
double dash_offset = 0,
|
||||
Cairo.LineJoin join = Cairo.LineJoin.MITER,
|
||||
Cairo.LineCap cap = Cairo.LineCap.ROUND
|
||||
) {
|
||||
this.color = color;
|
||||
this.width = width;
|
||||
this.dashes = dashes;
|
||||
this.dash_offset = dash_offset;
|
||||
this.join = join;
|
||||
this.cap = cap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies current style to the {@link Chart} ``Context``.
|
||||
*/
|
||||
public void apply (Chart chart) {
|
||||
chart.color = color;
|
||||
chart.ctx.set_line_width(width);
|
||||
chart.ctx.set_dash(dashes, dash_offset);
|
||||
chart.ctx.set_line_join(join);
|
||||
chart.ctx.set_line_cap(cap);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
namespace Gtk.CairoChart {
|
||||
public struct LineStyle {
|
||||
double width;
|
||||
Cairo.LineJoin line_join;
|
||||
Cairo.LineCap line_cap;
|
||||
double[]? dashes;
|
||||
double dash_offset;
|
||||
Color color;
|
||||
|
||||
public LineStyle (Color color = Color(),
|
||||
double width = 1,
|
||||
double[]? dashes = null, double dash_offset = 0,
|
||||
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;
|
||||
this.dashes = dashes;
|
||||
this.dash_offset = dash_offset;
|
||||
this.color = color;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
namespace CairoChart {
|
||||
|
||||
/**
|
||||
* {@link Series} Marker.
|
||||
*/
|
||||
public class Marker {
|
||||
|
||||
protected unowned Chart chart;
|
||||
|
||||
/**
|
||||
* ``Marker`` shape.
|
||||
*/
|
||||
public enum Shape {
|
||||
NONE = 0,
|
||||
SQUARE,
|
||||
CIRCLE,
|
||||
TRIANGLE,
|
||||
PRICLE_SQUARE,
|
||||
PRICLE_CIRCLE,
|
||||
PRICLE_TRIANGLE
|
||||
}
|
||||
|
||||
/**
|
||||
* ``Marker`` shape.
|
||||
*/
|
||||
public Shape shape;
|
||||
|
||||
/**
|
||||
* ``Marker`` size.
|
||||
*/
|
||||
public double size;
|
||||
|
||||
/**
|
||||
* Constructs a new ``Marker``.
|
||||
* @param chart ``Chart`` instance.
|
||||
* @param shape ``Marker`` shape.
|
||||
* @param size ``Marker`` size.
|
||||
*/
|
||||
public Marker (Chart chart,
|
||||
Shape shape = Shape.NONE,
|
||||
double size = 8
|
||||
) {
|
||||
this.chart = chart;
|
||||
this.shape = shape;
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a copy of the ``Marker``.
|
||||
*/
|
||||
public virtual Marker copy () {
|
||||
return new Marker (chart, shape, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws the ``Marker`` at specific position.
|
||||
* @param p coordinates.
|
||||
*/
|
||||
public virtual void draw_at_pos (Point p) {
|
||||
chart.ctx.move_to (p.x, p.y);
|
||||
switch (shape) {
|
||||
case Shape.SQUARE:
|
||||
chart.ctx.rectangle (p.x - size / 2, p.y - size / 2, size, size);
|
||||
chart.ctx.fill();
|
||||
break;
|
||||
|
||||
case Shape.CIRCLE:
|
||||
chart.ctx.arc (p.x, p.y, size / 2, 0, 2 * GLib.Math.PI);
|
||||
chart.ctx.fill();
|
||||
break;
|
||||
|
||||
case Shape.TRIANGLE:
|
||||
chart.ctx.move_to (p.x - size / 2, p.y - size / 2);
|
||||
chart.ctx.rel_line_to (size, 0);
|
||||
chart.ctx.rel_line_to (-size / 2, size);
|
||||
chart.ctx.rel_line_to (-size / 2, -size);
|
||||
chart.ctx.fill();
|
||||
break;
|
||||
|
||||
case Shape.PRICLE_SQUARE:
|
||||
chart.ctx.rectangle (p.x - size / 2, p.y - size / 2, size, size);
|
||||
chart.ctx.stroke();
|
||||
break;
|
||||
|
||||
case Shape.PRICLE_CIRCLE:
|
||||
chart.ctx.arc (p.x, p.y, size / 2, 0, 2 * GLib.Math.PI);
|
||||
chart.ctx.stroke();
|
||||
break;
|
||||
|
||||
case Shape.PRICLE_TRIANGLE:
|
||||
chart.ctx.move_to (p.x - size / 2, p.y - size / 2);
|
||||
chart.ctx.rel_line_to (size, 0);
|
||||
chart.ctx.rel_line_to (-size / 2, size);
|
||||
chart.ctx.rel_line_to (-size / 2, -size);
|
||||
chart.ctx.stroke();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,161 @@
|
|||
namespace CairoChart {
|
||||
|
||||
namespace Math {
|
||||
|
||||
internal Float128 calc_round_step (Float128 aver_step, bool date_time = false) {
|
||||
Float128 step = 1;
|
||||
|
||||
if (aver_step > 1) {
|
||||
if (date_time) while (step < aver_step) step *= 60;
|
||||
if (date_time) while (step < aver_step) step *= 60;
|
||||
if (date_time) while (step < aver_step) step *= 24;
|
||||
while (step < aver_step) step *= 10;
|
||||
if (step / 5 > aver_step) step /= 5;
|
||||
while (step / 2 > aver_step) step /= 2;
|
||||
} else if (aver_step > 0) {
|
||||
while (step / 10 > aver_step) step /= 10;
|
||||
if (step / 5 > aver_step) step /= 5;
|
||||
while (step / 2 > aver_step) step /= 2;
|
||||
}
|
||||
|
||||
return step;
|
||||
}
|
||||
|
||||
internal bool coord_cross (double a_min, double a_max, double b_min, double b_max) {
|
||||
if ( a_min < a_max <= b_min < b_max
|
||||
|| b_min < b_max <= a_min < a_max)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*internal bool rect_cross (Cairo.Rectangle r1, Cairo.Rectangle r2) {
|
||||
return coord_cross(r1.x, r1.x + r1.width, r2.x, r2.x + r2.width)
|
||||
&& coord_cross(r1.y, r1.y + r1.height, r2.y, r2.y + r2.height);
|
||||
}*/
|
||||
|
||||
internal bool point_belong (Float128 p, Float128 a, Float128 b) {
|
||||
if (a > b) { Float128 tmp = a; a = b; b = tmp; }
|
||||
if (a <= p <= b) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
internal bool x_in_range (double x, double x0, double x1) {
|
||||
if (x0 <= x <= x1 || x1 <= x <= x0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
internal bool y_in_range (double y, double y0, double y1) {
|
||||
if (y0 <= y <= y1 || y1 <= y <= y0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
internal bool point_in_rect (Point p, double x0, double x1, double y0, double y1) {
|
||||
if (x_in_range(p.x, x0, x1) && y_in_range(p.y, y0, y1))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
internal bool hcross (Point a1, Point a2, double h_x1, double h_x2, double h_y, out double x) {
|
||||
x = 0;
|
||||
if (a1.y == a2.y) return false;
|
||||
if (a1.y >= h_y && a2.y >= h_y || a1.y <= h_y && a2.y <= h_y) return false;
|
||||
x = a1.x + (a2.x - a1.x) * (h_y - a1.y) / (a2.y - a1.y);
|
||||
if (h_x1 <= x <= h_x2 || h_x2 <= x <= h_x1)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
internal bool vcross (Point a1, Point a2, double v_x, double v_y1, double v_y2, out double y) {
|
||||
y = 0;
|
||||
if (a1.x == a2.x) return false;
|
||||
if (a1.x >= v_x && a2.x >= v_x || a1.x <= v_x && a2.x <= v_x) return false;
|
||||
y = a1.y + (a2.y - a1.y) * (v_x - a1.x) / (a2.x - a1.x);
|
||||
if (v_y1 <= y <= v_y2 || v_y2 <= y <= v_y1)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
internal delegate int PointComparator(Point128 a, Point128 b);
|
||||
|
||||
internal void sort_points_delegate(Point128[] 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) {
|
||||
var tmp = points[i];
|
||||
points[i] = points[j];
|
||||
points[j] = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal bool cut_line (Point p_min, Point p_max, Point a, Point b, out Point c, out Point d) {
|
||||
int ncross = 0;
|
||||
Float128 x = 0, y = 0;
|
||||
Point pc[4];
|
||||
if (hcross(a, b, p_min.x, p_max.x, p_min.y, out x))
|
||||
pc[ncross++] = Point(x, p_min.y);
|
||||
if (hcross(a, b, p_min.x, p_max.x, p_max.y, out x))
|
||||
pc[ncross++] = Point(x, p_max.y);
|
||||
if (vcross(a, b, p_min.x, p_min.y, p_max.y, out y))
|
||||
pc[ncross++] = Point(p_min.x, y);
|
||||
if (vcross(a, b, p_max.x, p_min.y, p_max.y, out y))
|
||||
pc[ncross++] = Point(p_max.x, y);
|
||||
c = a;
|
||||
d = b;
|
||||
if (ncross == 0) {
|
||||
if ( point_in_rect (a, p_min.x, p_max.x, p_min.y, p_max.y)
|
||||
&& point_in_rect (b, p_min.x, p_max.x, p_min.y, p_max.y))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
if (ncross >= 2) {
|
||||
c = pc[0]; d = pc[1];
|
||||
return true;
|
||||
}
|
||||
if (ncross == 1) {
|
||||
if (point_in_rect (a, p_min.x, p_max.x, p_min.y, p_max.y)) {
|
||||
c = a;
|
||||
d = pc[0];
|
||||
return true;
|
||||
} else if (point_in_rect (b, p_min.x, p_max.x, p_min.y, p_max.y)) {
|
||||
c = b;
|
||||
d = pc[0];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
internal Point128[] sort_points (Series s, Series.Sort sort) {
|
||||
var points = s.points;
|
||||
switch(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;
|
||||
}
|
||||
|
||||
internal int find_arr<G> (G[] arr, G elem) {
|
||||
for (var i = 0; i < arr.length; ++i) {
|
||||
if (arr[i] == elem)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
namespace Gtk.CairoChart {
|
||||
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 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,9 +1,51 @@
|
|||
namespace Gtk.CairoChart {
|
||||
namespace CairoChart {
|
||||
|
||||
/**
|
||||
* 64-bit point.
|
||||
*/
|
||||
public struct Point {
|
||||
|
||||
/**
|
||||
* X-coordinate.
|
||||
*/
|
||||
double x;
|
||||
|
||||
/**
|
||||
* Y-coordinate.
|
||||
*/
|
||||
double y;
|
||||
|
||||
/**
|
||||
* Constructs a new ``Point``.
|
||||
* @param x x-coordinate.
|
||||
* @param y y-coordinate.
|
||||
*/
|
||||
public Point (double x = 0, double y = 0) {
|
||||
this.x = x; this.y = y;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 128-bit point.
|
||||
*/
|
||||
public struct Point128 {
|
||||
|
||||
/**
|
||||
* X-coordinate.
|
||||
*/
|
||||
Float128 x;
|
||||
|
||||
/**
|
||||
* Y-coordinate.
|
||||
*/
|
||||
Float128 y;
|
||||
|
||||
public Point (Float128 x = 0.0, Float128 y = 0.0) {
|
||||
/**
|
||||
* Constructs a new ``Point128``.
|
||||
* @param x x-coordinate.
|
||||
* @param y y-coordinate.
|
||||
*/
|
||||
public Point128 (Float128 x = 0, Float128 y = 0) {
|
||||
this.x = x; this.y = y;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,118 @@
|
|||
namespace CairoChart {
|
||||
|
||||
/**
|
||||
* Linear range.
|
||||
*/
|
||||
public class Range {
|
||||
|
||||
protected Float128 _min = 0;
|
||||
protected Float128 _max = 1;
|
||||
|
||||
/**
|
||||
* Zoomed min bound.
|
||||
*/
|
||||
public Float128 zmin = 0;
|
||||
|
||||
/**
|
||||
* Zoomed max bound.
|
||||
*/
|
||||
public Float128 zmax = 1;
|
||||
|
||||
/**
|
||||
* Low bound.
|
||||
*/
|
||||
public virtual Float128 min {
|
||||
get {
|
||||
return _min;
|
||||
}
|
||||
set {
|
||||
zmin = _min = value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* High bound.
|
||||
*/
|
||||
public virtual Float128 max {
|
||||
get {
|
||||
return _max;
|
||||
}
|
||||
set {
|
||||
zmax = _max = value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ``Range`` value.
|
||||
*/
|
||||
public virtual Float128 range {
|
||||
get {
|
||||
return _max - _min;
|
||||
}
|
||||
set {
|
||||
zmax = _max = _min + value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ``Range`` zoomed value.
|
||||
*/
|
||||
public virtual Float128 zrange {
|
||||
get {
|
||||
return zmax - zmin;
|
||||
}
|
||||
set {
|
||||
zmax = zmin + value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new ``Range``.
|
||||
*/
|
||||
public Range () { }
|
||||
|
||||
/**
|
||||
* Constructs a new ``Range`` with a ``Range`` instance.
|
||||
* @param range ``Range`` instance.
|
||||
*/
|
||||
public Range.with_range (Range range) {
|
||||
this.min = range.min;
|
||||
this.max = range.max;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new ``Range`` with absolute coordinates.
|
||||
* @param min minimum/low limit.
|
||||
* @param max maximum/high limit.
|
||||
*/
|
||||
public Range.with_abs (Float128 min, Float128 max) {
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new ``Range`` with relative coordinates.
|
||||
* @param min minimum/low limit.
|
||||
* @param range length of the ``Range``.
|
||||
*/
|
||||
public Range.with_rel (Float128 min, Float128 range) {
|
||||
this.min = min;
|
||||
this.range = range;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a copy of the ``Range``.
|
||||
*/
|
||||
public virtual Range copy () {
|
||||
return new Range.with_range(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Zooms out ``Range``.
|
||||
*/
|
||||
public virtual void zoom_out () {
|
||||
zmin = min;
|
||||
zmax = max;
|
||||
}
|
||||
}
|
||||
}
|
192
src/Series.vala
192
src/Series.vala
|
@ -1,65 +1,112 @@
|
|||
using Cairo;
|
||||
|
||||
namespace Gtk.CairoChart {
|
||||
namespace CairoChart {
|
||||
|
||||
/**
|
||||
* ``Chart`` series.
|
||||
*/
|
||||
public class Series {
|
||||
|
||||
public Point[] points = {};
|
||||
public enum Sort {
|
||||
BY_X = 0,
|
||||
BY_Y = 1,
|
||||
NO_SORT
|
||||
}
|
||||
public Sort sort = Sort.BY_X;
|
||||
protected unowned Chart chart { get; protected set; default = null; }
|
||||
|
||||
public Axis axis_x = new Axis();
|
||||
public Axis axis_y = new Axis();
|
||||
|
||||
public enum MarkerType {
|
||||
NONE = 0, // default
|
||||
SQUARE,
|
||||
CIRCLE,
|
||||
TRIANGLE,
|
||||
PRICLE_SQUARE,
|
||||
PRICLE_CIRCLE,
|
||||
PRICLE_TRIANGLE
|
||||
}
|
||||
|
||||
public Place place = new Place();
|
||||
public Text title = new Text ();
|
||||
public MarkerType marker_type = MarkerType.SQUARE;
|
||||
|
||||
public Grid grid = new Grid ();
|
||||
/**
|
||||
* Title of the ``Chart``.
|
||||
*/
|
||||
public Text title;
|
||||
|
||||
/**
|
||||
* ``Series`` line style.
|
||||
*/
|
||||
public LineStyle line_style = LineStyle ();
|
||||
|
||||
protected Color _color = Color (0.0, 0.0, 0.0, 1.0);
|
||||
public Color color {
|
||||
get { return _color; }
|
||||
set {
|
||||
_color = value;
|
||||
line_style.color = _color;
|
||||
axis_x.color = _color;
|
||||
axis_y.color = _color;
|
||||
grid.color = _color;
|
||||
grid.color.alpha = 0.5;
|
||||
grid.line_style.color = _color;
|
||||
grid.line_style.color.alpha = 0.5;
|
||||
}
|
||||
default = Color (0.0, 0.0, 0.0, 1.0);
|
||||
/**
|
||||
* 128-bit (X;Y) points.
|
||||
*/
|
||||
public Point128[] points = {};
|
||||
|
||||
/**
|
||||
* ``Marker`` style.
|
||||
*/
|
||||
public Marker marker;
|
||||
|
||||
/**
|
||||
* Sort style.
|
||||
*/
|
||||
public enum Sort {
|
||||
/**
|
||||
* Sort by X.
|
||||
*/
|
||||
BY_X = 0,
|
||||
|
||||
/**
|
||||
* Sort by Y.
|
||||
*/
|
||||
BY_Y = 1,
|
||||
|
||||
/**
|
||||
* Do not sort points on draw().
|
||||
*/
|
||||
UNSORTED
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort style.
|
||||
*/
|
||||
public Sort sort = Sort.BY_X;
|
||||
|
||||
/**
|
||||
* X-axis.
|
||||
*/
|
||||
public Axis axis_x;
|
||||
|
||||
/**
|
||||
* Y-axis.
|
||||
*/
|
||||
public Axis axis_y;
|
||||
|
||||
/**
|
||||
* ``Series`` color (set only).
|
||||
*/
|
||||
public Color color {
|
||||
protected get { return Color(); }
|
||||
set {
|
||||
line_style.color = value;
|
||||
axis_x.color = value;
|
||||
axis_y.color = value;
|
||||
axis_x.grid_style.color = value;
|
||||
axis_x.grid_style.color.alpha = 0.5;
|
||||
axis_y.grid_style.color = value;
|
||||
axis_y.grid_style.color.alpha = 0.5;
|
||||
}
|
||||
default = Color (0, 0, 0, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the ``Series`` in zoomed area or not.
|
||||
*/
|
||||
public bool zoom_show = true;
|
||||
|
||||
public Series copy () {
|
||||
var series = new Series ();
|
||||
series._color = this._color;
|
||||
/**
|
||||
* Constructs a new ``Series``.
|
||||
* @param chart ``Chart`` instance.
|
||||
*/
|
||||
public Series (Chart chart) {
|
||||
this.chart = chart;
|
||||
title = new Text(chart);
|
||||
axis_x = new Axis(chart, this, true);
|
||||
axis_y = new Axis(chart, this, false);
|
||||
this.marker = new Marker(chart);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a copy of the ``Series``.
|
||||
*/
|
||||
public virtual Series copy () {
|
||||
var series = new Series (chart);
|
||||
series.axis_x = this.axis_x.copy ();
|
||||
series.axis_y = this.axis_y.copy ();
|
||||
series.grid = this.grid.copy ();
|
||||
series.line_style = this.line_style;
|
||||
series.marker_type = this.marker_type;
|
||||
series.place = this.place.copy();
|
||||
series.marker = this.marker;
|
||||
series.points = this.points;
|
||||
series.sort = this.sort;
|
||||
series.title = this.title.copy();
|
||||
|
@ -67,7 +114,58 @@ namespace Gtk.CairoChart {
|
|||
return series;
|
||||
}
|
||||
|
||||
public Series () {
|
||||
/**
|
||||
* Gets screen point by real ``Series`` (X;Y) value.
|
||||
* @param p real ``Series`` (X;Y) value.
|
||||
*/
|
||||
public virtual Point scr_pnt (Point128 p) {
|
||||
return Point(axis_x.scr_pos(p.x), axis_y.scr_pos(p.y));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets real ``Series`` (X;Y) value by plot area screen point.
|
||||
* @param p (X;Y) screen point.
|
||||
*/
|
||||
public virtual Point128 axis_pnt (Point p) {
|
||||
return Point128 (axis_x.axis_val(p.x), axis_y.axis_val(p.y));
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws the ``Series``.
|
||||
*/
|
||||
public virtual void draw () {
|
||||
var points = Math.sort_points(this, sort);
|
||||
line_style.apply(chart);
|
||||
// draw series line
|
||||
for (int i = 1; i < points.length; ++i) {
|
||||
Point c, d;
|
||||
if (Math.cut_line (
|
||||
Point(chart.plarea.x0, chart.plarea.y0),
|
||||
Point(chart.plarea.x1, chart.plarea.y1),
|
||||
scr_pnt(points[i-1]),
|
||||
scr_pnt(points[i]),
|
||||
out c, out d)
|
||||
) {
|
||||
chart.ctx.move_to (c.x, c.y);
|
||||
chart.ctx.line_to (d.x, d.y);
|
||||
}
|
||||
}
|
||||
chart.ctx.stroke();
|
||||
for (int i = 0; i < points.length; ++i) {
|
||||
var p = scr_pnt(points[i]);
|
||||
if (Math.point_in_rect (p, chart.plarea.x0, chart.plarea.x1,
|
||||
chart.plarea.y0, chart.plarea.y1))
|
||||
marker.draw_at_pos(p);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Zooms out the ``Series``.
|
||||
*/
|
||||
public virtual void zoom_out () {
|
||||
zoom_show = true;
|
||||
axis_x.zoom_out();
|
||||
axis_y.zoom_out();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
169
src/Text.vala
169
src/Text.vala
|
@ -1,71 +1,142 @@
|
|||
namespace Gtk.CairoChart {
|
||||
[Compact]
|
||||
namespace CairoChart {
|
||||
|
||||
/**
|
||||
* ``CairoChart`` Text.
|
||||
*/
|
||||
public class Text {
|
||||
public string text = "";
|
||||
public FontStyle style = FontStyle ();
|
||||
|
||||
protected unowned Chart chart;
|
||||
protected string _text;
|
||||
protected Font _font;
|
||||
protected Cairo.TextExtents? _ext;
|
||||
|
||||
/**
|
||||
* ``Text`` string.
|
||||
*/
|
||||
public virtual string text {
|
||||
get {
|
||||
return _text;
|
||||
}
|
||||
set {
|
||||
_text = value;
|
||||
_ext = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ``Text`` font style.
|
||||
*/
|
||||
public virtual Font font {
|
||||
get {
|
||||
return _font;
|
||||
}
|
||||
set {
|
||||
_font = value;
|
||||
_font.notify.connect((s, p) => {
|
||||
_ext = null;
|
||||
});
|
||||
_ext = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ``Text`` color.
|
||||
*/
|
||||
public Color color = Color();
|
||||
|
||||
public Cairo.TextExtents get_extents (Cairo.Context context) {
|
||||
context.select_font_face (style.family, style.slant, style.weight);
|
||||
context.set_font_size (style.size);
|
||||
Cairo.TextExtents extents;
|
||||
context.text_extents (text, out extents);
|
||||
return extents;
|
||||
}
|
||||
|
||||
public double get_width (Cairo.Context context) {
|
||||
var extents = get_extents (context);
|
||||
switch (style.orientation) {
|
||||
case FontOrient.HORIZONTAL: return extents.width;
|
||||
case FontOrient.VERTICAL: return extents.height;
|
||||
default: return 0.0;
|
||||
/**
|
||||
* Cairo ``Text`` extents.
|
||||
*/
|
||||
protected virtual Cairo.TextExtents ext {
|
||||
get {
|
||||
if (_ext == null) {
|
||||
chart.ctx.select_font_face (font.family, font.slant, font.weight);
|
||||
chart.ctx.set_font_size (font.size);
|
||||
chart.ctx.text_extents (text, out _ext);
|
||||
}
|
||||
return _ext;
|
||||
}
|
||||
protected set {
|
||||
}
|
||||
}
|
||||
|
||||
public double get_height (Cairo.Context context) {
|
||||
var extents = get_extents (context);
|
||||
switch (style.orientation) {
|
||||
case FontOrient.HORIZONTAL: return extents.height;
|
||||
case FontOrient.VERTICAL: return extents.width;
|
||||
default: return 0.0;
|
||||
/**
|
||||
* ``Text`` width.
|
||||
*/
|
||||
public virtual double width {
|
||||
get {
|
||||
switch (font.orient) {
|
||||
case Gtk.Orientation.HORIZONTAL: return ext.width + ext.x_bearing;
|
||||
case Gtk.Orientation.VERTICAL: return ext.height;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
protected set {
|
||||
}
|
||||
}
|
||||
|
||||
public struct Size {
|
||||
double width;
|
||||
double height;
|
||||
}
|
||||
|
||||
public Size size (Cairo.Context context) {
|
||||
var sz = Size();
|
||||
var extents = get_extents (context);
|
||||
switch (style.orientation) {
|
||||
case FontOrient.HORIZONTAL:
|
||||
sz.width = extents.width + extents.x_bearing;
|
||||
sz.height = extents.height;
|
||||
break;
|
||||
case FontOrient.VERTICAL:
|
||||
sz.width = extents.height; // + extents.x_bearing ?
|
||||
sz.height = extents.width; // +- extents.y_bearing ?
|
||||
break;
|
||||
/**
|
||||
* ``Text`` height.
|
||||
*/
|
||||
public virtual double height {
|
||||
get {
|
||||
switch (font.orient) {
|
||||
case Gtk.Orientation.HORIZONTAL: return ext.height; // + ext.x_bearing ?
|
||||
case Gtk.Orientation.VERTICAL: return ext.width; // +- ext.y_bearing ?
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
protected set {
|
||||
}
|
||||
return sz;
|
||||
}
|
||||
|
||||
public Text (string text = "",
|
||||
FontStyle style = FontStyle(),
|
||||
Color color = Color()) {
|
||||
/**
|
||||
* Constructs a new ``Text``.
|
||||
* @param chart ``Chart`` instance.
|
||||
* @param text ``Text`` string.
|
||||
* @param font ``Text`` font style.
|
||||
* @param color ``Text`` color.
|
||||
*/
|
||||
public Text (Chart chart,
|
||||
string text = "",
|
||||
Font font = new Font(),
|
||||
Color color = Color()
|
||||
) {
|
||||
this.chart = chart;
|
||||
this.text = text;
|
||||
this.style = style;
|
||||
this.font = font;
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
public Text copy () {
|
||||
var text = new Text ();
|
||||
/**
|
||||
* Gets a copy of the ``Text``.
|
||||
*/
|
||||
public virtual Text copy () {
|
||||
var text = new Text (chart);
|
||||
text.chart = this.chart;
|
||||
text.text = this.text;
|
||||
text.style = this.style;
|
||||
text.font = this.font.copy();
|
||||
text._ext = this._ext;
|
||||
text.color = this.color;
|
||||
return text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show ``Text``.
|
||||
*/
|
||||
public virtual void show () {
|
||||
if (text == "") return;
|
||||
chart.ctx.select_font_face(font.family,
|
||||
font.slant,
|
||||
font.weight);
|
||||
chart.ctx.set_font_size(font.size);
|
||||
if (font.orient == Gtk.Orientation.VERTICAL) {
|
||||
chart.ctx.rotate(- GLib.Math.PI / 2);
|
||||
chart.ctx.show_text(text);
|
||||
chart.ctx.rotate(GLib.Math.PI / 2);
|
||||
} else {
|
||||
chart.ctx.show_text(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,14 @@
|
|||
namespace Gtk.CairoChart {
|
||||
namespace CairoChart {
|
||||
|
||||
/**
|
||||
* 128-bit float type.
|
||||
*/
|
||||
[CCode (cname = "cairo_chart_float128", has_type_id = false, cheader_filename = "cairo-chart-float128type.h")]
|
||||
public struct Float128 : double {}
|
||||
|
||||
/**
|
||||
* Long Double float type.
|
||||
*/
|
||||
[CCode (cname = "cairo_chart_long_double", has_type_id = false, cheader_filename = "cairo-chart-float128type.h")]
|
||||
public struct LongDouble : double {}
|
||||
}
|
||||
|
|
|
@ -1,211 +1,247 @@
|
|||
using Gtk, CairoChart;
|
||||
|
||||
void plot_chart1 (Chart chart) {
|
||||
var s1 = new Series ();
|
||||
var s2 = new Series ();
|
||||
var s3 = new Series ();
|
||||
chart.title.text = "Chart №1";
|
||||
var s1 = new Series (chart);
|
||||
var s2 = new Series (chart);
|
||||
var s3 = new Series (chart);
|
||||
|
||||
s1.title = new Text("Series 1"); s1.color = Color (1, 0, 0);
|
||||
s1.points = {Point(0, 0), Point(2, 1), Point(1, 3)};
|
||||
s1.title = new Text(chart, "Series 1"); s1.color = Color (1, 0, 0);
|
||||
s1.points = {Point128(0, 0), Point128(2, 1), Point128(1, 3)};
|
||||
s1.axis_x.position = Axis.Position.HIGH;
|
||||
s1.axis_x.format = "%.3Lf";
|
||||
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)};
|
||||
s2.title = new Text(chart, "Series 2"); s2.color = Color (0, 1, 0);
|
||||
s2.points = {Point128(5, -3), Point128(25, -18), Point128(-11, 173)};
|
||||
s3.title = new Text(chart, "Series 3"); s3.color = Color (0, 0, 1);
|
||||
s3.points = {Point128(9, 17), Point128(2, 10), Point128(122, 31)};
|
||||
s3.axis_y.position = Axis.Position.HIGH;
|
||||
|
||||
s1.axis_x.min = 0; s1.axis_x.max = 2;
|
||||
s1.axis_y.min = 0; s1.axis_y.max = 3;
|
||||
s1.place.x_low = 0.25; s1.place.x_high = 0.75;
|
||||
s1.place.y_low = 0.3; s1.place.y_high = 0.9;
|
||||
s1.axis_x.range.min = 0; s1.axis_x.range.max = 2;
|
||||
s1.axis_y.range.min = 0; s1.axis_y.range.max = 3;
|
||||
s1.axis_x.place.min = 0.25; s1.axis_x.place.max = 0.75;
|
||||
s1.axis_y.place.min = 0.3; s1.axis_y.place.max = 0.9;
|
||||
|
||||
s2.axis_x.min = -15; s2.axis_x.max = 30;
|
||||
s2.axis_y.min = -20; s2.axis_y.max = 200;
|
||||
s2.place.x_low = 0.5; s2.place.x_high = 1;
|
||||
s2.place.y_low = 0.0; s2.place.y_high = 0.5;
|
||||
s2.axis_x.range.min = -15; s2.axis_x.range.max = 30;
|
||||
s2.axis_y.range.min = -20; s2.axis_y.range.max = 200;
|
||||
s2.axis_x.place.min = 0.5; s2.axis_x.place.max = 1;
|
||||
s2.axis_y.place.min = 0.0; s2.axis_y.place.max = 0.5;
|
||||
|
||||
s3.axis_x.min = 0; s3.axis_x.max = 130;
|
||||
s3.axis_y.min = 15; s3.axis_y.max = 35;
|
||||
s3.place.x_low = 0; s3.place.x_high = 0.5;
|
||||
s3.place.y_low = 0.5; s3.place.y_high = 1.0;
|
||||
s3.axis_x.range.min = 0; s3.axis_x.range.max = 130;
|
||||
s3.axis_y.range.min = 15; s3.axis_y.range.max = 35;
|
||||
s3.axis_x.place.min = 0; s3.axis_x.place.max = 0.5;
|
||||
s3.axis_y.place.min = 0.5; s3.axis_y.place.max = 1.0;
|
||||
|
||||
s2.marker_type = Series.MarkerType.CIRCLE;
|
||||
s3.marker_type = Series.MarkerType.PRICLE_TRIANGLE;
|
||||
s1.marker.shape = Marker.Shape.SQUARE;
|
||||
s2.marker.shape = Marker.Shape.CIRCLE;
|
||||
s3.marker.shape = Marker.Shape.PRICLE_TRIANGLE;
|
||||
|
||||
s1.axis_x.title = new Text("Series 1: Axis X.");
|
||||
s1.axis_y.title = new Text("Series 1: Axis Y.");
|
||||
s2.axis_x.title = new Text("Series 2: Axis X.");
|
||||
s2.axis_y.title = new Text("Series 2: Axis Y.");
|
||||
s3.axis_x.title = new Text("Series 3: Axis X.");
|
||||
s3.axis_y.title = new Text("Series 3: Axis Y.");
|
||||
s1.axis_x.title = new Text(chart, "Series 1: Axis X.");
|
||||
s1.axis_y.title = new Text(chart, "Series 1: Axis Y.");
|
||||
s2.axis_x.title = new Text(chart, "Series 2: Axis X.");
|
||||
s2.axis_y.title = new Text(chart, "Series 2: Axis Y.");
|
||||
s3.axis_x.title = new Text(chart, "Series 3: Axis X.");
|
||||
s3.axis_y.title = new Text(chart, "Series 3: Axis Y.");
|
||||
|
||||
chart.series = { s1, s2, s3 };
|
||||
|
||||
chart.title.font.size = 24;
|
||||
//chart.legend.show = false;
|
||||
s1.title.font.size = 16;
|
||||
s2.title.font.size = 16;
|
||||
s2.title.font.slant = Cairo.FontSlant.ITALIC;
|
||||
s2.title.font.weight = Cairo.FontWeight.BOLD;
|
||||
s3.title.font.size = 18;
|
||||
s1.axis_x.title.font.size = 14;
|
||||
s2.axis_x.title.font.size = 14;
|
||||
s2.axis_x.title.font.slant = Cairo.FontSlant.ITALIC;
|
||||
s2.axis_x.title.font.weight = Cairo.FontWeight.BOLD;
|
||||
s3.axis_x.title.font.size = 18;
|
||||
s1.axis_y.title.font.size = 14;
|
||||
s2.axis_y.title.font.size = 14;
|
||||
s2.axis_y.title.font.slant = Cairo.FontSlant.ITALIC;
|
||||
s2.axis_y.title.font.weight = Cairo.FontWeight.BOLD;
|
||||
s3.axis_y.title.font.size = 18;
|
||||
s1.axis_x.font.size = 12;
|
||||
s2.axis_x.font.size = 12;
|
||||
s3.axis_x.font.size = 12;
|
||||
s1.axis_y.font.size = 12;
|
||||
s2.axis_y.font.size = 12;
|
||||
s3.axis_y.font.size = 14;
|
||||
s1.marker.size = 6;
|
||||
s2.marker.size = 8;
|
||||
s3.marker.size = 7;
|
||||
s3.line_style.width = 2;
|
||||
}
|
||||
|
||||
void plot_chart2 (Chart chart) {
|
||||
var s1 = new Series ();
|
||||
var s2 = new Series ();
|
||||
var s3 = new Series ();
|
||||
var s1 = new Series (chart);
|
||||
var s2 = new Series (chart);
|
||||
var s3 = new Series (chart);
|
||||
|
||||
s1.title = new Text("Series 1"); s1.color = Color (1, 0, 0);
|
||||
s1.points = {Point(-12, 0), Point(2, 1), Point(20, 3)};
|
||||
s1.title = new Text(chart, "Series 1"); s1.color = Color (1, 0, 0);
|
||||
s1.points = {Point128(-12, 0), Point128(2, 1), Point128(20, 3)};
|
||||
s2.axis_y.position = Axis.Position.HIGH;
|
||||
s1.axis_x.format = "%.3Lf";
|
||||
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)};
|
||||
s2.title = new Text(chart, "Series 2"); s2.color = Color (0, 1, 0);
|
||||
s2.points = {Point128(5, -3), Point128(25, -18), Point128(-11, 173)};
|
||||
s3.title = new Text(chart, "Series 3"); s3.color = Color (0, 0, 1);
|
||||
s3.points = {Point128(9, 17), Point128(2, 10), Point128(-15, 31)};
|
||||
s3.axis_y.position = Axis.Position.HIGH;
|
||||
|
||||
s1.axis_x.min = -15; s1.axis_x.max = 30;
|
||||
s1.axis_y.min = 0; s1.axis_y.max = 3;
|
||||
s1.place.x_low = 0.0; s1.place.x_high = 1.0;
|
||||
s1.place.y_low = 0.3; s1.place.y_high = 0.9;
|
||||
s1.axis_x.range.min = -15; s1.axis_x.range.max = 30;
|
||||
s1.axis_y.range.min = 0; s1.axis_y.range.max = 3;
|
||||
s1.axis_x.place.min = 0.0; s1.axis_x.place.max = 1.0;
|
||||
s1.axis_y.place.min = 0.3; s1.axis_y.place.max = 0.9;
|
||||
|
||||
s2.axis_x.min = -15; s2.axis_x.max = 30;
|
||||
s2.axis_y.min = -20; s2.axis_y.max = 200;
|
||||
s2.place.x_low = 0.0; s2.place.x_high = 1.0;
|
||||
s2.place.y_low = 0.0; s2.place.y_high = 0.5;
|
||||
s2.axis_x.range.min = -15; s2.axis_x.range.max = 30;
|
||||
s2.axis_y.range.min = -20; s2.axis_y.range.max = 200;
|
||||
s2.axis_x.place.min = 0.0; s2.axis_x.place.max = 1.0;
|
||||
s2.axis_y.place.min = 0.0; s2.axis_y.place.max = 0.5;
|
||||
|
||||
s3.axis_x.min = -15; s3.axis_x.max = 30;
|
||||
s3.axis_y.min = 15; s3.axis_y.max = 35;
|
||||
s3.place.x_low = 0.0; s3.place.x_high = 1.0;
|
||||
s3.place.y_low = 0.5; s3.place.y_high = 1.0;
|
||||
s3.axis_x.range.min = -15; s3.axis_x.range.max = 30;
|
||||
s3.axis_y.range.min = 15; s3.axis_y.range.max = 35;
|
||||
s3.axis_x.place.min = 0.0; s3.axis_x.place.max = 1.0;
|
||||
s3.axis_y.place.min = 0.5; s3.axis_y.place.max = 1.0;
|
||||
|
||||
s1.marker_type = Series.MarkerType.PRICLE_CIRCLE;
|
||||
s2.marker_type = Series.MarkerType.PRICLE_SQUARE;
|
||||
s1.marker.shape = Marker.Shape.PRICLE_CIRCLE;
|
||||
s2.marker.shape = Marker.Shape.PRICLE_SQUARE;
|
||||
s3.marker.shape = Marker.Shape.SQUARE;
|
||||
|
||||
s1.axis_x.title = new Text("All Series: Axis X.");
|
||||
s1.axis_y.title = new Text("Series 1: Axis Y.");
|
||||
s2.axis_x.title = new Text("All Series: Axis X.");
|
||||
s2.axis_y.title = new Text("Series 2: Axis Y.");
|
||||
s3.axis_x.title = new Text("All Series: Axis X.");
|
||||
s3.axis_y.title = new Text("Series 3: Axis Y.");
|
||||
s1.axis_x.title = new Text(chart, "All Series: Axis X.");
|
||||
s1.axis_y.title = new Text(chart, "Series 1: Axis Y.");
|
||||
s2.axis_x.title = new Text(chart, "All Series: Axis X.");
|
||||
s2.axis_y.title = new Text(chart, "Series 2: Axis Y.");
|
||||
s3.axis_x.title = new Text(chart, "All Series: Axis X.");
|
||||
s3.axis_y.title = new Text(chart, "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;
|
||||
//s1.axis_x.max = s2.axis_x.max = s3.axis_x.max = 5*24*3600;
|
||||
//s1.axis_x.dtype = s2.axis_x.dtype = s3.axis_x.dtype = Axis.DType.DATE_TIME;
|
||||
//s1.axis_x.range.max = s2.axis_x.range.max = s3.axis_x.range.max = 5*24*3600;
|
||||
|
||||
chart.series = { s1, s2, s3 };
|
||||
}
|
||||
|
||||
void plot_chart3 (Chart chart) {
|
||||
var s1 = new Series ();
|
||||
var s2 = new Series ();
|
||||
var s3 = new Series ();
|
||||
var s1 = new Series (chart);
|
||||
var s2 = new Series (chart);
|
||||
var s3 = new Series (chart);
|
||||
|
||||
s1.title = new Text("Series 1"); s1.color = Color (1, 0, 0);
|
||||
s1.points = {Point(0, 70), Point(2, 155), Point(1, -3)};
|
||||
s1.title = new Text(chart, "Series 1"); s1.color = Color (1, 0, 0);
|
||||
s1.points = {Point128(0, 70), Point128(2, 155), Point128(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 = Color (0, 1, 0);
|
||||
s2.points = {Point(5, -3), Point(25, -18), Point(-11, 173)};
|
||||
s2.title = new Text(chart, "Series 2"); s2.color = Color (0, 1, 0);
|
||||
s2.points = {Point128(5, -3), Point128(25, -18), Point128(-11, 173)};
|
||||
s2.axis_y.position = Axis.Position.HIGH;
|
||||
s3.title = new Text("Series 3"); s3.color = Color (0, 0, 1);
|
||||
s3.points = {Point(9, -17), Point(2, 10), Point(122, 31)};
|
||||
s3.title = new Text(chart, "Series 3"); s3.color = Color (0, 0, 1);
|
||||
s3.points = {Point128(9, -17), Point128(2, 10), Point128(122, 31)};
|
||||
s3.axis_y.position = Axis.Position.HIGH;
|
||||
|
||||
s1.axis_x.min = 0; s1.axis_x.max = 2;
|
||||
s1.axis_y.min = -20; s1.axis_y.max = 200;
|
||||
s1.place.x_low = 0.25; s1.place.x_high = 0.75;
|
||||
s1.place.y_low = 0.0; s1.place.y_high = 1.0;
|
||||
s1.axis_x.range.min = 0; s1.axis_x.range.max = 2;
|
||||
s1.axis_y.range.min = -20; s1.axis_y.range.max = 200;
|
||||
s1.axis_x.place.min = 0.25; s1.axis_x.place.max = 0.75;
|
||||
s1.axis_y.place.min = 0.0; s1.axis_y.place.max = 1.0;
|
||||
|
||||
s2.axis_x.min = -15; s2.axis_x.max = 30;
|
||||
s2.axis_y.min = -20; s2.axis_y.max = 200;
|
||||
s2.place.x_low = 0.5; s2.place.x_high = 1;
|
||||
s2.place.y_low = 0.0; s2.place.y_high = 1.0;
|
||||
s2.axis_x.range.min = -15; s2.axis_x.range.max = 30;
|
||||
s2.axis_y.range.min = -20; s2.axis_y.range.max = 200;
|
||||
s2.axis_x.place.min = 0.5; s2.axis_x.place.max = 1;
|
||||
s2.axis_y.place.min = 0.0; s2.axis_y.place.max = 1.0;
|
||||
|
||||
s3.axis_x.min = 0; s3.axis_x.max = 130;
|
||||
s3.axis_y.min = -20; s3.axis_y.max = 200;
|
||||
s3.place.x_low = 0; s3.place.x_high = 0.5;
|
||||
s3.place.y_low = 0.0; s3.place.y_high = 1.0;
|
||||
s3.axis_x.range.min = 0; s3.axis_x.range.max = 130;
|
||||
s3.axis_y.range.min = -20; s3.axis_y.range.max = 200;
|
||||
s3.axis_x.place.min = 0; s3.axis_x.place.max = 0.5;
|
||||
s3.axis_y.place.min = 0.0; s3.axis_y.place.max = 1.0;
|
||||
|
||||
s2.marker_type = Series.MarkerType.PRICLE_CIRCLE;
|
||||
s3.marker_type = Series.MarkerType.TRIANGLE;
|
||||
s1.marker.shape = Marker.Shape.SQUARE;
|
||||
s2.marker.shape = Marker.Shape.PRICLE_CIRCLE;
|
||||
s3.marker.shape = Marker.Shape.TRIANGLE;
|
||||
|
||||
s1.axis_x.title = new Text("Series 1: Axis X.");
|
||||
s1.axis_y.title = new Text("Series 1: Axis Y.");
|
||||
s2.axis_x.title = new Text("Series 2: Axis X.");
|
||||
s2.axis_y.title = new Text("Series 2: Axis Y.");
|
||||
s3.axis_x.title = new Text("Series 3: Axis X.");
|
||||
s3.axis_y.title = new Text("Series 3: Axis Y.");
|
||||
s1.axis_x.title = new Text(chart, "Series 1: Axis X.");
|
||||
s1.axis_y.title = new Text(chart, "All Series: Axis Y.");
|
||||
s2.axis_x.title = new Text(chart, "Series 2: Axis X.");
|
||||
s2.axis_y.title = new Text(chart, "All Series: Axis Y.");
|
||||
s3.axis_x.title = new Text(chart, "Series 3: Axis X.");
|
||||
s3.axis_y.title = new Text(chart, "All Series: Axis Y.");
|
||||
|
||||
//s1.axis_y.position = s2.axis_y.position = s3.axis_y.position = Axis.Position.LOW;
|
||||
chart.cursors.style.orientation = Cursors.Orientation.HORIZONTAL;
|
||||
|
||||
chart.series = { s1, s2, s3 };
|
||||
}
|
||||
|
||||
void plot_chart4 (Chart chart) {
|
||||
var s1 = new Series ();
|
||||
var s2 = new Series ();
|
||||
var s3 = new Series ();
|
||||
var s4 = new Series ();
|
||||
var s1 = new Series (chart);
|
||||
var s2 = new Series (chart);
|
||||
var s3 = new Series (chart);
|
||||
var s4 = new Series (chart);
|
||||
|
||||
s1.axis_x.type = Axis.Type.DATE_TIME;
|
||||
s3.axis_x.type = Axis.Type.DATE_TIME;
|
||||
s4.axis_x.type = Axis.Type.DATE_TIME;
|
||||
s1.axis_x.dtype = Axis.DType.DATE_TIME;
|
||||
s3.axis_x.dtype = Axis.DType.DATE_TIME;
|
||||
s4.axis_x.dtype = Axis.DType.DATE_TIME;
|
||||
s4.axis_x.dsec_signs = 5;
|
||||
|
||||
var now = new DateTime.now_local().to_unix();
|
||||
var high = (uint64) (253000000000L);
|
||||
|
||||
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.title = new Text(chart, "Series 1"); s1.color = Color (1, 0, 0);
|
||||
s1.points = {Point128(now, 70), Point128(now - 100000, 155), Point128(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 = Color (0, 1, 0);
|
||||
s2.points = {Point(5, -3), Point(25, -18), Point(-11, 173)};
|
||||
s2.title = new Text(chart, "Series 2"); s2.color = Color (0, 1, 0);
|
||||
s2.points = {Point128(5, -3), Point128(25, -18), Point128(-11, 173)};
|
||||
s2.axis_y.position = Axis.Position.HIGH;
|
||||
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.title = new Text(chart, "Series 3"); s3.color = Color (0, 0, 1);
|
||||
s3.points = {Point128(high - 2 + 0.73, -17), Point128(high - 1 + 0.234, 10), Point128(high + 1 + 0.411, 31)};
|
||||
s3.axis_y.position = Axis.Position.HIGH;
|
||||
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.title = new Text(chart, "Series 4"); s4.color = Color (0.5, 0.3, 0.9);
|
||||
s4.points = {Point128(high + 0.005, -19.05), Point128(high + 0.0051, 28), Point128(high + 0.0052, 55), Point128(high + 0.0053, 44)};
|
||||
s4.axis_y.position = Axis.Position.HIGH;
|
||||
|
||||
s1.axis_x.min = now - 100000; s1.axis_x.max = now + 100000;
|
||||
s1.axis_y.min = -20; s1.axis_y.max = 200;
|
||||
s1.place.x_low = 0.25; s1.place.x_high = 0.75;
|
||||
s1.place.y_low = 0.0; s1.place.y_high = 1.0;
|
||||
s1.axis_x.range.min = now - 100000; s1.axis_x.range.max = now + 100000;
|
||||
s1.axis_y.range.min = -20; s1.axis_y.range.max = 200;
|
||||
s1.axis_x.place.min = 0.25; s1.axis_x.place.max = 0.75;
|
||||
s1.axis_y.place.min = 0.0; s1.axis_y.place.max = 1.0;
|
||||
|
||||
s2.axis_x.min = -15; s2.axis_x.max = 30;
|
||||
s2.axis_y.min = -20; s2.axis_y.max = 200;
|
||||
s2.place.x_low = 0.2; s2.place.x_high = 1;
|
||||
s2.place.y_low = 0.0; s2.place.y_high = 1.0;
|
||||
s2.axis_x.range.min = -15; s2.axis_x.range.max = 30;
|
||||
s2.axis_y.range.min = -20; s2.axis_y.range.max = 200;
|
||||
s2.axis_x.place.min = 0.2; s2.axis_x.place.max = 1;
|
||||
s2.axis_y.place.min = 0.0; s2.axis_y.place.max = 1.0;
|
||||
|
||||
s3.axis_x.min = high - 2; s3.axis_x.max = high + 1;
|
||||
s3.axis_y.min = -20; s3.axis_y.max = 200;
|
||||
s3.place.x_low = 0; s3.place.x_high = 0.8;
|
||||
s3.place.y_low = 0.0; s3.place.y_high = 1.0;
|
||||
s3.axis_x.range.min = high - 2; s3.axis_x.range.max = high + 1;
|
||||
s3.axis_y.range.min = -20; s3.axis_y.range.max = 200;
|
||||
s3.axis_x.place.min = 0; s3.axis_x.place.max = 0.8;
|
||||
s3.axis_y.place.min = 0.0; s3.axis_y.place.max = 1.0;
|
||||
|
||||
s4.axis_x.min = high + 0.0049; s4.axis_x.max = high + 0.0054;
|
||||
s4.axis_y.min = -20; s4.axis_y.max = 200;
|
||||
s4.place.x_low = 0.2; s4.place.x_high = 1.0;
|
||||
s4.place.y_low = 0.0; s4.place.y_high = 1.0;
|
||||
s4.axis_x.range.min = high + 0.0049; s4.axis_x.range.max = high + 0.0054;
|
||||
s4.axis_y.range.min = -20; s4.axis_y.range.max = 200;
|
||||
s4.axis_x.place.min = 0.2; s4.axis_x.place.max = 1.0;
|
||||
s4.axis_y.place.min = 0.0; s4.axis_y.place.max = 1.0;
|
||||
|
||||
s2.marker_type = Series.MarkerType.PRICLE_CIRCLE;
|
||||
s3.marker_type = Series.MarkerType.TRIANGLE;
|
||||
s4.marker_type = Series.MarkerType.PRICLE_SQUARE;
|
||||
s1.marker.shape = Marker.Shape.SQUARE;
|
||||
s2.marker.shape = Marker.Shape.PRICLE_CIRCLE;
|
||||
s3.marker.shape = Marker.Shape.TRIANGLE;
|
||||
s4.marker.shape = Marker.Shape.PRICLE_SQUARE;
|
||||
|
||||
s1.axis_x.title = new Text("Series 1: Axis X.");
|
||||
s1.axis_y.title = new Text("Series 1: Axis Y.");
|
||||
s2.axis_x.title = new Text("Series 2: Axis X.");
|
||||
s2.axis_y.title = new Text("Series 2: Axis Y.");
|
||||
s3.axis_x.title = new Text("Series 3: Axis X.");
|
||||
s3.axis_y.title = new Text("Series 3: Axis Y.");
|
||||
s4.axis_x.title = new Text("Series 4: Axis X.");
|
||||
s4.axis_y.title = new Text("Series 4: Axis Y.");
|
||||
s1.axis_x.title = new Text(chart, "Series 1: Axis X.");
|
||||
s1.axis_y.title = new Text(chart, "All Series: Axis Y.");
|
||||
s2.axis_x.title = new Text(chart, "Series 2: Axis X.");
|
||||
s2.axis_y.title = new Text(chart, "All Series: Axis Y.");
|
||||
s3.axis_x.title = new Text(chart, "Series 3: Axis X.");
|
||||
s3.axis_y.title = new Text(chart, "All Series: Axis Y.");
|
||||
s4.axis_x.title = new Text(chart, "Series 4: Axis X.");
|
||||
s4.axis_y.title = new Text(chart, "All Series: Axis Y.");
|
||||
|
||||
chart.cursors.style.orientation = Cursors.Orientation.HORIZONTAL;
|
||||
|
||||
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;
|
||||
if (x < chart.plarea.x0) return false;
|
||||
if (x > chart.plarea.x1) return false;
|
||||
if (y < chart.plarea.y0) return false;
|
||||
if (y > chart.plarea.y1) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -230,10 +266,10 @@ int main (string[] args) {
|
|||
var chart2 = new Chart();
|
||||
var chart3 = new Chart();
|
||||
var chart4 = new Chart();
|
||||
var label = new Label ("Chart Test!");
|
||||
var label = new Gtk.Label ("Chart Test!");
|
||||
var button1 = new Button.with_label("Separate axes");
|
||||
var button2 = new Button.with_label("Common X axes");
|
||||
var button3 = new Button.with_label("Common Y axes");
|
||||
var button2 = new Button.with_label("Joint X axes");
|
||||
var button3 = new Button.with_label("Joint Y axes");
|
||||
var button4 = new Button.with_label("Dates/Times");
|
||||
var button5 = new Button.with_label("rm Axis Titles");
|
||||
var button6 = new Button.with_label("Dates only");
|
||||
|
@ -245,7 +281,7 @@ int main (string[] args) {
|
|||
plot_chart3 (chart3);
|
||||
plot_chart4 (chart4);
|
||||
|
||||
chart1.selection_style = LineStyle(Color(0.3, 0.3, 0.3, 0.7), 1);
|
||||
chart1.selection_style = LineStyle(Color(0.3, 0.3, 0.3, 0.7));
|
||||
|
||||
var da = new DrawingArea();
|
||||
da.set_events ( Gdk.EventMask.BUTTON_PRESS_MASK
|
||||
|
@ -270,9 +306,9 @@ int main (string[] args) {
|
|||
case Legend.Position.LEFT: radio_button3.set_active(true); break;
|
||||
case Legend.Position.BOTTOM: radio_button4.set_active(true); 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;
|
||||
switch (chart.cursors.style.orientation) {
|
||||
case Cursors.Orientation.VERTICAL: radio_button7.set_active(true); break;
|
||||
case Cursors.Orientation.HORIZONTAL: radio_button8.set_active(true); break;
|
||||
}
|
||||
});
|
||||
button2.clicked.connect (() => {
|
||||
|
@ -283,9 +319,9 @@ int main (string[] args) {
|
|||
case Legend.Position.LEFT: radio_button3.set_active(true); break;
|
||||
case Legend.Position.BOTTOM: radio_button4.set_active(true); 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;
|
||||
switch (chart.cursors.style.orientation) {
|
||||
case Cursors.Orientation.VERTICAL: radio_button7.set_active(true); break;
|
||||
case Cursors.Orientation.HORIZONTAL: radio_button8.set_active(true); break;
|
||||
}
|
||||
});
|
||||
button3.clicked.connect (() => {
|
||||
|
@ -296,9 +332,9 @@ int main (string[] args) {
|
|||
case Legend.Position.LEFT: radio_button3.set_active(true); break;
|
||||
case Legend.Position.BOTTOM: radio_button4.set_active(true); 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;
|
||||
switch (chart.cursors.style.orientation) {
|
||||
case Cursors.Orientation.VERTICAL: radio_button7.set_active(true); break;
|
||||
case Cursors.Orientation.HORIZONTAL: radio_button8.set_active(true); break;
|
||||
}
|
||||
});
|
||||
button4.clicked.connect (() => {
|
||||
|
@ -309,9 +345,9 @@ int main (string[] args) {
|
|||
case Legend.Position.LEFT: radio_button4.set_active(true); break;
|
||||
case Legend.Position.BOTTOM: radio_button4.set_active(true); 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;
|
||||
switch (chart.cursors.style.orientation) {
|
||||
case Cursors.Orientation.VERTICAL: radio_button7.set_active(true); break;
|
||||
case Cursors.Orientation.HORIZONTAL: radio_button8.set_active(true); break;
|
||||
}
|
||||
});
|
||||
button5.clicked.connect (() => {
|
||||
|
@ -393,13 +429,13 @@ int main (string[] args) {
|
|||
|
||||
radio_button7.toggled.connect ((button) => {
|
||||
if (button.get_active()) {
|
||||
chart.cursors_orientation = Chart.CursorOrientation.VERTICAL;
|
||||
chart.cursors.style.orientation = Cursors.Orientation.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;
|
||||
chart.cursors.style.orientation = Cursors.Orientation.HORIZONTAL;
|
||||
da.queue_draw_area(0, 0, da.get_allocated_width(), da.get_allocated_height());
|
||||
}
|
||||
});
|
||||
|
@ -409,10 +445,10 @@ int main (string[] args) {
|
|||
double sel_x0 = 0, sel_x1 = 0, sel_y0 = 0, sel_y1 = 0;
|
||||
double mov_x0 = 0, mov_y0 = 0;
|
||||
|
||||
da.draw.connect((context) => {
|
||||
chart.context = context;
|
||||
chart.width = da.get_allocated_width();
|
||||
chart.height = da.get_allocated_height();
|
||||
da.draw.connect((ctx) => {
|
||||
chart.ctx = ctx;
|
||||
chart.area.width = da.get_allocated_width();
|
||||
chart.area.height = da.get_allocated_height();
|
||||
chart.clear();
|
||||
|
||||
// user's pre draw operations here...
|
||||
|
@ -422,23 +458,23 @@ int main (string[] args) {
|
|||
|
||||
// user's post draw operations here...
|
||||
if (mouse_state == MouseState.DRAW_SELECTION)
|
||||
chart.draw_selection (sel_x0, sel_y0, sel_x1, sel_y1);
|
||||
chart.draw_selection (new Area.with_abs(sel_x0, sel_y0, sel_x1, sel_y1));
|
||||
|
||||
// show delta
|
||||
var str = chart.get_cursors_delta_str();
|
||||
var str = chart.cursors.get_delta_str();
|
||||
if (str != "") {
|
||||
var text = "Δ = " + str;
|
||||
var text_t = new Text(text);
|
||||
var w = text_t.get_width(context);
|
||||
var h = text_t.get_height(context);
|
||||
var x0 = chart.plot_area_x_max - w - 5;
|
||||
var y0 = chart.plot_area_y_min + h + 5;
|
||||
chart.set_source_rgba(chart.legend.bg_color);
|
||||
context.rectangle (x0, y0 - h, w, h);
|
||||
context.fill();
|
||||
context.move_to (x0, y0);
|
||||
chart.set_source_rgba(chart.common_axis_color);
|
||||
context.show_text(text);
|
||||
var text_t = new Text(chart, text);
|
||||
var w = text_t.width;
|
||||
var h = text_t.height;
|
||||
var x0 = chart.plarea.x1 - w - 5;
|
||||
var y0 = chart.plarea.y0 + h + 5;
|
||||
chart.color = chart.legend.bg_color;
|
||||
ctx.rectangle (x0, y0 - h, w, h);
|
||||
ctx.fill();
|
||||
ctx.move_to (x0, y0);
|
||||
chart.color = chart.joint_color;
|
||||
ctx.show_text(text);
|
||||
}
|
||||
|
||||
return true;//ret;
|
||||
|
@ -450,11 +486,11 @@ int main (string[] args) {
|
|||
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);
|
||||
chart.remove_active_cursor();
|
||||
chart.cursors.set_active (Point(event.x, event.y), true);
|
||||
chart.cursors.remove_active();
|
||||
mouse_state = MouseState.FREE;
|
||||
} else { // add cursor
|
||||
chart.set_active_cursor (event.x, event.y);
|
||||
chart.cursors.set_active (Point(event.x, event.y));
|
||||
mouse_state = MouseState.CURSOR_SELECTION;
|
||||
}
|
||||
da.queue_draw_area(0, 0, da.get_allocated_width(), da.get_allocated_height());
|
||||
|
@ -482,11 +518,11 @@ 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 ();
|
||||
//chart.remove_active ();
|
||||
//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 ();
|
||||
chart.cursors.add_active ();
|
||||
mouse_state = MouseState.FREE;
|
||||
}
|
||||
break;
|
||||
|
@ -495,7 +531,7 @@ int main (string[] args) {
|
|||
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);
|
||||
chart.zoom_in (new Area.with_abs(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());
|
||||
|
@ -521,14 +557,14 @@ int main (string[] args) {
|
|||
|
||||
case MouseState.MOVING_CHART:
|
||||
var delta_x = event.x - mov_x0, delta_y = event.y - mov_y0;
|
||||
chart.move (delta_x, delta_y);
|
||||
chart.move (Point(){x = delta_x, y = 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);
|
||||
chart.cursors.set_active (Point(event.x, event.y));
|
||||
da.queue_draw_area(0, 0, da.get_allocated_width(), da.get_allocated_height());
|
||||
break;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue