Coordinates of 2D points zoomed in/out This might be a simple issue that I am overcomplicating, but I've spent quite some time reading about polygon scaling and I've come to the conclusion that it is not precisely what I need.

Given a set of (x, y) coordinates for N points (shown in blue) I need the new set of coordinates that result after zooming in/out a given scale factor (shown in red).



I've come up with the simple method shown below, but I wonder if there might be another approach and/or more reasonable zooming methods.

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as patches

N = 5
xy = [np.random.uniform(0., 1000., 2) for _ in range(N)]
x, y = zip(*xy)

# Center of xy points, defined as the center of the minimal rectangle that
# contains all points.
xy_center = ((min(x) + max(x)) * .5, (min(y) + max(y)) * .5)

# Difference between the center coordinates and the xy points.
delta_x, delta_y = xy_center[0] - x, xy_center[1] - y

# Zoom scale (0. &lt; scale)
scale = 1.5

# Scaled xy points.
x_scale = xy_center[0] - scale * delta_x
y_scale = xy_center[1] - scale * delta_y

ax = plt.subplot(111)
# Original xy points.
ax.scatter(x, y, c='b')
# Defined center.
ax.scatter(*xy_center, marker='x', c='g')
# Zoomed points.
ax.scatter(x_scale, y_scale, c='r')
# Square: bottom left corner, width, height
ax.add_patch(
    patches.Rectangle(
        (min(x), min(y)), (max(x) - min(x)), (max(y) - min(y)), fill=False))
plt.show()
When working with NumPy, it's best to keep all our data in the form of NumPy arrays, instead of converting back and forth between NumPy and Python data structures.


So instead of creating a NumPy array for each point and then zipping them to extract tuples of \$x\$-coordinates and \$y\$-coordinates, create one array for all the points:

p = np.random.uniform(0., 1000., (N, 2))


We can extract the \$x\$-coordinates and \$y\$-coordinates, if we need them, by transposing the array:

x, y = p.T


but we won't need to do so until the very end when we pass the data to Matplotlib.
The coordinates of the origin can be computed like this:

o = (p.min(axis=0) + p.max(axis=0)) * .5

To scale the points about the origin, use:

q = o * (1 - scale) + p * scale


instead of (as in the code in the post):

delta = p - o
q = o + delta * scale


The former is slightly quicker, as it has only two arithmetic operations on arrays as long as p whereas the latter has three.


Putting this together:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as patches

N = 5
p = np.random.uniform(0., 1000., (N, 2))

# Center of points, defined as the center of the minimal rectangle
# that contains all points.
o = (p.min(axis=0) + p.max(axis=0)) * .5

# Scale factor (0. &lt; scale)
scale = 1.5

# Points scaled about center.
q = o * (1 - scale) + p * scale

ax = plt.subplot(111)
ax.scatter(*p.T, c='b')           # Original points.
ax.scatter(*o, marker='x', c='g') # Center.
ax.scatter(*q.T, c='r')           # Scaled points.
ax.add_patch(patches.Rectangle(p.min(axis=0), *p.ptp(axis=0), fill=False))
plt.show()


Here numpy.ptp stands for peak-to-peak and computes the range of values (maximum − minimum) along an axis in an array.