What is does the total transformation of painted objects look like for QGraphicsItem?

189 views Asked by At

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 maping 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.

1

There are 1 answers

0
Daniel Donnelly On BEST ANSWER

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) with painter.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:

p = self.shape()
T = self.transform()   # Local transform doesn't include translation in scene
p = T.map(p)
# b = p + pen stroke applied
painter.save()
painter.setTransform(QTransform().fromTranslation(self.pos()))
painter.strokePath(p, pen)
painter.restore()

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!