質問

これを行うための良い方法を見つけることは、私をしばらく困惑させました。ポイントのセットを含む選択ボックスがあると仮定します。角をドラッグすることで、ボックス内のポイント(距離)を拡大縮小できます。これで、軸が揃ったボックスの場合、これは簡単です。コーナーをアンカーポイントとして(各コーナーからこのコーナーを減算し、スケールしてから、再度ポイントに追加します)、各ポイントxおよびyにボックスが大きくなった係数を掛けます。

ただし、x軸とy軸に揃っていないボックスを使用するようになりました。角をドラッグするとき、このボックス内のポイントをどのようにスケーリングしますか?

役に立ちましたか?

解決

長方形の1つの角を原点として選択します。それに接続された2つのエッジがベースになります( 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 ですが、座標が反転し、そのうちの1つが無効になっています)

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

他のヒント

すべてのボックスは円の中に含まれています。
ボックスをバインドする円を見つけ、その中心を見つけて、軸が揃ったボックスで行うのとまったく同じようにします。

ボックスは、4つのポイント(P1、P2、P3、P4)のセットとして定義されているとしましょう。 簡単にするために、P1をドラッグし、P3が反対側のコーナー(アンカーとして使用しているコーナー)であると言います。

マウスの位置にMのラベルを付け、計算する新しいポイントにN1、N2、N4のラベルを付けます。 P3はもちろん同じままです。

スケーリング係数は、ベクトル減算とベクトル内積を使用して簡単に計算できます:

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

さらに、スカラー乗算とベクトル加算を使用して、3つの新しいポイントを見つけることができます。

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);
    }
}
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top