Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(247)

Side by Side Diff: lily/skyline.cc

Issue 5626052: Gets vertical skylines from grob stencils (Closed)
Patch Set: Corrects padding for horizontal skylines Created 12 years, 8 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 This file is part of LilyPond, the GNU music typesetter. 2 This file is part of LilyPond, the GNU music typesetter.
3 3
4 Copyright (C) 2006--2012 Joe Neeman <joeneeman@gmail.com> 4 Copyright (C) 2006--2012 Joe Neeman <joeneeman@gmail.com>
5 5
6 LilyPond is free software: you can redistribute it and/or modify 6 LilyPond is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by 7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or 8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version. 9 (at your option) any later version.
10 10
11 LilyPond is distributed in the hope that it will be useful, 11 LilyPond is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details. 14 GNU General Public License for more details.
15 15
16 You should have received a copy of the GNU General Public License 16 You should have received a copy of the GNU General Public License
17 along with LilyPond. If not, see <http://www.gnu.org/licenses/>. 17 along with LilyPond. If not, see <http://www.gnu.org/licenses/>.
18 */ 18 */
19 19
20 #include "skyline.hh" 20 #include "skyline.hh"
21 #include "skyline-pair.hh"
21 #include <deque> 22 #include <deque>
22 #include <cstdio> 23 #include <cstdio>
23 24
24 #include "ly-smobs.icc" 25 #include "ly-smobs.icc"
25 26
26 /* A skyline is a sequence of non-overlapping buildings: something like 27 /* A skyline is a sequence of non-overlapping buildings: something like
27 this: 28 this:
28 _______ 29 _______
29 | \ ________ 30 | \ ________
30 | \ ________/ \ 31 | \ ________/ \
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
80 for (vsize i = 0; i < ps.size (); i++) 81 for (vsize i = 0; i < ps.size (); i++)
81 printf ("(%f,%f)%s", ps[i][X_AXIS], ps[i][Y_AXIS], 82 printf ("(%f,%f)%s", ps[i][X_AXIS], ps[i][Y_AXIS],
82 (i % 2) == 1 ? "\n" : " "); 83 (i % 2) == 1 ? "\n" : " ");
83 } 84 }
84 85
85 Building::Building (Real start, Real start_height, Real end_height, Real end) 86 Building::Building (Real start, Real start_height, Real end_height, Real end)
86 { 87 {
87 if (isinf (start) || isinf (end)) 88 if (isinf (start) || isinf (end))
88 assert (start_height == end_height); 89 assert (start_height == end_height);
89 90
91 start_ = start;
90 end_ = end; 92 end_ = end;
91 precompute (start, start_height, end_height, end); 93 precompute (start, start_height, end_height, end);
92 } 94 }
93 95
94 Building::Building (Box const &b, Real horizon_padding, Axis horizon_axis, Direc tion sky) 96 Building::Building (Box const &b, Axis horizon_axis, Direction sky)
95 { 97 {
96 Real start = b[horizon_axis][LEFT] - horizon_padding; 98 Real start = b[horizon_axis][LEFT];
97 Real end = b[horizon_axis][RIGHT] + horizon_padding; 99 Real end = b[horizon_axis][RIGHT];
98 Real height = sky * b[other_axis (horizon_axis)][sky]; 100 Real height = sky * b[other_axis (horizon_axis)][sky];
99 101
102 start_ = start;
100 end_ = end; 103 end_ = end;
101 precompute (start, height, height, end); 104 precompute (start, height, height, end);
102 } 105 }
103 106
104 void 107 void
105 Building::precompute (Real start, Real start_height, Real end_height, Real end) 108 Building::precompute (Real start, Real start_height, Real end_height, Real end)
106 { 109 {
107 slope_ = (end_height - start_height) / (end - start); 110 slope_ = 0.0; /* if they were both infinite, we would get nan, not 0, from the prev line */
108 if (start_height == end_height) /* if they were both infinite, we would get na n, not 0, from the prev line */ 111 if (start_height != end_height)
109 slope_ = 0; 112 slope_ = (end_height - start_height) / (end - start);
110 113
111 assert (!isinf (slope_) && !isnan (slope_)); 114 assert (!isinf (slope_) && !isnan (slope_));
112 115
113 if (isinf (start)) 116 if (isinf (start))
114 { 117 {
115 assert (start_height == end_height); 118 assert (start_height == end_height);
116 y_intercept_ = start_height; 119 y_intercept_ = start_height;
117 } 120 }
118 else 121 else
119 y_intercept_ = start_height - slope_ * start; 122 y_intercept_ = start_height - slope_ * start;
120 } 123 }
121 124
122 Real 125 inline Real
123 Building::height (Real x) const 126 Building::height (Real x) const
124 { 127 {
125 return isinf (x) ? y_intercept_ : slope_ * x + y_intercept_; 128 return isinf (x) ? y_intercept_ : slope_ * x + y_intercept_;
126 } 129 }
127 130
128 void 131 void
129 Building::print () const 132 Building::print () const
130 { 133 {
131 printf ("%f x + %f ends at %f\n", slope_, y_intercept_, end_); 134 printf ("%f x + %f from %f to %f\n", slope_, y_intercept_, start_, end_);
132 } 135 }
133 136
134 Real 137 inline Real
135 Building::intersection_x (Building const &other) const 138 Building::intersection_x (Building const &other) const
136 { 139 {
137 Real ret = (y_intercept_ - other.y_intercept_) / (other.slope_ - slope_); 140 Real ret = (y_intercept_ - other.y_intercept_) / (other.slope_ - slope_);
138 return isnan (ret) ? -infinity_f : ret; 141 return isnan (ret) ? -infinity_f : ret;
139 } 142 }
140 143
141 void 144 void
142 Building::leading_part (Real chop) 145 Building::leading_part (Real chop)
143 { 146 {
144 assert (chop <= end_); 147 assert (chop <= end_);
145 end_ = chop; 148 end_ = chop;
146 } 149 }
147 150
148 Building 151 // Returns a shift s such that (x + s, y) intersects the roof of
149 Building::sloped_neighbour (Real start, Real horizon_padding, Direction d) const 152 // this building. If no such shift exists, returns infinity_f.
153 Real
154 Building::shift_to_intersect (Real x, Real y) const
150 { 155 {
151 Real x = (d == LEFT) ? start : end_; 156 // Solve for s: y = (x + s)*m + b
152 Real left = x; 157 Real ret = (y - y_intercept_ - slope_ * x) / slope_;
153 Real right = x + d * horizon_padding; 158
154 Real left_height = height (x); 159 if (ret >= start_ && ret <= end_ && !isinf (ret))
155 Real right_height = left_height - horizon_padding; 160 return ret;
156 if (d == LEFT) 161 return infinity_f;
157 { 162 }
158 swap (left, right); 163
159 swap (left_height, right_height); 164 // Returns the interval of horizontal shifts for which this
160 } 165 // building (pointing up) overlaps the other building (pointing down).
161 return Building (left, left_height, right_height, right); 166 Interval
167 Building::overlapping_shift_interval (Building const& other) const
168 {
169 Interval iv;
170
171 // If one building is empty, there will never be an overlap.
172 if (y_intercept_ == -infinity_f || other.y_intercept_ == -infinity_f)
173 return iv;
174
175 // There are two kinds of interesting positions:
176 // - when the horizontal extents of the buildings just touch
177 // - when an endpoint of one building intersects the roof of the other.
178 // The interval we are looking for is the smallest one that
179 // contains all of the interesting points.
180
181
182 Real my_y1 = height (start_);
183 Real my_y2 = height (end_);
184 Real his_y1 = -other.height (other.start_); // "-" because OTHER points down
185 Real his_y2 = -other.height (other.end_);
186
187 // If both buildings are infinite in the same direction,
188 // the return value is either empty or full.
189 if ((isinf (start_) && isinf (other.start_))
190 || (isinf (end_) && isinf (other.end_)))
191 return (y_intercept_ > other.y_intercept_)
192 ? Interval (-infinity_f, infinity_f) : Interval ();
193
194 // ...when the horizontal extents of the buildings just touch...
195 if (my_y1 >= his_y2)
196 iv.add_point (other.end_ - start_);
197 if (my_y2 >= his_y1)
198 iv.add_point (other.start_ - end_);
199
200 // ...when an endpoint of one building intersects the roof of the other.
201 Real p1 = shift_to_intersect (other.start_, his_y1);
202 Real p2 = shift_to_intersect (other.end_, his_y2);
203 // "-my_y1" because OTHER points down:
204 Real p3 = other.shift_to_intersect (start_, -my_y1);
205 Real p4 = other.shift_to_intersect (end_, -my_y2);
206 if (!isinf (p1))
207 iv.add_point (p1);
208 if (!isinf (p2))
209 iv.add_point (p2);
210 if (!isinf (p3))
211 iv.add_point (p3);
212 if (!isinf (p4))
213 iv.add_point (p4);
214
215 return iv;
162 } 216 }
163 217
164 static Real 218 static Real
165 first_intersection (Building const &b, list<Building> *const s, Real start_x) 219 first_intersection (Building const &b, list<Building> *const s, Real start_x)
166 { 220 {
167 while (!s->empty () && start_x < b.end_) 221 while (!s->empty () && start_x < b.end_)
168 { 222 {
169 Building c = s->front (); 223 Building c = s->front ();
224
225 // conceals and intersection_x involve multiplication and
226 // division. Avoid that, if we can.
227 if (c.y_intercept_ == -infinity_f)
228 {
229 if (c.end_ > b.end_)
230 return b.end_;
231 start_x = c.end_;
232 s->pop_front ();
233 continue;
234 }
235
170 if (c.conceals (b, start_x)) 236 if (c.conceals (b, start_x))
171 return start_x; 237 return start_x;
172 238
173 Real i = b.intersection_x (c); 239 Real i = b.intersection_x (c);
174 if (i > start_x && i <= b.end_ && i <= c.end_) 240 if (i > start_x && i <= b.end_ && i <= c.end_)
175 return i; 241 return i;
176 242
177 start_x = c.end_; 243 start_x = c.end_;
178 if (b.end_ > c.end_) 244 if (b.end_ > c.end_)
179 s->pop_front (); 245 s->pop_front ();
180 } 246 }
181 return b.end_; 247 return b.end_;
182 } 248 }
183 249
184 bool 250 bool
185 Building::conceals (Building const &other, Real x) const 251 Building::conceals (Building const &other, Real x) const
186 { 252 {
187 if (slope_ == other.slope_) 253 if (slope_ == other.slope_)
188 return y_intercept_ > other.y_intercept_; 254 return y_intercept_ > other.y_intercept_;
189 255
190 /* their slopes were not equal, so there is an intersection point */ 256 /* their slopes were not equal, so there is an intersection point */
191 Real i = intersection_x (other); 257 Real i = intersection_x (other);
192 return (i <= x && slope_ > other.slope_) 258 return (i <= x && slope_ > other.slope_)
193 || (i > x && slope_ < other.slope_); 259 || (i > x && slope_ < other.slope_);
194 } 260 }
195 261
196 void 262 void
263 Skyline::deholify ()
264 {
265 bool has_anchor = false;
266 for (list<Building>::iterator i (buildings_.begin ());
267 i != buildings_.end (); i++)
268 if (!isinf (i->y_intercept_))
269 {
270 has_anchor = true;
271 break;
272 }
273
274 if (!has_anchor)
275 return;
276
277 bool dirty = false;
278
279 do
280 {
281 bool dirty = false;··
dak 2012/07/09 08:42:16 Trailing whitespace at end of line, but actually t
282 bool do_correction = false;
283 list<Building>::iterator center;
284 list<Building>::iterator left;
285
286 for (list<Building>::iterator i (buildings_.begin ());
287 i != buildings_.end (); i++)
288 {
289 if (do_correction)
290 {
291 dirty = true;
292 Real p1 = left->end_ * left->slope_ + left->y_intercept_;
293 Real p2 = i->start_ * i->slope_ + i->y_intercept_;
294 center->slope_ = (p2 - p1) / (center->end_ - center->start_);
295 center->y_intercept_ = p2 - (center->end_ * center->slope_);
296 }
297 do_correction = isinf (i->y_intercept_) && !(i == buildings_.begin ()) ;
298 left = center;
299 center = i;
300 }
301 }
302 while (dirty);
303 }
304
305 void
197 Skyline::internal_merge_skyline (list<Building> *s1, list<Building> *s2, 306 Skyline::internal_merge_skyline (list<Building> *s1, list<Building> *s2,
198 list<Building> *const result) 307 list<Building> *const result) const
199 { 308 {
200 if (s1->empty () || s2->empty ()) 309 if (s1->empty () || s2->empty ())
201 { 310 {
202 programming_error ("tried to merge an empty skyline"); 311 programming_error ("tried to merge an empty skyline");
203 return; 312 return;
204 } 313 }
205 314
206 Real x = -infinity_f; 315 Real x = -infinity_f;
207 while (!s1->empty ()) 316 while (!s1->empty ())
208 { 317 {
209 if (s2->front ().conceals (s1->front (), x)) 318 if (s2->front ().conceals (s1->front (), x))
210 swap (s1, s2); 319 swap (s1, s2);
211 320
212 Building b = s1->front (); 321 Building b = s1->front ();
322 Building c = s2->front ();
323
324 // Optimization: if the other skyline is empty at this point,
325 // we can avoid testing some intersections. Just grab as many
326 // buildings from s1 as we can, and shove them onto the output.
327 if (c.y_intercept_ == -infinity_f
328 && c.end_ >= b.end_)
329 {
330 list<Building>::iterator i = s1->begin ();
331 i++;
332 while (i != s1->end () && i->end_ <= c.end_)
333 i++;
334
335 s1->front ().start_ = x;
336 result->splice (result->end (), *s1, s1->begin (), i);
337 x = result->back ().end_;
338 continue;
339 }
340
213 Real end = first_intersection (b, s2, x); 341 Real end = first_intersection (b, s2, x);
214
215 if (s2->empty ()) 342 if (s2->empty ())
216 { 343 {
217 result->push_front (b); 344 b.start_ = result->back ().end_;
345 result->push_back (b);
218 break; 346 break;
219 } 347 }
220 348
221 /* only include buildings wider than epsilon */ 349 /* only include buildings wider than epsilon */
222 if (end > x + EPS) 350 if (end > x + EPS)
223 { 351 {
224 b.leading_part (end); 352 b.leading_part (end);
225 result->push_front (b); 353 b.start_ = result->back ().end_;
354 result->push_back (b);
226 } 355 }
227 356
228 if (end >= s1->front ().end_) 357 if (end >= s1->front ().end_)
229 s1->pop_front (); 358 s1->pop_front ();
230 359
231 x = end; 360 x = end;
232 } 361 }
233 result->reverse ();
234 } 362 }
235 363
236 static void 364 static void
237 empty_skyline (list<Building> *const ret) 365 empty_skyline (list<Building> *const ret)
238 { 366 {
239 ret->push_front (Building (-infinity_f, -infinity_f, -infinity_f, infinity_f)) ; 367 ret->push_front (Building (-infinity_f, -infinity_f, -infinity_f, infinity_f)) ;
240 } 368 }
241 369
242 /* 370 /*
243 Given Building 'b' with starting wall location 'start', extend each side 371 Given Building 'b', build a skyline containing only that building.
244 with a sloped roofline of width 'horizon_padding'; put the skyline in 'ret'
245 */ 372 */
246 static void 373 static void
247 single_skyline (Building b, Real start, Real horizon_padding, list<Building> *co nst ret) 374 single_skyline (Building b, list<Building> *const ret)
248 { 375 {
249 bool sloped_neighbours = horizon_padding > 0 && !isinf (start) && !isinf (b.en d_); 376 if (b.end_ > b.start_ + EPS)
250 if (!isinf (b.end_)) 377 {
251 ret->push_front (Building (b.end_ + horizon_padding, -infinity_f, 378 ret->push_back (Building (-infinity_f, -infinity_f,
252 -infinity_f, infinity_f)); 379 -infinity_f, b.start_));
253 if (sloped_neighbours) 380 ret->push_back (b);
254 ret->push_front (b.sloped_neighbour (start, horizon_padding, RIGHT)); 381 ret->push_back (Building (b.end_, -infinity_f,
255 382 -infinity_f, infinity_f));
256 if (b.end_ > start + EPS) 383 }
257 ret->push_front (b); 384 else
258 385 {
259 if (sloped_neighbours) 386 empty_skyline (ret);
260 ret->push_front (b.sloped_neighbour (start, horizon_padding, LEFT)); 387 }
261
262 if (!isinf (start))
263 ret->push_front (Building (-infinity_f, -infinity_f,
264 -infinity_f, start - horizon_padding));
265 } 388 }
266 389
267 /* remove a non-overlapping set of boxes from BOXES and build a skyline 390 /* remove a non-overlapping set of boxes from BOXES and build a skyline
268 out of them */ 391 out of them */
269 static list<Building> 392 static list<Building>
270 non_overlapping_skyline (list<Box> *const boxes, Real horizon_padding, Axis hori zon_axis, Direction sky) 393 non_overlapping_skyline (list<Building> *const buildings)
271 { 394 {
272 list<Building> result; 395 list<Building> result;
273 Real last_end = -infinity_f; 396 Real last_end = -infinity_f;
274 list<Box>::iterator i = boxes->begin (); 397 Building last_building (-infinity_f, -infinity_f, -infinity_f, infinity_f);
275 while (i != boxes->end ()) 398 list<Building>::iterator i = buildings->begin ();
399 while (i != buildings->end ())
276 { 400 {
277 Interval iv = (*i)[horizon_axis]; 401 Real x1 = i->start_;
402 Real y1 = i->height (i->start_);
403 Real x2 = i->end_;
404 Real y2 = i->height (i->end_);
278 405
279 if (iv[LEFT] - horizon_padding < last_end) 406 // Drop buildings that will obviously have no effect.
407 if (last_building.height (x1) >= y1
408 && last_building.end_ >= x2
409 && last_building.height (x2) >= y2)
410 {
411 list<Building>::iterator j = i++;
412 buildings->erase (j);
413 continue;
414 }
415
416 if (x1 < last_end)
280 { 417 {
281 i++; 418 i++;
282 continue; 419 continue;
283 } 420 }
284 421
285 if (iv[LEFT] - horizon_padding > last_end + EPS) 422 if (x1 > last_end + EPS)
286 result.push_front (Building (last_end, -infinity_f, -infinity_f, iv[LEFT ] - 2 * horizon_padding)); 423 result.push_back (Building (last_end, -infinity_f, -infinity_f, x1));
287 424
288 Building b (*i, horizon_padding, horizon_axis, sky); 425 result.push_back (*i);
289 bool sloped_neighbours = horizon_padding > 0 && !isinf (iv.length ()); 426 last_building = *i;
290 if (sloped_neighbours) 427 last_end = i->end_;
291 result.push_front (b.sloped_neighbour (iv[LEFT] - horizon_padding, horiz on_padding, LEFT));
292 result.push_front (b);
293 if (sloped_neighbours)
294 result.push_front (b.sloped_neighbour (iv[LEFT] - horizon_padding, horiz on_padding, RIGHT));
295 428
296 list<Box>::iterator j = i++; 429 list<Building>::iterator j = i++;
297 boxes->erase (j); 430 buildings->erase (j);
298 last_end = result.front ().end_;
299 } 431 }
432
300 if (last_end < infinity_f) 433 if (last_end < infinity_f)
301 result.push_front (Building (last_end, -infinity_f, -infinity_f, infinity_f) ); 434 result.push_back (Building (last_end, -infinity_f, -infinity_f, infinity_f)) ;
302 result.reverse ();
303 return result; 435 return result;
304 } 436 }
305 437
306 class LessThanBox 438 class LessThanBuilding
307 { 439 {
308 Axis a_;
309
310 public: 440 public:
311 LessThanBox (Axis a) 441 bool operator () (Building const &b1, Building const &b2)
312 { 442 {
313 a_ = a; 443 return b1.start_ < b2.start_
314 } 444 || (b1.start_ == b2.start_ && b1.height (b1.start_) > b2.height (b1.start_ ));
315
316 bool operator () (Box const &b1, Box const &b2)
317 {
318 return b1[a_][LEFT] < b2[a_][LEFT];
319 } 445 }
320 }; 446 };
321 447
448 /**
449 BUILDINGS is a list of buildings, but they could be overlapping
450 and in any order. The returned list of buildings is ordered and non-overlapp ing.
451 */
322 list<Building> 452 list<Building>
323 Skyline::internal_build_skyline (list<Box> *boxes, Real horizon_padding, Axis ho rizon_axis, Direction sky) 453 Skyline::internal_build_skyline (list<Building> *buildings) const
324 { 454 {
325 vsize size = boxes->size (); 455 vsize size = buildings->size ();
326 456
327 if (size == 0) 457 if (size == 0)
328 { 458 {
329 list<Building> result; 459 list<Building> result;
330 empty_skyline (&result); 460 empty_skyline (&result);
331 return result; 461 return result;
332 } 462 }
333 else if (size == 1) 463 else if (size == 1)
334 { 464 {
335 list<Building> result; 465 list<Building> result;
336 single_skyline (Building (boxes->front (), horizon_padding, horizon_axis, sky), 466 single_skyline (buildings->front (), &result);
337 boxes->front ()[horizon_axis][LEFT] - horizon_padding,
338 horizon_padding, &result);
339 return result; 467 return result;
340 } 468 }
341 469
342 deque<list<Building> > partials; 470 deque<list<Building> > partials;
343 boxes->sort (LessThanBox (horizon_axis)); 471 buildings->sort (LessThanBuilding ());
344 while (!boxes->empty ()) 472 while (!buildings->empty ())
345 partials.push_back (non_overlapping_skyline (boxes, horizon_padding, horizon _axis, sky)); 473 partials.push_back (non_overlapping_skyline (buildings));
346 474
347 /* we'd like to say while (partials->size () > 1) but that's O (n). 475 /* we'd like to say while (partials->size () > 1) but that's O (n).
348 Instead, we exit in the middle of the loop */ 476 Instead, we exit in the middle of the loop */
349 while (!partials.empty ()) 477 while (!partials.empty ())
350 { 478 {
351 list<Building> merged; 479 list<Building> merged;
352 list<Building> one = partials.front (); 480 list<Building> one = partials.front ();
353 partials.pop_front (); 481 partials.pop_front ();
354 if (partials.empty ()) 482 if (partials.empty ())
355 return one; 483 return one;
(...skipping 25 matching lines...) Expand all
381 } 509 }
382 } 510 }
383 511
384 Skyline::Skyline (Direction sky) 512 Skyline::Skyline (Direction sky)
385 { 513 {
386 sky_ = sky; 514 sky_ = sky;
387 empty_skyline (&buildings_); 515 empty_skyline (&buildings_);
388 } 516 }
389 517
390 /* 518 /*
391 build padded skyline from an existing skyline with padding 519 Build skyline from a set of boxes.
392 added to it.
393 */
394 520
395 Skyline::Skyline (Skyline const &src, Real horizon_padding, Axis /*a*/) 521 Boxes should have fatness in the horizon_axis, otherwise they are ignored.
522 */
523 Skyline::Skyline (vector<Box> const &boxes, Axis horizon_axis, Direction sky)
396 { 524 {
397 /* 525 list<Building> buildings;
398 We extract boxes from the skyline, then build a new skyline from
399 the boxes.
400 A box is created for every horizontal portion of the skyline
401 Because skylines are defined positive, and then inverted if they
402 are to be down-facing, we create the new skyline in the UP
403 direction, then give it the down direction if needed.
404 */
405 Real start = -infinity_f;
406 list<Box> boxes;
407
408 // establish a baseline box
409 // FIXME: This has hardcoded logic, assuming a == X_AXIS!
410 boxes.push_back (Box (Interval (-infinity_f, infinity_f),
411 Interval (0, 0)));
412 list<Building>::const_iterator end = src.buildings_.end ();
413 for (list<Building>::const_iterator i = src.buildings_.begin (); i != end; sta rt = i->end_, i++)
414 if ((i->slope_ == 0) && !isinf (i->y_intercept_))
415 boxes.push_back (Box (Interval (start, i->end_),
416 Interval (-infinity_f, i->y_intercept_)));
417 buildings_ = internal_build_skyline (&boxes, horizon_padding, X_AXIS, UP);
418 sky_ = src.sky_;
419 }
420
421 /*
422 build skyline from a set of boxes. If horizon_padding > 0, expand all the boxe s
423 by that amount and add 45-degree sloped boxes to the edges of each box (of
424 width horizon_padding). That is, the total amount of horizontal expansion is
425 horizon_padding*4, half of which is sloped and half of which is flat.
426
427 Boxes should have fatness in the horizon_axis (after they are expanded by
428 horizon_padding), otherwise they are ignored.
429 */
430 Skyline::Skyline (vector<Box> const &boxes, Real horizon_padding, Axis horizon_a xis, Direction sky)
431 {
432 list<Box> filtered_boxes;
433 sky_ = sky; 526 sky_ = sky;
434 527
435 Axis vert_axis = other_axis (horizon_axis); 528 Axis vert_axis = other_axis (horizon_axis);
436 for (vsize i = 0; i < boxes.size (); i++) 529 for (vsize i = 0; i < boxes.size (); i++)
437 { 530 {
438 Interval iv = boxes[i][horizon_axis]; 531 Interval iv = boxes[i][horizon_axis];
439 iv.widen (horizon_padding);
440 if (iv.length () > EPS && !boxes[i][vert_axis].is_empty ()) 532 if (iv.length () > EPS && !boxes[i][vert_axis].is_empty ())
441 filtered_boxes.push_front (boxes[i]); 533 buildings.push_front (Building (boxes[i], horizon_axis, sky));
442 } 534 }
443 535
444 buildings_ = internal_build_skyline (&filtered_boxes, horizon_padding, horizon _axis, sky); 536 buildings_ = internal_build_skyline (&buildings);
445 } 537 }
446 538
447 Skyline::Skyline (Box const &b, Real horizon_padding, Axis horizon_axis, Directi on sky) 539 /*
540 build skyline from a set of line segments.
541
542 Buildings should have fatness in the horizon_axis, otherwise they are ignored.
543 */
544 Skyline::Skyline (vector<Drul_array<Offset> > const &segments, Axis horizon_axis , Direction sky)
545 {
546 list<Building> buildings;
547 sky_ = sky;
548
549 for (vsize i = 0; i < segments.size (); i++)
550 {
551 Drul_array<Offset> const& seg = segments[i];
552 Offset left = seg[LEFT];
553 Offset right = seg[RIGHT];
554 if (left[horizon_axis] > right[horizon_axis])
555 swap (left, right);
556
557 Real x1 = left[horizon_axis];
558 Real x2 = right[horizon_axis];
559 Real y1 = left[other_axis (horizon_axis)] * sky;
560 Real y2 = right[other_axis (horizon_axis)] * sky;
561
562 if (x1 + EPS < x2)
563 buildings.push_back (Building (x1, y1, y2, x2));
564 }
565
566 buildings_ = internal_build_skyline (&buildings);
567 }
568
569 Skyline::Skyline (vector<Skyline_pair> const &skypairs, Direction sky)
448 { 570 {
449 sky_ = sky; 571 sky_ = sky;
450 Building front (b, horizon_padding, horizon_axis, sky); 572
451 single_skyline (front, b[horizon_axis][LEFT] - horizon_padding, 573 deque<Skyline> partials;
452 horizon_padding, &buildings_); 574 for (vsize i = 0; i < skypairs.size (); i++)
575 partials.push_back (Skyline ((skypairs[i])[sky]));
576
577 while (partials.size () > 1)
578 {
579 Skyline one = partials.front ();
580 partials.pop_front ();
581 Skyline two = partials.front ();
582 partials.pop_front ();
583 ·
dak 2012/07/09 08:42:16 Whitespace in empty line.
584 one.merge (two);
585 partials.push_back (one);
586 }
587
588 if (partials.size ())
589 buildings_.swap (partials.front ().buildings_);
590 else
591 buildings_.clear ();
592 }
593
594 Skyline::Skyline (Box const &b, Axis horizon_axis, Direction sky)
595 {
596 sky_ = sky;
597 Building front (b, horizon_axis, sky);
598 single_skyline (front, &buildings_);
453 } 599 }
454 600
455 void 601 void
456 Skyline::merge (Skyline const &other) 602 Skyline::merge (Skyline const &other)
457 { 603 {
458 assert (sky_ == other.sky_); 604 assert (sky_ == other.sky_);
459 605
606 if (other.is_empty ())
607 return;
608
609 if (is_empty ())
610 {
611 buildings_ = other.buildings_;
612 return;
613 }
614
460 list<Building> other_bld (other.buildings_); 615 list<Building> other_bld (other.buildings_);
461 list<Building> my_bld; 616 list<Building> my_bld;
462 my_bld.splice (my_bld.begin (), buildings_); 617 my_bld.splice (my_bld.begin (), buildings_);
463 internal_merge_skyline (&other_bld, &my_bld, &buildings_); 618 internal_merge_skyline (&other_bld, &my_bld, &buildings_);
464 } 619 }
465 620
466 void 621 void
467 Skyline::insert (Box const &b, Real horizon_padding, Axis a) 622 Skyline::insert (Box const &b, Axis a)
468 { 623 {
469 list<Building> other_bld; 624 list<Building> other_bld;
470 list<Building> my_bld; 625 list<Building> my_bld;
471 626
472 if (isnan (b[other_axis (a)][LEFT]) 627 if (isnan (b[other_axis (a)][LEFT])
473 || isnan (b[other_axis (a)][RIGHT])) 628 || isnan (b[other_axis (a)][RIGHT]))
474 { 629 {
475 programming_error ("insane box for skyline"); 630 programming_error ("insane box for skyline");
476 return; 631 return;
477 } 632 }
478 633
479 /* do the same filtering as in Skyline (vector<Box> const&, etc.) */ 634 /* do the same filtering as in Skyline (vector<Box> const&, etc.) */
480 Interval iv = b[a]; 635 Interval iv = b[a];
481 iv.widen (horizon_padding);
482 if (iv.length () <= EPS || b[other_axis (a)].is_empty ()) 636 if (iv.length () <= EPS || b[other_axis (a)].is_empty ())
483 return; 637 return;
484 638
485 my_bld.splice (my_bld.begin (), buildings_); 639 my_bld.splice (my_bld.begin (), buildings_);
486 single_skyline (Building (b, horizon_padding, a, sky_), b[a][LEFT] - horizon_p adding, 640 single_skyline (Building (b, a, sky_), &other_bld);
487 horizon_padding, &other_bld);
488 internal_merge_skyline (&other_bld, &my_bld, &buildings_); 641 internal_merge_skyline (&other_bld, &my_bld, &buildings_);
489 } 642 }
490 643
491 void 644 void
492 Skyline::raise (Real r) 645 Skyline::raise (Real r)
493 { 646 {
494 list<Building>::iterator end = buildings_.end (); 647 list<Building>::iterator end = buildings_.end ();
495 for (list<Building>::iterator i = buildings_.begin (); i != end; i++) 648 for (list<Building>::iterator i = buildings_.begin (); i != end; i++)
496 i->y_intercept_ += sky_ * r; 649 i->y_intercept_ += sky_ * r;
497 } 650 }
498 651
499 void 652 void
500 Skyline::shift (Real s) 653 Skyline::shift (Real s)
501 { 654 {
502 list<Building>::iterator end = buildings_.end (); 655 list<Building>::iterator end = buildings_.end ();
503 for (list<Building>::iterator i = buildings_.begin (); i != end; i++) 656 for (list<Building>::iterator i = buildings_.begin (); i != end; i++)
504 { 657 {
658 i->start_ += s;
505 i->end_ += s; 659 i->end_ += s;
506 i->y_intercept_ -= s * i->slope_; 660 i->y_intercept_ -= s * i->slope_;
507 } 661 }
508 } 662 }
509 663
510 Real 664 Real
511 Skyline::distance (Skyline const &other, Real horizon_padding) const 665 Skyline::distance (Skyline const &other, Real horizon_padding) const
512 { 666 {
513 Real dummy; 667 Real dummy;
514 return internal_distance (other, horizon_padding, &dummy); 668 return internal_distance (other, horizon_padding, &dummy);
515 } 669 }
516 670
517 Real 671 Real
518 Skyline::touching_point (Skyline const &other, Real horizon_padding) const 672 Skyline::touching_point (Skyline const &other, Real horizon_padding) const
519 { 673 {
520 Real touch; 674 Real touch;
521 internal_distance (other, horizon_padding, &touch); 675 internal_distance (other, horizon_padding, &touch);
522 return touch; 676 return touch;
523 } 677 }
524 678
525 Real 679 Real
526 Skyline::internal_distance (Skyline const &other, Real horizon_padding, Real *to uch_point) const 680 Skyline::internal_distance (Skyline const &other, Real horizon_padding, Real *to uch_point) const
527 { 681 {
682 if (horizon_padding == 0.0)
683 return internal_distance (other, touch_point);
684
685 // Note that it is not necessary to build a padded version of other,
686 // because the same effect can be achieved just by doubling horizon_padding.
687 Skyline padded_this = padded (horizon_padding);
688 return padded_this.internal_distance (other, touch_point);
689 }
690
691
692 Skyline
693 Skyline::padded (Real horizon_padding) const
694 {
695 list<Building> pad_buildings;
696 for (list<Building>::const_iterator i = buildings_.begin (); i != buildings_.e nd (); ++i)
697 {
698 if (i->start_ > -infinity_f)
699 {
700 Real height = i->height (i->start_);
701 if (height > -infinity_f)
702 {
703 // Add the sloped building that pads the left side of the current building.
704 Real start = i->start_ - 2*horizon_padding;
705 Real end = i->start_ - horizon_padding;
706 pad_buildings.push_back (Building (start, height - horizon_padding , height, end));
707
708 // Add the flat building that pads the left side of the current bu ilding.
709 start = i->start_ - horizon_padding;
710 end = i->start_;···
dak 2012/07/09 08:42:16 Trailing whitespace.
711 pad_buildings.push_back (Building (start, height, height, end));··················
dak 2012/07/09 08:42:16 Trailing whitespace.
712 }
713 }
714
715 if (i->end_ < infinity_f)
716 {
717 Real height = i->height (i->end_);
718 if (height > -infinity_f)
719 {
720 // Add the flat building that pads the right side of the current b uilding.
721 Real start = i->end_;
722 Real end = start + horizon_padding;
723 pad_buildings.push_back (Building (start, height, height, end));
724
725 // Add the sloped building that pads the right side of the current building.
726 start = end;
727 end += horizon_padding;
728 pad_buildings.push_back (Building (start, height, height - horizon _padding, end));············
dak 2012/07/09 08:42:16 Trailing whitespace.
729 }
730 }
731 }
732
733 // The buildings may be overlapping, so resolve that.
734 list<Building> pad_skyline = internal_build_skyline (&pad_buildings);
735
736 // Merge the padding with the original, to make a new skyline.
737 Skyline padded (sky_);
738 list<Building> my_buildings = buildings_;
739 padded.buildings_.clear ();
740 internal_merge_skyline (&pad_skyline, &my_buildings, &padded.buildings_);
741
742 return padded;
743 }
744
745 Real
746 Skyline::internal_distance (Skyline const &other, Real *touch_point) const
747 {
528 assert (sky_ == -other.sky_); 748 assert (sky_ == -other.sky_);
529 749
530 Skyline const *padded_this = this;
531 Skyline const *padded_other = &other;
532 bool created_tmp_skylines = false;
533
534 /* 750 /*
535 For systems, padding is not added at creation time. Padding is 751 For systems, padding is not added at creation time. Padding is
536 added to AxisGroup objects when outside-staff objects are added. 752 added to AxisGroup objects when outside-staff objects are added.
537 Thus, when we want to place systems with horizontal padding, 753 Thus, when we want to place systems with horizontal padding,
538 we do it at distance calculation time. 754 we do it at distance calculation time.
539 */ 755 */
540 if (horizon_padding != 0.0)
541 {
542 padded_this = new Skyline (*padded_this, horizon_padding, X_AXIS);
543 padded_other = new Skyline (*padded_other, horizon_padding, X_AXIS);
544 created_tmp_skylines = true;
545 }
546 756
547 list<Building>::const_iterator i = padded_this->buildings_.begin (); 757 list<Building>::const_iterator i = buildings_.begin ();
548 list<Building>::const_iterator j = padded_other->buildings_.begin (); 758 list<Building>::const_iterator j = other.buildings_.begin ();
549 759
550 Real dist = -infinity_f; 760 Real dist = -infinity_f;
551 Real start = -infinity_f; 761 Real start = -infinity_f;
552 Real touch = -infinity_f; 762 Real touch = -infinity_f;
553 while (i != padded_this->buildings_.end () && j != padded_other->buildings_.en d ()) 763 while (i != buildings_.end () && j != other.buildings_.end ())
554 { 764 {
555 Real end = min (i->end_, j->end_); 765 Real end = min (i->end_, j->end_);
556 Real start_dist = i->height (start) + j->height (start); 766 Real start_dist = i->height (start) + j->height (start);
557 Real end_dist = i->height (end) + j->height (end); 767 Real end_dist = i->height (end) + j->height (end);
558 dist = max (dist, max (start_dist, end_dist)); 768 dist = max (dist, max (start_dist, end_dist));
559 769
560 if (end_dist == dist) 770 if (end_dist == dist)
561 touch = end; 771 touch = end;
562 else if (start_dist == dist) 772 else if (start_dist == dist)
563 touch = start; 773 touch = start;
564 774
565 if (i->end_ <= j->end_) 775 if (i->end_ <= j->end_)
566 i++; 776 i++;
567 else 777 else
568 j++; 778 j++;
569 start = end; 779 start = end;
570 } 780 }
571 781
572 if (created_tmp_skylines)
573 {
574 delete padded_this;
575 delete padded_other;
576 }
577
578 *touch_point = touch; 782 *touch_point = touch;
579 return dist; 783 return dist;
580 } 784 }
581 785
786 Skyline_intersection_info
787 Skyline::intersects (Skyline const &other) const
788 {
789 assert (sky_ == -other.sky_);
790
791 /*
792 For systems, padding is not added at creation time. Padding is
793 added to AxisGroup objects when outside-staff objects are added.
794 Thus, when we want to place systems with horizontal padding,
795 we do it at distance calculation time.
796 */
797
798 list<Building>::const_iterator i = buildings_.begin ();
799 list<Building>::const_iterator j = other.buildings_.begin ();
800
801 Real start = -infinity_f;
802 Drul_array<bool> intersections (false, false);
803 while (i != buildings_.end () && j != other.buildings_.end ())
804 {
805 Real end = min (i->end_, j->end_);
806 Real start_dist = i->height (start) + j->height (start);
807 Real end_dist = i->height (end) + j->height (end);
808 if (!isinf (start_dist))
809 {
810 if (start_dist == 0.0)
811 return INTERSECTS;
812 intersections[Direction (sign (start_dist))] = true;
813 }
814 if (!isinf (end_dist))
815 {
816 if (end_dist == 0.0)
817 return INTERSECTS;
818 intersections[Direction (sign (end_dist))] = true;
819 }
820 if (intersections[DOWN] && intersections[UP])
821 return INTERSECTS;
822
823 if (i->end_ <= j->end_)
824 i++;
825 else
826 j++;
827 start = end;
828 }
829
830 if (intersections[DOWN])
831 return ALWAYS_LESS;
832 if (intersections[UP])
833 return ALWAYS_GREATER;
834 return NOT_ENOUGH_INFO;····
dak 2012/07/09 08:42:16 Trailing whitespace.
835 }
836
837 // changes the direction that the skyline is pointing
838 void
839 Skyline::invert ()
840 {
841 list<Building>::iterator i;
842 for (i = buildings_.begin (); i != buildings_.end (); i++)
843 if (!isinf (i->y_intercept_))
844 {
845 i->y_intercept_ *= -1;
846 i->slope_ *= -1;
847 }
848
849 sky_ = -sky_;
850 }
851
582 Real 852 Real
583 Skyline::height (Real airplane) const 853 Skyline::height (Real airplane) const
584 { 854 {
585 assert (!isinf (airplane)); 855 assert (!isinf (airplane));
586 856
587 list<Building>::const_iterator i; 857 list<Building>::const_iterator i;
588 for (i = buildings_.begin (); i != buildings_.end (); i++) 858 for (i = buildings_.begin (); i != buildings_.end (); i++)
589 { 859 {
590 if (i->end_ >= airplane) 860 if (i->end_ >= airplane)
591 return sky_ * i->height (airplane); 861 return sky_ * i->height (airplane);
592 } 862 }
593 863
594 assert (0); 864 assert (0);
595 return 0; 865 return 0;
596 } 866 }
597 867
598 Real 868 Real
599 Skyline::max_height () const 869 Skyline::max_height () const
600 { 870 {
601 Skyline s (-sky_); 871 Real ret = -infinity_f;
602 s.set_minimum_height (0); 872
603 return sky_ * distance (s); 873 list<Building>::const_iterator i;
874 for (i = buildings_.begin (); i != buildings_.end (); ++i)
875 {
876 ret = max (ret, i->height (i->start_));
877 ret = max (ret, i->height (i->end_));
878 }
879
880 return sky_ * ret;
604 } 881 }
605 882
606 Real 883 Real
607 Skyline::max_height_position () const 884 Skyline::max_height_position () const
608 { 885 {
609 Skyline s (-sky_); 886 Skyline s (-sky_);
610 s.set_minimum_height (0); 887 s.set_minimum_height (0);
611 return touching_point (s); 888 return touching_point (s);
612 } 889 }
613 890
(...skipping 19 matching lines...) Expand all
633 start = i->end_; 910 start = i->end_;
634 } 911 }
635 912
636 if (horizon_axis == Y_AXIS) 913 if (horizon_axis == Y_AXIS)
637 for (vsize i = 0; i < out.size (); i++) 914 for (vsize i = 0; i < out.size (); i++)
638 out[i] = out[i].swapped (); 915 out[i] = out[i].swapped ();
639 916
640 return out; 917 return out;
641 } 918 }
642 919
920 // Returns the smallest (non-negative) shift in the given
921 // direction which will result in THIS and OTHER not overlapping.
922 // Warning: this function is O(n^2 log n). Use sparingly.
923 Real
924 Skyline::smallest_shift (Skyline const& other,
925 Direction d,
926 Real horizon_padding,
927 Real vertical_padding) const
928 {
929 // If one or both of the paddings is zero, this can
930 // be optimized...
931 Skyline padded_me = padded (horizon_padding);
932 padded_me.raise (vertical_padding);
933
934 list<Building>::const_iterator i = padded_me.buildings_.begin ();
935 list<Building>::const_iterator j = other.buildings_.begin ();
936 list<Building>::const_iterator i_end = padded_me.buildings_.end ();
937 list<Building>::const_iterator j_end = other.buildings_.end ();
938
939 // Find all shifts that are not allowed.
940 vector<Interval> forbidden_shifts;
941 for (; i != i_end; ++i)
942 if (i->y_intercept_ != -infinity_f)
943 for (j = other.buildings_.begin (); j != j_end; ++j)
944 {
945 Interval iv = i->overlapping_shift_interval (*j);
946 if (!iv.is_empty ())
947 forbidden_shifts.push_back (iv);
948 }
949
950 // Now comes the trick: we want to find the smallest point
951 // that is not in the union of forbidden_shifts. We can represent
952 // the union of forbidden_shifts as a skyline, where a point is
953 // allowed if it has height -infinity_f and forbidden otherwise.
954 vector<Box> boxes;
955 for (vector<Interval>::iterator k = forbidden_shifts.begin ();
956 k != forbidden_shifts.end (); ++k)
957 boxes.push_back (Box (*k, Interval(-1, 0)));
958 Skyline s (boxes, X_AXIS, UP);
959
960 // Find the smallest (ie. closest to zero, in the appropriate direction)
961 // coordinate where the height of s is -infinity_f.
962 Real last_good_point = -infinity_f;
963 for (i = s.buildings_.begin (); i != s.buildings_.end (); ++i)
964 {
965 if (d == LEFT && i->start_ > 0)
966 return last_good_point;
967
968 if (i->y_intercept_ == -infinity_f)
969 {
970 if (i->start_ <= 0 && i->end_ >= 0)
971 return 0;
972 if (d == RIGHT && i->start_ >= 0)
973 return i->start_;
974
975 last_good_point = i->end_;
976 }
977 }
978
979 return infinity_f * d;
980 }
981
982 Real
983 Skyline::left () const
984 {
985 for (list<Building>::const_iterator i (buildings_.begin ());
986 i != buildings_.end (); i++)
987 if (i->y_intercept_ > -infinity_f)
988 return i->start_;
989
990 return infinity_f;
991 }
992
993 Real
994 Skyline::right () const
995 {
996 for (list<Building>::const_reverse_iterator i (buildings_.rbegin ());
997 i != buildings_.rend (); ++i)
998 if (i->y_intercept_ > -infinity_f)
999 return i->end_;
1000
1001 return -infinity_f;
1002 }
1003
643 bool 1004 bool
644 Skyline::is_empty () const 1005 Skyline::is_empty () const
645 { 1006 {
1007 if (!buildings_.size ())
1008 return true;
646 Building b = buildings_.front (); 1009 Building b = buildings_.front ();
647 return b.end_ == infinity_f && b.y_intercept_ == -infinity_f; 1010 return b.end_ == infinity_f && b.y_intercept_ == -infinity_f;
648 } 1011 }
649 1012
1013 bool
1014 Skyline::is_singleton () const
1015 {
1016 return buildings_.size () == 3;
1017 }
1018
650 void 1019 void
651 Skyline::clear () 1020 Skyline::clear ()
652 { 1021 {
653 buildings_.clear (); 1022 buildings_.clear ();
654 empty_skyline (&buildings_); 1023 empty_skyline (&buildings_);
655 } 1024 }
656 1025
1026 string
1027 Skyline::intersection_info_to_string (Skyline_intersection_info sii)
1028 {
1029 if (sii == INTERSECTS)
1030 return "intersection";
1031 if (sii == ALWAYS_GREATER)
1032 return "alwaysGreater";
1033 if (sii == ALWAYS_LESS)
1034 return "alwaysLess";
1035 if (sii == NOT_ENOUGH_INFO)
1036 return "notEnoughInfo";
1037
1038 assert (1 == 0);
1039 return "veryBad";
1040 }
1041
1042 Direction
1043 Skyline::intersection_info_to_direction (Skyline_intersection_info sii)
1044 {
1045 if (sii == INTERSECTS)
1046 return CENTER;
1047 if (sii == ALWAYS_GREATER)
1048 return UP;
1049 if (sii == ALWAYS_LESS)
1050 return DOWN;
1051 if (sii == NOT_ENOUGH_INFO)
1052 return CENTER;
1053
1054 assert (1 == 0);
1055 return CENTER;
1056 }
1057
657 /****************************************************************/ 1058 /****************************************************************/
658 1059
659 IMPLEMENT_SIMPLE_SMOBS (Skyline); 1060 IMPLEMENT_SIMPLE_SMOBS (Skyline);
660 IMPLEMENT_TYPE_P (Skyline, "ly:skyline?"); 1061 IMPLEMENT_TYPE_P (Skyline, "ly:skyline?");
661 IMPLEMENT_DEFAULT_EQUAL_P (Skyline); 1062 IMPLEMENT_DEFAULT_EQUAL_P (Skyline);
662 1063
663 SCM 1064 SCM
664 Skyline::mark_smob (SCM s) 1065 Skyline::mark_smob (SCM s)
665 { 1066 {
666 ASSERT_LIVE_IS_ALLOWED (s); 1067 ASSERT_LIVE_IS_ALLOWED (s);
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
728 return scm_from_double (Skyline::unsmob (skyline_scm)->max_height_position ()) ; 1129 return scm_from_double (Skyline::unsmob (skyline_scm)->max_height_position ()) ;
729 } 1130 }
730 1131
731 MAKE_SCHEME_CALLBACK (Skyline, get_height, 2) 1132 MAKE_SCHEME_CALLBACK (Skyline, get_height, 2)
732 SCM 1133 SCM
733 Skyline::get_height (SCM skyline_scm, SCM x_scm) 1134 Skyline::get_height (SCM skyline_scm, SCM x_scm)
734 { 1135 {
735 Real x = robust_scm2double (x_scm, 0.0); 1136 Real x = robust_scm2double (x_scm, 0.0);
736 return scm_from_double (Skyline::unsmob (skyline_scm)->height (x)); 1137 return scm_from_double (Skyline::unsmob (skyline_scm)->height (x));
737 } 1138 }
OLDNEW

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b