diff --git a/GUI/Drawing3D.cs b/GUI/Drawing3D.cs index 9d04691..d6b0f53 100644 --- a/GUI/Drawing3D.cs +++ b/GUI/Drawing3D.cs @@ -150,6 +150,11 @@ void HandleMouseUp(object sender, MouseEventArgs e) var item = new MenuItem("Delete", new EventHandler(objectDeleteClicked)); item.Tag = clickedObject; menu.MenuItems.Add(item); + + item = new MenuItem("Set as Bottom Face", new EventHandler(objectSetAsBottomFaceClicked)); + item.Tag = clickedObject; + menu.MenuItems.Add(item); + menu.Show(this, e.Location); } } @@ -164,6 +169,33 @@ void HandleMouseUp(object sender, MouseEventArgs e) } } + private void objectSetAsBottomFaceClicked(object sender, EventArgs e) + { + var item = sender as MenuItem; + if (item != null) + { + IClickable3D toModify = item.Tag as IClickable3D; + var triangleMesh = item.Tag as TriangleMeshGUI; + if (triangleMesh != null) + { + Ray pointer = viewport.GetPointerRay(mouseDownLocation); + + // If we rotate the object, tabs will become invalid. + foreach (var tab in triangleMesh.Tabs) + { + objects.Remove(tab); + } + + triangleMesh.SetClickedFaceAsBottom(pointer); + + foreach (var tab in triangleMesh.Tabs) + { + objects.Add(tab); + } + } + } + } + void objectDeleteClicked(object sender, EventArgs e) { var item = sender as MenuItem; diff --git a/GUI/TriangleMeshGUI.cs b/GUI/TriangleMeshGUI.cs index b29dc7d..9c5cb77 100644 --- a/GUI/TriangleMeshGUI.cs +++ b/GUI/TriangleMeshGUI.cs @@ -34,6 +34,13 @@ class TriangleMeshGUI : TriangleMesh, IOpenGLDrawable, IClickable3D private Vector3 mouseHoverPoint = Vector3.Zero; private List tabs = new List(); private Vector3 offset = Vector3.Zero; + + // Variables for animated rotation/translation + bool isTransforming = false; + double secondsToTransform = 1.0f; + DateTime transformBeginTime = DateTime.MinValue; + Matrix4 targetTransform; + Matrix4 fromTransform; public TriangleMeshGUI() : base() { @@ -62,8 +69,10 @@ public Vector3 Offset } } + private float lastToolRadius = 0.1f; public void GenerateTabPaths(float toolRadius) { + lastToolRadius = toolRadius; tabs.Clear(); try { @@ -77,8 +86,9 @@ public void GenerateTabPaths(float toolRadius) tabs.Add(new TabsGUI(line, toolRadius, true)); } } - catch (Exception) + catch (Exception ex) { + Console.WriteLine("Error generating tab paths: " + ex.Message); } this.Offset = offset; // Force the offset update in the tabs } @@ -100,6 +110,33 @@ public void Draw() GL.PushMatrix(); GL.Translate(offset); + if (isTransforming) + { + double deltaTime = (DateTime.Now - transformBeginTime).TotalSeconds; + if (deltaTime > secondsToTransform) + { + isTransforming = false; + this.Transformation = targetTransform; + this.RefreshDisplayLists(); + } + else + { + float linear = (float)(deltaTime / secondsToTransform); + float interp = linear < 0.5f ? 2 * linear * linear : linear * (4 - 2 * linear) - 1.0f; + Quaternion source = fromTransform.ExtractRotation(); + Quaternion target = targetTransform.ExtractRotation(); + Quaternion slerp = Quaternion.Slerp(source, target, (float)interp); + + Vector3 translatePart = fromTransform.ExtractTranslation() * (1-interp) + targetTransform.ExtractTranslation() * interp; + + Matrix4 x = Matrix4.CreateFromQuaternion(slerp) * Matrix4.CreateTranslation(translatePart); + + //Matrix4 x = targetTransform * (float)interp + fromTransform * (float)(1 - interp); + + GL.MultMatrix(ref x); + } + } + Color triangleColor = Color.Green; Color lineColor = Color.Green; Color badLineColor = Color.White; @@ -274,6 +311,19 @@ public void Draw() GL.Enable(EnableCap.Lighting); } + //// Highlight the closest triangle - debugging + //if (hoveredTriangle != null) + //{ + // GL.LineWidth(2); + // GL.Color3(0, 0, 0); + // GL.Begin(PrimitiveType.LineLoop); + // GL.Vertex3(hoveredTriangle.A); + // GL.Vertex3(hoveredTriangle.B); + // GL.Vertex3(hoveredTriangle.C); + // GL.End(); + // GL.LineWidth(1); + //} + // Mesh analysis testing... //GL.PushMatrix(); @@ -359,7 +409,7 @@ void IClickable3D.MouseDown(Ray pointer) { mouseDownPoint = mouseHoverPoint; mouseDownOffset = offset; - Console.WriteLine("Mouse Down TriMeshGUI"); + //Console.WriteLine("Mouse Down TriMeshGUI"); } void IClickable3D.MouseUp(Ray pointer) @@ -368,27 +418,33 @@ void IClickable3D.MouseUp(Ray pointer) private bool hovered = false; //Edge closestEdge = null; + Triangle hoveredTriangle = null; float IClickable3D.DistanceToObject(Ray pointer) { Ray adjustedPointer = new Ray(pointer.Start - offset, pointer.Direction); hovered = false; float distance = float.PositiveInfinity; - foreach (Triangle t in base.Triangles) + hoveredTriangle = null; + + if (!isTransforming) { - TriangleRayIntersect i = new TriangleRayIntersect(t, adjustedPointer); - if (i.Intersects) + foreach (Triangle t in base.Triangles) { - float d = (adjustedPointer.Start - i.Point).Length; - if (d < distance) + TriangleRayIntersect i = new TriangleRayIntersect(t, adjustedPointer); + if (i.Intersects) { - distance = d; - mouseHoverPoint = i.Point; + float d = (adjustedPointer.Start - i.Point).Length; + if (d < distance) + { + distance = d; + mouseHoverPoint = i.Point; + hoveredTriangle = t; + } } } } - // Remember the closest edge - debugging //float x = 0; //closestEdge = null; @@ -421,5 +477,64 @@ void IClickable3D.MouseMove(Ray pointer) Vector3 point = plane.Distance(adjustedPointer) * adjustedPointer.Direction + adjustedPointer.Start; Offset = mouseDownOffset + point - mouseDownPoint; } + + internal void SetClickedFaceAsBottom(Ray pointer) + { + Ray adjustedPointer = new Ray(pointer.Start - offset, pointer.Direction); + hovered = false; + float distance = float.PositiveInfinity; + Vector3 clickedPoint = Vector3.Zero; + Plane clickedPlane = null; + + var triangleIndex = 0; + var count = 0; + foreach (Triangle t in base.Triangles) + { + TriangleRayIntersect i = new TriangleRayIntersect(t, adjustedPointer); + if (i.Intersects) + { + float d = (adjustedPointer.Start - i.Point).Length; + if (d < distance) + { + distance = d; + clickedPoint = i.Point; + clickedPlane = t.Plane; + triangleIndex = count; + } + } + count++; + } + + if (clickedPlane != null) + { + fromTransform = Transformation; + this.Transformation = Matrix4.Identity; + Vector3 originalCenter = (this.MinPoint + this.MaxPoint) * 0.5f; + + clickedPlane = Triangles.ElementAt(triangleIndex).Plane; + var up = Vector3.UnitZ; + if (Math.Abs(clickedPlane.Normal.Z) > 0.95) + { + up = Vector3.UnitX; + } + Matrix4 rotate = Matrix4.LookAt(Vector3.Zero, clickedPlane.Normal, up); + rotate = rotate.ClearTranslation(); + + // We want the original clicked point to be at the same X and Y, and at Z = 0 + Vector3 fix = Vector3.Transform(clickedPlane.Point, rotate); + Vector3 newCenter = Vector3.Transform(originalCenter, rotate); + Console.WriteLine("Original Center: " + originalCenter); + rotate = Matrix4.Mult(rotate, Matrix4.CreateTranslation(originalCenter.X - newCenter.X, originalCenter.Y - newCenter.Y, -fix.Z)); + + transformBeginTime = DateTime.Now; + targetTransform = rotate; + this.isTransforming = true; + + this.Transformation = targetTransform; + this.GenerateTabPaths(lastToolRadius); + this.Transformation = Matrix4.Identity; + this.RefreshDisplayLists(); + } + } } } diff --git a/Geometry/TriangleMesh.cs b/Geometry/TriangleMesh.cs index fcae225..018d927 100644 --- a/Geometry/TriangleMesh.cs +++ b/Geometry/TriangleMesh.cs @@ -29,15 +29,19 @@ namespace Geometry /// Class for building and storing a mesh of triangles. Each triangle contains pointers to adjacent triangles /// through the edges, and edges can also be enumerated. All operations are safe at any point, and iterating /// over edges and triangles is guarenteed accurate and complete if no add or clean operation occurs. + /// A transformation can be applied/modified which will apply to all vertices and cached values (min and max point). + /// The transformation will always be applied to the original locations of the vertices. /// public class TriangleMesh { private float epsilon = 0.0001f; // Distance between vertices considered to be unique - set to a value valid for inches. protected List vertices; + private List verticesTransformed; // List of vertices with transformation applied private Vector3 minPoint = new Vector3(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity); private Vector3 maxPoint = new Vector3(float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity); protected List triangles; private List segments; + private Matrix4 transformation = Matrix4.Identity; public class TriangleIndices { @@ -81,14 +85,14 @@ internal bool Matches(int a, int b) internal float Length() { - Vector3 v1 = parentMesh.vertices[a]; - Vector3 v2 = parentMesh.vertices[b]; + Vector3 v1 = parentMesh.verticesTransformed[a]; + Vector3 v2 = parentMesh.verticesTransformed[b]; return (v1 - v2).Length; } public LineSegment LineSegment { - get { return new LineSegment(parentMesh.vertices[a], parentMesh.vertices[b]); } + get { return new LineSegment(parentMesh.verticesTransformed[a], parentMesh.verticesTransformed[b]); } } public IEnumerable Triangles @@ -99,7 +103,7 @@ public IEnumerable Triangles while (--num >= 0) { var triangleIndices = triangles[num]; - yield return new Triangle(parentMesh.vertices[triangleIndices.a], parentMesh.vertices[triangleIndices.b], parentMesh.vertices[triangleIndices.c]); + yield return new Triangle(parentMesh.verticesTransformed[triangleIndices.a], parentMesh.verticesTransformed[triangleIndices.b], parentMesh.verticesTransformed[triangleIndices.c]); } } } @@ -108,8 +112,8 @@ public IEnumerable Vertices { get { - yield return parentMesh.vertices[a]; - yield return parentMesh.vertices[b]; + yield return parentMesh.verticesTransformed[a]; + yield return parentMesh.verticesTransformed[b]; } } } @@ -117,6 +121,7 @@ public IEnumerable Vertices public TriangleMesh() { vertices = new List(); + verticesTransformed = new List(); triangles = new List(); segments = new List(); } @@ -189,6 +194,32 @@ public float Epsilon set { epsilon = value; } } + public Matrix4 Transformation + { + get { return transformation; } + set + { + transformation = value; + // Update all the transformed vertices and min/max values. + minPoint = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue); + maxPoint = new Vector3(float.MinValue, float.MinValue, float.MinValue); + var vertexCount = vertices.Count; + while (--vertexCount >= 0) + { + var vertex = Vector3.Transform(vertices[vertexCount], transformation); + verticesTransformed[vertexCount] = vertex; + + minPoint.X = Math.Min(vertex.X, minPoint.X); + minPoint.Y = Math.Min(vertex.Y, minPoint.Y); + minPoint.Z = Math.Min(vertex.Z, minPoint.Z); + + maxPoint.X = Math.Max(vertex.X, maxPoint.X); + maxPoint.Y = Math.Max(vertex.Y, maxPoint.Y); + maxPoint.Z = Math.Max(vertex.Z, maxPoint.Z); + } + } + } + /// /// Add a new triangle with vertices a, b, and c. /// Vertices must be specified in counter-clockwise order when viewed from front. @@ -273,7 +304,10 @@ public IEnumerable Triangles while (--num >= 0) { var triangleIndices = triangles[num]; - yield return new Triangle(vertices[triangleIndices.a], vertices[triangleIndices.b], vertices[triangleIndices.c]); + yield return new Triangle( + verticesTransformed[triangleIndices.a], + verticesTransformed[triangleIndices.b], + verticesTransformed[triangleIndices.c]); } } } @@ -295,14 +329,16 @@ private int AddVertex(Vector3 vertex) { index = vertices.Count; vertices.Add(vertex); + var transformed = Vector3.Transform(vertex, transformation); + verticesTransformed.Add(transformed); - minPoint.X = Math.Min(minPoint.X, vertex.X); - minPoint.Y = Math.Min(minPoint.Y, vertex.Y); - minPoint.Z = Math.Min(minPoint.Z, vertex.Z); + minPoint.X = Math.Min(minPoint.X, transformed.X); + minPoint.Y = Math.Min(minPoint.Y, transformed.Y); + minPoint.Z = Math.Min(minPoint.Z, transformed.Z); - maxPoint.X = Math.Max(maxPoint.X, vertex.X); - maxPoint.Y = Math.Max(maxPoint.Y, vertex.Y); - maxPoint.Z = Math.Max(maxPoint.Z, vertex.Z); + maxPoint.X = Math.Max(maxPoint.X, transformed.X); + maxPoint.Y = Math.Max(maxPoint.Y, transformed.Y); + maxPoint.Z = Math.Max(maxPoint.Z, transformed.Z); } return index; }