|
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,
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
[Prev in Thread] | Current Thread | [Next in Thread] |