chore: import existing STP2GLB project
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
This commit is contained in:
parent
096812b7d2
commit
8218502ee8
18
.claude/settings.local.json
Normal file
18
.claude/settings.local.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"mcp__serena__activate_project",
|
||||
"mcp__serena__check_onboarding_performed",
|
||||
"mcp__serena__onboarding",
|
||||
"mcp__serena__list_dir",
|
||||
"mcp__serena__get_symbols_overview",
|
||||
"mcp__serena__find_symbol",
|
||||
"mcp__serena__write_memory",
|
||||
"mcp__serena__search_for_pattern",
|
||||
"mcp__context7__resolve-library-id",
|
||||
"mcp__context7__get-library-docs"
|
||||
],
|
||||
"deny": [],
|
||||
"ask": []
|
||||
}
|
||||
}
|
||||
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
# SCM syntax highlighting
|
||||
pixi.lock linguist-language=YAML linguist-generated=true
|
||||
104
.github/workflows/ci-build-and-release.yml
vendored
Normal file
104
.github/workflows/ci-build-and-release.yml
vendored
Normal file
@ -0,0 +1,104 @@
|
||||
name: ci-build-and-release
|
||||
|
||||
# bump 1
|
||||
on:
|
||||
workflow_dispatch:
|
||||
# push:
|
||||
# paths:
|
||||
# - 'src/**'
|
||||
# - 'cmake/**'
|
||||
# - '.github/workflows/ci-build-and-release.yml'
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
id-token: write
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ matrix.builds.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
builds: [
|
||||
{ name: win, os: windows-latest, exe_path: build/win-static/STP2GLB.exe },
|
||||
{ name: linux, os: ubuntu-latest, exe_path: build/linux-static/STP2GLB }
|
||||
]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: '1'
|
||||
|
||||
- uses: prefix-dev/setup-pixi@v0.8.1
|
||||
with:
|
||||
pixi-version: v0.39.2
|
||||
cache: true
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
pixi run -e static build && pixi run -e static install
|
||||
|
||||
- name: Upload binary as artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: STP2GLB-${{ matrix.builds.name }}
|
||||
path: ${{ matrix.builds.exe_path }}
|
||||
|
||||
create-release:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: '1'
|
||||
|
||||
- name: Get current date
|
||||
id: date
|
||||
run: echo "release_date=$(date +'%Y-%m-%d')" >> $GITHUB_ENV
|
||||
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.13'
|
||||
|
||||
- run: pip install toml
|
||||
|
||||
- name: Read toml using python
|
||||
shell: python
|
||||
run: |
|
||||
import toml
|
||||
import os
|
||||
|
||||
with open('pixi.toml') as f:
|
||||
data = toml.load(f)
|
||||
|
||||
version = data['project']['version']
|
||||
print(f"{version=}")
|
||||
|
||||
with open(os.environ['GITHUB_ENV'], 'a') as fh:
|
||||
print(f"VERSION={version}", file=fh)
|
||||
|
||||
- name: Download Windows binary
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: STP2GLB-win
|
||||
path: artifacts/windows
|
||||
|
||||
- name: Download Linux binary
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: STP2GLB-linux
|
||||
path: artifacts/linux
|
||||
|
||||
- name: Create GitHub release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
files: |
|
||||
artifacts/windows/STP2GLB.exe
|
||||
artifacts/linux/STP2GLB
|
||||
tag_name: ${{ env.VERSION }}
|
||||
name: Release ${{ env.VERSION }}
|
||||
body: |
|
||||
The ${{ env.VERSION }} release (${{ env.release_date }}) includes the following binaries:
|
||||
- Windows: `STP2GLB.exe`
|
||||
- Linux: `STP2GLB`
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
47
.github/workflows/ci-occt-static-build.yml
vendored
Normal file
47
.github/workflows/ci-occt-static-build.yml
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
name: ci-occt-static-build
|
||||
|
||||
# bump 1
|
||||
on:
|
||||
workflow_dispatch:
|
||||
# push:
|
||||
# paths:
|
||||
# - 'recipes/occt-static/**'
|
||||
# - '.github/workflows/ci-occt-static-build.yml'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [windows-latest, ubuntu-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: '1'
|
||||
|
||||
- uses: prefix-dev/setup-pixi@v0.8.1
|
||||
with:
|
||||
pixi-version: v0.40.2
|
||||
activate-environment: conda
|
||||
cache: true
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
pixi run -e conda build-occt
|
||||
|
||||
- name: Upload all packages
|
||||
shell: pixi run bash -e {0}
|
||||
run: |
|
||||
shopt -s nullglob
|
||||
EXIT_CODE=0
|
||||
for pkg in $(find output -type f \( -name "*.conda" -o -name "*.tar.bz2" \) ); do
|
||||
if ! rattler-build upload prefix -c adapy-tools "${pkg}"; then
|
||||
EXIT_CODE=1
|
||||
fi
|
||||
done
|
||||
exit $EXIT_CODE
|
||||
47
.github/workflows/ci-stepcode-static-build.yml
vendored
Normal file
47
.github/workflows/ci-stepcode-static-build.yml
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
name: ci-stepcode-static-build
|
||||
|
||||
# bump 1
|
||||
on:
|
||||
workflow_dispatch:
|
||||
# push:
|
||||
# paths:
|
||||
# - 'recipes/stepcode-static/**'
|
||||
# - '.github/workflows/ci-stepcode-static-build.yml'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [windows-latest, ubuntu-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: '1'
|
||||
|
||||
- uses: prefix-dev/setup-pixi@v0.8.1
|
||||
with:
|
||||
pixi-version: v0.40.2
|
||||
activate-environment: conda
|
||||
cache: true
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
pixi run -e conda build-stepcode
|
||||
|
||||
- name: Upload all packages
|
||||
shell: pixi run bash -e {0}
|
||||
run: |
|
||||
shopt -s nullglob
|
||||
EXIT_CODE=0
|
||||
for pkg in $(find output -type f \( -name "*.conda" -o -name "*.tar.bz2" \) ); do
|
||||
if ! rattler-build upload prefix -c adapy-tools "${pkg}"; then
|
||||
EXIT_CODE=1
|
||||
fi
|
||||
done
|
||||
exit $EXIT_CODE
|
||||
10
.gitignore
vendored
Normal file
10
.gitignore
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
build/
|
||||
.idea/
|
||||
cmake-build*/
|
||||
.env
|
||||
.env.json
|
||||
temp/
|
||||
.vs/
|
||||
# pixi environments
|
||||
.pixi/
|
||||
output/
|
||||
BIN
.serena/cache/cpp/document_symbols_cache_v23-06-25.pkl
vendored
Normal file
BIN
.serena/cache/cpp/document_symbols_cache_v23-06-25.pkl
vendored
Normal file
Binary file not shown.
46
.serena/memories/code_style_conventions.md
Normal file
46
.serena/memories/code_style_conventions.md
Normal file
@ -0,0 +1,46 @@
|
||||
# 代码风格和约定
|
||||
|
||||
## C++代码风格
|
||||
|
||||
### 命名约定
|
||||
- **变量名**: 小写+下划线 (snake_case)
|
||||
- `linear_deflection`, `debug_mode`, `max_geometry_num`
|
||||
- **函数名**: 小写+下划线 (snake_case)
|
||||
- `convert_stp_to_glb()`, `print_status()`, `process_parameters()`
|
||||
- **类名**: 大写驼峰 (PascalCase)
|
||||
- `GlobalConfig`, `BuildConfig`
|
||||
- **结构体成员**: 小写+下划线
|
||||
- `stpFile`, `glbFile`, `buildConfig`
|
||||
- **常量**: 大写+下划线
|
||||
- **宏定义**: 大写+下划线
|
||||
|
||||
### 代码组织
|
||||
- **头文件保护**: 使用 `#pragma once` 或传统的头文件保护
|
||||
- **包含顺序**:
|
||||
1. 系统头文件
|
||||
2. 第三方库头文件
|
||||
3. 项目头文件
|
||||
- **命名空间**: 避免使用 `using namespace std` 在头文件中
|
||||
|
||||
### 函数和类设计
|
||||
- **函数长度**: 保持函数简洁,主函数约60行
|
||||
- **错误处理**: 使用异常处理机制
|
||||
- **RAII原则**: 资源获取即初始化
|
||||
- **const正确性**: 适当使用const关键字
|
||||
|
||||
### 项目特定约定
|
||||
- **配置结构**: 使用结构体组织配置参数
|
||||
- **CLI参数**: 使用CLI11库进行命令行解析
|
||||
- **错误输出**: 使用 `std::cerr` 输出错误信息
|
||||
- **时间测量**: 使用 `std::chrono` 进行性能测量
|
||||
|
||||
### Windows特定
|
||||
- **宏定义**:
|
||||
- `WIN32_LEAN_AND_MEAN`
|
||||
- `NOMINMAX`
|
||||
- **运行时**: 静态构建使用 `/MT` 标志
|
||||
|
||||
### 文件组织
|
||||
- 头文件和实现文件分离
|
||||
- 每个模块有独立的目录结构
|
||||
- 测试文件单独组织在tests目录
|
||||
35
.serena/memories/codebase_structure.md
Normal file
35
.serena/memories/codebase_structure.md
Normal file
@ -0,0 +1,35 @@
|
||||
# 代码库结构
|
||||
|
||||
## 目录结构
|
||||
```
|
||||
Stp2Glb/
|
||||
├── src/ # 源代码目录
|
||||
│ ├── main.cpp # 主程序入口
|
||||
│ ├── config_utils.cpp # 配置处理工具
|
||||
│ ├── config_structs.h # 配置数据结构
|
||||
│ ├── cadit/ # CAD处理模块
|
||||
│ │ ├── occt/ # OpenCASCADE相关代码
|
||||
│ │ ├── ifc/ # IFC文件处理
|
||||
│ │ ├── stepcode/ # STEP文件解析
|
||||
│ │ └── tinygltf/ # GLTF/GLB输出
|
||||
│ └── geom/ # 几何数据结构
|
||||
├── tests/ # 测试配置
|
||||
├── cmake/ # CMake配置文件
|
||||
├── files/ # 测试文件
|
||||
├── recipes/ # Conda构建配方
|
||||
└── CMakeLists.txt # 主CMake配置
|
||||
```
|
||||
|
||||
## 核心模块
|
||||
- **main.cpp**: 程序入口,命令行解析和主流程控制
|
||||
- **cadit/occt/**: OpenCASCADE集成,STEP文件读取和几何处理
|
||||
- **cadit/occt/convert.cpp**: 主要转换逻辑
|
||||
- **cadit/occt/gltf_writer.cpp**: GLB文件写入
|
||||
- **cadit/occt/step_tree.cpp**: STEP文件树结构处理
|
||||
- **geom/**: 几何数据结构定义
|
||||
|
||||
## 关键文件
|
||||
- **CMakeLists.txt**: 主构建配置
|
||||
- **pixi.toml**: 依赖管理和任务定义
|
||||
- **CMakePresets.json**: CMake预设配置
|
||||
- **tests/tests.cmake**: 测试用例定义
|
||||
55
.serena/memories/development_guidelines.md
Normal file
55
.serena/memories/development_guidelines.md
Normal file
@ -0,0 +1,55 @@
|
||||
# 开发指南和最佳实践
|
||||
|
||||
## 开发原则
|
||||
1. **渐进迭代**: 小步快跑,每次提交保持稳定
|
||||
2. **稳定优先**: 不破坏现有功能
|
||||
3. **学习复用**: 研究现有代码模式,保持一致性
|
||||
4. **架构先行**: 设计后实现,确保可维护性
|
||||
|
||||
## 技术栈理解
|
||||
- **Core**: C++20, CMake, Pixi包管理
|
||||
- **CAD处理**: OpenCASCADE (OCCT) 7.8.1
|
||||
- **几何算法**: CGAL 5.6.1
|
||||
- **文件格式**: STEP输入, GLB输出
|
||||
- **并行处理**: TBB库
|
||||
- **JSON**: nlohmann_json
|
||||
- **CLI**: CLI11库
|
||||
|
||||
## 开发工作流
|
||||
1. **环境准备**: `pixi install` && `pixi shell`
|
||||
2. **理解需求**: 分析要实现的功能
|
||||
3. **研究现有代码**: 查看相似功能的实现
|
||||
4. **小步实现**: 增量开发,频繁测试
|
||||
5. **质量验证**: 编译、测试、手动验证
|
||||
6. **文档更新**: 必要时更新README和注释
|
||||
|
||||
## 架构约束
|
||||
- **不修改现有接口**: 除非绝对必要
|
||||
- **向后兼容**: 新功能不能破坏现有功能
|
||||
- **模块化**: 保持清晰的模块边界
|
||||
- **错误处理**: 使用C++异常机制
|
||||
|
||||
## 性能考虑
|
||||
- **大文件处理**: 注意内存使用
|
||||
- **并行处理**: 合理使用TBB
|
||||
- **几何计算**: 优化OCCT和CGAL的使用
|
||||
- **I/O操作**: 高效的文件读写
|
||||
|
||||
## 调试技巧
|
||||
- **使用调试模式**: `--debug` 参数获取详细信息
|
||||
- **分步测试**: 从小文件开始测试
|
||||
- **性能分析**: 使用计时器测量关键操作
|
||||
- **内存检查**: 注意OpenCASCADE对象的生命周期
|
||||
|
||||
## 常见陷阱
|
||||
- **路径处理**: Windows路径分隔符问题
|
||||
- **字符编码**: 文件名和路径的编码处理
|
||||
- **内存管理**: OCCT对象的智能指针使用
|
||||
- **线程安全**: 并行处理时的数据竞争
|
||||
|
||||
## 代码审查要点
|
||||
- 是否遵循现有命名约定
|
||||
- 错误处理是否完整
|
||||
- 是否有潜在的内存泄漏
|
||||
- 性能是否符合预期
|
||||
- 测试覆盖是否充分
|
||||
35
.serena/memories/project_overview.md
Normal file
35
.serena/memories/project_overview.md
Normal file
@ -0,0 +1,35 @@
|
||||
# STP2GLB 项目概览
|
||||
|
||||
## 项目目的
|
||||
STP2GLB 是一个STEP文件到GLB(glTF二进制)格式的转换器。它是一个命令行工具,能够将CAD格式的STEP文件转换为适用于Web和3D可视化的GLB格式。
|
||||
|
||||
## 主要功能
|
||||
- STEP文件到GLB格式转换
|
||||
- 支持线性和角度偏转参数调整
|
||||
- 调试模式,提供更详细的转换信息
|
||||
- 支持实体几何过滤
|
||||
- 支持名称过滤(包含/排除特定几何体)
|
||||
- 可配置的镶嵌超时设置
|
||||
- 支持最大几何体数量限制
|
||||
|
||||
## 技术架构
|
||||
- **语言**: C++20
|
||||
- **构建系统**: CMake 3.20+
|
||||
- **包管理**: Pixi (conda生态)
|
||||
- **编译器**: MSVC (Windows), GCC (Linux)
|
||||
- **平台**: Windows 64-bit, Linux 64-bit
|
||||
|
||||
## 主要依赖库
|
||||
- **OpenCASCADE (OCCT) 7.8.1**: CAD几何处理核心库
|
||||
- **CLI11**: 命令行参数解析
|
||||
- **nlohmann_json**: JSON处理
|
||||
- **tinygltf 2.8.19**: GLTF/GLB文件处理
|
||||
- **CGAL 5.6.1**: 计算几何算法库
|
||||
- **TBB**: 并行计算库
|
||||
- **IFCOpenShell**: IFC文件支持
|
||||
- **StepCode 0.8.2**: STEP文件解析
|
||||
|
||||
## 构建模式
|
||||
- **动态链接**: 用于开发和调试
|
||||
- **静态链接**: 用于发布部署
|
||||
- **调试模式**: 包含调试符号的动态构建
|
||||
81
.serena/memories/suggested_commands.md
Normal file
81
.serena/memories/suggested_commands.md
Normal file
@ -0,0 +1,81 @@
|
||||
# 建议的开发命令
|
||||
|
||||
## 环境设置
|
||||
```bash
|
||||
# 安装依赖环境
|
||||
pixi install
|
||||
|
||||
# 激活环境
|
||||
pixi shell
|
||||
```
|
||||
|
||||
## 构建命令
|
||||
|
||||
### 动态构建 (开发推荐)
|
||||
```bash
|
||||
# Windows构建
|
||||
pixi run build
|
||||
pixi run install
|
||||
|
||||
# 调试构建
|
||||
pixi run -e dynamic-debug build
|
||||
pixi run -e dynamic-debug install
|
||||
```
|
||||
|
||||
### 静态构建 (发布用)
|
||||
```bash
|
||||
# 静态构建
|
||||
pixi run -e static build
|
||||
pixi run -e static install
|
||||
```
|
||||
|
||||
## 测试命令
|
||||
```bash
|
||||
# 运行测试 (动态构建)
|
||||
pixi run test
|
||||
|
||||
# 运行测试 (调试构建)
|
||||
pixi run -e dynamic-debug test
|
||||
|
||||
# 手动运行CTest
|
||||
ctest --test-dir build/win-dynamic-release
|
||||
```
|
||||
|
||||
## 手动执行
|
||||
```bash
|
||||
# 基本转换
|
||||
STP2GLB.exe --stp input.stp --glb output.glb
|
||||
|
||||
# 调试模式转换
|
||||
STP2GLB.exe --stp input.stp --glb output.glb --debug
|
||||
|
||||
# 高级选项
|
||||
STP2GLB.exe --stp input.stp --glb output.glb --lin-defl 0.1 --ang-defl 0.5 --solid-only
|
||||
```
|
||||
|
||||
## Windows特定工具
|
||||
```bash
|
||||
# 查看文件
|
||||
dir
|
||||
type filename.txt
|
||||
|
||||
# 搜索文件
|
||||
findstr "pattern" *.cpp
|
||||
|
||||
# 文件操作
|
||||
copy source.txt dest.txt
|
||||
move source.txt dest.txt
|
||||
del filename.txt
|
||||
```
|
||||
|
||||
## 构建环境检查
|
||||
```bash
|
||||
# 检查Visual Studio工具
|
||||
vswhere -latest -property installationPath
|
||||
|
||||
# 检查CMake版本
|
||||
cmake --version
|
||||
|
||||
# 检查Ninja
|
||||
ninja --version
|
||||
```
|
||||
65
.serena/memories/task_completion_checklist.md
Normal file
65
.serena/memories/task_completion_checklist.md
Normal file
@ -0,0 +1,65 @@
|
||||
# 任务完成检查清单
|
||||
|
||||
## 代码完成后必须执行的步骤
|
||||
|
||||
### 1. 构建验证
|
||||
```bash
|
||||
# 确保代码能够成功编译
|
||||
pixi run build
|
||||
|
||||
# 检查是否有编译警告或错误
|
||||
# 所有警告都应该被解决
|
||||
```
|
||||
|
||||
### 2. 测试验证
|
||||
```bash
|
||||
# 运行所有测试用例
|
||||
pixi run test
|
||||
|
||||
# 确保所有测试通过
|
||||
# 新功能必须有相应的测试覆盖
|
||||
```
|
||||
|
||||
### 3. 代码质量检查
|
||||
- **无编译警告**: 代码应该零警告编译
|
||||
- **异常处理**: 适当的错误处理和异常捕获
|
||||
- **内存管理**: 无内存泄漏,遵循RAII原则
|
||||
- **const正确性**: 适当使用const关键字
|
||||
|
||||
### 4. 功能验证
|
||||
```bash
|
||||
# 手动测试核心功能
|
||||
STP2GLB.exe --stp test.stp --glb test.glb
|
||||
|
||||
# 验证输出文件的正确性
|
||||
# 检查GLB文件是否可以正常打开和渲染
|
||||
```
|
||||
|
||||
### 5. 文档更新
|
||||
- 如果修改了CLI参数,更新README.md
|
||||
- 如果添加了新功能,更新相关文档
|
||||
- 确保代码注释的准确性
|
||||
|
||||
### 6. 性能检查
|
||||
- 对于性能敏感的修改,运行性能测试
|
||||
- 确保没有引入明显的性能回归
|
||||
|
||||
### 7. Windows特定检查
|
||||
- 验证MSVC编译器兼容性
|
||||
- 检查静态链接构建是否正常
|
||||
- 确保路径分隔符正确处理
|
||||
|
||||
## 提交前检查
|
||||
- [ ] 代码编译成功
|
||||
- [ ] 所有测试通过
|
||||
- [ ] 无编译警告
|
||||
- [ ] 功能验证完成
|
||||
- [ ] 文档已更新
|
||||
- [ ] 代码风格一致
|
||||
|
||||
## 不允许的操作
|
||||
- ❌ 提交不能编译的代码
|
||||
- ❌ 跳过测试验证
|
||||
- ❌ 忽略编译警告
|
||||
- ❌ 破坏现有功能
|
||||
- ❌ 修改核心架构而不征得同意
|
||||
68
.serena/project.yml
Normal file
68
.serena/project.yml
Normal file
@ -0,0 +1,68 @@
|
||||
# language of the project (csharp, python, rust, java, typescript, go, cpp, or ruby)
|
||||
# * For C, use cpp
|
||||
# * For JavaScript, use typescript
|
||||
# Special requirements:
|
||||
# * csharp: Requires the presence of a .sln file in the project folder.
|
||||
language: cpp
|
||||
|
||||
# whether to use the project's gitignore file to ignore files
|
||||
# Added on 2025-04-07
|
||||
ignore_all_files_in_gitignore: true
|
||||
# list of additional paths to ignore
|
||||
# same syntax as gitignore, so you can use * and **
|
||||
# Was previously called `ignored_dirs`, please update your config if you are using that.
|
||||
# Added (renamed)on 2025-04-07
|
||||
ignored_paths: []
|
||||
|
||||
# whether the project is in read-only mode
|
||||
# If set to true, all editing tools will be disabled and attempts to use them will result in an error
|
||||
# Added on 2025-04-18
|
||||
read_only: false
|
||||
|
||||
|
||||
# list of tool names to exclude. We recommend not excluding any tools, see the readme for more details.
|
||||
# Below is the complete list of tools for convenience.
|
||||
# To make sure you have the latest list of tools, and to view their descriptions,
|
||||
# execute `uv run scripts/print_tool_overview.py`.
|
||||
#
|
||||
# * `activate_project`: Activates a project by name.
|
||||
# * `check_onboarding_performed`: Checks whether project onboarding was already performed.
|
||||
# * `create_text_file`: Creates/overwrites a file in the project directory.
|
||||
# * `delete_lines`: Deletes a range of lines within a file.
|
||||
# * `delete_memory`: Deletes a memory from Serena's project-specific memory store.
|
||||
# * `execute_shell_command`: Executes a shell command.
|
||||
# * `find_referencing_code_snippets`: Finds code snippets in which the symbol at the given location is referenced.
|
||||
# * `find_referencing_symbols`: Finds symbols that reference the symbol at the given location (optionally filtered by type).
|
||||
# * `find_symbol`: Performs a global (or local) search for symbols with/containing a given name/substring (optionally filtered by type).
|
||||
# * `get_current_config`: Prints the current configuration of the agent, including the active and available projects, tools, contexts, and modes.
|
||||
# * `get_symbols_overview`: Gets an overview of the top-level symbols defined in a given file.
|
||||
# * `initial_instructions`: Gets the initial instructions for the current project.
|
||||
# Should only be used in settings where the system prompt cannot be set,
|
||||
# e.g. in clients you have no control over, like Claude Desktop.
|
||||
# * `insert_after_symbol`: Inserts content after the end of the definition of a given symbol.
|
||||
# * `insert_at_line`: Inserts content at a given line in a file.
|
||||
# * `insert_before_symbol`: Inserts content before the beginning of the definition of a given symbol.
|
||||
# * `list_dir`: Lists files and directories in the given directory (optionally with recursion).
|
||||
# * `list_memories`: Lists memories in Serena's project-specific memory store.
|
||||
# * `onboarding`: Performs onboarding (identifying the project structure and essential tasks, e.g. for testing or building).
|
||||
# * `prepare_for_new_conversation`: Provides instructions for preparing for a new conversation (in order to continue with the necessary context).
|
||||
# * `read_file`: Reads a file within the project directory.
|
||||
# * `read_memory`: Reads the memory with the given name from Serena's project-specific memory store.
|
||||
# * `remove_project`: Removes a project from the Serena configuration.
|
||||
# * `replace_lines`: Replaces a range of lines within a file with new content.
|
||||
# * `replace_symbol_body`: Replaces the full definition of a symbol.
|
||||
# * `restart_language_server`: Restarts the language server, may be necessary when edits not through Serena happen.
|
||||
# * `search_for_pattern`: Performs a search for a pattern in the project.
|
||||
# * `summarize_changes`: Provides instructions for summarizing the changes made to the codebase.
|
||||
# * `switch_modes`: Activates modes by providing a list of their names
|
||||
# * `think_about_collected_information`: Thinking tool for pondering the completeness of collected information.
|
||||
# * `think_about_task_adherence`: Thinking tool for determining whether the agent is still on track with the current task.
|
||||
# * `think_about_whether_you_are_done`: Thinking tool for determining whether the task is truly completed.
|
||||
# * `write_memory`: Writes a named memory (for future reference) to Serena's project-specific memory store.
|
||||
excluded_tools: []
|
||||
|
||||
# initial prompt for the project. It will always be given to the LLM upon activating the project
|
||||
# (contrary to the memories, which are loaded on demand).
|
||||
initial_prompt: ""
|
||||
|
||||
project_name: "Stp2Glb"
|
||||
96
CLAUDE.md
Normal file
96
CLAUDE.md
Normal file
@ -0,0 +1,96 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Project Overview
|
||||
|
||||
STP2GLB is a C++20 command-line tool that converts STEP (CAD) files to GLB (glTF binary) format for 3D visualization. The core conversion pipeline uses OpenCASCADE Technology (OCCT) for CAD geometry processing and outputs to GLB via tinygltf.
|
||||
|
||||
## Build System and Commands
|
||||
|
||||
This project uses Pixi for package management and CMake with presets for building. All development should occur within Pixi environments.
|
||||
|
||||
### Essential Commands
|
||||
```bash
|
||||
# Setup environment
|
||||
pixi install
|
||||
pixi shell
|
||||
|
||||
# Development builds (with debugging symbols)
|
||||
pixi run build # Windows dynamic release
|
||||
pixi run install # Install after build
|
||||
pixi run -e dynamic-debug build # Debug build with symbols
|
||||
pixi run -e dynamic-debug install
|
||||
|
||||
# Testing
|
||||
pixi run test # Run all tests
|
||||
pixi run -e dynamic-debug test # Run tests with debug build
|
||||
ctest --test-dir build/win-dynamic-release # Direct CTest execution
|
||||
|
||||
# Production builds (static linking)
|
||||
pixi run -e static build
|
||||
pixi run -e static install
|
||||
```
|
||||
|
||||
### Manual Testing
|
||||
```bash
|
||||
# Basic conversion
|
||||
STP2GLB.exe --stp input.stp --glb output.glb
|
||||
|
||||
# Debug mode (provides detailed STEP entity failure information)
|
||||
STP2GLB.exe --stp input.stp --glb output.glb --debug
|
||||
|
||||
# Advanced options
|
||||
STP2GLB.exe --stp input.stp --glb output.glb --lin-defl 0.1 --ang-defl 0.5 --solid-only --max-geometry-num 100
|
||||
```
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
### Core Data Flow
|
||||
1. **CLI Parsing** (`main.cpp`) → **Configuration** (`config_structs.h`, `config_utils.cpp`)
|
||||
2. **STEP Reading** (`cadit/occt/step_helpers.cpp`) → **Geometry Processing** (`cadit/occt/convert.cpp`)
|
||||
3. **Tessellation & Filtering** → **GLB Output** (`cadit/occt/gltf_writer.cpp`)
|
||||
|
||||
### Key Modules
|
||||
- **`src/cadit/occt/`**: OpenCASCADE integration layer
|
||||
- `convert.cpp`: Main conversion pipeline (`convert_stp_to_glb()`, `debug_stp_to_glb()`)
|
||||
- `step_tree.cpp`: STEP file hierarchical processing
|
||||
- `gltf_writer.cpp`: GLB format output generation
|
||||
- `step_helpers.cpp`: STEP entity manipulation utilities
|
||||
- `helpers.cpp`: OCCT geometry utilities
|
||||
|
||||
- **`src/geom/`**: Abstract geometry data structures
|
||||
- Mesh, Color, and geometric primitives
|
||||
- Bridge between OCCT and output formats
|
||||
|
||||
### Build Configurations
|
||||
The project supports multiple build modes via CMake presets:
|
||||
- **Dynamic builds**: For development (faster builds, shared libraries)
|
||||
- **Static builds**: For distribution (single executable, slower builds)
|
||||
- **Debug vs Release**: Debug includes detailed STEP entity failure reporting
|
||||
|
||||
### Dependencies Architecture
|
||||
- **OpenCASCADE 7.8.1**: CAD kernel for STEP reading and geometry processing
|
||||
- **CGAL 5.6.1**: Computational geometry algorithms
|
||||
- **CLI11**: Command-line argument parsing
|
||||
- **tinygltf 2.8.19**: GLB/glTF file format handling
|
||||
- **nlohmann_json**: JSON processing for glTF structure
|
||||
|
||||
## Development Patterns
|
||||
|
||||
### Error Handling Strategy
|
||||
The codebase uses two conversion modes:
|
||||
- **Standard mode**: Fast conversion with basic error handling
|
||||
- **Debug mode** (`--debug` flag): Slower but provides detailed reporting of which STEP entities failed to convert and why
|
||||
|
||||
### Configuration Management
|
||||
All runtime parameters flow through `GlobalConfig` struct, populated via CLI11 from command-line arguments. This centralized configuration pattern should be maintained for new features.
|
||||
|
||||
### Testing Strategy
|
||||
Tests are defined in `tests/tests.cmake` and use actual STEP files from `files/` directory. New features should include corresponding test cases with real geometry files when possible.
|
||||
|
||||
### Code Style
|
||||
- Snake_case for functions and variables (`convert_stp_to_glb`, `linear_deflection`)
|
||||
- PascalCase for classes/structs (`GlobalConfig`, `BuildConfig`)
|
||||
- Use RAII and C++20 features appropriately
|
||||
- Prefer exceptions for error handling in conversion pipeline
|
||||
121
CMakePresets.json
Normal file
121
CMakePresets.json
Normal file
@ -0,0 +1,121 @@
|
||||
{
|
||||
"version": 6,
|
||||
"configurePresets": [
|
||||
{
|
||||
"name": "win-shared",
|
||||
"generator": "Ninja",
|
||||
"hidden": true,
|
||||
"cacheVariables": {
|
||||
"CLI11_DIR": "$env{LIBRARY_PREFIX}/include",
|
||||
"CMAKE_PREFIX_PATH": "$env{LIBRARY_PREFIX};$env{LIBRARY_PREFIX}/include;$env{LIBRARY_PREFIX}/lib;$env{LIBRARY_PREFIX}/bin",
|
||||
"CMAKE_INSTALL_PREFIX": "$env{LIBRARY_PREFIX}",
|
||||
"OpenCASCADE_DIR": "$env{LIBRARY_PREFIX}/lib/cmake/opencascade",
|
||||
"OpenCASCADE_INCLUDE_DIR": "$env{LIBRARY_PREFIX}/include/opencascade",
|
||||
"CONDA_LOCAL_DEV": "ON"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "linux-shared",
|
||||
"generator": "Ninja",
|
||||
"hidden": true,
|
||||
"cacheVariables": {
|
||||
"CLI11_DIR": "$env{PREFIX}/include",
|
||||
"CMAKE_PREFIX_PATH": "$env{PREFIX}:$env{PREFIX}/include:$env{PREFIX}/lib:$env{PREFIX}/bin",
|
||||
"CMAKE_INSTALL_PREFIX": "$env{PREFIX}",
|
||||
"OpenCASCADE_DIR": "$env{PREFIX}/lib/cmake/opencascade",
|
||||
"OpenCASCADE_INCLUDE_DIR": "$env{PREFIX}/include/opencascade"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "win-dynamic-debug",
|
||||
"inherits": "win-shared",
|
||||
"hidden": false,
|
||||
"binaryDir": "${sourceDir}/build/${presetName}",
|
||||
"environment": {
|
||||
"PREFIX": "${sourceDir}/.pixi/envs/dynamic-debug",
|
||||
"LIBRARY_PREFIX": "${sourceDir}/.pixi/envs/dynamic-debug/Library"
|
||||
},
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Debug",
|
||||
"BUILD_TESTING": "ON"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "win-dynamic-release",
|
||||
"hidden": false,
|
||||
"inherits": "win-shared",
|
||||
"binaryDir": "${sourceDir}/build/${presetName}",
|
||||
"environment": {
|
||||
"PREFIX": "${sourceDir}/.pixi/envs/dynamic",
|
||||
"LIBRARY_PREFIX": "${sourceDir}/.pixi/envs/dynamic/Library"
|
||||
},
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Release",
|
||||
"BUILD_TESTING": "ON",
|
||||
"CGAL_DIR": "$env{LIBRARY_PREFIX}/lib/cmake/CGAL"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "win-static",
|
||||
"inherits": "win-shared",
|
||||
"hidden": false,
|
||||
"binaryDir": "${sourceDir}/build/${presetName}",
|
||||
"environment": {
|
||||
"PREFIX": "${sourceDir}/.pixi/envs/static",
|
||||
"LIBRARY_PREFIX": "${sourceDir}/.pixi/envs/static/Library"
|
||||
},
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Release",
|
||||
"BUILD_TESTING": "OFF",
|
||||
"BUILD_STATIC": "ON",
|
||||
"BUILD_SHARED_LIBS": "OFF",
|
||||
"CONDA_LOCAL_DEV": "OFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "win-static-mingw",
|
||||
"inherits": "win-shared",
|
||||
"binaryDir": "${sourceDir}/build/${presetName}",
|
||||
"environment": {
|
||||
"PREFIX": "${sourceDir}/.pixi/envs/static-mingw",
|
||||
"LIBRARY_PREFIX": "${sourceDir}/.pixi/envs/static-mingw/Library",
|
||||
"CXX": "x86_64-w64-mingw32-g++"
|
||||
},
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Release",
|
||||
"BUILD_TESTING": "OFF",
|
||||
"BUILD_STATIC": "ON",
|
||||
"BUILD_SHARED_LIBS": "OFF",
|
||||
"CONDA_LOCAL_DEV": "OFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "linux-static",
|
||||
"binaryDir": "${sourceDir}/build/${presetName}",
|
||||
"environment": {
|
||||
"PREFIX": "${sourceDir}/.pixi/envs/static"
|
||||
},
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Release",
|
||||
"CMAKE_EXE_LINKER_FLAGS": "-std=c++11 -fuse-ld=lld -lpthread -ldl -lm",
|
||||
"BUILD_TESTING": "OFF",
|
||||
"BUILD_STATIC": "ON",
|
||||
"CONDA_LOCAL_DEV": "ON"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "linux-dynamic",
|
||||
"binaryDir": "${sourceDir}/build/${presetName}",
|
||||
"inherits": "linux-shared",
|
||||
"environment": {
|
||||
"PREFIX": "${sourceDir}/.pixi/envs/dynamic"
|
||||
},
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Debug",
|
||||
"BUILD_TESTING": "ON",
|
||||
"BUILD_STATIC": "OFF",
|
||||
"CONDA_LOCAL_DEV": "ON"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
60
README.md
Normal file
60
README.md
Normal file
@ -0,0 +1,60 @@
|
||||
# Stp2Glb
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
STEP to GLB converter
|
||||
Usage: STP2GLB.exe [OPTIONS]
|
||||
|
||||
Options:
|
||||
-h,--help Print this help message and exit
|
||||
--stp REQUIRED STEP filepath
|
||||
--glb REQUIRED GLB filepath
|
||||
--lin-defl :FLOAT in [0 - 1] [0.1]
|
||||
Linear deflection
|
||||
--ang-defl :FLOAT in [0 - 1] [0.5]
|
||||
Angular deflection
|
||||
--rel-defl Relative deflection
|
||||
--debug Debug mode. More robust but slower
|
||||
--solid-only Solid only
|
||||
--max-geometry-num [0] Maximum number of geometries to convert
|
||||
--filter-names-include Include Filter name. Command separated list
|
||||
--filter-names-file-include Include Filter name file
|
||||
--filter-names-exclude Exclude Filter name. Command separated list
|
||||
--filter-names-file-exclude Exclude Filter name file
|
||||
--tessellation-timeout [30]
|
||||
Tessellation timeout
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Development
|
||||
|
||||
### Install Pre-requisites
|
||||
|
||||
The pre-requisites build requirements are conda packages handled by using [pixi](https://pixi.sh).
|
||||
|
||||
Note that on windows the MSVC c++ compiler toolchain is used.
|
||||
So a pre-requisite on windows is that you have installed VS or VS build tools from https://visualstudio.microsoft.com/downloads/?q=build+tools
|
||||
|
||||
### Building
|
||||
|
||||
To build the STP2GLB executable using shared dependencies, run the following command:
|
||||
```bash
|
||||
pixi run build && pixi run install
|
||||
```
|
||||
|
||||
To build the STP2GLB executable using static dependencies, run the following command:
|
||||
```bash
|
||||
pixi run -e static build && pixi run -e static install
|
||||
```
|
||||
|
||||
|
||||
### Local IDE development
|
||||
|
||||
You can use the presets in the CMakePresets.json file (which points to the pixi environment). Just make sure you've
|
||||
either installed the pixi environment using `pixi install` or have run any of the build commands above.
|
||||
|
||||
|
||||
## Performance metrics
|
||||
Todo
|
||||
BIN
STP2GLB.exe
Normal file
BIN
STP2GLB.exe
Normal file
Binary file not shown.
2
aa-log.json
Normal file
2
aa-log.json
Normal file
@ -0,0 +1,2 @@
|
||||
[
|
||||
]
|
||||
12
cmake/deps_cgal.cmake
Normal file
12
cmake/deps_cgal.cmake
Normal file
@ -0,0 +1,12 @@
|
||||
# Find CGAL
|
||||
find_package(CGAL REQUIRED)
|
||||
|
||||
if (CGAL_FOUND)
|
||||
message(STATUS "CGAL version found: " ${CGAL_VERSION})
|
||||
message(STATUS "CGAL include directory: " ${CGAL_INCLUDE_DIRS})
|
||||
message(STATUS "CGAL binary directory: " ${CGAL_DIR})
|
||||
message(STATUS "CGAL library directory: " ${CGAL_LIBRARY_DIRS})
|
||||
|
||||
include_directories(${CGAL_INCLUDE_DIRS})
|
||||
link_directories(${CGAL_LIBRARY_DIRS})
|
||||
endif (CGAL_FOUND)
|
||||
8
cmake/deps_ifc.cmake
Normal file
8
cmake/deps_ifc.cmake
Normal file
@ -0,0 +1,8 @@
|
||||
# link the library files located in %LIBRARY_PREFIX%/lib/ifcparse/IfcParse.lib (on windows as an example)
|
||||
# to the executable
|
||||
|
||||
list(APPEND
|
||||
ADA_CPP_LINK_LIBS
|
||||
IfcParse
|
||||
IfcGeom
|
||||
)
|
||||
17
cmake/deps_occ.cmake
Normal file
17
cmake/deps_occ.cmake
Normal file
@ -0,0 +1,17 @@
|
||||
# Find OpenCASCADE
|
||||
find_package(OpenCASCADE REQUIRED)
|
||||
if (OpenCASCADE_FOUND)
|
||||
message(STATUS "OpenCASCADE version found: " ${OpenCASCADE_MAJOR_VERSION} ".." ${OpenCASCADE_MINOR_VERSION} ".." ${OpenCASCADE_MAINTENANCE_VERSION})
|
||||
message(STATUS "OpenCASCADE include directory: " ${OpenCASCADE_INCLUDE_DIR})
|
||||
message(STATUS "OpenCASCADE binary directory: " ${OpenCASCADE_BINARY_DIR})
|
||||
message(STATUS "OpenCASCADE library directory: " ${OpenCASCADE_LIBRARY_DIR})
|
||||
|
||||
include_directories(${OpenCASCADE_INCLUDE_DIR})
|
||||
link_directories(${OpenCASCADE_LIBRARY_DIR})
|
||||
# Order of linking is important
|
||||
# https://dev.opencascade.org/node/71506#comment-847
|
||||
list(APPEND
|
||||
ADA_CPP_LINK_LIBS
|
||||
${OpenCASCADE_LIBRARIES}
|
||||
)
|
||||
endif (OpenCASCADE_FOUND)
|
||||
10
cmake/deps_stepcode.cmake
Normal file
10
cmake/deps_stepcode.cmake
Normal file
@ -0,0 +1,10 @@
|
||||
|
||||
include_directories(${STEPCODE_INCLUDE_DIR})
|
||||
list(APPEND
|
||||
ADA_CPP_LINK_LIBS
|
||||
steplazyfile
|
||||
stepdai
|
||||
stepcore
|
||||
stepeditor
|
||||
steputils
|
||||
)
|
||||
6
cmake/pre_checks.cmake
Normal file
6
cmake/pre_checks.cmake
Normal file
@ -0,0 +1,6 @@
|
||||
if (NOT CMAKE_BUILD_TYPE)
|
||||
message(STATUS "Build type not set, defaulting to Release")
|
||||
set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE)
|
||||
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
|
||||
endif ()
|
||||
message(STATUS "Build type: " ${CMAKE_BUILD_TYPE})
|
||||
BIN
dragon_drc.glb
Normal file
BIN
dragon_drc.glb
Normal file
Binary file not shown.
1409
files/as1-oc-214-mini.stp
Normal file
1409
files/as1-oc-214-mini.stp
Normal file
File diff suppressed because it is too large
Load Diff
8378
files/as1-oc-214.stp
Normal file
8378
files/as1-oc-214.stp
Normal file
File diff suppressed because it is too large
Load Diff
286
files/flat_plate_abaqus_10x10_m_wColors.stp
Normal file
286
files/flat_plate_abaqus_10x10_m_wColors.stp
Normal file
@ -0,0 +1,286 @@
|
||||
ISO-10303-21;
|
||||
HEADER;
|
||||
FILE_DESCRIPTION(('FreeCAD Model'),'2;1');
|
||||
FILE_NAME('Open CASCADE Shape Model','2023-04-13T14:35:14',(''),(''),
|
||||
'Open CASCADE STEP processor 7.6','FreeCAD','Unknown');
|
||||
FILE_SCHEMA(('AUTOMOTIVE_DESIGN { 1 0 10303 214 1 1 1 1 }'));
|
||||
ENDSEC;
|
||||
DATA;
|
||||
#1 = APPLICATION_PROTOCOL_DEFINITION('international standard',
|
||||
'automotive_design',2000,#2);
|
||||
#2 = APPLICATION_CONTEXT(
|
||||
'core data for automotive mechanical design processes');
|
||||
#3 = SHAPE_DEFINITION_REPRESENTATION(#4,#10);
|
||||
#4 = PRODUCT_DEFINITION_SHAPE('','',#5);
|
||||
#5 = PRODUCT_DEFINITION('design','',#6,#9);
|
||||
#6 = PRODUCT_DEFINITION_FORMATION('','',#7);
|
||||
#7 = PRODUCT('Unnamed2','Unnamed2','',(#8));
|
||||
#8 = PRODUCT_CONTEXT('',#2,'mechanical');
|
||||
#9 = PRODUCT_DEFINITION_CONTEXT('part definition',#2,'design');
|
||||
#10 = SHAPE_REPRESENTATION('',(#11,#15,#19),#23);
|
||||
#11 = AXIS2_PLACEMENT_3D('',#12,#13,#14);
|
||||
#12 = CARTESIAN_POINT('',(0.,0.,0.));
|
||||
#13 = DIRECTION('',(0.,0.,1.));
|
||||
#14 = DIRECTION('',(1.,0.,-0.));
|
||||
#15 = AXIS2_PLACEMENT_3D('',#16,#17,#18);
|
||||
#16 = CARTESIAN_POINT('',(0.,0.,4.504));
|
||||
#17 = DIRECTION('',(0.,0.,1.));
|
||||
#18 = DIRECTION('',(1.,0.,0.));
|
||||
#19 = AXIS2_PLACEMENT_3D('',#20,#21,#22);
|
||||
#20 = CARTESIAN_POINT('',(0.,0.,0.));
|
||||
#21 = DIRECTION('',(0.,0.,1.));
|
||||
#22 = DIRECTION('',(1.,0.,0.));
|
||||
#23 = ( GEOMETRIC_REPRESENTATION_CONTEXT(3)
|
||||
GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#27)) GLOBAL_UNIT_ASSIGNED_CONTEXT(
|
||||
(#24,#25,#26)) REPRESENTATION_CONTEXT('Context #1',
|
||||
'3D Context with UNIT and UNCERTAINTY') );
|
||||
#24 = ( LENGTH_UNIT() NAMED_UNIT(*) SI_UNIT($,.METRE.) );
|
||||
#25 = ( NAMED_UNIT(*) PLANE_ANGLE_UNIT() SI_UNIT($,.RADIAN.) );
|
||||
#26 = ( NAMED_UNIT(*) SI_UNIT($,.STERADIAN.) SOLID_ANGLE_UNIT() );
|
||||
#27 = UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(1.E-10),#24,
|
||||
'distance_accuracy_value','confusion accuracy');
|
||||
#28 = PRODUCT_RELATED_PRODUCT_CATEGORY('part',$,(#7));
|
||||
#29 = SHAPE_DEFINITION_REPRESENTATION(#30,#36);
|
||||
#30 = PRODUCT_DEFINITION_SHAPE('','',#31);
|
||||
#31 = PRODUCT_DEFINITION('design','',#32,#35);
|
||||
#32 = PRODUCT_DEFINITION_FORMATION('','',#33);
|
||||
#33 = PRODUCT('red_plate','red_plate','',(#34));
|
||||
#34 = PRODUCT_CONTEXT('',#2,'mechanical');
|
||||
#35 = PRODUCT_DEFINITION_CONTEXT('part definition',#2,'design');
|
||||
#36 = MANIFOLD_SURFACE_SHAPE_REPRESENTATION('',(#11,#37),#111);
|
||||
#37 = SHELL_BASED_SURFACE_MODEL('',(#38));
|
||||
#38 = OPEN_SHELL('',(#39));
|
||||
#39 = ADVANCED_FACE('',(#40),#54,.T.);
|
||||
#40 = FACE_BOUND('',#41,.T.);
|
||||
#41 = EDGE_LOOP('',(#42,#65,#81,#97));
|
||||
#42 = ORIENTED_EDGE('',*,*,#43,.T.);
|
||||
#43 = EDGE_CURVE('',#44,#46,#48,.T.);
|
||||
#44 = VERTEX_POINT('',#45);
|
||||
#45 = CARTESIAN_POINT('',(10.,0.,0.));
|
||||
#46 = VERTEX_POINT('',#47);
|
||||
#47 = CARTESIAN_POINT('',(10.,10.,0.));
|
||||
#48 = SURFACE_CURVE('',#49,(#53),.PCURVE_S1.);
|
||||
#49 = LINE('',#50,#51);
|
||||
#50 = CARTESIAN_POINT('',(10.,0.,0.));
|
||||
#51 = VECTOR('',#52,1.E-03);
|
||||
#52 = DIRECTION('',(0.,1.,0.));
|
||||
#53 = PCURVE('',#54,#59);
|
||||
#54 = PLANE('',#55);
|
||||
#55 = AXIS2_PLACEMENT_3D('',#56,#57,#58);
|
||||
#56 = CARTESIAN_POINT('',(5.,5.,0.));
|
||||
#57 = DIRECTION('',(0.,0.,1.));
|
||||
#58 = DIRECTION('',(1.,0.,-0.));
|
||||
#59 = DEFINITIONAL_REPRESENTATION('',(#60),#64);
|
||||
#60 = LINE('',#61,#62);
|
||||
#61 = CARTESIAN_POINT('',(5.,-5.));
|
||||
#62 = VECTOR('',#63,1.);
|
||||
#63 = DIRECTION('',(0.,1.));
|
||||
#64 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2)
|
||||
PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE',''
|
||||
) );
|
||||
#65 = ORIENTED_EDGE('',*,*,#66,.T.);
|
||||
#66 = EDGE_CURVE('',#46,#67,#69,.T.);
|
||||
#67 = VERTEX_POINT('',#68);
|
||||
#68 = CARTESIAN_POINT('',(0.,10.,0.));
|
||||
#69 = SURFACE_CURVE('',#70,(#74),.PCURVE_S1.);
|
||||
#70 = LINE('',#71,#72);
|
||||
#71 = CARTESIAN_POINT('',(10.,10.,0.));
|
||||
#72 = VECTOR('',#73,1.E-03);
|
||||
#73 = DIRECTION('',(-1.,0.,0.));
|
||||
#74 = PCURVE('',#54,#75);
|
||||
#75 = DEFINITIONAL_REPRESENTATION('',(#76),#80);
|
||||
#76 = LINE('',#77,#78);
|
||||
#77 = CARTESIAN_POINT('',(5.,5.));
|
||||
#78 = VECTOR('',#79,1.);
|
||||
#79 = DIRECTION('',(-1.,0.));
|
||||
#80 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2)
|
||||
PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE',''
|
||||
) );
|
||||
#81 = ORIENTED_EDGE('',*,*,#82,.T.);
|
||||
#82 = EDGE_CURVE('',#67,#83,#85,.T.);
|
||||
#83 = VERTEX_POINT('',#84);
|
||||
#84 = CARTESIAN_POINT('',(0.,0.,0.));
|
||||
#85 = SURFACE_CURVE('',#86,(#90),.PCURVE_S1.);
|
||||
#86 = LINE('',#87,#88);
|
||||
#87 = CARTESIAN_POINT('',(0.,10.,0.));
|
||||
#88 = VECTOR('',#89,1.E-03);
|
||||
#89 = DIRECTION('',(0.,-1.,0.));
|
||||
#90 = PCURVE('',#54,#91);
|
||||
#91 = DEFINITIONAL_REPRESENTATION('',(#92),#96);
|
||||
#92 = LINE('',#93,#94);
|
||||
#93 = CARTESIAN_POINT('',(-5.,5.));
|
||||
#94 = VECTOR('',#95,1.);
|
||||
#95 = DIRECTION('',(0.,-1.));
|
||||
#96 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2)
|
||||
PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE',''
|
||||
) );
|
||||
#97 = ORIENTED_EDGE('',*,*,#98,.T.);
|
||||
#98 = EDGE_CURVE('',#83,#44,#99,.T.);
|
||||
#99 = SURFACE_CURVE('',#100,(#104),.PCURVE_S1.);
|
||||
#100 = LINE('',#101,#102);
|
||||
#101 = CARTESIAN_POINT('',(0.,0.,0.));
|
||||
#102 = VECTOR('',#103,1.E-03);
|
||||
#103 = DIRECTION('',(1.,0.,0.));
|
||||
#104 = PCURVE('',#54,#105);
|
||||
#105 = DEFINITIONAL_REPRESENTATION('',(#106),#110);
|
||||
#106 = LINE('',#107,#108);
|
||||
#107 = CARTESIAN_POINT('',(-5.,-5.));
|
||||
#108 = VECTOR('',#109,1.);
|
||||
#109 = DIRECTION('',(1.,0.));
|
||||
#110 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2)
|
||||
PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE',''
|
||||
) );
|
||||
#111 = ( GEOMETRIC_REPRESENTATION_CONTEXT(3)
|
||||
GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#115)) GLOBAL_UNIT_ASSIGNED_CONTEXT
|
||||
((#112,#113,#114)) REPRESENTATION_CONTEXT('Context #1',
|
||||
'3D Context with UNIT and UNCERTAINTY') );
|
||||
#112 = ( LENGTH_UNIT() NAMED_UNIT(*) SI_UNIT($,.METRE.) );
|
||||
#113 = ( NAMED_UNIT(*) PLANE_ANGLE_UNIT() SI_UNIT($,.RADIAN.) );
|
||||
#114 = ( NAMED_UNIT(*) SI_UNIT($,.STERADIAN.) SOLID_ANGLE_UNIT() );
|
||||
#115 = UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(1.E-10),#112,
|
||||
'distance_accuracy_value','confusion accuracy');
|
||||
#116 = CONTEXT_DEPENDENT_SHAPE_REPRESENTATION(#117,#119);
|
||||
#117 = ( REPRESENTATION_RELATIONSHIP('','',#36,#10)
|
||||
REPRESENTATION_RELATIONSHIP_WITH_TRANSFORMATION(#118)
|
||||
SHAPE_REPRESENTATION_RELATIONSHIP() );
|
||||
#118 = ITEM_DEFINED_TRANSFORMATION('','',#11,#15);
|
||||
#119 = PRODUCT_DEFINITION_SHAPE('Placement','Placement of an item',#120
|
||||
);
|
||||
#120 = NEXT_ASSEMBLY_USAGE_OCCURRENCE('3','red_plate','',#5,#31,$);
|
||||
#121 = PRODUCT_RELATED_PRODUCT_CATEGORY('part',$,(#33));
|
||||
#122 = SHAPE_DEFINITION_REPRESENTATION(#123,#129);
|
||||
#123 = PRODUCT_DEFINITION_SHAPE('','',#124);
|
||||
#124 = PRODUCT_DEFINITION('design','',#125,#128);
|
||||
#125 = PRODUCT_DEFINITION_FORMATION('','',#126);
|
||||
#126 = PRODUCT('blue_plate','blue_plate','',(#127));
|
||||
#127 = PRODUCT_CONTEXT('',#2,'mechanical');
|
||||
#128 = PRODUCT_DEFINITION_CONTEXT('part definition',#2,'design');
|
||||
#129 = MANIFOLD_SURFACE_SHAPE_REPRESENTATION('',(#11,#130),#204);
|
||||
#130 = SHELL_BASED_SURFACE_MODEL('',(#131));
|
||||
#131 = OPEN_SHELL('',(#132));
|
||||
#132 = ADVANCED_FACE('',(#133),#147,.T.);
|
||||
#133 = FACE_BOUND('',#134,.T.);
|
||||
#134 = EDGE_LOOP('',(#135,#158,#174,#190));
|
||||
#135 = ORIENTED_EDGE('',*,*,#136,.T.);
|
||||
#136 = EDGE_CURVE('',#137,#139,#141,.T.);
|
||||
#137 = VERTEX_POINT('',#138);
|
||||
#138 = CARTESIAN_POINT('',(10.,0.,0.));
|
||||
#139 = VERTEX_POINT('',#140);
|
||||
#140 = CARTESIAN_POINT('',(10.,10.,0.));
|
||||
#141 = SURFACE_CURVE('',#142,(#146),.PCURVE_S1.);
|
||||
#142 = LINE('',#143,#144);
|
||||
#143 = CARTESIAN_POINT('',(10.,0.,0.));
|
||||
#144 = VECTOR('',#145,1.E-03);
|
||||
#145 = DIRECTION('',(0.,1.,0.));
|
||||
#146 = PCURVE('',#147,#152);
|
||||
#147 = PLANE('',#148);
|
||||
#148 = AXIS2_PLACEMENT_3D('',#149,#150,#151);
|
||||
#149 = CARTESIAN_POINT('',(5.,5.,0.));
|
||||
#150 = DIRECTION('',(0.,0.,1.));
|
||||
#151 = DIRECTION('',(1.,0.,-0.));
|
||||
#152 = DEFINITIONAL_REPRESENTATION('',(#153),#157);
|
||||
#153 = LINE('',#154,#155);
|
||||
#154 = CARTESIAN_POINT('',(5.,-5.));
|
||||
#155 = VECTOR('',#156,1.);
|
||||
#156 = DIRECTION('',(0.,1.));
|
||||
#157 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2)
|
||||
PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE',''
|
||||
) );
|
||||
#158 = ORIENTED_EDGE('',*,*,#159,.T.);
|
||||
#159 = EDGE_CURVE('',#139,#160,#162,.T.);
|
||||
#160 = VERTEX_POINT('',#161);
|
||||
#161 = CARTESIAN_POINT('',(0.,10.,0.));
|
||||
#162 = SURFACE_CURVE('',#163,(#167),.PCURVE_S1.);
|
||||
#163 = LINE('',#164,#165);
|
||||
#164 = CARTESIAN_POINT('',(10.,10.,0.));
|
||||
#165 = VECTOR('',#166,1.E-03);
|
||||
#166 = DIRECTION('',(-1.,0.,0.));
|
||||
#167 = PCURVE('',#147,#168);
|
||||
#168 = DEFINITIONAL_REPRESENTATION('',(#169),#173);
|
||||
#169 = LINE('',#170,#171);
|
||||
#170 = CARTESIAN_POINT('',(5.,5.));
|
||||
#171 = VECTOR('',#172,1.);
|
||||
#172 = DIRECTION('',(-1.,0.));
|
||||
#173 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2)
|
||||
PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE',''
|
||||
) );
|
||||
#174 = ORIENTED_EDGE('',*,*,#175,.T.);
|
||||
#175 = EDGE_CURVE('',#160,#176,#178,.T.);
|
||||
#176 = VERTEX_POINT('',#177);
|
||||
#177 = CARTESIAN_POINT('',(0.,0.,0.));
|
||||
#178 = SURFACE_CURVE('',#179,(#183),.PCURVE_S1.);
|
||||
#179 = LINE('',#180,#181);
|
||||
#180 = CARTESIAN_POINT('',(0.,10.,0.));
|
||||
#181 = VECTOR('',#182,1.E-03);
|
||||
#182 = DIRECTION('',(0.,-1.,0.));
|
||||
#183 = PCURVE('',#147,#184);
|
||||
#184 = DEFINITIONAL_REPRESENTATION('',(#185),#189);
|
||||
#185 = LINE('',#186,#187);
|
||||
#186 = CARTESIAN_POINT('',(-5.,5.));
|
||||
#187 = VECTOR('',#188,1.);
|
||||
#188 = DIRECTION('',(0.,-1.));
|
||||
#189 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2)
|
||||
PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE',''
|
||||
) );
|
||||
#190 = ORIENTED_EDGE('',*,*,#191,.T.);
|
||||
#191 = EDGE_CURVE('',#176,#137,#192,.T.);
|
||||
#192 = SURFACE_CURVE('',#193,(#197),.PCURVE_S1.);
|
||||
#193 = LINE('',#194,#195);
|
||||
#194 = CARTESIAN_POINT('',(0.,0.,0.));
|
||||
#195 = VECTOR('',#196,1.E-03);
|
||||
#196 = DIRECTION('',(1.,0.,0.));
|
||||
#197 = PCURVE('',#147,#198);
|
||||
#198 = DEFINITIONAL_REPRESENTATION('',(#199),#203);
|
||||
#199 = LINE('',#200,#201);
|
||||
#200 = CARTESIAN_POINT('',(-5.,-5.));
|
||||
#201 = VECTOR('',#202,1.);
|
||||
#202 = DIRECTION('',(1.,0.));
|
||||
#203 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2)
|
||||
PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE',''
|
||||
) );
|
||||
#204 = ( GEOMETRIC_REPRESENTATION_CONTEXT(3)
|
||||
GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#208)) GLOBAL_UNIT_ASSIGNED_CONTEXT
|
||||
((#205,#206,#207)) REPRESENTATION_CONTEXT('Context #1',
|
||||
'3D Context with UNIT and UNCERTAINTY') );
|
||||
#205 = ( LENGTH_UNIT() NAMED_UNIT(*) SI_UNIT($,.METRE.) );
|
||||
#206 = ( NAMED_UNIT(*) PLANE_ANGLE_UNIT() SI_UNIT($,.RADIAN.) );
|
||||
#207 = ( NAMED_UNIT(*) SI_UNIT($,.STERADIAN.) SOLID_ANGLE_UNIT() );
|
||||
#208 = UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(1.E-10),#205,
|
||||
'distance_accuracy_value','confusion accuracy');
|
||||
#209 = CONTEXT_DEPENDENT_SHAPE_REPRESENTATION(#210,#212);
|
||||
#210 = ( REPRESENTATION_RELATIONSHIP('','',#129,#10)
|
||||
REPRESENTATION_RELATIONSHIP_WITH_TRANSFORMATION(#211)
|
||||
SHAPE_REPRESENTATION_RELATIONSHIP() );
|
||||
#211 = ITEM_DEFINED_TRANSFORMATION('','',#11,#19);
|
||||
#212 = PRODUCT_DEFINITION_SHAPE('Placement','Placement of an item',#213
|
||||
);
|
||||
#213 = NEXT_ASSEMBLY_USAGE_OCCURRENCE('4','blue_plate','',#5,#124,$);
|
||||
#214 = PRODUCT_RELATED_PRODUCT_CATEGORY('part',$,(#126));
|
||||
#215 = MECHANICAL_DESIGN_GEOMETRIC_PRESENTATION_REPRESENTATION('',(#216)
|
||||
,#111);
|
||||
#216 = STYLED_ITEM('color',(#217),#37);
|
||||
#217 = PRESENTATION_STYLE_ASSIGNMENT((#218,#224));
|
||||
#218 = SURFACE_STYLE_USAGE(.BOTH.,#219);
|
||||
#219 = SURFACE_SIDE_STYLE('',(#220));
|
||||
#220 = SURFACE_STYLE_FILL_AREA(#221);
|
||||
#221 = FILL_AREA_STYLE('',(#222));
|
||||
#222 = FILL_AREA_STYLE_COLOUR('',#223);
|
||||
#223 = DRAUGHTING_PRE_DEFINED_COLOUR('red');
|
||||
#224 = CURVE_STYLE('',#225,POSITIVE_LENGTH_MEASURE(0.1),#226);
|
||||
#225 = DRAUGHTING_PRE_DEFINED_CURVE_FONT('continuous');
|
||||
#226 = COLOUR_RGB('',9.803921802644E-02,9.803921802644E-02,
|
||||
9.803921802644E-02);
|
||||
#227 = MECHANICAL_DESIGN_GEOMETRIC_PRESENTATION_REPRESENTATION('',(#228)
|
||||
,#204);
|
||||
#228 = STYLED_ITEM('color',(#229),#130);
|
||||
#229 = PRESENTATION_STYLE_ASSIGNMENT((#230,#236));
|
||||
#230 = SURFACE_STYLE_USAGE(.BOTH.,#231);
|
||||
#231 = SURFACE_SIDE_STYLE('',(#232));
|
||||
#232 = SURFACE_STYLE_FILL_AREA(#233);
|
||||
#233 = FILL_AREA_STYLE('',(#234));
|
||||
#234 = FILL_AREA_STYLE_COLOUR('',#235);
|
||||
#235 = DRAUGHTING_PRE_DEFINED_COLOUR('blue');
|
||||
#236 = CURVE_STYLE('',#237,POSITIVE_LENGTH_MEASURE(0.1),#226);
|
||||
#237 = DRAUGHTING_PRE_DEFINED_CURVE_FONT('continuous');
|
||||
ENDSEC;
|
||||
END-ISO-10303-21;
|
||||
303
files/my_test.ifc
Normal file
303
files/my_test.ifc
Normal file
@ -0,0 +1,303 @@
|
||||
ISO-10303-21;
|
||||
HEADER;
|
||||
FILE_DESCRIPTION(('ViewDefinition[DesignTransferView]'),'2;1');
|
||||
FILE_NAME('/dev/null','2023-09-18T20:15:51+02:00',(),(),'IfcOpenShell 0.7.0','IfcOpenShell 0.7.0','Nobody');
|
||||
FILE_SCHEMA(('IFC4X1'));
|
||||
ENDSEC;
|
||||
DATA;
|
||||
#1=IFCPROJECT('1FHXGqcyrEhvZl5Q2VSoJh',$,'AdaProject',$,$,$,$,(#10),#5);
|
||||
#2=IFCSIUNIT(*,.LENGTHUNIT.,$,.METRE.);
|
||||
#3=IFCSIUNIT(*,.AREAUNIT.,$,.SQUARE_METRE.);
|
||||
#4=IFCSIUNIT(*,.VOLUMEUNIT.,$,.CUBIC_METRE.);
|
||||
#5=IFCUNITASSIGNMENT((#2,#3,#4));
|
||||
#6=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#7=IFCDIRECTION((0.,0.,1.));
|
||||
#8=IFCDIRECTION((1.,0.,0.));
|
||||
#9=IFCAXIS2PLACEMENT3D(#6,#7,#8);
|
||||
#10=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,1.E-05,#9,$);
|
||||
#11=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Body','Model',*,*,*,*,#10,$,.MODEL_VIEW.,$);
|
||||
#12=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Axis','Model',*,*,*,*,#10,$,.GRAPH_VIEW.,$);
|
||||
#13=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Box','Model',*,*,*,*,#10,$,.MODEL_VIEW.,$);
|
||||
#14=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Plan',2,1.E-05,#9,$);
|
||||
#15=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Axis','Plan',*,*,*,*,#14,$,.GRAPH_VIEW.,$);
|
||||
#16=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Annotation','Plan',*,*,*,*,#14,$,.PLAN_VIEW.,$);
|
||||
#17=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Annotation','Plan',*,*,*,*,#14,$,.SECTION_VIEW.,$);
|
||||
#18=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Annotation','Plan',*,*,*,*,#14,$,.ELEVATION_VIEW.,$);
|
||||
#19=IFCACTORROLE(.ENGINEER.,$,$);
|
||||
#20=IFCPERSON('krande',$,$,$,$,$,(#19),$);
|
||||
#21=IFCORGANIZATION('ADA','Assembly Test',$,$,$);
|
||||
#22=IFCPERSONANDORGANIZATION(#20,#21,$);
|
||||
#23=IFCAPPLICATION(#21,'XXX','ADA','ADA');
|
||||
#24=IFCOWNERHISTORY(#22,#23,.READWRITE.,$,$,#22,#23,1695060951);
|
||||
#25=IFCDIRECTION((0.,0.,1.));
|
||||
#26=IFCDIRECTION((1.,0.,0.));
|
||||
#27=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#28=IFCAXIS2PLACEMENT3D(#27,#25,#26);
|
||||
#29=IFCLOCALPLACEMENT($,#28);
|
||||
#30=IFCSITE('1cQMCZLayHxgf7v5utukF4',#24,'MyFirstIfcFile',$,$,#29,$,$,.ELEMENT.,$,$,$,$,$);
|
||||
#31=IFCRELAGGREGATES('1cWIcALayHxeWKv5utukF4',#24,'Project Container',$,#1,(#30));
|
||||
#32=IFCPROPERTYSINGLEVALUE('project',$,IFCTEXT('AdaProject'),$);
|
||||
#33=IFCPROPERTYSINGLEVALUE('schema',$,IFCTEXT('IFC4X1'),$);
|
||||
#34=IFCPROPERTYSET('1O_djxZjXASRMw6ucy9_3O',#24,'Properties',$,(#32,#33));
|
||||
#35=IFCRELDEFINESBYPROPERTIES('1cWIwdLayHxfyzv5utukF4',#24,'Properties',$,(#30),#34);
|
||||
#36=IFCDIRECTION((0.,0.,1.));
|
||||
#37=IFCDIRECTION((1.,0.,0.));
|
||||
#38=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#39=IFCAXIS2PLACEMENT3D(#38,#36,#37);
|
||||
#40=IFCLOCALPLACEMENT(#29,#39);
|
||||
#41=IFCBUILDINGSTOREY('1cVWQ2LayHxhRev5utukF4',#24,'MyBldg',$,$,#40,$,$,.ELEMENT.,0.);
|
||||
#42=IFCRELAGGREGATES('1cWIweLayHxeNEv5utukF4',#24,'Site Container',$,#30,(#41));
|
||||
#43=IFCPROPERTYSINGLEVALUE('ifctype',$,IFCTEXT('building'),$);
|
||||
#44=IFCPROPERTYSET('2mP4D_J7LCrArYIAqVBbAH',#24,'Properties',$,(#43));
|
||||
#45=IFCRELDEFINESBYPROPERTIES('1cWIwfLayHxfBov5utukF4',#24,'Properties',$,(#41),#44);
|
||||
#46=IFCCARTESIANPOINT((-0.1,0.15));
|
||||
#47=IFCCARTESIANPOINT((0.1,0.15));
|
||||
#48=IFCCARTESIANPOINT((0.1,-0.15));
|
||||
#49=IFCCARTESIANPOINT((-0.1,-0.15));
|
||||
#50=IFCCARTESIANPOINT((-0.1,0.15));
|
||||
#51=IFCCARTESIANPOINT((-0.09,0.13));
|
||||
#52=IFCCARTESIANPOINT((0.09,0.13));
|
||||
#53=IFCCARTESIANPOINT((0.09,-0.13));
|
||||
#54=IFCCARTESIANPOINT((-0.09,-0.13));
|
||||
#55=IFCCARTESIANPOINT((-0.09,0.13));
|
||||
#56=IFCPOLYLINE((#51,#52,#53,#54,#55));
|
||||
#57=IFCPOLYLINE((#46,#47,#48,#49,#50));
|
||||
#58=IFCARBITRARYPROFILEDEFWITHVOIDS(.AREA.,'BG300x200x10x20',#57,(#56));
|
||||
#59=IFCBEAMTYPE('1cPZ5vLayHxg6Fv5utukF4',#24,'BG300x200x10x20','BOX300x200x10x20',$,$,$,$,$,.BEAM.);
|
||||
#60=IFCISHAPEPROFILEDEF(.AREA.,'IPE220',$,0.11,0.22,0.0059,0.0092,$,$,$);
|
||||
#61=IFCBEAMTYPE('1cPqF_LayHxgIgv5utukF4',#24,'IPE220','IPE220',$,$,$,$,$,.BEAM.);
|
||||
#62=IFCMATERIAL('S355',$,'Steel');
|
||||
#63=IFCPROPERTYSINGLEVALUE('Grade',$,IFCTEXT('S355'),$);
|
||||
#64=IFCPROPERTYSINGLEVALUE('YieldStress',$,IFCPRESSUREMEASURE(355000000.),$);
|
||||
#65=IFCPROPERTYSINGLEVALUE('YoungModulus',$,IFCMODULUSOFELASTICITYMEASURE(210000000000.),$);
|
||||
#66=IFCPROPERTYSINGLEVALUE('PoissonRatio',$,IFCPOSITIVERATIOMEASURE(0.3),$);
|
||||
#67=IFCPROPERTYSINGLEVALUE('ThermalExpansionCoefficient',$,IFCTHERMALEXPANSIONCOEFFICIENTMEASURE(1.2E-05),$);
|
||||
#68=IFCPROPERTYSINGLEVALUE('SpecificHeatCapacity',$,IFCSPECIFICHEATCAPACITYMEASURE(1.15),$);
|
||||
#69=IFCPROPERTYSINGLEVALUE('MassDensity',$,IFCMASSDENSITYMEASURE(7850.),$);
|
||||
#70=IFCMATERIALPROPERTIES('MaterialMechanical','A Material property description',(#63,#64,#65,#66,#67,#68,#69),#62);
|
||||
#71=IFCRELASSOCIATESMATERIAL('1cPZ5xLayHxePlv5utukF4',#24,'S355','Objects related to S355',(#129,#164,#196,#227,#258,#289),#62);
|
||||
#72=IFCMATERIAL('S420',$,'Steel');
|
||||
#73=IFCPROPERTYSINGLEVALUE('Grade',$,IFCTEXT('S420'),$);
|
||||
#74=IFCPROPERTYSINGLEVALUE('YieldStress',$,IFCPRESSUREMEASURE(420000000.),$);
|
||||
#75=IFCPROPERTYSINGLEVALUE('YoungModulus',$,IFCMODULUSOFELASTICITYMEASURE(210000000000.),$);
|
||||
#76=IFCPROPERTYSINGLEVALUE('PoissonRatio',$,IFCPOSITIVERATIOMEASURE(0.3),$);
|
||||
#77=IFCPROPERTYSINGLEVALUE('ThermalExpansionCoefficient',$,IFCTHERMALEXPANSIONCOEFFICIENTMEASURE(1.2E-05),$);
|
||||
#78=IFCPROPERTYSINGLEVALUE('SpecificHeatCapacity',$,IFCSPECIFICHEATCAPACITYMEASURE(1.15),$);
|
||||
#79=IFCPROPERTYSINGLEVALUE('MassDensity',$,IFCMASSDENSITYMEASURE(7850.),$);
|
||||
#80=IFCMATERIALPROPERTIES('MaterialMechanical','A Material property description',(#73,#74,#75,#76,#77,#78,#79),#72);
|
||||
#81=IFCRELASSOCIATESMATERIAL('1cQMCYLayHxexvv5utukF4',#24,'S420','Objects related to S420',(#98),#72);
|
||||
#82=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#83=IFCDIRECTION((0.,0.,1.));
|
||||
#84=IFCDIRECTION((1.,0.,0.));
|
||||
#85=IFCAXIS2PLACEMENT3D(#82,#83,#84);
|
||||
#86=IFCLOCALPLACEMENT($,#85);
|
||||
#87=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#88=IFCDIRECTION((1.,0.,0.));
|
||||
#89=IFCDIRECTION((0.,0.,1.));
|
||||
#90=IFCAXIS2PLACEMENT3D(#87,#88,#89);
|
||||
#91=IFCCARTESIANPOINTLIST2D(((0.,-2.),(0.,0.),(2.,-2.),(2.,0.)),$);
|
||||
#92=IFCINDEXEDPOLYCURVE(#91,(IFCLINEINDEX((1,2)),IFCLINEINDEX((2,4)),IFCLINEINDEX((4,3)),IFCLINEINDEX((3,1))),.F.);
|
||||
#93=IFCARBITRARYCLOSEDPROFILEDEF(.AREA.,$,#92);
|
||||
#94=IFCDIRECTION((0.,0.,1.));
|
||||
#95=IFCEXTRUDEDAREASOLID(#93,#90,#94,0.01);
|
||||
#96=IFCSHAPEREPRESENTATION(#11,'Body','SolidModel',(#95));
|
||||
#97=IFCPRODUCTDEFINITIONSHAPE($,$,(#96));
|
||||
#98=IFCPLATE('1cQMCXLayHxgKdv5utukF4',#24,'pl1','pl1',$,#86,#97,$,$);
|
||||
#99=IFCCOLOURRGB('Color(red=0.5019607843137255, green=0.5019607843137255, blue=0.5019607843137255, opacity=1.0)',0.501960784313725,0.501960784313725,0.501960784313725);
|
||||
#100=IFCSURFACESTYLESHADING(#99,$);
|
||||
#101=IFCSURFACESTYLE('Color(red=0.5019607843137255, green=0.5019607843137255, blue=0.5019607843137255, opacity=1.0)',.BOTH.,(#100));
|
||||
#102=IFCSTYLEDITEM(#95,(#101),'Color(red=0.5019607843137255, green=0.5019607843137255, blue=0.5019607843137255, opacity=1.0)');
|
||||
#103=IFCDIRECTION((0.,0.,1.));
|
||||
#104=IFCDIRECTION((1.,0.,0.));
|
||||
#105=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#106=IFCAXIS2PLACEMENT3D(#105,#103,#104);
|
||||
#107=IFCLOCALPLACEMENT(#40,#106);
|
||||
#108=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#109=IFCDIRECTION((1.,0.,0.));
|
||||
#110=IFCDIRECTION((0.,1.,0.));
|
||||
#111=IFCAXIS2PLACEMENT3D(#108,#109,#110);
|
||||
#112=IFCDIRECTION((0.,0.,1.));
|
||||
#113=IFCEXTRUDEDAREASOLID(#58,#111,#112,2.);
|
||||
#114=IFCSHAPEREPRESENTATION(#11,'Body','SolidModel',(#113));
|
||||
#115=IFCDIRECTION((0.,0.,1.));
|
||||
#116=IFCDIRECTION((1.,0.,0.));
|
||||
#117=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#118=IFCAXIS2PLACEMENT3D(#117,#115,#116);
|
||||
#119=IFCLOCALPLACEMENT(#107,#118);
|
||||
#120=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#121=IFCCARTESIANPOINT((2.,0.,0.));
|
||||
#122=IFCPOLYLINE((#120,#121));
|
||||
#123=IFCSHAPEREPRESENTATION(#12,'Axis','Curve3D',(#122));
|
||||
#124=IFCCOLOURRGB('Color(red=0.5019607843137255, green=0.5019607843137255, blue=0.5019607843137255, opacity=1.0)',0.501960784313725,0.501960784313725,0.501960784313725);
|
||||
#125=IFCSURFACESTYLESHADING(#124,$);
|
||||
#126=IFCSURFACESTYLE('Color(red=0.5019607843137255, green=0.5019607843137255, blue=0.5019607843137255, opacity=1.0)',.BOTH.,(#125));
|
||||
#127=IFCSTYLEDITEM(#113,(#126),'Color(red=0.5019607843137255, green=0.5019607843137255, blue=0.5019607843137255, opacity=1.0)');
|
||||
#128=IFCPRODUCTDEFINITIONSHAPE($,$,(#114,#123));
|
||||
#129=IFCBEAM('1cPZ5wLayHxeFFv5utukF4',#24,'MyBeam','BOX300x200x10x20','Beam',#119,#128,$,$);
|
||||
#130=IFCRELDEFINESBYTYPE('1cWfvcLayHxggTv5utukF4',#24,'BOX',$,(#129),#59);
|
||||
#131=IFCMATERIALPROFILE('BG300x200x10x20','A material profile',#62,#58,$,'LoadBearing');
|
||||
#132=IFCMATERIALPROFILESET('BG300x200x10x20',$,(#131),$);
|
||||
#133=IFCMATERIALPROFILESETUSAGE(#132,5,$);
|
||||
#134=IFCRELASSOCIATESMATERIAL('1cWio8LayHxeSwv5utukF4',#24,$,$,(#129),#133);
|
||||
#135=IFCPROPERTYSINGLEVALUE('hidden',$,IFCBOOLEAN(.T.),$);
|
||||
#136=IFCPROPERTYSET('3S89ko$y96yBG4TM6e0J0K',#24,'Properties',$,(#135));
|
||||
#137=IFCRELDEFINESBYPROPERTIES('1cXGCQLayHxgByv5utukF4',#24,'Properties',$,(#129),#136);
|
||||
#138=IFCDIRECTION((0.,0.,1.));
|
||||
#139=IFCDIRECTION((1.,0.,0.));
|
||||
#140=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#141=IFCAXIS2PLACEMENT3D(#140,#138,#139);
|
||||
#142=IFCLOCALPLACEMENT(#40,#141);
|
||||
#143=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#144=IFCDIRECTION((1.,0.,0.));
|
||||
#145=IFCDIRECTION((0.,1.,0.));
|
||||
#146=IFCAXIS2PLACEMENT3D(#143,#144,#145);
|
||||
#147=IFCDIRECTION((0.,0.,1.));
|
||||
#148=IFCEXTRUDEDAREASOLID(#60,#146,#147,2.);
|
||||
#149=IFCSHAPEREPRESENTATION(#11,'Body','SolidModel',(#148));
|
||||
#150=IFCDIRECTION((0.,0.,1.));
|
||||
#151=IFCDIRECTION((1.,0.,0.));
|
||||
#152=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#153=IFCAXIS2PLACEMENT3D(#152,#150,#151);
|
||||
#154=IFCLOCALPLACEMENT(#142,#153);
|
||||
#155=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#156=IFCCARTESIANPOINT((2.,0.,0.));
|
||||
#157=IFCPOLYLINE((#155,#156));
|
||||
#158=IFCSHAPEREPRESENTATION(#12,'Axis','Curve3D',(#157));
|
||||
#159=IFCCOLOURRGB('Color(red=1.0, green=0.0, blue=0.0, opacity=1.0)',1.,0.,0.);
|
||||
#160=IFCSURFACESTYLESHADING(#159,$);
|
||||
#161=IFCSURFACESTYLE('Color(red=1.0, green=0.0, blue=0.0, opacity=1.0)',.BOTH.,(#160));
|
||||
#162=IFCSTYLEDITEM(#148,(#161),'Color(red=1.0, green=0.0, blue=0.0, opacity=1.0)');
|
||||
#163=IFCPRODUCTDEFINITIONSHAPE($,$,(#149,#158));
|
||||
#164=IFCBEAM('1cPbYcLayHxe$Cv5utukF4',#24,'bm1','IPE220','Beam',#154,#163,$,$);
|
||||
#165=IFCRELDEFINESBYTYPE('1cXLnKLayHxhQQv5utukF4',#24,'I',$,(#164,#196,#227,#258,#289),#61);
|
||||
#166=IFCMATERIALPROFILE('IPE220','A material profile',#62,#60,$,'LoadBearing');
|
||||
#167=IFCMATERIALPROFILESET('IPE220',$,(#166),$);
|
||||
#168=IFCMATERIALPROFILESETUSAGE(#167,5,$);
|
||||
#169=IFCRELASSOCIATESMATERIAL('1cXLnLLayHxetXv5utukF4',#24,$,$,(#164),#168);
|
||||
#170=IFCDIRECTION((0.,0.,1.));
|
||||
#171=IFCDIRECTION((1.,0.,0.));
|
||||
#172=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#173=IFCAXIS2PLACEMENT3D(#172,#170,#171);
|
||||
#174=IFCLOCALPLACEMENT(#40,#173);
|
||||
#175=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#176=IFCDIRECTION((0.,1.,0.));
|
||||
#177=IFCDIRECTION((-1.,0.,0.));
|
||||
#178=IFCAXIS2PLACEMENT3D(#175,#176,#177);
|
||||
#179=IFCDIRECTION((0.,0.,1.));
|
||||
#180=IFCEXTRUDEDAREASOLID(#60,#178,#179,2.);
|
||||
#181=IFCSHAPEREPRESENTATION(#11,'Body','SolidModel',(#180));
|
||||
#182=IFCDIRECTION((0.,0.,1.));
|
||||
#183=IFCDIRECTION((1.,0.,0.));
|
||||
#184=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#185=IFCAXIS2PLACEMENT3D(#184,#182,#183);
|
||||
#186=IFCLOCALPLACEMENT(#174,#185);
|
||||
#187=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#188=IFCCARTESIANPOINT((0.,2.,0.));
|
||||
#189=IFCPOLYLINE((#187,#188));
|
||||
#190=IFCSHAPEREPRESENTATION(#12,'Axis','Curve3D',(#189));
|
||||
#191=IFCCOLOURRGB('Color(red=0.0, green=0.0, blue=1.0, opacity=1.0)',0.,0.,1.);
|
||||
#192=IFCSURFACESTYLESHADING(#191,$);
|
||||
#193=IFCSURFACESTYLE('Color(red=0.0, green=0.0, blue=1.0, opacity=1.0)',.BOTH.,(#192));
|
||||
#194=IFCSTYLEDITEM(#180,(#193),'Color(red=0.0, green=0.0, blue=1.0, opacity=1.0)');
|
||||
#195=IFCPRODUCTDEFINITIONSHAPE($,$,(#181,#190));
|
||||
#196=IFCBEAM('1cPsijLayHxgUev5utukF4',#24,'bm2','IPE220','Beam',#186,#195,$,$);
|
||||
#197=IFCMATERIALPROFILE('IPE220','A material profile',#62,#60,$,'LoadBearing');
|
||||
#198=IFCMATERIALPROFILESET('IPE220',$,(#197),$);
|
||||
#199=IFCMATERIALPROFILESETUSAGE(#198,5,$);
|
||||
#200=IFCRELASSOCIATESMATERIAL('1cXTDfLayHxe6qv5utukF4',#24,$,$,(#196),#199);
|
||||
#201=IFCDIRECTION((0.,0.,1.));
|
||||
#202=IFCDIRECTION((1.,0.,0.));
|
||||
#203=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#204=IFCAXIS2PLACEMENT3D(#203,#201,#202);
|
||||
#205=IFCLOCALPLACEMENT(#40,#204);
|
||||
#206=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#207=IFCDIRECTION((0.,0.,1.));
|
||||
#208=IFCDIRECTION((1.,0.,0.));
|
||||
#209=IFCAXIS2PLACEMENT3D(#206,#207,#208);
|
||||
#210=IFCDIRECTION((0.,0.,1.));
|
||||
#211=IFCEXTRUDEDAREASOLID(#60,#209,#210,2.);
|
||||
#212=IFCSHAPEREPRESENTATION(#11,'Body','SolidModel',(#211));
|
||||
#213=IFCDIRECTION((0.,0.,1.));
|
||||
#214=IFCDIRECTION((1.,0.,0.));
|
||||
#215=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#216=IFCAXIS2PLACEMENT3D(#215,#213,#214);
|
||||
#217=IFCLOCALPLACEMENT(#205,#216);
|
||||
#218=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#219=IFCCARTESIANPOINT((0.,0.,2.));
|
||||
#220=IFCPOLYLINE((#218,#219));
|
||||
#221=IFCSHAPEREPRESENTATION(#12,'Axis','Curve3D',(#220));
|
||||
#222=IFCCOLOURRGB('Color(red=0.0, green=1.0, blue=0.0, opacity=1.0)',0.,1.,0.);
|
||||
#223=IFCSURFACESTYLESHADING(#222,$);
|
||||
#224=IFCSURFACESTYLE('Color(red=0.0, green=1.0, blue=0.0, opacity=1.0)',.BOTH.,(#223));
|
||||
#225=IFCSTYLEDITEM(#211,(#224),'Color(red=0.0, green=1.0, blue=0.0, opacity=1.0)');
|
||||
#226=IFCPRODUCTDEFINITIONSHAPE($,$,(#212,#221));
|
||||
#227=IFCBEAM('1cPv7pLayHxerkv5utukF4',#24,'bm3','IPE220','Beam',#217,#226,$,$);
|
||||
#228=IFCMATERIALPROFILE('IPE220','A material profile',#62,#60,$,'LoadBearing');
|
||||
#229=IFCMATERIALPROFILESET('IPE220',$,(#228),$);
|
||||
#230=IFCMATERIALPROFILESETUSAGE(#229,5,$);
|
||||
#231=IFCRELASSOCIATESMATERIAL('1cXYirLayHxe0mv5utukF4',#24,$,$,(#227),#230);
|
||||
#232=IFCDIRECTION((0.,0.,1.));
|
||||
#233=IFCDIRECTION((1.,0.,0.));
|
||||
#234=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#235=IFCAXIS2PLACEMENT3D(#234,#232,#233);
|
||||
#236=IFCLOCALPLACEMENT(#40,#235);
|
||||
#237=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#238=IFCDIRECTION((0.707106781186547,0.,0.707106781186547));
|
||||
#239=IFCDIRECTION((0.,1.,0.));
|
||||
#240=IFCAXIS2PLACEMENT3D(#237,#238,#239);
|
||||
#241=IFCDIRECTION((0.,0.,1.));
|
||||
#242=IFCEXTRUDEDAREASOLID(#60,#240,#241,2.82842712474619);
|
||||
#243=IFCSHAPEREPRESENTATION(#11,'Body','SolidModel',(#242));
|
||||
#244=IFCDIRECTION((0.,0.,1.));
|
||||
#245=IFCDIRECTION((1.,0.,0.));
|
||||
#246=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#247=IFCAXIS2PLACEMENT3D(#246,#244,#245);
|
||||
#248=IFCLOCALPLACEMENT(#236,#247);
|
||||
#249=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#250=IFCCARTESIANPOINT((2.,0.,2.));
|
||||
#251=IFCPOLYLINE((#249,#250));
|
||||
#252=IFCSHAPEREPRESENTATION(#12,'Axis','Curve3D',(#251));
|
||||
#253=IFCCOLOURRGB('Color(red=0.5019607843137255, green=0.5019607843137255, blue=0.5019607843137255, opacity=1.0)',0.501960784313725,0.501960784313725,0.501960784313725);
|
||||
#254=IFCSURFACESTYLESHADING(#253,$);
|
||||
#255=IFCSURFACESTYLE('Color(red=0.5019607843137255, green=0.5019607843137255, blue=0.5019607843137255, opacity=1.0)',.BOTH.,(#254));
|
||||
#256=IFCSTYLEDITEM(#242,(#255),'Color(red=0.5019607843137255, green=0.5019607843137255, blue=0.5019607843137255, opacity=1.0)');
|
||||
#257=IFCPRODUCTDEFINITIONSHAPE($,$,(#243,#252));
|
||||
#258=IFCBEAM('1cPxbBLayHxfhtv5utukF4',#24,'bm4','IPE220','Beam',#248,#257,$,$);
|
||||
#259=IFCMATERIALPROFILE('IPE220','A material profile',#62,#60,$,'LoadBearing');
|
||||
#260=IFCMATERIALPROFILESET('IPE220',$,(#259),$);
|
||||
#261=IFCMATERIALPROFILESETUSAGE(#260,5,$);
|
||||
#262=IFCRELASSOCIATESMATERIAL('1cXgAfLayHxf$bv5utukF4',#24,$,$,(#258),#261);
|
||||
#263=IFCDIRECTION((0.,0.,1.));
|
||||
#264=IFCDIRECTION((1.,0.,0.));
|
||||
#265=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#266=IFCAXIS2PLACEMENT3D(#265,#263,#264);
|
||||
#267=IFCLOCALPLACEMENT(#40,#266);
|
||||
#268=IFCCARTESIANPOINT((0.,0.,2.));
|
||||
#269=IFCDIRECTION((1.,0.,0.));
|
||||
#270=IFCDIRECTION((0.,1.,0.));
|
||||
#271=IFCAXIS2PLACEMENT3D(#268,#269,#270);
|
||||
#272=IFCDIRECTION((0.,0.,1.));
|
||||
#273=IFCEXTRUDEDAREASOLID(#60,#271,#272,2.);
|
||||
#274=IFCSHAPEREPRESENTATION(#11,'Body','SolidModel',(#273));
|
||||
#275=IFCDIRECTION((0.,0.,1.));
|
||||
#276=IFCDIRECTION((1.,0.,0.));
|
||||
#277=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#278=IFCAXIS2PLACEMENT3D(#277,#275,#276);
|
||||
#279=IFCLOCALPLACEMENT(#267,#278);
|
||||
#280=IFCCARTESIANPOINT((0.,0.,2.));
|
||||
#281=IFCCARTESIANPOINT((2.,0.,2.));
|
||||
#282=IFCPOLYLINE((#280,#281));
|
||||
#283=IFCSHAPEREPRESENTATION(#12,'Axis','Curve3D',(#282));
|
||||
#284=IFCCOLOURRGB('Color(red=1.0, green=1.0, blue=1.0, opacity=1.0)',1.,1.,1.);
|
||||
#285=IFCSURFACESTYLESHADING(#284,$);
|
||||
#286=IFCSURFACESTYLE('Color(red=1.0, green=1.0, blue=1.0, opacity=1.0)',.BOTH.,(#285));
|
||||
#287=IFCSTYLEDITEM(#273,(#286),'Color(red=1.0, green=1.0, blue=1.0, opacity=1.0)');
|
||||
#288=IFCPRODUCTDEFINITIONSHAPE($,$,(#274,#283));
|
||||
#289=IFCBEAM('1cQ0RLLayHxflGv5utukF4',#24,'bm5','IPE220','Beam',#279,#288,$,$);
|
||||
#290=IFCMATERIALPROFILE('IPE220','A material profile',#62,#60,$,'LoadBearing');
|
||||
#291=IFCMATERIALPROFILESET('IPE220',$,(#290),$);
|
||||
#292=IFCMATERIALPROFILESETUSAGE(#291,5,$);
|
||||
#293=IFCRELASSOCIATESMATERIAL('1cXlcyLayHxhTpv5utukF4',#24,$,$,(#289),#292);
|
||||
#294=IFCRELCONTAINEDINSPATIALSTRUCTURE('1cXlczLayHxfxNv5utukF4',#24,'Physical model',$,(#98,#129,#164,#196,#227,#258,#289),#41);
|
||||
ENDSEC;
|
||||
END-ISO-10303-21;
|
||||
267
main.py
Normal file
267
main.py
Normal file
@ -0,0 +1,267 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
STP到GLB转换工具 - Python包装器
|
||||
调用STP2GLB.exe的友好命令行接口
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
import pathlib
|
||||
from typing import List, Optional, Dict, Any
|
||||
|
||||
|
||||
# 质量预设配置
|
||||
QUALITY_PRESETS = {
|
||||
'low': {'linear': 0.5, 'angular': 0.8},
|
||||
'medium': {'linear': 0.1, 'angular': 0.5}, # 默认值
|
||||
'high': {'linear': 0.01, 'angular': 0.1},
|
||||
'ultra': {'linear': 0.001, 'angular': 0.05},
|
||||
'custom': None # 使用用户指定值
|
||||
}
|
||||
|
||||
|
||||
def find_stp2glb_executable() -> Optional[str]:
|
||||
"""查找STP2GLB.exe可执行文件"""
|
||||
|
||||
# 1. 检查环境变量
|
||||
if 'STP2GLB_PATH' in os.environ:
|
||||
exe_path = os.environ['STP2GLB_PATH']
|
||||
if os.path.isfile(exe_path):
|
||||
return exe_path
|
||||
|
||||
# 2. 当前目录
|
||||
current_dir = pathlib.Path.cwd()
|
||||
exe_candidates = ['STP2GLB.exe', 'STP2GLB']
|
||||
|
||||
for exe_name in exe_candidates:
|
||||
exe_path = current_dir / exe_name
|
||||
if exe_path.is_file():
|
||||
return str(exe_path)
|
||||
|
||||
# 3. Pixi环境目录
|
||||
pixi_dirs = [
|
||||
current_dir / '.pixi' / 'envs' / 'dynamic' / 'Library' / 'bin',
|
||||
current_dir / '.pixi' / 'envs' / 'dynamic-debug' / 'Library' / 'bin',
|
||||
current_dir / '.pixi' / 'envs' / 'static' / 'Library' / 'bin',
|
||||
]
|
||||
|
||||
for pixi_dir in pixi_dirs:
|
||||
for exe_name in exe_candidates:
|
||||
exe_path = pixi_dir / exe_name
|
||||
if exe_path.is_file():
|
||||
return str(exe_path)
|
||||
|
||||
# 4. 构建目录
|
||||
build_dirs = list(current_dir.glob('build/*/'))
|
||||
for build_dir in build_dirs:
|
||||
for exe_name in exe_candidates:
|
||||
exe_path = build_dir / exe_name
|
||||
if exe_path.is_file():
|
||||
return str(exe_path)
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def validate_input_file(input_path: str) -> bool:
|
||||
"""验证输入文件"""
|
||||
if not os.path.isfile(input_path):
|
||||
print(f"错误: 输入文件不存在: {input_path}")
|
||||
return False
|
||||
|
||||
# 检查文件扩展名
|
||||
ext = pathlib.Path(input_path).suffix.lower()
|
||||
if ext not in ['.stp', '.step']:
|
||||
print(f"错误: 输入文件必须是.stp或.step格式,当前: {ext}")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def validate_output_file(output_path: str) -> bool:
|
||||
"""验证输出文件"""
|
||||
# 检查文件扩展名
|
||||
ext = pathlib.Path(output_path).suffix.lower()
|
||||
if ext != '.glb':
|
||||
print(f"错误: 输出文件必须是.glb格式,当前: {ext}")
|
||||
return False
|
||||
|
||||
# 检查目录是否存在
|
||||
output_dir = pathlib.Path(output_path).parent
|
||||
if not output_dir.exists():
|
||||
print(f"错误: 输出目录不存在: {output_dir}")
|
||||
return False
|
||||
|
||||
# 警告如果文件已存在
|
||||
if os.path.exists(output_path):
|
||||
print(f"警告: 输出文件已存在,将被覆盖: {output_path}")
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def build_command_args(args: argparse.Namespace, exe_path: str) -> List[str]:
|
||||
"""构建命令行参数"""
|
||||
cmd_args = [exe_path]
|
||||
|
||||
# 必需参数
|
||||
cmd_args.extend(['--stp', args.input])
|
||||
cmd_args.extend(['--glb', args.output])
|
||||
|
||||
# 质量预设或自定义偏差值
|
||||
if args.quality == 'custom':
|
||||
if args.linear_deflection is not None:
|
||||
cmd_args.extend(['--lin-defl', str(args.linear_deflection)])
|
||||
if args.angular_deflection is not None:
|
||||
cmd_args.extend(['--ang-defl', str(args.angular_deflection)])
|
||||
else:
|
||||
preset = QUALITY_PRESETS[args.quality]
|
||||
cmd_args.extend(['--lin-defl', str(preset['linear'])])
|
||||
cmd_args.extend(['--ang-defl', str(preset['angular'])])
|
||||
|
||||
# 可选标志
|
||||
if args.debug:
|
||||
cmd_args.append('--debug')
|
||||
|
||||
if args.solid_only:
|
||||
cmd_args.append('--solid-only')
|
||||
|
||||
return cmd_args
|
||||
|
||||
|
||||
def run_conversion(cmd_args: List[str]) -> int:
|
||||
"""运行转换程序"""
|
||||
try:
|
||||
print("开始转换...")
|
||||
print(f"执行命令: {' '.join(cmd_args)}")
|
||||
print("-" * 50)
|
||||
|
||||
# 运行命令并实时显示输出
|
||||
process = subprocess.run(
|
||||
cmd_args,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
text=True,
|
||||
encoding='utf-8'
|
||||
)
|
||||
|
||||
# 显示输出
|
||||
if process.stdout:
|
||||
print(process.stdout)
|
||||
|
||||
if process.returncode == 0:
|
||||
print("-" * 50)
|
||||
print("转换完成!")
|
||||
else:
|
||||
print("-" * 50)
|
||||
print(f"转换失败,退出码: {process.returncode}")
|
||||
|
||||
return process.returncode
|
||||
|
||||
except Exception as e:
|
||||
print(f"执行错误: {e}")
|
||||
return 1
|
||||
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
parser = argparse.ArgumentParser(
|
||||
description='STP到GLB转换工具 - 默认使用层级转换并保留原始组件名称',
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter
|
||||
)
|
||||
|
||||
# 位置参数
|
||||
parser.add_argument('input', help='输入的STP文件路径')
|
||||
parser.add_argument('output', help='输出的GLB文件路径')
|
||||
|
||||
# 可选参数
|
||||
parser.add_argument(
|
||||
'--linear-deflection',
|
||||
type=float,
|
||||
help='线性偏差 (仅在quality=custom时生效)'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--angular-deflection',
|
||||
type=float,
|
||||
help='角度偏差 (仅在quality=custom时生效)'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--quality',
|
||||
choices=['low', 'medium', 'high', 'ultra', 'custom'],
|
||||
default='medium',
|
||||
help='网格质量等级 (默认: medium)'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--debug',
|
||||
action='store_true',
|
||||
help='调试模式,提供详细的转换信息'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--solid-only',
|
||||
action='store_true',
|
||||
help='仅处理实体几何'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--no-scale',
|
||||
action='store_true',
|
||||
help='禁用自动缩放 (预留功能)'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--no-center',
|
||||
action='store_true',
|
||||
help='禁用自动居中 (预留功能)'
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# 验证自定义质量参数
|
||||
if args.quality == 'custom':
|
||||
if args.linear_deflection is None and args.angular_deflection is None:
|
||||
print("错误: 使用custom质量等级时,必须指定--linear-deflection或--angular-deflection")
|
||||
return 1
|
||||
|
||||
# 验证输入输出文件
|
||||
if not validate_input_file(args.input):
|
||||
return 1
|
||||
|
||||
if not validate_output_file(args.output):
|
||||
return 1
|
||||
|
||||
# 查找可执行文件
|
||||
exe_path = find_stp2glb_executable()
|
||||
if not exe_path:
|
||||
print("错误: 未找到STP2GLB.exe可执行文件")
|
||||
print("请确保:")
|
||||
print("1. 已编译项目 (pixi run build && pixi run install)")
|
||||
print("2. 或设置环境变量STP2GLB_PATH指向可执行文件")
|
||||
return 1
|
||||
|
||||
print(f"使用可执行文件: {exe_path}")
|
||||
|
||||
# 显示质量预设信息
|
||||
if args.quality != 'custom':
|
||||
preset = QUALITY_PRESETS[args.quality]
|
||||
print(f"质量等级: {args.quality}")
|
||||
print(f"线性偏差: {preset['linear']}, 角度偏差: {preset['angular']}")
|
||||
else:
|
||||
print("质量等级: custom")
|
||||
if args.linear_deflection is not None:
|
||||
print(f"线性偏差: {args.linear_deflection}")
|
||||
if args.angular_deflection is not None:
|
||||
print(f"角度偏差: {args.angular_deflection}")
|
||||
|
||||
# 预留功能提示
|
||||
if args.no_scale:
|
||||
print("注意: --no-scale 功能暂未实现")
|
||||
if args.no_center:
|
||||
print("注意: --no-center 功能暂未实现")
|
||||
|
||||
# 构建命令参数
|
||||
cmd_args = build_command_args(args, exe_path)
|
||||
|
||||
# 运行转换
|
||||
return run_conversion(cmd_args)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
118
pixi.toml
Normal file
118
pixi.toml
Normal file
@ -0,0 +1,118 @@
|
||||
[project]
|
||||
name = "stp2glb"
|
||||
version = "1.1.0"
|
||||
description = "A STEP to GLB converter"
|
||||
channels = ["https://repo.prefix.dev/adapy-tools", "conda-forge"]
|
||||
platforms = ["win-64", "linux-64"]
|
||||
|
||||
[environments]
|
||||
dynamic-shared = { features=["dynamic-shared"], no-default-feature = true }
|
||||
dynamic-debug = { features=["dynamic-debug", "dynamic-shared"], no-default-feature = true }
|
||||
dynamic = { features=["dynamic", "dynamic-shared"], no-default-feature = true }
|
||||
static = { features=["static"], no-default-feature = true }
|
||||
static-mingw = { features=["static-mingw"], no-default-feature = true }
|
||||
conda = { features=["conda"], no-default-feature = true }
|
||||
|
||||
# Dependencies
|
||||
|
||||
[feature.conda.dependencies]
|
||||
rattler-build="<0.35"
|
||||
|
||||
[feature.dynamic-shared.dependencies]
|
||||
c-compiler = "*"
|
||||
cxx-compiler = "*"
|
||||
cmake = "==3.30.5"
|
||||
cli11 = "*"
|
||||
ninja = "*"
|
||||
nlohmann_json = "*"
|
||||
tbb-devel = "*"
|
||||
|
||||
# Optional dependencies
|
||||
tinygltf = "==2.8.19"
|
||||
cgal-cpp = "==5.6.1"
|
||||
pytest = "==7.3.1"
|
||||
python = "3.12.*"
|
||||
|
||||
[feature.dynamic.target.linux-64.dependencies]
|
||||
occt = { version = "==7.8.1", build="*novtk*" }
|
||||
ifcopenshell = "*"
|
||||
|
||||
[feature.dynamic.target.win-64.dependencies]
|
||||
vs2022_win-64 = "*"
|
||||
vswhere = "*"
|
||||
occt = { version = "==7.8.1", build = "*novtk*", channel = "conda-forge" }
|
||||
stepcode = { version = "==0.8.2" }
|
||||
ifcopenshell = { version = "*" }
|
||||
|
||||
[feature.dynamic-debug.target.win-64.dependencies]
|
||||
vs2022_win-64 = "*"
|
||||
vswhere = "*"
|
||||
occt = { version = "==7.8.1", build = "*novtk*debug*", channel = "https://repo.prefix.dev/adapy-tools" }
|
||||
stepcode = { version = "==0.8.2", build = "*debug*" }
|
||||
ifcopenshell = { version = "*", build = "*debug*" }
|
||||
|
||||
[feature.static.dependencies]
|
||||
cmake = "==3.30.5"
|
||||
cli11 = "*"
|
||||
ninja = "*"
|
||||
nlohmann_json = "*"
|
||||
|
||||
[feature.static.target.linux-64.dependencies]
|
||||
c-compiler = "*"
|
||||
cxx-compiler = "*"
|
||||
lld="*"
|
||||
occt = { version = "==7.8.1", build = "*static_novtk*", channel = "https://repo.prefix.dev/adapy-tools" }
|
||||
|
||||
[feature.static.target.win-64.dependencies]
|
||||
vs2022_win-64 = "*"
|
||||
vswhere = "*"
|
||||
freeimage = "*"
|
||||
occt = { version = "==7.8.1", build = "*static_novtk*", channel = "https://repo.prefix.dev/adapy-tools" }
|
||||
|
||||
[feature.static-mingw.target.win-64.dependencies]
|
||||
cmake = "==3.30.5"
|
||||
cli11 = "*"
|
||||
ninja = "*"
|
||||
nlohmann_json = "*"
|
||||
m2w64-sysroot_win-64 = "*"
|
||||
gcc_win-64 = "13*"
|
||||
gxx_win-64 = "13*"
|
||||
ld_impl_win-64 = "*"
|
||||
#occt = { version = "==7.8.1", build = "*static*", channel = "https://repo.prefix.dev/adapy-tools" }
|
||||
|
||||
|
||||
# TASKS
|
||||
[feature.dynamic.target.win-64.tasks]
|
||||
build = { cmd=["cmake","-G","Ninja", "--preset", "win-dynamic-release", "--fresh"] }
|
||||
install = { cmd = ["ninja", "-C", "build/win-dynamic-release", "install"], description = "Install the project" }
|
||||
test = { cmd = ["ctest"], cwd="build/win-dynamic-release", description = "Run the tests" }
|
||||
|
||||
[feature.dynamic-debug.target.win-64.tasks]
|
||||
build = { cmd=["cmake","-G","Ninja", "--preset", "win-dynamic-debug", "--fresh"] }
|
||||
install = { cmd = ["ninja", "-C", "build/win-dynamic-debug", "install"], description = "Install the project" }
|
||||
test = { cmd = ["ctest"], cwd="build/win-dynamic-debug", description = "Run the tests" }
|
||||
|
||||
[feature.dynamic.target.linux-64.tasks]
|
||||
build = { cmd=["cmake","-G","Ninja", "--preset", "linux-dynamic", "--fresh"] }
|
||||
install = { cmd = ["ninja", "-C", "build/linux-dynamic", "install"], description = "Install the project" }
|
||||
test = { cmd = ["ctest"], cwd="build/linux-local", description = "Run the tests" }
|
||||
|
||||
[feature.static.target.win-64.tasks]
|
||||
build = { cmd=["cmake","-G","Ninja", "--preset", "win-static", "--fresh"] }
|
||||
install = { cmd = ["ninja", "-C", "build/win-static", "install"], description = "Install the project" }
|
||||
|
||||
[feature.static.target.linux-64.tasks]
|
||||
build = { cmd=["cmake","-G","Ninja", "--preset", "linux-static", "--fresh"] }
|
||||
install = { cmd = ["ninja", "-C", "build/linux-static", "install"], description = "Install the project" }
|
||||
|
||||
[feature.static-mingw.tasks]
|
||||
build = { cmd=["cmake","-G","Ninja", "--preset", "win-static-mingw", "--fresh"] }
|
||||
install = { cmd = ["ninja", "-C", "build/win-static-mingw", "install"], description = "Install the project" }
|
||||
|
||||
[feature.conda.target.win-64.tasks]
|
||||
build-occt = { cmd=["rattler-build", "build", "-r", "recipes/occt-static/recipe.yaml", "-m","recipes/occt-static/conda_build_config-win.yaml"] }
|
||||
build-stepcode = { cmd=["rattler-build", "build", "-r", "recipes/stepcode-static/recipe.yaml", "-m","recipes/stepcode-static/conda_build_config-win.yaml"] }
|
||||
|
||||
[feature.conda.target.linux-64.tasks]
|
||||
build-occt = { cmd=["rattler-build", "build", "-r", "recipes/occt-static/recipe.yaml", "-m","recipes/occt-static/conda_build_config-linux.yaml"] }
|
||||
build-stepcode = { cmd=["rattler-build", "build", "-r", "recipes/stepcode-static/recipe.yaml", "-m","recipes/stepcode-static/conda_build_config-linux.yaml"] }
|
||||
10
recipes/occt-static/CMakeLists.txt
Normal file
10
recipes/occt-static/CMakeLists.txt
Normal file
@ -0,0 +1,10 @@
|
||||
# a little test to see if occt is installed
|
||||
project(occt_test_cmake NONE)
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
find_package(OpenCASCADE)
|
||||
if (OpenCASCADE_MAJOR_VERSION)
|
||||
message(STATUS " Found OpenCASCADE version: ${OpenCASCADE_MAJOR_VERSION}.${OpenCASCADE_MINOR_VERSION}.${OpenCASCADE_MAINTENANCE_VERSION}")
|
||||
else(OpenCASCADE_MAJOR_VERSION)
|
||||
message(FATAL_ERROR "could not find OpenCASCADE")
|
||||
endif(OpenCASCADE_MAJOR_VERSION)
|
||||
36
recipes/occt-static/build.bat
Normal file
36
recipes/occt-static/build.bat
Normal file
@ -0,0 +1,36 @@
|
||||
@echo OFF
|
||||
|
||||
echo "Building OpenCASCADE Technology"
|
||||
set FNAME=occt-%PKG_VERSION%.tar.gz
|
||||
|
||||
REM Extract the .tar.gz file to the current directory
|
||||
7z x %FNAME% -aoa -o.
|
||||
|
||||
REM Extract the .tar file to the current directory
|
||||
7z x occt-%PKG_VERSION%.tar -aoa -o. occt*
|
||||
|
||||
REM Copy the contents from occt*/ to the current directory
|
||||
for /D %%d in (occt*) do xcopy "%%d\*" . /E /H /K /Y /Q
|
||||
|
||||
cmake -S . -B build -G Ninja ^
|
||||
-D CMAKE_PREFIX_PATH:FILEPATH="%LIBRARY_PREFIX%" ^
|
||||
-D CMAKE_LIBRARY_PATH:FILEPATH="%LIBRARY_PREFIX%/lib" ^
|
||||
-D CMAKE_INSTALL_PREFIX:FILEPATH="%LIBRARY_PREFIX%" ^
|
||||
-D INSTALL_DIR_LAYOUT="Unix" ^
|
||||
-D BUILD_MODULE_Draw:BOOL=OFF ^
|
||||
-D 3RDPARTY_DIR:FILEPATH="%LIBRARY_PREFIX%" ^
|
||||
-D CMAKE_BUILD_TYPE="Release" ^
|
||||
-D USE_TBB:BOOL=OFF ^
|
||||
-D USE_VTK:BOOL=OFF ^
|
||||
-D USE_FREEIMAGE:BOOL=ON ^
|
||||
-D USE_RAPIDJSON:BOOL=ON ^
|
||||
-D BUILD_LIBRARY_TYPE="Static" ^
|
||||
-D BUILD_RELEASE_DISABLE_EXCEPTIONS:BOOL=OFF ^
|
||||
-D CMAKE_C_FLAGS_RELEASE="/MT /O2 /Ob2 /DNDEBUG" ^
|
||||
-D CMAKE_CXX_FLAGS_RELEASE="/MT /O2 /Ob2 /DNDEBUG"
|
||||
|
||||
if errorlevel 1 exit 1
|
||||
|
||||
cmake --build build -- -v install
|
||||
|
||||
if errorlevel 1 exit 1
|
||||
26
recipes/occt-static/build.sh
Normal file
26
recipes/occt-static/build.sh
Normal file
@ -0,0 +1,26 @@
|
||||
# extract tar.gz to this dir
|
||||
tar --strip-components=1 -xvzf occt-${PKG_VERSION}.tar.gz
|
||||
|
||||
# Failing on exptocasexe-undefined-reference
|
||||
# https://dev.opencascade.org/content/installing-occt-libraries-static-libraries-fails-ubuntu-2204-exptocasexe-undefined-reference
|
||||
|
||||
cmake -S . -B build -G Ninja \
|
||||
-D CMAKE_FIND_ROOT_PATH="$PREFIX;$BUILD_PREFIX/$HOST/sysroot" \
|
||||
-D CMAKE_INSTALL_PREFIX:FILEPATH=$PREFIX \
|
||||
-D CMAKE_PREFIX_PATH:FILEPATH=$PREFIX \
|
||||
-D 3RDPARTY_DIR:FILEPATH=$PREFIX \
|
||||
-D BUILD_MODULE_Draw:BOOL=OFF \
|
||||
-D USE_TBB:BOOL=OFF \
|
||||
-D CMAKE_BUILD_TYPE:STRING="Release" \
|
||||
-D BUILD_RELEASE_DISABLE_EXCEPTIONS=OFF \
|
||||
-D USE_VTK:BOOL=OFF \
|
||||
-D USE_FREEIMAGE:BOOL=ON \
|
||||
-D USE_RAPIDJSON:BOOL=ON \
|
||||
-D BUILD_RELEASE_DISABLE_EXCEPTIONS:BOOL=OFF \
|
||||
-D BUILD_LIBRARY_TYPE="Static" \
|
||||
-D CMAKE_VERBOSE_MAKEFILE:BOOL=ON \
|
||||
-D BUILD_MODULE_DETools:BOOL=OFF \
|
||||
-D CMAKE_EXE_LINKER_FLAGS="-lpthread -ldl -lm" \
|
||||
-D BUILD_SHARED_LIBS:BOOL=OFF
|
||||
|
||||
cmake --build build -- install
|
||||
25
recipes/occt-static/conda_build_config-linux.yaml
Normal file
25
recipes/occt-static/conda_build_config-linux.yaml
Normal file
@ -0,0 +1,25 @@
|
||||
c_stdlib:
|
||||
- sysroot
|
||||
c_stdlib_version:
|
||||
- '2.17'
|
||||
cdt_name:
|
||||
- cos7
|
||||
channel_sources:
|
||||
- conda-forge
|
||||
channel_targets:
|
||||
- conda-forge main
|
||||
cxx_compiler:
|
||||
- gxx
|
||||
cxx_compiler_version:
|
||||
- '13'
|
||||
docker_image:
|
||||
- quay.io/condaforge/linux-anvil-cos7-x86_64
|
||||
fontconfig:
|
||||
- '2'
|
||||
freetype:
|
||||
- '2'
|
||||
target_platform:
|
||||
- linux-64
|
||||
zip_keys:
|
||||
- - c_stdlib_version
|
||||
- cdt_name
|
||||
28
recipes/occt-static/conda_build_config-win.yaml
Normal file
28
recipes/occt-static/conda_build_config-win.yaml
Normal file
@ -0,0 +1,28 @@
|
||||
c_compiler:
|
||||
- vs2022
|
||||
c_stdlib:
|
||||
- vs
|
||||
channel_sources:
|
||||
- conda-forge
|
||||
channel_targets:
|
||||
- conda-forge main
|
||||
cxx_compiler:
|
||||
- vs2022
|
||||
gmp:
|
||||
- '6'
|
||||
libboost_devel:
|
||||
- '1.86'
|
||||
libxml2:
|
||||
- '2'
|
||||
mpfr:
|
||||
- '4'
|
||||
pin_run_as_build:
|
||||
python:
|
||||
min_pin: x.x
|
||||
max_pin: x.x
|
||||
python:
|
||||
- 3.12.* *_cpython
|
||||
target_platform:
|
||||
- win-64
|
||||
zlib:
|
||||
- '1'
|
||||
82
recipes/occt-static/recipe.yaml
Normal file
82
recipes/occt-static/recipe.yaml
Normal file
@ -0,0 +1,82 @@
|
||||
schema_version: 1
|
||||
|
||||
context:
|
||||
name: occt
|
||||
version: 7.8.1
|
||||
build: 3
|
||||
|
||||
package:
|
||||
name: ${{ name }}
|
||||
version: ${{ version }}
|
||||
|
||||
source:
|
||||
url: http://git.dev.opencascade.org/gitweb/?p=occt.git;a=snapshot;h=V${{ version | replace(".", "_") }};sf=tgz
|
||||
sha256: 006319a932cb8d76f32455bf4524eca2100657cb0cc65526754fd8da83b3924f
|
||||
file_name: ${{ name }}-${{ version }}.tar.gz
|
||||
|
||||
build:
|
||||
number: ${{ build }}
|
||||
string: static_novtk_h${{ hash }}_${{ build }}
|
||||
|
||||
requirements:
|
||||
build:
|
||||
- ${{ compiler('cxx') }}
|
||||
- ${{ stdlib("c") }}
|
||||
- cmake
|
||||
- ninja
|
||||
- if: win
|
||||
then:
|
||||
- 7zip
|
||||
- if: linux
|
||||
then:
|
||||
- ${{ cdt('mesa-libgl-devel') }}
|
||||
- ${{ cdt('libxi-devel') }}
|
||||
- ${{ cdt('libxext-devel') }}
|
||||
host:
|
||||
- freetype
|
||||
- freeimage
|
||||
- fontconfig
|
||||
- if: linux
|
||||
then:
|
||||
- xorg-libx11
|
||||
- xorg-xproto
|
||||
- pthread-stubs
|
||||
- rapidjson
|
||||
run:
|
||||
- freetype
|
||||
- freeimage
|
||||
- fontconfig
|
||||
- if: linux
|
||||
then: xorg-libxt
|
||||
- rapidjson
|
||||
run_exports:
|
||||
- ${{ pin_subpackage('occt', upper_bound='x.x.x') }}
|
||||
|
||||
tests:
|
||||
- files:
|
||||
recipe:
|
||||
- CMakeLists.txt
|
||||
requirements:
|
||||
run:
|
||||
- ninja
|
||||
- cmake
|
||||
script:
|
||||
- "cmake -G \"Ninja\" ."
|
||||
|
||||
about:
|
||||
license: LGPL-2.1-only
|
||||
license_file: LICENSE_LGPL_21.txt
|
||||
summary: this is the occ (opencascade) CAD-Kernel
|
||||
description: |
|
||||
Open Cascade Technology (OCCT), formerly called CAS.CADE
|
||||
is an open source software development platform for 3D CAD,
|
||||
CAM, CAE, etc. that is developed and supported by Open Cascade SAS.
|
||||
homepage: https://www.opencascade.com/
|
||||
repository: http://git.dev.opencascade.org/gitweb/?p=occt.git
|
||||
documentation: https://www.opencascade.com/content/documentation
|
||||
|
||||
extra:
|
||||
recipe-maintainers:
|
||||
- adrianinsaval
|
||||
- looooo
|
||||
|
||||
15
recipes/stepcode-static/build.bat
Normal file
15
recipes/stepcode-static/build.bat
Normal file
@ -0,0 +1,15 @@
|
||||
cmake -S . -B build -G Ninja ^
|
||||
-D CMAKE_BUILD_TYPE="Release" ^
|
||||
-D CMAKE_PREFIX_PATH:FILEPATH="%LIBRARY_PREFIX%" ^
|
||||
-D CMAKE_LIBRARY_PATH:FILEPATH="%LIBRARY_PREFIX%/lib" ^
|
||||
-D CMAKE_INSTALL_PREFIX:FILEPATH="%LIBRARY_PREFIX%" ^
|
||||
-D BUILD_SHARED_LIBS=OFF ^
|
||||
-D BUILD_STATIC_LIBS=ON ^
|
||||
-D CMAKE_C_FLAGS_RELEASE="/MT /O2 /Ob2 /DNDEBUG" ^
|
||||
-D CMAKE_CXX_FLAGS_RELEASE="/MT /O2 /Ob2 /DNDEBUG"
|
||||
|
||||
if errorlevel 1 exit 1
|
||||
|
||||
cmake --build build -- -v install
|
||||
|
||||
if errorlevel 1 exit 1
|
||||
13
recipes/stepcode-static/build.sh
Normal file
13
recipes/stepcode-static/build.sh
Normal file
@ -0,0 +1,13 @@
|
||||
cmake -S . -B build -G Ninja \
|
||||
-D SC_BUILD_TYPE="Release" \
|
||||
-D CMAKE_BUILD_TYPE="Release" \
|
||||
-D CMAKE_PREFIX_PATH:FILEPATH="${PREFIX}" \
|
||||
-D CMAKE_LIBRARY_PATH:FILEPATH="${PREFIX}/lib" \
|
||||
-D CMAKE_INSTALL_PREFIX:FILEPATH="${PREFIX}" \
|
||||
-D BUILD_SHARED_LIBS=OFF \
|
||||
-D ENABLE_ALL=ON \
|
||||
-D BUILD_STATIC_LIBS=ON \
|
||||
-D SC_CPP_GENERATOR:BOOL=ON \
|
||||
-D SC_PYTHON_GENERATOR:BOOL=ON
|
||||
|
||||
cmake --build build -- -v install
|
||||
25
recipes/stepcode-static/conda_build_config-linux.yaml
Normal file
25
recipes/stepcode-static/conda_build_config-linux.yaml
Normal file
@ -0,0 +1,25 @@
|
||||
c_stdlib:
|
||||
- sysroot
|
||||
c_stdlib_version:
|
||||
- '2.17'
|
||||
cdt_name:
|
||||
- cos7
|
||||
channel_sources:
|
||||
- conda-forge
|
||||
channel_targets:
|
||||
- conda-forge main
|
||||
cxx_compiler:
|
||||
- gxx
|
||||
cxx_compiler_version:
|
||||
- '13'
|
||||
docker_image:
|
||||
- quay.io/condaforge/linux-anvil-cos7-x86_64
|
||||
fontconfig:
|
||||
- '2'
|
||||
freetype:
|
||||
- '2'
|
||||
target_platform:
|
||||
- linux-64
|
||||
zip_keys:
|
||||
- - c_stdlib_version
|
||||
- cdt_name
|
||||
14
recipes/stepcode-static/conda_build_config-win.yaml
Normal file
14
recipes/stepcode-static/conda_build_config-win.yaml
Normal file
@ -0,0 +1,14 @@
|
||||
c_compiler:
|
||||
- vs2022
|
||||
c_stdlib:
|
||||
- vs
|
||||
channel_sources:
|
||||
- conda-forge
|
||||
channel_targets:
|
||||
- conda-forge main
|
||||
cxx_compiler:
|
||||
- vs2022
|
||||
target_platform:
|
||||
- win-64
|
||||
zlib:
|
||||
- '1'
|
||||
48
recipes/stepcode-static/recipe.yaml
Normal file
48
recipes/stepcode-static/recipe.yaml
Normal file
@ -0,0 +1,48 @@
|
||||
schema_version: 1
|
||||
|
||||
context:
|
||||
name: stepcode
|
||||
version: 0.8.2
|
||||
build: 0
|
||||
|
||||
package:
|
||||
name: ${{ name }}
|
||||
version: ${{ version }}
|
||||
|
||||
source:
|
||||
url: https://github.com/stepcode/stepcode/archive/refs/tags/v${{ version }}.tar.gz
|
||||
sha256: a0307185313a55e127db4476bf234b9ea90edfeee6ac7a57923f7608a48d91e5
|
||||
|
||||
build:
|
||||
number: ${{ build }}
|
||||
string: static_h${{ PKG_HASH }}_${{ build }}
|
||||
|
||||
requirements:
|
||||
build:
|
||||
- ${{ compiler('cxx') }}
|
||||
- ${{ stdlib("c") }}
|
||||
- if: unix
|
||||
then: lld
|
||||
- cmake
|
||||
- ninja
|
||||
run_exports:
|
||||
- ${{ pin_subpackage(name, upper_bound='x.x.x') }}
|
||||
|
||||
about:
|
||||
license: 0BSD
|
||||
license_file: COPYING
|
||||
summary: Data Exchange with ISO 10303 (STEP) standards
|
||||
description: |
|
||||
STEPcode (formerly NIST's STEP Class Library) is used with IFC, STEP,
|
||||
and other standards that utilize the technologies of ISO10303 (STEP).
|
||||
It generates C++ and Python from EXPRESS (10303-11) schemas. The code is
|
||||
capable of reading and writing STEP Part 21 exchange files.
|
||||
It also utilizes Parts 22 and 23 (SDAI and its C++ binding).
|
||||
homepage: https://stepcode.github.io/
|
||||
repository: https://github.com/stepcode/stepcode
|
||||
documentation: https://stepcode.github.io/docs/home/
|
||||
|
||||
extra:
|
||||
recipe-maintainers:
|
||||
- Krande
|
||||
|
||||
37
src/cadit/ifc/ifcop.cpp
Normal file
37
src/cadit/ifc/ifcop.cpp
Normal file
@ -0,0 +1,37 @@
|
||||
#include "ifcop.h"
|
||||
#include "ifcparse/Ifc4x1.h"
|
||||
#include "ifcparse/IfcFile.h"
|
||||
//#include "ifcparse/IfcHierarchyHelper.h"
|
||||
//#include "ifcparse/IfcBaseClass.h"
|
||||
//#include "ifcgeom_schema_agnostic/Serialization.h"
|
||||
#define IfcSchema Ifc4x1
|
||||
|
||||
|
||||
using namespace std::string_literals;
|
||||
|
||||
|
||||
int read_ifc_file(const std::string &file_name) {
|
||||
IfcParse::IfcFile file(file_name);
|
||||
if (!file.good()) {
|
||||
std::cout << "Unable to parse .ifc file" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// std::cout << "file name: " << file.header().file_name().name() << std::endl;
|
||||
|
||||
IfcSchema::IfcBeam::list::ptr elements = file.instances_by_type<IfcSchema::IfcBeam>();
|
||||
|
||||
std::cout << "Found " << elements->size() << " elements in " << file_name << ":" << std::endl;
|
||||
|
||||
for (auto element : *elements) {
|
||||
|
||||
std::cout << element->data().toString() << std::endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void ifc_module(nb::module_ &m) {
|
||||
m.def("read_ifc_file", &read_ifc_file, "file_name"_a, "Read an ifc file");
|
||||
}
|
||||
4
src/cadit/ifc/ifcop.h
Normal file
4
src/cadit/ifc/ifcop.h
Normal file
@ -0,0 +1,4 @@
|
||||
#include "../../binding_core.h"
|
||||
|
||||
|
||||
void ifc_module(nb::module_ &m);
|
||||
115
src/cadit/occt/bsplinesurf.cpp
Normal file
115
src/cadit/occt/bsplinesurf.cpp
Normal file
@ -0,0 +1,115 @@
|
||||
#include <BRepBuilderAPI_MakeEdge.hxx>
|
||||
#include <BRepBuilderAPI_MakeWire.hxx>
|
||||
#include <BRepBuilderAPI_MakeFace.hxx>
|
||||
#include <Geom_BSplineSurface.hxx>
|
||||
#include <Geom2d_Line.hxx>
|
||||
#include <TColgp_Array2OfPnt.hxx>
|
||||
#include <TColStd_Array1OfReal.hxx>
|
||||
#include <TopoDS_Shell.hxx>
|
||||
#include <TopoDS_Face.hxx>
|
||||
#include <STEPControl_Writer.hxx>
|
||||
#include <TopoDS_Wire.hxx>
|
||||
#include <TopoDS_Edge.hxx>
|
||||
#include <BRep_Builder.hxx>
|
||||
#include <gp_Lin2d.hxx>
|
||||
#include <gp_Pnt2d.hxx>
|
||||
#include <Geom2d_TrimmedCurve.hxx>
|
||||
#include <GeomAPI_ProjectPointOnSurf.hxx>
|
||||
#include <Precision.hxx>
|
||||
#include <TopLoc_Location.hxx> // For location
|
||||
#include <filesystem>
|
||||
#include "../../config_structs.h"
|
||||
|
||||
void make_a_bspline_surf(const GlobalConfig& config) {
|
||||
|
||||
// Define the control points for the B-Spline surface from STEP file
|
||||
TColgp_Array2OfPnt controlPoints(1, 4, 1, 2); // Adjust range as needed
|
||||
controlPoints.SetValue(1, 1, gp_Pnt(-0.5, 0.5, 0.0));
|
||||
controlPoints.SetValue(2, 1, gp_Pnt(-0.561, 0.272, 0.333));
|
||||
controlPoints.SetValue(3, 1, gp_Pnt(-0.622, 0.045, 0.667));
|
||||
controlPoints.SetValue(4, 1, gp_Pnt(-0.683, -0.183, 1.0));
|
||||
|
||||
controlPoints.SetValue(1, 2, gp_Pnt(-0.5, -0.5, 0.0));
|
||||
controlPoints.SetValue(2, 2, gp_Pnt(-0.272, -0.561, 0.333));
|
||||
controlPoints.SetValue(3, 2, gp_Pnt(-0.045, -0.622, 0.667));
|
||||
controlPoints.SetValue(4, 2, gp_Pnt(0.183, -0.683, 1.0));
|
||||
|
||||
// Define knots and multiplicities
|
||||
TColStd_Array1OfReal uKnots(1, 2);
|
||||
uKnots.SetValue(1, 0.0);
|
||||
uKnots.SetValue(2, 1224.744871392); // From STEP data
|
||||
|
||||
TColStd_Array1OfReal vKnots(1, 2);
|
||||
vKnots.SetValue(1, 3.0);
|
||||
vKnots.SetValue(2, 4.0); // From STEP data
|
||||
|
||||
TColStd_Array1OfInteger uMults(1, 2);
|
||||
uMults.SetValue(1, 4);
|
||||
uMults.SetValue(2, 4);
|
||||
|
||||
TColStd_Array1OfInteger vMults(1, 2);
|
||||
vMults.SetValue(1, 2);
|
||||
vMults.SetValue(2, 2);
|
||||
|
||||
// Create the B-Spline surface
|
||||
Handle(Geom_BSplineSurface) bsplineSurface = new Geom_BSplineSurface(
|
||||
controlPoints, uKnots, vKnots, uMults, vMults, 3, 1, Standard_False, Standard_False
|
||||
);
|
||||
|
||||
// Create boundary points and 3D edges
|
||||
gp_Pnt p1(-0.5, 0.5, 0.0); // Point from #55
|
||||
gp_Pnt p2(-0.5, -0.5, 0.0); // Point from #56
|
||||
gp_Pnt p3(0.183, -0.683, 1.0); // Point from #62
|
||||
gp_Pnt p4(-0.683, -0.183, 1.0); // Point from #61
|
||||
|
||||
TopoDS_Edge edge1 = BRepBuilderAPI_MakeEdge(p1, p2); // Edge from point #55 to #56
|
||||
TopoDS_Edge edge2 = BRepBuilderAPI_MakeEdge(p2, p3); // Edge from point #56 to #62
|
||||
TopoDS_Edge edge3 = BRepBuilderAPI_MakeEdge(p3, p4); // Edge from point #62 to #61
|
||||
TopoDS_Edge edge4 = BRepBuilderAPI_MakeEdge(p4, p1); // Edge from point #61 to #55
|
||||
|
||||
// Create corresponding 2D curves in the parametric space (u-v space) of the B-Spline surface
|
||||
gp_Pnt2d uv1(0.0, 0.0); // Parametric coordinates corresponding to p1 (u, v)
|
||||
gp_Pnt2d uv2(0.0, 1.0); // Parametric coordinates corresponding to p2 (u, v)
|
||||
gp_Pnt2d uv3(1.0, 1.0); // Parametric coordinates corresponding to p3 (u, v)
|
||||
gp_Pnt2d uv4(1.0, 0.0); // Parametric coordinates corresponding to p4 (u, v)
|
||||
|
||||
Handle(Geom2d_TrimmedCurve) c2dEdge1 = new Geom2d_TrimmedCurve(new Geom2d_Line(gp_Lin2d(uv1, gp_Dir2d(uv2.XY() - uv1.XY()))), 0.0, 1.0);
|
||||
Handle(Geom2d_TrimmedCurve) c2dEdge2 = new Geom2d_TrimmedCurve(new Geom2d_Line(gp_Lin2d(uv2, gp_Dir2d(uv3.XY() - uv2.XY()))), 0.0, 1.0);
|
||||
Handle(Geom2d_TrimmedCurve) c2dEdge3 = new Geom2d_TrimmedCurve(new Geom2d_Line(gp_Lin2d(uv3, gp_Dir2d(uv4.XY() - uv3.XY()))), 0.0, 1.0);
|
||||
Handle(Geom2d_TrimmedCurve) c2dEdge4 = new Geom2d_TrimmedCurve(new Geom2d_Line(gp_Lin2d(uv4, gp_Dir2d(uv1.XY() - uv4.XY()))), 0.0, 1.0);
|
||||
|
||||
// Assign the 2D curves to the edges on the B-Spline surface using the correct signature
|
||||
BRep_Builder builder;
|
||||
TopLoc_Location identityLocation; // No transformation (identity)
|
||||
builder.UpdateEdge(edge1, c2dEdge1, bsplineSurface, identityLocation, Precision::Confusion());
|
||||
builder.UpdateEdge(edge2, c2dEdge2, bsplineSurface, identityLocation, Precision::Confusion());
|
||||
builder.UpdateEdge(edge3, c2dEdge3, bsplineSurface, identityLocation, Precision::Confusion());
|
||||
builder.UpdateEdge(edge4, c2dEdge4, bsplineSurface, identityLocation, Precision::Confusion());
|
||||
|
||||
// Create a wire from the edges to represent the boundary
|
||||
BRepBuilderAPI_MakeWire wireMaker;
|
||||
wireMaker.Add(edge1);
|
||||
wireMaker.Add(edge2);
|
||||
wireMaker.Add(edge3);
|
||||
wireMaker.Add(edge4);
|
||||
TopoDS_Wire wire = wireMaker.Wire();
|
||||
|
||||
// Create a face from the B-Spline surface with the boundary wire
|
||||
TopoDS_Face face = BRepBuilderAPI_MakeFace(bsplineSurface, wire);
|
||||
|
||||
// Optionally, update the face tolerance
|
||||
builder.UpdateFace(face, 1e-6); // Set tolerance if needed
|
||||
|
||||
// Create a shell and add the face
|
||||
TopoDS_Shell shell;
|
||||
builder.MakeShell(shell);
|
||||
builder.Add(shell, face);
|
||||
|
||||
shell.Closed(Standard_True); // Set the shell as closed
|
||||
|
||||
// Export to STEP file if needed
|
||||
STEPControl_Writer writer;
|
||||
writer.Transfer(shell, STEPControl_AsIs);
|
||||
writer.Write(config.stpFile.string().c_str());
|
||||
|
||||
}
|
||||
12
src/cadit/occt/bsplinesurf.h
Normal file
12
src/cadit/occt/bsplinesurf.h
Normal file
@ -0,0 +1,12 @@
|
||||
//
|
||||
// Created by ofskrand on 13.09.2024.
|
||||
//
|
||||
|
||||
#ifndef STP2GLB_BSPLINESURF_H
|
||||
#define STP2GLB_BSPLINESURF_H
|
||||
|
||||
#include "../../config_structs.h"
|
||||
|
||||
void make_a_bspline_surf(const GlobalConfig& config);
|
||||
|
||||
#endif //STP2GLB_BSPLINESURF_H
|
||||
141
src/cadit/occt/convert.cpp
Normal file
141
src/cadit/occt/convert.cpp
Normal file
@ -0,0 +1,141 @@
|
||||
#include "convert.h"
|
||||
#include <STEPCAFControl_Reader.hxx>
|
||||
#include <XCAFDoc_DocumentTool.hxx>
|
||||
#include <RWGltf_CafWriter.hxx>
|
||||
#include <TDocStd_Document.hxx>
|
||||
#include <BRepMesh_IncrementalMesh.hxx>
|
||||
#include <filesystem>
|
||||
#include <XSControl_TransferReader.hxx>
|
||||
#include <Transfer_TransientProcess.hxx>
|
||||
#include <XCAFDoc_ShapeTool.hxx>
|
||||
#include <Interface_Graph.hxx>
|
||||
#include <Interface_Static.hxx>
|
||||
#include <Standard_Type.hxx>
|
||||
#include <map>
|
||||
#include <StepData_StepModel.hxx>
|
||||
#include <XSControl_WorkSession.hxx>
|
||||
|
||||
#include "custom_progress.h"
|
||||
#include "geometry_iterator.h"
|
||||
#include "step_helpers.h"
|
||||
#include "step_tree.h"
|
||||
#include "../../config_structs.h"
|
||||
|
||||
void convert_stp_to_glb(const GlobalConfig& config)
|
||||
{
|
||||
// Initialize the STEPCAFControl_Reader
|
||||
STEPCAFControl_Reader reader;
|
||||
reader.SetColorMode(true);
|
||||
reader.SetNameMode(true);
|
||||
reader.SetLayerMode(true);
|
||||
reader.SetPropsMode(false);
|
||||
reader.SetGDTMode(true);
|
||||
reader.SetMatMode(true);
|
||||
reader.SetViewMode(false);
|
||||
|
||||
// Mesh parameters
|
||||
IMeshTools_Parameters meshParams;
|
||||
meshParams.Angle = config.angularDeflection;
|
||||
meshParams.Deflection = config.linearDeflection;
|
||||
meshParams.Relative = config.relativeDeflection;
|
||||
meshParams.MinSize = 0.1;
|
||||
meshParams.AngleInterior = 0.5;
|
||||
meshParams.DeflectionInterior = 0.1;
|
||||
meshParams.CleanModel = Standard_True;
|
||||
meshParams.InParallel = Standard_True;
|
||||
meshParams.AllowQualityDecrease = Standard_True;
|
||||
|
||||
// Read the STEP file
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
std::cout << "Reading STEP file: " << config.stpFile << std::endl;
|
||||
if (reader.ReadFile(config.stpFile.string().c_str()) != IFSelect_RetDone)
|
||||
throw std::runtime_error("Error reading STEP file");
|
||||
auto stop = std::chrono::high_resolution_clock::now();
|
||||
auto duration = std::chrono::duration<double>(stop - start).count();
|
||||
|
||||
std::cout << "Read complete in " << std::fixed << std::setprecision(2) << duration << " seconds" << "\n";
|
||||
|
||||
// Transfer to a document
|
||||
std::cout << "Transferring data to document" << "\n";
|
||||
start = std::chrono::high_resolution_clock::now();
|
||||
const Handle(TDocStd_Document) doc = new TDocStd_Document("MDTV-XCAF");
|
||||
|
||||
Handle(CustomProgressIndicator) progress_indicator = new CustomProgressIndicator();
|
||||
|
||||
// Create a progress range with a default name and range
|
||||
if (Message_ProgressRange progressRange = progress_indicator->Start(); !reader.Transfer(doc, progressRange))
|
||||
throw std::runtime_error("Error transferring data to document");
|
||||
|
||||
stop = std::chrono::high_resolution_clock::now();
|
||||
duration = std::chrono::duration<double>(stop - start).count();
|
||||
std::cout << "Transfer complete in " << std::fixed << std::setprecision(2) << duration << " seconds" << "\n";
|
||||
|
||||
// Tessellation
|
||||
const Handle(XCAFDoc_ShapeTool) shapeTool = XCAFDoc_DocumentTool::ShapeTool(doc->Main());
|
||||
|
||||
TDF_LabelSequence labelSeq;
|
||||
shapeTool->GetShapes(labelSeq);
|
||||
std::vector<ProcessResult> failed_nodes;
|
||||
|
||||
std::cout << "Beginning tessellation of shapes: " << labelSeq.Length() << "\n";
|
||||
start = std::chrono::high_resolution_clock::now();
|
||||
for (Standard_Integer i = 1; i <= labelSeq.Length(); ++i)
|
||||
{
|
||||
auto label = labelSeq.Value(i);
|
||||
TopoDS_Shape shape = XCAFDoc_ShapeTool::GetShape(label);
|
||||
std::cout << "Tessellating shape " << i << " of " << labelSeq.Length() << std::endl;
|
||||
if (!perform_tessellation_with_timeout(shape, meshParams, config.tessellation_timout, progress_indicator)) {
|
||||
std::cout << "Tessellation timed out.\n";
|
||||
// get entity from shape
|
||||
auto result = ProcessResult();
|
||||
result.added_to_model = false;
|
||||
result.geometryIndex = i;
|
||||
result.skip_reason = "Tessellation timed out";
|
||||
failed_nodes.push_back(result);
|
||||
}
|
||||
}
|
||||
stop = std::chrono::high_resolution_clock::now();
|
||||
duration = std::chrono::duration<double>(stop - start).count();
|
||||
std::cout << "Tessellation complete in " << std::fixed << std::setprecision(2) << duration << " seconds" << "\n";
|
||||
|
||||
// Write to GLB
|
||||
std:: cout << "Writing to GLB file: " << config.glbFile << "\n";
|
||||
start = std::chrono::high_resolution_clock::now();
|
||||
RWGltf_CafWriter writer(config.glbFile.c_str(), true); // true for binary format
|
||||
|
||||
// Additional file information (can be empty if not needed)
|
||||
const TColStd_IndexedDataMapOfStringString file_info;
|
||||
|
||||
// Progress indicator (can be null if progress tracking is not needed)
|
||||
const Message_ProgressRange progress;
|
||||
|
||||
// if output parent directory is != "" and does not exist, create it
|
||||
if (const std::filesystem::path glb_dir = config.glbFile.parent_path(); !glb_dir.empty() && !exists(glb_dir))
|
||||
{
|
||||
create_directories(glb_dir);
|
||||
}
|
||||
|
||||
if (!writer.Perform(doc, file_info, progress))
|
||||
{
|
||||
throw std::runtime_error("Error writing GLB file");
|
||||
}
|
||||
stop = std::chrono::high_resolution_clock::now();
|
||||
duration = std::chrono::duration<double>(stop - start).count();
|
||||
std::cout << "Write complete in " << std::fixed << std::setprecision(2) << duration << " seconds" << "\n";
|
||||
|
||||
// iterate over all nodes that weren't added to the model and save the list to json
|
||||
const std::filesystem::path out_json_log_file = config.glbFile.parent_path() / config.glbFile.stem().concat(
|
||||
"-log.json");
|
||||
std::ofstream log_file(out_json_log_file);
|
||||
log_file << "[\n";
|
||||
|
||||
for (const auto &result: failed_nodes) {
|
||||
log_file << "{\n";
|
||||
log_file << "\"geometryIndex\": " << result.geometryIndex << ",\n";
|
||||
log_file << R"("skipReason": ")" << result.skip_reason << "\"\n";
|
||||
log_file << "},\n";
|
||||
}
|
||||
log_file << "]\n";
|
||||
log_file.close();
|
||||
}
|
||||
|
||||
13
src/cadit/occt/convert.h
Normal file
13
src/cadit/occt/convert.h
Normal file
@ -0,0 +1,13 @@
|
||||
//
|
||||
// Created by ofskrand on 12.09.2024.
|
||||
//
|
||||
|
||||
#ifndef STEP_TO_GLB_V1_H
|
||||
#define STEP_TO_GLB_V1_H
|
||||
|
||||
#include "../../config_structs.h"
|
||||
|
||||
// Function to convert STEP file to GLB file
|
||||
void convert_stp_to_glb(const GlobalConfig& config);
|
||||
|
||||
#endif //STEP_TO_GLB_V1_H
|
||||
80
src/cadit/occt/custom_progress.cpp
Normal file
80
src/cadit/occt/custom_progress.cpp
Normal file
@ -0,0 +1,80 @@
|
||||
#include "custom_progress.h"
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
|
||||
CustomProgressIndicator::CustomProgressIndicator() : shouldCancel(false), progress(0.0), lastProgress(-1.0) {
|
||||
// Start the progress update thread
|
||||
progressThread = std::thread(&CustomProgressIndicator::UpdateProgress, this);
|
||||
}
|
||||
|
||||
CustomProgressIndicator::~CustomProgressIndicator() {
|
||||
// Stop the progress update thread safely
|
||||
if (progressThread.joinable()) {
|
||||
shouldCancel = true;
|
||||
progressThread.join();
|
||||
}
|
||||
}
|
||||
|
||||
Standard_Boolean CustomProgressIndicator::UserBreak() {
|
||||
return shouldCancel; // Return true to stop the operation
|
||||
}
|
||||
|
||||
void CustomProgressIndicator::Show(const Message_ProgressScope &theScope, const Standard_Boolean isForce) {
|
||||
// This function will be used to set progress from your existing logic
|
||||
const double newProgress = GetPosition(); // Get current progress [0.0, 1.0]
|
||||
SetProgress(newProgress); // Update progress asynchronously
|
||||
}
|
||||
|
||||
void CustomProgressIndicator::Cancel() {
|
||||
shouldCancel = true;
|
||||
}
|
||||
|
||||
void CustomProgressIndicator::Reset() {
|
||||
shouldCancel = false;
|
||||
SetProgress(0.0); // Reset progress to 0
|
||||
}
|
||||
|
||||
void CustomProgressIndicator::SetProgress(double newProgress) {
|
||||
std::lock_guard<std::mutex> lock(mtx);
|
||||
progress = newProgress;
|
||||
}
|
||||
|
||||
void CustomProgressIndicator::UpdateProgress() {
|
||||
while (!shouldCancel) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Update progress every 100ms
|
||||
|
||||
// Lock the mutex to safely access the progress value
|
||||
std::lock_guard<std::mutex> lock(mtx);
|
||||
|
||||
// Only update the progress if it has changed by at least 5%
|
||||
if (static_cast<int>(progress * 100) / 5 > static_cast<int>(lastProgress * 100) / 5) {
|
||||
lastProgress = progress;
|
||||
|
||||
const int barWidth = 50; // Width of the progress bar
|
||||
const int pos = static_cast<int>(progress * barWidth);
|
||||
|
||||
// Lock std::cout for thread safety to avoid race conditions
|
||||
std::lock_guard<std::mutex> coutLock(coutMutex);
|
||||
|
||||
// Clear the line before printing the new progress
|
||||
std::cout << "\r" << std::string(80, ' '); // Clear the current line (adjust the size if necessary)
|
||||
std::cout << "\r[";
|
||||
|
||||
// Build the progress bar
|
||||
for (int i = 0; i < barWidth; ++i) {
|
||||
if (i < pos) std::cout << "="; // Completed part
|
||||
else if (i == pos) std::cout << ">"; // Current progress
|
||||
else std::cout << " "; // Remaining part
|
||||
}
|
||||
|
||||
std::cout << "] " << static_cast<int>(progress * 100.0) << "% " << std::flush;
|
||||
}
|
||||
}
|
||||
|
||||
// Finalize the progress bar when the task is done
|
||||
std::lock_guard<std::mutex> coutLock(coutMutex);
|
||||
std::cout << "\r[==================================================] 100%" << std::endl;
|
||||
}
|
||||
52
src/cadit/occt/custom_progress.h
Normal file
52
src/cadit/occt/custom_progress.h
Normal file
@ -0,0 +1,52 @@
|
||||
#ifndef CUSTOM_PROGRESS_H
|
||||
#define CUSTOM_PROGRESS_H
|
||||
|
||||
#include <Message_ProgressIndicator.hxx>
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
|
||||
class CustomProgressIndicator : public Message_ProgressIndicator {
|
||||
public:
|
||||
CustomProgressIndicator(); // Constructor
|
||||
~CustomProgressIndicator(); // Destructor
|
||||
|
||||
//! Called by OpenCASCADE to check if the operation should stop
|
||||
Standard_Boolean UserBreak() override;
|
||||
|
||||
//! Updates progress (called by OpenCASCADE)
|
||||
void Show(const Message_ProgressScope &theScope, const Standard_Boolean isForce) override;
|
||||
|
||||
//! Cancels the progress update (stops background thread)
|
||||
void Cancel();
|
||||
|
||||
//! Resets the progress indicator
|
||||
void Reset() override;
|
||||
|
||||
private:
|
||||
// Thread-safe method to set progress
|
||||
void SetProgress(double newProgress);
|
||||
|
||||
// Background thread function to update progress asynchronously
|
||||
void UpdateProgress();
|
||||
|
||||
// Flag to indicate whether the operation should be canceled
|
||||
std::atomic<bool> shouldCancel;
|
||||
|
||||
// Current progress (from 0.0 to 1.0)
|
||||
double progress;
|
||||
|
||||
// Last progress to track the threshold for updating
|
||||
double lastProgress;
|
||||
|
||||
// Mutex to protect shared progress data between threads
|
||||
std::mutex mtx;
|
||||
|
||||
// Mutex for locking std::cout to prevent race conditions in printing
|
||||
std::mutex coutMutex;
|
||||
|
||||
// Background thread for asynchronous progress updates
|
||||
std::thread progressThread;
|
||||
};
|
||||
|
||||
#endif // CUSTOM_PROGRESS_H
|
||||
239
src/cadit/occt/debug.cpp
Normal file
239
src/cadit/occt/debug.cpp
Normal file
@ -0,0 +1,239 @@
|
||||
#include <STEPCAFControl_Reader.hxx>
|
||||
#include <XCAFDoc_DocumentTool.hxx>
|
||||
#include <RWGltf_CafWriter.hxx>
|
||||
#include <TDocStd_Document.hxx>
|
||||
#include <BRepMesh_IncrementalMesh.hxx>
|
||||
#include <execution>
|
||||
#include <filesystem>
|
||||
#include <XCAFDoc_ShapeTool.hxx>
|
||||
#include <StepData_StepModel.hxx>
|
||||
#include <Interface_EntityIterator.hxx>
|
||||
#include <BRepBuilderAPI_MakeShape.hxx>
|
||||
#include "geometry_iterator.h"
|
||||
#include <iostream>
|
||||
#include "debug.h"
|
||||
|
||||
#include <future>
|
||||
|
||||
#include "step_writer.h"
|
||||
#include <Interface_Static.hxx>
|
||||
#include <Interface_Graph.hxx>
|
||||
#include <StepBasic_Product.hxx>
|
||||
|
||||
#include "custom_progress.h"
|
||||
#include "helpers.h"
|
||||
#include "step_helpers.h"
|
||||
#include "step_tree.h"
|
||||
#include "../../config_structs.h"
|
||||
|
||||
|
||||
bool should_process_geometry(const Handle(Standard_Transient) &brep, const ProductNode &node,
|
||||
const GlobalConfig &config) {
|
||||
if (!brep->IsKind(STANDARD_TYPE(StepShape_SolidModel))) {
|
||||
if (config.solidOnly) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!config.filter_names_include.empty()) {
|
||||
if (!check_if_string_in_vector(config.filter_names_include, node.name)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!config.filter_names_exclude.empty()) {
|
||||
if (check_if_string_in_vector(config.filter_names_exclude, node.name)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void debug_stp_to_glb(const GlobalConfig &config) {
|
||||
// Initialize the STEPCAFControl_Reader
|
||||
STEPCAFControl_Reader reader;
|
||||
Interface_Static::SetIVal("FromSTEP.FixShape.FixShellOrientationMode", 0);
|
||||
Interface_Static::SetIVal("read.step.shape.repair.mode", 0);
|
||||
Interface_Static::SetIVal("read.precision.mode", 0);
|
||||
|
||||
// Set reader parameters
|
||||
StepData_ConfParameters params;
|
||||
params.ReadProps = false;
|
||||
params.ReadRelationship = true;
|
||||
params.ReadLayer = true;
|
||||
params.ReadAllShapes = true;
|
||||
params.ReadName = true;
|
||||
params.ReadSubshapeNames = true;
|
||||
params.ReadResourceName = true;
|
||||
params.ReadColor = true;
|
||||
params.ReadPrecisionMode = StepData_ConfParameters::ReadMode_Precision_User;
|
||||
params.ReadPrecisionVal = 1;
|
||||
params.ReadNonmanifold = true;
|
||||
|
||||
// Mesh parameters
|
||||
IMeshTools_Parameters meshParams;
|
||||
meshParams.Angle = config.angularDeflection;
|
||||
meshParams.Deflection = config.linearDeflection;
|
||||
meshParams.Relative = config.relativeDeflection;
|
||||
meshParams.MinSize = 0.1;
|
||||
meshParams.AngleInterior = 0.5;
|
||||
meshParams.DeflectionInterior = 0.1;
|
||||
meshParams.CleanModel = Standard_True;
|
||||
meshParams.InParallel = Standard_True;
|
||||
meshParams.AllowQualityDecrease = Standard_True;
|
||||
|
||||
{
|
||||
TIME_BLOCK("Reading STEP file");
|
||||
|
||||
if (reader.ReadFile(config.stpFile.string().c_str(), params) != IFSelect_RetDone)
|
||||
throw std::runtime_error("Error reading STEP file");
|
||||
}
|
||||
|
||||
Interface_Static::SetIVal("FromSTEP.FixShape.FixShellOrientationMode", 0);
|
||||
|
||||
auto num_roots = reader.NbRootsForTransfer();
|
||||
std::cout << "Number of roots for transfer: " << num_roots << "\n";
|
||||
|
||||
auto default_reader = reader.ChangeReader();
|
||||
auto model = default_reader.StepModel();
|
||||
|
||||
// Build the graph of references
|
||||
Interface_Graph theGraph(model, /*keepTransient*/ Standard_False);
|
||||
|
||||
auto iterator = model->Entities();
|
||||
auto num_entities = iterator.NbEntities();
|
||||
|
||||
std::cout << "Number of entities: " << num_entities << "\n";
|
||||
|
||||
// Extract hierarchy
|
||||
auto roots = ExtractProductHierarchy(model, theGraph);
|
||||
add_geometries_to_nodes(roots, theGraph);
|
||||
|
||||
auto step_store = StepStore(roots);
|
||||
|
||||
// Convert Hierarchy to JSON
|
||||
std::string jsonOutput = ExportHierarchyToJson(roots);
|
||||
|
||||
// Then write to file or print to console:
|
||||
const std::filesystem::path out_json_file = config.glbFile.parent_path() / config.glbFile.stem().concat(
|
||||
"-hierarchy.json");
|
||||
std::ofstream file(out_json_file);
|
||||
file << jsonOutput;
|
||||
file.close();
|
||||
|
||||
std::cout << "Hierarchy exported to assembly_hierarchy.json\n";
|
||||
|
||||
int num_geometry = 0;
|
||||
int num_products = 0;
|
||||
|
||||
// first find the number of geometries
|
||||
for (const auto &node: GeometryRange(roots)) {
|
||||
num_geometry += node.geometryInstances.size();
|
||||
num_products++;
|
||||
}
|
||||
auto curr_shape = 0;
|
||||
auto curr_product = 0;
|
||||
|
||||
// Create a custom progress indicator in a separate thread
|
||||
const Handle(CustomProgressIndicator) progress = new CustomProgressIndicator();
|
||||
Handle(XCAFDoc_ColorTool) colorTool = XCAFDoc_DocumentTool::ColorTool(step_store.doc_->Main());
|
||||
|
||||
// Iterate over all nodes with geometry indices
|
||||
for (const auto &node: GeometryRange(roots)) {
|
||||
Handle(StepBasic_Product) product = Handle(StepBasic_Product)::DownCast(model->Entity(node.entityIndex));
|
||||
|
||||
std::cout << "Node: " << node.name << " (" << curr_product << "/" << num_products << ")"
|
||||
<< ", EntityIndex: " << node.entityIndex
|
||||
<< ", Geometry count: " << node.geometryInstances.size() << '\n';
|
||||
for (const GeometryInstance geometry_instance: node.geometryInstances) {
|
||||
|
||||
std::cout << "Geometry: " << geometry_instance.entityIndex << " (" << curr_shape << "/" << num_geometry << ")\n";
|
||||
|
||||
auto geometry = model->Entity(geometry_instance.entityIndex);
|
||||
|
||||
if (!should_process_geometry(geometry, node, config)) {
|
||||
std::cout << "Skipping shape: " << node.name << " (Entity: " << node.entityIndex << ")\n";
|
||||
node.processResult.added_to_model = false;
|
||||
node.processResult.geometryIndex = geometry_instance.entityIndex;
|
||||
node.processResult.skip_reason = "Skipped by filter";
|
||||
curr_shape++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!default_reader.TransferEntity(geometry)) {
|
||||
std::cerr << "Error transferring entity" << "\n";
|
||||
node.processResult.added_to_model = false;
|
||||
node.processResult.geometryIndex = geometry_instance.entityIndex;
|
||||
node.processResult.skip_reason = "Error transferring entity";
|
||||
curr_shape++;
|
||||
continue;
|
||||
};
|
||||
|
||||
TopoDS_Shape shape = default_reader.Shape(default_reader.NbShapes());
|
||||
if (shape.IsNull()) {
|
||||
std::cerr << "Error converting entity to shape" << "\n";
|
||||
node.processResult.added_to_model = false;
|
||||
node.processResult.geometryIndex = geometry_instance.entityIndex;
|
||||
node.processResult.skip_reason = "Unable to convert entity to shape";
|
||||
curr_shape++;
|
||||
continue;
|
||||
}
|
||||
Quantity_Color occ_color;
|
||||
const Standard_Boolean hasColor = colorTool->GetColor(shape, XCAFDoc_ColorSurf, occ_color);
|
||||
Color color;
|
||||
if (!hasColor) {
|
||||
color = random_color();
|
||||
} else {
|
||||
color = Color(occ_color.Red(), occ_color.Green(), occ_color.Blue());
|
||||
}
|
||||
|
||||
std::cout << "Adding Shape: " << node.name << " (Entity: " << node.entityIndex << ") to STEP Writer\n";
|
||||
step_store.add_shape(shape, node.name, color, node);
|
||||
|
||||
// Updated code block
|
||||
{
|
||||
TIME_BLOCK("Applying tessellation");
|
||||
if (!perform_tessellation_with_timeout(shape, meshParams, config.tessellation_timout, progress)) {
|
||||
std::cout << "Tessellation timed out.\n";
|
||||
node.processResult.added_to_model = false;
|
||||
node.processResult.geometryIndex = geometry_instance.entityIndex;
|
||||
node.processResult.skip_reason = "Tessellation timed out";
|
||||
curr_shape++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
curr_shape++;
|
||||
}
|
||||
|
||||
if (config.max_geometry_num != 0 && curr_shape >= config.max_geometry_num) {
|
||||
break;
|
||||
}
|
||||
curr_product++;
|
||||
}
|
||||
|
||||
step_store.to_glb(config.glbFile);
|
||||
|
||||
const std::filesystem::path out_file = config.glbFile.parent_path() / config.glbFile.stem().concat("-debug.stp");
|
||||
step_store.to_step(out_file.string().c_str());
|
||||
|
||||
// iterate over all nodes that werent added to the model and save the list to json
|
||||
const std::filesystem::path out_json_log_file = config.glbFile.parent_path() / config.glbFile.stem().concat(
|
||||
"-log.json");
|
||||
std::ofstream log_file(out_json_log_file);
|
||||
log_file << "[\n";
|
||||
|
||||
for (const auto &node: GeometryRange(roots)) {
|
||||
if (!node.processResult.added_to_model && node.processResult.geometryIndex != 0) {
|
||||
log_file << "{\n";
|
||||
log_file << R"("name": ")" << node.name << "\",\n";
|
||||
log_file << "\"entityIndex\": " << node.entityIndex << ",\n";
|
||||
log_file << "\"geometryIndex\": " << node.processResult.geometryIndex << ",\n";
|
||||
log_file << R"("skipReason": ")" << node.processResult.skip_reason << "\"\n";
|
||||
log_file << "},\n";
|
||||
}
|
||||
}
|
||||
log_file << "]\n";
|
||||
log_file.close();
|
||||
|
||||
|
||||
}
|
||||
13
src/cadit/occt/debug.h
Normal file
13
src/cadit/occt/debug.h
Normal file
@ -0,0 +1,13 @@
|
||||
//
|
||||
// Created by ofskrand on 23.01.2024.
|
||||
//
|
||||
#ifndef STEP_TO_GLB_H
|
||||
#define STEP_TO_GLB_H
|
||||
|
||||
#include "../../config_structs.h"
|
||||
|
||||
// Function to convert STEP file to GLB file
|
||||
void debug_stp_to_glb(const GlobalConfig& config);
|
||||
|
||||
|
||||
#endif //STEP_TO_GLB_H
|
||||
137
src/cadit/occt/geometry_iterator.h
Normal file
137
src/cadit/occt/geometry_iterator.h
Normal file
@ -0,0 +1,137 @@
|
||||
#ifndef GEOMETRY_ITERATOR_HPP
|
||||
#define GEOMETRY_ITERATOR_HPP
|
||||
|
||||
#include <stack>
|
||||
#include <utility>
|
||||
#include <iterator>
|
||||
#include "step_tree.h"
|
||||
|
||||
class GeometryIterator {
|
||||
public:
|
||||
using value_type = const ProductNode;
|
||||
using pointer = const ProductNode*;
|
||||
using reference = const ProductNode&;
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
|
||||
// Constructor: Initialize with a pointer to the root node
|
||||
explicit GeometryIterator(pointer root) {
|
||||
if (root) {
|
||||
stack.emplace(root, 0);
|
||||
advanceToNextValid();
|
||||
}
|
||||
}
|
||||
|
||||
// Default constructor for end iterator
|
||||
GeometryIterator() = default;
|
||||
|
||||
// Dereference operator
|
||||
reference operator*() const {
|
||||
return *stack.top().first;
|
||||
}
|
||||
|
||||
// Arrow operator
|
||||
pointer operator->() const {
|
||||
return stack.top().first;
|
||||
}
|
||||
|
||||
// Pre-increment
|
||||
GeometryIterator& operator++() {
|
||||
stack.top().second++;
|
||||
advanceToNextValid();
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Post-increment
|
||||
GeometryIterator operator++(int) {
|
||||
GeometryIterator temp = *this;
|
||||
++(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
// Equality
|
||||
bool operator==(const GeometryIterator& other) const {
|
||||
return stack == other.stack;
|
||||
}
|
||||
|
||||
// Inequality
|
||||
bool operator!=(const GeometryIterator& other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
private:
|
||||
// Each stack entry: (currentNode, currentChildIndex)
|
||||
std::stack<std::pair<pointer, size_t>> stack;
|
||||
|
||||
// Advance to the next node that has geometryIndices, or run out
|
||||
void advanceToNextValid() {
|
||||
while (!stack.empty()) {
|
||||
auto& [currentNode, childIndex] = stack.top();
|
||||
|
||||
// 1) If this is the first time we see this node (childIndex == 0)
|
||||
// and it has geometry, we stop here so operator* sees it.
|
||||
if (childIndex == 0 && !currentNode->geometryInstances.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 2) Otherwise, if we still have children to explore
|
||||
if (childIndex < currentNode->children.size()) {
|
||||
// Get the raw pointer from the unique_ptr
|
||||
auto* childPtr = currentNode->children[childIndex].get();
|
||||
stack.emplace(childPtr, 0);
|
||||
childIndex++;
|
||||
}
|
||||
else {
|
||||
// No more children -> pop
|
||||
stack.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Helper functions for iterating a single ProductNode
|
||||
inline GeometryIterator begin(const ProductNode& root) {
|
||||
return GeometryIterator(&root);
|
||||
}
|
||||
|
||||
inline GeometryIterator end(const ProductNode&) {
|
||||
return {};
|
||||
}
|
||||
|
||||
class GeometryRange {
|
||||
public:
|
||||
// Suppose we have a vector of unique_ptr<ProductNode> as roots
|
||||
explicit GeometryRange(const std::vector<std::unique_ptr<ProductNode>>& roots) {
|
||||
for (auto const& rootPtr : roots) {
|
||||
// rootPtr is a std::unique_ptr<ProductNode>, so we pass rootPtr.get()
|
||||
iterators.emplace_back(GeometryIterator(rootPtr.get()), GeometryIterator());
|
||||
}
|
||||
updateCurrentIterator();
|
||||
}
|
||||
|
||||
GeometryIterator begin() {
|
||||
return currentBegin;
|
||||
}
|
||||
|
||||
static GeometryIterator end() {
|
||||
return {};
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::pair<GeometryIterator, GeometryIterator>> iterators;
|
||||
GeometryIterator currentBegin;
|
||||
|
||||
void updateCurrentIterator() {
|
||||
// Find the first non-empty iterator
|
||||
for (auto& [it, endIt] : iterators) {
|
||||
if (it != endIt) {
|
||||
currentBegin = it;
|
||||
return;
|
||||
}
|
||||
}
|
||||
// If all are empty, we produce an end iterator
|
||||
currentBegin = GeometryIterator();
|
||||
}
|
||||
};
|
||||
|
||||
#endif // GEOMETRY_ITERATOR_HPP
|
||||
94
src/cadit/occt/gltf_writer.cpp
Normal file
94
src/cadit/occt/gltf_writer.cpp
Normal file
@ -0,0 +1,94 @@
|
||||
//
|
||||
// Created by Kristoffer on 07/05/2023.
|
||||
//
|
||||
|
||||
#include <vector>
|
||||
#include <filesystem>
|
||||
|
||||
#include <Message_ProgressRange.hxx>
|
||||
#include <RWGltf_CafWriter.hxx>
|
||||
#include <RWGltf_WriterTrsfFormat.hxx>
|
||||
#include <TCollection_AsciiString.hxx>
|
||||
#include <TCollection_ExtendedString.hxx>
|
||||
#include <TColStd_IndexedDataMapOfStringString.hxx>
|
||||
#include <TDocStd_Document.hxx>
|
||||
#include <TopoDS_Compound.hxx>
|
||||
#include <XCAFDoc_ColorTool.hxx>
|
||||
#include <XCAFDoc_DocumentTool.hxx>
|
||||
#include <XCAFDoc_ShapeTool.hxx>
|
||||
#include "../../geom/OccShape.h"
|
||||
#include "helpers.h"
|
||||
|
||||
|
||||
|
||||
enum class Units {
|
||||
M,
|
||||
MM,
|
||||
};
|
||||
|
||||
void to_gltf_from_shapes(
|
||||
const std::filesystem::path& gltf_file,
|
||||
const std::vector<OccShape>& occ_shape_iterable,
|
||||
Units export_units = Units::M,
|
||||
Units source_units = Units::M
|
||||
) {
|
||||
Handle(TDocStd_Document) doc = new TDocStd_Document(TCollection_ExtendedString("ada-py"));
|
||||
Handle(XCAFDoc_ShapeTool) shape_tool = XCAFDoc_DocumentTool::ShapeTool(doc->Main());
|
||||
Handle(XCAFDoc_ColorTool) color_tool = XCAFDoc_DocumentTool::ColorTool(doc->Main());
|
||||
|
||||
int i = 1;
|
||||
for (const auto& step_shape : occ_shape_iterable) {
|
||||
TopoDS_Shape shp = step_shape.shape;
|
||||
if (shp.ShapeType() == TopAbs_COMPOUND) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Mesh mesh = tessellate_shape(shp, true, 1.0, false);
|
||||
|
||||
TDF_Label sub_shape_label = shape_tool->AddShape(shp);
|
||||
set_color(sub_shape_label, step_shape.color, color_tool);
|
||||
set_name(sub_shape_label, step_shape.name);
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
RWGltf_WriterTrsfFormat a_format = RWGltf_WriterTrsfFormat_Compact;
|
||||
|
||||
TColStd_IndexedDataMapOfStringString a_file_info;
|
||||
a_file_info.Add(TCollection_AsciiString("Authors"), TCollection_AsciiString("ada-py"));
|
||||
|
||||
bool binary = gltf_file.extension() == ".glb";
|
||||
|
||||
RWGltf_CafWriter glb_writer(TCollection_AsciiString(gltf_file.string().c_str()), binary);
|
||||
if (export_units == Units::M && source_units == Units::MM) {
|
||||
glb_writer.ChangeCoordinateSystemConverter().SetInputLengthUnit(0.001);
|
||||
} else if (export_units == Units::MM && source_units == Units::M) {
|
||||
glb_writer.ChangeCoordinateSystemConverter().SetInputLengthUnit(1000);
|
||||
}
|
||||
|
||||
glb_writer.ChangeCoordinateSystemConverter().SetInputCoordinateSystem(RWMesh_CoordinateSystem_Zup);
|
||||
glb_writer.SetTransformationFormat(a_format);
|
||||
Message_ProgressRange pr;
|
||||
glb_writer.Perform(doc, a_file_info, pr);
|
||||
}
|
||||
|
||||
|
||||
void to_glb_from_doc(const std::filesystem::path& glb_file, const Handle(TDocStd_Document)& doc) {
|
||||
RWGltf_CafWriter writer(glb_file.c_str(), true); // true for binary format
|
||||
|
||||
// Additional file information (can be empty if not needed)
|
||||
const TColStd_IndexedDataMapOfStringString file_info;
|
||||
|
||||
// Progress indicator (can be null if progress tracking is not needed)
|
||||
const Message_ProgressRange progress;
|
||||
|
||||
// if output parent directory is != "" and does not exist, create it
|
||||
if (const std::filesystem::path glb_dir = glb_file.parent_path(); !glb_dir.empty() && !exists(glb_dir))
|
||||
{
|
||||
create_directories(glb_dir);
|
||||
}
|
||||
if (!writer.Perform(doc, file_info, progress))
|
||||
{
|
||||
throw std::runtime_error("Error writing GLB file");
|
||||
}
|
||||
}
|
||||
15
src/cadit/occt/gltf_writer.h
Normal file
15
src/cadit/occt/gltf_writer.h
Normal file
@ -0,0 +1,15 @@
|
||||
//
|
||||
// Created by Kristoffer on 07/05/2023.
|
||||
//
|
||||
|
||||
#ifndef NANO_OCCT_GLTF_WRITER_H
|
||||
#define NANO_OCCT_GLTF_WRITER_H
|
||||
|
||||
#include <filesystem>
|
||||
#include <Standard_Handle.hxx>
|
||||
#include <TDocStd_Document.hxx>
|
||||
|
||||
void to_glb_from_doc(const std::filesystem::path& glb_file, const Handle(TDocStd_Document)& doc);
|
||||
|
||||
|
||||
#endif //NANO_OCCT_GLTF_WRITER_H
|
||||
103
src/cadit/occt/helpers.cpp
Normal file
103
src/cadit/occt/helpers.cpp
Normal file
@ -0,0 +1,103 @@
|
||||
#include <vector>
|
||||
#include <random>
|
||||
#include <gp_XYZ.hxx>
|
||||
#include <gp_Pnt.hxx>
|
||||
#include <TopoDS_Solid.hxx>
|
||||
#include <BRepPrimAPI_MakeBox.hxx>
|
||||
#include <TDF_Label.hxx>
|
||||
#include <TDataStd_Name.hxx>
|
||||
#include <Quantity_Color.hxx>
|
||||
#include <XCAFDoc_ColorType.hxx>
|
||||
#include <XCAFDoc_ColorTool.hxx>
|
||||
#include <optional>
|
||||
#include "../../geom/Color.h"
|
||||
#include "helpers.h"
|
||||
|
||||
#include <chrono>
|
||||
|
||||
|
||||
TopoDS_Solid create_box(const std::vector<float>& box_origin, const std::vector<float>& box_dims)
|
||||
{
|
||||
gp_Pnt aBoxOrigin(box_origin[0], box_origin[1], box_origin[2]);
|
||||
gp_XYZ aBoxDims(box_dims[0], box_dims[1], box_dims[2]);
|
||||
|
||||
return BRepPrimAPI_MakeBox(aBoxOrigin, aBoxDims.X(), aBoxDims.Y(), aBoxDims.Z());
|
||||
}
|
||||
|
||||
// a function that returns a random tuple std::tuple<double, double, double>
|
||||
Color random_color()
|
||||
{
|
||||
static std::mt19937 generator(std::random_device{}());
|
||||
static std::uniform_real_distribution<float> distribution(0.0, 1.0);
|
||||
return Color(distribution(generator), distribution(generator), distribution(generator), 1.0);
|
||||
}
|
||||
|
||||
void set_name(const TDF_Label& label, const std::optional<std::string>& name)
|
||||
{
|
||||
if (!name)
|
||||
{
|
||||
return;
|
||||
}
|
||||
TCollection_ExtendedString ext_name(name->c_str());
|
||||
TDataStd_Name::Set(label, ext_name);
|
||||
}
|
||||
|
||||
void set_color(const TDF_Label& label, const Color& color,
|
||||
const Handle(XCAFDoc_ColorTool)& tool)
|
||||
{
|
||||
float r = color.r;
|
||||
float g = color.g;
|
||||
float b = color.b;
|
||||
|
||||
Quantity_Color qty_color(r, g, b, Quantity_TOC_RGB);
|
||||
tool->SetColor(label, qty_color, XCAFDoc_ColorType::XCAFDoc_ColorSurf);
|
||||
}
|
||||
|
||||
// Utility function to split a string by a delimiter
|
||||
std::vector<std::string> split(const std::string& input, char delimiter) {
|
||||
std::vector<std::string> tokens;
|
||||
std::stringstream ss(input);
|
||||
std::string token;
|
||||
while (std::getline(ss, token, delimiter)) {
|
||||
tokens.push_back(token);
|
||||
}
|
||||
return tokens;
|
||||
}
|
||||
|
||||
// Utility function to strip surrounding quotes from a string
|
||||
std::string strip_quotes(const std::string& input) {
|
||||
if (input.size() >= 2 && input.front() == '"' && input.back() == '"') {
|
||||
return input.substr(1, input.size() - 2);
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
bool check_if_string_in_vector(const std::vector<std::string>& vec, const std::string& str)
|
||||
{
|
||||
return std::any_of(vec.begin(), vec.end(), [&str](const std::string& filter_name)
|
||||
{
|
||||
return std::equal(str.begin(), str.end(),
|
||||
filter_name.begin(), filter_name.end(),
|
||||
[](char a, char b)
|
||||
{
|
||||
return std::tolower(static_cast<unsigned char>(a)) ==
|
||||
std::tolower(static_cast<unsigned char>(b));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
TimingContext::TimingContext(std::string name)
|
||||
: label(std::move(name)), start(std::chrono::high_resolution_clock::now())
|
||||
{
|
||||
std::cout << "Starting: " << label << std::endl;
|
||||
}
|
||||
|
||||
TimingContext::~TimingContext()
|
||||
{
|
||||
auto stop = std::chrono::high_resolution_clock::now();
|
||||
auto duration = std::chrono::duration<double>(stop - start).count();
|
||||
std::cout << label << " took " << std::fixed << std::setprecision(2)
|
||||
<< duration << " seconds." << std::endl;
|
||||
}
|
||||
|
||||
#define TIME_BLOCK(name) TimingContext timer_##__LINE__(name)
|
||||
44
src/cadit/occt/helpers.h
Normal file
44
src/cadit/occt/helpers.h
Normal file
@ -0,0 +1,44 @@
|
||||
#ifndef NANO_OCCT_HELPERS_H
|
||||
#define NANO_OCCT_HELPERS_H
|
||||
|
||||
#include <vector>
|
||||
#include <TopoDS_Solid.hxx>
|
||||
#include <TDF_Label.hxx>
|
||||
#include <XCAFDoc_ColorTool.hxx>
|
||||
#include <optional>
|
||||
#include "../../geom/Color.h"
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
|
||||
std::string strip_quotes(const std::string& input);
|
||||
|
||||
std::vector<std::string> split(const std::string& input, char delimiter);
|
||||
|
||||
bool check_if_string_in_vector(const std::vector<std::string> &vec, const std::string &str);
|
||||
|
||||
TopoDS_Solid create_box(const std::vector<float> &box_origin, const std::vector<float> &box_dims);
|
||||
|
||||
Color random_color();
|
||||
|
||||
void set_name(const TDF_Label &label, const std::optional<std::string> &name);
|
||||
|
||||
void set_color(const TDF_Label &label, const Color &color,
|
||||
const Handle(XCAFDoc_ColorTool) &tool);
|
||||
|
||||
|
||||
|
||||
class TimingContext
|
||||
{
|
||||
public:
|
||||
explicit TimingContext(std::string name);
|
||||
~TimingContext();
|
||||
|
||||
private:
|
||||
std::string label;
|
||||
std::chrono::time_point<std::chrono::high_resolution_clock> start;
|
||||
};
|
||||
|
||||
// Macro to create a timing block
|
||||
#define TIME_BLOCK(name) TimingContext timer_##__LINE__(name)
|
||||
|
||||
#endif // NANO_OCCT_HELPERS_H
|
||||
698
src/cadit/occt/step_helpers.cpp
Normal file
698
src/cadit/occt/step_helpers.cpp
Normal file
@ -0,0 +1,698 @@
|
||||
#include <STEPCAFControl_Reader.hxx>
|
||||
#include <XCAFDoc_DocumentTool.hxx>
|
||||
#include <RWGltf_CafWriter.hxx>
|
||||
#include <TDocStd_Document.hxx>
|
||||
#include <BRepMesh_IncrementalMesh.hxx>
|
||||
#include <filesystem>
|
||||
#include <XCAFDoc_ShapeTool.hxx>
|
||||
#include <Interface_EntityIterator.hxx>
|
||||
#include <StepShape_SolidModel.hxx>
|
||||
#include <StepShape_Face.hxx>
|
||||
#include <StepShape_AdvancedFace.hxx>
|
||||
#include <BRepBuilderAPI_MakeShape.hxx>
|
||||
#include <BRep_Builder.hxx>
|
||||
#include <TDataStd_Name.hxx>
|
||||
#include <iostream>
|
||||
#include "step_writer.h"
|
||||
#include "step_helpers.h"
|
||||
|
||||
#include <Interface_Graph.hxx>
|
||||
#include <StepGeom_Axis2Placement3d.hxx>
|
||||
#include <StepGeom_CartesianPoint.hxx>
|
||||
#include <StepGeom_Direction.hxx>
|
||||
#include <BRepBuilderAPI_Transform.hxx>
|
||||
#include <gp_Trsf.hxx>
|
||||
#include <StepRepr_ShapeRepresentationRelationship.hxx>
|
||||
#include <StepShape_ShapeRepresentation.hxx>
|
||||
#include <StepRepr_ProductDefinitionShape.hxx>
|
||||
#include <StepShape_ShapeDefinitionRepresentation.hxx>
|
||||
#include <StepBasic_ProductDefinition.hxx>
|
||||
#include <StepBasic_ProductDefinitionFormation.hxx>
|
||||
#include <StepBasic_Product.hxx>
|
||||
#include <StepRepr_Representation.hxx>
|
||||
#include <TCollection_HAsciiString.hxx>
|
||||
#include <thread>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "custom_progress.h"
|
||||
#include "helpers.h"
|
||||
|
||||
Handle(Standard_Transient) get_entity_from_graph_path(const Handle(Standard_Transient)& entity,
|
||||
Interface_Graph& theGraph, std::vector<std::string> path)
|
||||
{
|
||||
// First check if the entity is contained in the path
|
||||
if (std::find(path.begin(), path.end(), entity->DynamicType()->Name()) == path.end())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
// find index of entity in path
|
||||
auto index = std::find(path.begin(), path.end(), entity->DynamicType()->Name());
|
||||
// if entity is last item in path, return it
|
||||
if (index == path.end() - 1)
|
||||
{
|
||||
return entity;
|
||||
}
|
||||
// if not last item, find the next entity in the path
|
||||
auto target_type = path[index - path.begin() + 1];
|
||||
|
||||
auto parents = theGraph.Sharings(entity);
|
||||
|
||||
while (parents.More())
|
||||
{
|
||||
Handle(Standard_Transient) parent = parents.Value();
|
||||
auto parent_name = parent->DynamicType()->Name();
|
||||
if (parent_name == target_type)
|
||||
{
|
||||
return get_entity_from_graph_path(parent, theGraph, path);
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string extractProductNameFromSDR(const Handle(StepShape_ShapeDefinitionRepresentation)& sdr)
|
||||
{
|
||||
if (sdr.IsNull())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
// 1) sdr->Definition() returns a "StepRepr_RepresentedDefinition" (by value)
|
||||
StepRepr_RepresentedDefinition def = sdr->Definition();
|
||||
if (def.Value().IsNull())
|
||||
{
|
||||
// Nothing linked
|
||||
return {};
|
||||
}
|
||||
|
||||
// 2) The underlying handle is def.Value(). We expect it might be a StepBasic_ProductDefinition
|
||||
Handle(StepRepr_ProductDefinitionShape) pds =
|
||||
Handle(StepRepr_ProductDefinitionShape)::DownCast(def.Value());
|
||||
if (pds.IsNull())
|
||||
{
|
||||
// It's not a product definition. Could be something else.
|
||||
return {};
|
||||
}
|
||||
|
||||
StepRepr_CharacterizedDefinition cd = pds->Definition();
|
||||
if (cd.IsNull())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
// 3) Now from the product definition, we can get the formation -> product
|
||||
Handle(StepBasic_ProductDefinition) pd = cd.ProductDefinition();
|
||||
if (pd.IsNull())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
// 3) Now from the product definition, we can get the formation -> product
|
||||
Handle(StepBasic_ProductDefinitionFormation) pdf = pd->Formation();
|
||||
if (pdf.IsNull())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
Handle(StepBasic_Product) product = pdf->OfProduct();
|
||||
if (product.IsNull() || product->Name().IsNull())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
return product->Name()->ToCString();
|
||||
}
|
||||
|
||||
std::string getStepProductNameFromGraph(const Handle(Standard_Transient)& entity, Interface_Graph& theGraph)
|
||||
{
|
||||
// Relationship tree
|
||||
// StepShape_ManifoldSolidBrep -> SHAPE_REPRESENTATION -> ShapeDefinitionRepresentation
|
||||
// 1) Get the model index for this entity
|
||||
std::vector<std::string> path = {
|
||||
"StepShape_ManifoldSolidBrep", "StepShape_AdvancedBrepShapeRepresentation",
|
||||
"StepShape_ShapeDefinitionRepresentation"
|
||||
};
|
||||
// "StepBasic_ProductDefinition", "StepBasic_ProductDefinitionFormation", "StepBasic_Product"
|
||||
Handle(StepRepr_RepresentationItem) item =
|
||||
Handle(StepRepr_RepresentationItem)::DownCast(entity);
|
||||
|
||||
auto base_type = entity->DynamicType()->Name();
|
||||
auto shape_def_rep = get_entity_from_graph_path(entity, theGraph, path);
|
||||
|
||||
// get PRODUCT_DEFINITION_SHAPE from shape_def_rep
|
||||
// get PRODUCT_DEFINITION from PRODUCT_DEFINITION_SHAPE
|
||||
// get PRODUCT from PRODUCT_DEFINITION
|
||||
// get NAME from PRODUCT
|
||||
|
||||
if (!shape_def_rep.IsNull())
|
||||
{
|
||||
auto productName = extractProductNameFromSDR(
|
||||
Handle(StepShape_ShapeDefinitionRepresentation)::DownCast(shape_def_rep));
|
||||
if (!productName.empty())
|
||||
{
|
||||
return productName;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
// Extract product name from a single entity
|
||||
//--------------------------------------
|
||||
std::string getStepProductName(const Handle(Standard_Transient)& entity, Interface_Graph& theGraph)
|
||||
{
|
||||
// ----------------------------------------------------------
|
||||
// 1) StepShape_ShapeDefinitionRepresentation: might link to a ProductDefinition
|
||||
// ----------------------------------------------------------
|
||||
{
|
||||
Handle(StepShape_ShapeDefinitionRepresentation) sdr =
|
||||
Handle(StepShape_ShapeDefinitionRepresentation)::DownCast(entity);
|
||||
if (!sdr.IsNull())
|
||||
{
|
||||
// sdr->Definition() returns a by-value StepRepr_RepresentedDefinition
|
||||
StepRepr_RepresentedDefinition def = sdr->Definition();
|
||||
if (!def.Value().IsNull())
|
||||
{
|
||||
// Try cast to StepBasic_ProductDefinition
|
||||
Handle(StepBasic_ProductDefinition) pd =
|
||||
Handle(StepBasic_ProductDefinition)::DownCast(def.Value());
|
||||
if (!pd.IsNull())
|
||||
{
|
||||
// ProductDefinitionFormation -> OfProduct
|
||||
if (!pd->Formation().IsNull())
|
||||
{
|
||||
Handle(StepBasic_Product) product = pd->Formation()->OfProduct();
|
||||
if (!product.IsNull() && !product->Name().IsNull())
|
||||
{
|
||||
std::string result = product->Name()->ToCString();
|
||||
if (!result.empty())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------
|
||||
// 2) StepRepr_ShapeRepresentation / StepRepr_Representation
|
||||
// to see if there's a shape-level or representation-level name
|
||||
// ----------------------------------------------------------
|
||||
{
|
||||
Handle(StepShape_ShapeRepresentation) shapeRepr =
|
||||
Handle(StepShape_ShapeRepresentation)::DownCast(entity);
|
||||
if (!shapeRepr.IsNull())
|
||||
{
|
||||
// shapeRepr inherits from StepRepr_Representation, which has a Name()
|
||||
if (!shapeRepr->Name().IsNull())
|
||||
{
|
||||
std::string result = shapeRepr->Name()->ToCString();
|
||||
if (!result.empty())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
// Maybe it's a plain StepRepr_Representation
|
||||
Handle(StepRepr_Representation) repr =
|
||||
Handle(StepRepr_Representation)::DownCast(entity);
|
||||
if (!repr.IsNull())
|
||||
{
|
||||
if (!repr->Name().IsNull())
|
||||
{
|
||||
std::string result = repr->Name()->ToCString();
|
||||
if (!result.empty())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------
|
||||
// 3) StepRepr_RepresentationItem (like faces, edges, etc.)
|
||||
// ----------------------------------------------------------
|
||||
{
|
||||
Handle(StepRepr_RepresentationItem) item =
|
||||
Handle(StepRepr_RepresentationItem)::DownCast(entity);
|
||||
if (!item.IsNull())
|
||||
{
|
||||
if (!item->Name().IsNull())
|
||||
{
|
||||
std::string result = item->Name()->ToCString();
|
||||
if (!result.empty())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If everything fails or is empty, we return empty string
|
||||
return {};
|
||||
}
|
||||
|
||||
void update_location(TopoDS_Shape& shape)
|
||||
{
|
||||
TopLoc_Location loc = shape.Location();
|
||||
|
||||
// Extract translation components from the transformation matrix
|
||||
gp_XYZ translation = loc.Transformation().TranslationPart();
|
||||
Standard_Real x = translation.X();
|
||||
Standard_Real y = translation.Y();
|
||||
Standard_Real z = translation.Z();
|
||||
|
||||
// Output the coordinates
|
||||
std::cout << "X: " << x << ", Y: " << y << ", Z: " << z << "\n";
|
||||
|
||||
if (!loc.IsIdentity())
|
||||
{
|
||||
shape.Location(loc); // Apply the transformation
|
||||
}
|
||||
}
|
||||
|
||||
gp_Trsf get_product_transform(TopoDS_Shape& shape, const Handle(StepBasic_Product)& product)
|
||||
{
|
||||
gp_Trsf transform;
|
||||
|
||||
// Helper lambda to extract gp_Trsf from Axis2Placement3D
|
||||
auto extract_transform_from_placement = [](const Handle(StepGeom_Axis2Placement3d)& placement) -> gp_Trsf
|
||||
{
|
||||
gp_Trsf trsf;
|
||||
if (!placement.IsNull())
|
||||
{
|
||||
gp_Pnt location(
|
||||
placement->Location()->CoordinatesValue(1),
|
||||
placement->Location()->CoordinatesValue(2),
|
||||
placement->Location()->CoordinatesValue(3));
|
||||
|
||||
gp_Dir zDir(
|
||||
placement->Axis()->DirectionRatios()->Value(1),
|
||||
placement->Axis()->DirectionRatios()->Value(2),
|
||||
placement->Axis()->DirectionRatios()->Value(3));
|
||||
|
||||
gp_Dir xDir(
|
||||
placement->RefDirection()->DirectionRatios()->Value(1),
|
||||
placement->RefDirection()->DirectionRatios()->Value(2),
|
||||
placement->RefDirection()->DirectionRatios()->Value(3));
|
||||
|
||||
gp_Ax3 ax3(location, zDir, xDir);
|
||||
trsf.SetTransformation(ax3);
|
||||
}
|
||||
return trsf;
|
||||
};
|
||||
|
||||
// Traverse the STEP structure to accumulate transformations
|
||||
Handle(StepRepr_ProductDefinitionShape) prodDefShape;
|
||||
// Get FrameOfReference from Product
|
||||
Handle(StepRepr_Representation) representation;
|
||||
if (product->NbFrameOfReference() > 0)
|
||||
{
|
||||
// Assuming FrameOfReference() returns a list of StepBasic_ProductContext
|
||||
const auto& frameOfReference = product->FrameOfReference();
|
||||
for (Standard_Integer i = 1; i <= frameOfReference->Length(); ++i)
|
||||
{
|
||||
Handle(StepBasic_ProductContext) productContext = frameOfReference->Value(i);
|
||||
if (!productContext.IsNull())
|
||||
{
|
||||
// Retrieve the Representation (if applicable)
|
||||
Handle(StepRepr_Representation) rep =
|
||||
Handle(StepRepr_Representation)::DownCast(productContext);
|
||||
if (!rep.IsNull())
|
||||
{
|
||||
representation = rep;
|
||||
break; // Use the first valid representation
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!representation.IsNull())
|
||||
{
|
||||
// Iterate over items in the representation to find transformations
|
||||
for (Standard_Integer i = 1; i <= representation->NbItems(); ++i)
|
||||
{
|
||||
Handle(Standard_Transient) item = representation->ItemsValue(i);
|
||||
|
||||
// If the item is a ShapeRepresentation, get its transformation
|
||||
if (item->IsKind(STANDARD_TYPE(StepShape_ShapeRepresentation)))
|
||||
{
|
||||
Handle(StepShape_ShapeRepresentation) shapeRep =
|
||||
Handle(StepShape_ShapeRepresentation)::DownCast(item);
|
||||
|
||||
for (Standard_Integer j = 1; j <= shapeRep->NbItems(); ++j)
|
||||
{
|
||||
Handle(Standard_Transient) subItem = shapeRep->ItemsValue(j);
|
||||
|
||||
if (subItem->IsKind(STANDARD_TYPE(StepGeom_Axis2Placement3d)))
|
||||
{
|
||||
Handle(StepGeom_Axis2Placement3d) axisPlacement =
|
||||
Handle(StepGeom_Axis2Placement3d)::DownCast(subItem);
|
||||
|
||||
transform = transform * extract_transform_from_placement(axisPlacement);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Apply the accumulated transform to the shape
|
||||
{
|
||||
BRepBuilderAPI_Transform shapeTransform(transform);
|
||||
}
|
||||
|
||||
return transform;
|
||||
}
|
||||
|
||||
std::string get_name(const Handle(StepRepr_RepresentationItem)& repr_item)
|
||||
{
|
||||
if (!repr_item.IsNull())
|
||||
{
|
||||
auto name = repr_item->Name()->ToCString();
|
||||
|
||||
if (name)
|
||||
{
|
||||
return name;
|
||||
}
|
||||
return "Unnamed";
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
TopoDS_Shape make_shape(const Handle(StepShape_SolidModel)& solid_model, STEPControl_Reader& reader)
|
||||
{
|
||||
if (!solid_model.IsNull())
|
||||
{
|
||||
TIME_BLOCK("Transferring solid model entity");
|
||||
// Convert the solid model into an OpenCascade shape
|
||||
if (!reader.TransferEntity(solid_model))
|
||||
{
|
||||
std::cerr << "Error transferring entity" << std::endl;
|
||||
};
|
||||
TopoDS_Shape shape = reader.Shape(reader.NbShapes());
|
||||
|
||||
return shape;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
TopoDS_Shape make_shape(const Handle(StepShape_Face)& face, STEPControl_Reader& reader)
|
||||
{
|
||||
if (!face.IsNull())
|
||||
{
|
||||
TIME_BLOCK("Transferring face entity");
|
||||
if (!reader.TransferEntity(face))
|
||||
{
|
||||
std::cerr << "Error transferring face entity" << std::endl;
|
||||
}
|
||||
|
||||
TopoDS_Shape shape = reader.Shape(reader.NbShapes());
|
||||
|
||||
|
||||
return shape;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
TDF_Label add_shape_to_document(const TopoDS_Shape& shape, const std::string& name,
|
||||
const Handle(XCAFDoc_ShapeTool)& shape_tool, IMeshTools_Parameters& meshParams)
|
||||
{
|
||||
TDF_Label shape_label;
|
||||
|
||||
if (!shape.IsNull())
|
||||
{
|
||||
{
|
||||
TIME_BLOCK("Applying tessellation");
|
||||
// Perform tessellation (discretization) on the shape
|
||||
BRepMesh_IncrementalMesh mesh(shape, meshParams); // Adjust 0.1 for finer/coarser tessellation
|
||||
}
|
||||
|
||||
// Add the TopoDS_Shape to the document
|
||||
{
|
||||
TIME_BLOCK("Adding shape '" + name + "' to document\n");
|
||||
shape_label = shape_tool->AddShape(shape);
|
||||
}
|
||||
}
|
||||
return shape_label;
|
||||
}
|
||||
|
||||
// Constructor
|
||||
ConvertObject::ConvertObject(const std::string& name, const TopoDS_Shape& shape, TDF_Label& shape_label,
|
||||
bool addedToModel)
|
||||
: name(name), shape(shape), shape_label(shape_label), AddedToModel(addedToModel)
|
||||
{
|
||||
}
|
||||
|
||||
// Destructor (optional if needed)
|
||||
ConvertObject::~ConvertObject()
|
||||
{
|
||||
// Clean up resources if necessary
|
||||
}
|
||||
|
||||
// Custom filter function (example: filter by entity type or name)
|
||||
bool CustomFilter(const Handle(Standard_Transient)& entity)
|
||||
{
|
||||
if (entity->IsKind(STANDARD_TYPE(StepBasic_ProductDefinition)))
|
||||
{
|
||||
return true; // Matches filter
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Interface_EntityIterator MyTypedExpansions(const Handle(Standard_Transient)& rootEntity,
|
||||
const Handle(Standard_Type)& targetType,
|
||||
const Interface_Graph& theGraph)
|
||||
{
|
||||
// Prepare an empty sequence to accumulate matching entities
|
||||
Handle(TColStd_HSequenceOfTransient) matchedEntities =
|
||||
new TColStd_HSequenceOfTransient();
|
||||
|
||||
if (rootEntity.IsNull())
|
||||
{
|
||||
// Return an empty iterator
|
||||
return {matchedEntities};
|
||||
}
|
||||
|
||||
// We'll need the model to get entity indices (theGraph.Model()->Number())
|
||||
Handle(Interface_InterfaceModel) model = theGraph.Model();
|
||||
if (model.IsNull())
|
||||
{
|
||||
// Return empty if no model
|
||||
return {matchedEntities};
|
||||
}
|
||||
|
||||
// BFS queue
|
||||
std::queue<Handle(Standard_Transient)> toVisit;
|
||||
// track visited with a set of entity indices to avoid loops
|
||||
std::unordered_set<Standard_Integer> visited;
|
||||
|
||||
// Helper to enqueue unvisited
|
||||
auto enqueueIfNotVisited = [&](const Handle(Standard_Transient)& ent)
|
||||
{
|
||||
if (ent.IsNull()) return;
|
||||
// theGraph.Model()->Number(ent) gives 1-based index in the model
|
||||
Standard_Integer idx = model->Number(ent);
|
||||
// Only enqueue if it's in the model and hasn't been visited
|
||||
if (idx > 0 && visited.find(idx) == visited.end())
|
||||
{
|
||||
visited.insert(idx);
|
||||
toVisit.push(ent);
|
||||
}
|
||||
};
|
||||
|
||||
// Initialize BFS with the root
|
||||
enqueueIfNotVisited(rootEntity);
|
||||
|
||||
// BFS loop
|
||||
while (!toVisit.empty())
|
||||
{
|
||||
Handle(Standard_Transient) current = toVisit.front();
|
||||
toVisit.pop();
|
||||
|
||||
// If current is or derives from targetType, record it
|
||||
if (current->IsKind(targetType))
|
||||
{
|
||||
matchedEntities->Append(current);
|
||||
}
|
||||
|
||||
// Get child references using Sharings(...) = "downstream" references
|
||||
// (If you need “upstream,” you’d use Shareds(...).)
|
||||
Interface_EntityIterator sharings = theGraph.Sharings(current);
|
||||
for (sharings.Start(); sharings.More(); sharings.Next())
|
||||
{
|
||||
enqueueIfNotVisited(sharings.Value());
|
||||
}
|
||||
}
|
||||
|
||||
// Build an Interface_EntityIterator from the matched sequence
|
||||
return {matchedEntities};
|
||||
}
|
||||
|
||||
// A BFS that visits both Sharings (downstream references) and Shareds (upstream references).
|
||||
// This ensures we don't miss geometry that might only be discovered by climbing "up"
|
||||
// to a higher-level entity, then going "down" again.
|
||||
Interface_EntityIterator Get_Associated_SolidModel_BiDirectional(
|
||||
const Handle(Standard_Transient)& rootEntity,
|
||||
const Handle(Standard_Type)& targetType,
|
||||
const Interface_Graph& theGraph)
|
||||
{
|
||||
// Where we'll store any entity matching `targetType` that we encounter.
|
||||
Handle(TColStd_HSequenceOfTransient) matchedEntities = new TColStd_HSequenceOfTransient();
|
||||
|
||||
if (rootEntity.IsNull())
|
||||
{
|
||||
return {matchedEntities};
|
||||
}
|
||||
|
||||
// We'll need the model to get entity indices.
|
||||
Handle(Interface_InterfaceModel) model = theGraph.Model();
|
||||
if (model.IsNull())
|
||||
{
|
||||
return {matchedEntities};
|
||||
}
|
||||
|
||||
std::queue<Handle(Standard_Transient)> toVisit;
|
||||
std::unordered_set<Standard_Integer> visited; // track visited by their 1-based model index
|
||||
|
||||
// Helper to enqueue an entity if not already visited
|
||||
auto enqueueIfNotVisited = [&](const Handle(Standard_Transient)& ent)
|
||||
{
|
||||
if (!ent.IsNull())
|
||||
{
|
||||
Standard_Integer idx = model->Number(ent);
|
||||
if (idx > 0 && visited.find(idx) == visited.end())
|
||||
{
|
||||
visited.insert(idx);
|
||||
toVisit.push(ent);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Start BFS from the root
|
||||
enqueueIfNotVisited(rootEntity);
|
||||
|
||||
// BFS loop
|
||||
while (!toVisit.empty())
|
||||
{
|
||||
Handle(Standard_Transient) current = toVisit.front();
|
||||
toVisit.pop();
|
||||
|
||||
// Gather children (Sharings): "downstream" references
|
||||
{
|
||||
Interface_EntityIterator childIter = theGraph.Sharings(current);
|
||||
for (childIter.Start(); childIter.More(); childIter.Next())
|
||||
{
|
||||
enqueueIfNotVisited(childIter.Value());
|
||||
}
|
||||
}
|
||||
|
||||
// Gather parents (Shareds): "upstream" references
|
||||
{
|
||||
// Traverse SHAPE_REPRESENTATION_RELATIONSHIP to follow links
|
||||
if (current->IsKind(STANDARD_TYPE(StepRepr_ShapeRepresentationRelationship)))
|
||||
{
|
||||
Handle(StepRepr_ShapeRepresentationRelationship) relationship =
|
||||
Handle(StepRepr_ShapeRepresentationRelationship)::DownCast(current);
|
||||
|
||||
auto relatedRep2 = relationship->Rep2(); // Second representation
|
||||
|
||||
enqueueIfNotVisited(relatedRep2);
|
||||
}
|
||||
// if iskind product, skip this
|
||||
else if (current->IsKind(STANDARD_TYPE(StepShape_ShapeDefinitionRepresentation)))
|
||||
{
|
||||
Handle(StepShape_ShapeDefinitionRepresentation) item =
|
||||
Handle(StepShape_ShapeDefinitionRepresentation)::DownCast(current);
|
||||
auto rest = item->UsedRepresentation();
|
||||
enqueueIfNotVisited(rest);
|
||||
}
|
||||
else if (current->IsKind(STANDARD_TYPE(StepShape_ShapeRepresentation)))
|
||||
{
|
||||
Handle(StepShape_ShapeRepresentation) shapeRep =
|
||||
Handle(StepShape_ShapeRepresentation)::DownCast(current);
|
||||
|
||||
auto items = shapeRep->Items();
|
||||
if (!items.IsNull())
|
||||
{
|
||||
Standard_Integer nbItems = items->Length();
|
||||
for (Standard_Integer i = 1; i <= nbItems; i++)
|
||||
{
|
||||
// Each element is a StepRepr_RepresentationItem
|
||||
Handle(StepRepr_RepresentationItem) repItem = items->Value(i);
|
||||
|
||||
// Check if it is a solid model or another geometric representation
|
||||
if (repItem->IsKind(STANDARD_TYPE(StepShape_SolidModel)))
|
||||
{
|
||||
Handle(StepShape_SolidModel) solidModel =
|
||||
Handle(StepShape_SolidModel)::DownCast(repItem);
|
||||
// append only if solidmodel is not already added. Can contain many geometries
|
||||
if (visited.find(model->Number(solidModel)) == visited.end())
|
||||
{
|
||||
visited.insert(model->Number(solidModel));
|
||||
matchedEntities->Append(solidModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now build an iterator from the matched sequence
|
||||
return {matchedEntities};
|
||||
}
|
||||
|
||||
|
||||
// Function to perform tessellation with a timeout
|
||||
bool perform_tessellation_with_timeout(const TopoDS_Shape &shape, const IMeshTools_Parameters &meshParams,
|
||||
const int timeoutSeconds, const Handle(CustomProgressIndicator) &progress) {
|
||||
|
||||
|
||||
// Create a progress range with a default name and range
|
||||
const Message_ProgressRange progressRange = progress->Start();
|
||||
|
||||
// Flag to track if the operation timed out
|
||||
std::atomic tessellationComplete = false;
|
||||
|
||||
// Launch tessellation in a separate thread
|
||||
std::thread tessellationThread([&]() {
|
||||
try {
|
||||
// Run tessellation with progress monitoring
|
||||
BRepMesh_IncrementalMesh mesh(shape, meshParams, progressRange);
|
||||
|
||||
// Mark as complete
|
||||
tessellationComplete = true;
|
||||
} catch (const Standard_Failure &e) {
|
||||
std::cerr << "Tessellation failed: " << e.GetMessageString() << "\n";
|
||||
}
|
||||
});
|
||||
|
||||
// Monitor the tessellation thread for timeout
|
||||
const auto start = std::chrono::steady_clock::now();
|
||||
while (std::chrono::steady_clock::now() - start < std::chrono::seconds(timeoutSeconds)) {
|
||||
if (tessellationComplete) {
|
||||
// Tessellation finished successfully
|
||||
std::cout << "Tessellation completed successfully.\n";
|
||||
tessellationThread.join();
|
||||
return true;
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50)); // Poll every 50 ms
|
||||
}
|
||||
|
||||
// Timeout occurred, cancel tessellation
|
||||
progress->Cancel();
|
||||
if (tessellationThread.joinable()) {
|
||||
tessellationThread.join();
|
||||
}
|
||||
|
||||
return false; // Tessellation timed out
|
||||
}
|
||||
67
src/cadit/occt/step_helpers.h
Normal file
67
src/cadit/occt/step_helpers.h
Normal file
@ -0,0 +1,67 @@
|
||||
//
|
||||
// Created by ofskrand on 07.01.2025.
|
||||
//
|
||||
|
||||
#ifndef STEP_HELPERS_H
|
||||
#define STEP_HELPERS_H
|
||||
|
||||
#include <STEPCAFControl_Reader.hxx>
|
||||
#include <BRepMesh_IncrementalMesh.hxx>
|
||||
#include <StepBasic_Product.hxx>
|
||||
#include <Interface_Graph.hxx>
|
||||
#include <XCAFDoc_ShapeTool.hxx>
|
||||
#include <StepShape_SolidModel.hxx>
|
||||
#include <StepShape_Face.hxx>
|
||||
#include <string>
|
||||
#include <TopoDS_Shape.hxx> // Include the necessary OpenCascade header for TopoDS_Shape
|
||||
#include "custom_progress.h"
|
||||
|
||||
std::string getStepProductName(const Handle(Standard_Transient) &entity, Interface_Graph &theGraph);
|
||||
|
||||
void update_location(TopoDS_Shape &shape);
|
||||
|
||||
std::string get_name(const Handle(StepRepr_RepresentationItem) &repr_item);
|
||||
|
||||
TopoDS_Shape make_shape(const Handle(StepShape_Face) &face, STEPControl_Reader &reader);
|
||||
|
||||
TDF_Label add_shape_to_document(const TopoDS_Shape &shape, const std::string &name,
|
||||
const Handle(XCAFDoc_ShapeTool) &shape_tool, IMeshTools_Parameters &meshParams);
|
||||
|
||||
struct ConvertObject {
|
||||
std::string name; // Name of the object
|
||||
TopoDS_Shape shape; // Shape data
|
||||
bool AddedToModel = false; // Indicates if the object is added to the model
|
||||
TDF_Label shape_label; // Label for the shape in the document
|
||||
|
||||
// Constructor
|
||||
ConvertObject(const std::string& name, const TopoDS_Shape& , TDF_Label& shape_label, bool addedToModel = false);
|
||||
|
||||
// Optional: Destructor
|
||||
~ConvertObject();
|
||||
};
|
||||
|
||||
|
||||
ConvertObject entity_to_shape(const Handle(Standard_Transient) &entity,
|
||||
STEPControl_Reader default_reader,
|
||||
const Handle(XCAFDoc_ShapeTool) &shape_tool,
|
||||
IMeshTools_Parameters &meshParams,
|
||||
const bool solid_only = false);
|
||||
|
||||
std::string getStepProductNameFromGraph(const Handle(Standard_Transient) &entity, Interface_Graph &theGraph);
|
||||
|
||||
bool CustomFilter(const Handle(Standard_Transient)& entity);
|
||||
Interface_EntityIterator MyTypedExpansions(const Handle(Standard_Transient)& rootEntity,
|
||||
const Handle(Standard_Type)& targetType,
|
||||
const Interface_Graph& theGraph);
|
||||
|
||||
Interface_EntityIterator Get_Associated_SolidModel_BiDirectional(
|
||||
const Handle(Standard_Transient)& rootEntity,
|
||||
const Handle(Standard_Type)& targetType,
|
||||
const Interface_Graph& theGraph);
|
||||
|
||||
gp_Trsf get_product_transform(TopoDS_Shape& shape, const Handle(StepBasic_Product)& product);
|
||||
|
||||
bool perform_tessellation_with_timeout(const TopoDS_Shape &shape, const IMeshTools_Parameters &meshParams,
|
||||
const int timeoutSeconds, const Handle(CustomProgressIndicator) &progress);
|
||||
|
||||
#endif //STEP_HELPERS_H
|
||||
692
src/cadit/occt/step_tree.cpp
Normal file
692
src/cadit/occt/step_tree.cpp
Normal file
@ -0,0 +1,692 @@
|
||||
#include <Interface_InterfaceModel.hxx>
|
||||
#include <Interface_Graph.hxx>
|
||||
#include <Interface_EntityIterator.hxx>
|
||||
|
||||
// STEP entity classes
|
||||
#include <StepBasic_Product.hxx>
|
||||
#include <StepBasic_ProductDefinition.hxx>
|
||||
#include <StepBasic_ProductDefinitionFormation.hxx>
|
||||
#include <StepRepr_NextAssemblyUsageOccurrence.hxx>
|
||||
#include <StepRepr_ProductDefinitionShape.hxx>
|
||||
#include <TCollection_HAsciiString.hxx>
|
||||
#include <Standard_Type.hxx>
|
||||
#include <Standard_Transient.hxx>
|
||||
#include <StepRepr_RepresentationRelationshipWithTransformation.hxx>
|
||||
#include <StepRepr_ItemDefinedTransformation.hxx>
|
||||
#include <StepRepr_Transformation.hxx>
|
||||
#include <StepShape_ContextDependentShapeRepresentation.hxx>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <queue>
|
||||
|
||||
#include <sstream>
|
||||
#include "step_tree.h"
|
||||
|
||||
#include <gp_Ax1.hxx>
|
||||
#include <gp_Ax3.hxx>
|
||||
#include <ranges>
|
||||
#include <stack>
|
||||
#include <StepGeom_Axis2Placement3d.hxx>
|
||||
#include <StepGeom_CartesianPoint.hxx>
|
||||
#include <StepGeom_Direction.hxx>
|
||||
|
||||
#include "step_helpers.h"
|
||||
|
||||
|
||||
// Function to compute the transformation matrix for a given assembly instance
|
||||
gp_Trsf GetTransformationMatrix(
|
||||
const Handle(StepRepr_NextAssemblyUsageOccurrence) &nauo,
|
||||
const Interface_Graph &theGraph) {
|
||||
gp_Trsf transformation;
|
||||
|
||||
// Get the Related ProductDefinition
|
||||
Handle(StepBasic_ProductDefinition) relatedProdDef = nauo->RelatedProductDefinition();
|
||||
if (relatedProdDef.IsNull()) {
|
||||
return transformation; // Return identity transform if no related product definition
|
||||
}
|
||||
|
||||
// Find the corresponding ProductDefinitionShape
|
||||
Handle(StepRepr_ProductDefinitionShape) relatedShape;
|
||||
Interface_EntityIterator refs = theGraph.Sharings(relatedProdDef);
|
||||
for (refs.Start(); refs.More(); refs.Next()) {
|
||||
const Handle(Standard_Transient) &refEntity = refs.Value();
|
||||
if (refEntity->IsKind(STANDARD_TYPE(StepRepr_ProductDefinitionShape))) {
|
||||
relatedShape = Handle(StepRepr_ProductDefinitionShape)::DownCast(refEntity);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If no ProductDefinitionShape is found, return identity transform
|
||||
if (relatedShape.IsNull()) {
|
||||
return transformation;
|
||||
}
|
||||
|
||||
// Look for an Axis2Placement3D referenced by the ProductDefinitionShape
|
||||
Interface_EntityIterator shapeRefs = theGraph.Sharings(relatedShape);
|
||||
for (shapeRefs.Start(); shapeRefs.More(); shapeRefs.Next()) {
|
||||
const Handle(Standard_Transient) refEntity = shapeRefs.Value();
|
||||
if (refEntity->IsKind(STANDARD_TYPE(StepGeom_Axis2Placement3d))) {
|
||||
auto axisPlacement = Handle(StepGeom_Axis2Placement3d)::DownCast(refEntity);
|
||||
|
||||
// Extract translation
|
||||
if (!axisPlacement->Location().IsNull()) {
|
||||
auto location = axisPlacement->Location();
|
||||
transformation.SetTranslation(
|
||||
gp_Vec(location->CoordinatesValue(1), location->CoordinatesValue(2),
|
||||
location->CoordinatesValue(3)));
|
||||
}
|
||||
|
||||
// Extract rotation
|
||||
if (!axisPlacement->RefDirection().IsNull() && !axisPlacement->Axis().IsNull()) {
|
||||
auto refDirection = axisPlacement->RefDirection();
|
||||
auto axis = axisPlacement->Axis();
|
||||
auto angle = 0.0;
|
||||
|
||||
transformation.SetRotation(
|
||||
gp_Ax1(gp_Pnt(0, 0, 0),
|
||||
gp_Dir(axis->DirectionRatiosValue(1), axis->DirectionRatiosValue(2),
|
||||
axis->DirectionRatiosValue(3))),
|
||||
angle);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return transformation;
|
||||
}
|
||||
|
||||
gp_Trsf ComputeTransformationFromAxis2Placement(const Handle(StepGeom_Axis2Placement3d) &placement) {
|
||||
gp_Trsf transform;
|
||||
if (!placement.IsNull()) {
|
||||
// Extract translation
|
||||
if (!placement->Location().IsNull()) {
|
||||
const auto loc = placement->Location();
|
||||
transform.SetTranslation(gp_Vec(loc->CoordinatesValue(1), loc->CoordinatesValue(2),
|
||||
loc->CoordinatesValue(3)));
|
||||
}
|
||||
|
||||
// Extract rotation from directions
|
||||
if (!placement->RefDirection().IsNull() && !placement->Axis().IsNull()) {
|
||||
gp_Dir xDir(placement->Axis()->DirectionRatiosValue(1),
|
||||
placement->Axis()->DirectionRatiosValue(2),
|
||||
placement->Axis()->DirectionRatiosValue(3));
|
||||
gp_Dir zDir(placement->RefDirection()->DirectionRatiosValue(1),
|
||||
placement->RefDirection()->DirectionRatiosValue(2),
|
||||
placement->RefDirection()->DirectionRatiosValue(3));
|
||||
gp_Ax3 localAxis(transform.TranslationPart(), zDir, xDir);
|
||||
transform.SetTransformation(localAxis);
|
||||
}
|
||||
}
|
||||
return transform;
|
||||
}
|
||||
|
||||
gp_Trsf GetTransformFromShapeRelWithTrans(
|
||||
const Handle(StepRepr_RepresentationRelationshipWithTransformation) &relWithTrans) {
|
||||
gp_Trsf transformation;
|
||||
auto itemDefinedTrans = relWithTrans->TransformationOperator();
|
||||
if (!itemDefinedTrans.IsNull()) {
|
||||
auto itemDefTrans = itemDefinedTrans.ItemDefinedTransformation();
|
||||
auto trans1 = itemDefTrans->TransformItem1();
|
||||
auto trans2 = itemDefTrans->TransformItem2();
|
||||
if (!trans1.IsNull() && !trans2.IsNull()) {
|
||||
// Get AXIS2_PLACEMENT_3D entities for parent and child
|
||||
auto childPlacement = Handle(StepGeom_Axis2Placement3d)::DownCast(trans1);
|
||||
auto parentPlacement = Handle(StepGeom_Axis2Placement3d)::DownCast(trans2);
|
||||
|
||||
// Compute transformations
|
||||
if (!childPlacement.IsNull() && !parentPlacement.IsNull()) {
|
||||
gp_Trsf childTransform = ComputeTransformationFromAxis2Placement(childPlacement);
|
||||
gp_Trsf parentTransform = ComputeTransformationFromAxis2Placement(parentPlacement);
|
||||
|
||||
// Combine transformations: parent^-1 * child
|
||||
parentTransform.Invert();
|
||||
transformation = parentTransform * childTransform;
|
||||
}
|
||||
}
|
||||
}
|
||||
return transformation;
|
||||
}
|
||||
|
||||
|
||||
gp_Trsf GetAssemblyInstanceTransformation(
|
||||
const Handle(StepRepr_NextAssemblyUsageOccurrence) &nauo,
|
||||
const Interface_Graph &theGraph) {
|
||||
gp_Trsf transformation;
|
||||
|
||||
const Interface_EntityIterator refs = theGraph.Sharings(nauo);
|
||||
for (refs.Start(); refs.More(); refs.Next()) {
|
||||
const auto &entity = refs.Value();
|
||||
if (entity->IsKind(STANDARD_TYPE(StepRepr_ProductDefinitionShape))) {
|
||||
auto pds = Handle(StepRepr_ProductDefinitionShape)::DownCast(entity);
|
||||
Interface_EntityIterator pdsRefs = theGraph.Sharings(pds);
|
||||
for (pdsRefs.Start(); pdsRefs.More(); pdsRefs.Next()) {
|
||||
const auto &pdsRef = pdsRefs.Value();
|
||||
if (pdsRef->IsKind(STANDARD_TYPE(StepShape_ContextDependentShapeRepresentation))) {
|
||||
const auto contextDependentShape =
|
||||
Handle(StepShape_ContextDependentShapeRepresentation)::DownCast(pdsRef);
|
||||
auto r1 = contextDependentShape->RepresentationRelation();
|
||||
auto r2 = contextDependentShape->RepresentedProductRelation();
|
||||
if (!r1.IsNull() && r1->
|
||||
IsKind(STANDARD_TYPE(StepRepr_RepresentationRelationshipWithTransformation))) {
|
||||
auto relWithTrans = Handle(StepRepr_RepresentationRelationshipWithTransformation)::DownCast(r1);
|
||||
return GetTransformFromShapeRelWithTrans(relWithTrans);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return transformation;
|
||||
}
|
||||
|
||||
|
||||
// Helper: given a Product, find its associated ProductDefinition (if any)
|
||||
static Handle(StepBasic_ProductDefinition) FindProductDefinition(
|
||||
const Handle(StepBasic_Product) &product,
|
||||
const Handle(Interface_InterfaceModel) &model, const Interface_Graph &theGraph) {
|
||||
// A Product typically references a ProductDefinitionFormation
|
||||
// which references a ProductDefinition. Or in some schemas,
|
||||
// the Product's "FrameOfReference()" leads to the PDF.
|
||||
// So we walk the entire model to match the formation to the definition.
|
||||
//
|
||||
// If your schema doesn't use Formation(), adapt accordingly (e.g. FrameOfReference).
|
||||
Handle(StepBasic_ProductDefinitionFormation) formation; {
|
||||
const Interface_EntityIterator childIter = theGraph.Sharings(product);
|
||||
for (childIter.Start(); childIter.More(); childIter.Next()) {
|
||||
Handle(Standard_Transient) ent = childIter.Value();
|
||||
if (ent->IsKind(STANDARD_TYPE(StepBasic_ProductDefinitionFormation))) {
|
||||
formation = Handle(StepBasic_ProductDefinitionFormation)::DownCast(ent);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (formation.IsNull()) {
|
||||
return nullptr;
|
||||
} {
|
||||
const Interface_EntityIterator childIter = theGraph.Sharings(formation);
|
||||
for (childIter.Start(); childIter.More(); childIter.Next()) {
|
||||
Handle(Standard_Transient) ent = childIter.Value();
|
||||
if (ent->IsKind(STANDARD_TYPE(StepBasic_ProductDefinition))) {
|
||||
return Handle(StepBasic_ProductDefinition)::DownCast(ent);
|
||||
}
|
||||
}
|
||||
}
|
||||
// The ProductDefinition we want references this formation
|
||||
for (Standard_Integer i = 1; i <= model->NbEntities(); i++) {
|
||||
Handle(Standard_Transient) ent = model->Value(i);
|
||||
if (ent->IsKind(STANDARD_TYPE(StepBasic_ProductDefinition))) {
|
||||
auto pd = Handle(StepBasic_ProductDefinition)::DownCast(ent);
|
||||
if (!pd.IsNull() && pd->Formation() == formation) {
|
||||
return pd;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Build a map from StepBasic_Product -> entity index, for quick lookup
|
||||
static std::unordered_map<Handle(StepBasic_Product), int>
|
||||
BuildProductIndexMap(const Handle(Interface_InterfaceModel) &model) {
|
||||
std::unordered_map<Handle(StepBasic_Product), int> productToIndex;
|
||||
|
||||
for (Standard_Integer i = 1; i <= model->NbEntities(); i++) {
|
||||
Handle(Standard_Transient) entity = model->Value(i);
|
||||
if (entity->IsKind(STANDARD_TYPE(StepBasic_Product))) {
|
||||
auto product = Handle(StepBasic_Product)::DownCast(entity);
|
||||
if (!product.IsNull()) {
|
||||
productToIndex[product] = i; // store 1-based index
|
||||
}
|
||||
}
|
||||
}
|
||||
return productToIndex;
|
||||
}
|
||||
|
||||
// Build a map: parentIndex -> list of child indices
|
||||
static std::unordered_map<int, std::vector<int> >
|
||||
BuildAssemblyLinks(const Handle(Interface_InterfaceModel) &model, const Interface_Graph &theGraph) {
|
||||
// We'll track: Parent's entity index -> [Child's entity index...]
|
||||
std::unordered_map<int, std::vector<int> > parentToChildren;
|
||||
|
||||
// Build a quick map from ProductDefinition -> (Product, index)
|
||||
// so that given a PD, we know which Product it belongs to
|
||||
std::unordered_map<Handle(StepBasic_ProductDefinition), int> pdToProductIndex;
|
||||
|
||||
// 1) find all products, map each to its product definition
|
||||
auto productIndexMap = BuildProductIndexMap(model);
|
||||
for (auto &kv: productIndexMap) {
|
||||
Handle(StepBasic_Product) product = kv.first;
|
||||
int productIdx = kv.second;
|
||||
|
||||
Handle(StepBasic_ProductDefinition) pd = FindProductDefinition(product, model, theGraph);
|
||||
if (!pd.IsNull()) {
|
||||
pdToProductIndex[pd] = productIdx;
|
||||
}
|
||||
}
|
||||
|
||||
// 2) Traverse NextAssemblyUsageOccurrence to see parent-child relationships
|
||||
// Each NAUO references two ProductDefinitions: "RelatingProductDefinition"
|
||||
// (the parent) and "RelatedProductDefinition" (the child).
|
||||
for (Standard_Integer i = 1; i <= model->NbEntities(); i++) {
|
||||
Handle(Standard_Transient) ent = model->Value(i);
|
||||
if (ent->IsKind(STANDARD_TYPE(StepRepr_NextAssemblyUsageOccurrence))) {
|
||||
auto nauo = Handle(StepRepr_NextAssemblyUsageOccurrence)::DownCast(ent);
|
||||
if (!nauo.IsNull()) {
|
||||
Handle(StepBasic_ProductDefinition) pdParent = nauo->RelatingProductDefinition();
|
||||
Handle(StepBasic_ProductDefinition) pdChild = nauo->RelatedProductDefinition();
|
||||
|
||||
auto itParent = pdToProductIndex.find(pdParent);
|
||||
auto itChild = pdToProductIndex.find(pdChild);
|
||||
if (itParent != pdToProductIndex.end() && itChild != pdToProductIndex.end()) {
|
||||
int parentIdx = itParent->second;
|
||||
int childIdx = itChild->second;
|
||||
|
||||
parentToChildren[parentIdx].push_back(childIdx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return parentToChildren;
|
||||
}
|
||||
|
||||
// Build a map: parentIndex -> list of child indices with transformations
|
||||
static std::unordered_map<int, std::vector<ParentChildRelationship> >
|
||||
BuildAssemblyLinksWithTransformation(const Handle(Interface_InterfaceModel) &model, const Interface_Graph &theGraph) {
|
||||
// Map to store parent-child relationships along with transformations
|
||||
std::unordered_map<int, std::vector<ParentChildRelationship> > parentToChildrenWithTrans;
|
||||
|
||||
// Build a quick map from ProductDefinition -> (Product, index)
|
||||
std::unordered_map<Handle(StepBasic_ProductDefinition), int> pdToProductIndex;
|
||||
|
||||
// 1) Find all products, map each to its product definition
|
||||
auto productIndexMap = BuildProductIndexMap(model);
|
||||
for (auto &kv: productIndexMap) {
|
||||
Handle(StepBasic_Product) product = kv.first;
|
||||
int productIdx = kv.second;
|
||||
|
||||
Handle(StepBasic_ProductDefinition) pd = FindProductDefinition(product, model, theGraph);
|
||||
if (!pd.IsNull()) {
|
||||
pdToProductIndex[pd] = productIdx;
|
||||
}
|
||||
}
|
||||
|
||||
// 2) Traverse NextAssemblyUsageOccurrence to see parent-child relationships
|
||||
// Each NAUO references two ProductDefinitions: "RelatingProductDefinition"
|
||||
// (the parent) and "RelatedProductDefinition" (the child).
|
||||
for (Standard_Integer i = 1; i <= model->NbEntities(); i++) {
|
||||
Handle(Standard_Transient) ent = model->Value(i);
|
||||
if (ent->IsKind(STANDARD_TYPE(StepRepr_NextAssemblyUsageOccurrence))) {
|
||||
auto nauo = Handle(StepRepr_NextAssemblyUsageOccurrence)::DownCast(ent);
|
||||
if (!nauo.IsNull()) {
|
||||
Handle(StepBasic_ProductDefinition) pdParent = nauo->RelatingProductDefinition();
|
||||
Handle(StepBasic_ProductDefinition) pdChild = nauo->RelatedProductDefinition();
|
||||
|
||||
auto itParent = pdToProductIndex.find(pdParent);
|
||||
auto itChild = pdToProductIndex.find(pdChild);
|
||||
if (itParent != pdToProductIndex.end() && itChild != pdToProductIndex.end()) {
|
||||
int parentIdx = itParent->second;
|
||||
int childIdx = itChild->second;
|
||||
|
||||
// Retrieve the transformation (if available) for the parent-child relationship
|
||||
gp_Trsf transformation = GetAssemblyInstanceTransformation(nauo, theGraph);
|
||||
|
||||
// Add the parent-child relationship with transformation
|
||||
parentToChildrenWithTrans[parentIdx].emplace_back(parentIdx, childIdx, transformation, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return parentToChildrenWithTrans;
|
||||
}
|
||||
|
||||
// Definition of the static counter
|
||||
int ProductNode::instanceCounter = 0;
|
||||
// Main function: extracts top-level ProductNode trees with transformations
|
||||
std::vector<std::unique_ptr<ProductNode> > ExtractProductHierarchy(const Handle(Interface_InterfaceModel) &model,
|
||||
const Interface_Graph &theGraph) {
|
||||
// 1) Build the map of parent->children relationships
|
||||
// to be replaced by this
|
||||
const auto parentToChildrenWTransforms = BuildAssemblyLinksWithTransformation(model, theGraph);
|
||||
|
||||
// 2) We want to find "root" products (those that never appear as a child)
|
||||
std::unordered_set<int> allChildren;
|
||||
for (auto &kv: parentToChildrenWTransforms) {
|
||||
for (const auto &childRel: kv.second) // Access the ParentChildRelationship (includes transformation)
|
||||
{
|
||||
allChildren.insert(childRel.childIndex);
|
||||
}
|
||||
}
|
||||
|
||||
// 3) Gather all product indices
|
||||
std::vector<int> allProducts;
|
||||
for (auto productIndexMap = BuildProductIndexMap(model); auto &val: productIndexMap | std::views::values) {
|
||||
allProducts.push_back(val);
|
||||
}
|
||||
|
||||
// 4) For each product, if it’s NOT in allChildren => it’s a root
|
||||
std::vector<std::unique_ptr<ProductNode> > roots;
|
||||
for (int idx: allProducts) {
|
||||
if (!allChildren.contains(idx)) {
|
||||
// This is a root product, start with the identity transformation
|
||||
roots.push_back(
|
||||
BuildProductNodeWithTransform(idx, parentToChildrenWTransforms, model, theGraph, gp_Trsf()));
|
||||
}
|
||||
}
|
||||
return roots;
|
||||
}
|
||||
|
||||
|
||||
std::vector<Handle(StepRepr_NextAssemblyUsageOccurrence) > Get_NextAssemblyUsageOccurrences(
|
||||
const Handle(StepBasic_Product) &product,
|
||||
const Interface_Graph &theGraph) {
|
||||
std::vector<Handle(StepRepr_NextAssemblyUsageOccurrence) > nauos;
|
||||
|
||||
Interface_EntityIterator childIter = theGraph.Sharings(product);
|
||||
for (childIter.Start(); childIter.More(); childIter.Next()) {
|
||||
if (childIter.Value()->IsKind(STANDARD_TYPE(StepBasic_ProductDefinitionFormation))) {
|
||||
auto pdf = Handle(StepBasic_ProductDefinitionFormation)::DownCast(childIter.Value());
|
||||
Interface_EntityIterator pdfIter = theGraph.Sharings(pdf);
|
||||
for (pdfIter.Start(); pdfIter.More(); pdfIter.Next()) {
|
||||
if (pdfIter.Value()->IsKind(STANDARD_TYPE(StepBasic_ProductDefinition))) {
|
||||
auto pd = Handle(StepBasic_ProductDefinition)::DownCast(pdfIter.Value());
|
||||
Interface_EntityIterator pdIter = theGraph.Sharings(pd);
|
||||
for (pdIter.Start(); pdIter.More(); pdIter.Next()) {
|
||||
if (pdIter.Value()->IsKind(STANDARD_TYPE(StepRepr_NextAssemblyUsageOccurrence))) {
|
||||
nauos.push_back(Handle(StepRepr_NextAssemblyUsageOccurrence)::DownCast(pdIter.Value()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nauos;
|
||||
}
|
||||
|
||||
Handle(StepRepr_NextAssemblyUsageOccurrence) Get_NextAssemblyUsageOccurrence(const Handle(StepBasic_Product) &product,
|
||||
const Interface_Graph &theGraph) {
|
||||
auto result = Get_NextAssemblyUsageOccurrences(product, theGraph);
|
||||
if (result.size() == 1) {
|
||||
return result[0];
|
||||
}
|
||||
// return last element
|
||||
return result[result.size() - 1];
|
||||
}
|
||||
|
||||
|
||||
void add_geometries_to_nodes(const std::vector<std::unique_ptr<ProductNode> > &nodes, const Interface_Graph &theGraph) {
|
||||
for (auto &ref_node: nodes) {
|
||||
// 'node' is a std::unique_ptr<ProductNode>&
|
||||
if (!ref_node) {
|
||||
// If it's a null pointer, skip it
|
||||
continue;
|
||||
}
|
||||
auto &node = *ref_node;
|
||||
auto &product = theGraph.Entity(node.entityIndex);
|
||||
Interface_EntityIterator breps =
|
||||
Get_Associated_SolidModel_BiDirectional(product,
|
||||
STANDARD_TYPE(StepShape_SolidModel),
|
||||
theGraph);
|
||||
// get the geometry indices
|
||||
while (breps.More()) {
|
||||
const auto &entity = breps.Value();
|
||||
auto entityIndex = theGraph.Model()->Number(entity);
|
||||
node.geometryInstances.push_back(GeometryInstance(entityIndex));
|
||||
breps.Next();
|
||||
}
|
||||
|
||||
if (!node.children.empty()) {
|
||||
add_geometries_to_nodes(node.children, theGraph);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Recursive function that builds a ProductNode tree with transformations
|
||||
static std::unique_ptr<ProductNode> BuildProductNodeWithTransform(
|
||||
int productIndex,
|
||||
const std::unordered_map<int, std::vector<ParentChildRelationship> > &parentToChildrenWTransforms,
|
||||
const Handle(Interface_InterfaceModel) &model,
|
||||
const Interface_Graph &theGraph,
|
||||
const gp_Trsf &parentTransform,
|
||||
ProductNode *parent) {
|
||||
auto node = std::make_unique<ProductNode>();
|
||||
node->entityIndex = productIndex;
|
||||
node->parent = parent;
|
||||
|
||||
const Handle(Standard_Transient) ent = model->Value(productIndex);
|
||||
const auto product = Handle(StepBasic_Product)::DownCast(ent);
|
||||
|
||||
if (!product.IsNull() && !product->Name().IsNull()) {
|
||||
node->name = product->Name()->ToCString();
|
||||
} else {
|
||||
node->name = "(unnamed product)";
|
||||
}
|
||||
|
||||
// Combine parent transformation with local transformation
|
||||
const gp_Trsf absoluteTransform = parentTransform;
|
||||
node->transformation = absoluteTransform;
|
||||
|
||||
// Recurse for children, now using ParentChildRelationship
|
||||
auto it = parentToChildrenWTransforms.find(productIndex);
|
||||
if (it != parentToChildrenWTransforms.end()) {
|
||||
for (const auto &childRel: it->second) // Access the ParentChildRelationship (includes transformation)
|
||||
{
|
||||
// Combine parent transformation with the transformation from ParentChildRelationship
|
||||
gp_Trsf childAbsoluteTransform = absoluteTransform;
|
||||
childAbsoluteTransform.Multiply(childRel.transformation);
|
||||
|
||||
// Build the child node
|
||||
node->children.push_back(
|
||||
BuildProductNodeWithTransform(childRel.childIndex, parentToChildrenWTransforms, model, theGraph,
|
||||
childAbsoluteTransform, node.get()));
|
||||
}
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
// Iterative function
|
||||
std::unique_ptr<ProductNode> BuildProductNodeWithTransformIterative(
|
||||
int rootIndex,
|
||||
const std::unordered_map<int, std::vector<int> > &parentToChildren,
|
||||
const Handle(Interface_InterfaceModel) &model,
|
||||
const Interface_Graph &theGraph,
|
||||
const gp_Trsf &rootTransform) {
|
||||
// Create the root node
|
||||
auto rootNode = std::make_unique<ProductNode>();
|
||||
rootNode->parent = nullptr;
|
||||
rootNode->entityIndex = rootIndex;
|
||||
// We'll fill in transform/name/etc. below
|
||||
|
||||
// A stack item for our DFS
|
||||
struct StackItem {
|
||||
ProductNode *node; // pointer to the node in which we'll store data
|
||||
int productIndex; // which product index this node corresponds to
|
||||
gp_Trsf parentTrsf; // the parent's final transform to be combined with local transform
|
||||
};
|
||||
|
||||
std::stack<StackItem> stack;
|
||||
stack.push({rootNode.get(), rootIndex, rootTransform});
|
||||
|
||||
// A set to track visited product indices
|
||||
// to avoid infinite loops if there's a cycle
|
||||
std::unordered_set<int> visited;
|
||||
visited.insert(rootIndex);
|
||||
|
||||
while (!stack.empty()) {
|
||||
auto [nodePtr, productIdx, accumulatedTrsf] = stack.top();
|
||||
stack.pop();
|
||||
|
||||
// --- 1) Safety checks to avoid segfaults ---
|
||||
|
||||
// If productIdx is invalid (e.g. out of range for the model),
|
||||
// we skip filling data and proceed. Depending on your data,
|
||||
// you might want to throw an exception instead.
|
||||
if (productIdx < 1 || productIdx > model->NbEntities()) {
|
||||
// Mark something or skip
|
||||
nodePtr->name = "(invalid index)";
|
||||
// nodePtr->instanceIndex = -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
// --- 2) Look up the product from the model ---
|
||||
Handle(Standard_Transient) ent = model->Value(productIdx);
|
||||
if (ent.IsNull()) {
|
||||
nodePtr->name = "(model->Value() returned null)";
|
||||
// nodePtr->instanceIndex = -1;
|
||||
continue;
|
||||
}
|
||||
auto product = Handle(StepBasic_Product)::DownCast(ent);
|
||||
if (product.IsNull()) {
|
||||
nodePtr->name = "(not a StepBasic_Product)";
|
||||
// nodePtr->instanceIndex = -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
// --- 3) Compute local transform and combine with parent's ---
|
||||
auto nauo = Get_NextAssemblyUsageOccurrence(product, theGraph);
|
||||
if (!nauo.IsNull()) {
|
||||
gp_Trsf localTransform = GetAssemblyInstanceTransformation(nauo, theGraph);
|
||||
|
||||
gp_Trsf finalTransform = accumulatedTrsf;
|
||||
finalTransform.Multiply(localTransform);
|
||||
nodePtr->transformation = finalTransform;
|
||||
|
||||
// Instance index from the graph
|
||||
nodePtr->instanceIndex = theGraph.Model()->Number(nauo);
|
||||
} else {
|
||||
// If NAUO is invalid for some reason
|
||||
nodePtr->transformation = accumulatedTrsf;
|
||||
// nodePtr->instanceIndex = -1;
|
||||
}
|
||||
|
||||
// --- 4) Fill the node's name ---
|
||||
if (!product->Name().IsNull()) {
|
||||
nodePtr->name = product->Name()->ToCString();
|
||||
} else {
|
||||
nodePtr->name = "(unnamed product)";
|
||||
}
|
||||
|
||||
// --- 5) Create child nodes and push them onto the stack ---
|
||||
auto it = parentToChildren.find(productIdx);
|
||||
if (it != parentToChildren.end()) {
|
||||
for (int childIdx: it->second) {
|
||||
// If we've already seen this index, skip to avoid cycles
|
||||
if (!visited.insert(childIdx).second) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Create a child node by value in the parent's children vector
|
||||
nodePtr->children.emplace_back();
|
||||
std::unique_ptr<ProductNode> &childRef = nodePtr->children.back();
|
||||
childRef->parent = nodePtr;
|
||||
childRef->entityIndex = childIdx;
|
||||
|
||||
// We'll fill all the child details once we pop from the stack
|
||||
// For now, just push onto the stack
|
||||
stack.push({childRef.get(), childIdx, nodePtr->transformation});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rootNode;
|
||||
}
|
||||
|
||||
// Helper function to serialize a gp_Trsf (4x4 transformation matrix) to JSON
|
||||
static void TransformationToJson(const gp_Trsf &transform, std::ostream &os, int indentLevel) {
|
||||
// Utility lambda to insert some indentation spaces
|
||||
auto indent = [&](int level) {
|
||||
for (int i = 0; i < level; i++) os << " ";
|
||||
};
|
||||
|
||||
indent(indentLevel);
|
||||
os << "\"transformation\": [\n";
|
||||
|
||||
// gp_Trsf stores a 3x4 matrix (rotation and translation), so we manually append rows
|
||||
for (int i = 1; i <= 3; i++) {
|
||||
indent(indentLevel + 1);
|
||||
os << "[";
|
||||
for (int j = 1; j <= 4; j++) // OpenCASCADE uses 1-based indexing
|
||||
{
|
||||
os << transform.Value(i, j);
|
||||
if (j < 4) os << ", ";
|
||||
}
|
||||
os << "]";
|
||||
if (i < 3) os << ",\n";
|
||||
}
|
||||
|
||||
// Add the homogeneous row for a 4x4 matrix
|
||||
os << ",\n";
|
||||
indent(indentLevel + 1);
|
||||
os << "[0, 0, 0, 1]\n";
|
||||
|
||||
indent(indentLevel);
|
||||
os << "]";
|
||||
}
|
||||
|
||||
// Simple recursive JSON builder
|
||||
static void ProductNodeToJson(const ProductNode &node, std::ostream &os, int indentLevel) {
|
||||
// Utility lambda to insert some indentation spaces
|
||||
auto indent = [&](int level) {
|
||||
for (int i = 0; i < level; i++) os << " ";
|
||||
};
|
||||
|
||||
indent(indentLevel);
|
||||
os << "{\n";
|
||||
|
||||
indent(indentLevel + 1);
|
||||
os << "\"entityIndex\": " << node.entityIndex << ",\n";
|
||||
|
||||
indent(indentLevel + 1);
|
||||
os << "\"InstanceIndex\": " << node.instanceIndex << ",\n";
|
||||
|
||||
indent(indentLevel + 1);
|
||||
os << "\"targetIndex\": " << node.targetIndex.Tag() << ",\n";
|
||||
|
||||
indent(indentLevel + 1);
|
||||
os << "\"name\": \"" << node.name << "\",\n";
|
||||
|
||||
indent(indentLevel + 1);
|
||||
os << "\"geometryIndices\": [";
|
||||
for (size_t i = 0; i < node.geometryInstances.size(); i++) {
|
||||
os << node.geometryInstances[i].entityIndex;
|
||||
if (i + 1 < node.geometryInstances.size()) {
|
||||
os << ", ";
|
||||
}
|
||||
}
|
||||
os << "],\n";
|
||||
|
||||
// Add the transformation matrix to the JSON output
|
||||
TransformationToJson(node.transformation, os, indentLevel + 1);
|
||||
os << ",\n";
|
||||
|
||||
indent(indentLevel + 1);
|
||||
os << "\"children\": [\n";
|
||||
for (size_t i = 0; i < node.children.size(); i++) {
|
||||
ProductNodeToJson(*node.children[i], os, indentLevel + 2);
|
||||
if (i + 1 < node.children.size()) {
|
||||
os << ",";
|
||||
}
|
||||
os << "\n";
|
||||
}
|
||||
indent(indentLevel + 1);
|
||||
os << "]\n";
|
||||
|
||||
indent(indentLevel);
|
||||
os << "}";
|
||||
}
|
||||
|
||||
|
||||
std::string ExportHierarchyToJson(const std::vector<std::unique_ptr<ProductNode> > &roots) {
|
||||
std::ostringstream oss;
|
||||
oss << "[\n";
|
||||
for (size_t i = 0; i < roots.size(); i++) {
|
||||
ProductNodeToJson(*roots[i], oss, 1);
|
||||
if (i + 1 < roots.size()) {
|
||||
oss << ",";
|
||||
}
|
||||
oss << "\n";
|
||||
}
|
||||
oss << "]\n";
|
||||
|
||||
return oss.str();
|
||||
}
|
||||
98
src/cadit/occt/step_tree.h
Normal file
98
src/cadit/occt/step_tree.h
Normal file
@ -0,0 +1,98 @@
|
||||
//
|
||||
// Created by ofskrand on 09.01.2025.
|
||||
//
|
||||
|
||||
#ifndef STEP_TREE_H
|
||||
#define STEP_TREE_H
|
||||
|
||||
#include <gp_Trsf.hxx>
|
||||
#include <Interface_InterfaceModel.hxx>
|
||||
#include <Interface_Graph.hxx>
|
||||
#include <Standard_Handle.hxx> // For Handle
|
||||
#include <StepRepr_NextAssemblyUsageOccurrence.hxx>
|
||||
#include <coroutine>
|
||||
#include <string>
|
||||
#include <TDF_Label.hxx>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
struct ProcessResult {
|
||||
mutable bool added_to_model;
|
||||
mutable std::string skip_reason;
|
||||
mutable int geometryIndex;
|
||||
};
|
||||
|
||||
struct GeometryInstance {
|
||||
int entityIndex{};
|
||||
gp_Trsf transformation;
|
||||
};
|
||||
|
||||
// Updated struct
|
||||
struct ProductNode {
|
||||
// Static counter for generating unique instance indices
|
||||
static int instanceCounter;
|
||||
|
||||
|
||||
ProductNode* parent; // new field to keep track of the node's parent
|
||||
|
||||
int entityIndex;
|
||||
std::string name;
|
||||
std::vector<std::unique_ptr<ProductNode> > children;
|
||||
int instanceIndex;
|
||||
std::vector<GeometryInstance> geometryInstances;
|
||||
TDF_Label targetIndex;
|
||||
gp_Trsf transformation;
|
||||
ProcessResult processResult = ProcessResult(false, "");
|
||||
|
||||
// Constructor to initialize and assign unique instanceIndex
|
||||
ProductNode() : instanceIndex(instanceCounter++) {}
|
||||
|
||||
void collectNodesWithGeometry(std::vector<const ProductNode *> &result) const {
|
||||
if (!geometryInstances.empty()) {
|
||||
result.push_back(this);
|
||||
}
|
||||
for (const auto &child: children) {
|
||||
child->collectNodesWithGeometry(result);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Define a struct to hold the parent-child relationship and transformation
|
||||
struct ParentChildRelationship {
|
||||
int parentIndex;
|
||||
int childIndex;
|
||||
gp_Trsf transformation; // Transformation between parent and child
|
||||
int nauoIndex;
|
||||
|
||||
ParentChildRelationship(int pIdx, int cIdx, const gp_Trsf& transform, int nauoIdx)
|
||||
: parentIndex(pIdx), childIndex(cIdx), transformation(transform), nauoIndex(nauoIdx) {
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<std::unique_ptr<ProductNode> > ExtractProductHierarchy(const Handle(Interface_InterfaceModel) &model,
|
||||
const Interface_Graph &theGraph);
|
||||
|
||||
std::string ExportHierarchyToJson(const std::vector<std::unique_ptr<ProductNode> > &roots);
|
||||
|
||||
void add_geometries_to_nodes(const std::vector<std::unique_ptr<ProductNode> > &nodes, const Interface_Graph &theGraph);
|
||||
|
||||
gp_Trsf GetTransformationMatrix(
|
||||
const Handle(StepRepr_NextAssemblyUsageOccurrence) &nauo,
|
||||
const Interface_Graph &theGraph);
|
||||
|
||||
static std::unique_ptr<ProductNode> BuildProductNodeWithTransform(
|
||||
int productIndex,
|
||||
const std::unordered_map<int, std::vector<ParentChildRelationship>>& parentToChildrenWTransforms,
|
||||
const Handle(Interface_InterfaceModel)& model,
|
||||
const Interface_Graph& theGraph,
|
||||
const gp_Trsf& parentTransform = gp_Trsf(),
|
||||
ProductNode* parent = nullptr);
|
||||
|
||||
static std::unique_ptr<ProductNode> BuildProductNodeWithTransformIterative(
|
||||
int rootIndex,
|
||||
const std::unordered_map<int, std::vector<int> > &parentToChildren,
|
||||
const Handle(Interface_InterfaceModel) &model,
|
||||
const Interface_Graph &theGraph,
|
||||
const gp_Trsf &rootTransform = gp_Trsf());
|
||||
|
||||
#endif //STEP_TREE_H
|
||||
165
src/cadit/occt/step_writer.cpp
Normal file
165
src/cadit/occt/step_writer.cpp
Normal file
@ -0,0 +1,165 @@
|
||||
#include "step_writer.h"
|
||||
|
||||
#include <BRepBuilderAPI_Transform.hxx>
|
||||
#include <iostream>
|
||||
#include <filesystem>
|
||||
|
||||
#include <BRep_Builder.hxx>
|
||||
#include <Interface_Static.hxx>
|
||||
#include <Quantity_Color.hxx>
|
||||
#include <Quantity_TypeOfColor.hxx>
|
||||
#include <RWGltf_CafWriter.hxx>
|
||||
#include <STEPCAFControl_Writer.hxx>
|
||||
#include <TCollection_ExtendedString.hxx>
|
||||
#include <TDataStd_Name.hxx>
|
||||
#include <TDocStd_Application.hxx>
|
||||
#include <TDocStd_Document.hxx>
|
||||
#include <TopoDS_Compound.hxx>
|
||||
#include <XCAFDoc_DocumentTool.hxx>
|
||||
#include <XSControl_WorkSession.hxx>
|
||||
#include <TDF_Label.hxx>
|
||||
#include <XCAFDoc_ColorTool.hxx>
|
||||
#include <XCAFDoc_ShapeTool.hxx>
|
||||
#include "helpers.h"
|
||||
#include "step_tree.h"
|
||||
#include "../../geom/Color.h"
|
||||
#include <Standard_Handle.hxx> // For Handle
|
||||
|
||||
|
||||
// Constructor with only top-level name
|
||||
StepStore::StepStore(const std::string &top_level_name) {
|
||||
initialize();
|
||||
tll_ = shape_tool_->AddShape(comp_, Standard_True);
|
||||
set_name(tll_, top_level_name);
|
||||
}
|
||||
|
||||
// Constructor with product hierarchy
|
||||
StepStore::StepStore(const std::vector<std::unique_ptr<ProductNode> > &product_hierarchy) {
|
||||
initialize();
|
||||
create_hierarchy(product_hierarchy, tll_);
|
||||
}
|
||||
|
||||
|
||||
// Private initialization function
|
||||
void StepStore::initialize() {
|
||||
app_ = new TDocStd_Application();
|
||||
doc_ = new TDocStd_Document(TCollection_ExtendedString("XmlOcaf"));
|
||||
app_->InitDocument(doc_);
|
||||
|
||||
shape_tool_ = XCAFDoc_DocumentTool::ShapeTool(doc_->Main());
|
||||
XCAFDoc_ShapeTool::SetAutoNaming(false);
|
||||
color_tool_ = XCAFDoc_DocumentTool::ColorTool(doc_->Main());
|
||||
comp_builder_.MakeCompound(comp_);
|
||||
}
|
||||
|
||||
// Create a hierarchy of products
|
||||
void StepStore::create_hierarchy(const std::vector<std::unique_ptr<ProductNode> > &nodes,
|
||||
const TDF_Label &parent_label) {
|
||||
for (auto &node: nodes) {
|
||||
TDF_Label child_label = shape_tool_->NewShape();
|
||||
TDF_Label target_label;
|
||||
if (parent_label.IsNull()) {
|
||||
tll_ = shape_tool_->AddShape(comp_, Standard_True);
|
||||
target_label = tll_;
|
||||
} else {
|
||||
target_label = shape_tool_->AddComponent(parent_label, child_label, TopLoc_Location());
|
||||
}
|
||||
|
||||
set_name(child_label, node->name);
|
||||
product_labels_[node->name] = child_label;
|
||||
entity_labels_[node->instanceIndex] = child_label;
|
||||
node->targetIndex = target_label;
|
||||
|
||||
if (!node->children.empty()) {
|
||||
create_hierarchy(node->children, child_label);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set name for a label
|
||||
void StepStore::set_name(const TDF_Label &label, const std::string &name) {
|
||||
TDataStd_Name::Set(label, TCollection_ExtendedString(name.c_str()));
|
||||
}
|
||||
|
||||
// Set color for a label
|
||||
void StepStore::set_color(const TDF_Label &label, const Color &rgb_color,
|
||||
const Handle(XCAFDoc_ColorTool) &color_tool) {
|
||||
Quantity_Color color(rgb_color.r, rgb_color.g, rgb_color.b, Quantity_TOC_RGB);
|
||||
color_tool->SetColor(label, color, XCAFDoc_ColorGen);
|
||||
}
|
||||
|
||||
// Add a shape
|
||||
void StepStore::add_shape(const TopoDS_Shape &shape, const std::string &name,
|
||||
const Color &rgb_color, const ProductNode &dummy_product) {
|
||||
const TDF_Label dummy_label = entity_labels_[dummy_product.instanceIndex];
|
||||
const TDF_Label parent_label = entity_labels_[dummy_product.parent->instanceIndex];
|
||||
|
||||
if (dummy_label.IsNull()) {
|
||||
throw std::runtime_error("Parent product not found: " + dummy_product.name);
|
||||
}
|
||||
const auto location = TopLoc_Location(dummy_product.transformation);
|
||||
// replace the dummy label with the actual shape
|
||||
|
||||
// get parent of dummy label
|
||||
shape_tool_->RemoveComponent(dummy_label);
|
||||
const TDF_Label shape_label = shape_tool_->AddShape(shape, Standard_False, Standard_False);
|
||||
shape_tool_->AddComponent(parent_label, shape_label, location);
|
||||
|
||||
set_color(shape_label, rgb_color, color_tool_);
|
||||
set_name(shape_label, name);
|
||||
|
||||
const auto new_shape = XCAFDoc_ShapeTool::GetShape(shape_label);
|
||||
|
||||
// Add any additional location transformations from product to shape
|
||||
BRepBuilderAPI_Transform shapeTransform(dummy_product.transformation);
|
||||
shapeTransform.Perform(new_shape, Standard_False);
|
||||
}
|
||||
|
||||
// Export the STEP file
|
||||
void StepStore::to_step(const std::filesystem::path &step_file) const {
|
||||
shape_tool_->UpdateAssemblies();
|
||||
|
||||
if (!step_file.parent_path().empty() && step_file.parent_path() != "") {
|
||||
create_directories(step_file.parent_path());
|
||||
}
|
||||
|
||||
const Handle(XSControl_WorkSession) session = new XSControl_WorkSession();
|
||||
|
||||
STEPCAFControl_Writer writer(session, Standard_False);
|
||||
writer.SetColorMode(Standard_True);
|
||||
writer.SetNameMode(Standard_True);
|
||||
|
||||
Interface_Static::SetCVal("write.step.assembly", "ON");
|
||||
Interface_Static::SetCVal("write.step.product.context", "PRODUCT");
|
||||
Interface_Static::SetCVal("write.step.unit", "m");
|
||||
Interface_Static::SetCVal("write.step.schema", "AP242");
|
||||
|
||||
writer.Transfer(doc_, STEPControl_AsIs);
|
||||
const IFSelect_ReturnStatus status = writer.Write(step_file.string().c_str());
|
||||
|
||||
if (status != IFSelect_RetDone) {
|
||||
throw std::runtime_error("STEP export failed");
|
||||
}
|
||||
|
||||
std::cout << "STEP export status: " << status << "\n";
|
||||
}
|
||||
|
||||
void StepStore::to_glb(const std::filesystem::path &glb_file) const {
|
||||
shape_tool_->UpdateAssemblies();
|
||||
|
||||
RWGltf_CafWriter writer(glb_file.c_str(), true); // true for binary format
|
||||
|
||||
// Additional file information (can be empty if not needed)
|
||||
const TColStd_IndexedDataMapOfStringString file_info;
|
||||
|
||||
// Progress indicator (can be null if progress tracking is not needed)
|
||||
const Message_ProgressRange progress;
|
||||
|
||||
// if output parent directory is != "" and does not exist, create it
|
||||
if (const std::filesystem::path glb_dir = glb_file.parent_path(); !glb_dir.empty() && !exists(glb_dir)) {
|
||||
create_directories(glb_dir);
|
||||
}
|
||||
if (!writer.Perform(doc_, file_info, progress)) {
|
||||
throw std::runtime_error("Error writing GLB file");
|
||||
}
|
||||
}
|
||||
53
src/cadit/occt/step_writer.h
Normal file
53
src/cadit/occt/step_writer.h
Normal file
@ -0,0 +1,53 @@
|
||||
// AdaCPPStepWriter.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <BRep_Builder.hxx>
|
||||
#include <filesystem>
|
||||
#include <TDocStd_Application.hxx>
|
||||
#include <TDocStd_Document.hxx>
|
||||
#include <TopoDS_Compound.hxx>
|
||||
#include <TDF_Label.hxx>
|
||||
#include <XCAFDoc_ColorTool.hxx>
|
||||
#include <XCAFDoc_ShapeTool.hxx>
|
||||
#include "step_tree.h"
|
||||
#include "../../geom/Color.h"
|
||||
|
||||
|
||||
|
||||
class StepStore {
|
||||
public:
|
||||
Handle(TDocStd_Document) doc_;
|
||||
|
||||
explicit StepStore(const std::string& top_level_name = "Assembly");
|
||||
|
||||
explicit StepStore(const std::vector<std::unique_ptr<ProductNode>>& product_hierarchy);
|
||||
|
||||
void add_shape(const TopoDS_Shape& shape, const std::string& name, const Color& rgb_color,
|
||||
const ProductNode& parent_node);
|
||||
|
||||
void to_step(const std::filesystem::path& step_file) const;
|
||||
|
||||
void to_glb(const std::filesystem::path& glb_file) const;
|
||||
|
||||
private:
|
||||
// Handles (smart pointers) to OCC classes
|
||||
Handle(TDocStd_Application) app_;
|
||||
Handle(XCAFDoc_ShapeTool) shape_tool_;
|
||||
Handle(XCAFDoc_ColorTool) color_tool_;
|
||||
|
||||
TopoDS_Compound comp_;
|
||||
BRep_Builder comp_builder_;
|
||||
TDF_Label tll_;
|
||||
|
||||
// Map to store product name to TDF_Label mapping for hierarchy
|
||||
std::unordered_map<std::string, TDF_Label> product_labels_;
|
||||
|
||||
// Map to store source entity index to TDF_Label mapping for shapes
|
||||
std::unordered_map<int, TDF_Label> entity_labels_;
|
||||
|
||||
void initialize();
|
||||
void create_hierarchy(const std::vector<std::unique_ptr<ProductNode>> &nodes, const TDF_Label &parent_label);
|
||||
static void set_name(const TDF_Label& label, const std::string& name);
|
||||
static void set_color(const TDF_Label& label, const Color& rgb_color, const Handle(XCAFDoc_ColorTool)& color_tool);
|
||||
};
|
||||
39
src/cadit/stepcode/sc_parser.cpp
Normal file
39
src/cadit/stepcode/sc_parser.cpp
Normal file
@ -0,0 +1,39 @@
|
||||
//
|
||||
// Created by ofskrand on 12.09.2024.
|
||||
//
|
||||
#include <windows.h>
|
||||
#include "sc_parser.h"
|
||||
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include "cllazyfile/lazyInstMgr.h"
|
||||
#include <psapi.h>
|
||||
|
||||
#include "../../config_structs.h"
|
||||
|
||||
// Function to print memory usage
|
||||
void printMemoryUsage()
|
||||
{
|
||||
PROCESS_MEMORY_COUNTERS_EX pmc;
|
||||
if (GetProcessMemoryInfo(GetCurrentProcess(), (PROCESS_MEMORY_COUNTERS*)&pmc, sizeof(pmc)))
|
||||
{
|
||||
std::cout << "Memory usage: " << pmc.WorkingSetSize / 1024 << " KB\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Function to parse a STEP file using lazy loading
|
||||
void lazy_step_parser(const GlobalConfig& config)
|
||||
{
|
||||
auto mgr = std::make_unique<lazyInstMgr>();
|
||||
|
||||
printMemoryUsage(); // Memory before opening the file
|
||||
|
||||
mgr->openFile(config.stpFile.string());
|
||||
|
||||
printMemoryUsage(); // Memory after opening the file
|
||||
|
||||
int instances = mgr->totalInstanceCount();
|
||||
std::cout << "Total instances: " << instances << "\n";
|
||||
|
||||
printMemoryUsage(); // Memory after counting instances
|
||||
}
|
||||
11
src/cadit/stepcode/sc_parser.h
Normal file
11
src/cadit/stepcode/sc_parser.h
Normal file
@ -0,0 +1,11 @@
|
||||
//
|
||||
// Created by ofskrand on 12.09.2024.
|
||||
//
|
||||
|
||||
#ifndef SC_PARSER_H
|
||||
#define SC_PARSER_H
|
||||
|
||||
#include "../../config_structs.h"
|
||||
|
||||
void lazy_step_parser(const GlobalConfig& config);
|
||||
#endif //SC_PARSER_H
|
||||
175
src/cadit/tinygltf/tiny.cpp
Normal file
175
src/cadit/tinygltf/tiny.cpp
Normal file
@ -0,0 +1,175 @@
|
||||
//
|
||||
// Created by Kristoffer on 26/07/2023.
|
||||
//
|
||||
#include <iostream>
|
||||
#include "tinyload.h"
|
||||
#include "../../geom/Mesh.h"
|
||||
#include "../../binding_core.h"
|
||||
#include "../../visit/tess_helpers.h"
|
||||
#include "../../helpers/helpers.h"
|
||||
|
||||
std::pair<std::vector<double>, std::vector<double>> calculateBounds(const std::vector<float>& positions) {
|
||||
assert(positions.size() % 3 == 0); // Ensure there is 3D data
|
||||
|
||||
std::vector<double> minValues = { std::numeric_limits<double>::max(),
|
||||
std::numeric_limits<double>::max(),
|
||||
std::numeric_limits<double>::max() };
|
||||
|
||||
std::vector<double> maxValues = { std::numeric_limits<double>::lowest(),
|
||||
std::numeric_limits<double>::lowest(),
|
||||
std::numeric_limits<double>::lowest() };
|
||||
|
||||
for (size_t i = 0; i < positions.size(); i += 3) {
|
||||
minValues[0] = std::min(static_cast<double>(minValues[0]), static_cast<double>(positions[i]));
|
||||
minValues[1] = std::min(static_cast<double>(minValues[1]), static_cast<double>(positions[i + 1]));
|
||||
minValues[2] = std::min(static_cast<double>(minValues[2]), static_cast<double>(positions[i + 2]));
|
||||
|
||||
maxValues[0] = std::max(static_cast<double>(maxValues[0]), static_cast<double>(positions[i]));
|
||||
maxValues[1] = std::max(static_cast<double>(maxValues[1]), static_cast<double>(positions[i + 1]));
|
||||
maxValues[2] = std::max(static_cast<double>(maxValues[2]), static_cast<double>(positions[i + 2]));
|
||||
}
|
||||
|
||||
return {minValues, maxValues};
|
||||
}
|
||||
|
||||
|
||||
void AddMesh(tinygltf::Model &model, const std::string &name, Mesh my_mesh) {
|
||||
std::vector<float> positions = my_mesh.positions;
|
||||
std::vector<uint32_t> indices = my_mesh.indices;
|
||||
|
||||
// Append buffer for position data
|
||||
tinygltf::Buffer posBuffer;
|
||||
posBuffer.data = std::vector<unsigned char>(reinterpret_cast<const unsigned char*>(positions.data()),
|
||||
reinterpret_cast<const unsigned char*>(positions.data() + positions.size()));
|
||||
model.buffers.push_back(posBuffer);
|
||||
|
||||
// Buffer for indices
|
||||
tinygltf::Buffer indexBuffer;
|
||||
indexBuffer.data = std::vector<unsigned char>(reinterpret_cast<const unsigned char*>(indices.data()),
|
||||
reinterpret_cast<const unsigned char*>(indices.data() + indices.size()));
|
||||
model.buffers.push_back(indexBuffer);
|
||||
|
||||
// BufferView for positions
|
||||
tinygltf::BufferView posBufferView;
|
||||
posBufferView.buffer = model.buffers.size() - 2;
|
||||
posBufferView.byteLength = posBuffer.data.size();
|
||||
posBufferView.target = TINYGLTF_TARGET_ARRAY_BUFFER; // Add this line
|
||||
model.bufferViews.push_back(posBufferView);
|
||||
|
||||
// BufferView for indices
|
||||
tinygltf::BufferView indexBufferView;
|
||||
indexBufferView.buffer = model.buffers.size() - 1;
|
||||
indexBufferView.byteLength = indexBuffer.data.size();
|
||||
indexBufferView.target = TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER; // Add this line
|
||||
model.bufferViews.push_back(indexBufferView);
|
||||
|
||||
// Accessor for positions
|
||||
tinygltf::Accessor posAccessor;
|
||||
posAccessor.bufferView = model.bufferViews.size() - 2;
|
||||
posAccessor.byteOffset = 0;
|
||||
posAccessor.componentType = TINYGLTF_COMPONENT_TYPE_FLOAT;
|
||||
posAccessor.count = positions.size() / 3;
|
||||
posAccessor.type = TINYGLTF_TYPE_VEC3;
|
||||
|
||||
// Calculate and add min and max here
|
||||
auto [minValues, maxValues] = calculateBounds(positions);
|
||||
posAccessor.minValues = minValues;
|
||||
posAccessor.maxValues = maxValues;
|
||||
|
||||
model.accessors.push_back(posAccessor);
|
||||
|
||||
// Accessor for indices
|
||||
tinygltf::Accessor indexAccessor;
|
||||
indexAccessor.bufferView = model.bufferViews.size() - 1;
|
||||
indexAccessor.byteOffset = 0;
|
||||
indexAccessor.componentType = TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT; // uint32_t indices
|
||||
indexAccessor.count = indices.size();
|
||||
indexAccessor.type = TINYGLTF_TYPE_SCALAR;
|
||||
model.accessors.push_back(indexAccessor);
|
||||
|
||||
// Create a mesh that references the accessors
|
||||
tinygltf::Mesh mesh;
|
||||
tinygltf::Primitive primitive;
|
||||
primitive.attributes["POSITION"] = model.accessors.size() - 2;
|
||||
primitive.indices = model.accessors.size() - 1;
|
||||
primitive.mode = TINYGLTF_MODE_TRIANGLES;
|
||||
mesh.primitives.push_back(primitive);
|
||||
|
||||
// Create a material with PBR properties
|
||||
tinygltf::Material material;
|
||||
material.pbrMetallicRoughness.baseColorFactor = {my_mesh.color.r, my_mesh.color.g, my_mesh.color.b, my_mesh.color.a};
|
||||
material.pbrMetallicRoughness.metallicFactor = 1.0;
|
||||
material.pbrMetallicRoughness.roughnessFactor = 0.5;
|
||||
material.alphaMode = "OPAQUE";
|
||||
|
||||
// Add the material to the model
|
||||
int materialIndex = model.materials.size();
|
||||
model.materials.push_back(material);
|
||||
|
||||
// Assign the material to the primitive
|
||||
mesh.primitives[0].material = materialIndex; // Modify this line
|
||||
|
||||
model.meshes.push_back(mesh);
|
||||
|
||||
// Create a node that references the mesh
|
||||
tinygltf::Node node;
|
||||
node.mesh = model.meshes.size() - 1;
|
||||
model.nodes.push_back(node);
|
||||
|
||||
// Add the node to the scene
|
||||
model.scenes[0].nodes.push_back(model.nodes.size() - 1);
|
||||
}
|
||||
|
||||
int write_to_gltf(const std::string& filename, Mesh mesh) {
|
||||
tinygltf::Model model;
|
||||
|
||||
// Create a scene
|
||||
tinygltf::Scene scene;
|
||||
model.scenes.push_back(scene);
|
||||
model.defaultScene = 0;
|
||||
AddMesh(model, "mesh", mesh);
|
||||
|
||||
// Save to file
|
||||
tinygltf::TinyGLTF gltf;
|
||||
if (!gltf.WriteGltfSceneToFile(&model, filename, true, true, true, false)) {
|
||||
std::cerr << "Failed to write glTF file" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// take in a list of box dimensions and origins and write to step file using the AdaCPPStepWriter class
|
||||
int write_boxes_to_gltf(const std::string &filename, const std::vector<std::vector<float>> &box_origins,
|
||||
const std::vector<std::vector<float>> &box_dims) {
|
||||
tinygltf::Model model;
|
||||
|
||||
// Create a scene
|
||||
tinygltf::Scene scene;
|
||||
model.scenes.push_back(scene);
|
||||
model.defaultScene = 0;
|
||||
|
||||
for (int i = 0; i < box_origins.size(); i++) {
|
||||
TopoDS_Solid box = create_box(box_origins[i], box_dims[i]);
|
||||
Mesh mesh = tessellate_shape(0, box, true, 1.0, false);
|
||||
mesh.color = random_color();
|
||||
AddMesh(model, "mesh", mesh);
|
||||
}
|
||||
// If filename contains .glb set variable "glb" to true
|
||||
bool glb = filename.find(".glb") != std::string::npos;
|
||||
|
||||
// Save to file
|
||||
tinygltf::TinyGLTF gltf;
|
||||
if (!gltf.WriteGltfSceneToFile(&model, filename, true, true, true, glb)) {
|
||||
std::cerr << "Failed to write glTF file" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void tiny_gltf_module(nb::module_ &m) {
|
||||
m.def("write_mesh_to_gltf", &write_to_gltf, "filename"_a, "mesh"_a, "Write a Mesh to GLTF");
|
||||
m.def("write_boxes_to_gltf", &write_boxes_to_gltf, "filename"_a, "box_origins"_a, "box_dims"_a, "Write a list of boxes to GLTF");
|
||||
}
|
||||
12
src/cadit/tinygltf/tiny.h
Normal file
12
src/cadit/tinygltf/tiny.h
Normal file
@ -0,0 +1,12 @@
|
||||
//
|
||||
// Created by Kristoffer on 26/07/2023.
|
||||
//
|
||||
|
||||
|
||||
#ifndef ADA_CPP_TINY_H
|
||||
#define ADA_CPP_TINY_H
|
||||
#include "../../binding_core.h"
|
||||
|
||||
void tiny_gltf_module(nb::module_&m);
|
||||
|
||||
#endif //ADA_CPP_TINY_H
|
||||
10
src/cadit/tinygltf/tinyload.h
Normal file
10
src/cadit/tinygltf/tinyload.h
Normal file
@ -0,0 +1,10 @@
|
||||
//
|
||||
// Created by Kristoffer on 23/01/2024.
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#define TINYGLTF_IMPLEMENTATION
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
|
||||
#include "tiny_gltf.h"
|
||||
5
src/geom/Color.cpp
Normal file
5
src/geom/Color.cpp
Normal file
@ -0,0 +1,5 @@
|
||||
// Color.cpp
|
||||
#include "Color.h"
|
||||
|
||||
Color::Color(float r, float g, float b, float a)
|
||||
: r(r), g(g), b(b), a(a) {}
|
||||
18
src/geom/Color.h
Normal file
18
src/geom/Color.h
Normal file
@ -0,0 +1,18 @@
|
||||
//
|
||||
// Created by Kristoffer on 07/05/2023.
|
||||
//
|
||||
|
||||
#ifndef NANO_OCCT_COLOR_H
|
||||
#define NANO_OCCT_COLOR_H
|
||||
|
||||
class Color {
|
||||
public:
|
||||
float r;
|
||||
float g;
|
||||
float b;
|
||||
float a;
|
||||
|
||||
explicit Color(float r=0.5f, float g=0.5f, float b=0.5f, float a = 1.0f);
|
||||
};
|
||||
|
||||
#endif //NANO_OCCT_COLOR_H
|
||||
17
src/geom/GroupReference.h
Normal file
17
src/geom/GroupReference.h
Normal file
@ -0,0 +1,17 @@
|
||||
//
|
||||
// Created by Kristoffer on 07/05/2023.
|
||||
//
|
||||
|
||||
#ifndef NANO_OCCT_GROUPREFERENCE_H
|
||||
#define NANO_OCCT_GROUPREFERENCE_H
|
||||
|
||||
class GroupReference {
|
||||
public:
|
||||
int node_id;
|
||||
int start;
|
||||
int length;
|
||||
|
||||
GroupReference(int node_id, int start, int length);
|
||||
};
|
||||
|
||||
#endif //NANO_OCCT_GROUPREFERENCE_H
|
||||
32
src/geom/Mesh.h
Normal file
32
src/geom/Mesh.h
Normal file
@ -0,0 +1,32 @@
|
||||
#ifndef NANO_OCCT_MESH_H
|
||||
#define NANO_OCCT_MESH_H
|
||||
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include "MeshType.h"
|
||||
#include "GroupReference.h"
|
||||
#include "Color.h"
|
||||
|
||||
class Mesh {
|
||||
public:
|
||||
Mesh(int id,
|
||||
std::vector<float> positions,
|
||||
std::vector<uint32_t> faces,
|
||||
std::vector<uint32_t> edges = {},
|
||||
std::vector<float> normals = {},
|
||||
MeshType mesh_type = MeshType::TRIANGLES,
|
||||
Color color = Color(),
|
||||
std::vector<GroupReference> group_reference = {});
|
||||
|
||||
int id;
|
||||
std::vector<float> positions;
|
||||
std::vector<uint32_t> indices;
|
||||
std::vector<uint32_t> edges;
|
||||
std::vector<float> normals;
|
||||
MeshType mesh_type;
|
||||
Color color;
|
||||
std::vector<GroupReference> group_reference;
|
||||
};
|
||||
|
||||
#endif //NANO_OCCT_MESH_H
|
||||
20
src/geom/MeshType.h
Normal file
20
src/geom/MeshType.h
Normal file
@ -0,0 +1,20 @@
|
||||
//
|
||||
// Created by Kristoffer on 07/05/2023.
|
||||
//
|
||||
|
||||
#ifndef NANO_OCCT_MESHTYPE_H
|
||||
#define NANO_OCCT_MESHTYPE_H
|
||||
|
||||
enum class MeshType {
|
||||
POINTS = 0,
|
||||
LINES = 1,
|
||||
LINE_LOOP = 2,
|
||||
LINE_STRIP = 3,
|
||||
TRIANGLES = 4,
|
||||
TRIANGLE_STRIP = 5,
|
||||
TRIANGLE_FAN = 6
|
||||
};
|
||||
|
||||
MeshType from_int(int value);
|
||||
|
||||
#endif //NANO_OCCT_MESHTYPE_H
|
||||
43
src/geom/Models.cpp
Normal file
43
src/geom/Models.cpp
Normal file
@ -0,0 +1,43 @@
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <TopoDS_Shape.hxx>
|
||||
#include <stdexcept>
|
||||
#include "OccShape.h"
|
||||
#include "MeshType.h"
|
||||
#include "Mesh.h"
|
||||
#include "GroupReference.h"
|
||||
#include "Color.h"
|
||||
|
||||
Color::Color(float r, float g, float b, float a) : r(r), g(g), b(b), a(a) {}
|
||||
|
||||
MeshType from_int(int value) {
|
||||
if (value < 0 || value > 6) {
|
||||
throw std::out_of_range("Invalid MeshType value");
|
||||
}
|
||||
return static_cast<MeshType>(value);
|
||||
}
|
||||
|
||||
Mesh::Mesh(int id, std::vector<float> positions, std::vector<uint32_t> faces,
|
||||
std::vector<uint32_t> edges, std::vector<float> normals,
|
||||
MeshType mesh_type, Color color, std::vector<GroupReference> group_reference)
|
||||
: id(id),
|
||||
positions(std::move(positions)),
|
||||
indices(std::move(faces)),
|
||||
edges(std::move(edges)),
|
||||
normals(std::move(normals)),
|
||||
mesh_type(mesh_type),
|
||||
color(std::move(color)),
|
||||
group_reference(std::move(group_reference)) {}
|
||||
|
||||
OccShape::OccShape(TopoDS_Shape shape,
|
||||
Color color,
|
||||
int num_tot_entities,
|
||||
std::optional<std::string> name)
|
||||
: shape(std::move(shape)),
|
||||
color(color),
|
||||
num_tot_entities(num_tot_entities),
|
||||
name(std::move(name)) {}
|
||||
|
||||
GroupReference::GroupReference(int node_id, int start, int length)
|
||||
: node_id(node_id), start(start), length(length) {}
|
||||
27
src/geom/OccShape.h
Normal file
27
src/geom/OccShape.h
Normal file
@ -0,0 +1,27 @@
|
||||
//
|
||||
// Created by Kristoffer on 07/05/2023.
|
||||
//
|
||||
|
||||
#ifndef NANO_OCCT_OCCSHAPE_H
|
||||
#define NANO_OCCT_OCCSHAPE_H
|
||||
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
#include <TopoDS_Shape.hxx>
|
||||
#include "Color.h"
|
||||
|
||||
|
||||
class OccShape {
|
||||
public:
|
||||
explicit OccShape(TopoDS_Shape shape,
|
||||
Color color = Color(),
|
||||
int num_tot_entities = 0,
|
||||
std::optional<std::string> name = std::nullopt);
|
||||
|
||||
TopoDS_Shape shape;
|
||||
Color color;
|
||||
int num_tot_entities;
|
||||
std::optional<std::string> name;
|
||||
};
|
||||
|
||||
#endif //NANO_OCCT_OCCSHAPE_H
|
||||
18
src/geom/geometries.cpp
Normal file
18
src/geom/geometries.cpp
Normal file
@ -0,0 +1,18 @@
|
||||
#include "geometries.h"
|
||||
|
||||
//
|
||||
// Created by Kristoffer on 30/07/2023.
|
||||
//
|
||||
int Shape::next_id = 0;
|
||||
|
||||
void shape_module(nb::module_ &m) {
|
||||
nb::class_<Shape>(m, "Shape")
|
||||
.def_ro("id", &Shape::id);
|
||||
|
||||
nb::class_<Box, Shape>(m, "Box")
|
||||
.def(nb::init<std::vector<double>, double, double, double>())
|
||||
.def_rw("origin", &Box::origin)
|
||||
.def_rw("width", &Box::width)
|
||||
.def_rw("length", &Box::length)
|
||||
.def_rw("height", &Box::height);
|
||||
};
|
||||
34
src/geom/geometries.h
Normal file
34
src/geom/geometries.h
Normal file
@ -0,0 +1,34 @@
|
||||
#ifndef ADA_CPP_GEOMETRIES_H
|
||||
#define ADA_CPP_GEOMETRIES_H
|
||||
|
||||
#include <CGAL/Simple_cartesian.h>
|
||||
#include "../binding_core.h"
|
||||
|
||||
typedef CGAL::Simple_cartesian<double> Kernel;
|
||||
typedef Kernel::Point_3 Point_3;
|
||||
typedef Kernel::Vector_3 Vector_3;
|
||||
|
||||
struct Shape {
|
||||
// create a global unique int id for each shape
|
||||
int id;
|
||||
// upon instantiation of a shape, increment the global id
|
||||
static int next_id;
|
||||
|
||||
Shape() : id(next_id++) {}
|
||||
};
|
||||
|
||||
struct Box : Shape {
|
||||
Point_3 origin;
|
||||
double width;
|
||||
double length;
|
||||
double height;
|
||||
public:
|
||||
Box(const std::vector<double> &origin, double width, double length, double height) : origin(origin[0], origin[1],
|
||||
origin[2]),
|
||||
width(width), length(length),
|
||||
height(height) {}
|
||||
};
|
||||
|
||||
void shape_module(nb::module_ &m);
|
||||
|
||||
#endif //ADA_CPP_GEOMETRIES_H
|
||||
2
test-log.json
Normal file
2
test-log.json
Normal file
@ -0,0 +1,2 @@
|
||||
[
|
||||
]
|
||||
209
test_wrapper.py
Normal file
209
test_wrapper.py
Normal file
@ -0,0 +1,209 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
测试Python包装器功能
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
import tempfile
|
||||
import shutil
|
||||
|
||||
|
||||
def test_help():
|
||||
"""测试帮助信息"""
|
||||
print("测试 --help 参数...")
|
||||
result = subprocess.run([sys.executable, 'main.py', '--help'],
|
||||
capture_output=True, text=True)
|
||||
|
||||
if result.returncode == 0:
|
||||
print("✓ 帮助信息显示正常")
|
||||
return True
|
||||
else:
|
||||
print("✗ 帮助信息显示失败")
|
||||
print(result.stderr)
|
||||
return False
|
||||
|
||||
|
||||
def test_missing_args():
|
||||
"""测试缺少参数的情况"""
|
||||
print("测试缺少参数...")
|
||||
result = subprocess.run([sys.executable, 'main.py'],
|
||||
capture_output=True, text=True)
|
||||
|
||||
if result.returncode != 0:
|
||||
print("✓ 正确处理缺少参数的情况")
|
||||
return True
|
||||
else:
|
||||
print("✗ 未能正确处理缺少参数")
|
||||
return False
|
||||
|
||||
|
||||
def test_invalid_input():
|
||||
"""测试无效输入文件"""
|
||||
print("测试无效输入文件...")
|
||||
result = subprocess.run([sys.executable, 'main.py', 'nonexistent.stp', 'output.glb'],
|
||||
capture_output=True, text=True)
|
||||
|
||||
if result.returncode != 0 and "输入文件不存在" in result.stdout:
|
||||
print("✓ 正确处理无效输入文件")
|
||||
return True
|
||||
else:
|
||||
print("✗ 未能正确处理无效输入文件")
|
||||
print(result.stdout)
|
||||
return False
|
||||
|
||||
|
||||
def test_invalid_output():
|
||||
"""测试无效输出文件扩展名"""
|
||||
print("测试无效输出文件扩展名...")
|
||||
|
||||
# 创建临时输入文件
|
||||
with tempfile.NamedTemporaryFile(suffix='.stp', delete=False) as tmp_input:
|
||||
tmp_input.write(b"dummy content")
|
||||
tmp_input_path = tmp_input.name
|
||||
|
||||
try:
|
||||
result = subprocess.run([sys.executable, 'main.py', tmp_input_path, 'output.txt'],
|
||||
capture_output=True, text=True)
|
||||
|
||||
if result.returncode != 0 and "输出文件必须是.glb格式" in result.stdout:
|
||||
print("✓ 正确处理无效输出文件扩展名")
|
||||
return True
|
||||
else:
|
||||
print("✗ 未能正确处理无效输出文件扩展名")
|
||||
print(result.stdout)
|
||||
return False
|
||||
finally:
|
||||
os.unlink(tmp_input_path)
|
||||
|
||||
|
||||
def test_quality_presets():
|
||||
"""测试质量预设"""
|
||||
print("测试质量预设...")
|
||||
|
||||
# 创建临时输入文件
|
||||
with tempfile.NamedTemporaryFile(suffix='.stp', delete=False) as tmp_input:
|
||||
tmp_input.write(b"dummy content")
|
||||
tmp_input_path = tmp_input.name
|
||||
|
||||
# 创建临时输出目录
|
||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||
tmp_output_path = os.path.join(tmp_dir, 'output.glb')
|
||||
|
||||
try:
|
||||
# 测试不同质量等级
|
||||
for quality in ['low', 'medium', 'high', 'ultra']:
|
||||
print(f" 测试质量等级: {quality}")
|
||||
result = subprocess.run([
|
||||
sys.executable, 'main.py',
|
||||
tmp_input_path, tmp_output_path,
|
||||
'--quality', quality
|
||||
], capture_output=True, text=True)
|
||||
|
||||
# 应该失败(因为没有找到可执行文件),但参数解析应该成功
|
||||
if "未找到STP2GLB.exe可执行文件" in result.stdout:
|
||||
print(f" ✓ {quality} 质量等级参数解析正常")
|
||||
else:
|
||||
print(f" ✗ {quality} 质量等级参数解析异常")
|
||||
print(result.stdout)
|
||||
return False
|
||||
|
||||
return True
|
||||
finally:
|
||||
os.unlink(tmp_input_path)
|
||||
|
||||
|
||||
def test_custom_quality():
|
||||
"""测试自定义质量参数"""
|
||||
print("测试自定义质量参数...")
|
||||
|
||||
# 创建临时输入文件
|
||||
with tempfile.NamedTemporaryFile(suffix='.stp', delete=False) as tmp_input:
|
||||
tmp_input.write(b"dummy content")
|
||||
tmp_input_path = tmp_input.name
|
||||
|
||||
# 创建临时输出目录
|
||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||
tmp_output_path = os.path.join(tmp_dir, 'output.glb')
|
||||
|
||||
try:
|
||||
# 测试custom质量但没有指定参数
|
||||
result = subprocess.run([
|
||||
sys.executable, 'main.py',
|
||||
tmp_input_path, tmp_output_path,
|
||||
'--quality', 'custom'
|
||||
], capture_output=True, text=True)
|
||||
|
||||
if result.returncode != 0 and "必须指定--linear-deflection或--angular-deflection" in result.stdout:
|
||||
print(" ✓ 正确处理custom质量缺少参数")
|
||||
else:
|
||||
print(" ✗ 未能正确处理custom质量缺少参数")
|
||||
print(result.stdout)
|
||||
return False
|
||||
|
||||
# 测试custom质量并指定参数
|
||||
result = subprocess.run([
|
||||
sys.executable, 'main.py',
|
||||
tmp_input_path, tmp_output_path,
|
||||
'--quality', 'custom',
|
||||
'--linear-deflection', '0.05',
|
||||
'--angular-deflection', '0.2'
|
||||
], capture_output=True, text=True)
|
||||
|
||||
# 应该失败(因为没有找到可执行文件),但参数解析应该成功
|
||||
if "未找到STP2GLB.exe可执行文件" in result.stdout:
|
||||
print(" ✓ custom质量参数解析正常")
|
||||
return True
|
||||
else:
|
||||
print(" ✗ custom质量参数解析异常")
|
||||
print(result.stdout)
|
||||
return False
|
||||
finally:
|
||||
os.unlink(tmp_input_path)
|
||||
|
||||
|
||||
def main():
|
||||
"""主测试函数"""
|
||||
print("Python包装器功能测试")
|
||||
print("=" * 40)
|
||||
|
||||
# 检查main.py是否存在
|
||||
if not os.path.exists('main.py'):
|
||||
print("错误: main.py 文件不存在")
|
||||
return 1
|
||||
|
||||
tests = [
|
||||
test_help,
|
||||
test_missing_args,
|
||||
test_invalid_input,
|
||||
test_invalid_output,
|
||||
test_quality_presets,
|
||||
test_custom_quality,
|
||||
]
|
||||
|
||||
passed = 0
|
||||
total = len(tests)
|
||||
|
||||
for test_func in tests:
|
||||
try:
|
||||
if test_func():
|
||||
passed += 1
|
||||
print()
|
||||
except Exception as e:
|
||||
print(f"✗ 测试异常: {e}")
|
||||
print()
|
||||
|
||||
print("=" * 40)
|
||||
print(f"测试结果: {passed}/{total} 通过")
|
||||
|
||||
if passed == total:
|
||||
print("✓ 所有测试通过!")
|
||||
return 0
|
||||
else:
|
||||
print("✗ 部分测试失败")
|
||||
return 1
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
4
tests/.gitignore
vendored
Normal file
4
tests/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
*.stp
|
||||
*.glb
|
||||
*.gltf
|
||||
*.msh
|
||||
57
tests/tests.cmake
Normal file
57
tests/tests.cmake
Normal file
@ -0,0 +1,57 @@
|
||||
add_test(NAME plate COMMAND STP2GLB
|
||||
--stp ${CMAKE_CURRENT_SOURCE_DIR}/files/flat_plate_abaqus_10x10_m_wColors.stp
|
||||
--glb ${CMAKE_CURRENT_SOURCE_DIR}/temp/flat_plate_abaqus_10x10_m_wColors.glb
|
||||
WORKING_DIRECTORY "${CMAKE_INSTALL_PREFIX}/bin"
|
||||
)
|
||||
|
||||
add_test(NAME debug_plate COMMAND STP2GLB
|
||||
--stp ${CMAKE_CURRENT_SOURCE_DIR}/files/flat_plate_abaqus_10x10_m_wColors.stp
|
||||
--glb ${CMAKE_CURRENT_SOURCE_DIR}/temp/flat_plate_abaqus_10x10_m_wColors.glb
|
||||
--debug
|
||||
WORKING_DIRECTORY "${CMAKE_INSTALL_PREFIX}/bin"
|
||||
)
|
||||
|
||||
add_test(NAME as1 COMMAND STP2GLB
|
||||
--stp "${CMAKE_CURRENT_SOURCE_DIR}/files/as1-oc-214.stp"
|
||||
--glb ${CMAKE_CURRENT_SOURCE_DIR}/temp/as1-oc-214-std.glb
|
||||
WORKING_DIRECTORY "${CMAKE_INSTALL_PREFIX}/bin"
|
||||
)
|
||||
|
||||
add_test(NAME debug_as1 COMMAND STP2GLB
|
||||
--stp "${CMAKE_CURRENT_SOURCE_DIR}/files/as1-oc-214.stp"
|
||||
--glb ${CMAKE_CURRENT_SOURCE_DIR}/temp/as1-oc-214-debug.glb
|
||||
--debug
|
||||
--solid-only
|
||||
--max-geometry-num=0
|
||||
WORKING_DIRECTORY "${CMAKE_INSTALL_PREFIX}/bin"
|
||||
)
|
||||
|
||||
add_test(NAME debug_as1_mini COMMAND STP2GLB
|
||||
--stp "${CMAKE_CURRENT_SOURCE_DIR}/files/as1-oc-214-mini.stp"
|
||||
--glb ${CMAKE_CURRENT_SOURCE_DIR}/temp/as1-oc-214-mini-debug.glb
|
||||
--debug
|
||||
--solid-only
|
||||
--max-geometry-num=0
|
||||
WORKING_DIRECTORY "${CMAKE_INSTALL_PREFIX}/bin"
|
||||
)
|
||||
|
||||
add_test(NAME debug_as1_filter COMMAND STP2GLB
|
||||
--stp "${CMAKE_CURRENT_SOURCE_DIR}/files/as1-oc-214.stp"
|
||||
--glb ${CMAKE_CURRENT_SOURCE_DIR}/temp/as1-oc-214-filtered.glb
|
||||
--debug
|
||||
--solid-only
|
||||
--filter-names-include="l-bracket"
|
||||
WORKING_DIRECTORY "${CMAKE_INSTALL_PREFIX}/bin"
|
||||
)
|
||||
|
||||
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/temp/really_large.stp")
|
||||
add_test(NAME stp_glb_debug_large COMMAND STP2GLB
|
||||
--stp "${CMAKE_CURRENT_SOURCE_DIR}/temp/really_large.stp"
|
||||
--glb ${CMAKE_CURRENT_SOURCE_DIR}/temp/really_large-v2.glb
|
||||
--debug
|
||||
--solid-only
|
||||
--max-geometry-num=0
|
||||
--filter-names-file-exclude=${CMAKE_CURRENT_SOURCE_DIR}/temp/skip-these-nodes.txt
|
||||
WORKING_DIRECTORY "${CMAKE_INSTALL_PREFIX}/bin"
|
||||
)
|
||||
endif ()
|
||||
Loading…
Reference in New Issue
Block a user