2 spanner.cc -- implement Spanner
4 source file of the GNU LilyPond music typesetter
6 (c) 1996--2009 Han-Wen Nienhuys <hanwen@xs4all.nl>
9 #include "libc-extension.hh"
11 #include "paper-column.hh"
12 #include "paper-score.hh"
13 #include "pointer-group-interface.hh"
19 Spanner::clone () const
21 return new Spanner (*this);
25 Spanner::do_break_processing ()
28 Item
*left
= spanned_drul_
[LEFT
];
29 Item
*right
= spanned_drul_
[RIGHT
];
35 if (get_system () || is_broken ())
41 If we have a spanner spanning one column, we must break it
42 anyway because it might provide a parent for another item. */
46 Item
*bound
= left
->find_prebroken_piece (d
);
48 programming_error ("no broken bound");
49 else if (bound
->get_system ())
51 Spanner
*span
= dynamic_cast<Spanner
*> (clone ());
52 span
->set_bound (LEFT
, bound
);
53 span
->set_bound (RIGHT
, bound
);
55 assert (span
->get_system ());
56 span
->get_system ()->typeset_grob (span
);
57 broken_intos_
.push_back (span
);
60 while ((flip (&d
)) != LEFT
);
64 System
*root
= get_root_system (this);
65 vector
<Item
*> break_points
= root
->broken_col_range (left
, right
);
67 break_points
.insert (break_points
.begin () + 0, left
);
68 break_points
.push_back (right
);
70 Slice parent_rank_slice
;
71 parent_rank_slice
.set_full ();
74 Check if our parent in X-direction spans equally wide
77 for (int a
= X_AXIS
; a
< NO_AXES
; a
++)
79 if (Spanner
*parent
= dynamic_cast<Spanner
*> (get_parent ((Axis
)a
)))
80 parent_rank_slice
.intersect (parent
->spanned_rank_interval ());
83 for (vsize i
= 1; i
< break_points
.size (); i
++)
85 Drul_array
<Item
*> bounds
;
86 bounds
[LEFT
] = break_points
[i
- 1];
87 bounds
[RIGHT
] = break_points
[i
];
91 if (!bounds
[d
]->get_system ())
92 bounds
[d
] = bounds
[d
]->find_prebroken_piece (- d
);
94 while ((flip (&d
)) != LEFT
);
96 if (!bounds
[LEFT
] || ! bounds
[RIGHT
])
98 programming_error ("bounds of this piece aren't breakable. ");
102 bool ok
= parent_rank_slice
.contains (bounds
[LEFT
]->get_column ()->get_rank ());
103 ok
= ok
&& parent_rank_slice
.contains (bounds
[RIGHT
]->get_column ()->get_rank ());
107 programming_error (to_string ("Spanner `%s' is not fully contained in parent spanner. Ignoring orphaned part",
113 Spanner
*span
= dynamic_cast<Spanner
*> (clone ());
114 span
->set_bound (LEFT
, bounds
[LEFT
]);
115 span
->set_bound (RIGHT
, bounds
[RIGHT
]);
117 if (!bounds
[LEFT
]->get_system ()
118 || !bounds
[RIGHT
]->get_system ()
119 || bounds
[LEFT
]->get_system () != bounds
[RIGHT
]->get_system ())
121 programming_error ("bounds of spanner are invalid");
126 bounds
[LEFT
]->get_system ()->typeset_grob (span
);
127 broken_intos_
.push_back (span
);
131 vector_sort (broken_intos_
, Spanner::less
);
132 for (vsize i
= broken_intos_
.size (); i
--;)
133 broken_intos_
[i
]->break_index_
= i
;
137 Spanner::get_break_index () const
143 Spanner::set_my_columns ()
145 Direction i
= (Direction
) LEFT
;
148 if (!spanned_drul_
[i
]->get_system ())
149 set_bound (i
, spanned_drul_
[i
]->find_prebroken_piece ((Direction
) -i
));
151 while (flip (&i
) != LEFT
);
155 Spanner::spanned_rank_interval () const
157 Interval_t
<int> iv (0, 0);
159 if (spanned_drul_
[LEFT
] && spanned_drul_
[LEFT
]->get_column ())
160 iv
[LEFT
] = spanned_drul_
[LEFT
]->get_column ()->get_rank ();
161 if (spanned_drul_
[RIGHT
] && spanned_drul_
[RIGHT
]->get_column ())
162 iv
[RIGHT
] = spanned_drul_
[RIGHT
]->get_column ()->get_rank ();
167 Spanner::spanned_time () const
169 return spanned_time_interval (spanned_drul_
[LEFT
],
170 spanned_drul_
[RIGHT
]);
175 Spanner::get_bound (Direction d
) const
177 return spanned_drul_
[d
];
181 Set the items that this spanner spans. If D == LEFT, we also set the
182 X-axis parent of THIS to S.
185 Spanner::set_bound (Direction d
, Grob
*s
)
187 Item
*i
= dynamic_cast<Item
*> (s
);
190 programming_error ("must have Item for spanner bound of " + name());
194 spanned_drul_
[d
] = i
;
197 We check for System to prevent the column -> line_of_score
198 -> column -> line_of_score -> etc situation */
199 if (d
== LEFT
&& !dynamic_cast<System
*> (this))
200 set_parent (i
, X_AXIS
);
203 Signal that this column needs to be kept alive. They need to be
204 kept alive to have meaningful position and linebreaking.
206 [maybe we should try keeping all columns alive?, and perhaps
207 inherit position from their (non-)musical brother]
209 if (dynamic_cast<Paper_column
*> (i
))
210 Pointer_group_interface::add_grob (i
, ly_symbol2scm ("bounded-by-me"), this);
213 Spanner::Spanner (SCM s
)
217 spanned_drul_
.set (0, 0);
220 Spanner::Spanner (Spanner
const &s
)
223 spanned_drul_
.set (0, 0);
227 Spanner::spanner_length () const
229 Real l
= spanned_drul_
[LEFT
]->relative_coordinate (0, X_AXIS
);
230 Real r
= spanned_drul_
[RIGHT
]->relative_coordinate (0, X_AXIS
);
233 programming_error ("spanner with negative length");
239 Spanner::get_system () const
241 if (!spanned_drul_
[LEFT
] || !spanned_drul_
[RIGHT
])
243 if (spanned_drul_
[LEFT
]->get_system () != spanned_drul_
[RIGHT
]->get_system ())
245 return spanned_drul_
[LEFT
]->get_system ();
249 Spanner::find_broken_piece (System
*l
) const
251 vsize idx
= binary_search (broken_intos_
, (Spanner
*) l
, Spanner::less
);
253 return broken_intos_
[idx
];
258 Spanner::broken_neighbor (Direction d
) const
263 vsize k
= broken_spanner_index (this);
264 Spanner
*orig
= dynamic_cast<Spanner
*> (original_
);
266 if (j
< 0 || vsize (j
) >= orig
->broken_intos_
.size ())
269 return orig
->broken_intos_
[j
];
273 Spanner::compare (Spanner
*const &p1
, Spanner
*const &p2
)
275 return p1
->get_system ()->get_rank () - p2
->get_system ()->get_rank ();
279 Spanner::less (Spanner
*const &a
, Spanner
*const &b
)
281 return a
->get_system ()->get_rank () < b
->get_system ()->get_rank ();
285 Spanner::is_broken () const
287 return broken_intos_
.size ();
291 If this is a broken spanner, return the amount the left end is to be
292 shifted horizontally so that the spanner starts after the initial
293 clef and key on the staves. This is necessary for ties, slurs,
294 crescendo and decrescendo signs, for example.
297 Spanner::get_broken_left_end_align () const
299 Paper_column
*sc
= dynamic_cast<Paper_column
*> (spanned_drul_
[LEFT
]->get_column ());
301 // Relevant only if left span point is first column in line
303 && sc
->break_status_dir () == RIGHT
)
306 We used to do a full search for the Break_align_item.
307 But that doesn't make a difference, since the Paper_column
308 is likely to contain only a Break_align_item.
310 return sc
->extent (sc
, X_AXIS
)[RIGHT
];
317 Spanner::derived_mark () const
321 if (spanned_drul_
[d
])
322 scm_gc_mark (spanned_drul_
[d
]->self_scm ());
323 while (flip (&d
) != LEFT
)
326 for (vsize i
= broken_intos_
.size (); i
--;)
327 scm_gc_mark (broken_intos_
[i
]->self_scm ());
331 Set left or right bound to IT.
333 Warning: caller should ensure that subsequent calls put in ITems
334 that are left-to-right ordered.
337 add_bound_item (Spanner
*sp
, Grob
*it
)
339 if (!sp
->get_bound (LEFT
))
340 sp
->set_bound (LEFT
, it
);
342 sp
->set_bound (RIGHT
, it
);
345 MAKE_SCHEME_CALLBACK (Spanner
, set_spacing_rods
, 1);
347 Spanner::set_spacing_rods (SCM smob
)
349 Grob
*me
= unsmob_grob (smob
);
350 SCM num_length
= me
->get_property ("minimum-length");
351 if (scm_is_number (num_length
))
354 Spanner
*sp
= dynamic_cast<Spanner
*> (me
);
355 System
*root
= get_root_system (me
);
356 Drul_array
<Item
*> bounds (sp
->get_bound (LEFT
),
357 sp
->get_bound (RIGHT
));
358 if (!bounds
[LEFT
] || !bounds
[RIGHT
])
359 return SCM_UNSPECIFIED
;
361 vector
<Item
*> cols (root
->broken_col_range (bounds
[LEFT
]->get_column (),
362 bounds
[RIGHT
]->get_column ()));
367 r
.item_drul_
[LEFT
] = sp
->get_bound (LEFT
);
368 r
.item_drul_
[RIGHT
] = cols
[0]->find_prebroken_piece (LEFT
);
369 r
.distance_
= robust_scm2double (num_length
, 0);
372 r
.item_drul_
[LEFT
] = cols
.back ()->find_prebroken_piece (RIGHT
);
373 r
.item_drul_
[RIGHT
] = sp
->get_bound (RIGHT
);
377 r
.distance_
= robust_scm2double (num_length
, 0);
378 r
.item_drul_
[LEFT
] = sp
->get_bound (LEFT
);
379 r
.item_drul_
[RIGHT
] = sp
->get_bound (RIGHT
);
383 return SCM_UNSPECIFIED
;
387 Return I such that SP == SP->ORIGINAL ()->BROKEN_INTOS_[I].
390 broken_spanner_index (Spanner
const *sp
)
392 Spanner
*parent
= dynamic_cast<Spanner
*> (sp
->original ());
394 return find (parent
->broken_intos_
, (Spanner
*) sp
) - parent
->broken_intos_
.begin ();
398 unsmob_spanner (SCM s
)
400 return dynamic_cast<Spanner
*> (unsmob_grob (s
));
403 MAKE_SCHEME_CALLBACK (Spanner
, bounds_width
, 1);
405 Spanner::bounds_width (SCM grob
)
407 Spanner
*me
= unsmob_spanner (grob
);
409 Grob
*common
= me
->get_bound (LEFT
)->common_refpoint (me
->get_bound (RIGHT
), X_AXIS
);
411 Interval
w (me
->get_bound (LEFT
)->relative_coordinate (common
, X_AXIS
),
412 me
->get_bound (RIGHT
)->relative_coordinate (common
, X_AXIS
));
414 w
-= me
->relative_coordinate (common
, X_AXIS
);
416 return ly_interval2scm (w
);
419 MAKE_SCHEME_CALLBACK (Spanner
, kill_zero_spanned_time
, 1);
421 Spanner::kill_zero_spanned_time (SCM grob
)
423 Spanner
*me
= unsmob_spanner (grob
);
424 Interval_t
<Moment
> moments
= me
->spanned_time ();
426 Remove the line or hairpin at the start of the line. For
427 piano voice indicators, it makes no sense to have them at
428 the start of the line.
430 I'm not sure what the official rules for glissandi are, but
431 usually the 2nd note of the glissando is "exact", so when playing
432 from the start of the line, there is no need to glide.
434 From a typographical p.o.v. this makes sense, since the amount of
435 space left of a note at the start of a line is very small.
440 if (moments
.length () == Moment (0, 0))
443 return SCM_UNSPECIFIED
;
446 ADD_INTERFACE (Spanner
,
447 "Some objects are horizontally spanned between objects. For"
448 " example, slurs, beams, ties, etc. These grobs form a subtype"
449 " called @code{Spanner}. All spanners have two span points"
450 " (these must be @code{Item} objects), one on the left and one"
451 " on the right. The left bound is also the X@tie{}reference"
452 " point of the spanner.",