- VisualStudio2022插件的安装及使用-编程手把手系列文章
- pprof-在现网场景怎么用
- C#实现的下拉多选框,下拉多选树,多级节点
- 【学习笔记】基础数据结构:猫树
在上一小节中,我们完成了对BMPImage类的构建,成功实现了我们这个小小引擎的图像输出功能.
你已经完成了图像输出了,接着就开始路径追踪吧。。。 开个玩笑XD 对于曾经学习过一些图形学经典教材的人来说,下一步应当开始着手于画线算法了,但对于本文来说,肯定是要走一些不走寻常路的.
所谓万事开头难,我决定先打好地基。编写一个鲁棒性,扩展性还不错的向量类.
由于我们不打算借助外部库,所以,适当的设计是必要的。在这里,我打算借鉴一下pbrt-v4的设计。 也就是利用模板基类来定制我们的数据结构.
这么做的好处是,我们可以按照元素数量划分父类.
构建基本的数据结构,包括点和向量.
/**
* \brief every element's parent class who has two parameter
* \tparam Child a template class contains one template parameter
* \tparam T defined by the child's type of template parameter
*/
template <template <typename> class Child, typename T> class Tuple2
{
public:
static constexpr int nDimensions = 2;
Tuple2() = default;
Tuple2(T x, T y) : x(x), y(y)
{
}
Tuple2(Child<T> c)
{
x = c.x;
y = c.y;
}
Child<T>& operator=(Child<T> c)
{
x = c.x;
y = c.y;
return static_cast<Child<T>&>(*this);
}
template <typename U> auto operator+(Child<U> c) const -> Child<decltype(T{} + U{})>
{
return {x + c.x, y + c.y};
}
template <typename U> Child<T>& operator+=(Child<U> c)
{
x += c.x;
y += c.y;
return static_cast<Child<T>&>(*this);
}
template <typename U> auto operator-(Child<U> c) const -> Child<decltype(T{} - U{})>
{
return {x - c.x, y - c.y};
}
template <typename U> Child<T>& operator-=(Child<U> c)
{
x -= c.x;
y -= c.y;
return static_cast<Child<T>&>(*this);
}
bool operator==(Child<T> c) const
{
return x == c.x && y == c.y;
}
bool operator!=(Child<T> c) const
{
return x != c.x || y != c.y;
}
template <typename U> auto operator*(U s) const -> Child<decltype(T{} * U{})>
{
return {s * x, s * y};
}
template <typename U> Child<T>& operator*=(U s)
{
x *= s;
y *= s;
return static_cast<Child<T>&>(*this);
}
template <typename U> Child<decltype(T{} / U{})> operator/(U d) const
{
VEC_CHECK(d != 0);
return {x / d, y / d};
}
template <typename U> [[nodiscard]] Child<T>& operator/=(U d)
{
VEC_CHECK(d != 0);
x /= d;
y /= d;
return static_cast<Child<T>&>(*this);
}
[[nodiscard]] Child<T> operator-() const
{
return {-x, -y};
}
[[nodiscard]] T& operator[](const int i) const
{
VEC_CHECK(i >= 0 && i <= 1);
return (i == 0) ? x : y;
}
[[nodiscard]] T& operator[](const int i)
{
VEC_CHECK(i >= 0 && i <= 1);
return (i == 0) ? x : y;
}
[[nodiscard]] std::string toString() const
{
return std::to_string(x) + std::to_string(x);
}
T x{};
T y{};
};
在代码中,用到了一些C++的高级技巧,我将在下面一一解释给大家:
VEC_CHECK()宏定义 为了达成鲁棒性的需求,我在方法的定义中加入了仅在debug模式下生效的assert,同时封装进入宏变量中,提高了代码健壮性的同时,也不会影响性能.
[[nodiscard]]声明 该声明于C++17版本中加入,声明在使用该方法时,不应当遗弃返回值,否则会发出警告,在调试时,略有作用 。
template <template <typename> class Child, typename T> 作为Tuple2的模板参数声明,使用Child作为当基类进行继承时的模板特化。 这是一种二级的模板特化结构,子类对父类的继承仅指定了Child这一模板参数的值,如此实现,基类的模板方法会非常好写。 而T只需要在编译时进行确定,就可以生成对应的类了。 这是一种在C++中实现动态绑定的方式.
那么在Tuple2的基础上,再去实现Vec2和Point2就十分容易了.
// 派生类 Vec2,继承自 Tuple2
template <typename T> class Vec2 : public Tuple2<Vec2, T>
{
public:
using Tuple2<Vec2, T>::x;
using Tuple2<Vec2, T>::y;
Vec2() : Tuple2<Vec2, T>()
{
}
Vec2(T x, T y) : Tuple2<Vec2, T>(x, y)
{
}
void print() const
{
std::cout << "Vec2: (" << this->x << ", " << this->y << ")\n";
}
};
template <typename T> class Point2 : public Tuple2<Point2, T>
{
public:
using Tuple2<Point2, T>::x;
using Tuple2<Point2, T>::y;
Point2() : Tuple2<Point2, T>()
{
}
Point2(T x, T y) : Tuple2<Point2, T>(x, y)
{
}
void print() const
{
std::cout << "Point2: (" << this->x << ", " << this->y << ")\n";
}
};
同理,只需要把元素数量改成3个,我们就可以得到Tuple3以及其子类 。
// 基类模板 Tuple3
template <template <typename> class Child, typename T> class Tuple3
{
public:
static constexpr int nDimensions = 3;
Tuple3() = default;
Tuple3(T x, T y, T z) : x(x), y(y), z(z)
{
}
Tuple3(Child<T> c)
{
x = c.x;
y = c.y;
z = c.z;
}
Child<T>& operator=(Child<T> c)
{
x = c.x;
y = c.y;
z = c.z;
return static_cast<Child<T>&>(*this);
}
template <typename U> auto operator+(Child<U> c) const -> Child<decltype(T{} + U{})>
{
return {x + c.x, y + c.y, z + c.z};
}
template <typename U> Child<T>& operator+=(Child<U> c)
{
x += c.x;
y += c.y;
z += c.z;
return static_cast<Child<T>&>(*this);
}
template <typename U> auto operator-(Child<U> c) const -> Child<decltype(T{} - U{})>
{
return {x - c.x, y - c.y, z - c.z};
}
template <typename U> Child<T>& operator-=(Child<U> c)
{
x -= c.x;
y -= c.y;
z -= c.z;
return static_cast<Child<T>&>(*this);
}
bool operator==(Child<T> c) const
{
return x == c.x && y == c.y && z == c.z;
}
bool operator!=(Child<T> c) const
{
return x != c.x || y != c.y || z != c.z;
}
template <typename U> auto operator*(U s) const -> Child<decltype(T{} * U{})>
{
return {s * x, s * y, s * z};
}
template <typename U> Child<T>& operator*=(U s)
{
x *= s;
y *= s;
z *= s;
return static_cast<Child<T>&>(*this);
}
template <typename U> Child<decltype(T{} / U{})> operator/(U d) const
{
VEC_CHECK(d != 0);
return {x / d, y / d, z / d};
}
template <typename U> [[nodiscard]] Child<T>& operator/=(U d)
{
VEC_CHECK(d != 0);
x /= d;
y /= d;
z /= d;
return static_cast<Child<T>&>(*this);
}
[[nodiscard]] Child<T> operator-() const
{
return {-x, -y, -z};
}
[[nodiscard]] T& operator[](const int i) const
{
VEC_CHECK(i >= 0 && i <= 2);
return (i == 0) ? x : (i == 1) ? y : z;
}
[[nodiscard]] T& operator[](const int i)
{
VEC_CHECK(i >= 0 && i <= 2);
return (i == 0) ? x : (i == 1) ? y : z;
}
[[nodiscard]] std::string toString() const
{
return std::to_string(x) + ", " + std::to_string(y) + ", " + std::to_string(z);
}
T x{};
T y{};
T z{};
};
// 派生类 Vec3,继承自 Tuple3
template <typename T> class Vec3 : public Tuple3<Vec3, T>
{
public:
// 显式引入模板基类的属性
using Tuple3<Vec3, T>::x;
using Tuple3<Vec3, T>::y;
using Tuple3<Vec3, T>::z;
Vec3() : Tuple3<Vec3, T>()
{
}
Vec3(T x, T y, T z) : Tuple3<Vec3, T>(x, y, z)
{
}
void print() const
{
std::cout << "Vec3: (" << this->x << ", " << this->y << ", " << this->z << ")\n";
}
};
template <typename T> class Point3 : public Tuple3<Point3, T>
{
public:
using Tuple3<Point3, T>::x;
using Tuple3<Point3, T>::y;
using Tuple3<Point3, T>::z;
Point3() : Tuple3<Point3, T>()
{
}
Point3(T x, T y, T z) : Tuple3<Point3, T>(x, y, z)
{
}
void print() const
{
std::cout << "Point3: (" << this->x << ", " << this->y << ", " << this->z << ")\n";
}
};
最后此篇关于Chapter1p2vec的文章就讲到这里了,如果你想了解更多关于Chapter1p2vec的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
当我查看文档时,大多数页面的底部都有上一章和下一章的链接/按钮,例如 virtualenv .我无法找到如何使用 Sphinx 为我的项目文档完成此操作文档工具。有人可以告诉我这是如何工作的或为我指出
我有一个 docbook 文档,当我创建一个 PDF 时,我希望它显示章节标题,但前面没有“章节”一词。根据this以下应该有效: 我的自定义样式表:(doc.xsl)
一个关于括号的简单问题。我很想知道为什么括号有时会打印,有时却保持“不可见”?在这种情况下,我可以给出代码示例和答案来说明我的意思。 如果我输入: name = "Larry" print(nam
我是 HTML 的新手,但我正在习惯使用 CSS。 我的问题是: 我想放置一个不同颜色和不同文本对齐方式的分隔线。 举个例子: 我想复制“攻城车”和“重型武器” 到目前为止,我所做的是: tr:nt
因此,在本书第 3 章“创建有意无限循环”小节中,作者给出了以下示例: # Finicky Counter # Demonstrates the break and continue statemen
我正在 DocBook 中编写文档,并希望以带有页眉和页脚的 PDF 格式发布它。为此,我有这种风格:
大家好,我开始按照 NLTK 团队的官方书籍学习 NLTK。 我正在阅读第 5 章“标记”,但我无法解决 PDF 版本第 186 页的练习之一: 给定 cfd2['VN'].keys() 指定的过去分
我有一个测试套件需要大约 10 分钟才能在 BrowserStack(自动化)中运行。如果测试用例失败,则很难在录制的视频中找到失败发生的位置。在浏览器底部显示夹具名称和测试名称有助于浏览视频,但使用
我在一个假的 .h 文件中定义了一些 doxygen 组 @defgroup MainGroup @brief ... @defgroup SubGroup1 @brief ... @ingroup
我在调用来自 Eloquent Javascript (Chapter 6) 的匿名函数代码示例的 sum 函数时遇到困难,虽然我了解在此 post 上澄清了什么,真不明白匿名函数是怎么调用的。 根据
关闭。这个问题是off-topic .它目前不接受答案。 想改善这个问题吗? Update the question所以它是 on-topic对于堆栈溢出。 8年前关闭。 Improve this q
我正在做 TAOCP 第 1 卷第 3 版的练习,但无法理解以下练习的答案中使用的语法。 第 1 章练习 8 通过指定 Tj,sj,aj,b 计算正整数 m 和 n 的最大公约数j 让您的输入由字符串
使用 ffmpeg 将 QuickTime 中的音频替换为 WAV 中的音频。 有人知道为什么我得到未找到引用的 QT 章节轨道吗? 命令: $ ffmpeg\ -i“$video”-t 25\ -i
我很确定我准确地遵循了本教程的步骤,但我仍然遇到错误 =/ 目前,我的 settings.py 数据库看起来像这样 DATABASES = { 'default': { 'EN
我在 iBooks 中打开了 Swift 这本书,看到了注释“为了获得最佳体验,请在 Xcode 中打开本章作为 Playground 。”这是怎么做到的? 最佳答案 启动 Xcode 6 文档和 A
我写了一个Makefile来编译文件并输出文件“main”,我最终将通过“./main”执行。下面是我的 Makefile: CXX=clang++ CXXFLAGS=-g -std=c++11 -W
SELECT p.id FROM produkty p, przyporzadkowania pr, stany_magazynowe, gk_grupy_produkty INNER JOIN su
所以我试图从 think python 解决这个练习: 第 12 章的练习 6: What is the longest English word, that remains a valid Engl
我正在开发一个图书应用程序,主菜单包含 10 章,现在每章包含不同数量的练习,4、7、10 个这样。 1)我想要的是,当用户点击某一章时,它会加载其练习,例如练习 1、练习 2 等,当用户点击练习时,
有很多人在 github 上发布了本书问题的答案,我看过其中的一些人,它们看起来都非常相似。无论如何,这是其中之一的链接。问题在评论中,答案在下面。 https://github.com/auwsmi
我是一名优秀的程序员,十分优秀!