Is it possible to create a function grapher using the three.js API to plot a function in the form of z=f(x,y)? The program should:
Generate input values between -1 and 1 in increments of .1, and use these values to plot x, y, and z vertices as part of a mesh displayed graphically.
Implement mouse controls for rotating and scaling the mapped function using the mouse.
Include a plane and an axis helper to provide a reference point for the function, indicating the origin at x=0, y=0, z=0.
<script type="module">
import * as THREE from "https://cdn.skypack.dev/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="9de9f5eff8f8ddadb3acaeaeb3ac">[email protected]</a>";
import {OrbitControls}
// Global Variables
let scene, camera, cameraControls, renderer, axisHelper;
function init() {
scene = new THREE.Scene;
camera = new THREE.PerspectiveCamera(25, window.innerWidth/window.innerHeight, 1, 1000);
camera.position.set(-30, 50, -7);
const canvas = document.querySelector('#canvasElem');
renderer = new THREE.WebGLRenderer({canvas: canvas, antialias: true, opacity: 0.5, transparent: true, alpha: true});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
cameraControls = new THREE.OrbitControls(camera, renderer.domElement);
cameraControls.addEventListener("mousemove", renderer);
cameraControls.enablePan = false;
axisHelper = new THREE.AxesHelper;
scene.add(axisHelper);
window.addEventListener("resize", () => {
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
camera.aspect = window.innerWidth/window.innerHeight;
camera.updateProjectionMatrix()
});
// Constants for hyperbolic paraboloid
const a = 1.0;
const b = 1.0;
/**
* Compute z-position from X and Y positions for hyperbolic paraboloid.
* @param {*} u
* @param {*} v
* @param {*} w
*/
function ComputeZ(u,v,w) {
x = u * 2 - 1;
y = v * 2 - 1;
w.set(x,y,(x*x)/(a*a) - (y*y)/(b*b));
}
/**
* Get vertex colors function.
* @param {*} pointX
* @param {*} pointY
* @param {*} pointZ
* @returns
*/
function getVertexColors(pointX, pointY, pointZ) {
return new THREE.Color(pointX.x*0.5+0.5, pointY.y*0.5+0.5, pointZ.z*0.5+0.5);
}
/**
* Set vertex colors function.
* @param {*} geometry
*/
function setVertexColors(geometry) {
for (var i = 0; i < geometry.faces; i++) {
var face = geometry.faces[i];
face.vertexColors = [getVertexColors(geometry.vertices[face.a]),
getVertexColors(geometry.vertices[face.b]),
getVertexColors(geometry.vertices[face.c])];
}
}
/**
* Create a plane grid in the x-y direction.
* @param {number} size
* @param {number} steps
*/
function createPlaneGrid(size, steps) {
var group = new THREE.Group();
var material = new THREE.LineBasicMaterial({color:0x000000, transparent:true, opacity:0.25});
for (var i = 0; i <= steps; i+=1) {
var f = (i/steps)-0.5;
var geometry = new THREE.BufferGeometry();
const points = [
new THREE.Vector3( f*size, -size*0.5, 0 ),
new THREE.Vector3( f*size, size*0.5, 0 )
]
geometry.setFromPoints(points);
geometry.computeVertexNormals();
var axisX = new THREE.Line( geometry, material );
group.add( axisX );
}
for (var i = 0; i <= steps; i+=1) {
var f = (i/steps)-0.5;
var geometry = new THREE.BufferGeometry();
const points = [
new THREE.Vector3( -size*0.5, f*size, 0 ),
new THREE.Vector3( size*0.5, f*size, 0 ),
]
geometry.setFromPoints(points);
geometry.computeVertexNormals();
var axisY = new THREE.Line( geometry, material );
group.add( axisY );
}
return group;
}
var ParamGeometry = new THREE.ParametricGeometry(ComputeZ, 20, 20);
setVertexColors(ParamGeometry);
var ParaMaterial = new THREE.MeshBasicMaterial({color:0xffffff, side:THREE.DoubleSide, vertexColors: THREE.VertexColors});
var ParaMesh = new THREE.Mesh( ParamGeometry, ParaMaterial );
scene.add( ParaMesh );
var plane = createPlaneGrid(4,12);
scene.add( plane );
var directionalLight = new THREE.DirectionalLight( 0xffffff, 1.0 );
directionalLight.position.set(100,100,100);
directionalLight.castShadow = true;
directionalLight.shadow.camera.left = -100;
directionalLight.shadow.camera.bottom = -100;
directionalLight.shadow.camera.right = 100;
directionalLight.shadow.camera.top = 100;
directionalLight.shadow.camera.far = 1000;
scene.add( directionalLight );
}}
function animate(){
requestAnimationFrame(animate);
render();
}
function render(){
renderer.render(scene, camera);
}
init();
animate();
</script>
The graph is drawn, but the colors are not applied, resulting in a black and white shape. Various attempts to apply colors to the faces have been unsuccessful. Assistance is needed in resolving this issue.