]> git.donarmstrong.com Git - lilypond.git/commitdiff
Change flag creation to use the 'flag prop (function returning the stencil)
authorReinhold Kainhofer <reinhold@kainhofer.com>
Tue, 17 Jun 2008 21:25:37 +0000 (23:25 +0200)
committerReinhold Kainhofer <reinhold@kainhofer.com>
Sat, 30 Aug 2008 11:05:21 +0000 (13:05 +0200)
-) Added the 'flag grob property to the stem: It's a function taking the
   stem grob and returning a stencil for the whole flag (including a possible
   grace slash). It uses the 'flag-style property with the exact same values
   as previously, so any existing score should still be working.
   The default is ly:stem::calc-flag (implemented in C++), but
   I also implemented the default styles (no-flag, normal-flag and
   mensural-flag) in Scheme, where the function default-flag also uses
   the 'flag-style grob property. Both (the flag creation in C++ and in
   Scheme) show practically the same performance[*], so we might get rid of
   one of them in the future. Flag creation using scheme can thus be enabled
   by
      \override Stem #'flag = #default-flag
   flag creation in C++ can be explicitly enabled by
      \override Stem #'flag = #ly:stem::calc-flag

-) Implemented the default flag styles as scheme-functions, so that one can
   re-use them in one's own flag style functions. The default flags functions
   are implemented in a modular way, so one can easily create styles that
   adjust only some aspects of the default flags. An example style implemented
   in the regression test is to use mirrored flags (i.e. flags always pointing
   to the left). This can be implemented by creating the flag for the opposite
   stem direction and rotating it by 180 degrees ;-)

-) Added regression tests to check that the default flag styles all keep
   working.

-) In the regression tests, I also added some custom styles: weighted-flag,
   where the flags are shown as one big black box and the "number" of flags
   is indicated by the height of the box. The other example is the
   mirrored-normal-flag style mentioned above (useful for tutorials about music
   notation to show that flags should *NOT* be printed to the left!)

The real motivation for this feature, namely straight flags (either
old-style with a large slant or modern-style with a much smaller slant),
is not yet implemented, but should not be too hard, using the
ly:round-filled-polygon function.

[*] We now have two ways to generate flags: One C++ implementation
(ly:stem::calc-flag) and one pure-Scheme implementation (default-flag).
Both require the same amount of memory and there is hardly any difference
in their runtime. For example, a file consisting of 10,000 eighth notes
(nothing else) needs ~1.5GB RAM and runs for a bit over 3 minutes here,
with the C++ implementation beating the Scheme implementation by mere
5 seconds:
In C++:
    real    3m9.133s
    user    3m4.896s

In Scheme:
    real    3m14.016s
    user    3m10.024s

input/regression/flags-default.ly [new file with mode: 0644]
input/regression/flags-in-scheme.ly [new file with mode: 0644]
lily/include/stem.hh
lily/staff-symbol-referencer-scheme.cc
lily/stem.cc
scm/define-grob-properties.scm
scm/define-grobs.scm
scm/flag-styles.scm [new file with mode: 0644]
scm/lily.scm
scm/safe-lily.scm

