2018-01-08 23:33:13 +03:00
|
|
|
namespace CairoChart {
|
|
|
|
|
2017-08-28 14:47:31 +03:00
|
|
|
public class Legend {
|
2018-01-08 23:33:13 +03:00
|
|
|
|
2017-08-28 14:47:31 +03:00
|
|
|
public enum Position {
|
|
|
|
TOP = 0, // default
|
|
|
|
LEFT,
|
|
|
|
RIGHT,
|
|
|
|
BOTTOM
|
|
|
|
}
|
2018-01-08 23:33:13 +03:00
|
|
|
|
2017-08-28 14:47:31 +03:00
|
|
|
public Position position = Position.TOP;
|
2018-01-20 13:11:17 +03:00
|
|
|
public Font font_style = Font();
|
2017-08-28 14:47:31 +03:00
|
|
|
public Color bg_color = Color(1, 1, 1);
|
2018-01-20 13:11:17 +03:00
|
|
|
public LineStyle border_style = LineStyle ();
|
2018-01-19 10:52:06 +03:00
|
|
|
public double spacing = 5;
|
2018-01-08 23:33:13 +03:00
|
|
|
public double width = 0;
|
|
|
|
public double height = 0;
|
|
|
|
public double line_length = 30.0;
|
|
|
|
public double text_hspace = 10.0;
|
|
|
|
public double text_vspace = 2.0;
|
|
|
|
public bool show = true;
|
2017-08-28 14:47:31 +03:00
|
|
|
|
2018-01-15 12:05:21 +03:00
|
|
|
public virtual Legend copy () {
|
2017-08-28 18:16:48 +03:00
|
|
|
var legend = new Legend ();
|
|
|
|
legend.position = this.position;
|
|
|
|
legend.font_style = this.font_style;
|
|
|
|
legend.bg_color = this.bg_color;
|
2018-01-19 10:52:06 +03:00
|
|
|
legend.spacing = this.spacing;
|
2018-01-08 23:33:13 +03:00
|
|
|
legend.height = this.height;
|
|
|
|
legend.line_length = this.line_length;
|
|
|
|
legend.text_hspace = this.text_hspace;
|
|
|
|
legend.text_vspace = this.text_vspace;
|
|
|
|
legend.width = this.width;
|
|
|
|
legend.show = this.show;
|
|
|
|
legend.max_font_heights = this.max_font_heights;
|
2017-08-28 18:16:48 +03:00
|
|
|
return legend;
|
|
|
|
}
|
|
|
|
|
2017-08-28 14:47:31 +03:00
|
|
|
public Legend () {
|
|
|
|
border_style.color = Color (0, 0, 0, 0.3);
|
|
|
|
}
|
2018-01-08 23:33:13 +03:00
|
|
|
|
|
|
|
public virtual void draw (Chart chart) {
|
|
|
|
process (chart, ProcessType.CALC);
|
|
|
|
process (chart, ProcessType.DRAW);
|
|
|
|
}
|
|
|
|
|
|
|
|
public virtual void draw_rect (Chart chart, out double x0, out double y0) {
|
|
|
|
x0 = y0 = 0.0;
|
2018-01-17 10:36:10 +03:00
|
|
|
if (chart.ctx != null) {
|
2018-01-08 23:33:13 +03:00
|
|
|
switch (position) {
|
|
|
|
case Position.TOP:
|
2018-01-18 13:52:06 +03:00
|
|
|
x0 = (chart.area.width - width) / 2;
|
2018-01-18 12:55:20 +03:00
|
|
|
var title_height = chart.title.get_height(chart.ctx) + (chart.legend.position == Legend.Position.TOP ?
|
2018-01-19 11:09:33 +03:00
|
|
|
chart.title.vspacing * 2 : chart.title.vspacing);
|
2018-01-18 12:55:20 +03:00
|
|
|
y0 = title_height;
|
2018-01-08 23:33:13 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
case Position.BOTTOM:
|
2018-01-18 13:52:06 +03:00
|
|
|
x0 = (chart.area.width - width) / 2;
|
|
|
|
y0 = chart.area.height - height;
|
2018-01-08 23:33:13 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
case Position.LEFT:
|
|
|
|
x0 = 0;
|
2018-01-18 13:52:06 +03:00
|
|
|
y0 = (chart.area.height - height) / 2;
|
2018-01-08 23:33:13 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
case Position.RIGHT:
|
2018-01-18 13:52:06 +03:00
|
|
|
x0 = chart.area.width - width;
|
|
|
|
y0 = (chart.area.height - height) / 2;
|
2018-01-08 23:33:13 +03:00
|
|
|
break;
|
|
|
|
}
|
2018-01-16 20:39:59 +03:00
|
|
|
chart.color = bg_color;
|
2018-01-17 10:36:10 +03:00
|
|
|
chart.ctx.rectangle (x0, y0, width, height);
|
|
|
|
chart.ctx.fill();
|
2018-01-19 11:36:33 +03:00
|
|
|
border_style.apply(chart);
|
2018-01-17 10:36:10 +03:00
|
|
|
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 ();
|
2018-01-08 23:33:13 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public enum ProcessType {
|
|
|
|
CALC = 0, // default
|
|
|
|
DRAW
|
|
|
|
}
|
|
|
|
|
|
|
|
double [] max_font_heights;
|
|
|
|
public virtual void process (Chart chart, 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;
|
|
|
|
double max_font_h = 0.0;
|
|
|
|
|
|
|
|
// prepare
|
|
|
|
switch (process_type) {
|
|
|
|
case ProcessType.CALC:
|
|
|
|
width = 0.0;
|
|
|
|
height = 0.0;
|
|
|
|
max_font_heights = {};
|
|
|
|
heights_idx = 0;
|
|
|
|
break;
|
|
|
|
case ProcessType.DRAW:
|
|
|
|
draw_rect(chart, out legend_x0, out legend_y0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach (var s in chart.series) {
|
|
|
|
|
|
|
|
if (!s.zoom_show) continue;
|
|
|
|
|
2018-01-17 10:36:10 +03:00
|
|
|
var title_sz = s.title.get_size(chart.ctx);
|
2018-01-08 23:33:13 +03:00
|
|
|
|
|
|
|
// carry
|
|
|
|
switch (position) {
|
|
|
|
case Position.TOP:
|
|
|
|
case Position.BOTTOM:
|
|
|
|
var ser_title_width = title_sz.width + line_length;
|
2018-01-18 13:52:06 +03:00
|
|
|
if (leg_width_sum + (leg_width_sum == 0 ? 0 : text_hspace) + ser_title_width > chart.area.width) { // carry
|
2018-01-08 23:33:13 +03:00
|
|
|
leg_height_sum += max_font_h;
|
|
|
|
switch (process_type) {
|
|
|
|
case ProcessType.CALC:
|
|
|
|
max_font_heights += max_font_h;
|
|
|
|
width = double.max(width, leg_width_sum);
|
|
|
|
break;
|
|
|
|
case ProcessType.DRAW:
|
|
|
|
heights_idx++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
leg_width_sum = 0.0;
|
|
|
|
max_font_h = 0;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (process_type) {
|
|
|
|
case ProcessType.DRAW:
|
|
|
|
var x = legend_x0 + leg_width_sum + (leg_width_sum == 0.0 ? 0.0 : text_hspace);
|
|
|
|
var y = legend_y0 + leg_height_sum + max_font_heights[heights_idx];
|
|
|
|
|
|
|
|
// series title
|
2018-01-17 10:36:10 +03:00
|
|
|
chart.ctx.move_to (x + line_length, y);
|
2018-01-16 20:39:59 +03:00
|
|
|
chart.color = s.title.color;
|
2018-01-17 10:36:10 +03:00
|
|
|
s.title.show(chart.ctx);
|
2018-01-08 23:33:13 +03:00
|
|
|
|
|
|
|
// series line style
|
2018-01-17 10:36:10 +03:00
|
|
|
chart.ctx.move_to (x, y - title_sz.height / 2);
|
2018-01-19 11:36:33 +03:00
|
|
|
s.line_style.apply(chart);
|
2018-01-17 10:36:10 +03:00
|
|
|
chart.ctx.rel_line_to (line_length, 0);
|
|
|
|
chart.ctx.stroke();
|
2018-01-10 12:58:35 +03:00
|
|
|
s.marker.draw_at_pos (chart, x + line_length / 2, y - title_sz.height / 2);
|
2018-01-08 23:33:13 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (position) {
|
|
|
|
case Position.TOP:
|
|
|
|
case Position.BOTTOM:
|
|
|
|
var ser_title_width = title_sz.width + line_length;
|
|
|
|
leg_width_sum += (leg_width_sum == 0 ? 0 : text_hspace) + ser_title_width;
|
|
|
|
max_font_h = double.max (max_font_h, title_sz.height) + (leg_height_sum != 0 ? text_vspace : 0);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Position.LEFT:
|
|
|
|
case Position.RIGHT:
|
|
|
|
switch (process_type) {
|
|
|
|
case ProcessType.CALC:
|
|
|
|
max_font_heights += title_sz.height + (leg_height_sum != 0 ? text_vspace : 0);
|
|
|
|
width = double.max (width, title_sz.width + line_length);
|
|
|
|
break;
|
|
|
|
case ProcessType.DRAW:
|
|
|
|
heights_idx++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
leg_height_sum += title_sz.height + (leg_height_sum != 0 ? text_vspace : 0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// TOP, BOTTOM
|
|
|
|
switch (position) {
|
|
|
|
case Position.TOP:
|
|
|
|
case Position.BOTTOM:
|
|
|
|
if (leg_width_sum != 0) {
|
|
|
|
leg_height_sum += max_font_h;
|
|
|
|
switch (process_type) {
|
|
|
|
case ProcessType.CALC:
|
|
|
|
max_font_heights += max_font_h;
|
|
|
|
width = double.max(width, leg_width_sum);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (process_type) {
|
|
|
|
case ProcessType.CALC:
|
|
|
|
height = leg_height_sum;
|
|
|
|
switch (position) {
|
2018-01-20 20:07:06 +03:00
|
|
|
case Position.TOP: chart.evarea.y0 += height; break;
|
|
|
|
case Position.BOTTOM: chart.evarea.y1 -= height; break;
|
|
|
|
case Position.LEFT: chart.evarea.x0 += width; break;
|
|
|
|
case Position.RIGHT: chart.evarea.x1 -= width; break;
|
2018-01-08 23:33:13 +03:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2017-08-28 14:47:31 +03:00
|
|
|
}
|
|
|
|
}
|