Merge branch 'release-0.2.0'

This commit is contained in:
Kolan Sh 2018-02-20 20:11:32 +03:00
commit 654099f310
24 changed files with 3063 additions and 2146 deletions

View File

@ -6,8 +6,8 @@ SET (PROJECT_LOWERCASE_NAME "cairo-chart")
SET (PROJECT_DESCRIPTION "GtkChart for Gtk.DrawingArea (Cairo).") SET (PROJECT_DESCRIPTION "GtkChart for Gtk.DrawingArea (Cairo).")
SET (MAJOR 0) SET (MAJOR 0)
SET (MINOR 1) SET (MINOR 2)
SET (PATCH 1) SET (PATCH 0)
LIST (APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/backbone) LIST (APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/backbone)

@ -1 +1 @@
Subproject commit 5aec8e830a52e88f61c8844ac15dca72b1ef7f06 Subproject commit 9ba1995060ebb1c65ac9855e3671b3992c859059

128
src/Area.vala Normal file
View File

@ -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);
}
}
}

View File

@ -1,46 +1,102 @@
namespace Gtk.CairoChart { namespace 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 * ``Chart`` axis.
*/
public class Axis { public class Axis {
Float128 _min = 0;
Float128 _max = 0; protected unowned Chart chart;
public Float128 min { protected unowned Series ser;
get { return _min; } protected string _format = "%.2Lf";
set { _min = zoom_min = value; } protected string _date_format = "%Y.%m.%d";
default = 0; protected string _time_format = "%H:%M:%S";
} protected int _dsec_signs = 2; // 2 signs = centiseconds
public Float128 max { protected bool is_x;
get { return _max; }
set { _max = zoom_max = value; } /**
default = 1; * ``Axis`` title.
} */
public Float128 zoom_min = 0; public Text title;
public Float128 zoom_max = 1;
public Text title = new Text (""); /**
public enum Type { * ``Axis`` range/limits.
NUMBERS = 0, */
DATE_TIME public Range range = new Range();
}
public enum ScaleType { /**
LINEAR = 0, // default * ``Axis`` relative range/limits.
// LOGARITHMIC, // TODO */
// etc public Range place = new Range();
}
public Type type; /**
public ScaleType scale_type; * ``Axis`` position.
*/
public enum Position { public enum Position {
/**
* Bottom/Left ``Axis``.
*/
LOW = 0, LOW = 0,
/**
* Top/Right ``Axis``.
*/
HIGH = 1, HIGH = 1,
/**
* 2 ``Axes``.
*/
BOTH = 2 BOTH = 2
} }
/**
* Position.
*/
public Position position = Position.LOW; public Position position = Position.LOW;
string _format = "%.2Lf"; /**
string _date_format = "%Y.%m.%d"; * Data type.
string _time_format = "%H:%M:%S"; */
int _dsec_signs = 2; // 2 signs = centiseconds public enum DType {
public string format { /**
* 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; } get { return _format; }
set { set {
// TODO: check format // TODO: check format
@ -48,7 +104,11 @@ namespace Gtk.CairoChart {
} }
default = "%.2Lf"; default = "%.2Lf";
} }
public string date_format {
/**
* Date print string format.
*/
public virtual string date_format {
get { return _date_format; } get { return _date_format; }
set { set {
// TODO: check format // TODO: check format
@ -56,7 +116,11 @@ namespace Gtk.CairoChart {
} }
default = "%Y.%m.%d"; default = "%Y.%m.%d";
} }
public string time_format {
/**
* Time print string format.
*/
public virtual string time_format {
get { return _time_format; } get { return _time_format; }
set { set {
// TODO: check format // TODO: check format
@ -64,7 +128,13 @@ namespace Gtk.CairoChart {
} }
default = "%H:%M:%S"; 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; } get { return _dsec_signs; }
set { set {
// TODO: check format // TODO: check format
@ -72,30 +142,441 @@ namespace Gtk.CairoChart {
} }
default = 2; 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(0, 0, 0, 0.25)); // Color(), 1, {2, 3});
/**
* Number of equally placed points to evaluate records sizes.
*/
public int nrecords = 8;
/**
* 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._date_format = this._date_format;
axis._dsec_signs = this._dsec_signs; axis._dsec_signs = this._dsec_signs;
axis._format = this._format; axis._format = this._format;
axis._time_format = this._time_format; axis._time_format = this._time_format;
axis.color = this.color; axis.color = this.color;
axis.font_indent = this.font_indent; axis.font = this.font.copy();
axis.font_style = this.font_style; axis.grid_style = this.grid_style;
axis.line_style = this.line_style; axis.range = this.range.copy();
axis.max = this.max; axis.place = this.place.copy();
axis.min = this.min;
axis.position = this.position; axis.position = this.position;
axis.scale_type = this.scale_type; axis.scale = this.scale;
axis.title = this.title.copy(); axis.title = this.title.copy();
axis.type = this.type; axis.dtype = this.dtype;
axis.nrecords = this.nrecords;
return axis; 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 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;
}
}
var max_rec_spacing = 2 * (is_x ? font.vspacing : font.hspacing);
var max_title_width = title.text == "" ? 0 : title.width + font.hspacing;
var max_title_height = title.text == "" ? 0 : title.height + font.vspacing;
var dx = max_rec_width + max_rec_spacing + max_title_width;
var dy = max_rec_height + max_rec_spacing + max_title_height;
chart.evarea.x0 -= dx; chart.evarea.x1 += dx; chart.evarea.y0 -= dy; chart.evarea.y1 += dy;
if (nskip != 0)
--nskip;
else {
var max_rec_height_all = max_rec_height;
var max_rec_width_all = max_rec_width;
if (is_x)
join_rel_axes (si, true, ref max_rec_width_all, ref max_rec_height_all, ref max_rec_spacing, ref max_title_height, ref nskip);
else
join_rel_axes (si, true, ref max_rec_width_all, ref max_rec_height_all, ref max_rec_spacing, ref max_title_width, ref nskip);
if (is_x && (!chart.joint_x || si == chart.zoom_1st_idx)) {
var tmp = max_rec_height_all + max_rec_spacing + max_title_height;
switch (position) {
case Position.LOW: chart.evarea.y1 -= tmp; break;
case Position.HIGH: chart.evarea.y0 += tmp; break;
}
}
if (!is_x && (!chart.joint_y || si == chart.zoom_1st_idx)) {
var tmp = max_rec_width_all + max_rec_spacing + max_title_width;
switch (position) {
case Position.LOW: chart.evarea.x0 += tmp; break;
case Position.HIGH: 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.evarea.x0 += dx; chart.evarea.x1 -= dx; chart.evarea.y0 += dy; chart.evarea.y1 -= dy;
chart.ctx.stroke ();
}
/**
* Joins equal axes.
* @param nskip returns number of series to skip printing.
*/
public virtual void join_axes (ref int nskip) {
if (!ser.zoom_show) return;
if (nskip != 0) {--nskip; return;}
var max_rec_width = 0.0, max_rec_height = 0.0;
calc_rec_sizes (this, out max_rec_width, out max_rec_height, is_x);
var max_rec_spacing = 2 * (is_x ? font.vspacing : font.hspacing);
var max_title_width = title.text == "" ? 0 : title.width + font.hspacing;
var max_title_height = title.text == "" ? 0 : title.height + 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_rec_spacing, ref max_title_height, ref nskip);
else
join_rel_axes (si, true, ref max_rec_width, ref max_rec_height, ref max_rec_spacing, ref max_title_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 + font.vspacing;
switch (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_rec_spacing + max_title_height;
switch (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_rec_spacing + max_title_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 + 2 * text.font.hspacing);
max_rec_height = double.max (max_rec_height, text.height + 2 * text.font.vspacing);
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 + 2 * text.font.hspacing);
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 + 2 * text.font.hspacing);
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_rec_spacing,
ref double max_title_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_rec_spacing = double.max (max_rec_spacing, 2 * (is_x ? a2.font.vspacing : a2.font.hspacing));
max_title_size = double.max (
max_title_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;
grid_style.color.red = chart.joint_color.red;
grid_style.color.green = chart.joint_color.green;
grid_style.color.blue = chart.joint_color.blue;
} 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;
}
}
}
}
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +1,46 @@
namespace Gtk.CairoChart { namespace CairoChart {
/**
* R/G/B/A Color.
*/
public struct Color { public struct Color {
/**
* Red component.
*/
double red; double red;
/**
* Green component.
*/
double green; double green;
/**
* Blue component.
*/
double blue; double blue;
/**
* Alpha component.
*/
double alpha; 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;
} }
} }
} }

