/*
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)
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;
}
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_)
{
{
bool last_empty = false;
list<Building>::iterator i;
+
for (i = buildings_.begin (); i != buildings_.end (); i++)
{
if (last_empty && i->y_intercept_ == -infinity_f)
last_end = x;
continue;
}
-
+ // first_intersection() removes buildings from s2 if b hides them
Real end = first_intersection (b, s2, x);
if (s2->empty ())
{
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;
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;
}
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
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);
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;
/*
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 ();
/*
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)
{
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));
}
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
}
/* 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_);
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)
{
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
{
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
{
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
{
return b.end_ == infinity_f && b.y_intercept_ == -infinity_f;
}
-bool
-Skyline::is_singleton () const
-{
- return buildings_.size () == 3;
-}
-
void
Skyline::clear ()
{
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 ());
+}