]> git.donarmstrong.com Git - lilypond.git/blob - lily/spanner.cc
Consistent bounds for slurs; issue 427 and 379
[lilypond.git] / lily / spanner.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 1996--2012 Han-Wen Nienhuys <hanwen@xs4all.nl>
5
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.
10
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.
15
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/>.
18 */
19
20 #include "libc-extension.hh"
21 #include "moment.hh"
22 #include "paper-column.hh"
23 #include "paper-score.hh"
24 #include "pointer-group-interface.hh"
25 #include "stencil.hh"
26 #include "system.hh"
27 #include "warn.hh"
28
29 Grob *
30 Spanner::clone () const
31 {
32   return new Spanner (*this);
33 }
34
35 void
36 Spanner::do_break_processing ()
37 {
38   //break_into_pieces
39   Item *left = spanned_drul_[LEFT];
40   Item *right = spanned_drul_[RIGHT];
41
42   if (!left || !right)
43     return;
44
45   if (get_system () || is_broken ())
46     return;
47
48   if (left == right)
49     {
50       /*
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))
54         {
55           Item *bound = left->find_prebroken_piece (d);
56           if (!bound)
57             programming_error ("no broken bound");
58           else if (bound->get_system ())
59             {
60               Spanner *span = dynamic_cast<Spanner *> (clone ());
61               span->set_bound (LEFT, bound);
62               span->set_bound (RIGHT, bound);
63
64               assert (span->get_system ());
65               span->get_system ()->typeset_grob (span);
66               broken_intos_.push_back (span);
67             }
68         }
69     }
70   else
71     {
72       System *root = get_root_system (this);
73       vector<Item *> break_points = root->broken_col_range (left, right);
74
75       break_points.insert (break_points.begin () + 0, left);
76       break_points.push_back (right);
77
78       Slice parent_rank_slice;
79       parent_rank_slice.set_full ();
80
81       /*
82         Check if our parent in X-direction spans equally wide
83         or wider than we do.
84       */
85       for (int a = X_AXIS; a < NO_AXES; a++)
86         {
87           if (Spanner *parent = dynamic_cast<Spanner *> (get_parent ((Axis)a)))
88             parent_rank_slice.intersect (parent->spanned_rank_interval ());
89         }
90
91       for (vsize i = 1; i < break_points.size (); i++)
92         {
93           Drul_array<Item *> bounds;
94           bounds[LEFT] = break_points[i - 1];
95           bounds[RIGHT] = break_points[i];
96           for (LEFT_and_RIGHT (d))
97             {
98               if (!bounds[d]->get_system ())
99                 bounds[d] = bounds[d]->find_prebroken_piece (- d);
100             }
101
102           if (!bounds[LEFT] || ! bounds[RIGHT])
103             {
104               programming_error ("bounds of this piece aren't breakable. ");
105               continue;
106             }
107
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 ());
110
111           if (!ok)
112             {
113               programming_error (to_string ("Spanner `%s' is not fully contained in parent spanner. Ignoring orphaned part",
114                                             name ().c_str ()));
115               continue;
116             }
117
118           Spanner *span = dynamic_cast<Spanner *> (clone ());
119           span->set_bound (LEFT, bounds[LEFT]);
120           span->set_bound (RIGHT, bounds[RIGHT]);
121
122           if (!bounds[LEFT]->get_system ()
123               || !bounds[RIGHT]->get_system ()
124               || bounds[LEFT]->get_system () != bounds[RIGHT]->get_system ())
125             {
126               programming_error ("bounds of spanner are invalid");
127               span->suicide ();
128             }
129           else
130             {
131               bounds[LEFT]->get_system ()->typeset_grob (span);
132               broken_intos_.push_back (span);
133             }
134         }
135     }
136   vector_sort (broken_intos_, Spanner::less);
137   for (vsize i = broken_intos_.size (); i--;)
138     broken_intos_[i]->break_index_ = i;
139 }
140
141 vsize
142 Spanner::get_break_index () const
143 {
144   return break_index_;
145 }
146
147 void
148 Spanner::set_my_columns ()
149 {
150   for (LEFT_and_RIGHT (d))
151     {
152       if (!spanned_drul_[d]->get_system ())
153         set_bound (d, spanned_drul_[d]->find_prebroken_piece ((Direction) - d));
154     }
155 }
156
157 Interval_t<int>
158 Spanner::spanned_rank_interval () const
159 {
160   Interval_t<int> iv (0, 0);
161
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 ();
166   return iv;
167 }
168
169 Interval_t<Moment>
170 Spanner::spanned_time () const
171 {
172   return spanned_time_interval (spanned_drul_[LEFT],
173                                 spanned_drul_[RIGHT]);
174 }
175
176 Item *
177 Spanner::get_bound (Direction d) const
178 {
179   return spanned_drul_[d];
180 }
181
182 /*
183   Set the items that this spanner spans. If D == LEFT, we also set the
184   X-axis parent of THIS to S.
185 */
186 void
187 Spanner::set_bound (Direction d, Grob *s)
188 {
189   Item *i = dynamic_cast<Item *> (s);
190   if (!i)
191     {
192       programming_error ("must have Item for spanner bound of " + name ());
193       return;
194     }
195
196   spanned_drul_[d] = i;
197
198   /**
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);
203
204   /*
205     Signal that this column needs to be kept alive. They need to be
206     kept alive to have meaningful position and linebreaking.
207
208     [maybe we should try keeping all columns alive?, and perhaps
209     inherit position from their (non-)musical brother]
210   */
211   if (dynamic_cast<Paper_column *> (i))
212     Pointer_group_interface::add_grob (i, ly_symbol2scm ("bounded-by-me"), this);
213 }
214
215 Spanner::Spanner (SCM s)
216   : Grob (s)
217 {
218   break_index_ = 0;
219   spanned_drul_.set (0, 0);
220   pure_property_cache_ = SCM_UNDEFINED;
221 }
222
223 Spanner::Spanner (Spanner const &s)
224   : Grob (s)
225 {
226   spanned_drul_.set (0, 0);
227   pure_property_cache_ = SCM_UNDEFINED;
228 }
229
230 /*
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
237   the bound.
238
239   For those writing a new spanner, DO NOT use both X-positions and
240   left-bound-info/right-bound-info.
241 */
242 Real
243 Spanner::spanner_length () const
244 {
245   Interval lr = robust_scm2interval (get_property ("X-positions"),
246                                      Interval (1, -1));
247
248   if (lr.is_empty ())
249     {
250       Drul_array<SCM> bounds (get_property ("left-bound-info"),
251                               get_property ("right-bound-info"));
252
253       for (LEFT_and_RIGHT (d))
254         lr[d] = robust_scm2double (ly_assoc_get (ly_symbol2scm ("X"),
255                                                  bounds[d], SCM_BOOL_F), -d);
256     }
257
258   if (lr.is_empty ())
259     {
260       for (LEFT_and_RIGHT (d))
261         lr[d] = spanned_drul_[d]->relative_coordinate (0, X_AXIS);
262     }
263
264   if (lr.is_empty ())
265     programming_error ("spanner with negative length");
266
267   return lr.length ();
268 }
269
270 System *
271 Spanner::get_system () const
272 {
273   if (!spanned_drul_[LEFT] || !spanned_drul_[RIGHT])
274     return 0;
275   if (spanned_drul_[LEFT]->get_system () != spanned_drul_[RIGHT]->get_system ())
276     return 0;
277   return spanned_drul_[LEFT]->get_system ();
278 }
279
280 Grob *
281 Spanner::find_broken_piece (System *l) const
282 {
283   vsize idx = binary_search (broken_intos_, (Spanner *) l, Spanner::less);
284   if (idx != VPOS)
285     return broken_intos_ [idx];
286   return 0;
287 }
288
289 Spanner *
290 Spanner::broken_neighbor (Direction d) const
291 {
292   if (!original_)
293     return 0;
294
295   vsize k = get_break_index ();
296   Spanner *orig = dynamic_cast<Spanner *> (original_);
297   int j = int (k) + d;
298   if (j < 0 || vsize (j) >= orig->broken_intos_.size ())
299     return 0;
300
301   return orig->broken_intos_[j];
302 }
303
304 int
305 Spanner::compare (Spanner *const &p1, Spanner *const &p2)
306 {
307   return p1->get_system ()->get_rank () - p2->get_system ()->get_rank ();
308 }
309
310 bool
311 Spanner::less (Spanner *const &a, Spanner *const &b)
312 {
313   return a->get_system ()->get_rank () < b->get_system ()->get_rank ();
314 }
315
316 bool
317 Spanner::is_broken () const
318 {
319   return broken_intos_.size ();
320 }
321
322 void
323 Spanner::derived_mark () const
324 {
325   scm_gc_mark (pure_property_cache_);
326
327   for (LEFT_and_RIGHT (d))
328     if (spanned_drul_[d])
329       scm_gc_mark (spanned_drul_[d]->self_scm ());
330     ;
331
332   for (vsize i = broken_intos_.size (); i--;)
333     scm_gc_mark (broken_intos_[i]->self_scm ());
334 }
335
336 /*
337   Set left or right bound to IT.
338
339   Warning: caller should ensure that subsequent calls put in ITems
340   that are left-to-right ordered.
341 */
342 void
343 add_bound_item (Spanner *sp, Grob *it)
344 {
345   if (!sp->get_bound (LEFT))
346     sp->set_bound (LEFT, it);
347   else
348     sp->set_bound (RIGHT, it);
349 }
350
351 MAKE_SCHEME_CALLBACK (Spanner, set_spacing_rods, 1);
352 SCM
353 Spanner::set_spacing_rods (SCM smob)
354 {
355   Grob *me = unsmob_grob (smob);
356   SCM num_length = me->get_property ("minimum-length");
357   if (scm_is_number (num_length))
358     {
359       Rod r;
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;
366
367       vector<Item *> cols (root->broken_col_range (bounds[LEFT]->get_column (),
368                                                    bounds[RIGHT]->get_column ()));
369
370       if (cols.size ())
371         {
372           Rod r;
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);
376           r.add_to_cols ();
377
378           r.item_drul_[LEFT] = cols.back ()->find_prebroken_piece (RIGHT);
379           r.item_drul_[RIGHT] = sp->get_bound (RIGHT);
380           r.add_to_cols ();
381         }
382
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);
386       r.add_to_cols ();
387     }
388
389   return SCM_UNSPECIFIED;
390 }
391
392 MAKE_SCHEME_CALLBACK (Spanner, calc_normalized_endpoints, 1);
393 SCM
394 Spanner::calc_normalized_endpoints (SCM smob)
395 {
396   Spanner *me = unsmob_spanner (smob);
397   SCM result = SCM_EOL;
398
399   Spanner *orig = dynamic_cast<Spanner *> (me->original ());
400
401   orig = orig ? orig : me;
402
403   if (orig->is_broken ())
404     {
405       Real total_width = 0.0;
406       vector<Real> span_data;
407
408       if (!orig->is_broken ())
409         span_data.push_back (orig->spanner_length ());
410       else
411         for (vsize i = 0; i < orig->broken_intos_.size (); i++)
412           span_data.push_back (orig->broken_intos_[i]->spanner_length ());
413
414       vector<Interval> unnormalized_endpoints;
415
416       for (vsize i = 0; i < span_data.size (); i++)
417         {
418           unnormalized_endpoints.push_back (Interval (total_width, total_width + span_data[i]));
419           total_width += span_data[i];
420         }
421
422       for (vsize i = 0; i < unnormalized_endpoints.size (); i++)
423         {
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)
427             result = t;
428         }
429     }
430   else
431     {
432       result = scm_cons (scm_from_double (0.0), scm_from_double (1.0));
433       orig->set_property ("normalized-endpoints", result);
434     }
435
436   return result;
437 }
438
439 Spanner *
440 unsmob_spanner (SCM s)
441 {
442   return dynamic_cast<Spanner *> (unsmob_grob (s));
443 }
444
445 MAKE_SCHEME_CALLBACK (Spanner, bounds_width, 1);
446 SCM
447 Spanner::bounds_width (SCM grob)
448 {
449   Spanner *me = unsmob_spanner (grob);
450
451   Grob *common = me->get_bound (LEFT)->common_refpoint (me->get_bound (RIGHT), X_AXIS);
452
453   Interval w (me->get_bound (LEFT)->relative_coordinate (common, X_AXIS),
454               me->get_bound (RIGHT)->relative_coordinate (common, X_AXIS));
455
456   w -= me->relative_coordinate (common, X_AXIS);
457
458   return ly_interval2scm (w);
459 }
460
461 MAKE_SCHEME_CALLBACK (Spanner, kill_zero_spanned_time, 1);
462 SCM
463 Spanner::kill_zero_spanned_time (SCM grob)
464 {
465   Spanner *me = unsmob_spanner (grob);
466   /*
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.
470
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.
474
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.
477
478     --hwn.
479
480   */
481   if (me->get_bound (LEFT)->break_status_dir ())
482     {
483       Interval_t<Moment> moments = me->spanned_time ();
484       moments [LEFT].grace_part_ = 0;
485       if (moments.length () == Moment (0, 0))
486         me->suicide ();
487     }
488
489   return SCM_UNSPECIFIED;
490 }
491
492 SCM
493 Spanner::get_cached_pure_property (SCM sym, int start, int end)
494 {
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;
500
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);
503 }
504
505 void
506 Spanner::cache_pure_property (SCM sym, int start, int end, SCM val)
507 {
508   if (scm_hash_table_p (pure_property_cache_) == SCM_BOOL_F)
509     pure_property_cache_ = scm_c_make_hash_table (17);
510
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);
513 }
514
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.",
522
523                /* properties */
524                "normalized-endpoints "
525                "minimum-length "
526                "spanner-broken "
527                "spanner-id "
528                "to-barline "
529               );