tweening color values

I’m having some difficulty figuring out how to tween some colors for my Block class.


Block.selectedColor = {r=192,g=55,b=55,a=255}
Block.normalColor = {r=255,g=255,b=255,a=255}
local id = 0;

function Block:init(x,y,w,h)
    -- you can accept and set parameters here
    self.x = x
    self.y = y
    self.w = w or 150
    self.h = h or 40
    self.id = id;
    print( "creating a block", self.id )
    
    self.counters = { inputDisplay=0, outputDisplay=0 }
    
    self.isInputSelected = false;
    self.isOutputSelected = false;
    self.isBlockSelected = false;
    self.blockDrawColor = Block.normalColor;
    id = id + 1;
end

function Block:draw()
    -- Codea does not automatically call this method
    rectMode( CORNER );
    noFill();
    stroke(color(self.blockDrawColor.r, self.blockDrawColor.g, self.blockDrawColor.b, self.blockDrawColor.a));
    strokeWidth(3);
    rect( self.x, self.y, self.w, self.h );
end

function Block:blockDeselected()
    --make a couple tweens that toggle between Block.selectedColor and Block.normalColor
    _r,_g,_b,_a = unpack(Block.selectedColor);
    self.blockDrawColor = {r=_r,g=_g,b=_b,a=_a}
    n = tween( 0.5, 
            self.blockDrawColor, 
            {   r=Block.normalColor.r, 
                g=Block.normalColor.g, 
                b=Block.normalColor.b, 
                a=Block.normalColor.a 
            }
        )
    --[[
    s = tween( 0.01,
            self.blockDrawColor,
            {   r=Block.selectedColor.r,
                g=Block.selectedColor.g,
                b=Block.selectedColor.b,
                a=Block.selectedColor.a
            }
        )
    ]]
    --d = tween.delay( 0.1 )
    --tween.sequence( d, n, d, s, d, n, d, s, d, n, d, s, d, n )
    
    self.isBlockSelected = false;
end

Basically, I’m trying to have it flash between two colors when an individual block is deselected for a second or two. and i’m trying to do it with tweens instead of state variables/counters inside of draw().

the error i’m getting is:

... in pairs(tweens) do
   c = c + 1
   end
  return c
end

:48: Parameter 'g' is missing from subject or isn't a number
stack traceback:
  [C]: in function 'assert'
  ...in pairs(tweens) do c = c+1 end return c end
:48: in upvalue 'checkSubjectAndTargetRecursively'
:103: in upvalue 'checkStartParams'
:494: in function <...in pairs(tweens) do c= c+1 end return c end
:493>
   (...tail calls...)
   Block:111: in method 'blockDeselected'
   Main:67: in function 'touched'

what am I doing wrong?
@dave1707 @Jmv38 @Ignatz

You can tween a color object’s components directly, you shouldn’t need to convert them back and forth from tables. Eg:

function setup()
    colour=color(255, 0, 233, 255)
    tween(2, colour, {r=0, g=255, b=82, a=255}, {easing=tween.easing.sineInOut, loop=tween.loop.pingpong})
end

function draw()
    background(40, 40, 50)
    fill(colour)
    rect(100,100,800,800)
end

@yojimbo2000 what about doing it on an instance of a class tho?

such that fill( instance.colour ) is used and the tween uses
tween(2, instance.colour, { } …etc )

Yup, should work no matter where the color is stored. Eg tween(2, self.colour, {r=0, g=255, b=82, a=255})

ok. It seems i’m having trouble when I try to use a class const inside the {}

i.e.

