Documentación
Kernel de malla y CSG
Mallas de triángulos cerrados a partir de sólidos paramétricos, booleanos BSP con ruta rápida convexa, limpieza y comprobaciones de topología.
Traducido automáticamente del inglés. Ver el original en inglés
¿Qué es el núcleo de malla?
El núcleo de malla funciona en mallas de triángulos cerrados: las construye a partir de primitivas paramétricas (meshOf), los mide (meshVolume, meshBounds, meshTopology), y los combina con booleanos (booleanMesh, booleanAll). Es lo que muestra la ventana gráfica, lo que escribe la máquina generadora CAM y lo que escribe la exportación STL.
No es una representación de límites exacta. Un cilindro que se cortó sigue siendo un cilindro facetado y no existe una topología de cara para filetear o medir. Para caras exactas, filetes, propiedades de masa y STEP, el Núcleo B-rep es una pieza de código separada.
| Archivo | Pauta | Role |
|---|---|---|
cad/solid.ts | 659 | Esquemas sólidos (caja, cilindro, esfera, cono, toro, extrusión, revolución), meshOf, meshVolume, meshBounds |
cad/csg.ts | 459 | BSP booleanos, clasificador convexo, traducir/rotar/fusionar |
cad/mesh-clean.ts | 626 | soldadura, reparación de uniones en T, fusión coplanar, retriangulación |
cad/mesh-topology.ts | 110 | colector, conchas, género, volumen, área |
cad/triangulate.ts | 383 | triangulación de polígonos con agujeros |
El tipo de malla y sus primitivas.
export interface Mesh {
readonly positions: Float32Array // x, y, z per vertex
readonly indices: Uint32Array // three per triangle, counter-clockwise seen from outside
}
export const meshOf: (solid: Solid, tolerance?: number /* 0.1 mm */) => Mesh
export const meshVolume: (mesh: Mesh) => number // signed; positive when outward-facing
export const meshBounds: (mesh: Mesh) => SolidBounds | Sólido | Campos | Límites |
|---|---|---|
| Caja | origen, tamaño {x, y, z} | tamaños > 0 |
| Cilindro | origen, radio, altura, segmentos, eje? | segmentos 3–512; eje z | x | y |
| Esfera | centro, radio, anillos | anillos 2–256 |
| Cono | origen, baseRadius, topRadius, altura, segmentos, eje? | radio superior ≥ 0; segmentos 3–512 |
| Toro | centro, radio mayor, radio menor, ¿rotación?, segmentos mayores, segmentos menores, eje? | 3–512 y 3–256 |
| Extrudir | perfil (≥ 3 puntos, no cerrado), altura, origen? | perfil en XY |
| Girar | perfil (≥ 2 puntos en XZ), ángulo, segmentos? | acerca de Z; segmentos 3–512, predeterminado 16 |
segments y rings son mínimos: meshOf los sube hasta que el error de acorde esté dentro tolerance, using step = 4·asin(√(min(1, tol/r)/2)). Two budgets stop runaway tolerances.
| Guardia | Valor | Mensaje de error |
|---|---|---|
| la tolerancia debe ser positiva y finita. | — | La tolerancia de la malla debe ser positiva y finita. |
| segmentos por curva | 65 536 | La tolerancia de la malla supera el presupuesto de subdivisión |
| filas de cuadrícula × columnas (esfera, toro, revolución) | 1 000 000 | La tolerancia de la malla excede el presupuesto del vértice |
Un cilindro de Ø6 con segments: 24 en la tolerancia 0.05 salió con 96 triángulos (24 lados, dos tapas de 24-gon); uno de Ø20 con 128, porque el radio más grande necesitaba 32 segmentos para permanecer dentro de 0.05 mm.
Booleanos por partición de espacio binario
Cada operando se convierte en un clasificador. Una malla general obtiene un árbol BSP; una malla convexa (cerrada, conectada, cada borde convexo) son solo sus planos de cara distintos, por lo que una caja tiene seis planos y una esfera de diez mil triángulos se clasifica sin construir la cadena cuadrática en la que degenera un BSP de un sólido convexo. Los polígonos se clasifican como listas, y un polígono libre del cuadro delimitador del otro operando se mantiene o se elimina sin recorrer nada.
- If either mesh is empty, or the bounding boxes do not meet (within tolerance), answer directly: union = merge, subtract = a, intersect = empty.
- Construir los clasificadores de A y B (planos convexos o BSP).
- Unión: mantenga A fuera de B, B fuera de A y suelte las caras de B coincidiendo con las de A apuntando en la misma dirección, de modo que una cara compartida se mantenga una vez.
- Subtract and intersect follow the classic a.clipTo(b), b.clipTo(a), b.invert() sequence on polygon lists, without copying trees.
- Limpie la sopa de polígonos: suelde puntos dentro de un tamaño máximo (10·eps, 1e-9·), cierre uniones en T, cierre espacios de hasta 1e-4·tamaño entre astillas, fusione fragmentos coplanares y vuelva a triangular.
export type BooleanOp = "union" | "subtract" | "intersect"
export interface CsgOptions {
/** Distance below which a point counts as on a plane, mm. Default 1e-7 × the larger extent. */
readonly tolerance?: number
/** Merge coplanar fragments and re-triangulate (default true). */
readonly mergeCoplanar?: boolean
}
export const booleanMesh: (a: Mesh, b: Mesh, op: BooleanOp, options?: CsgOptions) => Mesh
/** Union reduced as a balanced tree; subtract and intersect fold left to right. */
export const booleanAll: (meshes: ReadonlyArray<Mesh>, op: BooleanOp, options?: CsgOptions) => Mesh
export const translateMesh: (mesh: Mesh, dx: number, dy: number, dz: number) => Mesh
export const rotateMesh: (mesh: Mesh, axis: V3, radians: number, origin?: V3) => Mesh
export const mergeMeshes: (meshes: ReadonlyArray<Mesh>) => Mesh El número de triángulos crece con las características, no con el número de booleanos: el paso de fusión devuelve fragmentos coplanares a caras completas. En la placa de arriba, la caja tenía 12 triángulos, después de cuatro agujeros 412, después del jefe 540, después del agujero 640; toda la cadena se ejecutó en 96 ms.
Unión, resta, intersección en entrada curva
A 20 mm cube at the origin against a sphere of radius 12 centred on its far corner (20, 20, 20), meshed at tolerance 0.2 (624 triangles). Real volumes:
| Resultado | Volumen, mm³ | Triangulos |
|---|---|---|
| esfera sola | 7 063.84 (exactamente 7 238.23) | 624 |
| cubo ∩ esfera | 882.98 | 112 |
| cubo ∪ esfera | 14 180.86 | — |
| cubo - esfera | 7 117.02 | — |
Comprueba: 8 000 + 7 063.84 − 882.98 = 14 180.86 y 8 000 − 882.98 = 7 117.02, por lo que los tres resultados son consistentes entre sí hasta la centésima. La malla de la esfera en sí es 2.4 % debajo de la esfera exacta, que es el facetado de una tolerancia aproximada, no de un error booleano.
Verificación de topología
meshTopology cuenta vértices, aristas y triángulos, encuentra límites, aristas no múltiples y mal orientadas, triángulos y conchas degenerados, y deriva la característica y el género de Euler. describeTopology imprime el resumen de una línea que utilizan el asistente y la ventana gráfica.
{"vertices":312,"triangles":640,"edges":960,"boundaryEdges":0,"nonManifoldEdges":0,
"misorientedEdges":0,"degenerateTriangles":0,"shells":1,"eulerCharacteristic":-8,"genus":5,
"closed":true,"solid":true,"volume":23981.43509870267,"area":8238.640705862534}
// describeTopology → "closed solid, 1 shell, genus 5, 640 triangles, V 23981.4 mm³, A 8238.6 mm²" V − E + F = 312 − 960 + 640 = −8 = 2 − 2g, so g = 5: the four drilled holes plus the bore, which runs through the boss and the plate.
Triangulación de polígonos
triangulatePolygon(outer, holes) devuelve el índice se triplica en la concatenación del anillo exterior y los agujeros. Un cuadrado 4 × 4 con un agujero 2 × 2 da ocho triángulos:
triangulatePolygon(
[{x:0,y:0},{x:4,y:0},{x:4,y:4},{x:0,y:4}],
[[{x:1,y:1},{x:1,y:3},{x:3,y:3},{x:3,y:1}]])
// [[0,4,5],[7,4,0],[3,0,5],[7,0,1],[2,3,5],[6,7,1],[2,5,6],[6,1,2]] triangulatePolygonChecked regresa undefined en lugar de un resultado parcial cuando la entrada no se puede triangular (anillos que se cruzan solos, agujeros fuera del anillo exterior).
Solución de problemas
| Síntoma | Causa | Arreglar |
|---|---|---|
| El resultado booleano tiene agujeros o caras invertidas | una entrada está abierta o enrollada en el sentido de las agujas del reloj | verifique ambas entradas con meshTopology: los bordes de límites y los bordes mal orientados deben ser 0 |
| falta una cara compartida por ambos operandos o está duplicada | las caras son casi coincidentes, pero no exactamente (más separadas que la tolerancia) | ajuste los operandos al mismo plano o aumente CsgOptions.tolerance |
| RangeError: la tolerancia de la malla excede el presupuesto de subdivisión | tolerancia demasiado fina para el radio | utilice los ajustes preestablecidos de tolerancia (0.1 a 0.0005 mm) |
| el volumen difiere de la fórmula. | facetado con tolerancia gruesa | malla más fina o utilice el núcleo B-rep para obtener propiedades de masa exactas |
| Booleano lento en muchas herramientas | unión de herramientas construidas primero | restar herramientas una por una (booleanAll hace esto): cada herramienta convexa es un puñado de planos |