2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 1996--2012 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];
45 if (get_system () || is_broken ())
51 If we have a spanner spanning one column, we must break it
52 anyway because it might provide a parent for another item. */
53 for (LEFT_and_RIGHT (d))
55 Item *bound = left->find_prebroken_piece (d);
57 programming_error ("no broken bound");
58 else if (bound->get_system ())
60 Spanner *span = dynamic_cast<Spanner *> (clone ());
61 span->set_bound (LEFT, bound);
62 span->set_bound (RIGHT, bound);
64 assert (span->get_system ());
65 span->get_system ()->typeset_grob (span);
66 broken_intos_.push_back (span);
72 System *root = get_root_system (this);
73 vector<Item *> break_points = root->broken_col_range (left, right);
75 break_points.insert (break_points.begin () + 0, left);
76 break_points.push_back (right);
78 Slice parent_rank_slice;
79 parent_rank_slice.set_full ();
82 Check if our parent in X-direction spans equally wide
85 for (int a = X_AXIS; a < NO_AXES; a++)
87 if (Spanner *parent = dynamic_cast<Spanner *> (get_parent ((Axis)a)))
88 parent_rank_slice.intersect (parent->spanned_rank_interval ());
91 for (vsize i = 1; i < break_points.size (); i++)
93 Drul_array<Item *> bounds;
94 bounds[LEFT] = break_points[i - 1];
95 bounds[RIGHT] = break_points[i];
96 for (LEFT_and_RIGHT (d))
98 if (!bounds[d]->get_system ())
99 bounds[d] = bounds[d]->find_prebroken_piece (- d);
102 if (!bounds[LEFT] || ! bounds[RIGHT])
104 programming_error ("bounds of this piece aren't breakable. ");
108 bool ok = parent_rank_slice.contains (bounds[LEFT]->get_column ()->get_rank ());
109 ok = ok && parent_rank_slice.contains (bounds[RIGHT]->get_column ()->get_rank ());
113 programming_error (to_string ("Spanner `%s' is not fully contained in parent spanner. Ignoring orphaned part",
118 Spanner *span = dynamic_cast<Spanner *> (clone ());
119 span->set_bound (LEFT, bounds[LEFT]);
120 span->set_bound (RIGHT, bounds[RIGHT]);
122 if (!bounds[LEFT]->get_system ()
123 || !bounds[RIGHT]->get_system ()
124 || bounds[LEFT]->get_system () != bounds[RIGHT]->get_system ())
126 programming_error ("bounds of spanner are invalid");
131 bounds[LEFT]->get_system ()->typeset_grob (span);
132 broken_intos_.push_back (span);
136 vector_sort (broken_intos_, Spanner::less);
137 for (vsize i = broken_intos_.size (); i--;)
138 broken_intos_[i]->break_index_ = i;
142 Spanner::get_break_index () const
148 Spanner::set_my_columns ()
150 for (LEFT_and_RIGHT (d))
152 if (!spanned_drul_[d]->get_system ())
153 set_bound (d, spanned_drul_[d]->find_prebroken_piece ((Direction) - d));
158 Spanner::spanned_rank_interval () const
160 Interval_t<int> iv (0, 0);
162 if (spanned_drul_[LEFT] && spanned_drul_[LEFT]->get_column ())
163 iv[LEFT] = spanned_drul_[LEFT]->get_column ()->get_rank ();
164 if (spanned_drul_[RIGHT] && spanned_drul_[RIGHT]->get_column ())
165 iv[RIGHT] = spanned_drul_[RIGHT]->get_column ()->get_rank ();
170 Spanner::spanned_time () const
172 return spanned_time_interval (spanned_drul_[LEFT],
173 spanned_drul_[RIGHT]);
177 Spanner::get_bound (Direction d) const
179 return spanned_drul_[d];
183 Set the items that this spanner spans. If D == LEFT, we also set the
184 X-axis parent of THIS to S.
187 Spanner::set_bound (Direction d, Grob *s)
189 Item *i = dynamic_cast<Item *> (s);
192 programming_error ("must have Item for spanner bound of " + name ());
196 spanned_drul_[d] = i;
199 We check for System to prevent the column -> line_of_score
200 -> column -> line_of_score -> etc situation */
201 if (d == LEFT && !dynamic_cast<System *> (this))
202 set_parent (i, X_AXIS);
205 Signal that this column needs to be kept alive. They need to be
206 kept alive to have meaningful position and linebreaking.
208 [maybe we should try keeping all columns alive?, and perhaps
209 inherit position from their (non-)musical brother]
211 if (dynamic_cast<Paper_column *> (i))
212 Pointer_group_interface::add_grob (i, ly_symbol2scm ("bounded-by-me"), this);
215 Spanner::Spanner (SCM s)
219 spanned_drul_.set (0, 0);
220 pure_property_cache_ = SCM_UNDEFINED;
223 Spanner::Spanner (Spanner const &s)
226 spanned_drul_.set (0, 0);
227 pure_property_cache_ = SCM_UNDEFINED;
231 Certain spanners have pre-computed X values that lie either in
232 X-positions or the X key of the alists returned for left-bound-info
233 and right-bound-info. These are calculated to give the real length
234 of a spanner (which, because of various padding or overhang properties,
235 can extend pass or arrive short of a given bound). If possible, we
236 use these to calculate the spanner's length, and otherwise, we use
239 For those writing a new spanner, DO NOT use both X-positions and
240 left-bound-info/right-bound-info.
243 Spanner::spanner_length () const
245 Interval lr = robust_scm2interval (get_property ("X-positions"),
250 Drul_array<SCM> bounds (get_property ("left-bound-info"),
251 get_property ("right-bound-info"));
253 for (LEFT_and_RIGHT (d))
254 lr[d] = robust_scm2double (ly_assoc_get (ly_symbol2scm ("X"),
255 bounds[d], SCM_BOOL_F), -d);
260 for (LEFT_and_RIGHT (d))
261 lr[d] = spanned_drul_[d]->relative_coordinate (0, X_AXIS);
265 programming_error ("spanner with negative length");
271 Spanner::get_system () const
273 if (!spanned_drul_[LEFT] || !spanned_drul_[RIGHT])
275 if (spanned_drul_[LEFT]->get_system () != spanned_drul_[RIGHT]->get_system ())
277 return spanned_drul_[LEFT]->get_system ();
281 Spanner::find_broken_piece (System *l) const
283 vsize idx = binary_search (broken_intos_, (Spanner *) l, Spanner::less);
285 return broken_intos_ [idx];
290 Spanner::broken_neighbor (Direction d) const
295 vsize k = get_break_index ();
296 Spanner *orig = dynamic_cast<Spanner *> (original_);
298 if (j < 0 || vsize (j) >= orig->broken_intos_.size ())
301 return orig->broken_intos_[j];
305 Spanner::compare (Spanner *const &p1, Spanner *const &p2)
307 return p1->get_system ()->get_rank () - p2->get_system ()->get_rank ();
311 Spanner::less (Spanner *const &a, Spanner *const &b)
313 return a->get_system ()->get_rank () < b->get_system ()->get_rank ();
317 Spanner::is_broken () const
319 return broken_intos_.size ();
323 Spanner::derived_mark () const
325 scm_gc_mark (pure_property_cache_);
327 for (LEFT_and_RIGHT (d))
328 if (spanned_drul_[d])
329 scm_gc_mark (spanned_drul_[d]->self_scm ());
332 for (vsize i = broken_intos_.size (); i--;)
333 scm_gc_mark (broken_intos_[i]->self_scm ());
337 Set left or right bound to IT.
339 Warning: caller should ensure that subsequent calls put in ITems
340 that are left-to-right ordered.
343 add_bound_item (Spanner *sp, Grob *it)
345 if (!sp->get_bound (LEFT))
346 sp->set_bound (LEFT, it);
348 sp->set_bound (RIGHT, it);
351 MAKE_SCHEME_CALLBACK (Spanner, set_spacing_rods, 1);
353 Spanner::set_spacing_rods (SCM smob)
355 Grob *me = unsmob_grob (smob);
356 SCM num_length = me->get_property ("minimum-length");
357 if (scm_is_number (num_length))
360 Spanner *sp = dynamic_cast<Spanner *> (me);
361 System *root = get_root_system (me);
362 Drul_array<Item *> bounds (sp->get_bound (LEFT),
363 sp->get_bound (RIGHT));
364 if (!bounds[LEFT] || !bounds[RIGHT])
365 return SCM_UNSPECIFIED;
367 vector<Item *> cols (root->broken_col_range (bounds[LEFT]->get_column (),
368 bounds[RIGHT]->get_column ()));
373 r.item_drul_[LEFT] = sp->get_bound (LEFT);
374 r.item_drul_[RIGHT] = cols[0]->find_prebroken_piece (LEFT);
375 r.distance_ = robust_scm2double (num_length, 0);
378 r.item_drul_[LEFT] = cols.back ()->find_prebroken_piece (RIGHT);
379 r.item_drul_[RIGHT] = sp->get_bound (RIGHT);
383 r.distance_ = robust_scm2double (num_length, 0);
384 r.item_drul_[LEFT] = sp->get_bound (LEFT);
385 r.item_drul_[RIGHT] = sp->get_bound (RIGHT);
389 return SCM_UNSPECIFIED;
392 MAKE_SCHEME_CALLBACK (Spanner, calc_normalized_endpoints, 1);
394 Spanner::calc_normalized_endpoints (SCM smob)
396 Spanner *me = unsmob_spanner (smob);
397 SCM result = SCM_EOL;
399 Spanner *orig = dynamic_cast<Spanner *> (me->original ());
401 orig = orig ? orig : me;
403 if (orig->is_broken ())
405 Real total_width = 0.0;
406 vector<Real> span_data;
408 if (!orig->is_broken ())
409 span_data.push_back (orig->spanner_length ());
411 for (vsize i = 0; i < orig->broken_intos_.size (); i++)
412 span_data.push_back (orig->broken_intos_[i]->spanner_length ());
414 vector<Interval> unnormalized_endpoints;
416 for (vsize i = 0; i < span_data.size (); i++)
418 unnormalized_endpoints.push_back (Interval (total_width, total_width + span_data[i]));
419 total_width += span_data[i];
422 for (vsize i = 0; i < unnormalized_endpoints.size (); i++)
424 SCM t = ly_interval2scm (1 / total_width * unnormalized_endpoints[i]);
425 orig->broken_intos_[i]->set_property ("normalized-endpoints", t);
426 if (me->get_break_index () == i)
432 result = scm_cons (scm_from_double (0.0), scm_from_double (1.0));
433 orig->set_property ("normalized-endpoints", result);
440 unsmob_spanner (SCM s)
442 return dynamic_cast<Spanner *> (unsmob_grob (s));
445 MAKE_SCHEME_CALLBACK (Spanner, bounds_width, 1);
447 Spanner::bounds_width (SCM grob)
449 Spanner *me = unsmob_spanner (grob);
451 Grob *common = me->get_bound (LEFT)->common_refpoint (me->get_bound (RIGHT), X_AXIS);
453 Interval w (me->get_bound (LEFT)->relative_coordinate (common, X_AXIS),
454 me->get_bound (RIGHT)->relative_coordinate (common, X_AXIS));
456 w -= me->relative_coordinate (common, X_AXIS);
458 return ly_interval2scm (w);
461 MAKE_SCHEME_CALLBACK (Spanner, kill_zero_spanned_time, 1);
463 Spanner::kill_zero_spanned_time (SCM grob)
465 Spanner *me = unsmob_spanner (grob);
467 Remove the line or hairpin at the start of the line. For
468 piano voice indicators, it makes no sense to have them at
469 the start of the line.
471 I'm not sure what the official rules for glissandi are, but
472 usually the 2nd note of the glissando is "exact", so when playing
473 from the start of the line, there is no need to glide.
475 From a typographical p.o.v. this makes sense, since the amount of
476 space left of a note at the start of a line is very small.
481 if (me->get_bound (LEFT)->break_status_dir ())
483 Interval_t<Moment> moments = me->spanned_time ();
484 moments [LEFT].grace_part_ = 0;
485 if (moments.length () == Moment (0, 0))
489 return SCM_UNSPECIFIED;
493 Spanner::get_cached_pure_property (SCM sym, int start, int end)
495 // The pure property cache is indexed by (name start . end), where name is
496 // a symbol, and start and end are numbers referring to the starting and
497 // ending column ranks of the current line.
498 if (scm_hash_table_p (pure_property_cache_) == SCM_BOOL_F)
499 return SCM_UNDEFINED;
501 SCM key = scm_cons (sym, scm_cons (scm_from_int (start), scm_from_int (end)));
502 return scm_hash_ref (pure_property_cache_, key, SCM_UNDEFINED);
506 Spanner::cache_pure_property (SCM sym, int start, int end, SCM val)
508 if (scm_hash_table_p (pure_property_cache_) == SCM_BOOL_F)
509 pure_property_cache_ = scm_c_make_hash_table (17);
511 SCM key = scm_cons (sym, scm_cons (scm_from_int (start), scm_from_int (end)));
512 scm_hash_set_x (pure_property_cache_, key, val);
515 ADD_INTERFACE (Spanner,
516 "Some objects are horizontally spanned between objects. For"
517 " example, slurs, beams, ties, etc. These grobs form a subtype"
518 " called @code{Spanner}. All spanners have two span points"
519 " (these must be @code{Item} objects), one on the left and one"
520 " on the right. The left bound is also the X@tie{}reference"
521 " point of the spanner.",
524 "normalized-endpoints "