HTML5 orientation axes jump - javascript

I'm making a web app for which I need to know the orientation of the phone around the depth axis: like where the phone would be if it were rotated like the arms on the face of clock. The beta axis tells me this.
However, when I hold the phone in portrait mode facing me, when I tilt the phone back and forth (the way the top card on a Rolodex would be tilted), all the values jump. See video:
https://drive.google.com/file/d/0B5C3MHv2Hmc6VTI2RDlOSkJPVHc/view?usp=sharing
I've tried it on two phones and they are consistent. How can I get that beta axis value without the jumping? I'm sure there is a mathematical way to cancel out the jumps, but I'm not sure where to start.

I was about to give up and I thought, "Gee, I never have that kind of problem with Unity. But, of course, Unity has quaternions." This thought led me to think that there must be a Quaternion library for JavaScript and indeed there is.
This led me to this code: I just rotate "up" to the phone orientation converted to a quaternion and grab the z axis:
let quaternion = new Quaternion();
let radian = Math.PI / 180;
$(window).on('deviceorientation', function(event) {
event = event.originalEvent;
quaternion.setFromEuler(
event.alpha * radian,
event.beta * radian,
event.gamma * radian);
let vector3 = quaternion.rotateVector([0, 1, 0]);
let result = vector3[2];
});
This gives exactly the result I need!

Related

struggling to create a smooth-moving snake in p5 js - tail catching up to head

