]> git.donarmstrong.com Git - lilypond.git/blob - lily/spanner.cc
Fix #296.
[lilypond.git] / lily / spanner.cc
1 /*
2   spanner.cc -- implement Spanner
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 1996--2007 Han-Wen Nienhuys <hanwen@xs4all.nl>
7 */
8
9 #include "pointer-group-interface.hh"
10 #include "libc-extension.hh"
11 #include "paper-column.hh"
12 #include "paper-column.hh"
13 #include "paper-score.hh"
14 #include "stencil.hh"
15 #include "system.hh"
16 #include "warn.hh"
17 #include "moment.hh"
18
19 Grob *
20 Spanner::clone () const
21 {
22   return new Spanner (*this);
23 }
24
25 void
26 Spanner::do_break_processing ()
27 {
28   //break_into_pieces
29   Item *left = spanned_drul_[LEFT];
30   Item *right = spanned_drul_[RIGHT];
31
32   if (!left || !right)
33     return;
34
35
36   if (get_system () || is_broken ())
37     return;
38
39   if (left == right)
40     {
41       /*
42         If we have a spanner spanning one column, we must break it
43         anyway because it might provide a parent for another item.  */
44       Direction d = LEFT;
45       do
46         {
47           Item *bound = left->find_prebroken_piece (d);
48           if (!bound)
49             programming_error ("no broken bound");
50           else if (bound->get_system ())
51             {
52               Spanner *span = dynamic_cast<Spanner *> (clone ());
53               span->set_bound (LEFT, bound);
54               span->set_bound (RIGHT, bound);
55
56               assert (span->get_system ());
57               span->get_system ()->typeset_grob (span);
58               broken_intos_.push_back (span);
59             }
60         }
61       while ((flip (&d)) != LEFT);
62     }
63   else
64     {
65       System *root  = get_root_system (this);
66       vector<Item*> break_points = root->broken_col_range (left, right);
67
68       break_points.insert (break_points.begin () + 0, left);
69       break_points.push_back (right);
70
71       Slice parent_rank_slice;
72       parent_rank_slice.set_full ();
73       
74       /*
75         Check if our parent in X-direction spans equally wide
76         or wider than we do.
77       */
78       for (int a = X_AXIS; a < NO_AXES; a++)
79         {
80           if (Spanner *parent = dynamic_cast<Spanner *> (get_parent ((Axis)a)))
81             {
82               parent_rank_slice.intersect (parent->spanned_rank_interval ());
83             }
84         }
85   
86       for (vsize i = 1; i < break_points.size (); i++)
87         {
88           Drul_array<Item *> bounds;
89           bounds[LEFT] = break_points[i - 1];
90           bounds[RIGHT] = break_points[i];
91           Direction d = LEFT;
92           do
93             {
94               if (!bounds[d]->get_system ())
95                 bounds[d] = bounds[d]->find_prebroken_piece (- d);
96             }
97           while ((flip (&d)) != LEFT);
98
99           if (!bounds[LEFT] || ! bounds[RIGHT])
100             {
101               programming_error ("bounds of this piece aren't breakable. ");
102               continue;
103             }
104
105           bool ok = parent_rank_slice.contains (bounds[LEFT]->get_column ()->get_rank ());
106           ok = ok && parent_rank_slice.contains (bounds[RIGHT]->get_column ()->get_rank ());
107           
108           if (!ok)
109             {
110               programming_error (to_string ("Spanner `%s' is not fully contained in parent spanner. Ignoring orphaned part",
111                                             name ().c_str ()));
112               continue;
113             }
114             
115           
116           Spanner *span = dynamic_cast<Spanner *> (clone ());
117           span->set_bound (LEFT, bounds[LEFT]);
118           span->set_bound (RIGHT, bounds[RIGHT]);
119
120           if (!bounds[LEFT]->get_system ()
121
122               || !bounds[RIGHT]->get_system ()
123               || bounds[LEFT]->get_system () != bounds[RIGHT]->get_system ())
124             {
125               programming_error ("bounds of spanner are invalid");
126               span->suicide ();
127             }
128           else
129             {
130               bounds[LEFT]->get_system ()->typeset_grob (span);
131               broken_intos_.push_back (span);
132             }
133         }
134     }
135   vector_sort (broken_intos_, Spanner::less);
136   for (vsize i = broken_intos_.size (); i--;)
137     broken_intos_[i]->break_index_ = i;
138 }
139
140 vsize
141 Spanner::get_break_index () const
142 {
143   return break_index_;
144 }
145
146 void
147 Spanner::set_my_columns ()
148 {
149   Direction i = (Direction) LEFT;
150   do
151     {
152       if (!spanned_drul_[i]->get_system ())
153         set_bound (i, spanned_drul_[i]->find_prebroken_piece ((Direction) -i));
154     }
155   while (flip (&i) != LEFT);
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
178 Item *
179 Spanner::get_bound (Direction d) const
180 {
181   return spanned_drul_ [d];
182 }
183
184 /*
185   Set the items that this spanner spans. If D == LEFT, we also set the
186   X-axis parent of THIS to S.
187 */
188 void
189 Spanner::set_bound (Direction d, Grob *s)
190 {
191   Item *i = dynamic_cast<Item *> (s);
192   if (!i)
193     {
194       programming_error ("must have Item for spanner bound");
195       return;
196     }
197
198   spanned_drul_[d] = i;
199
200   /**
201      We check for System to prevent the column -> line_of_score
202      -> column -> line_of_score -> etc situation */
203   if (d == LEFT && !dynamic_cast<System *> (this))
204     set_parent (i, X_AXIS);
205
206   /*
207     Signal that this column needs to be kept alive. They need to be
208     kept alive to have meaningful position and linebreaking.
209
210     [maybe we should try keeping all columns alive?, and perhaps
211     inherit position from their (non-)musical brother]
212
213   */
214   if (dynamic_cast<Paper_column *> (i))
215     Pointer_group_interface::add_grob (i, ly_symbol2scm ("bounded-by-me"), this);
216 }
217
218 Spanner::Spanner (SCM s)
219   : Grob (s)
220 {
221   break_index_ = 0;
222   spanned_drul_[LEFT] = 0;
223   spanned_drul_[RIGHT] = 0;
224 }
225
226 Spanner::Spanner (Spanner const &s)
227   : Grob (s)
228 {
229   spanned_drul_[LEFT] = spanned_drul_[RIGHT] = 0;
230 }
231
232 Real
233 Spanner::spanner_length () const
234 {
235   Real l = spanned_drul_[LEFT]->relative_coordinate (0, X_AXIS);
236   Real r = spanned_drul_[RIGHT]->relative_coordinate (0, X_AXIS);
237
238   if (r < l)
239     programming_error ("spanner with negative length");
240
241   return r - l;
242 }
243
244 System *
245 Spanner::get_system () const
246 {
247   if (!spanned_drul_[LEFT] || !spanned_drul_[RIGHT])
248     return 0;
249   if (spanned_drul_[LEFT]->get_system () != spanned_drul_[RIGHT]->get_system ())
250     return 0;
251   return spanned_drul_[LEFT]->get_system ();
252 }
253
254 Grob *
255 Spanner::find_broken_piece (System *l) const
256 {
257   vsize idx = binary_search (broken_intos_, (Spanner *)l, Spanner::less);
258   if (idx != VPOS)
259     return broken_intos_ [idx];
260   return 0;
261 }
262
263 Spanner *
264 Spanner::broken_neighbor (Direction d) const
265 {
266   if (!original_)
267     return 0;
268
269   vsize k = broken_spanner_index (this);
270   Spanner *orig = dynamic_cast<Spanner*> (original_);
271   int j = int (k) + d;
272   if (j < 0 || vsize (j) >= orig->broken_intos_.size ())
273     return 0;
274
275   return orig->broken_intos_[j];
276 }
277
278 int
279 Spanner::compare (Spanner *const &p1, Spanner *const &p2)
280 {
281   return p1->get_system ()->get_rank () - p2->get_system ()->get_rank ();
282 }
283
284 bool
285 Spanner::less (Spanner *const &a, Spanner *const &b)
286 {
287   return a->get_system ()->get_rank () < b->get_system ()->get_rank ();
288 }
289
290 bool
291 Spanner::is_broken () const
292 {
293   return broken_intos_.size ();
294 }
295
296 /*
297   If this is a broken spanner, return the amount the left end is to be
298   shifted horizontally so that the spanner starts after the initial
299   clef and key on the staves. This is necessary for ties, slurs,
300   crescendo and decrescendo signs, for example.
301 */
302 Real
303 Spanner::get_broken_left_end_align () const
304 {
305   Paper_column *sc = dynamic_cast<Paper_column *> (spanned_drul_[LEFT]->get_column ());
306
307   // Relevant only if left span point is first column in line
308   if (sc != NULL
309       && sc->break_status_dir () == RIGHT)
310     {
311       /*
312         We used to do a full search for the Break_align_item.
313         But that doesn't make a difference, since the Paper_column
314         is likely to contain only a Break_align_item.
315       */
316       return sc->extent (sc, X_AXIS)[RIGHT];
317     }
318
319   return 0.0;
320 }
321
322 void
323 Spanner::derived_mark () const
324 {
325   Direction d = LEFT;
326   do
327     if (spanned_drul_[d])
328       scm_gc_mark (spanned_drul_[d]->self_scm ());
329   while (flip (&d) != LEFT)
330     ;
331
332   for (vsize i = broken_intos_.size (); i--;)
333     scm_gc_mark (broken_intos_[i]->self_scm ());
334 }
335
336 /*
337   Set left or right bound to IT.
338
339   Warning: caller should ensure that subsequent calls put in ITems
340   that are left-to-right ordered.
341 */
342 void
343 add_bound_item (Spanner *sp, Grob *it)
344 {
345   if (!sp->get_bound (LEFT))
346     sp->set_bound (LEFT, it);
347   else
348     sp->set_bound (RIGHT, it);
349 }
350
351 MAKE_SCHEME_CALLBACK (Spanner, set_spacing_rods, 1);
352 SCM
353 Spanner::set_spacing_rods (SCM smob)
354 {
355   Grob *me = unsmob_grob (smob);
356   SCM num_length = me->get_property ("minimum-length");
357   if (scm_is_number (num_length))
358     {
359       Rod r;
360       Spanner *sp = dynamic_cast<Spanner *> (me);
361       
362
363       System *root = get_root_system (me);
364       vector<Item*> cols (root->broken_col_range (sp->get_bound (LEFT)->get_column (),
365                                                   sp->get_bound (RIGHT)->get_column ()));
366
367       if (cols.size ())
368         {
369           Rod r ;
370           r.item_drul_[LEFT] = sp->get_bound (LEFT);
371           r.item_drul_[RIGHT] = cols[0]->find_prebroken_piece (LEFT);
372           r.distance_ = robust_scm2double (num_length, 0);
373           r.add_to_cols ();
374           
375           r.item_drul_[LEFT] = cols.back ()->find_prebroken_piece (RIGHT);
376           r.item_drul_[RIGHT] = sp->get_bound (RIGHT);
377           r.add_to_cols ();
378         }
379            
380       r.distance_ = robust_scm2double (num_length, 0);
381       r.item_drul_[LEFT] = sp->get_bound (LEFT);
382       r.item_drul_[RIGHT] = sp->get_bound (RIGHT);
383       r.add_to_cols ();
384     }
385   
386   return SCM_UNSPECIFIED;
387 }
388
389 /*
390   Return I such that SP == SP->ORIGINAL ()->BROKEN_INTOS_[I].
391 */
392 int
393 broken_spanner_index (Spanner const *sp)
394 {
395   Spanner *parent = dynamic_cast<Spanner *> (sp->original ());
396   /* ugh: casting */
397   return find (parent->broken_intos_, (Spanner*) sp) - parent->broken_intos_.begin ();
398 }
399
400 Spanner *
401 unsmob_spanner (SCM s)
402 {
403   return dynamic_cast<Spanner *> (unsmob_grob (s));
404 }
405
406 MAKE_SCHEME_CALLBACK (Spanner, bounds_width, 1);
407 SCM
408 Spanner::bounds_width (SCM grob)
409 {
410   Spanner *me = unsmob_spanner (grob);
411
412
413   Grob *common = me->get_bound (LEFT)->common_refpoint (me->get_bound (RIGHT), X_AXIS);
414
415   Interval w (me->get_bound (LEFT)->relative_coordinate (common, X_AXIS),
416               me->get_bound (RIGHT)->relative_coordinate (common, X_AXIS));
417               
418   w -= me->relative_coordinate (common, X_AXIS);
419
420   return ly_interval2scm (w);
421 }
422
423 ADD_INTERFACE (Spanner,
424                "Some objects are horizontally spanned between objects. For\n"
425                "example, slur, beam, tie, etc. These grobs form a subtype called\n"
426                "@code{Spanner}. All spanners have two span-points (these must be\n"
427                "@code{Item} objects), one on the left and one on the right. The left bound is\n"
428                "also the X-reference point of the spanner.\n",
429
430                "minimum-length "
431                );
432