]> git.donarmstrong.com Git - lilypond.git/blob - lily/stencil-integral.cc
Replace C++ (in)equality checks with proper SCM syntax
[lilypond.git] / lily / stencil-integral.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 2012 Mike Solomon <mike@mikesolomon.org>
5
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
8   the Free Software Foundation, either version 3 of the License, or
9   (at your option) any later version.
10
11   LilyPond is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   GNU General Public License for more details.
15
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/>.
18 */
19
20 /*
21 tools for transform-matrices following the standard at
22 http://www.w3.org/TR/SVG/coords.html
23
24 a list in the form
25 (list a b c d e f g)
26 becomes this matrix:
27 [ a c e ]
28 [ b d f ]
29 [ 0 0 1 ]
30 when this transforms a point (x,y), the point is written as matrix:
31 [ x ]
32 [ y ]
33 [ 1 ]
34 */
35
36 #include <pango/pango-matrix.h>
37 #include <complex>
38 #include "box.hh"
39 #include "bezier.hh"
40 #include "dimensions.hh"
41 #include "font-metric.hh"
42 #include "grob.hh"
43 #include "interval.hh"
44 #include "freetype.hh"
45 #include "misc.hh"
46 #include "offset.hh"
47 #include "modified-font-metric.hh"
48 #include "open-type-font.hh"
49 #include "pango-font.hh"
50 #include "pointer-group-interface.hh"
51 #include "lily-guile.hh"
52 #include "real.hh"
53 #include "rest.hh"
54 #include "stencil.hh"
55 #include "string-convert.hh"
56 #include "skyline.hh"
57 #include "skyline-pair.hh"
58 #include "spanner.hh"
59 using namespace std;
60
61 Real QUANTIZATION_UNIT = 0.2;
62
63 void create_path_cap (vector<Box> &boxes, vector<Drul_array<Offset> > &buildings, PangoMatrix trans, Offset pt, Real rad, Real slope, Direction d);
64
65 struct Transform_matrix_and_expression
66 {
67   PangoMatrix tm_;
68   SCM expr_;
69
70   Transform_matrix_and_expression (PangoMatrix tm, SCM expr);
71 };
72
73 Transform_matrix_and_expression::Transform_matrix_and_expression (PangoMatrix tm, SCM expr)
74 {
75   tm_ = tm;
76   expr_ = expr;
77 }
78
79 PangoMatrix
80 make_transform_matrix (Real p0, Real p1, Real p2, Real p3, Real p4, Real p5)
81 {
82   PangoMatrix out;
83   out.xx = p0;
84   out.xy = p1;
85   out.yx = p2;
86   out.yy = p3;
87   out.x0 = p4;
88   out.y0 = p5;
89   return out;
90 }
91
92 //// UTILITY FUNCTIONS
93
94 /*
95   map x's placement between orig_l and orig_r onto
96   the interval final_l final_r
97 */
98 Real
99 linear_map (Real final_l, Real final_r, Real orig_l, Real orig_r, Real x)
100 {
101   return final_l + ((final_r - final_l) * ((x - orig_l) / (orig_r - orig_l)));
102 }
103
104 /*
105   from a nested SCM list, return the first list of numbers
106   useful for polygons
107 */
108 SCM
109 get_number_list (SCM l)
110 {
111   if (scm_is_pair (l))
112     {
113       if (scm_is_number (scm_car (l)))
114         return l;
115       SCM res = get_number_list (scm_car (l));
116       if (scm_is_false (res))
117         return get_number_list (scm_cdr (l));
118       return res;
119     }
120   return SCM_BOOL_F;
121 }
122
123 /*
124   from a nested SCM list, return the first list of numbers
125   useful for paths
126 */
127 SCM
128 get_path_list (SCM l)
129 {
130   if (scm_is_pair (l))
131     {
132       if (scm_is_true (scm_memv (scm_car (l),
133                                  scm_list_n (ly_symbol2scm ("moveto"),
134                                              ly_symbol2scm ("rmoveto"),
135                                              ly_symbol2scm ("lineto"),
136                                              ly_symbol2scm ("rlineto"),
137                                              ly_symbol2scm ("curveto"),
138                                              ly_symbol2scm ("rcurveto"),
139                                              ly_symbol2scm ("closepath"),
140                                              SCM_UNDEFINED))))
141         return l;
142       SCM res = get_path_list (scm_car (l));
143       if (scm_is_false (res))
144         return get_path_list (scm_cdr (l));
145       return res;
146     }
147   return SCM_BOOL_F;
148 }
149
150 Real
151 perpendicular_slope (Real s)
152 {
153   if (s == 0.0)
154     return infinity_f;
155   if (s == infinity_f)
156     return 0.0;
157   return -1.0 / s;
158 }
159
160 //// END UTILITY FUNCTIONS
161
162 /*
163   below, for all of the functions make_X_boxes, the expression
164   is always unpacked into variables.
165   then, after a line of /////, there are manipulations of these variables
166   (there may be no manipulations necessary depending on the function)
167   afterwards, there is another ///// followed by the creation of points
168   and boxes
169 */
170
171 void
172 make_draw_line_boxes (vector<Box> &boxes, vector<Drul_array<Offset> > &buildings, PangoMatrix trans, SCM expr, bool use_building)
173 {
174   Real thick = robust_scm2double (scm_car (expr), 0.0);
175   expr = scm_cdr (expr);
176   Real x0 = robust_scm2double (scm_car (expr), 0.0);
177   expr = scm_cdr (expr);
178   Real y0 = robust_scm2double (scm_car (expr), 0.0);
179   expr = scm_cdr (expr);
180   Real x1 = robust_scm2double (scm_car (expr), 0.0);
181   expr = scm_cdr (expr);
182   Real y1 = robust_scm2double (scm_car (expr), 0.0);
183   Real slope = x1 == x0 ? infinity_f : (y1 - y0) / (x1 - x0);
184   //////////////////////
185   if (x1 < x0)
186     {
187       swap (x0, x1);
188       swap (y0, y1);
189     }
190   Offset left (x0, y0);
191   Offset right (x1, y1);
192   Direction d = DOWN;
193   do
194     {
195       Offset inter_l = get_point_in_y_direction (left, perpendicular_slope (slope), thick / 2, d);
196       Offset inter_r = get_point_in_y_direction (right, perpendicular_slope (slope), thick / 2, d);
197       pango_matrix_transform_point (&trans, &inter_l[X_AXIS], &inter_l[Y_AXIS]);
198       pango_matrix_transform_point (&trans, &inter_r[X_AXIS], &inter_r[Y_AXIS]);
199       if ((inter_l[X_AXIS] == inter_r[X_AXIS]) || (inter_l[Y_AXIS] == inter_r[Y_AXIS]))
200         {
201           Box b;
202           b.add_point (inter_l);
203           b.add_point (inter_r);
204           boxes.push_back (b);
205         }
206       else if (use_building)
207         buildings.push_back (Drul_array<Offset> (inter_l, inter_r));
208       else
209         {
210           Offset inter_l = get_point_in_y_direction (left, perpendicular_slope (slope), thick / 2, d);
211           Offset inter_r = get_point_in_y_direction (right, perpendicular_slope (slope), thick / 2, d);
212           pango_matrix_transform_point (&trans, &inter_l[X_AXIS], &inter_l[Y_AXIS]);
213           pango_matrix_transform_point (&trans, &inter_r[X_AXIS], &inter_r[Y_AXIS]);
214           Real length = sqrt (((inter_l[X_AXIS] - inter_r[X_AXIS]) * (inter_l[X_AXIS] - inter_r[X_AXIS])) + ((inter_l[Y_AXIS] - inter_r[Y_AXIS]) * (inter_l[Y_AXIS] - inter_r[Y_AXIS])));
215
216           vsize passes = (vsize) ((length * 2) + 1);
217           vector<Offset> points;
218
219           for (vsize i = 0; i < 1 + passes; i++)
220             {
221               Offset pt (linear_map (x0, x1, 0, passes, i),
222                          linear_map (y0, y1, 0, passes, i));
223               Offset inter = get_point_in_y_direction (pt, perpendicular_slope (slope), thick / 2, d);
224               pango_matrix_transform_point (&trans, &inter[X_AXIS], &inter[Y_AXIS]);
225               points.push_back (inter);
226             }
227           for (vsize i = 0; i < points.size () - 1; i++)
228             {
229               Box b;
230               b.add_point (points[i]);
231               b.add_point (points[i + 1]);
232               boxes.push_back (b);
233             }
234         }
235     }
236   while (flip (&d) != DOWN);
237
238   if (thick > 0.0)
239     {
240       // beg line cap
241       create_path_cap (boxes,
242                        buildings,
243                        trans,
244                        Offset (x0, y0),
245                        thick / 2,
246                        perpendicular_slope (slope),
247                        Direction (sign (slope)));
248
249       // end line cap
250       create_path_cap (boxes,
251                        buildings,
252                        trans,
253                        Offset (x1, y1),
254                        thick / 2,
255                        perpendicular_slope (slope),
256                        Direction (sign (-slope)));
257     }
258 }
259
260 void
261 make_partial_ellipse_boxes (vector<Box> &boxes, vector<Drul_array<Offset> > &buildings, PangoMatrix trans, SCM expr)
262 {
263   Real x_rad = robust_scm2double (scm_car (expr), 0.0);
264   expr = scm_cdr (expr);
265   Real y_rad = robust_scm2double (scm_car (expr), 0.0);
266   expr = scm_cdr (expr);
267   Real start = robust_scm2double (scm_car (expr), 0.0);
268   expr = scm_cdr (expr);
269   Real end = robust_scm2double (scm_car (expr), 0.0);
270   expr = scm_cdr (expr);
271   Real th = robust_scm2double (scm_car (expr), 0.0);
272   expr = scm_cdr (expr);
273   bool connect = to_boolean (scm_car (expr));
274   expr = scm_cdr (expr);
275   bool fill = to_boolean (scm_car (expr));
276   //////////////////////
277   start = M_PI * start / 180;
278   end = M_PI * end / 180;
279   if (end == start)
280     end += (2 * M_PI);
281   complex<Real> sunit = polar (1.0, start);
282   complex<Real> eunit = polar (1.0, end);
283   Offset sp (real (sunit) * x_rad, imag (sunit) * y_rad);
284   Offset ep (real (eunit) * x_rad, imag (eunit) * y_rad);
285   //////////////////////
286   Drul_array<vector<Offset> > points;
287   Direction d = DOWN;
288   int quantization = max (1, (int) (((x_rad * trans.xx) + (y_rad * trans.yy)) * M_PI / QUANTIZATION_UNIT));
289   do
290     {
291       for (vsize i = 0; i < 1 + (vsize) quantization; i++)
292         {
293           Real ang = linear_map (start, end, 0, quantization, i);
294           complex<Real> coord = polar (1.0, ang);
295           Offset pt (real (coord) * x_rad,
296                      imag (coord) * y_rad);
297           Real slope = pt[Y_AXIS] / pt[X_AXIS];
298           Offset inter = get_point_in_y_direction (pt, perpendicular_slope (slope), th / 2, d);
299           pango_matrix_transform_point (&trans, &inter[X_AXIS], &inter[Y_AXIS]);
300           points[d].push_back (inter);
301         }
302     }
303   while (flip (&d) != DOWN);
304
305   for (vsize i = 0; i < points[DOWN].size () - 1; i++)
306     {
307       Box b;
308       do
309         {
310           b.add_point (points[d][i]);
311           b.add_point (points[d][i + 1]);
312         }
313       while (flip (&d) != DOWN);
314       boxes.push_back (b);
315     }
316
317   if (connect || fill)
318     {
319       make_draw_line_boxes (boxes, buildings, trans, scm_list_5 (scm_from_double (th),
320                                                                  scm_from_double (sp[X_AXIS]),
321                                                                  scm_from_double (sp[Y_AXIS]),
322                                                                  scm_from_double (ep[X_AXIS]),
323                                                                  scm_from_double (ep[Y_AXIS])),
324                             false);
325     }
326
327   if (th > 0.0)
328     {
329       // beg line cap
330       complex<Real> coord = polar (1.0, start);
331       Offset pt (real (coord) * x_rad,
332                  imag (coord) * y_rad);
333       Real slope = pt[Y_AXIS] / pt[X_AXIS];
334       create_path_cap (boxes,
335                        buildings,
336                        trans,
337                        pt,
338                        th / 2,
339                        perpendicular_slope (slope),
340                        Direction (sign (slope)));
341
342       // end line cap
343       coord = polar (1.0, start);
344       pt = Offset (real (coord) * x_rad,
345                    imag (coord) * y_rad);
346       slope = pt[Y_AXIS] / pt[X_AXIS];
347       create_path_cap (boxes,
348                        buildings,
349                        trans,
350                        pt,
351                        th / 2,
352                        perpendicular_slope (slope),
353                        Direction (sign (-slope)));
354     }
355 }
356
357 void
358 make_round_filled_box_boxes (vector<Box> &boxes, PangoMatrix trans, SCM expr)
359 {
360   Real left = robust_scm2double (scm_car (expr), 0.0);
361   expr = scm_cdr (expr);
362   Real right = robust_scm2double (scm_car (expr), 0.0);
363   expr = scm_cdr (expr);
364   Real bottom = robust_scm2double (scm_car (expr), 0.0);
365   expr = scm_cdr (expr);
366   Real top = robust_scm2double (scm_car (expr), 0.0);
367   expr = scm_cdr (expr);
368   Real th = robust_scm2double (scm_car (expr), 0.0);
369   //////////////////////
370   vector<Offset> points;
371   Box b;
372   Offset p0 = Offset (-left - (th / 2), -bottom - (th / 2));
373   Offset p1 = Offset (right + (th / 2), top + (th / 2));
374   pango_matrix_transform_point (&trans, &p0[X_AXIS], &p0[Y_AXIS]);
375   pango_matrix_transform_point (&trans, &p1[X_AXIS], &p1[Y_AXIS]);
376   b.add_point (p0);
377   b.add_point (p1);
378   boxes.push_back (b);
379 }
380
381 void
382 create_path_cap (vector<Box> &boxes, vector<Drul_array<Offset> > &buildings, PangoMatrix trans, Offset pt, Real rad, Real slope, Direction d)
383 {
384   Real angle = atan (slope) * 180 / M_PI;
385   Real other = angle > 180 ? angle - 180 : angle + 180;
386   if (angle < other)
387     {
388       Real holder = other;
389       other = angle;
390       angle = holder;
391     }
392   other = (slope >= 0 && d == DOWN) || (slope < 0 && d == UP)
393           ? other + 360.0
394           : other;
395   PangoMatrix new_trans (trans);
396   pango_matrix_translate (&new_trans, pt[X_AXIS], pt[Y_AXIS]);
397   make_partial_ellipse_boxes (boxes, buildings, new_trans,
398                               scm_list_n (scm_from_double (rad),
399                                           scm_from_double (rad),
400                                           scm_from_double (angle),
401                                           scm_from_double (other),
402                                           scm_from_double (0.0),
403                                           SCM_BOOL_F,
404                                           SCM_BOOL_F,
405                                           SCM_UNDEFINED));
406 }
407
408 void
409 make_draw_bezier_boxes (vector<Box> &boxes, vector<Drul_array<Offset> > &buildings, PangoMatrix trans, SCM expr)
410 {
411   Real th = robust_scm2double (scm_car (expr), 0.0);
412   expr = scm_cdr (expr);
413   Real x0 = robust_scm2double (scm_car (expr), 0.0);
414   expr = scm_cdr (expr);
415   Real y0 = robust_scm2double (scm_car (expr), 0.0);
416   expr = scm_cdr (expr);
417   Real x1 = robust_scm2double (scm_car (expr), 0.0);
418   expr = scm_cdr (expr);
419   Real y1 = robust_scm2double (scm_car (expr), 0.0);
420   expr = scm_cdr (expr);
421   Real x2 = robust_scm2double (scm_car (expr), 0.0);
422   expr = scm_cdr (expr);
423   Real y2 = robust_scm2double (scm_car (expr), 0.0);
424   expr = scm_cdr (expr);
425   Real x3 = robust_scm2double (scm_car (expr), 0.0);
426   expr = scm_cdr (expr);
427   Real y3 = robust_scm2double (scm_car (expr), 0.0);
428   //////////////////////
429   Bezier curve;
430   curve.control_[0] = Offset (x0, y0);
431   curve.control_[1] = Offset (x1, y1);
432   curve.control_[2] = Offset (x2, y2);
433   curve.control_[3] = Offset (x3, y3);
434   Offset temp0 (x0, y0);
435   Offset temp1 (x1, y1);
436   Offset temp2 (x2, y2);
437   Offset temp3 (x3, y3);
438   pango_matrix_transform_point (&trans, &temp0[X_AXIS], &temp0[Y_AXIS]);
439   pango_matrix_transform_point (&trans, &temp1[X_AXIS], &temp1[Y_AXIS]);
440   pango_matrix_transform_point (&trans, &temp2[X_AXIS], &temp2[Y_AXIS]);
441   pango_matrix_transform_point (&trans, &temp3[X_AXIS], &temp3[Y_AXIS]);
442   //////////////////////
443   Drul_array<vector<Offset> > points;
444   Direction d = DOWN;
445   int quantization = int (((temp1 - temp0).length ()
446                            + (temp2 - temp1).length ()
447                            + (temp3 - temp2).length ())
448                           / QUANTIZATION_UNIT);
449   do
450     {
451       Offset first = get_point_in_y_direction (curve.control_[0], perpendicular_slope (curve.slope_at_point (0.0)), th / 2, d);
452       pango_matrix_transform_point (&trans, &first[X_AXIS], &first[Y_AXIS]);
453       points[d].push_back (first);
454       for (vsize i = 1; i < (vsize) quantization; i++)
455         {
456           Real pt = (i * 1.0) / quantization;
457           Offset inter = get_point_in_y_direction (curve.curve_point (pt), perpendicular_slope (curve.slope_at_point (pt)), th / 2, d);
458           pango_matrix_transform_point (&trans, &inter[X_AXIS], &inter[Y_AXIS]);
459           points[d].push_back (inter);
460         }
461       Offset last = get_point_in_y_direction (curve.control_[3], curve.slope_at_point (1.0), th / 2, d);
462       pango_matrix_transform_point (&trans, &last[X_AXIS], &last[Y_AXIS]);
463       points[d].push_back (last);
464     }
465   while (flip (&d) != DOWN);
466
467   for (vsize i = 0; i < points[DOWN].size () - 1; i++)
468     {
469       Box b;
470       do
471         {
472           b.add_point (points[d][i]);
473           b.add_point (points[d][i + 1]);
474         }
475       while (flip (&d) != DOWN);
476       boxes.push_back (b);
477     }
478
479   // beg line cap
480   if (th >= 0)
481     {
482       Real slope = curve.slope_at_point (0.0);
483       d = Direction (sign (slope == 0.0 || abs (slope) == infinity_f
484                            ? curve.slope_at_point (0.0001)
485                            : slope));
486
487       create_path_cap (boxes,
488                        buildings,
489                        trans,
490                        curve.control_[0],
491                        th / 2,
492                        perpendicular_slope (curve.slope_at_point (0.0)),
493                        d);
494
495       // end line cap
496       slope = curve.slope_at_point (1.0);
497       d = Direction (sign (slope == 0.0 || abs (slope) == infinity_f
498                            ? curve.slope_at_point (0.9999)
499                            : slope));
500
501       create_path_cap (boxes,
502                        buildings,
503                        trans,
504                        curve.control_[3],
505                        th / 2,
506                        perpendicular_slope (curve.slope_at_point (1.0)),
507                        d);
508     }
509 }
510
511 /*
512   converts a path into lists of 4 (line) or 8 (curve) absolute coordinates
513   for example:
514   '(moveto 1 2 lineto 3 4 rlineto -1 -1 curveto 3 3 5 5 6 6 rcurveto -1 -1 -1 -1 -1 -1 closepath)
515   becomes
516   '((1 2 3 4)
517     (3 4 2 3)
518     (2 3 3 3 5 5 6 6)
519     (6 6 5 5 4 4 3 3)
520     (3 3 1 2))
521 */
522
523 SCM
524 all_commands_to_absolute_and_group (SCM expr)
525 {
526   SCM out = SCM_EOL;
527   Offset start (0, 0);
528   Offset current (0, 0);
529   bool first = true;
530   while (scm_is_pair (expr))
531     {
532       if (scm_is_eq (scm_car (expr), ly_symbol2scm ("moveto"))
533           || (scm_is_eq (scm_car (expr), ly_symbol2scm ("rmoveto")) && first))
534         {
535           Real x = robust_scm2double (scm_cadr (expr), 0.0);
536           Real y = robust_scm2double (scm_caddr (expr), 0.0);
537           start = Offset (x, y);
538           current = start;
539           expr = scm_cdddr (expr);
540         }
541       if (scm_is_eq (scm_car (expr), ly_symbol2scm ("rmoveto")))
542         {
543           Real x = robust_scm2double (scm_cadr (expr), 0.0);
544           Real y = robust_scm2double (scm_caddr (expr), 0.0);
545           start = (Offset (x, y) + current);
546           current = start;
547           expr = scm_cdddr (expr);
548         }
549       else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("lineto")))
550         {
551           Real x = robust_scm2double (scm_cadr (expr), 0.0);
552           Real y = robust_scm2double (scm_caddr (expr), 0.0);
553           out = scm_cons (scm_list_4 (scm_from_double (current[X_AXIS]),
554                                       scm_from_double (current[Y_AXIS]),
555                                       scm_from_double (x),
556                                       scm_from_double (y)),
557                           out);
558           current = Offset (x, y);
559           expr = scm_cdddr (expr);
560         }
561       else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("rlineto")))
562         {
563           Real x = robust_scm2double (scm_cadr (expr), 0.0);
564           Real y = robust_scm2double (scm_caddr (expr), 0.0);
565           out = scm_cons (scm_list_4 (scm_from_double (current[X_AXIS]),
566                                       scm_from_double (current[Y_AXIS]),
567                                       scm_from_double (x + current[X_AXIS]),
568                                       scm_from_double (y + current[Y_AXIS])),
569                           out);
570           current = (Offset (x, y) + current);
571           expr = scm_cdddr (expr);
572         }
573       else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("curveto")))
574         {
575           Real x1 = robust_scm2double (scm_cadr (expr), 0.0);
576           expr = scm_cddr (expr);
577           Real y1 = robust_scm2double (scm_car (expr), 0.0);
578           expr = scm_cdr (expr);
579           Real x2 = robust_scm2double (scm_car (expr), 0.0);
580           expr = scm_cdr (expr);
581           Real y2 = robust_scm2double (scm_car (expr), 0.0);
582           expr = scm_cdr (expr);
583           Real x3 = robust_scm2double (scm_car (expr), 0.0);
584           expr = scm_cdr (expr);
585           Real y3 = robust_scm2double (scm_car (expr), 0.0);
586           expr = scm_cdr (expr);
587           out = scm_cons (scm_list_n (scm_from_double (current[X_AXIS]),
588                                       scm_from_double (current[Y_AXIS]),
589                                       scm_from_double (x1),
590                                       scm_from_double (y1),
591                                       scm_from_double (x2),
592                                       scm_from_double (y2),
593                                       scm_from_double (x3),
594                                       scm_from_double (y3),
595                                       SCM_UNDEFINED),
596                           out);
597           current = Offset (x3, y3);
598         }
599       else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("rcurveto")))
600         {
601           Real x1 = robust_scm2double (scm_cadr (expr), 0.0);
602           expr = scm_cddr (expr);
603           Real y1 = robust_scm2double (scm_car (expr), 0.0);
604           expr = scm_cdr (expr);
605           Real x2 = robust_scm2double (scm_car (expr), 0.0);
606           expr = scm_cdr (expr);
607           Real y2 = robust_scm2double (scm_car (expr), 0.0);
608           expr = scm_cdr (expr);
609           Real x3 = robust_scm2double (scm_car (expr), 0.0);
610           expr = scm_cdr (expr);
611           Real y3 = robust_scm2double (scm_car (expr), 0.0);
612           expr = scm_cdr (expr);
613           out = scm_cons (scm_list_n (scm_from_double (current[X_AXIS]),
614                                       scm_from_double (current[Y_AXIS]),
615                                       scm_from_double (x1 + current[X_AXIS]),
616                                       scm_from_double (y1 + current[Y_AXIS]),
617                                       scm_from_double (x2 + current[X_AXIS]),
618                                       scm_from_double (y2 + current[Y_AXIS]),
619                                       scm_from_double (x3 + current[X_AXIS]),
620                                       scm_from_double (y3 + current[Y_AXIS]),
621                                       SCM_UNDEFINED),
622                           out);
623           current = (Offset (x3, y3) + current);
624         }
625       else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("closepath")))
626         {
627           if ((current[X_AXIS] != start[X_AXIS]) || (current[Y_AXIS] != start[Y_AXIS]))
628             {
629               out = scm_cons (scm_list_4 (scm_from_double (current[X_AXIS]),
630                                           scm_from_double (current[Y_AXIS]),
631                                           scm_from_double (start[X_AXIS]),
632                                           scm_from_double (start[Y_AXIS])),
633                               out);
634               current = start;
635             }
636           expr = scm_cdr (expr);
637         }
638       else
639         {
640           warning ("Malformed path for path stencil.");
641           return out;
642         }
643       first = false;
644     }
645   return scm_reverse_x (out, SCM_EOL);
646 }
647
648 void
649 internal_make_path_boxes (vector<Box> &boxes, vector<Drul_array<Offset> > &buildings, PangoMatrix trans, SCM expr, bool use_building)
650 {
651   SCM blot = scm_car (expr);
652   expr = scm_cdr (expr);
653   SCM path = all_commands_to_absolute_and_group (expr);
654   // note that expr has more stuff that we don't need after this - simply ignore it
655   //////////////////////
656   for (SCM s = path; scm_is_pair (s); s = scm_cdr (s))
657     {
658       scm_to_int (scm_length (scm_car (s))) == 4
659       ? make_draw_line_boxes (boxes, buildings, trans, scm_cons (blot, scm_car (s)), use_building)
660       : make_draw_bezier_boxes (boxes, buildings, trans, scm_cons (blot, scm_car (s)));
661     }
662 }
663
664 void
665 make_path_boxes (vector<Box> &boxes, vector<Drul_array<Offset> > &buildings, PangoMatrix trans, SCM expr)
666 {
667   return internal_make_path_boxes (boxes, buildings, trans, scm_cons (scm_car (expr), get_path_list (scm_cdr (expr))), false);
668 }
669
670 void
671 make_polygon_boxes (vector<Box> &boxes, vector<Drul_array<Offset> > &buildings, PangoMatrix trans, SCM expr)
672 {
673   SCM coords = get_number_list (scm_car (expr));
674   expr = scm_cdr (expr);
675   SCM blot_diameter = scm_car (expr);
676   //////////////////////
677   bool first = true;
678   SCM l = SCM_EOL;
679   for (SCM s = coords; scm_is_pair (s); s = scm_cddr (s))
680     {
681       l = scm_cons (first ? ly_symbol2scm ("moveto") : ly_symbol2scm ("lineto"), l);
682       l = scm_cons (scm_car (s), l);
683       l = scm_cons (scm_cadr (s), l);
684       first = false;
685     }
686   l = scm_cons (ly_symbol2scm ("closepath"), l);
687   internal_make_path_boxes (boxes, buildings, trans, scm_cons (blot_diameter, scm_reverse_x (l, SCM_EOL)), true);
688 }
689
690 void
691 make_named_glyph_boxes (vector<Box> &boxes, vector<Drul_array<Offset> > &buildings, PangoMatrix trans, SCM expr)
692 {
693   SCM fm_scm = scm_car (expr);
694   Font_metric *fm = Font_metric::unsmob (fm_scm);
695   expr = scm_cdr (expr);
696   SCM glyph = scm_car (expr);
697   string glyph_s = ly_scm2string (glyph);
698
699   //////////////////////
700   Open_type_font *open_fm
701     = dynamic_cast<Open_type_font *>
702       (dynamic_cast<Modified_font_metric *>(fm)->original_font ());
703   SCM_ASSERT_TYPE (open_fm, fm_scm, SCM_ARG1, __FUNCTION__, "OpenType font");
704
705   size_t gidx = open_fm->name_to_index (glyph_s);
706   // Bbox is the best approximation of the width based on how it would be
707   // calculated in open-type-font.cc if it were based on real extents
708   Box bbox = open_fm->get_unscaled_indexed_char_dimensions (gidx);
709   bbox.scale (dynamic_cast<Modified_font_metric *>(fm)->get_magnification () * open_fm->design_size () / open_fm->get_units_per_EM ());
710   // Real bbox is the real bbox of the object
711   Box real_bbox = open_fm->get_glyph_outline_bbox (gidx);
712
713   Real scale = bbox[X_AXIS].length () / real_bbox[X_AXIS].length ();
714
715   pango_matrix_scale (&trans, scale, scale);
716
717   SCM outline = open_fm->get_glyph_outline (gidx);
718   //////////////////////
719   for (SCM s = outline;
720        scm_is_pair (s);
721        s = scm_cdr (s))
722     {
723       scm_to_int (scm_length (scm_car (s))) == 4
724       ? make_draw_line_boxes (boxes, buildings, trans, scm_cons (scm_from_double (0), scm_car (s)), false)
725       : make_draw_bezier_boxes (boxes, buildings, trans, scm_cons (scm_from_double (0), scm_car (s)));
726     }
727 }
728
729 void
730 make_glyph_string_boxes (vector<Box> &boxes, vector<Drul_array<Offset> > &buildings, PangoMatrix trans, SCM expr)
731 {
732   SCM fm_scm = scm_car (expr);
733   Font_metric *fm = Font_metric::unsmob (fm_scm);
734   expr = scm_cdr (expr);
735   expr = scm_cdr (expr); // font-name
736   expr = scm_cdr (expr); // size
737   expr = scm_cdr (expr); // cid?
738   SCM whxy = scm_cadar (expr);
739   vector<Real> widths;
740   vector<Interval> heights;
741   vector<Real> xos;
742   vector<Real> yos;
743   vector<string> char_ids;
744   //////////////////////
745   Pango_font *pango_fm = dynamic_cast<Pango_font *> (fm);
746   SCM_ASSERT_TYPE (pango_fm, fm_scm, SCM_ARG1, __FUNCTION__, "Pango font");
747
748   for (SCM s = whxy; scm_is_pair (s); s = scm_cdr (s))
749     {
750       SCM now = scm_car (s);
751       widths.push_back (robust_scm2double (scm_car (now), 0.0));
752       now = scm_cdr (now);
753       heights.push_back (robust_scm2interval (scm_car (now), Interval (0, 0)));
754       now = scm_cdr (now);
755       xos.push_back (robust_scm2double (scm_car (now), 0.0));
756       now = scm_cdr (now);
757       yos.push_back (robust_scm2double (scm_car (now), 0.0));
758       now = scm_cdr (now);
759       char_ids.push_back (robust_scm2string (scm_car (now), ""));
760     }
761   Real cumulative_x = 0.0;
762   for (vsize i = 0; i < widths.size (); i++)
763     {
764       PangoMatrix transcopy (trans);
765       Offset pt0 (cumulative_x + xos[i], heights[i][DOWN] + yos[i]);
766       Offset pt1 (cumulative_x + widths[i] + xos[i], heights[i][UP] + yos[i]);
767       cumulative_x += widths[i];
768
769       Box kerned_bbox;
770       kerned_bbox.add_point (pt0);
771       kerned_bbox.add_point (pt1);
772       size_t gidx = pango_fm->name_to_index (char_ids[i]);
773       Box real_bbox = pango_fm->get_scaled_indexed_char_dimensions (gidx);
774       Box bbox = pango_fm->get_unscaled_indexed_char_dimensions (gidx);
775       SCM outline = pango_fm->get_glyph_outline (gidx);
776
777       // scales may have rounding error but should be close
778       Real xlen = real_bbox[X_AXIS].length () / bbox[X_AXIS].length ();
779       Real ylen = real_bbox[Y_AXIS].length () / bbox[Y_AXIS].length ();
780
781       /*
782         TODO:
783
784         The value will be nan for whitespace, in which case we just want
785         filler, so the kerned bbox is ok.
786
787         However, if the value is inf, this likely means that LilyPond is
788         using a font that is currently difficult to get the measurements
789         from the Pango_font.  This should eventually be fixed.  The solution
790         for now is just to use the bounding box.
791       */
792       if (isnan (xlen) || isnan (ylen) || isinf (xlen) || isinf (ylen))
793         outline = box_to_scheme_lines (kerned_bbox);
794       else
795         {
796           assert (abs (xlen - ylen) < 10e-3);
797
798           Real scale_factor = max (xlen, ylen);
799           // the three operations below move the stencil from its original coordinates to current coordinates
800           pango_matrix_translate (&transcopy, kerned_bbox[X_AXIS][LEFT], kerned_bbox[Y_AXIS][DOWN] - real_bbox[Y_AXIS][DOWN]);
801           pango_matrix_translate (&transcopy, real_bbox[X_AXIS][LEFT], real_bbox[Y_AXIS][DOWN]);
802           pango_matrix_scale (&transcopy, scale_factor, scale_factor);
803           pango_matrix_translate (&transcopy, -bbox[X_AXIS][LEFT], -bbox[Y_AXIS][DOWN]);
804         }
805       //////////////////////
806       for (SCM s = outline;
807            scm_is_pair (s);
808            s = scm_cdr (s))
809         {
810           scm_to_int (scm_length (scm_car (s))) == 4
811           ? make_draw_line_boxes (boxes, buildings, transcopy, scm_cons (scm_from_double (0), scm_car (s)), false)
812           : make_draw_bezier_boxes (boxes, buildings, transcopy, scm_cons (scm_from_double (0), scm_car (s)));
813         }
814     }
815 }
816
817 /*
818   receives a stencil expression and a transform matrix
819   depending on the stencil name, dispatches it to the appropriate function
820 */
821
822 void
823 stencil_dispatcher (vector<Box> &boxes, vector<Drul_array<Offset> > &buildings, PangoMatrix trans, SCM expr)
824 {
825   if (not scm_is_pair (expr))
826     return;
827   if (scm_is_eq (scm_car (expr), ly_symbol2scm ("draw-line")))
828     make_draw_line_boxes (boxes, buildings, trans, scm_cdr (expr), true);
829   else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("dashed-line")))
830     {
831       expr = scm_cdr (expr);
832       SCM th = scm_car (expr);
833       expr = scm_cdr (expr);
834       expr = scm_cdr (expr); // on
835       expr = scm_cdr (expr); // off
836       SCM x1 = scm_car (expr);
837       expr = scm_cdr (expr);
838       SCM x2 = scm_car (expr);
839       make_draw_line_boxes (boxes, buildings, trans, scm_list_5 (th, scm_from_double (0.0), scm_from_double (0.0), x1, x2), true);
840     }
841   else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("circle")))
842     {
843       expr = scm_cdr (expr);
844       SCM rad = scm_car (expr);
845       expr = scm_cdr (expr);
846       SCM th = scm_car (expr);
847       make_partial_ellipse_boxes (boxes, buildings, trans,
848                                   scm_list_n (rad,
849                                               rad,
850                                               scm_from_double (0.0),
851                                               scm_from_double (360.0),
852                                               th,
853                                               SCM_BOOL_F,
854                                               SCM_BOOL_T,
855                                               SCM_UNDEFINED));
856     }
857   else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("ellipse")))
858     {
859       expr = scm_cdr (expr);
860       SCM x_rad = scm_car (expr);
861       expr = scm_cdr (expr);
862       SCM y_rad = scm_car (expr);
863       expr = scm_cdr (expr);
864       SCM th = scm_car (expr);
865       make_partial_ellipse_boxes (boxes, buildings, trans,
866                                   scm_list_n (x_rad,
867                                               y_rad,
868                                               scm_from_double (0.0),
869                                               scm_from_double (360.0),
870                                               th,
871                                               SCM_BOOL_F,
872                                               SCM_BOOL_T,
873                                               SCM_UNDEFINED));
874     }
875   else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("partial-ellipse")))
876     make_partial_ellipse_boxes (boxes, buildings, trans, scm_cdr (expr));
877   else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("round-filled-box")))
878     make_round_filled_box_boxes (boxes, trans, scm_cdr (expr));
879   else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("named-glyph")))
880     make_named_glyph_boxes (boxes, buildings, trans, scm_cdr (expr));
881   else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("polygon")))
882     make_polygon_boxes (boxes, buildings, trans, scm_cdr (expr));
883   else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("path")))
884     make_path_boxes (boxes, buildings, trans, scm_cdr (expr));
885   else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("glyph-string")))
886     make_glyph_string_boxes (boxes, buildings, trans, scm_cdr (expr));
887   else
888     {
889 #if 0
890       warning ("Stencil expression not supported by the veritcal skylines.");
891 #endif
892       /*
893         We don't issue a warning here, as we assume that stencil-expression.cc
894         is doing stencil-checking correctly.
895       */
896     }
897 }
898
899 /*
900   traverses a stencil expression, returning a vector of Transform_matrix_and_expression
901   the struct Transform_matrix_and_expression contains two members,
902   a Transform_matrix that indicates where to move a stencil and the stencil expression
903   to show how to construct the stencil
904 */
905 vector<Transform_matrix_and_expression>
906 stencil_traverser (PangoMatrix trans, SCM expr)
907 {
908   if (scm_is_null (expr))
909     return vector<Transform_matrix_and_expression> ();
910   else if (scm_is_eq (expr, ly_string2scm ("")))
911     return vector<Transform_matrix_and_expression> ();
912   else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("combine-stencil")))
913     {
914       vector<Transform_matrix_and_expression> out;
915       for (SCM s = scm_cdr (expr); scm_is_pair (s); s = scm_cdr (s))
916         {
917           vector<Transform_matrix_and_expression> res = stencil_traverser (trans, scm_car (s));
918           out.insert (out.end (), res.begin (), res.end ());
919         }
920       return out;
921     }
922   else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("footnote")))
923     return vector<Transform_matrix_and_expression> ();
924   else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("translate-stencil")))
925     {
926       Real x = robust_scm2double (scm_caadr (expr), 0.0);
927       Real y = robust_scm2double (scm_cdadr (expr), 0.0);
928       pango_matrix_translate (&trans, x, y);
929       return stencil_traverser (trans, scm_caddr (expr));
930     }
931   else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("scale-stencil")))
932     {
933       Real x = robust_scm2double (scm_caadr (expr), 0.0);
934       Real y = robust_scm2double (scm_cadadr (expr), 0.0);
935       pango_matrix_scale (&trans, x, y);
936       return stencil_traverser (trans, scm_caddr (expr));
937     }
938   else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("rotate-stencil")))
939     {
940       Real ang = robust_scm2double (scm_caadr (expr), 0.0);
941       Real x = robust_scm2double (scm_car (scm_cadadr (expr)), 0.0);
942       Real y = robust_scm2double (scm_cdr (scm_cadadr (expr)), 0.0);
943       pango_matrix_translate (&trans, x, y);
944       pango_matrix_rotate (&trans, -ang);
945       pango_matrix_translate (&trans, -x, -y);
946       return stencil_traverser (trans, scm_caddr (expr));
947     }
948   else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("delay-stencil-evaluation")))
949     // should not use the place-holder text, but no need for the warning below
950     return vector<Transform_matrix_and_expression> ();
951   else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("grob-cause")))
952     return stencil_traverser (trans, scm_caddr (expr));
953   else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("color")))
954     return stencil_traverser (trans, scm_caddr (expr));
955   else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("transparent-stencil")))
956     return stencil_traverser (trans, scm_cadr (expr));
957   else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("id")))
958     return stencil_traverser (trans, scm_caddr (expr));
959   else
960     {
961       vector<Transform_matrix_and_expression> out;
962       out.push_back (Transform_matrix_and_expression (trans, expr));
963       return out;
964     }
965   warning ("Stencil expression not supported by the veritcal skylines.");
966   return vector<Transform_matrix_and_expression> ();
967 }
968
969 SCM
970 Grob::maybe_pure_internal_simple_skylines_from_extents (Grob *me, Axis a, bool pure, int beg, int end, bool ignore_x, bool ignore_y)
971 {
972   vector<Box> boxes;
973   // we don't know how far spanners stretch along the X axis before
974   // line breaking. better have them take up the whole thing
975   Interval xex = ignore_x
976                  ? Interval (-infinity_f, infinity_f)
977                  : me->extent (me, X_AXIS);
978
979   // If we're looking at the x exent of a cross staff grob, it could be
980   // very early on in the computation process.  We won't know its height
981   // until way later, so we give a brute force approximation.
982   Interval yex = ignore_y
983                  ? Interval (-infinity_f, infinity_f)
984                  : me->maybe_pure_extent (me, Y_AXIS, pure, beg, end);
985
986   if (xex.is_empty () || yex.is_empty ())
987     return Skyline_pair ().smobbed_copy ();
988
989   boxes.push_back (Box (xex, yex));
990   return Skyline_pair (boxes, a).smobbed_copy ();
991 }
992
993 MAKE_SCHEME_CALLBACK (Grob, pure_simple_vertical_skylines_from_extents, 3);
994 SCM
995 Grob::pure_simple_vertical_skylines_from_extents (SCM smob, SCM begscm, SCM endscm)
996 {
997   Grob *me = Grob::unsmob (smob);
998   int beg = robust_scm2int (begscm, 0);
999   int end = robust_scm2int (endscm, INT_MAX);
1000   // We cannot measure the widths before line breaking,
1001   // so we assume that the width is infinite: pass ignore_x=true
1002   return maybe_pure_internal_simple_skylines_from_extents (me, X_AXIS, true, beg, end, true, false);
1003 }
1004
1005 MAKE_SCHEME_CALLBACK (Grob, simple_vertical_skylines_from_extents, 1);
1006 SCM
1007 Grob::simple_vertical_skylines_from_extents (SCM smob)
1008 {
1009   Grob *me = Grob::unsmob (smob);
1010   return maybe_pure_internal_simple_skylines_from_extents (me, X_AXIS, false, 0, 0, false, false);
1011 }
1012
1013 MAKE_SCHEME_CALLBACK (Grob, pure_simple_horizontal_skylines_from_extents, 3);
1014 SCM
1015 Grob::pure_simple_horizontal_skylines_from_extents (SCM smob, SCM begscm, SCM endscm)
1016 {
1017   Grob *me = Grob::unsmob (smob);
1018   int beg = robust_scm2int (begscm, 0);
1019   int end = robust_scm2int (endscm, INT_MAX);
1020   // If the grob is cross staff, we cannot measure its Y-extent before
1021   // wayyyy downstream (after spacing of axis groups is done).
1022   // Thus, we assume that the Y extent is infinite for cross staff grobs.
1023   return maybe_pure_internal_simple_skylines_from_extents (me, Y_AXIS, true, beg, end, false, to_boolean (me->get_property ("cross-staff")));
1024 }
1025
1026 MAKE_SCHEME_CALLBACK (Grob, simple_horizontal_skylines_from_extents, 1);
1027 SCM
1028 Grob::simple_horizontal_skylines_from_extents (SCM smob)
1029 {
1030   Grob *me = Grob::unsmob (smob);
1031   // See comment in function above.
1032   return maybe_pure_internal_simple_skylines_from_extents (me, Y_AXIS, false, 0, 0, false, to_boolean (me->get_property ("cross-staff")));
1033 }
1034
1035 SCM
1036 Stencil::skylines_from_stencil (SCM sten, Real pad, Axis a)
1037 {
1038   Stencil *s = Stencil::unsmob (sten);
1039   if (!s)
1040     return Skyline_pair ().smobbed_copy ();
1041
1042   vector<Transform_matrix_and_expression> data
1043     = stencil_traverser (make_transform_matrix (1.0, 0.0, 0.0, 1.0, 0.0, 0.0),
1044                          s->expr ());
1045   vector<Box> boxes;
1046   vector<Drul_array<Offset> > buildings;
1047   for (vsize i = 0; i < data.size (); i++)
1048     stencil_dispatcher (boxes, buildings, data[i].tm_, data[i].expr_);
1049
1050   // we use the bounding box if there are no boxes
1051   if (!boxes.size () && !buildings.size ())
1052     boxes.push_back (Box (s->extent (X_AXIS), s->extent (Y_AXIS)));
1053
1054   Skyline_pair out (boxes, a);
1055   out.merge (Skyline_pair (buildings, a));
1056
1057   for (DOWN_and_UP (d))
1058     out[d] = out[d].padded (pad);
1059
1060   return out.smobbed_copy ();
1061 }
1062
1063 MAKE_SCHEME_CALLBACK (Grob, vertical_skylines_from_stencil, 1);
1064 SCM
1065 Grob::vertical_skylines_from_stencil (SCM smob)
1066 {
1067   Grob *me = Grob::unsmob (smob);
1068
1069   Real pad = robust_scm2double (me->get_property ("skyline-horizontal-padding"), 0.0);
1070   SCM out = Stencil::skylines_from_stencil (me->get_property ("stencil"), pad, X_AXIS);
1071
1072   return out;
1073 }
1074
1075 MAKE_SCHEME_CALLBACK (Grob, horizontal_skylines_from_stencil, 1);
1076 SCM
1077 Grob::horizontal_skylines_from_stencil (SCM smob)
1078 {
1079   Grob *me = Grob::unsmob (smob);
1080
1081   Real pad = robust_scm2double (me->get_property ("skyline-vertical-padding"), 0.0);
1082   SCM out = Stencil::skylines_from_stencil (me->get_property ("stencil"), pad, Y_AXIS);
1083
1084   return out;
1085 }
1086
1087 SCM
1088 Grob::internal_skylines_from_element_stencils (Grob *me, Axis a, bool pure, int beg, int end)
1089 {
1090
1091   extract_grob_set (me, "elements", elts);
1092   vector<Real> x_pos;
1093   vector<Real> y_pos;
1094   Grob *x_common = common_refpoint_of_array (elts, me, X_AXIS);
1095   Grob *y_common = common_refpoint_of_array (elts, me, Y_AXIS);
1096   for (vsize i = 0; i < elts.size (); i++)
1097     {
1098       x_pos.push_back (elts[i]->relative_coordinate (x_common, X_AXIS));
1099       y_pos.push_back (elts[i]->maybe_pure_coordinate (y_common, Y_AXIS, pure, beg, end));
1100     }
1101   Real my_x = me->relative_coordinate (x_common, X_AXIS);
1102   Real my_y = me->maybe_pure_coordinate (y_common, Y_AXIS, pure, beg, end);
1103
1104   Skyline_pair res;
1105   for (vsize i = 0; i < elts.size (); i++)
1106     {
1107       Skyline_pair *skyp = Skyline_pair::unsmob (elts[i]->get_maybe_pure_property (a == X_AXIS ? "vertical-skylines" : "horizontal-skylines", pure, beg, end));
1108       if (skyp)
1109         {
1110           /*
1111             Here, copying is essential.  Otherwise, the skyline pair will
1112             get doubly shifted!
1113           */
1114           /*
1115             It took Mike about 6 months of his life to add the `else' clause
1116             below.  For horizontal skylines, the raise and shift calls need
1117             to be reversed.  This is what was causing the problems in the
1118             shifting with all of the tests. RIP 6 months!
1119           */
1120           Skyline_pair copy = Skyline_pair (*skyp);
1121           if (a == X_AXIS)
1122             {
1123               copy.shift (x_pos[i] - my_x);
1124               copy.raise (y_pos[i] - my_y);
1125             }
1126           else
1127             {
1128               copy.raise (x_pos[i] - my_x);
1129               copy.shift (y_pos[i] - my_y);
1130             }
1131           res.merge (copy);
1132         }
1133     }
1134   return res.smobbed_copy ();
1135 }
1136
1137 MAKE_SCHEME_CALLBACK (Grob, vertical_skylines_from_element_stencils, 1);
1138 SCM
1139 Grob::vertical_skylines_from_element_stencils (SCM smob)
1140 {
1141   Grob *me = Grob::unsmob (smob);
1142   return internal_skylines_from_element_stencils (me, X_AXIS, false, 0, INT_MAX);
1143 }
1144
1145 MAKE_SCHEME_CALLBACK (Grob, horizontal_skylines_from_element_stencils, 1);
1146 SCM
1147 Grob::horizontal_skylines_from_element_stencils (SCM smob)
1148 {
1149   Grob *me = Grob::unsmob (smob);
1150   return internal_skylines_from_element_stencils (me, Y_AXIS, false, 0, INT_MAX);
1151 }
1152
1153 MAKE_SCHEME_CALLBACK (Grob, pure_vertical_skylines_from_element_stencils, 3);
1154 SCM
1155 Grob::pure_vertical_skylines_from_element_stencils (SCM smob, SCM beg_scm, SCM end_scm)
1156 {
1157   Grob *me = Grob::unsmob (smob);
1158   int beg = robust_scm2int (beg_scm, 0);
1159   int end = robust_scm2int (end_scm, 0);
1160   return internal_skylines_from_element_stencils (me, X_AXIS, true, beg, end);
1161 }
1162
1163 MAKE_SCHEME_CALLBACK (Grob, pure_horizontal_skylines_from_element_stencils, 3);
1164 SCM
1165 Grob::pure_horizontal_skylines_from_element_stencils (SCM smob, SCM beg_scm, SCM end_scm)
1166 {
1167   Grob *me = Grob::unsmob (smob);
1168   int beg = robust_scm2int (beg_scm, 0);
1169   int end = robust_scm2int (end_scm, 0);
1170   return internal_skylines_from_element_stencils (me, Y_AXIS, true, beg, end);
1171 }