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