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