2 stem.cc -- implement Stem
4 source file of the GNU LilyPond music typesetter
6 (c) 1996--2000 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 Jan Nieuwenhuizen <janneke@gnu.org>
9 TODO: This is way too hairy
11 #include <math.h> // m_pi
13 #include "directional-element-interface.hh"
14 #include "dimension-cache.hh"
17 #include "paper-def.hh"
18 #include "note-head.hh"
20 #include "molecule.hh"
21 #include "paper-column.hh"
25 #include "group-interface.hh"
26 #include "cross-staff.hh"
27 #include "staff-symbol-referencer.hh"
32 Stem::set_beaming (int i
, Direction d
)
34 SCM pair
= get_elt_property ("beaming");
36 if (!gh_pair_p (pair
))
38 pair
= gh_cons (gh_int2scm (0),gh_int2scm (0));
39 set_elt_property ("beaming", pair
);
41 index_set_cell (pair
, d
, gh_int2scm (i
));
45 Stem::beam_count (Direction d
) const
47 SCM p
=get_elt_property ("beaming");
49 return gh_scm2int (index_cell (p
,d
));
55 Stem::head_positions () const
64 Drul_array
<Note_head
*> e (extremal_heads ());
66 return Interval (staff_symbol_referencer (e
[DOWN
]).position_f (),
67 staff_symbol_referencer( e
[UP
]).position_f ());
72 Stem::chord_start_f () const
74 return head_positions()[get_direction ()]
75 * Staff_symbol_referencer_interface (this).staff_space ()/2.0;
79 Stem::stem_end_position () const
81 SCM p
=get_elt_property ("stem-end-position");
85 Stem
* me
= (Stem
*) this;
86 pos
= get_default_stem_end_position ();
87 me
->set_elt_property ("stem-end-position", gh_double2scm (pos
));
90 pos
= gh_scm2double (p
);
96 Stem::get_direction () const
98 Direction d
= Directional_element_interface (this).get ();
102 Stem
* me
= (Stem
*) this;
103 d
= get_default_dir ();
105 Directional_element_interface (me
).set (d
);
112 Stem::set_stemend (Real se
)
115 Direction d
= get_direction ();
117 if (d
&& d
* head_positions()[get_direction ()] >= se
*d
)
118 warning (_ ("Weird stem size; check for narrow beams"));
120 set_elt_property ("stem-end-position", gh_double2scm (se
));
124 Stem::type_i () const
126 return first_head () ? first_head ()->balltype_i () : 2;
130 Note head that determines hshift for upstems
133 Stem::support_head ()const
135 SCM h
= get_elt_pointer ("support-head");
136 Score_element
* nh
= unsmob_element (h
);
139 else if (heads_i () == 1)
145 return unsmob_element (gh_car (get_elt_pointer ("heads")));
148 return first_head ();
153 Stem::heads_i ()const
155 Pointer_group_interface
gi (this, "heads");
160 The note head which forms one end of the stem.
163 Stem::first_head () const
165 return extremal_heads ()[-get_direction ()];
169 START is part where stem reaches `last' head.
171 Drul_array
<Note_head
*>
172 Stem::extremal_heads () const
174 const int inf
= 1000000;
175 Drul_array
<int> extpos
;
179 Drul_array
<Note_head
*> exthead
;
180 exthead
[LEFT
] = exthead
[RIGHT
] =0;
182 for (SCM s
= get_elt_pointer ("heads"); gh_pair_p (s
); s
= gh_cdr (s
))
184 Note_head
* n
= dynamic_cast<Note_head
*> (unsmob_element (gh_car (s
)));
185 Staff_symbol_referencer_interface
si (n
);
187 int p
= int(si
.position_f ());
191 if (d
* p
> d
* extpos
[d
])
196 } while (flip (&d
) != DOWN
);
203 Stem::add_head (Rhythmic_head
*n
)
205 n
->set_elt_pointer ("stem", this->self_scm_
);
206 n
->add_dependency (this);
208 Pointer_group_interface
gi (this);
209 if (Note_head
*nh
= dynamic_cast<Note_head
*> (n
))
220 set_elt_pointer ("heads", SCM_EOL
);
221 set_elt_pointer ("rests", SCM_EOL
);
223 add_offset_callback ( &Stem::off_callback
, X_AXIS
);
227 Stem::invisible_b () const
230 UGH. Who determines balltype for stem?
232 Note_head
* nh
= dynamic_cast<Note_head
*> (support_head ());
233 return !(heads_i () && nh
->balltype_i () >= 1);
237 Stem::get_center_distance (Direction d
) const
239 int staff_center
= 0;
240 int distance
= (int) (d
*(head_positions()[d
] - staff_center
));
241 return distance
>? 0;
245 Stem::get_default_dir () const
247 int du
= get_center_distance (UP
);
248 int dd
= get_center_distance (DOWN
);
251 return Direction (sign (dd
-du
));
253 return Direction (int(paper_l ()->get_var ("stem_default_neutral_direction")));
257 ugh. A is used for different purposes. This functionality should be
258 moved into scheme at some point to get rid of the silly
259 conversions. (but lets wait till we have namespaces in SCM)
262 Stem::get_default_stem_end_position () const
264 bool grace_b
= to_boolean (get_elt_property ("grace"));
265 String type_str
= grace_b
? "grace-" : "";
270 SCM scm_len
= get_elt_property("length");
271 if (gh_number_p (scm_len
))
273 length_f
= gh_scm2double (scm_len
);
277 s
= scm_eval (ly_symbol2scm ((type_str
+ "stem-length").ch_C()));
278 for (SCM q
= s
; q
!= SCM_EOL
; q
= gh_cdr (q
))
279 a
.push (gh_scm2double (gh_car (q
)));
281 // stem uses half-spaces
282 length_f
= a
[((flag_i () - 2) >? 0) <? (a
.size () - 1)] * 2;
287 s
= scm_eval (ly_symbol2scm ((type_str
+ "stem-shorten").ch_C()));
288 for (SCM q
= s
; q
!= SCM_EOL
; q
= gh_cdr (q
))
289 a
.push (gh_scm2double (gh_car (q
)));
292 // stem uses half-spaces
294 // fixme: use gh_list_ref () iso. array[]
295 Real shorten_f
= a
[((flag_i () - 2) >? 0) <? (a
.size () - 1)] * 2;
298 'set-default-stemlen' sets direction too
300 Direction dir
= get_direction ();
303 dir
= get_default_dir ();
304 Directional_element_interface (this).set (dir
);
308 stems in unnatural (forced) direction should be shortened,
309 according to [Roush & Gourlay]
311 if (((int)chord_start_f ())
312 && (get_direction () != get_default_dir ()))
313 length_f
-= shorten_f
;
316 Real st
= head_positions()[dir
] + dir
* length_f
;
318 bool no_extend_b
= to_boolean (get_elt_property ("no-stem-extend"));
319 if (!grace_b
&& !no_extend_b
&& dir
* st
< 0)
329 Stem::flag_i () const
331 SCM s
= get_elt_property ("duration-log");
332 return (gh_number_p (s
)) ? gh_scm2int (s
) : 2;
336 Stem::position_noteheads ()
341 Link_array
<Score_element
> heads
=
342 Pointer_group_interface__extract_elements (this, (Score_element
*)0, "heads");
344 heads
.sort (compare_position
);
345 Direction dir
=get_direction ();
351 Real w
= support_head ()->extent (X_AXIS
)[dir
];
352 for (int i
=0; i
< heads
.size (); i
++)
354 heads
[i
]->translate_axis (w
- heads
[i
]->extent (X_AXIS
)[dir
], X_AXIS
);
357 bool parity
= true; // todo: make this settable.
358 int lastpos
= int (Staff_symbol_referencer_interface (heads
[0]).position_f ());
359 for (int i
=1; i
< heads
.size (); i
++)
361 Real p
= Staff_symbol_referencer_interface (heads
[i
]).position_f ();
362 int dy
=abs (lastpos
- (int)p
);
368 Real l
= heads
[i
]->extent (X_AXIS
).length ();
369 heads
[i
]->translate_axis (l
* get_direction (), X_AXIS
);
380 GLUE_SCORE_ELEMENT(Stem
,before_line_breaking
);
382 Stem::member_before_line_breaking ()
384 stem_end_position (); // ugh. Trigger direction calc.
385 position_noteheads ();
389 remove_elt_property ("molecule-callback");
393 set_spacing_hints ();
394 return SCM_UNDEFINED
;
400 set stem directions for hinting the optical spacing correction.
402 Modifies DIR_LIST property of the Stem's Paper_column
404 TODO: more advanced: supply height of noteheads as well, for more advanced spacing possibilities
407 Stem::set_spacing_hints ()
411 SCM scmdir
= gh_int2scm (get_direction ());
412 SCM dirlist
= column_l ()->get_elt_property ("dir-list");
413 if (dirlist
== SCM_UNDEFINED
)
416 if (scm_sloppy_memq (scmdir
, dirlist
) == SCM_EOL
)
418 dirlist
= gh_cons (scmdir
, dirlist
);
419 column_l ()->set_elt_property ("dir-list", dirlist
);
428 SCM st
= get_elt_property ("flag-style");
429 if ( gh_string_p (st
))
431 style
= ly_scm2string (st
);
434 char c
= (get_direction () == UP
) ? 'u' : 'd';
435 Molecule m
= lookup_l ()->afm_find (String ("flags-") + to_str (c
) +
437 if (!style
.empty_b ())
438 m
.add_molecule(lookup_l ()->afm_find (String ("flags-") + to_str (c
) + style
));
443 Stem::dim_callback (Score_element
const *se
, Axis
)
445 Stem
* s
= dynamic_cast<Stem
*> ((Score_element
*)se
);
448 if (unsmob_element (s
->get_elt_pointer ("beam")) || abs (s
->flag_i ()) <= 2)
452 r
= s
->flag ().extent (X_AXIS
);
458 const Real ANGLE
= 20* (2.0*M_PI
/360.0); // ugh! Should be settable.
461 GLUE_SCORE_ELEMENT(Stem
,brew_molecule
);
464 Stem::member_brew_molecule () const
468 Staff_symbol_referencer_interface
si (first_head ());
470 Real y1
= si
.position_f();
471 Real y2
= stem_end_position ();
473 Interval
stem_y(y1
,y2
);
474 stem_y
.unite (Interval (y2
,y1
));
476 Real dy
= staff_symbol_referencer (this).staff_space ()/2.0;
479 head_wid
= support_head ()->extent (X_AXIS
).length ();
480 stem_y
[Direction(-get_direction ())] += get_direction () * head_wid
* tan(ANGLE
)/(2*dy
);
484 Real stem_width
= paper_l ()->get_var ("stemthickness");
485 Molecule ss
=lookup_l ()->filledbox (Box (Interval (-stem_width
/2, stem_width
/2),
486 Interval (stem_y
[DOWN
]*dy
, stem_y
[UP
]*dy
)));
487 mol
.add_molecule (ss
);
490 if (!beam_l () && abs (flag_i ()) > 2)
492 Molecule fl
= flag ();
493 fl
.translate_axis(stem_y
[get_direction ()]*dy
, Y_AXIS
);
494 mol
.add_molecule (fl
);
497 return mol
.create_scheme();
501 Stem::off_callback (Score_element
const* se
, Axis
)
503 Stem
*st
= dynamic_cast<Stem
*> ((Score_element
*)se
);
506 if (Note_head
* f
= st
->first_head ())
508 Interval
head_wid(0, f
->extent (X_AXIS
).length ());
510 if (to_boolean (st
->get_elt_property ("stem-centered")))
511 return head_wid
.center ();
513 Real rule_thick
= st
->paper_l ()->get_var ("stemthickness");
514 Direction d
= st
->get_direction ();
515 r
= head_wid
[d
] - d
* rule_thick
;
525 SCM b
= get_elt_pointer ("beam");
526 return dynamic_cast<Beam
*> (unsmob_element (b
));
530 // ugh still very long.
532 Stem::calc_stem_info () const
536 Direction beam_dir
= Directional_element_interface (beam_l ()).get ();
539 programming_error ("Beam dir not set.");
543 Staff_symbol_referencer_interface
st (this);
544 Real staff_space
= st
.staff_space ();
545 Real half_space
= staff_space
/ 2;
546 Real interbeam_f
= paper_l ()->interbeam_f (beam_l ()->get_multiplicity ());
547 Real thick
= gh_scm2double (beam_l ()->get_elt_property ("beam-thickness"));
548 int multiplicity
= beam_l ()->get_multiplicity ();
551 info
.idealy_f_
= chord_start_f ();
553 // for simplicity, we calculate as if dir == UP
554 info
.idealy_f_
*= beam_dir
;
555 SCM grace_prop
= get_elt_property ("grace");
557 bool grace_b
= to_boolean (grace_prop
);
561 String type_str
= grace_b
? "grace-" : "";
563 s
= scm_eval (ly_symbol2scm ((type_str
+ "beamed-stem-minimum-length").ch_C()));
565 for (SCM q
= s
; q
!= SCM_EOL
; q
= gh_cdr (q
))
566 a
.push (gh_scm2double (gh_car (q
)));
569 Real minimum_length
= a
[multiplicity
<? (a
.size () - 1)] * staff_space
;
570 s
= scm_eval (ly_symbol2scm ((type_str
+ "beamed-stem-length").ch_C()));
573 for (SCM q
= s
; q
!= SCM_EOL
; q
= gh_cdr (q
))
574 a
.push (gh_scm2double (gh_car (q
)));
576 Real stem_length
= a
[multiplicity
<? (a
.size () - 1)] * staff_space
;
578 if (!beam_dir
|| (beam_dir
== Directional_element_interface (this).get ()))
579 /* normal beamed stem */
583 info
.idealy_f_
+= thick
+ (multiplicity
- 1) * interbeam_f
;
585 info
.miny_f_
= info
.idealy_f_
;
586 info
.maxy_f_
= INT_MAX
;
588 info
.idealy_f_
+= stem_length
;
589 info
.miny_f_
+= minimum_length
;
592 lowest beam of (UP) beam must never be lower than second staffline
594 Hmm, reference (Wanske?)
596 Although this (additional) rule is probably correct,
597 I expect that highest beam (UP) should also never be lower
598 than middle staffline, just as normal stems.
601 bool no_extend_b
= to_boolean (get_elt_property ("no-stem-extend"));
602 if (!grace_b
&& !no_extend_b
)
604 /* highest beam of (UP) beam must never be lower than middle
606 lowest beam of (UP) beam must never be lower than second staffline
610 >? (- 2 * half_space
- thick
611 + (multiplicity
> 0) * thick
612 + interbeam_f
* (multiplicity
- 1));
618 info
.idealy_f_
-= thick
;
619 info
.maxy_f_
= info
.idealy_f_
;
620 info
.miny_f_
= -INT_MAX
;
622 info
.idealy_f_
-= stem_length
;
623 info
.maxy_f_
-= minimum_length
;
626 info
.idealy_f_
= (info
.maxy_f_
<? info
.idealy_f_
) >? info
.miny_f_
;
628 s
= beam_l ()->get_elt_property ("shorten");
630 info
.idealy_f_
-= gh_scm2double (s
);
632 Real interstaff_f
= -beam_dir
* calc_interstaff_dist (this, beam_l ());
634 info
.idealy_f_
+= interstaff_f
;
635 info
.miny_f_
+= interstaff_f
;
636 info
.maxy_f_
+= interstaff_f
;