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