X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=lily%2Fskyline.cc;h=0250fc07f4743ba44e7bc1ac8af23ed6057aa435;hb=75b64f8ddda16f3b2eb77ec5e9631d92b9be7da4;hp=d8105e669b910eaadc3fe8e34286ae9d613c8e50;hpb=a6bd229f7fe1dc4a03478e14ccc0c0c66b225061;p=lilypond.git diff --git a/lily/skyline.cc b/lily/skyline.cc index d8105e669b..0250fc07f4 100644 --- a/lily/skyline.cc +++ b/lily/skyline.cc @@ -1,7 +1,7 @@ /* This file is part of LilyPond, the GNU music typesetter. - Copyright (C) 2006--2010 Joe Neeman + Copyright (C) 2006--2012 Joe Neeman LilyPond is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -78,8 +78,8 @@ Skyline::print_points () const vector 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" : " "); + 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) @@ -119,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 @@ -168,15 +168,15 @@ first_intersection (Building const &b, list *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_; } @@ -190,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 *s1, list *s2, - list *const result) + list *const result) { if (s1->empty () || s2->empty ()) { @@ -207,26 +207,26 @@ Skyline::internal_merge_skyline (list *s1, list *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; } @@ -239,16 +239,20 @@ empty_skyline (list *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 *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); @@ -257,7 +261,7 @@ single_skyline (Building b, Real start, Real horizon_padding, list *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 @@ -273,21 +277,21 @@ non_overlapping_skyline (list *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] - 2*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::iterator j = i++; boxes->erase (j); @@ -309,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]; } @@ -330,7 +334,8 @@ Skyline::internal_build_skyline (list *boxes, Real horizon_padding, Axis ho { list result; single_skyline (Building (boxes->front (), horizon_padding, horizon_axis, sky), - boxes->front ()[horizon_axis][LEFT], horizon_padding, &result); + boxes->front ()[horizon_axis][LEFT] - horizon_padding, + horizon_padding, &result); return result; } @@ -347,7 +352,7 @@ Skyline::internal_build_skyline (list *boxes, Real horizon_padding, Axis ho list one = partials.front (); partials.pop_front (); if (partials.empty ()) - return one; + return one; list two = partials.front (); partials.pop_front (); @@ -367,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::const_iterator i = src.buildings_.begin (); i != src.buildings_.end (); i++) @@ -382,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 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::const_iterator end = src.buildings_.end (); + for (list::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 @@ -402,9 +438,9 @@ Skyline::Skyline (vector 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); } @@ -412,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 @@ -446,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_); } @@ -470,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::const_iterator i = buildings_.begin (); - list::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::const_iterator i = padded_this->buildings_.begin (); + list::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; } @@ -502,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); @@ -517,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) { @@ -525,7 +619,6 @@ Skyline::set_minimum_height (Real h) merge (s); } - vector Skyline::to_points (Axis horizon_axis) const { @@ -563,15 +656,14 @@ Skyline::clear () /****************************************************************/ - IMPLEMENT_SIMPLE_SMOBS (Skyline); IMPLEMENT_TYPE_P (Skyline, "ly:skyline?"); IMPLEMENT_DEFAULT_EQUAL_P (Skyline); SCM -Skyline::mark_smob (SCM) +Skyline::mark_smob (SCM s) { - ASSERT_LIVE_IS_ALLOWED (); + ASSERT_LIVE_IS_ALLOWED (s); return SCM_EOL; } @@ -585,3 +677,61 @@ 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::get_touching_point (SCM skyline_scm, SCM other_skyline_scm, SCM horizon_padding_scm) +{ + 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)); +} + +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) +{ + 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->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)); +}