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);
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 = unsmob<Paper_column> (p);
271 string r = ::to_string (Paper_column::get_rank (me));
273 Moment *mom = unsmob<Moment> (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 = *unsmob<Stencil> (scm_mol);
286 t.add_at_edge (Y_AXIS, DOWN, *unsmob<Stencil> (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 = unsmob<Spring> (scm_caar (s));
305 if (!unsmob<Grob> (scm_cdar (s))
306 || !unsmob<Grob> (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 = unsmob<Stencil> (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 = unsmob<Grob> (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 = unsmob<Stencil> (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 ();
394 static bool grob_is_live (const Grob *g)
396 return g && g->is_live ();
400 This is all too hairy. We use bounded-by-me to make sure that some
401 columns are kept "alive". Unfortunately, when spanners are suicided,
402 this falls apart again, because suicided spanners are still in
405 THIS IS BROKEN KLUDGE. WE SHOULD INVENT SOMETHING BETTER.
407 MAKE_SCHEME_CALLBACK (Paper_column, before_line_breaking, 1);
409 Paper_column::before_line_breaking (SCM grob)
411 Grob *me = unsmob<Grob> (grob);
413 if (Grob_array *ga = unsmob<Grob_array> (me->get_object ("bounded-by-me")))
414 ga->filter (grob_is_live);
416 return SCM_UNSPECIFIED;
419 /* FIXME: This is a hack that we use to identify columns that used to
420 contain note-heads but whose note-heads were moved by one of the ligature
421 engravers. Once the ligature engravers are fixed to behave nicely, this
422 function can be removed.
425 Paper_column::is_extraneous_column_from_ligature (Grob *me)
427 if (!is_musical (me))
430 // If all the note-heads that I think are my children actually belong
431 // to another column, then I am extraneous.
432 extract_grob_set (me, "elements", elts);
433 bool has_notehead = false;
434 for (vsize i = 0; i < elts.size (); i++)
436 if (has_interface<Rhythmic_head> (elts[i]))
439 if (dynamic_cast<Item *> (elts[i])->get_column () == me)
446 ADD_INTERFACE (Paper_column,
447 "@code{Paper_column} objects form the top-most X@tie{}parents"
448 " for items. There are two types of columns: musical and"
449 " non-musical, to which musical and non-musical objects are"
450 " attached respectively. The spacing engine determines the"
451 " X@tie{}positions of these objects.\n"
453 "They are numbered, the first (leftmost) is column@tie{}0."
454 " Numbering happens before line breaking, and columns are not"
455 " renumbered after line breaking. Since many columns go"
456 " unused, you should only use the rank field to get ordering"
457 " information. Two adjacent columns may have non-adjacent"
463 "full-measure-extra-space "
466 "line-break-system-details "
467 "line-break-penalty "
468 "line-break-permission "
470 "page-break-penalty "
471 "page-break-permission "
473 "page-turn-permission "
475 "shortest-playing-duration "
476 "shortest-starter-duration "