2 stem.cc -- implement Stem
4 source file of the GNU LilyPond music typesetter
6 (c) 1996, 1997--1999 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 Jan Nieuwenhuizen <janneke@gnu.org>
9 TODO: This is way too hairy
11 #include "dimension-cache.hh"
14 #include "paper-def.hh"
15 #include "note-head.hh"
17 #include "molecule.hh"
18 #include "paper-column.hh"
22 #include "group-interface.hh"
23 #include "cross-staff.hh"
24 #include "staff-symbol-referencer.hh"
28 beams_i_drul_[LEFT] = beams_i_drul_[RIGHT] = -1;
32 Stem::head_positions () const
35 Mysterious FreeBSD fix by John Galbraith. Somehow, the empty intervals
36 trigger FP exceptions on FreeBSD. Fix: do not return infinity
41 return Interval_t<int> (100,-100);
44 Link_array<Note_head> head_l_arr =
45 Group_interface__extract_elements (this, (Note_head*)0, "heads");
48 for (int i =0; i < head_l_arr.size (); i++)
50 Staff_symbol_referencer_interface si (head_l_arr[i]);
51 int p = (int)si.position_f ();
52 r[BIGGER] = r[BIGGER] >? p;
53 r[SMALLER] = r[SMALLER] <? p;
60 Stem::stem_length_f () const
62 return yextent_.length();
66 Stem::stem_begin_f () const
68 return yextent_[Direction(-get_direction ())];
72 Stem::chord_start_f () const
74 return head_positions()[get_direction ()]
75 * Staff_symbol_referencer_interface (this).staff_line_leading_f ()/2.0;
79 Stem::stem_end_f () const
81 return yextent_[get_direction ()];
85 Stem::set_stemend (Real se)
88 if (get_direction () && get_direction () * head_positions()[get_direction ()] >= se*get_direction ())
89 warning (_ ("Weird stem size; check for narrow beams"));
92 yextent_[get_direction ()] = se;
93 yextent_[Direction(-get_direction ())] = head_positions()[-get_direction ()];
99 return first_head ()->balltype_i ();
103 Stem::first_head () const
105 SCM h =get_elt_property ("heads");
109 Score_element * sc = unsmob_element (gh_car (h));
111 return dynamic_cast<Note_head*> (sc);
115 Stem::add_head (Rhythmic_head *n)
117 n->set_elt_property ("stem", this->self_scm_);
118 n->add_dependency (this); // ?
121 Group_interface gi (this);
122 if (Note_head *nh = dynamic_cast<Note_head *> (n))
131 Stem::invisible_b () const
133 return !(first_head () && first_head()->balltype_i () >= 1);
137 Stem::get_center_distance (Direction d) const
139 int staff_center = 0;
140 int distance = d*(head_positions()[d] - staff_center);
141 return distance >? 0;
145 Stem::get_default_dir () const
147 int du = get_center_distance (UP);
148 int dd = get_center_distance (DOWN);
151 return Direction (sign (dd -du));
153 return Direction (int(paper_l ()->get_var ("stem_default_neutral_direction")));
157 Stem::set_default_stemlen ()
160 SCM scm_len = get_elt_property("length");
161 if (scm_len != SCM_UNDEFINED)
163 length_f = gh_scm2double (scm_len);
166 length_f = paper_l ()->get_var ("stem_length0");
168 bool grace_b = get_elt_property ("grace") != SCM_UNDEFINED;
169 String type_str = grace_b ? "grace_" : "";
171 Real shorten_f = paper_l ()->get_var (type_str + "forced_stem_shorten0");
173 if (!get_direction ())
174 set_direction (get_default_dir ());
177 stems in unnatural (forced) direction should be shortened,
178 according to [Roush & Gourlay]
180 if (((int)chord_start_f ())
181 && (get_direction () != get_default_dir ()))
182 length_f -= shorten_f;
189 set_stemend ((get_direction () > 0) ? head_positions()[BIGGER] + length_f:
190 head_positions()[SMALLER] - length_f);
192 bool no_extend_b = get_elt_property ("no-stem-extend") != SCM_UNDEFINED;
193 if (!grace_b && !no_extend_b && (get_direction () * stem_end_f () < 0))
198 Stem::flag_i () const
200 SCM s = get_elt_property ("duration-log");
201 return (gh_number_p (s)) ? gh_scm2int (s) : 2;
206 Stem::set_default_extents ()
208 if (!stem_length_f ())
209 set_default_stemlen ();
214 Stem::set_noteheads ()
220 Link_array<Note_head> head_l_arr =
221 Group_interface__extract_elements (this, (Note_head*)0, "heads");
223 head_l_arr.sort (Note_head::compare);
224 if (get_direction () < 0)
225 head_l_arr.reverse ();
227 Note_head * beginhead = first_head ();
228 beginhead->set_elt_property ("extremal", SCM_BOOL_T);
229 if (beginhead != head_l_arr.top ())
230 head_l_arr.top ()->set_elt_property ("extremal", SCM_BOOL_T);
233 int lastpos = int (Staff_symbol_referencer_interface (beginhead).position_f ());
234 for (int i=1; i < head_l_arr.size (); i ++)
236 Real p = Staff_symbol_referencer_interface (head_l_arr[i]).position_f ();
237 int dy =abs (lastpos- (int)p);
242 head_l_arr[i]->flip_around_stem (get_direction ());
252 Stem::do_pre_processing ()
254 if (yextent_.empty_b ())
255 set_default_extents ();
260 set_elt_property ("transparent", SCM_BOOL_T);
266 set_spacing_hints ();
272 set stem directions for hinting the optical spacing correction.
274 Modifies DIR_LIST property of the Stem's Score_column
276 TODO: more advanced: supply height of noteheads as well, for more advanced spacing possibilities
280 Stem::set_spacing_hints ()
284 SCM scmdir = gh_int2scm (get_direction ());
285 SCM dirlist = column_l ()->get_elt_property ("dir-list");
286 if (dirlist == SCM_UNDEFINED)
289 if (scm_sloppy_memq (scmdir, dirlist) == SCM_EOL)
291 dirlist = gh_cons (scmdir, dirlist);
292 column_l ()->set_elt_property ("dir-list", dirlist);
301 SCM st = get_elt_property ("style");
302 if ( st != SCM_UNDEFINED)
304 style = ly_scm2string (st);
307 char c = (get_direction () == UP) ? 'u' : 'd';
308 Molecule m = lookup_l ()->afm_find (String ("flags-") + to_str (c) +
310 if (!style.empty_b ())
311 m.add_molecule(lookup_l ()->afm_find (String ("flags-") + to_str (c) + style));
316 Stem::dim_callback (Dimension_cache const* c)
318 Stem * s = dynamic_cast<Stem*> (c->element_l ());
321 if (s->get_elt_property ("beam") != SCM_UNDEFINED || abs (s->flag_i ()) <= 2)
325 r = s->flag ().dim_.x ();
326 r += s->note_delta_f ();
334 const Real ANGLE = 20* (2.0*M_PI/360.0); // ugh!
337 Stem::do_brew_molecule_p () const
339 Molecule *mol_p =new Molecule;
340 Interval stem_y = yextent_;
341 Real dy = staff_symbol_referencer_interface (this)
342 .staff_line_leading_f ()/2.0;
346 head_wid = first_head ()->extent (X_AXIS).length ();
347 stem_y[Direction(-get_direction ())] += get_direction () * head_wid * tan(ANGLE)/(2*dy);
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);
357 if (get_elt_property ("beam") == SCM_UNDEFINED
358 && abs (flag_i ()) > 2)
360 Molecule fl = flag ();
361 fl.translate_axis(stem_y[get_direction ()]*dy, Y_AXIS);
362 mol_p->add_molecule (fl);
367 mol_p->translate_axis (note_delta_f (), X_AXIS);
373 Stem::note_delta_f () const
378 Interval head_wid(0, first_head()->extent (X_AXIS).length ());
379 Real rule_thick = paper_l ()->get_var ("stemthickness");
381 Interval stem_wid(-rule_thick/2, rule_thick/2);
382 if (get_direction () == CENTER)
383 r = head_wid.center ();
385 r = head_wid[get_direction ()] - stem_wid[get_direction ()];
391 Stem::hpos_f () const
393 return note_delta_f () + Item::hpos_f ();
400 SCM b= get_elt_property ("beam");
401 return dynamic_cast<Beam*> (unsmob_element (b));
406 Stem::calc_stem_info () const
410 SCM bd = get_elt_property ("beam-dir");
412 = staff_symbol_referencer_interface (this).staff_line_leading_f ()/2;
419 beam_dir = to_dir (bd);
423 programming_error ("Beam direction not set.");
424 beam_dir = UP; // GURAUGRNAGURAGU! urg !
427 Real interbeam_f = paper_l ()->interbeam_f (beam_l ()->multiplicity_i_);
428 Real beam_f = gh_scm2double (beam_l ()->get_elt_property ("beam-thickness"));
430 info.idealy_f_ = chord_start_f ();
432 // for simplicity, we calculate as if dir == UP
433 info.idealy_f_ *= beam_dir;
435 bool grace_b = get_elt_property ("grace") != SCM_UNDEFINED;
436 bool no_extend_b = get_elt_property ("no-stem-extend") != SCM_UNDEFINED;
438 int stem_max = (int)rint(paper_l ()->get_var ("stem_max"));
439 String type_str = grace_b ? "grace_" : "";
440 Real min_stem_f = paper_l ()->get_var (type_str + "minimum_stem_length"
441 + to_str (beam_l ()->multiplicity_i_ <? stem_max)) * internote_f;
442 Real stem_f = paper_l ()->get_var (type_str + "stem_length"
443 + to_str (beam_l ()->multiplicity_i_ <? stem_max)) * internote_f;
445 if (!beam_dir || (beam_dir == get_direction ()))
446 /* normal beamed stem */
448 if (beam_l ()->multiplicity_i_)
450 info.idealy_f_ += beam_f;
451 info.idealy_f_ += (beam_l ()->multiplicity_i_ - 1) * interbeam_f;
453 info.miny_f_ = info.idealy_f_;
454 info.maxy_f_ = INT_MAX;
456 info.idealy_f_ += stem_f;
457 info.miny_f_ += min_stem_f;
460 lowest beam of (UP) beam must never be lower than second staffline
462 Hmm, reference (Wanske?)
464 Although this (additional) rule is probably correct,
465 I expect that highest beam (UP) should also never be lower
466 than middle staffline, just as normal stems.
469 if (!grace_b && !no_extend_b)
471 //highest beam of (UP) beam must never be lower than middle staffline
472 info.miny_f_ = info.miny_f_ >? 0;
473 //lowest beam of (UP) beam must never be lower than second staffline
474 info.miny_f_ = info.miny_f_ >? (- 2 * internote_f - beam_f
475 + (beam_l ()->multiplicity_i_ > 0) * beam_f + interbeam_f * (beam_l ()->multiplicity_i_ - 1));
481 info.idealy_f_ -= beam_f;
482 info.maxy_f_ = info.idealy_f_;
483 info.miny_f_ = -INT_MAX;
485 info.idealy_f_ -= stem_f;
486 info.maxy_f_ -= min_stem_f;
489 info.idealy_f_ = info.maxy_f_ <? info.idealy_f_;
490 info.idealy_f_ = info.miny_f_ >? info.idealy_f_;
492 Real interstaff_f = calc_interstaff_dist (this, beam_l ());
493 info.idealy_f_ += interstaff_f * beam_dir;
495 SCM s = get_elt_property ("shorten");
496 if (s != SCM_UNDEFINED)
497 info.idealy_f_ -= gh_double2scm (s);
498 info.miny_f_ += interstaff_f * beam_dir;
499 info.maxy_f_ += interstaff_f * beam_dir;