Projectile Aim Prediction with Target Acceleration and Bullet Deceleration Varying with Angle - c++

I previously asked a question about this topic here, and now I'd want to review if the offered solutions worked, and then frame a new inquiry based on the difficulties and issues that resulted.
So my previous post briefly summarized, what I'm attempting to do is calculate the Impact point of a Bullet and a moving Target in 3D Space based on the Bullet's Position, Target Position, Bulletspeed, Target Velocity and Target Acceleration.
The only variable I was lacking to estimate the impact point was the time it would take the bullet to reach the target, which I could solve using the formula below thanks to this post:
Right now I'm using this C++ quartic solver which gives me the time from the formula above which looks like this:
double t4calc = (TargetAcceleration.Dot(TargetAcceleration)) / 4;
double t3calc = (TargetAceleration.Dot(TargetVelocity));
double t2calc = TargetAcceleration.Dot(relativepos) + velocity.Dot(TargetVelocity) - bulletspeed * bulletspeed;
double t1calc = 2.0 * destination.Dot(TargetVelocity);
double tcalc = (relativepos.Dot(relativepos));
std::complex<double>* solutions = solve_quartic(t3calc / t4calc, t2calc / t4calc, t1calc / t4calc, tcalc / t4calc);
using solutions->real() it returns the time it takes to hit the target in seconds.
The Problem:
Since I assumed that the Bullet's velocity is constant and hence does not contain acceleration, the function above only include the target's acceleration but disregard the bullet's acceleration.
Now I discovered that the Bullet has also an acceleration and that it slows down over time.
Unfortunately, I can't just use a constant acceleration like -10M/s since the bullet's speed and acceleration also fluctuates based on the pitch view angle of the player.
Because using the pitch view angle for a f(x,y) calculation would be tedious, I decided to switch from viewangle to relative height and distance of the target, as my pitch view angle varies depending on how high and far the target is from me.
So, collecting some data and testing around, as well as using curve fitting, I was able to obtain the following function, which gives me the Time the Bullet need to Reach the Target in Seconds, based on the relative height and distance of the Target:
double a0 = 0.28891;
double a1 = 0.00147988;
double a2 = 0.0000116694;
double x1 = DistanceToTarget;
double x2 = Targetheight - Playerheight; //relative height of Target
double TimeToTarget = a0 + (a1 * (x1^1.2)) + (a2 * (x2^1.75)) //Returns Time in seconds it needs to reach the Target (If Target is stationary)
Okay so using the function above I could get the time which the Bullet needs to hit the Target (if the Target is not moving and standing at one point).
But of course I want the Time it needs to hit the Target if the Target is moving and accelerating..
So now I could calculate the bulletspeed in M/s by dividing the current distance to the target by the time obtained from the function above and insert it into the quartic solver function to get the time which the bullet needs to hit the moving and accelerating target based on the predicted position right...
Wrong.. this won't be accurate for the quartic solver function above, since it would use the bulletspeed computed for the target's current distance, while it should be using the bulletspeed calculated for the target's distance at impact. I hope you can follow me here.. if not feel free to ask about it.
Final Question:
So to solve this problem I'm wondering whether I can somehow include this TimeToTarget function into the previous quartic function. Otherwise the speed will be calculated incorrectly and the result will be wrong.

