• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Accurate ParabolaZ function

Status
Not open for further replies.
Level 4
Joined
May 2, 2019
Messages
15
It has recently come to my attention that a number of ParabolaZ functions are incorrect, or rather, they are only accurate if the initial Z level and final Z level are identical - or if the maximum height value is sufficiently greater than either of the Z values.
Take for example, the popular ParabolaZ function:
JASS:
function ParabolaZ takes real y0, real y1, real h, real d, real x returns real
  local real A = (2*(y0+y1)-4*h)/(d*d)
  local real B = (y1-y0-A*d*d)/d
  return A*x*x + B*x + y0
endfunction
When h (height) is close to either y0 or y1 (initial and final Z), the actual maximum height of the generated parabola will be incorrect.
From some looking around, I've discovered most if not all other ParabolaZ functions are just a variant of this, some with and some without the starting/end Z values as inputs.

I've created a Desmos graph demonstrating a more accurate formula for determining the A and B constants of a parabola, and the difference between the popular method, and the more precise calculus derived method. My function of course, does not support having a height value inbetween the starting or ending Z level - as mathematically speaking, there is no real solution where a maximum/minimum height value could fall between those two.
Unfortunately, the more accurate version of the equation may be significantly slower, requiring the usage of two SquareRoot calls, and some additional if/else branching to determine the sign of, essentially demanding that the function only be used to generate the A and B constants once, and stored for later usage in calculating the Z level.

JASS:
function CalculateParabolaConstants takes real y0, real y1, real h, real d returns nothing
    local real dh = h-y0
    local real S = RSignBJ(dh)
    local real hy= 2*h-y0-y1
    if h > y0 and h < y1 then
        //fall back to less accurate but more lenient equation
        //call BJDebugMsg("Invalid height, undefined parabola value")
        set udg_ParabolaA = (2*(y0+y1)-4*h)/(d*d)
        set udg_ParabolaB = (-y1-3*y0+4*h)/d
    else
        set udg_ParabolaA = -1/(d*d)*(hy+2*S*SquareRoot((h-y0)*(h-y1)))
        set udg_ParabolaB = S*SquareRoot(-4*udg_ParabolaA*dh)
    endif
endfunction
I've also included a variation of the inaccurate formula for use when the height value is "invalid" - so that the function does not result in a thread crash from taking the square root of a negative number but still returns a (mostly) serviceable result.

If there was actually an accurate ParabolaZ function somewhere out there, please let me know so that I can link it, so that someone else who is also searching for accurate parabolas might stumble onto it.

Also if anyone has additional ParabolaZ functions (with initial and ending Z values as inputs) - inaccurate or not, feel free to mention them so that I can include them in the comparison graph for future reference.

Edit: Fixed an error in the JASS code, mistook (h-y0)+(h-y1) and (h-y0)*(h-y1) when copying over the formula from the graphs.
 
Last edited:
This seems to work just fine, but no idea if it is exactly accurate:
JASS:
    function GetParabolaZEx takes real curDist, real maxDist, real maxHeight, real startHeight, real endHeight returns real
        return 4 * maxHeight * curDist * (maxDist - curDist) / (maxDist * maxDist) + curDist * (endHeight-startHeight) / maxDist
    endfunction
It pairs with lerp very well (and easing functions)
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,198
The reason no one sensible factors in start and end height is that those are usually derived from values obtained using GetLocationZ which is not network safe as it returns a client local value. Native Warcraft III abilities and attacks do not even factor in start and end height as far as I am aware.

Edit: GetLocationZ is fine to use purely for visuals, but your parabola calculations cannot affect when or if your projectile impacts something. Such logic must not depend on GetLocationZ and the results of such logic could then be used to help try and sync up a parabola.
 
Last edited:
The reason no one sensible factors in start and end height is that those are usually derived from values obtained using GetLocationZ which is not network safe as it returns a client local value. Native Warcraft III abilities and attacks do not even factor in start and end height as far as I am aware.
I use it purely visually to avoid the async issues.
 
Level 4
Joined
May 2, 2019
Messages
15
This seems to work just fine, but no idea if it is exactly accurate:
JASS:
    function GetParabolaZEx takes real curDist, real maxDist, real maxHeight, real startHeight, real endHeight returns real
        return 4 * maxHeight * curDist * (maxDist - curDist) / (maxDist * maxDist) + curDist * (endHeight-startHeight) / maxDist
    endfunction
It pairs with lerp very well (and easing functions)
That one is actually much worse than the usual ParabolaZ. Here's a link to another graph with this formula included.

For reference, red is the accurate version, blue is the ParabolaZ method, and green is the one using your formula. As you can see, it does not actually handle cases where the starting height is non zero. I've also included an adjusted formula in dotted green lines, that should be the correct (but still inaccurate) version of the formula you are using.

JASS:
return 4 * maxHeight * curDist * (maxDist - curDist) / (maxDist * maxDist) + curDist * (endHeight-startHeight) / maxDist + startHeight

If I recall correctly, the formula you are using was derived by someone assuming a parabola's maximum height is in the center, between the starting and end distance (which is only true when starting height and ending height are zero).
JASS:
4 * maxHeight * curDist * (maxDist - curDist) / (maxDist * maxDist)
This formula actually gives us the correct maximum height, and is correct when both start and end height are 0. However, it completely ignores the starting height and final height.

So in an attempt to correct for this, they then added the linear component
JASS:
+ curDist * (endHeight-startHeight) / maxDist
Such that the start and end points should match up, or at least would if they also accounted for the starting height properly, which would be an additional component of just.
JASS:
+ startHeight

And while these adjustments would cause the parabola to correctly pass through the start and end heights, it completely threw off the actual final height, and caused the Z value to be incorrect for basically every non starting/end point. Though since it was still a parabola, I'd suspect nobody who used the function would ever notice because Z levels in wc3 are hard to gauge with your eyes - and people could just input a lower max height until it "looked about right".

Anyways, if you play with the graph and drag points around, you can see that each of the formulas will give similar values when the starting height and ending height are both set to 0, but begin to become much more inaccurate as these are given actual values.
 
Status
Not open for further replies.
Top