在上一小节中,我们完成了对BMPImage类的构建,成功实现了我们这个小小引擎的图像输出功能。

你已经完成了图像输出了,接着就开始路径追踪吧。。。

开个玩笑XD

对于曾经学习过一些图形学经典教材的人来说,下一步应当开始着手于画线算法了,但对于本文来说,肯定是要走一些不走寻常路的

所谓万事开头难,我决定先打好地基。编写一个鲁棒性,扩展性还不错的向量类。

由于我们不打算借助外部库,所以,适当的设计是必要的。在这里,我打算借鉴一下pbrt-v4的设计。

也就是利用模板基类来定制我们的数据结构。

这么做的好处是,我们可以按照元素数量划分父类

本章目标

构建基本的数据结构,包括点和向量。

需求

  • 类型可扩展,鲁棒性还可以
  • 易于使用

实现

class Tuple2
/**
* \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++的高级技巧,我将在下面一一解释给大家:

  1. VEC_CHECK()宏定义

    为了达成鲁棒性的需求,我在方法的定义中加入了仅在debug模式下生效的assert,同时封装进入宏变量中,提高了代码健壮性的同时,也不会影响性能。

  2. [[nodiscard]]声明

    该声明于C++17版本中加入,声明在使用该方法时,不应当遗弃返回值,否则会发出警告,在调试时,略有作用

  3. 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";
}
};

Chapter1 p2 vec的更多相关文章

  1. OpenCASCADE Root-Finding Algorithm

    OpenCASCADE Root-Finding Algorithm eryar@163.com Abstract. A root-finding algorithm is a numerical m ...

  2. POJ3525 Most Distant Point from the Sea(半平面交)

    给你一个凸多边形,问在里面距离凸边形最远的点. 方法就是二分这个距离,然后将对应的半平面沿着法向平移这个距离,然后判断是否交集为空,为空说明这个距离太大了,否则太小了,二分即可. #pragma wa ...

  3. USACO3.41Closed Fences(几何)

    这题水的真不易..300多行 累死了 对着数据查错啊 枚举每个边上的点到源点 是否中间隔着别的边  每条边划分500份就够了  注意一下与源点在一条直线上的边不算 几何 啊,,好繁琐 参考各种模版.. ...

  4. bzoj 1185

    题目大意: 给你n个点求最小矩形覆盖. 思路:枚举凸包上的边然后,旋转卡壳找三个相应的为止把矩形的四个点求出来. #include<bits/stdc++.h> #define LL lo ...

  5. [Processing]点到线段的最小距离

    PVector p1,p2,n; float d = 0; void setup() { size(600,600); p1 = new PVector(150,30);//线段第一个端点 p2 = ...

  6. STL vector 内存释放

    最近在论坛看到一个提问帖子,问题是vector中存储了对象的指针,调用clear后这些指针如何删除? class Test { public: Test() {} ~Test() { cout < ...

  7. [C++] Copy Control (part 1)

    Copy, Assign, and Destroy When we define a class, we specify what happens when objects of the class ...

  8. [LOJ#6437][BZOJ5373]「PKUSC2018」PKUSC

    [LOJ#6437][BZOJ5373]「PKUSC2018」PKUSC 试题描述 九条可怜是一个爱玩游戏的女孩子. 最近她在玩一个无双割草类的游戏,平面上有 \(n\) 个敌人,每一个敌人的坐标为 ...

  9. [BZOJ4920][Lydsy六月月赛]薄饼切割

    [BZOJ4920][Lydsy六月月赛]薄饼切割 试题描述 有一天,tangjz 送给了 quailty 一张薄饼,tangjz 将它放在了水平桌面上,从上面看下去,薄饼形成了一个 \(H \tim ...

  10. [AH2017/HNOI2017]影魔(主席树+单调栈)

    设\(l[i]\)为i左边第一个比i大的数的下标.\(r[i]\)为i右边第一个比i大的数的下标. 我们把\(p1,p2\)分开考虑. 当产生贡献为\(p1\)时\(i\)和\(j\)一定满足,分别为 ...

随机推荐

  1. Serverless 可观测性的过去、现在与未来

    简介: 函数计算可观测性经历了 1.0 -> 2.0 的发展,从闭门造车的可观测发展成开源的可观测,从平台的可观测发展为开发者的可观测,从FaaS Only 的可观测演进成了云原生的可观测. 作 ...

  2. 使用 Arthas 排查开源 Excel 组件问题

    简介: 有了实际的使用之后,不免会想到,Arthas 是如何做到在程序运行时,动态监测我们的代码的呢?带着这样的问题,我们一起来看下 Java Agent 技术实现原理. ​ 背景介绍 ​ 项目中有使 ...

  3. 实时数仓入门训练营:Hologres性能调优实践

    简介: <实时数仓入门训练营>由阿里云研究员王峰.阿里云资深技术专家金晓军.阿里云高级产品专家刘一鸣等实时计算 Flink 版和 Hologres 的多名技术/产品一线专家齐上阵,合力搭建 ...

  4. [FAQ] jQuery prop 与 attr 的区别

    .prop() 获取匹配的元素集中第一个元素的属性(property)值 或 设置每一个匹配元素的一个或多个属性. 当设置 selectedIndex, tagName, nodeName, node ...

  5. [Linux] 日志管理: 日志轮替 logrotate

    日志轮替包含了 "日志切割" 和 "删除旧的保留新的" 功能. 后缀 xx.1 xx.2 这种规则的一般出现的也比较多,目的系统是防止日志被覆盖. 查看详细配置 ...

  6. [FAQ] Truffle Deployer 合约传参问题: Invalid number of parameters for "undefined". Got 0 expected 1!

    在使用 `truffle migrate` 时,如果合约的构造函数需要传参,而部署脚本里没有传的时候,就会报这个错. 未传参时: const Migrations = artifacts.requir ...

  7. dotnet 使用 WpfAnalyzers 辅助分析 WPF 应用代码缺陷

    引入 WpfAnalyzers 代码分析工具,相当于给团队加入一个免费的代码审查工具人,可以帮忙在日常开发找到很多代码缺陷.加入 WpfAnalyzers 代码分析工具,可以减少代码编写里的低级缺陷, ...

  8. 2019-2-27-VisualStudio-插件-翻译注释

    title author date CreateTime categories VisualStudio 插件 翻译注释 lindexi 2019-02-27 15:50:33 +0800 2019- ...

  9. vue项目中element-ui等UI组件自定义样式不生效的解决

    引 在使用element-ui的时候虽然默认的样式已经能够满足很多的需求了,但是有总是有时候要加上些自定义的需求.不过,有的时候样式写上去了,按理说应该是没错的,但却是不生效呢. 其实在vue项目中使 ...

  10. JAVA下唯一一款搞定OLTP+OLAP的强类型查询这就是最好用的ORM相见恨晚

    JAVA下唯一一款搞定OLTP+OLAP的强类型查询这就是最好用的ORM相见恨晚 介绍 首先非常感谢 FreeSQL 提供的部分源码,让我借鉴了不少功能点,整体设计并没有参考FreeSQL(因为jav ...