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 Real start = robust_scm2double (scm_car (expr), 0.0);
264 expr = scm_cdr (expr);
265 Real end = robust_scm2double (scm_car (expr), 0.0);
266 expr = scm_cdr (expr);
267 Real th = robust_scm2double (scm_car (expr), 0.0);
268 expr = scm_cdr (expr);
269 bool connect = to_boolean (scm_car (expr));
270 expr = scm_cdr (expr);
271 bool fill = to_boolean (scm_car (expr));
272 //////////////////////
273 start = M_PI * start / 180;
274 end = M_PI * end / 180;
277 Offset sp (cos (start) * x_rad, sin (start) * y_rad);
278 Offset ep (cos (end) * x_rad, sin (end) * y_rad);
279 //////////////////////
280 Drul_array<vector<Offset> > points;
281 int quantization = max (1, (int) (((x_rad * trans.xx) + (y_rad * trans.yy)) * M_PI / QUANTIZATION_UNIT));
282 for (DOWN_and_UP (d))
284 for (vsize i = 0; i < 1 + (vsize) quantization; i++)
286 Real ang = linear_map (start, end, 0, quantization, i);
287 Offset pt (cos (ang) * x_rad, sin (ang) * y_rad);
288 Offset inter = pt + d * get_normal ((th/2) * pt.direction ());
289 pango_matrix_transform_point (&trans, &inter[X_AXIS], &inter[Y_AXIS]);
290 points[d].push_back (inter);
294 for (vsize i = 0; i < points[DOWN].size () - 1; i++)
297 for (DOWN_and_UP (d))
299 b.add_point (points[d][i]);
300 b.add_point (points[d][i + 1]);
307 make_draw_line_boxes (boxes, buildings, trans,
308 scm_list_5 (scm_from_double (th),
309 scm_from_double (sp[X_AXIS]),
310 scm_from_double (sp[Y_AXIS]),
311 scm_from_double (ep[X_AXIS]),
312 scm_from_double (ep[Y_AXIS])),
319 Offset pt (cos (start) * x_rad, sin (start) * y_rad);
320 create_path_cap (boxes,
328 pt = Offset (cos (end) * x_rad, sin (end) * y_rad);
329 create_path_cap (boxes,
339 make_round_filled_box_boxes (vector<Box> &boxes, PangoMatrix trans, SCM expr)
341 Real left = robust_scm2double (scm_car (expr), 0.0);
342 expr = scm_cdr (expr);
343 Real right = robust_scm2double (scm_car (expr), 0.0);
344 expr = scm_cdr (expr);
345 Real bottom = robust_scm2double (scm_car (expr), 0.0);
346 expr = scm_cdr (expr);
347 Real top = robust_scm2double (scm_car (expr), 0.0);
348 expr = scm_cdr (expr);
349 Real th = robust_scm2double (scm_car (expr), 0.0);
350 //////////////////////
351 vector<Offset> points;
353 Offset p0 = Offset (-left - (th / 2), -bottom - (th / 2));
354 Offset p1 = Offset (right + (th / 2), top + (th / 2));
355 pango_matrix_transform_point (&trans, &p0[X_AXIS], &p0[Y_AXIS]);
356 pango_matrix_transform_point (&trans, &p1[X_AXIS], &p1[Y_AXIS]);
363 create_path_cap (vector<Box> &boxes,
364 vector<Drul_array<Offset> > &buildings,
365 PangoMatrix trans, Offset pt, Real rad, Offset dir)
367 Real angle = dir.angle_degrees ();
368 PangoMatrix new_trans (trans);
369 pango_matrix_translate (&new_trans, pt[X_AXIS], pt[Y_AXIS]);
370 make_partial_ellipse_boxes (boxes, buildings, new_trans,
371 scm_list_n (scm_from_double (rad),
372 scm_from_double (rad),
373 scm_from_double (angle-90.01),
374 scm_from_double (angle+90.01),
375 scm_from_double (0.0),
382 make_draw_bezier_boxes (vector<Box> &boxes,
383 vector<Drul_array<Offset> > &buildings,
384 PangoMatrix trans, SCM expr)
386 Real th = robust_scm2double (scm_car (expr), 0.0);
387 expr = scm_cdr (expr);
388 Real x0 = robust_scm2double (scm_car (expr), 0.0);
389 expr = scm_cdr (expr);
390 Real y0 = robust_scm2double (scm_car (expr), 0.0);
391 expr = scm_cdr (expr);
392 Real x1 = robust_scm2double (scm_car (expr), 0.0);
393 expr = scm_cdr (expr);
394 Real y1 = robust_scm2double (scm_car (expr), 0.0);
395 expr = scm_cdr (expr);
396 Real x2 = robust_scm2double (scm_car (expr), 0.0);
397 expr = scm_cdr (expr);
398 Real y2 = robust_scm2double (scm_car (expr), 0.0);
399 expr = scm_cdr (expr);
400 Real x3 = robust_scm2double (scm_car (expr), 0.0);
401 expr = scm_cdr (expr);
402 Real y3 = robust_scm2double (scm_car (expr), 0.0);
403 //////////////////////
405 curve.control_[0] = Offset (x0, y0);
406 curve.control_[1] = Offset (x1, y1);
407 curve.control_[2] = Offset (x2, y2);
408 curve.control_[3] = Offset (x3, y3);
409 Offset temp0 (x0, y0);
410 Offset temp1 (x1, y1);
411 Offset temp2 (x2, y2);
412 Offset temp3 (x3, y3);
413 pango_matrix_transform_point (&trans, &temp0[X_AXIS], &temp0[Y_AXIS]);
414 pango_matrix_transform_point (&trans, &temp1[X_AXIS], &temp1[Y_AXIS]);
415 pango_matrix_transform_point (&trans, &temp2[X_AXIS], &temp2[Y_AXIS]);
416 pango_matrix_transform_point (&trans, &temp3[X_AXIS], &temp3[Y_AXIS]);
417 //////////////////////
418 Drul_array<vector<Offset> > points;
419 int quantization = int (((temp1 - temp0).length ()
420 + (temp2 - temp1).length ()
421 + (temp3 - temp2).length ())
422 / QUANTIZATION_UNIT);
423 for (DOWN_and_UP (d))
425 Offset first = curve.control_[0]
426 + d * get_normal ((th / 2) * curve.dir_at_point (0.0));
427 pango_matrix_transform_point (&trans, &first[X_AXIS], &first[Y_AXIS]);
428 points[d].push_back (first);
429 for (vsize i = 1; i < (vsize) quantization; i++)
431 Real pt = (i * 1.0) / quantization;
432 Offset inter = curve.curve_point (pt)
433 + d * get_normal ((th / 2) *curve.dir_at_point (pt));
434 pango_matrix_transform_point (&trans, &inter[X_AXIS], &inter[Y_AXIS]);
435 points[d].push_back (inter);
437 Offset last = curve.control_[3]
438 + d * get_normal ((th / 2) * curve.dir_at_point (1.0));
439 pango_matrix_transform_point (&trans, &last[X_AXIS], &last[Y_AXIS]);
440 points[d].push_back (last);
443 for (vsize i = 0; i < points[DOWN].size () - 1; i++)
446 for (DOWN_and_UP (d))
448 b.add_point (points[d][i]);
449 b.add_point (points[d][i + 1]);
457 create_path_cap (boxes,
462 -curve.dir_at_point (0.0));
465 create_path_cap (boxes,
470 curve.dir_at_point (1.0));
475 converts a path into lists of 4 (line) or 8 (curve) absolute coordinates
477 '(moveto 1 2 lineto 3 4 rlineto -1 -1 curveto
478 3 3 5 5 6 6 rcurveto -1 -1 -1 -1 -1 -1 closepath)
488 all_commands_to_absolute_and_group (SCM expr)
492 Offset current (0, 0);
494 while (scm_is_pair (expr))
496 if (scm_is_eq (scm_car (expr), ly_symbol2scm ("moveto"))
497 || (scm_is_eq (scm_car (expr), ly_symbol2scm ("rmoveto")) && first))
499 Real x = robust_scm2double (scm_cadr (expr), 0.0);
500 Real y = robust_scm2double (scm_caddr (expr), 0.0);
501 start = Offset (x, y);
503 expr = scm_cdddr (expr);
505 if (scm_is_eq (scm_car (expr), ly_symbol2scm ("rmoveto")))
507 Real x = robust_scm2double (scm_cadr (expr), 0.0);
508 Real y = robust_scm2double (scm_caddr (expr), 0.0);
509 start = (Offset (x, y) + current);
511 expr = scm_cdddr (expr);
513 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("lineto")))
515 Real x = robust_scm2double (scm_cadr (expr), 0.0);
516 Real y = robust_scm2double (scm_caddr (expr), 0.0);
517 out = scm_cons (scm_list_4 (scm_from_double (current[X_AXIS]),
518 scm_from_double (current[Y_AXIS]),
520 scm_from_double (y)),
522 current = Offset (x, y);
523 expr = scm_cdddr (expr);
525 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("rlineto")))
527 Real x = robust_scm2double (scm_cadr (expr), 0.0);
528 Real y = robust_scm2double (scm_caddr (expr), 0.0);
529 out = scm_cons (scm_list_4 (scm_from_double (current[X_AXIS]),
530 scm_from_double (current[Y_AXIS]),
531 scm_from_double (x + current[X_AXIS]),
532 scm_from_double (y + current[Y_AXIS])),
534 current = (Offset (x, y) + current);
535 expr = scm_cdddr (expr);
537 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("curveto")))
539 Real x1 = robust_scm2double (scm_cadr (expr), 0.0);
540 expr = scm_cddr (expr);
541 Real y1 = robust_scm2double (scm_car (expr), 0.0);
542 expr = scm_cdr (expr);
543 Real x2 = robust_scm2double (scm_car (expr), 0.0);
544 expr = scm_cdr (expr);
545 Real y2 = robust_scm2double (scm_car (expr), 0.0);
546 expr = scm_cdr (expr);
547 Real x3 = robust_scm2double (scm_car (expr), 0.0);
548 expr = scm_cdr (expr);
549 Real y3 = robust_scm2double (scm_car (expr), 0.0);
550 expr = scm_cdr (expr);
551 out = scm_cons (scm_list_n (scm_from_double (current[X_AXIS]),
552 scm_from_double (current[Y_AXIS]),
553 scm_from_double (x1),
554 scm_from_double (y1),
555 scm_from_double (x2),
556 scm_from_double (y2),
557 scm_from_double (x3),
558 scm_from_double (y3),
561 current = Offset (x3, y3);
563 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("rcurveto")))
565 Real x1 = robust_scm2double (scm_cadr (expr), 0.0);
566 expr = scm_cddr (expr);
567 Real y1 = robust_scm2double (scm_car (expr), 0.0);
568 expr = scm_cdr (expr);
569 Real x2 = robust_scm2double (scm_car (expr), 0.0);
570 expr = scm_cdr (expr);
571 Real y2 = robust_scm2double (scm_car (expr), 0.0);
572 expr = scm_cdr (expr);
573 Real x3 = robust_scm2double (scm_car (expr), 0.0);
574 expr = scm_cdr (expr);
575 Real y3 = robust_scm2double (scm_car (expr), 0.0);
576 expr = scm_cdr (expr);
577 out = scm_cons (scm_list_n (scm_from_double (current[X_AXIS]),
578 scm_from_double (current[Y_AXIS]),
579 scm_from_double (x1 + current[X_AXIS]),
580 scm_from_double (y1 + current[Y_AXIS]),
581 scm_from_double (x2 + current[X_AXIS]),
582 scm_from_double (y2 + current[Y_AXIS]),
583 scm_from_double (x3 + current[X_AXIS]),
584 scm_from_double (y3 + current[Y_AXIS]),
587 current = (Offset (x3, y3) + current);
589 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("closepath")))
591 if ((current[X_AXIS] != start[X_AXIS])
592 || (current[Y_AXIS] != start[Y_AXIS]))
594 out = scm_cons (scm_list_4 (scm_from_double (current[X_AXIS]),
595 scm_from_double (current[Y_AXIS]),
596 scm_from_double (start[X_AXIS]),
597 scm_from_double (start[Y_AXIS])),
601 expr = scm_cdr (expr);
605 warning ("Malformed path for path stencil.");
610 return scm_reverse_x (out, SCM_EOL);
614 internal_make_path_boxes (vector<Box> &boxes,
615 vector<Drul_array<Offset> > &buildings,
616 PangoMatrix trans, SCM expr, bool use_building)
618 SCM blot = scm_car (expr);
619 expr = scm_cdr (expr);
620 SCM path = all_commands_to_absolute_and_group (expr);
621 // note that expr has more stuff that we don't need after this - simply ignore it
622 //////////////////////
623 for (SCM s = path; scm_is_pair (s); s = scm_cdr (s))
625 scm_to_int (scm_length (scm_car (s))) == 4
626 ? make_draw_line_boxes (boxes, buildings, trans, scm_cons (blot, scm_car (s)), use_building)
627 : make_draw_bezier_boxes (boxes, buildings, trans, scm_cons (blot, scm_car (s)));
632 make_path_boxes (vector<Box> &boxes,
633 vector<Drul_array<Offset> > &buildings,
634 PangoMatrix trans, SCM expr)
636 return internal_make_path_boxes (boxes, buildings, trans, scm_cons (scm_car (expr), get_path_list (scm_cdr (expr))), false);
640 make_polygon_boxes (vector<Box> &boxes,
641 vector<Drul_array<Offset> > &buildings,
642 PangoMatrix trans, SCM expr)
644 SCM coords = get_number_list (scm_car (expr));
645 expr = scm_cdr (expr);
646 SCM blot_diameter = scm_car (expr);
647 //////////////////////
650 for (SCM s = coords; scm_is_pair (s); s = scm_cddr (s))
652 l = scm_cons (first ? ly_symbol2scm ("moveto") : ly_symbol2scm ("lineto"), l);
653 l = scm_cons (scm_car (s), l);
654 l = scm_cons (scm_cadr (s), l);
657 l = scm_cons (ly_symbol2scm ("closepath"), l);
658 internal_make_path_boxes (boxes, buildings, trans,
659 scm_cons (blot_diameter, scm_reverse_x (l, SCM_EOL)), true);
663 make_named_glyph_boxes (vector<Box> &boxes,
664 vector<Drul_array<Offset> > &buildings,
665 PangoMatrix trans, SCM expr)
667 SCM fm_scm = scm_car (expr);
668 Font_metric *fm = unsmob<Font_metric> (fm_scm);
669 expr = scm_cdr (expr);
670 SCM glyph = scm_car (expr);
671 string glyph_s = ly_scm2string (glyph);
673 //////////////////////
674 Open_type_font *open_fm
675 = dynamic_cast<Open_type_font *>
676 (dynamic_cast<Modified_font_metric *>(fm)->original_font ());
677 SCM_ASSERT_TYPE (open_fm, fm_scm, SCM_ARG1, __FUNCTION__, "OpenType font");
679 size_t gidx = open_fm->name_to_index (glyph_s);
680 // Bbox is the best approximation of the width based on how it would be
681 // calculated in open-type-font.cc if it were based on real extents
682 Box bbox = open_fm->get_unscaled_indexed_char_dimensions (gidx);
683 bbox.scale (dynamic_cast<Modified_font_metric *> (fm)->get_magnification ()
684 * open_fm->design_size () / open_fm->get_units_per_EM ());
685 // Real bbox is the real bbox of the object
686 Box real_bbox = open_fm->get_glyph_outline_bbox (gidx);
688 Real scale = bbox[X_AXIS].length () / real_bbox[X_AXIS].length ();
690 pango_matrix_scale (&trans, scale, scale);
692 SCM outline = open_fm->get_glyph_outline (gidx);
693 //////////////////////
694 for (SCM s = outline;
698 scm_to_int (scm_length (scm_car (s))) == 4
699 ? make_draw_line_boxes (boxes, buildings, trans,
700 scm_cons (scm_from_double (0), scm_car (s)),
702 : make_draw_bezier_boxes (boxes, buildings, trans,
703 scm_cons (scm_from_double (0), scm_car (s)));
708 make_glyph_string_boxes (vector<Box> &boxes,
709 vector<Drul_array<Offset> > &buildings,
710 PangoMatrix trans, SCM expr)
712 SCM fm_scm = scm_car (expr);
713 Font_metric *fm = unsmob<Font_metric> (fm_scm);
714 expr = scm_cdr (expr);
715 expr = scm_cdr (expr); // font-name
716 expr = scm_cdr (expr); // size
717 expr = scm_cdr (expr); // cid?
718 SCM whxy = scm_cadar (expr);
720 vector<Interval> heights;
723 vector<string> char_ids;
724 //////////////////////
725 Pango_font *pango_fm = dynamic_cast<Pango_font *> (fm);
726 SCM_ASSERT_TYPE (pango_fm, fm_scm, SCM_ARG1, __FUNCTION__, "Pango font");
728 for (SCM s = whxy; scm_is_pair (s); s = scm_cdr (s))
730 SCM now = scm_car (s);
731 widths.push_back (robust_scm2double (scm_car (now), 0.0));
733 heights.push_back (robust_scm2interval (scm_car (now), Interval (0, 0)));
735 xos.push_back (robust_scm2double (scm_car (now), 0.0));
737 yos.push_back (robust_scm2double (scm_car (now), 0.0));
739 char_ids.push_back (robust_scm2string (scm_car (now), ""));
741 Real cumulative_x = 0.0;
742 for (vsize i = 0; i < widths.size (); i++)
744 PangoMatrix transcopy (trans);
745 Offset pt0 (cumulative_x + xos[i], heights[i][DOWN] + yos[i]);
746 Offset pt1 (cumulative_x + widths[i] + xos[i], heights[i][UP] + yos[i]);
747 cumulative_x += widths[i];
750 kerned_bbox.add_point (pt0);
751 kerned_bbox.add_point (pt1);
752 size_t gidx = pango_fm->name_to_index (char_ids[i]);
753 Box real_bbox = pango_fm->get_scaled_indexed_char_dimensions (gidx);
754 Box bbox = pango_fm->get_unscaled_indexed_char_dimensions (gidx);
755 SCM outline = pango_fm->get_glyph_outline (gidx);
757 // scales may have rounding error but should be close
758 Real xlen = real_bbox[X_AXIS].length () / bbox[X_AXIS].length ();
759 Real ylen = real_bbox[Y_AXIS].length () / bbox[Y_AXIS].length ();
764 The value will be nan for whitespace, in which case we just want
765 filler, so the kerned bbox is ok.
767 However, if the value is inf, this likely means that LilyPond is
768 using a font that is currently difficult to get the measurements
769 from the Pango_font. This should eventually be fixed. The solution
770 for now is just to use the bounding box.
772 if (isnan (xlen) || isnan (ylen) || isinf (xlen) || isinf (ylen))
773 outline = box_to_scheme_lines (kerned_bbox);
776 assert (abs (xlen - ylen) < 10e-3);
778 Real scale_factor = max (xlen, ylen);
779 // the three operations below move the stencil from its original coordinates to current coordinates
780 pango_matrix_translate (&transcopy, kerned_bbox[X_AXIS][LEFT],
781 kerned_bbox[Y_AXIS][DOWN] - real_bbox[Y_AXIS][DOWN]);
782 pango_matrix_translate (&transcopy, real_bbox[X_AXIS][LEFT],
783 real_bbox[Y_AXIS][DOWN]);
784 pango_matrix_scale (&transcopy, scale_factor, scale_factor);
785 pango_matrix_translate (&transcopy, -bbox[X_AXIS][LEFT],
786 -bbox[Y_AXIS][DOWN]);
788 //////////////////////
789 for (SCM s = outline;
793 scm_to_int (scm_length (scm_car (s))) == 4
794 ? make_draw_line_boxes (boxes, buildings, transcopy,
795 scm_cons (scm_from_double (0), scm_car (s)),
797 : make_draw_bezier_boxes (boxes, buildings, transcopy,
798 scm_cons (scm_from_double (0), scm_car (s)));
804 receives a stencil expression and a transform matrix
805 depending on the stencil name, dispatches it to the appropriate function
809 stencil_dispatcher (vector<Box> &boxes,
810 vector<Drul_array<Offset> > &buildings,
811 PangoMatrix trans, SCM expr)
813 if (not scm_is_pair (expr))
815 if (scm_is_eq (scm_car (expr), ly_symbol2scm ("draw-line")))
816 make_draw_line_boxes (boxes, buildings, trans, scm_cdr (expr), true);
817 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("dashed-line")))
819 expr = scm_cdr (expr);
820 SCM th = scm_car (expr);
821 expr = scm_cdr (expr);
822 expr = scm_cdr (expr); // on
823 expr = scm_cdr (expr); // off
824 SCM x1 = scm_car (expr);
825 expr = scm_cdr (expr);
826 SCM x2 = scm_car (expr);
827 make_draw_line_boxes (boxes, buildings, trans,
828 scm_list_5 (th, scm_from_double (0.0),
829 scm_from_double (0.0), x1, x2), true);
831 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("circle")))
833 expr = scm_cdr (expr);
834 SCM rad = scm_car (expr);
835 expr = scm_cdr (expr);
836 SCM th = scm_car (expr);
837 make_partial_ellipse_boxes (boxes, buildings, trans,
840 scm_from_double (0.0),
841 scm_from_double (360.0),
847 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("ellipse")))
849 expr = scm_cdr (expr);
850 SCM x_rad = scm_car (expr);
851 expr = scm_cdr (expr);
852 SCM y_rad = scm_car (expr);
853 expr = scm_cdr (expr);
854 SCM th = scm_car (expr);
855 make_partial_ellipse_boxes (boxes, buildings, trans,
858 scm_from_double (0.0),
859 scm_from_double (360.0),
865 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("partial-ellipse")))
866 make_partial_ellipse_boxes (boxes, buildings, trans, scm_cdr (expr));
867 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("round-filled-box")))
868 make_round_filled_box_boxes (boxes, trans, scm_cdr (expr));
869 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("named-glyph")))
870 make_named_glyph_boxes (boxes, buildings, trans, scm_cdr (expr));
871 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("polygon")))
872 make_polygon_boxes (boxes, buildings, trans, scm_cdr (expr));
873 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("path")))
874 make_path_boxes (boxes, buildings, trans, scm_cdr (expr));
875 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("glyph-string")))
876 make_glyph_string_boxes (boxes, buildings, trans, scm_cdr (expr));
880 warning ("Stencil expression not supported by the veritcal skylines.");
883 We don't issue a warning here, as we assume that stencil-expression.cc
884 is doing stencil-checking correctly.
890 traverses a stencil expression, returning a vector of Transform_matrix_and_expression
891 the struct Transform_matrix_and_expression contains two members,
892 a Transform_matrix that indicates where to move a stencil and the stencil expression
893 to show how to construct the stencil
895 vector<Transform_matrix_and_expression>
896 stencil_traverser (PangoMatrix trans, SCM expr)
898 if (scm_is_null (expr))
899 return vector<Transform_matrix_and_expression> ();
900 else if (scm_is_eq (expr, ly_string2scm ("")))
901 return vector<Transform_matrix_and_expression> ();
902 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("combine-stencil")))
904 vector<Transform_matrix_and_expression> out;
905 for (SCM s = scm_cdr (expr); scm_is_pair (s); s = scm_cdr (s))
907 vector<Transform_matrix_and_expression> res =
908 stencil_traverser (trans, scm_car (s));
909 out.insert (out.end (), res.begin (), res.end ());
913 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("footnote")))
914 return vector<Transform_matrix_and_expression> ();
915 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("translate-stencil")))
917 Real x = robust_scm2double (scm_caadr (expr), 0.0);
918 Real y = robust_scm2double (scm_cdadr (expr), 0.0);
919 pango_matrix_translate (&trans, x, y);
920 return stencil_traverser (trans, scm_caddr (expr));
922 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("scale-stencil")))
924 Real x = robust_scm2double (scm_caadr (expr), 0.0);
925 Real y = robust_scm2double (scm_cadadr (expr), 0.0);
926 pango_matrix_scale (&trans, x, y);
927 return stencil_traverser (trans, scm_caddr (expr));
929 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("rotate-stencil")))
931 Real ang = robust_scm2double (scm_caadr (expr), 0.0);
932 Real x = robust_scm2double (scm_car (scm_cadadr (expr)), 0.0);
933 Real y = robust_scm2double (scm_cdr (scm_cadadr (expr)), 0.0);
934 pango_matrix_translate (&trans, x, y);
935 pango_matrix_rotate (&trans, -ang);
936 pango_matrix_translate (&trans, -x, -y);
937 return stencil_traverser (trans, scm_caddr (expr));
939 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("delay-stencil-evaluation")))
940 // should not use the place-holder text, but no need for the warning below
941 return vector<Transform_matrix_and_expression> ();
942 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("grob-cause")))
943 return stencil_traverser (trans, scm_caddr (expr));
944 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("color")))
945 return stencil_traverser (trans, scm_caddr (expr));
946 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("transparent-stencil")))
947 return stencil_traverser (trans, scm_cadr (expr));
948 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("id")))
949 return stencil_traverser (trans, scm_caddr (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);