I have a color circle where the user can choose a color from. The color is calculated with this method
public int getColorForPoint(int x, int y, float[] hsv) {
x -= fullCircleRadius;
y -= fullCircleRadius;
double centerDist = Math.sqrt(x * x + y * y);
hsv[0] = (float) (Math.atan2(y, x) / Math.PI * 180f) + 180;
hsv[1] = Math.max(0f, Math.min(1f, (float) (centerDist / innerCircleRadius)));
return Color.HSVToColor(hsv);
}
Now I need the reversed method to calculate the x and y coordinate by a given color (hsv array).
To be more specific: The user can save a color and the indicator in the color circle should "jump" to the saved color on the circle.
But I'm quite lost with this mathematics.
Looking at the way you calculate centerDist - I can tell your circle centre is at the origin (0,0).
Basically HSV is a polar co ordinate, all you need is to convert a polar co ordinate to cartesian co ordinate. which is done as follows.
public double[] getHSVtoCartesian(double[] hsv) {
double [] xy;
double theta = hsv[0];
double r = hsv[1];
xy[0] = r * Math.cos(theta);
xy[1] = r * Math.sin(theta);
return xy;
}
Related
I can't seem to get the standard answer to work. I'm trying to find the new x,y coordinates of a point inside a larger rectangle, after rotating the larger rectangle around its center. (The end goal is to calculate the 4 corners of a collision box inside the larger rectangle.)
The rectangle is 50W x 128H, and the point inside the rectangle is at 15,111. If I rotate this same rectangle 90 degrees clockwise in Photoshop around its center, the point becomes 17,15 inside the rectangle that is now 128W and 50H.
However, if I use the formula I've found 100 times around here for the same question, I get a completely different answer.
import java.awt.*;
public class MyClass {
public static void main(String args[]) {
int width=50;
int height=128;
int cx = width/2;
int cy = height/2;
// should get 17,15
rotateXY(15,111,cx,cy,-90D); // returns 61,85, which is not even inside the image anymore
rotateXY(15,111,cx,cy,90D); // returns -11,66.
}
public static void rotateXY(int x, int y, int cx, int cy, double degrees) {
// x, y - coordinates of a corner point of the square
// cx, cy - center of square coordinates
double angle = Math.toRadians(degrees);
double x1 = x - cx;
double y1 = y - cy;
double x2 = x1 * Math.cos(angle) - y1 * Math.sin(angle);
double y2 = y1 * Math.cos(angle) + x1 * Math.sin(angle);
int rx = (int) x2 + cx;
int ry = (int) y2 + cy;
System.out.println(rx + "," + ry);
}
}
The method I used to rotate the image in java:
public static BufferedImage rotate(BufferedImage bimg, Double angle) {
double sin = Math.abs(Math.sin(Math.toRadians(angle))),
cos = Math.abs(Math.cos(Math.toRadians(angle)));
int w = bimg.getWidth();
int h = bimg.getHeight();
int neww = (int) Math.floor(w*cos + h*sin),
newh = (int) Math.floor(h*cos + w*sin);
BufferedImage rotated = new BufferedImage(neww, newh, bimg.getType());
Graphics2D graphic = rotated.createGraphics();
graphic.translate((neww-w)/2, (newh-h)/2);
graphic.rotate(Math.toRadians(angle), w/2, h/2);
graphic.drawRenderedImage(bimg, null);
graphic.dispose();
return rotated;
}
I assume you created an AffineTransform. With that you need three transformations:
translate the rectangle so it's center point is the origin
rotate the rectangle by some angle
translate the rectangle back to the position where it came from
Now with this you were able to transform the rectangle's coordinates (corners) into new screen coordinates. What you need is a transformation to get from new screen coordinates back to coordinates inside your rectangle. I think it looks like this:
translate the point the same as in step 1
rotate by the negative angle
undo step 1
These AffineTransforms can be applied quite efficiently, compared to your algorightm using trigonometry which even may have a limited range of validity.
See also Rotating Image with AffineTransform
I have a Java class that creates a virtual screen (let's call it a map) that can be translated, resized, and rotated.
When I rotate it, however, it only rotates it around (0, 0).
To transform the point to the screen you first rotate it, then resize it, then translate it.
private double dx; //offset in x and y
private double dy;
private double t; //rotation (radians)
private double sx; //scale of x and y
private double sy;
public double[] toScreen(double x, double y) //takes (x, y) on the map and gives (x1, y1) for the screen
{
double[] xy = {x, y};
if(t != 0)
{
double distance = Math.hypot(xy[0], xy[1]);
double theta = Math.atan2(xy[1], xy[0]) + t;
xy[0] = Math.cos(theta)*distance;
xy[1] = Math.sin(theta)*distance;
}
xy[0] *= sx;
xy[1] *= sy;
xy[0] += dx;
xy[1] += dy;
return xy;
}
to set the rotation or change it, you manipulate the variable t, but it rotates on (0, 0).
If I make a method that takes in (x, y) to rotate around like public void changeRotation(double t, double x, double y).
I want (x, y) to be map coordinates. What would the method look like and can you explain what it does?
If I understood correctly this is what you need :
/**
* #param point point (x,y) of the coordinates to be rotated
* #param center point (x,y) of the center (pivot) coordinates
* #param angle in radians
* #return point (x,y) of the new (translated) coordinates
*/
static Point2D.Double rotateAPoint(Point2D.Double point, Point2D.Double center, double angle){
double newX = center.x + Math.cos(angle) * (point.x - center.x) -
Math.sin(angle) * (point.y-center.y) ;
double newY = center.y + Math.sin(angle) * (point.x - center.x) +
Math.cos(angle) * (point.y - center.y) ;
return new Point2D.Double(newX, newY);
}
Try with
Point2D.Double point = new Point2D.Double(200,100);
Point2D.Double center = new Point2D.Double(100,100);
double angle = Math.PI/2 ; //90 degress
System.out.println(rotateAPoint(point, center, angle) );
System.out.println(rotateAPoint(point, center, -angle));
If you prefer to use double[] :
/**
* #param point (x,y) of the coordinates to be rotated
* #param center (x,y) of the center (pivot) coordinates
* #param angle in radians
* #return (x,y) of the new (translated) coordinates
*/
static double[] rotateAPoint(double[] point, double[] center, double angle){
double newX = center[0] + Math.cos(angle) * (point[0] - center[0]) -
Math.sin(angle) * (point[0]-center[0]) ;
double newY = center[1] + Math.sin(angle) * (point[1] - center[1]) +
Math.cos(angle) * (point[1] - center[1]) ;
return new double[]{newX, newY};
}
Explanation about the math here
I'm creating a Quaternion from input from a serial device. In Processing I rotate around the x-axis in the code below. My Quaternion object takes the input and uses the set function to set the values, euler angles, and normalize. Is there something wrong with the math?
I commented out rotation for z and y, but basically the object doesn't rotate around very well or is jerky compared to the x-axis, which works perfectly. What am I doing wrong in the code below?
For reference, the shape(model) line below is the loaded 3d object from a .obj file loaded in using loadShape and the shape function displays it in the draw loop.
Quaternion q = new Quaternion(s);
q.set(x, y, z, w);
q = q.Euler(q.eulerAngles);
translate(x, y);
rotateX(q.eulerAngles.x);
//rotateY(q.eulerAngles.y);
//rotateZ(q.eulerAngles.z);
shape(model);
rotateX(-q.eulerAngles.x);
translate(-x, -y);
This is part of the Quaternion class:
public class Quaternion {
PApplet s;
public float w,x,y,z;
public PVector eulerAngles;
public Quaternion(PApplet s, float x, float y, float z, float w){
this.s = s;
this.x = x;
this.y = y;
this.z = z;
this.w = w;
normalize();
}
public Quaternion(Quaternion q){
this.s = q.s;
this.w = q.w;
this.x = q.x;
this.y = q.y;
this.z = q.z;
}
public Quaternion normalize() {
float magnitude = w*w + x*x + y*y + z*z;
if(magnitude != 0.0 && magnitude != 1.0){
magnitude = 1.0f / s.sqrt(magnitude);
w *= magnitude;
x *= magnitude;
y *= magnitude;
z *= magnitude;
}
eulerAngles = setEulerAngles();
return this;
}
public Quaternion set(float x, float y, float z, float w) {
this.x = x;
this.y = y;
this.z = z;
this.w = w;
return normalize();
}
// Returns a rotation that rotates z degrees around
// the z axis, x degrees around the x axis, and y
// degrees around the y axis.
public Quaternion Euler(){
float roll = eulerAngles.x;
float pitch = eulerAngles.y;
float yaw = eulerAngles.z;
float cr = (float)Math.cos(roll * 0.5);
float sr = (float)Math.sin(roll * 0.5);
float cp = (float)Math.cos(pitch * 0.5);
float sp = (float)Math.sin(pitch * 0.5);
float cy = (float)Math.cos(yaw * 0.5);
float sy = (float)Math.sin(yaw * 0.5);
w = cy*cr*cp + sy*sr*sp;
x = cy*sr*cp - sy*cr*sp;
y = cy*cr*sp + sy*sr*cp;
z = sy*cr*cp - cy*sr*sp;
return normalize();
}
// set euler angle representation of
// the rotation in 3-dim PVector
private PVector setEulerAngles(){
// roll: x-axis rotation
float sinr = 2.0f * (w*x + y*z);
float cosr = 1.0f - 2.0f * (x*x + y*y);
float roll = (float)Math.atan2(sinr, cosr);
// pitch: y-axis rotation
float sinp = 2.0f * (w*y - z*x);
float pitch = 0.0f;
if(Math.abs(sinp) >= 1){
pitch = (float)Math.copySign(Math.PI/2, sinp);
} else {
pitch = (float)Math.asin(sinp);
}
// yaw: z-axis rotation
float siny = 2.0f * (w*z + x*y);
float cosy = 1.0f - 2.0f * (y*y + z*z);
float yaw = (float)Math.atan2(siny, cosy);
return new PVector(roll, pitch, yaw);
}
}
As far as I can tell, the Euler angles that you get from your method should be applied in ZYX order rather than XYZ. But anyway, do not mess around with Euler angles unless you really have to. And in this case you don't.
Instead, convert the quaternion to a rotation matrix and apply this transform using applyMatrix(). There will be no ambiguity here.
To revert a transform, do not apply the inverse transform (like you did with rotateX(-q.eulerAngles.x) and translate(-x, -y)). It is very easy to confuse the order or forget a transform during development. Instead, use pushMatrix() / popMatrix() or resetMatrix.
Btw, I find the definition of your quaternion class very confusing. Some methods return values that I would not expect to return anything (e.g. normalize()). Furthermore, I do not think that having an Euler angle representation stored with the quaternion is a good idea. And even if you think it is, I don't understand the purpose of the method Euler() since it neither has parameters, nor can you set the Euler angles from outside.
I created a function for drawing a circle in oepnGL java, and I want to rotate another circle on the circumference of a circle ?
This is my function for create circle, how to change it for drawing the circle on circumference?
For example create a new circle using as center coordinates points from first circle ?
private void rotateAroundOz(GL2 gl, int r, double cx, double cy) {
int step = 1;
gl.glLineWidth(5);
gl.glBegin(GL.GL_LINE_LOOP);
for (int i=0; i<360; i+=step) {
gl.glColor3d(1, 0, 0);
gl.glVertex2d(cx + r * Math.cos(Math.toRadians(i)), cy + r * Math.sin(Math.toRadians(i)));
}
gl.glEnd();
}
You just have to draw a circle (with rotateAroundOz()) using a position that you compute from
cx + r * Math.cos(Math.toRadians(i)), cy + r * Math.sin(Math.toRadians(i))
// This is the attributes of the invisible circle: "PositionCircle"
//that will gives you the circumference
float positionCircle_Radius = 1.0;
float positionCircle_CenterX = 0.0;
float positionCircle_CenterY = 0.0;
// This is actually the circle that you want to draw from the
// "PositionCircle"
int positionOnCircumferenceInDegrees = 90;
float drawnCircle_Radius = 2.0;
float drawnCircle_CenterX = positionCircle_CenterX + positionCircle_Radius * Math.cos(Math.toRadians(positionOnCircumferenceInDegrees));
float drawnCircle_CenterY = positionCircle_CenterY + positionCircle_Radius * Math.sin(Math.toRadians(positionOnCircumferenceInDegrees));
rotateAroundOz(gl, drawnCircle_Radius, drawnCircle_CenterX, drawnCircle_CenterY)
So you can add positionOnCircumferenceInDegrees and drawnCircle_Radius as parameters for your new function.
(That's my first response ever on SO :p hope it's comprehensible!)
So I am making a 2D android game where you aim with your cursor and your character shoots where your cursor clicks.
When the arrow is created this method is called
private final float GRAVITY = 100, SPEED = 50f;
public Arrow(float dx, float dy, float x1, float y1, float x2, float y2,)
{
destination = new Vector2(dx, dy);//mouse clicked here
//x1 and y1 are the starting coordinates
bounds = new Polyline(new float[]{x1, y1, x2, y2});
double r = Math.atan2(dy-y1, dx-x1);//calculate angle
velocity = new Vector2();
velocity.x = (float)(Math.cos(r) * SPEED);
velocity.y = (float)(Math.sin(r) * SPEED) + ACCOUNT FOR GRAVITY;
acceleration= new Vector2(0, GRAVITY);
}
and this is the update method, pretty straight forward
public void update(float delta)
{
velocity.add(acceleration.cpy().scl(delta));
position.add(velocity.cpy().scl(delta));
}
How do I account for gravity? If gravity is set to 0 the arrow travels in a straight line to the coordinates the mouse clicked, but with gravity it always falls short. Im not sure how to account for gravity. I think delta might be screwing me up.
This is more of a math / physics question than a programming question. So first of all, you know the horizontal velocity of the arrow is constant (unless you have air resistance, in which case it is a lot more complicated). You can calculate the time it will take for the arrow to reach it's destination's x coordinate.
let (dx, dy) = displacement from launcher to destination
let c = cos(angle), s = sin(angle), vx = c * speed, vy = s * speed
vx * t = dx
t = dx / vx
With this value, you can compute the vertical displacement
dy = 0.5*acc * t^2 + V0 * t
dy = 0.5*acc * (dx/vx)^2 + vy*t
dy = 0.5*acc * (dx/(c*speed))^2 + (s*speed)*(dx/(c*speed))
since sin = sqrt(1 - cosine^2),
dy = 0.5*acc * (dx/(c*speed))^2 + (sqrt(1-c^2)*speed)*(dx/c*speed))
Now you have an equation with only known values (acc, dy, dx, speed) and c. If you solve for c, you know the cosine and you can find the sin.