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>
40 #include "dimensions.hh"
41 #include "font-metric.hh"
43 #include "interval.hh"
44 #include "freetype.hh"
47 #include "modified-font-metric.hh"
48 #include "open-type-font.hh"
49 #include "pango-font.hh"
50 #include "pointer-group-interface.hh"
51 #include "lily-guile.hh"
55 #include "string-convert.hh"
57 #include "skyline-pair.hh"
61 Real QUANTIZATION_UNIT = 0.2;
63 void create_path_cap (vector<Box> &boxes, vector<Drul_array<Offset> > &buildings, PangoMatrix trans, Offset pt, Real rad, Real slope, Direction d);
65 struct Transform_matrix_and_expression
70 Transform_matrix_and_expression (PangoMatrix tm, SCM expr);
73 Transform_matrix_and_expression::Transform_matrix_and_expression (PangoMatrix tm, SCM expr)
80 make_transform_matrix (Real p0, Real p1, Real p2, Real p3, Real p4, Real p5)
92 //// UTILITY FUNCTIONS
95 map x's placement between orig_l and orig_r onto
96 the interval final_l final_r
99 linear_map (Real final_l, Real final_r, Real orig_l, Real orig_r, Real x)
101 return final_l + ((final_r - final_l) * ((x - orig_l) / (orig_r - orig_l)));
105 from a nested SCM list, return the first list of numbers
109 get_number_list (SCM l)
113 if (scm_is_number (scm_car (l)))
115 SCM res = get_number_list (scm_car (l));
116 if (res == SCM_BOOL_F)
117 return get_number_list (scm_cdr (l));
124 from a nested SCM list, return the first list of numbers
128 get_path_list (SCM l)
132 if (scm_memv (scm_car (l),
133 scm_list_n (ly_symbol2scm ("moveto"),
134 ly_symbol2scm ("rmoveto"),
135 ly_symbol2scm ("lineto"),
136 ly_symbol2scm ("rlineto"),
137 ly_symbol2scm ("curveto"),
138 ly_symbol2scm ("rcurveto"),
139 ly_symbol2scm ("closepath"),
143 SCM res = get_path_list (scm_car (l));
144 if (res == SCM_BOOL_F)
145 return get_path_list (scm_cdr (l));
152 perpendicular_slope (Real s)
161 //// END UTILITY FUNCTIONS
164 below, for all of the functions make_X_boxes, the expression
165 is always unpacked into variables.
166 then, after a line of /////, there are manipulations of these variables
167 (there may be no manipulations necessary depending on the function)
168 afterwards, there is another ///// followed by the creation of points
173 make_draw_line_boxes (vector<Box> &boxes, vector<Drul_array<Offset> > &buildings, PangoMatrix trans, SCM expr, bool use_building)
175 Real thick = robust_scm2double (scm_car (expr), 0.0);
176 expr = scm_cdr (expr);
177 Real x0 = robust_scm2double (scm_car (expr), 0.0);
178 expr = scm_cdr (expr);
179 Real y0 = robust_scm2double (scm_car (expr), 0.0);
180 expr = scm_cdr (expr);
181 Real x1 = robust_scm2double (scm_car (expr), 0.0);
182 expr = scm_cdr (expr);
183 Real y1 = robust_scm2double (scm_car (expr), 0.0);
184 Real slope = x1 == x0 ? infinity_f : (y1 - y0) / (x1 - x0);
185 //////////////////////
191 Offset left (x0, y0);
192 Offset right (x1, y1);
196 Offset inter_l = get_point_in_y_direction (left, perpendicular_slope (slope), thick / 2, d);
197 Offset inter_r = get_point_in_y_direction (right, perpendicular_slope (slope), thick / 2, d);
198 pango_matrix_transform_point (&trans, &inter_l[X_AXIS], &inter_l[Y_AXIS]);
199 pango_matrix_transform_point (&trans, &inter_r[X_AXIS], &inter_r[Y_AXIS]);
200 if ((inter_l[X_AXIS] == inter_r[X_AXIS]) || (inter_l[Y_AXIS] == inter_r[Y_AXIS]))
203 b.add_point (inter_l);
204 b.add_point (inter_r);
207 else if (use_building)
208 buildings.push_back (Drul_array<Offset> (inter_l, inter_r));
211 Offset inter_l = get_point_in_y_direction (left, perpendicular_slope (slope), thick / 2, d);
212 Offset inter_r = get_point_in_y_direction (right, perpendicular_slope (slope), thick / 2, d);
213 pango_matrix_transform_point (&trans, &inter_l[X_AXIS], &inter_l[Y_AXIS]);
214 pango_matrix_transform_point (&trans, &inter_r[X_AXIS], &inter_r[Y_AXIS]);
215 Real length = sqrt (((inter_l[X_AXIS] - inter_r[X_AXIS]) * (inter_l[X_AXIS] - inter_r[X_AXIS])) + ((inter_l[Y_AXIS] - inter_r[Y_AXIS]) * (inter_l[Y_AXIS] - inter_r[Y_AXIS])));
217 vsize passes = (vsize) ((length * 2) + 1);
218 vector<Offset> points;
220 for (vsize i = 0; i < 1 + passes; i++)
222 Offset pt (linear_map (x0, x1, 0, passes, i),
223 linear_map (y0, y1, 0, passes, i));
224 Offset inter = get_point_in_y_direction (pt, perpendicular_slope (slope), thick / 2, d);
225 pango_matrix_transform_point (&trans, &inter[X_AXIS], &inter[Y_AXIS]);
226 points.push_back (inter);
228 for (vsize i = 0; i < points.size () - 1; i++)
231 b.add_point (points[i]);
232 b.add_point (points[i + 1]);
237 while (flip (&d) != DOWN);
242 create_path_cap (boxes,
247 perpendicular_slope (slope),
248 Direction (sign (slope)));
251 create_path_cap (boxes,
256 perpendicular_slope (slope),
257 Direction (sign (-slope)));
262 make_partial_ellipse_boxes (vector<Box> &boxes, vector<Drul_array<Offset> > &buildings, PangoMatrix trans, SCM expr)
264 Real x_rad = robust_scm2double (scm_car (expr), 0.0);
265 expr = scm_cdr (expr);
266 Real y_rad = robust_scm2double (scm_car (expr), 0.0);
267 expr = scm_cdr (expr);
268 Real start = robust_scm2double (scm_car (expr), 0.0);
269 expr = scm_cdr (expr);
270 Real end = robust_scm2double (scm_car (expr), 0.0);
271 expr = scm_cdr (expr);
272 Real th = robust_scm2double (scm_car (expr), 0.0);
273 expr = scm_cdr (expr);
274 bool connect = to_boolean (scm_car (expr));
275 expr = scm_cdr (expr);
276 bool fill = to_boolean (scm_car (expr));
277 //////////////////////
278 start = M_PI * start / 180;
279 end = M_PI * end / 180;
282 complex<Real> sunit = polar (1.0, start);
283 complex<Real> eunit = polar (1.0, end);
284 Offset sp (real (sunit) * x_rad, imag (sunit) * y_rad);
285 Offset ep (real (eunit) * x_rad, imag (eunit) * y_rad);
286 //////////////////////
287 Drul_array<vector<Offset> > points;
289 int quantization = max (1, (int) (((x_rad * trans.xx) + (y_rad * trans.yy)) * M_PI / QUANTIZATION_UNIT));
292 for (vsize i = 0; i < 1 + quantization; i++)
294 Real ang = linear_map (start, end, 0, quantization, i);
295 complex<Real> coord = polar (1.0, ang);
296 Offset pt (real (coord) * x_rad,
297 imag (coord) * y_rad);
298 Real slope = pt[Y_AXIS] / pt[X_AXIS];
299 Offset inter = get_point_in_y_direction (pt, perpendicular_slope (slope), th / 2, d);
300 pango_matrix_transform_point (&trans, &inter[X_AXIS], &inter[Y_AXIS]);
301 points[d].push_back (inter);
304 while (flip (&d) != DOWN);
306 for (vsize i = 0; i < points[DOWN].size () - 1; i++)
311 b.add_point (points[d][i]);
312 b.add_point (points[d][i + 1]);
314 while (flip (&d) != DOWN);
320 make_draw_line_boxes (boxes, buildings, trans, scm_list_5 (scm_from_double (th),
321 scm_from_double (sp[X_AXIS]),
322 scm_from_double (sp[Y_AXIS]),
323 scm_from_double (ep[X_AXIS]),
324 scm_from_double (ep[Y_AXIS])),
331 complex<Real> coord = polar (1.0, start);
332 Offset pt (real (coord) * x_rad,
333 imag (coord) * y_rad);
334 Real slope = pt[Y_AXIS] / pt[X_AXIS];
335 create_path_cap (boxes,
340 perpendicular_slope (slope),
341 Direction (sign (slope)));
344 coord = polar (1.0, start);
345 pt = Offset (real (coord) * x_rad,
346 imag (coord) * y_rad);
347 slope = pt[Y_AXIS] / pt[X_AXIS];
348 create_path_cap (boxes,
353 perpendicular_slope (slope),
354 Direction (sign (-slope)));
359 make_round_filled_box_boxes (vector<Box> &boxes, PangoMatrix trans, SCM expr)
361 Real left = robust_scm2double (scm_car (expr), 0.0);
362 expr = scm_cdr (expr);
363 Real right = robust_scm2double (scm_car (expr), 0.0);
364 expr = scm_cdr (expr);
365 Real bottom = robust_scm2double (scm_car (expr), 0.0);
366 expr = scm_cdr (expr);
367 Real top = robust_scm2double (scm_car (expr), 0.0);
368 expr = scm_cdr (expr);
369 Real th = robust_scm2double (scm_car (expr), 0.0);
370 //////////////////////
371 vector<Offset> points;
373 Offset p0 = Offset (-left - (th / 2), -bottom - (th / 2));
374 Offset p1 = Offset (right + (th / 2), top + (th / 2));
375 pango_matrix_transform_point (&trans, &p0[X_AXIS], &p0[Y_AXIS]);
376 pango_matrix_transform_point (&trans, &p1[X_AXIS], &p1[Y_AXIS]);
383 create_path_cap (vector<Box> &boxes, vector<Drul_array<Offset> > &buildings, PangoMatrix trans, Offset pt, Real rad, Real slope, Direction d)
385 Real angle = atan (slope) * 180 / M_PI;
386 Real other = angle > 180 ? angle - 180 : angle + 180;
393 other = (slope >= 0 && d == DOWN) || (slope < 0 && d == UP)
396 PangoMatrix new_trans (trans);
397 pango_matrix_translate (&new_trans, pt[X_AXIS], pt[Y_AXIS]);
398 make_partial_ellipse_boxes (boxes, buildings, new_trans,
399 scm_list_n (scm_from_double (rad),
400 scm_from_double (rad),
401 scm_from_double (angle),
402 scm_from_double (other),
403 scm_from_double (0.0),
410 make_draw_bezier_boxes (vector<Box> &boxes, vector<Drul_array<Offset> > &buildings, PangoMatrix trans, SCM expr)
412 Real th = robust_scm2double (scm_car (expr), 0.0);
413 expr = scm_cdr (expr);
414 Real x0 = robust_scm2double (scm_car (expr), 0.0);
415 expr = scm_cdr (expr);
416 Real y0 = robust_scm2double (scm_car (expr), 0.0);
417 expr = scm_cdr (expr);
418 Real x1 = robust_scm2double (scm_car (expr), 0.0);
419 expr = scm_cdr (expr);
420 Real y1 = robust_scm2double (scm_car (expr), 0.0);
421 expr = scm_cdr (expr);
422 Real x2 = robust_scm2double (scm_car (expr), 0.0);
423 expr = scm_cdr (expr);
424 Real y2 = robust_scm2double (scm_car (expr), 0.0);
425 expr = scm_cdr (expr);
426 Real x3 = robust_scm2double (scm_car (expr), 0.0);
427 expr = scm_cdr (expr);
428 Real y3 = robust_scm2double (scm_car (expr), 0.0);
429 //////////////////////
431 curve.control_[0] = Offset (x0, y0);
432 curve.control_[1] = Offset (x1, y1);
433 curve.control_[2] = Offset (x2, y2);
434 curve.control_[3] = Offset (x3, y3);
435 Offset temp0 (x0, y0);
436 Offset temp1 (x1, y1);
437 Offset temp2 (x2, y2);
438 Offset temp3 (x3, y3);
439 pango_matrix_transform_point (&trans, &temp0[X_AXIS], &temp0[Y_AXIS]);
440 pango_matrix_transform_point (&trans, &temp1[X_AXIS], &temp1[Y_AXIS]);
441 pango_matrix_transform_point (&trans, &temp2[X_AXIS], &temp2[Y_AXIS]);
442 pango_matrix_transform_point (&trans, &temp3[X_AXIS], &temp3[Y_AXIS]);
443 //////////////////////
444 Drul_array<vector<Offset> > points;
446 int quantization = int (((temp1 - temp0).length ()
447 + (temp2 - temp1).length ()
448 + (temp3 - temp2).length ())
449 / QUANTIZATION_UNIT);
452 Offset first = get_point_in_y_direction (curve.control_[0], perpendicular_slope (curve.slope_at_point (0.0)), th / 2, d);
453 pango_matrix_transform_point (&trans, &first[X_AXIS], &first[Y_AXIS]);
454 points[d].push_back (first);
455 for (vsize i = 1; i < quantization; i++)
457 Real pt = (i * 1.0) / quantization;
458 Offset inter = get_point_in_y_direction (curve.curve_point (pt), perpendicular_slope (curve.slope_at_point (pt)), th / 2, d);
459 pango_matrix_transform_point (&trans, &inter[X_AXIS], &inter[Y_AXIS]);
460 points[d].push_back (inter);
462 Offset last = get_point_in_y_direction (curve.control_[3], curve.slope_at_point (1.0), th / 2, d);
463 pango_matrix_transform_point (&trans, &last[X_AXIS], &last[Y_AXIS]);
464 points[d].push_back (last);
466 while (flip (&d) != DOWN);
468 for (vsize i = 0; i < points[DOWN].size () - 1; i++)
473 b.add_point (points[d][i]);
474 b.add_point (points[d][i + 1]);
476 while (flip (&d) != DOWN);
483 Real slope = curve.slope_at_point (0.0);
484 d = Direction (sign (slope == 0.0 || abs (slope) == infinity_f
485 ? curve.slope_at_point (0.0001)
488 create_path_cap (boxes,
493 perpendicular_slope (curve.slope_at_point (0.0)),
497 slope = curve.slope_at_point (1.0);
498 d = Direction (sign (slope == 0.0 || abs (slope) == infinity_f
499 ? curve.slope_at_point (0.9999)
502 create_path_cap (boxes,
507 perpendicular_slope (curve.slope_at_point (1.0)),
513 converts a path into lists of 4 (line) or 8 (curve) absolute coordinates
515 '(moveto 1 2 lineto 3 4 rlineto -1 -1 curveto 3 3 5 5 6 6 rcurveto -1 -1 -1 -1 -1 -1 closepath)
525 all_commands_to_absolute_and_group (SCM expr)
529 Offset current (0, 0);
531 while (scm_is_pair (expr))
533 if (scm_car (expr) == ly_symbol2scm ("moveto")
534 || (scm_car (expr) == ly_symbol2scm ("rmoveto") && first))
536 Real x = robust_scm2double (scm_cadr (expr), 0.0);
537 Real y = robust_scm2double (scm_caddr (expr), 0.0);
538 start = Offset (x, y);
540 expr = scm_cdddr (expr);
542 if (scm_car (expr) == ly_symbol2scm ("rmoveto"))
544 Real x = robust_scm2double (scm_cadr (expr), 0.0);
545 Real y = robust_scm2double (scm_caddr (expr), 0.0);
546 start = (Offset (x, y) + current);
548 expr = scm_cdddr (expr);
550 else if (scm_car (expr) == ly_symbol2scm ("lineto"))
552 Real x = robust_scm2double (scm_cadr (expr), 0.0);
553 Real y = robust_scm2double (scm_caddr (expr), 0.0);
554 out = scm_cons (scm_list_4 (scm_from_double (current[X_AXIS]),
555 scm_from_double (current[Y_AXIS]),
557 scm_from_double (y)),
559 current = Offset (x, y);
560 expr = scm_cdddr (expr);
562 else if (scm_car (expr) == ly_symbol2scm ("rlineto"))
564 Real x = robust_scm2double (scm_cadr (expr), 0.0);
565 Real y = robust_scm2double (scm_caddr (expr), 0.0);
566 out = scm_cons (scm_list_4 (scm_from_double (current[X_AXIS]),
567 scm_from_double (current[Y_AXIS]),
568 scm_from_double (x + current[X_AXIS]),
569 scm_from_double (y + current[Y_AXIS])),
571 current = (Offset (x, y) + current);
572 expr = scm_cdddr (expr);
574 else if (scm_car (expr) == ly_symbol2scm ("curveto"))
576 Real x1 = robust_scm2double (scm_cadr (expr), 0.0);
577 expr = scm_cddr (expr);
578 Real y1 = robust_scm2double (scm_car (expr), 0.0);
579 expr = scm_cdr (expr);
580 Real x2 = robust_scm2double (scm_car (expr), 0.0);
581 expr = scm_cdr (expr);
582 Real y2 = robust_scm2double (scm_car (expr), 0.0);
583 expr = scm_cdr (expr);
584 Real x3 = robust_scm2double (scm_car (expr), 0.0);
585 expr = scm_cdr (expr);
586 Real y3 = robust_scm2double (scm_car (expr), 0.0);
587 expr = scm_cdr (expr);
588 out = scm_cons (scm_list_n (scm_from_double (current[X_AXIS]),
589 scm_from_double (current[Y_AXIS]),
590 scm_from_double (x1),
591 scm_from_double (y1),
592 scm_from_double (x2),
593 scm_from_double (y2),
594 scm_from_double (x3),
595 scm_from_double (y3),
598 current = Offset (x3, y3);
600 else if (scm_car (expr) == ly_symbol2scm ("rcurveto"))
602 Real x1 = robust_scm2double (scm_cadr (expr), 0.0);
603 expr = scm_cddr (expr);
604 Real y1 = robust_scm2double (scm_car (expr), 0.0);
605 expr = scm_cdr (expr);
606 Real x2 = robust_scm2double (scm_car (expr), 0.0);
607 expr = scm_cdr (expr);
608 Real y2 = robust_scm2double (scm_car (expr), 0.0);
609 expr = scm_cdr (expr);
610 Real x3 = robust_scm2double (scm_car (expr), 0.0);
611 expr = scm_cdr (expr);
612 Real y3 = robust_scm2double (scm_car (expr), 0.0);
613 expr = scm_cdr (expr);
614 out = scm_cons (scm_list_n (scm_from_double (current[X_AXIS]),
615 scm_from_double (current[Y_AXIS]),
616 scm_from_double (x1 + current[X_AXIS]),
617 scm_from_double (y1 + current[Y_AXIS]),
618 scm_from_double (x2 + current[X_AXIS]),
619 scm_from_double (y2 + current[Y_AXIS]),
620 scm_from_double (x3 + current[X_AXIS]),
621 scm_from_double (y3 + current[Y_AXIS]),
624 current = (Offset (x3, y3) + current);
626 else if (scm_car (expr) == ly_symbol2scm ("closepath"))
628 if ((current[X_AXIS] != start[X_AXIS]) || (current[Y_AXIS] != start[Y_AXIS]))
630 out = scm_cons (scm_list_4 (scm_from_double (current[X_AXIS]),
631 scm_from_double (current[Y_AXIS]),
632 scm_from_double (start[X_AXIS]),
633 scm_from_double (start[Y_AXIS])),
637 expr = scm_cdr (expr);
641 warning ("Malformed path for path stencil.");
646 return scm_reverse_x (out, SCM_EOL);
650 internal_make_path_boxes (vector<Box> &boxes, vector<Drul_array<Offset> > &buildings, PangoMatrix trans, SCM expr, bool use_building)
652 SCM blot = scm_car (expr);
653 expr = scm_cdr (expr);
654 SCM path = all_commands_to_absolute_and_group (expr);
655 // note that expr has more stuff that we don't need after this - simply ignore it
656 //////////////////////
657 for (SCM s = path; scm_is_pair (s); s = scm_cdr (s))
659 scm_to_int (scm_length (scm_car (s))) == 4
660 ? make_draw_line_boxes (boxes, buildings, trans, scm_cons (blot, scm_car (s)), use_building)
661 : make_draw_bezier_boxes (boxes, buildings, trans, scm_cons (blot, scm_car (s)));
666 make_path_boxes (vector<Box> &boxes, vector<Drul_array<Offset> > &buildings, PangoMatrix trans, SCM expr)
668 return internal_make_path_boxes (boxes, buildings, trans, scm_cons (scm_car (expr), get_path_list (scm_cdr (expr))), false);
672 make_polygon_boxes (vector<Box> &boxes, vector<Drul_array<Offset> > &buildings, PangoMatrix trans, SCM expr)
674 SCM coords = get_number_list (scm_car (expr));
675 expr = scm_cdr (expr);
676 SCM blot_diameter = scm_car (expr);
677 //////////////////////
680 for (SCM s = coords; scm_is_pair (s); s = scm_cddr (s))
682 l = scm_cons (first ? ly_symbol2scm ("moveto") : ly_symbol2scm ("lineto"), l);
683 l = scm_cons (scm_car (s), l);
684 l = scm_cons (scm_cadr (s), l);
687 l = scm_cons (ly_symbol2scm ("closepath"), l);
688 internal_make_path_boxes (boxes, buildings, trans, scm_cons (blot_diameter, scm_reverse_x (l, SCM_EOL)), true);
692 make_named_glyph_boxes (vector<Box> &boxes, vector<Drul_array<Offset> > &buildings, PangoMatrix trans, SCM expr)
694 SCM fm_scm = scm_car (expr);
695 Font_metric *fm = unsmob_metrics (fm_scm);
696 expr = scm_cdr (expr);
697 SCM glyph = scm_car (expr);
698 string glyph_s = ly_scm2string (glyph);
700 //////////////////////
701 Open_type_font *open_fm
702 = dynamic_cast<Open_type_font *>
703 (dynamic_cast<Modified_font_metric *>(fm)->original_font ());
704 SCM_ASSERT_TYPE (open_fm, fm_scm, SCM_ARG1, __FUNCTION__, "OpenType font");
706 size_t gidx = open_fm->name_to_index (glyph_s);
707 // Bbox is the best approximation of the width based on how it would be
708 // calculated in open-type-font.cc if it were based on real extents
709 Box bbox = open_fm->get_unscaled_indexed_char_dimensions (gidx);
710 bbox.scale (dynamic_cast<Modified_font_metric *>(fm)->get_magnification () * open_fm->design_size () / open_fm->get_units_per_EM ());
711 // Real bbox is the real bbox of the object
712 Box real_bbox = open_fm->get_glyph_outline_bbox (gidx);
714 Real scale = bbox[X_AXIS].length () / real_bbox[X_AXIS].length ();
716 pango_matrix_scale (&trans, scale, scale);
718 SCM outline = open_fm->get_glyph_outline (gidx);
719 //////////////////////
720 for (SCM s = outline;
724 scm_to_int (scm_length (scm_car (s))) == 4
725 ? make_draw_line_boxes (boxes, buildings, trans, scm_cons (scm_from_double (0), scm_car (s)), false)
726 : make_draw_bezier_boxes (boxes, buildings, trans, scm_cons (scm_from_double (0), scm_car (s)));
731 make_glyph_string_boxes (vector<Box> &boxes, vector<Drul_array<Offset> > &buildings, PangoMatrix trans, SCM expr)
733 SCM fm_scm = scm_car (expr);
734 Font_metric *fm = unsmob_metrics (fm_scm);
735 expr = scm_cdr (expr);
736 expr = scm_cdr (expr); // font-name
737 expr = scm_cdr (expr); // size
738 expr = scm_cdr (expr); // cid?
739 SCM whxy = scm_cadar (expr);
741 vector<Interval> heights;
744 vector<string> char_ids;
745 //////////////////////
746 Pango_font *pango_fm = dynamic_cast<Pango_font *> (fm);
747 SCM_ASSERT_TYPE (pango_fm, fm_scm, SCM_ARG1, __FUNCTION__, "Pango font");
749 for (SCM s = whxy; scm_is_pair (s); s = scm_cdr (s))
751 SCM now = scm_car (s);
752 widths.push_back (robust_scm2double (scm_car (now), 0.0));
754 heights.push_back (robust_scm2interval (scm_car (now), Interval (0, 0)));
756 xos.push_back (robust_scm2double (scm_car (now), 0.0));
758 yos.push_back (robust_scm2double (scm_car (now), 0.0));
760 char_ids.push_back (robust_scm2string (scm_car (now), ""));
762 Real cumulative_x = 0.0;
763 for (vsize i = 0; i < widths.size (); i++)
765 PangoMatrix transcopy (trans);
766 Offset pt0 (cumulative_x + xos[i], heights[i][DOWN] + yos[i]);
767 Offset pt1 (cumulative_x + widths[i] + xos[i], heights[i][UP] + yos[i]);
768 cumulative_x += widths[i];
771 kerned_bbox.add_point (pt0);
772 kerned_bbox.add_point (pt1);
773 size_t gidx = pango_fm->name_to_index (char_ids[i]);
774 Box real_bbox = pango_fm->get_scaled_indexed_char_dimensions (gidx);
775 Box bbox = pango_fm->get_unscaled_indexed_char_dimensions (gidx);
776 SCM outline = pango_fm->get_glyph_outline (gidx);
778 // scales may have rounding error but should be close
779 Real xlen = real_bbox[X_AXIS].length () / bbox[X_AXIS].length ();
780 Real ylen = real_bbox[Y_AXIS].length () / bbox[Y_AXIS].length ();
785 The value will be nan for whitespace, in which case we just want
786 filler, so the kerned bbox is ok.
788 However, if the value is inf, this likely means that LilyPond is
789 using a font that is currently difficult to get the measurements
790 from the Pango_font. This should eventually be fixed. The solution
791 for now is just to use the bounding box.
793 if (isnan (xlen) || isnan (ylen) || isinf (xlen) || isinf (ylen))
794 outline = box_to_scheme_lines (kerned_bbox);
797 assert (abs (xlen - ylen) < 10e-3);
799 Real scale_factor = max (xlen, ylen);
800 // the three operations below move the stencil from its original coordinates to current coordinates
801 pango_matrix_translate (&transcopy, kerned_bbox[X_AXIS][LEFT], kerned_bbox[Y_AXIS][DOWN] - real_bbox[Y_AXIS][DOWN]);
802 pango_matrix_translate (&transcopy, real_bbox[X_AXIS][LEFT], real_bbox[Y_AXIS][DOWN]);
803 pango_matrix_scale (&transcopy, scale_factor, scale_factor);
804 pango_matrix_translate (&transcopy, -bbox[X_AXIS][LEFT], -bbox[Y_AXIS][DOWN]);
806 //////////////////////
807 for (SCM s = outline;
811 scm_to_int (scm_length (scm_car (s))) == 4
812 ? make_draw_line_boxes (boxes, buildings, transcopy, scm_cons (scm_from_double (0), scm_car (s)), false)
813 : make_draw_bezier_boxes (boxes, buildings, transcopy, scm_cons (scm_from_double (0), scm_car (s)));
819 receives a stencil expression and a transform matrix
820 depending on the stencil name, dispatches it to the appropriate function
824 stencil_dispatcher (vector<Box> &boxes, vector<Drul_array<Offset> > &buildings, PangoMatrix trans, SCM expr)
826 if (not scm_is_pair (expr))
828 if (scm_car (expr) == ly_symbol2scm ("draw-line"))
829 make_draw_line_boxes (boxes, buildings, trans, scm_cdr (expr), true);
830 else if (scm_car (expr) == ly_symbol2scm ("dashed-line"))
832 expr = scm_cdr (expr);
833 SCM th = scm_car (expr);
834 expr = scm_cdr (expr);
835 expr = scm_cdr (expr); // on
836 expr = scm_cdr (expr); // off
837 SCM x1 = scm_car (expr);
838 expr = scm_cdr (expr);
839 SCM x2 = scm_car (expr);
840 make_draw_line_boxes (boxes, buildings, trans, scm_list_5 (th, scm_from_double (0.0), scm_from_double (0.0), x1, x2), true);
842 else if (scm_car (expr) == ly_symbol2scm ("circle"))
844 expr = scm_cdr (expr);
845 SCM rad = scm_car (expr);
846 expr = scm_cdr (expr);
847 SCM th = scm_car (expr);
848 make_partial_ellipse_boxes (boxes, buildings, trans,
851 scm_from_double (0.0),
852 scm_from_double (360.0),
858 else if (scm_car (expr) == ly_symbol2scm ("ellipse"))
860 expr = scm_cdr (expr);
861 SCM x_rad = scm_car (expr);
862 expr = scm_cdr (expr);
863 SCM y_rad = scm_car (expr);
864 expr = scm_cdr (expr);
865 SCM th = scm_car (expr);
866 make_partial_ellipse_boxes (boxes, buildings, trans,
869 scm_from_double (0.0),
870 scm_from_double (360.0),
876 else if (scm_car (expr) == ly_symbol2scm ("partial-ellipse"))
877 make_partial_ellipse_boxes (boxes, buildings, trans, scm_cdr (expr));
878 else if (scm_car (expr) == ly_symbol2scm ("round-filled-box"))
879 make_round_filled_box_boxes (boxes, trans, scm_cdr (expr));
880 else if (scm_car (expr) == ly_symbol2scm ("named-glyph"))
881 make_named_glyph_boxes (boxes, buildings, trans, scm_cdr (expr));
882 else if (scm_car (expr) == ly_symbol2scm ("polygon"))
883 make_polygon_boxes (boxes, buildings, trans, scm_cdr (expr));
884 else if (scm_car (expr) == ly_symbol2scm ("path"))
885 make_path_boxes (boxes, buildings, trans, scm_cdr (expr));
886 else if (scm_car (expr) == ly_symbol2scm ("glyph-string"))
887 make_glyph_string_boxes (boxes, buildings, trans, scm_cdr (expr));
891 warning ("Stencil expression not supported by the veritcal skylines.");
894 We don't issue a warning here, as we assume that stencil-expression.cc
895 is doing stencil-checking correctly.
901 traverses a stencil expression, returning a vector of Transform_matrix_and_expression
902 the struct Transform_matrix_and_expression contains two members,
903 a Transform_matrix that indicates where to move a stencil and the stencil expression
904 to show how to construct the stencil
906 vector<Transform_matrix_and_expression>
907 stencil_traverser (PangoMatrix trans, SCM expr)
909 if (scm_is_null (expr))
910 return vector<Transform_matrix_and_expression> ();
911 else if (expr == ly_string2scm (""))
912 return vector<Transform_matrix_and_expression> ();
913 else if (scm_car (expr) == ly_symbol2scm ("combine-stencil"))
915 vector<Transform_matrix_and_expression> out;
916 for (SCM s = scm_cdr (expr); scm_is_pair (s); s = scm_cdr (s))
918 vector<Transform_matrix_and_expression> res = stencil_traverser (trans, scm_car (s));
919 out.insert (out.end (), res.begin (), res.end ());
923 else if (scm_car (expr) == ly_symbol2scm ("footnote"))
924 return vector<Transform_matrix_and_expression> ();
925 else if (scm_car (expr) == ly_symbol2scm ("translate-stencil"))
927 Real x = robust_scm2double (scm_caadr (expr), 0.0);
928 Real y = robust_scm2double (scm_cdadr (expr), 0.0);
929 pango_matrix_translate (&trans, x, y);
930 return stencil_traverser (trans, scm_caddr (expr));
932 else if (scm_car (expr) == ly_symbol2scm ("scale-stencil"))
934 Real x = robust_scm2double (scm_caadr (expr), 0.0);
935 Real y = robust_scm2double (scm_cadadr (expr), 0.0);
936 pango_matrix_scale (&trans, x, y);
937 return stencil_traverser (trans, scm_caddr (expr));
939 else if (scm_car (expr) == ly_symbol2scm ("rotate-stencil"))
941 Real ang = robust_scm2double (scm_caadr (expr), 0.0);
942 Real x = robust_scm2double (scm_car (scm_cadadr (expr)), 0.0);
943 Real y = robust_scm2double (scm_cdr (scm_cadadr (expr)), 0.0);
944 pango_matrix_translate (&trans, x, y);
945 pango_matrix_rotate (&trans, -ang);
946 pango_matrix_translate (&trans, -x, -y);
947 return stencil_traverser (trans, scm_caddr (expr));
949 else if (scm_car (expr) == ly_symbol2scm ("delay-stencil-evaluation"))
950 return stencil_traverser (trans, scm_force (scm_cadr (expr)));
951 else if (scm_car (expr) == ly_symbol2scm ("grob-cause"))
952 return stencil_traverser (trans, scm_caddr (expr));
953 else if (scm_car (expr) == ly_symbol2scm ("color"))
954 return stencil_traverser (trans, scm_caddr (expr));
955 else if (scm_car (expr) == ly_symbol2scm ("id"))
956 return stencil_traverser (trans, scm_caddr (expr));
959 vector<Transform_matrix_and_expression> out;
960 out.push_back (Transform_matrix_and_expression (trans, expr));
963 warning ("Stencil expression not supported by the veritcal skylines.");
964 return vector<Transform_matrix_and_expression> ();
968 Grob::maybe_pure_internal_simple_skylines_from_extents (Grob *me, Axis a, bool pure, int beg, int end, bool ignore_x, bool ignore_y)
971 // we don't know how far spanners stretch along the X axis before
972 // line breaking. better have them take up the whole thing
973 Interval xex = ignore_x
974 ? Interval (-infinity_f, infinity_f)
975 : me->extent (me, X_AXIS);
977 // If we're looking at the x exent of a cross staff grob, it could be
978 // very early on in the computation process. We won't know its height
979 // until way later, so we give a brute force approximation.
980 Interval yex = ignore_y
981 ? Interval (-infinity_f, infinity_f)
982 : me->maybe_pure_extent (me, Y_AXIS, pure, beg, end);
984 if (xex.is_empty () || yex.is_empty ())
985 return Skyline_pair ().smobbed_copy ();
987 boxes.push_back (Box (xex, yex));
988 return Skyline_pair (boxes, a).smobbed_copy ();
991 MAKE_SCHEME_CALLBACK (Grob, pure_simple_vertical_skylines_from_extents, 3);
993 Grob::pure_simple_vertical_skylines_from_extents (SCM smob, SCM begscm, SCM endscm)
995 Grob *me = unsmob_grob (smob);
996 int beg = robust_scm2int (begscm, 0);
997 int end = robust_scm2int (endscm, INT_MAX);
998 // We cannot measure the width of a spanner before line breaking,
999 // so we assume that the width is infinite.
1000 return maybe_pure_internal_simple_skylines_from_extents (me, X_AXIS, true, beg, end, dynamic_cast<Spanner *> (me), false);
1003 MAKE_SCHEME_CALLBACK (Grob, simple_vertical_skylines_from_extents, 1);
1005 Grob::simple_vertical_skylines_from_extents (SCM smob)
1007 Grob *me = unsmob_grob (smob);
1008 return maybe_pure_internal_simple_skylines_from_extents (me, X_AXIS, false, 0, 0, false, false);
1011 MAKE_SCHEME_CALLBACK (Grob, pure_simple_horizontal_skylines_from_extents, 3);
1013 Grob::pure_simple_horizontal_skylines_from_extents (SCM smob, SCM begscm, SCM endscm)
1015 Grob *me = unsmob_grob (smob);
1016 int beg = robust_scm2int (begscm, 0);
1017 int end = robust_scm2int (endscm, INT_MAX);
1018 // If the grob is cross staff, we cannot measure its Y-extent before
1019 // wayyyy downstream (after spacing of axis groups is done).
1020 // Thus, we assume that the Y extent is infinite for cross staff grobs.
1021 return maybe_pure_internal_simple_skylines_from_extents (me, Y_AXIS, true, beg, end, false, to_boolean (me->get_property ("cross-staff")));
1024 MAKE_SCHEME_CALLBACK (Grob, simple_horizontal_skylines_from_extents, 1);
1026 Grob::simple_horizontal_skylines_from_extents (SCM smob)
1028 Grob *me = unsmob_grob (smob);
1029 // See comment in function above.
1030 return maybe_pure_internal_simple_skylines_from_extents (me, Y_AXIS, false, 0, 0, false, to_boolean (me->get_property ("cross-staff")));
1034 Stencil::skylines_from_stencil (SCM sten, Real pad, Axis a)
1036 Stencil *s = unsmob_stencil (sten);
1038 return Skyline_pair ().smobbed_copy ();
1040 vector<Transform_matrix_and_expression> data
1041 = stencil_traverser (make_transform_matrix (1.0, 0.0, 0.0, 1.0, 0.0, 0.0),
1044 vector<Drul_array<Offset> > buildings;
1045 for (vsize i = 0; i < data.size (); i++)
1046 stencil_dispatcher (boxes, buildings, data[i].tm_, data[i].expr_);
1048 // we use the bounding box if there are no boxes
1049 if (!boxes.size () && !buildings.size ())
1050 boxes.push_back (Box (s->extent (X_AXIS), s->extent (Y_AXIS)));
1052 Skyline_pair out (boxes, a);
1053 out.merge (Skyline_pair (buildings, a));
1055 for (DOWN_and_UP (d))
1056 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 = Skyline_pair::unsmob (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);