[Rust] レイトレーシング – 1-1

はじめに

お疲れ様です。以前にベクトルクラス作る前に、Tuple3を作りました。方針としては、Tuple3でオペレーション系の実装を行って楽をしようみたいな感じでやっていました。
ただ、後日なんかイマイチな選択だったかもしれないと思ってきた次第です。
それを踏まえてこんな実装もあったなとメモがき程度の内容となります。

想定と違ったところ

Tuple3を先に実装したのは、見通しをよくしたかったからです。Vector3Color3を実装してみて気付いたことですが、当初意識できていなかったです。
レイトレーシング-1 では、Vector3Color3は、pub type Vector3 = Tuple3のように定義しています。
このうえで、Vector3Color3で機能を付け足せば、当初はVector3Color3で独立した機能を定義できるかと思っていました。しかし、実際は、Vector3で実装したものをColor3の変数で利用でき逆も然りみたいな感じになっていました。ちょっと気持ち悪いなと思い何が別の案がないか考えていて割とマシな案ができたのでメモ程度に残します。

絵心ねぇーー。

改善案?

方針は、Tuple3のトレイトを作成する。主に、要素の取得や代入の機能を定義します。その上で、macro_rulesの中でオペレーション系の実装をする方針です。利用の時は、Tuple3トレイトをVector3で実装した上で、macroで定義した実装を適用する感じにすればいいよりスッキリするかと思いました。

Traitの定義

pub trait Tuple3: Copy + Clone + PartialEq {
    fn new(e1: f64, e2: f64, e3:f64) -> Self;
    fn zero() -> Self;
    fn fill(value: f64) -> Self;
    fn get_element(&self, index: usize) -> f64;
    fn get_elements(&self) -> [f64; 3];
    fn set_element(&mut self, index: usize, value: f64);
}

macroの定義

macro_rules! impl_tuple3_ops{
    ($type: ty) => {
        impl Add for $type {
            type Output = $type;
            fn add(self, other: Self) -> Self::Output {
                <$type>::new(
                    self.get_element(0) + other.get_element(0),
                    self.get_element(1) + other.get_element(1),
                    self.get_element(2) + other.get_element(2),
                )
            }
        }

        impl Sub for $type {
            type Output = $type;
            fn sub(self, other: Self) -> Self::Output {
                <$type>::new(
                    self.get_element(0) - other.get_element(0),
                    self.get_element(1) - other.get_element(1),
                    self.get_element(2) - other.get_element(2),
                )
            }
        }

        impl Neg for $type {
            type Output = $type;
            fn neg(self) -> Self::Output {
                <$type>::new(
                    -self.get_element(0),
                    -self.get_element(1),
                    -self.get_element(2),
                )
            }
        }

        impl Div<f64> for $type {
            type Output = $type;
            fn div(self, scalar: f64) -> Self::Output {
                <$type>::new(
                    self.get_element(0) / scalar,
                    self.get_element(1) / scalar,
                    self.get_element(2) / scalar,
                )
            }
        }

        impl Mul<f64> for $type {
            type Output = $type;
            fn mul(self, scalar: f64) -> Self::Output {
                <$type>::new(
                    self.get_element(0) * scalar,
                    self.get_element(1) * scalar,
                    self.get_element(2) * scalar,
                )
            }
        }

        impl Mul<$type> for f64 {
            type Output = $type;
            fn mul(self, other: $type) -> Self::Output {
                <$type>::new(
                    self * other.get_element(0),
                    self * other.get_element(1),
                    self * other.get_element(2),
                )
            }
        }
    };
}

pub(crate) use impl_tuple3_ops;

Vector3の再定義

上記を使って、再定義してみます。細かいところは、重複するので省きますが、次の構成となります。

#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Vector3 {
    pub e: [f64; 3],
}

pub type Point3 = Vector3;   // Point3は、そのまま
impl_tuple3_ops!(Vector3);   // macroの使用

impl Tuple3 for Vector3 {
    // 実装
}

impl Vector3 {
    // 実装
}

Color3も同様に再定義します。

動作確認?

次のコードで動作確認はできるかな?

    // 省略
    let color_test = Color3::zero();
    let vector_test = Vector3::zero();

    color_test.length();  // エラーが出るはず
    vector_test.length();
    // 省略

使っているエディターにもよりますが、エディターの構文解析で怒られるはず。
変更前であれば、Color3でlengthを呼び出せてたはず、、、

さいごに

来週こそ、物体が出力される様にしたい。。。