Node Wrangler: Add more specific poll methods
[blender-addons.git] / add_mesh_extra_objects / add_mesh_pyramid.py
blob5999d75a061f31d4d5c91e0d190360f2af41ddf8
1 # SPDX-FileCopyrightText: 2011-2022 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
5 # Author: Phil Cote, cotejrp1, (http://www.blenderaddons.com)
7 import bpy
8 import bmesh
9 from bpy.props import (
10 FloatProperty,
11 IntProperty,
12 StringProperty,
13 BoolProperty,
15 from math import pi
16 from mathutils import (
17 Quaternion,
18 Vector,
20 from bpy_extras import object_utils
23 def create_step(width, base_level, step_height, num_sides):
25 axis = [0, 0, -1]
26 PI2 = pi * 2
27 rad = width / 2
29 quat_angles = [(cur_side / num_sides) * PI2
30 for cur_side in range(num_sides)]
32 quaternions = [Quaternion(axis, quat_angle)
33 for quat_angle in quat_angles]
35 init_vectors = [Vector([rad, 0, base_level])] * len(quaternions)
37 quat_vector_pairs = list(zip(quaternions, init_vectors))
38 vectors = [quaternion @ vec for quaternion, vec in quat_vector_pairs]
39 bottom_list = [(vec.x, vec.y, vec.z) for vec in vectors]
40 top_list = [(vec.x, vec.y, vec.z + step_height) for vec in vectors]
41 full_list = bottom_list + top_list
43 return full_list
46 def split_list(l, n):
47 """
48 split the blocks up. Credit to oremj for this one.
49 http://stackoverflow.com/questions/312443/how-do-you-split-a-list-into-evenly-sized-chunks-in-python
50 """
51 n *= 2
52 returned_list = [l[i: i + n] for i in range(0, len(l), n)]
53 return returned_list
56 def get_connector_pairs(lst, n_sides):
57 # chop off the verts that get used for the base and top
58 lst = lst[n_sides:]
59 lst = lst[:-n_sides]
60 lst = split_list(lst, n_sides)
61 return lst
64 def pyramid_mesh(self, context):
65 all_verts = []
67 height_offset = 0
68 cur_width = self.width
70 for i in range(self.num_steps):
71 verts_loc = create_step(cur_width, height_offset, self.height,
72 self.num_sides)
73 height_offset += self.height
74 cur_width -= self.reduce_by
75 all_verts.extend(verts_loc)
77 mesh = bpy.data.meshes.new("Pyramid")
78 bm = bmesh.new()
80 for v_co in all_verts:
81 bm.verts.new(v_co)
83 def add_faces(n, block_vert_sets):
84 for bvs in block_vert_sets:
85 for i in range(self.num_sides - 1):
86 bm.faces.new([bvs[i], bvs[i + n], bvs[i + n + 1], bvs[i + 1]])
87 bm.faces.new([bvs[n - 1], bvs[(n * 2) - 1], bvs[n], bvs[0]])
89 # get the base and cap faces done.
90 bm.faces.new(bm.verts[0:self.num_sides])
91 bm.faces.new(reversed(bm.verts[-self.num_sides:])) # otherwise normal faces intern... T44619.
93 # side faces
94 block_vert_sets = split_list(bm.verts, self.num_sides)
95 add_faces(self.num_sides, block_vert_sets)
97 # connector faces between faces and faces of the block above it.
98 connector_pairs = get_connector_pairs(bm.verts, self.num_sides)
99 add_faces(self.num_sides, connector_pairs)
101 bm.to_mesh(mesh)
102 mesh.update()
104 return mesh
107 class AddPyramid(bpy.types.Operator, object_utils.AddObjectHelper):
108 bl_idname = "mesh.primitive_steppyramid_add"
109 bl_label = "Pyramid"
110 bl_description = "Construct a step pyramid mesh"
111 bl_options = {'REGISTER', 'UNDO', 'PRESET'}
113 Pyramid : BoolProperty(name = "Pyramid",
114 default = True,
115 description = "Pyramid")
116 change : BoolProperty(name = "Change",
117 default = False,
118 description = "change Pyramid")
120 num_sides: IntProperty(
121 name="Number Sides",
122 description="How many sides each step will have",
123 min=3,
124 default=4
126 num_steps: IntProperty(
127 name="Number of Steps",
128 description="How many steps for the overall pyramid",
129 min=1,
130 default=10
132 width: FloatProperty(
133 name="Initial Width",
134 description="Initial base step width",
135 min=0.01,
136 default=2
138 height: FloatProperty(
139 name="Height",
140 description="How tall each step will be",
141 min=0.01,
142 default=0.1
144 reduce_by: FloatProperty(
145 name="Reduce Step By",
146 description="How much to reduce each succeeding step by",
147 min=.01,
148 default=.20
151 def draw(self, context):
152 layout = self.layout
154 layout.prop(self, 'num_sides', expand=True)
155 layout.prop(self, 'num_steps', expand=True)
156 layout.prop(self, 'width', expand=True)
157 layout.prop(self, 'height', expand=True)
158 layout.prop(self, 'reduce_by', expand=True)
160 if self.change == False:
161 col = layout.column(align=True)
162 col.prop(self, 'align', expand=True)
163 col = layout.column(align=True)
164 col.prop(self, 'location', expand=True)
165 col = layout.column(align=True)
166 col.prop(self, 'rotation', expand=True)
168 def execute(self, context):
169 # turn off 'Enter Edit Mode'
170 use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode
171 bpy.context.preferences.edit.use_enter_edit_mode = False
173 if bpy.context.mode == "OBJECT":
174 if context.selected_objects != [] and context.active_object and \
175 (context.active_object.data is not None) and ('Pyramid' in context.active_object.data.keys()) and \
176 (self.change == True):
177 obj = context.active_object
178 oldmesh = obj.data
179 oldmeshname = obj.data.name
180 obj.data = pyramid_mesh(self, context)
181 for material in oldmesh.materials:
182 obj.data.materials.append(material)
183 bpy.data.meshes.remove(oldmesh)
184 obj.data.name = oldmeshname
185 else:
186 mesh = pyramid_mesh(self, context)
187 obj = object_utils.object_data_add(context, mesh, operator=self)
189 obj.data["Pyramid"] = True
190 obj.data["change"] = False
191 for prm in PyramidParameters():
192 obj.data[prm] = getattr(self, prm)
194 if bpy.context.mode == "EDIT_MESH":
195 active_object = context.active_object
196 name_active_object = active_object.name
197 bpy.ops.object.mode_set(mode='OBJECT')
198 mesh = pyramid_mesh(self, context)
199 obj = object_utils.object_data_add(context, mesh, operator=self)
200 obj.select_set(True)
201 active_object.select_set(True)
202 bpy.context.view_layer.objects.active = active_object
203 bpy.ops.object.join()
204 context.active_object.name = name_active_object
205 bpy.ops.object.mode_set(mode='EDIT')
207 if use_enter_edit_mode:
208 bpy.ops.object.mode_set(mode = 'EDIT')
210 # restore pre operator state
211 bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode
213 return {'FINISHED'}
215 def PyramidParameters():
216 PyramidParameters = [
217 "num_sides",
218 "num_steps",
219 "width",
220 "height",
221 "reduce_by",
223 return PyramidParameters