634
src/Cursor.vala Normal file
View File

@ -0,0 +1,634 @@
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();
/**
* Value label style.
*/
public LabelStyle label_style = new LabelStyle();
/**
* 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.label_style = label_style.copy();
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;
// value label background
chart.color = label_style.bg_color;
chart.ctx.rectangle (svp.x - size.x / 2, svp.y - size.y / 2, size.x, size.y);
chart.ctx.fill();
// value label frame
label_style.frame_style.apply(chart);
chart.ctx.move_to (svp.x - size.x / 2, svp.y - size.y / 2);
chart.ctx.rel_line_to (size.x, 0);
chart.ctx.rel_line_to (0, size.y);
chart.ctx.rel_line_to (-size.x, 0);
chart.ctx.rel_line_to (0, -size.y);
chart.ctx.stroke();
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));
Point size; bool show_x, show_date, show_time, show_y;
cross_what_to_show(s, out show_x, out show_time, out show_date, out show_y);
calc_cross_sizes (s, point, out size, show_x, show_time, show_date, show_y);
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)));
Point size; bool show_x, show_date, show_time, show_y;
cross_what_to_show(s, out show_x, out show_time, out show_date, out show_y);
calc_cross_sizes (s, point, out size, show_x, show_time, show_date, show_y);
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;
Point 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 scr2cell (int m, int n, Point p, out int i, out int j) {
i = (int)((p.x - chart.plarea.x0) / chart.plarea.width * m);
j = (int)((p.y - chart.plarea.y0) / chart.plarea.height * n);
}
protected virtual void cell2scr (int m, int n, int i, int j, out Point p) {
p = Point(chart.plarea.x0 + chart.plarea.width * (i + 0.5) / m,
chart.plarea.y0 + chart.plarea.height * (j + 0.5)/ n);
}
protected virtual void calc_cursors_value_positions () {
// 1. Find maximum width/height of cursors values.
var max_width = 1.0, max_height = 1.0;
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) {
unowned Cross[] cr = crossings[ccsi].crossings;
max_width = double.max(max_width, cr[cci].size.x
+ 4 * double.max(chart.series[cr[cci].series_index].axis_x.font.hspacing,
chart.series[cr[cci].series_index].axis_y.font.hspacing));
max_height = double.max(max_height, cr[cci].size.y
+ 4 * double.max(chart.series[cr[cci].series_index].axis_x.font.vspacing,
chart.series[cr[cci].series_index].axis_y.font.vspacing));
}
}
// 2. Calculate 2D-array sizes.
var m = (int.max(1, (int)(chart.plarea.width / max_width))),
n = (int.max(1, (int)(chart.plarea.height / max_height)));
// 3. Create 2D-array of bool or links to cursors values.
var arr2d_e = new bool[m, n];
// 4. Set Busy/Cross Cells
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) {
unowned Cross[] cr = crossings[ccsi].crossings;
cr[cci].scr_point = chart.series[cr[cci].series_index].scr_pnt (cr[cci].point);
int i = 0, j = 0;
scr2cell(m, n, cr[cci].scr_point, out i, out j);
arr2d_e[i, j] = true;
}
}
// 5. Calculate 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) {
unowned Cross[] cr = crossings[ccsi].crossings;
int i = 0, j = 0;
scr2cell(m, n, cr[cci].scr_point, out i, out j);
for (var radius = 1; radius < int.max(m, n); ++radius) {
bool found = false;
// top, bottom
int[] ll = {int.max(0, j - radius), int.min(n - 1, j + radius)};
foreach (var l in ll) {
for (var k = int.max(0, i - radius); k <= int.min(m - 1, i + radius); ++k) {
if (k == i) continue;
if (!arr2d_e[k, l]) {
arr2d_e[k, l] = true;
cell2scr(m, n, k, l, out cr[cci].scr_value_point);
found = true;
break;
}
}
if (found) break;
}
if (found) break;
// left, right
int[] kk = {int.max(0, i - radius), int.min(m - 1, i + radius)};
foreach (var k in kk) {
for (var l = int.max(0, j - radius); l <= int.min(n - 1, j + radius); ++l) {
if (l == j) continue;
if (!arr2d_e[k, l]) {
arr2d_e[k, l] = true;
cell2scr(m, n, k, l, out cr[cci].scr_value_point);
found = true;
break;
}
}
if (found) break;
}
if (found) break;
}
}
}
}
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 Point size,
bool show_x = false, bool show_time = false,
bool show_date = false, bool show_y = false) {
if (show_x == show_time == show_date == show_y == false)
cross_what_to_show(s, out show_x, out show_time, out show_date, out show_y);
size = Point ();
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 += s.axis_x.font.hspacing + s.axis_y.font.hspacing;
if (show_date && show_time) h_x += s.axis_x.font.vspacing;
size.y = double.max (h_x, h_y);
}
}
}

90
src/Font.vala Normal file
View File

@ -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 = 2;
/**
* Horizontal spacing.
*/
public double hspacing = 2;
/**
* Both vertical & horizontal spacing (set only).
*/
public virtual double spacing {
protected get {
return 0;
}
set {
vspacing = hspacing = value;
}
default = 2;
}
/**
* 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 = 2,
double hspacing = 2
) {
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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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};
}
}
}

46
src/Label.vala Normal file
View File

@ -0,0 +1,46 @@
namespace CairoChart {
/**
* ``Cursors`` values style.
*/
public class LabelStyle {
/**
* Background color.
*/
public Color bg_color;
/**
* Frame line style.
*/
public LineStyle frame_style;
/**
* Font style.
*/
public Font font;
/**
* Constructs a new ``LabelStyle``.
* @param font font style.
* @param bg_color background color.
* @param frame_style frame line style.
*/
public LabelStyle (
Color bg_color = Color(1, 1, 1, 1),
LineStyle frame_style = LineStyle(Color(0, 0, 0, 0.1)),
Font font = new Font()
) {
this.bg_color = bg_color;
this.frame_style = frame_style;
this.font = font;
}
/**
* Gets a copy of the ``LabelStyle``.
*/
public virtual LabelStyle copy () {
return new LabelStyle(bg_color, frame_style, font);
}
}
}

