I need help to calculate the tangets of a circle in 3D space, this is what I have so far
Tangents are represented by the blue lines, and this is the method I got from a friend to calculate them
Vec3D getTangentBetweenTwoPoint( Vec3D p1, Vec3D p2 ) {
Vec3D r = new Vec3D( p1.x - p2.x,
p1.y - p2.y,
p1.z - p2.z );
r.normalize();
return r;
}
void getTangents() {
Vec3D p0, p1;
for ( int i = 1; i < curve_length + 1; i++ ) {
p0 = points[i % curve_length];
p1 = points[(i+1) % curve_length];
tangents[i % curve_length] = getTangentBetweenTwoPoint( p0, p1 );
}
}
Any help will be much appreciated
Basically, you'd find the vector from the point you need the tangent for to the circle's center and take the cross product of that vector as well as the circle's normal (which you get by taking 2 points of the circle plus the center resulting in a plane equation).
If you normalize that cross product you get the normal/tangent vector for that point.
Replace i with i-1 in your code here:
p0 = points[(i-1) % curve_length];
I am assuming your points are equally spaced on the circle, so the line between the previous point and the next point will be parallel to the tangent at the current point.
Related
I made the following picture to help visualize the problem:
You can control the monkey, by rotating it. You can step to the neigbour fields, but here I have a problem calculating which is the field to step.
The monkey has an angle, which goes from 0 to 360 degress, 0 is when its head is up. I would like to calculate the neigbour field from this angle. (Basically this field is where the orangutan "looks").
I store the neigbours, and also the vertices for every polygon.
I tried with the following idea:
Start a half line from the orangutan, then calculate the intersection with each side of the polygon. If we have an intersection, then we know the two endpoints. Then search through the neigbour fields, if one of them has both points in its vertices, then this is the good one.
Here the code I wrote:
public Field getNeighbourByAngle(double angle) {
// Field center
Point2D c = center;
// New point initial coordinates
double x = c.getX(); double y = c.getY()+1;
// Rotate the point by the angle
Point2D rotatedPoint = new Point2D(
-(x*Math.cos(Math.toRadians(angle)) - y*Math.sin(Math.toRadians(angle))),
y*Math.cos(Math.toRadians(angle)) + x*Math.sin(Math.toRadians(angle)) );
// Get the vector from the center to the roteted point
Point2D v = rotatedPoint.subtract(c);
// Iterate over the vertex arraylist
for (int i = 0; i<verts.size(); i++) {
// Get the two endpoints
Point2D p1 = verts.get(i);
Point2D p2;
if (i == verts.size() - 1) {
p2 = verts.get(0);
} else { p2 = verts.get(i+1);}
// Calculate the intersection
// Using the formula:
// x = c.x + v.x * t
// x = p1.x * (1-t) + p2.x * t
// These two are equals, get t from the equation:
double t = ( p1.getX() - c.getX( )) / (v.getX() + p1.getX() - p2.getX() );
// t has to be between [0, 1] because p1 <-> p2 is just a line segment
if (0 <= t && t<= 1) {
// Iterate over the neigbours
for (int j = 0; j<neighbours.size(); j++) {
// If the neigbour has both p1 and p2 in its vertices list, then we found the correct neigbour.
if (neighbours.get(j).getVerts().contains(p1) &&
neighbours.get(j).getVerts().contains(p2)) {
return neighbours.get(j);
}
}
}
}
return null;
}
But I not get the correct results, I don't know where is the problem.
I am working on a project in Java. I am trying to move the points p2, p3, p4 just outside the circumference of the circle in the opposite direction to the point p1. Below is the image, which describes the problem, I am trying to solve.
//given two points, calculates the angle
public static double calcAngle(Point2D.Double p1, Point2D.Double p2) {
double deltaX = p2.x - p1.x;
double deltaY = p2.y - p1.y;
return (Math.atan2(deltaY, deltaX) * 180 / Math.PI);
}
//calculates a point on a circle given the angle, center of the circle and the radius
public static Point2D.Double pointOnCircle(Point2D.Double point, double radius , double angle) {
double x = Math.abs(point.x + (radius * Math.cos(angle * Math.PI / 180F)));
double y = Math.abs(point.y + (radius * Math.sin(angle * Math.PI / 180F)));
return new Point2D.Double(x,y);
}
How do I calculate the angle in Java coordinate system and destination co-ordinates for each of the points p2, p3, p4?
I am yet to try the code above and would like to know if my approach is right before proceeding, since it is a part of the bigger project. Thanks in advance!
Your general idea seems workable but overly complicated. There is no need to convert from x/y-vector to angle and then back. SImply scaling vectors will be enough.
Point2D p = p2; // likewise for p3, p4
double factor = radius / p.distance(p1);
p.setLocation(p1.getX() + (p.getX() - p1.getX())*factor,
p1.getY() + (p.getY() - p1.getY())*factor);
This takes the vector (p - p1), i.e. the vector pointing from p1 towards p, scales it by factor and adds it to the position of p1. The factor is chosen such that the new distance is equal to radius.
All of this will fail if p1 and p are the same, since in this case you'll have a division by zero. If this can be a problem for you, you might want to ensure that factor is a finite number, e.g. using Double.isFinite(double).
I am trying to generate random points on a sphere that is filled with a cube.
Because I had no idea how to do that i started with 2d. (A circle filled with a quadrat.)
What I am trying to do: Generating random points inside the outer circle, but outside the green square.
Basically in the blue areas.
The square is located at (-1|-1),(1|-1),(1|1),(-1|1).
The circle has a radius of r = sqrt(2) and is centered at (0|0).
I already have scripts to:
generate a random point on a circle (uniformly):
float a = 2 * MathUtils.PI * MathUtils.random(1f); // angle between 0 and 2pi
float r = radius * Math.sqrt(MathUtils.random(0, 1f)
float x = r * MathUtils.cos(a);
float y = r * MathUtils.sin(a);
calculating the radius for a given angle to form a square:
float r = (1/Math.sqrt(2)) / MathUtils.cos(((a+45)%90-45)/180*MathUtils.PI);
with (1/Math.sqrt(2)) being half the side length of the square
Before anyone asks:
I know that I could just re-generate points which are inside the green square until I get one that is outside, but I don't want to do it this way.
I appreciate any help. Thank you :)
It is rather hard to generate points only in region of sphere outside the cube (caps and wedges), so rejecting method looks reasonable.
But you can diminish the number of useless points, generating points in the ring only in 2D case and in spherical shell in 3D case.
So pseudocode might look as
//2d
SquaredR = RandomUniformInRange(0.5, 1)
R = Sqrt(SquaredR)
//3d
CubedR = RandomUniformInRange(Pow(3, -3/2), 1)
R = Pow(CubedR, 1/3)
//generate point on the circle or on the sphere with radius R
if Abs(x) > Sqrt(2)/2 or Sqrt(3)/3 and so on - reject
Having R, you can generate point on the sphere using any approach from here
Here is rough sketch of the idea. You select one quadrant to sample, say, one on the right.
First, sample angles from -pi/4 to pi/4
float a = -MathUtils.PI/4.0f + MathUtils.PI/2.0 * MathUtils.random(0.f,1.f);
float c = MathUtils.cos(a);
float s = MathUtils.sin(a);
Second, find minimum radius. With ray going from (0,0) at angle a will intersect quadrant line at x=1 at minimum
float rmin = 1.0f / c;
float rmax = Math.sqrt(2.0f);
Sample from rmin to rmax = sqrt(2), taking into account that for plane you sample squared radius and then use sqrt(), and for 3d space you sample cubed radius and then use cbrt().
float r2 = rmin*rmin + (rmax*rmax-rmin*rmin)*MathUtils.random(0.f,1.f);
float r = Math.sqrt(r);
float x = r * c;
float y = r * s;
Now, we constructed (x,y) is a such way it is guaranteed to be in the right quadrant below circle and on the right of the x=1 line.
To cover all four quadrants just sample to which quadrant you will move the point
float q = MathUtils.random(0.f,1.f);
if (q < 0.25f) // top quadrant
return (y, x);
if (q < 0.5f) // left quadrant
return (-x, y);
if (q < 0.75f) // bottom quadrant
return (y, -x);
return (x,y); // right quadrant
Please bear with me - my Java is quite rusty, and I have no ways to test the code.
In 3D case you'll have to deal with two angles, cubed radius, eight octants instead of four quadrants, but general logic is the same
UPDATE
I was wrong, sampling like I propose would lead to non-uniform point distribution.
From PDF:
PDF(phi, r) = S_(-\pi/4)^\phi d\phi S_1/cos(\phi)^\sqrt(2) r dr
One could get that we have to make \phi sampling non-uniform. Unfortunately, from
U(0,1) to get to sampled \phi requires solving non-linear equation
\pi/2 (0.5*(\phi/\pi/4 + 1) - U(0,1)) = 0.5*(tan(phi) + 1) - U(0,1)
So algorithm would be:
Sample U(0,1)
Find appropriate \phi by solving equation above
Find lower R boundary
Sample R
Quick code (in Python, sorry) to plot this non-linear function
import numpy as np
import matplotlib.pyplot as plt
def f(phi, ksi):
c = 0.5 * np.pi
c_2 = 0.5 * c
left = c * (0.5 * (phi/c_2 + 1.0) - ksi)
rght = (0.5 * (np.tan(phi) + 1.0) - ksi)
return left - rght
nof_points = 41
phi = np.linspace(-0.25*np.pi, 0.25*np.pi, nof_points)
y0_00 = f(phi, 0.00)
y0_25 = f(phi, 0.25)
y0_50 = f(phi, 0.50)
y0_75 = f(phi, 0.75)
y0_99 = f(phi, 1.00)
plt.plot(phi, y0_00, 'ro', phi, y0_25, 'b+', phi, y0_50, 'gx', phi, y0_75, 'm.', phi, y0_99, 'y^')
plt.show()
and plotted functions for five values of U(0,1) (ksi in the code)
Sampling could be rearranged such that r sampling is non-linear, but it exhibits the same problem - need to solve non-linear equation with polynomial and trigonometric parts
UPDATE II
And just for the record, if you want to sample r first, then it has to be sampled from the solution of the non-linear equation:
r2 sec-1(r) - sqrt(r2 - 1) = U(0,1)*(\pi/2 - 1)
in the interval [1...sqrt(2)]
After solving it and finding sampled r, \phi could be sampled uniformly in the interval allowed by r: [-cos-1(1/r) ... +cos-1(1/r)]
I'm trying to get my head around this- and I've literally been looking for a whole day!
I think I understand the main concepts behind it, but I'm struggling to figure out the math I need to create the axis on which to project my shapes on to?
So if I have a rectange I find out each of the points and then use these to find the side of the shape edge = v(n) - v(n-1) and go through all sides.
But I don't know how to then create the separating axis.
The theorem is not difficult to understand: If you can find a line for which all points of shape A are on the one side, and all points of shape B are on the other (dot product positive or negative), that line is separating the shapes.
What do you want to do? Find separating lines for arbitrary shapes?
I would recommend to have a look at projective geometry, as the edge for two vertices of a polygon extended to infinity can be represented by the cross product of the two vertices (x, y, 1). For convex polygons you can simply create lines for all edges and then take the dot product of all vertices of your other polygon to check on which side they are. If for one edge all points are outside, that edge is a separating line.
Also keep in mind that by keeping the line normalized you get the distance of a point to the line using dot product. The sign identifies the side on which the point lies.
If your problem is more difficult, please explain it in more detail. Maybe you can use some sort of clipping to solve it fast.
Example: projective geometry
public double[] e2p(double x, double y) {
return new double[] { x, y, 1 };
}
// standard vector maths
public double[] getCrossProduct(double[] u, double[] v) {
return new double[] { u[1] * v[2] - u[2] * v[1],
u[2] * v[0] - u[0] * v[2], u[0] * v[1] - u[1] * v[0] };
}
public double getDotProduct(double[] u, double[] v) {
return u[0] * v[0] + u[1] * v[1] + u[2] * v[2];
}
// collision check
public boolean isCollision(List<Point2D> coordsA, List<Point2D> coordsB) {
return !(isSeparate(pointsA, pointsB) || isSeparate(pointsB, pointsA));
}
// the following implementation expects the convex polygon's vertices to be in counter clockwise order
private boolean isSeparate(List<Point2D> coordsA, List<Point2D> coordsB) {
edges: for (int i = 0; i < coordsA.size(); i++) {
double[] u = e2p(coordsA.get(i).getX(), coordsA.get(i).getY());
int ni = i + 1 < coordsA.size() ? i + 1 : 0;
double[] v = e2p(coordsA.get(ni).getX(), coordsA.get(ni).getY());
double[] pedge = getCrossProduct(u, v);
for (Point2D p : coordsB) {
double d = getDotProduct(pedge, e2p(p.getX(), p.getY()));
if (d > -0.001) {
continue edges;
}
}
return true;
}
return false;
}
The separating axis is one of the sides. You can find the signs of the vertexes of the shape itself when plugged in the equation of this side:
(X - Xn).(Y - Yn-1) - (X - Xn-1).(Y - Yn) = 0
Check that the vertices of the other shape yield opposite signs.
I have a circle drawn, and I want to make it so I can have more slices than four. I can easily do four quadrants because I just check if the mouse in in the circle and inside a box.
This is how I am checking if the point is in the circle.
if( Math.sqrt((xx-x)*(xx-x) + (yy-y)*(yy-y)) <= radius)
{
return true;
}
else
{
return false;
}
How can I modify this if the circle is divided into more than 4 regions?
For radial slices (circular sectors), you have a couple of alternatives:
Use Math.atan2 to calculate the 4-quadrant angle of the line from the circle center to the point. Compare to the slice angles to determine the slice index.
For a particular slice, you can test which side of each slice edge the point falls. Classify the point accordingly. This is more complicated to calculate but probably faster (for a single slice) than calling Math.atan2.
The following sample code calculates the slice index for a particular point:
int sliceIndex(double xx, double yy, double x, double y, int nSlices) {
double angle = Math.atan2(yy - y, xx - x);
double sliceAngle = 2.0 * Math.PI / nSlices;
return (int) (angle / sliceAngle);
}
The above code makes the following assumptions:
slices are all the same (angular) width
slices are indexed counter-clockwise
slice 0 starts at the +x axis
slices own their right edge but not their left edge
You can adjust the calculations if these assumptions do not apply. (For instance, you can subtract the start angle from angle to eliminate assumption 3.)
First we can check that the point is within the circle as you did. But I woudln't combine this with a check for which quadrant (is that why you have radius/2 ?)
if( (xx-x)*(xx-x) + (yy-y)*(yy-y) > radius*radius)
return false;
Now we can look to see which region the point is in by using the atan2 function. atan2 is like Arctan except the Arctangent function always returns a value between -pi/2 and pi/2 (-90 and +90 degrees). We need the actual angle in polar coordinate fashion. Now assuming that (x,y) is the center of your circle and we are interested in the location of the point (xx,yy) we have
double theta = Math.atan2(yy-y,xx-x);
//theta is now in the range -Math.PI to Math.PI
if(theta<0)
theta = Math.PI - theta;
//Now theta is in the range [0, 2*pi]
//Use this value to determine which slice of the circle the point resides in.
//For example:
int numSlices = 8;
int whichSlice = 0;
double sliceSize = Math.PI*2 / numSlices;
double sliceStart;
for(int i=1; i<=numSlices; i++) {
sliceStart = i*sliceSize;
if(theta < sliceStart) {
whichSlice = i;
break;
}
}
//whichSlice should now be a number from 1 to 8 representing which part of the circle
// the point is in, where the slices are numbered 1 to numSlices starting with
// the right middle (positive x-axis if the center is (0,0).
It is more a trig problem Try something like this.
int numberOfSlices=8;
double angleInDegrees=(Math.toDegrees(Math.atan2(xx-x ,yy-y)));
long slice= Math.round(( numberOfSlices*angleInDegrees )/360 );