Create a triangle around a point, perpendicular to a normal - javascript

I'd like get the points of a triangle around a point where the face would point in the direction of a specified normal. I'll be using THREE.js to add them to a BufferGeometry.
Very crude drawing:
Here's the code I have so far:
//The XYZ location of a point:
var x = model.points[i*3];
var y = model.points[i*3+1];
var z = model.points[i*3+2];
//The normal vector direction:
var nx = model.normals[i*3];
var ny = model.normals[i*3+1];
var nz = model.normals[i*3+2];
How can I pick 3 more points around this point that are all perpendicular to the normal and the same distance from the point / each other?
THANKS!

1) Take cross product of the normal with an arbitrary non-parallel vector. This will get you a vector perpendicular to the normal vector.
1.5) Normalize and scale the perpendicular vector to desired size. The length of this vector will be the distance from the triangle's centroid to each of its vertices.
2) Rotate the perpendicular vector by 2PI/3 and 4PI/3 around the normal vector.
3) Add the 3 vectors to the center point.

Note that there are infinitely many triangles that fit your criteria, even if we limit to only equilateral triangles. This is because there is an entire plane which is perpendicular to the given vector <nx, ny, nz> through the given point (x, y, z). Read here to see how to derive the equation for that plane. From there, you will need to pick a point on the plane. Then you can calculate the other two points by rotating around the given point at (x, y, z).

You need to find the plane parallel to the normal and containing the point (there is only one) and then pick any point in this plane with the specified distance and rotate it two times by 120 degree around the centeral point.

Related

3D model in HTML/CSS; Calculate Euler rotation of triangle

TLDR; Given a set of triangle vertices and a normal vector (all in unit space), how do I calculate X, Y, Z Euler rotation angles of the triangle in world space?
I am attemping to display a 3D model in HTML - with actual HTML tags and CSS transforms. I've already loaded an OBJ file into a Javascript class instance.
The model is triangulated. My first aim is just to display the triangles as planes (HTML elements are rectangular) - I'll be 'cutting out' the triangle shapes with CSS clip-path later on.
I am really struggling to understand and get the triangles of the model rotated correctly.
I thought a rotation matrix could help me out, but my only experience with those is where I already have the rotation vector and I need to convert and send that to WebGL. This time there is no WebGL (or tutorials) to make things easier.
The following excerpt shows the face creation/'rendering' of faces. I'm using the face normal as the rotation but I know this is wrong.
for (const face of _obj.faces) {
const vertices = face.vertices.map(_index => _obj.vertices[_index]);
const center = [
(vertices[0][0] + vertices[1][0] + vertices[2][0]) / 3,
(vertices[0][1] + vertices[1][1] + vertices[2][1]) / 3,
(vertices[0][2] + vertices[1][2] + vertices[2][2]) / 3
];
// Each vertex has a normal but I am just picking the first vertex' normal
// to use as the 'face normal'.
const normals = face.normals.map(_index => _obj.normals[_index]);
const normal = normals[0];
// HTML element creation code goes here; reference is 'element'.
// Set face position (unit space)
element.style.setProperty('--posX', center[0]);
element.style.setProperty('--posY', center[1]);
element.style.setProperty('--posZ', center[2]);
// Set face rotation, converting to degrees also.
const rotation = [
normal[0] * toDeg,
normal[1] * toDeg,
normal[2] * toDeg,
];
element.style.setProperty('--rotX', rotation[0]);
element.style.setProperty('--rotY', rotation[1]);
element.style.setProperty('--rotZ', rotation[2]);
}
The CSS first translates the face on X,Y,Z, then rotates it on X,Y,Z in that order.
I think I need to 'decompose' my triangles' rotation into separate axis rotations - i.e rotate on X, then on Y, then on Z to get the correct rotation as per the model face.
I realise that the normal vector gives me an orientation but not a rotation around itself - I need to calculate that. I think I have to determine a vector along one triangle side and cross it with the normal, but this is something I am not clear on.
I have spent hours looking at similar questions on SO but I'm not smart enough to understand or make them work for me.
Is it possible to describe what steps to take without Latex equations? I'm good with pseudo code but my Math skills are severely lacking.
The full code is here: https://whoshotdk.co.uk/cssfps/ (view HTML source)
The mesh building function is at line 422.
The OBJ file is here: https://whoshotdk.co.uk/cssfps/data/model/test.obj
The Blender file is here: https://whoshotdk.co.uk/cssfps/data/model/test.blend
The mesh is just a single plane at an angle, displayed in my example (wrongly) in pink.
The world is setup so that -X is left, -Y is up, -Z is into the screen.
Thank You!
If you have a plane and want to rotate it to be in the same direction as some normal, you need to figure out the angles between that plane's normal vector and the normal vector you want. The Euler angles between two 3D vectors can be complicated, but in this case the initial plane normal should always be the same, so I'll assume the plane normal starts pointing towards positive X to make the maths simpler.
You also probably want to rotate before you translate, so that everything is easier since you'll be rotating around the origin of the coordinate system.
By taking the general 3D rotation matrix (all three 3D rotation matrices multiplied together, you can find it on the Wikipedia page) and applying it to the vector (1,0,0) you can then get the equations for the three angles a, b, and c needed to rotate that initial vector to the vector (x,y,z). This results in:
x = cos(a)*cos(b)
y = sin(a)*cos(b)
z = -sin(b)
Then rearranging these equations to find a, b and c, which will be the three angles you need (the three values of the rotation array, respectively):
a = atan(y/x)
b = asin(-z)
c = 0
So in your code this would look like:
const rotation = [
Math.atan2(normal[1], normal[0]) * toDeg,
Math.asin(-normal[2]) * toDeg,
0
];
It may be that you need to use a different rotation matrix (if the order of the rotations is not what you expected) or a different starting vector (although you can just use this method and then do an extra 90 degree rotation if each plane actually starts in the positive Y direction, for example).

