From 26bd1d26064c0ae2ad3dff56465c0021e2685c0b Mon Sep 17 00:00:00 2001 From: David Allemang Date: Sun, 1 Apr 2018 03:28:50 -0400 Subject: [PATCH] working tetrahedron intersections --- Tetrahedrons/MVec3d.cs | 204 +++++++++++++++++++++ Tetrahedrons/OpenTK.dll.config | 25 +++ Tetrahedrons/Program.cs | 8 +- Tetrahedrons/TetrahedronWindow.cs | 293 ++++++++++++++++++++++++++++++ Tetrahedrons/Tetrahedrons.csproj | 100 +++++----- Tetrahedrons/packages.config | 4 + 6 files changed, 586 insertions(+), 48 deletions(-) create mode 100644 Tetrahedrons/MVec3d.cs create mode 100644 Tetrahedrons/OpenTK.dll.config create mode 100644 Tetrahedrons/TetrahedronWindow.cs create mode 100644 Tetrahedrons/packages.config diff --git a/Tetrahedrons/MVec3d.cs b/Tetrahedrons/MVec3d.cs new file mode 100644 index 0000000..7e60e19 --- /dev/null +++ b/Tetrahedrons/MVec3d.cs @@ -0,0 +1,204 @@ +using System; +using OpenTK; + +namespace Tetrahedrons +{ + // ReSharper disable once InconsistentNaming + public struct MVec3d + { + public static readonly MVec3d Unit = new MVec3d(1, 0, 0, 0, 0, 0, 0, 0); + public static readonly MVec3d Unit1 = new MVec3d(0, 1, 0, 0, 0, 0, 0, 0); + public static readonly MVec3d Unit2 = new MVec3d(0, 0, 1, 0, 0, 0, 0, 0); + public static readonly MVec3d Unit3 = new MVec3d(0, 0, 0, 1, 0, 0, 0, 0); + public static readonly MVec3d Unit23 = new MVec3d(0, 0, 0, 0, 1, 0, 0, 0); + public static readonly MVec3d Unit31 = new MVec3d(0, 0, 0, 0, 0, 1, 0, 0); + public static readonly MVec3d Unit12 = new MVec3d(0, 0, 0, 0, 0, 0, 1, 0); + public static readonly MVec3d Unit123 = new MVec3d(0, 0, 0, 0, 0, 0, 0, 1); + + public double E; + public double E1; + public double E2; + public double E3; + public double E23; + public double E31; + public double E12; + public double E123; + + public MVec3d(double e, double e1, double e2, double e3, double e23, double e31, double e12, double e123) + { + E = e; + E1 = e1; + E2 = e2; + E3 = e3; + E23 = e23; + E31 = e31; + E12 = e12; + E123 = e123; + } + + public MVec3d(MVec3d s, MVec3d v, MVec3d b, MVec3d t) + : this(s.E, v.E1, v.E2, v.E3, b.E23, b.E31, b.E12, t.E123) + { + } + + public static MVec3d Scalar(double e) + { + return new MVec3d(e, 0, 0, 0, 0, 0, 0, 0); + } + + public static MVec3d Vector(double e1, double e2, double e3) + { + return new MVec3d(0, e1, e2, e3, 0, 0, 0, 0); + } + + public static MVec3d Vector(Vector3d v) + { + return new MVec3d(0, v.X, v.Y, v.Z, 0, 0, 0, 0); + } + + public static MVec3d Bivector(double e23, double e31, double e12) + { + return new MVec3d(0, 0, 0, 0, e23, e31, e12, 0); + } + + public static MVec3d Trivector(double e123) + { + return new MVec3d(0, 0, 0, 0, 0, 0, 0, e123); + } + + public static MVec3d Complex(double real, double imag) + { + return new MVec3d(real, 0, 0, 0, 0, 0, 0, imag); + } + + public static MVec3d Complex(double w, double i, double j, double k) + { + return new MVec3d(w, 0, 0, 0, i, j, k, 0); + } + + public static MVec3d Complex(double angle, MVec3d plane) + { + return Math.Cos(angle) + Math.Sin(angle) * plane; + } + + public static MVec3d Add(MVec3d m, MVec3d n) + { + return new MVec3d(m.E + n.E, + m.E1 + n.E1, m.E2 + n.E2, m.E3 + n.E3, + m.E23 + n.E23, m.E31 + n.E31, m.E12 + n.E12, + m.E123 + n.E123); + } + + public static MVec3d Sub(MVec3d m, MVec3d n) + { + return new MVec3d(m.E - n.E, + m.E1 - n.E1, m.E2 - n.E2, m.E3 - n.E3, + m.E23 - n.E23, m.E31 - n.E31, m.E12 - n.E12, + m.E123 - n.E123); + } + + public static MVec3d Mult(MVec3d m, MVec3d n) + { + return new MVec3d( + m.E * n.E + m.E1 * n.E1 + m.E2 * n.E2 + m.E3 * n.E3 - m.E23 * n.E23 - m.E31 * n.E31 - m.E12 * n.E12 - m.E123 * n.E123, + m.E * n.E1 + m.E1 * n.E - m.E2 * n.E12 + m.E3 * n.E31 - m.E23 * n.E123 - m.E31 * n.E3 + m.E12 * n.E2 - m.E123 * n.E23, + m.E * n.E2 + m.E1 * n.E12 + m.E2 * n.E - m.E3 * n.E23 + m.E23 * n.E3 - m.E31 * n.E123 - m.E12 * n.E1 - m.E123 * n.E31, + m.E * n.E3 - m.E1 * n.E31 + m.E2 * n.E23 + m.E3 * n.E - m.E23 * n.E2 + m.E31 * n.E1 - m.E12 * n.E123 - m.E123 * n.E12, + m.E * n.E23 + m.E1 * n.E123 + m.E2 * n.E3 - m.E3 * n.E2 + m.E23 * n.E - m.E31 * n.E12 + m.E12 * n.E31 + m.E123 * n.E1, + m.E * n.E31 - m.E1 * n.E3 + m.E2 * n.E123 + m.E3 * n.E1 + m.E23 * n.E12 + m.E31 * n.E - m.E12 * n.E23 + m.E123 * n.E2, + m.E * n.E12 + m.E1 * n.E2 - m.E2 * n.E1 + m.E3 * n.E123 - m.E23 * n.E31 + m.E31 * n.E23 + m.E12 * n.E + m.E123 * n.E3, + m.E * n.E123 + m.E1 * n.E23 + m.E2 * n.E31 + m.E3 * n.E12 + m.E23 * n.E1 + m.E31 * n.E2 + m.E12 * n.E3 + m.E123 * n.E + ); + } + + public double Norm2 => E * E + E1 * E1 + E2 * E2 + E3 * E3 + E23 * E23 + E31 * E31 + E12 * E12 + E123 * E123; + public double Norm => Math.Sqrt(Norm2); + + public MVec3d Inv + { + get + { + var n2 = Norm2; + return new MVec3d(E / n2, -E1 / n2, -E2 / n2, -E3 / n2, -E23 / n2, -E31 / n2, -E12 / n2, E123 / n2); +// return new MVec3d(E / n2, E1 / n2, E2 / n2, E3 / n2, E23 / n2, E31 / n2, E12 / n2, E123 / n2); + } + } + + public MVec3d Grade(int k) + { + switch (k) + { + case 0: + return Scalar(E); + case 1: + return Vector(E1, E2, E3); + case 2: + return Bivector(E23, E31, E12); + case 3: + return Trivector(E123); + default: + return new MVec3d(); + } + } + + public static MVec3d Inner(MVec3d m, MVec3d n) + { + var m0 = m.Grade(0); + var m1 = m.Grade(1); + var m2 = m.Grade(2); + var m3 = m.Grade(3); + + var n0 = n.Grade(0); + var n1 = n.Grade(1); + var n2 = n.Grade(2); + var n3 = n.Grade(3); + + return new MVec3d( + m0 * n0 + m1 * n1 + m2 * n2 + m3 * n3, + m0 * n1 + m1 * n2 + m2 * n3, + m0 * n2 + m1 * n3, + m0 * n3 + ); + } + + public static MVec3d Outer(MVec3d m, MVec3d n) + { + var m0 = m.Grade(0); + var m1 = m.Grade(1); + var m2 = m.Grade(2); + var m3 = m.Grade(3); + + var n0 = n.Grade(0); + var n1 = n.Grade(1); + var n2 = n.Grade(2); + var n3 = n.Grade(3); + + return new MVec3d( + m0 * n0, + m1 * n0 + m0 * n1, + m2 * n0 + m1 * n1 + m0 * n2, + m3 * n0 + m2 * n1 + m1 * n2 + m0 * m3 + ); + } + + public static MVec3d operator ~(MVec3d m) => m.Inv; + public static MVec3d operator *(MVec3d m, MVec3d n) => Mult(m, n); + + public static MVec3d operator +(MVec3d m, MVec3d n) => Add(m, n); + public static MVec3d operator -(MVec3d m, MVec3d n) => Sub(m, n); + + public static MVec3d operator &(MVec3d m, MVec3d n) => Inner(m, n); + public static MVec3d operator ^(MVec3d m, MVec3d n) => Outer(m, n); + + public Vector3d VectorPart => new Vector3d(E1, E2, E3); + + public static implicit operator MVec3d(Vector3 v) => new MVec3d(0, v.X, v.Y, v.Z, 0, 0, 0, 0); + public static implicit operator MVec3d(Vector3d v) => new MVec3d(0, v.X, v.Y, v.Z, 0, 0, 0, 0); + public static implicit operator MVec3d(double s) => new MVec3d(s, 0, 0, 0, 0, 0, 0, 0); + + public override string ToString() + { + return $"({E:0.00} {E1:0.00}e1 {E2:0.00}e2 {E3:0.00}e3 {E23:0.00}e23 {E31:0.00}e31 {E12:0.00}e12 {E123:0.00}e123)"; + } + } +} \ No newline at end of file diff --git a/Tetrahedrons/OpenTK.dll.config b/Tetrahedrons/OpenTK.dll.config new file mode 100644 index 0000000..7098d39 --- /dev/null +++ b/Tetrahedrons/OpenTK.dll.config @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tetrahedrons/Program.cs b/Tetrahedrons/Program.cs index 8eb5155..6175764 100644 --- a/Tetrahedrons/Program.cs +++ b/Tetrahedrons/Program.cs @@ -1,9 +1,13 @@ -namespace Tetrahedrons +using OpenTK; + +namespace Tetrahedrons { - internal class Program + internal class Program : GameWindow { public static void Main(string[] args) { + using(var p = new TetrahedronWindow()) + p.Run(); } } } \ No newline at end of file diff --git a/Tetrahedrons/TetrahedronWindow.cs b/Tetrahedrons/TetrahedronWindow.cs new file mode 100644 index 0000000..8f02e7d --- /dev/null +++ b/Tetrahedrons/TetrahedronWindow.cs @@ -0,0 +1,293 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Net.Mail; +using System.Security; +using OpenTK; +using OpenTK.Graphics.OpenGL; +using OpenTK.Input; + +namespace Tetrahedrons +{ + public struct Polygon + { + public Vector3d[] Points; + public Color Color; + + public Polygon(Vector3d[] points) : this(points, Color.DodgerBlue) + { + } + + public Polygon(Vector3d[] points, Color color) + { + Points = points; + Color = color; + } + + public void Draw() + { + GL.Begin(PrimitiveType.TriangleStrip); + GL.Color3(Color); + foreach (var v in Points) + GL.Vertex3(v); + GL.End(); + } + + public static Polygon operator +(Polygon p, MVec3d m) + { + var pts = new Vector3d[p.Points.Length]; + for (var i = 0; i < p.Points.Length; i++) + pts[i] = (p.Points[i] + m).VectorPart; + return new Polygon(pts, p.Color); + } + + public static Polygon operator -(Polygon p, MVec3d m) + { + var pts = new Vector3d[p.Points.Length]; + for (var i = 0; i < p.Points.Length; i++) + pts[i] = (p.Points[i] - m).VectorPart; + return new Polygon(pts, p.Color); + } + + public static Polygon operator *(Polygon p, MVec3d m) + { + var pts = new Vector3d[p.Points.Length]; + for (var i = 0; i < p.Points.Length; i++) + pts[i] = (p.Points[i] * m).VectorPart; + return new Polygon(pts, p.Color); + } + + public static Polygon operator ^(Polygon p, MVec3d m) + { + var pts = new Vector3d[p.Points.Length]; + for (var i = 0; i < p.Points.Length; i++) + pts[i] = (p.Points[i] ^ m).VectorPart; + return new Polygon(pts, p.Color); + } + + public static Polygon operator &(Polygon p, MVec3d m) + { + var pts = new Vector3d[p.Points.Length]; + for (var i = 0; i < p.Points.Length; i++) + pts[i] = (p.Points[i] & m).VectorPart; + return new Polygon(pts, p.Color); + } + } + + public class Tetrahedron + { + public readonly Vector3d[] Points; + public Color Color; + public Color WireColor; + + public Tetrahedron(Vector3d p1, Vector3d p2, Vector3d p3, Vector3d p4) : this(p1, p2, p3, p4, Color.GhostWhite) + { + } + + public Tetrahedron(Vector3d p1, Vector3d p2, Vector3d p3, Vector3d p4, Color color) : this(p1, p2, p3, p4, color, Color.Maroon) + { + } + + public Tetrahedron(Vector3d p1, Vector3d p2, Vector3d p3, Vector3d p4, Color color, Color wireColor) + { + Color = color; + WireColor = wireColor; + Points = new[] {p1, p2, p3, p4}; + } + + public void Draw(bool wire = true) + { + if (wire) + { + GL.Begin(PrimitiveType.Lines); + GL.Color3(WireColor); + + for (var i = 0; i < 4; i++) + for (var j = i + 1; j < 4; j++) + { + GL.Vertex3(Points[i]); + GL.Vertex3(Points[j]); + } + + GL.End(); + } + else + { + GL.Begin(PrimitiveType.Triangles); + GL.Color3(Color); + + for (var i = 0; i < 4; i++) + for (var j = i + 1; j < 4; j++) + for (var k = j + 1; k < 4; k++) + { + GL.Vertex3(Points[i]); + GL.Vertex3(Points[j]); + GL.Vertex3(Points[k]); + } + + GL.End(); + } + } + + public Polygon Intersection(MVec3d blade, Vector3d pivot) + { + var pts = new Vector3d[4]; + for (var i = 0; i < 4; i++) + { + pts[i] = Points[i] - pivot; + } + + var a = blade * MVec3d.Unit123; + var sides = new double[4]; + for (var i = 0; i < 4; i++) + { + sides[i] = (a & ((pts[i] ^ blade) * ~blade)).E; // normal dot rejection + } + + var vecs = new List(4); + + for (var i = 0; i < 4; i++) + { + for (var j = i + 1; j < 4; j++) + { + if (sides[i] * sides[j] >= 0) continue; // both pts on same side + + var t = ((blade ^ pts[i]) * ~(blade ^ (pts[j] - pts[i]))).E; + var point = pts[i] + t * (pts[j] - pts[i]); + vecs.Add(point + pivot); + } + } + + return new Polygon(vecs.ToArray()); + } + } + + public class TetrahedronWindow : GameWindow + { + private Matrix4 _proj; + private Matrix4 _view; + + private double _t; + private bool _pause; + private bool _div; + private Tetrahedron _tetra; + private Polygon _polyg; + + private Polygon _square = new Polygon(new[] {new Vector3d(-1, -1, 0), new Vector3d(-1, 1, 0), new Vector3d(1, -1, 0), new Vector3d(1, 1, 0),}, Color.Red); + + protected override void OnLoad(EventArgs e) + { + base.OnLoad(e); + + X = (DisplayDevice.Default.Width - Width) / 2; + Y = (DisplayDevice.Default.Height - Height) / 2; + + _tetra = new Tetrahedron(new Vector3d(1, 1, 1), new Vector3d(-1, -1, 1), new Vector3d(-1, 1, -1), new Vector3d(1, -1, -1)); + _polyg = new Polygon(new[] {new Vector3d(-1, -1, 0), new Vector3d(-1, 1, 0), new Vector3d(1, -1, 0), new Vector3d(1, 1, 0)}); + + _view = Matrix4.LookAt(Vector3.Zero, -new Vector3(1, .6f, 1f), Vector3.UnitZ); + } + + protected override void OnRenderFrame(FrameEventArgs e) + { + base.OnRenderFrame(e); + + GL.Viewport(ClientRectangle); + + GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); + GL.PointSize(10f); + +// _view = Matrix4.LookAt(Vector3.Zero, new Vector3((float) Math.Cos(_t / 5), (float) Math.Sin(_t / 5), -1), Vector3.UnitZ); + + GL.PushMatrix(); + GL.MatrixMode(MatrixMode.Projection); + GL.LoadMatrix(ref _proj); + GL.MatrixMode(MatrixMode.Modelview); + GL.LoadMatrix(ref _view); + + GL.Enable(EnableCap.DepthTest); + _tetra.Draw(wire: false); + GL.Disable(EnableCap.DepthTest); + if (_div) + { + GL.PushMatrix(); + GL.MatrixMode(MatrixMode.Modelview); + GL.LoadMatrix(ref Matrix4d.Identity); + _square.Draw(); + _polyg.Draw(); + GL.PopMatrix(); + } + else + _polyg.Draw(); + + GL.Enable(EnableCap.DepthTest); + _tetra.Draw(wire: true); + + GL.PopMatrix(); + + SwapBuffers(); + } + + protected override void OnUpdateFrame(FrameEventArgs e) + { + base.OnUpdateFrame(e); + _proj = Matrix4.CreateOrthographic(6, 6f * Height / Width, -2, 2); + _t += e.Time; + + if (_pause) + return; + +// var b1 = MVec3d.Unit2; +// var b2 = MVec3d.Unit3; +// var pivot = new Vector3d(Math.Cos(_t), 0, 0); + + var b1 = MVec3d.Vector(0, Math.Cos(_t * .2), Math.Sin(_t * .5)); + var b2 = MVec3d.Vector(Math.Cos(_t * .6), 0, Math.Sin(_t * .4)); + var pivot = Vector3d.Zero; + +// var b1 = MVec3d.Vector(Math.Cos(_t/2), Math.Sin(_t/2), 0); +// var b2 = MVec3d.Unit3; +// var pivot = Vector3d.Zero; + + var blade = MVec3d.Outer(b1, b2); + +// var rot = MVec3d.Complex(_t / 2, MVec3d.Unit12); +// var b1 = rot * MVec3d.Unit2 * ~rot; +// var b2 = rot * MVec3d.Unit3 * ~rot; +// var pln = MVec3d.Outer(b1, b2); +// var blade = pln; +// var pivot = Vector3d.Zero; + + Console.Out.WriteLine("b1 = {0}", b1); + Console.Out.WriteLine("b2 = {0}", b2); + + _polyg = _tetra.Intersection(blade, pivot); + + if (_div) + { + for (var i = 0; i < _polyg.Points.Length; i++) + { + MVec3d p = _polyg.Points[i]; + var x = ((p & b1) * ~b1) & b1; + var y = ((p ^ b1) * ~b1) & ((b2 ^ b1) * ~b1); + _polyg.Points[i] = new Vector3d(x.E, y.E, 0); + } + } + } + + protected override void OnKeyDown(KeyboardKeyEventArgs e) + { + base.OnKeyDown(e); + + switch (e.Key) + { + case Key.Space: + _pause = !_pause; + break; + case Key.A: + _div = !_div; + break; + } + } + } +} \ No newline at end of file diff --git a/Tetrahedrons/Tetrahedrons.csproj b/Tetrahedrons/Tetrahedrons.csproj index eb7b4e3..a76912a 100644 --- a/Tetrahedrons/Tetrahedrons.csproj +++ b/Tetrahedrons/Tetrahedrons.csproj @@ -1,54 +1,62 @@  - - - Debug - AnyCPU - {D70DFA78-49B4-404B-BE1C-7CDC0DAE26FD} - Exe - Properties - Tetrahedrons - Tetrahedrons - v4.0 - 512 - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - - - - - - - - - - + \ No newline at end of file diff --git a/Tetrahedrons/packages.config b/Tetrahedrons/packages.config new file mode 100644 index 0000000..b2e9742 --- /dev/null +++ b/Tetrahedrons/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file