if by slowing you mean just gravity and not air friction like kv^2 or kv^3 then you can compute this very simply using 2 pass approach:
definitions
for simplicity lets assume bullet is p0,v0 and ball is p1,v1 ... meaning position,velocity and global acceleration a applying on both... so Newton d'Alembert physics dictates:
v0+=a*dt; p0+=v0*dt;
v1+=a*dt; p1+=v1*dt;
for updating time by dt ...
estimate bullet speed
simple direct line of sight is enough for this so:
v0=normalize(p1-p0)*v;
where v is your bullet start speed.
compute the collision time
simply by solving t in:
(p0+(v0*t)+(0.5*a*t*t)) = (p1+(v1*t)+(0.5*a*t*t));
t = (p1-p0)/(v0-v1);
as this is one solution per each axis use the smallest result above zero from the 3 see get_solution function in the example below
compute difference of position between both p0,p1 after t
simply by:
dp=(p1-p0)+((v1-v0)*t);
and correct initial v0 estimate so after t seconds the difference will be zero:
v0+=dp/t;
compute real time
again either use:
t = (p1-p0)/(v0-v1);
or if you need to account for radiuses of both objects then:
t = ((r0+r1-p0+p1)/(v0-v1));
However note that this approach will change the |v0| "slightly" so in case its a problem you should fit the v0 using spherical coordinates instead...
Here small C++/VCL example (just adaptation of my previous answer example)
//$$---- Form CPP ----
//---------------------------------------------------------------------------
#include <vcl.h>
#include "GLSL_math.h"
#pragma hdrstop
#include "win_main.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TMain *Main;
//---------------------------------------------------------------------------
vec3 p0=vec3( 0, 0, 0),v0=vec3( 0, 0,0); // bullet
vec3 p1=vec3(150,100, 0),v1=vec3(-50,-30,0); // ball
vec3 a =vec3(0,-10,0); // global acceleration
//---------------------------------------------------------------------------
float t0=0.0; // time to hit
const float r0= 2.0; // bullet radius
const float r1=15.0; // ball radius
float x0,y0,z0,x1,y1,z1; // walls
//---------------------------------------------------------------------------
float get_solution(vec3 tt)
{
float t=0.0;
// t = min(tt)
if (t<tt.x) t=tt.x;
if (t<tt.y) t=tt.y;
if (t<tt.z) t=tt.z;
return t;
}
//---------------------------------------------------------------------------
void shoot()
{
const float v=250.0; // bullet start speed
int i;
float t;
vec3 dp;
p0=vec3(0.5*(x0+x1),y0+r0,0); // reset shot start position
// solve t for colision:
// (p0+(v0*t)+(0.5*a*t*t)) - (p1+(v1*t)+(0.5*a*t*t)) = r0+r1;
// t = (r0+r1-p0+p1)/(v0-v1);
v0=normalize(p1-p0)*v; // v0 estimate
t=get_solution((p1-p0)/(v0-v1));// time estimate
dp=(p1-p0)+((v1-v0)*t); // diference after time t
v0+=dp/t; // correct v0 estimate
t0=get_solution((r0+r1-p0+p1)/(v0-v1)); // real time to collision
}
//---------------------------------------------------------------------------
void update(double dt)
{
int e=0;
// Newton/d'Lambert simulation
v0+=a*dt; p0+=v0*dt;
v1+=a*dt; p1+=v1*dt;
t0-=dt;
// bullet timeout
if (t0<=0.0) e=1;
// bullet hit the target
if (length(p1-p0)<=r1+r0) e=1;
// bullet colision with wall
if (p0.x<x0+r0) e=1;
if (p0.x>x1-r0) e=1;
if (p0.y<y0+r0) e=1;
if (p0.y>y1-r0) e=1;
if (p0.z<z0+r0) e=1;
if (p0.z>z1-r0) e=1;
// ball colision with wall
if (p1.x<x0+r1){ p1.x=x0+r1; v1.x=-v1.x; }
if (p1.x>x1-r1){ p1.x=x1-r1; v1.x=-v1.x; }
if (p1.y<y0+r1){ p1.y=y0+r1; v1.y=-v1.y; }
if (p1.y>y1-r1){ p1.y=y1-r1; v1.y=-v1.y; }
if (p1.z<z0+r1){ p1.z=z0+r1; v1.z=-v1.z; }
if (p1.z>z1-r1){ p1.z=z1-r1; v1.z=-v1.z; }
// shoot again if needed
if (e) shoot();
}
//---------------------------------------------------------------------------
void TMain::draw()
{
if (!_redraw) return;
float x,y,r;
// clear buffer
bmp->Canvas->Brush->Color=clBlack;
bmp->Canvas->FillRect(TRect(0,0,xs,ys));
// walls
bmp->Canvas->Pen->Color=clWhite;
bmp->Canvas->Brush->Color=clBlack;
bmp->Canvas->Rectangle(x0,ys-y0,x1,ys-y1);
// ball
bmp->Canvas->Pen->Color=clAqua;
bmp->Canvas->Brush->Color=clBlue;
x=p1.x; y=ys-p1.y; r=r1;
bmp->Canvas->Ellipse(x-r,y-r,x+r,y+r);
// bullet
x=p0.x; y=ys-p0.y; r=r0;
bmp->Canvas->Ellipse(x-r,y-r,x+r,y+r);
bmp->Canvas->Brush->Color=clBlack;
bmp->Canvas->Font->Color=clYellow;
bmp->Canvas->TextOutA(0,0,AnsiString().sprintf("time to hit: %.3fs",t0));
// render backbuffer
Main->Canvas->Draw(0,0,bmp);
_redraw=false;
}
//---------------------------------------------------------------------------
__fastcall TMain::TMain(TComponent* Owner) : TForm(Owner)
{
bmp=new Graphics::TBitmap;
bmp->HandleType=bmDIB;
bmp->PixelFormat=pf32bit;
pyx=NULL;
_redraw=true;
}
//---------------------------------------------------------------------------
void __fastcall TMain::FormDestroy(TObject *Sender)
{
if (pyx) delete[] pyx;
delete bmp;
}
//---------------------------------------------------------------------------
void __fastcall TMain::FormResize(TObject *Sender)
{
xs=ClientWidth; xs2=xs>>1;
ys=ClientHeight; ys2=ys>>1;
bmp->Width=xs;
bmp->Height=ys;
if (pyx) delete[] pyx;
pyx=new int*[ys];
for (int y=0;y<ys;y++) pyx[y]=(int*) bmp->ScanLine[y];
_redraw=true;
int w=32;
// boundaries
x0=w; x1=xs-w;
y0=w; y1=ys-w;
z0=-100; z1=+100;
p1=vec3(x1-r1,y1-r1,0);
shoot();
update(0.0);
}
//---------------------------------------------------------------------------
void __fastcall TMain::FormPaint(TObject *Sender)
{
_redraw=true;
}
//---------------------------------------------------------------------------
void __fastcall TMain::tim_redrawTimer(TObject *Sender)
{
update(0.04);
_redraw=true;
draw();
}
//---------------------------------------------------------------------------
and preview:
again the important stuff is function shoot() which aims and shoot bullet p0,v0 so it hits p1,v1 after t0 seconds... function update(dt) just simulates time and handle the collisions ...

