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