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, vector<Drul_array<Offset> > &buildings, 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;
282 int quantization = max (1, (int) (((x_rad * trans.xx) + (y_rad * trans.yy)) * M_PI / QUANTIZATION_UNIT));
285 for (vsize i = 0; i < 1 + (vsize) quantization; i++)
287 Real ang = linear_map (start, end, 0, quantization, i);
288 Offset pt (cos (ang) * x_rad, sin (ang) * y_rad);
289 Offset inter = pt + d * get_normal ((th/2) * pt.direction ());
290 pango_matrix_transform_point (&trans, &inter[X_AXIS], &inter[Y_AXIS]);
291 points[d].push_back (inter);
294 while (flip (&d) != DOWN);
296 for (vsize i = 0; i < points[DOWN].size () - 1; i++)
301 b.add_point (points[d][i]);
302 b.add_point (points[d][i + 1]);
304 while (flip (&d) != DOWN);
310 make_draw_line_boxes (boxes, buildings, trans, 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, vector<Drul_array<Offset> > &buildings, 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;
420 int quantization = int (((temp1 - temp0).length ()
421 + (temp2 - temp1).length ()
422 + (temp3 - temp2).length ())
423 / QUANTIZATION_UNIT);
426 Offset first = curve.control_[0]
427 + d * get_normal ((th / 2) * curve.dir_at_point (0.0));
428 pango_matrix_transform_point (&trans, &first[X_AXIS], &first[Y_AXIS]);
429 points[d].push_back (first);
430 for (vsize i = 1; i < (vsize) quantization; i++)
432 Real pt = (i * 1.0) / quantization;
433 Offset inter = curve.curve_point (pt)
434 + d * get_normal ((th / 2) *curve.dir_at_point (pt));
435 pango_matrix_transform_point (&trans, &inter[X_AXIS], &inter[Y_AXIS]);
436 points[d].push_back (inter);
438 Offset last = curve.control_[3]
439 + d * get_normal ((th / 2) * curve.dir_at_point (1.0));
440 pango_matrix_transform_point (&trans, &last[X_AXIS], &last[Y_AXIS]);
441 points[d].push_back (last);
443 while (flip (&d) != DOWN);
445 for (vsize i = 0; i < points[DOWN].size () - 1; i++)
450 b.add_point (points[d][i]);
451 b.add_point (points[d][i + 1]);
453 while (flip (&d) != DOWN);
460 create_path_cap (boxes,
465 -curve.dir_at_point (0.0));
468 create_path_cap (boxes,
473 curve.dir_at_point (1.0));
478 converts a path into lists of 4 (line) or 8 (curve) absolute coordinates
480 '(moveto 1 2 lineto 3 4 rlineto -1 -1 curveto 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]) || (current[Y_AXIS] != start[Y_AXIS]))
595 out = scm_cons (scm_list_4 (scm_from_double (current[X_AXIS]),
596 scm_from_double (current[Y_AXIS]),
597 scm_from_double (start[X_AXIS]),
598 scm_from_double (start[Y_AXIS])),
602 expr = scm_cdr (expr);
606 warning ("Malformed path for path stencil.");
611 return scm_reverse_x (out, SCM_EOL);
615 internal_make_path_boxes (vector<Box> &boxes, vector<Drul_array<Offset> > &buildings, PangoMatrix trans, SCM expr, bool use_building)
617 SCM blot = scm_car (expr);
618 expr = scm_cdr (expr);
619 SCM path = all_commands_to_absolute_and_group (expr);
620 // note that expr has more stuff that we don't need after this - simply ignore it
621 //////////////////////
622 for (SCM s = path; scm_is_pair (s); s = scm_cdr (s))
624 scm_to_int (scm_length (scm_car (s))) == 4
625 ? make_draw_line_boxes (boxes, buildings, trans, scm_cons (blot, scm_car (s)), use_building)
626 : make_draw_bezier_boxes (boxes, buildings, trans, scm_cons (blot, scm_car (s)));
631 make_path_boxes (vector<Box> &boxes, vector<Drul_array<Offset> > &buildings, PangoMatrix trans, SCM expr)
633 return internal_make_path_boxes (boxes, buildings, trans, scm_cons (scm_car (expr), get_path_list (scm_cdr (expr))), false);
637 make_polygon_boxes (vector<Box> &boxes, vector<Drul_array<Offset> > &buildings, PangoMatrix trans, SCM expr)
639 SCM coords = get_number_list (scm_car (expr));
640 expr = scm_cdr (expr);
641 SCM blot_diameter = scm_car (expr);
642 //////////////////////
645 for (SCM s = coords; scm_is_pair (s); s = scm_cddr (s))
647 l = scm_cons (first ? ly_symbol2scm ("moveto") : ly_symbol2scm ("lineto"), l);
648 l = scm_cons (scm_car (s), l);
649 l = scm_cons (scm_cadr (s), l);
652 l = scm_cons (ly_symbol2scm ("closepath"), l);
653 internal_make_path_boxes (boxes, buildings, trans, scm_cons (blot_diameter, scm_reverse_x (l, SCM_EOL)), true);
657 make_named_glyph_boxes (vector<Box> &boxes, vector<Drul_array<Offset> > &buildings, PangoMatrix trans, SCM expr)
659 SCM fm_scm = scm_car (expr);
660 Font_metric *fm = unsmob<Font_metric> (fm_scm);
661 expr = scm_cdr (expr);
662 SCM glyph = scm_car (expr);
663 string glyph_s = ly_scm2string (glyph);
665 //////////////////////
666 Open_type_font *open_fm
667 = dynamic_cast<Open_type_font *>
668 (dynamic_cast<Modified_font_metric *>(fm)->original_font ());
669 SCM_ASSERT_TYPE (open_fm, fm_scm, SCM_ARG1, __FUNCTION__, "OpenType font");
671 size_t gidx = open_fm->name_to_index (glyph_s);
672 // Bbox is the best approximation of the width based on how it would be
673 // calculated in open-type-font.cc if it were based on real extents
674 Box bbox = open_fm->get_unscaled_indexed_char_dimensions (gidx);
675 bbox.scale (dynamic_cast<Modified_font_metric *>(fm)->get_magnification () * open_fm->design_size () / open_fm->get_units_per_EM ());
676 // Real bbox is the real bbox of the object
677 Box real_bbox = open_fm->get_glyph_outline_bbox (gidx);
679 Real scale = bbox[X_AXIS].length () / real_bbox[X_AXIS].length ();
681 pango_matrix_scale (&trans, scale, scale);
683 SCM outline = open_fm->get_glyph_outline (gidx);
684 //////////////////////
685 for (SCM s = outline;
689 scm_to_int (scm_length (scm_car (s))) == 4
690 ? make_draw_line_boxes (boxes, buildings, trans, scm_cons (scm_from_double (0), scm_car (s)), false)
691 : make_draw_bezier_boxes (boxes, buildings, trans, scm_cons (scm_from_double (0), scm_car (s)));
696 make_glyph_string_boxes (vector<Box> &boxes, vector<Drul_array<Offset> > &buildings, PangoMatrix trans, SCM expr)
698 SCM fm_scm = scm_car (expr);
699 Font_metric *fm = unsmob<Font_metric> (fm_scm);
700 expr = scm_cdr (expr);
701 expr = scm_cdr (expr); // font-name
702 expr = scm_cdr (expr); // size
703 expr = scm_cdr (expr); // cid?
704 SCM whxy = scm_cadar (expr);
706 vector<Interval> heights;
709 vector<string> char_ids;
710 //////////////////////
711 Pango_font *pango_fm = dynamic_cast<Pango_font *> (fm);
712 SCM_ASSERT_TYPE (pango_fm, fm_scm, SCM_ARG1, __FUNCTION__, "Pango font");
714 for (SCM s = whxy; scm_is_pair (s); s = scm_cdr (s))
716 SCM now = scm_car (s);
717 widths.push_back (robust_scm2double (scm_car (now), 0.0));
719 heights.push_back (robust_scm2interval (scm_car (now), Interval (0, 0)));
721 xos.push_back (robust_scm2double (scm_car (now), 0.0));
723 yos.push_back (robust_scm2double (scm_car (now), 0.0));
725 char_ids.push_back (robust_scm2string (scm_car (now), ""));
727 Real cumulative_x = 0.0;
728 for (vsize i = 0; i < widths.size (); i++)
730 PangoMatrix transcopy (trans);
731 Offset pt0 (cumulative_x + xos[i], heights[i][DOWN] + yos[i]);
732 Offset pt1 (cumulative_x + widths[i] + xos[i], heights[i][UP] + yos[i]);
733 cumulative_x += widths[i];
736 kerned_bbox.add_point (pt0);
737 kerned_bbox.add_point (pt1);
738 size_t gidx = pango_fm->name_to_index (char_ids[i]);
739 Box real_bbox = pango_fm->get_scaled_indexed_char_dimensions (gidx);
740 Box bbox = pango_fm->get_unscaled_indexed_char_dimensions (gidx);
741 SCM outline = pango_fm->get_glyph_outline (gidx);
743 // scales may have rounding error but should be close
744 Real xlen = real_bbox[X_AXIS].length () / bbox[X_AXIS].length ();
745 Real ylen = real_bbox[Y_AXIS].length () / bbox[Y_AXIS].length ();
750 The value will be nan for whitespace, in which case we just want
751 filler, so the kerned bbox is ok.
753 However, if the value is inf, this likely means that LilyPond is
754 using a font that is currently difficult to get the measurements
755 from the Pango_font. This should eventually be fixed. The solution
756 for now is just to use the bounding box.
758 if (isnan (xlen) || isnan (ylen) || isinf (xlen) || isinf (ylen))
759 outline = box_to_scheme_lines (kerned_bbox);
762 assert (abs (xlen - ylen) < 10e-3);
764 Real scale_factor = max (xlen, ylen);
765 // the three operations below move the stencil from its original coordinates to current coordinates
766 pango_matrix_translate (&transcopy, kerned_bbox[X_AXIS][LEFT], kerned_bbox[Y_AXIS][DOWN] - real_bbox[Y_AXIS][DOWN]);
767 pango_matrix_translate (&transcopy, real_bbox[X_AXIS][LEFT], real_bbox[Y_AXIS][DOWN]);
768 pango_matrix_scale (&transcopy, scale_factor, scale_factor);
769 pango_matrix_translate (&transcopy, -bbox[X_AXIS][LEFT], -bbox[Y_AXIS][DOWN]);
771 //////////////////////
772 for (SCM s = outline;
776 scm_to_int (scm_length (scm_car (s))) == 4
777 ? make_draw_line_boxes (boxes, buildings, transcopy, scm_cons (scm_from_double (0), scm_car (s)), false)
778 : make_draw_bezier_boxes (boxes, buildings, transcopy, scm_cons (scm_from_double (0), scm_car (s)));
784 receives a stencil expression and a transform matrix
785 depending on the stencil name, dispatches it to the appropriate function
789 stencil_dispatcher (vector<Box> &boxes, vector<Drul_array<Offset> > &buildings, PangoMatrix trans, SCM expr)
791 if (not scm_is_pair (expr))
793 if (scm_is_eq (scm_car (expr), ly_symbol2scm ("draw-line")))
794 make_draw_line_boxes (boxes, buildings, trans, scm_cdr (expr), true);
795 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("dashed-line")))
797 expr = scm_cdr (expr);
798 SCM th = scm_car (expr);
799 expr = scm_cdr (expr);
800 expr = scm_cdr (expr); // on
801 expr = scm_cdr (expr); // off
802 SCM x1 = scm_car (expr);
803 expr = scm_cdr (expr);
804 SCM x2 = scm_car (expr);
805 make_draw_line_boxes (boxes, buildings, trans, scm_list_5 (th, scm_from_double (0.0), scm_from_double (0.0), x1, x2), true);
807 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("circle")))
809 expr = scm_cdr (expr);
810 SCM rad = scm_car (expr);
811 expr = scm_cdr (expr);
812 SCM th = scm_car (expr);
813 make_partial_ellipse_boxes (boxes, buildings, trans,
816 scm_from_double (0.0),
817 scm_from_double (360.0),
823 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("ellipse")))
825 expr = scm_cdr (expr);
826 SCM x_rad = scm_car (expr);
827 expr = scm_cdr (expr);
828 SCM y_rad = scm_car (expr);
829 expr = scm_cdr (expr);
830 SCM th = scm_car (expr);
831 make_partial_ellipse_boxes (boxes, buildings, trans,
834 scm_from_double (0.0),
835 scm_from_double (360.0),
841 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("partial-ellipse")))
842 make_partial_ellipse_boxes (boxes, buildings, trans, scm_cdr (expr));
843 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("round-filled-box")))
844 make_round_filled_box_boxes (boxes, trans, scm_cdr (expr));
845 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("named-glyph")))
846 make_named_glyph_boxes (boxes, buildings, trans, scm_cdr (expr));
847 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("polygon")))
848 make_polygon_boxes (boxes, buildings, trans, scm_cdr (expr));
849 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("path")))
850 make_path_boxes (boxes, buildings, trans, scm_cdr (expr));
851 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("glyph-string")))
852 make_glyph_string_boxes (boxes, buildings, trans, scm_cdr (expr));
856 warning ("Stencil expression not supported by the veritcal skylines.");
859 We don't issue a warning here, as we assume that stencil-expression.cc
860 is doing stencil-checking correctly.
866 traverses a stencil expression, returning a vector of Transform_matrix_and_expression
867 the struct Transform_matrix_and_expression contains two members,
868 a Transform_matrix that indicates where to move a stencil and the stencil expression
869 to show how to construct the stencil
871 vector<Transform_matrix_and_expression>
872 stencil_traverser (PangoMatrix trans, SCM expr)
874 if (scm_is_null (expr))
875 return vector<Transform_matrix_and_expression> ();
876 else if (scm_is_eq (expr, ly_string2scm ("")))
877 return vector<Transform_matrix_and_expression> ();
878 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("combine-stencil")))
880 vector<Transform_matrix_and_expression> out;
881 for (SCM s = scm_cdr (expr); scm_is_pair (s); s = scm_cdr (s))
883 vector<Transform_matrix_and_expression> res = stencil_traverser (trans, scm_car (s));
884 out.insert (out.end (), res.begin (), res.end ());
888 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("footnote")))
889 return vector<Transform_matrix_and_expression> ();
890 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("translate-stencil")))
892 Real x = robust_scm2double (scm_caadr (expr), 0.0);
893 Real y = robust_scm2double (scm_cdadr (expr), 0.0);
894 pango_matrix_translate (&trans, x, y);
895 return stencil_traverser (trans, scm_caddr (expr));
897 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("scale-stencil")))
899 Real x = robust_scm2double (scm_caadr (expr), 0.0);
900 Real y = robust_scm2double (scm_cadadr (expr), 0.0);
901 pango_matrix_scale (&trans, x, y);
902 return stencil_traverser (trans, scm_caddr (expr));
904 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("rotate-stencil")))
906 Real ang = robust_scm2double (scm_caadr (expr), 0.0);
907 Real x = robust_scm2double (scm_car (scm_cadadr (expr)), 0.0);
908 Real y = robust_scm2double (scm_cdr (scm_cadadr (expr)), 0.0);
909 pango_matrix_translate (&trans, x, y);
910 pango_matrix_rotate (&trans, -ang);
911 pango_matrix_translate (&trans, -x, -y);
912 return stencil_traverser (trans, scm_caddr (expr));
914 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("delay-stencil-evaluation")))
915 // should not use the place-holder text, but no need for the warning below
916 return vector<Transform_matrix_and_expression> ();
917 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("grob-cause")))
918 return stencil_traverser (trans, scm_caddr (expr));
919 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("color")))
920 return stencil_traverser (trans, scm_caddr (expr));
921 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("transparent-stencil")))
922 return stencil_traverser (trans, scm_cadr (expr));
923 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("id")))
924 return stencil_traverser (trans, scm_caddr (expr));
927 vector<Transform_matrix_and_expression> out;
928 out.push_back (Transform_matrix_and_expression (trans, expr));
931 warning ("Stencil expression not supported by the veritcal skylines.");
932 return vector<Transform_matrix_and_expression> ();
936 Grob::maybe_pure_internal_simple_skylines_from_extents (Grob *me, Axis a, bool pure, int beg, int end, bool ignore_x, bool ignore_y)
939 // we don't know how far spanners stretch along the X axis before
940 // line breaking. better have them take up the whole thing
941 Interval xex = ignore_x
942 ? Interval (-infinity_f, infinity_f)
943 : me->extent (me, X_AXIS);
945 // If we're looking at the x exent of a cross staff grob, it could be
946 // very early on in the computation process. We won't know its height
947 // until way later, so we give a brute force approximation.
948 Interval yex = ignore_y
949 ? Interval (-infinity_f, infinity_f)
950 : me->maybe_pure_extent (me, Y_AXIS, pure, beg, end);
952 if (xex.is_empty () || yex.is_empty ())
953 return Skyline_pair ().smobbed_copy ();
955 boxes.push_back (Box (xex, yex));
956 return Skyline_pair (boxes, a).smobbed_copy ();
959 MAKE_SCHEME_CALLBACK (Grob, pure_simple_vertical_skylines_from_extents, 3);
961 Grob::pure_simple_vertical_skylines_from_extents (SCM smob, SCM begscm, SCM endscm)
963 Grob *me = unsmob<Grob> (smob);
964 int beg = robust_scm2int (begscm, 0);
965 int end = robust_scm2int (endscm, INT_MAX);
966 // We cannot measure the widths before line breaking,
967 // so we assume that the width is infinite: pass ignore_x=true
968 return maybe_pure_internal_simple_skylines_from_extents (me, X_AXIS, true, beg, end, true, false);
971 MAKE_SCHEME_CALLBACK (Grob, simple_vertical_skylines_from_extents, 1);
973 Grob::simple_vertical_skylines_from_extents (SCM smob)
975 Grob *me = unsmob<Grob> (smob);
976 return maybe_pure_internal_simple_skylines_from_extents (me, X_AXIS, false, 0, 0, false, false);
979 MAKE_SCHEME_CALLBACK (Grob, pure_simple_horizontal_skylines_from_extents, 3);
981 Grob::pure_simple_horizontal_skylines_from_extents (SCM smob, SCM begscm, SCM endscm)
983 Grob *me = unsmob<Grob> (smob);
984 int beg = robust_scm2int (begscm, 0);
985 int end = robust_scm2int (endscm, INT_MAX);
986 // If the grob is cross staff, we cannot measure its Y-extent before
987 // wayyyy downstream (after spacing of axis groups is done).
988 // Thus, we assume that the Y extent is infinite for cross staff grobs.
989 return maybe_pure_internal_simple_skylines_from_extents (me, Y_AXIS, true, beg, end, false, to_boolean (me->get_property ("cross-staff")));
992 MAKE_SCHEME_CALLBACK (Grob, simple_horizontal_skylines_from_extents, 1);
994 Grob::simple_horizontal_skylines_from_extents (SCM smob)
996 Grob *me = unsmob<Grob> (smob);
997 // See comment in function above.
998 return maybe_pure_internal_simple_skylines_from_extents (me, Y_AXIS, false, 0, 0, false, to_boolean (me->get_property ("cross-staff")));
1002 Stencil::skylines_from_stencil (SCM sten, Real pad, Axis a)
1004 Stencil *s = unsmob<Stencil> (sten);
1006 return Skyline_pair ().smobbed_copy ();
1008 vector<Transform_matrix_and_expression> data
1009 = stencil_traverser (make_transform_matrix (1.0, 0.0, 0.0, 1.0, 0.0, 0.0),
1012 vector<Drul_array<Offset> > buildings;
1013 for (vsize i = 0; i < data.size (); i++)
1014 stencil_dispatcher (boxes, buildings, data[i].tm_, data[i].expr_);
1016 // we use the bounding box if there are no boxes
1017 if (!boxes.size () && !buildings.size ())
1018 boxes.push_back (Box (s->extent (X_AXIS), s->extent (Y_AXIS)));
1020 Skyline_pair out (boxes, a);
1021 out.merge (Skyline_pair (buildings, a));
1023 for (DOWN_and_UP (d))
1024 out[d] = out[d].padded (pad);
1026 return out.smobbed_copy ();
1029 MAKE_SCHEME_CALLBACK (Grob, vertical_skylines_from_stencil, 1);
1031 Grob::vertical_skylines_from_stencil (SCM smob)
1033 Grob *me = unsmob<Grob> (smob);
1035 Real pad = robust_scm2double (me->get_property ("skyline-horizontal-padding"), 0.0);
1036 SCM out = Stencil::skylines_from_stencil (me->get_property ("stencil"), pad, X_AXIS);
1041 MAKE_SCHEME_CALLBACK (Grob, horizontal_skylines_from_stencil, 1);
1043 Grob::horizontal_skylines_from_stencil (SCM smob)
1045 Grob *me = unsmob<Grob> (smob);
1047 Real pad = robust_scm2double (me->get_property ("skyline-vertical-padding"), 0.0);
1048 SCM out = Stencil::skylines_from_stencil (me->get_property ("stencil"), pad, Y_AXIS);
1054 Grob::internal_skylines_from_element_stencils (Grob *me, Axis a, bool pure, int beg, int end)
1057 extract_grob_set (me, "elements", elts);
1060 Grob *x_common = common_refpoint_of_array (elts, me, X_AXIS);
1061 Grob *y_common = common_refpoint_of_array (elts, me, Y_AXIS);
1062 for (vsize i = 0; i < elts.size (); i++)
1064 x_pos.push_back (elts[i]->relative_coordinate (x_common, X_AXIS));
1065 y_pos.push_back (elts[i]->maybe_pure_coordinate (y_common, Y_AXIS, pure, beg, end));
1067 Real my_x = me->relative_coordinate (x_common, X_AXIS);
1068 Real my_y = me->maybe_pure_coordinate (y_common, Y_AXIS, pure, beg, end);
1071 for (vsize i = 0; i < elts.size (); i++)
1073 Skyline_pair *skyp = unsmob<Skyline_pair> (elts[i]->get_maybe_pure_property (a == X_AXIS ? "vertical-skylines" : "horizontal-skylines", pure, beg, end));
1077 Here, copying is essential. Otherwise, the skyline pair will
1081 It took Mike about 6 months of his life to add the `else' clause
1082 below. For horizontal skylines, the raise and shift calls need
1083 to be reversed. This is what was causing the problems in the
1084 shifting with all of the tests. RIP 6 months!
1086 Skyline_pair copy = Skyline_pair (*skyp);
1089 copy.shift (x_pos[i] - my_x);
1090 copy.raise (y_pos[i] - my_y);
1094 copy.raise (x_pos[i] - my_x);
1095 copy.shift (y_pos[i] - my_y);
1100 return res.smobbed_copy ();
1103 MAKE_SCHEME_CALLBACK (Grob, vertical_skylines_from_element_stencils, 1);
1105 Grob::vertical_skylines_from_element_stencils (SCM smob)
1107 Grob *me = unsmob<Grob> (smob);
1108 return internal_skylines_from_element_stencils (me, X_AXIS, false, 0, INT_MAX);
1111 MAKE_SCHEME_CALLBACK (Grob, horizontal_skylines_from_element_stencils, 1);
1113 Grob::horizontal_skylines_from_element_stencils (SCM smob)
1115 Grob *me = unsmob<Grob> (smob);
1116 return internal_skylines_from_element_stencils (me, Y_AXIS, false, 0, INT_MAX);
1119 MAKE_SCHEME_CALLBACK (Grob, pure_vertical_skylines_from_element_stencils, 3);
1121 Grob::pure_vertical_skylines_from_element_stencils (SCM smob, SCM beg_scm, SCM end_scm)
1123 Grob *me = unsmob<Grob> (smob);
1124 int beg = robust_scm2int (beg_scm, 0);
1125 int end = robust_scm2int (end_scm, 0);
1126 return internal_skylines_from_element_stencils (me, X_AXIS, true, beg, end);
1129 MAKE_SCHEME_CALLBACK (Grob, pure_horizontal_skylines_from_element_stencils, 3);
1131 Grob::pure_horizontal_skylines_from_element_stencils (SCM smob, SCM beg_scm, SCM end_scm)
1133 Grob *me = unsmob<Grob> (smob);
1134 int beg = robust_scm2int (beg_scm, 0);
1135 int end = robust_scm2int (end_scm, 0);
1136 return internal_skylines_from_element_stencils (me, Y_AXIS, true, beg, end);