API Reference@vgpu/render

@vgpu/render/edit — Mesh Editing

CPU-side triangle mesh editing built on the render package's half-edge kernel. Use this entrypoint to convert render meshes into `EditableMesh`, select topology, run edit operators, and bake back to render meshes after the edit pipeline.

Index

All imports in this file use the public edit entrypoint:

import { EditableMesh } from "@vgpu/render/edit";

EditableMesh

Factory object for creating and baking EditableMeshValue instances. Use fromArrays when you already have typed geometry arrays; use EditableMesh.toRenderMesh or mesh.toRenderMesh only after the final edit step.

Import

import { EditableMesh } from "@vgpu/render/edit";

Signature

import type { Device } from "@vgpu/core";
import type { EditableMeshValue } from "@vgpu/render/edit";

declare const EditableMesh: {
  fromArrays(opts: {
    readonly positions: Float32Array;
    readonly normals?: Float32Array;
    readonly uvs?: Float32Array;
    readonly colors?: Float32Array;
    readonly indices?: Uint16Array | Uint32Array;
    readonly sharpEdges?: Uint8Array;
    readonly useSmooth?: Uint8Array;
    readonly creaseAngle?: number;
  }): EditableMeshValue;
  toRenderMesh(em: EditableMeshValue, opts: { readonly device: Device }): unknown;
};

Parameters

ParamTypeRequiredDefaultNotes
opts.positionsFloat32ArrayXYZ triples. Vertices with identical XYZ are welded by position during kernel build.
opts.indicesUint16Array | Uint32Arraysequential 0..positions.length / 3 - 1Triangle indices; length must represent triangles.
opts.normalsFloat32ArrayomittedPreserved only as hasNormals; edit operators recompute face topology.
opts.uvsFloat32ArrayomittedPreserved only as hasUVs; operators may drop seams.
opts.colorsFloat32ArrayomittedPreserved only as hasVertexColors.
opts.sharpEdgesUint8Arrayauto from creaseAnglePer-edge sharp mask in kernel edge order; if present it overrides auto-sharp detection.
opts.useSmoothUint8Arrayall faces smooth (1)Per-face smoothing flags.
opts.creaseAnglenumberMath.PI / 6Radians used to auto-mark sharp edges when sharpEdges is omitted.
emEditableMeshValueMesh to bake for static EditableMesh.toRenderMesh.
opts.deviceDeviceDevice used to create the render mesh buffers.

Returns: EditableMeshValue from fromArrays; render Mesh from toRenderMesh. Throws: — no MeshEditError is thrown directly. Invalid or mismatched raw arrays can still produce invalid geometry at JavaScript/WebGPU boundaries.

Examples

import { EditableMesh } from "@vgpu/render/edit";

const editableMeshExample = EditableMesh.fromArrays({
  positions: new Float32Array([0, 0, 0, 1, 0, 0, 0, 1, 0]),
  indices: new Uint32Array([0, 1, 2]),
});

const editableFaceCount: number = editableMeshExample.faceCount;

Notes

  • The editable kernel is triangle-only; higher-order fills are represented as deterministic triangles.
  • Bake once at the end of a pipeline instead of after every operator.
  • See also: toEditable, toEditableWithDiagnostics, EditableMeshValue, recomputeNormals.

toEditable

Converts a render Mesh into an editable half-edge mesh and discards diagnostics. Use when warnings are not important; otherwise call toEditableWithDiagnostics.

Import

import { toEditable } from "@vgpu/render/edit";

Signature

import { toEditable } from "@vgpu/render/edit";
import type { EditableMeshValue } from "@vgpu/render/edit";

type EditableInputMesh = Parameters<typeof toEditable>[0];
declare function toEditableSignature(mesh: EditableInputMesh, opts?: { readonly creaseAngle?: number }): EditableMeshValue;

Parameters

ParamTypeRequiredDefaultNotes
meshMeshRender mesh-like object with attributes and bounds. Source arrays are used when available; otherwise a bbox box fallback is built.
opts.creaseAnglenumberMath.PI / 6 through EditableMesh.fromArraysRadians for auto-sharp edge detection.

Returns: EditableMeshValue — editable mesh ready for selections/operators. Throws: — no MeshEditError is thrown directly.

Examples

import { toEditable } from "@vgpu/render/edit";

const renderMeshForEdit = {
  vertexBuffer: {},
  vertexCount: 3,
  attributes: { stride: 12, position: { offset: 0, format: "float32x3" as const } },
  bbox: { min: new Float32Array([0, 0, 0]), max: new Float32Array([1, 1, 1]) },
} as unknown as Parameters<typeof toEditable>[0];
const toEditableMesh = toEditable(renderMeshForEdit, { creaseAngle: Math.PI / 4 });

Notes

  • Tangent-stripping warnings are hidden by this convenience function.
  • See also: toEditableWithDiagnostics, EditableMesh, MeshEditWarning.

toEditableWithDiagnostics

Converts a render Mesh and returns warnings such as stripped tangents. Use this at import/conversion boundaries so LLM-generated pipelines do not silently lose render-layer data.

Import

import { toEditableWithDiagnostics } from "@vgpu/render/edit";

Signature

import { toEditableWithDiagnostics } from "@vgpu/render/edit";
import type { EditableMeshValue, MeshEditWarning } from "@vgpu/render/edit";

type DiagnosticInputMesh = Parameters<typeof toEditableWithDiagnostics>[0];
declare function toEditableWithDiagnosticsSignature(
  mesh: DiagnosticInputMesh,
  opts?: { readonly creaseAngle?: number },
): { readonly mesh: EditableMeshValue; readonly warnings: readonly MeshEditWarning[] };

Parameters

ParamTypeRequiredDefaultNotes
meshMeshRender mesh-like object. If edit-source arrays are absent, bbox fallback arrays are generated.
opts.creaseAnglenumberMath.PI / 6 through EditableMesh.fromArraysRadians for auto-sharp edge detection.

Returns: { mesh, warnings }warnings is an array of MeshEditWarning objects. Throws: — no MeshEditError is thrown directly.

Examples

import { toEditableWithDiagnostics } from "@vgpu/render/edit";

const renderMeshWithDiagnostics = {
  vertexBuffer: {},
  vertexCount: 3,
  attributes: { stride: 12, position: { offset: 0, format: "float32x3" as const } },
  bbox: { min: new Float32Array([0, 0, 0]), max: new Float32Array([1, 1, 1]) },
} as unknown as Parameters<typeof toEditableWithDiagnostics>[0];
const diagnostics = toEditableWithDiagnostics(renderMeshWithDiagnostics);
const diagnosticsWarnings = diagnostics.warnings.map((warning) => warning.code);

Notes

  • Current explicit warning source is TANGENTS_STRIPPED when mesh.attributes.tangent exists.
  • See also: toEditable, MeshEditWarning, EditableMesh.

