Jump to content

[INFO] KSP floatCurves and you - the magic of tangents.


Taverius

Recommended Posts

KSP floatCurves and you - the magic of tangents.

Or: Enter the magical land of unexpected behaviour:

You thought it was doing this:

TitleExampleA.jpg

Instead, its doing this:

TitleExampleB.jpg


Part 1: What are floatCurves?

If you make mods you've probably used floatCurves and never realized.

Every time you see something like this:

velocityCurve
{
key = 1000 0 0 0
key = 850 0.2 0 0
key = 0 1 0 0
}

Or like this:

    emission = 0.0 0.0
emission = 0.05 0.0
emission = 0.075 0.25
emission = 1.0 1.25

Or even something like this:

atmosphereCurve
{
key = 0 370
key = 1 320
}

You are making a floatCurve.

What is a floatCurve? Its a wrapper around a Unity Animation Curve. Essentially, its a way to describe an arbitrary line on a 2d graph using a few points rather than a formula.

Unity Animation Curves are a type of spline (cubic Hermite splines, I'm informed) and you can think of it as unity threading something that behaves like a stuff, springy steel cable through the points you set.

This lets you do things like describe how the playback volume of a sound should change with the throttle, or how a wheel's steering should change with speed, in a nonlinear way using only a few lines in a text file, and without complex maths.

Great, right?

They are, but if you're not careful floatCurves will trip you up, and things will not behave as you expect.

For example, here's a random curve I made:

key = 0 1
key = 0.4 0.6
key = 0.41 0.4
key = 1 0

What do you expect that looks like graphed out?

It looks like this:

RandomExampleA.jpg

In this tutorial, I will teach you how to make it look like this:

RandomExampleB.jpg


Part 2: So what are these tangents you mentioned?

In its most basic form, each point on a floatCurve will operate in 'Free' mode, where Unity fits a spline to the entire curve making it a single smooth, unbroken line.

However, as you saw above, that can cause your curve to behave differently than you expect it. If that curve is driving the thrust of an engine, or the steering of a wheel, or the length of a rocket's flame, it could look very very wrong, and you'll be scratching your head trying to fix it and adding tons of points to the curve trying to disciple it.

Thankfully, you don't need to do that.

A little known fact about points on a floatCurve is that they can take 2 optional parameters, in-tangent and out-tangent, that describe the angle of the curve as it enters and leaves the point.

Since we're talking trigonometric functions to describe angles between points on a plane of totally arbitrary width and height, they're rather complicate to visualize unless you're a mathematical genius.

Thankfully, r4m0n made (because I badgered him about it) a little plugin for the unity editor that lets you edit them graphically, interactively.

Since KSP floatCurves are unity animation curves, you can also be sure that the function used to draw it out for you is the same that will be used by KSP when the curve is run on a part.


Part 3: Let's have some examples

First, you need the Unity Editor, and r4m0n's FloatCurve Editor.

Install them so you can follow along.


Example 1: Torque curve for a racing wheel

The Tesla Roadster is a pretty neat car. It looks cool, its electric, and it goes pretty fast! Also, its made by a guy who makes rockets!

It would be pretty neat if we could make a rover wheel behave like the engine in of them, no? Well, we can, if we understand floatCurves.

First, lets try and find the torque curve for the vehicle. It so happens there's tons of them on the web, and a simple google image search for 'tesla roadster torque curve' got me this:

Example1A.jpg

We'll use Wolfram Alpha to convert the key points of the curve from Miles per Hour to Meters per Second.

That wavy bit at the start is probably the traction control keeping the torque in check.

If we do that in KSP we won't be able to start moving from a stop if we're on a hill, so lets start with 50% torque at 0m/s.

I have no idea how to convert lb-ft into whatever unity wheels for torque, so I'll start with the same value the 'RoveMax Model M1' starts with, 250 whatevers:

torqueCurve
{
key = 0 250
}

The next point on the line - roughly 7.5mph - is, Wolfram tells me, at 3.35m/s, and we go full torque there, a full 500 whatevers!:

torqueCurve
{
key = 0 250
key = 3.35 500
}

Next, at about 35mph (15.65m/s) torque has dropped a little, lets say to 485:

torqueCurve
{
key = 0 250
key = 3.35 500
key = 15.65 485
}

At 117mph(52.3m/s) its dropped to only 20%, or 100:

torqueCurve
{
key = 0 250
key = 3.35 500
key = 15.65 485
key = 52.3 100
}

By 120mph(53.64m/s) it seems to have pretty much dropped to 0, so lets do that:

torqueCurve
{
key = 0 250
key = 3.35 500
key = 15.65 485
key = 52.3 100
key = 53.64 0
}

Great! Now we should have a wheel that performs, roughly like a Tesla Roadster's engine, right?

... ehm ... not so much.

Lets look at it in Float Curve Editor, and you'll see what I mean.

First, start the unity client, and under the KSP menu created by the plugin, select 'Curve Editor':

Example1B.jpg

You'll get a window like this:

Example1C.jpg

Click on the text box in the middle of it, and paste your key lines into it, like so:

Example1D.jpg

If you use tabs rather than spaces to indent your code, you'll have to remove them or the plugin won't be able to read the curve.

See the little graph up top? That's the line you defined with your floatCurve, graphed out and editable for you ... and already you can see it looks nothing at all like what we based it on.

A reminder. The curve we based our values on:

Example1A.jpg

Now click on the little graph, and it'll expand to show your curve in all its wobbly, misbehaving glory:

Example1E.jpg

Don't worry! We can fix it, we have the technology!

To start, right-click on the second point from the left, hover over 'Right Tangent' and select 'Linear':

Example1F.jpg

You'll get this:

Example1G.jpg

Move over to the next point, right-click and select 'Broken':

Example1H.jpg

You'll see 2 small lines appear to each side of the point, these are the handles you use to tweak the curve. Grab the one on the left of the point and move it down till it looks like this:

Example1I.jpg

Over to the next point, right-click and select 'Broken' again:

Example1J.jpg

Lower the left handle of that point till its lying flat:

Example1K.jpg

Go back to the middle point, click it, and then move its right handle down till it looks like this:

Example1L.jpg

Lets tweak those two handles a bit more, till it looks something like this:

Example1M.jpg

That's close enough to this for me:

Example1A.jpg

Copy the tangents from the Curve Editor into your .cfg:

torqueCurve
{
key = 0 250
key = 3.35 500 36.70368 -1.219512
key = 15.65 485 -3.517569 -16.3017
key = 52.3 100 -3.71531 -15.77211
key = 53.64 0
}


Example 2: Emission curve for an an FX group

Lets say I'm making a jet engine. Since I do the .cfg-side for B9 Aerospace, I do this kind of thing a lot.

This hypothetical engine is some kind of afterburner-equipped part, and I want to have a nice FX starting at 66% throttle to look cool :)

