Skip to content
Gains Summary
Main Navigation 首页 / Home
C++ 编程 / C++ Programming
系统与高性能 / Systems & Performance
Web 开发 / Web Development
人工智能 / Artificial Intelligence
工业软件 / Industrial Software
其他内容 / Other Topics
C++ 编程 / C++系统与性能 / SystemsWeb 开发 / Web人工智能 / AI工业软件 / Industrial

外观

Sidebar Navigation

← C++ 编程 / C++ Programming

Qt 与可视化 / Qt & Visualization

1. Qt 6 与科学可视化路线 / Qt 6 and Scientific Visualization Roadmap

2. 信号槽机制深度解析 / Qt Signals and Slots in Depth

3. 对象树与内存管理 / QObject Trees and Memory Management

4. CAE开发常用核心API实战 / Essential Qt APIs for CAE Development

5. Model/View架构与CAE数据处理 / Model-View Architecture and CAE Data Processing

6. 多线程与求解器集成 / Multithreading and Solver Integration

7. CAE软件架构实战模式 / Practical Architecture Patterns for CAE Software

8. 3D可视化与交互 / Interactive 3D Visualization

9. 实战案例:从零拆解一个FEA前后处理器 / Building an FEA Pre- and Post-Processor from Scratch

本页目录

实战案例:从零拆解一个FEA前后处理器 / Building an FEA Pre- and Post-Processor from Scratch ​

📅 创建时间:2026-07-13 🏷️ 标签:#Qt #CAE #FEA #案例分析 #前后处理 #综合实战 📚 前置知识:Cae Architecture Patterns、Visualization 3d、Pre Post Processing


📋 本章目标 ​

  • 将前7篇的知识串联为一个完整的FEA前后处理器案例
  • 理解从几何导入→网格划分→加载→求解→后处理的完整数据流
  • 掌握Qt信号在整个系统中的端到端流动路径
  • 理解模块划分与代码组织策略
  • 能够将此案例架构应用到自己的CAE项目中

专题扩展 ​

  • CAE 案例生产化检查清单

第1部分:系统总览 ​

1.1 功能范围 ​

┌─────────────────────────────────────────────────────────────┐
│          FastCAE — 案例FEA前后处理器                          │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  本案例实现的核心功能:                                       │
│                                                             │
│  前处理:                                                    │
│  ┌──────────────────────────────────────────────────┐      │
│  │ 1. 导入几何(Nastran BDF网格文件)                 │      │
│  │ 2. 显示网格(线框/实体/收缩模式)                  │      │
│  │ 3. 鼠标交互(旋转/平移/缩放/拾取节点)             │      │
│  │ 4. 创建载荷集(点力/压力/固定约束)                │      │
│  │ 5. 分配材料属性                                    │      │
│  │ 6. 导出求解器输入文件(CalculiX .inp)              │      │
│  └──────────────────────────────────────────────────┘      │
│                                                             │
│  求解:                                                     │
│  ┌──────────────────────────────────────────────────┐      │
│  │ 7. 后台启动 CalculiX 求解器                        │      │
│  │ 8. 实时显示迭代进度                                │      │
│  │ 9. 支持取消                                        │      │
│  └──────────────────────────────────────────────────┘      │
│                                                             │
│  后处理:                                                   │
│  ┌──────────────────────────────────────────────────┐      │
│  │ 10. 加载结果文件(.frd)                           │      │
│  │ 11. 云图显示(位移/应力)                          │      │
│  │ 12. 变形图(放大位移)                             │      │
│  │ 13. 节点结果表格                                  │      │
│  └──────────────────────────────────────────────────┘      │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

1.2 技术栈 ​

层技术用途
UIQt6 Widgets + OpenGLWidgets主窗口、面板、视口
3DVTK 9.x (QVTKOpenGLNativeWidget)网格渲染、拾取、云图
构建CMake 3.21+AUTOMOC + 模块化
求解器CalculiX (QProcess调用)FEA求解
数据std::vector<Node/Element>核心数据结构
持久化QSettings INI + QTextStream项目文件 + INP导出

1.3 模块总览图 ​

┌─────────────────────────────────────────────────────────────┐
│                   FastCAE 模块总览                            │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│                    ┌──────────────┐                         │
│                    │  MainWindow  │  ← QMainWindow          │
│                    └──────┬───────┘                         │
│           ┌───────────────┼───────────────┐                │
│           ↓               ↓               ↓                │
│  ┌─────────────┐ ┌─────────────┐ ┌─────────────┐          │
│  │ VtkMeshView │ │ PartTreeView│ │NodeTableView│  ← Views  │
│  │ (3D视口)    │ │ (零件树)    │ │ (结果表格)  │          │
│  └──────┬──────┘ └──────┬──────┘ └──────┬──────┘          │
│         └───────────────┼───────────────┘                  │
│                         ↓ 信号槽                            │
│                ┌─────────────────┐                         │
│                │  ModelDocument  │  ← Logic (QObject)      │
│                │  MeshData       │                         │
│                │  LoadCases      │                         │
│                │  SolverWrapper  │                         │
│                └────────┬────────┘                         │
│                         ↓ 直接调用                          │
│                ┌─────────────────┐                         │
│                │  BdfImporter    │  ← Algorithm            │
│                │  InpExporter    │                         │
│                │  FrdReader      │                         │
│                └────────┬────────┘                         │
│                         ↓                                   │
│                ┌─────────────────┐                         │
│                │  QFile / Stream │  ← Data I/O             │
│                └─────────────────┘                         │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

第2部分:核心数据结构 ​

2.1 基础类型——POD结构 ​

cpp
// ===== 纯数据,不继承QObject(性能优先)=====

struct Node {
    int id;
    double x, y, z;
};

struct Element {
    int id;
    int type;               // 4=TET4, 5=PYRA5, 6=PENTA6, 8=HEX8
    std::array<int, 8> nodeIds;  // 最多8节点
    int materialId = -1;
};

struct Material {
    QString name;
    double youngsModulus;    // Pa
    double poissonRatio;
    double density;          // kg/m³
};

struct LoadCase {
    QString name;
    QString type;            // "Static", "Modal", "Thermal"
    QVector<Force> forces;
    QVector<Pressure> pressures;
    QVector<Constraint> constraints;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

2.2 MeshData——聚合数据容器 ​

cpp
class MeshData : public QObject {
    Q_OBJECT
public:
    explicit MeshData(QObject* parent = nullptr) : QObject(parent) {}

    // 数据访问
    const std::vector<Node>& nodes() const { return m_nodes; }
    const std::vector<Element>& elements() const { return m_elements; }

    // 修改数据(通知Model层的View刷新)
    void setNodes(std::vector<Node> nodes) {
        m_nodes = std::move(nodes);
        emit dataChanged();
    }

    void setElements(std::vector<Element> elems) {
        m_elements = std::move(elems);
        emit dataChanged();
    }

    // 几何范围
    void computeBounds() {
        m_boundsMin = QVector3D(1e30, 1e30, 1e30);
        m_boundsMax = QVector3D(-1e30, -1e30, -1e30);
        for (const auto& n : m_nodes) {
            m_boundsMin.setX(qMin(m_boundsMin.x(), n.x));
            m_boundsMin.setY(qMin(m_boundsMin.y(), n.y));
            m_boundsMin.setZ(qMin(m_boundsMin.z(), n.z));
            m_boundsMax.setX(qMax(m_boundsMax.x(), n.x));
            m_boundsMax.setY(qMax(m_boundsMax.y(), n.y));
            m_boundsMax.setZ(qMax(m_boundsMax.z(), n.z));
        }
    }

signals:
    void dataChanged();  // View们,刷新吧

private:
    std::vector<Node> m_nodes;
    std::vector<Element> m_elements;
    QVector3D m_boundsMin, m_boundsMax;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

2.3 ModelDocument——顶层文档对象 ​

cpp
class ModelDocument : public QObject {
    Q_OBJECT
public:
    explicit ModelDocument(QObject* parent = nullptr) : QObject(parent) {
        m_meshData = new MeshData(this);  // MeshData的parent=Document
        m_undoStack = new QUndoStack(this);
    }

    MeshData* meshData() const { return m_meshData; }
    QUndoStack* undoStack() const { return m_undoStack; }

