2 spanner.cc -- implement Spanner
4 source file of the GNU LilyPond music typesetter
6 (c) 1996--2007 Han-Wen Nienhuys <hanwen@xs4all.nl>
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"
20 Spanner::clone () const
22 return new Spanner (*this);
26 Spanner::do_break_processing ()
29 Item
*left
= spanned_drul_
[LEFT
];
30 Item
*right
= spanned_drul_
[RIGHT
];
36 if (get_system () || is_broken ())
42 If we have a spanner spanning one column, we must break it
43 anyway because it might provide a parent for another item. */
47 Item
*bound
= left
->find_prebroken_piece (d
);
49 programming_error ("no broken bound");
50 else if (bound
->get_system ())
52 Spanner
*span
= dynamic_cast<Spanner
*> (clone ());
53 span
->set_bound (LEFT
, bound
);
54 span
->set_bound (RIGHT
, bound
);
56 assert (span
->get_system ());
57 span
->get_system ()->typeset_grob (span
);
58 broken_intos_
.push_back (span
);
61 while ((flip (&d
)) != LEFT
);
65 System
*root
= get_root_system (this);
66 vector
<Item
*> break_points
= root
->broken_col_range (left
, right
);
68 break_points
.insert (break_points
.begin () + 0, left
);
69 break_points
.push_back (right
);
71 Slice parent_rank_slice
;
72 parent_rank_slice
.set_full ();
75 Check if our parent in X-direction spans equally wide
78 for (int a
= X_AXIS
; a
< NO_AXES
; a
++)
80 if (Spanner
*parent
= dynamic_cast<Spanner
*> (get_parent ((Axis
)a
)))
82 parent_rank_slice
.intersect (parent
->spanned_rank_interval ());
86 for (vsize i
= 1; i
< break_points
.size (); i
++)
88 Drul_array
<Item
*> bounds
;
89 bounds
[LEFT
] = break_points
[i
- 1];
90 bounds
[RIGHT
] = break_points
[i
];
94 if (!bounds
[d
]->get_system ())
95 bounds
[d
] = bounds
[d
]->find_prebroken_piece (- d
);
97 while ((flip (&d
)) != LEFT
);
99 if (!bounds
[LEFT
] || ! bounds
[RIGHT
])
101 programming_error ("bounds of this piece aren't breakable. ");
105 bool ok
= parent_rank_slice
.contains (bounds
[LEFT
]->get_column ()->get_rank ());
106 ok
= ok
&& parent_rank_slice
.contains (bounds
[RIGHT
]->get_column ()->get_rank ());
110 programming_error (to_string ("Spanner `%s' is not fully contained in parent spanner. Ignoring orphaned part",
116 Spanner
*span
= dynamic_cast<Spanner
*> (clone ());
117 span
->set_bound (LEFT
, bounds
[LEFT
]);
118 span
->set_bound (RIGHT
, bounds
[RIGHT
]);
120 if (!bounds
[LEFT
]->get_system ()
122 || !bounds
[RIGHT
]->get_system ()
123 || bounds
[LEFT
]->get_system () != bounds
[RIGHT
]->get_system ())
125 programming_error ("bounds of spanner are invalid");
130 bounds
[LEFT
]->get_system ()->typeset_grob (span
);
131 broken_intos_
.push_back (span
);
135 vector_sort (broken_intos_
, Spanner::less
);
136 for (vsize i
= broken_intos_
.size (); i
--;)
137 broken_intos_
[i
]->break_index_
= i
;
141 Spanner::get_break_index () const
147 Spanner::set_my_columns ()
149 Direction i
= (Direction
) LEFT
;
152 if (!spanned_drul_
[i
]->get_system ())
153 set_bound (i
, spanned_drul_
[i
]->find_prebroken_piece ((Direction
) -i
));
155 while (flip (&i
) != LEFT
);
159 Spanner::spanned_rank_interval () const
161 Interval_t
<int> iv (0, 0);
163 if (spanned_drul_
[LEFT
] && spanned_drul_
[LEFT
]->get_column ())
164 iv
[LEFT
] = spanned_drul_
[LEFT
]->get_column ()->get_rank ();
165 if (spanned_drul_
[RIGHT
] && spanned_drul_
[RIGHT
]->get_column ())
166 iv
[RIGHT
] = spanned_drul_
[RIGHT
]->get_column ()->get_rank ();
171 Spanner::spanned_time () const
173 return spanned_time_interval (spanned_drul_
[LEFT
],
174 spanned_drul_
[RIGHT
]);
179 Spanner::get_bound (Direction d
) const
181 return spanned_drul_
[d
];
185 Set the items that this spanner spans. If D == LEFT, we also set the
186 X-axis parent of THIS to S.
189 Spanner::set_bound (Direction d
, Grob
*s
)
191 Item
*i
= dynamic_cast<Item
*> (s
);
194 programming_error ("must have Item for spanner bound of " + name());
198 spanned_drul_
[d
] = i
;
201 We check for System to prevent the column -> line_of_score
202 -> column -> line_of_score -> etc situation */
203 if (d
== LEFT
&& !dynamic_cast<System
*> (this))
204 set_parent (i
, X_AXIS
);
207 Signal that this column needs to be kept alive. They need to be
208 kept alive to have meaningful position and linebreaking.
210 [maybe we should try keeping all columns alive?, and perhaps
211 inherit position from their (non-)musical brother]
214 if (dynamic_cast<Paper_column
*> (i
))
215 Pointer_group_interface::add_grob (i
, ly_symbol2scm ("bounded-by-me"), this);
218 Spanner::Spanner (SCM s
)
222 spanned_drul_
[LEFT
] = 0;
223 spanned_drul_
[RIGHT
] = 0;
226 Spanner::Spanner (Spanner
const &s
)
229 spanned_drul_
[LEFT
] = spanned_drul_
[RIGHT
] = 0;
233 Spanner::spanner_length () const
235 Real l
= spanned_drul_
[LEFT
]->relative_coordinate (0, X_AXIS
);
236 Real r
= spanned_drul_
[RIGHT
]->relative_coordinate (0, X_AXIS
);
239 programming_error ("spanner with negative length");
245 Spanner::get_system () const
247 if (!spanned_drul_
[LEFT
] || !spanned_drul_
[RIGHT
])
249 if (spanned_drul_
[LEFT
]->get_system () != spanned_drul_
[RIGHT
]->get_system ())
251 return spanned_drul_
[LEFT
]->get_system ();
255 Spanner::find_broken_piece (System
*l
) const
257 vsize idx
= binary_search (broken_intos_
, (Spanner
*)l
, Spanner::less
);
259 return broken_intos_
[idx
];
264 Spanner::broken_neighbor (Direction d
) const
269 vsize k
= broken_spanner_index (this);
270 Spanner
*orig
= dynamic_cast<Spanner
*> (original_
);
272 if (j
< 0 || vsize (j
) >= orig
->broken_intos_
.size ())
275 return orig
->broken_intos_
[j
];
279 Spanner::compare (Spanner
*const &p1
, Spanner
*const &p2
)
281 return p1
->get_system ()->get_rank () - p2
->get_system ()->get_rank ();
285 Spanner::less (Spanner
*const &a
, Spanner
*const &b
)
287 return a
->get_system ()->get_rank () < b
->get_system ()->get_rank ();
291 Spanner::is_broken () const
293 return broken_intos_
.size ();
297 If this is a broken spanner, return the amount the left end is to be
298 shifted horizontally so that the spanner starts after the initial
299 clef and key on the staves. This is necessary for ties, slurs,
300 crescendo and decrescendo signs, for example.
303 Spanner::get_broken_left_end_align () const
305 Paper_column
*sc
= dynamic_cast<Paper_column
*> (spanned_drul_
[LEFT
]->get_column ());
307 // Relevant only if left span point is first column in line
309 && sc
->break_status_dir () == RIGHT
)
312 We used to do a full search for the Break_align_item.
313 But that doesn't make a difference, since the Paper_column
314 is likely to contain only a Break_align_item.
316 return sc
->extent (sc
, X_AXIS
)[RIGHT
];
323 Spanner::derived_mark () const
327 if (spanned_drul_
[d
])
328 scm_gc_mark (spanned_drul_
[d
]->self_scm ());
329 while (flip (&d
) != LEFT
)
332 for (vsize i
= broken_intos_
.size (); i
--;)
333 scm_gc_mark (broken_intos_
[i
]->self_scm ());
337 Set left or right bound to IT.
339 Warning: caller should ensure that subsequent calls put in ITems
340 that are left-to-right ordered.
343 add_bound_item (Spanner
*sp
, Grob
*it
)
345 if (!sp
->get_bound (LEFT
))
346 sp
->set_bound (LEFT
, it
);
348 sp
->set_bound (RIGHT
, it
);
351 MAKE_SCHEME_CALLBACK (Spanner
, set_spacing_rods
, 1);
353 Spanner::set_spacing_rods (SCM smob
)
355 Grob
*me
= unsmob_grob (smob
);
356 SCM num_length
= me
->get_property ("minimum-length");
357 if (scm_is_number (num_length
))
360 Spanner
*sp
= dynamic_cast<Spanner
*> (me
);
361 System
*root
= get_root_system (me
);
362 Drul_array
<Item
*> bounds (sp
->get_bound (LEFT
),
363 sp
->get_bound (RIGHT
));
364 if (!bounds
[LEFT
] || !bounds
[RIGHT
])
365 return SCM_UNSPECIFIED
;
367 vector
<Item
*> cols (root
->broken_col_range (bounds
[LEFT
]->get_column (),
368 bounds
[RIGHT
]->get_column ()));
373 r
.item_drul_
[LEFT
] = sp
->get_bound (LEFT
);
374 r
.item_drul_
[RIGHT
] = cols
[0]->find_prebroken_piece (LEFT
);
375 r
.distance_
= robust_scm2double (num_length
, 0);
378 r
.item_drul_
[LEFT
] = cols
.back ()->find_prebroken_piece (RIGHT
);
379 r
.item_drul_
[RIGHT
] = sp
->get_bound (RIGHT
);
383 r
.distance_
= robust_scm2double (num_length
, 0);
384 r
.item_drul_
[LEFT
] = sp
->get_bound (LEFT
);
385 r
.item_drul_
[RIGHT
] = sp
->get_bound (RIGHT
);
389 return SCM_UNSPECIFIED
;
393 Return I such that SP == SP->ORIGINAL ()->BROKEN_INTOS_[I].
396 broken_spanner_index (Spanner
const *sp
)
398 Spanner
*parent
= dynamic_cast<Spanner
*> (sp
->original ());
400 return find (parent
->broken_intos_
, (Spanner
*) sp
) - parent
->broken_intos_
.begin ();
404 unsmob_spanner (SCM s
)
406 return dynamic_cast<Spanner
*> (unsmob_grob (s
));
409 MAKE_SCHEME_CALLBACK (Spanner
, bounds_width
, 1);
411 Spanner::bounds_width (SCM grob
)
413 Spanner
*me
= unsmob_spanner (grob
);
416 Grob
*common
= me
->get_bound (LEFT
)->common_refpoint (me
->get_bound (RIGHT
), X_AXIS
);
418 Interval
w (me
->get_bound (LEFT
)->relative_coordinate (common
, X_AXIS
),
419 me
->get_bound (RIGHT
)->relative_coordinate (common
, X_AXIS
));
421 w
-= me
->relative_coordinate (common
, X_AXIS
);
423 return ly_interval2scm (w
);
426 ADD_INTERFACE (Spanner
,
427 "Some objects are horizontally spanned between objects. For"
428 " example, slurs, beams, ties, etc. These grobs form a subtype"
429 " called @code{Spanner}. All spanners have two span points"
430 " (these must be @code{Item} objects), one on the left and one"
431 " on the right. The left bound is also the X@tie{}reference"
432 " point of the spanner.",