diff --git a/docs/_static/spitfire_wing.glb b/docs/_static/spitfire_wing.glb
new file mode 100644
index 0000000..93c275b
Binary files /dev/null and b/docs/_static/spitfire_wing.glb differ
diff --git a/docs/assets/surface_modeling/heart_token.png b/docs/assets/surface_modeling/heart_token.png
new file mode 100644
index 0000000..24cfeb7
Binary files /dev/null and b/docs/assets/surface_modeling/heart_token.png differ
diff --git a/docs/assets/surface_modeling/spitfire_wing.png b/docs/assets/surface_modeling/spitfire_wing.png
new file mode 100644
index 0000000..1092426
Binary files /dev/null and b/docs/assets/surface_modeling/spitfire_wing.png differ
diff --git a/docs/assets/surface_modeling/spitfire_wing_profiles_guides.svg b/docs/assets/surface_modeling/spitfire_wing_profiles_guides.svg
new file mode 100644
index 0000000..2dfbd4c
--- /dev/null
+++ b/docs/assets/surface_modeling/spitfire_wing_profiles_guides.svg
@@ -0,0 +1,11 @@
+
+
\ No newline at end of file
diff --git a/docs/assets/token_half_surface.png b/docs/assets/surface_modeling/token_half_surface.png
similarity index 100%
rename from docs/assets/token_half_surface.png
rename to docs/assets/surface_modeling/token_half_surface.png
diff --git a/docs/assets/token_heart_perimeter.png b/docs/assets/surface_modeling/token_heart_perimeter.png
similarity index 100%
rename from docs/assets/token_heart_perimeter.png
rename to docs/assets/surface_modeling/token_heart_perimeter.png
diff --git a/docs/assets/token_heart_solid.png b/docs/assets/surface_modeling/token_heart_solid.png
similarity index 100%
rename from docs/assets/token_heart_solid.png
rename to docs/assets/surface_modeling/token_heart_solid.png
diff --git a/docs/assets/token_sides.png b/docs/assets/surface_modeling/token_sides.png
similarity index 100%
rename from docs/assets/token_sides.png
rename to docs/assets/surface_modeling/token_sides.png
diff --git a/docs/heart_token.py b/docs/heart_token.py
new file mode 100644
index 0000000..da11e68
--- /dev/null
+++ b/docs/heart_token.py
@@ -0,0 +1,68 @@
+# [Code]
+from build123d import *
+from ocp_vscode import show
+
+# Create the edges of one half the heart surface
+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, (0, 0, 1.5) + (l3 @ 1 + l1 @ 0) / 2, l1 @ 0)
+heart_half = Wire([l1, l2, l3, l4])
+# [SurfaceEdges]
+
+# Create a point elevated off the center
+surface_pnt = l2.arc_center + (0, 0, 1.5)
+# [SurfacePoint]
+
+# Create the surface from the edges and point
+top_right_surface = Pos(Z=0.5) * -Face.make_surface(heart_half, [surface_pnt])
+# [Surface]
+
+# Use the mirror method to create the other top and bottom surfaces
+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)
+# [Surfaces]
+
+# Create the left and right sides
+left_wire = Wire([l3, l2, l1])
+left_side = Pos(Z=-0.5) * Shell.extrude(left_wire, (0, 0, 1))
+right_side = left_side.mirror(Plane.YZ)
+# [Sides]
+
+# Put all of the faces together into a Shell/Solid
+heart = Solid(
+ Shell(
+ [
+ top_right_surface,
+ top_left_surface,
+ bottom_right_surface,
+ bottom_left_surface,
+ left_side,
+ right_side,
+ ]
+ )
+)
+# [Solid]
+
+# Build a frame around the heart
+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)
+
+heart_token.part.color = "Red"
+
+show(heart_token)
+# [End]
+# export_gltf(heart_token.part, "heart_token.glb", binary=True)
diff --git a/docs/spitfire_wing_gordon.py b/docs/spitfire_wing_gordon.py
new file mode 100644
index 0000000..8b41a0c
--- /dev/null
+++ b/docs/spitfire_wing_gordon.py
@@ -0,0 +1,77 @@
+"""
+Supermarine Spitfire Wing
+"""
+
+# [Code]
+
+from build123d import *
+from ocp_vscode import show
+
+wing_span = 36 * FT + 10 * IN
+wing_leading = 2.5 * FT
+wing_trailing = wing_span / 4 - wing_leading
+wing_leading_fraction = wing_leading / (wing_leading + wing_trailing)
+wing_tip_section = wing_span / 2 - 1 * IN # distance from root to last section
+
+# Create leading and trailing edges
+leading_edge = EllipticalCenterArc(
+ (0, 0), wing_span / 2, wing_leading, start_angle=270, end_angle=360
+)
+trailing_edge = EllipticalCenterArc(
+ (0, 0), wing_span / 2, wing_trailing, start_angle=0, end_angle=90
+)
+
+# [AirfoilSizes]
+# Calculate the airfoil sizes from the leading/trailing edges
+airfoil_sizes = []
+for i in [0, 1]:
+ tip_axis = Axis(i * (wing_tip_section, 0, 0), (0, 1, 0))
+ leading_pnt = leading_edge.intersect(tip_axis)[0]
+ trailing_pnt = trailing_edge.intersect(tip_axis)[0]
+ airfoil_sizes.append(trailing_pnt.Y - leading_pnt.Y)
+
+# [Airfoils]
+# Create the root and tip airfoils - note that they are different NACA profiles
+airfoil_root = Plane.YZ * scale(
+ Airfoil("2213").translate((-wing_leading_fraction, 0, 0)), airfoil_sizes[0]
+)
+airfoil_tip = (
+ Plane.YZ
+ * Pos(Z=wing_tip_section)
+ * scale(Airfoil("2205").translate((-wing_leading_fraction, 0, 0)), airfoil_sizes[1])
+)
+
+# [Profiles]
+# Create the Gordon surface profiles and guides
+profiles = airfoil_root.edges() + airfoil_tip.edges()
+profiles.append(leading_edge @ 1) # wing tip
+guides = [leading_edge, trailing_edge]
+# Create the wing surface as a Gordon Surface
+wing_surface = -Face.make_gordon_surface(profiles, guides)
+# Create the root of the wing
+wing_root = -Face(Wire(wing_surface.edges().filter_by(Edge.is_closed)))
+
+# [Solid]
+# Create the wing Solid
+wing = Solid(Shell([wing_surface, wing_root]))
+wing.color = 0x99A3B9 # Azure Blue
+
+show(wing)
+# [End]
+# Documentation artifact generation
+# wing_control_edges = Curve(
+# [airfoil_root, airfoil_tip, Vertex(leading_edge @ 1), leading_edge, trailing_edge]
+# )
+# visible, _ = wing_control_edges.project_to_viewport((50 * FT, -50 * FT, 50 * FT))
+# max_dimension = max(*Compound(children=visible).bounding_box().size)
+# svg = ExportSVG(scale=100 / max_dimension)
+# svg.add_shape(visible)
+# svg.write("assets/surface_modeling/spitfire_wing_profiles_guides.svg")
+
+# export_gltf(
+# wing,
+# "assets/surface_modeling/spitfire_wing.glb",
+# binary=True,
+# linear_deflection=0.1,
+# angular_deflection=1,
+# )
diff --git a/docs/tutorial_surface_heart_token.rst b/docs/tutorial_surface_heart_token.rst
new file mode 100644
index 0000000..2c45f62
--- /dev/null
+++ b/docs/tutorial_surface_heart_token.rst
@@ -0,0 +1,125 @@
+##################################
+Tutorial: Heart Token (Basics)
+##################################
+
+This hands‑on tutorial introduces the fundamentals of surface modeling by building
+a heart‑shaped token from a small set of non‑planar faces. We’ll create
+non‑planar surfaces, mirror them, add side faces, and assemble a closed shell
+into a solid.
+
+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:
+
+.. raw:: html
+
+
+
+
+Useful :class:`~topology.Face` creation methods include
+:meth:`~topology.Face.make_surface`, :meth:`~topology.Face.make_bezier_surface`,
+and :meth:`~topology.Face.make_surface_from_array_of_points`. See the
+:doc:`surface_modeling` overview for the full list.
+
+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.
+
+To create the perimeter, we'll define the perimeter edges. Since the heart is
+symmetric, we'll only create half of its surface here:
+
+.. literalinclude:: heart_token.py
+ :start-after: [Code]
+ :end-before: [SurfaceEdges]
+
+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/surface_modeling/token_heart_perimeter.png
+ :align: center
+ :alt: token perimeter
+
+In preparation for creating the surface, we'll define a point on the surface:
+
+.. literalinclude:: heart_token.py
+ :start-after: [SurfaceEdges]
+ :end-before: [SurfacePoint]
+
+We will then use this point to create a non-planar ``Face``:
+
+.. literalinclude:: heart_token.py
+ :start-after: [SurfacePoint]
+ :end-before: [Surface]
+
+.. image:: ./assets/surface_modeling/token_half_surface.png
+ :align: center
+ :alt: token perimeter
+
+Note that the surface was raised up by 0.5 using an Algebra expression with Pos. 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:
+
+.. literalinclude:: heart_token.py
+ :start-after: [Surface]
+ :end-before: [Surfaces]
+
+The sides of the heart are going to be created by extruding the outside of the perimeter
+as follows:
+
+.. literalinclude:: heart_token.py
+ :start-after: [Surfaces]
+ :end-before: [Sides]
+
+.. image:: ./assets/surface_modeling/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`:
+
+.. literalinclude:: heart_token.py
+ :start-after: [Sides]
+ :end-before: [Solid]
+
+.. image:: ./assets/surface_modeling/token_heart_solid.png
+ :align: center
+ :alt: token heart solid
+
+.. 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.
+
+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:
+
+.. literalinclude:: heart_token.py
+ :start-after: [Solid]
+ :end-before: [End]
+
+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.
+
+Next steps
+----------
+
+Continue to :doc:`tutorial_heart_token` for an advanced example using
+:meth:`~topology.Face.make_gordon_surface` to create a Supermarine Spitfire wing.
diff --git a/docs/tutorial_surface_modeling.rst b/docs/tutorial_surface_modeling.rst
index 08ed253..4d8fca0 100644
--- a/docs/tutorial_surface_modeling.rst
+++ b/docs/tutorial_surface_modeling.rst
@@ -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.
-
-
+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.
\ No newline at end of file
diff --git a/examples/tea_cup.py b/examples/tea_cup.py
index 866ee1f..8bc8ed6 100644
--- a/examples/tea_cup.py
+++ b/examples/tea_cup.py
@@ -4,19 +4,19 @@ name: tea_cup.py
by: Gumyr
date: March 27th 2023
-desc: This example demonstrates the creation a tea cup, which serves as an example of
+desc: This example demonstrates the creation a tea cup, which serves as an example of
constructing complex, non-flat geometrical shapes programmatically.
The tea cup model involves several CAD techniques, such as:
- - Revolve Operations: There is 1 occurrence of a revolve operation. This is used
- to create the main body of the tea cup by revolving a profile around an axis,
+ - Revolve Operations: There is 1 occurrence of a revolve operation. This is used
+ to create the main body of the tea cup by revolving a profile around an axis,
a common technique for generating symmetrical objects like cups.
- Sweep Operations: There are 2 occurrences of sweep operations. The handle are
created by sweeping a profile along a path to generate non-planar surfaces.
- Offset/Shell Operations: the bowl of the cup is hollowed out with the offset
- operation leaving the top open.
- - Fillet Operations: There is 1 occurrence of a fillet operation which is used to
- round the edges for aesthetic improvement and to mimic real-world objects more
+ operation leaving the top open.
+ - Fillet Operations: There is 1 occurrence of a fillet operation which is used to
+ round the edges for aesthetic improvement and to mimic real-world objects more
closely.
license: