]> git.donarmstrong.com Git - lilypond.git/blob - lily/stem.cc
* lily/accidental-placement.cc (stagger_apes): try to arrange accs
[lilypond.git] / lily / stem.cc
1 /*
2   stem.cc -- implement Stem
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 1996--2002 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7     Jan Nieuwenhuizen <janneke@gnu.org>
8
9   TODO: This is way too hairy
10
11   TODO: fix naming.
12
13   Stem-end, chord-start, etc. is all confusing naming.
14 */
15
16 #include <math.h>               // rint
17
18 #include "lookup.hh"
19 #include "directional-element-interface.hh"
20 #include "note-head.hh"
21 #include "stem.hh"
22 #include "warn.hh"
23 #include "paper-def.hh"
24 #include "rhythmic-head.hh"
25 #include "font-interface.hh"
26 #include "molecule.hh"
27 #include "paper-column.hh"
28 #include "misc.hh"
29 #include "beam.hh"
30 #include "rest.hh"
31 #include "group-interface.hh"
32 #include "staff-symbol-referencer.hh"
33 #include "spanner.hh"
34 #include "side-position-interface.hh"
35 #include "dot-column.hh"
36
37 void
38 Stem::set_beaming (Grob*me, int beam_count,  Direction d)
39 {
40   SCM pair = me->get_grob_property ("beaming");
41   
42   if (!gh_pair_p (pair))
43     {
44       pair = gh_cons (SCM_EOL, SCM_EOL);
45       me->set_grob_property ("beaming", pair);
46     }
47
48   SCM l = index_get_cell (pair, d);
49   for( int i = 0; i<  beam_count; i++)
50     {
51       l = gh_cons (gh_int2scm (i), l);
52     }
53   index_set_cell (pair, d, l);          
54 }
55
56
57 Interval
58 Stem::head_positions (Grob*me) 
59 {
60   if (!head_count (me))
61     {
62       Interval iv;
63       return iv;
64     }
65
66   Drul_array<Grob*> e (extremal_heads (me));
67
68   return Interval (Staff_symbol_referencer::position_f (e[DOWN]),
69                    Staff_symbol_referencer::position_f (e[UP]));
70 }
71
72
73 Real
74 Stem::chord_start_y (Grob*me) 
75 {
76   return head_positions (me)[get_direction (me)]
77     * Staff_symbol_referencer::staff_space (me)/2.0;
78 }
79
80 Real
81 Stem::stem_end_position (Grob*me) 
82 {
83   SCM p =me->get_grob_property ("stem-end-position");
84   Real pos;
85   if (!gh_number_p (p))
86     {
87       pos = get_default_stem_end_position (me);
88       me->set_grob_property ("stem-end-position", gh_double2scm (pos));
89     }
90   else
91     pos = gh_scm2double (p);
92
93   return pos;
94 }
95
96 Direction
97 Stem::get_direction (Grob*me)
98 {
99   Direction d = Directional_element_interface::get (me);
100
101   if (!d)
102     {
103        d = get_default_dir (me);
104        // urg, AAARGH!
105        Directional_element_interface::set (me, d);
106     }
107   return d ;
108 }
109
110
111 void
112 Stem::set_stemend (Grob*me, Real se)
113 {
114   // todo: margins
115   Direction d= get_direction (me);
116   
117   if (d && d * head_positions (me)[get_direction (me)] >= se*d)
118     me->warning (_ ("Weird stem size; check for narrow beams"));
119
120   me->set_grob_property ("stem-end-position", gh_double2scm (se));
121 }
122
123
124 /*
125   Note head that determines hshift for upstems
126  */ 
127 Grob*
128 Stem::support_head (Grob*me)
129 {
130   SCM h = me->get_grob_property ("support-head");
131   Grob * nh = unsmob_grob (h);
132   if (nh)
133     return nh;
134   else if (head_count (me) == 1)
135     {
136       /*
137         UGH.
138        */
139       
140       return unsmob_grob (ly_car (me->get_grob_property ("note-heads")));
141     }
142   else
143     return first_head (me);
144 }
145
146
147 int
148 Stem::head_count (Grob*me)
149 {
150   return  Pointer_group_interface::count (me, "note-heads");
151 }
152
153 /*
154   The note head which forms one end of the stem.  
155  */
156 Grob*
157 Stem::first_head (Grob*me)
158 {
159   Direction d = get_direction (me);
160   if (!d)
161     return 0;
162   return extremal_heads (me)[-d];
163 }
164
165 /*
166   The note head opposite to the first head.
167  */
168 Grob*
169 Stem::last_head (Grob*me)
170 {
171   Direction d = get_direction (me);
172   if (!d)
173     return 0;
174   return extremal_heads (me)[d];
175 }
176
177 /*
178   START is part where stem reaches `last' head. 
179  */
180 Drul_array<Grob*>
181 Stem::extremal_heads (Grob*me) 
182 {
183   const int inf = 1000000;
184   Drul_array<int> extpos;
185   extpos[DOWN] = inf;
186   extpos[UP] = -inf;  
187   
188   Drul_array<Grob *> exthead;
189   exthead[LEFT] = exthead[RIGHT] =0;
190   
191   for (SCM s = me->get_grob_property ("note-heads"); gh_pair_p (s); s = ly_cdr (s))
192     {
193       Grob * n = unsmob_grob (ly_car (s));
194
195       
196       int p = int (Staff_symbol_referencer::position_f (n));
197
198       Direction d = LEFT;
199       do {
200       if (d* p > d* extpos[d])
201         {
202           exthead[d] = n;
203           extpos[d] = p;
204         }
205       } while (flip (&d) != DOWN);
206     }
207
208   return exthead;
209 }
210
211 static int
212 icmp (int const &a, int const &b)
213 {
214   return a-b;
215 }
216
217 Array<int>
218 Stem::note_head_positions (Grob *me)
219 {
220   Array<int> ps ;
221   for (SCM s = me->get_grob_property ("note-heads"); gh_pair_p (s); s = ly_cdr (s))
222     {
223       Grob * n = unsmob_grob (ly_car (s));
224       int p = int (Staff_symbol_referencer::position_f (n));
225
226       ps.push (p);
227     }
228
229   ps.sort (icmp);
230   return ps; 
231 }
232
233
234 void
235 Stem::add_head (Grob*me, Grob *n)
236 {
237   n->set_grob_property ("stem", me->self_scm ());
238   n->add_dependency (me);
239
240   if (Note_head::has_interface (n))
241     {
242       Pointer_group_interface::add_grob (me, ly_symbol2scm ("note-heads"), n);
243     }
244 }
245
246 bool
247 Stem::invisible_b (Grob*me)
248 {
249   return ! (head_count (me) && Note_head::balltype_i (support_head (me)) >= 1);
250 }
251
252 Direction
253 Stem::get_default_dir (Grob*me) 
254 {
255   int staff_center = 0;
256   Interval hp = head_positions (me);
257   if (hp.empty_b())
258     {
259       return CENTER;
260     }
261   
262   int udistance = (int) (UP * hp[UP] - staff_center);
263   int ddistance = (int) (DOWN* hp[DOWN] - staff_center);  
264   
265   if (sign (ddistance - udistance))
266     return Direction (sign (ddistance -udistance));
267
268   return to_dir (me->get_grob_property ("neutral-direction"));
269 }
270
271 Real
272 Stem::get_default_stem_end_position (Grob*me) 
273 {
274   SCM up_to_staff = me->get_grob_property ("up-to-staff");
275   if (to_boolean(up_to_staff))
276     {
277       int line_count = Staff_symbol_referencer::line_count (me);
278     
279       Direction dir = get_direction (me);
280     
281       return dir*  (line_count + 3.5);
282     }
283   
284   bool grace_b = to_boolean (me->get_grob_property ("grace"));
285   SCM s;
286   Array<Real> a;
287
288   Real length_f = 0.;
289   SCM scm_len = me->get_grob_property ("length");
290   if (gh_number_p (scm_len))
291     {
292       length_f = gh_scm2double (scm_len);
293     }
294   else
295     {
296       s = me->get_grob_property ("lengths");
297       length_f = 3.5;
298       
299       if (gh_pair_p (s))
300         {
301           length_f = 2* gh_scm2double (robust_list_ref (duration_log(me) -2, s));
302         }
303     }
304
305
306   Real shorten_f = 0.0;
307   
308   SCM sshorten = me->get_grob_property ("stem-shorten");
309   if (gh_pair_p (sshorten))
310     {
311       shorten_f = 2* gh_scm2double (robust_list_ref ((duration_log (me) - 2) >? 0, sshorten));
312     }
313
314   /* On boundary: shorten only half */
315   if (abs (chord_start_y (me)) == 0.5)
316     shorten_f *= 0.5;
317
318   /* URGURGURG
319      'set-default-stemlen' sets direction too
320    */
321   Direction dir = get_direction (me);
322   if (!dir)
323     {
324       dir = get_default_dir (me);
325       Directional_element_interface::set (me, dir);
326     }
327   
328   /* stems in unnatural (forced) direction should be shortened, 
329     according to [Roush & Gourlay] */
330   if (chord_start_y (me)
331       && (get_direction (me) != get_default_dir (me)))
332     length_f -= shorten_f;
333
334   Interval hp = head_positions (me);  
335   Real st = hp[dir] + dir * length_f;
336
337   bool no_extend_b = to_boolean (me->get_grob_property ("no-stem-extend"));
338   if (!grace_b && !no_extend_b && dir * st < 0) // junkme?
339     st = 0.0;
340
341   /*
342     Make a little room if we have a upflag and there is a dot.
343     previous approach was to lengthen the stem. This is not
344     good typesetting practice. 
345     
346   */
347   if (!beam_l (me) && dir == UP
348       && duration_log (me) > 2)
349     {
350       Grob * closest_to_flag = extremal_heads (me)[dir];
351       Grob * dots = closest_to_flag
352         ? Rhythmic_head::dots_l (closest_to_flag ) : 0;
353
354       if (dots)
355         {
356           Real dp = Staff_symbol_referencer::position_f  (dots);
357           Real flagy =  flag (me).extent (Y_AXIS)[-dir] * 2
358             / Staff_symbol_referencer::staff_space (me); 
359
360           /*
361             Very gory: add myself to the X-support of the parent,
362             which should be a dot-column.
363            */
364           if (dir * (st + flagy -  dp) < 0.5)
365             {
366               Grob *par = dots->get_parent (X_AXIS);
367
368               if (Dot_column::has_interface (par))
369                 {
370                   Side_position_interface::add_support (par, me);
371
372                   /*
373                     TODO: apply some better logic here. The flag is
374                     curved inwards, so this will typically be too
375                     much.
376                   */
377                 }
378             }
379         }
380     }
381
382
383   return st;
384 }
385
386
387
388 /*
389   
390   the log of the duration (Number of hooks on the flag minus two)
391  */
392 int
393 Stem::duration_log (Grob*me) 
394 {
395   SCM s = me->get_grob_property ("duration-log");
396   return (gh_number_p (s)) ? gh_scm2int (s) : 2;
397 }
398
399 void
400 Stem::position_noteheads (Grob*me)
401 {
402   if (!head_count (me))
403     return;
404   
405   Link_array<Grob> heads =
406     Pointer_group_interface__extract_grobs (me, (Grob*)0, "note-heads");
407
408   heads.sort (compare_position);
409   Direction dir =get_direction (me);
410   
411   if (dir < 0)
412     heads.reverse ();
413
414
415   bool invisible = invisible_b (me);
416   Real thick = 0.0;
417   if (invisible)
418         thick = gh_scm2double (me->get_grob_property ("thickness"))
419           * me->paper_l ()->get_var ("linethickness");
420       
421
422   Grob *hed = support_head (me);
423   Real w = Note_head::head_extent (hed,X_AXIS)[dir];
424   for (int i=0; i < heads.size (); i++)
425     {
426       heads[i]->translate_axis (w - Note_head::head_extent (heads[i],X_AXIS)[dir],
427                                 X_AXIS);
428     }
429   
430   bool parity= true;            // todo: make me settable.
431   int lastpos = int (Staff_symbol_referencer::position_f (heads[0]));
432   for (int i=1; i < heads.size (); i ++)
433     {
434       Real p = Staff_symbol_referencer::position_f (heads[i]);
435       int dy =abs (lastpos- (int)p);
436
437       if (dy <= 1)
438         {
439           if (parity)
440             {
441               Real l = Note_head::head_extent (heads[i], X_AXIS).length ();
442
443               Direction d = get_direction (me);
444               heads[i]->translate_axis (l * d, X_AXIS);
445
446               if (invisible_b(me))
447                 heads[i]->translate_axis (-thick *2* d , X_AXIS);
448
449               
450              /* TODO:
451                  
452               For some cases we should kern some more: when the
453               distance between the next or prev note is too large, we'd 
454               get large white gaps, eg.
455               
456                |
457               X|
458                |X  <- kern this.
459                |
460               X
461               
462               */
463             }
464           parity = !parity;
465         }
466       else
467         parity = true;
468       
469       lastpos = int (p);
470     }
471 }
472
473 MAKE_SCHEME_CALLBACK (Stem,before_line_breaking,1);
474 SCM
475 Stem::before_line_breaking (SCM smob)
476 {
477   Grob*me = unsmob_grob (smob);
478
479
480   /*
481     Do the calculations for visible stems, but also for invisible stems
482     with note heads (i.e. half notes.)
483    */
484   if (head_count (me))
485     {
486       stem_end_position (me);   // ugh. Trigger direction calc.
487       position_noteheads (me);
488     }
489   else
490     {
491       me->remove_grob_property ("molecule-callback");
492     }
493   
494   return SCM_UNSPECIFIED;
495 }
496
497 /*
498   ugh.
499   When in a beam with tuplet brackets, brew_mol is called early,
500   caching a wrong value.
501  */
502 MAKE_SCHEME_CALLBACK (Stem, height, 2);
503 SCM
504 Stem::height (SCM smob, SCM ax)
505 {
506   Axis a = (Axis)gh_scm2int (ax);
507   Grob * me = unsmob_grob (smob);
508   assert (a == Y_AXIS);
509
510   SCM mol = me->get_uncached_molecule ();
511   Interval iv;
512   if (mol != SCM_EOL)
513     iv = unsmob_molecule (mol)->extent (a);
514   return ly_interval2scm (iv);
515 }
516
517
518 Molecule
519 Stem::flag (Grob*me)
520 {
521   /* TODO: rename flag-style into something more appropriate,
522    e.g. "stroke-style", maybe with values "" (i.e. no stroke),
523    "single" and "double".  Needs more discussion.
524   */
525   String style, fstyle, staffline_offs;
526   SCM fst = me->get_grob_property ("flag-style");
527   if (gh_string_p (fst))
528     {
529       fstyle = ly_scm2string (fst);
530     }
531
532   SCM st = me->get_grob_property ("style");
533   if (gh_symbol_p (st))
534     {
535       style = (ly_scm2string (scm_symbol_to_string (st)));
536     }
537   else
538     {
539       style = "";
540     }
541   bool adjust = to_boolean (me->get_grob_property ("adjust-if-on-staffline"));
542
543   if (String::compare_i (style, "mensural") == 0)
544     /* Mensural notation: For notes on staff lines, use different
545        flags than for notes between staff lines.  The idea is that
546        flags are always vertically aligned with the staff lines,
547        regardless if the note head is on a staff line or between two
548        staff lines.  In other words, the inner end of a flag always
549        touches a staff line.
550     */
551     {
552       if (adjust)
553         {
554           /* Urrgh!  We have to detect wether this stem ends on a staff
555              line or between two staff lines.  But we can not call
556              stem_end_position(me) or get_default_stem_end_position(me),
557              since this encounters the flag and hence results in an
558              infinite recursion.  However, in pure mensural notation,
559              there are no multiple note heads attached to a single stem,
560              neither is there usually need for using the stem_shorten
561              property (except for 32th and 64th notes, but that is not a
562              problem since the stem length in this case is augmented by
563              an integral multiple of staff_space).  Hence, it should be
564              sufficient to just take the first note head, assume it's
565              the only one, look if it's on a staff line, and select the
566              flag's shape accordingly.  In the worst case, the shape
567              looks slightly misplaced, but that will usually be the
568              programmer's fault (e.g. when trying to attach multiple
569              note heads to a single stem in mensural notation).  */
570
571           /*
572             perhaps the detection whether this correction is needed should
573             happen in a different place  to avoid the recursion.
574             
575             --hwn.
576           */
577           Grob *first = first_head(me);
578           int sz = Staff_symbol_referencer::line_count (me)-1;
579           int p = (int)rint (Staff_symbol_referencer::position_f (first));
580           staffline_offs = (((p ^ sz) & 0x1) == 0) ? "1" : "0";
581         }
582       else
583         {
584           staffline_offs = "2";
585         }
586     }
587   else
588     {
589       staffline_offs = "";
590     }
591   char c = (get_direction (me) == UP) ? 'u' : 'd';
592   String index_str
593     = String ("flags-") + style + to_str (c) + staffline_offs + to_str (duration_log (me));
594   Molecule m
595     = Font_interface::get_default_font (me)->find_by_name (index_str);
596   if (!fstyle.empty_b ())
597     m.add_molecule (Font_interface::get_default_font (me)->find_by_name (String ("flags-") + to_str (c) + fstyle));
598   return m;
599 }
600
601 MAKE_SCHEME_CALLBACK (Stem,dim_callback,2);
602 SCM
603 Stem::dim_callback (SCM e, SCM ax)
604 {
605   Axis a = (Axis) gh_scm2int (ax);
606   assert (a == X_AXIS);
607   Grob *se = unsmob_grob (e);
608   Interval r (0, 0);
609   if (unsmob_grob (se->get_grob_property ("beam")) || abs (duration_log (se)) <= 2)
610     ;   // TODO!
611   else
612     {
613       r = flag (se).extent (X_AXIS);
614     }
615   return ly_interval2scm (r);
616 }
617  
618
619
620 MAKE_SCHEME_CALLBACK (Stem,brew_molecule,1);
621
622 SCM
623 Stem::brew_molecule (SCM smob) 
624 {
625   Grob*me = unsmob_grob (smob);
626   Molecule mol;
627   Direction d = get_direction (me);
628   
629   
630      
631   Real y1;
632
633   /*
634     This is required to avoid stems passing in tablature chords...
635    */
636
637
638   /*
639     TODO: make  the stem start a direction ?
640   */
641   
642
643   
644   if (to_boolean (me->get_grob_property ("avoid-note-head")))
645     {
646       Grob * lh = last_head (me);
647       if (!lh)
648         return SCM_EOL;
649       y1 = Staff_symbol_referencer::position_f (lh);
650     }
651   else
652     {
653       Grob * lh = first_head (me);
654       if (!lh)
655         return SCM_EOL;
656       y1 = Staff_symbol_referencer::position_f (lh);
657     }
658   
659   Real y2 = stem_end_position (me);
660   
661   Interval stem_y (y1 <? y2,y2 >? y1);
662
663
664   // dy?
665   Real dy = Staff_symbol_referencer::staff_space (me) * 0.5;
666
667   if (Grob *hed = support_head (me))
668     {
669       /*
670         must not take ledgers into account.
671        */
672       Interval head_height = Note_head::head_extent (hed,Y_AXIS);
673       Real y_attach = Note_head::stem_attachment_coordinate ( hed, Y_AXIS);
674
675       y_attach = head_height.linear_combination (y_attach);
676       stem_y[Direction (-d)] += d * y_attach/dy;
677     }
678   
679   if (!invisible_b (me))
680     {
681       Real stem_width = gh_scm2double (me->get_grob_property ("thickness"))
682         // URG
683         * me->paper_l ()->get_var ("linethickness");
684       
685       Molecule ss =Lookup::filledbox (Box (Interval (-stem_width/2, stem_width/2),
686                                            Interval (stem_y[DOWN]*dy, stem_y[UP]*dy)));
687       mol.add_molecule (ss);
688     }
689
690   if (!beam_l (me) && abs (duration_log (me)) > 2)
691     {
692       Molecule fl = flag (me);
693       fl.translate_axis (stem_y[d]*dy, Y_AXIS);
694       mol.add_molecule (fl);
695     }
696
697   return mol.smobbed_copy ();
698 }
699
700 /*
701   move the stem to right of the notehead if it is up.
702  */
703 MAKE_SCHEME_CALLBACK (Stem,off_callback,2);
704 SCM
705 Stem::off_callback (SCM element_smob, SCM)
706 {
707   Grob *me = unsmob_grob (element_smob);
708   
709   Real r=0;
710
711   if (head_count (me) == 0)
712     {
713       return gh_double2scm (0.0);
714     }
715   
716   if (Grob * f = first_head (me))
717     {
718       Interval head_wid = Note_head::head_extent(f, X_AXIS);
719
720       
721       Real attach =0.0;
722
723       if (invisible_b (me))
724         {
725           attach = 0.0;
726         }
727       else
728         attach = Note_head::stem_attachment_coordinate(f, X_AXIS);
729
730       Direction d = get_direction (me);
731
732       Real real_attach = head_wid.linear_combination (d * attach);
733
734       r = real_attach;
735
736       /*
737         If not centered: correct for stem thickness.
738        */
739       if (attach)
740         {
741           Real rule_thick
742             = gh_scm2double (me->get_grob_property ("thickness"))
743             * me->paper_l ()->get_var ("linethickness");
744
745           
746           r += - d * rule_thick * 0.5;
747         }
748     }
749   return gh_double2scm (r);
750 }
751
752
753
754 Grob*
755 Stem::beam_l (Grob*me)
756 {
757   SCM b=  me->get_grob_property ("beam");
758   return unsmob_grob (b);
759 }
760
761 // ugh still very long.
762 Stem_info
763 Stem::calc_stem_info (Grob*me) 
764 {
765   SCM up_to_staff = me->get_grob_property ("up-to-staff");
766   if (gh_scm2bool(up_to_staff)) {
767     
768     // Up-to-staff : the stem end out of the staff.
769
770     /*
771       FIXME: duplicate code.
772      */
773     int line_count = Staff_symbol_referencer::line_count (me);
774     
775     Stem_info si ;
776     
777     Direction dir = get_direction (me);
778     
779     si.ideal_y_ = dir*  (line_count + 1.5);
780     si.dir_ = dir;
781     si.shortest_y_ = si.ideal_y_; 
782     
783     return si;
784   }
785   
786   SCM scm_info = me->get_grob_property ("stem-info");
787
788   if (gh_pair_p (scm_info ))
789     {
790       Stem_info si ;
791
792       si.dir_ = Directional_element_interface::get(me); 
793       si.ideal_y_ = gh_scm2double (gh_car (scm_info)); 
794       si.shortest_y_ = gh_scm2double (gh_cadr (scm_info));
795
796       return si;
797     }
798
799   Direction mydir = Directional_element_interface::get (me);
800   Real staff_space = Staff_symbol_referencer::staff_space (me);
801   Real half_space = staff_space / 2;
802
803   Grob * beam = beam_l (me);
804   int beam_count = beam_multiplicity(me).length()+1;
805   Real beam_translation= Beam::get_beam_translation (beam);
806   Real thick = gh_scm2double (beam->get_grob_property ("thickness"));
807   Real note_start = chord_start_y (me);
808   
809   /* from here on, calculate as if dir == UP */
810   note_start *= mydir;
811   
812   SCM grace_prop = me->get_grob_property ("grace");
813   
814   bool grace_b = to_boolean (grace_prop);
815   SCM bml = robust_list_ref ( beam_count ,
816                               me->get_grob_property ("beamed-minimum-lengths"));
817
818   Real minimum_length = gh_scm2double(bml)*staff_space;
819   SCM bl =  robust_list_ref ( beam_count ,
820                               me->get_grob_property ("beamed-lengths"));
821   Real stem_length =  gh_scm2double(bl) * staff_space;
822
823
824   /*
825     stem goes to center of beam, hence 0.5
826    */
827   Real beam_lengthen = beam_translation* (beam_count - 1)
828     + ((beam_count > 0) ? thick : 0) - 0.5 * thick;
829
830   Real shortest_y = note_start + minimum_length + beam_lengthen;
831   Real ideal_y = stem_length + note_start + beam_lengthen;
832
833   /*
834     lowest beam of (UP) beam must never be lower than second staffline
835
836     Hmm, reference (Wanske?)
837
838     Although this (additional) rule is probably correct,
839     I expect that highest beam (UP) should also never be lower
840     than middle staffline, just as normal stems.
841         
842   */
843   bool no_extend_b = to_boolean (me->get_grob_property ("no-stem-extend"));
844   if (!grace_b && !no_extend_b)
845     {
846       /* highest beam of (UP) beam must never be lower than middle
847          staffline
848          lowest beam of (UP) beam must never be lower than second staffline
849       */
850       ideal_y =
851         ideal_y >? 0
852         >? (- 2 * half_space - thick + beam_lengthen);
853     }
854     
855   
856   //  ideal_y = ideal_y >? shortest_y;
857   SCM s = beam->get_grob_property ("shorten");
858   if (gh_number_p (s))
859     ideal_y -= gh_scm2double (s);
860
861   
862   ideal_y *= mydir;
863   shortest_y *= mydir; 
864   
865   me->set_grob_property ("stem-info",
866                          scm_list_n (gh_double2scm (ideal_y),
867                                      gh_double2scm (shortest_y),
868                                      SCM_UNDEFINED));
869
870   Stem_info si;
871   si.dir_ = mydir;
872   si.shortest_y_ = shortest_y;
873   si.ideal_y_ = ideal_y;
874   
875   return si;
876 }
877
878
879 // move to stem?
880 Slice
881 Stem::beam_multiplicity (Grob *stem)
882 {
883   SCM beaming= stem->get_grob_property ("beaming");
884   Slice l = int_list_to_slice (gh_car (beaming));
885   Slice r = int_list_to_slice (gh_cdr (beaming));
886   l.unite (r);
887
888   return l;
889 }
890
891
892 ADD_INTERFACE (Stem,"stem-interface",
893   "A stem",
894   "up-to-staff avoid-note-head adjust-if-on-staffline thickness stem-info beamed-lengths beamed-minimum-lengths lengths beam stem-shorten duration-log beaming neutral-direction stem-end-position support-head note-heads direction length style no-stem-extend flag-style dir-forced");
895
896