]> git.donarmstrong.com Git - lilypond.git/blobdiff - lily/skyline.cc
Imported Upstream version 2.16.0
[lilypond.git] / lily / skyline.cc
index e55ba3a6637024de8e0a9fb36b28e53298d2098f..0250fc07f4743ba44e7bc1ac8af23ed6057aa435 100644 (file)
@@ -1,12 +1,25 @@
-/* skyline.cc -- implement the Skyline class
+/*
+  This file is part of LilyPond, the GNU music typesetter.
+
+  Copyright (C) 2006--2012 Joe Neeman <joeneeman@gmail.com>
+
+  LilyPond is free software: you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation, either version 3 of the License, or
+  (at your option) any later version.
+
+  LilyPond is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
 
-   source file of the GNU LilyPond music typesetter
-   (c) 2006--2007 Joe Neeman <joeneeman@gmail.com>
+  You should have received a copy of the GNU General Public License
+  along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 #include "skyline.hh"
 #include <deque>
+#include <cstdio>
 
 #include "ly-smobs.icc"
 
@@ -59,6 +72,16 @@ Skyline::print () const
   print_buildings (buildings_);
 }
 
+void
+Skyline::print_points () const
+{
+  vector<Offset> ps (to_points (X_AXIS));
+
+  for (vsize i = 0; i < ps.size (); i++)
+    printf ("(%f,%f)%s", ps[i][X_AXIS], ps[i][Y_AXIS],
+            (i % 2) == 1 ? "\n" : " ");
+}
+
 Building::Building (Real start, Real start_height, Real end_height, Real end)
 {
   if (isinf (start) || isinf (end))
@@ -96,10 +119,10 @@ Building::precompute (Real start, Real start_height, Real end_height, Real end)
     y_intercept_ = start_height - slope_ * start;
 }
 
-Real 
+Real
 Building::height (Real x) const
 {
-  return isinf (x) ? y_intercept_ : slope_*x + y_intercept_;
+  return isinf (x) ? y_intercept_ : slope_ * x + y_intercept_;
 }
 
 void
@@ -145,15 +168,15 @@ first_intersection (Building const &b, list<Building> *const s, Real start_x)
     {
       Building c = s->front ();
       if (c.conceals (b, start_x))
-       return start_x;
+        return start_x;
 
       Real i = b.intersection_x (c);
       if (i > start_x && i <= b.end_ && i <= c.end_)
-       return i;
+        return i;
 
       start_x = c.end_;
       if (b.end_ > c.end_)
-       s->pop_front ();
+        s->pop_front ();
     }
   return b.end_;
 }
@@ -167,12 +190,12 @@ Building::conceals (Building const &other, Real x) const
   /* their slopes were not equal, so there is an intersection point */
   Real i = intersection_x (other);
   return (i <= x && slope_ > other.slope_)
