struct Accidental_placement_entry
{
- vector<Skyline_entry> left_skyline_;
- vector<Skyline_entry> right_skyline_;
+ Skyline left_skyline_;
+ Skyline right_skyline_;
Interval vertical_extent_;
vector<Box> extents_;
vector<Grob*> grobs_;
for (vsize i = apes.size (); i--;)
{
Accidental_placement_entry *ape = apes[i];
- ape->left_skyline_ = empty_skyline (LEFT);
- ape->right_skyline_ = empty_skyline (RIGHT);
for (vsize j = apes[i]->grobs_.size (); j--;)
{
Grob *a = apes[i]->grobs_[j];
-
vector<Box> boxes = Accidental_interface::accurate_boxes (a, common);
ape->extents_.insert (ape->extents_.end (), boxes.begin (), boxes.end ());
- for (vsize j = boxes.size (); j--;)
- {
- insert_extent_into_skyline (&ape->left_skyline_, boxes[j], Y_AXIS, LEFT);
- insert_extent_into_skyline (&ape->right_skyline_, boxes[j], Y_AXIS, RIGHT);
- }
}
+ ape->left_skyline_ = Skyline (ape->extents_, Y_AXIS, LEFT);
+ ape->right_skyline_ = Skyline (ape->extents_, Y_AXIS, RIGHT);
}
Interval total;
Accidental_placement_entry *head_ape = new Accidental_placement_entry;
common[X_AXIS] = common_refpoint_of_array (heads, common[X_AXIS], X_AXIS);
- vector<Skyline_entry> head_skyline (empty_skyline (LEFT));
vector<Box> head_extents;
for (vsize i = heads.size (); i--;)
- {
- Box b (heads[i]->extent (common[X_AXIS], X_AXIS),
- heads[i]->extent (common[Y_AXIS], Y_AXIS));
-
- insert_extent_into_skyline (&head_skyline, b, Y_AXIS, LEFT);
- }
+ head_extents.push_back (Box (heads[i]->extent (common[X_AXIS], X_AXIS),
+ heads[i]->extent (common[Y_AXIS], Y_AXIS)));
vector<Grob *> stems;
for (vsize i = 0; i < heads.size (); i++)
{
int very_large = INT_MAX;
- Box b (heads[i]->extent (common[X_AXIS], X_AXIS),
- heads[i]->pure_height (common[Y_AXIS], 0, very_large));
-
- insert_extent_into_skyline (&head_skyline, b, Y_AXIS, LEFT);
+ head_extents.push_back (Box (heads[i]->extent (common[X_AXIS], X_AXIS),
+ heads[i]->pure_height (common[Y_AXIS], 0, very_large)));
}
-
- head_ape->left_skyline_ = head_skyline;
+
+ head_ape->left_skyline_ = Skyline (head_extents, Y_AXIS, LEFT);
head_ape->offset_ = 0.0;
Real padding = robust_scm2double (me->get_property ("padding"), 0.2);
- vector<Skyline_entry> left_skyline = head_ape->left_skyline_;
- heighten_skyline (&left_skyline,
- -robust_scm2double (me->get_property ("right-padding"), 0));
+ Skyline left_skyline = head_ape->left_skyline_;
+ left_skyline.raise (-robust_scm2double (me->get_property ("right-padding"), 0))
+;
/*
Add accs entries right-to-left.
*/
for (vsize i = apes.size (); i-- > 0;)
{
- Real offset
- = -skyline_meshing_distance (apes[i]->right_skyline_, left_skyline);
+ Real offset = -apes[i]->right_skyline_.distance (left_skyline);
if (isinf (offset))
offset = (i < apes.size () - 1) ? apes[i + 1]->offset_ : 0.0;
else
apes[i]->offset_ = offset;
- vector<Skyline_entry> new_left_skyline = apes[i]->left_skyline_;
- heighten_skyline (&new_left_skyline, apes[i]->offset_);
- merge_skyline (&new_left_skyline, left_skyline, LEFT);
+ Skyline new_left_skyline = apes[i]->left_skyline_;
+ new_left_skyline.raise (apes[i]->offset_);
+ new_left_skyline.merge (left_skyline);
left_skyline = new_left_skyline;
}
-/*
- skyline.cc -- implement Skyline_entry and funcs.
+/* skyline.cc -- implement the Skyline class
- source file of the GNU LilyPond music typesetter
-
- (c) 2002--2006 Han-Wen Nienhuys <hanwen@xs4all.nl>
+ source file of the GNU LilyPond music typesetter
+
+ (c) 2006 Joe Neeman <joeneeman@gmail.com>
*/
#include "skyline.hh"
-/*
- A skyline is a shape of the form:
+/* A skyline is a sequence of non-overlapping buildings: something like
+ this:
+ _________
+ | | ________
+ | | _________| |
+ /| | \ | |
+ / |------- \ | |
+ / \ | |
+ / --------------- -------
+ --
+ Each building has a starting position, and ending position, a starting
+ height and an ending height.
+
+ The following invariants are observed:
+ - the start of the first building is at -infinity
+ - the end of the last building is at infinity
+ - if a building has infinite length (ie. the first and last buildings),
+ then its starting height and ending height are equal
+ - the end of one building is the same as the beginning of the next
+ building
+
+ We also allow skylines to point down (the structure is exactly the same,
+ but we think of the part above the line as being filled with mass and the
+ part below as being empty). ::distance finds the minimum distance between
+ an UP skyline and a DOWN skyline.
+
+ Note that we store DOWN skylines upside-down. That is, in order to compare
+ 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.
+*/
+#define EPS 1e-10
- * ----
- * | |
- * ---------| |
- * | |
- * | |
- * | |______
- * --------| |___
- *
+static inline bool
+equal (Real x, Real y)
+{
+ return abs (x - y) < EPS || (isinf (x) && isinf (y) && ((x > 0) == (y > 0)));
+}
- This file deals with building such skyline structure, and computing
- the minimum distance between two opposing skylines.
+bool
+Skyline::is_legal_skyline () const
+{
+ list<Building>::const_iterator i;
+ Real last_x = -infinity_f;
+ for (i = buildings_.begin (); i != buildings_.end (); i++)
+ {
+ if (isinf (i->start_height_) != isinf (i->end_height_))
+ return false;
+ if (i->iv_[LEFT] != last_x)
+ return false;
+ if (isinf (i->iv_.length ()) && i->start_height_ != i->end_height_)
+ return false;
+ last_x = i->iv_[RIGHT];
+ }
+ return last_x == infinity_f;
+}
- Invariants for a skyline:
+Building::Building (Real start, Real start_height, Real end_height, Real end)
+ : iv_ (start, end)
+{
+ start_height_ = start_height;
+ end_height_ = end_height;
- skyline[...].width_ forms a partition of the real interval, where
- the segments are adjacent, and ascending. Hence we have
+ if (isinf (start_height) || isinf (start) || isinf (end))
+ end_height_ = start_height;
+ else if (isinf (end_height))
+ start_height_ = end_height;
- skyline.back ().width_[RIGHT] = inf
- skyline[0].width_[LEFT] = -inf
-*/
+ m_ = (end_height - start_height) / (end - start);
+ b_ = start_height - m_*start;
-const Real EPS = 1e-12;
+ if (isinf (start_height) || isinf (start) || isinf (end))
+ {
+ m_ = 0;
+ b_ = start_height;
+ }
+}
-/*
- TODO: avoid unnecessary fragmentation.
+Real
+Building::height (Real x) const
+{
+ if (isinf (x))
+ return start_height_;
+ return m_*x + b_;
+}
+
+Real
+Building::intersection (Building const &other) const
+{
+ return (b_ - other.b_) / (other.m_ - m_);
+}
- This is O (n^2), searching and insertion. Could be O (n log n) with
- binsearch.
-*/
void
-insert_extent_into_skyline (vector<Skyline_entry> *line, Box b, Axis line_axis,
- Direction d)
+Building::leading_part (Real chop)
{
- Interval extent = b[line_axis];
- if (extent.is_empty ())
- return;
+ assert (chop > iv_[LEFT] && chop <= iv_[RIGHT] && !equal (chop, iv_[LEFT]));
+ iv_[RIGHT] = chop;
+ end_height_ = height (chop);
+}
- Real stick_out = b[other_axis (line_axis)][d];
+static void
+skyline_trailing_part (list<Building> *sky, Real x)
+{
+ if (equal (x, sky->front ().iv_[RIGHT]))
+ sky->pop_front ();
+ else
+ assert (x < sky->front ().iv_[RIGHT]);
- /*
- Intersect each segment of LINE with EXTENT, and if non-empty, insert relevant segments.
- */
- for (vsize i = line->size (); i--;)
+ if (!sky->empty ())
{
- Interval w = line->at (i).width_;
- w.intersect (extent);
+ sky->front ().iv_[LEFT] = x;
+ sky->front ().start_height_ = sky->front ().height (x);
+ }
+}
+
+bool
+Building::obstructs (Building const &other) const
+{
+ if (equal (intersection (other), iv_[LEFT]) || equal (start_height_, other.start_height_))
+ return m_ > other.m_;
+ return start_height_ > other.start_height_;
+}
- if (extent[LEFT] >= w[RIGHT])
- break;
- Real my_height = line->at (i).height_;
+/* precondition: the building should be visible above the first
+ building in skyline. The building and the skyline should
+ start at the same point.
- if (!w.is_empty ()
- && w.length () > EPS
- && d * (my_height - stick_out) < 0)
- {
- Interval e1 (line->at (i).width_[LEFT], extent[LEFT]);
- Interval e3 (extent[RIGHT], line->at (i).width_[RIGHT]);
+ return the point at which the building b is no longer visible,
+ either because it has ended or because the skyline has risen
+ above it. Truncate the skyline at that point.
+*/
+Real
+Skyline::last_visible_point (Building const &b, list<Building> *const skyline)
+{
+ assert (!skyline->front ().obstructs (b));
+ while (1)
+ {
+ Building other = skyline->front ();
- if (!e3.is_empty () && e3.length () > EPS)
- line->insert (line->begin () + i + 1, Skyline_entry (e3, my_height));
+ /* there are 3 interesting cases:
+ 1) the roofs intersect within the spans of the buildings */
+ Real intersect = b.intersection (other);
+ if (intersection (b.iv_, other.iv_).contains (intersect))
+ {
+ if (equal (intersect, b.iv_[LEFT]))
+ {
+ /* if the buildings have almost the same starting height, we can find
+ that their intersection "equals" the start point. In this case, we
+ just skip the intersection.
+ */
+ assert (b.m_ >= other.m_);
+ }
+ else
+ {
+ skyline_trailing_part (skyline, intersect);
+ return intersect;
+ }
+ }
- line->at (i).height_ = stick_out;
- line->at (i).width_ = w;
- if (!e1.is_empty () && e1.length () > EPS)
- line->insert (line->begin () + i, Skyline_entry (e1, my_height));
+ /* 2) the first building ends. This is guaranteed to happen before
+ the skyline becomes empty because it has to end at infinity */
+ if (skyline->empty () && !other.iv_.contains (b.iv_[RIGHT]))
+ assert (0);
+ if (other.iv_.contains (b.iv_[RIGHT]))
+ {
+ skyline_trailing_part (skyline, b.iv_[RIGHT]);
+ return b.iv_[RIGHT];
}
+
+ assert (!skyline->empty ());
+ skyline->pop_front ();
+ other = skyline->front ();
+
+ /* 3) the next building in the skyline starts above b */
+ if (other.start_height_ > b.height (other.iv_[LEFT]))
+ return other.iv_[LEFT];
}
}
void
-merge_skyline (vector<Skyline_entry> *a1,
- vector<Skyline_entry> const &a2,
- Direction dir)
+Skyline::internal_merge_skyline (list<Building> *s1, list<Building> *s2,
+ list<Building> *const result)
{
- for (vsize i = 0; i < a2.size (); i++)
+ while (!s1->empty ())
{
- Box b;
- b[X_AXIS] = a2[i].width_;
- b[Y_AXIS][dir] = a2[i].height_;
- b[Y_AXIS][-dir] = dir * infinity_f;
+ if (s2->front ().obstructs (s1->front ()))
+ swap (s1, s2);
+
+ Building b = s1->front ();
+ Real end = last_visible_point (b, s2);
+
+ b.leading_part (end);
+ result->push_front (b);
- insert_extent_into_skyline (a1, b, X_AXIS, dir);
+ skyline_trailing_part (s1, end);
}
+ result->reverse ();
}
-vector<Skyline_entry>
-empty_skyline (Direction d)
+static void
+empty_skyline (list<Building> *const ret)
{
- vector<Skyline_entry> skyline;
+ ret->push_front (Building (-infinity_f, -infinity_f, -infinity_f, infinity_f));
+}
- Interval i;
- i.set_empty ();
- i.swap ();
- Skyline_entry e;
- e.width_ = i;
- e.height_ = -d * infinity_f;
- skyline.push_back (e);
- return skyline;
+static void
+single_skyline (Building const &b, list<Building> *const ret)
+{
+ if (!isinf (b.iv_[RIGHT]))
+ ret->push_front (Building (b.iv_[RIGHT], -infinity_f, -infinity_f, infinity_f));
+ ret->push_front (b);
+ if (!isinf (b.iv_[LEFT]))
+ ret->push_front (Building (-infinity_f, -infinity_f, -infinity_f, b.iv_[LEFT]));
}
-vector<Skyline_entry>
-extents_to_skyline (vector<Box> const &extents, Axis a, Direction d)
+void
+Skyline::internal_build_skyline (list<Building> *buildings, list<Building> *const result)
{
+ vsize size = buildings->size ();
- vector<Skyline_entry> skyline = empty_skyline (d);
+ if (size == 0)
+ {
+ empty_skyline (result);
+ return;
+ }
+ else if (size == 1)
+ {
+ single_skyline (buildings->front (), result);
+ return;
+ }
- /*
- This makes a cubic algorithm (array insertion is O (n),
- searching the array dumbly is O (n), and for n items, we get O (n^3).)
+ list<Building> right_half;
+ list<Building>::iterator i = buildings->begin ();
- We could do a lot better (n log (n), using a balanced tree) but
- that seems overkill for now.
- */
- for (vsize j = extents.size (); j--;)
- insert_extent_into_skyline (&skyline, extents[j], a, d);
+ for (vsize s = 0; s < size/2; s++)
+ i++;
+ right_half.splice (right_half.end (), *buildings, i, buildings->end ());
- return skyline;
+ list<Building> right;
+ list<Building> left;
+ internal_build_skyline (&right_half, &right);
+ internal_build_skyline (buildings, &left);
+ internal_merge_skyline (&right, &left, result);
}
-/*
- minimum distance that can be achieved between baselines. "Clouds" is
- a skyline pointing down.
+Skyline::Skyline ()
+{
+ sky_ = UP;
+ empty_skyline (&buildings_);
+}
- This is an O (n) algorithm.
-*/
-Real
-skyline_meshing_distance (vector<Skyline_entry> const &buildings,
- vector<Skyline_entry> const &clouds)
+Skyline::Skyline (Direction sky)
{
- int i = buildings.size () -1;
- int j = clouds.size () -1;
+ sky_ = sky;
+ empty_skyline (&buildings_);
+}
- Real distance = -infinity_f;
+Skyline::Skyline (vector<Box> const &boxes, Axis a, Direction sky)
+{
+ list<Building> bldgs;
+ sky_ = sky;
- while (i > 0 || j > 0)
+ for (vsize i = 0; i < boxes.size (); i++)
{
- Interval w = buildings[i].width_;
- w.intersect (clouds[j].width_);
-
- if (!w.is_empty ())
- distance = max (distance, (buildings[i].height_ - clouds[j].height_));
-
- if (i > 0 && buildings[i].width_[LEFT] >= clouds[j].width_[LEFT])
- i--;
- else if (j > 0 && buildings[i].width_[LEFT] <= clouds[j].width_[LEFT])
- j--;
+ Interval iv = boxes[i][a];
+ Real height = sky * boxes[i][other_axis (a)][sky];
+ if (!iv.is_empty () && !isinf (height) && !equal (iv[LEFT], iv[RIGHT]))
+ bldgs.push_front (Building (iv[LEFT], height, height, iv[RIGHT]));
}
-
- return distance;
+ internal_build_skyline (&bldgs, &buildings_);
+ assert (is_legal_skyline ());
}
-Skyline_entry::Skyline_entry ()
+void
+Skyline::merge (Skyline const &other)
{
- height_ = 0.0;
+ assert (sky_ == other.sky_);
+
+ list<Building> other_bld (other.buildings_);
+ list<Building> my_bld;
+ my_bld.splice (my_bld.begin (), buildings_);
+ internal_merge_skyline (&other_bld, &my_bld, &buildings_);
+ assert (is_legal_skyline ());
}
-Skyline_entry::Skyline_entry (Interval i, Real r)
+void
+Skyline::insert (Box const &b, Axis a)
{
- width_ = i;
- height_ = r;
+ list<Building> other_bld;
+ list<Building> my_bld (buildings_);
+ Interval iv = b[a];
+ Real height = sky_ * b[other_axis (a)][sky_];
+
+ single_skyline (Building (iv[LEFT], height, height, iv[RIGHT]), &other_bld);
+ internal_merge_skyline (&other_bld, &my_bld, &buildings_);
+ assert (is_legal_skyline ());
}
void
-heighten_skyline (vector<Skyline_entry> *buildings, Real ground)
+Skyline::raise (Real r)
+{
+ list<Building>::iterator end = buildings_.end ();
+ for (list<Building>::iterator i = buildings_.begin (); i != end; i++)
+ {
+ i->start_height_ += sky_ * r;
+ i->end_height_ += sky_ * r;
+ i->b_ += sky_ * r;
+ }
+ assert (is_legal_skyline ());
+}
+
+Real
+Skyline::distance (Skyline const &other) const
{
- for (vsize i = 0; i < buildings->size (); i++)
- buildings->at (i).height_ += ground;
+ assert (sky_ == -other.sky_);
+ list<Building>::const_iterator i = buildings_.begin ();
+ list<Building>::const_iterator j = other.buildings_.begin ();
+
+ Real dist = -infinity_f;
+ for (; i != buildings_.end () && j != other.buildings_.end (); i++)
+ {
+ while (j->iv_[RIGHT] < i->iv_[LEFT])
+ j++;
+
+ list<Building>::const_iterator k;
+ for (k = j; k->iv_[LEFT] <= i->iv_[RIGHT] && k != other.buildings_.end (); k++)
+ {
+ Interval iv = intersection (i->iv_, k->iv_);
+ dist = max (dist, max (i->height (iv[LEFT]) + k->height (iv[LEFT]),
+ i->height (iv[RIGHT]) + k->height (iv[RIGHT])));
+ }
+ }
+ return dist;
}
Real
-skyline_height (vector<Skyline_entry> const &buildings,
- Real airplane,
- Direction sky_dir)
+Skyline::height (Real airplane) const
{
- Real h = - sky_dir * infinity_f;
+ assert (!isinf (airplane));
+
+ list<Building>::const_iterator i;
+ for (i = buildings_.begin (); i != buildings_.end (); i++)
+ {
+ if (i->iv_[RIGHT] > airplane)
+ return sky_ * i->height (airplane);
+ if (i->iv_[RIGHT] == airplane)
+ {
+ assert (i != buildings_.end ());
+ list<Building>::const_iterator j = i;
+ j++;
+ return sky_ * (max (i->end_height_, j->start_height_));
+ }
+ }
+ assert (0);
+ return 0;
+}
- /*
- Ugh! linear, should be O(log n).
- */
- for (vsize i = 0; i < buildings.size (); i++)
- if (buildings[i].width_.contains (airplane))
- h = sky_dir * max (sky_dir * h,
- sky_dir * buildings[i].height_);
-
- return h;
+Real
+Skyline::max_height () const
+{
+ Skyline s (-sky_);
+ s.set_minimum_height (0);
+ return sky_ * distance (s);
}
+void
+Skyline::set_minimum_height (Real h)
+{
+ Skyline s (sky_);
+ s.buildings_.front ().start_height_ = h*sky_;
+ s.buildings_.front ().end_height_ = h*sky_;
+ s.buildings_.front ().b_ = h*sky_;
+ merge (s);
+}
if (i == chord_outlines_.end ())
programming_error ("Can't find chord outline");
else
- attachments[d] = skyline_height ((*i).second, y, -d);
+ attachments[d] = i->second.height (y);
}
while (flip (&d) != LEFT);
}
Tuple2<int> key (column_rank, int (dir));
-
- chord_outlines_[key] = empty_skyline (-dir);
-
- if (bounds[0]->break_status_dir ())
- {
- Real x = robust_relative_extent (bounds[0], x_refpoint_, X_AXIS)[-dir];
- chord_outlines_[key].at (0).height_ = x;
- }
- else
- {
- Interval x;
- for (vsize j = 0; j < head_boxes.size (); j++)
- {
- x.unite (head_boxes[j][X_AXIS]);
- }
-
- chord_outlines_[key].at (0).height_ = x[dir];
- }
-
- for (vsize i = 0; i < boxes.size (); i++)
- insert_extent_into_skyline (&chord_outlines_[key] ,
- boxes[i], Y_AXIS, -dir);
if (stem
&& !Stem::is_invisible (stem))
Direction stemdir = get_grob_direction (stem);
y.add_point (Stem::head_positions (stem)[-stemdir]
* staff_space * .5);
-
- insert_extent_into_skyline (&chord_outlines_[key], Box (x,y), Y_AXIS, -dir);
+
+ boxes.push_back (Box (x, y));
stem_extents_[key].unite (Box (x,y));
{
Box flag_box = Stem::get_translated_flag (stem).extent_box ();
flag_box.translate( Offset (x[RIGHT], X_AXIS));
- insert_extent_into_skyline (&chord_outlines_[key], flag_box,
- Y_AXIS, -dir);
+ boxes.push_back (flag_box);
}
}
else if (stem)
Interval y_ext;
for (vsize j = 0; j < head_boxes.size (); j++)
y_ext.unite (head_boxes[j][Y_AXIS]);
-
- insert_extent_into_skyline (&chord_outlines_[key],
- Box (x_ext, y_ext),
- Y_AXIS, -dir);
+
+ boxes.push_back (Box (x_ext, y_ext));
}
Direction updowndir = DOWN;
}
if (!x.is_empty ())
- insert_extent_into_skyline (&chord_outlines_[key],
- Box (x,y),
- Y_AXIS, -dir);
+ boxes.push_back (Box (x, y));
}
while (flip (&updowndir) != DOWN);
+ chord_outlines_[key] = Skyline (boxes, Y_AXIS, -dir);
+ if (bounds[0]->break_status_dir ())
+ {
+ Real x = robust_relative_extent (bounds[0], x_refpoint_, X_AXIS)[-dir];
+ chord_outlines_[key].set_minimum_height (x);
+ }
+ else
+ {
+ Interval x;
+ for (vsize j = 0; j < head_boxes.size (); j++)
+ {
+ x.unite (head_boxes[j][X_AXIS]);
+ }
+
+ chord_outlines_[key].set_minimum_height (x[dir]);
+ }
+
head_extents_[key].set_empty ();
for (vsize i = 0; i < head_boxes.size (); i++)
{
set_chord_outline (heads, head_dir);
- Real extremal = head_dir * infinity_f;
-
Tuple2<int> head_key (column_rank, head_dir);
Tuple2<int> open_key (column_rank, -head_dir);
-
- for (vsize i = 0; i < chord_outlines_[head_key].size (); i++)
- {
- extremal = head_dir * min (head_dir * extremal,
- head_dir * chord_outlines_[head_key][i].height_);
- }
+ Real extremal = chord_outlines_[head_key].max_height ();
- Skyline_entry right_entry;
- right_entry.width_.set_full ();
- right_entry.height_ = extremal - head_dir * 1.5;
-
- chord_outlines_[open_key].push_back (right_entry);
+ chord_outlines_[open_key] = Skyline (head_dir);
+ chord_outlines_[open_key].set_minimum_height (extremal - head_dir * 1.5);
}