]> git.donarmstrong.com Git - lilypond.git/blob - lily/spanner.cc
Merge branch 'lilypond/translation' of ssh://git.sv.gnu.org/srv/git/lilypond into...
[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 Real
237 Spanner::spanner_length () const
238 {
239   Interval lr;
240
241   Drul_array<SCM> bounds (get_property ("left-bound-info"),
242                           get_property ("right-bound-info"));
243
244   Direction d = LEFT;
245   do
246     lr[d] = robust_scm2double (ly_assoc_get (ly_symbol2scm ("X"),
247                                              bounds[d], SCM_BOOL_F), -d);
248   while (flip (&d) != LEFT);
249
250   if (lr.is_empty ())
251     {
252       do
253         lr[d] = spanned_drul_[d]->relative_coordinate (0, X_AXIS);
254       while (flip (&d) != LEFT);
255     }
256
257   if (lr.is_empty ())
258     programming_error ("spanner with negative length");
259
260   return lr.length ();
261 }
262
263 System *
264 Spanner::get_system () const
265 {
266   if (!spanned_drul_[LEFT] || !spanned_drul_[RIGHT])
267     return 0;
268   if (spanned_drul_[LEFT]->get_system () != spanned_drul_[RIGHT]->get_system ())
269     return 0;
270   return spanned_drul_[LEFT]->get_system ();
271 }
272
273 Grob *
274 Spanner::find_broken_piece (System *l) const
275 {
276   vsize idx = binary_search (broken_intos_, (Spanner *) l, Spanner::less);
277   if (idx != VPOS)
278     return broken_intos_ [idx];
279   return 0;
280 }
281
282 Spanner *
283 Spanner::broken_neighbor (Direction d) const
284 {
285   if (!original_)
286     return 0;
287
288   vsize k = get_break_index ();
289   Spanner *orig = dynamic_cast<Spanner *> (original_);
290   int j = int (k) + d;
291   if (j < 0 || vsize (j) >= orig->broken_intos_.size ())
292     return 0;
293
294   return orig->broken_intos_[j];
295 }
296
297 int
298 Spanner::compare (Spanner *const &p1, Spanner *const &p2)
299 {
300   return p1->get_system ()->get_rank () - p2->get_system ()->get_rank ();
301 }
302
303 bool
304 Spanner::less (Spanner *const &a, Spanner *const &b)
305 {
306   return a->get_system ()->get_rank () < b->get_system ()->get_rank ();
307 }
308
309 bool
310 Spanner::is_broken () const
311 {
312   return broken_intos_.size ();
313 }
314
315 /*
316   If this is a broken spanner, return the amount the left end is to be
317   shifted horizontally so that the spanner starts after the initial
318   clef and key on the staves. This is necessary for ties, slurs,
319   crescendo and decrescendo signs, for example.
320 */
321 Real
322 Spanner::get_broken_left_end_align () const
323 {
324   Paper_column *sc = dynamic_cast<Paper_column *> (spanned_drul_[LEFT]->get_column ());
325
326   // Relevant only if left span point is first column in line
327   if (sc != NULL
328       && sc->break_status_dir () == RIGHT)
329     {
330       /*
331         We used to do a full search for the Break_align_item.
332         But that doesn't make a difference, since the Paper_column
333         is likely to contain only a Break_align_item.
334       */
335       return sc->extent (sc, X_AXIS)[RIGHT];
336     }
337
338   return 0.0;
339 }
340
341 void
342 Spanner::derived_mark () const
343 {
344   scm_gc_mark (pure_property_cache_);
345
346   Direction d = LEFT;
347   do
348     if (spanned_drul_[d])
349       scm_gc_mark (spanned_drul_[d]->self_scm ());
350   while (flip (&d) != LEFT)
351     ;
352
353   for (vsize i = broken_intos_.size (); i--;)
354     scm_gc_mark (broken_intos_[i]->self_scm ());
355 }
356
357 /*
358   Set left or right bound to IT.
359
360   Warning: caller should ensure that subsequent calls put in ITems
361   that are left-to-right ordered.
362 */
363 void
364 add_bound_item (Spanner *sp, Grob *it)
365 {
366   if (!sp->get_bound (LEFT))
367     sp->set_bound (LEFT, it);
368   else
369     sp->set_bound (RIGHT, it);
370 }
371
372 MAKE_SCHEME_CALLBACK (Spanner, set_spacing_rods, 1);
373 SCM
374 Spanner::set_spacing_rods (SCM smob)
375 {
376   Grob *me = unsmob_grob (smob);
377   SCM num_length = me->get_property ("minimum-length");
378   if (scm_is_number (num_length))
379     {
380       Rod r;
381       Spanner *sp = dynamic_cast<Spanner *> (me);
382       System *root = get_root_system (me);
383       Drul_array<Item *> bounds (sp->get_bound (LEFT),
384                                  sp->get_bound (RIGHT));
385       if (!bounds[LEFT] || !bounds[RIGHT])
386         return SCM_UNSPECIFIED;
387
388       vector<Item *> cols (root->broken_col_range (bounds[LEFT]->get_column (),
389                                                    bounds[RIGHT]->get_column ()));
390
391       if (cols.size ())
392         {
393           Rod r;
394           r.item_drul_[LEFT] = sp->get_bound (LEFT);
395           r.item_drul_[RIGHT] = cols[0]->find_prebroken_piece (LEFT);
396           r.distance_ = robust_scm2double (num_length, 0);
397           r.add_to_cols ();
398
399           r.item_drul_[LEFT] = cols.back ()->find_prebroken_piece (RIGHT);
400           r.item_drul_[RIGHT] = sp->get_bound (RIGHT);
401           r.add_to_cols ();
402         }
403
404       r.distance_ = robust_scm2double (num_length, 0);
405       r.item_drul_[LEFT] = sp->get_bound (LEFT);
406       r.item_drul_[RIGHT] = sp->get_bound (RIGHT);
407       r.add_to_cols ();
408     }
409
410   return SCM_UNSPECIFIED;
411 }
412
413 MAKE_SCHEME_CALLBACK (Spanner, calc_normalized_endpoints, 1);
414 SCM
415 Spanner::calc_normalized_endpoints (SCM smob)
416 {
417   Spanner *me = unsmob_spanner (smob);
418   SCM result = SCM_EOL;
419
420   Spanner *orig = dynamic_cast<Spanner *> (me->original ());
421
422   orig = orig ? orig : me;
423
424   if (orig->is_broken ())
425     {
426       Real total_width = 0.0;
427       vector<Real> span_data;
428
429       if (!orig->is_broken ())
430         span_data.push_back (orig->spanner_length ());
431       else
432         for (vsize i = 0; i < orig->broken_intos_.size (); i++)
433           span_data.push_back (orig->broken_intos_[i]->spanner_length ());
434
435       vector<Interval> unnormalized_endpoints;
436
437       for (vsize i = 0; i < span_data.size (); i++)
438         {
439           unnormalized_endpoints.push_back (Interval (total_width, total_width + span_data[i]));
440           total_width += span_data[i];
441         }
442
443       for (vsize i = 0; i < unnormalized_endpoints.size (); i++)
444         {
445           SCM t = ly_interval2scm (1 / total_width * unnormalized_endpoints[i]);
446           orig->broken_intos_[i]->set_property ("normalized-endpoints", t);
447           if (me->get_break_index () == i)
448             result = t;
449         }
450     }
451   else
452     {
453       result = scm_cons (scm_from_double (0.0), scm_from_double (1.0));
454       orig->set_property ("normalized-endpoints", result);
455     }
456
457   return result;
458 }
459
460 Spanner *
461 unsmob_spanner (SCM s)
462 {
463   return dynamic_cast<Spanner *> (unsmob_grob (s));
464 }
465
466 MAKE_SCHEME_CALLBACK (Spanner, bounds_width, 1);
467 SCM
468 Spanner::bounds_width (SCM grob)
469 {
470   Spanner *me = unsmob_spanner (grob);
471
472   Grob *common = me->get_bound (LEFT)->common_refpoint (me->get_bound (RIGHT), X_AXIS);
473
474   Interval w (me->get_bound (LEFT)->relative_coordinate (common, X_AXIS),
475               me->get_bound (RIGHT)->relative_coordinate (common, X_AXIS));
476
477   w -= me->relative_coordinate (common, X_AXIS);
478
479   return ly_interval2scm (w);
480 }
481
482 MAKE_SCHEME_CALLBACK (Spanner, kill_zero_spanned_time, 1);
483 SCM
484 Spanner::kill_zero_spanned_time (SCM grob)
485 {
486   Spanner *me = unsmob_spanner (grob);
487   /*
488     Remove the line or hairpin at the start of the line.  For
489     piano voice indicators, it makes no sense to have them at
490     the start of the line.
491
492     I'm not sure what the official rules for glissandi are, but
493     usually the 2nd note of the glissando is "exact", so when playing
494     from the start of the line, there is no need to glide.
495
496     From a typographical p.o.v. this makes sense, since the amount of
497     space left of a note at the start of a line is very small.
498
499     --hwn.
500
501   */
502   if (me->get_bound (LEFT)->break_status_dir ())
503     {
504       Interval_t<Moment> moments = me->spanned_time ();
505       moments [LEFT].grace_part_ = 0;
506       if (moments.length () == Moment (0, 0))
507         me->suicide ();
508     }
509
510   return SCM_UNSPECIFIED;
511 }
512
513 SCM
514 Spanner::get_cached_pure_property (SCM sym, int start, int end)
515 {
516   // The pure property cache is indexed by (name start . end), where name is
517   // a symbol, and start and end are numbers referring to the starting and
518   // ending column ranks of the current line.
519   if (scm_hash_table_p (pure_property_cache_) == SCM_BOOL_F)
520     return SCM_UNDEFINED;
521
522   SCM key = scm_cons (sym, scm_cons (scm_from_int (start), scm_from_int (end)));
523   return scm_hash_ref (pure_property_cache_, key, SCM_UNDEFINED);
524 }
525
526 void
527 Spanner::cache_pure_property (SCM sym, int start, int end, SCM val)
528 {
529   if (scm_hash_table_p (pure_property_cache_) == SCM_BOOL_F)
530     pure_property_cache_ = scm_c_make_hash_table (17);
531
532   SCM key = scm_cons (sym, scm_cons (scm_from_int (start), scm_from_int (end)));
533   scm_hash_set_x (pure_property_cache_, key, val);
534 }
535
536 ADD_INTERFACE (Spanner,
537                "Some objects are horizontally spanned between objects.  For"
538                " example, slurs, beams, ties, etc.  These grobs form a subtype"
539                " called @code{Spanner}.  All spanners have two span points"
540                " (these must be @code{Item} objects), one on the left and one"
541                " on the right.  The left bound is also the X@tie{}reference"
542                " point of the spanner.",
543
544                /* properties */
545                "normalized-endpoints "
546                "minimum-length "
547                "spanner-broken "
548                "spanner-id "
549                "to-barline "
550               );