Решение на Пресичания от Александър Лазаров

Обратно към всички решения

Към профила на Александър Лазаров

Резултати

  • 10 точки от тестове
  • 1 бонус точка
  • 11 точки общо
  • 20 успешни тест(а)
  • 0 неуспешни тест(а)

Код

package main
import (
"math"
"github.com/fmi/go-homework/geom"
)
const epsilon = 1e-7
// Triangle represents a triangle in the 3d space
// a, b, c are the coordinates of the vertices
type Triangle struct {
a, b, c geom.Vector
}
// NewTriangle returns a new triangle
func NewTriangle(a, b, c geom.Vector) Triangle {
return Triangle{a, b, c}
}
// NormalVector to the plane the triangle is laying on
func (t Triangle) NormalVector() geom.Vector {
ab := geom.Sub(t.b, t.a)
ac := geom.Sub(t.c, t.a)
return geom.Cross(ab, ac)
}
// Area of the triangle
func (t Triangle) Area() float64 {
return geom.Len(t.NormalVector()) / 2
}
// Plane the triangle is laying on
func (t Triangle) Plane() Plane {
n := t.NormalVector()
return Plane{
n.X, n.Y, n.Z,
-geom.Dot(n, t.a),
}
}
// IsInside returns if the point is inside the triangle
func (t Triangle) IsInside(p geom.Vector) bool {
return AreOnTheSameSide(p, t.a, t.b, t.c) &&
AreOnTheSameSide(p, t.b, t.a, t.c) &&
AreOnTheSameSide(p, t.c, t.a, t.b)
}
// Intersect returns true if the ray intersects with the triangle
func (t Triangle) Intersect(ray geom.Ray) bool {
plane := t.Plane()
hasIntersection, ti, actualPoint := Intersection(ray, plane)
if !hasIntersection || ti < -epsilon {
return false
}
return t.IsInside(actualPoint)
}
// Quad represents a quadrilateral in the 3d space
// a, b, c and d are the coordinates of the vertices
type Quad struct {
a, b, c, d geom.Vector
}
// NewQuad returns a new quad
func NewQuad(a, b, c, d geom.Vector) Quad {
return Quad{a, b, c, d}
}
// Intersect returns true if the ray intersects with the quad
func (q Quad) Intersect(ray geom.Ray) bool {
// quad is concave
// either b or d is the internal point
if AreOnTheSameSide(q.b, q.d, q.a, q.c) {
abd := Triangle{q.a, q.b, q.d}
bcd := Triangle{q.b, q.c, q.d}
return abd.Intersect(ray) || bcd.Intersect(ray)
}
abc := Triangle{q.a, q.b, q.c}
acd := Triangle{q.a, q.c, q.d}
// either a or c is the internal point or the quad is convex
return abc.Intersect(ray) || acd.Intersect(ray)
}
// Sphere represents a sphere in the 3d space
type Sphere struct {
center geom.Vector
radius float64
}
// NewSphere returns a new sphere
func NewSphere(center geom.Vector, radius float64) Sphere {
return Sphere{
center: center,
radius: radius,
}
}
// IsInside returns true if the point is inside or on the sphere
func (s Sphere) IsInside(v geom.Vector) bool {
d := geom.Len(geom.Sub(s.center, v))
return s.radius-d > -epsilon
}
// Intersect returns true if the ray intersects with the sphere
func (s Sphere) Intersect(ray geom.Ray) bool {
// Check if the origin of the ray
// are inside/touching the sphere so we don't have to do the
// more tricky maths
if s.IsInside(ray.Origin) {
return true
}
// too bad, we have to calculate the distance from the sphere center
// to the the ray
Area := Triangle{
ray.Origin,
geom.Add(ray.Origin, ray.Direction),
s.center,
}.Area()
d := geom.Len(ray.Direction)
h := Area / d
// the distance from the line to the sphere center is greater than
// the sphere radius, no need to check where it is relative to the ray
if h-s.radius > epsilon {
return false
}
// we're really interested in t's sign, so we won't subract
// by the squared Len of ray.Direction
t := -geom.Dot(geom.Sub(ray.Origin, s.center), ray.Direction)
if t >= -epsilon {
return true
}
return false
}
// Plane represents a plane in the 3d space in the form
// a * x + b * y + c * z + d = 0
type Plane struct {
a, b, c, d float64
}
// Intersection of a line (represented with a ray) with a plane
// * The first return value is false if the ray is parallel or
// laying on the plane
// * The second return value is the t param in the parametric equation of the
// line. Note that we are t can be negative. It is 0 in case the first return
// value is false
// * The third return value is the intersection point of the plane and the
// line. It is a zero vector in case the first return value is false
func Intersection(r geom.Ray, p Plane) (bool, float64, geom.Vector) {
tCoeff := p.a*r.Direction.X + p.b*r.Direction.Y + p.c*r.Direction.Z
if math.Abs(tCoeff) <= epsilon {
// the line is parallel to the Plane,
// either 0 or infinite number of solutions
return false, 0, geom.Vector{}
}
freeCoef := -(p.d + p.a*r.Origin.X + p.b*r.Origin.Y + p.c*r.Origin.Z)
t := freeCoef / tCoeff
return true, t, VectorAtT(r, t)
}
// VectorAtT returns a point of the ray for a given parameter t
// in the parametric form of the ray O + t.D
// Note that this will also work for t < 0
func VectorAtT(r geom.Ray, t float64) geom.Vector {
return geom.Add(r.Origin, geom.Mul(r.Direction, t))
}
// AreOnTheSameSide checks if p1 and p2 are on the same side of ab
func AreOnTheSameSide(p1, p2, a, b geom.Vector) bool {
ab := geom.Sub(b, a)
cp1 := geom.Cross(ab, geom.Sub(p1, a))
cp2 := geom.Cross(ab, geom.Sub(p2, a))
return geom.Dot(cp1, cp2) >= -epsilon
}

Лог от изпълнението

PASS
ok  	_/tmp/d20181122-57-1s32u9b	0.002s
PASS
ok  	_/tmp/d20181122-57-1s32u9b	0.002s
PASS
ok  	_/tmp/d20181122-57-1s32u9b	0.002s
PASS
ok  	_/tmp/d20181122-57-1s32u9b	0.002s
PASS
ok  	_/tmp/d20181122-57-1s32u9b	0.002s
PASS
ok  	_/tmp/d20181122-57-1s32u9b	0.002s
PASS
ok  	_/tmp/d20181122-57-1s32u9b	0.002s
PASS
ok  	_/tmp/d20181122-57-1s32u9b	0.002s
PASS
ok  	_/tmp/d20181122-57-1s32u9b	0.003s
PASS
ok  	_/tmp/d20181122-57-1s32u9b	0.002s
PASS
ok  	_/tmp/d20181122-57-1s32u9b	0.002s
PASS
ok  	_/tmp/d20181122-57-1s32u9b	0.002s
PASS
ok  	_/tmp/d20181122-57-1s32u9b	0.002s
PASS
ok  	_/tmp/d20181122-57-1s32u9b	0.002s
PASS
ok  	_/tmp/d20181122-57-1s32u9b	0.002s
PASS
ok  	_/tmp/d20181122-57-1s32u9b	0.002s
PASS
ok  	_/tmp/d20181122-57-1s32u9b	0.002s
PASS
ok  	_/tmp/d20181122-57-1s32u9b	0.002s
PASS
ok  	_/tmp/d20181122-57-1s32u9b	0.002s
PASS
ok  	_/tmp/d20181122-57-1s32u9b	0.002s

