Interpolation functions in Fuses?

pixelstuff
Posts: 40
Joined: Sun Jul 15, 2018 11:50 am
Been thanked: 1 time

Interpolation functions in Fuses?

#1

Post by pixelstuff » Mon Jan 07, 2019 7:04 am

Does anyone know if its possible to interpolate a new set of positions as a function in a .fuse, there is a command to convert to a bspline on masks but nothing as far as I can see in the docs or any of the examples in the shape fuses. I have looked into interpolation algorithms(linear seems the easiest) but in .lua that is still difficult.


Image

Code: Select all

-- Lines to points
	local line1 = Shape()
	line1:MoveTo(start1.X, convertY(start1.Y, img))
	line1:LineTo(V3[1],convertY(V3[2],img)) -- Line to Mid
	line1:LineTo(P2[1],convertY(P2[2],img)) -- Line to end
I have 3 points I need to interpolate in some way which ever way is easiest, hopefully just something simple :). If its not simple, then this maybe a possibility

De Casteljau's algorithm
https://www.khanacademy.org/partner-con ... animation7

Or
Catmull-rom interpolation
https://github.com/pkulchenko/ZeroBrane ... ullrom.lua

I looked into math.cos() and math.sin which is how most of the circular shapes are created, but would give a perfect arc which isn't what I want.

I would need the current Middle Vector point I have to act as a Bspline control for a new interpolated(curved) set of points that use the original 3 points as reference.

Here is a loop I would use to plot the new points and lines(currently just a circle) but I hopefully will be able to interpolate the points and resample using "sides" number

Code: Select all

line2:MoveTo(start1.X, convertY(start1.Y, img))
	for i = 1, sides - 1 do

		line2:LineTo(P1[1]+cos(i/sides * math.pi * 2 + angle) * r,convertY(P1[2]+sin(i/sides * math.pi * 2 + angle) * r,img))
	end

Best solution would be Shape() = MakeItSmooth(How.Smooth?) :)

User avatar
SirEdric
Fusionator
Posts: 1471
Joined: Tue Aug 05, 2014 10:04 am
Been thanked: 31 times
Contact:

Re: Interpolation functions in Fuses?

#2

Post by SirEdric » Mon Jan 07, 2019 9:57 am

Hi Pixelstuff.

