Reputation: 59
I'm making a 2D scatterplot with a tooltip, and currently the raycaster to detect when a point is being hovered over is broken. The tooltip only activates when touching an object, which is correct behavior, but it shows completely random data from points that aren't even close on the x/y plane, and changes information even when there are no other points close to the one being hovered over. Can anyone help me debug this behavior? Here is some relevant code (the rest can be found in the link above):
...loading in points (stored in data_points array), creating scene, etc.
raycaster = new THREE.Raycaster();
raycaster.params.Mesh.threshold = 20;
view.on("mousemove", () => {
let [mouseX, mouseY] = d3.mouse(view.node());
let mouse_position = [mouseX, mouseY];
checkIntersects(mouse_position);
});
function mouseToThree(mouseX, mouseY) {
return new THREE.Vector3(
mouseX / viz_width * 2 - 1,
-(mouseY / height) * 2 + 1,
1
);
}
function checkIntersects(mouse_position) {
let mouse_vector = mouseToThree(...mouse_position);
raycaster.setFromCamera(mouse_vector, camera);
let intersects = raycaster.intersectObjects(scene.children, true);
if (intersects[0]) {
let sorted_intersects = sortIntersectsByDistanceToRay(intersects);
let intersect = sorted_intersects[0];
let index = intersect.faceIndex;
let datum = data_points[index];
showTooltip(mouse_position, datum);
} else {
hideTooltip();
}
}
function sortIntersectsByDistanceToRay(intersects) {
return _.sortBy(intersects, "distanceToRay");
}
...tooltip functions, details
Any help would be greatly appreciated. Thank you!
Upvotes: 1
Views: 663
Reputation: 59
Figured out the answer. There was not a proper "index" variable for my datapoints (THREE.Group consisting of [THREE.Mesh, THREE.LineLoop]), which is why the raycasting worked but not point selection (DON'T use faceIndex). So I created one under the userData field of the mesh.
// Create circle geometries
for (var i=0; i<data_points.length; i++) {
// Circle
let geo = new THREE.CircleBufferGeometry(data_points[i].radius, 32);
let mat = new THREE.MeshBasicMaterial( {color: color_array[data_points[i].label] } );
let mesh = new THREE.Mesh(geo, mat);
mesh.userData.id = i;
...lineLoop and Group code
}
...more code
function onMouseMove(event) {
mouseRay.x = ( event.clientX / renderer.domElement.clientWidth ) * 2 - 1;
mouseRay.y = - ( event.clientY / renderer.domElement.clientHeight ) * 2 + 1;
mouseRay.z = 1;
let mouse = [event.clientX, event.clientY];
raycaster.setFromCamera(mouseRay, camera);
let intersects = raycaster.intersectObjects(scene.children, true);
if (intersects[0]) {
let sorted_intersects = sortIntersectsByDistanceToRay(intersects);
console.log(sorted_intersects);
let intersect = sorted_intersects[0];
// Here is the change I made!!!
let index = intersect.object.userData.id;
let datum = data_points[index];
highlightPoint(datum);
showTooltip(mouse, datum);
} else {
removeHighlights();
hideTooltip();
}
}
Upvotes: 0
Reputation: 28462
Why are you using d3.mouse(view.node());
to get the mouse position? It looks like that's giving you wild results. When moving the pointer in a tiny space, I get an X range from 2200 to -97, when it should be a few pixels apart.
I recommend that on mousemove
you get the exact XY screen position by using the default JavaScript method of event.clientX
and event.clientY
See this example, taken directly from a Three.js Raycasting example
function onMouseMove( event ) {
mouse.x = ( event.clientX / renderer.domElement.clientWidth ) * 2 - 1;
mouse.y = - ( event.clientY / renderer.domElement.clientHeight ) * 2 + 1;
raycaster.setFromCamera( mouse, camera );
}
Also, I recommend removing document margins via CSS so your measurements aren't off by a few pixels.
Upvotes: 1