In three.js how to retrieve the position of an object? - three.js

I have created an object, a cone, and have located it, orientated it, and then translated it along its axis z. Well, now I want to retrieve its position in x, y and z and store the information in an array for later use.
var cone = new THREE.Mesh(coneGeometry, coneMaterial);
var φ = wup[iter].Latitude*Math.PI/180;
var λ = - wup[iter].Longitude*Math.PI/180;
//φ phi lat λ lambda lon
cone.position.set (
Math.cos(λ) * 90 * Math.cos(φ),
Math.sin(φ) * 90,
Math.sin(λ) * 90 * Math.cos(φ)
);
cone.lookAt( new THREE.Vector3(0,0,0) );
cone.translateZ( - earthRadius * réduc);
wup[iter].x = cone.x;
wup[iter].y = cone.y;
wup[iter].z = cone.z; /
The problem is that cone.x does not contain anything.

Based on philipp's indication, and on the reply to a neighboring question of mine, here is the code that worked:
wup[iter].x = cone.position.x;
wup[iter].y = cone.position.y;
wup[iter].z = cone.position.z;
cone, as a geometry, has a vector called position associated to it with coordinates. Consequently, retrieving the coordinates of the geometry implies calling the coordinates of its vector position.

Related

ThreeJS : How to get the angle between2 object

I have an Avatar at a given Position (v0) and Rotation (r0)
I have an Object at a givent Position (v1)
I looking for the angle to rotate the avatar toward v1. I need the angle I don't want to use lookAt() function
// Get the Avatar Position
let v0 = new THREE.Vector3();
avatar.getWorldPosition(v0)
// Get the Object Position
let v1 = new THREE.Vector3();
obj.getWorldPosition(v0)
// Get the direction v0 to v1
let dir0 = new THREE.Vector3();
dir0.subVectors( v0, v1 ).normalize();
// Get the direction of avatar (where it look at)
let dir2 = new THREE.Vector3();
avatar.getWorldDirection(dir2)
// Get the angle between the 2 direction
let radians = dir0.angleTo(dir2)
It doesn't work !
The this.mesh.lookAt(v1.setY(0)) works and rotate correctly the mesh
But the angle computation didn't work because of avatar.getWorldDirection
BTW, since everything is on the same plane I don't need 3D (only 2D)
BTW, The avatar (Mixamo) seems to face backward
I need that angle to trigger some animation (if angle > 90 then trigger 90, if angle > 180 then turnback animation...)
Your code has a few mistakes. obj.getWorldPosition(v0) is going to overwrite the v0 values retrieved with avatar.getWorldPosition(v0), so when you subtract v0 - v1 you'll get a vector with a magnitude of 0.
Looking at the documentation of Vector3.angleTo(), it says all you need to do is input the positions, without any subtraction necessary:
let posAvatar = new THREE.Vector3();
avatar.getWorldPosition(posAvatar);
let posObj = new THREE.Vector3();
obj.getWorldPosition(posObj);
const angleRadians = posAvatar.angleTo(posObj);
// convert from radians to degrees
const angleDeg = THREE.MathUtils.radToDeg(angleRadians);
Keep in mind, both objects will need to be on the same plane for this 2D angle to be accurate.
Update:
This approach uses the Javascript Math.atan2() method to calculate the absolute y-axis rotation from your avatar's vantage point towards the obj. This also only uses the x,z positions so any height variations are ignored.
let posAvatar = new THREE.Vector3();
avatar.getWorldPosition(posAvatar);
let posObj = new THREE.Vector3();
obj.getWorldPosition(posObj);
const xDist = posObj.x - posAvatar.x;
const zDist = posObj.z - posAvatar.z;
const angle = Math.atan2(zDist, xDist) * 180 / Math.PI;
avatar.rotation.y = angle;
Your avatar might be facing a different axis, so you can just add 90 or 180 degrees to the final angle value.

Formula of n-vector from latitude and longitude with three.js objects

