[Add On] UV Align\Distribute


(Luca Carella) #1

Hi all!,
This is my new script, the purpose of this script is to have tools that help you organize your UV in the UV space.
It’s still in work but the alignment features already work.

Let me know what you think!

###############
Little Guide
###############

Select the uv island that you want to perform alignment, then choose on wich element do you want to perform alignmet(Uv space: use the uv space for alignment, useful for example to center the island on uv space; Cursor: align uv where the cursor is placed; Active Face: use the active uv face(the active face affect the island where to face is , so it perform alignment on the island where the face is and not only the face)

Then choose witch alignment to perform and you are done.

Rotation alignment, Scale equalization and Island matching is always performed on active face\island.

The distribution of UV Island work in this way: island are distributed, from the first island to the last, in the space between them, and not in the UV space(coming soon!!), same for gap equalization: the gap between the island is equalized so each island is at the same distance(edge to edge).

Island matching work in this way, just overlap two island and press the button now the island overlap perfectly, you can adjust which vertex are aligned with the active by adjusting the threshold(the distance between a vertex and another)

New version: https://github.com/c30ra/uv-align-distribute

Old:
Warning: this update break compatibility with previous version(<2.73)

Attachments

uv_align_distribute.zip (5.47 KB)


(Pitiwazou) #2

Some screenshots ?


(mifth) #3

OMG!!! man! You did great addon! You save my time a lot with this thing! Thank you a lot.

Question: is it possible to auto-rotate 90 degrees some parts of the UVS? It would be cool to have such a checkbox.

Also, i think this addon should be in blender default addons.


(Luca Carella) #4

Thank you very much XD!!! If I understand your question, no, i think there is no way to do this and i don’t see how to do it. For the screenshots, i don’t see a good reason to attach them, cause in this case i think its not much explenatory. However i’ll post guide as soon as i can. If you ever used Inkscape you should know how to use it, since it’s inspired from it!!


(mifth) #5

I think it’s possible. I asked a question here. Possibly someone answer us.


We have Islands… all we need to get their width and height and rotate according to proportions.


(Luca Carella) #6

Ok if you mean align the longest o smaller side in the same direction, yes it’ possible and its should be easy, In the image it seems you want that the uv should point in the same direction.


(mifth) #7

Yes, just just align all islands for the same height and width. No matter what direction. It will be easier to rotate to 180 degrees some parts after Align operation.
The issue is that the Unwrap rotates islands in strange directions sometimes.

Thanks.


(Luca Carella) #8

Ok, I make the auto-rotation, i’ll add it as button and not as check box, cause it can be useful also without aligning object. Maybe tomorrow i’ll release an update!


(mifth) #9

This is really great news! Btw, here is how i use your addon:


(Luca Carella) #10

OMG!! I’m honored!!! Just to be sure that is what you want, try this script:

import bmesh, bpy, mathutils, math
from collections import defaultdict


bm = bmesh.from_edit_mesh(bpy.context.object.data)    
uvlayer = bm.loops.layers.uv.active


def BBox(island):
    minX = +1000
    minY = +1000
    maxX = -1000
    maxY = -1000
    #for island in islands:
    #print(island)
    for face_id in island:
        face = bm.faces[face_id]
        for loop in face.loops:
            minX = min(loop[uvlayer].uv.x, minX)
            minY = min(loop[uvlayer].uv.y, minY)
            maxX = max(loop[uvlayer].uv.x, maxX)
            maxY = max(loop[uvlayer].uv.y, maxY)

    return mathutils.Vector((minX,minY)), mathutils.Vector((maxX,maxY))

def BBoxCenter(island):
    minX = +1000
    minY = +1000
    maxX = -1000
    maxY = -1000
    #for island in islands:
    for face_id in island:
        face = bm.faces[face_id]
        for loop in face.loops:
            minX = min(loop[uvlayer].uv.x, minX)
            minY = min(loop[uvlayer].uv.y, minY)
            maxX = max(loop[uvlayer].uv.x, maxX)
            maxY = max(loop[uvlayer].uv.y, maxY)
            
    return  (mathutils.Vector((minX,minY))+ mathutils.Vector((maxX,maxY)))/2

def islandDirection(island):
    bbox = BBox(island)
    
    width = abs(bbox[0].x - bbox[1].x)
    height = abs(bbox[0].y - bbox[1].y)
    
    print(width, height)
    if width &gt; height:
        return "Horizontal"
    elif width == height:
        return "Same"
    else:
        return "Vertical"
    
def moveIslands(vector, islands):        
        #for island in islands:
            for face_id in island:
                face = bm.faces[face_id]
                #print(face_id)
                for loop in face.loops:
                    #print(loop[uvlayer].uv) 
                    loop[bm.loops.layers.uv.active].uv += vector
                    #print(loop[bm.loops.layers.uv.active].uv) 
def rotateIsland(angle, island):
    
    rad = math.radians(angle)
    center = BBoxCenter(island)
    
    for face_id in island:
        face = bm.faces[face_id]
        #print(face_id)
        for loop in face.loops:
            #print(loop[uvlayer].uv) 
            x = loop[bm.loops.layers.uv.active].uv.x
            y = loop[bm.loops.layers.uv.active].uv.y
            #trans = mathutils.Vector((x- center.x ,y- center.y ))
            xt = x - center.x
            yt = y -center.y
            xr = (xt*math.cos(rad)) - (yt*math.sin(rad))
            yr = (xt*math.sin(rad)) + (yt*math.cos(rad))
            x += center.x
            y += center.y
            #loop[bm.loops.layers.uv.active].uv = trans
            loop[bm.loops.layers.uv.active].uv.x = xr + center.x
            loop[bm.loops.layers.uv.active].uv.y = yr + center.y
            #loop[bm.loops.layers.uv.active].uv += trans
                                                 
class MakeIslands:
    def __init__(self):
        #self.bm = bmesh.new()   
        self.face_to_verts = defaultdict(set)
        self.vert_to_faces = defaultdict(set)
        self.selectedIsland = set()
        
        for face in bm.faces:
            for loop in face.loops:
                #if loop[uvlayer].select :
                    #floating point error! keep it low
                    id = '{0[0]:.5} {0[1]:.5} {1}'.format(loop[uvlayer].uv, loop.vert.index)
                    self.face_to_verts[face.index].add(id)
                    self.vert_to_faces[id].add(face.index)
                    if loop[uvlayer].select :
                        self.selectedIsland.add(face.index)
                    
        #print(self.selectedIsland)
    def addToIsland(self, face_id):
        if face_id in self.faces_left:
            #add the face itself
            self.current_island.append(face_id)
            #print(face_id)
            self.faces_left.remove(face_id)
            #and add all faces that share uvs with this face
            verts = self.face_to_verts[face_id]
            for vert in verts:
                #print('looking at vert {}'.format(vert))
                connected_faces = self.vert_to_faces[vert]
                if connected_faces:
                    for face in connected_faces:
                        self.addToIsland(face)     
                
    def getIslands(self):
        self.islands = []
        self.faces_left = set(self.face_to_verts.keys())
        while len(self.faces_left) &gt; 0:
            face_id = list(self.faces_left)[0]
            self.current_island = []
            #print(self.faces_left)
            self.addToIsland(face_id)
            self.islands.append(self.current_island)
            
        return self.islands
    
    def newIsland(self):
        self.islands = []
        faces_left = set(self.face_to_verts.keys())
        for face_id in faces_left:
            verts = self.face_to_verts[face_id]
            for vert in verts:
                connected_vert = self.vert_to_faces[vert]
                if connected_vert:
                    self.islands.append(face_id)
        return self.islands
                                 
    def update(self):
        bmesh.update_edit_mesh(bpy.context.object.data, True)
        #bm.to_mesh(bpy.context.object.data)
        #bm.free()
    
    def activeIsland(self):
        for island in self.islands:
            if bm.faces.active.index in island:                
                return island
            
    def selectedIslands(self):
        _selectedIslands = []
        print('selectedIslands()')
        print(self.selectedIsland)
        for island in self.islands: 
            
            #print(island) 
                     
            if not self.selectedIsland.isdisjoint(island):    
                  _selectedIslands.append(island)   
                  #print('True')       
        return _selectedIslands
                                    
tool = MakeIslands()
islands = tool.getIslands()
#tool.moveIslands()

active = tool.activeIsland()
v = mathutils.Vector((0.5,0.5))
#print(islands)
#print(tool.selectedIslands())
activeAlignment = islandDirection(active)
selecteIslands = tool.selectedIslands()
print(activeAlignment)
for island in selecteIslands:
    islandAlignment = islandDirection(island)
    
    if activeAlignment == islandAlignment:        
        print("equal",islandAlignment)
    else:
        rotateIsland(90,island)
        #print(rotation)
            
    #print(islandDirection(island))
    #moveIslands(v - BBoxCenter(island), selecteIslands)
#print(islands[0][0])
tool.update()

copy and paste in script editor and run it, with the uv selected(at last two)


(mifth) #11

Sorry it does not work when i paste it. No rush. i’ll test when you finish it for sure! :slight_smile:


(Luca Carella) #12

New update with a new feature, thanks to idea of mifth! The upcoming version will add the distribution features, so stay tuned!!
As always, idea, critics are welcome.


(mifth) #13

I have exception when i press the button.

https://dl.dropboxusercontent.com/u/26887202/blender/test_UVAlign_01.blend


(Luca Carella) #14

you don’t have any active face, i forgot to handle this… sorry


(mifth) #15

Could you make like this?
If activeFace=None then activeFace=firstSelectedFace.


(mifth) #16

Anyway the addon is really powerful! I showed another usecase.


(mifth) #17

I have a new idea about fitting all island to one (which has active face).

Take a look at my explanation: http://i.imgur.com/iHDKTXl.png

And here is the test file: https://dl.dropboxusercontent.com/u/26887202/blender/test_UVAlign_02.blend

How do you think is it possible to make?


(Luca Carella) #18

Hi mifth,
for the first question, i have to say no, because, unfortunately i can’t determine witch island was selected first, or last. Since the active face is used for alignment the user should know on which face the alignment is performed, so i think the best way is to warn the user with a pop up.

For the second question, what you are looking for is averaging island scale witch is already implemented in blender, however if you need to scale the island to have the same size of the active face, witch is a more specific case, i think this need some more request by other users, however, this should not be so hard to implement so i’ll keep it in mind, and see if it’s really necessary.


(mifth) #19

Hello!

About first question - ok. You can even just make info report:
if activeFace is None:
self.report({‘INFO’}, “No Active Face!!!”)
else:
DO YOUR CODE

About second question:
This would be really cool feature. As sometimes some cloned meshes are distorted to different proportions. Like wood models.
This would be cool to make like this http://i.imgur.com/dPkF1l6.png and then align all islands.
Example is here https://dl.dropboxusercontent.com/u/26887202/blender/test_UVAlign_02.blend


(Crocadillian) #20

Hey, just wanted to say how awesome this add-on is, it seems really useful for game assets, where UV space needs to be economised. I currently use a Copy/Paste UV plugin to merge unwraps together, but for meshes that don’t have to 100% overlap each other, and even just for general UV arrangement this looks great. Thanks a lot :smiley: