]> git.donarmstrong.com Git - lilypond.git/blob - lily/stem.cc
Web-hu: Translated missing website strings
[lilypond.git] / lily / stem.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 1996--2009 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 = scm_to_double (calc_length (smob)) * ss / 2;
259       Direction dir = get_grob_direction (me);
260
261       Interval hp = head_positions (me);
262       if (dir == UP)
263         iv = Interval (0, len);
264       else
265         iv = Interval (-len, 0);
266
267       if (!hp.is_empty ())
268         iv.translate (hp[dir] * ss / 2);
269
270       /* extend the stem (away from the head) to cover the staff */
271       if (dir == UP)
272         iv[UP] = max (iv[UP], rad * ss);
273       else
274         iv[DOWN] = min (iv[DOWN], -rad * ss);
275     }
276   else
277     iv = Interval (-rad * ss, rad * ss);
278
279   return ly_interval2scm (iv);
280 }
281
282 MAKE_SCHEME_CALLBACK (Stem, calc_stem_end_position, 1)
283 SCM
284 Stem::calc_stem_end_position (SCM smob)
285 {
286   Grob *me = unsmob_grob (smob);
287
288   if (!head_count (me))
289     return scm_from_double (0.0);
290
291   if (Grob *beam = get_beam (me))
292     {
293       (void) beam->get_property ("quantized-positions");
294       return me->get_property ("stem-end-position");
295     }
296   
297   vector<Real> a;
298
299   /* WARNING: IN HALF SPACES */
300   Real length = robust_scm2double (me->get_property ("length"), 7);
301
302   Direction dir = get_grob_direction (me);
303   Interval hp = head_positions (me);
304   Real stem_end = dir ? hp[dir] + dir * length : 0;
305
306   /* TODO: change name  to extend-stems to staff/center/'()  */
307   bool no_extend = to_boolean (me->get_property ("no-stem-extend"));
308   if (!no_extend && dir * stem_end < 0)
309     stem_end = 0.0;
310
311   return scm_from_double (stem_end);
312 }
313
314 /* Length is in half-spaces (or: positions) here. */
315 MAKE_SCHEME_CALLBACK (Stem, calc_length, 1)
316 SCM
317 Stem::calc_length (SCM smob)
318 {
319   Grob *me = unsmob_grob (smob);
320   
321   SCM details = me->get_property ("details");
322   int durlog = duration_log (me);
323
324   Real ss = Staff_symbol_referencer::staff_space (me);
325   Real length = 7;
326   SCM s = ly_assoc_get (ly_symbol2scm ("lengths"), details, SCM_EOL);
327   if (scm_is_pair (s))
328     length = 2 * scm_to_double (robust_list_ref (durlog - 2, s));
329
330   Direction dir = get_grob_direction (me);
331
332   /* Stems in unnatural (forced) direction should be shortened,
333      according to [Roush & Gourlay] */
334   Interval hp = head_positions (me);
335   if (dir && dir * hp[dir] >= 0)
336     {
337       SCM sshorten = ly_assoc_get (ly_symbol2scm ("stem-shorten"), details, SCM_EOL);
338       SCM scm_shorten = scm_is_pair (sshorten)
339         ? robust_list_ref (max (duration_log (me) - 2, 0), sshorten) : SCM_EOL;
340       Real shorten = 2* robust_scm2double (scm_shorten, 0);
341
342       /* On boundary: shorten only half */
343       if (abs (head_positions (me)[dir]) <= 1)
344         shorten *= 0.5;
345
346       length -= shorten;
347     }
348
349   length *= robust_scm2double (me->get_property ("length-fraction"), 1.0);
350
351   /* Tremolo stuff.  */
352   Grob *t_flag = unsmob_grob (me->get_object ("tremolo-flag"));
353   if (t_flag && !unsmob_grob (me->get_object ("beam")))
354     {
355       /* Crude hack: add extra space if tremolo flag is there.
356
357       We can't do this for the beam, since we get into a loop
358       (Stem_tremolo::raw_stencil () looks at the beam.) --hwn  */
359
360       Real minlen = 1.0
361         + 2 * Stem_tremolo::vertical_length (t_flag) / ss;
362
363       /* We don't want to add the whole extent of the flag because the trem
364          and the flag can overlap partly. beam_translation gives a good
365          approximation */
366       if (durlog >= 3)
367         {
368           Real beam_trans = Stem_tremolo::get_beam_translation (t_flag);
369           /* the obvious choice is (durlog - 2) here, but we need a bit more space. */
370           minlen += 2 * (durlog - 1.5) * beam_trans;
371
372           /* up-stems need even a little more space to avoid collisions. This
373              needs to be in sync with the tremolo positioning code in
374              Stem_tremolo::print */
375           if (dir == UP)
376             minlen += beam_trans;
377         }
378       length = max (length, minlen + 1.0);
379     }
380   
381   return scm_from_double (length);
382 }
383 /* The log of the duration (Number of hooks on the flag minus two)  */
384 int
385 Stem::duration_log (Grob *me)
386 {
387   SCM s = me->get_property ("duration-log");
388   return (scm_is_number (s)) ? scm_to_int (s) : 2;
389 }
390
391 MAKE_SCHEME_CALLBACK (Stem, calc_positioning_done, 1);
392 SCM
393 Stem::calc_positioning_done (SCM smob)
394 {
395   Grob *me = unsmob_grob (smob);  
396   if (!head_count (me))
397     return SCM_BOOL_T;
398
399   me->set_property ("positioning-done", SCM_BOOL_T);
400   
401   extract_grob_set (me, "note-heads", ro_heads);
402   vector<Grob*> heads (ro_heads);
403   vector_sort (heads, position_less);
404   Direction dir = get_grob_direction (me);
405
406   if (dir < 0)
407     reverse (heads);
408
409   Real thick = thickness (me);
410
411   Grob *hed = support_head (me);
412   if (!dir)
413     {
414       programming_error ("Stem dir must be up or down.");
415       dir = UP;
416       set_grob_direction (me, dir);
417     }
418
419   bool is_harmonic_centered = false;
420   for (vsize i = 0; i < heads.size (); i++)
421     is_harmonic_centered = is_harmonic_centered 
422       || heads[i]->get_property ("style") == ly_symbol2scm ("harmonic");
423   is_harmonic_centered = is_harmonic_centered && is_invisible (me);
424
425   Real w = hed->extent (hed, X_AXIS)[dir];
426   for (vsize i = 0; i < heads.size (); i++)
427     {
428       Real amount = w - heads[i]->extent (heads[i], X_AXIS)[dir];
429
430       if (is_harmonic_centered)
431         amount =
432           hed->extent (hed, X_AXIS).linear_combination (CENTER)
433           - heads[i]->extent (heads[i], X_AXIS).linear_combination (CENTER);
434       
435       heads[i]->translate_axis (amount, X_AXIS);
436     }
437   bool parity = true;
438   Real lastpos = Real (Staff_symbol_referencer::get_position (heads[0]));
439   for (vsize i = 1; i < heads.size (); i++)
440     {
441       Real p = Staff_symbol_referencer::get_position (heads[i]);
442       Real dy = fabs (lastpos- p);
443
444       /*
445         dy should always be 0.5, 0.0, 1.0, but provide safety margin
446         for rounding errors.
447       */
448       if (dy < 1.1)
449         {
450           if (parity)
451             {
452               Real ell = heads[i]->extent (heads[i], X_AXIS).length ();
453
454               Direction d = get_grob_direction (me);
455               /*
456                 Reversed head should be shifted ell-thickness, but this
457                 looks too crowded, so we only shift ell-0.5*thickness.
458
459                 This leads to assymetry: Normal heads overlap the
460                 stem 100% whereas reversed heads only overlaps the
461                 stem 50%
462               */
463
464               Real reverse_overlap = 0.5;
465               heads[i]->translate_axis ((ell - thick * reverse_overlap) * d,
466                                         X_AXIS);
467
468               if (is_invisible (me))
469                 heads[i]->translate_axis (-thick * (2 - reverse_overlap) * d,
470                                           X_AXIS);
471
472               /* TODO:
473
474               For some cases we should kern some more: when the
475               distance between the next or prev note is too large, we'd
476               get large white gaps, eg.
477
478               |
479               X|
480               |X  <- kern this.
481               |
482               X
483
484               */
485             }
486           parity = !parity;
487         }
488       else
489         parity = true;
490
491       lastpos = int (p);
492     }
493
494   return SCM_BOOL_T;
495 }
496
497 MAKE_SCHEME_CALLBACK (Stem, calc_direction, 1);
498 SCM
499 Stem::calc_direction (SCM smob)
500 {
501   Grob *me = unsmob_grob (smob);
502   Direction dir = CENTER;
503   if (Grob *beam = unsmob_grob (me->get_object ("beam")))
504     {
505       SCM ignore_me = beam->get_property ("direction");
506       (void) ignore_me;
507       dir = get_grob_direction (me);
508     }
509   else
510     {
511       SCM dd = me->get_property ("default-direction");
512       dir = to_dir (dd);
513       if (!dir)
514         return me->get_property ("neutral-direction");
515     }
516   
517   return scm_from_int (dir);
518 }
519
520 MAKE_SCHEME_CALLBACK (Stem, calc_default_direction, 1);
521 SCM
522 Stem::calc_default_direction (SCM smob)
523 {
524   Grob *me = unsmob_grob (smob);
525
526   Direction dir = CENTER;
527   int staff_center = 0;
528   Interval hp = head_positions (me);
529   if (!hp.is_empty ())
530     {
531       int udistance = (int) (UP * hp[UP] - staff_center);
532       int ddistance = (int) (DOWN * hp[DOWN] - staff_center);
533       
534       dir = Direction (sign (ddistance - udistance));
535     }
536   
537   return scm_from_int (dir);
538 }
539
540
541 MAKE_SCHEME_CALLBACK (Stem, height, 1);
542 SCM
543 Stem::height (SCM smob)
544 {
545   Grob *me = unsmob_grob (smob);
546   if (!is_normal_stem (me))
547     return ly_interval2scm (Interval ());
548   
549   Direction dir = get_grob_direction (me);
550   
551   Grob *beam = get_beam (me);
552   if (beam)
553     {
554       /* trigger set-stem-lengths. */
555       beam->get_property ("quantized-positions");
556     }
557
558   /*
559     Can't get_stencil (), since that would cache stencils too early.
560     This causes problems with beams.
561    */
562   Stencil *stencil = unsmob_stencil (print (smob));
563   Interval iv = stencil ? stencil->extent (Y_AXIS) : Interval ();
564   if (beam)
565     {
566       if (dir == CENTER)
567         {
568           programming_error ("no stem direction");
569           dir = UP;
570         }
571       iv[dir] += dir * Beam::get_beam_thickness (beam) * 0.5;
572     }
573
574   return ly_interval2scm (iv);
575 }
576
577 Real
578 Stem::stem_end_position (Grob *me)
579 {
580   return robust_scm2double (me->get_property ("stem-end-position"), 0);
581 }
582
583 MAKE_SCHEME_CALLBACK (Stem, calc_flag, 1);
584 SCM
585 Stem::calc_flag (SCM smob)
586 {
587   Grob *me = unsmob_grob (smob);
588
589   int log = duration_log (me);
590   /*
591     TODO: maybe property stroke-style should take different values,
592     e.g. "" (i.e. no stroke), "single" and "double" (currently, it's
593     '() or "grace").  */
594   string flag_style;
595
596    SCM flag_style_scm = me->get_property ("flag-style");
597   if (scm_is_symbol (flag_style_scm))
598     flag_style = ly_symbol2string (flag_style_scm);
599
600   if (flag_style == "no-flag")
601     return Stencil ().smobbed_copy ();
602
603   bool adjust = true;
604
605   string staffline_offs;
606   if (flag_style == "mensural")
607     /* Mensural notation: For notes on staff lines, use different
608        flags than for notes between staff lines.  The idea is that
609        flags are always vertically aligned with the staff lines,
610        regardless if the note head is on a staff line or between two
611        staff lines.  In other words, the inner end of a flag always
612        touches a staff line.
613     */
614     {
615       if (adjust)
616         {
617           int p = (int) (rint (stem_end_position (me)));
618           staffline_offs
619             = Staff_symbol_referencer::on_line (me, p) ? "0" : "1";
620         }
621       else
622         staffline_offs = "2";
623      }
624   else
625     staffline_offs = "";
626
627   char dir = (get_grob_direction (me) == UP) ? 'u' : 'd';
628   string font_char = flag_style
629     + to_string (dir) + staffline_offs + to_string (log);
630   Font_metric *fm = Font_interface::get_default_font (me);
631   Stencil flag = fm->find_by_name ("flags." + font_char);
632   if (flag.is_empty ())
633     me->warning (_f ("flag `%s' not found", font_char));
634
635   SCM stroke_style_scm = me->get_property ("stroke-style");
636   if (scm_is_string (stroke_style_scm))
637     {
638       string stroke_style = ly_scm2string (stroke_style_scm);
639       if (!stroke_style.empty ())
640         {
641           string font_char = flag_style + to_string (dir) + stroke_style;
642           Stencil stroke = fm->find_by_name ("flags." + font_char);
643           if (stroke.is_empty ())
644             {
645               font_char = to_string (dir) + stroke_style;
646               stroke = fm->find_by_name ("flags." + font_char);
647             }
648           if (stroke.is_empty ())
649             me->warning (_f ("flag stroke `%s' not found", font_char));
650           else
651             flag.add_stencil (stroke);
652         }
653      }
654
655   return flag.smobbed_copy ();
656 }
657
658
659 Stencil
660 Stem::flag (Grob *me)
661 {
662   int log = duration_log (me);
663   if (log < 3
664       || unsmob_grob (me->get_object ("beam")))
665     return Stencil ();
666
667   if (!is_normal_stem (me))
668     return Stencil ();
669
670   // This get_property call already evaluates the scheme function with
671   // the grob passed as argument! Thus, we only have to check if a valid
672   // stencil is returned.
673   SCM flag_style_scm = me->get_property ("flag");
674   if (Stencil *flag = unsmob_stencil (flag_style_scm)) {
675     return *flag;
676   } else {
677     return Stencil ();
678   }
679 }
680
681 MAKE_SCHEME_CALLBACK (Stem, width, 1);
682 SCM
683 Stem::width (SCM e)
684 {
685   Grob *me = unsmob_grob (e);
686
687   Interval r;
688
689   if (is_invisible (me))
690     r.set_empty ();
691   else if (unsmob_grob (me->get_object ("beam"))
692            || abs (duration_log (me)) <= 2)
693     {
694       r = Interval (-1, 1);
695       r *= thickness (me) / 2;
696     }
697   else
698     {
699       r = Interval (-1, 1) * thickness (me) * 0.5;
700       r.unite (flag (me).extent (X_AXIS));
701     }
702   return ly_interval2scm (r);
703 }
704
705 Real
706 Stem::thickness (Grob *me)
707 {
708   return scm_to_double (me->get_property ("thickness"))
709     * Staff_symbol_referencer::line_thickness (me);
710 }
711
712 MAKE_SCHEME_CALLBACK (Stem, print, 1);
713 SCM
714 Stem::print (SCM smob)
715 {
716   Grob *me = unsmob_grob (smob);
717   Grob *beam = get_beam (me);
718     
719   Stencil mol;
720   Direction d = get_grob_direction (me);
721
722   Real stemlet_length = robust_scm2double (me->get_property ("stemlet-length"),
723                                            0.0);
724   bool stemlet = stemlet_length > 0.0;
725
726   /* TODO: make the stem start a direction ?
727      This is required to avoid stems passing in tablature chords.  */
728   Grob *lh
729     = to_boolean (me->get_property ("avoid-note-head"))
730     ? last_head (me)
731     : first_head (me);
732
733   if (!lh && !stemlet)
734     return SCM_EOL;
735
736   if (!lh && stemlet && !beam)
737     return SCM_EOL;
738
739   if (lh && robust_scm2int (lh->get_property ("duration-log"), 0) < 1) 
740     return SCM_EOL;
741
742   if (is_invisible (me))
743     return SCM_EOL;
744
745   Real y2 = robust_scm2double (me->get_property ("stem-end-position"), 0.0);
746   Real y1 = y2;
747   Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
748
749   if (lh)
750     y2 = Staff_symbol_referencer::get_position (lh);
751   else if (stemlet)
752     {
753       Real beam_translation = Beam::get_beam_translation (beam);
754       Real beam_thickness = Beam::get_beam_thickness (beam);
755       int beam_count = beam_multiplicity (me).length () + 1;
756
757       y2 -= d
758         * (0.5 * beam_thickness
759            + beam_translation * max (0, (beam_count - 1))
760            + stemlet_length) / half_space;
761     }
762
763   Interval stem_y (min (y1, y2), max (y2, y1));
764
765   if (Grob *head = support_head (me))
766     {
767       /*
768         must not take ledgers into account.
769       */
770       Interval head_height = head->extent (head, Y_AXIS);
771       Real y_attach = Note_head::stem_attachment_coordinate (head, Y_AXIS);
772
773       y_attach = head_height.linear_combination (y_attach);
774       stem_y[Direction (-d)] += d * y_attach / half_space;
775     }
776
777   // URG
778   Real stem_width = thickness (me);
779   Real blot
780     = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
781
782   Box b = Box (Interval (-stem_width / 2, stem_width / 2),
783                Interval (stem_y[DOWN] * half_space, stem_y[UP] * half_space));
784
785   Stencil ss = Lookup::round_filled_box (b, blot);
786   mol.add_stencil (ss);
787
788   mol.add_stencil (get_translated_flag (me));
789
790   return mol.smobbed_copy ();
791 }
792
793 Stencil
794 Stem::get_translated_flag (Grob *me)
795 {
796   Stencil fl = flag (me);
797   if (!fl.is_empty ())
798     {
799       Direction d = get_grob_direction (me);
800       Real blot
801         = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
802       Real stem_width = thickness (me);
803       Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
804       Real y2 = robust_scm2double (me->get_property ("stem-end-position"), 0.0);
805       fl.translate_axis (y2 * half_space - d * blot / 2, Y_AXIS);
806       fl.translate_axis (stem_width / 2, X_AXIS);
807     }
808   return fl;
809 }
810
811
812 /*
813   move the stem to right of the notehead if it is up.
814 */
815 MAKE_SCHEME_CALLBACK (Stem, offset_callback, 1);
816 SCM
817 Stem::offset_callback (SCM smob)
818 {
819   Grob *me = unsmob_grob (smob);
820
821   extract_grob_set (me, "rests", rests);
822   if (rests.size ())
823     {
824       Grob *rest = rests.back ();
825       Real r = rest->extent (rest, X_AXIS).center ();
826       return scm_from_double (r);
827     }
828
829   
830   if (Grob *f = first_head (me))
831     {
832       Interval head_wid = f->extent (f, X_AXIS);
833       Real attach = 0.0;
834
835       if (is_invisible (me))
836         attach = 0.0;
837       else
838         attach = Note_head::stem_attachment_coordinate (f, X_AXIS);
839
840       Direction d = get_grob_direction (me);
841       Real real_attach = head_wid.linear_combination (d * attach);
842       Real r = real_attach;
843
844       /* If not centered: correct for stem thickness.  */
845       if (attach)
846         {
847           Real rule_thick = thickness (me);
848           r += -d * rule_thick * 0.5;
849         }
850       return scm_from_double (r);
851     }
852
853   programming_error ("Weird stem.");
854   return scm_from_double (0.0);
855 }
856
857 Spanner *
858 Stem::get_beam (Grob *me)
859 {
860   SCM b = me->get_object ("beam");
861   return dynamic_cast<Spanner *> (unsmob_grob (b));
862 }
863
864 Stem_info
865 Stem::get_stem_info (Grob *me)
866 {
867   Stem_info si;
868   si.dir_ = get_grob_direction (me);
869   
870   SCM scm_info = me->get_property ("stem-info");
871   si.ideal_y_ = scm_to_double (scm_car (scm_info));
872   si.shortest_y_ = scm_to_double (scm_cadr (scm_info));
873   return si;
874 }
875
876 MAKE_SCHEME_CALLBACK (Stem, calc_stem_info, 1);
877 SCM
878 Stem::calc_stem_info (SCM smob)
879 {
880   Grob *me = unsmob_grob (smob);
881   Direction my_dir = get_grob_direction (me);
882
883   if (!my_dir)
884     {
885       programming_error ("no stem dir set");
886       my_dir = UP;
887     }
888
889   Real staff_space = Staff_symbol_referencer::staff_space (me);
890   Grob *beam = get_beam (me);
891
892   if (beam)
893     {
894       (void) beam->get_property ("beaming");
895     }
896   
897   Real beam_translation = Beam::get_beam_translation (beam);
898   Real beam_thickness = Beam::get_beam_thickness (beam);
899   int beam_count = Beam::get_direction_beam_count (beam, my_dir);
900   Real length_fraction
901     = robust_scm2double (me->get_property ("length-fraction"), 1.0);
902
903   /* Simple standard stem length */
904   SCM details = me->get_property ("details");
905   SCM lengths = ly_assoc_get (ly_symbol2scm ("beamed-lengths"), details, SCM_EOL);
906   
907   Real ideal_length
908     = scm_to_double (robust_list_ref (beam_count - 1, lengths))
909     * staff_space
910     * length_fraction
911     
912     /* stem only extends to center of beam
913      */
914     - 0.5 * beam_thickness;
915
916   /* Condition: sane minimum free stem length (chord to beams) */
917   lengths = ly_assoc_get (ly_symbol2scm ("beamed-minimum-free-lengths"), details, SCM_EOL);
918
919   Real ideal_minimum_free
920     = scm_to_double (robust_list_ref (beam_count - 1, lengths))
921     * staff_space
922     * length_fraction;
923
924   Real height_of_my_trem = 0.0;
925   Grob *trem = unsmob_grob (me->get_object ("tremolo-flag"));
926   if (trem)
927     {
928       height_of_my_trem
929         = Stem_tremolo::vertical_length (trem)
930         /* hack a bit of space around the trem. */
931         + beam_translation;
932     }
933
934   
935   /* UGH
936      It seems that also for ideal minimum length, we must use
937      the maximum beam count (for this direction):
938
939      \score{ \notes\relative c''{ [a8 a32] }}
940
941      must be horizontal. */
942   Real height_of_my_beams = beam_thickness
943     + (beam_count - 1) * beam_translation;
944
945   Real ideal_minimum_length = ideal_minimum_free
946     + height_of_my_beams
947     + height_of_my_trem
948     /* stem only extends to center of beam */
949     - 0.5 * beam_thickness;
950
951   ideal_length = max (ideal_length, ideal_minimum_length);
952
953   /* Convert to Y position, calculate for dir == UP */
954   Real note_start
955     =     /* staff positions */
956     head_positions (me)[my_dir] * 0.5
957     * my_dir * staff_space;
958   Real ideal_y = note_start + ideal_length;
959
960   /* Conditions for Y position */
961
962   /* Lowest beam of (UP) beam must never be lower than second staffline
963
964   Reference?
965
966   Although this (additional) rule is probably correct,
967   I expect that highest beam (UP) should also never be lower
968   than middle staffline, just as normal stems.
969
970   Reference?
971
972   Obviously not for grace beams.
973
974   Also, not for knees.  Seems to be a good thing. */
975   bool no_extend = to_boolean (me->get_property ("no-stem-extend"));
976   bool is_knee = to_boolean (beam->get_property ("knee"));
977   if (!no_extend && !is_knee)
978     {
979       /* Highest beam of (UP) beam must never be lower than middle
980          staffline */
981       ideal_y = max (ideal_y, 0.0);
982       /* Lowest beam of (UP) beam must never be lower than second staffline */
983       ideal_y = max (ideal_y, (-staff_space
984                                - beam_thickness + height_of_my_beams));
985     }
986
987   ideal_y -= robust_scm2double (beam->get_property ("shorten"), 0);
988
989   SCM bemfl = ly_assoc_get (ly_symbol2scm ("beamed-extreme-minimum-free-lengths"),
990                             details, SCM_EOL);
991   
992   Real minimum_free
993     = scm_to_double (robust_list_ref (beam_count - 1, bemfl))
994     * staff_space
995     * length_fraction;
996
997   Real minimum_length = max (minimum_free, height_of_my_trem)
998     + height_of_my_beams
999     /* stem only extends to center of beam */
1000     - 0.5 * beam_thickness;
1001
1002   ideal_y *= my_dir;
1003   Real minimum_y = note_start + minimum_length;
1004   Real shortest_y = minimum_y * my_dir;
1005
1006   return scm_list_2 (scm_from_double (ideal_y),
1007                      scm_from_double (shortest_y));
1008 }
1009
1010 Slice
1011 Stem::beam_multiplicity (Grob *stem)
1012 {
1013   SCM beaming = stem->get_property ("beaming");
1014   Slice le = int_list_to_slice (scm_car (beaming));
1015   Slice ri = int_list_to_slice (scm_cdr (beaming));
1016   le.unite (ri);
1017   return le;
1018 }
1019
1020 bool
1021 Stem::is_cross_staff (Grob *stem)
1022 {
1023   Grob *beam = unsmob_grob (stem->get_object ("beam"));
1024   return beam && Beam::is_cross_staff (beam);
1025 }
1026
1027 MAKE_SCHEME_CALLBACK (Stem, calc_cross_staff, 1)
1028 SCM
1029 Stem::calc_cross_staff (SCM smob)
1030 {
1031   return scm_from_bool (is_cross_staff (unsmob_grob (smob)));
1032 }
1033
1034 /* FIXME:  Too many properties  */
1035 ADD_INTERFACE (Stem,
1036                "The stem represents the graphical stem.  In addition, it"
1037                " internally connects note heads, beams, and tremolos.  Rests"
1038                " and whole notes have invisible stems.\n"
1039                "\n"
1040                "The following properties may be set in the @code{details}"
1041                " list.\n"
1042                "\n"
1043                "@table @code\n"
1044                "@item beamed-lengths\n"
1045                "List of stem lengths given beam multiplicity.\n"
1046                "@item beamed-minimum-free-lengths\n"
1047                "List of normal minimum free stem lengths (chord to beams)"
1048                " given beam multiplicity.\n"
1049                "@item beamed-extreme-minimum-free-lengths\n"
1050                "List of extreme minimum free stem lengths (chord to beams)"
1051                " given beam multiplicity.\n"
1052                "@item lengths\n"
1053                "Default stem lengths.  The list gives a length for each"
1054                " flag count.\n"
1055                "@item stem-shorten\n"
1056                "How much a stem in a forced direction should be shortened."
1057                "  The list gives an amount depending on the number of flags"
1058                " and beams.\n"
1059                "@end table\n",
1060
1061                /* properties */
1062                "avoid-note-head "
1063                "beam "
1064                "beaming "
1065                "beamlet-default-length "
1066                "beamlet-max-length-proportion "
1067                "default-direction "
1068                "details "
1069                "direction "
1070                "duration-log "
1071                "flag "
1072                "flag-style "
1073                "french-beaming "
1074                "length "
1075                "length-fraction "
1076                "max-beam-connect "
1077                "neutral-direction "
1078                "no-stem-extend "
1079                "note-heads "
1080                "positioning-done "
1081                "rests "
1082                "stem-end-position "
1083                "stem-info "
1084                "stemlet-length "
1085                "stroke-style "
1086                "thickness "
1087                "tremolo-flag "
1088                );
1089
1090 /****************************************************************/
1091
1092 Stem_info::Stem_info ()
1093 {
1094   ideal_y_ = shortest_y_ = 0;
1095   dir_ = CENTER;
1096 }
1097
1098 void
1099 Stem_info::scale (Real x)
1100 {
1101   ideal_y_ *= x;
1102   shortest_y_ *= x;
1103 }