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"
54 #include "string-convert.hh"
56 #include "skyline-pair.hh"
59 Real QUANTIZATION_UNIT = 0.2;
61 void create_path_cap (vector<Box> &boxes, vector<Drul_array<Offset> > &buildings, PangoMatrix trans, Offset pt, Real rad, Real slope, Direction d);
63 struct Transform_matrix_and_expression
68 Transform_matrix_and_expression (PangoMatrix tm, SCM expr);
71 Transform_matrix_and_expression::Transform_matrix_and_expression (PangoMatrix tm, SCM expr)
78 make_transform_matrix (Real p0, Real p1, Real p2, Real p3, Real p4, Real p5)
90 //// UTILITY FUNCTIONS
93 map x's placement between orig_l and orig_r onto
94 the interval final_l final_r
97 linear_map (Real final_l, Real final_r, Real orig_l, Real orig_r, Real x)
99 return final_l + ((final_r - final_l) * ((x - orig_l) / (orig_r - orig_l)));
103 from a nested SCM list, return the first list of numbers
107 get_number_list (SCM l)
111 if (scm_is_number (scm_car (l)))
113 SCM res = get_number_list (scm_car (l));
114 if (res == SCM_BOOL_F)
115 return get_number_list (scm_cdr (l));
122 from a nested SCM list, return the first list of numbers
126 get_path_list (SCM l)
130 if (scm_memv (scm_car (l),
131 scm_list_n (ly_symbol2scm ("moveto"),
132 ly_symbol2scm ("rmoveto"),
133 ly_symbol2scm ("lineto"),
134 ly_symbol2scm ("rlineto"),
135 ly_symbol2scm ("curveto"),
136 ly_symbol2scm ("rcurveto"),
137 ly_symbol2scm ("closepath"),
141 SCM res = get_path_list (scm_car (l));
142 if (res == SCM_BOOL_F)
143 return get_path_list (scm_cdr (l));
150 perpendicular_slope (Real s)
159 //// END UTILITY FUNCTIONS
162 below, for all of the functions make_X_boxes, the expression
163 is always unpacked into variables.
164 then, after a line of /////, there are manipulations of these variables
165 (there may be no manipulations necessary depending on the function)
166 afterwards, there is another ///// followed by the creation of points
171 make_draw_line_boxes (vector<Box> &boxes, vector<Drul_array<Offset> > &buildings, PangoMatrix trans, SCM expr, bool use_building)
173 Real thick = robust_scm2double (scm_car (expr), 0.0);
174 expr = scm_cdr (expr);
175 Real x0 = robust_scm2double (scm_car (expr), 0.0);
176 expr = scm_cdr (expr);
177 Real y0 = robust_scm2double (scm_car (expr), 0.0);
178 expr = scm_cdr (expr);
179 Real x1 = robust_scm2double (scm_car (expr), 0.0);
180 expr = scm_cdr (expr);
181 Real y1 = robust_scm2double (scm_car (expr), 0.0);
182 Real slope = x1 == x0 ? infinity_f : (y1 - y0) / (x1 - x0);
183 //////////////////////
189 Offset left (x0, y0);
190 Offset right (x1, y1);
194 Offset inter_l = get_point_in_y_direction (left, perpendicular_slope (slope), thick / 2, d);
195 Offset inter_r = get_point_in_y_direction (right, perpendicular_slope (slope), thick / 2, d);//printf ("O %4.4f %4.4f\n", inter_l[X_AXIS], inter_r[X_AXIS]);printf ("TRANNY %4.4f %4.4f %4.4f %4.4f %4.4f %4.4f\n", trans.xx, trans.xy, trans.yx, trans.yy, trans.x0, trans.y0);
196 pango_matrix_transform_point (&trans, &inter_l[X_AXIS], &inter_l[Y_AXIS]);
197 pango_matrix_transform_point (&trans, &inter_r[X_AXIS], &inter_r[Y_AXIS]);
198 if ((inter_l[X_AXIS] == inter_r[X_AXIS]) || (inter_l[Y_AXIS] == inter_r[Y_AXIS]))
200 //printf ("OO %4.4f %4.4f\n", inter_l[X_AXIS], inter_r[X_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 + 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 < 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_car (expr) == ly_symbol2scm ("moveto")
533 || (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_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_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_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_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_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_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 = unsmob_metrics (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 = unsmob_metrics (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_car (expr) == ly_symbol2scm ("draw-line"))
828 make_draw_line_boxes (boxes, buildings, trans, scm_cdr (expr), true);
829 else if (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_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_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_car (expr) == ly_symbol2scm ("partial-ellipse"))
876 make_partial_ellipse_boxes (boxes, buildings, trans, scm_cdr (expr));
877 else if (scm_car (expr) == ly_symbol2scm ("round-filled-box"))
878 make_round_filled_box_boxes (boxes, trans, scm_cdr (expr));
879 else if (scm_car (expr) == ly_symbol2scm ("named-glyph"))
880 make_named_glyph_boxes (boxes, buildings, trans, scm_cdr (expr));
881 else if (scm_car (expr) == ly_symbol2scm ("polygon"))
882 make_polygon_boxes (boxes, buildings, trans, scm_cdr (expr));
883 else if (scm_car (expr) == ly_symbol2scm ("path"))
884 make_path_boxes (boxes, buildings, trans, scm_cdr (expr));
885 else if (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 (expr == ly_string2scm (""))
911 return vector<Transform_matrix_and_expression> ();
912 else if (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_car (expr) == ly_symbol2scm ("footnote"))
923 return vector<Transform_matrix_and_expression> ();
924 else if (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_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_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_car (expr) == ly_symbol2scm ("delay-stencil-evaluation"))
949 return stencil_traverser (trans, scm_force (scm_cadr (expr)));
950 else if (scm_car (expr) == ly_symbol2scm ("grob-cause"))
951 return stencil_traverser (trans, scm_caddr (expr));
952 else if (scm_car (expr) == ly_symbol2scm ("color"))
953 return stencil_traverser (trans, scm_caddr (expr));
954 else if (scm_car (expr) == ly_symbol2scm ("id"))
955 return stencil_traverser (trans, scm_caddr (expr));
958 vector<Transform_matrix_and_expression> out;
959 out.push_back (Transform_matrix_and_expression (trans, expr));
962 warning ("Stencil expression not supported by the veritcal skylines.");
963 return vector<Transform_matrix_and_expression> ();
967 Grob::internal_simple_skylines_from_stencil (SCM smob, Axis a)
969 Grob *me = unsmob_grob (smob);
971 if (to_boolean (me->get_property ("cross-staff")))
972 return Skyline_pair ().smobbed_copy ();
974 extract_grob_set (me, "elements", elts);
976 return internal_skylines_from_element_stencils (smob, a);
978 Stencil *s = unsmob_stencil (me->get_property ("stencil"));
980 return Skyline_pair ().smobbed_copy ();
983 boxes.push_back (Box (s->extent (X_AXIS), s->extent (Y_AXIS)));
984 return Skyline_pair (boxes, a).smobbed_copy ();
987 MAKE_SCHEME_CALLBACK (Grob, simple_vertical_skylines_from_stencil, 1);
989 Grob::simple_vertical_skylines_from_stencil (SCM smob)
991 return internal_simple_skylines_from_stencil (smob, X_AXIS);
994 MAKE_SCHEME_CALLBACK (Grob, simple_horizontal_skylines_from_stencil, 1);
996 Grob::simple_horizontal_skylines_from_stencil (SCM smob)
998 return internal_simple_skylines_from_stencil (smob, Y_AXIS);
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);
1027 return out.smobbed_copy ();
1030 MAKE_SCHEME_CALLBACK (Grob, vertical_skylines_from_stencil, 1);
1032 Grob::vertical_skylines_from_stencil (SCM smob)
1034 Grob *me = unsmob_grob (smob);
1036 Real pad = robust_scm2double (me->get_property ("skyline-horizontal-padding"), 0.0);
1037 SCM out = Stencil::skylines_from_stencil (me->get_property ("stencil"), pad, X_AXIS);
1042 MAKE_SCHEME_CALLBACK (Grob, horizontal_skylines_from_stencil, 1);
1044 Grob::horizontal_skylines_from_stencil (SCM smob)
1046 Grob *me = unsmob_grob (smob);
1048 Real pad = robust_scm2double (me->get_property ("skyline-vertical-padding"), 0.0);
1049 SCM out = Stencil::skylines_from_stencil (me->get_property ("stencil"), pad, Y_AXIS);
1055 Grob::internal_skylines_from_element_stencils (SCM smob, Axis a)
1057 Grob *me = unsmob_grob (smob);
1059 extract_grob_set (me, "elements", elts);
1062 Grob *x_common = common_refpoint_of_array (elts, me, X_AXIS);
1063 Grob *y_common = common_refpoint_of_array (elts, me, Y_AXIS);
1064 for (vsize i = 0; i < elts.size (); i++)
1066 x_pos.push_back (elts[i]->relative_coordinate (x_common, X_AXIS));
1067 y_pos.push_back (elts[i]->relative_coordinate (y_common, Y_AXIS));
1069 Real my_x = me->relative_coordinate (x_common, X_AXIS);
1070 Real my_y = me->relative_coordinate (y_common, Y_AXIS);
1072 for (vsize i = 0; i < elts.size (); i++)
1074 Skyline_pair *skyp = Skyline_pair::unsmob (elts[i]->get_property (a == X_AXIS ? "vertical-skylines" : "horizontal-skylines"));
1078 Here, copying is essential. Otherwise, the skyline pair will
1082 It took Mike about 6 months of his life to add the `else' clause
1083 below. For horizontal skylines, the raise and shift calls need
1084 to be reversed. This is what was causing the problems in the
1085 shifting with all of the tests. RIP 6 months!
1087 Skyline_pair copy = Skyline_pair (*skyp);
1090 copy.shift (x_pos[i] - my_x);
1091 copy.raise (y_pos[i] - my_y);
1095 copy.raise (x_pos[i] - my_x);
1096 copy.shift (y_pos[i] - my_y);
1101 return res.smobbed_copy ();
1104 MAKE_SCHEME_CALLBACK (Grob, vertical_skylines_from_element_stencils, 1);
1106 Grob::vertical_skylines_from_element_stencils (SCM smob)
1108 return internal_skylines_from_element_stencils (smob, X_AXIS);
1111 MAKE_SCHEME_CALLBACK (Grob, horizontal_skylines_from_element_stencils, 1);
1113 Grob::horizontal_skylines_from_element_stencils (SCM smob)
1115 return internal_skylines_from_element_stencils (smob, Y_AXIS);