EditableMeshValue

Runtime shape of an editable mesh. It exposes counts, bounds, typed element sets, material/topology flags, hard-edge selection, an opaque kernel handle, and toRenderMesh.

Import

import type { EditableMeshValue } from "@vgpu/render/edit";

Signature

import type { Device } from "@vgpu/core";
import type { ElementSelection, ElementSet, KernelHandle } from "@vgpu/render/edit";

type Vec3 = Float32Array;

declare interface EditableMeshValue {
  readonly vertexCount: number;
  readonly edgeCount: number;
  readonly faceCount: number;
  readonly bounds: { readonly min: Vec3; readonly max: Vec3 };
  readonly vertices: ElementSet<"vertex">;
  readonly edges: ElementSet<"edge">;
  readonly faces: ElementSet<"face">;
  readonly isManifold: boolean;
  readonly hasUVs: boolean;
  readonly hasNormals: boolean;
  readonly hasVertexColors: boolean;
  readonly hardEdges: ElementSelection;
  readonly gpu: { readonly halfEdgeKernel: KernelHandle };
  toRenderMesh(opts: { readonly device: Device }): unknown;
}

Parameters

FieldTypeRequiredDefaultNotes
vertexCount / edgeCount / faceCountnumberCounts in the current immutable editable mesh value.
bounds.min / bounds.maxVec3Axis-aligned bounds computed from positions.
vertices / edges / facesElementSetSelection factories and traversal helpers for each domain.
isManifoldbooleantrue only when every edge has two incident faces in the current kernel.
hasUVs / hasNormals / hasVertexColorsbooleanFlags copied from input arrays; most operators rebuild topology arrays.
hardEdgesElementSelectionEdge selection where the kernel isSharp mask is nonzero.
gpu.halfEdgeKernelKernelHandleOpaque handle; do not construct or mutate directly.
toRenderMeshfunctionBakes the editable mesh with { device }.

Returns: N/A — this is an interface/type export. Throws: N/A — toRenderMesh can fail at render/WebGPU boundaries if passed an invalid Device.

Examples

import { EditableMesh, type EditableMeshValue } from "@vgpu/render/edit";

const editableValueExample: EditableMeshValue = EditableMesh.fromArrays({
  positions: new Float32Array([0, 0, 0, 1, 0, 0, 0, 1, 0]),
});
const editableBoundsMin = editableValueExample.bounds.min;

Notes

  • Operators return new EditableMeshValue objects; do not assume selections from an old mesh are valid on a new mesh unless the result explicitly returns them.
  • See also: ElementSet, ElementSelection, KernelHandle, EditableMesh.

ElementDomain

String union naming the selectable topology domains.

Import

import type { ElementDomain } from "@vgpu/render/edit";

Signature

export type ElementDomain = "vertex" | "edge" | "face" | "loop";

Parameters

VariantTypeRequiredDefaultNotes
"vertex"ElementDomainVertex selections and VertexView.
"edge"ElementDomainEdge selections, loops, rings, bridge/fill boundaries.
"face"ElementDomainFace selections for extrusion/inset/dissolve.
"loop"ElementDomainDeclared domain variant; public element sets currently operate on vertex/edge/face.

Returns: N/A — type alias. Throws: N/A.

Examples

import type { ElementDomain } from "@vgpu/render/edit";

const selectedDomain: ElementDomain = "edge";

Notes

  • Operator validation is strict: passing a selection with the wrong domain throws WRONG_DOMAIN.
  • See also: ElementSelection, ElementSet.

ElementSelection

Immutable selection object passed to operators. Use ElementSet helpers (mesh.faces.byIndex, mesh.edges.loop, etc.) instead of hand-building selections unless you need an ordered boundary loop.

Import

import type { ElementSelection } from "@vgpu/render/edit";

Signature

import type { ElementDomain } from "@vgpu/render/edit";

declare interface ElementSelection {
  readonly domain: ElementDomain;
  readonly indices: ReadonlyArray<number>;
  readonly count: number;
  readonly ordered?: boolean;
}

Parameters

FieldTypeRequiredDefaultNotes
domainElementDomainMust match the operator target domain.
indicesReadonlyArrayElement indices in the mesh that owns the selection.
countnumberUsually indices.length; operators check count === 0 for empty selections.
orderedbooleanomitted / falseRequired as true for loop-boundary operators (bridge, fillHole, gridFill).

Returns: N/A — interface. Throws: N/A.

Examples

import { EditableMesh, type ElementSelection } from "@vgpu/render/edit";

const selectionMesh = EditableMesh.fromArrays({
  positions: new Float32Array([0, 0, 0, 1, 0, 0, 0, 1, 0]),
});
const oneFaceSelection: ElementSelection = selectionMesh.faces.byIndex([0]);

Notes

  • Selections are mesh-local. Do not reuse a selection from the input mesh against an operator result unless the operator returned that selection for the new mesh.
  • See also: ElementSet, ScoredSelection, MeshEditError.

ElementSet

Domain-specific helper collection available as mesh.vertices, mesh.edges, and mesh.faces. Use it to create validated selections and compute simple adjacency expansions.

Import

import type { ElementSet } from "@vgpu/render/edit";

Signature

import type { ElementDomain, ElementSelection, EdgeView, FaceView, ScoredSelection, VertexView } from "@vgpu/render/edit";

type ElementView<D extends ElementDomain> = D extends "vertex" ? VertexView : D extends "edge" ? EdgeView : D extends "face" ? FaceView : never;

declare interface ElementSet<D extends ElementDomain> {
  readonly domain: D;
  readonly count: number;
  where(pred: (e: ElementView<D>) => boolean): ElementSelection;
  scoreBy(score: (e: ElementView<D>) => number): ScoredSelection;
  byIndex(indices: readonly number[]): ElementSelection;
  all(): ElementSelection;
  none(): ElementSelection;
  loop(seedEdge: number): D extends "edge" ? ElementSelection : never;
  ring(seedEdge: number): D extends "edge" ? ElementSelection : never;
  grow(sel: ElementSelection, layers?: number): ElementSelection;
  shrink(sel: ElementSelection, layers?: number): ElementSelection;
  boundaryOf(sel: ElementSelection): ElementSelection;
  connectedComponentOf(seed: number): ElementSelection;
}

Parameters

Method/FieldTypeRequiredDefaultNotes
domainD"vertex", "edge", or "face" for the owning set.
countnumberNumber of elements in the owning domain.
where.predfunctionCalled with VertexView, EdgeView, or FaceView; returned indices are sorted/deduped.
scoreBy.scorefunctionProduces a ScoredSelection sorted by score descending.
byIndex.indicesreadonly number[]Out-of-range indices are filtered out.
loop.seedEdge / ring.seedEdgenumberFor edge sets only; current implementation returns connected edge walk with ordered: true.
grow.layers / shrink.layersnumber1Number of adjacency layers to expand or contract.
boundaryOf.selElementSelectionReturns an edge selection around face/vertex selections; edge input returns itself.
connectedComponentOf.seednumberFlood-fills adjacent elements in the same domain.

