/// <summary> /// Make an edge mesh of a regular tiling. /// </summary> public static Mesh MakeEdgeMesh( int p, int q ) { Mesh mesh = new Mesh(); int maxTiles = 400; Tiling tiling = new Tiling(); TilingConfig config = new TilingConfig( p, q, maxTiles: maxTiles ); config.Shrink = 0.6; tiling.GenerateInternal( config ); TilingConfig boundaryConfig = new TilingConfig( 14, 7, maxTiles: 1 ); boundaryConfig.Shrink = 1.01; Tile boundary = Tiling.CreateBaseTile( boundaryConfig ); AddSymmetryTriangles( mesh, tiling, boundary.Drawn ); //AddSymmetryTriangles( mesh, tiling, null ); return mesh; HashSet<Vector3D> completed = new HashSet<Vector3D>(); int count = 0; foreach( Tile tile in tiling.Tiles ) { MeshEdges( mesh, tile, completed, null ); count++; if( count >= maxTiles ) break; } return mesh; }
/// <summary> /// Transform a mesh in the Poincare model to Dini's surface. /// </summary> public static Mesh Dini( Mesh mesh ) { System.Func<Vector3D,Vector3D> transform = v => { //v = DiskToUpper( v ); //v.Y = Math.Log( v.Y ); //if( v.Y < 1 || v.Y > 10 ) // return Infinity.InfinityVector; //if( v.X < -Math.PI || v.X > Math.PI ) //if( v.X < -3*Math.PI || v.X > 3*Math.PI ) // return Infinity.InfinityVector; //v.Y = Math.Log( v.Y ); //return v; return Dini( v ); }; Mesh result = new Mesh(); for( int i=0; i<mesh.Triangles.Count; i++ ) { Vector3D a = transform( mesh.Triangles[i].a ); Vector3D b = transform( mesh.Triangles[i].b ); Vector3D c = transform( mesh.Triangles[i].c ); if( Infinity.IsInfinite( a ) || Infinity.IsInfinite( b ) || Infinity.IsInfinite( c ) ) continue; result.Triangles.Add( new Mesh.Triangle( a, b, c ) ); } return result; }
public static void DrawTriangle( Mesh.Triangle triangle, Graphics g, ImageSpace i ) { using( Pen pen = new Pen( Color.Black, 1.0f ) ) { g.DrawLine( pen, VecToPoint( triangle.a, i ), VecToPoint( triangle.b, i ) ); g.DrawLine( pen, VecToPoint( triangle.b, i ), VecToPoint( triangle.c, i ) ); g.DrawLine( pen, VecToPoint( triangle.c, i ), VecToPoint( triangle.a, i ) ); } }
private static void WriteMesh( StreamWriter sw, Mesh mesh, string fileName, bool append = false ) { Vector3D[] verts, normals; List<int[]> faces; mesh.BuildIndexes( out verts, out normals, out faces ); // http://www.povray.org/documentation/view/3.6.0/68/ // http://www.povray.org/documentation/view/3.6.1/293/ // We are going to use mesh2 so that we can have per-vertex coloring. sw.WriteLine( "mesh2 {" ); // Vertices sw.WriteLine( " vertex_vectors {" ); sw.WriteLine( " " + verts.Length + "," ); foreach( Vector3D v in verts ) sw.WriteLine( " " + FormatVec( v ) + "," ); sw.WriteLine( " }" ); // Normals for smooth triangles sw.WriteLine( " normal_vectors {" ); sw.WriteLine( " " + verts.Length + "," ); foreach( Vector3D v in normals ) { v.Normalize(); sw.WriteLine( " " + FormatVec( v ) + "," ); } sw.WriteLine( " }" ); // Triangles sw.WriteLine( " face_indices {" ); sw.WriteLine( " " + faces.Count + "," ); foreach( int[] face in faces ) sw.WriteLine( string.Format( " <{0},{1},{2}>,", face[0], face[1], face[2] ) ); sw.WriteLine( " }" ); sw.WriteLine( "texture {tex2}" ); sw.WriteLine( "}" ); }
public static void WriteMesh( Mesh mesh, string fileName, bool append = false ) { if( append ) { using( StreamWriter sw = File.AppendText( fileName ) ) WriteMesh( sw, mesh, fileName, append ); } else { using( StreamWriter sw = File.CreateText( fileName ) ) WriteMesh( sw, mesh, fileName, append ); } }
private static H3.Cell[] GenTruss( Sphere[] simplex, Mesh mesh, Vector3D cen, bool ball ) { // We don't want to include the first mirror (which reflects across cells). Sphere[] mirrors = simplex.Skip( 1 ).ToArray(); Sphere[] allMirrors = simplex.ToArray(); // Simplices will be the "cells" in Recurse.CalcCells. H3.Cell.Facet[] simplexFacets = simplex.Select( m => new H3.Cell.Facet( m ) ).ToArray(); H3.Cell startingCell = new H3.Cell( simplexFacets ); startingCell.Center = cen; //FCOrient( startingCell ); //return null; startingCell = startingCell.Clone(); // So our mirrors don't get munged after we reflect around later. H3.Cell[] simplices = Recurse.CalcCells( mirrors, new H3.Cell[] { startingCell }, new Recurse.Settings() { Ball = ball } ); //H3.Cell[] simplices = new H3.Cell[] { startingCell }; // Subsets //return simplices.Where( s => s.Depths[0] == 1 ).ToArray(); //return simplices.ToArray(); List<H3.Cell> final = new List<H3.Cell>(); final.AddRange( simplices ); // Add in other truss cells. Recurse.Settings settings = new Recurse.Settings(); foreach( int[] reflections in TrussReflections() ) foreach( H3.Cell c in simplices ) { H3.Cell clone = c.Clone(); foreach( int r in reflections ) clone.Reflect( allMirrors[r] ); if( Recurse.CellOk( clone, settings ) ) final.Add( clone ); } return final.ToArray(); }
public Shapeways() { Mesh = new Mesh(); Div = 24; }
private static void CheckAndAdd( Mesh mesh, Triangle tri, Polygon boundary ) { if( Check( tri, boundary ) ) mesh.Triangles.Add( tri ); }
private static void MeshEdges( Mesh mesh, Tile tile, HashSet<Vector3D> completed, Polygon boundary ) { for( int i=0; i<tile.Boundary.Segments.Count; i++ ) { Segment boundarySeg = tile.Boundary.Segments[i]; Segment d1 = tile.Drawn.Segments[i]; if( completed.Contains( boundarySeg.Midpoint ) ) continue; // Find the incident segment. Segment seg2 = null, d2 = null; foreach( Tile incident in tile.EdgeIncidences ) { for( int j=0; j<incident.Boundary.Segments.Count; j++ ) { if( boundarySeg.Midpoint == incident.Boundary.Segments[j].Midpoint ) { seg2 = incident.Boundary.Segments[j]; d2 = incident.Drawn.Segments[j]; break; } } if( seg2 != null ) break; } // Found our incident edge? bool foundIncident = seg2 != null; if( !foundIncident ) seg2 = d2 = boundarySeg; // Do the endpoints mismatch? if( boundarySeg.P1 != seg2.P1 ) { Segment clone = d2.Clone(); clone.Reverse(); d2 = clone; } // Add the two vertices (careful of orientation). if( foundIncident ) { CheckAndAdd( mesh, new Mesh.Triangle( boundarySeg.P1, d1.P1, d2.P1 ), boundary ); CheckAndAdd( mesh, new Mesh.Triangle( boundarySeg.P2, d2.P2, d1.P2 ), boundary ); } int num = 1 + (int)(d1.Length * m_divisions); Vector3D[] list1 = d1.Subdivide( num ); Vector3D[] list2 = d2.Subdivide( num ); for( int j=0; j<num; j++ ) { CheckAndAdd( mesh, new Mesh.Triangle( list1[j], list1[j + 1], list2[j + 1] ), boundary ); CheckAndAdd( mesh, new Mesh.Triangle( list2[j], list1[j], list2[j + 1] ), boundary ); } completed.Add( boundarySeg.Midpoint ); } }
private static void AddSymmetryTriangles( Mesh mesh, Tiling tiling, Polygon boundary ) { // Assume template centered at the origin. Polygon template = tiling.Tiles.First().Boundary; List<Triangle> templateTris = new List<Triangle>(); foreach( Segment seg in template.Segments ) { int num = 1 + (int)(seg.Length * m_divisions); Vector3D a = new Vector3D(); Vector3D b = seg.P1; Vector3D c = seg.Midpoint; Vector3D centroid = ( a + b + c ) / 3; Polygon poly = new Polygon(); Segment segA = Segment.Line( new Vector3D(), seg.P1 ); Segment segB = seg.Clone(); segB.P2 = seg.Midpoint; Segment segC = Segment.Line( seg.Midpoint, new Vector3D() ); poly.Segments.Add( segA ); poly.Segments.Add( segB ); poly.Segments.Add( segC ); Vector3D[] coords = TextureHelper.TextureCoords( poly, Geometry.Hyperbolic ); int[] elements = TextureHelper.TextureElements( 3, LOD: 3 ); for( int i = 0; i < elements.Length / 3; i++ ) { int idx1 = i * 3; int idx2 = i * 3 + 1; int idx3 = i * 3 + 2; Vector3D v1 = coords[elements[idx1]]; Vector3D v2 = coords[elements[idx2]]; Vector3D v3 = coords[elements[idx3]]; templateTris.Add( new Triangle( v1, v2, v3 ) ); } /* // Need to shrink a little, so we won't // get intersections among neighboring faces. a = Shrink( a, centroid ); b = Shrink( b, centroid ); c = Shrink( c, centroid ); Vector3D[] list = seg.Subdivide( num * 2 ); list[0] = b; list[list.Length / 2] = c; for( int i = 0; i < list.Length / 2; i++ ) templateTris.Add( new Triangle( centroid, list[i], list[i + 1] ) ); for( int i = num - 1; i >= 0; i-- ) templateTris.Add( new Triangle( centroid, a + (c - a) * (i + 1) / num, a + (c - a) * i / num ) ); for( int i = 0; i < num; i++ ) templateTris.Add( new Triangle( centroid, a + (b - a) * i / num, a + (b - a) * (i + 1) / num ) ); */ } foreach( Tile tile in tiling.Tiles ) { Vector3D a = tile.Boundary.Segments[0].P1; Vector3D b = tile.Boundary.Segments[1].P1; Vector3D c = tile.Boundary.Segments[2].P1; Mobius m = new Mobius(); if( tile.Isometry.Reflected ) m.MapPoints( template.Segments[0].P1, template.Segments[1].P1, template.Segments[2].P1, c, b, a ); else m.MapPoints( template.Segments[0].P1, template.Segments[1].P1, template.Segments[2].P1, a, b, c ); foreach( Triangle tri in templateTris ) { Triangle transformed = new Triangle( m.Apply( tri.a ), m.Apply( tri.b ), m.Apply( tri.c ) ); CheckAndAdd( mesh, transformed, boundary ); } } }
public Mesh Clone() { Mesh clone = new Mesh(); clone.Triangles = Triangles.Select( t => t ).ToList(); return clone; }
internal static void TakePointsBack( Mesh mesh, Mobius m, H3.Settings settings ) { for( int i=0; i<mesh.Triangles.Count; i++ ) { mesh.Triangles[i] = new Mesh.Triangle( m.ApplyToQuaternion( mesh.Triangles[i].a ), m.ApplyToQuaternion( mesh.Triangles[i].b ), m.ApplyToQuaternion( mesh.Triangles[i].c ) ); /*if( Infinity.IsInfinite( mesh.Triangles[i].a ) || Infinity.IsInfinite( mesh.Triangles[i].b ) || Infinity.IsInfinite( mesh.Triangles[i].c ) ) System.Diagnostics.Debugger.Break();*/ } // Take all points back to Ball, if needed. if( !settings.Halfspace ) { for( int i=0; i<mesh.Triangles.Count; i++ ) { mesh.Triangles[i] = new Mesh.Triangle( H3Models.UHSToBall( mesh.Triangles[i].a ), H3Models.UHSToBall( mesh.Triangles[i].b ), H3Models.UHSToBall( mesh.Triangles[i].c ) ); } } }
/// <summary> /// Used to generate a regular cell as a set of simplices and save to a Pov-ray file. /// This will work for non-finite cells. /// </summary> private static H3.Cell[] GenCell( Sphere[] simplex, Mesh mesh, Vector3D cen, bool ball, bool dual ) { // We don't want to include the first mirror (which reflects across cells). Sphere[] mirrors = simplex.Skip( 1 ).ToArray(); if( dual ) mirrors = new Sphere[] { simplex[0], simplex[1], simplex[2] }; Sphere[] allMirrors = simplex.ToArray(); mirrors = simplex.ToArray(); // Simplices will be the "cells" in Recurse.CalcCells. H3.Cell.Facet[] simplexFacets = simplex.Select( m => new H3.Cell.Facet( m ) ).ToArray(); H3.Cell startingCell = new H3.Cell( simplexFacets ); startingCell.Center = cen; startingCell.Mesh = mesh; //FCOrient( startingCell ); startingCell = startingCell.Clone(); // So our mirrors don't get munged after we reflect around later. H3.Cell[] simplices = Recurse.CalcCells( mirrors, new H3.Cell[] { startingCell }, new Recurse.Settings() { Ball = ball } ); //H3.Cell[] simplices = new H3.Cell[] { startingCell }; // Layers. int layer = 0; return simplices.Where( s => s.Depths[0] <= layer /*&& s.Depths[0] == 3 && s.Depths[1] == 3*/ ).ToArray(); //return simplices.ToArray(); }
/// <summary> /// Create an STL file for a cell. /// This will be a 2D surface of triangles, which will need to be thickened using an external program. /// Currently only works for cells with both hyperideal vertices and cells. /// </summary> private static void CreateCellSTL( HoneycombDef imageData ) { int p = imageData.P; int q = imageData.Q; int r = imageData.R; bool ball = false; Sphere[] simplex = SimplexCalcs.Mirrors( p, q, r, moveToBall: ball ); H3.Cell.Edge[] edges; if( ball ) edges = SimplexCalcs.SimplexEdgesBall( p, q, r ); else edges = SimplexCalcs.SimplexEdgesUHS( p, q, r ); // Two edges of one simplex facet. int div = 5; H3.Cell.Edge e1 = edges[2]; H3.Cell.Edge e2 = edges[3]; Vector3D[] points1, points2; if( ball ) { points1 = H3Models.Ball.GeodesicPoints( e1.Start, e1.End, 2 * div ); points2 = H3Models.Ball.GeodesicPoints( e2.Start, e2.End, 2 * div ); } else { points1 = H3Models.UHS.GeodesicPoints( e1.Start, e1.End, 2 * div ); points2 = H3Models.UHS.GeodesicPoints( e2.Start, e2.End, 2 * div ); } Sphere cellSphere = simplex[0]; // Because one vertex the facet triangle is hyperideal, it will actually look like a square. List<Vector3D[]> allPoints = new List<Vector3D[]>(); for( int i = 0; i < points1.Length; i++ ) { Vector3D p1 = points1[i]; Vector3D p2 = points2[i]; // NOTE: This arc is not generally geodesic! Vector3D[] arcPoints; if( i == points1.Length - 1 ) { arcPoints = ball ? H3Models.Ball.GeodesicPoints( p1, p2, div ) : H3Models.UHS.GeodesicPoints( p1, p2, div ); } else { Circle3D c = Circle3D.FromCenterAnd2Points( cellSphere.Center, p1, p2 ); double angleTot = (p1 - c.Center).AngleTo( p2 - c.Center ); arcPoints = Shapeways.CalcArcPoints( cellSphere.Center, cellSphere.Radius, p1, c.Normal, -angleTot, div ); } //Vector3D[] arcPoints = new Vector3D[] { p1, p2 }; allPoints.Add( arcPoints ); } // Create the triangles for the patch. Mesh mesh = new Mesh(); for( int i = 0; i < allPoints.Count - 1; i++ ) { Vector3D[] arc1 = allPoints[i]; Vector3D[] arc2 = allPoints[i + 1]; for( int j = 0; j < arc1.Length - 1; j++ ) { // Points of (i,j) box; Vector3D p1 = arc1[j]; Vector3D p2 = arc2[j]; Vector3D p3 = arc1[j + 1]; Vector3D p4 = arc2[j + 1]; mesh.Triangles.Add( new Mesh.Triangle( p1, p2, p3 ) ); mesh.Triangles.Add( new Mesh.Triangle( p2, p4, p3 ) ); } } // Face centered orientation. // XXX - We need to do this prior to mesh generation, so the mesh isn't stretched out by these transformations. bool faceCentered = true; Vector3D cen = CellCenBall; if( faceCentered ) SimplexCalcs.PrepForFacetCentering( p, q, simplex, ref cen ); Mobius mUHS = SimplexCalcs.FCOrientMobius( p, q ); Mobius mBall = FCOrientMobius( H3Models.UHSToBall( cellSphere ) ); simplex = simplex.Select( s => { s = H3Models.UHSToBall( s ); H3Models.TransformInBall2( s, mBall ); return s; } ).ToArray(); { for( int i = 0; i < mesh.Triangles.Count; i++ ) { Mesh.Triangle tri = mesh.Triangles[i]; if( faceCentered ) { tri.a = mUHS.ApplyToQuaternion( tri.a ); tri.b = mUHS.ApplyToQuaternion( tri.b ); tri.c = mUHS.ApplyToQuaternion( tri.c ); } tri.a = H3Models.UHSToBall( tri.a ); tri.b = H3Models.UHSToBall( tri.b ); tri.c = H3Models.UHSToBall( tri.c ); if( faceCentered ) { tri.a = H3Models.TransformHelper( tri.a, mBall ); tri.b = H3Models.TransformHelper( tri.b, mBall ); tri.c = H3Models.TransformHelper( tri.c, mBall ); } mesh.Triangles[i] = tri; } if( faceCentered ) cen = H3Models.TransformHelper( cen, mBall ); } // Now we need to reflect around this fundamental patch. bool dual = false; H3.Cell[] simplicesFinal = GenCell( simplex, mesh, cen, ball, dual ); System.IO.File.Delete( "cell.stl" ); foreach( H3.Cell cell in simplicesFinal ) STL.AppendMeshToSTL( cell.Mesh, "cell.stl" ); //STL.AppendMeshToSTL( simplicesFinal[0].Mesh, "cell.stl" ); }