2008-12-02 94 views
1

找到一個很好的方法來做到這一點已經困擾了我一段時間:假設我有一個包含一組點的選擇框。通過拖動角可以縮放框中點之間的距離(距離)。現在對於一個軸對齊的框,這很容易。以一個角點作爲定位點(從每個點減去這個角點,對其進行縮放,然後再將其添加到該點),並將每個點x和y乘以框變大的因子。非軸對齊縮放

但現在拿一個不與x和y軸對齊的盒子。當您拖動角落時,如何縮放此框內的點?

回答

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)] 
3

任何框都包含在一個圓圈內。
您可以找到綁定框的圓,找到它的中心,並且與軸對齊的框完全相同。

+0

優雅和正確 – 2008-12-02 22:25:32

+0

您剛剛用另一個問題替換了一個問題。你如何得到綁定盒子的圓圈? – 2008-12-02 22:28:55

0

比方說,盒子被定義爲一組的四點(P1,P2,P3和P4)。 爲簡單起見,我們會說你拖着P1,而P3是對面的角落(你用作錨點的那個角落)。我們將鼠標位置標記爲M,並將希望計算的新點標記爲N1,N2和N4。 P3當然會保持不變。

你的縮放因子可以通過矢量減法和矢量點積簡單地計算:

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

scale = ((M - P3) dot (P1 - P3))/((P1 - P3) dot (P1 - 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); 
    } 
}