2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 2012 Mike Solomon <mike@mikesolomon.org>
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.
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.
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/>.
21 tools for transform-matrices following the standard at
22 http://www.w3.org/TR/SVG/coords.html
30 when this transforms a point (x,y), the point is written as matrix:
36 #include <pango/pango-matrix.h>
39 #include "dimensions.hh"
40 #include "font-metric.hh"
42 #include "interval.hh"
43 #include "freetype.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"
54 #include "string-convert.hh"
56 #include "skyline-pair.hh"
63 Real QUANTIZATION_UNIT = 0.2;
65 void create_path_cap (vector<Box> &boxes,
66 vector<Drul_array<Offset> > &buildings,
67 PangoMatrix trans, Offset pt, Real rad, Offset dir);
69 struct Transform_matrix_and_expression
74 Transform_matrix_and_expression (PangoMatrix tm, SCM expr);
77 Transform_matrix_and_expression::Transform_matrix_and_expression (PangoMatrix tm, SCM expr)
84 make_transform_matrix (Real p0, Real p1, Real p2, Real p3, Real p4, Real p5)
96 //// UTILITY FUNCTIONS
99 map x's placement between orig_l and orig_r onto
100 the interval final_l final_r
103 linear_map (Real final_l, Real final_r, Real orig_l, Real orig_r, Real x)
105 return final_l + ((final_r - final_l) * ((x - orig_l) / (orig_r - orig_l)));
109 from a nested SCM list, return the first list of numbers
113 get_number_list (SCM l)
117 if (scm_is_number (scm_car (l)))
119 SCM res = get_number_list (scm_car (l));
120 if (scm_is_false (res))
121 return get_number_list (scm_cdr (l));
128 from a nested SCM list, return the first list of numbers
132 get_path_list (SCM l)
136 if (scm_is_true (scm_memv (scm_car (l),
137 scm_list_n (ly_symbol2scm ("moveto"),
138 ly_symbol2scm ("rmoveto"),
139 ly_symbol2scm ("lineto"),
140 ly_symbol2scm ("rlineto"),
141 ly_symbol2scm ("curveto"),
142 ly_symbol2scm ("rcurveto"),
143 ly_symbol2scm ("closepath"),
146 SCM res = get_path_list (scm_car (l));
147 if (scm_is_false (res))
148 return get_path_list (scm_cdr (l));
154 // Gets an orthogonal vector with same size to orig, pointing left
155 // (in the complex domain, a multiplication by i)
158 get_normal (Offset orig)
160 return Offset (-orig[Y_AXIS], orig[X_AXIS]);
163 //// END UTILITY FUNCTIONS
166 below, for all of the functions make_X_boxes, the expression
167 is always unpacked into variables.
168 then, after a line of /////, there are manipulations of these variables
169 (there may be no manipulations necessary depending on the function)
170 afterwards, there is another ///// followed by the creation of points
175 make_draw_line_boxes (vector<Box> &boxes, vector<Drul_array<Offset> > &buildings, PangoMatrix trans, SCM expr, bool use_building)
177 Real thick = robust_scm2double (scm_car (expr), 0.0);
178 expr = scm_cdr (expr);
179 Real x0 = robust_scm2double (scm_car (expr), 0.0);
180 expr = scm_cdr (expr);
181 Real y0 = robust_scm2double (scm_car (expr), 0.0);
182 expr = scm_cdr (expr);
183 Real x1 = robust_scm2double (scm_car (expr), 0.0);
184 expr = scm_cdr (expr);
185 Real y1 = robust_scm2double (scm_car (expr), 0.0);
187 //////////////////////
193 Offset left (x0, y0);
194 Offset right (x1, y1);
195 Offset dir = (right - left).direction ();
196 for (DOWN_and_UP (d))
198 Offset outward = d * get_normal ((thick / 2) * dir);
199 Offset inter_l = left + outward;
200 Offset inter_r = right + outward;
201 pango_matrix_transform_point (&trans, &inter_l[X_AXIS], &inter_l[Y_AXIS]);
202 pango_matrix_transform_point (&trans, &inter_r[X_AXIS], &inter_r[Y_AXIS]);
203 if ((inter_l[X_AXIS] == inter_r[X_AXIS]) || (inter_l[Y_AXIS] == inter_r[Y_AXIS]))
206 b.add_point (inter_l);
207 b.add_point (inter_r);
210 else if (use_building)
211 buildings.push_back (Drul_array<Offset> (inter_l, inter_r));
214 Real length = (inter_l - inter_r).length ();
216 vsize passes = (vsize) ((length * 2) + 1);
217 vector<Offset> points;
219 for (vsize i = 0; i < 1 + passes; i++)
221 Offset pt (linear_map (x0, x1, 0, passes, i),
222 linear_map (y0, y1, 0, passes, i));
223 Offset inter = pt + outward;
224 pango_matrix_transform_point (&trans, &inter[X_AXIS], &inter[Y_AXIS]);
225 points.push_back (inter);
227 for (vsize i = 0; i < points.size () - 1; i++)
230 b.add_point (points[i]);
231 b.add_point (points[i + 1]);
240 create_path_cap (boxes,
248 create_path_cap (boxes,
258 make_partial_ellipse_boxes (vector<Box> &boxes,
259 vector<Drul_array<Offset> > &buildings,
260 PangoMatrix trans, SCM expr)
262 Real x_rad = robust_scm2double (scm_car (expr), 0.0);
263 expr = scm_cdr (expr);
264 Real y_rad = robust_scm2double (scm_car (expr), 0.0);
265 expr = scm_cdr (expr);
266 Real start = robust_scm2double (scm_car (expr), 0.0);
267 expr = scm_cdr (expr);
268 Real end = robust_scm2double (scm_car (expr), 0.0);
269 expr = scm_cdr (expr);
270 Real th = robust_scm2double (scm_car (expr), 0.0);
271 expr = scm_cdr (expr);
272 bool connect = to_boolean (scm_car (expr));
273 expr = scm_cdr (expr);
274 bool fill = to_boolean (scm_car (expr));
275 //////////////////////
276 start = M_PI * start / 180;
277 end = M_PI * end / 180;
280 Offset sp (cos (start) * x_rad, sin (start) * y_rad);
281 Offset ep (cos (end) * x_rad, sin (end) * y_rad);
282 //////////////////////
283 Drul_array<vector<Offset> > points;
284 int quantization = max (1, (int) (((x_rad * trans.xx) + (y_rad * trans.yy)) * M_PI / QUANTIZATION_UNIT));
285 for (DOWN_and_UP (d))
287 for (vsize i = 0; i < 1 + (vsize) quantization; i++)
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);
297 for (vsize i = 0; i < points[DOWN].size () - 1; i++)
300 for (DOWN_and_UP (d))
302 b.add_point (points[d][i]);
303 b.add_point (points[d][i + 1]);
310 make_draw_line_boxes (boxes, buildings, trans,
311 scm_list_5 (scm_from_double (th),
312 scm_from_double (sp[X_AXIS]),
313 scm_from_double (sp[Y_AXIS]),
314 scm_from_double (ep[X_AXIS]),
315 scm_from_double (ep[Y_AXIS])),
322 Offset pt (cos (start) * x_rad, sin (start) * y_rad);
323 create_path_cap (boxes,
331 pt = Offset (cos (end) * x_rad, sin (end) * y_rad);
332 create_path_cap (boxes,
342 make_round_filled_box_boxes (vector<Box> &boxes, PangoMatrix trans, SCM expr)
344 Real left = robust_scm2double (scm_car (expr), 0.0);
345 expr = scm_cdr (expr);
346 Real right = robust_scm2double (scm_car (expr), 0.0);
347 expr = scm_cdr (expr);
348 Real bottom = robust_scm2double (scm_car (expr), 0.0);
349 expr = scm_cdr (expr);
350 Real top = robust_scm2double (scm_car (expr), 0.0);
351 expr = scm_cdr (expr);
352 Real th = robust_scm2double (scm_car (expr), 0.0);
353 //////////////////////
354 vector<Offset> points;
356 Offset p0 = Offset (-left - (th / 2), -bottom - (th / 2));
357 Offset p1 = Offset (right + (th / 2), top + (th / 2));
358 pango_matrix_transform_point (&trans, &p0[X_AXIS], &p0[Y_AXIS]);
359 pango_matrix_transform_point (&trans, &p1[X_AXIS], &p1[Y_AXIS]);
366 create_path_cap (vector<Box> &boxes,
367 vector<Drul_array<Offset> > &buildings,
368 PangoMatrix trans, Offset pt, Real rad, Offset dir)
370 Real angle = dir.angle_degrees ();
371 PangoMatrix new_trans (trans);
372 pango_matrix_translate (&new_trans, pt[X_AXIS], pt[Y_AXIS]);
373 make_partial_ellipse_boxes (boxes, buildings, new_trans,
374 scm_list_n (scm_from_double (rad),
375 scm_from_double (rad),
376 scm_from_double (angle-90.01),
377 scm_from_double (angle+90.01),
378 scm_from_double (0.0),
385 make_draw_bezier_boxes (vector<Box> &boxes,
386 vector<Drul_array<Offset> > &buildings,
387 PangoMatrix trans, SCM expr)
389 Real th = robust_scm2double (scm_car (expr), 0.0);
390 expr = scm_cdr (expr);
391 Real x0 = robust_scm2double (scm_car (expr), 0.0);
392 expr = scm_cdr (expr);
393 Real y0 = robust_scm2double (scm_car (expr), 0.0);
394 expr = scm_cdr (expr);
395 Real x1 = robust_scm2double (scm_car (expr), 0.0);
396 expr = scm_cdr (expr);
397 Real y1 = robust_scm2double (scm_car (expr), 0.0);
398 expr = scm_cdr (expr);
399 Real x2 = robust_scm2double (scm_car (expr), 0.0);
400 expr = scm_cdr (expr);
401 Real y2 = robust_scm2double (scm_car (expr), 0.0);
402 expr = scm_cdr (expr);
403 Real x3 = robust_scm2double (scm_car (expr), 0.0);
404 expr = scm_cdr (expr);
405 Real y3 = robust_scm2double (scm_car (expr), 0.0);
406 //////////////////////
408 curve.control_[0] = Offset (x0, y0);
409 curve.control_[1] = Offset (x1, y1);
410 curve.control_[2] = Offset (x2, y2);
411 curve.control_[3] = Offset (x3, y3);
412 Offset temp0 (x0, y0);
413 Offset temp1 (x1, y1);
414 Offset temp2 (x2, y2);
415 Offset temp3 (x3, y3);
416 pango_matrix_transform_point (&trans, &temp0[X_AXIS], &temp0[Y_AXIS]);
417 pango_matrix_transform_point (&trans, &temp1[X_AXIS], &temp1[Y_AXIS]);
418 pango_matrix_transform_point (&trans, &temp2[X_AXIS], &temp2[Y_AXIS]);
419 pango_matrix_transform_point (&trans, &temp3[X_AXIS], &temp3[Y_AXIS]);
420 //////////////////////
421 Drul_array<vector<Offset> > points;
422 int quantization = int (((temp1 - temp0).length ()
423 + (temp2 - temp1).length ()
424 + (temp3 - temp2).length ())
425 / QUANTIZATION_UNIT);
426 for (DOWN_and_UP (d))
428 Offset first = curve.control_[0]
429 + d * get_normal ((th / 2) * curve.dir_at_point (0.0));
430 pango_matrix_transform_point (&trans, &first[X_AXIS], &first[Y_AXIS]);
431 points[d].push_back (first);
432 for (vsize i = 1; i < (vsize) quantization; i++)
434 Real pt = (i * 1.0) / quantization;
435 Offset inter = curve.curve_point (pt)
436 + d * get_normal ((th / 2) *curve.dir_at_point (pt));
437 pango_matrix_transform_point (&trans, &inter[X_AXIS], &inter[Y_AXIS]);
438 points[d].push_back (inter);
440 Offset last = curve.control_[3]
441 + d * get_normal ((th / 2) * curve.dir_at_point (1.0));
442 pango_matrix_transform_point (&trans, &last[X_AXIS], &last[Y_AXIS]);
443 points[d].push_back (last);
446 for (vsize i = 0; i < points[DOWN].size () - 1; i++)
449 for (DOWN_and_UP (d))
451 b.add_point (points[d][i]);
452 b.add_point (points[d][i + 1]);
460 create_path_cap (boxes,
465 -curve.dir_at_point (0.0));
468 create_path_cap (boxes,
473 curve.dir_at_point (1.0));
478 converts a path into lists of 4 (line) or 8 (curve) absolute coordinates
480 '(moveto 1 2 lineto 3 4 rlineto -1 -1 curveto
481 3 3 5 5 6 6 rcurveto -1 -1 -1 -1 -1 -1 closepath)
491 all_commands_to_absolute_and_group (SCM expr)
495 Offset current (0, 0);
497 while (scm_is_pair (expr))
499 if (scm_is_eq (scm_car (expr), ly_symbol2scm ("moveto"))
500 || (scm_is_eq (scm_car (expr), ly_symbol2scm ("rmoveto")) && first))
502 Real x = robust_scm2double (scm_cadr (expr), 0.0);
503 Real y = robust_scm2double (scm_caddr (expr), 0.0);
504 start = Offset (x, y);
506 expr = scm_cdddr (expr);
508 if (scm_is_eq (scm_car (expr), ly_symbol2scm ("rmoveto")))
510 Real x = robust_scm2double (scm_cadr (expr), 0.0);
511 Real y = robust_scm2double (scm_caddr (expr), 0.0);
512 start = (Offset (x, y) + current);
514 expr = scm_cdddr (expr);
516 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("lineto")))
518 Real x = robust_scm2double (scm_cadr (expr), 0.0);
519 Real y = robust_scm2double (scm_caddr (expr), 0.0);
520 out = scm_cons (scm_list_4 (scm_from_double (current[X_AXIS]),
521 scm_from_double (current[Y_AXIS]),
523 scm_from_double (y)),
525 current = Offset (x, y);
526 expr = scm_cdddr (expr);
528 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("rlineto")))
530 Real x = robust_scm2double (scm_cadr (expr), 0.0);
531 Real y = robust_scm2double (scm_caddr (expr), 0.0);
532 out = scm_cons (scm_list_4 (scm_from_double (current[X_AXIS]),
533 scm_from_double (current[Y_AXIS]),
534 scm_from_double (x + current[X_AXIS]),
535 scm_from_double (y + current[Y_AXIS])),
537 current = (Offset (x, y) + current);
538 expr = scm_cdddr (expr);
540 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("curveto")))
542 Real x1 = robust_scm2double (scm_cadr (expr), 0.0);
543 expr = scm_cddr (expr);
544 Real y1 = robust_scm2double (scm_car (expr), 0.0);
545 expr = scm_cdr (expr);
546 Real x2 = robust_scm2double (scm_car (expr), 0.0);
547 expr = scm_cdr (expr);
548 Real y2 = robust_scm2double (scm_car (expr), 0.0);
549 expr = scm_cdr (expr);
550 Real x3 = robust_scm2double (scm_car (expr), 0.0);
551 expr = scm_cdr (expr);
552 Real y3 = robust_scm2double (scm_car (expr), 0.0);
553 expr = scm_cdr (expr);
554 out = scm_cons (scm_list_n (scm_from_double (current[X_AXIS]),
555 scm_from_double (current[Y_AXIS]),
556 scm_from_double (x1),
557 scm_from_double (y1),
558 scm_from_double (x2),
559 scm_from_double (y2),
560 scm_from_double (x3),
561 scm_from_double (y3),
564 current = Offset (x3, y3);
566 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("rcurveto")))
568 Real x1 = robust_scm2double (scm_cadr (expr), 0.0);
569 expr = scm_cddr (expr);
570 Real y1 = robust_scm2double (scm_car (expr), 0.0);
571 expr = scm_cdr (expr);
572 Real x2 = robust_scm2double (scm_car (expr), 0.0);
573 expr = scm_cdr (expr);
574 Real y2 = robust_scm2double (scm_car (expr), 0.0);
575 expr = scm_cdr (expr);
576 Real x3 = robust_scm2double (scm_car (expr), 0.0);
577 expr = scm_cdr (expr);
578 Real y3 = robust_scm2double (scm_car (expr), 0.0);
579 expr = scm_cdr (expr);
580 out = scm_cons (scm_list_n (scm_from_double (current[X_AXIS]),
581 scm_from_double (current[Y_AXIS]),
582 scm_from_double (x1 + current[X_AXIS]),
583 scm_from_double (y1 + current[Y_AXIS]),
584 scm_from_double (x2 + current[X_AXIS]),
585 scm_from_double (y2 + current[Y_AXIS]),
586 scm_from_double (x3 + current[X_AXIS]),
587 scm_from_double (y3 + current[Y_AXIS]),
590 current = (Offset (x3, y3) + current);
592 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("closepath")))
594 if ((current[X_AXIS] != start[X_AXIS])
595 || (current[Y_AXIS] != start[Y_AXIS]))
597 out = scm_cons (scm_list_4 (scm_from_double (current[X_AXIS]),
598 scm_from_double (current[Y_AXIS]),
599 scm_from_double (start[X_AXIS]),
600 scm_from_double (start[Y_AXIS])),
604 expr = scm_cdr (expr);
608 warning ("Malformed path for path stencil.");
613 return scm_reverse_x (out, SCM_EOL);
617 internal_make_path_boxes (vector<Box> &boxes,
618 vector<Drul_array<Offset> > &buildings,
619 PangoMatrix trans, SCM expr, bool use_building)
621 SCM blot = scm_car (expr);
622 expr = scm_cdr (expr);
623 SCM path = all_commands_to_absolute_and_group (expr);
624 // note that expr has more stuff that we don't need after this - simply ignore it
625 //////////////////////
626 for (SCM s = path; scm_is_pair (s); s = scm_cdr (s))
628 scm_to_int (scm_length (scm_car (s))) == 4
629 ? make_draw_line_boxes (boxes, buildings, trans, scm_cons (blot, scm_car (s)), use_building)
630 : make_draw_bezier_boxes (boxes, buildings, trans, scm_cons (blot, scm_car (s)));
635 make_path_boxes (vector<Box> &boxes,
636 vector<Drul_array<Offset> > &buildings,
637 PangoMatrix trans, SCM expr)
639 return internal_make_path_boxes (boxes, buildings, trans, scm_cons (scm_car (expr), get_path_list (scm_cdr (expr))), false);
643 make_polygon_boxes (vector<Box> &boxes,
644 vector<Drul_array<Offset> > &buildings,
645 PangoMatrix trans, SCM expr)
647 SCM coords = get_number_list (scm_car (expr));
648 expr = scm_cdr (expr);
649 SCM blot_diameter = scm_car (expr);
650 //////////////////////
653 for (SCM s = coords; scm_is_pair (s); s = scm_cddr (s))
655 l = scm_cons (first ? ly_symbol2scm ("moveto") : ly_symbol2scm ("lineto"), l);
656 l = scm_cons (scm_car (s), l);
657 l = scm_cons (scm_cadr (s), l);
660 l = scm_cons (ly_symbol2scm ("closepath"), l);
661 internal_make_path_boxes (boxes, buildings, trans,
662 scm_cons (blot_diameter, scm_reverse_x (l, SCM_EOL)), true);
666 make_named_glyph_boxes (vector<Box> &boxes,
667 vector<Drul_array<Offset> > &buildings,
668 PangoMatrix trans, SCM expr)
670 SCM fm_scm = scm_car (expr);
671 Font_metric *fm = unsmob<Font_metric> (fm_scm);
672 expr = scm_cdr (expr);
673 SCM glyph = scm_car (expr);
674 string glyph_s = ly_scm2string (glyph);
676 //////////////////////
677 Open_type_font *open_fm
678 = dynamic_cast<Open_type_font *>
679 (dynamic_cast<Modified_font_metric *>(fm)->original_font ());
680 SCM_ASSERT_TYPE (open_fm, fm_scm, SCM_ARG1, __FUNCTION__, "OpenType font");
682 size_t gidx = open_fm->name_to_index (glyph_s);
683 // Bbox is the best approximation of the width based on how it would be
684 // calculated in open-type-font.cc if it were based on real extents
685 Box bbox = open_fm->get_unscaled_indexed_char_dimensions (gidx);
686 bbox.scale (dynamic_cast<Modified_font_metric *> (fm)->get_magnification ()
687 * open_fm->design_size () / open_fm->get_units_per_EM ());
688 // Real bbox is the real bbox of the object
689 Box real_bbox = open_fm->get_glyph_outline_bbox (gidx);
691 Real scale = bbox[X_AXIS].length () / real_bbox[X_AXIS].length ();
693 pango_matrix_scale (&trans, scale, scale);
695 SCM outline = open_fm->get_glyph_outline (gidx);
696 //////////////////////
697 for (SCM s = outline;
701 scm_to_int (scm_length (scm_car (s))) == 4
702 ? make_draw_line_boxes (boxes, buildings, trans,
703 scm_cons (scm_from_double (0), scm_car (s)),
705 : make_draw_bezier_boxes (boxes, buildings, trans,
706 scm_cons (scm_from_double (0), scm_car (s)));
711 make_glyph_string_boxes (vector<Box> &boxes,
712 vector<Drul_array<Offset> > &buildings,
713 PangoMatrix trans, SCM expr)
715 SCM fm_scm = scm_car (expr);
716 Font_metric *fm = unsmob<Font_metric> (fm_scm);
717 expr = scm_cdr (expr);
718 expr = scm_cdr (expr); // font-name
719 expr = scm_cdr (expr); // size
720 expr = scm_cdr (expr); // cid?
721 SCM whxy = scm_cadar (expr);
723 vector<Interval> heights;
726 vector<string> char_ids;
727 //////////////////////
728 Pango_font *pango_fm = dynamic_cast<Pango_font *> (fm);
729 SCM_ASSERT_TYPE (pango_fm, fm_scm, SCM_ARG1, __FUNCTION__, "Pango font");
731 for (SCM s = whxy; scm_is_pair (s); s = scm_cdr (s))
733 SCM now = scm_car (s);
734 widths.push_back (robust_scm2double (scm_car (now), 0.0));
736 heights.push_back (robust_scm2interval (scm_car (now), Interval (0, 0)));
738 xos.push_back (robust_scm2double (scm_car (now), 0.0));
740 yos.push_back (robust_scm2double (scm_car (now), 0.0));
742 char_ids.push_back (robust_scm2string (scm_car (now), ""));
744 Real cumulative_x = 0.0;
745 for (vsize i = 0; i < widths.size (); i++)
747 PangoMatrix transcopy (trans);
748 Offset pt0 (cumulative_x + xos[i], heights[i][DOWN] + yos[i]);
749 Offset pt1 (cumulative_x + widths[i] + xos[i], heights[i][UP] + yos[i]);
750 cumulative_x += widths[i];
753 kerned_bbox.add_point (pt0);
754 kerned_bbox.add_point (pt1);
755 size_t gidx = pango_fm->name_to_index (char_ids[i]);
756 Box real_bbox = pango_fm->get_scaled_indexed_char_dimensions (gidx);
757 Box bbox = pango_fm->get_unscaled_indexed_char_dimensions (gidx);
758 SCM outline = pango_fm->get_glyph_outline (gidx);
760 // scales may have rounding error but should be close
761 Real xlen = real_bbox[X_AXIS].length () / bbox[X_AXIS].length ();
762 Real ylen = real_bbox[Y_AXIS].length () / bbox[Y_AXIS].length ();
767 The value will be nan for whitespace, in which case we just want
768 filler, so the kerned bbox is ok.
770 However, if the value is inf, this likely means that LilyPond is
771 using a font that is currently difficult to get the measurements
772 from the Pango_font. This should eventually be fixed. The solution
773 for now is just to use the bounding box.
775 if (isnan (xlen) || isnan (ylen) || isinf (xlen) || isinf (ylen))
776 outline = box_to_scheme_lines (kerned_bbox);
779 assert (abs (xlen - ylen) < 10e-3);
781 Real scale_factor = max (xlen, ylen);
782 // the three operations below move the stencil from its original coordinates to current coordinates
783 pango_matrix_translate (&transcopy, kerned_bbox[X_AXIS][LEFT],
784 kerned_bbox[Y_AXIS][DOWN] - real_bbox[Y_AXIS][DOWN]);
785 pango_matrix_translate (&transcopy, real_bbox[X_AXIS][LEFT],
786 real_bbox[Y_AXIS][DOWN]);
787 pango_matrix_scale (&transcopy, scale_factor, scale_factor);
788 pango_matrix_translate (&transcopy, -bbox[X_AXIS][LEFT],
789 -bbox[Y_AXIS][DOWN]);
791 //////////////////////
792 for (SCM s = outline;
796 scm_to_int (scm_length (scm_car (s))) == 4
797 ? make_draw_line_boxes (boxes, buildings, transcopy,
798 scm_cons (scm_from_double (0), scm_car (s)),
800 : make_draw_bezier_boxes (boxes, buildings, transcopy,
801 scm_cons (scm_from_double (0), scm_car (s)));
807 receives a stencil expression and a transform matrix
808 depending on the stencil name, dispatches it to the appropriate function
812 stencil_dispatcher (vector<Box> &boxes,
813 vector<Drul_array<Offset> > &buildings,
814 PangoMatrix trans, SCM expr)
816 if (not scm_is_pair (expr))
818 if (scm_is_eq (scm_car (expr), ly_symbol2scm ("draw-line")))
819 make_draw_line_boxes (boxes, buildings, trans, scm_cdr (expr), true);
820 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("dashed-line")))
822 expr = scm_cdr (expr);
823 SCM th = scm_car (expr);
824 expr = scm_cdr (expr);
825 expr = scm_cdr (expr); // on
826 expr = scm_cdr (expr); // off
827 SCM x1 = scm_car (expr);
828 expr = scm_cdr (expr);
829 SCM x2 = scm_car (expr);
830 make_draw_line_boxes (boxes, buildings, trans,
831 scm_list_5 (th, scm_from_double (0.0),
832 scm_from_double (0.0), x1, x2), true);
834 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("circle")))
836 expr = scm_cdr (expr);
837 SCM rad = scm_car (expr);
838 expr = scm_cdr (expr);
839 SCM th = scm_car (expr);
840 make_partial_ellipse_boxes (boxes, buildings, trans,
843 scm_from_double (0.0),
844 scm_from_double (360.0),
850 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("ellipse")))
852 expr = scm_cdr (expr);
853 SCM x_rad = scm_car (expr);
854 expr = scm_cdr (expr);
855 SCM y_rad = scm_car (expr);
856 expr = scm_cdr (expr);
857 SCM th = scm_car (expr);
858 make_partial_ellipse_boxes (boxes, buildings, trans,
861 scm_from_double (0.0),
862 scm_from_double (360.0),
868 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("partial-ellipse")))
869 make_partial_ellipse_boxes (boxes, buildings, trans, scm_cdr (expr));
870 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("round-filled-box")))
871 make_round_filled_box_boxes (boxes, trans, scm_cdr (expr));
872 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("named-glyph")))
873 make_named_glyph_boxes (boxes, buildings, trans, scm_cdr (expr));
874 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("polygon")))
875 make_polygon_boxes (boxes, buildings, trans, scm_cdr (expr));
876 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("path")))
877 make_path_boxes (boxes, buildings, trans, scm_cdr (expr));
878 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("glyph-string")))
879 make_glyph_string_boxes (boxes, buildings, trans, scm_cdr (expr));
883 warning ("Stencil expression not supported by the veritcal skylines.");
886 We don't issue a warning here, as we assume that stencil-expression.cc
887 is doing stencil-checking correctly.
893 traverses a stencil expression, returning a vector of Transform_matrix_and_expression
894 the struct Transform_matrix_and_expression contains two members,
895 a Transform_matrix that indicates where to move a stencil and the stencil expression
896 to show how to construct the stencil
898 vector<Transform_matrix_and_expression>
899 stencil_traverser (PangoMatrix trans, SCM expr)
901 if (scm_is_null (expr))
902 return vector<Transform_matrix_and_expression> ();
903 else if (scm_is_eq (expr, ly_string2scm ("")))
904 return vector<Transform_matrix_and_expression> ();
905 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("combine-stencil")))
907 vector<Transform_matrix_and_expression> out;
908 for (SCM s = scm_cdr (expr); scm_is_pair (s); s = scm_cdr (s))
910 vector<Transform_matrix_and_expression> res =
911 stencil_traverser (trans, scm_car (s));
912 out.insert (out.end (), res.begin (), res.end ());
916 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("footnote")))
917 return vector<Transform_matrix_and_expression> ();
918 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("translate-stencil")))
920 Real x = robust_scm2double (scm_caadr (expr), 0.0);
921 Real y = robust_scm2double (scm_cdadr (expr), 0.0);
922 pango_matrix_translate (&trans, x, y);
923 return stencil_traverser (trans, scm_caddr (expr));
925 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("scale-stencil")))
927 Real x = robust_scm2double (scm_caadr (expr), 0.0);
928 Real y = robust_scm2double (scm_cadadr (expr), 0.0);
929 pango_matrix_scale (&trans, x, y);
930 return stencil_traverser (trans, scm_caddr (expr));
932 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("rotate-stencil")))
934 Real ang = robust_scm2double (scm_caadr (expr), 0.0);
935 Real x = robust_scm2double (scm_car (scm_cadadr (expr)), 0.0);
936 Real y = robust_scm2double (scm_cdr (scm_cadadr (expr)), 0.0);
937 pango_matrix_translate (&trans, x, y);
938 pango_matrix_rotate (&trans, -ang);
939 pango_matrix_translate (&trans, -x, -y);
940 return stencil_traverser (trans, scm_caddr (expr));
942 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("delay-stencil-evaluation")))
943 // should not use the place-holder text, but no need for the warning below
944 return vector<Transform_matrix_and_expression> ();
945 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("grob-cause")))
946 return stencil_traverser (trans, scm_caddr (expr));
947 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("color")))
948 return stencil_traverser (trans, scm_caddr (expr));
949 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("transparent-stencil")))
950 return stencil_traverser (trans, scm_cadr (expr));
951 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("id")))
952 return stencil_traverser (trans, scm_caddr (expr));
955 vector<Transform_matrix_and_expression> out;
956 out.push_back (Transform_matrix_and_expression (trans, expr));
959 warning ("Stencil expression not supported by the veritcal skylines.");
960 return vector<Transform_matrix_and_expression> ();
964 Grob::maybe_pure_internal_simple_skylines_from_extents (Grob *me, Axis a, bool pure, int beg, int end, bool ignore_x, bool ignore_y)
967 // we don't know how far spanners stretch along the X axis before
968 // line breaking. better have them take up the whole thing
969 Interval xex = ignore_x
970 ? Interval (-infinity_f, infinity_f)
971 : me->extent (me, X_AXIS);
973 // If we're looking at the x exent of a cross staff grob, it could be
974 // very early on in the computation process. We won't know its height
975 // until way later, so we give a brute force approximation.
976 Interval yex = ignore_y
977 ? Interval (-infinity_f, infinity_f)
978 : me->maybe_pure_extent (me, Y_AXIS, pure, beg, end);
980 if (xex.is_empty () || yex.is_empty ())
981 return Skyline_pair ().smobbed_copy ();
983 boxes.push_back (Box (xex, yex));
984 return Skyline_pair (boxes, a).smobbed_copy ();
987 MAKE_SCHEME_CALLBACK (Grob, pure_simple_vertical_skylines_from_extents, 3);
989 Grob::pure_simple_vertical_skylines_from_extents (SCM smob, SCM begscm, SCM endscm)
991 Grob *me = unsmob<Grob> (smob);
992 int beg = robust_scm2int (begscm, 0);
993 int end = robust_scm2int (endscm, INT_MAX);
994 // We cannot measure the widths before line breaking,
995 // so we assume that the width is infinite: pass ignore_x=true
996 return maybe_pure_internal_simple_skylines_from_extents (me, X_AXIS, true, beg, end, true, false);
999 MAKE_SCHEME_CALLBACK (Grob, simple_vertical_skylines_from_extents, 1);
1001 Grob::simple_vertical_skylines_from_extents (SCM smob)
1003 Grob *me = unsmob<Grob> (smob);
1004 return maybe_pure_internal_simple_skylines_from_extents (me, X_AXIS, false, 0, 0, false, false);
1007 MAKE_SCHEME_CALLBACK (Grob, pure_simple_horizontal_skylines_from_extents, 3);
1009 Grob::pure_simple_horizontal_skylines_from_extents (SCM smob, SCM begscm, SCM endscm)
1011 Grob *me = unsmob<Grob> (smob);
1012 int beg = robust_scm2int (begscm, 0);
1013 int end = robust_scm2int (endscm, INT_MAX);
1014 // If the grob is cross staff, we cannot measure its Y-extent before
1015 // wayyyy downstream (after spacing of axis groups is done).
1016 // Thus, we assume that the Y extent is infinite for cross staff grobs.
1017 return maybe_pure_internal_simple_skylines_from_extents (me, Y_AXIS, true, beg, end, false, to_boolean (me->get_property ("cross-staff")));
1020 MAKE_SCHEME_CALLBACK (Grob, simple_horizontal_skylines_from_extents, 1);
1022 Grob::simple_horizontal_skylines_from_extents (SCM smob)
1024 Grob *me = unsmob<Grob> (smob);
1025 // See comment in function above.
1026 return maybe_pure_internal_simple_skylines_from_extents (me, Y_AXIS, false, 0, 0, false, to_boolean (me->get_property ("cross-staff")));
1030 Stencil::skylines_from_stencil (SCM sten, Real pad, Axis a)
1032 Stencil *s = unsmob<Stencil> (sten);
1034 return Skyline_pair ().smobbed_copy ();
1036 vector<Transform_matrix_and_expression> data
1037 = stencil_traverser (make_transform_matrix (1.0, 0.0, 0.0, 1.0, 0.0, 0.0),
1040 vector<Drul_array<Offset> > buildings;
1041 for (vsize i = 0; i < data.size (); i++)
1042 stencil_dispatcher (boxes, buildings, data[i].tm_, data[i].expr_);
1044 // we use the bounding box if there are no boxes
1045 if (!boxes.size () && !buildings.size ())
1046 boxes.push_back (Box (s->extent (X_AXIS), s->extent (Y_AXIS)));
1048 Skyline_pair out (boxes, a);
1049 out.merge (Skyline_pair (buildings, a));
1051 for (DOWN_and_UP (d))
1052 out[d] = out[d].padded (pad);
1054 return out.smobbed_copy ();
1057 MAKE_SCHEME_CALLBACK (Grob, vertical_skylines_from_stencil, 1);
1059 Grob::vertical_skylines_from_stencil (SCM smob)
1061 Grob *me = unsmob<Grob> (smob);
1063 Real pad = robust_scm2double (me->get_property ("skyline-horizontal-padding"), 0.0);
1064 SCM out = Stencil::skylines_from_stencil (me->get_property ("stencil"), pad, X_AXIS);
1069 MAKE_SCHEME_CALLBACK (Grob, horizontal_skylines_from_stencil, 1);
1071 Grob::horizontal_skylines_from_stencil (SCM smob)
1073 Grob *me = unsmob<Grob> (smob);
1075 Real pad = robust_scm2double (me->get_property ("skyline-vertical-padding"), 0.0);
1076 SCM out = Stencil::skylines_from_stencil (me->get_property ("stencil"), pad, Y_AXIS);
1082 Grob::internal_skylines_from_element_stencils (Grob *me, Axis a, bool pure, int beg, int end)
1085 extract_grob_set (me, "elements", elts);
1088 Grob *x_common = common_refpoint_of_array (elts, me, X_AXIS);
1089 Grob *y_common = common_refpoint_of_array (elts, me, Y_AXIS);
1090 for (vsize i = 0; i < elts.size (); i++)
1092 x_pos.push_back (elts[i]->relative_coordinate (x_common, X_AXIS));
1093 y_pos.push_back (elts[i]->maybe_pure_coordinate (y_common, Y_AXIS, pure, beg, end));
1095 Real my_x = me->relative_coordinate (x_common, X_AXIS);
1096 Real my_y = me->maybe_pure_coordinate (y_common, Y_AXIS, pure, beg, end);
1099 for (vsize i = 0; i < elts.size (); i++)
1101 Skyline_pair *skyp = unsmob<Skyline_pair> (elts[i]->get_maybe_pure_property (a == X_AXIS ? "vertical-skylines" : "horizontal-skylines", pure, beg, end));
1105 Here, copying is essential. Otherwise, the skyline pair will
1109 It took Mike about 6 months of his life to add the `else' clause
1110 below. For horizontal skylines, the raise and shift calls need
1111 to be reversed. This is what was causing the problems in the
1112 shifting with all of the tests. RIP 6 months!
1114 Skyline_pair copy = Skyline_pair (*skyp);
1117 copy.shift (x_pos[i] - my_x);
1118 copy.raise (y_pos[i] - my_y);
1122 copy.raise (x_pos[i] - my_x);
1123 copy.shift (y_pos[i] - my_y);
1128 return res.smobbed_copy ();
1131 MAKE_SCHEME_CALLBACK (Grob, vertical_skylines_from_element_stencils, 1);
1133 Grob::vertical_skylines_from_element_stencils (SCM smob)
1135 Grob *me = unsmob<Grob> (smob);
1136 return internal_skylines_from_element_stencils (me, X_AXIS, false, 0, INT_MAX);
1139 MAKE_SCHEME_CALLBACK (Grob, horizontal_skylines_from_element_stencils, 1);
1141 Grob::horizontal_skylines_from_element_stencils (SCM smob)
1143 Grob *me = unsmob<Grob> (smob);
1144 return internal_skylines_from_element_stencils (me, Y_AXIS, false, 0, INT_MAX);
1147 MAKE_SCHEME_CALLBACK (Grob, pure_vertical_skylines_from_element_stencils, 3);
1149 Grob::pure_vertical_skylines_from_element_stencils (SCM smob, SCM beg_scm, SCM end_scm)
1151 Grob *me = unsmob<Grob> (smob);
1152 int beg = robust_scm2int (beg_scm, 0);
1153 int end = robust_scm2int (end_scm, 0);
1154 return internal_skylines_from_element_stencils (me, X_AXIS, true, beg, end);
1157 MAKE_SCHEME_CALLBACK (Grob, pure_horizontal_skylines_from_element_stencils, 3);
1159 Grob::pure_horizontal_skylines_from_element_stencils (SCM smob, SCM beg_scm, SCM end_scm)
1161 Grob *me = unsmob<Grob> (smob);
1162 int beg = robust_scm2int (beg_scm, 0);
1163 int end = robust_scm2int (end_scm, 0);
1164 return internal_skylines_from_element_stencils (me, Y_AXIS, true, beg, end);