简体   繁体   English

如何在非轴对齐框(任意旋转的框)内查找点的相对偏移

[英]How to find Relative Offset of a point inside a non axis aligned box (box that is arbitrarily rotated)

I'm trying to solve an problem where I cannot find the Relative Offset of a Point inside a Box that exists inside of a space that can be arbitrarily rotated and translated.我正在尝试解决一个问题,即我无法在可以任意旋转和平移的空间内找到一个盒子内的点的相对偏移量。

I know the WorldSpace Location of the Box (and its 4 Corners, the Coordinates on the Image are Relative) as well as its Rotation.我知道盒子的 WorldSpace 位置(及其 4 个角,图像上的坐标是相对的)及其旋转。 These can be arbitrary (its actually a 3D Trigger Volume within a game, but we are only concerned with it in a 2D plane from top down).这些可以是任意的(它实际上是游戏中的 3D 触发音量,但我们只关心它在自上而下的 2D 平面中)。

在此处输入图像描述

Looking at it Aligned to an Axis the Red Point Relative position would be看着它与轴对齐,红点相对 position 将是

0.25, 0.25 0.25, 0.25

If the Box was to be Rotated arbitrarily I cannot seem to figure out how to maintain that given we sample the same Point (its World Location will have changed) its Relative Position doesnt change even though the World Rotation of the Box has.如果要任意旋转盒子,我似乎无法弄清楚如何保持这一点,因为我们采样了相同的点(它的世界位置将会改变),即使盒子的世界旋转已经改变,它的相对 Position 也不会改变。

在此处输入图像描述

For reference, the Red Point represents an Object that exists in the scene that the Box is encompassing.作为参考,红点表示存在于 Box 所包围的场景中的 Object。

bool UPGMapWidget::GetMapMarkerRelativePosition(UPGMapMarkerComponent* MapMarker, FVector2D& OutPosition)
{
    bool bResult = false;
    if (MapMarker)
    {
        const FVector MapMarkerLocation = MapMarker->GetOwner()->GetActorLocation();
        float RelativeX = FMath::GetMappedRangeValueClamped(
            -FVector2D(FMath::Min(GetMapVolume()->GetCornerTopLeftLocation().X, GetMapVolume()->GetCornerBottomRightLocation().X), FMath::Max(GetMapVolume()->GetCornerTopLeftLocation().X, GetMapVolume()->GetCornerBottomRightLocation().X)),
            FVector2D(0.f, 1.f),
            MapMarkerLocation.X
        );

        float RelativeY = FMath::GetMappedRangeValueClamped(
            -FVector2D(FMath::Min(GetMapVolume()->GetCornerTopLeftLocation().Y, GetMapVolume()->GetCornerBottomRightLocation().Y), FMath::Max(GetMapVolume()->GetCornerTopLeftLocation().Y, GetMapVolume()->GetCornerBottomRightLocation().Y)),
            FVector2D(0.f, 1.f),
            MapMarkerLocation.Y
        );
        
        OutPosition.X = FMath::Abs(RelativeX);
        OutPosition.Y = FMath::Abs(RelativeY);

        bResult = true;
    }

    return bResult;
}

Currently, you can see with the above code that im only using the Top Left and Bottom Right corners of the Box to try and calculate the offset, I know this is not a sufficient solution as doing this does not allow for Rotation (Id need to use the other 2 corners as well) however I cannot for the life of me work out what I need to do to reach the solution.目前,您可以通过上面的代码看到我只使用 Box 的左上角和右下角来尝试计算偏移量,我知道这不是一个足够的解决方案,因为这样做不允许旋转(Id 需要也使用其他两个角)但是我一生都无法弄清楚我需要做些什么来达到解决方案。

FMath::GetMappedRangeValueClamped

This converts one range onto another.这会将一个范围转换为另一个范围。 (20 - 50) becomes (0 - 1) for example.例如,(20 - 50) 变为 (0 - 1)。

Any assistance/advice on how to approach this problem would be much appreciated.任何有关如何解决此问题的帮助/建议将不胜感激。

Thanks.谢谢。

UPDATE更新

@Voo's comment helped me realize that the solution was much simpler than anticipated. @Voo 的评论帮助我意识到解决方案比预期的要简单得多。

在此处输入图像描述

By knowing the Location of 3 of the Corners of the Box, I'm able to find the points on the 2 lines these 3 Locations create, then simply mapping those points into a 0-1 range gives the appropriate value regardless of how the Box is Translated.通过知道盒子的 3 个角的位置,我能够找到这 3 个位置创建的 2 条线上的点,然后简单地将这些点映射到 0-1 范围内,无论盒子如何被翻译。