View File

@ -1,8 +0,0 @@
namespace Gtk.CairoChart {
public struct LabelStyle {
FontStyle font_style;
LineStyle frame_line_style;
Color bg_color;
Color frame_color;
}
}

View File

@ -1,28 +1,275 @@
namespace Gtk.CairoChart { namespace CairoChart {
/**
* {@link Chart} ``Legend``.
*/
public class Legend { public class Legend {
protected unowned Chart chart;
protected double [] max_font_heights;
/**
* Show legend?
*/
public bool show = true;
/**
* ``Legend`` position.
*/
public enum Position { public enum Position {
TOP = 0, // default /**
* Top position.
*/
TOP = 0,
/**
* Left position.
*/
LEFT, LEFT,
/**
* Right position.
*/
RIGHT, RIGHT,
/**
* Bottom position.
*/
BOTTOM 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.position = this.position;
legend.font_style = this.font_style;
legend.bg_color = this.bg_color; 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; 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 = line_length + s.title.width + s.title.font.hspacing * 2;
if (leg_width_sum + 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 + s.title.font.hspacing;
var y = legend_y0 + leg_height_sum + mfh[heights_idx] / 2;
// series title
chart.ctx.move_to (x + line_length, y + s.title.height / 2);
chart.color = s.title.color;
s.title.show();
// series line style
chart.ctx.move_to (x, y);
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));
break;
}
switch (position) {
case Position.TOP:
case Position.BOTTOM:
var ser_title_width = line_length + s.title.width + s.title.font.hspacing * 2;
leg_width_sum += ser_title_width;
max_font_h = double.max (max_font_h, s.title.height + s.title.font.vspacing * 2);
break;
case Position.LEFT:
case Position.RIGHT:
switch (process_type) {
case ProcessType.CALC:
mfh += s.title.height + s.title.font.vspacing * 2;
width = double.max (width, s.title.font.hspacing * 2 + line_length + s.title.width);
break;
case ProcessType.DRAW:
heights_idx++;
break;
}
leg_height_sum += s.title.height + s.title.font.vspacing * 2;
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 + spacing; break;
case Position.BOTTOM: chart.evarea.y1 -= height + spacing; break;
case Position.LEFT: chart.evarea.x0 += width + spacing; break;
case Position.RIGHT: chart.evarea.x1 -= width + spacing; break;
}
break;
}
max_font_heights = mfh;
} }
} }
} }

72
src/Line.vala Normal file
View File

@ -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);
}
}
}

View File

@ -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;
}
}
}

100
src/Marker.vala Normal file
View File

@ -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;
}
}
}
}

161
src/Math.vala Normal file
View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -1,9 +1,51 @@
namespace Gtk.CairoChart { namespace CairoChart {
/**
* 64-bit point.
*/
public struct 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; Float128 x;
/**
* Y-coordinate.
*/
Float128 y; 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; this.x = x; this.y = y;
} }
} }

118
src/Range.vala Normal file
View File

@ -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;
}
}
}

View File

