|
1 | 1 | .. _3d_cad_primer:
|
2 | 2 |
|
3 |
| - |
| 3 | +.. _cadquery_concepts: |
4 | 4 | CadQuery Concepts
|
5 | 5 | ===================================
|
6 | 6 |
|
@@ -167,6 +167,190 @@ iterates on each member of the stack.
|
167 | 167 |
|
168 | 168 | This is really useful to remember when you author your own plugins. :py:meth:`cadquery.cq.Workplane.each` is useful for this purpose.
|
169 | 169 |
|
| 170 | +CadQuery API layers |
| 171 | +--------------------------- |
| 172 | + |
| 173 | +Once you start to dive a bit more into CadQuery, you may find yourself a bit confused juggling between different types of objects the CadQuery APIs can return. |
| 174 | +This chapter aims to give an explanation on this topic and to provide background on the underlying implementation and kernel layers so you can leverage more of CadQuery functionality. |
| 175 | + |
| 176 | +CadQuery is composed of 3 different API, which are implemented on top of each other. |
| 177 | + |
| 178 | +1. The Fluent API |
| 179 | +2. The Direct API |
| 180 | +3. The OCCT API |
| 181 | + |
| 182 | +The Fluent API |
| 183 | +~~~~~~~~~~~~~~~~~~~~~~ |
| 184 | + |
| 185 | +What we call the fluent API is what you work with when you first start using CadQuery, the :class:`~cadquery.Workplane` class and all its methods defines the Fluent API. |
| 186 | +This is the API you will use and see most of the time, it's fairly easy to use and it simplifies a lot of things for you. A classic example could be : :: |
| 187 | + |
| 188 | + part = Workplane('XY').box(1,2,3).faces(">Z").vertices().circle(0.5).cutThruAll() |
| 189 | + |
| 190 | +Here we create a :class:`~cadquery.Workplane` object on which we subsequently call several methods to create our part. A general way of thinking about the Fluent API is to |
| 191 | +consider the :class:`~cadquery.Workplane` as your part object and all it's methods as operations that will affect your part. |
| 192 | +Often you will start with an empty :class:`~cadquery.Workplane`, then add more features by calling :class:`~cadquery.Workplane` methods. |
| 193 | + |
| 194 | +This hierarchical structure of operations modifying a part is well seen with the traditional code style used in CadQuery code. |
| 195 | +Code written with the CadQuery fluent API will often look like this : :: |
| 196 | + |
| 197 | + part = (Workplane('XY') |
| 198 | + .box(1,2,3) |
| 199 | + .faces(">Z") |
| 200 | + .vertices() |
| 201 | + .circle(0.5) |
| 202 | + .cutThruAll() |
| 203 | + ) |
| 204 | + |
| 205 | +Or like this : :: |
| 206 | + |
| 207 | + part = Workplane('XY') |
| 208 | + part = part.box(1,2,3) |
| 209 | + part = part.faces(">Z") |
| 210 | + part = part.vertices() |
| 211 | + part = part.circle(0.5) |
| 212 | + part = part.cutThruAll() |
| 213 | + |
| 214 | +.. note:: |
| 215 | + While the first code style is what people default to, it's important to note that when you write your code like this it's equivalent as writting it on a single line. |
| 216 | + It's then more difficult to debug as you cannot visualize each operation step by step, which is a functionality that is provided by the CQ-Editor debugger for example. |
| 217 | + |
| 218 | +The Direct API |
| 219 | +~~~~~~~~~~~~~~~~~~~~~~ |
| 220 | + |
| 221 | +While the fluent API exposes much functionality, you may find scenarios that require extra flexibility or require working with lower level objects. |
| 222 | + |
| 223 | +The direct API is the API that is called by the fluent API under the hood. The 9 topological classes and their methods compose the direct API. |
| 224 | +These classes actually wrap the equivalent Open CASCADE Technology (OCCT) classes. |
| 225 | +The 9 topological classes are : |
| 226 | + |
| 227 | +1. :class:`~cadquery.Shape` |
| 228 | +2. :class:`~cadquery.Compound` |
| 229 | +3. :class:`~cadquery.CompSolid` |
| 230 | +4. :class:`~cadquery.Solid` |
| 231 | +5. :class:`~cadquery.Shell` |
| 232 | +6. :class:`~cadquery.Face` |
| 233 | +7. :class:`~cadquery.Wire` |
| 234 | +8. :class:`~cadquery.Edge` |
| 235 | +9. :class:`~cadquery.Vertex` |
| 236 | + |
| 237 | +Each class has its own methods to create and/or edit shapes of their respective type. As already explained in :ref:`cadquery_concepts` there is also some kind of hierarchy in the |
| 238 | +topological classes. A Wire is made of several edges which are themselves made of several vertices. This means you can create geometry from the bottom up and have a lot of control over it. |
| 239 | + |
| 240 | +For example we can create a circular face like so :: |
| 241 | + |
| 242 | + circle_wire = Wire.makeCircle(10,Vector(0,0,0), Vector(0,0,1)) |
| 243 | + circular_face = Face.makeFromWires(circle_wire, []) |
| 244 | + |
| 245 | +.. note:: |
| 246 | + In CadQuery (and OCCT) all the topological classes are shapes, the :class:`~cadquery.Shape` class is the most abstract topological class. |
| 247 | + The topological class inherits :class:`~cadquery.Mixin3D` or :class:`~cadquery.Mixin1D` which provide aditional methods that are shared between the classes that inherits them. |
| 248 | + |
| 249 | +The direct API as its name suggests doesn't provide a parent/children data structure, instead each method call directly returns an object of the specified topological type. |
| 250 | +It is more verbose than the fluent API and more tedious to work with, but as it offer more flexibility (you can work with faces, which is something you can't do in the fluent API) |
| 251 | +it is sometimes more convenient than the fluent API. |
| 252 | + |
| 253 | +The OCCT API |
| 254 | +~~~~~~~~~~~~~~~~~~~~~~ |
| 255 | + |
| 256 | +Finally we are discussing about the OCCT API. The OCCT API is the lowest level of CadQuery. The direct API is built upon the OCCT API, where the OCCT API in CadQuery is available through OCP. |
| 257 | +OCP are the Python bindings of the OCCT C++ libraries CadQuery uses. This means you have access to (almost) all the OCCT C++ libraries in Python and in CadQuery. |
| 258 | +Working with the OCCT API will give you the maximum flexibility and control over you designs, it is however very verbose and difficult to use. You will need to have a strong |
| 259 | +knowledge of the different C++ libraries to be able to achieve what you want. To obtain this knowledge the most obvious ways are : |
| 260 | + |
| 261 | +1. Read the direct API source code, since it is build upon the OCCT API it is full of example usage. |
| 262 | +2. Go through the `C++ documentation <https://dev.opencascade.org/doc/overview/html/>`_ |
| 263 | + |
| 264 | +.. note:: |
| 265 | + The general way of importing a specific class of the OCCT API is :: |
| 266 | + |
| 267 | + from OCP.thePackageName import theClassName |
| 268 | + |
| 269 | + For example if you want to use the class `BRepPrimAPI_MakeBox <https://dev.opencascade.org/doc/refman/html/class_b_rep_prim_a_p_i___make_box.html>`_. |
| 270 | + You will go by the following :: |
| 271 | + |
| 272 | + from OCP.BRepPrimAPI import BRepPrimAPI_MakeBox |
| 273 | + |
| 274 | + The package name of any class is written at the top of the documentation page. Often it's written in the class name itself as a prefix. |
| 275 | + |
| 276 | +Going back and forth between the APIs |
| 277 | +~~~~~~~~~~~~~~~~~~~~~~ |
| 278 | + |
| 279 | +While the 3 APIs provide 3 different layer of complexity and functionality you can mix the 3 layers as you wish. |
| 280 | +Below is presented the different ways you can interact with the different API layers. |
| 281 | + |
| 282 | +------------------------- |
| 283 | +Fluent API <=> Direct API |
| 284 | +------------------------- |
| 285 | + |
| 286 | +Here are all the possibilities you have to get an object from the Direct API (i.e a topological object). |
| 287 | + |
| 288 | +You can end the Fluent API call chain and get the last object on the stack with :py:meth:`Workplane.val` alternatively you can get all |
| 289 | +the objects with :py:meth:`Workplane.vals` :: |
| 290 | + |
| 291 | + box = Workplane().box(10,5,5) |
| 292 | + print(type(box)) |
| 293 | + >>> <class cadquery.cq.Workplane> |
| 294 | + |
| 295 | + box = Workplane().box(10,5,5).val() |
| 296 | + print(type(box)) |
| 297 | + >>> <class cadquery.occ_impl.shapes.Solid> |
| 298 | + |
| 299 | +If you are only interested in getting the context solid of your Workplane, you can use :py:meth:`Workplane.findSolid`:: |
| 300 | + |
| 301 | + part = Workplane().box(10,5,5).circle(3).val() |
| 302 | + print(type(part)) |
| 303 | + >>> <class cadquery.cq.Wire> |
| 304 | + |
| 305 | + part = Workplane().box(10,5,5).circle(3).findSolid() |
| 306 | + print(type(part)) |
| 307 | + >>> <class cadquery.occ_impl.shapes.Compound> |
| 308 | + # The return type of findSolid is either a Solid or a Compound object |
| 309 | + |
| 310 | +If you want to go the other way around i.e using objects from the topological API in the Fluent API here are your options : |
| 311 | + |
| 312 | +You can pass a topological object as a base object to the :class:`~cadquery.Workplane` object. :: |
| 313 | + |
| 314 | + solid_box = Solid.makeBox(10,10,10) |
| 315 | + part = Workplane(obj = solid_box) |
| 316 | + # And you can continue your modelling in the fluent API |
| 317 | + part = part.faces(">Z").circle(1).extrude(10) |
| 318 | + |
| 319 | + |
| 320 | +You can add a topological object as a new operation/step in the Fluent API call chain with :py:meth:`Workplane.newObject` :: |
| 321 | + |
| 322 | + circle_wire = Wire.makeCircle(1,Vector(0,0,0), Vector(0,0,1)) |
| 323 | + box = Workplane().box(10,10,10).newObject([circle_wire]) |
| 324 | + # And you can continue modelling |
| 325 | + box = box.toPending().cutThruAll() # notice the call to `toPending` that is needed if you want to use it in a subsequent operation |
| 326 | + |
| 327 | +------------------------- |
| 328 | +Direct API <=> OCCT API |
| 329 | +------------------------- |
| 330 | + |
| 331 | +Every object of the Direct API stores its OCCT equivalent object in its :attr:`wrapped` attribute. :: |
| 332 | + |
| 333 | + box = Solid.makeBox(10,5,5) |
| 334 | + print(type(box)) |
| 335 | + >>> <class cadquery.occ_impl.shapes.Solid> |
| 336 | + |
| 337 | + box = Solid.makeBox(10,5,5).wrapped |
| 338 | + print(type(box)) |
| 339 | + >>> <class OCP.TopoDS.TopoDS_Solid> |
| 340 | + |
| 341 | + |
| 342 | +If you want to cast an OCCT object into a Direct API one you can just pass it as a parameter of the intended class :: |
| 343 | + |
| 344 | + occt_box = BRepPrimAPI_MakeBox(5,5,5).Solid() |
| 345 | + print(type(occt_box)) |
| 346 | + >>> <class OCP.TopoDS.TopoDS_Solid> |
| 347 | + |
| 348 | + direct_api_box = Solid(occt_box) |
| 349 | + print(type(direct_api_box)) |
| 350 | + >>> <class cadquery.occ_impl.shapes.Solid> |
| 351 | + |
| 352 | +.. note:: |
| 353 | + You can cast into the direct API the types found `here <https://dev.opencascade.org/doc/refman/html/class_topo_d_s___shape.html>`_ |
170 | 354 |
|
171 | 355 | An Introspective Example
|
172 | 356 | ------------------------
|
|
0 commit comments