lilypond-user
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: TupletBrackets edge thickness


From: Lukas-Fabian Moser
Subject: Re: TupletBrackets edge thickness
Date: Sat, 10 Aug 2024 14:17:07 +0200
User-agent: Mozilla Thunderbird

Hi Kieren, hi Karim,

Am 09.08.24 um 16:40 schrieb Kieren MacMillan:
Just trying to find out how to manage the thickness of the edges of tupletbrackets and if this is possible (cf. screenshot). In the documentation we can adjust the thickness of the whole bracket but not just the edges. Any idea?
I would imagine that either TupletBracket.edge-text or TupletBracket.stencil could be tweaked… Unfortunately I’ve spent 15 minutes and can’t find the magic incantation — hopefully someone else can!

Well, trying to teach how to fish :-):

In the Internals Reference https://lilypond.org/doc/v2.23/Documentation/internals/tupletbracket we see that the TupletBracket stencil is ly:tuplet-bracket::print. Moving to the source, git grep "tuplet-bracket::print" on the command line yields:

Documentation/ly-examples/stockhausen-klavierstueckII.ly:         (ly:tuplet-bracket::print grob)))
lily/tuplet-bracket.cc:MAKE_SCHEME_CALLBACK (Tuplet_bracket, print, "ly:tuplet-bracket::print", 1);
scm/define-grobs.scm:        (stencil . ,ly:tuplet-bracket::print)
scm/define-grobs.scm:        (stencil . ,ly:tuplet-bracket::print)

The second line shows that the procedure is in the C++ part of LilyPond (alas...); the third and fourth lines show two grobs using this procedure as stencil. (Reading in scm/define-grobs.scm reveals that the other one besides TupletBracket is LigatureBracket).

Now in lily/tuplet-bracket.cc we find:

MAKE_SCHEME_CALLBACK (Tuplet_bracket, print, "ly:tuplet-bracket::print", 1);
SCM
Tuplet_bracket::print (SCM smob)
{
  Spanner *me = LY_ASSERT_SMOB (Spanner, smob, 1);
  Stencil mol;

  bool tuplet_slur = scm_is_true (get_property (me, "tuplet-slur"));

  bool bracket_visibility = bracket_basic_visibility (me);
  SCM bracket_vis_prop = get_property (me, "bracket-visibility");

  /*
    Don't print a tuplet bracket and number if
    no X or Y positions were calculated.
  */
  SCM scm_x_span = get_property (me, "X-positions");
  SCM scm_positions = get_property (me, "positions");
  if (!scm_is_pair (scm_x_span) || !scm_is_pair (scm_positions))
    {
      me->suicide ();
      return SCM_EOL;
    }

  Interval x_span = from_scm (scm_x_span, Interval (0.0, 0.0));
  Interval positions = from_scm (scm_positions, Interval (0.0, 0.0));

  Drul_array<Offset> points;
  for (const auto d : {LEFT, RIGHT})
    points[d] = Offset (x_span[d], positions[d]);

  Grob *number_grob = unsmob<Grob> (get_object (me, "tuplet-number"));

  /*
    Don't print the bracket when it would be smaller than the number.
    ...Unless the user has coded bracket-visibility = #t, that is.
  */

  Real gap = 0.;
  if (bracket_visibility && number_grob)
    {
      Interval ext = number_grob->extent (number_grob, X_AXIS);
      if (!ext.is_empty ())
        {
          gap = ext.length () + 1.0;

          if (!from_scm<bool> (bracket_vis_prop) && gap > x_span.length ())
            bracket_visibility = false;
        }
    }

  if (bracket_visibility)
    {
      Drul_array<Real> zero (0, 0);

      Drul_array<Real> shorten
        = from_scm (get_property (me, "shorten-pair"), zero);

      Real ss = Staff_symbol_referencer::staff_space (me);
      scale_drul (&shorten, ss);

      Stencil brack;

      if (tuplet_slur)
        {
          brack = make_tuplet_slur (me, points[LEFT], points[RIGHT], shorten);
          mol.add_stencil (brack);
        }
      else
        {
          Drul_array<Stencil> edge_stencils;

          Drul_array<Real> height
            = from_scm (get_property (me, "edge-height"), zero);
          Drul_array<Real> flare
            = from_scm (get_property (me, "bracket-flare"), zero);
          Direction dir = get_grob_direction (me);

          scale_drul (&height, -ss * dir);
          scale_drul (&flare, ss);

          const auto connect_to_other = from_scm (
            get_property (me, "connect-to-neighbor"), Drul_array<bool> ());

          for (const auto d : {LEFT, RIGHT})
            {
              if (connect_to_other[d])
                {
                  height[d] = 0.0;
                  flare[d] = 0.0;
                  shorten[d] = 0.0;

                  SCM edge_text = get_property (me, "edge-text");

                  if (scm_is_pair (edge_text))
                    {
                      SCM text = index_get_cell (edge_text, d);
                      if (Text_interface::is_markup (text))
                        {
                          auto &es = edge_stencils[d];
                          es = Text_interface::grob_interpret_markup (me, text);
                          es.translate_axis (x_span[d] - x_span[LEFT], X_AXIS);
                        }
                    }
                }
            }

          Stencil brack;
          if (tuplet_slur)
            brack = make_tuplet_slur (me, points[LEFT], points[RIGHT], shorten);
          else
            brack = Bracket::make_bracket (
              me, Y_AXIS, points[RIGHT] - points[LEFT], height,
              /*
                                             0.1 = more space at right due to italics
                                             TODO: use italic correction of font.
                                           */
              Interval (-0.5, 0.5) * gap + 0.1, flare, shorten);


          for (const auto d : {LEFT, RIGHT})
            {
              if (!edge_stencils[d].is_empty ())
                brack.add_stencil (edge_stencils[d]);
            }

          mol.add_stencil (brack);
          mol.translate (points[LEFT]);
        }
    }
  return mol.smobbed_copy ();
}

