要使用 **C 语言 + Windows API** 完整地读取 DXF 文件中的图形实体(如 LINE、CIRCLE、ARC 等),并将其在窗口中显示出来,需要完成以下关键步骤:
1. **解析 DXF 文件结构**
2. **提取图形实体数据**
3. **使用 GDI 在 Win32 窗口中绘制**
下面是一个 **完整可运行的 C 程序示例**,支持解析 `LINE`、`CIRCLE` 和 `ARC` 实体,并用 Windows GDI 显示。
---
### ✅ 完整代码:读取 DXF 实体并在窗口中显示
```c
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#define MAX_ENTITIES 1000
#define BUFFER_SIZE 256
// 实体类型枚举
typedef enum {
ENTITY_LINE,
ENTITY_CIRCLE,
ENTITY_ARC
} EntityType;
// 统一存储所有图形实体
typedef struct {
EntityType type;
union {
struct { double x1, y1, x2, y2; } line;
struct { double cx, cy, r; } circle;
struct { double cx, cy, r, startAngle, endAngle; } arc;
} data;
} GraphicEntity;
GraphicEntity entities[MAX_ENTITIES];
int entityCount = 0;
// 解析 DXF 文件(ASCII 格式)
void ParseDXF(const char* filename) {
FILE* file = fopen(filename, "r");
if (!file) {
MessageBoxA(NULL, "无法打开 DXF 文件!", "错误", MB_OK | MB_ICONERROR);
return;
}
char buffer[BUFFER_SIZE];
char groupCode[2] = {0};
char currentEntity[10] = {0};
// 临时变量
double cx = 0, cy = 0, r = 0, startAngle = 0, endAngle = 0;
double x1 = 0, y1 = 0, x2 = 0, y2 = 0;
int inEntities = 0;
while (fgets(buffer, sizeof(buffer), file)) {
buffer[strcspn(buffer, "\r\n")] = 0;
if (strlen(buffer) == 0) continue;
// 判断是否为组码行(单个数字)
if (strlen(buffer) == 1 && isdigit(buffer[0])) {
strcpy_s(groupCode, sizeof(groupCode), buffer);
continue;
}
char* value = buffer;
// 节处理
if (strcmp(value, "SECTION") == 0) {
inEntities = 0;
continue;
} else if (strcmp(value, "ENTITIES") == 0) {
inEntities = 1;
continue;
} else if (strcmp(value, "ENDSEC") == 0 && inEntities) {
inEntities = 0;
continue;
} else if (strcmp(value, "EOF") == 0) {
break;
}
if (!inEntities) continue;
// 实体类型识别
if (strcmp(groupCode, "0") == 0) {
if (strcmp(value, "LINE") == 0) {
strcpy_s(currentEntity, sizeof(currentEntity), "LINE");
x1 = y1 = x2 = y2 = 0;
} else if (strcmp(value, "CIRCLE") == 0) {
strcpy_s(currentEntity, sizeof(currentEntity), "CIRCLE");
cx = cy = r = 0;
} else if (strcmp(value, "ARC") == 0) {
strcpy_s(currentEntity, sizeof(currentEntity), "ARC");
cx = cy = r = startAngle = endAngle = 0;
} else {
currentEntity[0] = '\0';
}
}
// 提取数据
if (strcmp(currentEntity, "LINE") == 0) {
if (strcmp(groupCode, "10") == 0) x1 = atof(value);
else if (strcmp(groupCode, "20") == 0) y1 = atof(value);
else if (strcmp(groupCode, "11") == 0) x2 = atof(value);
else if (strcmp(groupCode, "21") == 0) y2 = atof(value);
if (strcmp(groupCode, "21") == 0 && entityCount < MAX_ENTITIES) {
entities[entityCount].type = ENTITY_LINE;
entities[entityCount].data.line.x1 = x1;
entities[entityCount].data.line.y1 = y1;
entities[entityCount].data.line.x2 = x2;
entities[entityCount].data.line.y2 = y2;
entityCount++;
}
}
else if (strcmp(currentEntity, "CIRCLE") == 0) {
if (strcmp(groupCode, "10") == 0) cx = atof(value);
else if (strcmp(groupCode, "20") == 0) cy = atof(value);
else if (strcmp(groupCode, "40") == 0) r = atof(value);
if (strcmp(groupCode, "40") == 0 && entityCount < MAX_ENTITIES) {
entities[entityCount].type = ENTITY_CIRCLE;
entities[entityCount].data.circle.cx = cx;
entities[entityCount].data.circle.cy = cy;
entities[entityCount].data.circle.r = r;
entityCount++;
}
}
else if (strcmp(currentEntity, "ARC") == 0) {
if (strcmp(groupCode, "10") == 0) cx = atof(value);
else if (strcmp(groupCode, "20") == 0) cy = atof(value);
else if (strcmp(groupCode, "40") == 0) r = atof(value);
else if (strcmp(groupCode, "50") == 0) startAngle = atof(value);
else if (strcmp(groupCode, "51") == 0) endAngle = atof(value);
if (strcmp(groupCode, "51") == 0 && entityCount < MAX_ENTITIES) {
entities[entityCount].type = ENTITY_ARC;
entities[entityCount].data.arc.cx = cx;
entities[entityCount].data.arc.cy = cy;
entities[entityCount].data.arc.r = r;
entities[entityCount].data.arc.startAngle = startAngle;
entities[entityCount].data.arc.endAngle = endAngle;
entityCount++;
}
}
}
fclose(file);
}
// 窗口过程函数
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
static HWND hButton;
HDC hdc;
PAINTSTRUCT ps;
RECT rect;
switch (uMsg) {
case WM_CREATE:
hButton = CreateWindow(
"BUTTON", "加载 DXF 文件",
WS_VISIBLE | WS_CHILD,
50, 50, 150, 30,
hwnd, (HMENU)1, NULL, NULL
);
break;
case WM_COMMAND:
if (LOWORD(wParam) == 1) {
ParseDXF("test.dxf"); // 放置你的 DXF 文件
InvalidateRect(hwnd, NULL, TRUE);
}
break;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
GetClientRect(hwnd, &rect);
// 设置坐标映射,适应绘图范围
SetMapMode(hdc, MM_ANISOTROPIC);
SetWindowExtEx(hdc, 1000, -1000, NULL); // Y向下为正
SetViewportExtEx(hdc, rect.right, rect.bottom, NULL);
SetViewportOrgEx(hdc, 0, 0, NULL);
// 绘制所有实体
for (int i = 0; i < entityCount; i++) {
switch (entities[i].type) {
case ENTITY_LINE:
MoveToEx(hdc,
(int)entities[i].data.line.x1,
(int)entities[i].data.line.y1, NULL);
LineTo(hdc,
(int)entities[i].data.line.x2,
(int)entities[i].data.line.y2);
break;
case ENTITY_CIRCLE: {
int cx = (int)entities[i].data.circle.cx;
int cy = (int)entities[i].data.circle.cy;
int r = (int)entities[i].data.circle.r;
Ellipse(hdc, cx - r, cy - r, cx + r, cy + r);
break;
}
case ENTITY_ARC: {
int cx = (int)entities[i].data.arc.cx;
int cy = (int)entities[i].data.arc.cy;
int r = (int)entities[i].data.arc.r;
double sa = entities[i].data.arc.startAngle;
double ea = entities[i].data.arc.endAngle;
// 转换角度为笛卡尔坐标点(GDI Arc 需要物理坐标)
int x1 = cx + (int)(r * cos(sa * M_PI / 180.0));
int y1 = cy - (int)(r * sin(sa * M_PI / 180.0)); // Y轴反向
int x2 = cx + (int)(r * cos(ea * M_PI / 180.0));
int y2 = cy - (int)(r * sin(ea * M_PI / 180.0));
Arc(hdc, cx - r, cy - r, cx + r, cy + r, x1, y1, x2, y2);
break;
}
}
}
EndPaint(hwnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}
// WinMain
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
const char CLASS_NAME[] = "DXF_Viewer";
WNDCLASS wc = {0};
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
if (!RegisterClass(&wc)) {
MessageBox(NULL, "注册窗口类失败!", "错误", MB_OK);
return 0;
}
HWND hwnd = CreateWindowEx(
0,
CLASS_NAME, "DXF 图形查看器",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 800, 600,
NULL, NULL, hInstance, NULL
);
if (!hwnd) {
MessageBox(NULL, "创建窗口失败!", "错误", MB_OK);
return 0;
}
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
```
---
### 🔍 解释
#### ✅ 功能说明:
- 支持从 ASCII DXF 文件中解析 `LINE`、`CIRCLE`、`ARC`。
- 使用标准 Win32 API 创建窗口和按钮。
- 点击按钮加载 `test.dxf` 文件并绘制图形。
- 使用 `SetMapMode(MM_ANISOTROPIC)` 实现逻辑坐标到屏幕坐标的缩放适配。
- 所有实体统一管理,便于扩展。
#### 📁 使用方法:
1. 将上述代码保存为 `.c` 文件(如 `dxf_viewer.c`)。
2. 编译环境:Visual Studio 或 MinGW(需链接 `user32.lib`, `gdi32.lib`)。
3. 准备一个简单的 ASCII DXF 文件(例如 AutoCAD 导出为“AutoCAD R12/LT2 DXF”格式)。
4. 把文件命名为 `test.dxf` 并放在程序同目录下。
5. 运行程序,点击按钮即可看到图形。
#### ⚠️ 注意事项:
- 本程序仅处理基本实体,未考虑图层、颜色、多段线(POLYLINE)、块(BLOCK)等复杂结构。
- 坐标系假设是二维 XY 平面,Z=0。
- GDI 的 `Arc` 函数按顺时针方向绘制,角度单位为度。
---