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