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::render_path (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
108 PolyItem::render_curve (area, context, first_control_points, second_control_points);
112 Curve::compute_control_points (Points const& knots,
113 Points& firstControlPoints,
114 Points& secondControlPoints)
116 Points::size_type n = knots.size() - 1;
123 /* Special case: Bezier curve should be a straight line. */
127 d.x = (2.0 * knots[0].x + knots[1].x) / 3;
128 d.y = (2.0 * knots[0].y + knots[1].y) / 3;
129 firstControlPoints.push_back (d);
131 d.x = 2.0 * firstControlPoints[0].x - knots[0].x;
132 d.y = 2.0 * firstControlPoints[0].y - knots[0].y;
133 secondControlPoints.push_back (d);
138 // Calculate first Bezier control points
139 // Right hand side vector
141 std::vector<double> rhs;
145 // Set right hand side X values
147 for (Points::size_type i = 1; i < n - 1; ++i) {
148 rhs[i] = 4 * knots[i].x + 2 * knots[i + 1].x;
150 rhs[0] = knots[0].x + 2 * knots[1].x;
151 rhs[n - 1] = (8 * knots[n - 1].x + knots[n].x) / 2.0;
153 // Get first control points X-values
154 double* x = solve (rhs);
156 // Set right hand side Y values
157 for (Points::size_type i = 1; i < n - 1; ++i) {
158 rhs[i] = 4 * knots[i].y + 2 * knots[i + 1].y;
160 rhs[0] = knots[0].y + 2 * knots[1].y;
161 rhs[n - 1] = (8 * knots[n - 1].y + knots[n].y) / 2.0;
163 // Get first control points Y-values
164 double* y = solve (rhs);
166 for (Points::size_type i = 0; i < n; ++i) {
168 firstControlPoints.push_back (Duple (x[i], y[i]));
171 secondControlPoints.push_back (Duple (2 * knots [i + 1].x - x[i + 1],
172 2 * knots[i + 1].y - y[i + 1]));
174 secondControlPoints.push_back (Duple ((knots [n].x + x[n - 1]) / 2,
175 (knots[n].y + y[n - 1]) / 2));
183 /** Solves a tridiagonal system for one of coordinates (x or y)
184 * of first Bezier control points.
188 Curve::solve (std::vector<double> const & rhs)
190 std::vector<double>::size_type n = rhs.size();
191 double* x = new double[n]; // Solution vector.
192 double* tmp = new double[n]; // Temp workspace.
198 for (std::vector<double>::size_type i = 1; i < n; i++) {
199 // Decomposition and forward substitution.
201 b = (i < n - 1 ? 4.0 : 3.5) - tmp[i];
202 x[i] = (rhs[i] - x[i - 1]) / b;
205 for (std::vector<double>::size_type i = 1; i < n; i++) {
207 x[n - i - 1] -= tmp[n - i] * x[n - i];