Having done a lot of work with matrices in Gideros recently, during a discussion at the Gideros forums it occurred to me that with the Matrix class that I recently covered, I could possibly address an oft-requested feature of Gideros. The problem is that in Gideros, all Sprite objects have an anchor point of [0,0] that you can't change. So all transformations such as rotation and scaling are relative to the top left point of the Sprite. The one exception to this is with Bitmap objects, where there is a `setAnchorPoint()`

method. But this doesn't help for other Sprite based objects, such as TextFields or Shapes.

So I whipped together a class called AnchorSprite, which when added to your project, adds a `setAnchorPoint()`

and `getAnchorPoint()`

method to every Sprite object and all objects that inherit from Sprite. Further, bowerandy had the great idea to make this support completely inactive until you set an anchor, that way you could avoid any overhead on sprites that still had the default anchor point.

So I'm going to explain a little bit about how this class works. I'm going to skip over most of the matrix stuff since I already covered that in detail.

### Intercepting Sprite transform methods

The first step is to intercept all calls to transform methods of the `Sprite`

class. This includes `setPosition()`

, `setScale()`

, `setRotation()`

as well as all the other specific ones, `setX()`

, `setScaleY()`

, etc. We also need to intercept `set()`

itself. We have to do this, because we don't want the Sprite class to actually change anything. Instead, we just take the desired changes and save them in private member variables. For example, here is how we intercept `set()`

.

```
-- Intercept Sprite's set and get functions for position, scale and rotation
function AnchorSprite:set(key, value)
if value then
if key == "x" then
self._positionX = value
elseif key == "y" then
self._positionY = value
elseif key == "scaleX" then
self._scaleX = value
elseif key == "scaleY" then
self._scaleY = value
elseif key == "rotation" then
self._rotation = value
else
self._anchorBackup.set(self, key, value)
end
self:_applyTransforms()
end
end
```

You'll notice that this is in the `AnchorSprite`

class and not the `Sprite`

class just yet. We don't want to intercept it until an anchor point is set, which I'll show in a bit.

So just to show another example, this is what our intercepted `setPosition()`

method will look like.

```
function AnchorSprite:setPosition(x, y)
self:set("x", x)
self:set("y", y)
end
```

### Applying the transforms

The thing to notice is that we're not doing anything with the values being passed in, just storing them in private member variables. This is because we need to basically keep a stack of transforms in memory, so that we can apply them all whenever any of them change. This is done in the `_applyTransforms`

method, which is called every time `set()`

is called.

```
-- New function to apply all transforms whenever anything changes
function AnchorSprite:_applyTransforms()
```

This function will do all the calculations needed to apply the transforms. It starts from scratch with a brand new Matrix.

```
-- Create a new identity matrix
local matrix = Matrix.new()
```

We then apply that new matrix to the sprite, thus resetting it back to completely default location, rotation and scale. This is so we can get an accurate width and height, so we know how to apply the anchor, which is specified as a percentage of the dimensions of the Sprite. We could forgo this and just have the anchor set as a specific pixel location, but I chose to do it this way to mirror the way that the `Bitmap:setAnchorPoint()`

works. Plus it means you can change the dimensions of the `Sprite`

and always know that the anchor point will be in the place you expect without having to calculate the anchor point yourself.

```
-- Zero ourselves out so we can get accurate width and height
self:setMatrix(matrix)
-- Calculate the actual position of the anchor
local anchorOffsetX = self._anchorPointX * self:getWidth()
local anchorOffsetY = self._anchorPointY * self:getHeight()
```

The next set of operations apply our transformations to the Matrix, effectively concatenating them. They should all be pretty self explanatory if you've read my matrix overview. In short, each is in turn concatenated onto the matrix much like characters can be concatenated together to form a string. First we set the position, then rotation and scale.

```
-- set position
matrix:translate(self._positionX, self._positionY)
-- concatenate rotation and scale matrices
matrix:rotate(self._rotation)
matrix:scaleX(self._scaleX)
matrix:scaleY(self._scaleY)
```

And finally the magic part, which is to offset by the negative of the anchor point.

```
-- concatenate offset to new origin in modified coordinate space
matrix:translate(-anchorOffsetX, -anchorOffsetY)
```

Since we concatenate the anchor point translate last, it is applied to the new coordinate space that is a result of the previous concatenations, and not based on the original coordinate space. Thus if you have an anchor point of [.5,.5] and rotate clockwise 45 degrees, the last translate will move the whole sprite along that 45 degree angle, or basically straight up. If you were to try this with the original Sprite:setPosition, it would move based on the original coordinate space, and so end up moving the sprite up and to the left, which is not what you wanted.

