参考:https://www.bilibili.com/video/BV1cv4y1S7RL
球体用了8个双三次bezier曲面。 双三次Beizer曲面算法 详上一篇
球体八卦限,每个象限内一个曲面。共八个曲面
第一卦限坐标
完整代码:
// 27-Bezier球体算法
// 参考 https://www.bilibili.com/video/BV1cv4y1S7RL
#define UNICODE
#include <Windows.h>
#include <Windowsx.h>
#include <math.h>
#define WINDOW_TEXT L"27-Bezier球体算法"
#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 Sphere // 球
{
public:
Point3 V[62]; // 球控制点
Patch P[8]; // 球曲面
BicubicBezierPatch Patch; // Bezier曲面
public:
Sphere() {};
~Sphere () {};
void ReadPoint() // 读取球控制点
{
const double m = 0.5523;// 魔术常数 (绘制圆)
// 第1卦限控制点
V[ 0].x = 0; V[ 0].y = 1; V[ 0].z = 0;
V[ 1].x = 0; V[ 1].y = 1; V[ 1].z = m;
V[ 2].x = 0; V[ 2].y = m; V[ 2].z = 1;
V[ 3].x = 0; V[ 3].y = 0; V[ 3].z = 1;
V[ 4].x = m*m; V[ 4].y = 1; V[ 4].z = m;
V[ 5].x = m; V[ 5].y = m; V[ 5].z = 1;
V[ 6].x = m; V[ 6].y = 0; V[ 6].z = 1;
V[ 7].x = m; V[ 7].y = 1; V[ 7].z = m*m;
V[ 8].x = 1; V[ 8].y = m; V[ 8].z = m;
V[ 9].x = 1; V[ 9].y = 0; V[ 9].z = m;
V[10].x = m; V[10].y = 1; V[10].z = 0;
V[11].x = 1; V[11].y = m; V[11].z = 0;
V[12].x = 1; V[12].y = 0; V[12].z = 0;
// 第2卦限控制点
V[13].x = m; V[13].y = 1; V[13].z = -m*m;
V[14].x = 1; V[14].y = m; V[14].z = -m;
V[15].x = 1; V[15].y = 0; V[15].z = -m;
V[16].x = m*m; V[16].y = 1; V[16].z = -m;
V[17].x = m; V[17].y = m; V[17].z = -1;
V[18].x = m; V[18].y = 0; V[18].z = -1;
V[19].x = 0; V[19].y = 1; V[19].z = -m;
V[20].x = 0; V[20].y = m; V[20].z = -1;
V[21].x = 0; V[21].y = 0; V[21].z = -1;
// 第3卦限控制点
V[22].x = -m*m; V[22].y = 1; V[22].z = -m;
V[23].x = -m; V[23].y = m; V[23].z = -1;
V[24].x = -m; V[24].y = 0; V[24].z = -1;
V[25].x = -m; V[25].y = 1; V[25].z = -m*m;
V[26].x = -1; V[26].y = m; V[26].z = -m;
V[27].x = -1; V[27].y = 0; V[27].z = -m;
V[28].x = -m; V[28].y = 1; V[28].z = 0;
V[29].x = -1; V[29].y = m; V[29].z = 0;
V[30].x = -1; V[30].y = 0; V[30].z = 0;
// 第4卦限控制点
V[31].x = -m; V[31].y = 1; V[31].z = m * m;
V[32].x = -1; V[32].y = m; V[32].z = m;
V[33].x = -1; V[33].y = 0; V[33].z = m;
V[34].x = -m * m; V[34].y = 1; V[34].z = m;
V[35].x = -m; V[35].y = m; V[35].z = 1;
V[36].x = -m; V[36].y = 0; V[36].z = 1;
// 第5卦限控制点
V[37].x = 0; V[37].y = -m; V[37].z = 1;
V[38].x = 0; V[38].y = -1; V[38].z = m;
V[39].x = m; V[39].y = -m; V[39].z = 1;
V[40].x = m*m; V[40].y = -1; V[40].z = m;
V[41].x = 1; V[41].y = -m; V[41].z = m;
V[42].x = m; V[42].y = -1; V[42].z = m*m;
V[43].x = 1; V[43].y = -m; V[43].z = 0;
V[44].x = m; V[44].y = -1; V[44].z = 0;
// 第6卦限控制点
V[45].x = 1; V[45].y = -m; V[45].z = -m;
V[46].x = m; V[46].y = -1; V[46].z = -m*m;
V[47].x = m; V[47].y = -m; V[47].z = -1;
V[48].x = m*m; V[48].y = -1; V[48].z = -m;
V[49].x = 0; V[49].y = -m; V[49].z = -1;
V[50].x = 0; V[50].y = -1; V[50].z = -m;
// 第7卦限控制点
V[51].x = -m; V[51].y = -m; V[51].z = -1;
V[52].x = -m * m; V[52].y = -1; V[52].z = -m;
V[53].x = -1; V[53].y = -m; V[53].z = -m;
V[54].x = -m; V[54].y = -1; V[54].z = -m * m;
V[55].x = -1; V[55].y = -m; V[55].z = 0;
V[56].x = -m; V[56].y = -1; V[56].z = 0;
// 第8卦限控制点
V[57].x = -1; V[57].y = -m; V[57].z = m;
V[58].x = -m; V[58].y = -1; V[58].z = m*m;
V[59].x = -m; V[59].y = -m; V[59].z = 1;
V[60].x = -m*m; V[60].y = -1; V[60].z = m;
V[61].x = 0; V[61].y = -1; V[61].z = 0;
}
void ReadPath() // 读取球面曲面片
{
// 第1卦限面片
P[0].ptIndex[0][0] = 3; P[0].ptIndex[0][1] = 2; P[0].ptIndex[0][2] = 1; P[0].ptIndex[0][3] = 0;
P[0].ptIndex[1][0] = 6; P[0].ptIndex[1][1] = 5; P[0].ptIndex[1][2] = 4; P[0].ptIndex[1][3] = 0;
P[0].ptIndex[2][0] = 9; P[0].ptIndex[2][1] = 8; P[0].ptIndex[2][2] = 7; P[0].ptIndex[2][3] = 0;
P[0].ptIndex[3][0] = 12; P[0].ptIndex[3][1] = 11; P[0].ptIndex[3][2] = 10; P[0].ptIndex[3][3] = 0;
// 第2卦限面片
P[1].ptIndex[0][0] = 12; P[1].ptIndex[0][1] = 11; P[1].ptIndex[0][2] = 10; P[1].ptIndex[0][3] = 0;
P[1].ptIndex[1][0] = 15; P[1].ptIndex[1][1] = 14; P[1].ptIndex[1][2] = 13; P[1].ptIndex[1][3] = 0;
P[1].ptIndex[2][0] = 18; P[1].ptIndex[2][1] = 17; P[1].ptIndex[2][2] = 16; P[1].ptIndex[2][3] = 0;
P[1].ptIndex[3][0] = 21; P[1].ptIndex[3][1] = 20; P[1].ptIndex[3][2] = 19; P[1].ptIndex[3][3] = 0;
// 第3卦限面片
P[2].ptIndex[0][0] = 21; P[2].ptIndex[0][1] = 20; P[2].ptIndex[0][2] = 19; P[2].ptIndex[0][3] = 0;
P[2].ptIndex[1][0] = 24; P[2].ptIndex[1][1] = 23; P[2].ptIndex[1][2] = 22; P[2].ptIndex[1][3] = 0;
P[2].ptIndex[2][0] = 27; P[2].ptIndex[2][1] = 26; P[2].ptIndex[2][2] = 25; P[2].ptIndex[2][3] = 0;
P[2].ptIndex[3][0] = 30; P[2].ptIndex[3][1] = 29; P[2].ptIndex[3][2] = 28; P[2].ptIndex[3][3] = 0;
// 第4卦限面片
P[3].ptIndex[0][0] = 30; P[3].ptIndex[0][1] = 29; P[3].ptIndex[0][2] = 28; P[3].ptIndex[0][3] = 0;
P[3].ptIndex[1][0] = 33; P[3].ptIndex[1][1] = 32; P[3].ptIndex[1][2] = 31; P[3].ptIndex[1][3] = 0;
P[3].ptIndex[2][0] = 36; P[3].ptIndex[2][1] = 35; P[3].ptIndex[2][2] = 34; P[3].ptIndex[2][3] = 0;
P[3].ptIndex[3][0] = 3; P[3].ptIndex[3][1] = 2; P[3].ptIndex[3][2] = 1; P[3].ptIndex[3][3] = 0;
// 第5卦限面片
P[4].ptIndex[0][0] = 61; P[4].ptIndex[0][1] = 38; P[4].ptIndex[0][2] = 37; P[4].ptIndex[0][3] = 3;
P[4].ptIndex[1][0] = 61; P[4].ptIndex[1][1] = 40; P[4].ptIndex[1][2] = 39; P[4].ptIndex[1][3] = 6;
P[4].ptIndex[2][0] = 61; P[4].ptIndex[2][1] = 42; P[4].ptIndex[2][2] = 41; P[4].ptIndex[2][3] = 9;
P[4].ptIndex[3][0] = 61; P[4].ptIndex[3][1] = 44; P[4].ptIndex[3][2] = 43; P[4].ptIndex[3][3] = 12;
// 第6卦限面片
P[5].ptIndex[0][0] = 61; P[5].ptIndex[0][1] = 44; P[5].ptIndex[0][2] = 43; P[5].ptIndex[0][3] = 12;
P[5].ptIndex[1][0] = 61; P[5].ptIndex[1][1] = 46; P[5].ptIndex[1][2] = 45; P[5].ptIndex[1][3] = 15;
P[5].ptIndex[2][0] = 61; P[5].ptIndex[2][1] = 48; P[5].ptIndex[2][2] = 47; P[5].ptIndex[2][3] = 18;
P[5].ptIndex[3][0] = 61; P[5].ptIndex[3][1] = 50; P[5].ptIndex[3][2] = 49; P[5].ptIndex[3][3] = 21;
// 第7卦限面片
P[6].ptIndex[0][0] = 61; P[6].ptIndex[0][1] = 50; P[6].ptIndex[0][2] = 49; P[6].ptIndex[0][3] = 21;
P[6].ptIndex[1][0] = 61; P[6].ptIndex[1][1] = 52; P[6].ptIndex[1][2] = 51; P[6].ptIndex[1][3] = 24;
P[6].ptIndex[2][0] = 61; P[6].ptIndex[2][1] = 54; P[6].ptIndex[2][2] = 53; P[6].ptIndex[2][3] = 27;
P[6].ptIndex[3][0] = 61; P[6].ptIndex[3][1] = 56; P[6].ptIndex[3][2] = 55; P[6].ptIndex[3][3] = 30;
// 第8卦限面片
P[7].ptIndex[0][0] = 61; P[7].ptIndex[0][1] = 56; P[7].ptIndex[0][2] = 55; P[7].ptIndex[0][3] = 30;
P[7].ptIndex[1][0] = 61; P[7].ptIndex[1][1] = 58; P[7].ptIndex[1][2] = 57; P[7].ptIndex[1][3] = 33;
P[7].ptIndex[2][0] = 61; P[7].ptIndex[2][1] = 60; P[7].ptIndex[2][2] = 59; P[7].ptIndex[2][3] = 36;
P[7].ptIndex[3][0] = 61; P[7].ptIndex[3][1] = 38; P[7].ptIndex[3][2] = 37; P[7].ptIndex[3][3] = 3;
}
void Draw(HDC hdc) // 绘制
{
Point3 point[4][4];
int n = 3;//递归深度
for (int nPatch = 0; nPatch < 8; nPatch++)
{
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
point[i][j] = V[P[nPatch].ptIndex[i][j]];
}
}
Patch.ReadControlPoint(point); // 读取控制点
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);
// 绘制图形
{
Sphere sphere;
Transform3 transform;
sphere.ReadPoint();
sphere.ReadPath();
transform.SetMatrix(sphere.V,62); // 设置顶点
double scale = 300;
transform.Scale(scale, scale, scale); // 放大顶点坐标
if (m_Play) // 旋转动画
{
transform.RotateX(alpha);
transform.RotateY(beta);
}
sphere.Draw(memDC);
}
// 内存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;
}