]> git.donarmstrong.com Git - lilypond.git/blob - lily/stem.cc
Issue 2853: Set up indent-tabs-mode for Emacs in lexer.ll and parser.yy
[lilypond.git] / lily / stem.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 1996--2012 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   return !is_normal_stem (me)
279          && (robust_scm2double (me->get_property ("stemlet-length"),
280                                 0.0) == 0.0);
281 }
282
283 bool
284 Stem::is_normal_stem (Grob *me)
285 {
286   if (!head_count (me))
287     return false;
288
289   extract_grob_set (me, "note-heads", heads);
290   SCM style = heads[0]->get_property ("style");
291   return style != ly_symbol2scm ("kievan") && scm_to_int (me->get_property ("duration-log")) >= 1;
292 }
293
294 MAKE_SCHEME_CALLBACK (Stem, pure_height, 3)
295 SCM
296 Stem::pure_height (SCM smob,
297                    SCM /* start */,
298                    SCM /* end */)
299 {
300   Grob *me = unsmob_grob (smob);
301   return ly_interval2scm (internal_pure_height (me, true));
302 }
303
304 Interval
305 Stem::internal_pure_height (Grob *me, bool calc_beam)
306 {
307   if (!is_normal_stem (me))
308     return Interval (0.0, 0.0);
309
310   Grob *beam = unsmob_grob (me->get_object ("beam"));
311
312   Interval iv = internal_height (me, false);
313
314   if (!beam)
315     return iv;
316   if (calc_beam)
317     {
318       Interval overshoot;
319       Direction dir = get_grob_direction (me);
320       for (DOWN_and_UP (d))
321         overshoot[d] = d == dir ? dir * infinity_f : iv[d];
322
323       vector<Interval> heights;
324       vector<Grob *> my_stems;
325       extract_grob_set (beam, "normal-stems", normal_stems);
326       for (vsize i = 0; i < normal_stems.size (); i++)
327         if (get_grob_direction (normal_stems[i]) == dir)
328           {
329             if (normal_stems[i] != me)
330               heights.push_back (Stem::internal_pure_height (normal_stems[i], false));
331             else
332               heights.push_back (iv);
333             my_stems.push_back (normal_stems[i]);
334           }
335       //iv.unite (heights.back ());
336       // look for cross staff effects
337       vector<Real> coords;
338       Grob *common = common_refpoint_of_array (my_stems, me, Y_AXIS);
339       Real min_pos = infinity_f;
340       Real max_pos = -infinity_f;
341       for (vsize i = 0; i < my_stems.size (); i++)
342         {
343           coords.push_back (my_stems[i]->pure_relative_y_coordinate (common, 0, INT_MAX));
344           min_pos = min (min_pos, coords[i]);
345           max_pos = max (max_pos, coords[i]);
346         }
347       for (vsize i = 0; i < heights.size (); i++)
348         {
349           heights[i][dir] += dir == DOWN
350                              ? coords[i] - max_pos
351                              : coords[i] - min_pos;
352         }
353
354       for (vsize i = 0; i < heights.size (); i++) iv.unite (heights[i]);
355
356       for (vsize i = 0; i < my_stems.size (); i++)
357         cache_pure_height (my_stems[i], iv, heights[i]);
358       iv.intersect (overshoot);
359     }
360
361   return iv;
362 }
363
364 void
365 Stem::cache_pure_height (Grob *me, Interval iv, Interval my_iv)
366 {
367   Interval overshoot;
368   Direction dir = get_grob_direction (me);
369   for (DOWN_and_UP (d))
370     overshoot[d] = d == dir ? dir * infinity_f : my_iv[d];
371
372   iv.intersect (overshoot);
373   dynamic_cast<Item *> (me)->cache_pure_height (iv);
374 }
375
376 MAKE_SCHEME_CALLBACK (Stem, calc_stem_end_position, 1)
377 SCM
378 Stem::calc_stem_end_position (SCM smob)
379 {
380   Grob *me = unsmob_grob (smob);
381   return scm_from_double (internal_calc_stem_end_position (me, true));
382 }
383
384 MAKE_SCHEME_CALLBACK (Stem, pure_calc_stem_end_position, 3)
385 SCM
386 Stem::pure_calc_stem_end_position (SCM smob,
387                                    SCM, /* start */
388                                    SCM /* end */)
389 {
390   Grob *me = unsmob_grob (smob);
391   return scm_from_double (internal_calc_stem_end_position (me, false));
392 }
393
394 Real
395 Stem::internal_calc_stem_end_position (Grob *me, bool calc_beam)
396 {
397   if (!head_count (me))
398     return 0.0;
399
400   Grob *beam = get_beam (me);
401   Real ss = Staff_symbol_referencer::staff_space (me);
402   Direction dir = get_grob_direction (me);
403
404   if (beam && calc_beam)
405     {
406       (void) beam->get_property ("quantized-positions");
407       return robust_scm2double (me->get_property ("length"), 0.0)
408              + dir * robust_scm2double (me->get_property ("stem-begin-position"), 0.0);
409     }
410
411   vector<Real> a;
412
413   /* WARNING: IN HALF SPACES */
414   SCM details = me->get_property ("details");
415   int durlog = duration_log (me);
416
417   Real staff_rad = Staff_symbol_referencer::staff_radius (me);
418   Real length = 7;
419   SCM s = ly_assoc_get (ly_symbol2scm ("lengths"), details, SCM_EOL);
420   if (scm_is_pair (s))
421     length = 2 * scm_to_double (robust_list_ref (durlog - 2, s));
422
423   /* Stems in unnatural (forced) direction should be shortened,
424      according to [Roush & Gourlay] */
425   Interval hp = head_positions (me);
426   if (dir && dir * hp[dir] >= 0)
427     {
428       SCM sshorten = ly_assoc_get (ly_symbol2scm ("stem-shorten"), details, SCM_EOL);
429       SCM scm_shorten = scm_is_pair (sshorten)
430                         ? robust_list_ref (max (duration_log (me) - 2, 0), sshorten) : SCM_EOL;
431       Real shorten_property = 2 * robust_scm2double (scm_shorten, 0);
432       /*  change in length between full-size and shortened stems is executed gradually.
433           "transition area" = stems between full-sized and fully-shortened.
434           */
435       Real quarter_stem_length = 2 * scm_to_double (robust_list_ref (0, s));
436       /*  shortening_step = difference in length between consecutive stem lengths
437           in transition area. The bigger the difference between full-sized
438           and shortened stems, the bigger shortening_step is.
439           (but not greater than 1/2 and not smaller than 1/4).
440           value 6 is heuristic; it determines the suggested transition slope steepnesas.
441           */
442       Real shortening_step = min (max (0.25, (shorten_property / 6)), 0.5);
443       /*  Shortening of unflagged stems should begin on the first stem that sticks
444           more than 1 staffspace (2 units) out of the staff.
445           Shortening of flagged stems begins in the same moment as unflagged ones,
446           but not earlier than on the middle line note.
447           */
448       Real which_step = (min (1.0, quarter_stem_length - (2 * staff_rad) - 2.0)) + abs (hp[dir]);
449       Real shorten = min (max (0.0, (shortening_step * which_step)), shorten_property);
450
451       length -= shorten;
452     }
453
454   length *= robust_scm2double (me->get_property ("length-fraction"), 1.0);
455
456   /* Tremolo stuff.  */
457   Grob *t_flag = unsmob_grob (me->get_object ("tremolo-flag"));
458   if (t_flag && (!unsmob_grob (me->get_object ("beam")) || !calc_beam))
459     {
460       /* Crude hack: add extra space if tremolo flag is there.
461
462       We can't do this for the beam, since we get into a loop
463       (Stem_tremolo::raw_stencil () looks at the beam.) --hwn  */
464
465       Real minlen = 1.0
466                     + 2 * Stem_tremolo::vertical_length (t_flag) / ss;
467
468       /* We don't want to add the whole extent of the flag because the trem
469          and the flag can overlap partly. beam_translation gives a good
470          approximation */
471       if (durlog >= 3)
472         {
473           Real beam_trans = Stem_tremolo::get_beam_translation (t_flag);
474           /* the obvious choice is (durlog - 2) here, but we need a bit more space. */
475           minlen += 2 * (durlog - 1.5) * beam_trans;
476
477           /* up-stems need even a little more space to avoid collisions. This
478              needs to be in sync with the tremolo positioning code in
479              Stem_tremolo::print */
480           if (dir == UP)
481             minlen += beam_trans;
482         }
483       length = max (length, minlen + 1.0);
484     }
485
486   Real stem_end = dir ? hp[dir] + dir * length : 0;
487
488   /* TODO: change name  to extend-stems to staff/center/'()  */
489   bool no_extend = to_boolean (me->get_property ("no-stem-extend"));
490   if (!no_extend && dir * stem_end < 0)
491     stem_end = 0.0;
492
493   return stem_end;
494 }
495
496 /* The log of the duration (Number of hooks on the flag minus two)  */
497 int
498 Stem::duration_log (Grob *me)
499 {
500   SCM s = me->get_property ("duration-log");
501   return (scm_is_number (s)) ? scm_to_int (s) : 2;
502 }
503
504 MAKE_SCHEME_CALLBACK (Stem, calc_positioning_done, 1);
505 SCM
506 Stem::calc_positioning_done (SCM smob)
507 {
508   Grob *me = unsmob_grob (smob);
509   if (!head_count (me))
510     return SCM_BOOL_T;
511
512   me->set_property ("positioning-done", SCM_BOOL_T);
513
514   extract_grob_set (me, "note-heads", ro_heads);
515   vector<Grob *> heads (ro_heads);
516   vector_sort (heads, position_less);
517   Direction dir = get_grob_direction (me);
518
519   if (dir < 0)
520     reverse (heads);
521
522   Real thick = thickness (me);
523
524   Grob *hed = support_head (me);
525   if (!dir)
526     {
527       programming_error ("Stem dir must be up or down.");
528       dir = UP;
529       set_grob_direction (me, dir);
530     }
531
532   bool is_harmonic_centered = false;
533   for (vsize i = 0; i < heads.size (); i++)
534     is_harmonic_centered = is_harmonic_centered
535                            || heads[i]->get_property ("style") == ly_symbol2scm ("harmonic");
536   is_harmonic_centered = is_harmonic_centered && is_invisible (me);
537
538   Real w = hed->extent (hed, X_AXIS)[dir];
539   for (vsize i = 0; i < heads.size (); i++)
540     {
541       Real amount = w - heads[i]->extent (heads[i], X_AXIS)[dir];
542
543       if (is_harmonic_centered)
544         amount
545           = hed->extent (hed, X_AXIS).linear_combination (CENTER)
546             - heads[i]->extent (heads[i], X_AXIS).linear_combination (CENTER);
547
548       if (!isnan (amount)) // empty heads can produce NaN
549         heads[i]->translate_axis (amount, X_AXIS);
550     }
551   bool parity = true;
552   Real lastpos = Real (Staff_symbol_referencer::get_position (heads[0]));
553   for (vsize i = 1; i < heads.size (); i++)
554     {
555       Real p = Staff_symbol_referencer::get_position (heads[i]);
556       Real dy = fabs (lastpos - p);
557
558       /*
559         dy should always be 0.5, 0.0, 1.0, but provide safety margin
560         for rounding errors.
561       */
562       if (dy < 1.1)
563         {
564           if (parity)
565             {
566               Real ell = heads[i]->extent (heads[i], X_AXIS).length ();
567
568               Direction d = get_grob_direction (me);
569               /*
570                 Reversed head should be shifted ell-thickness, but this
571                 looks too crowded, so we only shift ell-0.5*thickness.
572
573                 This leads to assymetry: Normal heads overlap the
574                 stem 100% whereas reversed heads only overlaps the
575                 stem 50%
576               */
577
578               Real reverse_overlap = 0.5;
579               heads[i]->translate_axis ((ell - thick * reverse_overlap) * d,
580                                         X_AXIS);
581
582               if (is_invisible (me))
583                 heads[i]->translate_axis (-thick * (2 - reverse_overlap) * d,
584                                           X_AXIS);
585
586               /* TODO:
587
588               For some cases we should kern some more: when the
589               distance between the next or prev note is too large, we'd
590               get large white gaps, eg.
591
592               |
593               X|
594               |X  <- kern this.
595               |
596               X
597
598               */
599             }
600           parity = !parity;
601         }
602       else
603         parity = true;
604
605       lastpos = int (p);
606     }
607
608   return SCM_BOOL_T;
609 }
610
611 MAKE_SCHEME_CALLBACK (Stem, calc_direction, 1);
612 SCM
613 Stem::calc_direction (SCM smob)
614 {
615   Grob *me = unsmob_grob (smob);
616   Direction dir = CENTER;
617   if (Grob *beam = unsmob_grob (me->get_object ("beam")))
618     {
619       SCM ignore_me = beam->get_property ("direction");
620       (void) ignore_me;
621       dir = get_grob_direction (me);
622     }
623   else
624     {
625       SCM dd = me->get_property ("default-direction");
626       dir = to_dir (dd);
627       if (!dir)
628         return me->get_property ("neutral-direction");
629     }
630
631   return scm_from_int (dir);
632 }
633
634 MAKE_SCHEME_CALLBACK (Stem, calc_default_direction, 1);
635 SCM
636 Stem::calc_default_direction (SCM smob)
637 {
638   Grob *me = unsmob_grob (smob);
639
640   Direction dir = CENTER;
641   int staff_center = 0;
642   Interval hp = head_positions (me);
643   if (!hp.is_empty ())
644     {
645       int udistance = (int) (UP * hp[UP] - staff_center);
646       int ddistance = (int) (DOWN * hp[DOWN] - staff_center);
647
648       dir = Direction (sign (ddistance - udistance));
649     }
650
651   return scm_from_int (dir);
652 }
653
654 // note - height property necessary to trigger quantized beam positions
655 // otherwise, we could just use Grob::stencil_height_proc
656 MAKE_SCHEME_CALLBACK (Stem, height, 1);
657 SCM
658 Stem::height (SCM smob)
659 {
660   Grob *me = unsmob_grob (smob);
661   return ly_interval2scm (internal_height (me, true));
662 }
663
664 Grob *
665 Stem::get_reference_head (Grob *me)
666 {
667   return to_boolean (me->get_property ("avoid-note-head"))
668          ? last_head (me)
669          : first_head (me);
670 }
671
672 Real
673 Stem::beam_end_corrective (Grob *me)
674 {
675   Grob *beam = unsmob_grob (me->get_object ("beam"));
676   Direction dir = get_grob_direction (me);
677   if (beam)
678     {
679       if (dir == CENTER)
680         {
681           programming_error ("no stem direction");
682           dir = UP;
683         }
684       return dir * Beam::get_beam_thickness (beam) * 0.5;
685     }
686   return 0.0;
687 }
688
689 Interval
690 Stem::internal_height (Grob *me, bool calc_beam)
691 {
692   Grob *beam = get_beam (me);
693   if (!is_valid_stem (me) && !beam)
694     return Interval ();
695
696   Direction dir = get_grob_direction (me);
697
698   if (beam && calc_beam)
699     {
700       /* trigger set-stem-lengths. */
701       (void) beam->get_property ("quantized-positions");
702     }
703
704   /*
705     If there is a beam but no stem, slope calculations depend on this
706     routine to return where the stem end /would/ be.
707   */
708   if (calc_beam && !beam && !unsmob_stencil (me->get_property ("stencil")))
709     return Interval ();
710
711   Real y1 = robust_scm2double ((calc_beam
712                                 ? me->get_property ("stem-begin-position")
713                                 : me->get_pure_property ("stem-begin-position", 0, INT_MAX)),
714                                0.0);
715
716   Real y2 = dir * robust_scm2double ((calc_beam
717                                       ? me->get_property ("length")
718                                       : me->get_pure_property ("length", 0, INT_MAX)),
719                                      0.0)
720             + y1;
721
722   Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
723
724   Interval stem_y = Interval (min (y1, y2), max (y2, y1)) * half_space;
725
726   return stem_y;
727 }
728
729 MAKE_SCHEME_CALLBACK (Stem, width, 1);
730 SCM
731 Stem::width (SCM e)
732 {
733   Grob *me = unsmob_grob (e);
734
735   Interval r;
736
737   if (is_invisible (me))
738     r.set_empty ();
739   else
740     {
741       r = Interval (-1, 1);
742       r *= thickness (me) / 2;
743     }
744
745   return ly_interval2scm (r);
746 }
747
748 Real
749 Stem::thickness (Grob *me)
750 {
751   return scm_to_double (me->get_property ("thickness"))
752          * Staff_symbol_referencer::line_thickness (me);
753 }
754
755 MAKE_SCHEME_CALLBACK (Stem, calc_stem_begin_position, 1);
756 SCM
757 Stem::calc_stem_begin_position (SCM smob)
758 {
759   Grob *me = unsmob_grob (smob);
760   return scm_from_double (internal_calc_stem_begin_position (me, true));
761 }
762
763 MAKE_SCHEME_CALLBACK (Stem, pure_calc_stem_begin_position, 3);
764 SCM
765 Stem::pure_calc_stem_begin_position (SCM smob,
766                                      SCM, /* start */
767                                      SCM /* end */)
768 {
769   Grob *me = unsmob_grob (smob);
770   return scm_from_double (internal_calc_stem_begin_position (me, false));
771 }
772
773 Real
774 Stem::internal_calc_stem_begin_position (Grob *me, bool calc_beam)
775 {
776   Grob *beam = get_beam (me);
777   Real ss = Staff_symbol_referencer::staff_space (me);
778   if (beam && calc_beam)
779     {
780       (void) beam->get_property ("quantized-positions");
781       return robust_scm2double (me->get_property ("stem-begin-position"), 0.0);
782     }
783
784   Direction d = get_grob_direction (me);
785   Grob *lh = get_reference_head (me);
786
787   if (!lh)
788     return 0.0;
789
790   Real pos = Staff_symbol_referencer::get_position (lh);
791
792   if (Grob *head = support_head (me))
793     {
794       Interval head_height = head->extent (head, Y_AXIS);
795       Real y_attach = Note_head::stem_attachment_coordinate (head, Y_AXIS);
796
797       y_attach = head_height.linear_combination (y_attach);
798       if (!isinf (y_attach) && !isnan (y_attach)) // empty heads
799         pos += d * y_attach * 2 / ss;
800     }
801
802   return pos;
803 }
804
805 bool
806 Stem::is_valid_stem (Grob *me)
807 {
808   /* TODO: make the stem start a direction ?
809      This is required to avoid stems passing in tablature chords.  */
810   Grob *lh = get_reference_head (me);
811   Grob *beam = unsmob_grob (me->get_object ("beam"));
812
813   if (!lh && !beam)
814     return false;
815
816   if (lh && robust_scm2int (lh->get_property ("duration-log"), 0) < 1)
817     return false;
818
819   if (is_invisible (me))
820     return false;
821
822   return true;
823 }
824
825 MAKE_SCHEME_CALLBACK (Stem, print, 1);
826 SCM
827 Stem::print (SCM smob)
828 {
829   Grob *me = unsmob_grob (smob);
830   if (!is_valid_stem (me))
831     return SCM_EOL;
832
833   Direction dir = get_grob_direction (me);
834   Real y1 = robust_scm2double (me->get_property ("stem-begin-position"), 0.0);
835   Real y2 = dir * robust_scm2double (me->get_property ("length"), 0.0) + y1;
836
837   Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
838
839   Interval stem_y = Interval (min (y1, y2), max (y2, y1)) * half_space;
840
841   stem_y[dir] -= beam_end_corrective (me);
842
843   // URG
844   Real stem_width = thickness (me);
845   Real blot
846     = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
847
848   Box b = Box (Interval (-stem_width / 2, stem_width / 2),
849                stem_y);
850
851   Stencil mol;
852   Stencil ss = Lookup::round_filled_box (b, blot);
853   mol.add_stencil (ss);
854
855   return mol.smobbed_copy ();
856 }
857
858 /*
859   move the stem to right of the notehead if it is up.
860 */
861 MAKE_SCHEME_CALLBACK (Stem, offset_callback, 1);
862 SCM
863 Stem::offset_callback (SCM smob)
864 {
865   Grob *me = unsmob_grob (smob);
866
867   extract_grob_set (me, "rests", rests);
868   if (rests.size ())
869     {
870       Grob *rest = rests.back ();
871       Real r = rest->extent (rest, X_AXIS).center ();
872       return scm_from_double (r);
873     }
874
875   if (Grob *f = first_head (me))
876     {
877       Interval head_wid = f->extent (f, X_AXIS);
878       Real attach = 0.0;
879
880       if (is_invisible (me))
881         attach = 0.0;
882       else
883         attach = Note_head::stem_attachment_coordinate (f, X_AXIS);
884
885       Direction d = get_grob_direction (me);
886       Real real_attach = head_wid.linear_combination (d * attach);
887       Real r = isnan(real_attach)? 0.0: real_attach;
888
889       /* If not centered: correct for stem thickness.  */
890       string style = robust_symbol2string (f->get_property ("style"), "default");
891       if (attach && style != "mensural"
892           && style != "neomensural"
893           && style != "petrucci")
894         {
895           Real rule_thick = thickness (me);
896           r += -d * rule_thick * 0.5;
897         }
898       return scm_from_double (r);
899     }
900
901   programming_error ("Weird stem.");
902   return scm_from_double (0.0);
903 }
904
905 Spanner *
906 Stem::get_beam (Grob *me)
907 {
908   SCM b = me->get_object ("beam");
909   return dynamic_cast<Spanner *> (unsmob_grob (b));
910 }
911
912 Stem_info
913 Stem::get_stem_info (Grob *me)
914 {
915   Stem_info si;
916   si.dir_ = get_grob_direction (me);
917
918   SCM scm_info = me->get_property ("stem-info");
919   si.ideal_y_ = scm_to_double (scm_car (scm_info));
920   si.shortest_y_ = scm_to_double (scm_cadr (scm_info));
921   return si;
922 }
923
924 MAKE_SCHEME_CALLBACK (Stem, calc_stem_info, 1);
925 SCM
926 Stem::calc_stem_info (SCM smob)
927 {
928   Grob *me = unsmob_grob (smob);
929   Direction my_dir = get_grob_direction (me);
930
931   if (!my_dir)
932     {
933       programming_error ("no stem dir set");
934       my_dir = UP;
935     }
936
937   Real staff_space = Staff_symbol_referencer::staff_space (me);
938   Grob *beam = get_beam (me);
939
940   if (beam)
941     {
942       (void) beam->get_property ("beaming");
943     }
944
945   Real beam_translation = Beam::get_beam_translation (beam);
946   Real beam_thickness = Beam::get_beam_thickness (beam);
947   int beam_count = Beam::get_direction_beam_count (beam, my_dir);
948   Real length_fraction
949     = robust_scm2double (me->get_property ("length-fraction"), 1.0);
950
951   /* Simple standard stem length */
952   SCM details = me->get_property ("details");
953   SCM lengths = ly_assoc_get (ly_symbol2scm ("beamed-lengths"), details, SCM_EOL);
954
955   Real ideal_length
956     = (scm_is_pair (lengths)
957        ? (scm_to_double (robust_list_ref (beam_count - 1, lengths))
958           * staff_space
959           * length_fraction
960           /*
961             stem only extends to center of beam
962           */
963           - 0.5 * beam_thickness)
964        : 0.0);
965
966   /* Condition: sane minimum free stem length (chord to beams) */
967   lengths = ly_assoc_get (ly_symbol2scm ("beamed-minimum-free-lengths"),
968                           details, SCM_EOL);
969
970   Real ideal_minimum_free
971     = (scm_is_pair (lengths)
972        ? (scm_to_double (robust_list_ref (beam_count - 1, lengths))
973           * staff_space
974           * length_fraction)
975        : 0.0);
976
977   Real height_of_my_trem = 0.0;
978   Grob *trem = unsmob_grob (me->get_object ("tremolo-flag"));
979   if (trem)
980     {
981       height_of_my_trem
982         = Stem_tremolo::vertical_length (trem)
983           /* hack a bit of space around the trem. */
984           + beam_translation;
985     }
986
987   /* UGH
988      It seems that also for ideal minimum length, we must use
989      the maximum beam count (for this direction):
990
991      \score { \relative c'' { a8[ a32] } }
992
993      must be horizontal. */
994   Real height_of_my_beams = beam_thickness
995                             + (beam_count - 1) * beam_translation;
996
997   Real ideal_minimum_length = ideal_minimum_free
998                               + height_of_my_beams
999                               + height_of_my_trem
1000                               /* stem only extends to center of beam */
1001                               - 0.5 * beam_thickness;
1002
1003   ideal_length = max (ideal_length, ideal_minimum_length);
1004
1005   /* Convert to Y position, calculate for dir == UP */
1006   Real note_start
1007     =     /* staff positions */
1008       head_positions (me)[my_dir] * 0.5
1009       * my_dir * staff_space;
1010   Real ideal_y = note_start + ideal_length;
1011
1012   /* Conditions for Y position */
1013
1014   /* Lowest beam of (UP) beam must never be lower than second staffline
1015
1016   Reference?
1017
1018   Although this (additional) rule is probably correct,
1019   I expect that highest beam (UP) should also never be lower
1020   than middle staffline, just as normal stems.
1021
1022   Reference?
1023
1024   Obviously not for grace beams.
1025
1026   Also, not for knees.  Seems to be a good thing. */
1027   bool no_extend = to_boolean (me->get_property ("no-stem-extend"));
1028   bool is_knee = Beam::is_knee (beam);
1029   if (!no_extend && !is_knee)
1030     {
1031       /* Highest beam of (UP) beam must never be lower than middle
1032          staffline */
1033       ideal_y = max (ideal_y, 0.0);
1034       /* Lowest beam of (UP) beam must never be lower than second staffline */
1035       ideal_y = max (ideal_y, (-staff_space
1036                                - beam_thickness + height_of_my_beams));
1037     }
1038
1039   ideal_y -= robust_scm2double (beam->get_property ("shorten"), 0);
1040
1041   SCM bemfl = ly_assoc_get (ly_symbol2scm ("beamed-extreme-minimum-free-lengths"),
1042                             details, SCM_EOL);
1043
1044   Real minimum_free
1045     = (scm_is_pair (bemfl)
1046        ? (scm_to_double (robust_list_ref (beam_count - 1, bemfl))
1047           * staff_space
1048           * length_fraction)
1049        : 0.0);
1050
1051   Real minimum_length = max (minimum_free, height_of_my_trem)
1052                         + height_of_my_beams
1053                         /* stem only extends to center of beam */
1054                         - 0.5 * beam_thickness;
1055
1056   ideal_y *= my_dir;
1057   Real minimum_y = note_start + minimum_length;
1058   Real shortest_y = minimum_y * my_dir;
1059
1060   return scm_list_2 (scm_from_double (ideal_y),
1061                      scm_from_double (shortest_y));
1062 }
1063
1064 Slice
1065 Stem::beam_multiplicity (Grob *stem)
1066 {
1067   SCM beaming = stem->get_property ("beaming");
1068   Slice le = int_list_to_slice (scm_car (beaming));
1069   Slice ri = int_list_to_slice (scm_cdr (beaming));
1070   le.unite (ri);
1071   return le;
1072 }
1073
1074 bool
1075 Stem::is_cross_staff (Grob *stem)
1076 {
1077   Grob *beam = unsmob_grob (stem->get_object ("beam"));
1078   return beam && Beam::is_cross_staff (beam);
1079 }
1080
1081 MAKE_SCHEME_CALLBACK (Stem, calc_cross_staff, 1)
1082 SCM
1083 Stem::calc_cross_staff (SCM smob)
1084 {
1085   return scm_from_bool (is_cross_staff (unsmob_grob (smob)));
1086 }
1087
1088 Grob *
1089 Stem::flag (Grob *me)
1090 {
1091   return unsmob_grob (me->get_object ("flag"));
1092 }
1093
1094 /* FIXME:  Too many properties  */
1095 ADD_INTERFACE (Stem,
1096                "The stem represents the graphical stem.  In addition, it"
1097                " internally connects note heads, beams, and tremolos.  Rests"
1098                " and whole notes have invisible stems.\n"
1099                "\n"
1100                "The following properties may be set in the @code{details}"
1101                " list.\n"
1102                "\n"
1103                "@table @code\n"
1104                "@item beamed-lengths\n"
1105                "List of stem lengths given beam multiplicity.\n"
1106                "@item beamed-minimum-free-lengths\n"
1107                "List of normal minimum free stem lengths (chord to beams)"
1108                " given beam multiplicity.\n"
1109                "@item beamed-extreme-minimum-free-lengths\n"
1110                "List of extreme minimum free stem lengths (chord to beams)"
1111                " given beam multiplicity.\n"
1112                "@item lengths\n"
1113                "Default stem lengths.  The list gives a length for each"
1114                " flag count.\n"
1115                "@item stem-shorten\n"
1116                "How much a stem in a forced direction should be shortened."
1117                "  The list gives an amount depending on the number of flags"
1118                " and beams.\n"
1119                "@end table\n",
1120
1121                /* properties */
1122                "avoid-note-head "
1123                "beam "
1124                "beaming "
1125                "beamlet-default-length "
1126                "beamlet-max-length-proportion "
1127                "default-direction "
1128                "details "
1129                "direction "
1130                "duration-log "
1131                "flag "
1132                "french-beaming "
1133                "length "
1134                "length-fraction "
1135                "max-beam-connect "
1136                "melody-spanner "
1137                "neutral-direction "
1138                "no-stem-extend "
1139                "note-heads "
1140                "positioning-done "
1141                "rests "
1142                "stem-begin-position "
1143                "stem-info "
1144                "stemlet-length "
1145                "thickness "
1146                "tremolo-flag "
1147                "tuplet-start "
1148               );
1149
1150 /****************************************************************/
1151
1152 Stem_info::Stem_info ()
1153 {
1154   ideal_y_ = shortest_y_ = 0;
1155   dir_ = CENTER;
1156 }
1157
1158 void
1159 Stem_info::scale (Real x)
1160 {
1161   ideal_y_ *= x;
1162   shortest_y_ *= x;
1163 }