Page 1 of 2

Is there a way to get current transformation?

Posted: Wed Aug 10, 2016 7:33 am
by AnRu
I would like to get current position, rotation and scaling values of graphics transform, caused by nested transformations

Code: Select all

function drawObject(object)
	love.graphics.push()
	love.graphics.translate(...)
	love.graphics.rotate(...)
	love.graphics.scale(...)
	
	-- I want to do something like this:
	globalPos, globalRot, globalScale = love.graphics.currentTransformation() 
	
	for k, v in pairs(object.children) do
		drawObject(v)
	end	
	love.graphics.pop()
end
Is there a way to achieve this?

Re: Is there a way to get current transformation?

Posted: Wed Aug 10, 2016 8:10 am
by Plu
I asked the same question recently (viewtopic.php?f=4&t=82545&hilit=translation) and the answer seems to be "no". :(

Re: Is there a way to get current transformation?

Posted: Wed Aug 10, 2016 8:36 am
by 4aiman
Uhmm...
When you call translate with some args you should know what args you're using, right?
So use those args and get what you want.
The same goes for "scale" and "rotate".

Or maybe...
You haven't hardcoded the arguments, have you?

Re: Is there a way to get current transformation?

Posted: Wed Aug 10, 2016 9:08 am
by zorg
4aiman wrote:Uhmm...
When you call translate with some args you should know what args you're using, right?
So use those args and get what you want.
The same goes for "scale" and "rotate".

Or maybe...
You haven't hardcoded the arguments, have you?
Doesn't matter whether the args are hardcoded or not, what matters is that, as you said, if he wants to use them later, he needs to store them somewhere.

You could monkeypatch the love.graphics coordsys manipulating functions to update a matrix you handle on the lua side, and have a neat function as you wrote, to get back those values, but i don't know why you would want to know this. (Or, i don't know the reason why, but i do believe löve uses such a matrix internally anyway, one could "beg" for a function to return those values... you'll need to work out the separate translation, rotation, scale, skew values yourself... if you need those, i mean.)



Two major paths when drawing stuff:

- You have the unmodified transform state (gettable by l.g.origin), because you want to draw "absolutely", or relative to the window itself.
- You have the modified transform state, because you want to draw "relatively" to that modified system.

Neither need you to get the current transformation, and although there may be other uses, i can't think of one myself.
...Also, only i replied to the other thread as well? I'm sad since graphics isn't exactly my forté either :P

Re: Is there a way to get current transformation?

Posted: Wed Aug 10, 2016 10:42 am
by pgimeno
zorg wrote:one could "beg" for a function to return those values...
And set them, please, to not have to deal with dirty tricks such as SVD which very few people even know about.

Re: Is there a way to get current transformation?

Posted: Wed Aug 10, 2016 10:53 am
by AnRu
The reason i ask that is that i need to get global position of an child object in the tree
I have no problems with storing positions, but what about rotation? If i rotate parent, childs will rotate with it and a have rotated coordinates. How get this changed coords?

Re: Is there a way to get current transformation?

Posted: Wed Aug 10, 2016 11:36 am
by 4aiman
AnRu wrote:The reason i ask that is that i need to get global position of an child object in the tree
I bet the real question should be not "How to get X" but "Why do I need X?"
Chances are you're doing something counter-productive or over-engineering things.
AnRu wrote:but what about rotation? If i rotate parent, childs will rotate with it and a have rotated coordinates.
In the case you're dealing with something most of people haven't heard of, see Wikipedia: https://en.wikipedia.org/wiki/Rotation_(mathematics)

Re: Is there a way to get current transformation?

Posted: Wed Aug 10, 2016 11:45 am
by airstruck
If you don't have control over all transformations that may be happening, the monkeypatching thing zorg mentioned will work. There's an example of that in box2dhelper (I don't remember where exactly, but I'm sure I saw it there).

If you do have control over the transformations (at least the ones you care about), it should be pretty easy to reverse them to convert between world and screen coordinates. There's an example of that here.

Re: Is there a way to get current transformation?

Posted: Wed Aug 10, 2016 11:53 am
by AnRu
Thank you all for replies :)
I\ll try to write own transform system

Re: Is there a way to get current transformation?

Posted: Wed Aug 10, 2016 1:52 pm
by pgimeno
You can always use a wrapper that keeps track of the matrix.

Code: Select all

local lgorigin = love.graphics.origin
local lgscale = love.graphics.scale
local lgrotate = love.graphics.rotate
local lgshear = love.graphics.shear
local lgtranslate = love.graphics.translate

-- The implicit third row is always 0, 0, 1
local matrix = {1,0,0,
                0,1,0}

