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