]> git.donarmstrong.com Git - lilypond.git/blob - lily/stem.cc
patch::: 1.5.30.uu1
[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   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       /* Urrgh!  We have to detect wether this stem ends on a staff
494          line or between two staff lines.  But we can not call
495          stem_end_position(me) or get_default_stem_end_position(me),
496          since this encounters the flag and hence results in an
497          infinite recursion.  However, in pure mensural notation,
498          there are no multiple note heads attached to a single stem,
499          neither is there usually need for using the stem_shorten
500          property (except for 32th and 64th notes, but that is not a
501          problem since the stem length in this case is augmented by
502          an integral multiple of staff_space).  Hence, it should be
503          sufficient to just take the first note head, assume it's
504          the only one, look if it's on a staff line, and select the
505          flag's shape accordingly.  In the worst case, the shape
506          looks slightly misplaced, but that will usually be the
507          programmer's fault (e.g. when trying to attach multiple
508          note heads to a single stem in mensural notation).
509
510       */
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 = "";
519     }
520   char c = (get_direction (me) == UP) ? 'u' : 'd';
521   String index_str
522     = String ("flags-") + style + to_str (c) + staffline_offs + to_str (flag_i (me));
523   Molecule m
524     = Font_interface::get_default_font (me)->find_by_name (index_str);
525   if (!fstyle.empty_b ())
526     m.add_molecule (Font_interface::get_default_font (me)->find_by_name (String ("flags-") + to_str (c) + fstyle));
527   return m;
528 }
529
530 MAKE_SCHEME_CALLBACK (Stem,dim_callback,2);
531 SCM
532 Stem::dim_callback (SCM e, SCM ax)
533 {
534   Axis a = (Axis) gh_scm2int (ax);
535   assert (a == X_AXIS);
536   Grob *se = unsmob_grob (e);
537   Interval r (0, 0);
538   if (unsmob_grob (se->get_grob_property ("beam")) || abs (flag_i (se)) <= 2)
539     ;   // TODO!
540   else
541     {
542       r = flag (se).extent (X_AXIS);
543     }
544   return ly_interval2scm (r);
545 }
546  
547
548
549 MAKE_SCHEME_CALLBACK (Stem,brew_molecule,1);
550
551 SCM
552 Stem::brew_molecule (SCM smob) 
553 {
554   Grob*me = unsmob_grob (smob);
555   Molecule mol;
556   Direction d = get_direction (me);
557   
558   
559   Real y1 = Staff_symbol_referencer::position_f (first_head (me));
560   Real y2 = stem_end_position (me);
561   
562   Interval stem_y (y1 <? y2,y2 >? y1);
563
564
565   // dy?
566   Real dy = Staff_symbol_referencer::staff_space (me) * 0.5;
567
568   if (Grob *hed = support_head (me))
569     {
570       /*
571         must not take ledgers into account.
572        */
573       Interval head_height = Note_head::head_extent (hed,Y_AXIS);
574       Real y_attach = Note_head::stem_attachment_coordinate ( hed, Y_AXIS);
575
576       y_attach = head_height.linear_combination (y_attach);
577       stem_y[Direction (-d)] += d * y_attach/dy;
578     }
579   
580   if (!invisible_b (me))
581     {
582       Real stem_width = gh_scm2double (me->get_grob_property ("thickness"))
583         // URG
584         * me->paper_l ()->get_var ("stafflinethickness");
585       
586       Molecule ss =Lookup::filledbox (Box (Interval (-stem_width/2, stem_width/2),
587                                            Interval (stem_y[DOWN]*dy, stem_y[UP]*dy)));
588       mol.add_molecule (ss);
589     }
590
591   if (!beam_l (me) && abs (flag_i (me)) > 2)
592     {
593       Molecule fl = flag (me);
594       fl.translate_axis (stem_y[d]*dy, Y_AXIS);
595       mol.add_molecule (fl);
596     }
597
598   return mol.smobbed_copy ();
599 }
600
601 /*
602   move the stem to right of the notehead if it is up.
603  */
604 MAKE_SCHEME_CALLBACK (Stem,off_callback,2);
605 SCM
606 Stem::off_callback (SCM element_smob, SCM)
607 {
608   Grob *me = unsmob_grob (element_smob);
609   
610   Real r=0;
611   if (Grob * f = first_head (me))
612     {
613       Interval head_wid = Note_head::head_extent(f, X_AXIS);
614
615       Real attach =
616         Note_head::stem_attachment_coordinate(f, X_AXIS);
617
618       Direction d = get_direction (me);
619
620       Real real_attach = head_wid.linear_combination (d * attach);
621
622       r = real_attach;
623
624       /*
625         If not centered: correct for stem thickness.
626        */
627       if (attach)
628         {
629           Real rule_thick
630             = gh_scm2double (me->get_grob_property ("thickness"))
631             * me->paper_l ()->get_var ("stafflinethickness");
632
633           
634           r += - d * rule_thick * 0.5;
635         }
636     }
637   return gh_double2scm (r);
638 }
639
640
641
642 Grob*
643 Stem::beam_l (Grob*me)
644 {
645   SCM b=  me->get_grob_property ("beam");
646   return unsmob_grob (b);
647 }
648
649
650 // ugh still very long.
651 Stem_info
652 Stem::calc_stem_info (Grob*me) 
653 {
654   Grob * beam = beam_l (me);
655
656   Direction beam_dir = Directional_element_interface::get (beam);
657   if (!beam_dir)
658     {
659       programming_error ("Beam dir not set.");
660       beam_dir = UP;
661     }
662     
663
664   Real staff_space = Staff_symbol_referencer::staff_space (me);
665   Real half_space = staff_space / 2;
666   int multiplicity = Beam::get_multiplicity (beam);
667
668
669   SCM space_proc = beam->get_grob_property ("space-function");
670   SCM space = gh_call1 (space_proc, gh_int2scm (multiplicity));
671   Real interbeam_f = gh_scm2double (space) * staff_space;
672
673   Real thick = gh_scm2double (beam->get_grob_property ("thickness"));
674   Stem_info info; 
675   info.idealy_f_ = chord_start_f (me);
676
677   // for simplicity, we calculate as if dir == UP
678   info.idealy_f_ *= beam_dir;
679   SCM grace_prop = me->get_grob_property ("grace");
680
681   bool grace_b = to_boolean (grace_prop);
682   
683   Array<Real> a;
684   SCM s;
685   
686   s = me->get_grob_property ("beamed-minimum-lengths");
687   a.clear ();
688   for (SCM q = s; q != SCM_EOL; q = ly_cdr (q))
689     a.push (gh_scm2double (ly_car (q)));
690
691
692   Real minimum_length = a[multiplicity <? (a.size () - 1)] * staff_space;
693   s = me->get_grob_property ("beamed-lengths");
694
695   a.clear ();
696   for (SCM q = s; q != SCM_EOL; q = ly_cdr (q))
697     a.push (gh_scm2double (ly_car (q)));
698
699   Real stem_length =  a[multiplicity <? (a.size () - 1)] * staff_space;
700
701   if (!beam_dir || (beam_dir == Directional_element_interface::get (me)))
702     /* normal beamed stem */
703     {
704       if (multiplicity)
705         {
706           info.idealy_f_ += thick + (multiplicity - 1) * interbeam_f;
707         }
708       info.miny_f_ = info.idealy_f_;
709       info.maxy_f_ = INT_MAX;
710
711       info.idealy_f_ += stem_length;
712       info.miny_f_ += minimum_length;
713
714       /*
715         lowest beam of (UP) beam must never be lower than second staffline
716
717         Hmm, reference (Wanske?)
718
719         Although this (additional) rule is probably correct,
720         I expect that highest beam (UP) should also never be lower
721         than middle staffline, just as normal stems.
722         
723       */
724       bool no_extend_b = to_boolean (me->get_grob_property ("no-stem-extend"));
725       if (!grace_b && !no_extend_b)
726         {
727           /* highest beam of (UP) beam must never be lower than middle
728              staffline
729              lowest beam of (UP) beam must never be lower than second staffline
730            */
731           info.miny_f_ =
732             info.miny_f_ >? 0
733             >? (- 2 * half_space - thick
734                 + (multiplicity > 0) * thick
735                 + interbeam_f * (multiplicity - 1));
736         }
737     }
738   else
739     /* knee */
740     {
741       info.idealy_f_ -= thick;
742       info.maxy_f_ = info.idealy_f_;
743       info.miny_f_ = -INT_MAX;
744
745       info.idealy_f_ -= stem_length;
746       info.maxy_f_ -= minimum_length;
747     }
748   
749   info.idealy_f_ = (info.maxy_f_ <? info.idealy_f_) >? info.miny_f_;
750
751   s = beam->get_grob_property ("shorten");
752   if (gh_number_p (s))
753     info.idealy_f_ -= gh_scm2double (s);
754
755  Grob *common = me->common_refpoint (beam, Y_AXIS);
756   Real interstaff_f = beam_dir *
757  (me->relative_coordinate (common, Y_AXIS)
758      - beam->relative_coordinate (common, Y_AXIS));
759
760   info.idealy_f_ += interstaff_f;
761   info.miny_f_ += interstaff_f;
762   info.maxy_f_ += interstaff_f ;
763
764   return info;
765 }
766
767 bool
768 Stem::has_interface (Grob*m)
769 {
770   return m && m->has_interface (ly_symbol2scm ("stem-interface"));
771 }
772
773 void
774 Stem::set_interface (Grob*me)
775 {    
776   me->set_interface (ly_symbol2scm ("stem-interface"));
777 }