Those interpolations in Fusion are driven by the position of the handles (which you could set along with the KeyFrames).
So you would need to calculate those.
Or (if we're talking animation BezierSplines) you could have a look here: viewtopic.php?f=6&t=2622

User avatar
Midgardsormr
Fusionista
Posts: 877
Joined: Wed Nov 26, 2014 8:04 pm
Location: Los Angeles, CA, USA
Been thanked: 9 times
Contact:

Re: Interpolation functions in Fuses?

#3

Post by Midgardsormr » Mon Jan 07, 2019 10:50 am

I think we're actually talking vector drawing with a Fuse, not manipulation of Fusion's existing splines. Is that right? This is a problem I've wanted to solve for a while, since I learned that the built-in BezierTo() function is broken. I'm very pleased to see someone else taking it on!

The simplest method I can think of would be to use a sub-division approach, where you're using a loop to repeatedly call LineTo(). This will create a BSpline-like spline, where the line won't pass through the control points. This has the advantage of letting you use Fusion's existing Shape() functions to render the spline. It's the method I've been looking at to create a "fractalizer" that might make better-looking lightning than the existing Lightning Fuse.

Or, you could create your own spline function, where you can choose exactly which algorithm you want to use, including the Bezier, which does pass through the control point, as illustrated in your graph. You'd have much more control over the shape, but you'd also have to write your own rendering routines, since you wouldn't be able to plug it into Shape().

It might also be possible to use a sort of hybrid of the two, where you use your custom spline function to determine the end-points of a set of LineTo() calls. That would get you the best of both worlds, but it also means solving both of the previous problems to get there.

I mentioned the Lightning Fuse above. It might be a good place to look to see how to create chained LineTo() calls, which is similar to what you'll need to do:
https://www.steakunderwater.com/VFXPedi ... _v1_1.Fuse

It's been a long time since I looked at it, though. Not sure how much help it will be.

pixelstuff
Posts: 40
Joined: Sun Jul 15, 2018 11:50 am
Been thanked: 1 time

Re: Interpolation functions in Fuses?

#4

Post by pixelstuff » Mon Jan 07, 2019 12:28 pm

Image

Thanks both for your help, yes its just using lineto() to create the lines in the fuse. Some vectors and variables setup for this but they can get really confusing really fast when things get complicated.

I will try and see if I can your nested lineto loop to work. I have created a for loop that creates multiple segments from the bottom 2 points, then tried to use the rotated vector to position them in a curve but I can't get the bottom point distance value to ramp the normalized vector up and down to create the shape.

Another idea is the bottom solution, divide up the positions on one side > get the distance > then rotate and position each vector. That will probably drive me nuts trying and I have little control over the curve.

I have only just found this bit of code, that may come in useful, I just don't understand how it works yet.

Bezier Interpolation(it could be good to have control over the shape as it would use handles)

Code: Select all

--a line from x1, y1 to x2, y2 as t varies from 0 to 1
function line(x1, y1, x2, y2)
        return function(t)
                return x1 + (x2 - x1) * t, y1 + (y2 - y1) * t
        end
end

--the Bezier curve formed by interpolating between multiple given curves or lines
function curve(c1, c2, c3, ...)

        if not c2 then return c1 end    --sanity check

        local c1c2 = function(t) 
                local x1, y1 = c1(t)
                return line(x1, y1, c2(t))(t)
        end

        return c3 and curve(c1c2, curve(c2, c3, ...)) or c1c2
end

--the Bezier curve determined by given control points
function bezier(xv, yv)
        lines = {}
        for i = 1, #xv-1 do
                lines[i] = line(xv[i], yv[i], xv[i+1], yv[i+1])
        end
        return curve(unpack(lines)) or lines[1]
end

In Houdini(VEX) you would just use

spline("catrom", t, @P, v1, v2, v3)

to return the position

It uses 3 points to calculate the cat-mull rom interpolation and t is just the offset, but in Lua you would need to manually write that function.

User avatar
Midgardsormr
Fusionista
Posts: 877
Joined: Wed Nov 26, 2014 8:04 pm
Location: Los Angeles, CA, USA
Been thanked: 9 times
Contact:

Re: Interpolation functions in Fuses?

#5

Post by Midgardsormr » Mon Jan 07, 2019 1:15 pm

I unfortunately don't have the time to really dig into it at the moment, but I think a recursive function similar to this would be useful:

Code: Select all

subdivide(x1,y1, x2,y2, x3,y3, subdivs)
    -- Points 1 and 2 are the end points of the curve, and x3 is a control point in the middle, creating two line segments.
    -- Subdivs is the number of iterations to perform
    i = 0
    if i < subdivs then
        -- Calculate midpoints. 
        xa = (x3 - x1) / 2
        ya = (y3 - y1) / 2
        xb = (x2 - x3) / 2
        yb = (y2 - y3) / 2
        -- These two ordered pairs, a and b, describe a new line segment, each end of which forms a new linear curve that is the
        -- basis for another subdivision
        subdivide(x1,y1, xb,yb, xa,ya, subdivs - 1)
        subdivide(xa,ya, x2,y2, xb,yb, subdivs - 1)
    else -- Draw a line segment
        segment = Shape()
        segment:MoveTo(x1, y1)
        segment:LineTo(x2, y2)
    end
end

I'm not sure how close that is to something useful, but it's roughly the algorithm I was working on a few months ago, and it's close to the process described in the Khan Academy course, if I recall correctly.

Fusion is supposed to have a function that does this, but as I said before, it's broken.

pixelstuff
Posts: 40
Joined: Sun Jul 15, 2018 11:50 am
Been thanked: 1 time

Re: Interpolation functions in Fuses?

#6

Post by pixelstuff » Tue Jan 08, 2019 5:25 am

Thank you, hopefully they will fix that function, but on the up side you may also have some more control this way by building it from the ground up.

Your recursive function was going in the same direction to the version I have now. I think that your recursion idea would be very good for when you need a flexible amount of points for the guide points. The bit where you divided up the edges was correct, and in line with what I did. So I thought I would share some process.

I was getting a little bit lost in bits of code I found from different places. So I went back to understanding the concepts and found a good example of how the Casteljau's algorithm works as soon as I worked out the motion of the middle point it started to all make sense.

A quick illustration I did to visualize the concept
-
Image

-

If you treat the points as rails that slide up and down the right and left edges and the middle point that slides left to right, it creates the motion needed.

So going from 0-1 left to right with the mid points that are offset this drives the position of the middle point on the middle segment and its position left to right along the "rail", that's the best way to think of it.

The direction of the vectors is really important so that all of the points move correctly at the same time(not easy to visually check) so I had to brute force a line to each one of the vector positions of the points to make sure it was all working correctly.

In a nut shell the steps would be

1) Define vectors for the reference points
2) Create the middle points on the rails you can use as many as you need to but I used just a few, as this gets complicated really quickly
3) You need an offset value to move all of the points along the "rails", this then becomes the length of each line segment(do the re-sampling once you have it working)
4) Work out all of the vectors driving the points
5) Re sample the segments 1.0 is the total length / number of sides
6) Do some of this :banghead:


