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 (scm_is_false (res))
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_is_true (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"),
142 SCM res = get_path_list (scm_car (l));
143 if (scm_is_false (res))
144 return get_path_list (scm_cdr (l));
151 perpendicular_slope (Real s)
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);
183 Real slope = x1 == x0 ? infinity_f : (y1 - y0) / (x1 - x0);
184 //////////////////////
190 Offset left (x0, y0);
191 Offset right (x1, y1);
195 Offset inter_l = get_point_in_y_direction (left, perpendicular_slope (slope), thick / 2, d);
196 Offset inter_r = get_point_in_y_direction (right, perpendicular_slope (slope), thick / 2, d);
197 pango_matrix_transform_point (&trans, &inter_l[X_AXIS], &inter_l[Y_AXIS]);
198 pango_matrix_transform_point (&trans, &inter_r[X_AXIS], &inter_r[Y_AXIS]);
199 if ((inter_l[X_AXIS] == inter_r[X_AXIS]) || (inter_l[Y_AXIS] == inter_r[Y_AXIS]))
202 b.add_point (inter_l);
203 b.add_point (inter_r);
206 else if (use_building)
207 buildings.push_back (Drul_array<Offset> (inter_l, inter_r));
210 Offset inter_l = get_point_in_y_direction (left, perpendicular_slope (slope), thick / 2, d);
211 Offset inter_r = get_point_in_y_direction (right, perpendicular_slope (slope), thick / 2, d);
212 pango_matrix_transform_point (&trans, &inter_l[X_AXIS], &inter_l[Y_AXIS]);
213 pango_matrix_transform_point (&trans, &inter_r[X_AXIS], &inter_r[Y_AXIS]);
214 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])));
216 vsize passes = (vsize) ((length * 2) + 1);
217 vector<Offset> points;
219 for (vsize i = 0; i < 1 + passes; i++)
221 Offset pt (linear_map (x0, x1, 0, passes, i),
222 linear_map (y0, y1, 0, passes, i));
223 Offset inter = get_point_in_y_direction (pt, perpendicular_slope (slope), thick / 2, d);
224 pango_matrix_transform_point (&trans, &inter[X_AXIS], &inter[Y_AXIS]);
225 points.push_back (inter);
227 for (vsize i = 0; i < points.size () - 1; i++)
230 b.add_point (points[i]);
231 b.add_point (points[i + 1]);
236 while (flip (&d) != DOWN);
241 create_path_cap (boxes,
246 perpendicular_slope (slope),
247 Direction (sign (slope)));
250 create_path_cap (boxes,
255 perpendicular_slope (slope),
256 Direction (sign (-slope)));
261 make_partial_ellipse_boxes (vector<Box> &boxes, vector<Drul_array<Offset> > &buildings, PangoMatrix trans, SCM expr)
263 Real x_rad = robust_scm2double (scm_car (expr), 0.0);
264 expr = scm_cdr (expr);
265 Real y_rad = robust_scm2double (scm_car (expr), 0.0);
266 expr = scm_cdr (expr);
267 Real start = robust_scm2double (scm_car (expr), 0.0);
268 expr = scm_cdr (expr);
269 Real end = robust_scm2double (scm_car (expr), 0.0);
270 expr = scm_cdr (expr);
271 Real th = robust_scm2double (scm_car (expr), 0.0);
272 expr = scm_cdr (expr);
273 bool connect = to_boolean (scm_car (expr));
274 expr = scm_cdr (expr);
275 bool fill = to_boolean (scm_car (expr));
276 //////////////////////
277 start = M_PI * start / 180;
278 end = M_PI * end / 180;
281 complex<Real> sunit = polar (1.0, start);
282 complex<Real> eunit = polar (1.0, end);
283 Offset sp (real (sunit) * x_rad, imag (sunit) * y_rad);
284 Offset ep (real (eunit) * x_rad, imag (eunit) * y_rad);
285 //////////////////////
286 Drul_array<vector<Offset> > points;
288 int quantization = max (1, (int) (((x_rad * trans.xx) + (y_rad * trans.yy)) * M_PI / QUANTIZATION_UNIT));
291 for (vsize i = 0; i < 1 + (vsize) quantization; i++)
293 Real ang = linear_map (start, end, 0, quantization, i);
294 complex<Real> coord = polar (1.0, ang);
295 Offset pt (real (coord) * x_rad,
296 imag (coord) * y_rad);
297 Real slope = pt[Y_AXIS] / pt[X_AXIS];
298 Offset inter = get_point_in_y_direction (pt, perpendicular_slope (slope), th / 2, d);
299 pango_matrix_transform_point (&trans, &inter[X_AXIS], &inter[Y_AXIS]);
300 points[d].push_back (inter);
303 while (flip (&d) != DOWN);
305 for (vsize i = 0; i < points[DOWN].size () - 1; i++)
310 b.add_point (points[d][i]);
311 b.add_point (points[d][i + 1]);
313 while (flip (&d) != DOWN);
319 make_draw_line_boxes (boxes, buildings, trans, scm_list_5 (scm_from_double (th),
320 scm_from_double (sp[X_AXIS]),
321 scm_from_double (sp[Y_AXIS]),
322 scm_from_double (ep[X_AXIS]),
323 scm_from_double (ep[Y_AXIS])),
330 complex<Real> coord = polar (1.0, start);
331 Offset pt (real (coord) * x_rad,
332 imag (coord) * y_rad);
333 Real slope = pt[Y_AXIS] / pt[X_AXIS];
334 create_path_cap (boxes,
339 perpendicular_slope (slope),
340 Direction (sign (slope)));
343 coord = polar (1.0, start);
344 pt = Offset (real (coord) * x_rad,
345 imag (coord) * y_rad);
346 slope = pt[Y_AXIS] / pt[X_AXIS];
347 create_path_cap (boxes,
352 perpendicular_slope (slope),
353 Direction (sign (-slope)));
358 make_round_filled_box_boxes (vector<Box> &boxes, PangoMatrix trans, SCM expr)
360 Real left = robust_scm2double (scm_car (expr), 0.0);
361 expr = scm_cdr (expr);
362 Real right = robust_scm2double (scm_car (expr), 0.0);
363 expr = scm_cdr (expr);
364 Real bottom = robust_scm2double (scm_car (expr), 0.0);
365 expr = scm_cdr (expr);
366 Real top = robust_scm2double (scm_car (expr), 0.0);
367 expr = scm_cdr (expr);
368 Real th = robust_scm2double (scm_car (expr), 0.0);
369 //////////////////////
370 vector<Offset> points;
372 Offset p0 = Offset (-left - (th / 2), -bottom - (th / 2));
373 Offset p1 = Offset (right + (th / 2), top + (th / 2));
374 pango_matrix_transform_point (&trans, &p0[X_AXIS], &p0[Y_AXIS]);
375 pango_matrix_transform_point (&trans, &p1[X_AXIS], &p1[Y_AXIS]);
382 create_path_cap (vector<Box> &boxes, vector<Drul_array<Offset> > &buildings, PangoMatrix trans, Offset pt, Real rad, Real slope, Direction d)
384 Real angle = atan (slope) * 180 / M_PI;
385 Real other = angle > 180 ? angle - 180 : angle + 180;
392 other = (slope >= 0 && d == DOWN) || (slope < 0 && d == UP)
395 PangoMatrix new_trans (trans);
396 pango_matrix_translate (&new_trans, pt[X_AXIS], pt[Y_AXIS]);
397 make_partial_ellipse_boxes (boxes, buildings, new_trans,
398 scm_list_n (scm_from_double (rad),
399 scm_from_double (rad),
400 scm_from_double (angle),
401 scm_from_double (other),
402 scm_from_double (0.0),
409 make_draw_bezier_boxes (vector<Box> &boxes, vector<Drul_array<Offset> > &buildings, PangoMatrix trans, SCM expr)
411 Real th = robust_scm2double (scm_car (expr), 0.0);
412 expr = scm_cdr (expr);
413 Real x0 = robust_scm2double (scm_car (expr), 0.0);
414 expr = scm_cdr (expr);
415 Real y0 = robust_scm2double (scm_car (expr), 0.0);
416 expr = scm_cdr (expr);
417 Real x1 = robust_scm2double (scm_car (expr), 0.0);
418 expr = scm_cdr (expr);
419 Real y1 = robust_scm2double (scm_car (expr), 0.0);
420 expr = scm_cdr (expr);
421 Real x2 = robust_scm2double (scm_car (expr), 0.0);
422 expr = scm_cdr (expr);
423 Real y2 = robust_scm2double (scm_car (expr), 0.0);
424 expr = scm_cdr (expr);
425 Real x3 = robust_scm2double (scm_car (expr), 0.0);
426 expr = scm_cdr (expr);
427 Real y3 = robust_scm2double (scm_car (expr), 0.0);
428 //////////////////////
430 curve.control_[0] = Offset (x0, y0);
431 curve.control_[1] = Offset (x1, y1);
432 curve.control_[2] = Offset (x2, y2);
433 curve.control_[3] = Offset (x3, y3);
434 Offset temp0 (x0, y0);
435 Offset temp1 (x1, y1);
436 Offset temp2 (x2, y2);
437 Offset temp3 (x3, y3);
438 pango_matrix_transform_point (&trans, &temp0[X_AXIS], &temp0[Y_AXIS]);
439 pango_matrix_transform_point (&trans, &temp1[X_AXIS], &temp1[Y_AXIS]);
440 pango_matrix_transform_point (&trans, &temp2[X_AXIS], &temp2[Y_AXIS]);
441 pango_matrix_transform_point (&trans, &temp3[X_AXIS], &temp3[Y_AXIS]);
442 //////////////////////
443 Drul_array<vector<Offset> > points;
445 int quantization = int (((temp1 - temp0).length ()
446 + (temp2 - temp1).length ()
447 + (temp3 - temp2).length ())
448 / QUANTIZATION_UNIT);
451 Offset first = get_point_in_y_direction (curve.control_[0], perpendicular_slope (curve.slope_at_point (0.0)), th / 2, d);
452 pango_matrix_transform_point (&trans, &first[X_AXIS], &first[Y_AXIS]);
453 points[d].push_back (first);
454 for (vsize i = 1; i < (vsize) quantization; i++)
456 Real pt = (i * 1.0) / quantization;
457 Offset inter = get_point_in_y_direction (curve.curve_point (pt), perpendicular_slope (curve.slope_at_point (pt)), th / 2, d);
458 pango_matrix_transform_point (&trans, &inter[X_AXIS], &inter[Y_AXIS]);
459 points[d].push_back (inter);
461 Offset last = get_point_in_y_direction (curve.control_[3], curve.slope_at_point (1.0), th / 2, d);
462 pango_matrix_transform_point (&trans, &last[X_AXIS], &last[Y_AXIS]);
463 points[d].push_back (last);
465 while (flip (&d) != DOWN);
467 for (vsize i = 0; i < points[DOWN].size () - 1; i++)
472 b.add_point (points[d][i]);
473 b.add_point (points[d][i + 1]);
475 while (flip (&d) != DOWN);
482 Real slope = curve.slope_at_point (0.0);
483 d = Direction (sign (slope == 0.0 || abs (slope) == infinity_f
484 ? curve.slope_at_point (0.0001)
487 create_path_cap (boxes,
492 perpendicular_slope (curve.slope_at_point (0.0)),
496 slope = curve.slope_at_point (1.0);
497 d = Direction (sign (slope == 0.0 || abs (slope) == infinity_f
498 ? curve.slope_at_point (0.9999)
501 create_path_cap (boxes,
506 perpendicular_slope (curve.slope_at_point (1.0)),
512 converts a path into lists of 4 (line) or 8 (curve) absolute coordinates
514 '(moveto 1 2 lineto 3 4 rlineto -1 -1 curveto 3 3 5 5 6 6 rcurveto -1 -1 -1 -1 -1 -1 closepath)
524 all_commands_to_absolute_and_group (SCM expr)
528 Offset current (0, 0);
530 while (scm_is_pair (expr))
532 if (scm_is_eq (scm_car (expr), ly_symbol2scm ("moveto"))
533 || (scm_is_eq (scm_car (expr), ly_symbol2scm ("rmoveto")) && first))
535 Real x = robust_scm2double (scm_cadr (expr), 0.0);
536 Real y = robust_scm2double (scm_caddr (expr), 0.0);
537 start = Offset (x, y);
539 expr = scm_cdddr (expr);
541 if (scm_is_eq (scm_car (expr), ly_symbol2scm ("rmoveto")))
543 Real x = robust_scm2double (scm_cadr (expr), 0.0);
544 Real y = robust_scm2double (scm_caddr (expr), 0.0);
545 start = (Offset (x, y) + current);
547 expr = scm_cdddr (expr);
549 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("lineto")))
551 Real x = robust_scm2double (scm_cadr (expr), 0.0);
552 Real y = robust_scm2double (scm_caddr (expr), 0.0);
553 out = scm_cons (scm_list_4 (scm_from_double (current[X_AXIS]),
554 scm_from_double (current[Y_AXIS]),
556 scm_from_double (y)),
558 current = Offset (x, y);
559 expr = scm_cdddr (expr);
561 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("rlineto")))
563 Real x = robust_scm2double (scm_cadr (expr), 0.0);
564 Real y = robust_scm2double (scm_caddr (expr), 0.0);
565 out = scm_cons (scm_list_4 (scm_from_double (current[X_AXIS]),
566 scm_from_double (current[Y_AXIS]),
567 scm_from_double (x + current[X_AXIS]),
568 scm_from_double (y + current[Y_AXIS])),
570 current = (Offset (x, y) + current);
571 expr = scm_cdddr (expr);
573 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("curveto")))
575 Real x1 = robust_scm2double (scm_cadr (expr), 0.0);
576 expr = scm_cddr (expr);
577 Real y1 = robust_scm2double (scm_car (expr), 0.0);
578 expr = scm_cdr (expr);
579 Real x2 = robust_scm2double (scm_car (expr), 0.0);
580 expr = scm_cdr (expr);
581 Real y2 = robust_scm2double (scm_car (expr), 0.0);
582 expr = scm_cdr (expr);
583 Real x3 = robust_scm2double (scm_car (expr), 0.0);
584 expr = scm_cdr (expr);
585 Real y3 = robust_scm2double (scm_car (expr), 0.0);
586 expr = scm_cdr (expr);
587 out = scm_cons (scm_list_n (scm_from_double (current[X_AXIS]),
588 scm_from_double (current[Y_AXIS]),
589 scm_from_double (x1),
590 scm_from_double (y1),
591 scm_from_double (x2),
592 scm_from_double (y2),
593 scm_from_double (x3),
594 scm_from_double (y3),
597 current = Offset (x3, y3);
599 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("rcurveto")))
601 Real x1 = robust_scm2double (scm_cadr (expr), 0.0);
602 expr = scm_cddr (expr);
603 Real y1 = robust_scm2double (scm_car (expr), 0.0);
604 expr = scm_cdr (expr);
605 Real x2 = robust_scm2double (scm_car (expr), 0.0);
606 expr = scm_cdr (expr);
607 Real y2 = robust_scm2double (scm_car (expr), 0.0);
608 expr = scm_cdr (expr);
609 Real x3 = robust_scm2double (scm_car (expr), 0.0);
610 expr = scm_cdr (expr);
611 Real y3 = robust_scm2double (scm_car (expr), 0.0);
612 expr = scm_cdr (expr);
613 out = scm_cons (scm_list_n (scm_from_double (current[X_AXIS]),
614 scm_from_double (current[Y_AXIS]),
615 scm_from_double (x1 + current[X_AXIS]),
616 scm_from_double (y1 + current[Y_AXIS]),
617 scm_from_double (x2 + current[X_AXIS]),
618 scm_from_double (y2 + current[Y_AXIS]),
619 scm_from_double (x3 + current[X_AXIS]),
620 scm_from_double (y3 + current[Y_AXIS]),
623 current = (Offset (x3, y3) + current);
625 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("closepath")))
627 if ((current[X_AXIS] != start[X_AXIS]) || (current[Y_AXIS] != start[Y_AXIS]))
629 out = scm_cons (scm_list_4 (scm_from_double (current[X_AXIS]),
630 scm_from_double (current[Y_AXIS]),
631 scm_from_double (start[X_AXIS]),
632 scm_from_double (start[Y_AXIS])),
636 expr = scm_cdr (expr);
640 warning ("Malformed path for path stencil.");
645 return scm_reverse_x (out, SCM_EOL);
649 internal_make_path_boxes (vector<Box> &boxes, vector<Drul_array<Offset> > &buildings, PangoMatrix trans, SCM expr, bool use_building)
651 SCM blot = scm_car (expr);
652 expr = scm_cdr (expr);
653 SCM path = all_commands_to_absolute_and_group (expr);
654 // note that expr has more stuff that we don't need after this - simply ignore it
655 //////////////////////
656 for (SCM s = path; scm_is_pair (s); s = scm_cdr (s))
658 scm_to_int (scm_length (scm_car (s))) == 4
659 ? make_draw_line_boxes (boxes, buildings, trans, scm_cons (blot, scm_car (s)), use_building)
660 : make_draw_bezier_boxes (boxes, buildings, trans, scm_cons (blot, scm_car (s)));
665 make_path_boxes (vector<Box> &boxes, vector<Drul_array<Offset> > &buildings, PangoMatrix trans, SCM expr)
667 return internal_make_path_boxes (boxes, buildings, trans, scm_cons (scm_car (expr), get_path_list (scm_cdr (expr))), false);
671 make_polygon_boxes (vector<Box> &boxes, vector<Drul_array<Offset> > &buildings, PangoMatrix trans, SCM expr)
673 SCM coords = get_number_list (scm_car (expr));
674 expr = scm_cdr (expr);
675 SCM blot_diameter = scm_car (expr);
676 //////////////////////
679 for (SCM s = coords; scm_is_pair (s); s = scm_cddr (s))
681 l = scm_cons (first ? ly_symbol2scm ("moveto") : ly_symbol2scm ("lineto"), l);
682 l = scm_cons (scm_car (s), l);
683 l = scm_cons (scm_cadr (s), l);
686 l = scm_cons (ly_symbol2scm ("closepath"), l);
687 internal_make_path_boxes (boxes, buildings, trans, scm_cons (blot_diameter, scm_reverse_x (l, SCM_EOL)), true);
691 make_named_glyph_boxes (vector<Box> &boxes, vector<Drul_array<Offset> > &buildings, PangoMatrix trans, SCM expr)
693 SCM fm_scm = scm_car (expr);
694 Font_metric *fm = Font_metric::unsmob (fm_scm);
695 expr = scm_cdr (expr);
696 SCM glyph = scm_car (expr);
697 string glyph_s = ly_scm2string (glyph);
699 //////////////////////
700 Open_type_font *open_fm
701 = dynamic_cast<Open_type_font *>
702 (dynamic_cast<Modified_font_metric *>(fm)->original_font ());
703 SCM_ASSERT_TYPE (open_fm, fm_scm, SCM_ARG1, __FUNCTION__, "OpenType font");
705 size_t gidx = open_fm->name_to_index (glyph_s);
706 // Bbox is the best approximation of the width based on how it would be
707 // calculated in open-type-font.cc if it were based on real extents
708 Box bbox = open_fm->get_unscaled_indexed_char_dimensions (gidx);
709 bbox.scale (dynamic_cast<Modified_font_metric *>(fm)->get_magnification () * open_fm->design_size () / open_fm->get_units_per_EM ());
710 // Real bbox is the real bbox of the object
711 Box real_bbox = open_fm->get_glyph_outline_bbox (gidx);
713 Real scale = bbox[X_AXIS].length () / real_bbox[X_AXIS].length ();
715 pango_matrix_scale (&trans, scale, scale);
717 SCM outline = open_fm->get_glyph_outline (gidx);
718 //////////////////////
719 for (SCM s = outline;
723 scm_to_int (scm_length (scm_car (s))) == 4
724 ? make_draw_line_boxes (boxes, buildings, trans, scm_cons (scm_from_double (0), scm_car (s)), false)
725 : make_draw_bezier_boxes (boxes, buildings, trans, scm_cons (scm_from_double (0), scm_car (s)));
730 make_glyph_string_boxes (vector<Box> &boxes, vector<Drul_array<Offset> > &buildings, PangoMatrix trans, SCM expr)
732 SCM fm_scm = scm_car (expr);
733 Font_metric *fm = Font_metric::unsmob (fm_scm);
734 expr = scm_cdr (expr);
735 expr = scm_cdr (expr); // font-name
736 expr = scm_cdr (expr); // size
737 expr = scm_cdr (expr); // cid?
738 SCM whxy = scm_cadar (expr);
740 vector<Interval> heights;
743 vector<string> char_ids;
744 //////////////////////
745 Pango_font *pango_fm = dynamic_cast<Pango_font *> (fm);
746 SCM_ASSERT_TYPE (pango_fm, fm_scm, SCM_ARG1, __FUNCTION__, "Pango font");
748 for (SCM s = whxy; scm_is_pair (s); s = scm_cdr (s))
750 SCM now = scm_car (s);
751 widths.push_back (robust_scm2double (scm_car (now), 0.0));
753 heights.push_back (robust_scm2interval (scm_car (now), Interval (0, 0)));
755 xos.push_back (robust_scm2double (scm_car (now), 0.0));
757 yos.push_back (robust_scm2double (scm_car (now), 0.0));
759 char_ids.push_back (robust_scm2string (scm_car (now), ""));
761 Real cumulative_x = 0.0;
762 for (vsize i = 0; i < widths.size (); i++)
764 PangoMatrix transcopy (trans);
765 Offset pt0 (cumulative_x + xos[i], heights[i][DOWN] + yos[i]);
766 Offset pt1 (cumulative_x + widths[i] + xos[i], heights[i][UP] + yos[i]);
767 cumulative_x += widths[i];
770 kerned_bbox.add_point (pt0);
771 kerned_bbox.add_point (pt1);
772 size_t gidx = pango_fm->name_to_index (char_ids[i]);
773 Box real_bbox = pango_fm->get_scaled_indexed_char_dimensions (gidx);
774 Box bbox = pango_fm->get_unscaled_indexed_char_dimensions (gidx);
775 SCM outline = pango_fm->get_glyph_outline (gidx);
777 // scales may have rounding error but should be close
778 Real xlen = real_bbox[X_AXIS].length () / bbox[X_AXIS].length ();
779 Real ylen = real_bbox[Y_AXIS].length () / bbox[Y_AXIS].length ();
784 The value will be nan for whitespace, in which case we just want
785 filler, so the kerned bbox is ok.
787 However, if the value is inf, this likely means that LilyPond is
788 using a font that is currently difficult to get the measurements
789 from the Pango_font. This should eventually be fixed. The solution
790 for now is just to use the bounding box.
792 if (isnan (xlen) || isnan (ylen) || isinf (xlen) || isinf (ylen))
793 outline = box_to_scheme_lines (kerned_bbox);
796 assert (abs (xlen - ylen) < 10e-3);
798 Real scale_factor = max (xlen, ylen);
799 // the three operations below move the stencil from its original coordinates to current coordinates
800 pango_matrix_translate (&transcopy, kerned_bbox[X_AXIS][LEFT], kerned_bbox[Y_AXIS][DOWN] - real_bbox[Y_AXIS][DOWN]);
801 pango_matrix_translate (&transcopy, real_bbox[X_AXIS][LEFT], real_bbox[Y_AXIS][DOWN]);
802 pango_matrix_scale (&transcopy, scale_factor, scale_factor);
803 pango_matrix_translate (&transcopy, -bbox[X_AXIS][LEFT], -bbox[Y_AXIS][DOWN]);
805 //////////////////////
806 for (SCM s = outline;
810 scm_to_int (scm_length (scm_car (s))) == 4
811 ? make_draw_line_boxes (boxes, buildings, transcopy, scm_cons (scm_from_double (0), scm_car (s)), false)
812 : make_draw_bezier_boxes (boxes, buildings, transcopy, scm_cons (scm_from_double (0), scm_car (s)));
818 receives a stencil expression and a transform matrix
819 depending on the stencil name, dispatches it to the appropriate function
823 stencil_dispatcher (vector<Box> &boxes, vector<Drul_array<Offset> > &buildings, PangoMatrix trans, SCM expr)
825 if (not scm_is_pair (expr))
827 if (scm_is_eq (scm_car (expr), ly_symbol2scm ("draw-line")))
828 make_draw_line_boxes (boxes, buildings, trans, scm_cdr (expr), true);
829 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("dashed-line")))
831 expr = scm_cdr (expr);
832 SCM th = scm_car (expr);
833 expr = scm_cdr (expr);
834 expr = scm_cdr (expr); // on
835 expr = scm_cdr (expr); // off
836 SCM x1 = scm_car (expr);
837 expr = scm_cdr (expr);
838 SCM x2 = scm_car (expr);
839 make_draw_line_boxes (boxes, buildings, trans, scm_list_5 (th, scm_from_double (0.0), scm_from_double (0.0), x1, x2), true);
841 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("circle")))
843 expr = scm_cdr (expr);
844 SCM rad = scm_car (expr);
845 expr = scm_cdr (expr);
846 SCM th = scm_car (expr);
847 make_partial_ellipse_boxes (boxes, buildings, trans,
850 scm_from_double (0.0),
851 scm_from_double (360.0),
857 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("ellipse")))
859 expr = scm_cdr (expr);
860 SCM x_rad = scm_car (expr);
861 expr = scm_cdr (expr);
862 SCM y_rad = scm_car (expr);
863 expr = scm_cdr (expr);
864 SCM th = scm_car (expr);
865 make_partial_ellipse_boxes (boxes, buildings, trans,
868 scm_from_double (0.0),
869 scm_from_double (360.0),
875 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("partial-ellipse")))
876 make_partial_ellipse_boxes (boxes, buildings, trans, scm_cdr (expr));
877 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("round-filled-box")))
878 make_round_filled_box_boxes (boxes, trans, scm_cdr (expr));
879 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("named-glyph")))
880 make_named_glyph_boxes (boxes, buildings, trans, scm_cdr (expr));
881 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("polygon")))
882 make_polygon_boxes (boxes, buildings, trans, scm_cdr (expr));
883 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("path")))
884 make_path_boxes (boxes, buildings, trans, scm_cdr (expr));
885 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("glyph-string")))
886 make_glyph_string_boxes (boxes, buildings, trans, scm_cdr (expr));
890 warning ("Stencil expression not supported by the veritcal skylines.");
893 We don't issue a warning here, as we assume that stencil-expression.cc
894 is doing stencil-checking correctly.
900 traverses a stencil expression, returning a vector of Transform_matrix_and_expression
901 the struct Transform_matrix_and_expression contains two members,
902 a Transform_matrix that indicates where to move a stencil and the stencil expression
903 to show how to construct the stencil
905 vector<Transform_matrix_and_expression>
906 stencil_traverser (PangoMatrix trans, SCM expr)
908 if (scm_is_null (expr))
909 return vector<Transform_matrix_and_expression> ();
910 else if (scm_is_eq (expr, ly_string2scm ("")))
911 return vector<Transform_matrix_and_expression> ();
912 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("combine-stencil")))
914 vector<Transform_matrix_and_expression> out;
915 for (SCM s = scm_cdr (expr); scm_is_pair (s); s = scm_cdr (s))
917 vector<Transform_matrix_and_expression> res = stencil_traverser (trans, scm_car (s));
918 out.insert (out.end (), res.begin (), res.end ());
922 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("footnote")))
923 return vector<Transform_matrix_and_expression> ();
924 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("translate-stencil")))
926 Real x = robust_scm2double (scm_caadr (expr), 0.0);
927 Real y = robust_scm2double (scm_cdadr (expr), 0.0);
928 pango_matrix_translate (&trans, x, y);
929 return stencil_traverser (trans, scm_caddr (expr));
931 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("scale-stencil")))
933 Real x = robust_scm2double (scm_caadr (expr), 0.0);
934 Real y = robust_scm2double (scm_cadadr (expr), 0.0);
935 pango_matrix_scale (&trans, x, y);
936 return stencil_traverser (trans, scm_caddr (expr));
938 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("rotate-stencil")))
940 Real ang = robust_scm2double (scm_caadr (expr), 0.0);
941 Real x = robust_scm2double (scm_car (scm_cadadr (expr)), 0.0);
942 Real y = robust_scm2double (scm_cdr (scm_cadadr (expr)), 0.0);
943 pango_matrix_translate (&trans, x, y);
944 pango_matrix_rotate (&trans, -ang);
945 pango_matrix_translate (&trans, -x, -y);
946 return stencil_traverser (trans, scm_caddr (expr));
948 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("delay-stencil-evaluation")))
949 // should not use the place-holder text, but no need for the warning below
950 return vector<Transform_matrix_and_expression> ();
951 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("grob-cause")))
952 return stencil_traverser (trans, scm_caddr (expr));
953 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("color")))
954 return stencil_traverser (trans, scm_caddr (expr));
955 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("transparent-stencil")))
956 return stencil_traverser (trans, scm_cadr (expr));
957 else if (scm_is_eq (scm_car (expr), ly_symbol2scm ("id")))
958 return stencil_traverser (trans, scm_caddr (expr));
961 vector<Transform_matrix_and_expression> out;
962 out.push_back (Transform_matrix_and_expression (trans, expr));
965 warning ("Stencil expression not supported by the veritcal skylines.");
966 return vector<Transform_matrix_and_expression> ();
970 Grob::maybe_pure_internal_simple_skylines_from_extents (Grob *me, Axis a, bool pure, int beg, int end, bool ignore_x, bool ignore_y)
973 // we don't know how far spanners stretch along the X axis before
974 // line breaking. better have them take up the whole thing
975 Interval xex = ignore_x
976 ? Interval (-infinity_f, infinity_f)
977 : me->extent (me, X_AXIS);
979 // If we're looking at the x exent of a cross staff grob, it could be
980 // very early on in the computation process. We won't know its height
981 // until way later, so we give a brute force approximation.
982 Interval yex = ignore_y
983 ? Interval (-infinity_f, infinity_f)
984 : me->maybe_pure_extent (me, Y_AXIS, pure, beg, end);
986 if (xex.is_empty () || yex.is_empty ())
987 return Skyline_pair ().smobbed_copy ();
989 boxes.push_back (Box (xex, yex));
990 return Skyline_pair (boxes, a).smobbed_copy ();
993 MAKE_SCHEME_CALLBACK (Grob, pure_simple_vertical_skylines_from_extents, 3);
995 Grob::pure_simple_vertical_skylines_from_extents (SCM smob, SCM begscm, SCM endscm)
997 Grob *me = Grob::unsmob (smob);
998 int beg = robust_scm2int (begscm, 0);
999 int end = robust_scm2int (endscm, INT_MAX);
1000 // We cannot measure the widths before line breaking,
1001 // so we assume that the width is infinite: pass ignore_x=true
1002 return maybe_pure_internal_simple_skylines_from_extents (me, X_AXIS, true, beg, end, true, false);
1005 MAKE_SCHEME_CALLBACK (Grob, simple_vertical_skylines_from_extents, 1);
1007 Grob::simple_vertical_skylines_from_extents (SCM smob)
1009 Grob *me = Grob::unsmob (smob);
1010 return maybe_pure_internal_simple_skylines_from_extents (me, X_AXIS, false, 0, 0, false, false);
1013 MAKE_SCHEME_CALLBACK (Grob, pure_simple_horizontal_skylines_from_extents, 3);
1015 Grob::pure_simple_horizontal_skylines_from_extents (SCM smob, SCM begscm, SCM endscm)
1017 Grob *me = Grob::unsmob (smob);
1018 int beg = robust_scm2int (begscm, 0);
1019 int end = robust_scm2int (endscm, INT_MAX);
1020 // If the grob is cross staff, we cannot measure its Y-extent before
1021 // wayyyy downstream (after spacing of axis groups is done).
1022 // Thus, we assume that the Y extent is infinite for cross staff grobs.
1023 return maybe_pure_internal_simple_skylines_from_extents (me, Y_AXIS, true, beg, end, false, to_boolean (me->get_property ("cross-staff")));
1026 MAKE_SCHEME_CALLBACK (Grob, simple_horizontal_skylines_from_extents, 1);
1028 Grob::simple_horizontal_skylines_from_extents (SCM smob)
1030 Grob *me = Grob::unsmob (smob);
1031 // See comment in function above.
1032 return maybe_pure_internal_simple_skylines_from_extents (me, Y_AXIS, false, 0, 0, false, to_boolean (me->get_property ("cross-staff")));
1036 Stencil::skylines_from_stencil (SCM sten, Real pad, Axis a)
1038 Stencil *s = Stencil::unsmob (sten);
1040 return Skyline_pair ().smobbed_copy ();
1042 vector<Transform_matrix_and_expression> data
1043 = stencil_traverser (make_transform_matrix (1.0, 0.0, 0.0, 1.0, 0.0, 0.0),
1046 vector<Drul_array<Offset> > buildings;
1047 for (vsize i = 0; i < data.size (); i++)
1048 stencil_dispatcher (boxes, buildings, data[i].tm_, data[i].expr_);
1050 // we use the bounding box if there are no boxes
1051 if (!boxes.size () && !buildings.size ())
1052 boxes.push_back (Box (s->extent (X_AXIS), s->extent (Y_AXIS)));
1054 Skyline_pair out (boxes, a);
1055 out.merge (Skyline_pair (buildings, a));
1057 for (DOWN_and_UP (d))
1058 out[d] = out[d].padded (pad);
1060 return out.smobbed_copy ();
1063 MAKE_SCHEME_CALLBACK (Grob, vertical_skylines_from_stencil, 1);
1065 Grob::vertical_skylines_from_stencil (SCM smob)
1067 Grob *me = Grob::unsmob (smob);
1069 Real pad = robust_scm2double (me->get_property ("skyline-horizontal-padding"), 0.0);
1070 SCM out = Stencil::skylines_from_stencil (me->get_property ("stencil"), pad, X_AXIS);
1075 MAKE_SCHEME_CALLBACK (Grob, horizontal_skylines_from_stencil, 1);
1077 Grob::horizontal_skylines_from_stencil (SCM smob)
1079 Grob *me = Grob::unsmob (smob);
1081 Real pad = robust_scm2double (me->get_property ("skyline-vertical-padding"), 0.0);
1082 SCM out = Stencil::skylines_from_stencil (me->get_property ("stencil"), pad, Y_AXIS);
1088 Grob::internal_skylines_from_element_stencils (Grob *me, Axis a, bool pure, int beg, int end)
1091 extract_grob_set (me, "elements", elts);
1094 Grob *x_common = common_refpoint_of_array (elts, me, X_AXIS);
1095 Grob *y_common = common_refpoint_of_array (elts, me, Y_AXIS);
1096 for (vsize i = 0; i < elts.size (); i++)
1098 x_pos.push_back (elts[i]->relative_coordinate (x_common, X_AXIS));
1099 y_pos.push_back (elts[i]->maybe_pure_coordinate (y_common, Y_AXIS, pure, beg, end));
1101 Real my_x = me->relative_coordinate (x_common, X_AXIS);
1102 Real my_y = me->maybe_pure_coordinate (y_common, Y_AXIS, pure, beg, end);
1105 for (vsize i = 0; i < elts.size (); i++)
1107 Skyline_pair *skyp = Skyline_pair::unsmob (elts[i]->get_maybe_pure_property (a == X_AXIS ? "vertical-skylines" : "horizontal-skylines", pure, beg, end));
1111 Here, copying is essential. Otherwise, the skyline pair will
1115 It took Mike about 6 months of his life to add the `else' clause
1116 below. For horizontal skylines, the raise and shift calls need
1117 to be reversed. This is what was causing the problems in the
1118 shifting with all of the tests. RIP 6 months!
1120 Skyline_pair copy = Skyline_pair (*skyp);
1123 copy.shift (x_pos[i] - my_x);
1124 copy.raise (y_pos[i] - my_y);
1128 copy.raise (x_pos[i] - my_x);
1129 copy.shift (y_pos[i] - my_y);
1134 return res.smobbed_copy ();
1137 MAKE_SCHEME_CALLBACK (Grob, vertical_skylines_from_element_stencils, 1);
1139 Grob::vertical_skylines_from_element_stencils (SCM smob)
1141 Grob *me = Grob::unsmob (smob);
1142 return internal_skylines_from_element_stencils (me, X_AXIS, false, 0, INT_MAX);
1145 MAKE_SCHEME_CALLBACK (Grob, horizontal_skylines_from_element_stencils, 1);
1147 Grob::horizontal_skylines_from_element_stencils (SCM smob)
1149 Grob *me = Grob::unsmob (smob);
1150 return internal_skylines_from_element_stencils (me, Y_AXIS, false, 0, INT_MAX);
1153 MAKE_SCHEME_CALLBACK (Grob, pure_vertical_skylines_from_element_stencils, 3);
1155 Grob::pure_vertical_skylines_from_element_stencils (SCM smob, SCM beg_scm, SCM end_scm)
1157 Grob *me = Grob::unsmob (smob);
1158 int beg = robust_scm2int (beg_scm, 0);
1159 int end = robust_scm2int (end_scm, 0);
1160 return internal_skylines_from_element_stencils (me, X_AXIS, true, beg, end);
1163 MAKE_SCHEME_CALLBACK (Grob, pure_horizontal_skylines_from_element_stencils, 3);
1165 Grob::pure_horizontal_skylines_from_element_stencils (SCM smob, SCM beg_scm, SCM end_scm)
1167 Grob *me = Grob::unsmob (smob);
1168 int beg = robust_scm2int (beg_scm, 0);
1169 int end = robust_scm2int (end_scm, 0);
1170 return internal_skylines_from_element_stencils (me, Y_AXIS, true, beg, end);