Files
cadquery/doc/sketch.rst
AU 6d7d1d640d Sketch and Workplane improvements (#1633)
* Add special methods to Sketch a clean up

* Sketch support in DXF exporter

* Use the new __iter__ interface

* Fix failing tests

* Add some exporter tests

* More tests

* Typo fix

* Fix test

* Bool ops for Sketch

* Use else

* Better annotations

* Implement add/subtract/replace

* Extend _getFaces()

* Annotation tweaks

* Additional operators

* Add deprecations

* Allow 0 spacing

* Implement __iter__

* Decouple exporters and Workplane

* Export methods

* Solve circular import

* More args for export

* Add brep

* Add some tests

* Fix stack searching

* Some tests

* Implement moved overloads for Sketch

* extract get_arity to utils

* Workplane __iter__ tests and tweaks

* Assy export tests

* Fix test

* Coverage tweaks

* Coverage tweaks 2

* Test for Sketch additions

* Black fix

* Make _selection optional

* More tests

* More sketch coverage

* Some doc improvements

* More docs

* Mention special methods for Sketch

* Doc fixes

* Coverage tweaks

* Doc tweaks

* Typo fix

* Spelling

Co-authored-by: Jeremy Wright <wrightjmf@gmail.com>

* Rework cq.py

* Rework sketch.py

* Rephrase

* Set spacing to 0

---------

Co-authored-by: Jeremy Wright <wrightjmf@gmail.com>
2024-08-21 06:07:30 +02:00

379 lines
9.5 KiB
ReStructuredText

.. _sketchtutorial:
******
Sketch
******
Sketch tutorial
---------------
The purpose of this section is to demonstrate how to construct sketches using different
approaches.
Face-based API
==============
The main approach for constructing sketches is based on constructing faces and
combining them using boolean operations.
.. cadquery::
:height: 600px
import cadquery as cq
result = (
cq.Sketch()
.trapezoid(4, 3, 90)
.vertices()
.circle(0.5, mode="s")
.reset()
.vertices()
.fillet(0.25)
.reset()
.rarray(0.6, 1, 5, 1)
.slot(1.5, 0.4, mode="s", angle=90)
)
Note that selectors are implemented, but selection has to be explicitly reset. Sketch
class does not implement history and all modifications happen in-place.
Modes
^^^^^
Every operation from the face API accepts a mode parameter to define
how to combine the created object with existing ones. It can be fused (``mode='a'``),
cut (``mode='s'``), intersected (``mode='i'``), replaced (``mode='r'``)
or just stored for construction (``mode='c'``).
In the last case, it is mandatory to specify a ``tag`` in order to be able to
refer to the object later on. By default faces are fused together.
Note the usage of the subtractive and additive modes in the example above.
The additional two are demonstrated below.
.. cadquery::
:height: 600px
result = (
cq.Sketch()
.rect(1, 2, mode="c", tag="base")
.vertices(tag="base")
.circle(0.7)
.reset()
.edges("|Y", tag="base")
.ellipse(1.2, 1, mode="i")
.reset()
.rect(2, 2, mode="i")
.clean()
)
Edge-based API
==============
If needed, one can construct sketches by placing individual edges.
.. cadquery::
:height: 600px
import cadquery as cq
result = (
cq.Sketch()
.segment((0.0, 0), (0.0, 2.0))
.segment((2.0, 0))
.close()
.arc((0.6, 0.6), 0.4, 0.0, 360.0)
.assemble(tag="face")
.edges("%LINE", tag="face")
.vertices()
.chamfer(0.2)
)
Once the construction is finished it has to be converted to the face-based representation
using :meth:`~cadquery.Sketch.assemble`. Afterwards, face based operations can be applied.
Convex hull
===========
.. warning:: The Convex Hull feature is currently experimental.
For certain special use-cases convex hull can be constructed from straight segments
and circles.
.. cadquery::
:height: 600px
result = (
cq.Sketch()
.arc((0, 0), 1.0, 0.0, 360.0)
.arc((1, 1.5), 0.5, 0.0, 360.0)
.segment((0.0, 2), (-1, 3.0))
.hull()
)
Constraint-based sketches
=========================
.. warning:: The 2D Sketch constraints and solver is currently experimental.
Finally, if desired, geometric constraints can be used to construct sketches. So
far only line segments and arcs can be used in such a use case.
.. cadquery::
:height: 600px
import cadquery as cq
result = (
cq.Sketch()
.segment((0, 0), (0, 3.0), "s1")
.arc((0.0, 3.0), (1.5, 1.5), (0.0, 0.0), "a1")
.constrain("s1", "Fixed", None)
.constrain("s1", "a1", "Coincident", None)
.constrain("a1", "s1", "Coincident", None)
.constrain("s1", "a1", "Angle", 45)
.solve()
.assemble()
)
Following constraints are implemented. Arguments are passed in as one tuple in :meth:`~cadquery.Sketch.constrain`. In this table, `0..1` refers to a float between 0 and 1 where 0 would create a constraint relative to the start of the element, and 1 the end.
.. list-table::
:widths: 15 10 15 30 30
:header-rows: 1
* - Name
- Arity
- Entities
- Arguments
- Description
* - FixedPoint
- 1
- All
- `None` for arc center or `0..1` for point on segment/arc
- Specified point is fixed
* - Coincident
- 2
- All
- None
- Specified points coincide
* - Angle
- 2
- All
- `angle`
- Angle between the tangents of the two entities is fixed
* - Length
- 1
- All
- `length`
- Specified entity has fixed length
* - Distance
- 2
- All
- `None or 0..1, None or 0..1, distance`
- Distance between two points is fixed
* - Radius
- 1
- Arc
- `radius`
- Specified entity has a fixed radius
* - Orientation
- 1
- Segment
- `x,y`
- Specified entity is parallel to `(x,y)`
* - ArcAngle
- 1
- Arc
- `angle`
- Specified entity is fixed angular span
Workplane integration
---------------------
Once created, a sketch can be used to construct various features on a workplane.
Supported operations include :meth:`~cadquery.Workplane.extrude`,
:meth:`~cadquery.Workplane.twistExtrude`, :meth:`~cadquery.Workplane.revolve`,
:meth:`~cadquery.Workplane.sweep`, :meth:`~cadquery.Workplane.cutBlind`, :meth:`~cadquery.Workplane.cutThruAll` and :meth:`~cadquery.Workplane.loft`.
Sketches can be created as separate entities and reused, but also created ad-hoc
in one fluent chain of calls as shown below.
Sketches in-place
=================
Constructing sketches in-place can be accomplished as follows.
.. cadquery::
:height: 600px
import cadquery as cq
result = (
cq.Workplane()
.box(5, 5, 1)
.faces(">Z")
.sketch()
.regularPolygon(2, 3, tag="outer")
.regularPolygon(1.5, 3, mode="s")
.vertices(tag="outer")
.fillet(0.2)
.finalize()
.extrude(0.5)
)
Sketch API is available after the :meth:`~cadquery.Workplane.sketch` call and original `workplane`.
Placing an existing sketch on a workplane
=========================================
Sometimes it is desired to place an existing sketches as-is on a workplane. This can be done with :meth:`~cadquery.Workplane.placeSketch`
.. cadquery::
:height: 600px
import cadquery as cq
s = cq.Sketch().trapezoid(3, 1, 110).vertices().fillet(0.2)
result = (
cq.Workplane()
.box(5, 5, 5)
.faces(">X")
.workplane()
.transformed((0, 0, -90))
.placeSketch(s)
.cutThruAll()
)
Sketches spanning multiple elements
===================================
When multiple elements are selected before constructing the sketch, multiple sketches will be created.
Note that the sketch is placed on all locations that are on the top of the stack.
.. cadquery::
:height: 600px
import cadquery as cq
result = (
cq.Workplane()
.box(5, 5, 1)
.faces(">Z")
.workplane()
.rarray(2, 2, 2, 2)
.rect(1.5, 1.5)
.extrude(0.5)
.faces(">Z")
.sketch()
.circle(0.4)
.wires()
.distribute(6)
.circle(0.1, mode="a")
.clean()
.finalize()
.cutBlind(-0.5, taper=10)
)
Lofting between two sketches
============================
Two sketches on different workplanes are needed when using :meth:`~cadquery.Workplane.loft`.
.. cadquery::
:height: 600px
from cadquery import Workplane, Sketch, Vector, Location
s1 = Sketch().trapezoid(3, 1, 110).vertices().fillet(0.2)
s2 = Sketch().rect(2, 1).vertices().fillet(0.2)
result = Workplane().placeSketch(s1, s2.moved(z=3)).loft()
When lofting only outer wires are taken into account and inner wires are silently ignored. Note that only sketches on the top of stack are considered for the current operation (i.e. there are no pending sketches), so when lofting or sweeping all relevant sketches have to be added in one `placeSketch` call.
Combining sketches
==================
Sketches can be combined using :meth:`~cadquery.Sketch.face`.
.. cadquery::
:height: 600px
import cadquery as cq
s1 = cq.Sketch().rect(2, 2)
s2 = cq.Sketch().circle(0.5)
result = s1.face(s2, mode='s')
It is also possible to use boolean operations to achieve the same effect.
.. cadquery::
:height: 600px
import cadquery as cq
s1 = cq.Sketch().rect(2, 2).vertices().fillet(0.25).reset()
s2 = cq.Sketch().rect(1, 1, angle=45).vertices().chamfer(0.1).reset()
result = s1 - s2
Boolean operations are selection sensitive, so in this example
:meth:`~cadquery.Sketch.reset` call is needed.
Offsets made easy
=================
Conveniently, it is possible to reuse a sketch to create an :meth:`~cadquery.Sketch.offset` shape.
.. cadquery::
:height: 600px
import cadquery as cq
sketch = (cq.Sketch()
.rect(1.0, 4.0)
.circle(1.0)
.clean()
)
sketch_offset = sketch.copy().wires().offset(0.25)
result = cq.Workplane("front").placeSketch(sketch_offset).extrude(1.0)
result = result.faces(">Z").workplane().placeSketch(sketch).cutBlind(-0.50)
It is obviously possible to use negative offsets, but it requires being more careful with the mode
of the offset operation. Usually one wants to replace the original face, hence ``mode='r'``.
.. cadquery::
:height: 600px
import cadquery as cq
sketch = (cq.Sketch()
.rect(1.0, 4.0)
.circle(1.0)
.clean()
)
sketch_offset = sketch.copy().wires().offset(-0.25, mode='r')
result = cq.Workplane("front").placeSketch(sketch).extrude(1.0)
result = result.faces(">Z").workplane().placeSketch(sketch_offset).cutBlind(-0.50)
Exporting and importing
=======================
It is possible to export sketches using :meth:`~cadquery.Sketch.export`.
See :ref:`importexport` for more details.
Importing of DXF files is supported as well using :meth:`~cadquery.Sketch.importDXF`.