]> git.donarmstrong.com Git - lilypond.git/blobdiff - lily/skyline.cc
Run grand-replace (issue 3765)
[lilypond.git] / lily / skyline.cc
index 9bbd7e9752ec76ed93a3b9dec396a3e334297333..33d2823d47e0d752934558f75ea74e90e85ead41 100644 (file)
@@ -1,7 +1,7 @@
 /*
   This file is part of LilyPond, the GNU music typesetter.
 
-  Copyright (C) 2006--2012 Joe Neeman <joeneeman@gmail.com>
+  Copyright (C) 2006--2014 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
    a DOWN skyline with an UP skyline, we need to flip the DOWN skyline first.
    This means that the merging routine doesn't need to be aware of direction,
    but the distance routine does.
-*/
 
-/* If we start including very thin buildings, numerical accuracy errors can
-   arise. Therefore, we ignore all buildings that are less than epsilon wide. */
-#define EPS 1e-5
+   From 2007 through 2012, buildings of width less than EPS were discarded,
+   citing numerical accuracy concerns.  We remember that floating point
+   comparisons of nearly-equal values can be affected by rounding error.
+   Also, some target machines use the x87 floating point unit, which provides
+   extended precision for intermediate results held in registers. On this type
+   of hardware comparisons such as
+     double c = 1.0/3.0; boolean compare = (c == 1.0/3.0)
+   could go either way because the 1.0/3.0 is allowed to be kept
+   higher precision than the variable 'c'.
+   Alert to these considerations, we now accept buildings of zero-width.
+*/
 
 static void
 print_buildings (list<Building> const &b)
@@ -118,6 +125,12 @@ Building::precompute (Real start, Real start_height, Real end_height, Real end)
       assert (start_height == end_height);
       y_intercept_ = start_height;
     }
+  else if (fabs(slope_) > 1e6)
+    // too steep to be stored in slope-intercept form, given round-off error
+    {
+      slope_ = 0.0;
+      y_intercept_ = max(start_height, end_height);
+    }
   else
     y_intercept_ = start_height - slope_ * start;
 }
@@ -161,62 +174,10 @@ Building::shift_to_intersect (Real x, Real y) const
   return infinity_f;
 }
 
-// Returns the interval of horizontal shifts for which this
-// building (pointing up) overlaps the other building (pointing down).
-Interval
-Building::overlapping_shift_interval (Building const &other) const
-{
-  Interval iv;
-
-  // If one building is empty, there will never be an overlap.
-  if (y_intercept_ == -infinity_f || other.y_intercept_ == -infinity_f)
-    return iv;
-
-  // There are two kinds of interesting positions:
-  // - when the horizontal extents of the buildings just touch
-  // - when an endpoint of one building intersects the roof of the other.
-  // The interval we are looking for is the smallest one that
-  // contains all of the interesting points.
-
-
-  Real my_y1 = height (start_);
-  Real my_y2 = height (end_);
-  Real his_y1 = -other.height (other.start_); // "-" because OTHER points down
-  Real his_y2 = -other.height (other.end_);
-
-  // If both buildings are infinite in the same direction,
-  // the return value is either empty or full.
-  if ((isinf (start_) && isinf (other.start_))
-      || (isinf (end_) && isinf (other.end_)))
-    return (y_intercept_ > other.y_intercept_)
-           ? Interval (-infinity_f, infinity_f) : Interval ();
-
-  // ...when the horizontal extents of the buildings just touch...
-  if (my_y1 >= his_y2)
-    iv.add_point (other.end_ - start_);
-  if (my_y2 >= his_y1)
-    iv.add_point (other.start_ - end_);
-
-  // ...when an endpoint of one building intersects the roof of the other.
-  Real p1 = shift_to_intersect (other.start_, his_y1);
-  Real p2 = shift_to_intersect (other.end_, his_y2);
-  // "-my_y1" because OTHER points down:
-  Real p3 = other.shift_to_intersect (start_, -my_y1);
-  Real p4 = other.shift_to_intersect (end_, -my_y2);
-  if (!isinf (p1))
-    iv.add_point (p1);
-  if (!isinf (p2))
-    iv.add_point (p2);
-  if (!isinf (p3))
-    iv.add_point (p3);
-  if (!isinf (p4))
-    iv.add_point (p4);
-
-  return iv;
-}
-
 static Real