Lastly we apply our built-up matrix with `setMatrix()`

, thus causing all the transforms to get applied. This will affect the current sprite as well as any children in the scene graph.

```
-- Apply the new matrix
self:setMatrix(matrix)
```

This is our finished `_applyTransforms()`

function.

```
-- New function to apply all transforms whenever anything changes
function AnchorSprite:_applyTransforms()
-- Create a new identity matrix
local matrix = Matrix.new()
-- Zero ourselves out so we can get accurate width and height
self:setMatrix(matrix)
-- Calculate the actual position of the anchor
local anchorOffsetX = self._anchorPointX * self:getWidth()
local anchorOffsetY = self._anchorPointY * self:getHeight()
-- set position
matrix:translate(self._positionX, self._positionY)
-- concatenate rotation and scale matrices
matrix:rotate(self._rotation)
matrix:scaleX(self._scaleX)
matrix:scaleY(self._scaleY)
-- concatenate offset to new origin in modified coordinate space
matrix:translate(-anchorOffsetX, -anchorOffsetY)
-- Apply the new matrix
self:setMatrix(matrix)
end
```

### Optimization

So the last bit is to make this code only get put into place when an anchor point is set. This way, after you add `AnchorPoint.lua`

to your project, it doesn't do anything to any `Sprite`

s by default. Only those that you set an anchor point for with `setAnchorPoint`

will start to intercept the transform methods, thus resulting in less overhead. This is why our functions so far have been in the `AnchorSprite`

class. First step is to add the `setAnchorPoint()`

method to the `Sprite`

class along with our private member variables for storing the transforms.

```
Sprite._anchorSupport = false
Sprite._anchorBackup = {}
Sprite._anchorPointX = 0
Sprite._anchorPointY = 0
Sprite._positionX = 0
Sprite._positionY = 0
Sprite._scaleX = 1
Sprite._scaleY = 1
Sprite._rotation = 0
-- New functions in Sprite for setting the anchor
function Sprite:setAnchorPoint(x, y)
self._anchorPointX = x
self._anchorPointY = y or x
if x ~= 0 or y ~= 0 and not self._anchorSupport then
self:_enableAnchorSupport()
elseif x == 0 and y == 0 and self._anchorSupport then
self:_disableAnchorSupport()
end
end
```

As you can see, this mainly calls a function called `_enableAnchorSupport()`

that does all the work of modifying the Sprite class accordingly.

```
function Sprite:_enableAnchorSupport()
self._anchorSupport = true
-- Get current values
self._positionX = self:getX()
self._positionY = self:getY()
self._scaleX = self:getScaleX()
self._scaleY = self:getScaleY()
self._rotation = self:getRotation()
-- Override Sprite functions with AnchorSprite versions
for k,v in pairs(AnchorSprite) do
if type(v) == "function" then
--print("AnchorSprite", k, v)
if self[k] then
-- Backup existing function
self._anchorBackup[k] = Sprite[k]
end
-- Replace with AnchorSprite version
self[k] = v
end
end
self:_applyTransforms()
end
```

This initializes our transform variables, then does a bit of magic of modifying the `Sprite`

class itself. If you're unfamiliar with how Lua works, the thing to remember is that almost everything in Lua is a table. All classes are just a table, and values of that table can be variables or references to functions. So after backing up the original reference in `_anchorBackup`

(a table), we just replace the reference in `Sprite`

's table with the one in `AnchorSprites`

table. So now functions like `AnchorSprite:_applyTransforms`

from earlier are now `Sprite:_applyTransforms`

. The methods in `AnchorSprite`

are never meant to be called while they're in `AnchorSprite`

, and would actually throw weird errors. We make backups so that if the anchor is reset to [0,0], we can called `_disableAnchorSupport()`

and return `Sprite`

to the way it was.

### Result

In the end, the result is that to the user, all we've done is added `Sprite:setAnchorPoint()`

and `Sprite:getAnchorPoint()`

, and everything just works. A sample project is available to see how it can be implemented and to show the results. To use it, simply include AnchorSprite.lua and Matrix.lua into your project.

Of course, this is hopefully just a stopgap solution until Gideros builds this functionality in. Until then, this is one way to get around the limitation. Let me know if you find it useful.

## Comments

comments powered by Disqus