参考:https://www.bilibili.com/video/BV1v14y1W7sA
数据:https://github.com/dasch/graphics/blob/master/data/teapot.data
茶壶类
class UTeapot // 茶壶类
{
public:
Point3 Vertex[306]; // 306顶点
Patch _patch[32]; // 32个曲面
BicubicBezierPatch Patch; // Bezier曲面
public:
UTeapot() {};
~UTeapot() {};
void ReadVertext()
{
// 茶壶顶点
std::string strVertex = R"( 1 1.40000 0.00000 2.40000
2 1.40000 -0.78400 2.40000
.....
.....
306 1.42500 -0.79800 0.00000)";
std::stringstream stream(strVertex);
char strLine[1024];
int index,res;
double x, y, z;
while (stream.getline(strLine, 1024))
{
res=sscanf(strLine, "%d %lf %lf %lf", &index, &x, &y, &z);
if (res > 0)
{
Vertex[index-1] = Point3(x,y,z);
}
}
}
void ReadPath()
{
// 茶壶顶点索引
std::string indices = R"( 1 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
.....
32 270 270 270 270 300 305 306 279 297 303 304 275 96 95 94 93)";
std::stringstream stream(indices);
char strLine[1024];
int index, res;
int a1, a2, a3, a4, b1, b2, b3, b4, c1, c2, c3, c4, d1, d2, d3, d4;
while (stream.getline(strLine, 1024))
{
res = sscanf(strLine, "%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d", &index,
&a1, &a2, &a3, &a4, &b1, &b2, &b3, &b4, &c1, &c2, &c3, &c4,&d1, &d2, &d3, &d4);
if (res > 0)
{
_patch[index - 1].ptIndex[0][0] = a1; _patch[index - 1].ptIndex[0][1] = a2; _patch[index - 1].ptIndex[0][2] = a3; _patch[index - 1].ptIndex[0][3] = a4;
_patch[index - 1].ptIndex[1][0] = b1; _patch[index - 1].ptIndex[1][1] = b2; _patch[index - 1].ptIndex[1][2] = b3; _patch[index - 1].ptIndex[1][3] = b4;
_patch[index - 1].ptIndex[2][0] = c1; _patch[index - 1].ptIndex[2][1] = c2; _patch[index - 1].ptIndex[2][2] = c3; _patch[index - 1].ptIndex[2][3] = c4;
_patch[index - 1].ptIndex[3][0] = d1; _patch[index - 1].ptIndex[3][1] = d2; _patch[index - 1].ptIndex[3][2] = d3; _patch[index - 1].ptIndex[3][3] = d4;
}
}
}
void DrawParts(HDC hdc,int nPatchStart,int nPatchEnd) // 绘制茶壶
{
Point3 P3[4][4]; // 曲面控制点
int n=3;//递归深度
for (int nPatch = nPatchStart; nPatch < nPatchEnd; nPatch++)
{
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
P3[i][j] = Vertex[_patch[nPatch].ptIndex[i][j] - 1]; // 数组从0开始 -1
}
}
Patch.ReadControlPoint(P3); // 读取控制点
Patch.DrawCurvedPatch(hdc,n); // 绘制曲面
//Patch.DrawControlGrid(hdc); // 绘制控制网格
}
}
};
完整代码
// 28-Utah茶壶算法
// 参考 https://www.bilibili.com/video/BV1v14y1W7sA
// 数据 https://github.com/dasch/graphics/blob/master/data/teapot.data
#define UNICODE
#include
#include
#include
#include
#include
#include
#define WINDOW_TEXT L"28-Utah茶壶算法"
#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) {}
Point2(double x, double y) :x(x), y(y), w(1) {}
friend Point2 operator + (Point2 pt0, Point2 pt1)
{
return Point2(pt0.x + pt1.x, pt0.y + pt1.y);
}
friend Point2 operator * (Point2 pt, double n)
{
return Point2(pt.x * n, pt.y * n);
}
friend Point2 operator * (double n, Point2 pt)
{
return Point2(pt.x * n, pt.y * n);
}
};
struct Point3 :Point2
{
double z;
Point3() :z(0) {}
Point3(double x, double y, double z) :Point2(x, y), z(z) {}
friend Point3 operator + (Point3 pt0, Point3 pt1)
{
return Point3(pt0.x + pt1.x, pt0.y + pt1.y, pt0.z + pt1.z);
}
friend Point3 operator * (double scalar, const Point3& pt)
{
return Point3(pt.x * scalar, pt.y * scalar, pt.z * scalar);
}
friend Point3 operator * (const Point3& pt, double scalar)
{
return Point3(pt.x * scalar, pt.y * scalar, pt.z * scalar);
}
double DotProduct(const Point3& p0, const Point3& p1) // 向量的点积
{
return(p0.x * p1.x + p0.y * p1.y + p0.z * p1.z);
}
Point3 CrossProduct(const Point3& v0, const Point3& v1) // 向量的叉积
{
return Point3(v0.y * v1.z - v0.z * v1.y,
v0.z * v1.x - v0.x * v1.z,
v0.x * v1.y - v0.y * v1.x);
}
};
class Transform3
{
public:
Point3* _P; // 顶点数组
int _ptNumber; // 顶点数量
double _M[4][4]; // 变换矩阵
public:
Transform3() :_P(NULL), _ptNumber(0)
{
memset(_M, 0, sizeof(_M));
};
~Transform3() {};
void MultiplyMatrix() //矩阵相乘
{
Point3* PTemp = new Point3[_ptNumber];
for (int i = 0; i < _ptNumber; i++)
{
PTemp[i] = _P[i];
}
for (int i = 0; i < _ptNumber; i++)
{
_P[i].x = _M[0][0] * PTemp[i].x + _M[0][1] * PTemp[i].y + _M[0][2] * PTemp[i].z + _M[3][0]; // * PTemp[i].w
_P[i].y = _M[1][0] * PTemp[i].x + _M[1][1] * PTemp[i].y + _M[1][2] * PTemp[i].z + _M[3][1]; // * PTemp[i].w
_P[i].z = _M[2][0] * PTemp[i].x + _M[2][1] * PTemp[i].y + _M[2][2] * PTemp[i].z + _M[3][2]; // * PTemp[i].w
_P[i].w = _M[3][0] * PTemp[i].x + _M[3][1] * PTemp[i].y + _M[3][2] * PTemp[i].z + _M[3][3]; // * PTemp[i].w
}
delete[]PTemp;
}
void SetMatrix(Point3* P, int ptNumber) //顶点数组初始化
{
_P = P;
_ptNumber = ptNumber;
}
void Identity()
{
_M[0][0] = 1.0, _M[0][1] = 0.0, _M[0][2] = 0.0, _M[0][3] = 0.0;
_M[1][0] = 0.0, _M[1][1] = 1.0, _M[1][2] = 0.0, _M[1][3] = 0.0;
_M[2][0] = 0.0, _M[2][1] = 0.0, _M[2][2] = 1.0, _M[2][3] = 0.0;
_M[3][0] = 0.0, _M[3][1] = 0.0, _M[3][2] = 0.0, _M[3][3] = 1.0;
}
void Translate(double tx, double ty, double tz)//平移变换
{
Identity();
_M[3][0] = tx;
_M[3][1] = ty;
_M[3][2] = tz;
MultiplyMatrix();
}
void Scale(double sx, double sy, double sz)//缩放变换
{
Identity();
_M[0][0] = sx;
_M[1][1] = sy;
_M[2][2] = sz;
MultiplyMatrix();
}
void RotateX(double beta)//绕X轴旋转变换
{
Identity();
beta = beta * PI / 180;
_M[1][1] = cos(beta), _M[1][2] = -sin(beta);
_M[2][1] = sin(beta), _M[2][2] = cos(beta);
MultiplyMatrix();
}
void RotateY(double beta)//绕Y轴旋转变换
{
Identity();
beta = beta * PI / 180;
_M[0][0] = cos(beta), _M[0][2] = sin(beta);
_M[2][0] = -sin(beta), _M[2][2] = cos(beta);
MultiplyMatrix();
}
};
struct T2
{
double u;
double v;
T2() :u(0), v(0) {}
T2(double u, double v) :u(u), v(v) {}
friend T2 operator+(const T2& t0, const T2& t1)
{
return T2(t0.u + t1.u, t0.v + t1.v);
}
friend T2 operator/(const T2& t, double n)
{
return T2(t.u / n, t.v / n);
}
};
class Projection // 透视投影
{
public:
Point3 Eye; // 视点
double R; // 视点球坐标
double Phi; //
double Theta; //
double D; //
double K[8]; // 透视常数
public:
Projection()
{
R = 1000;
D = 800;
Phi = 90;
Theta = 0; // 世界坐标z轴正上方 看向原点
InitialParameter();
}
~Projection() {}
void InitialParameter() // 初始化参数
{
K[0] = sin(PI * Theta / 180);
K[1] = sin(PI * Phi / 180);
K[2] = cos(PI * Theta / 180);
K[3] = cos(PI * Phi / 180);
K[4] = K[1] * K[2];
K[5] = K[0] * K[1];
K[6] = K[2] * K[3];
K[7] = K[0] * K[3];
Eye = Point3(R * K[5], R * K[5], R * K[4]); // 设置视点
}
void SetEye(double r, double phi, double theta) // 设置视点
{
R = r;
Phi = phi;
Theta = theta;
InitialParameter();
}
Point3 GetEye()
{
return Eye;
}
Point2 OrthographicProjection(Point3 worldPoint) // 正交投影
{
return Point2(worldPoint.x, worldPoint.y);
}
Point2 CavalierProjection(Point3 worldPoint) // 斜等侧投影
{
Point2 screenPoint; // 屏幕坐标
double cota = 1;
double beta = PI / 4;
screenPoint.x = worldPoint.x - worldPoint.z * cota * cos(beta);
screenPoint.y = worldPoint.y - worldPoint.z * cota * cos(beta);
return screenPoint;
}
Point2 CabinetProjection(Point3 worldPoint) // 斜二侧投影
{
Point2 screenPoint; // 屏幕坐标
double cota = 0.5;
double beta = PI / 4;
screenPoint.x = worldPoint.x - worldPoint.z * cota * cos(beta);
screenPoint.y = worldPoint.y - worldPoint.z * cota * cos(beta);
return screenPoint;
}
Point2 PerspectiveProjection2(Point3 worldPoint) // 二维透视投影
{
Point3 viewPoint; // 观察坐标系
viewPoint.x = K[2] * worldPoint.x - K[0] * worldPoint.z;
viewPoint.y = -K[7] * worldPoint.x + K[1] * worldPoint.y - K[6] * worldPoint.z;
viewPoint.z = -K[5] * worldPoint.x - K[3] * worldPoint.y - K[4] * worldPoint.z + R;
Point2 screenPoint; // 屏幕坐标
screenPoint.x = D * viewPoint.x / viewPoint.z;
screenPoint.y = D * viewPoint.y / viewPoint.z;
return screenPoint;
}
};
class BicubicBezierPatch // 双三次Bezier曲面
{
public:
Point3 P[4][4]; // 控制点
Point3 GridP[4]; // 网格坐标
Projection Proj; // 投影
public:
BicubicBezierPatch() {};
~BicubicBezierPatch() {};
void ReadControlPoint(Point3 p[4][4]) // 读入控制点
{
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
P[i][j] = p[i][j];
}
}
}
void DrawCurvedPatch(HDC hdc, int nRecursion) // 绘制递归曲面 nRecursion递归深度
{
T2 T[4];
T[0] = T2(0, 0); T[1] = T2(1, 0);
T[2] = T2(1, 1); T[3] = T2(0, 1);
Tessellate(hdc, nRecursion, T); // 递归函数
}
void Draw(HDC hdc) // 绘制四边形
{
Point2 point[4];
for (int i = 0; i < 4; i++)
{
point[i] = Proj.PerspectiveProjection2(GridP[i]); // 正投影
}
MoveToEx(hdc, ROUND(point[0].x), ROUND(point[0].y), NULL);
LineTo(hdc, ROUND(point[1].x), ROUND(point[1].y));
LineTo(hdc, ROUND(point[2].x), ROUND(point[2].y));
LineTo(hdc, ROUND(point[3].x), ROUND(point[3].y));
LineTo(hdc, ROUND(point[0].x), ROUND(point[0].y));
}
void DrawControlGrid(HDC hdc) // 绘制控制网格
{
HPEN redPen = CreatePen(PS_SOLID, 4, RGB(180, 0, 0));
HPEN bluePen = CreatePen(PS_SOLID, 2, RGB(0, 0, 180));
HGDIOBJ oldObj = SelectObject(hdc, bluePen);
Point2 P2[4][4];
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
P2[i][j] = Proj.PerspectiveProjection2(P[i][j]); // 正投影
}
}
for (int i = 0; i < 4; i++)
{
MoveToEx(hdc, ROUND(P2[i][0].x), ROUND(P2[i][0].y), NULL);
for (int j = 0; j < 4; j++)
{
LineTo(hdc, ROUND(P2[i][j].x), ROUND(P2[i][j].y));
}
}
for (int j = 0; j < 4; j++)
{
MoveToEx(hdc, ROUND(P2[0][j].x), ROUND(P2[0][j].y), NULL);
for (int i = 0; i < 4; i++)
{
LineTo(hdc, ROUND(P2[i][j].x), ROUND(P2[i][j].y));
}
}
SelectObject(hdc, redPen);
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
Ellipse(hdc, P2[i][j].x + 3, P2[i][j].y + 3, P2[i][j].x - 3, P2[i][j].y - 3);
}
}
SelectObject(hdc, oldObj);
DeletePen(redPen);
DeletePen(bluePen);
}
private:
void Tessellate(HDC hdc, int nRecursion, T2* t) // 细分曲面
{
if (0 == nRecursion)
{
MeshGrid(hdc, t);
Draw(hdc);
return;
}
else
{
T2 midP = (t[0] + t[1] + t[2] + t[3]) / 4.0;
T2 subT[4][4]; // 将定义域一分为四 (四叉树)
// 左下子曲面
subT[0][0] = t[0];
subT[0][1] = T2(midP.u, t[0].v);
subT[0][2] = midP;
subT[0][3] = T2(t[0].u, midP.v);
Tessellate(hdc, nRecursion - 1, subT[0]);
// 右下子曲面
subT[1][0] = T2(midP.u, t[1].v);
subT[1][1] = t[1];
subT[1][2] = T2(t[1].u, midP.v);
subT[1][3] = midP;
Tessellate(hdc, nRecursion - 1, subT[1]);
// 右下子曲面
subT[2][0] = midP;
subT[2][1] = T2(t[2].u, midP.v);
subT[2][2] = t[2];
subT[2][3] = T2(midP.u, t[2].v);
Tessellate(hdc, nRecursion - 1, subT[2]);
// 左上子曲面
subT[3][0] = T2(t[3].u, midP.v);
subT[3][1] = midP;
subT[3][2] = T2(midP.u, t[3].v);
subT[3][3] = t[3];
Tessellate(hdc, nRecursion - 1, subT[3]);
}
}
void MeshGrid(HDC hdc, T2 t[4]) // 计算四边形网格
{
double M[4][4]; // 系数矩阵
M[0][0] = -1.0; M[0][1] = 3.0; M[0][2] = -3.0; M[0][3] = 1.0;
M[1][0] = 3.0; M[1][1] = -6.0; M[1][2] = 3.0; M[1][3] = 0.0;
M[2][0] = -3.0; M[2][1] = 3.0; M[2][2] = 0.0; M[2][3] = 0.0;
M[3][0] = 1.0; M[3][1] = 0.0; M[3][2] = 0.0; M[3][3] = 0.0;
Point3 pTemp[4][4]; // 每次递归 控制点矩阵不变
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
pTemp[i][j] = P[i][j];
}
}
LeftMultiplyMatrix(M, pTemp); // 控制顶点矩阵左乘系数矩阵
TransposeMatrix(M); // 系数转置矩阵
RightMultiplyMatrix(pTemp, M); // 控制顶点矩阵右乘系数矩阵
double u0, u1, u2, u3, v0, v1, v2, v3;// u,v 参数的幂
for (int i = 0; i < 4; i++)
{
u3 = pow(t[i].u, 3.0); u2 = pow(t[i].u, 2.0); u1 = t[i].u; u0 = 1.0;
v3 = pow(t[i].v, 3.0); v2 = pow(t[i].v, 2.0); v1 = t[i].v; v0 = 1.0;
GridP[i] = (u3 * pTemp[0][0] + u2 * pTemp[1][0] + u1 * pTemp[2][0] + u0 * pTemp[3][0]) * v3
+ (u3 * pTemp[0][1] + u2 * pTemp[1][1] + u1 * pTemp[2][1] + u0 * pTemp[3][1]) * v2
+ (u3 * pTemp[0][2] + u2 * pTemp[1][2] + u1 * pTemp[2][2] + u0 * pTemp[3][2]) * v1
+ (u3 * pTemp[0][3] + u2 * pTemp[1][3] + u1 * pTemp[2][3] + u0 * pTemp[3][3]) * v0;
}
}
void LeftMultiplyMatrix(double m[4][4], Point3 p[4][4]) // 控制顶点矩阵左乘系数矩阵
{
Point3 pTemp[4][4];//临时矩阵
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
pTemp[i][j] = m[i][0] * p[0][j] + m[i][1] * p[1][j] + m[i][2] * p[2][j] + m[i][3] * p[3][j];
}
}
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
p[i][j] = pTemp[i][j];
}
}
}
void TransposeMatrix(double M[4][4]) // 转置矩阵
{
double pTemp[4][4]; // 临时矩阵
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
pTemp[j][i] = M[i][j];
}
}
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
M[i][j] = pTemp[i][j];
}
}
}
void RightMultiplyMatrix(Point3 p[4][4], double m[4][4]) // 控制顶点矩阵右乘系数矩阵
{
Point3 pTemp[4][4]; // 临时矩阵
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
pTemp[i][j] = p[i][0] * m[0][j] + p[i][1] * m[1][j] + p[i][2] * m[2][j] + p[i][3] * m[3][j];
}
}
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
p[i][j] = pTemp[i][j];
}
}
}
};
class Patch
{
public:
int ptIndex[4][4]; // 16个控制点索引号
};
class UTeapot // 茶壶类
{
public:
Point3 Vertex[306];
Patch _patch[32];
BicubicBezierPatch Patch; // Bezier曲面
public:
UTeapot() {};
~UTeapot() {};
void ReadVertext()
{
// 茶壶顶点
std::string strVertex = R"( 1 1.40000 0.00000 2.40000
2 1.40000 -0.78400 2.40000
3 0.78000 -1.40000 2.40000
4 0.00000 -1.40000 2.40000
5 1.33750 0.00000 2.53125
6 1.33750 -0.74900 2.53125
7 0.74900 -1.33750 2.53125
8 0.00000 -1.33750 2.53125
9 1.43750 0.00000 2.53125
10 1.43750 -0.80500 2.53125
11 0.80500 -1.43750 2.53125
12 0.00000 -1.43750 2.53125
13 1.50000 0.00000 2.40000
14 1.50000 -0.84000 2.40000
15 0.84000 -1.50000 2.40000
16 0.00000 -1.50000 2.40000
17 -0.78400 -1.40000 2.40000
18 -1.40000 -0.78400 2.40000
19 -1.40000 0.00000 2.40000
20 -0.74900 -1.33750 2.53125
21 -1.33750 -0.74900 2.53125
22 -1.33750 0.00000 2.53125
23 -0.80500 -1.43750 2.53125
24 -1.43750 -0.80500 2.53125
25 -1.43750 0.00000 2.53125
26 -0.84000 -1.50000 2.40000
27 -1.50000 -0.84000 2.40000
28 -1.50000 0.00000 2.40000
29 -1.40000 0.78400 2.40000
30 -0.78400 1.40000 2.40000
31 0.00000 1.40000 2.40000
32 -1.33750 0.74900 2.53125
33 -0.74900 1.33750 2.53125
34 0.00000 1.33750 2.53125
35 -1.43750 0.80500 2.53125
36 -0.80500 1.43750 2.53125
37 0.00000 1.43750 2.53125
38 -1.50000 0.84000 2.40000
39 -0.84000 1.50000 2.40000
40 0.00000 1.50000 2.40000
41 0.78400 1.40000 2.40000
42 1.40000 0.78400 2.40000
43 0.74900 1.33750 2.53125
44 1.33750 0.74900 2.53125
45 0.80500 1.43750 2.53125
46 1.43750 0.80500 2.53125
47 0.84000 1.50000 2.40000
48 1.50000 0.84000 2.40000
49 1.75000 0.00000 1.87500
50 1.75000 -0.98000 1.87500
51 0.98000 -1.75000 1.87500
52 0.00000 -1.75000 1.87500
53 2.00000 0.00000 1.35000
54 2.00000 -1.12000 1.35000
55 1.12000 -2.00000 1.35000
56 0.00000 -2.00000 1.35000
57 2.00000 0.00000 0.90000
58 2.00000 -1.12000 0.90000
59 1.12000 -2.00000 0.90000
60 0.00000 -2.00000 0.90000
61 -0.98000 -1.75000 1.87500
62 -1.75000 -0.98000 1.87500
63 -1.75000 0.00000 1.87500
64 -1.12000 -2.00000 1.35000
65 -2.00000 -1.12000 1.35000
66 -2.00000 0.00000 1.35000
67 -1.12000 -2.00000 0.90000
68 -2.00000 -1.12000 0.90000
69 -2.00000 0.00000 0.90000
70 -1.75000 0.98000 1.87500
71 -0.98000 1.75000 1.87500
72 0.00000 1.75000 1.87500
73 -2.00000 1.12000 1.35000
74 -1.12000 2.00000 1.35000
75 0.00000 2.00000 1.35000
76 -2.00000 1.12000 0.90000
77 -1.12000 2.00000 0.90000
78 0.00000 2.00000 0.90000
79 0.98000 1.75000 1.87500
80 1.75000 0.98000 1.87500
81 1.12000 2.00000 1.35000
82 2.00000 1.12000 1.35000
83 1.12000 2.00000 0.90000
84 2.00000 1.12000 0.90000
85 2.00000 0.00000 0.45000
86 2.00000 -1.12000 0.45000
87 1.12000 -2.00000 0.45000
88 0.00000 -2.00000 0.45000
89 1.50000 0.00000 0.22500
90 1.50000 -0.84000 0.22500
91 0.84000 -1.50000 0.22500
92 0.00000 -1.50000 0.22500
93 1.50000 0.00000 0.15000
94 1.50000 -0.84000 0.15000
95 0.84000 -1.50000 0.15000
96 0.00000 -1.50000 0.15000
97 -1.12000 -2.00000 0.45000
98 -2.00000 -1.12000 0.45000
99 -2.00000 0.00000 0.45000
100 -0.84000 -1.50000 0.22500
101 -1.50000 -0.84000 0.22500
102 -1.50000 0.00000 0.22500
103 -0.84000 -1.50000 0.15000
104 -1.50000 -0.84000 0.15000
105 -1.50000 0.00000 0.15000
106 -2.00000 1.12000 0.45000
107 -1.12000 2.00000 0.45000
108 0.00000 2.00000 0.45000
109 -1.50000 0.84000 0.22500
110 -0.84000 1.50000 0.22500
111 0.00000 1.50000 0.22500
112 -1.50000 0.84000 0.15000
113 -0.84000 1.50000 0.15000
114 0.00000 1.50000 0.15000
115 1.12000 2.00000 0.45000
116 2.00000 1.12000 0.45000
117 0.84000 1.50000 0.22500
118 1.50000 0.84000 0.22500
119 0.84000 1.50000 0.15000
120 1.50000 0.84000 0.15000
121 -1.60000 0.00000 2.02500
122 -1.60000 -0.30000 2.02500
123 -1.50000 -0.30000 2.25000
124 -1.50000 0.00000 2.25000
125 -2.30000 0.00000 2.02500
126 -2.30000 -0.30000 2.02500
127 -2.50000 -0.30000 2.25000
128 -2.50000 0.00000 2.25000
129 -2.70000 0.00000 2.02500
130 -2.70000 -0.30000 2.02500
131 -3.00000 -0.30000 2.25000
132 -3.00000 0.00000 2.25000
133 -2.70000 0.00000 1.80000
134 -2.70000 -0.30000 1.80000
135 -3.00000 -0.30000 1.80000
136 -3.00000 0.00000 1.80000
137 -1.50000 0.30000 2.25000
138 -1.60000 0.30000 2.02500
139 -2.50000 0.30000 2.25000
140 -2.30000 0.30000 2.02500
141 -3.00000 0.30000 2.25000
142 -2.70000 0.30000 2.02500
143 -3.00000 0.30000 1.80000
144 -2.70000 0.30000 1.80000
145 -2.70000 0.00000 1.57500
146 -2.70000 -0.30000 1.57500
147 -3.00000 -0.30000 1.35000
148 -3.00000 0.00000 1.35000
149 -2.50000 0.00000 1.12500
150 -2.50000 -0.30000 1.12500
151 -2.65000 -0.30000 0.93750
152 -2.65000 0.00000 0.93750
153 -2.00000 -0.30000 0.90000
154 -1.90000 -0.30000 0.60000
155 -1.90000 0.00000 0.60000
156 -3.00000 0.30000 1.35000
157 -2.70000 0.30000 1.57500
158 -2.65000 0.30000 0.93750
159 -2.50000 0.30000 1.12500
160 -1.90000 0.30000 0.60000
161 -2.00000 0.30000 0.90000
162 1.70000 0.00000 1.42500
163 1.70000 -0.66000 1.42500
164 1.70000 -0.66000 0.60000
165 1.70000 0.00000 0.60000
166 2.60000 0.00000 1.42500
167 2.60000 -0.66000 1.42500
168 3.10000 -0.66000 0.82500
169 3.10000 0.00000 0.82500
170 2.30000 0.00000 2.10000
171 2.30000 -0.25000 2.10000
172 2.40000 -0.25000 2.02500
173 2.40000 0.00000 2.02500
174 2.70000 0.00000 2.40000
175 2.70000 -0.25000 2.40000
176 3.30000 -0.25000 2.40000
177 3.30000 0.00000 2.40000
178 1.70000 0.66000 0.60000
179 1.70000 0.66000 1.42500
180 3.10000 0.66000 0.82500
181 2.60000 0.66000 1.42500
182 2.40000 0.25000 2.02500
183 2.30000 0.25000 2.10000
184 3.30000 0.25000 2.40000
185 2.70000 0.25000 2.40000
186 2.80000 0.00000 2.47500
187 2.80000 -0.25000 2.47500
188 3.52500 -0.25000 2.49375
189 3.52500 0.00000 2.49375
190 2.90000 0.00000 2.47500
191 2.90000 -0.15000 2.47500
192 3.45000 -0.15000 2.51250
193 3.45000 0.00000 2.51250
194 2.80000 0.00000 2.40000
195 2.80000 -0.15000 2.40000
196 3.20000 -0.15000 2.40000
197 3.20000 0.00000 2.40000
198 3.52500 0.25000 2.49375
199 2.80000 0.25000 2.47500
200 3.45000 0.15000 2.51250
201 2.90000 0.15000 2.47500
202 3.20000 0.15000 2.40000
203 2.80000 0.15000 2.40000
204 0.00000 0.00000 3.15000
205 0.00000 -0.00200 3.15000
206 0.00200 0.00000 3.15000
207 0.80000 0.00000 3.15000
208 0.80000 -0.45000 3.15000
209 0.45000 -0.80000 3.15000
210 0.00000 -0.80000 3.15000
211 0.00000 0.00000 2.85000
212 0.20000 0.00000 2.70000
213 0.20000 -0.11200 2.70000
214 0.11200 -0.20000 2.70000
215 0.00000 -0.20000 2.70000
216 -0.00200 0.00000 3.15000
217 -0.45000 -0.80000 3.15000
218 -0.80000 -0.45000 3.15000
219 -0.80000 0.00000 3.15000
220 -0.11200 -0.20000 2.70000
221 -0.20000 -0.11200 2.70000
222 -0.20000 0.00000 2.70000
223 0.00000 0.00200 3.15000
224 -0.80000 0.45000 3.15000
225 -0.45000 0.80000 3.15000
226 0.00000 0.80000 3.15000
227 -0.20000 0.11200 2.70000
228 -0.11200 0.20000 2.70000
229 0.00000 0.20000 2.70000
230 0.45000 0.80000 3.15000
231 0.80000 0.45000 3.15000
232 0.11200 0.20000 2.70000
233 0.20000 0.11200 2.70000
234 0.40000 0.00000 2.55000
235 0.40000 -0.22400 2.55000
236 0.22400 -0.40000 2.55000
237 0.00000 -0.40000 2.55000
238 1.30000 0.00000 2.55000
239 1.30000 -0.72800 2.55000
240 0.72800 -1.30000 2.55000
241 0.00000 -1.30000 2.55000
242 1.30000 0.00000 2.40000
243 1.30000 -0.72800 2.40000
244 0.72800 -1.30000 2.40000
245 0.00000 -1.30000 2.40000
246 -0.22400 -0.40000 2.55000
247 -0.40000 -0.22400 2.55000
248 -0.40000 0.00000 2.55000
249 -0.72800 -1.30000 2.55000
250 -1.30000 -0.72800 2.55000
251 -1.30000 0.00000 2.55000
252 -0.72800 -1.30000 2.40000
253 -1.30000 -0.72800 2.40000
254 -1.30000 0.00000 2.40000
255 -0.40000 0.22400 2.55000
256 -0.22400 0.40000 2.55000
257 0.00000 0.40000 2.55000
258 -1.30000 0.72800 2.55000
259 -0.72800 1.30000 2.55000
260 0.00000 1.30000 2.55000
261 -1.30000 0.72800 2.40000
262 -0.72800 1.30000 2.40000
263 0.00000 1.30000 2.40000
264 0.22400 0.40000 2.55000
265 0.40000 0.22400 2.55000
266 0.72800 1.30000 2.55000
267 1.30000 0.72800 2.55000
268 0.72800 1.30000 2.40000
269 1.30000 0.72800 2.40000
270 0.00000 0.00000 0.00000
271 1.50000 0.00000 0.15000
272 1.50000 0.84000 0.15000
273 0.84000 1.50000 0.15000
274 0.00000 1.50000 0.15000
275 1.50000 0.00000 0.07500
276 1.50000 0.84000 0.07500
277 0.84000 1.50000 0.07500
278 0.00000 1.50000 0.07500
279 1.42500 0.00000 0.00000
280 1.42500 0.79800 0.00000
281 0.79800 1.42500 0.00000
282 0.00000 1.42500 0.00000
283 -0.84000 1.50000 0.15000
284 -1.50000 0.84000 0.15000
285 -1.50000 0.00000 0.15000
286 -0.84000 1.50000 0.07500
287 -1.50000 0.84000 0.07500
288 -1.50000 0.00000 0.07500
289 -0.79800 1.42500 0.00000
290 -1.42500 0.79800 0.00000
291 -1.42500 0.00000 0.00000
292 -1.50000 -0.84000 0.15000
293 -0.84000 -1.50000 0.15000
294 0.00000 -1.50000 0.15000
295 -1.50000 -0.84000 0.07500
296 -0.84000 -1.50000 0.07500
297 0.00000 -1.50000 0.07500
298 -1.42500 -0.79800 0.00000
299 -0.79800 -1.42500 0.00000
300 0.00000 -1.42500 0.00000
301 0.84000 -1.50000 0.15000
302 1.50000 -0.84000 0.15000
303 0.84000 -1.50000 0.07500
304 1.50000 -0.84000 0.07500
305 0.79800 -1.42500 0.00000
306 1.42500 -0.79800 0.00000)";
std::stringstream stream(strVertex);
char strLine[1024];
int index,res;
double x, y, z;
while (stream.getline(strLine, 1024))
{
res=sscanf(strLine, "%d %lf %lf %lf", &index, &x, &y, &z);
if (res > 0)
{
Vertex[index-1] = Point3(x,y,z);
}
}
}
void ReadPath()
{
// 茶壶顶点索引
std::string indices = R"( 1 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
2 4 17 18 19 8 20 21 22 12 23 24 25 16 26 27 28
3 19 29 30 31 22 32 33 34 25 35 36 37 28 38 39 40
4 31 41 42 1 34 43 44 5 37 45 46 9 40 47 48 13
5 13 14 15 16 49 50 51 52 53 54 55 56 57 58 59 60
6 16 26 27 28 52 61 62 63 56 64 65 66 60 67 68 69
7 28 38 39 40 63 70 71 72 66 73 74 75 69 76 77 78
8 40 47 48 13 72 79 80 49 75 81 82 53 78 83 84 57
9 57 58 59 60 85 86 87 88 89 90 91 92 93 94 95 96
10 60 67 68 69 88 97 98 99 92 100 101 102 96 103 104 105
11 69 76 77 78 99 106 107 108 102 109 110 111 105 112 113 114
12 78 83 84 57 108 115 116 85 111 117 118 89 114 119 120 93
13 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
14 124 137 138 121 128 139 140 125 132 141 142 129 136 143 144 133
15 133 134 135 136 145 146 147 148 149 150 151 152 69 153 154 155
16 136 143 144 133 148 156 157 145 152 158 159 149 155 160 161 69
17 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
18 165 178 179 162 169 180 181 166 173 182 183 170 177 184 185 174
19 174 175 176 177 186 187 188 189 190 191 192 193 194 195 196 197
20 177 184 185 174 189 198 199 186 193 200 201 190 197 202 203 194
21 204 204 204 204 207 208 209 210 211 211 211 211 212 213 214 215
22 204 204 204 204 210 217 218 219 211 211 211 211 215 220 221 222
23 204 204 204 204 219 224 225 226 211 211 211 211 222 227 228 229
24 204 204 204 204 226 230 231 207 211 211 211 211 229 232 233 212
25 212 213 214 215 234 235 236 237 238 239 240 241 242 243 244 245
26 215 220 221 222 237 246 247 248 241 249 250 251 245 252 253 254
27 222 227 228 229 248 255 256 257 251 258 259 260 254 261 262 263
28 229 232 233 212 257 264 265 234 260 266 267 238 263 268 269 242
29 270 270 270 270 279 280 281 282 275 276 277 278 93 120 119 114
30 270 270 270 270 282 289 290 291 278 286 287 288 114 113 112 105
31 270 270 270 270 291 298 299 300 288 295 296 297 105 104 103 96
32 270 270 270 270 300 305 306 279 297 303 304 275 96 95 94 93)";
std::stringstream stream(indices);
char strLine[1024];
int index, res;
int a1, a2, a3, a4, b1, b2, b3, b4, c1, c2, c3, c4, d1, d2, d3, d4;
while (stream.getline(strLine, 1024))
{
res = sscanf(strLine, "%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d", &index,
&a1, &a2, &a3, &a4, &b1, &b2, &b3, &b4, &c1, &c2, &c3, &c4,&d1, &d2, &d3, &d4);
if (res > 0)
{
_patch[index - 1].ptIndex[0][0] = a1; _patch[index - 1].ptIndex[0][1] = a2; _patch[index - 1].ptIndex[0][2] = a3; _patch[index - 1].ptIndex[0][3] = a4;
_patch[index - 1].ptIndex[1][0] = b1; _patch[index - 1].ptIndex[1][1] = b2; _patch[index - 1].ptIndex[1][2] = b3; _patch[index - 1].ptIndex[1][3] = b4;
_patch[index - 1].ptIndex[2][0] = c1; _patch[index - 1].ptIndex[2][1] = c2; _patch[index - 1].ptIndex[2][2] = c3; _patch[index - 1].ptIndex[2][3] = c4;
_patch[index - 1].ptIndex[3][0] = d1; _patch[index - 1].ptIndex[3][1] = d2; _patch[index - 1].ptIndex[3][2] = d3; _patch[index - 1].ptIndex[3][3] = d4;
}
}
}
void DrawParts(HDC hdc,int nPatchStart,int nPatchEnd) // 绘制茶壶
{
Point3 P3[4][4]; // 曲面控制点
int n=3;//递归深度
for (int nPatch = nPatchStart; nPatch < nPatchEnd; nPatch++)
{
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
P3[i][j] = Vertex[_patch[nPatch].ptIndex[i][j] - 1]; // 数组从0开始 -1
}
}
Patch.ReadControlPoint(P3); // 读取控制点
Patch.DrawCurvedPatch(hdc,n); // 绘制曲面
//Patch.DrawControlGrid(hdc); // 绘制控制网格
}
}
};
static bool m_Play = false;
static wchar_t* m_str = L"左键旋转";
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hdc;
RECT rect;
static double alpha;
static double beta;
switch (uMsg)
{
case WM_TIMER:
alpha += 1;
beta += 1;
InvalidateRect(hwnd, NULL, FALSE);
return 0;
case WM_LBUTTONDOWN:
m_Play = !m_Play;
if (m_Play)
{
SetTimer(hwnd, 0, 0, NULL);
}
else
{
KillTimer(hwnd, 0);
}
return 0;
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), &rect, 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);
// 绘制图形
Transform3 transform;
UTeapot uteapot;
uteapot.ReadVertext();
uteapot.ReadPath();
// 放大茶壶
transform.SetMatrix(uteapot.Vertex,306);
int scale = 150;
transform.Scale(scale, scale, scale);
if (m_Play)
{
transform.RotateX(alpha);
transform.RotateY(beta);
}
// 绘制茶壶
uteapot.DrawParts(memDC,0, 12); // 绘制 Body
uteapot.DrawParts(memDC,12,16); // 绘制 Handle
uteapot.DrawParts(memDC,16,20); // 绘制 Spout
uteapot.DrawParts(memDC,20,28); // 绘制 Lid
uteapot.DrawParts(memDC,28,32); // 绘制 Bottom
// 内存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;
}