I'm trying to use OpenCV with WebGL.
In OpenCV, I'm looking for a projection (a priori the projection matrix of the camera) that I get using SolvePnp.
When I display the projection obtained in OpenCV with the 'projectPoints' function, everything is perfectly stalled.
The SolvePnp function returns 3 values for the rotation and 3 values for the translation. I also get the cameraMatrix(matrix 3x3) using the values fx, fy, cx, cy (see:https://docs.opencv.org/2.4/modules/calib3d/doc/camera_calibration_and_3d_reconstruction.html#solvepnp).
Once all these values are recovered, I try to use them with WebGL. But for the moment it does not work.
In WebGL, I calculate the projectionMatrix in two different ways:
m4.perspective (28 / 180.0 * Math.PI, aspect, 0.1, 1000);
or through the following example (https://blog.noctua-software.com/opencv-opengl-projection-matrix.html)
openCVToProjectionMatrix (fx, fy, cx, cy, 0.1, 1000, wSource, hSource);
Then I create the cameraMatrix and I apply to it the values recovered since OpenCV.
I compute the ViewMatrix by doing an inverseMatrix.
Then I compute the viewProjectionMatrix by multiplying projectionMatrix by viewMatrix.
I calculate ModelMatrix by simply viewProjectionMatrix.
And I apply everything on my plan (2 triangles) with the following values:
var size = 1.0
x: -size * 0.5
y: -size * 0.5
width: size
height: size
then I add:
gl.uniformMatrix4fv (matrixLocation, false, matrixModel)
Unfortunately it does not work.
My questions :
Do I apply the matrices properly or are there any errors at this level?
Are there other parameters to consider?
Do I have to apply a translation on the camera?
Is there an order to be complied with for matrix calculations (start with scale, then rotation and finally translation)?
And finally, can someone help me?
A sample of code
// Source Dimensions(Webcam)
var wSrc = 360, hSrc = 640;
// Trigger Dimensions
var wTrigger = 602, hTrigger = 452;
// OpenCV values for projectionMatrix
var fx = 603.92035729, fy = 605.26722937;
var cx = 179.804103189, cy = 320.14721692;
// Values from SolvePnp(openCV)
var rVecs = [0.464472599644,-0.210231064819,-0.0689626255534];
var tVecs = [-0.61758832993,-0.567979295625,2.78430675542];
// -------------------------------------------------------------------------------------
var vertexShader = `
attribute vec4 a_position;
attribute vec4 a_color;
uniform mat4 u_matrix;
void main() {
// Multiply the position by the matrix.
gl_Position = u_matrix * a_position;
}
`;
// -------------------------------------------------------------------------------------
var fragmentShader = `
precision mediump float;
void main() {
gl_FragColor = vec4(1, 0, 0, 1);
}
`;
// *************************************************************************************
// *************************************************************************************
// *************************************************************************************
var m4 = {
perspective: function(fieldOfViewInRadians, aspect, near, far) {
var f = Math.tan(Math.PI * 0.5 - 0.5 * fieldOfViewInRadians);
var rangeInv = 1.0 / (near - far);
return [
f / aspect, 0, 0, 0,
0, f, 0, 0,
0, 0, (near + far) * rangeInv, -1,
0, 0, near * far * rangeInv * 2, 0
];
},
projection: function(width, height, depth) {
// Note: This matrix flips the Y axis so 0 is at the top.
return [
2 / width, 0, 0, 0,
0, -2 / height, 0, 0,
0, 0, 2 / depth, 0,
-1, 1, 0, 1,
];
},
multiply: function(a, b) {
var a00 = a[0 * 4 + 0];
var a01 = a[0 * 4 + 1];
var a02 = a[0 * 4 + 2];
var a03 = a[0 * 4 + 3];
var a10 = a[1 * 4 + 0];
var a11 = a[1 * 4 + 1];
var a12 = a[1 * 4 + 2];
var a13 = a[1 * 4 + 3];
var a20 = a[2 * 4 + 0];
var a21 = a[2 * 4 + 1];
var a22 = a[2 * 4 + 2];
var a23 = a[2 * 4 + 3];
var a30 = a[3 * 4 + 0];
var a31 = a[3 * 4 + 1];
var a32 = a[3 * 4 + 2];
var a33 = a[3 * 4 + 3];
var b00 = b[0 * 4 + 0];
var b01 = b[0 * 4 + 1];
var b02 = b[0 * 4 + 2];
var b03 = b[0 * 4 + 3];
var b10 = b[1 * 4 + 0];
var b11 = b[1 * 4 + 1];
var b12 = b[1 * 4 + 2];
var b13 = b[1 * 4 + 3];
var b20 = b[2 * 4 + 0];
var b21 = b[2 * 4 + 1];
var b22 = b[2 * 4 + 2];
var b23 = b[2 * 4 + 3];
var b30 = b[3 * 4 + 0];
var b31 = b[3 * 4 + 1];
var b32 = b[3 * 4 + 2];
var b33 = b[3 * 4 + 3];
return [
b00 * a00 + b01 * a10 + b02 * a20 + b03 * a30,
b00 * a01 + b01 * a11 + b02 * a21 + b03 * a31,
b00 * a02 + b01 * a12 + b02 * a22 + b03 * a32,
b00 * a03 + b01 * a13 + b02 * a23 + b03 * a33,
b10 * a00 + b11 * a10 + b12 * a20 + b13 * a30,
b10 * a01 + b11 * a11 + b12 * a21 + b13 * a31,
b10 * a02 + b11 * a12 + b12 * a22 + b13 * a32,
b10 * a03 + b11 * a13 + b12 * a23 + b13 * a33,
b20 * a00 + b21 * a10 + b22 * a20 + b23 * a30,
b20 * a01 + b21 * a11 + b22 * a21 + b23 * a31,
b20 * a02 + b21 * a12 + b22 * a22 + b23 * a32,
b20 * a03 + b21 * a13 + b22 * a23 + b23 * a33,
b30 * a00 + b31 * a10 + b32 * a20 + b33 * a30,
b30 * a01 + b31 * a11 + b32 * a21 + b33 * a31,
b30 * a02 + b31 * a12 + b32 * a22 + b33 * a32,
b30 * a03 + b31 * a13 + b32 * a23 + b33 * a33,
];
},
translation: function(tx, ty, tz) {
return [
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
tx, ty, tz, 1,
];
},
xRotation: function(angleInRadians) {
var c = Math.cos(angleInRadians);
var s = Math.sin(angleInRadians);
return [
1, 0, 0, 0,
0, c, s, 0,
0, -s, c, 0,
0, 0, 0, 1,
];
},
yRotation: function(angleInRadians) {
var c = Math.cos(angleInRadians);
var s = Math.sin(angleInRadians);
return [
c, 0, -s, 0,
0, 1, 0, 0,
s, 0, c, 0,
0, 0, 0, 1,
];
},
zRotation: function(angleInRadians) {
var c = Math.cos(angleInRadians);
var s = Math.sin(angleInRadians);
return [
c, s, 0, 0,
-s, c, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1,
];
},
scaling: function(sx, sy, sz) {
return [
sx, 0, 0, 0,
0, sy, 0, 0,
0, 0, sz, 0,
0, 0, 0, 1,
];
},
translate: function(m, tx, ty, tz) {
return m4.multiply(m, m4.translation(tx, ty, tz));
},
xRotate: function(m, angleInRadians) {
return m4.multiply(m, m4.xRotation(angleInRadians));
},
yRotate: function(m, angleInRadians) {
return m4.multiply(m, m4.yRotation(angleInRadians));
},
zRotate: function(m, angleInRadians) {
return m4.multiply(m, m4.zRotation(angleInRadians));
},
scale: function(m, sx, sy, sz) {
return m4.multiply(m, m4.scaling(sx, sy, sz));
},
inverse: function(m) {
var m00 = m[0 * 4 + 0];
var m01 = m[0 * 4 + 1];
var m02 = m[0 * 4 + 2];
var m03 = m[0 * 4 + 3];
var m10 = m[1 * 4 + 0];
var m11 = m[1 * 4 + 1];
var m12 = m[1 * 4 + 2];
var m13 = m[1 * 4 + 3];
var m20 = m[2 * 4 + 0];
var m21 = m[2 * 4 + 1];
var m22 = m[2 * 4 + 2];
var m23 = m[2 * 4 + 3];
var m30 = m[3 * 4 + 0];
var m31 = m[3 * 4 + 1];
var m32 = m[3 * 4 + 2];
var m33 = m[3 * 4 + 3];
var tmp_0 = m22 * m33;
var tmp_1 = m32 * m23;
var tmp_2 = m12 * m33;
var tmp_3 = m32 * m13;
var tmp_4 = m12 * m23;
var tmp_5 = m22 * m13;
var tmp_6 = m02 * m33;
var tmp_7 = m32 * m03;
var tmp_8 = m02 * m23;
var tmp_9 = m22 * m03;
var tmp_10 = m02 * m13;
var tmp_11 = m12 * m03;
var tmp_12 = m20 * m31;
var tmp_13 = m30 * m21;
var tmp_14 = m10 * m31;
var tmp_15 = m30 * m11;
var tmp_16 = m10 * m21;
var tmp_17 = m20 * m11;
var tmp_18 = m00 * m31;
var tmp_19 = m30 * m01;
var tmp_20 = m00 * m21;
var tmp_21 = m20 * m01;
var tmp_22 = m00 * m11;
var tmp_23 = m10 * m01;
var t0 = (tmp_0 * m11 + tmp_3 * m21 + tmp_4 * m31) -
(tmp_1 * m11 + tmp_2 * m21 + tmp_5 * m31);
var t1 = (tmp_1 * m01 + tmp_6 * m21 + tmp_9 * m31) -
(tmp_0 * m01 + tmp_7 * m21 + tmp_8 * m31);
var t2 = (tmp_2 * m01 + tmp_7 * m11 + tmp_10 * m31) -
(tmp_3 * m01 + tmp_6 * m11 + tmp_11 * m31);
var t3 = (tmp_5 * m01 + tmp_8 * m11 + tmp_11 * m21) -
(tmp_4 * m01 + tmp_9 * m11 + tmp_10 * m21);
var d = 1.0 / (m00 * t0 + m10 * t1 + m20 * t2 + m30 * t3);
return [
d * t0,
d * t1,
d * t2,
d * t3,
d * ((tmp_1 * m10 + tmp_2 * m20 + tmp_5 * m30) -
(tmp_0 * m10 + tmp_3 * m20 + tmp_4 * m30)),
d * ((tmp_0 * m00 + tmp_7 * m20 + tmp_8 * m30) -
(tmp_1 * m00 + tmp_6 * m20 + tmp_9 * m30)),
d * ((tmp_3 * m00 + tmp_6 * m10 + tmp_11 * m30) -
(tmp_2 * m00 + tmp_7 * m10 + tmp_10 * m30)),
d * ((tmp_4 * m00 + tmp_9 * m10 + tmp_10 * m20) -
(tmp_5 * m00 + tmp_8 * m10 + tmp_11 * m20)),
d * ((tmp_12 * m13 + tmp_15 * m23 + tmp_16 * m33) -
(tmp_13 * m13 + tmp_14 * m23 + tmp_17 * m33)),
d * ((tmp_13 * m03 + tmp_18 * m23 + tmp_21 * m33) -
(tmp_12 * m03 + tmp_19 * m23 + tmp_20 * m33)),
d * ((tmp_14 * m03 + tmp_19 * m13 + tmp_22 * m33) -
(tmp_15 * m03 + tmp_18 * m13 + tmp_23 * m33)),
d * ((tmp_17 * m03 + tmp_20 * m13 + tmp_23 * m23) -
(tmp_16 * m03 + tmp_21 * m13 + tmp_22 * m23)),
d * ((tmp_14 * m22 + tmp_17 * m32 + tmp_13 * m12) -
(tmp_16 * m32 + tmp_12 * m12 + tmp_15 * m22)),
d * ((tmp_20 * m32 + tmp_12 * m02 + tmp_19 * m22) -
(tmp_18 * m22 + tmp_21 * m32 + tmp_13 * m02)),
d * ((tmp_18 * m12 + tmp_23 * m32 + tmp_15 * m02) -
(tmp_22 * m32 + tmp_14 * m02 + tmp_19 * m12)),
d * ((tmp_22 * m22 + tmp_16 * m02 + tmp_21 * m12) -
(tmp_20 * m12 + tmp_23 * m22 + tmp_17 * m02))
];
},
vectorMultiply: function(v, m) {
var dst = [];
for (var i = 0; i < 4; ++i) {
dst[i] = 0.0;
for (var j = 0; j < 4; ++j)
dst[i] += v[j] * m[j * 4 + i];
}
return dst;
},
};
// *************************************************************************************
// *************************************************************************************
// *************************************************************************************
initGL();
// -------------------------------------------------------------------------------------
function initializeWebGL(canvasName) {
var canvas = document.getElementById(canvasName);
var gl = null;
try {
gl = canvas.getContext("webgl");
}
catch (error) {
console.log("Error getContext WebGL", error);
}
if (!gl) {
throw new Error("Could not get WebGL context!");
}
return gl;
}
function createShader(gl, shaderSource, shaderType) {
var shader = gl.createShader(shaderType);
gl.shaderSource(shader, shaderSource);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
var infoLog = gl.getShaderInfoLog(shader);
gl.deleteShader(shader);
throw new Error("An error occurred compiling the shader: " + infoLog);
}
else {
return shader;
}
}
function createGlslProgram(gl, vertexSource, fragmentSource) {
var program = gl.createProgram();
gl.attachShader(program, createShader(gl, vertexSource, gl.VERTEX_SHADER));
gl.attachShader(program, createShader(gl, fragmentSource, gl.FRAGMENT_SHADER));
gl.linkProgram(program);
gl.validateProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
var infoLog = gl.getProgramInfoLog(program);
gl.deleteProgram(program);
throw new Error("An error occurred linking the program: " + infoLog);
}
else {
return program;
}
}
// -------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------
function initGL() {
var gl = initializeWebGL("canvasGL");
var program = createGlslProgram(gl, vertexShader, fragmentShader);
// ------------------------------------------------------------------------------
resizeCanvasToDisplaySize(gl.canvas, window.devicePixelRatio);
// ------------------------------------------------------------------------------
var positionAttributeLocation = gl.getAttribLocation(program, "a_position");
var matrixLocation = gl.getUniformLocation(program, "u_matrix");
var positionBuffer = gl.createBuffer();
// ------------------------------------------------------------------------------
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.clearColor(0, 0, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT);
// ------------------------------------------------------------------------------
gl.useProgram(program);
// ------------------------------------------------------------------------------
gl.enableVertexAttribArray(positionAttributeLocation);
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
// ------------------------------------------------------------------------------
initFlatPlane(gl, positionAttributeLocation, matrixLocation);
}
function initFlatPlane(gl, positionAttributeLocation, matrixLocation){
var size = 3;
var type = gl.FLOAT;
var normalize = false;
var stride = 0;
var offset = 0;
gl.vertexAttribPointer(positionAttributeLocation, size, type, normalize, stride, offset)
// ------------------------------------------------------------------------------
// projectionMatrix
var matOpenCV = openCVCameraMatrixToProjectionMatrix(fx, fy, cx, cy, 1000, 0.1, wSrc, hSrc);
var projectionMatrix = matOpenCV[0].concat(matOpenCV[1]).concat(matOpenCV[2]).concat(matOpenCV[3]);
console.log("projectionMatrix :", projectionMatrix);
// ------------------------------------------------------------------------------
// Compute a matrix for the camera
var cameraMatrix = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
cameraMatrix = m4.scale(cameraMatrix, 1, wTrigger/hTrigger, 1);
cameraMatrix = m4.xRotate(cameraMatrix, rVecs[0]);
cameraMatrix = m4.yRotate(cameraMatrix, rVecs[1]);
cameraMatrix = m4.zRotate(cameraMatrix, rVecs[2]);
cameraMatrix = m4.translate(cameraMatrix, tVecs[0], tVecs[1], tVecs[2]);
console.log("cameraMatrix", cameraMatrix);
// ------------------------------------------------------------------------------
// Make a view matrix from the camera matrix
var viewMatrix = m4.inverse(cameraMatrix);
console.log("viewMatrix", viewMatrix);
// ------------------------------------------------------------------------------
// Compute a view projection matrix
var viewProjectionMatrix = m4.multiply(projectionMatrix, viewMatrix);
console.log("viewProjectionMatrix", viewProjectionMatrix);
// ------------------------------------------------------------------------------
// modelMatrix
var modelMatrix = m4.translate(viewProjectionMatrix, 0.0, 0.0, 0.0);
console.log("modelMatrix", modelMatrix);
// ------------------------------------------------------------------------------
var size = 1.0
setRectangle(gl, -size * .5, -size * .5, size, size);
// Let modelMatrix
gl.uniformMatrix4fv(matrixLocation, false, modelMatrix);
// Draw
var primitiveType = gl.TRIANGLES;
var offset = 0;
var count = 6;
gl.drawArrays(primitiveType, offset, count);
}
function setRectangle(gl, x, y, width, height) {
var x1 = x;
var x2 = x + width;
var y1 = y;
var y2 = y + height;
var z = 0;
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
x1, y1, z,
x2, y1, z,
x1, y2, z,
x1, y2, z,
x2, y1, z,
x2, y2, z,
]), gl.STATIC_DRAW);
}
function resizeCanvasToDisplaySize(canvas, multiplier) {
multiplier = multiplier || 1;
var width = canvas.clientWidth * multiplier | 0;
var height = canvas.clientHeight * multiplier | 0;
if (canvas.width !== width || canvas.height !== height) {
canvas.width = width;
canvas.height = height;
return true;
}
return false;
}
function openCVCameraMatrixToProjectionMatrix(fx, fy, cx, cy, zfar, znear, width, height){
var m = [
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
];
m[0][0] = 2.0 * fx / width;
m[0][1] = 0.0;
m[0][2] = 0.0;
m[0][3] = 0.0;
m[1][0] = 0.0;
m[1][1] = -2.0 * fy / height;
m[1][2] = 0.0;
m[1][3] = 0.0;
m[2][0] = 1.0 - 2.0 * cx / width;
m[2][1] = 2.0 * cy / height - 1.0;
m[2][2] = (zfar + znear) / (znear - zfar);
m[2][3] = -1.0;
m[3][0] = 0.0;
m[3][1] = 0.0;
m[3][2] = 2.0 * zfar * znear / (znear - zfar);
m[3][3] = 0.0;
return m;
}
body {
margin:0;
padding:0;
background-color:lightgray;
}
#canvasGL, img {
position:absolute;
width:360px;
height:640px;
}
img {
opacity: 0.2;
}
#canvasBackground {
border: solid 1px darkGray;
}
<body>
<img src="http://www.indelebil.fr/tmp/preview_python_02.jpg" />
<canvas id="canvasGL"></canvas>
</body>
The result
Thanks for your help !!!!
When I drag the svg(red) to another position on the screen, the cursor mouse loses the original angular position (lag) and points to blank area ... I used a var dx and dy in "var point e.clientx" to fix it, unsuccessfully ... any suggestions?
code: http://jsfiddle.net/rebecavascon/evgLhdz3/2/
show: http://jsfiddle.net/rebecavascon/evgLhdz3/2/show/
function updateSVG(e) {
if (follow) {
var centerPoint = new Point(center[0].getAttribute("cx"), center[0].getAttribute("cy"));
var point = new Point(e.clientX, e.clientY);
var angle = Math.round(100 * getAngleFromPoint(point, centerPoint)) / 100;
var distance = Math.round(getDistance(point, centerPoint));
var d = "M " + centerPoint.X + " " + centerPoint.Y + " L " + point.X + " " + point.Y;
path.attr("d", d);
txt.attr("x", point.X);
txt.attr("y", point.Y);
txt.html(distance + arrows + " (" + angle + degree + ")");
txt.attr("transform", "rotate(" + angle + " " + point.X + " " + point.Y + ")");
dynamic.attr("r", distance);
}
fitSVG();
}
Creating an offset worked for my testing.
Code: http://jsfiddle.net/Twisty/8zx8p2wf/19/
Working: http://jsfiddle.net/Twisty/8zx8p2wf/19/show/
Added Function getCenter()
function getCenter(target) {
var b, x, y, w, h, cx, cy;
b = target[0].getBoundingClientRect();
console.log(target, b);
x = b.x;
y = b.y;
w = b.width;
h = b.height;
cx = x + (w / 2);
cy = y + (h / 2);
console.log(x, y, w, h, cx, cy);
return {
X: cx,
Y: cy
};
}
This gets the true center of an SVG Object. Looks like the cx and cy attributes do not get updated.
Updated function updateSVG()
function updateSVG(e) {
if (follow) {
var centerPoint = getCenter(center);
var point = new Point(e.clientX, e.clientY);
var angle = Math.round(100 * getAngleFromPoint(point, centerPoint)) / 100;
var distance = Math.round(getDistance(point, centerPoint));
var od = {
p: {
X: point.X - offset.X,
Y: point.Y - offset.Y
},
cp: {
X: centerPoint.X - offset.X,
Y: centerPoint.Y - offset.Y
}
};
var d = "M" + od.p.X + "," + od.p.Y + " L" + od.cp.X + "," + od.cp.Y;
path.attr("d", d);
txt.attr("x", point.X);
txt.attr("y", point.Y);
txt.html(distance + arrows + " (" + angle + degree + ")");
txt.attr("transform", "rotate(" + angle + " " + point.X + " " + point.Y + ")");
dynamic.attr("r", distance);
}
fitSVG();
}
This uses a new offset constant variable and the correct center points.
JavaScript
$(function() {
var center = $("#center"),
dynamic = $("#dynamic"),
path = $("#deg"),
svg = $("svg"),
txt = $("#txt"),
svgNS = svg[0].namespaceURI,
degree = String.fromCharCode(176),
arrows = String.fromCharCode(845),
follow = true,
startPos,
endPos,
offset = {
X: 0,
Y: 0
};
function Point(x, y) {
return {
"X": x,
"Y": y
};
}
function getCenter(target) {
var b, x, y, w, h, cx, cy;
b = target[0].getBoundingClientRect();
console.log(target, b);
x = b.x;
y = b.y;
w = b.width;
h = b.height;
cx = x + (w / 2);
cy = y + (h / 2);
console.log(x, y, w, h, cx, cy);
return {
X: cx,
Y: cy
};
}
// Credits goes to Stackoverflow: http://stackoverflow.com/a/14413632
function getAngleFromPoint(point, centerPoint) {
var dy = (point.Y - centerPoint.Y),
dx = (point.X - centerPoint.X);
var theta = Math.atan2(dy, dx);
var angle = (((theta * 180) / Math.PI)) % 360;
angle = (angle < 0) ? 360 + angle : angle;
return angle;
}
// Credits goes to http://snipplr.com/view/47207/
function getDistance(point1, point2) {
var xs = 0;
var ys = 0;
xs = point2.X - point1.X;
xs = xs * xs;
ys = point2.Y - point1.Y;
ys = ys * ys;
return Math.sqrt(xs + ys);
}
function fitSVG() {
var width = window.innerWidth,
height = window.innerHeight;
svg.width(width);
svg.height(height);
}
function updateSVG(e) {
if (follow) {
//var centerPoint = new Point(center[0].getAttribute("cx"), center[0].getAttribute("cy"));
var centerPoint = getCenter(center);
var point = new Point(e.clientX, e.clientY);
var angle = Math.round(100 * getAngleFromPoint(point, centerPoint)) / 100;
var distance = Math.round(getDistance(point, centerPoint));
var od = {
p: {
X: point.X - offset.X,
Y: point.Y - offset.Y
},
cp: {
X: centerPoint.X - offset.X,
Y: centerPoint.Y - offset.Y
}
};
var d = "M" + od.p.X + "," + od.p.Y + " L" + od.cp.X + "," + od.cp.Y;
$("#mouse").html(e.clientX + "," + e.clientY);
$("#svgPos").html(svg.position().left + "," + svg.position().top);
$("#offset").html(offset.X + "," + offset.Y);
$("#centerPoint").html(centerPoint.X + "," + centerPoint.Y);
$("#point").html(point.X + "," + point.Y);
$("#path").html(d);
$("#angle").html(angle);
$("#distance").html(distance);
path.attr("d", d);
txt.attr("x", point.X);
txt.attr("y", point.Y);
txt.html(distance + arrows + " (" + angle + degree + ")");
txt.attr("transform", "rotate(" + angle + " " + point.X + " " + point.Y + ")");
dynamic.attr("r", distance);
}
fitSVG();
}
grid_size = 10;
svg
.mousemove(updateSVG)
.click(function() {
follow = !follow;
return true;
});
$(".img").draggable({
handle: "svg",
classes: {
"ui-draggable-dragging": "opac"
},
cursor: "grab",
grid: [grid_size, grid_size],
start: function(e, ui) {
$(this).find(".text").hide();
follow = false;
startPos = ui.position;
},
stop: function() {
follow = true;
endPos = svg.position();
offset.X = endPos.left;
offset.Y = endPos.top;
}
});
});
Through testing, I adjusted the draggable a little bit, such that, the div.img wrapper is the draggable and the svg inside is the handle. I'm not sure if there is a benefit here, yet I didn't want it to go unnoticed.
Ive ported over some c code that renders a sphere in opengl for a webgl/typescript project I'm working on, however its not rendering correctly. I've compared the indices and vertices between the c and ts versions and they appear to match. The code is as follows:
constructor(ctx: WebGLRenderingContext, stacks:number,
slices:number, scale: number){
var vertices: number[] = [];
var normals: number[] = [];
var indices: number[] = [];
var ii: number;
var jj: number;
var v: number;
var u: number;
normals.push(0, 0, 1);
vertices.push(0, 0, scale);
for (ii = 0; ii < slices; ++ii) {
indices.push(0);
indices.push(ii + 1);
}
indices.push(0);
indices.push(1);
for (ii = 1; ii < stacks; ++ii) {
v = ii / stacks;
for (jj = 0; jj < slices; ++jj) {
u = jj / slices;
normals.push.apply(normals, this.shapeNormal(u, v));
vertices.push.apply(vertices, this.shapeVertex(scale, u, v));
indices.push((ii - 1) * slices + (jj + 1));
var index_offset: number = ((ii + 1) === stacks) ? 0 : jj;
var second: number = ii * slices + (index_offset + 1);
//console.log("Offset: " + String(index_offset) + " Value: " + String(second));
indices.push(second);
}
indices.push((ii - 1) * slices + 1);
indices.push(ii * slices + 1);
}
normals.push(0, 0, -1);
vertices.push(0, 0, -scale);
//console.log("Theoretical vertices: " + String(3 * (2 + slices * (stacks - 1))));
//initialise vbos
console.log("Vertices: " + String(vertices.length / 3));
for(var l = 0; l < vertices.length; l += 3)
console.log(vertices[l].toFixed(6) + " " + vertices[l+1].toFixed(6) + " " + vertices[l+2].toFixed(6));
this.vertices = new VertexBufferObject(ctx, 3, vertices.length / 3);
//console.log("Normals: " + String(normals.length));
this.normals = new VertexBufferObject(ctx, 3, normals.length / 3);
console.log("Indices: " + String(indices.length) + " " + indices.toString());
this.indices = new VertexBufferObject(ctx, 1, indices.length);
//populate vbo
ctx.enableVertexAttribArray(0);
ctx.bindBuffer(ctx.ARRAY_BUFFER, this.vertices.buffer);
ctx.bufferData(ctx.ARRAY_BUFFER, new Float32Array(vertices), ctx.STATIC_DRAW);
ctx.enableVertexAttribArray(1);
ctx.bindBuffer(ctx.ARRAY_BUFFER, this.normals.buffer);
ctx.bufferData(ctx.ARRAY_BUFFER, new Float32Array(normals), ctx.STATIC_DRAW);
ctx.bindBuffer(ctx.ELEMENT_ARRAY_BUFFER, this.indices.buffer);
ctx.bufferData(ctx.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices),
ctx.STATIC_DRAW);
ctx.bindBuffer(ctx.ARRAY_BUFFER, null);
ctx.bindBuffer(ctx.ELEMENT_ARRAY_BUFFER, null);
ctx.disableVertexAttribArray(0);
ctx.disableVertexAttribArray(1);
this.ctx = ctx;
}
private shapeVertex(r: number, u: number, v: number): number[] {
/* Use maths rather than physics spherical coordinate convention */
var theta: number = u * 2.0 * Math.PI;
var phi: number = v * Math.PI;
var vert: number[] = [
r * Math.cos(theta) * Math.sin(phi),
r * Math.sin(theta) * Math.sin(phi),
r * Math.cos(phi)
];
return vert;
}
private shapeNormal(u: number, v: number): number[] {
/* Use maths rather than physics spherical coordinate convention */
var theta: number = u * 2.0 * Math.PI;
var phi: number = v * Math.PI;
var norm: number[] = [
Math.cos(theta) * Math.sin(phi),
Math.sin(theta) * Math.sin(phi),
Math.cos(phi)
];
var mag: number = Math.sqrt(norm[0] * norm[0] + norm[1] * norm[1] + norm[2] * norm[2]);
norm[0] /= mag;
norm[1] /= mag;
norm[2] /= mag;
return norm;
}
public draw(shaderProgram: ShaderProgram): void {
//bind and draw vbo's
this.ctx.enableVertexAttribArray(0);
this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, this.vertices.buffer);
this.ctx.vertexAttribPointer(shaderProgram.attributes.position,
this.vertices.itemSize, this.ctx.FLOAT, false, 0, 0);
this.ctx.enableVertexAttribArray(1);
this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, this.normals.buffer);
this.ctx.vertexAttribPointer(shaderProgram.attributes.normal,
this.normals.itemSize, this.ctx.FLOAT, false, 0, 0);
this.ctx.bindBuffer(this.ctx.ELEMENT_ARRAY_BUFFER, this.indices.buffer);
this.ctx.drawElements(this.ctx.TRIANGLES, this.indices.numItems,
this.ctx.UNSIGNED_SHORT, 0);
this.ctx.bindBuffer(this.ctx.ELEMENT_ARRAY_BUFFER, null);
this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, null);
this.ctx.disableVertexAttribArray(0);
this.ctx.disableVertexAttribArray(1);
}
and a screenshot of the result:
Broken Sphere
Thank you in advance
As TypeScript is just a supersed of Javascript, your problem is probably related to how Javascript handle your code computations.
I'm not sure about your code as you didn't provide the original source.
Assuming your code is correct, you may encounter a floating point approximation error.