a-frame entity-positioning and rotation - javascript

Sadly I am not familiar with the positioning and rotation of entites in 3D space, so I want to create a function that positions an entity with easier to understand parameters like:
createEntity(vertical, horizontal, distance)
for
<a-entity position="-2 0 -2" rotation="-10 30 0"></a-entity>
where vertical and horizontal are float-values between 0 and 360 and distance is a float where 0 is position "0 0 0" and as higher the value than farther the entity goes.
the rotation should face the camera at init.
are there helper-function for the calculations?

It sounds like you want to use the Spherical coordinate system to position the elements, and the look-at component to rotate the objects towards the camera.
I'm not aware of any helpers, but it's quite easy to do this with a custom component, like this:
// Register the component
AFRAME.registerComponent('fromspherical', {
// we will use two angles and a radius provided by the user
schema: {
fi: {},
theta: {},
r: {},
},
init: function() {
// lets change it to radians
let fi = this.data.fi * Math.PI / 180
let theta = this.data.theta * Math.PI / 180
// The 'horizontal axis is x. The 'vertical' is y.
// The calculations below are straight from the wiki site.
let z = (-1) * Math.sin(theta) * Math.cos(fi) * this.data.r
let x = Math.sin(theta) * Math.sin(fi) * this.data.r
let y = Math.cos(theta) * this.data.r
// position the element using the provided data
this.el.setAttribute('position', {
x: x,
y: y,
z: z
})
// rotate the element towards the camera
this.el.setAttribute('look-at', '[camera]')
}
})
Check it out in this fiddle.
The calculations are in a different order than on the wiki website. This is because in aframe the XYZ space looks like this:
The camera is looking along the negative Z axis upon default initialization.

Related

How to detect when rotated rectangles are colliding each other

After saw this question many times and replied with an old (an not usable) code I decide to redo everything and post about it.
Rectangles are defined by:
center : x and y for his position (remember that 0;0 is TOP Left, so Y go down)
size: x and y for his size
angle for his rotation (in deg, 0 deg is following axis OX and turn clockwise)
The goal is to know if 2 rectangles are colliding or not.
Will use Javascript in order to demo this (and also provide code) but I can be done on every language following the process.
Links
Final Demo on Codepen
GitHub repository
Concept
In order to achieve this we'll use corners projections on the other rectangle 2 axis (X and Y).
The 2 rectangles are only colliding when the 4 projections on one rectangles hit the others:
Rect Blue corners on Rect Orange X axis
Rect Blue corners on Rect Orange Y axis
Rect Orange corners on Rect Blue X axis
Rect Orange corners on Rect Blue Y axis
Process
1- Find the rects axis
Start by creating 2 vectors for axis 0;0 (center of rect) to X (OX) and Y (OY) then rotate both of them in order to get aligned to rectangles axis.
Wikipedia about rotate a 2D vector
const getAxis = (rect) => {
const OX = new Vector({x:1, y:0});
const OY = new Vector({x:0, y:1});
// Do not forget to transform degree to radian
const RX = OX.Rotate(rect.angle * Math.PI / 180);
const RY = OY.Rotate(rect.angle * Math.PI / 180);
return [
new Line({...rect.center, dx: RX.x, dy: RX.y}),
new Line({...rect.center, dx: RY.x, dy: RY.y}),
];
}
Where Vector is a simple x,y object
class Vector {
constructor({x=0,y=0}={}) {
this.x = x;
this.y = y;
}
Rotate(theta) {
return new Vector({
x: this.x * Math.cos(theta) - this.y * Math.sin(theta),
y: this.x * Math.sin(theta) + this.y * Math.cos(theta),
});
}
}
And Line represent a slop using 2 vectors:
origin: Vector for Start position
direction: Vector for unit direction
class Line {
constructor({x=0,y=0, dx=0, dy=0}) {
this.origin = new Vector({x,y});
this.direction = new Vector({x:dx,y:dy});
}
}
Step Result
2- Use Rect Axis to get corners
First want extend our axis (we are 1px unit size) in order to get the half of width (for X) and height (for Y) in order to be able by adding when (and inverse) to get all corners.
const getCorners = (rect) => {
const axis = getAxis(rect);
const RX = axis[0].direction.Multiply(rect.w/2);
const RY = axis[1].direction.Multiply(rect.h/2);
return [
rect.center.Add(RX).Add(RY),
rect.center.Add(RX).Add(RY.Multiply(-1)),
rect.center.Add(RX.Multiply(-1)).Add(RY.Multiply(-1)),
rect.center.Add(RX.Multiply(-1)).Add(RY),
]
}
Using this 2 news methods for Vector:
// Add(5)
// Add(Vector)
// Add({x, y})
Add(factor) {
const f = typeof factor === 'object'
? { x:0, y:0, ...factor}
: {x:factor, y:factor}
return new Vector({
x: this.x + f.x,
y: this.y + f.y,
})
}
// Multiply(5)
// Multiply(Vector)
// Multiply({x, y})
Multiply(factor) {
const f = typeof factor === 'object'
? { x:0, y:0, ...factor}
: {x:factor, y:factor}
return new Vector({
x: this.x * f.x,
y: this.y * f.y,
})
}
Step Result
3- Get corners projections
For every corners of a rectangle, get the projection coord on both axis of the other rectangle.
Simply by adding this function to Vector class:
Project(line) {
let dotvalue = line.direction.x * (this.x - line.origin.x)
+ line.direction.y * (this.y - line.origin.y);
return new Vector({
x: line.origin.x + line.direction.x * dotvalue,
y: line.origin.y + line.direction.y * dotvalue,
})
}
(Special thank to Mbo for the solution to get projection.)
Step Result
4- Select externals corners on projections
In order to sort (along the rect axis) all the projected point and take the min and max projected points we can:
Create a vector to represent: Rect Center to Projected corner
Get the distance using the Vector Magnitude function.
get magnitude() {
return Math.sqrt(this.x * this.x + this.y * this.y);
}
Use the dot product to know if the vector is facing the same direction of axis of inverse (where signed distance" is negative)
getSignedDistance = (rect, line, corner) => {
const projected = corner.Project(line);
const CP = projected.Minus(rect.center);
// Sign: Same directon of axis : true.
const sign = (CP.x * line.direction.x) + (CP.y * line.direction.y) > 0;
const signedDistance = CP.magnitude * (sign ? 1 : -1);
}
Then using a simple loop and test of min/max we can find the 2 externals corners. The segment between them is the projection of a Rect on the other one axis.
Step result
5- Final: Do all projections hit rect ?
Using simple 1D test along the axis we can know if they hit or not:
const isProjectionHit = (minSignedDistance < 0 && maxSignedDistance > 0
|| Math.abs(minSignedDistance) < rectHalfSize
|| Math.abs(maxSignedDistance) < rectHalfSize);
Done
Testing all 4 projections will give you the final result. =] !!
Hope this answer will help as many people as possible. Any comments are appreciated.

