Reputation: 23
Im trying to make a Column scale/movable. But how can I set a max zoom and a min zoom? So that you don´t zoom to infinity.
Using this method right now:
Matrix4 matrix = Matrix4.identity();
MatrixGestureDetector(
shouldRotate: false
onMatrixUpdate: (Matrix4 m, Matrix4 tm, Matrix4 sm, Matrix4 rm) {
setState(() {
matrix = m;
});
},
child: Transform(
transform: matrix,
child: Column(
),
),
Upvotes: 1
Views: 3370
Reputation: 21684
Update: As @rdnobrega kindly explained, this is not an ideal solution by any means. It will only work when the only transformed Matrix is scale, and will break with other transformations. I am no math expert or Matrix4 expert, so the solution below is at your own risk.
I had the same problem and took a while but came across the solution.
After digging around just a bit in Flutter's Transform.scale
source reveals this line which gives us a hint:
transform = Matrix4.diagonal3Values(scale, scale, 1.0)
It's using the diagonal values from the Matrix4
you receive in onMatrixUpdate
. So it takes x
from the 1st Vector4, y
from the 2nd, and z
from the 3rd. (the 4th is fixed from what I can tell). So those are the values you need to limit. In this example I've made a small _minMax
method which bounds the scale to the relevant min/max when relevant (they can be passed null
to ignore either side of the limit).
I used this to limit the scale:
typedef MathF<T extends num> = T Function(T, T);
typedef VFn = Vector4 Function(double x, double y, double z, double w);
double _minMax(num _min, num _max, num actual) {
if (_min == null && _max == null) {
return actual.toDouble();
}
if (_min == null) {
return min(_max.toDouble(), actual.toDouble());
}
if (_max == null) {
return max(_min.toDouble(), actual.toDouble());
}
return min(_max.toDouble(), max(_min.toDouble(), actual.toDouble()));
}
// ... ... ...
onMatrixUpdate: (Matrix4 m, Matrix4 tm, Matrix4 sm, Matrix4 rm) {
var finalM = Matrix4.copy(m);
Map<int, VFn> colmap = {
0: (x, y, z, w) {
x = _minMax(widget.minScale, widget.maxScale, x);
return Vector4(x, y, z, w);
},
1: (x, y, z, w) {
y = _minMax(widget.minScale, widget.maxScale, y);
return Vector4(x, y, z, w);
},
2: (x, y, z, w) {
z = _minMax(widget.minScale, widget.maxScale, z);
return Vector4(x, y, z, w);
},
};
for (var col in colmap.keys) {
var oldCol = m.getColumn(col);
var colD = colmap[col];
if (colD != null) {
finalM.setColumn(col, colD(oldCol.x, oldCol.y, oldCol.z, oldCol.w));
}
}
setState(() {
matrix = finalM;
});
},
Upvotes: 4
Reputation: 789
Just to complement @casraf's answer.
The coordinate system used by Flutter (and other graphic languages such as OpenGL) is the homogeous coordinates. This is just a fancy name for a helper dimension that exists in these Matrices for perspective calculations, like when a thing is closer it gets bigger, if it is far it gets smaller.
So, in these coordinates, unlike cartesian, we need one extra dimension. If we are trying to represent 3 dimensions of space in perspective, we need a 4-dimension matrix.
For curiosity only, isometric games and 2d-like 3d games actually mess with this extra W dimension (4th row of the matrix) for applying no perspective at all.
That said, Flutter/openGL does the calculation of final vertices in the screen multiplying your raw coordinate in the form of a 1-line matrix (x, y, z, 1) by a transformation matrix Matrix4. If you do the math, the result still is a 1-line matrix, but translated/rotated/scaled/skewed from the original vertex, by the matrix.
That means a bulky matrix multiplication behind the scenes, and altering specific coefficients manually may lead to unexpected behaviors. In @casraf's case, it works for there is no translation nor rotation involved, so the changed coefficients are equal to the scale coefficients. If you apply any other transformation, the result may vary a lot.
TL;DR
Always use the proper built-in methods of Matrix4 to do the transformations, unless you really know what you are doing.
Also, keep in mind that the order of transformations matter. Doing a translation then a rotation is diferent from doing rotation -> translation.
Upvotes: 2