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 We do not know yet if the spanner is going to have a bound that is
391 broken. To account for this uncertainty, we add the rod twice:
392 once for the central column (see above) and once for the left column
393 (see below). As end_rods_ are never used when rods_ are used and vice
394 versa, this rod will only be accessed once for each spacing
395 configuraiton before line breaking. Then, as a grob never exists in
396 both unbroken and broken forms after line breaking, only one of these
397 two rods will be in the column vector used for spacing in
398 simple-spacer.cc get_line_confugration.
400 if (Item *left_pbp = sp->get_bound (RIGHT)->find_prebroken_piece (LEFT))
402 r.item_drul_[RIGHT] = left_pbp;
407 return SCM_UNSPECIFIED;
410 MAKE_SCHEME_CALLBACK (Spanner, calc_normalized_endpoints, 1);
412 Spanner::calc_normalized_endpoints (SCM smob)
414 Spanner *me = unsmob_spanner (smob);
415 SCM result = SCM_EOL;
417 Spanner *orig = dynamic_cast<Spanner *> (me->original ());
419 orig = orig ? orig : me;
421 if (orig->is_broken ())
423 Real total_width = 0.0;
424 vector<Real> span_data;
426 if (!orig->is_broken ())
427 span_data.push_back (orig->spanner_length ());
429 for (vsize i = 0; i < orig->broken_intos_.size (); i++)
430 span_data.push_back (orig->broken_intos_[i]->spanner_length ());
432 vector<Interval> unnormalized_endpoints;
434 for (vsize i = 0; i < span_data.size (); i++)
436 unnormalized_endpoints.push_back (Interval (total_width, total_width + span_data[i]));
437 total_width += span_data[i];
440 for (vsize i = 0; i < unnormalized_endpoints.size (); i++)
442 SCM t = ly_interval2scm (1 / total_width * unnormalized_endpoints[i]);
443 orig->broken_intos_[i]->set_property ("normalized-endpoints", t);
444 if (me->get_break_index () == i)
450 result = scm_cons (scm_from_double (0.0), scm_from_double (1.0));
451 orig->set_property ("normalized-endpoints", result);
458 unsmob_spanner (SCM s)
460 return dynamic_cast<Spanner *> (unsmob_grob (s));
463 MAKE_SCHEME_CALLBACK (Spanner, bounds_width, 1);
465 Spanner::bounds_width (SCM grob)
467 Spanner *me = unsmob_spanner (grob);
469 Grob *common = me->get_bound (LEFT)->common_refpoint (me->get_bound (RIGHT), X_AXIS);
471 Interval w (me->get_bound (LEFT)->relative_coordinate (common, X_AXIS),
472 me->get_bound (RIGHT)->relative_coordinate (common, X_AXIS));
474 w -= me->relative_coordinate (common, X_AXIS);
476 return ly_interval2scm (w);
479 MAKE_SCHEME_CALLBACK (Spanner, kill_zero_spanned_time, 1);
481 Spanner::kill_zero_spanned_time (SCM grob)
483 Spanner *me = unsmob_spanner (grob);
485 Remove the line or hairpin at the start of the line. For
486 piano voice indicators, it makes no sense to have them at
487 the start of the line.
489 I'm not sure what the official rules for glissandi are, but
490 usually the 2nd note of the glissando is "exact", so when playing
491 from the start of the line, there is no need to glide.
493 From a typographical p.o.v. this makes sense, since the amount of
494 space left of a note at the start of a line is very small.
499 if (me->get_bound (LEFT)->break_status_dir ())
501 Interval_t<Moment> moments = me->spanned_time ();
502 moments [LEFT].grace_part_ = 0;
503 if (moments.length () == Moment (0, 0))
507 return SCM_UNSPECIFIED;
511 Spanner::get_cached_pure_property (SCM sym, int start, int end)
513 // The pure property cache is indexed by (name start . end), where name is
514 // a symbol, and start and end are numbers referring to the starting and
515 // ending column ranks of the current line.
516 if (scm_hash_table_p (pure_property_cache_) == SCM_BOOL_F)
517 return SCM_UNDEFINED;
519 SCM key = scm_cons (sym, scm_cons (scm_from_int (start), scm_from_int (end)));
520 return scm_hash_ref (pure_property_cache_, key, SCM_UNDEFINED);
524 Spanner::cache_pure_property (SCM sym, int start, int end, SCM val)
526 if (scm_hash_table_p (pure_property_cache_) == SCM_BOOL_F)
527 pure_property_cache_ = scm_c_make_hash_table (17);
529 SCM key = scm_cons (sym, scm_cons (scm_from_int (start), scm_from_int (end)));
530 scm_hash_set_x (pure_property_cache_, key, val);
533 ADD_INTERFACE (Spanner,
534 "Some objects are horizontally spanned between objects. For"
535 " example, slurs, beams, ties, etc. These grobs form a subtype"
536 " called @code{Spanner}. All spanners have two span points"
537 " (these must be @code{Item} objects), one on the left and one"
538 " on the right. The left bound is also the X@tie{}reference"
539 " point of the spanner.",
542 "normalized-endpoints "