JSFiddle - React, Tailwind, and code Playground
by ozinfo
HTML
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
<script>
function ProfiledTubularGeometry(profileShape, contour, contourClosed, openEnded) {
contourClosed = contourClosed !== undefined ? contourClosed : true;
openEnded = openEnded !== undefined ? openEnded : false;
openEnded = contourClosed === true ? false : openEnded;
let profileGeometry = new THREE.ShapeBufferGeometry(profileShape);
let flipProfileGeometry = flipShapeGeometry(profileGeometry);
//profileGeometry.rotateX(Math.PI * 0.5);
let profile = profileGeometry.attributes.position;
let addEnds = openEnded === false ? 2 : 0;
let profilePoints = new Float32Array(profile.count * (contour.length + addEnds) * 3);
let endProfiles = [];
for (let i = 0; i < contour.length; i++) {
let prevP = contour[i - 1 < 0 ? contour.length - 1 : i - 1];
let currP = contour[i];
let nextP = contour[i + 1 == contour.length ? 0 : i + 1];
let v1 = new THREE.Vector3().subVectors(prevP, currP);
let v2 = new THREE.Vector3().subVectors(nextP, currP);
let midV = new THREE.Vector3().addVectors(v1, v2).multiplyScalar(0.5);
let midNormal = new THREE.Vector3();
let tri = new THREE.Triangle(prevP, currP, nextP);
tri.getNormal(midNormal);
midV.applyAxisAngle(midNormal, Math.PI * 0.5);
let angle = v2.angleTo(v1);
let halfAngle = angle * 0.5;
let hA = halfAngle;
if (!contourClosed){
if (i == 0 || i == contour.length - 1) {hA = 0;}
}
let shift = Math.tan(hA);
let shiftMatrix = new THREE.Matrix4().set(
1, 0, 0, 0,
0, 1, shift, 0,
0, 0, 1, 0,
0, 0, 0, 1
);
if (!contourClosed){
if (i === 0) midV.copy(v2).negate();
if (i === contour.length - 1) midV.copy(v1);
}
...
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(-50, 80, 100);
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, 0.75);
light.position.setScalar(10);
scene.add(light);
scene.add(new THREE.AmbientLight(0xffffff, 0.25));
var helper = new THREE.GridHelper(20, 20);
//helper.geometry.rotateX(Math.PI * -.5);
scene.add(helper);
var profileShape = new THREE.Shape();
profileShape.absarc(0, 0, 3, 0, Math.PI * 2);
var tubularSegments = [
new THREE.Vector3(-4, 0, -1),
new THREE.Vector3(0, 5, -2),
new THREE.Vector3(5, 5, 1),
new THREE.Vector3(5, 0, 2),
new THREE.Vector3(6, -2, 6),
new THREE.Vector3(-40, 0, 0),
new THREE.Vector3(0, 50, 0),
new THREE.Vector3(50, 50, 0),
new THREE.Vector3(50, 0, 0),
new THREE.Vector3(60, -20, 0),
new THREE.Vector3(-60, -20, 0),
new THREE.Vector3(-50, 70, 0),
new THREE.Vector3(100, 70, 0),
new THREE.Vector3(100, -80, 0),
new THREE.Vector3(-100, -80, 0),
new THREE.Vector3(-100, 100, 0),
new THREE.Vector3(150, 100, 0)
];
var geometry = ProfiledTubularGeometry(profileShape, tubularSegments, false, true);
var fullProfile = new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({
color: "green",
wireframe: true
}));
//fullProfile.position.set(-7, 1, -5);
scene.add(fullProfile);
var line = new THREE.Line(new THREE.BufferGeometry().setFromPoints(tubularSegments), new THREE.LineBasicMaterial({color: "yellow", depthTest: false}));
scene.add(line);
render();
function render() {
requestAnimationFrame(render);
renderer.render(scene, camera);
}