]> git.donarmstrong.com Git - lilypond.git/blob - lily/spanner.cc
($(outdir)/%.pdf): add DVIPS_FLAGS. This will
[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--2004 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 "stencil.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   for (int i= broken_intos_.size();i--;)
123     broken_intos_[i]->break_index_ = i;
124 }
125
126 int
127 Spanner::get_break_index ()const
128 {
129   return break_index_;
130 }
131
132 void
133 Spanner::set_my_columns ()
134 {
135   Direction i = (Direction) LEFT;
136   do 
137     {
138       if (!spanned_drul_[i]->get_system ())
139         set_bound (i,spanned_drul_[i]->find_prebroken_piece ((Direction) -i));
140     } 
141   while (flip (&i) != LEFT);
142 }       
143
144 Interval_t<int>
145 Spanner::spanned_rank_iv ()
146 {
147   Interval_t<int> iv (0, 0);
148
149   if (spanned_drul_[LEFT])
150     {
151       iv[LEFT] = Paper_column::get_rank (spanned_drul_[LEFT]->get_column ());
152     }
153   if (spanned_drul_[RIGHT])
154     {
155       iv[RIGHT] = Paper_column::get_rank (spanned_drul_[RIGHT]->get_column ());
156     }
157   return iv;
158 }
159
160
161 Item*
162 Spanner::get_bound (Direction d) const
163 {
164   return spanned_drul_ [d];
165 }
166
167 /*
168   Set the items that this spanner spans. If D == LEFT, we also set the
169   X-axis parent of THIS to S.
170 */
171 void
172 Spanner::set_bound (Direction d, Grob*s)
173 {
174   Item * i = dynamic_cast<Item*> (s);
175   if (!i)
176     {
177       programming_error ("Must have Item for spanner bound.");
178       return;
179     }
180   
181   spanned_drul_[d] =i;
182
183   /**
184      We check for System to prevent the column -> line_of_score
185      -> column -> line_of_score -> etc situation */
186   if (d== LEFT && !dynamic_cast<System*> (this))
187     {
188       set_parent (i, X_AXIS);
189     }
190
191   /*
192     Signal that this column needs to be kept alive. They need to be
193     kept alive to have meaningful position and linebreaking.
194
195     [maybe we should try keeping all columns alive?, and perhaps
196     inherit position from their (non-)musical brother]
197     
198   */
199   if (dynamic_cast<Paper_column*> (i))
200     {
201       Pointer_group_interface::add_grob (i, ly_symbol2scm ("bounded-by-me"), this);  
202     }
203 }
204
205 Spanner::Spanner (SCM s)
206   : Grob (s)
207 {
208   break_index_ = 0;
209   spanned_drul_[LEFT]=0;
210   spanned_drul_[RIGHT]=0;
211
212   Group_interface::add_thing (this, ly_symbol2scm ("interfaces"), ly_symbol2scm ("spanner-interface"));
213 }
214
215 Spanner::Spanner (Spanner const &s)
216   : Grob (s)
217 {
218   spanned_drul_[LEFT] = spanned_drul_[RIGHT] =0;
219 }
220
221 Real
222 Spanner::spanner_length () const
223 {  
224   Real l = spanned_drul_[LEFT]->relative_coordinate (0, X_AXIS);
225   Real r = spanned_drul_[RIGHT]->relative_coordinate (0, X_AXIS);
226
227   if (r< l)
228     programming_error ("spanner with negative length");
229
230   return r-l;
231 }
232
233 System *
234 Spanner::get_system () const
235 {
236   if (!spanned_drul_[LEFT] || !spanned_drul_[RIGHT])
237     return 0;
238   if (spanned_drul_[LEFT]->get_system () != spanned_drul_[RIGHT]->get_system ())
239     return 0;
240   return spanned_drul_[LEFT]->get_system ();
241 }
242
243
244 Grob*
245 Spanner::find_broken_piece (System*l) const
246 {
247   int idx = binsearch_links (broken_intos_, (Spanner*)l, Spanner::compare);
248   
249   if (idx < 0)
250     return 0;
251   else
252     return broken_intos_ [idx];
253 }
254
255
256 int
257 Spanner::compare (Spanner * const &p1, Spanner * const &p2)
258 {
259   return  p1->get_system ()->rank_ - p2->get_system ()->rank_;
260 }
261
262 bool
263 Spanner::broken_b () const
264 {
265   return broken_intos_.size ();
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]->get_column ());
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 (sc, X_AXIS)[RIGHT];
291     }
292
293   return 0.0;
294 }
295
296 SCM
297 Spanner::do_derived_mark () const
298 {
299   /*
300     We'd be fucked if this is called before spanned_drul_[] is inited.  */
301   if (status_ == ORPHAN)
302     return SCM_EOL;
303   
304   Direction d = LEFT;
305   do
306     if (spanned_drul_[d])
307       scm_gc_mark (spanned_drul_[d]->self_scm ());
308   while (flip (&d) != LEFT);
309
310   for (int i= broken_intos_.size () ; i--;)
311     scm_gc_mark (broken_intos_[i]->self_scm ());
312
313   return SCM_EOL;
314 }
315
316
317 /*
318   Set left or right bound to IT.
319
320   Warning: caller should ensure that subsequent calls put in ITems
321   that are left-to-right ordered.
322  */
323 void
324 add_bound_item (Spanner* sp, Grob*it)
325 {
326   if (!sp->get_bound (LEFT))
327     sp->set_bound (LEFT, it);
328   else
329     sp->set_bound (RIGHT, it);
330 }
331
332
333 MAKE_SCHEME_CALLBACK (Spanner,set_spacing_rods,1);
334 SCM
335 Spanner::set_spacing_rods (SCM smob)
336 {
337   Grob*me = unsmob_grob (smob);
338
339   Rod r;
340   Spanner*sp = dynamic_cast<Spanner*> (me);
341   r.item_l_drul_[LEFT] = sp->get_bound (LEFT);
342   r.item_l_drul_[RIGHT] = sp->get_bound (RIGHT);
343   r.distance_ =
344     robust_scm2double (me->get_grob_property ("minimum-length"), 0);
345
346   r.add_to_cols ();
347   return SCM_UNSPECIFIED;
348 }
349
350
351 /*
352   Return I such that SP == SP->ORIGINAL_->BROKEN_INTOS_[I]. 
353  */
354 int
355 broken_spanner_index (Spanner * sp)
356 {
357   Spanner * parent = dynamic_cast<Spanner*> (sp->original_);
358   return parent->broken_intos_.find_index (sp);
359 }
360                       
361
362 Spanner*
363 unsmob_spanner (SCM s )
364 {
365   return dynamic_cast<Spanner*> (unsmob_grob (s));
366 }
367
368 ADD_INTERFACE(Spanner,
369               "spanner-interface",
370 "Other grobs have a shape that depends on the horizontal spacing. For\n"
371 "example, slur, beam, tie, etc. These grobs form a subtype called\n"
372 "@code{Spanner}. All spanners have two span-points (these must be\n"
373 "@code{Item}s), one on the left and one on the right. The left bound is\n"
374 "also the X-reference point of the spanner.\n"
375 ,
376               "minimum-length");