1
0
mirror of https://github.com/Pomax/BezierInfo-2.git synced 2025-08-04 07:47:40 +02:00
Files
2018-07-13 18:14:31 -07:00

263 lines
6.6 KiB
JavaScript

var vectorOffset;
var normalsOffset;
var SHADOW_ALPHA = 0.2;
var SHOW_PROJECTIONS = true;
function normalize(v) {
var d = Math.sqrt(v.x*v.x + v.y*v.y + v.z*v.z);
return { x:v.x/d, y:v.y/d, z:v.z/d };
}
function vdot(v1, v2) {
return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z;
}
function vscale(v1, s) {
return {
x: s * v1.x,
y: s * v1.y,
z: s * v1.z
};
}
function vplus(v1, v2) {
return {
x: v1.x + v2.x,
y: v1.y + v2.y,
z: v1.z + v2.z
};
}
function vminus(v1, v2) {
return {
x: v1.x - v2.x,
y: v1.y - v2.y,
z: v1.z - v2.z
};
}
function vcross(v1, v2) {
return {
x: v1.y * v2.z - v1.z * v2.y,
y: v1.z * v2.x - v1.x * v2.z,
z: v1.x * v2.y - v1.y * v2.x
};
}
function vlerp(t, v1, v2) {
return {
x: (1-t)*v1.x + t*v2.x,
y: (1-t)*v1.y + t*v2.y,
z: (1-t)*v1.z + t*v2.z
};
}
module.exports = {
setup: function(api) {
vectorOffset = {
x: 2 * api.getPanelWidth() / 5,
y: 4 * api.getPanelHeight() / 5
};
api.setSize(1.25 * api.getPanelWidth(),api.getPanelHeight());
},
drawCube: function(api) {
var prj = p => api.project(p, vectorOffset);
var cube = [
{x:0, y:0, z:0},
{x:200,y:0, z:0},
{x:200,y:200,z:0},
{x:0, y:200,z:0},
{x:0, y:0, z:200},
{x:200,y:0, z:200},
{x:200,y:200,z:200},
{x:0, y:200,z:200}
].map(p => prj(p));
// "most of the cube"
api.setColor("grey");
api.drawLine(cube[1], cube[2]);
api.drawLine(cube[2], cube[3]);
api.drawLine(cube[1], cube[5]);
api.drawLine(cube[2], cube[6]);
api.drawLine(cube[3], cube[7]);
api.drawLine(cube[4], cube[5]);
api.drawLine(cube[5], cube[6]);
api.drawLine(cube[6], cube[7]);
api.drawLine(cube[7], cube[4]);
// x axis
api.setColor("blue");
api.drawLine(cube[0], cube[1]);
// y axis
api.setColor("red");
api.drawLine(cube[3], cube[0]);
// z axis
api.setColor("green");
api.drawLine(cube[0], cube[4]);
},
drawCurve(api, curvepoints, project) {
var prj = p => api.project(p, vectorOffset),
curve2d = curvepoints.map(p => prj(p)),
points;
if (project) {
// projections
api.setColor(`rgba(0,0,0,${SHADOW_ALPHA})`);
api.drawCurve({ points: curvepoints.map(p => api.projectXY(p, vectorOffset)) });
api.drawCurve({ points: curvepoints.map(p => api.projectYZ(p, vectorOffset)) });
api.drawCurve({ points: curvepoints.map(p => api.projectXZ(p, vectorOffset)) });
}
// control lines
api.setColor("#333");
api.drawLine(curve2d[0], curve2d[1]);
api.drawCircle(curve2d[1], 3);
api.drawCircle(curve2d[2], 3);
api.drawLine(curve2d[2], curve2d[3]);
// main curve
api.setColor("black");
api.drawCircle(curve2d[0], 3);
api.drawCircle(curve2d[3], 3);
var curve = new api.Bezier(curve2d);
api.drawCurve({ points: curve2d });
},
getFrenetVectors: function(t, curve, d1curve) {
var o = curve.get(t),
// get the normalized tangent
dt = d1curve.get(t),
// and then let's work in the change in tangent
ddt = d1curve.derivative(t),
b = normalize(vplus(dt, ddt)),
// compute the normalized axis of rotation
r = normalize(vcross(b, dt)),
// compute the normal
n = normalize(vcross(r, dt));
return { o, dt, r, n };
},
lerpVectors(t, v1, v2) {
var v = {};
['o', 'dt', 'r', 'n'].forEach(p => {
v[p] = vlerp(t, v1[p], v2[p]);
});
return v;
},
generateRMF: function(curve, d1curve) {
var frames = [], step = 0.05;
frames.push(this.getFrenetVectors(0, curve, d1curve));
for(var t0=0; t0<=1; t0+=step) {
var x0 = frames.slice(-1)[0],
t1 = t0 + step,
x1 = { o: curve.get(t1), dt: d1curve.get(t1) },
v1 = vminus(x1.o, x0.o),
c1 = vdot(v1, v1),
riL = vminus(x0.r, vscale(v1, 2/c1 * vdot(v1, x0.r))),
tiL = vminus(x0.dt, vscale(v1, 2/c1 * vdot(v1, x0.dt))),
v2 = vminus(x1.dt, tiL),
c2 = vdot(v2, v2);
x1.r = vminus(riL, vscale(v2, 2/c2 * vdot(v2, riL)));
x1.n = vcross(x1.r, x1.dt);
frames.push(x1); }
return frames;
},
getRMF: function(t, curve, d1curve) {
if (!this.rmf_LUT) {
this.rmf_LUT = this.generateRMF(curve, d1curve);
}
// find integer index
var l = this.rmf_LUT.length;
var i = t * l;
if (i != (i|0)) {
// no intenger index: interpolate values?
i = (i|0);
if (i===l-1) return this.rmf_LUT[i-1];
var j = i + 1, ti = i/l, tj = j/l;
t = (t - ti) / (tj - ti);
return this.lerpVectors(t, this.rmf_LUT[i], this.rmf_LUT[j]);
}
return this.rmf_LUT[i];
},
drawVector: function(api, from, to, len, r,g,b) {
var prj = p => api.project(p, vectorOffset);
to = normalize(to);
to = {
x: from.x + len * to.x,
y: from.y + len * to.y,
z: from.z + len * to.z
};
api.setColor(`rgba(${r},${g},${b},1)`);
// draw the actual vector
api.drawLine(prj(from), prj(to));
},
drawFrenetVectors: function(api) {
api.reset();
var prj = p => api.project(p, vectorOffset);
this.drawCube(api);
var curvepoints = [
{x:120,y:0,z:0},
{x:120,y:220,z:0},
{x:30,y:0,z:30},
{x:0,y:0,z:200}
];
this.drawCurve(api, curvepoints, SHOW_PROJECTIONS);
// let's mark t
var curve = new api.Bezier(curvepoints);
var d1curve = new api.Bezier(curve.dpoints[0]);
var t = Math.max(api.hover.x? api.hover.x / api.getPanelWidth() : 0, 0);
var mt = curve.get(t);
api.drawCircle(prj(mt), 3);
// draw the tangent, rotational axis, and normal
var vectors = this.getFrenetVectors(t, curve, d1curve);
this.drawVector(api, mt, vectors.dt, 40, 0,200,0);
this.drawVector(api, mt, vectors.r, 40, 0,0,200);
this.drawVector(api, mt, vectors.n, 40, 200,0,0);
},
drawRMFNormals: function(api) {
api.reset();
var prj = p => api.project(p, vectorOffset);
this.drawCube(api);
var curvepoints = [
{x:120,y:0,z:0},
{x:120,y:220,z:0},
{x:30,y:0,z:30},
{x:0,y:0,z:200}
];
this.drawCurve(api, curvepoints, SHOW_PROJECTIONS);
// let's mark t
var curve = new api.Bezier(curvepoints);
var d1curve = new api.Bezier(curve.dpoints[0]);
var t = Math.max(api.hover.x? api.hover.x / api.getPanelWidth() : 0, 0);
var mt = curve.get(t);
api.drawCircle(prj(mt), 3);
// draw the tangent, rotational axis, and normal
var vectors = this.getRMF(t, curve, d1curve);
this.drawVector(api, mt, vectors.dt, 40, 0,200,0);
this.drawVector(api, mt, vectors.r, 40, 0,0,200);
this.drawVector(api, mt, vectors.n, 40, 200,0,0);
}
};