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