-first_intersection (Building const &b, list<Building> *const s, Real start_x)
+first_intersection (Building const &b, list<Building> *s, Real start_x)
+/* Return the first x >= start_x where skyline s above Building b.
+ * Removes buildings from s that are concealed by b. */
 {
   while (!s->empty () && start_x < b.end_)
     {
@@ -267,6 +228,7 @@ Skyline::normalize ()
 {
   bool last_empty = false;
   list<Building>::iterator i;
+
   for (i = buildings_.begin (); i != buildings_.end (); i++)
     {
       if (last_empty && i->y_intercept_ == -infinity_f)
@@ -345,7 +307,7 @@ Skyline::internal_merge_skyline (list<Building> *s1, list<Building> *s2,
           last_end = x;
           continue;
         }
-
+      // first_intersection() removes buildings from s2 if b hides them
       Real end = first_intersection (b, s2, x);
       if (s2->empty ())
         {
@@ -354,8 +316,9 @@ Skyline::internal_merge_skyline (list<Building> *s1, list<Building> *s2,
           break;
         }
 
-      /* only include buildings wider than epsilon */
-      if (end > x + EPS)
+      // Should be (end > x), during ver2.19.  end == x happens fairly often,
+      // and we do not need to keep vertical segments within a skyline.
+      if (end >= x)
         {
           b.leading_part (end);
           b.start_ = last_end;
@@ -365,6 +328,9 @@ Skyline::internal_merge_skyline (list<Building> *s1, list<Building> *s2,
 
       if (end >= s1->front ().end_)
         s1->pop_front ();
+      // Should add during ver2.19 (to avoid an endless loop
+      // when  merging identical skylines with a vertical segment)
+      // if (end >= s2->front().end_) s2->pop_front();
 
       x = end;
     }
@@ -382,18 +348,15 @@ empty_skyline (list<Building> *const ret)
 static void
 single_skyline (Building b, list<Building> *const ret)
 {
-  if (b.end_ > b.start_ + EPS)
-    {
-      ret->push_back (Building (-infinity_f, -infinity_f,
-                                -infinity_f, b.start_));
-      ret->push_back (b);
-      ret->push_back (Building (b.end_, -infinity_f,
-                                -infinity_f, infinity_f));
-    }
-  else
-    {
-      empty_skyline (ret);
-    }
+  assert (b.end_ >= b.start_);
+
+  if (b.start_ != -infinity_f)
+    ret->push_back (Building (-infinity_f, -infinity_f,
+                              -infinity_f, b.start_));
+  ret->push_back (b);
+  if (b.end_ != infinity_f)
+    ret->push_back (Building (b.end_, -infinity_f,
+                              -infinity_f, infinity_f));
 }
 
 /* remove a non-overlapping set of boxes from BOXES and build a skyline
@@ -428,7 +391,8 @@ non_overlapping_skyline (list<Building> *const buildings)
           continue;
         }
 
-      if (x1 > last_end + EPS)
+      // Insert empty Buildings into any gaps. (TODO: is this needed? -KOH)
+      if (x1 > last_end)
         result.push_back (Building (last_end, -infinity_f, -infinity_f, x1));
 
       result.push_back (*i);
@@ -506,18 +470,6 @@ Skyline::Skyline ()
   empty_skyline (&buildings_);
 }
 
-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++)
-    {
-      buildings_.push_back (Building ((*i)));
-    }
-}
-
 Skyline::Skyline (Direction sky)
 {
   sky_ = sky;
@@ -527,20 +479,17 @@ Skyline::Skyline (Direction sky)
 /*
   Build skyline from a set of boxes.
 
-  Boxes should have fatness in the horizon_axis, otherwise they are ignored.
+  Boxes should be non-empty on both axes.  Otherwise, they will be ignored
  */
 Skyline::Skyline (vector<Box> const &boxes, Axis horizon_axis, Direction sky)
 {
   list<Building> buildings;
   sky_ = sky;
 
-  Axis vert_axis = other_axis (horizon_axis);
   for (vsize i = 0; i < boxes.size (); i++)
-    {
-      Interval iv = boxes[i][horizon_axis];
-      if (iv.length () > EPS && !boxes[i][vert_axis].is_empty ())
-        buildings.push_front (Building (boxes[i], horizon_axis, sky));
-    }
+    if (!boxes[i].is_empty (X_AXIS)
+        && !boxes[i].is_empty (Y_AXIS))
+      buildings.push_front (Building (boxes[i], horizon_axis, sky));
 
   buildings_ = internal_build_skyline (&buildings);
   normalize ();
@@ -549,7 +498,8 @@ Skyline::Skyline (vector<Box> const &boxes, Axis horizon_axis, Direction sky)
 /*
   build skyline from a set of line segments.
 
-  Buildings should have fatness in the horizon_axis, otherwise they are ignored.
+  Segments can be articulated from left to right or right to left.
+  In the case of the latter, they will be stored internally as left to right.
  */
 Skyline::Skyline (vector<Drul_array<Offset> > const &segments, Axis horizon_axis, Direction sky)
 {
@@ -569,7 +519,7 @@ Skyline::Skyline (vector<Drul_array<Offset> > const &segments, Axis horizon_axis
       Real y1 = left[other_axis (horizon_axis)] * sky;
       Real y2 = right[other_axis (horizon_axis)] * sky;
 
-      if (x1 + EPS < x2)
+      if (x1 <= x2)
         buildings.push_back (Building (x1, y1, y2, x2));
     }
 
@@ -605,8 +555,12 @@ Skyline::Skyline (vector<Skyline_pair> const &skypairs, Direction sky)
 Skyline::Skyline (Box const &b, Axis horizon_axis, Direction sky)
 {
   sky_ = sky;
-  Building front (b, horizon_axis, sky);
-  single_skyline (front, &buildings_);
+  if (!b.is_empty (X_AXIS) && !b.is_empty (Y_AXIS))
+    {
+      Building front (b, horizon_axis, sky);
+      single_skyline (front, &buildings_);
+      normalize ();
+    }
 }
 
 void
@@ -644,8 +598,7 @@ Skyline::insert (Box const &b, Axis a)
     }
 
   /* do the same filtering as in Skyline (vector<Box> const&, etc.) */
-  Interval iv = b[a];
-  if (iv.length () <= EPS || b[other_axis (a)].is_empty ())
+  if (b.is_empty (X_AXIS) || b.is_empty (Y_AXIS))
     return;
 
   my_bld.splice (my_bld.begin (), buildings_);
@@ -704,6 +657,12 @@ Skyline::internal_distance (Skyline const &other, Real horizon_padding, Real *to
 Skyline
 Skyline::padded (Real horizon_padding) const
 {
+  if (horizon_padding < 0.0)
+    warning ("Cannot have negative horizon padding.  Junking.");
+
+  if (horizon_padding <= 0.0)
+    return *this;
+
   list<Building> pad_buildings;
   for (list<Building>::const_iterator i = buildings_.begin (); i != buildings_.end (); ++i)
     {
@@ -789,21 +748,6 @@ Skyline::internal_distance (Skyline const &other, Real *touch_point) const
   return dist;
 }
 
-// changes the direction that the skyline is pointing
-void
-Skyline::invert ()
-{
-  list<Building>::iterator i;
-  for (i = buildings_.begin (); i != buildings_.end (); i++)
-    if (!isinf (i->y_intercept_))
-      {
-        i->y_intercept_ *= -1;
-        i->slope_ *= -1;
-      }
-
-  sky_ = -sky_;
-}
-
 Real
 Skyline::height (Real airplane) const
 {
@@ -835,6 +779,34 @@ Skyline::max_height () const
   return sky_ * ret;
 }
 
+Direction
+Skyline::direction () const
+{
+  return sky_;
+}
+
+Real
+Skyline::left () const
+{
+  for (list<Building>::const_iterator i (buildings_.begin ());
+       i != buildings_.end (); i++)
+    if (i->y_intercept_ > -infinity_f)
+      return i->start_;
+
+  return infinity_f;
+}
+
+Real
+Skyline::right () const
+{
+  for (list<Building>::const_reverse_iterator i (buildings_.rbegin ());
+       i != buildings_.rend (); ++i)
+    if (i->y_intercept_ > -infinity_f)
+      return i->end_;
+
+  return -infinity_f;
+}
+
 Real
 Skyline::max_height_position () const
 {
@@ -872,90 +844,6 @@ Skyline::to_points (Axis horizon_axis) const
   return out;
 }
 
-// Returns the smallest (non-negative) shift in the given
-// direction which will result in THIS and OTHER not overlapping.
-// Warning: this function is O(n^2 log n). Use sparingly.
-Real
-Skyline::smallest_shift (Skyline const &other,
-                         Direction d,
-                         Real horizon_padding,
-                         Real vertical_padding) const
-{
-  // If one or both of the paddings is zero, this can
-  // be optimized...
-  Skyline padded_me = padded (horizon_padding);
-  padded_me.raise (vertical_padding);
-
-  list<Building>::const_iterator i = padded_me.buildings_.begin ();
-  list<Building>::const_iterator j = other.buildings_.begin ();
-  list<Building>::const_iterator i_end = padded_me.buildings_.end ();
-  list<Building>::const_iterator j_end = other.buildings_.end ();
-
-  // Find all shifts that are not allowed.
-  vector<Interval> forbidden_shifts;
-  for (; i != i_end; ++i)
-    if (i->y_intercept_ != -infinity_f)
-      for (j = other.buildings_.begin (); j != j_end; ++j)
-        {
-          Interval iv = i->overlapping_shift_interval (*j);
-          if (!iv.is_empty ())
-            forbidden_shifts.push_back (iv);
-        }
-
-  // Now comes the trick: we want to find the smallest point
-  // that is not in the union of forbidden_shifts. We can represent
-  // the union of forbidden_shifts as a skyline, where a point is
-  // allowed if it has height -infinity_f and forbidden otherwise.
-  vector<Box> boxes;
-  for (vector<Interval>::iterator k = forbidden_shifts.begin ();
-       k != forbidden_shifts.end (); ++k)
-    boxes.push_back (Box (*k, Interval (-1, 0)));
-  Skyline s (boxes, X_AXIS, UP);
-
-  // Find the smallest (ie. closest to zero, in the appropriate direction)
-  // coordinate where the height of s is -infinity_f.
-  Real last_good_point = -infinity_f;
-  for (i = s.buildings_.begin (); i != s.buildings_.end (); ++i)
-    {
-      if (d == LEFT && i->start_ > 0)
-        return last_good_point;
-
-      if (i->y_intercept_ == -infinity_f)
-        {
-          if (i->start_ <= 0 && i->end_ >= 0)
-            return 0;
-          if (d == RIGHT && i->start_ >= 0)
-            return i->start_;
-
-          last_good_point = i->end_;
-        }
-    }
-
-  return infinity_f * d;
-}
-
-Real
-Skyline::left () const
-{
-  for (list<Building>::const_iterator i (buildings_.begin ());
-       i != buildings_.end (); i++)
-    if (i->y_intercept_ > -infinity_f)
-      return i->start_;
-
-  return infinity_f;
-}
-
-Real
-Skyline::right () const
-{
-  for (list<Building>::const_reverse_iterator i (buildings_.rbegin ());
-       i != buildings_.rend (); ++i)
-    if (i->y_intercept_ > -infinity_f)
-      return i->end_;
-
-  return -infinity_f;
-}
-
 bool
 Skyline::is_empty () const
 {
@@ -965,12 +853,6 @@ Skyline::is_empty () const
   return b.end_ == infinity_f && b.y_intercept_ == -infinity_f;
 }
 
-bool
-Skyline::is_singleton () const
-{
-  return buildings_.size () == 3;
-}
-
 void
 Skyline::clear ()
 {
@@ -1059,3 +941,12 @@ 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));
 }
+
+LY_DEFINE (ly_skyline_empty_p, "ly:skyline-empty?",
+           1, 0, 0, (SCM sky),
+           "Return whether @var{sky} is empty.")
+{
+  Skyline *s = Skyline::unsmob (sky);
+  LY_ASSERT_SMOB (Skyline, sky, 1);
+  return scm_from_bool (s->is_empty ());
+}