]> git.donarmstrong.com Git - lilypond.git/blob - lily/stem.cc
patch::: 1.5.41.jcn2
[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.idealy_f_ = gh_scm2double (gh_car (scm_info)); 
670       si.maxy_f_ = gh_scm2double (gh_cadr (scm_info)); 
671       si.miny_f_ = 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   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 = ly_cdr (q))
711     a.push (gh_scm2double (ly_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 = ly_cdr (q))
719     a.push (gh_scm2double (ly_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_ = 1000;  // 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_ = - 1000 ; // 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   me->set_grob_property ("stem-info",
787                          scm_list_n (gh_double2scm (info.idealy_f_),
788                                      gh_double2scm (info.maxy_f_ ),
789                                      gh_double2scm (info.miny_f_),
790                                      SCM_UNDEFINED));
791   
792   return info;
793 }
794
795 bool
796 Stem::has_interface (Grob*m)
797 {
798   return m && m->has_interface (ly_symbol2scm ("stem-interface"));
799 }
800
801 void
802 Stem::set_interface (Grob*me)
803 {    
804   me->set_interface (ly_symbol2scm ("stem-interface"));
805 }