Only show user-presets in favorite sidebar
[ardour.git] / libs / canvas / framed_curve.cc
index 6bd2f740b0d435fe5666374720573062307d97e2..2d22387fa4b506d9410c28eed0012fd80036ee70 100644 (file)
@@ -31,7 +31,7 @@ FramedCurve::FramedCurve (Canvas* c)
        : PolyItem (c)
        , n_samples (0)
        , points_per_segment (16)
-       , curve_fill (None)
+       , curve_fill (Inside)
 {
 }
 
@@ -39,7 +39,7 @@ FramedCurve::FramedCurve (Item* parent)
        : PolyItem (parent)
        , n_samples (0)
        , points_per_segment (16)
-       , curve_fill (None)
+       , curve_fill (Inside)
 {
 }
 
@@ -55,7 +55,7 @@ FramedCurve::set_points_per_segment (uint32_t n)
           just need to schedule a redraw rather than notify the parent of any
           changes
        */
-       points_per_segment = n;
+       points_per_segment = max (n, (uint32_t) 3);
        interpolate ();
        redraw ();
 }
@@ -85,8 +85,14 @@ FramedCurve::interpolate ()
        }
        samples.clear ();
 
-       InterpolatedCurve::interpolate (curve_points, points_per_segment, CatmullRomCentripetal, false, samples);
-       n_samples = samples.size();
+       if (_points.size() == 3) {
+               samples.push_back (curve_points.front());
+               samples.push_back (curve_points.back());
+               n_samples = 2;
+       } else {
+               InterpolatedCurve::interpolate (curve_points, points_per_segment, CatmullRomCentripetal, false, samples);
+               n_samples = samples.size();
+       }
 }
 
 void
@@ -96,10 +102,10 @@ FramedCurve::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) c
                return;
        }
 
-       Rect self = item_to_window (_bounding_box.get());
-       boost::optional<Rect> d = self.intersection (area);
+       Rect self = item_to_window (_bounding_box);
+       Rect d = self.intersection (area);
        assert (d);
-       Rect draw = d.get ();
+       Rect draw = d;
 
        /* Our approach is to always draw n_segments across our total size.
         *
@@ -108,22 +114,68 @@ FramedCurve::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) c
         * with this.
         */
 
+       /* x-axis limits of the curve, in window space coordinates */
 
-       setup_outline_context (context);
+       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;
+       }
+
+       setup_outline_context (context);
        if (_points.size() == 3) {
 
                /* straight line */
 
                Duple window_space;
                Points::const_iterator it = _points.begin();
+
+               Duple first_point = Duple (0.0, 0.0);
+               Duple last_point = Duple (0.0, 0.0);
+
                window_space = item_to_window (*it);
-               context->move_to (window_space.x, window_space.y);
+               if (window_space.x <= draw.x0) {
+                       first_point = Duple (draw.x0, window_space.y);
+               } else {
+                       first_point = Duple (window_space.x, window_space.y);
+               }
+               context->move_to (first_point.x, first_point.y);
+
                ++it;
-               window_space = item_to_window (*it);
-               context->line_to (window_space.x, window_space.y);
-               window_space = item_to_window (_points.back());
-               context->line_to (window_space.x, window_space.y);
+               window_space = item_to_window (*it, false);
+               if (window_space.x <= draw.x0) {
+                       context->line_to (draw.x0, window_space.y);
+               } else {
+                       context->line_to (window_space.x, window_space.y);
+               }
+
+               window_space = item_to_window (_points.back(), false);
+               if (window_space.x >= draw.x1) {
+                       last_point = Duple (draw.x1, window_space.y);
+               } else {
+                       last_point = Duple (window_space.x, window_space.y);
+               }
+
+               context->line_to (last_point.x, last_point.y);
 
                switch (curve_fill) {
                        case None:
@@ -131,10 +183,10 @@ FramedCurve::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) c
                                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.back().x, draw.height()));
