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 ();
196 Offset outward = d * get_normal ((thick / 2) * dir);
197 Offset inter_l = left + outward;
198 Offset inter_r = right + outward;
199 pango_matrix_transform_point (&trans, &inter_l[X_AXIS], &inter_l[Y_AXIS]);
200 pango_matrix_transform_point (&trans, &inter_r[X_AXIS], &inter_r[Y_AXIS]);
201 if ((inter_l[X_AXIS] == inter_r[X_AXIS]) || (inter_l[Y_AXIS] == inter_r[Y_AXIS]))
204 b.add_point (inter_l);
205 b.add_point (inter_r);
208 else if (use_building)
209 buildings.push_back (Drul_array<Offset> (inter_l, inter_r));
212 Real length = (inter_l - inter_r).length ();
214 vsize passes = (vsize) ((length * 2) + 1);
215 vector<Offset> points;
217 for (vsize i = 0; i < 1 + passes; i++)
219 Offset pt (linear_map (x0, x1, 0, passes, i),
220 linear_map (y0, y1, 0, passes, i));
221 Offset inter = pt + outward;
222 pango_matrix_transform_point (&trans, &inter[X_AXIS], &inter[Y_AXIS]);
223 points.push_back (inter);
225 for (vsize i = 0; i < points.size () - 1; i++)
228 b.add_point (points[i]);
229 b.add_point (points[i + 1]);
234 while (flip (&d) != DOWN);
239 create_path_cap (boxes,
247 create_path_cap (boxes,
257 make_partial_ellipse_boxes (vector<Box> &boxes,
258 vector<Drul_array<Offset> > &buildings,
259 PangoMatrix trans, SCM expr)
261 Real x_rad = robust_scm2double (scm_car (expr), 0.0);
262 expr = scm_cdr (expr);
263 Real y_rad = robust_scm2double (scm_car (expr), 0.0);
264 expr = scm_cdr (expr);
265 Real start = robust_scm2double (scm_car (expr), 0.0);
266 expr = scm_cdr (expr);
267 Real end = robust_scm2double (scm_car (expr), 0.0);
268 expr = scm_cdr (expr);
269 Real th = robust_scm2double (scm_car (expr), 0.0);
270 expr = scm_cdr (expr);
271 bool connect = to_boolean (scm_car (expr));
272 expr = scm_cdr (expr);
273 bool fill = to_boolean (scm_car (expr));
274 //////////////////////
275 start = M_PI * start / 180;
276 end = M_PI * end / 180;
279 Offset sp (cos (start) * x_rad, sin (start) * y_rad);
280 Offset ep (cos (end) * x_rad, sin (end) * y_rad);
281 //////////////////////
282 Drul_array<vector<Offset> > points;
284 int quantization = max (1, (int) (((x_rad * trans.xx) + (y_rad * trans.yy)) * M_PI / QUANTIZATION_UNIT));
287 for (vsize i = 0; i < 1 + (vsize) quantization; i++)
289 Real ang = linear_map (start, end, 0, quantization, i);
290 Offset pt (cos (ang) * x_rad, sin (ang) * y_rad);
291 Offset inter = pt + d * get_normal ((th/2) * pt.direction ());
292 pango_matrix_transform_point (&trans, &inter[X_AXIS], &inter[Y_AXIS]);
293 points[d].push_back (inter);
296 while (flip (&d) != DOWN);
298 for (vsize i = 0; i < points[DOWN].size () - 1; i++)
303 b.add_point (points[d][i]);
304 b.add_point (points[d][i + 1]);
306 while (flip (&d) != DOWN);
312 make_draw_line_boxes (boxes, buildings, trans,
313 scm_list_5 (scm_from_double (th),
314 scm_from_double (sp[X_AXIS]),
315 scm_from_double (sp[Y_AXIS]),
316 scm_from_double (ep[X_AXIS]),
317 scm_from_double (ep[Y_AXIS])),
324 Offset pt (cos (start) * x_rad, sin (start) * y_rad);
325 create_path_cap (boxes,
333 pt = Offset (cos (end) * x_rad, sin (end) * y_rad);
334 create_path_cap (boxes,
344 make_round_filled_box_boxes (vector<Box> &boxes, PangoMatrix trans, SCM expr)
346 Real left = robust_scm2double (scm_car (expr), 0.0);
347 expr = scm_cdr (expr);
348 Real right = robust_scm2double (scm_car (expr), 0.0);
349 expr = scm_cdr (expr);
350 Real bottom = robust_scm2double (scm_car (expr), 0.0);
351 expr = scm_cdr (expr);
352 Real top = robust_scm2double (scm_car (expr), 0.0);
353 expr = scm_cdr (expr);
354 Real th = robust_scm2double (scm_car (expr), 0.0);
355 //////////////////////
356 vector<Offset> points;
358 Offset p0 = Offset (-left - (th / 2), -bottom - (th / 2));
359 Offset p1 = Offset (right + (th / 2), top + (th / 2));
360 pango_matrix_transform_point (&trans, &p0[X_AXIS], &p0[Y_AXIS]);
361 pango_matrix_transform_point (&trans, &p1[X_AXIS], &p1[Y_AXIS]);
368 create_path_cap (vector<Box> &boxes,
369 vector<Drul_array<Offset> > &buildings,
370 PangoMatrix trans, Offset pt, Real rad, Offset dir)
372 Real angle = dir.angle_degrees ();
373 PangoMatrix new_trans (trans);
374 pango_matrix_translate (&new_trans, pt[X_AXIS], pt[Y_AXIS]);
375 make_partial_ellipse_boxes (boxes, buildings, new_trans,
376 scm_list_n (scm_from_double (rad),
377 scm_from_double (rad),
378 scm_from_double (angle-90.01),
379 scm_from_double (angle+90.01),
380 scm_from_double (0.0),
387 make_draw_bezier_boxes (vector<Box> &boxes,
388 vector<Drul_array<Offset> > &buildings,
389 PangoMatrix trans, SCM expr)
391 Real th = robust_scm2double (scm_car (expr), 0.0);
392 expr = scm_cdr (expr);
393 Real x0 = robust_scm2double (scm_car (expr), 0.0);
394 expr = scm_cdr (expr);
395 Real y0 = robust_scm2double (scm_car (expr), 0.0);
396 expr = scm_cdr (expr);
397 Real x1 = robust_scm2double (scm_car (expr), 0.0);
398 expr = scm_cdr (expr);
399 Real y1 = robust_scm2double (scm_car (expr), 0.0);
400 expr = scm_cdr (expr);
401 Real x2 = robust_scm2double (scm_car (expr), 0.0);
402 expr = scm_cdr (expr);
403 Real y2 = robust_scm2double (scm_car (expr), 0.0);
404 expr = scm_cdr (expr);
405 Real x3 = robust_scm2double (scm_car (expr), 0.0);
406 expr = scm_cdr (expr);
407 Real y3 = robust_scm2double (scm_car (expr), 0.0);
408 //////////////////////
410 curve.control_[0] = Offset (x0, y0);
411 curve.control_[1] = Offset (x1, y1);
412 curve.control_[2] = Offset (x2, y2);
413 curve.control_[3] = Offset (x3, y3);
414 Offset temp0 (x0, y0);
415 Offset temp1 (x1, y1);
416 Offset temp2 (x2, y2);
417 Offset temp3 (x3, y3);
418 pango_matrix_transform_point (&trans, &temp0[X_AXIS], &temp0[Y_AXIS]);
419 pango_matrix_transform_point (&trans, &temp1[X_AXIS], &temp1[Y_AXIS]);
420 pango_matrix_transform_point (&trans, &temp2[X_AXIS], &temp2[Y_AXIS]);
421 pango_matrix_transform_point (&trans, &temp3[X_AXIS], &temp3[Y_AXIS]);
422 //////////////////////
423 Drul_array<vector<Offset> > points;
425 int quantization = int (((temp1 - temp0).length ()
426 + (temp2 - temp1).length ()
427 + (temp3 - temp2).length ())
428 / QUANTIZATION_UNIT);
431 Offset first = curve.control_[0]
432 + d * get_normal ((th / 2) * curve.dir_at_point (0.0));
433 pango_matrix_transform_point (&trans, &first[X_AXIS], &first[Y_AXIS]);
434 points[d].push_back (first);
435 for (vsize i = 1; i < (vsize) quantization; i++)
437 Real pt = (i * 1.0) / quantization;
438 Offset inter = curve.curve_point (pt)
439 + d * get_normal ((th / 2) *curve.dir_at_point (pt));
440 pango_matrix_transform_point (&trans, &inter[X_AXIS], &inter[Y_AXIS]);
441 points[d].push_back (inter);
443 Offset last = curve.control_[3]
444 + d * get_normal ((th / 2) * curve.dir_at_point (1.0));
445 pango_matrix_transform_point (&trans, &last[X_AXIS], &last[Y_AXIS]);
446 points[d].push_back (last);
448 while (flip (&d) != DOWN);
450 for (vsize i = 0; i < points[DOWN].size () - 1; i++)
455 b.add_point (points[d][i]);
456 b.add_point (points[d][i + 1]);
458 while (flip (&d) != DOWN);
465 create_path_cap (boxes,
470 -curve.dir_at_point (0.0));
473 create_path_cap (boxes,
478 curve.dir_at_point (1.0));
483 converts a path into lists of 4 (line) or 8 (curve) absolute coordinates
485 '(moveto 1 2 lineto 3 4 rlineto -1 -1 curveto
486 3 3 5 5 6 6 rcurveto -1 -1 -1 -1 -1 -1 closepath)
496 all_commands_to_absolute_and_group (SCM expr)
500 Offset current (0, 0);
502 while (scm_is_pair (expr))
504 if (scm_is_eq (scm_car (expr), ly_symbol2scm ("moveto"))
505 || (scm_is_eq (scm_car (expr), ly_symbol2scm ("rmoveto")) && first))
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);
511 expr = scm_cdddr (expr);
513 if (scm_is_eq (scm_car (expr), ly_symbol2scm ("rmoveto")))
515 Real x = robust_scm2double (scm_cadr (expr), 0.0);
516 Real y = robust_scm2double (scm_caddr (expr), 0.0);
517 start = (Offset (x, y) + current);
519 expr = scm_cdddr (expr);
521 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("lineto")))
523 Real x = robust_scm2double (scm_cadr (expr), 0.0);
524 Real y = robust_scm2double (scm_caddr (expr), 0.0);
525 out = scm_cons (scm_list_4 (scm_from_double (current[X_AXIS]),
526 scm_from_double (current[Y_AXIS]),
528 scm_from_double (y)),
530 current = Offset (x, y);
531 expr = scm_cdddr (expr);
533 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("rlineto")))
535 Real x = robust_scm2double (scm_cadr (expr), 0.0);
536 Real y = robust_scm2double (scm_caddr (expr), 0.0);
537 out = scm_cons (scm_list_4 (scm_from_double (current[X_AXIS]),
538 scm_from_double (current[Y_AXIS]),
539 scm_from_double (x + current[X_AXIS]),
540 scm_from_double (y + current[Y_AXIS])),
542 current = (Offset (x, y) + current);
543 expr = scm_cdddr (expr);
545 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("curveto")))
547 Real x1 = robust_scm2double (scm_cadr (expr), 0.0);
548 expr = scm_cddr (expr);
549 Real y1 = robust_scm2double (scm_car (expr), 0.0);
550 expr = scm_cdr (expr);
551 Real x2 = robust_scm2double (scm_car (expr), 0.0);
552 expr = scm_cdr (expr);
553 Real y2 = robust_scm2double (scm_car (expr), 0.0);
554 expr = scm_cdr (expr);
555 Real x3 = robust_scm2double (scm_car (expr), 0.0);
556 expr = scm_cdr (expr);
557 Real y3 = robust_scm2double (scm_car (expr), 0.0);
558 expr = scm_cdr (expr);
559 out = scm_cons (scm_list_n (scm_from_double (current[X_AXIS]),
560 scm_from_double (current[Y_AXIS]),
561 scm_from_double (x1),
562 scm_from_double (y1),
563 scm_from_double (x2),
564 scm_from_double (y2),
565 scm_from_double (x3),
566 scm_from_double (y3),
569 current = Offset (x3, y3);
571 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("rcurveto")))
573 Real x1 = robust_scm2double (scm_cadr (expr), 0.0);
574 expr = scm_cddr (expr);
575 Real y1 = robust_scm2double (scm_car (expr), 0.0);
576 expr = scm_cdr (expr);
577 Real x2 = robust_scm2double (scm_car (expr), 0.0);
578 expr = scm_cdr (expr);
579 Real y2 = robust_scm2double (scm_car (expr), 0.0);
580 expr = scm_cdr (expr);
581 Real x3 = robust_scm2double (scm_car (expr), 0.0);
582 expr = scm_cdr (expr);
583 Real y3 = robust_scm2double (scm_car (expr), 0.0);
584 expr = scm_cdr (expr);
585 out = scm_cons (scm_list_n (scm_from_double (current[X_AXIS]),
586 scm_from_double (current[Y_AXIS]),
587 scm_from_double (x1 + current[X_AXIS]),
588 scm_from_double (y1 + current[Y_AXIS]),
589 scm_from_double (x2 + current[X_AXIS]),
590 scm_from_double (y2 + current[Y_AXIS]),
591 scm_from_double (x3 + current[X_AXIS]),
592 scm_from_double (y3 + current[Y_AXIS]),
595 current = (Offset (x3, y3) + current);
597 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("closepath")))
599 if ((current[X_AXIS] != start[X_AXIS])
600 || (current[Y_AXIS] != start[Y_AXIS]))
602 out = scm_cons (scm_list_4 (scm_from_double (current[X_AXIS]),
603 scm_from_double (current[Y_AXIS]),
604 scm_from_double (start[X_AXIS]),
605 scm_from_double (start[Y_AXIS])),
609 expr = scm_cdr (expr);
613 warning ("Malformed path for path stencil.");
618 return scm_reverse_x (out, SCM_EOL);
622 internal_make_path_boxes (vector<Box> &boxes,
623 vector<Drul_array<Offset> > &buildings,
624 PangoMatrix trans, SCM expr, bool use_building)
626 SCM blot = scm_car (expr);
627 expr = scm_cdr (expr);
628 SCM path = all_commands_to_absolute_and_group (expr);
629 // note that expr has more stuff that we don't need after this - simply ignore it
630 //////////////////////
631 for (SCM s = path; scm_is_pair (s); s = scm_cdr (s))
633 scm_to_int (scm_length (scm_car (s))) == 4
634 ? make_draw_line_boxes (boxes, buildings, trans, scm_cons (blot, scm_car (s)), use_building)
635 : make_draw_bezier_boxes (boxes, buildings, trans, scm_cons (blot, scm_car (s)));
640 make_path_boxes (vector<Box> &boxes,
641 vector<Drul_array<Offset> > &buildings,
642 PangoMatrix trans, SCM expr)
644 return internal_make_path_boxes (boxes, buildings, trans, scm_cons (scm_car (expr), get_path_list (scm_cdr (expr))), false);
648 make_polygon_boxes (vector<Box> &boxes,
649 vector<Drul_array<Offset> > &buildings,
650 PangoMatrix trans, SCM expr)
652 SCM coords = get_number_list (scm_car (expr));
653 expr = scm_cdr (expr);
654 SCM blot_diameter = scm_car (expr);
655 //////////////////////
658 for (SCM s = coords; scm_is_pair (s); s = scm_cddr (s))
660 l = scm_cons (first ? ly_symbol2scm ("moveto") : ly_symbol2scm ("lineto"), l);
661 l = scm_cons (scm_car (s), l);
662 l = scm_cons (scm_cadr (s), l);
665 l = scm_cons (ly_symbol2scm ("closepath"), l);
666 internal_make_path_boxes (boxes, buildings, trans,
667 scm_cons (blot_diameter, scm_reverse_x (l, SCM_EOL)), true);
671 make_named_glyph_boxes (vector<Box> &boxes,
672 vector<Drul_array<Offset> > &buildings,
673 PangoMatrix trans, SCM expr)
675 SCM fm_scm = scm_car (expr);
676 Font_metric *fm = unsmob<Font_metric> (fm_scm);
677 expr = scm_cdr (expr);
678 SCM glyph = scm_car (expr);
679 string glyph_s = ly_scm2string (glyph);
681 //////////////////////
682 Open_type_font *open_fm
683 = dynamic_cast<Open_type_font *>
684 (dynamic_cast<Modified_font_metric *>(fm)->original_font ());
685 SCM_ASSERT_TYPE (open_fm, fm_scm, SCM_ARG1, __FUNCTION__, "OpenType font");
687 size_t gidx = open_fm->name_to_index (glyph_s);
688 // Bbox is the best approximation of the width based on how it would be
689 // calculated in open-type-font.cc if it were based on real extents
690 Box bbox = open_fm->get_unscaled_indexed_char_dimensions (gidx);
691 bbox.scale (dynamic_cast<Modified_font_metric *> (fm)->get_magnification ()
692 * open_fm->design_size () / open_fm->get_units_per_EM ());
693 // Real bbox is the real bbox of the object
694 Box real_bbox = open_fm->get_glyph_outline_bbox (gidx);
696 Real scale = bbox[X_AXIS].length () / real_bbox[X_AXIS].length ();
698 pango_matrix_scale (&trans, scale, scale);
700 SCM outline = open_fm->get_glyph_outline (gidx);
701 //////////////////////
702 for (SCM s = outline;
706 scm_to_int (scm_length (scm_car (s))) == 4
707 ? make_draw_line_boxes (boxes, buildings, trans,
708 scm_cons (scm_from_double (0), scm_car (s)),
710 : make_draw_bezier_boxes (boxes, buildings, trans,
711 scm_cons (scm_from_double (0), scm_car (s)));
716 make_glyph_string_boxes (vector<Box> &boxes,
717 vector<Drul_array<Offset> > &buildings,
718 PangoMatrix trans, SCM expr)
720 SCM fm_scm = scm_car (expr);
721 Font_metric *fm = unsmob<Font_metric> (fm_scm);
722 expr = scm_cdr (expr);
723 expr = scm_cdr (expr); // font-name
724 expr = scm_cdr (expr); // size
725 expr = scm_cdr (expr); // cid?
726 SCM whxy = scm_cadar (expr);
728 vector<Interval> heights;
731 vector<string> char_ids;
732 //////////////////////
733 Pango_font *pango_fm = dynamic_cast<Pango_font *> (fm);
734 SCM_ASSERT_TYPE (pango_fm, fm_scm, SCM_ARG1, __FUNCTION__, "Pango font");
736 for (SCM s = whxy; scm_is_pair (s); s = scm_cdr (s))
738 SCM now = scm_car (s);
739 widths.push_back (robust_scm2double (scm_car (now), 0.0));
741 heights.push_back (robust_scm2interval (scm_car (now), Interval (0, 0)));
743 xos.push_back (robust_scm2double (scm_car (now), 0.0));
745 yos.push_back (robust_scm2double (scm_car (now), 0.0));
747 char_ids.push_back (robust_scm2string (scm_car (now), ""));
749 Real cumulative_x = 0.0;
750 for (vsize i = 0; i < widths.size (); i++)
752 PangoMatrix transcopy (trans);
753 Offset pt0 (cumulative_x + xos[i], heights[i][DOWN] + yos[i]);
754 Offset pt1 (cumulative_x + widths[i] + xos[i], heights[i][UP] + yos[i]);
755 cumulative_x += widths[i];
758 kerned_bbox.add_point (pt0);
759 kerned_bbox.add_point (pt1);
760 size_t gidx = pango_fm->name_to_index (char_ids[i]);
761 Box real_bbox = pango_fm->get_scaled_indexed_char_dimensions (gidx);
762 Box bbox = pango_fm->get_unscaled_indexed_char_dimensions (gidx);
763 SCM outline = pango_fm->get_glyph_outline (gidx);
765 // scales may have rounding error but should be close
766 Real xlen = real_bbox[X_AXIS].length () / bbox[X_AXIS].length ();
767 Real ylen = real_bbox[Y_AXIS].length () / bbox[Y_AXIS].length ();
772 The value will be nan for whitespace, in which case we just want
773 filler, so the kerned bbox is ok.
775 However, if the value is inf, this likely means that LilyPond is
776 using a font that is currently difficult to get the measurements
777 from the Pango_font. This should eventually be fixed. The solution
778 for now is just to use the bounding box.
780 if (isnan (xlen) || isnan (ylen) || isinf (xlen) || isinf (ylen))
781 outline = box_to_scheme_lines (kerned_bbox);
784 assert (abs (xlen - ylen) < 10e-3);
786 Real scale_factor = max (xlen, ylen);
787 // the three operations below move the stencil from its original coordinates to current coordinates
788 pango_matrix_translate (&transcopy, kerned_bbox[X_AXIS][LEFT],
789 kerned_bbox[Y_AXIS][DOWN] - real_bbox[Y_AXIS][DOWN]);
790 pango_matrix_translate (&transcopy, real_bbox[X_AXIS][LEFT],
791 real_bbox[Y_AXIS][DOWN]);
792 pango_matrix_scale (&transcopy, scale_factor, scale_factor);
793 pango_matrix_translate (&transcopy, -bbox[X_AXIS][LEFT],
794 -bbox[Y_AXIS][DOWN]);
796 //////////////////////
797 for (SCM s = outline;
801 scm_to_int (scm_length (scm_car (s))) == 4
802 ? make_draw_line_boxes (boxes, buildings, transcopy,
803 scm_cons (scm_from_double (0), scm_car (s)),
805 : make_draw_bezier_boxes (boxes, buildings, transcopy,
806 scm_cons (scm_from_double (0), scm_car (s)));
812 receives a stencil expression and a transform matrix
813 depending on the stencil name, dispatches it to the appropriate function
817 stencil_dispatcher (vector<Box> &boxes,
818 vector<Drul_array<Offset> > &buildings,
819 PangoMatrix trans, SCM expr)
821 if (not scm_is_pair (expr))
823 if (scm_is_eq (scm_car (expr), ly_symbol2scm ("draw-line")))
824 make_draw_line_boxes (boxes, buildings, trans, scm_cdr (expr), true);
825 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("dashed-line")))
827 expr = scm_cdr (expr);
828 SCM th = scm_car (expr);
829 expr = scm_cdr (expr);
830 expr = scm_cdr (expr); // on
831 expr = scm_cdr (expr); // off
832 SCM x1 = scm_car (expr);
833 expr = scm_cdr (expr);
834 SCM x2 = scm_car (expr);
835 make_draw_line_boxes (boxes, buildings, trans,
836 scm_list_5 (th, scm_from_double (0.0),
837 scm_from_double (0.0), x1, x2), true);
839 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("circle")))
841 expr = scm_cdr (expr);
842 SCM rad = scm_car (expr);
843 expr = scm_cdr (expr);
844 SCM th = scm_car (expr);
845 make_partial_ellipse_boxes (boxes, buildings, trans,
848 scm_from_double (0.0),
849 scm_from_double (360.0),
855 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("ellipse")))
857 expr = scm_cdr (expr);
858 SCM x_rad = scm_car (expr);
859 expr = scm_cdr (expr);
860 SCM y_rad = scm_car (expr);
861 expr = scm_cdr (expr);
862 SCM th = scm_car (expr);
863 make_partial_ellipse_boxes (boxes, buildings, trans,
866 scm_from_double (0.0),
867 scm_from_double (360.0),
873 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("partial-ellipse")))
874 make_partial_ellipse_boxes (boxes, buildings, trans, scm_cdr (expr));
875 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("round-filled-box")))
876 make_round_filled_box_boxes (boxes, trans, scm_cdr (expr));
877 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("named-glyph")))
878 make_named_glyph_boxes (boxes, buildings, trans, scm_cdr (expr));
879 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("polygon")))
880 make_polygon_boxes (boxes, buildings, trans, scm_cdr (expr));
881 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("path")))
882 make_path_boxes (boxes, buildings, trans, scm_cdr (expr));
883 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("glyph-string")))
884 make_glyph_string_boxes (boxes, buildings, trans, scm_cdr (expr));
888 warning ("Stencil expression not supported by the veritcal skylines.");
891 We don't issue a warning here, as we assume that stencil-expression.cc
892 is doing stencil-checking correctly.
898 traverses a stencil expression, returning a vector of Transform_matrix_and_expression
899 the struct Transform_matrix_and_expression contains two members,
900 a Transform_matrix that indicates where to move a stencil and the stencil expression
901 to show how to construct the stencil
903 vector<Transform_matrix_and_expression>
904 stencil_traverser (PangoMatrix trans, SCM expr)
906 if (scm_is_null (expr))
907 return vector<Transform_matrix_and_expression> ();
908 else if (scm_is_eq (expr, ly_string2scm ("")))
909 return vector<Transform_matrix_and_expression> ();
910 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("combine-stencil")))
912 vector<Transform_matrix_and_expression> out;
913 for (SCM s = scm_cdr (expr); scm_is_pair (s); s = scm_cdr (s))
915 vector<Transform_matrix_and_expression> res =
916 stencil_traverser (trans, scm_car (s));
917 out.insert (out.end (), res.begin (), res.end ());
921 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("footnote")))
922 return vector<Transform_matrix_and_expression> ();
923 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("translate-stencil")))
925 Real x = robust_scm2double (scm_caadr (expr), 0.0);
926 Real y = robust_scm2double (scm_cdadr (expr), 0.0);
927 pango_matrix_translate (&trans, x, y);
928 return stencil_traverser (trans, scm_caddr (expr));
930 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("scale-stencil")))
932 Real x = robust_scm2double (scm_caadr (expr), 0.0);
933 Real y = robust_scm2double (scm_cadadr (expr), 0.0);
934 pango_matrix_scale (&trans, x, y);
935 return stencil_traverser (trans, scm_caddr (expr));
937 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("rotate-stencil")))
939 Real ang = robust_scm2double (scm_caadr (expr), 0.0);
940 Real x = robust_scm2double (scm_car (scm_cadadr (expr)), 0.0);
941 Real y = robust_scm2double (scm_cdr (scm_cadadr (expr)), 0.0);
942 pango_matrix_translate (&trans, x, y);
943 pango_matrix_rotate (&trans, -ang);
944 pango_matrix_translate (&trans, -x, -y);
945 return stencil_traverser (trans, scm_caddr (expr));
947 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("delay-stencil-evaluation")))
948 // should not use the place-holder text, but no need for the warning below
949 return vector<Transform_matrix_and_expression> ();
950 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("grob-cause")))
951 return stencil_traverser (trans, scm_caddr (expr));
952 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("color")))
953 return stencil_traverser (trans, scm_caddr (expr));
954 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("transparent-stencil")))
955 return stencil_traverser (trans, scm_cadr (expr));
956 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("id")))
957 return stencil_traverser (trans, scm_caddr (expr));
960 vector<Transform_matrix_and_expression> out;
961 out.push_back (Transform_matrix_and_expression (trans, expr));
964 warning ("Stencil expression not supported by the veritcal skylines.");
965 return vector<Transform_matrix_and_expression> ();
969 Grob::maybe_pure_internal_simple_skylines_from_extents (Grob *me, Axis a, bool pure, int beg, int end, bool ignore_x, bool ignore_y)
972 // we don't know how far spanners stretch along the X axis before
973 // line breaking. better have them take up the whole thing
974 Interval xex = ignore_x
975 ? Interval (-infinity_f, infinity_f)
976 : me->extent (me, X_AXIS);
978 // If we're looking at the x exent of a cross staff grob, it could be
979 // very early on in the computation process. We won't know its height
980 // until way later, so we give a brute force approximation.
981 Interval yex = ignore_y
982 ? Interval (-infinity_f, infinity_f)
983 : me->maybe_pure_extent (me, Y_AXIS, pure, beg, end);
985 if (xex.is_empty () || yex.is_empty ())
986 return Skyline_pair ().smobbed_copy ();
988 boxes.push_back (Box (xex, yex));
989 return Skyline_pair (boxes, a).smobbed_copy ();
992 MAKE_SCHEME_CALLBACK (Grob, pure_simple_vertical_skylines_from_extents, 3);
994 Grob::pure_simple_vertical_skylines_from_extents (SCM smob, SCM begscm, SCM endscm)
996 Grob *me = unsmob<Grob> (smob);
997 int beg = robust_scm2int (begscm, 0);
998 int end = robust_scm2int (endscm, INT_MAX);
999 // We cannot measure the widths before line breaking,
1000 // so we assume that the width is infinite: pass ignore_x=true
1001 return maybe_pure_internal_simple_skylines_from_extents (me, X_AXIS, true, beg, end, true, false);
1004 MAKE_SCHEME_CALLBACK (Grob, simple_vertical_skylines_from_extents, 1);
1006 Grob::simple_vertical_skylines_from_extents (SCM smob)
1008 Grob *me = unsmob<Grob> (smob);
1009 return maybe_pure_internal_simple_skylines_from_extents (me, X_AXIS, false, 0, 0, false, false);
1012 MAKE_SCHEME_CALLBACK (Grob, pure_simple_horizontal_skylines_from_extents, 3);
1014 Grob::pure_simple_horizontal_skylines_from_extents (SCM smob, SCM begscm, SCM endscm)
1016 Grob *me = unsmob<Grob> (smob);
1017 int beg = robust_scm2int (begscm, 0);
1018 int end = robust_scm2int (endscm, INT_MAX);
1019 // If the grob is cross staff, we cannot measure its Y-extent before
1020 // wayyyy downstream (after spacing of axis groups is done).
1021 // Thus, we assume that the Y extent is infinite for cross staff grobs.
1022 return maybe_pure_internal_simple_skylines_from_extents (me, Y_AXIS, true, beg, end, false, to_boolean (me->get_property ("cross-staff")));
1025 MAKE_SCHEME_CALLBACK (Grob, simple_horizontal_skylines_from_extents, 1);
1027 Grob::simple_horizontal_skylines_from_extents (SCM smob)
1029 Grob *me = unsmob<Grob> (smob);
1030 // See comment in function above.
1031 return maybe_pure_internal_simple_skylines_from_extents (me, Y_AXIS, false, 0, 0, false, to_boolean (me->get_property ("cross-staff")));
1035 Stencil::skylines_from_stencil (SCM sten, Real pad, Axis a)
1037 Stencil *s = unsmob<Stencil> (sten);
1039 return Skyline_pair ().smobbed_copy ();
1041 vector<Transform_matrix_and_expression> data
1042 = stencil_traverser (make_transform_matrix (1.0, 0.0, 0.0, 1.0, 0.0, 0.0),
1045 vector<Drul_array<Offset> > buildings;
1046 for (vsize i = 0; i < data.size (); i++)
1047 stencil_dispatcher (boxes, buildings, data[i].tm_, data[i].expr_);
1049 // we use the bounding box if there are no boxes
1050 if (!boxes.size () && !buildings.size ())
1051 boxes.push_back (Box (s->extent (X_AXIS), s->extent (Y_AXIS)));
1053 Skyline_pair out (boxes, a);
1054 out.merge (Skyline_pair (buildings, a));
1056 for (DOWN_and_UP (d))
1057 out[d] = out[d].padded (pad);
1059 return out.smobbed_copy ();
1062 MAKE_SCHEME_CALLBACK (Grob, vertical_skylines_from_stencil, 1);
1064 Grob::vertical_skylines_from_stencil (SCM smob)
1066 Grob *me = unsmob<Grob> (smob);
1068 Real pad = robust_scm2double (me->get_property ("skyline-horizontal-padding"), 0.0);
1069 SCM out = Stencil::skylines_from_stencil (me->get_property ("stencil"), pad, X_AXIS);
1074 MAKE_SCHEME_CALLBACK (Grob, horizontal_skylines_from_stencil, 1);
1076 Grob::horizontal_skylines_from_stencil (SCM smob)
1078 Grob *me = unsmob<Grob> (smob);
1080 Real pad = robust_scm2double (me->get_property ("skyline-vertical-padding"), 0.0);
1081 SCM out = Stencil::skylines_from_stencil (me->get_property ("stencil"), pad, Y_AXIS);
1087 Grob::internal_skylines_from_element_stencils (Grob *me, Axis a, bool pure, int beg, int end)
1090 extract_grob_set (me, "elements", elts);
1093 Grob *x_common = common_refpoint_of_array (elts, me, X_AXIS);
1094 Grob *y_common = common_refpoint_of_array (elts, me, Y_AXIS);
1095 for (vsize i = 0; i < elts.size (); i++)
1097 x_pos.push_back (elts[i]->relative_coordinate (x_common, X_AXIS));
1098 y_pos.push_back (elts[i]->maybe_pure_coordinate (y_common, Y_AXIS, pure, beg, end));
1100 Real my_x = me->relative_coordinate (x_common, X_AXIS);
1101 Real my_y = me->maybe_pure_coordinate (y_common, Y_AXIS, pure, beg, end);
1104 for (vsize i = 0; i < elts.size (); i++)
1106 Skyline_pair *skyp = unsmob<Skyline_pair> (elts[i]->get_maybe_pure_property (a == X_AXIS ? "vertical-skylines" : "horizontal-skylines", pure, beg, end));
1110 Here, copying is essential. Otherwise, the skyline pair will
1114 It took Mike about 6 months of his life to add the `else' clause
1115 below. For horizontal skylines, the raise and shift calls need
1116 to be reversed. This is what was causing the problems in the
1117 shifting with all of the tests. RIP 6 months!
1119 Skyline_pair copy = Skyline_pair (*skyp);
1122 copy.shift (x_pos[i] - my_x);
1123 copy.raise (y_pos[i] - my_y);
1127 copy.raise (x_pos[i] - my_x);
1128 copy.shift (y_pos[i] - my_y);
1133 return res.smobbed_copy ();
1136 MAKE_SCHEME_CALLBACK (Grob, vertical_skylines_from_element_stencils, 1);
1138 Grob::vertical_skylines_from_element_stencils (SCM smob)
1140 Grob *me = unsmob<Grob> (smob);
1141 return internal_skylines_from_element_stencils (me, X_AXIS, false, 0, INT_MAX);
1144 MAKE_SCHEME_CALLBACK (Grob, horizontal_skylines_from_element_stencils, 1);
1146 Grob::horizontal_skylines_from_element_stencils (SCM smob)
1148 Grob *me = unsmob<Grob> (smob);
1149 return internal_skylines_from_element_stencils (me, Y_AXIS, false, 0, INT_MAX);
1152 MAKE_SCHEME_CALLBACK (Grob, pure_vertical_skylines_from_element_stencils, 3);
1154 Grob::pure_vertical_skylines_from_element_stencils (SCM smob, SCM beg_scm, SCM end_scm)
1156 Grob *me = unsmob<Grob> (smob);
1157 int beg = robust_scm2int (beg_scm, 0);
1158 int end = robust_scm2int (end_scm, 0);
1159 return internal_skylines_from_element_stencils (me, X_AXIS, true, beg, end);
1162 MAKE_SCHEME_CALLBACK (Grob, pure_horizontal_skylines_from_element_stencils, 3);
1164 Grob::pure_horizontal_skylines_from_element_stencils (SCM smob, SCM beg_scm, SCM end_scm)
1166 Grob *me = unsmob<Grob> (smob);
1167 int beg = robust_scm2int (beg_scm, 0);
1168 int end = robust_scm2int (end_scm, 0);
1169 return internal_skylines_from_element_stencils (me, Y_AXIS, true, beg, end);