Returns: ElementSelection or ScoredSelection depending on the method. Throws: — no MeshEditError is thrown directly by the public methods.

Examples

import { EditableMesh } from "@vgpu/render/edit";

const elementSetMesh = EditableMesh.fromArrays({
  positions: new Float32Array([0, 0, 0, 1, 0, 0, 0, 1, 0]),
});
const longEdges = elementSetMesh.edges.where((edge) => edge.length > 0.5);

Notes

  • loop and ring are typed for edge sets; do not call them on vertices or faces.
  • See also: ElementSelection, ScoredSelection, VertexView, EdgeView, FaceView.

ScoredSelection

Ranked selection helper returned by ElementSet.scoreBy. Use it to pick strongest or weakest candidates without manually sorting indices.

Import

import type { ScoredSelection } from "@vgpu/render/edit";

Signature

import type { ElementDomain, ElementSelection } from "@vgpu/render/edit";

declare interface ScoredSelection {
  readonly domain: ElementDomain;
  readonly entries: ReadonlyArray<{ readonly index: number; readonly score: number }>;
  top(): ElementSelection;
  topN(n: number): ElementSelection;
  threshold(min: number): ElementSelection;
  bottom(): ElementSelection;
  bottomN(n: number): ElementSelection;
}

Parameters

Method/FieldTypeRequiredDefaultNotes
domainElementDomainDomain of all ranked entries.
entriesReadonlyArraySorted by score descending, then index ascending.
top / bottomfunctionEquivalent to topN(1) / bottomN(1).
topN.n / bottomN.nnumberNegative values clamp to 0.
threshold.minnumberKeeps entries with score >= min.

Returns: ElementSelection from ranking methods. Throws: — no MeshEditError is thrown directly.

Examples

import { EditableMesh, type ScoredSelection } from "@vgpu/render/edit";

const scoredMesh = EditableMesh.fromArrays({
  positions: new Float32Array([0, 0, 0, 2, 0, 0, 0, 1, 0]),
});
const scoredEdges: ScoredSelection = scoredMesh.edges.scoreBy((edge) => edge.length);
const longestEdge = scoredEdges.top();

Notes

  • bottomN re-sorts ascending at call time; entries remains descending.
  • See also: ElementSet, ElementSelection.

VertexView

Read-only per-vertex data passed to vertex where/scoreBy callbacks.

Import

import type { VertexView } from "@vgpu/render/edit";

Signature

type Vec3 = Float32Array;

declare interface VertexView {
  readonly index: number;
  readonly position: Vec3;
  readonly normal: Vec3;
  readonly valence: number;
  readonly isBoundary: boolean;
  readonly isManifold: boolean;
}

Parameters

FieldTypeRequiredDefaultNotes
indexnumberVertex index in the current mesh.
positionVec3XYZ position.
normalVec3Kernel-computed vertex normal.
valencenumberNumber of incident edges.
isBoundarybooleanTrue if any incident edge is boundary.
isManifoldbooleanTrue when local incident topology is manifold.

Returns: N/A — interface. Throws: N/A.

Examples

import { EditableMesh, type VertexView } from "@vgpu/render/edit";

const vertexViewMesh = EditableMesh.fromArrays({
  positions: new Float32Array([0, 0, 0, 1, 0, 0, 0, 1, 0]),
});
const boundaryVertices = vertexViewMesh.vertices.where((vertex: VertexView) => vertex.isBoundary);

Notes

  • Views are snapshots produced by callbacks; do not store them as stable handles across edits.
  • See also: ElementSet, EdgeView, FaceView.

EdgeView

Read-only per-edge data passed to edge where/scoreBy callbacks.

Import

import type { EdgeView } from "@vgpu/render/edit";

Signature

type Vec3 = Float32Array;

declare interface EdgeView {
  readonly index: number;
  readonly midpoint: Vec3;
  readonly length: number;
  readonly direction: Vec3;
  readonly vertexA: number;
  readonly vertexB: number;
  readonly faceA: number | null;
  readonly faceB: number | null;
  readonly isBoundary: boolean;
  readonly isManifold: boolean;
  readonly isSharp: boolean;
}

Parameters

FieldTypeRequiredDefaultNotes
indexnumberEdge index in current mesh.
midpoint / directionVec3Derived from endpoints.
lengthnumberEuclidean length.
vertexA / vertexBnumberEndpoint vertex indices.
faceA / faceBnumber | nullIncident faces; faceB === null indicates boundary.
isBoundarybooleanTrue for one-sided edges.
isManifoldbooleanTrue when the edge has manifold incidence.
isSharpbooleanTrue when the kernel sharp mask is set.

Returns: N/A — interface. Throws: N/A.

Examples

import { EditableMesh, type EdgeView } from "@vgpu/render/edit";

const edgeViewMesh = EditableMesh.fromArrays({
  positions: new Float32Array([0, 0, 0, 1, 0, 0, 0, 1, 0]),
});
const sharpEdges = edgeViewMesh.edges.where((edge: EdgeView) => edge.isSharp);

Notes

  • Use mesh.hardEdges when you just need the current sharp-edge selection.
  • See also: VertexView, FaceView, bevel, subdivideEdges.

FaceView

Read-only per-face data passed to face where/scoreBy callbacks.

Import

import type { FaceView } from "@vgpu/render/edit";

Signature

type Vec3 = Float32Array;

declare interface FaceView {
  readonly index: number;
  readonly center: Vec3;
  readonly normal: Vec3;
  readonly area: number;
  readonly vertexCount: number;
  readonly vertexIndices: ReadonlyArray<number>;
  readonly edgeIndices: ReadonlyArray<number>;
  readonly useSmooth: boolean;
}

Parameters

FieldTypeRequiredDefaultNotes
indexnumberFace index in current mesh.
center / normalVec3Derived from triangle vertices and face normal.
areanumberTriangle area.
vertexCountnumberAlways 3 for the triangle-only editable kernel.
vertexIndices / edgeIndicesReadonlyArrayTriangle vertex/edge indices.
useSmoothbooleanPer-face smoothing flag.

Returns: N/A — interface. Throws: N/A.

Examples

import { EditableMesh, type FaceView } from "@vgpu/render/edit";

const faceViewMesh = EditableMesh.fromArrays({
  positions: new Float32Array([0, 0, 0, 1, 0, 0, 0, 1, 0]),
});
const upwardFaces = faceViewMesh.faces.where((face: FaceView) => face.normal[2] > 0);

