Search code examples
javascriptthree.jsbeziercurve

How to make parallel curves in three.js for road marking?


I´m trying to draw some parallel curved lines, one next to other, but if I separate them by modifying/translating their position in one axis, the result is not what I am looking for.

Code is simple, a bezier to make the central path and clones with other material for the sides.

var bezier = new THREE.CubicBezierCurve3(
                new THREE.Vector3(initx, inity, 0),
                new THREE.Vector3(cp1x, cp1y, 0),
                new THREE.Vector3( cp2x, cp2y, 0),
                new THREE.Vector3(finalx, finaly, 0)
                );

var curvePath = new THREE.CurvePath();
curvePath.add(bezier);

var path = curvePath.createPointsGeometry( 5 );
path.computeLineDistances();

var lineMat = new THREE.LineDashedMaterial({ color: 0xFFFFFF,
                                             dashSize: 3,
                                             gapSize: 2, 
                                             linewidth: 10});

var curveLine = new THREE.Line(path, lineMat);

curveLine.rotation.set(-Math.PI/2,0,0);
curveLine.position.y = 0.1;

var leftLine = curveLine.clone();
leftLine.material = matLine;

var rightLine = curveLine.clone();
rightLine.material = matLine;

leftLine.translateX(-3.5);
rightLine.position.set(3.5,0,0);

scene.add(curveLine);
scene.add(leftLine);
curveLine.add(rightLine);

This is the result: enter image description here

And this are some examples of what I am looking for:

dsource: https://pomax.github.io/bezierjs/source: https://pomax.github.io/bezierjs/

Last two images are from Bezier.js library.

I think that applying an offset to the curved line, or drawing another line using the normals of the first one might be the solution, but I can't find anything useful to do it in the documentation. I think that it could also be done drawing a line from a distance of the tangent axis or something like that, but it may be a easier way, any ideas?

I had a look on this but its for straight lines.


Solution

  • In the end, I did it by myself.

    I calculated the tangent and the normal of the main curve on its points using getTangent() and rotating it 90 degrees to obtain the normal. I created more vertices for that normal multiplying its values by a scalar. Then I got the point I wanted from the normals in world coordinates and connect them creating a CatmullRomCurve3 Geometry:

    for(var i = 0.0; i <= 1.0; i += 0.2){
    
        var point = curvePath.getPointAt(i);
    
        var tangent = curvePath.getTangent(i);
        var extension1 = new THREE.Vector3(tangent.x*2, tangent.y*2, 0);
        var extension2 = new THREE.Vector3(extension1.x*2, extension1.y*2, 0);
    
        var tangentGeometry = new THREE.Geometry();
        tangentGeometry.vertices.push(tangent, extension1, extension2);
    
        var tangentLine = new THREE.Line(tangentGeometry,greenMaterial);
        tangentLine.position.set(point.x, point.y, point.z);
    
        normal.updateMatrixWorld(); // VERY IMPORTANT
    
        var normal = tangentLine.clone();
        normal.rotateZ(Math.PI/2);
    
        var normalLastVertex = normal.geometry.vertices[2].clone();
        normalLastVertex.applyMatrix4(normal.matrixWorld); //convert to world coords.
    
        pointsArray.push(normalLastVertex); //for using them out of the loop
    
        curveLine.add(tangentLine);
        curveLine.add(normal);
    }
    

    Here I have last normals vertices in the points array, so I have to create the spline connecting them out of the loop.

        var splineCurveOffsetLeft = new THREE.CatmullRomCurve3([
    
           new THREE.Vector3((pointsArray[0].x), pointsArray[0].y, 0),
           new THREE.Vector3((pointsArray[1].x), pointsArray[1].y, 0),
           new THREE.Vector3((pointsArray[2].x), pointsArray[2].y, 0),
           new THREE.Vector3((pointsArray[3].x), pointsArray[3].y, 0),
           new THREE.Vector3((pointsArray[4].x), pointsArray[4].y, 0),
           new THREE.Vector3((pointsArray[5].x), pointsArray[5].y, 0)           
        ]);
    splineCurveOffsetLeft.type = 'catmullrom';
    splineCurveOffsetLeft.closed = false;
    
    var offsetGeometry = new THREE.Geometry();
    offsetGeometry.vertices = splineCurveOffsetLeft.getPoints(6);
    
    var offsetLine = new THREE.Line(offsetGeometry, cyanMaterial);
    
    offsetLine.rotation.set(-Math.PI/2, 0, 0);
    offsetLine.position.y=0.1;
    
    scene.add(offsetLine);
    

    I made the same for the other offset line just inverting its rotation and this is the result:

    Offset curve

    Green = Tangents, Red = Normals, Cyan = Parallel curve