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