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