]> git.donarmstrong.com Git - lilypond.git/blob - lily/stem.cc
release: 1.5.33
[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 = Note_head::head_extent (heads[i], X_AXIS).length ();
411
412               heads[i]->translate_axis (l * get_direction (me), X_AXIS);
413             }
414           parity = !parity;
415         }
416       else
417         parity = true;
418       
419       lastpos = int (p);
420     }
421 }
422
423 MAKE_SCHEME_CALLBACK (Stem,before_line_breaking,1);
424 SCM
425 Stem::before_line_breaking (SCM smob)
426 {
427   Grob*me = unsmob_grob (smob);
428   stem_end_position (me);       // ugh. Trigger direction calc.
429   position_noteheads (me);
430
431   if (invisible_b (me))
432     {
433       me->remove_grob_property ("molecule-callback");
434       // suicide ();
435     }
436   
437   return SCM_UNSPECIFIED;
438 }
439
440 /*
441   ugh.
442   When in a beam with tuplet brackets, brew_mol is called early,
443   caching a wrong value.
444  */
445 MAKE_SCHEME_CALLBACK (Stem, height, 2);
446 SCM
447 Stem::height (SCM smob, SCM ax)
448 {
449   Axis a = (Axis)gh_scm2int (ax);
450   Grob * me = unsmob_grob (smob);
451   assert (a == Y_AXIS);
452
453   SCM mol = me->get_uncached_molecule ();
454   Interval iv;
455   if (mol != SCM_EOL)
456     iv = unsmob_molecule (mol)->extent (a);
457   return ly_interval2scm (iv);
458 }
459
460
461 Molecule
462 Stem::flag (Grob*me)
463 {
464   /* TODO: rename flag-style into something more appropriate,
465    e.g. "stroke-style", maybe with values "" (i.e. no stroke),
466    "single" and "double".  Needs more discussion.
467   */
468   String style, fstyle, staffline_offs;
469   SCM fst = me->get_grob_property ("flag-style");
470   if (gh_string_p (fst))
471     {
472       fstyle = ly_scm2string (fst);
473     }
474
475   SCM st = me->get_grob_property ("style");
476   if (gh_symbol_p (st))
477     {
478       style = (ly_scm2string (scm_symbol_to_string (st)));
479     }
480   else
481     {
482       style = "";
483     }
484   bool adjust = to_boolean (me->get_grob_property ("adjust-if-on-staffline"));
485
486   if (String::compare_i (style, "mensural") == 0)
487     /* Mensural notation: For notes on staff lines, use different
488        flags than for notes between staff lines.  The idea is that
489        flags are always vertically aligned with the staff lines,
490        regardless if the note head is on a staff line or between two
491        staff lines.  In other words, the inner end of a flag always
492        touches a staff line.
493     */
494     {
495       if (adjust)
496         {
497           /* Urrgh!  We have to detect wether this stem ends on a staff
498              line or between two staff lines.  But we can not call
499              stem_end_position(me) or get_default_stem_end_position(me),
500              since this encounters the flag and hence results in an
501              infinite recursion.  However, in pure mensural notation,
502              there are no multiple note heads attached to a single stem,
503              neither is there usually need for using the stem_shorten
504              property (except for 32th and 64th notes, but that is not a
505              problem since the stem length in this case is augmented by
506              an integral multiple of staff_space).  Hence, it should be
507              sufficient to just take the first note head, assume it's
508              the only one, look if it's on a staff line, and select the
509              flag's shape accordingly.  In the worst case, the shape
510              looks slightly misplaced, but that will usually be the
511              programmer's fault (e.g. when trying to attach multiple
512              note heads to a single stem in mensural notation).  */
513           Grob *first = first_head(me);
514           int sz = Staff_symbol_referencer::line_count (me)-1;
515           int p = (int)rint (Staff_symbol_referencer::position_f (first));
516           staffline_offs = (((p ^ sz) & 0x1) == 0) ? "1" : "0";
517         }
518       else
519         {
520           staffline_offs = "2";
521         }
522     }
523   else
524     {
525       staffline_offs = "";
526     }
527   char c = (get_direction (me) == UP) ? 'u' : 'd';
528   String index_str
529     = String ("flags-") + style + to_str (c) + staffline_offs + to_str (flag_i (me));
530   Molecule m
531     = Font_interface::get_default_font (me)->find_by_name (index_str);
532   if (!fstyle.empty_b ())
533     m.add_molecule (Font_interface::get_default_font (me)->find_by_name (String ("flags-") + to_str (c) + fstyle));
534   return m;
535 }
536
537 MAKE_SCHEME_CALLBACK (Stem,dim_callback,2);
538 SCM
539 Stem::dim_callback (SCM e, SCM ax)
540 {
541   Axis a = (Axis) gh_scm2int (ax);
542   assert (a == X_AXIS);
543   Grob *se = unsmob_grob (e);
544   Interval r (0, 0);
545   if (unsmob_grob (se->get_grob_property ("beam")) || abs (flag_i (se)) <= 2)
546     ;   // TODO!
547   else
548     {
549       r = flag (se).extent (X_AXIS);
550     }
551   return ly_interval2scm (r);
552 }
553  
554
555
556 MAKE_SCHEME_CALLBACK (Stem,brew_molecule,1);
557
558 SCM
559 Stem::brew_molecule (SCM smob) 
560 {
561   Grob*me = unsmob_grob (smob);
562   Molecule mol;
563   Direction d = get_direction (me);
564   
565   
566   Real y1 = Staff_symbol_referencer::position_f (first_head (me));
567   Real y2 = stem_end_position (me);
568   
569   Interval stem_y (y1 <? y2,y2 >? y1);
570
571
572   // dy?
573   Real dy = Staff_symbol_referencer::staff_space (me) * 0.5;
574
575   if (Grob *hed = support_head (me))
576     {
577       /*
578         must not take ledgers into account.
579        */
580       Interval head_height = Note_head::head_extent (hed,Y_AXIS);
581       Real y_attach = Note_head::stem_attachment_coordinate ( hed, Y_AXIS);
582
583       y_attach = head_height.linear_combination (y_attach);
584       stem_y[Direction (-d)] += d * y_attach/dy;
585     }
586   
587   if (!invisible_b (me))
588     {
589       Real stem_width = gh_scm2double (me->get_grob_property ("thickness"))
590         // URG
591         * me->paper_l ()->get_var ("stafflinethickness");
592       
593       Molecule ss =Lookup::filledbox (Box (Interval (-stem_width/2, stem_width/2),
594                                            Interval (stem_y[DOWN]*dy, stem_y[UP]*dy)));
595       mol.add_molecule (ss);
596     }
597
598   if (!beam_l (me) && abs (flag_i (me)) > 2)
599     {
600       Molecule fl = flag (me);
601       fl.translate_axis (stem_y[d]*dy, Y_AXIS);
602       mol.add_molecule (fl);
603     }
604
605   return mol.smobbed_copy ();
606 }
607
608 /*
609   move the stem to right of the notehead if it is up.
610  */
611 MAKE_SCHEME_CALLBACK (Stem,off_callback,2);
612 SCM
613 Stem::off_callback (SCM element_smob, SCM)
614 {
615   Grob *me = unsmob_grob (element_smob);
616   
617   Real r=0;
618   if (Grob * f = first_head (me))
619     {
620       Interval head_wid = Note_head::head_extent(f, X_AXIS);
621
622       Real attach =
623         Note_head::stem_attachment_coordinate(f, X_AXIS);
624
625       Direction d = get_direction (me);
626
627       Real real_attach = head_wid.linear_combination (d * attach);
628
629       r = real_attach;
630
631       /*
632         If not centered: correct for stem thickness.
633        */
634       if (attach)
635         {
636           Real rule_thick
637             = gh_scm2double (me->get_grob_property ("thickness"))
638             * me->paper_l ()->get_var ("stafflinethickness");
639
640           
641           r += - d * rule_thick * 0.5;
642         }
643     }
644   return gh_double2scm (r);
645 }
646
647
648
649 Grob*
650 Stem::beam_l (Grob*me)
651 {
652   SCM b=  me->get_grob_property ("beam");
653   return unsmob_grob (b);
654 }
655
656
657 // ugh still very long.
658 Stem_info
659 Stem::calc_stem_info (Grob*me) 
660 {
661   Grob * beam = beam_l (me);
662
663   Direction beam_dir = Directional_element_interface::get (beam);
664   if (!beam_dir)
665     {
666       programming_error ("Beam dir not set.");
667       beam_dir = UP;
668     }
669     
670
671   Real staff_space = Staff_symbol_referencer::staff_space (me);
672   Real half_space = staff_space / 2;
673   int multiplicity = Beam::get_multiplicity (beam);
674
675
676   SCM space_proc = beam->get_grob_property ("space-function");
677   SCM space = gh_call1 (space_proc, gh_int2scm (multiplicity));
678   Real interbeam_f = gh_scm2double (space) * staff_space;
679
680   Real thick = gh_scm2double (beam->get_grob_property ("thickness"));
681   Stem_info info; 
682   info.idealy_f_ = chord_start_f (me);
683
684   // for simplicity, we calculate as if dir == UP
685   info.idealy_f_ *= beam_dir;
686   SCM grace_prop = me->get_grob_property ("grace");
687
688   bool grace_b = to_boolean (grace_prop);
689   
690   Array<Real> a;
691   SCM s;
692   
693   s = me->get_grob_property ("beamed-minimum-lengths");
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
699   Real minimum_length = a[multiplicity <? (a.size () - 1)] * staff_space;
700   s = me->get_grob_property ("beamed-lengths");
701
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   Real stem_length =  a[multiplicity <? (a.size () - 1)] * staff_space;
707
708   if (!beam_dir || (beam_dir == Directional_element_interface::get (me)))
709     /* normal beamed stem */
710     {
711       if (multiplicity)
712         {
713           info.idealy_f_ += thick + (multiplicity - 1) * interbeam_f;
714         }
715       info.miny_f_ = info.idealy_f_;
716       info.maxy_f_ = INT_MAX;
717
718       info.idealy_f_ += stem_length;
719       info.miny_f_ += minimum_length;
720
721       /*
722         lowest beam of (UP) beam must never be lower than second staffline
723
724         Hmm, reference (Wanske?)
725
726         Although this (additional) rule is probably correct,
727         I expect that highest beam (UP) should also never be lower
728         than middle staffline, just as normal stems.
729         
730       */
731       bool no_extend_b = to_boolean (me->get_grob_property ("no-stem-extend"));
732       if (!grace_b && !no_extend_b)
733         {
734           /* highest beam of (UP) beam must never be lower than middle
735              staffline
736              lowest beam of (UP) beam must never be lower than second staffline
737            */
738           info.miny_f_ =
739             info.miny_f_ >? 0
740             >? (- 2 * half_space - thick
741                 + (multiplicity > 0) * thick
742                 + interbeam_f * (multiplicity - 1));
743         }
744     }
745   else
746     /* knee */
747     {
748       info.idealy_f_ -= thick;
749       info.maxy_f_ = info.idealy_f_;
750       info.miny_f_ = -INT_MAX;
751
752       info.idealy_f_ -= stem_length;
753       info.maxy_f_ -= minimum_length;
754     }
755   
756   info.idealy_f_ = (info.maxy_f_ <? info.idealy_f_) >? info.miny_f_;
757
758   s = beam->get_grob_property ("shorten");
759   if (gh_number_p (s))
760     info.idealy_f_ -= gh_scm2double (s);
761
762  Grob *common = me->common_refpoint (beam, Y_AXIS);
763   Real interstaff_f = beam_dir *
764  (me->relative_coordinate (common, Y_AXIS)
765      - beam->relative_coordinate (common, Y_AXIS));
766
767   info.idealy_f_ += interstaff_f;
768   info.miny_f_ += interstaff_f;
769   info.maxy_f_ += interstaff_f ;
770
771   return info;
772 }
773
774 bool
775 Stem::has_interface (Grob*m)
776 {
777   return m && m->has_interface (ly_symbol2scm ("stem-interface"));
778 }
779
780 void
781 Stem::set_interface (Grob*me)
782 {    
783   me->set_interface (ly_symbol2scm ("stem-interface"));
784 }