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 (elts[i]->get_property ("break-align-symbol") == align_sym
213 && !elts[i]->extent (elts[i], X_AXIS).is_empty ())
223 return Interval (0, 0) + me->relative_coordinate (p, X_AXIS);
225 return align->extent (p, X_AXIS);
229 Loop through elements of a PaperColumn, find all grobs implementing specified
230 interface and return their combined extent.
233 Paper_column::get_interface_extent (Grob *column, SCM iface, Axis a)
235 Interval extent = Interval (0, 0);
236 extract_grob_set (column, "elements", elts);
238 for (vsize i = 0; i < elts.size (); i++)
239 if (elts[i]->internal_has_interface (iface))
240 extent.unite (robust_relative_extent (elts[i], elts[i], a));
250 - blue arrow representing ideal distance,
251 - red arrow representing minimum distance
252 to aid debugging. To turn this on, simply add
253 \override Score.PaperColumn #'stencil = #ly:paper-column::print
254 \override Score.NonMusicalPaperColumn #'stencil = #ly:paper-column::print
256 Also, as of 2013-10-16 there's a switch in Frescobaldi that turns this on.
258 MAKE_DOCUMENTED_SCHEME_CALLBACK (Paper_column, print, 1,
259 "Optional stencil for @code{PaperColumn} or"
260 "@code{NonMusicalPaperColumn}.\n"
261 "Draws the @code{rank number} of each column,"
262 " its moment in time, a blue arrow showing the"
263 " ideal distance, and a red arrow showing the"
264 " minimum distance between columns.");
266 Paper_column::print (SCM p)
268 Paper_column *me = unsmob<Paper_column> (p);
270 string r = ::to_string (Paper_column::get_rank (me));
272 Moment *mom = unsmob<Moment> (me->get_property ("when"));
273 string when = mom ? mom->to_string () : "?/?";
275 Font_metric *musfont = Font_interface::get_default_font (me);
276 SCM properties = Font_interface::text_font_alist_chain (me);
277 SCM scm_mol = Text_interface::interpret_markup (me->layout ()->self_scm (),
280 SCM when_mol = Text_interface::interpret_markup (me->layout ()->self_scm (),
282 ly_string2scm (when));
283 Stencil t = *unsmob<Stencil> (scm_mol);
285 t.add_at_edge (Y_AXIS, DOWN, *unsmob<Stencil> (when_mol), 0.1);
286 t.align_to (X_AXIS, LEFT);
287 // compensate for font serifs and half letter-distance
288 t.translate (Offset (-0.1, 0));
289 t.align_to (Y_AXIS, DOWN);
291 Stencil l = Lookup::filled_box (Box (Interval (0, 0.02),
294 Real small_pad = 0.15;
297 // number of printed arrows from *both* loops
300 for (SCM s = me->get_object ("ideal-distances");
301 scm_is_pair (s); s = scm_cdr (s))
303 Spring *sp = unsmob<Spring> (scm_caar (s));
304 if (!unsmob<Grob> (scm_cdar (s))
305 || !unsmob<Grob> (scm_cdar (s))->get_system ())
310 Stencil arrowhead (musfont->find_by_name ("arrowheads.open.01"));
311 // initial scaling; it will also scale with font-size.
312 arrowhead.scale (1, 1.66);
313 Real head_len = arrowhead.extent (X_AXIS).length ();
315 SCM stil = Text_interface::interpret_markup (me->layout ()->self_scm (),
317 ly_string2scm (String_convert::form_string ("%5.2lf", sp->distance ())));
318 Stencil *number_stc = unsmob<Stencil> (stil);
319 number_stc->scale (1, 1.1);
320 Real num_height = number_stc->extent (Y_AXIS).length ();
321 Real num_len = number_stc->extent (X_AXIS).length ();
322 number_stc->align_to (Y_AXIS, DOWN);
324 // arrow's y-coord relative to the top of l stencil:
326 y -= j * (num_height + small_pad + big_pad);
327 // horizontally center number on the arrow, excluding arrowhead.
328 Offset num_off = Offset ((sp->distance () - num_len - head_len) / 2,
332 pts.push_back (Offset (0, y));
334 Offset p2 (sp->distance (), y);
337 Stencil id_stencil = Lookup::points_to_line_stencil (0.1, pts);
338 id_stencil.add_stencil (arrowhead.translated (p2));
339 id_stencil.add_stencil (number_stc->translated (num_off));
340 // use a lighter shade of blue so it will remain legible on black background.
341 id_stencil = id_stencil.in_color (0.2, 0.4, 1);
342 l.add_stencil (id_stencil);
345 for (SCM s = me->get_object ("minimum-distances");
346 scm_is_pair (s); s = scm_cdr (s))
348 Real dist = scm_to_double (scm_cdar (s));
349 Grob *other = unsmob<Grob> (scm_caar (s));
350 if (!other || other->get_system () != me->get_system ())
355 Stencil arrowhead (musfont->find_by_name ("arrowheads.open.01"));
356 // initial scaling; it will also scale with font-size.
357 arrowhead.scale (1, 1.66);
358 Real head_len = arrowhead.extent (X_AXIS).length ();
360 SCM stil = Text_interface::interpret_markup (me->layout ()->self_scm (),
362 ly_string2scm (String_convert::form_string ("%5.2lf", dist)));
363 Stencil *number_stc = unsmob<Stencil> (stil);
364 number_stc->scale (1, 1.1);
365 Real num_height = number_stc->extent (Y_AXIS).length ();
366 Real num_len = number_stc->extent (X_AXIS).length ();
367 number_stc->align_to (Y_AXIS, UP);
369 // arrow's y-coord relative to the top of l stencil:
371 y -= j * (num_height + small_pad + big_pad);
372 // horizontally center number on the arrow, excluding arrowhead.
373 Offset num_off = Offset ((dist - num_len - head_len) / 2,
377 pts.push_back (Offset (0, y));
382 Stencil id_stencil = Lookup::points_to_line_stencil (0.1, pts);
383 id_stencil.add_stencil (arrowhead.translated (p2));
384 id_stencil.add_stencil (number_stc->translated (num_off));
385 // use a lighter shade of red so it will remain legible on black background.
386 id_stencil = id_stencil.in_color (1, 0.25, 0.25);
387 l.add_stencil (id_stencil);
390 return t.smobbed_copy ();
393 static bool grob_is_live (const Grob *g)
395 return g && g->is_live ();
399 This is all too hairy. We use bounded-by-me to make sure that some
400 columns are kept "alive". Unfortunately, when spanners are suicided,
401 this falls apart again, because suicided spanners are still in
404 THIS IS BROKEN KLUDGE. WE SHOULD INVENT SOMETHING BETTER.
406 MAKE_SCHEME_CALLBACK (Paper_column, before_line_breaking, 1);
408 Paper_column::before_line_breaking (SCM grob)
410 Grob *me = unsmob<Grob> (grob);
412 if (Grob_array *ga = unsmob<Grob_array> (me->get_object ("bounded-by-me")))
413 ga->filter (grob_is_live);
415 return SCM_UNSPECIFIED;
418 /* FIXME: This is a hack that we use to identify columns that used to
419 contain note-heads but whose note-heads were moved by one of the ligature
420 engravers. Once the ligature engravers are fixed to behave nicely, this
421 function can be removed.
424 Paper_column::is_extraneous_column_from_ligature (Grob *me)
426 if (!is_musical (me))
429 // If all the note-heads that I think are my children actually belong
430 // to another column, then I am extraneous.
431 extract_grob_set (me, "elements", elts);
432 bool has_notehead = false;
433 for (vsize i = 0; i < elts.size (); i++)
435 if (has_interface<Rhythmic_head> (elts[i]))
438 if (dynamic_cast<Item *> (elts[i])->get_column () == me)
445 ADD_INTERFACE (Paper_column,
446 "@code{Paper_column} objects form the top-most X@tie{}parents"
447 " for items. There are two types of columns: musical and"
448 " non-musical, to which musical and non-musical objects are"
449 " attached respectively. The spacing engine determines the"
450 " X@tie{}positions of these objects.\n"
452 "They are numbered, the first (leftmost) is column@tie{}0."
453 " Numbering happens before line breaking, and columns are not"
454 " renumbered after line breaking. Since many columns go"
455 " unused, you should only use the rank field to get ordering"
456 " information. Two adjacent columns may have non-adjacent"
462 "full-measure-extra-space "
465 "line-break-system-details "
466 "line-break-penalty "
467 "line-break-permission "
469 "page-break-penalty "
470 "page-break-permission "
472 "page-turn-permission "
474 "shortest-playing-duration "
475 "shortest-starter-duration "