File size: 21,741 Bytes
7c72eb2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558

Workplane
=========

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 anywhere in the 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, wedges, cylinders and spheres 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.

See :ref:`selectors` to learn more.


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 Workplane object with the result of that
operation. Each Workplane 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 Workplane methods return another Workplane object, so that you can chain the methods together
fluently. Use the core Workplane methods to get at the objects that were created.

Each time a new Workplane object is produced during these chained calls, it has a
:attr:`~cadquery.Workplane.parent` attribute that points to the Workplane object that created it.
Several CadQuery methods search this parent chain, for example when searching for the context solid.
You can also give a Workplane object a tag, and further down your chain of 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(1)

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 :meth:`~cadquery.Workplane.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 it's 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.Workplane.each` is useful for this purpose.


An Introspective Example
------------------------

.. note::
    If you are just beginning with CadQuery then you can leave this example for later.  If you have
    some experience with creating CadQuery models and now you want to read the CadQuery source to
    better understand what your code does, then it is recommended you read this example first.

To demonstrate the above concepts, we can define a more detailed string representations for the
:class:`~cadquery.Workplane`, :class:`~cadquery.Plane` and :class:`~cadquery.CQContext` classes and
patch them in::

    import cadquery as cq


    def tidy_repr(obj):
        """Shortens a default repr string"""
        return repr(obj).split(".")[-1].rstrip(">")


    def _ctx_str(self):
        return (
            tidy_repr(self)
            + ":\n"
            + f"    pendingWires: {self.pendingWires}\n"
            + f"    pendingEdges: {self.pendingEdges}\n"
            + f"    tags: {self.tags}"
        )


    cq.cq.CQContext.__str__ = _ctx_str


    def _plane_str(self):
        return (
            tidy_repr(self)
            + ":\n"
            + f"    origin: {self.origin.toTuple()}\n"
            + f"    z direction: {self.zDir.toTuple()}"
        )


    cq.occ_impl.geom.Plane.__str__ = _plane_str


    def _wp_str(self):
        out = tidy_repr(self) + ":\n"
        out += f"  parent: {tidy_repr(self.parent)}\n" if self.parent else "  no parent\n"
        out += f"  plane: {self.plane}\n"
        out += f"  objects: {self.objects}\n"
        out += f"  modelling context: {self.ctx}"
        return out


    cq.Workplane.__str__ = _wp_str

Now we can make a simple part and examine the :class:`~cadquery.Workplane` and
:class:`~cadquery.cq.CQContext` objects at each step. The final part looks like:

.. cadquery::
    :select: part

    part = (
        cq.Workplane()
        .box(1, 1, 1)
        .tag("base")
        .wires(">Z")
        .toPending()
        .translate((0.1, 0.1, 1.0))
        .toPending()
        .loft()
        .faces(">>X", tag="base")
        .workplane(centerOption="CenterOfMass")
        .circle(0.2)
        .extrude(1)
    )

.. note::
    Some of the modelling process for this part is a bit contrived and not a great example of fluent
    CadQuery techniques.

The start of our chain of calls is::

    part = cq.Workplane()
    print(part)

Which produces the output:

.. code-block:: none

    Workplane object at 0x2760:
      no parent
      plane: Plane object at 0x2850:
        origin: (0.0, 0.0, 0.0)
        z direction: (0.0, 0.0, 1.0)
      objects: []
      modelling context: CQContext object at 0x2730:
        pendingWires: []
        pendingEdges: []
        tags: {}

This is simply an empty :class:`~cadquery.Workplane`. Being the first :class:`~cadquery.Workplane`
in the chain, it does not have a parent. The :attr:`~cadquery.Workplane.plane` attribute contains a
:class:`~cadquery.Plane` object that describes the XY plane.

Now we create a simple box. To keep things short, the ``print(part)`` line will not be shown for the
rest of these code blocks::

    part = part.box(1, 1, 1)

Which produces the output:

.. code-block:: none

    Workplane object at 0xaa90:
      parent: Workplane object at 0x2760
      plane: Plane object at 0x3850:
        origin: (0.0, 0.0, 0.0)
        z direction: (0.0, 0.0, 1.0)
      objects: [<cadquery.occ_impl.shapes.Solid object at 0xbbe0>]
      modelling context: CQContext object at 0x2730:
        pendingWires: []
        pendingEdges: []
        tags: {}

