Spaces:
Sleeping
Sleeping
| .. _examples: | |
| .. currentmodule:: cadquery | |
| ********************************* | |
| Examples | |
| ********************************* | |
| The examples on this page can help you learn how to build objects with CadQuery. | |
| They are organized from simple to complex, so working through them in order is the best way to absorb them. | |
| Each example lists the API elements used in the example for easy reference. | |
| Items introduced in the example are marked with a **!** | |
| .. note:: | |
| We strongly recommend installing `CQ-editor <https://github.com/CadQuery/CQ-editor>`_, | |
| so that you can work along with these examples interactively. See :ref:`installation` for more info. | |
| If you do, make sure to take these steps so that they work: | |
| 1. import cadquery as cq | |
| 2. add the line ``show_object(result)`` at the end. The samples below are autogenerated, but they use a different | |
| syntax than the models on the website need to be. | |
| .. contents:: List of Examples | |
| :backlinks: entry | |
| Simple Rectangular Plate | |
| ------------------------ | |
| Just about the simplest possible example, a rectangular box | |
| .. cadquery:: | |
| result = cadquery.Workplane("front").box(2.0, 2.0, 0.5) | |
| .. topic:: Api References | |
| .. hlist:: | |
| :columns: 2 | |
| * :py:meth:`Workplane` **!** | |
| * :py:meth:`Workplane.box` **!** | |
| Plate with Hole | |
| ------------------------ | |
| A rectangular box, but with a hole added. | |
| "\>Z" selects the top most face of the resulting box. The hole is located in the center because the default origin | |
| of a working plane is the projected origin of the last Workplane, the last Workplane having origin at (0,0,0) the | |
| projection is at the center of the face. The default hole depth is through the entire part. | |
| .. cadquery:: | |
| # The dimensions of the box. These can be modified rather than changing the | |
| # object's code directly. | |
| length = 80.0 | |
| height = 60.0 | |
| thickness = 10.0 | |
| center_hole_dia = 22.0 | |
| # Create a box based on the dimensions above and add a 22mm center hole | |
| result = ( | |
| cq.Workplane("XY") | |
| .box(length, height, thickness) | |
| .faces(">Z") | |
| .workplane() | |
| .hole(center_hole_dia) | |
| ) | |
| .. topic:: Api References | |
| .. hlist:: | |
| :columns: 2 | |
| * :py:meth:`Workplane.hole` **!** | |
| * :py:meth:`Workplane.box` | |
| * :py:meth:`Workplane.box` | |
| An extruded prismatic solid | |
| ------------------------------- | |
| Build a prismatic solid using extrusion. After a drawing operation, the center of the previous object | |
| is placed on the stack, and is the reference for the next operation. So in this case, the rect() is drawn | |
| centered on the previously draw circle. | |
| By default, rectangles and circles are centered around the previous working point. | |
| .. cadquery:: | |
| result = cq.Workplane("front").circle(2.0).rect(0.5, 0.75).extrude(0.5) | |
| .. topic:: Api References | |
| .. hlist:: | |
| :columns: 2 | |
| * :py:meth:`Workplane.circle` **!** | |
| * :py:meth:`Workplane.rect` **!** | |
| * :py:meth:`Workplane.extrude` **!** | |
| * :py:meth:`Workplane` | |
| Building Profiles using lines and arcs | |
| -------------------------------------- | |
| Sometimes you need to build complex profiles using lines and arcs. This example builds a prismatic | |
| solid from 2D operations. | |
| 2D operations maintain a current point, which is initially at the origin. Use close() to finish a | |
| closed curve. | |
| .. cadquery:: | |
| result = ( | |
| cq.Workplane("front") | |
| .lineTo(2.0, 0) | |
| .lineTo(2.0, 1.0) | |
| .threePointArc((1.0, 1.5), (0.0, 1.0)) | |
| .close() | |
| .extrude(0.25) | |
| ) | |
| .. topic:: Api References | |
| .. hlist:: | |
| :columns: 2 | |
| * :py:meth:`Workplane.threePointArc` **!** | |
| * :py:meth:`Workplane.lineTo` **!** | |
| * :py:meth:`Workplane.extrude` | |
| * :py:meth:`Workplane` | |
| Moving The Current working point | |
| --------------------------------- | |
| In this example, a closed profile is required, with some interior features as well. | |
| This example also demonstrates using multiple lines of code instead of longer chained commands, | |
| though of course in this case it was possible to do it in one long line as well. | |
| A new work plane center can be established at any point. | |
| .. cadquery:: | |
| result = cq.Workplane("front").circle( | |
| 3.0 | |
| ) # current point is the center of the circle, at (0, 0) | |
| result = result.center(1.5, 0.0).rect(0.5, 0.5) # new work center is (1.5, 0.0) | |
| result = result.center(-1.5, 1.5).circle(0.25) # new work center is (0.0, 1.5). | |
| # The new center is specified relative to the previous center, not global coordinates! | |
| result = result.extrude(0.25) | |
| .. topic:: Api References | |
| .. hlist:: | |
| :columns: 2 | |
| * :py:meth:`Workplane.center` **!** | |
| * :py:meth:`Workplane` | |
| * :py:meth:`Workplane.circle` | |
| * :py:meth:`Workplane.rect` | |
| * :py:meth:`Workplane.extrude` | |
| Using Point Lists | |
| --------------------------- | |
| Sometimes you need to create a number of features at various locations, and using :py:meth:`Workplane.center` | |
| is too cumbersome. | |
| You can use a list of points to construct multiple objects at once. Most construction methods, | |
| like :py:meth:`Workplane.circle` and :py:meth:`Workplane.rect`, will operate on multiple points if they are on the stack | |
| .. cadquery:: | |
| r = cq.Workplane("front").circle(2.0) # make base | |
| r = r.pushPoints( | |
| [(1.5, 0), (0, 1.5), (-1.5, 0), (0, -1.5)] | |
| ) # now four points are on the stack | |
| r = r.circle(0.25) # circle will operate on all four points | |
| result = r.extrude(0.125) # make prism | |
| .. topic:: Api References | |
| .. hlist:: | |
| :columns: 2 | |
| * :py:meth:`Workplane.pushPoints` **!** | |
| * :py:meth:`Workplane` | |
| * :py:meth:`Workplane.circle` | |
| * :py:meth:`Workplane.extrude` | |
| Polygons | |
| ------------------------- | |
| You can create polygons for each stack point if you would like. Useful in 3d printers whose firmware does not | |
| correct for small hole sizes. | |
| .. cadquery:: | |
| result = ( | |
| cq.Workplane("front") | |
| .box(3.0, 4.0, 0.25) | |
| .pushPoints([(0, 0.75), (0, -0.75)]) | |
| .polygon(6, 1.0) | |
| .cutThruAll() | |
| ) | |
| .. topic:: Api References | |
| .. hlist:: | |
| :columns: 2 | |
| * :py:meth:`Workplane.polygon` **!** | |
| * :py:meth:`Workplane.pushPoints` | |
| * :py:meth:`Workplane.box` | |
| Polylines | |
| ------------------------- | |
| :py:meth:`Workplane.polyline` allows creating a shape from a large number of chained points connected by lines. | |
| This example uses a polyline to create one half of an i-beam shape, which is mirrored to create the final profile. | |
| .. cadquery:: | |
| (L, H, W, t) = (100.0, 20.0, 20.0, 1.0) | |
| pts = [ | |
| (0, H / 2.0), | |
| (W / 2.0, H / 2.0), | |
| (W / 2.0, (H / 2.0 - t)), | |
| (t / 2.0, (H / 2.0 - t)), | |
| (t / 2.0, (t - H / 2.0)), | |
| (W / 2.0, (t - H / 2.0)), | |
| (W / 2.0, H / -2.0), | |
| (0, H / -2.0), | |
| ] | |
| result = cq.Workplane("front").polyline(pts).mirrorY().extrude(L) | |
| .. topic:: Api References | |
| .. hlist:: | |
| :columns: 2 | |
| * :py:meth:`Workplane.polyline` **!** | |
| * :py:meth:`Workplane` | |
| * :py:meth:`Workplane.mirrorY` | |
| * :py:meth:`Workplane.extrude` | |
| Defining an Edge with a Spline | |
| ------------------------------ | |
| This example defines a side using a spline curve through a collection of points. Useful when you have an edge that | |
| needs a complex profile | |
| .. cadquery:: | |
| s = cq.Workplane("XY") | |
| sPnts = [ | |
| (2.75, 1.5), | |
| (2.5, 1.75), | |
| (2.0, 1.5), | |
| (1.5, 1.0), | |
| (1.0, 1.25), | |
| (0.5, 1.0), | |
| (0, 1.0), | |
| ] | |
| r = s.lineTo(3.0, 0).lineTo(3.0, 1.0).spline(sPnts, includeCurrent=True).close() | |
| result = r.extrude(0.5) | |
| .. topic:: Api References | |
| .. hlist:: | |
| :columns: 2 | |
| * :py:meth:`Workplane.spline` **!** | |
| * :py:meth:`Workplane` | |
| * :py:meth:`Workplane.close` | |
| * :py:meth:`Workplane.lineTo` | |
| * :py:meth:`Workplane.extrude` | |
| Mirroring Symmetric Geometry | |
| ----------------------------- | |
| You can mirror 2D geometry when your shape is symmetric. In this example we also | |
| introduce horizontal and vertical lines, which make for slightly easier coding. | |
| .. cadquery:: | |
| r = cq.Workplane("front").hLine(1.0) # 1.0 is the distance, not coordinate | |
| r = ( | |
| r.vLine(0.5).hLine(-0.25).vLine(-0.25).hLineTo(0.0) | |
| ) # hLineTo allows using xCoordinate not distance | |
| result = r.mirrorY().extrude(0.25) # mirror the geometry and extrude | |
| .. topic:: Api References | |
| .. hlist:: | |
| :columns: 2 | |
| * :py:meth:`Workplane.hLine` **!** | |
| * :py:meth:`Workplane.vLine` **!** | |
| * :py:meth:`Workplane.hLineTo` **!** | |
| * :py:meth:`Workplane.mirrorY` **!** | |
| * :py:meth:`Workplane.mirrorX` **!** | |
| * :py:meth:`Workplane` | |
| * :py:meth:`Workplane.extrude` | |
| Mirroring 3D Objects | |
| ----------------------------- | |
| .. cadquery:: | |
| result0 = ( | |
| cadquery.Workplane("XY") | |
| .moveTo(10, 0) | |
| .lineTo(5, 0) | |
| .threePointArc((3.9393, 0.4393), (3.5, 1.5)) | |
| .threePointArc((3.0607, 2.5607), (2, 3)) | |
| .lineTo(1.5, 3) | |
| .threePointArc((0.4393, 3.4393), (0, 4.5)) | |
| .lineTo(0, 13.5) | |
| .threePointArc((0.4393, 14.5607), (1.5, 15)) | |
| .lineTo(28, 15) | |
| .lineTo(28, 13.5) | |
| .lineTo(24, 13.5) | |
| .lineTo(24, 11.5) | |
| .lineTo(27, 11.5) | |
| .lineTo(27, 10) | |
| .lineTo(22, 10) | |
| .lineTo(22, 13.2) | |
| .lineTo(14.5, 13.2) | |
| .lineTo(14.5, 10) | |
| .lineTo(12.5, 10) | |
| .lineTo(12.5, 13.2) | |
| .lineTo(5.5, 13.2) | |
| .lineTo(5.5, 2) | |
| .threePointArc((5.793, 1.293), (6.5, 1)) | |
| .lineTo(10, 1) | |
| .close() | |
| ) | |
| result = result0.extrude(100) | |
| result = result.rotate((0, 0, 0), (1, 0, 0), 90) | |
| result = result.translate(result.val().BoundingBox().center.multiply(-1)) | |
| mirXY_neg = result.mirror(mirrorPlane="XY", basePointVector=(0, 0, -30)) | |
| mirXY_pos = result.mirror(mirrorPlane="XY", basePointVector=(0, 0, 30)) | |
| mirZY_neg = result.mirror(mirrorPlane="ZY", basePointVector=(-30, 0, 0)) | |
| mirZY_pos = result.mirror(mirrorPlane="ZY", basePointVector=(30, 0, 0)) | |
| result = result.union(mirXY_neg).union(mirXY_pos).union(mirZY_neg).union(mirZY_pos) | |
| .. topic:: Api References | |
| .. hlist:: | |
| :columns: 2 | |
| * :py:meth:`Workplane.moveTo` | |
| * :py:meth:`Workplane.lineTo` | |
| * :py:meth:`Workplane.threePointArc` | |
| * :py:meth:`Workplane.extrude` | |
| * :py:meth:`Workplane.mirror` | |
| * :py:meth:`Workplane.union` | |
| * :py:meth:`Workplane.rotate` | |
| Mirroring From Faces | |
| ----------------------------- | |
| This example shows how you can mirror about a selected face. It also shows how the resulting mirrored object can be unioned immediately with the referenced mirror geometry. | |
| .. cadquery:: | |
| result = cq.Workplane("XY").line(0, 1).line(1, 0).line(0, -0.5).close().extrude(1) | |
| result = result.mirror(result.faces(">X"), union=True) | |
| .. topic:: Api References | |
| .. hlist:: | |
| :columns: 2 | |
| * :py:meth:`Workplane.line` | |
| * :py:meth:`Workplane.close` | |
| * :py:meth:`Workplane.extrude` | |
| * :py:meth:`Workplane.faces` | |
| * :py:meth:`Workplane.mirror` | |
| * :py:meth:`Workplane.union` | |
| Creating Workplanes on Faces | |
| ----------------------------- | |
| This example shows how to locate a new workplane on the face of a previously created feature. | |
| .. note:: | |
| Using workplanes in this way are a key feature of CadQuery. Unlike a typical 3d scripting | |
| language, using work planes frees you from tracking the position of various features in | |
| variables, and allows the model to adjust itself with removing redundant dimensions | |
| The :py:meth:`Workplane.faces()` method allows you to select the faces of a resulting solid. It | |
| accepts a selector string or object, that allows you to target a single face, and make a workplane | |
| oriented on that face. | |
| Keep in mind that by default the origin of a new workplane is calculated by forming a plane from the | |
| selected face and projecting the previous origin onto that plane. This behaviour can be changed | |
| through the centerOption argument of :py:meth:`Workplane.workplane`. | |
| .. cadquery:: | |
| result = cq.Workplane("front").box(2, 3, 0.5) # make a basic prism | |
| result = ( | |
| result.faces(">Z").workplane().hole(0.5) | |
| ) # find the top-most face and make a hole | |
| .. topic:: Api References | |
| .. hlist:: | |
| :columns: 2 | |
| * :py:meth:`Workplane.faces` **!** | |
| * :py:meth:`StringSyntaxSelector` **!** | |
| * :ref:`selector_reference` **!** | |
| * :py:meth:`Workplane.workplane` | |
| * :py:meth:`Workplane.box` | |
| * :py:meth:`Workplane` | |
| Locating a Workplane on a vertex | |
| --------------------------------- | |
| Normally, the :py:meth:`Workplane.workplane` method requires a face to be selected. But if a vertex | |
| is selected **immediately after a face**, :py:meth:`Workplane.workplane` with the centerOption | |
| argument set to CenterOfMass will locate the workplane on the face, with the origin at the vertex | |
| instead of at the center of the face | |
| The example also introduces :py:meth:`Workplane.cutThruAll`, which makes a cut through the entire | |
| part, no matter how deep the part is. | |
| .. cadquery:: | |
| result = cq.Workplane("front").box(3, 2, 0.5) # make a basic prism | |
| result = ( | |
| result.faces(">Z").vertices("<XY").workplane(centerOption="CenterOfMass") | |
| ) # select the lower left vertex and make a workplane | |
| result = result.circle(1.0).cutThruAll() # cut the corner out | |
| .. topic:: Api References | |
| .. hlist:: | |
| :columns: 2 | |
| * :py:meth:`Workplane.cutThruAll` **!** | |
| * :ref:`selector_reference` **!** | |
| * :py:meth:`Workplane.vertices` **!** | |
| * :py:meth:`Workplane.box` | |
| * :py:meth:`Workplane` | |
| * :py:meth:`StringSyntaxSelector` **!** | |
| Offset Workplanes | |
| -------------------------- | |
| Workplanes do not have to lie exactly on a face. When you make a workplane, you can define it at an offset | |
| from an existing face. | |
| This example uses an offset workplane to make a compound object, which is perfectly valid! | |
| .. cadquery:: | |
| result = cq.Workplane("front").box(3, 2, 0.5) # make a basic prism | |
| result = result.faces("<X").workplane( | |
| offset=0.75 | |
| ) # workplane is offset from the object surface | |
| result = result.circle(1.0).extrude(0.5) # disc | |
| .. topic:: Api References | |
| .. hlist:: | |
| :columns: 2 | |
| * :py:meth:`Workplane.extrude` | |
| * :ref:`selector_reference` **!** | |
| * :py:meth:`Workplane.box` | |
| * :py:meth:`Workplane` | |
| Copying Workplanes | |
| -------------------------- | |
| An existing CQ object can copy a workplane from another CQ object. | |
| .. cadquery:: | |
| result = ( | |
| cq.Workplane("front") | |
| .circle(1) | |
| .extrude(10) # make a cylinder | |
| # We want to make a second cylinder perpendicular to the first, | |
| # but we have no face to base the workplane off | |
| .copyWorkplane( | |
| # create a temporary object with the required workplane | |
| cq.Workplane("right", origin=(-5, 0, 0)) | |
| ) | |
| .circle(1) | |
| .extrude(10) | |
| ) | |
| .. topic:: API References | |
| .. hlist:: | |
| :columns: 2 | |
| * :py:meth:`Workplane.copyWorkplane` **!** | |
| * :py:meth:`Workplane.circle` | |
| * :py:meth:`Workplane.extrude` | |
| * :py:meth:`Workplane` | |
| Rotated Workplanes | |
| -------------------------- | |
| You can create a rotated work plane by specifying angles of rotation relative to another workplane | |
| .. cadquery:: | |
| result = ( | |
| cq.Workplane("front") | |
| .box(4.0, 4.0, 0.25) | |
| .faces(">Z") | |
| .workplane() | |
| .transformed(offset=cq.Vector(0, -1.5, 1.0), rotate=cq.Vector(60, 0, 0)) | |
| .rect(1.5, 1.5, forConstruction=True) | |
| .vertices() | |
| .hole(0.25) | |
| ) | |
| .. topic:: Api References | |
| .. hlist:: | |
| :columns: 2 | |
| * :py:meth:`Workplane.transformed` **!** | |
| * :py:meth:`Workplane.box` | |
| * :py:meth:`Workplane.rect` | |
| * :py:meth:`Workplane.faces` | |
| Using construction Geometry | |
| --------------------------- | |
| You can draw shapes to use the vertices as points to locate other features. Features that are used to | |
| locate other features, rather than to create them, are called ``Construction Geometry`` | |
| In the example below, a rectangle is drawn, and its vertices are used to locate a set of holes. | |
| .. cadquery:: | |
| result = ( | |
| cq.Workplane("front") | |
| .box(2, 2, 0.5) | |
| .faces(">Z") | |
| .workplane() | |
| .rect(1.5, 1.5, forConstruction=True) | |
| .vertices() | |
| .hole(0.125) | |
| ) | |
| .. topic:: Api References | |
| .. hlist:: | |
| :columns: 2 | |
| * :py:meth:`Workplane.rect` (forConstruction=True) | |
| * :ref:`selector_reference` | |
| * :py:meth:`Workplane.workplane` | |
| * :py:meth:`Workplane.box` | |
| * :py:meth:`Workplane.hole` | |
| * :py:meth:`Workplane` | |
| Shelling To Create Thin features | |
| -------------------------------- | |
| Shelling converts a solid object into a shell of uniform thickness. | |
| To shell an object and 'hollow out' the inside pass a negative thickness parameter | |
| to the :py:meth:`Workplane.shell()` method of a shape. | |
| .. cadquery:: | |
| result = cq.Workplane("front").box(2, 2, 2).shell(-0.1) | |
| A positive thickness parameter wraps an object with filleted outside edges | |
| and the original object will be the 'hollowed out' portion. | |
| .. cadquery:: | |
| result = cq.Workplane("front").box(2, 2, 2).shell(0.1) | |
| Use face selectors to select a face to be removed from the resulting hollow shape. | |
| .. cadquery:: | |
| result = cq.Workplane("front").box(2, 2, 2).faces("+Z").shell(0.1) | |
| Multiple faces can be removed using more complex selectors. | |
| .. cadquery:: | |
| result = cq.Workplane("front").box(2, 2, 2).faces("+Z or -X or +X").shell(0.1) | |
| .. topic:: Api References | |
| .. hlist:: | |
| :columns: 2 | |
| * :py:meth:`Workplane.shell` **!** | |
| * :ref:`selector_reference` | |
| * :py:meth:`Workplane.box` | |
| * :py:meth:`Workplane.faces` | |
| * :py:meth:`Workplane` | |
| Making Lofts | |
| -------------------------------------------- | |
| A loft is a solid swept through a set of wires. This example creates lofted section between a rectangle | |
| and a circular section. | |
| .. cadquery:: | |
| result = ( | |
| cq.Workplane("front") | |
| .box(4.0, 4.0, 0.25) | |
| .faces(">Z") | |
| .circle(1.5) | |
| .workplane(offset=3.0) | |
| .rect(0.75, 0.5) | |
| .loft(combine=True) | |
| ) | |
| .. topic:: Api References | |
| .. hlist:: | |
| :columns: 2 | |
| * :py:meth:`Workplane.loft` **!** | |
| * :py:meth:`Workplane.box` | |
| * :py:meth:`Workplane.faces` | |
| * :py:meth:`Workplane.circle` | |
| * :py:meth:`Workplane.rect` | |
| Extruding until a given face | |
| -------------------------------------------- | |
| Sometimes you will want to extrude a wire until a given face that can be not planar or where you | |
| might not know easily the distance you have to extrude to. In such cases you can use `next`, `last` | |
| or even give a :class:`~cadquery.Face` object for the `until` argument of | |
| :meth:`~cadquery.Workplane.extrude`. | |
| .. cadquery:: | |
| result = ( | |
| cq.Workplane(origin=(20, 0, 0)) | |
| .circle(2) | |
| .revolve(180, (-20, 0, 0), (-20, -1, 0)) | |
| .center(-20, 0) | |
| .workplane() | |
| .rect(20, 4) | |
| .extrude("next") | |
| ) | |
| The same behaviour is available with :meth:`~cadquery.Workplane.cutBlind` and as you can see it is | |
| also possible to work on several :class:`~cadquery.Wire` objects at a time (the | |
| same is true for :meth:`~cadquery.Workplane.extrude`). | |
| .. cadquery:: | |
| skyscrapers_locations = [(-16, 1), (-8, 0), (7, 0.2), (17, -1.2)] | |
| angles = iter([15, 0, -8, 10]) | |
| skyscrapers = ( | |
| cq.Workplane() | |
| .pushPoints(skyscrapers_locations) | |
| .eachpoint( | |
| lambda loc: ( | |
| cq.Workplane() | |
| .rect(5, 16) | |
| .workplane(offset=10) | |
| .ellipse(3, 8) | |
| .workplane(offset=10) | |
| .slot2D(20, 5, 90) | |
| .loft() | |
| .rotateAboutCenter((0, 0, 1), next(angles)) | |
| .val() | |
| .located(loc) | |
| ) | |
| ) | |
| ) | |
| result = ( | |
| skyscrapers.transformed((0, -90, 0)) | |
| .moveTo(15, 0) | |
| .rect(3, 3, forConstruction=True) | |
| .vertices() | |
| .circle(1) | |
| .cutBlind("last") | |
| ) | |
| Here is a typical situation where extruding and cuting until a given surface is very handy. It allows us to extrude or cut until a curved surface without overlapping issues. | |
| .. cadquery:: | |
| import cadquery as cq | |
| sphere = cq.Workplane().sphere(5) | |
| base = cq.Workplane(origin=(0, 0, -2)).box(12, 12, 10).cut(sphere).edges("|Z").fillet(2) | |
| sphere_face = base.faces(">>X[2] and (not |Z) and (not |Y)").val() | |
| base = base.faces("<Z").workplane().circle(2).extrude(10) | |
| shaft = cq.Workplane().sphere(4.5).circle(1.5).extrude(20) | |
| spherical_joint = ( | |
| base.union(shaft) | |
| .faces(">X") | |
| .workplane(centerOption="CenterOfMass") | |
| .move(0, 4) | |
| .slot2D(10, 2, 90) | |
| .cutBlind(sphere_face) | |
| .workplane(offset=10) | |
| .move(0, 2) | |
| .circle(0.9) | |
| .extrude("next") | |
| ) | |
| result = spherical_joint | |
| .. warning:: | |
| If the wire you want to extrude cannot be fully projected on the target surface, the result will | |
| be unpredictable. Furthermore, the algorithm in charge of finding the candidate faces does its search by counting all the faces intersected | |
| by a line created from your wire center along your | |
| extrusion direction. So make sure your wire can be projected on your target face to avoid | |
| unexpected behaviour. | |
| .. topic:: Api References | |
| .. hlist:: | |
| :columns: 3 | |
| * :py:meth:`Workplane.cutBlind` **!** | |
| * :py:meth:`Workplane.rect` | |
| * :py:meth:`Workplane.ellipse` | |
| * :py:meth:`Workplane.workplane` | |
| * :py:meth:`Workplane.slot2D` | |
| * :py:meth:`Workplane.loft` | |
| * :py:meth:`Workplane.rotateAboutCenter` | |
| * :py:meth:`Workplane.transformed` | |
| * :py:meth:`Workplane.moveTo` | |
| * :py:meth:`Workplane.circle` | |
| Making Counter-bored and Counter-sunk Holes | |
| ---------------------------------------------- | |
| Counterbored and countersunk holes are so common that CadQuery creates macros to create them in a single step. | |
| Similar to :py:meth:`Workplane.hole`, these functions operate on a list of points as well as a single point. | |
| .. cadquery:: | |
| result = ( | |
| cq.Workplane(cq.Plane.XY()) | |
| .box(4, 2, 0.5) | |
| .faces(">Z") | |
| .workplane() | |
| .rect(3.5, 1.5, forConstruction=True) | |
| .vertices() | |
| .cboreHole(0.125, 0.25, 0.125, depth=None) | |
| ) | |
| .. topic:: Api References | |
| .. hlist:: | |
| :columns: 2 | |
| * :py:meth:`Workplane.cboreHole` **!** | |
| * :py:meth:`Workplane.cskHole` **!** | |
| * :py:meth:`Workplane.box` | |
| * :py:meth:`Workplane.rect` | |
| * :py:meth:`Workplane.workplane` | |
| * :py:meth:`Workplane.vertices` | |
| * :py:meth:`Workplane.faces` | |
| * :py:meth:`Workplane` | |
| Offsetting wires in 2D | |
| ---------------------- | |
| Two dimensional wires can be transformed with :py:meth:`Workplane.offset2D`. They can be offset | |
| inwards or outwards, and with different techniques for extending the corners. | |
| .. cadquery:: | |
| original = cq.Workplane().polygon(5, 10).extrude(0.1).translate((0, 0, 2)) | |
| arc = cq.Workplane().polygon(5, 10).offset2D(1, "arc").extrude(0.1).translate((0, 0, 1)) | |
| intersection = cq.Workplane().polygon(5, 10).offset2D(1, "intersection").extrude(0.1) | |
| result = original.add(arc).add(intersection) | |
| Using the forConstruction argument you can do the common task of offsetting a series of bolt holes | |
| from the outline of an object. Here is the counterbore example from above but with the bolt holes | |
| offset from the edges. | |
| .. cadquery:: | |
| result = ( | |
| cq.Workplane() | |
| .box(4, 2, 0.5) | |
| .faces(">Z") | |
| .edges() | |
| .toPending() | |
| .offset2D(-0.25, forConstruction=True) | |
| .vertices() | |
| .cboreHole(0.125, 0.25, 0.125, depth=None) | |
| ) | |
| Note that :py:meth:`Workplane.edges` is for selecting objects. It does not add the selected edges to | |
| pending edges in the modelling context, because this would result in your next extrusion including | |
| everything you had only selected in addition to the lines you had drawn. To specify you want these | |
| edges to be used in :py:meth:`Workplane.offset2D`, you call :py:meth:`Workplane.toPending` to | |
| explicitly put them in the list of pending edges. | |
| .. topic:: Api References | |
| .. hlist:: | |
| :columns: 2 | |
| * :py:meth:`Workplane.offset2D` **!** | |
| * :py:meth:`Workplane.cboreHole` | |
| * :py:meth:`Workplane.cskHole` | |
| * :py:meth:`Workplane.box` | |
| * :py:meth:`Workplane.polygon` | |
| * :py:meth:`Workplane.workplane` | |
| * :py:meth:`Workplane.vertices` | |
| * :py:meth:`Workplane.edges` | |
| * :py:meth:`Workplane.faces` | |
| * :py:meth:`Workplane` | |
| Rounding Corners with Fillet | |
| ----------------------------- | |
| Filleting is done by selecting the edges of a solid, and using the fillet function. | |
| Here we fillet all of the edges of a simple plate. | |
| .. cadquery:: | |
| result = cq.Workplane("XY").box(3, 3, 0.5).edges("|Z").fillet(0.125) | |
| .. topic:: Api References | |
| .. hlist:: | |
| :columns: 2 | |
| * :py:meth:`Workplane.fillet` **!** | |
| * :py:meth:`Workplane.box` | |
| * :py:meth:`Workplane.edges` | |
| * :py:meth:`Workplane` | |
| Tagging objects | |
| ---------------- | |
| The :py:meth:`Workplane.tag` method can be used to tag a particular object in the chain with a string, so that it can be referred to later in the chain. | |
| The :py:meth:`Workplane.workplaneFromTagged` method applies :py:meth:`Workplane.copyWorkplane` to a tagged object. For example, when extruding two different solids from a surface, after the first solid is extruded it can become difficult to reselect the original surface with CadQuery's other selectors. | |
| .. cadquery:: | |
| result = ( | |
| cq.Workplane("XY") | |
| # create and tag the base workplane | |
| .box(10, 10, 10) | |
| .faces(">Z") | |
| .workplane() | |
| .tag("baseplane") | |
| # extrude a cylinder | |
| .center(-3, 0) | |
| .circle(1) | |
| .extrude(3) | |
| # to reselect the base workplane, simply | |
| .workplaneFromTagged("baseplane") | |
| # extrude a second cylinder | |
| .center(3, 0) | |
| .circle(1) | |
| .extrude(2) | |
| ) | |
| Tags can also be used with most selectors, including :py:meth:`Workplane.vertices`, :py:meth:`Workplane.faces`, :py:meth:`Workplane.edges`, :py:meth:`Workplane.wires`, :py:meth:`Workplane.shells`, :py:meth:`Workplane.solids` and :py:meth:`Workplane.compounds`. | |
| .. cadquery:: | |
| result = ( | |
| cq.Workplane("XY") | |
| # create a triangular prism and tag it | |
| .polygon(3, 5) | |
| .extrude(4) | |
| .tag("prism") | |
| # create a sphere that obscures the prism | |
| .sphere(10) | |
| # create features based on the prism's faces | |
| .faces("<X", tag="prism") | |
| .workplane() | |
| .circle(1) | |
| .cutThruAll() | |
| .faces(">X", tag="prism") | |
| .faces(">Y") | |
| .workplane() | |
| .circle(1) | |
| .cutThruAll() | |
| ) | |
| .. topic:: Api References | |
| .. hlist:: | |
| :columns: 2 | |
| * :py:meth:`Workplane.tag` **!** | |
| * :py:meth:`Workplane.getTagged` **!** | |
| * :py:meth:`Workplane.workplaneFromTagged` **!** | |
| * :py:meth:`Workplane.extrude` | |
| * :py:meth:`Workplane.cutThruAll` | |
| * :py:meth:`Workplane.circle` | |
| * :py:meth:`Workplane.faces` | |
| * :py:meth:`Workplane` | |
| A Parametric Bearing Pillow Block | |
| ------------------------------------ | |
| Combining a few basic functions, its possible to make a very good parametric bearing pillow block, | |
| with just a few lines of code. | |
| .. cadquery:: | |
| (length, height, bearing_diam, thickness, padding) = (30.0, 40.0, 22.0, 10.0, 8.0) | |
| result = ( | |
| cq.Workplane("XY") | |
| .box(length, height, thickness) | |
| .faces(">Z") | |
| .workplane() | |
| .hole(bearing_diam) | |
| .faces(">Z") | |
| .workplane() | |
| .rect(length - padding, height - padding, forConstruction=True) | |
| .vertices() | |
| .cboreHole(2.4, 4.4, 2.1) | |
| ) | |
| Splitting an Object | |
| --------------------- | |
| You can split an object using a workplane, and retain either or both halves | |
| .. cadquery:: | |
| c = cq.Workplane("XY").box(1, 1, 1).faces(">Z").workplane().circle(0.25).cutThruAll() | |
| # now cut it in half sideways | |
| result = c.faces(">Y").workplane(-0.5).split(keepTop=True) | |
| .. topic:: Api References | |
| .. hlist:: | |
| :columns: 2 | |
| * :py:meth:`Workplane.split` **!** | |
| * :py:meth:`Workplane.box` | |
| * :py:meth:`Workplane.circle` | |
| * :py:meth:`Workplane.cutThruAll` | |
| * :py:meth:`Workplane.workplane` | |
| * :py:meth:`Workplane` | |
| The Classic OCC Bottle | |
| ---------------------- | |
| CadQuery is based on the OpenCascade.org (OCC) modeling Kernel. Those who are familiar with OCC know about the | |
| famous 'bottle' example. `The bottle example in the OCCT online documentation <https://old.opencascade.com/doc/occt-7.5.0/overview/html/occt__tutorial.html>`_. | |
| A pythonOCC version is listed `here <https://github.com/tpaviot/pythonocc-demos/blob/f3ea9b4f65a9dff482be04b153d4ce5ec2430e13/examples/core_classic_occ_bottle.py>`_. | |
| Of course one difference between this sample and the OCC version is the length. This sample is one of the longer | |
| ones at 13 lines, but that's very short compared to the pythonOCC version, which is 10x longer! | |
| .. cadquery:: | |
| (L, w, t) = (20.0, 6.0, 3.0) | |
| s = cq.Workplane("XY") | |
| # Draw half the profile of the bottle and extrude it | |
| p = ( | |
| s.center(-L / 2.0, 0) | |
| .vLine(w / 2.0) | |
| .threePointArc((L / 2.0, w / 2.0 + t), (L, w / 2.0)) | |
| .vLine(-w / 2.0) | |
| .mirrorX() | |
| .extrude(30.0, True) | |
| ) | |
| # Make the neck | |
| p = p.faces(">Z").workplane(centerOption="CenterOfMass").circle(3.0).extrude(2.0, True) | |
| # Make a shell | |
| result = p.faces(">Z").shell(0.3) | |
| .. topic:: Api References | |
| .. hlist:: | |
| :columns: 2 | |
| * :py:meth:`Workplane.extrude` | |
| * :py:meth:`Workplane.mirrorX` | |
| * :py:meth:`Workplane.threePointArc` | |
| * :py:meth:`Workplane.workplane` | |
| * :py:meth:`Workplane.vertices` | |
| * :py:meth:`Workplane.vLine` | |
| * :py:meth:`Workplane.faces` | |
| * :py:meth:`Workplane` | |
| A Parametric Enclosure | |
| ----------------------- | |
| .. cadquery:: | |
| :height: 400px | |
| # parameter definitions | |
| p_outerWidth = 100.0 # Outer width of box enclosure | |
| p_outerLength = 150.0 # Outer length of box enclosure | |
| p_outerHeight = 50.0 # Outer height of box enclosure | |
| p_thickness = 3.0 # Thickness of the box walls | |
| p_sideRadius = 10.0 # Radius for the curves around the sides of the box | |
| p_topAndBottomRadius = ( | |
| 2.0 # Radius for the curves on the top and bottom edges of the box | |
| ) | |
| p_screwpostInset = 12.0 # How far in from the edges the screw posts should be place. | |
| p_screwpostID = 4.0 # Inner Diameter of the screw post holes, should be roughly screw diameter not including threads | |
| p_screwpostOD = 10.0 # Outer Diameter of the screw posts.\nDetermines overall thickness of the posts | |
| p_boreDiameter = 8.0 # Diameter of the counterbore hole, if any | |
| p_boreDepth = 1.0 # Depth of the counterbore hole, if | |
| p_countersinkDiameter = 0.0 # Outer diameter of countersink. Should roughly match the outer diameter of the screw head | |
| p_countersinkAngle = 90.0 # Countersink angle (complete angle between opposite sides, not from center to one side) | |
| p_flipLid = True # Whether to place the lid with the top facing down or not. | |
| p_lipHeight = 1.0 # Height of lip on the underside of the lid.\nSits inside the box body for a snug fit. | |
| # outer shell | |
| oshell = ( | |
| cq.Workplane("XY") | |
| .rect(p_outerWidth, p_outerLength) | |
| .extrude(p_outerHeight + p_lipHeight) | |
| ) | |
| # weird geometry happens if we make the fillets in the wrong order | |
| if p_sideRadius > p_topAndBottomRadius: | |
| oshell = oshell.edges("|Z").fillet(p_sideRadius) | |
| oshell = oshell.edges("#Z").fillet(p_topAndBottomRadius) | |
| else: | |
| oshell = oshell.edges("#Z").fillet(p_topAndBottomRadius) | |
| oshell = oshell.edges("|Z").fillet(p_sideRadius) | |
| # inner shell | |
| ishell = ( | |
| oshell.faces("<Z") | |
| .workplane(p_thickness, True) | |
| .rect((p_outerWidth - 2.0 * p_thickness), (p_outerLength - 2.0 * p_thickness)) | |
| .extrude( | |
| (p_outerHeight - 2.0 * p_thickness), False | |
| ) # set combine false to produce just the new boss | |
| ) | |
| ishell = ishell.edges("|Z").fillet(p_sideRadius - p_thickness) | |
| # make the box outer box | |
| box = oshell.cut(ishell) | |
| # make the screw posts | |
| POSTWIDTH = p_outerWidth - 2.0 * p_screwpostInset | |
| POSTLENGTH = p_outerLength - 2.0 * p_screwpostInset | |
| box = ( | |
| box.faces(">Z") | |
| .workplane(-p_thickness) | |
| .rect(POSTWIDTH, POSTLENGTH, forConstruction=True) | |
| .vertices() | |
| .circle(p_screwpostOD / 2.0) | |
| .circle(p_screwpostID / 2.0) | |
| .extrude(-1.0 * (p_outerHeight + p_lipHeight - p_thickness), True) | |
| ) | |
| # split lid into top and bottom parts | |
| (lid, bottom) = ( | |
| box.faces(">Z") | |
| .workplane(-p_thickness - p_lipHeight) | |
| .split(keepTop=True, keepBottom=True) | |
| .all() | |
| ) # splits into two solids | |
| # translate the lid, and subtract the bottom from it to produce the lid inset | |
| lowerLid = lid.translate((0, 0, -p_lipHeight)) | |
| cutlip = lowerLid.cut(bottom).translate( | |
| (p_outerWidth + p_thickness, 0, p_thickness - p_outerHeight + p_lipHeight) | |
| ) | |
| # compute centers for screw holes | |
| topOfLidCenters = ( | |
| cutlip.faces(">Z") | |
| .workplane(centerOption="CenterOfMass") | |
| .rect(POSTWIDTH, POSTLENGTH, forConstruction=True) | |
| .vertices() | |
| ) | |
| # add holes of the desired type | |
| if p_boreDiameter > 0 and p_boreDepth > 0: | |
| topOfLid = topOfLidCenters.cboreHole( | |
| p_screwpostID, p_boreDiameter, p_boreDepth, 2.0 * p_thickness | |
| ) | |
| elif p_countersinkDiameter > 0 and p_countersinkAngle > 0: | |
| topOfLid = topOfLidCenters.cskHole( | |
| p_screwpostID, p_countersinkDiameter, p_countersinkAngle, 2.0 * p_thickness | |
| ) | |
| else: | |
| topOfLid = topOfLidCenters.hole(p_screwpostID, 2.0 * p_thickness) | |
| # flip lid upside down if desired | |
| if p_flipLid: | |
| topOfLid = topOfLid.rotateAboutCenter((1, 0, 0), 180) | |
| # return the combined result | |
| result = topOfLid.union(bottom) | |
| .. topic:: Api References | |
| .. hlist:: | |
| :columns: 3 | |
| * :py:meth:`Workplane.circle` | |
| * :py:meth:`Workplane.rect` | |
| * :py:meth:`Workplane.extrude` | |
| * :py:meth:`Workplane.box` | |
| * :py:meth:`Workplane.all` | |
| * :py:meth:`Workplane.faces` | |
| * :py:meth:`Workplane.vertices` | |
| * :py:meth:`Workplane.edges` | |
| * :py:meth:`Workplane.workplane` | |
| * :py:meth:`Workplane.fillet` | |
| * :py:meth:`Workplane.cut` | |
| * :py:meth:`Workplane.union` | |
| * :py:meth:`Workplane.rotateAboutCenter` | |
| * :py:meth:`Workplane.cboreHole` | |
| * :py:meth:`Workplane.cskHole` | |
| * :py:meth:`Workplane.hole` | |
| Lego Brick | |
| ------------------- | |
| This script will produce any size regular rectangular Lego(TM) brick. Its only tricky because of the logic | |
| regarding the underside of the brick. | |
| .. cadquery:: | |
| :select: tmp | |
| :height: 400px | |
| ##### | |
| # Inputs | |
| ###### | |
| lbumps = 6 # number of bumps long | |
| wbumps = 2 # number of bumps wide | |
| thin = True # True for thin, False for thick | |
| # | |
| # Lego Brick Constants-- these make a Lego brick a Lego :) | |
| # | |
| pitch = 8.0 | |
| clearance = 0.1 | |
| bumpDiam = 4.8 | |
| bumpHeight = 1.8 | |
| if thin: | |
| height = 3.2 | |
| else: | |
| height = 9.6 | |
| t = (pitch - (2 * clearance) - bumpDiam) / 2.0 | |
| postDiam = pitch - t # works out to 6.5 | |
| total_length = lbumps * pitch - 2.0 * clearance | |
| total_width = wbumps * pitch - 2.0 * clearance | |
| # make the base | |
| s = cq.Workplane("XY").box(total_length, total_width, height) | |
| # shell inwards not outwards | |
| s = s.faces("<Z").shell(-1.0 * t) | |
| # make the bumps on the top | |
| s = ( | |
| s.faces(">Z") | |
| .workplane() | |
| .rarray(pitch, pitch, lbumps, wbumps, True) | |
| .circle(bumpDiam / 2.0) | |
| .extrude(bumpHeight) | |
| ) | |
| # add posts on the bottom. posts are different diameter depending on geometry | |
| # solid studs for 1 bump, tubes for multiple, none for 1x1 | |
| tmp = s.faces("<Z").workplane(invert=True) | |
| if lbumps > 1 and wbumps > 1: | |
| tmp = ( | |
| tmp.rarray(pitch, pitch, lbumps - 1, wbumps - 1, center=True) | |
| .circle(postDiam / 2.0) | |
| .circle(bumpDiam / 2.0) | |
| .extrude(height - t) | |
| ) | |
| elif lbumps > 1: | |
| tmp = ( | |
| tmp.rarray(pitch, pitch, lbumps - 1, 1, center=True) | |
| .circle(t) | |
| .extrude(height - t) | |
| ) | |
| elif wbumps > 1: | |
| tmp = ( | |
| tmp.rarray(pitch, pitch, 1, wbumps - 1, center=True) | |
| .circle(t) | |
| .extrude(height - t) | |
| ) | |
| else: | |
| tmp = s | |
| Braille Example | |
| --------------------- | |
| .. cadquery:: | |
| :height: 400px | |
| from collections import namedtuple | |
| # text_lines is a list of text lines. | |
| # Braille (converted with braille-converter: | |
| # https://github.com/jpaugh/braille-converter.git). | |
| text_lines = ["⠠ ⠋ ⠗ ⠑ ⠑ ⠠ ⠉ ⠠ ⠁ ⠠ ⠙"] | |
| # See http://www.tiresias.org/research/reports/braille_cell.htm for examples | |
| # of braille cell geometry. | |
| horizontal_interdot = 2.5 | |
| vertical_interdot = 2.5 | |
| horizontal_intercell = 6 | |
| vertical_interline = 10 | |
| dot_height = 0.5 | |
| dot_diameter = 1.3 | |
| base_thickness = 1.5 | |
| # End of configuration. | |
| BrailleCellGeometry = namedtuple( | |
| "BrailleCellGeometry", | |
| ( | |
| "horizontal_interdot", | |
| "vertical_interdot", | |
| "intercell", | |
| "interline", | |
| "dot_height", | |
| "dot_diameter", | |
| ), | |
| ) | |
| class Point(object): | |
| def __init__(self, x, y): | |
| self.x = x | |
| self.y = y | |
| def __add__(self, other): | |
| return Point(self.x + other.x, self.y + other.y) | |
| def __len__(self): | |
| return 2 | |
| def __getitem__(self, index): | |
| return (self.x, self.y)[index] | |
| def __str__(self): | |
| return "({}, {})".format(self.x, self.y) | |
| def brailleToPoints(text, cell_geometry): | |
| # Unicode bit pattern (cf. https://en.wikipedia.org/wiki/Braille_Patterns). | |
| mask1 = 0b00000001 | |
| mask2 = 0b00000010 | |
| mask3 = 0b00000100 | |
| mask4 = 0b00001000 | |
| mask5 = 0b00010000 | |
| mask6 = 0b00100000 | |
| mask7 = 0b01000000 | |
| mask8 = 0b10000000 | |
| masks = (mask1, mask2, mask3, mask4, mask5, mask6, mask7, mask8) | |
| # Corresponding dot position | |
| w = cell_geometry.horizontal_interdot | |
| h = cell_geometry.vertical_interdot | |
| pos1 = Point(0, 2 * h) | |
| pos2 = Point(0, h) | |
| pos3 = Point(0, 0) | |
| pos4 = Point(w, 2 * h) | |
| pos5 = Point(w, h) | |
| pos6 = Point(w, 0) | |
| pos7 = Point(0, -h) | |
| pos8 = Point(w, -h) | |
| pos = (pos1, pos2, pos3, pos4, pos5, pos6, pos7, pos8) | |
| # Braille blank pattern (u'\u2800'). | |
| blank = "⠀" | |
| points = [] | |
| # Position of dot1 along the x-axis (horizontal). | |
| character_origin = 0 | |
| for c in text: | |
| for m, p in zip(masks, pos): | |
| delta_to_blank = ord(c) - ord(blank) | |
| if m & delta_to_blank: | |
| points.append(p + Point(character_origin, 0)) | |
| character_origin += cell_geometry.intercell | |
| return points | |
| def get_plate_height(text_lines, cell_geometry): | |
| # cell_geometry.vertical_interdot is also used as space between base | |
| # borders and characters. | |
| return ( | |
| 2 * cell_geometry.vertical_interdot | |
| + 2 * cell_geometry.vertical_interdot | |
| + (len(text_lines) - 1) * cell_geometry.interline | |
| ) | |
| def get_plate_width(text_lines, cell_geometry): | |
| # cell_geometry.horizontal_interdot is also used as space between base | |
| # borders and characters. | |
| max_len = max([len(t) for t in text_lines]) | |
| return ( | |
| 2 * cell_geometry.horizontal_interdot | |
| + cell_geometry.horizontal_interdot | |
| + (max_len - 1) * cell_geometry.intercell | |
| ) | |
| def get_cylinder_radius(cell_geometry): | |
| """Return the radius the cylinder should have | |
| The cylinder have the same radius as the half-sphere make the dots (the | |
| hidden and the shown part of the dots). | |
| The radius is such that the spherical cap with diameter | |
| cell_geometry.dot_diameter has a height of cell_geometry.dot_height. | |
| """ | |
| h = cell_geometry.dot_height | |
| r = cell_geometry.dot_diameter / 2 | |
| return (r**2 + h**2) / 2 / h | |
| def get_base_plate_thickness(plate_thickness, cell_geometry): | |
| """Return the height on which the half spheres will sit""" | |
| return ( | |
| plate_thickness + get_cylinder_radius(cell_geometry) - cell_geometry.dot_height | |
| ) | |
| def make_base(text_lines, cell_geometry, plate_thickness): | |
| base_width = get_plate_width(text_lines, cell_geometry) | |
| base_height = get_plate_height(text_lines, cell_geometry) | |
| base_thickness = get_base_plate_thickness(plate_thickness, cell_geometry) | |
| base = cq.Workplane("XY").box( | |
| base_width, base_height, base_thickness, centered=False | |
| ) | |
| return base | |
| def make_embossed_plate(text_lines, cell_geometry): | |
| """Make an embossed plate with dots as spherical caps | |
| Method: | |
| - make a thin plate on which sit cylinders | |
| - fillet the upper edge of the cylinders so to get pseudo half-spheres | |
| - make the union with a thicker plate so that only the sphere caps stay | |
| "visible". | |
| """ | |
| base = make_base(text_lines, cell_geometry, base_thickness) | |
| dot_pos = [] | |
| base_width = get_plate_width(text_lines, cell_geometry) | |
| base_height = get_plate_height(text_lines, cell_geometry) | |
| y = base_height - 3 * cell_geometry.vertical_interdot | |
| line_start_pos = Point(cell_geometry.horizontal_interdot, y) | |
| for text in text_lines: | |
| dots = brailleToPoints(text, cell_geometry) | |
| dots = [p + line_start_pos for p in dots] | |
| dot_pos += dots | |
| line_start_pos += Point(0, -cell_geometry.interline) | |
| r = get_cylinder_radius(cell_geometry) | |
| base = ( | |
| base.faces(">Z") | |
| .vertices("<XY") | |
| .workplane() | |
| .pushPoints(dot_pos) | |
| .circle(r) | |
| .extrude(r) | |
| ) | |
| # Make a fillet almost the same radius to get a pseudo spherical cap. | |
| base = base.faces(">Z").edges().fillet(r - 0.001) | |
| hidding_box = cq.Workplane("XY").box( | |
| base_width, base_height, base_thickness, centered=False | |
| ) | |
| result = hidding_box.union(base) | |
| return result | |
| _cell_geometry = BrailleCellGeometry( | |
| horizontal_interdot, | |
| vertical_interdot, | |
| horizontal_intercell, | |
| vertical_interline, | |
| dot_height, | |
| dot_diameter, | |
| ) | |
| if base_thickness < get_cylinder_radius(_cell_geometry): | |
| raise ValueError("Base thickness should be at least {}".format(dot_height)) | |
| result = make_embossed_plate(text_lines, _cell_geometry) | |
| Panel With Various Connector Holes | |
| ----------------------------------- | |
| .. cadquery:: | |
| :height: 400px | |
| # The dimensions of the model. These can be modified rather than changing the | |
| # object's code directly. | |
| width = 400 | |
| height = 500 | |
| thickness = 2 | |
| # Create a plate with two polygons cut through it | |
| result = cq.Workplane("front").box(width, height, thickness) | |
| h_sep = 60 | |
| for idx in range(4): | |
| result = ( | |
| result.workplane(offset=1, centerOption="CenterOfBoundBox") | |
| .center(157, 210 - idx * h_sep) | |
| .moveTo(-23.5, 0) | |
| .circle(1.6) | |
| .moveTo(23.5, 0) | |
| .circle(1.6) | |
| .moveTo(-17.038896, -5.7) | |
| .threePointArc((-19.44306, -4.70416), (-20.438896, -2.3)) | |
| .lineTo(-21.25, 2.3) | |
| .threePointArc((-20.25416, 4.70416), (-17.85, 5.7)) | |
| .lineTo(17.85, 5.7) | |
| .threePointArc((20.25416, 4.70416), (21.25, 2.3)) | |
| .lineTo(20.438896, -2.3) | |
| .threePointArc((19.44306, -4.70416), (17.038896, -5.7)) | |
| .close() | |
| .cutThruAll() | |
| ) | |
| for idx in range(4): | |
| result = ( | |
| result.workplane(offset=1, centerOption="CenterOfBoundBox") | |
| .center(157, -30 - idx * h_sep) | |
| .moveTo(-16.65, 0) | |
| .circle(1.6) | |
| .moveTo(16.65, 0) | |
| .circle(1.6) | |
| .moveTo(-10.1889, -5.7) | |
| .threePointArc((-12.59306, -4.70416), (-13.5889, -2.3)) | |
| .lineTo(-14.4, 2.3) | |
| .threePointArc((-13.40416, 4.70416), (-11, 5.7)) | |
| .lineTo(11, 5.7) | |
| .threePointArc((13.40416, 4.70416), (14.4, 2.3)) | |
| .lineTo(13.5889, -2.3) | |
| .threePointArc((12.59306, -4.70416), (10.1889, -5.7)) | |
| .close() | |
| .cutThruAll() | |
| ) | |
| h_sep4DB9 = 30 | |
| for idx in range(8): | |
| result = ( | |
| result.workplane(offset=1, centerOption="CenterOfBoundBox") | |
| .center(91, 225 - idx * h_sep4DB9) | |
| .moveTo(-12.5, 0) | |
| .circle(1.6) | |
| .moveTo(12.5, 0) | |
| .circle(1.6) | |
| .moveTo(-6.038896, -5.7) | |
| .threePointArc((-8.44306, -4.70416), (-9.438896, -2.3)) | |
| .lineTo(-10.25, 2.3) | |
| .threePointArc((-9.25416, 4.70416), (-6.85, 5.7)) | |
| .lineTo(6.85, 5.7) | |
| .threePointArc((9.25416, 4.70416), (10.25, 2.3)) | |
| .lineTo(9.438896, -2.3) | |
| .threePointArc((8.44306, -4.70416), (6.038896, -5.7)) | |
| .close() | |
| .cutThruAll() | |
| ) | |
| for idx in range(4): | |
| result = ( | |
| result.workplane(offset=1, centerOption="CenterOfBoundBox") | |
| .center(25, 210 - idx * h_sep) | |
| .moveTo(-23.5, 0) | |
| .circle(1.6) | |
| .moveTo(23.5, 0) | |
| .circle(1.6) | |
| .moveTo(-17.038896, -5.7) | |
| .threePointArc((-19.44306, -4.70416), (-20.438896, -2.3)) | |
| .lineTo(-21.25, 2.3) | |
| .threePointArc((-20.25416, 4.70416), (-17.85, 5.7)) | |
| .lineTo(17.85, 5.7) | |
| .threePointArc((20.25416, 4.70416), (21.25, 2.3)) | |
| .lineTo(20.438896, -2.3) | |
| .threePointArc((19.44306, -4.70416), (17.038896, -5.7)) | |
| .close() | |
| .cutThruAll() | |
| ) | |
| for idx in range(4): | |
| result = ( | |
| result.workplane(offset=1, centerOption="CenterOfBoundBox") | |
| .center(25, -30 - idx * h_sep) | |
| .moveTo(-16.65, 0) | |
| .circle(1.6) | |
| .moveTo(16.65, 0) | |
| .circle(1.6) | |
| .moveTo(-10.1889, -5.7) | |
| .threePointArc((-12.59306, -4.70416), (-13.5889, -2.3)) | |
| .lineTo(-14.4, 2.3) | |
| .threePointArc((-13.40416, 4.70416), (-11, 5.7)) | |
| .lineTo(11, 5.7) | |
| .threePointArc((13.40416, 4.70416), (14.4, 2.3)) | |
| .lineTo(13.5889, -2.3) | |
| .threePointArc((12.59306, -4.70416), (10.1889, -5.7)) | |
| .close() | |
| .cutThruAll() | |
| ) | |
| for idx in range(8): | |
| result = ( | |
| result.workplane(offset=1, centerOption="CenterOfBoundBox") | |
| .center(-41, 225 - idx * h_sep4DB9) | |
| .moveTo(-12.5, 0) | |
| .circle(1.6) | |
| .moveTo(12.5, 0) | |
| .circle(1.6) | |
| .moveTo(-6.038896, -5.7) | |
| .threePointArc((-8.44306, -4.70416), (-9.438896, -2.3)) | |
| .lineTo(-10.25, 2.3) | |
| .threePointArc((-9.25416, 4.70416), (-6.85, 5.7)) | |
| .lineTo(6.85, 5.7) | |
| .threePointArc((9.25416, 4.70416), (10.25, 2.3)) | |
| .lineTo(9.438896, -2.3) | |
| .threePointArc((8.44306, -4.70416), (6.038896, -5.7)) | |
| .close() | |
| .cutThruAll() | |
| ) | |
| for idx in range(4): | |
| result = ( | |
| result.workplane(offset=1, centerOption="CenterOfBoundBox") | |
| .center(-107, 210 - idx * h_sep) | |
| .moveTo(-23.5, 0) | |
| .circle(1.6) | |
| .moveTo(23.5, 0) | |
| .circle(1.6) | |
| .moveTo(-17.038896, -5.7) | |
| .threePointArc((-19.44306, -4.70416), (-20.438896, -2.3)) | |
| .lineTo(-21.25, 2.3) | |
| .threePointArc((-20.25416, 4.70416), (-17.85, 5.7)) | |
| .lineTo(17.85, 5.7) | |
| .threePointArc((20.25416, 4.70416), (21.25, 2.3)) | |
| .lineTo(20.438896, -2.3) | |
| .threePointArc((19.44306, -4.70416), (17.038896, -5.7)) | |
| .close() | |
| .cutThruAll() | |
| ) | |
| for idx in range(4): | |
| result = ( | |
| result.workplane(offset=1, centerOption="CenterOfBoundBox") | |
| .center(-107, -30 - idx * h_sep) | |
| .circle(14) | |
| .rect(24.7487, 24.7487, forConstruction=True) | |
| .vertices() | |
| .hole(3.2) | |
| .cutThruAll() | |
| ) | |
| for idx in range(8): | |
| result = ( | |
| result.workplane(offset=1, centerOption="CenterOfBoundBox") | |
| .center(-173, 225 - idx * h_sep4DB9) | |
| .moveTo(-12.5, 0) | |
| .circle(1.6) | |
| .moveTo(12.5, 0) | |
| .circle(1.6) | |
| .moveTo(-6.038896, -5.7) | |
| .threePointArc((-8.44306, -4.70416), (-9.438896, -2.3)) | |
| .lineTo(-10.25, 2.3) | |
| .threePointArc((-9.25416, 4.70416), (-6.85, 5.7)) | |
| .lineTo(6.85, 5.7) | |
| .threePointArc((9.25416, 4.70416), (10.25, 2.3)) | |
| .lineTo(9.438896, -2.3) | |
| .threePointArc((8.44306, -4.70416), (6.038896, -5.7)) | |
| .close() | |
| .cutThruAll() | |
| ) | |
| for idx in range(4): | |
| result = ( | |
| result.workplane(offset=1, centerOption="CenterOfBoundBox") | |
| .center(-173, -30 - idx * h_sep) | |
| .moveTo(-2.9176, -5.3) | |
| .threePointArc((-6.05, 0), (-2.9176, 5.3)) | |
| .lineTo(2.9176, 5.3) | |
| .threePointArc((6.05, 0), (2.9176, -5.3)) | |
| .close() | |
| .cutThruAll() | |
| ) | |
| Cycloidal gear | |
| -------------- | |
| You can define complex geometries using the parametricCurve functionality. | |
| This specific examples generates a helical cycloidal gear. | |
| .. cadquery:: | |
| :height: 400px | |
| import cadquery as cq | |
| from math import sin, cos, pi, floor | |
| # define the generating function | |
| def hypocycloid(t, r1, r2): | |
| return ( | |
| (r1 - r2) * cos(t) + r2 * cos(r1 / r2 * t - t), | |
| (r1 - r2) * sin(t) + r2 * sin(-(r1 / r2 * t - t)), | |
| ) | |
| def epicycloid(t, r1, r2): | |
| return ( | |
| (r1 + r2) * cos(t) - r2 * cos(r1 / r2 * t + t), | |
| (r1 + r2) * sin(t) - r2 * sin(r1 / r2 * t + t), | |
| ) | |
| def gear(t, r1=4, r2=1): | |
| if (-1) ** (1 + floor(t / 2 / pi * (r1 / r2))) < 0: | |
| return epicycloid(t, r1, r2) | |
| else: | |
| return hypocycloid(t, r1, r2) | |
| # create the gear profile and extrude it | |
| result = ( | |
| cq.Workplane("XY") | |
| .parametricCurve(lambda t: gear(t * 2 * pi, 6, 1)) | |
| .twistExtrude(15, 90) | |
| .faces(">Z") | |
| .workplane() | |
| .circle(2) | |
| .cutThruAll() | |
| ) | |