-                               context->line_to (window_space.x, window_space.y);
+                               window_space = item_to_window (Duple(0.0, draw.height()));
+                               context->line_to (last_point.x, window_space.y);
+                               window_space = item_to_window (Duple(0.0, draw.height()));
+                               context->line_to (first_point.x, window_space.y);
                                context->close_path();
                                setup_fill_context(context);
                                context->fill ();
@@ -142,83 +194,71 @@ FramedCurve::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) c
                        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);
+                               context->line_to (last_point.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->line_to (first_point.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;
+               Points::size_type right = n_samples - 1;
 
-               for (Points::size_type idx = 0; idx < n_samples; ++idx) {
+               for (Points::size_type idx = 0; idx < n_samples - 1; ++idx) {
                        window_space = item_to_window (Duple (samples[idx].x, 0.0));
-                       if (window_space.x >= draw.x0) break;
+                       if (window_space.x >= draw.x0) {
+                               break;
+                       }
                        left = idx;
                }
 
-               for (Points::size_type idx = n_samples; idx > left + 2; --idx) {
+               for (Points::size_type idx = left; idx < n_samples - 1; ++idx) {
                        window_space = item_to_window (Duple (samples[idx].x, 0.0));
-                       if (window_space.x <= draw.x1) break;
-                       right = idx;
+                       if (window_space.x > draw.x1) {
+                               right = idx;
+                               break;
+                       }
                }
 
-               window_space = item_to_window (*_points.begin());
+               const Duple first_sample = Duple (samples[left].x, samples[left].y);
+
+               /* move to the first sample's x and the draw height */
+               window_space = item_to_window (Duple (first_sample.x, draw.height()));
                context->move_to (window_space.x, window_space.y);
-               /* draw line between samples */
-               window_space = item_to_window (Duple (samples[left].x, samples[left].y));
-               context->line_to (window_space.x, window_space.y);
 
-               for (uint32_t idx = left; idx < right; ++idx) {
+               /* draw line to first sample and then between samples */
+               for (uint32_t idx = left; idx <= right; ++idx) {
                        window_space = item_to_window (Duple (samples[idx].x, samples[idx].y), false);
                        context->line_to (window_space.x, window_space.y);
                }
 
+               /* a redraw may have been requested between the last sample and the last point.
+                  if so, draw a line to the last _point.
+               */
+               Duple last_sample = Duple (samples[right].x, samples[right].y);
+
+               if (draw.x1 > last_sample.x) {
+                       last_sample = Duple (_points.back().x, _points.back().y);
+                       window_space = item_to_window (last_sample, false);
+                       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 (samples[right-1].x, draw.height()));
+                               /* close the sample, possibly using the last _point's x rather than samples[right].x */
+                               window_space = item_to_window (Duple (last_sample.x, draw.height()));
                                context->line_to (window_space.x, window_space.y);
-                               window_space = item_to_window (Duple (samples[left].x, draw.height()));
+                               window_space = item_to_window (Duple (first_sample.x, draw.height()));
                                context->line_to (window_space.x, window_space.y);
                                context->close_path();
                                setup_fill_context(context);
@@ -226,17 +266,17 @@ FramedCurve::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) c
                                break;
                        case Outside:
                                context->stroke_preserve ();
-                               window_space = item_to_window (Duple (samples[right-1].x, 0.0));
+                               window_space = item_to_window (Duple (last_sample.x, 0.0));
                                context->line_to (window_space.x, window_space.y);
-                               window_space = item_to_window (Duple (samples[left].x, 0.0));
+                               window_space = item_to_window (Duple (first_sample.x, 0.0));
                                context->line_to (window_space.x, window_space.y);
                                context->close_path();
                                setup_fill_context(context);
                                context->fill ();
                                break;
                }
-               context->restore ();
        }
+       context->restore ();
 
 #if 0
        /* add points */