This javascript code measures the horizontal acceleration on the x axis. The intention is to measure the acceleration if the device was to move on a straight line parallel to the earth "horizontal" surface, irrespective of the device orientation.
Can that be done via gyroscope data during a given period of acceleration approx. 5 seconds? How? thx
let xAxis = '';
function handleMotionEvent(event) {
let x = event.accelerationIncludingGravity.x;
if (!xAxis) {
xAxis = x;
} else {
if (Math.abs(x - xAxis) > 0.4) {
console.log(x);
}
}
}
if (window.DeviceMotionEvent) {
window.addEventListener("devicemotion", handleMotionEvent, true);
}
Based on this documentation, the accelerationIncludingGravity property is an object providing information about acceleration on three axis. Each axis is represented with its own property:
x: acceleration in x axis (west to east)
y: acceleration in y axis (south to north)
z: acceleration in z axis (down to up)
So from mathematics, to get total horizontal acceleration we can combine x and y components using r^2 = x^2 + y^2 formula.
...
a = Math.sqrt(x*x + y*y); //horizontal acceleration
...
Related
I am trying to animate a growing exponential graph using P5js.
I have successfully plotted the graph itself, but the "rulers/scales" on the sides won't work.
I want the "window" to scale according to the X and Y axis, just like this example: Animation I am trying to replicate this animation
I want the graph to "grow" and the rulers/scales on the sides to represent the growth, X is time and Y the multiplier (big text in the middle). As seen on the animation I linked, X and Y values move towards the origin after the graph has gone outside the box.
Link to P5 editor with code: P5 web editor
There is at least one big error in
scaleLevel -= 0.1;
because this way it gets zero and you will divide by it within REscale.
Your intention is to draw some exponential function f(x) in the interval 0 to x. The value of x is increasing by time. The value of the exponential function is also rising but with another rate. So you will have to use two separate scale factors: sX = display_width / x and sY = display_hight / f(x).
I hope this gets you started somehow.
Here is some code to illustrate which way to go:
var x = 10
function setup() {
createCanvas(400, 400);
noLoop();
}
function my_func(x) {
return exp(x * 0.2);
}
function draw() {
background(220);
stroke(155);
strokeWeight(8);
noFill();
beginShape();
let nPoints = 20;
let dx = x / nPoints;
let ymax = my_func(x);
let dy = ymax / nPoints;
for (let i = 0; i <= x; i += dx) {
xValue = map(i, 0, x, 0, width);
yValue = map(my_func(i), 0, ymax, height, 0);
vertex(xValue, yValue);
}
endShape();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.1/p5.js"></script>
I omitted the ticks on the axes. I decided to create a static plot for the value of x in the between 0 and 10. The code can easily be changed into an animation by removing the noLoop(); statement in the setup function and adding the line x += somedelta; within the draw function.
I want to apply a forward force in relation to the object's local axis, but the engine I'm using only allows to me apply a force over the global axis.
I have access to the object's global rotation as a quaternion. I'm not familiar with using quats however (generally untrained in advanced maths). Is that sufficient information to offset the applied force along the desired axis? How?
For example, to move forward globally I would do:
this.entity.rigidbody.applyForce(0, 0, 5);
but to keep that force applied along the object's local axis, I need to distribute the applied force in a different way along the axes, based on the object's rotational quat, for example:
w:0.5785385966300964
x:0
y:-0.815654993057251
z:0
I've researched quaternions trying to figure this out, but watching a video on what they are and why they're used hasn't helped me figure out how to actually work with them to even begin to figure out how to apply the offset needed here.
What I've tried so far was sort of a guess on how to do it, but it's wrong:
Math.degrees = function(radians) {
return radians * 180 / Math.PI;
};
//converted this from a python func on wikipedia,
//not sure if it's working properly or not
function convertQuatToEuler(w, x, y, z){
ysqr = y * y;
t0 = 2 * (w * x + y * z);
t1 = 1 - 2 * (x * x + ysqr);
X = Math.degrees(Math.atan2(t0, t1));
t2 = 2 * (w * y - z * x);
t2 = (t2 >= 1) ? 1 : t2;
t2 = (t2 < -1) ? -1 : t2;
Y = Math.degrees(Math.asin(t2));
t3 = 2 * (w * z + x * y);
t4 = 1 - 2 * (ysqr + z * z);
Z = Math.degrees(Math.atan2(t3, t4));
console.log('converted', {w, x, y, z}, 'to', {X, Y, Z});
return {X, Y, Z};
}
function applyGlobalShift(x, y, z, quat) {
var euler = convertQuatToEuler(quat.w, quat.x, quat.y, quat.z);
x = x - euler.X; // total guess
y = y - euler.Y; // total guess
z = z - euler.Z; // total guess
console.log('converted', quat, 'to', [x, y, z]);
return [x, y, z];
}
// represents the entity's current local rotation in space
var quat = {
w:0.6310858726501465,
x:0,
y:-0.7757129669189453,
z:0
}
console.log(applyGlobalShift(-5, 0, 0, quat));
Don't laugh at my terrible guess at how to calculate the offset :P I knew it was not even close but I'm really bad at math
Quaternions are used as a replacement for euler angles. Your approach, thus, defeats their purpose. Instead of trying to use euler angles, levy the properties of a quaternion.
A quaternion has 4 components, 3 vector components and a scalar component.
q = x*i + y*j + z*k + w
A quaternion therefore has a vector part x*i + y*j + z*k and a scalar part w. A vector is thus a quaternion with a zero scalar or real component.
It is important to note that a vector multiplied by a quaternion is another vector. This can be easily proved by using the rules of multiplication of quaternion basis elements (left as an exercise for the reader).
The inverse of a quaternion is simply its conjugate divided by its magnitude. The conjugate of a quaternion w + (x*i + y*j + z*k) is simply w - (x*i + y*j + z*k), and its magnitude is sqrt(x*x + y*y + z*z + w*w).
A rotation of a vector is simply the vector obtained by rotating that vector through an angle about an axis. Rotation quaternions represent such an angle-axis rotation as shown here.
A vector v can be rotated about the axis and through the angle represented by a rotation quaternion q by conjugating v by q. In other words,
v' = q * v * inverse(q)
Where v' is the rotated vector and q * v * inverse(q) is the conjugation operation.
Since the quaternion represents a rotation, it can be reasonably assumed that its magnitude is one, making inverse(q) = q* where q* is the conjugate of q.
On separating q into real part s and vector part u and simplifying the quaternion operation (as beautifully shown here),
v' = 2 * dot(u, v) * u + (s*s - dot(u, u)) * v + 2 * s * cross(u, v)
Where dot returns the dot product of two vectors, and cross returns the cross product of two vectors.
Putting the above into (pseudo)code,
function rotate(v: vector3, q: quaternion4) -> vector3 {
u = vector3(q.x, q.y, q.z)
s = q.w
return 2 * dot(u, v) * u + (s*s - dot(u, u)) * v + 2 * s * cross(u, v)
}
Now that we know how to rotate a vector with a quaternion, we can use the world (global) rotation quaternion to find the corresponding world direction (or axis) for a local direction by conjugating the local direction by the rotation quaternion.
The local forward axis is always given by 0*i + 0*j + 1*k. Therefore, to find the world forward axis for an object, you must conjugate the vector (0, 0, 1) with the world rotation quaternion.
Using the function defined above, the forward axis becomes
forward = rotate(vector3(0, 0, 1), rotationQuaternion)
Now that you have the world forward axis, a force applied along it will simply be a scalar multiple of the world forward axis.
I have a multiplayer Javascript game where the player is a circle, and is able shoot/"eject" circle bullets in the direction that the player is rotated. My code is working perfectly, except it shoots from the middle of the player. I would like it so that the circles are shot from the top right position of the player, where the gun is located. The issue is that when the players rotation changes, you cannot simply add (1, -1) to the position of the player.
Here is my code:
GameServer.prototype.ejectMass = function(client) {
for (var i = 0; i < client.cells.length; i++) {
var cell = client.cells[i]; // the player
var angle = this.toRad(client.rotation); // rotation of the player
var d = new Vec2(Math.sin(angle), Math.cos(-angle)).scale(-180);
var sq = (~~d.sqDist(d));
d.x = sq > 1 ? d.x / sq : 1;
d.y = sq > 1 ? d.y / sq : 0;
// cell.position is the players position
var pos = new Vec2(
cell.position.x + d.x * cell._size,
cell.position.y + d.y * cell._size
);
var ejected = 0;
// Create cell and add it to node list
ejected = new Entity.EjectedMass(this, null, pos, this.config.ejectSize * cell.ejectSize); // the bullet being shot
ejected.setBoostDefault(-this.config.ejectVelocity * cell.ejectSpeed, angle);
ejected.ejectedOwner = cell; // set the person who shot the bullet
this.addNode(ejected); // add the bullet into the game
if (typeof ejected !== 'undefined') {
setTimeout(this.removeNode.bind(this, ejected), 1000); // remove the ejected bullet after 1 second
}
}
};
And here is an illustration of the current way it is working:
Assuming that the player (circle) is at its own local origin then the position of the gun is relative to the player's origin. Assuming the coordinate system is that of the canvas with forward along the x axis from left to right, and clockwise 90deg (left of player) is the Y axis going down.
Image: C is local circle origin (0,0) with Forward along the red arrow from C, Gx and Gy are the local coordinates of the gun from the circle center C. Top left shows the canvas coordinate (world) system origin. In code below, The player position is relative to that world origin. The final gunPos is also give relative to the world coordinates. B vec is the bullets bullet.delta vector
const bulletSpeed = 10;
var gunPos = {x : 10, Y : 10} // ten units forward ten units left of circle center
var player = {rotation : ?, x : ?, y : ?} // unknown player position and rotation
// get the unit vector of the rotated x axis. Along player forward
var xAx = Math.cos(player.rotation);
var xAy = Math.sin(player.rotation);
// transform the gunpos to absolute position (world coordinates) of rotated player
var rotatedGunPos = {};
rotatedGunPos.x = gunPos.x * xAx - gunPos.y * xAy + player.x;
rotatedGunPos.y = gunPos.x * xAy + gunPos.y * xAx + player.y;
// and fire the bullet from
var bullet = {}
bullet.x = rotatedGunPos.x;
bullet.y = rotatedGunPos.y;
// bullet vector is
bullet.deltaX = xAx * BULLET_SPEED;
bullet.deltaY = xAy * BULLET_SPEED;
You didn't provide enough details about your layout such as what are orientations of X- and Y-axis? Where is 0 angle? Is angle clockwise or counterclockwise? Still the basic idea is the same. Let's assume that X-axis is to the right and Y-axis is down as it looks like from your attached image and adding (1, -1) to get top-right corner. Also assume that angle = 0 for X-axis and angle is clockwise i.e. angle = Pi/2 is aligned with positive direction of Y-axis = down. When the gun is pointed Up i.e. angle = -Pi/2 your starting point is (1, -1) which is at distance sqrt(2) and additionally rotated to Pi/4 corresponding to gun orientation. This is all you need to know.
var angle = this.toRad(client.rotation); // rotation of the player
var gunStartAngle = angle + Math.PI/4;
var sqrt2 = Math.sqrt(2);
// cell.position is the players position
var pos = new Vec2(
cell.position.x + cell._size * sqrt2 * Math.cos(gunStartAngle),
cell.position.y + cell._size * sqrt2 * Math.sin(gunStartAngle)
);
Obviously if your layout is different, you should fix the details of the math but the idea remains the same.
I am writing a mobile application for Windows Phone in HTML5. I want to detect the phone's rotation around the z-axis (I mean the rotation that is used for example in Doodle jump to make the character jump to the left or to the right).
In my application, a line that is vertical in the screen must remain vertical (absolute position in the real world) if I rotate the screen to the left or to the right. To accomplish this, I use the relative values of the angle: each time the screen is rotated a little, I rotate the points of the line by the same amount, but negative.
This works PERFECTLY if the phone is laying on a table or almost horizontal, but the behaviour is much less precise if for example I watch the phone in front of me. Anyway this is not a big problem of course because most users watch the phone from up to down, but a game like doodle jump behaves perfectly also in this situation.
Here is part of the code:
window.addEventListener("deviceorientation", handleOrientation, true);
function handleOrientation(event) {
alphaDiff = Math.floor(event.alpha) - alphaOld;
alphaOld = Math.floor(event.alpha);
//Rotation of the vertical line (x1,y1) - (x2,y2) around the center of the screen
var newPoint = rotate(225, 400, x1, y1, alphaDiff);
x1 = newPoint[0];
y1 = newPoint[1];
newPoint = rotate(225, 400, x2, y2, alphaDiff);
x2 = newPoint[0];
y2 = newPoint[1];
}
function rotate(cx, cy, x, y, angle) {
var radians = (Math.PI / 180) * angle;
cos = Math.cos(radians);
sin = Math.sin(radians);
nx = (cos * (x - cx)) - (sin * (y - cy)) + cx;
ny = (sin * (x - cx)) + (cos * (y - cy)) + cy;
return [nx, ny];
}
Have you tried beta (for the x-axis) or gamma (for the y-axis)?
e.g.
function handleOrientation(event) {
var absolute = event.absolute;
var alpha = event.alpha; // z-axis
var beta = event.beta; // x-axis
var gamma = event.gamma; // y-axis
// Do stuff with the new orientation data
}
Orientation Values Explained
The value reported for each axis indicates the amount of rotation around a given axis in reference to a standard coordinate frame.
The DeviceOrientationEvent.alpha value represents the motion of the device around the z-axis, represented in degrees with values ranging from 0 to 360.
The DeviceOrientationEvent.beta value represents the motion of the device around the x-axis, represented in degrees with values ranging from -180 to 180. This represents a front to back motion of the device.
The DeviceOrientationEvent.gamma value represents the motion of the device around the y-axis, represented in degrees with values ranging from -90 to 90. This represents a left to right motion of the device.
It also worth checking browser compatibility for this API.
As well as deviceorientation, you can check compassneedscalibration and devicemotion. For more details on deviceorientation see MDN.
A combination of beta and gamma seems to give pretty good results.
Basically, I normalize the values to a specific range and get the ratio.
window.addEventListener("deviceorientation", function(event){
var b = Math.abs(event.beta)/90;
if(b>1) b = 2-b;
var g = event.gamma/90;
if(Math.abs(event.beta)>90) g = -g;
var x = g/Math.max(0.25,b);
});
The final value is in x, positive means right, negative means left.
It works with the phone held horizontally or at any angle facing you.
Update: To make the detection work in a vertical position, the code calculates how much the phone is leaning and to which direction.
The value in x is always positive if the phone is leaning right and negative if leaning left.
This example shows a blue box in the middle of the screen and moves it to whichever direction the phone is leaning:
<!DOCTYPE html>
<html>
<head><meta name="viewport" content="width=1000,user-scalable=no"/></head>
<body style="margin:0">
<pre id="log" style="font-size:4em;"></pre>
<div id="box" style="background:#69c;width:100px;height:100px;position:relative;left:450px"></div>
<script>
var box = document.getElementById("box");
var log = document.getElementById("log");
var smoothx = 0;
window.addEventListener("deviceorientation", function(event){
var b = Math.abs(event.beta)/90;
if(b>1) b = 2-b;
var g = event.gamma/90;
if(Math.abs(event.beta)>90) g = -g;
var x = g/Math.max(0.25,b);
smoothx = smoothx*0.7+x*0.3;
box.style.left = Math.max(0, Math.min(900, smoothx*500+450))+"px";
log.innerText = x.toFixed(1)+"\n"+smoothx.toFixed(1);
});
</script>
</body>
</html>
Because the sensor seems to be very sensitive to movement, I implemented a simple smoothing that gives a steadier value in smoothx.
Use x for immediate value without smoothing.
I'm kinda confused with this one.
I have an object and I know it's velocities on axis x and y. My problem is how to determine the angle at which it's moving.
function Object(){
this.velocity = {x: 5, y: 1};
}
Basically I Know that a vector's direction is x_projectioncos(deg) + y_projectionsin(deg), but I don't know how to get those projections since I only have the velocity, as I said I'm really confused.
#EDIT:
in addition to the accepted answer, here's what I did to get a full 360 degree spectrum
var addDeg = 0;
if(obj.velocity.x<0)
addDeg = obj.velocity.y>=0 ? 180 : 270;
else if(obj.velocity.y<=0) addDeg = 360;
deg = Math.abs(Math.abs(Math.atan(obj.velocity.y/obj.velocity.x)*180/Math.PI)-addDeg)
I don't know how to get those projections since I only have the
velocity
Actually, what you seem to be missing is that you already have the projections. That's what x and y are.
x is speed * cos(angle)
y is speed * sin(angle)
So y/x = sin(angle)/cos(angle) which is tan(angle) so angle=arctan(y/x).
That's the angle rotating anti-clockwise starting from the x axis (with x pointing right and y pointing up).
Find the angle between that vector and (1,0) (Right horizontal positive direction).
The math is:
A = (5,1)
B = (1,0)
A.B = |A||B|cos(angle) -> angle = arccos((|A||B|)/(A.B))
Dot product, check geometric definition
Edit:
Another option is to use the cross product formula:
|AxB| = |A||B|sin(angle) -> angle = arcsin((|A||B|)/(|AxB|))
It will give you the angle you need.
There is an easier way to get full 360 degrees. What you're looking for, is Math.atan2:
deg = Math.atan2(obj.velocity.y,obj.velocity.x)*180/Math.PI;