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