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