Let
painter.transform()
= P,
self.transform()
= L,
self.sceneTransform()
= S
self.parent().transform()
= P1
self.parent().parent().transform()
= P2 (and so on...)
When I draw a point without setting any transforms (not on painter
, not map
ing any of the shapes, and not on self
(except what is done in other methods that control movement, scaling, & rotation). Then how is this point, x mapped to its final position on the screen using the above matrix variables?
My goal is to understand this enough so that I can do this (at first, but I'm always doing QGraphicsItem stuff); which means I don't want to post a specific problem code (yet) since that won't solve my general lack of understanding necessarily:
Drawing a selection rectangle works with the correct pen look & size, but upon calling
self.setTransform(self.transform().scale(Sx, Sy))
, the object scales correctly but the selection rect scales with it! So if at the original scale there are 10 1x1 pixel dots making up a side of the selection rect, then upon scaling there are 10 Sx by Sy pixel dots making up a side and it doesn't look right.My solution intuitively is to scale the selection shape
QPainterPath
first (before painting), and do the painting of it in the identity scale. This seems like a simple idea but is almost impossible to make work without understanding how the transformations involved are applied.
So please show me the matrix multiplication formula that maps a painted point to the screen, without any transformation consideration during painting.
The situtation starts out like this:
I = identity
To = painter origin (upper left widget corner) to scene center (on screen) translation.
L = I
S = I
P = To
Object Movement
Tm = translation matrix of object in scene coordinates.
(Tm in standard direction (+y = down, +x = right)
Translation seems to be handled differently from below operations, since there is a
setPos(x,y)
func which has not relative transformation unless you invert the old translation and multipy that in as well. If T is the total matrix of a sequence of the below operations then S will be:S = T * Tm^-1
Similarly,
P = T * Tm
L = T
Object Scaling
Ts = scaling matrix found by
QTransform().fromScale(Sx, Sy)
L *= Ts
S *= Ts
P *= Ts
The result is applied by
self.setTransform(self.transform().scale(Sx, Sy))
.Object Rotation
Tr = rotation matrix found by
QTransform().fromRotation(R)
, where+R
means clockwise.L *= Tr
S *= Tr
P *= Tr
The result is applied by
self.setTransform(self.transform().rotate(R))
.Mutliplication is done in the reverse order of conventional linear algebra, so make sure all you
m12(), m21(), m13(), m31(), ...
calls are transposed.Thus painting a point
x = (x,y)
withpainter.drawPoint(*x)
without any extra matrix changes will result in x' = x T Tm.Using that as a model, the defaultly painted selection box b gets painted as *b' = b TTm**,
So I want to remove scaling and rotation before painting and apply those to the
QPainterPath
first. Based on our model, it seems like this should work:That doesn't work, but the problem still seems to be with our model of translation only. The scaling of the pen path works.
We forgout about To. Note that To is going to change around with QGraphicsView resizing.
So actually P = T ToTm = T TmTo. TmTo only touches
dx = m31(), dy = m32()
of the matrix so, what we want to do is instead of.fromTranslate(self.pos()))
, use.fromTranslate(m31, m32)
.Does it work?
Yes, that works!