1D Hermite Cubic Splines with tangents of zero - how to make it look smoother - cubic-spline

I am given 3 values y0, y1, y2. They are supposed to be evenly spaced, say x0 = -0.5, x1 = 0.5, x2 = 1.5. And to be able to draw a spline through all of them, the derivatives at all points are said to be dy/dx = 0.
Now the result of rendering two Catmull-Rom-Splines (which is done via GLSL fragment shader, including a nonlinear transformation) looks pretty rigit. I.e. where the curve bends, it does so smoothly, though, but the bending area is very small. Zooming out makes the bends look too sharp.
I wanted to switch to TCB-Splines (aka. Kochanek-Bartels Splines), as those provide a tension parameter - thus I hoped I could smooth the look. But I realized that all TCB-Parameters applied to a zero tangent won't do any good.
Any ideas how I could get a smoother looking curve?

The tangent vector for a 2d parametric curve f(t)=(x(t), y(t)) is defined as f'(t)=(dx(t)/dt, dy(t)/dt). When you require your curve to have dy/dx = 0 at some points, it simply means the tangent vector at those points will go horizontally (i.e., dy/dt = 0). It does not necessarily mean the tangent vector itself is a zero vector. So, you should still be able to use TCB spline to do whatever you want to do.

Obviously nobody had a good answer, but as it's my job, I found a solution: The Points are evenly spaced, and the idea is to make transitions smoother. Now it's given, that the tangents are zero at all given Points, so it is most likely that close to the points we get the strongest curvature y''(x). This means, we'd like to stretch these "areas around the points".
Considering that currently we use Catmull-Rom-Splines, sectioned between the points. That makes y(x) => y(t) , t(x) = x-x0.
This t(x) needs to be stretched around the 0- and the 1-areas. So the cosine function jumped into my mind:
Replacing t(x) = x-x0 with t(x) = 0.5 * (1.0 - cos( PI * ( x-x0 ) ) did the job for me.
Short explanation:
cosine in the range [0,PI] runs smoothly from 1 to -1.
we want to run from 0 to 1, though
so flip it: 1-cos() -> now it runs from 0 to 2
halve that: 0.5*xxx -> now it runs from 0 to 1
Another problem was to find the correct tangents. Normally, calculating such a spline using Matrix-Vector-Math, you simply derive your t-vector to get the tangents, so deriving [t³ t² t 1] yields [3t² 2t 1 0]. But here, t is not simple. Using this I found the right derived vector:
| 0.375*PI*sin(PI*t)(1-cos(PI*t))² |
| 0.500*PI*sin(PI*t)(1-cos(PI*t)) |
| 0.500*PI*sin(PI*t) |
| 0 |

Related

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)

Simple math formula verification (normalize 0-100)