Notes

  • vertexCount is still exposed so future kernels can remain source-compatible with code that checks it.
  • See also: ElementSet, extrude, inset, dissolveFaces.

KernelHandle

Opaque branded handle to the internal half-edge kernel. It exists so editable values can carry kernel data without exposing mutation APIs.

Import

import type { KernelHandle } from "@vgpu/render/edit";

Signature

declare const kernelBrand: unique symbol;
export type KernelHandle = { readonly [kernelBrand]: never };

Parameters

FieldTypeRequiredDefaultNotes
branded propertyunique symbolCompile-time brand only; not constructible through public API.

Returns: N/A — type alias. Throws: N/A.

Examples

import { EditableMesh, type KernelHandle } from "@vgpu/render/edit";

const kernelHandleMesh = EditableMesh.fromArrays({
  positions: new Float32Array([0, 0, 0, 1, 0, 0, 0, 1, 0]),
});
const kernelHandle: KernelHandle = kernelHandleMesh.gpu.halfEdgeKernel;

Notes

  • Do not serialize or construct a KernelHandle; use public operators instead.
  • See also: EditableMeshValue, EditableMesh.

extrude

Extrudes selected faces along their face normals or an explicit direction. Use it for raised panels, shells, and block-out modeling.

Import

import { extrude } from "@vgpu/render/edit";

Signature

import type { EditableMeshValue, ElementSelection, MeshEditWarning } from "@vgpu/render/edit";

declare interface ExtrudeOptions {
  readonly distance: number;
  readonly inset?: number;
  readonly direction?: readonly [number, number, number];
  readonly mode?: "region" | "individual";
}

declare interface ExtrudeResult {
  readonly mesh: EditableMeshValue;
  readonly sideFaces: ElementSelection;
  readonly capFaces: ElementSelection;
  readonly boundaryEdges: ElementSelection;
  readonly warnings?: readonly MeshEditWarning[];
}

declare function extrude(em: EditableMeshValue, faces: ElementSelection, opts: ExtrudeOptions): ExtrudeResult;

Parameters

ParamTypeRequiredDefaultNotes
emEditableMeshValueSource mesh.
facesElementSelectionMust be a non-empty face selection from em.
opts.distancenumberOffset distance along selected face normal or normalized direction.
opts.insetnumber0Fraction toward each face center before lifting. No clamp is applied.
opts.direction[number, number, number]selected face normalNormalized internally; zero vector behaves as length 1 denominator.
opts.mode"region" | "individual"accepted but not used in v1Current implementation extrudes each selected triangle independently.

Returns: ExtrudeResult — edited mesh plus side, cap, and boundary-edge selections on the result mesh. Throws: MeshEditError WRONG_DOMAIN if faces.domain !== "face"; EMPTY_SELECTION if faces.count === 0.

Examples

import { EditableMesh, extrude } from "@vgpu/render/edit";

const extrudeMesh = EditableMesh.fromArrays({
  positions: new Float32Array([0, 0, 0, 1, 0, 0, 0, 1, 0]),
});
const extruded = extrude(extrudeMesh, extrudeMesh.faces.byIndex([0]), { distance: 0.2, inset: 0.1 });

Notes

  • Source faces are removed; new side and cap faces are returned for highlighting/chaining.
  • See also: inset, bevel, recomputeNormals.

bevel

Bevels selected edges by shrinking incident faces and inserting strip faces. Use it to soften hard edges; v1 supports a single segment.

Import

import { bevel } from "@vgpu/render/edit";

Signature

import type { EditableMeshValue, ElementSelection, MeshEditWarning } from "@vgpu/render/edit";

declare interface BevelOptions {
  readonly offset: number;
  readonly segments?: number;
  readonly profile?: number;
  readonly affect?: "edges" | "vertices";
  readonly markSharp?: boolean;
}

declare interface BevelResult {
  readonly mesh: EditableMeshValue;
  readonly newFaces: ElementSelection;
  readonly originalFaces: ElementSelection;
  readonly profileLoops: readonly ElementSelection[];
  readonly warnings?: readonly MeshEditWarning[];
}

declare function bevel(em: EditableMeshValue, edges: ElementSelection, opts: BevelOptions): BevelResult;

Parameters

ParamTypeRequiredDefaultNotes
emEditableMeshValueSource mesh.
edgesElementSelectionMust be a non-empty edge selection.
opts.offsetnumberFraction toward each incident face center; clamped to [0, 0.49].
opts.segmentsnumber1Any value other than 1 emits BEVEL_SEGMENTS_CLAMPED; geometry remains one segment.
opts.profilenumberaccepted but not used in v1Present in type for future bevel profiles.
opts.affect"edges" | "vertices"accepted but not used in v1Current implementation bevels selected edges/incident faces.
opts.markSharpbooleantrueMarks selected original/profile edges sharp when true.

Returns: BevelResult — edited mesh, strip faces, shrunken original faces, and profile loop edge selections. Throws: MeshEditError WRONG_DOMAIN if edges.domain !== "edge"; EMPTY_SELECTION if edges.count === 0.

Examples

import { EditableMesh, bevel } from "@vgpu/render/edit";

const bevelMesh = EditableMesh.fromArrays({
  positions: new Float32Array([0, 0, 0, 1, 0, 0, 0, 1, 0]),
});
const beveled = bevel(bevelMesh, bevelMesh.edges.byIndex([0]), { offset: 0.05 });

Notes

  • Boundary edges are processed on their one incident face and reported with NON_MANIFOLD_EDGE_SKIPPED warnings.
  • See also: extrude, inset, EdgeView.

inset

Insets selected faces by adding an inner triangle and boundary rim faces. Use it before extrude for panel-like forms.

Import

import { inset } from "@vgpu/render/edit";

Signature

import type { EditableMeshValue, ElementSelection, MeshEditWarning } from "@vgpu/render/edit";

declare interface InsetOptions {
  readonly thickness: number;
  readonly depth?: number;
  readonly individual?: boolean;
}

declare interface InsetResult {
  readonly mesh: EditableMeshValue;
  readonly insetFaces: ElementSelection;
  readonly boundaryFaces: ElementSelection;
  readonly rimEdges: ElementSelection;
  readonly warnings?: readonly MeshEditWarning[];
}

declare function inset(em: EditableMeshValue, faces: ElementSelection, opts: InsetOptions): InsetResult;

Parameters

ParamTypeRequiredDefaultNotes
emEditableMeshValueSource mesh.
facesElementSelectionMust be a non-empty face selection.
opts.thicknessnumberFraction toward face center; clamped to [0, 0.49]. Clamp emits INSET_OVERLAP_CLAMPED.
opts.depthnumber0Offset along each face normal after insetting.
opts.individualbooleanaccepted but not used in v1Current implementation processes selected triangles individually.

