/
smooth_line_area.vala
124 lines (99 loc) · 4.89 KB
/
smooth_line_area.vala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
using Cairo;
namespace LiveChart {
public class SmoothLineArea : SmoothLine {
public double area_alpha {get; set; default = 0.1;}
private SmoothLineAreaSerieDrawer drawer = new SmoothLineAreaSerieDrawer();
public SmoothLineArea(Values values = new Values()) {
base(values);
}
public override void draw(Context ctx, Config config) {
if (visible) {
drawer.draw(ctx, config, this.line, Points.create(values, config),this.main_color, this.area_alpha, this.region);
}
}
}
private class SmoothLineAreaSerieDrawer {
private RegionAreaDrawer region_drawer = new RegionAreaDrawer();
private RegionOnLineDrawer region_on_line_drawer = new RegionOnLineDrawer();
private SmoothLineDrawer smooth_line_drawer = new SmoothLineDrawer();
private SmoothLineAreaIntersectionsGenerator intersections_generator = new SmoothLineAreaIntersectionsGenerator();
public void draw(Context ctx, Config config, Path line, Points points, Gdk.RGBA color, double alpha, Region? region) {
if(points.size > 0) {
var curves = smooth_line_drawer.draw(ctx, config, line, points);
var intersections = intersections_generator.generate(region, config, points, curves);
var curve = ctx.copy_path();
if(region != null && region.has_line_color()) {
ctx.push_group();
ctx.stroke();
ctx.set_operator(Operator.IN);
region_on_line_drawer.draw(ctx, config, intersections);
ctx.fill();
ctx.pop_group_to_source();
ctx.paint();
} else {
ctx.stroke();
}
ctx.push_group();
ctx.append_path(curve);
var area = new Area(points, color, alpha);
area.draw(ctx, config);
ctx.fill();
// Clear pixels that overlaps existing path
ctx.append_path(curve);
ctx.set_operator(Operator.CLEAR);
ctx.stroke();
if (region != null && region.has_area_color()) {
// Clear existing area if regions
ctx.set_operator(Operator.CLEAR);
region_drawer.draw(ctx, config, intersections);
}
ctx.pop_group_to_source();
ctx.paint();
if (region != null && region.has_area_color()) {
ctx.push_group();
//Create a clip to match the region area only, without the line
ctx.append_path(curve);
area.draw(ctx, config);
ctx.clip();
region_drawer.draw(ctx, config, intersections);
ctx.append_path(curve);
ctx.set_operator(Operator.CLEAR);
ctx.stroke();
ctx.pop_group_to_source();
ctx.paint();
}
}
}
}
private class SmoothLineAreaIntersectionsGenerator : Drawer {
public Intersections generate(Region? region, Config config, Points points, Gee.List<BezierCurve?> curves) {
if(region != null) {
var resolver = new CurveRegionResolver(region);
var intersector = new BezierIntersector(resolver, config);
for (int pos = 0; pos <= points.size -1; pos++) {
var previous_point = points.get(pos);
var target_point = points.after(pos);
if (this.is_out_of_area(previous_point)) {
continue;
}
intersector.intersect(previous_point, target_point, curves.get(pos));
}
return resolver.get_intersections();
}
return new Intersections();
}
}
private class RegionAreaDrawer {
public void draw(Context ctx, Config config, Intersections intersections) {
var boundaries = config.boundaries();
intersections.foreach((intersection) => {
if (intersection != null) {
ctx.set_source_rgba(intersection.region.area_color.red, intersection.region.area_color.green, intersection.region.area_color.blue, intersection.region.area_color.alpha);
ctx.rectangle(intersection.start_x, boundaries.y.min, intersection.end_x - intersection.start_x, boundaries.height);
}
return true;
});
ctx.fill();
}
}
}