How to update an objects rotational pivot after change in position - javascript

When i use the "w" key to move my object and then pres the "x" key to rotate it, my object rotates around the world origin rather than its own center. How do I update the objects pivot after the move? I've seen a few questions and answers on this where its stated to use geometry.center(), however, this doesn't work for me.
var tMatrix = new THREE.Matrix4()
var radian = 5*Math.PI/180
function keyDown(event){
if(event.key == 'x'){
r5 = Math.cos(radian);
r6 = -Math.sin(radian);
r8 = Math.sin(radian);
r9 = Math.cos(radian);
tMatrix.set( 1, 0, 0, 0,
0, r5, r6, 0,
0, r8, r9, 0,
0, 0, 0, 1 );
mesh.applyMatrix4(tMatrix);
}
if(event.key == 'w'){
tMatrix.set( 1, 0, 0, 0,
0, 1, 0, 1,
0, 0, 1, 0,
0, 0, 0, 1 );
mesh.applyMatrix4(tMatrix);
}
}
var pivot = new THREE.Object3D();
let loader = new THREE.STLLoader();
var material = new THREE.MeshLambertMaterial({color: 0x818181});
loader.load('STL/test.stl', function (geometry) {
mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
});
window.addEventListener("keypress", keyDown);

Okay, I solved the problem by just getting the position of the object before I rotate it, and then set the position to its original after the new matrix has been set.
if(event.key == 'x'){
x = mesh.position.x;
y = mesh.position.y;
z = mesh.position.z;
r5 = Math.cos(radian);
r6 = -Math.sin(radian);
r8 = Math.sin(radian);
r9 = Math.cos(radian);
tMatrix.set( 1, 0, 0, 0,
0, r5, r6, 0,
0, r8, r9, 0,
0, 0, 0, 1 );
mesh.applyMatrix4(tMatrix);
mesh.position.set(x,y,z);
}

Related

How to fix my rotation from speeding up using Matrix4 in three.js

I want to be able to rotate my object when I press the "x" key using a Matrix4. My code below works however, the longer I hold the "x" key, the object starts to rotate much faster over time then to start. How can I fix this.
var r1 = 1, r5 = 1, r9 = 1, r2 = 0, r3 = 0, r4 = 0, r6 = 0, r7 = 0, r8 = 0;
var Rx = 0, Ry = 0, Rz = 0;
var rotation = new THREE.Matrix4();
function keyDown(event){
if(event.key == 'x'){
mesh.matrixAutoUpdate = false;
Rx += Math.PI/180 //1 degree per key press
r5 = Math.cos(Rx);
r6 = -Math.sin(Rx);
r8 = Math.sin(Rx);
r9 = Math.cos(Rx);
rotation.set( r1, r2, r3, 0,
r4, r5, r6, 0,
r7, r8, r9, 0,
0, 0, 0, 1 );
mesh.applyMatrix4(rotation);
}
if(event.key == 'y'){
}
if(event.key == 'z'){
}
}
let loader = new THREE.STLLoader();
var material = new THREE.MeshLambertMaterial({color: 0x818181});
loader.load('STL/test.stl', function (geometry) {
mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
});
window.addEventListener("keypress", keyDown);
.applyMatrix4 does not set a matrix, it concatenates the new transformation to the current transformation of the mesh. Since Rx is incremented, the rotation wich is add increase and the rotation speeds up. Don't increment Rx, append the same transformation (of 1°) per key press, to solve the issue:
Rx += Math.PI/180
Rx = Math.PI/180 //1 degree per key press
r5 = Math.cos(Rx);
r6 = -Math.sin(Rx);
r8 = Math.sin(Rx);
r9 = Math.cos(Rx);
rotation.set( r1, r2, r3, 0,
r4, r5, r6, 0,
r7, r8, r9, 0,
0, 0, 0, 1 );
mesh.applyMatrix4(rotation);

Javascript TileMap not re-drawing in Gameloop

