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