История (7 версии и 2 коментара)

Александър обнови решението на 20.11.2018 01:05 (преди 9 месеца)

+package main
+
+import (
+ "math"
+
+ "github.com/fmi/go-homework/geom"
+)
+
+var epsilon = 1e-7
+
+type Ray struct {
+ Origin, Direction Vector
+}
+
+type Vector struct {
+ geom.Vector
+}
+
+type Triangle struct {
+ a, b, c Vector
+}
+
+type Quad struct {
+ a, b, c, d Vector
+}
+
+type Sphere struct {
+ a Vector
+ r float64
+}
+
+type Plane struct {
+ a, b, c, d float64
+}
+
+func MakeRay(geomRay geom.Ray) Ray {
+ return Ray{
+ Origin: Vector{geomRay.Origin},
+ Direction: Vector{geomRay.Direction},
+ }
+}
+
+func (v Vector) Minus(o Vector) Vector {
+ return Vector{
+ geom.NewVector(
+ v.X-o.X,
+ v.Y-o.Y,
+ v.Z-o.Z)}
+}
+
+func (v Vector) Product(t float64) Vector {
+ return Vector{
+ geom.NewVector(
+ v.X*t,
+ v.Y*t,
+ v.Z*t)}
+}
+
+func (r Ray) Intersection(p Plane) (bool, float64, Vector) {
+ tCoeff := p.a*r.Direction.X + p.b*r.Direction.Y + p.c*r.Direction.Z
+
+ if math.Abs(tCoeff) <= epsilon {
+ // the line is parallel to the Plane,
+ // either 0 or infinite number of solutions
+ return false, 0, Vector{}
+ }
+
+ freeCoef := -(p.d + p.a*r.Origin.X + p.b*r.Origin.Y + p.c*r.Origin.Z)
+ t := freeCoef / tCoeff
+
+ return true, t, r.VectorAtT(t)
+}
+
+func (r Ray) VectorAtT(t float64) Vector {
+ return r.Origin.Plus(
+ r.Direction.Product(t))
+}
+
+func (v Vector) Plus(o Vector) Vector {
+ return Vector{
+ geom.NewVector(
+ v.X+o.X,
+ v.Y+o.Y,
+ v.Z+o.Z)}
+}
+
+func (v Vector) DotProduct(o Vector) float64 {
+ return v.X*o.X + v.Y*o.Y + v.Z*o.Z
+}
+
+func (v Vector) CrossProduct(o Vector) Vector {
+ return Vector{
+ geom.NewVector(
+ v.Y*o.Z-v.Z*o.Y,
+ v.Z*o.X-v.X*o.Z,
+ v.X*o.Y-v.Y*o.X)}
+}
+
+func (v Vector) Length() float64 {
+ return math.Sqrt(v.X*v.X + v.Y*v.Y + v.Z*v.Z)
+}
+
+func (t Triangle) NormalVector() Vector {
+ ab := t.b.Minus(t.a)
+ ac := t.c.Minus(t.a)
+
+ return ab.CrossProduct(ac)
+}
+
+func (t Triangle) Area() float64 {
+ return t.NormalVector().Length() / 2
+}
+
+func (t Triangle) Plane() Plane {
+ n := t.NormalVector()
+
+ return Plane{
+ n.X, n.Y, n.Z,
+ -n.DotProduct(t.a),
+ }
+}
+
+func (t Triangle) IsInside(p Vector) bool {
+ abp := Triangle{t.a, t.b, p}.Area()
+ bcp := Triangle{t.b, t.c, p}.Area()
+ cap := Triangle{t.c, t.a, p}.Area()
+ totalArea := t.Area()
+
+ // If the Area of the triangle is (almost) the same
+ // as the sum of the are of the triangles between p and the edges
+ // of the triangle, then p is inside or on the edge of the triangle
+ return math.Abs(totalArea-abp-bcp-cap) <= epsilon
+}
+
+func (t Triangle) Intersect(geomRay geom.Ray) bool {
+ ray := MakeRay(geomRay)
+ Plane := t.Plane()
+
+ hasIntersection, ti, actualPoint := ray.Intersection(Plane)
+
+ if !hasIntersection || ti < -epsilon {
+ return false
+ }
+
+ return t.IsInside(actualPoint)
+}
+
+func (q Quad) Intersect(geomRay geom.Ray) bool {
+ abc := Triangle{q.a, q.b, q.c}
+ abd := Triangle{q.a, q.b, q.d}
+ bcd := Triangle{q.b, q.c, q.d}
+ acd := Triangle{q.a, q.c, q.d}
+
+ // quad is concave
+ // either b or d is the internal point
+ if abc.IsInside(q.d) || acd.IsInside(q.b) {
+ return abd.Intersect(geomRay) || bcd.Intersect(geomRay)
+ }
+
+ // either a or c is the internal point or the quad is convex
+ return abc.Intersect(geomRay) || acd.Intersect(geomRay)
+}
+
+func (s Sphere) Intersect(geomRay geom.Ray) bool {
+ ray := MakeRay(geomRay)
+
+ Area := Triangle{
+ ray.Origin,
+ ray.Origin.Plus(ray.Direction),
+ s.a,
+ }.Area()
+
+ d := ray.Direction.Length()
+
+ // the distance from the sphere to the line the ray is laying on
+ h := Area / d
+
+ // the distance from the line to the sphere center is greater than
+ // the sphere radius, no need to check where it is relative to the ray
+ if h-s.r > epsilon {
+ return false
+ }
+
+ // we're really interested in t's sign, so we won't subract
+ // by the squared Length of ray.Direction
+ t := -ray.Origin.Minus(s.a).DotProduct(
+ ray.Direction)
+
+ if t >= -epsilon {
+ return true
+ }
+
+ return false
+}
+
+func NewTriangle(a, b, c geom.Vector) Triangle {
+ return Triangle{
+ Vector{a},
+ Vector{b},
+ Vector{c},
+ }
+}
+
+func NewQuad(a, b, c, d geom.Vector) Quad {
+ return Quad{
+ Vector{a},
+ Vector{b},
+ Vector{c},
+ Vector{d},
+ }
+}
+
+func NewSphere(origin geom.Vector, r float64) Sphere {
+ return Sphere{a: Vector{origin}, r: r}
+}

Александър обнови решението на 20.11.2018 10:59 (преди 9 месеца)