@ -1,65 +1,111 @@
using Cairo; using Cairo;
namespace Gtk.CairoChart { namespace CairoChart {
/**
* ``Chart`` series.
*/
public class Series { public class Series {
public Point[] points = {}; protected unowned Chart chart { get; protected set; default = null; }
public enum Sort {
BY_X = 0,
BY_Y = 1,
NO_SORT
}
public Sort sort = Sort.BY_X;
public Axis axis_x = new Axis(); /**
public Axis axis_y = new Axis(); * Title of the ``Chart``.
*/
public enum MarkerType { public Text title;
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 ();
/**
* ``Series`` line style.
*/
public LineStyle line_style = LineStyle (); public LineStyle line_style = LineStyle ();
protected Color _color = Color (0.0, 0.0, 0.0, 1.0); /**
public Color color { * 128-bit (X;Y) points.
get { return _color; } */
set { public Point128[] points = {};
_color = value;
line_style.color = _color; /**
axis_x.color = _color; * ``Marker`` style.
axis_y.color = _color; */
grid.color = _color; public Marker marker;
grid.color.alpha = 0.5;
grid.line_style.color = _color; /**
grid.line_style.color.alpha = 0.5; * Sort style.
} */
default = Color (0.0, 0.0, 0.0, 1.0); 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.red = axis_y.grid_style.color.red = value.red;
axis_x.grid_style.color.green = axis_y.grid_style.color.green = value.green;
axis_x.grid_style.color.blue = axis_y.grid_style.color.blue = value.blue;
}
default = Color (0, 0, 0, 1);
}
/**
* Show the ``Series`` in zoomed area or not.
*/
public bool zoom_show = true; public bool zoom_show = true;
public Series copy () { /**
var series = new Series (); * Constructs a new ``Series``.
series._color = this._color; * @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_x = this.axis_x.copy ();
series.axis_y = this.axis_y.copy (); series.axis_y = this.axis_y.copy ();
series.grid = this.grid.copy ();
series.line_style = this.line_style; series.line_style = this.line_style;
series.marker_type = this.marker_type; series.marker = this.marker;
series.place = this.place.copy();
series.points = this.points; series.points = this.points;
series.sort = this.sort; series.sort = this.sort;
series.title = this.title.copy(); series.title = this.title.copy();
@ -67,7 +113,58 @@ namespace Gtk.CairoChart {
return series; 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();
} }
} }
} }

View File

@ -1,85 +1,142 @@
namespace Gtk.CairoChart { namespace CairoChart {
[Compact]
/**
* ``CairoChart`` Text.
*/
public class 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 Color color = Color();
public Cairo.TextExtents get_extents (Cairo.Context context) { /**
context.select_font_face (style.family, * Cairo ``Text`` extents.
style.slant, */
style.weight); protected virtual Cairo.TextExtents ext {
context.set_font_size (style.size); get {
Cairo.TextExtents extents; if (_ext == null) {
context.text_extents (text, out extents); chart.ctx.select_font_face (font.family, font.slant, font.weight);
return extents; chart.ctx.set_font_size (font.size);
} chart.ctx.text_extents (text, out _ext);
}
public double get_width (Cairo.Context context) { return _ext;
var extents = get_extents (context); }
switch (style.orientation) { protected set {
case FontOrient.HORIZONTAL: return extents.width;
case FontOrient.VERTICAL: return extents.height;
default: return 0.0;
} }
} }
public double get_height (Cairo.Context context) { /**
var extents = get_extents (context); * ``Text`` width.
switch (style.orientation) { */
case FontOrient.HORIZONTAL: return extents.height; public virtual double width {
case FontOrient.VERTICAL: return extents.width; get {
default: return 0.0; 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 double get_x_bearing (Cairo.Context context) { /**
var extents = get_extents (context); * ``Text`` height.
switch (style.orientation) { */
case FontOrient.HORIZONTAL: return extents.x_bearing; public virtual double height {
case FontOrient.VERTICAL: return extents.y_bearing; get {
default: return 0.0; 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 {
} }
} }
public struct Size { /**
double width; * Constructs a new ``Text``.
double height; * @param chart ``Chart`` instance.
double x_bearing; * @param text ``Text`` string.
} * @param font ``Text`` font style.
* @param color ``Text`` color.
public Size size (Cairo.Context context) { */
var sz = Size(); public Text (Chart chart,
var extents = get_extents (context); string text = "",
switch (style.orientation) { Font font = new Font(),
case FontOrient.HORIZONTAL: Color color = Color()
sz.width = extents.width; ) {
sz.height = extents.height; this.chart = chart;
sz.x_bearing = extents.x_bearing;
break;
case FontOrient.VERTICAL:
sz.width = extents.height;
sz.height = extents.width;
sz.x_bearing = extents.y_bearing;
break;
}
return sz;
}
public Text (string text = "",
FontStyle style = FontStyle(),
Color color = Color()) {
this.text = text; this.text = text;
this.style = style; this.font = font;
this.color = color; 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.text = this.text;
text.style = this.style; text.font = this.font.copy();
text._ext = this._ext;
text.color = this.color; text.color = this.color;
return text; 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);
}
}
} }
} }

View File

@ -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")] [CCode (cname = "cairo_chart_float128", has_type_id = false, cheader_filename = "cairo-chart-float128type.h")]
public struct Float128 : double {} public struct Float128 : double {}
/**
* Long Double float type.
*/
[CCode (cname = "cairo_chart_long_double", has_type_id = false, cheader_filename = "cairo-chart-float128type.h")] [CCode (cname = "cairo_chart_long_double", has_type_id = false, cheader_filename = "cairo-chart-float128type.h")]
public struct LongDouble : double {} public struct LongDouble : double {}
} }

View File

