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