Related

normal estimation of mesh open3d c++

I have a problem when re-computing my surface normals of a mesh in open3d. The problem is that the estimation is not good enough and I don't know how to make it better. From the image below it cannot be seen that the mesh has a hole in the middle of the belly.
However, if the same mesh is seen from the side it clearly has a hole.
If I then use MeshLab to do the normal estimation the hole can suddenly clearly be seen. MeshLab support 4 different functions for the normal estimation of a mesh but the result are more or less the same no matter which function I use.
Here is the same mesh after estimating normals with MeshLab.
I find it very strange that open3d does no even come close to the accuracy of MeshLabs normal estimation, and I believe that it is most likely because I miss some important calculation before using open3d normal estimation function.
Here is the code which is use for the normal estimation in open3d:
void ReconstructionSystem::constructMeshDeformation(glm::vec3& intersectionPosition, glm::vec3& robotPosition) {
double depthOfProbe = glm::distance(intersectionPosition.z, robotPosition.z);
double affectedArea = 0.015 * (depthOfProbe*100.0); // 0.015 is a random value the function simply works good with it
if (affectedArea > 0.08) {
affectedArea = 0.08;
}
deformatedMesh = std::make_shared<open3d::geometry::TriangleMesh>(*final_mesh);
int i = 0;
for each (Eigen::Vector3d vertex in deformatedMesh->vertices_) {
glm::vec3 vex = glm::vec3(vertex.x(), vertex.y(), vertex.z());
double dist = glm::distance(vex, intersectionPosition);
if (dist < affectedArea) {
double ratio = dist / affectedArea;
double deformationAmount = glm::cos((2.0 * M_PI * ratio) / 4.0);
deformatedMesh->vertices_.at(i).z() -= depthOfProbe * deformationAmount;
}
i++;
}
*deformatedMesh = deformatedMesh->ComputeVertexNormals();
}

How do I implement the math function to calculate the ellipse normal that crosses a point into C++?