@ -1,211 +1,247 @@
using Gtk, CairoChart; using Gtk, CairoChart;
void plot_chart1 (Chart chart) { void plot_chart1 (Chart chart) {
var s1 = new Series (); chart.title.text = "Chart №1";
var s2 = new Series (); var s1 = new Series (chart);
var s3 = new Series (); var s2 = new Series (chart);
var s3 = new Series (chart);
s1.title = new Text("Series 1"); s1.color = Color (1, 0, 0); s1.title = new Text(chart, "Series 1"); s1.color = Color (1, 0, 0);
s1.points = {Point(0, 0), Point(2, 1), Point(1, 3)}; s1.points = {Point128(0, 0), Point128(2, 1), Point128(1, 3)};
s1.axis_x.position = Axis.Position.HIGH; s1.axis_x.position = Axis.Position.HIGH;
s1.axis_x.format = "%.3Lf"; s1.axis_x.format = "%.3Lf";
s2.title = new Text("Series 2"); s2.color = Color (0, 1, 0); s2.title = new Text(chart, "Series 2"); s2.color = Color (0, 1, 0);
s2.points = {Point(5, -3), Point(25, -18), Point(-11, 173)}; s2.points = {Point128(5, -3), Point128(25, -18), Point128(-11, 173)};
s3.title = new Text("Series 3"); s3.color = Color (0, 0, 1); s3.title = new Text(chart, "Series 3"); s3.color = Color (0, 0, 1);
s3.points = {Point(9, 17), Point(2, 10), Point(122, 31)}; s3.points = {Point128(9, 17), Point128(2, 10), Point128(122, 31)};
s3.axis_y.position = Axis.Position.HIGH; s3.axis_y.position = Axis.Position.HIGH;
s1.axis_x.min = 0; s1.axis_x.max = 2; s1.axis_x.range.min = 0; s1.axis_x.range.max = 2;
s1.axis_y.min = 0; s1.axis_y.max = 3; s1.axis_y.range.min = 0; s1.axis_y.range.max = 3;
s1.place.x_low = 0.25; s1.place.x_high = 0.75; s1.axis_x.place.min = 0.25; s1.axis_x.place.max = 0.75;
s1.place.y_low = 0.3; s1.place.y_high = 0.9; 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_x.range.min = -15; s2.axis_x.range.max = 30;
s2.axis_y.min = -20; s2.axis_y.max = 200; s2.axis_y.range.min = -20; s2.axis_y.range.max = 200;
s2.place.x_low = 0.5; s2.place.x_high = 1; s2.axis_x.place.min = 0.5; s2.axis_x.place.max = 1;
s2.place.y_low = 0.0; s2.place.y_high = 0.5; 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_x.range.min = 0; s3.axis_x.range.max = 130;
s3.axis_y.min = 15; s3.axis_y.max = 35; s3.axis_y.range.min = 15; s3.axis_y.range.max = 35;
s3.place.x_low = 0; s3.place.x_high = 0.5; s3.axis_x.place.min = 0; s3.axis_x.place.max = 0.5;
s3.place.y_low = 0.5; s3.place.y_high = 1.0; s3.axis_y.place.min = 0.5; s3.axis_y.place.max = 1.0;
s2.marker_type = Series.MarkerType.CIRCLE; s1.marker.shape = Marker.Shape.SQUARE;
s3.marker_type = Series.MarkerType.PRICLE_TRIANGLE; 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_x.title = new Text(chart, "Series 1: Axis X.");
s1.axis_y.title = new Text("Series 1: Axis Y."); s1.axis_y.title = new Text(chart, "Series 1: Axis Y.");
s2.axis_x.title = new Text("Series 2: Axis X."); s2.axis_x.title = new Text(chart, "Series 2: Axis X.");
s2.axis_y.title = new Text("Series 2: Axis Y."); s2.axis_y.title = new Text(chart, "Series 2: Axis Y.");
s3.axis_x.title = new Text("Series 3: Axis X."); s3.axis_x.title = new Text(chart, "Series 3: Axis X.");
s3.axis_y.title = new Text("Series 3: Axis Y."); s3.axis_y.title = new Text(chart, "Series 3: Axis Y.");
chart.series = { s1, s2, s3 }; 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) { void plot_chart2 (Chart chart) {
var s1 = new Series (); var s1 = new Series (chart);
var s2 = new Series (); var s2 = new Series (chart);
var s3 = new Series (); var s3 = new Series (chart);
s1.title = new Text("Series 1"); s1.color = Color (1, 0, 0); s1.title = new Text(chart, "Series 1"); s1.color = Color (1, 0, 0);
s1.points = {Point(-12, 0), Point(2, 1), Point(20, 3)}; s1.points = {Point128(-12, 0), Point128(2, 1), Point128(20, 3)};
s2.axis_y.position = Axis.Position.HIGH; s2.axis_y.position = Axis.Position.HIGH;
s1.axis_x.format = "%.3Lf"; s1.axis_x.format = "%.3Lf";
s2.title = new Text("Series 2"); s2.color = Color (0, 1, 0); s2.title = new Text(chart, "Series 2"); s2.color = Color (0, 1, 0);
s2.points = {Point(5, -3), Point(25, -18), Point(-11, 173)}; s2.points = {Point128(5, -3), Point128(25, -18), Point128(-11, 173)};
s3.title = new Text("Series 3"); s3.color = Color (0, 0, 1); s3.title = new Text(chart, "Series 3"); s3.color = Color (0, 0, 1);
s3.points = {Point(9, 17), Point(2, 10), Point(-15, 31)}; s3.points = {Point128(9, 17), Point128(2, 10), Point128(-15, 31)};
s3.axis_y.position = Axis.Position.HIGH; s3.axis_y.position = Axis.Position.HIGH;
s1.axis_x.min = -15; s1.axis_x.max = 30; s1.axis_x.range.min = -15; s1.axis_x.range.max = 30;
s1.axis_y.min = 0; s1.axis_y.max = 3; s1.axis_y.range.min = 0; s1.axis_y.range.max = 3;
s1.place.x_low = 0.0; s1.place.x_high = 1.0; s1.axis_x.place.min = 0.0; s1.axis_x.place.max = 1.0;
s1.place.y_low = 0.3; s1.place.y_high = 0.9; 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_x.range.min = -15; s2.axis_x.range.max = 30;
s2.axis_y.min = -20; s2.axis_y.max = 200; s2.axis_y.range.min = -20; s2.axis_y.range.max = 200;
s2.place.x_low = 0.0; s2.place.x_high = 1.0; s2.axis_x.place.min = 0.0; s2.axis_x.place.max = 1.0;
s2.place.y_low = 0.0; s2.place.y_high = 0.5; 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_x.range.min = -15; s3.axis_x.range.max = 30;
s3.axis_y.min = 15; s3.axis_y.max = 35; s3.axis_y.range.min = 15; s3.axis_y.range.max = 35;
s3.place.x_low = 0.0; s3.place.x_high = 1.0; s3.axis_x.place.min = 0.0; s3.axis_x.place.max = 1.0;
s3.place.y_low = 0.5; s3.place.y_high = 1.0; s3.axis_y.place.min = 0.5; s3.axis_y.place.max = 1.0;
s1.marker_type = Series.MarkerType.PRICLE_CIRCLE; s1.marker.shape = Marker.Shape.PRICLE_CIRCLE;
s2.marker_type = Series.MarkerType.PRICLE_SQUARE; 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_x.title = new Text(chart, "All Series: Axis X.");
s1.axis_y.title = new Text("Series 1: Axis Y."); s1.axis_y.title = new Text(chart, "Series 1: Axis Y.");
s2.axis_x.title = new Text("All Series: Axis X."); s2.axis_x.title = new Text(chart, "All Series: Axis X.");
s2.axis_y.title = new Text("Series 2: Axis Y."); s2.axis_y.title = new Text(chart, "Series 2: Axis Y.");
s3.axis_x.title = new Text("All Series: Axis X."); s3.axis_x.title = new Text(chart, "All Series: Axis X.");
s3.axis_y.title = new Text("Series 3: Axis Y."); 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.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.dtype = s2.axis_x.dtype = s3.axis_x.dtype = Axis.DType.DATE_TIME;
//s1.axis_x.max = s2.axis_x.max = s3.axis_x.max = 5*24*3600; //s1.axis_x.range.max = s2.axis_x.range.max = s3.axis_x.range.max = 5*24*3600;
chart.series = { s1, s2, s3 }; chart.series = { s1, s2, s3 };
} }
void plot_chart3 (Chart chart) { void plot_chart3 (Chart chart) {
var s1 = new Series (); var s1 = new Series (chart);
var s2 = new Series (); var s2 = new Series (chart);
var s3 = new Series (); var s3 = new Series (chart);
s1.title = new Text("Series 1"); s1.color = Color (1, 0, 0); s1.title = new Text(chart, "Series 1"); s1.color = Color (1, 0, 0);
s1.points = {Point(0, 70), Point(2, 155), Point(1, -3)}; s1.points = {Point128(0, 70), Point128(2, 155), Point128(1, -3)};
s1.axis_x.position = Axis.Position.HIGH; s1.axis_x.position = Axis.Position.HIGH;
s1.axis_y.position = Axis.Position.HIGH; s1.axis_y.position = Axis.Position.HIGH;
s1.axis_x.format = "%.3Lf"; s1.axis_x.format = "%.3Lf";
s2.title = new Text("Series 2"); s2.color = Color (0, 1, 0); s2.title = new Text(chart, "Series 2"); s2.color = Color (0, 1, 0);
s2.points = {Point(5, -3), Point(25, -18), Point(-11, 173)}; s2.points = {Point128(5, -3), Point128(25, -18), Point128(-11, 173)};
s2.axis_y.position = Axis.Position.HIGH; s2.axis_y.position = Axis.Position.HIGH;
s3.title = new Text("Series 3"); s3.color = Color (0, 0, 1); s3.title = new Text(chart, "Series 3"); s3.color = Color (0, 0, 1);
s3.points = {Point(9, -17), Point(2, 10), Point(122, 31)}; s3.points = {Point128(9, -17), Point128(2, 10), Point128(122, 31)};
s3.axis_y.position = Axis.Position.HIGH; s3.axis_y.position = Axis.Position.HIGH;
s1.axis_x.min = 0; s1.axis_x.max = 2; s1.axis_x.range.min = 0; s1.axis_x.range.max = 2;
s1.axis_y.min = -20; s1.axis_y.max = 200; s1.axis_y.range.min = -20; s1.axis_y.range.max = 200;
s1.place.x_low = 0.25; s1.place.x_high = 0.75; s1.axis_x.place.min = 0.25; s1.axis_x.place.max = 0.75;
s1.place.y_low = 0.0; s1.place.y_high = 1.0; 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_x.range.min = -15; s2.axis_x.range.max = 30;
s2.axis_y.min = -20; s2.axis_y.max = 200; s2.axis_y.range.min = -20; s2.axis_y.range.max = 200;
s2.place.x_low = 0.5; s2.place.x_high = 1; s2.axis_x.place.min = 0.5; s2.axis_x.place.max = 1;
s2.place.y_low = 0.0; s2.place.y_high = 1.0; 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_x.range.min = 0; s3.axis_x.range.max = 130;
s3.axis_y.min = -20; s3.axis_y.max = 200; s3.axis_y.range.min = -20; s3.axis_y.range.max = 200;
s3.place.x_low = 0; s3.place.x_high = 0.5; s3.axis_x.place.min = 0; s3.axis_x.place.max = 0.5;
s3.place.y_low = 0.0; s3.place.y_high = 1.0; s3.axis_y.place.min = 0.0; s3.axis_y.place.max = 1.0;
s2.marker_type = Series.MarkerType.PRICLE_CIRCLE; s1.marker.shape = Marker.Shape.SQUARE;
s3.marker_type = Series.MarkerType.TRIANGLE; 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_x.title = new Text(chart, "Series 1: Axis X.");
s1.axis_y.title = new Text("Series 1: Axis Y."); s1.axis_y.title = new Text(chart, "All Series: Axis Y.");
s2.axis_x.title = new Text("Series 2: Axis X."); s2.axis_x.title = new Text(chart, "Series 2: Axis X.");
s2.axis_y.title = new Text("Series 2: Axis Y."); s2.axis_y.title = new Text(chart, "All Series: Axis Y.");
s3.axis_x.title = new Text("Series 3: Axis X."); s3.axis_x.title = new Text(chart, "Series 3: Axis X.");
s3.axis_y.title = new Text("Series 3: Axis Y."); 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; //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 }; chart.series = { s1, s2, s3 };
} }
void plot_chart4 (Chart chart) { void plot_chart4 (Chart chart) {
var s1 = new Series (); var s1 = new Series (chart);
var s2 = new Series (); var s2 = new Series (chart);
var s3 = new Series (); var s3 = new Series (chart);
var s4 = new Series (); var s4 = new Series (chart);
s1.axis_x.type = Axis.Type.DATE_TIME; s1.axis_x.dtype = Axis.DType.DATE_TIME;
s3.axis_x.type = Axis.Type.DATE_TIME; s3.axis_x.dtype = Axis.DType.DATE_TIME;
s4.axis_x.type = Axis.Type.DATE_TIME; s4.axis_x.dtype = Axis.DType.DATE_TIME;
s4.axis_x.dsec_signs = 5; s4.axis_x.dsec_signs = 5;
var now = new DateTime.now_local().to_unix(); var now = new DateTime.now_local().to_unix();
var high = (uint64) (253000000000L); var high = (uint64) (253000000000L);
s1.title = new Text("Series 1"); s1.color = Color (1, 0, 0); s1.title = new Text(chart, "Series 1"); s1.color = Color (1, 0, 0);
s1.points = {Point(now, 70), Point(now - 100000, 155), Point(now + 100000, 30)}; s1.points = {Point128(now, 70), Point128(now - 100000, 155), Point128(now + 100000, 30)};
s1.axis_x.position = Axis.Position.HIGH; s1.axis_x.position = Axis.Position.HIGH;
s1.axis_y.position = Axis.Position.HIGH; s1.axis_y.position = Axis.Position.HIGH;
s2.title = new Text("Series 2"); s2.color = Color (0, 1, 0); s2.title = new Text(chart, "Series 2"); s2.color = Color (0, 1, 0);
s2.points = {Point(5, -3), Point(25, -18), Point(-11, 173)}; s2.points = {Point128(5, -3), Point128(25, -18), Point128(-11, 173)};
s2.axis_y.position = Axis.Position.HIGH; s2.axis_y.position = Axis.Position.HIGH;
s3.title = new Text("Series 3"); s3.color = Color (0, 0, 1); s3.title = new Text(chart, "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.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; s3.axis_y.position = Axis.Position.HIGH;
s4.title = new Text("Series 4"); s4.color = Color (0.5, 0.3, 0.9); s4.title = new Text(chart, "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.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; s4.axis_y.position = Axis.Position.HIGH;
s1.axis_x.min = now - 100000; s1.axis_x.max = now + 100000; s1.axis_x.range.min = now - 100000; s1.axis_x.range.max = now + 100000;
s1.axis_y.min = -20; s1.axis_y.max = 200; s1.axis_y.range.min = -20; s1.axis_y.range.max = 200;
s1.place.x_low = 0.25; s1.place.x_high = 0.75; s1.axis_x.place.min = 0.25; s1.axis_x.place.max = 0.75;
s1.place.y_low = 0.0; s1.place.y_high = 1.0; 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_x.range.min = -15; s2.axis_x.range.max = 30;
s2.axis_y.min = -20; s2.axis_y.max = 200; s2.axis_y.range.min = -20; s2.axis_y.range.max = 200;
s2.place.x_low = 0.2; s2.place.x_high = 1; s2.axis_x.place.min = 0.2; s2.axis_x.place.max = 1;
s2.place.y_low = 0.0; s2.place.y_high = 1.0; 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_x.range.min = high - 2; s3.axis_x.range.max = high + 1;
s3.axis_y.min = -20; s3.axis_y.max = 200; s3.axis_y.range.min = -20; s3.axis_y.range.max = 200;
s3.place.x_low = 0; s3.place.x_high = 0.8; s3.axis_x.place.min = 0; s3.axis_x.place.max = 0.8;
s3.place.y_low = 0.0; s3.place.y_high = 1.0; 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_x.range.min = high + 0.0049; s4.axis_x.range.max = high + 0.0054;
s4.axis_y.min = -20; s4.axis_y.max = 200; s4.axis_y.range.min = -20; s4.axis_y.range.max = 200;
s4.place.x_low = 0.2; s4.place.x_high = 1.0; s4.axis_x.place.min = 0.2; s4.axis_x.place.max = 1.0;
s4.place.y_low = 0.0; s4.place.y_high = 1.0; s4.axis_y.place.min = 0.0; s4.axis_y.place.max = 1.0;
s2.marker_type = Series.MarkerType.PRICLE_CIRCLE; s1.marker.shape = Marker.Shape.SQUARE;
s3.marker_type = Series.MarkerType.TRIANGLE; s2.marker.shape = Marker.Shape.PRICLE_CIRCLE;
s4.marker_type = Series.MarkerType.PRICLE_SQUARE; 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_x.title = new Text(chart, "Series 1: Axis X.");
s1.axis_y.title = new Text("Series 1: Axis Y."); s1.axis_y.title = new Text(chart, "All Series: Axis Y.");
s2.axis_x.title = new Text("Series 2: Axis X."); s2.axis_x.title = new Text(chart, "Series 2: Axis X.");
s2.axis_y.title = new Text("Series 2: Axis Y."); s2.axis_y.title = new Text(chart, "All Series: Axis Y.");
s3.axis_x.title = new Text("Series 3: Axis X."); s3.axis_x.title = new Text(chart, "Series 3: Axis X.");
s3.axis_y.title = new Text("Series 3: Axis Y."); s3.axis_y.title = new Text(chart, "All Series: Axis Y.");
s4.axis_x.title = new Text("Series 4: Axis X."); s4.axis_x.title = new Text(chart, "Series 4: Axis X.");
s4.axis_y.title = new Text("Series 4: Axis Y."); s4.axis_y.title = new Text(chart, "All Series: Axis Y.");
chart.cursors.style.orientation = Cursors.Orientation.HORIZONTAL;
chart.series = { s1, s2, s3, s4 }; chart.series = { s1, s2, s3, s4 };
} }
bool point_in_chart (Chart chart, double x, double y) { bool point_in_chart (Chart chart, double x, double y) {
if (x < chart.plot_area_x_min) return false; if (x < chart.plarea.x0) return false;
if (x > chart.plot_area_x_max) return false; if (x > chart.plarea.x1) return false;
if (y < chart.plot_area_y_min) return false; if (y < chart.plarea.y0) return false;
if (y > chart.plot_area_y_max) return false; if (y > chart.plarea.y1) return false;
return true; return true;
} }
@ -230,10 +266,10 @@ int main (string[] args) {
var chart2 = new Chart(); var chart2 = new Chart();
var chart3 = new Chart(); var chart3 = new Chart();
var chart4 = 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 button1 = new Button.with_label("Separate axes");
var button2 = new Button.with_label("Common X axes"); var button2 = new Button.with_label("Joint X axes");
var button3 = new Button.with_label("Common Y axes"); var button3 = new Button.with_label("Joint Y axes");
var button4 = new Button.with_label("Dates/Times"); var button4 = new Button.with_label("Dates/Times");
var button5 = new Button.with_label("rm Axis Titles"); var button5 = new Button.with_label("rm Axis Titles");
var button6 = new Button.with_label("Dates only"); var button6 = new Button.with_label("Dates only");
@ -245,12 +281,13 @@ int main (string[] args) {
plot_chart3 (chart3); plot_chart3 (chart3);
plot_chart4 (chart4); 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(); var da = new DrawingArea();
da.set_events ( Gdk.EventMask.BUTTON_PRESS_MASK da.set_events ( Gdk.EventMask.BUTTON_PRESS_MASK
|Gdk.EventMask.BUTTON_RELEASE_MASK |Gdk.EventMask.BUTTON_RELEASE_MASK
|Gdk.EventMask.POINTER_MOTION_MASK |Gdk.EventMask.POINTER_MOTION_MASK
|Gdk.EventMask.SCROLL_MASK
); );
var chart = chart1; var chart = chart1;
@ -270,9 +307,9 @@ int main (string[] args) {
case Legend.Position.LEFT: radio_button3.set_active(true); break; case Legend.Position.LEFT: radio_button3.set_active(true); break;
case Legend.Position.BOTTOM: radio_button4.set_active(true); break; case Legend.Position.BOTTOM: radio_button4.set_active(true); break;
} }
switch (chart.cursors_orientation) { switch (chart.cursors.style.orientation) {
case Chart.CursorOrientation.VERTICAL: radio_button7.set_active(true); break; case Cursors.Orientation.VERTICAL: radio_button7.set_active(true); break;
case Chart.CursorOrientation.HORIZONTAL: radio_button8.set_active(true); break; case Cursors.Orientation.HORIZONTAL: radio_button8.set_active(true); break;
} }
}); });
button2.clicked.connect (() => { button2.clicked.connect (() => {
@ -283,9 +320,9 @@ int main (string[] args) {
case Legend.Position.LEFT: radio_button3.set_active(true); break; case Legend.Position.LEFT: radio_button3.set_active(true); break;
case Legend.Position.BOTTOM: radio_button4.set_active(true); break; case Legend.Position.BOTTOM: radio_button4.set_active(true); break;
} }
switch (chart.cursors_orientation) { switch (chart.cursors.style.orientation) {
case Chart.CursorOrientation.VERTICAL: radio_button7.set_active(true); break; case Cursors.Orientation.VERTICAL: radio_button7.set_active(true); break;
case Chart.CursorOrientation.HORIZONTAL: radio_button8.set_active(true); break; case Cursors.Orientation.HORIZONTAL: radio_button8.set_active(true); break;
} }
}); });
button3.clicked.connect (() => { button3.clicked.connect (() => {
@ -296,9 +333,9 @@ int main (string[] args) {
case Legend.Position.LEFT: radio_button3.set_active(true); break; case Legend.Position.LEFT: radio_button3.set_active(true); break;
case Legend.Position.BOTTOM: radio_button4.set_active(true); break; case Legend.Position.BOTTOM: radio_button4.set_active(true); break;
} }
switch (chart.cursors_orientation) { switch (chart.cursors.style.orientation) {
case Chart.CursorOrientation.VERTICAL: radio_button7.set_active(true); break; case Cursors.Orientation.VERTICAL: radio_button7.set_active(true); break;
case Chart.CursorOrientation.HORIZONTAL: radio_button8.set_active(true); break; case Cursors.Orientation.HORIZONTAL: radio_button8.set_active(true); break;
} }
}); });
button4.clicked.connect (() => { button4.clicked.connect (() => {
@ -309,9 +346,9 @@ int main (string[] args) {
case Legend.Position.LEFT: radio_button4.set_active(true); break; case Legend.Position.LEFT: radio_button4.set_active(true); break;
case Legend.Position.BOTTOM: radio_button4.set_active(true); break; case Legend.Position.BOTTOM: radio_button4.set_active(true); break;
} }
switch (chart.cursors_orientation) { switch (chart.cursors.style.orientation) {
case Chart.CursorOrientation.VERTICAL: radio_button7.set_active(true); break; case Cursors.Orientation.VERTICAL: radio_button7.set_active(true); break;
case Chart.CursorOrientation.HORIZONTAL: radio_button8.set_active(true); break; case Cursors.Orientation.HORIZONTAL: radio_button8.set_active(true); break;
} }
}); });
button5.clicked.connect (() => { button5.clicked.connect (() => {
@ -393,13 +430,13 @@ int main (string[] args) {
radio_button7.toggled.connect ((button) => { radio_button7.toggled.connect ((button) => {
if (button.get_active()) { 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()); da.queue_draw_area(0, 0, da.get_allocated_width(), da.get_allocated_height());
} }
}); });
radio_button8.toggled.connect ((button) => { radio_button8.toggled.connect ((button) => {
if (button.get_active()) { 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()); da.queue_draw_area(0, 0, da.get_allocated_width(), da.get_allocated_height());
} }
}); });
@ -409,10 +446,10 @@ int main (string[] args) {
double sel_x0 = 0, sel_x1 = 0, sel_y0 = 0, sel_y1 = 0; double sel_x0 = 0, sel_x1 = 0, sel_y0 = 0, sel_y1 = 0;
double mov_x0 = 0, mov_y0 = 0; double mov_x0 = 0, mov_y0 = 0;
da.draw.connect((context) => { da.draw.connect((ctx) => {
chart.context = context; chart.ctx = ctx;
chart.width = da.get_allocated_width(); chart.area.width = da.get_allocated_width();
chart.height = da.get_allocated_height(); chart.area.height = da.get_allocated_height();
chart.clear(); chart.clear();
// user's pre draw operations here... // user's pre draw operations here...
@ -422,23 +459,23 @@ int main (string[] args) {
// user's post draw operations here... // user's post draw operations here...
if (mouse_state == MouseState.DRAW_SELECTION) 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 // show delta
var str = chart.get_cursors_delta_str(); var str = chart.cursors.get_delta_str();
if (str != "") { if (str != "") {
var text = "Δ = " + str; var text = "Δ = " + str;
var text_t = new Text(text); var text_t = new Text(chart, text);
var w = text_t.get_width(context); var w = text_t.width;
var h = text_t.get_height(context); var h = text_t.height;
var x0 = chart.plot_area_x_max - w - 5; var x0 = chart.plarea.x1 - w - 5;
var y0 = chart.plot_area_y_min + h + 5; var y0 = chart.plarea.y0 + h + 5;
chart.set_source_rgba(chart.legend.bg_color); chart.color = chart.legend.bg_color;
context.rectangle (x0, y0 - h, w, h); ctx.rectangle (x0, y0 - h, w, h);
context.fill(); ctx.fill();
context.move_to (x0, y0); ctx.move_to (x0, y0);
chart.set_source_rgba(chart.common_axis_color); chart.color = chart.joint_color;
context.show_text(text); ctx.show_text(text);
} }
return true;//ret; return true;//ret;
@ -450,11 +487,11 @@ int main (string[] args) {
switch (event.button) { switch (event.button) {
case 1: // start cursor position selection case 1: // start cursor position selection
if ((event.state & Gdk.ModifierType.SHIFT_MASK) != 0) { // remove cursor if ((event.state & Gdk.ModifierType.SHIFT_MASK) != 0) { // remove cursor
chart.set_active_cursor (event.x, event.y, true); chart.cursors.set_active (Point(event.x, event.y), true);
chart.remove_active_cursor(); chart.cursors.remove_active();
mouse_state = MouseState.FREE; mouse_state = MouseState.FREE;
} else { // add cursor } 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; mouse_state = MouseState.CURSOR_SELECTION;
} }
da.queue_draw_area(0, 0, da.get_allocated_width(), da.get_allocated_height()); da.queue_draw_area(0, 0, da.get_allocated_width(), da.get_allocated_height());
@ -482,11 +519,11 @@ int main (string[] args) {
switch (event.button) { switch (event.button) {
case 1: // start cursor position selection case 1: // start cursor position selection
if ((event.state & Gdk.ModifierType.SHIFT_MASK) != 0) { // remove cursor 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()); //da.queue_draw_area(0, 0, da.get_allocated_width(), da.get_allocated_height());
//mouse_state = MouseState.FREE; //mouse_state = MouseState.FREE;
} else { // add cursor } else { // add cursor
chart.add_active_cursor (); chart.cursors.add_active ();
mouse_state = MouseState.FREE; mouse_state = MouseState.FREE;
} }
break; break;
@ -495,7 +532,7 @@ int main (string[] args) {
sel_x1 = event.x; sel_x1 = event.x;
sel_y1 = event.y; sel_y1 = event.y;
if (sel_x1 > sel_x0 && sel_y1 > sel_y0) 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 else
chart.zoom_out (); chart.zoom_out ();
da.queue_draw_area(0, 0, da.get_allocated_width(), da.get_allocated_height()); da.queue_draw_area(0, 0, da.get_allocated_width(), da.get_allocated_height());
@ -521,25 +558,32 @@ int main (string[] args) {
case MouseState.MOVING_CHART: case MouseState.MOVING_CHART:
var delta_x = event.x - mov_x0, delta_y = event.y - mov_y0; 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_x0 = event.x;
mov_y0 = event.y; mov_y0 = event.y;
da.queue_draw_area(0, 0, da.get_allocated_width(), da.get_allocated_height()); da.queue_draw_area(0, 0, da.get_allocated_width(), da.get_allocated_height());
break; break;
case MouseState.CURSOR_SELECTION: 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()); da.queue_draw_area(0, 0, da.get_allocated_width(), da.get_allocated_height());
break; break;
} }
return true; // return ret; return true; // return ret;
}); });
da.add_events(Gdk.EventMask.SCROLL_MASK);
da.scroll_event.connect((event) => { da.scroll_event.connect((event) => {
switch (event.direction) {
case Gdk.ScrollDirection.UP:
chart.zoom_scroll_in(Point(event.x, event.y));
break;
case Gdk.ScrollDirection.DOWN:
chart.zoom_scroll_out(Point(event.x, event.y));
break;
}
da.queue_draw_area(0, 0, da.get_allocated_width(), da.get_allocated_height());
//var ret = chart.scroll_notify_event(event); //var ret = chart.scroll_notify_event(event);
return true; // return ret; return true; // return ret;
}); });