1 /* skyline.cc -- implement the Skyline class
3 source file of the GNU LilyPond music typesetter
5 (c) 2006--2007 Joe Neeman <joeneeman@gmail.com>
11 #include "ly-smobs.icc"
13 /* A skyline is a sequence of non-overlapping buildings: something like
23 Each building has a starting position, and ending position, a starting
24 height and an ending height.
26 The following invariants are observed:
27 - the start of the first building is at -infinity
28 - the end of the last building is at infinity
29 - if a building has infinite length (ie. the first and last buildings),
30 then its starting height and ending height are equal
31 - the end of one building is the same as the beginning of the next
34 We also allow skylines to point down (the structure is exactly the same,
35 but we think of the part above the line as being filled with mass and the
36 part below as being empty). ::distance finds the minimum distance between
37 an UP skyline and a DOWN skyline.
39 Note that we store DOWN skylines upside-down. That is, in order to compare
40 a DOWN skyline with an UP skyline, we need to flip the DOWN skyline first.
41 This means that the merging routine doesn't need to be aware of direction,
42 but the distance routine does.
45 /* If we start including very thin buildings, numerical accuracy errors can
46 arise. Therefore, we ignore all buildings that are less than epsilon wide. */
50 print_buildings (list
<Building
> const &b
)
52 for (list
<Building
>::const_iterator i
= b
.begin (); i
!= b
.end (); i
++)
57 Skyline::print () const
59 print_buildings (buildings_
);
63 Skyline::print_points () const
65 vector
<Offset
> ps (to_points (X_AXIS
));
67 for (vsize i
= 0; i
< ps
.size (); i
++)
68 printf ("(%f,%f)%s" , ps
[i
][X_AXIS
], ps
[i
][Y_AXIS
],
69 (i
%2)==1 ? "\n" : " ");
72 Building::Building (Real start
, Real start_height
, Real end_height
, Real end
)
74 if (isinf (start
) || isinf (end
))
75 assert (start_height
== end_height
);
78 precompute (start
, start_height
, end_height
, end
);
81 Building::Building (Box
const &b
, Real horizon_padding
, Axis horizon_axis
, Direction sky
)
83 Real start
= b
[horizon_axis
][LEFT
] - horizon_padding
;
84 Real end
= b
[horizon_axis
][RIGHT
] + horizon_padding
;
85 Real height
= sky
* b
[other_axis (horizon_axis
)][sky
];
88 precompute (start
, height
, height
, end
);
92 Building::precompute (Real start
, Real start_height
, Real end_height
, Real end
)
94 slope_
= (end_height
- start_height
) / (end
- start
);
95 if (start_height
== end_height
) /* if they were both infinite, we would get nan, not 0, from the prev line */
98 assert (!isinf (slope_
) && !isnan (slope_
));
102 assert (start_height
== end_height
);
103 y_intercept_
= start_height
;
106 y_intercept_
= start_height
- slope_
* start
;
110 Building::height (Real x
) const
112 return isinf (x
) ? y_intercept_
: slope_
*x
+ y_intercept_
;
116 Building::print () const
118 printf ("%f x + %f ends at %f\n", slope_
, y_intercept_
, end_
);
122 Building::intersection_x (Building
const &other
) const
124 Real ret
= (y_intercept_
- other
.y_intercept_
) / (other
.slope_
- slope_
);
125 return isnan (ret
) ? -infinity_f
: ret
;
129 Building::leading_part (Real chop
)
131 assert (chop
<= end_
);
136 Building::sloped_neighbour (Real start
, Real horizon_padding
, Direction d
) const
138 Real x
= (d
== LEFT
) ? start
: end_
;
140 Real right
= x
+ d
* horizon_padding
;
141 Real left_height
= height (x
);
142 Real right_height
= left_height
- horizon_padding
;
146 swap (left_height
, right_height
);
148 return Building (left
, left_height
, right_height
, right
);
152 first_intersection (Building
const &b
, list
<Building
> *const s
, Real start_x
)
154 while (!s
->empty () && start_x
< b
.end_
)
156 Building c
= s
->front ();
157 if (c
.conceals (b
, start_x
))
160 Real i
= b
.intersection_x (c
);
161 if (i
> start_x
&& i
<= b
.end_
&& i
<= c
.end_
)
172 Building::conceals (Building
const &other
, Real x
) const
174 if (slope_
== other
.slope_
)
175 return y_intercept_
> other
.y_intercept_
;
177 /* their slopes were not equal, so there is an intersection point */
178 Real i
= intersection_x (other
);
179 return (i
<= x
&& slope_
> other
.slope_
)
180 || (i
> x
&& slope_
< other
.slope_
);
184 Skyline::internal_merge_skyline (list
<Building
> *s1
, list
<Building
> *s2
,
185 list
<Building
> *const result
)
187 if (s1
->empty () || s2
->empty ())
189 programming_error ("tried to merge an empty skyline");
193 Real x
= -infinity_f
;
194 while (!s1
->empty ())
196 if (s2
->front ().conceals (s1
->front (), x
))
199 Building b
= s1
->front ();
200 Real end
= first_intersection (b
, s2
, x
);
204 result
->push_front (b
);
208 /* only include buildings wider than epsilon */
211 b
.leading_part (end
);
212 result
->push_front (b
);
215 if (end
>= s1
->front ().end_
)
224 empty_skyline (list
<Building
> *const ret
)
226 ret
->push_front (Building (-infinity_f
, -infinity_f
, -infinity_f
, infinity_f
));
230 single_skyline (Building b
, Real start
, Real horizon_padding
, list
<Building
> *const ret
)
232 bool sloped_neighbours
= horizon_padding
> 0 && !isinf (start
) && !isinf (b
.end_
);
234 ret
->push_front (Building (b
.end_
+ horizon_padding
, -infinity_f
,
235 -infinity_f
, infinity_f
));
236 if (sloped_neighbours
)
237 ret
->push_front (b
.sloped_neighbour (start
, horizon_padding
, RIGHT
));
239 if (b
.end_
> start
+ EPS
)
242 if (sloped_neighbours
)
243 ret
->push_front (b
.sloped_neighbour (start
, horizon_padding
, LEFT
));
246 ret
->push_front (Building (-infinity_f
, -infinity_f
,
247 -infinity_f
, start
- horizon_padding
));
250 /* remove a non-overlapping set of boxes from BOXES and build a skyline
252 static list
<Building
>
253 non_overlapping_skyline (list
<Box
> *const boxes
, Real horizon_padding
, Axis horizon_axis
, Direction sky
)
255 list
<Building
> result
;
256 Real last_end
= -infinity_f
;
257 list
<Box
>::iterator i
= boxes
->begin ();
258 while (i
!= boxes
->end ())
260 Interval iv
= (*i
)[horizon_axis
];
262 if (iv
[LEFT
] - horizon_padding
< last_end
)
268 if (iv
[LEFT
] - horizon_padding
> last_end
+ EPS
)
269 result
.push_front (Building (last_end
, -infinity_f
, -infinity_f
, iv
[LEFT
] - 2*horizon_padding
));
271 Building
b (*i
, horizon_padding
, horizon_axis
, sky
);
272 bool sloped_neighbours
= horizon_padding
> 0 && !isinf (iv
.length ());
273 if (sloped_neighbours
)
274 result
.push_front (b
.sloped_neighbour (iv
[LEFT
] - horizon_padding
, horizon_padding
, LEFT
));
275 result
.push_front (b
);
276 if (sloped_neighbours
)
277 result
.push_front (b
.sloped_neighbour (iv
[LEFT
] - horizon_padding
, horizon_padding
, RIGHT
));
279 list
<Box
>::iterator j
= i
++;
281 last_end
= result
.front ().end_
;
283 if (last_end
< infinity_f
)
284 result
.push_front (Building (last_end
, -infinity_f
, -infinity_f
, infinity_f
));
299 bool operator() (Box
const &b1
, Box
const &b2
)
301 return b1
[a_
][LEFT
] < b2
[a_
][LEFT
];
306 Skyline::internal_build_skyline (list
<Box
> *boxes
, Real horizon_padding
, Axis horizon_axis
, Direction sky
)
308 vsize size
= boxes
->size ();
312 list
<Building
> result
;
313 empty_skyline (&result
);
318 list
<Building
> result
;
319 single_skyline (Building (boxes
->front (), horizon_padding
, horizon_axis
, sky
),
320 boxes
->front ()[horizon_axis
][LEFT
], horizon_axis
, &result
);
324 deque
<list
<Building
> > partials
;
325 boxes
->sort (LessThanBox (horizon_axis
));
326 while (!boxes
->empty ())
327 partials
.push_back (non_overlapping_skyline (boxes
, horizon_padding
, horizon_axis
, sky
));
329 /* we'd like to say while (partials->size () > 1) but that's O (n).
330 Instead, we exit in the middle of the loop */
331 while (!partials
.empty ())
333 list
<Building
> merged
;
334 list
<Building
> one
= partials
.front ();
335 partials
.pop_front ();
336 if (partials
.empty ())
339 list
<Building
> two
= partials
.front ();
340 partials
.pop_front ();
341 internal_merge_skyline (&one
, &two
, &merged
);
342 partials
.push_back (merged
);
345 return list
<Building
> ();
351 empty_skyline (&buildings_
);
354 Skyline::Skyline (Skyline
const &src
)
358 /* doesn't a list's copy constructor do this? -- jneem */
359 for (list
<Building
>::const_iterator i
= src
.buildings_
.begin ();
360 i
!= src
.buildings_
.end (); i
++)
362 buildings_
.push_back (Building ((*i
)));
366 Skyline::Skyline (Direction sky
)
369 empty_skyline (&buildings_
);
373 build skyline from a set of boxes. If horizon_padding > 0, expand all the boxes
374 by that amount and add 45-degree sloped boxes to the edges of each box (of
375 width horizon_padding). That is, the total amount of horizontal expansion is
376 horizon_padding*4, half of which is sloped and half of which is flat.
378 Boxes should have fatness in the horizon_axis (after they are expanded by
379 horizon_padding), otherwise they are ignored.
381 Skyline::Skyline (vector
<Box
> const &boxes
, Real horizon_padding
, Axis horizon_axis
, Direction sky
)
383 list
<Box
> filtered_boxes
;
386 Axis vert_axis
= other_axis (horizon_axis
);
387 for (vsize i
= 0; i
< boxes
.size (); i
++)
389 Interval iv
= boxes
[i
][horizon_axis
];
390 iv
.widen (horizon_padding
);
391 if (iv
.length () > EPS
&& !boxes
[i
][vert_axis
].is_empty ())
392 filtered_boxes
.push_front (boxes
[i
]);
395 buildings_
= internal_build_skyline (&filtered_boxes
, horizon_padding
, horizon_axis
, sky
);
398 Skyline::Skyline (Box
const &b
, Real horizon_padding
, Axis horizon_axis
, Direction sky
)
401 Building
front (b
, horizon_padding
, horizon_axis
, sky
);
402 single_skyline (front
, b
[horizon_axis
][LEFT
], horizon_padding
, &buildings_
);
406 Skyline::merge (Skyline
const &other
)
408 assert (sky_
== other
.sky_
);
410 list
<Building
> other_bld (other
.buildings_
);
411 list
<Building
> my_bld
;
412 my_bld
.splice (my_bld
.begin (), buildings_
);
413 internal_merge_skyline (&other_bld
, &my_bld
, &buildings_
);
417 Skyline::insert (Box
const &b
, Real horizon_padding
, Axis a
)
419 list
<Building
> other_bld
;
420 list
<Building
> my_bld
;
422 if (isnan (b
[other_axis (a
)][LEFT
])
423 || isnan (b
[other_axis (a
)][RIGHT
]))
425 programming_error ("insane box for skyline");
429 /* do the same filtering as in Skyline (vector<Box> const&, etc.) */
431 iv
.widen (horizon_padding
);
432 if (iv
.length () <= EPS
|| b
[other_axis (a
)].is_empty ())
435 my_bld
.splice (my_bld
.begin (), buildings_
);
436 single_skyline (Building (b
, horizon_padding
, a
, sky_
), b
[a
][LEFT
], horizon_padding
, &other_bld
);
437 internal_merge_skyline (&other_bld
, &my_bld
, &buildings_
);
441 Skyline::raise (Real r
)
443 list
<Building
>::iterator end
= buildings_
.end ();
444 for (list
<Building
>::iterator i
= buildings_
.begin (); i
!= end
; i
++)
445 i
->y_intercept_
+= sky_
* r
;
449 Skyline::shift (Real s
)
451 list
<Building
>::iterator end
= buildings_
.end ();
452 for (list
<Building
>::iterator i
= buildings_
.begin (); i
!= end
; i
++)
455 i
->y_intercept_
-= s
* i
->slope_
;
460 Skyline::distance (Skyline
const &other
) const
462 assert (sky_
== -other
.sky_
);
463 list
<Building
>::const_iterator i
= buildings_
.begin ();
464 list
<Building
>::const_iterator j
= other
.buildings_
.begin ();
466 Real dist
= -infinity_f
;
467 Real start
= -infinity_f
;
468 while (i
!= buildings_
.end () && j
!= other
.buildings_
.end ())
470 Real end
= min (i
->end_
, j
->end_
);
471 Real start_dist
= i
->height (start
) + j
->height (start
);
472 Real end_dist
= i
->height (end
) + j
->height (end
);
473 dist
= max (dist
, max (start_dist
, end_dist
));
474 if (i
->end_
<= j
->end_
)
484 Skyline::height (Real airplane
) const
486 assert (!isinf (airplane
));
488 list
<Building
>::const_iterator i
;
489 for (i
= buildings_
.begin (); i
!= buildings_
.end (); i
++)
491 if (i
->end_
>= airplane
)
492 return sky_
* i
->height (airplane
);
500 Skyline::max_height () const
503 s
.set_minimum_height (0);
504 return sky_
* distance (s
);
508 Skyline::set_minimum_height (Real h
)
511 s
.buildings_
.front ().y_intercept_
= h
* sky_
;
517 Skyline::to_points (Axis horizon_axis
) const
521 Real start
= -infinity_f
;
522 for (list
<Building
>::const_iterator
i (buildings_
.begin ());
523 i
!= buildings_
.end (); i
++)
525 out
.push_back (Offset (start
, sky_
* i
->height (start
)));
526 out
.push_back (Offset (i
->end_
, sky_
* i
->height (i
->end_
)));
530 if (horizon_axis
== Y_AXIS
)
531 for (vsize i
= 0; i
< out
.size (); i
++)
532 out
[i
] = out
[i
].swapped ();
538 Skyline::is_empty () const
540 Building b
= buildings_
.front ();
541 return b
.end_
== infinity_f
&& b
.y_intercept_
== -infinity_f
;
545 /****************************************************************/
548 IMPLEMENT_SIMPLE_SMOBS (Skyline
);
549 IMPLEMENT_TYPE_P (Skyline
, "ly:skyline?");
550 IMPLEMENT_DEFAULT_EQUAL_P (Skyline
);
553 Skyline::mark_smob (SCM
)
555 ASSERT_LIVE_IS_ALLOWED ();
560 Skyline::print_smob (SCM s
, SCM port
, scm_print_state
*)
562 Skyline
*r
= (Skyline
*) SCM_CELL_WORD_1 (s
);
565 scm_puts ("#<Skyline>", port
);