Mesh Extra Tools: Update to version 0.33, various fixes

Bump version to 0.33
Some Pep8 clean up and general fixes
Some UI updates

Replace Mesh to wall with a similar results script
mesh_edges_floor_plan (for reasons see T51483)
It is not 1 to 1 replacement but should be more stable

If someone wants to continue development it's absolutely
possible to be re-included later on

Vertex Align: cleanup and refactor code
introduce a stored alignment setting, proper help operator call
move the property group to init, remove register call

Mesh Check: cleanup
Move the UI element draw code from init
to the Face/Info select, cleanup

mesh_info_select:
Add a timer for redraw (not sure if it is the best solution)
Add settings remove refresh operator (as edit mode is available)

Mesh Edge Tools:
remove the Intersect_Line_Face operator, add general error handling
fix several crashes with non proper selections passed

Select tools - replace deprecated imp call
This commit is contained in:
Vuk Gardašević 2017-05-21 07:26:34 +02:00
parent aaeb073354
commit 48a69db544
32 changed files with 3313 additions and 2946 deletions

View File

@ -17,20 +17,21 @@
# ##### END GPL LICENSE BLOCK #####
# Contributed to by:
# meta-androcto, Hidesato Ikeya, zmj100, luxuy_BlenderCN, TrumanBlending, PKHG, #
# meta-androcto, Hidesato Ikeya, zmj100, Gert De Roost, TrumanBlending, PKHG, #
# Oscurart, Greg, Stanislav Blinov, komi3D, BlenderLab, Paul Marshall (brikbot), #
# metalliandy, macouno, CoDEmanX, dustractor, Liero, lijenstina, Germano Cavalcante #
# Pistiwique, Jimmy Hazevoet #
bl_info = {
"name": "Edit Tools 2",
"author": "meta-androcto",
"version": (0, 3, 2),
"version": (0, 3, 3),
"blender": (2, 78, 0),
"location": "View3D > Toolshelf > Tools and Specials (W-key)",
"description": "Extra mesh edit tools - modifying meshes and selection",
"warning": "",
"wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/Py/Scripts/Modeling/Extra_Tools",
"tracker_url": "https://developer.blender.org/maniphest/task/edit/form/2/",
"wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/"
"Py/Scripts/Modeling/Extra_Tools",
"category": "Mesh"}
@ -46,7 +47,7 @@ if "bpy" in locals():
importlib.reload(mesh_edge_roundifier)
importlib.reload(mesh_cut_faces)
importlib.reload(split_solidify)
importlib.reload(mesh_to_wall)
importlib.reload(mesh_edges_floor_plan)
importlib.reload(mesh_edges_length)
importlib.reload(random_vertices)
importlib.reload(mesh_fastloop)
@ -76,7 +77,7 @@ else:
from . import mesh_edge_roundifier
from . import mesh_cut_faces
from . import split_solidify
from . import mesh_to_wall
from . import mesh_edges_floor_plan
from . import mesh_edges_length
from . import random_vertices
from . import mesh_fastloop
@ -101,8 +102,6 @@ else:
import bpy
import bpy_extras.keyconfig_utils
import bmesh
from bpy.props import EnumProperty
from bpy.types import (
Menu,
Panel,
@ -112,6 +111,9 @@ from bpy.types import (
from bpy.props import (
BoolProperty,
BoolVectorProperty,
EnumProperty,
FloatProperty,
FloatVectorProperty,
IntVectorProperty,
PointerProperty,
)
@ -159,7 +161,7 @@ class VIEW3D_MT_edit_mesh_extras(Menu):
col.operator("mesh.offset_edges", text="Offset Edges")
col.operator("mesh.edge_roundifier", text="Edge Roundify")
col.operator("object.mesh_edge_length_set", text="Set Edge Length")
col.operator("bpt.mesh_to_wall", text="Edge(s) to Wall")
col.operator("mesh.edges_floor_plan")
col = split.column()
col.label(text="Utilities", icon="SCRIPTWIN")
@ -241,21 +243,35 @@ class EditToolsPanel(Panel):
row.operator("mesh.random_vertices", text="Random Vertices")
row.operator("mesh.extra_tools_help",
icon="LAYER_USED").help_ids = "random_vertices"
# Vertex Align Properties And Menu
cen0 = context.scene.va_custom_props.en0 # get the properties list
layout = self.layout
layout.label(text="Vertex Align:", icon="VERTEXSEL")
layout.prop(context.scene.va_custom_props, 'en0', expand = False) # Draw the menu with 2 options
if cen0 == 'vertex':
row = layout.split(0.60)
row.label('Store data:')
row.operator('va.op0_store_id', text = 'Vertex')
row1 = layout.split(0.8, align=True)
row1.operator('va.op2_align_id', text = 'Align to Axis')
row1.operator('va.op7_help_id', text = '', icon = "LAYER_USED")
elif cen0 == 'coordinates':
layout.operator('va.op3_coord_list_id', text = 'Align Coordinates')
# Vertex Align Properties And Menu
cen0 = scene.mesh_extra_tools.vert_align_to
layout = self.layout
layout.label(text="Vertex Align:", icon="UV_VERTEXSEL")
# Draw the menu with 2 options
layout.prop(scene.mesh_extra_tools, "vert_align_to", expand=False)
if cen0 == 'vertex':
row = layout.row(align=True)
row.operator("vertex_align.store_id", text="Store Selected Vertex")
row = layout.split(0.8, align=True)
row.operator("vertex_align.align_original", text="Align to Axis")
props = row.operator("mesh.extra_tools_help", icon="LAYER_USED")
props.help_ids = "vertex_align"
props.popup_size = 400
elif cen0 == "coordinates":
layout.prop(scene.mesh_extra_tools, "vert_align_use_stored", toggle=True)
if scene.mesh_extra_tools.vert_align_use_stored:
col = layout.column(align=True)
col.prop(scene.mesh_extra_tools, "vert_align_store_axis", expand=True)
row = layout.split(0.8, align=True)
row.operator("vertex_align.coord_list_id", text="Align Coordinates")
row.operator("mesh.extra_tools_help",
icon="LAYER_USED").help_ids = "vertex_align"
# Edge options
box1 = self.layout.box()
@ -276,9 +292,9 @@ class EditToolsPanel(Panel):
row = layout.split(0.8, align=True)
row.operator("mesh.fillet_plus", text="Fillet plus")
prop = row.operator("mesh.extra_tools_help", icon="LAYER_USED")
prop.help_ids = "mesh_filletplus"
prop.popup_size = 400
props = row.operator("mesh.extra_tools_help", icon="LAYER_USED")
props.help_ids = "mesh_filletplus"
props.popup_size = 400
row = layout.split(0.8, align=True)
row.operator("mesh.offset_edges", text="Offset Edges")
@ -296,9 +312,11 @@ class EditToolsPanel(Panel):
icon="LAYER_USED").help_ids = "mesh_edges_length"
row = layout.split(0.8, align=True)
row.operator("bpt.mesh_to_wall", text="Edge(s) to Wall")
row.operator("mesh.extra_tools_help",
icon="LAYER_USED").help_ids = "mesh_to_wall"
row.operator("mesh.edges_floor_plan")
props = row.operator("mesh.extra_tools_help", icon="LAYER_USED")
props.help_ids = "mesh_edges_floor_plan"
props.popup_size = 400
# Face options
box1 = self.layout.box()
@ -364,82 +382,73 @@ class EditToolsPanel(Panel):
row = layout.split(0.8, align=True)
row.operator("object_ot.fastloop", text="Fast Loop")
prop = row.operator("mesh.extra_tools_help", icon="LAYER_USED")
prop.help_ids = "mesh_fastloop"
prop.popup_size = 400
props = row.operator("mesh.extra_tools_help", icon="LAYER_USED")
props.help_ids = "mesh_fastloop"
props.popup_size = 400
row = layout.row()
row.operator("mesh.flip_normals", text="Normals Flip")
row = layout.row()
row.operator("mesh.remove_doubles", text="Remove Doubles")
row = layout.row()
row.operator("mesh.subdivide", text="Subdivide")
row = layout.row()
row.operator("mesh.dissolve_limited", text="Dissolve Limited")
col = layout.column(align=True)
col.operator("mesh.flip_normals", text="Normals Flip")
col.operator("mesh.remove_doubles", text="Remove Doubles")
col.operator("mesh.subdivide", text="Subdivide")
col.operator("mesh.dissolve_limited", text="Dissolve Limited")
row = layout.row(align=True)
row.operator("mesh.select_vert_edge_face_index",
icon="VERTEXSEL", text="Select By Index").select_type = 'VERT'
# Mesh Check
layout = self.layout
icons = load_icons()
tris = icons.get("triangles")
ngons = icons.get("ngons")
mesh_check = context.window_manager.mesh_check
layout.prop(mesh_check, "mesh_check_use")
icon_active_4 = "TRIA_RIGHT" if not mesh_check.mesh_check_use else "TRIA_DOWN"
row = layout.row()
row = layout.split(0.8, align=True)
row.prop(mesh_check, "mesh_check_use", toggle=True, icon=icon_active_4)
row.operator("mesh.extra_tools_help", icon="LAYER_USED").help_ids = "mesh_check"
if mesh_check.mesh_check_use:
layout = self.layout
row = layout.row()
row.operator("object.face_type_select", text="Tris", icon_value=tris.icon_id).face_type = 'tris'
row.operator("object.face_type_select", text="Ngons",icon_value=ngons.icon_id).face_type = 'ngons'
row = layout.row(align=True)
row.operator("object.face_type_select", text="Tris",
icon_value=tris.icon_id).face_type = 'tris'
row.operator("object.face_type_select", text="Ngons",
icon_value=ngons.icon_id).face_type = 'ngons'
row = layout.row()
row.prop(mesh_check, "display_faces", text="Display Faces")
if mesh_check.display_faces:
col = layout.column(align=True)
col.prop(mesh_check, "edge_width")
col.prop(mesh_check, "face_opacity")
row = layout.row()
row.prop(mesh_check, "edge_width")
row = layout.row()
row.prop(mesh_check, "custom_tri_color",text="Tris color" )
row = layout.row()
row.prop(mesh_check, "custom_ngons_color")
row = layout.row()
row.prop(mesh_check, "face_opacity")
if bpy.context.object.mode == 'EDIT':
obj = bpy.context.object
me = obj.data
bm = bmesh.from_edit_mesh(me)
row.label(text="Custom Colors:", icon="COLOR")
info_str = ""
tris = ngons = 0
col = layout.column().split(percentage=0.1, align=True)
col.label(text="", icon_value=tris.icon_id)
col.prop(mesh_check, "custom_tri_color", text="")
for f in bm.faces:
col = layout.column().split(percentage=0.1, align=True)
col.label(text="", icon_value=ngons.icon_id)
col.prop(mesh_check, "custom_ngons_color", text="")
v = len(f.verts)
if v == 3:
tris += 1
elif v > 4:
ngons += 1
layout.separator()
bmesh.update_edit_mesh(me)
info_str = " Ngons: %i Tris: %i" % (ngons, tris)
split = layout.split(percentage=0.1)
split.separator()
split.label(info_str, icon='MESH_DATA')
row = layout.row(align=True)
if bpy.app.debug:
obj_data = getattr(context.active_object, "data", None)
if obj_data:
row.prop(obj_data, "show_extra_indices",
icon="LINENUMBERS_ON", toggle=True)
if context.mode == 'EDIT_MESH' and not context.space_data.use_occlude_geometry:
split = layout.split(percentage=0.1)
split.separator()
split2 = split.split()
row = split2.row()
row.prop(mesh_check, "finer_lines_behind_use")
row.prop(mesh_check, "finer_lines_behind_use", icon="ORTHO")
# ********** Edit Multiselect **********
@ -735,6 +744,55 @@ class MeshExtraToolsSceneProps(PropertyGroup):
default=(False,) * 4,
size=4,
)
# Vertex align
vert_align_store_axis = FloatVectorProperty(
name="Define Custom Coordinates",
description="Store the values of coordinates, for repeated use\n"
"as a starting point",
default=(0.0, 0.0, 0.0),
min=-100.0, max=100.0,
step=1, size=3,
subtype='XYZ',
precision=3
)
vert_align_use_stored = BoolProperty(
name="Use Stored Coordinates",
description="Use starting point coordinates for alignment",
default=False
)
vert_align_to = EnumProperty(
items=(('vertex', "Original vertex",
"Use the stored vertex coordinates for aligning"),
('coordinates', "Custom coordinates",
"Use defined custom coordinates for aligning")),
name="Align to",
default='vertex'
)
vert_align_axis = BoolVectorProperty(
name="Axis",
description="Align to a specific Axis",
default=(True, False, False),
size=3,
)
# Mesh Info select
mesh_info_show = BoolProperty(
name="Show Face Info",
description="Display the Object's Face Count information\n"
"Note: it can have some performance impact on dense meshes\n"
"Leave it closed if not needed or set the Delay to a higher value",
default=False
)
mesh_info_delay = FloatProperty(
name="Delay",
description="Set the Update time Delay in seconds\n"
"Set to zero to update with the UI refresh\n"
"Higher values will sometimes need to hover over the cursor",
default=2.0,
min=0.0, max=20.0,
step=100,
subtype='TIME',
precision=1
)
# Add-on Preferences
@ -802,7 +860,7 @@ def register():
vfe_specials.register()
mesh_extrude_and_reshape.register()
mesh_check.register()
vertex_align.register()
bpy.utils.register_module(__name__)
# Register Scene Properties
@ -827,7 +885,6 @@ def unregister():
vfe_specials.unregister()
mesh_extrude_and_reshape.unregister()
mesh_check.unregister()
vertex_align.unregister()
del bpy.types.Scene.mesh_extra_tools
del bpy.types.Object.tkkey

View File

@ -1,23 +1,23 @@
# -*- coding: utf-8 -*-
# ***** BEGIN GPL LICENSE BLOCK *****
# ##### 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 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.
#
# 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.
#
# 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 LICENCE BLOCK *****
# ##### END GPL LICENSE BLOCK #####
# based completely on addon by zmj100
# added some distance limits to prevent overlap - max12345
@ -31,7 +31,10 @@ from bpy.props import (
BoolProperty,
EnumProperty,
)
from math import tan, cos, degrees, radians, sin
from math import (
sin, cos, tan,
degrees, radians,
)
from mathutils import Matrix
@ -97,11 +100,17 @@ def face_inset_fillet(bme, face_index_list, inset_amount, distance,
val = ((f.normal).normalized() * h)
if out is True:
# this -(p - (vec2.normalized() * adj))) is just the freaking axis afaik...
p6 = angle_rotation(p, p + val, -(p - (vec2.normalized() * adj)), -radians(90))
p6 = angle_rotation(
p, p + val,
-(p - (vec2.normalized() * adj)),
-radians(90)
)
else:
p6 = angle_rotation(p, p - val,
((p - (vec1.normalized() * adj)) - (p - (vec2.normalized() * adj))),
-radians(90))
p6 = angle_rotation(
p, p - val,
((p - (vec1.normalized() * adj)) - (p - (vec2.normalized() * adj))),
-radians(90)
)
orientation_vertex_list.append(p6)
@ -200,6 +209,7 @@ def face_inset_fillet(bme, face_index_list, inset_amount, distance,
bme.faces.index_update()
del_ = [bme.faces.remove(f) for f in list_del]
if del_:
del del_
@ -209,59 +219,59 @@ def face_inset_fillet(bme, face_index_list, inset_amount, distance,
class MESH_OT_face_inset_fillet(Operator):
bl_idname = "mesh.face_inset_fillet"
bl_label = "Face Inset Fillet"
bl_description = ("Inset selected and Fillet (make round) the corners of\n"
bl_description = ("Inset selected and Fillet (make round) the corners \n"
"of the newly created Faces")
bl_options = {"REGISTER", "UNDO"}
# inset amount
inset_amount = FloatProperty(
name="Inset amount",
description="Define the size of the Inset relative to the selection",
default=0.04,
min=0, max=100.0,
step=1,
precision=3
)
name="Inset amount",
description="Define the size of the Inset relative to the selection",
default=0.04,
min=0, max=100.0,
step=1,
precision=3
)
# number of sides
number_of_sides = IntProperty(
name="Number of sides",
description="Define the roundness of the corners by specifying\n"
"the subdivision count",
default=4,
min=1, max=100,
step=1
)
name="Number of sides",
description="Define the roundness of the corners by specifying\n"
"the subdivision count",
default=4,
min=1, max=100,
step=1
)
distance = FloatProperty(
name="",
description="Use distance or radius for corners' size calculation",
default=0.04,
min=0.00001, max=100.0,
step=1,
precision=3
)
name="",
description="Use distance or radius for corners' size calculation",
default=0.04,
min=0.00001, max=100.0,
step=1,
precision=3
)
out = BoolProperty(
name="Outside",
description="Inset the Faces outwards in relation to the selection\n"
"Note: depending on the geometry, can give unsatisfactory results",
default=False
)
name="Outside",
description="Inset the Faces outwards in relation to the selection\n"
"Note: depending on the geometry, can give unsatisfactory results",
default=False
)
radius = BoolProperty(
name="Radius",
description="Use radius for corners' size calculation",
default=False
)
name="Radius",
description="Use radius for corners' size calculation",
default=False
)
type_enum = EnumProperty(
items=(('opt0', "N-gon", "N-gon corners - Keep the corner Faces uncut"),
('opt1', "Triangle", "Triangulate corners")),
name="Corner Type",
default="opt0"
)
items=(('opt0', "N-gon", "N-gon corners - Keep the corner Faces uncut"),
('opt1', "Triangle", "Triangulate corners")),
name="Corner Type",
default="opt0"
)
kp = BoolProperty(
name="Keep faces",
description="Do not delete the inside Faces\n"
"Only available if the Out option is checked",
default=False
)
name="Keep faces",
description="Do not delete the inside Faces\n"
"Only available if the Out option is checked",
default=False
)
def draw(self, context):
layout = self.layout
@ -308,8 +318,10 @@ class MESH_OT_face_inset_fillet(Operator):
face_index_list = [f.index for f in bme.faces if f.select and f.is_valid]
if len(face_index_list) == 0:
self.report({'WARNING'}, "No suitable Face selection found. Operation cancelled")
self.report({'WARNING'},
"No suitable Face selection found. Operation cancelled")
edit_mode_in()
return {'CANCELLED'}
elif len(face_index_list) != 0:

View File

@ -1,28 +1,31 @@
import os
import bpy
import bpy.utils.previews
mesh_check_icon_collections = {}
mesh_check_icons_loaded = False
def load_icons():
global mesh_check_icon_collections
global mesh_check_icons_loaded
if mesh_check_icons_loaded: return mesh_check_icon_collections["main"]
if mesh_check_icons_loaded:
return mesh_check_icon_collections["main"]
custom_icons = bpy.utils.previews.new()
icons_dir = os.path.join(os.path.dirname(__file__))
custom_icons.load("ngons", os.path.join(icons_dir, "ngon.png"), 'IMAGE')
custom_icons.load("triangles", os.path.join(icons_dir, "triangle.png"), 'IMAGE')
mesh_check_icon_collections["main"] = custom_icons
mesh_check_icons_loaded = True
return mesh_check_icon_collections["main"]
def clear_icons():
global mesh_check_icons_loaded
for icon in mesh_check_icon_collections.values():

View File

