]> git.donarmstrong.com Git - lilypond.git/blob - lily/spanner.cc
release: 1.3.70
[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                                          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] = Paper_column::rank_i (spanned_drul_[LEFT]->column_l ());
138     }
139   if (spanned_drul_[RIGHT])
140     {
141       iv[RIGHT] = Paper_column::rank_i (spanned_drul_[RIGHT]->column_l ());
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
236 /*
237   If this is a broken spanner, return the amount the left end is to be
238   shifted horizontally so that the spanner starts after the initial
239   clef and key on the staves. This is necessary for ties, slurs,
240   crescendo and decrescendo signs, for example.
241 */
242 Real
243 Spanner::get_broken_left_end_align () const
244 {
245   Paper_column *sc = dynamic_cast<Paper_column*> (spanned_drul_[LEFT]->column_l());
246
247   // Relevant only if left span point is first column in line
248   if(sc != NULL &&
249      sc->break_status_dir () == RIGHT)
250     {
251       /*
252         
253         We used to do a full search for the Break_align_item.
254         But that doesn't make a difference, since the Paper_column
255         is likely to contain only a Break_align_item.
256       */
257       return sc->extent (X_AXIS)[RIGHT];
258     }
259
260   return 0.0;
261 }
262
263 void
264 Spanner::do_derived_mark ()
265 {
266   Direction d = LEFT;
267   do
268     if (spanned_drul_[d])
269       scm_gc_mark (spanned_drul_[d]->self_scm_);
270   while (flip (&d) != LEFT);
271
272   for (int i= broken_into_l_arr_.size () ; i--;)
273     scm_gc_mark (broken_into_l_arr_[i]->self_scm_);
274 }
275
276 void
277 add_bound_item (Spanner* sp, Item*it)
278 {
279   if (!sp->get_bound (LEFT))
280     sp->set_bound (LEFT, it);
281   else
282     sp->set_bound (RIGHT, it);
283 }