The first thing to note is that this is a different :class:`~cadquery.Workplane` object to the
previous one, and in the :attr:`~cadquery.Workplane.parent` attribute of this
:class:`~cadquery.Workplane` is our previous :class:`~cadquery.Workplane`. Returning a new instance
of :class:`~cadquery.Workplane` is the normal behaviour of most :class:`~cadquery.Workplane` methods
(with some exceptions, as will be shown below) and this is how the `chaining`_ concept is
implemented.

Secondly, the modelling context object is the same as the one in the previous
:class:`~cadquery.Workplane`, and this one modelling context at ``0x2730`` will be shared between
every :class:`Workplane` object in this chain. If we instantiate a new :class:`~cadquery.Workplane`
with ``part2 = cq.Workplane()``, then this ``part2`` would have a different instance of the
:class:`~cadquery.cq.CQContext` attached to it.

Thirdly, in our objects list is a single :class:`~cadquery.Solid` object, which is the box we just
created.

Often when creating models you will find yourself wanting to refer back to a specific
:class:`~cadquery.Workplane` object, perhaps because it is easier to select the feature you want in this
earlier state, or because you want to reuse a plane. Tags offer a way to refer back to a previous
:class:`~cadquery.Workplane`. We can tag the :class:`~cadquery.Workplane` that contains this basic box now::

    part = part.tag("base")

The string representation of ``part`` is now:

.. code-block:: none

    Workplane object at 0xaa90:
      parent: Workplane object at 0x2760
      plane: Plane object at 0x3850:
        origin: (0.0, 0.0, 0.0)
        z direction: (0.0, 0.0, 1.0)
      objects: [<cadquery.occ_impl.shapes.Solid object at 0xbbe0>]
      modelling context: CQContext object at 0x2730:
        pendingWires: []
        pendingEdges: []
        tags: {'base': <cadquery.cq.Workplane object at 0xaa90>}

The :attr:`~cadquery.cq.CQContext.tags` attribute of the modelling context is simply a dict
associating the string name given by the :meth:`~cadquery.Workplane.tag` method to the
:class:`~cadquery.Workplane`. Methods such as :meth:`~cadquery.Workplane.workplaneFromTagged` and
selection methods like :meth:`~cadquery.Workplane.edges` can operate on a tagged
:class:`~cadquery.Workplane`. Note that unlike the ``part = part.box(1, 1, 1)`` step where we went
from ``Workplane object at 0x2760`` to ``Workplane object at 0xaa90``, the
:meth:`~cadquery.Workplane.tag` method has returned the same object at ``0xaa90``. This is unusual
for a :class:`~cadquery.Workplane` method.

The next step is::

    part = part.faces(">>Z")

The output is:

.. code-block:: none

    Workplane object at 0x8c40:
      parent: Workplane object at 0xaa90
      plane: Plane object at 0xac40:
        origin: (0.0, 0.0, 0.0)
        z direction: (0.0, 0.0, 1.0)
      objects: [<cadquery.occ_impl.shapes.Face object at 0x3c10>]
      modelling context: CQContext object at 0x2730:
        pendingWires: []
        pendingEdges: []
        tags: {'base': <cadquery.cq.Workplane object at 0xaa90>}

Our selection method has taken the :class:`~cadquery.Solid` from the
:attr:`~cadquery.Workplane.objects` list of the previous :class:`~cadquery.Workplane`, found the
face with its center furthest in the Z direction, and placed that face into the
:attr:`~cadquery.Workplane.objects` attribute. The :class:`~cadquery.Solid` representing the box we
are modelling is gone, and when a :class:`~cadquery.Workplane` method needs to access that solid it
searches through the parent chain for the nearest solid. This action can also be done by a user
through the :meth:`~cadquery.Workplane.findSolid` method.

Now we want to select the boundary of this :class:`~cadquery.Face` (a :class:`~cadquery.Wire`), so
we use::

    part = part.wires()

The output is now:

.. code-block:: none

    Workplane object at 0x6880:
      parent: Workplane object at 0x8c40
      plane: Plane object at 0x38b0:
        origin: (0.0, 0.0, 0.0)
        z direction: (0.0, 0.0, 1.0)
      objects: [<cadquery.occ_impl.shapes.Wire object at 0xaca0>]
      modelling context: CQContext object at 0x2730:
        pendingWires: []
        pendingEdges: []
        tags: {'base': <cadquery.cq.Workplane object at 0xaa90>}

