fix crash when copy'ing latent plugins
[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         , _y1 (0)
39 {
40 }
41
42 void
43 PolyLine::compute_bounding_box () const
44 {
45         PolyItem::compute_bounding_box ();
46         if (_y1 > 0 && _bounding_box) {
47                 _bounding_box.get().x0 = 0;
48                 _bounding_box.get().x1 = COORD_MAX;
49                 if (_y1 > _bounding_box.get().y1) {
50                         _bounding_box.get().y1 = _y1;
51                 }
52         }
53 }
54
55 void
56 PolyLine::set_fill_y1 (double y1) {
57         begin_change ();
58         _bounding_box_dirty = true;
59         _y1 = y1;
60         end_change ();
61 }
62
63 void
64 PolyLine::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
65 {
66         if (_fill && _y1 > 0 && _points.size() > 0) {
67                 const ArdourCanvas::Rect& vp (_canvas->visible_area());
68                 setup_fill_context (context);
69
70                 Duple y (0, _y1);
71                 float y1 = item_to_window (y).y;
72                 render_path (area, context);
73                 Duple c0 (item_to_window (_points.back()));
74                 Duple c1 (item_to_window (_points.front()));
75                 if (c0.x < vp.x1) {
76                         context->line_to (vp.x1, c0.y);
77                         context->line_to (vp.x1, y1);
78                 } else {
79                         context->line_to (vp.x1, y1);
80                 }
81                 if (c1.x > vp.x0) {
82                         context->line_to (vp.x0, y1);
83                         context->line_to (vp.x0, c1.y);
84                 } else {
85                         context->line_to (vp.x0, y1);
86                 }
87                 context->close_path ();
88                 context->fill ();
89         }
90
91         if (_outline) {
92                 setup_outline_context (context);
93                 render_path (area, context);
94                 context->stroke ();
95         }
96 }
97
98 void
99 PolyLine::set_steps (Points const & points, bool stepped)
100 {
101         if (!stepped) {
102                 PolyItem::set(points);
103                 return;
104         }
105
106         Points copy;
107         for (Points::const_iterator p = points.begin(); p != points.end();) {
108                 Points::const_iterator next = p;
109                 ++next;
110
111                 copy.push_back(*p);
112                 if (next != points.end() && next->x != p->x) {
113                         copy.push_back(Duple(next->x, p->y));
114                 }
115
116                 p = next;
117         }
118
119         PolyItem::set(copy);
120 }
121
122 bool
123 PolyLine::covers (Duple const & point) const
124 {
125         Duple p = window_to_item (point);
126
127         const Points::size_type npoints = _points.size();
128
129         if (npoints < 2) {
130                 return false;
131         }
132
133         Points::size_type i;
134         Points::size_type j;
135
136         /* repeat for each line segment */
137
138         const Rect visible (window_to_item (_canvas->visible_area()));
139
140         for (i = 1, j = 0; i < npoints; ++i, ++j) {
141
142                 Duple at;
143                 double t;
144                 Duple a (_points[j]);
145                 Duple b (_points[i]);
146
147                 /*
148                   Clamp the line endpoints to the visible area of the canvas. If we do
149                   not do this, we may have a line segment extending to COORD_MAX and our
150                   math goes wrong.
151                 */
152
153                 a.x = std::min (a.x, visible.x1);
154                 a.y = std::min (a.y, visible.y1);
155                 b.x = std::min (b.x, visible.x1);
156                 b.y = std::min (b.y, visible.y1);
157
158                 double d = distance_to_segment_squared (p, a, b, t, at);
159
160                 if (t < 0.0 || t > 1.0) {
161                         continue;
162                 }
163
164                 if (d < _threshold + _outline_width) {
165                         return true;
166                 }
167
168         }
169
170         return false;
171 }
172
173 void
174 PolyLine::set_covers_threshold (double t)
175 {
176         _threshold = t;
177 }