]> git.donarmstrong.com Git - lilypond.git/blob - lily/stem.cc
e69de92f549e16faf7cce22445d4bb20454883db
[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   Grob * beam = beam_l (me);
664
665   Direction beam_dir = Directional_element_interface::get (beam);
666   if (!beam_dir)
667     {
668       programming_error ("Beam dir not set.");
669       beam_dir = UP;
670     }
671     
672
673   Real staff_space = Staff_symbol_referencer::staff_space (me);
674   Real half_space = staff_space / 2;
675   int multiplicity = Beam::get_multiplicity (beam);
676
677
678   SCM space_proc = beam->get_grob_property ("space-function");
679   SCM space = gh_call1 (space_proc, gh_int2scm (multiplicity));
680   Real interbeam_f = gh_scm2double (space) * staff_space;
681
682   Real thick = gh_scm2double (beam->get_grob_property ("thickness"));
683   Stem_info info; 
684   info.idealy_f_ = chord_start_f (me);
685
686   // for simplicity, we calculate as if dir == UP
687   info.idealy_f_ *= beam_dir;
688   SCM grace_prop = me->get_grob_property ("grace");
689
690   bool grace_b = to_boolean (grace_prop);
691   
692   Array<Real> a;
693   SCM s;
694   
695   s = me->get_grob_property ("beamed-minimum-lengths");
696   a.clear ();
697   for (SCM q = s; q != SCM_EOL; q = ly_cdr (q))
698     a.push (gh_scm2double (ly_car (q)));
699
700
701   Real minimum_length = a[multiplicity <? (a.size () - 1)] * staff_space;
702   s = me->get_grob_property ("beamed-lengths");
703
704   a.clear ();
705   for (SCM q = s; q != SCM_EOL; q = ly_cdr (q))
706     a.push (gh_scm2double (ly_car (q)));
707
708   Real stem_length =  a[multiplicity <? (a.size () - 1)] * staff_space;
709
710   if (!beam_dir || (beam_dir == Directional_element_interface::get (me)))
711     /* normal beamed stem */
712     {
713       if (multiplicity)
714         {
715           info.idealy_f_ += thick + (multiplicity - 1) * interbeam_f;
716         }
717       info.miny_f_ = info.idealy_f_;
718       info.maxy_f_ = INT_MAX;
719
720       info.idealy_f_ += stem_length;
721       info.miny_f_ += minimum_length;
722
723       /*
724         lowest beam of (UP) beam must never be lower than second staffline
725
726         Hmm, reference (Wanske?)
727
728         Although this (additional) rule is probably correct,
729         I expect that highest beam (UP) should also never be lower
730         than middle staffline, just as normal stems.
731         
732       */
733       bool no_extend_b = to_boolean (me->get_grob_property ("no-stem-extend"));
734       if (!grace_b && !no_extend_b)
735         {
736           /* highest beam of (UP) beam must never be lower than middle
737              staffline
738              lowest beam of (UP) beam must never be lower than second staffline
739            */
740           info.miny_f_ =
741             info.miny_f_ >? 0
742             >? (- 2 * half_space - thick
743                 + (multiplicity > 0) * thick
744                 + interbeam_f * (multiplicity - 1));
745         }
746     }
747   else
748     /* knee */
749     {
750       info.idealy_f_ -= thick;
751       info.maxy_f_ = info.idealy_f_;
752       info.miny_f_ = -INT_MAX;
753
754       info.idealy_f_ -= stem_length;
755       info.maxy_f_ -= minimum_length;
756     }
757   
758   info.idealy_f_ = (info.maxy_f_ <? info.idealy_f_) >? info.miny_f_;
759
760   s = beam->get_grob_property ("shorten");
761   if (gh_number_p (s))
762     info.idealy_f_ -= gh_scm2double (s);
763
764  Grob *common = me->common_refpoint (beam, Y_AXIS);
765   Real interstaff_f = beam_dir *
766  (me->relative_coordinate (common, Y_AXIS)
767      - beam->relative_coordinate (common, Y_AXIS));
768
769   info.idealy_f_ += interstaff_f;
770   info.miny_f_ += interstaff_f;
771   info.maxy_f_ += interstaff_f ;
772
773   return info;
774 }
775
776 bool
777 Stem::has_interface (Grob*m)
778 {
779   return m && m->has_interface (ly_symbol2scm ("stem-interface"));
780 }
781
782 void
783 Stem::set_interface (Grob*me)
784 {    
785   me->set_interface (ly_symbol2scm ("stem-interface"));
786 }