    // 导入
    bool importMesh(const QString& path);
    // 导出
    bool exportSolverInput(const QString& path);
    // 求解
    void runSolver();

    bool isModified() const { return m_modified; }

signals:
    void modifiedChanged(bool modified);
    void solverProgress(int percent, const QString& msg);
    void solverFinished(int exitCode);
    void resultsLoaded();

private:
    MeshData* m_meshData;
    QUndoStack* m_undoStack;
    QVector<LoadCase> m_loadCases;
    QVector<Material> m_materials;
    bool m_modified = false;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

第3部分:模块拆解 ​

3.1 几何导入模块 ​

cpp
class BdfImporter {
public:
    bool import(const QString& path, MeshData& out) {
        QFile file(path);
        if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
            return false;

        QTextStream in(&file);
        std::vector<Node> nodes;
        std::vector<Element> elements;

        while (!in.atEnd()) {
            QString line = in.readLine().trimmed();

            // Nastran BDF 格式: GRID,ID,,X,Y,Z
            if (line.startsWith("GRID")) {
                QStringList f = line.split(',');
                Node n;
                n.id = f[1].trimmed().toInt();
                n.x  = f[3].trimmed().toDouble();
                n.y  = f[4].trimmed().toDouble();
                n.z  = f[5].trimmed().toDouble();
                nodes.push_back(n);
            }
            // CTETRA,EID,PID,N1,N2,N3,N4
            else if (line.startsWith("CTETRA")) {
                QStringList f = line.split(',');
                Element e;
                e.id = f[1].trimmed().toInt();
                e.type = 4; // TET4
                e.nodeIds[0] = f[3].trimmed().toInt() - 1;
                e.nodeIds[1] = f[4].trimmed().toInt() - 1;
                e.nodeIds[2] = f[5].trimmed().toInt() - 1;
                e.nodeIds[3] = f[6].trimmed().toInt() - 1;
                elements.push_back(e);
            }
        }

        out.setNodes(std::move(nodes));
        out.setElements(std::move(elements));
        out.computeBounds();
        return true;
    }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

3.2 视口桥接——QAbstractTableModel ​

cpp
// 让QTableView显示节点坐标
class NodeCoordinateModel : public QAbstractTableModel {
    Q_OBJECT
public:
    void setMeshData(MeshData* data) {
        beginResetModel();
        m_mesh = data;
        endResetModel();
    }

    int rowCount(const QModelIndex&) const override {
        return m_mesh ? m_mesh->nodes().size() : 0;
    }
    int columnCount(const QModelIndex&) const override { return 4; }

    QVariant data(const QModelIndex& idx, int role) const override {
        if (!m_mesh || role != Qt::DisplayRole) return {};
        const auto& n = m_mesh->nodes()[idx.row()];
        switch (idx.column()) {
        case 0: return n.id;
        case 1: return n.x;
        case 2: return n.y;
        case 3: return n.z;
        }
        return {};
    }

private:
    MeshData* m_mesh = nullptr;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

3.3 求解器集成模块 ​

cpp
class SolverWrapper : public QObject {
    Q_OBJECT
public:
    void run(const QString& inpPath) {
        m_thread = new QThread();
        m_worker = new SolverWorker(inpPath);
        m_worker->moveToThread(m_thread);

        connect(m_thread, &QThread::started, m_worker, &SolverWorker::run);
        connect(m_worker, &SolverWorker::progress, this, &SolverWrapper::progress);
        connect(m_worker, &SolverWorker::finished, this, &SolverWrapper::onFinished);
        connect(m_worker, &SolverWorker::finished, m_thread, &QThread::quit);
        connect(m_thread, &QThread::finished, m_worker, &QObject::deleteLater);
        connect(m_thread, &QThread::finished, m_thread, &QObject::deleteLater);

        m_thread->start();
    }

