Source code for uv_align_distribute.island

"""The Island module."""

import math
import os
import sys

import mathutils

from . import geometry, global_def, utils
# Add vendor directory to module search path
parent_dir = os.path.abspath(os.path.dirname(__file__))
nx_dir = os.path.join(parent_dir, 'networkx')
decorator_dir = os.path.join(parent_dir, 'decorator')

sys.path.append(decorator_dir)
sys.path.append(nx_dir)

import networkx


[docs]class Island: """Create Island from a set() of faces. :param island: a set() of face indexes. :type island: set(). """ def __init__(self, island): self.faceList = island def __iter__(self): """Iterate throught all face faces forming the island.""" for i in self.faceList: yield i def __len__(self): """Return the number of faces of this island.""" return len(self.faceList) def __str__(self): return str(self.faceList) def __repr__(self): return repr(self.faceList) def __eq__(self, other): """Compare two island.""" return self.faceList == other # properties
[docs] def BBox(self): """Return the bounding box of the island. :return: a Rectangle rappresenting the bounding box of the island. :rtype: :class:`.Rectangle` """ minX = minY = 1000 maxX = maxY = -1000 for face_id in self.faceList: face = global_def.bm.faces[face_id] for loop in face.loops: u, v = loop[global_def.uvlayer].uv minX = min(u, minX) minY = min(v, minY) maxX = max(u, maxX) maxY = max(v, maxY) return geometry.Rectangle(mathutils.Vector((minX, minY)), mathutils.Vector((maxX, maxY)))
[docs] def angle(self): """Return the island angle. :return: the angle of the island in radians. :rtype: float """ uvList = [] for face_id in self.faceList: face = global_def.bm.faces[face_id] for loop in face.loops: uv = loop[global_def.bm.loops.layers.uv.active].uv uvList.append(uv) angle = mathutils.geometry.box_fit_2d(uvList) return angle
[docs] def size(self): """Return the island size. :return: the size of the island(bounding box). :rtype: :class:`.Size` """ bbox = self.BBox() sizeX = bbox.right() - bbox.left() sizeY = bbox.bottom() - bbox.top() return geometry.Size(sizeX, sizeY)
# Transformation
[docs] def move(self, vector): """Move the island by vector. Move the island by 'vector', by adding 'vector' to the curretnt uv coords. :param vector: the vector to add. :rtype: :class:`mathutils.Vector` """ for face_id in self.faceList: face = global_def.bm.faces[face_id] for loop in face.loops: loop[global_def.bm.loops.layers.uv.active].uv += vector
[docs] def rotate(self, angle): """Rotate the island on it's center by 'angle(radians)'. :param angle: the angle(radians) of rotation. :rtype: float """ center = self.BBox().center() for face_id in self.faceList: face = global_def.bm.faces[face_id] for loop in face.loops: uv_act = global_def.bm.loops.layers.uv.active x, y = loop[uv_act].uv xt = x - center.x yt = y - center.y xr = (xt * math.cos(angle)) - (yt * math.sin(angle)) yr = (xt * math.sin(angle)) + (yt * math.cos(angle)) # loop[global_def.bm.loops.layers.uv.active].uv = trans loop[global_def.bm.loops.layers.uv.active].uv.x = xr + center.x loop[global_def.bm.loops.layers.uv.active].uv.y = yr + center.y
[docs] def scale(self, scaleX, scaleY): """Scale the island by 'scaleX, scaleY'. :param scaleX: x scale factor. :type scaleX: float :param scaleY: y scale factor :type scaleY: float """ center = self.BBox().center() for face_id in self.faceList: face = global_def.bm.faces[face_id] for loop in face.loops: x = loop[global_def.bm.loops.layers.uv.active].uv.x y = loop[global_def.bm.loops.layers.uv.active].uv.y xt = x - center.x yt = y - center.y xs = xt * scaleX ys = yt * scaleY loop[global_def.bm.loops.layers.uv.active].uv.x = xs + center.x loop[global_def.bm.loops.layers.uv.active].uv.y = ys + center.y
# FIXME: in some case doesn't work as expected...
[docs] def snapToUnselected(self, targetIslands, threshold): """Snap this island to 'targetIsland'. Use threshold to adjust vertex macthing. targetIsland is the island to use to search for nearest vertex. :param targetIsland: the target island for snapping :type targetIsland: :class:`.Island` :param threshold: distance from one vert to the others :type threshold: float """ bestMatcherList = [] # targetIslands.remove(self) activeUvLayer = global_def.bm.loops.layers.uv.active for face_id in self.faceList: face = global_def.bm.faces[face_id] for loop in face.loops: selectedUVvert = loop[activeUvLayer] uvList = [] for targetIsland in targetIslands: for targetFace_id in targetIsland: targetFace = global_def.bm.faces[targetFace_id] for targetLoop in targetFace.loops: # take the a reference vert targetUvVert = targetLoop[activeUvLayer].uv # get a selected vert and calc it's distance from # the ref # add it to uvList dist = round( utils.vectorDistance(selectedUVvert.uv, targetUvVert), 10) uvList.append((dist, targetLoop[activeUvLayer])) # for every vert in uvList take the ones with the shortest # distnace from ref minDist = uvList[0][0] bestMatcher = 0 # 1st pass get lower dist for bestDist in uvList: if bestDist[0] <= minDist: minDist = bestDist[0] # 2nd pass get the only ones with a match for bestVert in uvList: if bestVert[0] <= minDist: bestMatcherList.append((bestVert[0], selectedUVvert, bestVert[1])) for bestMatcher in bestMatcherList: if bestMatcher[0] <= threshold: bestMatcher[1].uv = bestMatcher[2].uv
[docs] def isIsomorphic(self, other): """Test for isomorphism. Return a verterx mapping between two island if they are isomorphic or 'None' if there is no isomorphism. The returned mapped vertex index, correspond to the mesh vertex index and not the uv one. :param other: the other island :type other: :class:`.Island` :return: mapping between vertex or None :rtype: dict, None """ def graphFromIsland(island): edgeVertex = set() for face_id in island: face = global_def.bm.faces[face_id] for edges in face.edges: edgeVert = (edges.verts[0].index, edges.verts[1].index) edgeVertex.add(tuple(sorted(edgeVert, key=lambda data: data))) graph = networkx.Graph(tuple(edgeVertex)) return graph selfGraph = graphFromIsland(self) otheGraph = graphFromIsland(other) iso = networkx.isomorphism graphMatcher = iso.GraphMatcher(selfGraph, otheGraph) if graphMatcher.is_isomorphic(): return graphMatcher.mapping else: None