├── MendelMax.py ├── MendelMax.stp ├── README ├── build ├── ExtrusionClip.stl ├── LowerVertex.stl ├── LowerVertexMiddle.stl ├── MendelMax.stl ├── README ├── RodLatch.stl ├── TSlotNut.stl ├── TopVertexX.stl ├── YIdlerMount.stl ├── YMotorMount.stl ├── YRodMount.stl ├── ZMotorMount.stl ├── ZRodLowerMount.stl └── compatibility │ ├── README │ ├── RodLatch-Thingiverse-YRodClasp-20.25mm.stl │ └── RodLatch-Thingiverse-ZRodTopLatch-26mm.stl ├── include ├── README └── RegularPolygon.py └── partsrc ├── LowerVertex.py ├── LowerVertexMiddle.py ├── README ├── RodLatch.py ├── TopVertexX.py ├── YIdlerMount.py ├── YMotorMount.py ├── YRodMount.py ├── ZMotorMount.py ├── ZRodLowerMount.py ├── __init__.py ├── accessories ├── ExtrusionClip.py └── TSlotNut.py └── optional_assembly └── README /MendelMax.py: -------------------------------------------------------------------------------- 1 | from __future__ import division # allows floating point division from integers 2 | import FreeCAD, Part, math 3 | from FreeCAD import Base 4 | 5 | #TODO: make all of this a package so we can do local imports without hacking the path 6 | import sys, os 7 | try: 8 | path = os.path.dirname(__file__) 9 | i = sys.path.index(path) 10 | except: 11 | sys.path.append(path) 12 | 13 | def MendelMax(): 14 | # Determines whether we are just generating the model, or if we are exporting the STL's as we go 15 | # 0 = No STL's 16 | # 1 = Export STL's 17 | export_stl = 0 18 | 19 | #all measurements are in mm until unit conversion is added 20 | 21 | #extrusion_profile 20mm, 25mm, 1" 22 | extrusion_profile = 20 23 | 24 | #how tall is the gap between the two bottom frame rectangles? 25 | frame_rectangle_spacing = 30 26 | 27 | #size_type ["extrusion length", "build area", "bounding box"] 28 | size_type = "extrusion length" 29 | 30 | extrusion_x_length = 300 31 | extrusion_y_length = 420 32 | extrusion_diagonal_length = 340 33 | 34 | #build_x_length, build_y_width, build_z_height (build area) 35 | #outside_x_length, outside_y_width, outsize_z_height (bounding box) 36 | 37 | #x_rail_type ["makerslide", "smooth rod"] 38 | x_rail_type = "smooth rod" 39 | x_rod_diameter = 8 40 | x_rod_spacing = 50 41 | #x_rod_orientation ["horizontal","vertical"] 42 | x_rod_orientation = "horizontal" 43 | 44 | #y_rail_type ["makerslide", "smooth rod"] 45 | y_rail_type = "smooth rod" 46 | y_rod_diameter = 8 47 | y_rod_spacing = 100 48 | 49 | #z_rail_type ["makerslide", "smooth rod"] 50 | z_rail_type = "smooth rod" 51 | z_rod_diameter = 8 52 | z_screw_diameter = 8 53 | #distance from z smooth rod to z threaded rod 54 | z_rod_spacing = 30 55 | 56 | #["NEMA14", "NEMA17", "NEMA23"] 57 | #TODO: implement motor module for dimensions, etc 58 | x_motor_size = "NEMA17" 59 | y_motor_size = "NEMA17" 60 | z_motor_size = "NEMA17" 61 | 62 | x_pulley_teeth_count = 36 63 | x_pulley_teeth_spacing = 2 64 | x_pulley_pitch_diameter = 72/math.pi 65 | y_pulley_teeth_count = 36 66 | y_pulley_teeth_spacing = 2 67 | y_pulley_pitch_diameter = 72/math.pi 68 | 69 | #should we limit parts to 100mmx100mm build area? 70 | small_printer = False 71 | 72 | #bushing_type ["printed","IGUS [part#]"LM8UU",etc] 73 | #y_carriage_height, y_carriage_width, y_carriage_depth 74 | #x_bearing_type, y_bearing_type ["608ZZ"] 75 | #build_platform_height, build_platform_width, build_platform_depth 76 | 77 | #absolute minimum thickness of any printed part, this is usually a constraint of the printing method to be used 78 | thick_min = 1 79 | #minimum vertical (the layered direction) thickness of any printed part 80 | thick_min_vertical = 2 81 | #minimum thickness of a part under compression, such as the edge of bolt holes 82 | thick_compress = 3 83 | #typical thickness of printed parts, for flat plates and such 84 | thick_typical = 4.25 85 | 86 | #TODO: better system for organizing these 87 | hole_spacing_narrow = 10 88 | hole_spacing_medium = 20 89 | hole_spacing_wide = 30 90 | 91 | # .-------. .-------. - 92 | # | | <-A-> | | B 93 | # | .----- -----. | - - 94 | # | | <-C-> | | _ D ^ 95 | # | \ / | | 96 | # | \ / | E 97 | # | \ / | v 98 | # | ----<-F->---- | - 99 | # 100 | # <--extrusion_profile--> 101 | 102 | extrusion_slot_opening_width = 6 #A 103 | extrusion_slot_opening_depth = 2 #B 104 | extrusion_slot_width = 11 #C 105 | extrusion_slot_vertical_depth = 1 #D 106 | extrusion_slot_depth = 4 #E 107 | 108 | #TODO: describe the profile more thoroughly 109 | extrusion_outline = Part.makePolygon([Base.Vector(0,0,0),Base.Vector(0,extrusion_profile,0),Base.Vector(extrusion_profile,extrusion_profile,0),Base.Vector(extrusion_profile,0,0),Base.Vector(0,0,0)]) 110 | extrusion_face = Part.Face(extrusion_outline) 111 | 112 | #TODO: learn how to initialize this object correctly 113 | extrusions = Part.makeBox(1,1,1) 114 | 115 | x_extrusion_lower = extrusion_face.extrude(Base.Vector(0,0,extrusion_x_length)) 116 | x_extrusion_lower.rotate(Base.Vector(extrusion_profile/2,0,extrusion_profile/2),Base.Vector(0,1,0),90) 117 | extrusions = extrusions.fuse(x_extrusion_lower) 118 | x_extrusion_lower.translate(Base.Vector(0,0,frame_rectangle_spacing+extrusion_profile)) 119 | extrusions = extrusions.fuse(x_extrusion_lower) 120 | x_extrusion_lower.translate(Base.Vector(0,extrusion_y_length+extrusion_profile,0)) 121 | extrusions = extrusions.fuse(x_extrusion_lower) 122 | x_extrusion_lower.translate(Base.Vector(0,0,-(frame_rectangle_spacing+extrusion_profile))) 123 | extrusions = extrusions.fuse(x_extrusion_lower) 124 | 125 | y_extrusion = extrusion_face.extrude(Base.Vector(0,0,extrusion_y_length)) 126 | y_extrusion.rotate(Base.Vector(0,extrusion_profile/2,extrusion_profile/2),Base.Vector(1,0,0),-90) 127 | y_extrusion.translate(Base.Vector(0,extrusion_profile,0)) 128 | extrusions = extrusions.fuse(y_extrusion) 129 | y_extrusion.translate(Base.Vector(0,0,frame_rectangle_spacing+extrusion_profile)) 130 | extrusions = extrusions.fuse(y_extrusion) 131 | y_extrusion.translate(Base.Vector(extrusion_x_length-extrusion_profile,0,0)) 132 | extrusions = extrusions.fuse(y_extrusion) 133 | y_extrusion.translate(Base.Vector(0,0,-(frame_rectangle_spacing+extrusion_profile))) 134 | extrusions = extrusions.fuse(y_extrusion) 135 | 136 | diagonal_extrusion = extrusion_face.extrude(Base.Vector(0,0,extrusion_diagonal_length)) 137 | diagonal_extrusion.translate(Base.Vector(0,0,frame_rectangle_spacing+extrusion_profile*2+15)) 138 | diagonal_extrusion.rotate(Base.Vector(0,extrusion_profile,frame_rectangle_spacing+extrusion_profile*2),Base.Vector(1,0,0),-30) 139 | extrusions = extrusions.fuse(diagonal_extrusion) 140 | diagonal_extrusion.translate(Base.Vector(extrusion_x_length-extrusion_profile,0,0)) 141 | extrusions = extrusions.fuse(diagonal_extrusion) 142 | diagonal_extrusion.rotate(Base.Vector(extrusion_x_length-extrusion_profile/2,(extrusion_y_length+extrusion_profile*2)/2,0),Base.Vector(0,0,1),180) 143 | extrusions = extrusions.fuse(diagonal_extrusion) 144 | diagonal_extrusion.translate(Base.Vector(-(extrusion_x_length-extrusion_profile),0,0)) 145 | extrusions = extrusions.fuse(diagonal_extrusion) 146 | 147 | x_extrusion_upper = extrusion_face.extrude(Base.Vector(0,0,extrusion_x_length+120)) 148 | x_extrusion_upper.rotate(Base.Vector(extrusion_profile/2,0,extrusion_profile/2),Base.Vector(0,1,0),90) 149 | x_extrusion_upper.translate(Base.Vector(-60,0,frame_rectangle_spacing+extrusion_profile*2+15+extrusion_diagonal_length)) 150 | x_extrusion_upper.rotate(Base.Vector(0,extrusion_profile,frame_rectangle_spacing+extrusion_profile*2),Base.Vector(1,0,0),-30) 151 | extrusions = extrusions.fuse(x_extrusion_upper) 152 | x_extrusion_upper.rotate(Base.Vector(extrusion_x_length/2,(extrusion_y_length+extrusion_profile*2)/2,0),Base.Vector(0,0,1),180) 153 | extrusions = extrusions.fuse(x_extrusion_upper) 154 | 155 | Part.show(extrusions) 156 | 157 | y_rod = Part.makeCylinder(y_rod_diameter/2,extrusion_y_length,Base.Vector(0,0,0),Base.Vector(0,1,0)) 158 | y_rod.translate(Base.Vector(extrusion_x_length/2-y_rod_spacing/2,extrusion_profile,(frame_rectangle_spacing+extrusion_profile*2)/2)) 159 | Part.show(y_rod) 160 | y_rod.translate(Base.Vector(y_rod_spacing,0,0)) 161 | Part.show(y_rod) 162 | 163 | import partsrc.LowerVertexMiddle 164 | import partsrc.LowerVertex 165 | import partsrc.RodLatch 166 | import partsrc.TopVertexX 167 | import partsrc.YIdlerMount 168 | import partsrc.YMotorMount 169 | import partsrc.YRodMount 170 | import partsrc.ZMotorMount 171 | import partsrc.ZRodLowerMount 172 | 173 | #TODO: figure out or fix mirroring so that we can avoid code duplication in all of these translations 174 | lvm = partsrc.LowerVertexMiddle.LowerVertexMiddle(extrusion_profile, frame_rectangle_spacing, thick_typical) 175 | lvm.rotate(Base.Vector(0,0,0),Base.Vector(0,1,0),90) 176 | lvm.translate(Base.Vector(0,0,frame_rectangle_spacing+extrusion_profile*2)) 177 | Part.show(lvm) 178 | lvm.translate(Base.Vector(extrusion_x_length-extrusion_profile,0,0)) 179 | Part.show(lvm) 180 | lvm.rotate(Base.Vector(extrusion_x_length-extrusion_profile/2,(extrusion_y_length+extrusion_profile*2)/2,0),Base.Vector(0,0,1),180) 181 | Part.show(lvm) 182 | lvm.translate(Base.Vector(-(extrusion_x_length-extrusion_profile),0,0)) 183 | Part.show(lvm) 184 | 185 | lv = partsrc.LowerVertex.LowerVertex(extrusion_profile, frame_rectangle_spacing, hole_spacing_medium, thick_typical) 186 | lv.rotate(Base.Vector(0,0,0),Base.Vector(0,1,0),90) 187 | lv.translate(Base.Vector(-thick_typical,0,frame_rectangle_spacing+extrusion_profile*2)) 188 | Part.show(lv) 189 | lv.translate(Base.Vector(extrusion_x_length+thick_typical,0,0)) 190 | Part.show(lv) 191 | lv.rotate(Base.Vector(extrusion_x_length+thick_typical/2,(extrusion_y_length+extrusion_profile*2)/2,0),Base.Vector(0,0,1),180) 192 | Part.show(lv) 193 | lv.translate(Base.Vector(-(extrusion_x_length+thick_typical),0,0)) 194 | Part.show(lv) 195 | 196 | tvx = partsrc.TopVertexX.TopVertexX(extrusion_profile, thick_typical, hole_spacing_medium) 197 | tvx.rotate(Base.Vector(0,0,0),Base.Vector(1,0,0),-90) 198 | tvx.translate(Base.Vector(0,-thick_typical,frame_rectangle_spacing+extrusion_profile*3+15+extrusion_diagonal_length)) 199 | tvx.rotate(Base.Vector(0,extrusion_profile,frame_rectangle_spacing+extrusion_profile*2),Base.Vector(1,0,0),-30) 200 | Part.show(tvx) 201 | tvx.rotate(Base.Vector(extrusion_x_length/2,(extrusion_y_length+extrusion_profile*2)/2,0),Base.Vector(0,0,1),180) 202 | Part.show(tvx) 203 | tvx = partsrc.TopVertexX.TopVertexX() 204 | tvx.rotate(Base.Vector(0,0,0),Base.Vector(1,0,0),-90) 205 | tvx.rotate(Base.Vector(0,0,0),Base.Vector(0,1,0),90) 206 | tvx.translate(Base.Vector(extrusion_x_length,-thick_typical,frame_rectangle_spacing+extrusion_profile*3+15+extrusion_diagonal_length)) 207 | tvx.rotate(Base.Vector(0,extrusion_profile,frame_rectangle_spacing+extrusion_profile*2),Base.Vector(1,0,0),-30) 208 | Part.show(tvx) 209 | tvx.rotate(Base.Vector(extrusion_x_length/2,(extrusion_y_length+extrusion_profile*2)/2,0),Base.Vector(0,0,1),180) 210 | Part.show(tvx) 211 | 212 | yim = partsrc.YIdlerMount.YIdlerMount(extrusion_profile, frame_rectangle_spacing, thick_typical) 213 | yim.rotate(Base.Vector(0,0,0),Base.Vector(1,0,0),90) 214 | yim.rotate(Base.Vector(0,0,0),Base.Vector(0,1,0),-90) 215 | yim.translate(Base.Vector(extrusion_x_length/2-10,0,0)) 216 | Part.show(yim) 217 | yim.rotate(Base.Vector(extrusion_x_length/2,0,(frame_rectangle_spacing+extrusion_profile*2)/2),Base.Vector(0,1,0),180) 218 | Part.show(yim) 219 | 220 | ymm = partsrc.YMotorMount.YMotorMount(extrusion_profile, frame_rectangle_spacing, thick_typical, thick_compress) 221 | ymm.rotate(Base.Vector(0,0,0),Base.Vector(1,0,0),90) 222 | ymm.rotate(Base.Vector(0,0,0),Base.Vector(0,1,0),-90) 223 | ymm.translate(Base.Vector(extrusion_x_length/2+20+ymm.BoundBox.YLength-thick_typical,0,0)) 224 | ymm.rotate(Base.Vector(extrusion_x_length/2,(extrusion_y_length+extrusion_profile*2)/2,(frame_rectangle_spacing+extrusion_profile*2)/2),Base.Vector(0,0,1),180) 225 | Part.show(ymm) 226 | 227 | yrm = partsrc.YRodMount.YRodMount(extrusion_profile, y_rail_type, y_rod_diameter, y_rod_spacing, frame_rectangle_spacing, thick_typical, thick_compress, thick_min, hole_spacing_wide) 228 | yrm.rotate(Base.Vector(0,0,0),Base.Vector(1,0,0),90) 229 | yrm.rotate(Base.Vector(0,extrusion_profile/2,(frame_rectangle_spacing+extrusion_profile*2)/2),Base.Vector(1,0,0),180) 230 | yrm.translate(Base.Vector(extrusion_x_length/2-y_rod_spacing/2,0,0)) 231 | Part.show(yrm) 232 | yrm.rotate(Base.Vector(extrusion_x_length/2,(extrusion_y_length+extrusion_profile*2)/2,(frame_rectangle_spacing+extrusion_profile*2)/2),Base.Vector(0,0,1),180) 233 | Part.show(yrm) 234 | 235 | zrlm = partsrc.ZRodLowerMount.ZRodLowerMount(extrusion_profile, z_rod_spacing, z_rod_diameter, z_screw_diameter, hole_spacing_wide, thick_typical, thick_min) 236 | zrlm.rotate(Base.Vector(0,0,0),Base.Vector(0,1,0),-90) 237 | zrlm.rotate(Base.Vector(0,extrusion_profile,0),Base.Vector(1,0,0),-90) 238 | zrlm.translate(Base.Vector(0,extrusion_y_length/2-zrlm.BoundBox.YLength/2)) 239 | Part.show(zrlm) 240 | zrlm.rotate(Base.Vector(extrusion_x_length/2,(extrusion_y_length+extrusion_profile*2)/2,0),Base.Vector(0,0,1),180) 241 | Part.show(zrlm) 242 | 243 | zmm = partsrc.ZMotorMount.ZMotorMount(z_rail_type, z_rod_diameter, z_rod_spacing, extrusion_profile, extrusion_y_length, extrusion_diagonal_length, hole_spacing_wide, thick_typical, thick_compress) 244 | zmm.rotate(Base.Vector(0,0,0),Base.Vector(0,0,1),90) 245 | zmm.rotate(Base.Vector(0,0,0),Base.Vector(0,1,0),90) 246 | zmm.translate(Base.Vector( 247 | -60-thick_typical, 248 | (extrusion_y_length+extrusion_profile*2)/2-zmm.BoundBox.YLength/2, 249 | (extrusion_diagonal_length+15)*math.cos(math.radians(30))+frame_rectangle_spacing+extrusion_profile*2 250 | )) 251 | Part.show(zmm) 252 | zmm.rotate(Base.Vector(extrusion_x_length/2,(extrusion_y_length+extrusion_profile*2)/2,0),Base.Vector(0,0,1),180) 253 | Part.show(zmm) 254 | 255 | #TODO: place all 8 rod latches in the correct positions 256 | #partsrc.RodLatch.RodLatch(z_rod_diameter, hole_spacing_wide, thick_typical, thick_compress) 257 | #partsrc.RodLatch.RodLatch(y_rod_diameter, hole_spacing_wide, thick_typical, thick_compress) 258 | 259 | #TODO: add Z rods 260 | #TODO: add motors 261 | #TODO: add bolts, probably in the form of a toggle when generating the parts 262 | #TODO: add Y idler hardware 263 | 264 | MendelMax() -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | This is FreeCAD code to generate the parts for a MendelMax. At present this is VERY incomplete, but it is a work in 2 | progress. If you just want to print a MendelMax, your best bet for now is to use the STL's from Thingiverse. 3 | 4 | To install, download the files and copy the entire install (omitting the build directory if you wish) to your FreeCAD 5 | Macro Directory (Defined on the Macro tab of the FreeCAD preferences). The files should remain in their existing folders. 6 | Once installed, launch FreeCAD (You may need to relaunch if it is running already). 7 | 8 | 9 | Once you install it, see this tutorial for instructions on how to run the script: http://mendelmax.com/tikiwiki/tiki-index.php?page=Getting+Started+with+FreeCAD+scripting 10 | 11 | Edit the script MendelMax.pyas necessary to change the variables and run it to build the model. 12 | 13 | See http://www.MendelMax.com for full details. -------------------------------------------------------------------------------- /build/ExtrusionClip.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxbots/MendelMax/fb90034768fc609448590c5ff41c85857e044a12/build/ExtrusionClip.stl -------------------------------------------------------------------------------- /build/LowerVertex.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxbots/MendelMax/fb90034768fc609448590c5ff41c85857e044a12/build/LowerVertex.stl -------------------------------------------------------------------------------- /build/LowerVertexMiddle.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxbots/MendelMax/fb90034768fc609448590c5ff41c85857e044a12/build/LowerVertexMiddle.stl -------------------------------------------------------------------------------- /build/MendelMax.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxbots/MendelMax/fb90034768fc609448590c5ff41c85857e044a12/build/MendelMax.stl -------------------------------------------------------------------------------- /build/README: -------------------------------------------------------------------------------- 1 | compiled / binary part files, such as STL, go here 2 | -------------------------------------------------------------------------------- /build/RodLatch.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxbots/MendelMax/fb90034768fc609448590c5ff41c85857e044a12/build/RodLatch.stl -------------------------------------------------------------------------------- /build/TSlotNut.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxbots/MendelMax/fb90034768fc609448590c5ff41c85857e044a12/build/TSlotNut.stl -------------------------------------------------------------------------------- /build/TopVertexX.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxbots/MendelMax/fb90034768fc609448590c5ff41c85857e044a12/build/TopVertexX.stl -------------------------------------------------------------------------------- /build/YIdlerMount.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxbots/MendelMax/fb90034768fc609448590c5ff41c85857e044a12/build/YIdlerMount.stl -------------------------------------------------------------------------------- /build/YMotorMount.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxbots/MendelMax/fb90034768fc609448590c5ff41c85857e044a12/build/YMotorMount.stl -------------------------------------------------------------------------------- /build/YRodMount.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxbots/MendelMax/fb90034768fc609448590c5ff41c85857e044a12/build/YRodMount.stl -------------------------------------------------------------------------------- /build/ZMotorMount.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxbots/MendelMax/fb90034768fc609448590c5ff41c85857e044a12/build/ZMotorMount.stl -------------------------------------------------------------------------------- /build/ZRodLowerMount.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxbots/MendelMax/fb90034768fc609448590c5ff41c85857e044a12/build/ZRodLowerMount.stl -------------------------------------------------------------------------------- /build/compatibility/README: -------------------------------------------------------------------------------- 1 | This directory contains binary (or otherwise non-source) model files, such as STLs, that are exported to parameters that match other designs 2 | -------------------------------------------------------------------------------- /build/compatibility/RodLatch-Thingiverse-YRodClasp-20.25mm.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxbots/MendelMax/fb90034768fc609448590c5ff41c85857e044a12/build/compatibility/RodLatch-Thingiverse-YRodClasp-20.25mm.stl -------------------------------------------------------------------------------- /build/compatibility/RodLatch-Thingiverse-ZRodTopLatch-26mm.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxbots/MendelMax/fb90034768fc609448590c5ff41c85857e044a12/build/compatibility/RodLatch-Thingiverse-ZRodTopLatch-26mm.stl -------------------------------------------------------------------------------- /include/README: -------------------------------------------------------------------------------- 1 | This folder is for helper functions and libraries, like RegularPolygon 2 | -------------------------------------------------------------------------------- /include/RegularPolygon.py: -------------------------------------------------------------------------------- 1 | def regPolygon(sides, radius=0, type="inner", edgeLength=0, \ 2 | X_offset=0, Y_offset=0, Z_offset=0, \ 3 | vX=0,vY=0,vZ=0, \ 4 | rotation=0, extrude=0, makeFace= 1): 5 | # # of sides 6 | #sides = 6 7 | 8 | # Draw the polygon based on the radius of a circle insdie the polygon, 9 | # touching the center of the flats 10 | #type = "inner" 11 | # Draw the polygon based on the radius of a circle outside the polygon, 12 | # touching the corners 13 | #type = "outer" 14 | # Draw the polygon with a predefined edge length 15 | #type = "edgeLength" 16 | 17 | # Radius, either inner or outer 18 | # ignored if type = "edgeLength" 19 | #radius = 2 20 | 21 | # The length of each edge 22 | # ignored if type = "inner" or "outer" if using 23 | #edgeLength = 2 24 | 25 | # Position of the center point of starting face 26 | #X_offset = 6 27 | #Y_offset = -3 28 | #Z_offset = 0 29 | 30 | # Vectors 31 | #vX = 0 32 | #vY = 0 33 | #vZ = 0 34 | 35 | # By default, the first point is drawn on the X axis. 36 | # Set to a value in radians to rotate from the default 37 | #rotation = -4 38 | 39 | # extrude length, leave 0 for a 2d polygon 40 | #extrude = 20 41 | 42 | # Set makeFace to 0 if you do not want a face 43 | #makeFace = 0 44 | 45 | import FreeCAD, Part, math 46 | from FreeCAD import Base 47 | from math import pi, cos, sin 48 | 49 | if sides < 3: 50 | raise ValueError("Sides must be >=3") 51 | if radius == 0 and edgeLength == 0: 52 | raise ValueError("Either radius or edgeLength must be assigined a non-zero value") 53 | 54 | r = {'inner': 0, 'outer': 0} 55 | i = 'innner' ## Allows shorthand r[i] instead of r["inner"] 56 | o = 'outer' ## Bad form, but useful 57 | 58 | # The function defines the polygon by the radius of a circle drawn 59 | # outside the polygon, in other words, the radius is the distance 60 | # from the center of the circle to one of the corners. 61 | ## Many common polygons, such as nuts and bolts are defined by 62 | # the radius inside the polygon, IOW the radius is the distance 63 | # from the center of the circle to the point on the flat side 64 | # closest to the center. 65 | # 66 | # In other cases, we may want to define a polygon by the length of the 67 | # sides rather than the radius. 68 | # 69 | # Since we need the outside radius for our calculations, we need 70 | # to calculate the correct outer radius here 71 | # 72 | # We should never need to know the R[i], but if we are specifying 73 | # the outer radius we will calculate the R[i] anyway, just for 74 | # future flexibility 75 | # http://www.algebra.com/algebra/homework/Polygons/Inscribed-and-circumscribed-polygons.lesson 76 | if type == "inner": 77 | r[i] = radius 78 | r[o] = radius / cos(pi/sides) 79 | radius = r[o] 80 | elif type == "edgeLength": 81 | radius = (edgeLength / (2*sin(pi/sides))) 82 | r[o] = radius 83 | r[i] = (edgeLength / (2*tan(pi/sides))) 84 | else: 85 | r[i] = cos(pi/sides) * radius 86 | r[o] = radius 87 | 88 | # Define the starting point for our calculations 89 | # regardless of the final orientation, we will start working in the X & Y 90 | x = radius 91 | y = 0 92 | z = Z_offset 93 | 94 | # Set initial point if rotation was specified and save it for the end point 95 | startx = round((x*cos((0))) - (y*sin((0))), 4) 96 | starty = round((x*sin((0))) + (y*cos((0))), 4) 97 | 98 | x = startx 99 | y = starty 100 | 101 | # create the points list and define the first point, off set in X,Y, and Z as needed 102 | points = [(Base.Vector(x + X_offset,y + Y_offset, z))] 103 | 104 | # Calculate the remaining points 105 | # These calculations assume the circle is centered at 0,0, so 106 | # the offset is applied after the calculations are applied 107 | for step in range (1, sides): 108 | # http://en.wikipedia.org/wiki/Cartesian_coordinate_system#Rotation 109 | newx = round((x*cos((2*pi/sides))) - (y*sin((2*pi/sides))), 4) 110 | newy = round((x*sin((2*pi/sides))) + (y*cos((2*pi/sides))), 4) 111 | x = newx 112 | y = newy 113 | points.append((Base.Vector(x + X_offset,y + Y_offset,z))) 114 | 115 | points.append((startx + X_offset, starty + Y_offset,z)) 116 | print(points) 117 | polygon = Part.makePolygon(points) 118 | 119 | if makeFace != 0: 120 | polygon = Part.Face(polygon) 121 | 122 | if extrude != 0: 123 | polygon = polygon.extrude(Base.Vector(0,0,extrude)) 124 | 125 | #polygon.rotate(vX, vY, vZ) 126 | 127 | return polygon -------------------------------------------------------------------------------- /partsrc/LowerVertex.py: -------------------------------------------------------------------------------- 1 | from __future__ import division # allows floating point division from integers 2 | import FreeCAD, Part, math 3 | from FreeCAD import Base 4 | 5 | def LowerVertex( 6 | extrusion_profile = 20, 7 | frame_rectangle_spacing = 30, 8 | hole_spacing = 20, 9 | thick_typical = 4.25 10 | ): 11 | 12 | #how big are the structural bolts? 13 | #TODO: replace with use of bolt module after it's written 14 | bolt_hole_diameter = 5.5 15 | 16 | box = Part.makeBox(extrusion_profile*2+frame_rectangle_spacing+thick_typical,extrusion_profile*2,thick_typical) 17 | box.translate(Base.Vector(-thick_typical,0,0)) 18 | vertex = box 19 | 20 | #reinforce the base rectangle plate to the diagonal plate 21 | filler_cross_section = Part.makePolygon([ 22 | Base.Vector(0,0,0), 23 | Base.Vector(0,-(extrusion_profile*math.sqrt(3)-thick_typical)/math.sqrt(3),0), 24 | Base.Vector(-(extrusion_profile*math.sqrt(3)-thick_typical),0,0), 25 | Base.Vector(0,0,0), 26 | ]) 27 | filler_face = Part.Face(filler_cross_section) 28 | filler = filler_face.extrude(Base.Vector(0,0,thick_typical)) 29 | filler.translate(Base.Vector(-thick_typical,extrusion_profile*2,0)) 30 | vertex = vertex.fuse(filler) 31 | 32 | #TODO: cleaner calculation of length of this part to reduce trimming 33 | box = Part.makeBox(extrusion_profile+hole_spacing+40,extrusion_profile,thick_typical) 34 | cylinder = Part.makeCylinder(bolt_hole_diameter/2,thick_typical) 35 | cylinder.translate(Base.Vector(extrusion_profile/2,extrusion_profile/2,0)) 36 | box = box.cut(cylinder) 37 | cylinder.translate(Base.Vector(hole_spacing,0,0)) 38 | box = box.cut(cylinder) 39 | box.translate(Base.Vector(-(extrusion_profile+hole_spacing+15),0,0)) 40 | box.rotate(Base.Vector(0,extrusion_profile,0),Base.Vector(0,0,1),-30) 41 | vertex = vertex.fuse(box) 42 | #TODO: reduce or eliminate this trimming 43 | box = Part.makeBox(40,10,extrusion_profile) 44 | box.translate(Base.Vector(-thick_typical-5,-10,0)) 45 | vertex = vertex.cut(box) 46 | 47 | cylinder = Part.makeCylinder(bolt_hole_diameter/2,thick_typical) 48 | cylinder.translate(Base.Vector(extrusion_profile/2,extrusion_profile/2,0)) 49 | vertex = vertex.cut(cylinder) 50 | cylinder.translate(Base.Vector(0,extrusion_profile,0)) 51 | vertex = vertex.cut(cylinder) 52 | cylinder.translate(Base.Vector(frame_rectangle_spacing+extrusion_profile,0,0)) 53 | vertex = vertex.cut(cylinder) 54 | cylinder.translate(Base.Vector(0,-extrusion_profile,0)) 55 | vertex = vertex.cut(cylinder) 56 | 57 | return vertex 58 | 59 | if __name__ == "__main__": 60 | Part.show(LowerVertex()) 61 | -------------------------------------------------------------------------------- /partsrc/LowerVertexMiddle.py: -------------------------------------------------------------------------------- 1 | from __future__ import division # allows floating point division from integers 2 | import FreeCAD, Part, math 3 | from FreeCAD import Base 4 | 5 | def LowerVertexMiddle( 6 | extrusion_profile = 20, 7 | frame_rectangle_spacing = 30, 8 | thick_typical = 4.25 9 | ): 10 | 11 | #how big are the structural bolts? 12 | #TODO: replace with use of bolt module after it's written 13 | bolt_hole_diameter = 5.5 14 | bolt_head_diameter = 10 15 | 16 | #should there be a visible hole for easy access to the "hidden" bolt? 17 | accessible_hole = 1 18 | #should we draw the extrusions for visualization purposes? 19 | render_extrusions = 0 20 | 21 | #connect the two X extrusions 22 | box = Part.makeBox(frame_rectangle_spacing+extrusion_profile*2,thick_typical,extrusion_profile) 23 | box.translate(Base.Vector(0,-thick_typical,0)) 24 | vertex = box 25 | cylinder = Part.makeCylinder(bolt_hole_diameter/2,thick_typical) 26 | cylinder.rotate(Base.Vector(0,0,0),Base.Vector(1,0,0),90) 27 | cylinder.translate(Base.Vector(extrusion_profile/2,0,extrusion_profile/2)) 28 | vertex = vertex.cut(cylinder) 29 | cylinder.translate(Base.Vector(frame_rectangle_spacing+extrusion_profile,0,0)) 30 | vertex = vertex.cut(cylinder) 31 | 32 | #mount to the top of the top Y extrusion 33 | box = Part.makeBox(thick_typical,40+extrusion_profile,extrusion_profile) 34 | box.translate(Base.Vector(-thick_typical,-thick_typical,0)) 35 | vertex = vertex.fuse(box) 36 | cylinder = Part.makeCylinder(bolt_hole_diameter/2,thick_typical) 37 | cylinder.rotate(Base.Vector(0,0,0),Base.Vector(0,1,0),-90) 38 | cylinder.translate(Base.Vector(0,40+extrusion_profile/2-thick_typical,extrusion_profile/2)) 39 | vertex = vertex.cut(cylinder) 40 | 41 | #mount to the top of the diagonal extrusion 42 | #TODO: cleaner calculation of length of this part to avoid trimming 43 | box = Part.makeBox(extrusion_profile+40,thick_typical,extrusion_profile) 44 | box.translate(Base.Vector(-extrusion_profile-15,-thick_typical,0)) 45 | #fill the space below the diagonal extrusion 46 | box2 = Part.makeBox(extrusion_profile+5,extrusion_profile,extrusion_profile) 47 | box2.translate(Base.Vector(-15,0,0)) 48 | box = box.fuse(box2) 49 | cylinder = Part.makeCylinder(bolt_hole_diameter/2,thick_typical) 50 | cylinder.rotate(Base.Vector(0,0,0),Base.Vector(1,0,0),90) 51 | cylinder.translate(Base.Vector(-15-extrusion_profile/2,0,extrusion_profile/2)) 52 | box = box.cut(cylinder) 53 | box.rotate(Base.Vector(0,extrusion_profile,0),Base.Vector(0,0,1),-30) 54 | vertex = vertex.fuse(box) 55 | 56 | if(accessible_hole): 57 | cylinder = Part.makeCylinder(bolt_hole_diameter/2,extrusion_profile+60) 58 | else: 59 | cylinder = Part.makeCylinder(bolt_hole_diameter/2,extrusion_profile+5) 60 | cylinder2 = Part.makeCylinder(bolt_head_diameter/2,extrusion_profile+5-thick_typical) 61 | cylinder2.translate(Base.Vector(0,0,thick_typical)) 62 | cylinder = cylinder.fuse(cylinder2) 63 | cylinder.rotate(Base.Vector(0,0,0),Base.Vector(0,1,0),90) 64 | cylinder.translate(Base.Vector(-15,0,0)) 65 | cylinder.translate(Base.Vector(0,extrusion_profile/2,extrusion_profile/2)) 66 | cylinder.rotate(Base.Vector(0,extrusion_profile,0),Base.Vector(0,0,1),-30) 67 | vertex = vertex.cut(cylinder) 68 | 69 | box = Part.makeBox(extrusion_profile,extrusion_profile,extrusion_profile) 70 | vertex = vertex.cut(box) 71 | #TODO: eliminate this trim by cleaning above 72 | box = Part.makeBox(40,10,extrusion_profile) 73 | box.translate(Base.Vector(-thick_typical-5,-10-thick_typical,0)) 74 | vertex = vertex.cut(box) 75 | 76 | return vertex 77 | 78 | if __name__ == "__main__": 79 | Part.show(LowerVertexMiddle()) 80 | -------------------------------------------------------------------------------- /partsrc/README: -------------------------------------------------------------------------------- 1 | part source files go here. python, openscad, etc 2 | -------------------------------------------------------------------------------- /partsrc/RodLatch.py: -------------------------------------------------------------------------------- 1 | from __future__ import division # allows floating point division from integers 2 | import FreeCAD, Part, math 3 | from FreeCAD import Base 4 | 5 | def RodLatch( 6 | rod_diameter = 8, 7 | #how far apart should the bolt holes be? 8 | hole_spacing = 30, 9 | #how thick should the non-flange parts be? 10 | thick_typical = 4.25, 11 | #how thick should the plastic under the bolt be? 12 | thick_compress = 3 13 | ): 14 | 15 | #how big are the bolts? 16 | #TODO: replace with use of bolt module after it's written 17 | bolt_hole_diameter = 5.5 18 | bolt_head_diameter = 8.5 19 | 20 | latch_width = bolt_head_diameter + thick_typical*2 21 | 22 | #bounding box for the main curved part of the latch 23 | box = Part.makeBox(hole_spacing,latch_width,rod_diameter/2+thick_typical) 24 | box.translate(Base.Vector(latch_width/2,0,0)) 25 | 26 | #main curved part of the latch, around the rod 27 | cylinder = Part.makeCylinder(rod_diameter/2+thick_typical,latch_width) 28 | cylinder.rotate(Base.Vector(0,0,0),Base.Vector(1,0,0),-90) 29 | cylinder.translate(Base.Vector(latch_width/2+hole_spacing/2,0,0)) 30 | latch = box.common(cylinder) 31 | 32 | #connects the curved part to the bolt holes 33 | box = Part.makeBox(hole_spacing,latch_width,max(thick_typical,thick_compress)) 34 | box.translate(Base.Vector(latch_width/2,0,0)) 35 | latch = latch.fuse(box) 36 | 37 | #housings for the bolt holes 38 | cylinder = Part.makeCylinder(latch_width/2,thick_typical) 39 | cylinder.translate(Base.Vector(latch_width/2,latch_width/2,0)) 40 | latch = latch.fuse(cylinder) 41 | cylinder = Part.makeCylinder(latch_width/2,thick_typical) 42 | cylinder.translate(Base.Vector(latch_width/2+hole_spacing,latch_width/2,0)) 43 | latch = latch.fuse(cylinder) 44 | 45 | #bolt holes 46 | cylinder = Part.makeCylinder(bolt_hole_diameter/2,rod_diameter/2+thick_typical) 47 | cylinder.translate(Base.Vector(latch_width/2,latch_width/2,0)) 48 | latch = latch.cut(cylinder) 49 | cylinder = Part.makeCylinder(bolt_head_diameter/2,rod_diameter/2+thick_typical) 50 | cylinder.translate(Base.Vector(latch_width/2,latch_width/2,thick_compress)) 51 | latch = latch.cut(cylinder) 52 | cylinder = Part.makeCylinder(bolt_hole_diameter/2,rod_diameter/2+thick_typical) 53 | cylinder.translate(Base.Vector(latch_width/2+hole_spacing,latch_width/2,0)) 54 | latch = latch.cut(cylinder) 55 | cylinder = Part.makeCylinder(bolt_head_diameter/2,rod_diameter/2+thick_typical) 56 | cylinder.translate(Base.Vector(latch_width/2+hole_spacing,latch_width/2,thick_compress)) 57 | latch = latch.cut(cylinder) 58 | 59 | #rod hole 60 | cylinder = Part.makeCylinder(rod_diameter/2,latch_width) 61 | cylinder.rotate(Base.Vector(0,0,0),Base.Vector(1,0,0),-90) 62 | cylinder.translate(Base.Vector(latch_width/2+hole_spacing/2,0,0)) 63 | latch = latch.cut(cylinder) 64 | 65 | return latch 66 | 67 | if __name__ == "__main__": 68 | Part.show(RodLatch()) 69 | -------------------------------------------------------------------------------- /partsrc/TopVertexX.py: -------------------------------------------------------------------------------- 1 | from __future__ import division # allows floating point division from integers 2 | import FreeCAD, Part, math 3 | from FreeCAD import Base 4 | 5 | small_printer = True 6 | 7 | def TopVertexX( 8 | extrusion_profile = 20, 9 | # how thick should the part be? 10 | thick_typical = 4, 11 | # how far apart should the holes be? 12 | hole_spacing = 20 13 | # how much wider than the spacing should the plates be? 14 | ): 15 | 16 | bolt_hole_diameter = 5.5 17 | # how much wider than the bolt spacing should the plates attached to the extrusion be? 18 | plate_padding = extrusion_profile/2 19 | # how far should the plates be from the extrusion corner? 20 | corner_offset = 60 21 | # how wide should the strut be? 22 | diagonal_width = 17 23 | 24 | # shrink the part, least important dimensions first 25 | if(small_printer): 26 | if(corner_offset+hole_spacing+plate_padding*2>100): 27 | plate_padding = max(bolt_hole_diameter/2+thick_min,(100-(corner_offset+hole_spacing))/2) 28 | hole_spacing = 100-(corner_offset+plate_padding*2) 29 | #need logic to reduce the number of holes if they overlap 30 | diagonal_width = min(diagonal_width,(hole_spacing+plate_padding*2)/math.sqrt(2)) 31 | #TODO: alert the user if the part is still too big 32 | 33 | shape = Part.makePolygon([ 34 | Base.Vector(corner_offset,0,0), 35 | Base.Vector(corner_offset+hole_spacing+plate_padding*2,0,0), 36 | Base.Vector(corner_offset+hole_spacing+plate_padding*2,extrusion_profile,0), 37 | Base.Vector(corner_offset+(hole_spacing+plate_padding*2)/2+diagonal_width/2*math.sqrt(2),extrusion_profile,0), 38 | Base.Vector(extrusion_profile,corner_offset+(hole_spacing+plate_padding*2)/2+diagonal_width/2*math.sqrt(2),0), 39 | Base.Vector(extrusion_profile,corner_offset+hole_spacing+plate_padding*2,0), 40 | Base.Vector(0,corner_offset+hole_spacing+plate_padding*2,0), 41 | Base.Vector(0,corner_offset,0), 42 | Base.Vector(extrusion_profile,corner_offset,0), 43 | Base.Vector(extrusion_profile,corner_offset+(hole_spacing+plate_padding*2)/2-diagonal_width/2*math.sqrt(2),0), 44 | Base.Vector(corner_offset+(hole_spacing+plate_padding*2)/2-diagonal_width/2*math.sqrt(2),extrusion_profile,0), 45 | Base.Vector(corner_offset,extrusion_profile,0), 46 | Base.Vector(corner_offset,0,0), 47 | ]) 48 | face = Part.Face(shape) 49 | vertex = face.extrude(Base.Vector(0,0,thick_typical)) 50 | 51 | cylinder = Part.makeCylinder(bolt_hole_diameter/2,thick_typical) 52 | cylinder.translate(Base.Vector(corner_offset+plate_padding,extrusion_profile/2,0)) 53 | vertex = vertex.cut(cylinder) 54 | cylinder.translate(Base.Vector(hole_spacing,0,0)) 55 | vertex = vertex.cut(cylinder) 56 | cylinder.rotate(Base.Vector(0,0,thick_typical/2),Base.Vector(1,1,0),180) 57 | vertex = vertex.cut(cylinder) 58 | cylinder.translate(Base.Vector(0,-hole_spacing,0)) 59 | vertex = vertex.cut(cylinder) 60 | 61 | return vertex 62 | 63 | if __name__ == "__main__": 64 | Part.show(TopVertexX()) -------------------------------------------------------------------------------- /partsrc/YIdlerMount.py: -------------------------------------------------------------------------------- 1 | from __future__ import division # allows floating point division from integers 2 | import FreeCAD, Part, math 3 | from FreeCAD import Base 4 | 5 | def YIdlerMount( 6 | extrusion_profile = 20, 7 | #how far apart should the bottom frame rectangles be? 8 | frame_rectangle_spacing = 30, 9 | #how thick should the non-flange parts be? 10 | thick_typical = 4.25 11 | ): 12 | 13 | idler_axle_diameter = 8 14 | #how big are the bolts? 15 | bolt_hole_diameter = 5.5 16 | 17 | #build the flat part of the mount 18 | box = Part.makeBox(frame_rectangle_spacing+extrusion_profile*2-extrusion_profile,thick_typical*2,thick_typical) 19 | box.translate(Base.Vector(extrusion_profile/2.0,0)) 20 | mount = box 21 | cylinder = Part.makeCylinder(extrusion_profile/2,thick_typical) 22 | cylinder.translate(Base.Vector(extrusion_profile/2,extrusion_profile/2,0)) 23 | mount = mount.fuse(cylinder) 24 | cylinder.translate(Base.Vector(frame_rectangle_spacing+extrusion_profile,0,0)) 25 | mount = mount.fuse(cylinder) 26 | 27 | box = Part.makeBox(extrusion_profile/2,extrusion_profile/2,thick_typical) 28 | box.translate(Base.Vector(extrusion_profile/2,thick_typical,0)) 29 | mount = mount.fuse(box) 30 | box.translate(Base.Vector(frame_rectangle_spacing+extrusion_profile/2,0,0)) 31 | mount = mount.fuse(box) 32 | 33 | box = Part.makeBox(extrusion_profile/2,thick_typical,thick_typical) 34 | box.translate(Base.Vector(0,extrusion_profile/2,0)) 35 | mount = mount.fuse(box) 36 | box.translate(Base.Vector(frame_rectangle_spacing+extrusion_profile*3/2,0,0)) 37 | mount = mount.fuse(box) 38 | 39 | cylinder = Part.makeCylinder(extrusion_profile/2,thick_typical) 40 | cylinder.translate(Base.Vector(extrusion_profile/2,extrusion_profile/2+thick_typical,0)) 41 | mount = mount.fuse(cylinder) 42 | cylinder.translate(Base.Vector(frame_rectangle_spacing+extrusion_profile,0,0)) 43 | mount = mount.fuse(cylinder) 44 | 45 | box = Part.makeBox(bolt_hole_diameter/2,bolt_hole_diameter/2,thick_typical) 46 | box.translate(Base.Vector(extrusion_profile,thick_typical*2,0)) 47 | mount = mount.fuse(box) 48 | box.translate(Base.Vector(frame_rectangle_spacing-bolt_hole_diameter/2,0,0)) 49 | mount = mount.fuse(box) 50 | 51 | cylinder = Part.makeCylinder(bolt_hole_diameter/2,thick_typical) 52 | cylinder.translate(Base.Vector(extrusion_profile+bolt_hole_diameter/2,thick_typical*2+bolt_hole_diameter/2,0)) 53 | mount = mount.cut(cylinder) 54 | cylinder.translate(Base.Vector(frame_rectangle_spacing-bolt_hole_diameter,0,0)) 55 | mount = mount.cut(cylinder) 56 | 57 | 58 | #extrusion bolt holes 59 | cylinder = Part.makeCylinder(bolt_hole_diameter/2,thick_typical) 60 | cylinder.translate(Base.Vector(extrusion_profile/2,extrusion_profile/2+thick_typical,0)) 61 | mount = mount.cut(cylinder) 62 | cylinder.translate(Base.Vector(frame_rectangle_spacing+extrusion_profile,0,0)) 63 | mount = mount.cut(cylinder) 64 | 65 | box = Part.makeBox(idler_axle_diameter*2+thick_typical*2,thick_typical*2,idler_axle_diameter/2+thick_typical) 66 | box.translate(Base.Vector((frame_rectangle_spacing+extrusion_profile*2)/2-(idler_axle_diameter*2+thick_typical*2)/2,0,0)) 67 | mount = mount.fuse(box) 68 | 69 | cylinder = Part.makeCylinder(idler_axle_diameter/2+thick_typical,thick_typical*2) 70 | cylinder.rotate(Base.Vector(0,0,0),Base.Vector(1,0,0),-90) 71 | cylinder.translate(Base.Vector((frame_rectangle_spacing+extrusion_profile*2)/2,0,idler_axle_diameter/2+thick_typical)) 72 | mount = mount.fuse(cylinder) 73 | 74 | #rounded corners and idler axle hole 75 | cylinder = Part.makeCylinder(idler_axle_diameter/2,thick_typical*2) 76 | cylinder.rotate(Base.Vector(0,0,0),Base.Vector(1,0,0),-90) 77 | cylinder.translate(Base.Vector((frame_rectangle_spacing+extrusion_profile*2)/2-idler_axle_diameter-thick_typical,0,idler_axle_diameter/2+thick_typical)) 78 | mount = mount.cut(cylinder) 79 | cylinder.translate(Base.Vector(idler_axle_diameter+thick_typical,0,0)) 80 | mount = mount.cut(cylinder) 81 | cylinder.translate(Base.Vector(idler_axle_diameter+thick_typical,0,0)) 82 | mount = mount.cut(cylinder) 83 | 84 | return mount 85 | 86 | if __name__ == "__main__": 87 | Part.show(YIdlerMount()) -------------------------------------------------------------------------------- /partsrc/YMotorMount.py: -------------------------------------------------------------------------------- 1 | from __future__ import division # allows floating point division from integers 2 | import FreeCAD, Part, math 3 | from FreeCAD import Base 4 | 5 | def YMotorMount( 6 | extrusion_profile = 20, 7 | #how far apart should the bottom frame rectangles be? 8 | frame_rectangle_spacing = 30, 9 | thick_typical = 4.25, 10 | thick_compress = 3 11 | ): 12 | 13 | #TODO: use the bolt module 14 | main_bolt_hole_diameter = 5.5 15 | main_bolt_head_diameter = 10 #? 16 | main_bolt_bore_depth = 4 17 | 18 | #TODO: use the motor and bolt modules 19 | motor_bolt_hole_diameter = 3.5 20 | motor_bolt_head_diameter = 6 #? 21 | 22 | mount_x = frame_rectangle_spacing+extrusion_profile*2 23 | mount_y = extrusion_profile+thick_typical+6 24 | base_z = thick_compress+main_bolt_bore_depth 25 | 26 | motor_plate_height = 16 27 | 28 | #build the flat part of the mount 29 | box = Part.makeBox(mount_x,mount_y,base_z) 30 | mount = box 31 | 32 | #TODO: use parametric motor dimensions 33 | #TODO: waste less plastic on this plate 34 | box = Part.makeBox(mount_x-motor_plate_height*2,thick_typical,motor_plate_height) 35 | box.translate(Base.Vector(motor_plate_height,0,0)) 36 | plate = box 37 | cylinder = Part.makeCylinder(motor_plate_height,thick_typical,Base.Vector(motor_plate_height,0,0),Base.Vector(0,1,0),90) 38 | cylinder.rotate(Base.Vector(motor_plate_height,0,0),Base.Vector(0,1,0),-90) 39 | plate = plate.fuse(cylinder) 40 | cylinder.rotate(Base.Vector(mount_x/2,thick_typical/2,0),Base.Vector(0,0,1),180) 41 | plate = plate.fuse(cylinder) 42 | #motor bolt holes 43 | cylinder = Part.makeCylinder(motor_bolt_hole_diameter/2,thick_typical) 44 | cylinder.rotate(Base.Vector(0,0,0),Base.Vector(1,0,0),-90) 45 | cylinder.translate(Base.Vector(mount_x/2-31/2,0,5.5)) 46 | plate = plate.cut(cylinder) 47 | cylinder.translate(Base.Vector(31,0,0)) 48 | plate = plate.cut(cylinder) 49 | cylinder.translate(Base.Vector(0,0,31)) 50 | plate = plate.cut(cylinder) 51 | cylinder.translate(Base.Vector(-31,0,0)) 52 | plate = plate.cut(cylinder) 53 | #motor cylinder hole 54 | cylinder = Part.makeCylinder(23/2,thick_typical) 55 | cylinder.rotate(Base.Vector(0,0,0),Base.Vector(1,0,0),-90) 56 | cylinder.translate(Base.Vector(mount_x/2,0,motor_plate_height)) 57 | plate = plate.cut(cylinder) 58 | plate.translate(Base.Vector(0,mount_y-thick_typical,base_z)) 59 | mount = mount.fuse(plate) 60 | 61 | #extrusion bolt holes 62 | cylinder = Part.makeCylinder(main_bolt_hole_diameter/2,base_z) 63 | cylinder2 = Part.makeCylinder(main_bolt_head_diameter/2,main_bolt_bore_depth) 64 | cylinder2.translate(Base.Vector(0,0,thick_compress)) 65 | cylinder = cylinder.fuse(cylinder2) 66 | cylinder.translate(Base.Vector(extrusion_profile/2,(mount_y-thick_typical)/2,0)) 67 | mount = mount.cut(cylinder) 68 | cylinder.translate(Base.Vector(mount_x-extrusion_profile,0,0)) 69 | mount = mount.cut(cylinder) 70 | 71 | #reinforcements 72 | support_cross_section = Part.makePolygon([ 73 | Base.Vector(0,0,0), 74 | Base.Vector(0,mount_y-thick_typical,0), 75 | Base.Vector(0,mount_y-thick_typical,motor_plate_height*math.sin(math.acos((motor_plate_height-thick_typical)/motor_plate_height))), 76 | Base.Vector(0,0,0)]) 77 | face = Part.Face(support_cross_section) 78 | support = face.extrude(Base.Vector(thick_typical,0,0)) 79 | cylinder = Part.makeCylinder(motor_plate_height,mount_y,Base.Vector(motor_plate_height,0,0),Base.Vector(0,1,0),90) 80 | cylinder.rotate(Base.Vector(motor_plate_height,0,0),Base.Vector(0,1,0),-90) 81 | support1 = support.common(cylinder) 82 | support1.translate(Base.Vector(0,0,base_z)) 83 | mount = mount.fuse(support1) 84 | support.translate(Base.Vector(mount_x-thick_typical,0,0)) 85 | cylinder.rotate(Base.Vector(motor_plate_height,0,0),Base.Vector(0,1,0),90) 86 | cylinder.translate(Base.Vector(mount_x-motor_plate_height*2)) 87 | support = support.common(cylinder) 88 | support.translate(Base.Vector(0,0,base_z)) 89 | mount = mount.fuse(support) 90 | 91 | #zip-tie tab 92 | box = Part.makeBox(10,10,base_z) 93 | box.translate(Base.Vector((mount_x-10)/2,-10,0)) 94 | mount = mount.fuse(box) 95 | 96 | return mount 97 | 98 | if __name__ == "__main__": 99 | Part.show(YMotorMount()) -------------------------------------------------------------------------------- /partsrc/YRodMount.py: -------------------------------------------------------------------------------- 1 | from __future__ import division # allows floating point division from integers 2 | import FreeCAD, Part, math 3 | from FreeCAD import Base 4 | 5 | import sys, os 6 | try: 7 | path = os.path.join(os.path.dirname(__file__), '..', 'include') 8 | i = sys.path.index(path) 9 | except: 10 | sys.path.append(path) 11 | 12 | from RegularPolygon import regPolygon 13 | 14 | def YRodMount( 15 | extrusion_profile = 20, 16 | y_rail_type = 'smooth rod', 17 | y_rod_diameter = 8, 18 | y_rod_spacing = 100, 19 | #how far apart should the bottom frame rectangles be? 20 | frame_rectangle_spacing = 30, 21 | #how thick should the parts be? 22 | thick_typical = 4.25, 23 | thick_compress = 3, 24 | thick_min = 1, 25 | #how far apart should the rod bolt holes be? 26 | hole_spacing = 30 27 | ): 28 | 29 | #TODO: fail if y_rail_type is not 'smooth rod' 30 | 31 | #TODO: add logic to specify bolt type instead of bolt dimensions 32 | #how big are the bolts? 33 | bolt_hole_diameter = 5.5 34 | nut_depth = 4 35 | nut_width = 8.5 36 | 37 | #large printer version raises concerns, so this is an override to always use the 100mm-wide version 38 | small_printer = True 39 | 40 | #if(small_printer and y_rod_spacing>100): 41 | #error 42 | 43 | #TODO: make sure tower_y > nut_depth + thick_compress 44 | tower_x = y_rod_diameter/2+thick_compress 45 | tower_y = hole_spacing+nut_width+thick_min*2 46 | tower_z = nut_width*math.sqrt(3)+thick_min*2 47 | 48 | mount_x = y_rod_spacing 49 | mount_y = frame_rectangle_spacing+extrusion_profile*2 50 | mount_z = thick_typical 51 | 52 | #base 53 | box = Part.makeBox(mount_x,mount_y,mount_z) 54 | box2 = Part.makeBox(mount_x-tower_x*2,frame_rectangle_spacing,mount_z) 55 | box2.translate(Base.Vector(tower_x,extrusion_profile,0)) 56 | mount = box.cut(box2) 57 | 58 | #"towers" 59 | box = Part.makeBox(tower_x,tower_y,tower_z) 60 | box.translate(Base.Vector(0,(mount_y-tower_y)/2,mount_z)) 61 | mount = mount.fuse(box) 62 | box.translate(Base.Vector(y_rod_spacing-tower_x,0,0)) 63 | mount = mount.fuse(box) 64 | 65 | #rod clamp bolt holes 66 | nuthole = regPolygon(sides = 6, radius = nut_width/2, extrude = nut_depth, Z_offset = tower_x-nut_depth) 67 | cylinder = Part.makeCylinder(bolt_hole_diameter/2,tower_x) 68 | cutout = cylinder.fuse(nuthole) 69 | cutout.rotate(Base.Vector(0,0,0),Base.Vector(0,1,0),90) 70 | cutout.translate(Base.Vector(0,mount_y/2-hole_spacing/2,mount_z+tower_z/2)) 71 | mount = mount.cut(cutout) 72 | #rotations around lines in the middle of the part to re-use the same cutout 73 | cutout.rotate(Base.Vector(0,mount_y/2,mount_z+tower_z/2),Base.Vector(1,0,0),180) 74 | mount = mount.cut(cutout) 75 | cutout.rotate(Base.Vector(mount_x/2,mount_y/2,0),Base.Vector(0,0,1),180) 76 | mount = mount.cut(cutout) 77 | cutout.rotate(Base.Vector(0,mount_y/2,mount_z+tower_z/2),Base.Vector(1,0,0),180) 78 | mount = mount.cut(cutout) 79 | 80 | #extrusion bolt holes and corner rounding 81 | cornerbox = Part.makeBox(extrusion_profile/2,extrusion_profile/2,mount_z) 82 | cornercylinder = Part.makeCylinder(extrusion_profile/2,mount_z) 83 | cornercylinder.translate(Base.Vector(extrusion_profile/2,extrusion_profile/2,0)) 84 | cornerbox=cornerbox.cut(cornercylinder) 85 | cylinder = Part.makeCylinder(bolt_hole_diameter/2,mount_z) 86 | #TODO: deal with bolt hole being too close to tower 87 | if(small_printer): 88 | cylinder.translate(Base.Vector(extrusion_profile/2,extrusion_profile/2,0)) 89 | else: 90 | box = Part.makeBox(extrusion_profile,mount_y,mount_z) 91 | box.translate(Base.Vector(-extrusion_profile,0,0)) 92 | mount=mount.fuse(box) 93 | box.translate(Base.Vector(mount_x+extrusion_profile,0,0)) 94 | mount=mount.fuse(box) 95 | cylinder.translate(Base.Vector(-extrusion_profile/2,extrusion_profile/2,0)) 96 | cornerbox.translate(Base.Vector(-extrusion_profile,0,0)) 97 | #add the corner rounding 98 | cylinder=cylinder.fuse(cornerbox) 99 | mount = mount.cut(cylinder) 100 | #rotations around lines in the middle of the part to re-use the same cutout 101 | cylinder.rotate(Base.Vector(0,mount_y/2,mount_z/2),Base.Vector(1,0,0),180) 102 | mount = mount.cut(cylinder) 103 | cylinder.rotate(Base.Vector(mount_x/2,0,mount_z/2),Base.Vector(0,1,0),180) 104 | mount = mount.cut(cylinder) 105 | cylinder.rotate(Base.Vector(0,mount_y/2,mount_z/2),Base.Vector(1,0,0),180) 106 | mount = mount.cut(cylinder) 107 | 108 | #rod holes 109 | cylinder = Part.makeCylinder(y_rod_diameter/2,tower_z+mount_z) 110 | cylinder.translate(Base.Vector(0,mount_y/2,0)) 111 | mount = mount.cut(cylinder) 112 | cylinder.translate(Base.Vector(y_rod_spacing,0,0)) 113 | mount = mount.cut(cylinder) 114 | 115 | return mount 116 | 117 | if __name__ == "__main__": 118 | Part.show(YRodMount()) -------------------------------------------------------------------------------- /partsrc/ZMotorMount.py: -------------------------------------------------------------------------------- 1 | from __future__ import division # allows floating point division from integers 2 | import FreeCAD, Part, math 3 | from FreeCAD import Base 4 | 5 | import sys, os 6 | try: 7 | path = os.path.join(os.path.dirname(__file__), '..', 'include') 8 | i = sys.path.index(path) 9 | except: 10 | sys.path.append(path) 11 | 12 | from RegularPolygon import regPolygon 13 | 14 | def ZMotorMount( 15 | z_rail_type = 'smooth rod', 16 | z_rod_diameter = 8, 17 | z_rod_spacing = 30, 18 | extrusion_profile = 20, 19 | extrusion_y_length = 420, 20 | extrusion_diagonal_length = 340, 21 | #how far apart should the rod clamp bolt holes be? 22 | hole_spacing = 30, 23 | thick_typical = 4.25, 24 | thick_compress = 3 25 | ): 26 | 27 | #how big are the structural bolts? 28 | main_bolt_hole_diameter = 5.5 29 | nut_depth = 4 30 | nut_width = 8.5 31 | #how big are the motor bolts? 32 | motor_bolt_hole_diameter = 3.5 33 | motor_bolt_head_diameter = 6 #? 34 | 35 | #lots of math to figure out the horizontal span of the mount based on the extrusions 36 | #15 is the length added to the diagonal extrusion by the angled plastic part of Lower Vertex Middle 37 | extrusion_hole_spacing = extrusion_y_length-((15+extrusion_diagonal_length+(extrusion_profile/2))*math.sin(math.radians(30))-(extrusion_profile/2)*math.cos(math.radians(30)))*2 38 | box = Part.makeBox(extrusion_hole_spacing-(extrusion_profile/2*math.cos(math.radians(30))-extrusion_profile/2*math.sin(math.radians(30)))*2,extrusion_profile*math.cos(math.radians(30))+thick_typical,thick_typical) 39 | mount = box 40 | 41 | #housings for the upper X extrusions 42 | box = Part.makeBox(extrusion_profile,extrusion_profile,thick_typical) 43 | box2= Part.makeBox(thick_typical,extrusion_profile,75) 44 | box2.translate(Base.Vector(extrusion_profile,0,0)) 45 | box = box.fuse(box2) 46 | #add mounting on the inner side of the extrusion? 47 | #box2= Part.makeBox(extrusion_profile+thick_typical,thick_typical,75) 48 | #box2.translate(Base.Vector(0,-thick_typical,0)) 49 | #box = box.fuse(box2) 50 | #bolt holes 51 | cylinder = Part.makeCylinder(main_bolt_hole_diameter/2,thick_typical) 52 | cylinder.translate(Base.Vector(extrusion_profile/2,extrusion_profile/2,0)) 53 | box = box.cut(cylinder) 54 | cylinder = Part.makeCylinder(main_bolt_hole_diameter/2,thick_typical) 55 | cylinder.rotate(Base.Vector(0,0,0),Base.Vector(1,0,0),90) 56 | cylinder.translate(Base.Vector(extrusion_profile/2,0,75-extrusion_profile/2)) 57 | #bolt holes on the inner side 58 | #box = box.cut(cylinder) 59 | #cylinder.translate(Base.Vector(0,0,-20)) 60 | #box = box.cut(cylinder) 61 | cylinder.rotate(Base.Vector(extrusion_profile+thick_typical/2,-thick_typical/2,0),Base.Vector(0,0,1),-90) 62 | box = box.cut(cylinder) 63 | #second bolt hole on the top side 64 | #cylinder.translate(Base.Vector(0,0,20)) 65 | #box = box.cut(cylinder) 66 | box.rotate(Base.Vector(0,0,0),Base.Vector(0,0,1),60) 67 | mount = mount.fuse(box) 68 | #mirror behaves oddly with rotated parts, so we unrotate before mirroring 69 | box.rotate(Base.Vector(0,0,0),Base.Vector(0,0,1),-60) 70 | box = box.mirror(Base.Vector((extrusion_hole_spacing-(extrusion_profile/2*math.cos(math.radians(30))-extrusion_profile/2*math.sin(math.radians(30)))*2)/2,0,0),Base.Vector(1,0,0)) 71 | box.rotate(Base.Vector(extrusion_hole_spacing-(extrusion_profile/2*math.cos(math.radians(30))-extrusion_profile/2*math.sin(math.radians(30)))*2,0,0),Base.Vector(0,0,1),-60) 72 | mount = mount.fuse(box) 73 | 74 | #TODO: use parametric motor dimensions 75 | #horizontal motor mount plate 76 | box = Part.makeBox(extrusion_hole_spacing-(extrusion_profile/2*math.cos(math.radians(30))+extrusion_profile/2*math.sin(math.radians(30)))*2,thick_typical,75) 77 | box.translate(Base.Vector(extrusion_profile*math.sin(math.radians(30)),extrusion_profile*math.cos(math.radians(30)),0)) 78 | mount = mount.fuse(box) 79 | #motor hole 80 | cylinder = Part.makeCylinder(25/2,thick_typical) 81 | cylinder.rotate(Base.Vector(0,0,0),Base.Vector(1,0,0),-90) 82 | cylinder.translate(Base.Vector((extrusion_hole_spacing-(extrusion_profile/2*math.cos(math.radians(30))+extrusion_profile/2*math.sin(math.radians(30)))*2)/2,0,z_rod_spacing)) 83 | cylinder.translate(Base.Vector(extrusion_profile*math.sin(math.radians(30)),extrusion_profile*math.cos(math.radians(30)),0)) 84 | mount = mount.cut(cylinder) 85 | #motor bolt hole 86 | cylinder = Part.makeCylinder(motor_bolt_hole_diameter/2,thick_typical) 87 | #conditionally necessary counterbore 88 | cylinder2 = Part.makeCylinder(motor_bolt_head_diameter/2,99) 89 | cylinder2.translate(Base.Vector(0,0,(thick_typical-thick_compress)-99)) 90 | cylinder = cylinder.fuse(cylinder2) 91 | cylinder.rotate(Base.Vector(0,0,0),Base.Vector(1,0,0),-90) 92 | cylinder.translate(Base.Vector((extrusion_hole_spacing-(extrusion_profile/2*math.cos(math.radians(30))+extrusion_profile/2*math.sin(math.radians(30)))*2)/2-31/2,0,z_rod_spacing-31/2)) 93 | cylinder.translate(Base.Vector(extrusion_profile*math.sin(math.radians(30)),extrusion_profile*math.cos(math.radians(30)),0)) 94 | mount = mount.cut(cylinder) 95 | cylinder.translate(Base.Vector(31,0,0)) 96 | mount = mount.cut(cylinder) 97 | cylinder.translate(Base.Vector(0,0,31)) 98 | mount = mount.cut(cylinder) 99 | cylinder.translate(Base.Vector(-31,0,0)) 100 | mount = mount.cut(cylinder) 101 | 102 | #move the mount over so we can build the latch in place before moving it back 103 | mount.translate(Base.Vector(-((extrusion_hole_spacing-(extrusion_profile/2*math.cos(math.radians(30))-extrusion_profile/2*math.sin(math.radians(30)))*2)/2-(7+hole_spacing/2)),0,0)) 104 | 105 | #TODO: the common bits from this, RodLatch, and YRodMount should end up in a file together 106 | #bounding box for the main curved part of the latch 107 | box = Part.makeBox(hole_spacing,extrusion_profile*math.cos(math.radians(30))+thick_typical,z_rod_diameter/2+thick_typical) 108 | box.translate(Base.Vector(7,0,0)) 109 | 110 | #main curved part of the latch, around the rod 111 | cylinder = Part.makeCylinder(z_rod_diameter/2+thick_typical,extrusion_profile*math.cos(math.radians(30))+thick_typical) 112 | cylinder.rotate(Base.Vector(0,0,0),Base.Vector(1,0,0),-90) 113 | cylinder.translate(Base.Vector(7+hole_spacing/2,0,0)) 114 | latch = box.common(cylinder) 115 | 116 | #connects the curved part to the bolt holes 117 | box = Part.makeBox(hole_spacing,14,max(thick_typical,thick_compress)) 118 | box.translate(Base.Vector(7,0,0)) 119 | latch = latch.fuse(box) 120 | mount = mount.fuse(latch) 121 | 122 | #rod hole 123 | cylinder = Part.makeCylinder(z_rod_diameter/2,extrusion_profile*math.cos(math.radians(30))+thick_typical) 124 | cylinder.rotate(Base.Vector(0,0,0),Base.Vector(1,0,0),-90) 125 | cylinder.translate(Base.Vector(7+hole_spacing/2,0,0)) 126 | mount = mount.cut(cylinder) 127 | 128 | #housings for the bolt holes 129 | cylinder = Part.makeCylinder(7,thick_compress+nut_depth) 130 | cylinder.translate(Base.Vector(7,7,0)) 131 | mount = mount.fuse(cylinder) 132 | cylinder = Part.makeCylinder(7,thick_compress+nut_depth) 133 | cylinder.translate(Base.Vector(7+hole_spacing,7,0)) 134 | mount = mount.fuse(cylinder) 135 | 136 | #bolt holes 137 | cylinder = Part.makeCylinder(main_bolt_hole_diameter/2,z_rod_diameter/2+thick_typical) 138 | cylinder.translate(Base.Vector(7,7,0)) 139 | mount = mount.cut(cylinder) 140 | cylinder = Part.makeCylinder(main_bolt_hole_diameter/2,z_rod_diameter/2+thick_typical) 141 | cylinder.translate(Base.Vector(7+hole_spacing,7,0)) 142 | mount = mount.cut(cylinder) 143 | #captive nuts 144 | nuthole = regPolygon(sides = 6, radius = nut_width/2, extrude = nut_depth, Z_offset = 0) 145 | nuthole.translate(Base.Vector(7,7,thick_compress)) 146 | mount = mount.cut(nuthole) 147 | nuthole = regPolygon(sides = 6, radius = nut_width/2, extrude = nut_depth, Z_offset = 0) 148 | nuthole.translate(Base.Vector(7+hole_spacing,7,thick_compress)) 149 | mount = mount.cut(nuthole) 150 | 151 | #move the mount back into postive coordinates 152 | mount.translate(Base.Vector(((extrusion_hole_spacing-(extrusion_profile/2*math.cos(math.radians(30))-extrusion_profile/2*math.sin(math.radians(30)))*2)/2-(7+hole_spacing/2)),0,0)) 153 | mount.translate(Base.Vector(extrusion_profile*math.cos(math.radians(30)),0,0)) 154 | 155 | return mount 156 | 157 | if __name__ == "__main__": 158 | Part.show(ZMotorMount()) -------------------------------------------------------------------------------- /partsrc/ZRodLowerMount.py: -------------------------------------------------------------------------------- 1 | from __future__ import division # allows floating point division from integers 2 | import FreeCAD, Part, math 3 | from FreeCAD import Base 4 | 5 | def ZRodLowerMount( 6 | extrusion_profile = 20, 7 | z_rod_spacing = 30, 8 | z_rod_diameter = 8, 9 | z_screw_diameter = 9, 10 | hole_spacing = 30, 11 | #how thick should the non-flange parts be? 12 | thick_typical = 4.25, 13 | thick_min = 1, 14 | ): 15 | 16 | #TODO: calculate these 17 | extrusion_to_z_screw = 35 18 | thrust_bearing_diameter = 16 19 | thrust_bearing_depth = 4 20 | 21 | #TODO: 22 | #how big are the clear holes for structural bolts? 23 | bolt_hole_diameter = 5.5 24 | #how big are the to-be-threaded holes for structural bolts? 25 | screw_hole_diameter = 4.5 26 | screw_hole_depth = 10 27 | 28 | tower_x = extrusion_profile/2+hole_spacing 29 | plate_x = tower_x+extrusion_profile*2 30 | 31 | #plate to attach to extrusion 32 | box = Part.makeBox(plate_x-extrusion_profile,extrusion_profile,thick_typical) 33 | box.translate(Base.Vector(extrusion_profile/2,0,0)) 34 | vertex = box 35 | cylinder = Part.makeCylinder(extrusion_profile/2,thick_typical) 36 | cylinder.translate(Base.Vector(extrusion_profile/2,extrusion_profile/2,0)) 37 | vertex = vertex.fuse(cylinder) 38 | cylinder.translate(Base.Vector(plate_x-extrusion_profile,0,0)) 39 | vertex = vertex.fuse(cylinder) 40 | #bolt holes into extrusion 41 | cylinder = Part.makeCylinder(bolt_hole_diameter/2,thick_typical) 42 | cylinder.translate(Base.Vector(extrusion_profile/2,extrusion_profile/2,0)) 43 | vertex = vertex.cut(cylinder) 44 | cylinder.translate(Base.Vector(plate_x-extrusion_profile,0,0)) 45 | vertex = vertex.cut(cylinder) 46 | 47 | #"tower" 48 | box = Part.makeBox(tower_x-extrusion_profile,extrusion_profile,extrusion_to_z_screw+z_rod_spacing-thick_typical) 49 | box.translate(Base.Vector(plate_x/2-(tower_x-extrusion_profile)/2,0,thick_typical)) 50 | vertex = vertex.fuse(box) 51 | cylinder = Part.makeCylinder(extrusion_profile/2,extrusion_to_z_screw+z_rod_spacing-thick_typical) 52 | cylinder.translate(Base.Vector(plate_x/2-(tower_x-extrusion_profile)/2,extrusion_profile/2,thick_typical)) 53 | vertex = vertex.fuse(cylinder) 54 | cylinder = Part.makeCylinder(extrusion_profile/2,extrusion_to_z_screw+z_rod_spacing-thick_typical) 55 | cylinder.translate(Base.Vector(plate_x/2+(tower_x-extrusion_profile)/2,extrusion_profile/2,thick_typical)) 56 | vertex = vertex.fuse(cylinder) 57 | 58 | #screw holes 59 | cylinder = Part.makeCylinder(screw_hole_diameter/2,screw_hole_depth) 60 | cylinder.translate(Base.Vector(plate_x/2-hole_spacing/2,extrusion_profile/2,extrusion_to_z_screw+z_rod_spacing-screw_hole_depth)) 61 | vertex = vertex.cut(cylinder) 62 | cylinder.translate(Base.Vector(hole_spacing,0,0)) 63 | vertex = vertex.cut(cylinder) 64 | 65 | #smooth rod hole 66 | cylinder = Part.makeCylinder(z_rod_diameter/2,extrusion_profile) 67 | cylinder.rotate(Base.Vector(0,0,0),Base.Vector(1,0,0),-90) 68 | cylinder.translate(Base.Vector(plate_x/2,0,extrusion_to_z_screw+z_rod_spacing)) 69 | vertex = vertex.cut(cylinder) 70 | 71 | #threaded rod and thrust bearing hole 72 | cylinder = Part.makeCylinder(z_screw_diameter/2+1,extrusion_profile) 73 | cylinder2 = Part.makeCylinder(thrust_bearing_diameter/2+2,thrust_bearing_depth) 74 | cylinder = cylinder.fuse(cylinder2) 75 | cylinder.rotate(Base.Vector(0,0,0),Base.Vector(1,0,0),-90) 76 | cylinder.translate(Base.Vector(plate_x/2,0,extrusion_to_z_screw)) 77 | vertex = vertex.cut(cylinder) 78 | 79 | #eliminate some bulk from the part to save plastic 80 | #cut holes in the bottom, requires bridging that will not be visible on the installed part 81 | cylinder = Part.makeCylinder(extrusion_profile/2-(thick_typical+thick_min)/2,extrusion_to_z_screw*3/4) 82 | cylinder.translate(Base.Vector(plate_x/2-(tower_x-extrusion_profile)/2,extrusion_profile/2,0)) 83 | vertex = vertex.cut(cylinder) 84 | cylinder = Part.makeCylinder(extrusion_profile/2-(thick_typical+thick_min)/2,extrusion_to_z_screw*3/4) 85 | cylinder.translate(Base.Vector(plate_x/2+(tower_x-extrusion_profile)/2,extrusion_profile/2,0)) 86 | vertex = vertex.cut(cylinder) 87 | 88 | return vertex 89 | 90 | if __name__ == "__main__": 91 | Part.show(ZRodLowerMount()) 92 | -------------------------------------------------------------------------------- /partsrc/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxbots/MendelMax/fb90034768fc609448590c5ff41c85857e044a12/partsrc/__init__.py -------------------------------------------------------------------------------- /partsrc/accessories/ExtrusionClip.py: -------------------------------------------------------------------------------- 1 | from __future__ import division # allows floating point division from integers 2 | from FreeCAD import Base 3 | import math 4 | import sys 5 | import os 6 | 7 | try: 8 | path = os.path.join(os.path.dirname(__file__), '..', '..', 'include') 9 | i = sys.path.index(path) 10 | except: 11 | sys.path.append(path) 12 | 13 | # all measurements in mm 14 | 15 | # .-------. .-------. - 16 | # | | <-A-> | | B 17 | # | .----- -----. | - - 18 | # | | <-C-> | | _ D ^ 19 | # | \ / | | 20 | # | \ / | E 21 | # | \ / | v 22 | # | ----<-F->---- | - 23 | # 24 | # <--extrusion_profile--> 25 | 26 | extrusion_slot_opening_width = 6 #A 27 | extrusion_slot_opening_depth = 2 #B 28 | extrusion_slot_width = 11 #C 29 | extrusion_slot_vertical_depth = 1 #D 30 | extrusion_slot_depth = 4 #E 31 | #F=C-(E-D) as long as the diagonals are 45 degrees 32 | 33 | #minimum thickness 34 | #TODO: enforce this everywhere, alert the user when it is violated 35 | thick_min = 1 36 | 37 | hole_spacing_medium = 20 38 | 39 | from MendelMax import * 40 | 41 | clip_thickness = 2 42 | 43 | #clearance between printed part and metal surfaces 44 | gap_horizontal = 0.25 45 | gap_vertical = 0.1 46 | 47 | clip_length_padding = 4 48 | #TODO: use the bolt module once it's written 49 | bolt_diameter = 4 50 | 51 | #should the clip have "wings"? 52 | flange_width = 20 53 | #how many bolts/nuts does this part take? 54 | num_holes = 2 55 | 56 | base_padding = max(bolt_diameter/2+thick_min,clip_length_padding) 57 | hole_span = max((num_holes-1),0) * hole_spacing_medium 58 | clip_length = hole_span + base_padding*2 59 | clip_width = extrusion_profile + clip_thickness*2 + gap_horizontal*2 60 | 61 | clip_top = Part.makeBox(clip_length, clip_width + flange_width*2, clip_thickness) 62 | 63 | clip_arm_height = extrusion_profile/2 + extrusion_slot_opening_width/2 - gap_vertical 64 | clip_barb_width = extrusion_slot_opening_depth 65 | clip_arm_cross_section = Part.makePolygon([ 66 | Base.Vector(0,0,0), 67 | Base.Vector(0,0, -clip_arm_height), 68 | Base.Vector(0, clip_thickness+clip_barb_width, -(extrusion_profile/2 - extrusion_slot_opening_width/2 + gap_vertical)), 69 | Base.Vector(0, clip_thickness, -(extrusion_profile/2 - extrusion_slot_opening_width/2 + gap_vertical)), 70 | Base.Vector(0, clip_thickness,0), 71 | Base.Vector(0,0,0)]) 72 | face = Part.Face(clip_arm_cross_section) 73 | clip_arm = face.extrude(Base.Vector(clip_length,0,0)) 74 | clip_arm.translate(Base.Vector(0,flange_width,0)) 75 | clip = clip_top.fuse(clip_arm) 76 | clip_arm.rotate(Base.Vector(clip_length/2,(clip_width+flange_width*2)/2,0),Base.Vector(0,0,1),180) 77 | clip = clip.fuse(clip_arm) 78 | 79 | hole = Part.makeCylinder(bolt_diameter/2,clip_thickness) 80 | hole.translate(Base.Vector(base_padding,(clip_width+flange_width*2)/2,0)) 81 | for n in range(num_holes): 82 | clip = clip.cut(hole) 83 | if(flange_width): 84 | hole.translate(Base.Vector(0,clip_width/2+flange_width/2,0)) 85 | clip = clip.cut(hole) 86 | hole.translate(Base.Vector(0,-(clip_width+flange_width),0)) 87 | clip = clip.cut(hole) 88 | hole.translate(Base.Vector(0,clip_width/2+flange_width/2,0)) 89 | hole.translate(Base.Vector(hole_spacing_medium,0,0)) 90 | 91 | clip.translate(Base.Vector(0,0,-clip_thickness)) 92 | clip.rotate(Base.Vector(clip_length/2,0,0),Base.Vector(0,1,0),180) 93 | 94 | Part.show(clip) 95 | -------------------------------------------------------------------------------- /partsrc/accessories/TSlotNut.py: -------------------------------------------------------------------------------- 1 | from __future__ import division # allows floating point division from integers 2 | from FreeCAD import Base 3 | import math 4 | import sys 5 | import os 6 | 7 | try: 8 | path = os.path.join(os.path.dirname(__file__), '..', '..', 'include') 9 | i = sys.path.index(path) 10 | except: 11 | sys.path.append(path) 12 | 13 | from RegularPolygon import regPolygon 14 | 15 | # all measurements in mm 16 | 17 | # .-------. .-------. - 18 | # | | <-A-> | | B 19 | # | .----- -----. | - - 20 | # | | <-C-> | | _ D ^ 21 | # | \ / | | 22 | # | \ / | E 23 | # | \ / | v 24 | # | ----<-F->---- | - 25 | # 26 | 27 | extrusion_slot_opening_width = 6 #A 28 | extrusion_slot_opening_depth = 2 #B 29 | extrusion_slot_width = 11 #C 30 | extrusion_slot_vertical_depth = 1 #D 31 | extrusion_slot_depth = 4 #E 32 | #F=C-(E-D) as long as the diagonals are 45 degrees 33 | 34 | #TODO: use the bolt module once it's written 35 | bolt_diameter = 4 36 | metal_nut_width = 8 37 | 38 | #minimum thickness 39 | #TODO: enforce this everywhere, alert the user when it is violated 40 | thick_min = 1 41 | 42 | hole_spacing_medium = 20 43 | 44 | #from MendelMax import * 45 | 46 | #clearance between printed part and metal surfaces 47 | gap_horizontal = 0.25 48 | gap_vertical = 0.1 49 | 50 | #how many bolts/nuts does this part take? 51 | num_holes = 2 52 | 53 | base_padding = max(bolt_diameter/2,metal_nut_width * (1/math.sqrt(3)))+thick_min 54 | hole_span = (num_holes-1) * hole_spacing_medium 55 | nut_length = hole_span + base_padding*2 56 | 57 | nut_top = Part.makeBox(nut_length, extrusion_slot_opening_width - gap_horizontal*2, extrusion_slot_opening_depth - gap_vertical) 58 | 59 | nut_bottom_width = extrusion_slot_width - gap_horizontal*2 60 | nut_bottom_height = extrusion_slot_depth - gap_vertical 61 | nut_bottom_cross_section = Part.makePolygon([ 62 | Base.Vector(0,0,0), 63 | Base.Vector(0,nut_bottom_width,0), 64 | Base.Vector(0,nut_bottom_width,-(extrusion_slot_vertical_depth-gap_vertical)), 65 | Base.Vector(0,nut_bottom_width - (nut_bottom_height-(extrusion_slot_vertical_depth-gap_vertical)),-nut_bottom_height), 66 | Base.Vector(0,nut_bottom_height-(extrusion_slot_vertical_depth-gap_vertical),-nut_bottom_height), 67 | Base.Vector(0,0,-(extrusion_slot_vertical_depth-gap_vertical)), 68 | Base.Vector(0,0,0)]) 69 | face = Part.Face(nut_bottom_cross_section) 70 | nut_bottom = face.extrude(Base.Vector(nut_length,0,0)) 71 | nut_bottom.translate(Base.Vector(0,-(nut_bottom_width-(extrusion_slot_opening_width - 0.5))/2,0)) 72 | nut = nut_bottom.fuse(nut_top) 73 | 74 | bolthole = Part.makeCylinder(bolt_diameter/2,extrusion_slot_opening_depth - gap_vertical) 75 | nuthole = regPolygon(sides = 6, radius = metal_nut_width/2, extrude = nut_bottom_height, Z_offset = -nut_bottom_height) 76 | holes = bolthole.fuse(nuthole) 77 | holes.translate(Base.Vector(base_padding,(extrusion_slot_opening_width-gap_horizontal*2)/2,0)) 78 | for n in range(num_holes): 79 | nut = nut.cut(holes) 80 | holes.translate(Base.Vector(hole_spacing_medium,0,0)) 81 | 82 | #bring back into octant 1 83 | nut.translate(Base.Vector(0,(nut_bottom_width - (extrusion_slot_opening_width - gap_horizontal*2))/2,nut_bottom_height)) 84 | 85 | Part.show(nut) 86 | -------------------------------------------------------------------------------- /partsrc/optional_assembly/README: -------------------------------------------------------------------------------- 1 | Optional parts used only during the assembly process, but not as part of the printer, go here. Drill guides, spacers, etc. 2 | --------------------------------------------------------------------------------