I'm using the meteor famous-views package and have a surface that is draggable in X using a GenericSync that registers a session var (dragPosition)
At the moment the translation is a 1:1 linear correspondence but I would like it to have an easing curve applied given a start position of [0,0] and limit (for now Math.round window.innerWidth/2) eg. 500
This coffeescript doesn't work but I think shows what I want to do. I understand why it doesn't work as the easing curve method has no understanding of where it is but how do provide that data and spit back a Famous.Transform.translate value that has the curve applied?
Template.test5.helpers
'dragTransform': ->
position = Session.get 'dragPosition'
absPosX = Math.abs position[0]
limX = Math.round (window.innerWidth * 0.5)
if absPosX < limX
position[0] = position[0]
else
position[0] = Math.sign(position[0]) * limX
transX = Famous.Transform.translate(position[0],position[1],0),
curve: Famous.Easing.inCirc
Famous.Transform.multiply transX
Given the Famous.Easing curves are simply functions that are defined on the domain [0, 1] and map to the range [0,1] you can use this to remap the values.
In the code below I've normalized the X position by dividing by it's limit (window.innerWidth). Multiplying this by the original position gave me the 'new' position with easing curve applied.
Template.test5.helpers
'dragTransform': ->
position = Session.get 'dragPosition'
absPosX = Math.abs position[0]
limX = Math.round (window.innerWidth * 0.5)
if absPosX < limX
position[0] = position[0]
else
position[0] = Math.sign(position[0]) * limX
console.log Famous.Transform
scalar = Famous.Easing.inCirc (absPosX/limX)
Famous.Transform.translate(position[0]*scalar,position[1],0)
Note
This code is based on a draggable object that is centered in the window and can't be dragged past it's extents. (that's why i've set the limit to window.innerWidth * 0.5)
Related
I have a graph like this:
And I want to be able to convert the position of P1 aka the ball you can drag around to scale with different starting and ending points on my screen.
I esentially want to make it so that the curve dot is around the same position no matter where the starting and ending positions are for the curve
So if I had a different points on my screen it would look the same as the graph
This is what I tried to do but it didn't work
function bezier.scale(startingPosition : Vector2, endingPosition : Vector2)
local screenSize = workspace.CurrentCamera.ViewportSize
local lengthX = (endingPosition.X - startingPosition.X)
local lengthY = (endingPosition.Y - startingPosition.Y)
local screenRelativeX = (screenSize.X - startingPosition.X) + lengthX
local screenRelativeY = (screenSize.Y - startingPosition.Y) + lengthY
local scaleX = (screenRelativeX / graphBackground.Size.X.Offset)
local scaleY = (screenRelativeY / graphBackground.Size.Y.Offset)
local x = (bezierPoint.Position.X.Offset * scaleX)
local y = (bezierPoint.Position.Y.Offset * scaleY)
return Vector2.new(x, y)
end
so your input is 4 2D points ... first 2 points p0,p1 are constant refer to your BEZIER start and end points and the next 2 q0,q1 are start and end point for your animation. So you want affine transform mapping between the two pairs. For that you need rotation and scale and offset...
Scale
is Easy its just ratio between line sizes so:
scale = |q1-q0| / |p1-p0|
Rotation
you can exploit dot product:
ang = acos( dot(p1-p0,q1-q0)/(|p1-p0|*|q1-q0|) )
the sign can be determined by 3D cross product (using z=0) for example:
if (cross(p1-p0,q1-q0).z >=0 ) ang=-ang;
however note that >=0 or <=0 depends on yoru coordinate system and rotation formula so it might be reversed in your case.
offset
simply apply the #1,#2 on p0 lets call the result P0 then the offset is easy:
offset = p0-P0
Putting all toghether
so transforming point p=(x,y) will be:
// #1 apply scale
x' = x*scale
y' = y*scale
// #2 apply rotation
x = x'*cos(ang) + y'*sin(ang)
y =-x'*sin(ang) + y'*cos(ang)
// #3 apply offset
x = x + offset.x
y = y + offset.y
Do not forget to use temp variables x',y' for the rotation! You might also construct 3x3 transform matrix for this instead.
For more info about transform matrices and vector math (dot and cross product included) see:
Understanding 4x4 homogenous transform matrices
Within a 2D infinite space, I have two circular sprites that represent massive bodies in outer space.
Each body has a pair of x, y coordinates, a mass, a speed and a direction (in radians).
On each frame of animation, I'm running the following code for each body (where this is the body being updated, and other is the other body):
x, y = other.x - this.x, other.y - this.y
angle = atan2(y, x)
distance = root(square(x) + square(y))
force = this.mass * other.mass / square(distance)
Note: I'm ignoring G as it's just a multiplier.
I know how to move the bodies around, based on their coordinates, speed and direction, but do not know how to update this.speed and this.direction to simulate gravity.
The gravitational force acting on a given body is represented as a vector and produces acceleration with components (ax and ay), which are calculated (based on what you already have) like this:
squared_distance = square(x) + square(y)
distance = sqrt(squared_distance)
accel = other.mass / squared_distance
ax = accel * x / distance
ay = accel * y / distance
Note that the angle (force/acceleration direction) is not needed.
Each body should have an associated velocity (instead of speed), which should be a two-component vector (vx and vy). It is updated like this (where dt is the time interval between updates):
this.vx += ax * dt
this.vy += ay * dt
Once the velocity of a given body has been updated, it can then be repositioned (updating its x, y coordinates) like this:
this.x += this.vx * dt
this.y += this.vy * dt
You can calculate the speed and direction if you need them, but they are not needed here.
I've got a camera attached to a parent that scales causing the camera to "zoom". I want the camera to tilt more at a lower scale. I need an equation that will tilt the camera between the min and max based on the scale of the parent.
Any help would be greatly appreciated =)
See the diagram below:
Instead of scale, you need distances. Consider the variable verical distance y and the target horizontal distance x which you want to keep fixed. The angle of the camera θ is related by
θ = ATAN(y/x)*(180/π)
Given the end conditions y_1/x = TAN(20°) and y_2/x = TAN(40°) one finds that
y_2 = TAN(40°)/TAN(20°)*y_1 = 2.3054*y_1
x = COS(20°)/SIN(20°)*y_1 = 2.7474*y_1
The initial height y_1 is required to compute the horizontal distance x.
Now since s=0.1 means y(s)=y_1 and s=1.0 means y(s)=y_2 then
y(s) = 10/9*(y_2-y_1)*s+(10*y_1-y_2)/9
= y_1*10*(1-s)/9+y_1*(10*s-1)*TAN(40°)/(9*TAN(20°))
= y_1*(1.450*s+0.855)
TAN(θ) = y(s)/x
TAN(θ) = 10*(1-s)*TAN(20°)/9+(10*s-1)*TAN(40°)/9
Use this:
θ(s) = 180/π*ATAN(0.5279*s+0.3112)
With the following example values
s θ(s)
0.1 20°
0.55 31°
1.0 40°
If I'm reading it right, the Scale varies from 0.1 to 1.0, and you want the Angle to vary from 20 to 40 degrees. Right?
A simple linear formula would look like
CurrentAngle = MinAngle + (CurrentScale - MinScale) * (MaxAngle - MinAngle) / (MaxScale - MinScale)
= 20 + (CurrentScale - 0.1) * (40 - 20) / (1 - 0.1)
= 20 + (CurrentScale - 0.1) * 20 / 0.9
So if you use 0.64 as the CurrentScale, as in your example above, you'd get
= 20 + (0.64 - 0.1) * 20 / 0.9
= 32
Linear is the simplest mathematically, but if your application is animated or needs to change the angle faster on one end or the other of your scale, you may get a more polished result from using a formula with a curve to it (logarithmic, parabolic or exponential, maybe?).
I wanna to produce a Pie Chart on a Hexagon. There are probably several solutions for this. In the picture are my Hexagon and two Ideas:
My Hexagon (6 vertices, 4 faces)
How it should look at the end (without the gray lines)
Math: Can I get some informations from the object to dynamically calculate new vertices (from the center to each point) to add colored faces?
Clipping: On a sphere a Pie-Chart is easy, maybe I can clip the THREE Object (WITHOUT SVG.js!) so I just see the Hexagon with the clipped Chart?
Well the whole clipping thing in three.js is already solved here : Object Overflow Clipping Three JS, with a fiddle that shows it works and all.
So I'll go for the "vertices" option, or rather, a function that, given a list of values gives back a list of polygons, one for each value, that are portions of the hexagon, such that
they all have the centre point as a vertex
the angle they have at that point is proportional to the value
they form a partition the hexagon
Let us suppose the hexagon is inscribed in a circle of radius R, and defined by the vertices :
{(R sqrt(3)/2, R/2), (0,R), (-R sqrt(3)/2, R/2), (-R sqrt(3)/2, -R/2), (0,-R), (R sqrt(3)/2, -R/2)}
This comes easily from the values cos(Pi/6), sin(Pi/6) and various symmetries.
Getting the angles at the centre for each polygon is pretty simple, since it is the same as for a circle. Now we need to know the position of the points that are on the hexagon.
Note that if you use the symmetries of the coordinate axes, there are only two cases : [0,Pi/6] and [Pi/6,Pi/2], and you then get your result by mirroring. If you use the rotational symmetry by Pi/3, you only have one case : [-Pi/6,Pi/6], and you get the result by rotation.
Using rotational symmetry
Thus for every point, you can consider it's angle to be between [-Pi/6,Pi/6]. Any point on the hexagon in that part has x=R sqrt(3)/2, which simplifies the problem a lot : we only have to find it's y value.
Now we assumed that we know the polar coordinate angle for our point, since it is the same as for a circle. Let us call it beta, and alpha its value in [-Pi/6,Pi/6] (modulo Pi/3). We don't know at what distance d it is from the centre, and thus we have the following system :
Which is trivially solved since cos is never 0 in the range [-Pi/6,Pi/6].
Thus d=R sqrt(3)/( 2 cos(alpha) ), and y=d sin(alpha)
So now we know
the angle from the centre beta
it's distance d from the centre, thanks to rotational symmetry
So our point is (d cos(beta), d sin(beta))
Code
Yeah, I got curious, so I ended up coding it. Sorry if you wanted to play with it yourself. It's working, and pretty ugly in the end (at least with this dataset), see the jsfiddle : http://jsfiddle.net/vb7on8vo/5/
var R = 100;
var hexagon = [{x:R*Math.sqrt(3)/2, y:R/2}, {x:0, y:R}, {x:-R*Math.sqrt(3)/2, y:R/2}, {x:-R*Math.sqrt(3)/2, y:-R/2}, {x:0, y:-R}, {x:R*Math.sqrt(3)/2, y:-R/2}];
var hex_angles = [Math.PI / 6, Math.PI / 2, 5*Math.PI / 6, 7*Math.PI / 6, 3*Math.PI / 2, 11*Math.PI / 6];
function regions(values)
{
var i, total = 0, regions = [];
for(i=0; i<values.length; i++)
total += values[i];
// first (0 rad) and last (2Pi rad) points are always at x=R Math.sqrt(3)/2, y=0
var prev_point = {x:hexagon[0].x, y:0}, last_angle = 0;
for(i=0; i<values.length; i++)
{
var j, theta, p = [{x:0,y:0}, prev_point], beta = last_angle + values[i] * 2 * Math.PI / total;
for( j=0; j<hexagon.length; j++)
{
theta = hex_angles[j];
if( theta <= last_angle )
continue;
else if( theta >= beta )
break;
else
p.push( hexagon[j] );
}
var alpha = beta - (Math.PI * (j % 6) / 3); // segment 6 is segment 0
var d = hexagon[0].x / Math.cos(alpha);
var point = {x:d*Math.cos(beta), y:d*Math.sin(beta)};
p.push( point );
regions.push(p.slice(0));
last_angle = beta;
prev_point = {x:point.x, y:point.y};
}
return regions;
}
This is a big one for any math/3d geometry lovers. Thank you in advance.
Overview
I have a figure created by extruding faces around twisting spline curves in space. I'm trying to place a "loop" (torus) oriented along the spline path at a given segment of the curve, so that it is "aligned" with the spline. By that I mean the torus's width is parallel to the spline path at the given extrusion segment, and it's height is perpendicular to the face that is selected (see below for picture).
Data I know:
I am given one of the faces of the figure. From that I can also glean that face's centroid (center point), the vertices that compose it, the surrounding faces, and the normal vector of the face.
Current (Non-working) solution outcome:
I can correctly create a torus loop around the centroid of the face that is clicked. However, it does not rotate properly to "align" with the face. See how they look a bit "off" below.
Here's a picture with the material around it:
and here's a picture with it in wireframe mode. You can see the extrusion segments pretty clearly.
Current (Non-working) methodology:
I am attempting to do two calculations. First, I'm calculating the the angle between two planes (the selected face and the horizontal plane at the origin). Second, I'm calculating the angle between the face and a vertical plane at the point of origin. With those two angles, I am then doing two rotations - an X and a Y rotation on the torus to what I hope would be the correct orientation. It's rotating the torus at a variable amount, but not in the place I want it to be.
Formulas:
In doing the above, I'm using the following to calculate the angle between two planes using their normal vectors:
Dot product of normal vector 1 and normal vector 2 = Magnitude of vector 1 * Magnitude of vector 2 * Cos (theta)
Or:
(n1)(n2) = || n1 || * || n2 || * cos (theta)
Or:
Angle = ArcCos { ( n1 * n2 ) / ( || n1 || * || n2 || ) }
To determine the magnitude of a vector, the formula is:
The square root of the sum of the components squared.
Or:
Sqrt { n1.x^2 + n1.y^2 + n1.z^2 }
Also, I'm using the following for the normal vectors of the "origin" planes:
Normal vector of horizontal plane: (1, 0, 0)
Normal vector of Vertical plane: (0, 1, 0)
I've thought through the above normal vectors a couple times... and I think(?) they are right?
Current Implementation:
Below is the code that I'm currently using to implement it. Any thoughts would be much appreciated. I have a sinking feeling that I'm taking a wrong approach in trying to calculate the angles between the planes. Any advice / ideas / suggestions would be much appreciated. Thank you very much in advance for any suggestions.
Function to calculate the angles:
this.toRadians = function (face, isX)
{
//Normal of the face
var n1 = face.normal;
//Normal of the vertical plane
if (isX)
var n2 = new THREE.Vector3(1, 0, 0); // Vector normal for vertical plane. Use for Y rotation.
else
var n2 = new THREE.Vector3(0, 1, 0); // Vector normal for horizontal plane. Use for X rotation.
//Equation to find the cosin of the angle. (n1)(n2) = ||n1|| * ||n2|| (cos theta)
//Find the dot product of n1 and n2.
var dotProduct = (n1.x * n2.x) + (n1.y * n2.y) + (n1.z * n2.z);
// Calculate the magnitude of each vector
var mag1 = Math.sqrt (Math.pow(n1.x, 2) + Math.pow(n1.y, 2) + Math.pow(n1.z, 2));
var mag2 = Math.sqrt (Math.pow(n2.x, 2) + Math.pow(n2.y, 2) + Math.pow(n2.z, 2));
//Calculate the angle of the two planes. Returns value in radians.
var a = (dotProduct)/(mag1 * mag2);
var result = Math.acos(a);
return result;
}
Function to create and rotate the torus loop:
this.createTorus = function (tubeMeshParams)
{
var torus = new THREE.TorusGeometry(5, 1.5, segments/10, 50);
fIndex = this.calculateFaceIndex();
//run the equation twice to calculate the angles
var xRadian = this.toRadians(geometry.faces[fIndex], false);
var yRadian = this.toRadians(geometry.faces[fIndex], true);
//Rotate the Torus
torus.applyMatrix(new THREE.Matrix4().makeRotationX(xRadian));
torus.applyMatrix(new THREE.Matrix4().makeRotationY(yRadian));
torusLoop = new THREE.Mesh(torus, this.m);
torusLoop.scale.x = torusLoop.scale.y = torusLoop.scale.z = tubeMeshParams['Scale'];
//Create the torus around the centroid
posx = geometry.faces[fIndex].centroid.x;
posy = geometry.faces[fIndex].centroid.y;
posz = geometry.faces[fIndex].centroid.z;
torusLoop.geometry.applyMatrix(new THREE.Matrix4().makeTranslation(posx, posy, posz));
torusLoop.geometry.computeCentroids();
torusLoop.geometry.computeFaceNormals();
torusLoop.geometry.computeVertexNormals();
return torusLoop;
}
I found I was using an incorrect approach to do this. Instead of trying to calculate each angle and do a RotationX and a RotationY, I should have done a rotation by axis. Definitely was over thinking it.
makeRotationAxis(); is a function built into three.js.