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