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