5

(new to stackoverflow, new to webgl/three.js, ...)

I'm using three.js r54 to plot a force-directed graph. the edges between the nodes are THREE.Lines, which is fine, but lines are not selectable with a raycaster. so my aim is to take cylinders instead(/along with) of lines(also because I can do some further stuff: using textures,...)

this is what I'm doing to place the cylinders:

// init reference vector
var upVec = new THREE.Vector3(0,1,0);

//---withhin a loop---
// get direction
var direction = startPoint.subSelf(endPoint).clone();

// half length for cylinder height
var halfLength = direction.length() * 0.5;  

// get offset
var offset = endPoint.clone().addSelf(direction.clone().multiplyScalar(0.5));

// normalize direc
direction.normalize();

//newUpVec = upVec - (upVec *(dot) direction) * direction - projection of direction
var newUpVec = upVec.clone().subSelf(direction.clone().multiplyScalar(upVec.dot(direction.clone()))).normalize();
var right = newUpVec.clone().crossSelf(direction.clone());

//build rotation matrix
var rot = new THREE.Matrix4(right.x, right.y, right.z, 0,
                            newUpVec.x, newUpVec.y, newUpVec.z, 0, 
                            direction.x, direction.y, direction.z,0,
                            0,0,0,1);
//build translation matrix
var transla = new THREE.Matrix4(1, 0, 0, offset.x,
                                0, 1, 0, offset.y,
                                0, 0, 1, offset.z,
                                0, 0, 0, 1);

 //build transformation matrix
 var transfo = new THREE.Matrix4().multiply(transla, rot);

 // create geometry
 var cylgeo = new THREE.CylinderGeometry(2, 2, halfLength * 2, 12, 1, false);
 cylgeo.applyMatrix(transfo);

 var cylMesh = new THREE.Mesh(cylgeo, new THREE.MeshLambertMaterial({color:0x000000, 
            wireframe: true, shading: THREE.FlatShading}));

(descripted in: http://www.fastgraph.com/makegames/3drotation/ )

So the cylinders are placed at the right offset and align in some kind of way, but not to the two points (start, end) of the edges.
any suggestion would be appreciated!

ewo
  • 75
  • 2
  • 5
  • 1
    Possible overlap with https://stackoverflow.com/questions/44317902/how-to-render-edges-as-cylinders/44346439#44346439 (it's a newer question, but has a better answer) – supergra Aug 06 '21 at 23:40

4 Answers4

8

using that : object3d-rotation-to-align-to-a-vector

given 2 Vector3 and a scene:

function drawCylinder(vstart, vend,scene){
var HALF_PI = +Math.PI * .5;
var distance = vstart.distanceTo(vend);
var position  = vend.clone().addSelf(vstart).divideScalar(2);

var material = new THREE.MeshLambertMaterial({color:0x0000ff});
var cylinder = new THREE.CylinderGeometry(10,10,distance,10,10,false);

var orientation = new THREE.Matrix4();//a new orientation matrix to offset pivot
var offsetRotation = new THREE.Matrix4();//a matrix to fix pivot rotation
var offsetPosition = new THREE.Matrix4();//a matrix to fix pivot position
orientation.lookAt(vstart,vend,new THREE.Vector3(0,1,0));//look at destination
offsetRotation.rotateX(HALF_PI);//rotate 90 degs on X
orientation.multiplySelf(offsetRotation);//combine orientation with rotation transformations
cylinder.applyMatrix(orientation)

var mesh = new THREE.Mesh(cylinder,material);
mesh.position=position;
scene.add(mesh);

}

r58+ code :

 function drawCylinder(vstart, vend,scene){
    var HALF_PI = Math.PI * .5;
    var distance = vstart.distanceTo(vend);
    var position  = vend.clone().add(vstart).divideScalar(2);

    var material = new THREE.MeshLambertMaterial({color:0x0000ff});
    var cylinder = new THREE.CylinderGeometry(10,10,distance,10,10,false);

    var orientation = new THREE.Matrix4();//a new orientation matrix to offset pivot
    var offsetRotation = new THREE.Matrix4();//a matrix to fix pivot rotation
    var offsetPosition = new THREE.Matrix4();//a matrix to fix pivot position
    orientation.lookAt(vstart,vend,new THREE.Vector3(0,1,0));//look at destination
    offsetRotation.makeRotationX(HALF_PI);//rotate 90 degs on X
    orientation.multiply(offsetRotation);//combine orientation with rotation transformations
    cylinder.applyMatrix(orientation)

    var mesh = new THREE.Mesh(cylinder,material);
    mesh.position=position;
    scene.add(mesh);
    }
Community
  • 1
  • 1
jdregister
  • 176
  • 6
  • 1
    YES!!! it works! well I saw your mentioned example but I didn't give it a try, because I don't really understand what is happining there. I will have a closer look at the used functions in the framework. thank you so much! – ewo Mar 02 '13 at 08:16
  • you should be able to see http://meta.stackexchange.com/questions/167748/user-cannot-accept-my-answer – jdregister Mar 04 '13 at 08:15
  • 2
    In r58, rotateX is deprecated for makeRotationX and multiplySelf, addSelf, subSelf have to be changed to multiply,add,sub respectively – jdregister May 02 '13 at 08:57
  • 1
    @jdregister You should change the deprecated function calls in your example code to match your previous comment. This makes copy&paste easier. Additionally why did you use `Math.round` here `var distance = Math.round(vstart.distanceTo(vend));`? I think the rounding should be removed. – Stefan Profanter Jul 15 '13 at 12:24
  • you're right for the rounding. I edited my answer per your suggestion. thx. – jdregister Jul 15 '13 at 14:51
  • Note that the offsetPosition variable is never used. Thanks for the great response. – Max Strater Apr 30 '14 at 20:01
  • Don't know why mesh.position=position was not working. Changed it to mesh.position.x=position.x ... – Thakur Karthik Aug 02 '19 at 16:13
1

@jdregister's answer didn't quite work for me in R77, since the cylinder ended up with its center at vstart (rotation and lookAt were otherwise fine).

This modification to the second last line of the R58+ answer did the trick:

mesh.position.set(position.x, position.y, position.z);
MBourne
  • 147
  • 2
  • 9
1

There's a very succinct answer here: https://stackoverflow.com/a/44346439/1556416

I paraphrased it here:

function drawCylinder(vstart, vend, radius){
    var cylLength = new THREE.Vector3().subVectors(vend, vstart).length();

    var cylGeom = new THREE.CylinderGeometry(radius, radius, cylLength, 16);
    cylGeom.translate(0, cylLength / 2, 0);
    cylGeom.rotateX(Math.PI / 2);

    var material = new THREE.MeshLambertMaterial({color: "blue"})

    var cyl = new THREE.Mesh(cylGeom, material);

    cyl.position.copy(vstart);
    cyl.lookAt(vend);  // and do the trick with orienation

    return cyl
}
supergra
  • 1,578
  • 13
  • 19
0

In R87 the "vend.clone().add(vstart).divideScalar(2);" is not working

You can position the item like this mesh.position.copy(start); mesh.position.lerp(end, 0.5);

All the others from R58 are fine :)

Dimitrios Ververidis
  • 1,118
  • 1
  • 9
  • 33