]> git.donarmstrong.com Git - lilypond.git/blob - lily/spanner.cc
release: 1.5.47
[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 "debug.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 "line-of-score.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 ().contains_b (this->spanned_rank_iv ()))
41             {
42               programming_error (to_str ("Spanner `%s' is not fully contained in parent spanner `%s'.",
43                                          name ().ch_C (),
44                                          parent->name ().ch_C ()));
45             }
46         }
47     }
48   
49   if (line_l () || broken_b ())
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->line_l ())
64             {
65               Spanner * span_p = dynamic_cast<Spanner*> (clone ());
66               span_p->set_bound (LEFT, bound);
67               span_p->set_bound (RIGHT, bound);
68
69               assert (span_p->line_l ()); 
70               span_p->line_l ()->typeset_grob (span_p);
71               broken_into_l_arr_.push (span_p);
72             }
73         }
74       while ((flip (&d))!= LEFT);
75     }
76   else
77     {
78       Link_array<Item> break_points = pscore_l_->line_l_->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]->line_l ())
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_p = dynamic_cast<Spanner*> (clone ());
103           span_p->set_bound (LEFT,bounds[LEFT]);
104           span_p->set_bound (RIGHT,bounds[RIGHT]);
105
106           if (!bounds[LEFT]->line_l () 
107             
108               || !bounds[RIGHT]->line_l ()
109               || bounds[LEFT]->line_l () != bounds[RIGHT]->line_l ())
110             {
111               programming_error ("bounds of spanner are invalid");
112               span_p->suicide ();
113             }
114           else
115             {
116               bounds[LEFT]->line_l ()->typeset_grob (span_p);
117               broken_into_l_arr_.push (span_p);
118             }
119         }
120     }
121   broken_into_l_arr_.sort (Spanner::compare);
122 }
123
124 void
125 Spanner::set_my_columns ()
126 {
127   Direction i = (Direction) LEFT;
128   do 
129     {
130       if (!spanned_drul_[i]->line_l ())
131         set_bound (i,spanned_drul_[i]->find_prebroken_piece ((Direction) -i));
132     } 
133   while (flip (&i) != LEFT);
134 }       
135
136 Interval_t<int>
137 Spanner::spanned_rank_iv ()
138 {
139   Interval_t<int> iv (0, 0);
140
141   if (spanned_drul_[LEFT])
142     {
143       iv[LEFT] = Paper_column::rank_i (spanned_drul_[LEFT]->column_l ());
144     }
145   if (spanned_drul_[RIGHT])
146     {
147       iv[RIGHT] = Paper_column::rank_i (spanned_drul_[RIGHT]->column_l ());
148     }
149   return iv;
150 }
151
152 Item*
153 Spanner::get_bound (Direction d) const
154 {
155   return spanned_drul_ [d];
156 }
157
158 /*
159   Set the items that this spanner spans. If D == LEFT, we also set the
160   X-axis parent of THIS to S.
161 */
162 void
163 Spanner::set_bound (Direction d, Grob*s)
164 {
165   Item * i = dynamic_cast<Item*> (s);
166   if (!i)
167     {
168       programming_error ("Must have Item for spanner bound.");
169       return;
170     }
171   
172   spanned_drul_[d] =i;
173
174   /**
175      We check for System to prevent the column -> line_of_score
176      -> column -> line_of_score -> etc situation */
177   if (d== LEFT && !dynamic_cast<System*> (this))
178     {
179       set_parent (i, X_AXIS);
180     }
181
182   /*
183     Signal that this column needs to be kept alive. They need to be
184     kept alive to have meaningful position and linebreaking.
185
186     [maybe we should try keeping all columns alive?, and perhaps
187     inherit position from their (non-)musical brother]
188     
189   */
190   if (dynamic_cast<Paper_column*> (i))
191     {
192       Pointer_group_interface::add_grob (i, ly_symbol2scm ("bounded-by-me"), this);  
193     }
194 }
195
196 Spanner::Spanner (SCM s)
197   : Grob (s)
198 {
199   spanned_drul_[LEFT]=0;
200   spanned_drul_[RIGHT]=0;
201   Group_interface::add_thing (this, ly_symbol2scm ("interfaces"), ly_symbol2scm ("spanner-interface"));
202                      
203   
204 }
205
206 Spanner::Spanner (Spanner const &s)
207   : Grob (s)
208 {
209   spanned_drul_[LEFT] = spanned_drul_[RIGHT] =0;
210 }
211
212 Real
213 Spanner::spanner_length () const
214 {  
215   Real l = spanned_drul_[LEFT]->relative_coordinate (0, X_AXIS);
216   Real r = spanned_drul_[RIGHT]->relative_coordinate (0, X_AXIS);
217
218   if (r< l)
219     programming_error ("spanner with negative length");
220
221   return r-l;
222 }
223
224 System *
225 Spanner::line_l () const
226 {
227   if (!spanned_drul_[LEFT] || !spanned_drul_[RIGHT])
228     return 0;
229   if (spanned_drul_[LEFT]->line_l () != spanned_drul_[RIGHT]->line_l ())
230     return 0;
231   return spanned_drul_[LEFT]->line_l ();
232 }
233
234
235 Grob*
236 Spanner::find_broken_piece (System*l) const
237 {
238   int idx = binsearch_link_array (broken_into_l_arr_, (Spanner*)l, Spanner::compare);
239   
240   if (idx < 0)
241     return 0;
242   else
243     return broken_into_l_arr_ [idx];
244 }
245
246
247 int
248 Spanner::compare (Spanner * const &p1, Spanner * const &p2)
249 {
250   return  p1->line_l ()->rank_i_ - p2->line_l ()->rank_i_;
251 }
252
253 bool
254 Spanner::broken_b () const
255 {
256   return broken_into_l_arr_.size ();
257 }
258
259
260 /*
261   If this is a broken spanner, return the amount the left end is to be
262   shifted horizontally so that the spanner starts after the initial
263   clef and key on the staves. This is necessary for ties, slurs,
264   crescendo and decrescendo signs, for example.
265 */
266 Real
267 Spanner::get_broken_left_end_align () const
268 {
269   Paper_column *sc = dynamic_cast<Paper_column*> (spanned_drul_[LEFT]->column_l ());
270
271   // Relevant only if left span point is first column in line
272   if (sc != NULL &&
273      sc->break_status_dir () == RIGHT)
274     {
275       /*
276         
277         We used to do a full search for the Break_align_item.
278         But that doesn't make a difference, since the Paper_column
279         is likely to contain only a Break_align_item.
280       */
281       return sc->extent (sc, X_AXIS)[RIGHT];
282     }
283
284   return 0.0;
285 }
286
287 SCM
288 Spanner::do_derived_mark ()
289 {
290   /*
291     We'd be fucked if this is called before spanned_drul_[] is inited.  */
292   if (status_c_ == ORPHAN)
293     return SCM_EOL;
294   
295   Direction d = LEFT;
296   do
297     if (spanned_drul_[d])
298       scm_gc_mark (spanned_drul_[d]->self_scm ());
299   while (flip (&d) != LEFT);
300
301   for (int i= broken_into_l_arr_.size () ; i--;)
302     scm_gc_mark (broken_into_l_arr_[i]->self_scm ());
303
304   return SCM_EOL;
305 }
306
307 void
308 add_bound_item (Spanner* sp, Item*it)
309 {
310   if (!sp->get_bound (LEFT))
311     sp->set_bound (LEFT, it);
312   else
313     sp->set_bound (RIGHT, it);
314 }
315
316 /*
317   Extends EXTREMAL_PAIR to include IT
318  */
319 static void
320 extend_spanner_over_item (Item *it, SCM extremal_pair)
321 {
322   if (!it)
323     return;
324   Item * col = it->column_l ();
325   Item * i1 = dynamic_cast<Item*> (unsmob_grob (ly_car (extremal_pair)));
326   Item * i2 = dynamic_cast<Item*> (unsmob_grob (ly_cdr (extremal_pair)));
327   int r = Paper_column::rank_i (col);
328   if (!i1 || r < Paper_column::rank_i (i1->column_l ()))
329     {
330       gh_set_car_x (extremal_pair, it->self_scm ());
331     }
332   if (!i2 || r > Paper_column::rank_i (i2->column_l ()))
333     {
334       gh_set_cdr_x (extremal_pair, it->self_scm ());
335     }
336 }
337
338 /*
339   Extends EXTREMAL_PAIR to include every grob in VALUE
340  */
341 static void
342 extend_spanner_over_elements (SCM value, SCM extremal_pair)
343 {
344   if (gh_pair_p (value))
345     {
346       extend_spanner_over_elements (ly_car (value), extremal_pair);
347       extend_spanner_over_elements (ly_cdr (value), extremal_pair);
348     }
349   else if (unsmob_grob (value))
350     {
351       if (Spanner * sp = dynamic_cast<Spanner*> (unsmob_grob (value)))
352         {
353           extend_spanner_over_item (sp->get_bound (LEFT), extremal_pair);
354           extend_spanner_over_item (sp->get_bound (RIGHT), extremal_pair);
355         }
356       else if (Item * it= dynamic_cast<Item*> (unsmob_grob (value)))
357         extend_spanner_over_item (it, extremal_pair);
358     }
359 }
360
361
362 /*
363   Make sure that the left and right bounds encompasses all objects it
364   points to.
365
366   TODO: maybe be more specific. Most probably fucks up if someone sets
367   a pointer to the staff symbol in S
368 */
369 void
370 extend_spanner_over_elements (Grob*s)
371 {
372   Spanner*sp = dynamic_cast<Spanner*> (s);
373
374   SCM s1 = sp->get_bound (LEFT) ? sp->get_bound (LEFT)->self_scm () : SCM_EOL;
375   SCM s2 = sp->get_bound (RIGHT) ? sp->get_bound (RIGHT)->self_scm () : SCM_EOL;  
376   
377   SCM pair = gh_cons (s1,s2);
378   extend_spanner_over_elements (sp->mutable_property_alist_, pair);
379
380   Grob *p1 =  unsmob_grob (ly_car (pair));
381   Grob* p2 = unsmob_grob (ly_cdr (pair));
382   sp->set_bound (LEFT,p1);
383   sp->set_bound (RIGHT, p2);
384 }
385
386
387 MAKE_SCHEME_CALLBACK (Spanner,set_spacing_rods,1);
388 SCM
389 Spanner::set_spacing_rods (SCM smob)
390 {
391   Grob*me = unsmob_grob (smob);
392
393   Rod r;
394   Spanner*sp = dynamic_cast<Spanner*> (me);
395   r.item_l_drul_[LEFT] = sp->get_bound (LEFT);
396   r.item_l_drul_[RIGHT] = sp->get_bound (RIGHT);
397   r.distance_f_ =
398     gh_scm2double (me->get_grob_property ("minimum-length"))
399     * 1.0;
400
401   r.add_to_cols ();
402   return SCM_UNSPECIFIED;
403 }
404
405
406 Spanner*
407 unsmob_spanner (SCM s )
408 {
409   return dynamic_cast<Spanner*> (unsmob_grob (s));
410 }
411
412 ADD_INTERFACE(Spanner,
413               "spanner-interface",
414               "",
415               "minimum-length");