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"
62 Real QUANTIZATION_UNIT = 0.2;
64 void create_path_cap (vector<Box> &boxes,
65 vector<Drul_array<Offset> > &buildings,
66 PangoMatrix trans, Offset pt, Real rad, Offset dir);
68 struct Transform_matrix_and_expression
73 Transform_matrix_and_expression (PangoMatrix tm, SCM expr);
76 Transform_matrix_and_expression::Transform_matrix_and_expression (PangoMatrix tm, SCM expr)
83 make_transform_matrix (Real p0, Real p1, Real p2, Real p3, Real p4, Real p5)
95 //// UTILITY FUNCTIONS
98 map x's placement between orig_l and orig_r onto
99 the interval final_l final_r
102 linear_map (Real final_l, Real final_r, Real orig_l, Real orig_r, Real x)
104 return final_l + ((final_r - final_l) * ((x - orig_l) / (orig_r - orig_l)));
108 from a nested SCM list, return the first list of numbers
112 get_number_list (SCM l)
116 if (scm_is_number (scm_car (l)))
118 SCM res = get_number_list (scm_car (l));
119 if (scm_is_false (res))
120 return get_number_list (scm_cdr (l));
127 from a nested SCM list, return the first list of numbers
131 get_path_list (SCM l)
135 if (scm_is_true (scm_memv (scm_car (l),
136 scm_list_n (ly_symbol2scm ("moveto"),
137 ly_symbol2scm ("rmoveto"),
138 ly_symbol2scm ("lineto"),
139 ly_symbol2scm ("rlineto"),
140 ly_symbol2scm ("curveto"),
141 ly_symbol2scm ("rcurveto"),
142 ly_symbol2scm ("closepath"),
145 SCM res = get_path_list (scm_car (l));
146 if (scm_is_false (res))
147 return get_path_list (scm_cdr (l));
153 // Gets an orthogonal vector with same size to orig, pointing left
154 // (in the complex domain, a multiplication by i)
157 get_normal (Offset orig)
159 return Offset (-orig[Y_AXIS], orig[X_AXIS]);
162 //// END UTILITY FUNCTIONS
165 below, for all of the functions make_X_boxes, the expression
166 is always unpacked into variables.
167 then, after a line of /////, there are manipulations of these variables
168 (there may be no manipulations necessary depending on the function)
169 afterwards, there is another ///// followed by the creation of points
174 make_draw_line_boxes (vector<Box> &boxes, vector<Drul_array<Offset> > &buildings, PangoMatrix trans, SCM expr, bool use_building)
176 Real thick = robust_scm2double (scm_car (expr), 0.0);
177 expr = scm_cdr (expr);
178 Real x0 = robust_scm2double (scm_car (expr), 0.0);
179 expr = scm_cdr (expr);
180 Real y0 = robust_scm2double (scm_car (expr), 0.0);
181 expr = scm_cdr (expr);
182 Real x1 = robust_scm2double (scm_car (expr), 0.0);
183 expr = scm_cdr (expr);
184 Real y1 = robust_scm2double (scm_car (expr), 0.0);
186 //////////////////////
192 Offset left (x0, y0);
193 Offset right (x1, y1);
194 Offset dir = (right - left).direction ();
195 for (DOWN_and_UP (d))
197 Offset outward = d * get_normal ((thick / 2) * dir);
198 Offset inter_l = left + outward;
199 Offset inter_r = right + outward;
200 pango_matrix_transform_point (&trans, &inter_l[X_AXIS], &inter_l[Y_AXIS]);
201 pango_matrix_transform_point (&trans, &inter_r[X_AXIS], &inter_r[Y_AXIS]);
202 if ((inter_l[X_AXIS] == inter_r[X_AXIS]) || (inter_l[Y_AXIS] == inter_r[Y_AXIS]))
205 b.add_point (inter_l);
206 b.add_point (inter_r);
209 else if (use_building)
210 buildings.push_back (Drul_array<Offset> (inter_l, inter_r));
213 Real length = (inter_l - inter_r).length ();
215 vsize passes = (vsize) ((length * 2) + 1);
216 vector<Offset> points;
218 for (vsize i = 0; i < 1 + passes; i++)
220 Offset pt (linear_map (x0, x1, 0, passes, i),
221 linear_map (y0, y1, 0, passes, i));
222 Offset inter = pt + outward;
223 pango_matrix_transform_point (&trans, &inter[X_AXIS], &inter[Y_AXIS]);
224 points.push_back (inter);
226 for (vsize i = 0; i < points.size () - 1; i++)
229 b.add_point (points[i]);
230 b.add_point (points[i + 1]);
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;
283 int quantization = std::max (1, (int) (((x_rad * trans.xx) + (y_rad * trans.yy)) * M_PI / QUANTIZATION_UNIT));
284 for (DOWN_and_UP (d))
286 for (vsize i = 0; i < 1 + (vsize) quantization; i++)
288 Real ang = linear_map (start, end, 0, quantization, i);
289 Offset pt (cos (ang) * x_rad, sin (ang) * y_rad);
290 Offset inter = pt + d * get_normal ((th/2) * pt.direction ());
291 pango_matrix_transform_point (&trans, &inter[X_AXIS], &inter[Y_AXIS]);
292 points[d].push_back (inter);
296 for (vsize i = 0; i < points[DOWN].size () - 1; i++)
299 for (DOWN_and_UP (d))
301 b.add_point (points[d][i]);
302 b.add_point (points[d][i + 1]);
309 make_draw_line_boxes (boxes, buildings, trans,
310 scm_list_5 (scm_from_double (th),
311 scm_from_double (sp[X_AXIS]),
312 scm_from_double (sp[Y_AXIS]),
313 scm_from_double (ep[X_AXIS]),
314 scm_from_double (ep[Y_AXIS])),
321 Offset pt (cos (start) * x_rad, sin (start) * y_rad);
322 create_path_cap (boxes,
330 pt = Offset (cos (end) * x_rad, sin (end) * y_rad);
331 create_path_cap (boxes,
341 make_round_filled_box_boxes (vector<Box> &boxes, PangoMatrix trans, SCM expr)
343 Real left = robust_scm2double (scm_car (expr), 0.0);
344 expr = scm_cdr (expr);
345 Real right = robust_scm2double (scm_car (expr), 0.0);
346 expr = scm_cdr (expr);
347 Real bottom = robust_scm2double (scm_car (expr), 0.0);
348 expr = scm_cdr (expr);
349 Real top = robust_scm2double (scm_car (expr), 0.0);
350 expr = scm_cdr (expr);
351 Real th = robust_scm2double (scm_car (expr), 0.0);
352 //////////////////////
353 vector<Offset> points;
355 Offset p0 = Offset (-left - (th / 2), -bottom - (th / 2));
356 Offset p1 = Offset (right + (th / 2), top + (th / 2));
357 pango_matrix_transform_point (&trans, &p0[X_AXIS], &p0[Y_AXIS]);
358 pango_matrix_transform_point (&trans, &p1[X_AXIS], &p1[Y_AXIS]);
365 create_path_cap (vector<Box> &boxes,
366 vector<Drul_array<Offset> > &buildings,
367 PangoMatrix trans, Offset pt, Real rad, Offset dir)
369 Real angle = dir.angle_degrees ();
370 PangoMatrix new_trans (trans);
371 pango_matrix_translate (&new_trans, pt[X_AXIS], pt[Y_AXIS]);
372 make_partial_ellipse_boxes (boxes, buildings, new_trans,
373 scm_list_n (scm_from_double (rad),
374 scm_from_double (rad),
375 scm_from_double (angle-90.01),
376 scm_from_double (angle+90.01),
377 scm_from_double (0.0),
384 make_draw_bezier_boxes (vector<Box> &boxes,
385 vector<Drul_array<Offset> > &buildings,
386 PangoMatrix trans, SCM expr)
388 Real th = robust_scm2double (scm_car (expr), 0.0);
389 expr = scm_cdr (expr);
390 Real x0 = robust_scm2double (scm_car (expr), 0.0);
391 expr = scm_cdr (expr);
392 Real y0 = robust_scm2double (scm_car (expr), 0.0);
393 expr = scm_cdr (expr);
394 Real x1 = robust_scm2double (scm_car (expr), 0.0);
395 expr = scm_cdr (expr);
396 Real y1 = robust_scm2double (scm_car (expr), 0.0);
397 expr = scm_cdr (expr);
398 Real x2 = robust_scm2double (scm_car (expr), 0.0);
399 expr = scm_cdr (expr);
400 Real y2 = robust_scm2double (scm_car (expr), 0.0);
401 expr = scm_cdr (expr);
402 Real x3 = robust_scm2double (scm_car (expr), 0.0);
403 expr = scm_cdr (expr);
404 Real y3 = robust_scm2double (scm_car (expr), 0.0);
405 //////////////////////
407 curve.control_[0] = Offset (x0, y0);
408 curve.control_[1] = Offset (x1, y1);
409 curve.control_[2] = Offset (x2, y2);
410 curve.control_[3] = Offset (x3, y3);
411 Offset temp0 (x0, y0);
412 Offset temp1 (x1, y1);
413 Offset temp2 (x2, y2);
414 Offset temp3 (x3, y3);
415 pango_matrix_transform_point (&trans, &temp0[X_AXIS], &temp0[Y_AXIS]);
416 pango_matrix_transform_point (&trans, &temp1[X_AXIS], &temp1[Y_AXIS]);
417 pango_matrix_transform_point (&trans, &temp2[X_AXIS], &temp2[Y_AXIS]);
418 pango_matrix_transform_point (&trans, &temp3[X_AXIS], &temp3[Y_AXIS]);
419 //////////////////////
420 Drul_array<vector<Offset> > points;
421 int quantization = int (((temp1 - temp0).length ()
422 + (temp2 - temp1).length ()
423 + (temp3 - temp2).length ())
424 / QUANTIZATION_UNIT);
425 for (DOWN_and_UP (d))
427 Offset first = curve.control_[0]
428 + d * get_normal ((th / 2) * curve.dir_at_point (0.0));
429 pango_matrix_transform_point (&trans, &first[X_AXIS], &first[Y_AXIS]);
430 points[d].push_back (first);
431 for (vsize i = 1; i < (vsize) quantization; i++)
433 Real pt = (i * 1.0) / quantization;
434 Offset inter = curve.curve_point (pt)
435 + d * get_normal ((th / 2) *curve.dir_at_point (pt));
436 pango_matrix_transform_point (&trans, &inter[X_AXIS], &inter[Y_AXIS]);
437 points[d].push_back (inter);
439 Offset last = curve.control_[3]
440 + d * get_normal ((th / 2) * curve.dir_at_point (1.0));
441 pango_matrix_transform_point (&trans, &last[X_AXIS], &last[Y_AXIS]);
442 points[d].push_back (last);
445 for (vsize i = 0; i < points[DOWN].size () - 1; i++)
448 for (DOWN_and_UP (d))
450 b.add_point (points[d][i]);
451 b.add_point (points[d][i + 1]);
459 create_path_cap (boxes,
464 -curve.dir_at_point (0.0));
467 create_path_cap (boxes,
472 curve.dir_at_point (1.0));
477 converts a path into lists of 4 (line) or 8 (curve) absolute coordinates
479 '(moveto 1 2 lineto 3 4 rlineto -1 -1 curveto
480 3 3 5 5 6 6 rcurveto -1 -1 -1 -1 -1 -1 closepath)
490 all_commands_to_absolute_and_group (SCM expr)
494 Offset current (0, 0);
496 while (scm_is_pair (expr))
498 if (scm_is_eq (scm_car (expr), ly_symbol2scm ("moveto"))
499 || (scm_is_eq (scm_car (expr), ly_symbol2scm ("rmoveto")) && first))
501 Real x = robust_scm2double (scm_cadr (expr), 0.0);
502 Real y = robust_scm2double (scm_caddr (expr), 0.0);
503 start = Offset (x, y);
505 expr = scm_cdddr (expr);
507 if (scm_is_eq (scm_car (expr), ly_symbol2scm ("rmoveto")))
509 Real x = robust_scm2double (scm_cadr (expr), 0.0);
510 Real y = robust_scm2double (scm_caddr (expr), 0.0);
511 start = (Offset (x, y) + current);
513 expr = scm_cdddr (expr);
515 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("lineto")))
517 Real x = robust_scm2double (scm_cadr (expr), 0.0);
518 Real y = robust_scm2double (scm_caddr (expr), 0.0);
519 out = scm_cons (scm_list_4 (scm_from_double (current[X_AXIS]),
520 scm_from_double (current[Y_AXIS]),
522 scm_from_double (y)),
524 current = Offset (x, y);
525 expr = scm_cdddr (expr);
527 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("rlineto")))
529 Real x = robust_scm2double (scm_cadr (expr), 0.0);
530 Real y = robust_scm2double (scm_caddr (expr), 0.0);
531 out = scm_cons (scm_list_4 (scm_from_double (current[X_AXIS]),
532 scm_from_double (current[Y_AXIS]),
533 scm_from_double (x + current[X_AXIS]),
534 scm_from_double (y + current[Y_AXIS])),
536 current = (Offset (x, y) + current);
537 expr = scm_cdddr (expr);
539 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("curveto")))
541 Real x1 = robust_scm2double (scm_cadr (expr), 0.0);
542 expr = scm_cddr (expr);
543 Real y1 = robust_scm2double (scm_car (expr), 0.0);
544 expr = scm_cdr (expr);
545 Real x2 = robust_scm2double (scm_car (expr), 0.0);
546 expr = scm_cdr (expr);
547 Real y2 = robust_scm2double (scm_car (expr), 0.0);
548 expr = scm_cdr (expr);
549 Real x3 = robust_scm2double (scm_car (expr), 0.0);
550 expr = scm_cdr (expr);
551 Real y3 = robust_scm2double (scm_car (expr), 0.0);
552 expr = scm_cdr (expr);
553 out = scm_cons (scm_list_n (scm_from_double (current[X_AXIS]),
554 scm_from_double (current[Y_AXIS]),
555 scm_from_double (x1),
556 scm_from_double (y1),
557 scm_from_double (x2),
558 scm_from_double (y2),
559 scm_from_double (x3),
560 scm_from_double (y3),
563 current = Offset (x3, y3);
565 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("rcurveto")))
567 Real x1 = robust_scm2double (scm_cadr (expr), 0.0);
568 expr = scm_cddr (expr);
569 Real y1 = robust_scm2double (scm_car (expr), 0.0);
570 expr = scm_cdr (expr);
571 Real x2 = robust_scm2double (scm_car (expr), 0.0);
572 expr = scm_cdr (expr);
573 Real y2 = robust_scm2double (scm_car (expr), 0.0);
574 expr = scm_cdr (expr);
575 Real x3 = robust_scm2double (scm_car (expr), 0.0);
576 expr = scm_cdr (expr);
577 Real y3 = robust_scm2double (scm_car (expr), 0.0);
578 expr = scm_cdr (expr);
579 out = scm_cons (scm_list_n (scm_from_double (current[X_AXIS]),
580 scm_from_double (current[Y_AXIS]),
581 scm_from_double (x1 + current[X_AXIS]),
582 scm_from_double (y1 + current[Y_AXIS]),
583 scm_from_double (x2 + current[X_AXIS]),
584 scm_from_double (y2 + current[Y_AXIS]),
585 scm_from_double (x3 + current[X_AXIS]),
586 scm_from_double (y3 + current[Y_AXIS]),
589 current = (Offset (x3, y3) + current);
591 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("closepath")))
593 if ((current[X_AXIS] != start[X_AXIS])
594 || (current[Y_AXIS] != start[Y_AXIS]))
596 out = scm_cons (scm_list_4 (scm_from_double (current[X_AXIS]),
597 scm_from_double (current[Y_AXIS]),
598 scm_from_double (start[X_AXIS]),
599 scm_from_double (start[Y_AXIS])),
603 expr = scm_cdr (expr);
607 warning ("Malformed path for path stencil.");
612 return scm_reverse_x (out, SCM_EOL);
616 internal_make_path_boxes (vector<Box> &boxes,
617 vector<Drul_array<Offset> > &buildings,
618 PangoMatrix trans, SCM expr, bool use_building)
620 SCM blot = scm_car (expr);
621 expr = scm_cdr (expr);
622 SCM path = all_commands_to_absolute_and_group (expr);
623 // note that expr has more stuff that we don't need after this - simply ignore it
624 //////////////////////
625 for (SCM s = path; scm_is_pair (s); s = scm_cdr (s))
627 scm_to_int (scm_length (scm_car (s))) == 4
628 ? make_draw_line_boxes (boxes, buildings, trans, scm_cons (blot, scm_car (s)), use_building)
629 : make_draw_bezier_boxes (boxes, buildings, trans, scm_cons (blot, scm_car (s)));
634 make_path_boxes (vector<Box> &boxes,
635 vector<Drul_array<Offset> > &buildings,
636 PangoMatrix trans, SCM expr)
638 return internal_make_path_boxes (boxes, buildings, trans, scm_cons (scm_car (expr), get_path_list (scm_cdr (expr))), false);
642 make_polygon_boxes (vector<Box> &boxes,
643 vector<Drul_array<Offset> > &buildings,
644 PangoMatrix trans, SCM expr)
646 SCM coords = get_number_list (scm_car (expr));
647 expr = scm_cdr (expr);
648 SCM blot_diameter = scm_car (expr);
649 //////////////////////
652 for (SCM s = coords; scm_is_pair (s); s = scm_cddr (s))
654 l = scm_cons (first ? ly_symbol2scm ("moveto") : ly_symbol2scm ("lineto"), l);
655 l = scm_cons (scm_car (s), l);
656 l = scm_cons (scm_cadr (s), l);
659 l = scm_cons (ly_symbol2scm ("closepath"), l);
660 internal_make_path_boxes (boxes, buildings, trans,
661 scm_cons (blot_diameter, scm_reverse_x (l, SCM_EOL)), true);
665 make_named_glyph_boxes (vector<Box> &boxes,
666 vector<Drul_array<Offset> > &buildings,
667 PangoMatrix trans, SCM expr)
669 SCM fm_scm = scm_car (expr);
670 Font_metric *fm = unsmob<Font_metric> (fm_scm);
671 expr = scm_cdr (expr);
672 SCM glyph = scm_car (expr);
673 string glyph_s = ly_scm2string (glyph);
675 //////////////////////
676 Open_type_font *open_fm
677 = dynamic_cast<Open_type_font *>
678 (dynamic_cast<Modified_font_metric *>(fm)->original_font ());
679 SCM_ASSERT_TYPE (open_fm, fm_scm, SCM_ARG1, __FUNCTION__, "OpenType font");
681 size_t gidx = open_fm->name_to_index (glyph_s);
682 // Bbox is the best approximation of the width based on how it would be
683 // calculated in open-type-font.cc if it were based on real extents
684 Box bbox = open_fm->get_unscaled_indexed_char_dimensions (gidx);
685 bbox.scale (dynamic_cast<Modified_font_metric *> (fm)->get_magnification ()
686 * open_fm->design_size () / open_fm->get_units_per_EM ());
687 // Real bbox is the real bbox of the object
688 Box real_bbox = open_fm->get_glyph_outline_bbox (gidx);
690 Real scale = bbox[X_AXIS].length () / real_bbox[X_AXIS].length ();
692 pango_matrix_scale (&trans, scale, scale);
694 SCM outline = open_fm->get_glyph_outline (gidx);
695 //////////////////////
696 for (SCM s = outline;
700 scm_to_int (scm_length (scm_car (s))) == 4
701 ? make_draw_line_boxes (boxes, buildings, trans,
702 scm_cons (scm_from_double (0), scm_car (s)),
704 : make_draw_bezier_boxes (boxes, buildings, trans,
705 scm_cons (scm_from_double (0), scm_car (s)));
710 make_glyph_string_boxes (vector<Box> &boxes,
711 vector<Drul_array<Offset> > &buildings,
712 PangoMatrix trans, SCM expr)
714 SCM fm_scm = scm_car (expr);
715 Font_metric *fm = unsmob<Font_metric> (fm_scm);
716 expr = scm_cdr (expr);
717 expr = scm_cdr (expr); // font-name
718 expr = scm_cdr (expr); // size
719 expr = scm_cdr (expr); // cid?
720 SCM whxy = scm_cadar (expr);
722 vector<Interval> heights;
725 vector<string> char_ids;
726 //////////////////////
727 Pango_font *pango_fm = dynamic_cast<Pango_font *> (fm);
728 SCM_ASSERT_TYPE (pango_fm, fm_scm, SCM_ARG1, __FUNCTION__, "Pango font");
730 for (SCM s = whxy; scm_is_pair (s); s = scm_cdr (s))
732 SCM now = scm_car (s);
733 widths.push_back (robust_scm2double (scm_car (now), 0.0));
735 heights.push_back (robust_scm2interval (scm_car (now), Interval (0, 0)));
737 xos.push_back (robust_scm2double (scm_car (now), 0.0));
739 yos.push_back (robust_scm2double (scm_car (now), 0.0));
741 char_ids.push_back (robust_scm2string (scm_car (now), ""));
743 Real cumulative_x = 0.0;
744 for (vsize i = 0; i < widths.size (); i++)
746 PangoMatrix transcopy (trans);
747 Offset pt0 (cumulative_x + xos[i], heights[i][DOWN] + yos[i]);
748 Offset pt1 (cumulative_x + widths[i] + xos[i], heights[i][UP] + yos[i]);
749 cumulative_x += widths[i];
752 kerned_bbox.add_point (pt0);
753 kerned_bbox.add_point (pt1);
754 size_t gidx = pango_fm->name_to_index (char_ids[i]);
755 Box real_bbox = pango_fm->get_scaled_indexed_char_dimensions (gidx);
756 Box bbox = pango_fm->get_unscaled_indexed_char_dimensions (gidx);
757 SCM outline = pango_fm->get_glyph_outline (gidx);
759 // scales may have rounding error but should be close
760 Real xlen = real_bbox[X_AXIS].length () / bbox[X_AXIS].length ();
761 Real ylen = real_bbox[Y_AXIS].length () / bbox[Y_AXIS].length ();
766 The value will be nan for whitespace, in which case we just want
767 filler, so the kerned bbox is ok.
769 However, if the value is inf, this likely means that LilyPond is
770 using a font that is currently difficult to get the measurements
771 from the Pango_font. This should eventually be fixed. The solution
772 for now is just to use the bounding box.
774 if (isnan (xlen) || isnan (ylen) || isinf (xlen) || isinf (ylen))
775 outline = box_to_scheme_lines (kerned_bbox);
778 assert (abs (xlen - ylen) < 10e-3);
780 Real scale_factor = std::max (xlen, ylen);
781 // the three operations below move the stencil from its original coordinates to current coordinates
782 pango_matrix_translate (&transcopy, kerned_bbox[X_AXIS][LEFT],
783 kerned_bbox[Y_AXIS][DOWN] - real_bbox[Y_AXIS][DOWN]);
784 pango_matrix_translate (&transcopy, real_bbox[X_AXIS][LEFT],
785 real_bbox[Y_AXIS][DOWN]);
786 pango_matrix_scale (&transcopy, scale_factor, scale_factor);
787 pango_matrix_translate (&transcopy, -bbox[X_AXIS][LEFT],
788 -bbox[Y_AXIS][DOWN]);
790 //////////////////////
791 for (SCM s = outline;
795 scm_to_int (scm_length (scm_car (s))) == 4
796 ? make_draw_line_boxes (boxes, buildings, transcopy,
797 scm_cons (scm_from_double (0), scm_car (s)),
799 : make_draw_bezier_boxes (boxes, buildings, transcopy,
800 scm_cons (scm_from_double (0), scm_car (s)));
806 receives a stencil expression and a transform matrix
807 depending on the stencil name, dispatches it to the appropriate function
811 stencil_dispatcher (vector<Box> &boxes,
812 vector<Drul_array<Offset> > &buildings,
813 PangoMatrix trans, SCM expr)
815 if (not scm_is_pair (expr))
817 if (scm_is_eq (scm_car (expr), ly_symbol2scm ("draw-line")))
818 make_draw_line_boxes (boxes, buildings, trans, scm_cdr (expr), true);
819 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("dashed-line")))
821 expr = scm_cdr (expr);
822 SCM th = scm_car (expr);
823 expr = scm_cdr (expr);
824 expr = scm_cdr (expr); // on
825 expr = scm_cdr (expr); // off
826 SCM x1 = scm_car (expr);
827 expr = scm_cdr (expr);
828 SCM x2 = scm_car (expr);
829 make_draw_line_boxes (boxes, buildings, trans,
830 scm_list_5 (th, scm_from_double (0.0),
831 scm_from_double (0.0), x1, x2), true);
833 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("circle")))
835 expr = scm_cdr (expr);
836 SCM rad = scm_car (expr);
837 expr = scm_cdr (expr);
838 SCM th = scm_car (expr);
839 make_partial_ellipse_boxes (boxes, buildings, trans,
842 scm_from_double (0.0),
843 scm_from_double (360.0),
849 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("ellipse")))
851 expr = scm_cdr (expr);
852 SCM x_rad = scm_car (expr);
853 expr = scm_cdr (expr);
854 SCM y_rad = scm_car (expr);
855 expr = scm_cdr (expr);
856 SCM th = scm_car (expr);
857 make_partial_ellipse_boxes (boxes, buildings, trans,
860 scm_from_double (0.0),
861 scm_from_double (360.0),
867 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("partial-ellipse")))
868 make_partial_ellipse_boxes (boxes, buildings, trans, scm_cdr (expr));
869 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("round-filled-box")))
870 make_round_filled_box_boxes (boxes, trans, scm_cdr (expr));
871 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("named-glyph")))
872 make_named_glyph_boxes (boxes, buildings, trans, scm_cdr (expr));
873 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("polygon")))
874 make_polygon_boxes (boxes, buildings, trans, scm_cdr (expr));
875 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("path")))
876 make_path_boxes (boxes, buildings, trans, scm_cdr (expr));
877 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("glyph-string")))
878 make_glyph_string_boxes (boxes, buildings, trans, scm_cdr (expr));
882 warning ("Stencil expression not supported by the veritcal skylines.");
885 We don't issue a warning here, as we assume that stencil-expression.cc
886 is doing stencil-checking correctly.
892 traverses a stencil expression, returning a vector of Transform_matrix_and_expression
893 the struct Transform_matrix_and_expression contains two members,
894 a Transform_matrix that indicates where to move a stencil and the stencil expression
895 to show how to construct the stencil
897 vector<Transform_matrix_and_expression>
898 stencil_traverser (PangoMatrix trans, SCM expr)
900 if (scm_is_null (expr))
901 return vector<Transform_matrix_and_expression> ();
902 else if (scm_is_eq (expr, ly_string2scm ("")))
903 return vector<Transform_matrix_and_expression> ();
904 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("combine-stencil")))
906 vector<Transform_matrix_and_expression> out;
907 for (SCM s = scm_cdr (expr); scm_is_pair (s); s = scm_cdr (s))
909 vector<Transform_matrix_and_expression> res =
910 stencil_traverser (trans, scm_car (s));
911 out.insert (out.end (), res.begin (), res.end ());
915 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("footnote")))
916 return vector<Transform_matrix_and_expression> ();
917 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("translate-stencil")))
919 Real x = robust_scm2double (scm_caadr (expr), 0.0);
920 Real y = robust_scm2double (scm_cdadr (expr), 0.0);
921 pango_matrix_translate (&trans, x, y);
922 return stencil_traverser (trans, scm_caddr (expr));
924 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("scale-stencil")))
926 Real x = robust_scm2double (scm_caadr (expr), 0.0);
927 Real y = robust_scm2double (scm_cadadr (expr), 0.0);
928 pango_matrix_scale (&trans, x, y);
929 return stencil_traverser (trans, scm_caddr (expr));
931 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("rotate-stencil")))
933 Real ang = robust_scm2double (scm_caadr (expr), 0.0);
934 Real x = robust_scm2double (scm_car (scm_cadadr (expr)), 0.0);
935 Real y = robust_scm2double (scm_cdr (scm_cadadr (expr)), 0.0);
936 pango_matrix_translate (&trans, x, y);
937 pango_matrix_rotate (&trans, -ang);
938 pango_matrix_translate (&trans, -x, -y);
939 return stencil_traverser (trans, scm_caddr (expr));
941 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("delay-stencil-evaluation")))
942 // should not use the place-holder text, but no need for the warning below
943 return vector<Transform_matrix_and_expression> ();
944 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("grob-cause")))
945 return stencil_traverser (trans, scm_caddr (expr));
946 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("color")))
947 return stencil_traverser (trans, scm_caddr (expr));
948 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("transparent-stencil")))
949 return stencil_traverser (trans, scm_cadr (expr));
950 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("id")))
951 return stencil_traverser (trans, scm_caddr (expr));
954 vector<Transform_matrix_and_expression> out;
955 out.push_back (Transform_matrix_and_expression (trans, expr));
958 warning ("Stencil expression not supported by the veritcal skylines.");
959 return vector<Transform_matrix_and_expression> ();
963 Grob::maybe_pure_internal_simple_skylines_from_extents (Grob *me, Axis a, bool pure, int beg, int end, bool ignore_x, bool ignore_y)
966 // we don't know how far spanners stretch along the X axis before
967 // line breaking. better have them take up the whole thing
968 Interval xex = ignore_x
969 ? Interval (-infinity_f, infinity_f)
970 : me->extent (me, X_AXIS);
972 // If we're looking at the x exent of a cross staff grob, it could be
973 // very early on in the computation process. We won't know its height
974 // until way later, so we give a brute force approximation.
975 Interval yex = ignore_y
976 ? Interval (-infinity_f, infinity_f)
977 : me->maybe_pure_extent (me, Y_AXIS, pure, beg, end);
979 if (xex.is_empty () || yex.is_empty ())
980 return Skyline_pair ().smobbed_copy ();
982 boxes.push_back (Box (xex, yex));
983 return Skyline_pair (boxes, a).smobbed_copy ();
986 MAKE_SCHEME_CALLBACK (Grob, pure_simple_vertical_skylines_from_extents, 3);
988 Grob::pure_simple_vertical_skylines_from_extents (SCM smob, SCM begscm, SCM endscm)
990 Grob *me = unsmob<Grob> (smob);
991 int beg = robust_scm2int (begscm, 0);
992 int end = robust_scm2int (endscm, INT_MAX);
993 // We cannot measure the widths before line breaking,
994 // so we assume that the width is infinite: pass ignore_x=true
995 return maybe_pure_internal_simple_skylines_from_extents (me, X_AXIS, true, beg, end, true, false);
998 MAKE_SCHEME_CALLBACK (Grob, simple_vertical_skylines_from_extents, 1);
1000 Grob::simple_vertical_skylines_from_extents (SCM smob)
1002 Grob *me = unsmob<Grob> (smob);
1003 return maybe_pure_internal_simple_skylines_from_extents (me, X_AXIS, false, 0, 0, false, false);
1006 MAKE_SCHEME_CALLBACK (Grob, pure_simple_horizontal_skylines_from_extents, 3);
1008 Grob::pure_simple_horizontal_skylines_from_extents (SCM smob, SCM begscm, SCM endscm)
1010 Grob *me = unsmob<Grob> (smob);
1011 int beg = robust_scm2int (begscm, 0);
1012 int end = robust_scm2int (endscm, INT_MAX);
1013 // If the grob is cross staff, we cannot measure its Y-extent before
1014 // wayyyy downstream (after spacing of axis groups is done).
1015 // Thus, we assume that the Y extent is infinite for cross staff grobs.
1016 return maybe_pure_internal_simple_skylines_from_extents (me, Y_AXIS, true, beg, end, false, to_boolean (me->get_property ("cross-staff")));
1019 MAKE_SCHEME_CALLBACK (Grob, simple_horizontal_skylines_from_extents, 1);
1021 Grob::simple_horizontal_skylines_from_extents (SCM smob)
1023 Grob *me = unsmob<Grob> (smob);
1024 // See comment in function above.
1025 return maybe_pure_internal_simple_skylines_from_extents (me, Y_AXIS, false, 0, 0, false, to_boolean (me->get_property ("cross-staff")));
1029 Stencil::skylines_from_stencil (SCM sten, Real pad, Axis a)
1031 Stencil *s = unsmob<Stencil> (sten);
1033 return Skyline_pair ().smobbed_copy ();
1035 vector<Transform_matrix_and_expression> data
1036 = stencil_traverser (make_transform_matrix (1.0, 0.0, 0.0, 1.0, 0.0, 0.0),
1039 vector<Drul_array<Offset> > buildings;
1040 for (vsize i = 0; i < data.size (); i++)
1041 stencil_dispatcher (boxes, buildings, data[i].tm_, data[i].expr_);
1043 // we use the bounding box if there are no boxes
1044 if (!boxes.size () && !buildings.size ())
1045 boxes.push_back (Box (s->extent (X_AXIS), s->extent (Y_AXIS)));
1047 Skyline_pair out (boxes, a);
1048 out.merge (Skyline_pair (buildings, a));
1050 for (DOWN_and_UP (d))
1051 out[d] = out[d].padded (pad);
1053 return out.smobbed_copy ();
1056 MAKE_SCHEME_CALLBACK (Grob, vertical_skylines_from_stencil, 1);
1058 Grob::vertical_skylines_from_stencil (SCM smob)
1060 Grob *me = unsmob<Grob> (smob);
1062 Real pad = robust_scm2double (me->get_property ("skyline-horizontal-padding"), 0.0);
1063 SCM out = Stencil::skylines_from_stencil (me->get_property ("stencil"), pad, X_AXIS);
1068 MAKE_SCHEME_CALLBACK (Grob, horizontal_skylines_from_stencil, 1);
1070 Grob::horizontal_skylines_from_stencil (SCM smob)
1072 Grob *me = unsmob<Grob> (smob);
1074 Real pad = robust_scm2double (me->get_property ("skyline-vertical-padding"), 0.0);
1075 SCM out = Stencil::skylines_from_stencil (me->get_property ("stencil"), pad, Y_AXIS);
1081 Grob::internal_skylines_from_element_stencils (Grob *me, Axis a, bool pure, int beg, int end)
1084 extract_grob_set (me, "elements", elts);
1087 Grob *x_common = common_refpoint_of_array (elts, me, X_AXIS);
1088 Grob *y_common = common_refpoint_of_array (elts, me, Y_AXIS);
1089 for (vsize i = 0; i < elts.size (); i++)
1091 x_pos.push_back (elts[i]->relative_coordinate (x_common, X_AXIS));
1092 y_pos.push_back (elts[i]->maybe_pure_coordinate (y_common, Y_AXIS, pure, beg, end));
1094 Real my_x = me->relative_coordinate (x_common, X_AXIS);
1095 Real my_y = me->maybe_pure_coordinate (y_common, Y_AXIS, pure, beg, end);
1098 for (vsize i = 0; i < elts.size (); i++)
1100 Skyline_pair *skyp = unsmob<Skyline_pair> (elts[i]->get_maybe_pure_property (a == X_AXIS ? "vertical-skylines" : "horizontal-skylines", pure, beg, end));
1104 Here, copying is essential. Otherwise, the skyline pair will
1108 It took Mike about 6 months of his life to add the `else' clause
1109 below. For horizontal skylines, the raise and shift calls need
1110 to be reversed. This is what was causing the problems in the
1111 shifting with all of the tests. RIP 6 months!
1113 Skyline_pair copy = Skyline_pair (*skyp);
1116 copy.shift (x_pos[i] - my_x);
1117 copy.raise (y_pos[i] - my_y);
1121 copy.raise (x_pos[i] - my_x);
1122 copy.shift (y_pos[i] - my_y);
1127 return res.smobbed_copy ();
1130 MAKE_SCHEME_CALLBACK (Grob, vertical_skylines_from_element_stencils, 1);
1132 Grob::vertical_skylines_from_element_stencils (SCM smob)
1134 Grob *me = unsmob<Grob> (smob);
1135 return internal_skylines_from_element_stencils (me, X_AXIS, false, 0, INT_MAX);
1138 MAKE_SCHEME_CALLBACK (Grob, horizontal_skylines_from_element_stencils, 1);
1140 Grob::horizontal_skylines_from_element_stencils (SCM smob)
1142 Grob *me = unsmob<Grob> (smob);
1143 return internal_skylines_from_element_stencils (me, Y_AXIS, false, 0, INT_MAX);
1146 MAKE_SCHEME_CALLBACK (Grob, pure_vertical_skylines_from_element_stencils, 3);
1148 Grob::pure_vertical_skylines_from_element_stencils (SCM smob, SCM beg_scm, SCM end_scm)
1150 Grob *me = unsmob<Grob> (smob);
1151 int beg = robust_scm2int (beg_scm, 0);
1152 int end = robust_scm2int (end_scm, 0);
1153 return internal_skylines_from_element_stencils (me, X_AXIS, true, beg, end);
1156 MAKE_SCHEME_CALLBACK (Grob, pure_horizontal_skylines_from_element_stencils, 3);
1158 Grob::pure_horizontal_skylines_from_element_stencils (SCM smob, SCM beg_scm, SCM end_scm)
1160 Grob *me = unsmob<Grob> (smob);
1161 int beg = robust_scm2int (beg_scm, 0);
1162 int end = robust_scm2int (end_scm, 0);
1163 return internal_skylines_from_element_stencils (me, Y_AXIS, true, beg, end);