lilypond-1.3.154
[lilypond.git] / lily / spanner.cc
blob0fba92f847c3f3d753d63f1e7cfa8a46bba0934d
1 /*
2 spanner.cc -- implement Spanner
4 source file of the GNU LilyPond music typesetter
6 (c) 1996--2001 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 */
8 #include <math.h>
9 #include <libc-extension.hh>
12 #include "debug.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 "line-of-score.hh"
20 #include "break-align-item.hh"
21 #include "group-interface.hh"
23 void
24 Spanner::do_break_processing ()
26 //break_into_pieces
27 Item * left = spanned_drul_[LEFT];
28 Item * right = spanned_drul_[RIGHT];
30 if (!left || !right)
31 return;
34 Check if our parent in X-direction spans equally wide
35 or wider than we do.
37 for (int a = X_AXIS; a < NO_AXES; a ++)
39 if (Spanner* parent = dynamic_cast<Spanner*> (parent_l ((Axis)a)))
41 if (!parent->spanned_rank_iv ().contains_b (this->spanned_rank_iv ()))
43 programming_error (to_str ("Spanner `%s' is not fully contained in parent spanner `%s'.",
44 name ().ch_C (),
45 parent->name ().ch_C ()));
50 if (line_l () || broken_b ())
51 return;
53 if (left == right)
56 If we have a spanner spanning one column, we must break it
57 anyway because it might provide a parent for another item. */
58 Direction d = LEFT;
61 Item* bound = left->find_prebroken_piece (d);
62 if (!bound)
63 programming_error ("no broken bound");
64 else if (bound->line_l ())
66 Spanner * span_p = dynamic_cast<Spanner*> (clone ());
67 span_p->set_bound (LEFT, bound);
68 span_p->set_bound (RIGHT, bound);
70 assert (span_p->line_l ());
71 span_p->line_l ()->typeset_grob (span_p);
72 broken_into_l_arr_.push (span_p);
75 while ((flip (&d))!= LEFT);
77 else
79 Link_array<Item> break_points = pscore_l_->line_l_->broken_col_range (left,right);
81 break_points.insert (left,0);
82 break_points.push (right);
84 for (int i=1; i < break_points.size (); i++)
86 Drul_array<Item*> bounds;
87 bounds[LEFT] = break_points[i-1];
88 bounds[RIGHT] = break_points[i];
89 Direction d = LEFT;
92 if (!bounds[d]->line_l ())
93 bounds[d] = bounds[d]->find_prebroken_piece (- d);
95 while ((flip (&d))!= LEFT);
97 if (!bounds[LEFT] || ! bounds[RIGHT])
99 programming_error ("bounds of this piece aren't breakable. ");
100 continue;
103 Spanner *span_p = dynamic_cast<Spanner*> (clone ());
104 span_p->set_bound (LEFT,bounds[LEFT]);
105 span_p->set_bound (RIGHT,bounds[RIGHT]);
107 if (!bounds[LEFT]->line_l ()
109 || !bounds[RIGHT]->line_l ()
110 || bounds[LEFT]->line_l () != bounds[RIGHT]->line_l ())
112 programming_error ("bounds of spanner are invalid");
113 span_p->suicide ();
115 else
117 bounds[LEFT]->line_l ()->typeset_grob (span_p);
118 broken_into_l_arr_.push (span_p);
122 broken_into_l_arr_.sort (Spanner::compare);
125 void
126 Spanner::set_my_columns ()
128 Direction i = (Direction) LEFT;
131 if (!spanned_drul_[i]->line_l ())
132 set_bound (i,spanned_drul_[i]->find_prebroken_piece ((Direction) -i));
134 while (flip (&i) != LEFT);
137 Interval_t<int>
138 Spanner::spanned_rank_iv ()
140 Interval_t<int> iv (0, 0);
142 if (spanned_drul_[LEFT])
144 iv[LEFT] = Paper_column::rank_i (spanned_drul_[LEFT]->column_l ());
146 if (spanned_drul_[RIGHT])
148 iv[RIGHT] = Paper_column::rank_i (spanned_drul_[RIGHT]->column_l ());
150 return iv;
153 Item*
154 Spanner::get_bound (Direction d) const
156 return spanned_drul_ [d];
160 Set the items that this spanner spans. If D == LEFT, we also set the
161 X-axis parent of THIS to S.
163 void
164 Spanner::set_bound (Direction d, Grob*s)
166 Item * i = dynamic_cast<Item*> (s);
167 if (!i)
169 programming_error ("Must have Item for spanner bound.");
170 return;
173 spanned_drul_[d] =i;
176 We check for Line_of_score to prevent the column -> line_of_score
177 -> column -> line_of_score -> etc situation */
178 if (d== LEFT && !dynamic_cast<Line_of_score*> (this))
180 set_parent (i, X_AXIS);
184 Signal that this column needs to be kept alive. They need to be
185 kept alive to have meaningful position and linebreaking.
187 [maybe we should try keeping all columns alive?, and perhaps
188 inherit position from their (non-)musical brother]
191 if (dynamic_cast<Paper_column*> (i))
193 Pointer_group_interface::add_element (i, "bounded-by-me",this);
197 Spanner::Spanner (SCM s)
198 : Grob (s)
200 spanned_drul_[LEFT]=0;
201 spanned_drul_[RIGHT]=0;
204 Spanner::Spanner (Spanner const &s)
205 : Grob (s)
207 spanned_drul_[LEFT] = spanned_drul_[RIGHT] =0;
210 Real
211 Spanner::spanner_length () const
213 Real l = spanned_drul_[LEFT]->relative_coordinate (0, X_AXIS);
214 Real r = spanned_drul_[RIGHT]->relative_coordinate (0, X_AXIS);
216 if (r< l)
217 programming_error ("spanner with negative length");
219 return r-l;
222 Line_of_score *
223 Spanner::line_l () const
225 if (!spanned_drul_[LEFT] || !spanned_drul_[RIGHT])
226 return 0;
227 if (spanned_drul_[LEFT]->line_l () != spanned_drul_[RIGHT]->line_l ())
228 return 0;
229 return spanned_drul_[LEFT]->line_l ();
233 Grob*
234 Spanner::find_broken_piece (Line_of_score*l) const
236 int idx = binsearch_link_array (broken_into_l_arr_, (Spanner*)l, Spanner::compare);
238 if (idx < 0)
239 return 0;
240 else
241 return broken_into_l_arr_ [idx];
246 Spanner::compare (Spanner * const &p1, Spanner * const &p2)
248 return p1->line_l ()->rank_i_ - p2->line_l ()->rank_i_;
251 bool
252 Spanner::broken_b () const
254 return broken_into_l_arr_.size ();
259 If this is a broken spanner, return the amount the left end is to be
260 shifted horizontally so that the spanner starts after the initial
261 clef and key on the staves. This is necessary for ties, slurs,
262 crescendo and decrescendo signs, for example.
264 Real
265 Spanner::get_broken_left_end_align () const
267 Paper_column *sc = dynamic_cast<Paper_column*> (spanned_drul_[LEFT]->column_l ());
269 // Relevant only if left span point is first column in line
270 if (sc != NULL &&
271 sc->break_status_dir () == RIGHT)
275 We used to do a full search for the Break_align_item.
276 But that doesn't make a difference, since the Paper_column
277 is likely to contain only a Break_align_item.
279 return sc->extent (sc, X_AXIS)[RIGHT];
282 return 0.0;
286 Spanner::do_derived_mark ()
289 We'd be fucked if this is called before spanned_drul_[] is inited. */
290 if (status_i_ == ORPHAN)
291 return SCM_EOL;
293 Direction d = LEFT;
295 if (spanned_drul_[d])
296 scm_gc_mark (spanned_drul_[d]->self_scm ());
297 while (flip (&d) != LEFT);
299 for (int i= broken_into_l_arr_.size () ; i--;)
300 scm_gc_mark (broken_into_l_arr_[i]->self_scm ());
302 return SCM_EOL;
305 void
306 add_bound_item (Spanner* sp, Item*it)
308 if (!sp->get_bound (LEFT))
309 sp->set_bound (LEFT, it);
310 else
311 sp->set_bound (RIGHT, it);
315 Extends EXTREMAL_PAIR to include IT
317 static void
318 extend_spanner_over_item (Item *it, SCM extremal_pair)
320 if (!it)
321 return;
322 Item * col = it->column_l ();
323 Item * i1 = dynamic_cast<Item*> (unsmob_grob (gh_car (extremal_pair)));
324 Item * i2 = dynamic_cast<Item*> (unsmob_grob (gh_cdr (extremal_pair)));
325 int r = Paper_column::rank_i (col);
326 if (!i1 || r < Paper_column::rank_i (i1->column_l ()))
328 gh_set_car_x (extremal_pair, it->self_scm ());
330 if (!i2 || r > Paper_column::rank_i (i2->column_l ()))
332 gh_set_cdr_x (extremal_pair, it->self_scm ());
337 Extends EXTREMAL_PAIR to include every grob in VALUE
339 static void
340 extend_spanner_over_elements (SCM value, SCM extremal_pair)
342 if (gh_pair_p (value))
344 extend_spanner_over_elements (gh_car (value), extremal_pair);
345 extend_spanner_over_elements (gh_cdr (value), extremal_pair);
347 else if (unsmob_grob (value))
349 if (Spanner * sp = dynamic_cast<Spanner*> (unsmob_grob (value)))
351 extend_spanner_over_item (sp->get_bound (LEFT), extremal_pair);
352 extend_spanner_over_item (sp->get_bound (RIGHT), extremal_pair);
354 else if (Item * it= dynamic_cast<Item*> (unsmob_grob (value)))
355 extend_spanner_over_item (it, extremal_pair);
361 Make sure that the left and right bounds encompasses all objects it
362 points to.
364 TODO: maybe be more specific. Most probably fucks up if someone sets
365 a pointer to the staffsymbol in S
367 void
368 extend_spanner_over_elements (Grob*s)
370 Spanner*sp = dynamic_cast<Spanner*> (s);
372 SCM s1 = sp->get_bound (LEFT) ? sp->get_bound (LEFT)->self_scm () : SCM_EOL;
373 SCM s2 = sp->get_bound (RIGHT) ? sp->get_bound (RIGHT)->self_scm () : SCM_EOL;
375 SCM pair = gh_cons (s1,s2);
376 extend_spanner_over_elements (sp->mutable_property_alist_, pair);
378 Grob *p1 = unsmob_grob (gh_car (pair));
379 Grob* p2 = unsmob_grob (gh_cdr (pair));
380 sp->set_bound (LEFT,p1);
381 sp->set_bound (RIGHT, p2);
385 MAKE_SCHEME_CALLBACK (Spanner,set_spacing_rods,1);
387 Spanner::set_spacing_rods (SCM smob)
389 Grob*me = unsmob_grob (smob);
391 Rod r;
392 Spanner*sp = dynamic_cast<Spanner*> (me);
393 r.item_l_drul_[LEFT] = sp->get_bound (LEFT);
394 r.item_l_drul_[RIGHT] = sp->get_bound (RIGHT);
395 r.distance_f_ =
396 gh_scm2double (me->get_grob_property ("minimum-length"))
397 * 1.0;
399 r.add_to_cols ();
400 return SCM_UNSPECIFIED;