mirror of
https://github.com/gumyr/build123d.git
synced 2025-12-06 02:30:55 -08:00
Refactored sweep to generate faces within a sketch
This commit is contained in:
parent
709e67cbc5
commit
c858b10e8b
11 changed files with 248 additions and 205 deletions
|
|
@ -151,7 +151,7 @@ BuildLine to BuildPart
|
||||||
**********************
|
**********************
|
||||||
|
|
||||||
The other primary reasons to use BuildLine is to create paths for BuildPart
|
The other primary reasons to use BuildLine is to create paths for BuildPart
|
||||||
:meth:`~operations_part.sweep` operations. Here some curved and straight segments
|
:meth:`~operations_generic.sweep` operations. Here some curved and straight segments
|
||||||
define a path:
|
define a path:
|
||||||
|
|
||||||
.. literalinclude:: objects_1d.py
|
.. literalinclude:: objects_1d.py
|
||||||
|
|
|
||||||
|
|
@ -48,14 +48,14 @@ implicitly - there are a couple things to consider when deciding how to proceed:
|
||||||
* Implicit parameters save some typing but hide some functionality - users have
|
* Implicit parameters save some typing but hide some functionality - users have
|
||||||
to decide what works best for them.
|
to decide what works best for them.
|
||||||
|
|
||||||
This tea cup example uses implicit parameters - note the :func:`~operations_part.sweep`
|
This tea cup example uses implicit parameters - note the :func:`~operations_generic.sweep`
|
||||||
operation on the last line:
|
operation on the last line:
|
||||||
|
|
||||||
.. literalinclude:: ../examples/tea_cup.py
|
.. literalinclude:: ../examples/tea_cup.py
|
||||||
:lines: 25-74
|
:lines: 25-74
|
||||||
:emphasize-lines: 50
|
:emphasize-lines: 50
|
||||||
|
|
||||||
:func:`~operations_part.sweep` requires a 2D cross section - ``handle_cross_section`` -
|
:func:`~operations_generic.sweep` requires a 2D cross section - ``handle_cross_section`` -
|
||||||
and a path - ``handle_path`` - which are both passed implicitly.
|
and a path - ``handle_path`` - which are both passed implicitly.
|
||||||
|
|
||||||
.. image:: tea_cup.png
|
.. image:: tea_cup.png
|
||||||
|
|
|
||||||
|
|
@ -80,6 +80,7 @@ Cheat Sheet
|
||||||
| :func:`~operations_generic.offset`
|
| :func:`~operations_generic.offset`
|
||||||
| :func:`~operations_generic.scale`
|
| :func:`~operations_generic.scale`
|
||||||
| :func:`~operations_generic.split`
|
| :func:`~operations_generic.split`
|
||||||
|
| :func:`~operations_generic.sweep`
|
||||||
|
|
||||||
.. grid-item-card:: 3D - BuildPart
|
.. grid-item-card:: 3D - BuildPart
|
||||||
|
|
||||||
|
|
@ -94,7 +95,7 @@ Cheat Sheet
|
||||||
| :func:`~operations_generic.scale`
|
| :func:`~operations_generic.scale`
|
||||||
| :func:`~operations_part.section`
|
| :func:`~operations_part.section`
|
||||||
| :func:`~operations_generic.split`
|
| :func:`~operations_generic.split`
|
||||||
| :func:`~operations_part.sweep`
|
| :func:`~operations_generic.sweep`
|
||||||
|
|
||||||
.. card:: Selectors
|
.. card:: Selectors
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -435,7 +435,7 @@ consuming, and more difficult to maintain.
|
||||||
|
|
||||||
* **Builder mode**
|
* **Builder mode**
|
||||||
|
|
||||||
The :meth:`~operations_part.sweep` method takes any pending faces and sweeps them through the provided
|
The :meth:`~operations_generic.sweep` method takes any pending faces and sweeps them through the provided
|
||||||
path (in this case the path is taken from the pending edges from ``ex14_ln``).
|
path (in this case the path is taken from the pending edges from ``ex14_ln``).
|
||||||
:meth:`~operations_part.revolve` requires a single connected wire. The pending faces must lie on the
|
:meth:`~operations_part.revolve` requires a single connected wire. The pending faces must lie on the
|
||||||
path.
|
path.
|
||||||
|
|
@ -446,7 +446,7 @@ consuming, and more difficult to maintain.
|
||||||
|
|
||||||
* **Algebra mode**
|
* **Algebra mode**
|
||||||
|
|
||||||
The :meth:`~operations_part.sweep` method takes any faces and sweeps them through the provided
|
The :meth:`~operations_generic.sweep` method takes any faces and sweeps them through the provided
|
||||||
path (in this case the path is taken from the pending edges from ``ex14_ln``).
|
path (in this case the path is taken from the pending edges from ``ex14_ln``).
|
||||||
|
|
||||||
.. literalinclude:: general_examples_algebra.py
|
.. literalinclude:: general_examples_algebra.py
|
||||||
|
|
|
||||||
|
|
@ -21,47 +21,47 @@ The following table summarizes all of the available operations. Operations marke
|
||||||
applicable to BuildLine and Algebra Curve, 2D to BuildSketch and Algebra Sketch, 3D to
|
applicable to BuildLine and Algebra Curve, 2D to BuildSketch and Algebra Sketch, 3D to
|
||||||
BuildPart and Algebra Part.
|
BuildPart and Algebra Part.
|
||||||
|
|
||||||
+----------------------------------------------+----------------------------------+----+----+----+----+------------------------+
|
+----------------------------------------------+------------------------------------+----+----+----+----+------------------------+
|
||||||
| Operation | Description | 0D | 1D | 2D | 3D | Example |
|
| Operation | Description | 0D | 1D | 2D | 3D | Example |
|
||||||
+==============================================+==================================+====+====+====+====+========================+
|
+==============================================+====================================+====+====+====+====+========================+
|
||||||
| :func:`~operations_generic.add` | Add object to builder | | ✓ | ✓ | ✓ | :ref:`16 <ex 16>` |
|
| :func:`~operations_generic.add` | Add object to builder | | ✓ | ✓ | ✓ | :ref:`16 <ex 16>` |
|
||||||
+----------------------------------------------+----------------------------------+----+----+----+----+------------------------+
|
+----------------------------------------------+------------------------------------+----+----+----+----+------------------------+
|
||||||
| :func:`~operations_generic.bounding_box` | Add bounding box as Shape | | ✓ | ✓ | ✓ | |
|
| :func:`~operations_generic.bounding_box` | Add bounding box as Shape | | ✓ | ✓ | ✓ | |
|
||||||
+----------------------------------------------+----------------------------------+----+----+----+----+------------------------+
|
+----------------------------------------------+------------------------------------+----+----+----+----+------------------------+
|
||||||
| :func:`~operations_generic.chamfer` | Bevel Vertex or Edge | | | ✓ | ✓ | :ref:`9 <ex 9>` |
|
| :func:`~operations_generic.chamfer` | Bevel Vertex or Edge | | | ✓ | ✓ | :ref:`9 <ex 9>` |
|
||||||
+----------------------------------------------+----------------------------------+----+----+----+----+------------------------+
|
+----------------------------------------------+------------------------------------+----+----+----+----+------------------------+
|
||||||
| :func:`~operations_part.extrude` | Draw 2D Shape into 3D | | | | ✓ | :ref:`3 <ex 3>` |
|
| :func:`~operations_part.extrude` | Draw 2D Shape into 3D | | | | ✓ | :ref:`3 <ex 3>` |
|
||||||
+----------------------------------------------+----------------------------------+----+----+----+----+------------------------+
|
+----------------------------------------------+------------------------------------+----+----+----+----+------------------------+
|
||||||
| :func:`~operations_generic.fillet` | Radius Vertex or Edge | | | ✓ | ✓ | :ref:`9 <ex 9>` |
|
| :func:`~operations_generic.fillet` | Radius Vertex or Edge | | | ✓ | ✓ | :ref:`9 <ex 9>` |
|
||||||
+----------------------------------------------+----------------------------------+----+----+----+----+------------------------+
|
+----------------------------------------------+------------------------------------+----+----+----+----+------------------------+
|
||||||
| :func:`~operations_part.loft` | Create 3D Shape from sections | | | | ✓ | :ref:`24 <ex 24>` |
|
| :func:`~operations_part.loft` | Create 3D Shape from sections | | | | ✓ | :ref:`24 <ex 24>` |
|
||||||
+----------------------------------------------+----------------------------------+----+----+----+----+------------------------+
|
+----------------------------------------------+------------------------------------+----+----+----+----+------------------------+
|
||||||
| :func:`~operations_part.make_brake_formed` | Create sheet metal parts | | | | ✓ | |
|
| :func:`~operations_part.make_brake_formed` | Create sheet metal parts | | | | ✓ | |
|
||||||
+----------------------------------------------+----------------------------------+----+----+----+----+------------------------+
|
+----------------------------------------------+------------------------------------+----+----+----+----+------------------------+
|
||||||
| :func:`~operations_sketch.make_face` | Create a Face from Edges | | | ✓ | | :ref:`4 <ex 4>` |
|
| :func:`~operations_sketch.make_face` | Create a Face from Edges | | | ✓ | | :ref:`4 <ex 4>` |
|
||||||
+----------------------------------------------+----------------------------------+----+----+----+----+------------------------+
|
+----------------------------------------------+------------------------------------+----+----+----+----+------------------------+
|
||||||
| :func:`~operations_sketch.make_hull` | Create Convex Hull from Edges | | | ✓ | | |
|
| :func:`~operations_sketch.make_hull` | Create Convex Hull from Edges | | | ✓ | | |
|
||||||
+----------------------------------------------+----------------------------------+----+----+----+----+------------------------+
|
+----------------------------------------------+------------------------------------+----+----+----+----+------------------------+
|
||||||
| :func:`~operations_generic.mirror` | Mirror about Plane | | ✓ | ✓ | ✓ | :ref:`15 <ex 15>` |
|
| :func:`~operations_generic.mirror` | Mirror about Plane | | ✓ | ✓ | ✓ | :ref:`15 <ex 15>` |
|
||||||
+----------------------------------------------+----------------------------------+----+----+----+----+------------------------+
|
+----------------------------------------------+------------------------------------+----+----+----+----+------------------------+
|
||||||
| :func:`~operations_generic.offset` | Inset or outset Shape | | ✓ | ✓ | ✓ | :ref:`25 <ex 25>` |
|
| :func:`~operations_generic.offset` | Inset or outset Shape | | ✓ | ✓ | ✓ | :ref:`25 <ex 25>` |
|
||||||
+----------------------------------------------+----------------------------------+----+----+----+----+------------------------+
|
+----------------------------------------------+------------------------------------+----+----+----+----+------------------------+
|
||||||
| :func:`~operations_generic.project` | Project points, lines or Faces | ✓ | ✓ | ✓ | | |
|
| :func:`~operations_generic.project` | Project points, lines or Faces | ✓ | ✓ | ✓ | | |
|
||||||
+----------------------------------------------+----------------------------------+----+----+----+----+------------------------+
|
+----------------------------------------------+------------------------------------+----+----+----+----+------------------------+
|
||||||
| :func:`~operations_part.project_workplane` | Create workplane for projection | | | | | |
|
| :func:`~operations_part.project_workplane` | Create workplane for projection | | | | | |
|
||||||
+----------------------------------------------+----------------------------------+----+----+----+----+------------------------+
|
+----------------------------------------------+------------------------------------+----+----+----+----+------------------------+
|
||||||
| :func:`~operations_part.revolve` | Swing 2D Shape about Axis | | | | ✓ | :ref:`23 <ex 23>` |
|
| :func:`~operations_part.revolve` | Swing 2D Shape about Axis | | | | ✓ | :ref:`23 <ex 23>` |
|
||||||
+----------------------------------------------+----------------------------------+----+----+----+----+------------------------+
|
+----------------------------------------------+------------------------------------+----+----+----+----+------------------------+
|
||||||
| :func:`~operations_generic.scale` | Change size of Shape | | ✓ | ✓ | ✓ | |
|
| :func:`~operations_generic.scale` | Change size of Shape | | ✓ | ✓ | ✓ | |
|
||||||
+----------------------------------------------+----------------------------------+----+----+----+----+------------------------+
|
+----------------------------------------------+------------------------------------+----+----+----+----+------------------------+
|
||||||
| :func:`~operations_part.section` | Generate 2D slices from 3D Shape | | | | ✓ | |
|
| :func:`~operations_part.section` | Generate 2D slices from 3D Shape | | | | ✓ | |
|
||||||
+----------------------------------------------+----------------------------------+----+----+----+----+------------------------+
|
+----------------------------------------------+------------------------------------+----+----+----+----+------------------------+
|
||||||
| :func:`~operations_generic.split` | Divide object by Plane | | ✓ | ✓ | ✓ | :ref:`27 <ex 27>` |
|
| :func:`~operations_generic.split` | Divide object by Plane | | ✓ | ✓ | ✓ | :ref:`27 <ex 27>` |
|
||||||
+----------------------------------------------+----------------------------------+----+----+----+----+------------------------+
|
+----------------------------------------------+------------------------------------+----+----+----+----+------------------------+
|
||||||
| :func:`~operations_part.sweep` | Extrude 2D section(s) along path | | | | ✓ | :ref:`14 <ex 14>` |
|
| :func:`~operations_generic.sweep` | Extrude 1/2D section(s) along path | | | ✓ | ✓ | :ref:`14 <ex 14>` |
|
||||||
+----------------------------------------------+----------------------------------+----+----+----+----+------------------------+
|
+----------------------------------------------+------------------------------------+----+----+----+----+------------------------+
|
||||||
| :func:`~operations_part.thicken` | Expand 2D section(s) | | | | ✓ | |
|
| :func:`~operations_part.thicken` | Expand 2D section(s) | | | | ✓ | |
|
||||||
+----------------------------------------------+----------------------------------+----+----+----+----+------------------------+
|
+----------------------------------------------+------------------------------------+----+----+----+----+------------------------+
|
||||||
|
|
||||||
Reference
|
Reference
|
||||||
^^^^^^^^^
|
^^^^^^^^^
|
||||||
|
|
@ -82,5 +82,5 @@ Reference
|
||||||
.. autoclass:: operations_generic.scale
|
.. autoclass:: operations_generic.scale
|
||||||
.. autoclass:: operations_part.section
|
.. autoclass:: operations_part.section
|
||||||
.. autoclass:: operations_generic.split
|
.. autoclass:: operations_generic.split
|
||||||
.. autoclass:: operations_part.sweep
|
.. autoclass:: operations_generic.sweep
|
||||||
.. autoclass:: operations_part.thicken
|
.. autoclass:: operations_part.thicken
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ Can't Get There from Here
|
||||||
Unfortunately, it's a reality that not all parts described using build123d can be
|
Unfortunately, it's a reality that not all parts described using build123d can be
|
||||||
successfully constructed by the underlying CAD core. Designers may have to
|
successfully constructed by the underlying CAD core. Designers may have to
|
||||||
explore different design approaches to get the OpenCascade CAD core to successfully
|
explore different design approaches to get the OpenCascade CAD core to successfully
|
||||||
build the target object. For instance, if a multi-section :func:`~operations_part.sweep`
|
build the target object. For instance, if a multi-section :func:`~operations_generic.sweep`
|
||||||
operation fails, a :func:`~operations_part.loft` operation may be a viable alternative
|
operation fails, a :func:`~operations_part.loft` operation may be a viable alternative
|
||||||
in certain situations. It's crucial to remember that CAD is a complex field and
|
in certain situations. It's crucial to remember that CAD is a complex field and
|
||||||
patience may be required to achieve the desired results.
|
patience may be required to achieve the desired results.
|
||||||
|
|
|
||||||
|
|
@ -111,7 +111,7 @@ operations_apply_to = {
|
||||||
"scale": ["BuildPart", "BuildSketch", "BuildLine"],
|
"scale": ["BuildPart", "BuildSketch", "BuildLine"],
|
||||||
"section": ["BuildPart"],
|
"section": ["BuildPart"],
|
||||||
"split": ["BuildPart", "BuildSketch", "BuildLine"],
|
"split": ["BuildPart", "BuildSketch", "BuildLine"],
|
||||||
"sweep": ["BuildPart"],
|
"sweep": ["BuildPart", "BuildSketch"],
|
||||||
"thicken": ["BuildPart"],
|
"thicken": ["BuildPart"],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ import logging
|
||||||
from typing import Union, Iterable
|
from typing import Union, Iterable
|
||||||
|
|
||||||
from build123d.build_common import Builder, LocationList, WorkplaneList, validate_inputs
|
from build123d.build_common import Builder, LocationList, WorkplaneList, validate_inputs
|
||||||
from build123d.build_enums import Keep, Kind, Mode, Side
|
from build123d.build_enums import Keep, Kind, Mode, Side, Transition
|
||||||
from build123d.build_line import BuildLine
|
from build123d.build_line import BuildLine
|
||||||
from build123d.build_part import BuildPart
|
from build123d.build_part import BuildPart
|
||||||
from build123d.build_sketch import BuildSketch
|
from build123d.build_sketch import BuildSketch
|
||||||
|
|
@ -891,3 +891,117 @@ def split(
|
||||||
return Curve(split_compound.wrapped)
|
return Curve(split_compound.wrapped)
|
||||||
else:
|
else:
|
||||||
return split_compound
|
return split_compound
|
||||||
|
|
||||||
|
|
||||||
|
#:TypeVar("SweepType"): Type of objects which can be swept
|
||||||
|
SweepType = Union[Compound, Edge, Wire, Face, Solid]
|
||||||
|
|
||||||
|
|
||||||
|
def sweep(
|
||||||
|
sections: Union[SweepType, Iterable[SweepType]] = None,
|
||||||
|
path: Union[Curve, Edge, Wire] = None,
|
||||||
|
multisection: bool = False,
|
||||||
|
is_frenet: bool = False,
|
||||||
|
transition: Transition = Transition.TRANSFORMED,
|
||||||
|
normal: VectorLike = None,
|
||||||
|
binormal: Union[Edge, Wire] = None,
|
||||||
|
clean: bool = True,
|
||||||
|
mode: Mode = Mode.ADD,
|
||||||
|
) -> Union[Part, Sketch]:
|
||||||
|
"""Generic Operation: sweep
|
||||||
|
|
||||||
|
Sweep pending 1D or 2D objects along path.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
sections (Union[Compound, Edge, Wire, Face, Solid]): cross sections to sweep into object
|
||||||
|
path (Union[Curve, Edge, Wire], optional): path to follow.
|
||||||
|
Defaults to context pending_edges.
|
||||||
|
multisection (bool, optional): sweep multiple on path. Defaults to False.
|
||||||
|
is_frenet (bool, optional): use frenet algorithm. Defaults to False.
|
||||||
|
transition (Transition, optional): discontinuity handling option.
|
||||||
|
Defaults to Transition.RIGHT.
|
||||||
|
normal (VectorLike, optional): fixed normal. Defaults to None.
|
||||||
|
binormal (Union[Edge, Wire], optional): guide rotation along path. Defaults to None.
|
||||||
|
clean (bool, optional): Remove extraneous internal structure. Defaults to True.
|
||||||
|
mode (Mode, optional): combination. Defaults to Mode.ADD.
|
||||||
|
"""
|
||||||
|
context: Builder = Builder._get_context("sweep")
|
||||||
|
|
||||||
|
section_list = (
|
||||||
|
[*sections] if isinstance(sections, (list, tuple, filter)) else [sections]
|
||||||
|
)
|
||||||
|
section_list = [sec for sec in section_list if sec is not None]
|
||||||
|
|
||||||
|
validate_inputs(context, "sweep", section_list)
|
||||||
|
|
||||||
|
if path is None:
|
||||||
|
if context is None or context is not None and not context.pending_edges:
|
||||||
|
raise ValueError("path must be provided")
|
||||||
|
path_wire = Wire.make_wire(context.pending_edges)
|
||||||
|
context.pending_edges = []
|
||||||
|
else:
|
||||||
|
path_wire = Wire.make_wire(path.edges()) if not isinstance(path, Wire) else path
|
||||||
|
|
||||||
|
if not section_list:
|
||||||
|
if (
|
||||||
|
context is not None
|
||||||
|
and isinstance(context, BuildPart)
|
||||||
|
and context.pending_faces
|
||||||
|
):
|
||||||
|
section_list = context.pending_faces
|
||||||
|
context.pending_faces = []
|
||||||
|
context.pending_face_planes = []
|
||||||
|
else:
|
||||||
|
raise ValueError("No sections provided")
|
||||||
|
|
||||||
|
edge_list = []
|
||||||
|
face_list = []
|
||||||
|
for sec in section_list:
|
||||||
|
if isinstance(sec, (Curve, Wire, Edge)):
|
||||||
|
edge_list.extend(sec.edges())
|
||||||
|
else:
|
||||||
|
face_list.extend(sec.faces())
|
||||||
|
|
||||||
|
# sweep to create solids
|
||||||
|
new_solids = []
|
||||||
|
if face_list:
|
||||||
|
if binormal is None and normal is not None:
|
||||||
|
binormal_mode = Vector(normal)
|
||||||
|
elif isinstance(binormal, Edge):
|
||||||
|
binormal_mode = Wire.make_wire([binormal])
|
||||||
|
else:
|
||||||
|
binormal_mode = binormal
|
||||||
|
if multisection:
|
||||||
|
sections = [face.outer_wire() for face in face_list]
|
||||||
|
new_solid = Solid.sweep_multi(
|
||||||
|
sections, path_wire, True, is_frenet, binormal_mode
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
for face in face_list:
|
||||||
|
new_solid = Solid.sweep(
|
||||||
|
section=face,
|
||||||
|
path=path_wire,
|
||||||
|
make_solid=True,
|
||||||
|
is_frenet=is_frenet,
|
||||||
|
mode=binormal_mode,
|
||||||
|
transition=transition,
|
||||||
|
)
|
||||||
|
new_solids.append(new_solid)
|
||||||
|
|
||||||
|
# sweep to create faces
|
||||||
|
new_faces = []
|
||||||
|
if edge_list:
|
||||||
|
for sec in section_list:
|
||||||
|
swept = Face.sweep(sec, path_wire) # Could generate a shell here
|
||||||
|
new_faces.extend(swept.faces())
|
||||||
|
|
||||||
|
if context is not None:
|
||||||
|
context._add_to_context(*(new_solids + new_faces), clean=clean, mode=mode)
|
||||||
|
elif clean:
|
||||||
|
new_solids = [solid.clean() for solid in new_solids]
|
||||||
|
new_faces = [face.clean() for face in new_faces]
|
||||||
|
|
||||||
|
if new_solids:
|
||||||
|
return Part(Compound.make_compound(new_solids).wrapped)
|
||||||
|
else:
|
||||||
|
return Sketch(Compound.make_compound(new_faces).wrapped)
|
||||||
|
|
|
||||||
|
|
@ -502,93 +502,6 @@ def section(
|
||||||
return Part(Compound.make_compound(result).wrapped)
|
return Part(Compound.make_compound(result).wrapped)
|
||||||
|
|
||||||
|
|
||||||
#:TypeVar("SweepType"): Type of objects which can be swept
|
|
||||||
SweepType = Union[Edge, Wire, Face, Solid]
|
|
||||||
|
|
||||||
|
|
||||||
def sweep(
|
|
||||||
sections: Union[SweepType, Iterable[SweepType]] = None,
|
|
||||||
path: Union[Edge, Wire] = None,
|
|
||||||
multisection: bool = False,
|
|
||||||
is_frenet: bool = False,
|
|
||||||
transition: Transition = Transition.TRANSFORMED,
|
|
||||||
normal: VectorLike = None,
|
|
||||||
binormal: Union[Edge, Wire] = None,
|
|
||||||
clean: bool = True,
|
|
||||||
mode: Mode = Mode.ADD,
|
|
||||||
) -> Part:
|
|
||||||
"""Part Operation: sweep
|
|
||||||
|
|
||||||
Sweep pending sketches/faces along path.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
sections (Union[Face, Compound]): cross sections to sweep into object
|
|
||||||
path (Union[Edge, Wire], optional): path to follow.
|
|
||||||
Defaults to context pending_edges.
|
|
||||||
multisection (bool, optional): sweep multiple on path. Defaults to False.
|
|
||||||
is_frenet (bool, optional): use frenet algorithm. Defaults to False.
|
|
||||||
transition (Transition, optional): discontinuity handling option.
|
|
||||||
Defaults to Transition.RIGHT.
|
|
||||||
normal (VectorLike, optional): fixed normal. Defaults to None.
|
|
||||||
binormal (Union[Edge, Wire], optional): guide rotation along path. Defaults to None.
|
|
||||||
clean (bool, optional): Remove extraneous internal structure. Defaults to True.
|
|
||||||
mode (Mode, optional): combination. Defaults to Mode.ADD.
|
|
||||||
"""
|
|
||||||
context: BuildPart = BuildPart._get_context("sweep")
|
|
||||||
|
|
||||||
if path is None:
|
|
||||||
path_wire = context.pending_edges_as_wire
|
|
||||||
context.pending_edges = []
|
|
||||||
else:
|
|
||||||
path_wire = Wire.make_wire([path]) if isinstance(path, Edge) else path
|
|
||||||
|
|
||||||
section_list = (
|
|
||||||
[*sections] if isinstance(sections, (list, tuple, filter)) else [sections]
|
|
||||||
)
|
|
||||||
validate_inputs(context, "sweep", section_list)
|
|
||||||
|
|
||||||
if all([s is None for s in section_list]):
|
|
||||||
if context is None or (context is not None and not context.pending_faces):
|
|
||||||
raise ValueError("No sections provided")
|
|
||||||
section_list = context.pending_faces
|
|
||||||
context.pending_faces = []
|
|
||||||
context.pending_face_planes = []
|
|
||||||
else:
|
|
||||||
section_list = [face for section in sections for face in section.faces()]
|
|
||||||
|
|
||||||
if binormal is None and normal is not None:
|
|
||||||
binormal_mode = Vector(normal)
|
|
||||||
elif isinstance(binormal, Edge):
|
|
||||||
binormal_mode = Wire.make_wire([binormal])
|
|
||||||
else:
|
|
||||||
binormal_mode = binormal
|
|
||||||
|
|
||||||
new_solids = []
|
|
||||||
if multisection:
|
|
||||||
sections = [section.outer_wire() for section in section_list]
|
|
||||||
new_solid = Solid.sweep_multi(
|
|
||||||
sections, path_wire, True, is_frenet, binormal_mode
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
for sec in section_list:
|
|
||||||
new_solid = Solid.sweep(
|
|
||||||
section=sec,
|
|
||||||
path=path_wire,
|
|
||||||
make_solid=True,
|
|
||||||
is_frenet=is_frenet,
|
|
||||||
mode=binormal_mode,
|
|
||||||
transition=transition,
|
|
||||||
)
|
|
||||||
new_solids.append(new_solid)
|
|
||||||
|
|
||||||
if context is not None:
|
|
||||||
context._add_to_context(*new_solids, clean=clean, mode=mode)
|
|
||||||
elif clean:
|
|
||||||
new_solids = [solid.clean() for solid in new_solids]
|
|
||||||
|
|
||||||
return Part(Compound.make_compound(new_solids).wrapped)
|
|
||||||
|
|
||||||
|
|
||||||
def thicken(
|
def thicken(
|
||||||
to_thicken: Union[Face, Sketch] = None,
|
to_thicken: Union[Face, Sketch] = None,
|
||||||
amount: float = None,
|
amount: float = None,
|
||||||
|
|
|
||||||
|
|
@ -614,5 +614,87 @@ class ScaleTests(unittest.TestCase):
|
||||||
scale(by="a")
|
scale(by="a")
|
||||||
|
|
||||||
|
|
||||||
|
class TestSweep(unittest.TestCase):
|
||||||
|
def test_single_section(self):
|
||||||
|
with BuildPart() as test:
|
||||||
|
with BuildLine():
|
||||||
|
Line((0, 0, 0), (0, 0, 10))
|
||||||
|
with BuildSketch():
|
||||||
|
Rectangle(2, 2)
|
||||||
|
sweep()
|
||||||
|
self.assertAlmostEqual(test.part.volume, 40, 5)
|
||||||
|
|
||||||
|
def test_multi_section(self):
|
||||||
|
segment_count = 6
|
||||||
|
with BuildPart() as handle:
|
||||||
|
with BuildLine() as handle_center_line:
|
||||||
|
Spline(
|
||||||
|
(-10, 0, 0),
|
||||||
|
(0, 0, 5),
|
||||||
|
(10, 0, 0),
|
||||||
|
tangents=((0, 0, 1), (0, 0, -1)),
|
||||||
|
tangent_scalars=(1.5, 1.5),
|
||||||
|
)
|
||||||
|
handle_path = handle_center_line.wires()[0]
|
||||||
|
for i in range(segment_count + 1):
|
||||||
|
with BuildSketch(
|
||||||
|
Plane(
|
||||||
|
origin=handle_path @ (i / segment_count),
|
||||||
|
z_dir=handle_path % (i / segment_count),
|
||||||
|
)
|
||||||
|
) as section:
|
||||||
|
if i % segment_count == 0:
|
||||||
|
Circle(1)
|
||||||
|
else:
|
||||||
|
Rectangle(1, 2)
|
||||||
|
fillet(section.vertices(), radius=0.2)
|
||||||
|
# Create the handle by sweeping along the path
|
||||||
|
sweep(multisection=True)
|
||||||
|
self.assertAlmostEqual(handle.part.volume, 54.11246334691092, 5)
|
||||||
|
|
||||||
|
def test_passed_parameters(self):
|
||||||
|
with BuildLine() as path:
|
||||||
|
Line((0, 0, 0), (0, 0, 10))
|
||||||
|
with BuildSketch() as section:
|
||||||
|
Rectangle(2, 2)
|
||||||
|
with BuildPart() as test:
|
||||||
|
sweep(section.faces(), path=path.wires()[0])
|
||||||
|
self.assertAlmostEqual(test.part.volume, 40, 5)
|
||||||
|
|
||||||
|
def test_binormal(self):
|
||||||
|
with BuildPart() as sweep_binormal:
|
||||||
|
with BuildLine() as path:
|
||||||
|
Spline((0, 0, 0), (-12, 8, 10), tangents=[(0, 0, 1), (-1, 0, 0)])
|
||||||
|
with BuildLine(mode=Mode.PRIVATE) as binormal:
|
||||||
|
Line((-5, 5), (-8, 10))
|
||||||
|
with BuildSketch() as section:
|
||||||
|
Rectangle(4, 6)
|
||||||
|
sweep(binormal=binormal.edges()[0])
|
||||||
|
|
||||||
|
end_face: Face = (
|
||||||
|
sweep_binormal.faces().filter_by(GeomType.PLANE).sort_by(Axis.X)[0]
|
||||||
|
)
|
||||||
|
face_binormal_axis = Axis(
|
||||||
|
end_face.center(), binormal.edges()[0] @ 1 - end_face.center()
|
||||||
|
)
|
||||||
|
face_normal_axis = Axis(end_face.center(), end_face.normal_at())
|
||||||
|
self.assertTrue(face_normal_axis.is_normal(face_binormal_axis))
|
||||||
|
|
||||||
|
def test_sweep_edge(self):
|
||||||
|
arc = PolarLine((1, 0), 2, 135)
|
||||||
|
arc_path = PolarLine(arc @ 1, 10, 45)
|
||||||
|
swept = sweep(sections=arc, path=arc_path)
|
||||||
|
self.assertTrue(isinstance(swept, Sketch))
|
||||||
|
self.assertAlmostEqual(swept.area, 2 * 10, 5)
|
||||||
|
|
||||||
|
def test_no_path(self):
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
sweep(PolarLine((1, 0), 2, 135))
|
||||||
|
|
||||||
|
def test_no_sections(self):
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
sweep(path=PolarLine((1, 0), 2, 135))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
||||||
|
|
@ -410,73 +410,6 @@ class TestSplit(unittest.TestCase):
|
||||||
self.assertAlmostEqual(test.part.volume, (2 / 3) * 1000 * pi, 5)
|
self.assertAlmostEqual(test.part.volume, (2 / 3) * 1000 * pi, 5)
|
||||||
|
|
||||||
|
|
||||||
class TestSweep(unittest.TestCase):
|
|
||||||
def test_single_section(self):
|
|
||||||
with BuildPart() as test:
|
|
||||||
with BuildLine():
|
|
||||||
Line((0, 0, 0), (0, 0, 10))
|
|
||||||
with BuildSketch():
|
|
||||||
Rectangle(2, 2)
|
|
||||||
sweep()
|
|
||||||
self.assertAlmostEqual(test.part.volume, 40, 5)
|
|
||||||
|
|
||||||
def test_multi_section(self):
|
|
||||||
segment_count = 6
|
|
||||||
with BuildPart() as handle:
|
|
||||||
with BuildLine() as handle_center_line:
|
|
||||||
Spline(
|
|
||||||
(-10, 0, 0),
|
|
||||||
(0, 0, 5),
|
|
||||||
(10, 0, 0),
|
|
||||||
tangents=((0, 0, 1), (0, 0, -1)),
|
|
||||||
tangent_scalars=(1.5, 1.5),
|
|
||||||
)
|
|
||||||
handle_path = handle_center_line.wires()[0]
|
|
||||||
for i in range(segment_count + 1):
|
|
||||||
with BuildSketch(
|
|
||||||
Plane(
|
|
||||||
origin=handle_path @ (i / segment_count),
|
|
||||||
z_dir=handle_path % (i / segment_count),
|
|
||||||
)
|
|
||||||
) as section:
|
|
||||||
if i % segment_count == 0:
|
|
||||||
Circle(1)
|
|
||||||
else:
|
|
||||||
Rectangle(1, 2)
|
|
||||||
fillet(section.vertices(), radius=0.2)
|
|
||||||
# Create the handle by sweeping along the path
|
|
||||||
sweep(multisection=True)
|
|
||||||
self.assertAlmostEqual(handle.part.volume, 54.11246334691092, 5)
|
|
||||||
|
|
||||||
def test_passed_parameters(self):
|
|
||||||
with BuildLine() as path:
|
|
||||||
Line((0, 0, 0), (0, 0, 10))
|
|
||||||
with BuildSketch() as section:
|
|
||||||
Rectangle(2, 2)
|
|
||||||
with BuildPart() as test:
|
|
||||||
sweep(section.faces(), path=path.wires()[0])
|
|
||||||
self.assertAlmostEqual(test.part.volume, 40, 5)
|
|
||||||
|
|
||||||
def test_binormal(self):
|
|
||||||
with BuildPart() as sweep_binormal:
|
|
||||||
with BuildLine() as path:
|
|
||||||
Spline((0, 0, 0), (-12, 8, 10), tangents=[(0, 0, 1), (-1, 0, 0)])
|
|
||||||
with BuildLine(mode=Mode.PRIVATE) as binormal:
|
|
||||||
Line((-5, 5), (-8, 10))
|
|
||||||
with BuildSketch() as section:
|
|
||||||
Rectangle(4, 6)
|
|
||||||
sweep(binormal=binormal.edges()[0])
|
|
||||||
|
|
||||||
end_face: Face = (
|
|
||||||
sweep_binormal.faces().filter_by(GeomType.PLANE).sort_by(Axis.X)[0]
|
|
||||||
)
|
|
||||||
face_binormal_axis = Axis(
|
|
||||||
end_face.center(), binormal.edges()[0] @ 1 - end_face.center()
|
|
||||||
)
|
|
||||||
face_normal_axis = Axis(end_face.center(), end_face.normal_at())
|
|
||||||
self.assertTrue(face_normal_axis.is_normal(face_binormal_axis))
|
|
||||||
|
|
||||||
|
|
||||||
class TestThicken(unittest.TestCase):
|
class TestThicken(unittest.TestCase):
|
||||||
def test_thicken(self):
|
def test_thicken(self):
|
||||||
with BuildPart() as bp:
|
with BuildPart() as bp:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue