Pergunta

Encontrar uma boa maneira de fazer isso me perseguiu há um tempo: suponha que eu tenha uma caixa de seleção com um conjunto de pontos. Ao arrastar os cantos, você pode escalar os pontos (entre) na caixa. Agora, para uma caixa alinhada com um eixo, isso é fácil. Pegue um canto como um ponto de ancoragem (subtraia este canto de cada ponto, escala -o e adicione -o ao ponto novamente) e multiplique cada ponto X e Y pelo fator com o qual a caixa ficou maior.

Mas agora pegue uma caixa que não esteja alinhada com o eixo X e Y. Como você escala os pontos dentro desta caixa quando arrasta seus cantos?

Foi útil?

Solução

Você escolhe um canto do retângulo como origem. As duas arestas conectadas a ele serão a base (u e v, que devem ser perpendiculares um ao outro). Você precisaria normalizá -los primeiro.

Subtraia a origem das coordenadas e calcule o produto dot com o vetor de escala (u), e com o outro vetor (v). Isso te daria quanto u e v contribui para a coordenada.

Então você escala o componente que deseja. Para obter a coordenada final, você apenas multiplica os componentes (agora em escala) com seu respectivo vetor e os adicionam.

Por exemplo:

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, mas com coordenadas invertidas, e uma delas negada)

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>

Como você pode ver, eles se moveram em direção à linha (8,0)-(9,4), desde que escalamos 0,5, com (0,8) como a origem.

Editar: Isso acabou sendo um pouco mais difícil de explicar do que eu previ.

No código Python, pode parecer algo assim:

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

Outras dicas

Qualquer caixa está contida dentro de um círculo.
Você encontra o círculo que liga a caixa, encontra seu centro e faz exatamente o mesmo que faz com uma caixa alinhada com um eixo.

Digamos que a caixa seja definida como um conjunto de quatro pontos (P1, P2, P3 e P4). Por uma questão de simplicidade, diremos que você está arrastando P1 e que P3 é o canto oposto (aquele que você está usando como âncora).

Vamos rotular a posição do mouse como M e os novos pontos que você deseja calcular como N1, N2 e N4. P3, é claro, permanecerá o mesmo.

Seu fator de escala pode ser simplesmente calculado usando a subtração vetorial e o produto Vector Dot:

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

E os três novos pontos podem ser encontrados usando multiplicação escalar e adição de vetores:

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

editar: eu vejo isso Mizardx Já respondeu à pergunta, então minha resposta está aqui para ajudar com essa explicação difícil. Espero que ajude!

editar: Aqui está o algoritmo para escala não proporcional. Nesse caso, N1 é igual a M (o ponto que está sendo arrastado segue o mouse), então os únicos pontos de interesse são N2 e 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

onde * representa multiplicação escalar

editar: Aqui está algum código C ++ que responde à pergunta. Tenho certeza de que essa pergunta já está morta há muito tempo, mas foi um problema interessante, e eu me diverti escrevendo o código.

#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);
    }
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top