2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 1996--2011 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 "libc-extension.hh"
22 #include "paper-column.hh"
23 #include "paper-score.hh"
24 #include "pointer-group-interface.hh"
30 Spanner::clone () const
32 return new Spanner (*this);
36 Spanner::do_break_processing ()
39 Item *left = spanned_drul_[LEFT];
40 Item *right = spanned_drul_[RIGHT];
46 if (get_system () || is_broken ())
52 If we have a spanner spanning one column, we must break it
53 anyway because it might provide a parent for another item. */
57 Item *bound = left->find_prebroken_piece (d);
59 programming_error ("no broken bound");
60 else if (bound->get_system ())
62 Spanner *span = dynamic_cast<Spanner *> (clone ());
63 span->set_bound (LEFT, bound);
64 span->set_bound (RIGHT, bound);
66 assert (span->get_system ());
67 span->get_system ()->typeset_grob (span);
68 broken_intos_.push_back (span);
71 while ((flip (&d)) != LEFT);
75 System *root = get_root_system (this);
76 vector<Item*> break_points = root->broken_col_range (left, right);
78 break_points.insert (break_points.begin () + 0, left);
79 break_points.push_back (right);
81 Slice parent_rank_slice;
82 parent_rank_slice.set_full ();
85 Check if our parent in X-direction spans equally wide
88 for (int a = X_AXIS; a < NO_AXES; a++)
90 if (Spanner *parent = dynamic_cast<Spanner *> (get_parent ((Axis)a)))
91 parent_rank_slice.intersect (parent->spanned_rank_interval ());
94 for (vsize i = 1; i < break_points.size (); i++)
96 Drul_array<Item *> bounds;
97 bounds[LEFT] = break_points[i - 1];
98 bounds[RIGHT] = break_points[i];
102 if (!bounds[d]->get_system ())
103 bounds[d] = bounds[d]->find_prebroken_piece (- d);
105 while ((flip (&d)) != LEFT);
107 if (!bounds[LEFT] || ! bounds[RIGHT])
109 programming_error ("bounds of this piece aren't breakable. ");
113 bool ok = parent_rank_slice.contains (bounds[LEFT]->get_column ()->get_rank ());
114 ok = ok && parent_rank_slice.contains (bounds[RIGHT]->get_column ()->get_rank ());
118 programming_error (to_string ("Spanner `%s' is not fully contained in parent spanner. Ignoring orphaned part",
124 Spanner *span = dynamic_cast<Spanner *> (clone ());
125 span->set_bound (LEFT, bounds[LEFT]);
126 span->set_bound (RIGHT, bounds[RIGHT]);
128 if (!bounds[LEFT]->get_system ()
129 || !bounds[RIGHT]->get_system ()
130 || bounds[LEFT]->get_system () != bounds[RIGHT]->get_system ())
132 programming_error ("bounds of spanner are invalid");
137 bounds[LEFT]->get_system ()->typeset_grob (span);
138 broken_intos_.push_back (span);
142 vector_sort (broken_intos_, Spanner::less);
143 for (vsize i = broken_intos_.size (); i--;)
144 broken_intos_[i]->break_index_ = i;
148 Spanner::get_break_index () const
154 Spanner::set_my_columns ()
156 Direction i = (Direction) LEFT;
159 if (!spanned_drul_[i]->get_system ())
160 set_bound (i, spanned_drul_[i]->find_prebroken_piece ((Direction) -i));
162 while (flip (&i) != LEFT);
166 Spanner::spanned_rank_interval () const
168 Interval_t<int> iv (0, 0);
170 if (spanned_drul_[LEFT] && spanned_drul_[LEFT]->get_column ())
171 iv[LEFT] = spanned_drul_[LEFT]->get_column ()->get_rank ();
172 if (spanned_drul_[RIGHT] && spanned_drul_[RIGHT]->get_column ())
173 iv[RIGHT] = spanned_drul_[RIGHT]->get_column ()->get_rank ();
178 Spanner::spanned_time () const
180 return spanned_time_interval (spanned_drul_[LEFT],
181 spanned_drul_[RIGHT]);
186 Spanner::get_bound (Direction d) const
188 return spanned_drul_[d];
192 Set the items that this spanner spans. If D == LEFT, we also set the
193 X-axis parent of THIS to S.
196 Spanner::set_bound (Direction d, Grob *s)
198 Item *i = dynamic_cast<Item *> (s);
201 programming_error ("must have Item for spanner bound of " + name());
205 spanned_drul_[d] = i;
208 We check for System to prevent the column -> line_of_score
209 -> column -> line_of_score -> etc situation */
210 if (d == LEFT && !dynamic_cast<System *> (this))
211 set_parent (i, X_AXIS);
214 Signal that this column needs to be kept alive. They need to be
215 kept alive to have meaningful position and linebreaking.
217 [maybe we should try keeping all columns alive?, and perhaps
218 inherit position from their (non-)musical brother]
220 if (dynamic_cast<Paper_column *> (i))
221 Pointer_group_interface::add_grob (i, ly_symbol2scm ("bounded-by-me"), this);
224 Spanner::Spanner (SCM s)
228 spanned_drul_.set (0, 0);
229 pure_property_cache_ = SCM_UNDEFINED;
232 Spanner::Spanner (Spanner const &s)
235 spanned_drul_.set (0, 0);
236 pure_property_cache_ = SCM_UNDEFINED;
240 Spanner::spanner_length () const
244 Drul_array<SCM> bounds (get_property ("left-bound-info"),
245 get_property ("right-bound-info"));
249 lr[d] = robust_scm2double (ly_assoc_get (ly_symbol2scm ("X"),
250 bounds[d], SCM_BOOL_F), -d);
251 while (flip (&d) != LEFT);
256 lr[d] = spanned_drul_[d]->relative_coordinate (0, X_AXIS);
257 while (flip (&d) != LEFT);
261 programming_error ("spanner with negative length");
267 Spanner::get_system () const
269 if (!spanned_drul_[LEFT] || !spanned_drul_[RIGHT])
271 if (spanned_drul_[LEFT]->get_system () != spanned_drul_[RIGHT]->get_system ())
273 return spanned_drul_[LEFT]->get_system ();
277 Spanner::find_broken_piece (System *l) const
279 vsize idx = binary_search (broken_intos_, (Spanner *) l, Spanner::less);
281 return broken_intos_ [idx];
286 Spanner::broken_neighbor (Direction d) const
291 vsize k = get_break_index ();
292 Spanner *orig = dynamic_cast<Spanner*> (original_);
294 if (j < 0 || vsize (j) >= orig->broken_intos_.size ())
297 return orig->broken_intos_[j];
301 Spanner::compare (Spanner *const &p1, Spanner *const &p2)
303 return p1->get_system ()->get_rank () - p2->get_system ()->get_rank ();
307 Spanner::less (Spanner *const &a, Spanner *const &b)
309 return a->get_system ()->get_rank () < b->get_system ()->get_rank ();
313 Spanner::is_broken () const
315 return broken_intos_.size ();
319 If this is a broken spanner, return the amount the left end is to be
320 shifted horizontally so that the spanner starts after the initial
321 clef and key on the staves. This is necessary for ties, slurs,
322 crescendo and decrescendo signs, for example.
325 Spanner::get_broken_left_end_align () const
327 Paper_column *sc = dynamic_cast<Paper_column *> (spanned_drul_[LEFT]->get_column ());
329 // Relevant only if left span point is first column in line
331 && sc->break_status_dir () == RIGHT)
334 We used to do a full search for the Break_align_item.
335 But that doesn't make a difference, since the Paper_column
336 is likely to contain only a Break_align_item.
338 return sc->extent (sc, X_AXIS)[RIGHT];
345 Spanner::derived_mark () const
347 scm_gc_mark (pure_property_cache_);
351 if (spanned_drul_[d])
352 scm_gc_mark (spanned_drul_[d]->self_scm ());
353 while (flip (&d) != LEFT)
356 for (vsize i = broken_intos_.size (); i--;)
357 scm_gc_mark (broken_intos_[i]->self_scm ());
361 Set left or right bound to IT.
363 Warning: caller should ensure that subsequent calls put in ITems
364 that are left-to-right ordered.
367 add_bound_item (Spanner *sp, Grob *it)
369 if (!sp->get_bound (LEFT))
370 sp->set_bound (LEFT, it);
372 sp->set_bound (RIGHT, it);
375 MAKE_SCHEME_CALLBACK (Spanner, set_spacing_rods, 1);
377 Spanner::set_spacing_rods (SCM smob)
379 Grob *me = unsmob_grob (smob);
380 SCM num_length = me->get_property ("minimum-length");
381 if (scm_is_number (num_length))
384 Spanner *sp = dynamic_cast<Spanner *> (me);
385 System *root = get_root_system (me);
386 Drul_array<Item*> bounds (sp->get_bound (LEFT),
387 sp->get_bound (RIGHT));
388 if (!bounds[LEFT] || !bounds[RIGHT])
389 return SCM_UNSPECIFIED;
391 vector<Item*> cols (root->broken_col_range (bounds[LEFT]->get_column (),
392 bounds[RIGHT]->get_column ()));
397 r.item_drul_[LEFT] = sp->get_bound (LEFT);
398 r.item_drul_[RIGHT] = cols[0]->find_prebroken_piece (LEFT);
399 r.distance_ = robust_scm2double (num_length, 0);
402 r.item_drul_[LEFT] = cols.back ()->find_prebroken_piece (RIGHT);
403 r.item_drul_[RIGHT] = sp->get_bound (RIGHT);
407 r.distance_ = robust_scm2double (num_length, 0);
408 r.item_drul_[LEFT] = sp->get_bound (LEFT);
409 r.item_drul_[RIGHT] = sp->get_bound (RIGHT);
413 return SCM_UNSPECIFIED;
416 MAKE_SCHEME_CALLBACK (Spanner, calc_normalized_endpoints, 1);
418 Spanner::calc_normalized_endpoints (SCM smob)
420 Spanner *me = unsmob_spanner (smob);
421 SCM result = SCM_EOL;
423 Spanner *orig = dynamic_cast<Spanner *> (me->original ());
425 orig = orig ? orig : me;
427 if (orig->is_broken ())
429 Real total_width = 0.0;
430 vector<Real> span_data;
432 if (!orig->is_broken ())
433 span_data.push_back (orig->spanner_length ());
435 for (vsize i = 0; i < orig->broken_intos_.size (); i++)
436 span_data.push_back (orig->broken_intos_[i]->spanner_length ());
438 vector<Interval> unnormalized_endpoints;
440 for (vsize i = 0; i < span_data.size (); i++)
442 unnormalized_endpoints.push_back (Interval (total_width, total_width + span_data[i]));
443 total_width += span_data[i];
446 for (vsize i = 0; i < unnormalized_endpoints.size (); i++)
448 SCM t = ly_interval2scm (1 / total_width * unnormalized_endpoints[i]);
449 orig->broken_intos_[i]->set_property ("normalized-endpoints", t);
450 if (me->get_break_index () == i)
456 result = scm_cons (scm_from_double (0.0), scm_from_double (1.0));
457 orig->set_property ("normalized-endpoints", result);
464 unsmob_spanner (SCM s)
466 return dynamic_cast<Spanner *> (unsmob_grob (s));
469 MAKE_SCHEME_CALLBACK (Spanner, bounds_width, 1);
471 Spanner::bounds_width (SCM grob)
473 Spanner *me = unsmob_spanner (grob);
475 Grob *common = me->get_bound (LEFT)->common_refpoint (me->get_bound (RIGHT), X_AXIS);
477 Interval w (me->get_bound (LEFT)->relative_coordinate (common, X_AXIS),
478 me->get_bound (RIGHT)->relative_coordinate (common, X_AXIS));
480 w -= me->relative_coordinate (common, X_AXIS);
482 return ly_interval2scm (w);
485 MAKE_SCHEME_CALLBACK (Spanner, kill_zero_spanned_time, 1);
487 Spanner::kill_zero_spanned_time (SCM grob)
489 Spanner *me = unsmob_spanner (grob);
491 Remove the line or hairpin at the start of the line. For
492 piano voice indicators, it makes no sense to have them at
493 the start of the line.
495 I'm not sure what the official rules for glissandi are, but
496 usually the 2nd note of the glissando is "exact", so when playing
497 from the start of the line, there is no need to glide.
499 From a typographical p.o.v. this makes sense, since the amount of
500 space left of a note at the start of a line is very small.
505 if (me->get_bound (LEFT)->break_status_dir ())
507 Interval_t<Moment> moments = me->spanned_time ();
508 moments [LEFT].grace_part_ = 0;
509 if (moments.length () == Moment (0, 0))
513 return SCM_UNSPECIFIED;
517 Spanner::get_cached_pure_property (SCM sym, int start, int end)
519 // The pure property cache is indexed by (name start . end), where name is
520 // a symbol, and start and end are numbers referring to the starting and
521 // ending column ranks of the current line.
522 if (scm_hash_table_p (pure_property_cache_) == SCM_BOOL_F)
523 return SCM_UNDEFINED;
525 SCM key = scm_cons (sym, scm_cons (scm_from_int (start), scm_from_int (end)));
526 return scm_hash_ref (pure_property_cache_, key, SCM_UNDEFINED);
530 Spanner::cache_pure_property (SCM sym, int start, int end, SCM val)
532 if (scm_hash_table_p (pure_property_cache_) == SCM_BOOL_F)
533 pure_property_cache_ = scm_c_make_hash_table (17);
535 SCM key = scm_cons (sym, scm_cons (scm_from_int (start), scm_from_int (end)));
536 scm_hash_set_x (pure_property_cache_, key, val);
539 ADD_INTERFACE (Spanner,
540 "Some objects are horizontally spanned between objects. For"
541 " example, slurs, beams, ties, etc. These grobs form a subtype"
542 " called @code{Spanner}. All spanners have two span points"
543 " (these must be @code{Item} objects), one on the left and one"
544 " on the right. The left bound is also the X@tie{}reference"
545 " point of the spanner.",
548 "normalized-endpoints "