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"
60 Real QUANTIZATION_UNIT = 0.2;
62 void create_path_cap (vector<Box> &boxes,
63 vector<Drul_array<Offset> > &buildings,
64 PangoMatrix trans, Offset pt, Real rad, Offset dir);
66 struct Transform_matrix_and_expression
71 Transform_matrix_and_expression (PangoMatrix tm, SCM expr);
74 Transform_matrix_and_expression::Transform_matrix_and_expression (PangoMatrix tm, SCM expr)
81 make_transform_matrix (Real p0, Real p1, Real p2, Real p3, Real p4, Real p5)
93 //// UTILITY FUNCTIONS
96 map x's placement between orig_l and orig_r onto
97 the interval final_l final_r
100 linear_map (Real final_l, Real final_r, Real orig_l, Real orig_r, Real x)
102 return final_l + ((final_r - final_l) * ((x - orig_l) / (orig_r - orig_l)));
106 from a nested SCM list, return the first list of numbers
110 get_number_list (SCM l)
114 if (scm_is_number (scm_car (l)))
116 SCM res = get_number_list (scm_car (l));
117 if (scm_is_false (res))
118 return get_number_list (scm_cdr (l));
125 from a nested SCM list, return the first list of numbers
129 get_path_list (SCM l)
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"),
143 SCM res = get_path_list (scm_car (l));
144 if (scm_is_false (res))
145 return get_path_list (scm_cdr (l));
151 // Gets an orthogonal vector with same size to orig, pointing left
152 // (in the complex domain, a multiplication by i)
155 get_normal (Offset orig)
157 return Offset (-orig[Y_AXIS], orig[X_AXIS]);
160 //// END UTILITY FUNCTIONS
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
172 make_draw_line_boxes (vector<Box> &boxes, vector<Drul_array<Offset> > &buildings, PangoMatrix trans, SCM expr, bool use_building)
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);
184 //////////////////////
190 Offset left (x0, y0);
191 Offset right (x1, y1);
192 Offset dir = (right - left).direction ();
193 for (DOWN_and_UP (d))
195 Offset outward = d * get_normal ((thick / 2) * dir);
196 Offset inter_l = left + outward;
197 Offset inter_r = right + outward;
198 pango_matrix_transform_point (&trans, &inter_l[X_AXIS], &inter_l[Y_AXIS]);
199 pango_matrix_transform_point (&trans, &inter_r[X_AXIS], &inter_r[Y_AXIS]);
200 if ((inter_l[X_AXIS] == inter_r[X_AXIS]) || (inter_l[Y_AXIS] == inter_r[Y_AXIS]))
203 b.add_point (inter_l);
204 b.add_point (inter_r);
207 else if (use_building)
208 buildings.push_back (Drul_array<Offset> (inter_l, inter_r));
211 Real length = (inter_l - inter_r).length ();
213 vsize passes = (vsize) ((length * 2) + 1);
214 vector<Offset> points;
216 for (vsize i = 0; i < 1 + passes; i++)
218 Offset pt (linear_map (x0, x1, 0, passes, i),
219 linear_map (y0, y1, 0, passes, i));
220 Offset inter = pt + outward;
221 pango_matrix_transform_point (&trans, &inter[X_AXIS], &inter[Y_AXIS]);
222 points.push_back (inter);
224 for (vsize i = 0; i < points.size () - 1; i++)
227 b.add_point (points[i]);
228 b.add_point (points[i + 1]);
237 create_path_cap (boxes,
245 create_path_cap (boxes,
255 make_partial_ellipse_boxes (vector<Box> &boxes,
256 vector<Drul_array<Offset> > &buildings,
257 PangoMatrix trans, SCM expr)
259 Real x_rad = robust_scm2double (scm_car (expr), 0.0);
260 expr = scm_cdr (expr);
261 Real y_rad = robust_scm2double (scm_car (expr), 0.0);
262 expr = scm_cdr (expr);
263 Offset rad (x_rad, y_rad);
264 Real start = robust_scm2double (scm_car (expr), 0.0);
265 expr = scm_cdr (expr);
266 Real end = robust_scm2double (scm_car (expr), 0.0);
267 expr = scm_cdr (expr);
268 Real th = robust_scm2double (scm_car (expr), 0.0);
269 expr = scm_cdr (expr);
270 bool connect = to_boolean (scm_car (expr));
271 expr = scm_cdr (expr);
272 bool fill = to_boolean (scm_car (expr));
273 //////////////////////
276 Offset sp (offset_directed (start).scale (rad));
277 Offset ep (offset_directed (end).scale (rad));
278 //////////////////////
279 Drul_array<vector<Offset> > points;
280 int quantization = max (1, (int) (((x_rad * trans.xx) + (y_rad * trans.yy)) * M_PI / QUANTIZATION_UNIT));
281 for (DOWN_and_UP (d))
283 for (vsize i = 0; i < 1 + (vsize) quantization; i++)
285 Real ang = linear_map (start, end, 0, quantization, i);
286 Offset pt (offset_directed (ang).scale (rad));
287 Offset inter = pt + d * get_normal ((th/2) * pt.direction ());
288 pango_matrix_transform_point (&trans, &inter[X_AXIS], &inter[Y_AXIS]);
289 points[d].push_back (inter);
293 for (vsize i = 0; i < points[DOWN].size () - 1; i++)
296 for (DOWN_and_UP (d))
298 b.add_point (points[d][i]);
299 b.add_point (points[d][i + 1]);
306 make_draw_line_boxes (boxes, buildings, trans,
307 scm_list_5 (scm_from_double (th),
308 scm_from_double (sp[X_AXIS]),
309 scm_from_double (sp[Y_AXIS]),
310 scm_from_double (ep[X_AXIS]),
311 scm_from_double (ep[Y_AXIS])),
318 Offset pt (offset_directed (start).scale (rad));
319 create_path_cap (boxes,
327 pt = offset_directed (end).scale (rad);
328 create_path_cap (boxes,
338 make_round_filled_box_boxes (vector<Box> &boxes, PangoMatrix trans, SCM expr)
340 Real left = robust_scm2double (scm_car (expr), 0.0);
341 expr = scm_cdr (expr);
342 Real right = robust_scm2double (scm_car (expr), 0.0);
343 expr = scm_cdr (expr);
344 Real bottom = robust_scm2double (scm_car (expr), 0.0);
345 expr = scm_cdr (expr);
346 Real top = robust_scm2double (scm_car (expr), 0.0);
347 expr = scm_cdr (expr);
348 Real th = robust_scm2double (scm_car (expr), 0.0);
349 //////////////////////
350 vector<Offset> points;
352 Offset p0 = Offset (-left - (th / 2), -bottom - (th / 2));
353 Offset p1 = Offset (right + (th / 2), top + (th / 2));
354 pango_matrix_transform_point (&trans, &p0[X_AXIS], &p0[Y_AXIS]);
355 pango_matrix_transform_point (&trans, &p1[X_AXIS], &p1[Y_AXIS]);
362 create_path_cap (vector<Box> &boxes,
363 vector<Drul_array<Offset> > &buildings,
364 PangoMatrix trans, Offset pt, Real rad, Offset dir)
366 Real angle = dir.angle_degrees ();
367 PangoMatrix new_trans (trans);
368 pango_matrix_translate (&new_trans, pt[X_AXIS], pt[Y_AXIS]);
369 make_partial_ellipse_boxes (boxes, buildings, new_trans,
370 scm_list_n (scm_from_double (rad),
371 scm_from_double (rad),
372 scm_from_double (angle-90.01),
373 scm_from_double (angle+90.01),
374 scm_from_double (0.0),
381 make_draw_bezier_boxes (vector<Box> &boxes,
382 vector<Drul_array<Offset> > &buildings,
383 PangoMatrix trans, SCM expr)
385 Real th = robust_scm2double (scm_car (expr), 0.0);
386 expr = scm_cdr (expr);
387 Real x0 = robust_scm2double (scm_car (expr), 0.0);
388 expr = scm_cdr (expr);
389 Real y0 = robust_scm2double (scm_car (expr), 0.0);
390 expr = scm_cdr (expr);
391 Real x1 = robust_scm2double (scm_car (expr), 0.0);
392 expr = scm_cdr (expr);
393 Real y1 = robust_scm2double (scm_car (expr), 0.0);
394 expr = scm_cdr (expr);
395 Real x2 = robust_scm2double (scm_car (expr), 0.0);
396 expr = scm_cdr (expr);
397 Real y2 = robust_scm2double (scm_car (expr), 0.0);
398 expr = scm_cdr (expr);
399 Real x3 = robust_scm2double (scm_car (expr), 0.0);
400 expr = scm_cdr (expr);
401 Real y3 = robust_scm2double (scm_car (expr), 0.0);
402 //////////////////////
404 curve.control_[0] = Offset (x0, y0);
405 curve.control_[1] = Offset (x1, y1);
406 curve.control_[2] = Offset (x2, y2);
407 curve.control_[3] = Offset (x3, y3);
408 Offset temp0 (x0, y0);
409 Offset temp1 (x1, y1);
410 Offset temp2 (x2, y2);
411 Offset temp3 (x3, y3);
412 pango_matrix_transform_point (&trans, &temp0[X_AXIS], &temp0[Y_AXIS]);
413 pango_matrix_transform_point (&trans, &temp1[X_AXIS], &temp1[Y_AXIS]);
414 pango_matrix_transform_point (&trans, &temp2[X_AXIS], &temp2[Y_AXIS]);
415 pango_matrix_transform_point (&trans, &temp3[X_AXIS], &temp3[Y_AXIS]);
416 //////////////////////
417 Drul_array<vector<Offset> > points;
418 int quantization = int (((temp1 - temp0).length ()
419 + (temp2 - temp1).length ()
420 + (temp3 - temp2).length ())
421 / QUANTIZATION_UNIT);
422 for (DOWN_and_UP (d))
424 Offset first = curve.control_[0]
425 + d * get_normal ((th / 2) * curve.dir_at_point (0.0));
426 pango_matrix_transform_point (&trans, &first[X_AXIS], &first[Y_AXIS]);
427 points[d].push_back (first);
428 for (vsize i = 1; i < (vsize) quantization; i++)
430 Real pt = (i * 1.0) / quantization;
431 Offset inter = curve.curve_point (pt)
432 + d * get_normal ((th / 2) *curve.dir_at_point (pt));
433 pango_matrix_transform_point (&trans, &inter[X_AXIS], &inter[Y_AXIS]);
434 points[d].push_back (inter);
436 Offset last = curve.control_[3]
437 + d * get_normal ((th / 2) * curve.dir_at_point (1.0));
438 pango_matrix_transform_point (&trans, &last[X_AXIS], &last[Y_AXIS]);
439 points[d].push_back (last);
442 for (vsize i = 0; i < points[DOWN].size () - 1; i++)
445 for (DOWN_and_UP (d))
447 b.add_point (points[d][i]);
448 b.add_point (points[d][i + 1]);
456 create_path_cap (boxes,
461 -curve.dir_at_point (0.0));
464 create_path_cap (boxes,
469 curve.dir_at_point (1.0));
474 converts a path into lists of 4 (line) or 8 (curve) absolute coordinates
476 '(moveto 1 2 lineto 3 4 rlineto -1 -1 curveto
477 3 3 5 5 6 6 rcurveto -1 -1 -1 -1 -1 -1 closepath)
487 all_commands_to_absolute_and_group (SCM expr)
491 Offset current (0, 0);
493 while (scm_is_pair (expr))
495 if (scm_is_eq (scm_car (expr), ly_symbol2scm ("moveto"))
496 || (scm_is_eq (scm_car (expr), ly_symbol2scm ("rmoveto")) && first))
498 Real x = robust_scm2double (scm_cadr (expr), 0.0);
499 Real y = robust_scm2double (scm_caddr (expr), 0.0);
500 start = Offset (x, y);
502 expr = scm_cdddr (expr);
504 if (scm_is_eq (scm_car (expr), ly_symbol2scm ("rmoveto")))
506 Real x = robust_scm2double (scm_cadr (expr), 0.0);
507 Real y = robust_scm2double (scm_caddr (expr), 0.0);
508 start = (Offset (x, y) + current);
510 expr = scm_cdddr (expr);
512 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("lineto")))
514 Real x = robust_scm2double (scm_cadr (expr), 0.0);
515 Real y = robust_scm2double (scm_caddr (expr), 0.0);
516 out = scm_cons (scm_list_4 (scm_from_double (current[X_AXIS]),
517 scm_from_double (current[Y_AXIS]),
519 scm_from_double (y)),
521 current = Offset (x, y);
522 expr = scm_cdddr (expr);
524 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("rlineto")))
526 Real x = robust_scm2double (scm_cadr (expr), 0.0);
527 Real y = robust_scm2double (scm_caddr (expr), 0.0);
528 out = scm_cons (scm_list_4 (scm_from_double (current[X_AXIS]),
529 scm_from_double (current[Y_AXIS]),
530 scm_from_double (x + current[X_AXIS]),
531 scm_from_double (y + current[Y_AXIS])),
533 current = (Offset (x, y) + current);
534 expr = scm_cdddr (expr);
536 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("curveto")))
538 Real x1 = robust_scm2double (scm_cadr (expr), 0.0);
539 expr = scm_cddr (expr);
540 Real y1 = robust_scm2double (scm_car (expr), 0.0);
541 expr = scm_cdr (expr);
542 Real x2 = robust_scm2double (scm_car (expr), 0.0);
543 expr = scm_cdr (expr);
544 Real y2 = robust_scm2double (scm_car (expr), 0.0);
545 expr = scm_cdr (expr);
546 Real x3 = robust_scm2double (scm_car (expr), 0.0);
547 expr = scm_cdr (expr);
548 Real y3 = robust_scm2double (scm_car (expr), 0.0);
549 expr = scm_cdr (expr);
550 out = scm_cons (scm_list_n (scm_from_double (current[X_AXIS]),
551 scm_from_double (current[Y_AXIS]),
552 scm_from_double (x1),
553 scm_from_double (y1),
554 scm_from_double (x2),
555 scm_from_double (y2),
556 scm_from_double (x3),
557 scm_from_double (y3),
560 current = Offset (x3, y3);
562 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("rcurveto")))
564 Real x1 = robust_scm2double (scm_cadr (expr), 0.0);
565 expr = scm_cddr (expr);
566 Real y1 = robust_scm2double (scm_car (expr), 0.0);
567 expr = scm_cdr (expr);
568 Real x2 = robust_scm2double (scm_car (expr), 0.0);
569 expr = scm_cdr (expr);
570 Real y2 = robust_scm2double (scm_car (expr), 0.0);
571 expr = scm_cdr (expr);
572 Real x3 = robust_scm2double (scm_car (expr), 0.0);
573 expr = scm_cdr (expr);
574 Real y3 = robust_scm2double (scm_car (expr), 0.0);
575 expr = scm_cdr (expr);
576 out = scm_cons (scm_list_n (scm_from_double (current[X_AXIS]),
577 scm_from_double (current[Y_AXIS]),
578 scm_from_double (x1 + current[X_AXIS]),
579 scm_from_double (y1 + current[Y_AXIS]),
580 scm_from_double (x2 + current[X_AXIS]),
581 scm_from_double (y2 + current[Y_AXIS]),
582 scm_from_double (x3 + current[X_AXIS]),
583 scm_from_double (y3 + current[Y_AXIS]),
586 current = (Offset (x3, y3) + current);
588 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("closepath")))
590 if ((current[X_AXIS] != start[X_AXIS])
591 || (current[Y_AXIS] != start[Y_AXIS]))
593 out = scm_cons (scm_list_4 (scm_from_double (current[X_AXIS]),
594 scm_from_double (current[Y_AXIS]),
595 scm_from_double (start[X_AXIS]),
596 scm_from_double (start[Y_AXIS])),
600 expr = scm_cdr (expr);
604 warning ("Malformed path for path stencil.");
609 return scm_reverse_x (out, SCM_EOL);
613 internal_make_path_boxes (vector<Box> &boxes,
614 vector<Drul_array<Offset> > &buildings,
615 PangoMatrix trans, SCM expr, bool use_building)
617 SCM blot = scm_car (expr);
618 expr = scm_cdr (expr);
619 SCM path = all_commands_to_absolute_and_group (expr);
620 // note that expr has more stuff that we don't need after this - simply ignore it
621 //////////////////////
622 for (SCM s = path; scm_is_pair (s); s = scm_cdr (s))
624 scm_to_int (scm_length (scm_car (s))) == 4
625 ? make_draw_line_boxes (boxes, buildings, trans, scm_cons (blot, scm_car (s)), use_building)
626 : make_draw_bezier_boxes (boxes, buildings, trans, scm_cons (blot, scm_car (s)));
631 make_path_boxes (vector<Box> &boxes,
632 vector<Drul_array<Offset> > &buildings,
633 PangoMatrix trans, SCM expr)
635 return internal_make_path_boxes (boxes, buildings, trans, scm_cons (scm_car (expr), get_path_list (scm_cdr (expr))), false);
639 make_polygon_boxes (vector<Box> &boxes,
640 vector<Drul_array<Offset> > &buildings,
641 PangoMatrix trans, SCM expr)
643 SCM coords = get_number_list (scm_car (expr));
644 expr = scm_cdr (expr);
645 SCM blot_diameter = scm_car (expr);
646 //////////////////////
649 for (SCM s = coords; scm_is_pair (s); s = scm_cddr (s))
651 l = scm_cons (first ? ly_symbol2scm ("moveto") : ly_symbol2scm ("lineto"), l);
652 l = scm_cons (scm_car (s), l);
653 l = scm_cons (scm_cadr (s), l);
656 l = scm_cons (ly_symbol2scm ("closepath"), l);
657 internal_make_path_boxes (boxes, buildings, trans,
658 scm_cons (blot_diameter, scm_reverse_x (l, SCM_EOL)), true);
662 make_named_glyph_boxes (vector<Box> &boxes,
663 vector<Drul_array<Offset> > &buildings,
664 PangoMatrix trans, SCM expr)
666 SCM fm_scm = scm_car (expr);
667 Font_metric *fm = unsmob<Font_metric> (fm_scm);
668 expr = scm_cdr (expr);
669 SCM glyph = scm_car (expr);
670 string glyph_s = ly_scm2string (glyph);
672 //////////////////////
673 Open_type_font *open_fm
674 = dynamic_cast<Open_type_font *>
675 (dynamic_cast<Modified_font_metric *>(fm)->original_font ());
676 SCM_ASSERT_TYPE (open_fm, fm_scm, SCM_ARG1, __FUNCTION__, "OpenType font");
678 size_t gidx = open_fm->name_to_index (glyph_s);
679 // Bbox is the best approximation of the width based on how it would be
680 // calculated in open-type-font.cc if it were based on real extents
681 Box bbox = open_fm->get_unscaled_indexed_char_dimensions (gidx);
682 bbox.scale (dynamic_cast<Modified_font_metric *> (fm)->get_magnification ()
683 * open_fm->design_size () / open_fm->get_units_per_EM ());
684 // Real bbox is the real bbox of the object
685 Box real_bbox = open_fm->get_glyph_outline_bbox (gidx);
687 Real scale = bbox[X_AXIS].length () / real_bbox[X_AXIS].length ();
689 pango_matrix_scale (&trans, scale, scale);
691 SCM outline = open_fm->get_glyph_outline (gidx);
692 //////////////////////
693 for (SCM s = outline;
697 scm_to_int (scm_length (scm_car (s))) == 4
698 ? make_draw_line_boxes (boxes, buildings, trans,
699 scm_cons (scm_from_double (0), scm_car (s)),
701 : make_draw_bezier_boxes (boxes, buildings, trans,
702 scm_cons (scm_from_double (0), scm_car (s)));
707 make_glyph_string_boxes (vector<Box> &boxes,
708 vector<Drul_array<Offset> > &buildings,
709 PangoMatrix trans, SCM expr)
711 SCM fm_scm = scm_car (expr);
712 Font_metric *fm = unsmob<Font_metric> (fm_scm);
713 expr = scm_cdr (expr);
714 expr = scm_cdr (expr); // font-name
715 expr = scm_cdr (expr); // size
716 expr = scm_cdr (expr); // cid?
717 SCM whxy = scm_cadar (expr);
719 vector<Interval> heights;
722 vector<string> char_ids;
723 //////////////////////
724 Pango_font *pango_fm = dynamic_cast<Pango_font *> (fm);
725 SCM_ASSERT_TYPE (pango_fm, fm_scm, SCM_ARG1, __FUNCTION__, "Pango font");
727 for (SCM s = whxy; scm_is_pair (s); s = scm_cdr (s))
729 SCM now = scm_car (s);
730 widths.push_back (robust_scm2double (scm_car (now), 0.0));
732 heights.push_back (robust_scm2interval (scm_car (now), Interval (0, 0)));
734 xos.push_back (robust_scm2double (scm_car (now), 0.0));
736 yos.push_back (robust_scm2double (scm_car (now), 0.0));
738 char_ids.push_back (robust_scm2string (scm_car (now), ""));
740 Real cumulative_x = 0.0;
741 for (vsize i = 0; i < widths.size (); i++)
743 PangoMatrix transcopy (trans);
744 Offset pt0 (cumulative_x + xos[i], heights[i][DOWN] + yos[i]);
745 Offset pt1 (cumulative_x + widths[i] + xos[i], heights[i][UP] + yos[i]);
746 cumulative_x += widths[i];
749 kerned_bbox.add_point (pt0);
750 kerned_bbox.add_point (pt1);
751 size_t gidx = pango_fm->name_to_index (char_ids[i]);
752 Box real_bbox = pango_fm->get_scaled_indexed_char_dimensions (gidx);
753 Box bbox = pango_fm->get_unscaled_indexed_char_dimensions (gidx);
754 SCM outline = pango_fm->get_glyph_outline (gidx);
756 // scales may have rounding error but should be close
757 Real xlen = real_bbox[X_AXIS].length () / bbox[X_AXIS].length ();
758 Real ylen = real_bbox[Y_AXIS].length () / bbox[Y_AXIS].length ();
763 The value will be nan for whitespace, in which case we just want
764 filler, so the kerned bbox is ok.
766 However, if the value is inf, this likely means that LilyPond is
767 using a font that is currently difficult to get the measurements
768 from the Pango_font. This should eventually be fixed. The solution
769 for now is just to use the bounding box.
771 if (isnan (xlen) || isnan (ylen) || isinf (xlen) || isinf (ylen))
772 outline = box_to_scheme_lines (kerned_bbox);
775 assert (abs (xlen - ylen) < 10e-3);
777 Real scale_factor = max (xlen, ylen);
778 // the three operations below move the stencil from its original coordinates to current coordinates
779 pango_matrix_translate (&transcopy, kerned_bbox[X_AXIS][LEFT],
780 kerned_bbox[Y_AXIS][DOWN] - real_bbox[Y_AXIS][DOWN]);
781 pango_matrix_translate (&transcopy, real_bbox[X_AXIS][LEFT],
782 real_bbox[Y_AXIS][DOWN]);
783 pango_matrix_scale (&transcopy, scale_factor, scale_factor);
784 pango_matrix_translate (&transcopy, -bbox[X_AXIS][LEFT],
785 -bbox[Y_AXIS][DOWN]);
787 //////////////////////
788 for (SCM s = outline;
792 scm_to_int (scm_length (scm_car (s))) == 4
793 ? make_draw_line_boxes (boxes, buildings, transcopy,
794 scm_cons (scm_from_double (0), scm_car (s)),
796 : make_draw_bezier_boxes (boxes, buildings, transcopy,
797 scm_cons (scm_from_double (0), scm_car (s)));
803 receives a stencil expression and a transform matrix
804 depending on the stencil name, dispatches it to the appropriate function
808 stencil_dispatcher (vector<Box> &boxes,
809 vector<Drul_array<Offset> > &buildings,
810 PangoMatrix trans, SCM expr)
812 if (not scm_is_pair (expr))
814 if (scm_is_eq (scm_car (expr), ly_symbol2scm ("draw-line")))
815 make_draw_line_boxes (boxes, buildings, trans, scm_cdr (expr), true);
816 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("dashed-line")))
818 expr = scm_cdr (expr);
819 SCM th = scm_car (expr);
820 expr = scm_cdr (expr);
821 expr = scm_cdr (expr); // on
822 expr = scm_cdr (expr); // off
823 SCM x1 = scm_car (expr);
824 expr = scm_cdr (expr);
825 SCM x2 = scm_car (expr);
826 make_draw_line_boxes (boxes, buildings, trans,
827 scm_list_5 (th, scm_from_double (0.0),
828 scm_from_double (0.0), x1, x2), true);
830 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("circle")))
832 expr = scm_cdr (expr);
833 SCM rad = scm_car (expr);
834 expr = scm_cdr (expr);
835 SCM th = scm_car (expr);
836 make_partial_ellipse_boxes (boxes, buildings, trans,
839 scm_from_double (0.0),
840 scm_from_double (360.0),
846 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("ellipse")))
848 expr = scm_cdr (expr);
849 SCM x_rad = scm_car (expr);
850 expr = scm_cdr (expr);
851 SCM y_rad = scm_car (expr);
852 expr = scm_cdr (expr);
853 SCM th = scm_car (expr);
854 make_partial_ellipse_boxes (boxes, buildings, trans,
857 scm_from_double (0.0),
858 scm_from_double (360.0),
864 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("partial-ellipse")))
865 make_partial_ellipse_boxes (boxes, buildings, trans, scm_cdr (expr));
866 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("round-filled-box")))
867 make_round_filled_box_boxes (boxes, trans, scm_cdr (expr));
868 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("named-glyph")))
869 make_named_glyph_boxes (boxes, buildings, trans, scm_cdr (expr));
870 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("polygon")))
871 make_polygon_boxes (boxes, buildings, trans, scm_cdr (expr));
872 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("path")))
873 make_path_boxes (boxes, buildings, trans, scm_cdr (expr));
874 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("glyph-string")))
875 make_glyph_string_boxes (boxes, buildings, trans, scm_cdr (expr));
879 warning ("Stencil expression not supported by the veritcal skylines.");
882 We don't issue a warning here, as we assume that stencil-expression.cc
883 is doing stencil-checking correctly.
889 traverses a stencil expression, returning a vector of Transform_matrix_and_expression
890 the struct Transform_matrix_and_expression contains two members,
891 a Transform_matrix that indicates where to move a stencil and the stencil expression
892 to show how to construct the stencil
894 vector<Transform_matrix_and_expression>
895 stencil_traverser (PangoMatrix trans, SCM expr)
897 if (scm_is_null (expr)
898 || (scm_is_string (expr) && scm_is_true (scm_string_null_p (expr))))
899 return vector<Transform_matrix_and_expression> ();
900 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("combine-stencil")))
902 vector<Transform_matrix_and_expression> out;
903 for (SCM s = scm_cdr (expr); scm_is_pair (s); s = scm_cdr (s))
905 vector<Transform_matrix_and_expression> res =
906 stencil_traverser (trans, scm_car (s));
907 out.insert (out.end (), res.begin (), res.end ());
911 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("footnote")))
912 return vector<Transform_matrix_and_expression> ();
913 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("translate-stencil")))
915 Real x = robust_scm2double (scm_caadr (expr), 0.0);
916 Real y = robust_scm2double (scm_cdadr (expr), 0.0);
917 pango_matrix_translate (&trans, x, y);
918 return stencil_traverser (trans, scm_caddr (expr));
920 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("scale-stencil")))
922 Real x = robust_scm2double (scm_caadr (expr), 0.0);
923 Real y = robust_scm2double (scm_cadadr (expr), 0.0);
924 pango_matrix_scale (&trans, x, y);
925 return stencil_traverser (trans, scm_caddr (expr));
927 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("rotate-stencil")))
929 Real ang = robust_scm2double (scm_caadr (expr), 0.0);
930 Real x = robust_scm2double (scm_car (scm_cadadr (expr)), 0.0);
931 Real y = robust_scm2double (scm_cdr (scm_cadadr (expr)), 0.0);
932 pango_matrix_translate (&trans, x, y);
933 pango_matrix_rotate (&trans, -ang);
934 pango_matrix_translate (&trans, -x, -y);
935 return stencil_traverser (trans, scm_caddr (expr));
937 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("delay-stencil-evaluation")))
938 // should not use the place-holder text, but no need for the warning below
939 return vector<Transform_matrix_and_expression> ();
940 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("grob-cause")))
941 return stencil_traverser (trans, scm_caddr (expr));
942 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("color")))
943 return stencil_traverser (trans, scm_caddr (expr));
944 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("transparent-stencil")))
945 return stencil_traverser (trans, scm_cadr (expr));
946 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("output-attributes")))
947 return stencil_traverser (trans, scm_caddr (expr));
948 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("with-outline")))
949 return stencil_traverser (trans, scm_cadr (expr));
952 vector<Transform_matrix_and_expression> out;
953 out.push_back (Transform_matrix_and_expression (trans, expr));
956 warning ("Stencil expression not supported by the veritcal skylines.");
957 return vector<Transform_matrix_and_expression> ();
961 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 // we don't know how far spanners stretch along the X axis before
965 // line breaking. better have them take up the whole thing
966 Interval xex = ignore_x
967 ? Interval (-infinity_f, infinity_f)
968 : me->extent (me, X_AXIS);
970 // If we're looking at the x exent of a cross staff grob, it could be
971 // very early on in the computation process. We won't know its height
972 // until way later, so we give a brute force approximation.
973 Interval yex = ignore_y
974 ? Interval (-infinity_f, infinity_f)
975 : me->maybe_pure_extent (me, Y_AXIS, pure, beg, end);
977 if (xex.is_empty () || yex.is_empty ())
978 return Skyline_pair ().smobbed_copy ();
980 boxes.push_back (Box (xex, yex));
981 return Skyline_pair (boxes, a).smobbed_copy ();
984 MAKE_SCHEME_CALLBACK (Grob, pure_simple_vertical_skylines_from_extents, 3);
986 Grob::pure_simple_vertical_skylines_from_extents (SCM smob, SCM begscm, SCM endscm)
988 Grob *me = unsmob<Grob> (smob);
989 int beg = robust_scm2int (begscm, 0);
990 int end = robust_scm2int (endscm, INT_MAX);
991 // We cannot measure the widths before line breaking,
992 // so we assume that the width is infinite: pass ignore_x=true
993 return maybe_pure_internal_simple_skylines_from_extents (me, X_AXIS, true, beg, end, true, false);
996 MAKE_SCHEME_CALLBACK (Grob, simple_vertical_skylines_from_extents, 1);
998 Grob::simple_vertical_skylines_from_extents (SCM smob)
1000 Grob *me = unsmob<Grob> (smob);
1001 return maybe_pure_internal_simple_skylines_from_extents (me, X_AXIS, false, 0, 0, false, false);
1004 MAKE_SCHEME_CALLBACK (Grob, pure_simple_horizontal_skylines_from_extents, 3);
1006 Grob::pure_simple_horizontal_skylines_from_extents (SCM smob, SCM begscm, SCM endscm)
1008 Grob *me = unsmob<Grob> (smob);
1009 int beg = robust_scm2int (begscm, 0);
1010 int end = robust_scm2int (endscm, INT_MAX);
1011 // If the grob is cross staff, we cannot measure its Y-extent before
1012 // wayyyy downstream (after spacing of axis groups is done).
1013 // Thus, we assume that the Y extent is infinite for cross staff grobs.
1014 return maybe_pure_internal_simple_skylines_from_extents (me, Y_AXIS, true, beg, end, false, to_boolean (me->get_property ("cross-staff")));
1017 MAKE_SCHEME_CALLBACK (Grob, simple_horizontal_skylines_from_extents, 1);
1019 Grob::simple_horizontal_skylines_from_extents (SCM smob)
1021 Grob *me = unsmob<Grob> (smob);
1022 // See comment in function above.
1023 return maybe_pure_internal_simple_skylines_from_extents (me, Y_AXIS, false, 0, 0, false, to_boolean (me->get_property ("cross-staff")));
1027 Stencil::skylines_from_stencil (SCM sten, Real pad, Axis a)
1029 Stencil *s = unsmob<Stencil> (sten);
1031 return Skyline_pair ().smobbed_copy ();
1033 vector<Transform_matrix_and_expression> data
1034 = stencil_traverser (make_transform_matrix (1.0, 0.0, 0.0, 1.0, 0.0, 0.0),
1037 vector<Drul_array<Offset> > buildings;
1038 for (vsize i = 0; i < data.size (); i++)
1039 stencil_dispatcher (boxes, buildings, data[i].tm_, data[i].expr_);
1041 // we use the bounding box if there are no boxes
1042 if (!boxes.size () && !buildings.size ())
1043 boxes.push_back (Box (s->extent (X_AXIS), s->extent (Y_AXIS)));
1045 Skyline_pair out (boxes, a);
1046 out.merge (Skyline_pair (buildings, a));
1048 for (DOWN_and_UP (d))
1049 out[d] = out[d].padded (pad);
1051 return out.smobbed_copy ();
1054 MAKE_SCHEME_CALLBACK (Grob, vertical_skylines_from_stencil, 1);
1056 Grob::vertical_skylines_from_stencil (SCM smob)
1058 Grob *me = unsmob<Grob> (smob);
1060 Real pad = robust_scm2double (me->get_property ("skyline-horizontal-padding"), 0.0);
1061 SCM out = Stencil::skylines_from_stencil (me->get_property ("stencil"), pad, X_AXIS);
1066 MAKE_SCHEME_CALLBACK (Grob, horizontal_skylines_from_stencil, 1);
1068 Grob::horizontal_skylines_from_stencil (SCM smob)
1070 Grob *me = unsmob<Grob> (smob);
1072 Real pad = robust_scm2double (me->get_property ("skyline-vertical-padding"), 0.0);
1073 SCM out = Stencil::skylines_from_stencil (me->get_property ("stencil"), pad, Y_AXIS);
1079 Grob::internal_skylines_from_element_stencils (Grob *me, Axis a, bool pure, int beg, int end)
1082 extract_grob_set (me, "elements", elts);
1085 Grob *x_common = common_refpoint_of_array (elts, me, X_AXIS);
1086 Grob *y_common = common_refpoint_of_array (elts, me, Y_AXIS);
1087 for (vsize i = 0; i < elts.size (); i++)
1089 x_pos.push_back (elts[i]->relative_coordinate (x_common, X_AXIS));
1090 y_pos.push_back (elts[i]->maybe_pure_coordinate (y_common, Y_AXIS, pure, beg, end));
1092 Real my_x = me->relative_coordinate (x_common, X_AXIS);
1093 Real my_y = me->maybe_pure_coordinate (y_common, Y_AXIS, pure, beg, end);
1096 for (vsize i = 0; i < elts.size (); i++)
1098 Skyline_pair *skyp = unsmob<Skyline_pair> (elts[i]->get_maybe_pure_property (a == X_AXIS ? "vertical-skylines" : "horizontal-skylines", pure, beg, end));
1102 Here, copying is essential. Otherwise, the skyline pair will
1106 It took Mike about 6 months of his life to add the `else' clause
1107 below. For horizontal skylines, the raise and shift calls need
1108 to be reversed. This is what was causing the problems in the
1109 shifting with all of the tests. RIP 6 months!
1111 Skyline_pair copy = Skyline_pair (*skyp);
1114 copy.shift (x_pos[i] - my_x);
1115 copy.raise (y_pos[i] - my_y);
1119 copy.raise (x_pos[i] - my_x);
1120 copy.shift (y_pos[i] - my_y);
1125 return res.smobbed_copy ();
1128 MAKE_SCHEME_CALLBACK (Grob, vertical_skylines_from_element_stencils, 1);
1130 Grob::vertical_skylines_from_element_stencils (SCM smob)
1132 Grob *me = unsmob<Grob> (smob);
1133 return internal_skylines_from_element_stencils (me, X_AXIS, false, 0, INT_MAX);
1136 MAKE_SCHEME_CALLBACK (Grob, horizontal_skylines_from_element_stencils, 1);
1138 Grob::horizontal_skylines_from_element_stencils (SCM smob)
1140 Grob *me = unsmob<Grob> (smob);
1141 return internal_skylines_from_element_stencils (me, Y_AXIS, false, 0, INT_MAX);
1144 MAKE_SCHEME_CALLBACK (Grob, pure_vertical_skylines_from_element_stencils, 3);
1146 Grob::pure_vertical_skylines_from_element_stencils (SCM smob, SCM beg_scm, SCM end_scm)
1148 Grob *me = unsmob<Grob> (smob);
1149 int beg = robust_scm2int (beg_scm, 0);
1150 int end = robust_scm2int (end_scm, 0);
1151 return internal_skylines_from_element_stencils (me, X_AXIS, true, beg, end);
1154 MAKE_SCHEME_CALLBACK (Grob, pure_horizontal_skylines_from_element_stencils, 3);
1156 Grob::pure_horizontal_skylines_from_element_stencils (SCM smob, SCM beg_scm, SCM end_scm)
1158 Grob *me = unsmob<Grob> (smob);
1159 int beg = robust_scm2int (beg_scm, 0);
1160 int end = robust_scm2int (end_scm, 0);
1161 return internal_skylines_from_element_stencils (me, Y_AXIS, true, beg, end);