]> git.donarmstrong.com Git - lilypond.git/blob - lily/spanner.cc
release: 1.3.65
[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 #include "dimension-cache.hh"
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                                          classname (this),
45                                          classname (parent)));
46             }
47         }
48     }
49   
50   if (line_l () || broken_b ())
51     return;
52
53   if  (left == right)
54     {
55       /*
56         FIXME: this is broken.
57        */
58       /*
59         If we have a spanner spanning one column, we must break it
60         anyway because it might provide a parent for another item.  */
61       Direction d = LEFT;
62       do
63         {
64           Item* bound = left->find_prebroken_piece (d);
65           if (bound->line_l ())
66             {
67               Spanner * span_p = dynamic_cast<Spanner*>( clone ());
68               span_p->set_bound (LEFT, bound);
69               span_p->set_bound (RIGHT, bound);
70
71               assert (span_p->line_l ()); 
72               span_p->line_l ()->typeset_element (span_p);
73               broken_into_l_arr_.push (span_p);
74             }
75         }
76       while ((flip(&d))!= LEFT);
77     }
78   else
79     {
80       Link_array<Item> break_points = pscore_l_->line_l_->broken_col_range (left,right);
81
82       break_points.insert (left,0);
83       break_points.push (right);
84
85       for (int i=1; i < break_points.size(); i++) 
86         {
87           Drul_array<Item*> bounds;
88           bounds[LEFT] = break_points[i-1];
89           bounds[RIGHT] = break_points[i];
90           Direction d = LEFT;
91           do
92             {
93               if (!bounds[d]->line_l())
94                 bounds[d] = bounds[d]->find_prebroken_piece(- d);
95             }
96           while ((flip(&d))!= LEFT);
97
98           if (!bounds[LEFT] ||  ! bounds[RIGHT])
99             {
100               programming_error ("bounds of this piece aren't breakable. ");
101               continue; 
102             }
103
104           Spanner *span_p = dynamic_cast<Spanner*>(clone ());
105           span_p->set_bound(LEFT,bounds[LEFT]);
106           span_p->set_bound(RIGHT,bounds[RIGHT]);
107
108
109           assert (bounds[LEFT]->line_l () ==
110                   bounds[RIGHT]->line_l ());
111
112           bounds[LEFT]->line_l ()->typeset_element (span_p);
113           broken_into_l_arr_.push (span_p);
114         }
115     }
116   broken_into_l_arr_.sort (Spanner::compare);
117 }
118
119 void
120 Spanner::set_my_columns()
121 {
122   Direction i = (Direction) LEFT;
123   do 
124     {
125       if (!spanned_drul_[i]->line_l())
126         set_bound(i,spanned_drul_[i]->find_prebroken_piece((Direction) -i));
127     } 
128   while (flip(&i) != LEFT);
129 }       
130
131 Interval_t<int>
132 Spanner::spanned_rank_iv ()
133 {
134   Interval_t<int> iv (0, 0);
135
136   if (spanned_drul_[LEFT])
137     {
138       iv[LEFT] = spanned_drul_[LEFT]->column_l ()->rank_i ();
139     }
140   if (spanned_drul_[RIGHT])
141     {
142       iv[RIGHT] = spanned_drul_[RIGHT]->column_l ()->rank_i ();
143     }
144   return iv;
145 }
146
147 Item*
148 Spanner::get_bound (Direction d) const
149 {
150   return spanned_drul_ [d];
151 }
152
153 void
154 Spanner::set_bound(Direction d, Score_element*s)
155 {
156   Item * i = dynamic_cast<Item*> (s);
157   if (!i)
158     {
159       programming_error ("Must have Item for spanner bound.");
160       return;
161     }
162   
163   spanned_drul_[d] =i;
164
165   /**
166      We check for Line_of_score to prevent the column -> line_of_score
167      -> column -> line_of_score -> etc situation */
168   if (d== LEFT && !dynamic_cast<Line_of_score*> (this))
169     {
170       set_parent (i, X_AXIS);
171     }
172 }
173
174
175 Spanner::Spanner (SCM s)
176   : Score_element (s)
177 {
178   spanned_drul_[LEFT]=0;
179   spanned_drul_[RIGHT]=0;
180 }
181
182 Spanner::Spanner (Spanner const &s)
183   : Score_element (s)
184 {
185   spanned_drul_[LEFT] = spanned_drul_[RIGHT] =0;
186 }
187
188
189 Real
190 Spanner::spanner_length() const
191 {  
192   Real l = spanned_drul_[LEFT]->relative_coordinate (0, X_AXIS);
193   Real r = spanned_drul_[RIGHT]->relative_coordinate (0, X_AXIS);
194
195   if (r< l)
196     programming_error ("spanner with negative length");
197
198   return r-l;
199 }
200
201 Line_of_score *
202 Spanner::line_l() const
203 {
204   if (!spanned_drul_[LEFT] || !spanned_drul_[RIGHT])
205     return 0;
206   if (spanned_drul_[LEFT]->line_l () != spanned_drul_[RIGHT]->line_l ())
207     return 0;
208   return spanned_drul_[LEFT]->line_l();
209 }
210
211
212 Score_element*
213 Spanner::find_broken_piece (Line_of_score*l) const
214 {
215   int idx = binsearch_link_array (broken_into_l_arr_,  (Spanner*)l, Spanner::compare);
216   
217   if (idx < 0)
218     return 0;
219   else
220     return broken_into_l_arr_ [idx];
221 }
222
223
224 int
225 Spanner::compare (Spanner * const &p1, Spanner * const &p2)
226 {
227   return p1->line_l ()->rank_i_ - p2->line_l ()->rank_i_;
228 }
229
230 bool
231 Spanner::broken_b() const
232 {
233   return broken_into_l_arr_.size();
234 }
235
236 Array<Rod>
237 Spanner::get_rods () const
238 {
239   Array<Rod> r;
240   return r;
241 }
242
243 Array<Spring>
244 Spanner::get_springs () const
245 {
246   Array<Spring> s;
247   return s;    
248 }
249
250 void
251 Spanner::do_space_processing ()
252 {
253   Array<Rod> rs (get_rods ());
254   for (int i=0; i < rs.size (); i++)
255     {
256       rs[i].add_to_cols ();
257     }
258
259   Array<Spring> ss (get_springs ());
260   for (int i=0; i < ss.size (); i++)
261     {
262       if (isinf (ss[i].distance_f_))
263         programming_error ("weird spring");
264       else
265         ss[i].add_to_cols ();
266     }
267 }
268
269 /*
270   If this is a broken spanner, return the amount the left end is to be
271   shifted horizontally so that the spanner starts after the initial
272   clef and key on the staves. This is necessary for ties, slurs,
273   crescendo and decrescendo signs, for example.
274 */
275 Real
276 Spanner::get_broken_left_end_align () const
277 {
278   Paper_column *sc = dynamic_cast<Paper_column*> (spanned_drul_[LEFT]->column_l());
279
280   // Relevant only if left span point is first column in line
281   if(sc != NULL &&
282      sc->break_status_dir () == RIGHT)
283     {
284       /*
285         
286         We used to do a full search for the Break_align_item.
287         But that doesn't make a difference, since the Paper_column
288         is likely to contain only a Break_align_item.
289       */
290       return sc->extent (X_AXIS)[RIGHT];
291     }
292
293   return 0.0;
294 }
295
296 void
297 Spanner::do_derived_mark ()
298 {
299   Direction d = LEFT;
300   do
301     if (spanned_drul_[d])
302       scm_gc_mark (spanned_drul_[d]->self_scm_);
303   while (flip (&d) != LEFT);
304 }
305
306 void
307 add_bound_item (Spanner* sp, Item*it)
308 {
309   if (!sp->get_bound (LEFT))
310     sp->set_bound (LEFT, it);
311   else
312     sp->set_bound (RIGHT, it);
313 }