-    || (i > x && slope_ < other.slope_);
+         || (i > x && slope_ < other.slope_);
 }
 
 void
 Skyline::internal_merge_skyline (list<Building> *s1, list<Building> *s2,
-                                list<Building> *const result)
+                                 list<Building> *const result)
 {
   if (s1->empty () || s2->empty ())
     {
@@ -184,26 +207,26 @@ Skyline::internal_merge_skyline (list<Building> *s1, list<Building> *s2,
   while (!s1->empty ())
     {
       if (s2->front ().conceals (s1->front (), x))
-       swap (s1, s2);
+        swap (s1, s2);
 
       Building b = s1->front ();
       Real end = first_intersection (b, s2, x);
 
       if (s2->empty ())
-       {
-         result->push_front (b);
-         break;
-       }
+        {
+          result->push_front (b);
+          break;
+        }
 
       /* only include buildings wider than epsilon */
       if (end > x + EPS)
-       {
-         b.leading_part (end);
-         result->push_front (b);
-       }
+        {
+          b.leading_part (end);
+          result->push_front (b);
+        }
 
       if (end >= s1->front ().end_)
-       s1->pop_front ();
+        s1->pop_front ();
 
       x = end;
     }
@@ -216,16 +239,20 @@ empty_skyline (list<Building> *const ret)
   ret->push_front (Building (-infinity_f, -infinity_f, -infinity_f, infinity_f));
 }
 
+/*
+  Given Building 'b' with starting wall location 'start', extend each side
+  with a sloped roofline of width 'horizon_padding'; put the skyline in 'ret'
+*/
 static void
 single_skyline (Building b, Real start, Real horizon_padding, list<Building> *const ret)
 {
   bool sloped_neighbours = horizon_padding > 0 && !isinf (start) && !isinf (b.end_);
   if (!isinf (b.end_))
     ret->push_front (Building (b.end_ + horizon_padding, -infinity_f,
-                              -infinity_f, infinity_f));
+                               -infinity_f, infinity_f));
   if (sloped_neighbours)
     ret->push_front (b.sloped_neighbour (start, horizon_padding, RIGHT));
-  
+
   if (b.end_ > start + EPS)
     ret->push_front (b);
 
@@ -234,7 +261,7 @@ single_skyline (Building b, Real start, Real horizon_padding, list<Building> *co
 
   if (!isinf (start))
     ret->push_front (Building (-infinity_f, -infinity_f,
-                              -infinity_f, start - horizon_padding));
+                               -infinity_f, start - horizon_padding));
 }
 
 /* remove a non-overlapping set of boxes from BOXES and build a skyline
@@ -250,21 +277,21 @@ non_overlapping_skyline (list<Box> *const boxes, Real horizon_padding, Axis hori
       Interval iv = (*i)[horizon_axis];
 
       if (iv[LEFT] - horizon_padding < last_end)
-       {
-         i++;
-         continue;
-       }
+        {
+          i++;
+          continue;
+        }
 
       if (iv[LEFT] - horizon_padding > last_end + EPS)
-       result.push_front (Building (last_end, -infinity_f, -infinity_f, iv[LEFT] - horizon_padding));
+        result.push_front (Building (last_end, -infinity_f, -infinity_f, iv[LEFT] - 2 * horizon_padding));
 
       Building b (*i, horizon_padding, horizon_axis, sky);
       bool sloped_neighbours = horizon_padding > 0 && !isinf (iv.length ());
       if (sloped_neighbours)
-       result.push_front (b.sloped_neighbour (iv[LEFT] - horizon_padding, horizon_padding, LEFT));
+        result.push_front (b.sloped_neighbour (iv[LEFT] - horizon_padding, horizon_padding, LEFT));
       result.push_front (b);
       if (sloped_neighbours)
-       result.push_front (b.sloped_neighbour (iv[LEFT] - horizon_padding, horizon_padding, RIGHT));
+        result.push_front (b.sloped_neighbour (iv[LEFT] - horizon_padding, horizon_padding, RIGHT));
 
       list<Box>::iterator j = i++;
       boxes->erase (j);
@@ -286,7 +313,7 @@ public:
     a_ = a;
   }
 
-  bool operator() (Box const &b1, Box const &b2)
+  bool operator () (Box const &b1, Box const &b2)
   {
     return b1[a_][LEFT] < b2[a_][LEFT];
   }
@@ -307,7 +334,8 @@ Skyline::internal_build_skyline (list<Box> *boxes, Real horizon_padding, Axis ho
     {
       list<Building> result;
       single_skyline (Building (boxes->front (), horizon_padding, horizon_axis, sky),
-                     boxes->front ()[horizon_axis][LEFT], horizon_axis, &result);
+                      boxes->front ()[horizon_axis][LEFT] - horizon_padding,
+                      horizon_padding, &result);
       return result;
     }
 
@@ -324,7 +352,7 @@ Skyline::internal_build_skyline (list<Box> *boxes, Real horizon_padding, Axis ho
       list<Building> one = partials.front ();
       partials.pop_front ();
       if (partials.empty ())
-       return one;
+        return one;
 
       list<Building> two = partials.front ();
       partials.pop_front ();
@@ -344,7 +372,7 @@ Skyline::Skyline ()
 Skyline::Skyline (Skyline const &src)
 {
   sky_ = src.sky_;
+
   /* doesn't a list's copy constructor do this? -- jneem */
   for (list<Building>::const_iterator i = src.buildings_.begin ();
        i != src.buildings_.end (); i++)
@@ -359,6 +387,37 @@ Skyline::Skyline (Direction sky)
   empty_skyline (&buildings_);
 }
 
