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