#!/usr/bin/env python3 # -*- coding: utf-8 -*- #************************************************************************** # Copyright (C) 2019, Paul Lutus * # * # 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. * # * # 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., * # 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * #************************************************************************** import math, re try: import bpy except: print("Error: No Blender libraries.") print("Run like this: blender -P %s" % __file__) quit() # use like this: $ blender -P create_adaptor.py # background: https://docs.blender.org/api/blender2.8/bpy.types.Mesh.html # JOINMODE must be False for reliable parts # if False, uses boolean operator UNION, # much better than JOIN JOINMODE = False def clear_all(): bpy.ops.object.select_all(action='SELECT') bpy.ops.object.delete() def create_bushing(rad,wt,w,vertices = 32): bpy.ops.mesh.primitive_cylinder_add( vertices=vertices, radius=rad+wt, depth=w, enter_editmode=False, location=(0, 0, 0) ) cyla = bpy.context.active_object bpy.ops.mesh.primitive_cylinder_add( vertices=vertices, radius=rad, depth=w*2, enter_editmode=False, location=(0, 0, 0) ) cylb = bpy.context.active_object bool_mod = cyla.modifiers.new(type="BOOLEAN",name="CutInterior") bool_mod.object = cylb bool_mod.operation = 'DIFFERENCE' bpy.ops.object.modifier_apply({"object": cyla},apply_as='DATA', modifier=bool_mod.name) bpy.data.objects.remove(cylb, do_unlink=True) return cyla def create_wall(irad,orad,wt,w,avg,angle,vertices = 32): x = avg * math.cos(angle) y = avg * math.sin(angle) bpy.ops.mesh.primitive_cube_add( size=1, enter_editmode=False, location=(x, y, 0) ) wall = bpy.context.active_object wall.scale = (orad-irad-wt*0.5,wt,w) wall.rotation_euler = (0,0,angle) return wall def build(odia,idia,wt,w,vertices = 32,supports = 4,name = "Filament Reel Adaptor",savepath = False): clear_all() orad = odia/2.0 irad = idia/2.0 - wt cyla = create_bushing(irad,wt,w,vertices) # for the Boolean union operation to work, # the two cylinder heights # must not lie on the same plane cylb = create_bushing(orad,wt,w*.9999,vertices) cylb.name = name avg = (orad+irad+wt)*0.5 # if supports are needed if abs(orad-irad) > wt: for n in range(supports): angle = float(n)/supports * 360 * math.pi/180 # the walls must also not be # on the same plane as the cylinders wall = create_wall(irad,orad,wt,w*.999,avg,angle,vertices) if not JOINMODE: sn = "mergewall%d" % n bool_mod = cylb.modifiers.new(type="BOOLEAN",name=sn) bool_mod.object = wall bool_mod.operation = 'UNION' bpy.ops.object.modifier_apply({"object": cylb},apply_as='DATA', modifier=bool_mod.name) bpy.data.objects.remove(wall, do_unlink=True) if not JOINMODE: bool_mod = cylb.modifiers.new(type="BOOLEAN",name="mergeouter") bool_mod.object = cyla bool_mod.operation = 'UNION' bpy.ops.object.modifier_apply({"object": cylb},apply_as='DATA', modifier=bool_mod.name) bpy.data.objects.remove(cyla, do_unlink=True) cylb.location = (0,0,0) if not JOINMODE: bpy.ops.object.select_all(action='DESELECT') # Deselect all objects bpy.context.view_layer.objects.active = cylb # Select our object cylb.select_set(True) else: # this method won't create a uniform, reliable part # it's only here for testing try: bpy.ops.object.select_by_type(type='MESH') bpy.ops.object.join() except: None # https://docs.blender.org/api/current/bpy.ops.export_mesh.html if savepath: path = savepath + "/" + re.sub(' +','_',name) + ".stl" bpy.ops.export_mesh.stl(filepath=path,check_existing=False,use_selection=True) def main(): # BEGIN user-defined values vertices = 64 supports = 4 wt = 2 # adaptor wall thickness mm # should not be be > width of filament reel w = 67 # adaptor width mm # outside diameter odia = 31.5 # QIDI external axle outer diameter mm (30) + 1.5mm for errors and shrinkage #odia = 51.5 # QIDI internal axle outer diameter + 1.5mm for errors and shrinkage # inside diameter idia = 72 # 3D Hero i.d. minus 1 for uncertainty (tested, fits) #idia = 33 # 3D Hero i.d. minus 1 for uncertainty (tested, fits) # END user-defined values build(odia,idia, wt,w,vertices,supports,"testing",".") if __name__ == "__main__": # run only if executed as an application main()