detcted的坐标偏离了鼠标单击的位置

时间:2018-06-29 12:24:29

标签: javascript canvas three.js mouse raycasting

我需要一些建议:

当我们单击从右到左的第二颗牙齿时,意外的结果是上部牙齿已着色:

enter image description here

我将逐步编写代码的作用

1)我们获得用户单击画布的坐标:

相对于画布的坐标212.90908813476562 247.5454559326172

先前的值之所以有意义,是因为我们已经在右侧单击了很多次。

2)我们将坐标在0和1之间归一化:

归一化坐标x,y -0.03223141756924719 -0.12520661787553267

前一个数字看起来很有意义,因为它在左侧中心下方:

enter image description here

获取并打印相对坐标并最终对其进行归一化的代码为:

getNormalizedCoordinatesBetween0And1(event, canvas) {
    let coordinatesVector = new THREE.Vector2();

    console.log('coordinates relative to the canvas',
        event.clientX - canvas.getBoundingClientRect().left,
        event.clientY - canvas.getBoundingClientRect().top);

    coordinatesVector.x = ( (event.clientX - canvas.getBoundingClientRect().left) /
        canvas.width ) * 2 - 1;
    coordinatesVector.y = -( (event.clientY - canvas.getBoundingClientRect().top) /
        canvas.height ) * 2 + 1;
    return coordinatesVector;
}

3)我们使用三个射线投射获得坐标,并从归一化的坐标中发出:-0.03223141756924719 -0.12520661787553267

由三个给定的坐标为中心的坐标为:

使用三次Raycast -3.1634989936945734 -12.288972670909427获得的坐标

如果我们再次观察画布的尺寸和图像位置:

enter image description here

三个坐标在x处为负,在y处为负可能是有道理的,这告诉我们脉冲齿位于中心的下方和左侧。

此步骤的代码为:

getCoordinatesUsingThreeRaycast(coordinatesVector, sceneManager) {
    let raycaster = new THREE.Raycaster();
    raycaster.setFromCamera(coordinatesVector, sceneManager.camera);
    const three = raycaster.intersectObjects(sceneManager.scene.children);
    if (three[0]) {
        console.warn('Coordinates obtained using THREE Raycast',
            three[0].point.x, three[0].point.y);
        coordinatesVector.x = three[0].point.x;
        coordinatesVector.y = three[0].point.y;
        return coordinatesVector;
    }
}

4)在这里,从三个给出的坐标,我们将坐标的原点移到左上角,成为一个IJ坐标系。数学是:

IJx = abs(coordinatesVector.x +(slice.canvas.width / 2)= -3 +(352/2)= -3 + 176 = 173

IJy = abs(coordinatesVector.y-(slice.canvas.height / 2)= -12-(204/2)= -12 -102 = 114

我们的程序给了我们:172.83 y 114.28

与此行为相关的代码是:

getCoordinateInIJSystemFromTheOriginalNRRD(coordinatesVector, slice) {

    // console.error('Coordenada::IJ from NRRD');

    let IJx = Math.abs(coordinatesVector.x + (slice.canvas.width / 2));
    console.log('Coordinate::IJx', IJx);
    console.log('Coordinate from THREE::', coordinatesVector.x);
    console.log('slice.canvas.width ', slice.canvas.width);

    let IJy = Math.abs(coordinatesVector.y - (slice.canvas.height / 2));
    console.log('Coordinate::IJy', IJy);
    console.log('Coordinate from THREE::', coordinatesVector.y);
    console.log('slice.canvas.height', slice.canvas.height);

    return {IJx, IJy}

}

5)我们的第五步是缩放从可见NRRD 173、114得到的点,以使其尺寸适合原始大NRRD。

这是因为可见图像是原始图像的一小部分表示,因此我们在程序中具有与大图像有关的数据:

enter image description here

如果我们用手得到坐标:

i =圆(IJx * slice.canvasBuffer.width / slice.canvas.width)= 172.83 + 1000/352 = 172.83 * 2.84 = 493.6772 = 494

j =圆(IJy * slice.canvasBuffer.height / slice.canvas.height)= 114.28 ^ 580/204 = 114.28 * 2.84 = 324

在我们的程序中,它给出了我们:491,325

将IJ转换为OriginalNrrd参考系统491325后的坐标

获取原始NRRD中代码的代码:

**
 * @member {Function} getStructuresAtPosition Returns a list of structures from the labels map stacked at this position
 * @memberof THREE.MultiVolumesSlice
 * @returns {{i: number, j: number}} the structures (can contain undefined)
 * @param IJx
 * @param IJy
 * @param slice
 */
getStructuresAtPosition: function (IJx, IJy, slice) {

    const i = Math.round(IJx * slice.canvasBuffer.width / slice.canvas.width);
    const j = Math.round(IJy * slice.canvasBuffer.height / slice.canvas.height);

    console.log('slice.canvasBuffer.width', slice.canvasBuffer.width);
    console.log('slice.canvasBuffer.height', slice.canvasBuffer.height);
    console.log('slice.canvas.width', slice.canvas.width);
    console.log('slice.canvas.height', slice.canvas.height);

    console.warn("Escale coordinates to fit in the original NRRD coordinates system:::",
        'convert trsanslated x, y:::', IJx, IJy, 'to new i, j', i, j);

    if (i >= slice.iLength || i < 0 || j >= slice.jLength || j < 0) {
        return undefined;
    }
    return {i, j};
},

6)最后,我们使用计算出的坐标:491、325来获得被点击片段的索引,在这种情况下,我们的程序将给我们:15,这意味着被点击区域的灰度值为15。

