Ridimensionamento allineato senza asse
-
22-07-2019 - |
Domanda
Trovare un buon modo per farlo mi ha messo in difficoltà per un po 'di tempo: supponiamo di avere una casella di selezione con una serie di punti. Trascinando gli angoli è possibile ridimensionare i punti (distanza tra) nella casella. Ora per una scatola allineata agli assi questo è facile. Prendi un angolo come punto di ancoraggio (sottrai questo angolo da ciascun punto, ridimensionalo, quindi aggiungilo di nuovo al punto) e moltiplica ogni punto xey per il fattore con cui la casella è diventata più grande.
Ma ora prendi una casella che non è allineata con gli assi xey. Come si ridimensionano i punti all'interno di questo riquadro quando si trascinano gli angoli?
Soluzione
Scegli un angolo del rettangolo come origine. I due bordi ad esso collegati costituiranno la base ( u
e v
, che dovrebbero essere perpendicolari tra loro). Dovresti prima normalizzarli.
Sottrai l'origine dalle coordinate e calcola il punto-prodotto con il vettore di ridimensionamento ( u
) e con l'altro vettore ( v
). Questo ti darebbe quanto u
e v
contribuiscono alla coordinata.
Quindi ridimensionate il componente desiderato. Per ottenere la coordinata finale, basta moltiplicare i componenti (ora ridimensionati) con il rispettivo vettore e aggiungerli insieme.
Ad esempio:
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
, ma con coordinate capovolte e una di esse negata)
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>
Come forse puoi vedere, si sono spostati verso la linea (8,0)
- (9,4)
, poiché abbiamo ridimensionato di 0,5, con
Modifica: questo si è rivelato un po 'più difficile da spiegare di quanto mi aspettassi.
Nel codice Python, potrebbe assomigliare a questo:
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)]
Altri suggerimenti
Ogni casella è contenuta in un cerchio.
Trovi il cerchio che lega la scatola, ne trovi il centro e fai esattamente lo stesso che fai con una scatola allineata agli assi.
Supponiamo che la casella sia definita come un insieme di quattro punti (P1, P2, P3 e P4). Per semplicità, diremo che stai trascinando P1 e che P3 è l'angolo opposto (quello che stai usando come ancora).
Etichettiamo la posizione del mouse come M e i nuovi punti che desideri calcolare come N1, N2 e N4. P3 rimarrà ovviamente lo stesso.
Il fattore di ridimensionamento può essere semplicemente calcolato utilizzando la sottrazione vettoriale e il prodotto punto vettoriale:
scale = ((M - P3) dot (P1 - P3)) / ((P1 - P3) dot (P1 - P3))
E i tre nuovi punti possono essere trovati usando la moltiplicazione scalare e l'aggiunta vettoriale:
N1 = scale*P1 + (1 - scale)*P3
N2 = scale*P2 + (1 - scale)*P3
N4 = scale*P4 + (1 - scale)*P3
modifica: Vedo che MizardX ha già risposto alla domanda, quindi la mia risposta è qui per aiutare con quella difficile spiegazione. Spero che sia d'aiuto!
modifica: ecco l'algoritmo per il ridimensionamento non proporzionale. In questo caso, N1 è uguale a M (il punto trascinato segue il mouse), quindi gli unici punti di interesse sono 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
dove * rappresenta la moltiplicazione scalare
modifica: Ecco del codice C ++ che risponde alla domanda. Sono sicuro che questa domanda è ormai morta da tempo, ma è stato un problema interessante e mi sono divertito a scrivere il codice.
#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);
}
}