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