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