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
242 Real l = spanned_drul_[LEFT]->relative_coordinate (0, X_AXIS);
243 Real r = spanned_drul_[RIGHT]->relative_coordinate (0, X_AXIS);
246 programming_error ("spanner with negative length");
252 Spanner::get_system () const
254 if (!spanned_drul_[LEFT] || !spanned_drul_[RIGHT])
256 if (spanned_drul_[LEFT]->get_system () != spanned_drul_[RIGHT]->get_system ())
258 return spanned_drul_[LEFT]->get_system ();
262 Spanner::find_broken_piece (System *l) const
264 vsize idx = binary_search (broken_intos_, (Spanner *) l, Spanner::less);
266 return broken_intos_ [idx];
271 Spanner::broken_neighbor (Direction d) const
276 vsize k = get_break_index ();
277 Spanner *orig = dynamic_cast<Spanner*> (original_);
279 if (j < 0 || vsize (j) >= orig->broken_intos_.size ())
282 return orig->broken_intos_[j];
286 Spanner::compare (Spanner *const &p1, Spanner *const &p2)
288 return p1->get_system ()->get_rank () - p2->get_system ()->get_rank ();
292 Spanner::less (Spanner *const &a, Spanner *const &b)
294 return a->get_system ()->get_rank () < b->get_system ()->get_rank ();
298 Spanner::is_broken () const
300 return broken_intos_.size ();
304 If this is a broken spanner, return the amount the left end is to be
305 shifted horizontally so that the spanner starts after the initial
306 clef and key on the staves. This is necessary for ties, slurs,
307 crescendo and decrescendo signs, for example.
310 Spanner::get_broken_left_end_align () const
312 Paper_column *sc = dynamic_cast<Paper_column *> (spanned_drul_[LEFT]->get_column ());
314 // Relevant only if left span point is first column in line
316 && sc->break_status_dir () == RIGHT)
319 We used to do a full search for the Break_align_item.
320 But that doesn't make a difference, since the Paper_column
321 is likely to contain only a Break_align_item.
323 return sc->extent (sc, X_AXIS)[RIGHT];
330 Spanner::derived_mark () const
332 scm_gc_mark (pure_property_cache_);
336 if (spanned_drul_[d])
337 scm_gc_mark (spanned_drul_[d]->self_scm ());
338 while (flip (&d) != LEFT)
341 for (vsize i = broken_intos_.size (); i--;)
342 scm_gc_mark (broken_intos_[i]->self_scm ());
346 Set left or right bound to IT.
348 Warning: caller should ensure that subsequent calls put in ITems
349 that are left-to-right ordered.
352 add_bound_item (Spanner *sp, Grob *it)
354 if (!sp->get_bound (LEFT))
355 sp->set_bound (LEFT, it);
357 sp->set_bound (RIGHT, it);
360 MAKE_SCHEME_CALLBACK (Spanner, set_spacing_rods, 1);
362 Spanner::set_spacing_rods (SCM smob)
364 Grob *me = unsmob_grob (smob);
365 SCM num_length = me->get_property ("minimum-length");
366 if (scm_is_number (num_length))
369 Spanner *sp = dynamic_cast<Spanner *> (me);
370 System *root = get_root_system (me);
371 Drul_array<Item*> bounds (sp->get_bound (LEFT),
372 sp->get_bound (RIGHT));
373 if (!bounds[LEFT] || !bounds[RIGHT])
374 return SCM_UNSPECIFIED;
376 vector<Item*> cols (root->broken_col_range (bounds[LEFT]->get_column (),
377 bounds[RIGHT]->get_column ()));
382 r.item_drul_[LEFT] = sp->get_bound (LEFT);
383 r.item_drul_[RIGHT] = cols[0]->find_prebroken_piece (LEFT);
384 r.distance_ = robust_scm2double (num_length, 0);
387 r.item_drul_[LEFT] = cols.back ()->find_prebroken_piece (RIGHT);
388 r.item_drul_[RIGHT] = sp->get_bound (RIGHT);
392 r.distance_ = robust_scm2double (num_length, 0);
393 r.item_drul_[LEFT] = sp->get_bound (LEFT);
394 r.item_drul_[RIGHT] = sp->get_bound (RIGHT);
398 return SCM_UNSPECIFIED;
401 MAKE_SCHEME_CALLBACK (Spanner, calc_normalized_endpoints, 1);
403 Spanner::calc_normalized_endpoints (SCM smob)
405 Spanner *me = unsmob_spanner (smob);
406 SCM result = SCM_EOL;
408 Spanner *orig = dynamic_cast<Spanner *> (me->original ());
410 orig = orig ? orig : me;
412 if (orig->is_broken ())
414 Real total_width = 0.0;
415 vector<Real> span_data;
417 if (!orig->is_broken ())
418 span_data.push_back (orig->spanner_length ());
420 for (vsize i = 0; i < orig->broken_intos_.size (); i++)
421 span_data.push_back (orig->broken_intos_[i]->spanner_length ());
423 vector<Interval> unnormalized_endpoints;
425 for (vsize i = 0; i < span_data.size (); i++)
427 unnormalized_endpoints.push_back (Interval (total_width, total_width + span_data[i]));
428 total_width += span_data[i];
431 for (vsize i = 0; i < unnormalized_endpoints.size (); i++)
433 SCM t = ly_interval2scm (1 / total_width * unnormalized_endpoints[i]);
434 orig->broken_intos_[i]->set_property ("normalized-endpoints", t);
435 if (me->get_break_index () == i)
441 result = scm_cons (scm_from_double (0.0), scm_from_double (1.0));
442 orig->set_property ("normalized-endpoints", result);
449 unsmob_spanner (SCM s)
451 return dynamic_cast<Spanner *> (unsmob_grob (s));
454 MAKE_SCHEME_CALLBACK (Spanner, bounds_width, 1);
456 Spanner::bounds_width (SCM grob)
458 Spanner *me = unsmob_spanner (grob);
460 Grob *common = me->get_bound (LEFT)->common_refpoint (me->get_bound (RIGHT), X_AXIS);
462 Interval w (me->get_bound (LEFT)->relative_coordinate (common, X_AXIS),
463 me->get_bound (RIGHT)->relative_coordinate (common, X_AXIS));
465 w -= me->relative_coordinate (common, X_AXIS);
467 return ly_interval2scm (w);
470 MAKE_SCHEME_CALLBACK (Spanner, kill_zero_spanned_time, 1);
472 Spanner::kill_zero_spanned_time (SCM grob)
474 Spanner *me = unsmob_spanner (grob);
476 Remove the line or hairpin at the start of the line. For
477 piano voice indicators, it makes no sense to have them at
478 the start of the line.
480 I'm not sure what the official rules for glissandi are, but
481 usually the 2nd note of the glissando is "exact", so when playing
482 from the start of the line, there is no need to glide.
484 From a typographical p.o.v. this makes sense, since the amount of
485 space left of a note at the start of a line is very small.
490 if (me->get_bound (LEFT)->break_status_dir ())
492 Interval_t<Moment> moments = me->spanned_time ();
493 moments [LEFT].grace_part_ = 0;
494 if (moments.length () == Moment (0, 0))
498 return SCM_UNSPECIFIED;
502 Spanner::get_cached_pure_property (SCM sym, int start, int end)
504 // The pure property cache is indexed by (name start . end), where name is
505 // a symbol, and start and end are numbers referring to the starting and
506 // ending column ranks of the current line.
507 if (scm_hash_table_p (pure_property_cache_) == SCM_BOOL_F)
508 return SCM_UNDEFINED;
510 SCM key = scm_cons (sym, scm_cons (scm_from_int (start), scm_from_int (end)));
511 return scm_hash_ref (pure_property_cache_, key, SCM_UNDEFINED);
515 Spanner::cache_pure_property (SCM sym, int start, int end, SCM val)
517 if (scm_hash_table_p (pure_property_cache_) == SCM_BOOL_F)
518 pure_property_cache_ = scm_c_make_hash_table (17);
520 SCM key = scm_cons (sym, scm_cons (scm_from_int (start), scm_from_int (end)));
521 scm_hash_set_x (pure_property_cache_, key, val);
524 ADD_INTERFACE (Spanner,
525 "Some objects are horizontally spanned between objects. For"
526 " example, slurs, beams, ties, etc. These grobs form a subtype"
527 " called @code{Spanner}. All spanners have two span points"
528 " (these must be @code{Item} objects), one on the left and one"
529 " on the right. The left bound is also the X@tie{}reference"
530 " point of the spanner.",
533 "normalized-endpoints "