はじめに
お疲れ様です。以前にベクトルクラス作る前に、Tuple3
を作りました。方針としては、Tuple3
でオペレーション系の実装を行って楽をしようみたいな感じでやっていました。
ただ、後日なんかイマイチな選択だったかもしれないと思ってきた次第です。
それを踏まえてこんな実装もあったなとメモがき程度の内容となります。
想定と違ったところ
Tuple3
を先に実装したのは、見通しをよくしたかったからです。Vector3
とColor3
を実装してみて気付いたことですが、当初意識できていなかったです。
レイトレーシング-1 では、Vector3
とColor3
は、pub type Vector3 = Tuple3
のように定義しています。
このうえで、Vector3
とColor3
で機能を付け足せば、当初はVector3
とColor3
で独立した機能を定義できるかと思っていました。しかし、実際は、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を呼び出せてたはず、、、

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