@ -1,13 +1,14 @@
# gpl author: Pistiwique
bl_info = {"name": "Mesh Check BGL edition",
"description": "Display the triangles and ngons of the mesh",
"author": "Pistiwique",
"version": (1, 0, 1),
"blender": (2, 75, 0),
"location": "3D View(s) -> Properties -> Shading",
"category": "3D View"
}
bl_info = {
"name": "Mesh Check BGL edition",
"description": "Display the triangles and ngons of the mesh",
"author": "Pistiwique",
"version": (1, 0, 1),
"blender": (2, 75, 0),
"location": "3D View(s) > Properties > Shading",
"category": "3D View"
}
import bpy
import bmesh
@ -180,10 +181,9 @@ def mesh_check_draw_callback():
for f in new_faces:
faces.append([
(
(matrix_world * bm.verts[i].co)[0] + face.normal.x * 0.001,
(matrix_world * bm.verts[i].co)[1] + face.normal.y * 0.001,
(matrix_world * bm.verts[i].co)[2] + face.normal.z * 0.001)
((matrix_world * bm.verts[i].co)[0] + face.normal.x * 0.001,
(matrix_world * bm.verts[i].co)[1] + face.normal.y * 0.001,
(matrix_world * bm.verts[i].co)[2] + face.normal.z * 0.001)
for i in f]
)
@ -220,24 +220,28 @@ def updateBGLData(self, context):
edge_width[0] = self.edge_width
finer_lines[0] = self.finer_lines_behind_use
face_opacity[0] = self.face_opacity
edges_tri_color[0] = (self.custom_tri_color[0],
self.custom_tri_color[1],
self.custom_tri_color[2],
1)
faces_tri_color[0] = (self.custom_tri_color[0],
self.custom_tri_color[1],
self.custom_tri_color[2],
self.face_opacity)
edges_ngons_color[0] = (self.custom_ngons_color[0],
self.custom_ngons_color[1],
self.custom_ngons_color[2],
1)
faces_ngons_color[0] = (self.custom_ngons_color[0],
self.custom_ngons_color[1],
self.custom_ngons_color[2],
self.face_opacity)
edges_tri_color[0] = (
self.custom_tri_color[0],
self.custom_tri_color[1],
self.custom_tri_color[2],
1)
faces_tri_color[0] = (
self.custom_tri_color[0],
self.custom_tri_color[1],
self.custom_tri_color[2],
self.face_opacity
)
edges_ngons_color[0] = (
self.custom_ngons_color[0],
self.custom_ngons_color[1],
self.custom_ngons_color[2],
1)
faces_ngons_color[0] = (
self.custom_ngons_color[0],
self.custom_ngons_color[1],
self.custom_ngons_color[2],
self.face_opacity
)
return
draw_enabled[0] = False
@ -246,11 +250,13 @@ def updateBGLData(self, context):
class FaceTypeSelect(Operator):
bl_idname = "object.face_type_select"
bl_label = "Face type select"
bl_description = "Select Triangles and / or Ngons on the Active Object"
bl_options = {'REGISTER', 'UNDO'}
face_type = EnumProperty(
items=(('tris', 'Tris', ""),
('ngons', 'Ngons', "")),
name="Face Type",
items=(('tris', "Tris", "Colorize Triangles in the Mesh"),
('ngons', "Ngons", "Colorize Ngons in the Mesh")),
default='ngons'
)
@ -273,61 +279,61 @@ class FaceTypeSelect(Operator):
class MeshCheckCollectionGroup(PropertyGroup):
mesh_check_use = BoolProperty(
name="Mesh Check",
description="Display Mesh Check options",
default=False,
update=updateBGLData
)
name="Mesh Check",
description="Display Mesh Check options",
default=False,
update=updateBGLData
)
display_faces = BoolProperty(
name="Display Faces",
description="Use BGL to display ngons en tris of the mesh",
default=False,
update=updateBGLData
)
name="Display Faces",
description="Use BGL to display Ngons and Tris of the mesh",
default=False,
update=updateBGLData
)
edge_width = FloatProperty(
name="Width",
description="Edges width in pixels",
min=1.0,
max=10.0,
default=3.0,
subtype='PIXEL',
update=updateBGLData
)
name="Width",
description="Drawn Edges width in pixels",
min=1.0,
max=10.0,
default=3.0,
subtype='PIXEL',
update=updateBGLData
)
finer_lines_behind_use = BoolProperty(
name="Finer Lines behind",
description="Display partially hidden edges finer in non-occlude mode",
default=True,
update=updateBGLData
)
name="Finer Lines behind",
description="Display partially hidden edges finer in non-occlude mode",
default=True,
update=updateBGLData
)
custom_tri_color = FloatVectorProperty(
name="Tri Color",
description="Custom color for the triangles",
min=0.0,
max=1.0,
default=(1.0, 1.0, 0.0),
size=3,
subtype='COLOR',
update=updateBGLData
)
name="Tri Color",
description="Custom color for the Triangles",
min=0.0,
max=1.0,
default=(1.0, 1.0, 0.0),
size=3,
subtype='COLOR',
update=updateBGLData
)
custom_ngons_color = FloatVectorProperty(
name="Ngons Color",
description="custom color for the ngons",
min=0.0,
max=1.0,
default=(1.0, 0.0, 0.0),
size=3,
subtype='COLOR',
update=updateBGLData
)
name="Ngons Color",
description="Custom color for the Ngons",
min=0.0,
max=1.0,
default=(1.0, 0.0, 0.0),
size=3,
subtype='COLOR',
update=updateBGLData
)
face_opacity = FloatProperty(
name="Face Opacity",
description="Opacity of the color for the face",
min=0.0,
max=1.0,
default=0.2,
subtype='FACTOR',
update=updateBGLData
)
name="Face Opacity",
description="Opacity of the color for the face",
min=0.0,
max=1.0,
default=0.2,
subtype='FACTOR',
update=updateBGLData
)
# Register
@ -340,6 +346,7 @@ classes = (
def register():
for cls in classes:
bpy.utils.register_class(cls)
bpy.types.WindowManager.mesh_check = PointerProperty(
type=MeshCheckCollectionGroup
)
@ -354,6 +361,7 @@ def unregister():
if mesh_check_handle:
bpy.types.SpaceView3D.draw_handler_remove(mesh_check_handle[0], 'WINDOW')
mesh_check_handle[:] = []
for cls in classes:
bpy.utils.unregister_class(cls)

View File

@ -10,7 +10,6 @@ bl_info = {
import bpy
import bmesh
from bpy.types import Operator
from bpy.props import (
BoolProperty,
@ -99,7 +98,8 @@ def get_edge_rings(bm, keep_caps=True):
try:
# generate a list of edges to select:
# traversing only tagged faces, since calc_face can walk and untag islands
for face in filter(lambda f: f.tag, selected_faces): edges += calc_face(face, keep_caps)
for face in filter(lambda f: f.tag, selected_faces):
edges += calc_face(face, keep_caps)
finally:
# housekeeping: clear tags
for face in selected_faces:
@ -165,32 +165,32 @@ class MESH_xOT_cut_faces(Operator):
SUBD_STRAIGHT_CUT = 3
num_cuts = IntProperty(
name="Number of Cuts",
default=1,
min=1,
max=100,
subtype='UNSIGNED'
)
name="Number of Cuts",
default=1,
min=1,
max=100,
subtype='UNSIGNED'
)
use_single_edge = BoolProperty(
name="Quad/Tri Mode",
description="Cut boundary faces",
default=False
)
name="Quad/Tri Mode",
description="Cut boundary faces",
default=False
)
corner_type = EnumProperty(
items=[('SUBD_INNERVERT', "Inner Vert", ""),
('SUBD_PATH', "Path", ""),
('SUBD_FAN', "Fan", ""),
('SUBD_STRAIGHT_CUT', "Straight Cut", ""),
],
name="Quad Corner Type",
description="How to subdivide quad corners",
default='SUBD_STRAIGHT_CUT'
)
items=[('SUBD_INNERVERT', "Inner Vert", ""),
('SUBD_PATH', "Path", ""),
('SUBD_FAN', "Fan", ""),
('SUBD_STRAIGHT_CUT', "Straight Cut", ""),
],
name="Quad Corner Type",
description="How to subdivide quad corners",
default='SUBD_STRAIGHT_CUT'
)
use_grid_fill = BoolProperty(
name="Use Grid Fill",
description="Fill fully enclosed faces with a grid",
default=True
)
name="Use Grid Fill",
description="Fill fully enclosed faces with a grid",
default=True
)
@classmethod
def poll(cls, context):
@ -216,17 +216,18 @@ class MESH_xOT_cut_faces(Operator):
try:
edges = get_edge_rings(bm, keep_caps=True)
if not edges:
self.report({'WARNING'}, "No suitable Face selection found. Operation cancelled")
self.report({'WARNING'},
"No suitable Face selection found. Operation cancelled")
return False
result = bmesh.ops.subdivide_edges(
bm,
edges=edges,
cuts=int(self.num_cuts),
use_grid_fill=bool(self.use_grid_fill),
use_single_edge=bool(self.use_single_edge),
quad_corner_type=eval("self." + self.corner_type))
bm,
edges=edges,
cuts=int(self.num_cuts),
use_grid_fill=bool(self.use_grid_fill),
use_single_edge=bool(self.use_single_edge),
quad_corner_type=eval("self." + self.corner_type)
)
bpy.ops.mesh.select_all(action='DESELECT')
bm.select_mode = {'EDGE'}

View File

@ -1,20 +1,20 @@
# ***** BEGIN GPL LICENSE BLOCK *****
# ##### 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 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.
# 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.
# 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 LICENCE BLOCK *****
# ##### END GPL LICENSE BLOCK #####
bl_info = {
"name": "Edge Roundifier",
@ -22,10 +22,10 @@ bl_info = {
"author": "Piotr Komisarczyk (komi3D), PKHG",
"version": (1, 0, 0),
"blender": (2, 7, 3),
"location": "SPACE > Edge Roundifier or CTRL-E > Edge Roundifier or Tools > Addons > Edge Roundifier",
"location": "SPACE > Edge Roundifier or CTRL-E > "
"Edge Roundifier or Tools > Addons > Edge Roundifier",
"description": "Mesh editing script allowing edge rounding",
"wiki_url": "",
"tracker_url": "",
"category": "Mesh"
}
@ -38,9 +38,14 @@ from bpy.props import (
EnumProperty,
IntProperty,
)
from math import sqrt, acos, pi, radians, degrees, sin
from mathutils import Vector, Euler, Quaternion
from math import (
sqrt, acos, pi,
radians, degrees, sin,
)
from mathutils import (
Vector, Euler,
Quaternion,
)
# CONSTANTS
two_pi = 2 * pi
@ -79,7 +84,6 @@ class CalculationHelper:
"""
Constructor
"""
def getLineCoefficientsPerpendicularToVectorInPoint(self, point, vector, plane):
x, y, z = point
xVector, yVector, zVector = vector
@ -92,7 +96,7 @@ class CalculationHelper:
def getQuadraticRoots(self, coef):
if len(coef) != 3:
return NaN # TODO lijenstina: Is this a valid return?
return None # Replaced NaN with None
else:
a, b, c = coef
delta = b ** 2 - 4 * a * c
@ -155,7 +159,8 @@ class CalculationHelper:
else:
return None
def getLineCircleIntersectionsWhenXPerpendicular(self, edgeCenter, circleMidPoint, radius, plane):
def getLineCircleIntersectionsWhenXPerpendicular(self, edgeCenter,
circleMidPoint, radius, plane):
# (x - a)**2 + (y - b)**2 = r**2 - circle equation
# x = xValue - line equation
# f * x**2 + g * x + h = 0 - quadratic equation
@ -194,8 +199,8 @@ class CalculationHelper:
# get two of three coordinates used for further calculation of spin center
# PKHG>nice if rescriction to these 3 types or planes is to be done
# komi3D> from 0.0.2 there is a restriction. In future I would like Edge Roundifier to work on
# komi3D> Normal and View coordinate systems. That would be great
# komi3D> from 0.0.2 there is a restriction. In future I would like Edge
# komi3D> Roundifier to work on Normal and View coordinate systems
def getCircleMidPointOnPlane(self, V1, plane):
X = V1[0]
Y = V1[1]
@ -260,206 +265,206 @@ class EdgeRoundifier(Operator):
obj = None
edgeScaleFactor = FloatProperty(
name="",
description="Set the Factor of scaling",
default=1.0,
min=0.00001, max=100000.0,
step=0.5,
precision=5
)
name="",
description="Set the Factor of scaling",
default=1.0,
min=0.00001, max=100000.0,
step=0.5,
precision=5
)
r = FloatProperty(
name="",
description="User Defined arc steepness by a Radius\n"
"Enabled only if Entry mode is set to Radius\n",
default=1,
min=0.00001, max=1000.0,
step=0.1,
precision=3
)
name="",
description="User Defined arc steepness by a Radius\n"
"Enabled only if Entry mode is set to Radius\n",
default=1,
min=0.00001, max=1000.0,
step=0.1,
precision=3
)
a = FloatProperty(
name="",
description="User defined arc steepness calculated from an Angle\n"
"Enabled only if Entry mode is set to Angle and\n"
"Angle presets is set Other",
default=180.0,
min=0.1, max=180.0,
step=0.5,
precision=1
)
name="",
description="User defined arc steepness calculated from an Angle\n"
"Enabled only if Entry mode is set to Angle and\n"
"Angle presets is set Other",
default=180.0,
min=0.1, max=180.0,
step=0.5,
precision=1
)
n = IntProperty(
name="",
description="Arc subdivision level",
default=4,
min=1, max=100,
step=1
)
name="",
description="Arc subdivision level",
default=4,
min=1, max=100,
step=1
)
flip = BoolProperty(
name="Flip",
description="If True, flip the side of the selected edges where the arcs are drawn",
default=False
)
name="Flip",
description="If True, flip the side of the selected edges where the arcs are drawn",
default=False
)
invertAngle = BoolProperty(
name="Invert",
description="If True, uses an inverted angle to draw the arc (360 degrees - angle)",
default=False
)
name="Invert",
description="If True, uses an inverted angle to draw the arc (360 degrees - angle)",
default=False
)
fullCircles = BoolProperty(
name="Circles",
description="If True, uses an angle of 360 degrees to draw the arcs",
default=False
)
name="Circles",
description="If True, uses an angle of 360 degrees to draw the arcs",
default=False
)
bothSides = BoolProperty(
name="Both sides",
description="If True, draw arcs on both sides of the selected edges",
default=False
)
name="Both sides",
description="If True, draw arcs on both sides of the selected edges",
default=False
)
drawArcCenters = BoolProperty(
name="Centers",
description="If True, draws a vertex for each spin center",
default=False
)
name="Centers",
description="If True, draws a vertex for each spin center",
default=False
)
removeEdges = BoolProperty(
name="Edges",
description="If True removes the Original selected edges",
default=False
)
name="Edges",
description="If True removes the Original selected edges",
default=False
)
removeScaledEdges = BoolProperty(
name="Scaled edges",
description="If True removes the Scaled edges (not part of the arcs)",
default=False
)
name="Scaled edges",
description="If True removes the Scaled edges (not part of the arcs)",
default=False
)
connectArcWithEdge = BoolProperty(
name="Arc - Edge",
description="Connect Arcs to Edges",
default=False
)
name="Arc - Edge",
description="Connect Arcs to Edges",
default=False
)
connectArcs = BoolProperty(
name="Arcs",
description="Connect subsequent Arcs",
default=False
)
name="Arcs",
description="Connect subsequent Arcs",
default=False
)
connectScaledAndBase = BoolProperty(
name="Scaled - Base Edge",
description="Connect Scaled to Base Edge",
default=False
)
name="Scaled - Base Edge",
description="Connect Scaled to Base Edge",
default=False
)
connectArcsFlip = BoolProperty(
name="Flip Arcs",
description="Flip the connection of subsequent Arcs",
default=False
)
name="Flip Arcs",
description="Flip the connection of subsequent Arcs",
default=False
)
connectArcWithEdgeFlip = BoolProperty(
name="Flip Arc - Edge",
description="Flip the connection of the Arcs to Edges",
default=False
)
name="Flip Arc - Edge",
description="Flip the connection of the Arcs to Edges",
default=False
)
axisAngle = FloatProperty(
name="",
description="Rotate Arc around the perpendicular axis",
default=0.0,
min=-180.0, max=180.0,
step=0.5,
precision=1
)
name="",
description="Rotate Arc around the perpendicular axis",
default=0.0,
min=-180.0, max=180.0,
step=0.5,
precision=1
)
edgeAngle = FloatProperty(
name="",
description="Rotate Arc around the Edge (Edge acts like as the axis)",
default=0.0,
min=-180.0, max=180.0,
step=0.5,
precision=1
)
name="",
description="Rotate Arc around the Edge (Edge acts like as the axis)",
default=0.0,
min=-180.0, max=180.0,
step=0.5,
precision=1
)
offset = FloatProperty(
name="",
description="Offset Arc perpendicular the Edge",
default=0.0,
min=-1000000.0, max=1000000.0,
step=0.1,
precision=5
)
name="",
description="Offset Arc perpendicular the Edge",
default=0.0,
min=-1000000.0, max=1000000.0,
step=0.1,
precision=5
)
offset2 = FloatProperty(
name="",
description="Offset Arc in parallel to the Edge",
default=0.0,
min=-1000000.0, max=1000000.0,
step=0.1,
precision=5
)
name="",
description="Offset Arc in parallel to the Edge",
default=0.0,
min=-1000000.0, max=1000000.0,
step=0.1,
precision=5
)
ellipticFactor = FloatProperty(
name="",
description="Make Arc elliptic",
default=0.0,
min=-1000000.0, max=1000000.0,
step=0.1,
precision=5
)
name="",
description="Make Arc elliptic",
default=0.0,
min=-1000000.0, max=1000000.0,
step=0.1,
precision=5
)
workModeItems = [("Normal", "Normal", ""), ("Reset", "Reset", "")]
workMode = EnumProperty(
items=workModeItems,
name="",
default='Normal',
description="Normal work with the current given paramaters set by the user\n"
"Reset - changes back the parameters to their default values"
)
items=workModeItems,
name="",
default='Normal',
description="Normal work with the current given paramaters set by the user\n"
"Reset - changes back the parameters to their default values"
)
entryModeItems = [("Radius", "Radius", ""), ("Angle", "Angle", "")]
entryMode = EnumProperty(
items=entryModeItems,
name="",
default='Angle',
description="Entry mode switch between Angle and Radius\n"
"If Angle is selected, arc radius is calculated from it"
)
items=entryModeItems,
name="",
default='Angle',
description="Entry mode switch between Angle and Radius\n"
"If Angle is selected, arc radius is calculated from it"
)
rotateCenterItems = [("Spin", "Spin", ""), ("V1", "V1", ""),
("Edge", "Edge", ""), ("V2", "V2", "")]
rotateCenter = EnumProperty(
items=rotateCenterItems,
name="",
default='Edge',
description="Rotate center for spin axis rotate"
)
items=rotateCenterItems,
name="",
default='Edge',
description="Rotate center for spin axis rotate"
)
arcModeItems = [("FullEdgeArc", "Full", "Full"), ('HalfEdgeArc', "Half", "Half")]
arcMode = EnumProperty(
items=arcModeItems,
name="",
default='FullEdgeArc',
description="Arc mode - switch between Full and Half arcs"
)
items=arcModeItems,
name="",
default='FullEdgeArc',
description="Arc mode - switch between Full and Half arcs"
)
angleItems = [('Other', "Other", "User defined angle"), ('180', "180", "HemiCircle (2 sides)"),
('120', "120", "TriangleCircle (3 sides)"), ('90', "90", "QuadCircle (4 sides)"),
('72', "72", "PentagonCircle (5 sides)"), ('60', "60", "HexagonCircle (6 sides)"),
('45', "45", "OctagonCircle (8 sides)"), ('30', "30", "DodecagonCircle (12 sides)")]
angleEnum = EnumProperty(
items=angleItems,
name="",
default='180',
description="Presets prepare standard angles and calculate proper ray"
)
items=angleItems,
name="",
default='180',
description="Presets prepare standard angles and calculate proper ray"
)
refItems = [('ORG', "Origin", "Use Origin Location"), ('CUR', "3D Cursor", "Use 3DCursor Location"),
('EDG', "Edge", "Use Individual Edge Reference")]
referenceLocation = EnumProperty(
items=refItems,
name="",
default='ORG',
description="Reference location used to calculate initial centers of drawn arcs"
)
items=refItems,
name="",
default='ORG',
description="Reference location used to calculate initial centers of drawn arcs"
)
planeItems = [(XY, "XY", "XY Plane (Z=0)"),
(YZ, "YZ", "YZ Plane (X=0)"),
(XZ, "XZ", "XZ Plane (Y=0)")]
planeEnum = EnumProperty(
items=planeItems,
name="",
default='XY',
description="Plane used to calculate spin plane of drawn arcs"
)
items=planeItems,
name="",
default='XY',
description="Plane used to calculate spin plane of drawn arcs"
)
edgeScaleCenterItems = [('V1', "V1", "v1 - First Edge's Vertex"),
('CENTER', "Center", "Center of the Edge"),
('V2', "V2", "v2 - Second Edge's Vertex")]
edgeScaleCenterEnum = EnumProperty(
items=edgeScaleCenterItems,
name="Edge scale center",
default='CENTER',
description="Center used for scaling the initial edge"
)
items=edgeScaleCenterItems,
name="Edge scale center",
default='CENTER',
description="Center used for scaling the initial edge"
)
calc = CalculationHelper()
sel = SelectionHelper()
@ -628,12 +633,8 @@ class EdgeRoundifier(Operator):
bpy.ops.object.mode_set(mode='OBJECT')
bm.to_mesh(mesh)
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action='TOGGLE')
bpy.ops.mesh.select_all(action='INVERT')
bpy.ops.mesh.remove_doubles()
bm.free()
return {'FINISHED'}
def resetValues(self, workMode):
@ -753,10 +754,16 @@ class EdgeRoundifier(Operator):
return arcVerts
def spinAndPostprocess(self, edge, parameters, bm, mesh, edgeCenter, roundifyParams):
spinnedVerts, roundifyParamsUpdated = self.drawSpin(edge, edgeCenter,
roundifyParams, parameters, bm, mesh)
postProcessedArcVerts = self.arcPostprocessing(edge, parameters, bm, mesh,
roundifyParamsUpdated, spinnedVerts, edgeCenter)
spinnedVerts, roundifyParamsUpdated = self.drawSpin(
edge, edgeCenter,
roundifyParams,
parameters, bm, mesh
)
postProcessedArcVerts = self.arcPostprocessing(
edge, parameters, bm, mesh,
roundifyParamsUpdated,
spinnedVerts, edgeCenter
)
return postProcessedArcVerts
def rotateArcAroundEdge(self, bm, mesh, arcVerts, parameters):
@ -768,9 +775,11 @@ class EdgeRoundifier(Operator):
def arc_rotator(self, arcVerts, extra_rotation, parameters):
bpy.ops.object.mode_set(mode='OBJECT')
old_location = self.obj.location.copy()
bpy.ops.transform.translate(value=- old_location, constraint_axis=(False, False, False),
constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED',
proportional_edit_falloff='SMOOTH', proportional_size=1)
bpy.ops.transform.translate(
value=-old_location, constraint_axis=(False, False, False),
constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED',
proportional_edit_falloff='SMOOTH', proportional_size=1
)
bpy.ops.object.mode_set(mode='EDIT')
adjust_matrix = self.obj.matrix_parent_inverse
bm = bmesh.from_edit_mesh(self.obj.data)
@ -797,9 +806,11 @@ class EdgeRoundifier(Operator):
bpy.ops.object.mode_set(mode='OBJECT')
# PKHG>INFO move origin object back print("old location = " , old_location)
bpy.ops.transform.translate(value=old_location, constraint_axis=(False, False, False),
constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED',
proportional_edit_falloff='SMOOTH', proportional_size=1)
bpy.ops.transform.translate(
value=old_location, constraint_axis=(False, False, False),
constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED',
proportional_edit_falloff='SMOOTH', proportional_size=1
)
bpy.ops.object.mode_set(mode='EDIT')
def makeElliptic(self, bm, mesh, arcVertices, parameters):
@ -826,21 +837,37 @@ class EdgeRoundifier(Operator):
[chosenSpinCenter, otherSpinCenter, spinAxis, angle, steps, refObjectLocation] = roundifyParams
rotatedVerts = []
if parameters["rotateCenter"] == 'Edge':
rotatedVerts = self.rotateArcAroundSpinAxis(bm, mesh, spinnedVerts, parameters, edgeCenter)
rotatedVerts = self.rotateArcAroundSpinAxis(
bm, mesh, spinnedVerts, parameters, edgeCenter
)
elif parameters["rotateCenter"] == 'Spin':
rotatedVerts = self.rotateArcAroundSpinAxis(bm, mesh, spinnedVerts, parameters, chosenSpinCenter)
rotatedVerts = self.rotateArcAroundSpinAxis(
bm, mesh, spinnedVerts, parameters, chosenSpinCenter
)
elif parameters["rotateCenter"] == 'V1':
rotatedVerts = self.rotateArcAroundSpinAxis(bm, mesh, spinnedVerts, parameters, edge.verts[0].co)
rotatedVerts = self.rotateArcAroundSpinAxis(
bm, mesh, spinnedVerts, parameters, edge.verts[0].co
)
elif parameters["rotateCenter"] == 'V2':
rotatedVerts = self.rotateArcAroundSpinAxis(bm, mesh, spinnedVerts, parameters, edge.verts[1].co)
rotatedVerts = self.rotateArcAroundSpinAxis(
bm, mesh, spinnedVerts, parameters, edge.verts[1].co
)
offsetVerts = self.offsetArcPerpendicular(bm, mesh, rotatedVerts, edge, parameters)
offsetVerts2 = self.offsetArcParallel(bm, mesh, offsetVerts, edge, parameters)
ellipticVerts = self.makeElliptic(bm, mesh, offsetVerts2, parameters)
offsetVerts = self.offsetArcPerpendicular(
bm, mesh, rotatedVerts, edge, parameters
)
offsetVerts2 = self.offsetArcParallel(
bm, mesh, offsetVerts, edge, parameters
)
ellipticVerts = self.makeElliptic(
bm, mesh, offsetVerts2, parameters
)
self.rotateArcAroundEdge(bm, mesh, ellipticVerts, parameters)
if parameters["connectArcWithEdge"]:
self.connectArcTogetherWithEdge(edge, offsetVerts2, bm, mesh, parameters)
self.connectArcTogetherWithEdge(
edge, offsetVerts2, bm, mesh, parameters
)
return offsetVerts2
def connectArcTogetherWithEdge(self, edge, arcVertices, bm, mesh, parameters):
@ -884,8 +911,10 @@ class EdgeRoundifier(Operator):
def connectArcsTogether(self, arcs, bm, mesh, parameters):
for i in range(0, len(arcs) - 1):
if arcs[i] is None or arcs[i + 1] is None: # in case on XZ or YZ there are no arcs drawn
# in case on XZ or YZ there are no arcs drawn
if arcs[i] is None or arcs[i + 1] is None:
return
lastVert = len(arcs[i]) - 1
if parameters["drawArcCenters"]:
lastVert = lastVert - 1 # center gets added as last vert of arc
@ -906,7 +935,9 @@ class EdgeRoundifier(Operator):
lastArcId = len(arcs) - 1
lastVertIdOfLastArc = len(arcs[lastArcId]) - 1
if parameters["drawArcCenters"]:
lastVertIdOfLastArc = lastVertIdOfLastArc - 1 # center gets added as last vert of arc
# center gets added as last vert of arc
lastVertIdOfLastArc = lastVertIdOfLastArc - 1
V1 = arcs[lastArcId][lastVertIdOfLastArc].co
V2 = arcs[0][0].co
if parameters["connectArcsFlip"]:
@ -976,20 +1007,22 @@ class EdgeRoundifier(Operator):
debugPrintNew(d_Plane, "PLANE: " + parameters["plane"])
lineAB = self.calc.getLineCoefficientsPerpendicularToVectorInPoint(
edgeCenter, edgeVector,
parameters["plane"]
)
edgeCenter, edgeVector,
parameters["plane"]
)
circleMidPoint = V1
circleMidPointOnPlane = self.calc.getCircleMidPointOnPlane(V1, parameters["plane"])
circleMidPointOnPlane = self.calc.getCircleMidPointOnPlane(
V1, parameters["plane"]
)
radius = parameters["radius"]
angle = 0
if (parameters["entryMode"] == 'Angle'):
if (parameters["angleEnum"] != 'Other'):
radius, angle = self.CalculateRadiusAndAngleForAnglePresets(
parameters["angleEnum"], radius,
angle, edgeLength
)
parameters["angleEnum"], radius,
angle, edgeLength
)
else:
radius, angle = self.CalculateRadiusAndAngle(edgeLength)
debugPrintNew(d_Radius_Angle, "RADIUS = " + str(radius) + " ANGLE = " + str(angle))
@ -997,14 +1030,17 @@ class EdgeRoundifier(Operator):
if angle != pi: # mode other than 180
if lineAB is None:
roots = self.calc.getLineCircleIntersectionsWhenXPerpendicular(
edgeCenter, circleMidPointOnPlane,
radius, parameters["plane"]
)
edgeCenter, circleMidPointOnPlane,
radius, parameters["plane"]
)
else:
roots = self.calc.getLineCircleIntersections(lineAB, circleMidPointOnPlane, radius)
roots = self.calc.getLineCircleIntersections(
lineAB, circleMidPointOnPlane, radius
)
if roots is None:
debugPrintNew(True, "[Edge Roundifier]: No centers were found. Change radius to higher value")
debugPrintNew(True,
"[Edge Roundifier]: No centers were found. Change radius to higher value")
return None
roots = self.addMissingCoordinate(roots, V1, parameters["plane"]) # adds X, Y or Z coordinate
else:
@ -1022,7 +1058,9 @@ class EdgeRoundifier(Operator):
refObjectLocation = self.calc.getEdgeReference(edge, edgeCenter, parameters["plane"])
debugPrintNew(d_RefObject, parameters["refObject"], refObjectLocation)
chosenSpinCenter, otherSpinCenter = self.getSpinCenterClosestToRefCenter(refObjectLocation, roots)
chosenSpinCenter, otherSpinCenter = self.getSpinCenterClosestToRefCenter(
refObjectLocation, roots
)
if (parameters["entryMode"] == "Radius"):
halfAngle = self.calc.getAngle(edgeCenter, chosenSpinCenter, circleMidPoint)
@ -1059,8 +1097,10 @@ class EdgeRoundifier(Operator):
v0 = bm.verts.new(v0org.co)
result = bmesh.ops.spin(bm, geom=[v0], cent=chosenSpinCenter, axis=spinAxis,
angle=angle, steps=steps, use_duplicate=False)
result = bmesh.ops.spin(
bm, geom=[v0], cent=chosenSpinCenter, axis=spinAxis,
angle=angle, steps=steps, use_duplicate=False
)
# it seems there is something wrong with last index of this spin
# I need to calculate the last index manually here
@ -1088,21 +1128,21 @@ class EdgeRoundifier(Operator):
if ((parameters["invertAngle"]) or (parameters["flip"])):
if (midVertexDistance > midEdgeDistance):
alternativeLastSpinVertIndices = self.alternateSpin(
bm, mesh, angle, chosenSpinCenter,
spinAxis, steps, v0, v1org, lastSpinVertIndices
)
bm, mesh, angle, chosenSpinCenter,
spinAxis, steps, v0, v1org, lastSpinVertIndices
)
else:
if (midVertexDistance < midEdgeDistance):
alternativeLastSpinVertIndices = self.alternateSpin(
bm, mesh, angle, chosenSpinCenter,
spinAxis, steps, v0, v1org, lastSpinVertIndices
)
bm, mesh, angle, chosenSpinCenter,
spinAxis, steps, v0, v1org, lastSpinVertIndices
)
elif (angle != two_pi): # to allow full circles
if (result['geom_last'][0].co - v1org.co).length > SPIN_END_THRESHOLD:
alternativeLastSpinVertIndices = self.alternateSpin(
bm, mesh, angle, chosenSpinCenter,
spinAxis, steps, v0, v1org, lastSpinVertIndices
)
bm, mesh, angle, chosenSpinCenter,
spinAxis, steps, v0, v1org, lastSpinVertIndices
)
alternate = True
self.sel.refreshMesh(bm, mesh)
@ -1120,19 +1160,19 @@ class EdgeRoundifier(Operator):
# do some more testing here!!!
if (angle == pi or angle == -pi):
alternativeLastSpinVertIndices = self.alternateSpinNoDelete(
bm, mesh, -angle, chosenSpinCenter,
spinAxis, steps, v0, v1org, []
)
bm, mesh, -angle, chosenSpinCenter,
spinAxis, steps, v0, v1org, []
)
elif alternate:
alternativeLastSpinVertIndices = self.alternateSpinNoDelete(
bm, mesh, angle, otherSpinCenter,
spinAxis, steps, v0, v1org, []
)
bm, mesh, angle, otherSpinCenter,
spinAxis, steps, v0, v1org, []
)
elif not alternate:
alternativeLastSpinVertIndices = self.alternateSpinNoDelete(
bm, mesh, -angle, otherSpinCenter,
spinAxis, steps, v0, v1org, []
)
bm, mesh, -angle, otherSpinCenter,
spinAxis, steps, v0, v1org, []
)
bothSpinVertices = [bm.verts[i] for i in lastSpinVertIndices]
alternativeSpinVertices = [bm.verts[i] for i in alternativeLastSpinVertIndices]
bothSpinVertices = [v0] + bothSpinVertices + alternativeSpinVertices
@ -1175,12 +1215,16 @@ class EdgeRoundifier(Operator):
lastSpinVertIndices2 = self.getLastSpinVertIndices(steps, lastVertIndex2)
return lastSpinVertIndices2
def alternateSpin(self, bm, mesh, angle, chosenSpinCenter, spinAxis, steps, v0, v1org, lastSpinVertIndices):
def alternateSpin(self, bm, mesh, angle, chosenSpinCenter,
spinAxis, steps, v0, v1org, lastSpinVertIndices):
self.deleteSpinVertices(bm, mesh, lastSpinVertIndices)
v0prim = v0
result2 = bmesh.ops.spin(bm, geom=[v0prim], cent=chosenSpinCenter, axis=spinAxis,
angle=-angle, steps=steps, use_duplicate=False)
result2 = bmesh.ops.spin(
bm, geom=[v0prim], cent=chosenSpinCenter, axis=spinAxis,
angle=-angle, steps=steps, use_duplicate=False
)
# it seems there is something wrong with last index of this spin
# I need to calculate the last index manually here
vertsLength = len(bm.verts)
@ -1245,7 +1289,8 @@ class EdgeRoundifier(Operator):
self.a = angle_convert
except:
self.a = 180 # fallback
debugPrintNew(True, "CalculateRadiusAndAngleForAnglePresets problem with int conversion")
debugPrintNew(True,
"CalculateRadiusAndAngleForAnglePresets problem with int conversion")
return self.CalculateRadiusAndAngle(edgeLength)

