以下是一个基于 Rust 的 CAD 工具框架基础实现,聚焦几何建模、坐标系管理和 DXF 文件交互(CAD 最常用的交换格式)。代码设计强调模块化、可扩展性和 Rust 语言特性(如所有权、错误处理、泛型)。


AUTOCAD

一、框架整体架构

cad-framework/
├── src/
│   ├── geometry/          # 几何核心(点、线、圆、多边形等)
│   ├── coordinate_system/ # 坐标系管理(WCS/UCS 转换)
│   ├── drawing/           # 绘图上下文与渲染接口
│   ├── io/                # 文件 IO(DXF 读写)
│   └── main.rs            # 示例用法
└── Cargo.toml

在这里插入图片描述

二、核心模块实现

在这里插入图片描述

1. 几何模型(Geometry)

定义基础几何对象(点、线、圆)及其操作(平移、旋转、相交检测等)。

// src/geometry/mod.rs
use std::ops::{Add, Sub, Mul, Div};
use std::fmt;

/// 二维点(浮点精度)
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Point2D {
    pub x: f64,
    pub y: f64,
}

impl Point2D {
    pub fn new(x: f64, y: f64) -> Self {
        Self { x, y }
    }

    /// 平移点
    pub fn translate(&self, dx: f64, dy: f64) -> Self {
        Self::new(self.x + dx, self.y + dy)
    }

    /// 绕原点旋转(弧度)
    pub fn rotate(&self, angle: f64) -> Self {
        let cos = angle.cos();
        let sin = angle.sin();
        Self::new(
            self.x * cos - self.y * sin,
            self.x * sin + self.y * cos,
        )
    }
}

impl Add for Point2D {
    type Output = Self;
    fn add(self, other: Self) -> Self {
        Self::new(self.x + other.x, self.y + other.y)
    }
}

impl Sub for Point2D {
    type Output = Self;
    fn sub(self, other: Self) -> Self {
        Self::new(self.x - other.x, self.y - other.y)
    }
}

/// 二维线段(两点确定)
#[derive(Debug, Clone, PartialEq)]
pub struct Line2D {
    pub start: Point2D,
    pub end: Point2D,
}

impl Line2D {
    pub fn new(start: Point2D, end: Point2D) -> Self {
        Self { start, end }
    }

    /// 计算线段长度
    pub fn length(&self) -> f64 {
        (self.end - self.start).x.hypot((self.end - self.start).y)
    }

    /// 判断线段是否与另一线段相交(快速排斥 + 跨立实验)
    pub fn intersects(&self, other: &Line2D) -> bool {
        let a = self.start;
        let b = self.end;
        let c = other.start;
        let d = other.end;

        // 快速排斥实验
        let max_x = [a.x, b.x, c.x, d.x].iter().fold(f64::MIN, |a, &b| a.max(b));
        let min_x = [a.x, b.x, c.x, d.x].iter().fold(f64::MAX, |a, &b| a.min(b));
        let max_y = [a.y, b.y, c.y, d.y].iter().fold(f64::MIN, |a, &b| a.max(b));
        let min_y = [a.y, b.y, c.y, d.y].iter().fold(f64::MAX, |a, &b| a.min(b));
        if (self.start.x < min_x && self.end.x < min_x) || (self.start.x > max_x && self.end.x > max_x) {
            return false;
        }
        if (self.start.y < min_y && self.end.y < min_y) || (self.start.y > max_y && self.end.y > max_y) {
            return false;
        }

        // 跨立实验
        let cross = |p: Point2D, a: Point2D, b: Point2D| -> f64 {
            (a.x - p.x) * (b.y - p.y) - (a.y - p.y) * (b.x - p.x)
        };
        let c1 = cross(a, b, c);
        let c2 = cross(a, b, d);
        let c3 = cross(c, d, a);
        let c4 = cross(c, d, b);

        (c1 * c2 <= 1e-8) && (c3 * c4 <= 1e-8) // 允许微小误差
    }
}