local function getMatrix(t)
  if t == nil then
    -- Return a copy
    return {matrix[ 1], matrix[ 2], matrix[ 3],
            matrix[ 4], matrix[ 5], matrix[ 6]}
  end
  -- Assign the elements (enables reusing tables)
  -- (unrolled loop)
  t[1] = matrix[1]; t[2] = matrix[2]; t[3] = matrix[3]
  t[4] = matrix[4]; t[5] = matrix[5]; t[6] = matrix[6]
  return t
end

local function origin()
  matrix[1] = 1  matrix[2] = 0  matrix[3] = 0
  matrix[4] = 0  matrix[5] = 1  matrix[6] = 0
  lgorigin()
end

local function scale(x, y)
  matrix[1] = matrix[1] * x; matrix[2] = matrix[2] * y
  matrix[4] = matrix[4] * x; matrix[5] = matrix[5] * y
  lgscale(x, y)
end

local function rotate(a)
  local c, s = math.cos(a), math.sin(a)
  matrix[1], matrix[2] = matrix[1]*c + matrix[2]*s, matrix[1]*-s + matrix[2]*c
  matrix[4], matrix[5] = matrix[4]*c + matrix[5]*s, matrix[4]*-s + matrix[5]*c
  lgrotate(a)
end

local function shear(x, y)
  matrix[1], matrix[2] = matrix[1] + matrix[2]*y, matrix[1]*x + matrix[2]
  matrix[4], matrix[5] = matrix[4] + matrix[5]*y, matrix[4]*x + matrix[5]
  lgshear(x, y)
end

local function translate(x, y)
  matrix[3] = matrix[3] + matrix[1]*x + matrix[2]*y
  matrix[6] = matrix[6] + matrix[4]*x + matrix[5]*y
  lgtranslate(x, y)
end

local function xform(matrix, x, y)
  return matrix[1]*x + matrix[2]*y + matrix[3], matrix[4]*x + matrix[5]*y + matrix[6]
end

local function xformBack(matrix, x, y)
  x, y = x - matrix[3], y - matrix[6]
  local det = matrix[1] * matrix[5] - matrix[2] * matrix[4]
  if det ~= 0 then
    return (matrix[5] * x - matrix[2] * y) / det,
           (matrix[1] * y - matrix[4] * x) / det
  end
  -- The transform is 1D or 0D
  return x, y
end

return {getMatrix=getMatrix, xform = xform, xformBack = xformBack, origin=origin,
        scale=scale, rotate=rotate, shear=shear, translate=translate}
Inverting the matrix is left as an exercise to the reader ;)

Removed the unnecessary generic matrix calculations from the above, leaving only the strictly necessary part, and used a 2D matrix to add the backward transform, now called xformBack.

Example:

Code: Select all

local xform = require('xform')
local matrix

function love.draw()
  xform.origin()
  love.graphics.setColor(255,255,255)
  xform.translate(400,300)
  xform.rotate(0.3)
  xform.scale(0.7, 1.1)
  xform.translate(42, 86)
  xform.rotate(0.5)
  xform.shear(1.7, 1.3)
  matrix = xform.getMatrix()
  love.graphics.rectangle("fill", 5, 9, 53, 31)

  xform.origin()
  love.graphics.setColor(255,0,0)
  local x, y = xform.xform(matrix, 5, 9)
  love.graphics.rectangle("fill", x-1, y-1, 3, 3)
  x, y = xform.xform(matrix, 5+52, 9)
  love.graphics.rectangle("fill", x-1, y-1, 3, 3)
  x, y = xform.xform(matrix, 5, 9+30)
  love.graphics.rectangle("fill", x-1, y-1, 3, 3)
  x, y = xform.xform(matrix, 5+52, 9+30)
  love.graphics.rectangle("fill", x-1, y-1, 3, 3)

  x, y = xform.xformBack(matrix, xform.xform(matrix, 5, 3))
  love.graphics.print(string.format("%.17g\t%.17g\t%s", x, y, "<- should be 5, 3"))
end
Remember to always call xform.origin() at the beginning of love.draw() to reset the matrix, because the default love.run calls love.graphics.origin() internally right before calling love.draw().

Edit: Modified so you can also do this hack if you want:

Code: Select all

local xform = require 'xform'

do
  local lg = love.graphics
  lg.translate = xform.translate
  lg.rotate = xform.rotate
  lg.scale = xform.scale
  lg.shear = xform.shear
  lg.origin = xform.origin
end
so that your program can access any transformation function transparently, and you don't need to call xform.origin() manually at the start.

Edit2: Simplified and added backwards transform.