I am updating one of our older apps from vb6 to c# and in the process have to recreate a custom control that the original programmer designed. The control simply took the dimensions of an object, rectangular or conical, and placed an outline sketch of the object in 3D (2.5D technically I think). Of course, the code for the control or the algorithim is nowhere to be had.
Knowing nothing about this before hand I have gotten pretty much everything replicated except the perspective. I am using this code that I found on another answer here.
}
double w = 400;
double h = 250;
double t = 0.6; // tilt angle
double X = w / 2 - x;
double Y = h / 2 - y;
double a = h / (h + Y * Math.Sin(t));
double u = a * X + w / 2;
double v = a * Y * Math.Cos(t) + h / 2;
}
The last piece I need help with though is turning the perspective about 30 degrees left-to-right so I'm not looking at straight on.
Thanks for any help.
As the commenter says: You should use matrices to make your live easy.
Rotation could be easily done by multiplying the 2 matrices, a rotation matrix and a perspective matrix this way:
// We don't have a view matrix here
Matrix4x4 modelProjection = Matrix4x4.Perspective(400, 250, Math.PI / 4) * Matrix4x4.RotationX(degree);
// Get a specifics point position, use x and y to determine the screen position and z for the z-order
Vector3 screenPosition = modelProjection * myPosition; // myPosition is a Vector3
For running the code you have to do some things:
Implement a C# matrix, or get it from anywhere else. Here is a excellent source for implementing matrices.
Related
OxyPlot is a cross-platform plotting library for .NET, very convenient for making plots,
Now there's a situation here, I have to draw a 95% confidence ellipse to an XY scatter plot.
Oxyplot provides with following annotation:-
Given here Ellipse Annotation(OxyPlot.Annotations) gives only following properties to add ellipse-
We don't have any rotation property or method here, IRender provides several draw methods to override but none of the methods have double angled rotation argument or so. Neither the documentation has provides any direct solution to it:-
Then how to draw this:-
*I was facing this issue for one of my assignment, and came up with a solution after going through the following forums discussion to get hints on how to generate such an ellipse.
https://github.com/oxyplot/oxyplot/issues/268
https://oxyplot.userecho.com/en/communities/1/topics/598-ellipse-annotation-rotation
Please add more solutions if anyone else has :-
Based on the link shared (in Quest.) best and easiest solution here was to draw an ellipse using PolygonAnnotation, which takes List of co-ordinate points,
Let's say if you give four co-ordinate points A,B,C,D--- polygonAnnotation will give me a closed 4-gon~quadrilateral sort of structure based on kind of points taken.
Now if you increase the number of points from 4 to 6--- it will give you hexagon, and so on.
Now at pixel level you can give infinite-number/discrete-number of points eclipsing over 360 degree.
So here we need an algorithm/equation of point on an 2D ellipse- given following inputs (based on this case):-
Center of ellipse (h,k)
rotation angle of the ellipse axis
major axis (a)
minor axis (b)
theta angle from the x-axis
private void GeneratePolygonAsEllipse(PolygonAnnotation polygonAnnotation)
{
double step = 2 * Math.PI / 200;
var h = xCenter;
var k = yCenter;
var rotation = AngleOfRotation;
var a = MajorAxisLength;
var b = MinorAxisLength;
for (double theta = 0; theta < 2 * Math.PI; theta += step)
{
var x = a * Math.Cos(rotation) * Math.Cos(theta) + b * Math.Sin(rotation) * Math.Sin(theta) + h;
var y = b * Math.Cos(rotation) * Math.Sin(theta) + a * Math.Sin(rotation) * Math.Cos(theta) + k;
polygonAnnotation.Points.Add(new DataPoint(x, y));
}
}
I hope above stipulated sample method equation can be useful to other folks like me looking for solution. I couldn't find direct solution anywhere else so I have added my solution here, that can be used as reference.
Result:-
if anyone can come-up with other solutions like how to use IRender or anything else, would be great to look at them.
Currently I have the following code:
AdjustableArrowCap arrow = new AdjustableArrowCap(10, 15, false);
penStateOutline.CustomEndCap = arrow;
And it draws this:
I have tried all day to make the arrow point to the ellipse itself rather than the center of it..
Update (I was wrong about the cap extending the line; it doesn't!)
To let the line and its cap end at the cirlce's outside you need to make the line shorter by the radius of the circle.
There are two approaches:
You can find a new endpoint that sits on the circle by calculating it, either with Pythagoras or by trigonometry. Then replace the endpoint, i.e. the circle's center, when drawing the line or curve by that new point.
Or put the other way round: You need to calculate a point on the circle as the new endpoint of the line.
It requires a little math, unless the line is horizontal or vertical...
This will work well for straight lines but for curves it may cause considerable changes in the shape, depending on how close the points get and how curved the shape is.
This may or may not be a problem.
To avoid it you can replace the curve points by a series of line points that are close enough to look like a curve when drawn. From the list of points we subtract all those that do not lie inside the circle.
This sounds more complicated than it is as there is a nice class called GraphicsPath that will allow you to add a curve and then flatten it. The result is a more or less large number of points. The same class also allows you to determine whether a point lies inside a shape, i.e. our case inside the circle.
To implement the latter approach, here is a routine that transforms a list of curve points to a list of line points that will end close to the circle..:
void FlattenCurveOutside(List<Point> points, float radius)//, int count)
{
using (GraphicsPath gp = new GraphicsPath())
using (GraphicsPath gpc = new GraphicsPath())
{
// firt create a path that looks like our circle:
PointF l = points.Last();
gpc.AddEllipse(l.X - radius, l.Y - radius, radius * 2, radius* 2);
// next one that holds the curve:
gp.AddCurve(points.ToArray());
// now we flatten it to a not too large number of line segments:
Matrix m = new Matrix(); // dummy matrix
gp.Flatten(m, 0.75f); // <== play with this param!!
// now we test the pathpoints from bach to front until we have left the circle:
int k = -1;
for (int i = gp.PathPoints.Length - 1; i >= 0; i--)
{
if ( !gpc.IsVisible(gp.PathPoints[i])) k = i;
if (k > 0) break;
}
// now we know how many pathpoints we want to retain:
points.Clear();
for (int i = 1; i <= k; i++)
points.Add(Point.Round(gp.PathPoints[i]));
}
}
Note that when the last part of the curve is too straight the result may look a little jagged..
Update 2
To implement the former approach here is a function that returns a PointF on a circle of radius r and a line connecting a Point b with the circle center c:
PointF Intersect(Point c, Point a, int rad)
{
float dx = c.X - a.X;
float dy = c.Y - a.Y;
var radians = Math.Atan2(dy, dx);
var angle = 180 - radians * (180 / Math.PI);
float alpha = (float)(angle * Math.PI / 180f);
float ry = (float)(Math.Sin(alpha) * rad );
float rx = (float)(Math.Cos(alpha) * rad );
return new PointF(c.X + rx, c.Y - ry);
}
The result thankfully looks rather similar:
The math can be simplified a little..
To apply it you can get the new endpoint and replace the old one:
PointF I = Intersect(c, b, r);
points2[points2.Count - 1] = Point.Round(I);
Thank you so much to #TaW for helping.
Unfortunately the answer he provided did not match my stupid OCD..
You made me think about the problem from a different perspective and I now have found a solution that I'll share if anyone else needs, note that the solution is not perfect.
public static Point ClosestPointOnCircle(int rad, PointF circle, PointF other)
{
if (other.X == circle.X && other.Y == circle.Y) // dealing with division by 0
other.Y--;
return new Point((int)Math.Floor(circle.X + rad * ((other.X - circle.X) / (Math.Sqrt(Math.Pow(other.X - circle.X, 2) + Math.Pow(other.Y - circle.Y, 2))))), (int)Math.Floor(circle.Y + rad * ((other.Y - circle.Y) / (Math.Sqrt(Math.Pow(other.X - circle.X, 2) + Math.Pow(other.Y - circle.Y, 2))))));
}
What the code does is return a point on the circle that is the closest to the another point, then, I used the curve middle point as the other point to change the end point of the curve.
Then I used the arrow cap as normal and got this:image
Which is good enough for my project.
I'm trying to follow a tutorial which is written in a math programming language I'm not familiar with and attempting to convert the tutorial to C# code for Unity 3d.
See here: http://blog.wolfram.com/2011/07/28/how-i-made-wine-glasses-from-sunflowers/
float theta = Mathf.PI * (3 - Mathf.Sqrt(5));
for (int i = 0; i < spawnPoints.Length; i++)
{
float r = (radius / 2) * Mathf.Sqrt(i) / Mathf.Sqrt(points);
float a = theta * i;
Vector3 coords = transform.TransformDirection( new Vector3(Mathf.Cos(a) * r, 0, Mathf.Sin(a) * r) )+transform.position;
spawnPoints[i] = coords;
}
This if course generates the flat phillotaxic arrangement in 2d. I'm trying to modify Y (up) axis for depth (creating the sphere).
I cannot seem to set the Y (up) axis correctly in proportion with i and radius.
Considering the tutorial above, how should I be calculating Y?
The 3D version is called a spherical Fibonacci lattice. This paper gives a nice explanation. This stackoverflow post has more links.
I am attempting to calculate the angle required to fire a projectile in order to hit a specific coordinate.
My projectile is located a random coordinate and my target coordinate at a static coordinate.
I ended up running across the following equation on Wikipedia for calculating the angle required to hit a coordinate at (x,y) from (0,0):
I have made some attempts to understand this and other formula and attempted the following implementation (I am using c# and XNA).
double y = source.Y - target.Y;
double x = Vector2.Distance(source, target);
double v = 1440; //velocity
double g = 25; //gravity
double sqrt = (v*v*v*v) - (g*(g*(x*x) + 2*y*(v*v)));
sqrt = Math.Sqrt(sqrt);
double angleInRadians = Math.Atan(((v*v) + sqrt)/(g*x));
I have also attempted the following, which resulted in an identical angle where the values of v and g remain the same.
double targetX = target.X - source.X;
double targetY = -(target.Y - source.Y);
double r1 = Math.Sqrt((v*v*v*v) - g*(g*(target.X*target.X) + ((2*target.Y)*(v*v))));
double a1 = ((v*v) + r1)/(g*target.X);
angleInRadians = -Math.Atan(a1);
if (targetX < 0)
{
angleInRadians -= 180/180*Math.PI;
}
My conjecture is that even in my (assumed) attempt to zero out the source coordinate, that I am still not performing the calculation correctly for coordinates with a non (0,0) source and different elevations.
Below is an image that depicts my coordinate system. It is the default for XNA.
Thanks to the help in the comments the solution to find this angle ended up requiring that the positions be translated to a (0,0) based system. For anyone looking for the same scenario the final working solution was:
double x = -(source.x - target.x);
double y = (source.y - target.y);
double v = 1440; //m/s
double g = 25; //m/s
double sqrt = (v*v*v*v) - (g*(g*(x*x) + 2*y*(v*v)));
sqrt = Math.Sqrt(sqrt);
angleInRadians = Math.Atan(((v*v) + sqrt)/(g*x));
Then to convert the radians into a vector that works with XNA, perform the following conversion:
Vector2 angleVector = new Vector2(-(float)Math.Cos(angleInRadians), (float)Math.Sin(angleInRadians));
I think the real problem lies in the use of arctan. Because the range is limited to -pi/2..pi/2 results are only in the right half plane.
Use arctan2 to get the proper coordinates:
angleInRadians = Math.Atan2(((v*v) + tmp), (g*x));
I'm trying to rotate a 3D object on its Z axis (up/down).
public void RotateY(float angle)
{
foreach (CoordinateVertices cv in this.GetAll<CoordinateVertices>())
{
for (int i = 0; i < cv.Coordinates.Length; i++)
{
Vector3 old = cv.Coordinates[i];
float theta = Math.Atan2(old.Y, old.X) + angle;
float rayon = Math.Sqrt(Math.Pow(old.X, 2) + Math.Pow(old.Y, 2));
cv.Coordinates[i] = new Vector3(Math.Cos(theta) * rayon, Math.Sin(theta) * rayon, old.Z);
}
}
}
The trigonometry is fairly simple, and it seems to work fine, but for some reason, my 3D object gets cut in half.
Does anybody have an idea of what's going on? I would have posted this on the maths StackExchange, but it might be a problem with my programming too, and the trigonometry is really simple.
Edit: The following is an alternative for the doing the same as the above. It took me a few minutes to realize the following solution is identical to the code initially posted.
It should look like this:
double Xnew = X * cos(theta) + Y * sin(theta);
double Ynew = Y * cos(theta) - X * sin(theta);
Or in your code:
public void RotateY(float angle)
{
foreach (CoordinateVertices cv in this.GetAll<CoordinateVertices>())
{
for (int i = 0; i < cv.Coordinates.Length; i++)
{
Vector3 old = cv.Coordinates[i];
float xnew = old.X * Math.Cos(angle) + old.Y * Math.Sin(angle);
float ynew = old.Y * Math.Cos(angle) - old.X * Math.Sin(angle);
cv.Coordinates[i] = new Vector3(xnew, ynew, old.Z);
}
}
}
The above code assumes you're rotating about the origin. If you're not rotating about the origin, you just need to translate to the origin, rotate, then translate back.
See here for more details: http://en.wikipedia.org/wiki/Transformation_matrix#Rotation
As has been noted, nothing wrong with your code. However, you may also be interested in using the Transform function (which can operate on your entire array of coordinates at once). Vector3.Transform (Vector3[], Matrix). You can get your rotation with a rotation matrix calculated for a given angle, theta, about any axis. I would expect this to be significantly faster for large numbers of points. (Less trig calculations, and possibly hardware acceleration)
Actually, the bug disappeared, out of nowhere. I went on to test some more values, and they worked. I went back to the same value as before, and it worked. This is ridiculous, it always happens to me.
What's the name for that? Bugs that disappear by themselves.