]> git.donarmstrong.com Git - lilypond.git/blob - lily/spanner.cc
7c36f1755b5701a45d2ea42aea9b7b0bf7a60963
[lilypond.git] / lily / spanner.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 1996--2014 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."
114                                             "  Ignoring orphaned part",
115                                             name ().c_str ()));
116               continue;
117             }
118
119           Spanner *span = dynamic_cast<Spanner *> (clone ());
120           span->set_bound (LEFT, bounds[LEFT]);
121           span->set_bound (RIGHT, bounds[RIGHT]);
122
123           if (!bounds[LEFT]->get_system ()
124               || !bounds[RIGHT]->get_system ()
125               || bounds[LEFT]->get_system () != bounds[RIGHT]->get_system ())
126             {
127               programming_error ("bounds of spanner are invalid");
128               span->suicide ();
129             }
130           else
131             {
132               bounds[LEFT]->get_system ()->typeset_grob (span);
133               broken_intos_.push_back (span);
134             }
135         }
136     }
137   vector_sort (broken_intos_, Spanner::less);
138   for (vsize i = broken_intos_.size (); i--;)
139     broken_intos_[i]->break_index_ = i;
140 }
141
142 vsize
143 Spanner::get_break_index () const
144 {
145   return break_index_;
146 }
147
148 void
149 Spanner::set_my_columns ()
150 {
151   for (LEFT_and_RIGHT (d))
152     {
153       if (!spanned_drul_[d]->get_system ())
154         set_bound (d, spanned_drul_[d]->find_prebroken_piece ((Direction) - d));
155     }
156 }
157
158 Interval_t<int>
159 Spanner::spanned_rank_interval () const
160 {
161   Interval_t<int> iv (0, 0);
162
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 ();
167   return iv;
168 }
169
170 Interval_t<Moment>
171 Spanner::spanned_time () const
172 {
173   return spanned_time_interval (spanned_drul_[LEFT],
174                                 spanned_drul_[RIGHT]);
175 }
176
177 Item *
178 Spanner::get_bound (Direction d) const
179 {
180   return spanned_drul_[d];
181 }
182
183 /*
184   Set the items that this spanner spans. If D == LEFT, we also set the
185   X-axis parent of THIS to S.
186
187   For example, when a slur crosses a line break, it's broken into two
188   pieces.  The second piece shouldn't be positioned relative to the
189   original NoteColumn, but rather to the PaperColumn after the break.
190 */
191 void
192 Spanner::set_bound (Direction d, Grob *s)
193 {
194   Item *i = dynamic_cast<Item *> (s);
195   if (!i)
196     {
197       programming_error ("must have Item for spanner bound of " + name ());
198       return;
199     }
200
201   spanned_drul_[d] = i;
202
203   /**
204      We check for System to prevent the column -> line_of_score
205      -> column -> line_of_score -> etc situation */
206   if (d == LEFT && !dynamic_cast<System *> (this))
207     /*
208       If the X-parent is a spanner, it will be split across linebreaks, too,
209       so we shouldn't have to overwrite it with the bound. Also, we need
210       original parent for alignment.
211       This happens e.g. for MultiMeasureRestNumbers and PercentRepeatCounters.
212     */
213     if (!dynamic_cast <Spanner *> (this->get_parent (X_AXIS)))
214       set_parent (i, X_AXIS);
215
216   /*
217     Signal that this column needs to be kept alive. They need to be
218     kept alive to have meaningful position and linebreaking.
219
220     [maybe we should try keeping all columns alive?, and perhaps
221     inherit position from their (non-)musical brother]
222   */
223   if (dynamic_cast<Paper_column *> (i))
224     Pointer_group_interface::add_grob (i, ly_symbol2scm ("bounded-by-me"), this);
225 }
226
227 Spanner::Spanner (SCM s)
228   : Grob (s)
229 {
230   break_index_ = 0;
231   spanned_drul_.set (0, 0);
232   pure_property_cache_ = SCM_UNDEFINED;
233 }
234
235 Spanner::Spanner (Spanner const &s)
236   : Grob (s)
237 {
238   spanned_drul_.set (0, 0);
239   pure_property_cache_ = SCM_UNDEFINED;
240 }
241
242 /*
243   Certain spanners have pre-computed X values that lie either in
244   X-positions or the X key of the alists returned for left-bound-info
245   and right-bound-info.  These are calculated to give the real length
246   of a spanner (which, because of various padding or overhang properties,
247   can extend pass or arrive short of a given bound).  If possible, we
248   use these to calculate the spanner's length, and otherwise, we use
249   the bound.
250
251   For those writing a new spanner, DO NOT use both X-positions and
252   left-bound-info/right-bound-info.
253 */
254 Real
255 Spanner::spanner_length () const
256 {
257   Interval lr = robust_scm2interval (get_property ("X-positions"),
258                                      Interval (1, -1));
259
260   if (lr.is_empty ())
261     {
262       Drul_array<SCM> bounds (get_property ("left-bound-info"),
263                               get_property ("right-bound-info"));
264
265       for (LEFT_and_RIGHT (d))
266         lr[d] = robust_scm2double (ly_assoc_get (ly_symbol2scm ("X"),
267                                                  bounds[d], SCM_BOOL_F), -d);
268     }
269
270   if (lr.is_empty ())
271     {
272       for (LEFT_and_RIGHT (d))
273         lr[d] = spanned_drul_[d]->relative_coordinate (0, X_AXIS);
274     }
275
276   if (lr.is_empty ())
277     programming_error ("spanner with negative length");
278
279   return lr.length ();
280 }
281
282 System *
283 Spanner::get_system () const
284 {
285   if (!spanned_drul_[LEFT] || !spanned_drul_[RIGHT])
286     return 0;
287   if (spanned_drul_[LEFT]->get_system () != spanned_drul_[RIGHT]->get_system ())
288     return 0;
289   return spanned_drul_[LEFT]->get_system ();
290 }
291
292 Grob *
293 Spanner::find_broken_piece (System *l) const
294 {
295   vsize idx = binary_search (broken_intos_, (Spanner *) l, Spanner::less);
296   if (idx != VPOS)
297     return broken_intos_ [idx];
298   return 0;
299 }
300
301 Spanner *
302 Spanner::broken_neighbor (Direction d) const
303 {
304   if (!original_)
305     return 0;
306
307   vsize k = get_break_index ();
308   Spanner *orig = dynamic_cast<Spanner *> (original_);
309   int j = int (k) + d;
310   if (j < 0 || vsize (j) >= orig->broken_intos_.size ())
311     return 0;
312
313   return orig->broken_intos_[j];
314 }
315
316 int
317 Spanner::compare (Spanner *const &p1, Spanner *const &p2)
318 {
319   return p1->get_system ()->get_rank () - p2->get_system ()->get_rank ();
320 }
321
322 bool
323 Spanner::less (Spanner *const &a, Spanner *const &b)
324 {
325   return a->get_system ()->get_rank () < b->get_system ()->get_rank ();
326 }
327
328 bool
329 Spanner::is_broken () const
330 {
331   return broken_intos_.size ();
332 }
333
334 void
335 Spanner::derived_mark () const
336 {
337   scm_gc_mark (pure_property_cache_);
338
339   for (LEFT_and_RIGHT (d))
340     if (spanned_drul_[d])
341       scm_gc_mark (spanned_drul_[d]->self_scm ());
342   ;
343
344   for (vsize i = broken_intos_.size (); i--;)
345     scm_gc_mark (broken_intos_[i]->self_scm ());
346 }
347
348 /*
349   Set left or right bound to IT.
350
351   Warning: caller should ensure that subsequent calls put in ITems
352   that are left-to-right ordered.
353 */
354 void
355 add_bound_item (Spanner *sp, Grob *it)
356 {
357   if (!sp->get_bound (LEFT))
358     sp->set_bound (LEFT, it);
359   else
360     sp->set_bound (RIGHT, it);
361 }
362
363 MAKE_SCHEME_CALLBACK (Spanner, set_spacing_rods, 1);
364 SCM
365 Spanner::set_spacing_rods (SCM smob)
366 {
367   Grob *me = unsmob_grob (smob);
368   SCM num_length = me->get_property ("minimum-length");
369   if (scm_is_number (num_length))
370     {
371       Rod r;
372       Spanner *sp = dynamic_cast<Spanner *> (me);
373       System *root = get_root_system (me);
374       Drul_array<Item *> bounds (sp->get_bound (LEFT),
375                                  sp->get_bound (RIGHT));
376       if (!bounds[LEFT] || !bounds[RIGHT])
377         return SCM_UNSPECIFIED;
378
379       vector<Item *> cols (root->broken_col_range (bounds[LEFT]->get_column (),
380                                                    bounds[RIGHT]->get_column ()));
381
382       if (cols.size ())
383         {
384           Rod r;
385           r.item_drul_[LEFT] = sp->get_bound (LEFT);
386           r.item_drul_[RIGHT] = cols[0]->find_prebroken_piece (LEFT);
387           r.distance_ = robust_scm2double (num_length, 0);
388           r.add_to_cols ();
389
390           r.item_drul_[LEFT] = cols.back ()->find_prebroken_piece (RIGHT);
391           r.item_drul_[RIGHT] = sp->get_bound (RIGHT);
392           r.add_to_cols ();
393         }
394
395       r.distance_ = robust_scm2double (num_length, 0);
396       r.item_drul_[LEFT] = sp->get_bound (LEFT);
397       r.item_drul_[RIGHT] = sp->get_bound (RIGHT);
398       r.add_to_cols ();
399
400       /*
401         We do not know yet if the spanner is going to have a bound that is
402         broken. To account for this uncertainty, we add the rod twice:
403         once for the central column (see above) and once for the left column
404         (see below). As end_rods_ are never used when rods_ are used and vice
405         versa, this rod will only be accessed once for each spacing
406         configuraiton before line breaking. Then, as a grob never exists in
407         both unbroken and broken forms after line breaking, only one of these
408         two rods will be in the column vector used for spacing in
409         simple-spacer.cc get_line_confugration.
410       */
411       if (Item *left_pbp = sp->get_bound (RIGHT)->find_prebroken_piece (LEFT))
412         {
413           r.item_drul_[RIGHT] = left_pbp;
414           r.add_to_cols ();
415         }
416     }
417
418   return SCM_UNSPECIFIED;
419 }
420
421 MAKE_SCHEME_CALLBACK (Spanner, calc_normalized_endpoints, 1);
422 SCM
423 Spanner::calc_normalized_endpoints (SCM smob)
424 {
425   Spanner *me = unsmob_spanner (smob);
426   SCM result = SCM_EOL;
427
428   Spanner *orig = dynamic_cast<Spanner *> (me->original ());
429
430   orig = orig ? orig : me;
431
432   if (orig->is_broken ())
433     {
434       Real total_width = 0.0;
435       vector<Real> span_data;
436
437       if (!orig->is_broken ())
438         span_data.push_back (orig->spanner_length ());
439       else
440         for (vsize i = 0; i < orig->broken_intos_.size (); i++)
441           span_data.push_back (orig->broken_intos_[i]->spanner_length ());
442
443       vector<Interval> unnormalized_endpoints;
444
445       for (vsize i = 0; i < span_data.size (); i++)
446         {
447           unnormalized_endpoints.push_back (Interval (total_width, total_width + span_data[i]));
448           total_width += span_data[i];
449         }
450
451       for (vsize i = 0; i < unnormalized_endpoints.size (); i++)
452         {
453           SCM t = ly_interval2scm (1 / total_width * unnormalized_endpoints[i]);
454           orig->broken_intos_[i]->set_property ("normalized-endpoints", t);
455           if (me->get_break_index () == i)
456             result = t;
457         }
458     }
459   else
460     {
461       result = scm_cons (scm_from_double (0.0), scm_from_double (1.0));
462       orig->set_property ("normalized-endpoints", result);
463     }
464
465   return result;
466 }
467
468 Spanner *
469 unsmob_spanner (SCM s)
470 {
471   return dynamic_cast<Spanner *> (unsmob_grob (s));
472 }
473
474 MAKE_SCHEME_CALLBACK (Spanner, bounds_width, 1);
475 SCM
476 Spanner::bounds_width (SCM grob)
477 {
478   Spanner *me = unsmob_spanner (grob);
479
480   Grob *common = me->get_bound (LEFT)->common_refpoint (me->get_bound (RIGHT), X_AXIS);
481
482   Interval w (me->get_bound (LEFT)->relative_coordinate (common, X_AXIS),
483               me->get_bound (RIGHT)->relative_coordinate (common, X_AXIS));
484
485   w -= me->relative_coordinate (common, X_AXIS);
486
487   return ly_interval2scm (w);
488 }
489
490 MAKE_SCHEME_CALLBACK (Spanner, kill_zero_spanned_time, 1);
491 SCM
492 Spanner::kill_zero_spanned_time (SCM grob)
493 {
494   Spanner *me = unsmob_spanner (grob);
495   /*
496     Remove the line or hairpin at the start of the line.  For
497     piano voice indicators, it makes no sense to have them at
498     the start of the line.
499
500     I'm not sure what the official rules for glissandi are, but
501     usually the 2nd note of the glissando is "exact", so when playing
502     from the start of the line, there is no need to glide.
503
504     From a typographical p.o.v. this makes sense, since the amount of
505     space left of a note at the start of a line is very small.
506
507     --hwn.
508
509   */
510   if (me->get_bound (LEFT)->break_status_dir ())
511     {
512       Interval_t<Moment> moments = me->spanned_time ();
513       moments [LEFT].grace_part_ = 0;
514       if (moments.length () == Moment (0, 0))
515         me->suicide ();
516     }
517
518   return SCM_UNSPECIFIED;
519 }
520
521 SCM
522 Spanner::get_cached_pure_property (SCM sym, int start, int end)
523 {
524   // The pure property cache is indexed by (name start . end), where name is
525   // a symbol, and start and end are numbers referring to the starting and
526   // ending column ranks of the current line.
527   if (scm_hash_table_p (pure_property_cache_) == SCM_BOOL_F)
528     return SCM_UNDEFINED;
529
530   SCM key = scm_cons (sym, scm_cons (scm_from_int (start), scm_from_int (end)));
531   return scm_hash_ref (pure_property_cache_, key, SCM_UNDEFINED);
532 }
533
534 void
535 Spanner::cache_pure_property (SCM sym, int start, int end, SCM val)
536 {
537   if (scm_hash_table_p (pure_property_cache_) == SCM_BOOL_F)
538     pure_property_cache_ = scm_c_make_hash_table (17);
539
540   SCM key = scm_cons (sym, scm_cons (scm_from_int (start), scm_from_int (end)));
541   scm_hash_set_x (pure_property_cache_, key, val);
542 }
543
544 ADD_INTERFACE (Spanner,
545                "Some objects are horizontally spanned between objects.  For"
546                " example, slurs, beams, ties, etc.  These grobs form a subtype"
547                " called @code{Spanner}.  All spanners have two span points"
548                " (these must be @code{Item} objects), one on the left and one"
549                " on the right.  The left bound is also the X@tie{}reference"
550                " point of the spanner.",
551
552                /* properties */
553                "normalized-endpoints "
554                "minimum-length "
555                "spanner-broken "
556                "spanner-id "
557                "to-barline "
558               );