    void cancel() {
        QMetaObject::invokeMethod(m_worker, "cancel", Qt::QueuedConnection);
    }

signals:
    void progress(int percent, const QString& status);
    void finished(int exitCode);

private:
    QThread* m_thread = nullptr;
    SolverWorker* m_worker = nullptr;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

第4部分:信号流动全景 ​

4.1 端到端信号链路 ​

┌─────────────────────────────────────────────────────────────┐
│             FastCAE 端到端信号流动                            │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  1. 用户点击 "File → Import"                                 │
│     ↓                                                       │
│     Action triggered → MainWindow::onImport()               │
│     ↓                                                       │
│     QFileDialog → 用户选择 wing.bdf                          │
│     ↓                                                       │
│     BdfImporter::import("wing.bdf", meshData)               │
│     ↓                                                       │
│     meshData->setNodes/ Elements()                           │
│     ↓                                                       │
│     emit MeshData::dataChanged()                            │
│     ↓                                                       │
│  ┌──────────────────────────────────────────────────────┐  │
│  │                  信号扇出 (Fan-Out)                    │  │
│  │  ├─→ NodeCoordinateModel → QTableView 刷新           │  │
│  │  ├─→ VtkMeshView → 构建VTK Pipeline → 3D渲染         │  │
│  │  ├─→ StatusBar → "Loaded 1234567 nodes"             │  │
│  │  └─→ Document → setModified(true)                   │  │
│  └──────────────────────────────────────────────────────┘  │
│                                                             │
│  2. 用户点击 "Solver → Run"                                 │
│     ↓                                                       │
│     InpExporter::export("job.inp", mesh, loads)             │
│     ↓                                                       │
│     SolverWrapper::run("job.inp")                           │
│     ↓                                                       │
│     QProcess started on worker thread                       │
│     ↓                                                       │
│     readyReadStandardOutput → parse "iteration 5" →         │
│     emit progress(50, "Iter 5/10")                         │
│     ↓ (QueuedConnection → 主线程)                           │
│     QProgressBar::setValue(50)                              │
│                                                             │
│  3. 求解完成                                                 │
│     ↓                                                       │
│     emit finished(0) → SolverWrapper → Document             │
│     ↓                                                       │
│     FrdReader::read("job.frd", results)                     │
│     ↓                                                       │
│     emit Document::resultsLoaded()                         │
│     ↓                                                       │
│  ┌──────────────────────────────────────────────────────┐  │
│  │  信号扇出                                              │  │
│  │  ├─→ VtkMeshView → 云图渲染(位移/应力)              │  │
│  │  ├─→ NodeResultModel → QTableView 结果数据           │  │
│  │  └─→ StatusBar → "Solution complete"                │  │
│  └──────────────────────────────────────────────────────┘  │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

4.2 关键connect()汇总 ​

cpp
// ===== MainWindow构造中的关键连接 =====
void MainWindow::setupConnections() {
    // 文档数据变更 → 所有视图刷新
    connect(m_document->meshData(), &MeshData::dataChanged,
            this, &MainWindow::refreshAllViews);

    // 3D视口拾取节点 → 属性面板 + 状态栏
    connect(m_vtkView, &VtkMeshView::nodePicked,
            this, &MainWindow::onNodePicked);

    // 求解器进度 → 进度条 + 状态栏
    connect(m_document, &ModelDocument::solverProgress,
            this, &MainWindow::onSolverProgress);

    // 求解完成 → 加载结果
    connect(m_document, &ModelDocument::solverFinished,
            this, &MainWindow::onSolverFinished);

    // 属性面板修改 → 更新3D显示
    connect(m_propertyPanel, &PropertyPanel::displayModeChanged,
            m_vtkView, &VtkMeshView::setDisplayMode);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

第5部分:代码组织 ​

5.1 目录结构 ​

FastCAE/
├── CMakeLists.txt                 # 顶层构建
├── src/
│   ├── main.cpp                   # 入口
│   ├── core/                      # 核心数据(无Qt依赖)
│   │   ├── node.h                 #   struct Node
│   │   ├── element.h              #   struct Element
│   │   └── material.h             #   struct Material
│   ├── io/                        # 文件I/O
│   │   ├── bdf_importer.h/cpp     #   Nastran BDF读取
│   │   ├── inp_exporter.h/cpp     #   CalculiX INP输出
│   │   └── frd_reader.h/cpp       #   CalculiX FRD结果读取
│   ├── model/                     # MVD Model层
│   │   ├── mesh_data.h/cpp        #   QObject, 核心数据聚合
│   │   ├── model_document.h/cpp   #   QObject, 顶层文档
│   │   ├── node_coord_model.h/cpp #   QAbstractTableModel
│   │   └── node_result_model.h/cpp#   QAbstractTableModel
│   ├── solver/                    # 求解器集成
│   │   └── solver_wrapper.h/cpp   #   QThread + QProcess
│   ├── view/                      # UI视图
│   │   ├── main_window.h/cpp      #   QMainWindow
│   │   ├── vtk_mesh_view.h/cpp    #   QVTKOpenGLNativeWidget
│   │   ├── property_panel.h/cpp   #   QDockWidget
│   │   └── status_bar.h/cpp       #   QStatusBar
│   └── resources/
│       └── resources.qrc          # 图标/翻译文件
└── tests/
    ├── test_bdf_import.cpp
    └── test_mesh_data.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

5.2 CMake模块化 ​

bash
# CMakeLists.txt (简化)
cmake_minimum_required(VERSION 3.21)
project(FastCAE VERSION 1.0 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_AUTOMOC ON)

find_package(Qt6 REQUIRED COMPONENTS Core Widgets OpenGLWidgets)
find_package(VTK REQUIRED COMPONENTS GUISupportQt
    CommonCore FiltersSources RenderingOpenGL2)

# 核心库(无Qt依赖)
add_library(fastcae_core STATIC
    src/core/node.h src/core/element.h src/core/material.h)

# IO库
add_library(fastcae_io STATIC
    src/io/bdf_importer.cpp src/io/inp_exporter.cpp
    src/io/frd_reader.cpp)
target_link_libraries(fastcae_io PUBLIC Qt6::Core fastcae_core)

# Model库
add_library(fastcae_model STATIC
    src/model/mesh_data.cpp src/model/model_document.cpp)
target_link_libraries(fastcae_model PUBLIC fastcae_core Qt6::Core)

# Solver库
add_library(fastcae_solver STATIC src/solver/solver_wrapper.cpp)
target_link_libraries(fastcae_solver PUBLIC Qt6::Core)

# 可执行文件
add_executable(fastcae
    src/main.cpp src/view/main_window.cpp
    src/view/vtk_mesh_view.cpp src/view/property_panel.cpp)
target_link_libraries(fastcae PRIVATE
    fastcae_io fastcae_model fastcae_solver
    Qt6::Widgets Qt6::OpenGLWidgets ${VTK_LIBRARIES})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

第6部分:从案例到生产 ​

6.1 本案例的简化之处 ​

简化项生产级替代
单一网格格式(BDF)多格式插件系统 (BDF/INP/UNV/STL)
无撤销/重做QUndoStack (见[06])
单一求解器(CalculiX)求解器插件接口 (见[06])
单一视口多视口 (四视图: XY/YZ/XZ/3D)
无国际化QTranslator (见[06])
无单元测试QTest + Google Test
同步文件I/O异步I/O + 进度条

6.2 深入学习资源 ​

  • FreeCAD: src/Mod/Mesh/ 和 src/Gui/View3DInventorViewer.cpp — 真正的CAD级视口
  • ParaView: VTK Pipeline + Qt 的终极例子
  • CalculiX GraphiX: 同类型的轻量FEA前后处理器(CGX)

核心总结 ​

总结1:数据流向是单向的 ​

导入 → MeshData (数据) → signal dataChanged()
  → Views (QTableView, QTreeView, VtkMeshView) 自动刷新
  → 用户编辑 → Document → dataChanged() → Views刷新

关键:数据只存在于Document中,Views是被动观察者
1
2
3
4
5

总结2:信号槽是整个系统的"神经系统" ​

导入触发dataChanged,求解器进度通过QueuedConnection从Worker线程传递到UI线程,视口拾取触发属性面板更新——信号槽让所有组件松耦合协作。

总结3:POD数据 + QObject容器 ​

百万节点用std::vector<Node>(12 bytes/node),只有一个QObject(MeshData)管理整批数据。永远不要为每个节点/单元创建QObject。


章节测试 ​

测试1:数据流 ​

在FastCAE中,用户导入新网格后,以下哪个描述最准确? A. 每个View各自读取文件并显示 B. Document导入→MeshData更新→emit dataChanged()→所有View自动刷新 C. MainWindow直接更新每个View D. View轮询检查文件是否变化

测试2:求解器通信 ​

求解器进度从Worker线程到进度条的正确路径是? A. Worker直接调用 m_progressBar->setValue() B. Worker emit signal → QueuedConnection → 主线程 slot 更新进度条 C. Worker写文件 → 主线程轮询读文件 D. Worker用QMutex保护进度条然后直接更新

测试3:架构层次 ​

以下哪个是FastCAE架构中不属于Logic层的? A. ModelDocument B. MeshData C. BdfImporter D. SolverWrapper


参考答案 ​

测试1答案 ​

答案:B。数据单向流动:Importer→MeshData→signal dataChanged()→Views。所有Views通过连接同一个信号来同步,Document不直接操作任何View。

测试2答案 ​

答案:B。Worker和进度条在不同线程。Worker emit signal,Qt自动检测到跨线程→QueuedConnection→主线程事件循环执行slot→安全更新UI。这是[05-多线程]的核心模式。

测试3答案 ​

答案:C。BdfImporter属于Algorithm层(第3层)——纯文件解析逻辑,不依赖QObject信号槽。ModelDocument、MeshData、SolverWrapper都属于Logic层。


相关笔记 ​

  • Cae Architecture Patterns - CAE软件架构的完整模式
  • Visualization 3d - 3D可视化与VTK集成
  • Model View Architecture - Model/View数据处理
  • Multithreading Cae - QThread与求解器集成
  • Pre Post Processing - 前后处理概念

下一步学习 ​

  • [ ] 用本案例的骨架代码搭建你自己的CAE工具
  • [ ] 研究FreeCAD源码中的View3DInventorViewer(真正的CAD级视口)
  • [ ] 阅读ParaView的ServerManager架构(分布式可视化的典范)
  • [ ] 将Plugin系统([06])集成到FastCAE中,支持多种求解器和文件格式

学习状态:🟡 开始学习

案例的端到端时序 ​

text
Open file
 -> validate header/index
 -> create Document rev 1
 -> populate lightweight model
 -> background load/compute chunks(rev 1)
 -> GUI commits batches if revision matches
 -> render uploads with scene generation
User edits -> Command -> rev 2 -> old rev 1 results rejected/rebased
Save rev 2 -> atomic commit -> dirty cleared for rev 2
Close -> cancel -> wait -> release GPU -> destroy Document
1
2
3
4
5
6
7
8
9
10

这个时序是检验架构的主线:任何对象、任务、index 或 GPU handle 都应能指出在其中何时生、何时失效。

故障注入表 ​

text
file truncated -> parser bounded failure, Document not half-created
disk full save -> old file remains, dirty stays true
solver cancelled -> partial result not committed unless protocol allows
window closes during upload -> generation rejects completion
plugin unload request -> wait objects/tasks before module close
GPU device loss -> rebuild cache from Document
1
2
3
4
5
6

面试连续追问 ​

问:为什么保存要绑定 revision? 答:后台保存 rev 5 时用户可能编辑到 rev 6;完成只能确认 rev 5 已落盘,不能清除 rev 6 的 dirty。

问:大工程加载如何尽快显示? 答:先读取索引/元数据建立轻量 Document 与 view,再按可见性/优先级分块加载,统一内存背压和取消。

问:如何避免旧任务覆盖新场景? 答:任务携带 document ID、input revision、scene generation,提交点全部核对。

自测与答案 ​

  1. 导入失败是否应留下空 Document 标签? 答:按产品策略,但构造/切换应事务化,不能覆盖当前有效文档后才发现失败。
  2. Undo 命令能否引用临时 UI index? 答:不能,应保存稳定领域 ID/必要 delta。
  3. 关闭 timeout 后怎么办? 答:报告仍在途组件并按协议升级取消;不能直接卸载其代码/销毁其数据。

最后更新于:

Pager
上一篇8. 3D可视化与交互 / Interactive 3D Visualization

持续记录,持续成长

Copyright © Tidenflow