/// 二维圆(圆心 + 半径)
#[derive(Debug, Clone, PartialEq)]
pub struct Circle2D {
    pub center: Point2D,
    pub radius: f64,
}

impl Circle2D {
    pub fn new(center: Point2D, radius: f64) -> Self {
        assert!(radius > 0.0, "Radius must be positive");
        Self { center, radius }
    }

    /// 判断圆与线段是否相交
    pub fn intersects_line(&self, line: &Line2D) -> bool {
        let d = line.start.distance_to(&line.end); // 线段长度
        let a = (line.end.x - line.start.x).powi(2) + (line.end.y - line.start.y).powi(2);
        let b = 2.0 * ((line.start.x - self.center.x) * (line.end.x - line.start.x) +
                        (line.start.y - self.center.y) * (line.end.y - line.start.y));
        let c = (line.start.x - self.center.x).powi(2) +
                (line.start.y - self.center.y).powi(2) - self.radius.powi(2);
        let delta = b.powi(2) - 4.0 * a * c;

        if delta < 0.0 {
            return false; // 无交点
        }

        let t1 = (-b - delta.sqrt()) / (2.0 * a);
        let t2 = (-b + delta.sqrt()) / (2.0 * a);

        // 交点在线段范围内(t ∈ [0,1])
        (t1 >= 0.0 && t1 <= 1.0) || (t2 >= 0.0 && t2 <= 1.0)
    }
}

// 为 Point2D 实现距离计算
trait Distance {
    fn distance_to(&self, other: &Self) -> f64;
}

impl Distance for Point2D {
    fn distance_to(&self, other: &Self) -> f64 {
        (self.x - other.x).hypot(self.y - other.y)
    }
}

2. 坐标系管理(Coordinate System)

支持世界坐标系(WCS)和用户坐标系(UCS)的转换。

// src/coordinate_system/mod.rs
use super::geometry::Point2D;

/// 坐标系类型(世界坐标系 WCS 或用户坐标系 UCS)
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CoordinateSystem {
    WCS, // 世界坐标系(全局固定)
    UCS, // 用户坐标系(可自定义原点、旋转)
}

/// 坐标系变换参数(原点偏移 + 旋转角度)
#[derive(Debug, Clone)]
pub struct Transform {
    pub origin: Point2D, // UCS 原点相对于 WCS 的偏移
    pub rotation: f64,   // UCS 相对于 WCS 的旋转角度(弧度)
}

impl Transform {
    pub fn new(origin: Point2D, rotation: f64) -> Self {
        Self { origin, rotation }
    }

    /// 将 UCS 坐标转换为 WCS 坐标
    pub fn ucs_to_wcs(&self, point: Point2D) -> Point2D {
        // 1. 平移(抵消 UCS 原点偏移)
        let translated = point.translate(-self.origin.x, -self.origin.y);
        // 2. 旋转(反向旋转抵消 UCS 旋转)
        translated.rotate(-self.rotation)
    }

    /// 将 WCS 坐标转换为 UCS 坐标
    pub fn wcs_to_ucs(&self, point: Point2D) -> Point2D {
        // 1. 旋转(应用 UCS 旋转)
        let rotated = point.rotate(self.rotation);
        // 2. 平移(添加 UCS 原点偏移)
        rotated.translate(self.origin.x, self.origin.y)
    }
}

3. 绘图上下文(Drawing Context)

抽象绘图操作接口,支持不同渲染目标(如屏幕、SVG、DXF)。

// src/drawing/mod.rs
use super::geometry::{Line2D, Circle2D, Point2D};
use std::error::Error;

