Appearance
Geometry Create
Curve creation — construct new geometric curves from point handles (all-handle model). Each method takes point handles (Vec3Handle) and returns an opaque curve handle (LineHandle or ArcHandle) that you pass to query, update, or entity-creation methods. Read geometry back as plain values via the snaptrude.core.geom.query.* methods. Accessed via snaptrude.core.geom.create.
The brepFrom* constructors mint solid B-reps. The OpenCascade-backed ones — fillet, chamfer, offset, shell, split, sweep, revolution, and the multi-section loft — only ever return solids Snaptrude can represent: planar and cylindrical faces bounded by straight and arc edges. Anything that would produce other curved surfaces — corner blends between adjacent filleted/chamfered edges, curved-spine sweeps, inclined or arc-segment revolutions — throws instead of silently approximating. (Twisted or structure-mismatched straight-edged lofts build with triangulated planar side faces — planar triangles, not approximation.)
Functions
line(startPoint, endPoint)
Create a straight line segment between two point handles. Host API call — returns a handle.
- Parameters:
startPoint:Vec3Handle— Start point of the line segmentendPoint:Vec3Handle— End point of the line segment
- Returns:
LineHandle— The new line
ts
const line = await snaptrude.core.geom.create.line(startPoint, endPoint);arc(startPoint, endPoint, centrePoint, axis)
Create a circular arc from its start, end, centre, and axis point handles. Host API call — returns a handle.
- Parameters:
startPoint:Vec3Handle— Start point of the arcendPoint:Vec3Handle— End point of the arccentrePoint:Vec3Handle— Centre point of the arcaxis:Vec3Handle— Axis direction of the arc
- Returns:
ArcHandle— The new arc
ts
const arc = await snaptrude.core.geom.create.arc(startPoint, endPoint, centrePoint, axis);circle(centrePoint, axis, radius)
Create a circle from a centre point, an axis (plane normal), and a radius. Host API call — returns a handle. A circle is a closed curve; read it back via snaptrude.core.geom.query.circle.*.
- Parameters:
centrePoint:Vec3Handle— Centre point of the circleaxis:Vec3Handle— Axis direction (plane normal) of the circleradius:number— Radius of the circle
- Returns:
CircleHandle— The new circle
ts
const circle = await snaptrude.core.geom.create.circle(centrePoint, axis, 2);profileFromLinePoints(points)
Create a closed profile from an ordered list of point handles connected by line segments (last auto-connected to first). Host API call — returns a handle.
- Parameters:
points:Vec3Handle[]— Ordered vertex handles of the profile
- Returns:
ProfileHandle— The new profile
ts
const profile = await snaptrude.core.geom.create.profileFromLinePoints(points);profileFromCurves(curves)
Create a profile from an ordered list of curve handles forming a closed loop. Host API call — returns a handle.
- Parameters:
curves:CurveHandle[]— Ordered curve handles forming a closed loop
- Returns:
ProfileHandle— The new profile
ts
const profile = await snaptrude.core.geom.create.profileFromCurves([line, arc]);profileRect(width, depth, center?)
Create an axis-aligned rectangle profile (XZ plane) of width × depth, centred at center (default origin). Pair with design.create.space / design.create.spaces to author a rectangular (box) space.
- Parameters:
width:number— Extent along Xdepth:number— Extent along Zcenter:Vec3Handle(optional, default origin) — Rectangle centre
- Returns:
ProfileHandle— The new rectangle profile
ts
const rect = await snaptrude.core.geom.create.profileRect(4, 3);
const contour = await snaptrude.core.geom.create.contourFromProfile(rect);
const space = await snaptrude.design.create.space(contour, 3);contourFromProfile(outer)
Create a contour (an outer profile loop, no holes). Host API call — returns a handle.
- Parameters:
outer:ProfileHandle— The outer boundary loop
- Returns:
ContourHandle— The new contour
ts
const contour = await snaptrude.core.geom.create.contourFromProfile(outer);contourFromProfiles(outer, holes?)
Create a contour from an outer profile plus zero or more inner profiles (holes). Host API call — returns a handle.
- Parameters:
outer:ProfileHandle— The outer boundary loopholes:ProfileHandle[](optional) — Optional inner hole loops
- Returns:
ContourHandle— The new contour
ts
const contour = await snaptrude.core.geom.create.contourFromProfiles(outer, [hole]);brepFromFaces(faces)
Create a closed solid B-rep from explicit face loops. Faces are plain arrays of {x, y, z} point components, NOT point handles (bulk-data precedent: design.query.geometry.getTriangulatedMeshes), in raw Babylon units.
Each face is one planar loop of ≥3 points; ≥4 faces are required. Loops may be authored in any consistent winding — the host validates edge coherence (every edge shared by exactly two faces, in opposite directions) and fixes the global orientation so faces point outward. Faces must be planar and the solid must be closed; holes in faces are not supported (v1).
Inspect the result via core.geom.query.brep.*, or commit it to the scene with design.create.massFromBrep.
- Parameters:
faces:{ x, y, z }[][]— Face loops, each an ordered array of points (≥3 points per face, ≥4 faces)
- Returns:
BrepHandle— The new solid - Throws: If a face is degenerate or non-planar, an edge is not shared by exactly two faces (open shell, non-manifold, or inconsistent winding), or the faces do not form a valid closed solid
ts
// A pyramid: square base + 4 triangular sides
const apex = { x: 0, y: 4, z: 0 };
const a = { x: -2, y: 0, z: -2 };
const b = { x: 2, y: 0, z: -2 };
const c = { x: 2, y: 0, z: 2 };
const d = { x: -2, y: 0, z: 2 };
const brep = await snaptrude.core.geom.create.brepFromFaces([
[a, d, c, b], // base
[a, b, apex],
[b, c, apex],
[c, d, apex],
[d, a, apex]
]);
const faceCount = await snaptrude.core.geom.query.brep.getFaceCount(brep); // 5brepFromExtrusion(contour, direction, amount)
Create a closed solid B-rep by extruding a contour along a direction. The direction is normalised by the host, so amount is the extrusion distance in raw Babylon units (negative extrudes the opposite way). The contour is copied — the input handle is never mutated. Holes and arc profiles extrude natively (an arc/circle cross-section yields a cylinder).
Inspect the result via core.geom.query.brep.*, or commit it to the scene with design.create.massFromBrep.
- Parameters:
contour:ContourHandle— The cross-section to extrude (outer profile + optional holes)direction:{ x, y, z }— Extrusion direction as plain components (non-zero; normalised by the host)amount:number— Extrusion distance (non-zero; negative extrudes opposite todirection)
- Returns:
BrepHandle— The new solid - Throws: If the extrusion is degenerate (zero amount, zero direction, or a direction lying in the contour plane) or the contour cannot be extruded into a valid solid
ts
// A 4 × 3 × 3 box
const rect = await snaptrude.core.geom.create.profileRect(4, 3);
const contour = await snaptrude.core.geom.create.contourFromProfile(rect);
const box = await snaptrude.core.geom.create.brepFromExtrusion(contour, { x: 0, y: 1, z: 0 }, 3);
await snaptrude.core.geom.query.brep.getFaceCount(box); // 6
// A cylinder: circle authored as two semicircular arcs (radius 2, height 5)
const c = await snaptrude.core.math.vec3.new(0, 0, 0);
const up = await snaptrude.core.math.vec3.new(0, 1, 0);
const p1 = await snaptrude.core.math.vec3.new(2, 0, 0);
const p2 = await snaptrude.core.math.vec3.new(-2, 0, 0);
const arc1 = await snaptrude.core.geom.create.arc(p1, p2, c, up);
const arc2 = await snaptrude.core.geom.create.arc(p2, p1, c, up);
const circleProfile = await snaptrude.core.geom.create.profileFromCurves([arc1, arc2]);
const circleContour = await snaptrude.core.geom.create.contourFromProfile(circleProfile);
const cylinder = await snaptrude.core.geom.create.brepFromExtrusion(
circleContour,
{ x: 0, y: 1, z: 0 },
5
);
await snaptrude.core.geom.query.brep.getFaceCount(cylinder); // 4 — top, bottom, 2 cylindrical sidesbrepFromLoft(bottomContour, topContour, intermediateContours?, options?)
Create a closed solid B-rep by lofting between a bottom and a top contour, optionally through intermediate cross-sections. Sections may have different edge counts: Snaptrude deterministically inserts vertices on the smaller sections at the perimeter positions that best line up with the largest section's corners, then lofts as usual (multi-section lofts normalize every section to the largest edge count in one global pass). For straight-edged sections the seam is auto-aligned too — the corner-to-corner correspondence that avoids a twist is chosen for you, so sections drawn from different starting corners loft cleanly; arc-bearing sections keep the authored correspondence. When no planar quad correspondence exists at all — a rotated or twisted top, or sections whose edge directions differ (square to hexagon) — hole-free straight-edged pairs still build: planar side quads stay quads and each warped one splits into two triangles (triangles are always planar), so the loft succeeds with a faceted-side solid instead of throwing. Arc-bearing or holed pairs keep the planar-quad requirement (matching hole counts allowed in the two-section form) and throw on twist. With intermediates the loft is a chain of ruled segments folded into one solid: sections must be planar and hole-free, ordered bottom → intermediates → top. The contours are copied — the input handles are never mutated.
Both of those automatic behaviors are switchable via options (each defaults to "auto", the behavior above):
compatibility: "strict"— refuse mismatched edge counts instead of auto-matching. The check runs before any vertex insertion: if outer edge counts differ across sections (or, in the two-section form, a paired hole's edge counts differ), the loft throwsVALIDATIONnaming the sections and their counts, and no normalization runs. With equal counts the result is identical to"auto".seamAlignment: "authored"— keep the authored start-vertex correspondence instead of searching seam rotations, so corners pair exactly as drawn. A pair that only lofts cleanly under a rotated seam then builds the drawn (twisted) correspondence through the triangulated lane instead of being rescued.
{ compatibility: "strict", seamAlignment: "authored" } together reproduce pre-0.9.4 lofting exactly.
- Parameters:
bottomContour:ContourHandle— The bottom cross-sectiontopContour:ContourHandle— The top cross-section (edge counts may differ — vertices are auto-inserted to match)intermediateContours:ContourHandle[](optional) — In-between cross-sections, ordered bottom to top (hole-free; edge counts may differ)options:{ compatibility?, seamAlignment? }(optional) — Behavior switches:compatibilityis"strict"(throw on mismatched edge counts) or"auto"(default — insert vertices to match);seamAlignmentis"authored"(keep the authored corner correspondence) or"auto"(default — search seam rotations on straight-edged sections)
- Returns:
BrepHandle— The new solid - Throws:
VALIDATIONif the contours are coincident, their hole counts differ, auto-matching would pair an arc with a straight edge (or with a non-congruent arc), any section has holes when intermediates are present (multi-section lofts take hole-free sections), a section is non-planar, or an arc-bearing/holed pair has a non-planar side face (straight-edged hole-free pairs triangulate instead of throwing); withcompatibility: "strict", if section edge counts (or hole-pair edge counts) differ — the error names the sections and counts and its hint points atcompatibility: "auto";OPERATION_FAILEDif the lofted segments cannot be joined into a valid solid
ts
// A square frustum: 4m base lofted to a 2m top, 3m up
const base = await snaptrude.core.geom.create.profileRect(4, 4);
const top = await snaptrude.core.geom.create.profileRect(
2,
2,
await snaptrude.core.math.vec3.new(0, 3, 0)
);
const frustum = await snaptrude.core.geom.create.brepFromLoft(
await snaptrude.core.geom.create.contourFromProfile(base),
await snaptrude.core.geom.create.contourFromProfile(top)
);
await snaptrude.core.geom.query.brep.getFaceCount(frustum); // 6
// A three-section loft: base → mid → top (hole-free; edge counts may differ)
const mid = await snaptrude.core.geom.create.profileRect(
3,
3,
await snaptrude.core.math.vec3.new(0, 1.5, 0)
);
const tapered = await snaptrude.core.geom.create.brepFromLoft(
await snaptrude.core.geom.create.contourFromProfile(base),
await snaptrude.core.geom.create.contourFromProfile(top),
[await snaptrude.core.geom.create.contourFromProfile(mid)]
);
await snaptrude.design.create.massFromBrep(tapered, "tapered tower");
// Exact authored lofting: throw on mismatched edge counts, keep the drawn seam
const exact = await snaptrude.core.geom.create.brepFromLoft(
await snaptrude.core.geom.create.contourFromProfile(base),
await snaptrude.core.geom.create.contourFromProfile(top),
undefined,
{ compatibility: "strict", seamAlignment: "authored" }
);brepFromMesh(positions, faces)
Create a closed solid B-rep from indexed mesh data: a vertex position array plus face loops of indices into it. The indexed form of brepFromFaces — same validation (planar faces, closed manifold solid, every edge shared by exactly two faces) after the indices are expanded to point loops.
- Parameters:
positions:{ x, y, z }[]— Vertex positions (≥4)faces:number[][]— Face loops, each an ordered array of indices intopositions(≥3 indices per face, ≥4 faces)
- Returns:
BrepHandle— The new solid - Throws: If an index is not an integer within
positionsbounds, a face is degenerate or non-planar, or the faces do not form a valid closed solid
ts
// A tetrahedron from 4 vertices and 4 triangular faces
const positions = [
{ x: 0, y: 0, z: 0 },
{ x: 4, y: 0, z: 0 },
{ x: 2, y: 0, z: 4 },
{ x: 2, y: 3, z: 1.5 }
];
const tetra = await snaptrude.core.geom.create.brepFromMesh(positions, [
[0, 2, 1], // base
[0, 1, 3],
[1, 2, 3],
[2, 0, 3]
]);
await snaptrude.core.geom.query.brep.getVertexCount(tetra); // 4brepFromUnion(a, b)
Create a closed solid B-rep as the boolean union of two breps (a ∪ b). Inputs are read-only and may be authored breps (any core.geom.create brep constructor) or scene-derived breps from design.query.geometry.getBrep; coordinates are combined as-is, so both inputs must share a frame. The result must be a single solid — disjoint inputs are rejected.
The first boolean call loads the OpenCascade geometry kernel (a wasm of tens of MB — expect a pause of seconds); subsequent calls are fast.
- Parameters:
a:BrepHandle— First solidb:BrepHandle— Second solid
- Returns:
Promise<BrepHandle>— The union as a new solid - Throws: If the inputs do not overlap or touch (the union would be disjoint solids) or the result cannot be built as a valid single solid
ts
// Two overlapping 4×4×3 boxes → one L-shaped solid
const boxAt = async (cx, cz, baseY = 0) => {
const centre = await snaptrude.core.math.vec3.new(cx, baseY, cz);
const rect = await snaptrude.core.geom.create.profileRect(4, 4, centre);
const contour = await snaptrude.core.geom.create.contourFromProfile(rect);
return snaptrude.core.geom.create.brepFromExtrusion(contour, { x: 0, y: 1, z: 0 }, 3);
};
const merged = await snaptrude.core.geom.create.brepFromUnion(
await boxAt(0, 0),
await boxAt(2, 2, 1)
);brepFromSubtraction(a, b)
Create a closed solid B-rep as the boolean subtraction of two breps: a minus b — b is cut away from a. Argument order matters. Inputs are read-only and may be authored or scene-derived breps (see brepFromUnion); coordinates are combined as-is.
The first boolean call loads the OpenCascade geometry kernel (a wasm of tens of MB — expect a pause of seconds); subsequent calls are fast.
- Parameters:
a:BrepHandle— The solid to subtract fromb:BrepHandle— The solid to remove froma
- Returns:
Promise<BrepHandle>—aminusbas a new solid - Throws: If
bconsumesaentirely (empty result), the result splits into disjoint solids, or it cannot be built as a valid single solid
ts
// Carve the courtyard out of the building mass — building minus courtyard
const carved = await snaptrude.core.geom.create.brepFromSubtraction(buildingBrep, courtyardBrep);brepFromIntersection(a, b)
Create a closed solid B-rep as the boolean intersection of two breps (a ∩ b — the shared volume only). Inputs are read-only and may be authored or scene-derived breps (see brepFromUnion); coordinates are combined as-is.
The first boolean call loads the OpenCascade geometry kernel (a wasm of tens of MB — expect a pause of seconds); subsequent calls are fast.
- Parameters:
a:BrepHandle— First solidb:BrepHandle— Second solid
- Returns:
Promise<BrepHandle>— The shared volume as a new solid - Throws: If the inputs do not overlap (empty intersection), the result splits into disjoint solids, or it cannot be built as a valid single solid
ts
// Clip the tower to the zoning envelope
const clipped = await snaptrude.core.geom.create.brepFromIntersection(towerBrep, envelopeBrep);brepFromFillet(brep, edges, radius)
Rounds the given straight edges of a solid with a constant radius. Returns a new brep; the input brep is read-only.
v1 fillets straight edges only, and no two filleted edges may share a vertex — corner blends produce spherical patches Snaptrude cannot represent. Runs on the OpenCascade kernel (the first kernel call loads a wasm of tens of MB — expect a pause of seconds).
Inspect the result via core.geom.query.brep.*, or commit it to the scene with design.create.massFromBrep.
- Parameters:
brep:BrepHandle— The solid whose edges to roundedges:EdgeHandle[]— The straight edges to fillet (≥1, all onbrep, no two sharing a vertex)radius:number— Fillet radius (positive, finite)
- Returns:
BrepHandle— The filleted solid as a new brep - Throws:
VALIDATIONifedgesis empty,radiusis not a positive finite number, an edge is not onbrep, an edge is an arc (v1 fillets straight edges only), or two edges share a vertex (fillet non-adjacent edges);OPERATION_FAILEDif the kernel cannot build the fillet (the radius likely exceeds the adjacent face size — reduce it) or the result contains curved surfaces Snaptrude cannot represent
ts
// Build a 4 × 3 × 3 box, round one edge, commit as a mass
const rect = await snaptrude.core.geom.create.profileRect(4, 3);
const contour = await snaptrude.core.geom.create.contourFromProfile(rect);
const box = await snaptrude.core.geom.create.brepFromExtrusion(contour, { x: 0, y: 1, z: 0 }, 3);
const edges = await snaptrude.core.geom.query.brep.listEdges(box);
const rounded = await snaptrude.core.geom.create.brepFromFillet(box, [edges[0]], 0.3);
await snaptrude.design.create.massFromBrep(rounded, "rounded box");brepFromOffset(brep, distance)
Grows or shrinks a solid by offsetting every face — positive distance moves faces outward, negative moves them inward. Returns a new brep; the input brep is read-only.
Runs on the OpenCascade kernel (the first kernel call loads a wasm of tens of MB — expect a pause of seconds).
Inspect the result via core.geom.query.brep.*, or commit it to the scene with design.create.massFromBrep.
- Parameters:
brep:BrepHandle— The solid to offsetdistance:number— Offset distance (finite, non-zero; positive grows, negative shrinks)
- Returns:
BrepHandle— The offset solid as a new brep - Throws:
VALIDATIONifdistanceis not finite, is too small to offset anything, or an inward distance consumes the solid entirely;OPERATION_FAILEDif the kernel cannot build the offset or the result contains curved surfaces Snaptrude cannot represent
ts
const rect = await snaptrude.core.geom.create.profileRect(4, 3);
const contour = await snaptrude.core.geom.create.contourFromProfile(rect);
const box = await snaptrude.core.geom.create.brepFromExtrusion(contour, { x: 0, y: 1, z: 0 }, 3);
const grown = await snaptrude.core.geom.create.brepFromOffset(box, 0.5);
await snaptrude.design.create.massFromBrep(grown, "grown box");brepFromShell(brep, openFaces, thickness)
Hollows a solid into constant-thickness walls, removing the given faces as openings. Returns a new brep; the input brep is read-only. The outer surface is kept and the walls grow inward.
Runs on the OpenCascade kernel (the first kernel call loads a wasm of tens of MB — expect a pause of seconds).
Inspect the result via core.geom.query.brep.*, or commit it to the scene with design.create.massFromBrep.
- Parameters:
brep:BrepHandle— The solid to hollowopenFaces:FaceHandle[]— The faces to remove as openings (≥1, all onbrep)thickness:number— Wall thickness (positive, finite, smaller than half the solid's smallest span)
- Returns:
BrepHandle— The hollowed solid as a new brep - Throws:
VALIDATIONifopenFacesis empty, a face is not onbrep,thicknessis not a positive finite number, or the thickness is too large (it must be smaller than half the solid's smallest span);OPERATION_FAILEDif the kernel cannot build the shell or the result contains curved surfaces Snaptrude cannot represent
ts
// Hollow a box into 200mm walls with one face open
const rect = await snaptrude.core.geom.create.profileRect(4, 3);
const contour = await snaptrude.core.geom.create.contourFromProfile(rect);
const box = await snaptrude.core.geom.create.brepFromExtrusion(contour, { x: 0, y: 1, z: 0 }, 3);
const faces = await snaptrude.core.geom.query.brep.listFaces(box);
const hollow = await snaptrude.core.geom.create.brepFromShell(box, [faces[0]], 0.2);
await snaptrude.design.create.massFromBrep(hollow, "hollow box");brepsFromSplit(brep, planeOrigin, planeNormal)
Cuts a solid by an infinite plane and returns one brep per resulting piece — a single piece if the plane misses the solid. Returns new brep handles; the input brep is read-only. The plane is defined by a point on it and its normal direction.
Runs on the OpenCascade kernel (the first kernel call loads a wasm of tens of MB — expect a pause of seconds).
Inspect each result via core.geom.query.brep.*, or commit them to the scene with design.create.massFromBrep.
- Parameters:
brep:BrepHandle— The solid to splitplaneOrigin:{ x, y, z }— A point on the cutting plane as plain componentsplaneNormal:{ x, y, z }— The plane normal as plain components (non-zero)
- Returns:
BrepHandle[]— The resulting pieces (one per solid) - Throws:
VALIDATIONifplaneNormalis zero-length or the origin/normal components are not finite;OPERATION_FAILEDif the kernel cannot split the solid
ts
// Cut a 24m tower at 12m with a horizontal plane, commit each piece
const rect = await snaptrude.core.geom.create.profileRect(8, 8);
const contour = await snaptrude.core.geom.create.contourFromProfile(rect);
const tower = await snaptrude.core.geom.create.brepFromExtrusion(contour, { x: 0, y: 1, z: 0 }, 24);
const pieces = await snaptrude.core.geom.create.brepsFromSplit(
tower,
{ x: 0, y: 12, z: 0 },
{ x: 0, y: 1, z: 0 }
);
for (const piece of pieces) {
await snaptrude.design.create.massFromBrep(piece);
}brepFromChamfer(brep, edges, distance)
Bevels the given straight edges of a solid with a symmetric planar cut. Returns a new brep; the input brep is read-only.
v1 chamfers straight edges only, and no two chamfered edges may share a vertex. Runs on the OpenCascade kernel (the first kernel call loads a wasm of tens of MB — expect a pause of seconds).
Inspect the result via core.geom.query.brep.*, or commit it to the scene with design.create.massFromBrep.
- Parameters:
brep:BrepHandle— The solid whose edges to beveledges:EdgeHandle[]— The straight edges to chamfer (≥1, all onbrep, no two sharing a vertex)distance:number— Chamfer distance from the edge on each adjacent face (positive, finite)
- Returns:
BrepHandle— The chamfered solid as a new brep - Throws:
VALIDATIONifedgesis empty,distanceis not a positive finite number, an edge is not onbrep, an edge is an arc (v1 chamfers straight edges only), or two edges share a vertex (chamfer non-adjacent edges);OPERATION_FAILEDif the kernel cannot build the chamfer (the distance likely exceeds the adjacent face size — reduce it)
ts
const rect = await snaptrude.core.geom.create.profileRect(4, 3);
const contour = await snaptrude.core.geom.create.contourFromProfile(rect);
const box = await snaptrude.core.geom.create.brepFromExtrusion(contour, { x: 0, y: 1, z: 0 }, 3);
const edges = await snaptrude.core.geom.query.brep.listEdges(box);
const beveled = await snaptrude.core.geom.create.brepFromChamfer(box, [edges[0]], 0.1);
await snaptrude.design.create.massFromBrep(beveled, "beveled box");brepFromSweep(profile, path, options?)
Sweeps a planar profile along a polyline path, with mitred corners at each bend by default (chamfered corners via options.transition — see Corner transitions below). Returns a new brep; the input contour is read-only. The profile must be hole-free and must not lie in a plane containing the first path segment's direction.
Closed paths are supported: repeat the first point as the last and the sweep returns a closed ring solid (a picture frame, a duct loop) with planar mitred joints — no end caps. Closed paths must be planar with straight segments and ≥3 distinct corners, and the profile must be straight-edged (arcs would sweep into elliptical edges at the mitres). The ring's cross-section is the authored profile projected along the first leg onto the first mitre plane — a profile not perpendicular to the first leg is sheared, not rotated.
Taper via options: either startScale/endScale (linear interpolation by path length) or a per-vertex scales array (one entry per path point — the taper rate can change mid-run). Scales apply about the profile's own centroid. A uniform scale (all values equal) works wherever today's sweep works — the pre-scaled profile takes the exact same lane as an unscaled one. A varying scale takes straight open paths and straight-edged profiles only — the side faces are planar trapezoids; tapering across a bend or on an arc edge would create surfaces Snaptrude cannot represent, so it throws with the workaround: sweep each straight run separately with its own scales, then combine with brepFromUnion.
Corner transitions via options.transition: "miter" (the default) keeps the sharp mitred bend described above. { bevel: b } chamfers each turning corner by cutting b back along both adjacent legs — pure path preprocessing (the corner point is replaced by the two cut points, then the sweep runs as usual), so it works on open and closed paths and composes with a uniform scale; each leg must be long enough for the cuts its beveled corners take from it (b per adjacent beveled corner) and no corner may nearly double back (its chamfer would collapse), or the sweep throws naming the corner. Rounded corners are not available yet — the kernel represents them as revolution surfaces beyond Snaptrude's planar/cylindrical face ceiling; they arrive with curved-surface support. On a corner-less (straight) path transition has no effect, and the varying-scale taper takes straight paths only, so it never sees a corner.
Open paths run on the OpenCascade kernel (the first kernel call loads a wasm of tens of MB — expect a pause of seconds); closed rings and varying-scale tapers are computed host-side and never load the wasm.
Inspect the result via core.geom.query.brep.*, or commit it to the scene with design.create.massFromBrep.
- Parameters:
profile:ContourHandle— The cross-section to sweep (hole-free contour)path:{ x, y, z }[]— The polyline path as plain points (≥2; repeat the first point last for a closed ring)options:{ startScale?, endScale?, scales?, transition? }(optional) — Taper: endpoint scales interpolate linearly by path length, orscalesgives one factor per path point (mutually exclusive with the endpoint scales); each factor must be finite and between 0.001 and 1000. Corner transition:"miter"(default) or{ bevel }(chamfer distance, finite and between 0.001 and 1000; open and closed paths)
- Returns:
BrepHandle— The swept solid as a new brep - Throws:
VALIDATIONif the profile has holes, the path has fewer than 2 points, consecutive path points coincide, a coordinate is not finite, the profile plane contains the first path segment's direction (degenerate sweep), a closed path is non-planar / doubles back / has fewer than 3 distinct corners, the profile is arc-bearing or too large for a mitred corner on a closed path, a varying scale is used on a closed path / across a bend / with an arc-bearing profile, a scale collapses the profile below modeling tolerance, or thescalesarray length differs from the path length; for{ bevel }, if a path leg is too short for the cuts its beveled corners take from it or a corner nearly doubles back so its chamfer collapses (the errors name the corner);OPERATION_FAILEDif the kernel cannot sweep the profile into a valid solid or a transition patch is a curved surface Snaptrude cannot represent
ts
// Sweep a 0.4 × 0.4 duct profile up a riser and along the ceiling.
// profileRect lies in the XZ plane, so the first path segment must
// leave that plane — start the path going up (+Y).
const rect = await snaptrude.core.geom.create.profileRect(0.4, 0.4);
const profile = await snaptrude.core.geom.create.contourFromProfile(rect);
const duct = await snaptrude.core.geom.create.brepFromSweep(profile, [
{ x: 0, y: 0, z: 0 },
{ x: 0, y: 2.8, z: 0 },
{ x: 10, y: 2.8, z: 0 }
]);
await snaptrude.design.create.massFromBrep(duct, "duct");
// A closed rectangular ring — repeat the first point to close the loop.
const frameProfile = await snaptrude.core.geom.create.contourFromProfile(
await snaptrude.core.geom.create.profileRect(0.2, 0.2)
);
const frame = await snaptrude.core.geom.create.brepFromSweep(frameProfile, [
{ x: 0, y: 0, z: 0 },
{ x: 0, y: 3, z: 0 },
{ x: 4, y: 3, z: 0 },
{ x: 4, y: 0, z: 0 },
{ x: 0, y: 0, z: 0 }
]);
await snaptrude.core.geom.query.brep.getFaceCount(frame); // 16 — 4 legs × 4 edges, no caps
// A tapered column: 1 × 1 at the base, 2 × 2 at the top.
const columnProfile = await snaptrude.core.geom.create.contourFromProfile(
await snaptrude.core.geom.create.profileRect(1, 1)
);
const column = await snaptrude.core.geom.create.brepFromSweep(
columnProfile,
[
{ x: 0, y: 0, z: 0 },
{ x: 0, y: 10, z: 0 }
],
{ startScale: 1, endScale: 2 }
);
await snaptrude.design.create.massFromBrep(column, "tapered column");
// Chamfered corners: each ring corner is cut back 0.5 along both legs.
const chamferedFrame = await snaptrude.core.geom.create.brepFromSweep(
frameProfile,
[
{ x: 0, y: 0, z: 0 },
{ x: 0, y: 3, z: 0 },
{ x: 4, y: 3, z: 0 },
{ x: 4, y: 0, z: 0 },
{ x: 0, y: 0, z: 0 }
],
{ transition: { bevel: 0.5 } }
);brepFromRevolution(profile, axisOrigin, axisDirection, angleInDegrees?)
Revolves a planar profile about an axis to make a solid of revolution — a full turn by default. Returns a new brep; the input contour is read-only. Holes in the profile are allowed. Profile segments must stay parallel or perpendicular to the axis — inclined or arc segments would revolve into surfaces Snaptrude cannot represent.
Runs on the OpenCascade kernel (the first kernel call loads a wasm of tens of MB — expect a pause of seconds).
Inspect the result via core.geom.query.brep.*, or commit it to the scene with design.create.massFromBrep.
- Parameters:
profile:ContourHandle— The cross-section to revolve (planar contour, holes allowed)axisOrigin:{ x, y, z }— A point on the revolution axis as plain componentsaxisDirection:{ x, y, z }— The axis direction as plain components (non-zero)angleInDegrees:number(optional, default 360) — Revolution angle in degrees (0 < angle ≤ 360)
- Returns:
BrepHandle— The revolved solid as a new brep - Throws:
VALIDATIONifaxisDirectionis zero-length,angleInDegreesis not in (0, 360], the axis passes through the profile interior, or a coordinate is not finite;OPERATION_FAILEDif the kernel cannot revolve the profile or the result contains curved surfaces Snaptrude cannot represent (keep profile segments parallel or perpendicular to the axis)
ts
// A cylinder: revolve a 2m-wide, 3m-tall rectangle about the Y axis at its edge
const rect = await snaptrude.core.geom.create.profileFromLinePoints([
await snaptrude.core.math.vec3.new(0, 0, 0),
await snaptrude.core.math.vec3.new(2, 0, 0),
await snaptrude.core.math.vec3.new(2, 3, 0),
await snaptrude.core.math.vec3.new(0, 3, 0)
]);
const profile = await snaptrude.core.geom.create.contourFromProfile(rect);
const cylinder = await snaptrude.core.geom.create.brepFromRevolution(
profile,
{ x: 0, y: 0, z: 0 },
{ x: 0, y: 1, z: 0 }
);
await snaptrude.design.create.massFromBrep(cylinder, "rotunda");Errors
Failed calls reject with a typed PluginError — see Error Handling. Conditions specific to this namespace:
| Code | Thrown by | When | details |
|---|---|---|---|
VALIDATION | profileRect | width or depth is not positive | width, depth |
VALIDATION | brepFromFaces / brepFromMesh | Bad face set: degenerate/non-planar face, open shell, non-manifold edge, inconsistent winding; bad mesh index (brepFromMesh) | — |
VALIDATION | brepFromExtrusion | Degenerate extrusion (zero amount, zero direction, direction in the contour plane) or the contour cannot form a valid solid | handles |
VALIDATION | brepFromLoft | Coincident contours, hole count mismatch, an arc/straight-edge pairing under edge-count auto-matching, a non-planar side face on an arc-bearing/holed pair (straight-edged hole-free pairs triangulate instead), holes or a non-planar section in a multi-section loft, invalid loft result, or — with compatibility: "strict" — mismatched section edge counts (or hole-pair edge counts; the error names the sections and counts) | handles |
OPERATION_FAILED | brepFromLoft | The lofted segments could not be joined into a valid solid (twists are caught earlier as VALIDATION) | handles |
VALIDATION | brepFromUnion / brepFromSubtraction / brepFromIntersection | The boolean produced an empty solid, or disjoint solids (a brep holds exactly one solid) | handles |
OPERATION_FAILED | brepFromUnion / brepFromSubtraction / brepFromIntersection | The geometry kernel could not compute the boolean, or the result failed brep integrity | handles |
VALIDATION | brepFromFillet / brepFromChamfer | Empty edges, non-positive/non-finite radius or distance, an edge not on the brep, an arc edge (v1 = straight edges only), or two edges sharing a vertex (corner blends) | — |
OPERATION_FAILED | brepFromFillet / brepFromChamfer | The kernel could not build the blend — the radius/distance likely exceeds the adjacent face size; or (fillet) the result contains curved surfaces Snaptrude cannot represent | — |
VALIDATION | brepFromOffset | Non-finite distance, a distance too small to offset anything, or an inward distance that consumes the solid entirely | — |
VALIDATION | brepFromShell | Empty openFaces, a face not on the brep, non-positive/non-finite thickness, or a thickness too large (must be smaller than half the solid's smallest span) | — |
OPERATION_FAILED | brepFromOffset / brepFromShell | The kernel could not build the offset/shell, or the result contains curved surfaces Snaptrude cannot represent | — |
VALIDATION | brepsFromSplit | Zero-length plane normal, or non-finite origin/normal components | — |
OPERATION_FAILED | brepsFromSplit | The kernel could not split the solid | handles |
VALIDATION | brepFromSweep | Profile has holes, path shorter than 2 points, coincident consecutive points, non-finite coordinates, the profile plane contains the first path segment's direction, a bad closed path (non-planar, doubles back, ❤️ corners, arc profile, oversized profile), a bad taper (varying scale on a closed/bent path, arc profile, out-of-range or mis-sized scales), or a { bevel } that does not fit a path leg or whose corner nearly doubles back (the error names the corner) | handles |
OPERATION_FAILED | brepFromSweep | The kernel could not sweep the profile into a valid solid, or a transition patch is a curved surface Snaptrude cannot represent | handles |
VALIDATION | brepFromRevolution | Zero-length axis, angleInDegrees outside (0, 360], the axis passing through the profile interior, or non-finite components | handles |
OPERATION_FAILED | brepFromRevolution | The kernel could not revolve the profile, or the result contains curved surfaces (keep profile segments parallel or perpendicular to the axis) | handles |
HANDLE_INVALID | all methods | A point, curve, profile, contour, face, edge, or brep handle cannot be resolved | — |