Modelling operations take their wires and edges from the modelling context's pending lists. In order
to use the :meth:`~cadquery.Workplane.loft` command further down the chain, we need to push this wire
to the modelling context with::

    part = part.toPending()

Now we have:

.. code-block:: none

    Workplane object at 0x6880:
      parent: Workplane object at 0x8c40
      plane: Plane object at 0x38b0:
        origin: (0.0, 0.0, 0.0)
        z direction: (0.0, 0.0, 1.0)
      objects: [<cadquery.occ_impl.shapes.Wire object at 0xaca0>]
      modelling context: CQContext object at 0x2730:
        pendingWires: [<cadquery.occ_impl.shapes.Wire object at 0xaca0>]
        pendingEdges: []
        tags: {'base': <cadquery.cq.Workplane object at 0xaa90>}

The :class:`~cadquery.Wire` object that was only in the :attr:`~cadquery.Workplane.objects`
attribute before is now also in the modelling context's :attr:`~cadquery.cq.CQContext.pendingWires`.
The :meth:`~cadquery.Workplane.toPending` method is also another of the unusual methods that return
the same :class:`~cadquery.Workplane` object instead of a new one.

To set up the other side of the :meth:`~cadquery.Workplane.loft` command further down the chain, we
translate the wire in :attr:`~cadquery.Workplane.objects` by calling::

    part = part.translate((0.1, 0.1, 1.0))

Now the string representation of ``part`` looks like:

.. code-block:: none

    Workplane object at 0x3a00:
      parent: Workplane object at 0x6880
      plane: Plane object at 0xac70:
        origin: (0.0, 0.0, 0.0)
        z direction: (0.0, 0.0, 1.0)
      objects: [<cadquery.occ_impl.shapes.Wire object at 0x35e0>]
      modelling context: CQContext object at 0x2730:
        pendingWires: [<cadquery.occ_impl.shapes.Wire object at 0xaca0>]
        pendingEdges: []
        tags: {'base': <cadquery.cq.Workplane object at 0xaa90>}

It may look similar to the previous step, but the :class:`~cadquery.Wire` object in
:attr:`~cadquery.Workplane.objects` is different. To get this wire into the pending wires list,
again we use::

    part = part.toPending()

The result:

.. code-block:: none

    Workplane object at 0x3a00:
      parent: Workplane object at 0x6880
      plane: Plane object at 0xac70:
        origin: (0.0, 0.0, 0.0)
        z direction: (0.0, 0.0, 1.0)
      objects: [<cadquery.occ_impl.shapes.Wire object at 0x35e0>]
      modelling context: CQContext object at 0x2730:
        pendingWires: [<cadquery.occ_impl.shapes.Wire object at 0xaca0>, <cadquery.occ_impl.shapes.Wire object at 0x7f5c7f5c35e0>]
        pendingEdges: []
        tags: {'base': <cadquery.cq.Workplane object at 0xaa90>}

The modelling context's :attr:`~cadquery.cq.CQContext.pendingWires` attribute now contains the two
wires we want to loft between, and we simply call::

    part = part.loft()

After the loft operation, our Workplane looks quite different:

.. code-block:: none

    Workplane object at 0x32b0:
      parent: Workplane object at 0x3a00
      plane: Plane object at 0x3d60:
        origin: (0.0, 0.0, 0.0)
        z direction: (0.0, 0.0, 1.0)
      objects: [<cadquery.occ_impl.shapes.Compound object at 0xad30>]
      modelling context: CQContext object at 0x2730:
        pendingWires: []
        pendingEdges: []
        tags: {'base': <cadquery.cq.Workplane object at 0xaa90>}

In the :attr:`cq.Workplane.objects` attribute we now have one :class:`~cadquery.Compound` object and the modelling
context's :attr:`~cadquery.cq.CQContext.pendingWires` has been cleared by
:meth:`~cadquery.Workplane.loft`.

.. note::
    To inspect the :class:`~cadquery.Compound` object further you can use
    :meth:`~cadquery.Workplane.val` or :meth:`~cadquery.Workplane.findSolid` to get at the
    :class:`~cadquery.Compound` object, then use :meth:`cadquery.Shape.Solids` to return a list
    of the :class:`~cadquery.Solid` objects contained in the :class:`~cadquery.Compound`, which in
    this example will be a single :class:`~cadquery.Solid` object. For example:

