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."
114 " Ignoring orphaned part",
119 Spanner *span = dynamic_cast<Spanner *> (clone ());
120 span->set_bound (LEFT, bounds[LEFT]);
121 span->set_bound (RIGHT, bounds[RIGHT]);
123 if (!bounds[LEFT]->get_system ()
124 || !bounds[RIGHT]->get_system ()
125 || bounds[LEFT]->get_system () != bounds[RIGHT]->get_system ())
127 programming_error ("bounds of spanner are invalid");
132 bounds[LEFT]->get_system ()->typeset_grob (span);
133 broken_intos_.push_back (span);
137 vector_sort (broken_intos_, Spanner::less);
138 for (vsize i = broken_intos_.size (); i--;)
139 broken_intos_[i]->break_index_ = i;
143 Spanner::get_break_index () const
149 Spanner::set_my_columns ()
151 for (LEFT_and_RIGHT (d))
153 if (!spanned_drul_[d]->get_system ())
154 set_bound (d, spanned_drul_[d]->find_prebroken_piece ((Direction) - d));
159 Spanner::spanned_rank_interval () const
161 Interval_t<int> iv (0, 0);
163 if (spanned_drul_[LEFT] && spanned_drul_[LEFT]->get_column ())
164 iv[LEFT] = spanned_drul_[LEFT]->get_column ()->get_rank ();
165 if (spanned_drul_[RIGHT] && spanned_drul_[RIGHT]->get_column ())
166 iv[RIGHT] = spanned_drul_[RIGHT]->get_column ()->get_rank ();
171 Spanner::spanned_time () const
173 return spanned_time_interval (spanned_drul_[LEFT],
174 spanned_drul_[RIGHT]);
178 Spanner::get_bound (Direction d) const
180 return spanned_drul_[d];
184 Set the items that this spanner spans. If D == LEFT, we also set the
185 X-axis parent of THIS to S.
188 Spanner::set_bound (Direction d, Grob *s)
190 Item *i = dynamic_cast<Item *> (s);
193 programming_error ("must have Item for spanner bound of " + name ());
197 spanned_drul_[d] = i;
200 We check for System to prevent the column -> line_of_score
201 -> column -> line_of_score -> etc situation */
202 if (d == LEFT && !dynamic_cast<System *> (this))
203 set_parent (i, X_AXIS);
206 Signal that this column needs to be kept alive. They need to be
207 kept alive to have meaningful position and linebreaking.
209 [maybe we should try keeping all columns alive?, and perhaps
210 inherit position from their (non-)musical brother]
212 if (dynamic_cast<Paper_column *> (i))
213 Pointer_group_interface::add_grob (i, ly_symbol2scm ("bounded-by-me"), this);
216 Spanner::Spanner (SCM s)
220 spanned_drul_.set (0, 0);
221 pure_property_cache_ = SCM_UNDEFINED;
224 Spanner::Spanner (Spanner const &s)
227 spanned_drul_.set (0, 0);
228 pure_property_cache_ = SCM_UNDEFINED;
232 Certain spanners have pre-computed X values that lie either in
233 X-positions or the X key of the alists returned for left-bound-info
234 and right-bound-info. These are calculated to give the real length
235 of a spanner (which, because of various padding or overhang properties,
236 can extend pass or arrive short of a given bound). If possible, we
237 use these to calculate the spanner's length, and otherwise, we use
240 For those writing a new spanner, DO NOT use both X-positions and
241 left-bound-info/right-bound-info.
244 Spanner::spanner_length () const
246 Interval lr = robust_scm2interval (get_property ("X-positions"),
251 Drul_array<SCM> bounds (get_property ("left-bound-info"),
252 get_property ("right-bound-info"));
254 for (LEFT_and_RIGHT (d))
255 lr[d] = robust_scm2double (ly_assoc_get (ly_symbol2scm ("X"),
256 bounds[d], SCM_BOOL_F), -d);
261 for (LEFT_and_RIGHT (d))
262 lr[d] = spanned_drul_[d]->relative_coordinate (0, X_AXIS);
266 programming_error ("spanner with negative length");
272 Spanner::get_system () const
274 if (!spanned_drul_[LEFT] || !spanned_drul_[RIGHT])
276 if (spanned_drul_[LEFT]->get_system () != spanned_drul_[RIGHT]->get_system ())
278 return spanned_drul_[LEFT]->get_system ();
282 Spanner::find_broken_piece (System *l) const
284 vsize idx = binary_search (broken_intos_, (Spanner *) l, Spanner::less);
286 return broken_intos_ [idx];
291 Spanner::broken_neighbor (Direction d) const
296 vsize k = get_break_index ();
297 Spanner *orig = dynamic_cast<Spanner *> (original_);
299 if (j < 0 || vsize (j) >= orig->broken_intos_.size ())
302 return orig->broken_intos_[j];
306 Spanner::compare (Spanner *const &p1, Spanner *const &p2)
308 return p1->get_system ()->get_rank () - p2->get_system ()->get_rank ();
312 Spanner::less (Spanner *const &a, Spanner *const &b)
314 return a->get_system ()->get_rank () < b->get_system ()->get_rank ();
318 Spanner::is_broken () const
320 return broken_intos_.size ();
324 Spanner::derived_mark () const
326 scm_gc_mark (pure_property_cache_);
328 for (LEFT_and_RIGHT (d))
329 if (spanned_drul_[d])
330 scm_gc_mark (spanned_drul_[d]->self_scm ());
333 for (vsize i = broken_intos_.size (); i--;)
334 scm_gc_mark (broken_intos_[i]->self_scm ());
338 Set left or right bound to IT.
340 Warning: caller should ensure that subsequent calls put in ITems
341 that are left-to-right ordered.
344 add_bound_item (Spanner *sp, Grob *it)
346 if (!sp->get_bound (LEFT))
347 sp->set_bound (LEFT, it);
349 sp->set_bound (RIGHT, it);
352 MAKE_SCHEME_CALLBACK (Spanner, set_spacing_rods, 1);
354 Spanner::set_spacing_rods (SCM smob)
356 Grob *me = unsmob_grob (smob);
357 SCM num_length = me->get_property ("minimum-length");
358 if (scm_is_number (num_length))
361 Spanner *sp = dynamic_cast<Spanner *> (me);
362 System *root = get_root_system (me);
363 Drul_array<Item *> bounds (sp->get_bound (LEFT),
364 sp->get_bound (RIGHT));
365 if (!bounds[LEFT] || !bounds[RIGHT])
366 return SCM_UNSPECIFIED;
368 vector<Item *> cols (root->broken_col_range (bounds[LEFT]->get_column (),
369 bounds[RIGHT]->get_column ()));
374 r.item_drul_[LEFT] = sp->get_bound (LEFT);
375 r.item_drul_[RIGHT] = cols[0]->find_prebroken_piece (LEFT);
376 r.distance_ = robust_scm2double (num_length, 0);
379 r.item_drul_[LEFT] = cols.back ()->find_prebroken_piece (RIGHT);
380 r.item_drul_[RIGHT] = sp->get_bound (RIGHT);
384 r.distance_ = robust_scm2double (num_length, 0);
385 r.item_drul_[LEFT] = sp->get_bound (LEFT);
386 r.item_drul_[RIGHT] = sp->get_bound (RIGHT);
390 return SCM_UNSPECIFIED;
393 MAKE_SCHEME_CALLBACK (Spanner, calc_normalized_endpoints, 1);
395 Spanner::calc_normalized_endpoints (SCM smob)
397 Spanner *me = unsmob_spanner (smob);
398 SCM result = SCM_EOL;
400 Spanner *orig = dynamic_cast<Spanner *> (me->original ());
402 orig = orig ? orig : me;
404 if (orig->is_broken ())
406 Real total_width = 0.0;
407 vector<Real> span_data;
409 if (!orig->is_broken ())
410 span_data.push_back (orig->spanner_length ());
412 for (vsize i = 0; i < orig->broken_intos_.size (); i++)
413 span_data.push_back (orig->broken_intos_[i]->spanner_length ());
415 vector<Interval> unnormalized_endpoints;
417 for (vsize i = 0; i < span_data.size (); i++)
419 unnormalized_endpoints.push_back (Interval (total_width, total_width + span_data[i]));
420 total_width += span_data[i];
423 for (vsize i = 0; i < unnormalized_endpoints.size (); i++)
425 SCM t = ly_interval2scm (1 / total_width * unnormalized_endpoints[i]);
426 orig->broken_intos_[i]->set_property ("normalized-endpoints", t);
427 if (me->get_break_index () == i)
433 result = scm_cons (scm_from_double (0.0), scm_from_double (1.0));
434 orig->set_property ("normalized-endpoints", result);
441 unsmob_spanner (SCM s)
443 return dynamic_cast<Spanner *> (unsmob_grob (s));
446 MAKE_SCHEME_CALLBACK (Spanner, bounds_width, 1);
448 Spanner::bounds_width (SCM grob)
450 Spanner *me = unsmob_spanner (grob);
452 Grob *common = me->get_bound (LEFT)->common_refpoint (me->get_bound (RIGHT), X_AXIS);
454 Interval w (me->get_bound (LEFT)->relative_coordinate (common, X_AXIS),
455 me->get_bound (RIGHT)->relative_coordinate (common, X_AXIS));
457 w -= me->relative_coordinate (common, X_AXIS);
459 return ly_interval2scm (w);
462 MAKE_SCHEME_CALLBACK (Spanner, kill_zero_spanned_time, 1);
464 Spanner::kill_zero_spanned_time (SCM grob)
466 Spanner *me = unsmob_spanner (grob);
468 Remove the line or hairpin at the start of the line. For
469 piano voice indicators, it makes no sense to have them at
470 the start of the line.
472 I'm not sure what the official rules for glissandi are, but
473 usually the 2nd note of the glissando is "exact", so when playing
474 from the start of the line, there is no need to glide.
476 From a typographical p.o.v. this makes sense, since the amount of
477 space left of a note at the start of a line is very small.
482 if (me->get_bound (LEFT)->break_status_dir ())
484 Interval_t<Moment> moments = me->spanned_time ();
485 moments [LEFT].grace_part_ = 0;
486 if (moments.length () == Moment (0, 0))
490 return SCM_UNSPECIFIED;
494 Spanner::get_cached_pure_property (SCM sym, int start, int end)
496 // The pure property cache is indexed by (name start . end), where name is
497 // a symbol, and start and end are numbers referring to the starting and
498 // ending column ranks of the current line.
499 if (scm_hash_table_p (pure_property_cache_) == SCM_BOOL_F)
500 return SCM_UNDEFINED;
502 SCM key = scm_cons (sym, scm_cons (scm_from_int (start), scm_from_int (end)));
503 return scm_hash_ref (pure_property_cache_, key, SCM_UNDEFINED);
507 Spanner::cache_pure_property (SCM sym, int start, int end, SCM val)
509 if (scm_hash_table_p (pure_property_cache_) == SCM_BOOL_F)
510 pure_property_cache_ = scm_c_make_hash_table (17);
512 SCM key = scm_cons (sym, scm_cons (scm_from_int (start), scm_from_int (end)));
513 scm_hash_set_x (pure_property_cache_, key, val);
516 ADD_INTERFACE (Spanner,
517 "Some objects are horizontally spanned between objects. For"
518 " example, slurs, beams, ties, etc. These grobs form a subtype"
519 " called @code{Spanner}. All spanners have two span points"
520 " (these must be @code{Item} objects), one on the left and one"
521 " on the right. The left bound is also the X@tie{}reference"
522 " point of the spanner.",
525 "normalized-endpoints "