* Initial version of the constraints docs * Adding the assy section * apireference cleanup * Include cutouts * Roadmap cleanup * _query cleanup * Black fix * Add RadiusNthSelector again * Typo fix * Paste error fix * Show detailed selector docs * Another typo fix * Mention other constrain overload * Added test for end()
269 lines
11 KiB
ReStructuredText
269 lines
11 KiB
ReStructuredText
.. _3d_cad_primer:
|
|
|
|
|
|
CadQuery Concepts
|
|
===================================
|
|
|
|
|
|
3D BREP Topology Concepts
|
|
---------------------------
|
|
Before talking about CadQuery, it makes sense to talk a little about 3D CAD topology. CadQuery is based upon the
|
|
OpenCascade kernel, which uses Boundary Representations ( BREP ) for objects. This just means that objects
|
|
are defined by their enclosing surfaces.
|
|
|
|
When working in a BREP system, these fundamental constructs exist to define a shape (working up the food chain):
|
|
|
|
:vertex: a single point in space
|
|
:edge: a connection between two or more vertices along a particular path (called a curve)
|
|
:wire: a collection of edges that are connected together.
|
|
:face: a set of edges or wires that enclose a surface
|
|
:shell: a collection of faces that are connected together along some of their edges
|
|
:solid: a shell that has a closed interior
|
|
:compound: a collection of solids
|
|
|
|
When using CadQuery, all of these objects are created, hopefully with the least possible work. In the actual CAD
|
|
kernel, there is another set of Geometrical constructs involved as well. For example, an arc-shaped edge will
|
|
hold a reference to an underlying curve that is a full circle, and each linear edge holds underneath it the equation
|
|
for a line. CadQuery shields you from these constructs.
|
|
|
|
|
|
CQ, the CadQuery Object
|
|
---------------------------
|
|
|
|
The CadQuery object wraps a BREP feature, and provides functionality around it. Typical examples include rotating,
|
|
transforming, combining objects, and creating workplanes.
|
|
|
|
See :ref:`apireference` to learn more.
|
|
|
|
|
|
Workplanes
|
|
---------------------------
|
|
|
|
Most CAD programs use the concept of Workplanes. If you have experience with other CAD programs you will probably
|
|
feel comfortable with CadQuery's Workplanes, but if you don't have experience then they are an essential concept to
|
|
understand.
|
|
|
|
Workplanes represent a plane in space, from which other features can be located. They have a center point and a local
|
|
coordinate system. Most methods that create an object do so relative to the current workplane.
|
|
|
|
Usually the first workplane created is the "XY" plane, also known as the "front" plane. Once a solid is defined the most
|
|
common way to create a workplane is to select a face on the solid that you intend to modify and create a new workplane
|
|
relative to it. You can also create new workplanes in anywhere in world coordinate system, or relative to other planes
|
|
using offsets or rotations.
|
|
|
|
The most powerful feature of workplanes is that they allow you to work in 2D space in the coordinate system of the
|
|
workplane, and then CadQuery will transform these points from the workplane coordinate system to the world coordinate
|
|
system so your 3D features are located where you intended. This makes scripts much easier to create and maintain.
|
|
|
|
See :py:class:`cadquery.Workplane` to learn more.
|
|
|
|
|
|
2D Construction
|
|
---------------------------
|
|
|
|
Once you create a workplane, you can work in 2D, and then later use the features you create to make 3D objects.
|
|
You'll find all of the 2D constructs you expect -- circles, lines, arcs, mirroring, points, etc.
|
|
|
|
See :ref:`2dOperations` to learn more.
|
|
|
|
|
|
3D Construction
|
|
---------------------------
|
|
|
|
You can construct 3D primitives such as boxes, spheres, wedges, and cylinders directly. You can also sweep, extrude,
|
|
and loft 2D geometry to form 3D features. Of course the basic primitive operations are also available.
|
|
|
|
See :ref:`3doperations` to learn more.
|
|
|
|
|
|
|
|
Selectors
|
|
---------------------------
|
|
|
|
Selectors allow you to select one or more features, in order to define new features. As an example, you might
|
|
extrude a box, and then select the top face as the location for a new feature. Or, you might extrude a box, and
|
|
then select all of the vertical edges so that you can apply a fillet to them.
|
|
|
|
You can select Vertices, Edges, Faces, Solids, and Wires using selectors.
|
|
|
|
Think of selectors as the equivalent of your hand and mouse, if you were to build an object using a conventional CAD system.
|
|
|
|
You can learn more about selectors :ref:`selectors`
|
|
|
|
|
|
Construction Geometry
|
|
---------------------------
|
|
Construction geometry are features that are not part of the object, but are only defined to aid in building the object.
|
|
A common example might be to define a rectangle, and then use the corners to define the location of a set of holes.
|
|
|
|
Most CadQuery construction methods provide a ``forConstruction`` keyword, which creates a feature that will only be used
|
|
to locate other features
|
|
|
|
|
|
The Stack
|
|
---------------------------
|
|
|
|
As you work in CadQuery, each operation returns a new CadQuery object with the result of that operations. Each CadQuery
|
|
object has a list of objects, and a reference to its parent.
|
|
|
|
You can always go backwards to older operations by removing the current object from the stack. For example::
|
|
|
|
Workplane(someObject).faces(">Z").first().vertices()
|
|
|
|
returns a CadQuery object that contains all of the vertices on the highest face of someObject. But you can always move
|
|
backwards in the stack to get the face as well::
|
|
|
|
Workplane(someObject).faces(">Z").first().vertices().end()
|
|
|
|
You can browse stack access methods here: :ref:`stackMethods`.
|
|
|
|
|
|
.. _chaining:
|
|
|
|
Chaining
|
|
---------------------------
|
|
|
|
All CadQuery methods return another CadQuery object, so that you can chain the methods together fluently. Use
|
|
the core CQ methods to get at the objects that were created.
|
|
|
|
Each time a new CadQuery object is produced during these chained calls, it has a ``parent`` attribute that points
|
|
to the CadQuery object that created it. Several CadQuery methods search this parent chain, for example when searching
|
|
for the context solid. You can also give a CadQuery object a tag, and further down your chain of CadQuery calls you
|
|
can refer back to this particular object using its tag.
|
|
|
|
|
|
The Context Solid
|
|
---------------------------
|
|
|
|
Most of the time, you are building a single object, and adding features to that single object. CadQuery watches
|
|
your operations, and defines the first solid object created as the 'context solid'. After that, any features
|
|
you create are automatically combined (unless you specify otherwise) with that solid. This happens even if the
|
|
solid was created a long way up in the stack. For example::
|
|
|
|
Workplane('XY').box(1,2,3).faces(">Z").circle(0.25).extrude()
|
|
|
|
Will create a 1x2x3 box, with a cylindrical boss extending from the top face. It was not necessary to manually
|
|
combine the cylinder created by extruding the circle with the box, because the default behavior for extrude is
|
|
to combine the result with the context solid. The hole() method works similarly -- CadQuery presumes that you want
|
|
to subtract the hole from the context solid.
|
|
|
|
If you want to avoid this, you can specify ``combine=False``, and CadQuery will create the solid separately.
|
|
|
|
|
|
Iteration
|
|
---------------------------
|
|
|
|
CAD models often have repeated geometry, and its really annoying to resort to for loops to construct features.
|
|
Many CadQuery methods operate automatically on each element on the stack, so that you don't have to write loops.
|
|
For example, this::
|
|
|
|
Workplane('XY').box(1,2,3).faces(">Z").vertices().circle(0.5)
|
|
|
|
Will actually create 4 circles, because ``vertices()`` selects 4 vertices of a rectangular face, and the ``circle()`` method
|
|
iterates on each member of the stack.
|
|
|
|
This is really useful to remember when you author your own plugins. :py:meth:`cadquery.cq.Workplane.each` is useful for this purpose.
|
|
|
|
|
|
Assemblies
|
|
----------
|
|
|
|
Simple models can be combined into complex, possibly nested, assemblies.
|
|
|
|
.. image:: _static/assy.png
|
|
|
|
A simple example could look as follows::
|
|
|
|
from cadquery import *
|
|
|
|
w = 10
|
|
d = 10
|
|
h = 10
|
|
|
|
part1 = Workplane().box(2*w,2*d,h)
|
|
part2 = Workplane().box(w,d,2*h)
|
|
part3 = Workplane().box(w,d,3*h)
|
|
|
|
assy = (
|
|
Assembly(part1, loc=Location(Vector(-w,0,h/2)))
|
|
.add(part2, loc=Location(Vector(1.5*w,-.5*d,h/2)), color=Color(0,0,1,0.5))
|
|
.add(part3, loc=Location(Vector(-.5*w,-.5*d,2*h)), color=Color("red"))
|
|
)
|
|
|
|
Resulting in:
|
|
|
|
.. image:: _static/simple_assy.png
|
|
|
|
Note that the locations of the children parts are defined with respect to their parents - in the above example ``part3`` will be located at (-5,-5,20) in the global coordinate system. Assemblies with different colors can be created this way and exported to STEP or the native OCCT xml format.
|
|
|
|
You can browse assembly related methods here: :ref:`assembly`.
|
|
|
|
Assemblies with constraints
|
|
---------------------------
|
|
|
|
Sometimes it is not desirable to define the component positions explicitly but rather use
|
|
constraints to obtain a fully parametric assembly. This can be achieved in the following way::
|
|
|
|
from cadquery import *
|
|
|
|
w = 10
|
|
d = 10
|
|
h = 10
|
|
|
|
part1 = Workplane().box(2*w,2*d,h)
|
|
part2 = Workplane().box(w,d,2*h)
|
|
part3 = Workplane().box(w,d,3*h)
|
|
|
|
assy = (
|
|
Assembly(part1, name='part1',loc=Location(Vector(-w,0,h/2)))
|
|
.add(part2, name='part2',color=Color(0,0,1,0.5))
|
|
.add(part3, name='part3',color=Color("red"))
|
|
.constrain('part1@faces@>Z','part3@faces@<Z','Axis')
|
|
.constrain('part1@faces@>Z','part2@faces@<Z','Axis')
|
|
.constrain('part1@faces@>Y','part3@faces@<Y','Axis')
|
|
.constrain('part1@faces@>Y','part2@faces@<Y','Axis')
|
|
.constrain('part1@vertices@>(-1,-1,1)','part3@vertices@>(-1,-1,-1)','Point')
|
|
.constrain('part1@vertices@>(1,-1,-1)','part2@vertices@>(-1,-1,-1)','Point')
|
|
.solve()
|
|
)
|
|
|
|
This code results in identical object as one from the previous section. The added
|
|
benefit is that with changing parameters ``w``, ``d``, ``h`` the final locations
|
|
will be calculated automatically. It is admittedly dense and can be made clearer
|
|
using tags. Tags can be directly referenced when constructing the constraints::
|
|
|
|
from cadquery import *
|
|
|
|
w = 10
|
|
d = 10
|
|
h = 10
|
|
|
|
part1 = Workplane().box(2*w,2*d,h)
|
|
part2 = Workplane().box(w,d,2*h)
|
|
part3 = Workplane().box(w,d,3*h)
|
|
|
|
part1.faces('>Z').edges('<X').vertices('<Y').tag('pt1')
|
|
part1.faces('>X').edges('<Z').vertices('<Y').tag('pt2')
|
|
part3.faces('<Z').edges('<X').vertices('<Y').tag('pt1')
|
|
part2.faces('<X').edges('<Z').vertices('<Y').tag('pt2')
|
|
|
|
assy1 = (
|
|
Assembly(part1, name='part1',loc=Location(Vector(-w,0,h/2)))
|
|
.add(part2, name='part2',color=Color(0,0,1,0.5))
|
|
.add(part3, name='part3',color=Color("red"))
|
|
.constrain('part1@faces@>Z','part3@faces@<Z','Axis')
|
|
.constrain('part1@faces@>Z','part2@faces@<Z','Axis')
|
|
.constrain('part1@faces@>Y','part3@faces@<Y','Axis')
|
|
.constrain('part1@faces@>Y','part2@faces@<Y','Axis')
|
|
.constrain('part1?pt1','part3?pt1','Point')
|
|
.constrain('part1?pt2','part2?pt2','Point')
|
|
.solve()
|
|
)
|
|
|
|
The following constraints are currently implemented:
|
|
|
|
:Axis: two normal vectors are anti-coincident or the cosine of the angle between them is equal to the specified value. Can be defined for all entities with consistent normal vector - planar faces, wires and edges.
|
|
:Point: two points are coincident or separated by a specified distance. Can be defined for all entities, center of mass is used for lines, faces, solids and the vertex position for vertices.
|
|
:Plane: combination of :Axis: and :Point: constraints.
|
|
|
|
For a more elaborate assembly example see :ref:`assytutorial`. |