Translating camera with radius/diameter instead of x,y,z?

So I have this example as shown below and I was wondering if its possible to translate a camera by changing the radius & diameter instead of using x,y,z positions (Vector). For now im using a cube but I want to add a second camera basically.
Since I know where 0,0,0 (origin) is, is there any way to translate the cube by setting diameter radius or whatever is required and also lock it on the origin?
What I use to move the Cube (Three.js)
var posX,posY,posZ;
var scene, camera, render;
var cubeMesh,cube_Geometry, cube_Material;
class myWorld{
/* ... Setup World ... */
//excecute cube();
/* ... Set/Get positions (xyz) ... */
cube(){
cube_Geometry = new THREE.BoxGeometry(20, 20, 20);
cube_Material = new THREE.MeshNormalMaterial();
cube_Mesh = new THREE.Mesh(cube_Geometry, cube_Material);
cube_Mesh.position.set(0, 100, 100);
scene.add(cube_Mesh);
}
animate(){ //loop function
//THREE.Mesh.position.set (Three.js)
cube_Mesh.position.set(posX, posY, posZ);
}
}
What I want to achieve:
Use Spherical and setFromSpherical:
var r = 10;
var theta = 310 * (Math.PI / 180); /// 310 degree to radians
var sphericalPos = new THREE.Spherical(r, 0, theta);
cube_Mesh.position.setFromSpherical(sphericalPos);
// or just do cube_Mesh.position.setFromSphericalCoords(radius, phi, theta)
Spherical(radius: Float, phi: Float, theta : Float)
radius - the radius, or the Euclidean distance (straight-line distance) from the point to the origin. Default is 1.0.
phi - polar angle from the y (up) axis. Default is 0.
theta - equator angle around the y (up) axis. Default is 0.
The poles (phi) are at the positive and negative y axis. The equator (theta) starts at positive z.

Fabric.js: custom rotation point

