2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 1997--2015 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_DOCUMENTED_SCHEME_CALLBACK (Paper_column, print, 1,
260 "Optional stencil for @code{PaperColumn} or"
261 "@code{NonMusicalPaperColumn}.\n"
262 "Draws the @code{rank number} of each column,"
263 " its moment in time, a blue arrow showing the"
264 " ideal distance, and a red arrow showing the"
265 " minimum distance between columns.");
267 Paper_column::print (SCM p)
269 Paper_column *me = dynamic_cast<Paper_column *> (Grob::unsmob (p));
271 string r = ::to_string (Paper_column::get_rank (me));
273 Moment *mom = Moment::unsmob (me->get_property ("when"));
274 string when = mom ? mom->to_string () : "?/?";
276 Font_metric *musfont = Font_interface::get_default_font (me);
277 SCM properties = Font_interface::text_font_alist_chain (me);
278 SCM scm_mol = Text_interface::interpret_markup (me->layout ()->self_scm (),
281 SCM when_mol = Text_interface::interpret_markup (me->layout ()->self_scm (),
283 ly_string2scm (when));
284 Stencil t = *Stencil::unsmob (scm_mol);
286 t.add_at_edge (Y_AXIS, DOWN, *Stencil::unsmob (when_mol), 0.1);
287 t.align_to (X_AXIS, LEFT);
288 // compensate for font serifs and half letter-distance
289 t.translate (Offset (-0.1, 0));
290 t.align_to (Y_AXIS, DOWN);
292 Stencil l = Lookup::filled_box (Box (Interval (0, 0.02),
295 Real small_pad = 0.15;
298 // number of printed arrows from *both* loops
301 for (SCM s = me->get_object ("ideal-distances");
302 scm_is_pair (s); s = scm_cdr (s))
304 Spring *sp = Spring::unsmob (scm_caar (s));
305 if (!Grob::is_smob (scm_cdar (s))
306 || !Grob::unsmob (scm_cdar (s))->get_system ())
311 Stencil arrowhead (musfont->find_by_name ("arrowheads.open.01"));
312 // initial scaling; it will also scale with font-size.
313 arrowhead.scale (1, 1.66);
314 Real head_len = arrowhead.extent (X_AXIS).length ();
316 SCM stil = Text_interface::interpret_markup (me->layout ()->self_scm (),
318 ly_string2scm (String_convert::form_string ("%5.2lf", sp->distance ())));
319 Stencil *number_stc = Stencil::unsmob (stil);
320 number_stc->scale (1, 1.1);
321 Real num_height = number_stc->extent (Y_AXIS).length ();
322 Real num_len = number_stc->extent (X_AXIS).length ();
323 number_stc->align_to (Y_AXIS, DOWN);
325 // arrow's y-coord relative to the top of l stencil:
327 y -= j * (num_height + small_pad + big_pad);
328 // horizontally center number on the arrow, excluding arrowhead.
329 Offset num_off = Offset ((sp->distance () - num_len - head_len) / 2,
333 pts.push_back (Offset (0, y));
335 Offset p2 (sp->distance (), y);
338 Stencil id_stencil = Lookup::points_to_line_stencil (0.1, pts);
339 id_stencil.add_stencil (arrowhead.translated (p2));
340 id_stencil.add_stencil (number_stc->translated (num_off));
341 // use a lighter shade of blue so it will remain legible on black background.
342 id_stencil = id_stencil.in_color (0.2, 0.4, 1);
343 l.add_stencil (id_stencil);
346 for (SCM s = me->get_object ("minimum-distances");
347 scm_is_pair (s); s = scm_cdr (s))
349 Real dist = scm_to_double (scm_cdar (s));
350 Grob *other = Grob::unsmob (scm_caar (s));
351 if (!other || other->get_system () != me->get_system ())
356 Stencil arrowhead (musfont->find_by_name ("arrowheads.open.01"));
357 // initial scaling; it will also scale with font-size.
358 arrowhead.scale (1, 1.66);
359 Real head_len = arrowhead.extent (X_AXIS).length ();
361 SCM stil = Text_interface::interpret_markup (me->layout ()->self_scm (),
363 ly_string2scm (String_convert::form_string ("%5.2lf", dist)));
364 Stencil *number_stc = Stencil::unsmob (stil);
365 number_stc->scale (1, 1.1);
366 Real num_height = number_stc->extent (Y_AXIS).length ();
367 Real num_len = number_stc->extent (X_AXIS).length ();
368 number_stc->align_to (Y_AXIS, UP);
370 // arrow's y-coord relative to the top of l stencil:
372 y -= j * (num_height + small_pad + big_pad);
373 // horizontally center number on the arrow, excluding arrowhead.
374 Offset num_off = Offset ((dist - num_len - head_len) / 2,
378 pts.push_back (Offset (0, y));
383 Stencil id_stencil = Lookup::points_to_line_stencil (0.1, pts);
384 id_stencil.add_stencil (arrowhead.translated (p2));
385 id_stencil.add_stencil (number_stc->translated (num_off));
386 // use a lighter shade of red so it will remain legible on black background.
387 id_stencil = id_stencil.in_color (1, 0.25, 0.25);
388 l.add_stencil (id_stencil);
391 return t.smobbed_copy ();
395 This is all too hairy. We use bounded-by-me to make sure that some
396 columns are kept "alive". Unfortunately, when spanners are suicided,
397 this falls apart again, because suicided spanners are still in
400 THIS IS BROKEN KLUDGE. WE SHOULD INVENT SOMETHING BETTER.
402 MAKE_SCHEME_CALLBACK (Paper_column, before_line_breaking, 1);
404 Paper_column::before_line_breaking (SCM grob)
406 Grob *me = Grob::unsmob (grob);
408 SCM bbm = me->get_object ("bounded-by-me");
409 Grob_array *ga = Grob_array::unsmob (bbm);
411 return SCM_UNSPECIFIED;
413 vector<Grob *> &array (ga->array_reference ());
415 for (vsize i = array.size (); i--;)
419 if (!g || !g->is_live ())
420 /* UGH . potentially quadratic. */
421 array.erase (array.begin () + i);
424 return SCM_UNSPECIFIED;
427 /* FIXME: This is a hack that we use to identify columns that used to
428 contain note-heads but whose note-heads were moved by one of the ligature
429 engravers. Once the ligature engravers are fixed to behave nicely, this
430 function can be removed.
433 Paper_column::is_extraneous_column_from_ligature (Grob *me)
435 if (!is_musical (me))
438 // If all the note-heads that I think are my children actually belong
439 // to another column, then I am extraneous.
440 extract_grob_set (me, "elements", elts);
441 bool has_notehead = false;
442 for (vsize i = 0; i < elts.size (); i++)
444 if (Rhythmic_head::has_interface (elts[i]))
447 if (dynamic_cast<Item *> (elts[i])->get_column () == me)
454 ADD_INTERFACE (Paper_column,
455 "@code{Paper_column} objects form the top-most X@tie{}parents"
456 " for items. There are two types of columns: musical and"
457 " non-musical, to which musical and non-musical objects are"
458 " attached respectively. The spacing engine determines the"
459 " X@tie{}positions of these objects.\n"
461 "They are numbered, the first (leftmost) is column@tie{}0."
462 " Numbering happens before line breaking, and columns are not"
463 " renumbered after line breaking. Since many columns go"
464 " unused, you should only use the rank field to get ordering"
465 " information. Two adjacent columns may have non-adjacent"
471 "full-measure-extra-space "
474 "line-break-system-details "
475 "line-break-penalty "
476 "line-break-permission "
478 "page-break-penalty "
479 "page-break-permission "
481 "page-turn-permission "
483 "shortest-playing-duration "
484 "shortest-starter-duration "