2 Copyright (C) 2013 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 #include "pbd/xml++.h"
25 #include "canvas/curve.h"
27 using namespace ArdourCanvas;
31 Curve::Curve (Group* parent)
39 Curve::compute_bounding_box () const
41 PolyItem::compute_bounding_box ();
51 for (Points::const_iterator i = first_control_points.begin(); i != first_control_points.end(); ++i) {
53 bbox1.x0 = min (bbox1.x0, i->x);
54 bbox1.y0 = min (bbox1.y0, i->y);
55 bbox1.x1 = max (bbox1.x1, i->x);
56 bbox1.y1 = max (bbox1.y1, i->y);
58 bbox1.x0 = bbox1.x1 = i->x;
59 bbox1.y0 = bbox1.y1 = i->y;
64 for (Points::const_iterator i = second_control_points.begin(); i != second_control_points.end(); ++i) {
66 bbox2.x0 = min (bbox2.x0, i->x);
67 bbox2.y0 = min (bbox2.y0, i->y);
68 bbox2.x1 = max (bbox2.x1, i->x);
69 bbox2.y1 = max (bbox2.y1, i->y);
71 bbox2.x0 = bbox2.x1 = i->x;
72 bbox2.y0 = bbox2.y1 = i->y;
77 Rect u = bbox1.extend (bbox2);
78 _bounding_box = u.extend (_bounding_box.get());
81 _bounding_box_dirty = false;
85 Curve::set (Points const& p)
89 first_control_points.clear ();
90 second_control_points.clear ();
92 compute_control_points (_points, first_control_points, second_control_points);
96 Curve::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
99 setup_outline_context (context);
100 render_path (area, context);
106 Curve::get_state () const
108 XMLNode* node = new XMLNode ("PolyLine");
109 add_poly_item_state (node);
110 add_outline_state (node);
115 Curve::set_state (XMLNode const * node)
117 set_poly_item_state (node);
118 set_outline_state (node);
122 Curve::render_path (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
124 PolyItem::render_curve (area, context, first_control_points, second_control_points);
128 Curve::compute_control_points (Points const& knots,
129 Points& firstControlPoints,
130 Points& secondControlPoints)
132 Points::size_type n = knots.size() - 1;
139 /* Special case: Bezier curve should be a straight line. */
143 d.x = (2.0 * knots[0].x + knots[1].x) / 3;
144 d.y = (2.0 * knots[0].y + knots[1].y) / 3;
145 firstControlPoints.push_back (d);
147 d.x = 2.0 * firstControlPoints[0].x - knots[0].x;
148 d.y = 2.0 * firstControlPoints[0].y - knots[0].y;
149 secondControlPoints.push_back (d);
154 // Calculate first Bezier control points
155 // Right hand side vector
157 std::vector<double> rhs;
161 // Set right hand side X values
163 for (Points::size_type i = 1; i < n - 1; ++i) {
164 rhs[i] = 4 * knots[i].x + 2 * knots[i + 1].x;
166 rhs[0] = knots[0].x + 2 * knots[1].x;
167 rhs[n - 1] = (8 * knots[n - 1].x + knots[n].x) / 2.0;
169 // Get first control points X-values
170 double* x = solve (rhs);
172 // Set right hand side Y values
173 for (Points::size_type i = 1; i < n - 1; ++i) {
174 rhs[i] = 4 * knots[i].y + 2 * knots[i + 1].y;
176 rhs[0] = knots[0].y + 2 * knots[1].y;
177 rhs[n - 1] = (8 * knots[n - 1].y + knots[n].y) / 2.0;
179 // Get first control points Y-values
180 double* y = solve (rhs);
182 for (Points::size_type i = 0; i < n; ++i) {
184 firstControlPoints.push_back (Duple (x[i], y[i]));
187 secondControlPoints.push_back (Duple (2 * knots [i + 1].x - x[i + 1],
188 2 * knots[i + 1].y - y[i + 1]));
190 secondControlPoints.push_back (Duple ((knots [n].x + x[n - 1]) / 2,
191 (knots[n].y + y[n - 1]) / 2));
199 /** Solves a tridiagonal system for one of coordinates (x or y)
200 * of first Bezier control points.
204 Curve::solve (std::vector<double> const & rhs)
206 std::vector<double>::size_type n = rhs.size();
207 double* x = new double[n]; // Solution vector.
208 double* tmp = new double[n]; // Temp workspace.
214 for (std::vector<double>::size_type i = 1; i < n; i++) {
215 // Decomposition and forward substitution.
217 b = (i < n - 1 ? 4.0 : 3.5) - tmp[i];
218 x[i] = (rhs[i] - x[i - 1]) / b;
221 for (std::vector<double>::size_type i = 1; i < n; i++) {
223 x[n - i - 1] -= tmp[n - i] * x[n - i];