spite / THREE.MeshLine

Mesh replacement for THREE.Line
MIT License
2.14k stars 381 forks source link

Draw tube with rounded corners #21

Open Jones-S opened 7 years ago

Jones-S commented 7 years ago

Hey there I actually wrote the stackoverflow question mentioned in the other issue:

19

I tried to get a result like the on in the picture of the post with your MeshLine.js, but What I currently get is something like this:

screen shot 2016-08-05 at 13 19 38

I disabled sizeAttenuation but it would still make this strange thickenings of the lines. Is it maybe because my meshLine has different vertices and I am not creating one line for each segment, like you did it in your graph-example? (https://www.clicktorelease.com/code/THREE.MeshLine/demo/graph.html)

Or do you have any other hints on how I could get such a shape with rounded corners, which looks tube-like?

I would be very thankful for any help here. :v: Cheers

Jones-S commented 7 years ago

If anyone would like to have a look into the code:

    $(function() { // Shorthand for $( document ).ready()
var MIN_DISTANCE = 0.15,
    LINE_THICKNESS = 0.012;

var renderer = new THREE.WebGLRenderer({ antialias: true }),
    scene = new THREE.Scene(),
    camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 500),
    resolution = new THREE.Vector2( window.innerWidth, window.innerHeight ),
    animating = false,
    line = null,
    tubeMesh = null,
    cube = null;

var material = new THREE.LineBasicMaterial({
    color: 0xffffff,
    linewidth: 5
});

var graph = new THREE.Object3D();
scene.add( graph );

var light = new THREE.AmbientLight( 0x404040 ); // soft white light
scene.add( light );

function run() {
    // Render the scene
    renderer.render(scene, camera);

    // Spin the cube for next frame
    if (animating) {
        // line.rotation.y -= 0.03;
        tubeMesh.rotation.y -= 0.01;

    }

    // Ask for another frame
    requestAnimationFrame(run);
}

function randomIntFromInterval(min, max) {
    return Math.floor(Math.random() * (max - min + 1) + min);
}

function generateRandomCoordinate() {
    // generate random number and round to nearest 0.05
    var new_coord = (Math.ceil(Math.random() * (1 / MIN_DISTANCE)) / (1 / MIN_DISTANCE)).toFixed(2);
    new_coord = parseFloat(new_coord);
    return new_coord;
}

function generateConst(direction) {
    var const_coord = randomIntFromInterval(0, 2);
    // also check if it is different coordinate than last time
    while (const_coord == direction){
        const_coord = randomIntFromInterval(0, 2);
    }
    return const_coord;
}

// Create the Three.js renderer, add it to our div
renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
camera.position.set(0, 1.5, -2);
camera.lookAt(new THREE.Vector3(0, 0.9, 0));

// draw outline cube
// var cube_geometry = new THREE.BoxGeometry(1, 1, 1, 0, 0, 0);
// var cube_material = new THREE.MeshBasicMaterial({color: 0xF0FF91, wireframe: false, transparent: true, opacity: 0.2});
// cube = new THREE.Mesh(cube_geometry, cube_material);
// cube.position.set(-0.5, -0.5, -0.5);
// scene.add(cube);

function generateLineVertices(argument) {

    var dir_next_prev = 0,
        raycaster,
        // generate starting vector (between (0,0,0) and (1,1,1) )
        pos_x = generateRandomCoordinate(),
        pos_y = generateRandomCoordinate(),
        pos_z = generateRandomCoordinate(),

        random_steps = randomIntFromInterval(7, 15),
        counter = 0,
        direction = 0,
        directions = [],

        // generate randomly which coordinate (x,y,z) should be changed, returns 0, 1 or 2
        const_coordinate = 0,

        vector_start = new THREE.Vector3(pos_x, pos_y, pos_z),
        vector_prev = vector_start,
        vector_next,

        vertices = [];

    // add first vertex
    vertices.push(vector_start);

   for (var i = 0; i < random_steps; i++) {

        direction = const_coordinate; // determines if x/y/z should be changed
        directions[i] = direction; // saves the last direction-change

        switch(const_coordinate) {
            case 0: // change only x
                vector_next = new THREE.Vector3(generateRandomCoordinate(), vector_prev.y, vector_prev.z);
                break;
            case 1: // change only y
                vector_next = new THREE.Vector3(vector_prev.x, generateRandomCoordinate(), vector_prev.z);
                break;
            case 2: // change only z
                vector_next = new THREE.Vector3(vector_prev.x, vector_prev.y, generateRandomCoordinate());
                break;
            default:

        }

        // direction between last vector and the vector to test
        // generate Three Vector from last vertex and vector_next
        dir_next_prev = new THREE.Vector3().subVectors(vertices[vertices.length - 1], vector_next);
        // normalize vector
        dir_next_prev = dir_next_prev.normalize();

        // a raycaster will check if the new vector could cross another line
        // generate a Raycaster with the direction from above and with the new vector to check
        raycaster = new THREE.Raycaster(vector_next, dir_next_prev);
        raycaster.linePrecision = 2;

        var geometry_next_prev;
        var flag_found_intersection = false;

        // check if new point is on line between to others
        for (var j = 0; j < vertices.length; j++) {

            if (j >= 1) {

                var vA = vertices[j - 1];
                var vB = vertices[j];

                // // get direction between the two points
                // var dir_B_A = new THREE.Vector3().subVectors(vA, vB);
                // console.log("dir_B_A: ", dir_B_A);

                // // move point A into the direction dir_B_A
                // dir_B_A.setLength(MIN_DISTANCE/2);
                // console.log("dir_B_A: ", dir_B_A);

                geometry_next_prev = new THREE.Geometry();
                // make a new geometry with the two points to evaluate
                geometry_next_prev.vertices.push(vA, vB);

                var test_line = new THREE.Line(geometry_next_prev, new THREE.LineBasicMaterial({ color: 0x0000ff }));
                // testing the raycaster against the test line
                var intersection = raycaster.intersectObject(test_line);
                // console.log("intersection: ", intersection);

                if (intersection.length > 0) {
                    flag_found_intersection = true;
                }
            }
        }

        if ((flag_found_intersection === true || vector_prev == vector_next)) {

            if (vector_prev == vector_next) {
                // console.log("%c length was 0", "background: #FD3FB7; color: #DA5C1B");
            }

            if (counter < 15) {
                i--;
                counter++;

                continue;
            } else if (counter >= 15 && counter < 25) {

            } else {

                // console.log("%c counter up - - - - - - - - - - - - - - - -", "background: #FD5B0F; color: #DA5C1B");
                // console.log("%c vertices", "background: #0D0B07; color: #FAFBFF", vertices);
                counter = 0;
                break;

            }
        }

        counter = 0;

        const_coordinate = generateConst(direction);

        // console.log("%c vector_next", "background: #0D0B07; color: #FAFBFF", vector_next);

        // add new vector to geometry
        vertices.push(vector_next);

        // set prev vector
        vector_prev = vector_next;
    }
    // console.log("directions: ", directions);
    return vertices;
}

function generateMesh() {
    var lineVertices = generateLineVertices();
    // console.log("%c lineVertices", "background: #0D0B07; color: #FAFBFF", lineVertices);

    var meshMaterial = new THREE.MeshBasicMaterial({ color: 0xffffff });
    // meshMaterial.color.setHSL(0, 0.8, 0.5);

    var tube_line_geometry = new THREE.Geometry();
    tube_line_geometry.vertices = lineVertices;

    var line = new THREE.MeshLine();
    line.setGeometry( tube_line_geometry );

    var material = new THREE.MeshLineMaterial({
            color: new THREE.Color( 0xffffff ),
            useMap: false,
            opacity: 0.5,
            taper: 'linear',
            resolution: resolution,
            sizeAttenuation: false,
            lineWidth: 30,
            near: camera.near,
            far: camera.far
        });

    tubeMesh = new THREE.Mesh( line.geometry, material ); // this syntax could definitely be improved!
    graph.add( tubeMesh );

}

generateMesh();

$('body').keydown(function( event ) {
    if ( event.which != 38 ) {
        return;
    }
    scene.remove(tubeMesh);
    // scene.remove(line);
    generateMesh();
});

// Add a mouse up handler to toggle the animation
$(window).mouseup(function() {
    // console.log("mouse changed");
    animating = !animating;
});

// Run our render loop
run();

});


}(jQuery));

Thanks in advance!

4nte commented 4 years ago

I'm facing a same issue. How did you solve this, if you did?

Jones-S commented 4 years ago

I did. I only vaguely remember: I just added a sphere at the end of the tube to imitate a round corner effect...