package main import ( "fmt" maths "math" ) type Vec3 struct { X, Y, Z float64 } func (v Vec3) Neg() Vec3 { return Vec3{-v.X, -v.Y, -v.Z}; } func (v Vec3) Add(w Vec3) Vec3 { return Vec3{v.X + w.X, v.Y + w.Y, v.Z + w.Z}; } func (v Vec3) Sub(w Vec3) Vec3 { return Vec3{v.X - w.X, v.Y - w.Y, v.Z - w.Z}; } func (v Vec3) Mul(w Vec3) Vec3 { return Vec3{v.X * w.X, v.Y * w.Y, v.Z * w.Z}; } func (v Vec3) Div(w Vec3) Vec3 { return Vec3{v.X / w.X, v.Y / w.Y, v.Z / w.Z}; } func (v Vec3) MagSqr() float64 { return v.Dot(v); } func (v Vec3) Mag() float64 { return maths.Sqrt(v.MagSqr()); } func (v Vec3) NearZero() bool { s := 1e-8; return v.X < s && v.Y < s && v.Z < s; } func (v Vec3) Unit() Vec3 { m := v.Mag(); return Vec3{v.X / m, v.Y / m, v.Z / m}; } func (v Vec3) Dot(w Vec3) float64 { return v.X * w.X + v.Y * w.Y + v.Z * w.Z; } func (v Vec3) Cross(w Vec3) Vec3 { return Vec3{v.Y * w.Z - v.Z * w.Y, v.Z * w.X - v.X * w.Z, v.X * w.Y - v.Y * w.X}; } func linear_to_gamma(comp float64) float64 { if comp > 0 { return maths.Sqrt(comp); } return 0; } func (v Vec3) ToPPM() string { i := Interval{0, 0.999}; r := uint8(256 * i.Clamp(linear_to_gamma(v.X))); g := uint8(256 * i.Clamp(linear_to_gamma(v.Y))); b := uint8(256 * i.Clamp(linear_to_gamma(v.Z))); return fmt.Sprintf("%3d %3d %3d", r, g, b); } func SplatVec3(f float64) Vec3 { return Vec3{f, f, f}; } func RandomVec3OnUnitSphere() (v Vec3) { i := Interval{-1, 1} for true { v = Vec3{i.Sample(), i.Sample(), i.Sample()}; if v.MagSqr() < 1 { v = v.Unit(); return; } } return; } func RandomVec3OnUnitDisk() (v Vec3) { i := Interval{-1, 1} for true { v = Vec3{i.Sample(), i.Sample(), 0}; if v.MagSqr() < 1 { return; } } return; } func (v Vec3) Reflect(n Vec3) Vec3 { return v.Sub(SplatVec3(2 * v.Dot(n)).Mul(n)); } func (v Vec3) Refract(n Vec3, eta_ratio float64) Vec3 { cos_theta := maths.Min(v.Neg().Dot(n), 1.0); r_out_perp := SplatVec3(eta_ratio). Mul(v.Add(SplatVec3(cos_theta).Mul(n))); rops := -maths.Sqrt(maths.Abs(1.0 - r_out_perp.MagSqr())); r_out_parr := SplatVec3(rops).Mul(n); return r_out_perp.Add(r_out_parr); }