Graphing functions with three.js

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:

  1. 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.

  2. Implement mouse controls for rotating and scaling the mapped function using the mouse.

  3. 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.

Answer №1

An illustration of a "plotter" featuring a distorted PlaneGeometry:

body{
  overflow: hidden;
  margin: 0;
}
<script type="module">
import * as THREE from "https://cdn.skypack.dev/three";
import {
  OrbitControls
} from "https://cdn.skypack.dev/three/examples/jsm/controls/OrbitControls.js";

let scene = new THREE.Scene();
let camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 0.1, 10);
camera.position.set(1, 1.5, 1).setLength(2.5);
camera.lookAt(scene.position);
let renderer = new THREE.WebGLRenderer({
  antialias: true
});
renderer.setSize(innerWidth, innerHeight);
renderer.setClearColor(0x161616);
document.body.appendChild(renderer.domElement);

let controls = new OrbitControls(camera, renderer.domElement);

let light = new THREE.DirectionalLight(0xffffff, 1);
light.position.setScalar(1);
scene.add(light, new THREE.AmbientLight(0xffffff, 0.5));

let grid = new THREE.GridHelper(2, 20, 0xffff00, 0xffff00);
grid.position.y = -0.001;
scene.add(grid, new THREE.AxesHelper(1));

let graphGeom = new THREE.PlaneGeometry(2, 2, 20, 20);
graphGeom.rotateX(Math.PI * -0.5);
let graphMat = new THREE.MeshNormalMaterial({side: THREE.DoubleSide, wireframe: false});
let graph = new THREE.Mesh(graphGeom, graphMat);

// f(x,z)
let pos = graphGeom.attributes.position;
for(let i = 0; i < pos.count; i++){
    let x = pos.getX(i);
  let z = pos.getZ(i);
    pos.setY(i, Math.sin(x * z * Math.PI) * Math.cos(z * z * Math.PI * 0.5) * 0.75);
}
graphGeom.computeVertexNormals();

scene.add(graph);

window.addEventListener("resize", onResize);

renderer.setAnimationLoop(_ => {
  renderer.render(scene, camera);
})

function onResize(event) {
  camera.aspect = innerWidth / innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(innerWidth, innerHeight);
}

</script>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Mocking objects in unit test cases to simulate real-life scenarios and test the

How do I pass a mocked event object and ensure it is validated? onCallFunction() { const eventValue = event; if (!eventValue.relatedTarget || !eventValue.relatedTarget.last.contain('value')) { super.onCallFuncti ...

Can a JavaScript file be imported exclusively for a Vue component?

When attempting to utilize the table component in the vue-ant framework, I am facing an issue. I am only looking to import the table style, but when I try to import the table style using import 'ant-design-vue/lib/table/style/css', it affects all ...

Flashing tilemap during the update process

I'm attempting to create a game map on a canvas using a JSON file produced by tiled map editor. I believe I am close to accomplishing this, but I encounter one issue. When I include the call to load the map in my update function, the map flickers on ...

The AngularJS error message [$rootScope:infdig] is triggered when the number of $digest() iterations exceeds 10 due to a window

It's evident that changing window.location.href/reload to $location.path() resolves the issue. However, using $location.path breaks the app in IE but works in Chrome with window. The purpose behind this is to update a site when a user logs in or out, ...

Code snippet for calculating the size of an HTML page using JavaScript/jQuery

Does anyone know of a way to calculate and display the size/weight (in KB) of an HTML page, similar to what is done here: Page size: 403.86KB This would include all resources such as text, images, and scripts. I came across a Pelican plugin that does th ...

Determine the minimum and maximum width of jQuery UI resizable during the "resizestart" event

As a newcomer to Javascript, I am facing challenges navigating my way around. Currently, I am attempting to create a jQuery plugin that will facilitate resizing elements using the jQuery UI resizable plugin. My goal is to implement logic that dynamically ...

Extension for capturing videos on Chrome or Firefox

I am interested in developing a Chrome or Firefox extension that can capture video from a window or tab. My goal is to record full screen videos, such as those on YouTube, for offline viewing similar to a DVR for online content. Creating an extension see ...

Fixing TypeError in React App: How to Resolve the "Cannot read property 'prototype' of undefined" Issue

I am completely new to JavaScript and I am struggling to understand the error that keeps popping up. After doing some research, it seems like the error could be due to a poorly written function or something along those lines. Here are the classes involved ...

What is the best way to create a text shadow effect for a heading element?

Is it possible to create a centered heading like the one in this image using only CSS, without using flexbox? [EDIT] Below is an example using flexbox. However, there are some limitations with this code as it doesn't allow reusing the same class for ...

Can an identification be included in a label element?

My inquiry is as follows: <label for="gender" class="error">Choose</label> I am interested in dynamically adding an id attribute to the above line using jQuery or JavaScript, resulting in the following html: <label for="gender" class="err ...

What is the best method for choosing the next item with jQuery?

I am facing an issue while trying to apply some design on the next element. The error message that I am encountering is: Error: Syntax error, unrecognized expression: [object Object] > label Below are my selections for browsing by category: BROWSE BY ...

I am unable to view the map on my webpage. This issue only arises when I apply a CSS style to it

Hey there! I'm having trouble displaying a map on my website. For some reason, the map is not showing up even after updating the Google secret key in my code: <?php session_start(); include '../../WSweb/serv.php'; if(isset($_SESSION[&a ...

Is Socket.io exclusive to browsers?

Similar Question: Using socket.io standalone without node.js How to run socket.io (client side only) on apache server My website is hosted on a Linux server with shared hosting. Since I don't have the ability to install node.js, I am looking ...

Is the size of the JSON file inhibiting successful parsing?

After retrieving a large list of schools with their respective columns from the database, totaling over 1000 rows, I converted it to JSON and passed it to my view. I then attempted to parse it using $.parseJSON('@Html.Raw(Model.subChoiceJsonString)& ...

What are some ways to enable text highlighting when it is disabled?

I've encountered an issue with text highlighting in my asp.net web application when using the latest versions of FireFox and Google Chrome. Strangely, pressing CTRL+A also does not work for all input fields. I haven't had the opportunity to test ...

Toggle the slide when you hit submit

I'm considering the functionality of my code. I am interested in creating a slide toggle effect on a div or span when hovering over an input submit button. Here is an example: https://i.sstatic.net/pBSK8.png What is the best approach to achieve thi ...

TypeORM reporting duplication error when bulk saving data instead of detecting and ignoring existing records or updating their values

According to the documentation provided by TypeOrm Framework, the Repository.save function is supposed to save/insert new values and ignore/update existing ones. However, I am currently experiencing an issue where it is throwing a duplication error for an ...

Innovative: Enhancing column width dynamically without compromising grid integrity

Currently, I am utilizing Bourbon's Neat library to structure my grid system. Within my code, I have the following setup: section { @include outer-container; aside { @include span-columns(3); } article { @include span-columns(9); } } The chal ...

Customizing Background Image Opacity in MuiCssBaseline

I recently tried to set a background image for my demo application built with React, Next, and Material-UI. In my _app.js file, I included the following code: import React from 'react'; import { ThemeProvider } from '@material-ui/core/styles ...

Efficient communication: sending emails using AngularJS and PHP

I have developed an innovative application using AngularJS that features an email contact form which communicates with a PHP file on the server to send emails. Here is a snippet from my Controller.js file in AngularJS: $scope.feedbacksubmit= function (){ ...