gtk / cairo-chart

SSH Git

To clone this repository:

git clone git@git.backbone.ws:gtk/cairo-chart.git

To push to this repository:

# Add a new remote
git remote add origin git@git.backbone.ws:gtk/cairo-chart.git

# Push the master branch to the newly added origin, and configure
# this remote and branch as the default:
git push -u origin master

# From now on you can push master to the "origin" remote with:
git push

Diffs from 82aa857 to 823c520

Commits

avatar Kolan Sh Fixes #139: Cut lines when they are outside of the chart grid. 002e995 12 days ago
avatar Kolan Sh Merge branch '#139_cut_lines' into develop ec0ed49 12 days ago
avatar Kolan Sh Feature #130: Zoom is in progress... 08f2f01 12 days ago
avatar Kolan Sh Feature #130: Zoom is in progress... 823c520 12 days ago

Summary

  • src/Axis.vala (16) --++++++++++++++
  • src/Chart.vala (250) ----------------------------------------------------------------------++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  • src/Place.vala (19) -----++++++++++++++
  • src/Series.vala (11) --+++++++++
  • test/ChartTest.vala (142) ------------------------------------------++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 3 // then draw separate axis for each/all series
4 4 // or specify series name near the axis
5 5 public class Axis {
6 public Float128 min = 0;
7 public Float128 max = 1;
6 Float128 _min = 0;
7 Float128 _max = 0;
8 public Float128 min {
9 get { return _min; }
10 set { _min = zoom_min = value; }
11 default = 0;
12 }
13 public Float128 max {
14 get { return _max; }
15 set { _max = zoom_max = value; }
16 default = 1;
17 }
18 public Float128 zoom_min = 0;
19 public Float128 zoom_max = 1;
8 20 public Text title = new Text ("");
9 21 public enum Type {
10 22 NUMBERS = 0,
91 91 }
92 92 }
93 93
94 public virtual void zoom_in (double x0, double y0, double x1, double y1) {
95 foreach (var s in series) {
96 s.axis_x.zoom_min = 0;
97 s.axis_x.zoom_max = 1;
98 s.axis_y.zoom_min = 0;
99 s.axis_y.zoom_max = 1;
100 s.place.x_low = 0;
101 s.place.x_high = 1;
102 s.place.y_low = 0;
103 s.place.y_high = 1;
104 }
105 }
106
107 public virtual void zoom_out () {
108 foreach (var s in series) {
109 s.axis_x.zoom_min = s.axis_x.min;
110 s.axis_x.zoom_max = s.axis_x.max;
111 s.axis_y.zoom_min = s.axis_y.min;
112 s.axis_y.zoom_max = s.axis_y.max;
113 s.zoom_place.x_low = s.place.x_low;
114 s.zoom_place.x_high = s.place.x_high;
115 s.zoom_place.y_low = s.place.y_low;
116 s.zoom_place.y_high = s.place.y_high;
117 }
118 }
119
94 120 protected double title_width = 0.0;
95 121 protected double title_height = 0.0;
96 122
389 389 protected virtual void calc_axis_rec_sizes (Axis axis, out double max_rec_width, out double max_rec_height, bool is_horizontal = true) {
390 390 max_rec_width = max_rec_height = 0;
391 391 for (var i = 0; i < axis_rec_npoints; ++i) {
392 Float128 x = axis.min + (axis.max - axis.min) / axis_rec_npoints * i;
392 Float128 x = axis.zoom_min + (axis.zoom_max - axis.zoom_min) / axis_rec_npoints * i;
393 393 switch (axis.type) {
394 394 case Axis.Type.NUMBERS:
395 395 var text = new Text (axis.format.printf((LongDouble)x) + (is_horizontal ? "_" : ""));
475 475 for (int si = series.length - 1; si >=0; --si) {
476 476 var s = series[si];
477 477 if ( s.axis_x.position != series[0].axis_x.position
478 || s.axis_x.min != series[0].axis_x.min
479 || s.axis_x.max != series[0].axis_x.max
480 || s.place.x_low != series[0].place.x_low
481 || s.place.x_high != series[0].place.x_high
478 || s.axis_x.zoom_min != series[0].axis_x.zoom_min
479 || s.axis_x.zoom_max != series[0].axis_x.zoom_max
480 || s.zoom_place.x_low != series[0].zoom_place.x_low
481 || s.zoom_place.x_high != series[0].zoom_place.x_high
482 482 || s.axis_x.type != series[0].axis_x.type)
483 483 common_x_axes = false;
484 484 if ( s.axis_y.position != series[0].axis_y.position
485 || s.axis_y.min != series[0].axis_y.min
486 || s.axis_y.max != series[0].axis_y.max
487 || s.place.y_low != series[0].place.y_low
488 || s.place.y_high != series[0].place.y_high)
485 || s.axis_y.zoom_min != series[0].axis_y.zoom_min
486 || s.axis_y.zoom_max != series[0].axis_y.zoom_max
487 || s.zoom_place.y_low != series[0].zoom_place.y_low
488 || s.zoom_place.y_high != series[0].zoom_place.y_high)
489 489 common_y_axes = false;
490 490 }
491 491 if (series.length == 1) common_x_axes = common_y_axes = false;
505 505 bool has_intersection = false;
506 506 for (int sk = si; sk > sj; --sk) {
507 507 var s3 = series[sk];
508 if (are_intersect(s2.place.x_low, s2.place.x_high, s3.place.x_low, s3.place.x_high)
508 if (are_intersect(s2.zoom_place.x_low, s2.zoom_place.x_high, s3.zoom_place.x_low, s3.zoom_place.x_high)
509 509 || s2.axis_x.position != s3.axis_x.position
510 510 || s2.axis_x.type != s3.axis_x.type) {
511 511 has_intersection = true;
550 550 bool has_intersection = false;
551 551 for (int sk = si; sk > sj; --sk) {
552 552 var s3 = series[sk];
553 if (are_intersect(s2.place.y_low, s2.place.y_high, s3.place.y_low, s3.place.y_high)
553 if (are_intersect(s2.zoom_place.y_low, s2.zoom_place.y_high, s3.zoom_place.y_low, s3.zoom_place.y_high)
554 554 || s2.axis_y.position != s3.axis_y.position
555 555 || s2.axis_x.type != s3.axis_x.type) {
556 556 has_intersection = true;
596 596 calc_axis_rec_sizes (s.axis_x, out max_rec_width, out max_rec_height, true);
597 597
598 598 // 2. Calculate maximal available number of records, take into account the space width.
599 long max_nrecs = (long) ((plot_area_x_max - plot_area_x_min) * (s.place.x_high - s.place.x_low) / max_rec_width);
599 long max_nrecs = (long) ((plot_area_x_max - plot_area_x_min) * (s.zoom_place.x_high - s.zoom_place.x_low) / max_rec_width);
600 600
601 601 // 3. Calculate grid step.
602 Float128 step = calc_round_step ((s.axis_x.max - s.axis_x.min) / max_nrecs, s.axis_x.type == Axis.Type.DATE_TIME);
603 if (step > s.axis_x.max - s.axis_x.min)
604 step = s.axis_x.max - s.axis_x.min;
602 Float128 step = calc_round_step ((s.axis_x.zoom_max - s.axis_x.zoom_min) / max_nrecs, s.axis_x.type == Axis.Type.DATE_TIME);
603 if (step > s.axis_x.zoom_max - s.axis_x.zoom_min)
604 step = s.axis_x.zoom_max - s.axis_x.zoom_min;
605 605
606 // 4. Calculate x_min (s.axis_x.min / step, round, multiply on step, add step if < s.axis_x.min).
606 // 4. Calculate x_min (s.axis_x.zoom_min / step, round, multiply on step, add step if < s.axis_x.zoom_min).
607 607 Float128 x_min = 0.0;
608 608 if (step >= 1) {
609 int64 x_min_nsteps = (int64) (s.axis_x.min / step);
609 int64 x_min_nsteps = (int64) (s.axis_x.zoom_min / step);
610 610 x_min = x_min_nsteps * step;
611 611 } else {
612 int64 round_axis_x_min = (int64)s.axis_x.min;
613 int64 x_min_nsteps = (int64) ((s.axis_x.min - round_axis_x_min) / step);
612 int64 round_axis_x_min = (int64)s.axis_x.zoom_min;
613 int64 x_min_nsteps = (int64) ((s.axis_x.zoom_min - round_axis_x_min) / step);
614 614 x_min = round_axis_x_min + x_min_nsteps * step;
615 615 }
616 if (x_min < s.axis_x.min) x_min += step;
616 if (x_min < s.axis_x.zoom_min) x_min += step;
617 617
618 618 // 4.5. Draw Axis title
619 619 if (s.axis_x.title.text != "")
620 620 switch (s.axis_x.position) {
621 621 case Axis.Position.LOW:
622 var scr_x = plot_area_x_min + (plot_area_x_max - plot_area_x_min) * (s.place.x_low + s.place.x_high) / 2.0;
622 var scr_x = plot_area_x_min + (plot_area_x_max - plot_area_x_min) * (s.zoom_place.x_low + s.zoom_place.x_high) / 2.0;
623 623 var scr_y = cur_y_max - s.axis_x.font_indent;
624 624 context.move_to(scr_x - s.axis_x.title.get_width(context) / 2.0, scr_y);
625 625 set_source_rgba(s.axis_x.color);
627 627 show_text(s.axis_x.title);
628 628 break;
629 629 case Axis.Position.HIGH:
630 var scr_x = plot_area_x_min + (plot_area_x_max - plot_area_x_min) * (s.place.x_low + s.place.x_high) / 2.0;
630 var scr_x = plot_area_x_min + (plot_area_x_max - plot_area_x_min) * (s.zoom_place.x_low + s.zoom_place.x_high) / 2.0;
631 631 var scr_y = cur_y_min + s.axis_x.font_indent + s.axis_x.title.get_height(context);
632 632 context.move_to(scr_x - s.axis_x.title.get_width(context) / 2.0, scr_y);
633 633 set_source_rgba(s.axis_x.color);
639 639 }
640 640
641 641 // 5. Draw records, update cur_{x,y}_{min,max}.
642 for (Float128 x = x_min, x_max = s.axis_x.max; point_belong (x, x_min, x_max); x += step) {
642 for (Float128 x = x_min, x_max = s.axis_x.zoom_max; point_belong (x, x_min, x_max); x += step) {
643 643 if (common_x_axes) set_source_rgba(Color(0,0,0,1));
644 644 else set_source_rgba(s.axis_x.color);
645 645 string text = "", time_text = "";
658 658 break;
659 659 }
660 660 var scr_x = plot_area_x_min + (plot_area_x_max - plot_area_x_min)
661 * (s.place.x_low + (s.place.x_high - s.place.x_low) / (s.axis_x.max - s.axis_x.min) * (x - s.axis_x.min));
661 * (s.zoom_place.x_low + (s.zoom_place.x_high - s.zoom_place.x_low) / (s.axis_x.zoom_max - s.axis_x.zoom_min) * (x - s.axis_x.zoom_min));
662 662 var text_t = new Text(text, s.axis_x.font_style, s.axis_x.color);
663 663 switch (s.axis_x.position) {
664 664 case Axis.Position.LOW:
666 666 switch (s.axis_x.type) {
667 667 case Axis.Type.NUMBERS:
668 668 var print_x = scr_x - text_t.get_width(context) / 2.0 - text_t.get_x_bearing(context)
669 - text_t.get_width(context) * (x - (s.axis_x.min + s.axis_x.max) / 2.0) / (s.axis_x.max - s.axis_x.min);
669 - text_t.get_width(context) * (x - (s.axis_x.zoom_min + s.axis_x.zoom_max) / 2.0) / (s.axis_x.zoom_max - s.axis_x.zoom_min);
670 670 context.move_to (print_x, print_y);
671 671 show_text(text_t);
672 672 break;
673 673 case Axis.Type.DATE_TIME:
674 674 var print_x = scr_x - text_t.get_width(context) / 2.0 - text_t.get_x_bearing(context)
675 - text_t.get_width(context) * (x - (s.axis_x.min + s.axis_x.max) / 2.0) / (s.axis_x.max - s.axis_x.min);
675 - text_t.get_width(context) * (x - (s.axis_x.zoom_min + s.axis_x.zoom_max) / 2.0) / (s.axis_x.zoom_max - s.axis_x.zoom_min);
676 676 context.move_to (print_x, print_y);
677 677 if (s.axis_x.date_format != "") show_text(text_t);
678 678 var time_text_t = new Text(time_text, s.axis_x.font_style, s.axis_x.color);
679 679 print_x = scr_x - time_text_t.get_width(context) / 2.0 - time_text_t.get_x_bearing(context)
680 - time_text_t.get_width(context) * (x - (s.axis_x.min + s.axis_x.max) / 2.0) / (s.axis_x.max - s.axis_x.min);
680 - time_text_t.get_width(context) * (x - (s.axis_x.zoom_min + s.axis_x.zoom_max) / 2.0) / (s.axis_x.zoom_max - s.axis_x.zoom_min);
681 681 context.move_to (print_x, print_y - (s.axis_x.date_format == "" ? 0 : text_t.get_height(context) + s.axis_x.font_indent));
682 682 if (s.axis_x.time_format != "") show_text(time_text_t);
683 683 break;
684 684 default:
685 685 break;
686 686 }
687 // 6. Draw grid lines to the s.place.y_high.
687 // 6. Draw grid lines to the s.zoom_place.y_high.
688 688 var line_style = s.grid.line_style;
689 689 if (common_x_axes) line_style.color = Color(0, 0, 0, 0.5);
690 690 set_line_style(line_style);
693 693 if (common_x_axes)
694 694 context.line_to (scr_x, plot_area_y_min);
695 695 else
696 context.line_to (scr_x, double.min (y, plot_area_y_max - (plot_area_y_max - plot_area_y_min) * s.place.y_high));
696 context.line_to (scr_x, double.min (y, plot_area_y_max - (plot_area_y_max - plot_area_y_min) * s.zoom_place.y_high));
697 697 break;
698 698 case Axis.Position.HIGH:
699 699 var print_y = cur_y_min + max_rec_height + s.axis_x.font_indent + (s.axis_x.title.text == "" ? 0 : s.axis_x.title.get_height(context) + s.axis_x.font_indent);
700 700 switch (s.axis_x.type) {
701 701 case Axis.Type.NUMBERS:
702 702 var print_x = scr_x - text_t.get_width(context) / 2.0 - text_t.get_x_bearing(context)
703 - text_t.get_width(context) * (x - (s.axis_x.min + s.axis_x.max) / 2.0) / (s.axis_x.max - s.axis_x.min);
703 - text_t.get_width(context) * (x - (s.axis_x.zoom_min + s.axis_x.zoom_max) / 2.0) / (s.axis_x.zoom_max - s.axis_x.zoom_min);
704 704 context.move_to (print_x, print_y);
705 705 show_text(text_t);
706 706 break;
707 707 case Axis.Type.DATE_TIME:
708 708 var print_x = scr_x - text_t.get_width(context) / 2.0 - text_t.get_x_bearing(context)
709 - text_t.get_width(context) * (x - (s.axis_x.min + s.axis_x.max) / 2.0) / (s.axis_x.max - s.axis_x.min);
709 - text_t.get_width(context) * (x - (s.axis_x.zoom_min + s.axis_x.zoom_max) / 2.0) / (s.axis_x.zoom_max - s.axis_x.zoom_min);
710 710 context.move_to (print_x, print_y);
711 711 if (s.axis_x.date_format != "") show_text(text_t);
712 712 var time_text_t = new Text(time_text, s.axis_x.font_style, s.axis_x.color);
713 713 print_x = scr_x - time_text_t.get_width(context) / 2.0 - time_text_t.get_x_bearing(context)
714 - time_text_t.get_width(context) * (x - (s.axis_x.min + s.axis_x.max) / 2.0) / (s.axis_x.max - s.axis_x.min);
714 - time_text_t.get_width(context) * (x - (s.axis_x.zoom_min + s.axis_x.zoom_max) / 2.0) / (s.axis_x.zoom_max - s.axis_x.zoom_min);
715 715 context.move_to (print_x, print_y - (s.axis_x.date_format == "" ? 0 : text_t.get_height(context) + s.axis_x.font_indent));
716 716 if (s.axis_x.time_format != "") show_text(time_text_t);
717 717 break;
718 718 default:
719 719 break;
720 720 }
721 // 6. Draw grid lines to the s.place.y_high.
721 // 6. Draw grid lines to the s.zoom_place.y_high.
722 722 var line_style = s.grid.line_style;
723 723 if (common_x_axes) line_style.color = Color(0, 0, 0, 0.5);
724 724 set_line_style(line_style);
727 727 if (common_x_axes)
728 728 context.line_to (scr_x, plot_area_y_max);
729 729 else
730 context.line_to (scr_x, double.max (y, plot_area_y_max - (plot_area_y_max - plot_area_y_min) * s.place.y_low));
730 context.line_to (scr_x, double.max (y, plot_area_y_max - (plot_area_y_max - plot_area_y_min) * s.zoom_place.y_low));
731 731 break;
732 732 case Axis.Position.BOTH:
733 733 break;
743 743 bool has_intersection = false;
744 744 for (int sk = si; sk > sj; --sk) {
745 745 var s3 = series[sk];
746 if (are_intersect(s2.place.x_low, s2.place.x_high, s3.place.x_low, s3.place.x_high)
746 if (are_intersect(s2.zoom_place.x_low, s2.zoom_place.x_high, s3.zoom_place.x_low, s3.zoom_place.x_high)
747 747 || s2.axis_x.position != s3.axis_x.position
748 748 || s2.axis_x.type != s3.axis_x.type) {
749 749 has_intersection = true;
784 784 calc_axis_rec_sizes (s.axis_y, out max_rec_width, out max_rec_height, false);
785 785
786 786 // 2. Calculate maximal available number of records, take into account the space width.
787 long max_nrecs = (long) ((plot_area_y_max - plot_area_y_min) * (s.place.y_high - s.place.y_low) / max_rec_height);
787 long max_nrecs = (long) ((plot_area_y_max - plot_area_y_min) * (s.zoom_place.y_high - s.zoom_place.y_low) / max_rec_height);
788 788
789 789 // 3. Calculate grid step.
790 Float128 step = calc_round_step ((s.axis_y.max - s.axis_y.min) / max_nrecs);
791 if (step > s.axis_y.max - s.axis_y.min)
792 step = s.axis_y.max - s.axis_y.min;
790 Float128 step = calc_round_step ((s.axis_y.zoom_max - s.axis_y.zoom_min) / max_nrecs);
791 if (step > s.axis_y.zoom_max - s.axis_y.zoom_min)
792 step = s.axis_y.zoom_max - s.axis_y.zoom_min;
793 793
794 // 4. Calculate y_min (s.axis_y.min / step, round, multiply on step, add step if < s.axis_y.min).
794 // 4. Calculate y_min (s.axis_y.zoom_min / step, round, multiply on step, add step if < s.axis_y.zoom_min).
795 795 Float128 y_min = 0.0;
796 796 if (step >= 1) {
797 int64 y_min_nsteps = (int64) (s.axis_y.min / step);
797 int64 y_min_nsteps = (int64) (s.axis_y.zoom_min / step);
798 798 y_min = y_min_nsteps * step;
799 799 } else {
800 int64 round_axis_y_min = (int64)s.axis_y.min;
801 int64 y_min_nsteps = (int64) ((s.axis_y.min - round_axis_y_min) / step);
800 int64 round_axis_y_min = (int64)s.axis_y.zoom_min;
801 int64 y_min_nsteps = (int64) ((s.axis_y.zoom_min - round_axis_y_min) / step);
802 802 y_min = round_axis_y_min + y_min_nsteps * step;
803 803 }
804 if (y_min < s.axis_y.min) y_min += step;
804 if (y_min < s.axis_y.zoom_min) y_min += step;
805 805
806 806 // 4.5. Draw Axis title
807 807 if (s.axis_y.title.text != "")
808 808 switch (s.axis_y.position) {
809 809 case Axis.Position.LOW:
810 var scr_y = plot_area_y_max - (plot_area_y_max - plot_area_y_min) * (s.place.y_low + s.place.y_high) / 2.0;
810 var scr_y = plot_area_y_max - (plot_area_y_max - plot_area_y_min) * (s.zoom_place.y_low + s.zoom_place.y_high) / 2.0;
811 811 var scr_x = cur_x_min + s.axis_y.font_indent + s.axis_y.title.get_width(context);
812 812 context.move_to(scr_x, scr_y + s.axis_y.title.get_height(context) / 2.0);
813 813 set_source_rgba(s.axis_y.color);
815 815 show_text(s.axis_y.title);
816 816 break;
817 817 case Axis.Position.HIGH:
818 var scr_y = plot_area_y_max - (plot_area_y_max - plot_area_y_min) * (s.place.y_low + s.place.y_high) / 2.0;
818 var scr_y = plot_area_y_max - (plot_area_y_max - plot_area_y_min) * (s.zoom_place.y_low + s.zoom_place.y_high) / 2.0;
819 819 var scr_x = cur_x_max - s.axis_y.font_indent;
820 820 context.move_to(scr_x, scr_y + s.axis_y.title.get_height(context) / 2.0);
821 821 set_source_rgba(s.axis_y.color);
827 827 }
828 828
829 829 // 5. Draw records, update cur_{x,y}_{min,max}.
830 for (Float128 y = y_min, y_max = s.axis_y.max; point_belong (y, y_min, y_max); y += step) {
830 for (Float128 y = y_min, y_max = s.axis_y.zoom_max; point_belong (y, y_min, y_max); y += step) {
831 831 if (common_y_axes) set_source_rgba(Color(0,0,0,1));
832 832 else set_source_rgba(s.axis_y.color);
833 833 var text = s.axis_y.format.printf((LongDouble)y);
834 834 var scr_y = plot_area_y_max - (plot_area_y_max - plot_area_y_min)
835 * (s.place.y_low + (s.place.y_high - s.place.y_low) / (s.axis_y.max - s.axis_y.min) * (y - s.axis_y.min));
835 * (s.zoom_place.y_low + (s.zoom_place.y_high - s.zoom_place.y_low) / (s.axis_y.zoom_max - s.axis_y.zoom_min) * (y - s.axis_y.zoom_min));
836 836 var text_t = new Text(text, s.axis_y.font_style, s.axis_y.color);
837 837 switch (s.axis_y.position) {
838 838 case Axis.Position.LOW:
839 839 context.move_to (cur_x_min + max_rec_width - (new Text(text)).get_width(context) + s.axis_y.font_indent - text_t.get_x_bearing(context)
840 840 + (s.axis_y.title.text == "" ? 0 : s.axis_y.title.get_width(context) + s.axis_y.font_indent),
841 841 scr_y + (new Text(text)).get_height(context) / 2.0
842 + text_t.get_height(context) * (y - (s.axis_y.min + s.axis_y.max) / 2.0) / (s.axis_y.max - s.axis_y.min));
842 + text_t.get_height(context) * (y - (s.axis_y.zoom_min + s.axis_y.zoom_max) / 2.0) / (s.axis_y.zoom_max - s.axis_y.zoom_min));
843 843 show_text(text_t);
844 // 6. Draw grid lines to the s.place.y_high.
844 // 6. Draw grid lines to the s.zoom_place.y_high.
845 845 var line_style = s.grid.line_style;
846 846 if (common_y_axes) line_style.color = Color(0, 0, 0, 0.5);
847 847 set_line_style(line_style);
850 850 if (common_y_axes)
851 851 context.line_to (plot_area_x_max, scr_y);
852 852 else
853 context.line_to (double.max (x, plot_area_x_min + (plot_area_x_max - plot_area_x_min) * s.place.x_high), scr_y);
853 context.line_to (double.max (x, plot_area_x_min + (plot_area_x_max - plot_area_x_min) * s.zoom_place.x_high), scr_y);
854 854 break;
855 855 case Axis.Position.HIGH:
856 856 context.move_to (cur_x_max - (new Text(text)).get_width(context) - s.axis_y.font_indent - text_t.get_x_bearing(context)
857 857 - (s.axis_y.title.text == "" ? 0 : s.axis_y.title.get_width(context) + s.axis_y.font_indent),
858 858 scr_y + (new Text(text)).get_height(context) / 2.0
859 + text_t.get_height(context) * (y - (s.axis_y.min + s.axis_y.max) / 2.0) / (s.axis_y.max - s.axis_y.min));
859 + text_t.get_height(context) * (y - (s.axis_y.zoom_min + s.axis_y.zoom_max) / 2.0) / (s.axis_y.zoom_max - s.axis_y.zoom_min));
860 860 show_text(text_t);
861 // 6. Draw grid lines to the s.place.y_high.
861 // 6. Draw grid lines to the s.zoom_place.y_high.
862 862 var line_style = s.grid.line_style;
863 863 if (common_y_axes) line_style.color = Color(0, 0, 0, 0.5);
864 864 set_line_style(line_style);
867 867 if (common_y_axes)
868 868 context.line_to (plot_area_x_min, scr_y);
869 869 else
870 context.line_to (double.min (x, plot_area_x_min + (plot_area_x_max - plot_area_x_min) * s.place.x_low), scr_y);
870 context.line_to (double.min (x, plot_area_x_min + (plot_area_x_max - plot_area_x_min) * s.zoom_place.x_low), scr_y);
871 871 break;
872 872 case Axis.Position.BOTH:
873 873 break;
883 883 bool has_intersection = false;
884 884 for (int sk = si; sk > sj; --sk) {
885 885 var s3 = series[sk];
886 if (are_intersect(s2.place.y_low, s2.place.y_high, s3.place.y_low, s3.place.y_high)
886 if (are_intersect(s2.zoom_place.y_low, s2.zoom_place.y_high, s3.zoom_place.y_low, s3.zoom_place.y_high)
887 887 || s2.axis_y.position != s3.axis_y.position) {
888 888 has_intersection = true;
889 889 break;
925 925 }
926 926
927 927 protected virtual double get_scr_x (Series s, Float128 x) {
928 return plot_area_x_min + (plot_area_x_max - plot_area_x_min) * (s.place.x_low + (x - s.axis_x.min)
929 / (s.axis_x.max - s.axis_x.min) * (s.place.x_high - s.place.x_low));
928 return plot_area_x_min + (plot_area_x_max - plot_area_x_min) * (s.zoom_place.x_low + (x - s.axis_x.zoom_min)
929 / (s.axis_x.zoom_max - s.axis_x.zoom_min) * (s.zoom_place.x_high - s.zoom_place.x_low));
930 930 }
931 931
932 932 protected virtual double get_scr_y (Series s, Float128 y) {
933 return plot_area_y_max - (plot_area_y_max - plot_area_y_min) * (s.place.y_low + (y - s.axis_y.min)
934 / (s.axis_y.max - s.axis_y.min) * (s.place.y_high - s.place.y_low));
933 return plot_area_y_max - (plot_area_y_max - plot_area_y_min) * (s.zoom_place.y_low + (y - s.axis_y.zoom_min)
934 / (s.axis_y.zoom_max - s.axis_y.zoom_min) * (s.zoom_place.y_high - s.zoom_place.y_low));
935 935 }
936 936
937 protected virtual Float128 get_real_x (Series s, double scr_x) {
938 return s.axis_x.zoom_min + ((scr_x - plot_area_x_min) / (plot_area_x_max - plot_area_x_min) - s.zoom_place.x_low)
939 * (s.axis_x.zoom_max - s.axis_x.zoom_min) / (s.zoom_place.x_high - s.zoom_place.x_low);
940 }
941
942 protected virtual Float128 get_real_y (Series s, double scr_y) {
943 return s.axis_y.zoom_min + ((plot_area_y_max - scr_y) / (plot_area_y_max - plot_area_y_min) - s.zoom_place.y_low)
944 * (s.axis_y.zoom_max - s.axis_y.zoom_min) / (s.zoom_place.y_high - s.zoom_place.y_low);
945 }
946
947 protected virtual bool point_in_rect (Point p, double x0, double x1, double y0, double y1) {
948 if ( (x0 <= p.x <= x1 || x1 <= p.x <= x0)
949 && (y0 <= p.y <= y1 || y1 <= p.y <= y0))
950 return true;
951 return false;
952 }
953
954 protected virtual bool point_in_plot_area (Point p) {
955 if (point_in_rect (p, plot_area_x_min, plot_area_x_max, plot_area_y_min, plot_area_y_max))
956 return true;
957 return false;
958 }
959
960 protected virtual bool hcross (Point a1, Point a2, Float128 h_x1, Float128 h_x2, Float128 h_y, out Float128 x) {
961 x = 0;
962 if (a1.y == a2.y) return false;
963 if (a1.y >= h_y && a2.y >= h_y || a1.y <= h_y && a2.y <= h_y) return false;
964 x = a1.x + (a2.x - a1.x) * (h_y - a1.y) / (a2.y - a1.y);
965 if (h_x1 <= x <= h_x2 || h_x2 <= x <= h_x1)
966 return true;
967 return false;
968 }
969
970 protected virtual bool vcross (Point a1, Point a2, Float128 v_x, Float128 v_y1, Float128 v_y2, out Float128 y) {
971 y = 0;
972 if (a1.x == a2.x) return false;
973 if (a1.x >= v_x && a2.x >= v_x || a1.x <= v_x && a2.x <= v_x) return false;
974 y = a1.y + (a2.y - a1.y) * (v_x - a1.x) / (a2.x - a1.x);
975 if (v_y1 <= y <= v_y2 || v_y2 <= y <= v_y1)
976 return true;
977 return false;
978 }
979
937 980 delegate int PointComparator(Point a, Point b);
938 981 void sort_points(Point[] points, PointComparator compare) {
939 982 for(var i = 0; i < points.length; ++i) {
990 990 }
991 991 }
992 992
993 protected virtual bool cut_line (Point a, Point b, out Point c, out Point d) {
994 int ncross = 0;
995 Float128 x = 0, y = 0;
996 Point pc[4];
997 if (hcross(a, b, plot_area_x_min, plot_area_x_max, plot_area_y_min, out x))
998 pc[ncross++] = Point(x, plot_area_y_min);
999 if (hcross(a, b, plot_area_x_min, plot_area_x_max, plot_area_y_max, out x))
1000 pc[ncross++] = Point(x, plot_area_y_max);
1001 if (vcross(a, b, plot_area_x_min, plot_area_y_min, plot_area_y_max, out y))
1002 pc[ncross++] = Point(plot_area_x_min, y);
1003 if (vcross(a, b, plot_area_x_max, plot_area_y_min, plot_area_y_max, out y))
1004 pc[ncross++] = Point(plot_area_x_max, y);
1005 c = a;
1006 d = b;
1007 if (ncross == 0) {
1008 if (point_in_plot_area (a) && point_in_plot_area (b))
1009 return true;
1010 return false;
1011 }
1012 if (ncross >= 2) {
1013 c = pc[0]; d = pc[1];
1014 return true;
1015 }
1016 if (ncross == 1) {
1017 if (point_in_plot_area (a)) {
1018 c = a;
1019 d = pc[0];
1020 return true;
1021 } else if (point_in_plot_area (b)) {
1022 c = b;
1023 d = pc[0];
1024 return true;
1025 }
1026 }
1027 return false;
1028 }
1029
993 1030 protected virtual void draw_series () {
994 1031 for (int si = 0; si < series.length; ++si) {
995 1032 var s = series[si];
1049 1049 break;
1050 1050 }
1051 1051 set_line_style(s.line_style);
1052 // move to s.points[0]
1053 context.move_to (get_scr_x(s, points[0].x), get_scr_y(s, points[0].y));
1054 1052 // draw series line
1055 for (int i = 1; i < points.length; ++i)
1056 context.line_to (get_scr_x(s, points[i].x), get_scr_y(s, points[i].y));
1053 for (int i = 1; i < points.length; ++i) {
1054 Point c, d;
1055 if (cut_line (Point(get_scr_x(s, points[i - 1].x), get_scr_y(s, points[i - 1].y)),
1056 Point(get_scr_x(s, points[i].x), get_scr_y(s, points[i].y)),
1057 out c, out d)) {
1058 context.move_to (c.x, c.y);
1059 context.line_to (d.x, d.y);
1060 }
1061 }
1057 1062 context.stroke();
1058 for (int i = 0; i < points.length; ++i)
1059 draw_marker_at_pos(s.marker_type, get_scr_x(s, points[i].x), get_scr_y(s, points[i].y));
1063 for (int i = 0; i < points.length; ++i) {
1064 var x = get_scr_x(s, points[i].x);
1065 var y = get_scr_y(s, points[i].y);
1066 if (point_in_plot_area (Point (x, y)))
1067 draw_marker_at_pos(s.marker_type, x, y);
1068 }
1060 1069 }
1061 1070 }
1062 1071
1106 1106 chart.title_vindent = this.title_vindent;
1107 1107 chart.title_width = this.title_width;
1108 1108 chart.width = this.width;
1109 chart.zoom = this.zoom;
1110 chart.zoom_x0 = this.zoom_x0;
1111 chart.zoom_x1 = this.zoom_x1;
1112 chart.zoom_y0 = this.zoom_y0;
1113 chart.zoom_y1 = this.zoom_y1;
1114 1109 return chart;
1115 1110 }
1116 1111 }
1 1 namespace Gtk.CairoChart {
2 public struct Place {
3 double x_low;
4 double x_high;
5 double y_low;
6 double y_high;
2 public class Place {
3 public double x_low;
4 public double x_high;
5 public double y_low;
6 public double y_high;
7
8 public Place copy () {
9 var place = new Place ();
10 place.x_low = this.x_low;
11 place.x_high = this.x_high;
12 place.y_low = this.y_low;
13 place.y_high = this.y_high;
14 return place;
15 }
7 16
8 17 public Place (double x_low = 0, double x_high = 0, double y_low = 0, double y_high = 0) {
9 18 this.x_low = x_low;
25 25 PRICLE_TRIANGLE
26 26 }
27 27
28 public Place place = Place();
28 Place _place = new Place();
29 public Place place {
30 get { return _place; }
31 set { _place = zoom_place = value; }
32 default = new Place();
33 }
34 public Place zoom_place = new Place();
29 35 public Text title = new Text ();
30 36 public MarkerType marker_type = MarkerType.SQUARE;
31 37
65 65 series.grid = this.grid.copy ();
66 66 series.line_style = this.line_style;
67 67 series.marker_type = this.marker_type;
68 series.place = this.place;
68 series.place = this.place.copy();
69 69 series.points = this.points.copy();
70 70 series.sort = this.sort;
71 71 series.title = this.title.copy();
73 73 }
74 74
75 75 public Series () {
76 zoom_place = place;
76 77 }
77 78 }
78 79 }
5 5 var s2 = new Series ();
6 6 var s3 = new Series ();
7 7
8 s1.title = new Text("Series 1"); s1.color = new Color (1, 0, 0);
9 s1.points = {new Point(0, 0), new Point(2, 1), new Point(1, 3)};
8 s1.title = new Text("Series 1"); s1.color = Color (1, 0, 0);
9 s1.points = {Point(0, 0), Point(2, 1), Point(1, 3)};
10 10 s1.axis_x.position = Axis.Position.HIGH;
11 11 s1.axis_x.format = "%.3Lf";
12 s2.title = new Text("Series 2"); s2.color = new Color (0, 1, 0);
13 s2.points = {new Point(5, -3), new Point(25, -18), new Point(-11, 173)};
14 s3.title = new Text("Series 3"); s3.color = new Color (0, 0, 1);
15 s3.points = {new Point(9, 17), new Point(2, 10), new Point(122, 31)};
12 s2.title = new Text("Series 2"); s2.color = Color (0, 1, 0);
13 s2.points = {Point(5, -3), Point(25, -18), Point(-11, 173)};
14 s3.title = new Text("Series 3"); s3.color = Color (0, 0, 1);
15 s3.points = {Point(9, 17), Point(2, 10), Point(122, 31)};
16 16 s3.axis_y.position = Axis.Position.HIGH;
17 17
18 18 s1.axis_x.min = 0; s1.axis_x.max = 2;
48 48 var s2 = new Series ();
49 49 var s3 = new Series ();
50 50
51 s1.title = new Text("Series 1"); s1.color = new Color (1, 0, 0);
52 s1.points = {new Point(-12, 0), new Point(2, 1), new Point(20, 3)};
51 s1.title = new Text("Series 1"); s1.color = Color (1, 0, 0);
52 s1.points = {Point(-12, 0), Point(2, 1), Point(20, 3)};
53 53 s2.axis_y.position = Axis.Position.HIGH;
54 54 s1.axis_x.format = "%.3Lf";
55 s2.title = new Text("Series 2"); s2.color = new Color (0, 1, 0);
56 s2.points = {new Point(5, -3), new Point(25, -18), new Point(-11, 173)};
57 s3.title = new Text("Series 3"); s3.color = new Color (0, 0, 1);
58 s3.points = {new Point(9, 17), new Point(2, 10), new Point(-15, 31)};
55 s2.title = new Text("Series 2"); s2.color = Color (0, 1, 0);
56 s2.points = {Point(5, -3), Point(25, -18), Point(-11, 173)};
57 s3.title = new Text("Series 3"); s3.color = Color (0, 0, 1);
58 s3.points = {Point(9, 17), Point(2, 10), Point(-15, 31)};
59 59 s3.axis_y.position = Axis.Position.HIGH;
60 60
61 61 s1.axis_x.min = -15; s1.axis_x.max = 30;
91 91 var s2 = new Series ();
92 92 var s3 = new Series ();
93 93
94 s1.title = new Text("Series 1"); s1.color = new Color (1, 0, 0);
95 s1.points = {new Point(0, 70), new Point(2, 155), new Point(1, -3)};
94 s1.title = new Text("Series 1"); s1.color = Color (1, 0, 0);
95 s1.points = {Point(0, 70), Point(2, 155), Point(1, -3)};
96 96 s1.axis_x.position = Axis.Position.HIGH;
97 97 s1.axis_y.position = Axis.Position.HIGH;
98 98 s1.axis_x.format = "%.3Lf";
99 s2.title = new Text("Series 2"); s2.color = new Color (0, 1, 0);
100 s2.points = {new Point(5, -3), new Point(25, -18), new Point(-11, 173)};
99 s2.title = new Text("Series 2"); s2.color = Color (0, 1, 0);
100 s2.points = {Point(5, -3), Point(25, -18), Point(-11, 173)};
101 101 s2.axis_y.position = Axis.Position.HIGH;
102 s3.title = new Text("Series 3"); s3.color = new Color (0, 0, 1);
103 s3.points = {new Point(9, -17), new Point(2, 10), new Point(122, 31)};
102 s3.title = new Text("Series 3"); s3.color = Color (0, 0, 1);
103 s3.points = {Point(9, -17), Point(2, 10), Point(122, 31)};
104 104 s3.axis_y.position = Axis.Position.HIGH;
105 105
106 106 s1.axis_x.min = 0; s1.axis_x.max = 2;
145 145 var now = new DateTime.now_local().to_unix();
146 146 var high = (uint64) (253000000000L);
147 147
148 s1.title = new Text("Series 1"); s1.color = new Color (1, 0, 0);
149 s1.points = {new Point(now, 70), new Point(now - 100000, 155), new Point(now + 100000, 30)};
148 s1.title = new Text("Series 1"); s1.color = Color (1, 0, 0);
149 s1.points = {Point(now, 70), Point(now - 100000, 155), Point(now + 100000, 30)};
150 150 s1.axis_x.position = Axis.Position.HIGH;
151 151 s1.axis_y.position = Axis.Position.HIGH;
152 s2.title = new Text("Series 2"); s2.color = new Color (0, 1, 0);
153 s2.points = {new Point(5, -3), new Point(25, -18), new Point(-11, 173)};
152 s2.title = new Text("Series 2"); s2.color = Color (0, 1, 0);
153 s2.points = {Point(5, -3), Point(25, -18), Point(-11, 173)};
154 154 s2.axis_y.position = Axis.Position.HIGH;
155 s3.title = new Text("Series 3"); s3.color = new Color (0, 0, 1);
156 s3.points = {new Point(high - 2 + 0.73, -17), new Point(high - 1 + 0.234, 10), new Point(high + 1 + 0.411, 31)};
155 s3.title = new Text("Series 3"); s3.color = Color (0, 0, 1);
156 s3.points = {Point(high - 2 + 0.73, -17), Point(high - 1 + 0.234, 10), Point(high + 1 + 0.411, 31)};
157 157 s3.axis_y.position = Axis.Position.HIGH;
158 s4.title = new Text("Series 4"); s4.color = new Color (0.5, 0.3, 0.9);
159 s4.points = {new Point(high + 0.005, -19.05), new Point(high + 0.0051, 28), new Point(high + 0.0052, 55), new Point(high + 0.0053, 44)};
158 s4.title = new Text("Series 4"); s4.color = Color (0.5, 0.3, 0.9);
159 s4.points = {Point(high + 0.005, -19.05), Point(high + 0.0051, 28), Point(high + 0.0052, 55), Point(high + 0.0053, 44)};
160 160 s4.axis_y.position = Axis.Position.HIGH;
161 161
162 162 s1.axis_x.min = now - 100000; s1.axis_x.max = now + 100000;
195 195 chart.series = { s1, s2, s3, s4 };
196 196 }
197 197
198 bool cursor_in_chart (Chart chart, double x, double y) {
199 if (x < chart.plot_area_x_min) return false;
200 if (x > chart.plot_area_x_max) return false;
201 if (y < chart.plot_area_y_min) return false;
202 if (y > chart.plot_area_y_max) return false;
203 return true;
204 }
205
198 206 int main (string[] args) {
199 207 init (ref args);
200 208
334 334 }
335 335 });
336 336
337 bool draw_selection = false;
338 double sel_x0 = 0, sel_x1 = 0, sel_y0 = 0, sel_y1 = 0;
339
337 340 da.draw.connect((context) => {
338 341 // user's pre draw operations here...
342
339 343 chart.context = context;
340 var ret = chart.draw();
344 /*var ret = */chart.draw();
345
341 346 // user's post draw operations here...
342 return ret;
347 if (draw_selection) {
348 context.set_source_rgba (0.5, 0.5, 0.5, 0.7);
349 context.set_line_join(Cairo.LineJoin.MITER);
350 context.set_line_cap(Cairo.LineCap.ROUND);
351 context.set_line_width(1);
352 //context.set_dash(null, 0);
353 context.rectangle (sel_x0, sel_y0, sel_x1 - sel_x0, sel_y1 - sel_y0);
354 stdout.puts("draw\n");
355 //context.fill();
356 context.stroke();
357 }
358
359 return true;//ret;
343 360 });
344 361 da.queue_draw_area(0, 0, da.get_allocated_width(), da.get_allocated_height());
345 362
346 da.button_release_event.connect((event) => {
347 // user's pre button_release_event operations here...
348 var ret = chart.button_release_event(event);
349 // user's post button_release_event operations here...
350 return ret;
351 });
352 363 da.button_press_event.connect((event) => {
353 364 // user's pre button_press_event operations here...
354 var ret = chart.button_press_event(event);
365 //stdout.puts("pre_press\n");
366
367 //var ret = chart.button_press_event(event);
368 stdout.printf("event.button = %u, in_chart?= %d\n", event.button, (int)cursor_in_chart(chart, event.x, event.y));
369 if (event.button == 2 && cursor_in_chart(chart, event.x, event.y)) {
370 draw_selection = true;
371 sel_x0 = sel_x1 = event.x;
372 sel_y0 = sel_y1 = event.y;
373 }
374
355 375 // user's post button_press_event operations here...
356 return ret;
376 //stdout.puts("post_press\n");
377
378 return true; // return ret;
357 379 });
380 da.button_release_event.connect((event) => {
381 // user's pre button_release_event operations here...
382 //stdout.puts("pre_release\n");
383
384 //var ret = chart.button_release_event(event);
385 if (event.button == 2) {
386 draw_selection = false;
387 sel_x1 = event.x;
388 sel_y1 = event.y;
389 chart.zoom_in (sel_x0, sel_y0, sel_x1, sel_y1);
390 }
391
392 // user's post button_release_event operations here...
393 //stdout.puts("post_release\n");
394
395 return true; // return ret;
396 });
358 397 da.motion_notify_event.connect((event) => {
359 398 // user's pre motion_notify_event operations here...
360 var ret = chart.motion_notify_event(event);
399 //stdout.puts("pre_motion\n");
400
401 //var ret = chart.motion_notify_event(event);
402
361 403 // user's post motion_notify_event operations here...
362 return ret;
404 //stdout.puts("post_motion\n");
405 stdout.puts(@"draw_selection = $draw_selection\n");
406 if (draw_selection && cursor_in_chart(chart, event.x, event.y)) {
407 sel_x1 = event.x;
408 sel_y1 = event.y;
409 da.queue_draw_area(0, 0, da.get_allocated_width(), da.get_allocated_height());
410 }
411
412 return true; // return ret;
363 413 });
364 414 da.add_events(Gdk.EventMask.SCROLL_MASK);
365 415 da.scroll_event.connect((event) => {
366 416 // user's pre scroll_notify_event operations here...
367 stdout.puts("pre_scroll\n");
417 //stdout.puts("pre_scroll\n");
368 418
369 var ret = chart.scroll_notify_event(event);
419 //var ret = chart.scroll_notify_event(event);
370 420
371 421 // user's post scroll_notify_event operations here...
372 stdout.puts("post_scroll\n");
422 //stdout.puts("post_scroll\n");
373 423
374 return ret;
424 return true; // return ret;
375 425 });
376 426
377 427 var vbox2 = new Box(Orientation.VERTICAL, 0);