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