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