Jump to content

[TOPIC: topicViewTemplate]
[GLOBAL: userSmallPhoto]
Photo

Mixing Easing Calls and Bezier Paths
Started by mpkostek Feb 03 2016 02:43 PM

- - - - -
18 replies to this topic

Best Answer StarCrunch , 03 February 2016 - 08:34 PM

Well, using the stuff above, you might try a proxy table and something like this (untested):

local function Interpolate (object, vx, vy, time, transition)
  local t = 0 -- captured by metamethods
  local curve = bezier:curve(vx, vy) -- ditto
  local proxy = setmetatable({}, { -- table is just a dummy; metamethods do all the heavy lifting
    __index = function()
      return t -- transition will do something like proxy.t = proxy.t + delta
    end,
    __newindex = function(_, _, v)
      t = v -- update

      object.x, object.y = curve(t) -- sneak in some work
    end
  })

  return transition.to(proxy, { t = 1, time = time, transition = transition })
end

and then call it, say, as

Interpolate(enemyGroup[1], xv, yv, 2000, easing.inCubic)

You'd be better off baking your x and y into the curve, I'd say.

[TOPIC CONTROLS]
This topic has been archived. This means that you cannot reply to this topic.
[/TOPIC CONTROLS]
[modOptionsDropdown]
[/modOptionsDropdown]
[reputationFilter]
[TOPIC: post.html]
#1

mpkostek

[GLOBAL: userInfoPane.html]
mpkostek
  • Enthusiast

  • 89 posts
  • Corona SDK

I'm trying to mix using this bezier.lua file I found:

 

local bezier = {}
 
