Add new docs section "Selectors and Operators"
- Expand ideas and concepts from key concepts section - Provide minimal usage examples in front matter - Add structure for examples of low to medium complexity for different criteria types
BIN
docs/assets/selectors_operators/filter_all_edges_circle.png
Normal file
|
After Width: | Height: | Size: 48 KiB |
BIN
docs/assets/selectors_operators/filter_axisplane.png
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
docs/assets/selectors_operators/filter_dot_axisplane.png
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
docs/assets/selectors_operators/filter_geomtype_cylinder.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
docs/assets/selectors_operators/filter_geomtype_line.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
docs/assets/selectors_operators/filter_inner_wire_count.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 44 KiB |
BIN
docs/assets/selectors_operators/filter_nested.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
docs/assets/selectors_operators/filter_shape_properties.png
Normal file
|
After Width: | Height: | Size: 70 KiB |
BIN
docs/assets/selectors_operators/group_axis_with.png
Normal file
|
After Width: | Height: | Size: 74 KiB |
BIN
docs/assets/selectors_operators/group_axis_without.png
Normal file
|
After Width: | Height: | Size: 60 KiB |
BIN
docs/assets/selectors_operators/group_hole_area.png
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
docs/assets/selectors_operators/group_length_key.png
Normal file
|
After Width: | Height: | Size: 111 KiB |
BIN
docs/assets/selectors_operators/group_radius_key.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
docs/assets/selectors_operators/operators_filter_z_normal.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
docs/assets/selectors_operators/operators_group_area.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
docs/assets/selectors_operators/operators_sort_x.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
docs/assets/selectors_operators/selectors_select_all.png
Normal file
|
After Width: | Height: | Size: 68 KiB |
BIN
docs/assets/selectors_operators/selectors_select_last.png
Normal file
|
After Width: | Height: | Size: 48 KiB |
BIN
docs/assets/selectors_operators/selectors_select_new.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
docs/assets/selectors_operators/selectors_select_new_fillet.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
docs/assets/selectors_operators/selectors_select_new_none.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
docs/assets/selectors_operators/sort_along_wire.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
docs/assets/selectors_operators/sort_axis.png
Normal file
|
After Width: | Height: | Size: 88 KiB |
BIN
docs/assets/selectors_operators/sort_distance_from_largest.png
Normal file
|
After Width: | Height: | Size: 60 KiB |
BIN
docs/assets/selectors_operators/sort_distance_from_origin.png
Normal file
|
After Width: | Height: | Size: 60 KiB |
BIN
docs/assets/selectors_operators/sort_not_along_wire.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
docs/assets/selectors_operators/sort_sortby_distance.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
docs/assets/selectors_operators/sort_sortby_length.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 43 KiB |
BIN
docs/assets/selectors_operators/thumb_filter_axisplane.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
docs/assets/selectors_operators/thumb_filter_geomtype.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 25 KiB |
BIN
docs/assets/selectors_operators/thumb_filter_nested.png
Normal file
|
After Width: | Height: | Size: 6.8 KiB |
|
After Width: | Height: | Size: 48 KiB |
BIN
docs/assets/selectors_operators/thumb_group_axis.png
Normal file
|
After Width: | Height: | Size: 57 KiB |
BIN
docs/assets/selectors_operators/thumb_group_hole_area.png
Normal file
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 45 KiB |
BIN
docs/assets/selectors_operators/thumb_sort_along_wire.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
docs/assets/selectors_operators/thumb_sort_axis.png
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
docs/assets/selectors_operators/thumb_sort_distance.png
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
docs/assets/selectors_operators/thumb_sort_sortby.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
|
|
@ -113,6 +113,7 @@ Table Of Contents
|
||||||
introductory_examples.rst
|
introductory_examples.rst
|
||||||
tutorials.rst
|
tutorials.rst
|
||||||
objects.rst
|
objects.rst
|
||||||
|
selectors_operators.rst
|
||||||
operations.rst
|
operations.rst
|
||||||
builders.rst
|
builders.rst
|
||||||
joints.rst
|
joints.rst
|
||||||
|
|
|
||||||
393
docs/selectors_operators.rst
Normal file
|
|
@ -0,0 +1,393 @@
|
||||||
|
#######################
|
||||||
|
Selectors and Operators
|
||||||
|
#######################
|
||||||
|
|
||||||
|
Selectors and operators are powerful methods to select and organize CAD objects for
|
||||||
|
subsequent operations.
|
||||||
|
|
||||||
|
.. _selectors:
|
||||||
|
|
||||||
|
*********
|
||||||
|
Selectors
|
||||||
|
*********
|
||||||
|
|
||||||
|
Selectors provide methods to extract all or a subset of a feature type in the referenced
|
||||||
|
object. These methods select Edges, Faces, Solids, Vertices, or Wires in Builder objects
|
||||||
|
or from Shape objects themselves. All of these methods return a :class:`~topology.ShapeList`,
|
||||||
|
which is a subclass of ``list`` and may be sorted, grouped, or filtered by
|
||||||
|
:ref:`operators`.
|
||||||
|
|
||||||
|
Overview
|
||||||
|
========
|
||||||
|
|
||||||
|
+--------------+----------------+-----------------------------------------------+-----------------------+
|
||||||
|
| Selector | Criteria | Applicability | Description |
|
||||||
|
+==============+================+===============================================+=======================+
|
||||||
|
| |vertices| | ALL, LAST | ``BuildLine``, ``BuildSketch``, ``BuildPart`` | ``Vertex`` extraction |
|
||||||
|
+--------------+----------------+-----------------------------------------------+-----------------------+
|
||||||
|
| |edges| | ALL, LAST, NEW | ``BuildLine``, ``BuildSketch``, ``BuildPart`` | ``Edge`` extraction |
|
||||||
|
+--------------+----------------+-----------------------------------------------+-----------------------+
|
||||||
|
| |wires| | ALL, LAST | ``BuildLine``, ``BuildSketch``, ``BuildPart`` | ``Wire`` extraction |
|
||||||
|
+--------------+----------------+-----------------------------------------------+-----------------------+
|
||||||
|
| |faces| | ALL, LAST | ``BuildSketch``, ``BuildPart`` | ``Face`` extraction |
|
||||||
|
+--------------+----------------+-----------------------------------------------+-----------------------+
|
||||||
|
| |solids| | ALL, LAST | ``BuildPart`` | ``Solid`` extraction |
|
||||||
|
+--------------+----------------+-----------------------------------------------+-----------------------+
|
||||||
|
|
||||||
|
Both shape objects and builder objects have access to selector methods to select all of
|
||||||
|
a feature as long as they can contain the feature being selected.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
# In context
|
||||||
|
with BuildSketch() as context:
|
||||||
|
Rectangle(1, 1)
|
||||||
|
context.edges()
|
||||||
|
|
||||||
|
# Build context implicitly has access to the selector
|
||||||
|
edges()
|
||||||
|
|
||||||
|
# Taking the sketch out of context
|
||||||
|
context.sketch.edges()
|
||||||
|
|
||||||
|
# Create sketch out of context
|
||||||
|
Rectangle(1, 1).edges()
|
||||||
|
|
||||||
|
Select In Build Objects
|
||||||
|
========================
|
||||||
|
|
||||||
|
Build objects track the last operation and their selector methods can take
|
||||||
|
:class:`~build_enums.Select` as criteria to specify a subset of
|
||||||
|
features to extract. By default, a selector will select ``ALL`` of a feature, while
|
||||||
|
``LAST`` selects features created or altered by the most recent operation. |edges| can
|
||||||
|
uniquely specify ``NEW`` to only select edges created in the last operation which neither
|
||||||
|
existed in the referenced object before the last operation, nor the modifying object.
|
||||||
|
|
||||||
|
.. important::
|
||||||
|
|
||||||
|
:class:`~build_enums.Select` as selector criteria is only valid for builder objects!
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
# In context
|
||||||
|
with BuildPart() as context:
|
||||||
|
Box(2, 2, 1)
|
||||||
|
Cylinder(1, 2)
|
||||||
|
context.edges(Select.LAST)
|
||||||
|
|
||||||
|
# Does not work out of context!
|
||||||
|
context.part.edges(Select.LAST)
|
||||||
|
(Box(2, 2, 1) + Cylinder(1, 2)).edges(Select.LAST)
|
||||||
|
|
||||||
|
Create a simple part to demonstrate selectors. Select using the default criteria
|
||||||
|
``Select.ALL``. Specifying ``Select.ALL`` for the selector is not required.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
with BuildPart() as part:
|
||||||
|
Box(5, 5, 1)
|
||||||
|
Cylinder(1, 5)
|
||||||
|
|
||||||
|
part.vertices()
|
||||||
|
part.edges()
|
||||||
|
part.faces()
|
||||||
|
|
||||||
|
# Is the same as
|
||||||
|
part.vertices(Select.ALL)
|
||||||
|
part.edges(Select.ALL)
|
||||||
|
part.faces(Select.ALL)
|
||||||
|
|
||||||
|
.. figure:: assets/selectors_operators/selectors_select_all.png
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
The default ``Select.ALL`` features
|
||||||
|
|
||||||
|
Select features changed in the last operation with criteria ``Select.LAST``.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
with BuildPart() as part:
|
||||||
|
Box(5, 5, 1)
|
||||||
|
Cylinder(1, 5)
|
||||||
|
|
||||||
|
part.vertices(Select.LAST)
|
||||||
|
part.edges(Select.LAST)
|
||||||
|
part.faces(Select.LAST)
|
||||||
|
|
||||||
|
.. figure:: assets/selectors_operators/selectors_select_last.png
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
``Select.LAST`` features
|
||||||
|
|
||||||
|
Select only new edges from the last operation with ``Select.NEW``. This option is only
|
||||||
|
available for a ``ShapeList`` of edges!
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
with BuildPart() as part:
|
||||||
|
Box(5, 5, 1)
|
||||||
|
Cylinder(1, 5)
|
||||||
|
|
||||||
|
part.edges(Select.NEW)
|
||||||
|
|
||||||
|
.. figure:: assets/selectors_operators/selectors_select_new.png
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
``Select.NEW`` edges where box and cylinder intersect
|
||||||
|
|
||||||
|
This only returns new edges which are not reused from Box or Cylinder, in this case where
|
||||||
|
the objects `intersect`. But what happens if the objects don't intersect and all the
|
||||||
|
edges are reused?
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
with BuildPart() as part:
|
||||||
|
Box(5, 5, 1, align=(Align.CENTER, Align.CENTER, Align.MAX))
|
||||||
|
Cylinder(2, 2, align=(Align.CENTER, Align.CENTER, Align.MIN))
|
||||||
|
|
||||||
|
part.edges(Select.NEW)
|
||||||
|
|
||||||
|
.. figure:: assets/selectors_operators/selectors_select_new_none.png
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
``Select.NEW`` edges when box and cylinder don't intersect
|
||||||
|
|
||||||
|
No edges are selected! Unlike the previous example, the Edge between the Box and Cylinder
|
||||||
|
objects is an edge reused from the Cylinder. Think of ``Select.NEW`` as a way to select
|
||||||
|
only completely new edges created by the operation.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
Chamfer and fillet modify the current object, but do not have new edges.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
with BuildPart() as part:
|
||||||
|
Box(5, 5, 1)
|
||||||
|
Cylinder(1, 5)
|
||||||
|
edges = part.edges().filter_by(lambda a: a.length == 1)
|
||||||
|
fillet(edges, 1)
|
||||||
|
|
||||||
|
part.edges(Select.NEW)
|
||||||
|
|
||||||
|
.. figure:: assets/selectors_operators/selectors_select_new_fillet.png
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
Left, ``Select.NEW`` returns no edges after fillet. Right, ``Select.LAST``
|
||||||
|
|
||||||
|
.. _operators:
|
||||||
|
|
||||||
|
*********
|
||||||
|
Operators
|
||||||
|
*********
|
||||||
|
|
||||||
|
Operators provide methods refine a ``ShapeList`` of features isolated by a *selector* to
|
||||||
|
further specify feature(s). These methods can sort, group, or filter ``ShapeList``
|
||||||
|
objects and return a modified ``ShapeList``, or in the case of |group_by|, ``GroupBy``,
|
||||||
|
a list of ``ShapeList`` objects accessible by index or key.
|
||||||
|
|
||||||
|
Overview
|
||||||
|
========
|
||||||
|
|
||||||
|
+----------------------+------------------------------------------------------------------+-------------------------------------------------------+
|
||||||
|
| Method | Criteria | Description |
|
||||||
|
+======================+==================================================================+=======================================================+
|
||||||
|
| |sort_by| | ``Axis``, ``Edge``, ``Wire``, ``SortBy``, callable, property | Sort ``ShapeList`` by criteria |
|
||||||
|
+----------------------+------------------------------------------------------------------+-------------------------------------------------------+
|
||||||
|
| |sort_by_distance| | ``Shape``, ``VectorLike`` | Sort ``ShapeList`` by distance from criteria |
|
||||||
|
+----------------------+------------------------------------------------------------------+-------------------------------------------------------+
|
||||||
|
| |group_by| | ``Axis``, ``Edge``, ``Wire``, ``SortBy``, callable, property | Group ``ShapeList`` by criteria |
|
||||||
|
+----------------------+------------------------------------------------------------------+-------------------------------------------------------+
|
||||||
|
| |filter_by| | ``Axis``, ``Plane``, ``GeomType``, ``ShapePredicate``, property | Filter ``ShapeList`` by criteria |
|
||||||
|
+----------------------+------------------------------------------------------------------+-------------------------------------------------------+
|
||||||
|
| |filter_by_position| | ``Axis`` | Filter ``ShapeList`` by ``Axis`` & mix / max values |
|
||||||
|
+----------------------+------------------------------------------------------------------+-------------------------------------------------------+
|
||||||
|
|
||||||
|
Operator methods take criteria to refine ``ShapeList``. Broadly speaking, the criteria
|
||||||
|
fall into the following categories, though not all operators take all criteria:
|
||||||
|
|
||||||
|
- Geometric objects: ``Axis``, ``Plane``
|
||||||
|
- Topological objects: ``Edge``, ``Wire``
|
||||||
|
- Enums: :class:`~build_enums.SortBy`, :class:`~build_enums.GeomType`
|
||||||
|
- Properties, eg: ``Face.area``, ``Edge.length``
|
||||||
|
- ``ShapePredicate``, eg: ``lambda e: e.is_interior == 1``, ``lambda f: lf.edges() >= 3``
|
||||||
|
- Callable eg: ``Vertex().distance``
|
||||||
|
|
||||||
|
Sort
|
||||||
|
=======
|
||||||
|
|
||||||
|
A ``ShapeList`` can be sorted with the |sort_by| and |sort_by_distance|
|
||||||
|
methods based on a sorting criteria. Sorting is a critical step when isolating individual
|
||||||
|
features as a ``ShapeList`` from a selector is typically unordered.
|
||||||
|
|
||||||
|
Here we want to capture some vertices from the object furthest along ``X``: All the
|
||||||
|
vertices are first captured with the |vertices| selector, then sort by ``Axis.X``.
|
||||||
|
Finally, the vertices can be captured with a list slice for the last 4 list items, as the
|
||||||
|
items are sorted from least to greatest ``X`` position. Remember, ``ShapeList`` is a
|
||||||
|
subclass of ``list``, so any list slice can be used.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
part.vertices().sort_by(Axis.X)[-4:]
|
||||||
|
|
||||||
|
.. figure:: assets/selectors_operators/operators_sort_x.png
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
|
|
||||||
|
|
||||||
|
Examples
|
||||||
|
--------
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 2
|
||||||
|
:hidden:
|
||||||
|
|
||||||
|
selectors_operators/sort_examples
|
||||||
|
|
||||||
|
.. grid:: 3
|
||||||
|
:gutter: 3
|
||||||
|
|
||||||
|
.. grid-item-card:: SortBy
|
||||||
|
:img-top: assets/selectors_operators/thumb_sort_sortby.png
|
||||||
|
:link: sort_sortby
|
||||||
|
:link-type: ref
|
||||||
|
|
||||||
|
.. grid-item-card:: Along Wire
|
||||||
|
:img-top: assets/selectors_operators/thumb_sort_along_wire.png
|
||||||
|
:link: sort_along_wire
|
||||||
|
:link-type: ref
|
||||||
|
|
||||||
|
.. grid-item-card:: Axis
|
||||||
|
:img-top: assets/selectors_operators/thumb_sort_axis.png
|
||||||
|
:link: sort_axis
|
||||||
|
:link-type: ref
|
||||||
|
|
||||||
|
.. grid-item-card:: Distance From
|
||||||
|
:img-top: assets/selectors_operators/thumb_sort_distance.png
|
||||||
|
:link: sort_distance_from
|
||||||
|
:link-type: ref
|
||||||
|
|
||||||
|
Group
|
||||||
|
========
|
||||||
|
|
||||||
|
A ShapeList can be grouped and sorted with the |group_by| method based on a grouping
|
||||||
|
criteria. Grouping can be a great way to organize features without knowing the values of
|
||||||
|
specific feature properties. Rather than returning a ``Shapelist``, |group_by| returns
|
||||||
|
a ``GroupBy`` which can be indexed to retrieve a ``Shapelist`` for further operations.
|
||||||
|
``GroupBy`` groups can also be accessed using a key with the ``group`` method. If the
|
||||||
|
keys are unknown they can be discovered with ``key_to_group_index``.
|
||||||
|
|
||||||
|
If we want only the edges from the smallest faces by area we can get the faces, then
|
||||||
|
group by ``SortBy.AREA``. The ``ShapeList`` of smallest faces is available from the first
|
||||||
|
list index. Finally, a ``ShapeList`` has access to selectors, so calling |edges| will
|
||||||
|
return a new list of all edges in the previous list.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
part.faces().group_by(SortBy.AREA)[0].edges())
|
||||||
|
|
||||||
|
.. figure:: assets/selectors_operators/operators_group_area.png
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
|
|
||||||
|
|
||||||
|
Examples
|
||||||
|
--------
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 2
|
||||||
|
:hidden:
|
||||||
|
|
||||||
|
selectors_operators/group_examples
|
||||||
|
|
||||||
|
.. grid:: 3
|
||||||
|
:gutter: 3
|
||||||
|
|
||||||
|
.. grid-item-card:: Axis and Length
|
||||||
|
:img-top: assets/selectors_operators/thumb_group_axis.png
|
||||||
|
:link: group_axis
|
||||||
|
:link-type: ref
|
||||||
|
|
||||||
|
.. grid-item-card:: Hole Area
|
||||||
|
:img-top: assets/selectors_operators/thumb_group_hole_area.png
|
||||||
|
:link: group_hole_area
|
||||||
|
:link-type: ref
|
||||||
|
|
||||||
|
.. grid-item-card:: Properties with Keys
|
||||||
|
:img-top: assets/selectors_operators/thumb_group_properties_with_keys.png
|
||||||
|
:link: group_properties_with_keys
|
||||||
|
:link-type: ref
|
||||||
|
|
||||||
|
Filter
|
||||||
|
=========
|
||||||
|
|
||||||
|
A ``ShapeList`` can be filtered with the |filter_by| and |filter_by_position| methods based
|
||||||
|
on a filtering criteria. Filters are flexible way to isolate (or exclude) features based
|
||||||
|
on known criteria.
|
||||||
|
|
||||||
|
Lets say we need all the faces with a normal in the ``+Z`` direction. One way to do this
|
||||||
|
might be with a list comprehension, however |filter_by| has the capability to take a
|
||||||
|
lambda function as a filter condition on the entire list. In this case, the normal of
|
||||||
|
each face can be checked against a vector direction and filtered accordingly.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
part.faces().filter_by(lambda f: f.normal_at() == Vector(0, 0, 1))
|
||||||
|
|
||||||
|
.. figure:: assets/selectors_operators/operators_filter_z_normal.png
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
|
|
||||||
|
|
||||||
|
Examples
|
||||||
|
--------
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 2
|
||||||
|
:hidden:
|
||||||
|
|
||||||
|
selectors_operators/filter_examples
|
||||||
|
|
||||||
|
.. grid:: 3
|
||||||
|
:gutter: 3
|
||||||
|
|
||||||
|
.. grid-item-card:: GeomType
|
||||||
|
:img-top: assets/selectors_operators/thumb_filter_geomtype.png
|
||||||
|
:link: filter_geomtype
|
||||||
|
:link-type: ref
|
||||||
|
|
||||||
|
.. grid-item-card:: All Edges Circle
|
||||||
|
:img-top: assets/selectors_operators/thumb_filter_all_edges_circle.png
|
||||||
|
:link: filter_all_edges_circle
|
||||||
|
:link-type: ref
|
||||||
|
|
||||||
|
.. grid-item-card:: Axis and Plane
|
||||||
|
:img-top: assets/selectors_operators/thumb_filter_axisplane.png
|
||||||
|
:link: filter_axis_plane
|
||||||
|
:link-type: ref
|
||||||
|
|
||||||
|
.. grid-item-card:: Inner Wire Count
|
||||||
|
:img-top: assets/selectors_operators/thumb_filter_inner_wire_count.png
|
||||||
|
:link: filter_inner_wire_count
|
||||||
|
:link-type: ref
|
||||||
|
|
||||||
|
.. grid-item-card:: Nested Filters
|
||||||
|
:img-top: assets/selectors_operators/thumb_filter_nested.png
|
||||||
|
:link: filter_nested
|
||||||
|
:link-type: ref
|
||||||
|
|
||||||
|
.. grid-item-card:: Shape Properties
|
||||||
|
:img-top: assets/selectors_operators/thumb_filter_shape_properties.png
|
||||||
|
:link: filter_shape_properties
|
||||||
|
:link-type: ref
|
||||||
|
|
||||||
|
.. |vertices| replace:: :meth:`~topology.Shape.vertices`
|
||||||
|
.. |edges| replace:: :meth:`~topology.Shape.edges`
|
||||||
|
.. |wires| replace:: :meth:`~topology.Shape.wires`
|
||||||
|
.. |faces| replace:: :meth:`~topology.Shape.faces`
|
||||||
|
.. |solids| replace:: :meth:`~topology.Shape.solids`
|
||||||
|
.. |sort_by| replace:: :meth:`~topology.ShapeList.sort_by`
|
||||||
|
.. |sort_by_distance| replace:: :meth:`~topology.ShapeList.sort_by_distance`
|
||||||
|
.. |group_by| replace:: :meth:`~topology.ShapeList.group_by`
|
||||||
|
.. |filter_by| replace:: :meth:`~topology.ShapeList.filter_by`
|
||||||
|
.. |filter_by_position| replace:: :meth:`~topology.ShapeList.filter_by_position`
|
||||||
50
docs/selectors_operators/examples/filter_all_edges_circle.py
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
import os
|
||||||
|
|
||||||
|
from build123d import *
|
||||||
|
from ocp_vscode import *
|
||||||
|
|
||||||
|
working_path = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
filedir = os.path.join(working_path, "..", "..", "assets", "selectors_operators")
|
||||||
|
|
||||||
|
with BuildPart() as part:
|
||||||
|
with BuildSketch() as s:
|
||||||
|
Rectangle(115, 50)
|
||||||
|
with Locations((5 / 2, 0)):
|
||||||
|
SlotOverall(90, 12, mode=Mode.SUBTRACT)
|
||||||
|
extrude(amount=15)
|
||||||
|
|
||||||
|
with BuildSketch(Plane.XZ.offset(50 / 2)) as s3:
|
||||||
|
with Locations((-115 / 2 + 26, 15)):
|
||||||
|
SlotOverall(42 + 2 * 26 + 12, 2 * 26, rotation=90)
|
||||||
|
zz = extrude(amount=-12)
|
||||||
|
split(bisect_by=Plane.XY)
|
||||||
|
edgs = part.part.edges().filter_by(Axis.Y).group_by(Axis.X)[-2]
|
||||||
|
fillet(edgs, 9)
|
||||||
|
|
||||||
|
with Locations(zz.faces().sort_by(Axis.Y)[0]):
|
||||||
|
with Locations((42 / 2 + 6, 0)):
|
||||||
|
CounterBoreHole(24 / 2, 34 / 2, 4)
|
||||||
|
mirror(about=Plane.XZ)
|
||||||
|
|
||||||
|
with BuildSketch() as s4:
|
||||||
|
RectangleRounded(115, 50, 6)
|
||||||
|
extrude(amount=80, mode=Mode.INTERSECT)
|
||||||
|
# fillet does not work right, mode intersect is safer
|
||||||
|
|
||||||
|
with BuildSketch(Plane.YZ) as s4:
|
||||||
|
with BuildLine() as bl:
|
||||||
|
l1 = Line((0, 0), (18 / 2, 0))
|
||||||
|
l2 = PolarLine(l1 @ 1, 8, 60, length_mode=LengthMode.VERTICAL)
|
||||||
|
l3 = Line(l2 @ 1, (0, 8))
|
||||||
|
mirror(about=Plane.YZ)
|
||||||
|
make_face()
|
||||||
|
extrude(amount=115 / 2, both=True, mode=Mode.SUBTRACT)
|
||||||
|
|
||||||
|
faces = part.faces().filter_by(
|
||||||
|
lambda f: all(e.geom_type == GeomType.CIRCLE for e in f.edges())
|
||||||
|
)
|
||||||
|
for i, f in enumerate(faces):
|
||||||
|
RigidJoint(f"bearing_bore_{i}", joint_location=f.center_location)
|
||||||
|
|
||||||
|
show(part, [f.translate(f.normal_at() * 0.01) for f in faces], render_joints=True)
|
||||||
|
save_screenshot(os.path.join(filedir, "filter_all_edges_circle.png"))
|
||||||
47
docs/selectors_operators/examples/filter_axisplane.py
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
import os
|
||||||
|
|
||||||
|
from build123d import *
|
||||||
|
from ocp_vscode import *
|
||||||
|
|
||||||
|
working_path = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
filedir = os.path.join(working_path, "..", "..", "assets", "selectors_operators")
|
||||||
|
|
||||||
|
axis = Axis.Z
|
||||||
|
plane = Plane.XY
|
||||||
|
with BuildPart() as part:
|
||||||
|
with BuildSketch(Plane.XY.shift_origin((1, 1))) as plane_rep:
|
||||||
|
Rectangle(2, 2)
|
||||||
|
with Locations((-.9, -.9)):
|
||||||
|
Text("Plane.XY", .2, align=(Align.MIN, Align.MIN), mode=Mode.SUBTRACT)
|
||||||
|
plane_rep = plane_rep.sketch
|
||||||
|
plane_rep.color = Color(0, .55, .55, .1)
|
||||||
|
|
||||||
|
with Locations((-1, -1, 0)):
|
||||||
|
b = Box(1, 1, 1)
|
||||||
|
f = b.faces()
|
||||||
|
res = f.filter_by(axis)
|
||||||
|
axis_rep = [Axis(f.center(), f.normal_at()) for f in res]
|
||||||
|
show_object([b, res, axis_rep])
|
||||||
|
|
||||||
|
with Locations((1, 1, 0)):
|
||||||
|
b = Box(1, 1, 1)
|
||||||
|
f = b.faces()
|
||||||
|
res = f.filter_by(plane)
|
||||||
|
show_object([b, res, plane_rep])
|
||||||
|
|
||||||
|
save_screenshot(os.path.join(filedir, "filter_axisplane.png"))
|
||||||
|
reset_show()
|
||||||
|
|
||||||
|
with Locations((-1, -1, 0)):
|
||||||
|
b = Box(1, 1, 1)
|
||||||
|
f = b.faces()
|
||||||
|
res = f.filter_by(lambda f: abs(f.normal_at().dot(axis.direction)) < 1e-6)
|
||||||
|
show_object([b, res, axis_rep])
|
||||||
|
|
||||||
|
with Locations((1, 1, 0)):
|
||||||
|
b = Box(1, 1, 1)
|
||||||
|
f = b.faces()
|
||||||
|
res = f.filter_by(lambda f: abs(f.normal_at().dot(plane.z_dir)) < 1e-6)
|
||||||
|
show_object([b, res, plane_rep])
|
||||||
|
|
||||||
|
save_screenshot(os.path.join(filedir, "filter_dot_axisplane.png"))
|
||||||
23
docs/selectors_operators/examples/filter_geomtype.py
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
import os
|
||||||
|
|
||||||
|
from build123d import *
|
||||||
|
from ocp_vscode import *
|
||||||
|
|
||||||
|
working_path = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
filedir = os.path.join(working_path, "..", "..", "assets", "selectors_operators")
|
||||||
|
|
||||||
|
with BuildPart() as part:
|
||||||
|
Box(5, 5, 1)
|
||||||
|
Cylinder(2, 5)
|
||||||
|
edges = part.edges().filter_by(lambda a: a.length == 1)
|
||||||
|
fillet(edges, 1)
|
||||||
|
|
||||||
|
part.edges().filter_by(GeomType.LINE)
|
||||||
|
|
||||||
|
part.faces().filter_by(GeomType.CYLINDER)
|
||||||
|
|
||||||
|
show(part, part.edges().filter_by(GeomType.LINE))
|
||||||
|
save_screenshot(os.path.join(filedir, "filter_geomtype_line.png"))
|
||||||
|
|
||||||
|
show(part, part.faces().filter_by(GeomType.CYLINDER))
|
||||||
|
save_screenshot(os.path.join(filedir, "filter_geomtype_cylinder.png"))
|
||||||
38
docs/selectors_operators/examples/filter_inner_wire_count.py
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
from copy import copy
|
||||||
|
import os
|
||||||
|
|
||||||
|
from build123d import *
|
||||||
|
from ocp_vscode import *
|
||||||
|
|
||||||
|
working_path = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
filedir = os.path.join(working_path, "..", "..", "assets", "selectors_operators")
|
||||||
|
|
||||||
|
bracket = import_step(os.path.join(working_path, "nema-17-bracket.step"))
|
||||||
|
faces = bracket.faces()
|
||||||
|
|
||||||
|
motor_mounts = faces.filter_by(GeomType.CYLINDER).filter_by(lambda f: f.radius == 3.3/2)
|
||||||
|
for i, f in enumerate(motor_mounts):
|
||||||
|
location = f.axis_of_rotation.location
|
||||||
|
RigidJoint(f"motor_m3_{i}", bracket, joint_location=location)
|
||||||
|
|
||||||
|
motor_face = faces.filter_by(lambda f: len(f.inner_wires()) == 5).sort_by(Axis.X)[-1]
|
||||||
|
motor_bore = motor_face.inner_wires().edges().filter_by(lambda e: e.radius == 16).edge()
|
||||||
|
location = Location(motor_bore.arc_center, motor_bore.normal() * 90, Intrinsic.YXZ)
|
||||||
|
RigidJoint(f"motor", bracket, joint_location=location)
|
||||||
|
|
||||||
|
before_linear = copy(bracket)
|
||||||
|
|
||||||
|
mount_face = faces.filter_by(lambda f: len(f.inner_wires()) == 6).sort_by(Axis.Z)[-1]
|
||||||
|
mount_slots = mount_face.inner_wires().edges().filter_by(GeomType.CIRCLE)
|
||||||
|
joint_edges = [
|
||||||
|
Line(mount_slots[i].arc_center, mount_slots[i + 1].arc_center)
|
||||||
|
for i in range(0, len(mount_slots), 2)
|
||||||
|
]
|
||||||
|
for i, e in enumerate(joint_edges):
|
||||||
|
LinearJoint(f"mount_m4_{i}", bracket, axis=Axis(e), linear_range=(0, e.length / 2))
|
||||||
|
|
||||||
|
show(before_linear, render_joints=True)
|
||||||
|
save_screenshot(os.path.join(filedir, "filter_inner_wire_count.png"))
|
||||||
|
|
||||||
|
show(bracket, render_joints=True)
|
||||||
|
save_screenshot(os.path.join(filedir, "filter_inner_wire_count_linear.png"))
|
||||||
39
docs/selectors_operators/examples/filter_nested.py
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
from copy import copy
|
||||||
|
import os
|
||||||
|
|
||||||
|
from build123d import *
|
||||||
|
from ocp_vscode import *
|
||||||
|
|
||||||
|
working_path = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
filedir = os.path.join(working_path, "..", "..", "assets", "selectors_operators")
|
||||||
|
|
||||||
|
with BuildPart() as part:
|
||||||
|
Cylinder(15, 2, align=(Align.CENTER, Align.CENTER, Align.MIN))
|
||||||
|
with BuildSketch():
|
||||||
|
RectangleRounded(10, 10, 2.5)
|
||||||
|
extrude(amount=15)
|
||||||
|
|
||||||
|
with BuildSketch():
|
||||||
|
Circle(2.5)
|
||||||
|
Rectangle(4, 5, mode=Mode.INTERSECT)
|
||||||
|
extrude(amount=15, mode=Mode.SUBTRACT)
|
||||||
|
|
||||||
|
with GridLocations(20, 0, 2, 1):
|
||||||
|
Hole(3.5 / 2)
|
||||||
|
|
||||||
|
before = copy(part)
|
||||||
|
|
||||||
|
faces = part.faces().filter_by(
|
||||||
|
lambda f: len(f.inner_wires().edges().filter_by(GeomType.LINE)) == 2
|
||||||
|
)
|
||||||
|
wires = faces.wires().filter_by(
|
||||||
|
lambda w: any(e.geom_type == GeomType.LINE for e in w.edges())
|
||||||
|
)
|
||||||
|
chamfer(wires.edges(), 0.5)
|
||||||
|
|
||||||
|
location = Location((-25, -25))
|
||||||
|
b = before.part.moved(location)
|
||||||
|
f = [f.moved(location) for f in faces]
|
||||||
|
|
||||||
|
show(b, f, part)
|
||||||
|
save_screenshot(os.path.join(filedir, "filter_nested.png"))
|
||||||
25
docs/selectors_operators/examples/filter_shape_properties.py
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
import os
|
||||||
|
|
||||||
|
from build123d import *
|
||||||
|
from ocp_vscode import *
|
||||||
|
|
||||||
|
working_path = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
filedir = os.path.join(working_path, "..", "..", "assets", "selectors_operators")
|
||||||
|
|
||||||
|
with BuildPart() as open_box_builder:
|
||||||
|
Box(20, 20, 5)
|
||||||
|
offset(amount=-2, openings=open_box_builder.faces().sort_by(Axis.Z)[-1])
|
||||||
|
inside_edges = open_box_builder.edges().filter_by(Edge.is_interior)
|
||||||
|
fillet(inside_edges, 1.5)
|
||||||
|
outside_edges = open_box_builder.edges().filter_by(Edge.is_interior, reverse=True)
|
||||||
|
fillet(outside_edges, 0.5)
|
||||||
|
|
||||||
|
open_box = open_box_builder.part
|
||||||
|
open_box.color = Color(0xEDAE49)
|
||||||
|
outside_fillets = Compound(open_box.faces().filter_by(Face.is_circular_convex))
|
||||||
|
outside_fillets.color = Color(0xD1495B)
|
||||||
|
inside_fillets = Compound(open_box.faces().filter_by(Face.is_circular_concave))
|
||||||
|
inside_fillets.color = Color(0x00798C)
|
||||||
|
|
||||||
|
show(open_box, inside_fillets, outside_fillets)
|
||||||
|
save_screenshot(os.path.join(filedir, "filter_shape_properties.png"))
|
||||||
28
docs/selectors_operators/examples/group_axis.py
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
import os
|
||||||
|
from copy import copy
|
||||||
|
|
||||||
|
from build123d import *
|
||||||
|
from ocp_vscode import *
|
||||||
|
|
||||||
|
working_path = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
filedir = os.path.join(working_path, "..", "..", "assets", "selectors_operators")
|
||||||
|
|
||||||
|
with BuildPart() as fins:
|
||||||
|
with GridLocations(4, 6, 4, 4):
|
||||||
|
Box(2, 3, 10, align=(Align.CENTER, Align.CENTER, Align.MIN))
|
||||||
|
|
||||||
|
with BuildPart() as part:
|
||||||
|
Box(34, 48, 5, align=(Align.CENTER, Align.CENTER, Align.MAX))
|
||||||
|
with GridLocations(20, 27, 2, 2):
|
||||||
|
add(fins)
|
||||||
|
|
||||||
|
without = copy(part)
|
||||||
|
|
||||||
|
target = part.edges().group_by(Axis.Z)[-1].group_by(Edge.length)[-1]
|
||||||
|
fillet(target, .75)
|
||||||
|
|
||||||
|
show(without)
|
||||||
|
save_screenshot(os.path.join(filedir, "group_axis_without.png"))
|
||||||
|
|
||||||
|
show(part)
|
||||||
|
save_screenshot(os.path.join(filedir, "group_axis_with.png"))
|
||||||
31
docs/selectors_operators/examples/group_hole_area.py
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
from copy import copy
|
||||||
|
import os
|
||||||
|
|
||||||
|
from build123d import *
|
||||||
|
from ocp_vscode import *
|
||||||
|
|
||||||
|
working_path = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
filedir = os.path.join(working_path, "..", "..", "assets", "selectors_operators")
|
||||||
|
|
||||||
|
with BuildPart() as part:
|
||||||
|
Cylinder(10, 30, rotation=(90, 0, 0))
|
||||||
|
Cylinder(8, 40, rotation=(90, 0, 0), align=(Align.CENTER, Align.CENTER, Align.MAX))
|
||||||
|
Cylinder(8, 23, rotation=(90, 0, 0), align=(Align.CENTER, Align.CENTER, Align.MIN))
|
||||||
|
Cylinder(5, 40, rotation=(90, 0, 0), align=(Align.CENTER, Align.CENTER, Align.MIN))
|
||||||
|
with BuildSketch(Plane.XY.offset(8)) as s:
|
||||||
|
SlotCenterPoint((0, 38), (0, 48), 5)
|
||||||
|
extrude(amount=2.5, both=True, mode=Mode.SUBTRACT)
|
||||||
|
|
||||||
|
before = copy(part)
|
||||||
|
|
||||||
|
faces = part.faces().group_by(
|
||||||
|
lambda f: Face(f.inner_wires()[0]).area if f.inner_wires() else 0
|
||||||
|
)
|
||||||
|
chamfer([f.outer_wire().edges() for f in faces[-1]], 0.5)
|
||||||
|
|
||||||
|
show(
|
||||||
|
before,
|
||||||
|
[f.translate(f.normal_at() * 0.01) for f in faces],
|
||||||
|
part.part.translate((40, 40)),
|
||||||
|
)
|
||||||
|
save_screenshot(os.path.join(filedir, "group_hole_area.png"))
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
import os
|
||||||
|
from copy import copy
|
||||||
|
|
||||||
|
from build123d import *
|
||||||
|
from ocp_vscode import *
|
||||||
|
|
||||||
|
working_path = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
filedir = os.path.join(working_path, "..", "..", "assets", "selectors_operators")
|
||||||
|
|
||||||
|
with BuildPart() as part:
|
||||||
|
with BuildSketch(Plane.XZ) as sketch:
|
||||||
|
with BuildLine():
|
||||||
|
CenterArc((-6, 12), 10, 0, 360)
|
||||||
|
Line((-16, 0), (16, 0))
|
||||||
|
make_hull()
|
||||||
|
Rectangle(50, 5, align=(Align.CENTER, Align.MAX))
|
||||||
|
|
||||||
|
extrude(amount=12)
|
||||||
|
|
||||||
|
Box(38, 6, 22, align=(Align.CENTER, Align.MAX, Align.MIN), mode=Mode.SUBTRACT)
|
||||||
|
|
||||||
|
circle = part.edges().filter_by(GeomType.CIRCLE).sort_by(Axis.Y)[0]
|
||||||
|
with Locations(Plane(circle.arc_center, z_dir=circle.normal())):
|
||||||
|
CounterBoreHole(13 / 2, 16 / 2, 4)
|
||||||
|
|
||||||
|
mirror(about=Plane.XZ)
|
||||||
|
|
||||||
|
before_fillet = copy(part)
|
||||||
|
|
||||||
|
length_groups = part.edges().group_by(Edge.length)
|
||||||
|
fillet(length_groups.group(6) + length_groups.group(5), 4)
|
||||||
|
|
||||||
|
after_fillet = copy(part)
|
||||||
|
|
||||||
|
with BuildSketch() as pins:
|
||||||
|
with Locations((-21, 0)):
|
||||||
|
Circle(3 / 2)
|
||||||
|
with Locations((21, 0)):
|
||||||
|
SlotCenterToCenter(1, 3)
|
||||||
|
extrude(amount=-12, mode=Mode.SUBTRACT)
|
||||||
|
|
||||||
|
with GridLocations(42, 16, 2, 2):
|
||||||
|
CounterBoreHole(3.5 / 2, 3.5, 0)
|
||||||
|
|
||||||
|
after_holes = copy(part)
|
||||||
|
|
||||||
|
radius_groups = part.edges().filter_by(GeomType.CIRCLE).group_by(Edge.radius)
|
||||||
|
bearing_edges = radius_groups.group(8).group_by(SortBy.DISTANCE)[-1]
|
||||||
|
pin_edges = radius_groups.group(1.5).filter_by_position(Axis.Z, -5, -5)
|
||||||
|
chamfer([pin_edges, bearing_edges], .5)
|
||||||
|
|
||||||
|
location = Location((-20, -20))
|
||||||
|
items = [before_fillet.part] + length_groups.group(6) + length_groups.group(5)
|
||||||
|
before = Compound(items).move(location)
|
||||||
|
show(before, after_fillet.part.move(Location((20, 20))))
|
||||||
|
save_screenshot(os.path.join(filedir, "group_length_key.png"))
|
||||||
|
|
||||||
|
location = Location((-20, -20), (180, 0, 0))
|
||||||
|
after = Compound([after_holes.part] + pin_edges + bearing_edges).move(location)
|
||||||
|
show(after, part.part.move(Location((20, 20), (180, 0, 0))))
|
||||||
|
save_screenshot(os.path.join(filedir, "group_radius_key.png"))
|
||||||
93
docs/selectors_operators/examples/selectors_operators.py
Normal file
|
|
@ -0,0 +1,93 @@
|
||||||
|
from copy import copy
|
||||||
|
import os
|
||||||
|
|
||||||
|
from build123d import *
|
||||||
|
from ocp_vscode import *
|
||||||
|
|
||||||
|
working_path = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
filedir = os.path.join(working_path, "..", "..", "assets", "selectors_operators")
|
||||||
|
|
||||||
|
selectors = [solids, vertices, edges, faces]
|
||||||
|
line = Line((-9, -9), (9, 9))
|
||||||
|
for i, selector in enumerate(selectors):
|
||||||
|
u = i / (len(selectors) - 1)
|
||||||
|
with BuildPart() as part:
|
||||||
|
with Locations(line @ u):
|
||||||
|
Box(5, 5, 1)
|
||||||
|
Cylinder(2, 5)
|
||||||
|
show_object([part, selector()])
|
||||||
|
|
||||||
|
save_screenshot(os.path.join(filedir, "selectors_select_all.png"))
|
||||||
|
reset_show()
|
||||||
|
|
||||||
|
for i, selector in enumerate(selectors[1:4]):
|
||||||
|
u = i / (len(selectors) - 1)
|
||||||
|
with BuildPart() as part:
|
||||||
|
with Locations(line @ u):
|
||||||
|
Box(5, 5, 1)
|
||||||
|
Cylinder(2, 5)
|
||||||
|
show_object([part, selector(Select.LAST)])
|
||||||
|
|
||||||
|
save_screenshot(os.path.join(filedir, "selectors_select_last.png"))
|
||||||
|
reset_show()
|
||||||
|
|
||||||
|
with BuildPart() as part:
|
||||||
|
with Locations(line @ 1/3):
|
||||||
|
Box(5, 5, 1)
|
||||||
|
Cylinder(2, 5)
|
||||||
|
edges = part.edges(Select.NEW)
|
||||||
|
part_copy = copy(part)
|
||||||
|
|
||||||
|
with Locations(line @ 2/3):
|
||||||
|
b = Box(5, 5, 1)
|
||||||
|
c = Cylinder(2, 5)
|
||||||
|
c.color = Color("DarkTurquoise")
|
||||||
|
|
||||||
|
show(part_copy, edges, b, c, alphas=[.5, 1, .5, 1])
|
||||||
|
|
||||||
|
save_screenshot(os.path.join(filedir, "selectors_select_new.png"))
|
||||||
|
reset_show()
|
||||||
|
|
||||||
|
with BuildPart() as part:
|
||||||
|
with Locations(line @ 1/3):
|
||||||
|
Box(5, 5, 1, align=(Align.CENTER, Align.CENTER, Align.MAX))
|
||||||
|
Cylinder(2, 2, align=(Align.CENTER, Align.CENTER, Align.MIN))
|
||||||
|
edges = part.edges(Select.NEW)
|
||||||
|
part_copy = copy(part)
|
||||||
|
|
||||||
|
with Locations(line @ 2/3):
|
||||||
|
b = Box(5, 5, 1, align=(Align.CENTER, Align.CENTER, Align.MAX), mode=Mode.PRIVATE)
|
||||||
|
c = Cylinder(2, 2, align=(Align.CENTER, Align.CENTER, Align.MIN), mode=Mode.PRIVATE)
|
||||||
|
c.color = Color("DarkTurquoise")
|
||||||
|
show(part_copy, edges, b, c, alphas=[.5, 1, .5, 1])
|
||||||
|
|
||||||
|
save_screenshot(os.path.join(filedir, "selectors_select_new_none.png"))
|
||||||
|
reset_show()
|
||||||
|
|
||||||
|
with BuildPart() as part:
|
||||||
|
with Locations(line @ 1/3):
|
||||||
|
Box(5, 5, 1)
|
||||||
|
Cylinder(2, 5)
|
||||||
|
edges = part.edges().filter_by(lambda a: a.length == 1)
|
||||||
|
fillet(edges, 1)
|
||||||
|
show_object([part, part.edges(Select.NEW)])
|
||||||
|
|
||||||
|
with BuildPart() as part:
|
||||||
|
with Locations(line @ 2/3):
|
||||||
|
Box(5, 5, 1)
|
||||||
|
Cylinder(2, 5)
|
||||||
|
edges = part.edges().filter_by(lambda a: a.length == 1)
|
||||||
|
fillet(edges, 1)
|
||||||
|
show_object([part, part.edges(Select.LAST)])
|
||||||
|
|
||||||
|
save_screenshot(os.path.join(filedir, "selectors_select_new_fillet.png"))
|
||||||
|
|
||||||
|
show(part, part.vertices().sort_by(Axis.X)[-4:])
|
||||||
|
save_screenshot(os.path.join(filedir, "operators_sort_x.png"))
|
||||||
|
|
||||||
|
show(part, part.faces().group_by(SortBy.AREA)[0].edges())
|
||||||
|
save_screenshot(os.path.join(filedir, "operators_group_area.png"))
|
||||||
|
|
||||||
|
faces = part.faces().filter_by(lambda f: f.normal_at() == Vector(0, 0, 1))
|
||||||
|
show(part, [f.translate(f.normal_at() * 0.01) for f in faces])
|
||||||
|
save_screenshot(os.path.join(filedir, "operators_filter_z_normal.png"))
|
||||||
31
docs/selectors_operators/examples/sort_along_wire.py
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
import os
|
||||||
|
|
||||||
|
from build123d import *
|
||||||
|
from ocp_vscode import *
|
||||||
|
|
||||||
|
working_path = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
filedir = os.path.join(working_path, "..", "..", "assets", "selectors_operators")
|
||||||
|
|
||||||
|
with BuildSketch() as along_wire:
|
||||||
|
Rectangle(48, 16, align=Align.MIN)
|
||||||
|
Rectangle(16, 48, align=Align.MIN)
|
||||||
|
Rectangle(32, 32, align=Align.MIN)
|
||||||
|
|
||||||
|
for i, v in enumerate(along_wire.vertices()):
|
||||||
|
fillet(v, i + 1)
|
||||||
|
|
||||||
|
show(along_wire)
|
||||||
|
save_screenshot(os.path.join(filedir, "sort_not_along_wire.png"))
|
||||||
|
|
||||||
|
|
||||||
|
with BuildSketch() as along_wire:
|
||||||
|
Rectangle(48, 16, align=Align.MIN)
|
||||||
|
Rectangle(16, 48, align=Align.MIN)
|
||||||
|
Rectangle(32, 32, align=Align.MIN)
|
||||||
|
|
||||||
|
sorted_verts = along_wire.vertices().sort_by(along_wire.wire())
|
||||||
|
for i, v in enumerate(sorted_verts):
|
||||||
|
fillet(v, i + 1)
|
||||||
|
|
||||||
|
show(along_wire)
|
||||||
|
save_screenshot(os.path.join(filedir, "sort_along_wire.png"))
|
||||||
28
docs/selectors_operators/examples/sort_axis.py
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
from copy import copy
|
||||||
|
import os
|
||||||
|
|
||||||
|
from build123d import *
|
||||||
|
from ocp_vscode import *
|
||||||
|
|
||||||
|
working_path = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
filedir = os.path.join(working_path, "..", "..", "assets", "selectors_operators")
|
||||||
|
|
||||||
|
with BuildPart() as part:
|
||||||
|
with BuildSketch(Plane.YZ) as profile:
|
||||||
|
with BuildLine():
|
||||||
|
l1 = FilletPolyline((16, 0), (32, 0), (32, 25), radius=12)
|
||||||
|
l2 = FilletPolyline((16, 4), (28, 4), (28, 15), radius=8)
|
||||||
|
Line(l1 @ 0, l2 @ 0)
|
||||||
|
Polyline(l1 @ 1, l1 @ 1 - Vector(2, 0), l2 @ 1 + Vector(2, 0), l2 @ 1)
|
||||||
|
make_face()
|
||||||
|
extrude(amount=34)
|
||||||
|
|
||||||
|
before = copy(part).part
|
||||||
|
|
||||||
|
face = part.faces().sort_by(Axis.X)[-1]
|
||||||
|
edge = face.edges().sort_by(Axis.Y)[0]
|
||||||
|
revolve(face, -Axis(edge), 90)
|
||||||
|
|
||||||
|
f = face.translate(face.normal_at() * 0.01)
|
||||||
|
show(before, f, edge, part.part.translate((25, 33)))
|
||||||
|
save_screenshot(os.path.join(filedir, "sort_axis.png"))
|
||||||
21
docs/selectors_operators/examples/sort_distance_from.py
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
import os
|
||||||
|
from itertools import product
|
||||||
|
|
||||||
|
from build123d import *
|
||||||
|
from ocp_vscode import *
|
||||||
|
|
||||||
|
working_path = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
filedir = os.path.join(working_path, "..", "..", "assets", "selectors_operators")
|
||||||
|
|
||||||
|
boxes = ShapeList(
|
||||||
|
Box(1, 1, 1).scale(0.75 if (i, j) == (1, 2) else 0.25).translate((i, j, 0))
|
||||||
|
for i, j in product(range(-3, 4), repeat=2)
|
||||||
|
)
|
||||||
|
|
||||||
|
boxes = boxes.sort_by_distance(Vertex())
|
||||||
|
show(*boxes, colors=ColorMap.listed(len(boxes)))
|
||||||
|
save_screenshot(os.path.join(filedir, "sort_distance_from_origin.png"))
|
||||||
|
|
||||||
|
boxes = boxes.sort_by_distance(boxes.sort_by(Solid.volume).last)
|
||||||
|
show(*boxes, colors=ColorMap.listed(len(boxes)))
|
||||||
|
save_screenshot(os.path.join(filedir, "sort_distance_from_largest.png"))
|
||||||
45
docs/selectors_operators/examples/sort_sortby.py
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
import os
|
||||||
|
|
||||||
|
from build123d import *
|
||||||
|
from ocp_vscode import *
|
||||||
|
|
||||||
|
working_path = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
filedir = os.path.join(working_path, "..", "..", "assets", "selectors_operators")
|
||||||
|
|
||||||
|
with BuildPart() as part:
|
||||||
|
Box(5, 5, 1)
|
||||||
|
Cylinder(2, 5)
|
||||||
|
edges = part.edges().filter_by(lambda a: a.length == 1)
|
||||||
|
fillet(edges, 1)
|
||||||
|
|
||||||
|
box = Box(5, 5, 5).move(Location((-6, -6)))
|
||||||
|
sphere = Sphere(5 / 2).move(Location((6, 6)))
|
||||||
|
solids = ShapeList([part.part, box, sphere])
|
||||||
|
|
||||||
|
part.wires().sort_by(SortBy.LENGTH)[:4]
|
||||||
|
|
||||||
|
part.wires().sort_by(Wire.length)[:4]
|
||||||
|
part.wires().group_by(SortBy.LENGTH)[0]
|
||||||
|
|
||||||
|
part.vertices().sort_by(SortBy.DISTANCE)[-2:]
|
||||||
|
|
||||||
|
part.vertices().sort_by_distance(Vertex())[-2:]
|
||||||
|
part.vertices().group_by(Vertex().distance)[-1]
|
||||||
|
|
||||||
|
|
||||||
|
show(part, part.wires().sort_by(SortBy.LENGTH)[:4])
|
||||||
|
save_screenshot(os.path.join(filedir, "sort_sortby_length.png"))
|
||||||
|
|
||||||
|
# show(part, part.faces().sort_by(SortBy.AREA)[-2:])
|
||||||
|
# save_screenshot(os.path.join(filedir, "sort_sortby_area.png"))
|
||||||
|
|
||||||
|
# solid = solids.sort_by(SortBy.VOLUME)[-1]
|
||||||
|
# solid.color = "violet"
|
||||||
|
# show([part, box, sphere], solid)
|
||||||
|
# save_screenshot(os.path.join(filedir, "sort_sortby_volume.png"))
|
||||||
|
|
||||||
|
# show(part, part.edges().filter_by(GeomType.CIRCLE).sort_by(SortBy.RADIUS)[-4:])
|
||||||
|
# save_screenshot(os.path.join(filedir, "sort_sortby_radius.png"))
|
||||||
|
|
||||||
|
show(part, part.vertices().sort_by(SortBy.DISTANCE)[-2:])
|
||||||
|
save_screenshot(os.path.join(filedir, "sort_sortby_distance.png"))
|
||||||
195
docs/selectors_operators/filter_examples.rst
Normal file
|
|
@ -0,0 +1,195 @@
|
||||||
|
##################
|
||||||
|
Filter Examples
|
||||||
|
##################
|
||||||
|
|
||||||
|
.. _filter_geomtype:
|
||||||
|
|
||||||
|
GeomType
|
||||||
|
=============
|
||||||
|
|
||||||
|
:class:`~build_enums.GeomType` enums are shape type shorthands for ``Edge`` and ``Face``
|
||||||
|
objects. They are most helpful for filtering objects of that specific type for further
|
||||||
|
operations, and are sometimes necessary e.g. before sorting or filtering by radius.
|
||||||
|
``Edge`` and ``Face`` each support a subset of ``GeomType``:
|
||||||
|
|
||||||
|
* ``Edge`` can be type ``LINE``, ``CIRCLE``, ``ELLIPSE``, ``HYPERBOLA``, ``PARABOLA``, ``BEZIER``, ``BSPLINE``, ``OFFSET``, ``OTHER``
|
||||||
|
* ``Face`` can be type ``PLANE``, ``CYLINDER``, ``CONE``, ``SPHERE``, ``TORUS``, ``BEZIER``, ``BSPLINE``, ``REVOLUTION``, ``EXTRUSION``, ``OFFSET``, ``OTHER``
|
||||||
|
|
||||||
|
.. dropdown:: Setup
|
||||||
|
|
||||||
|
.. literalinclude:: examples/filter_geomtype.py
|
||||||
|
:language: python
|
||||||
|
:lines: 3, 8-13
|
||||||
|
|
||||||
|
.. literalinclude:: examples/filter_geomtype.py
|
||||||
|
:language: python
|
||||||
|
:lines: 15
|
||||||
|
|
||||||
|
.. figure:: ../assets/selectors_operators/filter_geomtype_line.png
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
|
|
||||||
|
|
||||||
|
.. literalinclude:: examples/filter_geomtype.py
|
||||||
|
:language: python
|
||||||
|
:lines: 17
|
||||||
|
|
||||||
|
.. figure:: ../assets/selectors_operators/filter_geomtype_cylinder.png
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
|
|
||||||
|
|
||||||
|
.. _filter_all_edges_circle:
|
||||||
|
|
||||||
|
All Edges Circle
|
||||||
|
========================
|
||||||
|
|
||||||
|
In this complete bearing block, we want to add joints for the bearings. These should be
|
||||||
|
located in the counterbore recess. One way to locate the joints is by finding faces with
|
||||||
|
centers located where the joints need to be located. Filtering for faces with only
|
||||||
|
circular edges selects the counterbore faces that meet the joint criteria.
|
||||||
|
|
||||||
|
.. dropdown:: Setup
|
||||||
|
|
||||||
|
.. literalinclude:: examples/filter_all_edges_circle.py
|
||||||
|
:language: python
|
||||||
|
:lines: 3, 8-41
|
||||||
|
|
||||||
|
.. literalinclude:: examples/filter_all_edges_circle.py
|
||||||
|
:language: python
|
||||||
|
:lines: 43-47
|
||||||
|
|
||||||
|
.. figure:: ../assets/selectors_operators/filter_all_edges_circle.png
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
|
|
||||||
|
|
||||||
|
.. _filter_axis_plane:
|
||||||
|
|
||||||
|
Axis and Plane
|
||||||
|
=================
|
||||||
|
|
||||||
|
Filtering by an Axis will select faces perpendicular to the axis. Likewise filtering by
|
||||||
|
Plane will select faces parallel to the plane.
|
||||||
|
|
||||||
|
.. dropdown:: Setup
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from build123d import *
|
||||||
|
|
||||||
|
with BuildPart() as part:
|
||||||
|
Box(1, 1, 1)
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
part.faces().filter_by(Axis.Z)
|
||||||
|
part.faces().filter_by(Plane.XY)
|
||||||
|
|
||||||
|
.. figure:: ../assets/selectors_operators/filter_axisplane.png
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
|
|
||||||
|
|
||||||
|
It might be useful to filter by an Axis or Plane in other ways. A lambda can be used to
|
||||||
|
accomplish this with feature properties or methods. Here, we are looking for faces where
|
||||||
|
the dot product of face normal and either the axis direction or the plane normal is about
|
||||||
|
to 0. The result is faces parallel to the axis or perpendicular to the plane.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
part.faces().filter_by(lambda f: abs(f.normal_at().dot(Axis.Z.direction) < 1e-6)
|
||||||
|
part.faces().filter_by(lambda f: abs(f.normal_at().dot(Plane.XY.z_dir)) < 1e-6)
|
||||||
|
|
||||||
|
.. figure:: ../assets/selectors_operators/filter_dot_axisplane.png
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
|
|
||||||
|
|
||||||
|
.. _filter_inner_wire_count:
|
||||||
|
|
||||||
|
Inner Wire Count
|
||||||
|
========================
|
||||||
|
|
||||||
|
This motor bracket imported from a step file needs joints for adding to an assembly.
|
||||||
|
Joints for the M3 clearance holes were already found by using the cylindrical face's
|
||||||
|
axis of rotation, but the motor bore and slots need specific placement. The motor bore
|
||||||
|
can be found by filtering for faces with 5 inner wires, sorting for the desired face,
|
||||||
|
and then filtering for the specific inner wire by radius.
|
||||||
|
|
||||||
|
- bracket STEP model: :download:`nema-17-bracket.step <examples/nema-17-bracket.step>`
|
||||||
|
|
||||||
|
.. dropdown:: Setup
|
||||||
|
|
||||||
|
.. literalinclude:: examples/filter_inner_wire_count.py
|
||||||
|
:language: python
|
||||||
|
:lines: 4, 9-16
|
||||||
|
|
||||||
|
.. literalinclude:: examples/filter_inner_wire_count.py
|
||||||
|
:language: python
|
||||||
|
:lines: 18-21
|
||||||
|
|
||||||
|
.. figure:: ../assets/selectors_operators/filter_inner_wire_count.png
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
|
|
||||||
|
|
||||||
|
Linear joints for the slots are appropriate for mating flexibility, but require more
|
||||||
|
than a single location. The slot arc centers can be used for creating a linear joint
|
||||||
|
axis and range. To do that we can filter for faces with 6 inner wires, sort for and
|
||||||
|
select the top face, and then filter for the circular edges of the inner wires.
|
||||||
|
|
||||||
|
.. literalinclude:: examples/filter_inner_wire_count.py
|
||||||
|
:language: python
|
||||||
|
:lines: 25-32
|
||||||
|
|
||||||
|
.. figure:: ../assets/selectors_operators/filter_inner_wire_count_linear.png
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
|
|
||||||
|
|
||||||
|
.. _filter_nested:
|
||||||
|
|
||||||
|
Nested Filters
|
||||||
|
========================
|
||||||
|
|
||||||
|
Filters can be nested to specify features by characteristics other than their own, like
|
||||||
|
child properties. Here we want to chamfer the mating edges of the D bore and square
|
||||||
|
shaft. A way to do this is first looking for faces with only 2 line edges among the
|
||||||
|
inner wires. The nested filter captures the straight edges, while the parent filter
|
||||||
|
selects faces based on the count. Then, from those faces, we filter for the wires with
|
||||||
|
any line edges.
|
||||||
|
|
||||||
|
.. dropdown:: Setup
|
||||||
|
|
||||||
|
.. literalinclude:: examples/filter_nested.py
|
||||||
|
:language: python
|
||||||
|
:lines: 4, 9-22
|
||||||
|
|
||||||
|
.. literalinclude:: examples/filter_nested.py
|
||||||
|
:language: python
|
||||||
|
:lines: 26-32
|
||||||
|
|
||||||
|
.. figure:: ../assets/selectors_operators/filter_nested.png
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
|
|
||||||
|
|
||||||
|
.. _filter_shape_properties:
|
||||||
|
|
||||||
|
Shape Properties
|
||||||
|
========================
|
||||||
|
|
||||||
|
Selected features can be quickly filtered by feature properties. First, we filter by
|
||||||
|
interior and exterior edges using the ``Edge`` ``is interior`` property to apply
|
||||||
|
different fillets accordingly. Then the ``Face`` ``is_circular_*`` properties are used
|
||||||
|
to highlight the resulting fillets.
|
||||||
|
|
||||||
|
.. literalinclude:: examples/filter_shape_properties.py
|
||||||
|
:language: python
|
||||||
|
:lines: 3-4, 8-22
|
||||||
|
|
||||||
|
.. figure:: ../assets/selectors_operators/filter_shape_properties.png
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
|
|
||||||
116
docs/selectors_operators/group_examples.rst
Normal file
|
|
@ -0,0 +1,116 @@
|
||||||
|
#################
|
||||||
|
Group Examples
|
||||||
|
#################
|
||||||
|
|
||||||
|
.. _group_axis:
|
||||||
|
|
||||||
|
Axis and Length
|
||||||
|
==================
|
||||||
|
|
||||||
|
This heatsink component could use fillets on the ends of the fins on the long ends. One
|
||||||
|
way to accomplish this is to filter by length, sort by axis, and slice the
|
||||||
|
result knowing how many edges to expect.
|
||||||
|
|
||||||
|
.. dropdown:: Setup
|
||||||
|
|
||||||
|
.. literalinclude:: examples/group_axis.py
|
||||||
|
:language: python
|
||||||
|
:lines: 4, 9-17
|
||||||
|
|
||||||
|
.. figure:: ../assets/selectors_operators/group_axis_without.png
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
|
|
||||||
|
|
||||||
|
However, ``group_by`` can be used to first group all the edges by z-axis position and then
|
||||||
|
group again by length. In both cases, you can select the desired edges from the last group.
|
||||||
|
|
||||||
|
.. literalinclude:: examples/group_axis.py
|
||||||
|
:language: python
|
||||||
|
:lines: 21-22
|
||||||
|
|
||||||
|
.. figure:: ../assets/selectors_operators/group_axis_with.png
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
|
|
||||||
|
|
||||||
|
.. _group_hole_area:
|
||||||
|
|
||||||
|
Hole Area
|
||||||
|
==================
|
||||||
|
|
||||||
|
Callables are available to ``group_by``, like ``sort_by``. Here, the first inner wire
|
||||||
|
is converted to a face and then that area is the grouping criteria to find the faces
|
||||||
|
with the largest hole.
|
||||||
|
|
||||||
|
.. dropdown:: Setup
|
||||||
|
|
||||||
|
.. literalinclude:: examples/group_hole_area.py
|
||||||
|
:language: python
|
||||||
|
:lines: 4, 9-17
|
||||||
|
|
||||||
|
.. literalinclude:: examples/group_hole_area.py
|
||||||
|
:language: python
|
||||||
|
:lines: 21-24
|
||||||
|
|
||||||
|
.. figure:: ../assets/selectors_operators/group_hole_area.png
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
|
|
||||||
|
|
||||||
|
.. _group_properties_with_keys:
|
||||||
|
|
||||||
|
Properties with Keys
|
||||||
|
====================
|
||||||
|
|
||||||
|
Groups are usually selected by list slice, often smallest ``[0]`` or largest ``[-1]``,
|
||||||
|
but they can also be selected by key with the ``group`` method if the keys are known.
|
||||||
|
Starting with an incomplete bearing block we are looking to add fillets to the ribs
|
||||||
|
and corners. We know the edge lengths so the edges can be grouped by ``Edge.Length`` and
|
||||||
|
then the desired groups are selected with the ``group`` method using the lengths as keys.
|
||||||
|
|
||||||
|
.. dropdown:: Setup
|
||||||
|
|
||||||
|
.. literalinclude:: examples/group_properties_with_keys.py
|
||||||
|
:language: python
|
||||||
|
:lines: 4, 9-26
|
||||||
|
|
||||||
|
.. literalinclude:: examples/group_properties_with_keys.py
|
||||||
|
:language: python
|
||||||
|
:lines: 30, 31
|
||||||
|
|
||||||
|
.. figure:: ../assets/selectors_operators/group_length_key.png
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
|
|
||||||
|
|
||||||
|
Next, we add alignment pin and counterbore holes after the fillets to make sure
|
||||||
|
screw heads sit flush where they overlap the fillet. Once that is done, it's time to
|
||||||
|
finalize the tight-tolerance bearing and pin holes with chamfers to make installation
|
||||||
|
easier. We can filter by ``GeomType.CIRCLE`` and group by ``Edge.radius`` to group the
|
||||||
|
circular edges. Again, the radii are known, so we can retrieve those groups directly
|
||||||
|
and then further specify only the edges the bearings and pins are installed from.
|
||||||
|
|
||||||
|
.. dropdown:: Adding holes
|
||||||
|
|
||||||
|
.. literalinclude:: examples/group_properties_with_keys.py
|
||||||
|
:language: python
|
||||||
|
:lines: 35-43
|
||||||
|
|
||||||
|
.. literalinclude:: examples/group_properties_with_keys.py
|
||||||
|
:language: python
|
||||||
|
:lines: 47-50
|
||||||
|
|
||||||
|
.. figure:: ../assets/selectors_operators/group_radius_key.png
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
|
|
||||||
|
|
||||||
|
Note that ``group_by`` is not the only way to capture edges with a known property
|
||||||
|
value! ``filter_by`` with a lambda expression can be used as well:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
radius_groups = part.edges().filter_by(GeomType.CIRCLE)
|
||||||
|
bearing_edges = radius_groups.filter_by(lambda e: e.radius == 8)
|
||||||
|
pin_edges = radius_groups.filter_by(lambda e: e.radius == 1.5)
|
||||||
144
docs/selectors_operators/sort_examples.rst
Normal file
|
|
@ -0,0 +1,144 @@
|
||||||
|
################
|
||||||
|
Sort Examples
|
||||||
|
################
|
||||||
|
|
||||||
|
.. _sort_sortby:
|
||||||
|
|
||||||
|
SortBy
|
||||||
|
=============
|
||||||
|
|
||||||
|
:class:`~build_enums.SortBy` enums are shape property shorthands which work across
|
||||||
|
``Shape`` multiple object types. ``SortBy`` is a criteria for both ``sort_by`` and
|
||||||
|
``group_by``.
|
||||||
|
|
||||||
|
* ``SortBy.LENGTH`` works with ``Edge``, ``Wire``
|
||||||
|
* ``SortBy.AREA`` works with ``Face``, ``Solid``
|
||||||
|
* ``SortBy.VOLUME`` works with ``Solid``
|
||||||
|
* ``SortBy.RADIUS`` works with ``Edge``, ``Face`` with :class:`~build_enums.GeomType` ``CIRCLE``, ``CYLINDER``, ``SPHERE``
|
||||||
|
* ``SortBy.DISTANCE`` works ``Vertex``, ``Edge``, ``Wire``, ``Face``, ``Solid``
|
||||||
|
|
||||||
|
``SortBy`` is often interchangeable with specific shape properties and can alternatively
|
||||||
|
be used with``group_by``.
|
||||||
|
|
||||||
|
.. dropdown:: Setup
|
||||||
|
|
||||||
|
.. literalinclude:: examples/sort_sortby.py
|
||||||
|
:language: python
|
||||||
|
:lines: 3, 8-13
|
||||||
|
|
||||||
|
.. literalinclude:: examples/sort_sortby.py
|
||||||
|
:language: python
|
||||||
|
:lines: 19-22
|
||||||
|
|
||||||
|
.. figure:: ../assets/selectors_operators/sort_sortby_length.png
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
|
|
||||||
|
|
||||||
|
.. literalinclude:: examples/sort_sortby.py
|
||||||
|
:language: python
|
||||||
|
:lines: 24-27
|
||||||
|
|
||||||
|
.. figure:: ../assets/selectors_operators/sort_sortby_distance.png
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
|
|
||||||
|
|
||||||
|
.. _sort_along_wire:
|
||||||
|
|
||||||
|
Along Wire
|
||||||
|
=============
|
||||||
|
|
||||||
|
Vertices selected from an edge or wire might have a useful ordering when created from
|
||||||
|
a single object, but when created from multiple objects, the ordering not useful. For
|
||||||
|
example, when applying incrementing fillet radii to a list of vertices from the face,
|
||||||
|
the order is random.
|
||||||
|
|
||||||
|
.. dropdown:: Setup
|
||||||
|
|
||||||
|
.. literalinclude:: examples/sort_along_wire.py
|
||||||
|
:language: python
|
||||||
|
:lines: 3, 8-12
|
||||||
|
|
||||||
|
.. literalinclude:: examples/sort_along_wire.py
|
||||||
|
:language: python
|
||||||
|
:lines: 14-15
|
||||||
|
|
||||||
|
.. figure:: ../assets/selectors_operators/sort_not_along_wire.png
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
|
|
||||||
|
|
||||||
|
Vertices may be sorted along the wire they fall on to create order. Notice the fillet
|
||||||
|
radii now increase in order.
|
||||||
|
|
||||||
|
.. literalinclude:: examples/sort_along_wire.py
|
||||||
|
:language: python
|
||||||
|
:lines: 26-28
|
||||||
|
|
||||||
|
.. figure:: ../assets/selectors_operators/sort_along_wire.png
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
|
|
||||||
|
|
||||||
|
.. _sort_axis:
|
||||||
|
|
||||||
|
Axis
|
||||||
|
=========================
|
||||||
|
|
||||||
|
Sorting by axis is often the most straightforward way to optimize selections. In this
|
||||||
|
part we want to revolve the face at the end around an inside edge of the completed
|
||||||
|
extrusion. First, the face to extrude can be found by sorting along x-axis and the revolution
|
||||||
|
edge can be found sorting along y-axis.
|
||||||
|
|
||||||
|
.. dropdown:: Setup
|
||||||
|
|
||||||
|
.. literalinclude:: examples/sort_axis.py
|
||||||
|
:language: python
|
||||||
|
:lines: 4, 9-18
|
||||||
|
|
||||||
|
.. literalinclude:: examples/sort_axis.py
|
||||||
|
:language: python
|
||||||
|
:lines: 22-24
|
||||||
|
|
||||||
|
.. figure:: ../assets/selectors_operators/sort_axis.png
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
|
|
||||||
|
|
||||||
|
.. _sort_distance_from:
|
||||||
|
|
||||||
|
Distance From
|
||||||
|
=========================
|
||||||
|
|
||||||
|
A ``sort_by_distance`` can be used to sort objects by their distance from another object.
|
||||||
|
Here we are sorting the boxes by distance from the origin, using an empty ``Vertex``
|
||||||
|
(at the origin) as the reference shape to find distance to.
|
||||||
|
|
||||||
|
.. dropdown:: Setup
|
||||||
|
|
||||||
|
.. literalinclude:: examples/sort_distance_from.py
|
||||||
|
:language: python
|
||||||
|
:lines: 2-5, 9-13
|
||||||
|
|
||||||
|
.. literalinclude:: examples/sort_distance_from.py
|
||||||
|
:language: python
|
||||||
|
:lines: 15-16
|
||||||
|
|
||||||
|
.. figure:: ../assets/selectors_operators/sort_distance_from_origin.png
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
|
|
||||||
|
|
||||||
|
The example can be extended by first sorting the boxes by volume using the ``Solid``
|
||||||
|
property ``volume``, and getting the last (largest) box. Then, the boxes sorted by
|
||||||
|
their distance from the largest box.
|
||||||
|
|
||||||
|
.. literalinclude:: examples/sort_distance_from.py
|
||||||
|
:language: python
|
||||||
|
:lines: 19-20
|
||||||
|
|
||||||
|
.. figure:: ../assets/selectors_operators/sort_distance_from_largest.png
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
|
|
||||||