I got a object in a WebGL context that is rotated by a quaternion. Now I would like to rotate it around the x axis according to the current mouse position. How can I do this? I'm using the glMatrix library.
glMatrix doesn't (currently) include much in the way of quaternion transforms. Sorry. Probably not a bad idea to add, however.
You can poke around online to find some references for these things. For example: this page has some good examples for basic quaternion math. In the meantime, if all your looking for is rotation around X I think this will do the trick: (Note! Untested code ahead! This is just a quick optimization of the algorithms on the page I linked)
/**
* Rotates a quaternion by the given angle around the X axis
*
* #param {quat4} quat quat4 to rotate
* #param {number} angle Angle (in radians) to rotate
* #param {quat4} [dest] quat4 receiving operation result. If not specified result is written to quat
*
* #returns {quat4} dest if specified, quat otherwise
*/
quat4.rotateX = function (quat, angle, dest) {
if (!dest) { dest = quat; }
// NOTE: I really have no idea what this is for, the guy on the linked page seemed to think it was necessary, though.
angle *= 0.5;
var qax = quat[0], qay = quat[1], qaz = quat[2], qaw = quat[3],
qbx = Math.sin(angle), qbw = Math.cos(angle);
dest[0] = qax * qbw + qaw * qbx;
dest[1] = qay * qbw + qaz * qbx;
dest[2] = qaz * qbw - qay * qbx;
dest[3] = qaw * qbw - qax * qbx;
};
In the meantime, feel free to add an issue to the gl-matrix github page to request any operations you think would be helpful.
This should do the trick:
quat4.rotateX = function (quat, angle, dest) {
if (!dest) dest = quat;
quat4.multiply(quat, [Math.sin(angle/2), 0, 0, Math.cos(angle/2)]);
}
which you can use with, e.g.,
quat4.rotateX(myQuaternian, Math.PI/4);
(Rotation around Y and Z axes can be done by moving that sin term to the 2nd or 3rd slot.)
Related
Using this question: 3D point rotation algorithm I was able to assimilate a basic rotate function for my arbitrary vector library in javascript.
The issue is, the question above doesn't explain how to rotate around any point (Like an orbit)
This vector library extends the Array object type (I originally forked it from an old project, and then adapted it for my own projects), so think of each vector like an array with extra methods and getters (You can find the whole source code here)
This is the method I use to rotate the current vector with a Vector plugged in as the angle (So each axis can be controlled separately)
It grabs the cosine and sine values required for all three axi (pitch, roll, yaw) and then stores them as three vectors in an array.
Finally it multiplies each vector into each coordinate accordingly.
rotate(angle) {
let cos = (angle.copy).map(val => Math.cos(angle));
let sin = (angle.copy).map(val => Math.sin(angle));
let other = [new Vector([
cos.z * cos.y,
cos.z * sin.y * sin.x - sin.z * cos.x,
cos.z * sin.y * cos.x + sin.z * sin.x
]), new Vector([
sin.z * cos.y,
sin.z * sin.y * sin.x + cos.z * cos.x,
sin.z * sin.y * cos.x - cos.z * sin.x
]), new Vector([
-sin.y,
cos.y * sin.x,
cos.y * cos.x
])];
let stored = point.copy;
this.map((val, i) => (other[i].mul(stored)).sum);
}
My main question is, how can I adapt this tool to allow me to rotate around any point in 3D space
If you got a rotation matrix R and want to rotate a vector v around a point a, then the formula for the result is
a + R * (v-a)
This applies the same if the matrix-vector multiplication is from the left.
I wish to create the compass / arrow exactly like the one we see in AroundMe Mobile App that point exactly to a pin to the map accordingly with my mobile position and update the arrow when I move the phone.
I'm getting crazy to understand exactly how to do that and I can not find any guide or tutorial that explain a bit it.
What I found online is a bearing function and I created a directive around it:
app.directive('arrow', function () {
function bearing(lat1, lng1, lat2, lng2) {
var dLon = (lng2 - lng1);
var y = Math.sin(dLon) * Math.cos(lat2);
var x = Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(dLon);
var rad = Math.atan2(y, x);
var brng = toDeg(rad);
return (brng + 360) % 360;
}
function toRad(deg) {
return deg * Math.PI / 180;
}
function toDeg(rad) {
return rad * 180 / Math.PI;
}
return {
restrict: 'E',
link: function (scope, element, attrs) {
var arrowAngle = bearing(scope.user.position.lat, scope.user.position.lng, attrs.lat, attrs.lng);
element.parent().css('transform', 'rotate(' + arrowAngle + 'deg)');
}
};
});
It seems to update the arrow in a direction but unfortunately it is not the right direction because it is not calculated using also the mobile magneticHeading position.
So I added the ngCordova plugin for Device Orientation to get the magneticHeading and now I don't know exactly how to use it and where in the bearing function.
$cordovaDeviceOrientation.getCurrentHeading().then(function(result) {
var magneticHeading = result.magneticHeading;
var arrowAngle = bearing(scope.user.position.lat, scope.user.position.lng, attrs.lat, attrs.lng, magneticHeading);
element.parent().css('transform', 'rotate(' + arrowAngle + 'deg)');
});
I tried to add it in the return statement:
return (brng - heading) % 360;
or:
return (heading - ((brng + 360) % 360));
Implementing this code with a watcher I see the arrow moving but not in the exact position... For example from my position and the pin the arrow should point to N and it is pointing to E.
Always looking online I can not find any tutorial / question to find the bearing between a lat/lng point and a magnetingHeading.
Maybe I'm close to the solution but I can not go ahead alone.
I also tried to search for a mathematical formulas but even there is a huge pain to understand and implement it.
I hope you can help.
It's hard to give a plain answer to this question because a lot depends on the actual graphical representation. For instance, in what direction do you point where rotate(0deg).
I can explain the formula you've found, which might help you to clear the issue yourself. The hard part is the following:
var dLon = (lng2 - lng1);
var y = Math.sin(dLon) * Math.cos(lat2);
var x = Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(dLon);
var rad = Math.atan2(y, x);
What you see here is Haversines formula (https://en.wikipedia.org/wiki/Haversine_formula). Normally one could suffice with two sets of coordinates and calculate the angle between them. When working with latitude and longitude this will not work because the earth is not a flat surface. The first three lines are Haversine and the result (x and y) are coordinates on the unit circle (https://en.wikipedia.org/wiki/Unit_circle)
The next step is to calculate the angle from the point on this unit circle to the center. We can use the arctangant for this. Javascript has a helper function atan2 which simplifies this process. The result is simple the angle of your point towards the center of the circle. In other words, your position towards your point of interest. The result is in radians and needs to be converted to degrees. (https://en.wikipedia.org/wiki/Atan2)
A simplified version with a flat coordinate system would look like this:
var deltaX = poi.x - you.x;
var deltaY = you.y - poi.y;
var rotation = toDeg(Math.atan2(deltaX, deltaY));
bearingElement.css('transform', 'rotate(' + rotation + 'deg)');
Where poi is the Point of Interest and you is your position.
To compensate for your own rotation, you need to substract your own rotation. In the above sample the result would become:
var deltaX = poi.x - you.x;
var deltaY = you.y - poi.y;
var rotation = toDeg(Math.atan2(deltaX, deltaY));
rotation -= you.rotation;
bearingElement.css('transform', 'rotate(' + rotation + 'deg)');
I've made a Plunckr in which you can see a simple flat coordinate system. You can move and rotate you and the point of interest. The arrow inside 'you' will always point towards the poi, even if you rotate 'you'. This is because we compensate for our own rotation.
https://plnkr.co/edit/OJBGWsdcWp3nAkPk4lpC?p=preview
Note in the Plunckr that the 'zero'-position is always to the north. Check your app to see your 'zero'-position. Adjust it accordingly and your script will work.
Hope this helps :-)
I have 100 items entering the viewport in random order. Together, they need to form a circle inside a DOM container. I need some way to calculate the position the items need to move to...
The structure is kinda like this:
http://codepen.io/anon/pen/WvbKjb (visual sample with a bit of css and js inside)
<div id="circle"><!-- 100 items in here --></div</div>
And then the JS, for this sample, would generate 100 divs and set their position with css:
for (i = 0; i <= 100; i++) {
var item = document.createElement('div');
item.id = 'item'+i;
item.className = 'item';
item.setAttribute('style', 'left:0px;top:0px');
document.getElementById('circle').appendChild(item);
}
So I would generate 100 .item elements and move them around the screen. This movement is not an issue: what I don't know how to do is find the position each item has to end up at to properly fill the circle. Is there any way to easily calculate this with a formula? I'm afraid it's way beyond my math skills...
Thanks in advance for any help.
EDIT: using jQuery would be fine too.
You may start with this, just a bit of a math:
Example
the main part is:
var spacing = 360 / count;
var l = Math.cos(rotation * Math.PI / 180) * radius - itemRadius;
var t = Math.sin(rotation * Math.PI / 180) * radius - itemRadius;
rotation += spacing;
Where spacing is actually an angle
Probably a little overloaded but this is what i tried and what worked for me: https://jsfiddle.net/tx7po9eg/1/
Main Part is this function which will calculate the position of a specific element depending on the defined center and the radius.
function getPos(cent, rad, amount, iteration) {
var degree = 360 / amount * iteration;
var changeY = cent.y - (Math.sin(degree * Math.PI / 180) * rad);
var changeX = cent.x - (Math.cos(degree * Math.PI / 180) * rad);
var ret = {};
ret.x = Math.round(changeX * 100) / 100;
ret.y = Math.round(changeY * 100) / 100;
return ret;
}
here an example including visualization:
https://jsfiddle.net/tx7po9eg/3/
You will have to decide exactly HOW you want to fit all the elements inside the circle. Do you want them in a single line around the parameter? At Random? Fill the circle?
If you want to fill the circle, one thing you could do is work like that - move from the center outside, starting by putting an item in the middle, then going out and putting another circle around it, then another around it, and like that keep wrapping it in layers until you hit the end of the circle. The only thing to figure out here would be the spacing (e.g. how many circles in each layer), and that would depend on the size of each element (are the sizes fixed)?
If you can make the container circle as large as you desire, you can just start making layers until you run out of items, and then place the big circle around them.
Hope this helps...
this is my first question after having relied on this site for years!
Anyway, I'd like to accomplish something similar to this effect:
http://www.flashmonkey.co.uk/html5/wave-physics/
But on a circular path, instead of a horizon. Essentially, a floating circle/blob in the center of the screen that would react to mouse interaction. What I'm not looking for is gravity, or for the circle to bounce around the screen - only surface ripples.
If at all possible I'd like to apply a static texture to the shape, is this a possibility? I'm completely new to Canvas!
I've already tried replacing some code from the above example with circular code from the following link, to very limited success:
http://www.html5canvastutorials.com/tutorials/html5-canvas-circles/
If only it were that easy :)
Any ideas?
Thanks in advance!
I tried to figure out how wave simulation works using View Source and JavaScript console. It's working fine but threw some JS errors. Also, it seems physics update is entangled with rendering in the render() method.
Here is what I found about the code:
The mouseMove() method creates disturbances on the wave based on mouse position, creating a peak around the mouse. The target variable is the index of the particle that needs to be updated, it's calculated from mouse pos.
if (particle && mouseY > particle.y) {
var speed = mouseY - storeY;
particles[target - 2].vy = speed / 6;
particles[target - 1].vy = speed / 5;
particles[target].vy = speed / 3;
particles[target + 1].vy = speed / 5;
particles[target + 2].vy = speed / 6;
storeY = mouseY;
}
Then, the particles around target are updated. The problem I found is that it does no bounds checking, i.e. it can potentially particles[-1] when target == 0. If that happens, an exception is thrown, the method call ends, but the code does not stop.
The render() method first updates the particle positions, then renders the wave.
Here is its physics code:
for (var u = particles.length - 1; u >= 0; --u) {
var fExtensionY = 0;
var fForceY = 0;
if (u > 0) {
fExtensionY = particles[u - 1].y - particles[u].y - springs[u - 1].iLengthY;
fForceY += -fK * fExtensionY;
}
if (u < particles.length - 1) {
fExtensionY = particles[u].y - particles[u + 1].y - springs[u].iLengthY;
fForceY += fK * fExtensionY;
}
fExtensionY = particles[u].y - particles[u].origY;
fForceY += fK / 15 * fExtensionY;
particles[u].ay = -fForceY / particles[u].mass;
particles[u].vy += particles[u].ay;
particles[u].ypos += particles[u].vy;
particles[u].vy /= 1.04;
}
Basically, it's Hooke's Law for a chain of particles linked by springs between them. For each particle u, it adds the attraction to the previous and next particles (the if statements check if they are available), to the variable fForceY. I don't fully understand the purpose of the springs array.
In the last four lines, it calculates the acceleration (force / mass), updates the velocity (add acceleration), then position (add velocity), and finally, reduce velocity by 1.04 (friction).
After the physics update, the code renders the wave:
context.clearRect(0, 0, stageWidth, stageHeight);
context.fillStyle = color;
context.beginPath();
for (u = 0; u < particles.length; u++) {
...
}
...
context.closePath();
context.fill();
I'm not explaining that, you need to read a canvas tutorial to understand it.
Here are some ideas to get started, note that I didn't test these code.
To modify the code to draw a circular wave, we need introduce a polar coordinate system, where the particle's x-position is the angle in the circle and y-position the distance from center. We should use theta and r here but it requires a large amount of refactoring. We will talk about transforming later.
mouseMove(): Compute particle index from mouse position on screen to polar coordinates, and make sure the disturbance wrap around:
Define the function (outside mouseMove(), we need this again later)
function wrapAround(i, a) { return (i + a.length) % a.length; }
Then change
particles[target - 2] --> particles[wrapAround(target - 2, particles)]
particles[target - 1] --> particles[wrapAround(target - 1, particles)]
...
The modulo operator does the job but I added particles.length so I don't modulo a negative number.
render(): Make sure the force calculation wrap around, so we need to wrapAround function again. We can strip away the two if statements:
fExtensionY = particles[wrapAround(u - 1, particles)].y - particles[u].y - springs[wrapAround(u - 1, springs)].iLengthY;
fForceY += -fK * fExtensionY;
fExtensionY = particles[u].y - particles[wrapAround(u + 1, particles)].y - springs[warpAround(u, springs)].iLengthY;
fForceY += fK * fExtensionY;
Here is the result so far in jsfiddle: Notice the wave propagate from the other side. http://jsfiddle.net/DM68M/
After that's done, the hardest part is rendering them on a circle. To do that, we need coordinate transform functions that treat particle's (x, y) as (angle in the circle, distance from center), and we also need inverse transforms for mouse interaction in mouseMove().
function particleCoordsToScreenCoords(particleX, particleY) {
return [ radiusFactor * particleY * Math.cos(particleX / angleFactor),
radiusFactor * particleY * Math.sin(particleX / angleFactor) ];
}
function screenCoordsToParticleCoords(screenX, screenY) {
// something involving Math.atan2 and Math.sqrt
}
Where the ...Factor variables needed to be determined separately. The angleFactor is two pi over the highest x-position found among particles array
Then, in the coordinates supplied to the context.lineTo, context.arc, use the particleCoordsToScreenCoords to transform the coordinates.
As part of a word cloud rendering algorithm (inspired by this question), I created a Javascript / Processing.js function that moves a rectangle of a word along an ever increasing spiral, until there is no collision anymore with previously placed words. It works, yet I'm uncomfortable with the code quality.
So my question is: How can I restructure this code to be:
readable + understandable
fast (not doing useless calculations)
elegant (using few lines of code)
I would also appreciate any hints to best practices for programming with a lot of calculations.
Rectangle moveWordRect(wordRect){
// Perform a spiral movement from center
// using the archimedean spiral and polar coordinates
// equation: r = a + b * phi
// Calculate mid of rect
var midX = wordRect.x1 + (wordRect.x2 - wordRect.x1)/2.0;
var midY = wordRect.y1 + (wordRect.y2 - wordRect.y1)/2.0;
// Calculate radius from center
var r = sqrt(sq(midX - width/2.0) + sq(midY - height/2.0));
// Set a fixed spiral width: Distance between successive turns
var b = 15;
// Determine current angle on spiral
var phi = r / b * 2.0 * PI;
// Increase that angle and calculate new radius
phi += 0.2;
r = (b * phi) / (2.0 * PI);
// Convert back to cartesian coordinates
var newMidX = r * cos(phi);
var newMidY = r * sin(phi);
// Shift back respective to mid
newMidX += width/2;
newMidY += height/2;
// Calculate movement
var moveX = newMidX - midX;
var moveY = newMidY - midY;
// Apply movement
wordRect.x1 += moveX;
wordRect.x2 += moveX;
wordRect.y1 += moveY;
wordRect.y2 += moveY;
return wordRect;
}
The quality of the underlying geometric algorithm is outside my area of expertise. However, on the quality of the code, I would say you could extract a lot of functions from it. Many of the lines that you have commented could be turned into separate functions, for example:
Calculate Midpoint of Rectangle
Calculate Radius
Determine Current Angle
Convert Polar to Cartesian Coodinates
You could consider using more descriptive variable names too. 'b' and 'r' require looking back up the code to see what they are for, but 'spiralWidth' and 'radius' do not.
In addition to Stephen's answer,
simplify these two lines:
var midX = wordRect.x1 + (wordRect.x2 - wordRect.x1)/2.0;
var midY = wordRect.y1 + (wordRect.y2 - wordRect.y1)/2.0;
The better statements:
var midX = (wordRect.x1 + wordRect.x2)/2.0;
var midY = (wordRect.y1 + wordRect.y2)/2.0;