Pregunta

Encontrar una buena manera de hacerlo me ha dejado perplejo por un tiempo: supongamos que tengo un cuadro de selección con un conjunto de puntos. Al arrastrar las esquinas, puede escalar los puntos (distancia entre) en el cuadro. Ahora, para un cuadro alineado por eje, esto es fácil. Tome una esquina como punto de anclaje (reste esta esquina de cada punto, escale, luego agréguela nuevamente al punto) y multiplique cada punto x e y por el factor con el que la caja se ha vuelto más grande.

Pero ahora tome un cuadro que no esté alineado con los ejes xey. ¿Cómo escalas los puntos dentro de este cuadro cuando arrastras sus esquinas?

¿Fue útil?

Solución

Elige una esquina del rectángulo como origen. Las dos aristas conectadas serán la base ( u y v , que deben ser perpendiculares entre sí). Tendría que normalizarlos primero.

Reste el origen de las coordenadas y calcule el producto punto con el vector de escala ( u ) y con el otro vector ( v ). Esto le daría cuánto u y v contribuyen a la coordenada.

Luego escala el componente que desea. Para obtener la coordenada final, simplemente multiplique los componentes (ahora escalados) con su vector respectivo y agréguelos juntos.

Por ejemplo:

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 es u , pero con coordenadas invertidas y una de ellas 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 puede ver, se han movido hacia la línea (8,0) - (9,4) , ya que escalamos 0.5, con (0,8) como el origen.

Editar: Esto resultó ser un poco más difícil de explicar de lo que esperaba.

En el código de Python, podría verse así:

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

Otros consejos

Cualquier cuadro está contenido dentro de un círculo.
Encuentra el círculo que une el cuadro, encuentra su centro y hace exactamente lo mismo que con un cuadro alineado por eje.

Digamos que el cuadro se define como un conjunto de cuatro puntos (P1, P2, P3 y P4). En aras de la simplicidad, diremos que está arrastrando P1, y que P3 es la esquina opuesta (la que está utilizando como ancla).

Etiquetemos la posición del mouse como M y los nuevos puntos que desea calcular como N1, N2 y N4. P3, por supuesto, seguirá siendo el mismo.

Su factor de escala se puede calcular simplemente usando la resta de vectores y el producto de puntos vectoriales:

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

Y los tres nuevos puntos se pueden encontrar usando la multiplicación escalar y la suma de vectores:

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

editar: veo que MizardX ya ha respondido la pregunta, así que mi respuesta es aquí para ayudar con esa difícil explicación. ¡Espero que ayude!

editar: aquí está el algoritmo para el escalado no proporcional. En este caso, N1 es igual a M (el punto que se arrastra sigue al mouse), por lo que los únicos puntos de interés son N2 y 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

donde * representa la multiplicación escalar

editar: Aquí hay un código C ++ que responde a la pregunta. Estoy seguro de que esta pregunta está muerta hace mucho tiempo, pero fue un problema interesante y me divertí mucho escribiendo el 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 bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top