非轴对齐缩放

时间:2008-12-02 22:14:34

标签: scaling vectormath

找到一个好方法做到这一点已经让我感到困惑了一段时间:假设我有一个带有一组点的选择框。通过拖动角,您可以缩放框中的(点之间的距离)点。现在,对于轴对齐的盒子,这很容易。将一个角作为一个锚点(从每个点减去这个角,缩放它,然后再将它添加到该点)并将每个点x和y乘以该框变得更大的因子。

但是现在拿一个没有与x和y轴对齐的盒子。当你拖动它的角落时,如何缩放此框内的点?

3 个答案:

答案 0 :(得分:3)

任何方框都包含在圆圈内 您可以找到绑定框的圆圈,找到它的中心,并且与使用轴对齐框完全相同。

答案 1 :(得分:0)

您选择矩形的一个角作为原点。连接到它的两条边将是基础(uv,它们应该彼此垂直)。您需要先将它们标准化。

从坐标中减去原点并使用缩放矢量(u)和另一个矢量(v)计算点积。这将为您提供uv对坐标的贡献。

然后缩放所需的组件。要获得最终坐标,只需将(现在缩放的)组件与它们各自的向量相乘,然后将它们相加。

例如:

Points: p1 = (3,5) and p2 = (6,4)

Selection corners: (0,2),(8,0),(9,4),(1,6)
selected origin = (8,0)

u = ((0,2)-(8,0))/|(0,2)-(8,0)| = <-0.970, 0.242>
v = <-0.242, -0.970>

vu,但是有翻转的坐标,其中一个被否定了)

p1´ = p1 - origin = (-5, 5)
p2´ = p2 - origin = (-2, 4)

p1_u = p1´ . u = -0.970 * (-5) + 0.242 * 5 = 6.063
p1_v = p1´ . v = -0.242 * (-5) - 0.970 * 5 = -3.638

Scale p1_u by 0.5: 3.038

p1_u * u + p1_v * v + origin = <5.941, 4.265>

Same for p2: <7.412, 3.647>

正如您可能看到的那样,他们已经走向(8,0) - (9,4)行,因为我们缩放了0.5,其中(0,8)为原点。

编辑:这比我预期的要难得多。

在python代码中,它看起来像这样:

def scale(points, origin, u, scale):
    # normalize
    len_u = (u[0]**2 + u[1]**2) ** 0.5
    u = (u[0]/len_u, u[1]/len_u)
    # create v
    v = (-u[1],u[0])
    ret = []
    for x,y in points:
        # subtract origin
        x, y = x - origin[0], y - origin[1]
        # calculate dot product
        pu = x * u[0] + y * u[1]
        pv = x * v[0] + y * v[1]
        # scale
        pu = pu * scale
        # transform back to normal space
        x = pu * u[0] + pv * v[0] + origin[0]
        y = pu * u[1] + pv * v[1] + origin[1]
        ret.append((x,y))
    return ret

>>> scale([(3,5),(6,4)],(8,0),(-8,2),0.5)
[(5.9411764705882355, 4.2647058823529411), (7.4117647058823533, 3.6470588235294117)]

答案 2 :(得分:0)

假设该框被定义为一组四个点(P1,P2,P3和P4)。 为了简单起见,我们假设您正在拖动P1,并且P3是相反的角落(您用作锚点的角落)。

让我们将鼠标位置标记为M,并将您想要计算的新点标记为N1,N2和N4。当然,P3将保持不变。

您可以使用向量减法和向量点积来简单计算缩放因子:

scale = ((M - P3) dot (P1 - P3)) / ((P1 - P3) dot (P1 - P3))

使用标量乘法和向量加法可以找到三个新点:

N1 = scale*P1 + (1 - scale)*P3
N2 = scale*P2 + (1 - scale)*P3
N4 = scale*P4 + (1 - scale)*P3

编辑:我看到MizardX已经回答了这个问题,所以我的回答就是帮助解决这个难题。我希望它有所帮助!

编辑:这里是非比例缩放的算法。在这种情况下,N1等于M(拖动的点跟随鼠标),因此唯一感兴趣的点是N​​2和N4:

N2 = ((M - P3) dot (P2 - P3)) / ((P2 - P3) dot (P2 - P3)) * (P2 - P3) + P3
N4 = ((M - P3) dot (P4 - P3)) / ((P4 - P3) dot (P4 - P3)) * (P4 - P3) + P3

其中*表示标量乘法

编辑:这是一些回答问题的C ++代码。我相信这个问题现在已经很久了,但这是一个有趣的问题,我编写代码时有一些乐趣。

#include <vector>

class Point
{
    public:
        float x;
        float y;
        Point() { x = y = 0; }
        Point(float nx, float ny) { x = nx; y = ny; }
};

Point& operator-(Point& A, Point& B) { return Point(A.x-B.x, A.y-B.y); }
Point& operator+(Point& A, Point& B) { return Point(A.x+B.x, A.y+B.y); }
Point& operator*(float sc, Point& P) { return Point(sc*P.x, sc*P.y); }

float dot_product(Point A, Point B) { return A.x*B.x + A.y*B.y; }

struct Rect { Point point[4]; };

void scale_points(Rect box, int anchor, Point mouse, vector<Point> points)
{
    Point& P3 = box.point[anchor];
    Point& P2 = box.point[(anchor + 1)%4];
    Point& P1 = box.point[(anchor + 2)%4];
    Point& P4 = box.point[(anchor + 3)%4];

    Point A = P4 - P3;
    Point aFactor = dot_product(mouse - P3, A) / dot_product(A, A) * A;

    Point B = P2 - P3;
    Point bFactor = dot_product(mouse - P3, B) / dot_product(B, B) * B;

    for (int i = 0; i < points.size(); i++)
    {
        Point P = points[i] - P3;
        points[i] = P3 + dot_product(P, aFactor) + dot_product(P, bFactor);
    }
}