-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathweight_calculator_addon.py
390 lines (327 loc) · 13.3 KB
/
weight_calculator_addon.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
# 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 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
bl_info = {
"name": "Weight Calculator Add-on",
"blender": (3, 0, 0),
"category": "Object",
"version": (1, 0, 0),
"author": "pafurijaz",
"description": "A Blender add-on to calculate the weight of selected mesh objects based on their material.",
}
import bpy
from mathutils import Matrix
import bmesh
# List of supported materials with their names
materials = [
"iron", "steel", "stainless_steel", "aluminum", "copper", "brass", "bronze",
"titanium", "gold", "silver", "lead", "zinc", "magnesium", "nickel",
"plastic", "wood", "concrete", "glass", "brick", "marble", "granite",
"carbon_fiber", "foam", "rubber", "uranium", "mercury", "diamond"
]
def is_solid_mesh(obj):
"""
Check if the given object is a solid mesh.
:param obj: The Blender object to check.
:return: True if the object is a solid mesh, False otherwise.
"""
if obj.type != 'MESH':
return False
mesh = obj.data
bm = bmesh.new()
bm.from_mesh(mesh)
# Check for solid faces (faces that are not split or non-planar)
for face in bm.faces:
if len(face.verts) < 3:
continue
if face.calc_area() == 0:
return False
# Check for manifold edges (edges shared by exactly two faces)
for edge in bm.edges:
if not edge.is_manifold:
return False
bm.free()
return True
def get_mesh_volume(mesh, unit_conversion_factor):
"""
Calculate the volume of the given mesh.
:param mesh: The Blender mesh data.
:param unit_conversion_factor: Factor to convert the volume to the desired units.
:return: The volume of the mesh.
"""
volume = 0.0
bm = bmesh.new()
bm.from_mesh(mesh)
for face in bm.faces:
centroid = face.calc_center_median()
normal = face.normal
area = face.calc_area()
# Use a plane equation to calculate the signed distance from the origin
A, B, C, D = normal[0], normal[1], normal[2], -centroid.dot(normal)
volume += abs(D) * area / 3
bm.free()
return volume * unit_conversion_factor
def get_material_density(material_name):
"""
Get the density of the specified material.
:param material_name: The name of the material.
:return: The density of the material in kg/m^3.
"""
material_densities = {
"iron": 7850,
"steel": 7850,
"stainless_steel": 8000,
"aluminum": 2700,
"copper": 8960,
"brass": 8500,
"bronze": 8730,
"titanium": 4500,
"gold": 19300,
"silver": 10500,
"lead": 11340,
"zinc": 7130,
"magnesium": 1738,
"nickel": 8908,
"plastic": 1200,
"wood": 600,
"concrete": 2400,
"glass": 2500,
"brick": 1922,
"marble": 2700,
"granite": 2691,
"carbon_fiber": 1800,
"foam": 100,
"rubber": 1500,
"uranium": 19050,
"mercury": 13534,
"diamond": 3500
}
return material_densities.get(material_name.lower(), 1000)
def get_custom_material_density():
"""
Retrieve the custom material density from the scene property.
:return: The custom material density in kg/m^3.
"""
return bpy.context.scene.custom_density
def get_custom_volume(unit_conversion_factor):
"""
Retrieve the custom volume from the scene property.
:param unit_conversion_factor: Factor to convert the volume to the desired units.
:return: The custom volume.
"""
return bpy.context.scene.custom_volume * unit_conversion_factor
def get_mesh_weight(mesh, use_custom_density=False, use_custom_volume=False, unit_conversion_factor=1.0, density_conversion_factor=1.0):
"""
Calculate the weight of the given mesh.
:param mesh: The Blender mesh data.
:param use_custom_density: Whether to use a custom density.
:param use_custom_volume: Whether to use a custom volume.
:param unit_conversion_factor: Factor to convert the volume to the desired units.
:param density_conversion_factor: Factor to convert the density to the desired units.
:return: The weight of the mesh.
"""
if use_custom_volume:
volume = get_custom_volume(unit_conversion_factor)
else:
volume = get_mesh_volume(mesh, unit_conversion_factor)
if use_custom_density:
density = get_custom_material_density()
else:
density = get_material_density(bpy.context.scene.material_type)
return volume * density * density_conversion_factor
class OBJECT_PT_weight_calculator(bpy.types.Panel):
"""
Panel for the Weight Calculator UI.
"""
bl_label = "Weight Calculator"
bl_idname = "OBJECT_PT_weight_calculator"
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_category = 'Tool'
def draw(self, context):
layout = self.layout
scene = context.scene
row = layout.row()
row.prop(scene, "material_type")
# Option to use custom density
row = layout.row()
row.prop(scene, "use_custom_density", text="Use Custom Density")
if scene.use_custom_density:
row = layout.row()
row.prop(scene, "custom_density", text="Density (kg/m^3)")
# Option to use custom volume
row = layout.row()
row.prop(scene, "use_custom_volume", text="Use Custom Volume")
if scene.use_custom_volume:
row = layout.row()
row.prop(scene, "custom_volume", text="Volume (m^3)")
# Unit system selection
row = layout.row()
row.prop(scene, "unit_system", text="Unit System")
# Unit selection
row = layout.row()
row.prop(scene, "length_unit", text="Length Unit")
# Button to calculate volume
row = layout.row()
row.operator("object.volume_calculate", text="Calculate Volume")
# Button to calculate weight
row = layout.row()
row.operator("object.weight_calculate", text="Calculate Weight")
class OBJECT_OT_calculate_weight(bpy.types.Operator):
"""
Operator to calculate the weight of selected objects.
"""
bl_idname = "object.weight_calculate"
bl_label = "Calculate Weight"
def execute(self, context):
# Check if any objects are selected
if not bpy.context.selected_objects:
self.report({'ERROR'}, "No object is selected. Please select a valid mesh object.")
return {'CANCELLED'}
# Filter to get only mesh objects that are solid
solid_meshes = [obj for obj in context.selected_objects if is_solid_mesh(obj)]
if not solid_meshes:
self.report({'ERROR'}, "No solid mesh objects selected.")
return {'CANCELLED'}
use_custom_density = context.scene.use_custom_density
use_custom_volume = context.scene.use_custom_volume
# Get unit conversion factors
unit_conversion_factor, density_conversion_factor, weight_unit, volume_unit = get_unit_conversion_factors(context.scene.unit_system, context.scene.length_unit)
# Calculate and report the weight using the mesh data
total_weight = 0.0
for obj in solid_meshes:
mesh = obj.data
weight = get_mesh_weight(mesh, use_custom_density, use_custom_volume, unit_conversion_factor, density_conversion_factor)
total_weight += weight
self.report({'INFO'}, f"Total Weight: {total_weight:.6f} {weight_unit}")
return {'FINISHED'}
class OBJECT_OT_calculate_volume(bpy.types.Operator):
"""
Operator to calculate the volume of selected objects.
"""
bl_idname = "object.volume_calculate"
bl_label = "Calculate Volume"
def execute(self, context):
# Check if any objects are selected
if not bpy.context.selected_objects:
self.report({'ERROR'}, "No object is selected. Please select a valid mesh object.")
return {'CANCELLED'}
# Filter to get only mesh objects that are solid
solid_meshes = [obj for obj in context.selected_objects if is_solid_mesh(obj)]
if not solid_meshes:
self.report({'ERROR'}, "No solid mesh objects selected.")
return {'CANCELLED'}
# Get unit conversion factors
unit_conversion_factor, density_conversion_factor, weight_unit, volume_unit = get_unit_conversion_factors(context.scene.unit_system, context.scene.length_unit)
# Calculate and report the volume using the mesh data
total_volume = 0.0
for obj in solid_meshes:
mesh = obj.data
volume = get_mesh_volume(mesh, unit_conversion_factor)
total_volume += volume
self.report({'INFO'}, f"Total Volume: {total_volume:.6f} {volume_unit}")
return {'FINISHED'}
def get_unit_conversion_factors(unit_system, length_unit):
"""
Get the unit conversion factors based on the selected unit system and length unit.
:param unit_system: The selected unit system (METRIC or IMPERIAL).
:param length_unit: The selected length unit.
:return: Tuple containing volume conversion factor, density conversion factor, weight unit, and volume unit.
"""
if unit_system == 'METRIC':
if length_unit == 'MILLIMETERS':
return 1e-9, 1e3, 'g', 'mm³' # mm^3 to m^3, kg to g
elif length_unit == 'CENTIMETERS':
return 1e-6, 1e3, 'g', 'cm³' # cm^3 to m^3, kg to g
elif length_unit == 'METERS':
return 1.0, 1.0, 'kg', 'm³' # m^3 to m^3, kg to kg
elif unit_system == 'IMPERIAL':
if length_unit == 'INCHES':
return 1.63871e-5, 35.27396, 'oz', 'in³' # in^3 to m^3, kg to oz
elif length_unit == 'FEET':
return 0.0283168, 2.20462, 'lb', 'ft³' # ft^3 to m^3, kg to lb
return 1.0, 1.0, 'kg', 'm³' # Default to no conversion
def register():
"""
Register the classes and properties for the add-on.
"""
bpy.utils.register_class(OBJECT_PT_weight_calculator)
bpy.utils.register_class(OBJECT_OT_calculate_weight)
bpy.utils.register_class(OBJECT_OT_calculate_volume) # Register the new operator
# Register the scene properties for material type, use custom density, and custom density value
bpy.types.Scene.material_type = bpy.props.EnumProperty(
name="Material Type",
description="Select a material type to calculate its weight",
items=[(mat, mat.capitalize(), "") for mat in materials],
default="iron"
)
bpy.types.Scene.use_custom_density = bpy.props.BoolProperty(
name="Use Custom Density",
description="Enable custom density input",
default=False
)
bpy.types.Scene.custom_density = bpy.props.FloatProperty(
name="Density (kg/m^3)",
description="Enter the custom material density in kg/m^3",
min=1.0,
default=2500.0 # Default to a reasonable value for most materials
)
bpy.types.Scene.use_custom_volume = bpy.props.BoolProperty(
name="Use Custom Volume",
description="Enable custom volume input",
default=False
)
bpy.types.Scene.custom_volume = bpy.props.FloatProperty(
name="Volume (m^3)",
description="Enter the custom volume in m^3",
min=0.0,
default=1.0 # Default to a reasonable value
)
bpy.types.Scene.unit_system = bpy.props.EnumProperty(
name="Unit System",
description="Select the unit system",
items=[
('METRIC', 'Metric', ''),
('IMPERIAL', 'Imperial', '')
],
default='METRIC'
)
bpy.types.Scene.length_unit = bpy.props.EnumProperty(
name="Length Unit",
description="Select the length unit",
items=[
('MILLIMETERS', 'Millimeters', ''),
('CENTIMETERS', 'Centimeters', ''),
('METERS', 'Meters', ''),
('INCHES', 'Inches', ''),
('FEET', 'Feet', '')
],
default='MILLIMETERS'
)
def unregister():
"""
Unregister the classes and properties for the add-on.
"""
bpy.utils.unregister_class(OBJECT_PT_weight_calculator)
bpy.utils.unregister_class(OBJECT_OT_calculate_weight)
bpy.utils.unregister_class(OBJECT_OT_calculate_volume) # Unregister the new operator
# Unregister the scene properties
del bpy.types.Scene.material_type
del bpy.types.Scene.use_custom_density
del bpy.types.Scene.custom_density
del bpy.types.Scene.use_custom_volume
del bpy.types.Scene.custom_volume
del bpy.types.Scene.unit_system
del bpy.types.Scene.length_unit
# Register the classes
if __name__ == "__main__":
register()