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