I've been trying to figure this relationship out but I can't, maybe I'm just not searching for the right thing. If I project a world-space coordinate to clip space using Vector3.Project, the X and Y coordinates make sense but I can't figure out how it's computing the Z (0..1) coordinate. For instance, if my nearplane is 1 and farplane is 1000, I project a Vector3 of (0,0,500) (camera center, 50% of distance to far plane) to screen space I get (1050, 500, .9994785)
The resulting X and Y coordinates make perfect sense but I don't understand where it's getting the resulting Z-value.
I need this because I'm actually trying to UNPROJECT screen-space coordinates and I need to be able to pick a Z-value to tell it the distance from the camera I want the world-space coordinate to be, but I don't understand the relationship between clip space Z (0-1) and world-space Z (nearplane-farplane).
In case this helps, my transformation matrices are:
World = Matrix.Identity;
//basically centered at 0,0,0 looking into the screen
View = Matrix.LookAtLH(
new Vector3(0,0,0), //camera position
new Vector3(0,0,1), //look target
new Vector3(0,1,0)); //up vector
Projection = Matrix.PerspectiveFovLH(
(float)(Math.PI / 4), //FieldOfViewY
1.6f, // AspectRatio
1, //NearPlane
1000); //FarPlane
Standard perspective projection creates a reciprocal relationship between the scene depth and the depth buffer value, not a linear one. This causes a higher percentage of buffer precision to be applied to objects closer to the near plane than those closer to the far plane, which is typically desired. As for the actual math, here's the breakdown:
The bottom-right 2x2 elements (corresponding to z and w) of the projection matrix are:
[far / (far - near) ] [1]
[-far * near / (far - near)] [0]
This means that after multiplying, z' = z * far / (far - near) - far * near / (far - near) and w' = z. After this step, there is the perspective divide, z'' = z' / w'.
In your specific case, the math works out to the value you got:
z = 500
z' = z * 1000 / (1000 - 999) - 1000 / (1000 - 999) = 499.499499499...
w' = z = 500
z'' = z' / w' = 0.998998998...
To recover the original depth, simply reverse the operations:
z = (far / (far - near)) / ((far / (far - near)) - z'')
Related
I'm porting over a C++ volumetric clustered rendering engine to C#, and the code is here: https://github.com/Kermalis/HybridRenderingEngine-CS
Now, I'm trying to get a linear depth from the current fragment, because it is required for volumetric clustered rendering.
If you're using GLM to create the projection matrix, this is easy with the following equation in your shader:
float ndc = 2.0 * depthSample - 1.0;
float linear = 2.0 * zNear * zFar / (zFar + zNear - ndc * (zFar - zNear));
return linear;
This is because GLM projection matrices are created this way:
float num = (float)Math.Tan(fovy / 2f);
mat4 result = mat4.identity();
result[0, 0] = 1f / (aspect * num);
result[1, 1] = 1f / num;
result[2, 2] = (-(zFar + zNear)) / (zFar - zNear);
result[2, 3] = -1f;
result[3, 2] = (-2f * zFar * zNear) / (zFar - zNear);
However, with C# Matrix4x4, the projection matrix is created differently:
float num = (float)Math.Tan(fieldOfView * 0.5f);
float yScale = 1f / num;
float xScale = yScale / aspectRatio;
result.M11 = xScale;
result.M22 = yScale;
result.M33 = zFar / (zNear - zFar);
result.M34 = -1f;
result.M43 = zNear * zFar / (zNear - zFar);
M33 and M43 are different than GLM's. There are more differences if you create an "off center" projection matrix as well, but I'm not sure if they will make a difference.
The issue, is when you use that same shader code with the Matrix4x4 matrices, you get incorrect values.
If you do the math, you can find why that is:
Here's what you get with 1 Near, 300 Far:
And here's what you get with 0.1 Near, 1500 Far:
Sorry for my handwriting. C1 is M33, and C2 is M43.
You can see that when you divide C1 by C2, you get different depth values for the different projection matrices. So the shader code will not work with Matrix4x4, since it is not actually transforming it to where 0 = near, 1 = far, since the parts of the matrix are different and distribute a different depth curve.
I did find that multiplying by 0.5 will fix it for some near/far values, and you can see that it is close to half difference in my drawings between the two types.
What I want to know is, how would I get depth properly with these matrices in the shader, without having to multiply each fragment by the inverse projection matrix? That's being done in the compute shader (which is why I need the same linear result), but that only gets run one time, not every frame, so I don't care about performance there.
How is the first equation derived? If that becomes clear to me (trust me, I tried to make sense of it), then another one can be created for Matrix4x4, or any other custom one that is passed in. I know an inverse projection matrix will do the trick, but it'll be awfully slow. I can't think of any other robust solution that will cover any matrix. I just don't understand how that shader equation is created or how it applies the inverse.
I am trying to draw an arc between two points that represents a projectile's path. The angle that the projectile leaves point A at is known, and the X/Y coordinates of both points are known.
I'm trying to figure out the math behind this, and how to draw it up in c#.
Here is my failed attempt, based on some path examples I found
var g = new StreamGeometry();
var xDistance = Math.Abs(pointA.X - pointB.X);
var yDistance = Math.Abs(pointA.Y - pointB.Y);
var angle = 60;
var radiusX = xDistance / angle;
var radiusY = yDistance / angle;
using (var gc = g.Open())
{
gc.BeginFigure(
startPoint: pointA,
isFilled: false,
isClosed: false);
gc.ArcTo(
point: pointB,
size: new Size(radiusX, radiusY),
rotationAngle: 0d,
isLargeArc: false,
sweepDirection: SweepDirection.Clockwise,
isStroked: true,
isSmoothJoin: false);
}
Any help would be greatly appreciated!
Edit #2 (added clarity): For this problem assume that physics play no role (no gravity, velocity, or any outside forces). The projectile is guaranteed to land at point B and move along a parabolic path. The vertex will be halfway between point A and point B on the horizontal axis. The angle that it launches at is the angle up from the ground (horizontal).
So Point A (Ax, Ay) is known.
Point B (Bx, By) is known.
The angle of departure is known.
The X half of the vertex is known (Vx = Abs(Ax - Bx)).
Does this really boil down to needing to figure out the Y coordinate of the vertex?
Following on from the comments, we need a quadratic Bezier curve. This is defined by 3 points, the start, end, and a control point:
It is defined by the following equation:
We therefore need to find P1 using the given conditions (note that the gravity strength is determined implicitly). For a 2D coordinate we need two constraints / boundary conditions. They are given by:
The tangent vector at P0:
We need to match the angle to the horizontal:
The apex of the curve must be directly below the control point P1:
Therefore the vertical coordinate is given by:
[Please let me know if you would like some example code for the above]
Now for how to add a quadratic Bezier; thankfully, once you have done the above, it is not too difficult
The following method creates the parabolic geometry for the simple symmetric case. The angle is measured in degrees counterclockwise from the horizontal.
public Geometry CreateParabola(double x1, double x2, double y, double angle)
{
var startPoint = new Point(x1, y);
var endPoint = new Point(x2, y);
var controlPoint = new Point(
0.5 * (x1 + x2),
y - 0.5 * (x2 - x1) * Math.Tan(angle * Math.PI / 180));
var geometry = new StreamGeometry();
using (var context = geometry.Open())
{
context.BeginFigure(startPoint, false, false);
context.QuadraticBezierTo(controlPoint, endPoint, true, false);
}
return geometry;
}
A body movement subject only to the force of gravity (air resistance is ignored) can be evaluated with the following equations:
DistanceX(t) = dx0 + Vx0·t
DistanceY(t) = dy0 + Vy0·t - g/2·t^2
Where
g : gravity acceleration (9.8 m/s^2)
dx0 : initial position in the X axis
dy0 : initial position in the Y axis
Vy0 : initial X velocity component (muzzle speed)
Vy0 : initial Y velocity component (muzzle speed)
Well that doesn't seem very helpful, but lets investigate further. Your cannon has a muzzle speed V we can consider constant, so Vx0 and Vy0 can be written as:
Vx0 = V·cos(X)
Vy0 = V·sin(X)
Where X is the angle at which you are shooting. Ok, that seems interesting, we finally have an input that is useful to whoever is shooting the cannon: X. Lets go back to our equations and rewrite them:
DistanceX(t) = dx0 + V·cos(X)·t
DistanceY(t) = dy0 + V·sin(X)·t - g/2·t^2
And now, lets think through what we are trying to do. We want to figure out a way to hit a specific point P. Lets give it coordinates: (A, B). And in order to do that, the projectile has to reach that point in both projections at the same time. We'll call that time T. Ok, lets rewrite our equations again:
A = dx0 + V·cos(X)·T
B = dy0 + V·sin(X)·T - g/2·T^2
Lets get ourselves rid of some unnecessary constants here; if our cannon is located at (0, 0) our equations are now:
A = V·cos(X)·T [1]
B = V·sin(X)·T - g/2·T^2 [2]
From [1] we know that: T = A/(V·cos(X)), so we use that in [2]:
B = V·sin(X)·A/(V·cos(X)) - g/2·A^2/(V^2·cos^2(X))
Or
B = A·tan(X) - g/2·A^2/(V^2*cos^2(X))
And now some trigonometry will tell you that 1/cos^2 = 1 + tan^2 so:
B = A·tan(X) - g/2·A^2/V^2·(1+tan^2(X)) [3]
And now you have quadratic equation in tan(X) you can solve.
DISCLAIMER: typing math is kind of hard, I might have an error in there somewhere, but you should get the idea.
UPDATE The previous approach would allow you to solve the angle X that hits a target P given a muzzle speed V. Based on your comments, the angle X is given, so what you need to figure out is the muzzle speed that will make the projectile hit the target with the specified cannon angle. If it makes you more comfortable, don't think of V as muzzle speed, think of it as a form factor of the parabola you are trying to find.
Solve Vin [3]:
B = A·tan(X) - g/2·A^2/V^2·(1+tan^2(X))
This is a trivial quadratic equation, simply isolate V and take the square root. Obviously the negative root has no physical meaning but it will also do, you can take any of the two solutions. If there is no real number solution for V, it would mean that there is simply no possible shot (or parabola) that reaches P(angle X is too big; imagine you shoot straight up, you'll hit yourself, nothing else).
Now simply eliminate t in the parametrized equations:
x = V·cos(X)·t [4]
y = V·sin(X)·t - g/2·t^2 [5]
From [4] you have t = x/(V·cos(X)). Substitute in [5]:
y = tan(X)·x - g·x^2 /(2·V^2*cos^2(X))
And there is your parabola equation. Draw it and see your shot hit the mark.
I've given it a physical interpretation because I find it easier to follow, but you could change all the names I've written here to purely mathematical terms, it doesn't really matter, at the end of the day its all maths and the parabola is the same, any which way you want to think about it.
For a screen overlay I am making for a 3-dimensional game, I need to display icons over certain locations in the 3D world. I've managed to get several variables from the game that should make this possible:
Player position (x,y,z)
Camera position (x,y,z)
Point position (x,y,z)
Camera Angle(FOV)
Using these variables I managed to get the right and left edge of the camera view. Using these 2 variables I am able to get the point between 0 and 1 where the icon should be displayed on the x axis. (0 is on the left edge, 1 is right edge) This seems to work quite well, but only if I am aligned to either the X or Z axis, as shown in the following example:
I've been trying to fix this using the following rotation matrix:
[ Math.Cos(angle), -Math.Sin(angle) ]
[ Math.Sin(angle), Math.Cos(angle) ]
What I do is, I put the player position, camera position and the camera edge positions in the matrix with as rotation point the world point. The problem is, as soon as I put the angle amount at 90 degrees, the X and Y are being flipped. I've been trying to find a solution for this for a few days now, but I can't find it, so I hope anyone can push me in the right direction here. Below are a few parts of my code that might help in finding the solution:
float PCDistXZ = (float)Math.Sqrt(deltaPCx * deltaPCx + deltaPCz * deltaPCz); // X/Z distance from the world point to the camera
Point fakeAvatarPos = RotateAround(new Point((int)point.x, (int)point.z), new Point((int)avatar.x, (int)avatar.z), (int)90);
Point fakeCameraPos = RotateAround(new Point((int)point.x, (int)point.z), new Point((int)camera.x, (int)camera.z), (int)90);
double edgeRight = fakeC.X + (Math.Sin(45) * PCDistXZ);
double edgeLeft = fakeC.X - (Math.Sin(45) * PCDistXZ);
float edgeTest_ScreenPositionX = (1 - (float)((edgeRight - P.x) / (edgeRight - edgeLeft))) * screenWidth;
public static Point RotateAround(Point pCenter,Point pPoint, float pAngle)
{
double angle = (pAngle * Math.PI) / 180;
double[,] matrix = new Double[2, 2] {
{ Math.Cos(angle), Math.Sin(angle) },
{ Math.Sin(angle), Math.Cos(angle) }
};
double xOffset = pPoint.X - pCenter.X;
double yOffset = pPoint.Y - pCenter.Y;
Point newPoint = new Point(
(int)(((pPoint.X - xOffset) * matrix[0, 0]) - ((pPoint.Y - xOffset) * matrix[0, 1])),
(int)(((pPoint.X - yOffset) * matrix[1, 0]) + ((pPoint.Y - yOffset) * matrix[1, 1]))
);
newPoint.X += (int)xOffset;
newPoint.Y += (int)yOffset;
return new Point(newPoint.X,newPoint.Y);
}
Note: I've changed the names of some of the variables to more understandable one, so it could be possible that there are inconsistencies in the names.
EDIT: I found out about view- and projection matrices. I might be able to use those to convert the 3D position to screen. I'm not sure if it's possible to make this matrices with the limited information I have though.
I've spent a few hours today researching how random terrain generation tends to be done and after reading about the plasma fractal (midpoint displacement and diamond square algo's) I decided to try and have a go at implementing one. My result was actually not terriable, but I have these horrible square/line/grid type artefacts that I just can not seem to get rid of!
When rendered as a gray scale image my height map looks something like:
height map http://sphotos-d.ak.fbcdn.net/hphotos-ak-ash3/535816_10151739010123327_225111175_n.jpg
Obviously there is a fair amount of code involved in this but I will try to post what is only relevant. I've not not posted the code that turns it into a texture for example, but do not worry I have already tried just filling my height array with a smooth gradient and the texture comes out fine :)
I begin by setting the four corners of the map to random values between 0 and 1 and then start the recursive displacement algo:
public void GenerateTerrainLayer()
{
//set the four corners of the map to have random values
TerrainData[0, 0] = (float)RandomGenerator.NextDouble();
TerrainData[GenSize, 0] = (float)RandomGenerator.NextDouble();
TerrainData[0, GenSize] = (float)RandomGenerator.NextDouble();
TerrainData[GenSize, GenSize] = (float)RandomGenerator.NextDouble();
//begin midpoint displacement algorithm...
MidPointDisplace(new Vector2_I(0, 0), new Vector2_I(GenSize, 0), new Vector2_I(0, GenSize), new Vector2_I(GenSize, GenSize));
}
TerrainData is simply a 2D array of floats*. Vector2_I is just my own integer vector class. The last four functions are MidPointDisplace which is the recursive function, CalculateTerrainPointData which averages 2 data values and adds some noise, CalculateTerrainPointData2 which averages 4 data values and adds some noise and has a slightly higher scale value (its only used for center points) and finally my noise function which atm is just some random noise and not a real noise like perlin etc. They look like this:
private void MidPointDisplace(Vector2_I topleft, Vector2_I topright, Vector2_I bottomleft, Vector2_I bottomright)
{
//check size of square working on.. if its shorter than a certain amount stop the algo, we've done enough
if (topright.X - topleft.X < DisplacementMaxLOD)
{
return;
}
//calculate the positions of all the middle points for the square that has been passed to the function
Vector2_I MidLeft, MidRight, MidTop, MidBottom, Center;
MidLeft.X = topleft.X;
MidLeft.Y = topleft.Y + ((bottomleft.Y - topleft.Y) / 2);
MidRight.X = topright.X;
MidRight.Y = topright.Y + ((bottomright.Y - topright.Y) / 2);
MidTop.X = topleft.X + ((topright.X - topleft.X) / 2);
MidTop.Y = topleft.Y;
MidBottom.X = bottomleft.X + ((bottomright.X - bottomleft.X) / 2);
MidBottom.Y = bottomleft.Y;
Center.X = MidTop.X;
Center.Y = MidLeft.Y;
//collect the existing data from the corners of the area passed to algo
float TopLeftDat, TopRightDat, BottomLeftDat, BottomRightDat;
TopLeftDat = GetTerrainData(topleft.X, topleft.Y);
TopRightDat = GetTerrainData(topright.X, topright.Y);
BottomLeftDat = GetTerrainData(bottomleft.X, bottomleft.Y);
BottomRightDat = GetTerrainData(bottomright.X, bottomright.Y);
//and the center
//adverage data and insert for midpoints..
SetTerrainData(MidLeft.X, MidLeft.Y, CalculateTerrainPointData(TopLeftDat, BottomLeftDat, MidLeft.X, MidLeft.Y));
SetTerrainData(MidRight.X, MidRight.Y, CalculateTerrainPointData(TopRightDat, BottomRightDat, MidRight.X, MidRight.Y));
SetTerrainData(MidTop.X, MidTop.Y, CalculateTerrainPointData(TopLeftDat, TopRightDat, MidTop.X, MidTop.Y));
SetTerrainData(MidBottom.X, MidBottom.Y, CalculateTerrainPointData(BottomLeftDat, BottomRightDat, MidBottom.X, MidBottom.Y));
SetTerrainData(Center.X, Center.Y, CalculateTerrainPointData2(TopLeftDat, TopRightDat, BottomLeftDat, BottomRightDat, Center.X, Center.Y));
debug_displacement_iterations++;
//and recursively fire off new calls to the function to do the smaller squares
Rectangle NewTopLeft = new Rectangle(topleft.X, topleft.Y, Center.X - topleft.X, Center.Y - topleft.Y);
Rectangle NewTopRight = new Rectangle(Center.X, topright.Y, topright.X - Center.X, Center.Y - topright.Y);
Rectangle NewBottomLeft = new Rectangle(bottomleft.X, Center.Y, Center.X - bottomleft.X, bottomleft.Y - Center.Y);
Rectangle NewBottomRight = new Rectangle(Center.X , Center.Y, bottomright.X - Center.X, bottomright.Y - Center.Y);
MidPointDisplace(new Vector2_I(NewTopLeft.Left, NewTopLeft.Top), new Vector2_I(NewTopLeft.Right, NewTopLeft.Top), new Vector2_I(NewTopLeft.Left, NewTopLeft.Bottom), new Vector2_I(NewTopLeft.Right, NewTopLeft.Bottom));
MidPointDisplace(new Vector2_I(NewTopRight.Left, NewTopRight.Top), new Vector2_I(NewTopRight.Right, NewTopRight.Top), new Vector2_I(NewTopRight.Left, NewTopRight.Bottom), new Vector2_I(NewTopRight.Right, NewTopRight.Bottom));
MidPointDisplace(new Vector2_I(NewBottomLeft.Left, NewBottomLeft.Top), new Vector2_I(NewBottomLeft.Right, NewBottomLeft.Top), new Vector2_I(NewBottomLeft.Left, NewBottomLeft.Bottom), new Vector2_I(NewBottomLeft.Right, NewBottomLeft.Bottom));
MidPointDisplace(new Vector2_I(NewBottomRight.Left, NewBottomRight.Top), new Vector2_I(NewBottomRight.Right, NewBottomRight.Top), new Vector2_I(NewBottomRight.Left, NewBottomRight.Bottom), new Vector2_I(NewBottomRight.Right, NewBottomRight.Bottom));
}
//helper function to return a data value adveraged from two inputs, noise value added for randomness and result clamped to ensure a good value
private float CalculateTerrainPointData(float DataA, float DataB, int NoiseX, int NoiseY)
{
return MathHelper.Clamp(((DataA + DataB) / 2.0f) + NoiseFunction(NoiseX, NoiseY), 0.0f, 1.0f) * 1.0f;
}
//helper function to return a data value adveraged from four inputs, noise value added for randomness and result clamped to ensure a good value
private float CalculateTerrainPointData2(float DataA, float DataB, float DataC, float DataD, int NoiseX, int NoiseY)
{
return MathHelper.Clamp(((DataA + DataB + DataC + DataD) / 4.0f) + NoiseFunction(NoiseX, NoiseY), 0.0f, 1.0f) * 1.5f;
}
private float NoiseFunction(int x, int y)
{
return (float)(RandomGenerator.NextDouble() - 0.5) * 0.5f;
}
Ok thanks for taking the time to look - hopefully someone knows where this grid-like pattern is appearing from :)
*edit - accidently wrote ints, corrected to floats
I identified 3 problems in your code. (2 of which are related)
You don't scale down the randomness in each step. There must be a reduction of the randomness in each step. Otherwise you get white(-ish) noise. You choose a factor (0.5-0.7 worked fine for my purposes) and multiply the reduction by alpha in each recursion and scale the generated random number by that factor.
You swapped the diamond and square step. First the diamonds, then the squares. The other way round is impossible (see next).
Your square step uses only points in one direction. This one probably causes the rectangular structures you are talking about. The squares must average the values to all four sides. This means that the square step depends on the point generated by the diamond step. And not only the diamond step of the rectangle you are currently looking at, also of the rectangles next to it. For values outside of the map, you can either wrap, use a fixed value or only average 3 values.
I see a problem in your CalculateTerrainPointData implementation: you're not scaling down the result of NoiseFunction with each iteration.
See this description of the Midpoint Displacement algorithm:
Start with a single horizontal line segment.
Repeat for a sufficiently large number of times:
Repeat over each line segment in the scene:
Find the midpoint of the line segment.
Displace the midpoint in Y by a random amount.
Reduce the range for random numbers.
A fast way to do it in your code without changing too much is by adding some scale parameter to MidPointDisplace (with default set to 1.0f) and CalculateTerrainPointData; use it in CalculateTerrainPointData to multiply result of NoiseFunction; and reduce it with each recursive call to MidPointDisplace(..., 0.5f * scale).
Not sure though if that is the only cause to your image looking wrong or there are other problems.
According to Wikipedia's summary of midpoint displacement, only the average for the center most point get noise added to it - try only adding noise via CalculateTerrainPointData2 & removing the noise in CalculateTerrainPointData.
Lets Say I have a 3d Cartesian grid. Lets also assume that there are one or more log spirals emanating from the origin on the horizontal plane.
If I then have a point in the grid I want to test if that point is in one of the spirals. I acutally want to test if it within a certain range of the spirals but determining if it is on the point is a good start.
So I guess the question has a couple parts.
How to generate the arms from parameters (direction, tightness)
How to tell if a point in the grid is in one of the spiral arms
Any ideas? I have been googling all day and don't feel I am any closer to a solution than when I started.
Here is a bit more information that might help:
I don't actually need to render the spirals. I want to set the pitch and rotation and then pass a point to a method that can tell me if the point I passed is within the spiral (within a given range of any point on the spiral). Based on the value returned (true or false) my program will make a decision on whether or not something exists at the point in space.
How to parametrically define the log spirals (pitch and rotation and ??)
Test if a point (x, y, z) is withing a given range of any point on the spiral.
Note: Both of the above would be just on the horizontal plane
These are two functions defining an anti-clockwise spiral:
PolarPlot[{
Exp[(t + 10)/100],
Exp[t/100]},
{t, 0, 100 Pi}]
Output:
These are two functions defining a clockwise spiral:
PolarPlot[{
- Exp[(t + 10)/100],
- Exp[t/100]},
{t, 0, 100 Pi}]
Output:
Cartesian coordinates
The conversion Cartesian <-> Polar is
(1) Ro = Sqrt[x^2+y^2]
t = ArcTan[y/x]
(2) x = Ro Cos[t]
y = Ro Sin[t]
So, If you have a point in Cartesian Coords (x,y) you transform it to your equivalent polar coordinates using (1). Then you use the forula for the spiral function (any of the four mentinoned above the plots, or similar ones) putting in there the value for t, and obtaining Ro. The last step is to compare this Ro with the one we got from the coordinates converion. If they are equal, the point is on the spiral.
Edit Answering your comment
For a Log spiral is almost the same, but with multiple spirals you need to take care of the logs not going to negative values. That's why I used exponentials ...
Example:
PolarPlot[{
Log[t],
If[t > 3, Log[ t - 2], 0],
If[t > 5, Log[ t - 4], 0]
}, {t, 1, 10}]
Output:
Not sure this is what you want, but you can reverse the log function (or "any" other for that matter).
Say you have ln A = B, to get A from B you do e^B = A.
So you get your point and pass it as B, you'll get A. Then you just need to check if that A (with a certain +- range) is in the values you first passed on to ln to generate the spiral.
I think this might work...
Unfortunately, you will need to know some mathematics notation anyway - this is a good read about the logarithmic sprial.
http://en.wikipedia.org/wiki/Logarithmic_spiral
we will only need the top 4 equations.
For your question 1
- to control the tightness, you tune the parameter 'a' as in the wiki page.
- to control the direction, you offset theta by a certain amount.
For your question 2
In floating point arithmetic, you will never get absolute precision, which mean there will be no point falling exactly on the sprial. On the screen, however, you will know which pixel get rendered, and you can test whether you are hitting a point that is rendered.
To render a curve, you usually render it as a sequence of line segments, short enough so that overall it looks like a curve. If you want to know whether a point lies within certain distance from the spiral, you can render the curve (on a off-screen buffer if you wish) by having thicker lines.
here a C++ code drawing any spiral passing where the mouse here
(sorry for my English)
int cx = pWin->vue.right / 2;
int cy = pWin->vue.bottom / 2;
double theta_mouse = atan2((double)(pWin->y_mouse - cy),(double)(pWin->x_mouse - cx));
double square_d_mouse = (double)(pWin->y_mouse - cy)*(double)(pWin->y_mouse - cy)+
(double)(pWin->x_mouse - cx)*(double)(pWin->x_mouse - cx);
double d_mouse = sqrt(square_d_mouse);
double theta_t = log( d_mouse / 3.0 ) / log( 1.19 );
int x = cx + (3 * cos(theta_mouse));
int y = cy + (3 * sin(theta_mouse));
MoveToEx(hdc,x,y,NULL);
for(double theta=0.0;theta < PI2*5.0;theta+=0.1)
{
double d = pow( 1.19 , theta ) * 3.0;
x = cx + (d * cos(theta-theta_t+theta_mouse));
y = cy + (d * sin(theta-theta_t+theta_mouse));
LineTo(hdc,x,y);
}
Ok now the parameter of spiral is 1.19 (slope) and 3.0 (radius at center)
Just compare the points where theta is a mutiple of 2 PI = PI2 = 6,283185307179586476925286766559
if any points is near of a non rotated spiral like
x = cx + (d * cos(theta));
y = cy + (d * sin(theta));
then your mouse is ON the spiral... I searched this tonight and i googled your past question