문제

이 일을 할 좋은 방법을 찾는 것은 한동안 저를 괴롭 혔습니다. 포인트 세트가있는 선택 상자가 있다고 가정합니다. 모서리를 드래그하면 상자의 (거리) 점을 확장 할 수 있습니다. 이제 축 정렬 된 상자의 경우 쉽습니다. 코너를 앵커 포인트로 가져 가십시오 (각 지점 에서이 코너를 빼고 스케일링 한 다음 다시 포인트에 추가). 각 점 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)]

다른 팁

모든 상자에는 원 안에 포함되어 있습니다.
상자를 묶고 중앙을 찾아 축 정렬 된 상자와 정확히 동일하게 수행하는 원을 찾습니다.

상자가 4 점 (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);
    }
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top