-/* skyline.cc -- implement the Skyline class
+/*
+ This file is part of LilyPond, the GNU music typesetter.
+
+ Copyright (C) 2006--2011 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.
- source file of the GNU LilyPond music typesetter
-
- (c) 2006--2007 Joe Neeman <joeneeman@gmail.com>
+ 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.
+
+ 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"
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))
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
{
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_;
}
/* 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 ())
+ {
+ programming_error ("tried to merge an empty skyline");
+ return;
+ }
+
Real x = -infinity_f;
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;
}
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);
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
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);
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];
}
{
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;
}
list<Building> one = partials.front ();
partials.pop_front ();
if (partials.empty ())
- return one;
+ return one;
list<Building> two = partials.front ();
partials.pop_front ();
Skyline::Skyline ()
{
sky_ = UP;
- empty_skyline (&buildings_);
+ 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++)
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
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);
}
{
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
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);
+ if (iv.length () <= EPS || b[other_axis (a)].is_empty ())
+ 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_);
}
}
Real
-Skyline::distance (Skyline const &other) const
+Skyline::distance (Skyline const &other, Real horizon_padding) 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;
+
+ /*
+ 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);
+ }
+
+ 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 ())
+ 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 (i->end_ <= j->end_)
- i++;
+ i++;
else
- j++;
+ j++;
start = end;
}
return dist;
for (i = buildings_.begin (); i != buildings_.end (); i++)
{
if (i->end_ >= airplane)
- return sky_ * i->height (airplane);
+ return sky_ * i->height (airplane);
}
assert (0);
merge (s);
}
-
vector<Offset>
-Skyline::to_points (Axis a) const
+Skyline::to_points (Axis horizon_axis) const
{
vector<Offset> out;
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;
}
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))
-{
+ Building b = buildings_.front ();
+ return b.end_ == infinity_f && b.y_intercept_ == -infinity_f;
}
void
-Skyline_pair::raise (Real r)
+Skyline::clear ()
{
- skylines_[UP].raise (r);
- skylines_[DOWN].raise (r);
-}
-
-void
-Skyline_pair::shift (Real r)
-{
- skylines_[UP].shift (r);
- skylines_[DOWN].shift (r);
-}
-
-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
-{
- 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)
{
return 1;
}
-
-SCM
-Skyline_pair::mark_smob (SCM)
-{
- return SCM_EOL;
-}
-
-int
-Skyline_pair::print_smob (SCM s, SCM port, scm_print_state *)
-{
- Skyline_pair *r = (Skyline_pair *) SCM_CELL_WORD_1 (s);
- (void) r;
-
- scm_puts ("#<Skyline-pair>", port);
- return 1;
-}