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