/// 绘图操作 trait(抽象渲染接口)
pub trait Drawer {
    /// 设置当前颜色(RGB 0-255)
    fn set_color(&mut self, r: u8, g: u8, b: u8);
    /// 设置线宽(单位:毫米)
    fn set_line_width(&mut self, width: f64);
    /// 绘制线段
    fn draw_line(&mut self, line: &Line2D) -> Result<(), Box<dyn Error>>;
    /// 绘制圆
    fn draw_circle(&mut self, circle: &Circle2D) -> Result<(), Box<dyn Error>>;
    /// 绘制点(可选,用于调试)
    fn draw_point(&mut self, point: &Point2D) -> Result<(), Box<dyn Error>>;
}

/// 基于文本的简单绘图器(用于调试)
pub struct TextDrawer {
    current_color: (u8, u8, u8),
    current_line_width: f64,
}

impl TextDrawer {
    pub fn new() -> Self {
        Self {
            current_color: (0, 0, 0), // 默认黑色
            current_line_width: 0.5,
        }
    }
}

impl Drawer for TextDrawer {
    fn set_color(&mut self, r: u8, g: u8, b: u8) {
        self.current_color = (r, g, b);
    }

    fn set_line_width(&mut self, width: f64) {
        self.current_line_width = width;
    }

    fn draw_line(&mut self, line: &Line2D) -> Result<(), Box<dyn Error>> {
        println!(
            "Draw Line (Color: RGB{}, Width: {}): ({:.2}, {:.2}) -> ({:.2}, {:.2})",
            self.current_color.0,
            self.current_color.1,
            self.current_color.2,
            self.current_line_width,
            line.start.x,
            line.start.y,
            line.end.x,
            line.end.y
        );
        Ok(())
    }

    fn draw_circle(&mut self, circle: &Circle2D) -> Result<(), Box<dyn Error>> {
        println!(
            "Draw Circle (Color: RGB{}, Width: {}): Center ({:.2}, {:.2}), Radius: {:.2}",
            self.current_color.0,
            self.current_color.1,
            self.current_color.2,
            self.current_line_width,
            circle.center.x,
            circle.center.y,
            circle.radius
        );
        Ok(())
    }

    fn draw_point(&mut self, point: &Point2D) -> Result<(), Box<dyn Error>> {
        println!(
            "Draw Point (Color: RGB{}): ({:.2}, {:.2})",
            self.current_color.0, self.current_color.1, self.current_color.2, point.x, point.y
        );
        Ok(())
    }
}

4. DXF 文件 IO(关键交换格式)

实现 DXF 文件的读取和写入(支持线段、圆等基础实体)。

// src/io/dxf/mod.rs
use super::geometry::{Line2D, Circle2D, Point2D};
use std::fs::File;
use std::io::{BufWriter, Write};
use std::error::Error;

/// DXF 实体类型
#[derive(Debug, Clone, PartialEq)]
pub enum DxfEntity {
    Line(Line2D),
    Circle(Circle2D),
}

/// DXF 图层(可选,用于组织实体)
#[derive(Debug, Clone)]
pub struct DxfLayer {
    pub name: String,
    pub color: (u8, u8, u8), // RGB 颜色
}

/// DXF 文件写入器
pub struct DxfWriter {
    writer: BufWriter<File>,
    layers: Vec<DxfLayer>,
    current_layer: usize, // 当前使用的图层索引
}

impl DxfWriter {
    pub fn new(path: &str) -> Result<Self, Box<dyn Error>> {
        let file = File::create(path)?;
        Ok(Self {
            writer: BufWriter::new(file),
            layers: vec![DxfLayer {
                name: "0".to_string(), // DXF 默认图层
                color: (0, 0, 0),      // 默认黑色
            }],
            current_layer: 0,
        })
    }

    /// 添加新图层
    pub fn add_layer(&mut self, name: &str, color: (u8, u8, u8)) {
        self.layers.push(DxfLayer { name: name.to_string(), color });
    }

    /// 切换当前图层
    pub fn switch_layer(&mut self, index: usize) -> Result<(), Box<dyn Error>> {
        if index < self.layers.len() {
            self.current_layer = index;
            Ok(())
        } else {
            Err(format!("Layer index {} not found", index).into())
        }
    }

