]> git.donarmstrong.com Git - lilypond.git/blob - lily/spanner.cc
rename spanned_rank_iv -> spanned_rank_interval
[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_interval ().superset (this->spanned_rank_interval ()))
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_interval () 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   return spanned_time_interval (spanned_drul_[LEFT],
164                                 spanned_drul_[RIGHT]);
165 }
166
167
168 Item *
169 Spanner::get_bound (Direction d) const
170 {
171   return spanned_drul_ [d];
172 }
173
174 /*
175   Set the items that this spanner spans. If D == LEFT, we also set the
176   X-axis parent of THIS to S.
177 */
178 void
179 Spanner::set_bound (Direction d, Grob *s)
180 {
181   Item *i = dynamic_cast<Item *> (s);
182   if (!i)
183     {
184       programming_error ("must have Item for spanner bound");
185       return;
186     }
187
188   spanned_drul_[d] = i;
189
190   /**
191      We check for System to prevent the column -> line_of_score
192      -> column -> line_of_score -> etc situation */
193   if (d == LEFT && !dynamic_cast<System *> (this))
194     set_parent (i, X_AXIS);
195
196   /*
197     Signal that this column needs to be kept alive. They need to be
198     kept alive to have meaningful position and linebreaking.
199
200     [maybe we should try keeping all columns alive?, and perhaps
201     inherit position from their (non-)musical brother]
202
203   */
204   if (dynamic_cast<Paper_column *> (i))
205     Pointer_group_interface::add_grob (i, ly_symbol2scm ("bounded-by-me"), this);
206 }
207
208 Spanner::Spanner (SCM s)
209   : Grob (s)
210 {
211   break_index_ = 0;
212   spanned_drul_[LEFT] = 0;
213   spanned_drul_[RIGHT] = 0;
214 }
215
216 Spanner::Spanner (Spanner const &s)
217   : Grob (s)
218 {
219   spanned_drul_[LEFT] = spanned_drul_[RIGHT] = 0;
220 }
221
222 Real
223 Spanner::spanner_length () const
224 {
225   Real l = spanned_drul_[LEFT]->relative_coordinate (0, X_AXIS);
226   Real r = spanned_drul_[RIGHT]->relative_coordinate (0, X_AXIS);
227
228   if (r < l)
229     programming_error ("spanner with negative length");
230
231   return r - l;
232 }
233
234 System *
235 Spanner::get_system () const
236 {
237   if (!spanned_drul_[LEFT] || !spanned_drul_[RIGHT])
238     return 0;
239   if (spanned_drul_[LEFT]->get_system () != spanned_drul_[RIGHT]->get_system ())
240     return 0;
241   return spanned_drul_[LEFT]->get_system ();
242 }
243
244 Grob *
245 Spanner::find_broken_piece (System *l) const
246 {
247   vsize idx = binary_search (broken_intos_, (Spanner *)l, Spanner::less);
248   if (idx != VPOS)
249     return broken_intos_ [idx];
250   return 0;
251 }
252
253 Spanner *
254 Spanner::broken_neighbor (Direction d) const
255 {
256   if (!original_)
257     return 0;
258
259   vsize k = broken_spanner_index (this);
260   Spanner *orig = dynamic_cast<Spanner*> (original_);
261   int j = int (k) + d;
262   if (j < 0 || vsize (j) >= orig->broken_intos_.size ())
263     return 0;
264
265   return orig->broken_intos_[j];
266 }
267
268 int
269 Spanner::compare (Spanner *const &p1, Spanner *const &p2)
270 {
271   return p1->get_system ()->get_rank () - p2->get_system ()->get_rank ();
272 }
273
274 bool
275 Spanner::less (Spanner *const &a, Spanner *const &b)
276 {
277   return a->get_system ()->get_rank () < b->get_system ()->get_rank ();
278 }
279
280 bool
281 Spanner::is_broken () const
282 {
283   return broken_intos_.size ();
284 }
285
286 /*
287   If this is a broken spanner, return the amount the left end is to be
288   shifted horizontally so that the spanner starts after the initial
289   clef and key on the staves. This is necessary for ties, slurs,
290   crescendo and decrescendo signs, for example.
291 */
292 Real
293 Spanner::get_broken_left_end_align () const
294 {
295   Paper_column *sc = dynamic_cast<Paper_column *> (spanned_drul_[LEFT]->get_column ());
296
297   // Relevant only if left span point is first column in line
298   if (sc != NULL
299       && sc->break_status_dir () == RIGHT)
300     {
301       /*
302         We used to do a full search for the Break_align_item.
303         But that doesn't make a difference, since the Paper_column
304         is likely to contain only a Break_align_item.
305       */
306       return sc->extent (sc, X_AXIS)[RIGHT];
307     }
308
309   return 0.0;
310 }
311
312 void
313 Spanner::derived_mark () const
314 {
315   Direction d = LEFT;
316   do
317     if (spanned_drul_[d])
318       scm_gc_mark (spanned_drul_[d]->self_scm ());
319   while (flip (&d) != LEFT)
320     ;
321
322   for (vsize i = broken_intos_.size (); i--;)
323     scm_gc_mark (broken_intos_[i]->self_scm ());
324 }
325
326 /*
327   Set left or right bound to IT.
328
329   Warning: caller should ensure that subsequent calls put in ITems
330   that are left-to-right ordered.
331 */
332 void
333 add_bound_item (Spanner *sp, Grob *it)
334 {
335   if (!sp->get_bound (LEFT))
336     sp->set_bound (LEFT, it);
337   else
338     sp->set_bound (RIGHT, it);
339 }
340
341 MAKE_SCHEME_CALLBACK (Spanner, set_spacing_rods, 1);
342 SCM
343 Spanner::set_spacing_rods (SCM smob)
344 {
345   Grob *me = unsmob_grob (smob);
346   SCM num_length = me->get_property ("minimum-length");
347   if (scm_is_number (num_length))
348     {
349       Rod r;
350       Spanner *sp = dynamic_cast<Spanner *> (me);
351       
352
353       System *root = get_root_system (me);
354       vector<Item*> cols (root->broken_col_range (sp->get_bound (LEFT)->get_column (),
355                                                   sp->get_bound (RIGHT)->get_column ()));
356
357       if (cols.size ())
358         {
359           Rod r ;
360           r.item_drul_[LEFT] = sp->get_bound (LEFT);
361           r.item_drul_[RIGHT] = cols[0]->find_prebroken_piece (LEFT);
362           r.distance_ = robust_scm2double (num_length, 0);
363           r.add_to_cols ();
364           
365           r.item_drul_[LEFT] = cols.back ()->find_prebroken_piece (RIGHT);
366           r.item_drul_[RIGHT] = sp->get_bound (RIGHT);
367           r.add_to_cols ();
368         }
369            
370       r.distance_ = robust_scm2double (num_length, 0);
371       r.item_drul_[LEFT] = sp->get_bound (LEFT);
372       r.item_drul_[RIGHT] = sp->get_bound (RIGHT);
373       r.add_to_cols ();
374     }
375   
376   return SCM_UNSPECIFIED;
377 }
378
379 /*
380   Return I such that SP == SP->ORIGINAL ()->BROKEN_INTOS_[I].
381 */
382 int
383 broken_spanner_index (Spanner const *sp)
384 {
385   Spanner *parent = dynamic_cast<Spanner *> (sp->original ());
386   /* ugh: casting */
387   return find (parent->broken_intos_, (Spanner*) sp) - parent->broken_intos_.begin ();
388 }
389
390 Spanner *
391 unsmob_spanner (SCM s)
392 {
393   return dynamic_cast<Spanner *> (unsmob_grob (s));
394 }
395
396 MAKE_SCHEME_CALLBACK (Spanner, bounds_width, 1);
397 SCM
398 Spanner::bounds_width (SCM grob)
399 {
400   Spanner *me = unsmob_spanner (grob);
401
402
403   Grob *common = me->get_bound (LEFT)->common_refpoint (me->get_bound (RIGHT), X_AXIS);
404
405   Interval w (me->get_bound (LEFT)->relative_coordinate (common, X_AXIS),
406               me->get_bound (RIGHT)->relative_coordinate (common, X_AXIS));
407               
408   w -= me->relative_coordinate (common, X_AXIS);
409
410   return ly_interval2scm (w);
411 }
412
413 ADD_INTERFACE (Spanner,
414                "Some objects are horizontally spanned between objects. For\n"
415                "example, slur, beam, tie, etc. These grobs form a subtype called\n"
416                "@code{Spanner}. All spanners have two span-points (these must be\n"
417                "@code{Item} objects), one on the left and one on the right. The left bound is\n"
418                "also the X-reference point of the spanner.\n",
419
420                "minimum-length "
421                );
422