]> git.donarmstrong.com Git - lilypond.git/blob - lily/stem.cc
Revert "Issue 4550 (1/2) Avoid "using namespace std;" in included files"
[lilypond.git] / lily / stem.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 1996--2015 Han-Wen Nienhuys <hanwen@xs4all.nl>
5   Jan Nieuwenhuizen <janneke@gnu.org>
6
7   TODO: This is way too hairy
8
9   TODO: fix naming.
10
11   Stem-end, chord-start, etc. is all confusing naming.
12
13   LilyPond is free software: you can redistribute it and/or modify
14   it under the terms of the GNU General Public License as published by
15   the Free Software Foundation, either version 3 of the License, or
16   (at your option) any later version.
17
18   LilyPond is distributed in the hope that it will be useful,
19   but WITHOUT ANY WARRANTY; without even the implied warranty of
20   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21   GNU General Public License for more details.
22
23   You should have received a copy of the GNU General Public License
24   along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
25 */
26
27 /*
28   Note that several internal functions have a calc_beam bool argument.
29   This argument means: "If set, acknowledge the fact that there is a beam
30   and deal with it.  If not, give me the measurements as if there is no beam."
31   Most pure functions are called WITHOUT calc_beam, whereas non-pure functions
32   are called WITH calc_beam.
33
34   The only exception to this is ::pure_height, which calls internal_pure_height
35   with "true" for calc_beam in order to trigger the calculations of other
36   pure heights in case there is a beam.  It passes false, however, to
37   internal_height and internal_pure_height for all subsequent iterations.
38 */
39
40 #include "stem.hh"
41 #include "spanner.hh"
42
43 #include <cmath>                // rint
44 using namespace std;
45
46 #include "beam.hh"
47 #include "directional-element-interface.hh"
48 #include "dot-column.hh"
49 #include "font-interface.hh"
50 #include "international.hh"
51 #include "lookup.hh"
52 #include "misc.hh"
53 #include "note-head.hh"
54 #include "output-def.hh"
55 #include "paper-column.hh"
56 #include "pointer-group-interface.hh"
57 #include "rest.hh"
58 #include "rhythmic-head.hh"
59 #include "side-position-interface.hh"
60 #include "staff-symbol-referencer.hh"
61 #include "stem-tremolo.hh"
62 #include "warn.hh"
63
64 void
65 Stem::set_beaming (Grob *me, int beam_count, Direction d)
66 {
67   SCM pair = me->get_property ("beaming");
68
69   if (!scm_is_pair (pair))
70     {
71       pair = scm_cons (SCM_EOL, SCM_EOL);
72       me->set_property ("beaming", pair);
73     }
74
75   SCM lst = index_get_cell (pair, d);
76   if (beam_count)
77     for (int i = 0; i < beam_count; i++)
78       lst = scm_cons (scm_from_int (i), lst);
79   else
80     lst = SCM_BOOL_F;
81
82   index_set_cell (pair, d, lst);
83 }
84
85 int
86 Stem::get_beaming (Grob *me, Direction d)
87 {
88   SCM pair = me->get_property ("beaming");
89   if (!scm_is_pair (pair))
90     return 0;
91
92   SCM lst = index_get_cell (pair, d);
93
94   int len = scm_ilength (lst);
95   return max (len, 0);
96 }
97
98 Interval
99 Stem::head_positions (Grob *me)
100 {
101   if (head_count (me))
102     {
103       Drul_array<Grob *> e (extremal_heads (me));
104       return Interval (Staff_symbol_referencer::get_position (e[DOWN]),
105                        Staff_symbol_referencer::get_position (e[UP]));
106     }
107   return Interval ();
108 }
109
110 Real
111 Stem::chord_start_y (Grob *me)
112 {
113   if (head_count (me))
114     return Staff_symbol_referencer::get_position (last_head (me))
115       * Staff_symbol_referencer::staff_space (me) * 0.5;
116
117   return 0;
118 }
119
120 void
121 Stem::set_stem_positions (Grob *me, Real se)
122 {
123   // todo: margins
124   Direction d = get_grob_direction (me);
125
126   Grob *beam = unsmob<Grob> (me->get_object ("beam"));
127   if (d && d * head_positions (me)[get_grob_direction (me)] >= se * d)
128     me->warning (_ ("weird stem size, check for narrow beams"));
129
130   // trigger note collision mechanisms
131   Real stem_beg = internal_calc_stem_begin_position (me, false);
132   Real staff_space = Staff_symbol_referencer::staff_space (me);
133   Real half_space = staff_space * 0.5;
134
135   Interval height;
136   height[-d] = stem_beg * half_space;
137   height[d] = se * half_space + beam_end_corrective (me);
138
139   Real stemlet_length = robust_scm2double (me->get_property ("stemlet-length"),
140                                            0.0);
141   bool stemlet = stemlet_length > 0.0;
142
143   Grob *lh = get_reference_head (me);
144
145   if (!lh)
146     {
147       if (stemlet && beam)
148         {
149           Real beam_translation = Beam::get_beam_translation (beam);
150           Real beam_thickness = Beam::get_beam_thickness (beam);
151           int beam_count = beam_multiplicity (me).length () + 1;
152
153           height[-d] = (height[d] - d
154                         * (0.5 * beam_thickness
155                            + beam_translation * max (0, (beam_count - 1))
156                            + stemlet_length));
157         }
158       else if (!stemlet && beam)
159         height[-d] = height[d];
160       else if (stemlet && !beam)
161         me->programming_error ("Can't have a stemlet without a beam.");
162     }
163
164   me->set_property ("stem-begin-position", scm_from_double (height[-d] * 2 / staff_space));
165   me->set_property ("length", scm_from_double (height.length () * 2 / staff_space));
166 }
167
168 /* Note head that determines hshift for upstems
169    WARNING: triggers direction  */
170 Grob *
171 Stem::support_head (Grob *me)
172 {
173   extract_grob_set (me, "note-heads", heads);
174   if (heads.size () == 1)
175     return heads[0];
176
177   return first_head (me);
178 }
179
180 int
181 Stem::head_count (Grob *me)
182 {
183   return Pointer_group_interface::count (me, ly_symbol2scm ("note-heads"));
184 }
185
186 /* The note head which forms one end of the stem.
187    WARNING: triggers direction  */
188 Grob *
189 Stem::first_head (Grob *me)
190 {
191   Direction d = get_grob_direction (me);
192   if (d)
193     return extremal_heads (me)[-d];
194   return 0;
195 }
196
197 /* The note head opposite to the first head.  */
198 Grob *
199 Stem::last_head (Grob *me)
200 {
201   Direction d = get_grob_direction (me);
202   if (d)
203     return extremal_heads (me)[d];
204   return 0;
205 }
206
207 /*
208   START is part where stem reaches `last' head.
209
210   This function returns a drul with (bottom-head, top-head).
211 */
212 Drul_array<Grob *>
213 Stem::extremal_heads (Grob *me)
214 {
215   const int inf = INT_MAX;
216   Drul_array<int> extpos;
217   extpos[DOWN] = inf;
218   extpos[UP] = -inf;
219
220   Drul_array<Grob *> exthead (0, 0);
221   extract_grob_set (me, "note-heads", heads);
222
223   for (vsize i = heads.size (); i--;)
224     {
225       Grob *n = heads[i];
226       int p = Staff_symbol_referencer::get_rounded_position (n);
227
228       for (LEFT_and_RIGHT (d))
229         {
230           if (d * p > d * extpos[d])
231             {
232               exthead[d] = n;
233               extpos[d] = p;
234             }
235         }
236     }
237   return exthead;
238 }
239
240 /* The staff positions, in ascending order.
241  * If FILTER, include the main column of noteheads only */
242 vector<int>
243 Stem::note_head_positions (Grob *me, bool filter)
244 {
245   vector<int> ps;
246   extract_grob_set (me, "note-heads", heads);
247   Grob *xref = common_refpoint_of_array (heads, me, X_AXIS);
248
249   for (vsize i = heads.size (); i--;)
250     {
251       Grob *n = heads[i];
252       if (filter
253           && n->relative_coordinate (xref, X_AXIS) != 0.0)
254         continue;
255
256       int p = Staff_symbol_referencer::get_rounded_position (n);
257       ps.push_back (p);
258     }
259
260   vector_sort (ps, less<int> ());
261   return ps;
262 }
263
264 void
265 Stem::add_head (Grob *me, Grob *n)
266 {
267   n->set_object ("stem", me->self_scm ());
268
269   if (has_interface<Note_head> (n))
270     Pointer_group_interface::add_grob (me, ly_symbol2scm ("note-heads"), n);
271   else if (has_interface<Rest> (n))
272     Pointer_group_interface::add_grob (me, ly_symbol2scm ("rests"), n);
273 }
274
275 bool
276 Stem::is_invisible (Grob *me)
277 {
278   if (is_normal_stem (me))
279     return false;
280   else if (head_count (me))
281     return true;
282   else // if there are no note-heads, we might want stemlets
283     return 0.0 == robust_scm2double (me->get_property ("stemlet-length"), 0.0);
284 }
285
286 bool
287 Stem::is_normal_stem (Grob *me)
288 {
289   if (!head_count (me))
290     return false;
291
292   return scm_to_int (me->get_property ("duration-log")) >= 1;
293 }
294
295 MAKE_SCHEME_CALLBACK (Stem, pure_height, 3)
296 SCM
297 Stem::pure_height (SCM smob,
298                    SCM /* start */,
299                    SCM /* end */)
300 {
301   Grob *me = unsmob<Grob> (smob);
302   return ly_interval2scm (internal_pure_height (me, true));
303 }
304
305 Interval
306 Stem::internal_pure_height (Grob *me, bool calc_beam)
307 {
308   if (!is_normal_stem (me))
309     return Interval (0.0, 0.0);
310
311   Grob *beam = unsmob<Grob> (me->get_object ("beam"));
312
313   Interval iv = internal_height (me, false);
314
315   if (!beam)
316     return iv;
317   if (calc_beam)
318     {
319       Interval overshoot;
320       Direction dir = get_grob_direction (me);
321       for (DOWN_and_UP (d))
322         overshoot[d] = d == dir ? dir * infinity_f : iv[d];
323
324       vector<Interval> heights;
325       vector<Grob *> my_stems;
326       extract_grob_set (beam, "normal-stems", normal_stems);
327       for (vsize i = 0; i < normal_stems.size (); i++)
328         if (get_grob_direction (normal_stems[i]) == dir)
329           {
330             if (normal_stems[i] != me)
331               heights.push_back (Stem::internal_pure_height (normal_stems[i], false));
332             else
333               heights.push_back (iv);
334             my_stems.push_back (normal_stems[i]);
335           }
336       //iv.unite (heights.back ());
337       // look for cross staff effects
338       vector<Real> coords;
339       Grob *common = common_refpoint_of_array (my_stems, me, Y_AXIS);
340       Real min_pos = infinity_f;
341       Real max_pos = -infinity_f;
342       for (vsize i = 0; i < my_stems.size (); i++)
343         {
344           coords.push_back (my_stems[i]->pure_relative_y_coordinate (common, 0, INT_MAX));
345           min_pos = min (min_pos, coords[i]);
346           max_pos = max (max_pos, coords[i]);
347         }
348       for (vsize i = 0; i < heights.size (); i++)
349         {
350           heights[i][dir] += dir == DOWN
351                              ? coords[i] - max_pos
352                              : coords[i] - min_pos;
353         }
354
355       for (vsize i = 0; i < heights.size (); i++) iv.unite (heights[i]);
356
357       for (vsize i = 0; i < my_stems.size (); i++)
358         cache_pure_height (my_stems[i], iv, heights[i]);
359       iv.intersect (overshoot);
360     }
361
362   return iv;
363 }
364
365 void
366 Stem::cache_pure_height (Grob *me, Interval iv, Interval my_iv)
367 {
368   Interval overshoot;
369   Direction dir = get_grob_direction (me);
370   for (DOWN_and_UP (d))
371     overshoot[d] = d == dir ? dir * infinity_f : my_iv[d];
372
373   iv.intersect (overshoot);
374   dynamic_cast<Item *> (me)->cache_pure_height (iv);
375 }
376
377 MAKE_SCHEME_CALLBACK (Stem, calc_stem_end_position, 1)
378 SCM
379 Stem::calc_stem_end_position (SCM smob)
380 {
381   Grob *me = unsmob<Grob> (smob);
382   return scm_from_double (internal_calc_stem_end_position (me, true));
383 }
384
385 MAKE_SCHEME_CALLBACK (Stem, pure_calc_stem_end_position, 3)
386 SCM
387 Stem::pure_calc_stem_end_position (SCM smob,
388                                    SCM, /* start */
389                                    SCM /* end */)
390 {
391   Grob *me = unsmob<Grob> (smob);
392   return scm_from_double (internal_calc_stem_end_position (me, false));
393 }
394
395 Real
396 Stem::internal_calc_stem_end_position (Grob *me, bool calc_beam)
397 {
398   if (!head_count (me))
399     return 0.0;
400
401   Grob *beam = get_beam (me);
402   Real ss = Staff_symbol_referencer::staff_space (me);
403   Direction dir = get_grob_direction (me);
404
405   if (beam && calc_beam)
406     {
407       (void) beam->get_property ("quantized-positions");
408       return robust_scm2double (me->get_property ("length"), 0.0)
409              + dir * robust_scm2double (me->get_property ("stem-begin-position"), 0.0);
410     }
411
412   vector<Real> a;
413
414   /* WARNING: IN HALF SPACES */
415   SCM details = me->get_property ("details");
416   int durlog = duration_log (me);
417
418   Real staff_rad = Staff_symbol_referencer::staff_radius (me);
419   Real length = 7;
420   SCM s = ly_assoc_get (ly_symbol2scm ("lengths"), details, SCM_EOL);
421   if (scm_is_pair (s))
422     length = 2 * scm_to_double (robust_list_ref (durlog - 2, s));
423
424   /* Stems in unnatural (forced) direction should be shortened,
425      according to [Roush & Gourlay] */
426   Interval hp = head_positions (me);
427   if (dir && dir * hp[dir] >= 0)
428     {
429       SCM sshorten = ly_assoc_get (ly_symbol2scm ("stem-shorten"), details, SCM_EOL);
430       SCM scm_shorten = scm_is_pair (sshorten)
431                         ? robust_list_ref (max (duration_log (me) - 2, 0), sshorten) : SCM_EOL;
432       Real shorten_property = 2 * robust_scm2double (scm_shorten, 0);
433       /*  change in length between full-size and shortened stems is executed gradually.
434           "transition area" = stems between full-sized and fully-shortened.
435           */
436       Real quarter_stem_length = 2 * scm_to_double (robust_list_ref (0, s));
437       /*  shortening_step = difference in length between consecutive stem lengths
438           in transition area. The bigger the difference between full-sized
439           and shortened stems, the bigger shortening_step is.
440           (but not greater than 1/2 and not smaller than 1/4).
441           value 6 is heuristic; it determines the suggested transition slope steepnesas.
442           */
443       Real shortening_step = min (max (0.25, (shorten_property / 6)), 0.5);
444       /*  Shortening of unflagged stems should begin on the first stem that sticks
445           more than 1 staffspace (2 units) out of the staff.
446           Shortening of flagged stems begins in the same moment as unflagged ones,
447           but not earlier than on the middle line note.
448           */
449       Real which_step = (min (1.0, quarter_stem_length - (2 * staff_rad) - 2.0)) + abs (hp[dir]);
450       Real shorten = min (max (0.0, (shortening_step * which_step)), shorten_property);
451
452       length -= shorten;
453     }
454
455   length *= robust_scm2double (me->get_property ("length-fraction"), 1.0);
456
457   /* Tremolo stuff.  */
458   Grob *t_flag = unsmob<Grob> (me->get_object ("tremolo-flag"));
459   if (t_flag && (!unsmob<Grob> (me->get_object ("beam")) || !calc_beam))
460     {
461       /* Crude hack: add extra space if tremolo flag is there.
462
463       We can't do this for the beam, since we get into a loop
464       (Stem_tremolo::raw_stencil () looks at the beam.) --hwn  */
465
466       Real minlen = 1.0
467                     + 2 * Stem_tremolo::vertical_length (t_flag) / ss;
468
469       /* We don't want to add the whole extent of the flag because the trem
470          and the flag can overlap partly. beam_translation gives a good
471          approximation */
472       if (durlog >= 3)
473         {
474           Real beam_trans = Stem_tremolo::get_beam_translation (t_flag);
475           /* the obvious choice is (durlog - 2) here, but we need a bit more space. */
476           minlen += 2 * (durlog - 1.5) * beam_trans;
477
478           /* up-stems need even a little more space to avoid collisions. This
479              needs to be in sync with the tremolo positioning code in
480              Stem_tremolo::print */
481           if (dir == UP)
482             minlen += beam_trans;
483         }
484       length = max (length, minlen + 1.0);
485     }
486
487   Real stem_end = dir ? hp[dir] + dir * length : 0;
488
489   /* TODO: change name  to extend-stems to staff/center/'()  */
490   bool no_extend = to_boolean (me->get_property ("no-stem-extend"));
491   if (!no_extend && dir * stem_end < 0)
492     stem_end = 0.0;
493
494   return stem_end;
495 }
496
497 /* The log of the duration (Number of hooks on the flag minus two)  */
498 int
499 Stem::duration_log (Grob *me)
500 {
501   SCM s = me->get_property ("duration-log");
502   return (scm_is_number (s)) ? scm_to_int (s) : 2;
503 }
504
505 MAKE_SCHEME_CALLBACK (Stem, calc_positioning_done, 1);
506 SCM
507 Stem::calc_positioning_done (SCM smob)
508 {
509   Grob *me = unsmob<Grob> (smob);
510   if (!head_count (me))
511     return SCM_BOOL_T;
512
513   me->set_property ("positioning-done", SCM_BOOL_T);
514
515   extract_grob_set (me, "note-heads", ro_heads);
516   vector<Grob *> heads (ro_heads);
517   vector_sort (heads, position_less);
518   Direction dir = get_grob_direction (me);
519
520   if (dir < 0)
521     reverse (heads);
522
523   Real thick = thickness (me);
524
525   Grob *hed = support_head (me);
526   if (!dir)
527     {
528       programming_error ("Stem dir must be up or down.");
529       dir = UP;
530       set_grob_direction (me, dir);
531     }
532
533   bool is_harmonic_centered = false;
534   for (vsize i = 0; i < heads.size (); i++)
535     is_harmonic_centered = is_harmonic_centered
536                            || scm_is_eq (heads[i]->get_property ("style"),
537                                          ly_symbol2scm ("harmonic"));
538   is_harmonic_centered = is_harmonic_centered && is_invisible (me);
539
540   Real w = hed->extent (hed, X_AXIS)[dir];
541   for (vsize i = 0; i < heads.size (); i++)
542     {
543       Real amount = w - heads[i]->extent (heads[i], X_AXIS)[dir];
544
545       if (is_harmonic_centered)
546         amount
547           = hed->extent (hed, X_AXIS).linear_combination (CENTER)
548             - heads[i]->extent (heads[i], X_AXIS).linear_combination (CENTER);
549
550       if (!isnan (amount)) // empty heads can produce NaN
551         heads[i]->translate_axis (amount, X_AXIS);
552     }
553   bool parity = true;
554   Real lastpos = Real (Staff_symbol_referencer::get_position (heads[0]));
555   for (vsize i = 1; i < heads.size (); i++)
556     {
557       Real p = Staff_symbol_referencer::get_position (heads[i]);
558       Real dy = fabs (lastpos - p);
559
560       /*
561         dy should always be 0.5, 0.0, 1.0, but provide safety margin
562         for rounding errors.
563       */
564       if (dy < 1.1)
565         {
566           if (parity)
567             {
568               Real ell = heads[i]->extent (heads[i], X_AXIS).length ();
569
570               Direction d = get_grob_direction (me);
571               /*
572                 Reversed head should be shifted ell-thickness, but this
573                 looks too crowded, so we only shift ell-0.5*thickness.
574
575                 This leads to assymetry: Normal heads overlap the
576                 stem 100% whereas reversed heads only overlaps the
577                 stem 50%
578               */
579               Real reverse_overlap = 0.5;
580
581               /*
582                 However, the first reverse head has to be shifted even
583                 more than the full reverse overlap if it is the same
584                 height as the first head or there will be a gap
585                 because of the head slant (issue 346).
586               */
587
588               if (i == 1 && dy < 0.1)
589                 reverse_overlap = 1.1;
590
591               if (is_invisible (me))
592                 {
593                   // Semibreves and longer are tucked in considerably
594                   // to be recognizable as chorded rather than
595                   // parallel voices.  During the course of issue 346
596                   // there was a discussion to change this for unisons
597                   // (dy < 0.1) to reduce overlap but without reaching
598                   // agreement and with Gould being rather on the
599                   // overlapping front.
600                   reverse_overlap = 2;
601                 }
602
603               heads[i]->translate_axis ((ell - thick * reverse_overlap) * d,
604                                         X_AXIS);
605
606               /* TODO:
607
608               For some cases we should kern some more: when the
609               distance between the next or prev note is too large, we'd
610               get large white gaps, eg.
611
612               |
613               X|
614               |X  <- kern this.
615               |
616               X
617
618               */
619             }
620           parity = !parity;
621         }
622       else
623         parity = true;
624
625       lastpos = int (p);
626     }
627
628   return SCM_BOOL_T;
629 }
630
631 MAKE_SCHEME_CALLBACK (Stem, calc_direction, 1);
632 SCM
633 Stem::calc_direction (SCM smob)
634 {
635   Grob *me = unsmob<Grob> (smob);
636   Direction dir = CENTER;
637   if (Grob *beam = unsmob<Grob> (me->get_object ("beam")))
638     {
639       SCM ignore_me = beam->get_property ("direction");
640       (void) ignore_me;
641       dir = get_grob_direction (me);
642     }
643   else
644     {
645       SCM dd = me->get_property ("default-direction");
646       dir = to_dir (dd);
647       if (!dir)
648         return me->get_property ("neutral-direction");
649     }
650
651   return scm_from_int (dir);
652 }
653
654 MAKE_SCHEME_CALLBACK (Stem, calc_default_direction, 1);
655 SCM
656 Stem::calc_default_direction (SCM smob)
657 {
658   Grob *me = unsmob<Grob> (smob);
659
660   Direction dir = CENTER;
661   int staff_center = 0;
662   if (head_count (me))
663     {
664       Interval hp = head_positions (me);
665       int udistance = (int) (UP * hp[UP] - staff_center);
666       int ddistance = (int) (DOWN * hp[DOWN] - staff_center);
667
668       dir = Direction (sign (ddistance - udistance));
669     }
670
671   return scm_from_int (dir);
672 }
673
674 // note - height property necessary to trigger quantized beam positions
675 // otherwise, we could just use Grob::stencil_height_proc
676 MAKE_SCHEME_CALLBACK (Stem, height, 1);
677 SCM
678 Stem::height (SCM smob)
679 {
680   Grob *me = unsmob<Grob> (smob);
681   return ly_interval2scm (internal_height (me, true));
682 }
683
684 Grob *
685 Stem::get_reference_head (Grob *me)
686 {
687   return to_boolean (me->get_property ("avoid-note-head"))
688          ? last_head (me)
689          : first_head (me);
690 }
691
692 Real
693 Stem::beam_end_corrective (Grob *me)
694 {
695   Grob *beam = unsmob<Grob> (me->get_object ("beam"));
696   Direction dir = get_grob_direction (me);
697   if (beam)
698     {
699       if (dir == CENTER)
700         {
701           programming_error ("no stem direction");
702           dir = UP;
703         }
704       return dir * Beam::get_beam_thickness (beam) * 0.5;
705     }
706   return 0.0;
707 }
708
709 Interval
710 Stem::internal_height (Grob *me, bool calc_beam)
711 {
712   Grob *beam = get_beam (me);
713   if (!is_valid_stem (me) && !beam)
714     return Interval ();
715
716   Direction dir = get_grob_direction (me);
717
718   if (beam && calc_beam)
719     {
720       /* trigger set-stem-lengths. */
721       (void) beam->get_property ("quantized-positions");
722     }
723
724   /*
725     If there is a beam but no stem, slope calculations depend on this
726     routine to return where the stem end /would/ be.
727   */
728   if (calc_beam && !beam && !unsmob<Stencil> (me->get_property ("stencil")))
729     return Interval ();
730
731   Real y1 = robust_scm2double ((calc_beam
732                                 ? me->get_property ("stem-begin-position")
733                                 : me->get_pure_property ("stem-begin-position", 0, INT_MAX)),
734                                0.0);
735
736   Real y2 = dir * robust_scm2double ((calc_beam
737                                       ? me->get_property ("length")
738                                       : me->get_pure_property ("length", 0, INT_MAX)),
739                                      0.0)
740             + y1;
741
742   Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
743
744   Interval stem_y = Interval (min (y1, y2), max (y2, y1)) * half_space;
745
746   return stem_y;
747 }
748
749 MAKE_SCHEME_CALLBACK (Stem, width, 1);
750 SCM
751 Stem::width (SCM e)
752 {
753   Grob *me = unsmob<Grob> (e);
754
755   Interval r;
756
757   if (is_invisible (me))
758     r.set_empty ();
759   else
760     {
761       r = Interval (-1, 1);
762       r *= thickness (me) / 2;
763     }
764
765   return ly_interval2scm (r);
766 }
767
768 Real
769 Stem::thickness (Grob *me)
770 {
771   return scm_to_double (me->get_property ("thickness"))
772          * Staff_symbol_referencer::line_thickness (me);
773 }
774
775 MAKE_SCHEME_CALLBACK (Stem, calc_stem_begin_position, 1);
776 SCM
777 Stem::calc_stem_begin_position (SCM smob)
778 {
779   Grob *me = unsmob<Grob> (smob);
780   return scm_from_double (internal_calc_stem_begin_position (me, true));
781 }
782
783 MAKE_SCHEME_CALLBACK (Stem, pure_calc_stem_begin_position, 3);
784 SCM
785 Stem::pure_calc_stem_begin_position (SCM smob,
786                                      SCM, /* start */
787                                      SCM /* end */)
788 {
789   Grob *me = unsmob<Grob> (smob);
790   return scm_from_double (internal_calc_stem_begin_position (me, false));
791 }
792
793 Real
794 Stem::internal_calc_stem_begin_position (Grob *me, bool calc_beam)
795 {
796   Grob *beam = get_beam (me);
797   Real ss = Staff_symbol_referencer::staff_space (me);
798   if (beam && calc_beam)
799     {
800       (void) beam->get_property ("quantized-positions");
801       return robust_scm2double (me->get_property ("stem-begin-position"), 0.0);
802     }
803
804   Direction d = get_grob_direction (me);
805   Grob *lh = get_reference_head (me);
806
807   if (!lh)
808     return 0.0;
809
810   Real pos = Staff_symbol_referencer::get_position (lh);
811
812   if (Grob *head = support_head (me))
813     {
814       Interval head_height = head->extent (head, Y_AXIS);
815       Real y_attach = Note_head::stem_attachment_coordinate (head, Y_AXIS);
816
817       y_attach = head_height.linear_combination (y_attach);
818       if (!isinf (y_attach) && !isnan (y_attach)) // empty heads
819         pos += d * y_attach * 2 / ss;
820     }
821
822   return pos;
823 }
824
825
826 MAKE_SCHEME_CALLBACK (Stem, pure_calc_length, 3);
827 SCM
828 Stem::pure_calc_length (SCM smob, SCM /*start*/, SCM /*end*/)
829 {
830   Grob *me = unsmob<Grob> (smob);
831   Real beg = robust_scm2double (me->get_pure_property ("stem-begin-position", 0, INT_MAX), 0.0);
832   Real res = fabs (internal_calc_stem_end_position (me, false) - beg);
833   return scm_from_double (res);
834 }
835
836 MAKE_SCHEME_CALLBACK (Stem, calc_length, 1);
837 SCM
838 Stem::calc_length (SCM smob)
839 {
840   Grob *me = unsmob<Grob> (smob);
841   if (unsmob<Grob> (me->get_object ("beam")))
842     {
843       me->programming_error ("ly:stem::calc-length called but will not be used for beamed stem.");
844       return scm_from_double (0.0);
845     }
846
847   Real beg = robust_scm2double (me->get_property ("stem-begin-position"), 0.0);
848   Real res = fabs (internal_calc_stem_end_position (me, true) - beg);
849   return scm_from_double (res);
850 }
851
852 bool
853 Stem::is_valid_stem (Grob *me)
854 {
855   /* TODO: make the stem start a direction ?
856      This is required to avoid stems passing in tablature chords.  */
857   if (!me)
858     return false;
859   Grob *lh = get_reference_head (me);
860   Grob *beam = unsmob<Grob> (me->get_object ("beam"));
861
862   if (!lh && !beam)
863     return false;
864
865   if (is_invisible (me))
866     return false;
867
868   return true;
869 }
870
871 MAKE_SCHEME_CALLBACK (Stem, print, 1);
872 SCM
873 Stem::print (SCM smob)
874 {
875   Grob *me = unsmob<Grob> (smob);
876   if (!is_valid_stem (me))
877     return SCM_EOL;
878
879   Direction dir = get_grob_direction (me);
880   Real y1 = robust_scm2double (me->get_property ("stem-begin-position"), 0.0);
881   Real y2 = dir * robust_scm2double (me->get_property ("length"), 0.0) + y1;
882
883   Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
884
885   Interval stem_y = Interval (min (y1, y2), max (y2, y1)) * half_space;
886
887   stem_y[dir] -= beam_end_corrective (me);
888
889   // URG
890   Real stem_width = thickness (me);
891   Real blot
892     = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
893
894   Box b = Box (Interval (-stem_width / 2, stem_width / 2),
895                stem_y);
896
897   Stencil mol;
898   Stencil ss = Lookup::round_filled_box (b, blot);
899   mol.add_stencil (ss);
900
901   return mol.smobbed_copy ();
902 }
903
904 /*
905   move the stem to right of the notehead if it is up.
906 */
907 MAKE_SCHEME_CALLBACK (Stem, offset_callback, 1);
908 SCM
909 Stem::offset_callback (SCM smob)
910 {
911   Grob *me = unsmob<Grob> (smob);
912
913   extract_grob_set (me, "rests", rests);
914   if (rests.size ())
915     {
916       Grob *rest = rests.back ();
917       Real r = robust_relative_extent (rest, rest, X_AXIS).center ();
918       return scm_from_double (r);
919     }
920
921   if (Grob *f = first_head (me))
922     {
923       Interval head_wid = f->extent (f, X_AXIS);
924       Real attach = 0.0;
925
926       if (is_invisible (me))
927         attach = 0.0;
928       else
929         attach = Note_head::stem_attachment_coordinate (f, X_AXIS);
930
931       Direction d = get_grob_direction (me);
932       Real real_attach = head_wid.linear_combination (d * attach);
933       Real r = isnan(real_attach)? 0.0: real_attach;
934
935       /* If not centered: correct for stem thickness.  */
936       string style = robust_symbol2string (f->get_property ("style"), "default");
937       if (attach && style != "mensural"
938           && style != "neomensural"
939           && style != "petrucci")
940         {
941           Real rule_thick = thickness (me);
942           r += -d * rule_thick * 0.5;
943         }
944       return scm_from_double (r);
945     }
946
947   programming_error ("Weird stem.");
948   return scm_from_double (0.0);
949 }
950
951 Spanner *
952 Stem::get_beam (Grob *me)
953 {
954   SCM b = me->get_object ("beam");
955   return unsmob<Spanner> (b);
956 }
957
958 Stem_info
959 Stem::get_stem_info (Grob *me)
960 {
961   Stem_info si;
962   si.dir_ = get_grob_direction (me);
963
964   SCM scm_info = me->get_property ("stem-info");
965   si.ideal_y_ = scm_to_double (scm_car (scm_info));
966   si.shortest_y_ = scm_to_double (scm_cadr (scm_info));
967   return si;
968 }
969
970 MAKE_SCHEME_CALLBACK (Stem, calc_stem_info, 1);
971 SCM
972 Stem::calc_stem_info (SCM smob)
973 {
974   Grob *me = unsmob<Grob> (smob);
975   Direction my_dir = get_grob_direction (me);
976
977   if (!my_dir)
978     {
979       programming_error ("no stem dir set");
980       my_dir = UP;
981     }
982
983   Real staff_space = Staff_symbol_referencer::staff_space (me);
984   Grob *beam = get_beam (me);
985
986   if (beam)
987     {
988       (void) beam->get_property ("beaming");
989     }
990
991   Real beam_translation = Beam::get_beam_translation (beam);
992   Real beam_thickness = Beam::get_beam_thickness (beam);
993   int beam_count = Beam::get_direction_beam_count (beam, my_dir);
994   Real length_fraction
995     = robust_scm2double (me->get_property ("length-fraction"), 1.0);
996
997   /* Simple standard stem length */
998   SCM details = me->get_property ("details");
999   SCM lengths = ly_assoc_get (ly_symbol2scm ("beamed-lengths"), details, SCM_EOL);
1000
1001   Real ideal_length
1002     = (scm_is_pair (lengths)
1003        ? (scm_to_double (robust_list_ref (beam_count - 1, lengths))
1004           * staff_space
1005           * length_fraction
1006           /*
1007             stem only extends to center of beam
1008           */
1009           - 0.5 * beam_thickness)
1010        : 0.0);
1011
1012   /* Condition: sane minimum free stem length (chord to beams) */
1013   lengths = ly_assoc_get (ly_symbol2scm ("beamed-minimum-free-lengths"),
1014                           details, SCM_EOL);
1015
1016   Real ideal_minimum_free
1017     = (scm_is_pair (lengths)
1018        ? (scm_to_double (robust_list_ref (beam_count - 1, lengths))
1019           * staff_space
1020           * length_fraction)
1021        : 0.0);
1022
1023   Real height_of_my_trem = 0.0;
1024   Grob *trem = unsmob<Grob> (me->get_object ("tremolo-flag"));
1025   if (trem)
1026     {
1027       height_of_my_trem
1028         = Stem_tremolo::vertical_length (trem)
1029           /* hack a bit of space around the trem. */
1030           + beam_translation;
1031     }
1032
1033   /* UGH
1034      It seems that also for ideal minimum length, we must use
1035      the maximum beam count (for this direction):
1036
1037      \score { \relative c'' { a8[ a32] } }
1038
1039      must be horizontal. */
1040   Real height_of_my_beams = beam_thickness
1041                             + (beam_count - 1) * beam_translation;
1042
1043   Real ideal_minimum_length = ideal_minimum_free
1044                               + height_of_my_beams
1045                               + height_of_my_trem
1046                               /* stem only extends to center of beam */
1047                               - 0.5 * beam_thickness;
1048
1049   ideal_length = max (ideal_length, ideal_minimum_length);
1050
1051   /* Convert to Y position, calculate for dir == UP */
1052   Real note_start
1053     =     /* staff positions */
1054       head_positions (me)[my_dir] * 0.5
1055       * my_dir * staff_space;
1056   Real ideal_y = note_start + ideal_length;
1057
1058   /* Conditions for Y position */
1059
1060   /* Lowest beam of (UP) beam must never be lower than second staffline
1061
1062   Reference?
1063
1064   Although this (additional) rule is probably correct,
1065   I expect that highest beam (UP) should also never be lower
1066   than middle staffline, just as normal stems.
1067
1068   Reference?
1069
1070   Obviously not for grace beams.
1071
1072   Also, not for knees.  Seems to be a good thing. */
1073   bool no_extend = to_boolean (me->get_property ("no-stem-extend"));
1074   bool is_knee = Beam::is_knee (beam);
1075   if (!no_extend && !is_knee)
1076     {
1077       /* Highest beam of (UP) beam must never be lower than middle
1078          staffline */
1079       ideal_y = max (ideal_y, 0.0);
1080       /* Lowest beam of (UP) beam must never be lower than second staffline */
1081       ideal_y = max (ideal_y, (-staff_space
1082                                - beam_thickness + height_of_my_beams));
1083     }
1084
1085   ideal_y -= robust_scm2double (beam->get_property ("shorten"), 0);
1086
1087   SCM bemfl = ly_assoc_get (ly_symbol2scm ("beamed-extreme-minimum-free-lengths"),
1088                             details, SCM_EOL);
1089
1090   Real minimum_free
1091     = (scm_is_pair (bemfl)
1092        ? (scm_to_double (robust_list_ref (beam_count - 1, bemfl))
1093           * staff_space
1094           * length_fraction)
1095        : 0.0);
1096
1097   Real minimum_length = max (minimum_free, height_of_my_trem)
1098                         + height_of_my_beams
1099                         /* stem only extends to center of beam */
1100                         - 0.5 * beam_thickness;
1101
1102   ideal_y *= my_dir;
1103   Real minimum_y = note_start + minimum_length;
1104   Real shortest_y = minimum_y * my_dir;
1105
1106   return scm_list_2 (scm_from_double (ideal_y),
1107                      scm_from_double (shortest_y));
1108 }
1109
1110 Slice
1111 Stem::beam_multiplicity (Grob *stem)
1112 {
1113   SCM beaming = stem->get_property ("beaming");
1114   Slice le = int_list_to_slice (scm_car (beaming));
1115   Slice ri = int_list_to_slice (scm_cdr (beaming));
1116   le.unite (ri);
1117   return le;
1118 }
1119
1120 bool
1121 Stem::is_cross_staff (Grob *stem)
1122 {
1123   Grob *beam = unsmob<Grob> (stem->get_object ("beam"));
1124   return beam && Beam::is_cross_staff (beam);
1125 }
1126
1127 MAKE_SCHEME_CALLBACK (Stem, calc_cross_staff, 1)
1128 SCM
1129 Stem::calc_cross_staff (SCM smob)
1130 {
1131   return scm_from_bool (is_cross_staff (unsmob<Grob> (smob)));
1132 }
1133
1134 Grob *
1135 Stem::flag (Grob *me)
1136 {
1137   return unsmob<Grob> (me->get_object ("flag"));
1138 }
1139
1140 /* FIXME:  Too many properties  */
1141 ADD_INTERFACE (Stem,
1142                "The stem represents the graphical stem.  In addition, it"
1143                " internally connects note heads, beams, and tremolos.  Rests"
1144                " and whole notes have invisible stems.\n"
1145                "\n"
1146                "The following properties may be set in the @code{details}"
1147                " list.\n"
1148                "\n"
1149                "@table @code\n"
1150                "@item beamed-lengths\n"
1151                "List of stem lengths given beam multiplicity.\n"
1152                "@item beamed-minimum-free-lengths\n"
1153                "List of normal minimum free stem lengths (chord to beams)"
1154                " given beam multiplicity.\n"
1155                "@item beamed-extreme-minimum-free-lengths\n"
1156                "List of extreme minimum free stem lengths (chord to beams)"
1157                " given beam multiplicity.\n"
1158                "@item lengths\n"
1159                "Default stem lengths.  The list gives a length for each"
1160                " flag count.\n"
1161                "@item stem-shorten\n"
1162                "How much a stem in a forced direction should be shortened."
1163                "  The list gives an amount depending on the number of flags"
1164                " and beams.\n"
1165                "@end table\n",
1166
1167                /* properties */
1168                "avoid-note-head "
1169                "beam "
1170                "beaming "
1171                "beamlet-default-length "
1172                "beamlet-max-length-proportion "
1173                "default-direction "
1174                "details "
1175                "direction "
1176                "double-stem-separation "
1177                "duration-log "
1178                "flag "
1179                "french-beaming "
1180                "length "
1181                "length-fraction "
1182                "max-beam-connect "
1183                "melody-spanner "
1184                "neutral-direction "
1185                "no-stem-extend "
1186                "note-heads "
1187                "positioning-done "
1188                "rests "
1189                "stem-begin-position "
1190                "stem-info "
1191                "stemlet-length "
1192                "thickness "
1193                "tremolo-flag "
1194                "tuplet-start "
1195               );
1196
1197 /****************************************************************/
1198
1199 Stem_info::Stem_info ()
1200 {
1201   ideal_y_ = shortest_y_ = 0;
1202   dir_ = CENTER;
1203 }
1204
1205 void
1206 Stem_info::scale (Real x)
1207 {
1208   ideal_y_ *= x;
1209   shortest_y_ *= x;
1210 }