2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 2002--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/>.
21 #include "accidental-placement.hh"
24 #include "rhythmic-head.hh"
25 #include "accidental-interface.hh"
27 #include "note-collision.hh"
28 #include "note-column.hh"
29 #include "pointer-group-interface.hh"
31 #include "stream-event.hh"
35 accidental_pitch (Grob
*acc
)
37 SCM cause
= acc
->get_parent (Y_AXIS
)->get_property ("cause");
39 Stream_event
*mcause
= unsmob_stream_event (cause
);
42 programming_error ("note head has no event cause");
46 return unsmob_pitch (mcause
->get_property ("pitch"));
50 Accidental_placement::add_accidental (Grob
*me
, Grob
*a
)
52 Pitch
*p
= accidental_pitch (a
);
56 a
->set_parent (me
, X_AXIS
);
57 a
->set_property ("X-offset", Grob::x_parent_positioning_proc
);
58 int n
= p
->get_notename ();
60 SCM accs
= me
->get_object ("accidental-grobs");
61 SCM key
= scm_from_int (n
);
62 SCM entry
= scm_assq (key
, accs
);
63 if (entry
== SCM_BOOL_F
)
66 entry
= scm_cdr (entry
);
68 entry
= scm_cons (a
->self_scm (), entry
);
70 accs
= scm_assq_set_x (accs
, key
, entry
);
72 me
->set_object ("accidental-grobs", accs
);
76 Split into break reminders.
79 Accidental_placement::split_accidentals (Grob
*accs
,
80 vector
<Grob
*> *break_reminder
,
81 vector
<Grob
*> *real_acc
)
83 for (SCM acs
= accs
->get_object ("accidental-grobs"); scm_is_pair (acs
);
85 for (SCM s
= scm_cdar (acs
); scm_is_pair (s
); s
= scm_cdr (s
))
87 Grob
*a
= unsmob_grob (scm_car (s
));
89 if (unsmob_grob (a
->get_object ("tie"))
90 && !to_boolean (a
->get_property ("forced")))
91 break_reminder
->push_back (a
);
93 real_acc
->push_back (a
);
98 Accidental_placement::get_relevant_accidentals (vector
<Grob
*> const &elts
, Grob
*left
)
103 bool right
= dynamic_cast<Item
*> (left
)->break_status_dir () == RIGHT
;
105 for (vsize i
= 0; i
< elts
.size (); i
++)
107 split_accidentals (elts
[i
], &br
, &ra
);
109 ret
.insert (ret
.end (), ra
.begin (), ra
.end ());
112 ret
.insert (ret
.end (), br
.begin (), br
.end ());
117 struct Accidental_placement_entry
119 Skyline left_skyline_
;
120 Skyline right_skyline_
;
121 Interval vertical_extent_
;
122 vector
<Box
> extents_
;
123 vector
<Grob
*> grobs_
;
126 Real
ape_priority (Accidental_placement_entry
const *a
)
128 return a
->vertical_extent_
[UP
];
131 bool ape_less (Accidental_placement_entry
*const &a
,
132 Accidental_placement_entry
*const &b
)
134 return ape_priority (a
) < ape_priority (b
);
138 This function provides a method for sorting accidentals that belong to the
139 same note. The accidentals that this function considers to be "smallest"
140 will be placed to the left of the "larger" accidentals.
142 Naturals are the largest (so that they don't get confused with cancellation
143 naturals); apart from that, we order according to the alteration (so
144 double-flats are the smallest).
146 Precondition: the accidentals are attached to NoteHeads of the same note
147 name -- the octaves, however, may be different.
150 acc_less (Grob
*const &a
, Grob
*const &b
)
152 Pitch
*p
= accidental_pitch (a
);
153 Pitch
*q
= accidental_pitch (b
);
157 programming_error ("these accidentals do not have a pitch");
161 if (p
->get_octave () != q
->get_octave ())
162 return p
->get_octave () < q
->get_octave ();
164 if (p
->get_alteration () == Rational (0))
166 if (q
->get_alteration () == Rational (0))
169 return p
->get_alteration () < q
->get_alteration ();
181 stagger_apes (vector
<Accidental_placement_entry
*> *apes
)
183 vector
<Accidental_placement_entry
*> asc
= *apes
;
185 vector_sort (asc
, &ape_less
);
190 for (vsize i
= 0; i
< asc
.size ();)
192 Accidental_placement_entry
*a
= 0;
208 static vector
<Accidental_placement_entry
*>
209 build_apes (SCM accs
)
211 vector
<Accidental_placement_entry
*> apes
;
212 for (SCM s
= accs
; scm_is_pair (s
); s
= scm_cdr (s
))
214 Accidental_placement_entry
*ape
= new Accidental_placement_entry
;
216 for (SCM t
= scm_cdar (s
); scm_is_pair (t
); t
= scm_cdr (t
))
217 ape
->grobs_
.push_back (unsmob_grob (scm_car (t
)));
219 apes
.push_back (ape
);
226 set_ape_skylines (Accidental_placement_entry
*ape
,
229 vector
<Grob
*> accs (ape
->grobs_
);
230 vector_sort (accs
, &acc_less
);
232 /* We know that each accidental has the same note name and we assume that
233 accidentals in different octaves won't collide. If two or more
234 accidentals are in the same octave:
235 1) if they are the same accidental, print them in overstrike
236 2) otherwise, shift one to the left so they don't overlap. */
239 Real last_offset
= 0;
240 Rational
last_alteration (0);
241 for (vsize i
= accs
.size (); i
--;)
244 Pitch
*p
= accidental_pitch (a
);
249 if (i
== accs
.size () - 1 || p
->get_octave () != last_octave
)
252 offset
= a
->extent (a
, X_AXIS
)[LEFT
] - 0.2;
254 else if (p
->get_alteration () == last_alteration
)
255 a
->translate_axis (last_offset
, X_AXIS
);
256 else /* Our alteration is different from the last one */
258 Real this_offset
= offset
- a
->extent (a
, X_AXIS
)[RIGHT
];
259 a
->translate_axis (this_offset
, X_AXIS
);
261 /* FIXME: read the padding from the AccidentalPlacement grob */
262 last_offset
= this_offset
;
263 offset
-= a
->extent (a
, X_AXIS
).length () + 0.2;
266 vector
<Box
> boxes
= Accidental_interface::accurate_boxes (a
, common
);
267 ape
->extents_
.insert (ape
->extents_
.end (), boxes
.begin (), boxes
.end ());
269 for (vsize j
= boxes
.size (); j
--;)
270 ape
->vertical_extent_
.unite (boxes
[j
][Y_AXIS
]);
272 last_octave
= p
->get_octave ();
273 last_alteration
= p
->get_alteration ();
275 ape
->left_skyline_
= Skyline (ape
->extents_
, 0, Y_AXIS
, LEFT
);
276 ape
->right_skyline_
= Skyline (ape
->extents_
, 0, Y_AXIS
, RIGHT
);
280 extract_heads_and_stems (vector
<Accidental_placement_entry
*> const &apes
)
282 vector
<Grob
*> note_cols
;
285 for (vsize i
= apes
.size (); i
--;)
287 Accidental_placement_entry
*ape
= apes
[i
];
288 for (vsize j
= ape
->grobs_
.size (); j
--;)
290 Grob
*acc
= ape
->grobs_
[j
];
291 Grob
*head
= acc
->get_parent (Y_AXIS
);
292 Grob
*col
= head
->get_parent (X_AXIS
);
294 if (Note_column::has_interface (col
))
295 note_cols
.push_back (col
);
297 ret
.push_back (head
);
302 This is a little kludgy: in case there are note columns without
303 accidentals, we get them from the Note_collision objects.
305 for (vsize i
= note_cols
.size (); i
--;)
307 Grob
*c
= note_cols
[i
]->get_parent (X_AXIS
);
308 if (Note_collision_interface::has_interface (c
))
310 extract_grob_set (c
, "elements", columns
);
311 concat (note_cols
, columns
);
315 /* Now that we have all of the columns, grab all of the note-heads */
316 for (vsize i
= note_cols
.size (); i
--;)
317 concat (ret
, extract_grob_array (note_cols
[i
], "note-heads"));
319 /* Now that we have all of the heads, grab all of the stems */
320 for (vsize i
= ret
.size (); i
--;)
321 if (Grob
*s
= Rhythmic_head::get_stem (ret
[i
]))
325 vector_sort (ret
, less
<Grob
*> ());
331 common_refpoint_of_accidentals (vector
<Accidental_placement_entry
*> const &apes
, Axis a
)
335 for (vsize i
= apes
.size (); i
--;)
336 for (vsize j
= apes
[i
]->grobs_
.size (); j
--;)
339 ret
= apes
[i
]->grobs_
[j
];
341 ret
= ret
->common_refpoint (apes
[i
]->grobs_
[j
], a
);
348 build_heads_skyline (vector
<Grob
*> const &heads_and_stems
,
351 vector
<Box
> head_extents
;
352 for (vsize i
= heads_and_stems
.size (); i
--;)
353 head_extents
.push_back (Box (heads_and_stems
[i
]->extent (common
[X_AXIS
], X_AXIS
),
354 heads_and_stems
[i
]->pure_height (common
[Y_AXIS
], 0, INT_MAX
)));
356 return Skyline (head_extents
, 0, Y_AXIS
, LEFT
);
360 Position the apes, starting from the right, so that they don't collide.
361 Return the total width.
364 position_apes (Grob
*me
,
365 vector
<Accidental_placement_entry
*> const &apes
,
366 Skyline
const &heads_skyline
)
368 Real padding
= robust_scm2double (me
->get_property ("padding"), 0.2);
369 Skyline left_skyline
= heads_skyline
;
370 left_skyline
.raise (-robust_scm2double (me
->get_property ("right-padding"), 0));
373 Add accs entries right-to-left.
376 Real last_offset
= 0.0;
377 for (vsize i
= apes
.size (); i
-- > 0;)
379 Accidental_placement_entry
*ape
= apes
[i
];
381 Real offset
= -ape
->right_skyline_
.distance (left_skyline
);
383 offset
= last_offset
;
387 Skyline new_left_skyline
= ape
->left_skyline_
;
388 new_left_skyline
.raise (offset
);
389 new_left_skyline
.merge (left_skyline
);
390 left_skyline
= new_left_skyline
;
392 /* Shift all of the accidentals in this ape */
393 for (vsize j
= ape
->grobs_
.size (); j
--;)
394 ape
->grobs_
[j
]->translate_axis (offset
, X_AXIS
);
396 for (vsize j
= ape
->extents_
.size (); j
--;)
397 width
.unite (offset
+ ape
->extents_
[j
][X_AXIS
]);
399 last_offset
= offset
;
407 This routine computes placements of accidentals. During
408 add_accidental (), accidentals are already grouped by note, so that
409 octaves are placed above each other; they form columns. Then the
410 columns are sorted: the biggest columns go closest to the note.
411 Then the columns are spaced as closely as possible (using skyline
415 TODO: more advanced placement. Typically, the accs should be placed
416 to form a C shape, like this
425 The naturals should be left of the C as well; they should
428 Note that this placement problem looks NP hard, so we just use a
429 simple strategy, not an optimal choice.
433 TODO: there should be more space in the following situation
447 MAKE_SCHEME_CALLBACK (Accidental_placement
, calc_positioning_done
, 1);
449 Accidental_placement::calc_positioning_done (SCM smob
)
451 Grob
*me
= unsmob_grob (smob
);
455 me
->set_property ("positioning-done", SCM_BOOL_T
);
457 SCM accs
= me
->get_object ("accidental-grobs");
458 if (!scm_is_pair (accs
))
461 vector
<Accidental_placement_entry
*> apes
= build_apes (accs
);
463 Grob
*common
[] = {me
, 0};
465 vector
<Grob
*> heads_and_stems
= extract_heads_and_stems (apes
);
467 common
[Y_AXIS
] = common_refpoint_of_accidentals (apes
, Y_AXIS
);
468 common
[Y_AXIS
] = common_refpoint_of_array (heads_and_stems
, common
[Y_AXIS
], Y_AXIS
);
469 common
[X_AXIS
] = common_refpoint_of_array (heads_and_stems
, me
, X_AXIS
);
471 for (vsize i
= apes
.size (); i
--;)
472 set_ape_skylines (apes
[i
], common
);
473 Skyline heads_skyline
= build_heads_skyline (heads_and_stems
, common
);
475 stagger_apes (&apes
);
476 Interval width
= position_apes (me
, apes
, heads_skyline
);
478 me
->flush_extent_cache (X_AXIS
);
479 me
->set_property ("X-extent", ly_interval2scm (width
));
481 junk_pointers (apes
);
486 ADD_INTERFACE (Accidental_placement
,
487 "Resolve accidental collisions.",