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