-
- // Calculate first Bezier control points
- // Right hand side vector
-
- std::vector<double> rhs;
-
- rhs.assign (n, 0);
-
- // Set right hand side X values
-
- for (Points::size_type i = 1; i < n - 1; ++i) {
- rhs[i] = 4 * knots[i].x + 2 * knots[i + 1].x;
- }
- rhs[0] = knots[0].x + 2 * knots[1].x;
- rhs[n - 1] = (8 * knots[n - 1].x + knots[n].x) / 2.0;
-
- // Get first control points X-values
- double* x = solve (rhs);
-
- // Set right hand side Y values
- for (Points::size_type i = 1; i < n - 1; ++i) {
- rhs[i] = 4 * knots[i].y + 2 * knots[i + 1].y;
- }
- rhs[0] = knots[0].y + 2 * knots[1].y;
- rhs[n - 1] = (8 * knots[n - 1].y + knots[n].y) / 2.0;
-
- // Get first control points Y-values
- double* y = solve (rhs);
-
- for (Points::size_type i = 0; i < n; ++i) {
-
- firstControlPoints.push_back (Duple (x[i], y[i]));
-
- if (i < n - 1) {
- secondControlPoints.push_back (Duple (2 * knots [i + 1].x - x[i + 1],
- 2 * knots[i + 1].y - y[i + 1]));
- } else {
- secondControlPoints.push_back (Duple ((knots [n].x + x[n - 1]) / 2,
- (knots[n].y + y[n - 1]) / 2));
+
+ Rect self = item_to_window (_bounding_box.get());
+ boost::optional<Rect> d = self.intersection (area);
+ assert (d);
+ Rect draw = d.get ();
+
+ /* Our approach is to always draw n_segments across our total size.
+ *
+ * This is very inefficient if we are asked to only draw a small
+ * section of the curve. For now we rely on cairo clipping to help
+ * with this.
+ */
+
+
+ setup_outline_context (context);
+
+ if (_points.size() == 2) {
+
+ /* straight line */
+
+ Duple window_space;
+
+ window_space = item_to_window (_points.front());
+ context->move_to (window_space.x, window_space.y);
+ window_space = item_to_window (_points.back());
+ context->line_to (window_space.x, window_space.y);
+
+
+ switch (curve_fill) {
+ case None:
+ context->stroke();
+ break;
+ case Inside:
+ context->stroke_preserve ();
+ window_space = item_to_window (Duple(_points.back().x, draw.height()));
+ context->line_to (window_space.x, window_space.y);
+ window_space = item_to_window (Duple(_points.front().x, draw.height()));
+ context->line_to (window_space.x, window_space.y);
+ context->close_path();
+ setup_fill_context(context);
+ context->fill ();
+ break;
+ case Outside:
+ context->stroke_preserve ();
+ window_space = item_to_window (Duple(_points.back().x, 0.0));
+ context->line_to (window_space.x, window_space.y);
+ window_space = item_to_window (Duple(_points.front().x, 0.0));
+ context->line_to (window_space.x, window_space.y);
+ context->close_path();
+ setup_fill_context(context);
+ context->fill ();
+ break;
+ }
+
+ } else {
+
+ /* curve of at least 3 points */
+
+ /* x-axis limits of the curve, in window space coordinates */
+
+ Duple w1 = item_to_window (Duple (_points.front().x, 0.0));
+ Duple w2 = item_to_window (Duple (_points.back().x, 0.0));
+
+ /* clamp actual draw to area bound by points, rather than our bounding box which is slightly different */
+
+ context->save ();
+ context->rectangle (draw.x0, draw.y0, draw.width(), draw.height());
+ context->clip ();
+
+ /* expand drawing area by several pixels on each side to avoid cairo stroking effects at the boundary.
+ they will still occur, but cairo's clipping will hide them.
+ */
+
+ draw = draw.expand (4.0);
+
+ /* now clip it to the actual points in the curve */
+
+ if (draw.x0 < w1.x) {
+ draw.x0 = w1.x;
+ }
+
+ if (draw.x1 >= w2.x) {
+ draw.x1 = w2.x;
+ }
+
+ /* find left and right-most sample */
+ Duple window_space;
+ Points::size_type left = 0;
+ Points::size_type right = n_samples;
+
+ for (Points::size_type idx = 0; idx < n_samples - 1; ++idx) {
+ left = idx;
+ window_space = item_to_window (Duple (samples[idx].x, 0.0));
+ if (window_space.x >= draw.x0) break;