Native geometry operations backed by Open CASCADE Technology (OCCT).
Calls run immediately and return {:ok, value} or {:error, reason}.
Modeling operations return an opaque OCEx.Shape; query functions return
measurements, topology, or mesh data. Inputs remain usable after an operation.
Coordinates and numbers
Points and vectors are {x, y, z} tuples. Use millimeters when working with
Smith or STEP exchange. Pure geometry operations use the numbers supplied;
shapes do not carry a unit tag. Modeling angles are degrees, while mesh
angular deflection is in radians.
The native boundary accepts finite floats and signed 64-bit integers. Most positive dimensions and direction magnitudes must exceed 1.0e-7. Each operation documents further restrictions.
Shape ownership
Shapes are garbage-collected native resources. Extract edges with edges/1
from the body you intend to fillet or chamfer. Every modeling operation
creates a new selection identity, even if the resulting geometry looks
unchanged. A BREP round trip also creates a new identity.
For pipeline-based recipes, sketches, assemblies, and print bundles, use Smith. For native setup and execution limits, read installation and errors and lifetimes.
Summary
Primitives
Creates a box with its minimum corner at the origin.
Creates a cone or frustum centered on world Z.
Creates a solid cylinder centered on world Z, from Z=0 to height.
Creates a solid sphere centered at the origin.
Profiles
Creates a directed circular arc in a world-coordinate plane.
Creates a full circular edge in XY, centered at the origin.
Creates a straight edge from from to to, in world coordinates.
Builds one planar face bounded by a closed wire.
Sews a nonempty list of faces into surfaces at 1.0e-7 mm tolerance.
Interpolates a nonperiodic B-spline through ordered world points.
Joins a nonempty list of edges into a wire.
Modeling
Bevels a nonempty selection of edges with a common distance.
Merges adjacent faces and edges that share underlying geometry.
Returns the geometric intersection of two shapes.
Copies shapes into a compound without joining their boundaries.
Subtracts tool from body, preserving both inputs.
Tapers selected faces of one solid around a neutral plane.
Extrudes a planar face or a nonempty compound of planar faces along a world vector.
Extrudes planar faces with symmetric extent and tapered walls.
Extrudes planar faces along a direction until they meet an infinite plane.
Rounds a nonempty selection of edges with a common radius.
Unites two shapes, preserving both inputs.
Builds a capped loft through at least two closed wires.
Builds a parallel surface or expands/contracts a solid by a signed distance.
Projects edges or wire boundaries onto target surfaces, returning wires.
Revolves a face around a world axis.
Intersects solid material with an infinite plane, returning filled planar faces.
Hollows a solid by removing selected faces and offsetting the remaining walls.
Splits solids with an infinite plane through origin along normal.
Sweeps one closed planar wire along an open wire, returning a capped solid.
Builds solid material between an open surface and its signed offset.
Transforms
Returns a copy rotated about a world axis, in degrees.
Returns a copy uniformly scaled about the world origin.
Returns a copy translated by a world-coordinate vector.
Topology
Returns unique edges contained in the shape, including the shape itself if applicable.
Returns unique faces contained in the shape, including the shape itself if applicable.
Compares native topology identity and location, ignoring orientation.
Returns the top-level topology kind.
Returns unique shells contained in the shape, including the shape itself if applicable.
Returns unique solids contained in the shape, including the shape itself if applicable.
Runs OCCT's shape analyzer and returns a boolean result.
Returns unique vertices contained in the shape, including the shape itself if applicable.
Returns unique wires contained in the shape, including the shape itself if applicable.
Measurements
Measures the sum of face areas, in square model units.
Returns world-axis-aligned bounds as {:ok, {minimum, maximum}}.
Returns the volume centroid in world coordinates.
Measures the minimum distance between a shape and a world point.
Describes an edge's curve and directed endpoints.
Samples a directed edge at a fraction of its parameter interval.
Describes a face's surface and area properties.
Measures the sum of edge lengths, in model units.
Returns the world coordinates of a vertex.
Measures signed volume from closed shells, in cubic model units.
Exchange
Creates an orthographic drawing with separate visible and hidden curves.
Reads OCCT text BREP from a binary into a new native resource.
Tessellates a copy of a shape into indexed triangles.
Samples each nondegenerate edge into an ordered list of 3D points.
Imports geometry from a STEP file.
Serializes a shape to OCCT text BREP in a binary.
Writes geometry-only STEP to a path, returning {:ok, :ok}.
Writes binary STL from a tessellated copy, returning {:ok, :ok}.
Types
A point or vector returned by the native kernel.
Functions
Reflects a shape across the plane through origin with the given normal.
Creates a complete ring torus centered at the origin, around world Z.
Primitives
@spec box(number(), number(), number()) :: result(OCEx.Shape.t())
Creates a box with its minimum corner at the origin.
The dimensions are its X, Y, and Z extents. Each must exceed 1.0e-7 model
units. The result is a solid; negative dimensions do not reverse its direction.
Use translate/2 to move it.
Examples
iex> {:ok, box} = OCEx.box(10, 20, 3)
iex> {:ok, volume} = OCEx.volume(box)
iex> Float.round(volume, 6)
600.0
iex> OCEx.box(10, 20, 0)
{:error, :invalid_argument}
@spec cone(number(), number(), number()) :: result(OCEx.Shape.t())
Creates a cone or frustum centered on world Z.
bottom is the radius at Z=0; top is the radius at Z=height.
Both radii must be nonnegative and differ by more than 1.0e-7 model units.
One radius may be zero. height must exceed 1.0e-7. Use cylinder/2
when the radii are equal.
@spec cylinder(number(), number()) :: result(OCEx.Shape.t())
Creates a solid cylinder centered on world Z, from Z=0 to height.
radius and height must each exceed 1.0e-7 model units. Its X and Y bounds
are -radius..radius. Use rotate/4 for another axis.
@spec sphere(number()) :: result(OCEx.Shape.t())
Creates a solid sphere centered at the origin.
radius must exceed 1.0e-7 model units.
Profiles
Creates a directed circular arc in a world-coordinate plane.
center is the circle center. normal and x_direction define its
frame: they must be nonzero and nonparallel. OCCT projects x_direction
onto the plane. The start angle is measured from that projected direction.
start and sweep are degrees. Positive sweep follows the right-hand
rule around normal; negative sweep reverses it. The absolute sweep must
be greater than 1.0e-9 and at most 360. The radius must exceed 1.0e-7 model
units. Returns an edge, including for a full turn.
@spec circle(number()) :: result(OCEx.Shape.t())
Creates a full circular edge in XY, centered at the origin.
radius must exceed 1.0e-7 model units. To make a disk, wrap the edge in
a wire and then a face:
iex> {:ok, circle} = OCEx.circle(3)
iex> {:ok, wire} = OCEx.wire([circle])
iex> {:ok, disk} = OCEx.face(wire)
iex> OCEx.shape_type(disk)
{:ok, :face}
@spec edge(point3(), point3()) :: result(OCEx.Shape.t())
Creates a straight edge from from to to, in world coordinates.
The points must be separated by more than 1.0e-7 model units. The edge is
directed from the first point to the second; edge_info/1 and
edge_sample/2 respect that direction.
@spec face(OCEx.Shape.t()) :: result(OCEx.Shape.t())
Builds one planar face bounded by a closed wire.
An open wire returns {:error, :open_wire}. A nonplanar or invalid
boundary fails during construction or shape validation. This constructor
accepts one outer wire; use Boolean subtraction to add holes.
@spec sew([OCEx.Shape.t()]) :: result(OCEx.Shape.t())
Sews a nonempty list of faces into surfaces at 1.0e-7 mm tolerance.
Shared boundary edges are joined. Returns a face, a shell, or a compound of disconnected surfaces; it does not fill closed shells into solids. Faces may come from different shape revisions and remain unchanged. Supply coherently oriented faces when the surface's normal matters.
Empty input returns :empty_selection, repeated topology returns
:duplicate_subshape, non-face members return :wrong_shape_type,
and non-manifold joins return :non_manifold_surface. Native sewing
or validation failures are tagged errors.
@spec spline([point3()], {point3(), point3()} | nil) :: result(OCEx.Shape.t())
Interpolates a nonperiodic B-spline through ordered world points.
Supply 2 to 100,000 points. Consecutive points must be more than 1.0e-6 model units apart. OCCT uses chord-length parameters and an interpolation tolerance of 1.0e-6.
tangents is either nil (unconstrained endpoints) or
{start_tangent, end_tangent}. Each tangent is a nonzero world vector;
OCCT scales its magnitude. Returns an edge, not a control-point polygon.
@spec wire([OCEx.Shape.t()]) :: result(OCEx.Shape.t())
Joins a nonempty list of edges into a wire.
Supply edges in connected order. Each added edge must connect to the
wire built so far; otherwise the call returns {:error, :disconnected_wire}.
Open wires are allowed. The result need not be planar, but face/1
requires a closed planar boundary.
Modeling
@spec chamfer(OCEx.Shape.t(), [OCEx.Shape.t()], number()) :: result(OCEx.Shape.t())
Bevels a nonempty selection of edges with a common distance.
distance must exceed 1.0e-7 model units. This is an equal-distance
chamfer; angle/distance and two-distance variants are not exposed.
Selections have the same ownership and duplicate checks as fillet/3.
The kernel may reject a distance that does not fit the adjoining faces.
@spec clean(OCEx.Shape.t()) :: result(OCEx.Shape.t())
Merges adjacent faces and edges that share underlying geometry.
This includes concatenating compatible B-splines. Returns a new native resource even when no simplification is possible. It can change edge and face counts, so query selections from the returned shape.
Boolean operations in OCEx do not call this automatically.
@spec common(OCEx.Shape.t(), OCEx.Shape.t()) :: result(OCEx.Shape.t())
Returns the geometric intersection of two shapes.
Disjoint inputs produce an empty compound. A successful result can have
zero volume; query its topology before using it as a solid. Neither input
is changed, and same-domain cleanup is left to clean/1.
@spec compound([OCEx.Shape.t()]) :: result(OCEx.Shape.t())
Copies shapes into a compound without joining their boundaries.
The list may mix topology kinds or be empty. Overlapping solids retain
their individual volumes and faces; use fuse/2 to combine material.
iex> {:ok, empty} = OCEx.compound([])
iex> OCEx.solids(empty)
{:ok, []}
iex> OCEx.bounds(empty)
{:error, :empty_shape}
@spec cut(OCEx.Shape.t(), OCEx.Shape.t()) :: result(OCEx.Shape.t())
Subtracts tool from body, preserving both inputs.
A missed tool can leave the geometry unchanged; removing the entire
body can return an empty compound. The result is not guaranteed to be
a single solid. Call clean/1 to merge same-domain faces afterward.
@spec draft(OCEx.Shape.t(), [OCEx.Shape.t()], point3(), number(), point3(), point3()) :: result(OCEx.Shape.t())
Tapers selected faces of one solid around a neutral plane.
A compound wrapping exactly one solid is also accepted. Collections with
multiple solids or free faces/edges return :wrong_shape_type.
pull is a nonzero direction vector; angle is in degrees, strictly
between -90 and 90. neutral_origin and neutral_normal define the
world plane where the selected surfaces retain their intersection.
The pull vector must not lie in that plane. Positive angles remove
material on the pull side of the neutral plane; negative angles add it.
Zero returns an independent copy after validating the inputs.
Faces must be planar, cylindrical, or conical and belong to this exact
solid revision. Empty, foreign, and duplicate selections return
:empty_selection, :foreign_subshape, or :duplicate_subshape.
Unsupported surfaces return :unsupported_draft_surface.
OCCT propagates draft through tangent-connected faces; those faces must
also support drafting. The operation cannot handle a taper that requires
a topology change, such as collapsing an edge or deleting a face.
Build failures return :draft_failed or a native geometry error.
Invalid angles/vectors return :invalid_argument; a pull direction
parallel to the neutral plane returns :invalid_direction.
The input solid and selected faces remain unchanged.
@spec extrude(OCEx.Shape.t(), point3()) :: result(OCEx.Shape.t())
Extrudes a planar face or a nonempty compound of planar faces along a world vector.
The vector may be oblique to the face, but its normal component must
exceed 1.0e-7 model units in magnitude. A vector lying in the face's plane
returns {:error, :degenerate_extrusion}; a nonplanar face returns
{:error, :non_planar_profile}. Face holes pass through the extrusion.
Each face produces a solid. Compounds retain separate results; they are
not fused. Collections containing edges, solids, or nonplanar faces fail.
@spec extrude(OCEx.Shape.t(), point3(), keyword()) :: result(OCEx.Shape.t())
Extrudes planar faces with symmetric extent and tapered walls.
Options are both: false and taper: 0. With both: true, the
full vector applies in each direction, doubling the extent. Each profile
still produces one solid; disconnected faces remain separate solids.
Taper is in degrees, strictly between −90 and 90. Positive taper removes material away from the starting plane: outer walls narrow and holes widen. Negative taper adds material. Symmetric taper applies the same angle to both halves with the original profile as their shared neutral section.
Nonzero taper requires travel perpendicular to the profile plane and
planar or cylindrical prism walls. Unsupported curves return
:unsupported_draft_surface; oblique taper returns
:invalid_taper_direction. Collapsing walls or topology changes may
return :draft_failed or :invalid_solid. This is not a loft through
scaled sections. Zero taper retains the prism behavior of extrude/2.
Invalid option lists return :invalid_options; out-of-range angles
return :invalid_argument. Inputs remain unchanged.
@spec extrude_until(OCEx.Shape.t(), point3(), point3(), point3()) :: result(OCEx.Shape.t())
Extrudes planar faces along a direction until they meet an infinite plane.
origin and normal define the target plane in world coordinates.
Direction and normal are nonzero vectors and are normalized. The entire
profile must reach the target in the positive travel direction, at a
distance greater than 1.0e-7 model units. The target may be tilted, but
may not cross or touch the starting profile. Reversing the target normal
does not change the result; reverse the travel direction to extrude backwards.
Holes and disconnected faces are preserved. Each face produces a separate solid. Travel may be oblique to the profile; walls remain straight and untapered. This operation targets a plane, not the nearest face of a body.
A parallel target returns :invalid_direction; a target not strictly
ahead returns :target_not_ahead. Input topology and profile errors
follow extrude/2. Invalid vectors return :invalid_argument.
Inputs remain unchanged.
@spec fillet(OCEx.Shape.t(), [OCEx.Shape.t()], number()) :: result(OCEx.Shape.t())
Rounds a nonempty selection of edges with a common radius.
radius must exceed 1.0e-7 model units. Obtain the edges from this exact
body using edges/1. Stale or foreign edges return
:foreign_subshape; repeated selections return :duplicate_subshape.
An empty selection returns :invalid_argument.
The kernel may reject a radius that cannot fit the surrounding geometry.
Inputs remain unchanged. Call clean/1 if further topology simplification
is needed.
@spec fuse(OCEx.Shape.t(), OCEx.Shape.t()) :: result(OCEx.Shape.t())
Unites two shapes, preserving both inputs.
Disjoint solids remain separate solids in the result. Touching or
overlapping solids are combined where the Boolean algorithm permits.
Call clean/1 explicitly to merge same-domain faces and edges.
@spec loft([OCEx.Shape.t()], keyword()) :: result(OCEx.Shape.t())
Builds a capped loft through at least two closed wires.
The only option is ruled: true (default), which joins sections with
straight generators. ruled: false fits a smooth surface through them;
it can overshoot between sections and does not guarantee a particular
continuity at caps or seams. Unknown, duplicate, or invalid options return
:invalid_options.
List sections in loft order. Each wire supplies one boundary; holes,
guide rails, and seam controls are not supported.
OCCT determines correspondence between section edges. An open section
returns {:error, :open_wire}. Degenerate section arrangements may fail
kernel construction or validation.
@spec offset(OCEx.Shape.t(), number(), keyword()) :: result(OCEx.Shape.t())
Builds a parallel surface or expands/contracts a solid by a signed distance.
Accepts faces, shells, solids, or compounds of those shapes. Positive distance follows surface normals, outward for an oriented solid; negative distance goes inward. Its magnitude must exceed 1.0e-7 mm. This is a 3D surface offset, not a planar outline offset.
join: :arc (default) fills convex gaps with rounded transitions;
:intersection extends adjacent offset surfaces until they meet.
Compound members are offset independently and are not fused. Sew faces
first with sew/1 when adjacent faces must offset as one shell.
For solid inputs, the result must have positive volume and preserve
directional containment: outward results contain the original; inward
results stay inside it. Volume changes and containment use a tolerance
of max(1.0e-9 mm³, original volume * 1.0e-9). Violations return
:invalid_offset. A complete collapse is an error, not an empty result.
OCCT requires sufficiently smooth surfaces and offsets small enough to
avoid inversion or self-intersection. C0 spline surfaces and complex
intersections may fail. Global self-intersection repair is not enabled;
successful BREP validation does not prove absence of every geometric
self-intersection. Inputs remain unchanged. Unknown/duplicate options
return :invalid_options; invalid distances return :invalid_argument.
@spec project(OCEx.Shape.t(), OCEx.Shape.t(), keyword()) :: result(OCEx.Shape.t())
Projects edges or wire boundaries onto target surfaces, returning wires.
Supply exactly one option: direction: {x, y, z} for parallel projection,
or from: {x, y, z} for conical projection through a world point.
Parallel directions are nonzero and normalized. Input coordinates and
resulting curves remain in world space.
Sources may be edges, wires, faces, or compounds of those types. A face contributes every boundary wire, including holes; it does not contribute filled material. Targets may be faces, shells, solids, or collections of those types. Free edges in a target collection are rejected.
Returns one wire or a compound of wires. Curves are clipped to target face boundaries. Multiple target intersections are retained; this is not a nearest-hit selection. Parallel projection is bidirectional, so reversing its direction does not select the opposite side of a solid. Select target faces before projection when only one surface is wanted. Conical projection follows half-rays from the point through the source; it can hit before or beyond the source, but not behind the point.
A failed or missed source boundary returns :projection_failed; source
collections are not partially accepted after a failed boundary. Successful
partial intersections may yield open wires. No faces, shells, or solids
are filled from the projected curves. Coincident target/sweep surfaces may
return their boundary edges rather than isolated intersection curves.
Degenerate geometry can fail.
Unknown, duplicate, or missing options return :invalid_options.
Bad vectors/points return :invalid_argument; unsupported topology
returns :wrong_shape_type. Both inputs remain unchanged.
@spec revolve(OCEx.Shape.t(), point3(), point3(), number()) :: result(OCEx.Shape.t())
Revolves a face around a world axis.
origin is a point on the axis; axis is its nonzero direction vector.
degrees must be greater than 1.0e-7 and at most 360. Rotation follows
the right-hand rule; reverse the axis for the opposite turn.
The profile and sweep must form valid geometry. This binding validates
the returned shape but does not separately assert a single positive-volume
solid. Inspect solids/1 and volume/1 when that is required.
@spec section(OCEx.Shape.t(), point3(), point3()) :: result(OCEx.Shape.t())
Intersects solid material with an infinite plane, returning filled planar faces.
origin and normal define a world plane; the nonzero normal is
normalized. Accepts the same solid collections as split/4. Holes and
disconnected material regions are preserved. Returns one face or a
compound of zero/multiple faces. An outside plane or contact only at
points/edges gives an empty compound; a coincident boundary face remains.
Face normals follow the supplied plane normal. Coordinates remain in
world space. Use area/1, wires/1, or extrude/2 on the result.
Inputs remain unchanged. Invalid plane arguments return
:invalid_argument; unsupported input topology returns
:wrong_shape_type. This operation does not project geometry.
@spec shell(OCEx.Shape.t(), [OCEx.Shape.t()], number(), keyword()) :: result(OCEx.Shape.t())
Hollows a solid by removing selected faces and offsetting the remaining walls.
A compound wrapping exactly one solid is also accepted. Free faces/edges
or multiple solids return :wrong_shape_type.
thickness is signed: negative builds inward, positive outward. Its
magnitude must exceed 1.0e-7 model units. At least one opening is required.
Faces must come from this exact body revision; foreign and duplicate faces
return :foreign_subshape and :duplicate_subshape. The input is copied,
including the correspondence of selected faces, before native construction.
join: :arc (default) rounds gaps between offset surfaces;
join: :intersection extends adjacent surfaces to their intersection.
Concave details, small radii, and excessive thickness can make the operation
fail. Only valid, positive-volume solid results are returned. Inward
results must remove material and stay inside the source solid within a
volume tolerance of max(1.0e-9, source volume * 1.0e-9); failures return
:invalid_thickness. General
self-intersection repair, closed cavities, and face thickening are not
exposed by this operation.
@spec split(OCEx.Shape.t(), point3(), point3(), keyword()) :: result(OCEx.Shape.t())
Splits solids with an infinite plane through origin along normal.
The plane normal is normalized. keep: accepts :both (default),
:positive, or :negative; positive means the side toward the normal.
Accepts a solid or a nonempty compound/compsolid containing only solids.
Free faces and edges are rejected with :wrong_shape_type.
Returns a solid for one remaining piece or a compound for zero/multiple pieces. Both sides retain separate solids at the cut. A plane outside the body preserves the whole body on its side and leaves the opposite side empty. Boundary-only contact does not produce zero-volume solids. Inputs remain unchanged; output topology belongs to a new revision.
Invalid planes return :invalid_argument; unknown, duplicate, or invalid
options return :invalid_options. Kernel failures are tagged errors.
@spec sweep(OCEx.Shape.t(), OCEx.Shape.t(), keyword()) :: result(OCEx.Shape.t())
Sweeps one closed planar wire along an open wire, returning a capped solid.
Place the profile in the plane through the path's starting vertex,
perpendicular to its starting tangent. Its in-plane offset is retained;
OCEx does not center or rotate the profile automatically. Misplacement
returns :misaligned_profile. The spine must be a connected, nonbranching
wire. Closed paths and profiles with holes are not supported by this binding.
Options:
:frame—:corrected(default, corrected Frenet) or:frenet. Controls how the section turns along a curved path.:transition—:transformed(default),:right(intersect adjoining swept segments), or:round(rotate around the corner). Sharp corners may fail or self-intersect; use tangent-continuous paths for predictable results.
Both inputs are copied before construction. Invalid geometry returns a tagged kernel error. Successful BREP validation does not prove that an arbitrary sweep is free of geometric self-intersections.
@spec thicken(OCEx.Shape.t(), number(), keyword()) :: result(OCEx.Shape.t())
Builds solid material between an open surface and its signed offset.
Accepts a face, an open shell, or a compound of these. Thickness magnitude must exceed 1.0e-7 mm; positive follows oriented surface normals and negative goes against them. The original surface forms one boundary, and free edges receive connecting walls. Holes in faces remain holes.
join: :intersection (default) extends adjacent offset surfaces;
:arc uses rounded transitions where applicable. Sew connected faces
with sew/1 first. Disconnected compound members produce separate
solids without fusing. Solid inputs return :wrong_shape_type; closed
shells return :closed_shell.
Results must contain positive-volume solids and pass native validation.
Surface smoothness and self-intersection limits follow offset/3.
Excessive thickness may collapse or invert curved features and fail with
:thicken_failed, :invalid_solid, or another native geometry error.
No global self-intersection repair or variable wall thickness is provided.
Input geometry remains unchanged.
Transforms
@spec rotate(OCEx.Shape.t(), point3(), point3(), number()) :: result(OCEx.Shape.t())
Returns a copy rotated about a world axis, in degrees.
origin is a point on the axis and axis is a nonzero vector.
Positive angles follow the right-hand rule; negative and zero angles are
allowed. Rotation moves the whole shape, including its position relative
to origin.
@spec scale(OCEx.Shape.t(), number()) :: result(OCEx.Shape.t())
Returns a copy uniformly scaled about the world origin.
factor must exceed 1.0e-7. Positions and lengths scale by this factor,
areas by its square, and volumes by its cube. Negative factors and
nonuniform scaling are not supported.
@spec translate(OCEx.Shape.t(), point3()) :: result(OCEx.Shape.t())
Returns a copy translated by a world-coordinate vector.
A zero vector is allowed. Selections from the input cannot be used to fillet or chamfer the copy.
Topology
@spec edges(OCEx.Shape.t()) :: result([OCEx.Shape.t()])
Returns unique edges contained in the shape, including the shape itself if applicable.
Returns {:ok, shapes}; no matches gives {:ok, []}. Uniqueness is
by native topology identity and location, not geometric equivalence.
List order is not a persistent naming scheme. Query again after each
modeling operation, especially before selecting edges for finishing.
@spec faces(OCEx.Shape.t()) :: result([OCEx.Shape.t()])
Returns unique faces contained in the shape, including the shape itself if applicable.
Returns {:ok, shapes}; no matches gives {:ok, []}. Uniqueness is
by native topology identity and location, not geometric equivalence.
List order is not a persistent naming scheme. Query again after each
modeling operation, especially before selecting edges for finishing.
@spec same?(OCEx.Shape.t(), OCEx.Shape.t()) :: result(boolean())
Compares native topology identity and location, ignoring orientation.
This uses OCCT's IsSame comparison. It does not compare dimensions or
boundary geometry. Independently constructed equal boxes are different;
two queries for the same subshape can be the same despite having distinct
Elixir resource handles.
iex> {:ok, a} = OCEx.box(1, 2, 3)
iex> {:ok, b} = OCEx.box(1, 2, 3)
iex> OCEx.same?(a, a)
{:ok, true}
iex> OCEx.same?(a, b)
{:ok, false}
@spec shape_type(OCEx.Shape.t()) :: result(atom())
Returns the top-level topology kind.
Possible kinds are :compound, :compsolid, :solid, :shell,
:face, :wire, :edge, :vertex, and :shape. A compound may
contain one or many solids; use solids/1 to count them.
@spec shells(OCEx.Shape.t()) :: result([OCEx.Shape.t()])
Returns unique shells contained in the shape, including the shape itself if applicable.
Returns {:ok, shapes}; no matches gives {:ok, []}. Uniqueness is
by native topology identity and location, not geometric equivalence.
List order is not a persistent naming scheme. Query again after each
modeling operation, especially before selecting edges for finishing.
@spec solids(OCEx.Shape.t()) :: result([OCEx.Shape.t()])
Returns unique solids contained in the shape, including the shape itself if applicable.
Returns {:ok, shapes}; no matches gives {:ok, []}. Uniqueness is
by native topology identity and location, not geometric equivalence.
List order is not a persistent naming scheme. Query again after each
modeling operation, especially before selecting edges for finishing.
@spec valid?(OCEx.Shape.t()) :: result(boolean())
Runs OCCT's shape analyzer and returns a boolean result.
New shapes exposed by OCEx already pass this check. Validity describes kernel topology and geometry consistency; an empty compound or an edge can be valid. It does not establish solid count, dimensions, clearance, mesh quality, or printability.
@spec vertices(OCEx.Shape.t()) :: result([OCEx.Shape.t()])
Returns unique vertices contained in the shape, including the shape itself if applicable.
Returns {:ok, shapes}; no matches gives {:ok, []}. Uniqueness is
by native topology identity and location, not geometric equivalence.
List order is not a persistent naming scheme. Query again after each
modeling operation, especially before selecting edges for finishing.
@spec wires(OCEx.Shape.t()) :: result([OCEx.Shape.t()])
Returns unique wires contained in the shape, including the shape itself if applicable.
Returns {:ok, shapes}; no matches gives {:ok, []}. Uniqueness is
by native topology identity and location, not geometric equivalence.
List order is not a persistent naming scheme. Query again after each
modeling operation, especially before selecting edges for finishing.
Measurements
@spec area(OCEx.Shape.t()) :: result(float())
Measures the sum of face areas, in square model units.
Shared faces are counted once by topology identity. Independently created coincident faces still contribute separately. For a solid, this includes both outer faces and faces bounding cavities.
@spec bounds(OCEx.Shape.t()) :: result(bounds3())
Returns world-axis-aligned bounds as {:ok, {minimum, maximum}}.
Each corner is a world point. OCCT calculates these bounds from geometry, without using a display mesh or adding shape tolerances. Curved geometry is still subject to numerical approximation; compare bounds with a tolerance rather than treating them as exact symbolic values.
Empty shapes return {:error, :empty_shape}.
@spec center_of_mass(OCEx.Shape.t()) :: result(vector3())
Returns the volume centroid in world coordinates.
Uses closed-shell volume properties with uniform density. Zero-volume
inputs, including a standalone face or empty compound, return
{:error, :empty_shape}. For a face's area centroid use face_info/1.
The centroid need not lie inside the material.
@spec distance_to_point(OCEx.Shape.t(), point3()) :: result(float())
Measures the minimum distance between a shape and a world point.
A point inside a solid has distance zero. To measure distance to its
boundary, query shells/1 and measure against the shells instead.
The returned distance uses the shape's length unit.
iex> {:ok, box} = OCEx.box(2, 2, 2)
iex> OCEx.distance_to_point(box, {1, 1, 1})
{:ok, 0.0}
iex> OCEx.distance_to_point(box, {5, 1, 1})
{:ok, 3.0}
@spec edge_info(OCEx.Shape.t()) :: result(edge_info())
Describes an edge's curve and directed endpoints.
The returned map always contains these keys:
| Key | Value |
|---|---|
:type | :line, :circle, or :other (including splines) |
:start, :end | World points in edge traversal order |
:length | Curve length in model units |
:direction | Directed unit vector for a line; otherwise nil |
:radius | Radius for a circle or circular arc; otherwise nil |
:parameter_bounds | Underlying curve's {first, last} parameters |
Reversed edges swap endpoints and reverse line direction; parameter bounds
remain in underlying curve order. Non-edge inputs return :wrong_shape_type.
@spec edge_sample(OCEx.Shape.t(), number()) :: result(%{point: vector3(), tangent: vector3()})
Samples a directed edge at a fraction of its parameter interval.
fraction must be between 0 and 1 inclusive. Returns
{:ok, %{point: {x, y, z}, tangent: {dx, dy, dz}}}, with a unit tangent
following the edge orientation. Fractions 0 and 1 correspond to the
:start and :end in edge_info/1.
Parameter spacing need not be uniform in distance: 0.5 is not necessarily
halfway along a spline's length. A degenerate derivative returns
{:error, :undefined_tangent}.
iex> {:ok, edge} = OCEx.edge({0, 0, 0}, {10, 0, 0})
iex> OCEx.edge_sample(edge, 0.25)
{:ok, %{point: {2.5, 0.0, 0.0}, tangent: {1.0, 0.0, 0.0}}}
@spec face_info(OCEx.Shape.t()) :: result(face_info())
Describes a face's surface and area properties.
The returned map always contains these keys:
| Key | Value |
|---|---|
:type | :plane, :cylinder, :sphere, or :other |
:area | Trimmed face area in square model units |
:center | Area centroid in world coordinates |
:uv_bounds | {u_min, u_max, v_min, v_max} in surface parameters |
:normal | Orientation-adjusted unit plane normal; otherwise nil |
:radius | Cylinder or sphere radius; otherwise nil |
:axis_origin, :axis_direction | Cylinder axis point and unit direction; otherwise nil |
A correctly oriented solid's planar normal points out of the material.
A standalone face has its own orientation, with no solid interior to define
"outward". A curved face's centroid need not lie on its surface.
Non-face inputs return :wrong_shape_type.
@spec length(OCEx.Shape.t()) :: result(float())
Measures the sum of edge lengths, in model units.
Shared edges are counted once by topology identity. This is the total topological edge length, not a body's perimeter in a chosen projection.
@spec point(OCEx.Shape.t()) :: result(vector3())
Returns the world coordinates of a vertex.
Use vertices/1 to extract vertices from a body. Other topology kinds
return {:error, :wrong_shape_type}.
@spec volume(OCEx.Shape.t()) :: result(float())
Measures signed volume from closed shells, in cubic model units.
Shared topology is counted once. Independent overlapping solids in a
compound are not Boolean-unioned before measurement. Edges, open faces,
and empty compounds can return zero; use solids/1 as well when checking
for a solid model.
Exchange
@spec drawing(OCEx.Shape.t(), point3(), point3(), point3(), keyword()) :: result(%{visible: OCEx.Shape.t(), hidden: OCEx.Shape.t()})
Creates an orthographic drawing with separate visible and hidden curves.
The view looks along the negative normal. The origin becomes drawing
coordinate {0, 0}; its depth has no effect on orthographic coordinates.
The normal and X direction must be nonzero and nonparallel. X is projected
into the view plane and normalized; local Y is normal cross X.
Returns {:ok, %{visible: shape, hidden: shape}}, containing native curves in
local XY at Z=0. Both values support edge queries and BREP serialization;
they are edge collections, not filled faces or joined wires. No edges in
a category gives an empty compound. End-on edges with projected length
at most 1.0e-7 are omitted. Coincident edges are not geometrically merged.
Accepts solids, shells, faces, wires, edges, and collections of them.
Free vertices return :wrong_shape_type. Sharp boundaries and silhouettes
are included. The only option is tangents: true to include smooth G1
boundaries between faces (default false). Surface seams and isoparametric
lines are excluded. Unknown, duplicate, or malformed options return
:invalid_options; malformed frames return :invalid_argument.
OCCT computes visibility from a copy of the BREP, independently of any triangle mesh. The source is unchanged. Kernel failures return tagged errors; a drawing is not a solid and cannot be used as a printable mesh.
@spec from_brep(binary()) :: result(OCEx.Shape.t())
Reads OCCT text BREP from a binary into a new native resource.
Malformed or truncated input can return :invalid_brep; decoded
invalid topology returns :invalid_shape. Kernel exceptions use the
errors described in the error guide.
This is an in-process parser, not an isolation boundary for untrusted files.
@spec mesh(OCEx.Shape.t(), number(), number()) :: result(mesh())
Tessellates a copy of a shape into indexed triangles.
tolerance is absolute linear deflection in model units (default 0.1).
angular_tolerance is angular deflection in radians (default 0.5).
Both must exceed 1.0e-7. Lower values generally produce more triangles;
they do not offset or change the original boundary geometry.
Returns a map with:
:vertices— a list of world{x, y, z}points.:triangles— a list of zero-based{a, b, c}vertex indices.:triangles_per_face— triangle counts in face traversal order.:face_types— OCCT surface-type integers in the same face order.
Vertices are separate per face and may coincide at boundaries. Winding follows face orientation. The mesh is not welded or checked for closed topology, and a shape without faces can produce empty lists.
iex> {:ok, box} = OCEx.box(2, 3, 4)
iex> {:ok, mesh} = OCEx.mesh(box)
iex> length(mesh.triangles)
12
iex> Enum.sum(mesh.triangles_per_face)
12
@spec polylines(OCEx.Shape.t(), number(), number()) :: result([[point3()]])
Samples each nondegenerate edge into an ordered list of 3D points.
Returns {:ok, polylines}, one point list per unique topological edge.
Lines contain their endpoints; curves are sampled with OCCT's tangential
deflection algorithm. Linear deflection defaults to 0.03 model units and
angular deflection to 0.1 radians; both must exceed 1.0e-7. These control
sampling, not exact analytic curve representation or a certified global
distance bound for arbitrary splines.
Points follow each edge's orientation and include both endpoints, including
the repeated endpoint of a closed edge. Closed circular edges retain at
least three distinct points even at coarse tolerances. Edges are not joined or sorted into
wires. Empty geometry and shapes without edges return an empty list.
Sampling leaves the input unchanged. Invalid deflections return
:invalid_argument; a failed sampler returns :sampling_failed.
@spec read_step(String.t()) :: result(OCEx.Shape.t())
Imports geometry from a STEP file.
path is a nonempty string without NUL bytes. Transferred roots are
combined into one shape. File-read failures return :io_error and
invalid transferred geometry returns :invalid_shape.
Product names, assembly instances, colors, and materials are not exposed. The reader uses OCCT's default exchange units; use millimeters for models that will be exchanged through this API.
@spec to_brep(OCEx.Shape.t()) :: result(binary())
Serializes a shape to OCCT text BREP in a binary.
BREP stores geometry and topology, not the Elixir construction recipe. No file is written. Retain the recipe for editing, and use this function or STEP for persistence instead of serializing resource handles.
iex> {:ok, box} = OCEx.box(2, 3, 4)
iex> {:ok, bytes} = OCEx.to_brep(box)
iex> {:ok, restored} = OCEx.from_brep(bytes)
iex> {:ok, volume} = OCEx.volume(restored)
iex> Float.round(volume, 6)
24.0
@spec write_step(OCEx.Shape.t(), String.t()) :: result(:ok)
Writes geometry-only STEP to a path, returning {:ok, :ok}.
The parent directory must exist; an existing file is overwritten. path
must be a nonempty string without NUL bytes. The writer uses OCCT's default
exchange configuration. It does not preserve a named assembly product tree
or reimport the file to check its contents.
Transfer failures return :operation_failed; write failures return
:io_error. For verified bundles use Smith's export API.
@spec write_stl(OCEx.Shape.t(), String.t(), number(), number()) :: result(:ok)
Writes binary STL from a tessellated copy, returning {:ok, :ok}.
Deflections have the same meaning and defaults as mesh/3: 0.1 model
units and 0.5 radians. Both must exceed 1.0e-7. Parent directories must
exist, and an existing file is overwritten. Write failures return
:io_error.
STL does not record a unit. This function does not weld or verify the serialized mesh; Smith provides verified STL and 3MF export.
Toolkit
Types
@type mesh() :: %{ vertices: [vector3()], triangles: [{non_neg_integer(), non_neg_integer(), non_neg_integer()}], triangles_per_face: [non_neg_integer()], face_types: [non_neg_integer()] }
@type result(value) :: {:ok, value} | {:error, atom()}
A point or vector returned by the native kernel.
Functions
@spec mirror(OCEx.Shape.t(), point3(), point3()) :: result(OCEx.Shape.t())
Reflects a shape across the plane through origin with the given normal.
Coordinates are world coordinates in mm. The normal is normalized and
must be nonzero. Supports edges, faces, solids, and compounds. Returns
an independent shape revision, preserving the input and outward solid
orientation. It returns only the reflection; use compound/1 or
fuse/2 to retain both copies. Malformed points or zero normals return
:invalid_argument.
@spec torus(number(), number()) :: result(OCEx.Shape.t())
Creates a complete ring torus centered at the origin, around world Z.
major_radius is the distance from the Z axis to the tube center;
minor_radius is the tube radius, both in mm. Both radii and their
difference must exceed 1.0e-7 mm. Horn and self-intersecting spindle
tori return :invalid_argument. The torus extends from -minor_radius
to +minor_radius in Z. Use transforms for other orientations.