blender/release/scripts/sel_same.py
Willian Padovani Germano a96ed881dc BPython:
- Scripts:
    fixed error in "Save Current Theme" which prevented it from automatically updating script registration in menus.
    cosmetic changes in a couple of Campbell's sel_same.py script strings + more descriptive name for its new menu place (3d view, face mode -> select menu).
    small updates to help_browser.py script.

 The above changes are related to this:
- Added new script menu entries: Render (for exporters to renderers), Themes, FaceSelect (this already at the proper place).  Updated Scripts win->Scripts menu so it won't show all available entries, only  the ones we mean to see there.
- Updated menu registration so that scripts folders can become trees.  The release/scripts/ dir should be updated soon with subdirs like converters/, modifiers/, generators/ or whatever -- better discuss first (or is it? /me afraid of long irc discussions during meetings :) ).

- Modules:
    Blender: added 'udatadir' option to .Get() function and added var Blender.mode to tell if Blender is in bg or interactive mode.
    NMesh: added Campbell's nmesh.transform(matrix, recalc_normals = False) method (reworked, so my fault if it doesn't work).

- Bugs fixed:
    #2123: http://projects.blender.org/tracker/?func=detail&atid=125&aid=2123&group_id=9
    Reported by Ken Hughes (thanks!), who also found the exact problem later (it was in Text.Load, not with script links -- if only I had checked emails these days ... lost > 1 hour today to find the problem: passed filename to M_Text_Load was later being written over by a function called by add_text).  Also saw that Text.Load wasn't checking existence of passed filename (duh!), now it does.

    #1655: http://projects.blender.org/tracker/?func=detail&atid=125&aid=1655&group_id=9
    Reported by Chris Want (thanks!): command line "blender -P script" not working properly for bg mode ("blender -b blendfile -P script").
    Had to make some small updates to get it working (bg mode for scripts was never explicitely handled, it worked due to collateral effects, let's say), interested readers can check the report after I update it or the API_intro.py doc file.  After more testing we can make further updates.  Updated many places to not call redraws if in bg mode, now it is officially available.  Blender outputs its own info when rendering in bg mode, if that is considered a nuissance we'll have to add a few "if (during_script())" calls outside bpython.

- Removed a few warnings here and there and also updated docs.
2005-03-19 06:24:55 +00:00

399 lines
12 KiB
Python

#!BPY
"""
Name: 'Similar to Active'
Blender: 234
Group: 'FaceSelect'
Tooltip: 'Select faces that match a given attribute of the active face'
"""
__author__ = "Campbell Barton"
__url__ = ["blender", "elysiun"]
__version__ = "1.0"
__bpydoc__ = """\
This script selects faces matching a given attribute of the currently
active face.
Usage:
Enter "UV Face Select" mode and make the desired face active. Then run this
script and choose the selection rule: by same (or similar for some itens):
- material;<br>
- texture image;<br>
- mode;<br>
- vertex colors;<br>
- uv coordinates;<br>
- area;<br>
- proportions;<br>
- normal vector;<br>
- coplanar.
Another menu will ask if the script should add, subtract, overwrite or
overwrite inverse of current current selection. For some choices like vcolors,
area, etc., a pop-up will ask for a maximum threshold value.
Notes:<br>
Again, to select / deselect faces, enter "UV Face Select" mode. This is not
the same as selecting faces in edit mode (new feature in Blender 2.35).
"""
# $Id$
#
#===============================================#
# Sel Same script 1.0 by Campbell Barton #
# email me ideasman@linuxmail.org #
#===============================================#
# --------------------------------------------------------------------------
# Sel Same Face 1.0 By Campbell Barton (AKA Ideasman)
# --------------------------------------------------------------------------
# ***** BEGIN GPL LICENSE BLOCK *****
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# ***** END GPL LICENCE BLOCK *****
# --------------------------------------------------------------------------
from Blender import *
from Blender.Mathutils import DotVecs, Vector
from math import sqrt
#====================================#
# Sanity checks #
#====================================#
def error(str):
Draw.PupMenu('ERROR: '+str)
af = None
selection = Object.GetSelected()
if len(selection) == 0:
error('No object selected')
else:
object = Object.GetSelected()[0]
if object.getType() != 'Mesh':
error('Active object must be a mesh')
else:
mesh = object.getData()
# We have a mesh so find AF.
af = mesh.getActiveFace()
if af: af = mesh.faces[af]
if af == None:
error('No active face')
else: # Okay everything seems sane
#=====================================
# Popup menu to select the functions #
#====================================#
method = Draw.PupMenu(\
'Selection Attribute%t|\
Material|\
UV Image|\
Face Mode|\
Vertex Colours|\
UV Coordinates|\
Area|\
Edge Proportions|\
Normal Vector|\
Coplanar|')
if method != -1:
#================================================#
# Do we add, seb or set to the existing face sel #
#================================================#
faceOp = Draw.PupMenu(\
'Active Face Match%t|\
Add to Selection|\
Subtract From Selection |\
Overwrite Selection|\
Overwrite Selection Inverse|')
if faceOp != -1:
def setFSel(f):
if faceOp == 1 or faceOp == 3:
f.flag |= NMesh.FaceFlags['SELECT'] # will set selection
elif faceOp == 2 or faceOp ==4:
f.flag &=~NMesh.FaceFlags['SELECT'] # will unselect, note the '~' to invert bitflags
def setFUnSel(f):
if faceOp == 3:
f.flag &=~NMesh.FaceFlags['SELECT'] # will unselect, note the '~' to invert bitflags
elif faceOp == 4:
f.flag |= NMesh.FaceFlags['SELECT'] # will set selection
#================#
# Math functions #
#================#
def compare(f1, f2, limit):
if f1 + limit > f2 and f1 - limit < f2:
return 1
return 0
def compare2(v1, v2, limit):
if v1[0] + limit > v2[0] and v1[0] - limit < v2[0]:
if v1[1] + limit > v2[1] and v1[1] - limit < v2[1]:
return 1
return 0
def compare3(v1, v2, limit):
if v1[0] + limit > v2[0] and v1[0] - limit < v2[0]:
if v1[1] + limit > v2[1] and v1[1] - limit < v2[1]:
if v1[2] + limit > v2[2] and v1[2] - limit < v2[2]:
return 1
return 0
def colCompare(v1, v2, limit):
# Simple test first
if v1.r == v2.r:
if v1.g == v2.g:
if v1.b == v2.b:
return 1
# Now a test that uses the limit.
limit = int(limit * 255)
if v1.r + limit >= v2.r and v1.r - limit <= v2.r:
if v1.g + limit >= v2.g and v1.g - limit <= v2.g:
if v1.b + limit >= v2.b and v1.b - limit <= v2.b:
return 1
return 0
# Makes sure face 2 has all the colours of face 1
def faceColCompare(f1, f2, limit):
avcolIdx = 0
while avcolIdx < len(f1.col):
match = 0
vcolIdx = 0
while vcolIdx < len(f2.col):
if colCompare(f1.col[avcolIdx], f2.col[vcolIdx], limit):
match = 1
break
vcolIdx += 1
if match == 0: # premature exit if a motch not found
return 0
avcolIdx += 1
return 1
# Makes sure face 2 has matching UVs within the limit.
def faceUvCompare(f1, f2, limit):
for auv in f1.uv:
match = 0
for uv in f2.uv:
if compare2(auv, uv, limit):
match = 1
break
if match == 0: # premature exit if a motch not found
return 0
return 1
def measure(v1, v2):
return Mathutils.Vector([v1[0]-v2[0], v1[1] - v2[1], v1[2] - v2[2]]).length
def triArea2D(v1, v2, v3):
e1 = measure(v1, v2)
e2 = measure(v2, v3)
e3 = measure(v3, v1)
p = e1+e2+e3
return 0.25 * sqrt(p*(p-2*e1)*(p-2*e2)*(p-2*e3))
#====================#
# End Math Functions #
#====================#
#=============================#
# Blender functions/shortcuts #
#=============================#
def getLimit(text):
return Draw.PupFloatInput(text, 0.1, 0.0, 1.0, 0.1, 3)
def faceArea(f):
if len(f.v) == 4:
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)
elif len(f.v) == 3:
return triArea2D(f.v[0].co, f.v[1].co, f.v[2].co)
def getEdgeLengths(f):
if len(f.v) == 4:
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) )
elif len(f.v) == 3:
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) )
def faceCent(f):
x = y = z = 0
for v in f.v:
x += v.co[0]
y += v.co[1]
z += v.co[2]
x = x/len(f.v)
y = y/len(f.v)
z = z/len(f.v)
return Vector([x, y, z])
#========================================#
# Should we bother computing this faces #
#========================================#
def fShouldCompare(f):
# Only calculate for faces that will be affected.
if len(f.v) < 3: # cant be an edge
return 0
elif faceOp == 1 and f.flag == 1:
return 0
elif faceOp == 0 and f.flag == 0:
return 0
elif f.flag == 64: # Ignore hidden
return 0
return 1
#=======================================#
# Sel same funcs as called by the menus #
#=======================================#
def get_same_mat():
for f in mesh.faces:
if fShouldCompare(f):
if af.mat == f.mat: setFSel(f)
else: setFUnSel(f)
def get_same_image():
if mesh.hasFaceUV() == 0:
error('mesh has no uv image')
else:
for f in mesh.faces:
if fShouldCompare(f):
if af.image == f.image: setFSel(f)
else: setFUnSel(f)
def get_same_mode():
for f in mesh.faces:
if fShouldCompare(f):
if af.mode == f.mode: setFSel(f)
else: setFUnSel(f)
def get_same_vcol(limit):
for f in mesh.faces:
if fShouldCompare(f):
if faceColCompare(f, af, limit) and faceColCompare(af, f, limit) :
setFSel(f)
else:
setFUnSel(f)
def get_same_uvco(limit):
for f in mesh.faces:
if fShouldCompare(f):
if faceUvCompare(af, f, limit): setFSel(f)
else: setFUnSel(f)
def get_same_area(limit):
afArea = faceArea(af)
limit = limit * afArea # Make the lomot proportinal to the
for f in mesh.faces:
if fShouldCompare(f):
if compare(afArea, faceArea(f), limit): setFSel(f)
else: setFUnSel(f)
def get_same_prop(limit):
# Here we get the perimeter and use it for a proportional limit modifier.
afEdgeLens = getEdgeLengths(af)
perim = 0
for e in afEdgeLens:
perim += e
limit = limit * perim
for f in mesh.faces:
if fShouldCompare(f):
for ae in afEdgeLens:
match = 0
for e in getEdgeLengths(f):
if compare(ae, e, limit):
match = 1
break
if not match:
break
if match: setFSel(f)
else: setFUnSel(f)
def get_same_normal(limit):
limit = limit * 2
for f in mesh.faces:
if fShouldCompare(f):
if compare3(af.no, f.no, limit): setFSel(f)
else: setFUnSel(f)
def get_same_coplaner(limit):
nlimit = limit * 2 # * 1 # limit for normal test
climit = limit * 3 # limit for coplaner test.
afCent = faceCent(af)
for f in mesh.faces:
if fShouldCompare(f):
match = 0
if compare3(af.no, f.no, nlimit):
fCent = faceCent(f)
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:
match = 1
if match:
setFSel(f)
else:
setFUnSel(f)
#=====================#
# End Sel same funcs #
#=====================#
limit = 1 # some of these dont use the limit so it needs to be set, to somthing.
# act on the menu item selected
if method == 1: # Material
get_same_mat()
elif method == 2: # UV Image
get_same_image()
elif method == 3: # mode
get_same_mode()
elif method == 4: # vertex colours
limit = getLimit('vert col limit: ')
if limit != None:
get_same_vcol(limit)
elif method == 5: # UV-coords
limit = getLimit('uv-coord limit: ')
if limit != None:
get_same_uvco(limit)
elif method == 6: # area
limit = getLimit('area limit: ')
if limit != None:
get_same_area(limit)
elif method == 7: # proportions
limit = getLimit('proportion limit: ')
if limit != None:
get_same_prop(limit)
elif method == 8: # normal
limit = getLimit('normal limit: ')
if limit != None:
get_same_normal(limit)
elif method == 9: # coplaner
limit = getLimit('coplanar limit: ')
if limit != None:
get_same_coplaner(limit)
# If limit is not set then dont bother
if limit != None:
mesh.update(0)