]> git.donarmstrong.com Git - lilypond.git/blob - lily/spanner.cc
release: 1.3.66
[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         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
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         }
114     }
115   broken_into_l_arr_.sort (Spanner::compare);
116 }
117
118 void
119 Spanner::set_my_columns()
120 {
121   Direction i = (Direction) LEFT;
122   do 
123     {
124       if (!spanned_drul_[i]->line_l())
125         set_bound(i,spanned_drul_[i]->find_prebroken_piece((Direction) -i));
126     } 
127   while (flip(&i) != LEFT);
128 }       
129
130 Interval_t<int>
131 Spanner::spanned_rank_iv ()
132 {
133   Interval_t<int> iv (0, 0);
134
135   if (spanned_drul_[LEFT])
136     {
137       iv[LEFT] = spanned_drul_[LEFT]->column_l ()->rank_i ();
138     }
139   if (spanned_drul_[RIGHT])
140     {
141       iv[RIGHT] = spanned_drul_[RIGHT]->column_l ()->rank_i ();
142     }
143   return iv;
144 }
145
146 Item*
147 Spanner::get_bound (Direction d) const
148 {
149   return spanned_drul_ [d];
150 }
151
152 void
153 Spanner::set_bound(Direction d, Score_element*s)
154 {
155   Item * i = dynamic_cast<Item*> (s);
156   if (!i)
157     {
158       programming_error ("Must have Item for spanner bound.");
159       return;
160     }
161   
162   spanned_drul_[d] =i;
163
164   /**
165      We check for Line_of_score to prevent the column -> line_of_score
166      -> column -> line_of_score -> etc situation */
167   if (d== LEFT && !dynamic_cast<Line_of_score*> (this))
168     {
169       set_parent (i, X_AXIS);
170     }
171 }
172
173
174 Spanner::Spanner (SCM s)
175   : Score_element (s)
176 {
177   spanned_drul_[LEFT]=0;
178   spanned_drul_[RIGHT]=0;
179 }
180
181 Spanner::Spanner (Spanner const &s)
182   : Score_element (s)
183 {
184   spanned_drul_[LEFT] = spanned_drul_[RIGHT] =0;
185 }
186
187
188 Real
189 Spanner::spanner_length() const
190 {  
191   Real l = spanned_drul_[LEFT]->relative_coordinate (0, X_AXIS);
192   Real r = spanned_drul_[RIGHT]->relative_coordinate (0, X_AXIS);
193
194   if (r< l)
195     programming_error ("spanner with negative length");
196
197   return r-l;
198 }
199
200 Line_of_score *
201 Spanner::line_l() const
202 {
203   if (!spanned_drul_[LEFT] || !spanned_drul_[RIGHT])
204     return 0;
205   if (spanned_drul_[LEFT]->line_l () != spanned_drul_[RIGHT]->line_l ())
206     return 0;
207   return spanned_drul_[LEFT]->line_l();
208 }
209
210
211 Score_element*
212 Spanner::find_broken_piece (Line_of_score*l) const
213 {
214   int idx = binsearch_link_array (broken_into_l_arr_,  (Spanner*)l, Spanner::compare);
215   
216   if (idx < 0)
217     return 0;
218   else
219     return broken_into_l_arr_ [idx];
220 }
221
222
223 int
224 Spanner::compare (Spanner * const &p1, Spanner * const &p2)
225 {
226   return p1->line_l ()->rank_i_ - p2->line_l ()->rank_i_;
227 }
228
229 bool
230 Spanner::broken_b() const
231 {
232   return broken_into_l_arr_.size();
233 }
234
235 Array<Rod>
236 Spanner::get_rods () const
237 {
238   Array<Rod> r;
239   return r;
240 }
241
242 Array<Spring>
243 Spanner::get_springs () const
244 {
245   Array<Spring> s;
246   return s;    
247 }
248
249 void
250 Spanner::do_space_processing ()
251 {
252   Array<Rod> rs (get_rods ());
253   for (int i=0; i < rs.size (); i++)
254     {
255       rs[i].add_to_cols ();
256     }
257
258   Array<Spring> ss (get_springs ());
259   for (int i=0; i < ss.size (); i++)
260     {
261       if (isinf (ss[i].distance_f_))
262         programming_error ("weird spring");
263       else
264         ss[i].add_to_cols ();
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 (X_AXIS)[RIGHT];
290     }
291
292   return 0.0;
293 }
294
295 void
296 Spanner::do_derived_mark ()
297 {
298   Direction d = LEFT;
299   do
300     if (spanned_drul_[d])
301       scm_gc_mark (spanned_drul_[d]->self_scm_);
302   while (flip (&d) != LEFT);
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 }