To calculate the shortest distance from a point to the circumference of an ellipse, I have to calculate the normal/angle that passes through this point. See the following illustration:
The math to calculate this normal is explained here. In short:
Where E is the ellipse and L is the line between the point p and the circumference that creates the normal.
In C++, I have the following code fragment, where I need to insert this formula - solving it to variable t:
#include <cmath>
/// Calculate the normal of a line from ellipse circumference to point.
///
/// #param ew The width of the ellipse
/// #param eh The height of the ellipse
/// #param x The relative x position (relative to ellipse center)
/// #param y The relative y position (relative to ellipse center)
/// #return The normal in radians.
///
auto calculateNormal(float ew, float eh, float x, float y) -> double {
// ???
return std::atan(...);
};
How can I implement the formula in C++ to get the normal as shown?
What you currently have is not yet solved system of equations which as I originally taught from experience with similar but slightly simpler problem the algebraic solution leads to horrible stuff that would be too slow and inaccurate to compute (math solver throw about 2 pages of equations for roots which I throw away right away) so I would attack this with search approach instead.
If you are worrying about speed due to using goniometrics you can avoid that completely by simply computing normal as numeric derivation of the circumference points and just compare the slopes between normal (nx,ny) and found_point(x,y) - input_point(px,py).
The algo is like this:
search x in range <-a,+a>
compute y=f(x) and y1=f(x+epsilon)
I simply rewrite the implicit ellipse equation into something like this:
float ellipse_y(float rx,float ry,float x) // y = f(x)
{
if (fabs(x)>rx) return 0.0
return sqrt((rx*rx)-(x*x))*ry/rx;
}
of coarse the result should be +/- depending on the quadrant so if py<0 use negative values...
The epsilon should be some small value but not too small, I used 0.001*rx where rx,ry are sizes of the ellipse half axises.
compute normal (nx,ny)
so simply take two consequent points (x,y) and (x+epsilon,y1) substract them and rotate by 90deg by exchanging their coordinates and negate one of them. Once put together I got this:
void ellipse_n(float rx,float ry,float &nx,float &ny,float x,float &y) // x',y',y = f(x)
{
if (x<=-rx){ y=0.0; nx=-1.0; ny=0.0; return; }
ny=x+(0.001*rx); // epsilon
if (ny>=+rx){ y=0.0; nx=+1.0; ny=0.0; return; }
y=ellipse_y(rx,ry,x); // first point
nx=y-ellipse_y(rx,ry,ny); // second point
ny=ny-x;
/*
// normalize
x=divide(1.0,sqrt((nx*nx)+(ny*ny)));
nx*=x;
ny*=x;
*/
}
The normalization is optional (I commented it out for speed as its not needed for the search itself).
compute error e for the search
Simply the slopes (x-px,y-py) and (nx,ny) should be equal so:
e=fabs(((y-py)*nx)-((x-px)*ny));
The x search should minimize the e towards zero.
Do not forget to handle py<0 by negating y. Putting all togetherusing my approx search leads to:
//---------------------------------------------------------------------------
float ellipse_y(float rx,float ry,float x) // y = f(x)
{
if (fabs(x)>rx) return 0.0;
return sqrt((rx*rx)-(x*x))*ry/rx;
}
//---------------------------------------------------------------------------
void ellipse_pn(float rx,float ry,float &nx,float &ny,float x,float &y) // x',y',y = f(x) if (py>=0)
{
if (x<=-rx){ y=0.0; nx=-1.0; ny=0.0; return; }
ny=x+(0.001*rx); // epsilon
if (ny>=+rx){ y=0.0; nx=+1.0; ny=0.0; return; }
y=ellipse_y(rx,ry,x); // first point
nx=y-ellipse_y(rx,ry,ny); // second point
ny=ny-x;
}
//---------------------------------------------------------------------------
void ellipse_nn(float rx,float ry,float &nx,float &ny,float x,float &y) // x',y',y = f(x) if (py<=0)
{
if (x<=-rx){ y=0.0; nx=-1.0; ny=0.0; return; }
ny=x+(0.001*rx); // epsilon
if (ny>=+rx){ y=0.0; nx=+1.0; ny=0.0; return; }
y=-ellipse_y(rx,ry,x); // first point
nx=y+ellipse_y(rx,ry,ny); // second point
ny=ny-x;
}
//---------------------------------------------------------------------------
void this_is_main_code()
{
float rx=0.95,ry=0.35; // ellipse
float px=-0.25,py=0.15; // input point
float x,y,nx,ny;
approx ax; double e;
if (py>=0.0)
{
for (ax.init(-rx,+rx,0.25*rx,3,&e);!ax.done;ax.step())
{
x=ax.a;
ellipse_pn(rx,ry,nx,ny,x,y);
e=fabs(((y-py)*nx)-((x-px)*ny));
}
x=ax.aa; y=+ellipse_y(rx,ry,x);
}
else{
for (ax.init(-rx,+rx,0.25*rx,3,&e);!ax.done;ax.step())
{
x=ax.a;
ellipse_nn(rx,ry,nx,ny,x,y);
e=fabs(((y-py)*nx)-((x-px)*ny));
}
x=ax.aa; y=-ellipse_y(rx,ry,x);
}
// here (x,y) is found solution and (nx,ny) normal
}
//---------------------------------------------------------------------------
You still can adjust the search parameters (step and recursions) to have desired precision and speed. Also you can optimize by inlinening and removing heap thrashing and moving the range checks before the iterations...
Also you should adjust the normal computation for range <rx-epsilon,rx> as it truncates it to x=+rx (you could use negative epsilon for that).
[Ed1t1]
If I see it right the point with matching normal is also closest points so we can search directly that (which is even faster):
// search closest point
if (py>=0.0)
{
for (ax.init(-rx,+rx,0.25*rx,3,&e);!ax.done;ax.step())
{
x=ax.a;
y=ellipse_y(rx,ry,x);
x-=px; y-=py; e=(x*x)+(y*y);
}
x=ax.aa; y=+ellipse_y(rx,ry,x);
}
else{
for (ax.init(-rx,+rx,0.25*rx,3,&e);!ax.done;ax.step())
{
x=ax.a;
y=-ellipse_y(rx,ry,x);
x-=px; y-=py; e=(x*x)+(y*y);
}
x=ax.aa; y=-ellipse_y(rx,ry,x);
}
Also I feel there still might be some better solution using graphic approach like rescale to circle solve for circle and then scale back to ellipse +/- some corrections however too lazy to try...

