]> git.donarmstrong.com Git - lilypond.git/blob - lily/stem.cc
release: 1.3.12
[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, 1997--1999 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 "dimension-cache.hh"
13 #include "stem.hh"
14 #include "debug.hh"
15 #include "paper-def.hh"
16 #include "note-head.hh"
17 #include "lookup.hh"
18 #include "molecule.hh"
19 #include "paper-column.hh"
20 #include "misc.hh"
21 #include "beam.hh"
22 #include "rest.hh"
23 #include "group-interface.hh"
24 #include "cross-staff.hh"
25 #include "staff-symbol-referencer.hh"
26
27
28 void
29 Stem::set_beaming (int i,  Direction d )
30 {
31   SCM pair = get_elt_property ("beaming");
32   
33   if (!gh_pair_p (pair))
34     {
35       pair = gh_cons (gh_int2scm (0),gh_int2scm (0));
36       set_elt_property ("beaming", pair);
37     }
38   index_set_cell (pair, d, gh_int2scm (i));
39 }
40
41 int
42 Stem::beam_count (Direction d) const
43 {
44   SCM p=get_elt_property ("beaming");
45   if (gh_pair_p (p))
46     return gh_scm2int (index_cell (p,d));
47   else
48     return 0;
49 }
50
51 Interval_t<int>
52 Stem::head_positions () const
53 {
54   /* 
55     Mysterious FreeBSD fix by John Galbraith.  Somehow, the empty intervals 
56     trigger FP exceptions on FreeBSD.  Fix: do not return infinity 
57
58    */
59   if (!first_head ())
60     {
61       return Interval_t<int> (100,-100);        
62     }
63
64   Link_array<Note_head> head_l_arr =
65     Group_interface__extract_elements (this, (Note_head*)0, "heads");
66   
67   Interval_t<int> r;
68   for (int i =0; i < head_l_arr.size (); i++)
69     {
70       Staff_symbol_referencer_interface si (head_l_arr[i]);
71       int p = (int)si.position_f ();
72       r[BIGGER] = r[BIGGER] >? p;
73       r[SMALLER] = r[SMALLER] <? p;
74     }
75   return r;
76 }
77
78 Real
79 Stem::stem_begin_f () const
80 {
81   return yextent_[Direction(-get_direction ())];
82 }
83
84 Real
85 Stem::chord_start_f () const
86 {
87   return head_positions()[get_direction ()]
88     * Staff_symbol_referencer_interface (this).staff_line_leading_f ()/2.0;
89 }
90
91 Real
92 Stem::stem_end_f () const
93 {
94   return yextent_[get_direction ()];
95 }
96
97 void
98 Stem::set_stemend (Real se)
99 {
100   // todo: margins
101   if (get_direction () && get_direction () * head_positions()[get_direction ()] >= se*get_direction ())
102     warning (_ ("Weird stem size; check for narrow beams"));
103
104   
105   yextent_[get_direction ()]  =  se;
106   yextent_[Direction(-get_direction ())] = head_positions()[-get_direction ()];
107 }
108
109 int
110 Stem::type_i () const
111 {
112   return first_head () ?  first_head ()->balltype_i () : 2;
113 }
114
115
116
117 /*
118   Note head that determines hshift for upstems
119  */ 
120 Score_element*
121 Stem::support_head ()const
122 {
123   SCM h = get_elt_property ("support-head");
124   Score_element * nh = unsmob_element (h);
125   if (nh)
126     return nh;
127   else
128     return first_head ();
129 }
130
131
132 /*
133   The note head which forms one end of the stem.  
134  */
135 Note_head*
136 Stem::first_head () const
137 {
138   const int inf = 1000000;
139   int pos = -inf;               
140   Direction dir = get_direction ();
141
142
143   Note_head *nh =0;
144   for (SCM s = get_elt_property ("heads"); gh_pair_p (s); s = gh_cdr (s))
145     {
146       Note_head * n = dynamic_cast<Note_head*> (unsmob_element (gh_car (s)));
147       Staff_symbol_referencer_interface si (n);
148       int p = dir * int(si.position_f ());
149       if (p > pos)
150         {
151           nh = n;
152         }
153     }
154
155   return nh;
156 }
157
158 void
159 Stem::add_head (Rhythmic_head *n)
160 {
161   n->set_elt_property ("stem", this->self_scm_);
162   n->add_dependency (this);     // ?
163   
164
165   Group_interface gi (this);
166   if (Note_head *nh = dynamic_cast<Note_head *> (n))
167     gi.name_ = "heads";
168   else
169     gi.name_ = "rests";
170
171   gi.add_element (n);
172 }
173
174 Stem::Stem ()
175 {
176   set_elt_property ("heads", SCM_EOL);
177   set_elt_property ("rests", SCM_EOL);
178 }
179
180 bool
181 Stem::invisible_b () const
182 {
183   return !(first_head () && first_head()->balltype_i () >= 1);
184 }
185
186 int
187 Stem::get_center_distance (Direction d) const
188 {
189   int staff_center = 0;
190   int distance = d*(head_positions()[d] - staff_center);
191   return distance >? 0;
192 }
193
194 Direction
195 Stem::get_default_dir () const
196 {
197   int du = get_center_distance (UP);
198   int dd = get_center_distance (DOWN);
199
200   if (sign (dd - du))
201     return Direction (sign (dd -du));
202
203   return Direction (int(paper_l ()->get_var ("stem_default_neutral_direction")));
204 }
205
206 void
207 Stem::set_default_stemlen ()
208 {
209   Real length_f = 0.;
210   SCM scm_len = get_elt_property("length");
211   if (scm_len != SCM_UNDEFINED)
212     {
213       length_f = gh_scm2double (scm_len);
214     }
215   else
216     length_f = paper_l ()->get_var ("stem_length0");
217
218   bool grace_b = get_elt_property ("grace") != SCM_UNDEFINED;
219   String type_str = grace_b ? "grace_" : "";
220
221   Real shorten_f = paper_l ()->get_var (type_str + "forced_stem_shorten0");
222
223   /* URGURGURG
224      'set-default-stemlen' sets direction too
225    */ 
226   if (!get_direction ())
227     set_direction (get_default_dir ());
228
229   /* 
230     stems in unnatural (forced) direction should be shortened, 
231     according to [Roush & Gourlay]
232    */
233   if (((int)chord_start_f ())
234       && (get_direction () != get_default_dir ()))
235     length_f -= shorten_f;
236  
237  if (flag_i () >= 5)
238     length_f += 2.0;
239   if (flag_i () >= 6)
240     length_f += 1.0;
241   
242   set_stemend ((get_direction () > 0) ? head_positions()[BIGGER] + length_f:
243                head_positions()[SMALLER] - length_f);
244
245   bool no_extend_b = get_elt_property ("no-stem-extend") != SCM_UNDEFINED;
246   if (!grace_b && !no_extend_b && (get_direction () * stem_end_f () < 0))
247     set_stemend (0);
248 }
249
250 int
251 Stem::flag_i () const
252 {
253   SCM s = get_elt_property ("duration-log");
254   return  (gh_number_p (s)) ? gh_scm2int (s) : 2;
255 }
256
257 //xxx
258 void
259 Stem::set_default_extents ()
260 {
261   if (yextent_.empty_b ())
262     set_default_stemlen ();
263 }
264
265 void
266 Stem::set_noteheads ()
267 {
268   if (!first_head ())
269     return;
270   
271   Link_array<Score_element> heads =
272     Group_interface__extract_elements (this, (Score_element*)0, "heads");
273
274   heads.sort (compare_position);
275   Direction dir =get_direction ();
276   
277   if (dir < 0)
278     heads.reverse ();
279
280
281   Real w = support_head ()->extent (X_AXIS)[dir];
282   for (int i=0; i < heads.size (); i++)
283     {
284       heads[i]->translate_axis (w - heads[i]->extent (X_AXIS)[dir], X_AXIS);
285     }
286   
287   bool parity= true;
288   int lastpos = int (Staff_symbol_referencer_interface (heads[0]).position_f ());
289   for (int i=1; i < heads.size (); i ++)
290     {
291       Real p = Staff_symbol_referencer_interface (heads[i]).position_f ();
292       int dy =abs (lastpos- (int)p);
293
294       if (dy <= 1)
295         {
296           if (parity)
297             {
298               Real l  = heads[i]->extent (X_AXIS).length ();
299               heads[i]->translate_axis (l * get_direction (), X_AXIS);
300             }
301           parity = !parity;
302         }
303       else
304         parity = true;
305       
306       lastpos = int (p);
307     }
308 }
309
310 void
311 Stem::do_pre_processing ()
312 {
313   if (yextent_.empty_b ())
314     set_default_extents ();
315   set_noteheads ();
316
317   if (invisible_b ())
318     {
319       set_elt_property ("transparent", SCM_BOOL_T);
320       set_empty (Y_AXIS);      
321       set_empty (X_AXIS);      
322     }
323
324   set_spacing_hints ();
325 }
326
327
328
329 /**
330    set stem directions for hinting the optical spacing correction.
331
332    Modifies DIR_LIST property of the Stem's Score_column
333
334    TODO: more advanced: supply height of noteheads as well, for more advanced spacing possibilities
335  */
336
337 void
338 Stem::set_spacing_hints () 
339 {
340   if (!invisible_b ())
341     {
342       SCM scmdir  = gh_int2scm (get_direction ());
343       SCM dirlist = column_l ()->get_elt_property ("dir-list");
344       if (dirlist == SCM_UNDEFINED)
345         dirlist = SCM_EOL;
346
347       if (scm_sloppy_memq (scmdir, dirlist) == SCM_EOL)
348         {
349           dirlist = gh_cons (scmdir, dirlist);
350           column_l ()->set_elt_property ("dir-list", dirlist);
351         }
352     }
353 }
354
355 Molecule
356 Stem::flag () const
357 {
358   String style;
359   SCM st = get_elt_property ("style");
360   if ( st != SCM_UNDEFINED)
361     {
362       style = ly_scm2string (st);
363     }
364
365   char c = (get_direction () == UP) ? 'u' : 'd';
366   Molecule m = lookup_l ()->afm_find (String ("flags-") + to_str (c) + 
367                                       to_str (flag_i ()));
368   if (!style.empty_b ())
369     m.add_molecule(lookup_l ()->afm_find (String ("flags-") + to_str (c) + style));
370   return m;
371 }
372
373 Interval
374 Stem::dim_callback (Dimension_cache const* c) 
375 {
376   Stem * s = dynamic_cast<Stem*> (c->element_l ());
377   
378   Interval r (0, 0);
379   if (s->get_elt_property ("beam") != SCM_UNDEFINED || abs (s->flag_i ()) <= 2)
380     ;   // TODO!
381   else
382     {
383       r = s->flag ().dim_.x ();
384       r += s->note_delta_f ();
385     }
386   return r;
387 }
388
389
390 const Real ANGLE = 20* (2.0*M_PI/360.0); // ugh!
391
392 Molecule*
393 Stem::do_brew_molecule_p () const
394 {
395   Molecule *mol_p =new Molecule;
396   Interval stem_y = yextent_;
397   Real dy = staff_symbol_referencer_interface (this)
398     .staff_line_leading_f ()/2.0;
399
400   Real head_wid = 0;
401   if (support_head ())
402     head_wid = support_head ()->extent (X_AXIS).length ();
403   stem_y[Direction(-get_direction ())] += get_direction () * head_wid * tan(ANGLE)/(2*dy);
404   
405   if (!invisible_b ())
406     {
407       Real stem_width = paper_l ()->get_var ("stemthickness");
408       Molecule ss =lookup_l ()->filledbox (Box (Interval (-stem_width/2, stem_width/2),
409                                                  Interval (stem_y[DOWN]*dy, stem_y[UP]*dy)));
410       mol_p->add_molecule (ss);
411     }
412
413   if (!beam_l () && abs (flag_i ()) > 2)
414     {
415       Molecule fl = flag ();
416       fl.translate_axis(stem_y[get_direction ()]*dy, Y_AXIS);
417       mol_p->add_molecule (fl);
418     }
419
420   if (first_head ())
421     {
422       mol_p->translate_axis (note_delta_f (), X_AXIS);
423     }
424   return mol_p;
425 }
426
427 Real
428 Stem::note_delta_f () const
429 {
430   Real r=0;
431   if (first_head ())
432     {
433       Interval head_wid(0,  first_head()->extent (X_AXIS).length ());
434          Real rule_thick = paper_l ()->get_var ("stemthickness");
435
436       Interval stem_wid(-rule_thick/2, rule_thick/2);
437       if (get_direction () == CENTER)
438         r = head_wid.center ();
439       else
440         r = head_wid[get_direction ()] - stem_wid[get_direction ()];
441     }
442   return r;
443 }
444
445 Real
446 Stem::hpos_f () const
447 {
448   return note_delta_f () + Item::hpos_f ();
449 }
450
451
452 Beam*
453 Stem::beam_l ()const
454 {
455   SCM b=  get_elt_property ("beam");
456   return dynamic_cast<Beam*> (unsmob_element (b));
457 }
458
459
460 // ugh still very long.
461 Stem_info
462 Stem::calc_stem_info () const
463 {
464   assert (beam_l ());
465
466   Direction beam_dir = beam_l ()->get_direction ();
467   if (!beam_dir)
468     {
469       programming_error ("Beam dir not set.");
470       beam_dir = UP;
471     }
472     
473   Stem_info info; 
474   Real internote_f
475      = staff_symbol_referencer_interface (this).staff_line_leading_f ()/2;
476    Real interbeam_f = paper_l ()->interbeam_f (beam_l ()->get_multiplicity ());
477   Real beam_f = gh_scm2double (beam_l ()->get_elt_property ("beam-thickness"));
478          
479   info.idealy_f_ = chord_start_f ();
480
481   // for simplicity, we calculate as if dir == UP
482   info.idealy_f_ *= beam_dir;
483
484   bool grace_b = get_elt_property ("grace") != SCM_UNDEFINED;
485   bool no_extend_b = get_elt_property ("no-stem-extend") != SCM_UNDEFINED;
486
487   int stem_max = (int)rint(paper_l ()->get_var ("stem_max"));
488   String type_str = grace_b ? "grace_" : "";
489   Real min_stem_f = paper_l ()->get_var (type_str + "minimum_stem_length"
490     + to_str (beam_l ()->get_multiplicity () <? stem_max)) * internote_f;
491   Real stem_f = paper_l ()->get_var (type_str + "stem_length"
492     + to_str (beam_l ()->get_multiplicity () <? stem_max)) * internote_f;
493
494   if (!beam_dir || (beam_dir == get_direction ()))
495     /* normal beamed stem */
496     {
497       if (beam_l ()->get_multiplicity ())
498         {
499           info.idealy_f_ += beam_f;
500           info.idealy_f_ += (beam_l ()->get_multiplicity () - 1) * interbeam_f;
501         }
502       info.miny_f_ = info.idealy_f_;
503       info.maxy_f_ = INT_MAX;
504
505       info.idealy_f_ += stem_f;
506       info.miny_f_ += min_stem_f;
507
508       /*
509         lowest beam of (UP) beam must never be lower than second staffline
510
511         Hmm, reference (Wanske?)
512
513         Although this (additional) rule is probably correct,
514         I expect that highest beam (UP) should also never be lower
515         than middle staffline, just as normal stems.
516         
517       */
518       if (!grace_b && !no_extend_b)
519         {
520           //highest beam of (UP) beam must never be lower than middle staffline
521           info.miny_f_ = info.miny_f_ >? 0;
522           //lowest beam of (UP) beam must never be lower than second staffline
523           info.miny_f_ = info.miny_f_ >? (- 2 * internote_f - beam_f
524                                 + (beam_l ()->get_multiplicity () > 0) * beam_f + interbeam_f * (beam_l ()->get_multiplicity () - 1));
525         }
526     }
527   else
528     /* knee */
529     {
530       info.idealy_f_ -= beam_f;
531       info.maxy_f_ = info.idealy_f_;
532       info.miny_f_ = -INT_MAX;
533
534       info.idealy_f_ -= stem_f;
535       info.maxy_f_ -= min_stem_f;
536     }
537
538   info.idealy_f_ = info.maxy_f_ <? info.idealy_f_;
539   info.idealy_f_ = info.miny_f_ >? info.idealy_f_;
540
541   Real interstaff_f = calc_interstaff_dist (this, beam_l ());
542
543   SCM s = beam_l ()->get_elt_property ("shorten");
544   if (s != SCM_UNDEFINED)
545     info.idealy_f_ -= gh_double2scm (s);
546
547   info.idealy_f_ += interstaff_f * beam_dir;
548   info.miny_f_ += interstaff_f * beam_dir;
549   info.maxy_f_ += interstaff_f * beam_dir;
550
551   return info;
552 }
553