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