-

Code: Select all

	 -----------------

	-- P1[1] is left bottom point X P1[2] is left bottom point Y
	-- P2[1] is Right bottom point X P2[2] is Right bottom point Y
	-- V3[1] is offset middle point X V3[2] is offset middle point Y

	local line2 = Shape()
	step = 0 -- Number off segments
	step = 1/sides -- 1 is the total length / sides resamples the curve
	slide = 0
	line2:MoveTo(P1[1], P1[2], img) -- Start Position

	for i = 1,sides,1 -- Loop to slide middle point around
	do  
		slide = slide+step -- Length of each segment
		LM = {((V3[1]-P1[1])*slide)+P1[1],((V3[2]-P1[2])*slide)+P1[2]} -- Vector left middle
		RM = {((P2[1]-V3[1])*slide)+V3[1],((P2[2]-V3[2])*slide)+V3[2]} -- Vector right middle
		MM = {((RM[1]-LM[1])*slide)+LM[1],((RM[2]-LM[2])*slide)+LM[2]} -- Vector Middle middle
				
		line2:LineTo(MM[1],convertY(MM[2],img)) -- draw line to End of segement
		print("side: "..i) -- debug
		print("slide: "..slide) -- debug	
	end

	print("step: "..step) -- debug
	print("--------------") -- debug

	line1:AddShape( line2:OutlineOfShape(thickness, outlinetypes[linetype], "OJT_Round", (req:IsQuick() and 8 or 16)) )

	----------------------
	
	

tberakis
Posts: 27
Joined: Mon Dec 29, 2014 2:30 pm

Re: Interpolation functions in Fuses?

#7

Post by tberakis » Tue Jan 08, 2019 2:36 pm

If you are actually after catmul-rom, then maybe something like this would help, too:

Code: Select all

function catrom1d(t, p1, p2, p3, p4)
	return 0.5 * ((2 * p2) + (-p1 + p3) * t + (2*p1 - 5*p2 + 4*p3 - p4) * t^2 + (-p1 + 3*p2 - 3*p3 + p4) * t^3)
end

function catrom2d(t, p1, p2, p3, p4)
	return { catrom1d(t, p1[1], p2[1], p3[1], p4[1]), catrom1d(t, p1[2], p2[2], p3[2], p4[2]) }
end
This is fairly standard catmul-rom spline interpolation, where the function takes 't', the interpolation position from 0.0 .. 1.0, and four points, where the middle two are the ones you are interpolating between and the first and last are previous/next. Catmul-rom goes "through" all points, so where t=0, you will get exactly p2, and where t=1, you will get p3, with appropriate interpolation for in between values.

Generally recursive approximation is used based on the delta between results, to avoid some line segments being a significantly larger step than others. It's not necessary for basic constant-step interpolation. So to do that, you can recursively use catrom (or your interpolation of choice) between two 't' values, check the line length, and subdivide if below a threshold, ensuring you don't get steppiness in some parts of a curve.

pixelstuff
Posts: 40
Joined: Sun Jul 15, 2018 11:50 am
Been thanked: 1 time

Re: Interpolation functions in Fuses?

#8

Post by pixelstuff » Wed Jan 09, 2019 8:18 am

Thanks for sharing this, the Casteljau's algorithm works best for what I need with just the 3 control points that I have and means I don't need to create the extra "invisible" points that are used for catmul-rom. But what you have there would be great to iterate through a series of points, that would be easily update-able. This makes me question how the original polygon masks are created. They seem to be stored as a table of results positions, and handles that are relative to the points?

I'm not sure how you would access the code on a polygon mask, to see if the "lineto" iteration can be interrupted to then be able to animate the stroke along the path?

User avatar
Dunn
Moderator
Posts: 474
Joined: Mon Aug 04, 2014 4:27 am
Location: Hamburg, Germany
Been thanked: 2 times
Contact:

Re: Interpolation functions in Fuses?

#9

Post by Dunn » Wed Jan 09, 2019 2:32 pm

I just dug out some old code i was once working on too. Never got to finish it ( Somehow the BezierTo is not working right )
I can get the point count on the shape but can't access the points themselves .. maybe one can help out ( cdata<struct Shape *>: )

donno.. maybe this can help. ;)
Screenshot 2019-01-09 at 23.22.40.png
You do not have the required permissions to view the files attached to this post.