+/*
+  build padded skyline from an existing skyline with padding
+  added to it.
+*/
+
+Skyline::Skyline (Skyline const &src, Real horizon_padding, Axis /*a*/)
+{
+  /*
+     We extract boxes from the skyline, then build a new skyline from
+     the boxes.
+     A box is created for every horizontal portion of the skyline
+     Because skylines are defined positive, and then inverted if they
+     are to be down-facing, we create the new skyline in the UP
+     direction, then give it the down direction if needed.
+  */
+  Real start = -infinity_f;
+  list<Box> boxes;
+
+  // establish a baseline box
+  // FIXME: This has hardcoded logic, assuming a == X_AXIS!
+  boxes.push_back (Box (Interval (-infinity_f, infinity_f),
+                        Interval (0, 0)));
+  list<Building>::const_iterator end = src.buildings_.end ();
+  for (list<Building>::const_iterator i = src.buildings_.begin (); i != end; start = i->end_, i++)
+    if ((i->slope_ == 0) && !isinf (i->y_intercept_))
+      boxes.push_back (Box (Interval (start, i->end_),
+                            Interval (-infinity_f, i->y_intercept_)));
+  buildings_ = internal_build_skyline (&boxes, horizon_padding, X_AXIS, UP);
+  sky_ = src.sky_;
+}
+
 /*
   build skyline from a set of boxes. If horizon_padding > 0, expand all the boxes
   by that amount and add 45-degree sloped boxes to the edges of each box (of
@@ -379,9 +438,9 @@ Skyline::Skyline (vector<Box> const &boxes, Real horizon_padding, Axis horizon_a
       Interval iv = boxes[i][horizon_axis];
       iv.widen (horizon_padding);
       if (iv.length () > EPS && !boxes[i][vert_axis].is_empty ())
-       filtered_boxes.push_front (boxes[i]);
+        filtered_boxes.push_front (boxes[i]);
     }
-  
+
   buildings_ = internal_build_skyline (&filtered_boxes, horizon_padding, horizon_axis, sky);
 }
 
@@ -389,7 +448,8 @@ Skyline::Skyline (Box const &b, Real horizon_padding, Axis horizon_axis, Directi
 {
   sky_ = sky;
   Building front (b, horizon_padding, horizon_axis, sky);
-  single_skyline (front, b[horizon_axis][LEFT], horizon_padding, &buildings_);
+  single_skyline (front, b[horizon_axis][LEFT] - horizon_padding,
+                  horizon_padding, &buildings_);
 }
 
 void
@@ -409,6 +469,13 @@ Skyline::insert (Box const &b, Real horizon_padding, Axis a)
   list<Building> other_bld;
   list<Building> my_bld;
 
+  if (isnan (b[other_axis (a)][LEFT])
+      || isnan (b[other_axis (a)][RIGHT]))
+    {
+      programming_error ("insane box for skyline");
+      return;
+    }
+
   /* do the same filtering as in Skyline (vector<Box> const&, etc.) */
   Interval iv = b[a];
   iv.widen (horizon_padding);
@@ -416,7 +483,8 @@ Skyline::insert (Box const &b, Real horizon_padding, Axis a)
     return;
 
   my_bld.splice (my_bld.begin (), buildings_);
-  single_skyline (Building (b, horizon_padding, a, sky_), b[a][LEFT], horizon_padding, &other_bld);
+  single_skyline (Building (b, horizon_padding, a, sky_), b[a][LEFT] - horizon_padding,
+                  horizon_padding, &other_bld);
   internal_merge_skyline (&other_bld, &my_bld, &buildings_);
 }
 
@@ -440,26 +508,74 @@ Skyline::shift (Real s)
 }
 
 Real
