参考:https://www.bilibili.com/video/BV1ge4y1777j
!!!注意矩阵行列方向,视频和书不一样。
正三棱-顶点表
正三棱-面表
主视图
俯视图
侧视图
斜投影
算法设计
完整代码
// 23-三视图算法
// 参考 https://www.bilibili.com/video/BV1ge4y1777j
#define UNICODE
#include <Windows.h>
#include <Windowsx.h>
#include <math.h>
#define WINDOW_TEXT L"23-三视图算法"
#define ROUND(d) int(floor(d)+0.5) // 四舍五入
#define PI 3.1415926
struct Point2 // 二维点
{
double x;
double y;
double w; // 齐次坐标
Point2() :x(0), y(0), w(1.0) {}
Point2(double x, double y) :x(x), y(y), w(1.0) {}
};
struct Point3 :Point2 // 三维点
{
double z;
Point3():z(0) {}
Point3(double x, double y, double z) :Point2(x, y), z(z) {}
};
class Face // 面
{
public:
int ptIndex[4] = { 0,0,0,0 }; // 面顶点索引
int ptNubmer = 0; // 面的顶点数
public:
void SetPtNumber(int number)
{
ptNubmer = number;
}
};
class Projection // 三视图投影
{
private:
double T[4][4]; // 矩阵
Point3* P3d; // 三维顶点数组
Point2* POblView; // 斜视图顶点数组
Point2* PTriView; // 三视图投影顶点数组
int PtNumber; // 三维顶点个数
public:
Projection() {};
~Projection() {};
void SetMatrix(Point3* P, int ptNumber) // 初始化顶点
{
P3d = P;
PtNumber = ptNumber;
POblView = new Point2[ptNumber];
PTriView = new Point2[ptNumber];
}
void Identity()
{
T[0][0] = 1.0, T[0][1] = 0.0, T[0][2] = 0.0, T[0][3] = 0.0;
T[1][0] = 0.0, T[1][1] = 1.0, T[1][2] = 0.0, T[1][3] = 0.0;
T[2][0] = 0.0, T[2][1] = 0.0, T[2][2] = 1.0, T[2][3] = 0.0;
T[3][0] = 0.0, T[3][1] = 0.0, T[3][2] = 0.0, T[3][3] = 1.0;
}
Point2* OProject() // 斜等侧
{
Identity();
T[2][0] = -0.707;
T[2][1] = -0.707;
T[2][2] = 0.0;
MultiplyMatrix();
return POblView;
}
Point2* VProject() // 主视图
{
Identity();
T[0][0] = 0;
MultiplyMatrix();
return PTriView;
}
Point2* HProject() // 俯视图
{
Identity();
T[0][0] = 0;
T[0][1] = -1;
T[1][1] = 0;
MultiplyMatrix();
return PTriView;
}
Point2* WProject() // 侧视图
{
Identity();
T[0][0] = 0;
T[0][2] = -1;
T[2][2] = 0;
MultiplyMatrix();
return PTriView;
}
void MultiplyMatrix() //矩阵相乘
{
Point3* pTemp = new Point3[PtNumber];
for (int i = 0; i < PtNumber; i++)
{
pTemp[i].x = P3d[i].x * T[0][0] + P3d[i].y * T[1][0] + P3d[i].z * T[2][0] + P3d[i].w * T[3][0];
pTemp[i].y = P3d[i].x * T[0][1] + P3d[i].y * T[1][1] + P3d[i].z * T[2][1] + P3d[i].w * T[3][1];
pTemp[i].z = P3d[i].x * T[0][2] + P3d[i].y * T[1][2] + P3d[i].z * T[2][2] + P3d[i].w * T[3][2];
pTemp[i].w = P3d[i].x * T[0][3] + P3d[i].y * T[1][3] + P3d[i].z * T[2][3] + P3d[i].w * T[3][3];
PTriView[i].x = -pTemp[i].z; // 三视图顶点
PTriView[i].y = pTemp[i].y; // 三视图顶点
POblView[i] = Point2(pTemp[i].x, pTemp[i].y); // 三视图顶点
}
delete[]pTemp;
pTemp = NULL;
}
};
class Prism // 三棱柱
{
public:
int _a; // 三角形边长
int _b; // 棱长
Point3 V[6]; // 三棱柱三维顶点
Face F[5]; // 小面表
Point2* _pTrivew; // 三视图顶点数组指针
Point2* _pObliqueView; // 倾视图顶点数组指针
Projection _projection; // 投影对象
Point2 _ptCenter; // 中心点
public:
Prism()
{
_pTrivew = NULL;
_pObliqueView = NULL;
};
~Prism ()
{
if (_pTrivew != NULL)
{
delete[]_pTrivew;
_pTrivew = NULL;
}
if (_pObliqueView != NULL)
{
delete[]_pObliqueView;
_pObliqueView = NULL;
}
};
void SetParameter(int a, int b) // 读入参数
{
_a = a;
_b = b;
}
void ReadVertex()
{
V[0].x = -_a / 2.0; V[0].y = 0; V[0].z = -_b / 2.0;
V[1].x = 0; V[1].y = sqrt(3.0) / 2 * _a; V[1].z = -_b / 2.0;
V[2].x = _a / 2.0; V[2].y = 0; V[2].z = -_b / 2.0;
V[3].x = -_a / 2.0; V[3].y = 0; V[3].z = _b / 2.0;
V[4].x = 0; V[4].y = sqrt(3.0) / 2 * _a; V[4].z = _b / 2.0;
V[5].x = _a / 2.0; V[5].y = 0; V[5].z = _b / 2.0;
}
void ReadFace()
{
F[0].SetPtNumber(4); F[0].ptIndex[0] = 0; F[0].ptIndex[1] = 3; F[0].ptIndex[2] = 4; F[0].ptIndex[3] = 1;
F[1].SetPtNumber(3); F[1].ptIndex[0] = 0; F[1].ptIndex[1] = 1; F[1].ptIndex[2] = 2;
F[2].SetPtNumber(4); F[2].ptIndex[0] = 0; F[2].ptIndex[1] = 2; F[2].ptIndex[2] = 5; F[2].ptIndex[3] = 3;
F[3].SetPtNumber(4); F[3].ptIndex[0] = 1; F[3].ptIndex[1] = 4; F[3].ptIndex[2] = 5; F[3].ptIndex[3] = 2;
F[4].SetPtNumber(3); F[4].ptIndex[0] = 3; F[4].ptIndex[1] = 5; F[4].ptIndex[2] = 4;
}
void DrawOblique(HDC hdc,Point2 ptCenter) // 绘制倾等侧
{
TextOut(hdc,50,-10, L"立体图", wcslen(L"立体图"));
_ptCenter = ptCenter;
_projection.SetMatrix(V, 6);
_pObliqueView = _projection.OProject();
Draw(hdc, _pObliqueView);
}
void DrawVView(HDC hdc, Point2 ptCenter) // 绘制主视图
{
TextOut(hdc, -450, 320, L"主视图", wcslen(L"主视图"));
_ptCenter = ptCenter;
_projection.SetMatrix(V, 6);
_pTrivew = _projection.VProject();
Draw(hdc, _pTrivew);
}
void DrawHView(HDC hdc, Point2 ptCenter) // 绘制俯视图
{
TextOut(hdc, -450, -10, L"俯视图", wcslen(L"俯视图"));
_ptCenter = ptCenter;
_projection.SetMatrix(V, 6);
_pTrivew = _projection.HProject();
Draw(hdc, _pTrivew);
}
void DrawWView(HDC hdc, Point2 ptCenter) // 绘制侧视图
{
TextOut(hdc, 50, 320, L"侧视图", wcslen(L"侧视图"));
_ptCenter = ptCenter;
_projection.SetMatrix(V, 6);
_pTrivew = _projection.WProject();
Draw(hdc, _pTrivew);
}
void Draw(HDC hdc, Point2* p2d) // 绘制线框
{
Point2 screenPoint, pTemp;
for (int nFace = 0; nFace < 5; nFace++) // 访问面
{
for (int nPoint = 0; nPoint < F[nFace].ptNubmer; nPoint++) // 访问面顶点
{
screenPoint = p2d[F[nFace].ptIndex[nPoint]];
int x = ROUND(_ptCenter.x + screenPoint.x);
int y = ROUND(_ptCenter.y + screenPoint.y);
if (0 == nPoint)
{
MoveToEx(hdc, x, y, NULL);
pTemp = screenPoint;
}
else
{
LineTo(hdc, x, y );
}
}
LineTo(hdc, ROUND(_ptCenter.x + pTemp.x), ROUND(_ptCenter.y + pTemp.y));
}
}
};
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hdc;
RECT rect;
switch (uMsg)
{
case WM_PAINT:
{
hdc = BeginPaint(hwnd, &ps);
GetClientRect(hwnd, &rect);
SetMapMode(hdc, MM_ANISOTROPIC);
SetWindowExtEx(hdc, rect.right, rect.bottom, NULL);
SetViewportExtEx(hdc, rect.right, -rect.bottom, NULL);
SetViewportOrgEx(hdc, rect.right / 2, rect.bottom / 2, NULL);
// 内存DC
HDC memDC = CreateCompatibleDC(hdc); // 创建兼容DC 画板
HBITMAP newBitmap = CreateCompatibleBitmap(hdc, rect.right, rect.bottom); // 创建画布
HGDIOBJ oldBitmap = SelectObject(memDC, newBitmap); // 将画布选入画板
FillRect(memDC, &rect, (HBRUSH)(COLOR_WINDOW + 1));
//DrawText(memDC, m_str, wcslen(m_str), &rc, NULL);
SetMapMode(memDC, MM_ANISOTROPIC);
SetWindowExtEx(memDC, rect.right, rect.bottom, NULL);
SetViewportExtEx(memDC, rect.right, -rect.bottom, NULL);
SetViewportOrgEx(memDC, rect.right / 2, rect.bottom / 2, NULL);
int nClientWidth = rect.right;
int nClientHeght = rect.bottom;
{ // 绘制边框
MoveToEx(memDC, -nClientWidth / 2, 0, NULL);
LineTo(memDC, nClientWidth / 2, 0);
MoveToEx(memDC, 0, -nClientHeght / 2, NULL);
LineTo(memDC, 0, nClientHeght / 2);
}
{ // 绘制图形
static Prism prism;
prism.SetParameter(100,150);
prism.ReadVertex();
prism.ReadFace();
Point2 ptCenter;
ptCenter = Point2(nClientHeght/4.0,-nClientHeght/4.0);
prism.DrawOblique(memDC,ptCenter); // 绘制立体图
ptCenter = Point2(-nClientHeght / 4.0, nClientHeght / 4.0);
prism.DrawVView(memDC, ptCenter); // 绘制主视图
ptCenter = Point2(nClientHeght / 4.0, nClientHeght / 4.0);
prism.DrawWView(memDC, ptCenter); // 绘制侧视图
ptCenter = Point2(-nClientHeght / 4.0, -nClientHeght / 4.0);
prism.DrawHView(memDC, ptCenter); // 绘制俯视图
}
// 内存dc复制到设备
BitBlt(hdc, ROUND(-rect.right / 2), ROUND(-rect.bottom / 2), rect.right, rect.bottom, memDC, ROUND(-rect.right / 2), ROUND(-rect.bottom / 2), SRCCOPY);
SelectObject(memDC, oldBitmap);
DeleteObject(newBitmap);
DeleteDC(memDC);
EndPaint(hwnd, &ps);
}
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
{
// Register the window class.
const wchar_t CLASS_NAME[] = L"CAG";
WNDCLASS wc = { };
wc.style = CS_HREDRAW | CS_VREDRAW; // 重新绘制整个工作区
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
RegisterClass(&wc);
HWND hwnd = CreateWindowEx(
0, // Optional window styles.
CLASS_NAME, // Window class
WINDOW_TEXT, // Window text
WS_OVERLAPPEDWINDOW, // Window style
// Size and position
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL, // Parent window
NULL, // Menu
hInstance, // Instance handle
NULL // Additional application data
);
if (hwnd == NULL)
{
return 0;
}
ShowWindow(hwnd, nCmdShow);
MSG msg = { };
while (GetMessage(&msg, NULL, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
案例总结