5086df76a3f52800b037bb116b106d4812af38c8
[ardour.git] / libs / canvas / poly_line.cc
1 /*
2     Copyright (C) 2011-2013 Paul Davis
3     Author: Carl Hetherington <cth@carlh.net>
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20 #include <algorithm>
21
22 #include "canvas/poly_line.h"
23 #include "canvas/canvas.h"
24 #include "canvas/utils.h"
25
26 using namespace ArdourCanvas;
27
28 PolyLine::PolyLine (Canvas* c)
29         : PolyItem (c)
30         , _threshold (1.0)
31         , _y1 (0)
32 {
33 }
34
35 PolyLine::PolyLine (Item* parent)
36         : PolyItem (parent)
37         , _threshold (1.0)
38 {
39 }
40
41 void
42 PolyLine::compute_bounding_box () const
43 {
44         PolyItem::compute_bounding_box ();
45         if (_y1 > 0 && _bounding_box) {
46                 _bounding_box.get().x0 = 0;
47                 _bounding_box.get().x1 = COORD_MAX;
48                 if (_y1 > _bounding_box.get().y1) {
49                         _bounding_box.get().y1 = _y1;
50                 }
51         }
52 }
53
54 void
55 PolyLine::set_fill_y1 (double y1) {
56         begin_change ();
57         _bounding_box_dirty = true;
58         _y1 = y1;
59         end_change ();
60 }
61
62 void
63 PolyLine::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
64 {
65         if (_fill && _y1 > 0 && _points.size() > 0) {
66                 const ArdourCanvas::Rect& vp (_canvas->visible_area());
67                 setup_fill_context (context);
68
69                 Duple y (0, _y1);
70                 float y1 = item_to_window (y).y;
71                 render_path (area, context);
72                 Duple c0 (item_to_window (_points.back()));
73                 Duple c1 (item_to_window (_points.front()));
74                 if (c0.x < vp.x1) {
75                         context->line_to (vp.x1, c0.y);
76                         context->line_to (vp.x1, y1);
77                 } else {
78                         context->line_to (vp.x1, y1);
79                 }
80                 if (c1.x > vp.x0) {
81                         context->line_to (vp.x0, y1);
82                         context->line_to (vp.x0, c1.y);
83                 } else {
84                         context->line_to (vp.x0, y1);
85                 }
86                 context->close_path ();
87                 context->fill ();
88         }
89
90         if (_outline) {
91                 setup_outline_context (context);
92                 render_path (area, context);
93                 context->stroke ();
94         }
95 }
96
97 void
98 PolyLine::set_steps (Points const & points, bool stepped)
99 {
100         if (!stepped) {
101                 PolyItem::set(points);
102                 return;
103         }
104
105         Points copy;
106         for (Points::const_iterator p = points.begin(); p != points.end();) {
107                 Points::const_iterator next = p;
108                 ++next;
109
110                 copy.push_back(*p);
111                 if (next != points.end() && next->x != p->x) {
112                         copy.push_back(Duple(next->x, p->y));
113                 }
114
115                 p = next;
116         }
117
118         PolyItem::set(copy);
119 }
120
121 bool
122 PolyLine::covers (Duple const & point) const
123 {
124         Duple p = window_to_item (point);
125
126         const Points::size_type npoints = _points.size();
127
128         if (npoints < 2) {
129                 return false;
130         }
131
132         Points::size_type i;
133         Points::size_type j;
134
135         /* repeat for each line segment */
136
137         const Rect visible (window_to_item (_canvas->visible_area()));
138
139         for (i = 1, j = 0; i < npoints; ++i, ++j) {
140
141                 Duple at;
142                 double t;
143                 Duple a (_points[j]);
144                 Duple b (_points[i]);
145
146                 /*
147                   Clamp the line endpoints to the visible area of the canvas. If we do
148                   not do this, we may have a line segment extending to COORD_MAX and our
149                   math goes wrong.
150                 */
151
152                 a.x = std::min (a.x, visible.x1);
153                 a.y = std::min (a.y, visible.y1);
154                 b.x = std::min (b.x, visible.x1);
155                 b.y = std::min (b.y, visible.y1);
156
157                 double d = distance_to_segment_squared (p, a, b, t, at);
158
159                 if (t < 0.0 || t > 1.0) {
160                         continue;
161                 }
162
163                 if (d < _threshold + _outline_width) {
164                         return true;
165                 }
166
167         }
168
169         return false;
170 }
171
172 void
173 PolyLine::set_covers_threshold (double t)
174 {
175         _threshold = t;
176 }