首页 > 其他分享 >Chapter1 p2 vec

Chapter1 p2 vec

时间:2024-06-12 22:59:31浏览次数:17  
标签:p2 return Chapter1 Tuple3 vec template Child operator const

在上一小节中,我们完成了对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";
    }
};

标签:p2,return,Chapter1,Tuple3,vec,template,Child,operator,const
From: https://www.cnblogs.com/qiyuewuyi/p/18244909

相关文章

  • [GDOI2014] 世界杯&[AHOI2001] 彩票摇奖&[NOIP2009 普及组] 分数线划定
    [GDOI2014]世界杯de题目描述(复制的题目可能有错,请用你手头上的)3014年世界杯足球赛就要开始了!作为卫冕冠军中国足球队的教练,手下每位球员都是猛将,如何摆出最强的11人阵容也是一件幸福的烦恼事啊。众所周知,足球阵容里的11个球员都会被分配到场上某一个特别的位置,而这......
  • 【NOIP2023普及组复赛】题1:小苹果
    题1:小苹果【题目描述】小Y的桌子上放着nnn个苹果从左到右排成一列,编号为从11......
  • 【NOIP2023普及组复赛】题2:公路
    题2:公路【题目描述】小苞准备开着车沿着公路自驾。公路上一共有nnn个站点,编号为从11......
  • BootStrap24
    一、概念一个前端开发的框架,Bootstrap,来自Twitter,是目前很受欢迎的前端框架。Bootstrap是基于HTML、CSS、JavaScript的,它简洁灵活,使得Web开发更加快捷。框架:一个半成品软件,开发人员可以在框架基础上,在进行开发,简化编码。好处:1.定义了很多的css样式和js插......
  • [DP] [倍增优化] Luogu P1081 [NOIP2012 提高组] 开车旅行
    [NOIP2012提高组]开车旅行题目描述小\(\text{A}\)和小\(\text{B}\)决定利用假期外出旅行,他们将想去的城市从$1$到\(n\)编号,且编号较小的城市在编号较大的城市的西边,已知各个城市的海拔高度互不相同,记城市\(i\)的海拔高度为\(h_i\),城市\(i\)和城市\(j\)之间的距......
  • C137 线段树分治 P2147 [SDOI2008] 洞穴勘测
    视频链接: P2147[SDOI2008]洞穴勘测-洛谷|计算机科学教育新生态(luogu.com.cn)//线段树分治O(mlogmlogm)#include<iostream>#include<cstring>#include<algorithm>#include<vector>#include<map>usingnamespacestd;#definels(u<<1)......
  • 2024年生物医学、医学图像与信号处理国际会议(ICBMISP2024)
    2024年生物医学、医学图像与信号处理国际会议(ICBMISP2024)会议简介2024年国际生物医学、医学成像和信号处理会议(ICBMISP2024)很高兴邀请您提交主题为“生物医学、医学图像和信号处理的当前挑战和未来前景”的原稿。通过ICBMISP2024,生物医学、医学成像和信号处理三个重要领域......
  • CSP历年复赛题-P5018 [NOIP2018 普及组] 对称二叉树
    原题链接:https://www.luogu.com.cn/problem/P5018题意解读:找到是对称二叉树的最大子树节点数。解题思路:1、先统计每一个节点为子树的节点数intdfs1(introot){if(root==-1)return0;returncnt[root]=dfs1(tree[root].l)+dfs1(tree[root].r)+1;}2、再......
  • 终端输入变长vector的方法
    功能:从终端输入一串数字序列,空格分隔,赋值给vector<int>容器方法1.换行符结束--需使用getline进行字符串转换整数处理,使用字符串流处理函数(stringstream)代码:#include<iostream>#include<vector>#include<string>#include<sstream>//要包含这个才能用stringstreamusingn......
  • NOIP2024模拟11:忠于自我
    NOIP2024模拟11:忠于自我T1一句话题意:有若干个容量为\(L\)的包,从左往右装物品,当前包还能装则装,否则必须重开一个包装进去,对于\(\foralli\in[1,n]\),问想要以此装入第\(i\simn\)个物品需要开多少个包?结论题:倒着装和正着装所需要的包数是一样的.感性理解:在"......