diff --git a/input/regression/flags-default.ly b/input/regression/flags-default.ly
new file mode 100644 (file)
index 0000000..bed879f
--- /dev/null
@@ -0,0 +1,62 @@
+\version "2.11.57"
+
+\header {
+  texidoc = "Default flag styles: '(), 'mensural and 'no-flag.
+  Compare all three methods to print them (C++ default implementation, 
+  Scheme implementation using the 'flag-style grob property and 
+  setting the 'flag property explicitly to the desired Scheme function.
+  All three lines should be absolutely identical."
+}
+
+
+% test notes, which will be shown in different style:
+testnotes = { \autoBeamOff c'8 d'16 c'32 d'64 \acciaccatura {c'8} d'64 c''8 d''16 c''32 d''64 \acciaccatura {c''8} d''64  }
+
+{
+  \override Score.RehearsalMark #'self-alignment-X = #LEFT
+  \time 2/4
+  s2 \break
+
+  % Old settings: default, 'mensural, 'no-flag
+  \mark "Default flags (C++)"
+  \testnotes
+
+  \mark "Symbol: 'mensural (C++)"
+  \override Stem #'flag-style = #'mensural
+  \testnotes
+
+  \mark "Symbol: 'no-flag (C++)"
+  \override Stem #'flag-style = #'no-flag
+  \testnotes
+
+  \break
+
+  % The same, but with the Scheme implementation of default-flag
+  \override Stem #'flag = #default-flag
+  \revert Stem #'flag-style
+  \mark "Default flags (Scheme)"
+  \testnotes
+
+  \mark "Symbol: 'mensural (Scheme)"
+  \override Stem #'flag-style = #'mensural
+  \testnotes
+
+  \mark "Symbol: 'no-flag (Scheme)"
+  \override Stem #'flag-style = #'no-flag
+  \testnotes
+
+  \break
+
+  % New settings: no settings, normal-flag, mensural-flag, no-flag
+  \mark "Function: normal-flag"
+  \override Stem #'flag = #normal-flag
+  \testnotes
+
+  \mark "Function: mensural-flag"
+  \override Stem #'flag = #mensural-flag
+  \testnotes
+
+  \mark "Function: no-flag"
+  \override Stem #'flag = #no-flag
+  \testnotes
+}
diff --git a/input/regression/flags-in-scheme.ly b/input/regression/flags-in-scheme.ly
new file mode 100644 (file)
index 0000000..0707f84
--- /dev/null
@@ -0,0 +1,42 @@
+\version "2.11.57"
+
+\header {
+  texidoc = "The 'flag property of the Stem grob can be set to a custom
+scheme function to generate the glyph for the flag."
+}
+
+
+% test notes, which will be shown in different style:
+testnotes = { \autoBeamOff c'8 d'16 c'32 d'64 \acciaccatura {c'8} d'64 c''8 d''16 c''32 d''64 \acciaccatura {c''8} d''64  }
+
+#(define-public (weight-flag stem-grob)
+  (let* ((log (- (ly:grob-property stem-grob 'duration-log) 2))
+         (is-up (eqv? (ly:grob-property stem-grob 'direction) UP))
+         (yext (if is-up (cons (* log -0.8) 0) (cons 0 (* log 0.8))))
+         (flag-stencil (make-filled-box-stencil '(-0.4 . 0.4) yext))
+         (stroke-style (ly:grob-property stem-grob 'stroke-style))
+         (stroke-stencil (if (equal? stroke-style "grace") (make-line-stencil 0.2 -0.9 -0.4 0.9 -0.4) empty-stencil)))
+    (ly:stencil-add flag-stencil stroke-stencil)))
+
+
+% Create a flag stencil by looking up the glyph from the font
+#(define (inverted-flag stem-grob)
+  (let* ((dir (if (eqv? (ly:grob-property stem-grob 'direction) UP) "d" "u"))
+         (flag (retrieve-glyph-flag "" dir "" stem-grob))
+         (stroke-style (ly:grob-property stem-grob 'stroke-style))
+         (stencil (if (null? stroke-style) flag
+                         (add-stroke-glyph flag stem-grob dir stroke-style ""))))
+    (ly:stencil-rotate stencil 180 -1 -1)))
+
+{
+  \override Score.RehearsalMark #'self-alignment-X = #LEFT
+  \time 2/4
+  \mark "Function: weight-flag (custom)"
+  \override Stem #'flag = #weight-flag
+  \testnotes
+
+  \mark "Function: inverted-flag (custom)"
+  \override Stem #'flag = #inverted-flag
+  \testnotes
+
+}
index 7de67b0f7421c563c6d2969cbd64df9b07b8fc03..26ceec7a0ee5431d40f4fcbfc6736f1b0eb444aa 100644 (file)
@@ -55,5 +55,6 @@ public:
   DECLARE_SCHEME_CALLBACK (pure_height, (SCM, SCM, SCM));
   DECLARE_SCHEME_CALLBACK (height, (SCM));
   DECLARE_SCHEME_CALLBACK (calc_cross_staff, (SCM));
+  DECLARE_SCHEME_CALLBACK (calc_flag, (SCM));
 };
 #endif
index 8e0094f143d29abc7f56e8db760e161f934e9f23..84391d47ad8b09e02874e78bb403ea01dd354094 100644 (file)
@@ -8,6 +8,7 @@
 
 #include "grob.hh"
 #include "staff-symbol-referencer.hh"
+#include "staff-symbol.hh"
 #include "libc-extension.hh"
 
 LY_DEFINE (ly_grob_staff_position, "ly:grob-staff-position",
@@ -23,3 +24,16 @@ LY_DEFINE (ly_grob_staff_position, "ly:grob-staff-position",
   else
     return scm_from_double (pos);
 }
+
+LY_DEFINE (ly_position_on_line_p, "ly:position-on-line?",
+           2, 0, 0, (SCM sg, SCM spos),
+           "Return whether @var{pos} is on a line of the staff associated with the the grob @var{sg} (even on an extender line).")
+{
+  LY_ASSERT_SMOB (Grob, sg, 1);
+  LY_ASSERT_TYPE (scm_is_number, spos, 1);
+  Grob *g = unsmob_grob (sg);
+  Grob *st = Staff_symbol_referencer::get_staff_symbol (g);
+  int pos = scm_to_int (spos);
+  bool on_line = st ? Staff_symbol::on_line (g, pos) : false;
+  return scm_from_bool (on_line);
+}
index b517b6ec9f69f323dfa97fdea5a52f1781187b92..c66712fb2495dac98aeec001dfb1acf6d3e40ba2 100644 (file)
@@ -570,29 +570,25 @@ Stem::stem_end_position (Grob *me)
   return robust_scm2double (me->get_property ("stem-end-position"), 0);
 }
 
-Stencil
-Stem::flag (Grob *me)
+MAKE_SCHEME_CALLBACK (Stem, calc_flag, 1);
+SCM
+Stem::calc_flag (SCM smob)
 {
-  int log = duration_log (me);
-  if (log < 3
-      || unsmob_grob (me->get_object ("beam")))
-    return Stencil ();
+  Grob *me = unsmob_grob (smob);
 
-  if (!is_normal_stem (me))
-    return Stencil ();
-  
+  int log = duration_log (me);
   /*
     TODO: maybe property stroke-style should take different values,
     e.g. "" (i.e. no stroke), "single" and "double" (currently, it's
     '() or "grace").  */
   string flag_style;
 
-  SCM flag_style_scm = me->get_property ("flag-style");
+   SCM flag_style_scm = me->get_property ("flag-style");
   if (scm_is_symbol (flag_style_scm))
     flag_style = ly_symbol2string (flag_style_scm);
 
   if (flag_style == "no-flag")
-    return Stencil ();
+    return Stencil ().smobbed_copy ();
 
   bool adjust = true;
 
@@ -607,14 +603,14 @@ Stem::flag (Grob *me)
     */
     {
       if (adjust)
-       {
-         int p = (int) (rint (stem_end_position (me)));
-         staffline_offs
-           = Staff_symbol_referencer::on_line (me, p) ? "0" : "1";
-       }
+        {
+          int p = (int) (rint (stem_end_position (me)));
+          staffline_offs
+            = Staff_symbol_referencer::on_line (me, p) ? "0" : "1";
+        }
       else
-       staffline_offs = "2";
-    }
+        staffline_offs = "2";
+     }
   else
     staffline_offs = "";
 
@@ -631,17 +627,40 @@ Stem::flag (Grob *me)
     {
       string stroke_style = ly_scm2string (stroke_style_scm);
       if (!stroke_style.empty ())
-       {
-         string font_char = to_string (dir) + stroke_style;
-         Stencil stroke = fm->find_by_name ("flags." + font_char);
-         if (stroke.is_empty ())
-           me->warning (_f ("flag stroke `%s' not found", font_char));
-         else
-           flag.add_stencil (stroke);
-       }
-    }
+        {
+          string font_char = to_string (dir) + stroke_style;
+          Stencil stroke = fm->find_by_name ("flags." + font_char);
+          if (stroke.is_empty ())
+            me->warning (_f ("flag stroke `%s' not found", font_char));
+          else
+            flag.add_stencil (stroke);
+        }
+     }
+
+  return flag.smobbed_copy ();
+}
+
 
-  return flag;
+Stencil
+Stem::flag (Grob *me)
+{
+  int log = duration_log (me);
+  if (log < 3
+      || unsmob_grob (me->get_object ("beam")))
+    return Stencil ();
+
+  if (!is_normal_stem (me))
+    return Stencil ();
+
+  // This get_property call already evaluates the scheme function with
+  // the grob passed as argument! Thus, we only have to check if a valid
+  // stencil is returned.
+  SCM flag_style_scm = me->get_property ("flag");
+  if (Stencil *flag = unsmob_stencil (flag_style_scm)) {
+    return *flag;
+  } else {
+    return Stencil ();
+  }
 }
 
 MAKE_SCHEME_CALLBACK (Stem, width, 1);
@@ -1032,6 +1051,7 @@ ADD_INTERFACE (Stem,
               "details "
               "direction "
               "duration-log "
+              "flag "
               "flag-style "
               "french-beaming "
               "length "
index cd122c5dbab2224f5a1411485601eb301387946a..fedda67579051d8b64b832a92258acb694c4ede7 100644 (file)
@@ -204,11 +204,16 @@ problem, we pad each item by this amount (by adding the @q{car} on the
 left side of the item and adding the @q{cdr} on the right side of the
 item).  In order to make a grob take up no horizontal space at all,
 set this to @code{(+inf.0 . -inf.0)}.")
+     (flag ,ly:stencil? "A function returning the full flag stencil for
+the @code{Stem}, which is passed to the function as the only argument.
+The default ly:stem::calc-stencil function uses the @code{flag-style}
+property to determine the correct glyph for the
+flag. By providing your own function, you can create arbitrary flags.")
      (flag-count ,number? "The number of tremolo beams.")
-     (flag-style ,symbol? "A string determining what style of flag
-glyph is typeset on a @code{Stem}.  Valid options include @code{()}
-and @code{mensural}.  Additionally, @code{no-flag} switches off the
-flag.")
+     (flag-style ,symbol? "A symbol determining what style of flag
+glyph is typeset on a @code{Stem}.  Valid options include @code{'()} for
+standard flags, @code{'mensural} and @code{'no-flag}, which switches off 
+the flag.")
      (font-encoding ,symbol? "The font encoding is the broadest
 category for selecting a font.  Options include: @code{fetaMusic},
 @code{fetaNumber}, @code{TeX-text}, @code{TeX-math},
index f8a6526acf6659202a3845cc2e9a1454ed301da1..975d42582ffbd2544bcba886724cbe5dbf63f8f3 100644 (file)
        (length . ,ly:stem::calc-length)
        (thickness . 1.3)
        (cross-staff . ,ly:stem::calc-cross-staff)
+       (flag . ,ly:stem::calc-flag)
        (details
         . (
            ;; 3.5 (or 3 measured from note head) is standard length
diff --git a/scm/flag-styles.scm b/scm/flag-styles.scm
new file mode 100644 (file)
index 0000000..4a6db49
--- /dev/null
@@ -0,0 +1,159 @@
+;;;;  flag-styles.scm
+;;;;
+;;;;  source file of the GNU LilyPOnd music typesetter
+;;;;
+
+(define-public (no-flag stem-grob)
+  "No flag: Simply return empty stencil"
+  empty-stencil)
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;;  Straight flags
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+
+;; ;; TODO
+;; (define-public (add-stroke-straight stencil dir stroke-style)
+;;   stencil
+;; )
+;;
+;; ;; Create a stencil for a straight flag
+;; ;; flag-thickness, -spacing are given in staff spaces
+;; ;; *flag-length are given in black notehead widths
+;; ;; TODO
+;; (define-public (straight-flag flag-thickness flag-spacing
+;;                        upflag-angle upflag-length
+;;                        downflag-angle downflag-length)
+;;   (lambda (stem-grob)
+;;     (let* ((log (ly:grob-property stem-grob 'duration-log))
+;;            (staff-space 1) ; TODO
+;;            (black-notehead-width 1) ; TODO
+;;            (stem-thickness 1) ; TODO: get rid of
+;;            (half-stem-thickness (/ stem-thickness 2))
+;;            (staff-space 1) ; TODO
+;;            (up-length (+ (* upflag-length black-notehead-width) half-stem-thickness))
+;;            (down-length (+ (* downflag-length black-notehead-width) half-stem-thickness))
+;;            (thickness (* flag-thickness staff-space))
+;;            (spacing (* flag-spacing staff-space)))
+;;       empty-stencil
+;;     )
+;;   )
+;; )
+;;
+;; ;; Modern straight flags: angles are not so large as with the old style
+;; (define-public (modern-straight-flag stem-grob)
+;;   ((straight-flag 0.55 0.9 -18 0.95 22 1.0) stem-grob))
+;;
+;; ;; Old-straight flags (Bach, etc.): quite large flag angles
+;; (define-public (old-straight-flag stem-grob)
+;;   ((straight-flag 0.55 0.9 -45 0.95 45 1.0) stem-grob))
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;;  Flags created from feta glyphs (normal and mensural flags)
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+
+; NOTE: By default, lilypond uses the C++ method Stem::calc-flag
+; (ly:stem::calc-flag is the corresponding Scheme interface) to generate the
+; flag stencil. The following functions are simply a reimplementation in
+; Scheme, so that one has that functionality available in Scheme, if one
+; wants to write a flag style, which modifies one of the standard flags
+; by some stencil operations.
+
+
+(define-public (add-stroke-glyph stencil stem-grob dir stroke-style flag-style)
+  "Load and add a stroke (represented by a glyph in the font) to the given
+   flag stencil"
+  (if (not (string? stroke-style))
+    stencil
+    ; Otherwise: look up the stroke glyph and combine it with the flag
+    (let* ((font-char (string-append "flags." flag-style dir stroke-style))
+           (alt-font-char (string-append "flags." dir stroke-style))
+           (font (ly:grob-default-font stem-grob))
+           (tmpstencil (ly:font-get-glyph font font-char))
+           (stroke-stencil (if (ly:stencil-empty? tmpstencil)
+                               (ly:font-get-glyph font alt-font-char)
+                               tmpstencil)))
+      (if (ly:stencil-empty? stroke-stencil)
+        (begin
+          (ly:warning (_ "flag stroke `~a' or `~a'not found") font-char alt-font-char)
+          stencil)
+        (ly:stencil-add stencil stroke-stencil)))))
+
+
+(define-public (retrieve-glyph-flag flag-style dir dir-modifier stem-grob)
+  "Load the correct flag glyph from the font"
+  (let* ((log (ly:grob-property stem-grob 'duration-log))
+         (font (ly:grob-default-font stem-grob))
+         (font-char (string-append "flags." flag-style dir dir-modifier (number->string log)))
+         (flag (ly:font-get-glyph font font-char)))
+    (if (ly:stencil-empty? flag)
+      (ly:warning "flag ~a not found" font-char))
+    flag))
+
+
+(define-public (create-glyph-flag flag-style dir-modifier stem-grob)
+  "Create a flag stencil by looking up the glyph from the font"
+  (let* ((dir (if (eqv? (ly:grob-property stem-grob 'direction) UP) "u" "d"))
+         (flag (retrieve-glyph-flag flag-style dir dir-modifier stem-grob))
+         (stroke-style (ly:grob-property stem-grob 'stroke-style)))
+    (if (null? stroke-style)
+      flag
+      (add-stroke-glyph flag stem-grob dir stroke-style flag-style))))
+
+
+
+(define-public (mensural-flag stem-grob)
+  "Mensural flags: Create the flag stencil by loading the glyph from the font.
+   Flags are always aligned with staff lines, so we need to check the end point
+   of the stem: For stems ending on staff lines, use different flags than for 
+   notes between staff lines.  The idea is that flags are always vertically 
+   aligned with the staff lines, regardless of whether the note head is on a 
+   staff line or between two staff lines.  In other words, the inner end of 
+   a flag always touches a staff line."
+
+  (let* ((adjust #t)
+         (stem-end (inexact->exact (round (ly:grob-property stem-grob 'stem-end-position))))
+         ; For some reason the stem-end is a real instead of an integer...
+         (dir-modifier (if (ly:position-on-line? stem-grob stem-end) "1" "0"))
+         (modifier (if adjust dir-modifier "2")))
+    (create-glyph-flag "mensural" modifier stem-grob)))
+
+
+
+(define-public ((glyph-flag flag-style) stem-grob)
+  "Simulates the default way of generating flags: look up glyphs
+   flags.style[ud][1234] from the feta font and use it for the flag stencil."
+  (create-glyph-flag flag-style "" stem-grob))
+
+
+
+(define-public (normal-flag stem-grob)
+  "Create a default flag"
+  (create-glyph-flag "" "" stem-grob))
+
+
+
+(define-public (default-flag stem-grob)
+  "Create a flag stencil for the stem. Its style will be derived from the 
+   @code{'flag-style} Stem property. By default, @code{lilypond} uses a
+   C++ Function (which is slightly faster) to do exactly the same as this 
+   function. However, if one wants to modify the default flags, this function 
+   can be used to obtain the default flag stencil, which can then be modified
+   at will. The correct way to do this is:
+@example
+\\override Stem #'flag = #default-flag
+\\override Stem #'flag-style = #'mensural
+@end example
+"
+  (let* ((flag-style-symbol (ly:grob-property stem-grob 'flag-style))
+         (flag-style (if (symbol? flag-style-symbol)
+                         (symbol->string flag-style-symbol)
+                         "")))
+    (cond
+        ((equal? flag-style "") (normal-flag stem-grob))
+        ((equal? flag-style "mensural") (mensural-flag stem-grob))
+        ((equal? flag-style "no-flag") (no-flag stem-grob))
+        (else ((glyph-flag flag-style) stem-grob)))))
index e518ce298345fc9a0dd7c1316dedb108dfd72c2e..a7ff41a4d19c1e475bcd807dc2f422f6c0aeefd9 100644 (file)
@@ -337,6 +337,7 @@ The syntax is the same as `define*-public'."
            "font.scm"
            "encoding.scm"
            
+           "flag-styles.scm"
            "fret-diagrams.scm"
            "harp-pedals.scm"
            "predefined-fretboards.scm"
index 6c2917890e8a3c0003ed2f8b8c921229986e829c..ae3d56ccfb9b452fdd2303a42dc0cd88b0f17207 100644 (file)
@@ -92,6 +92,7 @@
    ly:number->string
    ly:option-usage
    ly:output-def-clone
+   ly:output-def-lookup
    ly:output-def-scope
    ly:output-description
    ly:paper-book?
    ly:paper-get-font
    ly:paper-get-number
    ly:paper-system?
-   ly:output-def-lookup
    ly:parser-parse-string
    ly:pitch-alteration
    ly:pitch-diff
    ly:pitch-transpose
    ly:pitch<?
    ly:pitch?
+   ly:position-on-line?
    ly:round-filled-box
    ly:run-translator
    ly:set-option