]> git.donarmstrong.com Git - lilypond.git/blob - lily/stem.cc
Run grand replace for 2015.
[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   Interval hp = head_positions (me);
114   if (!hp.is_empty ())
115     return hp[get_grob_direction (me)] * Staff_symbol_referencer::staff_space (me)
116            * 0.5;
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 = Grob::unsmob (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 (Note_head::has_interface (n))
270     Pointer_group_interface::add_grob (me, ly_symbol2scm ("note-heads"), n);
271   else if (Rest::has_interface (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 = Grob::unsmob (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 = Grob::unsmob (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 = Grob::unsmob (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 = Grob::unsmob (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 = Grob::unsmob (me->get_object ("tremolo-flag"));
459   if (t_flag && (!Grob::is_smob (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 = Grob::unsmob (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                            || heads[i]->get_property ("style") == ly_symbol2scm ("harmonic");
537   is_harmonic_centered = is_harmonic_centered && is_invisible (me);
538
539   Real w = hed->extent (hed, X_AXIS)[dir];
540   for (vsize i = 0; i < heads.size (); i++)
541     {
542       Real amount = w - heads[i]->extent (heads[i], X_AXIS)[dir];
543
544       if (is_harmonic_centered)
545         amount
546           = hed->extent (hed, X_AXIS).linear_combination (CENTER)
547             - heads[i]->extent (heads[i], X_AXIS).linear_combination (CENTER);
548
549       if (!isnan (amount)) // empty heads can produce NaN
550         heads[i]->translate_axis (amount, X_AXIS);
551     }
552   bool parity = true;
553   Real lastpos = Real (Staff_symbol_referencer::get_position (heads[0]));
554   for (vsize i = 1; i < heads.size (); i++)
555     {
556       Real p = Staff_symbol_referencer::get_position (heads[i]);
557       Real dy = fabs (lastpos - p);
558
559       /*
560         dy should always be 0.5, 0.0, 1.0, but provide safety margin
561         for rounding errors.
562       */
563       if (dy < 1.1)
564         {
565           if (parity)
566             {
567               Real ell = heads[i]->extent (heads[i], X_AXIS).length ();
568
569               Direction d = get_grob_direction (me);
570               /*
571                 Reversed head should be shifted ell-thickness, but this
572                 looks too crowded, so we only shift ell-0.5*thickness.
573
574                 This leads to assymetry: Normal heads overlap the
575                 stem 100% whereas reversed heads only overlaps the
576                 stem 50%
577               */
578               Real reverse_overlap = 0.5;
579
580               /*
581                 However, the first reverse head has to be shifted even
582                 more than the full reverse overlap if it is the same
583                 height as the first head or there will be a gap
584                 because of the head slant (issue 346).
585               */
586
587               if (i == 1 && dy < 0.1)
588                 reverse_overlap = 1.1;
589
590               if (is_invisible (me))
591                 {
592                   // Semibreves and longer are tucked in considerably
593                   // to be recognizable as chorded rather than
594                   // parallel voices.  During the course of issue 346
595                   // there was a discussion to change this for unisons
596                   // (dy < 0.1) to reduce overlap but without reaching
597                   // agreement and with Gould being rather on the
598                   // overlapping front.
599                   reverse_overlap = 2;
600                 }
601
602               heads[i]->translate_axis ((ell - thick * reverse_overlap) * d,
603                                         X_AXIS);
604
605               /* TODO:
606
607               For some cases we should kern some more: when the
608               distance between the next or prev note is too large, we'd
609               get large white gaps, eg.
610
611               |
612               X|
613               |X  <- kern this.
614               |
615               X
616
617               */
618             }
619           parity = !parity;
620         }
621       else
622         parity = true;
623
624       lastpos = int (p);
625     }
626
627   return SCM_BOOL_T;
628 }
629
630 MAKE_SCHEME_CALLBACK (Stem, calc_direction, 1);
631 SCM
632 Stem::calc_direction (SCM smob)
633 {
634   Grob *me = Grob::unsmob (smob);
635   Direction dir = CENTER;
636   if (Grob *beam = Grob::unsmob (me->get_object ("beam")))
637     {
638       SCM ignore_me = beam->get_property ("direction");
639       (void) ignore_me;
640       dir = get_grob_direction (me);
641     }
642   else
643     {
644       SCM dd = me->get_property ("default-direction");
645       dir = to_dir (dd);
646       if (!dir)
647         return me->get_property ("neutral-direction");
648     }
649
650   return scm_from_int (dir);
651 }
652
653 MAKE_SCHEME_CALLBACK (Stem, calc_default_direction, 1);
654 SCM
655 Stem::calc_default_direction (SCM smob)
656 {
657   Grob *me = Grob::unsmob (smob);
658
659   Direction dir = CENTER;
660   int staff_center = 0;
661   Interval hp = head_positions (me);
662   if (!hp.is_empty ())
663     {
664       int udistance = (int) (UP * hp[UP] - staff_center);
665       int ddistance = (int) (DOWN * hp[DOWN] - staff_center);
666
667       dir = Direction (sign (ddistance - udistance));
668     }
669
670   return scm_from_int (dir);
671 }
672
673 // note - height property necessary to trigger quantized beam positions
674 // otherwise, we could just use Grob::stencil_height_proc
675 MAKE_SCHEME_CALLBACK (Stem, height, 1);
676 SCM
677 Stem::height (SCM smob)
678 {
679   Grob *me = Grob::unsmob (smob);
680   return ly_interval2scm (internal_height (me, true));
681 }
682
683 Grob *
684 Stem::get_reference_head (Grob *me)
685 {
686   return to_boolean (me->get_property ("avoid-note-head"))
687          ? last_head (me)
688          : first_head (me);
689 }
690
691 Real
692 Stem::beam_end_corrective (Grob *me)
693 {
694   Grob *beam = Grob::unsmob (me->get_object ("beam"));
695   Direction dir = get_grob_direction (me);
696   if (beam)
697     {
698       if (dir == CENTER)
699         {
700           programming_error ("no stem direction");
701           dir = UP;
702         }
703       return dir * Beam::get_beam_thickness (beam) * 0.5;
704     }
705   return 0.0;
706 }
707
708 Interval
709 Stem::internal_height (Grob *me, bool calc_beam)
710 {
711   Grob *beam = get_beam (me);
712   if (!is_valid_stem (me) && !beam)
713     return Interval ();
714
715   Direction dir = get_grob_direction (me);
716
717   if (beam && calc_beam)
718     {
719       /* trigger set-stem-lengths. */
720       (void) beam->get_property ("quantized-positions");
721     }
722
723   /*
724     If there is a beam but no stem, slope calculations depend on this
725     routine to return where the stem end /would/ be.
726   */
727   if (calc_beam && !beam && !Stencil::is_smob (me->get_property ("stencil")))
728     return Interval ();
729
730   Real y1 = robust_scm2double ((calc_beam
731                                 ? me->get_property ("stem-begin-position")
732                                 : me->get_pure_property ("stem-begin-position", 0, INT_MAX)),
733                                0.0);
734
735   Real y2 = dir * robust_scm2double ((calc_beam
736                                       ? me->get_property ("length")
737                                       : me->get_pure_property ("length", 0, INT_MAX)),
738                                      0.0)
739             + y1;
740
741   Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
742
743   Interval stem_y = Interval (min (y1, y2), max (y2, y1)) * half_space;
744
745   return stem_y;
746 }
747
748 MAKE_SCHEME_CALLBACK (Stem, width, 1);
749 SCM
750 Stem::width (SCM e)
751 {
752   Grob *me = Grob::unsmob (e);
753
754   Interval r;
755
756   if (is_invisible (me))
757     r.set_empty ();
758   else
759     {
760       r = Interval (-1, 1);
761       r *= thickness (me) / 2;
762     }
763
764   return ly_interval2scm (r);
765 }
766
767 Real
768 Stem::thickness (Grob *me)
769 {
770   return scm_to_double (me->get_property ("thickness"))
771          * Staff_symbol_referencer::line_thickness (me);
772 }
773
774 MAKE_SCHEME_CALLBACK (Stem, calc_stem_begin_position, 1);
775 SCM
776 Stem::calc_stem_begin_position (SCM smob)
777 {
778   Grob *me = Grob::unsmob (smob);
779   return scm_from_double (internal_calc_stem_begin_position (me, true));
780 }
781
782 MAKE_SCHEME_CALLBACK (Stem, pure_calc_stem_begin_position, 3);
783 SCM
784 Stem::pure_calc_stem_begin_position (SCM smob,
785                                      SCM, /* start */
786                                      SCM /* end */)
787 {
788   Grob *me = Grob::unsmob (smob);
789   return scm_from_double (internal_calc_stem_begin_position (me, false));
790 }
791
792 Real
793 Stem::internal_calc_stem_begin_position (Grob *me, bool calc_beam)
794 {
795   Grob *beam = get_beam (me);
796   Real ss = Staff_symbol_referencer::staff_space (me);
797   if (beam && calc_beam)
798     {
799       (void) beam->get_property ("quantized-positions");
800       return robust_scm2double (me->get_property ("stem-begin-position"), 0.0);
801     }
802
803   Direction d = get_grob_direction (me);
804   Grob *lh = get_reference_head (me);
805
806   if (!lh)
807     return 0.0;
808
809   Real pos = Staff_symbol_referencer::get_position (lh);
810
811   if (Grob *head = support_head (me))
812     {
813       Interval head_height = head->extent (head, Y_AXIS);
814       Real y_attach = Note_head::stem_attachment_coordinate (head, Y_AXIS);
815
816       y_attach = head_height.linear_combination (y_attach);
817       if (!isinf (y_attach) && !isnan (y_attach)) // empty heads
818         pos += d * y_attach * 2 / ss;
819     }
820
821   return pos;
822 }
823
824
825 MAKE_SCHEME_CALLBACK (Stem, pure_calc_length, 3);
826 SCM
827 Stem::pure_calc_length (SCM smob, SCM /*start*/, SCM /*end*/)
828 {
829   Grob *me = Grob::unsmob (smob);
830   Real beg = robust_scm2double (me->get_pure_property ("stem-begin-position", 0, INT_MAX), 0.0);
831   Real res = fabs (internal_calc_stem_end_position (me, false) - beg);
832   return scm_from_double (res);
833 }
834
835 MAKE_SCHEME_CALLBACK (Stem, calc_length, 1);
836 SCM
837 Stem::calc_length (SCM smob)
838 {
839   Grob *me = Grob::unsmob (smob);
840   if (Grob::is_smob (me->get_object ("beam")))
841     {
842       me->programming_error ("ly:stem::calc-length called but will not be used for beamed stem.");
843       return scm_from_double (0.0);
844     }
845
846   Real beg = robust_scm2double (me->get_property ("stem-begin-position"), 0.0);
847   Real res = fabs (internal_calc_stem_end_position (me, true) - beg);
848   return scm_from_double (res);
849 }
850
851 bool
852 Stem::is_valid_stem (Grob *me)
853 {
854   /* TODO: make the stem start a direction ?
855      This is required to avoid stems passing in tablature chords.  */
856   if (!me)
857     return false;
858   Grob *lh = get_reference_head (me);
859   Grob *beam = Grob::unsmob (me->get_object ("beam"));
860
861   if (!lh && !beam)
862     return false;
863
864   if (is_invisible (me))
865     return false;
866
867   return true;
868 }
869
870 MAKE_SCHEME_CALLBACK (Stem, print, 1);
871 SCM
872 Stem::print (SCM smob)
873 {
874   Grob *me = Grob::unsmob (smob);
875   if (!is_valid_stem (me))
876     return SCM_EOL;
877
878   Direction dir = get_grob_direction (me);
879   Real y1 = robust_scm2double (me->get_property ("stem-begin-position"), 0.0);
880   Real y2 = dir * robust_scm2double (me->get_property ("length"), 0.0) + y1;
881
882   Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
883
884   Interval stem_y = Interval (min (y1, y2), max (y2, y1)) * half_space;
885
886   stem_y[dir] -= beam_end_corrective (me);
887
888   // URG
889   Real stem_width = thickness (me);
890   Real blot
891     = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
892
893   Box b = Box (Interval (-stem_width / 2, stem_width / 2),
894                stem_y);
895
896   Stencil mol;
897   Stencil ss = Lookup::round_filled_box (b, blot);
898   mol.add_stencil (ss);
899
900   return mol.smobbed_copy ();
901 }
902
903 /*
904   move the stem to right of the notehead if it is up.
905 */
906 MAKE_SCHEME_CALLBACK (Stem, offset_callback, 1);
907 SCM
908 Stem::offset_callback (SCM smob)
909 {
910   Grob *me = Grob::unsmob (smob);
911
912   extract_grob_set (me, "rests", rests);
913   if (rests.size ())
914     {
915       Grob *rest = rests.back ();
916       Real r = rest->extent (rest, X_AXIS).center ();
917       return scm_from_double (r);
918     }
919
920   if (Grob *f = first_head (me))
921     {
922       Interval head_wid = f->extent (f, X_AXIS);
923       Real attach = 0.0;
924
925       if (is_invisible (me))
926         attach = 0.0;
927       else
928         attach = Note_head::stem_attachment_coordinate (f, X_AXIS);
929
930       Direction d = get_grob_direction (me);
931       Real real_attach = head_wid.linear_combination (d * attach);
932       Real r = isnan(real_attach)? 0.0: real_attach;
933
934       /* If not centered: correct for stem thickness.  */
935       string style = robust_symbol2string (f->get_property ("style"), "default");
936       if (attach && style != "mensural"
937           && style != "neomensural"
938           && style != "petrucci")
939         {
940           Real rule_thick = thickness (me);
941           r += -d * rule_thick * 0.5;
942         }
943       return scm_from_double (r);
944     }
945
946   programming_error ("Weird stem.");
947   return scm_from_double (0.0);
948 }
949
950 Spanner *
951 Stem::get_beam (Grob *me)
952 {
953   SCM b = me->get_object ("beam");
954   return dynamic_cast<Spanner *> (Grob::unsmob (b));
955 }
956
957 Stem_info
958 Stem::get_stem_info (Grob *me)
959 {
960   Stem_info si;
961   si.dir_ = get_grob_direction (me);
962
963   SCM scm_info = me->get_property ("stem-info");
964   si.ideal_y_ = scm_to_double (scm_car (scm_info));
965   si.shortest_y_ = scm_to_double (scm_cadr (scm_info));
966   return si;
967 }
968
969 MAKE_SCHEME_CALLBACK (Stem, calc_stem_info, 1);
970 SCM
971 Stem::calc_stem_info (SCM smob)
972 {
973   Grob *me = Grob::unsmob (smob);
974   Direction my_dir = get_grob_direction (me);
975
976   if (!my_dir)
977     {
978       programming_error ("no stem dir set");
979       my_dir = UP;
980     }
981
982   Real staff_space = Staff_symbol_referencer::staff_space (me);
983   Grob *beam = get_beam (me);
984
985   if (beam)
986     {
987       (void) beam->get_property ("beaming");
988     }
989
990   Real beam_translation = Beam::get_beam_translation (beam);
991   Real beam_thickness = Beam::get_beam_thickness (beam);
992   int beam_count = Beam::get_direction_beam_count (beam, my_dir);
993   Real length_fraction
994     = robust_scm2double (me->get_property ("length-fraction"), 1.0);
995
996   /* Simple standard stem length */
997   SCM details = me->get_property ("details");
998   SCM lengths = ly_assoc_get (ly_symbol2scm ("beamed-lengths"), details, SCM_EOL);
999
1000   Real ideal_length
1001     = (scm_is_pair (lengths)
1002        ? (scm_to_double (robust_list_ref (beam_count - 1, lengths))
1003           * staff_space
1004           * length_fraction
1005           /*
1006             stem only extends to center of beam
1007           */
1008           - 0.5 * beam_thickness)
1009        : 0.0);
1010
1011   /* Condition: sane minimum free stem length (chord to beams) */
1012   lengths = ly_assoc_get (ly_symbol2scm ("beamed-minimum-free-lengths"),
1013                           details, SCM_EOL);
1014
1015   Real ideal_minimum_free
1016     = (scm_is_pair (lengths)
1017        ? (scm_to_double (robust_list_ref (beam_count - 1, lengths))
1018           * staff_space
1019           * length_fraction)
1020        : 0.0);
1021
1022   Real height_of_my_trem = 0.0;
1023   Grob *trem = Grob::unsmob (me->get_object ("tremolo-flag"));
1024   if (trem)
1025     {
1026       height_of_my_trem
1027         = Stem_tremolo::vertical_length (trem)
1028           /* hack a bit of space around the trem. */
1029           + beam_translation;
1030     }
1031
1032   /* UGH
1033      It seems that also for ideal minimum length, we must use
1034      the maximum beam count (for this direction):
1035
1036      \score { \relative c'' { a8[ a32] } }
1037
1038      must be horizontal. */
1039   Real height_of_my_beams = beam_thickness
1040                             + (beam_count - 1) * beam_translation;
1041
1042   Real ideal_minimum_length = ideal_minimum_free
1043                               + height_of_my_beams
1044                               + height_of_my_trem
1045                               /* stem only extends to center of beam */
1046                               - 0.5 * beam_thickness;
1047
1048   ideal_length = max (ideal_length, ideal_minimum_length);
1049
1050   /* Convert to Y position, calculate for dir == UP */
1051   Real note_start
1052     =     /* staff positions */
1053       head_positions (me)[my_dir] * 0.5
1054       * my_dir * staff_space;
1055   Real ideal_y = note_start + ideal_length;
1056
1057   /* Conditions for Y position */
1058
1059   /* Lowest beam of (UP) beam must never be lower than second staffline
1060
1061   Reference?
1062
1063   Although this (additional) rule is probably correct,
1064   I expect that highest beam (UP) should also never be lower
1065   than middle staffline, just as normal stems.
1066
1067   Reference?
1068
1069   Obviously not for grace beams.
1070
1071   Also, not for knees.  Seems to be a good thing. */
1072   bool no_extend = to_boolean (me->get_property ("no-stem-extend"));
1073   bool is_knee = Beam::is_knee (beam);
1074   if (!no_extend && !is_knee)
1075     {
1076       /* Highest beam of (UP) beam must never be lower than middle
1077          staffline */
1078       ideal_y = max (ideal_y, 0.0);
1079       /* Lowest beam of (UP) beam must never be lower than second staffline */
1080       ideal_y = max (ideal_y, (-staff_space
1081                                - beam_thickness + height_of_my_beams));
1082     }
1083
1084   ideal_y -= robust_scm2double (beam->get_property ("shorten"), 0);
1085
1086   SCM bemfl = ly_assoc_get (ly_symbol2scm ("beamed-extreme-minimum-free-lengths"),
1087                             details, SCM_EOL);
1088
1089   Real minimum_free
1090     = (scm_is_pair (bemfl)
1091        ? (scm_to_double (robust_list_ref (beam_count - 1, bemfl))
1092           * staff_space
1093           * length_fraction)
1094        : 0.0);
1095
1096   Real minimum_length = max (minimum_free, height_of_my_trem)
1097                         + height_of_my_beams
1098                         /* stem only extends to center of beam */
1099                         - 0.5 * beam_thickness;
1100
1101   ideal_y *= my_dir;
1102   Real minimum_y = note_start + minimum_length;
1103   Real shortest_y = minimum_y * my_dir;
1104
1105   return scm_list_2 (scm_from_double (ideal_y),
1106                      scm_from_double (shortest_y));
1107 }
1108
1109 Slice
1110 Stem::beam_multiplicity (Grob *stem)
1111 {
1112   SCM beaming = stem->get_property ("beaming");
1113   Slice le = int_list_to_slice (scm_car (beaming));
1114   Slice ri = int_list_to_slice (scm_cdr (beaming));
1115   le.unite (ri);
1116   return le;
1117 }
1118
1119 bool
1120 Stem::is_cross_staff (Grob *stem)
1121 {
1122   Grob *beam = Grob::unsmob (stem->get_object ("beam"));
1123   return beam && Beam::is_cross_staff (beam);
1124 }
1125
1126 MAKE_SCHEME_CALLBACK (Stem, calc_cross_staff, 1)
1127 SCM
1128 Stem::calc_cross_staff (SCM smob)
1129 {
1130   return scm_from_bool (is_cross_staff (Grob::unsmob (smob)));
1131 }
1132
1133 Grob *
1134 Stem::flag (Grob *me)
1135 {
1136   return Grob::unsmob (me->get_object ("flag"));
1137 }
1138
1139 /* FIXME:  Too many properties  */
1140 ADD_INTERFACE (Stem,
1141                "The stem represents the graphical stem.  In addition, it"
1142                " internally connects note heads, beams, and tremolos.  Rests"
1143                " and whole notes have invisible stems.\n"
1144                "\n"
1145                "The following properties may be set in the @code{details}"
1146                " list.\n"
1147                "\n"
1148                "@table @code\n"
1149                "@item beamed-lengths\n"
1150                "List of stem lengths given beam multiplicity.\n"
1151                "@item beamed-minimum-free-lengths\n"
1152                "List of normal minimum free stem lengths (chord to beams)"
1153                " given beam multiplicity.\n"
1154                "@item beamed-extreme-minimum-free-lengths\n"
1155                "List of extreme minimum free stem lengths (chord to beams)"
1156                " given beam multiplicity.\n"
1157                "@item lengths\n"
1158                "Default stem lengths.  The list gives a length for each"
1159                " flag count.\n"
1160                "@item stem-shorten\n"
1161                "How much a stem in a forced direction should be shortened."
1162                "  The list gives an amount depending on the number of flags"
1163                " and beams.\n"
1164                "@end table\n",
1165
1166                /* properties */
1167                "avoid-note-head "
1168                "beam "
1169                "beaming "
1170                "beamlet-default-length "
1171                "beamlet-max-length-proportion "
1172                "default-direction "
1173                "details "
1174                "direction "
1175                "double-stem-separation "
1176                "duration-log "
1177                "flag "
1178                "french-beaming "
1179                "length "
1180                "length-fraction "
1181                "max-beam-connect "
1182                "melody-spanner "
1183                "neutral-direction "
1184                "no-stem-extend "
1185                "note-heads "
1186                "positioning-done "
1187                "rests "
1188                "stem-begin-position "
1189                "stem-info "
1190                "stemlet-length "
1191                "thickness "
1192                "tremolo-flag "
1193                "tuplet-start "
1194               );
1195
1196 /****************************************************************/
1197
1198 Stem_info::Stem_info ()
1199 {
1200   ideal_y_ = shortest_y_ = 0;
1201   dir_ = CENTER;
1202 }
1203
1204 void
1205 Stem_info::scale (Real x)
1206 {
1207   ideal_y_ *= x;
1208   shortest_y_ *= x;
1209 }