Graphing a polar curve with uniform speed - math

I would like to draw an animation of a polar curve (a spiral) being graphed. I am using javascript and canvas. Currently, I am using setInterval to call a draw function, which graphs an x and y coordinate found from a parametric representation of the polar curve (x and y in terms of theta). I am incrementing theta by 0.01, from 0 to 2*pi, once for every call to draw(). The problem is that I wish for the animation to draw the same amount of the curve for each call to draw, so that the drawing appears to progress with uniform speed. It doesn't matter if the time between each call to draw is different; I just need the speed (in terms of pixels drawn / # of calls to draw) to be constant for the entire awing. In other words, I need the arc length of the segment of the polar graph drawn for each call to draw to be the same. I have no idea how to go about this. Any help/sugestions would be greatly appreciated. Thanks

Let f(z) be the theta variable you are referring to in your question. Here are two parametric equations that should be very similar to what you have:
x(f(z)) = f(z)cos(f(z))
y(f(z)) = f(z)sin(f(z))
We can define the position p(f(z)) at f(z) as
p(f(z)) = [x(f(z)), y(f(z))]
The speed s(f(z)) at f(z) is the length of the derivative of p at f(z).
x'(f(z)) = f'(z)cos(f(z)) - f(z)f'(z)sin(f(z))
y'(f(z)) = f'(z)sin(f(z)) + f(z)f'(z)cos(f(z))
s(f(z)) = length(p'(f(z))) = length([x'(f(z)), y'(f(z))])
= length([f'(z)cos(f(z)) - f(z)f'(z)sin(f(z)), f'(z)sin(f(z)) + f(z)f'(z)cos(f(z))])
= sqrt([f'(z)cos(f(z))]2 + [f(z)f'(z)sin(f(z))]2 + [f'(z)sin(f(z))]2 + [f(z)f'(z)cos(f(z))]2)
= sqrt(f'(z) + [f(z)f'(z)]2)
If you want the speed s(f(z)) to be constant at C as z increases at a constant rate of 1, you need to solve this first-order nonlinear ordinary differential equation:
s(f(z)) = sqrt(f'(z) + [f(z)f'(z)]2) = C
http://www.wolframalpha.com/input/?i=sqrt%28f%27%28z%29+%2B+%5Bf%28z%29f%27%28z%29%5D%5E2%29+%3D+C
Solving this would give you a function theta = f(z) that you could use to compute theta as you keep increasing z. However, this differential equation has no closed form solution.
In other words, you'll have to make guesses at how much you should increase theta at each step, doing binary search on the delta to add to theta and line integrals over p(t) to evaluate how far each guess moves.

Easier method - change the parameter to setInterval proportional to the step arc length. That way you don't have to try to invert the arc length equation. If the interval starts getting too large, you can adjust the step size, but you can do so approximately.

Related

Calculate total absolute curvature from coordinates in R

Given a set of coordinates corresponding to a closed shape, I want to calculate the total absolute curvature, which requires calculating the curvature for each point, taking the absolute value, and summing them. Simple enough.
I used the answer to this question to calculate the curvature from a matrix of x y coordinates (xymat) and get what I thought would be the total absolute curvature:
sum(abs(predict(smooth.spline(xymat), deriv = 2)$y))
The problem is that total absolute curvature has a minimum value of 2*pi and is exactly that for circles, but this code is evaluating to values less than 2*pi:
library(purrr)
xymat <- map_df(data.frame(degrees=seq(0:360)),
function(theta) data.frame(x = sin(theta), y = cos(theta)))
sum(abs(predict(smooth.spline(xymat), deriv = 2)$y))
This returns 1.311098 instead of the expected value of 6.283185.
If I change the df parameter of smooth.spline to 3 as in the previous answer, the returned value is 3.944053, still shy of 2*pi (the df value smooth.spline calculated for itself was 2.472213).
Is there a better way to calculate curvature? Is smooth.spline parameterized by arc length or will incorporating it (somehow) rescue this calculation?
Okay, a few things before we begin. You're using degrees in your seq, which will give you incorrect results (0 to 360 degrees). You can check that this is wrong by taking cos(360) in R, which isn't 1. This is explained in the documentation for the trig functions under Details.
So let's change your function to this
xymat <- map_df(data.frame(degrees=seq(0,2*pi,length=360)),
function(theta) data.frame(x = sin(theta), y = cos(theta)))
If you plot this, this indeed looks like a circle.
Let's actually restrict this to the lower half of the circle. If you put a spline through this without understanding the symmetry and looking at the plot, chances are that you'll get a horizontal line through the circle.
Why? because the spline doesn't know that it's symmetric above and below y = 0. The spline is trying to fit a function that explains the "data", not trace an arc. It splits the difference between two symmetric sets of points around y = 0.
If we restrict the spline to the lower half of the circle, we can use y values between 1 and -1, like this:
lower.semicircle <- data.frame(predict(smooth.spline(xymat[91:270,], all.knots = T)))
And let's fit a spline through it.
lower.semicircle.pred<-data.frame(predict(smooth.spline(lower.semicircle, all.knots = T)))
Note that I'm not using the deriv function here. That is for a different problem in the cars example to which you linked. You want total absolute curvature and they are looking at rate of change of curvature.
What we have now is an approximation to a lower semicircle using splines. Now you want the distance between all of the little sequential points like in the integral from the wikipedia page.
Let's calculate all of the little arc distances using a distance matrix. This literally calculates the Euclidean distances between each point to every other point.
all.pairwise.distances.in.the.spline.approx<-dist(lower.semicircle.pred, diag=F)
dist.matrix<-as.matrix(all.pairwise.distances.in.the.spline.approx)
seq.of.distances.you.want<-dist.matrix[row(dist.matrix) == col(dist.matrix) + 1]
This last object is what you need to sum across.
sum(seq.of.distances.you.want)
..which evaluates to [1] 3.079 for the lower semicircle, around half of your 2*pi expected value.
It's not perfect but splines have problems with edge effects.

Calculating Normals across a sphere with a wave-like vertex shader

I've been trying to get the correct normals for a sphere I'm messing with using a vertex shader. The algorithm can be boiled down simply to
vert.xyz += max(0, sin(time + 0.004*vert.x))*10*normal.xyz
This causes a wave to roll across the sphere.
In order to make my normals correct, I need to transform them as well. I can take the tangent vector at a given x,y,z, get a perpendicular vector (0, -vert.z, vert.y), and then cross the tangent with the perp vector.
I've been having some issue with the math though, and it's become a personal vendetta at this point. I've solved for the derivative hundreds of times but I keep getting it incorrect. How can I get the tangent?
Breaking down the above line, I can make a math function
f(x,y,z) = max(0, sin(time + 0.004*x))*10*Norm(x,y,z) + (x,y,z)
where Norm(..) is Normalize((x,y,z) - CenterOfSphere)
After applying f(x,y,z), unchanged normals
What is the correct f '(x,y,z)?
I've accounted for the weirdness caused by the max in f(...), so that's not the issue.
Edit: The most successful algorithm I have right now is as follows:
Tangent vector.x = 0.004*10*cos(0.004*vert.x + time)*norm.x + 10*sin(0.004*vert.x + time) + 1
Tangent vector.y = 10*sin(0.004*vert.x + time) + 1
Tangent vector.z = 10*sin(0.004*vert.x + time) + 1
2nd Tangent vector.x = 0
2nd Tangent vector.y = -norm.z
2nd Tangent vector.z = norm.y
Normalize both, and perform Cross(Tangent2, Tangent1). Normalize again, and done (it should be Cross(Tangent1, Tangent2), but this seems to have better results... more hints of an issue in my math!).
This yields this
Get tangent/normal by derivate of function can sometimes fail if your surface points are nonlinearly distributed and or some math singularity is present or if you make a math mistake (which is the case in 99.99%). Anyway you can always use the geometric approach:
1. you can get the tangents easy by
U(x,y,z)=f(x+d,y,z)-f(x,y,z);
V(x,y,z)=f(x,y+d,z)-f(x,y,z);
where d is some small enough step
and f(x,y,z) is you current surface point computation
not sure why you use 3 input variables I would use just 2
but therefore if the shifted point is the same as unshifted
use this instead =f(x,y,z+d)-f(x,y,z);
at the end do not forget to normalize U,V size to unit vector
2. next step
if bullet 1 leads to correct normals
then you can simply solve the U,V algebraically
so rewrite U(x,y,z)=f(x+d,y,z)-f(x,y,z); to full equation
by substituting f(x,y,z) with the surface point equation
and simplify
[notes]
sometimes well selected d can simplify normalization to multipliyng by a constant
you should add normals visualization for example like this:
to actually see what is really happening (for debug purposses)

