Can we accurately pinpoint individual LineSegments in three.js by hovering over them, especially those with unique geometries?

Creating two examples of drawing lines in three.js was quite a fun challenge. One example showcased different geometry and material, while the other kept it simple with just one geometry and material. The demonstration links can be found here: Example 1 and Example 2.

In example 1, I enjoyed hovering over the lines to see the material color change on mouseover. However, in example 2, the whole lineMesh changed its color when hovered since there was only one material.

Now I'm intrigued by the thought of using just one geometry like in example 2, but still achieving the distinct hover effect seen in example 1 with multiple LineSegment pieces. Is this even possible?

An important consideration for me is that using multiple geometry seems to slow down the browser significantly, especially when rendering large models with numerous members. As I plan to incorporate multiple three.js models in different div elements on a page, I am concerned about the overall performance impact on the browser.

Answer №1

Utilizing THREE.BufferGeometry(), THREE.LineSegments(), and setting

.vertexColors = THREE.VertexColors
:

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);

// Define points for creating the geometry
var points = [
  new THREE.Vector3(-5, 5, 0),
  new THREE.Vector3(),
  new THREE.Vector3(),
  new THREE.Vector3(5, 5, 0),
  new THREE.Vector3(5, 5, 0),
  new THREE.Vector3(5, -5, 0)
];

// Create buffer geometry and set color attributes
var geom = new THREE.BufferGeometry().setFromPoints(points);
geom.addAttribute("color", new THREE.BufferAttribute(new Float32Array([/* colors */]), 3));
geom.addAttribute("colorBase", new THREE.BufferAttribute(new Float32Array([/* original colors */]), 3));

// Define material with vertex colors enabled
var mat = new THREE.LineBasicMaterial({
  vertexColors: THREE.VertexColors
});

// Create line segments and add to scene
var line = new THREE.LineSegments(geom, mat);
scene.add(line);

// Additional variables for interactivity and color manipulation
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
var intersects = [];
var oldIndex = -1;
var col = new THREE.Color();

// Event listener for mouse movement
window.addEventListener("mousemove", onMouseMove, false);

function onMouseMove(event) {
  // Update mouse position
}

// Helper function to highlight segment on hover
function highlightSegment(idx, color) {
  // Change color of segment
}

// Function to change color of a segment
function setColor(idx, color) {
  // Logic for changing color
}

// Restore original color of a segment
function restoreColor() {
  // Reset color to original value
}

// Render loop
render();

function render() {
  requestAnimationFrame(render);
  renderer.render(scene, camera);
}
body {
  overflow: hidden;
  margin: 0;
}
<script src="https://cdn.jsdelivr.net/npm/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="c8bca0baAdad88f8e6f9fafae6f8">[email protected]</a>/build/three.min.js"></script>

Answer №2

To achieve the desired effect, it is important to utilize vertex colors rather than relying solely on material colors. Here's how you can implement this:

var lineMaterial = new THREE.LineBasicMaterial({ vertexColors: THREE.VertexColors});

Each vertex should have its corresponding color pushed. For example:

geometry.colors.push(new THREE.Color("rgb(0, 0, 255)"), new THREE.Color("rgb(0, 0, 255)"));

In the mouse hover handler, you must retrieve the index of the intersected line and modify the color of that specific vertex like so:

intersects[0].object.geometry.colors[intersects[0].index].setHex(0xff0000);

let nextIndex = intersects[0].index + (intersects[0].index % 2 == 0 ? 1 : -1 );
intersects[0].object.geometry.colors[nextIndex].setHex(0xff0000);
intersects[0].object.geometry.colorsNeedUpdate = true;

Here's a simplified version based on your second approach:

     var container, scene, camera, renderer;
        var mouse = new THREE.Vector2();
        var intersected;

        // Initialization function
        function init() {
            /* Code snippet for initialization */
        }

        // Animation loop function
        function animate() {
            /* Code snippet for animation loop */
        }

        // Mouse move event handler
        function onMouseMove(event) {
            /* Code snippet for handling mouse movement */
        }

        // Rendering function
        function render() {
            /* Code snippet for rendering the scene */
        }
        
        // Add event listener for mouse movements
        container.addEventListener('mousemove', onMouseMove, false);

For an alternative method to the raycaster, GPU picking can be utilized to determine the intersected line.

Answer №3

If you want to change the appearance of a LineSegment using a single geometry, one workaround is to overlay an additional LineSegment dynamically on top of the segment you wish to highlight. This method should help create the desired visual effect you seek.

To implement this solution, you can generate a new material without utilizing depthTest:

material.depthTest=false

By doing so, the added LineSegment will always display above any geometries with materials featuring depthTest functionality.

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

Is that file or directory non-existent?

While working on developing a Discord bot, I keep encountering an error message stating: "Line 23: no such file or directory, open 'C:\Users\Owner\Desktop\Limited Bot\Items\Valkyrie_Helm.json']" even though the filep ...

