home *** CD-ROM | disk | FTP | other *** search
/ Windows News 2005 November / WNnov2005.iso / Windows / Equipement / Blender / blender-2.37a-windows.exe / $_5_ / .blender / scripts / sel_same.py < prev    next >
Text File  |  2005-03-26  |  13KB  |  399 lines

  1. #!BPY
  2.  
  3. """
  4. Name: 'Similar to Active'
  5. Blender: 234
  6. Group: 'FaceSelect'
  7. Tooltip: 'Select faces that match a given attribute of the active face'
  8. """
  9.  
  10. __author__ = "Campbell Barton"
  11. __url__ = ["blender", "elysiun"]
  12. __version__ = "1.0"
  13.  
  14. __bpydoc__ = """\
  15. This script selects faces matching a given attribute of the currently
  16. active face.
  17.  
  18. Usage:
  19.  
  20. Enter "UV Face Select" mode and make the desired face active.  Then run this
  21. script and choose the selection rule: by same (or similar for some itens):
  22.  
  23. - material;<br>
  24. - texture image;<br>
  25. - mode;<br>
  26. - vertex colors;<br>
  27. - uv coordinates;<br>
  28. - area;<br>
  29. - proportions;<br>
  30. - normal vector;<br>
  31. - coplanar.
  32.  
  33. Another menu will ask if the script should add, subtract, overwrite or
  34. overwrite inverse of current current selection.  For some choices like vcolors,
  35. area, etc., a pop-up will ask for a maximum threshold value.
  36.  
  37. Notes:<br>
  38.    Again, to select / deselect faces, enter "UV Face Select" mode.  This is not
  39. the same as selecting faces in edit mode (new feature in Blender 2.35).
  40. """
  41.  
  42. # $Id: sel_same.py,v 1.5 2005/03/19 06:24:54 ianwill Exp $
  43. #
  44. #===============================================#
  45. # Sel Same script 1.0 by Campbell Barton        #
  46. # email me ideasman@linuxmail.org               #
  47. #===============================================#
  48.  
  49.  
  50. # -------------------------------------------------------------------------- 
  51. # Sel Same Face 1.0 By Campbell Barton (AKA Ideasman)
  52. # -------------------------------------------------------------------------- 
  53. # ***** BEGIN GPL LICENSE BLOCK ***** 
  54. # This program is free software; you can redistribute it and/or 
  55. # modify it under the terms of the GNU General Public License 
  56. # as published by the Free Software Foundation; either version 2 
  57. # of the License, or (at your option) any later version. 
  58. # This program is distributed in the hope that it will be useful, 
  59. # but WITHOUT ANY WARRANTY; without even the implied warranty of 
  60. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
  61. # GNU General Public License for more details. 
  62. # You should have received a copy of the GNU General Public License 
  63. # along with this program; if not, write to the Free Software Foundation, 
  64. # Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA. 
  65. # ***** END GPL LICENCE BLOCK ***** 
  66. # -------------------------------------------------------------------------- 
  67.  
  68.  
  69.  
  70. from Blender import *
  71. from Blender.Mathutils import DotVecs, Vector
  72. from math import sqrt
  73.  
  74.  
  75. #====================================#
  76. # Sanity checks                      #
  77. #====================================#
  78. def error(str):
  79.     Draw.PupMenu('ERROR: '+str)
  80. af = None 
  81. selection = Object.GetSelected()
  82. if len(selection) == 0:
  83.   error('No object selected')
  84. else:
  85.   object = Object.GetSelected()[0]
  86.   if object.getType() != 'Mesh':
  87.     error('Active object must be a mesh')
  88.   else:
  89.     mesh = object.getData()
  90.     
  91.     # We have a mesh so find AF.
  92.     af = mesh.getActiveFace()
  93.     if af: af = mesh.faces[af]
  94.  
  95. if af == None:
  96.   error('No active face')
  97.  
  98. else: # Okay everything seems sane
  99.   
  100.   #=====================================
  101.   # Popup menu to select the functions #
  102.   #====================================#
  103.   method = Draw.PupMenu(\
  104.   'Selection Attribute%t|\
  105.   Material|\
  106.   UV Image|\
  107.   Face Mode|\
  108.   Vertex Colours|\
  109.   UV Coordinates|\
  110.   Area|\
  111.   Edge Proportions|\
  112.   Normal Vector|\
  113.   Coplanar|')
  114.   
  115.   if method != -1:
  116.     #================================================#
  117.     # Do we add, seb or set to the existing face sel #
  118.     #================================================#
  119.     faceOp = Draw.PupMenu(\
  120.     'Active Face Match%t|\
  121.     Add to Selection|\
  122.     Subtract From Selection |\
  123.     Overwrite Selection|\
  124.     Overwrite Selection Inverse|')
  125.     
  126.     if faceOp != -1:
  127.       
  128.       def setFSel(f):
  129.         if faceOp == 1 or faceOp == 3:
  130.           f.flag |= NMesh.FaceFlags['SELECT'] # will set selection
  131.         elif faceOp == 2 or faceOp ==4:
  132.           f.flag &=~NMesh.FaceFlags['SELECT'] # will unselect, note the '~' to invert bitflags
  133.       
  134.       def setFUnSel(f):
  135.         if faceOp == 3:
  136.           f.flag &=~NMesh.FaceFlags['SELECT'] # will unselect, note the '~' to invert bitflags
  137.         elif faceOp == 4:
  138.           f.flag |= NMesh.FaceFlags['SELECT'] # will set selection
  139.       
  140.       
  141.       
  142.       #================#
  143.       # Math functions #
  144.       #================#
  145.       def compare(f1, f2, limit):
  146.         if f1 + limit > f2 and f1 - limit < f2:
  147.           return 1
  148.         return 0
  149.       
  150.       def compare2(v1, v2, limit):
  151.         if v1[0] + limit > v2[0] and v1[0] - limit < v2[0]:
  152.             if v1[1] + limit > v2[1] and v1[1] - limit < v2[1]:
  153.               return 1
  154.         return 0
  155.       
  156.       def compare3(v1, v2, limit):
  157.         if v1[0] + limit > v2[0] and v1[0] - limit < v2[0]:
  158.             if v1[1] + limit > v2[1] and v1[1] - limit < v2[1]:
  159.               if v1[2] + limit > v2[2] and v1[2] - limit < v2[2]:
  160.                 return 1
  161.         return 0
  162.       
  163.       def colCompare(v1, v2, limit):
  164.         # Simple test first
  165.         if v1.r == v2.r:
  166.           if v1.g == v2.g:
  167.             if v1.b == v2.b:
  168.               return 1  
  169.         # Now a test that uses the limit.
  170.         limit = int(limit * 255)
  171.         if v1.r + limit >= v2.r and v1.r - limit <= v2.r:
  172.           if v1.g + limit >= v2.g and v1.g - limit <= v2.g:
  173.             if v1.b + limit >= v2.b and v1.b - limit <= v2.b:
  174.               return 1
  175.         return 0
  176.       
  177.       # Makes sure face 2 has all the colours of face 1
  178.       def faceColCompare(f1, f2, limit):
  179.         avcolIdx = 0
  180.         while avcolIdx < len(f1.col):
  181.           match = 0
  182.           
  183.           vcolIdx = 0
  184.           while vcolIdx < len(f2.col):
  185.             if colCompare(f1.col[avcolIdx], f2.col[vcolIdx], limit):
  186.               match = 1
  187.               break
  188.             vcolIdx += 1
  189.           if match == 0: # premature exit if a motch not found
  190.             return 0
  191.           avcolIdx += 1
  192.         return 1
  193.       
  194.       # Makes sure face 2 has matching UVs within the limit.
  195.       def faceUvCompare(f1, f2, limit):
  196.         for auv in f1.uv:
  197.           match = 0
  198.           for uv in f2.uv:
  199.             if compare2(auv, uv, limit):
  200.               match = 1
  201.               break
  202.           if match == 0: # premature exit if a motch not found
  203.             return 0
  204.         return 1
  205.       
  206.       
  207.       def measure(v1, v2):
  208.         return Mathutils.Vector([v1[0]-v2[0], v1[1] - v2[1], v1[2] - v2[2]]).length
  209.       
  210.       def triArea2D(v1, v2, v3):
  211.         e1 = measure(v1, v2)  
  212.         e2 = measure(v2, v3)  
  213.         e3 = measure(v3, v1)  
  214.         p = e1+e2+e3
  215.         return 0.25 * sqrt(p*(p-2*e1)*(p-2*e2)*(p-2*e3))
  216.       #====================#
  217.       # End Math Functions #
  218.       #====================#
  219.       
  220.       
  221.       #=============================#
  222.       # Blender functions/shortcuts #
  223.       #=============================#
  224.       def getLimit(text):
  225.         return Draw.PupFloatInput(text, 0.1, 0.0, 1.0, 0.1, 3)
  226.       
  227.       def faceArea(f):
  228.         if len(f.v) == 4:
  229.           return triArea2D(f.v[0].co, f.v[1].co, f.v[2].co) + triArea2D(f.v[0].co, f.v[2].co, f.v[3].co)
  230.         elif len(f.v) == 3:
  231.           return triArea2D(f.v[0].co, f.v[1].co, f.v[2].co)
  232.         
  233.       def getEdgeLengths(f):
  234.         if len(f.v) == 4:
  235.           return (measure(f.v[0].co, f.v[1].co), measure(f.v[1].co, f.v[2].co), measure(f.v[2].co, f.v[3].co) , measure(f.v[3].co, f.v[0].co) )
  236.         elif len(f.v) == 3:
  237.           return (measure(f.v[0].co, f.v[1].co), measure(f.v[1].co, f.v[2].co), measure(f.v[2].co, f.v[0].co) )
  238.       
  239.       def faceCent(f):
  240.         x = y = z = 0
  241.         for v in f.v:
  242.           x += v.co[0]
  243.           y += v.co[1]
  244.           z += v.co[2]
  245.         x = x/len(f.v)
  246.         y = y/len(f.v)
  247.         z = z/len(f.v)
  248.         return Vector([x, y, z])
  249.       
  250.       #========================================#
  251.       # Should we bother computing this faces  #
  252.       #========================================#
  253.       def fShouldCompare(f):
  254.         # Only calculate for faces that will be affected.
  255.         if len(f.v) < 3: # cant be an edge
  256.           return 0
  257.         elif faceOp == 1 and f.flag == 1:
  258.           return 0
  259.         elif faceOp == 0 and f.flag == 0:
  260.           return 0
  261.         elif f.flag == 64: # Ignore hidden
  262.           return 0
  263.         return 1
  264.         
  265.       #=======================================#
  266.       # Sel same funcs as called by the menus #
  267.       #=======================================#
  268.       def get_same_mat():
  269.         for f in mesh.faces:
  270.           if fShouldCompare(f):
  271.             if af.mat == f.mat: setFSel(f)
  272.             else:               setFUnSel(f)
  273.       
  274.       def get_same_image():
  275.         if mesh.hasFaceUV() == 0:
  276.           error('mesh has no uv image')
  277.         else:
  278.           for f in mesh.faces:
  279.             if fShouldCompare(f):
  280.               if af.image == f.image: setFSel(f)
  281.               else:                   setFUnSel(f)
  282.       
  283.       def get_same_mode():
  284.         for f in mesh.faces:
  285.           if fShouldCompare(f):
  286.             if af.mode == f.mode: setFSel(f)
  287.             else:                 setFUnSel(f)
  288.       
  289.       def get_same_vcol(limit):
  290.         for f in mesh.faces:
  291.           if fShouldCompare(f):
  292.             if faceColCompare(f, af, limit) and faceColCompare(af, f, limit) :
  293.               setFSel(f)
  294.             else:
  295.               setFUnSel(f)
  296.       
  297.       def get_same_uvco(limit):
  298.         for f in mesh.faces:
  299.           if fShouldCompare(f):
  300.             if faceUvCompare(af, f, limit): setFSel(f)
  301.             else:                           setFUnSel(f)
  302.       
  303.       def get_same_area(limit):
  304.         afArea = faceArea(af)
  305.         limit = limit * afArea # Make the lomot proportinal to the 
  306.         for f in mesh.faces:
  307.           if fShouldCompare(f):
  308.             if compare(afArea, faceArea(f), limit): setFSel(f)
  309.             else:                                   setFUnSel(f)
  310.       
  311.       def get_same_prop(limit):
  312.         # Here we get the perimeter and use it for a proportional limit modifier.
  313.         afEdgeLens = getEdgeLengths(af)
  314.         perim = 0
  315.         for e in afEdgeLens:
  316.           perim += e
  317.       
  318.         limit = limit * perim
  319.         for f in mesh.faces:
  320.           if fShouldCompare(f):
  321.             for ae in afEdgeLens:
  322.               match = 0
  323.               for e in getEdgeLengths(f):
  324.                 if compare(ae, e, limit):
  325.                   match = 1
  326.                   break
  327.               if not match:
  328.                 break
  329.            
  330.             if match: setFSel(f)
  331.             else:     setFUnSel(f)
  332.       
  333.       def get_same_normal(limit):    
  334.         limit = limit * 2
  335.         for f in mesh.faces:
  336.           if fShouldCompare(f):
  337.             if compare3(af.no, f.no, limit): setFSel(f)
  338.             else:                            setFUnSel(f)
  339.       
  340.       def get_same_coplaner(limit):
  341.         nlimit = limit * 2 # * 1 # limit for normal test
  342.         climit = limit * 3 # limit for coplaner test.
  343.         afCent = faceCent(af)
  344.         for f in mesh.faces:
  345.           if fShouldCompare(f):
  346.             match = 0
  347.             if compare3(af.no, f.no, nlimit):
  348.               fCent = faceCent(f)
  349.               if abs(DotVecs(Vector([af.no[0], af.no[1], af.no[2]]), afCent ) - DotVecs(Vector([af.no[0], af.no[1], af.no[2]]), fCent )) <= climit:
  350.                 match = 1
  351.             if match:
  352.               setFSel(f)
  353.             else:
  354.               setFUnSel(f)
  355.       
  356.       #=====================#
  357.       # End Sel same funcs  #
  358.       #=====================#
  359.       limit = 1 # some of these dont use the limit so it needs to be set, to somthing.
  360.       # act on the menu item selected
  361.       if method == 1: # Material
  362.         get_same_mat()
  363.       elif method == 2: # UV Image
  364.         get_same_image()
  365.       elif method == 3: # mode
  366.         get_same_mode()
  367.       elif method == 4: # vertex colours
  368.         limit = getLimit('vert col limit: ')
  369.         if limit != None:
  370.           get_same_vcol(limit)
  371.       elif method == 5: # UV-coords
  372.         limit = getLimit('uv-coord limit: ')
  373.         if limit != None:
  374.           get_same_uvco(limit)
  375.       elif method == 6: # area
  376.         limit = getLimit('area limit: ')
  377.         if limit != None:
  378.           get_same_area(limit)
  379.       elif method == 7: # proportions
  380.         limit = getLimit('proportion limit: ')
  381.         if limit != None:
  382.           get_same_prop(limit)
  383.       elif method == 8: # normal
  384.         limit = getLimit('normal limit: ')
  385.         if limit != None:
  386.           get_same_normal(limit)
  387.       elif method == 9: # coplaner
  388.         limit = getLimit('coplanar limit: ')
  389.         if limit != None:
  390.           get_same_coplaner(limit)
  391.       
  392.       # If limit is not set then dont bother
  393.       if limit != None:
  394.         mesh.update(0)
  395.