To quickly fit the entire UV-map, or its separate selected island, into the UV area boundaries, we can make a simple script using the Blender Python API.
Add a cube to the scene, create a UV-map for it, and randomly shift and scale it. Now let’s fit it into the UV area boundaries, make sure that the coordinates of all the points are between 0.0 and 1.0 along the X and Y axes.
We need to work with the UV-map coordinates from the object mode.
1 |
bpy.ops.object.mode_set(mode='OBJECT') |
A pointer to the currently active UV-map layer.
1 2 3 |
uv_layer = bpy.context.object.data.uv_layers.active # <bpy_struct, MeshUVLoopLayer("UVMap") at 0x000001F95F9E0EF8> |
Get only the selected points.
1 2 3 |
points = [point for point in uv_layer.data if point.select] # [bpy.data.meshes['Cube'].uv_layers["UVMap"].data[0], ...] |
We can get the coordinates of all selected UV points from their “uv” property.
1 2 3 |
[(point.uv.x, point.uv.y) for point in points] # [(0.644528865814209, 0.0), ...] |
First, let’s move all the selected points to the left and bottom borders of the UV region.
To do this, we need to find the points with the minimum coordinates along the X and Y axes.
1 2 3 4 5 |
min_x = min((point.uv.x for point in points)) min_y = min((point.uv.y for point in points)) # 0.39864683151245117 # 0.108274444937706 |
And subtract the found values from the coordinates of each selected point. This will shift them all as far down and to the left in the UV area as possible.
1 2 3 |
for point in points: point.uv.x -= min_x point.uv.y -= min_y |
Now we need to stretch our UV-map to the right and up so that at least along one axis (X or Y) the scan occupies the entire UV area.
Now find the points with the maximum coordinates along these axes.
1 2 3 4 5 |
max_x = max((point.uv.x for point in points)) max_y = max((point.uv.y for point in points)) # 0.4475545883178711 # 0.5212229490280151 |
To make the UV-map stretch to cover the area from 0.0 to 1.0 on each axis, we need to multiply the coordinates of all points by the ratio of 1 to the current maximum coordinate.
1 2 |
x_scale_factor = 1 / max_x y_scale_factor = 1 / max_y |
All that remains is to multiply.
1 2 3 |
for point in points: point.uv.x *= x_scale_factor point.uv.y *= y_scale_factor |
But, if we do it this way, the UV-map will be distorted.
If we want to avoid UV distortion, we need to multiply the coordinates of the points by the minimum of the two values along the X and Y axes.
1 |
scale_factor = min(x_scale_factor, y_scale_factor) |
And now we can perform the multiplication.
1 2 3 |
for point in points: point.uv.x *= scale_factor point.uv.y *= scale_factor |
Back in mesh editing mode,
1 |
bpy.ops.object.mode_set(mode='EDIT') |
and we will see that now the UV, or its selected part, is clearly fitted to the UV area having coordinates from 0.0 to 1.0.