]> git.donarmstrong.com Git - lilypond.git/blob - lily/tuplet-number.cc
Issue 4550 (2/2) Avoid "using namespace std;" in included files
[lilypond.git] / lily / tuplet-number.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 2005--2015 Han-Wen Nienhuys <hanwen@xs4all.nl>
5
6
7   LilyPond is free software: you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation, either version 3 of the License, or
10   (at your option) any later version.
11
12   LilyPond is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16
17   You should have received a copy of the GNU General Public License
18   along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "tuplet-number.hh"
22 #include "tuplet-bracket.hh"
23 #include "moment.hh"      // needed?
24 #include "paper-column.hh"
25 #include "text-interface.hh"
26 #include "spanner.hh"
27 #include "lookup.hh"
28 #include "pointer-group-interface.hh"
29 #include "staff-symbol-referencer.hh"
30 #include "axis-group-interface.hh"
31 #include "directional-element-interface.hh"
32 #include "note-column.hh"
33 #include "beam.hh"
34 #include "stem.hh"
35 #include "warn.hh"
36
37 using std::vector;
38
39 /*
40   The reference stem is used to determine on which side of the beam to place
41   the tuplet number when it is positioned independently of a bracket.  (The number
42   is always placed on the opposite side of this stem.)
43 */
44 Grob *
45 Tuplet_number::select_reference_stem (Grob *me_grob, vector<Grob *> const &cols)
46 {
47   Spanner *me = dynamic_cast<Spanner *> (me_grob);
48
49   int col_count = cols.size ();
50
51   if (!col_count)
52     return 0;
53
54   /*
55     When we have an odd number of stems, we choose the middle stem as
56     our reference.
57   */
58   Grob *ref_stem = Note_column::get_stem (cols[col_count / 2]);
59
60   if (col_count % 2 == 1)
61     return ref_stem;
62
63   /*
64     When we have an even number of stems, we choose between the central
65     two stems.
66   */
67   Direction me_dir = robust_scm2dir (me->get_property ("direction"), UP);
68   Drul_array<Item *> bounding_stems (Note_column::get_stem (cols[col_count / 2 - 1]),
69                                      Note_column::get_stem (cols[col_count / 2]));
70
71   for (LEFT_and_RIGHT (d))
72     if (!bounding_stems[d])
73       return bounding_stems[-d];
74
75   /*
76     If the central stems point in opposite directions, the number may
77     be placed on either side unless there is a fractional beam, in which
78     case the number goes opposite to the partial beam.
79
80     When there is an option, we use the setting of TupletNumber.direction.
81
82     If the central stems are in the same direction, it doesn't matter
83     which is used as the reference.  We use the one on the left.
84   */
85   Direction dir_left = get_grob_direction (bounding_stems[LEFT]);
86   Direction dir_right = get_grob_direction (bounding_stems[RIGHT]);
87
88   if (dir_left == dir_right)
89     ref_stem = bounding_stems[LEFT];
90   else
91     {
92       int beam_count_L_R = Stem::get_beaming (bounding_stems[LEFT], RIGHT);
93       int beam_count_R_L = Stem::get_beaming (bounding_stems[RIGHT], LEFT);
94       if (beam_count_L_R == beam_count_R_L)
95         ref_stem = (dir_left == me_dir) ? bounding_stems[LEFT] : bounding_stems[RIGHT];
96       else
97         ref_stem = (beam_count_L_R > beam_count_R_L)
98                    ? bounding_stems[LEFT] : bounding_stems[RIGHT];
99     }
100
101   return ref_stem;
102 }
103
104 /*
105   When we place the number close to the beam, we need to consider the note
106   columns adjoining the tuplet number on the same side of the beam.  The
107   number may not fit in the available space, or may need to be shifted
108   horizontally out of the way of stems and ledger lines.
109 */
110 Drul_array<Grob *>
111 Tuplet_number::adjacent_note_columns (Grob *me_grob, Grob *ref_stem)
112 {
113   Spanner *me = dynamic_cast<Spanner *> (me_grob);
114   Spanner *tuplet = unsmob<Spanner> (me->get_object ("bracket"));
115
116   extract_grob_set (tuplet, "note-columns", columns);
117   Grob *ref_col = ref_stem->get_parent (X_AXIS); // X-parent of Stem = NoteColumn
118   Direction ref_stem_dir = get_grob_direction (ref_stem);
119   vector<Grob *> filtered_cols;
120   vsize ref_pos = 0;
121
122   for (vsize i = 0, counter = 0; i < columns.size (); ++i)
123     {
124       Grob *stem = Note_column::get_stem (columns[i]);
125       if (stem && get_grob_direction (stem) == -ref_stem_dir)
126         {
127           filtered_cols.push_back (columns[i]);
128           ++counter;
129         }
130       if (columns[i] == ref_col)
131         {
132           filtered_cols.push_back (columns[i]);
133           ref_pos = counter;
134         }
135     }
136
137   Drul_array<Grob *> adj_cols (0, 0);
138
139   if (ref_pos > 0)
140     adj_cols[LEFT] = filtered_cols[ref_pos - 1];
141   if (ref_pos < filtered_cols.size () - 1)
142     adj_cols[RIGHT] = filtered_cols[ref_pos + 1];
143
144   return adj_cols;
145 }
146
147 /*
148   We determine whether our tuplet number will be put next to the beam
149   independently of the positioning of the associated tuplet bracket.
150
151   Draw next to the beam if:
152   --bracket isn't visible, AND
153   --there is a beam above or below the number, AND
154   --this beam is kneed, AND
155   --the tuplet number will fit between adjoining note columns
156 */
157 bool
158 Tuplet_number::knee_position_against_beam (Grob *me_grob, Grob *ref_stem)
159 {
160   Spanner *me = dynamic_cast<Spanner *> (me_grob);
161   Spanner *tuplet = unsmob<Spanner> (me->get_object ("bracket"));
162
163   bool bracket_visible = to_boolean (me->get_property ("bracket-visibility"))
164                          || !tuplet->extent (tuplet, Y_AXIS).is_empty ();
165
166   if (bracket_visible || !to_boolean (me->get_property ("knee-to-beam")))
167     return false;
168
169   Grob *beam = Stem::get_beam (ref_stem);
170
171   if (!beam || !Beam::is_knee (beam))
172     return false;
173
174   Grob *commonx = Tuplet_bracket::get_common_x (tuplet);
175   commonx = commonx->common_refpoint (me, X_AXIS);
176
177   Interval number_ext = me->extent (commonx, X_AXIS);
178
179   Drul_array<Grob *> adj_cols = adjacent_note_columns (me, ref_stem);
180
181   Item *left = me->get_bound (LEFT);
182   Item *right = me->get_bound (RIGHT);
183
184   if (!left || !right)
185     return false;
186
187   Drul_array<Item *> bounds (left, right);
188
189   Interval available_ext;
190   Real padding = robust_scm2double (me->get_property ("padding"), 0.5);
191
192   /*
193      If there is no note column on a given side of the tuplet number, we use
194      a paper column instead to determine the available space.  Padding is only
195      considered in the case of a note column.
196   */
197   for (LEFT_and_RIGHT (d))
198     {
199       if (adj_cols[d])
200         available_ext[d] = Axis_group_interface::generic_bound_extent (adj_cols[d], commonx, X_AXIS)[-d] + (-d * padding);
201       else
202         available_ext[d] = Axis_group_interface::generic_bound_extent (bounds[d], commonx, X_AXIS)[-d];
203     }
204
205   if (number_ext.length () > available_ext.length ())
206     {
207       programming_error ("not enough space for tuplet number against beam");
208       return false;
209     }
210
211   return true;
212 }
213
214 MAKE_SCHEME_CALLBACK (Tuplet_number, print, 1);
215 SCM
216 Tuplet_number::print (SCM smob)
217 {
218   Spanner *me = unsmob<Spanner> (smob);
219   Spanner *tuplet = unsmob<Spanner> (me->get_object ("bracket"));
220
221   if (!tuplet || !tuplet->is_live ())
222     {
223       me->suicide ();
224       return SCM_EOL;
225     }
226
227   SCM stc_scm = Text_interface::print (smob);
228   Stencil *stc = unsmob<Stencil> (stc_scm);
229
230   stc->align_to (X_AXIS, CENTER);
231   stc->align_to (Y_AXIS, CENTER);
232
233   return stc->smobbed_copy ();
234 }
235
236 /*
237   For a given horizontal displacement of the tuplet number, how much
238   vertical shift is necessary to keep it the same distance from the beam?
239 */
240 Real
241 calc_beam_y_shift (Grob *ref_stem, Real dx)
242 {
243   Grob *beam = Stem::get_beam (ref_stem);
244   Interval x_pos = robust_scm2interval (beam->get_property ("X-positions"), Interval (0.0, 0.0));
245   Interval y_pos = robust_scm2interval (beam->get_property ("quantized-positions"), Interval (0.0, 0.0));
246   Real beam_dx = x_pos.length ();
247   Real beam_dy = y_pos[RIGHT] - y_pos[LEFT];
248   Real slope = beam_dx ? beam_dy / beam_dx : 0.0;
249
250   return (slope * dx);
251 }
252
253 /*
254   The X- and Y-offset of the tuplet number are calculated in relation either
255   to the bracket associated with it, or with the beam it is placed against.
256 */
257
258 MAKE_SCHEME_CALLBACK (Tuplet_number, calc_x_offset, 1);
259 SCM
260 Tuplet_number::calc_x_offset (SCM smob)
261 {
262   Spanner *me = unsmob<Spanner> (smob);
263
264   Item *left_bound = me->get_bound (LEFT);
265   Item *right_bound = me->get_bound (RIGHT);
266   Drul_array<Item *> bounds (left_bound, right_bound);
267
268   Spanner *tuplet = unsmob<Spanner> (me->get_object ("bracket"));
269
270   Grob *commonx = Tuplet_bracket::get_common_x (tuplet);
271   commonx = commonx->common_refpoint (me, X_AXIS);
272
273   Interval bound_poss;
274
275   for (LEFT_and_RIGHT (d))
276     {
277       if (has_interface<Note_column> (bounds[d])
278           && Note_column::get_stem (bounds[d]))
279         bounds[d] = Note_column::get_stem (bounds[d]);
280       bound_poss[d] = Axis_group_interface::generic_bound_extent (bounds[d], commonx, X_AXIS)[-d];
281     }
282
283   extract_grob_set (tuplet, "note-columns", cols);
284   Grob *ref_stem = select_reference_stem (me, cols);
285
286   /*
287     Return bracket-based positioning.
288   */
289   if (!ref_stem
290       || !knee_position_against_beam (me, ref_stem))
291     {
292       Interval x_positions;
293       x_positions = robust_scm2interval (tuplet->get_property ("X-positions"),
294                                          Interval (0.0, 0.0));
295       return scm_from_double (x_positions.center ());
296     }
297
298   /*
299      Horizontally center the number on the beam.
300   */
301   Real col_pos = left_bound->relative_coordinate (commonx, X_AXIS);
302   Real x_offset = bound_poss.center () - col_pos;
303
304   /*
305     Consider possible collisions with adjacent note columns.
306   */
307   Drul_array<Grob *> adj_cols = adjacent_note_columns (me, ref_stem);
308   Interval number_ext = me->extent (commonx, X_AXIS);
309   number_ext.translate (x_offset);
310   Real padding = robust_scm2double (me->get_property ("padding"), 0.5);
311   number_ext.widen (padding);
312
313   Interval cor (0.0, 0.0);
314
315   for (LEFT_and_RIGHT (d))
316     if (adj_cols[d])
317       {
318         Interval nc_ext = adj_cols[d]->extent (commonx, X_AXIS);
319         Interval overlap (nc_ext);
320         overlap.intersect (number_ext);
321         if (!overlap.is_empty ())
322           cor[d] = overlap.length () * -d;
323         x_offset += cor[d];
324       }
325
326   return scm_from_double (x_offset);
327 }
328
329 /*
330   When a number is placed against the beam (independently of a bracket), the
331   Y-extent of a reference stem is used to determine the vertical placement of
332   the number.  When French beams are used the stem may not reach all beams.
333 */
334 int
335 count_beams_not_touching_stem (SCM beaming)
336 {
337   int count = 0;
338
339   for (SCM s = scm_car (beaming); scm_is_pair (s); s = scm_cdr (s))
340     {
341       if (scm_is_true (scm_c_memq (scm_car (s), scm_cdr (beaming))))
342         ++count;
343     }
344
345   return std::max (0, count - 1);
346 }
347
348 MAKE_SCHEME_CALLBACK (Tuplet_number, calc_y_offset, 1);
349 SCM
350 Tuplet_number::calc_y_offset (SCM smob)
351 {
352   Spanner *me = unsmob<Spanner> (smob);
353   Spanner *tuplet = unsmob<Spanner> (me->get_object ("bracket"));
354   Drul_array<Real> positions = robust_scm2drul (tuplet->get_property ("positions"),
355                                                 Drul_array<Real> (0.0, 0.0));
356   SCM to_bracket = scm_from_double ((positions[LEFT] + positions[RIGHT]) / 2.0);
357
358   Grob *commonx = Tuplet_bracket::get_common_x (me);
359   commonx = commonx->common_refpoint (me, X_AXIS);
360   Real x_coord = me->relative_coordinate (commonx, X_AXIS);
361   extract_grob_set (tuplet, "note-columns", columns);
362   Grob *ref_stem = select_reference_stem (me, columns);
363
364   if (!ref_stem || !knee_position_against_beam (me, ref_stem))
365     return to_bracket;
366
367   /*
368     First, we calculate the Y-offset of the tuplet number as if it
369     is positioned at the reference stem.
370   */
371   Grob *commony = common_refpoint_of_array (columns, tuplet, Y_AXIS);
372   commony = commony->common_refpoint (me, Y_AXIS);
373   extract_grob_set (me, "tuplets", tuplets);
374   commony = common_refpoint_of_array (tuplets, commony, Y_AXIS);
375   if (Grob *st = Staff_symbol_referencer::get_staff_symbol (me))
376     commony = st->common_refpoint (commony, Y_AXIS);
377
378   Interval ref_stem_ext = ref_stem->extent (commony, Y_AXIS);
379   Real tuplet_y = tuplet->relative_coordinate (commony, Y_AXIS);
380   Direction ref_stem_dir = get_grob_direction (ref_stem);
381
382   Real y_offset = ref_stem_ext[ref_stem_dir] - tuplet_y;
383
384   /*
385     Additional displacement for French beaming.
386   */
387   if (to_boolean (ref_stem->get_property ("french-beaming")))
388     {
389       Grob *beam = Stem::get_beam (ref_stem);
390       Real beam_translation = Beam::get_beam_translation (beam);
391       SCM beaming = ref_stem->get_property ("beaming");
392       y_offset += ref_stem_dir
393                   * count_beams_not_touching_stem (beaming)
394                   * beam_translation;
395     }
396
397   Real padding = robust_scm2double (me->get_property ("padding"), 0.5);
398   Real num_height = me->extent (commony, Y_AXIS).length ();
399
400   y_offset += ref_stem_dir * (padding + num_height / 2.0);
401
402   /*
403     Now we adjust the vertical position of the number to reflect
404     its actual horizontal placement along the beam.
405   */
406   Real ref_stem_x = ref_stem->relative_coordinate (commonx, X_AXIS);
407   y_offset += calc_beam_y_shift (ref_stem, x_coord - ref_stem_x);
408
409   /*
410     Check if the number is between the beam and the staff.  If so, it will collide
411     with ledger lines.  Move it into the staff.
412   */
413   if (Grob *st = Staff_symbol_referencer::get_staff_symbol (ref_stem))
414     {
415       Interval staff_ext_y = st->extent (commony, Y_AXIS);
416       bool move = ref_stem_dir == DOWN
417                   ? ref_stem_ext[DOWN] > staff_ext_y[UP]
418                   : staff_ext_y[DOWN] > ref_stem_ext[UP];
419       if (move)
420         {
421           Interval ledger_domain = Interval (std::min (staff_ext_y[UP], ref_stem_ext[UP]),
422                                              std::max (staff_ext_y[DOWN], ref_stem_ext[DOWN]));
423           Interval num_y (me->extent (commony, Y_AXIS));
424           num_y.translate (y_offset);
425           Interval num_ledger_overlap (num_y);
426           num_ledger_overlap.intersect (ledger_domain);
427           Real line_thickness = Staff_symbol_referencer::line_thickness (st);
428           Real staff_space = Staff_symbol_referencer::staff_space (st);
429           // Number will touch outer staff line.
430           if (!num_ledger_overlap.is_empty ()
431               && num_ledger_overlap.length () > (staff_space / 2.0)
432               && move)
433             y_offset += staff_ext_y[-ref_stem_dir] - num_y[-ref_stem_dir]
434                         + line_thickness * ref_stem_dir;
435         }
436     }
437
438   /*
439     Now consider possible collisions with accidentals on the right.  We
440     move the accidental away from the beam.
441   */
442   Drul_array<Grob *> adj_cols = adjacent_note_columns (me, ref_stem);
443
444   if (!adj_cols[RIGHT])
445     return scm_from_double (y_offset);
446
447   /*
448     Collect Y-extents of accidentals that overlap the number
449     along the X-axis.
450   */
451   extract_grob_set (adj_cols[RIGHT], "note-heads", heads);
452   Interval colliding_acc_ext_y;
453
454   for (vsize i = 0; i < heads.size (); i++)
455     if (Grob *acc = unsmob<Grob> (heads[i]->get_object ("accidental-grob")))
456       {
457         commony = commony->common_refpoint (acc, Y_AXIS);
458         Interval acc_ext_y = acc->extent (commony, Y_AXIS);
459
460         commonx = commonx->common_refpoint (acc, X_AXIS);
461         Interval num_ext_x = me->extent (commonx, X_AXIS);
462         num_ext_x.widen (padding);
463         Interval overlap_x (num_ext_x);
464         Interval acc_x = acc->extent (commonx, X_AXIS);
465         overlap_x.intersect (acc_x);
466
467         if (!overlap_x.is_empty ())
468           colliding_acc_ext_y.unite (acc_ext_y);
469       }
470   /*
471     Does our number intersect vertically with the accidental Y-extents we
472     combined above?  If so, move it.
473   */
474   Interval overlap_acc_y (colliding_acc_ext_y);
475   Interval num_ext_y (me->extent (commony, Y_AXIS));
476   num_ext_y.translate (y_offset);
477   overlap_acc_y.intersect (num_ext_y);
478
479   if (!overlap_acc_y.is_empty ())
480     y_offset += colliding_acc_ext_y[ref_stem_dir] - num_ext_y[-ref_stem_dir] + padding * ref_stem_dir;
481
482   return scm_from_double (y_offset);
483 }
484
485 MAKE_SCHEME_CALLBACK (Tuplet_number, calc_cross_staff, 1)
486 SCM
487 Tuplet_number::calc_cross_staff (SCM smob)
488 {
489   Grob *me = unsmob<Grob> (smob);
490   return unsmob<Grob> (me->get_object ("bracket"))->get_property ("cross-staff");
491 }
492
493 ADD_INTERFACE (Tuplet_number,
494                "The number for a bracket.",
495
496                /* properties */
497                "avoid-slur "    // UGH.
498                "bracket "
499                "direction "
500                "knee-to-beam "
501               );
502