I looked at many examples but so far nothing worked. I want the circle to rotate on mousemove and it is rotating centered, so no problems so far. But, it does a 180 jump when I pass half of the stage. So everything is fine till I pass the half of the stage, clearly I'm missing something. Math.atan2 gives me an error: the circle jumps to the (0,0) of the stage.
Please help I really need this badly.
Thanks in advance!
new Kinetic.Tween({
node: cGrad1,
x: stage.getWidth()/2,
y: stage.getHeight()/2,
duration: 1,
opacity:1,
easing: Kinetic.Easings.EaseInOut
}).play();
clickO.on('mousemove', function() {
for(var n = 0; n < 16; n++) {
var shape = cGradlayer1.getChildren()[n];
var stage = shape.getStage();
var mousePos = stage.getMousePosition();
var x = mousePos.x - shape.getPosition().x;
var y = mousePos.y -shape.getPosition().y ;
var degree = (Math.PI)+(Math.atan(y/x));
shape.setAttrs({
rotation: degree
})
cGradlayer1.draw()
}
})
Well this is what I came up with, and hopefully it's close to what you were looking for: jsfiddle
Basically, to calculate the angle you want to rotate to, we need to store two points:
The origin of the shape (centre coordinate)
The coordinate of the mouse click
Once you have that, you can calculate the angle between the two points with a little bit of trigonometry (Sorry if I am not accurate here, Trigonometry is not my strong suit). Calculate the distance between the two points (dx, dy) and then use the trig formula to find the angle in degrees.
layer.on('click', function() {
var mousePos = stage.getMousePosition();
var x = mousePos.x;
var y = mousePos.y;
var rectX = rect.getX()+rect.getWidth()/2;
var rectY = rect.getY()+rect.getHeight()/2;
var dx = x - rectX;
var dy = y - rectY;
var rotation = (Math.atan2(dy, dx)*180/Math.PI+360)%360;
var rotateOnClick = new Kinetic.Tween({
node: rect,
duration: 1,
rotationDeg: rotation,
easing: Kinetic.Easings.EaseInOut
});
rotateOnClick.play();
});
EDIT:
Based off the gathered information below (which I came to the same conclusion as) I have updated my fiddle: jsfiddle
As markE mentioned below in the comments, KineticJS does not support "force-clockwise flag", so the rotation always jumps when rotating past 360 in the clockwise direction, or past 0 in the counter clockwise position. Otherwise, we know that the rotation works properly.
So, to fix this manually there are two cases we need to consider:
When we rotate past 360 in the clockwise direction.
When we rotate past 0 in the counter clockwise direction.
And this is the math I used to calculate whether to counter the rotation or not:
var currentDeg = rect.getRotationDeg();
var oneWay = rotation - currentDeg;
var oneWayAbsolute = Math.abs(rotation - currentDeg);
var otherWay = 360-oneWayAbsolute;
if (otherWay < oneWayAbsolute) {
//Take the other way
if (oneWay > 0) {
//Clicked direction was positive/clockwise
var trueRotation = currentDeg - otherWay;
} else {
//Clicked direction was negative/counter clockwise
var trueRotation = currentDeg + otherWay;
}
} else {
//Take the clicked way
var trueRotation = rotation;
}
Basically we want to figure out which way to rotate, by comparing the angle degrees of which direction would be closer, the direction we clicked in, or the opposite way.
If we determined that the otherWay was closer to the currentDeg, then we need to see if the direction we clicked in was in the counter clockwise (negative) or clockwise (positive) direction, and we set the otherWay direction to go in the opposite direction.
And then you can normalise the rotationDeg onFinish event.
var rotateOnClick = new Kinetic.Tween({
node: rect,
duration: 1,
rotationDeg: trueRotation,
easing: Kinetic.Easings.EaseInOut,
onFinish: function() {
trueRotation = (trueRotation+360)%360;
rect.setRotationDeg(trueRotation);
layer.draw();
}
});
You might need to set the x and y position of the Tween as follows:
new Kinetic.Tween({
x: mousePos.x,
y: mousePos.y,
node: shape,
rotation: degree,
easing: Kinetic.Easings.EaseInOut,
duration:1,
}).play();
See this tutorial for reference http://www.html5canvastutorials.com/kineticjs/html5-canvas-stop-and-resume-transitions-with-kineticjs/
Related
I am rotating a cube around a particular axis (x or y or z) by dragging my mouse, lets say while dragging I calculate how much angle rotation I have to appy.
Now I have my current cube rotation in quaternion, and also an array of quaternions containing quaternions of 0, 45, 90, 135, 180, 225, 270, 315, and 360 degrees.
When I am rotating my cube I want to find the closest quaternion from the array, lets say I am rotating in anti-clock and my cube is at 30, then the closest will be quaternion of 90 degrees from the array., similarly for 170 I should get 180 deg quaternion from the array.
currently I am maintaining a variable and depending upon the direction (clock or anti-clock) I am rotating the cube I am managing that variable and finding the next required quaternion from the array. But I need a more efficient way if it exists.
currently My code is doing something like this, If anyone have some solution about this, or a new way of doing this, then please help me
function handleDrag() {
let target = new THREE.Vector3();
raycaster.setFromCamera(mouseNDC, camera);
raycaster.ray.intersectPlane(Zplane, target);
let temp = target
target.sub(mesh.position); // final vector after drag
initial.sub(mesh.position); // initial vector
// get rotation direction using cross product
let xx = new THREE.Vector3().copy(target).normalize()
let yy = new THREE.Vector3().copy(prevDir).normalize()
let dir = yy.cross(xx).z
const angleBtwVec = angleInPlane(initial, target, Zplane.normal);
let quaternion = new THREE.Quaternion();
quaternion.setFromAxisAngle(new THREE.Vector3(0, 1, 0), angleBtwVec);
let effectiveQuatChange = new THREE.Quaternion().copy(initialCubeQuat)
effectiveQuatChange.multiply(quaternion)
// find next quaterion for which we need to compare for snapping
let check = next
if (dir > 0) { // then anti-clock
check = (next + 1) % 8
} else { // else rotation is in clock direction
check = (next - 1 + 8) % 8
}
// apply quaternion change
mesh.quaternion.copy(effectiveQuatChange)
let reqQuatArray = quaternionArrayYR
let angleDiff = toDegrees(mesh.quaternion.angleTo(reqQuatArray[check]))
console.log(angleDiff, check);
if (angleDiff <= 15) { // if mesh angle with next req quaternion is less than 15 degree, then set mesh quaternion to required quaternion
next = check
mesh.quaternion.copy(reqQuatArray[next]);
initialCubeQuat.copy(reqQuatArray[next]);
initial = temp;
}
prevDir = temp
}
I am trying to get the Z value on the mesh when i pass the X & Y coordinate. Sorry, i am new to three js.
I am using raycaster for the same. My plan is to set origin exactly above the point and direction just below it. So that it will intersect on mesh and will return me the respective values.
Here is my code:
for(var i=0;(i)<points.length;i++){
var pts = points[i];
var top = new THREE.Vector3(pts.x , pts.y , 50 );
var bottom = new THREE.Vector3( pts.x , pts.y , -50 );
//start raycaster
var raycaster = new THREE.Raycaster();
raycaster.set( top, bottom );
// calculate objects intersecting the picking ray
var intersects = rayCaster.intersectObjects(scene.getObjectByName('MyObj_s').children, false);
if (intersects.length > 0){
console.log(intersects[0].point);
}
}
However the above code results shows totally different X & Y positions, and definitely inaccurate Z values.
top
Object { x: 58.26593421875712, y: 63.505675324244834, z: 50 }
bottom
Object { x: 58.26593421875712, y: 63.505675324244834, z: -50 }
Result
Object { x: -2.9414508017947445, y: -13.236528362050667, z:
-2.0969017881066634 }
raycaster.set( top, bottom );
It seems you are not using Raycaster.set() correctly. As you can see in the documentation, the method expects an origin and a direction vector. In your code, you just pass in two points.
The first parameter origin represents the origin vector where the ray casts from.
The second parameter direction is a normalized (!) vector representing the direction of the ray.
three.js R104
I'm new to threejs
I need to draw a sphere connected with triangles. I use Icosahedron to construct the sphere in the following way
var material = new THREE.MeshPhongMaterial({
emissive : 0xffffff,
transparent: true,
opacity : 0.5,
wireframe : true
});
var icogeo = new THREE.IcosahedronGeometry(80,2);
var mesh = new THREE.Mesh(icogeo, material);
scean.add(mesh);
But i need the width of the line to be more but line width won't show up in windows so i taught of looping through the vertices and draw a cylinder/tube between the vertices. (I can't draw lines because the LineBasicMaterial was not responding to Light.)
for(i=0;i<icogeo.faces.length;i++){
var face = icogeo.faces[i];
//get vertices from face and draw cylinder/tube between the three vertices
}
Can some one please help on drawing the tube/cylinder between two vector3 vertices?
**the problem i'm facing with wireframe was it was not smooth and i can't increase width of it in windows.
If you really want to create a cylinder between two points one way to do is to create it in a unit space and then transform it to your line. But that is very mathy.
An intuitive way to create it is to think about how would you do it in a unit space? A circle around the z axis (in x,y) and another one a bit down z.
Creating a circle in 2d is easy: for ( angle(0,360,360/numsteps) ) (x,y)=(sin(angle),cos(angle))*radius. (see for example Calculating the position of points in a circle).
Now the two butt ends of your cylinder are not in x,y! But If you have two vectors dx,dy you can just multiply your x,y with them and get a 3d position!
So how to get dx, dy? One way is http://en.wikipedia.org/wiki/Gram%E2%80%93Schmidt_process
which reads way more scary than it is. You start with your forward direction, which is your line. forward = normalize(end-start). Then you just pick a direction "up". Usually (0,1,0). Unless forward is already close to up, then pick another one like (1,0,0). Take their cross product. This gives you "left". Then take the cross product between "left" and "forward" to get "right". Now "left" and "right" are you dx and dy!
That way you can make two circles at the two ends of your line. Add triangles in between and you have a cylinder!
Even though I do believe it is an overkill for what you are trying to achieve, here is code that draws a capsule (cylinder with spheres at the end) between two endpoints.
/**
* Returns a THREE.Object3D cylinder and spheres going from top to bottom positions
* #param radius - the radius of the capsule's cylinder
* #param top, bottom - THREE.Vector3, top and bottom positions of cone
* #param radiusSegments - tessellation around equator
* #param openTop, openBottom - whether the end is given a sphere; true means they are not
* #param material - THREE.Material
*/
function createCapsule (radius, top, bottom, radiusSegments, openTop, openBottom, material)
{
radiusSegments = (radiusSegments === undefined) ? 32 : radiusSegments;
openTop = (openTop === undefined) ? false : openTop;
openBottom = (openBottom === undefined) ? false : openBottom;
var capsule = new THREE.Object3D();
var cylinderAxis = new THREE.Vector3();
cylinderAxis.subVectors (top, bottom); // get cylinder height
var cylinderGeom = new THREE.CylinderGeometry (radius, radius, cylinderAxis.length(), radiusSegments, 1, true); // open-ended
var cylinderMesh = new THREE.Mesh (cylinderGeom, material);
// get cylinder center for translation
var center = new THREE.Vector3();
center.addVectors (top, bottom);
center.divideScalar (2.0);
// pass in the cylinder itself, its desired axis, and the place to move the center.
makeLengthAngleAxisTransform (cylinderMesh, cylinderAxis, center);
capsule.add (cylinderMesh);
if (! openTop || ! openBottom)
{
// instance geometry
var hemisphGeom = new THREE.SphereGeometry (radius, radiusSegments, radiusSegments/2, 0, 2*Math.PI, 0, Math.PI/2);
// make a cap instance of hemisphGeom around 'center', looking into some 'direction'
var makeHemiCapMesh = function (direction, center)
{
var cap = new THREE.Mesh (hemisphGeom, material);
makeLengthAngleAxisTransform (cap, direction, center);
return cap;
};
// ================================================================================
if (! openTop)
capsule.add (makeHemiCapMesh (cylinderAxis, top));
// reverse the axis so that the hemiCaps would look the other way
cylinderAxis.negate();
if (! openBottom)
capsule.add (makeHemiCapMesh (cylinderAxis, bottom));
}
return capsule;
}
// Transform object to align with given axis and then move to center
function makeLengthAngleAxisTransform (obj, align_axis, center)
{
obj.matrixAutoUpdate = false;
// From left to right using frames: translate, then rotate; TR.
// So translate is first.
obj.matrix.makeTranslation (center.x, center.y, center.z);
// take cross product of axis and up vector to get axis of rotation
var yAxis = new THREE.Vector3 (0, 1, 0);
// Needed later for dot product, just do it now;
var axis = new THREE.Vector3();
axis.copy (align_axis);
axis.normalize();
var rotationAxis = new THREE.Vector3();
rotationAxis.crossVectors (axis, yAxis);
if (rotationAxis.length() < 0.000001)
{
// Special case: if rotationAxis is just about zero, set to X axis,
// so that the angle can be given as 0 or PI. This works ONLY
// because we know one of the two axes is +Y.
rotationAxis.set (1, 0, 0);
}
rotationAxis.normalize();
// take dot product of axis and up vector to get cosine of angle of rotation
var theta = -Math.acos (axis.dot (yAxis));
// obj.matrix.makeRotationAxis (rotationAxis, theta);
var rotMatrix = new THREE.Matrix4();
rotMatrix.makeRotationAxis (rotationAxis, theta);
obj.matrix.multiply (rotMatrix);
}
I'm trying to make a static 3D prism out of point clouds with specific numbers of particles in each. I've got the the corner coordinates of each side of the prism based on the angle of turn, and tried spawning the particles in the area bound by these coordinates. Instead, the resulting point clouds have kept only the bottom left coordinate.
Screenshot: http://i.stack.imgur.com/uQ7Q8.png
I've tried to set the rotation of each cloud object such that their edges meet, but they will rotate only around the world centre. I gather this is something to do with rotation matrices and Euler angles, but, having been trying to work them out for 3 solid days, I've despaired. (I'm a sociologist, not a dev, and haven't touched graphics before this project.)
Please help? How do I set the rotation on each face of the prism? Or maybe there is a more sensible way to get the particles to spawn in the correct area in the first place?
The code:
// draw the particles
var n = 0;
do {
var geom = new THREE.Geometry();
var material = new THREE.PointCloudMaterial({size: 1, vertexColors: true, color: 0xffffff});
for (i = 0; i < group[n]; i++) {
if (geom.vertices.length < group[n]){
var particle = new THREE.Vector3(
Math.random() * screens[n].bottomrightback.x + screens[n].bottomleftfront.x,
Math.random() * screens[n].toprightback.y + screens[n].bottomleftfront.y,
Math.random() * screens[n].bottomrightfront.z + screens[n].bottomleftfront.z);
geom.vertices.push(particle);
geom.colors.push(new THREE.Color(Math.random() * 0x00ffff));
}
}
var system = new THREE.PointCloud(geom, material);
scene.add(system);
**// something something matrix Euler something?**
n++
}
while (n < numGroups);
I've tried to set the rotation of each cloud object such that their
edges meet, but they will rotate only around the world centre.
It is true they only rotate around 0,0,0. The simple solution then is to move the object to the center, rotate it, and then move it back to its original position.
For example (Code not tested so might take a bit of tweaking):
var m = new THREE.Matrix4();
var movetocenter = new THREE.Matrix4();
movetocenter.makeTranslation(-x, -y, -z);
var rotate = new THREE.Matrix4();
rotate.makeRotationFromEuler(); //Build your rotation here
var moveback = new THREE.Matrix4();
moveback .makeTranslation(x, y, z);
m.multiply(movetocenter);
m.multiply(rotate);
m.multiply(moveback);
//Now you can use geometry.applyMatrix(m)
I looked at many examples but so far nothing worked. I want the circle to rotate on mousemove and it is rotating centered, so no problems so far. But, it does a 180 jump when I pass half of the stage. So everything is fine till I pass the half of the stage, clearly I'm missing something. Math.atan2 gives me an error: the circle jumps to the (0,0) of the stage.
Please help I really need this badly.
Thanks in advance!
new Kinetic.Tween({
node: cGrad1,
x: stage.getWidth()/2,
y: stage.getHeight()/2,
duration: 1,
opacity:1,
easing: Kinetic.Easings.EaseInOut
}).play();
clickO.on('mousemove', function() {
for(var n = 0; n < 16; n++) {
var shape = cGradlayer1.getChildren()[n];
var stage = shape.getStage();
var mousePos = stage.getMousePosition();
var x = mousePos.x - shape.getPosition().x;
var y = mousePos.y -shape.getPosition().y ;
var degree = (Math.PI)+(Math.atan(y/x));
shape.setAttrs({
rotation: degree
})
cGradlayer1.draw()
}
})
Well this is what I came up with, and hopefully it's close to what you were looking for: jsfiddle
Basically, to calculate the angle you want to rotate to, we need to store two points:
The origin of the shape (centre coordinate)
The coordinate of the mouse click
Once you have that, you can calculate the angle between the two points with a little bit of trigonometry (Sorry if I am not accurate here, Trigonometry is not my strong suit). Calculate the distance between the two points (dx, dy) and then use the trig formula to find the angle in degrees.
layer.on('click', function() {
var mousePos = stage.getMousePosition();
var x = mousePos.x;
var y = mousePos.y;
var rectX = rect.getX()+rect.getWidth()/2;
var rectY = rect.getY()+rect.getHeight()/2;
var dx = x - rectX;
var dy = y - rectY;
var rotation = (Math.atan2(dy, dx)*180/Math.PI+360)%360;
var rotateOnClick = new Kinetic.Tween({
node: rect,
duration: 1,
rotationDeg: rotation,
easing: Kinetic.Easings.EaseInOut
});
rotateOnClick.play();
});
EDIT:
Based off the gathered information below (which I came to the same conclusion as) I have updated my fiddle: jsfiddle
As markE mentioned below in the comments, KineticJS does not support "force-clockwise flag", so the rotation always jumps when rotating past 360 in the clockwise direction, or past 0 in the counter clockwise position. Otherwise, we know that the rotation works properly.
So, to fix this manually there are two cases we need to consider:
When we rotate past 360 in the clockwise direction.
When we rotate past 0 in the counter clockwise direction.
And this is the math I used to calculate whether to counter the rotation or not:
var currentDeg = rect.getRotationDeg();
var oneWay = rotation - currentDeg;
var oneWayAbsolute = Math.abs(rotation - currentDeg);
var otherWay = 360-oneWayAbsolute;
if (otherWay < oneWayAbsolute) {
//Take the other way
if (oneWay > 0) {
//Clicked direction was positive/clockwise
var trueRotation = currentDeg - otherWay;
} else {
//Clicked direction was negative/counter clockwise
var trueRotation = currentDeg + otherWay;
}
} else {
//Take the clicked way
var trueRotation = rotation;
}
Basically we want to figure out which way to rotate, by comparing the angle degrees of which direction would be closer, the direction we clicked in, or the opposite way.
If we determined that the otherWay was closer to the currentDeg, then we need to see if the direction we clicked in was in the counter clockwise (negative) or clockwise (positive) direction, and we set the otherWay direction to go in the opposite direction.
And then you can normalise the rotationDeg onFinish event.
var rotateOnClick = new Kinetic.Tween({
node: rect,
duration: 1,
rotationDeg: trueRotation,
easing: Kinetic.Easings.EaseInOut,
onFinish: function() {
trueRotation = (trueRotation+360)%360;
rect.setRotationDeg(trueRotation);
layer.draw();
}
});
You might need to set the x and y position of the Tween as follows:
new Kinetic.Tween({
x: mousePos.x,
y: mousePos.y,
node: shape,
rotation: degree,
easing: Kinetic.Easings.EaseInOut,
duration:1,
}).play();
See this tutorial for reference http://www.html5canvastutorials.com/kineticjs/html5-canvas-stop-and-resume-transitions-with-kineticjs/