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