【学写LibreCAD】1 创建核心模块库
从核心模块开始重写 LibreCAD 的 Rust 实现,需要从基础数据结构、数学工具和关键接口入手。以下是具体的代码起步步骤:
- 创建 Rust 核心模块项目
(1) 初始化项目
cargo new cad_core --lib
cd cad_core
(2) 添加 cxx-qt 依赖(用于与 C++/Qt 交互)
在 Cargo.toml 中添加:
[lib]
crate-type = ["staticlib", "cdylib"] # 生成静态库和动态库
[dependencies]
cxx = "1.0"
cxx-qt = "0.7"
cxx-qt-lib = "0.7"
- 定义核心数据结构
(1) 几何对象(geometry.rs)
// src/geometry.rs
#[derive(Debug, Clone, Copy)]
pub struct Point2D {
pub x: f64,
pub y: f64,
}
#[derive(Debug, Clone)]
pub enum Shape {
Line(Point2D, Point2D),
Circle(Point2D, f64), // 圆心 + 半径
Arc(Point2D, f64, f64, f64), // 圆心、半径、起始角、终止角(弧度)
}
(2) 数学工具(math.rs)
// src/math.rs
use crate::geometry::Point2D;
// 计算两点距离
pub fn distance(p1: &Point2D, p2: &Point2D) -> f64 {
((p1.x - p2.x).powi(2) + (p1.y - p2.y).powi(2)).sqrt()
}
// 判断点是否在线上(简化版)
pub fn is_point_on_line(point: &Point2D, line_start: &Point2D, line_end: &Point2D) -> bool {
// 实现具体的几何判断逻辑
// 示例:简单阈值判断
distance(point, line_start) + distance(point, line_end) - distance(line_start, line_end) < 1e-6
}
- 文档管理模块(document.rs)
// src/document.rs
use std::collections::HashMap;
use crate::geometry::Shape;
pub struct Layer {
pub name: String,
pub visible: bool,
}
pub struct Document {
pub layers: HashMap<String, Layer>,
pub shapes: Vec<Shape>,
}
impl Document {
pub fn new() -> Self {
Document {
layers: HashMap::new(),
shapes: Vec::new(),
}
}
pub fn add_shape(&mut self, shape: Shape) {
self.shapes.push(shape);
}
pub fn create_layer(&mut self, name: &str) {
self.layers.insert(
name.to_string(),
Layer {
name: name.to_string(),
visible: true,
},
);
}
}
- 通过 CXX-QT 暴露接口给 C++
(1) 创建桥接文件(src/cxx_qt.rs)
// src/cxx_qt.rs
#[cxx_qt::bridge]
mod ffi {
use super::*;
use crate::geometry::{Point2D, Shape};
use crate::document::Document;
// 暴露 Point2D 到 C++
#[derive(Debug, Clone, Copy)]
#[namespace = "rust::core"]
struct Point2D {
x: f64,
y: f64,
}
// 暴露 Document 的接口
#[namespace = "rust::core"]
extern "Rust" {
type Document;
fn new_document() -> UniquePtr<Document>;
fn add_shape(self: Pin<&mut Document>, shape: Shape);
fn create_layer(self: Pin<&mut Document>, name: &str);
}
}
(2) 实现 CXX-QT 绑定(src/lib.rs)
// src/lib.rs
mod geometry;
mod math;
mod document;
use cxx_qt::CxxQtType;
use std::pin::Pin;
// 实现 CXX-QT 的 Document 类型
impl document::Document {
fn new_document() -> cxx::UniquePtr<Self> {
cxx::UniquePtr::new(document::Document::new())
}
fn add_shape(self: Pin<&mut Self>, shape: geometry::Shape) {
self.get_mut().add_shape(shape);
}
fn create_layer(self: Pin<&mut Self>, name: &str) {
self.get_mut().create_layer(name);
}
}
- 单元测试
// tests/core_tests.rs
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_distance() {
let p1 = geometry::Point2D { x: 0.0, y: 0.0 };
let p2 = geometry::Point2D { x: 3.0, y: 4.0 };
assert_eq!(math::distance(&p1, &p2), 5.0);
}
#[test]
fn test_add_shape() {
let mut doc = document::Document::new();
let line = geometry::Shape::Line(
geometry::Point2D { x: 0.0, y: 0.0 },
geometry::Point2D { x: 1.0, y: 1.0 },
);
doc.add_shape(line);
assert_eq!(doc.shapes.len(), 1);
}
}
- 集成到现有 C++ 项目
(1) C++ 调用示例
// main.cpp
#include "rust/core.h"
int main() {
auto doc = rust::core::new_document();
doc->create_layer("Default");
rust::core::Point2D p1{0.0, 0.0};
rust::core::Point2D p2{10.0, 10.0};
doc->add_shape(rust::core::Shape::Line(p1, p2));
return 0;
}
(2) 构建配置(CMakeLists.txt)
cmake_minimum_required(VERSION 3.15)
project(LibreCAD_Core)
# 添加 Rust 库
add_subdirectory(libre_cad_core)
add_executable(libre_cad_core_test main.cpp)
target_link_libraries(libre_cad_core_test PRIVATE libre_cad_core)
- 关键注意事项
- 内存管理:
- 使用 UniquePtr 确保 Rust 对象在 C++ 中安全传递。
- 避免跨 FFI 边界传递复杂生命周期对象。
- 性能优化:
- 对高频调用的数学函数(如 distance)添加 #[inline]。
- 使用 Arc<Mutex> 实现多线程安全(如果涉及并发)。
- 逐步替换策略:
- 先替换无 UI 依赖的模块(如几何计算)。
- 通过 CXX-QT 逐步将 Rust 实现的模块接入 Qt 界面。
下一步行动
- 运行测试:
cargo test
- 构建并链接到 C++:
mkdir build
cd build
cmake ..
make
- 扩展功能:
- 实现更多几何类型(如圆弧、多段线)。
- 添加文件 I/O 支持(如 DXF 解析)。
通过这种方式,可以逐步将 LibreCAD 的核心模块迁移到 Rust,同时保持与现有 C++/Qt 代码的互操作性。