mirror of
https://github.com/gumyr/build123d.git
synced 2025-12-06 02:30:55 -08:00
Updating surface modeling docs
This commit is contained in:
parent
283767f69d
commit
4a32cedcd2
13 changed files with 327 additions and 147 deletions
|
|
@ -1,156 +1,55 @@
|
|||
################
|
||||
#################
|
||||
Surface Modeling
|
||||
################
|
||||
#################
|
||||
|
||||
Surface modeling is employed to create objects with non-planar surfaces that can't be
|
||||
generated using functions like :func:`~operations_part.extrude`,
|
||||
:func:`~operations_generic.sweep`, or :func:`~operations_part.revolve`. Since there are no
|
||||
specific builders designed to assist with the creation of non-planar surfaces or objects,
|
||||
the following should be considered a more advanced technique.
|
||||
|
||||
As described in the `topology_` section, a BREP model consists of vertices, edges, faces,
|
||||
and other elements that define the boundary of an object. When creating objects with
|
||||
non-planar faces, it is often more convenient to explicitly create the boundary faces of
|
||||
the object. To illustrate this process, we will create the following game token:
|
||||
Surface modeling refers to the direct creation and manipulation of the skin of a 3D
|
||||
object—its bounding faces—rather than starting from volumetric primitives or solid
|
||||
operations.
|
||||
|
||||
.. raw:: html
|
||||
Instead of defining a shape by extruding or revolving a 2D profile to fill a volume,
|
||||
surface modeling focuses on building the individual curved or planar faces that together
|
||||
define the outer boundary of a part. This approach allows for precise control of complex
|
||||
freeform geometry such as aerodynamic surfaces, boat hulls, or organic transitions that
|
||||
cannot easily be expressed with simple parametric solids.
|
||||
|
||||
<script type="module" src="https://unpkg.com/@google/model-viewer/dist/model-viewer.min.js"></script>
|
||||
<model-viewer poster="_images/heart_token.png" src="_static/heart_token.glb" alt="Game Token" auto-rotate camera-controls style="width: 100%; height: 50vh;"></model-viewer>
|
||||
In build123d, as in other CAD kernels based on BREP (Boundary Representation) modeling,
|
||||
all solids are ultimately defined by their boundaries: a hierarchy of faces, edges, and
|
||||
vertices. Each face represents a finite patch of a geometric surface (plane, cylinder,
|
||||
Bézier patch, etc.) bounded by one or more edge loops or wires. When adjacent faces share
|
||||
edges consistently and close into a continuous boundary, they form a manifold
|
||||
:class:`~topology.Shell`—the watertight surface of a volume. If this shell is properly
|
||||
oriented and encloses a finite region of space, the model becomes a solid.
|
||||
|
||||
There are several methods of the :class:`~topology.Face` class that can be used to create
|
||||
non-planar surfaces:
|
||||
Surface modeling therefore operates at the most fundamental level of BREP construction.
|
||||
Rather than relying on higher-level modeling operations to implicitly generate faces,
|
||||
it allows you to construct and connect those faces explicitly. This provides a path to
|
||||
build geometry that blends analytical and freeform shapes seamlessly, with full control
|
||||
over continuity, tangency, and curvature across boundaries.
|
||||
|
||||
* :meth:`~topology.Face.make_bezier_surface`,
|
||||
* :meth:`~topology.Face.make_surface`, and
|
||||
* :meth:`~topology.Face.make_surface_from_array_of_points`.
|
||||
This section provides:
|
||||
- A concise overview of surface‑building tools in build123d
|
||||
- Hands‑on tutorials, from fundamentals to advanced techniques like Gordon surfaces
|
||||
|
||||
In this case, we'll use the ``make_surface`` method, providing it with the edges that define
|
||||
the perimeter of the surface and a central point on that surface.
|
||||
.. rubric:: Available surface methods
|
||||
|
||||
To create the perimeter, we'll use a ``BuildLine`` instance as follows. Since the heart is
|
||||
symmetric, we'll only create half of its surface here:
|
||||
Methods on :class:`~topology.Face` for creating non‑planar surfaces:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
with BuildLine() as heart_half:
|
||||
l1 = JernArc((0, 0), (1, 1.4), 40, -17)
|
||||
l2 = JernArc(l1 @ 1, l1 % 1, 4.5, 175)
|
||||
l3 = IntersectingLine(l2 @ 1, l2 % 1, other=Edge.make_line((0, 0), (0, 20)))
|
||||
l4 = ThreePointArc(l3 @ 1, Vector(0, 0, 1.5) + (l3 @ 1 + l1 @ 0) / 2, l1 @ 0)
|
||||
|
||||
Note that ``l4`` is not in the same plane as the other lines; it defines the center line
|
||||
of the heart and archs up off ``Plane.XY``.
|
||||
|
||||
.. image:: ./assets/token_heart_perimeter.png
|
||||
:align: center
|
||||
:alt: token perimeter
|
||||
|
||||
In preparation for creating the surface, we'll define a point on the surface:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
surface_pnt = l2.edge().arc_center + Vector(0, 0, 1.5)
|
||||
|
||||
We will then use this point to create a non-planar ``Face``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
top_right_surface = -Face.make_surface(heart_half.wire(), [surface_pnt]).locate(
|
||||
Pos(Z=0.5)
|
||||
)
|
||||
|
||||
.. image:: ./assets/token_half_surface.png
|
||||
:align: center
|
||||
:alt: token perimeter
|
||||
|
||||
Note that the surface was raised up by 0.5 using the locate method. Also, note that
|
||||
the ``-`` in front of ``Face`` simply flips the face normal so that the colored side
|
||||
is up, which isn't necessary but helps with viewing.
|
||||
|
||||
Now that one half of the top of the heart has been created, the remainder of the top
|
||||
and bottom can be created by mirroring:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
top_left_surface = top_right_surface.mirror(Plane.YZ)
|
||||
bottom_right_surface = top_right_surface.mirror(Plane.XY)
|
||||
bottom_left_surface = -top_left_surface.mirror(Plane.XY)
|
||||
|
||||
The sides of the heart are going to be created by extruding the outside of the perimeter
|
||||
as follows:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
left_wire = Wire([l3.edge(), l2.edge(), l1.edge()])
|
||||
left_side = Face.extrude(left_wire, (0, 0, 1)).locate(Pos(Z=-0.5))
|
||||
right_side = left_side.mirror(Plane.YZ)
|
||||
|
||||
.. image:: ./assets/token_sides.png
|
||||
:align: center
|
||||
:alt: token sides
|
||||
|
||||
With the top, bottom, and sides, the complete boundary of the object is defined. We can
|
||||
now put them together, first into a :class:`~topology.Shell` and then into a
|
||||
:class:`~topology.Solid`:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
heart = Solid(
|
||||
Shell(
|
||||
[
|
||||
top_right_surface,
|
||||
top_left_surface,
|
||||
bottom_right_surface,
|
||||
bottom_left_surface,
|
||||
left_side,
|
||||
right_side,
|
||||
]
|
||||
)
|
||||
)
|
||||
|
||||
.. image:: ./assets/token_heart_solid.png
|
||||
:align: center
|
||||
:alt: token heart solid
|
||||
* :meth:`~topology.Face.make_bezier_surface`
|
||||
* :meth:`~topology.Face.make_gordon_surface`
|
||||
* :meth:`~topology.Face.make_surface`
|
||||
* :meth:`~topology.Face.make_surface_from_array_of_points`
|
||||
* :meth:`~topology.Face.make_surface_from_curves`
|
||||
* :meth:`~topology.Face.make_surface_patch`
|
||||
|
||||
.. note::
|
||||
When creating a Solid from a Shell, the Shell must be "water-tight," meaning it
|
||||
should have no holes. For objects with complex Edges, it's best practice to reuse
|
||||
Edges in adjoining Faces whenever possible to avoid slight mismatches that can
|
||||
create openings.
|
||||
Surface modeling is an advanced technique. Robust results usually come from
|
||||
reusing the same :class:`~topology.Edge` objects across adjacent faces and
|
||||
ensuring the final :class:`~topology.Shell` is *water‑tight* or *manifold* (no gaps).
|
||||
|
||||
Finally, we'll create the frame around the heart as a simple extrusion of a planar
|
||||
shape defined by the perimeter of the heart and merge all of the components together:
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
.. code-block:: python
|
||||
tutorial_surface_heart_token.rst
|
||||
tutorial_spitfire_wing_gordon.rst
|
||||
|
||||
with BuildPart() as heart_token:
|
||||
with BuildSketch() as outline:
|
||||
with BuildLine():
|
||||
add(l1)
|
||||
add(l2)
|
||||
add(l3)
|
||||
Line(l3 @ 1, l1 @ 0)
|
||||
make_face()
|
||||
mirror(about=Plane.YZ)
|
||||
center = outline.sketch
|
||||
offset(amount=2, kind=Kind.INTERSECTION)
|
||||
add(center, mode=Mode.SUBTRACT)
|
||||
extrude(amount=2, both=True)
|
||||
add(heart)
|
||||
|
||||
Note that an additional planar line is used to close ``l1`` and ``l3`` so a ``Face``
|
||||
can be created. The :func:`~operations_generic.offset` function defines the outside of
|
||||
the frame as a constant distance from the heart itself.
|
||||
|
||||
Summary
|
||||
-------
|
||||
|
||||
In this tutorial, we've explored surface modeling techniques to create a non-planar
|
||||
heart-shaped object using build123d. By utilizing methods from the :class:`~topology.Face`
|
||||
class, such as :meth:`~topology.Face.make_surface`, we constructed the perimeter and
|
||||
central point of the surface. We then assembled the complete boundary of the object
|
||||
by creating the top, bottom, and sides, and combined them into a :class:`~topology.Shell`
|
||||
and eventually a :class:`~topology.Solid`. Finally, we added a frame around the heart
|
||||
using the :func:`~operations_generic.offset` function to maintain a constant distance
|
||||
from the heart.
|
||||
Loading…
Add table
Add a link
Reference in a new issue