Returns: InsetResult — edited mesh plus inner faces, rim/boundary faces, and rim edges on the result mesh. Throws: MeshEditError WRONG_DOMAIN if faces.domain !== "face"; EMPTY_SELECTION if faces.count === 0.

Examples

import { EditableMesh, inset } from "@vgpu/render/edit";

const insetMesh = EditableMesh.fromArrays({
  positions: new Float32Array([0, 0, 0, 1, 0, 0, 0, 1, 0]),
});
const insetResult = inset(insetMesh, insetMesh.faces.all(), { thickness: 0.2, depth: 0.05 });

Notes

  • Follow with extrude(result.mesh, result.insetFaces, ...) for raised or recessed panels.
  • See also: extrude, bevel, FaceView.

subdivideEdges

Splits selected edges and retriangulates incident triangles. Use it to add local resolution before detailed edits.

Import

import { subdivideEdges } from "@vgpu/render/edit";

Signature

import type { EditableMeshValue, ElementSelection } from "@vgpu/render/edit";

declare interface SubdivideEdgesOptions { readonly cuts?: number }
declare interface SubdivideEdgesResult {
  readonly mesh: EditableMeshValue;
  readonly newVertices: ElementSelection;
  readonly newEdges: ElementSelection;
}

declare function subdivideEdges(em: EditableMeshValue, edges: ElementSelection, opts?: SubdivideEdgesOptions): SubdivideEdgesResult;

Parameters

ParamTypeRequiredDefaultNotes
emEditableMeshValueSource mesh.
edgesElementSelectionMust be a non-empty edge selection.
optsSubdivideEdgesOptions{}Options object may be omitted.
opts.cutsnumber1Floored and clamped to minimum 1; inserts this many points per selected edge.

Returns: SubdivideEdgesResult — edited mesh with selections for inserted vertices and child edges. Throws: MeshEditError WRONG_DOMAIN if edges.domain !== "edge"; EMPTY_SELECTION if edges.count === 0.

Examples

import { EditableMesh, subdivideEdges } from "@vgpu/render/edit";

const subdivideEdgesMesh = EditableMesh.fromArrays({
  positions: new Float32Array([0, 0, 0, 1, 0, 0, 0, 1, 0]),
});
const edgeSubdivision = subdivideEdges(subdivideEdgesMesh, subdivideEdgesMesh.edges.byIndex([0]), { cuts: 2 });

Notes

  • Sharp selected edges propagate sharpness to child edges.
  • See also: subdivideFaces, loopCut, bevel.

subdivideFaces

Subdivides selected triangles into four triangles per cut iteration using edge midpoints.

Import

import { subdivideFaces } from "@vgpu/render/edit";

Signature

import type { EditableMeshValue, ElementSelection } from "@vgpu/render/edit";

declare interface SubdivideFacesOptions { readonly cuts?: number }
declare interface SubdivideFacesResult {
  readonly mesh: EditableMeshValue;
  readonly newFaces: ElementSelection;
  readonly newEdges: ElementSelection;
}

declare function subdivideFaces(em: EditableMeshValue, faces: ElementSelection, opts?: SubdivideFacesOptions): SubdivideFacesResult;

Parameters

ParamTypeRequiredDefaultNotes
emEditableMeshValueSource mesh.
facesElementSelectionMust be a non-empty face selection.
optsSubdivideFacesOptions{}Options object may be omitted.
opts.cutsnumber1Floored and clamped to minimum 1; repeated cut iterations apply to newly-created faces.

Returns: SubdivideFacesResult — edited mesh with selections for descendant faces and new edges. Throws: MeshEditError WRONG_DOMAIN if faces.domain !== "face"; EMPTY_SELECTION if faces.count === 0.

Examples

import { EditableMesh, subdivideFaces } from "@vgpu/render/edit";

const subdivideFacesMesh = EditableMesh.fromArrays({
  positions: new Float32Array([0, 0, 0, 1, 0, 0, 0, 1, 0]),
});
const faceSubdivision = subdivideFaces(subdivideFacesMesh, subdivideFacesMesh.faces.all(), { cuts: 1 });

Notes

  • Original sharp face edges are split into sharp child edges.
  • See also: subdivideEdges, loopCut, FaceView.

loopCut

Attempts to cut an edge loop/ring through coplanar triangle pairs. Falls back to cutting only the seed edge when continuation is ambiguous.

Import

import { loopCut } from "@vgpu/render/edit";

Signature

import type { EditableMeshValue, ElementSelection, MeshEditWarning } from "@vgpu/render/edit";

declare interface LoopCutOptions {
  readonly cuts?: number;
  readonly slide?: number;
  readonly markSharp?: boolean;
}

declare interface LoopCutResult {
  readonly mesh: EditableMeshValue;
  readonly insertedLoop: ElementSelection;
  readonly warnings?: readonly MeshEditWarning[];
}

declare function loopCut(em: EditableMeshValue, seedEdge: number, opts?: LoopCutOptions): LoopCutResult;

Parameters

ParamTypeRequiredDefaultNotes
emEditableMeshValueSource mesh.
seedEdgenumberEdge index; must be 0 <= seedEdge < em.edgeCount.
optsLoopCutOptions{}Options object may be omitted.
opts.cutsnumber1 in fallback onlyUsed only when ambiguous continuation falls back to subdivideEdges.
opts.slidenumber0Maps to split factor 0.5 + slide * 0.5, clamped to [0.001, 0.999].
opts.markSharpbooleanfalseMarks inserted loop edges sharp only in successful ring cuts.

Returns: LoopCutResult — edited mesh plus ordered inserted-loop edge selection. Throws: MeshEditError EMPTY_SELECTION when seedEdge is out of range.

Examples

import { EditableMesh, loopCut } from "@vgpu/render/edit";

const loopCutMesh = EditableMesh.fromArrays({
  positions: new Float32Array([0, 0, 0, 1, 0, 0, 0, 1, 0]),
});
const loopCutResult = loopCut(loopCutMesh, 0, { slide: 0.25 });

Notes

  • Ambiguous topology returns LOOP_CUT_AMBIGUOUS_CONTINUATION and cuts only the seed edge.
  • See also: subdivideEdges, subdivideFaces, EdgeView.

bridge

Creates faces between two ordered edge loops in one selection. Use for connecting holes or separated boundary rings.

Import

import { bridge } from "@vgpu/render/edit";

Signature

import type { EditableMeshValue, ElementSelection, MeshEditWarning } from "@vgpu/render/edit";

declare interface BridgeOptions {
  readonly twist?: number;
  readonly mode?: "faces" | "merge";
}

declare interface BridgeResult {
  readonly mesh: EditableMeshValue;
  readonly bridgeFaces: ElementSelection;
  readonly chosenTwist: number;
  readonly warnings?: readonly MeshEditWarning[];
}

declare function bridge(em: EditableMeshValue, sel: ElementSelection, opts?: BridgeOptions): BridgeResult;

