Can someone help me out to understand the behaviour of the scale function inside of a grid.
The scale function has three parameters, which affect the scaling of the body: A positional parameter factor and two named parameters x and y - all of which can either be a length, a ratio or auto.
I thought, that the factor scales the body symmetrically in x- and y-direction, but this isn’t always the case. For illustration see the following example code + screenshot
Why is the “square” in row 2 rectangular and not square?
Why is the square in row 3 not 5mm x 5mm?
Why is the factor in row 7 ignored and width and height are different?
What is going on in row 8?
Why is the square height in row 10 and 11 larger than the row height even though the y parameter is 100%?
And in general
There are 3 parameters for scaling in 2 dimensions. What is the overall logic if different combinations of auto, ratio and length parameters are given?
What is the precedence if all three parameters are specified?
How does the auto keyword work in conjunction with the other parameters?
There are 3 parameters for scaling in 2 dimensions. What is the overall logic if different combinations of auto, ratio and length parameters are given?
The factor is just an optional shorthand notation for setting x and y to the same value.
In factor, the factor field is marked as #[external]. It means that “it appears in the documentation, but is otherwise ignored”, and it’s added only “to do something manually for more flexibility”.
Implementation details
I cannot fully understand its behaviour either, but it looks like the logic is here:
/// Resolves scale parameters, preserving aspect ratio if one of the scales
/// is set to `auto`.
fn resolve_scale(
elem: &Packed<ScaleElem>,
engine: &mut Engine,
locator: Locator,
container: Size,
styles: StyleChain,
) -> SourceResult<Axes<Ratio>> {
Why is the square in row 3 not 5mm x 5mm?
row 3: scale(sz, x: auto, sq)
auto means to preserve the aspect ratio.
height: 7.5 mm, the same as row 2.
width: 15 mm * 50% = 7.5 mm, as implied by x: auto, which means the same ratio in y axis.
Why is the factor in row 7 ignored and width and height are different?
row 7: scale(sz, x: 100%, sq)
The precedence of factor, x, y is:
Resolved x = first value that is not none in the list (x, factor, 100%).
Resolved y = first value that is not none in the list (y, factor, 100%).
Therefore,
width: 15 mm * 100% = 15 mm, as specified by x: 100% — it’s relative to the original size of the content.
height: 7.5 mm, the same as row 2.
What is going on in row 8?
row 8: scale(sz, x:1.5 * sz, y: 1.1 * sz, sq)
As explained in row 7, the resolved x is 1.5 * sz, and resolved y is 1.1 * sz.
width: 1.5 * 5 mm = 7.5 mm, as specified by x: 1.5 * sz.
height: 15mm * ((1.1 * 5mm) / 10mm), as specified by y: 1.1 * sz and similar to row 2.
Why is the square height in row 10 and 12 larger than the row height even though the y parameter is 100%?
row 10: scale(sz, y: 100%, sq)
row 12: scale(sz, x: 100%, y: 100%, sq)
ratios are relative to the original size of the content, not available region.
Row 10:
width: 5 mm, as specified.
height: 15 mm * 100% = 15 mm, as specified.
Row 12:
width: 15 mm * 100% = 15 mm, as specified.
height: 15 mm * 100% = 15 mm, as specified.
Answer to general questions
Resolve (factor, x, y) into (x, y).
Resolved x = first value that is not none in the list (x, factor, 100%).
Resolved y = first value that is not none in the list (y, factor, 100%).
Here factor, x, y can be auto | length | ratio.
Convert auto | length | ratio to ratio.
length → ratio: As explained in row 2, length ↦ length / min(available-size, content-size).
auto → ratio: Use the ratio in the other axis, keeping the aspect ratio.
Thanks for your detailed analysis. I learned a lot
overall logic
The factor is just an optional shorthand notation for setting x and y to the same value.
To make it more explicit:
If factor is given, then both named parameters x and y are set to that value. If later also named parameters are given, then those overwrite the previously set values. If a named parameter is auto, then scaling is according to the other parameter and the dimension of the auto parameter is scaled such that the original aspect ratio is preserved.
If the type of a parameter is a ratio then scaling is performed relative to the original content size and not relative to the available space.
The default for both x and y parameters is 100%.
Strange things can happen, if the unscaled (sic!) content does not fit into the available space.
implementation details
While I am happy to read code, rust is a different kind of beast …
This
let scale-ratio = scale-abs / calc.min(available-height, content-height)