]> git.donarmstrong.com Git - lilypond.git/blob - lily/spanner.cc
Merge branch 'master' into lilypond/translation
[lilypond.git] / lily / spanner.cc
1 /*
2   spanner.cc -- implement Spanner
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 1996--2009 Han-Wen Nienhuys <hanwen@xs4all.nl>
7 */
8
9 #include "libc-extension.hh"
10 #include "moment.hh"
11 #include "paper-column.hh"
12 #include "paper-score.hh"
13 #include "pointer-group-interface.hh"
14 #include "stencil.hh"
15 #include "system.hh"
16 #include "warn.hh"
17
18 Grob *
19 Spanner::clone () const
20 {
21   return new Spanner (*this);
22 }
23
24 void
25 Spanner::do_break_processing ()
26 {
27   //break_into_pieces
28   Item *left = spanned_drul_[LEFT];
29   Item *right = spanned_drul_[RIGHT];
30
31   if (!left || !right)
32     return;
33
34
35   if (get_system () || is_broken ())
36     return;
37
38   if (left == right)
39     {
40       /*
41         If we have a spanner spanning one column, we must break it
42         anyway because it might provide a parent for another item.  */
43       Direction d = LEFT;
44       do
45         {
46           Item *bound = left->find_prebroken_piece (d);
47           if (!bound)
48             programming_error ("no broken bound");
49           else if (bound->get_system ())
50             {
51               Spanner *span = dynamic_cast<Spanner *> (clone ());
52               span->set_bound (LEFT, bound);
53               span->set_bound (RIGHT, bound);
54
55               assert (span->get_system ());
56               span->get_system ()->typeset_grob (span);
57               broken_intos_.push_back (span);
58             }
59         }
60       while ((flip (&d)) != LEFT);
61     }
62   else
63     {
64       System *root  = get_root_system (this);
65       vector<Item*> break_points = root->broken_col_range (left, right);
66
67       break_points.insert (break_points.begin () + 0, left);
68       break_points.push_back (right);
69
70       Slice parent_rank_slice;
71       parent_rank_slice.set_full ();
72       
73       /*
74         Check if our parent in X-direction spans equally wide
75         or wider than we do.
76       */
77       for (int a = X_AXIS; a < NO_AXES; a++)
78         {
79           if (Spanner *parent = dynamic_cast<Spanner *> (get_parent ((Axis)a)))
80             parent_rank_slice.intersect (parent->spanned_rank_interval ());
81         }
82   
83       for (vsize i = 1; i < break_points.size (); i++)
84         {
85           Drul_array<Item *> bounds;
86           bounds[LEFT] = break_points[i - 1];
87           bounds[RIGHT] = break_points[i];
88           Direction d = LEFT;
89           do
90             {
91               if (!bounds[d]->get_system ())
92                 bounds[d] = bounds[d]->find_prebroken_piece (- d);
93             }
94           while ((flip (&d)) != LEFT);
95
96           if (!bounds[LEFT] || ! bounds[RIGHT])
97             {
98               programming_error ("bounds of this piece aren't breakable. ");
99               continue;
100             }
101
102           bool ok = parent_rank_slice.contains (bounds[LEFT]->get_column ()->get_rank ());
103           ok = ok && parent_rank_slice.contains (bounds[RIGHT]->get_column ()->get_rank ());
104           
105           if (!ok)
106             {
107               programming_error (to_string ("Spanner `%s' is not fully contained in parent spanner. Ignoring orphaned part",
108                                             name ().c_str ()));
109               continue;
110             }
111             
112           
113           Spanner *span = dynamic_cast<Spanner *> (clone ());
114           span->set_bound (LEFT, bounds[LEFT]);
115           span->set_bound (RIGHT, bounds[RIGHT]);
116
117           if (!bounds[LEFT]->get_system ()
118               || !bounds[RIGHT]->get_system ()
119               || bounds[LEFT]->get_system () != bounds[RIGHT]->get_system ())
120             {
121               programming_error ("bounds of spanner are invalid");
122               span->suicide ();
123             }
124           else
125             {
126               bounds[LEFT]->get_system ()->typeset_grob (span);
127               broken_intos_.push_back (span);
128             }
129         }
130     }
131   vector_sort (broken_intos_, Spanner::less);
132   for (vsize i = broken_intos_.size (); i--;)
133     broken_intos_[i]->break_index_ = i;
134 }
135
136 vsize
137 Spanner::get_break_index () const
138 {
139   return break_index_;
140 }
141
142 void
143 Spanner::set_my_columns ()
144 {
145   Direction i = (Direction) LEFT;
146   do
147     {
148       if (!spanned_drul_[i]->get_system ())
149         set_bound (i, spanned_drul_[i]->find_prebroken_piece ((Direction) -i));
150     }
151   while (flip (&i) != LEFT);
152 }
153
154 Interval_t<int>
155 Spanner::spanned_rank_interval () const
156 {
157   Interval_t<int> iv (0, 0);
158
159   if (spanned_drul_[LEFT] && spanned_drul_[LEFT]->get_column ())
160     iv[LEFT] = spanned_drul_[LEFT]->get_column ()->get_rank ();
161   if (spanned_drul_[RIGHT] && spanned_drul_[RIGHT]->get_column ())
162     iv[RIGHT] = spanned_drul_[RIGHT]->get_column ()->get_rank ();
163   return iv;
164 }
165
166 Interval_t<Moment>
167 Spanner::spanned_time () const
168 {
169   return spanned_time_interval (spanned_drul_[LEFT],
170                                 spanned_drul_[RIGHT]);
171 }
172
173
174 Item *
175 Spanner::get_bound (Direction d) const
176 {
177   return spanned_drul_[d];
178 }
179
180 /*
181   Set the items that this spanner spans. If D == LEFT, we also set the
182   X-axis parent of THIS to S.
183 */
184 void
185 Spanner::set_bound (Direction d, Grob *s)
186 {
187   Item *i = dynamic_cast<Item *> (s);
188   if (!i)
189     {
190       programming_error ("must have Item for spanner bound of " + name());
191       return;
192     }
193
194   spanned_drul_[d] = i;
195
196   /**
197      We check for System to prevent the column -> line_of_score
198      -> column -> line_of_score -> etc situation */
199   if (d == LEFT && !dynamic_cast<System *> (this))
200     set_parent (i, X_AXIS);
201
202   /*
203     Signal that this column needs to be kept alive. They need to be
204     kept alive to have meaningful position and linebreaking.
205
206     [maybe we should try keeping all columns alive?, and perhaps
207     inherit position from their (non-)musical brother]
208   */
209   if (dynamic_cast<Paper_column *> (i))
210     Pointer_group_interface::add_grob (i, ly_symbol2scm ("bounded-by-me"), this);
211 }
212
213 Spanner::Spanner (SCM s)
214   : Grob (s)
215 {
216   break_index_ = 0;
217   spanned_drul_.set (0, 0);
218 }
219
220 Spanner::Spanner (Spanner const &s)
221   : Grob (s)
222 {
223   spanned_drul_.set (0, 0);
224 }
225
226 Real
227 Spanner::spanner_length () const
228 {
229   Real l = spanned_drul_[LEFT]->relative_coordinate (0, X_AXIS);
230   Real r = spanned_drul_[RIGHT]->relative_coordinate (0, X_AXIS);
231
232   if (r < l)
233     programming_error ("spanner with negative length");
234
235   return r - l;
236 }
237
238 System *
239 Spanner::get_system () const
240 {
241   if (!spanned_drul_[LEFT] || !spanned_drul_[RIGHT])
242     return 0;
243   if (spanned_drul_[LEFT]->get_system () != spanned_drul_[RIGHT]->get_system ())
244     return 0;
245   return spanned_drul_[LEFT]->get_system ();
246 }
247
248 Grob *
249 Spanner::find_broken_piece (System *l) const
250 {
251   vsize idx = binary_search (broken_intos_, (Spanner *) l, Spanner::less);
252   if (idx != VPOS)
253     return broken_intos_ [idx];
254   return 0;
255 }
256
257 Spanner *
258 Spanner::broken_neighbor (Direction d) const
259 {
260   if (!original_)
261     return 0;
262
263   vsize k = broken_spanner_index (this);
264   Spanner *orig = dynamic_cast<Spanner*> (original_);
265   int j = int (k) + d;
266   if (j < 0 || vsize (j) >= orig->broken_intos_.size ())
267     return 0;
268
269   return orig->broken_intos_[j];
270 }
271
272 int
273 Spanner::compare (Spanner *const &p1, Spanner *const &p2)
274 {
275   return p1->get_system ()->get_rank () - p2->get_system ()->get_rank ();
276 }
277
278 bool
279 Spanner::less (Spanner *const &a, Spanner *const &b)
280 {
281   return a->get_system ()->get_rank () < b->get_system ()->get_rank ();
282 }
283
284 bool
285 Spanner::is_broken () const
286 {
287   return broken_intos_.size ();
288 }
289
290 /*
291   If this is a broken spanner, return the amount the left end is to be
292   shifted horizontally so that the spanner starts after the initial
293   clef and key on the staves. This is necessary for ties, slurs,
294   crescendo and decrescendo signs, for example.
295 */
296 Real
297 Spanner::get_broken_left_end_align () const
298 {
299   Paper_column *sc = dynamic_cast<Paper_column *> (spanned_drul_[LEFT]->get_column ());
300
301   // Relevant only if left span point is first column in line
302   if (sc != NULL
303       && sc->break_status_dir () == RIGHT)
304     {
305       /*
306         We used to do a full search for the Break_align_item.
307         But that doesn't make a difference, since the Paper_column
308         is likely to contain only a Break_align_item.
309       */
310       return sc->extent (sc, X_AXIS)[RIGHT];
311     }
312
313   return 0.0;
314 }
315
316 void
317 Spanner::derived_mark () const
318 {
319   Direction d = LEFT;
320   do
321     if (spanned_drul_[d])
322       scm_gc_mark (spanned_drul_[d]->self_scm ());
323   while (flip (&d) != LEFT)
324     ;
325
326   for (vsize i = broken_intos_.size (); i--;)
327     scm_gc_mark (broken_intos_[i]->self_scm ());
328 }
329
330 /*
331   Set left or right bound to IT.
332
333   Warning: caller should ensure that subsequent calls put in ITems
334   that are left-to-right ordered.
335 */
336 void
337 add_bound_item (Spanner *sp, Grob *it)
338 {
339   if (!sp->get_bound (LEFT))
340     sp->set_bound (LEFT, it);
341   else
342     sp->set_bound (RIGHT, it);
343 }
344
345 MAKE_SCHEME_CALLBACK (Spanner, set_spacing_rods, 1);
346 SCM
347 Spanner::set_spacing_rods (SCM smob)
348 {
349   Grob *me = unsmob_grob (smob);
350   SCM num_length = me->get_property ("minimum-length");
351   if (scm_is_number (num_length))
352     {
353       Rod r;
354       Spanner *sp = dynamic_cast<Spanner *> (me);
355       System *root = get_root_system (me);
356       Drul_array<Item*> bounds (sp->get_bound (LEFT),
357                                 sp->get_bound (RIGHT));
358       if (!bounds[LEFT] || !bounds[RIGHT])
359         return SCM_UNSPECIFIED;
360       
361       vector<Item*> cols (root->broken_col_range (bounds[LEFT]->get_column (),
362                                                   bounds[RIGHT]->get_column ()));
363
364       if (cols.size ())
365         {
366           Rod r ;
367           r.item_drul_[LEFT] = sp->get_bound (LEFT);
368           r.item_drul_[RIGHT] = cols[0]->find_prebroken_piece (LEFT);
369           r.distance_ = robust_scm2double (num_length, 0);
370           r.add_to_cols ();
371           
372           r.item_drul_[LEFT] = cols.back ()->find_prebroken_piece (RIGHT);
373           r.item_drul_[RIGHT] = sp->get_bound (RIGHT);
374           r.add_to_cols ();
375         }
376            
377       r.distance_ = robust_scm2double (num_length, 0);
378       r.item_drul_[LEFT] = sp->get_bound (LEFT);
379       r.item_drul_[RIGHT] = sp->get_bound (RIGHT);
380       r.add_to_cols ();
381     }
382   
383   return SCM_UNSPECIFIED;
384 }
385
386 /*
387   Return I such that SP == SP->ORIGINAL ()->BROKEN_INTOS_[I].
388 */
389 int
390 broken_spanner_index (Spanner const *sp)
391 {
392   Spanner *parent = dynamic_cast<Spanner *> (sp->original ());
393   /* ugh: casting */
394   return find (parent->broken_intos_, (Spanner*) sp) - parent->broken_intos_.begin ();
395 }
396
397 Spanner *
398 unsmob_spanner (SCM s)
399 {
400   return dynamic_cast<Spanner *> (unsmob_grob (s));
401 }
402
403 MAKE_SCHEME_CALLBACK (Spanner, bounds_width, 1);
404 SCM
405 Spanner::bounds_width (SCM grob)
406 {
407   Spanner *me = unsmob_spanner (grob);
408
409   Grob *common = me->get_bound (LEFT)->common_refpoint (me->get_bound (RIGHT), X_AXIS);
410
411   Interval w (me->get_bound (LEFT)->relative_coordinate (common, X_AXIS),
412               me->get_bound (RIGHT)->relative_coordinate (common, X_AXIS));
413               
414   w -= me->relative_coordinate (common, X_AXIS);
415
416   return ly_interval2scm (w);
417 }
418
419 MAKE_SCHEME_CALLBACK (Spanner, kill_zero_spanned_time, 1);
420 SCM
421 Spanner::kill_zero_spanned_time (SCM grob)
422 {
423   Spanner *me = unsmob_spanner (grob);
424   Interval_t<Moment> moments = me->spanned_time ();
425   /*
426     Remove the line or hairpin at the start of the line.  For
427     piano voice indicators, it makes no sense to have them at
428     the start of the line.
429
430     I'm not sure what the official rules for glissandi are, but
431     usually the 2nd note of the glissando is "exact", so when playing
432     from the start of the line, there is no need to glide.
433
434     From a typographical p.o.v. this makes sense, since the amount of
435     space left of a note at the start of a line is very small.
436
437     --hwn.
438
439   */
440   if (moments.length () == Moment (0, 0))
441     me->suicide ();
442
443   return SCM_UNSPECIFIED;
444 }
445
446 ADD_INTERFACE (Spanner,
447                "Some objects are horizontally spanned between objects.  For"
448                " example, slurs, beams, ties, etc.  These grobs form a subtype"
449                " called @code{Spanner}.  All spanners have two span points"
450                " (these must be @code{Item} objects), one on the left and one"
451                " on the right.  The left bound is also the X@tie{}reference"
452                " point of the spanner.",
453
454                /* properties */
455                "minimum-length "
456                "to-barline "
457                );
458