Parameters

ParamTypeRequiredDefaultNotes
emEditableMeshValueSource mesh.
selElementSelectionMust be a non-empty ordered edge selection containing two loops.
optsBridgeOptions{}Options object may be omitted.
opts.twistnumberauto by shortest squared endpoint distanceShift applied to second loop correspondence; returned as positive modulo loop length.
opts.mode"faces" | "merge""faces""merge" throws UNSUPPORTED_INPUT in the triangle-only kernel.

Returns: BridgeResult — edited mesh, bridge face selection, chosen twist, optional length-mismatch warnings. Throws: MeshEditError WRONG_DOMAIN, EMPTY_SELECTION, or NOT_ORDERED from loop validation; AMBIGUOUS_TOPOLOGY when two loops cannot be split; UNSUPPORTED_INPUT for mode: "merge"; DEGENERATE_RESULT can propagate from invalid loop vertices.

Examples

import { EditableMesh, bridge, type ElementSelection } from "@vgpu/render/edit";

const bridgeMesh = EditableMesh.fromArrays({
  positions: new Float32Array([0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1]),
  indices: new Uint32Array([0, 1, 2, 3, 4, 5]),
});
const twoTriangleLoops: ElementSelection = { domain: "edge", indices: [0, 1, 2, 3, 4, 5], count: 6, ordered: true };
const bridged = bridge(bridgeMesh, twoTriangleLoops, { twist: 0 });

Notes

  • Different loop lengths are allowed; modulo correspondence is used and BRIDGE_LOOP_LENGTH_MISMATCH is emitted.
  • See also: fillHole, gridFill, ElementSelection.

fillHole

Fills an ordered boundary loop with a triangle fan.

Import

import { fillHole } from "@vgpu/render/edit";

Signature

import type { EditableMeshValue, ElementSelection, MeshEditWarning } from "@vgpu/render/edit";

declare interface FillHoleOptions { readonly method?: "triangulate" | "ngon" | "beautify" }
declare interface FillHoleResult {
  readonly mesh: EditableMeshValue;
  readonly newFaces: ElementSelection;
  readonly warnings?: readonly MeshEditWarning[];
}

declare function fillHole(em: EditableMeshValue, boundary: ElementSelection, opts?: FillHoleOptions): FillHoleResult;

Parameters

ParamTypeRequiredDefaultNotes
emEditableMeshValueSource mesh.
boundaryElementSelectionMust be a non-empty ordered edge loop.
optsFillHoleOptions{}Options object may be omitted.
opts.method"triangulate" | "ngon" | "beautify""triangulate"Non-triangulate methods still emit a triangle fan and warn FILL_HOLE_TRIANGULATED.

Returns: FillHoleResult — edited mesh and newly-created faces. Throws: MeshEditError WRONG_DOMAIN, EMPTY_SELECTION, or NOT_ORDERED from loop validation; AMBIGUOUS_TOPOLOGY/DEGENERATE_RESULT can propagate from invalid loop vertices.

Examples

import { EditableMesh, fillHole, type ElementSelection } from "@vgpu/render/edit";

const fillHoleMesh = EditableMesh.fromArrays({
  positions: new Float32Array([0, 0, 0, 1, 0, 0, 0, 1, 0]),
});
const triangleBoundary: ElementSelection = { domain: "edge", indices: [0, 1, 2], count: 3, ordered: true };
const filledHole = fillHole(fillHoleMesh, triangleBoundary);

Notes

  • Non-planar loops warn FILL_NON_PLANAR_BOUNDARY and are still triangulated.
  • See also: gridFill, bridge, ElementSet.boundaryOf.

gridFill

Deterministically represents a grid fill as triangles around the boundary center. Use it when callers request grid-fill semantics but the triangle-only kernel is acceptable.

Import

import { gridFill } from "@vgpu/render/edit";

Signature

import type { EditableMeshValue, ElementSelection, MeshEditWarning } from "@vgpu/render/edit";

declare interface GridFillOptions { readonly spanMode?: "auto" | number }
declare interface GridFillResult {
  readonly mesh: EditableMeshValue;
  readonly newFaces: ElementSelection;
  readonly warnings?: readonly MeshEditWarning[];
}

declare function gridFill(em: EditableMeshValue, boundary: ElementSelection, opts?: GridFillOptions): GridFillResult;

Parameters

ParamTypeRequiredDefaultNotes
emEditableMeshValueSource mesh.
boundaryElementSelectionMust be a non-empty ordered edge loop.
optsGridFillOptions{}Options object may be omitted.
opts.spanMode"auto" | number"auto" for warning textNumeric values < 1 throw DEGENERATE_RESULT; all modes triangulate.

Returns: GridFillResult — edited mesh, new fan faces, and warnings. Throws: MeshEditError WRONG_DOMAIN, EMPTY_SELECTION, or NOT_ORDERED from loop validation; DEGENERATE_RESULT for numeric spanMode < 1; AMBIGUOUS_TOPOLOGY can propagate from invalid loop vertices.

Examples

import { EditableMesh, gridFill, type ElementSelection } from "@vgpu/render/edit";

const gridFillMesh = EditableMesh.fromArrays({
  positions: new Float32Array([0, 0, 0, 1, 0, 0, 0, 1, 0]),
});
const gridBoundary: ElementSelection = { domain: "edge", indices: [0, 1, 2], count: 3, ordered: true };
const gridFilled = gridFill(gridFillMesh, gridBoundary, { spanMode: "auto" });

Notes

  • Always emits GRID_FILL_TRIANGULATED; odd loop lengths also warn with FILL_NON_PLANAR_BOUNDARY wording.
  • See also: fillHole, bridge, MeshEditWarning.

dissolveVertices

Dissolves selected non-boundary vertices by dissolving their surrounding faces.

Import

import { dissolveVertices } from "@vgpu/render/edit";

Signature

import type { EditableMeshValue, ElementSelection, MeshEditWarning } from "@vgpu/render/edit";

declare interface DissolveVerticesOptions {
  readonly useFaceSplit?: boolean;
  readonly useBoundaryTear?: boolean;
}

declare interface DissolveVerticesResult {
  readonly mesh: EditableMeshValue;
  readonly surroundingFaces: ElementSelection;
  readonly warnings?: readonly MeshEditWarning[];
}

declare function dissolveVertices(em: EditableMeshValue, vertices: ElementSelection, opts?: DissolveVerticesOptions): DissolveVerticesResult;

Parameters

ParamTypeRequiredDefaultNotes
emEditableMeshValueSource mesh.
verticesElementSelectionMust be a non-empty vertex selection.
optsDissolveVerticesOptions{}Options object may be omitted.
opts.useFaceSplitbooleanaccepted but not used in v1Present in type only.
opts.useBoundaryTearbooleanaccepted but not used in v1Boundary vertices are skipped with warnings.

