|
From: | Urs Liska |
Subject: | Re: how offsets and alignment works: an explanation |
Date: | Sat, 23 Mar 2013 18:45:15 +0100 |
User-agent: | Mozilla/5.0 (Windows NT 6.1; WOW64; rv:17.0) Gecko/20130307 Thunderbird/17.0.4 |
Hi Janek,
thanks for these explanations. If the feedback doesn't give you a sufficiently clear idea where to put the material, you know what I'd suggest? ;-) Best Urs Am 23.03.2013 15:05, schrieb Janek Warchoł: Hi, here's an explanation of what's happening when we're using XY-offset, self-alignment etc. I hope that it'll help users get a better understanding of LilyPond internals; i also think it would be a good material for documentation, but i'm not sure where should it be placed - it seems to fit both in Learning as well as Extending, and maybe even CG... Let's start at the very beginning: LilyPond needs to know where each and every grob should be placed. This placement information is stored using relative coordinates: position of a grob is defined relative to the position of its parent (if we had used absolute coordinates - e.g. distances from the edges of the page - it would be hard to maintain spatial relationships between grobs). Every grob knows which grob is its parent in respective axis. For example, a Flag knows that its X-parent is a particular Stem. Relative coordinates that describe grob's placement are stored in grob properties called X-offset and Y-offset. They are measured in staffspaces. X-offset is the horizontal displacement between grob's reference point and the reference point of grob's X-parent (similarly with Y-offset). What is a reference point? It's a special point that defines the grob's position. Think about geometry: if you have to define where a figure is placed on a plane, you'll usually say something like "the lower left corner of this square has coordinates (0, 2)" "or "the center of this circle is at (-1, 3)". "Lower left corner" and "center" would be the reference points for square and circle. This illustration shows where refpoints of particular grobs are located: { \override NoteHead #'style = #'altdefault g'2-> c''\fermata | as'1^"Yogi" | b'\breve _"Larry" | \mark "Twinkle" e''8 s4. } -> see attached "refpoint examples.png" By overriding X-offset value, we can move grobs relative to their parents: { \override NoteHead #'style = #'altdefault \override Script #'X-offset = #2 \override TextScript #'X-offset = #1.5 \override Stem #'X-offset = #2 \override Score.RehearsalMark #'X-offset = #-2 g'2-> c''\fermata | as'1^"Yogi" | b'\breve _"Larry" | \mark "Twinkle" e''8 s4. } -> see attached "offsets.png" (notice that the Flag moved together with its Stem) Now, let's explain another pair of properties: X-extent and Y-extent. Each of them is a pair of numbers (an Interval), and they store grob's dimensions relative to its reference point. For example X-extent equal to (-1 . 4) means that the left edge of the grob is 1 staffspace to the left of its reference point, and right edge is 4 staffspace to the right from reference point, for a total width of 4 - (-1) = 4 + 1 = 5 staffspaces. Both numbers in an extent may be positive, for example (2 . 3) is a valid extent: it means that the whole grob is on the right of its refpoint, and the width of the grob is 3 - 2 = 1 staffspace. Similarly, both numbers can be negative; these situations are quite unusual but won't give LilyPond headaches. The most common situation (at least for X-extent) is that the first number is 0, which means that the reference point is on the left edge of the grob. Now, suppose that we want to position a RehearsalMark so that its right edge is aligned with a Barline. With X-offset = 0 (i.e. its reference point aligned on parent, which is equal to Barline in this situation) RehearsalMark would be placed like this: { \override Score.RehearsalMark #'X-offset = #0 b4 b b b \mark "Twinkle" b b b b } -> see image (1) in attached "images.png" So, we need to shift it. Remember what the second number in X-extent means? It's the position of grob's right edge relative to its refpoint. If we subtract this value from 0, we'll get the X-offset we want: { \override Score.RehearsalMark #'X-offset = #-10.4 b4 b b b \mark "Twinkle" b b b b } (2) What if we wanted to center some grob on the refpoint on its parent? That's simple: calculate the displacement between grob's refpoint and /center/ of it's extent. Some examples: 1) X-extent = (-2 . 2) -> X-offset = 0 - 0 = 0 2) X-extent = (0 . 4) -> X-offset = 0 - (0 + 0.5 * 4) = -2 3) X-extent = (-2 . 4) -> X-offset = 0 - ((0.5 * -2) + (0.5 * 4)) = 0 - (-1 + 2) = -1 There is a special C++ procedure x_aligned_on_self that can do these calculations for us; it's defined in Self_alignment_interface class. To use it, we say: \override GrobName #'X-offset = #ly:self-alignment-interface::x-aligned-on-self And it means "when you need to know a GrobName's X-offset, run x_aligned_on_self procedure form Self_alignment_interface C++ class on this RehearsalMark and take the value that the procedure returned". Then we need to specify what alignment we want: \override GrobName #'self-alignment-X = #RIGHT will tell the procedure to find the displacement between grob's refpoint and its right edge, and use it as X-offset, which will result in grob being right-aligned. So, if you change a grob's extent, you will affect how it will be aligned (because LilyPond will think that grob's dimensions are different): { \override Score.RehearsalMark #'X-extent = #'(0 . 8) \override Score.RehearsalMark #'self-alignment-X = #RIGHT b4 b b b \mark "Twinkle" b b b b } (3) Also, if a grob's extent is empty (i.e. it's not an Interval), procedures like x_aligned_on_self won't have any information about grob's dimensions, so they won't be able to calculate an offset (they'll just return 0). In other words, a grob with an empty extent can only be "aligned" on its refpoint, because there's no other information that can be used for alignment: { \override Score.RehearsalMark #'X-extent = ##f b4 b b b \mark "Twinkle" b b b b } (4) Notice that empty extent and zero extent result in the same positioning: { \override Score.RehearsalMark #'X-extent = #'(0 . 0) b4 b b b \mark "Twinkle" b b b b } (5) However, empty extent (or zero extent, or any other extent) doesn't prevent us from placing the grob at any location we want - we just can't use alignment procedures for that. We can still specify any offset we want, and it will work as usual: { \override Score.RehearsalMark #'X-extent = ##f \override Score.RehearsalMark #'X-offset = #-10.4 b4 b b b \mark "Twinkle" b b b b } (6) Now, there is one more thing to keep in mind: grob's parent has its own dimensions, too, and we need to take them into account. For example, if we write { a'1 } \addlyrics { \override LyricText #'X-offset = #ly:self-alignment-interface::x-aligned-on-self \override LyricText #'self-alignment-X = #CENTER nn } (7) the LyricText will be centered, but *on the refpoint of its parent* (i.e. the center of the syllable will be aligned to the refpoint of the notehead). If we want the center of the LyricText to be aligned with the center of its parent NoteHead, we have to use a different procedure: aligned_on_x_parent. It works very similarly to x_aligned_on_self, but in addition to calculating offset based on grob's own extent, it also uses grob's parent extent: { a'1 } \addlyrics { \override LyricText #'X-offset = #ly:self-alignment-interface::aligned-on-x-parent \override LyricText #'self-alignment-X = #CENTER nn } (8) I hope that this helps anyone, and that i got everything right. cheers, Janek |
[Prev in Thread] | Current Thread | [Next in Thread] |