I have a square, which goes from -1 to 1 in x and y.
Choosing a random point in this square is pretty easy:
Random r = new Random();
float x = (float)Math.Round(r.NextDouble() * 2 - 1, 4);
float y = (float)Math.Round(r.NextDouble() * 2 - 1, 4);
This gives me any point, with equal probability, in my square.
It woud also be pretty easy to just remove a section of the square from the possibilities
Random r = new Random();
float x = (float)Math.Round(r.NextDouble() * 1.5 - 1, 4);
float y = (float)Math.Round(r.NextDouble() * 2 - 1, 4);
But what I'm really struggling to do, is to weight the random towards a certain zone. Specifically, I would like the section highlighted here to be more likely, and everything else (except the red section, which is still off-limits) should have a probability lower depending on the distance from the highligthed line. The furthest point should have 0 chance, and the rest an existing chance which is higher when closer to the line, with points exactly on my line (since I round them to a specific decimal, there are points with are on the line) having the best odds.
Sorry for the ugly pictures. This is the best i could do in paint to show my thoughts.
The "most likely" area is an empty diamond (just the that with the vertices (-1, 0), (0, -0.5), (1, 0), (0, 0.5), with of course the red area override the weighting because it's off limits. The red area is anything with x > 0.5
Does anyone knows how to do this? I'm working in C# but honestly an algorithm in any non-esoteric language would do the trick. I'm completely lost as to how to proceed.
A commenter noted that adding the off-limits zone to the algorithm is an added difficulty with no real use.
You can assume that I'll take care of the off-limit section by myself after running the weighting algorithm. Since it's just 25% of the area, most of the times it wouldn't even make a difference performance-wise if I just made this:
while (x > 0.5)
{
runAlgorithmAgain();
}
So you can safely ignore that part for answers.
Ok, here my thoughts on this matter. I would like to propose algorithm which, with some rejections, might solve your problem. Note, due to need of acceptance-rejection, it might be slower than you expected it to be.
We sample in single quadrant (say, lower left one), then use reflection to put point into any other quadrant, and then reject red zone points.
Basically, sampling in quadrant is two-step process. First, we sample first position on the border line. As soon as we got position on the line, we sample from distribution which is bell-like shape (Gaussian or Laplace for example), and move point in the orthogonal to the border line direction.
Code compiles, but completely untested, so please check everything startign with the numbers
using System;
namespace diamond
{
class Program
{
public const double SQRT_5 = 2.2360679774997896964091736687313;
public static double gaussian((double mu, double sigma) N, Random rng) {
var phi = 2.0 * Math.PI * rng.NextDouble();
var r = Math.Sqrt( -2.0 * Math.Log(1.0 - rng.NextDouble()) );
return N.mu + N.sigma * r * Math.Sin(phi);
}
public static double laplace((double mu, double sigma) L, Random rng) {
var v = - L.sigma * Math.Log(1.0 - rng.NextDouble());
return L.mu + ((rng.NextDouble() < 0.5) ? v : -v );
}
public static double sample_length(double lmax, Random rng) {
return lmax * rng.NextDouble();
}
public static (double, double) move_point((double x, double y) pos, (double wx, double wy) dir, double l) {
return (pos.x + dir.wx * l, pos.y + dir.wy * l);
}
public static (double, double) sample_in_quadrant((double x0, double y0) pos, (double wx, double wy) dir, double lmax, double sigma, Random rng) {
while (true) {
var l = sample_length(lmax, rng);
(double x, double y) = move_point(pos, dir, l);
var dort = (dir.wy, -dir.wx); // orthogonal to the line direction
var s = gaussian((0.0, sigma), rng); // could be laplace instead of gaussian
(x, y) = move_point((x, y), dort, s);
if (x >= -1.0 && x <= 0.0 && y >= 0.0 && y <= 1.0) // acceptance/rejection
return (x, y);
}
}
public static (double, double) sample_in_plane((double x, double y) pos, (double wx, double wy) dir, double lmax, double sigma, Random rng) {
(double x, double y) = sample_in_quadrant(pos, dir, lmax, sigma, rng);
if (rng.NextDouble() < 0.25)
return (x, y);
if (rng.NextDouble() < 0.5) // reflection over X
return (x, -y);
if (rng.NextDouble() < 0.75) // reflection over Y
return (-x, y);
return (-x, -y); // reflection over X&Y
}
static void Main(string[] args) {
var rng = new Random(32345);
var L = 0.5 * SQRT_5 + 0.5 / SQRT_5; // sampling length, BIGGER THAN JUST A SEGMENT IN THE QUADRANT
(double x0, double y0) pos = (-1.0, 0.0); // initial position
(double wx, double wy) dir = (2.0 / SQRT_5, 1.0 / SQRT_5); // directional cosines, wx*wx + wy*wy = 1
double sigma = 0.2; // that's a value to play with
// last rejection stage
(double x, double y) pt;
while(true) {
pt = sample_in_plane(pos, dir, L, sigma, rng);
if (pt.x < 0.5) // reject points in the red area, accept otherwise
break;
}
Console.WriteLine(String.Format("{0} {1}", pt.x, pt.y));
}
}
}
Related
I'm attempting to convert from state vectors (position and speed) into Kepler elements, however I'm running into problems where a negative velocity or position will give me wrong results when trying to calculate true anomaly.
Here are the different ways I'm trying to calculate the True Anomaly:
/// <summary>
/// https://en.wikipedia.org/wiki/True_anomaly#From_state_vectors
/// </summary>
public static double TrueAnomaly(Vector4 eccentVector, Vector4 position, Vector4 velocity)
{
var dotEccPos = Vector4.Dot(eccentVector, position);
var talen = eccentVector.Length() * position.Length();
talen = dotEccPos / talen;
talen = GMath.Clamp(talen, -1, 1);
var trueAnomoly = Math.Acos(talen);
if (Vector4.Dot(position, velocity) < 0)
trueAnomoly = Math.PI * 2 - trueAnomoly;
return trueAnomoly;
}
//sgp = standard gravitational parameter
public static double TrueAnomaly(double sgp, Vector4 position, Vector4 velocity)
{
var H = Vector4.Cross(position, velocity).Length();
var R = position.Length();
var q = Vector4.Dot(position, velocity); // dot product of r*v
var TAx = H * H / (R * sgp) - 1;
var TAy = H * q / (R * sgp);
var TA = Math.Atan2(TAy, TAx);
return TA;
}
public static double TrueAnomalyFromEccentricAnomaly(double eccentricity, double eccentricAnomaly)
{
var x = Math.Sqrt(1 - Math.Pow(eccentricity, 2)) * Math.Sin(eccentricAnomaly);
var y = Math.Cos(eccentricAnomaly) - eccentricity;
return Math.Atan2(x, y);
}
public static double TrueAnomalyFromEccentricAnomaly2(double eccentricity, double eccentricAnomaly)
{
var x = Math.Cos(eccentricAnomaly) - eccentricity;
var y = 1 - eccentricity * Math.Cos(eccentricAnomaly);
return Math.Acos(x / y);
}
Edit: another way of doing it which Spectre pointed out:
public static double TrueAnomaly(Vector4 position, double loP)
{
return Math.Atan2(position.Y, position.X) - loP;
}
Positions are all relative to the parent body.
These functions all agree if position.x, position.y and velocity.y are all positive.
How do I fix these so that I get a consistent results when position and velocity are negitive?
Just to clarify: My angles appear to be sort of correct, just pointing in the wrong quadrant depending on the position and or velocity vectors.
Yeah so I was wrong, the above all do return the correct values after all.
So I found an edge case where most of the above calculations fail.
Given position and velocity:
pos = new Vector4() { X = -0.208994076275941, Y = 0.955838328099748 };
vel = new Vector4() { X = -2.1678187689294E-07, Y = -7.93096769486992E-08 };
I get some odd results, ie ~ -31.1 degrees, when I think it should return ` 31.1 (non negative). one of them returns ~ 328.8.
However testing with this position and velocity the results apear to be ok:
pos = new Vector4() { X = -0.25, Y = 0.25 };
vel = new Vector4() { X = Distance.KmToAU(-25), Y = Distance.KmToAU(-25) };
See my answer for extra code on how I'm testing and the math I'm using for some of the other variables.
I'm going around in circles on this one. this is a result of a bug in my existing code that shows up under some conditions but not others.
I guess the real question now is WHY am I getting different results with position/velocity above that don't match to my expectations or each other?
Assuming 2D case... I am doing this differently:
compute radius of semi axises and rotation
so you need to remember whole orbit and find 2 most distant points on it that is major axis a. The minor axis b usually is 90 deg from major axis but to be sure just fins 2 perpendicularly most distant points on your orbit to major axis. So now you got both semi axises. The initial rotation is computed from the major axis by atan2.
compute true anomaly E
so if center is x0,y0 (intersection of a,b or center point of both) initial rotation is ang0 (angle of a) and your point on orbit is x,y then:
E = atan2(y-y0,x-x0) - ang0
However in order to match Newton/D'Alembert physics to Kepler orbital parameters you need to boost the integration precision like I did here:
Is it possible to make realistic n-body solar system simulation in matter of size and mass?
see the [Edit3] Improving Newton D'ALembert integration precision even more in there.
For more info and equations see:
Solving Kepler's equation
[Edit1] so you want to compute V I see it like this:
As you got your coordinates relative to parent you can assume they are already in focal point centered so no need for x0,y0 anymore. Of coarse if you want high precision and have more than 2 bodies (focal mass + object + proximity object(s) like moons) then the parent mass will no longer be in focal point of orbit but close to it ... and to remedy you need to use real focal point position so x0,y0 again... So how to do it:
compute center point (cx,cy) and a,b semi axises
so its the same as in previous text.
compute focal point (x0,y0) in orbit axis aligned coordinates
simple:
x0 = cx + sqrt( a^2 + b^2 );
y0 = cy;
initial angle ang0 of a
let xa,ya be the intersection of orbit and major axis a on the side with bigger speeds (near parent object focus). Then:
ang0 = atan2( ya-cy , xa-cx );
and finally the V fore any of yours x,y
V = atan2( y-y0 , x-x0 ) - ang0;
Ok so on further testing it appears my original calcs do all return the correct values, however when I was looking at the outputs I was not taking the LoP into account and basically not recognizing that 180 is essentially the same angle as -180.
(I was also looking at the output in radians and just didn't see what should have been obvious)
Long story short, I have a bug I thought was in this area of the code and got lost in the weeds.
Seems I was wrong above. see OP for edge case.
Here's some code I used to test these,
I used variations of the following inputs:
pos = new Vector4() { X = 0.25, Y = 0.25 };
vel = new Vector4() { X = Distance.KmToAU(-25), Y = Distance.KmToAU(25) };
And tested them with the following
double parentMass = 1.989e30;
double objMass = 2.2e+15;
double sgp = GameConstants.Science.GravitationalConstant * (parentMass + objMass) / 3.347928976e33;
Vector4 ev = OrbitMath.EccentricityVector(sgp, pos, vel);
double e = ev.Length();
double specificOrbitalEnergy = Math.Pow(vel.Length(), 2) * 0.5 - sgp / pos.Length();
double a = -sgp / (2 * specificOrbitalEnergy);
double ae = e * a;
double aop = Math.Atan2(ev.Y, ev.X);
double eccentricAnomaly = OrbitMath.GetEccentricAnomalyFromStateVectors(pos, a, ae, aop);
double aopD = Angle.ToDegrees(aop);
double directAngle = Math.Atan2(pos.Y, pos.X);
var θ1 = OrbitMath.TrueAnomaly(sgp, pos, vel);
var θ2 = OrbitMath.TrueAnomaly(ev, pos, vel);
var θ3 = OrbitMath.TrueAnomalyFromEccentricAnomaly(e, eccentricAnomaly);
var θ4 = OrbitMath.TrueAnomalyFromEccentricAnomaly2(e, eccentricAnomaly);
var θ5 = OrbitMath.TrueAnomaly(pos, aop);
double angleΔ = 0.0000001; //this is the "acceptable" amount of error, really only the TrueAnomalyFromEccentricAnomaly() calcs needed this.
Assert.AreEqual(0, Angle.DifferenceBetweenRadians(directAngle, aop - θ1), angleΔ);
Assert.AreEqual(0, Angle.DifferenceBetweenRadians(directAngle, aop - θ2), angleΔ);
Assert.AreEqual(0, Angle.DifferenceBetweenRadians(directAngle, aop - θ3), angleΔ);
Assert.AreEqual(0, Angle.DifferenceBetweenRadians(directAngle, aop - θ4), angleΔ);
Assert.AreEqual(0, Angle.DifferenceBetweenRadians(directAngle, aop - θ5), angleΔ);
and the following to compare the angles:
public static double DifferenceBetweenRadians(double a1, double a2)
{
return Math.PI - Math.Abs(Math.Abs(a1 - a2) - Math.PI);
}
And eccentricity Vector found thus:
public static Vector4 EccentricityVector(double sgp, Vector4 position, Vector4 velocity)
{
Vector4 angularMomentum = Vector4.Cross(position, velocity);
Vector4 foo1 = Vector4.Cross(velocity, angularMomentum) / sgp;
var foo2 = position / position.Length();
return foo1 - foo2;
}
And EccentricAnomaly:
public static double GetEccentricAnomalyFromStateVectors(Vector4 position, double a, double linierEccentricity, double aop)
{
var x = (position.X * Math.Cos(-aop)) - (position.Y * Math.Sin(-aop));
x = linierEccentricity + x;
double foo = GMath.Clamp(x / a, -1, 1); //because sometimes we were getting a floating point error that resulted in numbers infinatly smaller than -1
return Math.Acos(foo);
}
Thanks to Futurogogist and Spektre for their help.
I am assuming you are working in two dimensions?
Two dimensional vectors of position p and velocity v. The constant K is the the product of the gravitational constant and the mass of the gravity generating body. Calculate the eccentricity vector
eccVector = (dot(v, v)*p - dot(v, p)*v) / K - p / sqrt(dot(p, p));
eccentricity = sqrt(dot(eccVector, eccVector));
eccVector = eccVector / eccentricity;
b = { - eccVector.y, eccVector.x}; //unit vector perpendicular to eccVector
r = sqrt(dot(p, p));
cos_TA = dot(p, eccVector) / r; \\ cosine of true anomaly
sin_TA = dot(p, b) / r; \\ sine of true anomaly
if (sin_TA >= 0) {
trueAnomaly = arccos(cos_TA);
}
else if (sin_TA < 0){
trueAnomaly = 2*pi - arccos(cos_TA);
}
Here we have the Spatial domain implementation of Gabor filter. But, I need to implement a Gabor filter in the Frequency Domain for performance reasons.
I have found the Frequency Domain equation of Gabor Filter:
I am actually in doubt about the correctness and/or applicability of this formula.
Source Code
So, I have implemented the following :
public partial class GaborFfftForm : Form
{
private double Gabor(double u, double v, double f0, double theta, double a, double b)
{
double rad = Math.PI / 180 * theta;
double uDash = u * Math.Cos(rad) + v * Math.Sin(rad);
double vDash = (-1) * u * Math.Sin(rad) + v * Math.Cos(rad);
return Math.Exp((-1) * Math.PI * Math.PI * ((uDash - f0) / (a * a)) + (vDash / (b * b)));
}
public Array2d<Complex> GaborKernelFft(int sizeX, int sizeY, double f0, double theta, double a, double b)
{
int halfX = sizeX / 2;
int halfY = sizeY / 2;
Array2d<Complex> kernel = new Array2d<Complex>(sizeX, sizeY);
for (int u = -halfX; u < halfX; u++)
{
for (int v = -halfY; v < halfY; v++)
{
double g = Gabor(u, v, f0, theta, a, b);
kernel[u + halfX, v + halfY] = new Complex(g, 0);
}
}
return kernel;
}
public GaborFfftForm()
{
InitializeComponent();
Bitmap image = DataConverter2d.ReadGray(StandardImage.LenaGray);
Array2d<double> dImage = DataConverter2d.ToDouble(image);
int newWidth = Tools.ToNextPowerOfTwo(dImage.Width) * 2;
int newHeight = Tools.ToNextPowerOfTwo(dImage.Height) * 2;
double u0 = 0.2;
double v0 = 0.2;
double alpha = 10;//1.5;
double beta = alpha;
Array2d<Complex> kernel2d = GaborKernelFft(newWidth, newHeight, u0, v0, alpha, beta);
dImage.PadTo(newWidth, newHeight);
Array2d<Complex> cImage = DataConverter2d.ToComplex(dImage);
Array2d<Complex> fImage = FourierTransform.ForwardFft(cImage);
// FFT convolution .................................................
Array2d<Complex> fOutput = new Array2d<Complex>(newWidth, newHeight);
for (int x = 0; x < newWidth; x++)
{
for (int y = 0; y < newHeight; y++)
{
fOutput[x, y] = fImage[x, y] * kernel2d[x, y];
}
}
Array2d<Complex> cOutput = FourierTransform.InverseFft(fOutput);
Array2d<double> dOutput = Rescale2d.Rescale(DataConverter2d.ToDouble(cOutput));
//dOutput.CropBy((newWidth-image.Width)/2, (newHeight - image.Height)/2);
Bitmap output = DataConverter2d.ToBitmap(dOutput, image.PixelFormat);
Array2d<Complex> cKernel = FourierTransform.InverseFft(kernel2d);
cKernel = FourierTransform.RemoveFFTShift(cKernel);
Array2d<double> dKernel = DataConverter2d.ToDouble(cKernel);
Array2d<double> dRescaledKernel = Rescale2d.Rescale(dKernel);
Bitmap kernel = DataConverter2d.ToBitmap(dRescaledKernel, image.PixelFormat);
pictureBox1.Image = image;
pictureBox2.Image = kernel;
pictureBox3.Image = output;
}
}
Just concentrate on the algorithmic steps at this time.
I have generated a Gabor kernel in the frequency domain. Since, the kernel is already in Frequency domain, I didn't apply FFT to it, whereas image is FFT-ed. Then, I multiplied the kernel and the image to achieve FFT-Convolution. Then they are inverse-FFTed and converted back to Bitmap as usual.
Output
The kernel looks okay. But, The filter-output doesn't look very promising (or, does it?).
The orientation (theta) doesn't have any effect on the kernel.
The calculation/formula is frequently suffering from divide-by-zero exception up on changing values.
How can I fix those problems?
Oh, and, also,
what do the parameters α, β, represent?
what should be the appropriate value of f0?
Update:
I have modified my code according to #Cris Luoengo's answer.
private double Gabor(double u, double v, double u0, double v0, double a, double b)
{
double p = (-2) * Math.PI * Math.PI;
double q = (u-u0)/(a*a);
double r = (v - v0) / (b * b);
return Math.Exp(p * (q + r));
}
public Array2d<Complex> GaborKernelFft(int sizeX, int sizeY, double u0, double v0, double a, double b)
{
double xx = sizeX;
double yy = sizeY;
double halfX = (xx - 1) / xx;
double halfY = (yy - 1) / yy;
Array2d<Complex> kernel = new Array2d<Complex>(sizeX, sizeY);
for (double u = 0; u <= halfX; u += 0.1)
{
for (double v = 0; v <= halfY; v += 0.1)
{
double g = Gabor(u, v, u0, v0, a, b);
int x = (int)(u * 10);
int y = (int)(v * 10);
kernel[x,y] = new Complex(g, 0);
}
}
return kernel;
}
where,
double u0 = 0.2;
double v0 = 0.2;
double alpha = 10;//1.5;
double beta = alpha;
I am not sure whether this is a good output.
There seems to be a typo in the equation for the Gabor filter that you found. The Gabor filter is a translated Gaussian in the frequency domain. Hence, it needs to have u² and v² in the exponent.
Equation (2) in your link seems more sensible, but still misses a 2:
exp( -2(πσ)² (u-f₀)² )
It is the 1D case, this is the filter we want to use in the direction θ. We now multiply in the perpendicular direction, v, with a non-shifted Gaussian. I set α and β to be the inverse of the two sigmas:
exp( -2(π/α)² (u-f₀)² ) exp( -2(π/β)² v² ) = exp( -2π²((u-f₀)/α)² + -2π²(v/β)² )
You should implement the above equation with u and v rotated over θ, as you already do.
Also, u and v should run from -0.5 to 0.5, not from -sizeX/2 to sizeX/2. And that is assuming your FFT sets the origin in the middle of the image, which is not common. Typically the FFT algorithms set the origin in a corner of the image. So you should probably have your u and v run from 0 to (sizeX-1)/sizeX instead.
With a corrected implementation as above, you should set f₀ to be between 0 and 0.5 (try 0.2 to start with), and α and β should be small enough such that the Gaussian doesn't reach the 0 frequency (you want the filter to be 0 there)
In the frequency domain, your filter will look like a rotated Gaussian away from the origin.
In the spatial domain, the magnitude of your filter should look again like a Gaussian. The imaginary component should look like this (picture links to Wikipedia page I found it on):
(i.e. it is anti-symmetric (odd) in the θ direction), possibly with more lobes depending on α, β and f₀. The real component should be similar but symmetric (even), with a maximum in the middle. Note that after IFFT, you might need to shift the origin from the top-left corner to the middle of the image (Google "fftshift").
Note that if you set α and β to be equal, the rotation of the u-v plane is irrelevant. In this case, you can use cartesian coordinates instead of polar coordinates to define the frequency. That is, instead of defining f₀ and θ as parameters, you define u₀ and v₀. In the exponent you then replace u-f₀ with u-u₀, and v with v-v₀.
The code after the edit of the question misses the square again. I would write the code as follows:
private double Gabor(double u, double v, double u0, double v0, double a, double b)
{
double p = (-2) * Math.PI * Math.PI;
double q = (u-u0)/a;
double r = (v - v0)/b;
return Math.Exp(p * (q*q + r*r));
}
public Array2d<Complex> GaborKernelFft(int sizeX, int sizeY, double u0, double v0, double a, double b)
{
double halfX = sizeX / 2;
double halfY = sizeY / 2;
Array2d<Complex> kernel = new Array2d<Complex>(sizeX, sizeY);
for (double y = 0; y < sizeY; y++)
{
double v = y / sizeY;
// v -= HalfY; // whether this is necessary or not depends on your FFT
for (double x = 0; x < sizeX; x++)
{
double u = x / sizeX;
// u -= HalfX; // whether this is necessary or not depends on your FFT
double g = Gabor(u, v, u0, v0, a, b);
kernel[(int)x, (int)y] = new Complex(g, 0);
}
}
return kernel;
}
So I'm trying to make a Program that will draw a triangle given some user input. The variables that the user provides are angleA, angleB, andleC, and the corresponding sides. The code I have set up to find the three points of the angle is as follows.
double angle_A = double.Parse(angleA.Text);
double angle_B = double.Parse(angleB.Text);
double angle_C = double.Parse(angleC.Text);
double side_A = double.Parse(sideA.Text);
double side_B = double.Parse(sideB.Text);
double side_C = double.Parse(sideC.Text);
double triangleHeight = Area * 2 / (double.Parse(sideB.Text));
double height = canvas.Height;
double width = canvas.Width;
int aX, aY, bX, bY, cX, cY;
aY = Convert.ToInt32(canvas.Height - triangleHeight / 2);
if (angle_A <= 90 && angle_C <= 90)
{
aX = Convert.ToInt32((width - side_B) / 2);
}
else if (angle_A > 90)
{
double extraLength = Math.Sqrt(Math.Pow(side_C, 2) - Math.Pow(triangleHeight, 2));
aX = Convert.ToInt32(width - ((width - (side_B + extraLength)) / 2) + side_B);
}
else if (angle_C > 90)
{
double extraLength = Math.Sqrt(Math.Pow(side_A, 2) - Math.Pow(triangleHeight, 2));
aX = Convert.ToInt32((width - side_B + extraLength) / 2);
}
else
{
aX = 0;
MessageBox.Show("ERROR: No such triangle exists", "ERROR:");
}
cX = aX + Convert.ToInt32(side_B);
cY = aY;
bX = Convert.ToInt32(side_A * Math.Cos(Math.PI * angle_C / 180) + cX);
bY = Convert.ToInt32(side_A * Math.Sin(Math.PI * angle_C / 180) - cY);
Point pointA = new Point(aX, aY);
Point pointB = new Point(bX, bY);
Point pointC = new Point(cX, cY);
Point[] points = new Point[3] { pointA, pointB, pointC };
return points;
This returns the three points that the paint method should use to draw the triangle. However, when I insert the values, the triangle it draws looks nothing like the triangle I have described with the user input. Any thoughts on why this is? Thanks in advance.
P.S. The error is not in my code, as it gives me no errors and does not crash. It is strictly a math error that I have not been able to locate.
I imagine the triangle ABC with corners A and C along the base line with A to the left and C to the right, and B somewhere above them. Side A is the side opposite corner A, and so on.
As Damien_the_Unbeliever says, you should only allow input of, say, side B, side C and angle of corner A. Validate that A is not over 180 degrees. Start off with A at the origin, so we know straight away that xA = 0, yA = 0, xC = length of side B, yC=0, xB = side C * cos A, and yB = side C * sin A. I believe this works even if A is over 90 degrees, you do get a negative value for xB but don't worry, continue anyway!
Now all you have to do is centre the triangle on the canvas. I don't understand where you are getting Area from. It makes no sense to calculate the triangle's height from its area. The triangle height is yB, you can calculate the offset you need to centre it vertically as you know, so long as yB <= height. Add the same y offset to all the points.
The horizontal offset is a bit more complicated! If xB is negative, I would add an offset to all the x values to bring xB to 0, this positions your triangle at the left side of the canvas, and its width is given by the new xC. If xB is non-negative, the width is the maximum of xC or xB. Then you can calculate the x offset from the width as you know.
I have had time to do some of the code, for example values; this will draw a triangle but not yet centre it:
int sideB = 100;
int sideC = 143;
int angleA = 28;
double angleARadians = Math.PI * angleA / 180.0;
int[] xs = new int[3];
int[] ys = new int[3];
//1st corner is at the origin
xs[0] = 0; ys[0] = 0;
//Then the third corner is along the x axis from there to the length of side B
xs[2] = sideB; ys[2] = 0;
// The second corner is up a bit above the x axis. x could be negative.
// Note, when you draw it, the y axis extends downwards, so a positive y value will be drawn below the x axis.
xs[1] = (int)Math.Round(sideC * Math.Cos(angleARadians));
ys[1] = (int)Math.Round(sideC * Math.Sin(angleARadians));
//If Bx is negative, move all the points over until it's 0
if (xs[1] < 0)
{
int zeroX = xs[1] * -1;
for (int i = 0; i < 3; i++)
{
xs[i] += zeroX;
}
}
// Now centre the triangle on the canvas.
// Firstly find the width of the triangle. Point B could be to the left of A, or between A and C, or to the right of C.
// So the left most point of the triangle is the minimum of A or B, and the right most point is the maximum of B or C.
int minX = Math.Min(xs[0],xs[1]);
int maxX = Math.Max(xs[2], xs[1]);
//The height of the triangle is yB.
int offsetX = (panCanvas.Width - (maxX - minX)) / 2;
int offsetY = (panCanvas.Height - ys[1]) / 2;
//offset all the points by the same amount, to centre the triangle.
for (int i = 0; i < 3; i++)
{
xs[i] += offsetX;
ys[i] += offsetY;
}
Given the three sides of a triangle a, b, and c the coordinates of the vertices are
P=[0,0]
Q=[a,0]
R=[(a^2+c^2-b^2)/(2*a), sqrt(c^2*(2*(a^2+b^2)-c^2)-(a+b)^2*(a-b)^2)/(4*a^2))]
Example, a=6, b=4 and c=8
P=[0,0]
Q=[6,0]
R=[7,√15]
I have a binary object in a 3d array located in the origin (0,0,0). I need rotate this object dinamically in z axis. How can I rotate that multidimensional array with no fixed size in any angle?
I have created a 3d Point Class:
public class Point3
{
public double X { get; set; }
public double Y { get; set; }
public double Z { get; set; }
}
I'm thinking in do a foreach in each point and rotate it:
foreach (Point3 p in listPoint3)
{
RotatePoint(p, angle);
}
Any suggestion?
You colud create a routine to rotate each point using the parametric equation for rotate the 3d object.
x' = x*cos(o)-y*sin(o)
y' = y*sin(o)-y*cos(o)
z' = z
private Point3 RotatePoint(Point3 p0, int angle)
{
Point3 p = new Point3()
{
X = p0.X * Math.Cos(angle) - p0.Y * Math.Sin(angle),
Y = p0.X * Math.Sin(angle) + p0.Y * Math.Cos(angle),
Z = p0.Z,
};
return p;
}
You need to know what axis you want to rotate on. But if this is only a question where to look at. (namespace System.Windows.Media.Media3D)
You can try this:
double angle = 45;
RotateTransform3D zrotation = new RotateTransform3D(new AxisAngleRotation3D(
new Vector3D(0, 0, 1), angle));
foreach (Point3D p in listPoint3)
{
Point3D rotatedPoint = zrotation.Transform(p);
}
You should use the build-in Point3D
Also if you want to Stack those: (multiple transforms)
double zAngle = 45;
double xAngle = 10;
Transform3DGroup group = new Transform3DGroup();
group.Children.Add( new RotateTransform3D(new AxisAngleRotation3D(new Vector3D(1, 0, 0), xAngle)));
group.Children.Add( new RotateTransform3D(new AxisAngleRotation3D(new Vector3D(0, 0, 1), zAngle)));
foreach (Point3D p in listPoint3)
{
Point3D rotatedPoint = group.Transform(p);
}
So you have a "monochrome object" which is stored like a 3D bitmap which you want to rotate around the Z axis. You must first understand that after a number of rotations you will end up with optical aberrations caused by the fact that you're using an array index which is a natural number to represent a coordinate of an object's component.
Upon rotation, any integer value will most likely become an irrational number. The fact that traditionally (not talking about special programs and frameworks) people store approximations of irrational numbers in double or float or decimal variables (which can only store a small subset of the rational numbers set) is nothing compared to the approximation of an irrational number by storing it in an integer (an array index).
Furthermore, even if that quality loss if of no big importance in the case of your application, you must be sure you understand that mathematically speaking, after a number of rotations, your 3d shape will be trimmed by the cylinder which is inscribed in the original parallelepiped, alongside the Z axis.
It goes like this. You said you already made a class called Point3:
public class Point3 {
public double X { get; set; }
public double Y { get; set; }
public double Z { get; set; }
}
Maybe you should follow #Jeroen van Langen's advice and use a standard class, if such a class already exists. The benefit would be that if someone out there already built or will ever build a library which uses that class you can start using the library right away.
But that's not so important right now.
#Alpert already gave a great C# code for rotating a point around the oZ axis. This is n "extension method" adaption of that code:
public static class RotationHelpers {
public static Point3 RotatePoint(this Point3 point, int angle) {
var result = new Point3() {
X = point.X * Math.Cos(angle) - point.Y * Math.Sin(angle),
Y = point.X * Math.Sin(angle) + point.Y * Math.Cos(angle),
Z = point.Z,
};
return result;
}
...
}
You can go even further and make an extension method which rotates a sequence of points around the oZ axis:
public static class RotationHelpers {
...
public static IEnumerable<Point3> RotatePoints(this IEnumerable<Point3> points, int angle) {
foreach (var point in points)
yield return point.RotatePoint(angle);
}
...
}
Now you said you have a 3d primitive matrix with 1s and 0s in it:
int[,,] matrix;
You need to somehow convert the intrinsically defined points in that matrix into a sequence of Point3 instances, rotate those and then convert the resulting sequence back into an int[,,] matrix.
That could be achieved like so (remember about the loss of quality I was talking about earlier):
public static class RotationHelpers {
...
public static IEnumerable<Point3> ToPoints(this int[,,] matrix) {
int lx = matrix.GetLength(0);
int ly = matrix.GetLength(1);
int lz = matrix.GetLength(2);
for (int x = 0; x < lx; x++)
for (int y = 0; y < ly; y++)
for (int z = 0; z < lz; z++) {
bool is1 = matrix[x, y, z] != 0;
if (is1)
yield return new Point3 {
X = x - lx / 2,
Y = y - ly / 2,
Z = z - lz / 2
};
}
}
...
}
That will take all of the cells in the WIDTH x HEIGHT x DEPTH matrix and for every cell which is not equal to 0 it will yield a new Point3 instance with the coordinates of that particular position.
That sequence can then be rotate by an angle using the earlier described RotatePoints method and then the following method could be used to "render" back the resulting sequence of Point3 instances into the array:
public static class RotationHelpers {
...
public static void AssignPoints(this int[,,] matrix, IEnumerable<Point3> points) {
int lx = matrix.GetLength(0);
int ly = matrix.GetLength(1);
int lz = matrix.GetLength(2);
for (int x = 0; x < lx; x++)
for (int y = 0; y < ly; y++)
for (int z = 0; z < lz; z++)
matrix[x, y, z] = 0;
foreach (var point in points) {
// this is when quality is lost, because things like 1.7 and 1.71
// will both become =2
var x = (int)Math.Round(point.X) + lx / 2;
var y = (int)Math.Round(point.Y) + ly / 2;
var z = (int)Math.Round(point.Z) + lz / 2;
// this is where you loose parts of the object because
// it doesn't fit anymore inside the parallelepiped
if ((x >= 0) && (y >= 0) && (z >= 0) &&
(x < lx) && (y < ly) && (z < lz))
matrix[x, y, z] = 1;
}
}
...
}
To wrap it up, you can use all of these methods like so:
int[,,] matrix = ...
int angle = ...
IEnumerable<Point3> points = matrix.ToPoints();
IEnumerable<Point3> rotatedPoints = points.RotatePoints(angle);
matrix.AssignPoints(rotatedPoints);
// now you have the original matrix, rotated by angle
I need to find the minimum size that has an aspect ratio of exactly (or within 0.001) some value. Are there any quick math tricks or framework tricks for doing this?
Here's the pseudo code for the current bad idea I had running in O(n^2):
epsilon = 0.001;
from x = 1 to MAX_X
{
from y = 1 to MAX_Y
{
if(Abs(x / y - aspectRatio) <= epsilon)
{
return new Size(x, y);
}
}
}
return Size.Empty;
Unusual. You need to find the greatest common divisor and divide width and height by it. The algorithm is by Euclid and is two thousand three hundred years old. Details are here.
You can write aspectRatio as a fraction (if you want it up to a presicion of 0.001, than you can use round(aspectRatio,3)/1000 )
Then, simplify this fraction. The resulting fraction is the x/y you're looking for.
A quicker way, but still not formulaic would be to only look at possible y values instead of iterating up to MAX_Y. e.g.:
static Size FindMinSize(double requiredRatio, double epsilon)
{
int x = 1;
do
{
int y = (int)(x * requiredRatio);
if (Test(x, y, requiredRatio, epsilon))
{
return new Size(x, y);
}
y = (int)((x + 1) * requiredRatio);
if (Test(x, y, requiredRatio, epsilon))
{
return new Size(x, y);
}
x++;
} while (x != int.MaxValue);
return new Size(0, 0);
}
static bool Test(int x, int y, double requiredRatio, double epsilon)
{
double aspectRatio = ((double)y)/x;
return Math.Abs(aspectRatio - requiredRatio) < epsilon;
}
Instead of testing all possible combinations, just increase the side that gets you closer to the aspect ratio:
public static Size GetSizeFromAspectRatio(double aspectRatio) {
double epsilon = 0.001;
int x = 1;
int y = 1;
while (true) {
double a = (double)x / (double)y;
if (Math.Abs(aspectRatio - a) < epsilon) break;
if (a < aspectRatio) {
x++;
} else {
y++;
}
}
return new Size(x, y);
}
The aspect ratio is the ratio between x and y. You can define the aspect ratio as x / y or y / x.
The minimum aspect ratio is 0 / 0.
Some other minimum has to be defined, either a minimum x or a minimum y.
min x = (min y * x) / y
min y = (min x * y) / x