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