2 accidental-placement.cc -- implement Accidental_placement
4 source file of the GNU LilyPond music typesetter
6 (c) 2002--2007 Han-Wen Nienhuys <hanwen@xs4all.nl>
10 #include "accidental-placement.hh"
13 #include "rhythmic-head.hh"
14 #include "accidental-interface.hh"
16 #include "note-collision.hh"
17 #include "note-column.hh"
18 #include "pointer-group-interface.hh"
20 #include "stream-event.hh"
25 Accidental_placement::add_accidental (Grob
*me
, Grob
*a
)
27 a
->set_parent (me
, X_AXIS
);
28 a
->set_property ("X-offset", Grob::x_parent_positioning_proc
);
29 SCM cause
= a
->get_parent (Y_AXIS
)->get_property ("cause");
31 Stream_event
*mcause
= unsmob_stream_event (cause
);
34 programming_error ("note head has no event cause");
38 Pitch
*p
= unsmob_pitch (mcause
->get_property ("pitch"));
40 int n
= p
->get_notename ();
42 SCM accs
= me
->get_object ("accidental-grobs");
43 SCM key
= scm_from_int (n
);
44 SCM entry
= scm_assq (key
, accs
);
45 if (entry
== SCM_BOOL_F
)
48 entry
= scm_cdr (entry
);
50 entry
= scm_cons (a
->self_scm (), entry
);
52 accs
= scm_assq_set_x (accs
, key
, entry
);
54 me
->set_object ("accidental-grobs", accs
);
58 Split into break reminders.
61 Accidental_placement::split_accidentals (Grob
*accs
,
62 vector
<Grob
*> *break_reminder
,
63 vector
<Grob
*> *real_acc
)
65 for (SCM acs
= accs
->get_object ("accidental-grobs"); scm_is_pair (acs
);
67 for (SCM s
= scm_cdar (acs
); scm_is_pair (s
); s
= scm_cdr (s
))
69 Grob
*a
= unsmob_grob (scm_car (s
));
71 if (unsmob_grob (a
->get_object ("tie")))
72 break_reminder
->push_back (a
);
74 real_acc
->push_back (a
);
79 Accidental_placement::get_relevant_accidentals (vector
<Grob
*> const &elts
, Grob
*left
)
84 bool right
= dynamic_cast<Item
*> (left
)->break_status_dir () == RIGHT
;
86 for (vsize i
= 0; i
< elts
.size (); i
++)
88 split_accidentals (elts
[i
], &br
, &ra
);
90 ret
.insert (ret
.end (), ra
.begin (), ra
.end ());
93 ret
.insert (ret
.end (), br
.begin (), br
.end ());
98 struct Accidental_placement_entry
100 Skyline left_skyline_
;
101 Skyline right_skyline_
;
102 Interval vertical_extent_
;
103 vector
<Box
> extents_
;
104 vector
<Grob
*> grobs_
;
107 Accidental_placement_entry ()
114 static Interval all_accidental_vertical_extent
;
115 Real
ape_priority (Accidental_placement_entry
const *a
)
117 return a
->vertical_extent_
[UP
];
120 int ape_compare (Accidental_placement_entry
*const &a
,
121 Accidental_placement_entry
*const &b
)
123 return sign (ape_priority (a
) - ape_priority (b
));
126 bool ape_less (Accidental_placement_entry
*const &a
,
127 Accidental_placement_entry
*const &b
)
129 return ape_priority (a
) < ape_priority (b
);
132 int ape_rcompare (Accidental_placement_entry
*const &a
,
133 Accidental_placement_entry
*const &b
)
135 return -sign (ape_priority (a
) - ape_priority (b
));
147 stagger_apes (vector
<Accidental_placement_entry
*> *apes
)
149 vector
<Accidental_placement_entry
*> asc
= *apes
;
151 vector_sort (asc
, &ape_less
);
156 for (vsize i
= 0; i
< asc
.size ();)
158 Accidental_placement_entry
*a
= 0;
175 This routine computes placements of accidentals. During
176 add_accidental (), accidentals are already grouped by note, so that
177 octaves are placed above each other; they form columns. Then the
178 columns are sorted: the biggest columns go closest to the note.
179 Then the columns are spaced as closely as possible (using skyline
183 TODO: more advanced placement. Typically, the accs should be placed
184 to form a C shape, like this
193 The naturals should be left of the C as well; they should
196 Note that this placement problem looks NP hard, so we just use a
197 simple strategy, not an optimal choice.
201 TODO: there should be more space in the following situation
215 MAKE_SCHEME_CALLBACK (Accidental_placement
, calc_positioning_done
, 1);
217 Accidental_placement::calc_positioning_done (SCM smob
)
219 Grob
*me
= unsmob_grob (smob
);
223 me
->set_property ("positioning-done", SCM_BOOL_T
);
225 SCM accs
= me
->get_object ("accidental-grobs");
226 if (!scm_is_pair (accs
))
230 TODO: there is a bug in this code. If two accs are on the same
231 Y-position, they share an Ape, and will be printed in overstrike.
233 vector
<Accidental_placement_entry
*> apes
;
234 for (SCM s
= accs
; scm_is_pair (s
); s
= scm_cdr (s
))
236 Accidental_placement_entry
*ape
= new Accidental_placement_entry
;
237 ape
->notename_
= scm_to_int (scm_caar (s
));
239 for (SCM t
= scm_cdar (s
); scm_is_pair (t
); t
= scm_cdr (t
))
240 ape
->grobs_
.push_back (unsmob_grob (scm_car (t
)));
242 apes
.push_back (ape
);
245 Grob
*common
[] = {me
, 0};
248 First we must extract *all* pointers. We can only determine
249 extents if we're sure that we've found the right common refpoint
251 vector
<Grob
*> note_cols
, heads
;
252 for (vsize i
= apes
.size (); i
--;)
254 Accidental_placement_entry
*ape
= apes
[i
];
255 for (vsize j
= ape
->grobs_
.size (); j
--;)
257 Grob
*a
= ape
->grobs_
[j
];
260 common
[Y_AXIS
] = common
[Y_AXIS
]->common_refpoint (a
, Y_AXIS
);
264 Grob
*head
= a
->get_parent (Y_AXIS
);
266 Grob
*col
= head
->get_parent (X_AXIS
);
267 if (Note_column::has_interface (col
))
268 note_cols
.push_back (col
);
270 heads
.push_back (head
);
275 This is a little kludgy: to get all notes, we look if there are
278 for (vsize i
= note_cols
.size (); i
--;)
280 Grob
*c
= note_cols
[i
]->get_parent (X_AXIS
);
281 if (Note_collision_interface::has_interface (c
))
283 extract_grob_set (c
, "elements", gs
);
285 concat (note_cols
, gs
);
289 for (vsize i
= note_cols
.size (); i
--;)
290 concat (heads
, extract_grob_array (note_cols
[i
], "note-heads"));
292 vector_sort (heads
, less
<Grob
*> ());
295 vector
<Grob
*> stems
;
296 for (vsize i
= 0; i
< heads
.size (); i
++)
298 if (Grob
*s
= Rhythmic_head::get_stem (heads
[i
]))
302 vector_sort (stems
, less
<Grob
*> ());
305 common
[Y_AXIS
] = common_refpoint_of_array (heads
, common
[Y_AXIS
], Y_AXIS
);
306 common
[Y_AXIS
] = common_refpoint_of_array (stems
, common
[Y_AXIS
], Y_AXIS
);
308 for (vsize i
= 0; i
< heads
.size (); i
++)
310 if (Grob
*s
= Rhythmic_head::get_stem (heads
[i
]))
313 common
[Y_AXIS
] = s
->common_refpoint (common
[Y_AXIS
], Y_AXIS
);
317 vector_sort (stems
, less
<Grob
*> ());
321 for (vsize i
= apes
.size (); i
--;)
323 Accidental_placement_entry
*ape
= apes
[i
];
325 for (vsize j
= apes
[i
]->grobs_
.size (); j
--;)
327 Grob
*a
= apes
[i
]->grobs_
[j
];
328 vector
<Box
> boxes
= Accidental_interface::accurate_boxes (a
, common
);
330 ape
->extents_
.insert (ape
->extents_
.end (), boxes
.begin (), boxes
.end ());
332 ape
->left_skyline_
= Skyline (ape
->extents_
, 0, Y_AXIS
, LEFT
);
333 ape
->right_skyline_
= Skyline (ape
->extents_
, 0, Y_AXIS
, RIGHT
);
337 for (vsize i
= apes
.size (); i
--;)
341 for (vsize j
= apes
[i
]->extents_
.size (); j
--;)
342 y
.unite (apes
[i
]->extents_
[j
][Y_AXIS
]);
343 apes
[i
]->vertical_extent_
= y
;
346 all_accidental_vertical_extent
= total
;
347 stagger_apes (&apes
);
349 Accidental_placement_entry
*head_ape
= new Accidental_placement_entry
;
350 common
[X_AXIS
] = common_refpoint_of_array (heads
, common
[X_AXIS
], X_AXIS
);
352 vector
<Box
> head_extents
;
353 for (vsize i
= heads
.size (); i
--;)
354 head_extents
.push_back (Box (heads
[i
]->extent (common
[X_AXIS
], X_AXIS
),
355 heads
[i
]->extent (common
[Y_AXIS
], Y_AXIS
)));
357 for (vsize i
= 0; i
< stems
.size (); i
++)
359 int very_large
= INT_MAX
;
361 head_extents
.push_back (Box (stems
[i
]->extent (common
[X_AXIS
], X_AXIS
),
362 stems
[i
]->pure_height (common
[Y_AXIS
], 0, very_large
)));
365 head_ape
->left_skyline_
= Skyline (head_extents
, 0, Y_AXIS
, LEFT
);
366 head_ape
->offset_
= 0.0;
368 Real padding
= robust_scm2double (me
->get_property ("padding"), 0.2);
370 Skyline left_skyline
= head_ape
->left_skyline_
;
371 left_skyline
.raise (-robust_scm2double (me
->get_property ("right-padding"), 0));
374 Add accs entries right-to-left.
376 for (vsize i
= apes
.size (); i
-- > 0;)
378 Real offset
= -apes
[i
]->right_skyline_
.distance (left_skyline
);
380 offset
= (i
+ 1 < apes
.size ()) ? apes
[i
+ 1]->offset_
: 0.0;
384 apes
[i
]->offset_
= offset
;
386 Skyline new_left_skyline
= apes
[i
]->left_skyline_
;
387 new_left_skyline
.raise (apes
[i
]->offset_
);
388 new_left_skyline
.merge (left_skyline
);
389 left_skyline
= new_left_skyline
;
392 for (vsize i
= apes
.size (); i
--;)
394 Accidental_placement_entry
*ape
= apes
[i
];
395 for (vsize j
= ape
->grobs_
.size (); j
--;)
396 ape
->grobs_
[j
]->translate_axis (ape
->offset_
, X_AXIS
);
399 Interval left_extent
, right_extent
;
400 Accidental_placement_entry
*ape
= apes
[0];
402 for (vsize i
= ape
->extents_
.size (); i
--;)
403 left_extent
.unite (ape
->offset_
+ ape
->extents_
[i
][X_AXIS
]);
406 for (vsize i
= ape
->extents_
.size (); i
--;)
407 right_extent
.unite (ape
->offset_
+ ape
->extents_
[i
][X_AXIS
]);
409 left_extent
[LEFT
] -= robust_scm2double (me
->get_property ("left-padding"), 0);
410 Interval
width (left_extent
[LEFT
], right_extent
[RIGHT
]);
412 SCM scm_width
= ly_interval2scm (width
);
413 me
->flush_extent_cache (X_AXIS
);
414 me
->set_property ("X-extent", scm_width
);
416 junk_pointers (apes
);
423 ADD_INTERFACE (Accidental_placement
,
424 "Resolve accidental collisions.",