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