# Copyright (C) 2011, Dolf Veenvliet # Extrude a selection from a mesh multiple times # ##### 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # # ##### END GPL LICENSE BLOCK ##### """ Usage: Select all items whose normals face a certain direction Additional links: Author Site: http://www.macouno.com e-mail: dolf {at} macouno {dot} com """ import bpy from bpy.types import Operator from mathutils import Vector from math import radians from bpy.props import ( FloatVectorProperty, FloatProperty, BoolProperty, EnumProperty, ) class Select_by_direction(): # Initialise the class def __init__(self, context, direction, divergence, extend, space): self.ob = context.active_object bpy.ops.object.mode_set(mode='OBJECT') self.space = space # if we do stuff in global space we need to object matrix if self.space == 'GLO': # TODO: not sure if this is the correct way to solve the crash - lijenstina mat = self.ob.matrix_world mat_rot = mat.to_3x3().inverted() direction = mat_rot * Vector(direction) else: direction = Vector(direction) direction = direction.normalized() vertSelect = bpy.context.tool_settings.mesh_select_mode[0] edgeSelect = bpy.context.tool_settings.mesh_select_mode[1] faceSelect = bpy.context.tool_settings.mesh_select_mode[2] if Vector(direction).length: # Vert select if vertSelect: hasSelected = self.hasSelected(self.ob.data.vertices) for v in self.ob.data.vertices: normal = v.normal s = self.selectCheck(v.select, hasSelected, extend) d = self.deselectCheck(v.select, hasSelected, extend) if s or d: angle = direction.angle(normal) # Check if the verts match any of the directions if s and angle <= divergence: v.select = True if d and angle > divergence: v.select = False # Edge select if edgeSelect: hasSelected = self.hasSelected(self.ob.data.edges) for e in self.ob.data.edges: s = self.selectCheck(e.select, hasSelected, extend) d = self.deselectCheck(e.select, hasSelected, extend) # Check if the edges match any of the directions if s or d: normal = self.ob.data.vertices[e.vertices[0]].normal normal += self.ob.data.vertices[e.vertices[1]].normal angle = direction.angle(normal) if s and angle <= divergence: e.select = True if d and angle > divergence: e.select = False # Face select if faceSelect: hasSelected = self.hasSelected(self.ob.data.polygons) # Loop through all the given faces for f in self.ob.data.polygons: s = self.selectCheck(f.select, hasSelected, extend) d = self.deselectCheck(f.select, hasSelected, extend) if s or d: angle = direction.angle(f.normal) # Check if the faces match any of the directions if s and angle <= divergence: f.select = True if d and angle > divergence: f.select = False bpy.ops.object.mode_set(mode='EDIT') # See if the current item should be selected or not def selectCheck(self, isSelected, hasSelected, extend): # If the current item is not selected we may want to select if not isSelected: # If we are extending or nothing is selected we want to select if extend or not hasSelected: return True return False # See if the current item should be deselected or not def deselectCheck(self, isSelected, hasSelected, extend): # If the current item is selected we may want to deselect if isSelected: # If something is selected and we're not extending we want to deselect if hasSelected and not extend: return True return False # See if there is at least one selected item def hasSelected(self, items): for item in items: if item.select: return True return False class Select_init(Operator): bl_idname = "mesh.select_by_direction" bl_label = "Select by direction" bl_description = ("Select all items with normals facing a certain direction,\n" "defined by a vector with coordinates X, Y, Z") bl_options = {'REGISTER', 'UNDO'} direction = FloatVectorProperty( name="Direction", description="Define a vector from the inputs axis X, Y, Z\n" "Used to define the normals direction", default=(0.0, 0.0, 1.0), min=-100.0, max=100.0, soft_min=-10.0, soft_max=10.0, step=100, precision=2 ) divergence = FloatProperty( name="Divergence", description="The number of degrees the selection may differ from the Vector\n" "(Input is converted to radians)", default=radians(30.0), min=0.0, max=radians(360.0), soft_min=0.0, soft_max=radians(360.0), step=radians(5000), precision=2, subtype='ANGLE' ) extend = BoolProperty( name="Extend", description="Extend the current selection", default=False ) # The spaces we use spaces = (('LOC', 'Local', ''), ('GLO', 'Global', '')) space = EnumProperty( items=spaces, name="Space", description="The space to interpret the directions in", default='LOC' ) @classmethod def poll(cls, context): obj = context.active_object return (obj and obj.type == 'MESH') def execute(self, context): Select_by_direction(context, self.direction, self.divergence, self.extend, self.space) return {'FINISHED'}