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"
29 #include "output-def.hh"
30 #include "paper-score.hh"
31 #include "pointer-group-interface.hh"
32 #include "rhythmic-head.hh"
33 #include "separation-item.hh"
34 #include "skyline-pair.hh"
35 #include "spaceable-grob.hh"
37 #include "string-convert.hh"
39 #include "text-interface.hh"
43 Paper_column::clone () const
45 return new Paper_column (*this);
49 Paper_column::do_break_processing ()
51 Item::do_break_processing ();
55 Paper_column::get_rank (Grob const *me)
57 return dynamic_cast<Paper_column const *> (me)->rank_;
61 Paper_column::set_rank (int rank)
67 Paper_column::get_system () const
73 Paper_column::set_system (System *s)
79 Paper_column::get_column () const
81 return (Paper_column *) (this);
84 Paper_column::Paper_column (SCM l)
91 Paper_column::Paper_column (Paper_column const &src)
99 Paper_column::compare (Grob *const &a,
102 return sign (dynamic_cast<Paper_column *> (a)->rank_
103 - dynamic_cast<Paper_column *> (b)->rank_);
107 Paper_column::less_than (Grob *const &a,
110 Paper_column *pa = dynamic_cast<Paper_column *> (a);
111 Paper_column *pb = dynamic_cast<Paper_column *> (b);
113 return pa->rank_ < pb->rank_;
117 Paper_column::when_mom (Grob *me)
119 SCM m = me->get_property ("when");
120 if (Moment *when = unsmob<Moment> (m))
126 Paper_column::is_musical (Grob *me)
128 SCM m = me->get_property ("shortest-starter-duration");
130 if (unsmob<Moment> (m))
131 s = *unsmob<Moment> (m);
132 return s != Moment (0);
136 Paper_column::is_used (Grob *me)
138 extract_grob_set (me, "elements", elts);
142 extract_grob_set (me, "bounded-by-me", bbm);
146 if (Paper_column::is_breakable (me))
149 if (to_boolean (me->get_property ("used")))
152 if (scm_is_pair (me->get_property ("labels")))
159 Paper_column::is_breakable (Grob *me)
161 return scm_is_symbol (me->get_property ("line-break-permission"));
165 Paper_column::minimum_distance (Grob *left, Grob *right)
167 Drul_array<Grob *> cols (left, right);
168 Drul_array<Skyline> skys = Drul_array<Skyline> (Skyline (RIGHT), Skyline (LEFT));
170 for (LEFT_and_RIGHT (d))
172 Skyline_pair *sp = unsmob<Skyline_pair> (cols[d]->get_property ("horizontal-skylines"));
177 skys[RIGHT].merge (Separation_item::conditional_skyline (right, left));
179 return max (0.0, skys[LEFT].distance (skys[RIGHT]));
183 Paper_column::break_align_width (Grob *me, SCM align_syms)
185 Grob *p = me->get_parent (X_AXIS);
187 if (scm_is_symbol (align_syms))
188 align_syms = scm_list_1 (align_syms);
192 me->programming_error ("tried to get break-align-width of a musical column");
193 return Interval (0, 0) + me->relative_coordinate (p, X_AXIS);
197 for (;!align && scm_is_pair (align_syms); align_syms = scm_cdr (align_syms))
199 SCM align_sym = scm_car (align_syms);
200 if (scm_is_eq (align_sym, ly_symbol2scm ("staff-bar"))
201 || scm_is_eq (align_sym, ly_symbol2scm ("break-alignment")))
202 align = Pointer_group_interface::find_grob
203 (me, ly_symbol2scm ("elements"),
204 (scm_is_eq (align_sym, ly_symbol2scm ("staff-bar"))
205 ? Bar_line::non_empty_barline
206 : has_interface<Break_alignment_interface>));
209 extract_grob_set (me, "elements", elts);
210 for (vsize i = 0; i < elts.size (); i++)
212 if (scm_is_eq (align_sym, elts[i]->get_property ("break-align-symbol"))
213 // TODO SCM: there must be a simpler way to put this.
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);
229 LY_DEFINE (ly_paper_column__break_align_width, "ly:paper-column::break-align-width",
230 2, 0, 0, (SCM col, SCM align_syms),
231 "Determine the extent along the X-axis of a grob used for"
232 " break-alignment organized by column @var{col}. The grob is"
233 " specified by @var{align-syms}, which contains either a"
234 " single @code{break-align-symbol} or a list of such"
237 LY_ASSERT_SMOB (Grob, col, 1);
238 SCM_ASSERT_TYPE (scm_is_symbol (align_syms) || ly_is_list (align_syms),
239 align_syms, SCM_ARG2, __FUNCTION__, "symbol or list");
241 Interval ext = Paper_column::break_align_width (unsmob<Grob> (col), align_syms);
242 return ly_interval2scm (ext);
246 Loop through elements of a PaperColumn, find all grobs implementing specified
247 interface and return their combined extent.
250 Paper_column::get_interface_extent (Grob *column, SCM iface, Axis a)
252 Interval extent = Interval (0, 0);
253 extract_grob_set (column, "elements", elts);
255 for (vsize i = 0; i < elts.size (); i++)
256 if (elts[i]->internal_has_interface (iface))
257 extent.unite (robust_relative_extent (elts[i], elts[i], a));
267 - blue arrow representing ideal distance,
268 - red arrow representing minimum distance
269 to aid debugging. To turn this on, simply add
270 \override Score.PaperColumn #'stencil = #ly:paper-column::print
271 \override Score.NonMusicalPaperColumn #'stencil = #ly:paper-column::print
273 Also, as of 2013-10-16 there's a switch in Frescobaldi that turns this on.
275 MAKE_DOCUMENTED_SCHEME_CALLBACK (Paper_column, print, 1,
276 "Optional stencil for @code{PaperColumn} or"
277 "@code{NonMusicalPaperColumn}.\n"
278 "Draws the @code{rank number} of each column,"
279 " its moment in time, a blue arrow showing the"
280 " ideal distance, and a red arrow showing the"
281 " minimum distance between columns.");
283 Paper_column::print (SCM p)
285 Paper_column *me = unsmob<Paper_column> (p);
287 string r = ::to_string (Paper_column::get_rank (me));
289 Moment *mom = unsmob<Moment> (me->get_property ("when"));
290 string when = mom ? mom->to_string () : "?/?";
292 Font_metric *musfont = Font_interface::get_default_font (me);
293 SCM properties = Font_interface::text_font_alist_chain (me);
294 SCM scm_mol = Text_interface::interpret_markup (me->layout ()->self_scm (),
297 SCM when_mol = Text_interface::interpret_markup (me->layout ()->self_scm (),
299 ly_string2scm (when));
300 Stencil t = *unsmob<Stencil> (scm_mol);
302 t.add_at_edge (Y_AXIS, DOWN, *unsmob<Stencil> (when_mol), 0.1);
303 t.align_to (X_AXIS, LEFT);
304 // compensate for font serifs and half letter-distance
305 t.translate (Offset (-0.1, 0));
306 t.align_to (Y_AXIS, DOWN);
308 Stencil l = Lookup::filled_box (Box (Interval (0, 0.02),
311 Real small_pad = 0.15;
314 // number of printed arrows from *both* loops
317 for (SCM s = me->get_object ("ideal-distances");
318 scm_is_pair (s); s = scm_cdr (s))
320 Spring *sp = unsmob<Spring> (scm_caar (s));
321 if (!unsmob<Grob> (scm_cdar (s))
322 || !unsmob<Grob> (scm_cdar (s))->get_system ())
327 Stencil arrowhead (musfont->find_by_name ("arrowheads.open.01"));
328 // initial scaling; it will also scale with font-size.
329 arrowhead.scale (1, 1.66);
330 Real head_len = arrowhead.extent (X_AXIS).length ();
332 SCM stil = Text_interface::interpret_markup (me->layout ()->self_scm (),
334 ly_string2scm (String_convert::form_string ("%5.2lf", sp->distance ())));
335 Stencil *number_stc = unsmob<Stencil> (stil);
336 number_stc->scale (1, 1.1);
337 Real num_height = number_stc->extent (Y_AXIS).length ();
338 Real num_len = number_stc->extent (X_AXIS).length ();
339 number_stc->align_to (Y_AXIS, DOWN);
341 // arrow's y-coord relative to the top of l stencil:
343 y -= j * (num_height + small_pad + big_pad);
344 // horizontally center number on the arrow, excluding arrowhead.
345 Offset num_off = Offset ((sp->distance () - num_len - head_len) / 2,
349 pts.push_back (Offset (0, y));
351 Offset p2 (sp->distance (), y);
354 Stencil id_stencil = Lookup::points_to_line_stencil (0.1, pts);
355 id_stencil.add_stencil (arrowhead.translated (p2));
356 id_stencil.add_stencil (number_stc->translated (num_off));
357 // use a lighter shade of blue so it will remain legible on black background.
358 id_stencil = id_stencil.in_color (0.2, 0.4, 1);
359 l.add_stencil (id_stencil);
362 for (SCM s = me->get_object ("minimum-distances");
363 scm_is_pair (s); s = scm_cdr (s))
365 Real dist = scm_to_double (scm_cdar (s));
366 Grob *other = unsmob<Grob> (scm_caar (s));
367 if (!other || other->get_system () != me->get_system ())
372 Stencil arrowhead (musfont->find_by_name ("arrowheads.open.01"));
373 // initial scaling; it will also scale with font-size.
374 arrowhead.scale (1, 1.66);
375 Real head_len = arrowhead.extent (X_AXIS).length ();
377 SCM stil = Text_interface::interpret_markup (me->layout ()->self_scm (),
379 ly_string2scm (String_convert::form_string ("%5.2lf", dist)));
380 Stencil *number_stc = unsmob<Stencil> (stil);
381 number_stc->scale (1, 1.1);
382 Real num_height = number_stc->extent (Y_AXIS).length ();
383 Real num_len = number_stc->extent (X_AXIS).length ();
384 number_stc->align_to (Y_AXIS, UP);
386 // arrow's y-coord relative to the top of l stencil:
388 y -= j * (num_height + small_pad + big_pad);
389 // horizontally center number on the arrow, excluding arrowhead.
390 Offset num_off = Offset ((dist - num_len - head_len) / 2,
394 pts.push_back (Offset (0, y));
399 Stencil id_stencil = Lookup::points_to_line_stencil (0.1, pts);
400 id_stencil.add_stencil (arrowhead.translated (p2));
401 id_stencil.add_stencil (number_stc->translated (num_off));
402 // use a lighter shade of red so it will remain legible on black background.
403 id_stencil = id_stencil.in_color (1, 0.25, 0.25);
404 l.add_stencil (id_stencil);
407 return t.smobbed_copy ();
410 static bool grob_is_live (const Grob *g)
412 return g && g->is_live ();
416 This is all too hairy. We use bounded-by-me to make sure that some
417 columns are kept "alive". Unfortunately, when spanners are suicided,
418 this falls apart again, because suicided spanners are still in
421 THIS IS BROKEN KLUDGE. WE SHOULD INVENT SOMETHING BETTER.
423 MAKE_SCHEME_CALLBACK (Paper_column, before_line_breaking, 1);
425 Paper_column::before_line_breaking (SCM grob)
427 Grob *me = unsmob<Grob> (grob);
429 if (Grob_array *ga = unsmob<Grob_array> (me->get_object ("bounded-by-me")))
430 ga->filter (grob_is_live);
432 return SCM_UNSPECIFIED;
435 /* FIXME: This is a hack that we use to identify columns that used to
436 contain note-heads but whose note-heads were moved by one of the ligature
437 engravers. Once the ligature engravers are fixed to behave nicely, this
438 function can be removed.
441 Paper_column::is_extraneous_column_from_ligature (Grob *me)
443 if (!is_musical (me))
446 // If all the note-heads that I think are my children actually belong
447 // to another column, then I am extraneous.
448 extract_grob_set (me, "elements", elts);
449 bool has_notehead = false;
450 for (vsize i = 0; i < elts.size (); i++)
452 if (has_interface<Rhythmic_head> (elts[i]))
455 if (dynamic_cast<Item *> (elts[i])->get_column () == me)
462 ADD_INTERFACE (Paper_column,
463 "@code{Paper_column} objects form the top-most X@tie{}parents"
464 " for items. There are two types of columns: musical and"
465 " non-musical, to which musical and non-musical objects are"
466 " attached respectively. The spacing engine determines the"
467 " X@tie{}positions of these objects.\n"
469 "They are numbered, the first (leftmost) is column@tie{}0."
470 " Numbering happens before line breaking, and columns are not"
471 " renumbered after line breaking. Since many columns go"
472 " unused, you should only use the rank field to get ordering"
473 " information. Two adjacent columns may have non-adjacent"
479 "full-measure-extra-space "
482 "line-break-system-details "
483 "line-break-penalty "
484 "line-break-permission "
486 "page-break-penalty "
487 "page-break-permission "
489 "page-turn-permission "
491 "shortest-playing-duration "
492 "shortest-starter-duration "