]> git.donarmstrong.com Git - lilypond.git/blob - lily/spanner.cc
release: 1.3.84
[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--2000 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_element (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 0
108           assert (bounds[LEFT]->line_l () ==
109                   bounds[RIGHT]->line_l ());
110
111           bounds[LEFT]->line_l ()->typeset_element (span_p);
112           broken_into_l_arr_.push (span_p);
113 #else
114
115           if (bounds[LEFT]->line_l () != bounds[RIGHT]->line_l ())
116             {
117               programming_error ("bounds[LEFT]->line_l () != bounds[RIGHT]->line_l ()");
118 #if 0
119               /*
120                 lily crashes upon displaying this ...
121                 
122                */
123               
124               gh_display (ly_str02scm ("\nspanner:mutable_property_alist_\n"));
125               gh_display (mutable_property_alist_);
126               gh_display (ly_str02scm ("\nspanner:immutable_property_alist_\n"));
127               gh_display (immutable_property_alist_);
128               gh_newline ();
129 #endif
130               span_p->suicide ();
131             }
132           else
133             {
134               bounds[LEFT]->line_l ()->typeset_element (span_p);
135               broken_into_l_arr_.push (span_p);
136             }
137 #endif
138           
139         }
140     }
141   broken_into_l_arr_.sort (Spanner::compare);
142 }
143
144 void
145 Spanner::set_my_columns()
146 {
147   Direction i = (Direction) LEFT;
148   do 
149     {
150       if (!spanned_drul_[i]->line_l())
151         set_bound(i,spanned_drul_[i]->find_prebroken_piece((Direction) -i));
152     } 
153   while (flip(&i) != LEFT);
154 }       
155
156 Interval_t<int>
157 Spanner::spanned_rank_iv ()
158 {
159   Interval_t<int> iv (0, 0);
160
161   if (spanned_drul_[LEFT])
162     {
163       iv[LEFT] = Paper_column::rank_i (spanned_drul_[LEFT]->column_l ());
164     }
165   if (spanned_drul_[RIGHT])
166     {
167       iv[RIGHT] = Paper_column::rank_i (spanned_drul_[RIGHT]->column_l ());
168     }
169   return iv;
170 }
171
172 Item*
173 Spanner::get_bound (Direction d) const
174 {
175   return spanned_drul_ [d];
176 }
177
178 void
179 Spanner::set_bound(Direction d, Score_element*s)
180 {
181   Item * i = dynamic_cast<Item*> (s);
182   if (!i)
183     {
184       programming_error ("Must have Item for spanner bound.");
185       return;
186     }
187   
188   spanned_drul_[d] =i;
189
190   /**
191      We check for Line_of_score to prevent the column -> line_of_score
192      -> column -> line_of_score -> etc situation */
193   if (d== LEFT && !dynamic_cast<Line_of_score*> (this))
194     {
195       set_parent (i, X_AXIS);
196     }
197
198   /*
199     Signal that this column needs to be kept alive. They need to be
200     kept alive to have meaningful position and linebreaking.
201
202     [maybe we should try keeping all columns alive?, and perhaps
203     inherit position from their (non-)musical brother]
204     
205   */
206   if (dynamic_cast<Paper_column*> (i))
207     {
208       Pointer_group_interface (i, "bounded-by-me").add_element (this);  
209     }
210 }
211
212
213 Spanner::Spanner (SCM s)
214   : Score_element (s)
215 {
216   spanned_drul_[LEFT]=0;
217   spanned_drul_[RIGHT]=0;
218 }
219
220 Spanner::Spanner (Spanner const &s)
221   : Score_element (s)
222 {
223   spanned_drul_[LEFT] = spanned_drul_[RIGHT] =0;
224 }
225
226
227 Real
228 Spanner::spanner_length() const
229 {  
230   Real l = spanned_drul_[LEFT]->relative_coordinate (0, X_AXIS);
231   Real r = spanned_drul_[RIGHT]->relative_coordinate (0, X_AXIS);
232
233   if (r< l)
234     programming_error ("spanner with negative length");
235
236   return r-l;
237 }
238
239 Line_of_score *
240 Spanner::line_l() const
241 {
242   if (!spanned_drul_[LEFT] || !spanned_drul_[RIGHT])
243     return 0;
244   if (spanned_drul_[LEFT]->line_l () != spanned_drul_[RIGHT]->line_l ())
245     return 0;
246   return spanned_drul_[LEFT]->line_l();
247 }
248
249
250 Score_element*
251 Spanner::find_broken_piece (Line_of_score*l) const
252 {
253   int idx = binsearch_link_array (broken_into_l_arr_,  (Spanner*)l, Spanner::compare);
254   
255   if (idx < 0)
256     return 0;
257   else
258     return broken_into_l_arr_ [idx];
259 }
260
261
262 int
263 Spanner::compare (Spanner * const &p1, Spanner * const &p2)
264 {
265   return  p1->line_l ()->rank_i_ - p2->line_l ()->rank_i_;
266 }
267
268 bool
269 Spanner::broken_b() const
270 {
271   return broken_into_l_arr_.size();
272 }
273
274
275 /*
276   If this is a broken spanner, return the amount the left end is to be
277   shifted horizontally so that the spanner starts after the initial
278   clef and key on the staves. This is necessary for ties, slurs,
279   crescendo and decrescendo signs, for example.
280 */
281 Real
282 Spanner::get_broken_left_end_align () const
283 {
284   Paper_column *sc = dynamic_cast<Paper_column*> (spanned_drul_[LEFT]->column_l());
285
286   // Relevant only if left span point is first column in line
287   if(sc != NULL &&
288      sc->break_status_dir () == RIGHT)
289     {
290       /*
291         
292         We used to do a full search for the Break_align_item.
293         But that doesn't make a difference, since the Paper_column
294         is likely to contain only a Break_align_item.
295       */
296       return sc->extent (X_AXIS)[RIGHT];
297     }
298
299   return 0.0;
300 }
301
302 SCM
303 Spanner::do_derived_mark ()
304 {
305   /*
306     We'd be fucked if this is called before spanned_drul_[] is inited.  */
307   if (status_i_ == ORPHAN)
308     return SCM_EOL;
309   
310   Direction d = LEFT;
311   do
312     if (spanned_drul_[d])
313       scm_gc_mark (spanned_drul_[d]->self_scm ());
314   while (flip (&d) != LEFT);
315
316   for (int i= broken_into_l_arr_.size () ; i--;)
317     scm_gc_mark (broken_into_l_arr_[i]->self_scm ());
318
319   return SCM_EOL;
320 }
321
322 void
323 add_bound_item (Spanner* sp, Item*it)
324 {
325   if (!sp->get_bound (LEFT))
326     sp->set_bound (LEFT, it);
327   else
328     sp->set_bound (RIGHT, it);
329 }
330
331 static void
332 extend_spanner_over_item (Item *it, SCM extremal_pair)
333 {
334   if (!it)
335     return;
336   Item * col = it->column_l ();
337   Item * i1 = dynamic_cast<Item*> (unsmob_element (gh_car (extremal_pair)));
338   Item * i2 = dynamic_cast<Item*> (unsmob_element (gh_cdr (extremal_pair)));
339   int r = Paper_column::rank_i (col);
340   if (!i1 || r < Paper_column::rank_i (i1->column_l ()))
341     {
342       gh_set_car_x (extremal_pair, it->self_scm ());
343     }
344   if (!i2 || r > Paper_column::rank_i (i2->column_l ()))
345     {
346       gh_set_cdr_x (extremal_pair, it->self_scm ());
347     }
348 }
349
350 static void
351 extend_spanner_over_elements (SCM value, SCM extremal_pair)
352 {
353   if (gh_pair_p (value))
354     {
355       extend_spanner_over_elements (gh_car (value), extremal_pair);
356       extend_spanner_over_elements (gh_cdr (value), extremal_pair);
357     }
358   else if (unsmob_element (value))
359     {
360       if (Spanner * sp = dynamic_cast<Spanner*> (unsmob_element(value)))
361         {
362           extend_spanner_over_item (sp->get_bound (LEFT), extremal_pair);
363           extend_spanner_over_item (sp->get_bound (RIGHT), extremal_pair);
364         }
365       else if (Item * it= dynamic_cast<Item*> (unsmob_element(value)))
366         extend_spanner_over_item (it, extremal_pair);
367     }
368 }
369
370
371 /*
372   Make sure that the left and right bounds encompasses all objects it
373   points to.
374
375   TODO: maybe be more specific. Most probably fucks up if someone sets
376   a pointer to the staffsymbol in S
377 */
378 void
379 extend_spanner_over_elements (Score_element*s)
380 {
381   Spanner*sp = dynamic_cast<Spanner*> (s);
382
383   SCM s1 = sp->get_bound (LEFT) ? sp->get_bound (LEFT)->self_scm () : SCM_EOL;
384   SCM s2 = sp->get_bound (RIGHT) ? sp->get_bound (RIGHT)->self_scm () : SCM_EOL;  
385   
386   SCM pair = gh_cons (s1,s2);
387   extend_spanner_over_elements (sp->mutable_property_alist_, pair);
388
389   Score_element *p1 =  unsmob_element (gh_car (pair));
390   Score_element* p2 = unsmob_element (gh_cdr (pair));
391   sp->set_bound (LEFT,p1);
392   sp->set_bound (RIGHT, p2);
393
394   //extra precaution.
395 }
396