Position(t) on cubic bezier curve

The only equation to calculate this that I can find involves t in the range [0, 1], but I have no idea how long it will take to travel the entire path, so I can't calculate (1 - t).
I know the speed at which I'm traveling, but it seems to be a heavy idea to calculate the total time beforehand (nor do I actually know how to do that calculation). What is an equation to figure out the position without knowing the total time?
Edit To clarify on the cubic bezier curve: I have four control points (P0 to P1), and to get a value on the curve with t, I need to use the four points as such:
B(t) = (1-t)^3P0 + 3t(1-t)^2P1 + 3t^2(1-t)P2 + t^3P3
I am not using a parametric equation to define the curve. The control points are what define the curve. What I need is an equation that does not require the use of knowing the range of t.
I think there is a misunderstanding here. The 't' in the cubic Bezier curve's definition does not refer to 'time'. It is parameter that the x, y or even z functions based on. Unlike the traditional way of representing y as a function of x, such as y=f(x), an alternative way of representing a curve is by the parametric form that represents x, y and z as functions of an additional parameter t, C(t)=(x(t), y(t), z(t)). Typically the t value will range from 0 to 1, but this is not a must. The common representation for a circle as x=cos(t) and y=sin(t) is an example of parametric representation. So, if you have the parametric representation of a curve, you can evaluate the position on the curve for any given t value. It has nothing to do with the time it takes to travel the entire path.
You have the given curve and you have your speed. To calculate what you're asking for you need to divide the total distance by the speed you traveled given that time. That will give you the parametric (t) you need. So if the total curve has a distance of 72.2 units and your speed is 1 unit then your t is 1/72.2.
Your only missing bit is calculating the length of a given curve. This is typically done by subdividing it into line segments small enough that you don't care, and then adding up the total distance of those line segments. You could likely combine those two steps as well if you were so inclined. If you have your given speed, just iteration like 1000th of the curve add the line segment between the start and point 1000th of the way through the curve, and subtract that from how far you need to travel (given that you have speed and time, you have distance you need to travel), and keep that up until you've gone as far as you need to go.
The range for t is between 0 and 1.
x = (1-t)*(1-t)*(1-t)*p0x + 3*(1-t)*(1-t)*t*p1x + 3*(1-t)*t*t*p2x + t*t*t*p3x;
y = (1-t)*(1-t)*(1-t)*p0y + 3*(1-t)*(1-t)*t*p1y + 3*(1-t)*t*t*p2y + t*t*t*p3y;

making a sine wave steeper?

