JSFiddle - React, Tailwind, and code Playground

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 ProfiledContourGeometry(profileShape, contour, contourClosed) {
    
  	contourClosed = contourClosed !== undefined ? contourClosed : true;
		
    let profileGeometry = new THREE.ShapeBufferGeometry(profileShape);
    profileGeometry.rotateX(Math.PI * .5);
    let profile = profileGeometry.attributes.position;

    let profilePoints = new Float32Array(profile.count * contour.length * 3);

    for (let i = 0; i < contour.length; i++) {
      let v1 = new THREE.Vector2().subVectors(contour[i - 1 < 0 ? contour.length - 1 : i - 1], contour[i]);
      let v2 = new THREE.Vector2().subVectors(contour[i + 1 == contour.length ? 0 : i + 1], contour[i]);
      let angle = v2.angle() - v1.angle();
      let halfAngle = angle * .5;
			
      let hA = halfAngle;
      let tA = v2.angle() + Math.PI * .5;
      if (!contourClosed){
      	if (i == 0 || i == contour.length - 1) {hA = Math.PI * .5;}
        if (i == contour.length - 1) {tA = v1.angle() - Math.PI * .5;}
      }
      
      let shift = Math.tan(hA - Math.PI * .5);
      console.log(shift);
      let shiftMatrix = new THREE.Matrix4().set(
        1, 0, 0, 0, -shift, 1, 0, 0,
        0, 0, 1, 0,
        0, 0, 0, 1
      );
			
      
      let tempAngle = tA;
      let rotationMatrix = new THREE.Matrix4().set(
        Math.cos(tempAngle), -Math.sin(tempAngle), 0, 0,
        Math.sin(tempAngle), Math.cos(tempAngle), 0, 0,
        0, 0, 1, 0,
        0, 0, 0, 1
      );

      let translationMatrix = new THREE.Matrix4().set(
        1, 0, 0, contour[i].x,
        0, 1, 0, contour[i].y,
        0, 0, 1, 0,
        0, 0, 0, 1,
      );

      let cloneProfile = profile.clone();
   		shiftMatrix.applyToBufferAttribute(cloneProfile);
      rotationMatrix.applyToBufferAttribute(cloneProfile);
      translationMatrix.applyToBufferAttribute(cloneProfile);

     ...

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(5, 5, 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 light = new THREE.DirectionalLight(0xffffff, .75);
light.position.setScalar(10);
scene.add(light);
scene.add(new THREE.AmbientLight(0xffffff, .25));

var helper = new THREE.GridHelper(20, 20);
//helper.geometry.rotateX(Math.PI * -.5);
scene.add(helper);

var profileShape1 = new THREE.Shape();
profileShape1.moveTo(0, 0);
profileShape1.lineTo(0, 1);
profileShape1.absarc(1, 1, .5, Math.PI, Math.PI * 1.5);
profileShape1.lineTo(1, 0);

var contour1 = [
  new THREE.Vector2(0, 0),
  new THREE.Vector2(1, 1),
  new THREE.Vector2(2, 1),
  new THREE.Vector2(2, 0),
  new THREE.Vector2(1, -1)
];

var geometry1 = ProfiledContourGeometry(profileShape1, contour1, false);
geometry1.rotateX(-Math.PI * .5);
var fullProfile1 = new THREE.Mesh(geometry1, new THREE.MeshLambertMaterial({
  color: "red",
  wireframe: false
}));
scene.add(fullProfile1);

var profileShape2 = new THREE.Shape();
profileShape2.absarc(0, 0, 1, 0, Math.PI * 2);

var contour2 = [
	new THREE.Vector2(-4, 0),
  new THREE.Vector2(0, 5),
  new THREE.Vector2(5, 5),
  new THREE.Vector2(5, 0)
]

var geometry2 = ProfiledContourGeometry(profileShape2, contour2, false);
var fullProfile2 = new THREE.Mesh(geometry2, new THREE.MeshBasicMaterial({
  color: "aqua",
  wireframe: true
}));
fullProfile2.position.set(-7, 1, -5);
scene.add(fullProfile2);

var geometry2 = ProfiledContourGeometry(profileShape2, contour2, true);
var fullProfile2 = new THREE.Mesh(geometry2, new THREE.MeshBasicMaterial({
  color: "yellow",
  wireframe: true
}));
fullProfile2.position.set(7, 1, -5);
scene.add(fullProfile2);

render();

function render() {
 ...