| 
									
										
										
										
											2014-10-25 12:57:46 -04:00
										 |  |  | .. _extending:
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Extending CadQuery
 | 
					
						
							| 
									
										
										
										
											2015-11-28 21:05:20 -05:00
										 |  |  | ======================
 | 
					
						
							| 
									
										
										
										
											2014-10-25 12:57:46 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-30 06:32:37 -05:00
										 |  |  | If you find that CadQuery does not suit your needs, you can easily extend it.  CadQuery provides several extension
 | 
					
						
							| 
									
										
										
										
											2014-10-25 12:57:46 -04:00
										 |  |  | methods:
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |    * You can load plugins others have developed. This is by far the easiest way to access other code
 | 
					
						
							| 
									
										
										
										
											2019-01-17 13:25:51 -05:00
										 |  |  |    * You can define your own plugins.
 | 
					
						
							| 
									
										
										
										
											2020-09-30 18:18:40 +02:00
										 |  |  |    * You can use OCP scripting directly
 | 
					
						
							| 
									
										
										
										
											2014-10-25 12:57:46 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-30 18:18:40 +02:00
										 |  |  | Using OpenCascade methods
 | 
					
						
							|  |  |  | -------------------------
 | 
					
						
							| 
									
										
										
										
											2014-10-25 12:57:46 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-30 18:18:40 +02:00
										 |  |  | The easiest way to extend CadQuery is to simply use OpenCascade/OCP scripting inside of your build method.  Just about
 | 
					
						
							|  |  |  | any valid OCP script will execute just fine. For example, this simple CadQuery script::
 | 
					
						
							| 
									
										
										
										
											2014-10-25 12:57:46 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-22 13:55:00 +12:00
										 |  |  |     return cq.Workplane("XY").box(1.0, 2.0, 3.0).val()
 | 
					
						
							| 
									
										
										
										
											2014-10-25 12:57:46 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | is actually equivalent to::
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-22 13:55:00 +12:00
										 |  |  |     return cq.Shape.cast(
 | 
					
						
							|  |  |  |         BRepPrimAPI_MakeBox(
 | 
					
						
							|  |  |  |             gp_Ax2(Vector(-0.1, -1.0, -1.5), Vector(0, 0, 1)), 1.0, 2.0, 3.0
 | 
					
						
							|  |  |  |         ).Shape()
 | 
					
						
							|  |  |  |     )
 | 
					
						
							| 
									
										
										
										
											2014-10-25 12:57:46 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-30 18:18:40 +02:00
										 |  |  | As long as you return a valid OCP Shape, you can use any OCP methods you like. You can even mix and match the
 | 
					
						
							|  |  |  | two. For example, consider this script, which creates a OCP box, but then uses CadQuery to select its faces::
 | 
					
						
							| 
									
										
										
										
											2014-10-25 12:57:46 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-22 13:55:00 +12:00
										 |  |  |     box = cq.Shape.cast(
 | 
					
						
							|  |  |  |         BRepPrimAPI_MakeBox(
 | 
					
						
							|  |  |  |             gp_Ax2(Vector(-0.1, -1.0, -1.5), Vector(0, 0, 1)), 1.0, 2.0, 3.0
 | 
					
						
							|  |  |  |         ).Shape()
 | 
					
						
							|  |  |  |     )
 | 
					
						
							|  |  |  |     cq = Workplane(box).faces(">Z").size()  # returns 6
 | 
					
						
							| 
									
										
										
										
											2014-10-25 12:57:46 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Extending CadQuery: Plugins
 | 
					
						
							|  |  |  | ----------------------------
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-30 18:18:40 +02:00
										 |  |  | Though you can get a lot done with OpenCascade, the code gets pretty nasty in a hurry. CadQuery shields you from
 | 
					
						
							|  |  |  | a lot of the complexity of the OpenCascade API.
 | 
					
						
							| 
									
										
										
										
											2014-10-25 12:57:46 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-30 18:18:40 +02:00
										 |  |  | You can get the best of both worlds by wrapping your OCP script into a CadQuery plugin.
 | 
					
						
							| 
									
										
										
										
											2014-10-25 12:57:46 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-09 21:01:14 -05:00
										 |  |  | A CadQuery plugin is simply a function that is attached to the CadQuery :py:meth:`cadquery.CQ` or :py:meth:`cadquery.Workplane` class.
 | 
					
						
							| 
									
										
										
										
											2014-10-25 12:57:46 -04:00
										 |  |  | When connected, your plugin can be used in the chain just like the built-in functions.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | There are a few key concepts important to understand when building a plugin
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The Stack
 | 
					
						
							| 
									
										
										
										
											2015-11-28 21:05:20 -05:00
										 |  |  | -------------------
 | 
					
						
							| 
									
										
										
										
											2014-10-25 12:57:46 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | Every CadQuery object has a local stack, which contains a list of items.  The items on the stack will be
 | 
					
						
							|  |  |  | one of these types:
 | 
					
						
							| 
									
										
										
										
											2015-11-28 21:05:20 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-30 18:18:40 +02:00
										 |  |  |    * **A CadQuery SolidReference object**, which holds a reference to a OCP solid
 | 
					
						
							|  |  |  |    * **A OCP object**, a Vertex, Edge, Wire, Face, Shell, Solid, or Compound
 | 
					
						
							| 
									
										
										
										
											2014-10-25 12:57:46 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | The stack is available by using self.objects, and will always contain at least one object.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | .. note::
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Objects and points on the stack are **always** in global coordinates.  Similarly, any objects you
 | 
					
						
							|  |  |  |     create must be created in terms of global coordinates as well!
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Preserving the Chain
 | 
					
						
							| 
									
										
										
										
											2015-11-28 21:05:20 -05:00
										 |  |  | -----------------------
 | 
					
						
							| 
									
										
										
										
											2014-10-25 12:57:46 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-17 13:25:51 -05:00
										 |  |  | CadQuery's fluent API relies on the ability to chain calls together one after another. For this to work,
 | 
					
						
							| 
									
										
										
										
											2014-10-25 12:57:46 -04:00
										 |  |  | you must return a valid CadQuery object as a return value.  If you choose not to return a CadQuery object,
 | 
					
						
							| 
									
										
										
										
											2020-06-22 08:42:16 +02:00
										 |  |  | then your plugin will end the chain. Sometimes this is desired for example :py:meth:`cadquery.Workplane.size`
 | 
					
						
							| 
									
										
										
										
											2014-10-25 12:57:46 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | There are two ways you can safely continue the chain:
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |    1.  **return self**  If you simply wish to modify the stack contents, you can simply return a reference to
 | 
					
						
							|  |  |  |        self.  This approach is destructive, because the contents of the stack are modified, but it is also the
 | 
					
						
							|  |  |  |        simplest.
 | 
					
						
							| 
									
										
										
										
											2020-06-22 08:42:16 +02:00
										 |  |  |    2.  :py:meth:`cadquery.Workplane.newObject`  Most of the time, you will want to return a new object.  Using newObject will
 | 
					
						
							| 
									
										
										
										
											2014-10-25 12:57:46 -04:00
										 |  |  |        return a new CQ or Workplane object having the stack you specify, and will link this object to the
 | 
					
						
							|  |  |  |        previous one.  This preserves the original object and its stack.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Helper Methods
 | 
					
						
							| 
									
										
										
										
											2015-11-28 21:05:20 -05:00
										 |  |  | -----------------------
 | 
					
						
							| 
									
										
										
										
											2014-10-25 12:57:46 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | When you implement a CadQuery plugin, you are extending CadQuery's base objects.  As a result, you can call any
 | 
					
						
							|  |  |  | CadQuery or Workplane methods from inside of your extension.  You can also call a number of internal methods that
 | 
					
						
							|  |  |  | are designed to aid in plugin creation:
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-09 21:01:14 -05:00
										 |  |  |    * :py:meth:`cadquery.Workplane._makeWireAtPoints` will invoke a factory function you supply for all points on the stack,
 | 
					
						
							| 
									
										
										
										
											2014-10-25 12:57:46 -04:00
										 |  |  |      and return a properly constructed cadquery object. This function takes care of registering wires for you
 | 
					
						
							|  |  |  |      and everything like that
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-09 21:01:14 -05:00
										 |  |  |    * :py:meth:`cadquery.Workplane.newObject` returns a new Workplane object with the provided stack, and with its parent set
 | 
					
						
							| 
									
										
										
										
											2014-10-25 12:57:46 -04:00
										 |  |  |      to the current object. The preferred way to continue the chain
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-22 08:42:16 +02:00
										 |  |  |    * :py:meth:`cadquery.Workplane.findSolid` returns the first Solid found in the chain, working from the current object upwards
 | 
					
						
							| 
									
										
										
										
											2014-10-25 12:57:46 -04:00
										 |  |  |      in the chain. commonly used when your plugin will modify an existing solid, or needs to create objects and
 | 
					
						
							|  |  |  |      then combine them onto the 'main' part that is in progress
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-09 21:01:14 -05:00
										 |  |  |    * :py:meth:`cadquery.Workplane._addPendingWire` must be called if you add a wire.  This allows the base class to track all the wires
 | 
					
						
							| 
									
										
										
										
											2014-10-25 12:57:46 -04:00
										 |  |  |      that are created, so that they can be managed when extrusion occurs.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-09 21:01:14 -05:00
										 |  |  |    * :py:meth:`cadquery.Workplane.wire` gathers up all of the edges that have been drawn ( eg, by line, vline, etc ), and
 | 
					
						
							| 
									
										
										
										
											2014-10-25 12:57:46 -04:00
										 |  |  |      attempts to combine them into a single wire, which is returned. This should be used when your plugin creates
 | 
					
						
							| 
									
										
										
										
											2021-08-20 15:44:42 +03:00
										 |  |  |      2D edges, and you know it is time to collect them into a single wire.
 | 
					
						
							| 
									
										
										
										
											2014-10-25 12:57:46 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-09 21:01:14 -05:00
										 |  |  |    * :py:meth:`cadquery.Workplane.plane` provides a reference to the workplane, which allows you to convert between workplane
 | 
					
						
							| 
									
										
										
										
											2014-10-25 12:57:46 -04:00
										 |  |  |      coordinates and global coordinates:
 | 
					
						
							| 
									
										
										
										
											2019-01-17 13:25:51 -05:00
										 |  |  |      * :py:meth:`cadquery.occ_impl.geom.Plane.toWorldCoords` will convert local coordinates to global ones
 | 
					
						
							| 
									
										
										
										
											2019-01-30 06:32:37 -05:00
										 |  |  |      * :py:meth:`cadquery.occ_impl.geom.Plane.toLocalCoords` will convert from global coordinates to local coordinates
 | 
					
						
							| 
									
										
										
										
											2014-10-25 12:57:46 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | Coordinate Systems
 | 
					
						
							| 
									
										
										
										
											2015-11-28 21:05:20 -05:00
										 |  |  | -----------------------
 | 
					
						
							| 
									
										
										
										
											2014-10-25 12:57:46 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | Keep in mind that the user may be using a work plane that has created a local coordinate system. Consequently,
 | 
					
						
							|  |  |  | the orientation of shapes that you create are often implicitly defined by the user's workplane.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Any objects that you create must be fully defined in *global coordinates*, even though some or all of the users'
 | 
					
						
							|  |  |  | inputs may be defined in terms of local coordinates.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Linking in your plugin
 | 
					
						
							| 
									
										
										
										
											2015-11-28 21:05:20 -05:00
										 |  |  | -----------------------
 | 
					
						
							| 
									
										
										
										
											2014-10-25 12:57:46 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | Your plugin is a single method, which is attached to the main Workplane or CadQuery object.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Your plugin method's first parameter should be 'self', which will provide a reference to base class functionality.
 | 
					
						
							|  |  |  | You can also accept other arguments.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | To install it, simply attach it to the CadQuery or Workplane object, like this::
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-22 13:55:00 +12:00
										 |  |  |     def _yourFunction(self, arg1, arg):
 | 
					
						
							| 
									
										
										
										
											2023-07-22 13:36:52 +12:00
										 |  |  |         # do stuff
 | 
					
						
							| 
									
										
										
										
											2014-10-25 12:57:46 -04:00
										 |  |  |         return whatever_you_want
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-22 13:55:00 +12:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-09 21:01:14 -05:00
										 |  |  |     cq.Workplane.yourPlugin = _yourFunction
 | 
					
						
							| 
									
										
										
										
											2014-10-25 12:57:46 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | That's it!
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-28 21:05:20 -05:00
										 |  |  | CadQueryExample Plugins
 | 
					
						
							|  |  |  | -----------------------
 | 
					
						
							|  |  |  | Some core cadquery code is intentionally written exactly like a plugin.
 | 
					
						
							|  |  |  | If you are writing your own plugins, have a look at these methods for inspiration:
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-09 21:01:14 -05:00
										 |  |  |    * :py:meth:`cadquery.Workplane.polygon`
 | 
					
						
							|  |  |  |    * :py:meth:`cadquery.Workplane.cboreHole`
 | 
					
						
							| 
									
										
										
										
											2015-11-28 21:05:20 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-10-25 12:57:46 -04:00
										 |  |  | Plugin Example
 | 
					
						
							| 
									
										
										
										
											2015-11-28 21:05:20 -05:00
										 |  |  | -----------------------
 | 
					
						
							| 
									
										
										
										
											2014-10-25 12:57:46 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | This ultra simple plugin makes cubes of the specified size for each stack point.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | (The cubes are off-center because the boxes have their lower left corner at the reference points.)
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-03 19:48:31 +03:00
										 |  |  | .. cadquery::
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         from cadquery.occ_impl.shapes import box
 | 
					
						
							| 
									
										
										
										
											2014-10-25 12:57:46 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-22 13:55:00 +12:00
										 |  |  |         def makeCubes(self, length):
 | 
					
						
							|  |  |  |             # self refers to the CQ or Workplane object
 | 
					
						
							| 
									
										
										
										
											2014-10-25 12:57:46 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-22 13:55:00 +12:00
										 |  |  |             # inner method that creates a cube
 | 
					
						
							| 
									
										
										
										
											2020-06-22 08:42:16 +02:00
										 |  |  |             def _singleCube(loc):
 | 
					
						
							| 
									
										
										
										
											2023-07-22 13:55:00 +12:00
										 |  |  |                 # loc is a location in local coordinates
 | 
					
						
							|  |  |  |                 # since we're using eachpoint with useLocalCoordinates=True
 | 
					
						
							| 
									
										
										
										
											2024-07-03 19:48:31 +03:00
										 |  |  |                 return box(length, length, length).locate(loc)
 | 
					
						
							| 
									
										
										
										
											2023-07-22 13:55:00 +12:00
										 |  |  | 
 | 
					
						
							|  |  |  |             # use CQ utility method to iterate over the stack, call our
 | 
					
						
							|  |  |  |             # method, and convert to/from local coordinates.
 | 
					
						
							|  |  |  |             return self.eachpoint(_singleCube, True)
 | 
					
						
							| 
									
										
										
										
											2014-10-25 12:57:46 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-22 13:55:00 +12:00
										 |  |  |         # link the plugin into CadQuery
 | 
					
						
							| 
									
										
										
										
											2015-12-09 21:01:14 -05:00
										 |  |  |         cq.Workplane.makeCubes = makeCubes
 | 
					
						
							| 
									
										
										
										
											2014-10-25 12:57:46 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-22 13:55:00 +12:00
										 |  |  |         # use the plugin
 | 
					
						
							|  |  |  |         result = (
 | 
					
						
							|  |  |  |             cq.Workplane("XY")
 | 
					
						
							|  |  |  |             .box(6.0, 8.0, 0.5)
 | 
					
						
							|  |  |  |             .faces(">Z")
 | 
					
						
							|  |  |  |             .rect(4.0, 4.0, forConstruction=True)
 | 
					
						
							|  |  |  |             .vertices()
 | 
					
						
							|  |  |  |             .makeCubes(1.0)
 | 
					
						
							|  |  |  |             .combineSolids()
 | 
					
						
							|  |  |  |         )
 | 
					
						
							| 
									
										
										
										
											2014-10-25 12:57:46 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-03 19:48:31 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | Extending CadQuery: Special Methods
 | 
					
						
							|  |  |  | -----------------------------------
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The above-mentioned approach has one drawback, it requires monkey-patching or subclassing. To avoid this
 | 
					
						
							| 
									
										
										
										
											2024-08-21 06:07:30 +02:00
										 |  |  | one can also use the following special methods of :py:class:`cadquery.Workplane` and :py:class:`cadquery.Sketch`
 | 
					
						
							| 
									
										
										
										
											2024-07-03 19:48:31 +03:00
										 |  |  | and write plugins in a more functional style.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     * :py:meth:`cadquery.Workplane.map`
 | 
					
						
							|  |  |  |     * :py:meth:`cadquery.Workplane.apply`
 | 
					
						
							|  |  |  |     * :py:meth:`cadquery.Workplane.invoke`
 | 
					
						
							| 
									
										
										
										
											2024-08-21 06:07:30 +02:00
										 |  |  |     * :py:meth:`cadquery.Sketch.map`
 | 
					
						
							|  |  |  |     * :py:meth:`cadquery.Sketch.apply`
 | 
					
						
							|  |  |  |     * :py:meth:`cadquery.Sketch.invoke`
 | 
					
						
							| 
									
										
										
										
											2024-07-03 19:48:31 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | Here is the same plugin rewritten using one of those methods.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | .. cadquery::
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         from cadquery.occ_impl.shapes import box
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         def makeCubes(length):
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # inner method that creates the cubes
 | 
					
						
							|  |  |  |             def callback(wp):
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 return wp.eachpoint(box(length, length, length), True)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             return callback
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # use the plugin
 | 
					
						
							|  |  |  |         result = (
 | 
					
						
							|  |  |  |             cq.Workplane("XY")
 | 
					
						
							|  |  |  |             .box(6.0, 8.0, 0.5)
 | 
					
						
							|  |  |  |             .faces(">Z")
 | 
					
						
							|  |  |  |             .rect(4.0, 4.0, forConstruction=True)
 | 
					
						
							|  |  |  |             .vertices()
 | 
					
						
							|  |  |  |             .invoke(makeCubes(1.0))
 | 
					
						
							|  |  |  |             .combineSolids()
 | 
					
						
							|  |  |  |         )
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Such an approach is more friendly for auto-completion and static analysis tools.
 |