[Solved] How to get spheres in selected area?

Here I get a 3D model with a sphere as shown in the figure. My goal is to use a selection area to turn the spheres in this area red. However, while doing this, I cannot paint the spheres in the exact area I selected.

Spheres in straight alignment in the area I have chosen should be painted. In other words, if the x,y axis matches, all spheres in the z axis must be red.

image

As can be seen in this image, the area I selected from above decreases downwards. However, it should land straight and every sphere within the area should be red.
Camera:

 const camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000); 

This is how to create select area box and color spheres:

    // Select area and spheres
    const raycaster = new THREE.Raycaster();
    const mouse = new THREE.Vector2();
    var selectedMeshes = [];
    var selectionBox = document.getElementById("sel_box");
    let isOrbitControlEnabled = true;
    document.getElementById('forceareaadd').addEventListener('click', onPickAreaClick, false);
    var mouseDownCoords = { x: 0, y: 0 };
    var mouseUpCoords = { x: 0, y: 0 };

    function onPickAreaClick() {
        isOrbitControlEnabled = !isOrbitControlEnabled;
        controls.enabled = isOrbitControlEnabled;
        if (!isOrbitControlEnabled) {

        }
    }

    document.getElementById('clearselectedsphere').addEventListener('click', clearSelectedSpheres, false);

    // Function to clear selected spheres
    function clearSelectedSpheres() {
        // Iterate through selectedMeshes and reset their colors
        selectedMeshes.forEach((sphere) => {
            sphere.material.color.set(0x00ff00);
        });

        // Clear the selectedMeshes array
        selectedMeshes = [];
        console.log("selected meshes after clear: " + selectedMeshes);
    }

    function getCursorPosition(e) {
        e = e || window.event;
        if (e) {
            if (e.pageX || e.pageX == 0) return [e.pageX, e.pageY];
                var dE = document.documentElement || {};
                var dB = document.body || {};
            if ((e.clientX || e.clientX == 0) && ((dB.scrollLeft || dB.scrollLeft == 0) || (dE.clientLeft || dE.clientLeft == 0))) return [e.clientX + (dE.scrollLeft || dB.scrollLeft || 0) - (dE.clientLeft || 0), e.clientY + (dE.scrollTop || dB.scrollTop || 0) - (dE.clientTop || 0)];
        }
        return null;
    }

    function mousedown(e) {
        if (!isOrbitControlEnabled) {
            var mxy = getCursorPosition(e);
            var box = document.getElementById("sel_box");
            mouseDownCoords.x = mxy[0];
            mouseDownCoords.y = mxy[1];
            box.orig_x = mxy[0];
            box.orig_y = mxy[1];
            box.style.left = mxy[0] + "px";
            box.style.top = mxy[1] + "px";
            box.style.display = "block";
            document.onmousemove = mousemove;
            document.onmouseup = mouseup;
            console.log("down x y: " + mouseDownCoords.x + " " + mouseDownCoords.y);
        }
    }

    function mousemove(e) {
        if (!isOrbitControlEnabled) {
            var mxy = getCursorPosition(e);
            var box = document.getElementById("sel_box");
            if (mxy[0] - box.orig_x < 0) {
                box.style.left = mxy[0] + "px";

            }
            if (mxy[1] - box.orig_y < 0) {
                box.style.top = mxy[1] + "px";
            }
            box.style.width = Math.abs(mxy[0] - box.orig_x) + "px";
            box.style.height = Math.abs(mxy[1] - box.orig_y) + "px";
        }
    }

    function mouseup(e) {
        if (!isOrbitControlEnabled) {
            var box = document.getElementById("sel_box");
            var mxy = getCursorPosition(e);
            box.style.display = "none";
            box.style.width = "0";
            box.style.height = "0";
            document.onmousemove = null;
            document.onmouseup = null;
    
            mouseUpCoords.x = mxy[0];
            mouseUpCoords.y = mxy[1];
            console.log("up x y: " + mouseUpCoords.x + " " + mouseUpCoords.y);
    
            const designPartContainer = document.querySelector('.design-part');
            const containerRect = designPartContainer.getBoundingClientRect();
    
            // Account for potential scroll offsets
            const scrollX = window.scrollX || window.pageXOffset;
            const scrollY = window.scrollY || window.pageYOffset;
    
            // Calculate the depth range based on the camera's near and far planes
            const near = camera.near;
            const far = camera.far;
    
            spheres.forEach((sphere) => {
                const spherePosition = sphere.position.clone();
                const screenPosition = spherePosition.clone().project(camera);
    
                // Convert screen position to DOM coordinates within the design-part class
                const domX = (screenPosition.x + 1) / 2 * containerRect.width + containerRect.left + scrollX;
                const domY = (-screenPosition.y + 1) / 2 * containerRect.height + containerRect.top + scrollY;
    
                // Check if the sphere is within the selection box and within the depth range
                const isInsideSelectionBox =
                    domX >= Math.min(mouseDownCoords.x, mouseUpCoords.x) &&
                    domX <= Math.max(mouseDownCoords.x, mouseUpCoords.x) &&
                    domY >= Math.min(mouseDownCoords.y, mouseUpCoords.y) &&
                    domY <= Math.max(mouseDownCoords.y, mouseUpCoords.y);
    
                const isWithinDepthRange = screenPosition.z >= 0 && screenPosition.z <= 1;
    
                if (isInsideSelectionBox && isWithinDepthRange) {
                    console.log("sphere x y: " + domX + " " + domY);
                    sphere.material.color.set(0xff0000); // Set to red color
                    selectedMeshes.push(sphere);
                }
            });
    
            // Center the camera on the selected area
            const centerX = (mouseDownCoords.x + mouseUpCoords.x) / 2;
            const centerY = (mouseDownCoords.y + mouseUpCoords.y) / 2;
    
            const centerScreenPosition = new THREE.Vector3(
                (centerX - containerRect.left - scrollX) / containerRect.width * 2 - 1,
                -(centerY - containerRect.top - scrollY) / containerRect.height * 2 + 1,
                0.5 // Adjust this value based on the depth range of your scene
            );
    
            const centerWorldPosition = centerScreenPosition.unproject(camera);
            camera.lookAt(centerWorldPosition);
        }
    }
    
    document.onmousedown = mousedown;

Box Selection, could be a good starting point! If it’s not the exact behavior you’re looking for, you can always customize it.

Thank you so much, it looks useful for my case. I’m gonna try it.

I worked on with this example. It is better but not enough for my case. I have the same problem here. I think, using raycaster is not work for this case. Any option except raycaster?

I finally solved the problem. I’ve changed camera type PerspectiveCamera() to OrthographicCamera(). After that, i can get all spheres that in the selected box area. And this is really helped me: three-mesh-bvh - Geometry Collect Triangles