]> git.donarmstrong.com Git - lilypond.git/blob - lily/stem.cc
release: 1.5.9
[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 #include <math.h>               // m_pi
12
13 #include "lookup.hh"
14 #include "directional-element-interface.hh"
15 #include "note-head.hh"
16 #include "stem.hh"
17 #include "debug.hh"
18 #include "paper-def.hh"
19 #include "rhythmic-head.hh"
20 #include "font-interface.hh"
21 #include "molecule.hh"
22 #include "paper-column.hh"
23 #include "misc.hh"
24 #include "beam.hh"
25 #include "rest.hh"
26 #include "group-interface.hh"
27 #include "staff-symbol-referencer.hh"
28 #include "spanner.hh"
29 #include "side-position-interface.hh"
30
31 void
32 Stem::set_beaming (Grob*me ,int i,  Direction d)
33 {
34   SCM pair = me->get_grob_property ("beaming");
35   
36   if (!gh_pair_p (pair))
37     {
38       pair = gh_cons (gh_int2scm (0),gh_int2scm (0));
39       me->      set_grob_property ("beaming", pair);
40     }
41   index_set_cell (pair, d, gh_int2scm (i));
42 }
43
44 int
45 Stem::beam_count (Grob*me,Direction d)
46 {
47   SCM p=me->get_grob_property ("beaming");
48   if (gh_pair_p (p))
49     return gh_scm2int (index_cell (p,d));
50   else
51     return 0;
52 }
53
54 Interval
55 Stem::head_positions (Grob*me) 
56 {
57   if (!heads_i (me))
58     {
59       Interval iv;
60       return iv;
61     }
62
63   Drul_array<Grob*> e (extremal_heads (me));
64
65   return Interval (Staff_symbol_referencer::position_f (e[DOWN]),
66                    Staff_symbol_referencer::position_f (e[UP]));
67 }
68
69
70 Real
71 Stem::chord_start_f (Grob*me) 
72 {
73   return head_positions (me)[get_direction (me)]
74     * Staff_symbol_referencer::staff_space (me)/2.0;
75 }
76
77 Real
78 Stem::stem_end_position (Grob*me) 
79 {
80   SCM p =me->get_grob_property ("stem-end-position");
81   Real pos;
82   if (!gh_number_p (p))
83     {
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 (gh_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 = gh_cdr (s))
180     {
181       Grob * n = unsmob_grob (gh_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 = gh_cdr (s))
210     {
211       Grob * n = unsmob_grob (gh_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_element (me, "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 = gh_cdr (q))
281         a.push (gh_scm2double (gh_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 = gh_cdr (q))
291     a.push (gh_scm2double (gh_car (q)));
292
293
294   // stem uses half-spaces
295
296   // fixme: use gh_list_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->parent_l (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_elements (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 = hed->extent (hed, X_AXIS)[dir];
393   for (int i=0; i < heads.size (); i++)
394     {
395       heads[i]->translate_axis (w - heads[i]->extent (heads[i], X_AXIS)[dir], X_AXIS);
396     }
397   
398   bool parity= true;            // todo: make me settable.
399   int lastpos = int (Staff_symbol_referencer::position_f (heads[0]));
400   for (int i=1; i < heads.size (); i ++)
401     {
402       Real p = Staff_symbol_referencer::position_f (heads[i]);
403       int dy =abs (lastpos- (int)p);
404
405       if (dy <= 1)
406         {
407           if (parity)
408             {
409               Real l = heads[i]->extent (heads[i], X_AXIS).length ();
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   set_spacing_hints (me);
436   return SCM_UNSPECIFIED;
437 }
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 /**
462    set stem directions for hinting the optical spacing correction.
463
464    Modifies DIR_LIST property of the Stem's Paper_column
465
466    TODO: more advanced: supply height of noteheads as well, for more advanced spacing possibilities
467  */
468 void
469 Stem::set_spacing_hints (Grob*me) 
470 {
471   if (!invisible_b (me))
472     {
473       SCM scmdir  = gh_int2scm (get_direction (me));
474
475       Item* item = dynamic_cast<Item*> (me);
476       Item * col =  item->column_l ();
477       SCM dirlist =col->get_grob_property ("dir-list");
478       if (scm_c_memq (scmdir, dirlist) == SCM_BOOL_F)
479         {
480           dirlist = gh_cons (scmdir, dirlist);
481           col->set_grob_property ("dir-list", dirlist);
482         }
483     }
484 }
485
486 Molecule
487 Stem::flag (Grob*me)
488 {
489   /* TODO: rename flag-style into something more appropriate,
490    e.g. "stroke-style", maybe with values "" (i.e. no stroke),
491    "single" and "double".  Needs more discussion.
492   */
493   String style, fstyle, staffline_offs;
494   SCM fst = me->get_grob_property ("flag-style");
495   if (gh_string_p (fst))
496     {
497       fstyle = ly_scm2string (fst);
498     }
499
500   SCM st = me->get_grob_property ("style");
501   if (gh_symbol_p (st))
502     {
503       style = (ly_scm2string (scm_symbol_to_string (st)));
504     }
505   else
506     {
507       style = "";
508     }
509   if (String::compare_i (style, "mensural") == 0)
510     /* Mensural notation: For notes on staff lines, use different
511        flags than for notes between staff lines.  The idea is that
512        flags are always vertically aligned with the staff lines,
513        regardless if the note head is on a staff line or between two
514        staff lines.  In other words, the inner end of a flag always
515        touches a staff line.
516     */
517     {
518       /* Urrgh!  We have to detect wether this stem ends on a staff
519          line or between two staff lines.  But we can not call
520          stem_end_position(me) or get_default_stem_end_position(me),
521          since this encounters the flag and hence results in an
522          infinite recursion.  However, in pure mensural notation,
523          there are no multiple note heads attached to a single stem,
524          neither is there usually need for using the stem_shorten
525          property (except for 32th and 64th notes, but that is not a
526          problem since the stem length in this case is augmented by
527          an integral multiple of staff_space).  Hence, it should be
528          sufficient to just take the first note head, assume it's
529          the only one, look if it's on a staff line, and select the
530          flag's shape accordingly.  In the worst case, the shape
531          looks slightly misplaced, but that will usually be the
532          programmer's fault (e.g. when trying to attach multiple
533          note heads to a single stem in mensural notation).
534
535       */
536         Grob *first = first_head(me);
537         int sz = Staff_symbol_referencer::line_count (me)-1;
538         int p = (int)rint (Staff_symbol_referencer::position_f (first));
539         staffline_offs = (((p ^ sz) & 0x1) == 0) ? "1" : "0";
540     }
541   else
542     {
543         staffline_offs = "";
544     }
545   char c = (get_direction (me) == UP) ? 'u' : 'd';
546   String index_str
547     = String ("flags-") + style + to_str (c) + staffline_offs + to_str (flag_i (me));
548   Molecule m
549     = Font_interface::get_default_font (me)->find_by_name (index_str);
550   if (!fstyle.empty_b ())
551     m.add_molecule (Font_interface::get_default_font (me)->find_by_name (String ("flags-") + to_str (c) + fstyle));
552   return m;
553 }
554
555 MAKE_SCHEME_CALLBACK (Stem,dim_callback,2);
556 SCM
557 Stem::dim_callback (SCM e, SCM ax)
558 {
559   Axis a = (Axis) gh_scm2int (ax);
560   assert (a == X_AXIS);
561   Grob *se = unsmob_grob (e);
562   Interval r (0, 0);
563   if (unsmob_grob (se->get_grob_property ("beam")) || abs (flag_i (se)) <= 2)
564     ;   // TODO!
565   else
566     {
567       r = flag (se).extent (X_AXIS);
568     }
569   return ly_interval2scm (r);
570 }
571  
572
573
574 MAKE_SCHEME_CALLBACK (Stem,brew_molecule,1);
575
576 SCM
577 Stem::brew_molecule (SCM smob) 
578 {
579   Grob*me = unsmob_grob (smob);
580   Molecule mol;
581   Direction d = get_direction (me);
582   
583   
584   Real y1 = Staff_symbol_referencer::position_f (first_head (me));
585   Real y2 = stem_end_position (me);
586   
587   Interval stem_y (y1,y2);
588   stem_y.unite (Interval (y2,y1));
589
590   // dy?
591   Real dy = Staff_symbol_referencer::staff_space (me)/2.0;
592     
593   if (Grob *hed = support_head (me))
594     {
595       Interval head_height = hed->extent (hed,Y_AXIS);
596       Real y_attach = Note_head::stem_attachment_coordinate ( hed, Y_AXIS);
597
598       y_attach = head_height.linear_combination (y_attach);
599       stem_y[Direction (-d)] += d * 2*y_attach;
600     }
601   
602   if (!invisible_b (me))
603     {
604       Real stem_width = gh_scm2double (me->get_grob_property ("thickness"))
605         // URG
606         * me->paper_l ()->get_var ("stafflinethickness");
607       
608       Molecule ss =Lookup::filledbox (Box (Interval (-stem_width/2, stem_width/2),
609                                            Interval (stem_y[DOWN]*dy, stem_y[UP]*dy)));
610       mol.add_molecule (ss);
611     }
612
613   if (!beam_l (me) && abs (flag_i (me)) > 2)
614     {
615       Molecule fl = flag (me);
616       fl.translate_axis (stem_y[d]*dy, Y_AXIS);
617       mol.add_molecule (fl);
618     }
619
620   return mol.smobbed_copy ();
621 }
622
623 /*
624   move the stem to right of the notehead if it is up.
625  */
626 MAKE_SCHEME_CALLBACK (Stem,off_callback,2);
627 SCM
628 Stem::off_callback (SCM element_smob, SCM)
629 {
630   Grob *me = unsmob_grob (element_smob);
631   
632   Real r=0;
633   if (Grob * f = first_head (me))
634     {
635       Interval head_wid = f->extent (f,X_AXIS);
636
637       Real attach =
638         Note_head::stem_attachment_coordinate(f, X_AXIS);
639
640       Direction d = get_direction (me);
641
642       Real real_attach = head_wid.linear_combination (d * attach);
643
644       r = real_attach;
645
646       /*
647         If not centered: correct for stem thickness.
648        */
649       if (attach)
650         {
651           Real rule_thick
652             = gh_scm2double (me->get_grob_property ("thickness"))
653             * me->paper_l ()->get_var ("stafflinethickness");
654
655           
656           r += - d * rule_thick * 0.5;
657         }
658     }
659   return gh_double2scm (r);
660 }
661
662
663
664 Grob*
665 Stem::beam_l (Grob*me)
666 {
667   SCM b=  me->get_grob_property ("beam");
668   return unsmob_grob (b);
669 }
670
671
672 // ugh still very long.
673 Stem_info
674 Stem::calc_stem_info (Grob*me) 
675 {
676   Grob * beam = beam_l (me);
677
678   Direction beam_dir = Directional_element_interface::get (beam);
679   if (!beam_dir)
680     {
681       programming_error ("Beam dir not set.");
682       beam_dir = UP;
683     }
684     
685
686   Real staff_space = Staff_symbol_referencer::staff_space (me);
687   Real half_space = staff_space / 2;
688   int multiplicity = Beam::get_multiplicity (beam);
689
690
691   SCM space_proc = beam->get_grob_property ("space-function");
692   SCM space = gh_call1 (space_proc, gh_int2scm (multiplicity));
693   Real interbeam_f = gh_scm2double (space) * staff_space;
694
695   Real thick = gh_scm2double (beam->get_grob_property ("thickness"));
696   Stem_info info; 
697   info.idealy_f_ = chord_start_f (me);
698
699   // for simplicity, we calculate as if dir == UP
700   info.idealy_f_ *= beam_dir;
701   SCM grace_prop = me->get_grob_property ("grace");
702
703   bool grace_b = to_boolean (grace_prop);
704   
705   Array<Real> a;
706   SCM s;
707   
708   s = me->get_grob_property ("beamed-minimum-lengths");
709   a.clear ();
710   for (SCM q = s; q != SCM_EOL; q = gh_cdr (q))
711     a.push (gh_scm2double (gh_car (q)));
712
713
714   Real minimum_length = a[multiplicity <? (a.size () - 1)] * staff_space;
715   s = me->get_grob_property ("beamed-lengths");
716
717   a.clear ();
718   for (SCM q = s; q != SCM_EOL; q = gh_cdr (q))
719     a.push (gh_scm2double (gh_car (q)));
720
721   Real stem_length =  a[multiplicity <? (a.size () - 1)] * staff_space;
722
723   if (!beam_dir || (beam_dir == Directional_element_interface::get (me)))
724     /* normal beamed stem */
725     {
726       if (multiplicity)
727         {
728           info.idealy_f_ += thick + (multiplicity - 1) * interbeam_f;
729         }
730       info.miny_f_ = info.idealy_f_;
731       info.maxy_f_ = INT_MAX;
732
733       info.idealy_f_ += stem_length;
734       info.miny_f_ += minimum_length;
735
736       /*
737         lowest beam of (UP) beam must never be lower than second staffline
738
739         Hmm, reference (Wanske?)
740
741         Although this (additional) rule is probably correct,
742         I expect that highest beam (UP) should also never be lower
743         than middle staffline, just as normal stems.
744         
745       */
746       bool no_extend_b = to_boolean (me->get_grob_property ("no-stem-extend"));
747       if (!grace_b && !no_extend_b)
748         {
749           /* highest beam of (UP) beam must never be lower than middle
750              staffline
751              lowest beam of (UP) beam must never be lower than second staffline
752            */
753           info.miny_f_ =
754             info.miny_f_ >? 0
755             >? (- 2 * half_space - thick
756                 + (multiplicity > 0) * thick
757                 + interbeam_f * (multiplicity - 1));
758         }
759     }
760   else
761     /* knee */
762     {
763       info.idealy_f_ -= thick;
764       info.maxy_f_ = info.idealy_f_;
765       info.miny_f_ = -INT_MAX;
766
767       info.idealy_f_ -= stem_length;
768       info.maxy_f_ -= minimum_length;
769     }
770   
771   info.idealy_f_ = (info.maxy_f_ <? info.idealy_f_) >? info.miny_f_;
772
773   s = beam->get_grob_property ("shorten");
774   if (gh_number_p (s))
775     info.idealy_f_ -= gh_scm2double (s);
776
777  Grob *common = me->common_refpoint (beam, Y_AXIS);
778   Real interstaff_f = beam_dir *
779  (me->relative_coordinate (common, Y_AXIS)
780      - beam->relative_coordinate (common, Y_AXIS));
781
782   info.idealy_f_ += interstaff_f;
783   info.miny_f_ += interstaff_f;
784   info.maxy_f_ += interstaff_f ;
785
786   return info;
787 }
788
789 bool
790 Stem::has_interface (Grob*m)
791 {
792   return m && m->has_interface (ly_symbol2scm ("stem-interface"));
793 }
794
795 void
796 Stem::set_interface (Grob*me)
797 {    
798   me->set_interface (ly_symbol2scm ("stem-interface"));
799 }