Returns: DissolveVerticesResult — edited mesh and resulting surrounding face selection. Throws: MeshEditError WRONG_DOMAIN if vertices.domain !== "vertex"; EMPTY_SELECTION if vertices.count === 0.

Examples

import { EditableMesh, dissolveVertices } from "@vgpu/render/edit";

const dissolveVerticesMesh = EditableMesh.fromArrays({
  positions: new Float32Array([0, 0, 0, 1, 0, 0, 0, 1, 0]),
});
const dissolvedVertices = dissolveVertices(dissolveVerticesMesh, dissolveVerticesMesh.vertices.byIndex([0]));

Notes

  • Boundary vertices are skipped with NON_MANIFOLD_VERTEX_SKIPPED.
  • See also: dissolveEdges, dissolveFaces, mergeByDistance.

dissolveEdges

Removes selected internal edges by merging each adjacent face pair and retriangulating with a deterministic diagonal.

Import

import { dissolveEdges } from "@vgpu/render/edit";

Signature

import type { EditableMeshValue, ElementSelection, MeshEditWarning } from "@vgpu/render/edit";

declare interface DissolveEdgesOptions { readonly useVerts?: boolean }
declare interface DissolveEdgesResult {
  readonly mesh: EditableMeshValue;
  readonly mergedFaces: ElementSelection;
  readonly warnings?: readonly MeshEditWarning[];
}

declare function dissolveEdges(em: EditableMeshValue, edges: ElementSelection, opts?: DissolveEdgesOptions): DissolveEdgesResult;

Parameters

ParamTypeRequiredDefaultNotes
emEditableMeshValueSource mesh.
edgesElementSelectionMust be a non-empty edge selection. Boundary/overlapping jobs are skipped with warnings.
optsDissolveEdgesOptions{}Options object may be omitted.
opts.useVertsbooleanaccepted but not used in v1Present in type only.

Returns: DissolveEdgesResult — edited mesh and merged face selection. Throws: MeshEditError WRONG_DOMAIN if edges.domain !== "edge"; EMPTY_SELECTION if edges.count === 0.

Examples

import { EditableMesh, dissolveEdges } from "@vgpu/render/edit";

const dissolveEdgesMesh = EditableMesh.fromArrays({
  positions: new Float32Array([0, 0, 0, 1, 0, 0, 0, 1, 0]),
});
const dissolvedEdges = dissolveEdges(dissolveEdgesMesh, dissolveEdgesMesh.edges.byIndex([0]));

Notes

  • Successful jobs emit DISSOLVE_FACES_RETRIANGULATED because the merged quad remains two triangles.
  • See also: dissolveVertices, dissolveFaces, EdgeView.

dissolveFaces

Removes selected face regions and retriangulates each region boundary as a fan.

Import

import { dissolveFaces } from "@vgpu/render/edit";

Signature

import type { EditableMeshValue, ElementSelection, MeshEditWarning } from "@vgpu/render/edit";

declare interface DissolveFacesResult {
  readonly mesh: EditableMeshValue;
  readonly resultFace: ElementSelection;
  readonly warnings?: readonly MeshEditWarning[];
}

declare function dissolveFaces(em: EditableMeshValue, faces: ElementSelection): DissolveFacesResult;

Parameters

ParamTypeRequiredDefaultNotes
emEditableMeshValueSource mesh.
facesElementSelectionMust be a non-empty face selection. Connected components are dissolved independently.

Returns: DissolveFacesResult — edited mesh and result face selection. Throws: MeshEditError WRONG_DOMAIN if faces.domain !== "face"; EMPTY_SELECTION if faces.count === 0.

Examples

import { EditableMesh, dissolveFaces } from "@vgpu/render/edit";

const dissolveFacesMesh = EditableMesh.fromArrays({
  positions: new Float32Array([0, 0, 0, 1, 0, 0, 0, 1, 0]),
});
const dissolvedFaces = dissolveFaces(dissolveFacesMesh, dissolveFacesMesh.faces.all());

Notes

  • Degenerate regions warn DEGENERATE_FACE_DROPPED; multi-face or n-gon regions warn DISSOLVE_FACES_RETRIANGULATED.
  • See also: dissolveEdges, dissolveVertices, fillHole.

mergeByDistance

Welds vertices whose positions are within a threshold, removes collapsed faces, and returns an old-to-new vertex map.

Import

import { mergeByDistance } from "@vgpu/render/edit";

Signature

import type { EditableMeshValue, ElementSelection, MeshEditWarning } from "@vgpu/render/edit";

declare interface MergeByDistanceOptions {
  readonly threshold?: number;
  readonly selection?: ElementSelection;
  readonly key?: "position" | "full-vertex";
}

declare interface MergeByDistanceResult {
  readonly mesh: EditableMeshValue;
  readonly mergeMap: ReadonlyMap<number, number>;
  readonly weldedCount: number;
  readonly warnings?: readonly MeshEditWarning[];
}

declare function mergeByDistance(em: EditableMeshValue, opts?: MergeByDistanceOptions): MergeByDistanceResult;

Parameters

ParamTypeRequiredDefaultNotes
emEditableMeshValueSource mesh.
optsMergeByDistanceOptions{}Options object may be omitted.
opts.thresholdnumber1e-4Euclidean position distance for clustering.
opts.selectionElementSelectionem.vertices.all()Must be a vertex selection. Only selected vertices are clustered; all faces are remapped.
opts.key"position" | "full-vertex"warning mode equivalent to "full-vertex"Clustering is position-based in v1. "position" emits SEAM_DESTROYED when UV/normal/color flags exist.

Returns: MergeByDistanceResult — welded mesh, old vertex index to new vertex index map (-1 for unused), and welded count. Throws: MeshEditError WRONG_DOMAIN if opts.selection.domain !== "vertex"; EMPTY_SELECTION if opts.selection.count === 0.

Examples

import { EditableMesh, mergeByDistance } from "@vgpu/render/edit";

const mergeMesh = EditableMesh.fromArrays({
  positions: new Float32Array([0, 0, 0, 0.00001, 0, 0, 0, 1, 0]),
});
const merged = mergeByDistance(mergeMesh, { threshold: 0.001 });

Notes

  • Collapsed faces are removed and reported with MERGE_DEGENERATE_FACES_REMOVED.
  • See also: healManifold, dissolveVertices, recomputeNormals.

healManifold

Deterministic cleanup pass that removes duplicate, degenerate, and overused-edge faces where possible.

Import

import { healManifold } from "@vgpu/render/edit";

Signature

import type { EditableMeshValue, MeshEditWarning } from "@vgpu/render/edit";

declare interface HealManifoldReport {
  readonly nonManifoldEdgesFixed: number;
  readonly nonManifoldVerticesFixed: number;
  readonly holesFixed: number;
  readonly duplicateFacesRemoved: number;
}

