cadquery-freecad-module/doc/extending.rst
2014-10-25 12:57:46 -04:00

179 lines
7.3 KiB
ReStructuredText

.. _extending:
******************
Extending CadQuery
******************
.. automodule:: cadfile.cadutils.cadquery
If you find that CadQuery doesnt suit your needs, you can easily extend it. CadQuery provides several extension
methods:
* You can load plugins others have developed. This is by far the easiest way to access other code
* you can define your own plugins.
* you can use FreeCAD script directly
Loading external Plugins
-----------------------
You can load a plugin using the tools.loadScript(*URL*) directive in your script.
Using FreeCAD Script
--------------------
The easiest way to extend CadQuery is to simply use FreeCAD script inside of your build method. Just about
any valid FreeCAD script will execute just fine. For example, this simple CadQuery script::
return Workplane("XY").box(1.0,2.0,3.0).val()
is actually equivalent to::
return Part.makeBox(1.0,2.0,3.0)
As long as you return a valid FreeCAD Shape, you can use any FreeCAD methods you like. You can even mix and match the
two. For example, consider this script, which creates a FreeCAD box, but then uses cadquery to select its faces::
box = Part.makeBox(1.0,2.0,3.0)
cq = CQ(box).faces(">Z").size() # returns 6
Extending CadQuery: Plugins
----------------------------
Though you can get a lot done with FreeCAD, the code gets pretty nasty in a hurry. CadQuery shields you from
a lot of the complexity of the FreeCAD api.
You can get the best of both worlds by wrapping your freecad script into a CadQuery plugin.
A CadQuery plugin is simply a function that is attached to the CadQuery :py:meth:`CQ` or :py:meth:`Workplane` class.
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
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Every CadQuery object has a local stack, which contains a list of items. The items on the stack will be
one of these types:
* **A CadQuery SolidReference object**, which holds a reference to a FreeCAD solid
* **A FreeCAD object**, a Vertex, Edge, Wire, Face, Shell, Solid, or Compound
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
^^^^^^^^^^^^^^^^^^^^^^^^^
CadQuery's fluent api relies on the ability to chain calls together one after another. For this to work,
you must return a valid CadQuery object as a return value. If you choose not to return a CadQuery object,
then your plugin will end the chain. Sometimes this is desired for example :py:meth:`CQ.size`
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.
2. :py:meth:`CQ.newObject` Most of the time, you will want to return a new object. Using newObject will
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
^^^^^^^^^^^^^^^^^^^^^^^^^
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:
* :py:meth:`Workplane._pointsOnStack` returns a FreeCAD Vector ( a point ) for each item on the stack. Useful if you
are writing a plugin that you'd like to operate on all values on the stack, like :py:meth:`Workplane.circle` and
most other built-ins do
* :py:meth:`Workplane._makeWireAtPoints` will invoke a factory function you supply for all points on the stack,
and return a properly constructed cadquery object. This function takes care of registering wires for you
and everything like that
* :py:meth:`Workplane.newObject` returns a new Workplane object with the provided stack, and with its parent set
to the current object. The preferred way to continue the chain
* :py:meth:`Workplane.findSolid` returns the first Solid found in the chain, working from the current object upwards
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
* :py:meth:`Workplane._addWire` must be called if you add a wire. This allows the base class to track all the wires
that are created, so that they can be managed when extrusion occurs.
* :py:meth:`Workplane.wire` gathers up all of the edges that have been drawn ( eg, by line, vline, etc ), and
attempts to combine them into a single wire, which is returned. This should be used when your plugin creates
2-d edges, and you know it is time to collect them into a single wire.
* :py:meth:`Workplane.plane` provides a reference to the workplane, which allows you to convert between workplane
coordinates and global coordinates:
* :py:meth:`Plane.toWorldCoords` will convert local coordinates to global ones
* :py:meth:`Plane.toLocalCoords` will convet from global coordinates to local coordinates
Coordinate Systems
^^^^^^^^^^^^^^^^^^^^^^
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
^^^^^^^^^^^^^^^^^^^^^^^
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::
def _yourFunction(self,arg1,arg):
do stuff
return whatever_you_want
Workplane.yourPlugin = _yourFunction
That's it!
Plugin Example
^^^^^^^^^^^^^^^
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.)
.. cq_plot::
def makeCubes(self,length):
#self refers to the CQ or Workplane object
#inner method that creates a cube
def _singleCube(pnt):
#pnt is a location in local coordinates
#since we're using eachpoint with useLocalCoordinates=True
return Solid.makeBox(length,length,length,pnt)
#use CQ utility method to iterate over the stack, call our
#method, and convert to/from local coordinates.
return self.eachpoint(_singleCube,True)
#link the plugin into cadQuery
Workplane.makeCubes = makeCubes
#use the plugin
result = Workplane("XY").box(6.0,8.0,0.5).faces(">Z").rect(4.0,4.0,forConstruction=True).vertices() \
.makeCubes(1.0).combineSolids()