Cesium JS ellipsoid tangent plane calculation

Problem
I am a bit confused on how Cesium calculates geodetic and geocentric surface normal. Planes, generated from calculated normal do not actually tangent to ellipsoid surface in a given point, more then, the plane created from geodetic surface normal is just the same as one generated from geocentric surface normal.
Example & why do I need this
In this Sandcastle XYZ axis along with planes that should be tangent to ellipsoid are drawn when you click on the globe surface.
What I need is to get an XY plane tangent to Ellipsoid at given point and then project other points onto this plane. The problem is as I see the plane is not tangent to ellipsoid at all.
By the way - when I use built in method: Cesium.EllipsoidTangentPlane(); I also get strange result - plane is still not tangent to ellipsoid and also floating somewhere in space. Here is sandcastle.
Appreciate any help because I do not understand what happens here.
The normal of the plane is defined at the local coordinate system of the plane, of which x, y, and z-axis indicate the east, north, and up direction in the earth(world or ECEF ), respectively.
So if the normal vector of a plane is given in the ECEF, you have to convert it at the plane's local coordinate system like this.
// get the local coordinate system of the plane
var transform = Cesium.Transforms.eastNorthUpToFixedFrame(clickedPoint);
// get invert matrix
var inv = Cesium.Matrix4.inverseTransformation(transform, new Cesium.Matrix4());
// in this case actually world normal coincide with up direction(z axis of coordinate system)
// to avoid error we slightly extend it.
var extendedWordNormal = Cesium.Cartesian3.multiplyByScalar(clickedPoint, 1.001, new Cesium.Cartesian3());
// it will be nearly same as (0, 0, 1)
var localNormal = Cesium.Matrix4.multiplyByPoint(inv, extendedWordNormal, new Cesium.Cartesian3());
//var localNormal = new Cesium.Cartesian3(0, 0, 1);
Please check this sandcastle and this sample

Finding the coordinates (x,y,z) of vertices of a square plane rotated about its center point in P5 js

I am recreating conic sections in P5.js and need to find the equation of a square plane.
I know the size of the square plane, and the rotation in each axis in degrees from its center point (as dictated by the P5 sliders).
I want to calculate the coordinates (x, y, z) of the four vertices of this square plane, given known values for rotation.
This is my P5 sketch.
https://editor.p5js.org/inglog/sketches/HsMUb8UPA
I want to use these coordinates to create an equation for the plane, in the form ax+by+cz+d=0
Once I have the vertices of the square, I will use this calculator to get the equation of the plane: https://keisan.casio.com/exec/system/1223596129)
Any advice on how to calculate the coordinates of the vertices of the plane, given a known rotation about its center point?
Is this related to conversion between Cylindrical and Cartesian Coordinates? I also wonder if this answer is connected to the solution (Rotating vertices about point)
Thank you in advance for reading through.
Since there is a unique plane that goes through a given set of 3 (noncollinear) points, you don't need the vertices of the square in order to find an equation for the plane. You just need 3 random points on the plane.
Answer:
A = (0, slider.value(), 0)
B = (1, slider.value(), 0)
C = (0, slider.value() - sin(slider2.value()), cos(slider2.value()))
From here, you can get the plane equation as they describe on the site you gave:
AB = (1, 0, 0)
AC = (0, -sin, cos)
AB x AC = (0, -cos, -sin)
Equation:
0x - cos(slider2.value())*y - sin(slider2.value())*z + cos(slider2.value())*slider.value() = 0
simplifies to
cos(slider2.value())*(y - slider.value()) + sin(slider2.value())*z = 0
or
z = (slider.value() - y)/tan(slider2.value())
I'm about 99.9% sure this is the right equation. I used it to overlay points on the plane in your program, and it looked right.
How I got the three points:
We know that there will be one point at the center, which is on the y-axis. At this point, x=z=0 and y=slider.value(). So that is point A: (0, slider.value(), 0).
We also know that the plane intersects the xy-plane in the line defined by y=slider.value(). So point B can be any point on this line, let's arbitrarily pick (1, slider.value(), 0).
The third point is the hardest, since we can't have z=0, and we have to consider the angle. Starting at the center, let's walk one unit along the plane, keeping x=0 and moving in the positive z direction.
It's hard to convey that point over text, but this is a classic unit circle problem: x=0, and y, z are on a unit circle centered at point A: C = (0, slider.value() - sin(slider2.value()), cos(slider2.value())).

