mirror of
https://github.com/blender/blender-addons.git
synced 2025-07-29 12:05:36 +00:00
991 lines
30 KiB
Python
991 lines
30 KiB
Python
# SPDX-License-Identifier: GPL-2.0-or-later
|
|
# Copyright 2009-2010 Michel J. Anders (varkenvarken)
|
|
|
|
import bpy
|
|
from bpy.types import Operator
|
|
from math import (
|
|
atan, asin, cos,
|
|
sin, tan, pi,
|
|
radians,
|
|
)
|
|
from bpy.props import (
|
|
FloatProperty,
|
|
IntProperty,
|
|
BoolProperty,
|
|
StringProperty,
|
|
FloatVectorProperty
|
|
)
|
|
from mathutils import (
|
|
Vector,
|
|
Matrix,
|
|
)
|
|
from bpy_extras import object_utils
|
|
|
|
# A very simple "bridge" tool.
|
|
# Connects two equally long vertex rows with faces.
|
|
# Returns a list of the new faces (list of lists)
|
|
#
|
|
# vertIdx1 ... First vertex list (list of vertex indices)
|
|
# vertIdx2 ... Second vertex list (list of vertex indices)
|
|
# closed ... Creates a loop (first & last are closed)
|
|
# flipped ... Invert the normal of the face(s)
|
|
#
|
|
# Note: You can set vertIdx1 to a single vertex index to create
|
|
# a fan/star of faces
|
|
# Note: If both vertex idx list are the same length they have
|
|
# to have at least 2 vertices
|
|
|
|
def createFaces(vertIdx1, vertIdx2, closed=False, flipped=False):
|
|
faces = []
|
|
|
|
if not vertIdx1 or not vertIdx2:
|
|
return None
|
|
|
|
if len(vertIdx1) < 2 and len(vertIdx2) < 2:
|
|
return None
|
|
|
|
fan = False
|
|
if (len(vertIdx1) != len(vertIdx2)):
|
|
if (len(vertIdx1) == 1 and len(vertIdx2) > 1):
|
|
fan = True
|
|
else:
|
|
return None
|
|
|
|
total = len(vertIdx2)
|
|
|
|
if closed:
|
|
# Bridge the start with the end.
|
|
if flipped:
|
|
face = [
|
|
vertIdx1[0],
|
|
vertIdx2[0],
|
|
vertIdx2[total - 1]]
|
|
if not fan:
|
|
face.append(vertIdx1[total - 1])
|
|
faces.append(face)
|
|
|
|
else:
|
|
face = [vertIdx2[0], vertIdx1[0]]
|
|
if not fan:
|
|
face.append(vertIdx1[total - 1])
|
|
face.append(vertIdx2[total - 1])
|
|
faces.append(face)
|
|
|
|
# Bridge the rest of the faces.
|
|
for num in range(total - 1):
|
|
if flipped:
|
|
if fan:
|
|
face = [vertIdx2[num], vertIdx1[0], vertIdx2[num + 1]]
|
|
else:
|
|
face = [vertIdx2[num], vertIdx1[num],
|
|
vertIdx1[num + 1], vertIdx2[num + 1]]
|
|
faces.append(face)
|
|
else:
|
|
if fan:
|
|
face = [vertIdx1[0], vertIdx2[num], vertIdx2[num + 1]]
|
|
else:
|
|
face = [vertIdx1[num], vertIdx2[num],
|
|
vertIdx2[num + 1], vertIdx1[num + 1]]
|
|
faces.append(face)
|
|
|
|
return faces
|
|
|
|
|
|
# Calculate the vertex coordinates for a single
|
|
# section of a gear tooth.
|
|
# Returns 4 lists of vertex coords (list of tuples):
|
|
# *-*---*---* (1.) verts_inner_base
|
|
# | | | |
|
|
# *-*---*---* (2.) verts_outer_base
|
|
# | | |
|
|
# *---*---* (3.) verts_middle_tooth
|
|
# \ | /
|
|
# *-*-* (4.) verts_tip_tooth
|
|
#
|
|
# a
|
|
# t
|
|
# d
|
|
# radius
|
|
# Ad
|
|
# De
|
|
# base
|
|
# p_angle
|
|
# rack
|
|
# crown
|
|
|
|
def add_tooth(a, t, d, radius, Ad, De, base, p_angle, rack=0, crown=0.0):
|
|
A = [a, a + t / 4, a + t / 2, a + 3 * t / 4]
|
|
C = [cos(i) for i in A]
|
|
S = [sin(i) for i in A]
|
|
|
|
Ra = radius + Ad
|
|
Rd = radius - De
|
|
Rb = Rd - base
|
|
|
|
# Pressure angle calc
|
|
O = Ad * tan(p_angle)
|
|
if Ra != 0:
|
|
p_angle = atan(O / Ra)
|
|
else:
|
|
p_angle = atan(O)
|
|
|
|
if radius < 0:
|
|
p_angle = -p_angle
|
|
|
|
if rack:
|
|
S = [sin(t / 4) * I for I in range(-2, 3)]
|
|
Sp = [0, sin(-t / 4 + p_angle), 0, sin(t / 4 - p_angle)]
|
|
|
|
verts_inner_base = [(Rb, radius * S[I], d) for I in range(4)]
|
|
verts_outer_base = [(Rd, radius * S[I], d) for I in range(4)]
|
|
verts_middle_tooth = [(radius, radius * S[I], d) for I in range(1, 4)]
|
|
verts_tip_tooth = [(Ra, radius * Sp[I], d) for I in range(1, 4)]
|
|
|
|
else:
|
|
Cp = [
|
|
0,
|
|
cos(a + t / 4 + p_angle),
|
|
cos(a + t / 2),
|
|
cos(a + 3 * t / 4 - p_angle)]
|
|
Sp = [0,
|
|
sin(a + t / 4 + p_angle),
|
|
sin(a + t / 2),
|
|
sin(a + 3 * t / 4 - p_angle)]
|
|
|
|
verts_inner_base = [(Rb * C[I], Rb * S[I], d)
|
|
for I in range(4)]
|
|
verts_outer_base = [(Rd * C[I], Rd * S[I], d)
|
|
for I in range(4)]
|
|
verts_middle_tooth = [(radius * C[I], radius * S[I], d + crown / 3)
|
|
for I in range(1, 4)]
|
|
verts_tip_tooth = [(Ra * Cp[I], Ra * Sp[I], d + crown)
|
|
for I in range(1, 4)]
|
|
|
|
return (verts_inner_base, verts_outer_base,
|
|
verts_middle_tooth, verts_tip_tooth)
|
|
|
|
|
|
# EXPERIMENTAL Calculate the vertex coordinates for a single
|
|
# section of a gearspoke.
|
|
# Returns them as a list of tuples
|
|
#
|
|
# a
|
|
# t
|
|
# d
|
|
# radius
|
|
# De
|
|
# base
|
|
# s
|
|
# w
|
|
# l
|
|
# gap
|
|
# width
|
|
#
|
|
# @todo Finish this.
|
|
|
|
def add_spoke(a, t, d, radius, De, base, s, w, l, gap=0, width=19):
|
|
Rd = radius - De
|
|
Rb = Rd - base
|
|
|
|
verts = []
|
|
edgefaces = []
|
|
edgefaces2 = []
|
|
sf = []
|
|
|
|
if not gap:
|
|
for N in range(width, 1, -2):
|
|
edgefaces.append(len(verts))
|
|
ts = t / 4
|
|
tm = a + 2 * ts
|
|
te = asin(w / Rb)
|
|
td = te - ts
|
|
t4 = ts + td * (width - N) / (width - 3.0)
|
|
A = [tm + (i - int(N / 2)) * t4 for i in range(N)]
|
|
C = [cos(i) for i in A]
|
|
S = [sin(i) for i in A]
|
|
|
|
verts.extend((Rb * I, Rb * J, d) for (I, J) in zip(C, S))
|
|
edgefaces2.append(len(verts) - 1)
|
|
|
|
Rb = Rb - s
|
|
|
|
n = 0
|
|
for N in range(width, 3, -2):
|
|
sf.extend([(i + n, i + 1 + n, i + 2 + n, i + N + n)
|
|
for i in range(0, N - 1, 2)])
|
|
sf.extend([(i + 2 + n, i + N + n, i + N + 1 + n, i + N + 2 + n)
|
|
for i in range(0, N - 3, 2)])
|
|
|
|
n = n + N
|
|
|
|
return verts, edgefaces, edgefaces2, sf
|
|
|
|
|
|
# Create gear geometry.
|
|
# Returns:
|
|
# * A list of vertices (list of tuples)
|
|
# * A list of faces (list of lists)
|
|
# * A list (group) of vertices of the tip (list of vertex indices)
|
|
# * A list (group) of vertices of the valley (list of vertex indices)
|
|
#
|
|
# teethNum ... Number of teeth on the gear
|
|
# radius ... Radius of the gear, negative for crown gear
|
|
# Ad ... Addendum, extent of tooth above radius
|
|
# De ... Dedendum, extent of tooth below radius
|
|
# base ... Base, extent of gear below radius
|
|
# p_angle ... Pressure angle. Skewness of tooth tip. (radiant)
|
|
# width ... Width, thickness of gear
|
|
# skew ... Skew of teeth. (radiant)
|
|
# conangle ... Conical angle of gear. (radiant)
|
|
# rack
|
|
# crown ... Inward pointing extend of crown teeth
|
|
#
|
|
# inner radius = radius - (De + base)
|
|
|
|
def add_gear(teethNum, radius, Ad, De, base, p_angle,
|
|
width=1, skew=0, conangle=0, rack=0, crown=0.0):
|
|
|
|
if teethNum < 2:
|
|
return None, None, None, None
|
|
|
|
t = 2 * pi / teethNum
|
|
|
|
if rack:
|
|
teethNum = 1
|
|
|
|
#print(radius, width, conangle)
|
|
if radius != 0:
|
|
scale = (radius - 2 * width * tan(conangle)) / radius
|
|
else:
|
|
scale = radius - 2 * width * tan(conangle)
|
|
|
|
verts = []
|
|
faces = []
|
|
vgroup_top = [] # Vertex group of top/tip? vertices.
|
|
vgroup_valley = [] # Vertex group of valley vertices
|
|
|
|
verts_bridge_prev = []
|
|
for toothCnt in range(teethNum):
|
|
a = toothCnt * t
|
|
|
|
verts_bridge_start = []
|
|
verts_bridge_end = []
|
|
|
|
verts_outside_top = []
|
|
verts_outside_bottom = []
|
|
for (s, d, c, top) \
|
|
in [(0, -width, 1, True), (skew, width, scale, False)]:
|
|
|
|
verts1, verts2, verts3, verts4 = add_tooth(a + s, t, d,
|
|
radius * c, Ad * c, De * c, base * c, p_angle,
|
|
rack, crown)
|
|
|
|
vertsIdx1 = list(range(len(verts), len(verts) + len(verts1)))
|
|
verts.extend(verts1)
|
|
vertsIdx2 = list(range(len(verts), len(verts) + len(verts2)))
|
|
verts.extend(verts2)
|
|
vertsIdx3 = list(range(len(verts), len(verts) + len(verts3)))
|
|
verts.extend(verts3)
|
|
vertsIdx4 = list(range(len(verts), len(verts) + len(verts4)))
|
|
verts.extend(verts4)
|
|
|
|
verts_outside = []
|
|
verts_outside.extend(vertsIdx2[:2])
|
|
verts_outside.append(vertsIdx3[0])
|
|
verts_outside.extend(vertsIdx4)
|
|
verts_outside.append(vertsIdx3[-1])
|
|
verts_outside.append(vertsIdx2[-1])
|
|
|
|
if top:
|
|
# verts_inside_top = vertsIdx1
|
|
verts_outside_top = verts_outside
|
|
|
|
verts_bridge_start.append(vertsIdx1[0])
|
|
verts_bridge_start.append(vertsIdx2[0])
|
|
verts_bridge_end.append(vertsIdx1[-1])
|
|
verts_bridge_end.append(vertsIdx2[-1])
|
|
|
|
else:
|
|
# verts_inside_bottom = vertsIdx1
|
|
verts_outside_bottom = verts_outside
|
|
|
|
verts_bridge_start.append(vertsIdx2[0])
|
|
verts_bridge_start.append(vertsIdx1[0])
|
|
verts_bridge_end.append(vertsIdx2[-1])
|
|
verts_bridge_end.append(vertsIdx1[-1])
|
|
|
|
# Valley = first 2 vertices of outer base:
|
|
vgroup_valley.extend(vertsIdx2[:1])
|
|
# Top/tip vertices:
|
|
vgroup_top.extend(vertsIdx4)
|
|
|
|
faces_tooth_middle_top = createFaces(vertsIdx2[1:], vertsIdx3,
|
|
flipped=top)
|
|
faces_tooth_outer_top = createFaces(vertsIdx3, vertsIdx4,
|
|
flipped=top)
|
|
|
|
faces_base_top = createFaces(vertsIdx1, vertsIdx2, flipped=top)
|
|
faces.extend(faces_base_top)
|
|
|
|
faces.extend(faces_tooth_middle_top)
|
|
faces.extend(faces_tooth_outer_top)
|
|
|
|
# faces_inside = createFaces(verts_inside_top, verts_inside_bottom)
|
|
# faces.extend(faces_inside)
|
|
|
|
faces_outside = createFaces(verts_outside_top, verts_outside_bottom,
|
|
flipped=True)
|
|
faces.extend(faces_outside)
|
|
|
|
if toothCnt == 0:
|
|
verts_bridge_first = verts_bridge_start
|
|
|
|
# Bridge one tooth to the next
|
|
if verts_bridge_prev:
|
|
faces_bridge = createFaces(verts_bridge_prev, verts_bridge_start)
|
|
faces.extend(faces_bridge)
|
|
|
|
# Remember "end" vertices for next tooth.
|
|
verts_bridge_prev = verts_bridge_end
|
|
|
|
# Bridge the first to the last tooth.
|
|
faces_bridge_f_l = createFaces(verts_bridge_prev, verts_bridge_first)
|
|
faces.extend(faces_bridge_f_l)
|
|
|
|
return verts, faces, vgroup_top, vgroup_valley
|
|
|
|
|
|
# Create spokes geometry
|
|
# Returns:
|
|
# * A list of vertices (list of tuples)
|
|
# * A list of faces (list of lists)
|
|
#
|
|
# teethNum ... Number of teeth on the gear.
|
|
# radius ... Radius of the gear, negative for crown gear
|
|
# De ... Dedendum, extent of tooth below radius
|
|
# base ... Base, extent of gear below radius
|
|
# width ... Width, thickness of gear
|
|
# conangle ... Conical angle of gear. (radiant)
|
|
# rack
|
|
# spoke
|
|
# spbevel
|
|
# spwidth
|
|
# splength
|
|
# spresol
|
|
#
|
|
# @todo Finish this
|
|
# @todo Create a function that takes a "Gear" and creates a
|
|
# matching "Gear Spokes" object
|
|
|
|
def add_spokes(teethNum, radius, De, base, width=1, conangle=0, rack=0,
|
|
spoke=3, spbevel=0.1, spwidth=0.2, splength=1.0, spresol=9):
|
|
|
|
if teethNum < 2:
|
|
return None, None, None, None
|
|
|
|
if spoke < 2:
|
|
return None, None, None, None
|
|
|
|
t = 2 * pi / teethNum
|
|
|
|
if rack:
|
|
teethNum = 1
|
|
|
|
scale = (radius - 2 * width * tan(conangle)) / radius
|
|
|
|
verts = []
|
|
faces = []
|
|
|
|
c = scale # debug
|
|
|
|
fl = len(verts)
|
|
for toothCnt in range(teethNum):
|
|
a = toothCnt * t
|
|
s = 0 # For test
|
|
|
|
if toothCnt % spoke == 0:
|
|
for d in (-width, width):
|
|
sv, edgefaces, edgefaces2, sf = add_spoke(a + s, t, d,
|
|
radius * c, De * c, base * c,
|
|
spbevel, spwidth, splength, 0, spresol)
|
|
verts.extend(sv)
|
|
faces.extend([j + fl for j in i] for i in sf)
|
|
fl += len(sv)
|
|
|
|
d1 = fl - len(sv)
|
|
d2 = fl - 2 * len(sv)
|
|
|
|
faces.extend([(i + d2, j + d2, j + d1, i + d1)
|
|
for (i, j) in zip(edgefaces[:-1], edgefaces[1:])])
|
|
faces.extend([(i + d2, j + d2, j + d1, i + d1)
|
|
for (i, j) in zip(edgefaces2[:-1], edgefaces2[1:])])
|
|
|
|
else:
|
|
for d in (-width, width):
|
|
sv, edgefaces, edgefaces2, sf = add_spoke(a + s, t, d,
|
|
radius * c, De * c, base * c,
|
|
spbevel, spwidth, splength, 1, spresol)
|
|
|
|
verts.extend(sv)
|
|
fl += len(sv)
|
|
|
|
d1 = fl - len(sv)
|
|
d2 = fl - 2 * len(sv)
|
|
|
|
faces.extend([[i + d2, i + 1 + d2, i + 1 + d1, i + d1]
|
|
for (i) in range(0, 3)])
|
|
faces.extend([[i + d2, i + 1 + d2, i + 1 + d1, i + d1]
|
|
for (i) in range(5, 8)])
|
|
|
|
return verts, faces
|
|
|
|
|
|
# Create worm geometry.
|
|
# Returns:
|
|
# * A list of vertices
|
|
# * A list of faces
|
|
# * A list (group) of vertices of the tip
|
|
# * A list (group) of vertices of the valley
|
|
#
|
|
# teethNum ... Number of teeth on the worm
|
|
# radius ... Radius of the gear, negative for crown gear
|
|
# Ad ... Addendum, extent of tooth above radius
|
|
# De ... Dedendum, extent of tooth below radius
|
|
# p_angle ... Pressure angle. Skewness of tooth tip. (radiant)
|
|
# width ... Width, thickness of gear
|
|
# crown ... Inward pointing extend of crown teeth
|
|
#
|
|
# @todo: Fix teethNum. Some numbers are not possible yet
|
|
# @todo: Create start & end geometry (closing faces)
|
|
|
|
def add_worm(teethNum, rowNum, radius, Ad, De, p_angle,
|
|
width=1, skew=radians(11.25), crown=0.0):
|
|
|
|
worm = teethNum
|
|
teethNum = 24
|
|
|
|
t = 2 * pi / teethNum
|
|
|
|
verts = []
|
|
faces = []
|
|
vgroup_top = [] # Vertex group of top/tip? vertices.
|
|
vgroup_valley = [] # Vertex group of valley vertices
|
|
|
|
# width = width / 2.0
|
|
|
|
edgeloop_prev = []
|
|
for Row in range(rowNum):
|
|
edgeloop = []
|
|
|
|
for toothCnt in range(teethNum):
|
|
a = toothCnt * t
|
|
|
|
s = Row * skew
|
|
d = Row * width
|
|
c = 1
|
|
|
|
isTooth = False
|
|
if toothCnt % (teethNum / worm) != 0:
|
|
# Flat
|
|
verts1, verts2, verts3, verts4 = add_tooth(a + s, t, d,
|
|
radius - De, 0.0, 0.0, 0, p_angle)
|
|
|
|
# Ignore other verts than the "other base".
|
|
verts1 = verts3 = verts4 = []
|
|
|
|
else:
|
|
# Tooth
|
|
isTooth = True
|
|
verts1, verts2, verts3, verts4 = add_tooth(a + s, t, d,
|
|
radius * c, Ad * c, De * c, 0 * c, p_angle, 0, crown)
|
|
|
|
# Remove various unneeded verts (if we are "inside" the tooth)
|
|
del(verts2[2]) # Central vertex in the base of the tooth.
|
|
del(verts3[1]) # Central vertex in the middle of the tooth.
|
|
|
|
vertsIdx2 = list(range(len(verts), len(verts) + len(verts2)))
|
|
verts.extend(verts2)
|
|
vertsIdx3 = list(range(len(verts), len(verts) + len(verts3)))
|
|
verts.extend(verts3)
|
|
vertsIdx4 = list(range(len(verts), len(verts) + len(verts4)))
|
|
verts.extend(verts4)
|
|
|
|
if isTooth:
|
|
verts_current = []
|
|
verts_current.extend(vertsIdx2[:2])
|
|
verts_current.append(vertsIdx3[0])
|
|
verts_current.extend(vertsIdx4)
|
|
verts_current.append(vertsIdx3[-1])
|
|
verts_current.append(vertsIdx2[-1])
|
|
|
|
# Valley = first 2 vertices of outer base:
|
|
vgroup_valley.extend(vertsIdx2[:1])
|
|
# Top/tip vertices:
|
|
vgroup_top.extend(vertsIdx4)
|
|
|
|
else:
|
|
# Flat
|
|
verts_current = vertsIdx2
|
|
|
|
# Valley - all of them.
|
|
vgroup_valley.extend(vertsIdx2)
|
|
|
|
edgeloop.extend(verts_current)
|
|
|
|
# Create faces between rings/rows.
|
|
if edgeloop_prev:
|
|
faces_row = createFaces(edgeloop, edgeloop_prev, closed=True)
|
|
faces.extend(faces_row)
|
|
|
|
# Remember last ring/row of vertices for next ring/row iteration.
|
|
edgeloop_prev = edgeloop
|
|
|
|
return verts, faces, vgroup_top, vgroup_valley
|
|
|
|
def AddGearMesh(self, context):
|
|
|
|
verts, faces, verts_tip, verts_valley = add_gear(
|
|
self.number_of_teeth,
|
|
self.radius,
|
|
self.addendum,
|
|
self.dedendum,
|
|
self.base,
|
|
self.angle,
|
|
width=self.width,
|
|
skew=self.skew,
|
|
conangle=self.conangle,
|
|
crown=self.crown
|
|
)
|
|
|
|
mesh = bpy.data.meshes.new("Gear")
|
|
mesh.from_pydata(verts, [], faces)
|
|
|
|
return mesh, verts_tip, verts_valley
|
|
|
|
|
|
class AddGear(Operator, object_utils.AddObjectHelper):
|
|
bl_idname = "mesh.primitive_gear"
|
|
bl_label = "Add Gear"
|
|
bl_description = "Construct a gear mesh"
|
|
bl_options = {'REGISTER', 'UNDO', 'PRESET'}
|
|
|
|
Gear : BoolProperty(name = "Gear",
|
|
default = True,
|
|
description = "Gear")
|
|
|
|
#### change properties
|
|
name : StringProperty(name = "Name",
|
|
description = "Name")
|
|
|
|
change : BoolProperty(name = "Change",
|
|
default = False,
|
|
description = "change Gear")
|
|
|
|
number_of_teeth: IntProperty(name="Number of Teeth",
|
|
description="Number of teeth on the gear",
|
|
min=2,
|
|
soft_max=1000,
|
|
default=12
|
|
)
|
|
radius: FloatProperty(name="Radius",
|
|
description="Radius of the gear, negative for crown gear",
|
|
soft_min=-1000.0,
|
|
soft_max=1000.0,
|
|
unit='LENGTH',
|
|
default=1.0
|
|
)
|
|
addendum: FloatProperty(name="Addendum",
|
|
description="Addendum, extent of tooth above radius",
|
|
soft_min=-1000.0,
|
|
soft_max=1000.0,
|
|
unit='LENGTH',
|
|
default=0.1
|
|
)
|
|
dedendum: FloatProperty(name="Dedendum",
|
|
description="Dedendum, extent of tooth below radius",
|
|
soft_min=-1000.0,
|
|
soft_max=1000.0,
|
|
unit='LENGTH',
|
|
default=0.1
|
|
)
|
|
angle: FloatProperty(name="Pressure Angle",
|
|
description="Pressure angle, skewness of tooth tip",
|
|
soft_min=radians(-45.0),
|
|
soft_max=radians(45.0),
|
|
unit='ROTATION',
|
|
default=radians(20.0)
|
|
)
|
|
base: FloatProperty(name="Base",
|
|
description="Base, extent of gear below radius",
|
|
soft_min=-1000.0,
|
|
soft_max=1000.0,
|
|
unit='LENGTH',
|
|
default=0.2
|
|
)
|
|
width: FloatProperty(name="Width",
|
|
description="Width, thickness of gear",
|
|
soft_min=-1000.0,
|
|
soft_max=1000.0,
|
|
unit='LENGTH',
|
|
default=0.2
|
|
)
|
|
skew: FloatProperty(name="Skewness",
|
|
description="Skew of teeth",
|
|
soft_min=radians(-360.0),
|
|
soft_max=radians(360.0),
|
|
unit='ROTATION',
|
|
default=radians(0.0)
|
|
)
|
|
conangle: FloatProperty(name="Conical angle",
|
|
description="Conical angle of gear",
|
|
soft_min=radians(-360.0),
|
|
soft_max=radians(360.0),
|
|
unit='ROTATION',
|
|
default=radians(0.0)
|
|
)
|
|
crown: FloatProperty(name="Crown",
|
|
description="Inward pointing extend of crown teeth",
|
|
soft_min=-1000.0,
|
|
soft_max=1000.0,
|
|
unit='LENGTH',
|
|
default=0.0
|
|
)
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
|
|
box = layout.box()
|
|
box.prop(self, 'number_of_teeth')
|
|
|
|
box = layout.box()
|
|
box.prop(self, 'radius')
|
|
box.prop(self, 'width')
|
|
box.prop(self, 'base')
|
|
|
|
box = layout.box()
|
|
box.prop(self, 'dedendum')
|
|
box.prop(self, 'addendum')
|
|
|
|
box = layout.box()
|
|
box.prop(self, 'angle')
|
|
box.prop(self, 'skew')
|
|
box.prop(self, 'conangle')
|
|
box.prop(self, 'crown')
|
|
|
|
if self.change == False:
|
|
# generic transform props
|
|
box = layout.box()
|
|
box.prop(self, 'align', expand=True)
|
|
box.prop(self, 'location', expand=True)
|
|
box.prop(self, 'rotation', expand=True)
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return context.scene is not None
|
|
|
|
def execute(self, context):
|
|
# turn off 'Enter Edit Mode'
|
|
use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode
|
|
bpy.context.preferences.edit.use_enter_edit_mode = False
|
|
|
|
if bpy.context.mode == "OBJECT":
|
|
if context.selected_objects != [] and context.active_object and \
|
|
(context.active_object.data is not None) and ('Gear' in context.active_object.data.keys()) and \
|
|
(self.change == True):
|
|
obj = context.active_object
|
|
oldmesh = obj.data
|
|
oldmeshname = obj.data.name
|
|
mesh, verts_tip, verts_valley = AddGearMesh(self, context)
|
|
obj.data = mesh
|
|
try:
|
|
bpy.ops.object.vertex_group_remove(all=True)
|
|
except:
|
|
pass
|
|
|
|
for material in oldmesh.materials:
|
|
obj.data.materials.append(material)
|
|
|
|
bpy.data.meshes.remove(oldmesh)
|
|
obj.data.name = oldmeshname
|
|
else:
|
|
mesh, verts_tip, verts_valley = AddGearMesh(self, context)
|
|
obj = object_utils.object_data_add(context, mesh, operator=self)
|
|
|
|
# Create vertex groups from stored vertices.
|
|
tipGroup = obj.vertex_groups.new(name='Tips')
|
|
tipGroup.add(verts_tip, 1.0, 'ADD')
|
|
|
|
valleyGroup = obj.vertex_groups.new(name='Valleys')
|
|
valleyGroup.add(verts_valley, 1.0, 'ADD')
|
|
|
|
obj.data["Gear"] = True
|
|
obj.data["change"] = False
|
|
for prm in GearParameters():
|
|
obj.data[prm] = getattr(self, prm)
|
|
|
|
if bpy.context.mode == "EDIT_MESH":
|
|
active_object = context.active_object
|
|
name_active_object = active_object.name
|
|
bpy.ops.object.mode_set(mode='OBJECT')
|
|
mesh, verts_tip, verts_valley = AddGearMesh(self, context)
|
|
obj = object_utils.object_data_add(context, mesh, operator=self)
|
|
|
|
# Create vertex groups from stored vertices.
|
|
tipGroup = obj.vertex_groups.new(name='Tips')
|
|
tipGroup.add(verts_tip, 1.0, 'ADD')
|
|
|
|
valleyGroup = obj.vertex_groups.new(name='Valleys')
|
|
valleyGroup.add(verts_valley, 1.0, 'ADD')
|
|
|
|
obj.select_set(True)
|
|
active_object.select_set(True)
|
|
bpy.context.view_layer.objects.active = active_object
|
|
bpy.ops.object.join()
|
|
context.active_object.name = name_active_object
|
|
bpy.ops.object.mode_set(mode='EDIT')
|
|
|
|
if use_enter_edit_mode:
|
|
bpy.ops.object.mode_set(mode = 'EDIT')
|
|
|
|
# restore pre operator state
|
|
bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode
|
|
|
|
return {'FINISHED'}
|
|
|
|
def invoke(self, context, event):
|
|
self.execute(context)
|
|
|
|
return {'FINISHED'}
|
|
|
|
def GearParameters():
|
|
GearParameters = [
|
|
"number_of_teeth",
|
|
"radius",
|
|
"addendum",
|
|
"dedendum",
|
|
"base",
|
|
"angle",
|
|
"width",
|
|
"skew",
|
|
"conangle",
|
|
"crown",
|
|
]
|
|
return GearParameters
|
|
|
|
def AddWormGearMesh(self, context):
|
|
|
|
verts, faces, verts_tip, verts_valley = add_worm(
|
|
self.number_of_teeth,
|
|
self.number_of_rows,
|
|
self.radius,
|
|
self.addendum,
|
|
self.dedendum,
|
|
self.angle,
|
|
width=self.row_height,
|
|
skew=self.skew,
|
|
crown=self.crown
|
|
)
|
|
|
|
mesh = bpy.data.meshes.new("Worm Gear")
|
|
mesh.from_pydata(verts, [], faces)
|
|
|
|
return mesh, verts_tip, verts_valley
|
|
|
|
|
|
class AddWormGear(Operator, object_utils.AddObjectHelper):
|
|
bl_idname = "mesh.primitive_worm_gear"
|
|
bl_label = "Add Worm Gear"
|
|
bl_description = "Construct a worm gear mesh"
|
|
bl_options = {'REGISTER', 'UNDO', 'PRESET'}
|
|
|
|
WormGear : BoolProperty(name = "WormGear",
|
|
default = True,
|
|
description = "WormGear")
|
|
|
|
#### change properties
|
|
name : StringProperty(name = "Name",
|
|
description = "Name")
|
|
|
|
change : BoolProperty(name = "Change",
|
|
default = False,
|
|
description = "change WormGear")
|
|
|
|
number_of_teeth: IntProperty(
|
|
name="Number of Teeth",
|
|
description="Number of teeth on the gear",
|
|
min=1,
|
|
soft_max=1000,
|
|
default=12
|
|
)
|
|
number_of_rows: IntProperty(
|
|
name="Number of Rows",
|
|
description="Number of rows on the worm gear",
|
|
min=0,
|
|
soft_max=1000,
|
|
default=32
|
|
)
|
|
radius: FloatProperty(
|
|
name="Radius",
|
|
description="Radius of the gear, negative for crown gear",
|
|
soft_min=-1000.0,
|
|
soft_max=1000.0,
|
|
unit='LENGTH',
|
|
default=1.0
|
|
)
|
|
addendum: FloatProperty(
|
|
name="Addendum",
|
|
description="Addendum, extent of tooth above radius",
|
|
soft_min=-1000.0,
|
|
soft_max=1000.0,
|
|
unit='LENGTH',
|
|
default=0.1
|
|
)
|
|
dedendum: FloatProperty(
|
|
name="Dedendum",
|
|
description="Dedendum, extent of tooth below radius",
|
|
soft_min=-1000.0,
|
|
soft_max=1000.0,
|
|
unit='LENGTH',
|
|
default=0.1
|
|
)
|
|
angle: FloatProperty(
|
|
name="Pressure Angle",
|
|
description="Pressure angle, skewness of tooth tip",
|
|
soft_min=radians(-45.0),
|
|
soft_max=radians(45.0),
|
|
default=radians(20.0),
|
|
unit='ROTATION'
|
|
)
|
|
row_height: FloatProperty(
|
|
name="Row Height",
|
|
description="Height of each Row",
|
|
soft_min=-1000.0,
|
|
soft_max=1000.0,
|
|
unit='LENGTH',
|
|
default=0.2
|
|
)
|
|
skew: FloatProperty(
|
|
name="Skewness per Row",
|
|
description="Skew of each row",
|
|
soft_min=radians(-360.0),
|
|
soft_max=radians(360.0),
|
|
default=radians(11.25),
|
|
unit='ROTATION'
|
|
)
|
|
crown: FloatProperty(
|
|
name="Crown",
|
|
description="Inward pointing extend of crown teeth",
|
|
soft_min=-1000.0,
|
|
soft_max=1000.0,
|
|
unit='LENGTH',
|
|
default=0.0
|
|
)
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
box = layout.box()
|
|
box.prop(self, "number_of_teeth")
|
|
box.prop(self, "number_of_rows")
|
|
box.prop(self, "radius")
|
|
box.prop(self, "row_height")
|
|
|
|
box = layout.box()
|
|
box.prop(self, "addendum")
|
|
box.prop(self, "dedendum")
|
|
|
|
box = layout.box()
|
|
box.prop(self, "angle")
|
|
box.prop(self, "skew")
|
|
box.prop(self, "crown")
|
|
|
|
if self.change == False:
|
|
# generic transform props
|
|
box = layout.box()
|
|
box.prop(self, 'align', expand=True)
|
|
box.prop(self, 'location', expand=True)
|
|
box.prop(self, 'rotation', expand=True)
|
|
|
|
def execute(self, context):
|
|
# turn off 'Enter Edit Mode'
|
|
use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode
|
|
bpy.context.preferences.edit.use_enter_edit_mode = False
|
|
|
|
if bpy.context.mode == "OBJECT":
|
|
if context.selected_objects != [] and context.active_object and \
|
|
(context.active_object.data is not None) and ('WormGear' in context.active_object.data.keys()) and \
|
|
(self.change == True):
|
|
obj = context.active_object
|
|
oldmesh = obj.data
|
|
oldmeshname = obj.data.name
|
|
|
|
mesh, verts_tip, verts_valley = AddWormGearMesh(self, context)
|
|
obj.data = mesh
|
|
try:
|
|
bpy.ops.object.vertex_group_remove(all=True)
|
|
except:
|
|
pass
|
|
|
|
for material in oldmesh.materials:
|
|
obj.data.materials.append(material)
|
|
|
|
bpy.data.meshes.remove(oldmesh)
|
|
obj.data.name = oldmeshname
|
|
else:
|
|
mesh, verts_tip, verts_valley = AddWormGearMesh(self, context)
|
|
obj = object_utils.object_data_add(context, mesh, operator=self)
|
|
|
|
# Create vertex groups from stored vertices.
|
|
tipGroup = obj.vertex_groups.new(name = 'Tips')
|
|
tipGroup.add(verts_tip, 1.0, 'ADD')
|
|
|
|
valleyGroup = obj.vertex_groups.new(name = 'Valleys')
|
|
valleyGroup.add(verts_valley, 1.0, 'ADD')
|
|
|
|
obj.data["WormGear"] = True
|
|
obj.data["change"] = False
|
|
for prm in WormGearParameters():
|
|
obj.data[prm] = getattr(self, prm)
|
|
|
|
if bpy.context.mode == "EDIT_MESH":
|
|
active_object = context.active_object
|
|
name_active_object = active_object.name
|
|
bpy.ops.object.mode_set(mode='OBJECT')
|
|
mesh, verts_tip, verts_valley = AddWormGearMesh(self, context)
|
|
obj = object_utils.object_data_add(context, mesh, operator=self)
|
|
|
|
# Create vertex groups from stored vertices.
|
|
tipGroup = obj.vertex_groups.new(name = 'Tips')
|
|
tipGroup.add(verts_tip, 1.0, 'ADD')
|
|
|
|
valleyGroup = obj.vertex_groups.new(name = 'Valleys')
|
|
valleyGroup.add(verts_valley, 1.0, 'ADD')
|
|
|
|
obj.select_set(True)
|
|
active_object.select_set(True)
|
|
bpy.context.view_layer.objects.active = active_object
|
|
bpy.ops.object.join()
|
|
context.active_object.name = name_active_object
|
|
bpy.ops.object.mode_set(mode='EDIT')
|
|
|
|
if use_enter_edit_mode:
|
|
bpy.ops.object.mode_set(mode = 'EDIT')
|
|
|
|
# restore pre operator state
|
|
bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode
|
|
|
|
return {'FINISHED'}
|
|
|
|
def WormGearParameters():
|
|
WormGearParameters = [
|
|
"number_of_teeth",
|
|
"number_of_rows",
|
|
"radius",
|
|
"addendum",
|
|
"dedendum",
|
|
"angle",
|
|
"row_height",
|
|
"skew",
|
|
"crown",
|
|
]
|
|
return WormGearParameters
|