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