package main
import (
"math"
"github.com/fmi/go-homework/geom"
)
var epsilon = 1e-7
type Ray struct {
Origin, Direction Vector
}
type Vector struct {
geom.Vector
}
type Triangle struct {
a, b, c Vector
}
type Quad struct {
a, b, c, d Vector
}
type Sphere struct {
a Vector
r float64
}
type Plane struct {
a, b, c, d float64
}
func MakeRay(geomRay geom.Ray) Ray {
return Ray{
Origin: Vector{geomRay.Origin},
Direction: Vector{geomRay.Direction},
}
}
func (v Vector) Minus(o Vector) Vector {
return Vector{
geom.NewVector(
v.X-o.X,
v.Y-o.Y,
v.Z-o.Z)}
}
func (v Vector) Product(t float64) Vector {
return Vector{
geom.NewVector(
v.X*t,
v.Y*t,
v.Z*t)}
}
func (r Ray) Intersection(p Plane) (bool, float64, Vector) {
tCoeff := p.a*r.Direction.X + p.b*r.Direction.Y + p.c*r.Direction.Z
if math.Abs(tCoeff) <= epsilon {
// the line is parallel to the Plane,
// either 0 or infinite number of solutions
return false, 0, Vector{}
}
freeCoef := -(p.d + p.a*r.Origin.X + p.b*r.Origin.Y + p.c*r.Origin.Z)
t := freeCoef / tCoeff
return true, t, r.VectorAtT(t)
}
func (r Ray) VectorAtT(t float64) Vector {
return r.Origin.Plus(
r.Direction.Product(t))
}
func (v Vector) Plus(o Vector) Vector {
return Vector{
geom.NewVector(
v.X+o.X,
v.Y+o.Y,
v.Z+o.Z)}
}
func (v Vector) DotProduct(o Vector) float64 {
return v.X*o.X + v.Y*o.Y + v.Z*o.Z
}
func (v Vector) CrossProduct(o Vector) Vector {
return Vector{
geom.NewVector(
v.Y*o.Z-v.Z*o.Y,
v.Z*o.X-v.X*o.Z,
v.X*o.Y-v.Y*o.X)}
}
func (v Vector) Length() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y + v.Z*v.Z)
}
func (t Triangle) NormalVector() Vector {
ab := t.b.Minus(t.a)
ac := t.c.Minus(t.a)
return ab.CrossProduct(ac)
}
func (t Triangle) Area() float64 {
return t.NormalVector().Length() / 2
}
func (t Triangle) Plane() Plane {
n := t.NormalVector()
return Plane{
n.X, n.Y, n.Z,
-n.DotProduct(t.a),
}
}
func (t Triangle) IsInside(p Vector) bool {
abp := Triangle{t.a, t.b, p}.Area()
bcp := Triangle{t.b, t.c, p}.Area()
cap := Triangle{t.c, t.a, p}.Area()
totalArea := t.Area()
// If the Area of the triangle is (almost) the same
// as the sum of the are of the triangles between p and the edges
// of the triangle, then p is inside or on the edge of the triangle
return math.Abs(totalArea-abp-bcp-cap) <= epsilon
}
func (t Triangle) Intersect(geomRay geom.Ray) bool {
ray := MakeRay(geomRay)
Plane := t.Plane()
hasIntersection, ti, actualPoint := ray.Intersection(Plane)
if !hasIntersection || ti < -epsilon {
return false
}
return t.IsInside(actualPoint)
}
+func (s Sphere) IsInside(v Vector) bool {
+ d := s.a.Minus(v).Length()
+
+ return s.r-d > -epsilon
+}
+
func (q Quad) Intersect(geomRay geom.Ray) bool {
abc := Triangle{q.a, q.b, q.c}
abd := Triangle{q.a, q.b, q.d}
bcd := Triangle{q.b, q.c, q.d}
acd := Triangle{q.a, q.c, q.d}
// quad is concave
// either b or d is the internal point
if abc.IsInside(q.d) || acd.IsInside(q.b) {
return abd.Intersect(geomRay) || bcd.Intersect(geomRay)
}
// either a or c is the internal point or the quad is convex
return abc.Intersect(geomRay) || acd.Intersect(geomRay)
}
func (s Sphere) Intersect(geomRay geom.Ray) bool {
ray := MakeRay(geomRay)
+ // Check if the origin of the ray
+ // are inside/touching the sphere so we don't have to do the
+ // more tricky maths
+ if s.IsInside(ray.Origin) {
+ return true
+ }
+
+ // too bad, we have to calculate the distance from the sphere center
+ // to the the ray
Area := Triangle{
ray.Origin,
ray.Origin.Plus(ray.Direction),
s.a,
}.Area()
d := ray.Direction.Length()
- // the distance from the sphere to the line the ray is laying on
h := Area / d
// the distance from the line to the sphere center is greater than
// the sphere radius, no need to check where it is relative to the ray
if h-s.r > epsilon {
return false
}
// we're really interested in t's sign, so we won't subract
// by the squared Length of ray.Direction
t := -ray.Origin.Minus(s.a).DotProduct(
ray.Direction)
if t >= -epsilon {
return true
}
return false
}
func NewTriangle(a, b, c geom.Vector) Triangle {
return Triangle{
Vector{a},
Vector{b},
Vector{c},
}
}
func NewQuad(a, b, c, d geom.Vector) Quad {
return Quad{
Vector{a},
Vector{b},
Vector{c},
Vector{d},
}
}
func NewSphere(origin geom.Vector, r float64) Sphere {
return Sphere{a: Vector{origin}, r: r}
}

Александър обнови решението на 20.11.2018 11:07 (преди 9 месеца)

