Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3b66719029 | ||
|
|
dd5f519c06 |
@@ -53,3 +53,6 @@ docs/theory/solver_2x_sdf/out/
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
Thumbs.db
|
Thumbs.db
|
||||||
desktop.ini
|
desktop.ini
|
||||||
|
|
||||||
|
# Общие заметки Claude Code: один файл на все ветки, вне версионирования
|
||||||
|
/CLAUDE.md
|
||||||
|
|||||||
@@ -1,202 +0,0 @@
|
|||||||
# CLAUDE.md
|
|
||||||
|
|
||||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
||||||
|
|
||||||
## Project Overview
|
|
||||||
|
|
||||||
**SimVulcan** is a minimal **Vulkan 1.3 / C++20** 3D model editor. It renders a
|
|
||||||
3D editor space — three reference grid planes (XY, XZ, YZ) through the origin plus
|
|
||||||
coloured X/Y/Z axes — viewed through an orbit camera, and loads/displays `.obj`
|
|
||||||
models with selectable display modes (solid, wireframe, solid + wireframe).
|
|
||||||
|
|
||||||
The C++ side runs no simulation: it is a focused rendering skeleton with an ImGui
|
|
||||||
interface (a Viewport control panel and a Mesh load panel). The repository *also*
|
|
||||||
carries an unrelated Python research prototype under `docs/theory/` — see
|
|
||||||
[Research prototype](#research-prototype-docstheory) below; it is not part of the
|
|
||||||
CMake build.
|
|
||||||
|
|
||||||
## Build
|
|
||||||
|
|
||||||
Prerequisites: **Vulkan SDK 1.3.290+** (provides `glslangValidator` for offline
|
|
||||||
shader compilation), **CMake 3.26+**, **Ninja**, a C++20 compiler (MSVC 19.36+,
|
|
||||||
gcc 11+, clang 14+). On macOS, Vulkan is via MoltenVK (Apple Silicon only).
|
|
||||||
|
|
||||||
```sh
|
|
||||||
cmake --preset windows-msvc-release
|
|
||||||
cmake --build --preset windows-msvc-release
|
|
||||||
```
|
|
||||||
|
|
||||||
Presets: `windows-msvc-debug`, `windows-msvc-release`, `linux-gcc-release`,
|
|
||||||
`linux-clang-release`, `macos-arm64-release` (all Ninja, one dir per preset under
|
|
||||||
`build/<presetName>/`). The first configure fetches dependencies via FetchContent
|
|
||||||
(GLFW, GLM, volk, vk-bootstrap, VulkanMemoryAllocator, Dear ImGui, spdlog,
|
|
||||||
tinyobjloader, Catch2) and needs network access. `VK_NO_PROTOTYPES` is set
|
|
||||||
project-wide; Vulkan entry points load through **volk**.
|
|
||||||
|
|
||||||
Warnings come from `simv_set_warnings` (`/W4 /permissive-`, or
|
|
||||||
`-Wall -Wextra -Wpedantic -Wshadow -Wold-style-cast …`); they are **not** errors.
|
|
||||||
`cmake/Sanitizers.cmake` defines `simv_enable_sanitizers` (Debug-only ASan/UBSan)
|
|
||||||
but no target currently calls it — wire it in manually when chasing memory bugs.
|
|
||||||
|
|
||||||
## Running
|
|
||||||
|
|
||||||
The executable resolves SPIR-V relative to the working directory (`FindSpvPath`
|
|
||||||
probes `spirv/<rel>` and `current_path()/spirv/<rel>`). The `SimVulcan` POST_BUILD
|
|
||||||
step copies the compiled `spirv/` tree and `assets/` next to the executable, so
|
|
||||||
**run from the executable's own directory** (`build/<preset>/src/app/`). `main.cpp`
|
|
||||||
also probes several `../` ancestors for `assets/meshes`. Meshes load at runtime
|
|
||||||
through the ImGui Mesh panel.
|
|
||||||
|
|
||||||
Running writes two files into the CWD: `pipeline_cache.bin` (serialised
|
|
||||||
`VkPipelineCache`, reloaded on the next start) and ImGui's `imgui.ini`. Both are
|
|
||||||
disposable — delete them if pipeline creation or the panel layout misbehaves.
|
|
||||||
|
|
||||||
`ContextOptions::enableValidation` / `enableDebugUtils` default to **true** in
|
|
||||||
every build config, so the Khronos validation layer is requested even in Release;
|
|
||||||
messages (error + warning severity) go through spdlog.
|
|
||||||
|
|
||||||
`run.bat` at the repo root is **stale**: it launches
|
|
||||||
`build/vs2022/src/app/Release/SimVulcan.exe`, a path the Ninja presets never
|
|
||||||
produce. Do not point users at it without fixing the path first.
|
|
||||||
|
|
||||||
## Tests
|
|
||||||
|
|
||||||
Catch2 unit tests, pure CPU/math (mesh bounds + welding). No GPU required.
|
|
||||||
|
|
||||||
```sh
|
|
||||||
cmake --build --preset windows-msvc-debug --target simv_tests
|
|
||||||
ctest --preset windows-msvc-debug
|
|
||||||
```
|
|
||||||
|
|
||||||
`windows-msvc-debug` is the only preset with a `testPreset` — for the other
|
|
||||||
configs, invoke `ctest` in `build/<preset>/` directly.
|
|
||||||
|
|
||||||
Single test / subset — either through CTest (each `TEST_CASE` is registered
|
|
||||||
individually by `catch_discover_tests`):
|
|
||||||
|
|
||||||
```sh
|
|
||||||
ctest --preset windows-msvc-debug -R "WeldVertices" -V
|
|
||||||
```
|
|
||||||
|
|
||||||
or by running the binary with a Catch2 name or tag filter:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
./build/windows-msvc-debug/tests/simv_tests.exe "[decimator]"
|
|
||||||
./build/windows-msvc-debug/tests/simv_tests.exe --list-tests
|
|
||||||
```
|
|
||||||
|
|
||||||
## Architecture
|
|
||||||
|
|
||||||
### Library / target map
|
|
||||||
|
|
||||||
```
|
|
||||||
SimVulcan (exe) → simv_core, simv_vk, simv_mesh, simv_editor
|
|
||||||
simv_core → simv_vk (App owns Window + vk::Renderer; no Vulkan calls)
|
|
||||||
simv_editor → simv_core, simv_mesh (Camera, input, ImGui panels; no Vulkan)
|
|
||||||
simv_mesh → simv_core (CPU mesh only; no Vulkan)
|
|
||||||
simv_vk → third-party (volk, vk-bootstrap, VMA, GLFW, GLM, spdlog, imgui)
|
|
||||||
simv_shaders → glslangValidator (GLSL → SPIR-V)
|
|
||||||
```
|
|
||||||
|
|
||||||
Namespaces follow directories: `simv::core`, `simv::vk`, `simv::mesh`,
|
|
||||||
`simv::editor`. Each library exports `src/` as its include root, so includes are
|
|
||||||
written module-qualified (`#include "mesh/Mesh.h"`, `#include "vk/Renderer.h"`).
|
|
||||||
|
|
||||||
### Frame loop
|
|
||||||
|
|
||||||
`main.cpp` creates `core::App`, which owns the `core::Window` and a
|
|
||||||
`vk::Renderer`, then runs the loop. Each frame `Renderer::DrawFrame` calls the UI
|
|
||||||
callback (between ImGui NewFrame/Render), then records the scene: grid + mesh into
|
|
||||||
one dynamic-rendering pass with a depth attachment, followed by ImGui, then
|
|
||||||
presents (2 frames in flight, sync2 submits).
|
|
||||||
|
|
||||||
`main.cpp` wires the editor via the UI callback: it draws the panels, applies
|
|
||||||
mouse input to the `editor::Camera`, and pushes the resulting state into the
|
|
||||||
renderer (`SetViewProj`, `SetRenderMode`, `SetGridVisible`). Mesh loads go through
|
|
||||||
`MeshLoadPanel`'s callback → `Renderer::SetMeshCpu`.
|
|
||||||
|
|
||||||
### Mesh load path
|
|
||||||
|
|
||||||
`MeshLoadPanel` lists `*.obj` in the mesh directory and kicks off
|
|
||||||
`mesh::LoadObjAsync` (worker thread, `std::future`). The future is **drained on
|
|
||||||
the main thread** at the top of `MeshLoadPanel::Draw`, so the `OnLoaded` callback
|
|
||||||
— and therefore the GPU upload — always runs on the render thread. Loader
|
|
||||||
exceptions surface as the panel's status string.
|
|
||||||
|
|
||||||
`main.cpp`'s `OnLoaded` welds the mesh (`WeldVertices`, tolerance `1e-4`), logs
|
|
||||||
the counts, uploads via `SetMeshCpu`, and reframes the camera to the bbox radius.
|
|
||||||
Despite the file name, `mesh/MeshDecimator.h` implements **only** spatial-hash
|
|
||||||
vertex welding (collapse near-duplicates, drop degenerate triangles, recompute
|
|
||||||
bounds) — there is no LOD/decimation.
|
|
||||||
|
|
||||||
### Non-obvious invariants (read before editing)
|
|
||||||
|
|
||||||
- **All Vulkan lives in `src/vk/`.** `core/`, `mesh/`, `editor/` and `app/` make no
|
|
||||||
Vulkan API calls. `vk::Renderer`'s public header is deliberately Vulkan-free
|
|
||||||
(pImpl + glm/`RenderMode` only) so `App` can own it without pulling in volk. Keep
|
|
||||||
it that way — do not leak `Vk*` types into the public interfaces of those modules.
|
|
||||||
- **Single render pass with depth.** Scene (grid + mesh) and ImGui draw into one
|
|
||||||
`vkCmdBeginRendering` pass that has both a colour and a `D32_SFLOAT` depth
|
|
||||||
attachment. The ImGui backend is initialised with `depthAttachmentFormat` set so
|
|
||||||
its pipeline matches the pass; the depth buffer is recreated with the swapchain.
|
|
||||||
- **Wireframe needs `fillModeNonSolid`.** `MeshRenderer` builds a fill pipeline and
|
|
||||||
a `VK_POLYGON_MODE_LINE` pipeline; the line pipeline uses a small depth bias so
|
|
||||||
the overlay sits on top of the fill. The device feature is requested in `Context`.
|
|
||||||
Culling is off (`VK_CULL_MODE_NONE`) — loaded models may have mixed winding.
|
|
||||||
- **Camera is fixed on the origin.** `editor::Camera` orbits (yaw/pitch/distance)
|
|
||||||
the world origin; loaded models are recentred there via a translate-only model
|
|
||||||
matrix. Projection uses Vulkan clip space (`GLM_FORCE_DEPTH_ZERO_TO_ONE` + Y flip,
|
|
||||||
isolated to `Camera.cpp`).
|
|
||||||
- **Buffers.** `vk::GpuMesh` owns the model's vertex/index buffers (staged upload on
|
|
||||||
the transfer queue). `GridRenderer` builds a static host-visible line buffer once.
|
|
||||||
Both scene renderers use only a push constant — no descriptor sets.
|
|
||||||
- **Push-constant layout is a cross-file contract.** `MeshRenderer.cpp`'s anonymous
|
|
||||||
`MeshPC { mat4 mvp; vec4 color; }` must stay byte-identical to the
|
|
||||||
`push_constant` block in `mesh.vert`/`mesh.frag`; `color.a` is a *flag*, not
|
|
||||||
alpha (1 = flat-shaded, 0 = constant colour for wireframe). `GridRenderer` pushes
|
|
||||||
a bare `mat4` (vertex stage only). Change either side and you must change both.
|
|
||||||
- **Swapchain recreation rebuilds sync objects.** `RecreateSwapDependent` recreates
|
|
||||||
the per-frame `imageAvailable` semaphores (a failed acquire can leave one
|
|
||||||
signalled) *and* the per-swapchain-image `renderFinished` semaphores (the image
|
|
||||||
count may change), then re-ensures both pipelines. Keep that ordering if you
|
|
||||||
touch resize handling.
|
|
||||||
- **Mesh upload stalls the device.** `SetMeshCpu`/`ClearMesh` call `WaitIdle`
|
|
||||||
before touching `GpuMesh` — acceptable because loads are rare; do not copy that
|
|
||||||
pattern into per-frame paths.
|
|
||||||
|
|
||||||
### Shaders
|
|
||||||
|
|
||||||
GLSL under `shaders/editor/` (`mesh.{vert,frag}`, `grid.{vert,frag}`). `simv_shaders`
|
|
||||||
compiles each to `build/<preset>/spirv/editor/<name>.spv` targeting `vulkan1.3`,
|
|
||||||
with `shaders/` as the `-I` root (so `#include "common/foo.glsl"` would resolve).
|
|
||||||
Adding a shader under that globbed dir is picked up automatically
|
|
||||||
(`CONFIGURE_DEPENDS`). `mesh.frag` reconstructs a flat normal from screen-space
|
|
||||||
derivatives, so the vertex stream carries only positions (tight `float3`, one
|
|
||||||
binding, one attribute).
|
|
||||||
|
|
||||||
## Logging
|
|
||||||
|
|
||||||
`simv::core::Logger` (spdlog-backed, singleton) with category-based throttling.
|
|
||||||
Log via `LogFmt(LogCategory, LogLevel, fmt, args...)`. Categories: `Core`,
|
|
||||||
`Vulkan`, `MeshIO`, `UI`, `Test`. Per-category level, throttle interval and
|
|
||||||
on/off are settable at runtime (`SetMinLevel` / `SetThrottle` / `SetEnabled`).
|
|
||||||
Some low-level Vulkan code still calls `spdlog::` directly.
|
|
||||||
|
|
||||||
## Research prototype (`docs/theory/`)
|
|
||||||
|
|
||||||
Separate from the C++ application and from the CMake build: a Python **Lattice
|
|
||||||
Boltzmann (D2Q9, KBC-N1)** research codebase — cylinder flow with a ×2 nested AMR
|
|
||||||
patch and SDF+Bouzidi boundaries. **GPU/CuPy only, no CPU fallback.** Its
|
|
||||||
documentation and the running experiment log are in Russian.
|
|
||||||
|
|
||||||
- `docs/theory/solver_2x_sdf/` — the component-split solver (`run.py`,
|
|
||||||
`run_blockage.py`, `run_factors.py`); its `README.md` is the authoritative
|
|
||||||
status/experiment log and maps every file to the physics it owns.
|
|
||||||
- `docs/theory/demos_gpu/` — demo runs that produce the notebook's figures/GIFs;
|
|
||||||
they import physics from `solver_2x_sdf`, never duplicate it.
|
|
||||||
- `docs/theory/kbc_lbm.ipynb` — the write-up; it embeds pre-rendered artefacts and
|
|
||||||
does not execute the simulations.
|
|
||||||
- `docs/origins/` — source PDFs behind the theory.
|
|
||||||
|
|
||||||
Do not fold this into the CMake build, and do not treat it as dead code — it is
|
|
||||||
active research with a documented experiment history.
|
|
||||||
@@ -0,0 +1,466 @@
|
|||||||
|
# Геометрические ядра САПР: что доступно и что брать
|
||||||
|
|
||||||
|
Документ по задаче #5. Цель — выбрать геометрическое ядро, поверх которого строится
|
||||||
|
САПР-часть CFDManager, и обосновать выбор, а не объявить его.
|
||||||
|
|
||||||
|
Сейчас в дереве нет ничего, что можно было бы назвать ядром: `simv_mesh` читает `.obj`
|
||||||
|
через tinyobjloader, сваривает вершины и считает AABB. Ни B-rep, ни NURBS, ни булевых
|
||||||
|
операций, ни параметрических форматов. Всё это придётся взять снаружи — своё ядро
|
||||||
|
пишется годами и не является предметом этого проекта.
|
||||||
|
|
||||||
|
## Требования и как они проверялись
|
||||||
|
|
||||||
|
Пять требований заданы в задаче, шестое добавлено здесь, потому что без него выбор
|
||||||
|
некорректен.
|
||||||
|
|
||||||
|
| № | Требование | Как проверялось |
|
||||||
|
|---|---|---|
|
||||||
|
| 1 | Лёгкость и оптимизированность | сборка и запуск на этой машине, вес артефактов |
|
||||||
|
| 2 | Rust-стек или доступное для Rust API | наличие крейта/биндингов, собрал и запустил |
|
||||||
|
| 3 | Базовое редактирование 3D (аналог Plasticity) | наличие булевых, фасок, скруглений, выдавливания |
|
||||||
|
| 4 | NURBS | какие поверхности представимы, что уходит в STEP |
|
||||||
|
| 5 | Стандартные параметрические и полигональные форматы | STEP/IGES на запись **и чтение**, STL/OBJ/glTF |
|
||||||
|
| 6 | **Лицензия** | текст лицензии и то, что она требует при поставке продукта |
|
||||||
|
|
||||||
|
Шестое требование добавлено по следующей причине. Ядро — не библиотека утилит, его
|
||||||
|
нельзя заменить за неделю; лицензионное условие, замеченное после того, как на ядре
|
||||||
|
написан год работы, стоит переписывания продукта. Поэтому GPL-ядра и ядра
|
||||||
|
с некоммерческой лицензией разбираются здесь наравне с техникой, а не в сноске.
|
||||||
|
|
||||||
|
Измерения выполнены на машине разработки: Windows 11, MSVC 19.37.32825 (VS 2022
|
||||||
|
Community), CMake 4.3.2, Ninja 1.13.2, Rust 1.97.1, сборки release. Числа, взятые
|
||||||
|
из внешних источников, помечены явно.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Группа 1. Открытый исходный код
|
||||||
|
|
||||||
|
### Open CASCADE Technology (OCCT)
|
||||||
|
|
||||||
|
Единственное зрелое открытое B-rep/NURBS ядро промышленного класса. Версия **8.0.1**
|
||||||
|
от 30 июля 2026; семь модулей (Foundation Classes, Modeling Data, Modeling Algorithms,
|
||||||
|
Visualization, Data Exchange, Application Framework, Mesh).
|
||||||
|
|
||||||
|
**Форматы:** STEP (AP203, AP214, AP242), IGES до 5.3, STL, OBJ, glTF 2.0, VRML 1.0 —
|
||||||
|
на чтение и запись. ACIS, Parasolid, DXF, IFC, JT — **только через платные компоненты**
|
||||||
|
Open Cascade, в открытую поставку не входят.
|
||||||
|
|
||||||
|
**Лицензия — LGPL 2.1 с исключением, и исключение не то, за которое его обычно
|
||||||
|
принимают.** Полный текст `OCCT_LGPL_EXCEPTION.txt` разрешает ровно одно: включать
|
||||||
|
в объектный код материал из заголовочных файлов библиотеки (инлайн-функции, шаблоны,
|
||||||
|
CDL-классы) и распространять такой объектный код на своих условиях — при заметном
|
||||||
|
уведомлении, что продукт использует OCCT. Про линковку исключение не говорит **ничего**.
|
||||||
|
|
||||||
|
Практический вывод: динамическая линковка — штатный путь, обязанности сводятся
|
||||||
|
к уведомлению и к тому, что пользователь может подменить библиотеку. Статическая
|
||||||
|
линковка остаётся под LGPL 2.1 §6 и требует выдать объектные файлы приложения, чтобы
|
||||||
|
пользователь мог пересобрать его с изменённым OCCT. Это важно, потому что
|
||||||
|
`opencascade-rs` со своим режимом `builtin` собирает OCCT **статически**
|
||||||
|
(`BUILD_LIBRARY_TYPE=Static` в вызове CMake) — то есть самый удобный путь сборки
|
||||||
|
оказывается как раз тем, который для проприетарной поставки неудобен.
|
||||||
|
|
||||||
|
Динамический путь существует: без `builtin` обвязка ищет **предустановленную** OCCT
|
||||||
|
и, если та собрана с `BUILD_SHARED_LIBS=ON`, линкуется к ней как `dylib`
|
||||||
|
(`crates/opencascade-sys/build.rs:43`). Но там же, строкой ниже, стоит жёсткая
|
||||||
|
проверка версии: требуется мажор ровно **7** и минор не ниже 8, иначе сборка
|
||||||
|
паникует. Поставить в системе свежую OCCT 8.0.1 и слинковаться с ней **не получится** —
|
||||||
|
обвязка её отвергнет.
|
||||||
|
|
||||||
|
### opencascade-rs — путь к OCCT из Rust
|
||||||
|
|
||||||
|
Крейты `opencascade` / `opencascade-sys` версии 0.3.0 (обновлены 24 августа 2026),
|
||||||
|
биндинги через `cxx`, лицензия LGPL-2.1. Форк `bschwind` живой, 263 звезды; форк
|
||||||
|
`mkovaxx` последний раз трогали в октябре 2025 — его рассматривать не стоит.
|
||||||
|
|
||||||
|
API высокоуровневый и на вид приятный:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let my_box = Shape::box_with_dimensions(10.0, 10.0, 1.0);
|
||||||
|
let another = Shape::box_with_dimensions(1.0, 1.0, 0.8);
|
||||||
|
my_box.subtract(&another).chamfer(0.07)
|
||||||
|
```
|
||||||
|
|
||||||
|
Есть `write_step` / `write_iges` / `write_stl`, `read_step`, `mesh()`
|
||||||
|
с настраиваемым допуском, фаски, скругления, выдавливание, заметание по траектории.
|
||||||
|
|
||||||
|
**Существенное отставание:** `occt-sys` зафиксирован на OCCT **7.8.1**, тогда как
|
||||||
|
апстрим уже 8.0.1. Это не косметика — между ними мажорный релиз, и всё, что исправлено
|
||||||
|
в 7.9 и 8.0, в этом пути недоступно, пока обвязку не обновят. Причём 8.0 заявлен
|
||||||
|
командой OCCT3D (Capgemini Engineering) как релиз с ломающими изменениями API,
|
||||||
|
чисткой интерфейсов и переработкой булевых операций, тесселяции и обработки допусков —
|
||||||
|
то есть обновление обвязки будет не механическим, и ждать его быстро не стоит.
|
||||||
|
|
||||||
|
### truck — чистый Rust
|
||||||
|
|
||||||
|
Единственный живой чисто-растовый B-rep/NURBS кернел. Apache-2.0, 1559 звёзд,
|
||||||
|
коммиты в репозитории вплоть до 7 сентября 2026 — **но крейты на crates.io не
|
||||||
|
обновлялись с 20 сентября 2024** (`truck-modeling` 0.6.0). Два года git-активности
|
||||||
|
без релиза — характерный признак: пользоваться придётся либо старым релизом,
|
||||||
|
либо git-зависимостью.
|
||||||
|
|
||||||
|
Умеет: NURBS-геометрию, топологию (вершины/рёбра/грани/оболочки/тела), булевы операции
|
||||||
|
(`truck-shapeops`), тесселяцию (`truck-meshalgo`), чтение и запись STEP
|
||||||
|
(`truck-stepio`, есть и `in`, и `out`), полигональные форматы через `truck-polymesh`.
|
||||||
|
|
||||||
|
### Остальные открытые — отсев с причиной
|
||||||
|
|
||||||
|
| Проект | Состояние | Почему не подходит |
|
||||||
|
|---|---|---|
|
||||||
|
| **Fornjot** | архивирован 19 июня 2026 | Автор закрыл проект: «This project has been shut down. Its goals were never reached», код read-only, пригоден лишь для «very simple models» |
|
||||||
|
| **SolveSpace** | живой, 4163★, GPL-3.0 | GPLv3 несовместим с проприетарной поставкой; NURBS-булевы, по признанию самих авторов, медленные, ограничены поверхностями выдавливания и вращения и «not too robust even then» |
|
||||||
|
| **BRL-CAD** | живой, 1029★ | CSG-ориентированное ядро с Tcl-инфраструктурой, не про интерактивное NURBS-моделирование |
|
||||||
|
| **openNURBS** | живой, 562★ | Чтение/запись `.3dm` и NURBS-геометрия, **но моделирования нет**: ни булевых, ни фасок |
|
||||||
|
| **curvo** | живой, MIT, 0.1.91 | NURBS-кривые и поверхности как математика, не B-rep ядро |
|
||||||
|
| **Manifold** | живой, Apache-2.0, есть крейт `manifold3d` 0.4.1 (обновлён 9 сентября 2026) | Быстрые и надёжные булевы, но по **полигональным сеткам**; NURBS и B-rep отсутствуют как понятие. Полезен позже — для подготовки геометрии к расчётной сетке, не как ядро |
|
||||||
|
| **CGAL** | живой | Вычислительная геометрия, не САПР-ядро; ядро GPL с платной альтернативой |
|
||||||
|
|
||||||
|
Отдельно стоит отметить `libslvs` из SolveSpace: это **решатель геометрических
|
||||||
|
ограничений**, то есть ровно та подсистема, которой нет ни у OCCT, ни у truck.
|
||||||
|
К выбору ядра он отношения не имеет, но к параметрике — прямое (см. раздел о том,
|
||||||
|
что придётся писать самим).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Группа 2. Закрытый код, бесплатные и некоммерческие лицензии
|
||||||
|
|
||||||
|
Задача изначально говорила про открытые ядра; рамка расширена владельцем проекта.
|
||||||
|
Результат этой группы отрезвляющий: **бесплатного входа в промышленное ядро нет ни
|
||||||
|
у одного из четырёх.**
|
||||||
|
|
||||||
|
### C3D Modeler (C3D Labs)
|
||||||
|
|
||||||
|
Единственное коммерческое геометрическое ядро российской разработки, одно из пяти
|
||||||
|
лицензируемых ядер в мире. B-rep с NURBS, обмен со STEP, IGES, Parasolid, ACIS,
|
||||||
|
полигональными форматами. API — C++ с C-обёрткой; Rust-биндингов нет, их пришлось бы
|
||||||
|
писать самим.
|
||||||
|
|
||||||
|
Публично доступна **только 90-дневная пробная версия** с техподдержкой. Бесплатной
|
||||||
|
некоммерческой или академической лицензии в открытом доступе найти не удалось:
|
||||||
|
у компании есть образовательные партнёрства с вузами, хакатоны и стажировки, но
|
||||||
|
лицензия под них публично не описана и выдаётся по договорённости. Условия
|
||||||
|
коммерческой лицензии компания называет гибкими и определяет индивидуально; цифра
|
||||||
|
в 1 750 000 ₽ в год за лицензию разработчика плюс роялти встречается во вторичных
|
||||||
|
источниках и **здесь не проверена** — уточнять её нужно у самой C3D Labs.
|
||||||
|
|
||||||
|
### Parasolid (Siemens)
|
||||||
|
|
||||||
|
Ядро, на котором построена названная в задаче Plasticity, а также SolidWorks, NX и
|
||||||
|
десятки других систем. Эталон по надёжности булевых и качеству поверхностей.
|
||||||
|
|
||||||
|
Лицензируется коммерчески; публичных условий, прайса, бесплатного или академического
|
||||||
|
тарифа в открытом доступе нет — вход только через переговоры с Siemens, и по отзывам
|
||||||
|
разработчиков процесс и суммы для небольших проектов оказываются заградительными.
|
||||||
|
Rust-биндингов нет.
|
||||||
|
|
||||||
|
### 3D ACIS Modeler и CGM Modeler (Spatial / Dassault Systèmes)
|
||||||
|
|
||||||
|
Второе историческое коммерческое ядро (ACIS, с конца 1980-х) и ядро CATIA (CGM).
|
||||||
|
Технически полноценные B-rep/NURBS ядра. Ситуация с лицензией та же, что у Parasolid:
|
||||||
|
публичных бесплатных или академических условий нет, пробный доступ и академические
|
||||||
|
программы — по запросу. Rust-биндингов нет.
|
||||||
|
|
||||||
|
### Zoo Design API (бывш. KittyCAD)
|
||||||
|
|
||||||
|
Самый близкий к требованию 2 вариант: проприетарное ядро, **написанное на Rust**,
|
||||||
|
GPU-native, выдаёт настоящий B-rep. Есть официальный Rust-клиент `kittycad.rs`,
|
||||||
|
открытые Design Studio и язык KCL.
|
||||||
|
|
||||||
|
Но это **облачный сервис**, а не библиотека: геометрия считается на их серверах,
|
||||||
|
тарификация поминутная, бесплатно даётся 20 минут API (баланс $10), дальше
|
||||||
|
pay-as-you-go. Для настольного САПР с интерактивным редактированием это означает
|
||||||
|
зависимость от сети, задержку на каждую операцию и оплату за использование, которая
|
||||||
|
растёт вместе с числом пользователей. Для проекта, который сам по себе считает
|
||||||
|
гидродинамику локально, такая схема противоречива.
|
||||||
|
|
||||||
|
### Обмен форматами отдельно
|
||||||
|
|
||||||
|
CAD Exchanger SDK, HOOPS Exchange, ODA — не ядра моделирования, а конвертеры.
|
||||||
|
Они закрывают требование 5 там, где ядро не справляется (DWG, JT, CATIA, NX), и
|
||||||
|
имеют смысл как **дополнение** к выбранному ядру, если такие форматы понадобятся.
|
||||||
|
Все три коммерческие. Для OCCT, который сам читает STEP и IGES, необходимости
|
||||||
|
в них на текущем этапе нет.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Сводная таблица
|
||||||
|
|
||||||
|
Обозначения: ● полностью, ◐ частично, ○ нет.
|
||||||
|
|
||||||
|
| Кандидат | 1. Лёгкость | 2. Rust | 3. Редактирование | 4. NURBS | 5. Форматы | 6. Лицензия и её риск |
|
||||||
|
|---|---|---|---|---|---|---|
|
||||||
|
| **OCCT + opencascade-rs** | ○ 2,7 ГБ сборки, exe 19,4 МБ | ◐ биндинги есть, но на Windows не собираются без патчей | ● булевы, фаски, скругления | ● полноценно, аналитика сохраняется | ● STEP/IGES/STL/OBJ/glTF | LGPL-2.1+искл.: динамическая линковка — ок, статическая обязывает |
|
||||||
|
| **truck** | ● 98 с сборки, exe 1,6 МБ | ● родной, собрался с первой попытки | ◐ булевы есть (в 4–6 раз медленнее), набор беднее | ◐ есть, но аналитика уходит в B-сплайны | ◐ STEP r/w, полигоны; IGES нет | Apache-2.0 — риска нет |
|
||||||
|
| Fornjot | — | ● | ○ | ○ | ○ | архивирован |
|
||||||
|
| SolveSpace | ● | ○ | ◐ | ◐ слабые булевы | ◐ | **GPL-3.0 — стоп для проприетарного** |
|
||||||
|
| openNURBS | ● | ○ | ○ | ● | ◐ только 3dm | своя, свободная |
|
||||||
|
| Manifold | ● | ● крейт `manifold3d` | ◐ только сетки | ○ | ◐ | Apache-2.0 — риска нет |
|
||||||
|
| C3D Modeler | ◐ | ○ писать самим | ● | ● | ● | 90 дней, дальше платно |
|
||||||
|
| Parasolid | ◐ | ○ | ● эталон | ● | ● | платно, условий публично нет |
|
||||||
|
| ACIS / CGM | ◐ | ○ | ● | ● | ● | платно, условий публично нет |
|
||||||
|
| Zoo Design API | ● клиент лёгкий | ● родной Rust | ● | ● | ● | облако, поминутная оплата |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Измерения
|
||||||
|
|
||||||
|
Всё в этом разделе измерено здесь, а не переписано из чужих статей.
|
||||||
|
|
||||||
|
### truck
|
||||||
|
|
||||||
|
Зонд: два куба → булево объединение → тесселяция → экспорт OBJ и STEP. Написан
|
||||||
|
по документации и **скомпилировался с первой попытки без правок** — показатель того,
|
||||||
|
что API соответствует документации.
|
||||||
|
|
||||||
|
| Что | Значение |
|
||||||
|
|---|---|
|
||||||
|
| холодная сборка release | **98,6 с** |
|
||||||
|
| пакетов в графе зависимостей | 103 |
|
||||||
|
| исходники самих крейтов truck | 1,9 МБ, 233 файла, 10 крейтов |
|
||||||
|
| размер `target/` | 446,8 МБ |
|
||||||
|
| исполняемый файл зонда | 1,60 МБ |
|
||||||
|
| построение двух кубов | 116 мкс |
|
||||||
|
| булево объединение (плоскости) | 1,72 мс |
|
||||||
|
| тесселяция результата | 340 мкс, 60 вершин / 36 граней |
|
||||||
|
| экспорт STEP | 445 мкс, 17,6 КБ |
|
||||||
|
| экспорт OBJ | 637 мкс, 8,8 КБ |
|
||||||
|
|
||||||
|
Второй зонд — на кривых поверхностях: цилиндр (через вращательное заметание) против
|
||||||
|
куба, пересечение и объединение.
|
||||||
|
|
||||||
|
| Что | Значение |
|
||||||
|
|---|---|
|
||||||
|
| булево пересечение цилиндр ∩ куб | **32,6 мс** (в 19 раз дороже плоскостного) |
|
||||||
|
| булево объединение цилиндр ∪ куб | 29,5 мс |
|
||||||
|
| результат тесселируется | да, 84 вершины |
|
||||||
|
|
||||||
|
Третий зонд — чтение STEP обратно: файл, записанный первым зондом, разобран за
|
||||||
|
**11,1 мс**, из него восстановлены 1 оболочка, 12 граней и 18 плоскостей. То есть
|
||||||
|
круг «записал — прочитал» у truck замкнут. Мелочь, характеризующая зрелость API:
|
||||||
|
`Table::from_step` возвращает `Option`, а не `Result`, поэтому причина неудачного
|
||||||
|
разбора вызывающему не сообщается вовсе.
|
||||||
|
|
||||||
|
Обе булевы операции отработали — это лучше, чем можно было ожидать от ядра такой
|
||||||
|
зрелости. Но экспорт цилиндра в STEP показал существенное: файл содержит `B_SPLINE_SURFACE` и
|
||||||
|
`PLANE`, и **не содержит `CYLINDRICAL_SURFACE`**. То есть аналитические поверхности
|
||||||
|
truck не сохраняет, всё сводится к сплайнам. Файл валиден, но принимающая система
|
||||||
|
получает вместо цилиндра сплайновую аппроксимацию: хуже для распознавания элементов,
|
||||||
|
хуже для точности, тяжелее.
|
||||||
|
|
||||||
|
### OCCT через opencascade-rs
|
||||||
|
|
||||||
|
Сборка этого пути оказалась мерой сама по себе — она упёрлась в три препятствия
|
||||||
|
подряд, и каждое стоит знать заранее.
|
||||||
|
|
||||||
|
1. **CMake 4.x против OCCT 7.8.1.** `occt-sys` собирает OCCT 7.8.1, чей
|
||||||
|
`cmake_minimum_required` объявляет совместимость ниже 3.5. В CMake 4.3.2 такая
|
||||||
|
совместимость удалена, и конфигурация падает сразу:
|
||||||
|
«Compatibility with CMake < 3.5 has been removed from CMake». Обходится
|
||||||
|
переменной окружения `CMAKE_POLICY_VERSION_MINIMUM=3.5`. В OCCT 8.0.1 этой
|
||||||
|
проблемы нет — но обвязка на него не переведена.
|
||||||
|
2. **Генератор Visual Studio.** Крейт `cmake` по умолчанию берёт генератор
|
||||||
|
«Visual Studio 17 2022», который на этой машине не отработал: установлены
|
||||||
|
и Build Tools 2026, и VS 2022 Community. Лечится `CMAKE_GENERATOR=Ninja`
|
||||||
|
из среды `vcvars64.bat`.
|
||||||
|
3. **Длина пути Windows.** Дерево OCCT глубокое, и в каталоге с длинным путём
|
||||||
|
сборка предупреждает про `CMAKE_OBJECT_PATH_MAX` (187 символов при пределе 250)
|
||||||
|
и падает. Понадобился короткий путь вида `C:\occprobe`.
|
||||||
|
|
||||||
|
После того как все три обойдены, сборка идёт и **само ядро OCCT собирается успешно**.
|
||||||
|
А потом падает компиляция cxx-мостов — тремя однотипными ошибками в собственных
|
||||||
|
заголовках обвязки:
|
||||||
|
|
||||||
|
```
|
||||||
|
b_rep.hxx(16): error C2440: невозможно преобразовать
|
||||||
|
"opencascade::handle<Geom_Surface> *" в "std::unique_ptr<Handle_Geom_Surface, ...>"
|
||||||
|
```
|
||||||
|
|
||||||
|
| Что | Значение |
|
||||||
|
|---|---|
|
||||||
|
| время до провала | **36 мин 09 с** (01:59:43 → 02:35:52) |
|
||||||
|
| объём `target/` к этому моменту | ~1,0 ГБ |
|
||||||
|
| исходники OCCT (`occt-sys` 7.8.1) | 87 МБ, 14 984 файла; скачиваемый архив 13,7 МБ |
|
||||||
|
| ошибок компиляции | 3, все в `b_rep.hxx` |
|
||||||
|
| собралось ли ядро OCCT | **да** |
|
||||||
|
| собрались ли биндинги | **нет** |
|
||||||
|
|
||||||
|
Причина — коллизия имён: обвязка объявляет собственные типы `Handle_Geom_Surface`
|
||||||
|
и им подобные, которые под MSVC сталкиваются с одноимёнными типами самого OCCT.
|
||||||
|
Это **известная незакрытая проблема проекта**, а не особенность этой машины:
|
||||||
|
PR #196 «Handle name conflict to fix Windows build» был влит ещё в январе 2025,
|
||||||
|
PR #230 «Fix MSVC handle name collisions» от 19 августа 2026 — **закрыт, не влит**,
|
||||||
|
а PR #216 «Fix Windows build» открыт с октября 2025 и всё ещё не принят.
|
||||||
|
|
||||||
|
Проверка обхода: патч из непринятого PR #230 (переименование `Handle_*` →
|
||||||
|
`RustHandle_*`, 26 файлов) применяется к текущему master чисто — `git apply` проходит
|
||||||
|
без конфликтов. После него мосты компилируются, **но сборка падает уже на линковке**:
|
||||||
|
23 неразрешённых символа, среди них `InitializeAcl` и `InitializeSecurityDescriptor`,
|
||||||
|
то есть не подключены системные библиотеки Windows. Это ровно предмет второго,
|
||||||
|
до сих пор **открытого** PR #216 — и наложить его поверх первого не получается,
|
||||||
|
два непринятых патча конфликтуют между собой в `build.rs`. Понадобилось дописать
|
||||||
|
четыре строки вручную:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
println!("cargo:rustc-link-lib=dylib=advapi32");
|
||||||
|
println!("cargo:rustc-link-lib=dylib=gdi32");
|
||||||
|
println!("cargo:rustc-link-lib=dylib=shell32");
|
||||||
|
println!("cargo:rustc-link-lib=dylib=comdlg32");
|
||||||
|
```
|
||||||
|
|
||||||
|
**После этого всё собралось и заработало.** Итого, чтобы получить рабочий
|
||||||
|
OCCT-из-Rust на Windows, потребовались: три настройки окружения, один непринятый
|
||||||
|
PR и четыре строки своей правки.
|
||||||
|
|
||||||
|
| Что | Значение |
|
||||||
|
|---|---|
|
||||||
|
| холодная сборка до провала (без патчей) | 36 мин 09 с |
|
||||||
|
| пересборка после патчей (ядро уже собрано) | 1 мин 45 с |
|
||||||
|
| исходники OCCT (`occt-sys` 7.8.1) | 87 МБ, 14 984 файла; архив 13,7 МБ |
|
||||||
|
| размер `target/` | ~2,7 ГБ |
|
||||||
|
| исполняемый файл зонда | **19,4 МБ** (против 1,60 МБ у truck) |
|
||||||
|
|
||||||
|
Работа зонда: куб 10×10×10 минус куб 6×6×6, фаска 0,5, тесселяция, три экспорта
|
||||||
|
и чтение STEP обратно.
|
||||||
|
|
||||||
|
| Операция | Время |
|
||||||
|
|---|---|
|
||||||
|
| два примитива | 503 мкс |
|
||||||
|
| булево вычитание | 7,46 мс |
|
||||||
|
| фаска по всем рёбрам | 20,2 мс |
|
||||||
|
| тесселяция | 12,1 мс, 174 вершины / 86 треугольников |
|
||||||
|
| экспорт STEP | 25,4 мс, 110,8 КБ, 2378 сущностей |
|
||||||
|
| экспорт IGES | 14,6 мс, 87,3 КБ |
|
||||||
|
| экспорт STL | 15,2 мс, 22,4 КБ |
|
||||||
|
| чтение STEP обратно | 48,4 мс, те же 174 вершины |
|
||||||
|
| всего | 145 мс |
|
||||||
|
|
||||||
|
### Прямое сравнение на одной задаче
|
||||||
|
|
||||||
|
Чтобы сравнение было честным, обоим ядрам дана одна и та же работа: цилиндр
|
||||||
|
радиуса 2 высоты 4, пересечение и объединение с кубом 1,5, затем запись цилиндра
|
||||||
|
в STEP.
|
||||||
|
|
||||||
|
| | truck | OCCT |
|
||||||
|
|---|---|---|
|
||||||
|
| цилиндр ∩ куб | 32,6 мс | **7,2 мс** |
|
||||||
|
| цилиндр ∪ куб | 29,5 мс | **4,7 мс** |
|
||||||
|
| тесселяция результата | 84 вершины | 38 вершин |
|
||||||
|
| STEP цилиндра, размер | 13 281 байт | **5 652 байта** |
|
||||||
|
| боковая поверхность в STEP | 9 × `B_SPLINE_SURFACE` | **1 × `CYLINDRICAL_SURFACE`** |
|
||||||
|
|
||||||
|
Последняя строка — самая содержательная во всём документе. Там, где OCCT пишет
|
||||||
|
одну аналитическую цилиндрическую поверхность, truck пишет девять сплайновых
|
||||||
|
заплаток: файл вдвое толще, а принимающая система видит не цилиндр, а его
|
||||||
|
аппроксимацию. Булевы операции при этом у OCCT в 4–6 раз быстрее.
|
||||||
|
|
||||||
|
Мелкая, но заметная деталь эксплуатации: OCCT при записи STEP печатает в stdout
|
||||||
|
собственную статистику трансфера («Statistics on Transfer», рамки из звёздочек).
|
||||||
|
В библиотеке, встраиваемой в приложение с интерфейсом, этот вывод придётся глушить.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Рекомендация
|
||||||
|
|
||||||
|
**Основной вариант — Open CASCADE Technology, из Rust через `opencascade-rs`.**
|
||||||
|
|
||||||
|
Причины, по порядку веса:
|
||||||
|
|
||||||
|
1. **Это единственное открытое ядро, которое закрывает требования 3, 4 и 5
|
||||||
|
целиком.** Всё остальное открытое либо не умеет моделировать (openNURBS, curvo,
|
||||||
|
Manifold), либо умеет плохо и под GPL (SolveSpace), либо закрыто (Fornjot).
|
||||||
|
2. **Требование 2 выполняется без написания биндингов.** `opencascade-rs` даёт
|
||||||
|
готовый Rust-API с булевыми, фасками, тесселяцией и обменом форматами.
|
||||||
|
3. **Требование 5 закрывается из коробки:** STEP AP242 и IGES на чтение и запись —
|
||||||
|
это то, чем САПР обменивается с внешним миром, и ни truck, ни полигональные
|
||||||
|
библиотеки этого в таком объёме не дают.
|
||||||
|
4. Зрелость: ядро развивается с 1990-х; на нём построены FreeCAD (его B-rep/NURBS
|
||||||
|
слой — это OCCT), CadQuery и 3D-часть KiCad.
|
||||||
|
|
||||||
|
Цена решения, названная честно и **измеренная**: ядро тяжёлое (87 МБ исходников,
|
||||||
|
2,7 ГБ сборочного каталога, 19,4 МБ исполняемый файл против 1,6 МБ у truck), это
|
||||||
|
C++ с версией 7.8.1 в текущей обвязке, а сама обвязка **на Windows из коробки не
|
||||||
|
собирается**: понадобились три настройки окружения, патч из непринятого PR #230 и
|
||||||
|
четыре строки правки `build.rs`. Плюс лицензия, обязывающая к динамической линковке
|
||||||
|
при проприетарной поставке — а динамический путь требует системной OCCT ветки 7.x.
|
||||||
|
|
||||||
|
Это не аргумент против выбора, но это работа, которую придётся сделать до того, как
|
||||||
|
будет написана первая строка САПР-логики, и её лучше запланировать явно. Разумный
|
||||||
|
первый шаг после утверждения — **форк `opencascade-rs`** с наложенными исправлениями:
|
||||||
|
иначе каждый разработчик проекта будет проходить этот путь заново, а обновление
|
||||||
|
апстрима будет ломать сборку.
|
||||||
|
|
||||||
|
**Запасной вариант — truck**, и он не запасной «на всякий случай», а по конкретному
|
||||||
|
сценарию: если проект останется целиком на Rust и согласится на собственную
|
||||||
|
реализацию недостающего. truck легче, собирается за полторы минуты, не тянет C++
|
||||||
|
и не создаёт лицензионных обязанностей вовсе (Apache-2.0). Но за это придётся
|
||||||
|
заплатить отсутствием IGES, потерей аналитических поверхностей при экспорте,
|
||||||
|
более скудным набором операций и работой с крейтами двухлетней давности либо
|
||||||
|
git-зависимостью.
|
||||||
|
|
||||||
|
**Отдельно про требование 3.** В задаче эталоном названа Plasticity — она построена
|
||||||
|
на Parasolid, и это надо понимать буквально: уровня её прямого моделирования
|
||||||
|
(push/pull по грани с мгновенной перестройкой тела) ни одно открытое ядро само по
|
||||||
|
себе не даёт. OCCT предоставляет операции, из которых такой режим собирается, но
|
||||||
|
собирать его придётся самим.
|
||||||
|
|
||||||
|
**Про закрытые ядра.** Ни одно из четырёх не даёт бесплатного пути в продукт:
|
||||||
|
C3D — 90 дней, Parasolid и ACIS/CGM — только переговоры, Zoo — поминутная оплата
|
||||||
|
и облако. Возвращаться к ним осмысленно тогда, когда у проекта появится бюджет
|
||||||
|
и станет ясно, что именно в OCCT не устраивает: менять ядро на этапе прототипа
|
||||||
|
дороже, чем позже, когда требования известны точно.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Что придётся писать самим поверх любого ядра
|
||||||
|
|
||||||
|
Это не недостаток конкретного кандидата, а общее свойство: ядро даёт геометрию,
|
||||||
|
а не САПР.
|
||||||
|
|
||||||
|
- **Решатель геометрических ограничений.** Ни OCCT, ни truck его не содержат.
|
||||||
|
Без него нет параметрического эскиза. Кандидаты: `libslvs` из SolveSpace
|
||||||
|
(GPL-3.0 — то же лицензионное ограничение, что и у самого SolveSpace),
|
||||||
|
либо собственная реализация.
|
||||||
|
- **Дерево построения и история операций.** Ядро выполняет операции, но не помнит,
|
||||||
|
в каком порядке и от чего они зависели.
|
||||||
|
- **Топологические имена.** Устойчивая идентификация граней и рёбер между
|
||||||
|
перестроениями — классическая трудная задача, ядром не решается.
|
||||||
|
- **Связь с расчётной частью.** Для CFD нужна сетка, а не B-rep: понадобится
|
||||||
|
контролируемая тесселяция и, вероятно, воксельное или SDF-представление,
|
||||||
|
согласованное с решателем KBC.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Что нужно от владельца проекта
|
||||||
|
|
||||||
|
1. **Утвердить ядро** — критерий готовности задачи #5 сформулирован именно так.
|
||||||
|
2. **Решить вопрос линковки**, если продукт будет проприетарным: удобный путь
|
||||||
|
(`builtin`) даёт статическую сборку OCCT, а динамический требует предустановленной
|
||||||
|
в системе OCCT ветки 7.8/7.9 — версию 8.x обвязка отвергает. Выбор: принять
|
||||||
|
обязанности LGPL 2.1 §6, держать системную 7.x, либо править обвязку под себя.
|
||||||
|
3. **Согласиться на форк `opencascade-rs`** (или явно отказаться): без него сборка
|
||||||
|
на Windows воспроизводится только вручную, по шагам из раздела измерений.
|
||||||
|
4. Если решено смотреть на закрытые ядра всерьёз — **запросить условия** у C3D Labs
|
||||||
|
(90-дневная пробная плюс индивидуальные условия) и у Siemens по Parasolid.
|
||||||
|
Регистрация и подписание — действия владельца, агентом они не выполняются.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Источники
|
||||||
|
|
||||||
|
- [Open CASCADE Technology — обзор и форматы](https://occt3d.com/dev/doc/overview/html/index.html)
|
||||||
|
- [FOSDEM 2026: OCCT3D 8.0 — ломающие изменения и развитие ядра](https://archive.fosdem.org/2026/schedule/event/QQRAAF-occt3d-8-kernel-evolution/)
|
||||||
|
- [OCCT_LGPL_EXCEPTION.txt — текст исключения](https://raw.githubusercontent.com/Open-Cascade-SAS/OCCT/master/OCCT_LGPL_EXCEPTION.txt)
|
||||||
|
- [Релизы OCCT (8.0.1, 30.07.2026)](https://github.com/Open-Cascade-SAS/OCCT/releases)
|
||||||
|
- [opencascade-rs](https://github.com/bschwind/opencascade-rs)
|
||||||
|
- [opencascade-rs PR #230 «Fix MSVC handle name collisions» — закрыт, не влит](https://github.com/bschwind/opencascade-rs/pull/230)
|
||||||
|
- [opencascade-rs PR #216 «Fix Windows build: …missing system libraries» — открыт](https://github.com/bschwind/opencascade-rs/pull/216)
|
||||||
|
- [truck — shape processing kernel by Rust](https://github.com/ricosjp/truck)
|
||||||
|
- [Fornjot — архив, заявление о закрытии](https://github.com/hannobraun/fornjot)
|
||||||
|
- [SolveSpace — использование как библиотеки](https://solvespace.com/library.pl)
|
||||||
|
- [openNURBS](https://github.com/mcneel/opennurbs)
|
||||||
|
- [Manifold](https://github.com/elalish/manifold)
|
||||||
|
- [curvo](https://github.com/mattatz/curvo)
|
||||||
|
- [C3D Toolkit — лицензирование](https://c3dlabs.ru/products/c3d-toolkit/licensing/)
|
||||||
|
- [C3D Toolkit — тестовая версия](https://c3dlabs.ru/evaluation/)
|
||||||
|
- [Zoo Design API](https://zoo.dev/design-api)
|
||||||
|
- [Zoo — тарифы API](https://zoo.dev/api-pricing)
|
||||||
|
- [kittycad.rs — Rust-клиент Zoo](https://github.com/KittyCAD/kittycad.rs)
|
||||||
Reference in New Issue
Block a user