]> git.donarmstrong.com Git - lilypond.git/blob - lily/spanner.cc
* input/mutopia/J.S.Bach/baerenreiter-sarabande.ly: Warn when not
[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--2002 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 */
8 #include <math.h>
9 #include <libc-extension.hh>
10
11
12 #include "warn.hh"
13 #include "spanner.hh"
14 #include "paper-column.hh"
15 #include "paper-score.hh"
16 #include "molecule.hh"
17 #include "paper-outputter.hh"
18 #include "paper-column.hh"
19 #include "system.hh"
20 #include "group-interface.hh"
21
22
23 MAKE_SCHEME_CALLBACK (Spanner, get_broken_into, 1);
24 SCM
25 Spanner::get_broken_into (SCM smob)
26 {
27   if (Spanner *me = dynamic_cast<Spanner*> (unsmob_grob (smob)))
28 #if 0    
29     return ly_grob_array2scm (me->broken_into_l_arr_);
30 #else
31   {
32       SCM s = SCM_EOL;
33       for (int i = me->broken_into_l_arr_.size (); i; i--)
34         s = gh_cons (me->broken_into_l_arr_[i-1]->self_scm (), s);
35       return s;
36   }
37 #endif
38   
39   return SCM_EOL;
40 }
41
42 void
43 Spanner::do_break_processing ()
44 {
45   //break_into_pieces
46   Item * left = spanned_drul_[LEFT];
47   Item * right = spanned_drul_[RIGHT];
48
49   if (!left || !right)
50     return;
51   
52   /*
53     Check if our parent in X-direction spans equally wide
54     or wider than we do.
55    */
56   for (int a = X_AXIS; a < NO_AXES; a ++)
57     {
58       if (Spanner* parent = dynamic_cast<Spanner*> (get_parent ((Axis)a)))
59         {
60           if (!parent->spanned_rank_iv ().contains_b (this->spanned_rank_iv ()))
61             {
62               programming_error (to_str ("Spanner `%s' is not fully contained in parent spanner `%s'.",
63                                          name ().ch_C (),
64                                          parent->name ().ch_C ()));
65             }
66         }
67     }
68   
69   if (line_l () || broken_b ())
70     return;
71
72   if (left == right)
73     {
74       /*
75         If we have a spanner spanning one column, we must break it
76         anyway because it might provide a parent for another item.  */
77       Direction d = LEFT;
78       do
79         {
80           Item* bound = left->find_prebroken_piece (d);
81           if (!bound)
82             programming_error ("no broken bound");
83           else if (bound->line_l ())
84             {
85               Spanner * span_p = dynamic_cast<Spanner*> (clone ());
86               span_p->set_bound (LEFT, bound);
87               span_p->set_bound (RIGHT, bound);
88
89               assert (span_p->line_l ()); 
90               span_p->line_l ()->typeset_grob (span_p);
91               broken_into_l_arr_.push (span_p);
92             }
93         }
94       while ((flip (&d))!= LEFT);
95     }
96   else
97     {
98       Link_array<Item> break_points = pscore_l_->line_l_->broken_col_range (left,right);
99
100       break_points.insert (left,0);
101       break_points.push (right);
102
103       for (int i=1; i < break_points.size (); i++) 
104         {
105           Drul_array<Item*> bounds;
106           bounds[LEFT] = break_points[i-1];
107           bounds[RIGHT] = break_points[i];
108           Direction d = LEFT;
109           do
110             {
111               if (!bounds[d]->line_l ())
112                 bounds[d] = bounds[d]->find_prebroken_piece (- d);
113             }
114           while ((flip (&d))!= LEFT);
115
116           if (!bounds[LEFT] ||  ! bounds[RIGHT])
117             {
118               programming_error ("bounds of this piece aren't breakable. ");
119               continue; 
120             }
121
122           Spanner *span_p = dynamic_cast<Spanner*> (clone ());
123           span_p->set_bound (LEFT,bounds[LEFT]);
124           span_p->set_bound (RIGHT,bounds[RIGHT]);
125
126           if (!bounds[LEFT]->line_l () 
127             
128               || !bounds[RIGHT]->line_l ()
129               || bounds[LEFT]->line_l () != bounds[RIGHT]->line_l ())
130             {
131               programming_error ("bounds of spanner are invalid");
132               span_p->suicide ();
133             }
134           else
135             {
136               bounds[LEFT]->line_l ()->typeset_grob (span_p);
137               broken_into_l_arr_.push (span_p);
138             }
139         }
140     }
141   broken_into_l_arr_.sort (Spanner::compare);
142 }
143
144 void
145 Spanner::set_my_columns ()
146 {
147   Direction i = (Direction) LEFT;
148   do 
149     {
150       if (!spanned_drul_[i]->line_l ())
151         set_bound (i,spanned_drul_[i]->find_prebroken_piece ((Direction) -i));
152     } 
153   while (flip (&i) != LEFT);
154 }       
155
156 Interval_t<int>
157 Spanner::spanned_rank_iv ()
158 {
159   Interval_t<int> iv (0, 0);
160
161   if (spanned_drul_[LEFT])
162     {
163       iv[LEFT] = Paper_column::rank_i (spanned_drul_[LEFT]->column_l ());
164     }
165   if (spanned_drul_[RIGHT])
166     {
167       iv[RIGHT] = Paper_column::rank_i (spanned_drul_[RIGHT]->column_l ());
168     }
169   return iv;
170 }
171
172 Item*
173 Spanner::get_bound (Direction d) const
174 {
175   return spanned_drul_ [d];
176 }
177
178 /*
179   Set the items that this spanner spans. If D == LEFT, we also set the
180   X-axis parent of THIS to S.
181 */
182 void
183 Spanner::set_bound (Direction d, Grob*s)
184 {
185   Item * i = dynamic_cast<Item*> (s);
186   if (!i)
187     {
188       programming_error ("Must have Item for spanner bound.");
189       return;
190     }
191   
192   spanned_drul_[d] =i;
193
194   /**
195      We check for System to prevent the column -> line_of_score
196      -> column -> line_of_score -> etc situation */
197   if (d== LEFT && !dynamic_cast<System*> (this))
198     {
199       set_parent (i, X_AXIS);
200     }
201
202   /*
203     Signal that this column needs to be kept alive. They need to be
204     kept alive to have meaningful position and linebreaking.
205
206     [maybe we should try keeping all columns alive?, and perhaps
207     inherit position from their (non-)musical brother]
208     
209   */
210   if (dynamic_cast<Paper_column*> (i))
211     {
212       Pointer_group_interface::add_grob (i, ly_symbol2scm ("bounded-by-me"), this);  
213     }
214 }
215
216 Spanner::Spanner (SCM s)
217   : Grob (s)
218 {
219   spanned_drul_[LEFT]=0;
220   spanned_drul_[RIGHT]=0;
221   Group_interface::add_thing (this, ly_symbol2scm ("interfaces"), ly_symbol2scm ("spanner-interface"));
222                      
223   
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::line_l () const
246 {
247   if (!spanned_drul_[LEFT] || !spanned_drul_[RIGHT])
248     return 0;
249   if (spanned_drul_[LEFT]->line_l () != spanned_drul_[RIGHT]->line_l ())
250     return 0;
251   return spanned_drul_[LEFT]->line_l ();
252 }
253
254
255 Grob*
256 Spanner::find_broken_piece (System*l) const
257 {
258   int idx = binsearch_link_array (broken_into_l_arr_, (Spanner*)l, Spanner::compare);
259   
260   if (idx < 0)
261     return 0;
262   else
263     return broken_into_l_arr_ [idx];
264 }
265
266
267 int
268 Spanner::compare (Spanner * const &p1, Spanner * const &p2)
269 {
270   return  p1->line_l ()->rank_i_ - p2->line_l ()->rank_i_;
271 }
272
273 bool
274 Spanner::broken_b () const
275 {
276   return broken_into_l_arr_.size ();
277 }
278
279
280 /*
281   If this is a broken spanner, return the amount the left end is to be
282   shifted horizontally so that the spanner starts after the initial
283   clef and key on the staves. This is necessary for ties, slurs,
284   crescendo and decrescendo signs, for example.
285 */
286 Real
287 Spanner::get_broken_left_end_align () const
288 {
289   Paper_column *sc = dynamic_cast<Paper_column*> (spanned_drul_[LEFT]->column_l ());
290
291   // Relevant only if left span point is first column in line
292   if (sc != NULL &&
293      sc->break_status_dir () == RIGHT)
294     {
295       /*
296         
297         We used to do a full search for the Break_align_item.
298         But that doesn't make a difference, since the Paper_column
299         is likely to contain only a Break_align_item.
300       */
301       return sc->extent (sc, X_AXIS)[RIGHT];
302     }
303
304   return 0.0;
305 }
306
307 SCM
308 Spanner::do_derived_mark ()
309 {
310   /*
311     We'd be fucked if this is called before spanned_drul_[] is inited.  */
312   if (status_c_ == ORPHAN)
313     return SCM_EOL;
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   for (int i= broken_into_l_arr_.size () ; i--;)
322     scm_gc_mark (broken_into_l_arr_[i]->self_scm ());
323
324   return SCM_EOL;
325 }
326
327
328 /*
329   Set left or right bound to IT.
330
331   Warning: caller should ensure that subsequent calls put in ITems
332   that are left-to-right ordered.
333  */
334 void
335 add_bound_item (Spanner* sp, Grob*it)
336 {
337   if (!sp->get_bound (LEFT))
338     sp->set_bound (LEFT, it);
339   else
340     sp->set_bound (RIGHT, it);
341 }
342
343
344 MAKE_SCHEME_CALLBACK (Spanner,set_spacing_rods,1);
345 SCM
346 Spanner::set_spacing_rods (SCM smob)
347 {
348   Grob*me = unsmob_grob (smob);
349
350   Rod r;
351   Spanner*sp = dynamic_cast<Spanner*> (me);
352   r.item_l_drul_[LEFT] = sp->get_bound (LEFT);
353   r.item_l_drul_[RIGHT] = sp->get_bound (RIGHT);
354   r.distance_f_ =
355     gh_scm2double (me->get_grob_property ("minimum-length"))
356     * 1.0;
357
358   r.add_to_cols ();
359   return SCM_UNSPECIFIED;
360 }
361
362
363 Spanner*
364 unsmob_spanner (SCM s )
365 {
366   return dynamic_cast<Spanner*> (unsmob_grob (s));
367 }
368
369 ADD_INTERFACE(Spanner,
370               "spanner-interface",
371               "
372 Other grobs have a shape that depends on the horizontal spacing. For
373 example, slur, beam, tie, etc. These grobs form a subtype called
374 @code{Spanner}. All spanners have two span-points (these must be
375 @code{Item}s), one on the left and one on the right. The left bound is
376 also the X-reference point of the spanner.
377 ",
378               "minimum-length");