The ion-input border seems to be fluctuating as the keyboard appears on the screen

I'm currently working with Ionic 3 and experiencing an issue where the selected ion-input's border in the ion-content is shifting when the device keyboard appears on the screen. Here is a visual representation of the problem: https://i.stack.imgu ...

How is it that the identical group is unable to produce an accurate line chart and its corresponding range chart?

Check out my code snippet on JSFiddle here: https://jsfiddle.net/8yf7j3k6/11/ I am currently working on replicating a similar visualization of my data for a range chart, allowing me to scrub through the chart while utilizing tooltips in the line chart rep ...

Angular 12 - Encountering an issue with undefined properties when trying to access 'values'

Currently in the process of upgrading Angular Version from 8 to 12. Successfully made the upgrade from 8 to 11 without any issues. However, upon updating Angular to version 12, encountered an error stating "loadComponent TypeError: Cannot read propert ...

Organize divs based on their attributes

Using inspiration from this SO post, my goal is to group divs based on their "message-id" attribute. The idea is to wrap all divs with the same "message-id" in a div with the class name "group". <div class="message" message-id="1"> ...

What is the best way to select the destination folder for output in Webpack?

Whenever I run webpack using "webpack --mode development", it generates a dist folder and places the bundle.js file inside it. My aim is to have it created and placed in the same directory instead. How can I achieve this? module.exports = { entry: " ...

Storing a Vue JS component as a string in a variable and displaying it using that variable

I am currently working on a project where I need to implement the following code snippet: <template v-for="(element, k) in place.data.content"> <data_elements[element.name].component></data_elements[element.name].component> </te ...

What could be causing the removal of style classes from my element after it is appended?

Could you please explain how it functions? I am attempting to append a div with content using innerHTML and it is working, but without any of the styling classes being applied. const element = document.createElement("div"); element.classList.add("col ...

The radial gradient in d3.js does not properly affect paths

My goal is to create a radial gradient on my path element, but for some reason the radial gradient does not apply correctly. Can anyone help me troubleshoot this issue? Below is my updated code: // Define the canvas / graph dimensions var margin = {top: ...

Having trouble invoking an established route within a different route in an Express JS project

While working with an Express JS application connected to a mySQL database, I encountered an issue when trying to fetch data from a pre-defined route/query: // customers.model.js CUSTOMERS.getAll = (result) => { let query = "SELECT * FROM custo ...

Error: Attempting to modify a constant value for property 'amount' within object '#<Object>'

After fetching data from an API, I stored an array in a state. Upon trying to update a specific field within an object inside the array using user input, I encountered the error message: 'Uncaught TypeError: Cannot assign to read only property 'a ...

retrieve all users who are not currently in the group

I'm currently struggling to retrieve all users who are not members of a particular group within a many-to-many association. After investing several hours into researching and experimenting, I've developed the following code. However, it falls sh ...

What is the best way to include the "onChange" function in a component that does not natively support this prop?

After finding a useful code snippet on this page to switch languages, I attempted to enhance it by incorporating material UI components for better styling. However, whenever I change the language, it redirects me back to the home page because the MenuList ...

The .ajaxSubmit function in jquery.form.js seems to be malfunctioning when using the data option

I'm currently working with the jQuery plugin jquery.form.js and I'm looking to programmatically submit a form while including a file. Although I've set up the code and options to work with $.ajax, it's not functioning with .ajaxSubmit. ...

What is the best way to retrieve an object within a class?

Exploring a Class Structure: export class LayerEditor { public layerManager: LayerManager; public commandManager: CommandManager; public tools: EditorTools; constructor() { this.commandManager = new CommandManager(); this.lay ...

What is the best way to navigate to a specific location on a web page?

After clicking the "Add comment" link, a comment form popped up using Ajax. I now need assistance with scrolling to it, can you please help me out? ...

Loading a series of images in advance using jQuery

I have a series of frames in an animation, with file names like: frame-1.jpg, frame-2.jpg, and I have a total of 400 images. My goal is to preload all 400 images before the animation begins. Usually, when preloading images, I use the following method: v ...

What steps can be taken to resolve the delay in transition when clicking on "read less

I implemented a transition effect for the Read More and Read Less buttons, however, there seems to be a delay on the transition when clicking on the Read Less button. This delay was not intentionally set by me. How can I resolve this issue and also apply a ...

Deciphering JSON data within AngularJS

When I retrieve JSON data in my controller using $http.get, it looks like this: $http.get('http://webapp-app4car.rhcloud.com/product/feed.json').success(function(data) The retrieved data is in JSON format and I need to access the value correspo ...

The filter functionality isn't functioning properly when attempting to filter an array of objects within a Vuex action

I have a collection of objects that is accessed through a getter. I am using this getter inside an action to filter the items, but no matter what I do, the filtering does not seem to work and it ends up returning all the mapped item IDs. filterItems({ gett ...