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
12 #include "dimension-cache.hh"
15 #include "paper-def.hh"
16 #include "note-head.hh"
18 #include "molecule.hh"
19 #include "paper-column.hh"
23 #include "group-interface.hh"
24 #include "cross-staff.hh"
25 #include "staff-symbol-referencer.hh"
29 Stem::set_beaming (int i, Direction d )
31 SCM pair = get_elt_property ("beaming");
33 if (!gh_pair_p (pair))
34 pair = gh_cons (gh_int2scm (0),gh_int2scm (0));
36 index_set_cell (pair, d, gh_int2scm (i));
40 Stem::beam_count (Direction d) const
42 SCM p=get_elt_property ("beaming");
44 return gh_scm2int (index_cell (p,d));
50 Stem::head_positions () const
53 Mysterious FreeBSD fix by John Galbraith. Somehow, the empty intervals
54 trigger FP exceptions on FreeBSD. Fix: do not return infinity
59 return Interval_t<int> (100,-100);
62 Link_array<Note_head> head_l_arr =
63 Group_interface__extract_elements (this, (Note_head*)0, "heads");
66 for (int i =0; i < head_l_arr.size (); i++)
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;
77 Stem::stem_begin_f () const
79 return yextent_[Direction(-get_direction ())];
83 Stem::chord_start_f () const
85 return head_positions()[get_direction ()]
86 * Staff_symbol_referencer_interface (this).staff_line_leading_f ()/2.0;
90 Stem::stem_end_f () const
92 return yextent_[get_direction ()];
96 Stem::set_stemend (Real se)
99 if (get_direction () && get_direction () * head_positions()[get_direction ()] >= se*get_direction ())
100 warning (_ ("Weird stem size; check for narrow beams"));
103 yextent_[get_direction ()] = se;
104 yextent_[Direction(-get_direction ())] = head_positions()[-get_direction ()];
108 Stem::type_i () const
110 return first_head () ? first_head ()->balltype_i () : 2;
114 Stem::first_head () const
116 SCM h =get_elt_property ("heads");
120 Score_element * sc = unsmob_element (gh_car (h));
122 return dynamic_cast<Note_head*> (sc);
126 Stem::add_head (Rhythmic_head *n)
128 n->set_elt_property ("stem", this->self_scm_);
129 n->add_dependency (this); // ?
132 Group_interface gi (this);
133 if (Note_head *nh = dynamic_cast<Note_head *> (n))
142 Stem::invisible_b () const
144 return !(first_head () && first_head()->balltype_i () >= 1);
148 Stem::get_center_distance (Direction d) const
150 int staff_center = 0;
151 int distance = d*(head_positions()[d] - staff_center);
152 return distance >? 0;
156 Stem::get_default_dir () const
158 int du = get_center_distance (UP);
159 int dd = get_center_distance (DOWN);
162 return Direction (sign (dd -du));
164 return Direction (int(paper_l ()->get_var ("stem_default_neutral_direction")));
168 Stem::set_default_stemlen ()
171 SCM scm_len = get_elt_property("length");
172 if (scm_len != SCM_UNDEFINED)
174 length_f = gh_scm2double (scm_len);
177 length_f = paper_l ()->get_var ("stem_length0");
179 bool grace_b = get_elt_property ("grace") != SCM_UNDEFINED;
180 String type_str = grace_b ? "grace_" : "";
182 Real shorten_f = paper_l ()->get_var (type_str + "forced_stem_shorten0");
184 if (!get_direction ())
185 set_direction (get_default_dir ());
188 stems in unnatural (forced) direction should be shortened,
189 according to [Roush & Gourlay]
191 if (((int)chord_start_f ())
192 && (get_direction () != get_default_dir ()))
193 length_f -= shorten_f;
200 set_stemend ((get_direction () > 0) ? head_positions()[BIGGER] + length_f:
201 head_positions()[SMALLER] - length_f);
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))
209 Stem::flag_i () const
211 SCM s = get_elt_property ("duration-log");
212 return (gh_number_p (s)) ? gh_scm2int (s) : 2;
217 Stem::set_default_extents ()
219 if (yextent_.empty_b ())
220 set_default_stemlen ();
224 Stem::set_noteheads ()
229 Link_array<Score_element> head_l_arr =
230 Group_interface__extract_elements (this, (Score_element*)0, "heads");
232 head_l_arr.sort (compare_position);
233 if (get_direction () < 0)
234 head_l_arr.reverse ();
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);
242 int lastpos = int (Staff_symbol_referencer_interface (beginhead).position_f ());
243 for (int i=1; i < head_l_arr.size (); i ++)
245 Real p = Staff_symbol_referencer_interface (head_l_arr[i]).position_f ();
246 int dy =abs (lastpos- (int)p);
252 Real l = head_l_arr[i]->extent (X_AXIS).length ();
253 head_l_arr[i]->translate_axis (l * get_direction (), X_AXIS);
265 Stem::do_pre_processing ()
267 if (yextent_.empty_b ())
268 set_default_extents ();
273 set_elt_property ("transparent", SCM_BOOL_T);
278 set_spacing_hints ();
284 set stem directions for hinting the optical spacing correction.
286 Modifies DIR_LIST property of the Stem's Score_column
288 TODO: more advanced: supply height of noteheads as well, for more advanced spacing possibilities
292 Stem::set_spacing_hints ()
296 SCM scmdir = gh_int2scm (get_direction ());
297 SCM dirlist = column_l ()->get_elt_property ("dir-list");
298 if (dirlist == SCM_UNDEFINED)
301 if (scm_sloppy_memq (scmdir, dirlist) == SCM_EOL)
303 dirlist = gh_cons (scmdir, dirlist);
304 column_l ()->set_elt_property ("dir-list", dirlist);
313 SCM st = get_elt_property ("style");
314 if ( st != SCM_UNDEFINED)
316 style = ly_scm2string (st);
319 char c = (get_direction () == UP) ? 'u' : 'd';
320 Molecule m = lookup_l ()->afm_find (String ("flags-") + to_str (c) +
322 if (!style.empty_b ())
323 m.add_molecule(lookup_l ()->afm_find (String ("flags-") + to_str (c) + style));
328 Stem::dim_callback (Dimension_cache const* c)
330 Stem * s = dynamic_cast<Stem*> (c->element_l ());
333 if (s->get_elt_property ("beam") != SCM_UNDEFINED || abs (s->flag_i ()) <= 2)
337 r = s->flag ().dim_.x ();
338 r += s->note_delta_f ();
344 const Real ANGLE = 20* (2.0*M_PI/360.0); // ugh!
347 Stem::do_brew_molecule_p () const
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;
356 head_wid = first_head ()->extent (X_AXIS).length ();
357 stem_y[Direction(-get_direction ())] += get_direction () * head_wid * tan(ANGLE)/(2*dy);
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);
367 if (!beam_l () && abs (flag_i ()) > 2)
369 Molecule fl = flag ();
370 fl.translate_axis(stem_y[get_direction ()]*dy, Y_AXIS);
371 mol_p->add_molecule (fl);
376 mol_p->translate_axis (note_delta_f (), X_AXIS);
382 Stem::note_delta_f () const
387 Interval head_wid(0, first_head()->extent (X_AXIS).length ());
388 Real rule_thick = paper_l ()->get_var ("stemthickness");
390 Interval stem_wid(-rule_thick/2, rule_thick/2);
391 if (get_direction () == CENTER)
392 r = head_wid.center ();
394 r = head_wid[get_direction ()] - stem_wid[get_direction ()];
400 Stem::hpos_f () const
402 return note_delta_f () + Item::hpos_f ();
409 SCM b= get_elt_property ("beam");
410 return dynamic_cast<Beam*> (unsmob_element (b));
414 // ugh still very long.
416 Stem::calc_stem_info () const
420 SCM bd = get_elt_property ("beam-dir");
422 = staff_symbol_referencer_interface (this).staff_line_leading_f ()/2;
429 beam_dir = to_dir (bd);
433 programming_error ("Beam direction not set.");
434 beam_dir = UP; // GURAUGRNAGURAGU! urg !
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"));
440 info.idealy_f_ = chord_start_f ();
442 // for simplicity, we calculate as if dir == UP
443 info.idealy_f_ *= beam_dir;
445 bool grace_b = get_elt_property ("grace") != SCM_UNDEFINED;
446 bool no_extend_b = get_elt_property ("no-stem-extend") != SCM_UNDEFINED;
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;
455 if (!beam_dir || (beam_dir == get_direction ()))
456 /* normal beamed stem */
458 if (beam_l ()->multiplicity_i ())
460 info.idealy_f_ += beam_f;
461 info.idealy_f_ += (beam_l ()->multiplicity_i () - 1) * interbeam_f;
463 info.miny_f_ = info.idealy_f_;
464 info.maxy_f_ = INT_MAX;
466 info.idealy_f_ += stem_f;
467 info.miny_f_ += min_stem_f;
470 lowest beam of (UP) beam must never be lower than second staffline
472 Hmm, reference (Wanske?)
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.
479 if (!grace_b && !no_extend_b)
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));
491 info.idealy_f_ -= beam_f;
492 info.maxy_f_ = info.idealy_f_;
493 info.miny_f_ = -INT_MAX;
495 info.idealy_f_ -= stem_f;
496 info.maxy_f_ -= min_stem_f;
499 info.idealy_f_ = info.maxy_f_ <? info.idealy_f_;
500 info.idealy_f_ = info.miny_f_ >? info.idealy_f_;
502 Real interstaff_f = calc_interstaff_dist (this, beam_l ());
503 info.idealy_f_ += interstaff_f * beam_dir;
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;