#### Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

# Problem with tweening color

edited February 2013 in General Posts: 26

Am I trying to do something incorrect?
Also noticed the color() selector doesn't occur on the "tween(2, tweentest, {fillColor = color(0, 233, 233, 255)})" line.

``````-- tween test

-- Use this function to perform your initial setup
function setup()

tweentest = {x = 100,y = 100, fillColor = color(206, 33, 33, 255), red = 206, green =33, blue = 33, alpha = 255}

-- uncommenting the following line causes an error.
--tween(2, tweentest, {fillColor = color(0, 233, 233, 255)})

tween(2, tweentest, {red = 0, green = 233, blue = 255, alpha = 255},{loop = tween.loop.pingpong})
tween(2,tweentest,{x = 600, y =300},{easing = tween.easing.linear, loop = tween.loop.pingpong})

end

-- This function gets called once every frame
function draw()
-- This sets a dark background color
background(19, 19, 187, 255)
stroke(255)

fill(tweentest.fillColor)

-- the following fill works
--fill(tweentest.red,tweentest.green,tweentest.blue,tweentest.alpha)
-- This sets the line thickness
strokeWidth(5)

ellipse(tweentest.x,tweentest.y,100)
end
``````

• edited February 2013 Posts: 5,387

The reason this doesn't work is because the `color` type does not provide arithmetic operators. So for example:

``````myColor1 = color(0,0,100)
myColor2 = color(100,100,100)
myColor3 = myColor1 + myColor2
``````

Does not work.

This is not that obvious and perhaps we should look at adding arithmetic operators to color.

For the moment, simply change your `fillColor` to a `vec4` type instead, and then set the fill as follows:

``````fill( tweentest.fillColor.x, tweentest.fillColor.y, tweentest.fillColor.z )
``````
• Posts: 26

Ok I understand. Color tweening is shown in the box example for tween in the reference. So that's why I was trying it out.

• Posts: 5,387

.@Reyals123 sorry about that, that's a mistake on our part. We'll add arithmetic operators to color to allow tween to work with it.

• Posts: 26

I can work around that easily enough I just thought I was screwing something up and just wasn't seeing the problem.

• Posts: 622

The Tween library was originally designed to work on table properties (specifically numbers), an alternative is to do the following:

``````object = {fillColor = color(255,255,255,255)}
tween(2, object.fillColor, {r = 255, g = 0, b = 255})
...
fill(object.fillColor)
``````
• Posts: 26

Thanks John, the reference under tween needs to have your code snippet as the example for color.

• Posts: 29

Hi! I’m just flagging this up again, as I’ve been trying to tween colours, couldn’t get it to work (getting weird results) and found this post. Wondering if this is till the problem?

• Posts: 8,464

@pjholden Try this.

``````supportedOrientations(LANDSCAPE_ANY)
displayMode(FULLSCREEN)

function setup()
rectMode(CENTER)
fontSize(50)
c={r=0,g=255,b=0}
end

function draw()
background(0)
fill(c.r,c.g,c.b)
text("Tap screen to start the color change",WIDTH/2,HEIGHT-100)
rect(WIDTH/2,HEIGHT/2,200,200)
end

function touched(t)
if t.state==BEGAN then
t2=tween(5,c,{r=255,g=0},{loop=tween.loop.pingpong})
end
end
``````
• Posts: 29

Cheers dave, i’d Already worked around it (more or less doing what you’ve suggested) just wondering if, as Simeon said upthread (in 2013!) a fix ever came up for it (I’m assuming it never did)

• Posts: 490

@pjholden What problems are you seeing? I just tried the following and saw what I expected to see (a rectangle fading in to purple).

``````function setup()
t = { colour = color(40,40,50)}
tween(5,t, {colour = color(255,0,255)})
end

function draw()
background(40,40,50)
fill(t.colour)
rect(WIDTH/2,HEIGHT/2,100)
end
``````
• edited October 2017 Posts: 8,464

