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 return vector<Transform_matrix_and_expression> ();
899 else if (scm_is_eq (expr, ly_string2scm ("")))
900 return vector<Transform_matrix_and_expression> ();
901 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("combine-stencil")))
903 vector<Transform_matrix_and_expression> out;
904 for (SCM s = scm_cdr (expr); scm_is_pair (s); s = scm_cdr (s))
906 vector<Transform_matrix_and_expression> res =
907 stencil_traverser (trans, scm_car (s));
908 out.insert (out.end (), res.begin (), res.end ());
912 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("footnote")))
913 return vector<Transform_matrix_and_expression> ();
914 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("translate-stencil")))
916 Real x = robust_scm2double (scm_caadr (expr), 0.0);
917 Real y = robust_scm2double (scm_cdadr (expr), 0.0);
918 pango_matrix_translate (&trans, x, y);
919 return stencil_traverser (trans, scm_caddr (expr));
921 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("scale-stencil")))
923 Real x = robust_scm2double (scm_caadr (expr), 0.0);
924 Real y = robust_scm2double (scm_cadadr (expr), 0.0);
925 pango_matrix_scale (&trans, x, y);
926 return stencil_traverser (trans, scm_caddr (expr));
928 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("rotate-stencil")))
930 Real ang = robust_scm2double (scm_caadr (expr), 0.0);
931 Real x = robust_scm2double (scm_car (scm_cadadr (expr)), 0.0);
932 Real y = robust_scm2double (scm_cdr (scm_cadadr (expr)), 0.0);
933 pango_matrix_translate (&trans, x, y);
934 pango_matrix_rotate (&trans, -ang);
935 pango_matrix_translate (&trans, -x, -y);
936 return stencil_traverser (trans, scm_caddr (expr));
938 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("delay-stencil-evaluation")))
939 // should not use the place-holder text, but no need for the warning below
940 return vector<Transform_matrix_and_expression> ();
941 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("grob-cause")))
942 return stencil_traverser (trans, scm_caddr (expr));
943 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("color")))
944 return stencil_traverser (trans, scm_caddr (expr));
945 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("transparent-stencil")))
946 return stencil_traverser (trans, scm_cadr (expr));
947 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("output-attributes")))
948 return stencil_traverser (trans, scm_caddr (expr));
951 vector<Transform_matrix_and_expression> out;
952 out.push_back (Transform_matrix_and_expression (trans, expr));
955 warning ("Stencil expression not supported by the veritcal skylines.");
956 return vector<Transform_matrix_and_expression> ();
960 Grob::maybe_pure_internal_simple_skylines_from_extents (Grob *me, Axis a, bool pure, int beg, int end, bool ignore_x, bool ignore_y)
963 // we don't know how far spanners stretch along the X axis before
964 // line breaking. better have them take up the whole thing
965 Interval xex = ignore_x
966 ? Interval (-infinity_f, infinity_f)
967 : me->extent (me, X_AXIS);
969 // If we're looking at the x exent of a cross staff grob, it could be
970 // very early on in the computation process. We won't know its height
971 // until way later, so we give a brute force approximation.
972 Interval yex = ignore_y
973 ? Interval (-infinity_f, infinity_f)
974 : me->maybe_pure_extent (me, Y_AXIS, pure, beg, end);
976 if (xex.is_empty () || yex.is_empty ())
977 return Skyline_pair ().smobbed_copy ();
979 boxes.push_back (Box (xex, yex));
980 return Skyline_pair (boxes, a).smobbed_copy ();
983 MAKE_SCHEME_CALLBACK (Grob, pure_simple_vertical_skylines_from_extents, 3);
985 Grob::pure_simple_vertical_skylines_from_extents (SCM smob, SCM begscm, SCM endscm)
987 Grob *me = unsmob<Grob> (smob);
988 int beg = robust_scm2int (begscm, 0);
989 int end = robust_scm2int (endscm, INT_MAX);
990 // We cannot measure the widths before line breaking,
991 // so we assume that the width is infinite: pass ignore_x=true
992 return maybe_pure_internal_simple_skylines_from_extents (me, X_AXIS, true, beg, end, true, false);
995 MAKE_SCHEME_CALLBACK (Grob, simple_vertical_skylines_from_extents, 1);
997 Grob::simple_vertical_skylines_from_extents (SCM smob)
999 Grob *me = unsmob<Grob> (smob);
1000 return maybe_pure_internal_simple_skylines_from_extents (me, X_AXIS, false, 0, 0, false, false);
1003 MAKE_SCHEME_CALLBACK (Grob, pure_simple_horizontal_skylines_from_extents, 3);
1005 Grob::pure_simple_horizontal_skylines_from_extents (SCM smob, SCM begscm, SCM endscm)
1007 Grob *me = unsmob<Grob> (smob);
1008 int beg = robust_scm2int (begscm, 0);
1009 int end = robust_scm2int (endscm, INT_MAX);
1010 // If the grob is cross staff, we cannot measure its Y-extent before
1011 // wayyyy downstream (after spacing of axis groups is done).
1012 // Thus, we assume that the Y extent is infinite for cross staff grobs.
1013 return maybe_pure_internal_simple_skylines_from_extents (me, Y_AXIS, true, beg, end, false, to_boolean (me->get_property ("cross-staff")));
1016 MAKE_SCHEME_CALLBACK (Grob, simple_horizontal_skylines_from_extents, 1);
1018 Grob::simple_horizontal_skylines_from_extents (SCM smob)
1020 Grob *me = unsmob<Grob> (smob);
1021 // See comment in function above.
1022 return maybe_pure_internal_simple_skylines_from_extents (me, Y_AXIS, false, 0, 0, false, to_boolean (me->get_property ("cross-staff")));
1026 Stencil::skylines_from_stencil (SCM sten, Real pad, Axis a)
1028 Stencil *s = unsmob<Stencil> (sten);
1030 return Skyline_pair ().smobbed_copy ();
1032 vector<Transform_matrix_and_expression> data
1033 = stencil_traverser (make_transform_matrix (1.0, 0.0, 0.0, 1.0, 0.0, 0.0),
1036 vector<Drul_array<Offset> > buildings;
1037 for (vsize i = 0; i < data.size (); i++)
1038 stencil_dispatcher (boxes, buildings, data[i].tm_, data[i].expr_);
1040 // we use the bounding box if there are no boxes
1041 if (!boxes.size () && !buildings.size ())
1042 boxes.push_back (Box (s->extent (X_AXIS), s->extent (Y_AXIS)));
1044 Skyline_pair out (boxes, a);
1045 out.merge (Skyline_pair (buildings, a));
1047 for (DOWN_and_UP (d))
1048 out[d] = out[d].padded (pad);
1050 return out.smobbed_copy ();
1053 MAKE_SCHEME_CALLBACK (Grob, vertical_skylines_from_stencil, 1);
1055 Grob::vertical_skylines_from_stencil (SCM smob)
1057 Grob *me = unsmob<Grob> (smob);
1059 Real pad = robust_scm2double (me->get_property ("skyline-horizontal-padding"), 0.0);
1060 SCM out = Stencil::skylines_from_stencil (me->get_property ("stencil"), pad, X_AXIS);
1065 MAKE_SCHEME_CALLBACK (Grob, horizontal_skylines_from_stencil, 1);
1067 Grob::horizontal_skylines_from_stencil (SCM smob)
1069 Grob *me = unsmob<Grob> (smob);
1071 Real pad = robust_scm2double (me->get_property ("skyline-vertical-padding"), 0.0);
1072 SCM out = Stencil::skylines_from_stencil (me->get_property ("stencil"), pad, Y_AXIS);
1078 Grob::internal_skylines_from_element_stencils (Grob *me, Axis a, bool pure, int beg, int end)
1081 extract_grob_set (me, "elements", elts);
1084 Grob *x_common = common_refpoint_of_array (elts, me, X_AXIS);
1085 Grob *y_common = common_refpoint_of_array (elts, me, Y_AXIS);
1086 for (vsize i = 0; i < elts.size (); i++)
1088 x_pos.push_back (elts[i]->relative_coordinate (x_common, X_AXIS));
1089 y_pos.push_back (elts[i]->maybe_pure_coordinate (y_common, Y_AXIS, pure, beg, end));
1091 Real my_x = me->relative_coordinate (x_common, X_AXIS);
1092 Real my_y = me->maybe_pure_coordinate (y_common, Y_AXIS, pure, beg, end);
1095 for (vsize i = 0; i < elts.size (); i++)
1097 Skyline_pair *skyp = unsmob<Skyline_pair> (elts[i]->get_maybe_pure_property (a == X_AXIS ? "vertical-skylines" : "horizontal-skylines", pure, beg, end));
1101 Here, copying is essential. Otherwise, the skyline pair will
1105 It took Mike about 6 months of his life to add the `else' clause
1106 below. For horizontal skylines, the raise and shift calls need
1107 to be reversed. This is what was causing the problems in the
1108 shifting with all of the tests. RIP 6 months!
1110 Skyline_pair copy = Skyline_pair (*skyp);
1113 copy.shift (x_pos[i] - my_x);
1114 copy.raise (y_pos[i] - my_y);
1118 copy.raise (x_pos[i] - my_x);
1119 copy.shift (y_pos[i] - my_y);
1124 return res.smobbed_copy ();
1127 MAKE_SCHEME_CALLBACK (Grob, vertical_skylines_from_element_stencils, 1);
1129 Grob::vertical_skylines_from_element_stencils (SCM smob)
1131 Grob *me = unsmob<Grob> (smob);
1132 return internal_skylines_from_element_stencils (me, X_AXIS, false, 0, INT_MAX);
1135 MAKE_SCHEME_CALLBACK (Grob, horizontal_skylines_from_element_stencils, 1);
1137 Grob::horizontal_skylines_from_element_stencils (SCM smob)
1139 Grob *me = unsmob<Grob> (smob);
1140 return internal_skylines_from_element_stencils (me, Y_AXIS, false, 0, INT_MAX);
1143 MAKE_SCHEME_CALLBACK (Grob, pure_vertical_skylines_from_element_stencils, 3);
1145 Grob::pure_vertical_skylines_from_element_stencils (SCM smob, SCM beg_scm, SCM end_scm)
1147 Grob *me = unsmob<Grob> (smob);
1148 int beg = robust_scm2int (beg_scm, 0);
1149 int end = robust_scm2int (end_scm, 0);
1150 return internal_skylines_from_element_stencils (me, X_AXIS, true, beg, end);
1153 MAKE_SCHEME_CALLBACK (Grob, pure_horizontal_skylines_from_element_stencils, 3);
1155 Grob::pure_horizontal_skylines_from_element_stencils (SCM smob, SCM beg_scm, SCM end_scm)
1157 Grob *me = unsmob<Grob> (smob);
1158 int beg = robust_scm2int (beg_scm, 0);
1159 int end = robust_scm2int (end_scm, 0);
1160 return internal_skylines_from_element_stencils (me, Y_AXIS, true, beg, end);