Skip to content

Commit

Permalink
Added "Lay Flat" functionality
Browse files Browse the repository at this point in the history
Right click on any polygon on an object and a context menu appears with
the option "Set as Bottom Face".  Selecting this will move the object
such that the bottom face is flat on the plane.  Also it's got a nice
animation!
  • Loading branch information
xenovacivus committed Dec 22, 2017
1 parent d929a43 commit 76de84c
Show file tree
Hide file tree
Showing 3 changed files with 206 additions and 23 deletions.
32 changes: 32 additions & 0 deletions GUI/Drawing3D.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Expand All @@ -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;
Expand Down
135 changes: 125 additions & 10 deletions GUI/TriangleMeshGUI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,13 @@ class TriangleMeshGUI : TriangleMesh, IOpenGLDrawable, IClickable3D
private Vector3 mouseHoverPoint = Vector3.Zero;
private List<TabsGUI> tabs = new List<TabsGUI>();
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()
{
Expand Down Expand Up @@ -62,8 +69,10 @@ public Vector3 Offset
}
}

private float lastToolRadius = 0.1f;
public void GenerateTabPaths(float toolRadius)
{
lastToolRadius = toolRadius;
tabs.Clear();
try
{
Expand All @@ -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
}
Expand All @@ -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;
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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)
Expand All @@ -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;
Expand Down Expand Up @@ -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();
}
}
}
}
62 changes: 49 additions & 13 deletions Geometry/TriangleMesh.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
/// </summary>
public class TriangleMesh
{
private float epsilon = 0.0001f; // Distance between vertices considered to be unique - set to a value valid for inches.
protected List<Vector3> vertices;
private List<Vector3> 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<TriangleIndices> triangles;
private List<Edge> segments;
private Matrix4 transformation = Matrix4.Identity;

public class TriangleIndices
{
Expand Down Expand Up @@ -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<Triangle> Triangles
Expand All @@ -99,7 +103,7 @@ public IEnumerable<Triangle> 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]);
}
}
}
Expand All @@ -108,15 +112,16 @@ public IEnumerable<Vector3> Vertices
{
get
{
yield return parentMesh.vertices[a];
yield return parentMesh.vertices[b];
yield return parentMesh.verticesTransformed[a];
yield return parentMesh.verticesTransformed[b];
}
}
}

public TriangleMesh()
{
vertices = new List<Vector3>();
verticesTransformed = new List<Vector3>();
triangles = new List<TriangleIndices>();
segments = new List<Edge>();
}
Expand Down Expand Up @@ -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);
}
}
}

/// <summary>
/// Add a new triangle with vertices a, b, and c.
/// Vertices must be specified in counter-clockwise order when viewed from front.
Expand Down Expand Up @@ -273,7 +304,10 @@ public IEnumerable<Triangle> 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]);
}
}
}
Expand All @@ -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;
}
Expand Down

0 comments on commit 76de84c

Please sign in to comment.