I set-up a simple canvas tile map with one player object that should move in keydown. So far it is working as I am clearing the canvas in the beginning on every gameLoop, however I noticed that the tile map is drawn only the first time and after the clearRect of the canvas, it just disappears. Can you please help?
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Canvas Tile Map</title>
<style>
#canvas {
border: 1px solid black;
}
</style>
</head>
<body>
<canvas id="canvas" height="400px" width="500px"></canvas>
<script>
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var mapArray = [
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0],
[0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1],
[0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0],
[0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0]
];
//-----------------------------------------------------------------------
//-----------------------------------------------------------------------
// DRAW PLAYER
var player = new Object();
player.y = canvas.height / 2 - 40;
player.x = canvas.width / 2 - 40;
player.Width = 80;
player.Height = 80;
player_image = new Image();
player_image.src = 'https://sarahkerrigan.biz/wpmtest/1/images/horseright1.png';
function drawPlayer() { // drawing the player
context.beginPath();
context.drawImage(player_image, player.x, player.y, player.Width, player.Height);
context.closePath();
}
//-----------------------------------------------------------------------
//-----------------------------------------------------------------------
//SOME VARS
var updateX = (player.x - 210); // Starting point of canvas X
var updateY = (player.y - 160); // Starting point of canvas Y
var posX = updateX;
var posY = updateY;
//-----------------------------------------------------------------------
//-----------------------------------------------------------------------
//DRAW THE MAP
function drawMap() {
var posY = 0;
var grass = new Image();
var stone = new Image();
grass.src = 'https://sarahkerrigan.biz/wpmtest/1/images/tile/grass.jpeg';
stone.src = 'https://sarahkerrigan.biz/wpmtest/1/images/tile/sand.jpeg';
grass.onload = function() {
stone.onload = function() {
for (var i = 0; i < mapArray.length; i++) {
for (var j = 0; j < mapArray[i].length; j++) {
if (mapArray[i][j] == 0) {
context.drawImage(grass, posX, posY, 32, 32);
}
if (mapArray[i][j] == 1) {
context.drawImage(stone, posX, posY, 32, 32);
}
posX += 32;
}
posY += 32;
posX = updateX;
drawPlayer();
}
}
}
}
//-----------------------------------------------------------------------
//------------------------------------------------------------------------------------------------------
// PLAYER MOVEMENT
document.onkeydown = function(e) {
var keyCode = e.keyCode;
if (keyCode == 39) {
player.x = player.x + 10; // right is pressed
}
if (keyCode == 37) {
player.x = player.x - 10; // left is pressed
}
if (keyCode == 38) {
player.y = player.y - 10; // up is pressed
}
if (keyCode == 40) {
player.y = player.y + 10; // down is pressed
}
}
//-----------------------------------------------------------------------
//------------------------------------------------------------------------------------------------------
// THE GAME LOOP
function gameLoop() {
drawMap();
setInterval(gameLoop, 100); // 100 milisec to draw next frame
}
//-----------------------------------------------------------------------
//------------------------------------------------------------------------------------------------------
gameLoop(); // start the gameLoop
</script>
</body>
</html>

Calculate percentage from an array