declare interface HealManifoldResult {
  readonly mesh: EditableMeshValue;
  readonly report: HealManifoldReport;
  readonly warnings?: readonly MeshEditWarning[];
}

declare function healManifold(em: EditableMeshValue): HealManifoldResult;

Parameters

ParamTypeRequiredDefaultNotes
emEditableMeshValueSource mesh.

Returns: HealManifoldResult — cleaned mesh plus report. nonManifoldVerticesFixed and holesFixed are currently always 0. Throws: — no MeshEditError is thrown directly.

Examples

import { EditableMesh, healManifold } from "@vgpu/render/edit";

const healMesh = EditableMesh.fromArrays({
  positions: new Float32Array([0, 0, 0, 1, 0, 0, 0, 1, 0]),
  indices: new Uint32Array([0, 1, 2, 0, 1, 2]),
});
const healed = healManifold(healMesh);

Notes

  • Remaining non-manifold residue is reported as HEAL_NON_MANIFOLD_RESIDUE.
  • See also: mergeByDistance, recomputeNormals, MeshEditWarning.

recomputeNormals

Rebuilds the editable mesh and recomputes face normals using smoothing components and sharp edges or a new crease angle.

Import

import { recomputeNormals } from "@vgpu/render/edit";

Signature

import type { EditableMeshValue } from "@vgpu/render/edit";

declare interface RecomputeNormalsOptions {
  readonly weighting?: "angle" | "area" | "uniform";
  readonly creaseAngle?: number;
}

declare function recomputeNormals(em: EditableMeshValue, opts?: RecomputeNormalsOptions): EditableMeshValue;

Parameters

ParamTypeRequiredDefaultNotes
emEditableMeshValueSource mesh. Empty meshes are returned unchanged.
optsRecomputeNormalsOptions{}Options object may be omitted.
opts.weighting"angle" | "area" | "uniform""angle"Weighting mode for smoothing component normals.
opts.creaseAnglenumberpreserve current sharp-edge maskIf provided, rebuilds sharp edges from the crease angle instead of preserving isSharp.

Returns: EditableMeshValue — new mesh with recomputed kernel face normals, or the same mesh when faceCount === 0. Throws: — no MeshEditError is thrown directly.

Examples

import { EditableMesh, recomputeNormals } from "@vgpu/render/edit";

const normalsMesh = EditableMesh.fromArrays({
  positions: new Float32Array([0, 0, 0, 1, 0, 0, 0, 1, 0]),
});
const normalsRecomputed = recomputeNormals(normalsMesh, { weighting: "area" });

Notes

  • Run after topology-changing operators if downstream code relies on smooth normals.
  • See also: EditableMesh, mergeByDistance, healManifold.

MeshEditError

Error class thrown by validation and topology operators. Catch by instanceof MeshEditError and branch on code.

Import

import { MeshEditError } from "@vgpu/render/edit";

Signature

export type MeshEditErrorCode =
  | "NON_MANIFOLD"
  | "STALE_SELECTION"
  | "EMPTY_SELECTION"
  | "WRONG_DOMAIN"
  | "NOT_ORDERED"
  | "DEGENERATE_RESULT"
  | "AMBIGUOUS_TOPOLOGY"
  | "UNSUPPORTED_INPUT";

declare class MeshEditError extends Error {
  readonly code: MeshEditErrorCode;
  readonly suggestion?: string;
  constructor(opts: { readonly code: MeshEditErrorCode; readonly message?: string; readonly suggestion?: string });
}

Parameters

Param/FieldTypeRequiredDefaultNotes
opts.codeMeshEditErrorCodeMachine-readable error code.
opts.messagestringopts.codeError message passed to Error.
opts.suggestionstringomittedOptional recovery hint.
codeMeshEditErrorCodePublic readonly field copied from constructor.
suggestionstringomittedPublic readonly optional field.
namestring"MeshEditError"Set by constructor.

Returns: MeshEditError instance from new MeshEditError(...). Throws: — constructor does not throw.

Examples

import { MeshEditError } from "@vgpu/render/edit";

const meshEditError = new MeshEditError({ code: "EMPTY_SELECTION", suggestion: "Select at least one face." });
const meshEditErrorCode = meshEditError.code;

Notes

  • Common validation codes: WRONG_DOMAIN, EMPTY_SELECTION, NOT_ORDERED.
  • See also: ElementSelection, bridge, gridFill.

MeshEditWarning

Non-fatal diagnostic emitted in operator result warnings arrays. Use warnings to explain deterministic fallbacks and data loss.

Import

import { MeshEditWarning } from "@vgpu/render/edit";

Signature

export type MeshEditWarningCode =
  | "NON_MANIFOLD_EDGE_SKIPPED"
  | "NON_MANIFOLD_VERTEX_SKIPPED"
  | "DEGENERATE_FACE_DROPPED"
  | "TANGENTS_STRIPPED"
  | "BEVEL_ACUTE_CLAMPED"
  | "BEVEL_SEGMENTS_CLAMPED"
  | "INSET_OVERLAP_CLAMPED"
  | "SEAM_DESTROYED"
  | "BRIDGE_LOOP_LENGTH_MISMATCH"
  | "FILL_NON_PLANAR_BOUNDARY"
  | "LOOP_CUT_AMBIGUOUS_CONTINUATION"
  | "FILL_HOLE_TRIANGULATED"
  | "GRID_FILL_TRIANGULATED"
  | "DISSOLVE_FACES_RETRIANGULATED"
  | "MERGE_DEGENERATE_FACES_REMOVED"
  | "HEAL_NON_MANIFOLD_RESIDUE";

declare class MeshEditWarning {
  readonly code: MeshEditWarningCode;
  readonly reason: string;
  readonly element?: { readonly domain: "vertex" | "edge" | "face"; readonly index: number };
  constructor(
    code: MeshEditWarningCode,
    reason: string,
    element?: { readonly domain: "vertex" | "edge" | "face"; readonly index: number },
  );
}

Parameters

Param/FieldTypeRequiredDefaultNotes
codeMeshEditWarningCodeMachine-readable warning code.
reasonstringHuman-readable explanation.
element{ domain: "vertex" | "edge" | "face"; index: number }omittedOptional source element associated with the warning.

Returns: MeshEditWarning instance from new MeshEditWarning(...). Throws: — constructor does not throw.

Examples

import { MeshEditWarning } from "@vgpu/render/edit";

const meshEditWarning = new MeshEditWarning("GRID_FILL_TRIANGULATED", "Triangle-only output was used.");
const warningReason = meshEditWarning.reason;

Notes

  • Treat warnings as actionable diagnostics, not failures; operators still return a mesh.
  • See also: toEditableWithDiagnostics, bevel, gridFill, healManifold.