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