How to apply a scalar multiplication to a Quaternion - javascript

So, I'm converting some c++ to javascript and I really need to know how D3DX defines their quaternion operators.
//Here's the c++ version
D3DXQUATERNION Qvel = 0.5f * otherQuat* D3DXQUATERNION(angVel.x, angVel.y, angVel.z, 0);
//Here's the js quat multiplication
function quat_mul(q1, q2) {
return
[q1.x * q2.w + q1.y * q2.z - q1.z * q2.y + q1.w * q2.x,
-q1.x * q2.z + q1.y * q2.w + q1.z * q2.x + q1.w * q2.y,
q1.x * q2.y - q1.y * q2.x + q1.z * q2.w + q1.w * q2.z,
-q1.x * q2.x - q1.y * q2.y - q1.z * q2.z + q1.w * q2.w]
Is the scalar operation quat * 0.5f just like this?
quat.x *= .5;
quat.y *= .5;
quat.z *= .5;
quat.w *= .5;

According to this link, it's like you say (and also in the quaternion number system):
inline D3DXQUATERNION& D3DXQUATERNION::operator *= (FLOAT f)
{
x *= f;
y *= f;
z *= f;
w *= f;
return *this;
}

This is a very old question but I can verify this from a published source: Vince 2011 - Quaternion for Computer Graphics, page 57-58.
In simpler form:
q = [s, v] where s \in R and v \in R^3
\lambda q = [\lambda s, \lambda v] where \lambda \in R
In more complex notation:
q = a + bi + cj + dk
q * s = s * a + bi * s + cj * s + dk * s
= s * a + (b * s)i + (c * s)j + (d * s)k
in javascript
let quat = [0.4, 0.3, 0.7, 0.1];
function quaternion_scalar_multiplication(quat, s){
quat[0] *= s;
quat[1] *= s;
quat[2] *= s;
quat[3] *= s;
return quat;
}

Related

How to detect collision between object made of bezier curves and a circle?

So I've wrote a microbe animation.
It's all cool, but I think that it would be even better, if the microbe would be able to eat diatoms, and to destroy bubbles.
The issue is that the microbe is made of bezier curves.
I have no idea how to check collision between object made of bezier curves, and a circle in a reasonable way.
The only thing that comes to my mind, is to paint the microbe shape and bubbles a hidden canvas, and then check if they paint to the same pixels. But that would cause big performance issues IMHO.
Code: https://codepen.io/michaelKurowski/pen/opWeKY
class Cell is the cell, while class CellWallNode is a node of bezier curve, in case if somebody needs to look up the implementation.
The bubbles and diatoms can be easily simplified to circles.
Solution to bounds testing object defined by beziers
Below is an example solution to finding if a circle is inside an object defined by a center point and a set of beziers defining the perimeter.
The solution has only been tested for non intersecting cubic beziers. Also will not work if there are more than two intercepts between the object being tested and the center of the cell. However all you need to solve for the more complex bounds is there in the code.
The method
Define a center point to test from as a 2D point
Define the test point as a 2D point
Define a line from the center to the test point
For each bezier
Translate bezier so first point is at start of line
Rotate the bezier such that the line is aligned to the x axis
Solve the bezier polynomials to find the roots (location of x axis intercepts)
Use the roots to find position on bezier curve of line intercept.
Use the closest intercept to the point to find distance from center to perimeter.
If perimeter distance is greater than test point distance plus radius then inside.
Notes
The test is to a point along a line to the center not to a circle which would be a area defined by a triangle. As long as the circle radius is small compared to the size of the beziers the approximation works well.
Not sure if you are using cubic or quadratic beziers so the solution covers both cubic and quadratic beziers.
Example
The snippet creates a set of beziers (cubic) around a center point. the object theBlob holds the animated beziers. The function testBlob tests the mouse position and returns true if inside theBlob. The object bezHelper contains all the functionality needed to solve the problem.
The cubic root solver was derived from github intersections cube root solver.
const bezHelper = (()=>{
// creates a 2D point
const P2 = (x=0, y= x === 0 ? 0 : x.y + (x = x.x, 0)) => ({x, y});
const setP2As = (p,pFrom) => (p.x = pFrom.x, p.y = pFrom.y, p);
// To prevent heap thrashing close over some pre defined 2D points
const v1 = P2();
const v2 = P2();
const v3 = P2();
const v4 = P2();
var u,u1,u2;
// solves quadratic for bezier 2 returns first root
function solveBezier2(A, B, C){
// solve the 2nd order bezier equation.
// There can be 2 roots, u,u1 hold the results;
// 2nd order function a+2(-a+b)x+(a-2b+c)x^2
a = (A - 2 * B + C);
b = 2 * ( - A + B);
c = A;
a1 = 2 * a;
c = b * b - 4 * a * c;
if(c < 0){
u = Infinity;
u1 = Infinity;
return u;
}else{
b1 = Math.sqrt(c);
}
u = (-b + b1) / a1;
u1 = (-b - b1) / a1;
return u;
}
// solves cubic for bezier 3 returns first root
function solveBezier3(A, B, C, D){
// There can be 3 roots, u,u1,u2 hold the results;
// Solves 3rd order a+(-2a+3b)t+(2a-6b+3c)t^2+(-a+3b-3c+d)t^3 Cardano method for finding roots
// this function was derived from http://pomax.github.io/bezierinfo/#intersections cube root solver
// Also see https://en.wikipedia.org/wiki/Cubic_function#Cardano.27s_method
function crt(v) {
if(v<0) return -Math.pow(-v,1/3);
return Math.pow(v,1/3);
}
function sqrt(v) {
if(v<0) return -Math.sqrt(-v);
return Math.sqrt(v);
}
var a, b, c, d, p, p3, q, q2, discriminant, U, v1, r, t, mp3, cosphi,phi, t1, sd;
u2 = u1 = u = -Infinity;
d = (-A + 3 * B - 3 * C + D);
a = (3 * A - 6 * B + 3 * C) / d;
b = (-3 * A + 3 * B) / d;
c = A / d;
p = (3 * b - a * a) / 3;
p3 = p / 3;
q = (2 * a * a * a - 9 * a * b + 27 * c) / 27;
q2 = q / 2;
a /= 3;
discriminant = q2 * q2 + p3 * p3 * p3;
if (discriminant < 0) {
mp3 = -p / 3;
r = sqrt(mp3 * mp3 * mp3);
t = -q / (2 * r);
cosphi = t < -1 ? -1 : t > 1 ? 1 : t;
phi = Math.acos(cosphi);
t1 = 2 * crt(r);
u = t1 * Math.cos(phi / 3) - a;
u1 = t1 * Math.cos((phi + 2 * Math.PI) / 3) - a;
u2 = t1 * Math.cos((phi + 4 * Math.PI) / 3) - a;
return u;
}
if(discriminant === 0) {
U = q2 < 0 ? crt(-q2) : -crt(q2);
u = 2 * U - a;
u1 = -U - a;
return u;
}
sd = sqrt(discriminant);
u = crt(sd - q2) - crt(sd + q2) - a;
return u;
}
// get a point on the bezier at pos ( from 0 to 1 values outside this range will be outside the bezier)
// p1, p2 are end points and cp1, cp2 are control points.
// ret is the resulting point. If given it is set to the result, if not given a new point is created
function getPositionOnBez(pos,p1,p2,cp1,cp2,ret = P2()){
if(pos === 0){
ret.x = p1.x;
ret.y = p1.y;
return ret;
}else
if(pos === 1){
ret.x = p2.x;
ret.y = p2.y;
return ret;
}
v1.x = p1.x;
v1.y = p1.y;
var c = pos;
if(cp2 === undefined){
v2.x = cp1.x;
v2.y = cp1.y;
v1.x += (v2.x - v1.x) * c;
v1.y += (v2.y - v1.y) * c;
v2.x += (p2.x - v2.x) * c;
v2.y += (p2.y - v2.y) * c;
ret.x = v1.x + (v2.x - v1.x) * c;
ret.y = v1.y + (v2.y - v1.y) * c;
return ret;
}
v2.x = cp1.x;
v2.y = cp1.y;
v3.x = cp2.x;
v3.y = cp2.y;
v1.x += (v2.x - v1.x) * c;
v1.y += (v2.y - v1.y) * c;
v2.x += (v3.x - v2.x) * c;
v2.y += (v3.y - v2.y) * c;
v3.x += (p2.x - v3.x) * c;
v3.y += (p2.y - v3.y) * c;
v1.x += (v2.x - v1.x) * c;
v1.y += (v2.y - v1.y) * c;
v2.x += (v3.x - v2.x) * c;
v2.y += (v3.y - v2.y) * c;
ret.x = v1.x + (v2.x - v1.x) * c;
ret.y = v1.y + (v2.y - v1.y) * c;
return ret;
}
const cubicBez = 0;
const quadraticBez = 1;
const none = 2;
var type = none;
// working bezier
const p1 = P2();
const p2 = P2();
const cp1 = P2();
const cp2 = P2();
// rotated bezier
const rp1 = P2();
const rp2 = P2();
const rcp1 = P2();
const rcp2 = P2();
// translate and rotate bezier
function transformBez(pos,rot){
const ax = Math.cos(rot);
const ay = Math.sin(rot);
var x = p1.x - pos.x;
var y = p1.y - pos.y;
rp1.x = x * ax - y * ay;
rp1.y = x * ay + y * ax;
x = p2.x - pos.x;
y = p2.y - pos.y;
rp2.x = x * ax - y * ay;
rp2.y = x * ay + y * ax;
x = cp1.x - pos.x;
y = cp1.y - pos.y;
rcp1.x = x * ax - y * ay;
rcp1.y = x * ay + y * ax;
if(type === cubicBez){
x = cp2.x - pos.x;
y = cp2.y - pos.y;
rcp2.x = x * ax - y * ay;
rcp2.y = x * ay + y * ax;
}
}
function getPosition2(pos,ret){
return getPositionOnBez(pos,p1,p2,cp1,undefined,ret);
}
function getPosition3(pos,ret){
return getPositionOnBez(pos,p1,p2,cp1,cp2,ret);
}
const API = {
getPosOnQBez(pos,p1,cp1,p2,ret){
return getPositionOnBez(pos,p1,p2,cp1,undefined,ret);
},
getPosOnCBez(pos,p1,cp1,cp2,p2,ret){
return getPositionOnBez(pos,p1,p2,cp1,cp2,ret);
},
set bezQ(points){
setP2As(p1, points[0]);
setP2As(cp1, points[1]);
setP2As(p2, points[2]);
type = quadraticBez;
},
set bezC(points){
setP2As(p1, points[0]);
setP2As(cp1, points[1]);
setP2As(cp2, points[2]);
setP2As(p2, points[3]);
type = cubicBez;
},
isInside(center, testPoint, pointRadius){
drawLine(testPoint , center);
v1.x = (testPoint.x - center.x);
v1.y = (testPoint.y - center.y);
const pointDist = Math.sqrt(v1.x * v1.x + v1.y * v1.y)
const dir = -Math.atan2(v1.y,v1.x);
transformBez(center,dir);
if(type === cubicBez){
solveBezier3(rp1.y, rcp1.y, rcp2.y, rp2.y);
if (u < 0 || u > 1) { u = u1 }
if (u < 0 || u > 1) { u = u2 }
if (u < 0 || u > 1) { return }
getPosition3(u, v4);
}else{
solveBezier2(rp1.y, rcp1.y, rp2.y);
if (u < 0 || u > 1) { u = u1 }
if (u < 0 || u > 1) { return }
getPosition2(u, v4);
}
drawCircle(v4);
const dist = Math.sqrt((v4.x - center.x) ** 2 + (v4.y - center.y) ** 2);
const dist1 = Math.sqrt((v4.x - testPoint.x) ** 2 + (v4.y - testPoint.y) ** 2);
return dist1 < dist && dist > pointDist - pointRadius;
}
}
return API;
})();
const ctx = canvas.getContext("2d");
const m = {x : 0, y : 0};
document.addEventListener("mousemove",e=>{
var b = canvas.getBoundingClientRect();
m.x = e.pageX - b.left - scrollX - 2;
m.y = e.pageY - b.top - scrollY - 2;
});
function drawCircle(p,r = 5,col = "black"){
ctx.beginPath();
ctx.strokeStyle = col;
ctx.arc(p.x,p.y,r,0,Math.PI*2)
ctx.stroke();
}
function drawLine(p1,p2,r = 5,col = "black"){
ctx.beginPath();
ctx.strokeStyle = col;
ctx.lineTo(p1.x,p1.y);
ctx.lineTo(p2.x,p2.y);
ctx.stroke();
}
const w = 400;
const h = 400;
const diag = Math.sqrt(w * w + h * h);
// creates a 2D point
const P2 = (x=0, y= x === 0 ? 0 : x.y + (x = x.x, 0)) => ({x, y});
const setP2As = (p,pFrom) => (p.x = pFrom.x, p.y = pFrom.y, p);
// random int and double
const randI = (min, max = min + (min = 0)) => (Math.random()*(max - min) + min) | 0;
const rand = (min = 1, max = min + (min = 0)) => Math.random() * (max - min) + min;
const theBlobSet = [];
const theBlob = [];
function createCubicBlob(segs){
const step = Math.PI / segs;
for(var i = 0; i < Math.PI * 2; i += step){
const dist = rand(diag * (1/6), diag * (1/5));
const ang = i + rand(-step * 0.2,step * 0.2);
const p = P2(
w / 2 + Math.cos(ang) * dist,
h / 2 + Math.sin(ang) * dist
);
theBlobSet.push(p);
theBlob.push(P2(p));
}
theBlobSet[theBlobSet.length -1] = theBlobSet[0];
theBlob[theBlobSet.length -1] = theBlob[0];
}
createCubicBlob(8);
function animateTheBlob(time){
for(var i = 0; i < theBlobSet.length-1; i++){
const ang = Math.sin(time + i) * 6;
theBlob[i].x = theBlobSet[i].x + Math.cos(ang) * diag * 0.04;
theBlob[i].y = theBlobSet[i].y + Math.sin(ang) * diag * 0.04;
}
}
function drawTheBlob(){
ctx.strokeStyle = "black";
ctx.lineWidth = 3;
ctx.beginPath();
var i = 0;
ctx.moveTo(theBlob[i].x,theBlob[i++].y);
while(i < theBlob.length){
ctx.bezierCurveTo(
theBlob[i].x,theBlob[i++].y,
theBlob[i].x,theBlob[i++].y,
theBlob[i].x,theBlob[i++].y
);
}
ctx.stroke();
}
var center = P2(w/2,h/2);
function testBlob(){
var i = 0;
while(i < theBlob.length-3){
bezHelper.bezC = [theBlob[i++], theBlob[i++], theBlob[i++], theBlob[i]];
if(bezHelper.isInside(center,m,6)){
return true;
}
}
return false;
}
// main update function
function update(timer){
ctx.clearRect(0,0,w,h);
animateTheBlob(timer/1000)
drawTheBlob();
if(testBlob()){
ctx.strokeStyle = "red";
}else{
ctx.strokeStyle = "black";
}
ctx.beginPath();
ctx.arc(m.x,m.y,5,0,Math.PI*2)
ctx.stroke();
requestAnimationFrame(update);
}
requestAnimationFrame(update);
canvas { border : 2px solid black; }
<canvas id="canvas" width = "400" height = "400"></canvas>
I had created an animation of bubbles in which al the circle will expand which are 50px neer to the mouse.
so here is the trick. you can just simply change mouseX,mouseY with your microbe's X and Y coordinates and 50 to the radius of your microbe.
And when my bubbles get bigger, so there you can destroy you air bubbles.
here is the link to my Animation.
https://ankittorenzo.github.io/canvasAnimations/Elements/Bubbles/
here is the link to my GitHub Code.
https://github.com/AnkitTorenzo/canvasAnimations/blob/master/Elements/Bubbles/js/main.js
Let Me Know if you have any problem.

html5 canvas bezier curve get all the points

I like to get some points from bezier curve.I found
Find all the points of a cubic bezier curve in javascript
Position is easy. First, compute the blending functions. These control the "effect" of your control points on the curve.
B0_t = (1-t)^3
B1_t = 3 * t * (1-t)^2
B2_t = 3 * t^2 * (1-t)
B3_t = t^3
Notice how B0_t is1 when t is 0 (and everything else is zero). Also, B3_t is 1 when t is 1 (and everything else is zero). So the curve starts at (ax, ay), and ends at (dx, dy).
Any intermediate point (px_t, py_t) will be given by the following (vary t from 0 to 1, in small increments inside a loop):
px_t = (B0_t * ax) + (B1_t * bx) + (B2_t * cx) + (B3_t * dx)
py_t = (B0_t * ay) + (B1_t * by) + (B2_t * cy) + (B3_t * dy)
My code
var ax = 100, ay = 250;
var bx = 150, by = 100;
var cx = 350, cy = 100;
var dx = 400, dy = 250;
ctx.lineWidth = 1;
ctx.strokeStyle = "#333";
ctx.beginPath();
ctx.moveTo(ax, ay);
ctx.bezierCurveTo(bx, by, cx, cy, dx, dy);
ctx.stroke();
var t = 0
var B0_t = (1 - t) ^ 3
var B1_t = 3 * t * (1 - t) ^ 2
var B2_t = 3 * t ^ 2 * (1 - t)
var B3_t = t ^ 3
// override manually *Notice* above
//This is work first and laste point in curve
// B0_t = 1; B1_t = 0; B2_t = 0; B3_t = 0; t = 0;
// B0_t = 0; B1_t = 0; B2_t = 0; B3_t = 1; t = 1;
var px_t = (B0_t * ax) + (B1_t * bx) + (B2_t * cx) + (B3_t * dx)
var py_t = (B0_t * ay) + (B1_t * by) + (B2_t * cy) + (B3_t * dy)
// doesnt work
var t = 0
var B0_t = (1 - t) ^ 3 //*Notice* above should be 1
//Debug (1 - t) ^ 3 = 2 ??
var B1_t = 3 * t * (1 - t) ^ 2 //*Notice* above should be 0
//Debug 3 * t * (1 - t) ^ 2 = 2 ??
var B2_t = 3 * t ^ 2 * (1 - t)//*Notice* above should be 0
//Debug 3 * t ^ 2 * (1 - t) =2 ??
var B3_t = t ^ 3//*Notice* above should be 0 but its 2
//Debug t ^ 3 = 3 ??
var px_t = (B0_t * ax) + (B1_t * bx) + (B2_t * cx) + (B3_t * dx)
var py_t = (B0_t * ay) + (B1_t * by) + (B2_t * cy) + (B3_t * dy)
Appreciate any help thanks
How to find the pixels along a Bezier Curve
This set of functions will find an [x,y] point at interval T along cubic Bezier curve where 0<=T<=1.
In simple terms: It plots points along a cubic Bezier curve from start to end.
// Given the 4 control points on a Bezier curve
// get x,y at interval T along the curve (0<=T<=1)
// The curve starts when T==0 and ends when T==1
function getCubicBezierXYatPercent(startPt, controlPt1, controlPt2, endPt, percent) {
var x = CubicN(percent, startPt.x, controlPt1.x, controlPt2.x, endPt.x);
var y = CubicN(percent, startPt.y, controlPt1.y, controlPt2.y, endPt.y);
return ({
x: x,
y: y
});
}
// cubic helper formula
function CubicN(T, a, b, c, d) {
var t2 = T * T;
var t3 = t2 * T;
return a + (-a * 3 + T * (3 * a - a * T)) * T + (3 * b + T * (-6 * b + b * 3 * T)) * T + (c * 3 - c * 3 * T) * t2 + d * t3;
}
You can fetch the points along the curve by sending the plotting function a large number of T values between 0.00 & 1.00.
Example code and a demo:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
var cBez1=[{x:250,y: 120},{x:290,y:-40},{x:300,y:200},{x:400,y:150}]
drawBez(cBez1);
var cPoints=findCBezPoints(cBez1);
drawPlots(cPoints);
function findCBezPoints(b){
var startPt=b[0];
var controlPt1=b[1];
var controlPt2=b[2];
var endPt=b[3];
var pts=[b[0]];
var lastPt=b[0];
var tests=5000;
for(var t=0;t<=tests;t++){
// calc another point along the curve
var pt=getCubicBezierXYatT(b[0],b[1],b[2],b[3], t/tests);
// add the pt if it's not already in the pts[] array
var dx=pt.x-lastPt.x;
var dy=pt.y-lastPt.y;
var d=Math.sqrt(dx*dx+dy*dy);
var dInt=parseInt(d);
if(dInt>0 || t==tests){
lastPt=pt;
pts.push(pt);
}
}
return(pts);
}
// Given the 4 control points on a Bezier curve
// Get x,y at interval T along the curve (0<=T<=1)
// The curve starts when T==0 and ends when T==1
function getCubicBezierXYatT(startPt, controlPt1, controlPt2, endPt, T) {
var x = CubicN(T, startPt.x, controlPt1.x, controlPt2.x, endPt.x);
var y = CubicN(T, startPt.y, controlPt1.y, controlPt2.y, endPt.y);
return ({
x: x,
y: y
});
}
// cubic helper formula
function CubicN(T, a, b, c, d) {
var t2 = T * T;
var t3 = t2 * T;
return a + (-a * 3 + T * (3 * a - a * T)) * T + (3 * b + T * (-6 * b + b * 3 * T)) * T + (c * 3 - c * 3 * T) * t2 + d * t3;
}
function drawPlots(pts){
ctx.fillStyle='red';
// don't draw the last dot b/ its radius will display past the curve
for(var i=0;i<pts.length-1;i++){
ctx.beginPath();
ctx.arc(pts[i].x,pts[i].y,1,0,Math.PI*2);
ctx.fill();
}
}
function drawBez(b){
ctx.lineWidth=7;
ctx.beginPath();
ctx.moveTo(b[0].x,b[0].y);
ctx.bezierCurveTo(b[1].x,b[1].y, b[2].x,b[2].y, b[3].x,b[3].y);
ctx.stroke();
}
body{ background-color: ivory; }
#canvas{border:1px solid red; margin:0 auto; }
<h4>Black line is context.bezierCurveTo<br>Red "line" is really dot-points plotted along the curve</h4>
<canvas id="canvas" width=500 height=300></canvas>

WebGL Perspective Projection Matrix

Can someone show me a function in javascript/webGL that will change 3d coordinates into 2d projected perspective coordinates? Thnx
Here's a pretty typical perspective matrix function
function perspective(fieldOfViewYInRadians, aspect, zNear, zFar, dst) {
dst = dst || new Float32Array(16);
var f = Math.tan(Math.PI * 0.5 - 0.5 * fieldOfViewYInRadians);
var rangeInv = 1.0 / (zNear - zFar);
dst[0] = f / aspect;
dst[1] = 0;
dst[2] = 0;
dst[3] = 0;
dst[4] = 0;
dst[5] = f;
dst[6] = 0;
dst[7] = 0;
dst[8] = 0;
dst[9] = 0;
dst[10] = (zNear + zFar) * rangeInv;
dst[11] = -1;
dst[12] = 0;
dst[13] = 0;
dst[14] = zNear * zFar * rangeInv * 2;
dst[15] = 0;
return dst;
}
If you have a vertex shader like this
attribute vec4 a_position;
uniform mat4 u_matrix;
void main() {
gl_Position = u_matrix * a_position;
}
You'll get 2d projected coordinates in WebGL. If actually want those coordinates in pixels in say JavaScript you need to divide by w and the expand to pixels
var transformPoint = function(m, v) {
var x = v[0];
var y = v[1];
var z = v[2];
var w = x * m[0*4+3] + y * m[1*4+3] + z * m[2*4+3] + m[3*4+3];
return [(x * m[0*4+0] + y * m[1*4+0] + z * m[2*4+0] + m[3*4+0]) / w,
(x * m[0*4+1] + y * m[1*4+1] + z * m[2*4+1] + m[3*4+1]) / w,
(x * m[0*4+2] + y * m[1*4+2] + z * m[2*4+2] + m[3*4+2]) / w];
};
var somePoint = [20,30,40];
var projectedPoint = transformPoint(projectionMatrix, somePoint);
var screenX = (projectedPoint[0] * 0.5 + 0.5) * canvas.width;
var screenZ = (projectedPoint[1] * -0.5 + 0.5) * canvas.height;
more here
Assuming the point is in world coordinates and your camera has a world matrix and a projection matrix, this is much simpler:
function point3d_to_screen(point, cameraWorldMatrix, projMatrix, screenWidth, screenHeight) {
var mat, p, x, y;
p = [point[0], point[1], point[2], 1];
mat = mat4.create();
mat4.invert(mat, cameraWorldMatrix);
mat4.mul(mat, projMatrix, mat);
vec4.transformMat4(p, p, mat);
x = (p[0] / p[3] + 1) * 0.5 * screenWidth;
y = (1 - p[1] / p[3]) * 0.5 * screenHeight;
return [x, y];
}
I'm using gl-matrix in this function.

Recreating CSS3 transitions Cubic-Bezier curve

In CSS3 transitions, you can specify a timing function as 'cubic-bezier:(0.25, 0.3, 0.8, 1.0)'
In that string, you are only specifying the XY for points P1 and P2 along the curve, as P0 and P3 are always (0.0, 0.0), and (1.0, 1.0) respectively.
According to Apple's site:
x [is] expressed as a fraction of the overall duration and y expressed as a fraction of the overall change
My question is how can this be mapped back to a traditional 1 dimensional T value in javascript?
--
From Apple docs on animating with transitions
Browsing through webkit-source a bit, the following code will give the correct T value for the implicit curve used in CSS3 transitions:
Visual demo (codepen.io)
Hope this helps someone!
function loop(){
var t = (now - animationStartTime) / ( animationDuration*1000 );
var curve = new UnitBezier(Bx, By, Cx, Cy);
var t1 = curve.solve(t, UnitBezier.prototype.epsilon);
var s1 = 1.0-t1;
// Lerp using solved T
var finalPosition.x = (startPosition.x * s1) + (endPosition.x * t1);
var finalPosition.y = (startPosition.y * s1) + (endPosition.y * t1);
}
/**
* Solver for cubic bezier curve with implicit control points at (0,0) and (1.0, 1.0)
*/
function UnitBezier(p1x, p1y, p2x, p2y) {
// pre-calculate the polynomial coefficients
// First and last control points are implied to be (0,0) and (1.0, 1.0)
this.cx = 3.0 * p1x;
this.bx = 3.0 * (p2x - p1x) - this.cx;
this.ax = 1.0 - this.cx -this.bx;
this.cy = 3.0 * p1y;
this.by = 3.0 * (p2y - p1y) - this.cy;
this.ay = 1.0 - this.cy - this.by;
}
UnitBezier.prototype.epsilon = 1e-6; // Precision
UnitBezier.prototype.sampleCurveX = function(t) {
return ((this.ax * t + this.bx) * t + this.cx) * t;
}
UnitBezier.prototype.sampleCurveY = function (t) {
return ((this.ay * t + this.by) * t + this.cy) * t;
}
UnitBezier.prototype.sampleCurveDerivativeX = function (t) {
return (3.0 * this.ax * t + 2.0 * this.bx) * t + this.cx;
}
UnitBezier.prototype.solveCurveX = function (x, epsilon) {
var t0;
var t1;
var t2;
var x2;
var d2;
var i;
// First try a few iterations of Newton's method -- normally very fast.
for (t2 = x, i = 0; i < 8; i++) {
x2 = this.sampleCurveX(t2) - x;
if (Math.abs (x2) < epsilon)
return t2;
d2 = this.sampleCurveDerivativeX(t2);
if (Math.abs(d2) < epsilon)
break;
t2 = t2 - x2 / d2;
}
// No solution found - use bi-section
t0 = 0.0;
t1 = 1.0;
t2 = x;
if (t2 < t0) return t0;
if (t2 > t1) return t1;
while (t0 < t1) {
x2 = this.sampleCurveX(t2);
if (Math.abs(x2 - x) < epsilon)
return t2;
if (x > x2) t0 = t2;
else t1 = t2;
t2 = (t1 - t0) * .5 + t0;
}
// Give up
return t2;
}
// Find new T as a function of Y along curve X
UnitBezier.prototype.solve = function (x, epsilon) {
return this.sampleCurveY( this.solveCurveX(x, epsilon) );
}
You want to find the [0,1] value for any time value t [0,1]? There's a well-defined equation for a cubic bezier curve. Wikipedia page: http://en.wikipedia.org/wiki/B%C3%A9zier_curve#Cubic_B.C3.A9zier_curves
So I don't have to type out their (probably LaTeX-formatted) formula, I copy-pasted the same formula from http://local.wasp.uwa.edu.au/~pbourke/geometry/bezier/index2.html . This also has a C implementation which, on a quick read-through, should be easy to port to javascript:
B(u) = P0 * ( 1 - u )3 + P1 * 3 * u * ( 1 - u )2 + P2 * 3 * u2 * ( 1 - u ) + P3 * u3
What he's calling mu on that page is your time variable t.
Edit: If you don't want to do the math it looks like someone already wrote a small utility library in javascript to do basic bezier curve math: https://github.com/sporritt/jsBezier . pointOnCurve(curve, location) looks like just what you're asking for.
I have try and search a lot of time and forms and definetly i have reached one simple and fast. The trick is get the cubic bezier function in this form:
P(u) = u^3(c0 + 3c1 -3c2 +c3) + u^2(3c0 -6c1 +3c2) + u(-3c0 +3c1) + c0
where ci are the control points.
The other part is search y from x with a binary search.
static public class CubicBezier {
private BezierCubic bezier = new BezierCubic();
public CubicBezier(float x1, float y1, float x2, float y2) {
bezier.set(new Vector3(0,0,0), new Vector3(x1,y1,0), new Vector3(x2,y2,0), new Vector3(1,1,1));
}
public float get(float t) {
float l=0, u=1, s=(u+l)*0.5f;
float x = bezier.getValueX(s);
while (Math.abs(t-x) > 0.0001f) {
if (t > x) { l = s; }
else { u = s; }
s = (u+l)*0.5f;
x = bezier.getValueX(s);
}
return bezier.getValueY(s);
}
};
public class BezierCubic {
private float[][] cpoints = new float[4][3];
private float[][] polinom = new float[4][3];
public BezierCubic() {}
public void set(Vector3 c0, Vector3 c1, Vector3 c2, Vector3 c3) {
setPoint(0, c0);
setPoint(1, c1);
setPoint(2, c2);
setPoint(3, c3);
generate();
}
public float getValueX(float u) {
return getValue(0, u);
}
public float getValueY(float u) {
return getValue(1, u);
}
public float getValueZ(float u) {
return getValue(2, u);
}
private float getValue(int i, float u) {
return ((polinom[0][i]*u + polinom[1][i])*u + polinom[2][i])*u + polinom[3][i];
}
private void generate() {
for (int i=0; i<3; i++) {
float c0 = cpoints[0][i], c1 = cpoints[1][i], c2 = cpoints[2][i], c3 = cpoints[3][i];
polinom[0][i] = c0 + 3*(c1 - c2) + c3;
polinom[1][i] = 3*(c0 - 2*c1 + c2);
polinom[2][i] = 3*(-c0 + c1);
polinom[3][i] = c0;
}
}
private void setPoint(int i, Vector3 v) {
cpoints[i][0] = v.x;
cpoints[i][1] = v.y;
cpoints[i][2] = v.z;
}
}

iOS: Destination point given distance and bearing from start point

Given a start point, initial bearing, and distance, this will calculate the destination point and final bearing travelling along a (shortest distance) great circle arc:
var lat2 =
Math.asin( Math.sin(lat1)*Math.cos(d/R) +
Math.cos(lat1)*Math.sin(d/R)*Math.cos(brng) );
var lon2 =
lon1 + Math.atan2(Math.sin(brng)*Math.sin(d/R)*Math.cos(lat1),
Math.cos(d/R)-Math.sin(lat1)*Math.sin(lat2));
This code is in JavaScript.
I'dd like the same thing for iOS, so in objective-c.
Anyone knows about a class that would do the trick?
I'm not translating your Javascript, this is just a routine I found somewhere for a project of mine that does the same thing:
- (CLLocationCoordinate2D) NewLocationFrom:(CLLocationCoordinate2D)startingPoint
atDistanceInMiles:(float)distanceInMiles
alongBearingInDegrees:(double)bearingInDegrees {
double lat1 = DEG2RAD(startingPoint.latitude);
double lon1 = DEG2RAD(startingPoint.longitude);
double a = 6378137, b = 6356752.3142, f = 1/298.257223563; // WGS-84 ellipsiod
double s = distanceInMiles * 1.61 * 1000; // Convert to meters
double alpha1 = DEG2RAD(bearingInDegrees);
double sinAlpha1 = sin(alpha1);
double cosAlpha1 = cos(alpha1);
double tanU1 = (1 - f) * tan(lat1);
double cosU1 = 1 / sqrt((1 + tanU1 * tanU1));
double sinU1 = tanU1 * cosU1;
double sigma1 = atan2(tanU1, cosAlpha1);
double sinAlpha = cosU1 * sinAlpha1;
double cosSqAlpha = 1 - sinAlpha * sinAlpha;
double uSq = cosSqAlpha * (a * a - b * b) / (b * b);
double A = 1 + uSq / 16384 * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq)));
double B = uSq / 1024 * (256 + uSq * (-128 + uSq * (74 - 47 * uSq)));
double sigma = s / (b * A);
double sigmaP = 2 * kPi;
double cos2SigmaM;
double sinSigma;
double cosSigma;
while (abs(sigma - sigmaP) > 1e-12) {
cos2SigmaM = cos(2 * sigma1 + sigma);
sinSigma = sin(sigma);
cosSigma = cos(sigma);
double deltaSigma = B * sinSigma * (cos2SigmaM + B / 4 * (cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM) - B / 6 * cos2SigmaM * (-3 + 4 * sinSigma * sinSigma) * (-3 + 4 * cos2SigmaM * cos2SigmaM)));
sigmaP = sigma;
sigma = s / (b * A) + deltaSigma;
}
double tmp = sinU1 * sinSigma - cosU1 * cosSigma * cosAlpha1;
double lat2 = atan2(sinU1 * cosSigma + cosU1 * sinSigma * cosAlpha1, (1 - f) * sqrt(sinAlpha * sinAlpha + tmp * tmp));
double lambda = atan2(sinSigma * sinAlpha1, cosU1 * cosSigma - sinU1 * sinSigma * cosAlpha1);
double C = f / 16 * cosSqAlpha * (4 + f * (4 - 3 * cosSqAlpha));
double L = lambda - (1 - C) * f * sinAlpha * (sigma + C * sinSigma * (cos2SigmaM + C * cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM)));
double lon2 = lon1 + L;
// Create a new CLLocationCoordinate2D for this point
CLLocationCoordinate2D edgePoint = CLLocationCoordinate2DMake(RAD2DEG(lat2), RAD2DEG(lon2));
return edgePoint;
}
You are making things very difficult :)
Just try my code:
LatDistance = Cos(BearingInDegrees)*distance;
LongDistance = Sin(BearingInDegrees)*distance;
CLLocationDegrees *destinationLat = CurrentLatitude + (LatDistance*0.00001);
CLLocationDegrees *destinationLong = CurrentLongitude + (LongDistance*0.00001);
That's it. Very simple.

Categories

Resources