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