tween( 2, self.colour, { r=Block.selectedColor.r, g=Block.selectedColor.r etc… )

Actually where I get stuck is when the following happens:

if( self.isBlockDeselected ) then
  self.blockDrawColor = Block.selectedColor; --self.blockDrawColor is used when drawing the outline inside Block:draw()
tween(1, self.blockDrawColor, {r=Block.normalColor.r, g=Block.normalColor.g, b=Block.normalColor.b, a=Block.normalColor.a } ) 

–the above tween ends up modifying Block.selectedColor, which is supposed to be a separate object from self.blockDrawColor

self.blockDrawColor = Block.selectedColor

–the above tween ends up modifying Block.selectedColor, which is supposed to be a separate object from self.blockDrawColo

Nope, they’re not separate objects. color is a Codea object. Therefore, if you “copy” it just with =, you are actually coping a reference to it, not making an independent copy.

Edit: i see Block.selectedColor is a table, rather than a color object. Same applies though, = will just set a reference value pointer to the table

Why do you need to copy the color though, when you’re tweening? Why not just let the tween determine what the color is?

Block.selectedColor and Block.normalColor are supposed to be my class constants for the two colors to use when drawing the block.

the idea is that when you are done selecting the block, the block’s color is set to Block.selectedColor, and then the tween causes the color to switch back and forth from selectedColor to normalColor a few times before it stops on normalColor. Basically flashing the block. Perhaps my approach is wrong to try to use class constants?

In that case, create a “deep copy” of the constant color like this: self.blockDrawColor = color(Block.selectedColor.r, Block.selectedColor.g, Block.selectedColor.b, Block.selectedColor.a). By returning a new color object, you’re not creating a reference copy

Or, as just pointed out on the other thread by @dave1707 : self.blockDrawColor = color(Block.selectedColor:unpack())

@matkatmusic Here’s a way to flash using 2 colors. You can change the time values in the tweens for what you want. Tap the screen to flash.

function setup()
    sp={r=255,g=255,b=255}
    print("tap screen to flash")
end

function draw()
    background(0)
    fill(color(sp.r,sp.g,sp.b))
    rect(200,300,200,200)
end

function flash()
    sp={r=192,g=55,b=55}
    tween(.08,sp,{r=255,g=255,b=255},{loop=tween.loop.pingpong})
    tween.delay(1,function() tween.stopAll() sp={r=255,g=255,b=255} end)
end

function touched(t)
    if t.state==BEGAN then
        flash()
    end
end

@dave1707 any chance you can update it to use a class instance’s members as the value that is tweened?

@matkatmusic Not sure if this is what you’re after. I didn’t use your Block.selected or Block.normal colors.

function setup()
    sp={r=255,g=255,b=255}
    print("tap screen to flash")    
    b1=Block(200,200,100,100)
end

function draw()
    background(0)
    fill(color(sp.r,sp.g,sp.b))
    b1:draw()
end

function touched(t)
    if t.state==BEGAN then        
        b1:flash()
    end
end

Block=class()

--Block.selectedColor = {r=192,g=55,b=55,a=255}
--Block.normalColor = {r=255,g=255,b=255,a=255}
local id = 0;

function Block:init(x,y,w,h)
    self.x = x
    self.y = y
    self.w = w or 150
    self.h = h or 40
    self.id = id;
    print( "creating a block", self.id )
    self.counters = { inputDisplay=0, outputDisplay=0 }
    self.isInputSelected = false;
    self.isOutputSelected = false;
    self.isBlockSelected = false;
    self.blockDrawColor = Block.normalColor;
    id = id + 1;
end

function Block:draw()
    rectMode( CORNER );
    noFill();
    stroke(sp.r,sp.g,sp.b)
    strokeWidth(3);
    rect(self.x,self.y,self.w,self.h );
end

function Block:flash()
    sp={r=192,g=55,b=55}
    tween(.08,sp,{r=255,g=255,b=255},{loop=tween.loop.pingpong})
    tween.delay(1,function() tween.stopAll() sp={r=255,g=255,b=255} end)    
end

@matkatmusic Here’s a version using your Block colors. It could probably be reduced, but I don’t have any more time to work on it.

function setup()
    print("tap screen to flash")    
    b1=Block(200,200,100,100)
end

function draw()
    background(0)
    b1:draw()
end

function touched(t)
    if t.state==BEGAN then    
        b1:flash()
    end
end

Block=class()

Block.selectedColor = {r=192,g=55,b=55,a=255}
Block.normalColor = {r=255,g=255,b=255,a=255}
Block.sp={r=Block.normalColor.r,g=Block.normalColor.g,b=Block.normalColor.b}
local id = 0;

function Block:init(x,y,w,h)
    self.x = x
    self.y = y
    self.w = w or 150
    self.h = h or 40
    self.id = id;
    print( "creating a block", self.id )
    self.counters = { inputDisplay=0, outputDisplay=0 }
    self.isInputSelected = false;
    self.isOutputSelected = false;
    self.isBlockSelected = false;
    self.blockDrawColor = Block.normalColor;
    id = id + 1;
end

function Block:draw()
    rectMode( CORNER );
    noFill();
    stroke(Block.sp.r,Block.sp.g,Block.sp.b)
    strokeWidth(3);
    rect(self.x,self.y,self.w,self.h );
end

function Block:flash()
    Block.sp.r,Block.sp.g,Block.sp.b=
            Block.selectedColor.r,Block.selectedColor.g,Block.selectedColor.b
    tween(.08,Block.sp,{r=255,g=255,b=255},{loop=tween.loop.pingpong})
    tween.delay(1,function() tween.stopAll() 
            Block.sp.r,Block.sp.g,Block.sp.b=
                Block.normalColor.r,Block.normalColor.g,Block.normalColor.b 
            end)    
end