.. code-block:: pycon

    >>> a_compound = part.findSolid()
    >>> a_list_of_solids = a_compound.Solids()
    >>> len(a_list_of_solids)
    1

Now we will create a small cylinder protruding from a face on the original box. We need to set up a
workplane to draw a circle on, so firstly we will select the correct face::

    part = part.faces(">>X", tag="base")

Which results in:

.. code-block:: none

    Workplane object at 0x3f10:
      parent: Workplane object at 0x32b0
      plane: Plane object at 0xefa0:
        origin: (0.0, 0.0, 0.0)
        z direction: (0.0, 0.0, 1.0)
      objects: [<cadquery.occ_impl.shapes.Face object at 0x3af0>]
      modelling context: CQContext object at 0x2730:
        pendingWires: []
        pendingEdges: []
        tags: {'base': <cadquery.cq.Workplane object at 0xaa90>}

We have the desired :class:`~cadquery.Face` in the :attr:`~cadquery.Workplane.objects` attribute,
but the :attr:`~cadquery.Workplane.plane` has not changed yet. To create the new plane we use the
:meth:`Workplane.workplane` method::

    part = part.workplane()

Now:

.. code-block:: none

    Workplane object at 0xe700:
      parent: Workplane object at 0x3f10
      plane: Plane object at 0xe730:
        origin: (0.5, 0.0, 0.0)
        z direction: (1.0, 0.0, 0.0)
      objects: []
      modelling context: CQContext object at 0x2730:
        pendingWires: []
        pendingEdges: []
        tags: {'base': <cadquery.cq.Workplane object at 0xaa90>}

The :attr:`~cadquery.Workplane.objects` list has been cleared and the :class:`~cadquery.Plane`
object has a local Z direction in the global X direction. Since the base of the plane is the side of
the box, the origin is offset in the X direction.

Onto this plane we can draw a circle::

    part = part.circle(0.2)

Now:

.. code-block:: none

    Workplane object at 0xe790:
      parent: Workplane object at 0xe700
      plane: Plane object at 0xaf40:
        origin: (0.5, 0.0, 0.0)
        z direction: (1.0, 0.0, 0.0)
      objects: [<cadquery.occ_impl.shapes.Wire object at 0xe610>]
      modelling context: CQContext object at 0x2730:
        pendingWires: [<cadquery.occ_impl.shapes.Wire object at 0xe610>]
        pendingEdges: []
        tags: {'base': <cadquery.cq.Workplane object at 0xaa90>}

The :meth:`~cadquery.Workplane.circle` method - like all 2D drawing methods - has placed the circle
into both the :attr:`~cadquery.Workplane.objects` attribute (where it will be cleared during the
next modelling step), and the modelling context's pending wires (where it will persist until used by
another :class:`~cadquery.Workplane` method).

The next step is to extrude this circle and create a cylindrical protrusion::

    part = part.extrude(1, clean=False)

Now:

.. code-block:: none

    Workplane object at 0xafd0:
      parent: Workplane object at 0xe790
      plane: Plane object at 0x3e80:
        origin: (0.5, 0.0, 0.0)
        z direction: (1.0, 0.0, 0.0)
      objects: [<cadquery.occ_impl.shapes.Compound object at 0xaaf0>]
      modelling context: CQContext object at 0x2730:
        pendingWires: []
        pendingEdges: []
        tags: {'base': <cadquery.cq.Workplane object at 0xaa90>}

The :meth:`~cadquery.Workplane.extrude` method has cleared all the pending wires and edges. The
:attr:`~cadquery.Workplane.objects` attribute contains the final :class:`~cadquery.Compound` object
that is shown in the 3D view above.


.. note::
  The :meth:`~cadquery.Workplane.extrude` has an argument for ``clean`` which defaults to ``True``.
  This extrudes the pending wires (creating a new :class:`~cadquery.Workplane` object), then runs
  the :meth:`~cadquery.Workplane.clean` method to refine the result, creating another
  :class:`~cadquery.Workplane`. If you were to run the example with the default
  ``clean=True`` then you would see an intermediate
  :class:`~cadquery.Workplane` object in :attr:`~cadquery.Workplane.parent`
  rather than the object from the previous step.