-Skyline::distance (Skyline const &other) const
+Skyline::distance (Skyline const &other, Real horizon_padding) const
+{
+  Real dummy;
+  return internal_distance (other, horizon_padding, &dummy);
+}
+
+Real
+Skyline::touching_point (Skyline const &other, Real horizon_padding) const
+{
+  Real touch;
+  internal_distance (other, horizon_padding, &touch);
+  return touch;
+}
+
+Real
+Skyline::internal_distance (Skyline const &other, Real horizon_padding, Real *touch_point) const
 {
   assert (sky_ == -other.sky_);
-  list<Building>::const_iterator i = buildings_.begin ();
-  list<Building>::const_iterator j = other.buildings_.begin ();
+
+  Skyline const *padded_this = this;
+  Skyline const *padded_other = &other;
+  bool created_tmp_skylines = false;
+
+  /*
+    For systems, padding is not added at creation time.  Padding is
+    added to AxisGroup objects when outside-staff objects are added.
+    Thus, when we want to place systems with horizontal padding,
+    we do it at distance calculation time.
+  */
+  if (horizon_padding != 0.0)
+    {
+      padded_this = new Skyline (*padded_this, horizon_padding, X_AXIS);
+      padded_other = new Skyline (*padded_other, horizon_padding, X_AXIS);
+      created_tmp_skylines = true;
+    }
+
+  list<Building>::const_iterator i = padded_this->buildings_.begin ();
+  list<Building>::const_iterator j = padded_other->buildings_.begin ();
 
   Real dist = -infinity_f;
   Real start = -infinity_f;
-  while (i != buildings_.end () && j != other.buildings_.end ())
+  Real touch = -infinity_f;
+  while (i != padded_this->buildings_.end () && j != padded_other->buildings_.end ())
     {
       Real end = min (i->end_, j->end_);
       Real start_dist = i->height (start) + j->height (start);
       Real end_dist = i->height (end) + j->height (end);
       dist = max (dist, max (start_dist, end_dist));
+
+      if (end_dist == dist)
+        touch = end;
+      else if (start_dist == dist)
+        touch = start;
+
       if (i->end_ <= j->end_)
-       i++;
+        i++;
       else
-       j++;
+        j++;
       start = end;
     }
+
+  if (created_tmp_skylines)
+    {
+      delete padded_this;
+      delete padded_other;
+    }
+
+  *touch_point = touch;
   return dist;
 }
 
@@ -472,7 +588,7 @@ Skyline::height (Real airplane) const
   for (i = buildings_.begin (); i != buildings_.end (); i++)
     {
       if (i->end_ >= airplane)
-       return sky_ * i->height (airplane);
+        return sky_ * i->height (airplane);
     }
 
   assert (0);
@@ -487,6 +603,14 @@ Skyline::max_height () const
   return sky_ * distance (s);
 }
 
+Real
+Skyline::max_height_position () const
+{
+  Skyline s (-sky_);
+  s.set_minimum_height (0);
+  return touching_point (s);
+}
+
 void
 Skyline::set_minimum_height (Real h)
 {
@@ -495,9 +619,8 @@ Skyline::set_minimum_height (Real h)
   merge (s);
 }
 
-
 vector<Offset>
-Skyline::to_points (Axis a) const
+Skyline::to_points (Axis horizon_axis) const
 {
   vector<Offset> out;
 
@@ -510,9 +633,9 @@ Skyline::to_points (Axis a) const
       start = i->end_;
     }
 
-  if (a == Y_AXIS)
+  if (horizon_axis == Y_AXIS)
     for (vsize i = 0; i < out.size (); i++)
-      out[i] = Offset (out[i][Y_AXIS], out[i][X_AXIS]);
+      out[i] = out[i].swapped ();
 
   return out;
 }
@@ -520,86 +643,27 @@ Skyline::to_points (Axis a) const
 bool
 Skyline::is_empty () const
 {
-  return buildings_.empty ();
-}
-
-Skyline_pair::Skyline_pair ()
-  : skylines_ (Skyline (DOWN), Skyline (UP))
-{
-}
-
-Skyline_pair::Skyline_pair (vector<Box> const &boxes, Real padding, Axis a)
-  : skylines_ (Skyline (boxes, padding, a, DOWN), Skyline (boxes, padding, a, UP))
-{
-}
-
-Skyline_pair::Skyline_pair (Box const &b, Real padding, Axis a)
-  : skylines_ (Skyline (b, padding, a, DOWN), Skyline (b, padding, a, UP))
-{
-}
-
-void
-Skyline_pair::raise (Real r)
-{
-  skylines_[UP].raise (r);
-  skylines_[DOWN].raise (r);
-}
-
-void
-Skyline_pair::shift (Real r)
-{
-  skylines_[UP].shift (r);
-  skylines_[DOWN].shift (r);
+  Building b = buildings_.front ();
+  return b.end_ == infinity_f && b.y_intercept_ == -infinity_f;
 }
 
 void