So, lets make the curve for the FX. Emission should start at 0.66, so we start off like this:

MODEL_MULTI_PARTICLE
{
modelName = Squad/FX/shockExhaust_red_small
transformName = thrustTransform
emission = 0.66 0
}

Afterburner flames should ramp up with flames pretty quickly, so lets make another point at, say, 68% throttle, and put emission at 75% there.

MODEL_MULTI_PARTICLE
{
modelName = Squad/FX/shockExhaust_red_small
transformName = thrustTransform
emission = 0.66 0
emission = 0.68 0.75
}

Finally, full throttle should be full emission, so lets add a final point at 100% throttle, 100% emission.

MODEL_MULTI_PARTICLE
{
modelName = Squad/FX/shockExhaust_red_small
transformName = thrustTransform
emission = 0.66 0
emission = 0.68 0.75
emission = 1 1
}

Cool, right?

Now we should have the emission property of the FX ramp start at 66% throttle, ramp up quickly to 75% at 68% throttle, and then gradually increase to full at 100% throttle. Right? Right?

WRONG.

Lets copy-paste that curve into the FloatCurve Editor.

You'll need to change 'emission' to 'key', and delete any tabs in front of them:

key = 0.66 0
key = 0.68 0.75
key = 1 1

What its actually doing is this:

Example2A.jpg

Starts at 66% throttle, ramps waaaaaaaay up to peak at about 170% emission at 79% throttle, and then dips back down to 100% emission at 100% throttle.

