2 skyline.cc -- implement Skyline_entry and funcs.
4 source file of the GNU LilyPond music typesetter
6 (c) 2002--2006 Han-Wen Nienhuys <hanwen@xs4all.nl>
12 A skyline is a shape of the form:
24 This file deals with building such skyline structure, and computing
25 the minimum distance between two opposing skylines.
27 Invariants for a skyline:
29 skyline[...].width_ forms a partition of the real interval, where
30 the segments are adjacent, and ascending. Hence we have
32 skyline.back ().width_[RIGHT] = inf
33 skyline[0].width_[LEFT] = -inf
36 const Real EPS = 1e-12;
39 TODO: avoid unnecessary fragmentation.
41 This is O (n^2), searching and insertion. Could be O (n log n) with
45 insert_extent_into_skyline (vector<Skyline_entry> *line, Box b, Axis line_axis,
48 Interval extent = b[line_axis];
49 if (extent.is_empty ())
52 Real stick_out = b[other_axis (line_axis)][d];
55 Intersect each segment of LINE with EXTENT, and if non-empty, insert relevant segments.
57 for (vsize i = line->size (); i--;)
59 Interval w = line->at (i).width_;
62 if (extent[LEFT] >= w[RIGHT])
65 Real my_height = line->at (i).height_;
69 && d * (my_height - stick_out) < 0)
71 Interval e1 (line->at (i).width_[LEFT], extent[LEFT]);
72 Interval e3 (extent[RIGHT], line->at (i).width_[RIGHT]);
74 if (!e3.is_empty () && e3.length () > EPS)
75 line->insert (line->begin () + i + 1, Skyline_entry (e3, my_height));
77 line->at (i).height_ = stick_out;
78 line->at (i).width_ = w;
79 if (!e1.is_empty () && e1.length () > EPS)
80 line->insert (line->begin () + i, Skyline_entry (e1, my_height));
86 merge_skyline (vector<Skyline_entry> *a1,
87 vector<Skyline_entry> const &a2,
90 for (vsize i = 0; i < a2.size (); i++)
93 b[X_AXIS] = a2[i].width_;
94 b[Y_AXIS][dir] = a2[i].height_;
95 b[Y_AXIS][-dir] = dir * infinity_f;
97 insert_extent_into_skyline (a1, b, X_AXIS, dir);
101 vector<Skyline_entry>
102 empty_skyline (Direction d)
104 vector<Skyline_entry> skyline;
111 e.height_ = -d * infinity_f;
112 skyline.push_back (e);
116 vector<Skyline_entry>
117 extents_to_skyline (vector<Box> const &extents, Axis a, Direction d)
120 vector<Skyline_entry> skyline = empty_skyline (d);
123 This makes a cubic algorithm (array insertion is O (n),
124 searching the array dumbly is O (n), and for n items, we get O (n^3).)
126 We could do a lot better (n log (n), using a balanced tree) but
127 that seems overkill for now.
129 for (vsize j = extents.size (); j--;)
130 insert_extent_into_skyline (&skyline, extents[j], a, d);
136 minimum distance that can be achieved between baselines. "Clouds" is
137 a skyline pointing down.
139 This is an O (n) algorithm.
142 skyline_meshing_distance (vector<Skyline_entry> const &buildings,
143 vector<Skyline_entry> const &clouds)
145 int i = buildings.size () -1;
146 int j = clouds.size () -1;
148 Real distance = -infinity_f;
150 while (i > 0 || j > 0)
152 Interval w = buildings[i].width_;
153 w.intersect (clouds[j].width_);
156 distance = max (distance, (buildings[i].height_ - clouds[j].height_));
158 if (i > 0 && buildings[i].width_[LEFT] >= clouds[j].width_[LEFT])
160 else if (j > 0 && buildings[i].width_[LEFT] <= clouds[j].width_[LEFT])
167 Skyline_entry::Skyline_entry ()
172 Skyline_entry::Skyline_entry (Interval i, Real r)
179 heighten_skyline (vector<Skyline_entry> *buildings, Real ground)
181 for (vsize i = 0; i < buildings->size (); i++)
182 buildings->at (i).height_ += ground;
186 skyline_height (vector<Skyline_entry> const &buildings,
190 Real h = - sky_dir * infinity_f;
193 Ugh! linear, should be O(log n).
195 for (vsize i = 0; i < buildings.size (); i++)
196 if (buildings[i].width_.contains (airplane))
197 h = sky_dir * max (sky_dir * h,
198 sky_dir * buildings[i].height_);