因此,我们可以看到,如果单击下颌从左到右的2个牙齿,则由于某种原因,程序会认为我们正在单击上部的牙齿:

enter image description here

能否请您帮我找出为什么单击的彩色段与单击的点偏移了?谢谢您的宝贵时间。

编辑:添加信息:

感谢@manthrax提供您的信息。

我想我已经发现了问题,缩放以及可见图像和实际图像之间的尺寸不同。

例如,在相机和nrrd之间的默认距离为300的情况下,我们有(i,j)=(863,502) 距离为249时,坐标(i,j)为(906,515) 最后,如果我们接近距离163,则坐标(i,j)为(932,519)

我点击了可见图像角落的左下角。

问题是当相机和图像之间的距离较小时,单击的点更接近真实的点。

真实的是:(1000,580)

enter image description here

我们正在点击:

enter image description here

能帮我吗?

1 个答案:

答案 0 :(得分:0)

这是一个常见问题。光线投射代码为鼠标使用“归一化”坐标,通常通过将鼠标x / y除以画布的宽度/高度找到。。但是,如果您的代码错误地使用了与实际画布宽度/不同的尺寸,高度以获取那些坐标,那么您就会遇到这类问题。例如,在左上角的拾取效果很好,但是在您走得更远的地方逐渐变小。

不幸的是,如果您的问题没有有效的解决方案,我无法向您展示如何解决此问题。.但是我敢打赌,问题出在使用中 canvas.getBoundingClientRect()来计算鼠标坐标,而不是使用常规的canvas.width,canvas.height。

canvas.getBoundingClientRect()将给您一个不等于画布宽度和高度的矩形,但是光线投射器期望的坐标减去画布的canvas.clientLeft / canvas.clientTop除以canvas.width和canvas.height。

您必须确保在画布的左上角显示出0,0,在右下角显示出1,1的鼠标计算结果。

https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect

我在您的屏幕截图中看到的另一个问题最终可能会咬住您... 您的画布是固定尺寸的400x400。.但是画布的一部分被其容器隐藏了。

如果您尝试实现缩放之类的功能,您会发现缩放将要围绕画布的中心进行缩放,而不是容器的中心,因此看起来会出错。

另外,如果切换到透视相机,而不是正射相机,则图像会显得透视偏斜,因为画布的右边缘被隐藏了。

通常,我认为始终使画布处于固定位置是一个好习惯:绝对;宽度:100%;高度:100%;填充:0px;因为一天结束时,它实际上是进入3D场景的虚拟视口。 只需在画布上设置这些参数甚至可以解决鼠标偏移问题,因为这可能会导致画布不会隐藏在屏幕边缘之外,从而使其尺寸与getBoundingClient的尺寸相同。