-Skyline_pair::insert (Box const &b, Real padding, Axis a)
-{
-  skylines_[UP].insert (b, padding, a);
-  skylines_[DOWN].insert (b, padding, a);
-}
-
-void
-Skyline_pair::merge (Skyline_pair const &other)
-{
-  skylines_[UP].merge (other[UP]);
-  skylines_[DOWN].merge (other[DOWN]);
-}
-
-bool
-Skyline_pair::is_empty () const
-{
-  return skylines_[UP].is_empty ()
-    && skylines_[DOWN].is_empty ();
-}
-
-Skyline&
-Skyline_pair::operator [] (Direction d)
-{
-  return skylines_[d];
-}
-
-Skyline const&
-Skyline_pair::operator [] (Direction d) const
+Skyline::clear ()
 {
-  return skylines_[d];
+  buildings_.clear ();
+  empty_skyline (&buildings_);
 }
 
 /****************************************************************/
 
-
 IMPLEMENT_SIMPLE_SMOBS (Skyline);
 IMPLEMENT_TYPE_P (Skyline, "ly:skyline?");
 IMPLEMENT_DEFAULT_EQUAL_P (Skyline);
 
-IMPLEMENT_SIMPLE_SMOBS (Skyline_pair);
-IMPLEMENT_TYPE_P (Skyline_pair, "ly:skyline-pair?");
-IMPLEMENT_DEFAULT_EQUAL_P (Skyline_pair);
-
 SCM
-Skyline::mark_smob (SCM)
+Skyline::mark_smob (SCM s)
 {
-  ASSERT_LIVE_IS_ALLOWED ();
+  ASSERT_LIVE_IS_ALLOWED (s);
   return SCM_EOL;
 }
 
@@ -614,18 +678,60 @@ Skyline::print_smob (SCM s, SCM port, scm_print_state *)
   return 1;
 }
 
+MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Skyline, get_touching_point, 3, 1, "")
 SCM
-Skyline_pair::mark_smob (SCM)
+Skyline::get_touching_point (SCM skyline_scm, SCM other_skyline_scm, SCM horizon_padding_scm)
 {
-  return SCM_EOL;
+  LY_ASSERT_SMOB (Skyline, other_skyline_scm, 1);
+
+  Real horizon_padding = 0;
+  if (horizon_padding_scm != SCM_UNDEFINED)
+    {
+      LY_ASSERT_TYPE (scm_is_number, horizon_padding_scm, 3);
+      horizon_padding = scm_to_double (horizon_padding_scm);
+    }
+
+  Skyline *skyline = Skyline::unsmob (skyline_scm);
+  Skyline *other_skyline = Skyline::unsmob (other_skyline_scm);
+  return scm_from_double (skyline->touching_point (*other_skyline, horizon_padding));
 }
 
-int
-Skyline_pair::print_smob (SCM s, SCM port, scm_print_state *)
+MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Skyline, get_distance, 3, 1, "")
+SCM
+Skyline::get_distance (SCM skyline_scm, SCM other_skyline_scm, SCM horizon_padding_scm)
 {
-  Skyline_pair *r = (Skyline_pair *) SCM_CELL_WORD_1 (s);
-  (void) r;
+  LY_ASSERT_SMOB (Skyline, other_skyline_scm, 1);
 
-  scm_puts ("#<Skyline-pair>", port);
-  return 1;
+  Real horizon_padding = 0;
+  if (horizon_padding_scm != SCM_UNDEFINED)
+    {
+      LY_ASSERT_TYPE (scm_is_number, horizon_padding_scm, 3);
+      horizon_padding = scm_to_double (horizon_padding_scm);
+    }
+
+  Skyline *skyline = Skyline::unsmob (skyline_scm);
+  Skyline *other_skyline = Skyline::unsmob (other_skyline_scm);
+  return scm_from_double (skyline->distance (*other_skyline, horizon_padding));
+}
+
+MAKE_SCHEME_CALLBACK (Skyline, get_max_height, 1)
+SCM
+Skyline::get_max_height (SCM skyline_scm)
+{
+  return scm_from_double (Skyline::unsmob (skyline_scm)->max_height ());
+}
+
+MAKE_SCHEME_CALLBACK (Skyline, get_max_height_position, 1)
+SCM
+Skyline::get_max_height_position (SCM skyline_scm)
+{
+  return scm_from_double (Skyline::unsmob (skyline_scm)->max_height_position ());
+}
+
+MAKE_SCHEME_CALLBACK (Skyline, get_height, 2)
+SCM
+Skyline::get_height (SCM skyline_scm, SCM x_scm)
+{
+  Real x = robust_scm2double (x_scm, 0.0);
+  return scm_from_double (Skyline::unsmob (skyline_scm)->height (x));
 }