Topology¶
The afem.topology
package provides tools for the creation and use of
OpenCASCADE topology (i.e., shapes). While geometry defines curves and
surfaces, topology describes their connectivity and boundary representation.
OpenCASCADE shapes are the core building blocks for building more complex parts
and assemblies. The topology entities and tools can be imported by:
from afem.topology import *
The user should review the OpenCASCADE topology documentation to become with the underlying types and data structures:
The types in afem.topology.entities
are essentially wrappers of their
underlying TopoDS_Shape
object. This is an attempt to provide a more
“Pythonic” interface for the OpenCASCADE TopoDS_Shape
types. Many tools are
provided for the user to easily create, modify, and operate on shapes. If a
tool is not available, the user can still use the pyOCCT package and access
the wrapped TopoDS_Shape
instance using the object
property of
Shape
.
A subset of afem.topology
entities and tools are featured in the following
example:
from afem.geometry import *
from afem.graphics import *
from afem.topology import *
gui = Viewer()
# Create a box by size
builder = BoxBySize(10, 10, 10)
box = builder.solid
box.set_transparency(0.5)
# Create a cylinder partially inside the box
circle = CircleByNormal((5, 5, 5), (0, 0, 1), 2).circle
face = FaceByPlanarWire(circle).face
cyl = SolidByDrag(face, (0, 0, 15)).solid
# View the two shapes
gui.add(box, cyl)
gui.start()
gui.clear()
# Fuse the shapes
fuse = FuseShapes(box, cyl)
fused_shape = fuse.shape
fused_shape.set_transparency(0.5)
gui.add(fused_shape)
gui.start()
gui.clear()
# Cut the cylinder from the box
cut = CutShapes(box, cyl)
cut_shape = cut.shape
gui.add(cut_shape)
gui.start()
gui.clear()
# Common material between the two shapes
common = CommonShapes(box, cyl)
common_shape = common.shape
# Show original box for reference
gui.add(common_shape, box)
gui.start()
gui.clear()
# Intersect the shapes
sec = IntersectShapes(box, cyl)
sec_shape = sec.shape
# Original shapes shown for reference
gui.add(sec_shape, box, cyl)
gui.start()
gui.clear()
# Split the box with the cylinder. The resulting shape is a compound with
# two solids.
split = SplitShapes(box, cyl)
split_shape = split.shape
split_shape.set_transparency(0.5)
gui.add(split_shape)
gui.start()
gui.clear()
# Locally split one face of the box with a plane
pln = PlaneByAxes((5, 5, 5), 'xz').plane
local = LocalSplit(builder.front_face, pln, box)
local_shape = local.shape
local_shape.set_transparency(0.5)
gui.add(local_shape)
gui.start()
gui.clear()
# Offset the box
offset = OffsetShape(box, 2)
offset_shape = offset.shape
offset_shape.set_transparency(0.5)
gui.add(box, offset_shape)
gui.start()
gui.clear()
# Rebuild the box with the results of the cut tool.
rebuild = RebuildShapeByTool(box, cut)
new_shape = rebuild.new_shape
gui.add(new_shape)
gui.start()
# Check the new shape for errors
check = CheckShape(new_shape)
print('Shape is valid:', check.is_valid)
print('Shape type:', new_shape.shape_type)
# Since a face is removed it is no longer a valid solid but a shell. Try to
# fix the shape.
fix = FixShape(new_shape)
fixed_shape = fix.shape
check = CheckShape(fixed_shape)
print('Shape is valid:', check.is_valid)
print('Shape type:', fixed_shape.shape_type)
gui.add(fixed_shape)
gui.start()
# Find free edges of a shape
tool = ExploreFreeEdges(fixed_shape)
gui.add(*tool.free_edges)
gui.start()
The needed entities and tools are imported by:
from afem.geometry import *
from afem.graphics import *
from afem.topology import *
A number of tools exist to create shapes but these examples the primary shapes are a solid box and a solid cylinder. The box is created by a length, width, and height:
builder = BoxBySize(10, 10, 10)
box = builder.solid
box.set_transparency(0.5)
The cylinder is created by extruding a circular face along a vector:
circle = CircleByNormal((5, 5, 5), (0, 0, 1), 2).circle
face = FaceByPlanarWire(circle).face
cyl = SolidByDrag(face, (0, 0, 15)).solid
The two shapes are shown below:
Boolean operations are some of the most commonly used and most powerful
modeling tools. The two shapes are fused together to form, in this case, a
single solid using the FuseShapes
tool:
fuse = FuseShapes(box, cyl)
fused_shape = fuse.shape
In the resulting shape, the portion of the cylinder that was inside the solid box has been removed and the union of the box and cylinder is shown below:
The cylinder is cut from the box using the CutShapes
tool:
cut = CutShapes(box, cyl)
cut_shape = cut.shape
In this tool, material from shape2
is cut away from shape1
and the
result is shown below:
Finding common material between two shapes is done by the
CommonShapes
tool:
common = CommonShapes(box, cyl)
common_shape = common.shape
This tool will find the material that is shared by both the box and the cylinder, which in this case in the segment of the cylinder inside the box. The result is shown below where the original box is shown for reference:
The intersection of shapes is done by IntersectShapes
and in this
case the resulting shape is a compound of edges:
sec = IntersectShapes(box, cyl)
sec_shape = sec.shape
Sometimes it’s possible for the result to only contain vertices if, for example, to edges are used in the intersection. The intersecting shape is shown in red in the image below:
The SplitShapes
tool splits arbitrary shapes with each other and in
this example is used to split the box with the cylinder:
split = SplitShapes(box, cyl)
split_shape = split.shape
The resulting shape in this case in a compound consisting of two different solids as shown below:
The SplitShapes
tool is very general and can be used for splitting
faces with edges, among other applications.
In some cases it may be desirable to only split a sub-shape of a basis shape.
This example uses the LocalSplit
tool to split one of the front face
of the original solid box with a plane:
pln = PlaneByAxes((5, 5, 5), 'xz').plane
local = LocalSplit(builder.front_face, pln, box)
local_shape = local.shape
The front face of the box is provided as the shape to split and the box is provided as the “basis shape”, where the shape to fit must be a sub-shape of the basis shape. The result is still a solid but with one of the faces now split:
The OffsetShape
tool is used to offset the solid box:
offset = OffsetShape(box, 2)
offset_shape = offset.shape
The resulting offset shape is shown below:
The rest of the example script demonstrates rebuilding (or substituting) shapes
using specified substitutions or, in this example, the results of a Boolean
operation. The rebuilding tools may not be used that often in practice, but
set up an example of using the CheckShape
and FixShape
tools. Since a face was removed from the solid, it is not longer a valid,
closed solid. This is detected by the CheckShape
tool and fixed with
the generic FixShape
tool. In this example the only fix was just
switching the type from a Solid
to a Shell
, but they can be
used to detect and fix more complicated errors like self-intersecting shapes or
improperly defined geometry.