View File

@ -0,0 +1,384 @@
# ##### 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; version 2
# of the License.
#
# 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 #####
# based upon the functionality of Mesh to wall by luxuy_BlenderCN
# thanks to meta-androcto
bl_info = {
"name": "Edge Floor Plan",
"author": "lijenstina",
"version": (0, 2),
"blender": (2, 78, 0),
"location": "View3D > EditMode > Mesh",
"description": "Make a Floor Plan from Edges",
"wiki_url": "",
"category": "Mesh"}
import bpy
import bmesh
from bpy.types import Operator
from bpy.props import (
BoolProperty,
EnumProperty,
FloatProperty,
FloatVectorProperty,
IntProperty,
)
# Handle error notifications
def error_handlers(self, error, reports="ERROR"):
if self and reports:
self.report({'WARNING'}, reports + " (See Console for more info)")
print("\n[mesh.edges_floor_plan]\nError: {}\n".format(error))
class MESH_OT_edges_floor_plan(Operator):
bl_idname = "mesh.edges_floor_plan"
bl_label = "Edges Floor Plan"
bl_description = "Top View, Extrude Flat Along Edges"
bl_options = {'REGISTER', 'UNDO'}
wid = FloatProperty(
name="Wall width:",
description="Set the width of the generated walls\n",
default=0.1,
min=0.001, max=30000
)
depth = FloatProperty(
name="Inner height:",
description="Set the height of the inner wall edges",
default=0.0,
min=0, max=10
)
connect_ends = BoolProperty(
name="Connect Ends",
description="Connect the ends of the boundary Edge loops",
default=False
)
repeat_cleanup = IntProperty(
name="Recursive Prepare",
description="Number of times that the preparation phase runs\n"
"at the start of the script\n"
"If parts of the mesh are not modified, increase this value",
min=1, max=20,
default=1
)
fill_items = [
('EDGE_NET', "Edge Net",
"Edge Net Method for mesh preparation - Initial Fill\n"
"The filled in faces will be Inset individually\n"
"Supports simple 3D objects"),
('SINGLE_FACE', "Single Face",
"Single Face Method for mesh preparation - Initial Fill\n"
"The produced face will be Triangulated before Inset Region\n"
"Good for edges forming a circle, avoid 3D objects"),
('SOLIDIFY', "Solidify",
"Extrude and Solidify Method\n"
"Useful for complex meshes, however works best on flat surfaces\n"
"as the extrude direction has to be defined")
]
fill_type = EnumProperty(
name="Fill Type",
items=fill_items,
description="Choose the method for creating geometry",
default='SOLIDIFY'
)
keep_faces = BoolProperty(
name="Keep Faces",
description="Keep or not the fill faces\n"
"Can depend on Remove Ngons state",
default=False
)
tri_faces = BoolProperty(
name="Triangulate Faces",
description="Triangulate the created fill faces\n"
"Sometimes can lead to unsatisfactory results",
default=False
)
initial_extrude = FloatVectorProperty(
name="Initial Extrude",
description="",
default=(0.0, 0.0, 0.1),
min=-20.0, max=20.0,
subtype='XYZ',
precision=3,
size=3
)
remove_ngons = BoolProperty(
name="Remove Ngons",
description="Keep or not the Ngon Faces\n"
"Note about limitations:\n"
"Sometimes the kept Faces could be Ngons\n"
"Removing the Ngons can lead to no geometry created",
default=True
)
offset = FloatProperty(
name="Wall Offset:",
description="Set the offset for the Solidify modifier",
default=0.0,
min=-1.0, max=1.0
)
only_rim = BoolProperty(
name="Rim Only",
description="Solidify Fill Rim only option",
default=False
)
@classmethod
def poll(cls, context):
ob = context.active_object
return (ob and ob.type == 'MESH' and context.mode == 'EDIT_MESH')
def check_edge(self, context):
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.mode_set(mode='EDIT')
obj = bpy.context.object
me_check = obj.data
if len(me_check.edges) < 1:
return False
return True
@staticmethod
def ensure(bm):
if bm:
bm.verts.ensure_lookup_table()
bm.edges.ensure_lookup_table()
bm.faces.ensure_lookup_table()
def solidify_mod(self, context, ob, wid, offset, only_rim):
try:
mods = ob.modifiers.new(
name="_Mesh_Solidify_Wall", type='SOLIDIFY'
)
mods.thickness = wid
mods.use_quality_normals = True
mods.offset = offset
mods.use_even_offset = True
mods.use_rim = True
mods.use_rim_only = only_rim
mods.show_on_cage = True
bpy.ops.object.modifier_apply(
modifier="_Mesh_Solidify_Wall"
)
except Exception as e:
error_handlers(self, e,
reports="Adding a Solidify Modifier failed")
pass
def draw(self, context):
layout = self.layout
box = layout.box()
box.label(text="Choose Method:", icon="SCRIPTWIN")
box.prop(self, "fill_type")
col = box.column(align=True)
if self.fill_type == 'EDGE_NET':
col.prop(self, "repeat_cleanup")
col.prop(self, "remove_ngons", toggle=True)
elif self.fill_type == 'SOLIDIFY':
col.prop(self, "offset", slider=True)
col.prop(self, "initial_extrude")
else:
col.prop(self, "remove_ngons", toggle=True)
col.prop(self, "tri_faces", toggle=True)
box = layout.box()
box.label(text="Settings:", icon="MOD_BUILD")
col = box.column(align=True)
col.prop(self, "wid")
if self.fill_type != 'SOLIDIFY':
col.prop(self, "depth")
col.prop(self, "connect_ends", toggle=True)
col.prop(self, "keep_faces", toggle=True)
else:
col.prop(self, "only_rim", toggle=True)
def execute(self, context):
if not self.check_edge(context):
self.report({'WARNING'},
"Operation Cancelled. Needs a Mesh with at least one edge")
return {'CANCELLED'}
wid = self.wid * 0.1
depth = self.depth * 0.1
offset = self.offset * 0.1
store_selection_mode = context.tool_settings.mesh_select_mode
# Note: the remove_doubles called after bmesh creation would make
# blender crash with certain meshes - keep it in mind for the future
bpy.ops.mesh.remove_doubles(threshold=0.003)
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.mode_set(mode='EDIT')
ob = bpy.context.object
me = ob.data
bm = bmesh.from_edit_mesh(me)
bmesh.ops.delete(bm, geom=bm.faces, context=3)
self.ensure(bm)
context.tool_settings.mesh_select_mode = (False, True, False)
original_edges = [edge.index for edge in bm.edges]
original_verts = [vert.index for vert in bm.verts]
self.ensure(bm)
bpy.ops.mesh.select_all(action='DESELECT')
if self.fill_type == 'EDGE_NET':
for i in range(self.repeat_cleanup):
bmesh.ops.edgenet_prepare(bm, edges=bm.edges)
self.ensure(bm)
bmesh.ops.edgenet_fill(bm, edges=bm.edges, mat_nr=0, use_smooth=True, sides=0)
self.ensure(bm)
if self.remove_ngons:
ngons = [face for face in bm.faces if len(face.edges) > 4]
self.ensure(bm)
bmesh.ops.delete(bm, geom=ngons, context=5) # 5 - delete faces
del ngons
self.ensure(bm)
elif self.fill_type == 'SOLIDIFY':
for vert in bm.verts:
vert.normal_update()
self.ensure(bm)
bmesh.ops.extrude_edge_only(
bm, edges=bm.edges, use_select_history=False
)
self.ensure(bm)
verts_extrude = [vert for vert in bm.verts if vert.index in original_verts]
self.ensure(bm)
bmesh.ops.translate(
bm,
verts=verts_extrude,
vec=(self.initial_extrude)
)
self.ensure(bm)
del verts_extrude
self.ensure(bm)
for edge in bm.edges:
if edge.is_boundary:
edge.select = True
bm = bmesh.update_edit_mesh(ob.data, 1, 1)
bpy.ops.object.mode_set(mode='OBJECT')
self.solidify_mod(context, ob, wid, offset, self.only_rim)
bpy.ops.object.mode_set(mode='EDIT')
context.tool_settings.mesh_select_mode = store_selection_mode
return {'FINISHED'}
else:
bm.faces.new(bm.verts)
self.ensure(bm)
if self.tri_faces:
bmesh.ops.triangle_fill(
bm, use_beauty=True, use_dissolve=False, edges=bm.edges
)
self.ensure(bm)
if self.remove_ngons and self.fill_type != 'EDGE_NET':
ngons = [face for face in bm.faces if len(face.edges) > 4]
self.ensure(bm)
bmesh.ops.delete(bm, geom=ngons, context=5) # 5 - delete faces
del ngons
self.ensure(bm)
del_boundary = [edge for edge in bm.edges if edge.index not in original_edges]
self.ensure(bm)
del original_edges
self.ensure(bm)
if self.fill_type == 'EDGE_NET':
extrude_inner = bmesh.ops.inset_individual(
bm, faces=bm.faces, thickness=wid, depth=depth,
use_even_offset=True, use_interpolate=False,
use_relative_offset=False
)
else:
extrude_inner = bmesh.ops.inset_region(
bm, faces=bm.faces, faces_exclude=[], use_boundary=True,
use_even_offset=True, use_interpolate=False,
use_relative_offset=False, use_edge_rail=False,
thickness=wid, depth=depth, use_outset=False
)
self.ensure(bm)
del_faces = [faces for faces in bm.faces if faces not in extrude_inner["faces"]]
self.ensure(bm)
del extrude_inner
self.ensure(bm)
if not self.keep_faces:
bmesh.ops.delete(bm, geom=del_faces, context=5) # 5 delete faces
del del_faces
self.ensure(bm)
face_del = set()
for face in bm.faces:
for edge in del_boundary:
if isinstance(edge, bmesh.types.BMEdge):
if edge in face.edges:
face_del.add(face)
self.ensure(bm)
face_del = list(face_del)
self.ensure(bm)
del del_boundary
self.ensure(bm)
if not self.connect_ends:
bmesh.ops.delete(bm, geom=face_del, context=5)
self.ensure(bm)
del face_del
self.ensure(bm)
for edge in bm.edges:
if edge.is_boundary:
edge.select = True
bm = bmesh.update_edit_mesh(ob.data, 1, 1)
context.tool_settings.mesh_select_mode = store_selection_mode
return {'FINISHED'}
def register():
bpy.utils.register_class(MESH_OT_edges_floor_plan)
def unregister():
bpy.utils.unregister_class(MESH_OT_edges_floor_plan)
if __name__ == "__main__":
register()

View File

