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