2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 1996--2010 Han-Wen Nienhuys <hanwen@xs4all.nl>
6 LilyPond is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
11 LilyPond is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with LilyPond. If not, see <http://www.gnu.org/licenses/>.
20 #include "libc-extension.hh"
22 #include "paper-column.hh"
23 #include "paper-score.hh"
24 #include "pointer-group-interface.hh"
30 Spanner::clone () const
32 return new Spanner (*this);
36 Spanner::do_break_processing ()
39 Item
*left
= spanned_drul_
[LEFT
];
40 Item
*right
= spanned_drul_
[RIGHT
];
46 if (get_system () || is_broken ())
52 If we have a spanner spanning one column, we must break it
53 anyway because it might provide a parent for another item. */
57 Item
*bound
= left
->find_prebroken_piece (d
);
59 programming_error ("no broken bound");
60 else if (bound
->get_system ())
62 Spanner
*span
= dynamic_cast<Spanner
*> (clone ());
63 span
->set_bound (LEFT
, bound
);
64 span
->set_bound (RIGHT
, bound
);
66 assert (span
->get_system ());
67 span
->get_system ()->typeset_grob (span
);
68 broken_intos_
.push_back (span
);
71 while ((flip (&d
)) != LEFT
);
75 System
*root
= get_root_system (this);
76 vector
<Item
*> break_points
= root
->broken_col_range (left
, right
);
78 break_points
.insert (break_points
.begin () + 0, left
);
79 break_points
.push_back (right
);
81 Slice parent_rank_slice
;
82 parent_rank_slice
.set_full ();
85 Check if our parent in X-direction spans equally wide
88 for (int a
= X_AXIS
; a
< NO_AXES
; a
++)
90 if (Spanner
*parent
= dynamic_cast<Spanner
*> (get_parent ((Axis
)a
)))
91 parent_rank_slice
.intersect (parent
->spanned_rank_interval ());
94 for (vsize i
= 1; i
< break_points
.size (); i
++)
96 Drul_array
<Item
*> bounds
;
97 bounds
[LEFT
] = break_points
[i
- 1];
98 bounds
[RIGHT
] = break_points
[i
];
102 if (!bounds
[d
]->get_system ())
103 bounds
[d
] = bounds
[d
]->find_prebroken_piece (- d
);
105 while ((flip (&d
)) != LEFT
);
107 if (!bounds
[LEFT
] || ! bounds
[RIGHT
])
109 programming_error ("bounds of this piece aren't breakable. ");
113 bool ok
= parent_rank_slice
.contains (bounds
[LEFT
]->get_column ()->get_rank ());
114 ok
= ok
&& parent_rank_slice
.contains (bounds
[RIGHT
]->get_column ()->get_rank ());
118 programming_error (to_string ("Spanner `%s' is not fully contained in parent spanner. Ignoring orphaned part",
124 Spanner
*span
= dynamic_cast<Spanner
*> (clone ());
125 span
->set_bound (LEFT
, bounds
[LEFT
]);
126 span
->set_bound (RIGHT
, bounds
[RIGHT
]);
128 if (!bounds
[LEFT
]->get_system ()
129 || !bounds
[RIGHT
]->get_system ()
130 || bounds
[LEFT
]->get_system () != bounds
[RIGHT
]->get_system ())
132 programming_error ("bounds of spanner are invalid");
137 bounds
[LEFT
]->get_system ()->typeset_grob (span
);
138 broken_intos_
.push_back (span
);
142 vector_sort (broken_intos_
, Spanner::less
);
143 for (vsize i
= broken_intos_
.size (); i
--;)
144 broken_intos_
[i
]->break_index_
= i
;
148 Spanner::get_break_index () const
154 Spanner::set_my_columns ()
156 Direction i
= (Direction
) LEFT
;
159 if (!spanned_drul_
[i
]->get_system ())
160 set_bound (i
, spanned_drul_
[i
]->find_prebroken_piece ((Direction
) -i
));
162 while (flip (&i
) != LEFT
);
166 Spanner::spanned_rank_interval () const
168 Interval_t
<int> iv (0, 0);
170 if (spanned_drul_
[LEFT
] && spanned_drul_
[LEFT
]->get_column ())
171 iv
[LEFT
] = spanned_drul_
[LEFT
]->get_column ()->get_rank ();
172 if (spanned_drul_
[RIGHT
] && spanned_drul_
[RIGHT
]->get_column ())
173 iv
[RIGHT
] = spanned_drul_
[RIGHT
]->get_column ()->get_rank ();
178 Spanner::spanned_time () const
180 return spanned_time_interval (spanned_drul_
[LEFT
],
181 spanned_drul_
[RIGHT
]);
186 Spanner::get_bound (Direction d
) const
188 return spanned_drul_
[d
];
192 Set the items that this spanner spans. If D == LEFT, we also set the
193 X-axis parent of THIS to S.
196 Spanner::set_bound (Direction d
, Grob
*s
)
198 Item
*i
= dynamic_cast<Item
*> (s
);
201 programming_error ("must have Item for spanner bound of " + name());
205 spanned_drul_
[d
] = i
;
208 We check for System to prevent the column -> line_of_score
209 -> column -> line_of_score -> etc situation */
210 if (d
== LEFT
&& !dynamic_cast<System
*> (this))
211 set_parent (i
, X_AXIS
);
214 Signal that this column needs to be kept alive. They need to be
215 kept alive to have meaningful position and linebreaking.
217 [maybe we should try keeping all columns alive?, and perhaps
218 inherit position from their (non-)musical brother]
220 if (dynamic_cast<Paper_column
*> (i
))
221 Pointer_group_interface::add_grob (i
, ly_symbol2scm ("bounded-by-me"), this);
224 Spanner::Spanner (SCM s
)
228 spanned_drul_
.set (0, 0);
231 Spanner::Spanner (Spanner
const &s
)
234 spanned_drul_
.set (0, 0);
238 Spanner::spanner_length () const
240 Real l
= spanned_drul_
[LEFT
]->relative_coordinate (0, X_AXIS
);
241 Real r
= spanned_drul_
[RIGHT
]->relative_coordinate (0, X_AXIS
);
244 programming_error ("spanner with negative length");
250 Spanner::get_system () const
252 if (!spanned_drul_
[LEFT
] || !spanned_drul_
[RIGHT
])
254 if (spanned_drul_
[LEFT
]->get_system () != spanned_drul_
[RIGHT
]->get_system ())
256 return spanned_drul_
[LEFT
]->get_system ();
260 Spanner::find_broken_piece (System
*l
) const
262 vsize idx
= binary_search (broken_intos_
, (Spanner
*) l
, Spanner::less
);
264 return broken_intos_
[idx
];
269 Spanner::broken_neighbor (Direction d
) const
274 vsize k
= broken_spanner_index (this);
275 Spanner
*orig
= dynamic_cast<Spanner
*> (original_
);
277 if (j
< 0 || vsize (j
) >= orig
->broken_intos_
.size ())
280 return orig
->broken_intos_
[j
];
284 Spanner::compare (Spanner
*const &p1
, Spanner
*const &p2
)
286 return p1
->get_system ()->get_rank () - p2
->get_system ()->get_rank ();
290 Spanner::less (Spanner
*const &a
, Spanner
*const &b
)
292 return a
->get_system ()->get_rank () < b
->get_system ()->get_rank ();
296 Spanner::is_broken () const
298 return broken_intos_
.size ();
302 If this is a broken spanner, return the amount the left end is to be
303 shifted horizontally so that the spanner starts after the initial
304 clef and key on the staves. This is necessary for ties, slurs,
305 crescendo and decrescendo signs, for example.
308 Spanner::get_broken_left_end_align () const
310 Paper_column
*sc
= dynamic_cast<Paper_column
*> (spanned_drul_
[LEFT
]->get_column ());
312 // Relevant only if left span point is first column in line
314 && sc
->break_status_dir () == RIGHT
)
317 We used to do a full search for the Break_align_item.
318 But that doesn't make a difference, since the Paper_column
319 is likely to contain only a Break_align_item.
321 return sc
->extent (sc
, X_AXIS
)[RIGHT
];
328 Spanner::derived_mark () const
332 if (spanned_drul_
[d
])
333 scm_gc_mark (spanned_drul_
[d
]->self_scm ());
334 while (flip (&d
) != LEFT
)
337 for (vsize i
= broken_intos_
.size (); i
--;)
338 scm_gc_mark (broken_intos_
[i
]->self_scm ());
342 Set left or right bound to IT.
344 Warning: caller should ensure that subsequent calls put in ITems
345 that are left-to-right ordered.
348 add_bound_item (Spanner
*sp
, Grob
*it
)
350 if (!sp
->get_bound (LEFT
))
351 sp
->set_bound (LEFT
, it
);
353 sp
->set_bound (RIGHT
, it
);
356 MAKE_SCHEME_CALLBACK (Spanner
, set_spacing_rods
, 1);
358 Spanner::set_spacing_rods (SCM smob
)
360 Grob
*me
= unsmob_grob (smob
);
361 SCM num_length
= me
->get_property ("minimum-length");
362 if (scm_is_number (num_length
))
365 Spanner
*sp
= dynamic_cast<Spanner
*> (me
);
366 System
*root
= get_root_system (me
);
367 Drul_array
<Item
*> bounds (sp
->get_bound (LEFT
),
368 sp
->get_bound (RIGHT
));
369 if (!bounds
[LEFT
] || !bounds
[RIGHT
])
370 return SCM_UNSPECIFIED
;
372 vector
<Item
*> cols (root
->broken_col_range (bounds
[LEFT
]->get_column (),
373 bounds
[RIGHT
]->get_column ()));
378 r
.item_drul_
[LEFT
] = sp
->get_bound (LEFT
);
379 r
.item_drul_
[RIGHT
] = cols
[0]->find_prebroken_piece (LEFT
);
380 r
.distance_
= robust_scm2double (num_length
, 0);
383 r
.item_drul_
[LEFT
] = cols
.back ()->find_prebroken_piece (RIGHT
);
384 r
.item_drul_
[RIGHT
] = sp
->get_bound (RIGHT
);
388 r
.distance_
= robust_scm2double (num_length
, 0);
389 r
.item_drul_
[LEFT
] = sp
->get_bound (LEFT
);
390 r
.item_drul_
[RIGHT
] = sp
->get_bound (RIGHT
);
394 return SCM_UNSPECIFIED
;
398 Return I such that SP == SP->ORIGINAL ()->BROKEN_INTOS_[I].
401 broken_spanner_index (Spanner
const *sp
)
403 Spanner
*parent
= dynamic_cast<Spanner
*> (sp
->original ());
405 return find (parent
->broken_intos_
, (Spanner
*) sp
) - parent
->broken_intos_
.begin ();
409 unsmob_spanner (SCM s
)
411 return dynamic_cast<Spanner
*> (unsmob_grob (s
));
414 MAKE_SCHEME_CALLBACK (Spanner
, bounds_width
, 1);
416 Spanner::bounds_width (SCM grob
)
418 Spanner
*me
= unsmob_spanner (grob
);
420 Grob
*common
= me
->get_bound (LEFT
)->common_refpoint (me
->get_bound (RIGHT
), X_AXIS
);
422 Interval
w (me
->get_bound (LEFT
)->relative_coordinate (common
, X_AXIS
),
423 me
->get_bound (RIGHT
)->relative_coordinate (common
, X_AXIS
));
425 w
-= me
->relative_coordinate (common
, X_AXIS
);
427 return ly_interval2scm (w
);
430 MAKE_SCHEME_CALLBACK (Spanner
, kill_zero_spanned_time
, 1);
432 Spanner::kill_zero_spanned_time (SCM grob
)
434 Spanner
*me
= unsmob_spanner (grob
);
435 Interval_t
<Moment
> moments
= me
->spanned_time ();
437 Remove the line or hairpin at the start of the line. For
438 piano voice indicators, it makes no sense to have them at
439 the start of the line.
441 I'm not sure what the official rules for glissandi are, but
442 usually the 2nd note of the glissando is "exact", so when playing
443 from the start of the line, there is no need to glide.
445 From a typographical p.o.v. this makes sense, since the amount of
446 space left of a note at the start of a line is very small.
451 if (moments
.length () == Moment (0, 0))
454 return SCM_UNSPECIFIED
;
457 ADD_INTERFACE (Spanner
,
458 "Some objects are horizontally spanned between objects. For"
459 " example, slurs, beams, ties, etc. These grobs form a subtype"
460 " called @code{Spanner}. All spanners have two span points"
461 " (these must be @code{Item} objects), one on the left and one"
462 " on the right. The left bound is also the X@tie{}reference"
463 " point of the spanner.",