I've written a a little function that gives me out a value based on a sine wave when I put in a float between 0 and 1. I'm using it to lerp things around in a game.
public static class Utilities
{
public static float SineMe(float prop)
{
float output = (prop*180f)-90f;
output = Mathf.Sin(output*Mathf.Deg2Rad);
output = (output+1f)/2f;
return output;
}
}
It works fine.. But I was wondering is there a mathematical way of altering the sine wave so I can make it 'steeper' or 'shallower' in the middle?
In the diagram below the blue curve is a sine wave, I'm wondering if I can make it more like the green line.
What you're showing already isn't really sine - the range of sine is between -1 and +1. You're applying the linear function f(x) = (x+1)/2 to change that range. So place another function between the sine and that transform.
To change the shape, you need a non-linear function. So, here's a cubic equation you might try...
g(x) = Ax^3 + Bx^2 + Cx + D
D = 0
C = p
B = 3 - 3C
A = 1 - (B + C)
The parameter p should be given a value between 0.0 and 9.0. If it's 1.0, g(x) is the identity function (the output is the unmodified input). With values between 0.0 and 1.0, it will tend to "fatten" your sine wave (push it away from 0.0 and towards 1.0 or -1.0) which is what you seem to require.
I once "designed" this function as a way to get "fractal waveforms". Using values of p between 1.0 and 9.0 (and particularly between around 3.0 and 6.0) iterative application of this formula is chaotic. I stole the idea from the population fluctuation modelling chaotic function by R. M. May, but that's a quadratic - I wanted something symmetric, so I needed a cubic function. Not really relevant here, and a pretty aweful idea as it happens. Although you certainly get chaotic waveforms, what that really means is huge problems with aliassing - change the sample rate and you get a very different sound. Still, without the iteration, maybe this will give you what you need.
If you iterate enough times with p between 0.0 and 1.0, you end up with a square wave with slightly rounded corners.
Most likely you can just choose a value of p between 0.0 and 1.0, apply that function once, then apply your function to change the range and you'll get what you want.
By the way, there's already a comment suggesting a cheat sheet of "easing functions". "Easing" is a term from animation, and computer animation software often uses Bezier curves for that purpose - the same Bezier curves that vector graphics software often uses. Bezier curves come in quadratic and cubic variants, with cubic being the more common. So what this is doing probably isn't that different. However, cubic Bezier easing gives you more control - you can control the "ease-in" independently of the "ease-out", where my function only provides one parameter.
You can use the y(x) = 1-(1-x)^n function when x = [0..1], as a transform function.
You will just have to replace x by the absolute value of your sinus and report the sign of sinus to the result. In that way you can tweak the sinus slope by increasing n. So what you want is this:
float sinus = Mathf.Sin(output*Mathf.Deg2Rad);
int sign = (sinus >= 0 ? 1 : -1);
int n = 4; // slope parameter
float waveform = sign * ( 1-Mathf.Pow(1-Mathf.Abs(sinus), n) );
You can root the sine function to make it steeper (only working for positive values). The higher the root, the steeper the sine.
Graph of a steeper sine wave function
I discovered this nifty trick for a steeper sine wave (0..1).
f(x) = cos(sin(x)^3)^10
If you need (-1..1):
2 * (f(x) - 0.5)
I think I found the solution.
(0.5+sin(x*π-π/2)/2)^((2*(1-x))^k)
in the interval x = [0.0, 1.0]
with k that control the steepness.
k=0.0 for the unmodified sinus (purple)
k=1.0 (green)
k=2.0 (blue)
https://www.desmos.com/calculator/wdtfsassev
I was looking for a similar function, not for the whole sine but just half the period.
I bumped into the Logistic function:
f(x) = L / (1 + e^(-k(x-x0)))
where
e = the natural logarithm base (also known as Euler's number),
x0 = the x-value of the sigmoid's midpoint,
L = the curve's maximum value, and
k = the steepness of the curve.
See https://en.wikipedia.org/wiki/Logistic_function
Works for me
what about
sign(sin(x))*sqrt(abs(sin(x))
https://www.desmos.com/calculator/5nn34xqkfr

Gradients for 2D geometric shapes

I've been generating islands using a combination of Perlin noise and radial gradients -- as outlined in this awesome Reddit answer. It's working pretty well, but obviously the islands are all circular in shape, and I'd like more variety.
I was wondering if there is a general method of converting equations for geometrical shapes into equations for gradients. Eg. an equation that is to its shape what Euclidean distance is for a circle. Is this possible? Is it obvious?
Defining an implicit surface for a geometric shape, or the Level Set (wiki), is basically what you are asking for. For example, the implicit surface of a circle with radius R is
f(x,y) = x^2 + y^2 - R^2
Note that when f(x,y) = 0, that defines the surface of the circle. When f(x,y) < 0, then x^2 + y^2 < R^2, which means the point (x,y) is inside the circle centered at the origin. Finally f(x,y) > 0 means the point (x,y) is outside the circle. In order to create an image out of this, you would need to iterate over every pixel and evaluate f(x,y), while finding the min and max value, then scale the whole image to have a range between 0 and 1.
This would technically not give you distance. The circle is a special case because the equation for it (x^2 + y^2) is the squared distance function from the origin. This does give you the effect you are most likely looking for your problem - negative inside, positive inside (or vice versa), while the magnitude is scaled w.r.t. to the distance of a given point to the surface.
For any equation you come up with for a geometric shape, you need to get all the terms on one side to form f(x,y). For example, take x*y = log(x). That can become x*y - log(x) = 0, so f(x,y) = x*y - log(x).

Resources