I'm trying to create a torus graph where there are different coloured segments of different widths (depending on the data size), basically a pie graph that looks nicer.
Is it possible to do this using torus geometry or would it be best to create the segments as shapes and use exctrudegeometry?
In wither case, what's the best way of doing this?
Edit
I have implemented this via torus geometry like:
var prevAngle = 0;
for(var i = 0; i < data.length; i++){
var mat = new THREE.MeshPhongmaterial({materialOptions});
var angle = 2* Math.PI * data[i].size //size is decimal
var geo = new THREE.TorusGeometry(500, 200, 8, 6, angle);
var mesh = new THREE.Mesh(geo, mat);
mesh.rotation.z = prevAngle;
scene.add(mesh);
prevAngle = angle;
}
But when I render this (with 4 objects in data with size 0.25) I only get the top half of the torus (semi-torus?).
is the rotation correct?
You can look at the example at https://github.com/josdirksen/learning-threejs/blob/master/chapter-05/07-basic-3d-geometries-torus.html
As you can see, the last argument to the TorusGeometry is the arc which you can use to directly map the segments of your pie graph to segments of a torus geometry.
Working solution
var prevAngle = 0;
var material = new THREE.MeshPhongmaterial({materialOptions});
var graphContainer = new THREE.Object3D();
graphContainer.castShadow = false;
for(var i = 0; i < data.length; i++){
var mat = material.clone();
mat.color = new THREE.Color(this.coloursArray[i]); //colorsArray = ['#46adad', ...]
var angle = 2* Math.PI * data[i].size //size is decimal
var geo = new THREE.TorusGeometry(500, 200, 8, 6, angle);
var mesh = new THREE.Mesh(geo, mat);
mesh.rotation.z = prevAngle;
graphContainer.add(mesh);
prevAngle += angle;
}
this.scene.add(graphContainer);
Related
I'm trying to make a game with THREE.js. I have a path with a lot of curves combined with some straight paths.
I found an example on Internet and tried to implement TrackballControls.js to make the camera follow a line, but the camera doesn't lookAt the front, it moves weird.
Here is the code:
var controls = new THREE.TrackballControls(camera, render.domElement);
var numPoints = 50;
spline = new THREE.CatmullRomCurve3([
new THREE.Vector3(0, 0, 0),
new THREE.Vector3(50, 0, 0),
new THREE.Vector3(0, 0, -100)
]);
var material = new THREE.LineBasicMaterial({
color: 0xff00f0,
});
var geometry = new THREE.Geometry();
var splinePoints = spline.getPoints(numPoints);
for (var i = 0; i < splinePoints.length; i++) {
geometry.vertices.push(splinePoints[i]);
}
line = new THREE.Line(geometry, material);
line.position.set(0, 0, 0);
scene.add(line);
var counter = 0;
if (counter <= 1) {
camera.position.copy( spline.getPointAt(counter) );
tangent = spline.getTangentAt(counter).normalize();
axis.crossVectors(up, tangent).normalize();
var radians = Math.acos(up.dot(tangent));
camera.quaternion.setFromAxisAngle(axis, radians);
counter += 0.005
} else {
counter = 0;
}
Thanks.
The following example shows how to create a camera that follows a spline curve. Just click on the button "Camera Spline Animation View" to see the effect. Maybe you can use the underlying code to solve your problem.
I am trying to add a number of spheres in the following example. Initially it had only three cubes, but I need to add some 10 spheres that would be equidistant from each other and would be rotating in different speeds.
My Try
var parent, renderer, scene, camera, controls;
init();
animate();
function init()
{
// renderer
renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// scene
scene = new THREE.Scene();
// camera
camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 100);
camera.position.set(20, 20, 20);
// controls
controls = new THREE.OrbitControls(camera);
controls.minDistance = 10;
controls.maxDistance = 50;
// axes
scene.add(new THREE.AxisHelper(20));
// geometry
var geometry = new THREE.SphereGeometry(0.3, 50, 50, 0, Math.PI * 2, 0, Math.PI * 2)
// material
var material = new THREE.MeshBasicMaterial({
color: 0xffffff,
wireframe: true
});
// parent
parent = new THREE.Object3D();
scene.add(parent);
// pivots
var pivot1 = new THREE.Object3D();
var pivot2 = new THREE.Object3D();
var pivot3 = new THREE.Object3D();
var pivot4 = new THREE.Object3D();
pivot1.rotation.z = 0;
pivot2.rotation.z = 2 * Math.PI / 3;
pivot3.rotation.z = 4 * Math.PI / 3;
pivot4.rotation.z = 6 * Math.PI / 3;
parent.add(pivot1);
parent.add(pivot2);
parent.add(pivot3);
parent.add(pivot4);
// mesh
var mesh1 = new THREE.Mesh(geometry, material);
var mesh2 = new THREE.Mesh(geometry, material);
var mesh3 = new THREE.Mesh(geometry, material);
var mesh4 = new THREE.Mesh(geometry, material);
mesh1.position.y = 5;
mesh2.position.y = 5;
mesh3.position.y = 5;
mesh4.position.y = 5;
pivot1.add(mesh1);
pivot2.add(mesh2);
pivot3.add(mesh3);
pivot4.add(mesh4);
}
function animate()
{
requestAnimationFrame(animate);
parent.rotation.z += 0.01;
controls.update();
renderer.render(scene, camera);
}
Why am I not able to add more than 3 spheres into the scene? I tried to add the fourth sphere but it did not work. How can speed be accounted for here? That is: can I specify different speeds for some spheres?
Missing 4th Sphere
You specify:
pivot1.rotation.z = 0;
pivot2.rotation.z = 2 * Math.PI / 3;
pivot3.rotation.z = 4 * Math.PI / 3;
pivot4.rotation.z = 6 * Math.PI / 3;
6 * Math.PI / 3 = 2 * Math.PI
Note, three.js uses radians, therefore 2 * PI is 0 (a full revolution is the same place as no rotation.
So pivot1 and pivot4 have the same effective rotation and your 2 sphere end up in the same place in space.
Speed
You currently handle speed by mutating the z rotation on every frame.
parent.rotation.z += 0.01;
This obviously works just fine for a demo. You can speed it up by moving more per frame (or getting more frames, ie better machine or other upgrades)
parent.rotation.z += 0.04;
Now it rotates at 4 times the speed!
More Spheres
Once you get past working with counts larger than your number of fingers on a hand, I recommend getting generic with arrays. Instead of listing out pivot1, pivot2, pivot3, . . . pivot0451, generate this with a loop. (Functionally you could use ranges if you prefer).
First, we declare how many spheres to make. Then divide up the circle (2 * Math.PI radians to go around). Then for ever sphere, make a pivot. Then, for every pivot, add a mesh. And you're done.
var numberOfSpheres = 10;
var radiansPerSphere = 2 * Math.PI / numberOfSpheres;
// pivots
var pivots = [];
for (var i = 0; i < numberOfSpheres; i++) {
var pivot = new THREE.Object3D();
pivot.rotation.z = i * radiansPerSphere;
parent.add(pivot);
pivots.push(pivot);
}
var meshes = pivots.map((pivot) => {
var mesh = new THREE.Mesh(geometry, material);
mesh.position.y = 5;
pivot.add(mesh)
return mesh;
});
I implemented this at this codepen.io
Happy coding.
Here's what i am trying to do, i already have created the vertices based on a formula, and connected lines between these vertices using a threejs method, and now i want to create shapes based on these vertices and connect them together to use them as map tiles, my suggestion is to modify the shape's vertices y, x, z axis, but i'm not able to find the right formula for these vertices:
mesh1 = new THREE.Mesh(); // sphere container
mesh2 = new THREE.Mesh(); // sphere container
mesh3 = new THREE.Mesh(); // sphere container
var R = 5.6; // radius
var LON = 32; var LAT = 16; // approximation
var PILAT = Math.PI/LAT;
var PILON = 2 * Math.PI/LON;
var cos1,cos2,sin1,sin2,t1,t2;
var y1,y2,r1,r2,t1,t2;
var plotG = new THREE.PlaneGeometry(0.06, 0.06);
var lineColor = new THREE.LineBasicMaterial({color: 0xaaaaaa});
var geometry = new THREE.Geometry();
var oldLATCounter = 0;
var oldLONCounter = 0;
for (var i=0; i<LAT; i++){
t1 = Math.PI - i*PILAT;
t2 = Math.PI - (i+1)*PILAT;
oldT1 = Math.PI - oldLATCounter*PILAT;
oldT2 = Math.PI - (oldLATCounter+1)*PILAT;
y1 = Math.cos(t1); // 1 latitudes radius y-position;
y2 = Math.cos(t2); // 2 latitudes radius y-position;
oldY1 = Math.cos(oldT1); // 1 latitudes radius y-position;
oldY2 = Math.cos(oldT2); // 2 latitudes radius y-position;
r1 = Math.abs( Math.sin(t1) ); // 1 latitudes radius;
r2 = Math.abs( Math.sin(t2) ); // 2 latitudes radius;
oldR1 = Math.abs( Math.sin(oldT1) ); // 1 latitudes radius;
oldR2 = Math.abs( Math.sin(oldT2) ); // 2 latitudes radius;
for (var j=0; j<LON; j++) // walk longitudes segments
{
t1 = j*PILON;
t2 = (j+1)*PILON;
oldT1 = oldLONCounter*PILON;
oldT2 = (oldLONCounter+1)*PILON;
cos1 = Math.cos(t1);
cos2 = Math.cos(t2);
sin1 = Math.sin(t1);
sin2 = Math.sin(t2);
oldCos1 = Math.cos(oldT1);
oldCos2 = Math.cos(oldT2);
oldSin1 = Math.sin(oldT1);
oldSin2 = Math.sin(oldT2);
geometry.vertices.push(
new THREE.Vector3( r1*cos1, y1, r1*sin1 ),
new THREE.Vector3( r2*cos1, y2, r2*sin1 ),
new THREE.Vector3( r2*cos2, y2, r2*sin2 )
);
geometry.dynamic = true;
var m1 = new THREE.Mesh( plotG );
m1.position.set(r2*cos2, y2, r2*sin2);
// m1.geometry.vertices[0].y = 0;
// m1.geometry.vertices[0].x = 0;
// m1.geometry.vertices[0].z = 0;
// m1.geometry.vertices[1].y = 0;
// m1.geometry.vertices[1].x = (oldR2*oldCos2) - (r2*cos2);
// m1.geometry.vertices[1].z = -(oldR2*oldSin2);
// m1.geometry.vertices[2].y = oldTy2;
// m1.geometry.vertices[2].x = 0;
// m1.geometry.vertices[2].z = 0.1;
// m1.geometry.vertices[3].y = 0;
// m1.geometry.vertices[3].x = 0;
// m1.geometry.vertices[3].z = 0.1;
mesh2.add(m1.clone());
oldLONCounter = j;
}
oldLATCounter = i;
}
mesh2.add( new THREE.Line( geometry, new THREE.LineBasicMaterial({color: 0xaaaaaa}) ) );
scene.add(mesh2);
mesh2.scale.set(R,R,R);
mesh2.position.x = 0;
This is the sphere i'm working on
Have you checked the THREE.Shape Class? It let you draw a shape/polygon based on some given points.
So my suggestion in your case is to loop through the vertices you need and draw a shape with them.
Here you can find out more about THREE.Shape
If you then want to create a geometry with that shape, to then add it to a mesh, then also have a look at THREE.ShapeGeometry
I am creating a Cube with standard new THREE.CubeGeometry. I am also changing the position of the mesh (x,y,z) and I add it to the scene. Later when user clicks on the option, I would like to draw a cube in all 6 corners of the cube. How can I get the position of each vertice in the coordinate system?
You can do simply :
var geometry = new THREE.CubeGeometry(...);
var verices = geometry.vertices;
if u looking for 8 vertices/corners... http://jsfiddle.net/ebeit303/54uQ9/
function addShapes() {
width = 100;
height = 100
depth = 100;
var geometry = new THREE.CubeGeometry( width, height, depth );
var material = new THREE.MeshBasicMaterial({color:0xffccff, side:2, overdraw:true} );
mesh = new THREE.Mesh( geometry, material );
group.add( mesh );
mesh.position.set(10,20,20);
var x = mesh.position.x;
var y = mesh.position.y;
var z = mesh.position.z;
console.log(geometry.vertices);
var len = geometry.vertices.length;
for(var i = 0; i < len; i++){
var vx = geometry.vertices[i].x;
var vy = geometry.vertices[i].y;
var vz = geometry.vertices[i].z;
var material1 = new THREE.MeshBasicMaterial({color:Math.random()*0xffffff, side:2, overdraw:true, opacity:0.6, transparent:true} );
var mesh1 = new THREE.Mesh( geometry, material1 );
group.add(mesh1);
mesh1.position.set( (vx*2)+x,(vy*2)+y,(vz*2)+z);
}
}
here the cube consist of 8 vertices/corners, now new cubes were drawn and position on each vertices of the middle cube, its a simple logic... cubegeometry - geometry.vertices and mesh position is used for simple calculation.... just go through the jsfiddle link... it might give you some fair idea...
I have points like this:
"POLYLINE":[[
{"x":"-6094.1665707632401","y":"3074.764386330728","r":""},
{"x":"-699.22595358468595","y":"1099.941236568309","r":""},
{"x":"-4940.397089330264","y":"576.87996358328382","r":""},
{"x":"-1329.5259580814709","y":"3149.4874087018579","r":"0.5163456475719181"},
{"x":"-6094.1665707632401","y":"3074.764386330728","r":""}
]]
where x y are vertices of a polyline and r is the radius if it is an arc.
i can manage drawing the are if this isnt possible but i would need to jump past the arc in the script
function DRAWpline(vert){
vert = JSON.parse(vert);
var material = new THREE.LineBasicMaterial({
color: 0x0000ff
});
var geometry = new THREE.Geometry();
for (var i = 0; i < vert.length; i++) {
if(vert[i]['r'] != ""){
geometry.vertices.push(new THREE.Vector3(vert[i]['x'], vert[i]['y'], 0));
} else {
}
}
var line = new THREE.Line(geometry, material);
scene.add(line);
}
Something like this, could do the job:
var p0 = new THREE.Vector3(vert[i]['x'], 0, 0);
var p1 = new THREE.Vector3(vert[i]['x'], vert[i]['y'], 0);
var p2 = new THREE.Vector3(vert[i+1]['x'], vert[i+1]['y'], 0);
var startAngle = Math.acos(p0.dot(p1)/(p0.length()*p1.length()));
var angle = Math.acos(p1.dot(p2)/(p1.length()*p2.length()));
geometry = new THREE.CircleGeometry(radius, nbSegments, startAngle, angle);
// remove center vertex
geometry.vertices.splice(0,1);
Add a check on vert[i+1] is exiting before compute p2