From: Jan Nieuwenhuizen Date: Thu, 1 Feb 2001 15:17:13 +0000 (+0100) Subject: patch::: 1.3.127.jcn2 X-Git-Tag: release/1.3.128~3 X-Git-Url: https://git.donarmstrong.com/?a=commitdiff_plain;h=04e1bc1c75af8f56a15013837ecd881ae4e4e319;p=lilypond.git patch::: 1.3.127.jcn2 1.3.127.jcn2 ============ * Beam (y, dy) calculations now as list of scm functions. * Added cleaned-up excerpt from Coriolan for tutorial. * Bugfix: beam looks at smallest gap for auto knees. * Property-description-fix: allow auto-knee-gap to be set to false. --- diff --git a/CHANGES b/CHANGES index 2af4125e72..0d4c60ba31 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,14 @@ +1.3.127.jcn2 +============ + +* Beam (y, dy) calculations now as list of scm functions. + +* Added cleaned-up excerpt from Coriolan for tutorial. + +* Bugfix: beam looks at smallest gap for auto knees. + +* Property-description-fix: allow auto-knee-gap to be set to false. + 1.3.127.jcn1 ============ diff --git a/Documentation/user/refman.itely b/Documentation/user/refman.itely index 1c85434ee2..57a51016be 100644 --- a/Documentation/user/refman.itely +++ b/Documentation/user/refman.itely @@ -5,6 +5,21 @@ @c to automagically fill in these menus @c before saving changes + +@ignore + TODO: + + fix all FIXMEs + + Rhythm staff (clef, x-notehead) + Piano pedals + \addlyrics, \autochange, \partcombine: music (re)grouping + markup text + postscript, scheme output? + (links to?) using/existance of ly2dvi, lilypond-book +@end ignore + + @c.{Reference Manual} @node Reference Manual @@ -1002,10 +1017,12 @@ See the documentation of @code{measurePosition}. * Ottava:: * Span requests:: @end menu + @c. {Beam} @node Beam @subsubsection Beam @cindex Beam + @c. {Automatic beams} @unnumberedsubsubsec Automatic beams @@ -1014,10 +1031,12 @@ See the documentation of @code{measurePosition}. @cindex @code{Voice.noAutoBeaming} -By default, LilyPond will generate beams automatically. This feature -can be disabled by setting the @code{Voice.noAutoBeaming} property to -true. It can be overridden for specific cases by specifying explicit -beams. +LilyPond will group flagged notes and generate beams autmatically, where +appropriate. This feature can be disabled by setting the +@code{Voice.noAutoBeaming} property to true, which you may find +necessary for the melody that goes with lyrics, eg. Automatic beaming +can easily be overridden for specific cases by specifying explicit +beams, see @ref{Manual beams}. @cindex @code{Voice.autoBeamSettings} @cindex @code{(end * * * *)} @@ -1027,15 +1046,11 @@ A large number of Voice properties are used to decide how to generate beams. Their default values appear in @file{scm/auto-beam.scm}. In general, beams can begin anywhere, but their ending location is significant. Beams can end on a beat, or at durations specified by the -properties in -@code{Voice.autoBeamSettings}. -To end beams every quarter note, for example, you could set the property -@code{(end * * * *)} to @code{(make-moment 1 -4)}. To end beams at every three eighth notes you would set -it to @code{(make-moment 1 8)}. -The same syntax can be used to specify beam -starting points using -@code{(begin * * * *)}, eg: +properties in @code{Voice.autoBeamSettings}. To end beams every quarter +note, for example, you could set the property @code{(end * * * *)} to +@code{(make-moment 1 4)}. To end beams at every three eighth notes you +would set it to @code{(make-moment 1 8)}. The same syntax can be used +to specify beam starting points using @code{(begin * * * *)}, eg: @quotation @example \property Voice.autoBeamSettings \override @@ -1064,16 +1079,19 @@ you would use @code{(end * * 1 32)}. @c. {Manual beams} @cindex Automatic beams @unnumberedsubsubsec Manual beams - @cindex beams, manual @cindex @code{]} @cindex @code{[} -FIXME -Beaming can be generated automatically; see section @ref{Automatic Beaming}. +For most situations, beaming can be generated automatically; see section +@ref{Automatic Beaming}. In certain cases it may be necessary to +override the automatic beaming decisions that LilyPond makes. You can +do this by specifying beams explicitely. -A beam is specified by surrounding the beamed notes with brackets -`@code{[}' and `@code{]}'. +A manual beam is specified by surrounding the notes that should make up +the beam, with brackets `@code{[}' and `@code{]}'. + +FIXME: example. (or tell: why would the auto-beamer fail here?) @lilypond[fragment,verbatim,center] [a'8 a'] [a'16 a' a' a'] @@ -1082,13 +1100,12 @@ A beam is specified by surrounding the beamed notes with brackets @end lilypond -@cindex @code{-}@code{-} - @c. {Adjusting beams} @unnumberedsubsubsec Adjusting beams @cindex Adjusting beams +FIXME @c. {Slur} diff --git a/VERSION b/VERSION index dbd42b45bb..4606ef3a63 100644 --- a/VERSION +++ b/VERSION @@ -2,7 +2,7 @@ PACKAGE_NAME=LilyPond MAJOR_VERSION=1 MINOR_VERSION=3 PATCH_LEVEL=127 -MY_PATCH_LEVEL=jcn1 +MY_PATCH_LEVEL=jcn2 # use the above to send patches: MY_PATCH_LEVEL is always empty for a # released version. diff --git a/input/bugs/beamed-chord.ly b/input/bugs/beamed-chord.ly new file mode 100644 index 0000000000..c274b48a40 --- /dev/null +++ b/input/bugs/beamed-chord.ly @@ -0,0 +1,12 @@ +\header{ +texidoc="Beam thinks that first two notes should be stem down. Can be fixed by uncommenting \stemUp"; +} + +\score{ + \notes\relative c'{ + %\stemUp + \clef alto; + \time 3/4; + r8 ) bes' d g, c, c,> r | + } +} diff --git a/input/tutorial/orchestral-score.ly b/input/tutorial/orchestral-score.ly new file mode 100644 index 0000000000..2e641fb901 --- /dev/null +++ b/input/tutorial/orchestral-score.ly @@ -0,0 +1,240 @@ +\version "1.3.120"; + +\include "paper16.ly"; + +% #(set! point-and-click #t) +#(define text-flat '((font-relative-size . -2) (music "accidentals--1"))) + + +% Coriolan 218-222 +flautoI = \notes\relative c'' { + \property Score.currentBarNumber = #218 + des2.()c4|e(f e)f|\break + r2 des4\sf()c|r2 des4\sf()c| +} +flautoII = \notes\relative c'' { + g2.()as4|bes(as bes)as| + R1*2 +} +oboeI = \notes\relative c'' { + e2.()f4|e(f e)f| + r2 as|r as\sf| +} +oboeII = \notes\relative c'' { + g2.()as4|bes(as bes)as| + r2 as'|r as\sf| +} +clarinettoI = \notes\relative c' { + es2.()d4|c(bis c)bes| +% r2 es4\sf()d|r2 es4\sf()d| +} +clarinettoII = \notes\relative c' { + es2.()d4|c(bes c)bes| +% r2 es'4\sf()d|r2 es4\sf()d| +} +fagottoI = \notes\relative c' { + bes2.()as4|g(f g)f| + r r8 des' des4\sf()es|r r8 des des4\sf()es| +} +fagottoII = \notes\relative c' { + bes2.()as4 | g(f g)f| + f4 r r2 | f4 r r2| +} +cornoI = \notes\relative c''' { + g2. f4|g f g f| +% r4 r8 f f2|r4 r8 f f2| +} +cornoII = \notes\relative c''' { + g,2. d'4|g, d' g, d'| + r4 r8 d d4\sf()es|r4 r8 d d4\sf()es| +} +tromboI = \notes\relative c'' { + c2. c4|c c c c| + R1*2| +} +tromboII = \notes\relative c' { + c2. c4|c c c c| + R1*2| +} +timpani = \notes\relative c { + c2:16 c4 c|c c c c | + R1*2| +} +violinoI = \notes\relative c' { + des'2:16\ff des4: c:|e,: f: e: f:| + r8 as des,4\sf~des()c |r8 as' des,4\sf~des()c| +} +violinoII = \notes\relative c' { + des2:16\ff des4: c: | bes: as: bes: as:| +% r8 as des,4\sf ~ des()c| r8 as' des,4\sf ~ des()c| +} +violaI = \notes\relative c' { + e2:16\ff e4: f:|b,: c: b: c:| + r4 r8 as as2\sf|r4 r8 as as2\sf| +} +violaII = \notes\relative c' { + bes2:16\ff bes4: as:|g: f: g: f: | + r4 r8 f f4\sf()ges|r4 r8 f f4\sf()ges| +} +violoncello = \notes\relative c { + bes2.\ff()as4|g( f g )f | + r4 r8 f' f4\sf()ges |r4 r8 f f4\sf()ges | +} +contrabasso = \notes\relative c { + bes2.\ff()as4|g( f g )f| + f4 r r2 |f'4 r r2| +} + + + +%% +%% Hmm, can't we move this to a `template.ly' +%% +\score { + < + \context StaffGroup = wood < + \context Staff = flauti < + \property Staff.midiInstrument = #"flute" + \property Staff.instrument = "2 Flauti" + \property Staff.instr = "Fl." + \context Voice=one \partcombine Voice + \context Thread=one \flautoI + \context Thread=two \flautoII + > + \context Staff = oboes < + \property Staff.midiInstrument = #"oboe" + \property Staff.instrument = "2 Oboi" + \property Staff.instr = "Ob." + \context Voice=one \partcombine Voice + \context Thread=one \oboeI + \context Thread=two \oboeII + > + \context Staff = clarinets < + \property Staff.midiInstrument = #"clarinet" + \property Staff.instrument = #`((kern . 0.5) + (lines "2 Clarinetti" (rows "(B" ,text-flat ")"))) + \property Staff.instr = #`((kern . 0.5) + (lines "Cl." (rows "(B" ,text-flat ")"))) + \property Staff.transposing = #-2 + \notes \key f \major; + \context Voice=one \partcombine Voice + \context Thread=one \clarinettoI + \context Thread=two \clarinettoII + > + \context Staff = bassoons < + \property Staff.midiInstrument = #"bassoon" + \property Staff.instrument = "2 Fagotti" + \property Staff.instr = "Fg." + \clef bass; + \context Voice=one \partcombine Voice + \context Thread=one \fagottoI + \context Thread=two \fagottoII + > + > + \context StaffGroup = brass < + \context Staff = frenshHorns < + \property Staff.midiInstrument = #"french horn" + \property Staff.instrument = #`((kern . 0.5) + (lines "2 Corni" (rows "(E" ,text-flat ")"))) + \property Staff.instr = #`((kern . 0.5) + (lines "Cor." (rows "(E" ,text-flat ")"))) + \property Staff.transposing = #3 + \notes \key c \major; + \context Voice=one \partcombine Voice + \context Thread=one \cornoI + \context Thread=two \cornoII + > + \context Staff = trumpets < + \property Staff.midiInstrument = #"clarinet" + \property Staff.instrument = #`((kern . 0.5) + (lines "2 Trombe" (rows "(C)"))) + \property Staff.instr = #`((kern . 0.5) + (lines "Tbe." (rows "(C)"))) + \context Voice=one \partcombine Voice + \context Thread=one \tromboI + \context Thread=two \tromboII + > + > + \context StaffGroup = timpani < + \context Staff = timpani < + \property Staff.midiInstrument = #"timpani" + \property Staff.instrument = #'((kern . 0.5) + (lines "2 Timpani" "(C-G)")) + \property Staff.instr = #"Timp." + \clef bass; + \timpani + > + > + \context StaffGroup = strings < + \context GrandStaff = violins < + \context Staff = viI < + \property Staff.midiInstrument = #"violin" + \property Staff.instrument = "Violino I" + \property Staff.instr = "Vi. I" + \violinoI + > + \context Staff = viII < + \property Staff.midiInstrument = #"violin" + \property Staff.instrument = "Violino II" + \property Staff.instr = "Vi. II" + \violinoII + > + > + \context Staff = vla < + \property Staff.midiInstrument = #"viola" + \property Staff.instrument = "Viola" + \property Staff.instr = "Vla." + \clef alto; + \context Voice=one \partcombine Voice + \context Thread=one \violaI + \context Thread=two \violaII + > + \context GrandStaff=bass < + \property GrandStaff.soloADue = ##t + \property GrandStaff.soloText = #"" + \property GrandStaff.soloIIText = #"" + % This is non-conventional, but currently it is + % the only way to tell the difference. + \property GrandStaff.aDueText = #"\\`a2" + \property GrandStaff.splitInterval = #'(1 . 0) + \property GrandStaff.changeMoment = + #`(,(make-moment 1 1) . ,(make-moment 1 1)) + + \context Staff=one < + \property Staff.midiInstrument = #"cello" + \property Staff.instrument = #'((kern . 0.5) + (lines "Violoncello" (rows " e") (rows "Contrabasso"))) + \property Staff.instr = "Vc." + \clef bass; + > + \context Staff=two < + \property Staff.midiInstrument = #"contrabass" + \property Staff.instrument = "Contrabasso" + \property Staff.instr = "C.B." + \clef bass; + \skip 1*4; % sustain clef + > + \context Staff=one \partcombine Staff + \context Voice=one \violoncello + \context Voice=two \contrabasso + > + > + > + \paper { + \paperSixteen + linewidth = 80 * \staffspace; + textheight = 200 * \staffspace; + \translator { + \OrchestralScoreContext + skipBars = ##t + % Hmm + currentBarNumber = #218 + BarNumber \override #'padding = #3 + RestCollision \override #'maximum-rest-count = #1 + marginScriptHorizontalAlignment = #1 + TimeSignature \override #'style = #'C + } + \translator { \HaraKiriStaffContext } + } +} + diff --git a/lily/beam.cc b/lily/beam.cc index 8ac599334a..f7c2f126eb 100644 --- a/lily/beam.cc +++ b/lily/beam.cc @@ -10,12 +10,16 @@ /* [TODO] + + * remove *-hs variables, and do all y-position stuff in staff-space. + This is not trivial, as Stem, and Stem_info both use point dimensions + (indicated by _f suffix) in several places too. + * shorter! (now +- 1000 lines) - * less hairy code - * move paper vars to scm + * less hairy code + * move paper vars to scm - remove *-hs variables, and do all y-position stuff in staff-space. -*/ + */ #include // tanh. @@ -202,7 +206,7 @@ Beam::consider_auto_knees (Grob *me) Real left = Stem::extremal_heads (stems[l])[d] ->relative_coordinate (common, Y_AXIS); - Real right = Stem::extremal_heads (stems[i])[d] + Real right = Stem::extremal_heads (stems[i])[-d] ->relative_coordinate (common, Y_AXIS); Real dy = right - left; @@ -277,141 +281,104 @@ Beam::set_stem_shorten (Grob*m) } /* - Set elt properties height and y-position if not set. - Adjust stem lengths to reach beam. + Call list of y-dy-callbacks, that handle setting of + grob-properties y, dy. + + User may set grob-properties: y-position-hs and height-hs + (to be fixed) that override the calculated y and dy. + + Because y and dy cannot be calculated and quanted separately, we + always calculate both, then check for user override. */ -MAKE_SCHEME_CALLBACK(Beam,after_line_breaking,1); +MAKE_SCHEME_CALLBACK (Beam, after_line_breaking, 1); SCM Beam::after_line_breaking (SCM smob) { Grob * me = unsmob_grob (smob); - - /* first, calculate y, dy */ - Real y, dy; - calc_default_position_and_height (me, &y, &dy); - if (visible_stem_count (me)) - { - if (suspect_slope_b (me, y, dy)) - dy = 0; - - Real damped_dy = calc_slope_damping_f (me, dy); - Real quantised_dy = quantise_dy_f (me, damped_dy); - - y += (dy - quantised_dy) / 2; - dy = quantised_dy; - } - /* - until here, we used only stem_info, which acts as if dir=up - */ - y *= Directional_element_interface::get (me); - dy *= Directional_element_interface::get (me); - - - Real half_space = Staff_symbol_referencer::staff_space (me) / 2; - - /* weird: why do we do calc_position_and_height () ? regardless of - this setting? - - If the user sets height, we still need to calculate the y-position. - If the user sets height-hs, we still need to calculate and - quantise y-position. - - We use least squares to calculate y-position and height, so we - inherently always calculate both. */ - /* check for user-override of dy */ - SCM s = me->remove_grob_property ("height-hs"); - if (gh_number_p (s)) - { - dy = gh_scm2double (s) * half_space; - } - me->set_grob_property ("height", gh_double2scm (dy)); + me->set_grob_property ("y", gh_double2scm (0)); + me->set_grob_property ("dy", gh_double2scm (0)); - /* check for user-override of y */ - s = me->remove_grob_property ("y-position-hs"); - if (gh_number_p (s)) - { - y = gh_scm2double (s) * half_space; - } - else - { - /* we can modify y, so we should quantise y */ - Real y_shift = check_stem_length_f (me, y, dy); - y += y_shift; - y = quantise_y_f (me,y, dy, 0); - set_stem_length (me, y, dy); - y_shift = check_stem_length_f (me, y, dy); + /* Hmm, callbacks should be called by, a eh, callback mechanism + somewhere(?), I guess, not by looping here. */ + + SCM list = me->get_grob_property ("y-dy-callbacks"); + for (SCM i = list; gh_pair_p (i); i = gh_cdr (i)) + gh_call1 (gh_car (i), smob); - if (y_shift > half_space / 4) - { - y += y_shift; - - /* - for significantly lengthened or shortened stems, - request quanting the other way. - */ - int quant_dir = 0; - if (abs (y_shift) > half_space / 2) - quant_dir = sign (y_shift) * Directional_element_interface::get (me); - y = quantise_y_f (me, y, dy, quant_dir); - } - } // UGH. Y is not in staff position unit? // Ik dacht datwe daar juist van weg wilden? - set_stem_length (me, y, dy); - me->set_grob_property ("y-position", gh_double2scm (y)); + + // Hmm, nu hebben we 3 dimensies, want inmiddels zijn we daar + // weer terug, maar dan / 2 + // (staff-space iso staff-position) + + set_stem_lengths (me); return SCM_UNSPECIFIED; } -/* - See Documentation/tex/fonts.doc - */ -void -Beam::calc_default_position_and_height (Grob*me,Real* y, Real* dy) + +MAKE_SCHEME_CALLBACK (Beam, least_squares, 1); +SCM +Beam::least_squares (SCM smob) { - *y = 0; - *dy = 0; - if (visible_stem_count (me) <= 1) - return; + Grob *me = unsmob_grob (smob); + + if (visible_stem_count (me) <= 1) + return SCM_UNSPECIFIED; + + Real y = 0; + Real dy = 0; Real first_ideal = Stem::calc_stem_info (first_visible_stem (me)).idealy_f_; if (first_ideal == Stem::calc_stem_info (last_visible_stem (me)).idealy_f_) { - *dy = 0; - *y = first_ideal; - return; + y = first_ideal; + dy = 0; } + else + { + Array ideals; - Array ideals; + // ugh -> use commonx + Real x0 = first_visible_stem (me)->relative_coordinate (0, X_AXIS); + Link_array stems= + Pointer_group_interface__extract_elements (me, (Item*)0, "stems"); - // ugh -> use commonx - Real x0 = first_visible_stem (me)->relative_coordinate (0, X_AXIS); - Link_array stems= - Pointer_group_interface__extract_elements (me, (Item*)0, "stems"); + for (int i=0; i < stems.size (); i++) + { + Item* s = stems[i]; + if (Stem::invisible_b (s)) + continue; + ideals.push (Offset (s->relative_coordinate (0, X_AXIS) - x0, + Stem::calc_stem_info (s).idealy_f_)); + } + Real dydx; + minimise_least_squares (&dydx, &y, ideals); - for (int i=0; i < stems.size (); i++) - { - Item* s = stems[i]; - if (Stem::invisible_b (s)) - continue; - ideals.push (Offset (s->relative_coordinate (0, X_AXIS) - x0, - Stem::calc_stem_info (s).idealy_f_)); + Real dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS) - x0; + dy = dydx * dx; } - Real dydx; - minimise_least_squares (&dydx, y, ideals); // duh, takes references - Real dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS) - x0; - *dy = dydx * dx; + me->set_grob_property ("y", gh_double2scm (y)); + me->set_grob_property ("dy", gh_double2scm (dy)); + return SCM_UNSPECIFIED; } -bool -Beam::suspect_slope_b (Grob*me, Real y, Real dy) +MAKE_SCHEME_CALLBACK (Beam, cancel_suspect_slope, 1); +SCM +Beam::cancel_suspect_slope (SCM smob) { - /* first, calculate y, dy */ - /* - steep slope running against lengthened stem is suspect - */ + Grob *me = unsmob_grob (smob); + + if (visible_stem_count (me) <= 1) + return SCM_UNSPECIFIED; + + Real y = gh_scm2double (me->get_grob_property ("y")); + Real dy = gh_scm2double (me->get_grob_property ("dy")); + + /* steep slope running against lengthened stem is suspect */ Real first_ideal = Stem::calc_stem_info (first_visible_stem (me)).idealy_f_; Real last_ideal = Stem::calc_stem_info (last_visible_stem (me)).idealy_f_; Real lengthened = gh_scm2double (me->get_grob_property ("outer-stem-length-limit")); @@ -424,9 +391,11 @@ Beam::suspect_slope_b (Grob*me, Real y, Real dy) if (((y - first_ideal > lengthened) && (dydx > steep)) || ((y + dy - last_ideal > lengthened) && (dydx < -steep))) { - return true; + Real adjusted_y = y + dy / 2; + me->set_grob_property ("y", gh_double2scm (adjusted_y)); + me->set_grob_property ("dy", gh_double2scm (0)); } - return false; + return SCM_UNSPECIFIED; } /* @@ -434,24 +403,184 @@ Beam::suspect_slope_b (Grob*me, Real y, Real dy) damped = tanh (slope) corresponds with some tables in [Wanske] */ -Real -Beam::calc_slope_damping_f (Grob*me,Real dy) +MAKE_SCHEME_CALLBACK (Beam, slope_damping, 1); +SCM +Beam::slope_damping (SCM smob) { - SCM damp = me->get_grob_property ("damping"); - int damping = gh_scm2int (damp); + Grob *me = unsmob_grob (smob); + + if (visible_stem_count (me) <= 1) + return SCM_UNSPECIFIED; + + SCM s = me->get_grob_property ("damping"); + int damping = gh_scm2int (s); if (damping) { - // ugh -> use commonx + Real y = gh_scm2double (me->get_grob_property ("y")); + Real dy = gh_scm2double (me->get_grob_property ("dy")); + + // ugh -> use commonx Real dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS) - first_visible_stem (me)->relative_coordinate (0, X_AXIS); Real dydx = dy && dx ? dy/dx : 0; dydx = 0.6 * tanh (dydx) / damping; - return dydx * dx; + + Real damped_dy = dydx * dx; + Real adjusted_y = y + (dy - damped_dy) / 2; + me->set_grob_property ("y", gh_double2scm (adjusted_y)); + me->set_grob_property ("dy", gh_double2scm (damped_dy)); } - return dy; + return SCM_UNSPECIFIED; } +/* + Quantise dy (height) of beam. + Generalisation of [Ross]. + */ +MAKE_SCHEME_CALLBACK (Beam, quantise_dy, 1); +SCM +Beam::quantise_dy (SCM smob) +{ + Grob *me = unsmob_grob (smob); + + if (visible_stem_count (me) <= 1) + return SCM_UNSPECIFIED; + + Array a; + SCM proc = me->get_grob_property ("height-quants"); + SCM quants = gh_call2 (proc, me->self_scm (), + gh_double2scm (me->paper_l ()->get_var ("stafflinethickness") + / 1.0)); + + for (SCM s = quants; gh_pair_p (s); s = gh_cdr (s)) + a.push (gh_scm2double (gh_car (s))); + + if (a.size () > 1) + { + Real y = gh_scm2double (me->get_grob_property ("y")); + Real dy = gh_scm2double (me->get_grob_property ("dy")); + + Real staff_space = Staff_symbol_referencer::staff_space (me); + + Interval iv = quantise_iv (a, abs (dy)/staff_space) * staff_space; + Real q = (abs (dy) - iv[SMALLER] <= iv[BIGGER] - abs (dy)) + ? iv[SMALLER] + : iv[BIGGER]; + + Real quantised_dy = q * sign (dy); + Real adjusted_y = y + (dy - quantised_dy) / 2; + me->set_grob_property ("y", gh_double2scm (adjusted_y)); + me->set_grob_property ("dy", gh_double2scm (quantised_dy)); + } + return SCM_UNSPECIFIED; +} + + +/* + What to do? Why do we have two dimensions (staff-position and + staff-space)? Do other grobs export staff-position to the user, + should we junk that? + + height-hs -> staff-position-height + y-position-hs -> staff-position + + or + + height-hs -> height / 2 + y-postion-hs -> y-position / 2 + + + UGHUGH. IF this callback is omitted, we hang. + FIXME: until here, we used only stem_info, which acts as if dir=up. +*/ +MAKE_SCHEME_CALLBACK (Beam, user_override, 1); +SCM +Beam::user_override (SCM smob) +{ + Grob *me = unsmob_grob (smob); + Real half_space = Staff_symbol_referencer::staff_space (me) / 2; + + Real y = gh_scm2double (me->get_grob_property ("y")); + Real dy = gh_scm2double (me->get_grob_property ("dy")); + + + SCM s = me->get_grob_property ("y-position-hs"); + if (gh_number_p (s)) + y = gh_scm2double (s) * half_space; + else + // ughugh + y *= Directional_element_interface::get (me); + + s = me->get_grob_property ("height-hs"); + if (gh_number_p (s)) + dy = gh_scm2double (s) * half_space; + else + // ughugh + dy *= Directional_element_interface::get (me); + + + me->set_grob_property ("y", gh_double2scm (y)); + me->set_grob_property ("dy", gh_double2scm (dy)); + + return SCM_UNSPECIFIED; +} + +/* + Ugh, this must be last, after user_override + Assumes directionised y/dy. + */ +MAKE_SCHEME_CALLBACK (Beam, do_quantise_y, 1); +SCM +Beam::do_quantise_y (SCM smob) +{ + Grob *me = unsmob_grob (smob); + + /* + If the user set y-position, we shouldn't do quanting. + */ + if (gh_number_p (me->get_grob_property ("y-position-hs"))) + return SCM_UNSPECIFIED; + + Real y = gh_scm2double (me->get_grob_property ("y")); + Real dy = gh_scm2double (me->get_grob_property ("dy")); + + /* we can modify y, so we should quantise y */ + Real half_space = Staff_symbol_referencer::staff_space (me) / 2; + Real y_shift = check_stem_length_f (me, y, dy); + y += y_shift; + y = quantise_y_f (me, y, dy, 0); + + /* + Hmm, this is a bit keyhole operation: we're passing `this' as a + parameter, and member vars as SCM properties. We should decide on + SCM/C/C++ boundary */ + me->set_grob_property ("y", gh_double2scm (y)); + set_stem_lengths (me); + y = gh_scm2double (me->get_grob_property ("y")); + + y_shift = check_stem_length_f (me, y, dy); + + if (y_shift > half_space / 4) + { + y += y_shift; + + /* + for significantly lengthened or shortened stems, + request quanting the other way. + */ + int quant_dir = 0; + if (abs (y_shift) > half_space / 2) + quant_dir = sign (y_shift) * Directional_element_interface::get (me); + y = quantise_y_f (me, y, dy, quant_dir); + } + + me->set_grob_property ("y", gh_double2scm (y)); + // me->set_grob_property ("dy", gh_double2scm (dy)); + return SCM_UNSPECIFIED; +} + + Real Beam::calc_stem_y_f (Grob*me,Item* s, Real y, Real dy) { @@ -530,15 +659,18 @@ Beam::check_stem_length_f (Grob*me,Real y, Real dy) stem directions and length should set to relative to the chord's position of the beam. */ void -Beam::set_stem_length (Grob*me,Real y, Real dy) +Beam::set_stem_lengths (Grob *me) { + if (visible_stem_count (me) <= 1) + return; + + Real y = gh_scm2double (me->get_grob_property ("y")); + Real dy = gh_scm2double (me->get_grob_property ("dy")); + Real half_space = Staff_symbol_referencer::staff_space (me)/2; Link_array stems= Pointer_group_interface__extract_elements (me, (Item*)0, "stems"); - if (stems.size () < 1) - return; - Grob *common = me->common_refpoint (stems[0], Y_AXIS); for (int i=1; i < stems.size (); i++) if (!Stem::invisible_b (stems[i])) @@ -559,46 +691,10 @@ Beam::set_stem_length (Grob*me,Real y, Real dy) } } -/* - [Ross] (simplification of) - Set dy complying with: - - zero - - thick / 2 + staffline_f / 2 - - thick + staffline_f - + n * staff_space -*/ -Real -Beam::quantise_dy_f (Grob*me,Real dy) -{ - Array a; - - SCM proc = me->get_grob_property ("height-quants"); - SCM quants = gh_call2 (proc, me->self_scm (), - gh_double2scm (me->paper_l ()->get_var ("stafflinethickness") - / 1.0)); - - - for (SCM s = quants; gh_pair_p (s); s = gh_cdr (s)) - a.push (gh_scm2double (gh_car (s))); - - if (a.size () <= 1) - return dy; - - Real staff_space = Staff_symbol_referencer::staff_space (me); - - Interval iv = quantise_iv (a, abs (dy)/staff_space) * staff_space; - Real q = (abs (dy) - iv[SMALLER] <= iv[BIGGER] - abs (dy)) - ? iv[SMALLER] - : iv[BIGGER]; - - return q * sign (dy); -} - /* Prevent interference from stafflines and beams. - See Documentation/tex/fonts.doc - We only need to quantise the (left) y-position of the beam, + We only need to quantise the (left) y of the beam, since dy is quantised too. if extend_b then stems must *not* get shorter */ @@ -694,7 +790,7 @@ Beam::stem_beams (Grob*me,Item *here, Item *next, Item *prev) Real dx = visible_stem_count (me) ? last_visible_stem (me)->relative_coordinate (0, X_AXIS) - first_visible_stem (me)->relative_coordinate (0, X_AXIS) : 0.0; - Real dy = gh_scm2double (me->get_grob_property ("height")); + Real dy = gh_scm2double (me->get_grob_property ("dy")); Real dydx = dy && dx ? dy/dx : 0; Molecule leftbeams; @@ -794,10 +890,6 @@ Beam::stem_beams (Grob*me,Item *here, Item *next, Item *prev) return leftbeams; } -/* - TODO: it would be nice to introduce y-position via callbacks. - */ - MAKE_SCHEME_CALLBACK(Beam,brew_molecule,1); SCM Beam::brew_molecule (SCM smob) @@ -823,9 +915,9 @@ Beam::brew_molecule (SCM smob) } - Real dy = gh_scm2double (me->get_grob_property ("height")); + Real dy = gh_scm2double (me->get_grob_property ("dy")); Real dydx = dy && dx ? dy/dx : 0; - Real y = gh_scm2double (me->get_grob_property ("y-position")); + Real y = gh_scm2double (me->get_grob_property ("y")); for (int j=0; j get_grob_property ("height"); + SCM s = beam->get_grob_property ("dy"); if (gh_number_p (s)) beam_dy = gh_scm2double (s); - s = beam->get_grob_property ("y-position"); + s = beam->get_grob_property ("y"); if (gh_number_p (s)) beam_y = gh_scm2double (s); @@ -993,10 +1085,12 @@ Beam::has_interface (Grob*me) void Beam::set_interface (Grob*me) { +#if 0 /* why the init? No way to tell difference between default and user override. */ - me->set_grob_property ("height", gh_int2scm (0)); // ugh. - me->set_grob_property ("y-position" ,gh_int2scm (0)); + me->set_grob_property ("y" ,gh_double2scm (0)); + me->set_grob_property ("dy", gh_double2scm (0)); me->set_interface (ly_symbol2scm("beam-interface")); +#endif } diff --git a/lily/include/beam.hh b/lily/include/beam.hh index 18126770af..c1eb843b1a 100644 --- a/lily/include/beam.hh +++ b/lily/include/beam.hh @@ -29,6 +29,17 @@ public: DECLARE_SCHEME_CALLBACK(brew_molecule, (SCM )); DECLARE_SCHEME_CALLBACK(before_line_breaking, (SCM )); DECLARE_SCHEME_CALLBACK(after_line_breaking, (SCM )); + + /* + y-dy callbacks + */ + DECLARE_SCHEME_CALLBACK (least_squares, (SCM)); + DECLARE_SCHEME_CALLBACK (cancel_suspect_slope, (SCM)); + DECLARE_SCHEME_CALLBACK (slope_damping, (SCM)); + DECLARE_SCHEME_CALLBACK (quantise_dy, (SCM)); + DECLARE_SCHEME_CALLBACK (user_override, (SCM)); + DECLARE_SCHEME_CALLBACK (do_quantise_y, (SCM)); + static Molecule stem_beams (Grob*,Item *here, Item *next, Item *prev); private: @@ -36,13 +47,9 @@ private: static void set_stem_directions (Grob*); static void consider_auto_knees (Grob*); static void set_stem_shorten (Grob*); - static void calc_default_position_and_height (Grob*,Real* y, Real* dy); - static bool suspect_slope_b (Grob*, Real y, Real dy); - static Real calc_slope_damping_f (Grob*, Real dy); static Real calc_stem_y_f (Grob*, Item* s, Real y, Real dy); static Real check_stem_length_f (Grob*, Real y, Real dy); - static void set_stem_length (Grob*, Real y, Real dy); - static Real quantise_dy_f (Grob*, Real dy); + static void set_stem_lengths (Grob*); static Real quantise_y_f (Grob*, Real y, Real dy, int quant_dir); static int forced_stem_count (Grob*); }; diff --git a/lily/stem-tremolo.cc b/lily/stem-tremolo.cc index 36cbc63f97..5ff2b8f2a5 100644 --- a/lily/stem-tremolo.cc +++ b/lily/stem-tremolo.cc @@ -76,7 +76,7 @@ Stem_tremolo::brew_molecule (SCM smob) if (beam) { Real dy = 0; - SCM s = beam->get_grob_property ("height"); + SCM s = beam->get_grob_property ("dy"); if (gh_number_p (s)) dy = gh_scm2double (s); Real dx = Beam::last_visible_stem (beam)->relative_coordinate (0, X_AXIS) diff --git a/scm/c++.scm b/scm/c++.scm index e3ce13b875..18cd4e273f 100644 --- a/scm/c++.scm +++ b/scm/c++.scm @@ -8,14 +8,20 @@ ;;; Note: this file can't be used without LilyPond executable (define (number-pair? x) - (and (pair? x) (number? (car x)) (number? (cdr x)))) + (and (pair? x) + (number? (car x)) (number? (cdr x)))) -(define (boolean-or-symbol? x) (or boolean? x) (or symbol? x)) +(define (boolean-or-symbol? x) + (or (boolean? x) (symbol? x))) -(define (number-or-string? x) (or (number? x) (string? x))) +(define (number-or-boolean? x) + (or (number? x) (boolean? x))) -(define markup? - (lambda (x) (or (string? x) (list? x)))) +(define (number-or-string? x) + (or (number? x) (string? x))) + +(define (markup? x) + (or (string? x) (list? x))) ;; ugh: code dup ; merge. (define (object-type obj) @@ -39,6 +45,7 @@ ((procedure? obj) "procedure") ((boolean-or-symbol? obj) "boolean or symbol") ((number-or-string? obj) "number or string") + ((number-or-boolean? obj) "number or boolean") ((markup? obj) "markup (list or string)") (else "unknown type"))) diff --git a/scm/grob-description.scm b/scm/grob-description.scm index 47ee15416f..df810a0048 100644 --- a/scm/grob-description.scm +++ b/scm/grob-description.scm @@ -57,6 +57,13 @@ ;; todo: clean this up a bit: the list is getting ;; rather long. (molecule-callback . ,Beam::brew_molecule) + (y-dy-callbacks . (,Beam::least_squares + ,Beam::cancel_suspect_slope + ,Beam::slope_damping + ,Beam::quantise_dy + ,Beam::user_override + ,Beam::do_quantise_y)) + (thickness . 0.48) ; in staff-space (before-line-breaking-callback . ,Beam::before_line_breaking) (after-line-breaking-callback . ,Beam::after_line_breaking) diff --git a/scm/grob-property-description.scm b/scm/grob-property-description.scm index cc6de9985d..8eb13e78ce 100644 --- a/scm/grob-property-description.scm +++ b/scm/grob-property-description.scm @@ -41,11 +41,8 @@ This procedure is called (using dependency resolution) after line breaking. Retu '(LEFT-offset . RIGHT-offset). This offset is added to the attachments to prevent ugly slurs. [fixme: we need more documentation here]. .") -(grob-property-description 'auto-interstaff-knee-gap number? ".") -(grob-property-description 'auto-knee-gap number? ".") - +(grob-property-description 'auto-knee-gap number-or-boolean? "the minimal smallest gap between two adjacent beamed chords for which beam will create auto-knees. Set to false for no auto knees." ) (grob-property-description 'axes list? "list of axis numbers. - In the case of alignment grobs, this should contain only one number.") (grob-property-description 'bar-size number? "size of a bar line.") (grob-property-description 'bars list? "list of barline pointers.") diff --git a/scm/translator-property-description.scm b/scm/translator-property-description.scm index 7ee1dc69cb..b34dc23090 100644 --- a/scm/translator-property-description.scm +++ b/scm/translator-property-description.scm @@ -100,6 +100,7 @@ key signatures after the bar lines: @end example ") (translator-property-description 'centralCPosition number? "Place of the central C. Usually determined by looking at clefPosition and clefGlyph.") +(translator-property-description 'changeMoment moment? "duration that voices are examined for differences, when part-combining. Usually unset or zero when combining threads into one voice, and 1 (or the duration of one measure) when combining voices into one staff.") (translator-property-description 'chordChanges boolean? "Only show changes in chords scheme?") (translator-property-description 'clefGlyph string? "Name of the symbol within the music font") (translator-property-description 'clefOctavation integer? "Add @@ -241,7 +242,8 @@ r1 r1*3 R1*3 \\\\property Score.skipBars= ##t r1*3 R1*3 (translator-property-description 'soloIIText string? "text for begin of solo for voice ``two'' when part-combining.") (translator-property-description 'soloText string? "text for begin of solo when part-combining.") (translator-property-description 'sparseTies boolean? "only create one tie per chord.") -(translator-property-description 'split-interval number-pair? "always split into two voices for contained intervals when part-combining.") +(translator-property-description 'splitInterval number-pair? "part-combiner will separate its two voices (or threads) when interval between the two voices is contained in this range.") +(translator-property-description 'split-interval boolean? "set if part-combiner separated voices based on splitInterval.") (translator-property-description 'squashedPosition integer? " Vertical position of squashing for Pitch_squash_engraver.") (translator-property-description 'staffsFound list? "list of all staff-symbols found.")