@ -2,16 +2,15 @@
bl_info = {
"name": "Set edges length",
"description": "edges length",
"description": "Edges length",
"author": "Giuseppe De Marco [BlenderLab] inspired by NirenYang",
"version": (0, 1, 0),
"blender": (2, 7, 0, 5),
"location": "[Toolbar][Tools][Mesh Tools]: set Length(Shit+Alt+E)",
"blender": (2, 7, 1),
"location": "Toolbar > Tools > Mesh Tools: set Length(Shit+Alt+E)",
"warning": "",
"category": "Mesh",
"wiki_url": "",
"tracker_url": "",
}
"category": "Mesh",
}
import bpy
import bmesh
@ -40,8 +39,8 @@ def get_edge_vector(edge):
def get_selected(bmesh_obj, geometry_type):
# geometry type should be edges, verts or faces
selected = []
for i in getattr(bmesh_obj, geometry_type):
if i.select:
selected.append(i)
@ -66,51 +65,58 @@ class LengthSet(Operator):
bl_options = {'REGISTER', 'UNDO'}
old_length = FloatProperty(
name="Original length",
options={'HIDDEN'},
)
name="Original length",
options={'HIDDEN'},
)
set_lenght_type = EnumProperty(
items=[
('manual', "Manual", "Input manually the desired Target Lenght"),
('existing', "Existing Lenght", "Use existing geometry Edges' characteristics"),
],
name="Set Type of Input",
)
items=[
('manual', "Manual",
"Input manually the desired Target Lenght"),
('existing', "Existing Lenght",
"Use existing geometry Edges' characteristics"),
],
name="Set Type of Input",
)
target_length = FloatProperty(
name="Target Length",
description="Input a value for an Edges Lenght target",
default=1.00,
unit='LENGTH',
precision=5
)
name="Target Length",
description="Input a value for an Edges Lenght target",
default=1.00,
unit='LENGTH',
precision=5
)
existing_lenght = EnumProperty(
items=[
('min', "Shortest", "Set all to shortest Edge of selection"),
('max', "Longest", "Set all to the longest Edge of selection"),
('average', "Average", "Set all to the average Edge lenght of selection"),
('active', "Active", "Set all to the active Edge's one\n"
"Needs a selection to be done in Edge Select mode"),
],
name="Existing lenght"
)
items=[
('min', "Shortest",
"Set all to shortest Edge of selection"),
('max', "Longest",
"Set all to the longest Edge of selection"),
('average', "Average",
"Set all to the average Edge lenght of selection"),
('active', "Active",
"Set all to the active Edge's one\n"
"Needs a selection to be done in Edge Select mode"),
],
name="Existing lenght"
)
mode = EnumProperty(
items=[
('fixed', "Fixed", "Fixed"),
('increment', "Increment", "Increment"),
('decrement', "Decrement", "Decrement"),
],
name="Mode"
)
items=[
('fixed', "Fixed", "Fixed"),
('increment', "Increment", "Increment"),
('decrement', "Decrement", "Decrement"),
],
name="Mode"
)
behaviour = EnumProperty(
items=[
('proportional', "Proportional",
"Move vertex locations proportionally to the center of the Edge"),
('clockwise', "Clockwise",
"Compute the Edges' vertex locations in a clockwise fashion"),
('unclockwise', "Counterclockwise",
"Compute the Edges' vertex locations in a counterclockwise fashion"),
],
name="Resize behavior")
items=[
('proportional', "Proportional",
"Move vertex locations proportionally to the center of the Edge"),
('clockwise', "Clockwise",
"Compute the Edges' vertex locations in a clockwise fashion"),
('unclockwise', "Counterclockwise",
"Compute the Edges' vertex locations in a counterclockwise fashion"),
],
name="Resize behavior"
)
originary_edge_length_dict = {}
edge_lenghts = []
@ -138,7 +144,7 @@ class LengthSet(Operator):
layout.label("Mode:")
layout.prop(self, "mode", text="")
layout.label("Resize Behavior")
layout.label("Resize Behavior:")
layout.prop(self, "behaviour", text="")
def get_existing_edge_lenght(self, bm):
@ -251,7 +257,8 @@ class LengthSet(Operator):
if edge_length_debug:
self.report({'INFO'},
' - '.join(('vector ' + str(vector),
'originary_vector ' + str(self.originary_edge_length_dict[verts_index])
'originary_vector ' +
str(self.originary_edge_length_dict[verts_index])
)))
verts = (edge.verts[0].co, edge.verts[1].co)
@ -281,18 +288,22 @@ class LengthSet(Operator):
elif self.behaviour == 'unclockwise':
if self.mode == 'increment':
edge.verts[1].co = verts[0] + (self.originary_edge_length_dict[verts_index] + vector)
edge.verts[1].co = \
verts[0] + (self.originary_edge_length_dict[verts_index] + vector)
elif self.mode == 'decrement':
edge.verts[0].co = verts[1] - (self.originary_edge_length_dict[verts_index] - vector)
edge.verts[0].co = \
verts[1] - (self.originary_edge_length_dict[verts_index] - vector)
else:
edge.verts[1].co = verts[0] + vector
else:
# clockwise
if self.mode == 'increment':
edge.verts[0].co = verts[1] - (self.originary_edge_length_dict[verts_index] + vector)
edge.verts[0].co = \
verts[1] - (self.originary_edge_length_dict[verts_index] + vector)
elif self.mode == 'decrement':
edge.verts[1].co = verts[0] + (self.originary_edge_length_dict[verts_index] - vector)
edge.verts[1].co = \
verts[0] + (self.originary_edge_length_dict[verts_index] - vector)
else:
edge.verts[0].co = verts[1] - vector

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
### BEGIN GPL LICENSE BLOCK #####
# ##### 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
@ -25,14 +25,17 @@ bl_info = {
"version": (0, 8, 1),
"blender": (2, 76, 5),
"location": "View3D > TOOLS > Tools > Mesh Tools > Add: > Extrude Menu (Alt + E)",
"description": "Extrude face and merge edge intersections between the mesh and the new edges",
"wiki_url" : "http://blenderartists.org/forum/showthread.php?376618-Addon-Push-Pull-Face",
"tracker_url": "https://developer.blender.org/maniphest/task/edit/form/2/",
"description": "Extrude face and merge edge intersections "
"between the mesh and the new edges",
"wiki_url": "http://blenderartists.org/forum/"
"showthread.php?376618-Addon-Push-Pull-Face",
"category": "Mesh"}
import bpy, bmesh
import bpy
import bmesh
from mathutils.geometry import intersect_line_line
from bpy.props import FloatProperty
from bpy.types import Operator
class BVHco():
i = 0
@ -43,7 +46,8 @@ class BVHco():
c2y = 0.0
c2z = 0.0
def edges_BVH_overlap(bm, edges, epsilon = 0.0001):
def edges_BVH_overlap(bm, edges, epsilon=0.0001):
bco = set()
for e in edges:
bvh = BVHco()
@ -113,19 +117,19 @@ def edges_BVH_overlap(bm, edges, epsilon = 0.0001):
overlap[e1] = oget(e1, set()).union({e2})
return overlap
def intersect_edges_edges(overlap, precision = 4):
def intersect_edges_edges(overlap, precision=4):
epsilon = .1**precision
fpre_min = -epsilon
fpre_max = 1+epsilon
fpre_max = 1 + epsilon
splits = {}
sp_get = splits.get
new_edges1 = set()
new_edges2 = set()
targetmap = {}
for edg1 in overlap:
#print("***", ed1.index, "***")
# print("***", ed1.index, "***")
for edg2 in overlap[edg1]:
#print('loop', ed2.index)
a1 = edg1.verts[0]
a2 = edg1.verts[1]
b1 = edg2.verts[0]
@ -133,7 +137,7 @@ def intersect_edges_edges(overlap, precision = 4):
# test if are linked
if a1 in {b1, b2} or a2 in {b1, b2}:
#print('linked')
# print('linked')
continue
aco1, aco2 = a1.co, a2.co
@ -141,23 +145,23 @@ def intersect_edges_edges(overlap, precision = 4):
tp = intersect_line_line(aco1, aco2, bco1, bco2)
if tp:
p1, p2 = tp
if (p1 - p2).to_tuple(precision) == (0,0,0):
v = aco2-aco1
if (p1 - p2).to_tuple(precision) == (0, 0, 0):
v = aco2 - aco1
f = p1 - aco1
x,y,z = abs(v.x), abs(v.y), abs(v.z)
x, y, z = abs(v.x), abs(v.y), abs(v.z)
max1 = 0 if x >= y and x >= z else\
1 if y >= x and y >= z else 2
fac1 = f[max1]/v[max1]
fac1 = f[max1] / v[max1]
v = bco2-bco1
v = bco2 - bco1
f = p2 - bco1
x,y,z = abs(v.x), abs(v.y), abs(v.z)
x, y, z = abs(v.x), abs(v.y), abs(v.z)
max2 = 0 if x >= y and x >= z else\
1 if y >= x and y >= z else 2
fac2 = f[max2]/v[max2]
fac2 = f[max2] / v[max2]
if fpre_min <= fac1 <= fpre_max:
#print(edg1.index, 'can intersect', edg2.index)
# print(edg1.index, 'can intersect', edg2.index)
ed1 = edg1
elif edg1 in splits:
@ -168,21 +172,21 @@ def intersect_edges_edges(overlap, precision = 4):
vco1 = a1.co
vco2 = a2.co
v = vco2-vco1
v = vco2 - vco1
f = p1 - vco1
fac1 = f[max1]/v[max1]
fac1 = f[max1] / v[max1]
if fpre_min <= fac1 <= fpre_max:
#print(e.index, 'can intersect', edg2.index)
# print(e.index, 'can intersect', edg2.index)
break
else:
#print(edg1.index, 'really does not intersect', edg2.index)
# print(edg1.index, 'really does not intersect', edg2.index)
continue
else:
#print(edg1.index, 'not intersect', edg2.index)
# print(edg1.index, 'not intersect', edg2.index)
continue
if fpre_min <= fac2 <= fpre_max:
#print(ed1.index, 'actually intersect', edg2.index)
# print(ed1.index, 'actually intersect', edg2.index)
ed2 = edg2
elif edg2 in splits:
@ -193,17 +197,17 @@ def intersect_edges_edges(overlap, precision = 4):
vco1 = b1.co
vco2 = b2.co
v = vco2-vco1
v = vco2 - vco1
f = p2 - vco1
fac2 = f[max2]/v[max2]
fac2 = f[max2] / v[max2]
if fpre_min <= fac2 <= fpre_max:
#print(ed1.index, 'actually intersect', e.index)
# print(ed1.index, 'actually intersect', e.index)
break
else:
#print(ed1.index, 'really does not intersect', ed2.index)
# print(ed1.index, 'really does not intersect', ed2.index)
continue
else:
#print(ed1.index, 'not intersect', edg2.index)
# print(ed1.index, 'not intersect', edg2.index)
continue
new_edges1.add(ed1)
@ -227,30 +231,28 @@ def intersect_edges_edges(overlap, precision = 4):
new_edges2.add(ne2)
splits[edg2] = sp_get(edg2, set()).union({ne2})
if nv1 != nv2: #necessary?
if nv1 != nv2: # necessary?
targetmap[nv1] = nv2
#else:
#print('not coplanar')
#else:
#print("parallel or collinear")
return new_edges1, new_edges2, targetmap
class Extrude_and_Reshape(bpy.types.Operator):
"""Push and pull face entities to sculpt 3d models"""
class Extrude_and_Reshape(Operator):
bl_idname = "mesh.extrude_reshape"
bl_label = "Extrude and Reshape"
bl_description = "Push and pull face entities to sculpt 3d models"
bl_options = {'REGISTER', 'GRAB_CURSOR', 'BLOCKING'}
@classmethod
def poll(cls, context):
return context.mode is not 'EDIT_MESH'
return context.mode is not 'EDIT_MESH'
def modal(self, context, event):
if self.confirm:
sface = self.bm.faces.active
if not sface:
for face in self.bm.faces:
if face.select == True:
if face.select is True:
sface = face
break
else:
@ -259,13 +261,13 @@ class Extrude_and_Reshape(bpy.types.Operator):
edges = set()
[[edges.add(ed) for ed in v.link_edges] for v in sface.verts]
overlap = edges_BVH_overlap(self.bm, edges, epsilon = 0.0001)
overlap = {k: v for k,v in overlap.items() if k not in edges} # remove repetition
#print([e.index for e in edges])
#for a, b in overlap.items():
#print(a.index, [e.index for e in b])
overlap = edges_BVH_overlap(self.bm, edges, epsilon=0.0001)
overlap = {k: v for k, v in overlap.items() if k not in edges} # remove repetition
"""
print([e.index for e in edges])
for a, b in overlap.items():
print(a.index, [e.index for e in b])
"""
new_edges1, new_edges2, targetmap = intersect_edges_edges(overlap)
pos_weld = set()
for e in new_edges1:
@ -274,9 +276,11 @@ class Extrude_and_Reshape(bpy.types.Operator):
pos_weld.add((targetmap[v1], targetmap[v2]))
if targetmap:
bmesh.ops.weld_verts(self.bm, targetmap=targetmap)
#print([e.is_valid for e in new_edges1])
#print([e.is_valid for e in new_edges2])
#sp_faces1 = set()
"""
print([e.is_valid for e in new_edges1])
print([e.is_valid for e in new_edges2])
sp_faces1 = set()
"""
for e in pos_weld:
v1, v2 = e
lf1 = set(v1.link_faces)
@ -285,11 +289,11 @@ class Extrude_and_Reshape(bpy.types.Operator):
for f in rlfe:
try:
nf = bmesh.utils.face_split(f, v1, v2)
#sp_faces1.update({f, nf[0]})
# sp_faces1.update({f, nf[0]})
except:
pass
#sp_faces2 = set()
# sp_faces2 = set()
for e in new_edges2:
lfe = set(e.link_faces)
v1, v2 = e.verts
@ -298,7 +302,7 @@ class Extrude_and_Reshape(bpy.types.Operator):
rlfe = lf1.intersection(lf2)
for f in rlfe.difference(lfe):
nf = bmesh.utils.face_split(f, v1, v2)
#sp_faces2.update({f, nf[0]})
# sp_faces2.update({f, nf[0]})
bmesh.update_edit_mesh(self.mesh, tessface=True, destructive=True)
return {'FINISHED'}
@ -315,7 +319,7 @@ class Extrude_and_Reshape(bpy.types.Operator):
selection = self.bm.select_history[-1]
except:
for face in self.bm.faces:
if face.select == True:
if face.select is True:
selection = face
break
else:
@ -325,22 +329,27 @@ class Extrude_and_Reshape(bpy.types.Operator):
return {'FINISHED'}
else:
face = selection
#face.select = False
# face.select = False
bpy.ops.mesh.select_all(action='DESELECT')
geom = []
for edge in face.edges:
if abs(edge.calc_face_angle(0) - 1.5707963267948966) < 0.01: #self.angle_tolerance:
if abs(edge.calc_face_angle(0) - 1.5707963267948966) < 0.01: # self.angle_tolerance:
geom.append(edge)
ret_dict = bmesh.ops.extrude_discrete_faces(self.bm, faces = [face])
ret_dict = bmesh.ops.extrude_discrete_faces(self.bm, faces=[face])
for face in ret_dict['faces']:
self.bm.faces.active = face
face.select = True
sface = face
dfaces = bmesh.ops.dissolve_edges(self.bm, edges = geom, use_verts=True, use_face_split=False)
dfaces = bmesh.ops.dissolve_edges(
self.bm, edges=geom, use_verts=True, use_face_split=False
)
bmesh.update_edit_mesh(self.mesh, tessface=True, destructive=True)
bpy.ops.transform.translate('INVOKE_DEFAULT', constraint_axis=(False, False, True), constraint_orientation='NORMAL', release_confirm=True)
bpy.ops.transform.translate(
'INVOKE_DEFAULT', constraint_axis=(False, False, True),
constraint_orientation='NORMAL', release_confirm=True
)
context.window_manager.modal_handler_add(self)
@ -348,18 +357,22 @@ class Extrude_and_Reshape(bpy.types.Operator):
self.confirm = False
return {'RUNNING_MODAL'}
def operator_draw(self,context):
def operator_draw(self, context):
layout = self.layout
col = layout.column(align=True)
col.operator("mesh.extrude_reshape", text="Extrude and Reshape")
def register():
bpy.utils.register_class(Extrude_and_Reshape)
bpy.types.VIEW3D_MT_edit_mesh_extrude.append(operator_draw)
def unregister():
bpy.types.VIEW3D_MT_edit_mesh_extrude.remove(operator_draw)
bpy.utils.unregister_class(Extrude_and_Reshape)
if __name__ == "__main__":
register()

View File

@ -20,12 +20,11 @@ bl_info = {
"name": "Fast Loop",
"description": "Add loops fast",
"author": "Andy Davies (metalliandy)",
"version": (0, 16),
"version": (0, 1, 7),
"blender": (2, 5, 6),
"location": "Tool Shelf",
"warning": "",
"wiki_url": "",
"tracker_url": "",
"category": "Mesh"
}

View File

@ -1,7 +1,6 @@
# -*- coding: utf-8 -*-
# ***** BEGIN GPL LICENSE BLOCK *****
#
# ##### END 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
@ -17,8 +16,7 @@
# 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 LICENCE BLOCK *****
# ##### END GPL LICENSE BLOCK #####
bl_info = {
"name": "FilletPlus",
@ -29,7 +27,6 @@ bl_info = {
"description": "",
"warning": "",
"wiki_url": "",
"tracker_url": "",
"category": "Mesh"}
@ -42,7 +39,10 @@ from bpy.props import (
from bpy.types import Operator
import bmesh
from mathutils import Matrix
from math import cos, pi, degrees, sin, tan
from math import (
cos, pi, sin,
degrees, tan,
)
def list_clear_(l):
@ -298,36 +298,36 @@ class MESH_OT_fillet_plus(Operator):
bl_options = {"REGISTER", "UNDO"}
adj = FloatProperty(
name="",
description="Size of the filleted corners",
default=0.1,
min=0.00001, max=100.0,
step=1,
precision=3
)
name="",
description="Size of the filleted corners",
default=0.1,
min=0.00001, max=100.0,
step=1,
precision=3
)
n = IntProperty(
name="",
description="Subdivision of the filleted corners",
default=3,
min=1, max=50,
step=1
)
name="",
description="Subdivision of the filleted corners",
default=3,
min=1, max=50,
step=1
)
out = BoolProperty(
name="Outside",
description="Fillet towards outside",
default=False
)
name="Outside",
description="Fillet towards outside",
default=False
)
flip = BoolProperty(
name="Flip",
description="Flip the direction of the Fillet\n"
"Only available if Outside option is not active",
default=False
)
name="Flip",
description="Flip the direction of the Fillet\n"
"Only available if Outside option is not active",
default=False
)
radius = BoolProperty(
name="Radius",
description="Use radius for the size of the filleted corners",
default=False
)
name="Radius",
description="Use radius for the size of the filleted corners",
default=False
)
@classmethod
def poll(cls, context):
@ -338,8 +338,8 @@ class MESH_OT_fillet_plus(Operator):
layout = self.layout
if f_buf.check is False:
layout.label(text="Angle equal to 0 or 180", icon="INFO")
layout.label("Can not fillet")
layout.label(text="Angle is equal to 0 or 180", icon="INFO")
layout.label(text="Can not fillet", icon="BLANK1")
else:
layout.prop(self, "radius")
if self.radius is True:
@ -349,6 +349,7 @@ class MESH_OT_fillet_plus(Operator):
layout.prop(self, "adj")
layout.label("Number of sides:")
layout.prop(self, "n")
if self.n > 1:
row = layout.row(align=False)
row.prop(self, "out")

View File

@ -1,13 +1,13 @@
# gpl authors: lijenstina, meta-androcto
# Note: this script contains the Help Operator used by the various functions
# Usage: add a key string the dictionary with the list of strings to pass to labels in this file
# Usage: add a key string to the dictionary in this file with the list of strings to pass to labels
# and call the operator from the add-on UI draw function by passing the help_ids parameter
# If the size of the pop-up if needed, define popup_size in the call by using varibles
# Example (variable prop):
# prop = layout.row("mesh.extra_tools_help")
# prop.help_ids = "default"
# prop.popup_size = 400
# Example (with using the variable props):
# props = layout.row("mesh.extra_tools_help")
# props.help_ids = "default"
# props.popup_size = 400
import bpy
@ -24,16 +24,16 @@ class MESH_OT_extra_tools_help(Operator):
bl_options = {'REGISTER'}
help_ids = StringProperty(
name="ID of the Operator to display",
options={'HIDDEN'},
default="default"
)
name="ID of the Operator to display",
options={'HIDDEN'},
default="default"
)
popup_size = IntProperty(
name="Size of the Help Pop-up Menu",
default=350,
min=100,
max=600,
)
name="Size of the Help Pop-up Menu",
default=350,
min=100,
max=600,
)
def draw(self, context):
layout = self.layout
@ -55,144 +55,174 @@ def help_custom_draw(identifier="default"):
# and call them separately
# In case nothing is passed from the UI call, the returned list is default
# If undefined one is passed, it will return a warning message
help_text = {"default": [
"This is a placeholder text",
"Please fill up the entries in the " + __name__ + " script",
],
"random_vertices": [
"To use:",
"Make a selection or selection of Vertices",
"Randomize displaced positions",
"Note:",
"There is an option to use Vertex Weights for displacement",
"Before use, don't forget to assign after updating the Group Weight",
],
"mesh_vertex_chamfer": [
"To use:",
"Make a selection or selection of vertices",
"Result is a triangle Chamfer, works on a single vertex",
"Note:",
"The difference to the vertex Bevel is that original geometry",
"(selected vertices) can be kept as an option",
"Limitation:",
"In some cases, may need to press F to fill the result",
],
"mesh_filletplus": [
"To use:",
"Select two adjacent edges and press Fillet button",
"Limitation:",
"Works on a mesh whose all faces share the same normal",
"(Flat Surface - faces have the same direction)",
"Planes with already round corners can produce unsatisfactory results",
"Only boundary edges will be evaluated",
],
"mesh_offset_edges": [
"To use:",
"Make a selection or selection of Edges",
"Extrude, rotate extrusions and more",
"Limitation:",
"Operates only on separate Edge loops selections",
"(i.e. Edge loops that are not connected by a selected edge)",
],
"mesh_edge_roundifier": [
"To use:",
"Select a single or multiple Edges",
"Make Arcs with various parameters",
"Reference, Rotation, Scaling, Connection and Offset",
"Note:",
"The Mode - Reset button restores the default values",
],
"mesh_edges_length": [
"To use:",
"Select a single or multiple Edges",
"Change length with various parameters",
"Limitation:",
"Does not operate on edges that share a vertex",
"If the selection wasn't done in Edge Selection mode,",
"the option Active will not work (due to Blender's limitation)",
],
"mesh_to_wall": [
"To use:",
"Extrudes flat along edges",
"Adds depth on both sides of an edge wire",
"Limitation:",
"Works best on Flat surfaces i.e. Ground Plan like geometry",
],
"mesh_mextrude_plus": [
"To use:",
"Make a selection of Faces",
"Extrude with Rotation, Scaling, Variation,",
"Randomization and Offset parameters",
"Limitation:",
"Works only with selections that enclose Faces",
"(i.e. all Edges or Vertices of a Face selected)",
],
"mesh_extrude_and_reshape": [
"To use:",
"Extrude Face and merge Edge intersections,",
"between the mesh and the new Edges",
"Note:",
"If selected Vertices don't form Face they will be",
"still extruded in the same direction",
"Limitation:",
"Works only with the last selected face",
"(or all Edges or Vertices of a Face selected)",
],
"face_inset_fillet": [
"To use:",
"Select one or multiple faces and inset",
"Inset square, circle or outside",
"Note:",
"Radius: use remove doubles to tidy joins",
"Out: select and use normals flip before extruding",
"Limitation:",
"Using the Out option, sometimes can lead to unsatisfactory results",
],
"mesh_cut_faces": [
"To use:",
"Make a selection or selection of Faces",
"Some Functions work on a plane only",
"Limitation:",
"The selection must include at least two Faces with adjacent edges",
"(Selections not sharing edges will not work)",
],
"split_solidify": [
"To use:",
"Make a selection or selection of Faces",
"Split Faces and Extrude results",
"Similar to a shatter/explode effect",
],
"mesh_fastloop": [
"To use:",
"Activate the tool and hover over the mesh in the general area",
"for the loop and left click once to confirm the loop placement",
"Slide using the mouse to fine tune its position, left click to confirm",
"Repeat the operations if needed for new loops",
"Press Esc. twice to exit the tool",
"Limitation:",
"The tool has the same limitations as Loop Cut and Slide",
"In the Operator Panel, only the last loop can be tweaked",
],
"mesh_pen_tool": [
"To use:",
"Press Ctrl + D key or click Draw button",
"To draw along x use SHIFT + MOUSEMOVE",
"To draw along y use ALT + MOUSEMOVE",
"Press Ctrl to toggle Extrude at Cursor tool",
"Right click to finish drawing or",
"Press Esc to cancel",
],
"pkhg_faces": [
"To use:",
"Needs a Face Selection in Edit Mode",
"Select an option from Face Types drop down list",
"Extrude, rotate extrusions and more",
"Toggle Edit Mode after use",
"Note:",
"After using the operator, normals could need repair,",
"or Removing Doubles",
],
}
help_text = {
"default": [
"This is a placeholder text",
"Please fill up the entries in the " + __name__ + " script",
],
"random_vertices": [
"To use:",
"Make a selection or selection of Vertices",
"Randomize displaced positions",
"Note:",
"There is an option to use Vertex Weights for displacement",
"Prior to use, don't forget to assign after updating the Group Weight",
],
"mesh_vertex_chamfer": [
"To use:",
"Make a selection or selection of vertices",
"Result is a triangle Chamfer, works on a single vertex",
"Note:",
"The difference to the vertex Bevel is that original geometry",
"(selected vertices) can optionally be kept and displaced",
"Limitation:",
"In some cases, may need to press F to fill the result",
],
"mesh_filletplus": [
"To use:",
"Select two adjacent edges and press Fillet button",
"Limitation:",
"Works on a mesh with all faces sharing the same normal",
"(Flat Surface - faces have the same direction)",
"Planes with already round corners can produce unsatisfactory results",
"Only boundary edges will be evaluated",
],
"mesh_offset_edges": [
"To use:",
"Make a selection or selection of Edges",
"Extrude, rotate extrusions and more",
"Limitation:",
"Operates only on separate Edge loops selections",
"(i.e. Edge loops that are not connected by a selected edge)",
],
"mesh_edge_roundifier": [
"To use:",
"Select a single or multiple Edges",
"Make Arcs with various parameters",
"Reference, Rotation, Scaling, Connection and Offset",
"Note:",
"The Mode - Reset button restores the default values",
],
"mesh_edges_length": [
"To use:",
"Select a single or multiple Edges",
"Change length with various parameters",
"Limitation:",
"Does not operate on edges that share a vertex",
"If the selection wasn't done in Edge Selection mode,",
"the option Active will not work (due to Blender's limitation)",
],
"mesh_edges_floor_plan": [
"To use:",
"Starting edges will be flat extruded forming faces strips",
"on the inside. Similar to using Face fill inset select outer",
"Methods:",
"Edge Net: Fills the edge grid with faces then Inset",
"Single Face: Single Face fill (all Edges) then Inset",
"Solidify: Extrude along defined axis, apply a Solidify modifier",
"Note:",
"Grid Fill and Single Face sometimes need tweaking with the options",
"Limitation:",
"Depending on the input geometry, Keep Ngons sometimes needs to be",
"enabled to produce any results",
"Edge Net and Single Face depend on bmesh face fill and inset",
"that sometimes can fail to produce good results",
"Avoid using Single Face Method on Edges that define a Volume - like Suzanne",
"Solidify method works best for flat surfaces and complex geometry",
],
"mesh_mextrude_plus": [
"To use:",
"Make a selection of Faces",
"Extrude with Rotation, Scaling, Variation,",
"Randomization and Offset parameters",
"Limitation:",
"Works only with selections that enclose Faces",
"(i.e. all Edges or Vertices of a Face selected)",
],
"mesh_extrude_and_reshape": [
"To use:",
"Extrude Face and merge Edge intersections,",
"between the mesh and the new Edges",
"Note:",
"If selected Vertices don't form Face they will be",
"still extruded in the same direction",
"Limitation:",
"Works only with the last selected face",
"(or all Edges or Vertices of a Face selected)",
],
"face_inset_fillet": [
"To use:",
"Select one or multiple faces and inset",
"Inset square, circle or outside",
"Note:",
"Radius: use remove doubles to tidy joins",
"Out: select and use normals flip before extruding",
"Limitation:",
"Using the Out option, sometimes can lead to unsatisfactory results",
],
"mesh_cut_faces": [
"To use:",
"Make a selection or selection of Faces",
"Some Functions work on a plane only",
"Limitation:",
"The selection must include at least two Faces with adjacent edges",
"(Selections not sharing edges will not work)",
],
"split_solidify": [
"To use:",
"Make a selection or selection of Faces",
"Split Faces and Extrude results",
"Similar to a shatter/explode effect",
],
"mesh_fastloop": [
"To use:",
"Activate the tool and hover over the mesh in the general area",
"for the loop and left click once to confirm the loop placement",
"Slide using the mouse to fine tune its position, left click to confirm",
"Repeat the operations if needed for new loops",
"Press Esc. twice to exit the tool",
"Limitation:",
"The tool has the same limitations as Loop Cut and Slide",
"In the Operator Panel, only the last loop can be tweaked",
],
"mesh_pen_tool": [
"To use:",
"Press Ctrl + D key or click Draw button",
"To draw along x use SHIFT + MOUSEMOVE",
"To draw along y use ALT + MOUSEMOVE",
"Press Ctrl to toggle Extrude at Cursor tool",
"Right click to finish drawing or",
"Press Esc to cancel",
],
"pkhg_faces": [
"To use:",
"Needs a Face Selection in Edit Mode",
"Select an option from Face Types drop down list",
"Extrude, rotate extrusions and more",
"Toggle Edit Mode after use",
"Note:",
"After using the operator, normals could need repair,",
"or Removing Doubles",
],
"vertex_align": [
"To use:",
"Select vertices that you want to align and click Align button",
"Options include aligning to defined Custom coordinates or",
"Stored vertex - (a single selected one with Store Selected Vertex)",
"Note:",
"Use Stored Coordinates - allows to save a set of coordinates",
"as a starting point that can be tweaked on during operation",
],
"mesh_check": [
"To use:",
"Tris and Ngons will select Faces by corensponding type",
"Display faces will color the faces depending on the",
"defined Colors, Edges' width and Face Opacity",
"Note:",
"The Faces' type count is already included elsewhere:",
"In the Properties Editor > Data > Face / Info Select Panel",
],
}
if identifier in help_text:
return help_text[identifier]
@ -202,13 +232,11 @@ def help_custom_draw(identifier="default"):
# register
def register():
bpy.utils.register_module(__name__)
pass
bpy.utils.register_class(MESH_OT_extra_tools_help)
def unregister():
bpy.utils.unregister_module(__name__)
pass
bpy.utils.unregister_class(MESH_OT_extra_tools_help)
if __name__ == "__main__":

View File

@ -15,11 +15,11 @@
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
#
# Repeats extrusion + rotation + scale for one or more faces
# Original code by liero
# Update by Jimmy Hazevoet 03/2017 for Blender 2.79
# normal rotation, probability, scaled offset, object coörds, initial and per step noise
# normal rotation, probability, scaled offset, object coords, initial and per step noise
bl_info = {
@ -31,7 +31,6 @@ bl_info = {
"description": "Repeat extrusions from faces to create organic shapes",
"warning": "",
"wiki_url": "",
"tracker_url": "https://developer.blender.org/T28570",
"category": "Mesh"}
@ -41,32 +40,38 @@ import random
from bpy.types import Operator
from random import gauss
from math import radians
from mathutils import Euler, Vector
from mathutils import (
Euler, Vector,
)
from bpy.props import (
FloatProperty,
IntProperty,
BoolProperty,
EnumProperty,
)
def gloc(self, r):
return Vector((self.offx, self.offy, self.offz))
def vloc(self, r):
random.seed(self.ran + r)
return self.off * (1 + gauss(0, self.var1 / 3))
def nrot(self, n):
return Euler((radians(self.nrotx) * n[0],
radians(self.nroty) * n[1],
radians(self.nrotz) * n[2]), 'XYZ')
def vrot(self, r):
random.seed(self.ran + r)
return Euler((radians(self.rotx) + gauss(0, self.var2 / 3),
radians(self.roty) + gauss(0, self.var2 / 3),
radians(self.rotz) + gauss(0, self.var2 / 3)), 'XYZ')
def vsca(self, r):
random.seed(self.ran + r)
return self.sca * (1 + gauss(0, self.var3 / 3))
@ -80,142 +85,142 @@ class MExtrude(Operator):
bl_options = {"REGISTER", "UNDO", "PRESET"}
off = FloatProperty(
name="Offset",
soft_min=0.001, soft_max=10,
min=-100, max=100,
default=1.0,
description="Translation"
)
name="Offset",
soft_min=0.001, soft_max=10,
min=-100, max=100,
default=1.0,
description="Translation"
)
offx = FloatProperty(
name="Loc X",
soft_min=-10.0, soft_max=10.0,
min=-100.0, max=100.0,
default=0.0,
description="Global translation X"
)
name="Loc X",
soft_min=-10.0, soft_max=10.0,
min=-100.0, max=100.0,
default=0.0,
description="Global Translation X"
)
offy = FloatProperty(
name="Loc Y",
soft_min=-10.0, soft_max=10.0,
min=-100.0, max=100.0,
default=0.0,
description="Global translation Y"
)
name="Loc Y",
soft_min=-10.0, soft_max=10.0,
min=-100.0, max=100.0,
default=0.0,
description="Global Translation Y"
)
offz = FloatProperty(
name="Loc Z",
soft_min=-10.0, soft_max=10.0,
min=-100.0, max=100.0,
default=0.0,
description="Global translation Z"
)
name="Loc Z",
soft_min=-10.0, soft_max=10.0,
min=-100.0, max=100.0,
default=0.0,
description="Global Translation Z"
)
rotx = FloatProperty(
name="Rot X",
min=-85, max=85,
soft_min=-30, soft_max=30,
default=0,
description="X Rotation"
)
name="Rot X",
min=-85, max=85,
soft_min=-30, soft_max=30,
default=0,
description="X Rotation"
)
roty = FloatProperty(
name="Rot Y",
min=-85, max=85,
soft_min=-30,
soft_max=30,
default=0,
description="Y Rotation"
)
name="Rot Y",
min=-85, max=85,
soft_min=-30,
soft_max=30,
default=0,
description="Y Rotation"
)
rotz = FloatProperty(
name="Rot Z",
min=-85, max=85,
soft_min=-30, soft_max=30,
default=-0,
description="Z Rotation"
)
name="Rot Z",
min=-85, max=85,
soft_min=-30, soft_max=30,
default=-0,
description="Z Rotation"
)
nrotx = FloatProperty(
name="N Rot X",
min=-85, max=85,
soft_min=-30, soft_max=30,
default=0,
description="Normal X Rotation"
)
name="N Rot X",
min=-85, max=85,
soft_min=-30, soft_max=30,
default=0,
description="Normal X Rotation"
)
nroty = FloatProperty(
name="N Rot Y",
min=-85, max=85,
soft_min=-30, soft_max=30,
default=0,
description="Normal Y Rotation"
)
name="N Rot Y",
min=-85, max=85,
soft_min=-30, soft_max=30,
default=0,
description="Normal Y Rotation"
)
nrotz = FloatProperty(
name="N Rot Z",
min=-85, max=85,
soft_min=-30, soft_max=30,
default=-0,
description="Normal Z Rotation"
)
name="N Rot Z",
min=-85, max=85,
soft_min=-30, soft_max=30,
default=-0,
description="Normal Z Rotation"
)
sca = FloatProperty(
name="Scale",
min=0.01, max=10,
soft_min=0.5, soft_max=1.5,
default=1.0,
description="Scaling of the selected faces after extrusion"
)
name="Scale",
min=0.01, max=10,
soft_min=0.5, soft_max=1.5,
default=1.0,
description="Scaling of the selected faces after extrusion"
)
var1 = FloatProperty(
name="Offset Var", min=-10, max=10,
soft_min=-1, soft_max=1,
default=0,
description="Offset variation"
)
name="Offset Var", min=-10, max=10,
soft_min=-1, soft_max=1,
default=0,
description="Offset variation"
)
var2 = FloatProperty(
name="Rotation Var",
min=-10, max=10,
soft_min=-1, soft_max=1,
default=0,
description="Rotation variation"
)
name="Rotation Var",
min=-10, max=10,
soft_min=-1, soft_max=1,
default=0,
description="Rotation variation"
)
var3 = FloatProperty(
name="Scale Noise",
min=-10, max=10,
soft_min=-1, soft_max=1,
default=0,
description="Scaling noise"
)
name="Scale Noise",
min=-10, max=10,
soft_min=-1, soft_max=1,
default=0,
description="Scaling noise"
)
var4 = IntProperty(
name="Probability",
min=0, max=100,
default=100,
description="Probability, chance of extruding a face"
)
name="Probability",
min=0, max=100,
default=100,
description="Probability, chance of extruding a face"
)
num = IntProperty(
name="Repeat",
min=1, max=500,
soft_max=100,
default=5,
description="Repetitions"
)
name="Repeat",
min=1, max=500,
soft_max=100,
default=5,
description="Repetitions"
)
ran = IntProperty(
name="Seed",
min=-9999, max=9999,
default=0,
description="Seed to feed random values"
)
name="Seed",
min=-9999, max=9999,
default=0,
description="Seed to feed random values"
)
opt1 = BoolProperty(
name="Polygon coördinates",
default=True,
description="Polygon coördinates, Object coördinates"
)
name="Polygon coordinates",
default=True,
description="Polygon coordinates, Object coordinates"
)
opt2 = BoolProperty(
name="Proportional offset",
default=False,
description="Scale * Offset"
)
name="Proportional offset",
default=False,
description="Scale * Offset"
)
opt3 = BoolProperty(
name="Per step rotation noise",
default=False,
description="Per step rotation noise, Initial rotation noise"
)
name="Per step rotation noise",
default=False,
description="Per step rotation noise, Initial rotation noise"
)
opt4 = BoolProperty(
name="Per step scale noise",
default=False,
description="Per step scale noise, Initial scale noise"
)
name="Per step scale noise",
default=False,
description="Per step scale noise, Initial scale noise"
)
@classmethod
def poll(cls, context):
@ -235,9 +240,9 @@ class MExtrude(Operator):
col.prop(self, "rotx", slider=True)
col.prop(self, "roty", slider=True)
col.prop(self, "rotz", slider=True)
col.prop(self, 'nrotx', slider=True)
col.prop(self, 'nroty', slider=True)
col.prop(self, 'nrotz', slider=True)
col.prop(self, "nrotx", slider=True)
col.prop(self, "nroty", slider=True)
col.prop(self, "nrotz", slider=True)
col = layout.column(align=True)
col.prop(self, "sca", slider=True)
@ -288,10 +293,8 @@ class MExtrude(Operator):
# extrusion loop
for r in range(self.num):
## random probability % for extrusions
if self.var4 > int(random.random()*100):
# random probability % for extrusions
if self.var4 > int(random.random() * 100):
nf = of.copy()
nf.normal_update()
no = nf.normal.copy()
@ -348,7 +351,8 @@ class MExtrude(Operator):
bpy.ops.object.mode_set(mode=om)
if not len(sel):
self.report({"WARNING"}, "No suitable Face selection found. Operation cancelled")
self.report({"WARNING"},
"No suitable Face selection found. Operation cancelled")
return {'CANCELLED'}
return {'FINISHED'}

View File

@ -1,20 +1,20 @@
# ***** BEGIN GPL LICENSE BLOCK *****
# ##### 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 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.
# 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.
# 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 LICENCE BLOCK *****
# ##### END GPL LICENSE BLOCK #####
bl_info = {
"name": "Offset Edges",
@ -24,8 +24,8 @@ bl_info = {
"location": "VIEW3D > Edge menu(CTRL-E) > Offset Edges",
"description": "Offset Edges",
"warning": "",
"wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/Scripts/Modeling/offset_edges",
"tracker_url": "",
"wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/"
"Py/Scripts/Modeling/offset_edges",
"category": "Mesh"}
import bpy
@ -510,103 +510,103 @@ class OffsetEdges(Operator):
bl_options = {'REGISTER', 'UNDO'}
geometry_mode = EnumProperty(
items=[('offset', "Offset", "Offset edges"),
('extrude', "Extrude", "Extrude edges"),
('move', "Move", "Move selected edges")],
name="Geometry mode",
default='offset',
update=use_cashes
)
items=[('offset', "Offset", "Offset edges"),
('extrude', "Extrude", "Extrude edges"),
('move', "Move", "Move selected edges")],
name="Geometry mode",
default='offset',
update=use_cashes
)
width = FloatProperty(
name="Width",
default=.2,
precision=4, step=1,
update=use_cashes
)
name="Width",
default=.2,
precision=4, step=1,
update=use_cashes
)
flip_width = BoolProperty(
name="Flip Width",
default=False,
description="Flip width direction",
update=use_cashes
)
name="Flip Width",
default=False,
description="Flip width direction",
update=use_cashes
)
depth = FloatProperty(
name="Depth",
default=.0,
precision=4, step=1,
update=use_cashes
)
name="Depth",
default=.0,
precision=4, step=1,
update=use_cashes
)
flip_depth = BoolProperty(
name="Flip Depth",
default=False,
description="Flip depth direction",
update=use_cashes
)
name="Flip Depth",
default=False,
description="Flip depth direction",
update=use_cashes
)
depth_mode = EnumProperty(
items=[('angle', "Angle", "Angle"),
('depth', "Depth", "Depth")],
name="Depth mode",
default='angle',
update=use_cashes
)
items=[('angle', "Angle", "Angle"),
('depth', "Depth", "Depth")],
name="Depth mode",
default='angle',
update=use_cashes
)
angle = FloatProperty(
name="Angle", default=0,
precision=3, step=.1,
min=-2 * pi, max=2 * pi,
subtype='ANGLE',
description="Angle",
update=use_cashes
)
name="Angle", default=0,
precision=3, step=.1,
min=-2 * pi, max=2 * pi,
subtype='ANGLE',
description="Angle",
update=use_cashes
)
flip_angle = BoolProperty(
name="Flip Angle",
default=False,
description="Flip Angle",
update=use_cashes
)
name="Flip Angle",
default=False,
description="Flip Angle",
update=use_cashes
)
follow_face = BoolProperty(
name="Follow Face",
default=False,
description="Offset along faces around"
)
name="Follow Face",
default=False,
description="Offset along faces around"
)
mirror_modifier = BoolProperty(
name="Mirror Modifier",
default=False,
description="Take into account of Mirror modifier"
)
name="Mirror Modifier",
default=False,
description="Take into account of Mirror modifier"
)
edge_rail = BoolProperty(
name="Edge Rail",
default=False,
description="Align vertices along inner edges"
)
name="Edge Rail",
default=False,
description="Align vertices along inner edges"
)
edge_rail_only_end = BoolProperty(
name="Edge Rail Only End",
default=False,
description="Apply edge rail to end verts only"
)
name="Edge Rail Only End",
default=False,
description="Apply edge rail to end verts only"
)
threshold = FloatProperty(
name="Flat Face Threshold",
default=radians(0.05), precision=5,
step=1.0e-4, subtype='ANGLE',
description="If difference of angle between two adjacent faces is "
"below this value, those faces are regarded as flat",
options={'HIDDEN'}
)
name="Flat Face Threshold",
default=radians(0.05), precision=5,
step=1.0e-4, subtype='ANGLE',
description="If difference of angle between two adjacent faces is "
"below this value, those faces are regarded as flat",
options={'HIDDEN'}
)
caches_valid = BoolProperty(
name="Caches Valid",
default=False,
options={'HIDDEN'}
)
name="Caches Valid",
default=False,
options={'HIDDEN'}
)
angle_presets = EnumProperty(
items=[('', "", ""),
('15°', "15°", "15°"),
('30°', "30°", "30°"),
('45°', "45°", "45°"),
('60°', "60°", "60°"),
('75°', "75°", "75°"),
('90°', "90°", "90°"), ],
name="Angle Presets",
default='',
update=assign_angle_presets
)
items=[('', "", ""),
('15°', "15°", "15°"),
('30°', "30°", "30°"),
('45°', "45°", "45°"),
('60°', "60°", "60°"),
('75°', "75°", "75°"),
('90°', "90°", "90°"), ],
name="Angle Presets",
default='',
update=assign_angle_presets
)
_cache_offset_infos = None
_cache_edges_orig_ixs = None

View File

@ -1,33 +1,32 @@
# -*- coding: utf-8 -*-
# ***** BEGIN GPL LICENSE BLOCK *****
# ##### 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 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.
# 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.
# 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 LICENCE BLOCK *****
# ##### END GPL LICENSE BLOCK #####
bl_info = {
"name": "Pen Tool",
"author": "zmj100",
"version": (0, 2, 9),
"version": (0, 3, 1),
"blender": (2, 78, 0),
"location": "View3D > Tool Shelf",
"description": "",
"warning": "",
"wiki_url": "",
"tracker_url": "",
"category": "Mesh",
}
@ -47,8 +46,14 @@ from bpy.props import (
PointerProperty,
BoolProperty
)
from bpy_extras.view3d_utils import region_2d_to_location_3d, location_3d_to_region_2d
from mathutils import Vector, Matrix
from bpy_extras.view3d_utils import (
region_2d_to_location_3d,
location_3d_to_region_2d,
)
from mathutils import (
Vector,
Matrix,
)
from math import degrees
@ -117,10 +122,10 @@ def draw_callback_px(self, context):
# location 3d
if context.scene.pen_tool_props.b2 is True:
mloc3d = region_2d_to_location_3d(
context.region,
context.space_data.region_3d, Vector((pt_buf.x, pt_buf.y)),
pt_buf.depth_location
)
context.region,
context.space_data.region_3d, Vector((pt_buf.x, pt_buf.y)),
pt_buf.depth_location
)
blf.position(font_id, pt_buf.x + 15, pt_buf.y - 15, 0)
blf.size(font_id, font_size, context.user_preferences.system.dpi)
blf.draw(font_id,
@ -135,17 +140,19 @@ def draw_callback_px(self, context):
bgl.glPointSize(4.0)
bgl.glBegin(bgl.GL_POINTS)
for i in pt_buf.list_m_loc_3d:
loc_0 = location_3d_to_region_2d(context.region, context.space_data.region_3d, i)
loc_0 = location_3d_to_region_2d(
context.region, context.space_data.region_3d, i
)
bgl.glVertex2f(loc_0[0], loc_0[1])
bgl.glEnd()
bgl.glDisable(bgl.GL_BLEND)
# text next to the mouse
m_loc_3d = region_2d_to_location_3d(
context.region,
context.space_data.region_3d, Vector((pt_buf.x, pt_buf.y)),
pt_buf.depth_location
)
context.region,
context.space_data.region_3d, Vector((pt_buf.x, pt_buf.y)),
pt_buf.depth_location
)
vec0 = pt_buf.list_m_loc_3d[-1] - m_loc_3d
blf.position(font_id, pt_buf.x + 15, pt_buf.y + 15, 0)
blf.size(font_id, font_size, context.user_preferences.system.dpi)
@ -234,9 +241,9 @@ def draw_callback_px(self, context):
pass
else:
loc_4 = location_3d_to_region_2d(
context.region, context.space_data.region_3d,
pt_buf.list_m_loc_3d[h]
)
context.region, context.space_data.region_3d,
pt_buf.list_m_loc_3d[h]
)
bgl.glColor4f(0.0, 1.0, 0.525, alpha)
blf.position(font_id, loc_4[0] + 10, loc_4[1] + 10, 0)
blf.size(font_id, font_size, context.user_preferences.system.dpi)
@ -252,41 +259,41 @@ def draw_callback_px(self, context):
class pen_tool_properties(PropertyGroup):
a = FloatProperty(
name="Alpha",
description="Set Font Alpha",
default=1.0,
min=0.1, max=1.0,
step=10,
precision=1
)
name="Alpha",
description="Set Font Alpha",
default=1.0,
min=0.1, max=1.0,
step=10,
precision=1
)
fs = IntProperty(
name="Size",
description="Set Font Size",
default=14,
min=12, max=40,
step=1
)
name="Size",
description="Set Font Size",
default=14,
min=12, max=40,
step=1
)
b0 = BoolProperty(
name="Angles",
description="Display All Angles on Drawn Edges",
default=False
)
name="Angles",
description="Display All Angles on Drawn Edges",
default=False
)
b1 = BoolProperty(
name="Edge Length",
description="Display All Lenghts of Drawn Edges",
default=False
)
name="Edge Length",
description="Display All Lenghts of Drawn Edges",
default=False
)
b2 = BoolProperty(
name="Mouse Location 3D",
description="Display the location coordinates of the mouse cursor",
default=False
)
name="Mouse Location 3D",
description="Display the location coordinates of the mouse cursor",
default=False
)
restore_view = BoolProperty(
name="Restore View",
description="After the tool has finished, is the Viewport restored\n"
"to it's previous state",
default=True
)
name="Restore View",
description="After the tool has finished, is the Viewport restored\n"
"to it's previous state",
default=True
)
class pt_buf():
@ -314,25 +321,27 @@ class pen_tool_panel(Panel):
def draw(self, context):
layout = self.layout
pen_tool_props = context.scene.pen_tool_props
if pt_buf.sws == "on":
layout.active = False
layout.label("Pen Tool Active")
layout.label(text="Pen Tool Active", icon="INFO")
else:
layout.label("Font:")
col = layout.column(align=True)
col.label("Font:")
col.prop(pen_tool_props, "fs", text="Size", slider=True)
col.prop(pen_tool_props, "a", text="Alpha", slider=True)
row = layout.split(0.50, align=True)
row.prop(context.scene.pen_tool_props, "fs", text="Size", slider=True)
row.prop(context.scene.pen_tool_props, "a", text="Alpha", slider=True)
col = layout.column(align=True)
col.label("Settings:")
col.prop(pen_tool_props, "b0", text="Angles", toggle=True)
col.prop(pen_tool_props, "b1", text="Edge Length", toggle=True)
col.prop(pen_tool_props, "b2", text="Mouse Location 3D", toggle=True)
col.prop(pen_tool_props, "restore_view", text="Restore View", toggle=True)
layout.prop(context.scene.pen_tool_props, "b0", text="Angles")
layout.prop(context.scene.pen_tool_props, "b1", text="Edge Length")
layout.prop(context.scene.pen_tool_props, "b2", text="Mouse Location 3D")
layout.prop(context.scene.pen_tool_props, "restore_view", text="Restore View")
row1 = layout.split(0.80, align=True)
row1.operator("pen_tool.operator", text="Draw")
row1.operator("mesh.extra_tools_help",
split = layout.split(0.80, align=True)
split.operator("pen_tool.operator", text="Draw")
split.operator("mesh.extra_tools_help",
icon="LAYER_USED").help_ids = "mesh_pen_tool"
@ -343,10 +352,10 @@ class pen_tool_operator(Operator):
bl_options = {"REGISTER", "UNDO", "INTERNAL"}
text_location = IntProperty(
name="",
default=0,
options={'HIDDEN'}
)
name="",
default=0,
options={'HIDDEN'}
)
@classmethod
def poll(cls, context):
@ -498,7 +507,8 @@ class pen_tool_operator(Operator):
self.text_location = region.width - 100
if overlap:
for region in context.area.regions:
if region.type == 'TOOL_PROPS':
# The Properties Region on the right is of UI type
if region.type == "UI":
self.text_location = self.text_location - region.width
if pt_buf.sws == 'on':
@ -513,20 +523,22 @@ class pen_tool_operator(Operator):
pt_buf.sws = 'on'
return {'RUNNING_MODAL'}
else:
self.report({'WARNING'}, "Pen Tool: View3D not found, operation cancelled")
self.report({'WARNING'}, "Pen Tool: Operation Cancelled. View3D not found")
return {'CANCELLED'}
class_list = [pen_tool_panel,
pen_tool_operator,
pen_tool_properties
]
class_list = (
pen_tool_panel,
pen_tool_operator,
pen_tool_properties
)
KEYMAPS = (
# First, keymap identifiers (last bool is True for modal km).
(("3D View", "VIEW_3D", "WINDOW", False), (
# Then a tuple of keymap items, defined by a dict of kwargs for the km new func, and a tuple of tuples (name, val)
# Then a tuple of keymap items, defined by a dict of kwargs
# for the km new func, and a tuple of tuples (name, val)
# for ops properties, if needing non-default values.
({"idname": pen_tool_operator.bl_idname, "type": 'D', "value": 'PRESS', "ctrl": True},
()),

View File

@ -15,32 +15,34 @@
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
#
# ##### END GPL LICENSE BLOCK #####
# menu & updates by meta-androcto #
# contributed to by Macouno, dustractor, liero, CoDEmanX, meta-androcto #
# contributed to by :
# Macouno, dustractor, liero, lijenstina, #
# CoDEmanX, Dolf Veenvliet, meta-androcto #
bl_info = {
"name": "Select Tools",
"author": "Multiple Authors",
"version": (0, 3),
"version": (0, 3, 1),
"blender": (2, 64, 0),
"location": "Editmode Select Menu/Toolshelf Tools Tab",
"description": "Adds More vert/face/edge select modes.",
"warning": "",
"wiki_url": "",
"tracker_url": "",
"category": "Mesh"
}
if "bpy" in locals():
import imp
imp.reload(mesh_select_by_direction)
imp.reload(mesh_select_by_edge_length)
imp.reload(mesh_select_by_pi)
imp.reload(mesh_select_by_type)
imp.reload(mesh_select_connected_faces)
imp.reload(mesh_index_select)
imp.reload(mesh_selection_topokit)
imp.reload(mesh_info_select)
import importlib
importlib.reload(mesh_select_by_direction)
importlib.reload(mesh_select_by_edge_length)
importlib.reload(mesh_select_by_pi)
importlib.reload(mesh_select_by_type)
importlib.reload(mesh_select_connected_faces)
importlib.reload(mesh_index_select)
importlib.reload(mesh_selection_topokit)
importlib.reload(mesh_info_select)
else:
from . import mesh_select_by_direction
from . import mesh_select_by_edge_length

View File

@ -22,7 +22,7 @@ from bpy.props import (
class SelVertEdgeFace(Operator):
bl_idname = "mesh.select_vert_edge_face_index"
bl_label = "Select mesh index"
bl_description = "Select Vertices, Edges, Faces by index"
bl_description = "Select Vertices, Edges, Faces by their indices"
bl_options = {"REGISTER", "UNDO"}
select_type = EnumProperty(
@ -59,7 +59,6 @@ class SelVertEdgeFace(Operator):
description="Start from no previous selection\n"
"If unchecked the previous selection is kept"
)
delta_text = {'VERT': "Use Cursor",
'EDGE': "Use Edges' Length",
'FACE': "Use Faces' Area"}

View File

@ -17,12 +17,58 @@
# ##### END GPL LICENSE BLOCK #####
# By CoDEmanX
# updated by lijenstina
import bpy
from bpy.types import (
Panel,
Operator,
)
import bmesh
from bpy.types import Panel
import time
# Define Globals
STORE_COUNT = (0, 0, 0) # Store the previous count
TIMER_STORE = 1 # Store the time.time floats
def check_the_obj_polycount(context, delay=0.0):
global STORE_COUNT
global TIMER_STORE
info_str = ""
tris = quads = ngons = 0
try:
# it's weak sauce but this will in certain cases run many times a second
if TIMER_STORE == 1 or delay == 0 or time.time() > TIMER_STORE + delay:
ob = context.active_object
if ob.mode == 'EDIT':
me = ob.data
bm = bmesh.from_edit_mesh(me)
for f in bm.faces:
v = len(f.verts)
if v == 3:
tris += 1
elif v == 4:
quads += 1
else:
ngons += 1
bmesh.update_edit_mesh(me)
else:
for p in ob.data.polygons:
count = p.loop_total
if count == 3:
tris += 1
elif count == 4:
quads += 1
else:
ngons += 1
STORE_COUNT = (ngons, quads, tris)
info_str = " Ngons: %i Quads: %i Tris: %i" % (ngons, quads, tris)
TIMER_STORE = time.time()
else:
info_str = " Ngons: %i Quads: %i Tris: %i" % STORE_COUNT
except:
info_str = " Polygon info could not be retrieved"
return info_str
class DATA_PT_info_panel(Panel):
@ -41,28 +87,20 @@ class DATA_PT_info_panel(Panel):
def draw(self, context):
layout = self.layout
ob = context.active_object
mesh_extra_tools = context.scene.mesh_extra_tools
check_used = mesh_extra_tools.mesh_info_show
check_delay = mesh_extra_tools.mesh_info_delay
info_str = ""
tris = quads = ngons = 0
for p in ob.data.polygons:
count = p.loop_total
if count == 3:
tris += 1
elif count == 4:
quads += 1
else:
ngons += 1
box = layout.box()
col = box.column()
split = col.split(percentage=0.6 if check_used else 0.75, align=True)
split.prop(mesh_extra_tools, "mesh_info_show", toggle=True)
split.prop(mesh_extra_tools, "mesh_info_delay")
info_str = " Ngons: %i Quads: %i Tris: %i" % (ngons, quads, tris)
col = layout.column()
split = col.split(0.9)
split.label(info_str, icon='MESH_DATA')
split.operator("mesh.refresh_info_panel", text="", icon="FILE_REFRESH")
if check_used:
info_str = check_the_obj_polycount(context, check_delay)
col.label(info_str, icon='MESH_DATA')
col = layout.column()
col.label("Select faces by type:")
@ -71,38 +109,3 @@ class DATA_PT_info_panel(Panel):
row.operator("data.facetype_select", text="Ngons").face_type = "5"
row.operator("data.facetype_select", text="Quads").face_type = "4"
row.operator("data.facetype_select", text="Tris").face_type = "3"
class MESH_OT_refresh_info_panel(Operator):
bl_idname = "mesh.refresh_info_panel"
bl_label = "Refresh"
bl_description = ("Refresh the info panel by switching to Object mode and back\n"
"Limitation: the information doesn't account modifiers\n"
"Be careful with usage if you need the Redo History in Edit Mode")
bl_options = {"REGISTER", "INTERNAL"}
@classmethod
def poll(self, context):
return (context.active_object is not None and
context.active_object.type == 'MESH')
def invoke(self, context, event):
return self.execute(context)
def execute(self, context):
try:
mode = bpy.context.active_object.mode
# switch to Object mode and restore selection
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.mode_set(mode=mode)
return {'FINISHED'}
except:
import traceback
traceback.print_exc()
self.report({'WARNING'},
"The refresh could not be performed (Check the console for more info)")
return {'CANCELLED'}

View File

@ -1,43 +1,31 @@
# Copyright (C) 2011, Dolf Veenvliet
# Extrude a selection from a mesh multiple times
# ***** BEGIN GPL LICENSE BLOCK *****
# ##### 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 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.
#
# 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.
#
# 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 LICENCE BLOCK *****
'''
bl_info = {
"name": "Select by direction",
"author": "Dolf Veenvliet",
"version": (1,),
"blender": (2, 56, 0),
"location": "View3D > Select",
"description": "Select all items whose normals face a certain direction",
"warning": "",
"wiki_url": "",
"tracker_url": "",
"category": "Mesh"}
# ##### 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
@ -175,39 +163,39 @@ class Select_init(Operator):
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
)
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'
)
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
)
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'
)
items=spaces,
name="Space",
description="The space to interpret the directions in",
default='LOC'
)
@classmethod
def poll(cls, context):

View File

@ -1,47 +1,33 @@
# mesh_select_by_edge_length.py Copyright (C) 2011, Dolf Veenvliet
# Extrude a selection from a mesh multiple times
# ***** BEGIN GPL LICENSE BLOCK *****
# ##### 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 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.
# 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.
# 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 LICENCE BLOCK *****
'''
bl_info = {
"name": "Select by edge length",
"author": "Dolf Veenvliet",
"version": (1,),
"blender": (2, 56, 0),
"location": "View3D > Select",
"description": "Select all items whose scale/length/surface matches a certain edge length",
"warning": "",
"wiki_url": "",
"tracker_url": "",
"category": "Mesh"}
# ##### END GPL LICENSE BLOCK #####
"""
Usage:
Launch from from "Select -> By edge length"
Launch from from "Select -> By edge length"
Select all items whose scale/length/surface matches a certain edge length
Additional links:
Author Site: http://www.macouno.com
e-mail: dolf {at} macouno {dot} com
"""
'''
import bpy
from bpy.props import (
@ -198,43 +184,43 @@ class Select_init(bpy.types.Operator):
bl_options = {'REGISTER', 'UNDO'}
edgeLength = FloatProperty(
name="Edge length",
description="The comparison scale in Blender units",
default=1.0,
min=0.0, max=1000.0,
soft_min=0.0, soft_max=100.0,
step=100,
precision=2
)
name="Edge length",
description="The comparison scale in Blender units",
default=1.0,
min=0.0, max=1000.0,
soft_min=0.0, soft_max=100.0,
step=100,
precision=2
)
# Changed to Enum as two separate Booleans didn't make much sense
sizes = (('SMALL', 'Smaller', "Select items smaller or equal the size setting"),
('BIG', 'Bigger', "Select items bigger or equal to the size setting"),
('EQUAL', 'Equal', "Select edges equal to the size setting"))
edgeSize = EnumProperty(
items=sizes,
name="Edge comparison",
description="Choose the relation to set edge lenght",
default='EQUAL'
)
items=sizes,
name="Edge comparison",
description="Choose the relation to set edge lenght",
default='EQUAL'
)
extend = BoolProperty(
name="Extend",
description="Extend the current selection",
default=False
)
name="Extend",
description="Extend the current selection",
default=False
)
start_new = BoolProperty(
name="Fresh Start",
default=False,
description="Start from no previous selection"
)
name="Fresh Start",
default=False,
description="Start from no previous selection"
)
# The spaces we use
spaces = (('LOC', 'Local', "Use Local space"),
('GLO', 'Global', "Use Global Space"))
space = EnumProperty(
items=spaces,
name="Space",
description="The space to interpret the directions in",
default='LOC'
)
items=spaces,
name="Space",
description="The space to interpret the directions in",
default='LOC'
)
@classmethod
def poll(cls, context):

View File

@ -1,45 +1,31 @@
# mesh_select_by_pi.py Copyright (C) 2011, Dolf Veenvliet
# Extrude a selection from a mesh multiple times
# ***** BEGIN GPL LICENSE BLOCK *****
# ##### 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 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.
# 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.
# 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 LICENCE BLOCK *****
'''
bl_info = {
"name": "Select by pi",
"author": "Dolf Veenvliet",
"version": 1,
"blender": (2, 56, 0),
"location": "View3D > Select",
"description": "Select fake random based on pi",
"warning": "",
"wiki_url": "",
"tracker_url": "",
"category": "Mesh"}
# ##### END GPL LICENSE BLOCK #####
"""
Usage:
Select fake random based on pi
Additional links:
Author Site: http://www.macouno.com
e-mail: dolf {at} macouno {dot} com
"""
'''
import bpy
from bpy.types import Operator
@ -179,20 +165,20 @@ class Select_init(Operator):
bl_options = {'REGISTER', 'UNDO'}
e = BoolProperty(
name="Use e",
description="Use e as the base of selection instead of pi",
default=False
)
name="Use e",
description="Use e as the base of selection instead of pi",
default=False
)
invert = BoolProperty(
name="Invert",
description="Invert the selection result",
default=False
)
name="Invert",
description="Invert the selection result",
default=False
)
extend = BoolProperty(
name="Extend",
description="Extend the current selection",
default=False
)
name="Extend",
description="Extend the current selection",
default=False
)
start_new = BoolProperty(
name="Fresh Start",
default=False,

View File

@ -33,17 +33,17 @@ class DATA_OP_facetype_select(Operator):
bl_options = {'REGISTER', 'UNDO'}
face_type = EnumProperty(
name="Select faces:",
items=(("3", "Triangles", "Faces made up of 3 vertices"),
("4", "Quads", "Faces made up of 4 vertices"),
("5", "Ngons", "Faces made up of 5 and more vertices")),
default="5"
)
name="Select faces:",
items=(("3", "Triangles", "Faces made up of 3 vertices"),
("4", "Quads", "Faces made up of 4 vertices"),
("5", "Ngons", "Faces made up of 5 and more vertices")),
default="5"
)
extend = BoolProperty(
name="Extend",
description="Extend Selection",
default=False
)
name="Extend",
description="Extend Selection",
default=False
)
@classmethod
def poll(cls, context):
@ -68,11 +68,8 @@ class DATA_OP_facetype_select(Operator):
return {'FINISHED'}
except Exception as e:
print("\n[Select by face type]\nERROR:\n")
import traceback
traceback.printexc()
self.report('WARNING', "Face selection could not be performed (Check the console for more info)")
print("\n[Select by face type]\nOperator: data.facetype_select\nERROR: %s\n" % e)
self.report({'WARNING'},
"Face selection could not be performed (Check the console for more info)")
return {'CANCELLED'}

View File

@ -1,36 +1,23 @@
# Copyright (C) 2011, Dolf Veenvliet
# Extrude a selection from a mesh multiple times
# ***** BEGIN GPL LICENSE BLOCK *****
# ##### 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 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.
# 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.
# 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 LICENCE BLOCK *****
'''
bl_info = {
"name": "Select connected faces",
"author": "Dolf Veenvliet",
"version": (1,),
"blender": (2, 56, 0),
"location": "View3D > Select",
"description": "Select all faces connected to the current selection",
"warning": "",
"wiki_url": "",
"tracker_url": "",
"category": "Mesh"}
# ##### END GPL LICENSE BLOCK #####
"""
Usage:
@ -40,7 +27,7 @@ Additional links:
Author Site: http://www.macouno.com
e-mail: dolf {at} macouno {dot} com
"""
'''
import bpy
from bpy.types import Operator
@ -121,16 +108,17 @@ class Select_init(Operator):
# Iterations
iterations = IntProperty(
name="Iterations",
default=1,
min=0, max=300,
soft_min=0, soft_max=100
)
name="Iterations",
description="Run the selection the given number of times",
default=1,
min=0, max=300,
soft_min=0, soft_max=100
)
extend = BoolProperty(
name="Extend",
description="Extend the current selection",
default=False
)
name="Extend",
description="Extend the current selection",
default=False
)
@classmethod
def poll(cls, context):

View File

@ -21,11 +21,10 @@ bl_info = {
"author": "dustractor",
"version": (2, 0),
"blender": (2, 60, 0),
"location": "edit mesh vertices/edges/faces menus",
"location": "Edit mesh > Vertices/ Edges/ Faces menus",
"description": "",
"warning": "",
"wiki_url": "",
"tracker_url": "",
"category": "Mesh"}
@ -88,13 +87,15 @@ class MESH_OT_vneighbors_edgewise(meshpoller, Operator):
prev_state = None
if not prev_state:
selected_vert_indices = filter(lambda _: mesh.vertices[_].select,
range(len(mesh.vertices)))
selected_vert_indices = filter(
lambda _: mesh.vertices[_].select,
range(len(mesh.vertices))
)
else:
selected_vert_indices = filter(
lambda _: mesh.vertices[_].select and not prev_state[_],
range(len(mesh.vertices))
)
lambda _: mesh.vertices[_].select and not prev_state[_],
range(len(mesh.vertices))
)
for v in selected_vert_indices:
for neighbor_index in vert_to_vert_map[v]:
@ -242,7 +243,8 @@ class MESH_OT_eneighbors_shared_v(meshpoller, Operator):
state_mask = bytearray(len(mesh.edges))
for e in mesh.edges:
state_mask[e.index] = mesh.vertices[e.vertices[0]].select ^ mesh.vertices[e.vertices[1]].select
state_mask[e.index] = \
mesh.vertices[e.vertices[0]].select ^ mesh.vertices[e.vertices[1]].select
mesh.edges.foreach_set('select', state_mask)
bpy.ops.object.mode_set(mode="EDIT")
@ -279,10 +281,12 @@ class MESH_OT_eneighbors_shared_f(meshpoller, Operator):
else:
edge_key_to_index = {k: i for i, k in enumerate(mesh.edge_keys)}
edge_to_edges_dict = {i: set() for i in range(len(mesh.edges))}
for f in mesh.polygons:
fed = [edge_key_to_index[k] for k in f.edge_keys]
for k in f.edge_keys:
edge_to_edges_dict[edge_key_to_index[k]].update(fed)
obj.tkkey = meshkey
state_mask, esel = (bytearray(meshkey[1]), bytearray(meshkey[1]))
mesh.edges.foreach_get('select', esel)

View File

@ -1,258 +0,0 @@
# ##### 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; version 2
# of the License.
#
# 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 #####
bl_info = {
"name": "Mesh to wall",
"author": "luxuy_BlenderCN",
"version": (0.8),
"blender": (2, 71, 0),
"location": "View3D > EditMode > Mesh",
"description": "Make wall from single mesh lines.",
"url": "https://luxuy.github.io/BlenderAddons/Mesh-to-wall/Mesh_to_wall.html",
"category": "Mesh"}
import bpy
import bmesh
from bpy.types import Operator
from bpy.props import FloatProperty
from math import sin, radians
from mathutils import Vector, Quaternion
def link_verts(bm, ver_index):
linked_verts = []
bm.verts.ensure_lookup_table()
v = bm.verts[ver_index]
for e in v.link_edges:
linked_verts.append(e.verts[1].index)
linked_verts.append(e.verts[0].index)
linked_verts = list(set(linked_verts) - set([ver_index]))
return linked_verts
def qq_sort(bm, ver_index, vm, wm):
verts = link_verts(bm, ver_index)
pt0 = bm.verts[ver_index].co
def ang_2d_sort(x):
pt1 = bm.verts[x].co
vec = vm * wm * pt1 - vm * wm * pt0
vec = Vector(vec[0:2])
ang = vec.angle_signed(Vector((1, 0)))
if ang < 0:
return ang + 3.1415926 * 2
else:
return ang
verts = sorted(verts, key=ang_2d_sort)
return verts
def turn_left(bm, v1, v2, vm, wm):
links = [v1, v2]
verts = qq_sort(bm, links[-1], vm, wm)
v = verts.index(links[-2])
if v == 0:
v_nxt = verts[-1]
else:
v_nxt = verts[v - 1]
links.append(v_nxt)
while not(links[-1] == links[1] and links[-2] == links[0]):
verts = qq_sort(bm, links[-1], vm, wm)
v = verts.index(links[-2])
if v == 0:
v_nxt = verts[-1]
else:
v_nxt = verts[v - 1]
links.append(v_nxt)
links.pop()
return links
def lp_left(bm, lp, wid, vm, wm):
size = len(lp)
up = wm.inverted() * vm.inverted() * Vector((0, 0, 1))
lp_off = []
faces = []
for i in range(size - 1):
if i == 0:
pt = bm.verts[lp[i]].co
pre = bm.verts[lp[-2]].co
nxt = bm.verts[lp[1]].co
pre_ind = lp[size - 2]
nxt_ind = lp[1]
else:
bm.verts.ensure_lookup_table()
pt = bm.verts[lp[i]].co
pre = bm.verts[lp[i - 1]].co
nxt = bm.verts[lp[i + 1]].co
pre_ind = lp[i - 1]
nxt_ind = lp[i + 1]
vec1 = pt - pre
vec2 = pt - nxt
mid = vec1.normalized() + vec2.normalized()
if mid.length < 10e-4:
up2 = Vector((0, 0, 1))
mid = up2.cross(vec1)
else:
xx = mid.cross(vec1).dot(up)
if xx > 0:
mid.negate()
mid.normalize()
if pre_ind == nxt_ind:
mid = (pt - pre).normalized()
q_a = Quaternion((0.0, 0.0, 1.0), radians(90.0))
q_b = Quaternion((0.0, 0.0, 1.0), radians(-180.0))
mid.rotate(q_a)
pt1 = pt + mid * wid
mid.rotate(q_b)
pt2 = pt + mid * wid
new_vert_1 = bm.verts.new(pt1)
new_vert_2 = bm.verts.new(pt2)
lp_off.append([new_vert_1, new_vert_2])
else:
ang = mid.angle(pre - pt)
vec_len = wid / (sin(ang))
pt = pt + mid * vec_len
new_vert = bm.verts.new(pt)
lp_off.append(new_vert)
lp_off.append(lp_off[0])
bm.verts.index_update()
for i in range(len(lp_off) - 1):
bm.verts.ensure_lookup_table()
p1 = bm.verts[lp[i]]
p2 = bm.verts[lp[i + 1]]
p3 = lp_off[i + 1]
p4 = lp_off[i]
if isinstance(p3, list):
faces.append((p1, p2, p3[0], p4))
# faces.append((p3[0],p2,p3[1]))
elif isinstance(p4, list):
faces.append((p1, p2, p3, p4[1]))
else:
faces.append((p1, p2, p3, p4))
return faces
# Operators
class MeshtoWall(Operator):
bl_idname = "bpt.mesh_to_wall"
bl_label = "Edge(s) to Wall"
bl_description = "Top View, Extrude Flat Along Edges"
bl_options = {'REGISTER', 'UNDO'}
wid = FloatProperty(
name='Wall width:',
default=0.1,
min=0.001, max=10
)
def check_vert(self, context):
obj = bpy.context.object
if len(obj.data.vertices) <= 1:
return False
return True
def execute(self, context):
# Note: the remove_doubles called after bmesh creation would make
# blender crash with certain meshes - keep it in mind for the future
bpy.ops.mesh.remove_doubles(threshold=0.003) # <<< Remove doubles is called from here
bpy.ops.object.mode_set(mode='OBJECT')
if self.check_vert(context):
bpy.ops.object.mode_set(mode='EDIT')
ob = bpy.context.object
me = ob.data
bm = bmesh.from_edit_mesh(me)
bpy.ops.mesh.delete(type='ONLY_FACE')
context.tool_settings.mesh_select_mode = (True, True, False)
v3d = context.space_data
rv3d = v3d.region_3d
vm = rv3d.view_matrix
wm = ob.matrix_world
faces = []
sel = []
for v in bm.verts:
sel.append(v.index)
bpy.ops.mesh.select_all(action='DESELECT')
for j in sel:
verts = link_verts(bm, j)
if len(verts) > 1:
for i in verts:
lp = turn_left(bm, j, i, vm, wm)
bpy.ops.mesh.select_all(action='DESELECT')
faces += lp_left(bm, lp, self.wid * 0.5, vm, wm)
lp = [bm.verts[i] for i in lp]
lp = lp[1:]
bpy.ops.mesh.select_all(action='DESELECT')
for f in faces:
try:
bm.faces.new(f)
except:
pass
bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=0.003)
bm = bmesh.update_edit_mesh(ob.data, 1, 1)
else:
bpy.ops.object.mode_set(mode='EDIT')
self.report({'WARNING'}, "None or a single vertex found, cancelling the operation")
return {'CANCELLED'}
return {'FINISHED'}
def register():
bpy.utils.register_module(__name__)
def unregister():
bpy.utils.unregister_module(__name__)
if __name__ == "__main__":
register()

View File

@ -26,7 +26,6 @@ bl_info = {
"location": "Spacebar Menu",
"description": "Chamfer vertex",
"wiki_url": "",
"tracker_url": "",
"category": "Mesh"}
@ -133,9 +132,11 @@ class VertexChamfer(Operator):
# Loop over all the loops of the vert
for l in v.link_loops:
# Split the face
bmesh.utils.face_split(l.face,
l.link_loop_next.vert,
l.link_loop_prev.vert)
bmesh.utils.face_split(
l.face,
l.link_loop_next.vert,
l.link_loop_prev.vert
)
# Remove the vert or displace otherwise
if dissolve:
@ -149,11 +150,11 @@ class VertexChamfer(Operator):
def register():
bpy.utils.register_module(__name__)
bpy.utils.register_class(VertexChamfer)
def unregister():
bpy.utils.unregister_module(__name__)
bpy.utils.unregister_class(VertexChamfer)
if __name__ == "__main__":

View File

@ -3,7 +3,7 @@
bl_info = {
"name": "PKHG faces",
"author": "PKHG",
"version": (0, 0, 5),
"version": (0, 0, 6),
"blender": (2, 7, 1),
"location": "View3D > Tools > PKHG (tab)",
"description": "Faces selected will become added faces of different style",
@ -14,10 +14,7 @@ bl_info = {
import bpy
import bmesh
from bpy.types import (
Operator,
Panel
)
from bpy.types import Operator
from mathutils import Vector
from bpy.props import (
BoolProperty,
@ -35,159 +32,158 @@ class MESH_OT_add_faces_to_object(Operator):
bl_options = {'REGISTER', 'UNDO', 'PRESET'}
reverse_faces = BoolProperty(
name="Reverse Faces",
default=False,
description="Revert the normals of selected faces"
)
name="Reverse Faces",
default=False,
description="Revert the normals of selected faces"
)
name_source_object = StringProperty(
name="Mesh",
description="Choose a Source Mesh",
default="Cube"
)
name="Mesh",
description="Choose a Source Mesh",
default="Cube"
)
remove_start_faces = BoolProperty(
name="Remove Start Faces",
default=True,
description="Make a choice about removal of Original Faces"
)
name="Remove Start Faces",
default=True,
description="Make a choice about removal of Original Faces"
)
base_height = FloatProperty(
name="Base Height",
min=-20,
soft_max=10, max=20,
default=0.2,
description="Set general Base Height"
)
name="Base Height",
min=-20,
soft_max=10, max=20,
default=0.2,
description="Set general Base Height"
)
use_relative_base_height = BoolProperty(
name="rel.base_height",
default=False,
description="Relative or absolute Base Height"
)
name="Relative Base Height",
default=False,
description="Relative or absolute Base Height"
)
second_height = FloatProperty(
name="2nd height", min=-5,
soft_max=5, max=20,
default=0.2,
description="Second height for various shapes"
)
name="2nd height", min=-5,
soft_max=5, max=20,
default=0.2,
description="Second height for various shapes"
)
width = FloatProperty(
name="Width Faces",
min=-20, max=20,
default=0.5,
description="Set general width"
)
name="Width Faces",
min=-20, max=20,
default=0.5,
description="Set general width"
)
repeat_extrude = IntProperty(
name="Repeat",
min=1,
soft_max=5, max=20,
description="For longer base"
)
name="Repeat",
min=1,
soft_max=5, max=20,
description="For longer base"
)
move_inside = FloatProperty(
name="Move Inside",
min=0.0,
max=1.0,
default=0.5,
description="How much move to inside"
)
name="Move Inside",
min=0.0,
max=1.0,
default=0.5,
description="How much move to inside"
)
thickness = FloatProperty(
name="Thickness",
soft_min=0.01, min=0,
soft_max=5.0, max=20.0,
default=0
)
name="Thickness",
soft_min=0.01, min=0,
soft_max=5.0, max=20.0,
default=0
)
depth = FloatProperty(
name="Depth",
min=-5,
soft_max=5.0, max=20.0,
default=0
)
name="Depth",
min=-5,
soft_max=5.0, max=20.0,
default=0
)
collapse_edges = BoolProperty(
name="Make Point",
default=False,
description="Collapse the vertices of edges"
)
name="Make Point",
default=False,
description="Collapse the vertices of edges"
)
spike_base_width = FloatProperty(
name="Spike Base Width",
default=0.4,
min=-4.0,
soft_max=1, max=20,
description="Base width of a spike"
)
name="Spike Base Width",
default=0.4,
min=-4.0,
soft_max=1, max=20,
description="Base width of a spike"
)
base_height_inset = FloatProperty(
name="Base Height Inset",
default=0.0,
min=-5, max=5,
description="To elevate or drop the Base height Inset"
)
name="Base Height Inset",
default=0.0,
min=-5, max=5,
description="To elevate or drop the Base height Inset"
)
top_spike = FloatProperty(
name="Top Spike",
default=1.0,
min=-10.0, max=10.0,
description="The Base Height of a spike"
)
name="Top Spike",
default=1.0,
min=-10.0, max=10.0,
description="The Base Height of a spike"
)
top_extra_height = FloatProperty(
name="Top Extra Height",
default=0.0,
min=-10.0, max=10.0,
description="Add extra height"
)
name="Top Extra Height",
default=0.0,
min=-10.0, max=10.0,
description="Add extra height"
)
step_with_real_spike = BoolProperty(
name="Step with Real Spike",
default=False,
description="In stepped, use a real spike"
)
name="Step with Real Spike",
default=False,
description="In stepped, use a real spike"
)
use_relative = BoolProperty(
name="Use Relative",
default=False,
description="Change size using area, min or max"
)
name="Use Relative",
default=False,
description="Change size using area, min or max"
)
face_types = EnumProperty(
name="Face Types",
description="Different types of Faces",
default="no",
items=[
('no', "Pick an Option", "Choose one of the available options"),
('open_inset', "Open Inset", "Inset without closing faces (holes)"),
('with_base', "With Base", "Base and ..."),
('clsd_vertical', "Closed Vertical", "Closed Vertical"),
('open_vertical', "Open Vertical", "Open Vertical"),
('spiked', "Spiked", "Spike"),
('stepped', "Stepped", "Stepped"),
('boxed', "Boxed", "Boxed"),
('bar', "Bar", "Bar"),
]
)
name="Face Types",
description="Different types of Faces",
default="no",
items=[
('no', "Pick an Option", "Choose one of the available options"),
('open_inset', "Open Inset", "Inset without closing faces (holes)"),
('with_base', "With Base", "Base and ..."),
('clsd_vertical', "Closed Vertical", "Closed Vertical"),
('open_vertical', "Open Vertical", "Open Vertical"),
('spiked', "Spiked", "Spike"),
('stepped', "Stepped", "Stepped"),
('boxed', "Boxed", "Boxed"),
('bar', "Bar", "Bar"),
]
)
strange_boxed_effect = BoolProperty(
name="Strange Effect",
default=False,
description="Do not show one extrusion"
)
name="Strange Effect",
default=False,
description="Do not show one extrusion"
)
use_boundary = BoolProperty(
name="Use Boundary",
default=True
)
name="Use Boundary",
default=True
)
use_even_offset = BoolProperty(
name="Even Offset",
default=True
)
name="Even Offset",
default=True
)
use_relative_offset = BoolProperty(
name="Relative Offset",
default=True
)
name="Relative Offset",
default=True
)
use_edge_rail = BoolProperty(
name="Edge Rail",
default=False
)
name="Edge Rail",
default=False
)
use_outset = BoolProperty(
name="Outset",
default=False
)
name="Outset",
default=False
)
use_select_inset = BoolProperty(
name="Inset",
default=False
)
name="Inset",
default=False
)
use_interpolate = BoolProperty(
name="Interpolate",
default=True
)
name="Interpolate",
default=True
)
@classmethod
def poll(cls, context):
@ -438,21 +434,24 @@ class MESH_OT_add_faces_to_object(Operator):
fac = top_spike
object_matrix = startinfo['obj'].matrix_local
for i in range(len(next_ring_edges_list)):
translate_ONE_ring(self, context, bm=bm,
object_matrix=object_matrix,
ring_edges=next_ring_edges_list[i],
normal=normals[i], distance=fac)
translate_ONE_ring(
self, context, bm=bm,
object_matrix=object_matrix,
ring_edges=next_ring_edges_list[i],
normal=normals[i], distance=fac
)
next_ring_edges_list_2 = extrude_edges(self, context, bm=bm,
edge_l_l=next_ring_edges_list)
top_extra_height = self.top_extra_height
for i in range(len(next_ring_edges_list_2)):
move_corner_vecs_outside(self, context, bm=bm,
edge_list=next_ring_edges_list_2[i],
center=centers[i], normal=normals[i],
base_height_erlier=fac + top_extra_height,
distance=fac)
move_corner_vecs_outside(
self, context, bm=bm,
edge_list=next_ring_edges_list_2[i],
center=centers[i], normal=normals[i],
base_height_erlier=fac + top_extra_height,
distance=fac
)
bpy.ops.mesh.select_mode(type="VERT")
bpy.ops.mesh.select_more()
@ -481,24 +480,30 @@ def find_one_ring(sel_vertices):
class Stepped:
def __init__(self, spike_base_width=0.5, base_height_inset=0.0, top_spike=0.2,
top_relative=False, top_extra_height=0, use_relative_offset=False, with_spike=False):
top_relative=False, top_extra_height=0, use_relative_offset=False,
with_spike=False):
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.inset(use_boundary=True, use_even_offset=True, use_relative_offset=False,
use_edge_rail=False, thickness=spike_base_width, depth=0, use_outset=True,
use_select_inset=False, use_individual=True, use_interpolate=True)
bpy.ops.mesh.inset(use_boundary=True, use_even_offset=True, use_relative_offset=use_relative_offset,
use_edge_rail=False, thickness=top_extra_height, depth=base_height_inset,
use_outset=True, use_select_inset=False, use_individual=True, use_interpolate=True)
bpy.ops.mesh.inset(use_boundary=True, use_even_offset=True, use_relative_offset=use_relative_offset,
use_edge_rail=False, thickness=spike_base_width, depth=0, use_outset=True,
use_select_inset=False, use_individual=True, use_interpolate=True)
bpy.ops.mesh.inset(use_boundary=True, use_even_offset=True, use_relative_offset=False,
use_edge_rail=False, thickness=0, depth=top_spike, use_outset=True,
use_select_inset=False, use_individual=True, use_interpolate=True)
bpy.ops.mesh.inset(
use_boundary=True, use_even_offset=True, use_relative_offset=False,
use_edge_rail=False, thickness=spike_base_width, depth=0, use_outset=True,
use_select_inset=False, use_individual=True, use_interpolate=True
)
bpy.ops.mesh.inset(
use_boundary=True, use_even_offset=True, use_relative_offset=use_relative_offset,
use_edge_rail=False, thickness=top_extra_height, depth=base_height_inset,
use_outset=True, use_select_inset=False, use_individual=True, use_interpolate=True
)
bpy.ops.mesh.inset(
use_boundary=True, use_even_offset=True, use_relative_offset=use_relative_offset,
use_edge_rail=False, thickness=spike_base_width, depth=0, use_outset=True,
use_select_inset=False, use_individual=True, use_interpolate=True
)
bpy.ops.mesh.inset(
use_boundary=True, use_even_offset=True, use_relative_offset=False,
use_edge_rail=False, thickness=0, depth=top_spike, use_outset=True,
use_select_inset=False, use_individual=True, use_interpolate=True
)
if with_spike:
bpy.ops.mesh.merge(type='COLLAPSE')
@ -510,13 +515,16 @@ class Spiked:
obj = bpy.context.active_object
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.inset(use_boundary=True, use_even_offset=True, use_relative_offset=False,
use_edge_rail=False, thickness=spike_base_width, depth=base_height_inset,
use_outset=True, use_select_inset=False, use_individual=True, use_interpolate=True)
bpy.ops.mesh.inset(use_boundary=True, use_even_offset=True, use_relative_offset=top_relative,
use_edge_rail=False, thickness=0, depth=top_spike, use_outset=True,
use_select_inset=False, use_individual=True, use_interpolate=True)
bpy.ops.mesh.inset(
use_boundary=True, use_even_offset=True, use_relative_offset=False,
use_edge_rail=False, thickness=spike_base_width, depth=base_height_inset,
use_outset=True, use_select_inset=False, use_individual=True, use_interpolate=True
)
bpy.ops.mesh.inset(
use_boundary=True, use_even_offset=True, use_relative_offset=top_relative,
use_edge_rail=False, thickness=0, depth=top_spike, use_outset=True,
use_select_inset=False, use_individual=True, use_interpolate=True
)
bm = bmesh.from_edit_mesh(obj.data)
bpy.ops.mesh.merge(type='COLLAPSE')
@ -596,50 +604,63 @@ class StripFaces:
use_select_inset=False, use_individual=True, use_interpolate=True):
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.inset(use_boundary=use_boundary, use_even_offset=True, use_relative_offset=False,
use_edge_rail=True, thickness=thickness, depth=depth, use_outset=use_outset,
use_select_inset=use_select_inset, use_individual=use_individual,
use_interpolate=use_interpolate)
bpy.ops.mesh.inset(
use_boundary=use_boundary, use_even_offset=True, use_relative_offset=False,
use_edge_rail=True, thickness=thickness, depth=depth, use_outset=use_outset,
use_select_inset=use_select_inset, use_individual=use_individual,
use_interpolate=use_interpolate
)
bpy.ops.object.mode_set(mode='OBJECT')
# PKHG>IMFO only 3 parameters inc execution context supported!!
if False:
bpy.ops.mesh.inset(use_boundary, use_even_offset, use_relative_offset, use_edge_rail,
thickness, depth, use_outset, use_select_inset, use_individual, use_interpolate)
bpy.ops.mesh.inset(
use_boundary, use_even_offset, use_relative_offset, use_edge_rail,
thickness, depth, use_outset, use_select_inset, use_individual,
use_interpolate
)
elif type == 0:
bpy.ops.mesh.inset(use_boundary=True, use_even_offset=True, use_relative_offset=False,
use_edge_rail=True, thickness=thickness, depth=depth, use_outset=False,
use_select_inset=False, use_individual=True, use_interpolate=True)
bpy.ops.mesh.inset(
use_boundary=True, use_even_offset=True, use_relative_offset=False,
use_edge_rail=True, thickness=thickness, depth=depth, use_outset=False,
use_select_inset=False, use_individual=True, use_interpolate=True
)
elif type == 1:
bpy.ops.mesh.inset(use_boundary=True, use_even_offset=True, use_relative_offset=False,
use_edge_rail=True, thickness=thickness, depth=depth, use_outset=False,
use_select_inset=False, use_individual=True, use_interpolate=False)
bpy.ops.mesh.inset(
use_boundary=True, use_even_offset=True, use_relative_offset=False,
use_edge_rail=True, thickness=thickness, depth=depth, use_outset=False,
use_select_inset=False, use_individual=True, use_interpolate=False
)
bpy.ops.mesh.delete(type='FACE')
elif type == 2:
bpy.ops.mesh.inset(use_boundary=True, use_even_offset=False, use_relative_offset=True,
use_edge_rail=True, thickness=thickness, depth=depth, use_outset=False,
use_select_inset=False, use_individual=True, use_interpolate=False)
bpy.ops.mesh.inset(
use_boundary=True, use_even_offset=False, use_relative_offset=True,
use_edge_rail=True, thickness=thickness, depth=depth, use_outset=False,
use_select_inset=False, use_individual=True, use_interpolate=False
)
bpy.ops.mesh.delete(type='FACE')
elif type == 3:
bpy.ops.mesh.inset(use_boundary=True, use_even_offset=False, use_relative_offset=True,
use_edge_rail=True, thickness=depth, depth=thickness, use_outset=False,
use_select_inset=False, use_individual=True, use_interpolate=True)
bpy.ops.mesh.inset(
use_boundary=True, use_even_offset=False, use_relative_offset=True,
use_edge_rail=True, thickness=depth, depth=thickness, use_outset=False,
use_select_inset=False, use_individual=True, use_interpolate=True
)
bpy.ops.mesh.delete(type='FACE')
elif type == 4:
bpy.ops.mesh.inset(use_boundary=True, use_even_offset=False, use_relative_offset=True,
use_edge_rail=True, thickness=thickness, depth=depth, use_outset=True,
use_select_inset=False, use_individual=True, use_interpolate=True)
bpy.ops.mesh.inset(use_boundary=True, use_even_offset=False, use_relative_offset=True,
use_edge_rail=True, thickness=thickness, depth=depth, use_outset=True,
use_select_inset=False, use_individual=True, use_interpolate=True)
bpy.ops.mesh.inset(
use_boundary=True, use_even_offset=False, use_relative_offset=True,
use_edge_rail=True, thickness=thickness, depth=depth, use_outset=True,
use_select_inset=False, use_individual=True, use_interpolate=True
)
bpy.ops.mesh.inset(
use_boundary=True, use_even_offset=False, use_relative_offset=True,
use_edge_rail=True, thickness=thickness, depth=depth, use_outset=True,
use_select_inset=False, use_individual=True, use_interpolate=True
)
bpy.ops.mesh.delete(type='FACE')
bpy.ops.object.mode_set(mode='OBJECT')
@ -668,16 +689,26 @@ def prepare(self, context, remove_start_faces=True):
centers_copy = [Vector((el[0], el[1], el[2])) for el in centers]
normals = [face.normal for face in selectedpolygons]
normals_copy = [Vector((el[0], el[1], el[2])) for el in normals]
vertindicesofpolgons = [[vert for vert in face.vertices] for face in selectedpolygons]
vertVectorsOfSelectedFaces = [[obj.data.vertices[ind].co for ind in vertIndiceofface]
for vertIndiceofface in vertindicesofpolgons]
vertVectorsOfSelectedFaces_copy = [[Vector((el[0], el[1], el[2])) for el in listofvecs]
for listofvecs in vertVectorsOfSelectedFaces]
vertindicesofpolgons = [
[vert for vert in face.vertices] for face in selectedpolygons
]
vertVectorsOfSelectedFaces = [
[obj.data.vertices[ind].co for ind in vertIndiceofface] for
vertIndiceofface in vertindicesofpolgons
]
vertVectorsOfSelectedFaces_copy = [
[Vector((el[0], el[1], el[2])) for el in listofvecs] for
listofvecs in vertVectorsOfSelectedFaces
]
bpy.ops.object.mode_set(mode='EDIT')
bm = bmesh.from_edit_mesh(obj.data)
selected_bm_faces = [ele for ele in bm.faces if ele.select]
selected_edges_per_face_ind = [[ele.index for ele in face.edges] for face in selected_bm_faces]
selected_edges_per_face_ind = [
[ele.index for ele in face.edges] for face in selected_bm_faces
]
indices = [el.index for el in selectedpolygons]
selected_faces_areas = [bm.faces[:][i] for i in indices]
tmp_area = [el.calc_area() for el in selected_faces_areas]
@ -692,20 +723,27 @@ def prepare(self, context, remove_start_faces=True):
bm.verts.ensure_lookup_table()
bm.faces.ensure_lookup_table()
start_ring_raw = [[bm.verts[ind].index for ind in vertIndiceofface]
for vertIndiceofface in vertindicesofpolgons]
start_ring_raw = [
[bm.verts[ind].index for ind in vertIndiceofface] for
vertIndiceofface in vertindicesofpolgons
]
start_ring = []
for el in start_ring_raw:
start_ring.append(set(el))
bm.edges.ensure_lookup_table()
bm_selected_edges_l_l = [[bm.edges[i] for i in bm_ind_list] for bm_ind_list in selected_edges_per_face_ind]
bm_selected_edges_l_l = [
[bm.edges[i] for i in bm_ind_list] for
bm_ind_list in selected_edges_per_face_ind
]
result = {
'obj': obj, 'centers': centers_copy, 'normals': normals_copy,
'rings': vertVectorsOfSelectedFaces_copy, 'bm': bm,
'areas': tmp_area, 'startBMRingVerts': start_ring,
'base_edges': bm_selected_edges_l_l
}
result = {'obj': obj, 'centers': centers_copy, 'normals': normals_copy,
'rings': vertVectorsOfSelectedFaces_copy, 'bm': bm,
'areas': tmp_area, 'startBMRingVerts': start_ring,
'base_edges': bm_selected_edges_l_l}
return result
@ -769,8 +807,8 @@ def translate_ONE_ring(self, context, bm=None, object_matrix=None, ring_edges=No
return ring_edges
def move_corner_vecs_outside(self, context, bm=None, edge_list=None, center=None, normal=None,
base_height_erlier=0.5, distance=0.5):
def move_corner_vecs_outside(self, context, bm=None, edge_list=None, center=None,
normal=None, base_height_erlier=0.5, distance=0.5):
# move corners (outside meant mostly) dependent on the parameters
tmp = []
for edge in edge_list:

View File

@ -6,10 +6,9 @@ bl_info = {
"version": (1, 3),
"blender": (2, 6, 3),
"location": "Object > Transform > Random Vertices",
"description": "Randomize selected components of active object.",
"description": "Randomize selected components of active object",
"warning": "",
"wiki_url": "",
"tracker_url": "",
"category": "Mesh"}
@ -45,7 +44,8 @@ def add_object(self, context, valmin, valmax, factor, vgfilter):
if vertice.select:
listver.append(vertice.index)
# If the minimum value is greater than the maximum, it adds a value to the maximum
# If the minimum value is greater than the maximum,
# it adds a value to the maximum
if valmin[0] >= valmax[0]:
valmax[0] = valmin[0] + 1
@ -95,25 +95,25 @@ class MESH_OT_random_vertices(Operator):
bl_options = {'REGISTER', 'UNDO'}
vgfilter = BoolProperty(
name="Vertex Group",
description="Use Vertex Weight defined in the Active Group",
default=False
)
name="Vertex Group",
description="Use Vertex Weight defined in the Active Group",
default=False
)
factor = FloatProperty(
name="Factor",
description="Base Multiplier of the randomization effect",
default=1
)
name="Factor",
description="Base Multiplier of the randomization effect",
default=1
)
valmin = IntVectorProperty(
name="Min XYZ",
description="Define the minimum range of randomization values",
default=(0, 0, 0)
)
name="Min XYZ",
description="Define the minimum range of randomization values",
default=(0, 0, 0)
)
valmax = IntVectorProperty(
name="Max XYZ",
description="Define the maximum range of randomization values",
default=(1, 1, 1)
)
name="Max XYZ",
description="Define the maximum range of randomization values",
default=(1, 1, 1)
)
@classmethod
def poll(cls, context):
@ -122,6 +122,7 @@ class MESH_OT_random_vertices(Operator):
def execute(self, context):
add_object(self, context, self.valmin, self.valmax, self.factor, self.vgfilter)
return {'FINISHED'}

View File

@ -1,22 +1,22 @@
# -*- coding: utf-8 -*-
# ***** BEGIN GPL LICENSE BLOCK *****
# ##### 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 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.
# 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.
# 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 LICENCE BLOCK *****
# ##### END GPL LICENSE BLOCK #####
bl_info = {
"name": "Split Solidify",
@ -27,7 +27,6 @@ bl_info = {
"description": "",
"warning": "",
"wiki_url": "",
"tracker_url": "",
"category": "Mesh"}
import bpy
@ -111,44 +110,44 @@ class MESH_OT_split_solidify(Operator):
bl_options = {"REGISTER", "UNDO"}
distance = FloatProperty(
name="",
description="Distance of the splitted Faces to the original geometry",
default=0.4,
min=-100.0, max=100.0,
step=1,
precision=3
)
name="",
description="Distance of the splitted Faces to the original geometry",
default=0.4,
min=-100.0, max=100.0,
step=1,
precision=3
)
thickness = FloatProperty(
name="",
description="Thickness of the splitted Faces",
default=0.04,
min=-100.0, max=100.0,
step=1,
precision=3
)
name="",
description="Thickness of the splitted Faces",
default=0.04,
min=-100.0, max=100.0,
step=1,
precision=3
)
random_dist = FloatProperty(
name="",
description="Randomization factor of the splitted Faces' location",
default=0.06,
min=-10.0, max=10.0,
step=1,
precision=3
)
name="",
description="Randomization factor of the splitted Faces' location",
default=0.06,
min=-10.0, max=10.0,
step=1,
precision=3
)
loc_random = BoolProperty(
name="Random",
description="Randomize the locations of splitted faces",
default=False
)
name="Random",
description="Randomize the locations of splitted faces",
default=False
)
del_original = BoolProperty(
name="Delete original faces",
default=True
)
name="Delete original faces",
default=True
)
normal_extr = EnumProperty(
items=(('opt0', "Face", "Solidify along Face Normals"),
('opt1', "Vertex", "Solidify along Vertex Normals")),
name="Normal",
default='opt0'
)
items=(('opt0', "Face", "Solidify along Face Normals"),
('opt1', "Vertex", "Solidify along Vertex Normals")),
name="Normal",
default='opt0'
)
def draw(self, context):
layout = self.layout
@ -176,7 +175,8 @@ class MESH_OT_split_solidify(Operator):
list_0 = [f.index for f in self.bm.faces if f.select]
if len(list_0) == 0:
self.report({'WARNING'}, "No suitable selection found. Operation cancelled")
self.report({'WARNING'},
"No suitable selection found. Operation cancelled")
return {'CANCELLED'}

View File

@ -1,63 +1,55 @@
# -*- coding: utf-8 -*-
# ***** BEGIN GPL LICENSE BLOCK *****
# ##### 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 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.
#
# 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.
#
# 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 LICENCE BLOCK *****
# ##### END GPL LICENSE BLOCK #####
# Note: Property group was moved to __init__
# ------ ------
bl_info = {
'name': 'vertex align',
'author': '',
'version': (0, 1, 6),
'blender': (2, 6, 1),
'location': 'View3D > Tool Shelf',
'description': '',
'warning': '',
'wiki_url': '',
'tracker_url': '',
'category': 'Mesh' }
"name": "Vertex Align",
"author": "",
"version": (0, 1, 7),
"blender": (2, 6, 1),
"location": "View3D > Tool Shelf",
"description": "",
"warning": "",
"wiki_url": "",
"category": "Mesh"}
# ------ ------
import bpy
from bpy.props import (
BoolProperty,
EnumProperty,
PointerProperty,
FloatProperty,
BoolVectorProperty,
FloatVectorProperty,
)
from mathutils import Vector
from mathutils.geometry import (
intersect_point_line,
intersect_line_plane,
)
from bpy.types import (
PropertyGroup,
)
from bpy.types import Operator
# ------ Edit Mode Toggle------
# Edit Mode Toggle
def edit_mode_out():
bpy.ops.object.mode_set(mode = 'OBJECT')
bpy.ops.object.mode_set(mode='OBJECT')
def edit_mode_in():
bpy.ops.object.mode_set(mode = 'EDIT')
bpy.ops.object.mode_set(mode='EDIT')
# ------ ------
def get_mesh_data_():
edit_mode_out()
ob_act = bpy.context.active_object
@ -65,160 +57,206 @@ def get_mesh_data_():
edit_mode_in()
return me
def list_clear_(l):
l[:] = []
return l
# -- -- Prpoerty Group-- --
class va_property_group(PropertyGroup):
en0 = EnumProperty( items =( ('vertex', 'Original vertex', ''),
('coordinates', 'Custom coordinates', '')),
name = 'Align to',
default = 'vertex' )
en1 = EnumProperty( items =( ('en1_opt0', 'x', ''),
('en1_opt1', 'y', ''),
('en1_opt2', 'z', '')),
name = 'Axis',
default = 'en1_opt0' )
# ------ ------
class va_buf():
list_v = []
list_f = []
list_0 = []
# ------ operator 0 Store The Vert------
class va_op0_store(bpy.types.Operator):
bl_idname = 'va.op0_store_id'
bl_label = ''
bl_description = "Store single vert as align point"
# Store The Vertex coordinates
class Vertex_align_store(Operator):
bl_idname = "vertex_align.store_id"
bl_label = "Active Vertex"
bl_description = ("Store Selected Vertex coordinates as an align point\n"
"Single Selected Vertex only")
@classmethod
def poll(cls, context):
obj = context.active_object
return (obj and obj.type == 'MESH' and context.mode == 'EDIT_MESH')
def execute(self, context):
me = get_mesh_data_()
list_clear_(va_buf.list_v)
for v in me.vertices:
if v.select:
va_buf.list_v.append(v.index)
bpy.ops.mesh.select_all(action = 'DESELECT')
try:
me = get_mesh_data_()
list_0 = [v.index for v in me.vertices if v.select]
if len(list_0) == 1:
list_clear_(va_buf.list_v)
for v in me.vertices:
if v.select:
va_buf.list_v.append(v.index)
bpy.ops.mesh.select_all(action='DESELECT')
else:
self.report({'WARNING'}, "Please select just One Vertex")
return {'CANCELLED'}
except:
self.report({'WARNING'}, "Storing selection could not be completed")
return {'CANCELLED'}
self.report({'INFO'}, "Selected Vertex coordinates are stored")
return {'FINISHED'}
# ------ operator 1 Append to list------
class va_op1_list(bpy.types.Operator):
bl_idname = 'va.op1_id'
bl_label = ''
bl_description = "test2"
def execute(self, context):
me = get_mesh_data_()
list_clear_(va_buf.list_f)
for f in me.faces:
if f.select:
va_buf.list_f.append(f.index)
bpy.ops.mesh.select_all(action = 'DESELECT')
return {'FINISHED'}
# ------ operator 2 ------ align to original
class va_op2_align(bpy.types.Operator):
bl_idname = 'va.op2_align_id'
bl_label = 'Align to original'
# Align to original
class Vertex_align_original(Operator):
bl_idname = "vertex_align.align_original"
bl_label = "Align to original"
bl_description = "Align selection to stored single vertex coordinates"
bl_options = {'REGISTER', 'UNDO'}
bl_description = "Align selection to stored single vert"
@classmethod
def poll(cls, context):
obj = context.active_object
return (obj and obj.type == 'MESH' and context.mode == 'EDIT_MESH')
def draw(self, context):
layout = self.layout
layout.label('Axis:')
layout.prop(context.scene.va_custom_props, 'en1', expand = True)
layout.label("Axis:")
row = layout.row(align=True)
row.prop(context.scene.mesh_extra_tools, "vert_align_axis",
text="X", index=0, toggle=True)
row.prop(context.scene.mesh_extra_tools, "vert_align_axis",
text="Y", index=1, toggle=True)
row.prop(context.scene.mesh_extra_tools, "vert_align_axis",
text="Z", index=2, toggle=True)
def execute(self, context):
edit_mode_out()
ob_act = context.active_object
me = ob_act.data
cen1 = context.scene.va_custom_props.en1
cen1 = context.scene.mesh_extra_tools.vert_align_axis
list_0 = [v.index for v in me.vertices if v.select]
if len(va_buf.list_v) == 0:
self.report({'INFO'}, 'Original vertex not stored in memory')
self.report({'INFO'},
"Original vertex not stored in memory. Operation Cancelled")
edit_mode_in()
return {'CANCELLED'}
elif len(va_buf.list_v) != 0:
if len(list_0) == 0:
self.report({'INFO'}, 'No vertices selected')
self.report({'INFO'}, "No vertices selected. Operation Cancelled")
edit_mode_in()
return {'CANCELLED'}
elif len(list_0) != 0:
vo = (me.vertices[va_buf.list_v[0]].co).copy()
if cen1 == 'en1_opt0':
if cen1[0] is True:
for i in list_0:
v = (me.vertices[i].co).copy()
me.vertices[i].co = Vector(( vo[0], v[1], v[2] ))
elif cen1 == 'en1_opt1':
me.vertices[i].co = Vector((vo[0], v[1], v[2]))
if cen1[1] is True:
for i in list_0:
v = (me.vertices[i].co).copy()
me.vertices[i].co = Vector(( v[0], vo[1], v[2] ))
elif cen1 == 'en1_opt2':
me.vertices[i].co = Vector((v[0], vo[1], v[2]))
if cen1[2] is True:
for i in list_0:
v = (me.vertices[i].co).copy()
me.vertices[i].co = Vector(( v[0], v[1], vo[2] ))
me.vertices[i].co = Vector((v[0], v[1], vo[2]))
edit_mode_in()
return {'FINISHED'}
# ------ operator 3 ------ align to custom coordinates
class va_op3_coord_list(bpy.types.Operator):
bl_idname = 'va.op3_coord_list_id'
bl_label = ''
# Align to custom coordinates
class Vertex_align_coord_list(Operator):
bl_idname = "vertex_align.coord_list_id"
bl_label = ""
bl_description = "Align to custom coordinates"
@classmethod
def poll(cls, context):
obj = context.active_object
return (obj and obj.type == 'MESH' and context.mode == 'EDIT_MESH')
def execute(self, context):
edit_mode_out()
ob_act = context.active_object
me = ob_act.data
list_clear_(va_buf.list_0)
va_buf.list_0 = [v.index for v in me.vertices if v.select][:]
if len(va_buf.list_0) == 0:
self.report({'INFO'}, 'No vertices selected')
self.report({'INFO'}, "No vertices selected. Operation Cancelled")
edit_mode_in()
return {'CANCELLED'}
elif len(va_buf.list_0) != 0:
bpy.ops.va.op4_id('INVOKE_DEFAULT')
bpy.ops.vertex_align.coord_menu_id('INVOKE_DEFAULT')
edit_mode_in()
return {'FINISHED'}
# ------ operator 4 ------ align to custom coordinates menu
class va_op4_coord_menu(bpy.types.Operator):
bl_idname = 'va.op4_id'
bl_label = 'Align to custom coordinates'
bl_options = {'REGISTER', 'UNDO'}
bl_description = "Align to custom coordinates2"
x = y = z = FloatProperty( name = '',
default = 0.0,
min = -100.0,
max = 100.0,
step = 1,
precision = 3 )
b_x = b_y = b_z = BoolProperty()
# Align to custom coordinates menu
class Vertex_align_coord_menu(Operator):
bl_idname = "vertex_align.coord_menu_id"
bl_label = "Tweak custom coordinates"
bl_description = "Change the custom coordinates for aligning"
bl_options = {'REGISTER', 'UNDO'}
def_axis_coord = FloatVectorProperty(
name="",
description="Enter the values of coordinates",
default=(0.0, 0.0, 0.0),
min=-100.0, max=100.0,
step=1, size=3,
subtype='XYZ',
precision=3
)
use_axis_coord = BoolVectorProperty(
name="Axis",
description="Choose Custom Coordinates axis",
default=(False,) * 3,
size=3,
)
is_not_undo = False
@classmethod
def poll(cls, context):
obj = context.active_object
return (obj and obj.type == 'MESH')
def using_store(self, context):
scene = context.scene
return scene.mesh_extra_tools.vert_align_use_stored
def draw(self, context):
layout = self.layout
if self.using_store(context) and self.is_not_undo:
layout.label("Using Stored Coordinates", icon="INFO")
row = layout.split(0.25)
row.prop(self, 'b_x', text = 'x')
row.prop(self, 'x')
row.prop(self, "use_axis_coord", index=0, text="X")
row.prop(self, "def_axis_coord", index=0)
row = layout.split(0.25)
row.prop(self, 'b_y', text = 'y')
row.prop(self, 'y')
row.prop(self, "use_axis_coord", index=1, text="Y")
row.prop(self, "def_axis_coord", index=1)
row = layout.split(0.25)
row.prop(self, 'b_z', text = 'z')
row.prop(self, 'z')
row.prop(self, "use_axis_coord", index=2, text="Z")
row.prop(self, "def_axis_coord", index=2)
def invoke(self, context, event):
return context.window_manager.invoke_props_dialog(self, width = 200)
self.is_not_undo = True
scene = context.scene
if self.using_store(context):
self.def_axis_coord = scene.mesh_extra_tools.vert_align_store_axis
return context.window_manager.invoke_props_dialog(self, width=200)
def execute(self, context):
self.is_not_undo = False
edit_mode_out()
ob_act = context.active_object
me = ob_act.data
@ -226,71 +264,38 @@ class va_op4_coord_menu(bpy.types.Operator):
for i in va_buf.list_0:
v = (me.vertices[i].co).copy()
tmp = Vector((v[0], v[1], v[2]))
if self.b_x == True:
tmp[0] = self.x
if self.b_y == True:
tmp[1] = self.y
if self.b_z == True:
tmp[2] = self.z
if self.use_axis_coord[0] is True:
tmp[0] = self.def_axis_coord[0]
if self.use_axis_coord[1] is True:
tmp[1] = self.def_axis_coord[1]
if self.use_axis_coord[2] is True:
tmp[2] = self.def_axis_coord[2]
me.vertices[i].co = tmp
edit_mode_in()
return {'FINISHED'}
# ------ operator 7 Help------
class va_op7_help(bpy.types.Operator):
bl_idname = 'va.op7_help_id'
bl_label = ''
bl_description = "Info"
def draw(self, context):
layout = self.layout
layout.label('Help:')
layout.label('To use select whatever you want vertices to be aligned to ')
layout.label('and click button next to store data label. ')
layout.label('Select vertices that you want to align and click Align button. ')
def execute(self, context):
return {'FINISHED'}
def invoke(self, context, event):
return context.window_manager.invoke_popup(self, width = 400)
# ------ operator 8 ------
class va_op8_execute(bpy.types.Operator):
bl_idname = 'va.op8_id'
bl_label = ''
bl_description = "Executs"
def execute(self, context):
bpy.ops.va.op7_id('INVOKE_DEFAULT')
return {'FINISHED'}
# ------ Classes ------
# Register
classes = (
va_property_group,
va_op0_store,
va_op1_list,
va_op2_align,
va_op3_coord_list,
va_op4_coord_menu,
va_op7_help,
va_op8_execute,
Vertex_align_store,
Vertex_align_original,
Vertex_align_coord_list,
Vertex_align_coord_menu,
)
# ------ Register ------
def register():
for c in classes:
bpy.utils.register_class(c)
for cls in classes:
bpy.utils.register_class(cls)
bpy.types.Scene.va_custom_props = PointerProperty(type = va_property_group)
# ------ ------
def unregister():
del bpy.types.Scene.va_custom_props
for cls in classes:
bpy.utils.unregister_class(cls)
for c in classes:
bpy.utils.unregister_class(c)
# ------ ------
if __name__ == "__main__":
register()

View File

@ -11,7 +11,6 @@ bl_info = {
import bpy
import bpy_extras
from bpy.types import (
Menu,
Operator,
@ -60,16 +59,17 @@ class MESH_OT_CallContextMenu(Operator):
return bpy.ops.wm.call_menu(name=MESH_MT_CombinedMenu.bl_idname)
classes = [
classes = (
MESH_MT_CombinedMenu,
MESH_OT_CallContextMenu
]
MESH_OT_CallContextMenu,
)
KEYMAPS = (
# First, keymap identifiers (last bool is True for modal km).
(("3D View", "VIEW_3D", "WINDOW", False), (
# Then a tuple of keymap items, defined by a dict of kwargs for the km new func, and a tuple of tuples (name, val)
# Then a tuple of keymap items, defined by a dict of kwargs
# for the km new func, and a tuple of tuples (name, val)
# for ops properties, if needing non-default values.
({"idname": MESH_OT_CallContextMenu.bl_idname, "type": 'RIGHTMOUSE', "value": 'DOUBLE_CLICK'},
()),