]> git.donarmstrong.com Git - lilypond.git/blob - lily/stem.cc
release: 1.5.29
[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 (0),gh_int2scm (0));
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 0;
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     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   else
233     {
234       n->set_grob_property ("rest", n->self_scm ());
235     }
236 }
237
238 bool
239 Stem::invisible_b (Grob*me)
240 {
241   return ! (heads_i (me) && Rhythmic_head::balltype_i (support_head (me)) >= 1);
242 }
243
244 int
245 Stem::get_center_distance (Grob*me, Direction d)
246 {
247   int staff_center = 0;
248   int distance = (int) (d* (head_positions (me)[d] - staff_center));
249   return distance >? 0;
250 }
251
252 Direction
253 Stem::get_default_dir (Grob*me) 
254 {
255   int du = get_center_distance (me,UP);
256   int dd = get_center_distance (me,DOWN);
257
258   if (sign (dd - du))
259     return Direction (sign (dd -du));
260
261   return to_dir (me->get_grob_property ("neutral-direction"));
262 }
263
264 Real
265 Stem::get_default_stem_end_position (Grob*me) 
266 {
267   bool grace_b = to_boolean (me->get_grob_property ("grace"));
268   SCM s;
269   Array<Real> a;
270
271   Real length_f = 0.;
272   SCM scm_len = me->get_grob_property ("length");
273   if (gh_number_p (scm_len))
274     {
275       length_f = gh_scm2double (scm_len);
276     }
277   else
278     {
279       s = me->get_grob_property ("lengths");
280       for (SCM q = s; q != SCM_EOL; q = ly_cdr (q))
281         a.push (gh_scm2double (ly_car (q)));
282                 
283       // stem uses half-spaces
284       length_f = a[ ((flag_i (me) - 2) >? 0) <? (a.size () - 1)] * 2;
285     }
286
287
288   a.clear ();
289   s = me->get_grob_property ("stem-shorten");
290   for (SCM q = s; gh_pair_p (q); q = ly_cdr (q))
291     a.push (gh_scm2double (ly_car (q)));
292
293
294   // stem uses half-spaces
295
296   // fixme: use scm_list_n_ref () iso. array[]
297   Real shorten_f = a[ ((flag_i (me) - 2) >? 0) <? (a.size () - 1)] * 2;
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   /* 
310     stems in unnatural (forced) direction should be shortened, 
311     according to [Roush & Gourlay]
312    */
313   if (( (int)chord_start_f (me))
314       && (get_direction (me) != get_default_dir (me)))
315     length_f -= shorten_f;
316
317   Interval hp = head_positions (me);  
318   Real st = hp[dir] + dir * length_f;
319
320
321   
322
323   /*
324     Make a little room if we have a flag and there is a dot.
325
326     TODO:
327
328     maybe  we should consider moving the dot to the right?
329   */
330   if (!beam_l (me)
331       && flag_i (me))
332     {
333       Grob * closest_to_flag = extremal_heads (me)[dir];
334       Grob * dots = closest_to_flag
335         ? Rhythmic_head::dots_l (closest_to_flag ) : 0;
336
337       if (dots)
338         {
339           Real dp = Staff_symbol_referencer::position_f  (dots);
340           Real flagy =  flag (me).extent (Y_AXIS)[-dir] * 2; // should divide by staffspace
341
342           /*
343             Very gory: add myself to the X-support of the parent,
344             which should be a dot-column.
345            */
346           if (dir * (st + flagy -  dp) < 0.5)
347             Side_position_interface::add_support (dots->get_parent (X_AXIS), me);
348
349           /*
350             previous approach was to lengthen the stem. This is not
351             good typesetting practice.  */
352         }
353     }
354
355
356   bool no_extend_b = to_boolean (me->get_grob_property ("no-stem-extend"));
357    if (!grace_b && !no_extend_b && dir * st < 0) // junkme?
358       st = 0.0;
359
360   return st;
361 }
362
363
364
365 /*
366   Number of hooks on the flag, ie. the log of the duration.
367  */
368 int
369 Stem::flag_i (Grob*me) 
370 {
371   SCM s = me->get_grob_property ("duration-log");
372   return (gh_number_p (s)) ? gh_scm2int (s) : 2;
373 }
374
375 void
376 Stem::position_noteheads (Grob*me)
377 {
378   if (!heads_i (me))
379     return;
380   
381   Link_array<Grob> heads =
382     Pointer_group_interface__extract_grobs (me, (Grob*)0, "heads");
383
384   heads.sort (compare_position);
385   Direction dir =get_direction (me);
386   
387   if (dir < 0)
388     heads.reverse ();
389
390
391   Grob *hed = support_head (me);
392   Real w = Note_head::head_extent (hed,X_AXIS)[dir];
393   for (int i=0; i < heads.size (); i++)
394     {
395       heads[i]->translate_axis (w - Note_head::head_extent (heads[i],X_AXIS)[dir],
396                                 X_AXIS);
397     }
398   
399   bool parity= true;            // todo: make me settable.
400   int lastpos = int (Staff_symbol_referencer::position_f (heads[0]));
401   for (int i=1; i < heads.size (); i ++)
402     {
403       Real p = Staff_symbol_referencer::position_f (heads[i]);
404       int dy =abs (lastpos- (int)p);
405
406       if (dy <= 1)
407         {
408           if (parity)
409             {
410               Real l = heads[i]->extent (heads[i], X_AXIS).length ();
411               heads[i]->translate_axis (l * get_direction (me), X_AXIS);
412             }
413           parity = !parity;
414         }
415       else
416         parity = true;
417       
418       lastpos = int (p);
419     }
420 }
421
422 MAKE_SCHEME_CALLBACK (Stem,before_line_breaking,1);
423 SCM
424 Stem::before_line_breaking (SCM smob)
425 {
426   Grob*me = unsmob_grob (smob);
427   stem_end_position (me);       // ugh. Trigger direction calc.
428   position_noteheads (me);
429
430   if (invisible_b (me))
431     {
432       me->remove_grob_property ("molecule-callback");
433       // suicide ();
434     }
435   
436   return SCM_UNSPECIFIED;
437 }
438
439 /*
440   ugh.
441   When in a beam with tuplet brackets, brew_mol is called early,
442   caching a wrong value.
443  */
444 MAKE_SCHEME_CALLBACK (Stem, height, 2);
445 SCM
446 Stem::height (SCM smob, SCM ax)
447 {
448   Axis a = (Axis)gh_scm2int (ax);
449   Grob * me = unsmob_grob (smob);
450   assert (a == Y_AXIS);
451
452   SCM mol = me->get_uncached_molecule ();
453   Interval iv;
454   if (mol != SCM_EOL)
455     iv = unsmob_molecule (mol)->extent (a);
456   return ly_interval2scm (iv);
457 }
458
459
460 Molecule
461 Stem::flag (Grob*me)
462 {
463   /* TODO: rename flag-style into something more appropriate,
464    e.g. "stroke-style", maybe with values "" (i.e. no stroke),
465    "single" and "double".  Needs more discussion.
466   */
467   String style, fstyle, staffline_offs;
468   SCM fst = me->get_grob_property ("flag-style");
469   if (gh_string_p (fst))
470     {
471       fstyle = ly_scm2string (fst);
472     }
473
474   SCM st = me->get_grob_property ("style");
475   if (gh_symbol_p (st))
476     {
477       style = (ly_scm2string (scm_symbol_to_string (st)));
478     }
479   else
480     {
481       style = "";
482     }
483   if (String::compare_i (style, "mensural") == 0)
484     /* Mensural notation: For notes on staff lines, use different
485        flags than for notes between staff lines.  The idea is that
486        flags are always vertically aligned with the staff lines,
487        regardless if the note head is on a staff line or between two
488        staff lines.  In other words, the inner end of a flag always
489        touches a staff line.
490     */
491     {
492       /* Urrgh!  We have to detect wether this stem ends on a staff
493          line or between two staff lines.  But we can not call
494          stem_end_position(me) or get_default_stem_end_position(me),
495          since this encounters the flag and hence results in an
496          infinite recursion.  However, in pure mensural notation,
497          there are no multiple note heads attached to a single stem,
498          neither is there usually need for using the stem_shorten
499          property (except for 32th and 64th notes, but that is not a
500          problem since the stem length in this case is augmented by
501          an integral multiple of staff_space).  Hence, it should be
502          sufficient to just take the first note head, assume it's
503          the only one, look if it's on a staff line, and select the
504          flag's shape accordingly.  In the worst case, the shape
505          looks slightly misplaced, but that will usually be the
506          programmer's fault (e.g. when trying to attach multiple
507          note heads to a single stem in mensural notation).
508
509       */
510         Grob *first = first_head(me);
511         int sz = Staff_symbol_referencer::line_count (me)-1;
512         int p = (int)rint (Staff_symbol_referencer::position_f (first));
513         staffline_offs = (((p ^ sz) & 0x1) == 0) ? "1" : "0";
514     }
515   else
516     {
517         staffline_offs = "";
518     }
519   char c = (get_direction (me) == UP) ? 'u' : 'd';
520   String index_str
521     = String ("flags-") + style + to_str (c) + staffline_offs + to_str (flag_i (me));
522   Molecule m
523     = Font_interface::get_default_font (me)->find_by_name (index_str);
524   if (!fstyle.empty_b ())
525     m.add_molecule (Font_interface::get_default_font (me)->find_by_name (String ("flags-") + to_str (c) + fstyle));
526   return m;
527 }
528
529 MAKE_SCHEME_CALLBACK (Stem,dim_callback,2);
530 SCM
531 Stem::dim_callback (SCM e, SCM ax)
532 {
533   Axis a = (Axis) gh_scm2int (ax);
534   assert (a == X_AXIS);
535   Grob *se = unsmob_grob (e);
536   Interval r (0, 0);
537   if (unsmob_grob (se->get_grob_property ("beam")) || abs (flag_i (se)) <= 2)
538     ;   // TODO!
539   else
540     {
541       r = flag (se).extent (X_AXIS);
542     }
543   return ly_interval2scm (r);
544 }
545  
546
547
548 MAKE_SCHEME_CALLBACK (Stem,brew_molecule,1);
549
550 SCM
551 Stem::brew_molecule (SCM smob) 
552 {
553   Grob*me = unsmob_grob (smob);
554   Molecule mol;
555   Direction d = get_direction (me);
556   
557   
558   Real y1 = Staff_symbol_referencer::position_f (first_head (me));
559   Real y2 = stem_end_position (me);
560   
561   Interval stem_y (y1 <? y2,y2 >? y1);
562
563
564   // dy?
565   Real dy = Staff_symbol_referencer::staff_space (me) * 0.5;
566
567   if (Grob *hed = support_head (me))
568     {
569       /*
570         must not take ledgers into account.
571        */
572       Interval head_height = Note_head::head_extent (hed,Y_AXIS);
573       Real y_attach = Note_head::stem_attachment_coordinate ( hed, Y_AXIS);
574
575       y_attach = head_height.linear_combination (y_attach);
576       stem_y[Direction (-d)] += d * y_attach/dy;
577     }
578   
579   if (!invisible_b (me))
580     {
581       Real stem_width = gh_scm2double (me->get_grob_property ("thickness"))
582         // URG
583         * me->paper_l ()->get_var ("stafflinethickness");
584       
585       Molecule ss =Lookup::filledbox (Box (Interval (-stem_width/2, stem_width/2),
586                                            Interval (stem_y[DOWN]*dy, stem_y[UP]*dy)));
587       mol.add_molecule (ss);
588     }
589
590   if (!beam_l (me) && abs (flag_i (me)) > 2)
591     {
592       Molecule fl = flag (me);
593       fl.translate_axis (stem_y[d]*dy, Y_AXIS);
594       mol.add_molecule (fl);
595     }
596
597   return mol.smobbed_copy ();
598 }
599
600 /*
601   move the stem to right of the notehead if it is up.
602  */
603 MAKE_SCHEME_CALLBACK (Stem,off_callback,2);
604 SCM
605 Stem::off_callback (SCM element_smob, SCM)
606 {
607   Grob *me = unsmob_grob (element_smob);
608   
609   Real r=0;
610   if (Grob * f = first_head (me))
611     {
612       Interval head_wid = Note_head::head_extent(f, X_AXIS);
613
614       Real attach =
615         Note_head::stem_attachment_coordinate(f, X_AXIS);
616
617       Direction d = get_direction (me);
618
619       Real real_attach = head_wid.linear_combination (d * attach);
620
621       r = real_attach;
622
623       /*
624         If not centered: correct for stem thickness.
625        */
626       if (attach)
627         {
628           Real rule_thick
629             = gh_scm2double (me->get_grob_property ("thickness"))
630             * me->paper_l ()->get_var ("stafflinethickness");
631
632           
633           r += - d * rule_thick * 0.5;
634         }
635     }
636   return gh_double2scm (r);
637 }
638
639
640
641 Grob*
642 Stem::beam_l (Grob*me)
643 {
644   SCM b=  me->get_grob_property ("beam");
645   return unsmob_grob (b);
646 }
647
648
649 // ugh still very long.
650 Stem_info
651 Stem::calc_stem_info (Grob*me) 
652 {
653   Grob * beam = beam_l (me);
654
655   Direction beam_dir = Directional_element_interface::get (beam);
656   if (!beam_dir)
657     {
658       programming_error ("Beam dir not set.");
659       beam_dir = UP;
660     }
661     
662
663   Real staff_space = Staff_symbol_referencer::staff_space (me);
664   Real half_space = staff_space / 2;
665   int multiplicity = Beam::get_multiplicity (beam);
666
667
668   SCM space_proc = beam->get_grob_property ("space-function");
669   SCM space = gh_call1 (space_proc, gh_int2scm (multiplicity));
670   Real interbeam_f = gh_scm2double (space) * staff_space;
671
672   Real thick = gh_scm2double (beam->get_grob_property ("thickness"));
673   Stem_info info; 
674   info.idealy_f_ = chord_start_f (me);
675
676   // for simplicity, we calculate as if dir == UP
677   info.idealy_f_ *= beam_dir;
678   SCM grace_prop = me->get_grob_property ("grace");
679
680   bool grace_b = to_boolean (grace_prop);
681   
682   Array<Real> a;
683   SCM s;
684   
685   s = me->get_grob_property ("beamed-minimum-lengths");
686   a.clear ();
687   for (SCM q = s; q != SCM_EOL; q = ly_cdr (q))
688     a.push (gh_scm2double (ly_car (q)));
689
690
691   Real minimum_length = a[multiplicity <? (a.size () - 1)] * staff_space;
692   s = me->get_grob_property ("beamed-lengths");
693
694   a.clear ();
695   for (SCM q = s; q != SCM_EOL; q = ly_cdr (q))
696     a.push (gh_scm2double (ly_car (q)));
697
698   Real stem_length =  a[multiplicity <? (a.size () - 1)] * staff_space;
699
700   if (!beam_dir || (beam_dir == Directional_element_interface::get (me)))
701     /* normal beamed stem */
702     {
703       if (multiplicity)
704         {
705           info.idealy_f_ += thick + (multiplicity - 1) * interbeam_f;
706         }
707       info.miny_f_ = info.idealy_f_;
708       info.maxy_f_ = INT_MAX;
709
710       info.idealy_f_ += stem_length;
711       info.miny_f_ += minimum_length;
712
713       /*
714         lowest beam of (UP) beam must never be lower than second staffline
715
716         Hmm, reference (Wanske?)
717
718         Although this (additional) rule is probably correct,
719         I expect that highest beam (UP) should also never be lower
720         than middle staffline, just as normal stems.
721         
722       */
723       bool no_extend_b = to_boolean (me->get_grob_property ("no-stem-extend"));
724       if (!grace_b && !no_extend_b)
725         {
726           /* highest beam of (UP) beam must never be lower than middle
727              staffline
728              lowest beam of (UP) beam must never be lower than second staffline
729            */
730           info.miny_f_ =
731             info.miny_f_ >? 0
732             >? (- 2 * half_space - thick
733                 + (multiplicity > 0) * thick
734                 + interbeam_f * (multiplicity - 1));
735         }
736     }
737   else
738     /* knee */
739     {
740       info.idealy_f_ -= thick;
741       info.maxy_f_ = info.idealy_f_;
742       info.miny_f_ = -INT_MAX;
743
744       info.idealy_f_ -= stem_length;
745       info.maxy_f_ -= minimum_length;
746     }
747   
748   info.idealy_f_ = (info.maxy_f_ <? info.idealy_f_) >? info.miny_f_;
749
750   s = beam->get_grob_property ("shorten");
751   if (gh_number_p (s))
752     info.idealy_f_ -= gh_scm2double (s);
753
754  Grob *common = me->common_refpoint (beam, Y_AXIS);
755   Real interstaff_f = beam_dir *
756  (me->relative_coordinate (common, Y_AXIS)
757      - beam->relative_coordinate (common, Y_AXIS));
758
759   info.idealy_f_ += interstaff_f;
760   info.miny_f_ += interstaff_f;
761   info.maxy_f_ += interstaff_f ;
762
763   return info;
764 }
765
766 bool
767 Stem::has_interface (Grob*m)
768 {
769   return m && m->has_interface (ly_symbol2scm ("stem-interface"));
770 }
771
772 void
773 Stem::set_interface (Grob*me)
774 {    
775   me->set_interface (ly_symbol2scm ("stem-interface"));
776 }