├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── examples ├── 3D_Printer_Extruder_Support.py ├── Braille.py ├── Classic_OCC_Bottle.py ├── Involute_Gear.py ├── Iqon-Building ├── Numpy.py ├── Panel_with_Various_Holes_for_Connector_Installation.py ├── Parametric_Enclosure.py ├── Reinforce_Junction_UsingFillet.py ├── Remote_Enclosure.py ├── Resin_Mold.py ├── Shelled_Cube_Inside_Chamfer_With_Logical_Selector_Operators.py ├── Tetrakaidecahedron.py ├── Thread.py ├── cylindrical_gear.py ├── door.py ├── hexagonal_drawers │ ├── assembly.py │ ├── base.py │ ├── hmd.jpg │ ├── hmd.png │ ├── organiser_3_125_bits.py │ ├── organiser_blank.py │ └── organiser_collets.py ├── images │ ├── Classic_OCC_Bottle.png │ ├── Involute_Gear.png │ ├── Numpy.png │ ├── Parametric_Enclosure.png │ ├── Remote_Enclosure.png │ ├── Tetrakaidecahedron.png │ ├── Thread.png │ ├── braille.png │ ├── cylindrical_gear.png │ ├── door.png │ ├── panel_with_various_holes.png │ ├── reinforce_junction_using_fillet.png │ ├── resin_mold.png │ ├── shelled_cube.png │ └── tray.png ├── tray.py ├── truss-gherkin-building └── vslot-2020_1.dxf ├── grouped-tiles ├── partcad.yaml └── tutorials ├── Ex000 Start Here.ipynb ├── Ex001 Simple Block.ipynb └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | # don't display diffs or treat the following as text files 2 | *.png -text -diff 3 | *.jpg -text -diff 4 | *.dxf -text -diff 5 | 6 | # Jupyter notebooks should be classified as documentation and not counted as lines of code 7 | *.ipynb linguist-documentation -diff -text 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .ipynb_checkpoints 2 | tray 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Dave Cowden 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cadquery-contrib 2 | A place to share CadQuery scripts, modules, tutorials and projects 3 | 4 | ## Contents 5 | 6 | ### Examples 7 | 8 | * [Braille.py](examples/Braille.py) - Configurable braille label/sign generator where user inputs text and the braille dots are generated automatically 9 | 10 | 11 | 12 | 13 | * [Panel_with_Various_Holes_for_Connector_Installation.py](examples/Panel_with_Various_Holes_for_Connector_Installation.py) - Example of creating various knock-out holes in a panel 14 | 15 | 16 | 17 | * [Parametric_Enclosure.py](examples/Parametric_Enclosure.py) - Standard CadQuery example of an electronics enclosure with a base, fastener bosses, and a lid 18 | 19 | 20 | 21 | * [Reinforce_Junction_UsingFillet.py](examples/Reinforce_Junction_UsingFillet.py) - Example of using fillets to reinforce a joint, reducing stress concentrators at the joint 22 | 23 | 24 | 25 | 26 | * [Resin_Mold.py](examples/Resin_Mold.py) - A resin casting mold created to repair the strain-relief on an expensive cable 27 | 28 | 29 | 30 | * [Shelled_Cube_Inside_Chamfer_With_Logical_Selector_Operators.py](examples/Shelled_Cube_Inside_Chamfer_With_Logical_Selector_Operators.py) - Shows a somewhat more advanced use of selectors to chamfer the inside edges of a shelled cube 31 | 32 | 33 | 34 | * [tray.py](examples/tray.py) - Manual assembly example including export to DXF for laser cutting 35 | 36 | 37 | 38 | * [Tetrakaidecahedron.py](examples/Tetrakaidecahedron.py) - The Tetrakaidecahedron volume (Kelvin Cell) can pave 3D space and is often encountered in cristallography. 39 | 40 | 41 | 42 | * [cylindrical_gear.py](examples/cylindrical_gear.py) - A cylindrical straight or helix gear 43 | 44 | 45 | 46 | * [Remote_Enclosure.py](examples/Remote_Enclosure.py) - An electronics enclosure created to be mounted on motorcycle handlebars 47 | 48 | 49 | 50 | * [Classic_OCC_Bottle.py](examples/Classic_OCC_Bottle.py) - Standard OCCT bottle example, implemented using the CadQuery API 51 | 52 | 53 | 54 | * [Numpy.py](examples/Numpy.py) - Example of integrating Numpy with CadQuery 55 | 56 | 57 | 58 | * [3D_Printer_Extruder_Support.py](examples/3D_Printer_Extruder_Support.py) - Designed for mounting hotend to an i3 X-carriage inspired by the P3steel Toolson 59 | 60 | * [Involute_Gear.py](examples/Involute_Gear.py) - Fast involute gear generator. 61 | 62 | 63 | 64 | * [Thread.py](examples/Thread.py) - Thread example. 65 | 66 | 67 | 68 | * [Hexagonal modular drawers](examples/hexagonal_drawers/assembly.py) - Inspired by [this on Prusa Printers](https://www.prusaprinters.org/prints/54113-hexagonal-organizer-system), these drawers are 3D printed (without needing supports) and clip together. 69 | 70 | 71 | 72 | 73 | * [Digital sundial](https://github.com/lopezsolerluis/reloj-de-sol-digital-cadquery) - Inspired by [Mojoptix's digital sundial](https://www.thingiverse.com/thing:1068443) and derived from [my own version](https://github.com/lopezsolerluis/reloj-de-sol-digital) in OpenSCAD. 74 | 75 | 76 | 77 | * [Truss Gherkin with spirals](https://github.com/moOsama76/cadquery-contrib/blob/moOsama76-patch-1/examples/truss-gherkin-building) - Inspired by [3D Beast](https://www.youtube.com/watch?v=mwJQm70NRYY) 78 | 79 | 80 | 81 | * [IQON Building following a spline](https://github.com/moOsama76/cadquery-contrib/blob/moOsama76-patch-1/examples/Iqon-Building) - Inspired by [3D Beast](https://www.youtube.com/watch?v=wQvikT0DUCU) 82 | 83 | 84 | 85 | * [Tiles any shape](https://github.com/moOsama76/cadquery-contrib/blob/moOsama76-patch-1/grouped-tiles) - grid of tiles constrained to any 2D shape and seperated into individual groups 86 | 87 | 88 | 89 | ### Tutorials 90 | 91 | * [Ex000 Start Here.ipynb](tutorials/Ex000%20Start%20Here.ipynb) - iPython notebook that is the entry point for a set of CadQuery tutorials 92 | * [Ex001 Simple Block.ipynb](tutorials/Ex001%20Simple%20Block.ipynb) - iPython notebook that shows how to create an extremely simple block 93 | 94 | -------------------------------------------------------------------------------- /examples/3D_Printer_Extruder_Support.py: -------------------------------------------------------------------------------- 1 | # 3d printer for mounting hotend to X-carriage inspired by the P3steel Toolson 2 | # edition - http://www.thingiverse.com/thing:1054909 3 | import cadquery as cq 4 | 5 | 6 | def move_to_center(cqObject, shape): 7 | ''' 8 | Moves the origin of the current Workplane to the center of a given 9 | geometry object 10 | ''' 11 | 12 | # transform to workplane local coordinates 13 | shape_center = shape.Center().sub(cqObject.plane.origin) 14 | 15 | # project onto plane using dot product 16 | x_offset = shape_center.dot(cqObject.plane.xDir) 17 | y_offset = shape_center.dot(cqObject.plane.yDir) 18 | 19 | return cqObject.center(x_offset, y_offset) 20 | 21 | # Parameter definitions 22 | 23 | main_plate_size_y = 67 # size of the main plate in y direction 24 | main_plate_size_x = 50. # size of the main plate in x direction 25 | main_plate_thickness = 10. # thickness of the main plate 26 | 27 | wing_size_x = 10. # size of the side wing supporting the bridge in x direction 28 | wing_size_y = 10. # size of the side wing supporting the bridge in y direction 29 | 30 | bridge_depth = 35. # depth of the bridge 31 | 32 | support_depth = 18. # depth of the bridge support 33 | 34 | cutout_depth = 15. # depth of the hotend cutout 35 | cutout_rad = 8. # radius of the cutout (cf groove mount sizes of E3D hotends) 36 | cutout_offset = 2. # delta radius of the second cutout (cf groove mount sizes of E3D hotends) 37 | 38 | extruder_hole_spacing = 50. # spacing of the extruder mounting holes (Wade's geared extruder) 39 | 40 | m4_predrill = 3.7 # hole diameter for m4 tapping 41 | m3_predrill = 2.5 # hole diameter for m3 tapping 42 | m3_cbore = 5. # counterbore size for m3 socket screw 43 | 44 | mounting_hole_spacing = 28. # spacing of the mounting holes for attaching to x-carriage 45 | 46 | aux_hole_depth = 6. # depth of the auxiliary holes at the sides of the object 47 | aux_hole_spacing = 5. # spacing of the auxiliary holes within a group 48 | aux_hole_N = 2 # number of the auxiliary hole per group 49 | 50 | # make the main plate 51 | res = cq.Workplane('front').box(main_plate_size_x, 52 | main_plate_size_y, 53 | main_plate_thickness) 54 | 55 | 56 | def add_wing(obj, sign=1): 57 | ''' 58 | Adds a wing to the main plate, defined to keep the code DRY 59 | ''' 60 | obj = obj.workplane()\ 61 | .hLine(sign*wing_size_x)\ 62 | .vLine(-wing_size_y)\ 63 | .line(-sign*wing_size_x, -2*wing_size_y)\ 64 | .close().extrude(main_plate_thickness) 65 | return obj 66 | 67 | # add wings 68 | 69 | # add right wing 70 | res = res.faces('XY') 71 | res = add_wing(res) 72 | 73 | # store sides of the plate for further reuse, their area is used later on to calculate "optimum" spacing of the aux hole groups 74 | face_right = res.faces('>X[1]').val() 75 | face_left = res.faces('>X[-2]').val() 76 | 77 | # add left wing 78 | res = res.faces('Y').vertices('Z') # select top face 83 | e = wp.edges('>Y') # select most extreme edge in Y direction 84 | 85 | bridge_length = e.val().Length() # the width of the bridge equals to the length of the selected edge 86 | 87 | # draw the bridge x-section and extrude 88 | res = e.vertices('Z[1]') # take all faces in Z direction and select the middle one; note the new selector syntax 96 | edge = faces.edges('>Y') # select the top edge of this face... 97 | res = move_to_center(faces.workplane(), edge.val()).\ 98 | transformed(rotate=(0, 90, 0)) # ...and make a workplane that is centered in this edge and oriented along X direction 99 | 100 | res = res.vLine(-support_depth).\ 101 | line(-support_depth, support_depth).\ 102 | close() # draw a triangle 103 | 104 | res = res.extrude(main_plate_size_x/2, both=True, clean=True) # extrude the triangle, now the bridge has a nice support making it much more stiff 105 | 106 | # Start cutting out a slot for hotend mounting 107 | face = res.faces('>Y') # select the most extreme face in Y direction, i.e. top of the "bridge" 108 | res = move_to_center(face.workplane(), face.edges('>Z').val()) # shift the workplane to the center of the most extreme edge of the bridge 109 | 110 | 111 | def make_slot(obj, depth=None): 112 | ''' 113 | Utility function that makes a slot for hotend mounting 114 | ''' 115 | obj = obj.moveTo(cutout_rad, -cutout_depth).\ 116 | threePointArc((0, -cutout_depth-cutout_rad), 117 | (-cutout_rad, -cutout_depth)).\ 118 | vLineTo(0).hLineTo(cutout_rad).close() 119 | 120 | if depth is None: 121 | obj = obj.cutThruAll() 122 | else: 123 | obj = obj.cutBlind(depth) 124 | 125 | return obj 126 | 127 | res = make_slot(res, None) # make the smaller slot 128 | 129 | cutout_rad += cutout_offset # increase the cutout radius... 130 | res = make_slot(res.end().end(), -main_plate_thickness/2) # ...and make a slightly larger slot 131 | 132 | res = res.end().moveTo(0, 0) \ 133 | .pushPoints([(-extruder_hole_spacing/2, -cutout_depth), (extruder_hole_spacing/2, -cutout_depth)]) \ 134 | .hole(m4_predrill) # add extruder mounting holes at the top of the bridge 135 | 136 | 137 | # make additional slot in the bridge support which allows the hotend's radiator to fit 138 | cutout_rad += 3*cutout_offset 139 | res = make_slot(res.end().moveTo(0, 0).workplane(offset=-main_plate_thickness)) 140 | 141 | # add reinforcement holes 142 | cutout_rad -= 2*cutout_offset 143 | res = res.faces('>Z').workplane().\ 144 | pushPoints([(-cutout_rad, -main_plate_thickness/4), 145 | (cutout_rad, -main_plate_thickness/4)]).\ 146 | hole(m3_predrill) 147 | 148 | # add aux holes on the front face 149 | res = res.moveTo(-main_plate_size_x/2., 0).workplane().rarray(aux_hole_spacing, 1, aux_hole_N, 1) \ 150 | .hole(m3_predrill, depth=aux_hole_depth) 151 | res = res.moveTo(main_plate_size_x, 0).workplane().rarray(aux_hole_spacing, 1, aux_hole_N, 1) \ 152 | .hole(m3_predrill, depth=aux_hole_depth) 153 | 154 | # make a hexagonal cutout 155 | res = res.faces('>Z[1]') 156 | res = res.workplane(offset=bridge_depth). \ 157 | transformed(rotate=(0, 0, 90)). \ 158 | polygon(6, 30).cutThruAll() 159 | 160 | # make 4 mounting holes with cbores 161 | res = res.end().moveTo(0, 0). \ 162 | rect(mounting_hole_spacing, 163 | mounting_hole_spacing, forConstruction=True) 164 | 165 | res = res.vertices(). \ 166 | cboreHole(m3_predrill, 167 | m3_cbore, 168 | bridge_depth+m3_cbore/2) 169 | 170 | # make cutout and holes for mounting of the fan 171 | res = res.transformed(rotate=(0, 0, 45)). \ 172 | rect(35, 35).cutBlind(-bridge_depth).end(). \ 173 | rect(25, 25, forConstruction=True).vertices().hole(m3_predrill) 174 | 175 | 176 | def make_aux_holes(workplane, holes_span, N_hole_groups=3): 177 | ''' 178 | Utility function for creation of auxiliary mouting holes at the sides of the object 179 | ''' 180 | res = workplane.moveTo(-holes_span/2).workplane().rarray(aux_hole_spacing, 1, aux_hole_N, 1) \ 181 | .hole(m3_predrill, depth=aux_hole_depth) 182 | for i in range(N_hole_groups-1): 183 | res = res.moveTo(holes_span/(N_hole_groups-1.)).workplane().rarray(aux_hole_spacing, 1, aux_hole_N, 1) \ 184 | .hole(m3_predrill, depth=aux_hole_depth) 185 | 186 | return res 187 | 188 | # make aux holes at the bottom 189 | res = res.faces('X').workplane().transformed((90, 0, 0)) 196 | res = make_aux_holes(res, main_plate_size_x*2/3., 3) 197 | 198 | # make aux holes at the side (@main plate) 199 | res = res.faces('|X').edges('X') 200 | res = res.workplane() 201 | res = move_to_center(res, face_right) 202 | res = res.transformed((90, 0, 0)) 203 | hole_sep = 0.5*face_right.Area()/main_plate_thickness 204 | res = make_aux_holes(res, hole_sep, 2) 205 | 206 | # make aux holes at the side (@main plate) 207 | res = res.faces('|X').edges('