3D interpolation between curves [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 1 year ago.
Improve this question
I have a set of curves that are temperature dependent.
I.e curve Mat1 is for temperature 310C and Mat2 is for temp 420C.
As you can see, the data looks better when put in a logarithmic scale;
Now I need to get Mat3 curve for temperature 370C by interpolating Mat1 and Mat2 curves. What is the best way to get around doing this? I'm guessing that I might need to do some sort of 3D interpolation. The nature of the data (logarithmic behavior) also needs to be considered.
Here's the data for Mat1
9.43E+06 6.00E+04
3.96E+06 6.20E+04
1.78E+06 6.40E+04
8.52E+05 6.60E+04
4.28E+05 6.80E+04
2.25E+05 7.00E+04
1.23E+05 7.20E+04
6.95E+04 7.40E+04
4.05E+04 7.60E+04
2.43E+04 7.80E+04
1.49E+04 8.00E+04
9.39E+03 8.20E+04
Here's the data for Mat2
5.14E+08 4.80E+04
1.35E+08 5.00E+04
4.36E+07 5.20E+04
1.64E+07 5.40E+04
6.90E+06 5.60E+04
3.18E+06 5.80E+04
1.58E+06 6.00E+04
8.35E+05 6.20E+04
4.64E+05 6.40E+04
2.69E+05 6.60E+04
1.62E+05 6.80E+04
1.01E+05 7.00E+04
6.47E+04 7.20E+04
4.25E+04 7.40E+04
2.86E+04 7.60E+04
1.96E+04 7.80E+04
1.37E+04 8.00E+04
9735.23 8.20E+04
Any help would be appreciated.
Edit:
I'm adding data for two additional curves;
Curve at temperature 21C
3.98E+07 6.30E+04
1.58E+07 6.40E+04
4.03E+06 6.60E+04
1.47E+06 6.80E+04
6.57E+05 7.00E+04
3.37E+05 7.20E+04
1.91E+05 7.40E+04
1.16E+05 7.60E+04
7.49E+04 7.80E+04
5.04E+04 8.00E+04
3.52E+04 8.20E+04
2.53E+04 8.40E+04
1.87E+04 8.60E+04
1.41E+04 8.80E+04
1.08E+04 9.00E+04
8.47E+03 9.20E+04
Curve at temperature 537C
7.91E+06 3.80E+04
3.29E+06 4.00E+04
1.51E+06 4.20E+04
7.48E+05 4.40E+04
3.95E+05 4.60E+04
2.20E+05 4.80E+04
1.28E+05 5.00E+04
7.77E+04 5.20E+04
4.87E+04 5.40E+04
3.14E+04 5.60E+04
2.08E+04 5.80E+04
1.41E+04 6.00E+04
9.73E+03 6.20E+04
6.85E+03 6.40E+04
More info about the curves - These are alternating stress (y axis), number of cycles to failure (x axis) curves for a material at different temperatures.
Thanks.
I managed to get simple example working. First of all your data must be ordered so the measurements must be sorted by temperature and each measurement must be ordered by y (stress). I used ascending order. First algorith:
compute BBOX
simply compute min and max x,y coordinates of all measurements together. This will be used for conversion between logarithmic and linear scale and also for aligning.
resample and align all measurements
so convert all of your measurements to form that it's samples are at the same y values (across all measurements). I used uniformly sampled y axis. So simply step is (ymax-ymin)/(n-1) where n is number of points of the resampled data. So all measurements will have the same size and all the y values will be the same across measurement on the same index. Missing x data will be filled with 0.
The resampling can be done in linear scale. I used piecewise cubic interpolation.
create new measurement for new temperature
so simply create new measurement again containing n-points. The y value is the same as before (so just copy it from any of the aligned measurements) and then just take 1 point from each of the 4 measurements corresponding to the same point as we are processing and cubicaly interpolate its position. However this must be done in logarithmic scale!
The valid range of temperature is between the 2nd and 3th measurement temperature.
Here preview using your data and 370 C:
And here C++/VCL example for this (just ignore the VCL stuff):
//$$---- Form CPP ----
//---------------------------------------------------------------------------
#include <vcl.h>
#include <math.h>
#pragma hdrstop
#include "win_main.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
int xs,ys; // screen resolution
Graphics::TBitmap *bmp; // back buffer bitmap for rendering
//---------------------------------------------------------------------------
// here starts the important stuff
//---------------------------------------------------------------------------
float in[4][40]= // input measureements format is: { temperature,x0,y0,x1,y1...,-1 }
{{ 21.0,
3.98E+07,6.30E+04,
1.58E+07,6.40E+04,
4.03E+06,6.60E+04,
1.47E+06,6.80E+04,
6.57E+05,7.00E+04,
3.37E+05,7.20E+04,
1.91E+05,7.40E+04,
1.16E+05,7.60E+04,
7.49E+04,7.80E+04,
5.04E+04,8.00E+04,
3.52E+04,8.20E+04,
2.53E+04,8.40E+04,
1.87E+04,8.60E+04,
1.41E+04,8.80E+04,
1.08E+04,9.00E+04,
8.47E+03,9.20E+04,
-1.0 },
{ 310.0,
9.43E+06,6.00E+04,
3.96E+06,6.20E+04,
1.78E+06,6.40E+04,
8.52E+05,6.60E+04,
4.28E+05,6.80E+04,
2.25E+05,7.00E+04,
1.23E+05,7.20E+04,
6.95E+04,7.40E+04,
4.05E+04,7.60E+04,
2.43E+04,7.80E+04,
1.49E+04,8.00E+04,
9.39E+03,8.20E+04,
-1.0 },
{ 420.0,
5.14E+08,4.80E+04,
1.35E+08,5.00E+04,
4.36E+07,5.20E+04,
1.64E+07,5.40E+04,
6.90E+06,5.60E+04,
3.18E+06,5.80E+04,
1.58E+06,6.00E+04,
8.35E+05,6.20E+04,
4.64E+05,6.40E+04,
2.69E+05,6.60E+04,
1.62E+05,6.80E+04,
1.01E+05,7.00E+04,
6.47E+04,7.20E+04,
4.25E+04,7.40E+04,
2.86E+04,7.60E+04,
1.96E+04,7.80E+04,
1.37E+04,8.00E+04,
9735.23 ,8.20E+04,
-1.0 },
{ 537.0,
7.91E+06,3.80E+04,
3.29E+06,4.00E+04,
1.51E+06,4.20E+04,
7.48E+05,4.40E+04,
3.95E+05,4.60E+04,
2.20E+05,4.80E+04,
1.28E+05,5.00E+04,
7.77E+04,5.20E+04,
4.87E+04,5.40E+04,
3.14E+04,5.60E+04,
2.08E+04,5.80E+04,
1.41E+04,6.00E+04,
9.73E+03,6.20E+04,
6.85E+03,6.40E+04,
-1.0 }};
//---------------------------------------------------------------------------
// temp and output data
//---------------------------------------------------------------------------
const n=40; // points to resmaple curves with
float dat[4][2+n+n]; // resampled input curves
float out[2+n+n]; // interpolated curve
float xmin,xmax,ymin,ymax; // BBOX
void resample(float *out,float *in,float y0,float y1) // resample and align y to range and n points and store it to out
{
float t,d1,d2,a0,a1,a2,a3,x,y,x0,x1,x2,x3;
int i,ii,i0,i1,i2,i3,nn;
// scan how many points in[] has
for (nn=0,i=1;in[i]>=0.0;i+=2) nn++;
// resample input curves to n points
out[0]=in[0]; // copy T
out[n+n+1]=-1; // end of data
for (i=0;i<n;i++)
{
// y uniformly distributed and aligned in the dat array
y=y0+((y1-y0)*float(i)/float(n-1));
ii=1+i +i ;
// check if range present
if ((y<in[1+1])||(y>in[1+nn-1+nn-1+1]))
{
out[ii+0]=0.0;
out[ii+1]=y;
continue;
}
// find i1 so in[i1] <= y < in[i1+1]
// linear search, can be replaced with binary search
for (i1=0;i1<nn;i1++) if (in[1+i1+i1+1]>=y) break;
if (in[1+i1+i1+1]>y) i1--;
// neigboring indexes
i0=i1-1; if (i0< 0) i0= 0;
i2=i1+1; if (i2>=nn) i2=nn-1;
i3=i1+2; if (i3>=nn) i3=nn-1;
// convert to array index
i0=1+i0+i0;
i1=1+i1+i1;
i2=1+i2+i2;
i3=1+i3+i3;
// parameter is based on y value
d1=y-in[i1+1];
d2=in[i2+1]-in[i1+1];
if (fabs(d2)>1e-6) t=d1/d2; else t=0.0;
// points to interpolate
x0=in[i0];
x1=in[i1];
x2=in[i2];
x3=in[i3];
// cubic interpoaltion of x
d1=0.5*(x2-x0);
d2=0.5*(x3-x1);
a0=x1;
a1=d1;
a2=(3.0*(x2-x1))-(2.0*d1)-d2;
a3=d1+d2+(2.0*(-x2+x1));
x=a0+(a1*t)+(a2*t*t)+(a3*t*t*t);
if (x<0.0) x=0.0; // just to be sure data is not messed up
// copy point
out[ii+0]=x;
out[ii+1]=y;
}
}
//---------------------------------------------------------------------------
void interpolate(float *out,float T) // interpolate out[] as n point curve from dat[4][] matching temperature T
{ // dat[][] must be ordered ascending by T,x,y
int i,ii; // valid T range is <dat[1][0],dat[2][0]>
float t,d1,d2,a0,a1,a2,a3,x,x0,x1,x2,x3,t0,t1,t2,t3;
out[0]=T; // copy T
out[n+n+1]=-1; // end of data
// parameter from T
t=(T-dat[1][0])/(dat[2][0]-dat[1][0]);
t0=dat[0][0];
t1=dat[1][0];
t2=dat[2][0];
t3=dat[3][0];
// cubic interpolation between curves
for (i=0;i<n;i++)
{
// points to interpolate
ii=1+i+i;
x0=dat[0][ii];
x1=dat[1][ii];
x2=dat[2][ii];
x3=dat[3][ii];
// logarithm scale
(x0>=xmin)?x0=log(x0/xmin)/log(xmax/xmin):x0=0.0;
(x1>=xmin)?x1=log(x1/xmin)/log(xmax/xmin):x1=0.0;
(x2>=xmin)?x2=log(x2/xmin)/log(xmax/xmin):x2=0.0;
(x3>=xmin)?x3=log(x3/xmin)/log(xmax/xmin):x3=0.0;
out[ii+1]=dat[0][ii+1]; // copy y
// too much missing data
if ((x1<=0.0)||(x2<=0.0)){ out[ii+0]=0; continue; }
// mirror missing data
if (x0<=0.0) x0=x1-((x2-x1)*(t1-t0)/(t2-t1));
if (x3<=0.0) x3=x2+((x2-x1)*(t3-t2)/(t2-t1));
// interpolate x
d1=0.5*(x2-x0);
d2=0.5*(x3-x1);
a0=x1;
a1=d1;
a2=(3.0*(x2-x1))-(2.0*d1)-d2;
a3=d1+d2+(2.0*(-x2+x1));
x=a0+(a1*t)+(a2*t*t)+(a3*t*t*t);
if (x<0.0) x=0.0; // just to be sure data is not messed up
else x=exp(x*log(xmax/xmin))*xmin; // back to linear scale
out[ii+0]=x;
}
}
//---------------------------------------------------------------------------
void minmax(float *dat,bool _reset) // compute BBOX of the curves
{
int i;
float x,y;
for (i=1;dat[i]>=0.0;)
{
x=dat[i]; i++;
y=dat[i]; i++;
if (x<=0.0) continue;
if (_reset){ xmin=xmax=x; ymin=ymax=y; _reset=false; }
if (xmin>x) xmin=x;
if (xmax<x) xmax=x;
if (ymin>y) ymin=y;
if (ymax<y) ymax=y;
}
}
//---------------------------------------------------------------------------
void toscr(float &x,float &y) // convert x,y from plot data to screen coordinates (just for rendering)
{
float x0,dx,y1,dy;
// range <0,1>
// x=(x-xmin)/(xmax-xmin); // linear
// y=(y-ymin)/(ymax-ymin); // linear
(x>=xmin)?x=log(x/xmin)/log(xmax/xmin):x=0.0; // logarithmic
(y>=ymin)?y=log(y/ymin)/log(ymax/ymin):y=0.0; // logarithmic
// view
x0=0.1*xs; dx=0.8*xs;
y1=0.9*ys; dy=0.8*ys;
// [pixels]
x=x0+x*dx;
y=y1-y*dy;
}
//---------------------------------------------------------------------------
void plot(float *dat,TColor col)// renders measurement data (just for rendering)
{
int i,e;
float x,y,r=2;
// curve
bmp->Canvas->Pen->Color=col;
bmp->Canvas->Font->Color=col;
for (e=1,i=1;dat[i]>=0.0;)
{
x=dat[i]; i++;
y=dat[i]; i++;
if (x<=0.0) continue;
toscr(x,y);
if (e)
{
bmp->Canvas->TextOutA(x,y,AnsiString().sprintf("%.0f C",dat[0]));
bmp->Canvas->MoveTo(x,y);
e=0;
}
else bmp->Canvas->LineTo(x,y);
}
// points
for (i=1;dat[i]>=0.0;)
{
x=dat[i]; i++;
y=dat[i]; i++;
if (x<=0.0) continue;
toscr(x,y);
bmp->Canvas->Ellipse(x-r,y-r,x+r,y+r);
}
}
//---------------------------------------------------------------------------
void draw() // just render of my App
{
bmp->Canvas->Brush->Color=clWhite;
bmp->Canvas->FillRect(TRect(0,0,xs,ys));
plot(dat[0],clRed);
plot(dat[1],clGreen);
plot(dat[2],clBlue);
plot(dat[3],clBlack);
plot(out,clMaroon);
Form1->Canvas->Draw(0,0,bmp);
// bmp->SaveToFile("out.bmp");
}
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner):TForm(Owner) // init of my app
{
// init backbuffer
bmp=new Graphics::TBitmap;
bmp->HandleType=bmDIB;
bmp->PixelFormat=pf32bit;
// here prepare data (important)
int i;
for (i=0;i<4;i++) minmax(in[i],i==0);
for (i=0;i<4;i++) resample(dat[i],in[i],ymin,ymax);
// here create new data for T=370[C]
interpolate(out,370.0);
// and also include it to the BBOX for rendering
minmax(out,false);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormDestroy(TObject *Sender) // not important just destructor of my App
{
delete bmp;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormResize(TObject *Sender) // not important just resize event
{
xs=ClientWidth;
ys=ClientHeight;
bmp->Width=xs;
bmp->Height=ys;
draw();
}
//-------------------------------------------------------------------------
void __fastcall TForm1::FormPaint(TObject *Sender) // not important just repaint event
{
draw();
}
//---------------------------------------------------------------------------
See function TForm1::TForm1(TComponent* Owner) on how to use this.
However physical validity is questionable You should test if this kind of interpolation leads to valid data by having 5 measurements. Use 4 to interpolate the 5th and check if they overlap If not then this might need additional tweaking like increasing the interpolation polynomial degree, or use log scale also for resampling etc ...

How to rotate an object so it faces another?

I am making a game in opengl, and i can't figure out how to make my enemy characters turn to face my player. I only need the enemy to rotate on the y axis towards the player. Then I want them to move towards him.I have tried a bunch of different methods but haven't been able to get anything to work.
There are a few things you need to decide on yourself at the beginning of the project to be used throughout the project, like the representation of positions and the orientation (as well as the setup of the screen/clip planes etc.) However, you haven't mentioned any of this. So you may have to adapt the code below to suit your game, but it should be easily adaptable and applicable.
For the following example, I'll assume that -y axis is the top of your screen.
#include <math.h> // atan2
// you need to way to represent position and directions
struct vector2{
float x;
float y;
} playerPosition, enemyPosition;
float playerRotation;
// setup the instances and values
void setup() {
// Set some default values for the positions
playerPosition.x = 100;
playerPosition.y = 100;
enemyPosition.x = 200;
enemyPosition.y = 300;
}
// called every frame
void update(float delta){
// get the direction vector between the player and the enemy. We can then use this to both calculate the rotation angle between the two as well as move the player towards the enemy.
vector2 dirToEnemy;
dirToEnemy.x = playerPosition.x - enemyPosition.x;
dirToEnemy.y = playerPosition.y - enemyPosition.y;
// move the player towards the enemy
playerPosition.x += dirToEnemy.x * delta * MOVEMENT_SPEED;
playerPosition.y += dirToEnemy.y * delta * MOVEMENT_SPEED;
// get the player angle on the y axis
playerRotation = atan2(-dirToEnemy.y, dirToEnemy.x);
}
void draw(){
// use the playerPosition and playerAngle to render the player
}
Using the above code, you should be able to move your player object around and set the angle of rotation (you need to watch out for radians/degrees of the returned and expected angle values).

Particle system physics acting weird

My particle system's physics update function seems to be incorrect. I'm aiming for all the particles to be attracted towards the mouse.
The particles move towards the mouse pointer just as expected, until they go very near. When they are near, they speed up so much, that they fly far away from the pointer and never return.
Here's the update function:
void updateParticle(particle& p,double time){
const double G=0.000000000066726;
const double POINTERMASS=1000000000000;
double squareDistance=pow(p.coords.x-pointerDevice.x,2)+pow(p.coords.y-pointerDevice.y,2)+pow(p.coords.z-pointerDevice.z,2);
if(squareDistance<0.001)
squareDistance=0.001;//to fix the possible division by zero
coords_3d_f accelerationVector={p.coords.x-pointerDevice.x,p.coords.y-pointerDevice.y,p.coords.z-pointerDevice.z};
accelerationVector=vector_scalar_multiplication(vector_unit(accelerationVector),((G*POINTERMASS)/squareDistance));
accelerationVector=vector_scalar_multiplication(accelerationVector,time);
p.velocity=vector_addition(p.velocity,accelerationVector);
p.coords.x-=p.velocity.x*time;
p.coords.y-=p.velocity.y*time;
p.coords.z-=p.velocity.z*time;
}
When the squareDistance is constant, the program looks OK, but I know it's false.
So, what am I doing wrong?
Force is inversely proportional to the square of the distance, so as the distance approaches 0, force (and acceleration) approach infinity. In other words, if the particles get very close, they also get very fast.
If you want to be physically accurate, make your pointer-object have a finite size, so that particles bounce off of it.
If you don't need to be accurate, you can make the force decrease when the particles are very close.
It's very simple: when particles get in touch with the mouse pointer squareDistance becomes 0 and produces undefined behavior for your particles by ((G*POINTERMASS)/squareDistance) because dividing by zero is illegal.
This might work better for you:
if (squareDistance >= 1.0) // 1.0 is the zero tolerance for your context of pixel distances
{
// proceed normally
accelerationVector=vector_scalar_multiplication(vector_unit(accelerationVector),((G*POINTERMASS)/squareDistance));
accelerationVector=vector_scalar_multiplication(accelerationVector,time);
}
else
{
// no acceleration
accelerationVector=/*{0, 0}*/;
}
When your particle gets very close to the mouse pointer the particle is going to have a very high velocity. When this velocity is multiplied by the time this is when the particle will jump very far away.
You can try to fix this by setting a maximum velocity.
Simulating the equation of motion involve the integration of a function at finite intervals, and so one can only approximate the function. This give rise to instability in the system. An easy and fast solution is to use a fixed-step verlet integration:
void integrate(particle& p, double t2, const particle& mouse)
{
// universal gravitational constant
const double G = 0.000000000066726;
// artificial drag
// set it to 1.0 to not have any drag
// set it to 0.0 to not have any momentum
const double drag = 0.99;
// get direction and distance between the particle and the mouse
dvec3 dir = p.pos - mouse.pos;
double dist2 = dot(dir, dir);
double dist = sqrt(dist2);
dir /= dist;
// calculate relative acceleration vector
dvec3 a = -dir * G * (p.mass + mouse.mass) / dist2;
// verlet integration
dvec3 tmp = p.pos;
p.pos += (p.pos - p.prev_pos) * drag + a * t2;
p.prev_pos = tmp;
}
void update(particle& p, double elapsed, const particle& mouse, double& accumulator)
{
// fixed timestep (arbitrary)
const double timestep = 1.0 / 120.0;
const double timestep2 = timestep * timestep;
// "accumulate" time
accumulator += elapsed;
// "consume" time
while(accumulator > timestep)
{
// perform integration
integrate(p, timestep2, mouse);
accumulator -= timestep;
}
}
Note: It use the GLM math library for clarity.