Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Doc] Clarifications on the TRSSF canonical order #40

Open
xuy opened this issue Jan 29, 2022 · 0 comments
Open

[Doc] Clarifications on the TRSSF canonical order #40

xuy opened this issue Jan 29, 2022 · 0 comments

Comments

@xuy
Copy link

xuy commented Jan 29, 2022

In the wiki doc on shape adjustment, the doc showed an example

shape foo {
    bar [ x 1 y 2 rot 30 s 2 0.5 skew 10 0 flip 45 ]
}

and in the text below it explained that in this case the adjustment order is to translate first, then rotate, and so on.

I was a bit confused by the text, because if we translate bar first to (1, 2), a rotation would move it around the origin, not (1, 2). The same logic goes to sizing, where it would move to (2, 1).

Fundamentally, the confusion is on the order of applying the affine transformation. If the affine matrix for the translate operation is T, and the R for rotate, then the behavior of CF suggests T * R (shape), while the text says R * T(shape).

I dug around the source code to see how this behavior is implemented. The ASTmodification::makeCanonical function in astexpression.cpp would sort all transformations in canonical order, while ASTmodTerm::evaluate would then apply those transformations. The code would call m.m_transform.premultiply(T) where I believe m is the existing affine transformation and T is the shape transformation. When combined with the canonical order, I believe we are concatenating the transformation matrix as

M * T * R * S * S * F (shape)

where M is the existing/external transformation.

While the order of concatenating the shape transformations is indeed TRSSF, when we do it step by step, we do need to apply the transformations in the reverse order, i.e. F S S R T.

I constructed the following example to illustrate the behavior.

startshape board

m = 5

shape board {
    grid []
    // marks the origin
    SQUARE [b 0.6]
    // If the applying order is TRSSF, then the R here would be no-op on SQUARE
    // However the r is applied to turn a 3x1 bar to 1x3.
    // Indicating R is applied after S.
    SQUARE[x 2 1 s 3 1 r 90]
}

// Draw a horizontal line y = k
path yline(number k)
{
    MOVETO(-m, k)
    LINETO(m, k)
    STROKE(0.01)[]
}

// Draw a vertical line x = k
path xline(number k)
{
    MOVETO(k, -m)
    LINETO(k, m)
    STROKE(0.01)[]
}

shape grid
{
    loop i = -5, 6, 1 [] xline(i) []
    loop i = -5, 6, 1 [] yline(i) []
}

Would like to use your help to clarify if my understanding is correct.

I am happy to send over a small PR to update the wiki.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant