Fix #105067: Node Wrangler: cannot preview multi-user material group
[blender-addons.git] / add_mesh_extra_objects / add_mesh_gears.py
blob43cf0e6da1afc36ece3b162c02790ddb42afb6e5
1 # SPDX-FileCopyrightText: 2009-2010 Michel J. Anders (varkenvarken)
3 # SPDX-License-Identifier: GPL-2.0-or-later
5 import bpy
6 from bpy.types import Operator
7 from math import (
8 atan, asin, cos,
9 sin, tan, pi,
10 radians,
12 from bpy.props import (
13 FloatProperty,
14 IntProperty,
15 BoolProperty,
16 StringProperty,
17 FloatVectorProperty
19 from mathutils import (
20 Vector,
21 Matrix,
23 from bpy_extras import object_utils
25 # A very simple "bridge" tool.
26 # Connects two equally long vertex rows with faces.
27 # Returns a list of the new faces (list of lists)
29 # vertIdx1 ... First vertex list (list of vertex indices)
30 # vertIdx2 ... Second vertex list (list of vertex indices)
31 # closed ... Creates a loop (first & last are closed)
32 # flipped ... Invert the normal of the face(s)
34 # Note: You can set vertIdx1 to a single vertex index to create
35 # a fan/star of faces
36 # Note: If both vertex idx list are the same length they have
37 # to have at least 2 vertices
39 def createFaces(vertIdx1, vertIdx2, closed=False, flipped=False):
40 faces = []
42 if not vertIdx1 or not vertIdx2:
43 return None
45 if len(vertIdx1) < 2 and len(vertIdx2) < 2:
46 return None
48 fan = False
49 if (len(vertIdx1) != len(vertIdx2)):
50 if (len(vertIdx1) == 1 and len(vertIdx2) > 1):
51 fan = True
52 else:
53 return None
55 total = len(vertIdx2)
57 if closed:
58 # Bridge the start with the end.
59 if flipped:
60 face = [
61 vertIdx1[0],
62 vertIdx2[0],
63 vertIdx2[total - 1]]
64 if not fan:
65 face.append(vertIdx1[total - 1])
66 faces.append(face)
68 else:
69 face = [vertIdx2[0], vertIdx1[0]]
70 if not fan:
71 face.append(vertIdx1[total - 1])
72 face.append(vertIdx2[total - 1])
73 faces.append(face)
75 # Bridge the rest of the faces.
76 for num in range(total - 1):
77 if flipped:
78 if fan:
79 face = [vertIdx2[num], vertIdx1[0], vertIdx2[num + 1]]
80 else:
81 face = [vertIdx2[num], vertIdx1[num],
82 vertIdx1[num + 1], vertIdx2[num + 1]]
83 faces.append(face)
84 else:
85 if fan:
86 face = [vertIdx1[0], vertIdx2[num], vertIdx2[num + 1]]
87 else:
88 face = [vertIdx1[num], vertIdx2[num],
89 vertIdx2[num + 1], vertIdx1[num + 1]]
90 faces.append(face)
92 return faces
95 # Calculate the vertex coordinates for a single
96 # section of a gear tooth.
97 # Returns 4 lists of vertex coords (list of tuples):
98 # *-*---*---* (1.) verts_inner_base
99 # | | | |
100 # *-*---*---* (2.) verts_outer_base
101 # | | |
102 # *---*---* (3.) verts_middle_tooth
103 # \ | /
104 # *-*-* (4.) verts_tip_tooth
109 # radius
110 # Ad
111 # De
112 # base
113 # p_angle
114 # rack
115 # crown
117 def add_tooth(a, t, d, radius, Ad, De, base, p_angle, rack=0, crown=0.0):
118 A = [a, a + t / 4, a + t / 2, a + 3 * t / 4]
119 C = [cos(i) for i in A]
120 S = [sin(i) for i in A]
122 Ra = radius + Ad
123 Rd = radius - De
124 Rb = Rd - base
126 # Pressure angle calc
127 O = Ad * tan(p_angle)
128 if Ra != 0:
129 p_angle = atan(O / Ra)
130 else:
131 p_angle = atan(O)
133 if radius < 0:
134 p_angle = -p_angle
136 if rack:
137 S = [sin(t / 4) * I for I in range(-2, 3)]
138 Sp = [0, sin(-t / 4 + p_angle), 0, sin(t / 4 - p_angle)]
140 verts_inner_base = [(Rb, radius * S[I], d) for I in range(4)]
141 verts_outer_base = [(Rd, radius * S[I], d) for I in range(4)]
142 verts_middle_tooth = [(radius, radius * S[I], d) for I in range(1, 4)]
143 verts_tip_tooth = [(Ra, radius * Sp[I], d) for I in range(1, 4)]
145 else:
146 Cp = [
148 cos(a + t / 4 + p_angle),
149 cos(a + t / 2),
150 cos(a + 3 * t / 4 - p_angle)]
151 Sp = [0,
152 sin(a + t / 4 + p_angle),
153 sin(a + t / 2),
154 sin(a + 3 * t / 4 - p_angle)]
156 verts_inner_base = [(Rb * C[I], Rb * S[I], d)
157 for I in range(4)]
158 verts_outer_base = [(Rd * C[I], Rd * S[I], d)
159 for I in range(4)]
160 verts_middle_tooth = [(radius * C[I], radius * S[I], d + crown / 3)
161 for I in range(1, 4)]
162 verts_tip_tooth = [(Ra * Cp[I], Ra * Sp[I], d + crown)
163 for I in range(1, 4)]
165 return (verts_inner_base, verts_outer_base,
166 verts_middle_tooth, verts_tip_tooth)
169 # EXPERIMENTAL Calculate the vertex coordinates for a single
170 # section of a gearspoke.
171 # Returns them as a list of tuples
176 # radius
177 # De
178 # base
182 # gap
183 # width
185 # @todo Finish this.
187 def add_spoke(a, t, d, radius, De, base, s, w, l, gap=0, width=19):
188 Rd = radius - De
189 Rb = Rd - base
191 verts = []
192 edgefaces = []
193 edgefaces2 = []
194 sf = []
196 if not gap:
197 for N in range(width, 1, -2):
198 edgefaces.append(len(verts))
199 ts = t / 4
200 tm = a + 2 * ts
201 te = asin(w / Rb)
202 td = te - ts
203 t4 = ts + td * (width - N) / (width - 3.0)
204 A = [tm + (i - int(N / 2)) * t4 for i in range(N)]
205 C = [cos(i) for i in A]
206 S = [sin(i) for i in A]
208 verts.extend((Rb * I, Rb * J, d) for (I, J) in zip(C, S))
209 edgefaces2.append(len(verts) - 1)
211 Rb = Rb - s
213 n = 0
214 for N in range(width, 3, -2):
215 sf.extend([(i + n, i + 1 + n, i + 2 + n, i + N + n)
216 for i in range(0, N - 1, 2)])
217 sf.extend([(i + 2 + n, i + N + n, i + N + 1 + n, i + N + 2 + n)
218 for i in range(0, N - 3, 2)])
220 n = n + N
222 return verts, edgefaces, edgefaces2, sf
225 # Create gear geometry.
226 # Returns:
227 # * A list of vertices (list of tuples)
228 # * A list of faces (list of lists)
229 # * A list (group) of vertices of the tip (list of vertex indices)
230 # * A list (group) of vertices of the valley (list of vertex indices)
232 # teethNum ... Number of teeth on the gear
233 # radius ... Radius of the gear, negative for crown gear
234 # Ad ... Addendum, extent of tooth above radius
235 # De ... Dedendum, extent of tooth below radius
236 # base ... Base, extent of gear below radius
237 # p_angle ... Pressure angle. Skewness of tooth tip. (radiant)
238 # width ... Width, thickness of gear
239 # skew ... Skew of teeth. (radiant)
240 # conangle ... Conical angle of gear. (radiant)
241 # rack
242 # crown ... Inward pointing extend of crown teeth
244 # inner radius = radius - (De + base)
246 def add_gear(teethNum, radius, Ad, De, base, p_angle,
247 width=1, skew=0, conangle=0, rack=0, crown=0.0):
249 if teethNum < 2:
250 return None, None, None, None
252 t = 2 * pi / teethNum
254 if rack:
255 teethNum = 1
257 #print(radius, width, conangle)
258 if radius != 0:
259 scale = (radius - 2 * width * tan(conangle)) / radius
260 else:
261 scale = radius - 2 * width * tan(conangle)
263 verts = []
264 faces = []
265 vgroup_top = [] # Vertex group of top/tip? vertices.
266 vgroup_valley = [] # Vertex group of valley vertices
268 verts_bridge_prev = []
269 for toothCnt in range(teethNum):
270 a = toothCnt * t
272 verts_bridge_start = []
273 verts_bridge_end = []
275 verts_outside_top = []
276 verts_outside_bottom = []
277 for (s, d, c, top) \
278 in [(0, -width, 1, True), (skew, width, scale, False)]:
280 verts1, verts2, verts3, verts4 = add_tooth(a + s, t, d,
281 radius * c, Ad * c, De * c, base * c, p_angle,
282 rack, crown)
284 vertsIdx1 = list(range(len(verts), len(verts) + len(verts1)))
285 verts.extend(verts1)
286 vertsIdx2 = list(range(len(verts), len(verts) + len(verts2)))
287 verts.extend(verts2)
288 vertsIdx3 = list(range(len(verts), len(verts) + len(verts3)))
289 verts.extend(verts3)
290 vertsIdx4 = list(range(len(verts), len(verts) + len(verts4)))
291 verts.extend(verts4)
293 verts_outside = []
294 verts_outside.extend(vertsIdx2[:2])
295 verts_outside.append(vertsIdx3[0])
296 verts_outside.extend(vertsIdx4)
297 verts_outside.append(vertsIdx3[-1])
298 verts_outside.append(vertsIdx2[-1])
300 if top:
301 # verts_inside_top = vertsIdx1
302 verts_outside_top = verts_outside
304 verts_bridge_start.append(vertsIdx1[0])
305 verts_bridge_start.append(vertsIdx2[0])
306 verts_bridge_end.append(vertsIdx1[-1])
307 verts_bridge_end.append(vertsIdx2[-1])
309 else:
310 # verts_inside_bottom = vertsIdx1
311 verts_outside_bottom = verts_outside
313 verts_bridge_start.append(vertsIdx2[0])
314 verts_bridge_start.append(vertsIdx1[0])
315 verts_bridge_end.append(vertsIdx2[-1])
316 verts_bridge_end.append(vertsIdx1[-1])
318 # Valley = first 2 vertices of outer base:
319 vgroup_valley.extend(vertsIdx2[:1])
320 # Top/tip vertices:
321 vgroup_top.extend(vertsIdx4)
323 faces_tooth_middle_top = createFaces(vertsIdx2[1:], vertsIdx3,
324 flipped=top)
325 faces_tooth_outer_top = createFaces(vertsIdx3, vertsIdx4,
326 flipped=top)
328 faces_base_top = createFaces(vertsIdx1, vertsIdx2, flipped=top)
329 faces.extend(faces_base_top)
331 faces.extend(faces_tooth_middle_top)
332 faces.extend(faces_tooth_outer_top)
334 # faces_inside = createFaces(verts_inside_top, verts_inside_bottom)
335 # faces.extend(faces_inside)
337 faces_outside = createFaces(verts_outside_top, verts_outside_bottom,
338 flipped=True)
339 faces.extend(faces_outside)
341 if toothCnt == 0:
342 verts_bridge_first = verts_bridge_start
344 # Bridge one tooth to the next
345 if verts_bridge_prev:
346 faces_bridge = createFaces(verts_bridge_prev, verts_bridge_start)
347 faces.extend(faces_bridge)
349 # Remember "end" vertices for next tooth.
350 verts_bridge_prev = verts_bridge_end
352 # Bridge the first to the last tooth.
353 faces_bridge_f_l = createFaces(verts_bridge_prev, verts_bridge_first)
354 faces.extend(faces_bridge_f_l)
356 return verts, faces, vgroup_top, vgroup_valley
359 # Create spokes geometry
360 # Returns:
361 # * A list of vertices (list of tuples)
362 # * A list of faces (list of lists)
364 # teethNum ... Number of teeth on the gear.
365 # radius ... Radius of the gear, negative for crown gear
366 # De ... Dedendum, extent of tooth below radius
367 # base ... Base, extent of gear below radius
368 # width ... Width, thickness of gear
369 # conangle ... Conical angle of gear. (radiant)
370 # rack
371 # spoke
372 # spbevel
373 # spwidth
374 # splength
375 # spresol
377 # @todo Finish this
378 # @todo Create a function that takes a "Gear" and creates a
379 # matching "Gear Spokes" object
381 def add_spokes(teethNum, radius, De, base, width=1, conangle=0, rack=0,
382 spoke=3, spbevel=0.1, spwidth=0.2, splength=1.0, spresol=9):
384 if teethNum < 2:
385 return None, None, None, None
387 if spoke < 2:
388 return None, None, None, None
390 t = 2 * pi / teethNum
392 if rack:
393 teethNum = 1
395 scale = (radius - 2 * width * tan(conangle)) / radius
397 verts = []
398 faces = []
400 c = scale # debug
402 fl = len(verts)
403 for toothCnt in range(teethNum):
404 a = toothCnt * t
405 s = 0 # For test
407 if toothCnt % spoke == 0:
408 for d in (-width, width):
409 sv, edgefaces, edgefaces2, sf = add_spoke(a + s, t, d,
410 radius * c, De * c, base * c,
411 spbevel, spwidth, splength, 0, spresol)
412 verts.extend(sv)
413 faces.extend([j + fl for j in i] for i in sf)
414 fl += len(sv)
416 d1 = fl - len(sv)
417 d2 = fl - 2 * len(sv)
419 faces.extend([(i + d2, j + d2, j + d1, i + d1)
420 for (i, j) in zip(edgefaces[:-1], edgefaces[1:])])
421 faces.extend([(i + d2, j + d2, j + d1, i + d1)
422 for (i, j) in zip(edgefaces2[:-1], edgefaces2[1:])])
424 else:
425 for d in (-width, width):
426 sv, edgefaces, edgefaces2, sf = add_spoke(a + s, t, d,
427 radius * c, De * c, base * c,
428 spbevel, spwidth, splength, 1, spresol)
430 verts.extend(sv)
431 fl += len(sv)
433 d1 = fl - len(sv)
434 d2 = fl - 2 * len(sv)
436 faces.extend([[i + d2, i + 1 + d2, i + 1 + d1, i + d1]
437 for (i) in range(0, 3)])
438 faces.extend([[i + d2, i + 1 + d2, i + 1 + d1, i + d1]
439 for (i) in range(5, 8)])
441 return verts, faces
444 # Create worm geometry.
445 # Returns:
446 # * A list of vertices
447 # * A list of faces
448 # * A list (group) of vertices of the tip
449 # * A list (group) of vertices of the valley
451 # teethNum ... Number of teeth on the worm
452 # radius ... Radius of the gear, negative for crown gear
453 # Ad ... Addendum, extent of tooth above radius
454 # De ... Dedendum, extent of tooth below radius
455 # p_angle ... Pressure angle. Skewness of tooth tip. (radiant)
456 # width ... Width, thickness of gear
457 # crown ... Inward pointing extend of crown teeth
459 # @todo: Fix teethNum. Some numbers are not possible yet
460 # @todo: Create start & end geometry (closing faces)
462 def add_worm(teethNum, rowNum, radius, Ad, De, p_angle,
463 width=1, skew=radians(11.25), crown=0.0):
465 worm = teethNum
466 teethNum = 24
468 t = 2 * pi / teethNum
470 verts = []
471 faces = []
472 vgroup_top = [] # Vertex group of top/tip? vertices.
473 vgroup_valley = [] # Vertex group of valley vertices
475 # width = width / 2.0
477 edgeloop_prev = []
478 for Row in range(rowNum):
479 edgeloop = []
481 for toothCnt in range(teethNum):
482 a = toothCnt * t
484 s = Row * skew
485 d = Row * width
486 c = 1
488 isTooth = False
489 if toothCnt % (teethNum / worm) != 0:
490 # Flat
491 verts1, verts2, verts3, verts4 = add_tooth(a + s, t, d,
492 radius - De, 0.0, 0.0, 0, p_angle)
494 # Ignore other verts than the "other base".
495 verts1 = verts3 = verts4 = []
497 else:
498 # Tooth
499 isTooth = True
500 verts1, verts2, verts3, verts4 = add_tooth(a + s, t, d,
501 radius * c, Ad * c, De * c, 0 * c, p_angle, 0, crown)
503 # Remove various unneeded verts (if we are "inside" the tooth)
504 del(verts2[2]) # Central vertex in the base of the tooth.
505 del(verts3[1]) # Central vertex in the middle of the tooth.
507 vertsIdx2 = list(range(len(verts), len(verts) + len(verts2)))
508 verts.extend(verts2)
509 vertsIdx3 = list(range(len(verts), len(verts) + len(verts3)))
510 verts.extend(verts3)
511 vertsIdx4 = list(range(len(verts), len(verts) + len(verts4)))
512 verts.extend(verts4)
514 if isTooth:
515 verts_current = []
516 verts_current.extend(vertsIdx2[:2])
517 verts_current.append(vertsIdx3[0])
518 verts_current.extend(vertsIdx4)
519 verts_current.append(vertsIdx3[-1])
520 verts_current.append(vertsIdx2[-1])
522 # Valley = first 2 vertices of outer base:
523 vgroup_valley.extend(vertsIdx2[:1])
524 # Top/tip vertices:
525 vgroup_top.extend(vertsIdx4)
527 else:
528 # Flat
529 verts_current = vertsIdx2
531 # Valley - all of them.
532 vgroup_valley.extend(vertsIdx2)
534 edgeloop.extend(verts_current)
536 # Create faces between rings/rows.
537 if edgeloop_prev:
538 faces_row = createFaces(edgeloop, edgeloop_prev, closed=True)
539 faces.extend(faces_row)
541 # Remember last ring/row of vertices for next ring/row iteration.
542 edgeloop_prev = edgeloop
544 return verts, faces, vgroup_top, vgroup_valley
546 def AddGearMesh(self, context):
548 verts, faces, verts_tip, verts_valley = add_gear(
549 self.number_of_teeth,
550 self.radius,
551 self.addendum,
552 self.dedendum,
553 self.base,
554 self.angle,
555 width=self.width,
556 skew=self.skew,
557 conangle=self.conangle,
558 crown=self.crown
561 mesh = bpy.data.meshes.new("Gear")
562 mesh.from_pydata(verts, [], faces)
564 return mesh, verts_tip, verts_valley
567 class AddGear(Operator, object_utils.AddObjectHelper):
568 bl_idname = "mesh.primitive_gear"
569 bl_label = "Add Gear"
570 bl_description = "Construct a gear mesh"
571 bl_options = {'REGISTER', 'UNDO', 'PRESET'}
573 Gear : BoolProperty(name = "Gear",
574 default = True,
575 description = "Gear")
577 #### change properties
578 name : StringProperty(name = "Name",
579 description = "Name")
581 change : BoolProperty(name = "Change",
582 default = False,
583 description = "change Gear")
585 number_of_teeth: IntProperty(name="Number of Teeth",
586 description="Number of teeth on the gear",
587 min=2,
588 soft_max=1000,
589 default=12
591 radius: FloatProperty(name="Radius",
592 description="Radius of the gear, negative for crown gear",
593 soft_min=-1000.0,
594 soft_max=1000.0,
595 unit='LENGTH',
596 default=1.0
598 addendum: FloatProperty(name="Addendum",
599 description="Addendum, extent of tooth above radius",
600 soft_min=-1000.0,
601 soft_max=1000.0,
602 unit='LENGTH',
603 default=0.1
605 dedendum: FloatProperty(name="Dedendum",
606 description="Dedendum, extent of tooth below radius",
607 soft_min=-1000.0,
608 soft_max=1000.0,
609 unit='LENGTH',
610 default=0.1
612 angle: FloatProperty(name="Pressure Angle",
613 description="Pressure angle, skewness of tooth tip",
614 soft_min=radians(-45.0),
615 soft_max=radians(45.0),
616 unit='ROTATION',
617 default=radians(20.0)
619 base: FloatProperty(name="Base",
620 description="Base, extent of gear below radius",
621 soft_min=-1000.0,
622 soft_max=1000.0,
623 unit='LENGTH',
624 default=0.2
626 width: FloatProperty(name="Width",
627 description="Width, thickness of gear",
628 soft_min=-1000.0,
629 soft_max=1000.0,
630 unit='LENGTH',
631 default=0.2
633 skew: FloatProperty(name="Skewness",
634 description="Skew of teeth",
635 soft_min=radians(-360.0),
636 soft_max=radians(360.0),
637 unit='ROTATION',
638 default=radians(0.0)
640 conangle: FloatProperty(name="Conical angle",
641 description="Conical angle of gear",
642 soft_min=radians(-360.0),
643 soft_max=radians(360.0),
644 unit='ROTATION',
645 default=radians(0.0)
647 crown: FloatProperty(name="Crown",
648 description="Inward pointing extend of crown teeth",
649 soft_min=-1000.0,
650 soft_max=1000.0,
651 unit='LENGTH',
652 default=0.0
655 def draw(self, context):
656 layout = self.layout
658 box = layout.box()
659 box.prop(self, 'number_of_teeth')
661 box = layout.box()
662 box.prop(self, 'radius')
663 box.prop(self, 'width')
664 box.prop(self, 'base')
666 box = layout.box()
667 box.prop(self, 'dedendum')
668 box.prop(self, 'addendum')
670 box = layout.box()
671 box.prop(self, 'angle')
672 box.prop(self, 'skew')
673 box.prop(self, 'conangle')
674 box.prop(self, 'crown')
676 if self.change == False:
677 # generic transform props
678 box = layout.box()
679 box.prop(self, 'align', expand=True)
680 box.prop(self, 'location', expand=True)
681 box.prop(self, 'rotation', expand=True)
683 @classmethod
684 def poll(cls, context):
685 return context.scene is not None
687 def execute(self, context):
688 # turn off 'Enter Edit Mode'
689 use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode
690 bpy.context.preferences.edit.use_enter_edit_mode = False
692 if bpy.context.mode == "OBJECT":
693 if context.selected_objects != [] and context.active_object and \
694 (context.active_object.data is not None) and ('Gear' in context.active_object.data.keys()) and \
695 (self.change == True):
696 obj = context.active_object
697 oldmesh = obj.data
698 oldmeshname = obj.data.name
699 mesh, verts_tip, verts_valley = AddGearMesh(self, context)
700 obj.data = mesh
701 try:
702 bpy.ops.object.vertex_group_remove(all=True)
703 except:
704 pass
706 for material in oldmesh.materials:
707 obj.data.materials.append(material)
709 bpy.data.meshes.remove(oldmesh)
710 obj.data.name = oldmeshname
711 else:
712 mesh, verts_tip, verts_valley = AddGearMesh(self, context)
713 obj = object_utils.object_data_add(context, mesh, operator=self)
715 # Create vertex groups from stored vertices.
716 tipGroup = obj.vertex_groups.new(name='Tips')
717 tipGroup.add(verts_tip, 1.0, 'ADD')
719 valleyGroup = obj.vertex_groups.new(name='Valleys')
720 valleyGroup.add(verts_valley, 1.0, 'ADD')
722 obj.data["Gear"] = True
723 obj.data["change"] = False
724 for prm in GearParameters():
725 obj.data[prm] = getattr(self, prm)
727 if bpy.context.mode == "EDIT_MESH":
728 active_object = context.active_object
729 name_active_object = active_object.name
730 bpy.ops.object.mode_set(mode='OBJECT')
731 mesh, verts_tip, verts_valley = AddGearMesh(self, context)
732 obj = object_utils.object_data_add(context, mesh, operator=self)
734 # Create vertex groups from stored vertices.
735 tipGroup = obj.vertex_groups.new(name='Tips')
736 tipGroup.add(verts_tip, 1.0, 'ADD')
738 valleyGroup = obj.vertex_groups.new(name='Valleys')
739 valleyGroup.add(verts_valley, 1.0, 'ADD')
741 obj.select_set(True)
742 active_object.select_set(True)
743 bpy.context.view_layer.objects.active = active_object
744 bpy.ops.object.join()
745 context.active_object.name = name_active_object
746 bpy.ops.object.mode_set(mode='EDIT')
748 if use_enter_edit_mode:
749 bpy.ops.object.mode_set(mode = 'EDIT')
751 # restore pre operator state
752 bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode
754 return {'FINISHED'}
756 def invoke(self, context, event):
757 self.execute(context)
759 return {'FINISHED'}
761 def GearParameters():
762 GearParameters = [
763 "number_of_teeth",
764 "radius",
765 "addendum",
766 "dedendum",
767 "base",
768 "angle",
769 "width",
770 "skew",
771 "conangle",
772 "crown",
774 return GearParameters
776 def AddWormGearMesh(self, context):
778 verts, faces, verts_tip, verts_valley = add_worm(
779 self.number_of_teeth,
780 self.number_of_rows,
781 self.radius,
782 self.addendum,
783 self.dedendum,
784 self.angle,
785 width=self.row_height,
786 skew=self.skew,
787 crown=self.crown
790 mesh = bpy.data.meshes.new("Worm Gear")
791 mesh.from_pydata(verts, [], faces)
793 return mesh, verts_tip, verts_valley
796 class AddWormGear(Operator, object_utils.AddObjectHelper):
797 bl_idname = "mesh.primitive_worm_gear"
798 bl_label = "Add Worm Gear"
799 bl_description = "Construct a worm gear mesh"
800 bl_options = {'REGISTER', 'UNDO', 'PRESET'}
802 WormGear : BoolProperty(name = "WormGear",
803 default = True,
804 description = "WormGear")
806 #### change properties
807 name : StringProperty(name = "Name",
808 description = "Name")
810 change : BoolProperty(name = "Change",
811 default = False,
812 description = "change WormGear")
814 number_of_teeth: IntProperty(
815 name="Number of Teeth",
816 description="Number of teeth on the gear",
817 min=1,
818 soft_max=1000,
819 default=12
821 number_of_rows: IntProperty(
822 name="Number of Rows",
823 description="Number of rows on the worm gear",
824 min=0,
825 soft_max=1000,
826 default=32
828 radius: FloatProperty(
829 name="Radius",
830 description="Radius of the gear, negative for crown gear",
831 soft_min=-1000.0,
832 soft_max=1000.0,
833 unit='LENGTH',
834 default=1.0
836 addendum: FloatProperty(
837 name="Addendum",
838 description="Addendum, extent of tooth above radius",
839 soft_min=-1000.0,
840 soft_max=1000.0,
841 unit='LENGTH',
842 default=0.1
844 dedendum: FloatProperty(
845 name="Dedendum",
846 description="Dedendum, extent of tooth below radius",
847 soft_min=-1000.0,
848 soft_max=1000.0,
849 unit='LENGTH',
850 default=0.1
852 angle: FloatProperty(
853 name="Pressure Angle",
854 description="Pressure angle, skewness of tooth tip",
855 soft_min=radians(-45.0),
856 soft_max=radians(45.0),
857 default=radians(20.0),
858 unit='ROTATION'
860 row_height: FloatProperty(
861 name="Row Height",
862 description="Height of each Row",
863 soft_min=-1000.0,
864 soft_max=1000.0,
865 unit='LENGTH',
866 default=0.2
868 skew: FloatProperty(
869 name="Skewness per Row",
870 description="Skew of each row",
871 soft_min=radians(-360.0),
872 soft_max=radians(360.0),
873 default=radians(11.25),
874 unit='ROTATION'
876 crown: FloatProperty(
877 name="Crown",
878 description="Inward pointing extend of crown teeth",
879 soft_min=-1000.0,
880 soft_max=1000.0,
881 unit='LENGTH',
882 default=0.0
885 def draw(self, context):
886 layout = self.layout
887 box = layout.box()
888 box.prop(self, "number_of_teeth")
889 box.prop(self, "number_of_rows")
890 box.prop(self, "radius")
891 box.prop(self, "row_height")
893 box = layout.box()
894 box.prop(self, "addendum")
895 box.prop(self, "dedendum")
897 box = layout.box()
898 box.prop(self, "angle")
899 box.prop(self, "skew")
900 box.prop(self, "crown")
902 if self.change == False:
903 # generic transform props
904 box = layout.box()
905 box.prop(self, 'align', expand=True)
906 box.prop(self, 'location', expand=True)
907 box.prop(self, 'rotation', expand=True)
909 def execute(self, context):
910 # turn off 'Enter Edit Mode'
911 use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode
912 bpy.context.preferences.edit.use_enter_edit_mode = False
914 if bpy.context.mode == "OBJECT":
915 if context.selected_objects != [] and context.active_object and \
916 (context.active_object.data is not None) and ('WormGear' in context.active_object.data.keys()) and \
917 (self.change == True):
918 obj = context.active_object
919 oldmesh = obj.data
920 oldmeshname = obj.data.name
922 mesh, verts_tip, verts_valley = AddWormGearMesh(self, context)
923 obj.data = mesh
924 try:
925 bpy.ops.object.vertex_group_remove(all=True)
926 except:
927 pass
929 for material in oldmesh.materials:
930 obj.data.materials.append(material)
932 bpy.data.meshes.remove(oldmesh)
933 obj.data.name = oldmeshname
934 else:
935 mesh, verts_tip, verts_valley = AddWormGearMesh(self, context)
936 obj = object_utils.object_data_add(context, mesh, operator=self)
938 # Create vertex groups from stored vertices.
939 tipGroup = obj.vertex_groups.new(name = 'Tips')
940 tipGroup.add(verts_tip, 1.0, 'ADD')
942 valleyGroup = obj.vertex_groups.new(name = 'Valleys')
943 valleyGroup.add(verts_valley, 1.0, 'ADD')
945 obj.data["WormGear"] = True
946 obj.data["change"] = False
947 for prm in WormGearParameters():
948 obj.data[prm] = getattr(self, prm)
950 if bpy.context.mode == "EDIT_MESH":
951 active_object = context.active_object
952 name_active_object = active_object.name
953 bpy.ops.object.mode_set(mode='OBJECT')
954 mesh, verts_tip, verts_valley = AddWormGearMesh(self, context)
955 obj = object_utils.object_data_add(context, mesh, operator=self)
957 # Create vertex groups from stored vertices.
958 tipGroup = obj.vertex_groups.new(name = 'Tips')
959 tipGroup.add(verts_tip, 1.0, 'ADD')
961 valleyGroup = obj.vertex_groups.new(name = 'Valleys')
962 valleyGroup.add(verts_valley, 1.0, 'ADD')
964 obj.select_set(True)
965 active_object.select_set(True)
966 bpy.context.view_layer.objects.active = active_object
967 bpy.ops.object.join()
968 context.active_object.name = name_active_object
969 bpy.ops.object.mode_set(mode='EDIT')
971 if use_enter_edit_mode:
972 bpy.ops.object.mode_set(mode = 'EDIT')
974 # restore pre operator state
975 bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode
977 return {'FINISHED'}
979 def WormGearParameters():
980 WormGearParameters = [
981 "number_of_teeth",
982 "number_of_rows",
983 "radius",
984 "addendum",
985 "dedendum",
986 "angle",
987 "row_height",
988 "skew",
989 "crown",
991 return WormGearParameters