找到一个好的方法来做到这一点让我有一段时间感到困惑:假设我有一个包含一组点的选择框。通过拖动角,您可以缩放框中的点(之间的距离)。现在对于轴对齐的盒子,这很容易。取一个角作为锚点(从每个点中减去这个角,对其进行缩放,然后再次将其添加到该点)并将每个点 x 和 y 乘以框变大的因子。
但是现在取一个不与 x 和 y 轴对齐的框。当你拖动它的角时,你如何缩放这个框内的点?
找到一个好的方法来做到这一点让我有一段时间感到困惑:假设我有一个包含一组点的选择框。通过拖动角,您可以缩放框中的点(之间的距离)。现在对于轴对齐的盒子,这很容易。取一个角作为锚点(从每个点中减去这个角,对其进行缩放,然后再次将其添加到该点)并将每个点 x 和 y 乘以框变大的因子。
但是现在取一个不与 x 和 y 轴对齐的框。当你拖动它的角时,你如何缩放这个框内的点?
任何盒子都包含在一个圆圈内。
你找到绑定盒子的圆,找到它的中心,然后做与轴对齐盒子完全相同的事情。
您选择矩形的一个角作为原点。连接到它的两条边将是基础(u
和v
,应该相互垂直)。您需要先将它们标准化。
u
从坐标中减去原点,并使用缩放向量 ( ) 和另一个向量 ( )计算点积v
。这会给你多少u
和v
对坐标的贡献。
然后你缩放你想要的组件。要获得最终坐标,只需将(现在缩放的)分量与它们各自的向量相乘,然后将它们相加。
例如:
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>
(v
是u
,但坐标翻转,其中一个被否定)
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)]
假设盒子被定义为一组四个点(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(被拖动的点跟随鼠标),所以唯一的兴趣点是 N2 和 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);
}
}