Off-axis scaling

Finding a good way to do this puzzled me a little: Suppose I have a select box with a bunch of points in it. By dragging the corners, you can scale (distance between) points in the box. Now it's easy for an axis-aligned field. Take the angle as an anchor point (subtract that angle from each point, scale it, then add it again to the point) and multiply each point x and y by the multiplier with which the box got bigger.

But now take a box that is not aligned with the x and y axis. How do you scale the points inside this window while dragging its corners?

+1


source to share


3 answers


You select one corner of the rectangle as the source. The two associated edges will be the base ( u

and v

, which should be perpendicular to each other). You will need to normalize them first.

Subtract the origin from the coordinates and compute the dot product using the scaling vector ( u

) and another vector ( v

). This would give you how much u

and v

contribute to the coordinate.

Then you scale the component you want. To get the final coordinate, you simply multiply the (now scaled) components by the corresponding vector and add them together.

For example:

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

yes u

, but with inverted coordinates and one of them is negated)



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>

      

As you can see, they have moved to line (8,0)

- (9,4)

as we scaled by 0.5, and (0,8)

- as the source.

Edit: This turned out to be a little more difficult to explain than I expected.

In python code, it might look something like this:

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

      

0


source


Any cell is contained within a circle.
You will find a circle that ties the box, finds its center, and does the same as with an axis-aligned box.



+3


source


Let's say a field is defined as a set of four points (P1, P2, P3, and P4). For simplicity, we'll say that you are dragging P1 and P3 is the opposite corner (the one you use as the anchor).

Let's mark the mouse position as M and the new points you want to calculate as N1, N2 and N4. The P3 will of course remain the same.

Your scaling factor can simply be calculated using vector subtraction and vector dot product:

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

And the three new points can be found using scalar multiplication and vector addition:

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

edit: I see that MizardX has already answered the question, so my answer is here to help with this difficult explanation. Hope this helps!

edit: here is the algorithm for disproportionate scaling. In this case, N1 is equal to M (point is dragged with the mouse), so the only points of interest are N2 and 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
      

where * represents scalar multiplication

edit: Here is some C ++ code that answers the question. I'm sure this question is dead a long time ago, but it was an interesting problem and I had fun writing code.

#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);
    }
}
      

0


source







All Articles