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