    /// 写入 DXF 文件头
    fn write_header(&mut self) -> Result<(), Box<dyn Error>> {
        writeln!(self.writer, "0\nSECTION\n2\nENTITIES")?;
        Ok(())
    }

    /// 写入 DXF 实体(线段/圆)
    fn write_entity(&mut self, entity: &DxfEntity) -> Result<(), Box<dyn Error>> {
        let layer = &self.layers[self.current_layer];
        match entity {
            DxfEntity::Line(line) => {
                writeln!(
                    self.writer,
                    "0\nLINE\n8\n{}\n62\n{:02x}{:02x}{:02x}\n10\n{:.3}\n20\n{:.3}\n11\n{:.3}\n21\n{:.3}",
                    layer.name,
                    layer.color.0, layer.color.1, layer.color.2,
                    line.start.x, line.start.y,
                    line.end.x, line.end.y
                )?;
            }
            DxfEntity::Circle(circle) => {
                writeln!(
                    self.writer,
                    "0\nCIRCLE\n8\n{}\n62\n{:02x}{:02x}{:02x}\n10\n{:.3}\n40\n{:.3}",
                    layer.name,
                    layer.color.0, layer.color.1, layer.color.2,
                    circle.center.x, circle.radius
                )?;
            }
        }
        Ok(())
    }

    /// 完成写入并关闭文件
    fn write_footer(&mut self) -> Result<(), Box<dyn Error>> {
        writeln!(self.writer, "0\nENDSEC\n0\nEOF")?;
        self.writer.flush()?;
        Ok(())
    }

    /// 保存所有实体到 DXF 文件
    pub fn save(&mut self, entities: &[DxfEntity]) -> Result<(), Box<dyn Error>> {
        self.write_header()?;
        for entity in entities {
            self.write_entity(entity)?;
        }
        self.write_footer()
    }
}

/// DXF 文件读取器(简化版,仅解析线段和圆)
pub struct DxfReader;

impl DxfReader {
    /// 从 DXF 文件读取实体(仅支持 LINE 和 CIRCLE)
    pub fn read(path: &str) -> Result<Vec<DxfEntity>, Box<dyn Error>> {
        let content = std::fs::read_to_string(path)?;
        let mut entities = Vec::new();
        let mut current_entity: Option<DxfEntity> = None;
        let mut current_code: Option<u32> = None;
        let mut current_value: Option<String> = None;

        for line in content.lines() {
            let parts: Vec<&str> = line.trim().split_whitespace().collect();
            if parts.is_empty() {
                continue;
            }

            match parts[0] {
                "0" => {
                    // 新实体开始
                    if let Some(ent) = current_entity.take() {
                        entities.push(ent);
                    }
                    current_entity = match parts[1] {
                        "LINE" => Some(DxfEntity::Line(Line2D {
                            start: Point2D::new(0.0, 0.0),
                            end: Point2D::new(0.0, 0.0),
                        })),
                        "CIRCLE" => Some(DxfEntity::Circle(Circle2D {
                            center: Point2D::new(0.0, 0.0),
                            radius: 0.0,
                        })),
                        _ => None, // 忽略不支持的实体
                    };
                    current_code = None;
                    current_value = None;
                }
                "8" => {
                    // 图层名称
                    if let Some(ent) = ¤t_entity {
                        if let DxfEntity::Line(_) | DxfEntity::Circle(_) = ent {
                            current_code = Some(parts[1].parse()?);
                        }
                    }
                }
                "10" | "20" => {
                    // 线段的起点(10=x, 20=y)
                    if let Some(DxfEntity::Line(line)) = ¤t_entity {
                        let coord = parts[1].parse::<f64>()?;
                        if parts[0] == "10" {
                            line.start.x = coord;
                        } else {
                            line.start.y = coord;
                        }
                    }
                }
                "11" | "21" => {
                    // 线段的终点(11=x, 21=y)
                    if let Some(DxfEntity::Line(line)) = ¤t_entity {
                        let coord = parts[1].parse::<f64>()?;
                        if parts[0] == "11" {
                            line.end.x = coord;
                        } else {
                            line.end.y = coord;
                        }
                    }
                }
                "100" => {
                    // 圆的半径(假设 40 码对应半径)
                    if let Some(DxfEntity::Circle(circle)) = ¤t_entity {
                        if parts[0] == "40" {
                            circle.radius = parts[1].parse()?;
                        }
                    }
                }
                _ => {}
            }
        }

        // 处理最后一个实体
        if let Some(ent) = current_entity.take() {
            entities.push(ent);
        }

        Ok(entities)
    }
}

