====== Python Notes, Used within Blender ====== * [[.addonnotes:|AddOn Notes]] * [[.addons:|AddOns Developed]] * [[programming:python:|Python Cheat Sheet]] To view "Run Script" output, display the console window, described here: http://wiki.blender.org/index.php/Doc:2.5/Manual/Interface/Window_system/Console_window When Blender is started on a Windows operating system, the Console Window is first created as a separate window on the desktop. Assuming that the right start-up conditions are met, the main Blender window should also appear and the Console Window will then be toggled off. This is unlike the 2.4x series where the Console Window would remain visible while the main Blender window was open. To display the console in current versions of Blender, go to Help ยป Toggle System Console. 24/Sep/2011 18:01 http://www.blender.org/documentation/blender_python_api_2_59_0/info_overview.html ===== Simple Display of Operators, Methods, Variables ===== ==== dir(some type, such as bpy.data) ==== dir (bpy.data) ['__doc__', '__module__', '__slots__', 'actions', 'annotations', 'armatures', 'batch_remove', 'bl_rna', 'brushes', 'cache_files', 'cameras', 'collections', 'colorspace', 'curves', 'file_path_foreach', 'file_path_map', 'filepath', 'fonts', 'grease_pencils', 'hair_curves', 'images', ... ==== help(some type, such as bpy.data) ==== help(bpy.data) | | is_property_overridable_library(...) | .. method:: is_property_overridable_library(property, /) | | Check if a property is overridable. | | :arg property: Property name. | :type property: str | :return: True when the property is overridable. | :rtype: bool | | is_property_readonly(...) | .. method:: is_property_readonly(property, /) | | Check if a property is readonly. | | :arg property: Property name. | :type property: str | :return: True when the property is readonly (not writable). | :rtype: bool ... bpy.data Access to blenders internal data Type : bpy.types.BlendData Ran this in the console: bpy.data >>> bpy.context.selected_objects[0].data.values It's possible that the vertexes aren't available. Saw note regarding built-in mesh object. http://wiki.blender.org/index.php/Doc:2.5/Manual/Introduction/Installing_Blender/DirectoryLayout 24/Sep/2011 21:05 ==== User Scripts Path ==== The user preferences script path provides a way to set your own directory which is used for scripts as well as the user scripts path. Be sure to create subfolders within this directory which match the structure of blenders scripts directory, startup/, addons/, modules/ etc. because copying scripts directly into this folder will not load them on startup or as addons. http://blenderartists.org/forum/showthread.php?226024-2.58-Mesh-UVs&highlight=access+to+UV+coordinates bpy.context.active_object.data.uv_textures[0].data[0] (can't be in edit mode) ....I run a count through the number of vertices in the mesh. The current count number is passed to a routine as the index number of a vertex. The routine iterates through the faces of the mesh. When the vertex number is found in one of the face vertices, the appropriate uv is retrieved, and the routine exits, returning the uv. ... uvs = {} for f, uv in zip(mesh.faces, mesh.uv_textures.active.data): for j, v in enumerate(f.vertices): uvs[v] = uv.uv_raw[2*j:2*j + 2] http://www.celsiusgs.com/blog/?p=102 >>> len(bpy.data.meshes["Mesh.001"].vertices) 1053 bpy.data.meshes["Mesh.001"] doesn't have .uv Has "NUT" exporter which also exports UVs, albeit in his own format: from: http://www.devmaster.net/forums/showthread.php?t=17915 Run in text window, click "Run Script". Make sure the mesh is not in edit mode: import bpy file = open('/tmp/temp.txt', 'w') mesh = bpy.data.meshes["Mesh.001"] for uvTexture in mesh.uv_textures: file.write("\t\t\t" % len(uvTexture.data)) uvDataCount = len(uvTexture.data) for i in range(uvDataCount): # UV per face index, not per vertex if i > 0: file.write(' ') uvCount = len(uvTexture.data[i].uv) for j in range(uvCount): if j > 0: file.write(" ") file.write("%f %f" % (uvTexture.data[i].uv[j][0], uvTexture.data[i].uv[j][1])) file.write("\n") file.close() To run example, do a File->Export->NUT format, and look at the result. 27/Sep/2011 23:24 ----------------------- ==== Make a script to change one UV coordinate: ==== import bpy mesh = bpy.data.meshes["Mesh.001"] mesh.uv_textures[0].data[0].uv[0][0] = mesh.uv_textures[0].data[0].uv[0][0] - .1 .. moved x coordinate of first UV a little to the left. This works. Need to make an algorithm to straighten the curve. Some thoughts. * form a virtual line that goes through points. Straighten those points. Mark them moved, repeat. * Hard to determine the corner points, to do the line above. * The UVs will be evenly spaced that are together for straightening. May be able to use this fact to determine those points that are together for straightening. * May need to investigate to see if there is a line between UVs that can be used to get neighbor. -------------------------- import bpy file = open('/tmp/temp.txt', 'w') mesh = bpy.data.meshes["Mesh.001"] # for one UV "island," there should only be one mesh.uv_textures for uvTexture in mesh.uv_textures: file.write("\t\t\t" % len(uvTexture.data)) uvDataCount = len(uvTexture.data) for i in range(uvDataCount): # UV per face index, not per vertex if i > 0: file.write(' ') uvCount = len(uvTexture.data[i].uv) for j in range(uvCount): if j > 0: file.write(" ") file.write("%f %f" % (uvTexture.data[i].uv[j][0], uvTexture.data[i].uv[j][1])) file.write("\n") file.close() # For spiralWrapTestBA.blend, following data shows up import bpy mesh = bpy.data.meshes["Mesh.001"] len(mesh.uv_textures) # prints 1 len(mesh.uv_textures[0].data) # prints 960 # now, lets check to see import bpy mesh = bpy.data.meshes["Mesh.001"] print ("Mess quantity", len(mesh.uv_textures)) print ("data quantity in Mesh 0:", len(mesh.uv_textures[0].data)) for i in range(20): if i > 0: print ("uvCount in data", i, len(mesh.uv_textures[0].data[i].uv)) # each shows 4 ---------------------- Lets look more closely at the array content: import bpy file = open('/tmp/temp.txt', 'w') mesh = bpy.data.meshes["Mesh.001"] # for one UV "island," there should only be one mesh.uv_textures for uvTexture in mesh.uv_textures: file.write("\t\t\t" % len(uvTexture.data)) uvDataCount = len(uvTexture.data) file.write("uvDataCount: %d\n" % uvDataCount); for i in range(uvDataCount): # UV per face index, not per vertex if i >= 0: file.write(" for uvDataCount (i value) %d\n" % i); uvCount = len(uvTexture.data[i].uv) for j in range(uvCount): if j >= 0: file.write(" for uvCount (j value) %d\n" % j); file.write(" ") file.write("%f %f" % (uvTexture.data[i].uv[j][0], uvTexture.data[i].uv[j][1])) file.write("\n") file.close() def It looks like a lot of the data is repeated. Need to store muliple occurrences so that when the UVs are moved, we move all of them together. absorb uvs into a usable datastructure: (works) import bpy file = open('/tmp/temp.txt', 'w') mesh = bpy.data.meshes["Mesh.001"] # Creating a blank dictionary d = {} # for one UV "island," there should only be one mesh.uv_textures for uvTexture in mesh.uv_textures: uvDataCount = len(uvTexture.data) for i in range(uvDataCount): # UV per face index, not per vertex if i >= 0: uvCount = len(uvTexture.data[i].uv) for j in range(uvCount): if j >= 0: myU = uvTexture.data[i].uv[j][0] myV = uvTexture.data[i].uv[j][1] tuv = myU, myV if not tuv in d: # create new UV record d[tuv] = [] # add blender indices that had those UV values tcoord = i, j d[tuv].append(tcoord) # Now, write to file to see if it worked for k in d.keys(): sf = '' for t3 in d[k]: sf += "(%i, %i) " % (t3[0], t3[1]) file.write(" " + ("%f, %f" % (k[0], k[1])) + ": " + sf + "\n") file.close() --------------------------------------------- To see if this will work, read in UVs, multiply each by 1.2, and see if it works import bpy mesh = bpy.data.meshes["Mesh.001"] # Creating a blank dictionary d = {} # for one UV "island," there should only be one mesh.uv_textures for uvTexture in mesh.uv_textures: uvDataCount = len(uvTexture.data) for i in range(uvDataCount): # UV per face index, not per vertex if i >= 0: uvCount = len(uvTexture.data[i].uv) for j in range(uvCount): if j >= 0: myU = uvTexture.data[i].uv[j][0] myV = uvTexture.data[i].uv[j][1] tuv = myU, myV if not tuv in d: # create new UV record d[tuv] = [] # add blender indices that had those UV values tcoord = i, j d[tuv].append(tcoord) # at this point, each record in d looks like this: # (2.5, 3.2), [(1, 5), (2, 9), (13, 19)] # where there is a UV coordinate tuple as a key, and an # array of tuples, representing the i, j locations in the array above # where these tuplies are found # Now, write back to initial array locations for k in d.keys(): for t3 in d[k]: uvTexture.data[t3[0]].uv[t3[1]][0] = k[0] * 1.2 uvTexture.data[t3[0]].uv[t3[1]][1] = k[1] * 1.2 # Worked! 3/Oct/2011 21:07 --------------------------- # Moving on, let's do some calculations def slope(tup1, tup2): if (abs(tup1[0] - tup2[0]) < .001): return 1000 return ((tup1[1] - tup2[1])/(tup1[0] - tup2[0])) import bpy mesh = bpy.data.meshes["Mesh.001"] # Creating a blank dictionary d = {} # for one UV "island," there should only be one mesh.uv_textures for uvTexture in mesh.uv_textures: uvDataCount = len(uvTexture.data) for i in range(uvDataCount): # UV per face index, not per vertex if i >= 0: uvCount = len(uvTexture.data[i].uv) for j in range(uvCount): if j >= 0: myU = uvTexture.data[i].uv[j][0] myV = uvTexture.data[i].uv[j][1] tuv = myU, myV if not tuv in d: # create new UV record d[tuv] = [] # add blender indices that had those UV values tcoord = i, j d[tuv].append(tcoord) # at this point, each record in d looks like this: # (2.5, 3.2), [(1, 5), (2, 9), (13, 19)] # where there is a UV coordinate tuple as a key, and an # array of tuples, representing the i, j locations in the array above # where these tuplies are found # Now, write back to initial array locations #for k in d.keys(): # for t3 in d[k]: # uvTexture.data[t3[0]].uv[t3[1]][0] = k[0] * 1.2 # uvTexture.data[t3[0]].uv[t3[1]][1] = k[1] * 1.2 # Determine center point for x and y directions minx = 0 maxx = 0 miny = 0 maxy = 0 for k in d.keys(): if minx == 0 or k[0] < minx: minx = k[0] if maxx == 0 or k[0] > maxx: maxx = k[0] if miny == 0 or k[1] < miny: miny = k[1] if maxy == 0 or k[1] > maxy: maxy = k[1] centerx = (minx + maxx) / 2 centery = (miny + maxy) / 2 print ("minx: ", minx) print ("maxx: ", maxx) print ("miny: ", miny) print ("maxy: ", maxy) print ("centerx: ", centerx) print ("centery: ", centery) # this will hold the new locations newloc = {} # later, loop starting here # find the upper-left most point that isn't in newloc # start off with lower-right upperleftUnmarked = maxx, miny for k in d.keys(): if k in newloc: continue if k == upperleftUnmarked: continue distSq = (k[1] - maxy) * (k[1] - maxy) + (k[0] - minx) * (k[0] - minx) if distSq < (upperleftUnmarked[1] - maxy) * (upperleftUnmarked[1] - maxy) + (upperleftUnmarked[0] - minx) * (upperleftUnmarked[0] - minx): upperleftUnmarked = k # upperleftUnmarked now has upperleft most non-newloc marked point # Find the highest-sloped point from here highslope = 0, 0 for k in d.keys(): if k in newloc: continue if k == upperleftUnmarked: continue if (highslope[0] == 0 and highslope[1] == 0) \ or slope(k, upperleftUnmarked) > slope(highslope, upperleftUnmarked) \ or (slope(k, upperleftUnmarked) == slope(highslope, upperleftUnmarked) \ and (upperleftUnmarked[1] - k[1]) > (upperleftUnmarked[1] - highslope[1])): highslope = k print ('upperleftUnmarked: ', upperleftUnmarked) print ('highslope: ', highslope)