RoundedCornerLine

by Paul West

HTML

<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
<script>
  function roundedCornerLine(points, radius, smoothness, closed) {

    radius = radius !== undefined ? radius : .1;
    smoothness = smoothness !== undefined ? Math.floor(smoothness) : 3;
    closed = closed !== undefined ? closed : false;

    let newGeometry = new THREE.BufferGeometry();

    if (points === undefined) {
      console.log("RoundedCornerLine: 'points' is undefined");
      return newGeometry;
    }
    if (points.length < 3) {
      console.log("RoundedCornerLine: 'points' has insufficient length (should be equal or greater than 3)");
      return newGeometry.setFromPoints(points);
    }

    // minimal segment
    let minVector = new THREE.Vector3();
    let minLength = minVector.subVectors(points[0], points[1]).length();
    for (let i = 1; i < points.length - 1; i++) {
      minLength = Math.min(minLength, minVector.subVectors(points[i], points[i + 1]).length());
    }
    if (closed) {
      minLength = Math.min(minLength, minVector.subVectors(points[points.length - 1], points[0]).length());
    }

    radius = radius > minLength * .5 ? minLength * .5 : radius; // radius can't be greater than a half of a minimal segment

    let startIndex = 1;
    let endIndex = points.length - 2;
    if (closed) {
      startIndex = 0;
      endIndex = points.length - 1;
    }

    let positions = [];
    if (!closed) {
      positions.push(points[0].clone())
    };

    for (let i = startIndex; i <= endIndex; i++) {

      let iStart = i - 1 < 0 ? points.length - 1 : i - 1;
      let iMid = i;
      let iEnd = i + 1 > points.length - 1 ? 0 : i + 1;
      let pStart = points[iStart];
      let pMid = points[iMid];
      let pEnd = points[iEnd];

      // key points
      let vStartMid = new THREE.Vector3().subVectors(pStart, pMid).normalize();
      let vEndMid = new THREE.Vector3().subVectors(pEnd,...

CSS

body {
  overflow: hidden;
  margin: 0;
}

JavaScript

var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(0, 0, 20);
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 = [
  new THREE.Vector3(0, -2, -10),
  new THREE.Vector3(-10, -10, -10),
  new THREE.Vector3(-10, 5, -10),
  new THREE.Vector3(0, -5, 0),
  new THREE.Vector3(10, 5, 10),
  new THREE.Vector3(10, -5, 0)
];

var radius = 1;
var smoothness = 12;

var geom = roundedCornerLine(points, radius, smoothness, true);
var line = new THREE.Line(geom, new THREE.LineBasicMaterial({
  color: "yellow"
}));
scene.add(line);

var baseLine = new THREE.Line(new THREE.BufferGeometry().setFromPoints(points), new THREE.LineBasicMaterial({
  color: "aqua"
}));
scene.add(baseLine);

render();

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