]> git.donarmstrong.com Git - lilypond.git/blob - lily/stencil-integral.cc
Merge remote-tracking branch 'origin/master' into translation
[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 "stencil.hh"
54 #include "string-convert.hh"
55 #include "skyline.hh"
56 #include "skyline-pair.hh"
57 using namespace std;
58
59 Real QUANTIZATION_UNIT = 0.2;
60
61 void create_path_cap (vector<Box> &boxes, vector<Drul_array<Offset> > &buildings, PangoMatrix trans, Offset pt, Real rad, Real slope, Direction d);
62
63 struct Transform_matrix_and_expression
64 {
65   PangoMatrix tm_;
66   SCM expr_;
67
68   Transform_matrix_and_expression (PangoMatrix tm, SCM expr);
69 };
70
71 Transform_matrix_and_expression::Transform_matrix_and_expression (PangoMatrix tm, SCM expr)
72 {
73   tm_ = tm;
74   expr_ = expr;
75 }
76
77 PangoMatrix
78 make_transform_matrix (Real p0, Real p1, Real p2, Real p3, Real p4, Real p5)
79 {
80   PangoMatrix out;
81   out.xx = p0;
82   out.xy = p1;
83   out.yx = p2;
84   out.yy = p3;
85   out.x0 = p4;
86   out.y0 = p5;
87   return out;
88 }
89
90 //// UTILITY FUNCTIONS
91
92 /*
93   map x's placement between orig_l and orig_r onto
94   the interval final_l final_r
95 */
96 Real
97 linear_map (Real final_l, Real final_r, Real orig_l, Real orig_r, Real x)
98 {
99   return final_l + ((final_r - final_l) * ((x - orig_l) / (orig_r - orig_l)));
100 }
101
102 /*
103   from a nested SCM list, return the first list of numbers
104   useful for polygons
105 */
106 SCM
107 get_number_list (SCM l)
108 {
109   if (scm_is_pair (l))
110     {
111       if (scm_is_number (scm_car (l)))
112         return l;
113       SCM res = get_number_list (scm_car (l));
114       if (res == SCM_BOOL_F)
115         return get_number_list (scm_cdr (l));
116       return res;
117     }
118   return SCM_BOOL_F;
119 }
120
121 /*
122   from a nested SCM list, return the first list of numbers
123   useful for paths
124 */
125 SCM
126 get_path_list (SCM l)
127 {
128   if (scm_is_pair (l))
129     {
130       if (scm_memv (scm_car (l),
131                     scm_list_n (ly_symbol2scm ("moveto"),
132                                 ly_symbol2scm ("rmoveto"),
133                                 ly_symbol2scm ("lineto"),
134                                 ly_symbol2scm ("rlineto"),
135                                 ly_symbol2scm ("curveto"),
136                                 ly_symbol2scm ("rcurveto"),
137                                 ly_symbol2scm ("closepath"),
138                                 SCM_UNDEFINED))
139           != SCM_BOOL_F)
140         return l;
141       SCM res = get_path_list (scm_car (l));
142       if (res == SCM_BOOL_F)
143         return get_path_list (scm_cdr (l));
144       return res;
145     }
146   return SCM_BOOL_F;
147 }
148
149 Real
150 perpendicular_slope (Real s)
151 {
152   if (s == 0.0)
153     return infinity_f;
154   if (s == infinity_f)
155     return 0.0;
156   return -1.0 / s;
157 }
158
159 //// END UTILITY FUNCTIONS
160
161 /*
162   below, for all of the functions make_X_boxes, the expression
163   is always unpacked into variables.
164   then, after a line of /////, there are manipulations of these variables
165   (there may be no manipulations necessary depending on the function)
166   afterwards, there is another ///// followed by the creation of points
167   and boxes
168 */
169
170 void
171 make_draw_line_boxes (vector<Box> &boxes, vector<Drul_array<Offset> > &buildings, PangoMatrix trans, SCM expr, bool use_building)
172 {
173   Real thick = robust_scm2double (scm_car (expr), 0.0);
174   expr = scm_cdr (expr);
175   Real x0 = robust_scm2double (scm_car (expr), 0.0);
176   expr = scm_cdr (expr);
177   Real y0 = robust_scm2double (scm_car (expr), 0.0);
178   expr = scm_cdr (expr);
179   Real x1 = robust_scm2double (scm_car (expr), 0.0);
180   expr = scm_cdr (expr);
181   Real y1 = robust_scm2double (scm_car (expr), 0.0);
182   Real slope = x1 == x0 ? infinity_f : (y1 - y0) / (x1 - x0);
183   //////////////////////
184   if (x1 < x0)
185     {
186       swap (x0, x1);
187       swap (y0, y1);
188     }
189   Offset left (x0, y0);
190   Offset right (x1, y1);
191   Direction d = DOWN;
192   do
193     {
194       Offset inter_l = get_point_in_y_direction (left, perpendicular_slope (slope), thick / 2, d);
195       Offset inter_r = get_point_in_y_direction (right, perpendicular_slope (slope), thick / 2, d);//printf ("O %4.4f %4.4f\n", inter_l[X_AXIS], inter_r[X_AXIS]);printf ("TRANNY %4.4f %4.4f %4.4f %4.4f %4.4f %4.4f\n", trans.xx, trans.xy, trans.yx, trans.yy, trans.x0, trans.y0);
196       pango_matrix_transform_point (&trans, &inter_l[X_AXIS], &inter_l[Y_AXIS]);
197       pango_matrix_transform_point (&trans, &inter_r[X_AXIS], &inter_r[Y_AXIS]);
198       if ((inter_l[X_AXIS] == inter_r[X_AXIS]) || (inter_l[Y_AXIS] == inter_r[Y_AXIS]))
199         {
200           //printf ("OO %4.4f %4.4f\n", inter_l[X_AXIS], inter_r[X_AXIS]);
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 + 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 < 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_car (expr) == ly_symbol2scm ("moveto")
533           || (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_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_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_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_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_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_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 = unsmob_metrics (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 = unsmob_metrics (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_car (expr) == ly_symbol2scm ("draw-line"))
828     make_draw_line_boxes (boxes, buildings, trans, scm_cdr (expr), true);
829   else if (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_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_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_car (expr) == ly_symbol2scm ("partial-ellipse"))
876     make_partial_ellipse_boxes (boxes, buildings, trans, scm_cdr (expr));
877   else if (scm_car (expr) == ly_symbol2scm ("round-filled-box"))
878     make_round_filled_box_boxes (boxes, trans, scm_cdr (expr));
879   else if (scm_car (expr) == ly_symbol2scm ("named-glyph"))
880     make_named_glyph_boxes (boxes, buildings, trans, scm_cdr (expr));
881   else if (scm_car (expr) == ly_symbol2scm ("polygon"))
882     make_polygon_boxes (boxes, buildings, trans, scm_cdr (expr));
883   else if (scm_car (expr) == ly_symbol2scm ("path"))
884     make_path_boxes (boxes, buildings, trans, scm_cdr (expr));
885   else if (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 (expr == ly_string2scm (""))
911     return vector<Transform_matrix_and_expression> ();
912   else if (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_car (expr) == ly_symbol2scm ("footnote"))
923     return vector<Transform_matrix_and_expression> ();
924   else if (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_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_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_car (expr) == ly_symbol2scm ("delay-stencil-evaluation"))
949     return stencil_traverser (trans, scm_force (scm_cadr (expr)));
950   else if (scm_car (expr) == ly_symbol2scm ("grob-cause"))
951     return stencil_traverser (trans, scm_caddr (expr));
952   else if (scm_car (expr) == ly_symbol2scm ("color"))
953     return stencil_traverser (trans, scm_caddr (expr));
954   else if (scm_car (expr) == ly_symbol2scm ("id"))
955     return stencil_traverser (trans, scm_caddr (expr));
956   else
957     {
958       vector<Transform_matrix_and_expression> out;
959       out.push_back (Transform_matrix_and_expression (trans, expr));
960       return out;
961     }
962   warning ("Stencil expression not supported by the veritcal skylines.");
963   return vector<Transform_matrix_and_expression> ();
964 }
965
966 SCM
967 Grob::internal_simple_skylines_from_stencil (SCM smob, Axis a)
968 {
969   Grob *me = unsmob_grob (smob);
970
971   if (to_boolean (me->get_property ("cross-staff")))
972     return Skyline_pair ().smobbed_copy ();
973
974   extract_grob_set (me, "elements", elts);
975   if (elts.size ())
976     return internal_skylines_from_element_stencils (smob, a);
977
978   Stencil *s = unsmob_stencil (me->get_property ("stencil"));
979   if (!s)
980     return Skyline_pair ().smobbed_copy ();
981
982   vector<Box> boxes;
983   boxes.push_back (Box (s->extent (X_AXIS), s->extent (Y_AXIS)));
984   return Skyline_pair (boxes, a).smobbed_copy ();
985 }
986
987 MAKE_SCHEME_CALLBACK (Grob, simple_vertical_skylines_from_stencil, 1);
988 SCM
989 Grob::simple_vertical_skylines_from_stencil (SCM smob)
990 {
991   return internal_simple_skylines_from_stencil (smob, X_AXIS);
992 }
993
994 MAKE_SCHEME_CALLBACK (Grob, simple_horizontal_skylines_from_stencil, 1);
995 SCM
996 Grob::simple_horizontal_skylines_from_stencil (SCM smob)
997 {
998   return internal_simple_skylines_from_stencil (smob, Y_AXIS);
999 }
1000
1001 SCM
1002 Stencil::skylines_from_stencil (SCM sten, Real pad, Axis a)
1003 {
1004   Stencil *s = unsmob_stencil (sten);
1005   if (!s)
1006     return Skyline_pair ().smobbed_copy ();
1007
1008   vector<Transform_matrix_and_expression> data
1009     = stencil_traverser (make_transform_matrix (1.0, 0.0, 0.0, 1.0, 0.0, 0.0),
1010                          s->expr ());
1011   vector<Box> boxes;
1012   vector<Drul_array<Offset> > buildings;
1013   for (vsize i = 0; i < data.size (); i++)
1014     stencil_dispatcher (boxes, buildings, data[i].tm_, data[i].expr_);
1015
1016   // we use the bounding box if there are no boxes
1017   if (!boxes.size () && !buildings.size ())
1018     boxes.push_back (Box (s->extent (X_AXIS), s->extent (Y_AXIS)));
1019
1020   Skyline_pair out (boxes, a);
1021   out.merge (Skyline_pair (buildings, a));
1022
1023   for (DOWN_and_UP (d))
1024     out[d] = out[d].padded (pad);
1025
1026   out.deholify ();
1027   return out.smobbed_copy ();
1028 }
1029
1030 MAKE_SCHEME_CALLBACK (Grob, vertical_skylines_from_stencil, 1);
1031 SCM
1032 Grob::vertical_skylines_from_stencil (SCM smob)
1033 {
1034   Grob *me = unsmob_grob (smob);
1035
1036   Real pad = robust_scm2double (me->get_property ("skyline-horizontal-padding"), 0.0);
1037   SCM out = Stencil::skylines_from_stencil (me->get_property ("stencil"), pad, X_AXIS);
1038
1039   return out;
1040 }
1041
1042 MAKE_SCHEME_CALLBACK (Grob, horizontal_skylines_from_stencil, 1);
1043 SCM
1044 Grob::horizontal_skylines_from_stencil (SCM smob)
1045 {
1046   Grob *me = unsmob_grob (smob);
1047
1048   Real pad = robust_scm2double (me->get_property ("skyline-vertical-padding"), 0.0);
1049   SCM out = Stencil::skylines_from_stencil (me->get_property ("stencil"), pad, Y_AXIS);
1050
1051   return out;
1052 }
1053
1054 SCM
1055 Grob::internal_skylines_from_element_stencils (SCM smob, Axis a)
1056 {
1057   Grob *me = unsmob_grob (smob);
1058
1059   extract_grob_set (me, "elements", elts);
1060   vector<Real> x_pos;
1061   vector<Real> y_pos;
1062   Grob *x_common = common_refpoint_of_array (elts, me, X_AXIS);
1063   Grob *y_common = common_refpoint_of_array (elts, me, Y_AXIS);
1064   for (vsize i = 0; i < elts.size (); i++)
1065     {
1066       x_pos.push_back (elts[i]->relative_coordinate (x_common, X_AXIS));
1067       y_pos.push_back (elts[i]->relative_coordinate (y_common, Y_AXIS));
1068     }
1069   Real my_x = me->relative_coordinate (x_common, X_AXIS);
1070   Real my_y = me->relative_coordinate (y_common, Y_AXIS);
1071   Skyline_pair res;
1072   for (vsize i = 0; i < elts.size (); i++)
1073     {
1074       Skyline_pair *skyp = Skyline_pair::unsmob (elts[i]->get_property (a == X_AXIS ? "vertical-skylines" : "horizontal-skylines"));
1075       if (skyp)
1076         {
1077           /*
1078             Here, copying is essential.  Otherwise, the skyline pair will
1079             get doubly shifted!
1080           */
1081           /*
1082             It took Mike about 6 months of his life to add the `else' clause
1083             below.  For horizontal skylines, the raise and shift calls need
1084             to be reversed.  This is what was causing the problems in the
1085             shifting with all of the tests. RIP 6 months!
1086           */
1087           Skyline_pair copy = Skyline_pair (*skyp);
1088           if (a == X_AXIS)
1089             {
1090               copy.shift (x_pos[i] - my_x);
1091               copy.raise (y_pos[i] - my_y);
1092             }
1093           else
1094             {
1095               copy.raise (x_pos[i] - my_x);
1096               copy.shift (y_pos[i] - my_y);
1097             }
1098           res.merge (copy);
1099         }
1100     }
1101   return res.smobbed_copy ();
1102 }
1103
1104 MAKE_SCHEME_CALLBACK (Grob, vertical_skylines_from_element_stencils, 1);
1105 SCM
1106 Grob::vertical_skylines_from_element_stencils (SCM smob)
1107 {
1108   return internal_skylines_from_element_stencils (smob, X_AXIS);
1109 }
1110
1111 MAKE_SCHEME_CALLBACK (Grob, horizontal_skylines_from_element_stencils, 1);
1112 SCM
1113 Grob::horizontal_skylines_from_element_stencils (SCM smob)
1114 {
1115   return internal_skylines_from_element_stencils (smob, Y_AXIS);
1116 }