I'm trying to fill a jqPlot Chart with percentage values for a stacked bar chart. I get the data via mysql and COUNT. For example, if I have 4 categories and 12 months, I am able to produce:
var s1 = [0, 2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, ];
var s2 = [0, 5, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, ];
var s3 = [0, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, ];
var s4 = [0, 3, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, ];
Which produces a stacked bar chart with numbers, every number of each variable is a months value.
Now I want to show a stacked bar chart where the values of each month are percentages. I must somehow be able to make a percentage calculation with the values of the array. For example: add all values from position two (February) (100/(2+5+3+3)) and then multiply with positon two.
I am nowhere near a solution.
EDIT: Well, thanks for the fast answers.
I will try to explain better. I get the data from the 'MySQL' query, then to a PHP array, then convert it to a string to paste it to the JavaScript for plotting:
Try a for loop to calculate the percent you wanted to get and put it inside an array again
$(document).ready(function(){
var s1 = [0, 2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, ];
var s2 = [0, 5, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, ];
var s3 = [0, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, ];
var s4 = [0, 3, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, ];
var s5 = new Array();
for (var i = 0; i < s1.length; i++){
//Or use your own formula on getting the value you wanted.
s5.push(100/(s1[i]+s2[i]+s3[i]+s4[i]);
}
// Can specify a custom tick Array.
// Ticks should match up one for each y value (category) in the series.
var ticks = ['January','February','March','April','May','June','July','August','September','October','November','December'];
var plot1 = $.jqplot('chart1', [s1, s2, s3, s4, s5], {
// The "seriesDefaults" option is an options object that will
// be applied to all series in the chart.
seriesDefaults:{
renderer:$.jqplot.BarRenderer,
rendererOptions: {fillToZero: true}
},
// Custom labels for the series are specified with the "label"
// option on the series option. Here a series option object
// is specified for each series.
series:[
{label:'Hotel'},
{label:'Event Regristration'},
{label:'Airfare'}
],
// Show the legend and put it outside the grid, but inside the
// plot container, shrinking the grid to accomodate the legend.
// A value of "outside" would not shrink the grid and allow
// the legend to overflow the container.
legend: {
show: true,
placement: 'outsideGrid'
},
axes: {
// Use a category axis on the x axis and use our custom ticks.
xaxis: {
renderer: $.jqplot.CategoryAxisRenderer,
ticks: ticks
},
// Pad the y axis just a little so bars can get close to, but
// not touch, the grid boundaries. 1.2 is the default padding.
yaxis: {
pad: 1.05,
tickOptions: {formatString: '$%d'}
}
}
});
});
<script>
/* are there to be more than 4 arrays? */
var s1 = [0, 2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, ];
var s2 = [0, 5, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, ];
var s3 = [0, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, ];
var s4 = [0, 3, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, ];
/* add new arrays to this array */
var arrs=[s1,s2,s3,s4];
var pc=[];
for( var i=0; i < arrs[0].length; i++ ){
var a=0;
for( var j=0; j < arrs.length; j++ ){
if( !isNaN( arrs[ j ][ i ] ) ) a+=arrs[ j ][ i ];
}
console.log( 'i=%d, j=%d, a=%d', i, j, a );
/* confused by the `then multiply with positon two` */
pc.push( a > 0 ? Math.round( 100 / a ) : 0 );
}
alert( pc );
</script>
If I understood correctly, it will work in your case :
function getPercent(array){
var return_array = [];
var total_sum = 0; // total sum of data
for(var i = 0; i < array.length; i++){
total_sum += array[i].reduce(function(pv, cv) { return pv + cv; }, 0);
}
for( var i=0; i < array[0].length; i++ ){
var sum = 0; // month sum
for( var j=0; j < array.length; j++ ){
sum += array[j][i];
}
return_array[i] = sum*100/total_sum; // percent calculation of the month
}
return return_array;
}
var s1 = [0, 2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, ];
var s2 = [0, 5, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, ];
var s3 = [0, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, ];
var s4 = [0, 3, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, ];
var data=[s1,s2,s3,s4];
var percent = getPercent(data);
console.log(percent);
And results are :
[0, 61.904761904761905, 4.761904761904762, 4.761904761904762, 19.047619047619047, 9.523809523809524, 0, 0, 0, 0, 0, 0]

Setting three.js projection matrix direction

I read following codes writing for webgl, and I want rewrite it using three.js. But I could not find any way to do so, please help me.
pMatrix = mat4.create();
mat4.perspective(pMatrix,1.01,gl.drawingBufferWidth/gl.drawingBufferHeight, 10.0, 300000.0);
var eciMat = [1, 0, 0, 0,
0, 0, -1, 0,
0, 1, 0, 0,
0, 0, 0, 1
];
mat4.mul(pMatrix, pMatrix, eciMat );
Matrix classes are pretty sophisticated in Three.js, look at the docs here: http://threejs.org/docs/#Reference/Math/Matrix4
Here's how I would write it in Three.js
// Arguments are fov, aspect, near, far
var perspectiveMatrix = new THREE.Matrix4().makePerspective(30, 1, 0.01, 20);
var eciMatrix = new THREE.Matrix4();
eciMatrix.set(1, 0, 0, 0,
0, 0, -1, 0,
0, 1, 0, 0,
0, 0, 0, 1);
var resultMatrix = new THREE.Matrix4();
resultMatrix.multiply(eciMatrix);
resultMatrix.multiply(perspectiveMatrix);
resultMatrix.multiply(perspectiveMatrix);
console.log(resultMatrix);

webgl not using the entire canvas space

I have a spinning 3D letter "F". For some reason, the "F" is not drawn in the top 20% of the canvas. See this jsFiddle: http://jsfiddle.net/c948jos0/.
The render function:
var rX = 0;
var rY = 0;
gl.enable(gl.CULL_FACE);
gl.enable(gl.DEPTH_TEST);
gl.canvas.width = 400;
gl.canvas.height = 200;
gl.clearColor(0.5,0.5,0.5,1.0);
function render(){
rX += Math.PI/64;
rY += Math.PI/128;
var projection = make2DProjection(gl.canvas.clientWidth,gl.canvas.clientHeight,400);
var translation = makeTranslation(200,50,0);
var rotX = makeXRotation(rX);
var rotY = makeYRotation(rY);
var mat = matrixMultiply(rotX,rotY);
mat = matrixMultiply(mat,translation);
mat = matrixMultiply(mat,projection);
uniforms["u_matrix"](new Float32Array(mat));
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLES,0, 16*6);
setTimeout(render,33);
}
render();
The matrices:
function makeTranslation(tx, ty, tz) {
return [
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
tx, ty, tz, 1
];
}
function makeXRotation(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
];
}
function makeYRotation(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
];
}
function make2DProjection(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
];
}
I have been following http://webglfundamentals.org/webgl/lessons/webgl-3d-orthographic.html so I figure I must have changed something I shouldn't have or made a typo. The problem is I cant figure out what. I think its the make2DProjection matrix not setting the origin to the top left corner of canvas, but the same code is working in the tutorial so I dont know.
The canvas element is initialized with 300x150 dimensions and so is your viewport.
When changing the canvas size after you acquired the webgl context you also need to set the viewport using
gl.viewport(x, y, width, height)
I've adjusted your fiddle and added a second canvas with an outline so you can see the difference.
Unless you have a specific need to only cover part of the canvas a recommended way to do this is.
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);

Categories

Resources