Документация
Сетка и ядро CSG
Замкнутые треугольные сетки из параметрических тел, логические значения BSP с выпуклым быстрым путем, очистка и проверка топологии.
Автоматически переведено с английского. Смотреть оригинал на английском
Что такое ядро сетки
Ядро сетки работает с сетками замкнутых треугольников: оно строит их из параметрических примитивов (meshOf), измеряет их (meshVolume, meshBounds, meshTopology) и объединяет их с логическими значениями (booleanMesh, booleanAll). Это то, что показывает окно просмотра, что делает генератор CAM и что пишет экспорт STL.
Это не точное представление границ. Разрезанный цилиндр остается граненым цилиндром, и у него нет топологии грани, по которой можно было бы выполнить сопряжение или измерение. Для точных граней, скруглений, массовых свойств и STEP Ядро B-rep это отдельный фрагмент кода.
| Файл | Линии | Роль |
|---|---|---|
cad/solid.ts | 659 | Схемы твердого тела (Box, Cylinder, Sphere, Cone, Torus, Extrume, Revolve), meshOf, meshVolume, meshBounds |
cad/csg.ts | 459 | BSP Booleans, выпуклый классификатор, перевод/поворот/объединение |
cad/mesh-clean.ts | 626 | сварной шов, ремонт Т-образного соединения, копланарное слияние, повторная триангуляция |
cad/mesh-topology.ts | 110 | многообразие, оболочки, род, объём, площадь |
cad/triangulate.ts | 383 | триангуляция многоугольника с отверстиями |
Тип сетки и примитивы
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 | Твердый | Поля | Пределы |
|---|---|---|
| Коробка | начало координат, размер {x, y, z} | размеры > 0 |
| Цилиндр | начало координат, радиус, высота, сегменты, ось? | сегменты 3–512; ось z | х | й |
| Сфера | центр, радиус, кольца | кольца 2–256 |
| Конус | начало координат, baseRadius, topRadius, высота, сегменты, ось? | топРадиус ≥ 0; сегменты 3–512 |
| Тор | центр, мажорРадиус, минорРадиус, вращение?, мажорСегментс, минорСегментс, ось? | 3–512 и 3–256 |
| Выдавливание | профиль (≥ 3 точек, не закрытый), высота, начало координат? | профиль в XY |
| вращаться | профиль (≥ 2 точек по XZ), угол, сегменты? | о З; сегменты 3–512, по умолчанию 16 |
segments и rings являются минимумами: meshOf поднимает их до тех пор, пока ошибка аккорда не окажется в пределах tolerance, using step = 4·asin(√(min(1, tol/r)/2)). Two budgets stop runaway tolerances.
| Сторожить | Ценить | Сообщение об ошибке |
|---|---|---|
| толерантность должна быть положительной и конечной | — | Допуск сетки должен быть положительным и конечным. |
| сегментов на кривую | 65 536 | Допуск сетки превышает бюджет подразделения |
| строки сетки × столбцы (сфера, тор, вращение) | 1 000 000 | Допуск сетки превышает бюджет вершин |
Цилиндр Ø6 с segments: 24 при допуске 0.05 получилось с треугольниками 96 (стороны 24, две вершины 24-угольника); Ø20 с 128, поскольку для большего радиуса требовалось, чтобы сегменты 32 оставались в пределах 0.05 мм.
Логические значения путем разделения двоичного пространства
Каждый операнд становится классификатором. Общая сетка получает дерево BSP; Выпуклая сетка (замкнутая, связная, каждый край выпуклый) представляет собой просто отдельные плоскости ее граней, поэтому ящик состоит из шести плоскостей, а сфера из десяти тысяч треугольников классифицируется без построения квадратичной цепи, в которую вырождается BSP выпуклого твердого тела. Полигоны классифицируются как списки, и полигон, выходящий за рамки ограничивающего прямоугольника другого операнда, сохраняется или удаляется без каких-либо перемещений.
- If either mesh is empty, or the bounding boxes do not meet (within tolerance), answer directly: union = merge, subtract = a, intersect = empty.
- Постройте классификаторы A и B (выпуклые плоскости или BSP).
- Объединение: оставьте A снаружи B, B снаружи A и отбросьте грани B, совпадающие с гранями A, указывающими в том же направлении, чтобы общее лицо сохранялось один раз.
- Subtract and intersect follow the classic a.clipTo(b), b.clipTo(a), b.invert() sequence on polygon lists, without copying trees.
- Очистите полигональный суп: точки сварки в пределах max(10·eps, 1e-9·size), закройте Т-образные соединения, закройте зазоры размером до 1e-4·между полосками, объедините копланарные фрагменты и повторите триангуляцию.
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 Число треугольников растет вместе с объектами, а не с количеством логических значений: шаг слияния возвращает копланарные фрагменты обратно в целые грани. На пластине выше коробка имела треугольники 12, после четырёх отверстий 412, после бобышки 540, после отверстия 640; вся цепочка выполнялась за 96 мс.
Объединение, вычитание, пересечение на изогнутом входе
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:
| Результат | Объем, мм³ | Треугольники |
|---|---|---|
| только сфера | 7 063.84 (точно 7 238.23) | 624 |
| куб ∩ сфера | 882.98 | 112 |
| куб ∪ сфера | 14 180.86 | — |
| куб − сфера | 7 117.02 | — |
Проверьте: 8 000 + 7 063.84 – 882.98 = 14 180.86 и 8 000 – 882.98 = 7 117.02, чтобы эти три результата согласовывались друг с другом с точки зрения сотый. Сама сетка сферы составляет 2.4 % под точной сферой, что является гранью грубого допуска, а не логической ошибки.
Проверка топологии
meshTopology подсчитывает вершины, ребра и треугольники, находит границы, не многообразные и неправильно ориентированные ребра, вырожденные треугольники и оболочки, а также выводит эйлерову характеристику и род. describeTopology печатает однострочную сводку, которую использует помощник и область просмотра.
{"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.
Триангуляция многоугольника
triangulatePolygon(outer, holes) возвращает тройки индексов в результате объединения внешнего кольца и отверстий. Квадрат 4 × 4 с отверстием 2 × 2 дает восемь треугольников:
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 возвращает undefined вместо частичного результата, когда входные данные не могут быть триангулированы (самопересекающиеся кольца, отверстия за пределами внешнего кольца).
Поиск неисправностей
| Симптом | Причина | Исправить |
|---|---|---|
| Логический результат имеет дыры или перевернутые грани | вход открыт или закручен по часовой стрелке | проверьте оба входа с помощью meshTopology: borderEdges и misориентedEdges должны быть 0 |
| лицо, общее для обоих операндов, отсутствует или дублируется | грани почти, но не совсем совпадают (на расстоянии, превышающем допуск) | привяжите операнды к одной плоскости или поднимите CsgOptions.tolerance |
| RangeError: допуск сетки превышает бюджет подразделения | допуск слишком мал для радиуса | используйте настройки допуска (от 0.1 до 0.0005 мм) |
| объем отличается от формулы | огранка с грубым допуском | сетка более мелкая или используйте ядро B-rep для точных массовых свойств |
| Логическое значение медленно на многих инструментах | объединение инструментов, созданных первыми | вычитать инструменты один за другим (booleanAll делает это): каждый выпуклый инструмент представляет собой несколько плоскостей |