X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;ds=sidebyside;f=lily%2Fskyline.cc;h=0250fc07f4743ba44e7bc1ac8af23ed6057aa435;hb=44f8873e8cb9533ddb6713c5e79fe2edb59524c7;hp=ebdc891cdc2c705585944ff372d5717feb8ad696;hpb=c4fd8507a0fe7b527fab21a772ba78194d030624;p=lilypond.git diff --git a/lily/skyline.cc b/lily/skyline.cc index ebdc891cdc..0250fc07f4 100644 --- a/lily/skyline.cc +++ b/lily/skyline.cc @@ -1,23 +1,37 @@ -/* skyline.cc -- implement the Skyline class +/* + This file is part of LilyPond, the GNU music typesetter. + + 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 + 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 Joe Neeman + 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 . */ #include "skyline.hh" +#include +#include #include "ly-smobs.icc" /* 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. @@ -41,169 +55,180 @@ but the distance routine does. */ -#define EPS 1e-10 +/* 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 -static inline bool -equal (Real x, Real y) +static void +print_buildings (list const &b) { - return abs (x - y) < EPS || (isinf (x) && isinf (y) && ((x > 0) == (y > 0))); + for (list::const_iterator i = b.begin (); i != b.end (); i++) + i->print (); } void Skyline::print () const { - for (list::const_iterator i = buildings_.begin (); - i != buildings_.end (); i++) - { - (*i).print (); - } + print_buildings (buildings_); } -bool -Skyline::is_legal_skyline () const +void +Skyline::print_points () const { - list::const_iterator i; - Real last_x = -infinity_f; - Real last_h = -infinity_f; - for (i = buildings_.begin (); i != buildings_.end (); i++) - { - if (i->iv_[LEFT] != last_x) - return false; - if (i != buildings_.begin () && !equal (i->height_[LEFT], last_h)) - return false; - last_x = i->iv_[RIGHT]; - last_h = i->height_[RIGHT]; - } - return last_x == infinity_f; + 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" : " "); } -Building::Building (Real start, Real start_height, Real end_height, - Real end, Real max_slope) - : iv_ (start, end) +Building::Building (Real start, Real start_height, Real end_height, Real end) { - height_[LEFT] = start_height; - height_[RIGHT] = end_height; + if (isinf (start) || isinf (end)) + assert (start_height == end_height); - if (isinf (start)) - assert (isinf (start_height) || start_height == end_height); - if (isinf (end)) - assert (isinf (end_height) || start_height == end_height); + end_ = end; + precompute (start, start_height, end_height, end); +} - precompute (max_slope); +Building::Building (Box const &b, Real horizon_padding, Axis horizon_axis, Direction sky) +{ + Real start = b[horizon_axis][LEFT] - horizon_padding; + Real end = b[horizon_axis][RIGHT] + horizon_padding; + Real height = sky * b[other_axis (horizon_axis)][sky]; + + end_ = end; + precompute (start, height, height, end); } void -Building::precompute (Real max_slope) +Building::precompute (Real start, Real start_height, Real end_height, Real end) { - slope_ = (height_[RIGHT] - height_[LEFT]) / (iv_.length()); - if (height_[LEFT] == height_[RIGHT]) + slope_ = (end_height - start_height) / (end - start); + if (start_height == end_height) /* if they were both infinite, we would get nan, not 0, from the prev line */ slope_ = 0; - if (isinf (slope_) || isnan (slope_)) - slope_ = max_slope * (height_[LEFT] < height_[RIGHT] ? 1 : -1); -#if 0 - /* - this check is sensitive to roundoff errors when converting to/from - sequences of points. - */ - assert (abs (slope_) <= max_slope + EPS); -#endif - - if (isinf (iv_[START])) + assert (!isinf (slope_) && !isnan (slope_)); + + if (isinf (start)) { - if (isinf (iv_[STOP])) - zero_height_ = height_[LEFT]; - else - zero_height_ = height_[RIGHT] - slope_ * iv_[STOP]; + assert (start_height == end_height); + y_intercept_ = start_height; } else - zero_height_ = height_[LEFT] - slope_ * iv_[START]; + y_intercept_ = start_height - slope_ * start; } -Real +Real Building::height (Real x) const { - if (isinf (x)) - return (x > 0) ? height_[RIGHT] : height_[LEFT]; - return slope_*x + zero_height_; + return isinf (x) ? y_intercept_ : slope_ * x + y_intercept_; } void Building::print () const { - printf ("X[%f,%f] -> Y[%f,%f]\n", - iv_[LEFT], iv_[RIGHT], - height_[LEFT], height_[RIGHT]); + printf ("%f x + %f ends at %f\n", slope_, y_intercept_, end_); } Real -Building::intersection (Building const &other) const +Building::intersection_x (Building const &other) const { - return (zero_height_ - other.zero_height_) / (other.slope_ - slope_); + Real ret = (y_intercept_ - other.y_intercept_) / (other.slope_ - slope_); + return isnan (ret) ? -infinity_f : ret; } void -Building::leading_part (Real chop, Real h) +Building::leading_part (Real chop) { - assert (chop > iv_[LEFT] && chop <= iv_[RIGHT] && !equal (chop, iv_[LEFT])); - assert (equal (h, height (chop))); - iv_[RIGHT] = chop; - height_[RIGHT] = h; + assert (chop <= end_); + end_ = chop; } -static void -skyline_trailing_part (list *sky, Real x) +Building +Building::sloped_neighbour (Real start, Real horizon_padding, Direction d) const { - if (equal (x, sky->front ().iv_[RIGHT])) - sky->pop_front (); - else - assert (x < sky->front ().iv_[RIGHT]); + Real x = (d == LEFT) ? start : end_; + Real left = x; + Real right = x + d * horizon_padding; + Real left_height = height (x); + Real right_height = left_height - horizon_padding; + if (d == LEFT) + { + swap (left, right); + swap (left_height, right_height); + } + return Building (left, left_height, right_height, right); +} - if (!sky->empty ()) +static Real +first_intersection (Building const &b, list *const s, Real start_x) +{ + while (!s->empty () && start_x < b.end_) { - sky->front ().iv_[LEFT] = x; - sky->front ().height_[LEFT] = sky->front ().height (x); + Building c = s->front (); + if (c.conceals (b, start_x)) + return start_x; + + Real i = b.intersection_x (c); + if (i > start_x && i <= b.end_ && i <= c.end_) + return i; + + start_x = c.end_; + if (b.end_ > c.end_) + s->pop_front (); } + return b.end_; } bool -Building::obstructs (Building const &other) const +Building::conceals (Building const &other, Real x) const { - if (equal (intersection (other), iv_[LEFT]) || equal (height_[LEFT], other.height_[LEFT])) - return slope_ > other.slope_ || (slope_ == other.slope_ && zero_height_ > other.zero_height_); - return height_[LEFT] > other.height_[LEFT]; + if (slope_ == other.slope_) + return y_intercept_ > other.y_intercept_; + + /* 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_); } void Skyline::internal_merge_skyline (list *s1, list *s2, - list *const result) + list *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 ().obstructs (s1->front ())) - swap (s1, s2); + if (s2->front ().conceals (s1->front (), x)) + swap (s1, s2); Building b = s1->front (); - while (s2->front ().iv_[RIGHT] < b.iv_[RIGHT] - && s2->front ().height_[RIGHT] <= b.height (s2->front ().iv_[RIGHT]) + EPS) - s2->pop_front (); - - /* the front of s2 either intersects with b or it ends after b */ - Real end = infinity_f; - Real s2_end_height = s2->front ().height_[RIGHT]; - Real s1_end_height = b.height (s2->front ().iv_[RIGHT]); - if (s2_end_height > s1_end_height + EPS) - end = b.intersection (s2->front ()); - end = min (end, b.iv_[RIGHT]); - Real height = b.height (end); - - b.leading_part (end, height); - result->push_front (b); - - skyline_trailing_part (s1, end); - if (!s1->empty ()) - s1->front ().height_[LEFT] = height; - skyline_trailing_part (s2, end); + Real end = first_intersection (b, s2, x); + + if (s2->empty ()) + { + result->push_front (b); + break; + } + + /* only include buildings wider than epsilon */ + if (end > x + EPS) + { + b.leading_part (end); + result->push_front (b); + } + + if (end >= s1->front ().end_) + s1->pop_front (); + + x = end; } result->reverse (); } @@ -211,65 +236,144 @@ Skyline::internal_merge_skyline (list *s1, list *s2, static void empty_skyline (list *const ret) { - ret->push_front (Building (-infinity_f, -infinity_f, -infinity_f, infinity_f, 0)); + 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 const &b, list *const ret, Real max_slope) +single_skyline (Building b, Real start, Real horizon_padding, list *const ret) { - if (!isinf (b.iv_[RIGHT])) - ret->push_front (Building (b.iv_[RIGHT], b.height_[RIGHT], - -infinity_f, infinity_f, max_slope)); - ret->push_front (b); - if (!isinf (b.iv_[LEFT])) + 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)); + if (sloped_neighbours) + ret->push_front (b.sloped_neighbour (start, horizon_padding, RIGHT)); + + if (b.end_ > start + EPS) + ret->push_front (b); + + if (sloped_neighbours) + ret->push_front (b.sloped_neighbour (start, horizon_padding, LEFT)); + + if (!isinf (start)) ret->push_front (Building (-infinity_f, -infinity_f, - b.height_[LEFT], b.iv_[LEFT], max_slope)); + -infinity_f, start - horizon_padding)); } -void -Skyline::internal_build_skyline (list *buildings, list *const result) +/* remove a non-overlapping set of boxes from BOXES and build a skyline + out of them */ +static list +non_overlapping_skyline (list *const boxes, Real horizon_padding, Axis horizon_axis, Direction sky) { - vsize size = buildings->size (); + list result; + Real last_end = -infinity_f; + list::iterator i = boxes->begin (); + while (i != boxes->end ()) + { + Interval iv = (*i)[horizon_axis]; + + if (iv[LEFT] - horizon_padding < last_end) + { + 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)); + + 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); + if (sloped_neighbours) + result.push_front (b.sloped_neighbour (iv[LEFT] - horizon_padding, horizon_padding, RIGHT)); + + list::iterator j = i++; + boxes->erase (j); + last_end = result.front ().end_; + } + if (last_end < infinity_f) + result.push_front (Building (last_end, -infinity_f, -infinity_f, infinity_f)); + result.reverse (); + return result; +} + +class LessThanBox +{ + Axis a_; + +public: + LessThanBox (Axis a) + { + a_ = a; + } + + bool operator () (Box const &b1, Box const &b2) + { + return b1[a_][LEFT] < b2[a_][LEFT]; + } +}; + +list +Skyline::internal_build_skyline (list *boxes, Real horizon_padding, Axis horizon_axis, Direction sky) +{ + vsize size = boxes->size (); if (size == 0) { - empty_skyline (result); - return; + list result; + empty_skyline (&result); + return result; } else if (size == 1) { - single_skyline (buildings->front (), result, max_slope_); - return; + list result; + single_skyline (Building (boxes->front (), horizon_padding, horizon_axis, sky), + boxes->front ()[horizon_axis][LEFT] - horizon_padding, + horizon_padding, &result); + return result; } - list right_half; - list::iterator i = buildings->begin (); - - for (vsize s = 0; s < size/2; s++) - i++; - right_half.splice (right_half.end (), *buildings, i, buildings->end ()); + deque > partials; + boxes->sort (LessThanBox (horizon_axis)); + while (!boxes->empty ()) + partials.push_back (non_overlapping_skyline (boxes, horizon_padding, horizon_axis, sky)); - list right; - list left; - internal_build_skyline (&right_half, &right); - internal_build_skyline (buildings, &left); - internal_merge_skyline (&right, &left, result); + /* we'd like to say while (partials->size () > 1) but that's O (n). + Instead, we exit in the middle of the loop */ + while (!partials.empty ()) + { + list merged; + list one = partials.front (); + partials.pop_front (); + if (partials.empty ()) + return one; + + list two = partials.front (); + partials.pop_front (); + internal_merge_skyline (&one, &two, &merged); + partials.push_back (merged); + } + assert (0); + return list (); } Skyline::Skyline () { - max_slope_ = 2; sky_ = UP; empty_skyline (&buildings_); - - } Skyline::Skyline (Skyline const &src) { - max_slope_ = src.max_slope_; 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++) { @@ -279,51 +383,73 @@ Skyline::Skyline (Skyline const &src) Skyline::Skyline (Direction sky) { - max_slope_ = 2; sky_ = sky; empty_skyline (&buildings_); } /* - build skyline from a set of boxes. + 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_; +} - Boxes should have fatness in the horizon_axis, otherwise they are ignored. +/* + 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 + width horizon_padding). That is, the total amount of horizontal expansion is + horizon_padding*4, half of which is sloped and half of which is flat. + + Boxes should have fatness in the horizon_axis (after they are expanded by + horizon_padding), otherwise they are ignored. */ -Skyline::Skyline (vector const &boxes, Axis horizon_axis, Direction sky) +Skyline::Skyline (vector const &boxes, Real horizon_padding, Axis horizon_axis, Direction sky) { - list bldgs; + list filtered_boxes; sky_ = sky; - max_slope_ = 2; + Axis vert_axis = other_axis (horizon_axis); for (vsize i = 0; i < boxes.size (); i++) { Interval iv = boxes[i][horizon_axis]; - Real height = sky * boxes[i][other_axis (horizon_axis)][sky]; - if (!iv.is_empty () && !isinf (height) && !equal (iv[LEFT], iv[RIGHT])) - bldgs.push_front (Building (iv[LEFT], height, height, iv[RIGHT], - max_slope_)); + iv.widen (horizon_padding); + if (iv.length () > EPS && !boxes[i][vert_axis].is_empty ()) + filtered_boxes.push_front (boxes[i]); } - - internal_build_skyline (&bldgs, &buildings_); - assert (is_legal_skyline ()); + + buildings_ = internal_build_skyline (&filtered_boxes, horizon_padding, horizon_axis, sky); } -Skyline::Skyline (vector const &points, Real max_slope, Direction sky) +Skyline::Skyline (Box const &b, Real horizon_padding, Axis horizon_axis, Direction sky) { sky_ = sky; - max_slope_ = max_slope; - - for (vsize i = 1; i < points.size (); i++) - { - buildings_.push_back (Building (points[i-1][X_AXIS], sky * points[i-1][Y_AXIS], - sky * points[i][Y_AXIS], - - points[i][X_AXIS], - max_slope)); - - } - - assert (is_legal_skyline ()); + Building front (b, horizon_padding, horizon_axis, sky); + single_skyline (front, b[horizon_axis][LEFT] - horizon_padding, + horizon_padding, &buildings_); } void @@ -335,56 +461,121 @@ Skyline::merge (Skyline const &other) list my_bld; my_bld.splice (my_bld.begin (), buildings_); internal_merge_skyline (&other_bld, &my_bld, &buildings_); - assert (is_legal_skyline ()); } void -Skyline::insert (Box const &b, Axis a) +Skyline::insert (Box const &b, Real horizon_padding, Axis a) { list other_bld; list my_bld; - Interval iv = b[a]; - Real height = sky_ * b[other_axis (a)][sky_]; - assert (!iv.is_empty ()); + 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 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 (iv[LEFT], height, height, iv[RIGHT], max_slope_), &other_bld, max_slope_); + 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_); - assert (is_legal_skyline ()); } void Skyline::raise (Real r) +{ + list::iterator end = buildings_.end (); + for (list::iterator i = buildings_.begin (); i != end; i++) + i->y_intercept_ += sky_ * r; +} + +void +Skyline::shift (Real s) { list::iterator end = buildings_.end (); for (list::iterator i = buildings_.begin (); i != end; i++) { - i->height_[LEFT] += sky_ * r; - i->height_[RIGHT] += sky_ * r; - i->zero_height_ += sky_ * r; + i->end_ += s; + i->y_intercept_ -= s * i->slope_; } - assert (is_legal_skyline ()); } 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; - while (i != buildings_.end () && j != other.buildings_.end ()) + Real start = -infinity_f; + Real touch = -infinity_f; + while (i != padded_this->buildings_.end () && j != padded_other->buildings_.end ()) { - Interval iv = intersection (i->iv_, j->iv_); - dist = max (dist, max (i->height (iv[LEFT]) + j->height (iv[LEFT]), - i->height (iv[RIGHT]) + j->height (iv[RIGHT]))); - if (i->iv_[RIGHT] <= j->iv_[RIGHT]) - i++; + 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++; else - j++; + j++; + start = end; } + + if (created_tmp_skylines) + { + delete padded_this; + delete padded_other; + } + + *touch_point = touch; return dist; } @@ -396,8 +587,8 @@ Skyline::height (Real airplane) const list::const_iterator i; for (i = buildings_.begin (); i != buildings_.end (); i++) { - if (i->iv_[RIGHT] >= airplane) - return sky_ * i->height (airplane); + if (i->end_ >= airplane) + return sky_ * i->height (airplane); } assert (0); @@ -412,46 +603,67 @@ 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) { Skyline s (sky_); - s.buildings_.front ().height_[LEFT] = h * sky_; - s.buildings_.front ().height_[RIGHT] = h * sky_; - s.buildings_.front ().zero_height_ = h * sky_; + s.buildings_.front ().y_intercept_ = h * sky_; merge (s); } - vector -Skyline::to_points () const +Skyline::to_points (Axis horizon_axis) const { vector out; - bool first = true; + Real start = -infinity_f; for (list::const_iterator i (buildings_.begin ()); i != buildings_.end (); i++) { - if (first) - out.push_back (Offset ((*i).iv_[LEFT], sky_ * (*i).height_[LEFT])); - - first = false; - out.push_back (Offset ((*i).iv_[RIGHT], sky_ * (*i).height_[RIGHT])); + out.push_back (Offset (start, sky_ * i->height (start))); + out.push_back (Offset (i->end_, sky_ * i->height (i->end_))); + start = i->end_; } + if (horizon_axis == Y_AXIS) + for (vsize i = 0; i < out.size (); i++) + out[i] = out[i].swapped (); + return out; } -/****************************************************************/ +bool +Skyline::is_empty () const +{ + Building b = buildings_.front (); + return b.end_ == infinity_f && b.y_intercept_ == -infinity_f; +} + +void +Skyline::clear () +{ + buildings_.clear (); + empty_skyline (&buildings_); +} +/****************************************************************/ 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 (s); return SCM_EOL; } @@ -465,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)); +}