How Can I Convert a THREE.CatmullRomCurve3 to a Mesh?

3.8k Views Asked by At

I have created a line graph in 3D using THREE.js and the CatmullRomCurve3 class, which takes care of the curves and smoothing I wanted.

Unfortunately, now that I want to take that curve and turn it into a mesh, and perhaps extrude that mesh, it appears I can't. When I attempt something like:

    var geometry = new THREE.ShapeGeometry( curve );
    var material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
    var mesh = new THREE.Mesh( geometry, material ) ;

I get errors that prove a curve isn't quite what a geometry constructor wants:

 three.js:27731 Uncaught TypeError: shape.extractPoints is not a function
    at ExtrudeBufferGeometry.addShape (three.js:27731)
    at ExtrudeBufferGeometry.addShapeList (three.js:27656)
    at new ExtrudeBufferGeometry (three.js:27612)
    at new ChartTest (ChartTest.js:33)
    at createSceneSubjects (SceneManager.js:89)
    at new SceneManager (SceneManager.js:27)
    at main.js:16

I have seen examples where a curve is passed as an extrudePath parameter, such as:

var extrudeSettings = {
    steps           : 100,
    bevelEnabled    : false,
    extrudePath     : curve
};

but that requires a shape to be passed to ExtrudeGeometry along with extrudeSettings, but there has to be a more straightforward way than trying to create a separate shape, when the curve already defines the shape I want to extrude. (This also seems to extrude the path and not the shape it describes.)

Any help would be greatly appreciated!

2

There are 2 best solutions below

3
Mugen87 On BEST ANSWER

ShapeGeometry expects an instance of type THREE.Shape as a parameter, not an object of type THREE.Curve. Besides, THREE.CatmullRomCurve3 is a 3D curve whereas THREE.Shape is a 2D entity. This means that your shapes will always be flat. You can do the following to get things working:

Sample you curve first and then create an instance of THREE.Shape. Bear in mind that z-coordinates of the given points will be ignored.

const points = curve.getPoints( 64 );
const shape = new THREE.Shape( points );
const geometry = new THREE.ShapeGeometry( shape );

https://jsfiddle.net/f2Lommf5/3775/

three.js R91

0
prisoner849 On

In the case, if you want kind of a ribbon, here is a rough concept of how you can do it:

enter image description here

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

var pointsCount = 50;
var pointsCount1 = pointsCount + 1;
var points = curve.getPoints(pointsCount);

var pts = curve.getPoints(pointsCount);
var width = 2;
var widthSteps = 1;
let pts2 = curve.getPoints(pointsCount);
pts2.forEach(p => {
  p.z += width;
});
pts = pts.concat(pts2);

var ribbonGeom = new THREE.BufferGeometry().setFromPoints(pts);

var indices = [];
for (iy = 0; iy < widthSteps; iy++) { // the idea taken from PlaneBufferGeometry
  for (ix = 0; ix < pointsCount; ix++) {
    var a = ix + pointsCount1 * iy;
    var b = ix + pointsCount1 * (iy + 1);
    var c = (ix + 1) + pointsCount1 * (iy + 1);
    var d = (ix + 1) + pointsCount1 * iy;
    // faces
    indices.push(a, b, d);
    indices.push(b, c, d);
  }
}
ribbonGeom.setIndex(indices);
ribbonGeom.computeVertexNormals();

var ribbon = new THREE.Mesh(ribbonGeom, new THREE.MeshNormalMaterial({
  side: THREE.DoubleSide
}));
scene.add(ribbon);

var line = new THREE.Line(new THREE.BufferGeometry().setFromPoints(points), new THREE.LineBasicMaterial({
  color: "red",
  depthTest: false
}));
scene.add(line);

render();

function render() {
  requestAnimationFrame(render);
  renderer.render(scene, camera);
}
body {
  overflow: hidden;
  margin: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/91/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>