在这里插入图片描述

三、示例用法

// src/main.rs
use cad_framework::{
    geometry::{Line2D, Circle2D, Point2D},
    coordinate_system::Transform,
    drawing::TextDrawer,
    io::dxf::{DxfWriter, DxfEntity},
};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 1. 创建几何对象
    let line = Line2D::new(Point2D::new(0.0, 0.0), Point2D::new(100.0, 100.0));
    let circle = Circle2D::new(Point2D::new(200.0, 200.0), 50.0);

    // 2. 坐标系变换(示例:将 UCS 原点偏移到 (50,50),旋转 30 度)
    let ucs_transform = Transform::new(Point2D::new(50.0, 50.0), 30.0_f64.to_radians());
    let wcs_line = line.translate(ucs_transform.origin.x, ucs_transform.origin.y)
        .rotate(ucs_transform.rotation);
    let wcs_circle = Circle2D::new(
        Point2D::new(200.0 - 50.0, 200.0 - 50.0).rotate(ucs_transform.rotation),
        50.0,
    );

    // 3. 使用文本绘图器可视化
    let mut text_drawer = TextDrawer::new();
    text_drawer.set_color(255, 0, 0); // 红色
    text_drawer.set_line_width(1.0);
    text_drawer.draw_line(&wcs_line)?;
    text_drawer.draw_circle(&wcs_circle)?;

    // 4. 导出为 DXF 文件
    let dxf_entities = vec![
        DxfEntity::Line(wcs_line),
        DxfEntity::Circle(wcs_circle),
    ];
    let mut dxf_writer = DxfWriter::new("output.dxf")?;
    dxf_writer.add_layer("MyLayer", (0, 255, 0)); // 添加绿色图层
    dxf_writer.switch_layer(1)?; // 切换到新图层
    dxf_writer.save(&dxf_entities)?;

    println!("DXF 文件已生成:output.dxf");
    Ok(())
}

四、扩展方向

  1. 三维支持:扩展 Point3DLine3DPlane 等几何类型,添加三维变换(平移、旋转、缩放矩阵)。
  2. 约束求解:实现几何约束(如平行、垂直、共点)的求解器(参考 drakegeometric-constraints 库)。
  3. 更复杂的 DXF 支持:解析图层、线型、块(Block)、尺寸标注(Dimension)等高级特性。
  4. GUI 集成:使用 eguirelm4 开发图形界面,支持可视化绘图和交互操作。
  5. 性能优化:使用空间分区数据结构(如四叉树、R-tree)加速几何查询(如碰撞检测)。

五、依赖配置(Cargo.toml)

[package]
name = "cad-framework"
version = "0.1.0"
edition = "2021"

[dependencies]
thiserror = "1.0"   # 错误处理
num-traits = "0.2"  # 数值特性(可选,用于泛型计算)

该框架实现了 CAD 的核心基础功能(几何建模、坐标系转换、文件交互),并通过模块化设计支持后续扩展。Rust 的内存安全和零成本抽象特性使其适合高性能 CAD 工具开发。

更多推荐