I want the formula giving the n-vector (perpendicular to the globe) from latitude and longitude in a three.js scene.
My code is currently the following, based on a list of cities (in the table called "wup"):
var conegeometry = new THREE.CylinderGeometry(0, 10, 10, 500, false);
var earthRadius = 6371; // kilometres
var réduc = 0.02;
for (var iter=1;iter<1693;iter++){
var cone = new THREE.Mesh(conegeometry, conematerial);
var lat = wup[iter].Latitude*Math.PI/180;
var lon = wup[iter].Longitude*Math.PI/180;
cone.position.set (
earthRadius * réduc * Math.cos(lat) * Math.cos(lon),
earthRadius * réduc * Math.cos(lat) * Math.sin(lon),
earthRadius * réduc * Math.sin(lat));
cone.rotation.set (
Math.sin(lat),
Math.cos(lat) * Math.sin(lon),
-Math.cos(lat) * Math.cos(lon));
scene.add(cone);
}
Formulae come from this article, p 402.
My aim is to have all cities represented as cones with the top at lat/lon position and the rest below the surface of earth, and perpendicularly to the earth surface (simplified as a sphere).
Latitude and longitude come from UN cities dataset, seem very clean and classical in degrees.
Santiago Del Estero -27,79511 -64,26149
Yerevan 40,181998 44,514619
Adelaide -34,92866 138,59863
But something is wrong in the rotation formulae, and I can't find the right adjustment. Any idea?
You need to fix two things: calculation of position and calculation of rotation.
Position:
The geographical coordinate system is left-handed (longitude grows towards east). Three.js uses a right-handed coordinate system (longitude grows towards west). Therefore the longitude value needs to be inverted in your geometry. Also, the formulae you found work for coordinates with Z-axis pointing up. Three.js has the Y-axis pointing up. So you need to swap the z and y formulas. This is the correct code for position:
var lat = wup[iter].Latitude*Math.PI/180;
var lon = - wup[iter].Longitude*Math.PI/180;
cone.position.set (
earthRadius * réduc * Math.cos(lat) * Math.cos(lon),
earthRadius * réduc * Math.sin(lat),
earthRadius * réduc * Math.cos(lat) * Math.sin(lon));
Rotation:
You just need to rotate the cone by their longitude/latitude values (not sines and cosines)
cone.rotation.set ( 0, -lon, - Math.PI / 2 + lat);
I found a solution, much simpler than my initial idea. Based on this project and this example at using the instruction lookAt which avoids complicate trigonometric formulae.
I also found that my formulae for lat/lon to x, y, z where wrong, and not corresponding to the formulae used in the project shp.js which I also use.
Objects can be set at facing a given point, here the centre of the earth. But then cones are cylinders and must for a reason I do not fully understand be turned of PI/2. So this worked:
var lat = wup[iter].Latitude*Math.PI/180;
var lon = - wup[iter].Longitude*Math.PI/180;
cone.position.set (
Math.cos(lon) * 90 * Math.cos(lat),
Math.sin(lat) * 90,
Math.sin(lon) * 90 * Math.cos(lat)
);
cone.lookAt( new THREE.Vector3(0,0,0) ); //orientate the cone to the center of earth
cone.translateZ( - earthRadius * réduc); //to follow the genral contraction
cone.translateZ( coneHeight/2); //to put the top edge at lat/lon position
cone.rotateX(- Math.PI / 2 ); //because cylinders.cones are drawn horizontally

Points inside circular sector

