Updating doc: separating key concepts, adding OpenSCAD section

This commit is contained in:
gumyr 2025-01-22 20:04:42 -05:00
parent 50663a21c4
commit 94fdd97a55
4 changed files with 547 additions and 298 deletions

149
docs/OpenSCAD.rst Normal file
View file

@ -0,0 +1,149 @@
Transitioning from OpenSCAD
===========================
Welcome to build123d! If you're familiar with OpenSCAD, you'll notice key differences in
how models are constructed. This guide is designed to help you adapt your design approach
and understand the fundamental differences in modeling philosophies. While OpenSCAD relies
heavily on Constructive Solid Geometry (CSG) to combine primitive 3D shapes like cubes and
spheres, build123d encourages a more flexible and efficient workflow based on building
lower-dimensional objects.
Why Transition to build123d?
----------------------------
Transitioning to build123d allows you to harness a modern and efficient approach to 3D modeling.
By starting with lower-dimensional objects and leveraging powerful transformation tools, you can
create precise, complex designs with ease. This workflow emphasizes modularity and maintainability,
enabling quick modifications and reducing computational complexity.
Moving Beyond Constructive Solid Geometry (CSG)
-----------------------------------------------
OpenSCAD's modeling paradigm heavily relies on Constructive Solid Geometry (CSG) to build
models by combining and subtracting 3D solids. While build123d supports similar operations,
its design philosophy encourages a fundamentally different, often more efficient approach:
starting with lower-dimensional entities like faces and edges and then transforming them
into solids.
### Why Transition Away from CSG?
CSG is a powerful method for creating 3D models, but it has limitations when dealing with
complex designs. build123ds approach offers several advantages:
- **Simplified Complexity Management**:
Working with 2D profiles and faces instead of directly manipulating 3D solids simplifies
your workflow. In large models, the number of operations on solids can grow exponentially,
making it difficult to manage and debug. Building with 2D profiles helps keep designs
modular and organized.
- **Improved Robustness**:
Operations on 2D profiles are inherently less computationally intensive and
less error-prone than equivalent operations on 3D solids. This robustness ensures smoother
workflows and reduces the likelihood of failing operations in complex models.
- **Enhanced Efficiency**:
Constructing models from 2D profiles using operations like **extruding**, **lofting**,
**sweeping**, or **revolving** is computationally faster. These methods also provide
greater design flexibility, enabling you to create intricate forms with ease.
- **Better Precision and Control**:
Starting with 2D profiles allows for more precise geometric control. Constraints, dimensions,
and relationships between entities can be established more effectively in 2D, ensuring a solid
foundation for your 3D design.
Using a More Traditional CAD Design Workflow
--------------------------------------------
Most industry-standard CAD packages recommend starting with a sketch (a 2D object) and
transforming it into a 3D model—a design philosophy that is central to build123d.
In build123d, the design process typically begins with defining the outline of an object.
This might involve creating a complex 1D object using **BuildLine**, which provides tools
for constructing intricate wireframe geometries. The next step involves converting these
1D objects into 2D sketches using **BuildSketch**, which offers a wide range of 2D primitives
and advanced capabilities, such as:
- **make_face**: Converts a 1D **BuildLine** object into a planar 2D face.
- **make_hull**: Generates a convex hull from a 1D **BuildLine** object.
Once a 2D profile is created, it can be transformed into 3D objects in a **BuildPart** context
using operations such as:
- **Extrusion**: Extends a 2D profile along a straight path to create a 3D shape.
- **Revolution**: Rotates a 2D profile around an axis to form a symmetrical 3D object.
- **Lofting**: Connects multiple 2D profiles along a path to create smooth transitions
between shapes.
- **Sweeping**: Moves a 2D profile along a defined path to create a 3D form.
### Refining the Model
After creating the initial 3D shape, you can refine the model by adding details or making
modifications using build123d's advanced features, such as:
- **Fillets and Chamfers**: Smooth or bevel edges to enhance the design.
- **Boolean Operations**: Combine, subtract, or intersect 3D shapes to achieve the desired
geometry.
### Example Comparison
To illustrate the advantages of this approach, compare a simple model in OpenSCAD and
build123d:
**OpenSCAD Approach**
.. code-block:: openscad
// A basic cylinder with a hole
difference() {
cylinder(r=10, h=20);
translate([0, 0, 5]) cylinder(r=5, h=20);
}
**build123d Approach**
.. code-block:: python
from build123d import *
# In Builder mode
with BuildPart() as cylinder_with_hole:
with BuildSketch():
Circle(10)
extrude(amount=20)
with BuildSketch(cylinder_with_hole.faces().sort_by(Axis.Z).last):
Circle(5)
extrude(amount=-15, mode=Mode.SUBTRACT)
# In Algebra mode
cyl = extrude(Circle(10), 20)
cyl -= extrude(Plane(cyl.faces().sort_by(Axis.Z)[-1]) * Circle
This approach emphasizes creating a 2D profile (such as the **Circle**) and then applying a
3D operation (like **extrude**) to achieve the desired result. Topological features of the
part under construction are extracted and used as references for adding further details.
Tips for Transitioning
----------------------
- **Think in Lower Dimensions**: Begin with 1D curves or 2D sketches as the foundation
and progressively build upwards into 3D shapes.
- **Leverage Topological References**: Use build123d's powerful selector system to
reference features of existing objects for creating new ones. For example, apply
inside or outside fillets and chamfers to vertices and edges of an existing part
with precision.
- **Explore the Documentation**: Dive into build123ds comprehensive API documentation
to unlock its full potential and discover advanced features.
By shifting your design mindset from solid-based CSG to a profile-driven approach, you
can fully harness build123d's capabilities to create precise, efficient, and complex models.
Welcome aboard, and happy designing!
Conclusion
----------
While OpenSCAD and build123d share the goal of empowering users to create parametric 3D
models, their approaches differ significantly. Embracing build123ds workflow of building
with lower-dimensional objects and applying extrusion, lofting, sweeping, or revolution
will unlock its full potential and lead to better design outcomes.

View file

@ -106,7 +106,9 @@ Table Of Contents
introduction.rst
installation.rst
key_concepts.rst
key_concepts_builder.rst
key_concepts_algebra.rst
OpenSCAD.rst
introductory_examples.rst
tutorials.rst
objects.rst

View file

@ -1,14 +1,6 @@
###########################
Key Concepts (builder mode)
###########################
There are two primary APIs provided by build123d: builder and algebra. The builder
API may be easier for new users as it provides some assistance and shortcuts; however,
if you know what a Quaternion is you might prefer the algebra API which allows
CAD objects to be created in the style of mathematical equations. Both API can
be mixed in the same model with the exception that the algebra API can't be used
from within a builder context. As with music, there is no "best" genre or API,
use the one you prefer or both if you like.
############
Key Concepts
############
The following key concepts will help new users understand build123d quickly.
@ -120,118 +112,6 @@ topology of a shape as shown here for a unit cube:
Users of build123d will often reference topological objects as part of the
process of creating the object as described below.
Builders
========
The three builders, ``BuildLine``, ``BuildSketch``, and ``BuildPart`` are tools to create
new objects - not the objects themselves. Each of the objects and operations applicable
to these builders create objects of the standard CadQuery Direct API, most commonly
``Compound`` objects. This is opposed to CadQuery's Fluent API which creates objects
of the ``Workplane`` class which frequently needed to be converted back to base
class for further processing.
One can access the objects created by these builders by referencing the appropriate
instance variable. For example:
.. code-block:: python
with BuildPart() as my_part:
...
show_object(my_part.part)
.. code-block:: python
with BuildSketch() as my_sketch:
...
show_object(my_sketch.sketch)
.. code-block:: python
with BuildLine() as my_line:
...
show_object(my_line.line)
Implicit Builder Instance Variables
===================================
One might expect to have to reference a builder's instance variable when using
objects or operations that impact that builder like this:
.. code-block:: python
with BuildPart() as part_builder:
Box(part_builder, 10,10,10)
Instead, build123d determines from the scope of the object or operation which
builder it applies to thus eliminating the need for the user to provide this
information - as follows:
.. code-block:: python
with BuildPart() as part_builder:
Box(10,10,10)
with BuildSketch() as sketch_builder:
Circle(2)
In this example, ``Box`` is in the scope of ``part_builder`` while ``Circle``
is in the scope of ``sketch_builder``.
Workplanes
==========
As build123d is a 3D CAD package one must be able to position objects anywhere. As one
frequently will work in the same plane for a sequence of operations, the first parameter(s)
of the builders is a (sequence of) workplane(s) which is (are) used
to aid in the location of features. The default workplane in most cases is the ``Plane.XY``
where a tuple of numbers represent positions on the x and y axes. However workplanes can
be generated on any plane which allows users to put a workplane where they are working
and then work in local 2D coordinate space.
.. code-block:: python
with BuildPart(Plane.XY) as example:
... # a 3D-part
with BuildSketch(example.faces().sort_by(sort_by=Axis.Z)[0]) as bottom:
...
with BuildSketch(Plane.XZ) as vertical:
...
with BuildSketch(example.faces().sort_by(sort_by=Axis.Z)[-1]) as top:
...
When ``BuildPart`` is invoked it creates the workplane provided as a parameter (which has a
default of the ``Plane.XY``). The ``bottom`` sketch is therefore created on the ``Plane.XY`` but with the
normal reversed to point down. Subsequently the user has created the ``vertical`` (``Plane.XZ``) sketch.
All objects or operations within the scope of a workplane will automatically be orientated with
respect to this plane so the user only has to work with local coordinates.
As shown above, workplanes can be created from faces as well. The ``top`` sketch is
positioned on top of ``example`` by selecting its faces and finding the one with the greatest z value.
One is not limited to a single workplane at a time. In the following example all six
faces of the first box are used to define workplanes which are then used to position
rotated boxes.
.. code-block:: python
import build123d as bd
with bd.BuildPart() as bp:
bd.Box(3, 3, 3)
with bd.BuildSketch(*bp.faces()):
bd.Rectangle(1, 2, rotation=45)
bd.extrude(amount=0.1)
This is the result:
.. image:: boxes_on_faces.svg
:align: center
.. _location_context_link:
Location
========
@ -286,182 +166,7 @@ There are also four methods that are used to change the location of objects:
Locations can be combined with the ``*`` operator and have their direction flipped with
the ``-`` operator.
Locations Context
=================
When positioning objects or operations within a builder Location Contexts are used. These
function in a very similar was to the builders in that they create a context where one or
more locations are active within a scope. For example:
.. code-block:: python
with BuildPart():
with Locations((0,10),(0,-10)):
Box(1,1,1)
with GridLocations(x_spacing=5, y_spacing=5, x_count=2, y_count=2):
Sphere(1)
Cylinder(1,1)
In this example ``Locations`` creates two positions on the current workplane at (0,10) and (0,-10).
Since ``Box`` is within the scope of ``Locations``, two boxes are created at these locations. The
``GridLocations`` context creates four positions which apply to the ``Sphere``. The ``Cylinder`` is
out of the scope of ``GridLocations`` but in the scope of ``Locations`` so two cylinders are created.
Note that these contexts are creating Location objects not just simple points. The difference
isn't obvious until the ``PolarLocations`` context is used which can also rotate objects within
its scope - much as the hour and minute indicator on an analogue clock.
Also note that the locations are local to the current location(s) - i.e. ``Locations`` can be
nested. It's easy for a user to retrieve the global locations:
.. code-block:: python
with Locations(Plane.XY, Plane.XZ):
locs = GridLocations(1, 1, 2, 2)
for l in locs:
print(l)
.. code-block::
Location(p=(-0.50,-0.50,0.00), o=(0.00,-0.00,0.00))
Location(p=(-0.50,0.50,0.00), o=(0.00,-0.00,0.00))
Location(p=(0.50,-0.50,0.00), o=(0.00,-0.00,0.00))
Location(p=(0.50,0.50,0.00), o=(0.00,-0.00,0.00))
Location(p=(-0.50,-0.00,-0.50), o=(90.00,-0.00,0.00))
Location(p=(-0.50,0.00,0.50), o=(90.00,-0.00,0.00))
Location(p=(0.50,0.00,-0.50), o=(90.00,-0.00,0.00))
Location(p=(0.50,0.00,0.50), o=(90.00,-0.00,0.00))
Operation Inputs
================
When one is operating on an existing object, e.g. adding a fillet to a part,
an iterable of objects is often required (often a ShapeList).
Here is the definition of :meth:`~operations_generic.fillet` to help illustrate:
.. code-block:: python
def fillet(
objects: Union[Union[Edge, Vertex], Iterable[Union[Edge, Vertex]]],
radius: float,
):
To use this fillet operation, an edge or vertex or iterable of edges or
vertices must be provided followed by a fillet radius with or without the keyword as follows:
.. code-block:: python
with BuildPart() as pipes:
Box(10, 10, 10, rotation=(10, 20, 30))
...
fillet(pipes.edges(Select.LAST), radius=0.2)
Here the fillet accepts the iterable ShapeList of edges from the last operation of
the ``pipes`` builder and a radius is provided as a keyword argument.
Combination Modes
=================
Almost all objects or operations have a ``mode`` parameter which is defined by the
``Mode`` Enum class as follows:
.. code-block:: python
class Mode(Enum):
ADD = auto()
SUBTRACT = auto()
INTERSECT = auto()
REPLACE = auto()
PRIVATE = auto()
The ``mode`` parameter describes how the user would like the object or operation to
interact with the object within the builder. For example, ``Mode.ADD`` will
integrate a new object(s) in with an existing ``part``. Note that a part doesn't
necessarily have to be a single object so multiple distinct objects could be added
resulting is multiple objects stored as a ``Compound`` object. As one might expect
``Mode.SUBTRACT``, ``Mode.INTERSECT``, and ``Mode.REPLACE`` subtract, intersect, or replace
(from) the builder's object. ``Mode.PRIVATE`` instructs the builder that this object
should not be combined with the builder's object in any way.
Most commonly, the default ``mode`` is ``Mode.ADD`` but this isn't always true.
For example, the ``Hole`` classes use a default ``Mode.SUBTRACT`` as they remove
a volume from the part under normal circumstances. However, the ``mode`` used in
the ``Hole`` classes can be specified as ``Mode.ADD`` or ``Mode.INTERSECT`` to
help in inspection or debugging.
Selectors
=========
.. include:: selectors.rst
Using Locations & Rotating Objects
==================================
build123d stores points (to be specific ``Location`` (s)) internally to be used as
positions for the placement of new objects. By default, a single location
will be created at the origin of the given workplane such that:
.. code-block:: python
with BuildPart() as pipes:
Box(10, 10, 10, rotation=(10, 20, 30))
will create a single 10x10x10 box centered at (0,0,0) - by default objects are
centered. One can create multiple objects by pushing points prior to creating
objects as follows:
.. code-block:: python
with BuildPart() as pipes:
with Locations((-10, -10, -10), (10, 10, 10)):
Box(10, 10, 10, rotation=(10, 20, 30))
which will create two boxes.
To orient a part, a ``rotation`` parameter is available on ``BuildSketch``` and
``BuildPart`` APIs. When working in a sketch, the rotation is a single angle in
degrees so the parameter is a float. When working on a part, the rotation is
a three dimensional ``Rotation`` object of the form
``Rotation(<about x>, <about y>, <about z>)`` although a simple three tuple of
floats can be used as input. As 3D rotations are not cumulative, one can
combine rotations with the `*` operator like this:
``Rotation(10, 20, 30) * Rotation(0, 90, 0)`` to generate any desired rotation.
.. hint::
Experts Only
``Locations`` will accept ``Location`` objects for input which allows one
to specify both the position and orientation. However, the orientation
is often determined by the ``Plane`` that an object was created on.
``Rotation`` is a subclass of ``Location`` and therefore will also accept
a position component.
Builder's Pending Objects
=========================
When a builder exits, it will push the object created back to its parent if
there was one. Here is an example:
.. code-block:: python
height, width, thickness, f_rad = 60, 80, 20, 10
with BuildPart() as pillow_block:
with BuildSketch() as plan:
Rectangle(width, height)
fillet(plan.vertices(), radius=f_rad)
extrude(amount=thickness)
``BuildSketch`` exits after the ``fillet`` operation and when doing so it transfers
the sketch to the ``pillow_block`` instance of ``BuildPart`` as the internal instance variable
``pending_faces``. This allows the ``extrude`` operation to be immediately invoked as it
extrudes these pending faces into ``Solid`` objects. Likewise, ``loft`` would take all of the
``pending_faces`` and attempt to create a single ``Solid`` object from them.
Normally the user will not need to interact directly with pending objects; however,
one can see pending Edges and Faces with ``<builder_instance>.pending_edges`` and
``<builder_instance>.pending_faces`` attributes. In the above example, by adding a
``print(pillow_block.pending_faces)`` prior to the ``extrude(amount=thickness)`` the
pending ``Face`` from the ``BuildSketch`` will be displayed.

View file

@ -0,0 +1,393 @@
###########################
Key Concepts (builder mode)
###########################
There are two primary APIs provided by build123d: builder and algebra. The builder
API may be easier for new users as it provides some assistance and shortcuts; however,
if you know what a Quaternion is you might prefer the algebra API which allows
CAD objects to be created in the style of mathematical equations. Both API can
be mixed in the same model with the exception that the algebra API can't be used
from within a builder context. As with music, there is no "best" genre or API,
use the one you prefer or both if you like.
The following key concepts will help new users understand build123d quickly.
Understanding the Builder Paradigm
==================================
The **Builder** paradigm in build123d provides a powerful and intuitive way to construct
complex geometric models. At its core, the Builder works like adding a column of numbers
on a piece of paper: a running "total" is maintained internally as each new object is
added or modified. This approach simplifies the process of constructing models by breaking
it into smaller, incremental steps.
How the Builder Works
----------------------
When using a Builder (such as **BuildLine**, **BuildSketch**, or **BuildPart**), the
following principles apply:
1. **Running Total**:
- The Builder maintains an internal "total," which represents the current state of
the object being built.
- Each operation updates this total by combining the new object with the existing one.
2. **Combination Modes**:
- Just as numbers in a column may have a `+` or `-` sign to indicate addition or
subtraction, Builders use **modes** to control how each object is combined with
the current total.
- Common modes include:
- **ADD**: Adds the new object to the current total.
- **SUBTRACT**: Removes the new object from the current total.
- **INTERSECT**: Keeps only the overlapping regions of the new object and the current total.
- **REPLACE**: Entirely replace the running total.
- **PRIVATE**: Don't change the running total at all.
- The mode can be set dynamically for each operation, allowing for flexible and precise modeling.
3. **Extracting the Result**:
- At the end of the building process, the final object is accessed through the
Builder's attributes, such as ``.line``, ``.sketch``, or ``.part``, depending on
the Builder type.
- For example:
- **BuildLine**: Use ``.line`` to retrieve the final wireframe geometry.
- **BuildSketch**: Use ``.sketch`` to extract the completed 2D profile.
- **BuildPart**: Use ``.part`` to obtain the 3D solid.
Example Workflow
-----------------
Here is an example of using a Builder to create a simple part:
.. code-block:: python
from build123d import *
# Using BuildPart to create a 3D model
with BuildPart() as example_part:
with BuildSketch() as base_sketch:
Rectangle(20, 20)
extrude(amount=10) # Create a base block
with BuildSketch(Plane(example_part.faces().sort_by(Axis.Z).last)) as cut_sketch:
Circle(5)
extrude(amount=-5, mode=Mode.SUBTRACT) # Subtract a cylinder
# Access the final part
result_part = example_part.part
Key Concepts
------------
- **Incremental Construction**:
Builders allow you to build objects step-by-step, maintaining clarity and modularity.
- **Dynamic Mode Switching**:
The **mode** parameter gives you precise control over how each operation modifies
the current total.
- **Seamless Extraction**:
The Builder paradigm simplifies the retrieval of the final object, ensuring that you
always have access to the most up-to-date result.
Analogy: Adding Numbers on Paper
--------------------------------
Think of the Builder as a running tally when adding numbers on a piece of paper:
- Each number represents an operation or object.
- The ``+`` or ``-`` sign corresponds to the **ADD** or **SUBTRACT** mode.
- At the end, the total is the sum of all operations, which you can retrieve by referencing
the Builders output.
By adopting this approach, build123d ensures a natural, intuitive workflow for constructing
2D and 3D models.
Builders
========
The three builders, ``BuildLine``, ``BuildSketch``, and ``BuildPart`` are tools to create
new objects - not the objects themselves. Each of the objects and operations applicable
to these builders create objects of the standard CadQuery Direct API, most commonly
``Compound`` objects. This is opposed to CadQuery's Fluent API which creates objects
of the ``Workplane`` class which frequently needed to be converted back to base
class for further processing.
One can access the objects created by these builders by referencing the appropriate
instance variable. For example:
.. code-block:: python
with BuildPart() as my_part:
...
show_object(my_part.part)
.. code-block:: python
with BuildSketch() as my_sketch:
...
show_object(my_sketch.sketch)
.. code-block:: python
with BuildLine() as my_line:
...
show_object(my_line.line)
Implicit Builder Instance Variables
===================================
One might expect to have to reference a builder's instance variable when using
objects or operations that impact that builder like this:
.. code-block:: python
with BuildPart() as part_builder:
Box(part_builder, 10,10,10)
Instead, build123d determines from the scope of the object or operation which
builder it applies to thus eliminating the need for the user to provide this
information - as follows:
.. code-block:: python
with BuildPart() as part_builder:
Box(10,10,10)
with BuildSketch() as sketch_builder:
Circle(2)
In this example, ``Box`` is in the scope of ``part_builder`` while ``Circle``
is in the scope of ``sketch_builder``.
Workplanes
==========
As build123d is a 3D CAD package one must be able to position objects anywhere. As one
frequently will work in the same plane for a sequence of operations, the first parameter(s)
of the builders is a (sequence of) workplane(s) which is (are) used
to aid in the location of features. The default workplane in most cases is the ``Plane.XY``
where a tuple of numbers represent positions on the x and y axes. However workplanes can
be generated on any plane which allows users to put a workplane where they are working
and then work in local 2D coordinate space.
.. code-block:: python
with BuildPart(Plane.XY) as example:
... # a 3D-part
with BuildSketch(example.faces().sort_by(sort_by=Axis.Z)[0]) as bottom:
...
with BuildSketch(Plane.XZ) as vertical:
...
with BuildSketch(example.faces().sort_by(sort_by=Axis.Z)[-1]) as top:
...
When ``BuildPart`` is invoked it creates the workplane provided as a parameter (which has a
default of the ``Plane.XY``). The ``bottom`` sketch is therefore created on the ``Plane.XY`` but with the
normal reversed to point down. Subsequently the user has created the ``vertical`` (``Plane.XZ``) sketch.
All objects or operations within the scope of a workplane will automatically be orientated with
respect to this plane so the user only has to work with local coordinates.
As shown above, workplanes can be created from faces as well. The ``top`` sketch is
positioned on top of ``example`` by selecting its faces and finding the one with the greatest z value.
One is not limited to a single workplane at a time. In the following example all six
faces of the first box are used to define workplanes which are then used to position
rotated boxes.
.. code-block:: python
import build123d as bd
with bd.BuildPart() as bp:
bd.Box(3, 3, 3)
with bd.BuildSketch(*bp.faces()):
bd.Rectangle(1, 2, rotation=45)
bd.extrude(amount=0.1)
This is the result:
.. image:: boxes_on_faces.svg
:align: center
.. _location_context_link:
Locations Context
=================
When positioning objects or operations within a builder Location Contexts are used. These
function in a very similar was to the builders in that they create a context where one or
more locations are active within a scope. For example:
.. code-block:: python
with BuildPart():
with Locations((0,10),(0,-10)):
Box(1,1,1)
with GridLocations(x_spacing=5, y_spacing=5, x_count=2, y_count=2):
Sphere(1)
Cylinder(1,1)
In this example ``Locations`` creates two positions on the current workplane at (0,10) and (0,-10).
Since ``Box`` is within the scope of ``Locations``, two boxes are created at these locations. The
``GridLocations`` context creates four positions which apply to the ``Sphere``. The ``Cylinder`` is
out of the scope of ``GridLocations`` but in the scope of ``Locations`` so two cylinders are created.
Note that these contexts are creating Location objects not just simple points. The difference
isn't obvious until the ``PolarLocations`` context is used which can also rotate objects within
its scope - much as the hour and minute indicator on an analogue clock.
Also note that the locations are local to the current location(s) - i.e. ``Locations`` can be
nested. It's easy for a user to retrieve the global locations:
.. code-block:: python
with Locations(Plane.XY, Plane.XZ):
locs = GridLocations(1, 1, 2, 2)
for l in locs:
print(l)
.. code-block::
Location(p=(-0.50,-0.50,0.00), o=(0.00,-0.00,0.00))
Location(p=(-0.50,0.50,0.00), o=(0.00,-0.00,0.00))
Location(p=(0.50,-0.50,0.00), o=(0.00,-0.00,0.00))
Location(p=(0.50,0.50,0.00), o=(0.00,-0.00,0.00))
Location(p=(-0.50,-0.00,-0.50), o=(90.00,-0.00,0.00))
Location(p=(-0.50,0.00,0.50), o=(90.00,-0.00,0.00))
Location(p=(0.50,0.00,-0.50), o=(90.00,-0.00,0.00))
Location(p=(0.50,0.00,0.50), o=(90.00,-0.00,0.00))
Operation Inputs
================
When one is operating on an existing object, e.g. adding a fillet to a part,
an iterable of objects is often required (often a ShapeList).
Here is the definition of :meth:`~operations_generic.fillet` to help illustrate:
.. code-block:: python
def fillet(
objects: Union[Union[Edge, Vertex], Iterable[Union[Edge, Vertex]]],
radius: float,
):
To use this fillet operation, an edge or vertex or iterable of edges or
vertices must be provided followed by a fillet radius with or without the keyword as follows:
.. code-block:: python
with BuildPart() as pipes:
Box(10, 10, 10, rotation=(10, 20, 30))
...
fillet(pipes.edges(Select.LAST), radius=0.2)
Here the fillet accepts the iterable ShapeList of edges from the last operation of
the ``pipes`` builder and a radius is provided as a keyword argument.
Combination Modes
=================
Almost all objects or operations have a ``mode`` parameter which is defined by the
``Mode`` Enum class as follows:
.. code-block:: python
class Mode(Enum):
ADD = auto()
SUBTRACT = auto()
INTERSECT = auto()
REPLACE = auto()
PRIVATE = auto()
The ``mode`` parameter describes how the user would like the object or operation to
interact with the object within the builder. For example, ``Mode.ADD`` will
integrate a new object(s) in with an existing ``part``. Note that a part doesn't
necessarily have to be a single object so multiple distinct objects could be added
resulting is multiple objects stored as a ``Compound`` object. As one might expect
``Mode.SUBTRACT``, ``Mode.INTERSECT``, and ``Mode.REPLACE`` subtract, intersect, or replace
(from) the builder's object. ``Mode.PRIVATE`` instructs the builder that this object
should not be combined with the builder's object in any way.
Most commonly, the default ``mode`` is ``Mode.ADD`` but this isn't always true.
For example, the ``Hole`` classes use a default ``Mode.SUBTRACT`` as they remove
a volume from the part under normal circumstances. However, the ``mode`` used in
the ``Hole`` classes can be specified as ``Mode.ADD`` or ``Mode.INTERSECT`` to
help in inspection or debugging.
Using Locations & Rotating Objects
==================================
build123d stores points (to be specific ``Location`` (s)) internally to be used as
positions for the placement of new objects. By default, a single location
will be created at the origin of the given workplane such that:
.. code-block:: python
with BuildPart() as pipes:
Box(10, 10, 10, rotation=(10, 20, 30))
will create a single 10x10x10 box centered at (0,0,0) - by default objects are
centered. One can create multiple objects by pushing points prior to creating
objects as follows:
.. code-block:: python
with BuildPart() as pipes:
with Locations((-10, -10, -10), (10, 10, 10)):
Box(10, 10, 10, rotation=(10, 20, 30))
which will create two boxes.
To orient a part, a ``rotation`` parameter is available on ``BuildSketch``` and
``BuildPart`` APIs. When working in a sketch, the rotation is a single angle in
degrees so the parameter is a float. When working on a part, the rotation is
a three dimensional ``Rotation`` object of the form
``Rotation(<about x>, <about y>, <about z>)`` although a simple three tuple of
floats can be used as input. As 3D rotations are not cumulative, one can
combine rotations with the `*` operator like this:
``Rotation(10, 20, 30) * Rotation(0, 90, 0)`` to generate any desired rotation.
.. hint::
Experts Only
``Locations`` will accept ``Location`` objects for input which allows one
to specify both the position and orientation. However, the orientation
is often determined by the ``Plane`` that an object was created on.
``Rotation`` is a subclass of ``Location`` and therefore will also accept
a position component.
Builder's Pending Objects
=========================
When a builder exits, it will push the object created back to its parent if
there was one. Here is an example:
.. code-block:: python
height, width, thickness, f_rad = 60, 80, 20, 10
with BuildPart() as pillow_block:
with BuildSketch() as plan:
Rectangle(width, height)
fillet(plan.vertices(), radius=f_rad)
extrude(amount=thickness)
``BuildSketch`` exits after the ``fillet`` operation and when doing so it transfers
the sketch to the ``pillow_block`` instance of ``BuildPart`` as the internal instance variable
``pending_faces``. This allows the ``extrude`` operation to be immediately invoked as it
extrudes these pending faces into ``Solid`` objects. Likewise, ``loft`` would take all of the
``pending_faces`` and attempt to create a single ``Solid`` object from them.
Normally the user will not need to interact directly with pending objects; however,
one can see pending Edges and Faces with ``<builder_instance>.pending_edges`` and
``<builder_instance>.pending_faces`` attributes. In the above example, by adding a
``print(pillow_block.pending_faces)`` prior to the ``extrude(amount=thickness)`` the
pending ``Face`` from the ``BuildSketch`` will be displayed.