]> git.donarmstrong.com Git - lilypond.git/blob - lily/spanner.cc
Run `make grand-replace'.
[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--2008 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 of " + name());
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       System *root = get_root_system (me);
362       Drul_array<Item*> bounds (sp->get_bound (LEFT),
363                                 sp->get_bound (RIGHT));
364       if (!bounds[LEFT] || !bounds[RIGHT])
365         return SCM_UNSPECIFIED;
366       
367       vector<Item*> cols (root->broken_col_range (bounds[LEFT]->get_column (),
368                                                   bounds[RIGHT]->get_column ()));
369
370       if (cols.size ())
371         {
372           Rod r ;
373           r.item_drul_[LEFT] = sp->get_bound (LEFT);
374           r.item_drul_[RIGHT] = cols[0]->find_prebroken_piece (LEFT);
375           r.distance_ = robust_scm2double (num_length, 0);
376           r.add_to_cols ();
377           
378           r.item_drul_[LEFT] = cols.back ()->find_prebroken_piece (RIGHT);
379           r.item_drul_[RIGHT] = sp->get_bound (RIGHT);
380           r.add_to_cols ();
381         }
382            
383       r.distance_ = robust_scm2double (num_length, 0);
384       r.item_drul_[LEFT] = sp->get_bound (LEFT);
385       r.item_drul_[RIGHT] = sp->get_bound (RIGHT);
386       r.add_to_cols ();
387     }
388   
389   return SCM_UNSPECIFIED;
390 }
391
392 /*
393   Return I such that SP == SP->ORIGINAL ()->BROKEN_INTOS_[I].
394 */
395 int
396 broken_spanner_index (Spanner const *sp)
397 {
398   Spanner *parent = dynamic_cast<Spanner *> (sp->original ());
399   /* ugh: casting */
400   return find (parent->broken_intos_, (Spanner*) sp) - parent->broken_intos_.begin ();
401 }
402
403 Spanner *
404 unsmob_spanner (SCM s)
405 {
406   return dynamic_cast<Spanner *> (unsmob_grob (s));
407 }
408
409 MAKE_SCHEME_CALLBACK (Spanner, bounds_width, 1);
410 SCM
411 Spanner::bounds_width (SCM grob)
412 {
413   Spanner *me = unsmob_spanner (grob);
414
415
416   Grob *common = me->get_bound (LEFT)->common_refpoint (me->get_bound (RIGHT), X_AXIS);
417
418   Interval w (me->get_bound (LEFT)->relative_coordinate (common, X_AXIS),
419               me->get_bound (RIGHT)->relative_coordinate (common, X_AXIS));
420               
421   w -= me->relative_coordinate (common, X_AXIS);
422
423   return ly_interval2scm (w);
424 }
425
426 ADD_INTERFACE (Spanner,
427                "Some objects are horizontally spanned between objects.  For"
428                " example, slurs, beams, ties, etc.  These grobs form a subtype"
429                " called @code{Spanner}.  All spanners have two span points"
430                " (these must be @code{Item} objects), one on the left and one"
431                " on the right.  The left bound is also the X@tie{}reference"
432                " point of the spanner.",
433
434                /* properties */
435                "minimum-length "
436                "to-barline "
437                );
438