]> git.donarmstrong.com Git - lilypond.git/blob - lily/spanner.cc
release: 1.3.83
[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
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
200 Spanner::Spanner (SCM s)
201   : Score_element (s)
202 {
203   spanned_drul_[LEFT]=0;
204   spanned_drul_[RIGHT]=0;
205 }
206
207 Spanner::Spanner (Spanner const &s)
208   : Score_element (s)
209 {
210   spanned_drul_[LEFT] = spanned_drul_[RIGHT] =0;
211 }
212
213
214 Real
215 Spanner::spanner_length() const
216 {  
217   Real l = spanned_drul_[LEFT]->relative_coordinate (0, X_AXIS);
218   Real r = spanned_drul_[RIGHT]->relative_coordinate (0, X_AXIS);
219
220   if (r< l)
221     programming_error ("spanner with negative length");
222
223   return r-l;
224 }
225
226 Line_of_score *
227 Spanner::line_l() const
228 {
229   if (!spanned_drul_[LEFT] || !spanned_drul_[RIGHT])
230     return 0;
231   if (spanned_drul_[LEFT]->line_l () != spanned_drul_[RIGHT]->line_l ())
232     return 0;
233   return spanned_drul_[LEFT]->line_l();
234 }
235
236
237 Score_element*
238 Spanner::find_broken_piece (Line_of_score*l) const
239 {
240   int idx = binsearch_link_array (broken_into_l_arr_,  (Spanner*)l, Spanner::compare);
241   
242   if (idx < 0)
243     return 0;
244   else
245     return broken_into_l_arr_ [idx];
246 }
247
248
249 int
250 Spanner::compare (Spanner * const &p1, Spanner * const &p2)
251 {
252   return  p1->line_l ()->rank_i_ - p2->line_l ()->rank_i_;
253 }
254
255 bool
256 Spanner::broken_b() const
257 {
258   return broken_into_l_arr_.size();
259 }
260
261
262 /*
263   If this is a broken spanner, return the amount the left end is to be
264   shifted horizontally so that the spanner starts after the initial
265   clef and key on the staves. This is necessary for ties, slurs,
266   crescendo and decrescendo signs, for example.
267 */
268 Real
269 Spanner::get_broken_left_end_align () const
270 {
271   Paper_column *sc = dynamic_cast<Paper_column*> (spanned_drul_[LEFT]->column_l());
272
273   // Relevant only if left span point is first column in line
274   if(sc != NULL &&
275      sc->break_status_dir () == RIGHT)
276     {
277       /*
278         
279         We used to do a full search for the Break_align_item.
280         But that doesn't make a difference, since the Paper_column
281         is likely to contain only a Break_align_item.
282       */
283       return sc->extent (X_AXIS)[RIGHT];
284     }
285
286   return 0.0;
287 }
288
289 SCM
290 Spanner::do_derived_mark ()
291 {
292   /*
293     We'd be fucked if this is called before spanned_drul_[] is inited.  */
294   if (status_i_ == ORPHAN)
295     return SCM_EOL;
296   
297   Direction d = LEFT;
298   do
299     if (spanned_drul_[d])
300       scm_gc_mark (spanned_drul_[d]->self_scm ());
301   while (flip (&d) != LEFT);
302
303   for (int i= broken_into_l_arr_.size () ; i--;)
304     scm_gc_mark (broken_into_l_arr_[i]->self_scm ());
305
306   return SCM_EOL;
307 }
308
309 void
310 add_bound_item (Spanner* sp, Item*it)
311 {
312   if (!sp->get_bound (LEFT))
313     sp->set_bound (LEFT, it);
314   else
315     sp->set_bound (RIGHT, it);
316 }