How to get 3D point coordinates given UV coordinates on a 3d plane object - Threejs

I'm trying to build some simple data visualisation and my weapon of choice is Three.js.I'have a series of PlaneGeometry meshes on which I apply a transparent texture dynamically created with a series of red square on it drawn at different opacity values.My plan is to use those points to create other meshes ( eg. CylinderGeometry ) and place them on top of the red square with an height value based on the red square opacity value.So far I could manage to find the UV values for each square and store it to an array, but I'm getting blocked at converting such red square UV coordinates to the 3D world coordinates system.I've found several resource describing the same concept applied to a sphere, and surprisingly it is pretty straight forward, but no other resources about applying the same concept to other mesh.
How can I get the 3D coordinates of those red square inside the texture?
EDIT: I think this is it:
function texturePosToPlaneWorld(planeOb, texcoord)
{
var pos = new THREE.Vector3();
pos.x = (texcoord.x - 0.5) * PLANESIZE;
pos.y = (texcoord.y - 0.5) * PLANESIZE;
pos.applyMatrix4(planeOb.matrix);
return pos;
}
Is used like this in the jsfiddle I made: http://jsfiddle.net/L0rdzbej/2/
var texcoord = new THREE.Vector2(0.8, 0.65);
var newpos = texturePosToPlaneWorld(planeOb, texcoord);
cubeOb.position.copy(newpos);
Planes are simple. The edge between vertices A, B -- vector A->B defines the direction for 'x' in your texture, and A->C similarily for the other direction in which the plane goes in the 3d space .. where you have texture's 'y' mapped on the plane.
Let's assume your pivot point is in the middle. So that's known in world space. Then as UV go from 0 to 1, e.g. UV coord (1.0, 0.5) would be in half way of the full width of the plane in the direction of the vector from of your Plane object pivot .. going from middle all the way to the edge. And then in the middle otherwise, where you have 0.5 in V (as in normalized texture y pixelcoord).
To know the coordinates of the vertices of the plane in world space, you just multiple them with the orientation of the object..
Given you know the size of your plane, you actually don't need to look at the vertices as the orientation of the plane is already in the object matrix. So you just need to adapt your UV coord to the pivot in middle (-0.5) and multiply with the plane size to get the point in plane space. Then the matrix multiplication converts that to world space.

3D normal/look-at vector from Euler angles

I'm working on a JavaScript/Canvas 3D FPS-like engine and desperately need a normal vector (or look-at vector if you will) for near and far plane clipping. I have the x and y axis rotation angles and am able to do it easily with only one of them at the time, but I just can't figure out how to get both of them...
The idea is to use this vector it to calculate a point in front of the camera, the near and far clipping planes must also be definable by constants so the vector has to be normalized, I hoped that with only the angles it would be possible to get this vector length to 1 without normalizing, but that's not the problem.
I don't have any roll (rotation around z axis) so it's that much easier.
My math looks like this:
zNear = 200; // near plane at an arbitrary 200 "points" away from camera position
// normal calculated with only y rotation angle (vertical axis)
normal = {
x: Math.sin(rotation.y),
y: 0,
z: Math.cos(rotation.y)};
Then clip a point in 3D space by testing the vector from the plane to it by means of a dot product.
nearPlane = {
x: position.x+normal.x*zNear,
y: position.y+normal.y*zNear,
z: position.z+normal.z*zNear};
// test a point at x, y, z against the near clipping plane
if(
(nearPlane.x-x)*normal.x+
(nearPlane.y-y)*normal.y+
(nearPlane.z-z)*normal.z < 0
)
{
return;
}
// then project the 3D point to screen
When a point is behind the player its projection coordinates are reversed (x=-x, y=-y) so nothing makes sense any more, that's why I'm trying to remove them.
I want that green arrow there, but in 3D.
After some intensive brain processing I figured out that
My original look-at vector was (0, 0, 1)
The z-rotation angle (roll) was always 0
There was no reason for the rotation matrix found on Wikipedia not to work
By applying the full rotation matrix on the (0, 0, 1) vector and taking in account that rz = 0 the solution I got was:
normal = {
x: Math.cos(camera.rotation.x)*Math.sin(camera.rotation.y),
y: -Math.sin(camera.rotation.x),
z: Math.cos(camera.rotation.y)*Math.cos(camera.rotation.x)};
And now everything works perfectly. The error was using only the x and y rotation matrices without taking in account rz = 0 for all angles which changed the matrix a little.

Categories

Resources