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