function bezier:curve(xv, yv)
    local reductor = {__index = function(self, ind)
        return setmetatable({tree = self, level = ind}, {__index = function(curves, ind)
            return function(t)
                local x1, y1 = curves.tree[curves.level-1][ind](t)
                local x2, y2 = curves.tree[curves.level-1][ind+1](t)
                return x1 + (x2 - x1) * t, y1 + (y2 - y1) * t
            end    
            end})
        end
    }
    local points = {}
    for i = 1, #xv do
        points[i] = function(t) return xv[i], yv[i] end
    end
    return setmetatable({points}, reductor)[#points][1]
end
 
return bezier

with the easing transition functions found here: https://docs.coronalabs.com/daily/api/library/easing/index.html.

 

Is there a smart way to do this? I'm finding that the bezier movement is often don'e with a timer or listener in many iterations of a loop of some kind. But the problem is that the transition.to() call (something like:

transition.to(enemyGroup[1], { x = 300, y = 300, time = 2000, transition=easing.inCubic })

is just called once and I can't see a way to fit a bezier path in there. Any ideas?

 

 



[TOPIC: post.html]
#2

Alex@Panc

[GLOBAL: userInfoPane.html]
Alex@Panc
  • Corona Geek

  • 1,733 posts
  • Corona SDK

[TOPIC: post.html]
#3

mpkostek

[GLOBAL: userInfoPane.html]
mpkostek
  • Enthusiast

  • 89 posts
  • Corona SDK

 

I don't think this will work. The transition ends up happening in the code block:

			transition.to( obj, {
				tag = "moveObject",
				time = transTime,
				x = pathPoints[obj.nextPoint].x,
				y = pathPoints[obj.nextPoint].y,
				onComplete = nextTransition
			})

which gets repeated several times. I don't think throwing an easing call in there would do what I want it to since it would just perform the easing motion on the very small segments that is being traveled in each of the many transition.to() calls. By all means correct me if I am wrong, but I don't think this will work.



[TOPIC: post.html]
#4

Alex@Panc

[GLOBAL: userInfoPane.html]
Alex@Panc
  • Corona Geek

  • 1,733 posts
  • Corona SDK

Super-simple way would be to count the points in the curve and only perform the transition with easing at specific points.

 

Should I assume that you only want to the easing to be applied to the first/last points in the curve, or do you have other arbitrary points you'd like it applied to as well?



[TOPIC: post.html]
#5

mpkostek

[GLOBAL: userInfoPane.html]
mpkostek
  • Enthusiast

  • 89 posts
  • Corona SDK

Well my thought is that the easing effect won't be recognizable when you apply it over and over to, say, 100 tiny line segments. I guess I am confused as to what you are asking.



[TOPIC: post.html]
#6

Alex@Panc

[GLOBAL: userInfoPane.html]
Alex@Panc
  • Corona Geek

  • 1,733 posts
  • Corona SDK

I agree with your last point, which is why it would be more aesthetically pleasing to only apply the easing at specific points, rather than along the entire path. 

 

If you go verbatim by what the above tutorial is describing, you are generating a table pathPoints which would contain all of the relevant points that the object would move along. The important part here is the obj.nextPoint portion. Above, you left out the "obj.nextPoint = obj.nextPoint+1", which is really where the progression takes place. If you set some logic to say:

if obj.nextPoint == 2 then
 -- set easing here
end

That would allow you to set easings as you see fit.

 

EDIT: Obviously the above is a ham-fisted example, but it illustrates the technique.



[TOPIC: post.html]
#7

mpkostek

[GLOBAL: userInfoPane.html]
mpkostek
  • Enthusiast

  • 89 posts
  • Corona SDK

I think there is still a disconnect somewhere between us. I don't feel like applying easing effects to any number (no matter how large or small) of these tiny segments will create a noticeable effect that I am looking for. I want the easing to appear as though the bezier path was one contiguous line (meaning the easing motion is noticed from the very first to the very last point).



[TOPIC: post.html]
#8

mpkostek

[GLOBAL: userInfoPane.html]
mpkostek
  • Enthusiast

  • 89 posts
  • Corona SDK

I think you might me assuming that this code produces curved segments, or that it has some segments that a a few 10's of pixels apart from one another. The points for each transition.to() call are pixels away from each other, so applying an easing effect to any amount of them causes a weird shaky behavior.



[TOPIC: post.html]
#9

Alex@Panc

[GLOBAL: userInfoPane.html]
Alex@Panc
  • Corona Geek

  • 1,733 posts
  • Corona SDK

Ah, I see what you are saying, now. However, I'm a bit confused, as well. If you want to apply a special easing to a transition as a whole, per your example "inCubic", would you not want the path to incorporate the properties of the easing you have in mind? 

 

 If I were to attempt to mimic the easing "inCubic" (start slow, speed up over time) with the tutorial's function, I'd do something like:

if obj.nextPoint % 9 then
	transTime = 1000
else
	transTime = transTime-100
end

Again, another short-hand example, but the theory should still apply. I'm not going to pretend to be an expert on transitions, so perhaps someone else can provide something more intelligent/tested. 

 

If I've again misunderstood your question and/or intent, I apologize.



[TOPIC: post.html]
#10

Alex@Panc

[GLOBAL: userInfoPane.html]
Alex@Panc
  • Corona Geek

  • 1,733 posts
  • Corona SDK

I heard TweenTrain is also a good option. 



[TOPIC: post.html]
#11

mpkostek

[GLOBAL: userInfoPane.html]
mpkostek
  • Enthusiast

  • 89 posts
  • Corona SDK

Ah, I see what you are saying, now. However, I'm a bit confused, as well. If you want to apply a special easing to a transition as a whole, per your example "inCubic", would you not want the path to incorporate the properties of the easing you have in mind? 

 

 If I were to attempt to mimic the easing "inCubic" (start slow, speed up over time) with the tutorial's function, I'd do something like:

if obj.nextPoint % 9 then
	transTime = 1000
else
	transTime = transTime-100
end

Again, another short-hand example, but the theory should still apply. I'm not going to pretend to be an expert on transitions, so perhaps someone else can provide something more intelligent/tested. 

 

If I've again misunderstood your question and/or intent, I apologize.

I see what you are going for, and I know this indeed will produce the effect I am going for. However, I know if I go this route I won't be able to use all the nice predefined easing functions. I will effectively be writing my own which isn't really ideal but it may have to suffice until someone comes up with something better :/



[TOPIC: post.html]
#12

mpkostek

[GLOBAL: userInfoPane.html]
mpkostek
  • Enthusiast

  • 89 posts
  • Corona SDK

And to answer your question of "would you not want the path to incorporate the properties of the easing you have in mind?": Applying the "starting slow and speeding up" effect to line segments which are 1-5 pixels long makes the movement look jittery and not at all like the behavior of performing the ease effect on the line as a whole.



[TOPIC: post.html]
#13

davebollinger

[GLOBAL: userInfoPane.html]
davebollinger
  • Corona Geek

  • 1,373 posts
  • Corona SDK

you (probably) need the parametric bezier equation, because it sounds like you're trying to alter the t parameter *a priori* to determining the xy's.  (and "easing" is simply modifying a linear "t" in a parametric equation to something non-linear, like t^2 fe)

 

the code you're using seems to do its own quanitizing of the curve (based on a two-second read), so it's "too late" to ease those quantized values (they were created into linear array indices by a linear t).  yes, you could ease between pairs of those linear-interpolated positions, but as you seem to be trying to point out -- that WON'T do much.

 

on the other hand, a parametric bezier typically looks like f(a,b,c,d,t) -- if you ease on THAT "t", THEN quantize it into discrete array of x/y's, THEN I think you'd get something you could use transition.to() as a linear interpolation as per that article.



[TOPIC: post.html]
#14

mpkostek

[GLOBAL: userInfoPane.html]
mpkostek
  • Enthusiast

  • 89 posts
  • Corona SDK

you (probably) need the parametric bezier equation, because it sounds like you're trying to alter the t parameter *a priori* to determining the xy's.  (and "easing" is simply modifying a linear "t" in a parametric equation to something non-linear, like t^2 fe)

 

the code you're using seems to do its own quanitizing of the curve (based on a two-second read), so it's "too late" to ease those quantized values (they were created into linear array indices by a linear t).  yes, you could ease between pairs of those linear-interpolated positions, but as you seem to be trying to point out -- that WON'T do much.

 

on the other hand, a parametric bezier typically looks like f(a,b,c,d,t) -- if you ease on THAT "t", THEN quantize it into discrete array of x/y's, THEN I think you'd get something you could use transition.to() as a linear interpolation as per that article.

This does sound very promising, is there some documentation on this kind of thing that you could point me to?



[TOPIC: post.html]
#15

mpkostek

[GLOBAL: userInfoPane.html]
mpkostek
  • Enthusiast

  • 89 posts
  • Corona SDK

Does anyone know where I could find an example of an implementation of a transition with "parametric bezier equations"?



[TOPIC: post.html]
#16

StarCrunch

[GLOBAL: userInfoPane.html]
StarCrunch
  • Contributor

  • 842 posts
  • Corona SDK

  Best Answer

Well, using the stuff above, you might try a proxy table and something like this (untested):

local function Interpolate (object, vx, vy, time, transition)
  local t = 0 -- captured by metamethods
  local curve = bezier:curve(vx, vy) -- ditto
  local proxy = setmetatable({}, { -- table is just a dummy; metamethods do all the heavy lifting
    __index = function()
      return t -- transition will do something like proxy.t = proxy.t + delta
    end,
    __newindex = function(_, _, v)
      t = v -- update

      object.x, object.y = curve(t) -- sneak in some work
    end
  })

  return transition.to(proxy, { t = 1, time = time, transition = transition })
end

and then call it, say, as

Interpolate(enemyGroup[1], xv, yv, 2000, easing.inCubic)

You'd be better off baking your x and y into the curve, I'd say.



[TOPIC: post.html]
#17

mpkostek

[GLOBAL: userInfoPane.html]
mpkostek
  • Enthusiast

  • 89 posts
  • Corona SDK

Well, using the stuff above, you might try a proxy table and something like this (untested):

local function Interpolate (object, vx, vy, time, transition)
  local t = 0 -- captured by metamethods
  local curve = bezier:curve(vx, vy) -- ditto
  local proxy = setmetatable({}, { -- table is just a dummy; metamethods do all the heavy lifting
    __index = function()
      return t -- transition will do something like proxy.t = proxy.t + delta
    end,
    __newindex = function(_, _, v)
      t = v -- update

      object.x, object.y = curve(t) -- sneak in some work
    end
  })

  return transition.to(proxy, { t = 1, time = time, transition = transition })
end

and then call it, say, as

Interpolate(enemyGroup[1], xv, yv, 2000, easing.inCubic)

You'd be better off baking your x and y into the curve, I'd say.

 

THIS DID IT. Thank you so much! I'm definitely going to take the time to go through this and make sure I know what is happening with this code block!

 

Thank you to everyone else who contributed to this answer as well! This community is so helpful!



[TOPIC: post.html]
#18

davebollinger

[GLOBAL: userInfoPane.html]
davebollinger
  • Corona Geek

  • 1,373 posts
  • Corona SDK

Does anyone know where I could find an example of an implementation of a transition with "parametric bezier equations"?

-- building from StarCrunch's voodoo, replacing "curve"/etal..
function CubicBezierTransition(obj,x1,y1,x2,y2,x3,y3,x4,y4,time,tran)
	local t = 0
	local function cubicBezier(a,b,c,d,t)
		local s = 1-t
		local ss = s*s
		local tt = t*t
		return ss*s*a + 3*ss*t*b + 3*s*tt*c + tt*t*d
	end
	local proxy = setmetatable({},{
		__index = function()
			return t
		end,
		__newindex = function(_,_,v)
			t = v;
			obj.x, obj.y = cubicBezier(x1,x2,x3,x4,t), cubicBezier(y1,y2,y3,y4,t)
		end
	})
	return transition.to(proxy, {t=1, time=time, transition=tran})
end

local obj1 = display.newRect(0,0,10,10)
	obj1:setFillColor(1,0,0)
local obj2 = display.newRect(0,0,10,10)
	obj2:setFillColor(0,1,0)
local obj3 = display.newRect(0,0,10,10)
	obj3:setFillColor(0,0,1)
local obj4 = display.newRect(0,0,10,10)
	obj4:setFillColor(1,0,1)

-- nb: bezier coords assume 320x480 screen, a lazy/loose "Z"

-- linear
CubicBezierTransition(obj1, 10,100, 300,200, 10,300, 300,400, 5000, easing.linear)

-- eased
CubicBezierTransition(obj2, 10,100, 300,200, 10,300, 300,400, 5000, easing.inCubic)

-- double-speed there and back again linearly
-- (just for contrast against custom ease below)
CubicBezierTransition(obj3, 10,100, 300,200, 10,300, 300,400, 5000, easing.continuousLoop)

function easingSinLoop(t,tmax,start,delta)
	return start+math.sin(t/tmax*math.pi)*delta
end

-- custom-eased, double-speed there and back again smoothed
CubicBezierTransition(obj4, 10,100, 300,200, 10,300, 300,400, 5000, easingSinLoop)



[TOPIC: post.html]
#19

trg

[GLOBAL: userInfoPane.html]
trg
  • Observer

  • 2 posts
  • Corona SDK

This was excellent help thanks!

 

I've modified it slightly to include rotation while following the curve:

local function angleBetween( srcX, srcY, dstX, dstY )
	local angle = ( math.deg( math.atan2( dstY-srcY, dstX-srcX ) )+90 )
	return angle % 360
end


function CubicBezierTransition(obj,x1,y1,x2,y2,x3,y3,x4,y4,time,tran)
	local t = 0
	local function cubicBezier(a,b,c,d,t)
		local s = 1-t
		local ss = s*s
		local tt = t*t
		return ss*s*a + 3*ss*t*b + 3*s*tt*c + tt*t*d
	end
	local proxy = setmetatable({},{
		__index = function()
			return t
		end,
		__newindex = function(_,_,v)
			t = v;
			local oldX = obj.x;
			local oldY = obj.y;
			
			obj.x, obj.y = cubicBezier(x1,x2,x3,x4,t), cubicBezier(y1,y2,y3,y4,t)
			obj.rotation = angleBetween(oldX, oldY, obj.x, obj.y)

		end
	})
	return transition.to(proxy, {t=1, time=time, transition=tran})
end



[topic_controls]
[/topic_controls]