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