I am working on a mobile application. My application gets the user position and orientation and then show some points of interest near his location.
Now, what I need is to know if the user is in front of a point of interest. My idea is to do this based on his position and orientation, using a circular sector of a small radius (about 5 meters) and I want to know if there is any formula or if anyone has a recommendation on how to achieve this.
I assume you have Lat/Lon coordinates and user moving/viewing azimuth.
You can calculate direction to object and it's bearing (azimuth) and compare with your limits.
Note: It is difficult to ensure 5 meters precision with GPS/geolocation
This excellent page contains all the needed formulas. Excerpt:
distance
JavaScript:
var R = 6371000; // metres
var φ1 = lat1.toRadians();
var φ2 = lat2.toRadians();
var Δφ = (lat2-lat1).toRadians();
var Δλ = (lon2-lon1).toRadians();
var a = Math.sin(Δφ/2) * Math.sin(Δφ/2) +
Math.cos(φ1) * Math.cos(φ2) *
Math.sin(Δλ/2) * Math.sin(Δλ/2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
var d = R * c;
bearing
JavaScript:
(all angles in radians)
var y = Math.sin(λ2-λ1) * Math.cos(φ2);
var x = Math.cos(φ1)*Math.sin(φ2) -
Math.sin(φ1)*Math.cos(φ2)*Math.cos(λ2-λ1);
var brng = Math.atan2(y, x).toDegrees();

Rotate scene about Up vector in jsc3d

I'm using jsc3d to load and display some 3d objects on a canvas. The viewer has already a built-in feature that allows to rotate the "view coordinates" (correct me if i'm wrong) about the Y axis by dragging the mouse.
The rotation is performed through a classic rotation matrix, and finally the trasformation matrix is multiplied by this rotation matrix.
The totation about the Y axis is calculated in a way that resembles a circular movement around the whole scene of loaded objects:
JSC3D.Matrix3x4.prototype.rotateAboutYAxis = function(angle) {
if(angle != 0) {
angle *= Math.PI / 180;
var c = Math.cos(angle);
var s = Math.sin(angle);
var m00 = c * this.m00 + s * this.m20;
var m01 = c * this.m01 + s * this.m21;
var m02 = c * this.m02 + s * this.m22;
var m03 = c * this.m03 + s * this.m23;
var m20 = c * this.m20 - s * this.m00;
var m21 = c * this.m21 - s * this.m01;
var m22 = c * this.m22 - s * this.m02;
var m23 = c * this.m23 - s * this.m03;
this.m00 = m00; this.m01 = m01; this.m02 = m02; this.m03 = m03;
this.m20 = m20; this.m21 = m21; this.m22 = m22; this.m23 = m23;
}
};
Now, dragging the mouse will apply this rotation about the Y axis on the whole world, like on the left side in the picture below. Is there a way, to apply a rotation about the Up vector to keep it in the initial position, like it appear on the right side?
I tried something like that:
var rotY = (x - viewer.mouseX) * 360 / viewer.canvas.height;
var rotMat = new JSC3D.Matrix3x4; // identity
rotMat.rotateAboutYAxis(rotY);
viewer.rotMatrix.multiply(rotMat);
but it has no effect.
What operations shall be applied to my rotation matrix to achieve a rotation about the Up vector?
Sample: https://jsfiddle.net/4xzjnnar/1/
This 3D library has already some built-in functions to allow scene rotation about X,Y,and Z axis, so there is no need to implement new matrix operations for that, we can use the existing functions rotateAboutXAyis, rotateAboutYAxis and rotateAboutZAxis, which apply an in-place matrix multiplication of the desired rotation angle in degrees.
The scene in JSC3D is transformed by a 3x4 matrix where the rotation is stored in the first 3 values of each row.
After applying a scene rotation and/or translation, applying a subsequent rotation about the Up vector, is a problem of calculate a rotation about an arbitrary axis.
A very clean and didactic explanation how to solve this problem is described here: http://ami.ektf.hu/uploads/papers/finalpdf/AMI_40_from175to186.pdf
Translate the P 0 (x 0 ,y 0 ,z 0 ) axis point to the origin of the coordinate system.
Perform appropriate rotations to make the axis of rotation coincident with
z-coordinate axis.
Rotate about the z-axis by the angle θ.
Perform the inverse of the combined rotation transformation.
Perform the inverse of the translation.
Now, its easy to write a function for that, because we use the functions already available in JSC3D (translation part is omitted here).
JSC3D.Viewer.prototype.rotateAboutUpVector = function(angle) {
angle %= 360;
/* pitch, counter-clockwise rotation about the Y axis */
var degX = this.rpy[0], degZ = this.rpy[2];
this.rotMatrix.rotateAboutXAxis(-degX);
this.rotMatrix.rotateAboutZAxis(-degZ);
this.rotMatrix.rotateAboutYAxis(angle);
this.rotMatrix.rotateAboutZAxis(degZ);
this.rotMatrix.rotateAboutXAxis(degX);
}
Because all above mentioned functions are using degrees, we need to get back the actual Euler angles from the rotation matrix (simplified):
JSC3D.Viewer.prototype.calcRollPitchYaw = function() {
var m = this.rotMatrix;
var radians = 180 / Math.PI;
var angleX = Math.atan2(-m.m12, m.m22) * radians;
var angleY = Math.asin(m.m01) * radians;
var angleZ = Math.atan2(-m.m01, m.m00) * radians;
this.rpy[0] = angleX;
this.rpy[1] = angleY;
this.rpy[2] = angleZ;
}
The tricky part here, is that we need always to get back the current rotation angles, as they results from the applied rotations, so a separate function must be used to store the current Euler angles every time that a rotation is applied to the scene.
For that, we can use a very simple structure:
JSC3D.Viewer.prototype.rpy = [0, 0, 0];
This will be the final result:

famo.us quaternion rotation around z axis

As far as I know a quaternion is a set of four values (W X Y Z) that are used to specify a rotation in 3D space. For a given axis (x y z) and angle (α), the quaternion representing a rotation around the axis from the origin (0,0,0) to (x,y,z). So a rotation of 90 degrees about the z axis (0 0 1) should be:
var quaternion = new Quaternion(Math.PI/2, 0, 0, 1);
but famo.us turns it for ~60 degrees...
I've also tried var quaternion = new Quaternion(90, 0, 0, 1); but in this case famo.us turns it for ~5 degrees
is it a bug of the framework?
How should I use it to turn it on 90 degreez around z axis?
Documentation is still totally useless..
Try using this method Quaternion.makeFromAngleAndAxis(angle, v)
I have found this to be the most straight forward approach to making it a little more readable and useable.
Example jsBin
Where
var degrees = 90;
var angle = Math.PI/180 * degrees;
var v = new Vector(0, 0, 1);
var quaternion = new Quaternion();
quaternion.makeFromAngleAndAxis(angle, v);
...To get the transform
quaternion.getTransform();
Something to remember from Math Class
A circle has 360 degrees. Each degree is represented by the unit circumference of a circle 2 * PI * r. We will assume we have a radius of 1. So divide your total circumference by 360 and you get one degrees 2PI/360 or PI/180.
In Summary:
one degrees of our circle is = Math.PI/180
your angle of direction is = Math.PI/180 * degrees
Just found answer in one wiki article:
var angle = Math.PI/2;
var axis = [0,0,1];
var w = Math.cos(.5 * angle);
var x = axis[0] * Math.sin(.5 * angle);
var y = axis[1] * Math.sin(.5 * angle);
var z = axis[2] * Math.sin(.5 * angle);
var quaternion = new Quaternion(w, x, y, z);
try this transformation - Transform.rotateZ(angle);
Refer to - http://famo.us/docs/reference/pages/0.3/transforms.html

Resources