I'm putting together a p5 sketch with little wiggling snakes that move randomly across the screen.
Unfortunately, the tail keeps catching up to the head every time it does a sharpish turn.
Here is the function I'm using to calculate the move, I've tried with a few different ways of calculating the speed, fixed numbers, relative to the snake's length.
It's supposed to work by moving the snakes head (points[3]) in a semi-random direction and then having each body point move towards the one before it by the same amount. This isn't working, and I feel there's something wrong with my algorithm itself. I'm not familiar with these kinds of intermediate random-walks, so I've just been going by guesswork for the most part.
this["moveCurve"] = function() {
let newDir = this["oldDir"] + (random() - 1/2)*PI/6;
let velocity = createVector(1,0);
velocity.setMag(5);
velocity.setHeading(newDir);
this["points"][3].add(velocity);
for (let i = 2; i >= 0; i--) {
this["points"][i].add(p5.Vector.sub(this["points"][i + 1],this["points"][i]).setMag(5));
}
this["oldDir"] = newDir;
}
If you have any idea what I could do to make this work properly, I'd love to hear your advice. Thanks!
This does look like an algorithmic issue / not a bug with how you implemented it.
Here's my go at explaining why the gap between two points must decrease in this algorithm:
Let's consider just a two point snake, with two points Hi (head) and Ti (tail) at an initial locations Hi: (20, 0), and Ti: (0, 0). So, the heading here is 0 radians.
What happens when moveCurve is called? A new heading is chosen (let's use PI/2, a right angle to make it easy to imagine) and using a fixed velocity of 5 we calculate a new position for the head of (20, 5), let's call it Hf. T also moves, but it also moves toward Hf at the same 5 unit velocity, ending up at about (4.85, 1.21). The distance between these two final positions is now 15.62657, which is smaller than the initial distance.
To visualize this, think of the triangle formed between Ti, Hi, and Hf. Ti, and Hi, form the base of this triangle. Ti will move along the hypotenuse to get to Tf, while Hi will move along the other side. The directions they are moving in form an angle which is smaller than PI radians and both points are moving at the same speed so intuitively the points must be getting closer together.
So how to solve this? Well if we consider our tiny snake's movement, the tail moved in a decent direction but too far. One solution might be to scale the velocity vector in order to maintain a fixed distance between points instead of using a fixed velocity. For example instead of stepping 5 units along the hypotenuse from Ti toward Hf in the example, you could step 20 units along the hypotenuse from Hf toward Ti. I'm not sure how this would work out for your snake, just an idea!
Keep slithering!
Fortunately, it turns out p5's documentation itself had the answer for me. By adapting the code from here to use p5 Vectors, I was able to get it all working.
The segLengths property is defined when the object is made, just takes the distances between all the points.
this["moveCurve"] = function() {
let newDir = this["oldDir"] + (random() - 1/2)*PI/6;
let velocity = p5.Vector.fromAngle(newDir).setMag(5);
this["points"][3].add(velocity);
for (let i = 2; i >= 0; i--) {
this["points"][i].set(p5.Vector.sub(this["points"][i+1], p5.Vector.fromAngle(p5.Vector.sub(this["points"][i+1],this["points"][i]).heading()).setMag(this["segLengths"][i])));
}
this["oldDir"] = newDir;
}
I might spend a little time trying to clean up the code a bit, it's a jot messy for my tastes at the moment. But it works.

How can I detect that the device was rotated 360deg given (x,y,z) gyroscope?

I am using react-native-sensor to grab the raw data from the sensor.
setUpdateIntervalForType(SensorTypes.gyroscope, 100)
gyroscope.subscribe(({ x, y, z, timestamp }) => {
let pitch = Math.atan2(-x, -z) * 180 / Math.PI;// In degrees
let roll = Math.atan2(-y, -x) * 180 / Math.PI;// In degrees
let yaw = Math.atan2(y, -z) * 180 / Math.PI;// In degrees
this.setState({pitch: pitch, roll: roll, yaw: yaw})
})
How do i know that the device was spined 360
A bit of theory
Generally speaking, gyroscopes measure rotational motion. Most of the sensors that are included in our phones will specifically measure angular velocity. It means that the output in most cases will describe how much the phone has rotated over time and is usually expressed in degrees per second (°/s).
There are 3 axes that you can rotate around: x, y and z. There's a nice picture of it in MATLAB documentation:
There are also 3 important concepts (that you used in your snippet): pitch, roll and yaw (or azimuth). Again, MATLAB documentation comes in handy. They described it very well in the "More about" section, but I recommend reading the whole article.
Get your hands dirty
As far as I'm aware, the react-native-sensors library will return exactly degrees per second. This means, that using the provided timestamp you could try to count how much the phone rotated around any axis within any time delta. You would simply need to save values and timestamps, do a few transformations and you would get the result. This, however, would require additional time and memory.
There's an easier way, which you probably already realize after reading the attached article. Depending on the axis you want to rotate around, use pitch, roll or yaw.
Also, if you use the library just to get the gyroscope data, you might want to consider Detecting device orientation Web API. It is still an experimental feature but will work in most of the modern browsers.

N-Body Gravity / Solar System Javascript Simulation

Good day, I am trying to create a simple 2D solar system model in javascript, but am having some trouble understanding how to go about calculating where planets will be for the next frame, aswell as a few other bits which I'll go into detail with soon.
After watching this very nice video and a whole bunch of his others, I made a quick MS paint image to try and simplify my situation.
With the second scene, you can see that the new position is calulated using the velocity, gravitational pull, and the angle between these two 'directions'?
I cannot get my head around how to figure this all out.
Below is a JS fiddle of my code. You'll notice I'm trying my best to use real NASA given data to keep it accurate.
You'll want to look specifically at lines 138 which is where all the calculations for its next move are made.
https://jsfiddle.net/c8eru7mk/9/
attraction: function(p2) {
// Distance to other body
var dx = p2.position.x - this.position.x;
var dy = p2.position.y - this.position.y;
var d = Math.sqrt(dx ** 2 + dy ** 2); // Possibly correct
// Force of attracrtion
this.f = G * (this.mass * p2.mass) / (d ** 2); // Possibly Correct
// Direction of force, If you read it hard enough you should be able to hear my screams of pain
// Not sure if this is correct, most likely not.
var theta = Math.atan2(dy, dx);
var fx = Math.cos(theta) * this.f;
var fy = Math.sin(theta) * this.f;
this.velocity.x += fx / this.mass;
this.velocity.y += fy / this.mass;
this.position.x += this.velocity.x;
this.position.y += this.velocity.y;
}
The problems I'm currently facing are
If I am to use NASA values, the distance between planets is so big, they won't fit on the screen, and I can't simply scale the distances down by multiplying them by 0.0002 or whatever, as that'll mess with the gravitational constant, and the simulation will be completely off.
I have no idea how to caluclate the next position and my brain has imploded several times this past week trying to attempt it several times.
I have no idea on how to check if my configuration data of planets is wrong, or if the simulation is wrong, so I'm pretty much just guessing.
This is also my first time actually coding anything more complex than a button in javascript too, so feedback on code layout and whatnot is welcome!
Many thanks
Using NASA values is not a problem when using separate coordinates for drawing. Using an appropriate linear transfomration from real coordinates to screen coordinatees for displaying does not influence the physical values and computations.
For simulating the motion of a planet with iterative updates one can assume that the gravitational force and the velocity are constant for a small portion of time dt. This factor dt is missing in your conversions from accelration to velocity and from velocity to distance. Choosing an appropriate value for dt may need some experiments. If the value is too big the approximation will be too far off from reality. If the value is too small you may not see any movement or rounding errors may influence the result.
For the beginning let us assume that the sun is always at (0,0). Also for a start let us ignore the forces between the planets. Then here are the necessary formulas for a first not too bad approximation:
scalar acceleration of a planet at position (x,y) by the gravitational force of the sun (with mass M): a = G*M/(d*d) where d=sqrt(x*x+y*y). Note that this is indepent of the planet's mass.
acceleration vector: ax = -a*x/d, ay = -a*y/d (the vector (-x,-y) is pointing towards the sun and must be brought the length a)
change of the planet's velocity (vx,vy): vx += ax*dt, vy += ay*dt
change of the planet's position: x += vx*dt, y += vy*dt

Three.js Camera Rotation Needs Update

I have a Three.js perspective camera and it orients differently in different browsers. I'm trying to normalize the behavior by rotating the camera in Firefox to match Chrome. However, rotating the camera 90 degrees in the render loop causes the x-axis rotation to be off.
camera.rotation.y = camera.rotation.y - (90 * Math.PI / 180);
It looks like the camera is rotating around the y-axis without updating so that the x-axis is in the right place, so what would normally control pitch in my environment (x), is controlling roll (which should be z). Do I have to update the euler order? That seems like a less than ideal solution.
When you use an euler you can inverse the vector order :
var a = new THREE.Euler( 0, 1, 1.57, 'XYZ' );
If the vector order doesn't corresponds your expectation you can use the reorder function.
a.reorder('ZYX');
As far as I'm concerned, that's the best you can do to solve your problem.

How to map deviceorientation to MozOrientation values

I have previously created a labyrinth game which works with Firefox's MozOrientation. I am now looking into getting it working with WebKit also...
But Webkit uses the w3c's DeviceOrientation. The values appear to be totally different, but someone must have some algorithm to get it from one to the other? Or am I missing something simple?
The labyrinth game with github link
http://playground.marmaladeontoast.co.uk/labyrinth/
MozOrientation
https://developer.mozilla.org/en/Detecting_device_orientation
DeviceOrientation
http://dev.w3.org/geo/api/spec-source-orientation.html
Some sample values I have obtained:
Chrome
alpha = null
beta = -178
gamma = 4.57
Firefox
x = 0.035999998450279236
y = -0.02800000086426735
z = 1
Any help would be greatly appreciated :)
I've been playing around with this a bit. The source below 'maps' both alternatives to an understandable degrees range for x/gamma and y/beta.
The MozOrientation.z value is completely different from the deviceorientation.alpha value. The first returns the vertical orientation, the latter returns a sort of compass value. These values are therefor not interchangable / convertible.
function displayOrientation(orientData){
var x = Math.round(orientData.x);
var y = Math.round(orientData.y);
var z = orientData.z;
document.getElementById('x').value = x;
document.getElementById('y').value = y;
document.getElementById('z').value = z;
}
window.addEventListener("MozOrientation", function(orientData){
var obj = {};
obj.x = orientData.x * 90;
obj.y = orientData.y * 90;
obj.z = orientData.z;
displayOrientation(obj);
}, true);
window.addEventListener("deviceorientation", function(orientData) {
var obj = {};
obj.x = orientData.gamma;
obj.y = orientData.beta;
obj.z = orientData.alpha;
displayOrientation(obj);
}, true);
It seems the current desktop Safari (5.0.3) doesn't support this event at all. The beta value in desktop Chrome 9 is exactly 180 less than it is in mobile Safari.
The x & y values in Firefox and mobile Safari should be approximately the same.
I hope that this codes clarify everything. Although the values this code returns can't be matched directly with the values of beta and gamma:
Mozilla returns the sin of the angle so, to get the angle...
X: Math.asin(eventInfo.x)*180/Math.PI
Y: Math.asin(eventInfo.y)*180/Math.PI
Z: Math.asin(eventInfo.z)*180/Math.PI
Webkit returns the acceleration in each axe. Then, to get the angle... (The sing changes is just to unify returned values)
X: Math.asin(clean(eventInfo.accelerationIncludingGravity.x/GRAVITY))*180/Math.PI
Y: Math.asin(clean(-eventInfo.accelerationIncludingGravity.y/GRAVITY))*180/Math.PI
Z: Math.asin(clean(-eventInfo.accelerationIncludingGravity.z/GRAVITY))*180/Math.PI
Being clean: (Because sometimes, the data returned, even without accelerating the phone, it's more than 9.8)
function clean(data)
{
if(data<-1.0)
{
return -1.0;
}
else
{if(data>1.0)
{
return 1.0;
}else
{
return data;
}}
}
The meaning of each axe is:
X-> inclination of the mobile to its right or left. Will be + if you tilt the mobile to its right (clockwise), keeping the buttons of volume up.
Y-> Tells if the mobile is up -if the lock/on-off button is up- (angle +) or down (angle -). It tells the inclination of the mobile relative to the floor. (angle of Y vector with plane X-Z)
Z-> Tells if the mobile screen is facing up (angle +) or upside down (angle -). It's the angle of the Z vector on the plane Y-X
Hope this is worthy for you!
Anyway, I'm working on some GWT classes to make working with this easier :) It's not difficult but I do not have so much time.
I'll tell you
You should be able to calculate one according to the other.
The problem I see is that Mozilla doesn't tell what means the data it returns.
I'm working now with three mobile devices: ipad, iphone4 and nokia n900.
When I work with iOS and safari and I get the accelerationIncludingGravity, I get values from -9.8 to 9.8. Those are values of the gravity that affect each axe. Then using trigonometry, I can calculate the angle in each axe.
If I get orientation in the iphone4, I can get directly the angle in each axe.
The problem comes with nokia n900, whose browser is based in Mozilla. That browser only returns data from -1 to 1. Documentation says that it's the tilt of each axe. In what unities? Cosine? Or just angle/180?
I guess that it's the cosine as when I put it in horizontal, it returns 0, and when I put it in vertical, it gets 1. And upside down, returns -1. Moreover, If you tilt it in aprox 60degrees, it returns 1/2.
As far I've seen, when using a Macbook laptop with Firefox, the data x,y and z is get as the cosine.
Mozilla Firefox now fully supports the W3C DeviceOrientation Events API. You should not use MozOrientation in your web applications any more.

Categories

Resources