Lets fix that.

First, right-click on the last point on the curve, over on the right, and select 'Flat':

Example2B.jpg

Next, right-click on the middle point, and select 'Free Smooth':

Example2C.jpg

Now grab the right handle of the point, and drag it down until the curve stops rising above 1 before it reaches the end:

Example2D.jpg

Now copy the tangents the FloatCurve Editor wrote out in its main window. We end up with:

MODEL_MULTI_PARTICLE
{
modelName = Squad/FX/shockExhaust_red_small
transformName = thrustTransform
emission = 0.66 0
emission = 0.68 0.75 1.668269 1.668269
emission = 1 1 0 0
}

And we're done - hopefully this helps you do better with floatCurves!


Further reading and useful links

Interactive editing:

Cubic Hermite Splines on Wikipedia.

Edited by Taverius
spline type
Link to comment
Share on other sites

Nice stuff! Although it looks like the "in tangent" and "out tangent" are not the angles per se, but rather the limit of the slope as it approaches the key from the left for "in" and right for "out." All that fancy calculus stuff and whatnot. :P

Yeah, but 'tangent' is what C7 said when I asked back in 2012 or so, and so 'tangent' it will always be to me :D

Link to comment
Share on other sites

Thanks for this doc Taverius.

One question: unless I miss the point, if what you wrote is true, does this mean pretty much ALL the engines made using these curves, not using tangents or whatever we call them, which rely on a "there is no official documentation but it should work as I think it works", including all stock engines, are all misbehave ?

Link to comment
Share on other sites

Thanks for this doc Taverius.

One question: unless I miss the point, if what you wrote is true, does this mean pretty much ALL the engines made using these curves, not using tangents or whatever we call them, which rely on a "there is no official documentation but it should work as I think it works", including all stock engines, are all misbehave ?

pretty much. This is one of those things where I went... WTF why didn't I know this sooner??

Actually Ferram did say the same thing back in 2013... had I read it more closely I would have realized what he was talking about.

The extra zeroes specify the slope of the curve at that point; the first specifies the slope coming from the left, the other specifies it coming from the right. This can be used to specify the curve more exactly with fewer inputs.

Now that I think about it... 0,0 would mean the in/out slopes for the keys is flat... so the curve would have a staircase shape. which would still be off.

Edited by nli2work
Link to comment
Share on other sites

Now that I think about it... 0,0 would mean the in/out slopes for the keys is flat... so the curve would have a staircase shape. which would still be off.

Yeah. Stock turbojet velocity curve:

Screenshot_188.png

Mine from TVPP/B9 rebalance of it:

Screenshot_189.png

The basic jet is just as bad, and the RAPIER just uses a copy of the TJets.

Torque/Steering curves for the wheels have a few fun shapes mixed in there too :)

Link to comment
Share on other sites

No joke... I was JUST LAST NIGHT trying to pick some numbers for my Solid Boosters to use in Engine Thrust Controller V2 with FAR, to throttle back at MaxQ. I was "this close" to writing a mod that would do nothing more than init a floatCurve and then print to the log file every value from 0.01 to 1.00 (step 0.01) for thrust output (and then look at how that would affect TWR and velocity at different altitudes because I also have KIDS installed so my booster is going to be gaining thrust with altitude too).

I was thinking, "SOMEONE must have something I can use. Someone must have some sort of documentation I can read to understand this. I'll search for it tomorrow."

Now I must go digest your entire post. TY TY TY TY TY!

Edited by Felbourn
Link to comment
Share on other sites

For engine thrust controller...

To do this...

012srbthrust.jpg

To get this...

JGpzs4L.png

Use this...


key = 0.00 0.01
key = 0.08 0.13
key = 0.15 0.52
key = 0.18 0.59
key = 0.42 0.84
key = 0.60 0.74
key = 0.83 0.99
key = 0.92 0.98
key = 1.00 0.92

Edited by Felbourn
Link to comment
Share on other sites

Excellent!

Here's the actual math behind how Unity/KSP evaluates tangents. Indeed they are not tangents per se, but slopes (which depend on the displacement in both x and y between keys).

Oh also, MuMech CurveEd, so you can edit ingame.

