隐线算法
一、实验目的
(1)掌握凸多边形的隐线算法;
二、实验要求
(1)三维坐标系的原点位于屏幕中心,X轴水平向右,X轴垂直向上,Z轴垂直于坐标屏幕,指向屏幕外。
(2)使用实验五的绘制的动态正四面体线框图形为基础,通过隐线算法对正四面体的线框图进行消隐处理。
(3)使用点表和面表的双表结构,重新构造正四面体的数据
ps:本实验运行环境为Visual Studio2022
前面的实验可以搜直线扫描转换(中点Bresenham扫描转换算法),那个博主写的很全,代码也都在。
三、实验过程
- 打开实验四MFC工程文件(3D扫描转换转换)
- 在CP3Edge.h/cpp中添加表面类类
图3.2.1 在CP3Edge.h头文件中添加表面类
图3.2.2 在CP3Edge.cpp源文件中添加边表类
(3)建立正四面体的点表和边表
表3-1 正四面体的面表
图3.3.1 在_.view.h头文件中添加成员函数
图3.3.2 在_.view.h头文件中存储成员变量
图3.3.3 在_view.cpp实现并调用面表函数
(4)通过隐线算法绘制正四面体的消隐线框图
图3.4.1 建立修Vector.h头文件
图3.4.2 建立Vector.cpp源文件
隐线算法中用到了三维矢量的运算,这里通过建立三维矢量类CVector来实现。矢量类中包含存储三个分量的成员变量,以及实现基本矢量运算的成员函数,包点乘、叉乘和单位化等运算。
(5)正四面体的消隐线框图绘制
图3.5.1 修改_view.cpp中Ondraw函数
四、实验结果
消隐算法
五、完整代码
CP3Edge.h
#pragma once
class CP3
{
public:
CP3();
virtual ~CP3();
CP3(double, double, double);
public:
double x;
double y;
double z;
double w;
};
//边表类
class CEdge
{
public:
CEdge();
virtual ~CEdge();
void SetPointsIndex(int, int);
public:
int Start;
int End;
};
//平面类
class CFace
{
public:
CFace();
virtual ~CFace();
void SetPoints(int *points, int num);
public:
int Num;
int *Points;
};
CP3Edge.cpp
#include"pch.h"
#include"CP3Edge.h"
CP3::CP3()
{
x = 0.0;
y = 0.0;
z = 0.0;
w = 1.0;
}
CP3::~CP3()
{}
CP3::CP3(double x, double y, double z) {
this->x = x;
this->y = y;
this->z = z;
this->w = 1.0;
}
//边表类
CEdge::CEdge() {
Start = 0;
End = 0;
}
CEdge::~CEdge()
{}
void CEdge::SetPointsIndex(int start, int end) {
Start = start;
End = end;
}
// 表面类
CFace::CFace(){
Num = 0;
Points = NULL;
}
CFace::~CFace()
{
if (Points)
{
delete[] Points;
Points=NULL;
}
}
void CFace::SetPoints(int *points, int num)
{
Num = num;
if(Points)
{
delete[] Points;
Points = NULL;
}
Points = new int[Num];
for (int i = 0; i < Num; i++)
Points[i] = points[i];
}
Vector.h
#pragma once
#include "CP3Edge.h"
class CVector {
public:
CVector();
virtual ~CVector();
CVector(CP3); // 假设CP3是一个已经定义的类或结构体
CVector(CP3,CP3); // 构造函数,用于计算两个点之间的向量
double Mold(); // 矢量的模
CVector Unit(); // 单位矢量
// 运算符重载
friend CVector operator +(const CVector &, const CVector &);
friend CVector operator -(const CVector &, const CVector &);
friend CVector operator *(const CVector &, double);
friend CVector operator /(const CVector &, double);
friend CVector operator +=(const CVector &, const CVector &);
friend CVector operator-=(const CVector &, const CVector &);
friend CVector operator*=(const CVector &, const CVector &);
friend CVector operator/=(const CVector &, double);
friend double Dot(CVector &,CVector &); // 矢量点积
friend CVector operator *(CVector &,CVector &); // 矢量叉积
public:
double x, y, z;
};
Vector.cpp
#include "pch.h"
#include "Vector.h"
#include "math.h"
CVector::CVector()
{
x = 0.0;
y = 0.0;
z = 0.0;
}
CVector::~CVector()
{}
CVector::CVector(CP3 p)
{
x = p.x;
y = p.y;
z = p.z;
}
CVector::CVector(CP3 p1,CP3 p2)
{
x = p2.x-p1.x;
y = p2.y-p1.y;
z = p2.z-p1.z;
}
CVector CVector::Unit()
{
CVector vector;
double product = sqrt(x * x + y * y + z * z);
if (fabs(product) < 1e-5)
product = 1.0;
vector.x = x / product;
vector.y = y / product;
vector.z = z / product;
return vector;
}
double CVector::Mold()
{
double product = sqrt(x * x + y * y + z * z);
return product;
}
//矢量的和
CVector operator +(CVector &v1, CVector &v2)
{
CVector vector;
vector.x = v1.x + v2.x;
vector.y = v1.y + v2.y;
vector.z = v1.z + v2.z;
return vector;
}
//矢量的差
CVector operator -(CVector &v1, CVector &v2)
{
CVector vector;
vector.x = v1.x - v2.x;
vector.y = v1.y - v2.y;
vector.z = v1.z - v2.z;
return vector;
}
//矢量和常量的积
CVector operator *(CVector &v,double k)
{
CVector vector;
vector.x = v.x * k;
vector.y = v.y * k;
vector.z = v.z * k;
return vector;
}
//矢量数除
CVector operator /(CVector& v, double k)
{
if (fabs(k) < 1e-6)
k = 1.0;
CVector vector;
vector.x = v.x / k;
vector.y = v.y / k;
vector.z = v.z / k;
return vector;
}
//+=运算符重载
CVector operator +=(CVector& v1, CVector& v2)
{
v1.x = v1.x + v2.x;
v1.y = v1.y + v2.y;
v1.z = v1.z + v2.z;
return v1;
}
//-=运算符重载
CVector operator -=(CVector& v1, CVector& v2)
{
v1.x = v1.x - v2.x;
v1.y = v1.y - v2.y;
v1.z = v1.z - v2.z;
return v1;
}
//*=运算符重载
CVector operator *=(CVector& v1, CVector& v2)
{
v1.x = v1.x * v2.x;
v1.y = v1.y * v2.y;
v1.z = v1.z * v2.z;
return v1;
}
///=运算符重载
CVector operator /=(CVector& v1, double k)
{
v1.x = v1.x / k;
v1.y = v1.y / k;
v1.z = v1.z / k;
return v1;
}
//矢量的点积
double Dot(CVector& v1, CVector& v2)
{
return(v1.x * v2.x + v1.y * v2.y + v1.z * v2.z);
}
//矢量的叉积
CVector operator*(CVector& v1, CVector& v2)
{
CVector vector;
vector.x = v1.y * v2.z - v1.z * v2.y;
vector.y = v1.z * v2.x - v1.x * v2.z;
vector.z = v1.x * v2.y - v1.y * v2.x;
return vector;
}
MFCApplication5view.h
#include "CP3Edge.h"
// MFCApplication5View.h: CMFCApplication5View 类的接口
//
#pragma once
class CMFCApplication5View : public CView
{
protected: // 仅从序列化创建
CMFCApplication5View() noexcept;
DECLARE_DYNCREATE(CMFCApplication5View)
// 特性
public:
CMFCApplication5Doc* GetDocument() const;
// 操作
public:
void BuildPointEdge();
void BuildPointFace();
// 重写
public:
virtual void OnDraw(CDC* pDC); // 重写以绘制该视图
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
protected:
virtual BOOL OnPreparePrinting(CPrintInfo* pInfo);
virtual void OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo);
virtual void OnEndPrinting(CDC* pDC, CPrintInfo* pInfo);
// 实现
public:
virtual ~CMFCApplication5View();
#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endif
protected:
CP3 P[4];
CEdge E[6];
CFace F[4];
// 生成的消息映射函数
protected:
afx_msg void OnFilePrintPreview();
afx_msg void OnRButtonUp(UINT nFlags, CPoint point);
afx_msg void OnContextMenu(CWnd* pWnd, CPoint point);
DECLARE_MESSAGE_MAP()
};
#ifndef _DEBUG // MFCApplication5View.cpp 中的调试版本
inline CMFCApplication5Doc* CMFCApplication5View::GetDocument() const
{ return reinterpret_cast<CMFCApplication5Doc*>(m_pDocument); }
#endif
MFCApplication5view.cpp
// MFCApplication5View.cpp: CMFCApplication5View 类的实现
//
#include "pch.h"
#include "framework.h"
#include "Line.h"
#include "CTrans3D.h"
#include "Vector.h"
// SHARED_HANDLERS 可以在实现预览、缩略图和搜索筛选器句柄的
// ATL 项目中进行定义,并允许与该项目共享文档代码。
#ifndef SHARED_HANDLERS
#include "MFCApplication5.h"
#endif
#include "MFCApplication5Doc.h"
#include "MFCApplication5View.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// CMFCApplication5View
IMPLEMENT_DYNCREATE(CMFCApplication5View, CView)
BEGIN_MESSAGE_MAP(CMFCApplication5View, CView)
// 标准打印命令
ON_COMMAND(ID_FILE_PRINT, &CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_DIRECT, &CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_PREVIEW, &CMFCApplication5View::OnFilePrintPreview)
ON_WM_CONTEXTMENU()
ON_WM_RBUTTONUP()
END_MESSAGE_MAP()
//CMFCApplication5View 构造/析构
CMFCApplication5View::CMFCApplication5View() noexcept
{
// TODO: 在此处添加构造代码
BuildPointFace();
}
void CMFCApplication5View::BuildPointFace()
{
double d = 400;
P[0].x = d / 2; P[0].y = d / 2; P[0].z = d / 2;
P[1].x = d / 2; P[1].y = -d / 2; P[1].z = -d / 2;
P[2].x = -d / 2; P[2].y = -d / 2; P[2].z = d / 2;
P[3].x = -d / 2; P[3].y = d / 2; P[3].z = -d / 2;
E[0].SetPointsIndex(0, 1);
E[1].SetPointsIndex(0, 2);
E[2].SetPointsIndex(0, 3);
E[3].SetPointsIndex(1, 2);
E[4].SetPointsIndex(1, 3);
E[5].SetPointsIndex(2, 3);
//面表
int points[3];
points[0] = 1, points[1] = 2, points[2] = 3;
F[0].SetPoints(points, 3);
points[0] = 0, points[1] = 3, points[2] = 2;
F[1].SetPoints(points, 3);
points[0] = 0, points[1] = 1, points[2] = 3;
F[2].SetPoints(points, 3);
points[0] = 0, points[1] = 2, points[2] = 1;
F[3].SetPoints(points, 3);
}
CMFCApplication5View::~CMFCApplication5View()
{
}
/*
// CTTestView 构造/析构
CTTestView::CTTestView() noexcept
{
// TODO: 在此处添加构造代码
BuildPointEdge();
}
void CTTestView::BuildPointEdge()
{
double d = 400;
P[0].x = d / 2; P[0].y = d / 2; P[0].z = d / 2;
P[1].x = d / 2; P[1].y = -d / 2; P[1].z = -d / 2;
P[2].x = -d / 2; P[2].y = -d / 2; P[2].z = d / 2;
P[3].x = -d / 2; P[3].y = d / 2; P[3].z = -d / 2;
E[0].SetPointsIndex(0, 1);
E[1].SetPointsIndex(0, 2);
E[2].SetPointsIndex(0, 3);
E[3].SetPointsIndex(1, 2);
E[4].SetPointsIndex(1, 3);
E[5].SetPointsIndex(2, 3);
}
CTTestView::~CTTestView()
{
}
*/
BOOL CMFCApplication5View::PreCreateWindow(CREATESTRUCT& cs)
{
// TODO: 在此处通过修改
// CREATESTRUCT cs 来修改窗口类或样式
return CView::PreCreateWindow(cs);
}
// CMFCApplication5View 绘图
void CMFCApplication5View::OnDraw(CDC* pDC)
{
CMFCApplication5Doc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
// TODO: 在此处为本机数据添加绘制代码
//1.设置坐标系
CRect rect;
GetClientRect(&rect);
pDC->SetMapMode(MM_ANISOTROPIC);
pDC->SetWindowExt(rect.Width(), rect.Height());
pDC->SetViewportExt(rect.Width(), -rect.Height());
pDC->SetViewportOrg(rect.Width() / 2, rect.Height() / 2);
// rect.OffsetRect(-rect.Width() / 2, -rect.Height() / 2);
//双缓冲机制绘制
CDC MemDC;
CBitmap NewBitmap, * pOldBitmap;
MemDC.CreateCompatibleDC(pDC);
NewBitmap.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());
pOldBitmap = MemDC.SelectObject(&NewBitmap);
MemDC.FillSolidRect(rect, pDC->GetBkColor());
MemDC.SetMapMode(MM_ANISOTROPIC);
MemDC.SetWindowExt(rect.Width(), rect.Height());
MemDC.SetViewportExt(rect.Width(), -rect.Height());
MemDC.SetViewportOrg(rect.Width() / 2, rect.Height() / 2);
CLine* line = new CLine;
line->SetLineColor(RGB(188, 143, 143));
line->MoveTo(CP2(-rect.Width() / 2, 0));
line->LineTo(CP2(rect.Width() / 2, 0), &MemDC);
line->MoveTo(CP2(0, -rect.Height() / 2));
line->LineTo(CP2(0, rect.Height() / 2), &MemDC);
//3.旋转、缩放、正交投影变换
CTrans3D tans;
tans.SetPoints(P, 4);
static float s = 1.0;
static float step = 0.01f;
if (s >= 2.0 || s <= 0.5)
step = -step;
s += step;
tans.Scale(s, s, s);
static float theta = 0.0;
theta += 1.0;
if (theta >= 360.0)
theta = 0.0;
tans.RotateY(theta);
tans.ProjXOY();
//4.绘制四面体的消隐线框图
CVector VS(CP3(0, 0, 1));
for (int i = 0; i < 4; i++)
{
CVector V12(tans.m_p3Points[F[i].Points[0]], tans.m_p3Points[F[i].Points[1]]);
CVector V23(tans.m_p3Points[F[i].Points[0]], tans.m_p3Points[F[i].Points[2]]);
CVector Normal = V12 * V23;
if (Dot(VS, Normal.Unit()) >= 0)
{
line->SetLineColor(RGB(0, 255, 0));
line->MoveTo(tans.m_p2Screen[F[i].Points[0]]);
line->LineTo(tans.m_p2Screen[F[i].Points[1]],&MemDC);
line->LineTo(tans.m_p2Screen[F[i].Points[2]], &MemDC);
line->LineTo(tans.m_p2Screen[F[i].Points[0]], &MemDC);
}
}
delete line;
//5.将内存位图拷贝到屏幕
pDC->BitBlt(-rect.Width() / 2, -rect.Height() / 2, rect.Width(), rect.Height(), &MemDC, -rect.Width() / 2, -rect.Height() / 2, SRCCOPY);
MemDC.SelectObject(pOldBitmap);
NewBitmap.DeleteObject();
Invalidate(FALSE);
}
// CMFCApplication5View 打印
void CMFCApplication5View::OnFilePrintPreview()
{
#ifndef SHARED_HANDLERS
AFXPrintPreview(this);
#endif
}
BOOL CMFCApplication5View::OnPreparePrinting(CPrintInfo* pInfo)
{
// 默认准备
return DoPreparePrinting(pInfo);
}
void CMFCApplication5View::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
{
// TODO: 添加额外的打印前进行的初始化过程
}
void CMFCApplication5View::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
{
// TODO: 添加打印后进行的清理过程
}
void CMFCApplication5View::OnRButtonUp(UINT /* nFlags */, CPoint point)
{
ClientToScreen(&point);
OnContextMenu(this, point);
}
void CMFCApplication5View::OnContextMenu(CWnd* /* pWnd */, CPoint point)
{
#ifndef SHARED_HANDLERS
theApp.GetContextMenuManager()->ShowPopupMenu(IDR_POPUP_EDIT, point.x, point.y, this, TRUE);
#endif
}
// CMFCApplication5View 诊断
#ifdef _DEBUG
void CMFCApplication5View::AssertValid() const
{
CView::AssertValid();
}
void CMFCApplication5View::Dump(CDumpContext& dc) const
{
CView::Dump(dc);
}
CMFCApplication5Doc* CMFCApplication5View::GetDocument() const // 非调试版本是内联的
{
ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CMFCApplication5Doc)));
return (CMFCApplication5Doc*)m_pDocument;
}
#endif //_DEBUG
// CMFCApplication5View 消息处理程序