I'm trying to simulate an analog stick on a mobile platform.
I calculate a stick vector which based on the position of the finger and the radius of the stick returns a value from -1 to 1.
public void calcStickVector(float x, float y)
{
float cx = getCenterX();
float cy = getCenterY();
float distX = x - cx;
float distY = y - cy;
distX /= getRadius();
distY /= getRadius();
distX = JMath.clamp(-1.0f, 1.0f, distX);
distY = JMath.clamp(-1.0f, 1.0f, distY);
stickVector.x = distX;
stickVector.y = distY;
}
public RectF getInnerStickRect()
{
float r = getInnerRadius();
float cx = getCenterX() + (getRadius() * getStickVector().x);
float cy = getCenterY() + (getRadius() * getStickVector().y);
innerStickRect.left = cx - r;
innerStickRect.top = cy - r;
innerStickRect.right = cx + r;
innerStickRect.bottom = cy + r;
return innerStickRect;
}
It almost works, but visually the inner stick, when moved around seems to form a square rather than going around in a circle. Is there something wrong with my logic?
Thanks
As it is, you really are making a box with 1's as the corners. You need to normalize the vector(divide x and y by distance) instead of dividing by radius and clamping.
double dist = Math.sqrt(distX*distX) + (distY*distY));
distX /= dist;
distY /= dist;
Related
I have to points in 3D space that I want to draw a cylinder between. I currently have this code:
applet.pushMatrix();
applet.stroke(0);
applet.fill(0);
applet.line(this.start.x, this.start.y, this.start.z, this.end.x, this.end.y, this.end.z); //debug, shows where the cylinder should be
applet.translate(this.start.x, this.start.y);
applet.beginShape(PConstants.TRIANGLE_STRIP);
float xdif = this.end.x - this.start.x;
float ydif = this.end.y - this.start.y;
float zdif = this.end.z - this.start.z;
float rx = (float)Math.atan(ydif / xdif);
float ry = (float)Math.atan(zdif / xdif);
float rz = (float)Math.atan(zdif == 0 ? 0 : ydif / zdif);
applet.rotateZ((float) (rx - Math.PI / 2));
applet.rotateY((float) (ry - Math.PI / 2));
applet.rotateX((float) (rz - Math.PI / 2));
float r = 20;
float len = HelperFunctions.distance(this.start, this.end) / 2;
boolean w = false;
for (int i = 0; i < 2; i += 1) {
for (float j = 0; j < Math.PI * 2 + 0.2; j += 0.4) {
w = !w;
float z = (i + (w ? 0 : 1)) * len;
float x = (float)(r * Math.cos(j));
float y = (float)(r * Math.sin(j));
applet.vertex(y, x, z);
}
}
applet.endShape();
applet.popMatrix();
The cylinder does draw correctly, however, it doesn't rotate correctly. I'm trying to use trig to determine the rotation angles, however, I'm not sure I've done it correctly. How would I get the correct angles to rotate around such that it is drawn from Vector start to Vector end?
Vector is a custom class with just a float x, y, z, and HelperFunctions.distance takes two Vectors and calculates the distance (pythagoras).
Thanks in advance.
I have custom View for color picking and I want to restrict selector be inside circle cardview in the center.
Selector is moving by ACTION_MOVE and ACTION_UP.Coordinates change via this function:
private void updateSelector(float eventX, float eventY) {
float x = eventX - centerX;
float y = eventY - centerY;
double r = Math.sqrt(x * x + y * y);
if (r > radius) {
x *= radius / r;
y *= radius / r;
}
currentPoint.x = x + centerX;
currentPoint.y = y + centerY;
selector.setCurrentPoint(currentPoint);
}
I've also written this helper function to check if selector inside cardview and it's correctly works, but I've no idea how to restrict selector go in.
private boolean isInBounds(float x, float y) {
return Math.sqrt(x - centerX) + Math.sqrt(y - centerY) <= Math.sqrt(cardradius);
}
I am having a bit of trouble trying to figure out how to draw paths from a point on a canvas with the start of each path being equally distanced from the initial point. To illustrate what I mean, the code that I have so far is able to generate this:
and the desired result is something like this:
My code:
int n = 3;
int r;
double x;
double y;
point1 = new Point(mWidth/2, mHeight/2);
double angle;
double angleFactor;
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (int i = 0; i < 3; i++){
angleFactor = 2 * Math.PI / n;
angle = i * angleFactor;
x = (point1.x) + r * Math.cos(angle);
y = (point1.y) + r * Math.sin(angle);
//Draw paths
path.reset();
path.moveTo(point1.x, point1.y);
path.lineTo((float) x, (float) y);
canvas.drawPath(path, paint);
}
}
Is there a simple solution to this?
Since you want to have a tiny distance between the offset of a line and the center point, you can define start coordinates like this:
double xStart, xEnd;
double yStart, yEnd;
double offsetFraction = 0.1;
Inside the for loop in onDraw() :
double lengthX = r * Math.cos(angle);
double lengthY = r * Math.sin(angle);
xStart = (point1.x) + offsetFraction * lengthX;
yStart = (point1.y) + offsetFraction * lengthY;
xEnd = (point1.x) + lengthX;
yEnd = (point1.y) + lengthY;
//Draw paths
path.reset();
path.moveTo((float) xStart, (float) yStart);
path.lineTo((float) xEnd, (float) yEnd);
canvas.drawPath(path, paint);
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.
this is a more math related question.
In my android app I have a canvas that draws a line from start to endpoint.
I also have an angle attribute to rotate the line.
I tried to apply the code bellow to my cocept, but it jumps around weirdly, not pointing in the direction it is supposed to. (0deg = horizontal line)
//Coordinates for P1 and P2
int startx = 0;
int starty = 66;
int endx = 420;
int endy = 66;
//Alpha
float angle = 0.000F;
final float radius = 209.500F;
final float extra_radius = 20.000F; //required later - don't mind it
private void reCal(float[] vals) {
float xAcc = vals[0]*(-1);
float yAcc = vals[1]*(-1);
angle = yAcc / 10.000F * 90.000F;
final float rRadius = radius + extra_radius;
startx = (int) (radius - Math.cos(angle) * rRadius); //left
endx = (int) (radius + Math.cos(angle) * rRadius); //right
starty = 66 + (int) (Math.sin(angle) * rRadius); //top
endy = 66 - (int) (Math.sin(angle) * rRadius); //bot
}
Are their any type conversions that might cause this, or is my math wrong?
UPDATE:
I looked at Math.cos(angle) and Math.sin(angle) at runtime.They jump around between 0 and 1, even if angle is only changed by a little amount.
Now I am wondering: DO THE METHODS TAKE RAD OR DEG AS PARAMETER? (I need deg, obviously)
if you want to rotate a line starting at [cx, cy] and length of dx, use that code snippet:
canvas.save();
canvas.rotate(angle, cx, cy);
canvas.drawLine(cx, cy, cx + dx, cy, paint);
canvas.restore();