Consider accidentals in optical spacing correction.
[lilypond.git] / lily / skyline.cc
bloba3047a98bbcefffe4c071df3fa49618ee9ddc453
1 /* skyline.cc -- implement the Skyline class
3 source file of the GNU LilyPond music typesetter
5 (c) 2006--2009 Joe Neeman <joeneeman@gmail.com>
6 */
8 #include "skyline.hh"
9 #include <deque>
10 #include <cstdio>
12 #include "ly-smobs.icc"
14 /* A skyline is a sequence of non-overlapping buildings: something like
15 this:
16 _______
17 | \ ________
18 | \ ________/ \
19 /\ | \ / \
20 / -------- \ / \
21 / \ / \
22 / ------------/ ----
24 Each building has a starting position, and ending position, a starting
25 height and an ending height.
27 The following invariants are observed:
28 - the start of the first building is at -infinity
29 - the end of the last building is at infinity
30 - if a building has infinite length (ie. the first and last buildings),
31 then its starting height and ending height are equal
32 - the end of one building is the same as the beginning of the next
33 building
35 We also allow skylines to point down (the structure is exactly the same,
36 but we think of the part above the line as being filled with mass and the
37 part below as being empty). ::distance finds the minimum distance between
38 an UP skyline and a DOWN skyline.
40 Note that we store DOWN skylines upside-down. That is, in order to compare
41 a DOWN skyline with an UP skyline, we need to flip the DOWN skyline first.
42 This means that the merging routine doesn't need to be aware of direction,
43 but the distance routine does.
46 /* If we start including very thin buildings, numerical accuracy errors can
47 arise. Therefore, we ignore all buildings that are less than epsilon wide. */
48 #define EPS 1e-5
50 static void
51 print_buildings (list<Building> const &b)
53 for (list<Building>::const_iterator i = b.begin (); i != b.end (); i++)
54 i->print ();
57 void
58 Skyline::print () const
60 print_buildings (buildings_);
63 void
64 Skyline::print_points () const
66 vector<Offset> ps (to_points (X_AXIS));
68 for (vsize i = 0; i < ps.size (); i++)
69 printf ("(%f,%f)%s" , ps[i][X_AXIS], ps[i][Y_AXIS],
70 (i%2)==1 ? "\n" : " ");
73 Building::Building (Real start, Real start_height, Real end_height, Real end)
75 if (isinf (start) || isinf (end))
76 assert (start_height == end_height);
78 end_ = end;
79 precompute (start, start_height, end_height, end);
82 Building::Building (Box const &b, Real horizon_padding, Axis horizon_axis, Direction sky)
84 Real start = b[horizon_axis][LEFT] - horizon_padding;
85 Real end = b[horizon_axis][RIGHT] + horizon_padding;
86 Real height = sky * b[other_axis (horizon_axis)][sky];
88 end_ = end;
89 precompute (start, height, height, end);
92 void
93 Building::precompute (Real start, Real start_height, Real end_height, Real end)
95 slope_ = (end_height - start_height) / (end - start);
96 if (start_height == end_height) /* if they were both infinite, we would get nan, not 0, from the prev line */
97 slope_ = 0;
99 assert (!isinf (slope_) && !isnan (slope_));
101 if (isinf (start))
103 assert (start_height == end_height);
104 y_intercept_ = start_height;
106 else
107 y_intercept_ = start_height - slope_ * start;
110 Real
111 Building::height (Real x) const
113 return isinf (x) ? y_intercept_ : slope_*x + y_intercept_;
116 void
117 Building::print () const
119 printf ("%f x + %f ends at %f\n", slope_, y_intercept_, end_);
122 Real
123 Building::intersection_x (Building const &other) const
125 Real ret = (y_intercept_ - other.y_intercept_) / (other.slope_ - slope_);
126 return isnan (ret) ? -infinity_f : ret;
129 void
130 Building::leading_part (Real chop)
132 assert (chop <= end_);
133 end_ = chop;
136 Building
137 Building::sloped_neighbour (Real start, Real horizon_padding, Direction d) const
139 Real x = (d == LEFT) ? start : end_;
140 Real left = x;
141 Real right = x + d * horizon_padding;
142 Real left_height = height (x);
143 Real right_height = left_height - horizon_padding;
144 if (d == LEFT)
146 swap (left, right);
147 swap (left_height, right_height);
149 return Building (left, left_height, right_height, right);
152 static Real
153 first_intersection (Building const &b, list<Building> *const s, Real start_x)
155 while (!s->empty () && start_x < b.end_)
157 Building c = s->front ();
158 if (c.conceals (b, start_x))
159 return start_x;
161 Real i = b.intersection_x (c);
162 if (i > start_x && i <= b.end_ && i <= c.end_)
163 return i;
165 start_x = c.end_;
166 if (b.end_ > c.end_)
167 s->pop_front ();
169 return b.end_;
172 bool
173 Building::conceals (Building const &other, Real x) const
175 if (slope_ == other.slope_)
176 return y_intercept_ > other.y_intercept_;
178 /* their slopes were not equal, so there is an intersection point */
179 Real i = intersection_x (other);
180 return (i <= x && slope_ > other.slope_)
181 || (i > x && slope_ < other.slope_);
184 void
185 Skyline::internal_merge_skyline (list<Building> *s1, list<Building> *s2,
186 list<Building> *const result)
188 if (s1->empty () || s2->empty ())
190 programming_error ("tried to merge an empty skyline");
191 return;
194 Real x = -infinity_f;
195 while (!s1->empty ())
197 if (s2->front ().conceals (s1->front (), x))
198 swap (s1, s2);
200 Building b = s1->front ();
201 Real end = first_intersection (b, s2, x);
203 if (s2->empty ())
205 result->push_front (b);
206 break;
209 /* only include buildings wider than epsilon */
210 if (end > x + EPS)
212 b.leading_part (end);
213 result->push_front (b);
216 if (end >= s1->front ().end_)
217 s1->pop_front ();
219 x = end;
221 result->reverse ();
224 static void
225 empty_skyline (list<Building> *const ret)
227 ret->push_front (Building (-infinity_f, -infinity_f, -infinity_f, infinity_f));
230 static void
231 single_skyline (Building b, Real start, Real horizon_padding, list<Building> *const ret)
233 bool sloped_neighbours = horizon_padding > 0 && !isinf (start) && !isinf (b.end_);
234 if (!isinf (b.end_))
235 ret->push_front (Building (b.end_ + horizon_padding, -infinity_f,
236 -infinity_f, infinity_f));
237 if (sloped_neighbours)
238 ret->push_front (b.sloped_neighbour (start, horizon_padding, RIGHT));
240 if (b.end_ > start + EPS)
241 ret->push_front (b);
243 if (sloped_neighbours)
244 ret->push_front (b.sloped_neighbour (start, horizon_padding, LEFT));
246 if (!isinf (start))
247 ret->push_front (Building (-infinity_f, -infinity_f,
248 -infinity_f, start - horizon_padding));
251 /* remove a non-overlapping set of boxes from BOXES and build a skyline
252 out of them */
253 static list<Building>
254 non_overlapping_skyline (list<Box> *const boxes, Real horizon_padding, Axis horizon_axis, Direction sky)
256 list<Building> result;
257 Real last_end = -infinity_f;
258 list<Box>::iterator i = boxes->begin ();
259 while (i != boxes->end ())
261 Interval iv = (*i)[horizon_axis];
263 if (iv[LEFT] - horizon_padding < last_end)
265 i++;
266 continue;
269 if (iv[LEFT] - horizon_padding > last_end + EPS)
270 result.push_front (Building (last_end, -infinity_f, -infinity_f, iv[LEFT] - 2*horizon_padding));
272 Building b (*i, horizon_padding, horizon_axis, sky);
273 bool sloped_neighbours = horizon_padding > 0 && !isinf (iv.length ());
274 if (sloped_neighbours)
275 result.push_front (b.sloped_neighbour (iv[LEFT] - horizon_padding, horizon_padding, LEFT));
276 result.push_front (b);
277 if (sloped_neighbours)
278 result.push_front (b.sloped_neighbour (iv[LEFT] - horizon_padding, horizon_padding, RIGHT));
280 list<Box>::iterator j = i++;
281 boxes->erase (j);
282 last_end = result.front ().end_;
284 if (last_end < infinity_f)
285 result.push_front (Building (last_end, -infinity_f, -infinity_f, infinity_f));
286 result.reverse ();
287 return result;
290 class LessThanBox
292 Axis a_;
294 public:
295 LessThanBox (Axis a)
297 a_ = a;
300 bool operator() (Box const &b1, Box const &b2)
302 return b1[a_][LEFT] < b2[a_][LEFT];
306 list<Building>
307 Skyline::internal_build_skyline (list<Box> *boxes, Real horizon_padding, Axis horizon_axis, Direction sky)
309 vsize size = boxes->size ();
311 if (size == 0)
313 list<Building> result;
314 empty_skyline (&result);
315 return result;
317 else if (size == 1)
319 list<Building> result;
320 single_skyline (Building (boxes->front (), horizon_padding, horizon_axis, sky),
321 boxes->front ()[horizon_axis][LEFT], horizon_padding, &result);
322 return result;
325 deque<list<Building> > partials;
326 boxes->sort (LessThanBox (horizon_axis));
327 while (!boxes->empty ())
328 partials.push_back (non_overlapping_skyline (boxes, horizon_padding, horizon_axis, sky));
330 /* we'd like to say while (partials->size () > 1) but that's O (n).
331 Instead, we exit in the middle of the loop */
332 while (!partials.empty ())
334 list<Building> merged;
335 list<Building> one = partials.front ();
336 partials.pop_front ();
337 if (partials.empty ())
338 return one;
340 list<Building> two = partials.front ();
341 partials.pop_front ();
342 internal_merge_skyline (&one, &two, &merged);
343 partials.push_back (merged);
345 assert (0);
346 return list<Building> ();
349 Skyline::Skyline ()
351 sky_ = UP;
352 empty_skyline (&buildings_);
355 Skyline::Skyline (Skyline const &src)
357 sky_ = src.sky_;
359 /* doesn't a list's copy constructor do this? -- jneem */
360 for (list<Building>::const_iterator i = src.buildings_.begin ();
361 i != src.buildings_.end (); i++)
363 buildings_.push_back (Building ((*i)));
367 Skyline::Skyline (Direction sky)
369 sky_ = sky;
370 empty_skyline (&buildings_);
374 build skyline from a set of boxes. If horizon_padding > 0, expand all the boxes
375 by that amount and add 45-degree sloped boxes to the edges of each box (of
376 width horizon_padding). That is, the total amount of horizontal expansion is
377 horizon_padding*4, half of which is sloped and half of which is flat.
379 Boxes should have fatness in the horizon_axis (after they are expanded by
380 horizon_padding), otherwise they are ignored.
382 Skyline::Skyline (vector<Box> const &boxes, Real horizon_padding, Axis horizon_axis, Direction sky)
384 list<Box> filtered_boxes;
385 sky_ = sky;
387 Axis vert_axis = other_axis (horizon_axis);
388 for (vsize i = 0; i < boxes.size (); i++)
390 Interval iv = boxes[i][horizon_axis];
391 iv.widen (horizon_padding);
392 if (iv.length () > EPS && !boxes[i][vert_axis].is_empty ())
393 filtered_boxes.push_front (boxes[i]);
396 buildings_ = internal_build_skyline (&filtered_boxes, horizon_padding, horizon_axis, sky);
399 Skyline::Skyline (Box const &b, Real horizon_padding, Axis horizon_axis, Direction sky)
401 sky_ = sky;
402 Building front (b, horizon_padding, horizon_axis, sky);
403 single_skyline (front, b[horizon_axis][LEFT], horizon_padding, &buildings_);
406 void
407 Skyline::merge (Skyline const &other)
409 assert (sky_ == other.sky_);
411 list<Building> other_bld (other.buildings_);
412 list<Building> my_bld;
413 my_bld.splice (my_bld.begin (), buildings_);
414 internal_merge_skyline (&other_bld, &my_bld, &buildings_);
417 void
418 Skyline::insert (Box const &b, Real horizon_padding, Axis a)
420 list<Building> other_bld;
421 list<Building> my_bld;
423 if (isnan (b[other_axis (a)][LEFT])
424 || isnan (b[other_axis (a)][RIGHT]))
426 programming_error ("insane box for skyline");
427 return;
430 /* do the same filtering as in Skyline (vector<Box> const&, etc.) */
431 Interval iv = b[a];
432 iv.widen (horizon_padding);
433 if (iv.length () <= EPS || b[other_axis (a)].is_empty ())
434 return;
436 my_bld.splice (my_bld.begin (), buildings_);
437 single_skyline (Building (b, horizon_padding, a, sky_), b[a][LEFT], horizon_padding, &other_bld);
438 internal_merge_skyline (&other_bld, &my_bld, &buildings_);
441 void
442 Skyline::raise (Real r)
444 list<Building>::iterator end = buildings_.end ();
445 for (list<Building>::iterator i = buildings_.begin (); i != end; i++)
446 i->y_intercept_ += sky_ * r;
449 void
450 Skyline::shift (Real s)
452 list<Building>::iterator end = buildings_.end ();
453 for (list<Building>::iterator i = buildings_.begin (); i != end; i++)
455 i->end_ += s;
456 i->y_intercept_ -= s * i->slope_;
460 Real
461 Skyline::distance (Skyline const &other) const
463 assert (sky_ == -other.sky_);
464 list<Building>::const_iterator i = buildings_.begin ();
465 list<Building>::const_iterator j = other.buildings_.begin ();
467 Real dist = -infinity_f;
468 Real start = -infinity_f;
469 while (i != buildings_.end () && j != other.buildings_.end ())
471 Real end = min (i->end_, j->end_);
472 Real start_dist = i->height (start) + j->height (start);
473 Real end_dist = i->height (end) + j->height (end);
474 dist = max (dist, max (start_dist, end_dist));
475 if (i->end_ <= j->end_)
476 i++;
477 else
478 j++;
479 start = end;
481 return dist;
484 Real
485 Skyline::height (Real airplane) const
487 assert (!isinf (airplane));
489 list<Building>::const_iterator i;
490 for (i = buildings_.begin (); i != buildings_.end (); i++)
492 if (i->end_ >= airplane)
493 return sky_ * i->height (airplane);
496 assert (0);
497 return 0;
500 Real
501 Skyline::max_height () const
503 Skyline s (-sky_);
504 s.set_minimum_height (0);
505 return sky_ * distance (s);
508 void
509 Skyline::set_minimum_height (Real h)
511 Skyline s (sky_);
512 s.buildings_.front ().y_intercept_ = h * sky_;
513 merge (s);
517 vector<Offset>
518 Skyline::to_points (Axis horizon_axis) const
520 vector<Offset> out;
522 Real start = -infinity_f;
523 for (list<Building>::const_iterator i (buildings_.begin ());
524 i != buildings_.end (); i++)
526 out.push_back (Offset (start, sky_ * i->height (start)));
527 out.push_back (Offset (i->end_, sky_ * i->height (i->end_)));
528 start = i->end_;
531 if (horizon_axis == Y_AXIS)
532 for (vsize i = 0; i < out.size (); i++)
533 out[i] = out[i].swapped ();
535 return out;
538 bool
539 Skyline::is_empty () const
541 Building b = buildings_.front ();
542 return b.end_ == infinity_f && b.y_intercept_ == -infinity_f;
546 /****************************************************************/
549 IMPLEMENT_SIMPLE_SMOBS (Skyline);
550 IMPLEMENT_TYPE_P (Skyline, "ly:skyline?");
551 IMPLEMENT_DEFAULT_EQUAL_P (Skyline);
554 Skyline::mark_smob (SCM)
556 ASSERT_LIVE_IS_ALLOWED ();
557 return SCM_EOL;
561 Skyline::print_smob (SCM s, SCM port, scm_print_state *)
563 Skyline *r = (Skyline *) SCM_CELL_WORD_1 (s);
564 (void) r;
566 scm_puts ("#<Skyline>", port);
568 return 1;