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
))
35 pair
= gh_cons (gh_int2scm (0),gh_int2scm (0));
36 set_elt_property ("beaming", pair
);
38 index_set_cell (pair
, d
, gh_int2scm (i
));
42 Stem::beam_count (Direction d
) const
44 SCM p
=get_elt_property ("beaming");
46 return gh_scm2int (index_cell (p
,d
));
52 Stem::head_positions () const
55 Mysterious FreeBSD fix by John Galbraith. Somehow, the empty intervals
56 trigger FP exceptions on FreeBSD. Fix: do not return infinity
61 return Interval_t
<int> (100,-100);
64 Link_array
<Note_head
> head_l_arr
=
65 Group_interface__extract_elements (this, (Note_head
*)0, "heads");
68 for (int i
=0; i
< head_l_arr
.size (); i
++)
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
;
79 Stem::stem_begin_f () const
81 return yextent_
[Direction(-get_direction ())];
85 Stem::chord_start_f () const
87 return head_positions()[get_direction ()]
88 * Staff_symbol_referencer_interface (this).staff_line_leading_f ()/2.0;
92 Stem::stem_end_f () const
94 return yextent_
[get_direction ()];
98 Stem::set_stemend (Real se
)
101 if (get_direction () && get_direction () * head_positions()[get_direction ()] >= se
*get_direction ())
102 warning (_ ("Weird stem size; check for narrow beams"));
105 yextent_
[get_direction ()] = se
;
106 yextent_
[Direction(-get_direction ())] = head_positions()[-get_direction ()];
110 Stem::type_i () const
112 return first_head () ? first_head ()->balltype_i () : 2;
118 Note head that determines hshift for upstems
121 Stem::support_head ()const
123 SCM h
= get_elt_property ("support-head");
124 Score_element
* nh
= unsmob_element (h
);
128 return first_head ();
133 The note head which forms one end of the stem.
136 Stem::first_head () const
138 const int inf
= 1000000;
140 Direction dir
= get_direction ();
144 for (SCM s
= get_elt_property ("heads"); gh_pair_p (s
); s
= gh_cdr (s
))
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 ());
159 Stem::add_head (Rhythmic_head
*n
)
161 n
->set_elt_property ("stem", this->self_scm_
);
162 n
->add_dependency (this); // ?
165 Group_interface
gi (this);
166 if (Note_head
*nh
= dynamic_cast<Note_head
*> (n
))
176 set_elt_property ("heads", SCM_EOL
);
177 set_elt_property ("rests", SCM_EOL
);
181 Stem::invisible_b () const
183 return !(first_head () && first_head()->balltype_i () >= 1);
187 Stem::get_center_distance (Direction d
) const
189 int staff_center
= 0;
190 int distance
= d
*(head_positions()[d
] - staff_center
);
191 return distance
>? 0;
195 Stem::get_default_dir () const
197 int du
= get_center_distance (UP
);
198 int dd
= get_center_distance (DOWN
);
201 return Direction (sign (dd
-du
));
203 return Direction (int(paper_l ()->get_var ("stem_default_neutral_direction")));
207 Stem::set_default_stemlen ()
210 SCM scm_len
= get_elt_property("length");
211 if (scm_len
!= SCM_UNDEFINED
)
213 length_f
= gh_scm2double (scm_len
);
216 length_f
= paper_l ()->get_var ("stem_length0");
218 bool grace_b
= get_elt_property ("grace") != SCM_UNDEFINED
;
219 String type_str
= grace_b
? "grace_" : "";
221 Real shorten_f
= paper_l ()->get_var (type_str
+ "forced_stem_shorten0");
224 'set-default-stemlen' sets direction too
226 if (!get_direction ())
227 set_direction (get_default_dir ());
230 stems in unnatural (forced) direction should be shortened,
231 according to [Roush & Gourlay]
233 if (((int)chord_start_f ())
234 && (get_direction () != get_default_dir ()))
235 length_f
-= shorten_f
;
242 set_stemend ((get_direction () > 0) ? head_positions()[BIGGER
] + length_f
:
243 head_positions()[SMALLER
] - length_f
);
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))
251 Stem::flag_i () const
253 SCM s
= get_elt_property ("duration-log");
254 return (gh_number_p (s
)) ? gh_scm2int (s
) : 2;
259 Stem::set_default_extents ()
261 if (yextent_
.empty_b ())
262 set_default_stemlen ();
266 Stem::set_noteheads ()
271 Link_array
<Score_element
> heads
=
272 Group_interface__extract_elements (this, (Score_element
*)0, "heads");
274 heads
.sort (compare_position
);
275 Direction dir
=get_direction ();
281 Real w
= support_head ()->extent (X_AXIS
)[dir
];
282 for (int i
=0; i
< heads
.size (); i
++)
284 heads
[i
]->translate_axis (w
- heads
[i
]->extent (X_AXIS
)[dir
], X_AXIS
);
288 int lastpos
= int (Staff_symbol_referencer_interface (heads
[0]).position_f ());
289 for (int i
=1; i
< heads
.size (); i
++)
291 Real p
= Staff_symbol_referencer_interface (heads
[i
]).position_f ();
292 int dy
=abs (lastpos
- (int)p
);
298 Real l
= heads
[i
]->extent (X_AXIS
).length ();
299 heads
[i
]->translate_axis (l
* get_direction (), X_AXIS
);
311 Stem::do_pre_processing ()
313 if (yextent_
.empty_b ())
314 set_default_extents ();
319 set_elt_property ("transparent", SCM_BOOL_T
);
324 set_spacing_hints ();
330 set stem directions for hinting the optical spacing correction.
332 Modifies DIR_LIST property of the Stem's Score_column
334 TODO: more advanced: supply height of noteheads as well, for more advanced spacing possibilities
338 Stem::set_spacing_hints ()
342 SCM scmdir
= gh_int2scm (get_direction ());
343 SCM dirlist
= column_l ()->get_elt_property ("dir-list");
344 if (dirlist
== SCM_UNDEFINED
)
347 if (scm_sloppy_memq (scmdir
, dirlist
) == SCM_EOL
)
349 dirlist
= gh_cons (scmdir
, dirlist
);
350 column_l ()->set_elt_property ("dir-list", dirlist
);
359 SCM st
= get_elt_property ("style");
360 if ( st
!= SCM_UNDEFINED
)
362 style
= ly_scm2string (st
);
365 char c
= (get_direction () == UP
) ? 'u' : 'd';
366 Molecule m
= lookup_l ()->afm_find (String ("flags-") + to_str (c
) +
368 if (!style
.empty_b ())
369 m
.add_molecule(lookup_l ()->afm_find (String ("flags-") + to_str (c
) + style
));
374 Stem::dim_callback (Dimension_cache
const* c
)
376 Stem
* s
= dynamic_cast<Stem
*> (c
->element_l ());
379 if (s
->get_elt_property ("beam") != SCM_UNDEFINED
|| abs (s
->flag_i ()) <= 2)
383 r
= s
->flag ().dim_
.x ();
384 r
+= s
->note_delta_f ();
390 const Real ANGLE
= 20* (2.0*M_PI
/360.0); // ugh!
393 Stem::do_brew_molecule_p () const
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;
402 head_wid
= support_head ()->extent (X_AXIS
).length ();
403 stem_y
[Direction(-get_direction ())] += get_direction () * head_wid
* tan(ANGLE
)/(2*dy
);
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
);
413 if (!beam_l () && abs (flag_i ()) > 2)
415 Molecule fl
= flag ();
416 fl
.translate_axis(stem_y
[get_direction ()]*dy
, Y_AXIS
);
417 mol_p
->add_molecule (fl
);
422 mol_p
->translate_axis (note_delta_f (), X_AXIS
);
428 Stem::note_delta_f () const
433 Interval
head_wid(0, first_head()->extent (X_AXIS
).length ());
434 Real rule_thick
= paper_l ()->get_var ("stemthickness");
436 Interval
stem_wid(-rule_thick
/2, rule_thick
/2);
437 if (get_direction () == CENTER
)
438 r
= head_wid
.center ();
440 r
= head_wid
[get_direction ()] - stem_wid
[get_direction ()];
446 Stem::hpos_f () const
448 return note_delta_f () + Item::hpos_f ();
455 SCM b
= get_elt_property ("beam");
456 return dynamic_cast<Beam
*> (unsmob_element (b
));
460 // ugh still very long.
462 Stem::calc_stem_info () const
466 Direction beam_dir
= beam_l ()->get_direction ();
469 programming_error ("Beam dir not set.");
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"));
479 info
.idealy_f_
= chord_start_f ();
481 // for simplicity, we calculate as if dir == UP
482 info
.idealy_f_
*= beam_dir
;
484 bool grace_b
= get_elt_property ("grace") != SCM_UNDEFINED
;
485 bool no_extend_b
= get_elt_property ("no-stem-extend") != SCM_UNDEFINED
;
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
;
494 if (!beam_dir
|| (beam_dir
== get_direction ()))
495 /* normal beamed stem */
497 if (beam_l ()->get_multiplicity ())
499 info
.idealy_f_
+= beam_f
;
500 info
.idealy_f_
+= (beam_l ()->get_multiplicity () - 1) * interbeam_f
;
502 info
.miny_f_
= info
.idealy_f_
;
503 info
.maxy_f_
= INT_MAX
;
505 info
.idealy_f_
+= stem_f
;
506 info
.miny_f_
+= min_stem_f
;
509 lowest beam of (UP) beam must never be lower than second staffline
511 Hmm, reference (Wanske?)
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.
518 if (!grace_b
&& !no_extend_b
)
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));
530 info
.idealy_f_
-= beam_f
;
531 info
.maxy_f_
= info
.idealy_f_
;
532 info
.miny_f_
= -INT_MAX
;
534 info
.idealy_f_
-= stem_f
;
535 info
.maxy_f_
-= min_stem_f
;
538 info
.idealy_f_
= info
.maxy_f_
<? info
.idealy_f_
;
539 info
.idealy_f_
= info
.miny_f_
>? info
.idealy_f_
;
541 Real interstaff_f
= calc_interstaff_dist (this, beam_l ());
543 SCM s
= beam_l ()->get_elt_property ("shorten");
544 if (s
!= SCM_UNDEFINED
)
545 info
.idealy_f_
-= gh_double2scm (s
);
547 info
.idealy_f_
+= interstaff_f
* beam_dir
;
548 info
.miny_f_
+= interstaff_f
* beam_dir
;
549 info
.maxy_f_
+= interstaff_f
* beam_dir
;