How do I normalize any given number between 0 and 100?
The min is 0 and the max has no bounds (it's the search volume for a keyword).
normalized = (x-min(x))/(max(x)-min(x)) won't work since I have no definition of max.
Arcus tangens
Algebraically, you might start with some function that has poles, e.g. tan, and use its inverse, atan. That inverse will never exceed a given limit, namely π/2 in this case. Then you can use a formula of the kind
f(x) = 100 * 2/π * atan(x - min)
If that doesn't produce “nice” results for small inputs, you might want to preprocess the inputs:
f(x) = 100 * 2/π * atan(a*(x - min))
for some suitably chosen a. Making a larger than one increases values, while for 0 < a < 1 you get smaller values. According to a comment, the latter is what you'd most likely want.
You could even add a power in there:
f(x) = 100 * 2/π * atan(a*(x - min)^b) = 100 * 2/π * atan(pow(a*(x - min), b))
for some positive parameter b. Having two parameters to tweak gives you more freedom in adjusting the function to your needs. But to decide on what would be good fits, you might have to decide up front as to what values you'd expect for various inputs. A bit like in this question, although there the input range is not unbounded.
Stereographic projection
If you prefer geometric approaches: you can imagine your input as the positive half of the x axis, namely the ray from (0,0) to (∞,0). Then imagine a circle with center (0,1) and radius 1 sitting on that line. If you connect the point (0,2) with any point on the ray, the connecting line will intersect the circle in one other point. That way you can map the ray onto the right half of the circle. Now take either the angle as seen from the center of the circle, or the y coordinate of the point on the circle, or any other finite value like this, normalize input and output properly, and you have a function matching your requirements. You can also work out a formula for this, and atan will likely play a role in that.

determine trajectory for object followers - curve of pursuit

I have started to develop unit trajectories for a game server and for now I'm trying to retrieve the position of a unit at a given time. It is easy when the trajectory is just a straight line, but it is far more complicated when unit chases another unit.
I've done flash app to illustrate the problem. Black trajectory is for unit which travels in a single direction. Blue chases black and red chases blue. What I want is to precalculate whole trajectory for blue and red to be able to retrieve their position in a constant time.
Is it possible? Thanks for any help!!
Here's a paper A classic chase problem solved from a
physics perspective by Carl E. Mungan that solves a particular version in which the chaser is initially perpendicular to the chased object's trajectory. I believe this is an inessential element of the solution since that perpendicularity disappears along the rest of the trajectory.
It is an autonomous system of differential equations in the sense that time does not appear explicitly in the coefficients or terms of the equations. This supports the idea that the family of solutions given in the paper is general enough to provide for non-perpendicular initial conditions.
The paper provides further links and references, as well as a useful search term, "curves of pursuit".
Let's state a slight different, slightly more general initial condition than Mungan's. Suppose the chased object ("ship") is initially located at the origin and travels up the positive y-axis (x=0) with constant speed V. The chasing object ("torpedo") is initially located at (x0,y0), and although instantaneous reorienting directly at the "ship", also travels at some constant speed v.
The special case where x0 is zero results in a linear pursuit curve, i.e. a head-on collision or a trailing chase accordingly as y0 is positive or negative. Otherwise by reflection in the y-axis one may assume without loss of generality that x0 > 0. Thus rational powers of x-coordinates will be well-defined.
Assume for our immediate purpose that speeds V,v are unequal, so that ratio r = V/v is not 1. The following is a closed-form solution (1) for the "torpedo" curve similar to Mungan's equation (10):
(1+r) (1-r)
[ (x/H) (x/H) ]
(y/H) = (1/2) [ ----- - ----- ] + C (1)
[ (1+r) (1-r) ]
in which the constants H,C can be determined by the initial conditions.
Applying the condition that initially the torpedo moves toward the ship's location at the origin, we take the derivative with respect to x in (1) and cancel a factor 1/H from both sides:
r -r
dy/dx = (1/2) [ (x/H) - (x/H) ] (2)
Now equate the curve's slope dy/dx at initial point (x0,y0) with that of its line passing through the origin:
r -r
(x0/H) - (x0/H) = 2y0/x0 = K (3)
This amounts to a quadratic equation for positive B = (x0/H)^r:
B^2 - K*B - 1 = 0 (4)
namely B = [K + sqrt(K^2 + 4)]/2 (but use the alternative form if K < 0 to avoid cancellation error), which allows H to be determined from our knowledge of x0 and r:
H = x0/(B^(1/r)) (5)
Knowing H makes it a simple matter to determine the additive constant C in (1) by substituting the initial point (x0,y0) there.
The tricky part will be to determine which point on the "torpedo" trajectory corresponds to a given time t > 0. The inverse of that problem is solved fairly simply. Given a point on the trajectory, find the tangent line at that point using derivative formula (2) and deduce time t from the y-intercept b of that line (i.e. from the current "ship" position):
t = b/V (6)
Therefore determining (x(t),y(t)) where the "torpedo" is located at a given time t > 0 is essentially a root-finding exercise. One readily brackets the desired x(t) between two x-coordinates x1 and x2 that correspond to times t1 and t2 such that t1 < t < t2. A root-finding method can be used to refine this interval until the desired accuracy is achieved. Once a fairly small interval has been refined, Newton's method will provide rapid convergence. We can look at the details of such a procedure in a next installment!
I can set up the problem for you but not solve it.
The black curve is moving at a constant velocity v0, and in a straight line.
The blue curve moves at a constant velocity v1 in the direction of black.
For simplicity, choose coordinates so that at time t=0 the black curve starts at (x=0, y=0) and is moving in the direction x.
Thus, at time t >= 0, the position of the black curve is (v0 t, 0).
Problem statement
The goal is to find x, y of the blue curve for times t >= 0 given the initial position (x(t=0), y(t=0)). The differential equations of motion are
dx / dt = v1 (v0 t - x) / a(t)
dy / dt = v1 (- y) / a(t)
where a(t) = sqrt((v0 t - x)^2 + (y^2)) is the distance between blue and black at time t.
This is a system of two nonlinear coupled differential equations. It seems likely that there is no complete anaytical solution. Wolfram Alpha gives up without trying for the input
D[y[t],t] = -y[t] / sqrt[(t-x[t])^2 + y[t]^2], D[x[t],t] = (t-x[t]) / sqrt[(t-x[t])^2 + y[t]^2]
You could try asking on math.stackexchange. Good luck!

Scale-agnostic, differentiable, co-planarity measure

I am looking for an (almost everywhere) differentiable function f(p1, p2, p3, p4) that given four points will give me a scale-agnostic measure for co-planarity. It is zero if the four points lie on the same plane and positive otherwise. Scale-agnostic means that, when I uniformly scale all points the planarity measure will return the same.
I came up with something that is quite complex and not easy to optimize. Define u=p2-p1, v=p3-p1, w=p4-p1. Then the planarity measure is:
[(u x v) * w]² / (|u x v|² |w|²)
where x means cross product and '*' means dot product.
The numerator is simply (the square of) the volume of the tetrahedron defined by the four points, and the denominator is a normalizing factor that makes this measure become simply the cosine of an angle. Because angles do not changed under uniform scale, this function satisfies all my requirements.
Does anybody know of something simpler?
Alex.
Edit:
I eventually used an Augmented Lagrangian method to perform optimization, so I don't need it to be scale agnostic. Just using the constraint (u x v) * w = 0 is enough, as the optimization procedure finds the correct Lagrange multiplier to compensate for the scale.
Your methods seems ok, I'd do something like this for efficient implementation:
Take u, v, w as you did
Normalize them: various tricks exist to evaluate the inverse square root efficiently with whatever precision you want, like this jewel. Most modern processors have builtins for this operation.
Take f = |det(u, v, w)| ( = (u x v) . w ). There are fast direct implementations for 3x3 matrices; see #batty's answer to this question.
This amounts to what you do without the squares. It is still homogeneous and almost everywhere differentiable. Take the square of the determinant if you want something differentiable everywhere.
EDIT: #phkahler implicitly suggested using the ratio of the radius of the inscribed sphere to the radius of the circumscribed sphere as a measure of planarity. This is a bounded differentiable function of the points, invariant by scaling. However, this is at least as difficult to compute as what you (and I) suggest. Especially computing the radius of the circumscribed sphere is very sensitive to roundoff errors.
A measure that should be symmetric with respect to point reorderings is:
((u x v).w)^2/(|u||v||w||u-v||u-w||v-w|)
which is proportional to the volume of the tetrahedron squared divided by all 6 edge lengths. It is not simpler than your formula or Alexandre C.'s, but it is not much more complicated. However, it does become unnecessarily singular when any two points coincide.
A better-behaved, order-insensitive formula is:
let a = u x v
b = v x w
c = w x u
(a.w)^2/(|a| + |b| + |c| + |a+b+c|)^3
which is something like the volume of the tetrahedron divided by the surface area, but raised to appropriate powers to make the whole thing scale-insensitive. This is also a bit more complex than your formula, but it works unless all 4 points are collinear.
How about
|(u x v) * w| / |u|^3
(and you can change |x| to (x)^2 if you think it's simpler).

formula for best approximation for center of 2D rotation with small angles

This is not a homework. I am asking to see if problem is classical (trivial) or non-trivial. It looks simple on a surface, and I hope it is truly a simple problem.
Have N points (N >= 2) with
coordinates Xn, Yn on a surface of
2D solid body.
Solid body has some small rotation (below Pi/180)
combined with small shifts (below 1% of distance between any 2 points of N). Possibly some small deformation too (<<0.001%)
Same N points have new coordinates named XXn, YYn
Calculate with best approximation the location of center of rotation as point C with coordinates XXX, YYY.
Thank you
If you know correspondence (i.e. you know which points are the same before and after the transformation), and you choose to allow scaling, then the problem is a set of linear equations. If you have 2 or more points then you can find a least-squares solution with little difficulty.
For initial points (xi,yi) and transformed points (xi',yi') you have equations of the form
xi' = a xi + b yi + c
yi' =-b xi + a yi + d
which you can rearrange into a linear system
A x = y
where
A = | x1 y1 1 0 |
| y1 -x1 0 1 |
| x2 y2 1 0 |
| y2 -x2 0 1 |
| ... |
| xn yn 1 0 |
| yn -xn 0 1 |
x = | a |
| b |
| c |
| d |
y = | x1' |
| y1' |
| x2' |
| y2' |
| ... |
| xn' |
| yn' |
the standard "least-squares" form of which is
A^T A x = A^T y
and has the solution
x = (A^T A)^-1 A^T y
with A^T as the transpose of A and A^-1 as the inverse of A. Normally you would use an SVD or QR decomposition to compute the solution as they ought to be more stable and less computationally intensive than the inverse.
Once you've found x (and so the four elements of the transformation a, b, c and d) then the various elements of the transformation are given by
scale = sqrt(a*a+b*b)
rotation = atan2(b,a)
translation = (c,d)/scale
If you don't include scaling then the system is non-linear, and requires an iterative solution (but isn't too difficult to solve). If you do not know correspondence then the problem is substantially harder, for small transformations something like iterated closest point works, for large transformations it's a lot harder.
Edit: I forgot to include the centre of rotation. A rotation theta about an arbitrary point p is a sequence
translate(p) rotate(theta) translate(-p)
if you expand it all out as an affine transformation (essentially what we have above) then the translation terms come to
dx = px - cos(theta)*px + sin(theta)*py
dy = py - sin(theta)*px - cos(theta)*py
we know theta (rotation), dx (c) and dy (d) from the equations above. With a little bit of fiddling we can solve for px and py
px = 0.5*(dx - sin(theta)*dy/(1-cos(theta)))
py = 0.5*(dy + sin(theta)*dx/(1-cos(theta)))
You'll notice that the equations are undefined if theta is zero, because there is no centre of rotation when no rotation is performed.
I think I have all that correct, but I don't have time to double check it all right now.
Look up the "Kabsch Algorithm". It is a general-purpose algorithm for creating rotation matrices using N known pairs. Kabsch created it to assist denoising stereo photographs. You rotate a feature in picture A to picture B, and if it is not in the target position, the feature is noise.
http://en.wikipedia.org/wiki/Kabsch_algorithm
See On calculating the finite centre of rotation for
rigid planar motion for a relatively simple solution. I say "relatively simple" because it still uses things like psuedo-inverses and SVD (singular value decomposition). And here's a wikipedia article on Instant centre of rotation. And another paper: ESTIMATION OF THE FINITE CENTER OF ROTATION IN PLANAR MOVEMENTS.
If you can handle stiffer stuff, try Least Squares Estimation of Transformation Parameters Between Two Point Patterns.
First of all, the problem is non-trivial.
A "simple" solition. It works best when the polygon resembles circle, and points are distributed evenly.
iterate through N
For both old and new dataset, find the 2 farthest points of the point N.
So now you have the triangle before and after the transformation. Use the clockwise direction from the center of each triangle to number its vertices as [0] (=the N-th point in the original dataset), [1], and [2] (the 2 farthest points).
Calculate center of rotation, and deformation (both x and y) of this triangle. If the deformation is more then your 0.001% - drop the data for this triangle, otherwise save it.
Calculate the average for the centers of rotation.
The right solution: define the function Err(Point BEFORE[N], Point AFTER[N], double TFORM[3][3]), where BEFORE - constant old data points, AFTER - constant new data points, TFORM[3][3] affine transformation matrix, Err(...) function that returns the scalar error value, 0.0 when the TFORM translated BEFORE to exact AFTER, or some >0.0 error value. Then use any numeric math you want to find the minimum of the Err(TFORM): e.g. gradient search.
Calculate polygon centers O1 and O2. Determine line formulae for O1 with (X0, Y0) and O2 with (XX0, YY0). Find intersection of lines to get C.
If I understand your problem correctly, this could be solved in this way:
find extremities (furthest points, probably on several axises)
scale either one to match
their rotation should now be trivial (?)
Choose any 2 points on the body, P1, P2, before and after rotation. Find vectors between these before and after points. Cross these vectors with a vector normal to the plane of rotation. This results in two new vectors, the intersection of the lines formed by the initial points and these two new vectors is the center of the rotation.
{
if P1after = P1before return P1after
if P2after = P2before return P2after
Vector V1 = P1after - P1before
Vector V2 = P2after - P2before
normal = Vn // can be messy to create for arbitrary 3d orientation but is simple if you know orientation, for instance, normal = (0,0,1) for an object in the x,y plane)
Vector VL1 = V1 x Vn //Vector V1 cross product with Vn
Vector VL2 = V2 x Vn
return intersectLines(P1after,VL1,P2after,VL2) //Center of rotation is intersection of two lines
}
intersectLines(Point P1, Vector V1, Point P2, Vector V2)
{
//intersect two lines using point, direction form of a line
//returns a Point
}

Resources