http://svn.mumech.com/KSP/trunk/MuMechLib/FloatCurveEditor.cs

Edited by NathanKell
Link to comment
Share on other sites

Excellent!

Here's the actual math behind how Unity/KSP evaluates tangents. Indeed they are not tangents per se, but slopes (which depend on the displacement in both x and y between keys).

Oh also, MuMech CurveEd, so you can edit ingame.

http://svn.mumech.com/KSP/trunk/MuMechLib/FloatCurveEditor.cs

Do I need to make a project for that and compiler it myself, or has the author already made it into a DLL, or is that already in MJ or something and I never noticed?

Link to comment
Share on other sites

Excellent!

Here's the actual math behind how Unity/KSP evaluates tangents. Indeed they are not tangents per se, but slopes (which depend on the displacement in both x and y between keys).

Oh also, MuMech CurveEd, so you can edit ingame.

http://svn.mumech.com/KSP/trunk/MuMechLib/FloatCurveEditor.cs

They ARE tangents.

It's just a third-order bezier curve (which has 4 control points) with two key/value entries as the start and the end control points, and two additional control points that have preset X values that are at 1/3 and 2/3 position between your start/end points' X coordinates. These two additional control points' Y value will be calculated based on the tangents and start/end points' Y coordinates. So yes they do work as tangents.

Link to comment
Share on other sites

They do work as tangents, but the value stored is a slope (since the x-displacement is fixed). Hence "per se". Though I don't mean to quibble about terminology, so I'll shut up now. :)

Felbourn, to follow on you can indeed compile it as a partmodule, add it to a dummy part, and that way use it ingame.

HoneyFox also has an ingame floatcurve editor, in Tweakable Parameters.

Link to comment
Share on other sites

They do work as tangents, but the value stored is a slope (since the x-displacement is fixed). Hence "per se". Though I don't mean to quibble about terminology, so I'll shut up now. :)

Felbourn, to follow on you can indeed compile it as a partmodule, add it to a dummy part, and that way use it ingame.

HoneyFox also has an ingame floatcurve editor, in Tweakable Parameters.

I don't get it. x-displacement is fixed? shouldn't it be 1/3 * (key2 - key1) or 2/3 * (key2 - key1)? or perhaps I misunderstood your statement. :P

Anyway, here's a simple illustration:

Float_Curve.jpg

The two black dots and two grey dots are the four control points for this section of bezier curve.

The two black dots are two adjacent entries in float curve.

Edited by HoneyFox
Link to comment
Share on other sites

Yeah, I mean "fixed to that," i.e. in and outtangents, since they are one-dimensional, are slopes rather than coordinates for the middle two points (tangent points) on the bezier segment. I'm used to dealing with this stuff in max, where you can actually set their position, rather than just their slope.

Link to comment
Share on other sites

Yeah, I mean "fixed to that," i.e. in and outtangents, since they are one-dimensional, are slopes rather than coordinates for the middle two points (tangent points) on the bezier segment. I'm used to dealing with this stuff in max, where you can actually set their position, rather than just their slope.

Guess I did not get what "slope" means, it's k=dy/dx at one point on the curve right?

Link to comment
Share on other sites

Yeah, my problem is I was approaching it as an artist, not as a mathematician / programmer. In the latter sense, yes, they are indeed tangents (the instantaneous slope at the CP). :)

Well, it's half a tangent... If the curve is smooth (in-slope == out-slope), then it's a tangent. Otherwise you have a hard corner with an undefined instantaneous slope at that point. It's easy to quibble about terminology (this is the internet, after all), and I hope the discussion/debate has enlightened a few folks.

Link to comment
Share on other sites

Well, it's half a tangent... If the curve is smooth (in-slope == out-slope), then it's a tangent. Otherwise you have a hard corner with an undefined instantaneous slope at that point. It's easy to quibble about terminology (this is the internet, after all), and I hope the discussion/debate has enlightened a few folks.

lol you are right, mathematically speaking, that point on the curve is not differentiable if in/out tangents are not equal.

So we are actually talking about the slope at two points whose x coordinates are X-epsilon and X+epsilon (for in & out)

Link to comment
Share on other sites

This thread is quite old. Please consider starting a new thread rather than reviving this one.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...