bool UPGMapWidget::GetMapMarkerRelativePosition(UPGMapMarkerComponent* MapMarker, FVector2D& OutPosition)
{
    bool bResult = false;
    if (MapMarker && GetMapVolume())
    {
        const FVector MapMarkerLocation = MapMarker->GetOwner()->GetActorLocation();
        const FVector TopLeftLocation = GetMapVolume()->GetCornerTopLeftLocation();
        const FVector TopRightLocation = GetMapVolume()->GetCornerTopRightLocation();
        const FVector BottomLeftLocation = GetMapVolume()->GetCornerBottomLeftLocation();

        FVector XPlane = FMath::ClosestPointOnLine(TopLeftLocation, TopRightLocation, MapMarkerLocation);
        FVector YPlane = FMath::ClosestPointOnLine(TopLeftLocation, BottomLeftLocation, MapMarkerLocation);

        // Convert the X axis into a 0-1 range.
        float RelativeX = FMath::GetMappedRangeValueUnclamped(
            FVector2D(GetMapVolume()->GetCornerTopLeftLocation().X, GetMapVolume()->GetCornerTopRightLocation().X),
            FVector2D(0.f, 1.f),
            XPlane.X
        );

        // Convert the Y axis into a 0-1 range.
        float RelativeY = FMath::GetMappedRangeValueUnclamped(
            FVector2D(GetMapVolume()->GetCornerTopLeftLocation().Y, GetMapVolume()->GetCornerBottomLeftLocation().Y),
            FVector2D(0.f, 1.f),
            YPlane.Y
        );

        OutPosition.X = RelativeX;
        OutPosition.Y = RelativeY;

        bResult = true;
    }

    return bResult;
}

The above code is the amended code from the original question with the correct solution.上面的代码是原始问题的修改代码,具有正确的解决方案。

assume the origin is at (x0, y0) , the other three are at (x_x_axis, y_x_axis) , (x_y_axis, y_y_axis) , (x1, y1) , the object is at (x_obj, y_obj)假设原点在(x0, y0) ,其他三个在(x_x_axis, y_x_axis) , (x_y_axis, y_y_axis) , (x1, y1) ,object 在(x_obj, y_obj)

do these operations to all five points:对所有五点进行这些操作:
(1)translate all five points by (-x0, -y0) , to make the origin moved to (0, 0) (after that (x_x_axis, y_x_axis) is moved to (x_x_axis - x0, y_x_axis - y0) ); (1)将所有五个点平移(-x0, -y0) ,使原点移动到(0, 0) (之后(x_x_axis, y_x_axis)移动到(x_x_axis - x0, y_x_axis - y0) );
(2)rotate all five points around (0, 0) by -arctan((y_x_axis - y0)/(x_x_axis - x0)) , to make the (x_x_axis - x0, y_x_axis - y0) moved to x_axis; (2)通过-arctan((y_x_axis - y0)/(x_x_axis - x0)) ) 将所有五个点围绕(0, 0)旋转,使(x_x_axis - x0, y_x_axis - y0)移动到 x_axis;
(3)assume the new coordinates are (0, 0) , (x_x_axis', 0) , (0, y_y_axis') , (x_x_axis', y_y_axis') , (x_obj', y_obj') , then the object's zero-one coordinate is (x_obj'/x_x_axis', y_obj'/y_y_axis') ; (3)假设新坐标为(0, 0) , (x_x_axis', 0) , (0, y_y_axis') , (x_x_axis', y_y_axis') , (x_obj', y_obj') , 那么物体的零一坐标为(x_obj'/x_x_axis', y_obj'/y_y_axis')

rotate formula: (x_new, y_new)=(x_old * cos(theta) - y_old * sin(theta), x_old * sin(theta) + y_old * cos(theta))旋转公式: (x_new, y_new)=(x_old * cos(theta) - y_old * sin(theta), x_old * sin(theta) + y_old * cos(theta))

Update:更新:
Note:笔记:

  1. If you use the distance method, you have to take care of the sign of the coordinate if the object might go out of the scene in the future;如果使用距离法,如果 object 将来可能 go 出场景,则必须注意坐标的符号;
  2. If there will be other transformations on the scene in the future (like symmetry transformation if you have mirror magic in the game, or transvection transformation if you have shockwaves, heatwaves or gravitational waves in the game), then the distance method no longer applies and you still have to reverse all the transformations your scene has in order to get the object's coordinate.如果以后场景中还有其他变换(比如游戏中有镜像魔法的对称变换,游戏中有冲击波、热浪或引力波的横切变换),那么距离法不再适用,您仍然需要反转场景中的所有转换才能获得对象的坐标。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM