]> git.donarmstrong.com Git - lilypond.git/blob - lily/stem.cc
Fixes issue 1881: cyclic dependency in Stem #'Y-extent.
[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       if (attach)
853         {
854           Real rule_thick = thickness (me);
855           r += -d * rule_thick * 0.5;
856         }
857       return scm_from_double (r);
858     }
859
860   programming_error ("Weird stem.");
861   return scm_from_double (0.0);
862 }
863
864 Spanner *
865 Stem::get_beam (Grob *me)
866 {
867   SCM b = me->get_object ("beam");
868   return dynamic_cast<Spanner *> (unsmob_grob (b));
869 }
870
871 Stem_info
872 Stem::get_stem_info (Grob *me)
873 {
874   Stem_info si;
875   si.dir_ = get_grob_direction (me);
876
877   SCM scm_info = me->get_property ("stem-info");
878   si.ideal_y_ = scm_to_double (scm_car (scm_info));
879   si.shortest_y_ = scm_to_double (scm_cadr (scm_info));
880   return si;
881 }
882
883 MAKE_SCHEME_CALLBACK (Stem, calc_stem_info, 1);
884 SCM
885 Stem::calc_stem_info (SCM smob)
886 {
887   Grob *me = unsmob_grob (smob);
888   Direction my_dir = get_grob_direction (me);
889
890   if (!my_dir)
891     {
892       programming_error ("no stem dir set");
893       my_dir = UP;
894     }
895
896   Real staff_space = Staff_symbol_referencer::staff_space (me);
897   Grob *beam = get_beam (me);
898
899   if (beam)
900     {
901       (void) beam->get_property ("beaming");
902     }
903
904   Real beam_translation = Beam::get_beam_translation (beam);
905   Real beam_thickness = Beam::get_beam_thickness (beam);
906   int beam_count = Beam::get_direction_beam_count (beam, my_dir);
907   Real length_fraction
908     = robust_scm2double (me->get_property ("length-fraction"), 1.0);
909
910   /* Simple standard stem length */
911   SCM details = me->get_property ("details");
912   SCM lengths = ly_assoc_get (ly_symbol2scm ("beamed-lengths"), details, SCM_EOL);
913
914   Real ideal_length
915     = (scm_is_pair (lengths)
916        ? (scm_to_double (robust_list_ref (beam_count - 1, lengths))
917           * staff_space
918           * length_fraction
919           /*
920             stem only extends to center of beam
921           */
922           - 0.5 * beam_thickness)
923        : 0.0);
924
925   /* Condition: sane minimum free stem length (chord to beams) */
926   lengths = ly_assoc_get (ly_symbol2scm ("beamed-minimum-free-lengths"),
927                           details, SCM_EOL);
928
929   Real ideal_minimum_free
930     = (scm_is_pair (lengths)
931        ? (scm_to_double (robust_list_ref (beam_count - 1, lengths))
932           * staff_space
933           * length_fraction)
934        : 0.0);
935
936   Real height_of_my_trem = 0.0;
937   Grob *trem = unsmob_grob (me->get_object ("tremolo-flag"));
938   if (trem)
939     {
940       height_of_my_trem
941         = Stem_tremolo::vertical_length (trem)
942           /* hack a bit of space around the trem. */
943           + beam_translation;
944     }
945
946   /* UGH
947      It seems that also for ideal minimum length, we must use
948      the maximum beam count (for this direction):
949
950      \score { \relative c'' { a8[ a32] } }
951
952      must be horizontal. */
953   Real height_of_my_beams = beam_thickness
954                             + (beam_count - 1) * beam_translation;
955
956   Real ideal_minimum_length = ideal_minimum_free
957                               + height_of_my_beams
958                               + height_of_my_trem
959                               /* stem only extends to center of beam */
960                               - 0.5 * beam_thickness;
961
962   ideal_length = max (ideal_length, ideal_minimum_length);
963
964   /* Convert to Y position, calculate for dir == UP */
965   Real note_start
966     =     /* staff positions */
967       head_positions (me)[my_dir] * 0.5
968       * my_dir * staff_space;
969   Real ideal_y = note_start + ideal_length;
970
971   /* Conditions for Y position */
972
973   /* Lowest beam of (UP) beam must never be lower than second staffline
974
975   Reference?
976
977   Although this (additional) rule is probably correct,
978   I expect that highest beam (UP) should also never be lower
979   than middle staffline, just as normal stems.
980
981   Reference?
982
983   Obviously not for grace beams.
984
985   Also, not for knees.  Seems to be a good thing. */
986   bool no_extend = to_boolean (me->get_property ("no-stem-extend"));
987   bool is_knee = to_boolean (beam->get_property ("knee"));
988   if (!no_extend && !is_knee)
989     {
990       /* Highest beam of (UP) beam must never be lower than middle
991          staffline */
992       ideal_y = max (ideal_y, 0.0);
993       /* Lowest beam of (UP) beam must never be lower than second staffline */
994       ideal_y = max (ideal_y, (-staff_space
995                                - beam_thickness + height_of_my_beams));
996     }
997
998   ideal_y -= robust_scm2double (beam->get_property ("shorten"), 0);
999
1000   SCM bemfl = ly_assoc_get (ly_symbol2scm ("beamed-extreme-minimum-free-lengths"),
1001                             details, SCM_EOL);
1002
1003   Real minimum_free
1004     = (scm_is_pair (bemfl)
1005        ? (scm_to_double (robust_list_ref (beam_count - 1, bemfl))
1006           * staff_space
1007           * length_fraction)
1008        : 0.0);
1009
1010   Real minimum_length = max (minimum_free, height_of_my_trem)
1011                         + height_of_my_beams
1012                         /* stem only extends to center of beam */
1013                         - 0.5 * beam_thickness;
1014
1015   ideal_y *= my_dir;
1016   Real minimum_y = note_start + minimum_length;
1017   Real shortest_y = minimum_y * my_dir;
1018
1019   return scm_list_2 (scm_from_double (ideal_y),
1020                      scm_from_double (shortest_y));
1021 }
1022
1023 Slice
1024 Stem::beam_multiplicity (Grob *stem)
1025 {
1026   SCM beaming = stem->get_property ("beaming");
1027   Slice le = int_list_to_slice (scm_car (beaming));
1028   Slice ri = int_list_to_slice (scm_cdr (beaming));
1029   le.unite (ri);
1030   return le;
1031 }
1032
1033 bool
1034 Stem::is_cross_staff (Grob *stem)
1035 {
1036   Grob *beam = unsmob_grob (stem->get_object ("beam"));
1037   return beam && Beam::is_cross_staff (beam);
1038 }
1039
1040 MAKE_SCHEME_CALLBACK (Stem, calc_cross_staff, 1)
1041 SCM
1042 Stem::calc_cross_staff (SCM smob)
1043 {
1044   return scm_from_bool (is_cross_staff (unsmob_grob (smob)));
1045 }
1046
1047 Grob*
1048 Stem::flag (Grob *me)
1049 {
1050   return unsmob_grob (me->get_object ("flag"));
1051 }
1052
1053 /* FIXME:  Too many properties  */
1054 ADD_INTERFACE (Stem,
1055                "The stem represents the graphical stem.  In addition, it"
1056                " internally connects note heads, beams, and tremolos.  Rests"
1057                " and whole notes have invisible stems.\n"
1058                "\n"
1059                "The following properties may be set in the @code{details}"
1060                " list.\n"
1061                "\n"
1062                "@table @code\n"
1063                "@item beamed-lengths\n"
1064                "List of stem lengths given beam multiplicity.\n"
1065                "@item beamed-minimum-free-lengths\n"
1066                "List of normal minimum free stem lengths (chord to beams)"
1067                " given beam multiplicity.\n"
1068                "@item beamed-extreme-minimum-free-lengths\n"
1069                "List of extreme minimum free stem lengths (chord to beams)"
1070                " given beam multiplicity.\n"
1071                "@item lengths\n"
1072                "Default stem lengths.  The list gives a length for each"
1073                " flag count.\n"
1074                "@item stem-shorten\n"
1075                "How much a stem in a forced direction should be shortened."
1076                "  The list gives an amount depending on the number of flags"
1077                " and beams.\n"
1078                "@end table\n",
1079
1080                /* properties */
1081                "avoid-note-head "
1082                "beam "
1083                "beaming "
1084                "beamlet-default-length "
1085                "beamlet-max-length-proportion "
1086                "default-direction "
1087                "details "
1088                "direction "
1089                "duration-log "
1090                "flag "
1091                "french-beaming "
1092                "length "
1093                "length-fraction "
1094                "max-beam-connect "
1095                "neutral-direction "
1096                "no-stem-extend "
1097                "note-heads "
1098                "positioning-done "
1099                "rests "
1100                "stem-begin-position "
1101                "stem-info "
1102                "stemlet-length "
1103                "thickness "
1104                "tremolo-flag "
1105               );
1106
1107 /****************************************************************/
1108
1109 Stem_info::Stem_info ()
1110 {
1111   ideal_y_ = shortest_y_ = 0;
1112   dir_ = CENTER;
1113 }
1114
1115 void
1116 Stem_info::scale (Real x)
1117 {
1118   ideal_y_ *= x;
1119   shortest_y_ *= x;
1120 }