Answer the question
In order to leave comments, you need to log in
How to implement scaling relative to an arbitrary point?
I am implementing a specialized 2D editor in .NET. I want to implement scaling relative to the cursor.
In GDI+, the Graphics class has a wonderful Transform property that allows me to implement what I need using the affine transformation apparatus in matrix form. In all books they write that in order to scale relative to an arbitrary point, it is necessary:
public class DrawingAreaControl : UserControl
{
public DrawingAreaControl()
{
DoubleBuffered = true;
transform = new Matrix();
}
public Matrix transform { get; set; }
protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
e.Graphics.Transform = transform;
base.OnPaint(e);
}
const float SCALE_MUL = 1.05f;
protected override void OnMouseWheel(MouseEventArgs e)
{
base.OnMouseWheel(e);
transform.Multiply(new Matrix(1, 0, 0, 1, e.Location.X, e.Location.Y));
transform.Multiply(e.Delta > 0 ?
new Matrix(SCALE_MUL, 0, 0, SCALE_MUL, 0, 0) :
new Matrix(1 / SCALE_MUL, 0, 0, 1 / SCALE_MUL, 0, 0));
transform.Multiply(new Matrix(1, 0, 0, 1, -e.Location.X, -e.Location.Y));
Invalidate();
}
}
Answer the question
In order to leave comments, you need to log in
I found the solution myself.
I carefully painted the matrices, manually multiplied them, got a ready-made scaling matrix relative to the cursor, substituted it into the program and everything works.
I began to look at what was wrong with my program.
It turned out that I misunderstood the order of matrix multiplication when applying the Multiply(Matrix) method. Fortunately, Multiply has an overloaded version with an arbitrary choice of operand order.
It should be like this:
public class DrawingAreaControl : UserControl
{
public DrawingAreaControl()
{
DoubleBuffered = true;
transform = new Matrix();
}
public Matrix transform { get; set; }
protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
e.Graphics.Transform = transform;
base.OnPaint(e);
}
const float SCALE_MUL = 1.05f;
protected override void OnMouseWheel(MouseEventArgs e)
{
base.OnMouseWheel(e);
var matrixOrder = MatrixOrder.Append;
var K = e.Delta > 0 ? SCALE_MUL : 1 / SCALE_MUL;
transform.Multiply(new Matrix(1, 0, 0, 1, -e.Location.X, -e.Location.Y), matrixOrder);
transform.Multiply(new Matrix(K, 0, 0, K, 0, 0), matrixOrder);
transform.Multiply(new Matrix(1, 0, 0, 1, e.Location.X, e.Location.Y), matrixOrder);
Invalidate();
}
}
I had the same task on js - here is the solution code (as is)
onScroll = ({dy, x, y}) => {
let border = this.props.border * this.state.containerWidth / 100;
let originalDeltaX = x - this.state.x ;
let originalDeltaY = y - this.state.y ;
let deltaX = originalDeltaX;
let deltaY = originalDeltaY;
let minWidth = this.state.containerWidth - border * 2;
let minHeight = this.state.containerHeight - border * 2;
let prevWidth = this.state.width;
this.state.width -= dy / 5;
this.state.height *= this.state.width / prevWidth;
deltaX *= this.state.width / prevWidth;
deltaY *= this.state.width / prevWidth;
let newX = this.state.x - deltaX + originalDeltaX;
if(this.state.width < minWidth) {
let scale = minWidth / this.state.width;
this.state.width *= scale;
this.state.height *= scale;
deltaX *= scale;
delta Y *= scale;
}
if(this.state.height < minHeight) {
let scale = minHeight / this.state.height;
this.state.width *= scale;
this.state.height *= scale;
deltaX *= scale;
delta Y *= scale;
}
let newY = this.state.y - deltaY + originalDeltaY;
if(newX > border) {
newX = border;
}
if(newY > border) {
newY = border;
}
if(newX + this.state.width < this.state.containerWidth - border) {
newX = this.state.containerWidth - border - this.state.width;
}
if(newY + this.state.height < this.state.containerHeight - border) {
newY = this.state.containerHeight - border - this.state.height;
}
this.setState({
width : this.state.width,
height : this.state.height,
x : newX,
y : newY
});
};
Didn't find what you were looking for?
Ask your questionAsk a Question
731 491 924 answers to any question