2 spanner.cc -- implement Spanner
4 source file of the GNU LilyPond music typesetter
6 (c) 1996--2007 Han-Wen Nienhuys <hanwen@xs4all.nl>
9 #include "pointer-group-interface.hh"
10 #include "libc-extension.hh"
11 #include "paper-column.hh"
12 #include "paper-column.hh"
13 #include "paper-score.hh"
20 Spanner::clone () const
22 return new Spanner (*this);
26 Spanner::do_break_processing ()
29 Item *left = spanned_drul_[LEFT];
30 Item *right = spanned_drul_[RIGHT];
36 Check if our parent in X-direction spans equally wide
39 for (int a = X_AXIS; a < NO_AXES; a++)
41 if (Spanner *parent = dynamic_cast<Spanner *> (get_parent ((Axis)a)))
43 if (!parent->spanned_rank_iv ().superset (this->spanned_rank_iv ()))
45 programming_error (to_string ("Spanner `%s' is not fully contained in parent spanner `%s'.",
47 parent->name ().c_str ()));
52 if (get_system () || is_broken ())
58 If we have a spanner spanning one column, we must break it
59 anyway because it might provide a parent for another item. */
63 Item *bound = left->find_prebroken_piece (d);
65 programming_error ("no broken bound");
66 else if (bound->get_system ())
68 Spanner *span = dynamic_cast<Spanner *> (clone ());
69 span->set_bound (LEFT, bound);
70 span->set_bound (RIGHT, bound);
72 assert (span->get_system ());
73 span->get_system ()->typeset_grob (span);
74 broken_intos_.push_back (span);
77 while ((flip (&d)) != LEFT);
81 System *root = get_root_system (this);
82 vector<Item*> break_points = root->broken_col_range (left, right);
84 break_points.insert (break_points.begin () + 0, left);
85 break_points.push_back (right);
87 for (vsize i = 1; i < break_points.size (); i++)
89 Drul_array<Item *> bounds;
90 bounds[LEFT] = break_points[i - 1];
91 bounds[RIGHT] = break_points[i];
95 if (!bounds[d]->get_system ())
96 bounds[d] = bounds[d]->find_prebroken_piece (- d);
98 while ((flip (&d)) != LEFT);
100 if (!bounds[LEFT] || ! bounds[RIGHT])
102 programming_error ("bounds of this piece aren't breakable. ");
106 Spanner *span = dynamic_cast<Spanner *> (clone ());
107 span->set_bound (LEFT, bounds[LEFT]);
108 span->set_bound (RIGHT, bounds[RIGHT]);
110 if (!bounds[LEFT]->get_system ()
112 || !bounds[RIGHT]->get_system ()
113 || bounds[LEFT]->get_system () != bounds[RIGHT]->get_system ())
115 programming_error ("bounds of spanner are invalid");
120 bounds[LEFT]->get_system ()->typeset_grob (span);
121 broken_intos_.push_back (span);
125 vector_sort (broken_intos_, Spanner::less);
126 for (vsize i = broken_intos_.size (); i--;)
127 broken_intos_[i]->break_index_ = i;
131 Spanner::get_break_index () const
137 Spanner::set_my_columns ()
139 Direction i = (Direction) LEFT;
142 if (!spanned_drul_[i]->get_system ())
143 set_bound (i, spanned_drul_[i]->find_prebroken_piece ((Direction) -i));
145 while (flip (&i) != LEFT);
149 Spanner::spanned_rank_iv () const
151 Interval_t<int> iv (0, 0);
153 if (spanned_drul_[LEFT] && spanned_drul_[LEFT]->get_column ())
154 iv[LEFT] = spanned_drul_[LEFT]->get_column ()->get_rank ();
155 if (spanned_drul_[RIGHT] && spanned_drul_[RIGHT]->get_column ())
156 iv[RIGHT] = spanned_drul_[RIGHT]->get_column ()->get_rank ();
161 Spanner::spanned_time () const
163 return spanned_time_interval (spanned_drul_[LEFT],
164 spanned_drul_[RIGHT]);
169 Spanner::get_bound (Direction d) const
171 return spanned_drul_ [d];
175 Set the items that this spanner spans. If D == LEFT, we also set the
176 X-axis parent of THIS to S.
179 Spanner::set_bound (Direction d, Grob *s)
181 Item *i = dynamic_cast<Item *> (s);
184 programming_error ("must have Item for spanner bound");
188 spanned_drul_[d] = i;
191 We check for System to prevent the column -> line_of_score
192 -> column -> line_of_score -> etc situation */
193 if (d == LEFT && !dynamic_cast<System *> (this))
194 set_parent (i, X_AXIS);
197 Signal that this column needs to be kept alive. They need to be
198 kept alive to have meaningful position and linebreaking.
200 [maybe we should try keeping all columns alive?, and perhaps
201 inherit position from their (non-)musical brother]
204 if (dynamic_cast<Paper_column *> (i))
205 Pointer_group_interface::add_grob (i, ly_symbol2scm ("bounded-by-me"), this);
208 Spanner::Spanner (SCM s)
212 spanned_drul_[LEFT] = 0;
213 spanned_drul_[RIGHT] = 0;
216 Spanner::Spanner (Spanner const &s)
219 spanned_drul_[LEFT] = spanned_drul_[RIGHT] = 0;
223 Spanner::spanner_length () const
225 Real l = spanned_drul_[LEFT]->relative_coordinate (0, X_AXIS);
226 Real r = spanned_drul_[RIGHT]->relative_coordinate (0, X_AXIS);
229 programming_error ("spanner with negative length");
235 Spanner::get_system () const
237 if (!spanned_drul_[LEFT] || !spanned_drul_[RIGHT])
239 if (spanned_drul_[LEFT]->get_system () != spanned_drul_[RIGHT]->get_system ())
241 return spanned_drul_[LEFT]->get_system ();
245 Spanner::find_broken_piece (System *l) const
247 vsize idx = binary_search (broken_intos_, (Spanner *)l, Spanner::less);
249 return broken_intos_ [idx];
254 Spanner::broken_neighbor (Direction d) const
259 vsize k = broken_spanner_index (this);
260 Spanner *orig = dynamic_cast<Spanner*> (original_);
262 if (j < 0 || vsize (j) >= orig->broken_intos_.size ())
265 return orig->broken_intos_[j];
269 Spanner::compare (Spanner *const &p1, Spanner *const &p2)
271 return p1->get_system ()->get_rank () - p2->get_system ()->get_rank ();
275 Spanner::less (Spanner *const &a, Spanner *const &b)
277 return a->get_system ()->get_rank () < b->get_system ()->get_rank ();
281 Spanner::is_broken () const
283 return broken_intos_.size ();
287 If this is a broken spanner, return the amount the left end is to be
288 shifted horizontally so that the spanner starts after the initial
289 clef and key on the staves. This is necessary for ties, slurs,
290 crescendo and decrescendo signs, for example.
293 Spanner::get_broken_left_end_align () const
295 Paper_column *sc = dynamic_cast<Paper_column *> (spanned_drul_[LEFT]->get_column ());
297 // Relevant only if left span point is first column in line
299 && sc->break_status_dir () == RIGHT)
302 We used to do a full search for the Break_align_item.
303 But that doesn't make a difference, since the Paper_column
304 is likely to contain only a Break_align_item.
306 return sc->extent (sc, X_AXIS)[RIGHT];
313 Spanner::derived_mark () const
317 if (spanned_drul_[d])
318 scm_gc_mark (spanned_drul_[d]->self_scm ());
319 while (flip (&d) != LEFT)
322 for (vsize i = broken_intos_.size (); i--;)
323 scm_gc_mark (broken_intos_[i]->self_scm ());
327 Set left or right bound to IT.
329 Warning: caller should ensure that subsequent calls put in ITems
330 that are left-to-right ordered.
333 add_bound_item (Spanner *sp, Grob *it)
335 if (!sp->get_bound (LEFT))
336 sp->set_bound (LEFT, it);
338 sp->set_bound (RIGHT, it);
341 MAKE_SCHEME_CALLBACK (Spanner, set_spacing_rods, 1);
343 Spanner::set_spacing_rods (SCM smob)
345 Grob *me = unsmob_grob (smob);
346 SCM num_length = me->get_property ("minimum-length");
347 if (scm_is_number (num_length))
350 Spanner *sp = dynamic_cast<Spanner *> (me);
353 System *root = get_root_system (me);
354 vector<Item*> cols (root->broken_col_range (sp->get_bound (LEFT)->get_column (),
355 sp->get_bound (RIGHT)->get_column ()));
360 r.item_drul_[LEFT] = sp->get_bound (LEFT);
361 r.item_drul_[RIGHT] = cols[0]->find_prebroken_piece (LEFT);
362 r.distance_ = robust_scm2double (num_length, 0);
365 r.item_drul_[LEFT] = cols.back ()->find_prebroken_piece (RIGHT);
366 r.item_drul_[RIGHT] = sp->get_bound (RIGHT);
370 r.distance_ = robust_scm2double (num_length, 0);
371 r.item_drul_[LEFT] = sp->get_bound (LEFT);
372 r.item_drul_[RIGHT] = sp->get_bound (RIGHT);
376 return SCM_UNSPECIFIED;
380 Return I such that SP == SP->ORIGINAL ()->BROKEN_INTOS_[I].
383 broken_spanner_index (Spanner const *sp)
385 Spanner *parent = dynamic_cast<Spanner *> (sp->original ());
387 return find (parent->broken_intos_, (Spanner*) sp) - parent->broken_intos_.begin ();
391 unsmob_spanner (SCM s)
393 return dynamic_cast<Spanner *> (unsmob_grob (s));
396 MAKE_SCHEME_CALLBACK(Spanner, bounds_width, 1);
398 Spanner::bounds_width (SCM grob)
400 Spanner *me = unsmob_spanner (grob);
403 Grob *common = me->get_bound (LEFT)->common_refpoint (me->get_bound (RIGHT), X_AXIS);
405 Interval w (me->get_bound (LEFT)->relative_coordinate (common, X_AXIS),
406 me->get_bound (RIGHT)->relative_coordinate (common, X_AXIS));
408 w -= me->relative_coordinate (common, X_AXIS);
410 return ly_interval2scm (w);
413 ADD_INTERFACE (Spanner,
414 "Some objects are horizontally spanned between objects. For\n"
415 "example, slur, beam, tie, etc. These grobs form a subtype called\n"
416 "@code{Spanner}. All spanners have two span-points (these must be\n"
417 "@code{Item} objects), one on the left and one on the right. The left bound is\n"
418 "also the X-reference point of the spanner.\n",