@LoopSpace It doesn’t work, it just looked like it did. Red should go from 40 to 255, green from 40 to 0, and blue from 50 to 255. Run the code below and watch the r,g,b values.

This doesn’t work.

``````function setup()
parameter.watch("t.colour.r")
parameter.watch("t.colour.g")
parameter.watch("t.colour.b")

t = { colour = color(40,40,50)}
tween(5,t, {colour = color(255,0,255)})
end

function draw()
background(40,40,50)
fill(t.colour)
rect(WIDTH/2,HEIGHT/2,100)
end
``````

This does work.

``````function setup()
parameter.watch("t.r")
parameter.watch("t.g")
parameter.watch("t.b")

t = { r=40,g=40,b=50}
tween(5,t, {r=255,g=0,b=255})
end

function draw()
background(40,40,50)
fill(t.r,t.g,t.b)
rect(WIDTH/2,HEIGHT/2,100)
end
``````
• Posts: 29

I usually notice the alpha values, they go particularly screwy.

As it is, I ended up seperati g out RGBA values in to their components, so I could tween them and corners of a rectangle as part of a single tween (mostly so I could hold the tween’s ID in a single variable which I then reset to zero after it’s finished tweeting, so I can easily check if the thing is animating or not)

``````self.animating = tween( .7, self,
{ focusX = self.panels[self.currentPanel].x,
focusY = self.panels[self.currentPanel].y,
focusW = self.panels[self.currentPanel].w,
focusH = self.panels[self.currentPanel].h,
focusZoom = zoom,
focusBGR = self.panels[self.currentPanel].bg.r,
focusBGG = self.panels[self.currentPanel].bg.g,
focusBGB = self.panels[self.currentPanel].bg.b,
focusBGA = self.panels[self.currentPanel].bg.a
},
tween.easing.In,
function()
self.animating = false
end
)

``````
• Posts: 490

Interesting. It appears that the arithmetic operators on colours are clipped to the 0-255 range. That shouldn't be a problem as tweening is affine, except that the implementation of tweening converts the natural affine relationship into a linear one and that takes colours out of the 0-255 range temporarily.

The proper fix would be to change `tween.lua` in the following ways:

``````local function easeWithTween(self, subject, target, initial)
local t,b,c,d

for k,v in pairs(target) do
if type(v)=='table' then
easeWithTween(self, subject[k], v, initial[k])
else
t,b,c,d = self.running, initial[k], v, self.time -- third argument changed from v - initial[k]

if self.loop then
t = self.loop(t,d)
end

subject[k] = self.easing(t,b,c,d)
end
end
end
``````

and then the easing functions need to be changed, eg for the linear one:

``````local function linear(t, b, c, d) return c * (t / d) + (1 - t / d) * b end
``````

If the second colour is greater than the first, (ie all components of the target colour are bigger than or equal to the corresponding components of the initial colour) then a simple solution is to pass an easing function with correct bracketting. Using my earlier code:

``````tween(5,t, {colour = color(255,0,255)}, function(t,b,c,d) return (t/d)*c + b end)
``````

Lastly, an option is to modify the arithmetic operators on the color userdata to remove the clipping:

``````local mt = getmetatable(color())

return color(c1.r+c2.r, c1.g+c2.g, c1.b+c2.b, c1.a+c2.a)
end

mt.__sub = function(c1,c2)
return color(c1.r-c2.r, c1.g-c2.g, c1.b-c2.b, c1.a-c2.a)
end

mt.__mul = function(c1,c2)
if type(c2) == "number" then
c1,c2 = c2,c1
end
return color(c1*c2.r, c1*c2.g, c1*c2.b, c1*c2.a)
end

mt.__div = function(c1,c2)
return color(c1.r/c2, c1.g/c2, c1.b/c2, c1.a/c2)
end
``````

This has the advantage that it can be used with Codea as-is.
Though of course, this might break other things that rely on colours having their values clipped.