I have found a workaround here (see code below), but I wonder if Fabric has built-in support to set the rotation point of an object to a specific place.
function rotateObject(fabObj, angleRadian, pivotX, pivotY) {
ty = pivotY - fabObj.height / 2.0;
tx = pivotX - fabObj.width / 2.0;
if (angleRadian >= Math.PI * 2) {
angleRadian -= Math.PI * 2;
}
angle2 = Math.atan2(ty, tx);
angle3 = (2 * angle2 + angleRadian - Math.PI) / 2.0;
pdist_sq = tx * tx + ty * ty;
disp = Math.sqrt(2 * pdist_sq * (1 - Math.cos(angleRadian)));
fabObj.set({transformMatrix:[
Math.cos(angleRadian),
Math.sin(angleRadian),
-Math.sin(angleRadian),
Math.cos(angleRadian),
disp * Math.cos(angle3),
disp * Math.sin(angle3)
]});
}
There is no built in method.
By default mouse rotation on fabricjs is around the center point.
If you deactivate centeredRotation on objects, setting it to false:
fabric.Object.prototype.centeredRotation = false
the object will start to rotate around the originX and originY position.
Then you can set for each object a specific originX and originY, that can be numeric and represent any point inside the object ( with values ranging from 0 to 1 ) i m not even sure if you can do it with points outside the object.
At that point the object rotates around that point, just setting the angle property to a desired angle.
Consider that now also position is relative to that origin.
As a side not, do not set transformMatrix of an object. is unsupported and will give you weird controls in case of interactivity with controls.
In your specific example, once found the matrix:
var matrix = [
Math.cos(angleRadian),
Math.sin(angleRadian),
-Math.sin(angleRadian),
Math.cos(angleRadian),
disp * Math.cos(angle3),
disp * Math.sin(angle3)
];
var options = fabric.util.qrDecompose(matrix);
object.set(options);
object.setPositionByOrigin({ x: options.translateX, y: options.translateY }, 'center', 'center');
this should give you same effect but being supported by fabricjs better.
Rotation on fabricjs is around the center point.
This fuction is on developmenting...
but I have implemented to line rotation.
Redrawing line from custom center point to mouse position.
it works well for me.

Calculate angle change after hitting a tilted wall

