three-ts-types icon indicating copy to clipboard operation
three-ts-types copied to clipboard

Fix parameter type in Raycaster::intersectObject(s)

Open Degubi opened this issue 3 years ago • 3 comments

Why

#130 added generic typing to the Intersection type, so that we could get back the same type that we passed in to the intersect methods. That pr missed the Raycaster::intersectObject(s)'s 'object' & 'objects' parameters. This problem causes the following:

const raycaster: Raycaster = null;
const mesh: Mesh = null;
const meshes: Mesh[] = null;

const result1: Mesh = raycaster.intersectObject(mesh)[0].object;
const result2: Mesh = raycaster.intersectObjects(meshes)[0].object;

This code doesn't compile because the type of result1 is still not a Mesh, it's a Object3D<Event>. This is because according to the current typing we pass in a Object3D and we get back an Object3D. After these changes the given code compiles without any errors.

What

Made the 'object' and 'objects' parameters generic in intersectObject & intersectObjects

Checklist

  • [x] Checked the target branch (current goes master, next goes dev)
  • [x] Added myself to contributors table
  • [ ] Ready to be merged

Degubi avatar Apr 19 '22 10:04 Degubi

The test is failing in test/cameras/camera-cinematic.ts. It looks like it calls intersectObjects like this:

    const intersects = raycaster.intersectObjects<THREE.Mesh>(scene.children);

scene.children has the type THREE.Object3D<THREE.Event>[], which should give us back a THREE.Object3D<THREE.Event>, not a THREE.Mesh This could be fixed like this:

const intersects = raycaster.intersectObjects(scene.children);

    if (intersects.length > 0) {
        const targetDistance = intersects[0].distance;
        const intersectedObject = intersects[0].object as THREE.Mesh;

        camera.focusAt(targetDistance); // using Cinematic camera focusAt method

        if (INTERSECTED && INTERSECTED.uuid !== intersectedObject.uuid) {
            if (INTERSECTED) {
                (INTERSECTED.material as THREE.MeshLambertMaterial).emissive.setHex(INTERSECTED.userData.currentHex);
            }

            INTERSECTED = intersectedObject;
...

Here we explicitly cast intersects[0].object to a THREE.Mesh object I think this might become a breaking change, because with the previous typing the given example would compile with the wrong passed in object type, because it wasn't validated.

Degubi avatar Apr 19 '22 11:04 Degubi

Can you provide a codesandbox of this breaking?

joshuaellis avatar May 02 '22 11:05 joshuaellis

Hi! Sorry, I couldn't figure out how to show the error on codesandbox.io The following code

import { Raycaster, Mesh } from 'three';

const raycaster = new Raycaster();
const meshes: Mesh[] = [ new Mesh() ];

const result: Mesh = raycaster.intersectObjects(meshes)[0]?.object;

Produces this error in vscode: 'Type 'Object3D<Event>' is missing the following properties from type 'Mesh<BufferGeometry, Material | Material[]>'...' (This basically means that we get back an Object3D, which is incorrect because we pass in a mesh[], which should give us back a Mesh, not an Object3D)

Now if we simplify the code in the tests:

import { Raycaster, Scene, Mesh } from 'three';

const raycaster = new Raycaster();
const scene = new Scene();

const intersects: Mesh = raycaster.intersectObjects<Mesh>(scene.children)[0]?.object;

Here we pass in scene.children which contains Object3D objects, but we get back a Mesh object, because we call raycaster.intersectObjects with an explicit type parameter, which is incorrect after the changes (thus the test failing correctly)

Degubi avatar Jul 05 '22 08:07 Degubi