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 "canvas/curve.h"
25 using namespace ArdourCanvas;
29 Curve::Curve (Group* parent)
37 Curve::compute_bounding_box () const
39 PolyItem::compute_bounding_box ();
49 for (Points::const_iterator i = first_control_points.begin(); i != first_control_points.end(); ++i) {
51 bbox1.x0 = min (bbox1.x0, i->x);
52 bbox1.y0 = min (bbox1.y0, i->y);
53 bbox1.x1 = max (bbox1.x1, i->x);
54 bbox1.y1 = max (bbox1.y1, i->y);
56 bbox1.x0 = bbox1.x1 = i->x;
57 bbox1.y0 = bbox1.y1 = i->y;
62 for (Points::const_iterator i = second_control_points.begin(); i != second_control_points.end(); ++i) {
64 bbox2.x0 = min (bbox2.x0, i->x);
65 bbox2.y0 = min (bbox2.y0, i->y);
66 bbox2.x1 = max (bbox2.x1, i->x);
67 bbox2.y1 = max (bbox2.y1, i->y);
69 bbox2.x0 = bbox2.x1 = i->x;
70 bbox2.y0 = bbox2.y1 = i->y;
75 Rect u = bbox1.extend (bbox2);
76 _bounding_box = u.extend (_bounding_box.get());
79 _bounding_box_dirty = false;
83 Curve::set (Points const& p)
87 first_control_points.clear ();
88 second_control_points.clear ();
90 compute_control_points (_points, first_control_points, second_control_points);
94 Curve::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
97 setup_outline_context (context);
98 render_path (area, context);
104 Curve::render_path (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
106 PolyItem::render_curve (area, context, first_control_points, second_control_points);
110 Curve::compute_control_points (Points const& knots,
111 Points& firstControlPoints,
112 Points& secondControlPoints)
114 Points::size_type n = knots.size() - 1;
121 /* Special case: Bezier curve should be a straight line. */
125 d.x = (2.0 * knots[0].x + knots[1].x) / 3;
126 d.y = (2.0 * knots[0].y + knots[1].y) / 3;
127 firstControlPoints.push_back (d);
129 d.x = 2.0 * firstControlPoints[0].x - knots[0].x;
130 d.y = 2.0 * firstControlPoints[0].y - knots[0].y;
131 secondControlPoints.push_back (d);
136 // Calculate first Bezier control points
137 // Right hand side vector
139 std::vector<double> rhs;
143 // Set right hand side X values
145 for (Points::size_type i = 1; i < n - 1; ++i) {
146 rhs[i] = 4 * knots[i].x + 2 * knots[i + 1].x;
148 rhs[0] = knots[0].x + 2 * knots[1].x;
149 rhs[n - 1] = (8 * knots[n - 1].x + knots[n].x) / 2.0;
151 // Get first control points X-values
152 double* x = solve (rhs);
154 // Set right hand side Y values
155 for (Points::size_type i = 1; i < n - 1; ++i) {
156 rhs[i] = 4 * knots[i].y + 2 * knots[i + 1].y;
158 rhs[0] = knots[0].y + 2 * knots[1].y;
159 rhs[n - 1] = (8 * knots[n - 1].y + knots[n].y) / 2.0;
161 // Get first control points Y-values
162 double* y = solve (rhs);
164 for (Points::size_type i = 0; i < n; ++i) {
166 firstControlPoints.push_back (Duple (x[i], y[i]));
169 secondControlPoints.push_back (Duple (2 * knots [i + 1].x - x[i + 1],
170 2 * knots[i + 1].y - y[i + 1]));
172 secondControlPoints.push_back (Duple ((knots [n].x + x[n - 1]) / 2,
173 (knots[n].y + y[n - 1]) / 2));
181 /** Solves a tridiagonal system for one of coordinates (x or y)
182 * of first Bezier control points.
186 Curve::solve (std::vector<double> const & rhs)
188 std::vector<double>::size_type n = rhs.size();
189 double* x = new double[n]; // Solution vector.
190 double* tmp = new double[n]; // Temp workspace.
196 for (std::vector<double>::size_type i = 1; i < n; i++) {
197 // Decomposition and forward substitution.
199 b = (i < n - 1 ? 4.0 : 3.5) - tmp[i];
200 x[i] = (rhs[i] - x[i - 1]) / b;
203 for (std::vector<double>::size_type i = 1; i < n; i++) {
205 x[n - i - 1] -= tmp[n - i] * x[n - i];