package main
import (
"math"
"github.com/fmi/go-homework/geom"
)
var epsilon = 1e-7
type Ray struct {
Origin, Direction Vector
}
type Vector struct {
geom.Vector
}
type Triangle struct {
a, b, c Vector
}
type Quad struct {
a, b, c, d Vector
}
type Sphere struct {
- a Vector
- r float64
+ center Vector
+ radius float64
}
type Plane struct {
a, b, c, d float64
}
-func MakeRay(geomRay geom.Ray) Ray {
- return Ray{
- Origin: Vector{geomRay.Origin},
- Direction: Vector{geomRay.Direction},
- }
-}
-
func (v Vector) Minus(o Vector) Vector {
- return Vector{
- geom.NewVector(
- v.X-o.X,
- v.Y-o.Y,
- v.Z-o.Z)}
+ return NewVector(
+ v.X-o.X,
+ v.Y-o.Y,
+ v.Z-o.Z)
}
func (v Vector) Product(t float64) Vector {
- return Vector{
- geom.NewVector(
- v.X*t,
- v.Y*t,
- v.Z*t)}
+ return NewVector(
+ v.X*t,
+ v.Y*t,
+ v.Z*t)
}
func (r Ray) Intersection(p Plane) (bool, float64, Vector) {
tCoeff := p.a*r.Direction.X + p.b*r.Direction.Y + p.c*r.Direction.Z
if math.Abs(tCoeff) <= epsilon {
// the line is parallel to the Plane,
// either 0 or infinite number of solutions
return false, 0, Vector{}
}
freeCoef := -(p.d + p.a*r.Origin.X + p.b*r.Origin.Y + p.c*r.Origin.Z)
t := freeCoef / tCoeff
return true, t, r.VectorAtT(t)
}
func (r Ray) VectorAtT(t float64) Vector {
return r.Origin.Plus(
r.Direction.Product(t))
}
func (v Vector) Plus(o Vector) Vector {
- return Vector{
- geom.NewVector(
- v.X+o.X,
- v.Y+o.Y,
- v.Z+o.Z)}
+ return NewVector(
+ v.X+o.X,
+ v.Y+o.Y,
+ v.Z+o.Z)
}
func (v Vector) DotProduct(o Vector) float64 {
return v.X*o.X + v.Y*o.Y + v.Z*o.Z
}
func (v Vector) CrossProduct(o Vector) Vector {
- return Vector{
- geom.NewVector(
- v.Y*o.Z-v.Z*o.Y,
- v.Z*o.X-v.X*o.Z,
- v.X*o.Y-v.Y*o.X)}
+ return NewVector(
+ v.Y*o.Z-v.Z*o.Y,
+ v.Z*o.X-v.X*o.Z,
+ v.X*o.Y-v.Y*o.X)
}
func (v Vector) Length() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y + v.Z*v.Z)
}
func (t Triangle) NormalVector() Vector {
ab := t.b.Minus(t.a)
ac := t.c.Minus(t.a)
return ab.CrossProduct(ac)
}
func (t Triangle) Area() float64 {
return t.NormalVector().Length() / 2
}
func (t Triangle) Plane() Plane {
n := t.NormalVector()
return Plane{
n.X, n.Y, n.Z,
-n.DotProduct(t.a),
}
}
func (t Triangle) IsInside(p Vector) bool {
abp := Triangle{t.a, t.b, p}.Area()
bcp := Triangle{t.b, t.c, p}.Area()
cap := Triangle{t.c, t.a, p}.Area()
totalArea := t.Area()
// If the Area of the triangle is (almost) the same
// as the sum of the are of the triangles between p and the edges
// of the triangle, then p is inside or on the edge of the triangle
return math.Abs(totalArea-abp-bcp-cap) <= epsilon
}
func (t Triangle) Intersect(geomRay geom.Ray) bool {
- ray := MakeRay(geomRay)
+ ray := NewRay(geomRay)
Plane := t.Plane()
hasIntersection, ti, actualPoint := ray.Intersection(Plane)
if !hasIntersection || ti < -epsilon {
return false
}
return t.IsInside(actualPoint)
}
func (s Sphere) IsInside(v Vector) bool {
- d := s.a.Minus(v).Length()
+ d := s.center.Minus(v).Length()
- return s.r-d > -epsilon
+ return s.radius-d > -epsilon
}
func (q Quad) Intersect(geomRay geom.Ray) bool {
abc := Triangle{q.a, q.b, q.c}
abd := Triangle{q.a, q.b, q.d}
bcd := Triangle{q.b, q.c, q.d}
acd := Triangle{q.a, q.c, q.d}
// quad is concave
// either b or d is the internal point
if abc.IsInside(q.d) || acd.IsInside(q.b) {
return abd.Intersect(geomRay) || bcd.Intersect(geomRay)
}
// either a or c is the internal point or the quad is convex
return abc.Intersect(geomRay) || acd.Intersect(geomRay)
}
func (s Sphere) Intersect(geomRay geom.Ray) bool {
- ray := MakeRay(geomRay)
+ ray := NewRay(geomRay)
// Check if the origin of the ray
// are inside/touching the sphere so we don't have to do the
// more tricky maths
if s.IsInside(ray.Origin) {
return true
}
// too bad, we have to calculate the distance from the sphere center
// to the the ray
Area := Triangle{
ray.Origin,
ray.Origin.Plus(ray.Direction),
- s.a,
+ s.center,
}.Area()
d := ray.Direction.Length()
h := Area / d
// the distance from the line to the sphere center is greater than
// the sphere radius, no need to check where it is relative to the ray
- if h-s.r > epsilon {
+ if h-s.radius > epsilon {
return false
}
// we're really interested in t's sign, so we won't subract
// by the squared Length of ray.Direction
- t := -ray.Origin.Minus(s.a).DotProduct(
+ t := -ray.Origin.Minus(s.center).DotProduct(
ray.Direction)
if t >= -epsilon {
return true
}
return false
}
+func NewRay(geomRay geom.Ray) Ray {
+ return Ray{
+ Origin: Vector{geomRay.Origin},
+ Direction: Vector{geomRay.Direction},
+ }
+}
+
+func NewVector(x, y, z float64) Vector {
+ return Vector{
+ geom.NewVector(x, y, z)}
+}
+
func NewTriangle(a, b, c geom.Vector) Triangle {
return Triangle{
Vector{a},
Vector{b},
Vector{c},
}
}
func NewQuad(a, b, c, d geom.Vector) Quad {
return Quad{
Vector{a},
Vector{b},
Vector{c},
Vector{d},
}
}
-func NewSphere(origin geom.Vector, r float64) Sphere {
- return Sphere{a: Vector{origin}, r: r}
+func NewSphere(center geom.Vector, radius float64) Sphere {
+ return Sphere{center: Vector{center}, radius: radius}
}

Александър обнови решението на 20.11.2018 16:11 (преди 9 месеца)

package main
import (
"math"
"github.com/fmi/go-homework/geom"
)
-var epsilon = 1e-7
+const epsilon = 1e-7
type Ray struct {
Origin, Direction Vector
}
type Vector struct {
geom.Vector
}
type Triangle struct {
a, b, c Vector
}
type Quad struct {
a, b, c, d Vector
}
type Sphere struct {
center Vector
radius float64
}
type Plane struct {
a, b, c, d float64
}
func (v Vector) Minus(o Vector) Vector {
return NewVector(
v.X-o.X,
v.Y-o.Y,
v.Z-o.Z)
}
func (v Vector) Product(t float64) Vector {
return NewVector(
v.X*t,
v.Y*t,
v.Z*t)
}
func (r Ray) Intersection(p Plane) (bool, float64, Vector) {
tCoeff := p.a*r.Direction.X + p.b*r.Direction.Y + p.c*r.Direction.Z
if math.Abs(tCoeff) <= epsilon {
// the line is parallel to the Plane,
// either 0 or infinite number of solutions
return false, 0, Vector{}
}
freeCoef := -(p.d + p.a*r.Origin.X + p.b*r.Origin.Y + p.c*r.Origin.Z)
t := freeCoef / tCoeff
return true, t, r.VectorAtT(t)
}
func (r Ray) VectorAtT(t float64) Vector {
return r.Origin.Plus(
r.Direction.Product(t))
}
func (v Vector) Plus(o Vector) Vector {
return NewVector(
v.X+o.X,
v.Y+o.Y,
v.Z+o.Z)
}
func (v Vector) DotProduct(o Vector) float64 {
return v.X*o.X + v.Y*o.Y + v.Z*o.Z
}
func (v Vector) CrossProduct(o Vector) Vector {
return NewVector(
v.Y*o.Z-v.Z*o.Y,
v.Z*o.X-v.X*o.Z,
v.X*o.Y-v.Y*o.X)
}
func (v Vector) Length() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y + v.Z*v.Z)
}
func (t Triangle) NormalVector() Vector {
ab := t.b.Minus(t.a)
ac := t.c.Minus(t.a)
return ab.CrossProduct(ac)
}
func (t Triangle) Area() float64 {
return t.NormalVector().Length() / 2
}
func (t Triangle) Plane() Plane {
n := t.NormalVector()
return Plane{
n.X, n.Y, n.Z,
-n.DotProduct(t.a),
}
}
func (t Triangle) IsInside(p Vector) bool {
abp := Triangle{t.a, t.b, p}.Area()
bcp := Triangle{t.b, t.c, p}.Area()
cap := Triangle{t.c, t.a, p}.Area()
totalArea := t.Area()
// If the Area of the triangle is (almost) the same
// as the sum of the are of the triangles between p and the edges
// of the triangle, then p is inside or on the edge of the triangle
return math.Abs(totalArea-abp-bcp-cap) <= epsilon
}
func (t Triangle) Intersect(geomRay geom.Ray) bool {
ray := NewRay(geomRay)
Plane := t.Plane()
hasIntersection, ti, actualPoint := ray.Intersection(Plane)
if !hasIntersection || ti < -epsilon {
return false
}
return t.IsInside(actualPoint)
}
func (s Sphere) IsInside(v Vector) bool {
d := s.center.Minus(v).Length()
return s.radius-d > -epsilon
}
func (q Quad) Intersect(geomRay geom.Ray) bool {
abc := Triangle{q.a, q.b, q.c}
abd := Triangle{q.a, q.b, q.d}
bcd := Triangle{q.b, q.c, q.d}
acd := Triangle{q.a, q.c, q.d}
// quad is concave
// either b or d is the internal point
if abc.IsInside(q.d) || acd.IsInside(q.b) {
return abd.Intersect(geomRay) || bcd.Intersect(geomRay)
}
// either a or c is the internal point or the quad is convex
return abc.Intersect(geomRay) || acd.Intersect(geomRay)
}
func (s Sphere) Intersect(geomRay geom.Ray) bool {
ray := NewRay(geomRay)
// Check if the origin of the ray
// are inside/touching the sphere so we don't have to do the
// more tricky maths
if s.IsInside(ray.Origin) {
return true
}
// too bad, we have to calculate the distance from the sphere center
// to the the ray
Area := Triangle{
ray.Origin,
ray.Origin.Plus(ray.Direction),
s.center,
}.Area()
d := ray.Direction.Length()
h := Area / d
// the distance from the line to the sphere center is greater than
// the sphere radius, no need to check where it is relative to the ray
if h-s.radius > epsilon {
return false
}
// we're really interested in t's sign, so we won't subract
// by the squared Length of ray.Direction
t := -ray.Origin.Minus(s.center).DotProduct(
ray.Direction)
if t >= -epsilon {
return true
}
return false
}
func NewRay(geomRay geom.Ray) Ray {
return Ray{
Origin: Vector{geomRay.Origin},
Direction: Vector{geomRay.Direction},
}
}
func NewVector(x, y, z float64) Vector {
return Vector{
geom.NewVector(x, y, z)}
}
func NewTriangle(a, b, c geom.Vector) Triangle {
return Triangle{
Vector{a},
Vector{b},
Vector{c},
}
}
func NewQuad(a, b, c, d geom.Vector) Quad {
return Quad{
Vector{a},
Vector{b},
Vector{c},
Vector{d},
}
}
func NewSphere(center geom.Vector, radius float64) Sphere {
return Sphere{center: Vector{center}, radius: radius}
}

Александър обнови решението на 20.11.2018 22:33 (преди 9 месеца)

package main
import (
"math"
"github.com/fmi/go-homework/geom"
)
const epsilon = 1e-7
-type Ray struct {
- Origin, Direction Vector
-}
-
-type Vector struct {
- geom.Vector
-}
-
+// Triangle represents a triangle in the 3d space
+// a, b, c are the coordinates of the vertices
type Triangle struct {
- a, b, c Vector
+ a, b, c geom.Vector
}
+// Quad represents a quadrilateral in the 3d space
+// a, b, c and d are the coordinates of the vertices
type Quad struct {
- a, b, c, d Vector
+ a, b, c, d geom.Vector
}
+// Sphere represents a sphere in the 3d space
type Sphere struct {
- center Vector
+ center geom.Vector
radius float64
}
+// Plane represents a plane in the 3d space in the form
+// a * x + b * y + c * z + d = 0
type Plane struct {
a, b, c, d float64
}
-func (v Vector) Minus(o Vector) Vector {
- return NewVector(
- v.X-o.X,
- v.Y-o.Y,
- v.Z-o.Z)
-}
-
-func (v Vector) Product(t float64) Vector {
- return NewVector(
- v.X*t,
- v.Y*t,
- v.Z*t)
-}
-
-func (r Ray) Intersection(p Plane) (bool, float64, Vector) {
+// Intersection of a line (represented with a ray) with a plane
+// * The first return value is false if the ray is parallel or
+// laying on the plane
+// * The second return value is the t param in the parametric equation of the
+// line. Note that we are t can be negative. It is 0 in case the first return
+// value is false
+// * The third return value is the intersection point of the plane and the
+// line. It is a zero vector in case the first return value is false
+func Intersection(r geom.Ray, p Plane) (bool, float64, geom.Vector) {
tCoeff := p.a*r.Direction.X + p.b*r.Direction.Y + p.c*r.Direction.Z
if math.Abs(tCoeff) <= epsilon {
// the line is parallel to the Plane,
// either 0 or infinite number of solutions
- return false, 0, Vector{}
+ return false, 0, geom.Vector{}
}
freeCoef := -(p.d + p.a*r.Origin.X + p.b*r.Origin.Y + p.c*r.Origin.Z)
t := freeCoef / tCoeff
- return true, t, r.VectorAtT(t)
+ return true, t, VectorAtT(r, t)
}
-func (r Ray) VectorAtT(t float64) Vector {
- return r.Origin.Plus(
- r.Direction.Product(t))
+// VectorAtT returns a point of the ray for a given parameter t
+// in the parametric form of the ray O + t.D
+// Note that this will also work for t < 0
+func VectorAtT(r geom.Ray, t float64) geom.Vector {
+ return geom.Add(r.Origin, geom.Mul(r.Direction, t))
}
-func (v Vector) Plus(o Vector) Vector {
- return NewVector(
- v.X+o.X,
- v.Y+o.Y,
- v.Z+o.Z)
-}
+// NormalVector of the plane the triangle is laying on
+func (t Triangle) NormalVector() geom.Vector {
+ ab := geom.Sub(t.b, t.a)
+ ac := geom.Sub(t.c, t.a)
-func (v Vector) DotProduct(o Vector) float64 {
- return v.X*o.X + v.Y*o.Y + v.Z*o.Z
+ return geom.Cross(ab, ac)
}
-func (v Vector) CrossProduct(o Vector) Vector {
- return NewVector(
- v.Y*o.Z-v.Z*o.Y,
- v.Z*o.X-v.X*o.Z,
- v.X*o.Y-v.Y*o.X)
-}
-
-func (v Vector) Length() float64 {
- return math.Sqrt(v.X*v.X + v.Y*v.Y + v.Z*v.Z)
-}
-
-func (t Triangle) NormalVector() Vector {
- ab := t.b.Minus(t.a)
- ac := t.c.Minus(t.a)
-
- return ab.CrossProduct(ac)
-}
-
+// Area of the triangle
func (t Triangle) Area() float64 {
- return t.NormalVector().Length() / 2
+ return geom.Len(t.NormalVector()) / 2
}
+// Plane the triangle is laying on
func (t Triangle) Plane() Plane {
n := t.NormalVector()
return Plane{
n.X, n.Y, n.Z,
- -n.DotProduct(t.a),
+ -geom.Dot(n, t.a),
}
}
-func (t Triangle) IsInside(p Vector) bool {
- abp := Triangle{t.a, t.b, p}.Area()
- bcp := Triangle{t.b, t.c, p}.Area()
- cap := Triangle{t.c, t.a, p}.Area()
- totalArea := t.Area()
+// IsOnSameSide checks if p1 and p2 are on the same side of ab
+func IsOnSameSide(p1, p2, a, b geom.Vector) bool {
+ ab := geom.Sub(b, a)
+ cp1 := geom.Cross(ab, geom.Sub(p1, a))
+ cp2 := geom.Cross(ab, geom.Sub(p2, a))
- // If the Area of the triangle is (almost) the same
- // as the sum of the are of the triangles between p and the edges
- // of the triangle, then p is inside or on the edge of the triangle
- return math.Abs(totalArea-abp-bcp-cap) <= epsilon
+ return geom.Dot(cp1, cp2) >= -epsilon
}
-func (t Triangle) Intersect(geomRay geom.Ray) bool {
- ray := NewRay(geomRay)
- Plane := t.Plane()
+// IsInside returns if the point is inside the triangle
+func (t Triangle) IsInside(p geom.Vector) bool {
+ return IsOnSameSide(p, t.a, t.b, t.c) &&
+ IsOnSameSide(p, t.b, t.a, t.c) &&
+ IsOnSameSide(p, t.c, t.a, t.b)
+}
- hasIntersection, ti, actualPoint := ray.Intersection(Plane)
+// Intersect returns true if the ray intersects with the triangle
+func (t Triangle) Intersect(ray geom.Ray) bool {
+ plane := t.Plane()
+ hasIntersection, ti, actualPoint := Intersection(ray, plane)
+
if !hasIntersection || ti < -epsilon {
return false
}
return t.IsInside(actualPoint)
}
-func (s Sphere) IsInside(v Vector) bool {
- d := s.center.Minus(v).Length()
+// IsInside returns true if the point is inside or on the sphere
+func (s Sphere) IsInside(v geom.Vector) bool {
+ d := geom.Len(geom.Sub(s.center, v))
return s.radius-d > -epsilon
}
-func (q Quad) Intersect(geomRay geom.Ray) bool {
+// Intersect returns true if the ray intersects with the quad
+func (q Quad) Intersect(ray geom.Ray) bool {
abc := Triangle{q.a, q.b, q.c}
abd := Triangle{q.a, q.b, q.d}
bcd := Triangle{q.b, q.c, q.d}
acd := Triangle{q.a, q.c, q.d}
// quad is concave
// either b or d is the internal point
if abc.IsInside(q.d) || acd.IsInside(q.b) {
- return abd.Intersect(geomRay) || bcd.Intersect(geomRay)
+ return abd.Intersect(ray) || bcd.Intersect(ray)
}
// either a or c is the internal point or the quad is convex
- return abc.Intersect(geomRay) || acd.Intersect(geomRay)
+ return abc.Intersect(ray) || acd.Intersect(ray)
}
-func (s Sphere) Intersect(geomRay geom.Ray) bool {
- ray := NewRay(geomRay)
-
+// Intersect returns true if the ray intersects with the sphere
+func (s Sphere) Intersect(ray geom.Ray) bool {
// Check if the origin of the ray
// are inside/touching the sphere so we don't have to do the
// more tricky maths
if s.IsInside(ray.Origin) {
return true
}
// too bad, we have to calculate the distance from the sphere center
// to the the ray
Area := Triangle{
ray.Origin,
- ray.Origin.Plus(ray.Direction),
+ geom.Add(ray.Origin, ray.Direction),
s.center,
}.Area()
- d := ray.Direction.Length()
+ d := geom.Len(ray.Direction)
h := Area / d
// the distance from the line to the sphere center is greater than
// the sphere radius, no need to check where it is relative to the ray
if h-s.radius > epsilon {
return false
}
// we're really interested in t's sign, so we won't subract
- // by the squared Length of ray.Direction
- t := -ray.Origin.Minus(s.center).DotProduct(
- ray.Direction)
+ // by the squared Len of ray.Direction
+ t := -geom.Dot(geom.Sub(ray.Origin, s.center), ray.Direction)
if t >= -epsilon {
return true
}
return false
}
-func NewRay(geomRay geom.Ray) Ray {
- return Ray{
- Origin: Vector{geomRay.Origin},
- Direction: Vector{geomRay.Direction},
- }
-}
-
-func NewVector(x, y, z float64) Vector {
- return Vector{
- geom.NewVector(x, y, z)}
-}
-
+// NewTriangle returns a new triangle
func NewTriangle(a, b, c geom.Vector) Triangle {
- return Triangle{
- Vector{a},
- Vector{b},
- Vector{c},
- }
+ return Triangle{a, b, c}
}
+// NewQuad returns a new quad
func NewQuad(a, b, c, d geom.Vector) Quad {
- return Quad{
- Vector{a},
- Vector{b},
- Vector{c},
- Vector{d},
- }
+ return Quad{a, b, c, d}
}
+// NewSphere returns a new sphere
func NewSphere(center geom.Vector, radius float64) Sphere {
- return Sphere{center: Vector{center}, radius: radius}
+ return Sphere{
+ center: center,
+ radius: radius,
+ }
}

Александър обнови решението на 20.11.2018 22:54 (преди 9 месеца)

package main
import (
"math"
"github.com/fmi/go-homework/geom"
)
const epsilon = 1e-7
// Triangle represents a triangle in the 3d space
// a, b, c are the coordinates of the vertices
type Triangle struct {
a, b, c geom.Vector
}
// Quad represents a quadrilateral in the 3d space
// a, b, c and d are the coordinates of the vertices
type Quad struct {
a, b, c, d geom.Vector
}
// Sphere represents a sphere in the 3d space
type Sphere struct {
center geom.Vector
radius float64
}
// Plane represents a plane in the 3d space in the form
// a * x + b * y + c * z + d = 0
type Plane struct {
a, b, c, d float64
}
// Intersection of a line (represented with a ray) with a plane
// * The first return value is false if the ray is parallel or
// laying on the plane
// * The second return value is the t param in the parametric equation of the
// line. Note that we are t can be negative. It is 0 in case the first return
// value is false
// * The third return value is the intersection point of the plane and the
// line. It is a zero vector in case the first return value is false
func Intersection(r geom.Ray, p Plane) (bool, float64, geom.Vector) {
tCoeff := p.a*r.Direction.X + p.b*r.Direction.Y + p.c*r.Direction.Z
if math.Abs(tCoeff) <= epsilon {
// the line is parallel to the Plane,
// either 0 or infinite number of solutions
return false, 0, geom.Vector{}
}
freeCoef := -(p.d + p.a*r.Origin.X + p.b*r.Origin.Y + p.c*r.Origin.Z)
t := freeCoef / tCoeff
return true, t, VectorAtT(r, t)
}
// VectorAtT returns a point of the ray for a given parameter t
// in the parametric form of the ray O + t.D
// Note that this will also work for t < 0
func VectorAtT(r geom.Ray, t float64) geom.Vector {
return geom.Add(r.Origin, geom.Mul(r.Direction, t))
}
// NormalVector of the plane the triangle is laying on
func (t Triangle) NormalVector() geom.Vector {
ab := geom.Sub(t.b, t.a)
ac := geom.Sub(t.c, t.a)
return geom.Cross(ab, ac)
}
// Area of the triangle
func (t Triangle) Area() float64 {
return geom.Len(t.NormalVector()) / 2
}
// Plane the triangle is laying on
func (t Triangle) Plane() Plane {
n := t.NormalVector()
return Plane{
n.X, n.Y, n.Z,
-geom.Dot(n, t.a),
}
}
-// IsOnSameSide checks if p1 and p2 are on the same side of ab
-func IsOnSameSide(p1, p2, a, b geom.Vector) bool {
+// AreOnTheSameSide checks if p1 and p2 are on the same side of ab
+func AreOnTheSameSide(p1, p2, a, b geom.Vector) bool {
ab := geom.Sub(b, a)
cp1 := geom.Cross(ab, geom.Sub(p1, a))
cp2 := geom.Cross(ab, geom.Sub(p2, a))
return geom.Dot(cp1, cp2) >= -epsilon
}
// IsInside returns if the point is inside the triangle
func (t Triangle) IsInside(p geom.Vector) bool {
- return IsOnSameSide(p, t.a, t.b, t.c) &&
- IsOnSameSide(p, t.b, t.a, t.c) &&
- IsOnSameSide(p, t.c, t.a, t.b)
+ return AreOnTheSameSide(p, t.a, t.b, t.c) &&
+ AreOnTheSameSide(p, t.b, t.a, t.c) &&
+ AreOnTheSameSide(p, t.c, t.a, t.b)
}
// Intersect returns true if the ray intersects with the triangle
func (t Triangle) Intersect(ray geom.Ray) bool {
plane := t.Plane()
hasIntersection, ti, actualPoint := Intersection(ray, plane)
if !hasIntersection || ti < -epsilon {
return false
}
return t.IsInside(actualPoint)
}
// IsInside returns true if the point is inside or on the sphere
func (s Sphere) IsInside(v geom.Vector) bool {
d := geom.Len(geom.Sub(s.center, v))
return s.radius-d > -epsilon
}
// Intersect returns true if the ray intersects with the quad
func (q Quad) Intersect(ray geom.Ray) bool {
- abc := Triangle{q.a, q.b, q.c}
- abd := Triangle{q.a, q.b, q.d}
- bcd := Triangle{q.b, q.c, q.d}
- acd := Triangle{q.a, q.c, q.d}
-
// quad is concave
// either b or d is the internal point
- if abc.IsInside(q.d) || acd.IsInside(q.b) {
+ if AreOnTheSameSide(q.b, q.d, q.a, q.c) {
+ abd := Triangle{q.a, q.b, q.d}
+ bcd := Triangle{q.b, q.c, q.d}
+
return abd.Intersect(ray) || bcd.Intersect(ray)
}
+
+ abc := Triangle{q.a, q.b, q.c}
+ acd := Triangle{q.a, q.c, q.d}
// either a or c is the internal point or the quad is convex
return abc.Intersect(ray) || acd.Intersect(ray)
}
// Intersect returns true if the ray intersects with the sphere
func (s Sphere) Intersect(ray geom.Ray) bool {
// Check if the origin of the ray
// are inside/touching the sphere so we don't have to do the
// more tricky maths
if s.IsInside(ray.Origin) {
return true
}
// too bad, we have to calculate the distance from the sphere center
// to the the ray
Area := Triangle{
ray.Origin,
geom.Add(ray.Origin, ray.Direction),
s.center,
}.Area()
d := geom.Len(ray.Direction)
h := Area / d
// the distance from the line to the sphere center is greater than
// the sphere radius, no need to check where it is relative to the ray
if h-s.radius > epsilon {
return false
}
// we're really interested in t's sign, so we won't subract
// by the squared Len of ray.Direction
t := -geom.Dot(geom.Sub(ray.Origin, s.center), ray.Direction)
if t >= -epsilon {
return true
}
return false
}
// NewTriangle returns a new triangle
func NewTriangle(a, b, c geom.Vector) Triangle {
return Triangle{a, b, c}
}
// NewQuad returns a new quad
func NewQuad(a, b, c, d geom.Vector) Quad {
return Quad{a, b, c, d}
}
// NewSphere returns a new sphere
func NewSphere(center geom.Vector, radius float64) Sphere {
return Sphere{
center: center,
radius: radius,
}
}

Файла ти е малко разбъркан. Събери дефинициите и методите по тип. Така ако искаш да гледаш нещо за сфера, то дефиницията на типа, методите и конструктор функциите му са близо един до друг. В момента е трудно за четене тъй като във файла вървят нещо подобно на триъгълник, сфера, триъгълник, четириъгълник, друго, сфера, триъгълник, четириъгълник, друго. Трябва много да се подскача нагоре-надолу.

Александър обнови решението на 21.11.2018 00:45 (преди 9 месеца)

package main
import (
"math"
"github.com/fmi/go-homework/geom"
)
const epsilon = 1e-7
// Triangle represents a triangle in the 3d space
// a, b, c are the coordinates of the vertices
type Triangle struct {
a, b, c geom.Vector
}
-// Quad represents a quadrilateral in the 3d space
-// a, b, c and d are the coordinates of the vertices
-type Quad struct {
- a, b, c, d geom.Vector
+// NewTriangle returns a new triangle
+func NewTriangle(a, b, c geom.Vector) Triangle {
+ return Triangle{a, b, c}
}
-// Sphere represents a sphere in the 3d space
-type Sphere struct {
- center geom.Vector
- radius float64
-}
-
-// Plane represents a plane in the 3d space in the form
-// a * x + b * y + c * z + d = 0
-type Plane struct {
- a, b, c, d float64
-}
-
-// Intersection of a line (represented with a ray) with a plane
-// * The first return value is false if the ray is parallel or
-// laying on the plane
-// * The second return value is the t param in the parametric equation of the
-// line. Note that we are t can be negative. It is 0 in case the first return
-// value is false
-// * The third return value is the intersection point of the plane and the
-// line. It is a zero vector in case the first return value is false
-func Intersection(r geom.Ray, p Plane) (bool, float64, geom.Vector) {
- tCoeff := p.a*r.Direction.X + p.b*r.Direction.Y + p.c*r.Direction.Z
-
- if math.Abs(tCoeff) <= epsilon {
- // the line is parallel to the Plane,
- // either 0 or infinite number of solutions
- return false, 0, geom.Vector{}
- }
-
- freeCoef := -(p.d + p.a*r.Origin.X + p.b*r.Origin.Y + p.c*r.Origin.Z)
- t := freeCoef / tCoeff
-
- return true, t, VectorAtT(r, t)
-}
-
-// VectorAtT returns a point of the ray for a given parameter t
-// in the parametric form of the ray O + t.D
-// Note that this will also work for t < 0
-func VectorAtT(r geom.Ray, t float64) geom.Vector {
- return geom.Add(r.Origin, geom.Mul(r.Direction, t))
-}
-
-// NormalVector of the plane the triangle is laying on
+// NormalVector to the plane the triangle is laying on
func (t Triangle) NormalVector() geom.Vector {
ab := geom.Sub(t.b, t.a)
ac := geom.Sub(t.c, t.a)
return geom.Cross(ab, ac)
}
// Area of the triangle
func (t Triangle) Area() float64 {
return geom.Len(t.NormalVector()) / 2
}
// Plane the triangle is laying on
func (t Triangle) Plane() Plane {
n := t.NormalVector()
return Plane{
n.X, n.Y, n.Z,
-geom.Dot(n, t.a),
}
}
-// AreOnTheSameSide checks if p1 and p2 are on the same side of ab
-func AreOnTheSameSide(p1, p2, a, b geom.Vector) bool {
- ab := geom.Sub(b, a)
- cp1 := geom.Cross(ab, geom.Sub(p1, a))
- cp2 := geom.Cross(ab, geom.Sub(p2, a))
-
- return geom.Dot(cp1, cp2) >= -epsilon
-}
-
// IsInside returns if the point is inside the triangle
func (t Triangle) IsInside(p geom.Vector) bool {
return AreOnTheSameSide(p, t.a, t.b, t.c) &&
AreOnTheSameSide(p, t.b, t.a, t.c) &&
AreOnTheSameSide(p, t.c, t.a, t.b)
}
// Intersect returns true if the ray intersects with the triangle
func (t Triangle) Intersect(ray geom.Ray) bool {
plane := t.Plane()
hasIntersection, ti, actualPoint := Intersection(ray, plane)
if !hasIntersection || ti < -epsilon {
return false
}
return t.IsInside(actualPoint)
}
-// IsInside returns true if the point is inside or on the sphere
-func (s Sphere) IsInside(v geom.Vector) bool {
- d := geom.Len(geom.Sub(s.center, v))
+// Quad represents a quadrilateral in the 3d space
+// a, b, c and d are the coordinates of the vertices
+type Quad struct {
+ a, b, c, d geom.Vector
+}
- return s.radius-d > -epsilon
+// NewQuad returns a new quad
+func NewQuad(a, b, c, d geom.Vector) Quad {
+ return Quad{a, b, c, d}
}
// Intersect returns true if the ray intersects with the quad
func (q Quad) Intersect(ray geom.Ray) bool {
// quad is concave
// either b or d is the internal point
if AreOnTheSameSide(q.b, q.d, q.a, q.c) {
abd := Triangle{q.a, q.b, q.d}
bcd := Triangle{q.b, q.c, q.d}
return abd.Intersect(ray) || bcd.Intersect(ray)
}
abc := Triangle{q.a, q.b, q.c}
acd := Triangle{q.a, q.c, q.d}
// either a or c is the internal point or the quad is convex
return abc.Intersect(ray) || acd.Intersect(ray)
}
+// Sphere represents a sphere in the 3d space
+type Sphere struct {
+ center geom.Vector
+ radius float64
+}
+
+// NewSphere returns a new sphere
+func NewSphere(center geom.Vector, radius float64) Sphere {
+ return Sphere{
+ center: center,
+ radius: radius,
+ }
+}
+
+// IsInside returns true if the point is inside or on the sphere
+func (s Sphere) IsInside(v geom.Vector) bool {
+ d := geom.Len(geom.Sub(s.center, v))
+
+ return s.radius-d > -epsilon
+}
+
// Intersect returns true if the ray intersects with the sphere
func (s Sphere) Intersect(ray geom.Ray) bool {
// Check if the origin of the ray
// are inside/touching the sphere so we don't have to do the
// more tricky maths
if s.IsInside(ray.Origin) {
return true
}
// too bad, we have to calculate the distance from the sphere center
// to the the ray
Area := Triangle{
ray.Origin,
geom.Add(ray.Origin, ray.Direction),
s.center,
}.Area()
d := geom.Len(ray.Direction)
h := Area / d
// the distance from the line to the sphere center is greater than
// the sphere radius, no need to check where it is relative to the ray
if h-s.radius > epsilon {
return false
}
// we're really interested in t's sign, so we won't subract
// by the squared Len of ray.Direction
t := -geom.Dot(geom.Sub(ray.Origin, s.center), ray.Direction)
if t >= -epsilon {
return true
}
return false
}
-// NewTriangle returns a new triangle
-func NewTriangle(a, b, c geom.Vector) Triangle {
- return Triangle{a, b, c}
+// Plane represents a plane in the 3d space in the form
+// a * x + b * y + c * z + d = 0
+type Plane struct {
+ a, b, c, d float64
}
-// NewQuad returns a new quad
-func NewQuad(a, b, c, d geom.Vector) Quad {
- return Quad{a, b, c, d}
-}
+// Intersection of a line (represented with a ray) with a plane
+// * The first return value is false if the ray is parallel or
+// laying on the plane
+// * The second return value is the t param in the parametric equation of the
+// line. Note that we are t can be negative. It is 0 in case the first return
+// value is false
+// * The third return value is the intersection point of the plane and the
+// line. It is a zero vector in case the first return value is false
+func Intersection(r geom.Ray, p Plane) (bool, float64, geom.Vector) {
+ tCoeff := p.a*r.Direction.X + p.b*r.Direction.Y + p.c*r.Direction.Z
-// NewSphere returns a new sphere
-func NewSphere(center geom.Vector, radius float64) Sphere {
- return Sphere{
- center: center,
- radius: radius,
+ if math.Abs(tCoeff) <= epsilon {
+ // the line is parallel to the Plane,
+ // either 0 or infinite number of solutions
+ return false, 0, geom.Vector{}
}
+
+ freeCoef := -(p.d + p.a*r.Origin.X + p.b*r.Origin.Y + p.c*r.Origin.Z)
+ t := freeCoef / tCoeff
+
+ return true, t, VectorAtT(r, t)
+}
+
+// VectorAtT returns a point of the ray for a given parameter t
+// in the parametric form of the ray O + t.D
+// Note that this will also work for t < 0
+func VectorAtT(r geom.Ray, t float64) geom.Vector {
+ return geom.Add(r.Origin, geom.Mul(r.Direction, t))
+}
+
+// AreOnTheSameSide checks if p1 and p2 are on the same side of ab
+func AreOnTheSameSide(p1, p2, a, b geom.Vector) bool {
+ ab := geom.Sub(b, a)
+ cp1 := geom.Cross(ab, geom.Sub(p1, a))
+ cp2 := geom.Cross(ab, geom.Sub(p2, a))
+
+ return geom.Dot(cp1, cp2) >= -epsilon
}