最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Fill Area between Lines with color - Three.js - Stack Overflow

programmeradmin0浏览0评论

I have series of lines, and I need to color all the area beneath or between lines with a color.

For the sake of simplicity, I have created a snippet with closed lines. In reality, i have a program that draws many lines (like a stock market graph) , and I need to color all the area below the graph. see also the screenshot.

would appreciate any help or even new approach that maybe I need to take.

<html>

<head>
    <title>My first three.js app</title>
    <style>
        body {
            margin: 0;
        }

        canvas {
            width: 100%;
            height: 100%
        }
    </style>
</head>

<body>
    <script src=".min.js"></script>
    <script>
        var scene = new THREE.Scene();
        var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 500);

        var renderer = new THREE.WebGLRenderer();
        renderer.setSize(window.innerWidth, window.innerHeight);
        document.body.appendChild(renderer.domElement);
        function createLine(startX,startY,endX,endY) {
            const geometry = new THREE.Geometry();
            geometry.vertices.push(new THREE.Vector3(startX, startY, 0));
            geometry.vertices.push(new THREE.Vector3(endX, endY, 0));
            const material = new THREE.LineBasicMaterial({
                color: 0xffffff
            });
            return new THREE.Line(geometry, material);
        }
        scene.add(createLine(0,0,1,0));
        scene.add(createLine(1,0,1,1));
        scene.add(createLine(1,1,0,1));
        scene.add(createLine(0,1,0,0));

        camera.position.z = 5;

        var animate = function () {
            requestAnimationFrame(animate);
            renderer.render(scene, camera);
        };

        animate();
    </script>
</body>

</html>

I have series of lines, and I need to color all the area beneath or between lines with a color.

For the sake of simplicity, I have created a snippet with closed lines. In reality, i have a program that draws many lines (like a stock market graph) , and I need to color all the area below the graph. see also the screenshot.

would appreciate any help or even new approach that maybe I need to take.

<html>

<head>
    <title>My first three.js app</title>
    <style>
        body {
            margin: 0;
        }

        canvas {
            width: 100%;
            height: 100%
        }
    </style>
</head>

<body>
    <script src="https://threejs.org/build/three.min.js"></script>
    <script>
        var scene = new THREE.Scene();
        var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 500);

        var renderer = new THREE.WebGLRenderer();
        renderer.setSize(window.innerWidth, window.innerHeight);
        document.body.appendChild(renderer.domElement);
        function createLine(startX,startY,endX,endY) {
            const geometry = new THREE.Geometry();
            geometry.vertices.push(new THREE.Vector3(startX, startY, 0));
            geometry.vertices.push(new THREE.Vector3(endX, endY, 0));
            const material = new THREE.LineBasicMaterial({
                color: 0xffffff
            });
            return new THREE.Line(geometry, material);
        }
        scene.add(createLine(0,0,1,0));
        scene.add(createLine(1,0,1,1));
        scene.add(createLine(1,1,0,1));
        scene.add(createLine(0,1,0,0));

        camera.position.z = 5;

        var animate = function () {
            requestAnimationFrame(animate);
            renderer.render(scene, camera);
        };

        animate();
    </script>
</body>

</html>

Share Improve this question edited Oct 22, 2017 at 7:06 Rabbid76 211k30 gold badges156 silver badges198 bronze badges asked Oct 21, 2017 at 21:04 LiranCLiranC 2,4802 gold badges30 silver badges56 bronze badges 3
  • I'm not too familiar with three.js, but in order to color a graph like you want, I would guess you need to draw one big shape, not a lot of separate lines. Or am I totally wrong? – agrm Commented Oct 21, 2017 at 21:12
  • don't know. that's the question. will be glad if you try and come up with solution :) I can of course "close" the graph with additional lines. that's why i attached the simple snippet which is 4 closed lines. to keep things simple. – LiranC Commented Oct 21, 2017 at 21:13
  • As an option, you can use THREE.PlaneGeometry() and change its upper vertices. – prisoner849 Commented Oct 22, 2017 at 9:51
Add a comment  | 

3 Answers 3

Reset to default 10

You can use THREE.Shape and THREE.ShapeGeometry to draw a filled polygon by THREE.Mesh:

function createPolygon( poly ) {
    var shape = new THREE.Shape();
    shape.moveTo( poly[0][0], poly[0][1] );
    for (var i = 1; i < poly.length; ++ i)
        shape.lineTo( poly[i][0], poly[i][1] );
    shape.lineTo( poly[0][0], poly[0][1] );

    var geometry = new THREE.ShapeGeometry( shape );
    var material = new THREE.MeshBasicMaterial( {
        color: 0x800000
    } );
    return new THREE.Mesh(geometry, material);
}

var poly = [[0,1],[0.25,0],[0.5,0.5],[0.75,0],[1,1]];
scene.add(createPolygon(poly))

See the example:

var renderer, scene, camera, controls;

init();
animate();

function createLine(startX,startY,endX,endY) {
    const geometry = new THREE.Geometry();
    geometry.vertices.push(new THREE.Vector3(startX, startY, 0));
    geometry.vertices.push(new THREE.Vector3(endX, endY, 0));
    const material = new THREE.LineBasicMaterial({
        color: 0xffffff
    });
    return new THREE.Line(geometry, material);
}

function createPolygon( poly ) {
  var shape = new THREE.Shape();
  shape.moveTo( poly[0][0], poly[0][1] );
  for (var i = 1; i < poly.length; ++ i)
    shape.lineTo( poly[i][0], poly[i][1] );
  shape.lineTo( poly[0][0], poly[0][1] );
    
  var geometry = new THREE.ShapeGeometry( shape );
    var material = new THREE.MeshBasicMaterial( {
        color: 0x800000
    } );
    return new THREE.Mesh(geometry, material);
}

function init() {

    // renderer
    renderer = new THREE.WebGLRenderer();
    renderer.setSize( window.innerWidth, window.innerHeight );
    document.body.appendChild( renderer.domElement );

    scene = new THREE.Scene();
    
    camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 1000 );
    //camera.position.set( 20, 20, 20 );
    camera.position.z = 5;
    window.onresize = resize;

    var light = new THREE.HemisphereLight( 0xeeeeee, 0x888888, 1 );
      light.position.set( 0, 20, 0 );
      scene.add( light );
    
    var poly = [[0,1],[0.25,0],[0.5,0.5],[0.75,0],[1,1]];
    
    scene.add(createPolygon(poly))
    
    for (var i = 0; i < poly.length-1; ++i)
    {
        var i2 = i<poly.length-1 ? i+1 : 0 
        scene.add(createLine(poly[i][0],poly[i][1],poly[i2][0],poly[i2][1]));
    }
}

function resize() {
    
    var aspect = window.innerWidth / window.innerHeight;
    renderer.setSize(window.innerWidth, window.innerHeight);
    camera.aspect = aspect;
    camera.updateProjectionMatrix();
}

function animate() {
    requestAnimationFrame( animate );
    renderer.render( scene, camera );
}
body {
    margin: 0;
}

canvas {
    width: 100%;
    height: 100%
}
<script src="https://cdn.jsdelivr.net/npm/[email protected]/build/three.js"></script>

To fill it with colour, you have to use a geometry with faces (THREE.Face3()). To have it, you need to trialngulate a shape or to use an existing geometry, which fits to your needs, with some changes of its vertices.

Below, there is a very rough concept, using an existing geometry (THREE.PlaneGeometry()), which colours the area below the graph. But keep in mind, that such approach is okay to use, when you need to show only positive or only negative values in one graph, otherwise, the result will look strange (or better to say, ugly).

var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(0, 2, 10);
var renderer = new THREE.WebGLRenderer({
  antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

var controls = new THREE.OrbitControls(camera, renderer.domElement);

var points = [],
  pointCount = 21;
var plane, line;

scene.add(new THREE.GridHelper( pointCount - 1, pointCount -1 ));

for (let i = 0; i < pointCount; i++) {
  points.push({
    initValue: THREE.Math.randFloat(-1, 1),
    amplitude: THREE.Math.randFloat(1, 2),
    speed: THREE.Math.randFloat(.5, 2)
  });
}

createGraph( pointCount );

function createGraph( pointCount ) {
  var planeGeom = new THREE.PlaneGeometry(pointCount - 1, 1, pointCount - 1, 1);
  planeGeom.translate(0, .5, 0);

  plane = new THREE.Mesh(planeGeom, new THREE.MeshBasicMaterial({
    color: "red",
    wireframe: false,
    side: THREE.DoubleSide,
    transparent: true,
    opacity: .75
  }));
  scene.add(plane);

  var lineGeom = new THREE.Geometry();
  for (let i = 0; i < plane.geometry.parameters.widthSegments + 1; i++) {
    lineGeom.vertices.push(planeGeom.vertices[i]); // share the upper points of the plane
  }
  line = new THREE.Line(lineGeom, new THREE.LineBasicMaterial({
    color: "aqua"
  }));
  plane.add(line);
}


var time = 0;

render();

function render() {
  time = Date.now() * .001;
  requestAnimationFrame(render);
  points.forEach( ( p, idx ) => {
    plane.geometry.vertices[idx].y = 2.5 + Math.sin( (time + p.initValue) * p.speed) * p.amplitude;  // the trick is that indices of upper vertices are from 0 to N consequently in row (r87),
    // thus we can assign the data from the `points` array to their Y-coordinates to form the graph
  });
  plane.geometry.verticesNeedUpdate = true; // the most important thing when you change coordiantes of vertices
  line.geometry.verticesNeedUpdate = true; // the most important thing when you change coordiantes of vertices
  renderer.render(scene, camera);
}
body {
  overflow: hidden;
  margin: 0;
}
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>

You need to create 1 big shape taking into account the whole graph you want to fill. So, just like in your image but then include the axis.

Then you can fill the shape with any kind of material.

Here are some examples on the threejs website:

https://threejs.org/examples/?q=shape#webgl_geometry_extrude_shapes2 https://threejs.org/examples/?q=shape#webgl_geometry_shapes

You probably wont need to extrude like in those examples.

发布评论

评论列表(0)

  1. 暂无评论