summaryrefslogtreecommitdiff
path: root/libs/canvas/arrow.cc
blob: f95c6ecc8ff81bab6b035cfab60cb1622b066b6b (plain)
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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
/*
 * Copyright (C) 2012 Carl Hetherington <carl@carlh.net>
 * Copyright (C) 2013-2014 Paul Davis <paul@linuxaudiosystems.com>
 * Copyright (C) 2015-2017 Robin Gareus <robin@gareus.org>
 * Copyright (C) 2015 David Robillard <d@drobilla.net>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

/** @file  canvas/arrow.cc
 *  @brief Implementation of the Arrow canvas object.
 */

#include "pbd/compose.h"

#include "canvas/arrow.h"
#include "canvas/debug.h"
#include "canvas/polygon.h"
#include "canvas/line.h"

using namespace ArdourCanvas;

/** Construct an Arrow.
 *  @param parent Parent canvas group.
 */
Arrow::Arrow (Canvas* c)
	: Container (c)
{
	setup ();
}

Arrow::Arrow (Item* parent)
	: Container (parent)
{
	setup ();
}

void
Arrow::setup ()
{
	/* set up default arrow heads at each end */
	for (int i = 0; i < 2; ++i) {
		_heads[i].polygon = new Polygon (this);
		_heads[i].outward = true;
		_heads[i].width = 4;
		_heads[i].height = 4;
		setup_polygon (i);
		CANVAS_DEBUG_NAME (_heads[i].polygon, string_compose ("arrow head %1", i));
	}

	_line = new Line (this);
	CANVAS_DEBUG_NAME (_line, "arrow line");
}

void
Arrow::compute_bounding_box () const
{
	/* Compute our bounding box manually rather than using the default
	   container algorithm, since having the bounding box with origin other
	   than zero causes strange problems for mysterious reasons. */

	const double outline_pad = 0.5 + (_line->outline_width() / 2.0);
	const double head_width  = std::max(_heads[0].width, _heads[1].width);

	_bounding_box = Rect(0,
	                     0,
	                     _line->x1() + (head_width / 2.0) + outline_pad,
	                     _line->y1());

	_bounding_box_dirty = false;
}

/** Set whether to show an arrow head at one end or other
 *  of the line.
 *  @param which 0 or 1 to specify the arrow head to set up.
 *  @param true if this arrow head should be shown.
 */
void
Arrow::set_show_head (int which, bool show)
{
	assert (which == 0 || which == 1);

	begin_change ();

	if (!show) {
		delete _heads[which].polygon;
		_heads[which].polygon = 0;
	} else {
		setup_polygon (which);
	}

	_bounding_box_dirty = true;
	end_change ();
}

/** Set whether a given arrow head points into the line or
 *  away from it.
 *  @param which 0 or 1 to specify the arrow head to set up.
 *  @param true if this arrow head should point out from the line,
 *  otherwise false to point in.
 */
void
Arrow::set_head_outward (int which, bool outward)
{
	assert (which == 0 || which == 1);

	begin_change ();

	_heads[which].outward = outward;

	setup_polygon (which);
	_bounding_box_dirty = true;
	end_change ();
}

/** Set the height of a given arrow head.
 *  @param which 0 or 1 to specify the arrow head to set up.
 *  @param height Height of the arrow head in pixels.
 */
void
Arrow::set_head_height (int which, Distance height)
{
	assert (which == 0 || which == 1);

	begin_change ();

	_heads[which].height = height;

	setup_polygon (which);
	_bounding_box_dirty = true;
	end_change ();
}

/** Set the width of a given arrow head.
 *  @param which 0 or 1 to specify the arrow head to set up.
 *  @param width Width of the arrow head in pixels.
 */
void
Arrow::set_head_width (int which, Distance width)
{
	assert (which == 0 || which == 1);

	begin_change ();

	_heads[which].width = width;

	setup_polygon (which);
	_bounding_box_dirty = true;
	end_change ();
}

/** Set the width of our line, and the outline of our arrow(s).
 *  @param width New width in pixels.
 */
void
Arrow::set_outline_width (Distance width)
{
	_line->set_outline_width (width);
	if (_heads[0].polygon) {
		_heads[0].polygon->set_outline_width (width);
	}
	if (_heads[1].polygon) {
		_heads[1].polygon->set_outline_width (width);
	}
	_bounding_box_dirty = true;
}

/** Set the x position of our line.
 *  @param x New x position in pixels (in our coordinate system).
 */
void
Arrow::set_x (Coord x)
{
	_line->set_x0 (x);
	_line->set_x1 (x);
	for (int i = 0; i < 2; ++i) {
		if (_heads[i].polygon) {
			_heads[i].polygon->set_x_position (x - _heads[i].width / 2);
		}
	}
	_bounding_box_dirty = true;
}

/** Set the y position of end 0 of our line.
 *  @param y0 New y0 position in pixels (in our coordinate system).
 */
void
Arrow::set_y0 (Coord y0)
{
	_line->set_y0 (y0);
	if (_heads[0].polygon) {
		_heads[0].polygon->set_y_position (y0);
	}
	_bounding_box_dirty = true;
}

/** Set the y position of end 1 of our line.
 *  @param y1 New y1 position in pixels (in our coordinate system).
 */
void
Arrow::set_y1 (Coord y1)
{
	_line->set_y1 (y1);
	if (_heads[1].polygon) {
		_heads[1].polygon->set_y_position (y1 - _heads[1].height);
	}
	_bounding_box_dirty = true;
}

/** @return x position of our line in pixels (in our coordinate system) */
Coord
Arrow::x () const
{
	return _line->x0 ();
}

/** @return y position of end 1 of our line in pixels (in our coordinate system) */
Coord
Arrow::y1 () const
{
	return _line->y1 ();
}

/** Set up the polygon used to represent a particular arrow head.
 *  @param which 0 or 1 to specify the arrow head to set up.
 */
void
Arrow::setup_polygon (int which)
{
	assert (which == 0 || which == 1);

	Points points;

	if ((which == 0 && _heads[which].outward) || (which == 1 && !_heads[which].outward)) {
		/* this is an arrow head pointing towards -ve y */
		points.push_back (Duple (_heads[which].width / 2, 0));
		points.push_back (Duple (_heads[which].width, _heads[which].height));
		points.push_back (Duple (0, _heads[which].height));
	} else {
		/* this is an arrow head pointing towards +ve y */
		points.push_back (Duple (0, 0));
		points.push_back (Duple (_heads[which].width, 0));
		points.push_back (Duple (_heads[which].width / 2, _heads[which].height));
		points.push_back (Duple (0, 0));
	}

	_heads[which].polygon->set (points);
}

/** Set the color of our line and arrow heads.
 *  @param color New color.
 */
void
Arrow::set_color (Gtkmm2ext::Color color)
{
	_line->set_outline_color (color);
	for (int i = 0; i < 2; ++i) {
		if (_heads[i].polygon) {
			_heads[i].polygon->set_outline_color (color);
			_heads[i].polygon->set_fill_color (color);
		}
	}
}

bool
Arrow::covers (Duple const & point) const
{
	if (_heads[0].polygon && _heads[0].polygon->covers (point)) {
		return true;
	}
	if (_line && _line->covers (point)) {
		return true;
	}

	if (_heads[1].polygon && _heads[1].polygon->covers (point)) {
		return true;
	}

	return false;
}