]> git.donarmstrong.com Git - lilypond.git/blob - lily/spanner.cc
beam: knee_b fix
[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--2002 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 */
8 #include <math.h>
9 #include <libc-extension.hh>
10
11
12 #include "warn.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 "system.hh"
20 #include "group-interface.hh"
21
22 void
23 Spanner::do_break_processing ()
24 {
25   //break_into_pieces
26   Item * left = spanned_drul_[LEFT];
27   Item * right = spanned_drul_[RIGHT];
28
29   if (!left || !right)
30     return;
31   
32   /*
33     Check if our parent in X-direction spans equally wide
34     or wider than we do.
35    */
36   for (int a = X_AXIS; a < NO_AXES; a ++)
37     {
38       if (Spanner* parent = dynamic_cast<Spanner*> (get_parent ((Axis)a)))
39         {
40           if (!parent->spanned_rank_iv ().contains_b (this->spanned_rank_iv ()))
41             {
42               programming_error (to_string ("Spanner `%s' is not fully contained in parent spanner `%s'.",
43                                          name ().to_str0 (),
44                                          parent->name ().to_str0 ()));
45             }
46         }
47     }
48   
49   if (get_system () || broken_b ())
50     return;
51
52   if (left == right)
53     {
54       /*
55         If we have a spanner spanning one column, we must break it
56         anyway because it might provide a parent for another item.  */
57       Direction d = LEFT;
58       do
59         {
60           Item* bound = left->find_prebroken_piece (d);
61           if (!bound)
62             programming_error ("no broken bound");
63           else if (bound->get_system ())
64             {
65               Spanner * span = dynamic_cast<Spanner*> (clone ());
66               span->set_bound (LEFT, bound);
67               span->set_bound (RIGHT, bound);
68
69               assert (span->get_system ()); 
70               span->get_system ()->typeset_grob (span);
71               broken_intos_.push (span);
72             }
73         }
74       while ((flip (&d))!= LEFT);
75     }
76   else
77     {
78       Link_array<Item> break_points = pscore_->system_->broken_col_range (left,right);
79
80       break_points.insert (left,0);
81       break_points.push (right);
82
83       for (int i=1; i < break_points.size (); i++) 
84         {
85           Drul_array<Item*> bounds;
86           bounds[LEFT] = break_points[i-1];
87           bounds[RIGHT] = break_points[i];
88           Direction d = LEFT;
89           do
90             {
91               if (!bounds[d]->get_system ())
92                 bounds[d] = bounds[d]->find_prebroken_piece (- d);
93             }
94           while ((flip (&d))!= LEFT);
95
96           if (!bounds[LEFT] ||  ! bounds[RIGHT])
97             {
98               programming_error ("bounds of this piece aren't breakable. ");
99               continue; 
100             }
101
102           Spanner *span = dynamic_cast<Spanner*> (clone ());
103           span->set_bound (LEFT,bounds[LEFT]);
104           span->set_bound (RIGHT,bounds[RIGHT]);
105
106           if (!bounds[LEFT]->get_system () 
107             
108               || !bounds[RIGHT]->get_system ()
109               || bounds[LEFT]->get_system () != bounds[RIGHT]->get_system ())
110             {
111               programming_error ("bounds of spanner are invalid");
112               span->suicide ();
113             }
114           else
115             {
116               bounds[LEFT]->get_system ()->typeset_grob (span);
117               broken_intos_.push (span);
118             }
119         }
120     }
121   broken_intos_.sort (Spanner::compare);
122 }
123
124 void
125 Spanner::set_my_columns ()
126 {
127   Direction i = (Direction) LEFT;
128   do 
129     {
130       if (!spanned_drul_[i]->get_system ())
131         set_bound (i,spanned_drul_[i]->find_prebroken_piece ((Direction) -i));
132     } 
133   while (flip (&i) != LEFT);
134 }       
135
136 Interval_t<int>
137 Spanner::spanned_rank_iv ()
138 {
139   Interval_t<int> iv (0, 0);
140
141   if (spanned_drul_[LEFT])
142     {
143       iv[LEFT] = Paper_column::get_rank (spanned_drul_[LEFT]->get_column ());
144     }
145   if (spanned_drul_[RIGHT])
146     {
147       iv[RIGHT] = Paper_column::get_rank (spanned_drul_[RIGHT]->get_column ());
148     }
149   return iv;
150 }
151
152 Item*
153 Spanner::get_bound (Direction d) const
154 {
155   return spanned_drul_ [d];
156 }
157
158 /*
159   Set the items that this spanner spans. If D == LEFT, we also set the
160   X-axis parent of THIS to S.
161 */
162 void
163 Spanner::set_bound (Direction d, Grob*s)
164 {
165   Item * i = dynamic_cast<Item*> (s);
166   if (!i)
167     {
168       programming_error ("Must have Item for spanner bound.");
169       return;
170     }
171   
172   spanned_drul_[d] =i;
173
174   /**
175      We check for System to prevent the column -> line_of_score
176      -> column -> line_of_score -> etc situation */
177   if (d== LEFT && !dynamic_cast<System*> (this))
178     {
179       set_parent (i, X_AXIS);
180     }
181
182   /*
183     Signal that this column needs to be kept alive. They need to be
184     kept alive to have meaningful position and linebreaking.
185
186     [maybe we should try keeping all columns alive?, and perhaps
187     inherit position from their (non-)musical brother]
188     
189   */
190   if (dynamic_cast<Paper_column*> (i))
191     {
192       Pointer_group_interface::add_grob (i, ly_symbol2scm ("bounded-by-me"), this);  
193     }
194 }
195
196 Spanner::Spanner (SCM s)
197   : Grob (s)
198 {
199   spanned_drul_[LEFT]=0;
200   spanned_drul_[RIGHT]=0;
201   Group_interface::add_thing (this, ly_symbol2scm ("interfaces"), ly_symbol2scm ("spanner-interface"));
202                      
203   
204 }
205
206 Spanner::Spanner (Spanner const &s)
207   : Grob (s)
208 {
209   spanned_drul_[LEFT] = spanned_drul_[RIGHT] =0;
210 }
211
212 Real
213 Spanner::spanner_length () const
214 {  
215   Real l = spanned_drul_[LEFT]->relative_coordinate (0, X_AXIS);
216   Real r = spanned_drul_[RIGHT]->relative_coordinate (0, X_AXIS);
217
218   if (r< l)
219     programming_error ("spanner with negative length");
220
221   return r-l;
222 }
223
224 System *
225 Spanner::get_system () const
226 {
227   if (!spanned_drul_[LEFT] || !spanned_drul_[RIGHT])
228     return 0;
229   if (spanned_drul_[LEFT]->get_system () != spanned_drul_[RIGHT]->get_system ())
230     return 0;
231   return spanned_drul_[LEFT]->get_system ();
232 }
233
234
235 Grob*
236 Spanner::find_broken_piece (System*l) const
237 {
238   int idx = binsearch_links (broken_intos_, (Spanner*)l, Spanner::compare);
239   
240   if (idx < 0)
241     return 0;
242   else
243     return broken_intos_ [idx];
244 }
245
246
247 int
248 Spanner::compare (Spanner * const &p1, Spanner * const &p2)
249 {
250   return  p1->get_system ()->rank_ - p2->get_system ()->rank_;
251 }
252
253 bool
254 Spanner::broken_b () const
255 {
256   return broken_intos_.size ();
257 }
258
259
260 /*
261   If this is a broken spanner, return the amount the left end is to be
262   shifted horizontally so that the spanner starts after the initial
263   clef and key on the staves. This is necessary for ties, slurs,
264   crescendo and decrescendo signs, for example.
265 */
266 Real
267 Spanner::get_broken_left_end_align () const
268 {
269   Paper_column *sc = dynamic_cast<Paper_column*> (spanned_drul_[LEFT]->get_column ());
270
271   // Relevant only if left span point is first column in line
272   if (sc != NULL &&
273      sc->break_status_dir () == RIGHT)
274     {
275       /*
276         
277         We used to do a full search for the Break_align_item.
278         But that doesn't make a difference, since the Paper_column
279         is likely to contain only a Break_align_item.
280       */
281       return sc->extent (sc, X_AXIS)[RIGHT];
282     }
283
284   return 0.0;
285 }
286
287 SCM
288 Spanner::do_derived_mark ()
289 {
290   /*
291     We'd be fucked if this is called before spanned_drul_[] is inited.  */
292   if (status_ == ORPHAN)
293     return SCM_EOL;
294   
295   Direction d = LEFT;
296   do
297     if (spanned_drul_[d])
298       scm_gc_mark (spanned_drul_[d]->self_scm ());
299   while (flip (&d) != LEFT);
300
301   for (int i= broken_intos_.size () ; i--;)
302     scm_gc_mark (broken_intos_[i]->self_scm ());
303
304   return SCM_EOL;
305 }
306
307
308 /*
309   Set left or right bound to IT.
310
311   Warning: caller should ensure that subsequent calls put in ITems
312   that are left-to-right ordered.
313  */
314 void
315 add_bound_item (Spanner* sp, Grob*it)
316 {
317   if (!sp->get_bound (LEFT))
318     sp->set_bound (LEFT, it);
319   else
320     sp->set_bound (RIGHT, it);
321 }
322
323
324 MAKE_SCHEME_CALLBACK (Spanner,set_spacing_rods,1);
325 SCM
326 Spanner::set_spacing_rods (SCM smob)
327 {
328   Grob*me = unsmob_grob (smob);
329
330   Rod r;
331   Spanner*sp = dynamic_cast<Spanner*> (me);
332   r.item_l_drul_[LEFT] = sp->get_bound (LEFT);
333   r.item_l_drul_[RIGHT] = sp->get_bound (RIGHT);
334   r.distance_ =
335     gh_scm2double (me->get_grob_property ("minimum-length"))
336     * 1.0;
337
338   r.add_to_cols ();
339   return SCM_UNSPECIFIED;
340 }
341
342
343 Spanner*
344 unsmob_spanner (SCM s )
345 {
346   return dynamic_cast<Spanner*> (unsmob_grob (s));
347 }
348
349 ADD_INTERFACE(Spanner,
350               "spanner-interface",
351               "
352 Other grobs have a shape that depends on the horizontal spacing. For
353 example, slur, beam, tie, etc. These grobs form a subtype called
354 @code{Spanner}. All spanners have two span-points (these must be
355 @code{Item}s), one on the left and one on the right. The left bound is
356 also the X-reference point of the spanner.
357 ",
358               "minimum-length");