/ concept-collection / mesh-studio
concept-collection / mesh-studio
mesh-studio / src / model / types.ts
101 lines · 3.5 KBBlameHistoryRaw
1/**
2 * The internal mesh model for mesh-studio.
3 *
4 * Deliberately broader than a plain triangle mesh: a `SurfaceModel` is a
5 * collection of *patches*, and a patch is a discriminated union so that
6 * different mesh-producing tools can contribute different kinds of faces
7 * without a redesign. v1 (OpenCASCADE) emits `nurbs` patches — each carrying
8 * both a triangulation for display and the underlying rational-B-spline
9 * surface (the "polynomial on the face"). The other kinds are reserved growth
10 * points documented below.
11 */
13/** A triangulated approximation of one patch — what three.js consumes. */
14export interface TriMesh {
15 /** Flat xyz triples, 3 numbers per vertex. */
16 positions: Float32Array
17 /** Flat xyz triples, 3 numbers per vertex (matches positions). */
18 normals: Float32Array
19 /** Flat triangle indices (0-based), 3 per triangle. */
20 indices: Uint32Array
23/**
24 * A rational tensor-product B-spline (NURBS) surface — a piecewise polynomial
25 * in two parameters (u, v). This is the exact object an OpenCASCADE B-rep face
26 * carries. Poles are stored row-major: pole (i, j) with 0 <= i < nu,
27 * 0 <= j < nv lives at index `(i * nv + j) * 3` in `poles`.
28 */
29export interface NurbsSurface {
30 uDegree: number
31 vDegree: number
32 nu: number
33 nv: number
34 /** Control points ("poles"), nu*nv*3 numbers, row-major. */
35 poles: Float32Array
36 /** Per-pole weights, nu*nv numbers, or null when non-rational (pure polynomial). */
37 weights: Float32Array | null
38 /** Distinct knot values in u, with matching multiplicities. */
39 uKnots: number[]
40 uMults: number[]
41 vKnots: number[]
42 vMults: number[]
45/** One face of a model. Discriminated on `kind`. */
46export type Patch =
47 | NurbsPatch
48 // --- reserved growth points (not emitted in v1) ---
49 // A flat linear cell mesh imported from a triangle/quad/polygon format.
50 | { kind: 'linear'; id: number; tri: TriMesh }
51 // A nodal high-order element (e.g. surfacefun): order + a grid of sample nodes.
52 | { kind: 'lagrange'; id: number; tri: TriMesh; order: number }
53 // A generic parametric patch carrying its own evaluator.
54 | { kind: 'parametric'; id: number; tri: TriMesh }
56export interface NurbsPatch {
57 kind: 'nurbs'
58 id: number
59 /** Triangulation for display (from OpenCASCADE's BRepMesh, respects trimming). */
60 tri: TriMesh
61 /** The underlying polynomial surface, or null if extraction was unavailable. */
62 nurbs: NurbsSurface | null
63 /** Sampled iso-parameter curves (world xyz polylines) for the isocurve view. */
64 isoLines?: Float32Array[]
67export interface SurfaceModel {
68 patches: Patch[]
69 source: {
70 kind: 'primitive' | 'step' | 'iges'
71 label: string
72 }
73 /** Original uploaded bytes, retained so STEP/IGES sources can be re-exported verbatim. */
74 raw?: { format: 'step' | 'iges'; bytes: Uint8Array }
77/** Every patch has a triangulation; this narrows the union for callers. */
78export function patchTri(patch: Patch): TriMesh {
79 return patch.tri
82export function triangleCount(model: SurfaceModel): number {
83 let n = 0
84 for (const p of model.patches) n += p.tri.indices.length / 3
85 return n
88export function vertexCount(model: SurfaceModel): number {
89 let n = 0
90 for (const p of model.patches) n += p.tri.positions.length / 3
91 return n
94/** How many faces carry extracted NURBS data (for the "N/M faces" readout). */
95export function nurbsCoverage(model: SurfaceModel): { withNurbs: number; total: number } {
96 let withNurbs = 0
97 for (const p of model.patches) {
98 if (p.kind === 'nurbs' && p.nurbs) withNurbs++
99 }
100 return { withNurbs, total: model.patches.length }