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/curves/NURBSCurve.js"></script>
<script src="https://threejs.org/examples/js/curves/NURBSUtils.js"></script>
<script src="https://threejs.org/examples/js/libs/dat.gui.min.js"></script>
<script>
function getCurveGeometry(points, smoothness, weight) {
var nurbsControlPoints = [];
var nurbsKnots = [];
var nurbsDegree = 2;
for (let i = 0; i <= nurbsDegree; i++) {
nurbsKnots.push(0);
}
for (let i = 0, j = points.length; i < j; i++) {
nurbsControlPoints.push(
new THREE.Vector4(
points[i].x,
points[i].y,
points[i].z,
i === 1 ? weight : 1
)
);
let knot = (i + 1) / (j - nurbsDegree);
nurbsKnots.push(THREE.Math.clamp(knot, 0, 1));
}
let nurbsCurve = new THREE.NURBSCurve(nurbsDegree, nurbsKnots, nurbsControlPoints);
return new THREE.BufferGeometry().setFromPoints(nurbsCurve.getPoints(smoothness));
}
</script>
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, 10);
var renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var controlPoints = [],
currentPoint,
plane = new THREE.Plane(),
planeNormal = new THREE.Vector3(0, 0, 1),
dragging = false,
raycaster = new THREE.Raycaster(),
mouse = new THREE.Vector2(),
intersects,
intersectShift = new THREE.Vector3(),
planePoint = new THREE.Vector3(),
weightObj = {
weight: 1
};
var datGUI = new dat.GUI();
datGUI.add(weightObj, 'weight', -1, 2).onChange(setGeometry);
createPoint(new THREE.Vector3(-2, 0, 0), 0xff0000);
createPoint(new THREE.Vector3(0, 2, 0), 0x00ff00);
createPoint(new THREE.Vector3(2, 0, 0), 0x0000ff);
var curve = new THREE.Line(createCurve(), new THREE.LineBasicMaterial({
color: "aqua"
}));
scene.add(curve);
function createCurve() {
return getCurveGeometry([controlPoints[0].position, controlPoints[1].position, controlPoints[2].position], 12, weightObj.weight);
}
window.addEventListener("mousedown", onMouseDown, false);
function onMouseDown(event) {
setMouse(event);
raycaster.setFromCamera(mouse, camera);
intersects = raycaster.intersectObjects(controlPoints);
if (intersects.length === 0) return;
currentPoint = intersects[0].object;
currentPoint.worldToLocal(intersectShift.copy(intersects[0].point));
plane.setFromNormalAndCoplanarPoint(planeNormal, intersects[0].point);
dragging = true;
}
window.addEventListener("mouseup", onMouseUp, false);
function onMouseUp() {
dragging = false;
}
window.addEventListener("mousemove", onMouseMove, false);
function onMouseMove(event) {
if (!dragging) return;
setMouse(event);
raycaster.setFromCamera(mouse, camera);
raycaster.ray.intersectPlane(plane, planePoint);
...