]> git.donarmstrong.com Git - lilypond.git/blob - lily/spanner.cc
make spanned_rank_iv() const; add Spanner::spanned_time() method
[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     Check if our parent in X-direction spans equally wide
37     or wider than we do.
38   */
39   for (int a = X_AXIS; a < NO_AXES; a++)
40     {
41       if (Spanner *parent = dynamic_cast<Spanner *> (get_parent ((Axis)a)))
42         {
43           if (!parent->spanned_rank_iv ().superset (this->spanned_rank_iv ()))
44             {
45               programming_error (to_string ("Spanner `%s' is not fully contained in parent spanner `%s'.",
46                                             name ().c_str (),
47                                             parent->name ().c_str ()));
48             }
49         }
50     }
51
52   if (get_system () || is_broken ())
53     return;
54
55   if (left == right)
56     {
57       /*
58         If we have a spanner spanning one column, we must break it
59         anyway because it might provide a parent for another item.  */
60       Direction d = LEFT;
61       do
62         {
63           Item *bound = left->find_prebroken_piece (d);
64           if (!bound)
65             programming_error ("no broken bound");
66           else if (bound->get_system ())
67             {
68               Spanner *span = dynamic_cast<Spanner *> (clone ());
69               span->set_bound (LEFT, bound);
70               span->set_bound (RIGHT, bound);
71
72               assert (span->get_system ());
73               span->get_system ()->typeset_grob (span);
74               broken_intos_.push_back (span);
75             }
76         }
77       while ((flip (&d)) != LEFT);
78     }
79   else
80     {
81       System *root  = get_root_system (this);
82       vector<Item*> break_points = root->broken_col_range (left, right);
83
84       break_points.insert (break_points.begin () + 0, left);
85       break_points.push_back (right);
86
87       for (vsize i = 1; i < break_points.size (); i++)
88         {
89           Drul_array<Item *> bounds;
90           bounds[LEFT] = break_points[i - 1];
91           bounds[RIGHT] = break_points[i];
92           Direction d = LEFT;
93           do
94             {
95               if (!bounds[d]->get_system ())
96                 bounds[d] = bounds[d]->find_prebroken_piece (- d);
97             }
98           while ((flip (&d)) != LEFT);
99
100           if (!bounds[LEFT] || ! bounds[RIGHT])
101             {
102               programming_error ("bounds of this piece aren't breakable. ");
103               continue;
104             }
105
106           Spanner *span = dynamic_cast<Spanner *> (clone ());
107           span->set_bound (LEFT, bounds[LEFT]);
108           span->set_bound (RIGHT, bounds[RIGHT]);
109
110           if (!bounds[LEFT]->get_system ()
111
112               || !bounds[RIGHT]->get_system ()
113               || bounds[LEFT]->get_system () != bounds[RIGHT]->get_system ())
114             {
115               programming_error ("bounds of spanner are invalid");
116               span->suicide ();
117             }
118           else
119             {
120               bounds[LEFT]->get_system ()->typeset_grob (span);
121               broken_intos_.push_back (span);
122             }
123         }
124     }
125   vector_sort (broken_intos_, Spanner::less);
126   for (vsize i = broken_intos_.size (); i--;)
127     broken_intos_[i]->break_index_ = i;
128 }
129
130 vsize
131 Spanner::get_break_index () const
132 {
133   return break_index_;
134 }
135
136 void
137 Spanner::set_my_columns ()
138 {
139   Direction i = (Direction) LEFT;
140   do
141     {
142       if (!spanned_drul_[i]->get_system ())
143         set_bound (i, spanned_drul_[i]->find_prebroken_piece ((Direction) -i));
144     }
145   while (flip (&i) != LEFT);
146 }
147
148 Interval_t<int>
149 Spanner::spanned_rank_iv () const
150 {
151   Interval_t<int> iv (0, 0);
152
153   if (spanned_drul_[LEFT] && spanned_drul_[LEFT]->get_column ())
154     iv[LEFT] = spanned_drul_[LEFT]->get_column ()->get_rank ();
155   if (spanned_drul_[RIGHT] && spanned_drul_[RIGHT]->get_column ())
156     iv[RIGHT] = spanned_drul_[RIGHT]->get_column ()->get_rank ();
157   return iv;
158 }
159
160 Interval_t<Moment>
161 Spanner::spanned_time () const
162 {
163   Interval_t<Moment> iv;
164
165   Direction d = LEFT;
166   do
167     {
168       if (spanned_drul_[d] && spanned_drul_[d]->get_column ())
169         iv[d] = robust_scm2moment (spanned_drul_[d]->get_column ()->get_property ("when"),
170                                   iv[d]);
171     }
172   while (flip (&d) != LEFT);
173
174   do
175     {
176       if (!spanned_drul_[d] || !spanned_drul_[d]->get_column ())
177         iv[d] = iv[-d];
178     }
179   while (flip (&d) != LEFT);
180   
181   
182   return iv;
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");
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   */
221   if (dynamic_cast<Paper_column *> (i))
222     Pointer_group_interface::add_grob (i, ly_symbol2scm ("bounded-by-me"), this);
223 }
224
225 Spanner::Spanner (SCM s)
226   : Grob (s)
227 {
228   break_index_ = 0;
229   spanned_drul_[LEFT] = 0;
230   spanned_drul_[RIGHT] = 0;
231 }
232
233 Spanner::Spanner (Spanner const &s)
234   : Grob (s)
235 {
236   spanned_drul_[LEFT] = spanned_drul_[RIGHT] = 0;
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 = broken_spanner_index (this);
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   Direction d = LEFT;
333   do
334     if (spanned_drul_[d])
335       scm_gc_mark (spanned_drul_[d]->self_scm ());
336   while (flip (&d) != LEFT)
337     ;
338
339   for (vsize i = broken_intos_.size (); i--;)
340     scm_gc_mark (broken_intos_[i]->self_scm ());
341 }
342
343 /*
344   Set left or right bound to IT.
345
346   Warning: caller should ensure that subsequent calls put in ITems
347   that are left-to-right ordered.
348 */
349 void
350 add_bound_item (Spanner *sp, Grob *it)
351 {
352   if (!sp->get_bound (LEFT))
353     sp->set_bound (LEFT, it);
354   else
355     sp->set_bound (RIGHT, it);
356 }
357
358 MAKE_SCHEME_CALLBACK (Spanner, set_spacing_rods, 1);
359 SCM
360 Spanner::set_spacing_rods (SCM smob)
361 {
362   Grob *me = unsmob_grob (smob);
363   SCM num_length = me->get_property ("minimum-length");
364   if (scm_is_number (num_length))
365     {
366       Rod r;
367       Spanner *sp = dynamic_cast<Spanner *> (me);
368       
369
370       System *root = get_root_system (me);
371       vector<Item*> cols (root->broken_col_range (sp->get_bound (LEFT)->get_column (),
372                                                   sp->get_bound (RIGHT)->get_column ()));
373
374       if (cols.size ())
375         {
376           Rod r ;
377           r.item_drul_[LEFT] = sp->get_bound (LEFT);
378           r.item_drul_[RIGHT] = cols[0]->find_prebroken_piece (LEFT);
379           r.distance_ = robust_scm2double (num_length, 0);
380           r.add_to_cols ();
381           
382           r.item_drul_[LEFT] = cols.back ()->find_prebroken_piece (RIGHT);
383           r.item_drul_[RIGHT] = sp->get_bound (RIGHT);
384           r.add_to_cols ();
385         }
386            
387       r.distance_ = robust_scm2double (num_length, 0);
388       r.item_drul_[LEFT] = sp->get_bound (LEFT);
389       r.item_drul_[RIGHT] = sp->get_bound (RIGHT);
390       r.add_to_cols ();
391     }
392   
393   return SCM_UNSPECIFIED;
394 }
395
396 /*
397   Return I such that SP == SP->ORIGINAL ()->BROKEN_INTOS_[I].
398 */
399 int
400 broken_spanner_index (Spanner const *sp)
401 {
402   Spanner *parent = dynamic_cast<Spanner *> (sp->original ());
403   /* ugh: casting */
404   return find (parent->broken_intos_, (Spanner*) sp) - parent->broken_intos_.begin ();
405 }
406
407 Spanner *
408 unsmob_spanner (SCM s)
409 {
410   return dynamic_cast<Spanner *> (unsmob_grob (s));
411 }
412
413 MAKE_SCHEME_CALLBACK(Spanner, bounds_width, 1);
414 SCM
415 Spanner::bounds_width (SCM grob)
416 {
417   Spanner *me = unsmob_spanner (grob);
418
419
420   Grob *common = me->get_bound (LEFT)->common_refpoint (me->get_bound (RIGHT), X_AXIS);
421
422   Interval w (me->get_bound (LEFT)->relative_coordinate (common, X_AXIS),
423               me->get_bound (RIGHT)->relative_coordinate (common, X_AXIS));
424               
425   w -= me->relative_coordinate (common, X_AXIS);
426
427   return ly_interval2scm (w);
428 }
429
430 ADD_INTERFACE (Spanner,
431                "Some objects are horizontally spanned between objects. For\n"
432                "example, slur, beam, tie, etc. These grobs form a subtype called\n"
433                "@code{Spanner}. All spanners have two span-points (these must be\n"
434                "@code{Item} objects), one on the left and one on the right. The left bound is\n"
435                "also the X-reference point of the spanner.\n",
436
437                "minimum-length "
438                );
439