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>
12 #include "ly-smobs.icc"
14 /* A skyline is a sequence of non-overlapping buildings: something like
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
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. */
51 print_buildings (list
<Building
> const &b
)
53 for (list
<Building
>::const_iterator i
= b
.begin (); i
!= b
.end (); i
++)
58 Skyline::print () const
60 print_buildings (buildings_
);
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
);
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
];
89 precompute (start
, height
, height
, end
);
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 */
99 assert (!isinf (slope_
) && !isnan (slope_
));
103 assert (start_height
== end_height
);
104 y_intercept_
= start_height
;
107 y_intercept_
= start_height
- slope_
* start
;
111 Building::height (Real x
) const
113 return isinf (x
) ? y_intercept_
: slope_
*x
+ y_intercept_
;
117 Building::print () const
119 printf ("%f x + %f ends at %f\n", slope_
, y_intercept_
, end_
);
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
;
130 Building::leading_part (Real chop
)
132 assert (chop
<= end_
);
137 Building::sloped_neighbour (Real start
, Real horizon_padding
, Direction d
) const
139 Real x
= (d
== LEFT
) ? start
: end_
;
141 Real right
= x
+ d
* horizon_padding
;
142 Real left_height
= height (x
);
143 Real right_height
= left_height
- horizon_padding
;
147 swap (left_height
, right_height
);
149 return Building (left
, left_height
, right_height
, right
);
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
))
161 Real i
= b
.intersection_x (c
);
162 if (i
> start_x
&& i
<= b
.end_
&& i
<= c
.end_
)
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_
);
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");
194 Real x
= -infinity_f
;
195 while (!s1
->empty ())
197 if (s2
->front ().conceals (s1
->front (), x
))
200 Building b
= s1
->front ();
201 Real end
= first_intersection (b
, s2
, x
);
205 result
->push_front (b
);
209 /* only include buildings wider than epsilon */
212 b
.leading_part (end
);
213 result
->push_front (b
);
216 if (end
>= s1
->front ().end_
)
225 empty_skyline (list
<Building
> *const ret
)
227 ret
->push_front (Building (-infinity_f
, -infinity_f
, -infinity_f
, infinity_f
));
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_
);
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
)
243 if (sloped_neighbours
)
244 ret
->push_front (b
.sloped_neighbour (start
, horizon_padding
, LEFT
));
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
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
)
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
++;
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
));
300 bool operator() (Box
const &b1
, Box
const &b2
)
302 return b1
[a_
][LEFT
] < b2
[a_
][LEFT
];
307 Skyline::internal_build_skyline (list
<Box
> *boxes
, Real horizon_padding
, Axis horizon_axis
, Direction sky
)
309 vsize size
= boxes
->size ();
313 list
<Building
> result
;
314 empty_skyline (&result
);
319 list
<Building
> result
;
320 single_skyline (Building (boxes
->front (), horizon_padding
, horizon_axis
, sky
),
321 boxes
->front ()[horizon_axis
][LEFT
], horizon_padding
, &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 ())
340 list
<Building
> two
= partials
.front ();
341 partials
.pop_front ();
342 internal_merge_skyline (&one
, &two
, &merged
);
343 partials
.push_back (merged
);
346 return list
<Building
> ();
352 empty_skyline (&buildings_
);
355 Skyline::Skyline (Skyline
const &src
)
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
)
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
;
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
)
402 Building
front (b
, horizon_padding
, horizon_axis
, sky
);
403 single_skyline (front
, b
[horizon_axis
][LEFT
], horizon_padding
, &buildings_
);
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_
);
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");
430 /* do the same filtering as in Skyline (vector<Box> const&, etc.) */
432 iv
.widen (horizon_padding
);
433 if (iv
.length () <= EPS
|| b
[other_axis (a
)].is_empty ())
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_
);
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
;
450 Skyline::shift (Real s
)
452 list
<Building
>::iterator end
= buildings_
.end ();
453 for (list
<Building
>::iterator i
= buildings_
.begin (); i
!= end
; i
++)
456 i
->y_intercept_
-= s
* i
->slope_
;
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_
)
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
);
501 Skyline::max_height () const
504 s
.set_minimum_height (0);
505 return sky_
* distance (s
);
509 Skyline::set_minimum_height (Real h
)
512 s
.buildings_
.front ().y_intercept_
= h
* sky_
;
518 Skyline::to_points (Axis horizon_axis
) const
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_
)));
531 if (horizon_axis
== Y_AXIS
)
532 for (vsize i
= 0; i
< out
.size (); i
++)
533 out
[i
] = out
[i
].swapped ();
539 Skyline::is_empty () const
541 Building b
= buildings_
.front ();
542 return b
.end_
== infinity_f
&& b
.y_intercept_
== -infinity_f
;
549 empty_skyline (&buildings_
);
552 /****************************************************************/
555 IMPLEMENT_SIMPLE_SMOBS (Skyline
);
556 IMPLEMENT_TYPE_P (Skyline
, "ly:skyline?");
557 IMPLEMENT_DEFAULT_EQUAL_P (Skyline
);
560 Skyline::mark_smob (SCM
)
562 ASSERT_LIVE_IS_ALLOWED ();
567 Skyline::print_smob (SCM s
, SCM port
, scm_print_state
*)
569 Skyline
*r
= (Skyline
*) SCM_CELL_WORD_1 (s
);
572 scm_puts ("#<Skyline>", port
);