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