]> git.donarmstrong.com Git - lilypond.git/blob - lily/stem.cc
Issue 4550 (2/2) Avoid "using namespace std;" in included files
[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
45 #include "beam.hh"
46 #include "directional-element-interface.hh"
47 #include "dot-column.hh"
48 #include "font-interface.hh"
49 #include "international.hh"
50 #include "lookup.hh"
51 #include "misc.hh"
52 #include "note-head.hh"
53 #include "output-def.hh"
54 #include "paper-column.hh"
55 #include "pointer-group-interface.hh"
56 #include "rest.hh"
57 #include "rhythmic-head.hh"
58 #include "side-position-interface.hh"
59 #include "staff-symbol-referencer.hh"
60 #include "stem-tremolo.hh"
61 #include "warn.hh"
62
63 using std::string;
64 using std::vector;
65
66 void
67 Stem::set_beaming (Grob *me, int beam_count, Direction d)
68 {
69   SCM pair = me->get_property ("beaming");
70
71   if (!scm_is_pair (pair))
72     {
73       pair = scm_cons (SCM_EOL, SCM_EOL);
74       me->set_property ("beaming", pair);
75     }
76
77   SCM lst = index_get_cell (pair, d);
78   if (beam_count)
79     for (int i = 0; i < beam_count; i++)
80       lst = scm_cons (scm_from_int (i), lst);
81   else
82     lst = SCM_BOOL_F;
83
84   index_set_cell (pair, d, lst);
85 }
86
87 int
88 Stem::get_beaming (Grob *me, Direction d)
89 {
90   SCM pair = me->get_property ("beaming");
91   if (!scm_is_pair (pair))
92     return 0;
93
94   SCM lst = index_get_cell (pair, d);
95
96   int len = scm_ilength (lst);
97   return std::max (len, 0);
98 }
99
100 Interval
101 Stem::head_positions (Grob *me)
102 {
103   if (head_count (me))
104     {
105       Drul_array<Grob *> e (extremal_heads (me));
106       return Interval (Staff_symbol_referencer::get_position (e[DOWN]),
107                        Staff_symbol_referencer::get_position (e[UP]));
108     }
109   return Interval ();
110 }
111
112 Real
113 Stem::chord_start_y (Grob *me)
114 {
115   if (head_count (me))
116     return Staff_symbol_referencer::get_position (last_head (me))
117       * Staff_symbol_referencer::staff_space (me) * 0.5;
118
119   return 0;
120 }
121
122 void
123 Stem::set_stem_positions (Grob *me, Real se)
124 {
125   // todo: margins
126   Direction d = get_grob_direction (me);
127
128   Grob *beam = unsmob<Grob> (me->get_object ("beam"));
129   if (d && d * head_positions (me)[get_grob_direction (me)] >= se * d)
130     me->warning (_ ("weird stem size, check for narrow beams"));
131
132   // trigger note collision mechanisms
133   Real stem_beg = internal_calc_stem_begin_position (me, false);
134   Real staff_space = Staff_symbol_referencer::staff_space (me);
135   Real half_space = staff_space * 0.5;
136
137   Interval height;
138   height[-d] = stem_beg * half_space;
139   height[d] = se * half_space + beam_end_corrective (me);
140
141   Real stemlet_length = robust_scm2double (me->get_property ("stemlet-length"),
142                                            0.0);
143   bool stemlet = stemlet_length > 0.0;
144
145   Grob *lh = get_reference_head (me);
146
147   if (!lh)
148     {
149       if (stemlet && beam)
150         {
151           Real beam_translation = Beam::get_beam_translation (beam);
152           Real beam_thickness = Beam::get_beam_thickness (beam);
153           int beam_count = beam_multiplicity (me).length () + 1;
154
155           height[-d] = (height[d] - d
156                         * (0.5 * beam_thickness
157                            + beam_translation * std::max (0, (beam_count - 1))
158                            + stemlet_length));
159         }
160       else if (!stemlet && beam)
161         height[-d] = height[d];
162       else if (stemlet && !beam)
163         me->programming_error ("Can't have a stemlet without a beam.");
164     }
165
166   me->set_property ("stem-begin-position", scm_from_double (height[-d] * 2 / staff_space));
167   me->set_property ("length", scm_from_double (height.length () * 2 / staff_space));
168 }
169
170 /* Note head that determines hshift for upstems
171    WARNING: triggers direction  */
172 Grob *
173 Stem::support_head (Grob *me)
174 {
175   extract_grob_set (me, "note-heads", heads);
176   if (heads.size () == 1)
177     return heads[0];
178
179   return first_head (me);
180 }
181
182 int
183 Stem::head_count (Grob *me)
184 {
185   return Pointer_group_interface::count (me, ly_symbol2scm ("note-heads"));
186 }
187
188 /* The note head which forms one end of the stem.
189    WARNING: triggers direction  */
190 Grob *
191 Stem::first_head (Grob *me)
192 {
193   Direction d = get_grob_direction (me);
194   if (d)
195     return extremal_heads (me)[-d];
196   return 0;
197 }
198
199 /* The note head opposite to the first head.  */
200 Grob *
201 Stem::last_head (Grob *me)
202 {
203   Direction d = get_grob_direction (me);
204   if (d)
205     return extremal_heads (me)[d];
206   return 0;
207 }
208
209 /*
210   START is part where stem reaches `last' head.
211
212   This function returns a drul with (bottom-head, top-head).
213 */
214 Drul_array<Grob *>
215 Stem::extremal_heads (Grob *me)
216 {
217   const int inf = INT_MAX;
218   Drul_array<int> extpos;
219   extpos[DOWN] = inf;
220   extpos[UP] = -inf;
221
222   Drul_array<Grob *> exthead (0, 0);
223   extract_grob_set (me, "note-heads", heads);
224
225   for (vsize i = heads.size (); i--;)
226     {
227       Grob *n = heads[i];
228       int p = Staff_symbol_referencer::get_rounded_position (n);
229
230       for (LEFT_and_RIGHT (d))
231         {
232           if (d * p > d * extpos[d])
233             {
234               exthead[d] = n;
235               extpos[d] = p;
236             }
237         }
238     }
239   return exthead;
240 }
241
242 /* The staff positions, in ascending order.
243  * If FILTER, include the main column of noteheads only */
244 vector<int>
245 Stem::note_head_positions (Grob *me, bool filter)
246 {
247   vector<int> ps;
248   extract_grob_set (me, "note-heads", heads);
249   Grob *xref = common_refpoint_of_array (heads, me, X_AXIS);
250
251   for (vsize i = heads.size (); i--;)
252     {
253       Grob *n = heads[i];
254       if (filter
255           && n->relative_coordinate (xref, X_AXIS) != 0.0)
256         continue;
257
258       int p = Staff_symbol_referencer::get_rounded_position (n);
259       ps.push_back (p);
260     }
261
262   vector_sort (ps, std::less<int> ());
263   return ps;
264 }
265
266 void
267 Stem::add_head (Grob *me, Grob *n)
268 {
269   n->set_object ("stem", me->self_scm ());
270
271   if (has_interface<Note_head> (n))
272     Pointer_group_interface::add_grob (me, ly_symbol2scm ("note-heads"), n);
273   else if (has_interface<Rest> (n))
274     Pointer_group_interface::add_grob (me, ly_symbol2scm ("rests"), n);
275 }
276
277 bool
278 Stem::is_invisible (Grob *me)
279 {
280   if (is_normal_stem (me))
281     return false;
282   else if (head_count (me))
283     return true;
284   else // if there are no note-heads, we might want stemlets
285     return 0.0 == robust_scm2double (me->get_property ("stemlet-length"), 0.0);
286 }
287
288 bool
289 Stem::is_normal_stem (Grob *me)
290 {
291   if (!head_count (me))
292     return false;
293
294   return 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 = std::min (min_pos, coords[i]);
348           max_pos = std::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 (std::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 = std::min (std::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 = (std::min (1.0, quarter_stem_length - (2 * staff_rad) - 2.0)) + abs (hp[dir]);
452       Real shorten = std::min (std::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 = std::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                            || scm_is_eq (heads[i]->get_property ("style"),
539                                          ly_symbol2scm ("harmonic"));
540   is_harmonic_centered = is_harmonic_centered && is_invisible (me);
541
542   Real w = hed->extent (hed, X_AXIS)[dir];
543   for (vsize i = 0; i < heads.size (); i++)
544     {
545       Real amount = w - heads[i]->extent (heads[i], X_AXIS)[dir];
546
547       if (is_harmonic_centered)
548         amount
549           = hed->extent (hed, X_AXIS).linear_combination (CENTER)
550             - heads[i]->extent (heads[i], X_AXIS).linear_combination (CENTER);
551
552       if (!isnan (amount)) // empty heads can produce NaN
553         heads[i]->translate_axis (amount, X_AXIS);
554     }
555   bool parity = true;
556   Real lastpos = Real (Staff_symbol_referencer::get_position (heads[0]));
557   for (vsize i = 1; i < heads.size (); i++)
558     {
559       Real p = Staff_symbol_referencer::get_position (heads[i]);
560       Real dy = fabs (lastpos - p);
561
562       /*
563         dy should always be 0.5, 0.0, 1.0, but provide safety margin
564         for rounding errors.
565       */
566       if (dy < 1.1)
567         {
568           if (parity)
569             {
570               Real ell = heads[i]->extent (heads[i], X_AXIS).length ();
571
572               Direction d = get_grob_direction (me);
573               /*
574                 Reversed head should be shifted ell-thickness, but this
575                 looks too crowded, so we only shift ell-0.5*thickness.
576
577                 This leads to assymetry: Normal heads overlap the
578                 stem 100% whereas reversed heads only overlaps the
579                 stem 50%
580               */
581               Real reverse_overlap = 0.5;
582
583               /*
584                 However, the first reverse head has to be shifted even
585                 more than the full reverse overlap if it is the same
586                 height as the first head or there will be a gap
587                 because of the head slant (issue 346).
588               */
589
590               if (i == 1 && dy < 0.1)
591                 reverse_overlap = 1.1;
592
593               if (is_invisible (me))
594                 {
595                   // Semibreves and longer are tucked in considerably
596                   // to be recognizable as chorded rather than
597                   // parallel voices.  During the course of issue 346
598                   // there was a discussion to change this for unisons
599                   // (dy < 0.1) to reduce overlap but without reaching
600                   // agreement and with Gould being rather on the
601                   // overlapping front.
602                   reverse_overlap = 2;
603                 }
604
605               heads[i]->translate_axis ((ell - thick * reverse_overlap) * d,
606                                         X_AXIS);
607
608               /* TODO:
609
610               For some cases we should kern some more: when the
611               distance between the next or prev note is too large, we'd
612               get large white gaps, eg.
613
614               |
615               X|
616               |X  <- kern this.
617               |
618               X
619
620               */
621             }
622           parity = !parity;
623         }
624       else
625         parity = true;
626
627       lastpos = int (p);
628     }
629
630   return SCM_BOOL_T;
631 }
632
633 MAKE_SCHEME_CALLBACK (Stem, calc_direction, 1);
634 SCM
635 Stem::calc_direction (SCM smob)
636 {
637   Grob *me = unsmob<Grob> (smob);
638   Direction dir = CENTER;
639   if (Grob *beam = unsmob<Grob> (me->get_object ("beam")))
640     {
641       SCM ignore_me = beam->get_property ("direction");
642       (void) ignore_me;
643       dir = get_grob_direction (me);
644     }
645   else
646     {
647       SCM dd = me->get_property ("default-direction");
648       dir = to_dir (dd);
649       if (!dir)
650         return me->get_property ("neutral-direction");
651     }
652
653   return scm_from_int (dir);
654 }
655
656 MAKE_SCHEME_CALLBACK (Stem, calc_default_direction, 1);
657 SCM
658 Stem::calc_default_direction (SCM smob)
659 {
660   Grob *me = unsmob<Grob> (smob);
661
662   Direction dir = CENTER;
663   int staff_center = 0;
664   if (head_count (me))
665     {
666       Interval hp = head_positions (me);
667       int udistance = (int) (UP * hp[UP] - staff_center);
668       int ddistance = (int) (DOWN * hp[DOWN] - staff_center);
669
670       dir = Direction (sign (ddistance - udistance));
671     }
672
673   return scm_from_int (dir);
674 }
675
676 // note - height property necessary to trigger quantized beam positions
677 // otherwise, we could just use Grob::stencil_height_proc
678 MAKE_SCHEME_CALLBACK (Stem, height, 1);
679 SCM
680 Stem::height (SCM smob)
681 {
682   Grob *me = unsmob<Grob> (smob);
683   return ly_interval2scm (internal_height (me, true));
684 }
685
686 Grob *
687 Stem::get_reference_head (Grob *me)
688 {
689   return to_boolean (me->get_property ("avoid-note-head"))
690          ? last_head (me)
691          : first_head (me);
692 }
693
694 Real
695 Stem::beam_end_corrective (Grob *me)
696 {
697   Grob *beam = unsmob<Grob> (me->get_object ("beam"));
698   Direction dir = get_grob_direction (me);
699   if (beam)
700     {
701       if (dir == CENTER)
702         {
703           programming_error ("no stem direction");
704           dir = UP;
705         }
706       return dir * Beam::get_beam_thickness (beam) * 0.5;
707     }
708   return 0.0;
709 }
710
711 Interval
712 Stem::internal_height (Grob *me, bool calc_beam)
713 {
714   Grob *beam = get_beam (me);
715   if (!is_valid_stem (me) && !beam)
716     return Interval ();
717
718   Direction dir = get_grob_direction (me);
719
720   if (beam && calc_beam)
721     {
722       /* trigger set-stem-lengths. */
723       (void) beam->get_property ("quantized-positions");
724     }
725
726   /*
727     If there is a beam but no stem, slope calculations depend on this
728     routine to return where the stem end /would/ be.
729   */
730   if (calc_beam && !beam && !unsmob<Stencil> (me->get_property ("stencil")))
731     return Interval ();
732
733   Real y1 = robust_scm2double ((calc_beam
734                                 ? me->get_property ("stem-begin-position")
735                                 : me->get_pure_property ("stem-begin-position", 0, INT_MAX)),
736                                0.0);
737
738   Real y2 = dir * robust_scm2double ((calc_beam
739                                       ? me->get_property ("length")
740                                       : me->get_pure_property ("length", 0, INT_MAX)),
741                                      0.0)
742             + y1;
743
744   Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
745
746   Interval stem_y = Interval (std::min (y1, y2), std::max (y2, y1)) * half_space;
747
748   return stem_y;
749 }
750
751 MAKE_SCHEME_CALLBACK (Stem, width, 1);
752 SCM
753 Stem::width (SCM e)
754 {
755   Grob *me = unsmob<Grob> (e);
756
757   Interval r;
758
759   if (is_invisible (me))
760     r.set_empty ();
761   else
762     {
763       r = Interval (-1, 1);
764       r *= thickness (me) / 2;
765     }
766
767   return ly_interval2scm (r);
768 }
769
770 Real
771 Stem::thickness (Grob *me)
772 {
773   return scm_to_double (me->get_property ("thickness"))
774          * Staff_symbol_referencer::line_thickness (me);
775 }
776
777 MAKE_SCHEME_CALLBACK (Stem, calc_stem_begin_position, 1);
778 SCM
779 Stem::calc_stem_begin_position (SCM smob)
780 {
781   Grob *me = unsmob<Grob> (smob);
782   return scm_from_double (internal_calc_stem_begin_position (me, true));
783 }
784
785 MAKE_SCHEME_CALLBACK (Stem, pure_calc_stem_begin_position, 3);
786 SCM
787 Stem::pure_calc_stem_begin_position (SCM smob,
788                                      SCM, /* start */
789                                      SCM /* end */)
790 {
791   Grob *me = unsmob<Grob> (smob);
792   return scm_from_double (internal_calc_stem_begin_position (me, false));
793 }
794
795 Real
796 Stem::internal_calc_stem_begin_position (Grob *me, bool calc_beam)
797 {
798   Grob *beam = get_beam (me);
799   Real ss = Staff_symbol_referencer::staff_space (me);
800   if (beam && calc_beam)
801     {
802       (void) beam->get_property ("quantized-positions");
803       return robust_scm2double (me->get_property ("stem-begin-position"), 0.0);
804     }
805
806   Direction d = get_grob_direction (me);
807   Grob *lh = get_reference_head (me);
808
809   if (!lh)
810     return 0.0;
811
812   Real pos = Staff_symbol_referencer::get_position (lh);
813
814   if (Grob *head = support_head (me))
815     {
816       Interval head_height = head->extent (head, Y_AXIS);
817       Real y_attach = Note_head::stem_attachment_coordinate (head, Y_AXIS);
818
819       y_attach = head_height.linear_combination (y_attach);
820       if (!isinf (y_attach) && !isnan (y_attach)) // empty heads
821         pos += d * y_attach * 2 / ss;
822     }
823
824   return pos;
825 }
826
827
828 MAKE_SCHEME_CALLBACK (Stem, pure_calc_length, 3);
829 SCM
830 Stem::pure_calc_length (SCM smob, SCM /*start*/, SCM /*end*/)
831 {
832   Grob *me = unsmob<Grob> (smob);
833   Real beg = robust_scm2double (me->get_pure_property ("stem-begin-position", 0, INT_MAX), 0.0);
834   Real res = fabs (internal_calc_stem_end_position (me, false) - beg);
835   return scm_from_double (res);
836 }
837
838 MAKE_SCHEME_CALLBACK (Stem, calc_length, 1);
839 SCM
840 Stem::calc_length (SCM smob)
841 {
842   Grob *me = unsmob<Grob> (smob);
843   if (unsmob<Grob> (me->get_object ("beam")))
844     {
845       me->programming_error ("ly:stem::calc-length called but will not be used for beamed stem.");
846       return scm_from_double (0.0);
847     }
848
849   Real beg = robust_scm2double (me->get_property ("stem-begin-position"), 0.0);
850   Real res = fabs (internal_calc_stem_end_position (me, true) - beg);
851   return scm_from_double (res);
852 }
853
854 bool
855 Stem::is_valid_stem (Grob *me)
856 {
857   /* TODO: make the stem start a direction ?
858      This is required to avoid stems passing in tablature chords.  */
859   if (!me)
860     return false;
861   Grob *lh = get_reference_head (me);
862   Grob *beam = unsmob<Grob> (me->get_object ("beam"));
863
864   if (!lh && !beam)
865     return false;
866
867   if (is_invisible (me))
868     return false;
869
870   return true;
871 }
872
873 MAKE_SCHEME_CALLBACK (Stem, print, 1);
874 SCM
875 Stem::print (SCM smob)
876 {
877   Grob *me = unsmob<Grob> (smob);
878   if (!is_valid_stem (me))
879     return SCM_EOL;
880
881   Direction dir = get_grob_direction (me);
882   Real y1 = robust_scm2double (me->get_property ("stem-begin-position"), 0.0);
883   Real y2 = dir * robust_scm2double (me->get_property ("length"), 0.0) + y1;
884
885   Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
886
887   Interval stem_y = Interval (std::min (y1, y2), std::max (y2, y1)) * half_space;
888
889   stem_y[dir] -= beam_end_corrective (me);
890
891   // URG
892   Real stem_width = thickness (me);
893   Real blot
894     = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
895
896   Box b = Box (Interval (-stem_width / 2, stem_width / 2),
897                stem_y);
898
899   Stencil mol;
900   Stencil ss = Lookup::round_filled_box (b, blot);
901   mol.add_stencil (ss);
902
903   return mol.smobbed_copy ();
904 }
905
906 /*
907   move the stem to right of the notehead if it is up.
908 */
909 MAKE_SCHEME_CALLBACK (Stem, offset_callback, 1);
910 SCM
911 Stem::offset_callback (SCM smob)
912 {
913   Grob *me = unsmob<Grob> (smob);
914
915   extract_grob_set (me, "rests", rests);
916   if (rests.size ())
917     {
918       Grob *rest = rests.back ();
919       Real r = robust_relative_extent (rest, rest, X_AXIS).center ();
920       return scm_from_double (r);
921     }
922
923   if (Grob *f = first_head (me))
924     {
925       Interval head_wid = f->extent (f, X_AXIS);
926       Real attach = 0.0;
927
928       if (is_invisible (me))
929         attach = 0.0;
930       else
931         attach = Note_head::stem_attachment_coordinate (f, X_AXIS);
932
933       Direction d = get_grob_direction (me);
934       Real real_attach = head_wid.linear_combination (d * attach);
935       Real r = isnan(real_attach)? 0.0: real_attach;
936
937       /* If not centered: correct for stem thickness.  */
938       string style = robust_symbol2string (f->get_property ("style"), "default");
939       if (attach && style != "mensural"
940           && style != "neomensural"
941           && style != "petrucci")
942         {
943           Real rule_thick = thickness (me);
944           r += -d * rule_thick * 0.5;
945         }
946       return scm_from_double (r);
947     }
948
949   programming_error ("Weird stem.");
950   return scm_from_double (0.0);
951 }
952
953 Spanner *
954 Stem::get_beam (Grob *me)
955 {
956   SCM b = me->get_object ("beam");
957   return unsmob<Spanner> (b);
958 }
959
960 Stem_info
961 Stem::get_stem_info (Grob *me)
962 {
963   Stem_info si;
964   si.dir_ = get_grob_direction (me);
965
966   SCM scm_info = me->get_property ("stem-info");
967   si.ideal_y_ = scm_to_double (scm_car (scm_info));
968   si.shortest_y_ = scm_to_double (scm_cadr (scm_info));
969   return si;
970 }
971
972 MAKE_SCHEME_CALLBACK (Stem, calc_stem_info, 1);
973 SCM
974 Stem::calc_stem_info (SCM smob)
975 {
976   Grob *me = unsmob<Grob> (smob);
977   Direction my_dir = get_grob_direction (me);
978
979   if (!my_dir)
980     {
981       programming_error ("no stem dir set");
982       my_dir = UP;
983     }
984
985   Real staff_space = Staff_symbol_referencer::staff_space (me);
986   Grob *beam = get_beam (me);
987
988   if (beam)
989     {
990       (void) beam->get_property ("beaming");
991     }
992
993   Real beam_translation = Beam::get_beam_translation (beam);
994   Real beam_thickness = Beam::get_beam_thickness (beam);
995   int beam_count = Beam::get_direction_beam_count (beam, my_dir);
996   Real length_fraction
997     = robust_scm2double (me->get_property ("length-fraction"), 1.0);
998
999   /* Simple standard stem length */
1000   SCM details = me->get_property ("details");
1001   SCM lengths = ly_assoc_get (ly_symbol2scm ("beamed-lengths"), details, SCM_EOL);
1002
1003   Real ideal_length
1004     = (scm_is_pair (lengths)
1005        ? (scm_to_double (robust_list_ref (beam_count - 1, lengths))
1006           * staff_space
1007           * length_fraction
1008           /*
1009             stem only extends to center of beam
1010           */
1011           - 0.5 * beam_thickness)
1012        : 0.0);
1013
1014   /* Condition: sane minimum free stem length (chord to beams) */
1015   lengths = ly_assoc_get (ly_symbol2scm ("beamed-minimum-free-lengths"),
1016                           details, SCM_EOL);
1017
1018   Real ideal_minimum_free
1019     = (scm_is_pair (lengths)
1020        ? (scm_to_double (robust_list_ref (beam_count - 1, lengths))
1021           * staff_space
1022           * length_fraction)
1023        : 0.0);
1024
1025   Real height_of_my_trem = 0.0;
1026   Grob *trem = unsmob<Grob> (me->get_object ("tremolo-flag"));
1027   if (trem)
1028     {
1029       height_of_my_trem
1030         = Stem_tremolo::vertical_length (trem)
1031           /* hack a bit of space around the trem. */
1032           + beam_translation;
1033     }
1034
1035   /* UGH
1036      It seems that also for ideal minimum length, we must use
1037      the maximum beam count (for this direction):
1038
1039      \score { \relative c'' { a8[ a32] } }
1040
1041      must be horizontal. */
1042   Real height_of_my_beams = beam_thickness
1043                             + (beam_count - 1) * beam_translation;
1044
1045   Real ideal_minimum_length = ideal_minimum_free
1046                               + height_of_my_beams
1047                               + height_of_my_trem
1048                               /* stem only extends to center of beam */
1049                               - 0.5 * beam_thickness;
1050
1051   ideal_length = std::max (ideal_length, ideal_minimum_length);
1052
1053   /* Convert to Y position, calculate for dir == UP */
1054   Real note_start
1055     =     /* staff positions */
1056       head_positions (me)[my_dir] * 0.5
1057       * my_dir * staff_space;
1058   Real ideal_y = note_start + ideal_length;
1059
1060   /* Conditions for Y position */
1061
1062   /* Lowest beam of (UP) beam must never be lower than second staffline
1063
1064   Reference?
1065
1066   Although this (additional) rule is probably correct,
1067   I expect that highest beam (UP) should also never be lower
1068   than middle staffline, just as normal stems.
1069
1070   Reference?
1071
1072   Obviously not for grace beams.
1073
1074   Also, not for knees.  Seems to be a good thing. */
1075   bool no_extend = to_boolean (me->get_property ("no-stem-extend"));
1076   bool is_knee = Beam::is_knee (beam);
1077   if (!no_extend && !is_knee)
1078     {
1079       /* Highest beam of (UP) beam must never be lower than middle
1080          staffline */
1081       ideal_y = std::max (ideal_y, 0.0);
1082       /* Lowest beam of (UP) beam must never be lower than second staffline */
1083       ideal_y = std::max (ideal_y, (-staff_space
1084                                - beam_thickness + height_of_my_beams));
1085     }
1086
1087   ideal_y -= robust_scm2double (beam->get_property ("shorten"), 0);
1088
1089   SCM bemfl = ly_assoc_get (ly_symbol2scm ("beamed-extreme-minimum-free-lengths"),
1090                             details, SCM_EOL);
1091
1092   Real minimum_free
1093     = (scm_is_pair (bemfl)
1094        ? (scm_to_double (robust_list_ref (beam_count - 1, bemfl))
1095           * staff_space
1096           * length_fraction)
1097        : 0.0);
1098
1099   Real minimum_length = std::max (minimum_free, height_of_my_trem)
1100                         + height_of_my_beams
1101                         /* stem only extends to center of beam */
1102                         - 0.5 * beam_thickness;
1103
1104   ideal_y *= my_dir;
1105   Real minimum_y = note_start + minimum_length;
1106   Real shortest_y = minimum_y * my_dir;
1107
1108   return scm_list_2 (scm_from_double (ideal_y),
1109                      scm_from_double (shortest_y));
1110 }
1111
1112 Slice
1113 Stem::beam_multiplicity (Grob *stem)
1114 {
1115   SCM beaming = stem->get_property ("beaming");
1116   Slice le = int_list_to_slice (scm_car (beaming));
1117   Slice ri = int_list_to_slice (scm_cdr (beaming));
1118   le.unite (ri);
1119   return le;
1120 }
1121
1122 bool
1123 Stem::is_cross_staff (Grob *stem)
1124 {
1125   Grob *beam = unsmob<Grob> (stem->get_object ("beam"));
1126   return beam && Beam::is_cross_staff (beam);
1127 }
1128
1129 MAKE_SCHEME_CALLBACK (Stem, calc_cross_staff, 1)
1130 SCM
1131 Stem::calc_cross_staff (SCM smob)
1132 {
1133   return scm_from_bool (is_cross_staff (unsmob<Grob> (smob)));
1134 }
1135
1136 Grob *
1137 Stem::flag (Grob *me)
1138 {
1139   return unsmob<Grob> (me->get_object ("flag"));
1140 }
1141
1142 /* FIXME:  Too many properties  */
1143 ADD_INTERFACE (Stem,
1144                "The stem represents the graphical stem.  In addition, it"
1145                " internally connects note heads, beams, and tremolos.  Rests"
1146                " and whole notes have invisible stems.\n"
1147                "\n"
1148                "The following properties may be set in the @code{details}"
1149                " list.\n"
1150                "\n"
1151                "@table @code\n"
1152                "@item beamed-lengths\n"
1153                "List of stem lengths given beam multiplicity.\n"
1154                "@item beamed-minimum-free-lengths\n"
1155                "List of normal minimum free stem lengths (chord to beams)"
1156                " given beam multiplicity.\n"
1157                "@item beamed-extreme-minimum-free-lengths\n"
1158                "List of extreme minimum free stem lengths (chord to beams)"
1159                " given beam multiplicity.\n"
1160                "@item lengths\n"
1161                "Default stem lengths.  The list gives a length for each"
1162                " flag count.\n"
1163                "@item stem-shorten\n"
1164                "How much a stem in a forced direction should be shortened."
1165                "  The list gives an amount depending on the number of flags"
1166                " and beams.\n"
1167                "@end table\n",
1168
1169                /* properties */
1170                "avoid-note-head "
1171                "beam "
1172                "beaming "
1173                "beamlet-default-length "
1174                "beamlet-max-length-proportion "
1175                "default-direction "
1176                "details "
1177                "direction "
1178                "double-stem-separation "
1179                "duration-log "
1180                "flag "
1181                "french-beaming "
1182                "length "
1183                "length-fraction "
1184                "max-beam-connect "
1185                "melody-spanner "
1186                "neutral-direction "
1187                "no-stem-extend "
1188                "note-heads "
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 }