I'm making a game in javascript, where an object is supposed to bounce from walls. I really tried to get it to work myself, but it never works correctly.
Let's say theres a ball bouncing inside this cage (blue = 30°, brown = 60°);
The ball's coordinates are known. The angle of movement is known. The point of collision (P) coordinates are known. The angle of the wall is known. The ball's position is updating it's coordinates inside a setInterval function using this function:
function findNewPoint(x, y, angle, distance) {
var result = {};
result.x =(Math.cos(angle * Math.PI / 180) * distance + x);
result.y = (Math.sin(angle * Math.PI / 180) * distance + y);
return result;
So, upon colliding, there should be a function that properly changes the ball's angle. It's a very complicated problem it seems, because even if I know that the wall is 30°, its important to know from what side the ball is colliding into it. I tried using the "Reflection across a line in the plane" formula and also some vectors, but it never worked out for me. I'm not expecting a complete answer with code, if someone could suggest in what way this should be programmed, it would help aswell.
Edit:
Thanks for your tips guys, I realized what was causing the most confustion; if I select an angle on the canvas with my mouse, the starting coordinate(0,0) is in the bottom left corner. But since the canvas' starting coordinate is in the top left corner, this has to be considered.
Basically using this formula for calculating the angle:
function angle(cx, cy, ex, ey) {
var dy = ey - cy;
var dx = ex - cx;
var theta = Math.atan2(dy, dx);
theta *= 180 / Math.PI;
return theta;
}
if the ball moved from (50,50) to (100,100), the angle would be -45.
Now, this angle changes in the following way when hitting walls:
If im honest, I got these out of trial and error, am not really understanding why exactly 60 and 120.
It is not wise to use angle for moving ball and calculate Cos/Sin again and again. Instead use unit velocity direction vector with components vx, vy like this:
new_x = old_x + vx * Velocity_Magnitude * Time_Interval
Note that vx = Cos(angle), vy = Sin(angle), but with direction approach you seldom need to use trigonometric functions.
Tilted wall with angle Fi has normal
nx = -Sin(Fi)
ny = Cos(Fi)
To find reflection , you need to calculate dot product of velocity and normal
dot = vx * nx + vy * ny
Velocity after reflection transforms:
vnewx = v.x - 2 * dot * n.x
vnewy = v.y - 2 * dot * n.y
Use these values for further moving
(note that you can use both internal and external normal direction, because direction flip changes both components, and sign of 2 * dot * n.x remains the same)
Examples:
horizontal moving right
vx=1, vy=0
30 degrees wall has normal
nx=-1/2, ny=Sqrt(3)/2
dot = -1/2
vnewx = 1 - 2 * (-1/2) * (-1/2) = 1/2
vnewy = 0 - 2 * (-1/2) * Sqrt(3)/2 = Sqrt(3)/2
(velocity direction angle becomes 60 degrees)
horizontal moving left
vx=-1, vy=0
330 degrees wall (left bottom corner) has normal
nx=1/2, ny=Sqrt(3)/2
dot = -1/2
vnewx = -1 - 2 * (-1/2) * (1/2) = -1/2
vnewy = 0 - 2 * (-1/2) * (Sqrt(3)/2) = Sqrt(3)/2
(velocity direction angle becomes 120 degrees)
Here is a function that returns the angle of reflection given an angle of incidence and a surface angle (in degrees). It also ensures that the returned angle is between 0 and 359 degrees.
function angleReflect(incidenceAngle, surfaceAngle){
var a = surfaceAngle * 2 - incidenceAngle;
return a >= 360 ? a - 360 : a < 0 ? a + 360 : a;
}
Here's a demonstration, where the blue line is the angle of incidence, the purple line is the angle of reflection, and the black line is the surface.
If you're assuming that the ball behaves like light bouncing off a mirror, then the angle of incidence equals the angle of reflection.
So your board is 30° from 0° (straight up). The means the normal (perpendicular to the board at the point the ball hits ) is 300°. Say the ball arrives from 280°, it must leave at 320° as the difference between the angle of incidence and the normal and the angle of reflection and the normal must be equal.

SVG, Raphael.js, Drawing

This I guess is more of a maths question or maybe an SVG question. I was looking at modifying some example code I found on the raphael.js site. I already modified it to have a custom centre point. Now I want to modify it so that I can specify at which angle the arc is started at. (similar to d3.js so I can use it to have something like a bar chart with the middle missing).
However I have no idea where or how to begin. My maths is terrible, I have no idea what alpha is and the a variable does. Or why x and y are calculated that way. I have been reading the SVG specification over and over but I am missing some crucial basic knowledge and I don't know.
Can someone point me in the right direction so I can begin to understand this stuff?
window.onload = function () {
var r = Raphael("holder", 600, 600),
R = 200,
init = true,
param = {stroke: "#fff", "stroke-width": 30};
// Custom Attribute
r.customAttributes.arc = function (xPos, yPos, value, total, R) {
var alpha = 360 / total * value,
a = (90 - alpha) * Math.PI / 180,
x = xPos + R * Math.cos(a),
y = yPos - R * Math.sin(a),
var path = [["M", xPos, yPos - R], ["A", R, R, 0, +(alpha > 180), 1, x, y]];
return {path: path};
};
var sec = r.path().attr(param).attr({arc: [300, 300, 3, 60, R]});
};
Running the code produces:
<svg height="600" version="1.1" width="600" xmlns="http://www.w3.org/2000/svg" style="overflow: hidden; position: relative;">
<path style="" fill="none" stroke="#bfb5b5" d="M300,100A200,200,0,0,1,361.8033988749895,109.7886967409693" stroke-width="30">
</svg>
Also I have no idea how the arc parameters work together to draw what they are drawing.
Apologies for the lack of focus on the question.
EDIT:
It's based on the polar clock example. http://raphaeljs.com/polar-clock.html
I think the author of the example is trying to create a custom attribute in order to make it easy to create arcs based on clock rotation.
Basically the total paramter of the custom attribute represents the total movement of the clock (60 seconds) while value (3 in your case) represents the length (in seconds) of the arc you are trying to draw. So basically you have an arc of 3 seconds.
Now for the math:
alpha : the angle (in degrees) of the arc. You notice the conversion from seconds to degrees: 3 seconds -> 18 degrees
a : the angle in radians. Trigonometric formulas use radians not degrees, so you need this conversion. For some reason that I don't understand, this is the complementary angle (90 - alpha)
Edit: the complementary angle is (probably) used to compensate for the fact that in trigonometry the y-axis points upwards while on the svg canvas it points downwards.
x, y : ending points of the path (arc) you are drawing. These are caculated using elementary trigonometry (sorry..you're not getting any help here).
The parameters for the svg arc are described here: http://www.w3.org/TR/SVG/paths.html#PathDataEllipticalArcCommands

Categories

Resources