2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 1997--2014 Han-Wen Nienhuys <hanwen@xs4all.nl>
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/>.
20 #include "paper-column.hh"
22 #include "axis-group-interface.hh"
23 #include "bar-line.hh"
24 #include "break-align-interface.hh"
25 #include "font-interface.hh"
26 #include "grob-array.hh"
30 #include "output-def.hh"
31 #include "paper-score.hh"
32 #include "pointer-group-interface.hh"
33 #include "rhythmic-head.hh"
34 #include "separation-item.hh"
35 #include "skyline-pair.hh"
36 #include "spaceable-grob.hh"
38 #include "string-convert.hh"
40 #include "text-interface.hh"
44 Paper_column::clone () const
46 return new Paper_column (*this);
50 Paper_column::do_break_processing ()
52 Item::do_break_processing ();
56 Paper_column::get_rank (Grob const *me)
58 return dynamic_cast<Paper_column const *> (me)->rank_;
62 Paper_column::set_rank (int rank)
68 Paper_column::get_system () const
74 Paper_column::set_system (System *s)
80 Paper_column::get_column () const
82 return (Paper_column *) (this);
85 Paper_column::Paper_column (SCM l)
92 Paper_column::Paper_column (Paper_column const &src)
100 Paper_column::compare (Grob *const &a,
103 return sign (dynamic_cast<Paper_column *> (a)->rank_
104 - dynamic_cast<Paper_column *> (b)->rank_);
108 Paper_column::less_than (Grob *const &a,
111 Paper_column *pa = dynamic_cast<Paper_column *> (a);
112 Paper_column *pb = dynamic_cast<Paper_column *> (b);
114 return pa->rank_ < pb->rank_;
118 Paper_column::when_mom (Grob *me)
120 SCM m = me->get_property ("when");
121 if (Moment *when = Moment::unsmob (m))
127 Paper_column::is_musical (Grob *me)
129 SCM m = me->get_property ("shortest-starter-duration");
131 if (Moment::unsmob (m))
132 s = *Moment::unsmob (m);
133 return s != Moment (0);
137 Paper_column::is_used (Grob *me)
139 extract_grob_set (me, "elements", elts);
143 extract_grob_set (me, "bounded-by-me", bbm);
147 if (Paper_column::is_breakable (me))
150 if (to_boolean (me->get_property ("used")))
153 if (scm_is_pair (me->get_property ("labels")))
160 Paper_column::is_breakable (Grob *me)
162 return scm_is_symbol (me->get_property ("line-break-permission"));
166 Paper_column::minimum_distance (Grob *left, Grob *right)
168 Drul_array<Grob *> cols (left, right);
169 Drul_array<Skyline> skys = Drul_array<Skyline> (Skyline (RIGHT), Skyline (LEFT));
171 for (LEFT_and_RIGHT (d))
173 Skyline_pair *sp = Skyline_pair::unsmob (cols[d]->get_property ("horizontal-skylines"));
178 skys[RIGHT].merge (Separation_item::conditional_skyline (right, left));
180 return max (0.0, skys[LEFT].distance (skys[RIGHT]));
184 Paper_column::break_align_width (Grob *me, SCM align_syms)
186 Grob *p = me->get_parent (X_AXIS);
188 if (scm_is_symbol (align_syms))
189 align_syms = scm_list_1 (align_syms);
193 me->programming_error ("tried to get break-align-width of a musical column");
194 return Interval (0, 0) + me->relative_coordinate (p, X_AXIS);
198 for (;!align && scm_is_pair (align_syms); align_syms = scm_cdr (align_syms))
200 SCM align_sym = scm_car (align_syms);
201 if (align_sym == ly_symbol2scm ("staff-bar")
202 || align_sym == ly_symbol2scm ("break-alignment"))
203 align = Pointer_group_interface::find_grob
204 (me, ly_symbol2scm ("elements"),
205 (align_sym == ly_symbol2scm ("staff-bar")
206 ? Bar_line::non_empty_barline
207 : Break_alignment_interface::has_interface));
210 extract_grob_set (me, "elements", elts);
211 for (vsize i = 0; i < elts.size (); i++)
213 if (elts[i]->get_property ("break-align-symbol") == align_sym
214 && !elts[i]->extent (elts[i], X_AXIS).is_empty ())
224 return Interval (0, 0) + me->relative_coordinate (p, X_AXIS);
226 return align->extent (p, X_AXIS);
230 Loop through elements of a PaperColumn, find all grobs implementing specified
231 interface and return their combined extent.
234 Paper_column::get_interface_extent (Grob *column, SCM iface, Axis a)
236 Interval extent = Interval (0, 0);
237 extract_grob_set (column, "elements", elts);
239 for (vsize i = 0; i < elts.size (); i++)
240 if (elts[i]->internal_has_interface (iface))
241 extent.unite (robust_relative_extent (elts[i], elts[i], a));
251 - blue arrow representing ideal distance,
252 - red arrow representing minimum distance
253 to aid debugging. To turn this on, simply add
254 \override Score.PaperColumn #'stencil = #ly:paper-column::print
255 \override Score.NonMusicalPaperColumn #'stencil = #ly:paper-column::print
257 Also, as of 2013-10-16 there's a switch in Frescobaldi that turns this on.
259 MAKE_SCHEME_CALLBACK (Paper_column, print, 1);
261 Paper_column::print (SCM p)
263 Paper_column *me = dynamic_cast<Paper_column *> (Grob::unsmob (p));
265 string r = ::to_string (Paper_column::get_rank (me));
267 Moment *mom = Moment::unsmob (me->get_property ("when"));
268 string when = mom ? mom->to_string () : "?/?";
270 Font_metric *musfont = Font_interface::get_default_font (me);
271 SCM properties = Font_interface::text_font_alist_chain (me);
272 SCM scm_mol = Text_interface::interpret_markup (me->layout ()->self_scm (),
275 SCM when_mol = Text_interface::interpret_markup (me->layout ()->self_scm (),
277 ly_string2scm (when));
278 Stencil t = *Stencil::unsmob (scm_mol);
280 t.add_at_edge (Y_AXIS, DOWN, *Stencil::unsmob (when_mol), 0.1);
281 t.align_to (X_AXIS, LEFT);
282 // compensate for font serifs and half letter-distance
283 t.translate (Offset (-0.1, 0));
284 t.align_to (Y_AXIS, DOWN);
286 Stencil l = Lookup::filled_box (Box (Interval (0, 0.02),
289 Real small_pad = 0.15;
292 // number of printed arrows from *both* loops
295 for (SCM s = me->get_object ("ideal-distances");
296 scm_is_pair (s); s = scm_cdr (s))
298 Spring *sp = Spring::unsmob (scm_caar (s));
299 if (!Grob::is_smob (scm_cdar (s))
300 || !Grob::unsmob (scm_cdar (s))->get_system ())
305 Stencil arrowhead (musfont->find_by_name ("arrowheads.open.01"));
306 // initial scaling; it will also scale with font-size.
307 arrowhead.scale (1, 1.66);
308 Real head_len = arrowhead.extent (X_AXIS).length ();
310 SCM stil = Text_interface::interpret_markup (me->layout ()->self_scm (),
312 ly_string2scm (String_convert::form_string ("%5.2lf", sp->distance ())));
313 Stencil *number_stc = Stencil::unsmob (stil);
314 number_stc->scale (1, 1.1);
315 Real num_height = number_stc->extent (Y_AXIS).length ();
316 Real num_len = number_stc->extent (X_AXIS).length ();
317 number_stc->align_to (Y_AXIS, DOWN);
319 // arrow's y-coord relative to the top of l stencil:
321 y -= j * (num_height + small_pad + big_pad);
322 // horizontally center number on the arrow, excluding arrowhead.
323 Offset num_off = Offset ((sp->distance () - num_len - head_len) / 2,
327 pts.push_back (Offset (0, y));
329 Offset p2 (sp->distance (), y);
332 Stencil id_stencil = Lookup::points_to_line_stencil (0.1, pts);
333 id_stencil.add_stencil (arrowhead.translated (p2));
334 id_stencil.add_stencil (number_stc->translated (num_off));
335 // use a lighter shade of blue so it will remain legible on black background.
336 id_stencil = id_stencil.in_color (0.2, 0.4, 1);
337 l.add_stencil (id_stencil);
340 for (SCM s = me->get_object ("minimum-distances");
341 scm_is_pair (s); s = scm_cdr (s))
343 Real dist = scm_to_double (scm_cdar (s));
344 Grob *other = Grob::unsmob (scm_caar (s));
345 if (!other || other->get_system () != me->get_system ())
350 Stencil arrowhead (musfont->find_by_name ("arrowheads.open.01"));
351 // initial scaling; it will also scale with font-size.
352 arrowhead.scale (1, 1.66);
353 Real head_len = arrowhead.extent (X_AXIS).length ();
355 SCM stil = Text_interface::interpret_markup (me->layout ()->self_scm (),
357 ly_string2scm (String_convert::form_string ("%5.2lf", dist)));
358 Stencil *number_stc = Stencil::unsmob (stil);
359 number_stc->scale (1, 1.1);
360 Real num_height = number_stc->extent (Y_AXIS).length ();
361 Real num_len = number_stc->extent (X_AXIS).length ();
362 number_stc->align_to (Y_AXIS, UP);
364 // arrow's y-coord relative to the top of l stencil:
366 y -= j * (num_height + small_pad + big_pad);
367 // horizontally center number on the arrow, excluding arrowhead.
368 Offset num_off = Offset ((dist - num_len - head_len) / 2,
372 pts.push_back (Offset (0, y));
377 Stencil id_stencil = Lookup::points_to_line_stencil (0.1, pts);
378 id_stencil.add_stencil (arrowhead.translated (p2));
379 id_stencil.add_stencil (number_stc->translated (num_off));
380 // use a lighter shade of red so it will remain legible on black background.
381 id_stencil = id_stencil.in_color (1, 0.25, 0.25);
382 l.add_stencil (id_stencil);
385 return t.smobbed_copy ();
389 This is all too hairy. We use bounded-by-me to make sure that some
390 columns are kept "alive". Unfortunately, when spanners are suicided,
391 this falls apart again, because suicided spanners are still in
394 THIS IS BROKEN KLUDGE. WE SHOULD INVENT SOMETHING BETTER.
396 MAKE_SCHEME_CALLBACK (Paper_column, before_line_breaking, 1);
398 Paper_column::before_line_breaking (SCM grob)
400 Grob *me = Grob::unsmob (grob);
402 SCM bbm = me->get_object ("bounded-by-me");
403 Grob_array *ga = Grob_array::unsmob (bbm);
405 return SCM_UNSPECIFIED;
407 vector<Grob *> &array (ga->array_reference ());
409 for (vsize i = array.size (); i--;)
413 if (!g || !g->is_live ())
414 /* UGH . potentially quadratic. */
415 array.erase (array.begin () + i);
418 return SCM_UNSPECIFIED;
421 /* FIXME: This is a hack that we use to identify columns that used to
422 contain note-heads but whose note-heads were moved by one of the ligature
423 engravers. Once the ligature engravers are fixed to behave nicely, this
424 function can be removed.
427 Paper_column::is_extraneous_column_from_ligature (Grob *me)
429 if (!is_musical (me))
432 // If all the note-heads that I think are my children actually belong
433 // to another column, then I am extraneous.
434 extract_grob_set (me, "elements", elts);
435 bool has_notehead = false;
436 for (vsize i = 0; i < elts.size (); i++)
438 if (Rhythmic_head::has_interface (elts[i]))
441 if (dynamic_cast<Item *> (elts[i])->get_column () == me)
448 ADD_INTERFACE (Paper_column,
449 "@code{Paper_column} objects form the top-most X@tie{}parents"
450 " for items. There are two types of columns: musical and"
451 " non-musical, to which musical and non-musical objects are"
452 " attached respectively. The spacing engine determines the"
453 " X@tie{}positions of these objects.\n"
455 "They are numbered, the first (leftmost) is column@tie{}0."
456 " Numbering happens before line breaking, and columns are not"
457 " renumbered after line breaking. Since many columns go"
458 " unused, you should only use the rank field to get ordering"
459 " information. Two adjacent columns may have non-adjacent"
465 "full-measure-extra-space "
468 "line-break-system-details "
469 "line-break-penalty "
470 "line-break-permission "
472 "page-break-penalty "
473 "page-break-permission "
475 "page-turn-permission "
477 "shortest-playing-duration "
478 "shortest-starter-duration "