I highlighted the relevant lines in bold face. These show that edge-text is only being used for TupletBrackets that connect to their neighbors (i.e. broken TupletBracket grobs), which can also be observed in the regression test input/regression/tuplet-broken.ly). I admit that this is not so clear from reading the description of edge-text in https://lilypond.org/doc/v2.23/Documentation/internals/tuplet_002dbracket_002dinterface ...

So in any case: The bracket itself gets drawn by Bracket::make_bracket, which has the following signature (found in lily/bracket.cc):

Stencil
Bracket::make_bracket (Grob *me, // for line properties.
                       Axis protrusion_axis, Offset dz, Drul_array<Real> height,
                       Interval gap, Drul_array<Real> flare,
                       Drul_array<Real> shorten)

So there's no parameter for thickness only for the side lines, and indeed line properties are taken from the grob itself for all three lines. Conclusion: There's no easy way to influence the way the bracket is drawn.

So we have to do something else. One brute-force possibility would be to just crack the finished stencil open and change the line width of the edge lines. If we look at the stencil _expression_ of a TupletBracket, using

\version "2.24.0"

{
  \override TupletBracket.stencil =
  #(grob-transformer 'stencil
                     (lambda (grob stil)
                     (pretty-print (ly:stencil-expr stil))
                     stil))

  \tuplet 3/2 { c'4 d' c''' }
}

we find:

(translate-stencil
  (1.1742119999999971 . 3.954403577019947)
  (combine-stencil
    (draw-line
      0.16
      5.465341561078519
      2.323245692493028
      5.465341561078519
      1.6232456924930279)
    (draw-line
      0.16
      -0.1840604182213724
      -0.07824169249302755
      -0.1840604182213724
      -0.7782416924930275)
    (draw-line
      0.16
      5.465341561078519
      2.323245692493028
      3.632732022820297
      1.544227186842694)
    (draw-line
      0.16
      -0.1840604182213724
      -0.07824169249302755
      1.8326095382582217
      0.7790185056503338)))

This looks as if the left and right line are the first two lines in the stencil (those have constant x coordinates). This aligns with the definition of make_bracket that adds the corner stencils first.

So we might do:

\version "2.24.0"

{
  \override TupletBracket.stencil =
  #(grob-transformer 'stencil
                     (lambda (grob stil)
                       (let*
                        ((expr (ly:stencil-expr stil))
                         (xext (ly:stencil-extent stil X))
                         (yext (ly:stencil-extent stil Y))
                         (lines (cdaddr expr))
                         (line1 (first lines))
                         (line2 (second lines)))
                        
                        (list-set! line1 1 0.5)
                        (list-set! line2 1 0.5)
                        
                        (ly:make-stencil expr xext yext))))

  \tuplet 3/2 { c'4 d' c''' }
  \tuplet 3/2 { c'4 d' e' }
}

But note that this is a) an dirty hack since it assumes that the TupletBracket stencil always comes in the form we expect, b) not yet ideal since the rounded lines still have to be moved down. At least the second issue can be remedied:

\version "2.24.0"

{
  \override TupletBracket.stencil =
  #(grob-transformer 'stencil
                     (lambda (grob stil)
                       (let*
                        ((expr (ly:stencil-expr stil))
                         (xext (ly:stencil-extent stil X))
                         (yext (ly:stencil-extent stil Y))
                         (lines (cdaddr expr))
                         (line1 (first lines))
                         (thick (second line1))
                         (new-thick 0.6)  ;; adjust at will
                         (offset (/ (- new-thick thick) 2))
                         (line2 (second lines)))
                        
                        (list-set! line1 1 new-thick)
                        (list-set! line2 1 new-thick)
                        
                        (list-set! line1 3 (- (list-ref line1 3) offset))
                        (list-set! line2 3 (- (list-ref line2 3) offset))

                        (ly:make-stencil expr xext yext))))

  \tuplet 3/2 { c'4 d' c''' }
  \tuplet 3/2 { c'4 d' e' }
}

Left to do: We probably also should modify the extents of the resulting stencil to avoid collisions...

Lukas


reply via email to

[Prev in Thread] Current Thread [Next in Thread]