Shape — Geometry Recognition & Polygon/Triangulation Data
This page documents the geometry-utility and polygon/triangulation public API from Sources/OCCTSwift/Shape.swift. It covers coordinate-system helpers, curve/surface construction utilities, 2D constraint solvers, shape modification tools, and the full polygon and triangulation layer. See the main Shape page for the core B-Rep API.
Topics
- Axis2Placement · ShapeConstruct_Curve extensions · Bisector utilities · GeomLib_Tool — Parameter Finding · GeomLib_IsPlanarSurface · GeomLib_CheckBSplineCurve / Check2dBSplineCurve · GeomLib_Interpolate · GccAna_Circ2d2TanRad · GccAna_Circ2dTanCen · GccAna_Lin2d2Tan · Approx_SameParameter · ShapeUpgrade Curve Splitting · Shape Modifications · Surface Splitting · Curve/Surface Recognition · Polygon2D · Triangulation · Polygon3D · PolygonOnTriangulation · Mesh Node Merging
Axis2Placement
A standalone Swift class wrapping Geom_Axis2Placement — a right-handed 3D coordinate system with an origin, a main (Z) direction, and an X direction. Used to define placement frames for geometry factories.
Axis2Placement.init(origin:normal:xDirection:)
Creates a right-handed 3D axis placement.
public init(origin: SIMD3<Double>, normal: SIMD3<Double>, xDirection: SIMD3<Double>)
- Parameters:
origin— origin point;normal— main (Z) direction;xDirection— X direction (must not be parallel tonormal). - OCCT:
Geom_Axis2Placement(gp_Pnt, gp_Dir main, gp_Dir xDir)viaOCCTAxis2PlacementCreate. - Example:
let ax = Axis2Placement(origin: SIMD3(0, 0, 10), normal: SIMD3(0, 0, 1), xDirection: SIMD3(1, 0, 0))
location
The origin of this placement.
public var location: SIMD3<Double> { get }
- Returns: The origin point.
- OCCT:
Geom_Axis2Placement::LocationviaOCCTAxis2PlacementLocation.
mainDirection
The main (Z) direction of this placement.
public var mainDirection: SIMD3<Double> { get }
- Returns: The main axis direction.
- OCCT:
Geom_Axis2Placement::DirectionviaOCCTAxis2PlacementDirection.
xDirection
The X direction of this placement.
public var xDirection: SIMD3<Double> { get }
- Returns: The X-axis direction.
- OCCT:
Geom_Axis2Placement::XDirectionviaOCCTAxis2PlacementXDirection.
yDirection
The Y direction of this placement (computed from main × X).
public var yDirection: SIMD3<Double> { get }
- Returns: The Y-axis direction.
- OCCT:
Geom_Axis2Placement::YDirectionviaOCCTAxis2PlacementYDirection.
setDirection(_:)
Sets the main (Z) direction in place.
public func setDirection(_ dir: SIMD3<Double>)
- Parameters:
dir— new main direction. - OCCT:
Geom_Axis2Placement::SetDirectionviaOCCTAxis2PlacementSetDirection.
setXDirection(_:)
Sets the X direction in place.
public func setXDirection(_ dir: SIMD3<Double>)
- Parameters:
dir— new X direction (must not be parallel to the main direction). - OCCT:
Geom_Axis2Placement::SetXDirectionviaOCCTAxis2PlacementSetXDirection.
ShapeConstruct_Curve extensions
Extensions on Curve3D and Curve2D that expose ShapeConstruct_Curve utilities for B-Spline conversion and endpoint adjustment.
Curve3D.convertSegmentToBSpline(first:last:precision:)
Converts a segment of this 3D curve to a BSpline using ShapeConstruct_Curve.
public func convertSegmentToBSpline(first: Double, last: Double,
precision: Double = 1e-6) -> Curve3D?
- Parameters:
first— start parameter;last— end parameter;precision— geometric tolerance. - Returns: New
Curve3Das a BSpline, ornilon failure. - OCCT:
ShapeConstruct_Curve::ConvertToBSplineviaOCCTShapeConstructConvertToBSpline3D. - Example:
if let bsp = curve.convertSegmentToBSpline(first: 0, last: 1) { print(bsp.degree) }
Curve3D.adjustEndpoints(start:end:)
Adjusts the 3D curve endpoints to match given 3D points.
public func adjustEndpoints(start: SIMD3<Double>, end: SIMD3<Double>) -> Bool
- Parameters:
start— desired start point;end— desired end point. - Returns:
trueon success. - OCCT:
ShapeConstruct_Curve::AdjustCurveviaOCCTShapeConstructAdjustCurve3D.
Curve2D.convertSegmentToBSpline(first:last:precision:)
Converts a segment of this 2D curve to a BSpline using ShapeConstruct_Curve.
public func convertSegmentToBSpline(first: Double, last: Double,
precision: Double = 1e-6) -> Curve2D?
- Parameters:
first— start parameter;last— end parameter;precision— geometric tolerance. - Returns: New
Curve2Das a BSpline, ornilon failure. - OCCT:
ShapeConstruct_Curve::ConvertToBSplineviaOCCTShapeConstructConvertToBSpline2D.
Curve2D.adjustEndpoints(start:end:)
Adjusts the 2D curve endpoints to match given 2D points.
public func adjustEndpoints(start: (Double, Double), end: (Double, Double)) -> Bool
- Parameters:
start— desired start point as(x, y);end— desired end point as(x, y). - Returns:
trueon success. - OCCT:
ShapeConstruct_Curve::AdjustCurve2dviaOCCTShapeConstructAdjustCurve2D.
Bisector utilities
Free-function bisector utilities and their associated value types.
BisectorIntersection
Result of a bisector-vs-bisector intersection computation.
public struct BisectorIntersection {
public let x: Double
public let y: Double
public let paramOnFirst: Double
public let paramOnSecond: Double
}
BisectorIntersection.x
X coordinate of the intersection point.
public let x: Double
BisectorIntersection.y
Y coordinate of the intersection point.
public let y: Double
BisectorIntersection.paramOnFirst
Parameter of the intersection point along the bisector of (a, b) (IntRes2d_IntersectionPoint::ParamOnFirst()).
public let paramOnFirst: Double
BisectorIntersection.paramOnSecond
Parameter of the intersection point along the bisector of (c, d) (IntRes2d_IntersectionPoint::ParamOnSecond()).
public let paramOnSecond: Double
bisectorIntersections(a:b:c:d:)
Computes intersections between the perpendicular bisectors of two point pairs.
public func bisectorIntersections(
a: (Double, Double), b: (Double, Double),
c: (Double, Double), d: (Double, Double)
) -> [BisectorIntersection]
The bisector of (a, b) is intersected with the bisector of (c, d). The result is the circumcenter when the two pairs form a triangle.
- Parameters:
a,b— first point pair;c,d— second point pair, all as(x, y). - Returns: Array of intersection points (zero, one, or two).
- OCCT:
Bisector_BisecCC/Bisector_InterviaOCCTBisectorInterPointPoint. - Example:
let hits = bisectorIntersections(a: (0, 0), b: (4, 0), c: (4, 0), d: (2, 3)) // hits[0] is the circumcenter of the triangle
GeomLib_Tool — Parameter Finding
Extensions on Curve3D, Surface, and Curve2D for locating parameter values corresponding to 3D/2D points.
Curve3D.parameterOf(point:maxDistance:)
Finds the parameter of a 3D point on this curve.
public func parameterOf(point: SIMD3<Double>, maxDistance: Double = 1.0) -> Double?
- Parameters:
point— 3D point to locate;maxDistance— maximum allowed distance from the curve. - Returns: Parameter value, or
nilif the point lies farther thanmaxDistancefrom the curve. - OCCT:
GeomLib_Tool::ParameterviaOCCTGeomLibToolParameter3D. - Example:
if let t = curve.parameterOf(point: SIMD3(1, 2, 3), maxDistance: 0.01) { let pt = curve.point(at: t) }
Surface.parametersOf(point:maxDistance:)
Finds the UV parameters of a 3D point on this surface.
public func parametersOf(point: SIMD3<Double>, maxDistance: Double = 1.0) -> (u: Double, v: Double)?
- Parameters:
point— 3D point to locate;maxDistance— maximum allowed distance from the surface. - Returns:
(u, v)parameter tuple, ornilif the point lies farther thanmaxDistance. - OCCT:
GeomLib_Tool::ParametersviaOCCTGeomLibToolParametersSurface. - Example:
let s = Surface.sphere(center: .zero, radius: 5)! if let uv = s.parametersOf(point: SIMD3(5, 0, 0), maxDistance: 0.1) { print(uv.u, uv.v) }
Curve2D.parameterOf(point:maxDistance:)
Finds the parameter of a 2D point on this curve.
public func parameterOf(point: SIMD2<Double>, maxDistance: Double = 1.0) -> Double?
- Parameters:
point— 2D point to locate;maxDistance— maximum allowed distance from the curve. - Returns: Parameter value, or
nilif the point is too far from the curve. - OCCT:
GeomLib_Tool::ParameterviaOCCTGeomLibToolParameter2D.
GeomLib_IsPlanarSurface
Extensions on Surface for planarity testing.
Surface.isPlanar(tolerance:)
Checks if this surface is planar within a given tolerance.
public func isPlanar(tolerance: Double = 1e-7) -> Bool
- Parameters:
tolerance— planarity tolerance. - Returns:
trueif the surface is planar withintolerance. - OCCT:
GeomLib_IsPlanarSurface::IsPlanarviaOCCTGeomLibIsPlanarSurface. - Example:
let plane = Surface.plane(origin: .zero, normal: SIMD3(0, 0, 1))! print(plane.isPlanar()) // true
Surface.planarPlane(tolerance:)
Returns the underlying plane parameters if this surface is planar.
public func planarPlane(tolerance: Double = 1e-7) -> (origin: SIMD3<Double>, normal: SIMD3<Double>, xDirection: SIMD3<Double>)?
- Parameters:
tolerance— planarity tolerance. - Returns: Tuple of
(origin, normal, xDirection)if planar,nilotherwise. - OCCT:
GeomLib_IsPlanarSurface::PlaneviaOCCTGeomLibPlanarSurfacePlane. - Example:
if let plane = surface.planarPlane() { print(plane.normal) }
GeomLib_CheckBSplineCurve / Check2dBSplineCurve
Extensions on Curve3D and Curve2D for detecting and fixing reversed end tangents on BSpline curves.
Curve3D.checkBSplineTangents(tolerance:angularTolerance:)
Checks if this BSpline curve has reversed end tangents.
public func checkBSplineTangents(tolerance: Double = 0.01,
angularTolerance: Double = 0.1) -> (fixFirst: Bool, fixLast: Bool)?
- Parameters:
tolerance— positional tolerance;angularTolerance— angular tolerance in radians. - Returns:
(fixFirst, fixLast)indicating which ends need fixing, ornilif not a BSpline or check failed. - OCCT:
GeomLib_CheckBSplineCurveviaOCCTGeomLibCheckBSpline3D.
Curve3D.fixBSplineTangents(fixFirst:fixLast:tolerance:angularTolerance:)
Fixes reversed end tangents on a BSpline curve.
public func fixBSplineTangents(fixFirst: Bool, fixLast: Bool,
tolerance: Double = 0.01,
angularTolerance: Double = 0.1) -> Curve3D?
- Parameters:
fixFirst— fix the start tangent;fixLast— fix the end tangent;tolerance— positional tolerance;angularTolerance— angular tolerance. - Returns: New
Curve3Dwith corrected tangents, ornilon failure. - OCCT:
GeomLib_CheckBSplineCurve::FixTangentviaOCCTGeomLibFixBSpline3D. - Example:
if let flags = curve.checkBSplineTangents(), (flags.fixFirst || flags.fixLast) { let fixed = curve.fixBSplineTangents(fixFirst: flags.fixFirst, fixLast: flags.fixLast) }
Curve2D.checkBSplineTangents(tolerance:angularTolerance:)
Checks if this 2D BSpline curve has reversed end tangents.
public func checkBSplineTangents(tolerance: Double = 0.01,
angularTolerance: Double = 0.1) -> (fixFirst: Bool, fixLast: Bool)?
- Parameters:
tolerance— positional tolerance;angularTolerance— angular tolerance. - Returns:
(fixFirst, fixLast)flags, ornilif not a BSpline or check failed. - OCCT:
GeomLib_Check2dBSplineCurveviaOCCTGeomLibCheckBSpline2D.
Curve2D.fixBSplineTangents(fixFirst:fixLast:tolerance:angularTolerance:)
Fixes reversed end tangents on a 2D BSpline curve.
public func fixBSplineTangents(fixFirst: Bool, fixLast: Bool,
tolerance: Double = 0.01,
angularTolerance: Double = 0.1) -> Curve2D?
- Parameters:
fixFirst— fix the start tangent;fixLast— fix the end tangent;tolerance— positional tolerance;angularTolerance— angular tolerance. - Returns: Fixed
Curve2D, ornilon failure. - OCCT:
GeomLib_Check2dBSplineCurve::FixTangentviaOCCTGeomLibFixBSpline2D.
GeomLib_Interpolate
Curve3D.polynomialInterpolation(degree:points:parameters:)
Creates a BSpline curve by polynomial interpolation of 3D points at given parameters.
public static func polynomialInterpolation(degree: Int, points: [SIMD3<Double>],
parameters: [Double]) -> Curve3D?
points and parameters must have equal counts (≥ 2). The parameter values define how the polynomial fits progress along the curve.
- Parameters:
degree— polynomial degree;points— interpolation points;parameters— parameter values corresponding to each point. - Returns: Interpolated BSpline
Curve3D, ornilif counts mismatch or interpolation fails. - OCCT:
GeomLib_InterpolateviaOCCTGeomLibInterpolate. - Example:
let pts: [SIMD3<Double>] = [SIMD3(0,0,0), SIMD3(5,3,0), SIMD3(10,0,0)] let params: [Double] = [0, 0.5, 1] if let c = Curve3D.polynomialInterpolation(degree: 2, points: pts, parameters: params) { let pt = c.point(at: 0.25) }
GccAna_Circ2d2TanRad
Free functions and supporting types for computing 2D circles tangent to two lines or through two points with a given radius.
Circle2DSolution
A 2D circle solution returned by circle construction functions.
public struct Circle2DSolution: Sendable {
public let center: SIMD2<Double>
public let radius: Double
}
circlesTangentToLines(_:_:_:_:radius:tolerance:)
Finds circles tangent to two 2D lines with a given radius.
public func circlesTangentToLines(_ l1Origin: SIMD2<Double>, _ l1Direction: SIMD2<Double>,
_ l2Origin: SIMD2<Double>, _ l2Direction: SIMD2<Double>,
radius: Double, tolerance: Double = 1e-6) -> [Circle2DSolution]
- Parameters:
l1Origin,l1Direction— first line (point + direction);l2Origin,l2Direction— second line;radius— required circle radius;tolerance— geometric tolerance. - Returns: Array of up to four
Circle2DSolutionvalues (may be empty if no solution exists). - OCCT:
GccAna_Circ2d2TanRad(Lin+Lin variant) viaOCCTGccAnaCirc2d2TanRadLineLin. - Note:
radiusis the radius of the circles to find, and it must be positive (#553). Asked for zero,GccAna_Circ2d2TanRadobliges and returns solution circles of radius zero. A non-positive radius returns an empty array. - Example:
let circles = circlesTangentToLines(SIMD2(0,0), SIMD2(1,0), SIMD2(0,0), SIMD2(0,1), radius: 3)
circlesThroughPointsWithRadius(_:_:radius:tolerance:)
Finds circles passing through two 2D points with a given radius.
public func circlesThroughPointsWithRadius(_ p1: SIMD2<Double>, _ p2: SIMD2<Double>,
radius: Double,
tolerance: Double = 1e-6) -> [Circle2DSolution]
- Parameters:
p1,p2— two points to pass through;radius— required circle radius;tolerance— geometric tolerance. - Returns: Array of up to two
Circle2DSolutionvalues. - OCCT:
GccAna_Circ2d2TanRad(Pnt+Pnt variant) viaOCCTGccAnaCirc2d2TanRadPntPnt. - Note:
radiusis the radius of the circles to find, and it must be positive; zero would ask for a solution circle that is a point (#553). A non-positive radius returns an empty array, as does a radius too small to reach both points. - Example:
let circles = circlesThroughPointsWithRadius(SIMD2(-3, 0), SIMD2(3, 0), radius: 5)
GccAna_Circ2dTanCen
Free functions for computing 2D circles with a given centre.
circleThroughPointCentered(point:center:)
Finds the circle centered at a given point that passes through another point.
public func circleThroughPointCentered(point: SIMD2<Double>,
center: SIMD2<Double>) -> Circle2DSolution?
- Parameters:
point— a point on the circle;center— the required circle centre. - Returns: A
Circle2DSolution, ornilif no solution exists. - OCCT:
GccAna_Circ2dTanCen(Pnt+Pnt variant) viaOCCTGccAnaCirc2dTanCenPntPnt. - Example:
if let c = circleThroughPointCentered(point: SIMD2(5, 0), center: .zero) { print(c.radius) // 5.0 }
circleTangentToLineCentered(lineOrigin:lineDirection:center:)
Finds the circle centred at a given point that is tangent to a line.
public func circleTangentToLineCentered(lineOrigin: SIMD2<Double>,
lineDirection: SIMD2<Double>,
center: SIMD2<Double>) -> Circle2DSolution?
- Parameters:
lineOrigin— a point on the line;lineDirection— line direction;center— required circle centre. - Returns: A
Circle2DSolution, ornilif no solution exists. - OCCT:
GccAna_Circ2dTanCen(Lin+Pnt variant) viaOCCTGccAnaCirc2dTanCenLinPnt. - Example:
if let c = circleTangentToLineCentered(lineOrigin: SIMD2(0, 3), lineDirection: SIMD2(1, 0), center: SIMD2(2, 0)) { print(c.radius) // 3.0 }
GccAna_Lin2d2Tan
Free functions and supporting types for 2D line construction.
Line2DSolution
A 2D line solution returned by line construction functions.
public struct Line2DSolution: Sendable {
public let origin: SIMD2<Double>
public let direction: SIMD2<Double>
}
lineThroughPoints(_:_:tolerance:)
Finds the line passing through two 2D points.
public func lineThroughPoints(_ p1: SIMD2<Double>, _ p2: SIMD2<Double>,
tolerance: Double = 1e-6) -> Line2DSolution?
- Parameters:
p1,p2— two points;tolerance— geometric tolerance. - Returns: A
Line2DSolution, ornilif the points coincide within tolerance. - OCCT:
GccAna_Lin2d2Tan(Pnt+Pnt variant) viaOCCTGccAnaLin2d2TanPntPnt. - Example:
if let line = lineThroughPoints(SIMD2(0, 0), SIMD2(1, 1)) { print(line.direction) }
linesTangentToCircleThroughPoint(circleCenter:circleRadius:point:tolerance:)
Finds lines tangent to a circle and passing through a given point.
public func linesTangentToCircleThroughPoint(circleCenter: SIMD2<Double>,
circleRadius: Double,
point: SIMD2<Double>,
tolerance: Double = 1e-6) -> [Line2DSolution]
- Parameters:
circleCenter,circleRadius— the circle;point— point the line must pass through;tolerance— geometric tolerance. - Returns: Array of up to two
Line2DSolutionvalues (one if the point lies on the circle). - OCCT:
GccAna_Lin2d2Tan(Circ+Pnt variant) viaOCCTGccAnaLin2d2TanCircPnt. - Note:
circleRadiusmust be positive (#553). With a radius of zero the solver returns the single line through the centre twice; the point/point entry points answer that question once. - Example:
let tangents = linesTangentToCircleThroughPoint(circleCenter: .zero, circleRadius: 3, point: SIMD2(5, 0))
Approx_SameParameter
SameParameterResult
Result of a same-parameterisation check between a 3D curve and a 2D curve on a surface.
public struct SameParameterResult: Sendable {
public let isSameParameter: Bool
public let toleranceReached: Double
}
toleranceReached is the maximum distance between the 3D curve and the surface-evaluated 2D curve.
| Field | Meaning |
|---|---|
isSameParameter | true if the curves already share the same parameterisation within tolerance |
toleranceReached | Maximum distance between the 3D curve and the surface-evaluated 2D curve |
SameParameterResult.toleranceReached
Maximum distance actually measured between the 3D curve and the surface-evaluated 2D curve.
Curve3D.checkSameParameter(curve2D:surface:tolerance:)
Checks if a 2D curve on a surface has the same parameterisation as this 3D curve.
public func checkSameParameter(curve2D: Curve2D, surface: Surface,
tolerance: Double = 1e-6) -> SameParameterResult?
- Parameters:
curve2D— the 2D curve;surface— the surface;tolerance— parameterisation tolerance. - Returns:
SameParameterResult, ornilif the check fails. - OCCT:
Approx_SameParameterviaOCCTApproxSameParameter. - Example:
if let r = curve3d.checkSameParameter(curve2D: pcurve, surface: face.surface!) { print(r.isSameParameter, r.toleranceReached) }
ShapeUpgrade Curve Splitting
Extensions on Curve3D and Curve2D for splitting by continuity and converting to Bezier or arc/segment decompositions.
Curve3D.splitByContinuity(criterion:tolerance:)
Splits this 3D curve at continuity breaks.
public func splitByContinuity(criterion: Int = 2, tolerance: Double = 1e-6) -> [Curve3D]
- Parameters:
criterion— aParametricContinuityraw value: 0=C0, 1=C1, 2=C2, 3=C3, and anything above asks for CN (split at every break);tolerance— geometric tolerance. - Returns: Array of
Curve3Dsegments; may be a single-element array if no breaks are found. - OCCT:
ShapeUpgrade_SplitCurve3dContinuityviaOCCTSplitCurve3dContinuity. - Example:
let segments = curve.splitByContinuity(criterion: 1)
Curve2D.splitByContinuity(criterion:tolerance:)
Splits this 2D curve at continuity breaks.
public func splitByContinuity(criterion: Int = 2, tolerance: Double = 1e-6) -> [Curve2D]
- Parameters:
criterion— aParametricContinuityraw value: 0=C0, 1=C1, 2=C2, 3=C3, and anything above asks for CN (split at every break);tolerance— geometric tolerance. - Returns: Array of
Curve2Dsegments. - OCCT:
ShapeUpgrade_SplitCurve2dContinuityviaOCCTSplitCurve2dContinuity.
Curve2D.convertToBezierSegments()
Converts this 2D curve to Bezier segments via ShapeUpgrade.
public func convertToBezierSegments() -> [Curve2D]
- Returns: Array of
Curve2DBezier segments. Returns an empty array on failure. - OCCT:
ShapeUpgrade_ConvertCurve2dToBezierviaOCCTConvertCurve2dToBezier.
Curve2D.approxArcsAndSegments(tolerance:angleTolerance:)
Approximates this 2D curve as a sequence of arcs and line segments.
public func approxArcsAndSegments(tolerance: Double, angleTolerance: Double) -> [Curve2D]
- Parameters:
tolerance— positional approximation tolerance;angleTolerance— angular tolerance in radians. - Returns: Array of
Curve2Darcs and segments. Returns an empty array on failure. - OCCT:
Geom2dConvert_ApproxArcsSegmentsviaOCCTGeom2dConvertApproxArcsSegments.
Shape Modifications
Shape extension methods wrapping BRepTools modification helpers.
Shape.trsfModification(_:a11:a12:a13:a14:a21:a22:a23:a24:a31:a32:a33:a34:)
Applies a 3×4 affine transformation matrix to a shape via BRepTools_TrsfModification.
public static func trsfModification(_ shape: Shape,
a11: Double, a12: Double, a13: Double, a14: Double,
a21: Double, a22: Double, a23: Double, a24: Double,
a31: Double, a32: Double, a33: Double, a34: Double) -> Shape?
The matrix is specified row-major. Supports uniform scaling and rotation but not non-uniform scaling; use gtrsfModification for general affine transforms.
- Parameters:
shape— input shape;a11…a34— row-major 3×4 transformation matrix coefficients. - Returns: Transformed shape, or
nilon failure. - OCCT:
BRepTools_TrsfModificationviaOCCTShapeTrsfModification. - Example:
// Translate by (10, 0, 0) if let moved = Shape.trsfModification(box, a11: 1, a12: 0, a13: 0, a14: 10, a21: 0, a22: 1, a23: 0, a24: 0, a31: 0, a32: 0, a33: 1, a34: 0) { // use moved }
Shape.gtrsfModification(_:a11:a12:a13:a14:a21:a22:a23:a24:a31:a32:a33:a34:)
Applies a general (non-uniform) 3×4 transformation matrix via BRepTools_GTrsfModification.
public static func gtrsfModification(_ shape: Shape,
a11: Double, a12: Double, a13: Double, a14: Double,
a21: Double, a22: Double, a23: Double, a24: Double,
a31: Double, a32: Double, a33: Double, a34: Double) -> Shape?
Supports non-uniform scaling. Convert the shape to NURBS first for non-affine transforms to ensure geometry validity.
- Parameters:
shape— input shape;a11…a34— row-major 3×4 matrix. - Returns: Transformed shape, or
nilon failure. - OCCT:
BRepTools_GTrsfModificationviaOCCTShapeGTrsfModification.
Shape.deepCopy(_:copyGeometry:copyMesh:)
Creates a deep copy of a shape via BRepTools_CopyModification.
public static func deepCopy(_ shape: Shape,
copyGeometry: Bool = true,
copyMesh: Bool = true) -> Shape?
- Parameters:
shape— shape to copy;copyGeometry— whether to copy underlying geometry;copyMesh— whether to copy cached triangulations. - Returns: Independent deep copy, or
nilon failure. - OCCT:
BRepTools_CopyModificationviaOCCTShapeCopyModification. - Example:
if let copy = Shape.deepCopy(original) { // Modifications to copy do not affect original }
Shape.bsplineRestrictionAdvanced(_:approxSurface:approxCurve3d:approxCurve2d:tol3d:tol2d:continuity3d:continuity2d:maxDegree:maxSegments:priorityDegree:convertRational:)
Restricts BSpline degree and segment count in a shape with fine-grained control.
public static func bsplineRestrictionAdvanced(_ shape: Shape,
approxSurface: Bool = true,
approxCurve3d: Bool = true,
approxCurve2d: Bool = true,
tol3d: Double = 0.01,
tol2d: Double = 0.01,
continuity3d: ParametricContinuity = .c1,
continuity2d: ParametricContinuity = .c1,
maxDegree: Int = 5,
maxSegments: Int = 20,
priorityDegree: Bool = true,
convertRational: Bool = false) -> Shape?
- Parameters:
approxSurface/approxCurve3d/approxCurve2d— which geometry types to process;tol3d/tol2d— tolerances;continuity3d/continuity2d— required continuity,.c2being the practical maximum (.c3fails the whole call);maxDegree— maximum polynomial degree;maxSegments— maximum segment count;priorityDegree—true= reduce degree first,false= reduce segments first;convertRational— convert rational BSplines to non-rational. - Returns: Restricted shape, or
nilon failure. - OCCT:
ShapeCustom_BSplineRestrictiondriven throughBRepTools_ModifierviaOCCTShapeBSplineRestrictionAdvanced— the same mechanismShape.bsplineRestriction(...)reaches through the staticShapeCustom::BSplineRestrictionhelper, and since #490 both read the continuity the same way. The continuity is a ceiling, not a guarantee, through either: OCCT silently reduces what it delivers when the requested one cannot meettol3dwithinmaxDegree(#570). This entry point used to read it as aGeomAbs_Shapeordinal (1=G1,2=C1), so the same integer asked for a different continuity through each, and four of the seven values that reading offered failed the whole call. A deprecatedIntoverload remains for source compatibility; it now decodes asParametricContinuitytoo.
Shape.convertToBSplineAdvanced(_:extrusionMode:revolutionMode:offsetMode:planeMode:)
Converts surfaces in a shape to BSpline with per-type control.
public static func convertToBSplineAdvanced(_ shape: Shape,
extrusionMode: Bool = true,
revolutionMode: Bool = true,
offsetMode: Bool = true,
planeMode: Bool = false) -> Shape?
- Parameters:
extrusionMode— convert extrusion surfaces;revolutionMode— convert revolution surfaces;offsetMode— convert offset surfaces;planeMode— convert planes. - Returns: Shape with BSpline surfaces, or
nilon failure. - OCCT:
ShapeUpgrade_ConvertSurfaceToBSplineSurfaceviaOCCTShapeConvertToBSplineAdvanced.
Surface Splitting
Surface extension for splitting surfaces by continuity, angle, or area.
Surface.SplitResult
Result of a surface splitting operation.
public struct SplitResult: Sendable {
public let uSplitCount: Int
public let vSplitCount: Int
}
Surface.SplitResult
Split-count result shared by splitSurfaceByContinuity(criterion:tolerance:), splitByAngle(_:) and splitByArea(parts:intoSquares:) below.
public struct SplitResult: Sendable {
public let uSplitCount: Int
public let vSplitCount: Int
}
uSplitCount/vSplitCount: number of splits introduced in each parametric direction.
SplitResult.vSplitCount
Surface.splitSurfaceByContinuity(criterion:tolerance:)
Splits this surface at continuity breaks.
public func splitSurfaceByContinuity(criterion: Int, tolerance: Double) -> SplitResult?
- Parameters:
criterion— aParametricContinuityraw value: 0=C0, 1=C1, 2=C2, 3=C3, above asks for CN;tolerance— geometric tolerance. - Returns:
SplitResultwith U and V split counts, ornilif no splits are found. - OCCT:
ShapeUpgrade_SplitSurfaceContinuityviaOCCTSplitSurfaceContinuity— the same classSurface.splitByContinuity(criterion:tolerance:)wraps. Before #490 this entry point readcriterionas aGeomAbs_Shapeordinal while its sibling read it as a parametric continuity, socriterion: 2asked for C1 through one and C2 through the other.
Surface.splitByAngle(_:)
Splits this surface where the normal varies by more than a maximum angle.
public func splitByAngle(_ maxAngle: Double) -> SplitResult?
- Parameters:
maxAngle— maximum allowed normal deviation in radians. - Returns:
SplitResult, ornilif no splits are needed. - OCCT:
ShapeUpgrade_SplitSurfaceAngleviaOCCTSplitSurfaceAngle.
Surface.splitByArea(parts:intoSquares:)
Splits this surface into approximately equal-area parts.
public func splitByArea(parts: Int, intoSquares: Bool = false) -> SplitResult?
- Parameters:
parts— desired number of parts;intoSquares— iftrue, target square patches. - Returns:
SplitResult, ornilon failure. - OCCT:
ShapeUpgrade_SplitSurfaceAreaviaOCCTSplitSurfaceArea.
Curve/Surface Recognition
Types and extensions for recognising and converting geometry to analytical (canonical) forms.
Every spelling below reaches one bridge entry point per OCCT converter class, and they share one contract (#492):
- An already-analytical input converts. A circle recognised as a circle is a success, not a rejection, and reports
gap == 0exactly. That is how you tell it apart from a fit. - The result is independent of the input. No returned curve or surface shares state with the geometry it was recognised from, so an in-place transform on one never moves the other.
- Failure is one outcome. An unrecognisable input, and bounds OCCT rejects, both return
nil.
CurveToAnalyticalResult
Result of converting a 3D curve to its analytical form.
public struct CurveToAnalyticalResult: Sendable {
public let curve: Curve3D
public let newFirst: Double
public let newLast: Double
public let gap: Double
}
gap is the maximum deviation between the original and the recognized analytical curve. newFirst/newLast are expressed in the recognised curve’s own parameterisation, not the input’s: a BSpline circle examined over [π/2, 3π/2] reports a range starting at 0 on the Geom_Circle it returns.
CurveToAnalyticalResult.newLast
Curve3D.toAnalytical(tolerance:)
Attempts to convert this curve to an analytical form over its whole domain.
public func toAnalytical(tolerance: Double = 1e-4) -> Curve3D?
- Parameters:
tolerance— recognition tolerance. - Returns: The recognised curve, or
nilif no analytical form is recognised. - OCCT:
GeomConvert_CurveToAnaCurveviaOCCTGeomConvertCurveToAnalytical. - Example:
let circle = Curve3D.circle(center: .zero, normal: SIMD3(0, 0, 1), radius: 5)! if let analytical = circle.toBSpline()?.toAnalytical(tolerance: 1e-4) { print(analytical.curveKind) // .circle }
Curve3D.toAnalyticalWithGap(tolerance:)
Attempts to convert this curve to an analytical form over its whole domain, reporting the deviation. The full-range spelling of toAnalytical(tolerance:first:last:), and the curve counterpart of Surface.toAnalyticalWithGap(tolerance:).
public func toAnalyticalWithGap(tolerance: Double = 1e-4) -> CurveToAnalyticalResult?
- Parameters:
tolerance— recognition tolerance. - Returns:
CurveToAnalyticalResultwith the recognised curve, its range and the gap, ornil. - OCCT:
GeomConvert_CurveToAnaCurveviaOCCTGeomConvertCurveToAnalytical. - Example:
let bspline = Curve3D.circle(center: .zero, normal: SIMD3(0, 0, 1), radius: 5)!.toBSpline()! if let r = bspline.toAnalyticalWithGap(tolerance: 1e-4) { print(r.gap) // how far the BSpline strayed from the circle }
Curve3D.toAnalytical(tolerance:first:last:)
Attempts to convert this curve to an analytical form (line, circle, ellipse, etc.) over a chosen parameter range, so a curve that is a circle along part of its domain can be recognised there even when the whole domain is not.
public func toAnalytical(tolerance: Double, first: Double, last: Double) -> CurveToAnalyticalResult?
- Parameters:
tolerance— recognition tolerance;first,last— parameter range to examine. - Returns:
CurveToAnalyticalResultwith the simplified curve and gap, ornilif no analytical form is recognised. - OCCT:
GeomConvert_CurveToAnaCurveviaOCCTGeomConvertCurveToAnalytical. - Example:
if let r = bsplineCurve.toAnalytical(tolerance: 1e-4, first: 0, last: 1) { print(r.curve.curveKind, r.gap) }
Curve3D.arePointsLinear(_:tolerance:)
Checks whether a set of 3D points are collinear within a tolerance.
public static func arePointsLinear(_ points: [SIMD3<Double>],
tolerance: Double) -> (isLinear: Bool, deviation: Double)
- Parameters:
points— array of 3D points;tolerance— collinearity tolerance. - Returns:
(isLinear, deviation)—isLinearindicates collinearity;deviationis the maximum perpendicular distance from the best-fit line. - OCCT:
GeomConvert_ConvType::IsLinearviaOCCTGeomConvertIsLinear. - Example:
let pts: [SIMD3<Double>] = [.zero, SIMD3(1,0,0), SIMD3(2,0,0)] let (linear, dev) = Curve3D.arePointsLinear(pts, tolerance: 1e-6) // linear == true, dev ≈ 0
SurfaceToAnalyticalResult
Result of converting a surface to its analytical form.
public struct SurfaceToAnalyticalResult: Sendable {
public let surface: Surface
public let gap: Double
}
Surface.toAnalyticalWithGap(tolerance:)
Attempts to convert this surface to an analytical form.
public func toAnalyticalWithGap(tolerance: Double) -> SurfaceToAnalyticalResult?
- Parameters:
tolerance— recognition tolerance. - Returns:
SurfaceToAnalyticalResultwith the simplified surface and deviation, ornilif no analytical form is recognised. - OCCT:
GeomConvert_SurfToAnaSurfviaOCCTGeomConvertSurfToAnalytical. - Example:
if let r = bsplineSurface.toAnalyticalWithGap(tolerance: 1e-5) { print(r.surface.surfaceKind, r.gap) }
Surface.toAnalyticalWithGap(tolerance:uMin:uMax:vMin:vMax:)
Attempts to convert this surface to an analytical form within UV bounds.
public func toAnalyticalWithGap(tolerance: Double,
uMin: Double, uMax: Double,
vMin: Double, vMax: Double) -> SurfaceToAnalyticalResult?
- Parameters:
tolerance— recognition tolerance;uMin,uMax,vMin,vMax— UV parameter bounds to consider. - Returns:
SurfaceToAnalyticalResult, ornilon failure. Inverted bounds (uMin > uMax) are rejected rather than normalised. - OCCT:
GeomConvert_SurfToAnaSurf(bounded variant) viaOCCTGeomConvertSurfToAnalyticalBounded. - Example:
let d = bsplineSurface.domain if let r = bsplineSurface.toAnalyticalWithGap(tolerance: 1e-4, uMin: d.uMin, uMax: (d.uMin + d.uMax) / 2, vMin: d.vMin, vMax: d.vMax) { print(r.surface.surfaceKind) }
Surface.isCanonical
Whether this surface is already in a canonical (analytical) form.
public var isCanonical: Bool { get }
- Returns:
trueif the surface is a plane, sphere, cylinder, cone, or torus rather than a BSpline. - OCCT:
GeomConvert_ConvType::IsCanonicalviaOCCTGeomConvertIsCanonical.
Polygon2D
Polygon2D is a standalone Swift class wrapping Poly_Polygon2D — a sequence of 2D points used to represent a parametric-space polygon on a face.
Polygon2D.create(points:)
Creates a 2D polygon from an array of 2D points.
public static func create(points: [SIMD2<Double>]) -> Polygon2D?
- Parameters:
points— ordered sequence of 2D points. - Returns:
Polygon2D, ornilon failure. - OCCT:
Poly_Polygon2DviaOCCTPolyPolygon2DCreate. - Example:
if let poly = Polygon2D.create(points: [SIMD2(0,0), SIMD2(1,0), SIMD2(0.5,1)]) { print(poly.nodeCount) // 3 }
Polygon2D.nodeCount
The number of nodes in this polygon.
public var nodeCount: Int { get }
- OCCT:
Poly_Polygon2D::NbNodesviaOCCTPolyPolygon2DNbNodes.
Polygon2D.node(at:)
Returns the 2D point at a given 0-based index.
public func node(at index: Int) -> SIMD2<Double>?
- Parameters:
index— 0-based node index. - Returns:
SIMD2<Double>position, ornilif the index is out of range. - OCCT:
Poly_Polygon2D::NodesviaOCCTPolyPolygon2DNode.
Polygon2D.nodes()
Returns all nodes.
public func nodes() -> [SIMD2<Double>]
- Returns: Array of all 2D node positions in sequence order.
Polygon2D.deflection
The deflection value associated with this polygon.
public var deflection: Double { get set }
- OCCT:
Poly_Polygon2D::Deflection/SetDeflectionviaOCCTPolyPolygon2DDeflection/OCCTPolyPolygon2DSetDeflection.
Polygon2D.copy()
Creates a deep copy of this polygon.
public func copy() -> Polygon2D?
- Returns: Independent copy, or
nilon failure. - OCCT:
Poly_Polygon2D::CopyviaOCCTPolyPolygon2DCopy.
Triangulation
Triangulation wraps Poly_Triangulation — a 3D mesh defined by node positions and triangle vertex indices. Used as input to BRepGraph.createTriangulationRep(_:) for populating the cached mesh tier of a graph. Triangle indices are 0-based on the Swift boundary; the bridge converts to OCCT’s 1-based representation internally.
Triangulation.create(nodes:triangles:)
Creates a triangulation from node positions and triangle vertex indices.
public static func create(nodes: [SIMD3<Double>], triangles: [Int]) -> Triangulation?
- Parameters:
nodes— 3D node positions;triangles— triangle vertex indices, 0-based, three per triangle (triangles.countmust be a multiple of 3). - Returns:
Triangulation, ornilif inputs are empty,triangles.countis not a multiple of 3, or any index is out of range. - OCCT:
Poly_Triangulation(nbNodes, nbTriangles)viaOCCTPolyTriangulationCreate. - Example:
let nodes: [SIMD3<Double>] = [SIMD3(0,0,0), SIMD3(1,0,0), SIMD3(0,1,0)] if let tri = Triangulation.create(nodes: nodes, triangles: [0, 1, 2]) { print(tri.triangleCount) // 1 }
Triangulation.nodeCount
The number of nodes.
public var nodeCount: Int { get }
- OCCT:
Poly_Triangulation::NbNodesviaOCCTPolyTriangulationNbNodes.
Triangulation.triangleCount
The number of triangles.
public var triangleCount: Int { get }
- OCCT:
Poly_Triangulation::NbTrianglesviaOCCTPolyTriangulationNbTriangles.
Triangulation.node(at:)
Returns the 3D position of a node at a given 0-based index.
public func node(at index: Int) -> SIMD3<Double>?
- Parameters:
index— 0-based node index. - Returns: Node position, or
nilif out of range. - OCCT:
Poly_Triangulation::NodeviaOCCTPolyTriangulationNode.
Triangulation.triangle(at:)
Returns the three 0-based vertex indices for a triangle.
public func triangle(at index: Int) -> (Int, Int, Int)?
- Parameters:
index— 0-based triangle index. - Returns: Tuple of three 0-based node indices, or
nilif out of range. - OCCT:
Poly_Triangulation::TriangleviaOCCTPolyTriangulationTriangle. - Example:
if let (n0, n1, n2) = tri.triangle(at: 0) { let p0 = tri.node(at: n0) }
Triangulation.deflection
The deflection value of this triangulation.
public var deflection: Double { get set }
- OCCT:
Poly_Triangulation::Deflection/SetDeflectionviaOCCTPolyTriangulationDeflection/OCCTPolyTriangulationSetDeflection.
Polygon3D
Polygon3D wraps Poly_Polygon3D — a sequence of 3D points with optional curve parameters, used to represent an edge approximation in 3D space.
Polygon3D.create(points:)
Creates a 3D polygon from an array of 3D points.
public static func create(points: [SIMD3<Double>]) -> Polygon3D?
- Parameters:
points— ordered sequence of 3D points. - Returns:
Polygon3D, ornilon failure. - OCCT:
Poly_Polygon3DviaOCCTPolyPolygon3DCreate.
Polygon3D.create(points:parameters:)
Creates a 3D polygon with curve parameters.
public static func create(points: [SIMD3<Double>], parameters: [Double]) -> Polygon3D?
- Parameters:
points— ordered 3D points;parameters— corresponding curve parameter values (must have the same count aspoints). - Returns:
Polygon3Dwith parameters, ornilon failure. - OCCT:
Poly_Polygon3D(parameterised overload) viaOCCTPolyPolygon3DCreateWithParams. - Example:
let pts: [SIMD3<Double>] = [SIMD3(0,0,0), SIMD3(5,0,0), SIMD3(10,0,0)] let params: [Double] = [0, 0.5, 1] if let poly = Polygon3D.create(points: pts, parameters: params) { print(poly.hasParameters) // true }
Polygon3D.nodeCount
The number of nodes.
public var nodeCount: Int { get }
- OCCT:
Poly_Polygon3D::NbNodesviaOCCTPolyPolygon3DNbNodes.
Polygon3D.node(at:)
Returns the 3D position at a given 0-based node index.
public func node(at index: Int) -> SIMD3<Double>?
- Parameters:
index— 0-based node index. - Returns: Node position, or
nilif out of range. - OCCT:
Poly_Polygon3D::NodesviaOCCTPolyPolygon3DNode.
Polygon3D.nodes()
Returns all nodes.
public func nodes() -> [SIMD3<Double>]
- Returns: Array of all 3D node positions in sequence order.
Polygon3D.hasParameters
Whether this polygon has curve parameters.
public var hasParameters: Bool { get }
- OCCT:
Poly_Polygon3D::HasParametersviaOCCTPolyPolygon3DHasParameters.
Polygon3D.parameter(at:)
Returns the curve parameter at a given 0-based index.
public func parameter(at index: Int) -> Double
- Parameters:
index— 0-based index. - Returns: The curve parameter value. Returns 0 if
hasParametersisfalse. - OCCT:
Poly_Polygon3D::ParameterviaOCCTPolyPolygon3DParameter.
Polygon3D.deflection
The deflection value of this polygon.
public var deflection: Double { get set }
- OCCT:
Poly_Polygon3D::Deflection/SetDeflectionviaOCCTPolyPolygon3DDeflection/OCCTPolyPolygon3DSetDeflection.
PolygonOnTriangulation
PolygonOnTriangulation wraps Poly_PolygonOnTriangulation — a polygon defined as a sequence of indices into a shared Triangulation, with optional curve parameters. Used to associate an edge’s 2D approximation with a face triangulation.
PolygonOnTriangulation.create(nodeIndices:)
Creates a polygon from node indices into a triangulation.
public static func create(nodeIndices: [Int32]) -> PolygonOnTriangulation?
- Parameters:
nodeIndices— array of 0-based node indices into the associated triangulation. - Returns:
PolygonOnTriangulation, ornilon failure. - OCCT:
Poly_PolygonOnTriangulationviaOCCTPolyPolygonOnTriCreate.
PolygonOnTriangulation.create(nodeIndices:parameters:)
Creates a polygon from node indices with curve parameters.
public static func create(nodeIndices: [Int32], parameters: [Double]) -> PolygonOnTriangulation?
- Parameters:
nodeIndices— 0-based node indices;parameters— corresponding curve parameter values. - Returns:
PolygonOnTriangulationwith parameters, ornilon failure. - OCCT:
Poly_PolygonOnTriangulation(parameterised overload) viaOCCTPolyPolygonOnTriCreateWithParams. - Example:
if let poly = PolygonOnTriangulation.create(nodeIndices: [0, 5, 12], parameters: [0, 0.5, 1]) { print(poly.nodeCount) // 3 }
PolygonOnTriangulation.nodeCount
The number of nodes referenced by this polygon.
public var nodeCount: Int { get }
- OCCT:
Poly_PolygonOnTriangulation::NbNodesviaOCCTPolyPolygonOnTriNbNodes.
PolygonOnTriangulation.nodeIndex(at:)
Returns the triangulation node index at a given 0-based position.
public func nodeIndex(at position: Int) -> Int
- Parameters:
position— 0-based position in the polygon’s node sequence. - Returns: 0-based index into the associated triangulation’s node array.
- OCCT:
Poly_PolygonOnTriangulation::NodeviaOCCTPolyPolygonOnTriNode.
PolygonOnTriangulation.hasParameters
Whether this polygon has curve parameters.
public var hasParameters: Bool { get }
- OCCT:
Poly_PolygonOnTriangulation::HasParametersviaOCCTPolyPolygonOnTriHasParameters.
PolygonOnTriangulation.parameter(at:)
Returns the curve parameter at a given 0-based index.
public func parameter(at index: Int) -> Double
- Parameters:
index— 0-based index. - Returns: The curve parameter value. Returns 0 if
hasParametersisfalse. - OCCT:
Poly_PolygonOnTriangulation::ParameterviaOCCTPolyPolygonOnTriParameter.
PolygonOnTriangulation.deflection
The deflection value of this polygon.
public var deflection: Double { get set }
- OCCT:
Poly_PolygonOnTriangulation::Deflection/SetDeflectionviaOCCTPolyPolygonOnTriDeflection/OCCTPolyPolygonOnTriSetDeflection.
PolygonOnTriangulation.copy()
Creates a deep copy of this polygon.
public func copy() -> PolygonOnTriangulation?
- Returns: Independent copy, or
nilon failure. - OCCT:
Poly_PolygonOnTriangulation::CopyviaOCCTPolyPolygonOnTriCopy.
PolygonOnTriangulation.setNodes(_:)
Overwrites the node-index array in place.
@discardableResult
public func setNodes(_ nodeIndices: [Int32]) -> Bool
The supplied array must have the same length as nodeCount.
- Parameters:
nodeIndices— replacement node index array (same count asnodeCount). - Returns:
trueon success,falseon size mismatch. - OCCT:
Poly_PolygonOnTriangulation::ChangeNodeArrayviaOCCTPolyPolygonOnTriSetNodes.
PolygonOnTriangulation.setParameters(_:)
Overwrites the parameter array in place.
@discardableResult
public func setParameters(_ params: [Double]) -> Bool
Requires hasParameters == true and the array length must equal nodeCount.
- Parameters:
params— replacement parameter array. - Returns:
trueon success,falseifhasParametersisfalseor lengths mismatch. - OCCT:
Poly_PolygonOnTriangulation::ChangeParameterArrayviaOCCTPolyPolygonOnTriSetParameters.
Mesh Node Merging
MergedMeshData
Output of merging triangulation nodes across all faces of a meshed shape.
public struct MergedMeshData: Sendable {
public let vertices: [SIMD3<Float>]
public let normals: [SIMD3<Float>]
public let indices: [UInt32]
public let triangleCount: Int
public let vertexCount: Int
}
Normals are computed per merged vertex using the smoothAngle threshold.
mergedMeshNodes(from:smoothAngle:mergeTolerance:)
Merges nodes from all face triangulations of a meshed shape into a single indexed mesh suitable for GPU upload.
public func mergedMeshNodes(from shape: Shape,
smoothAngle: Double,
mergeTolerance: Double = 0.0) -> MergedMeshData?
- Parameters:
shape: a shape that has been triangulated (e.g., viaShape.mesh(linearDeflection:angularDeflection:));smoothAngle: normal-smoothing angle threshold in radians;mergeTolerance: distance threshold for merging nodes (0 = positional identity only). - Returns:
MergedMeshDatawith interleaved vertex, normal, and index arrays, ornilif the shape has no triangulation or the output would exceed 1 000 000 vertices / 3 000 000 indices. - OCCT:
BRep_Builderface iteration +Poly_TriangulationviaOCCTPolyMergeNodes. - Example:
let shape = Shape.box(width: 10, height: 10, depth: 10)! _ = shape.mesh(linearDeflection: 0.1) if let mesh = mergedMeshNodes(from: shape, smoothAngle: .pi / 6) { // Upload mesh.vertices and mesh.indices to a Metal vertex buffer print(mesh.vertexCount, mesh.triangleCount) }