/// <summary> /// Hopf Link between two points on S^2. /// </summary> public static void HopfLink(StreamWriter sw, Vector3D s2_1, Vector3D s2_2, bool anti) { Vector3D[] circlePoints; string circleString; circlePoints = OneHopfCircleProjected(s2_1, anti); circleString = PovRay.EdgeSphereSweep(circlePoints, SizeFunc); sw.WriteLine(circleString); circlePoints = OneHopfCircleProjected(s2_2, anti); circleString = PovRay.EdgeSphereSweep(circlePoints, SizeFunc); sw.WriteLine(circleString); Mesh mesh = new Mesh(); Vector3D[] interpolated = S3.GeodesicPoints(s2_1, s2_2); for (int i = 0; i < interpolated.Length - 1; i++) { Vector3D v1 = interpolated[i]; Vector3D v2 = interpolated[i + 1]; Vector3D[] p1 = OneHopfCircleProjected(v1, anti); Vector3D[] p2 = OneHopfCircleProjected(v2, anti); for (int j = 0; j < p1.Length - 1; j++) { Mesh.Triangle t1 = new Mesh.Triangle(p1[j], p1[j + 1], p2[j]); Mesh.Triangle t2 = new Mesh.Triangle(p2[j], p1[j + 1], p2[j + 1]); mesh.Triangles.Add(t1); mesh.Triangles.Add(t2); } } PovRay.WriteMesh(sw, mesh, append: true); }
private static Mesh.Triangle Transform(Mesh.Triangle tri, System.Func <Vector3D, Vector3D> transform) { tri.a = transform(tri.a); tri.b = transform(tri.b); tri.c = transform(tri.c); return(tri); }
static public 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 ReverseTris(Mesh m) { for (int i = 0; i < m.Triangles.Count; i++) { Mesh.Triangle tri = m.Triangles[i]; // Ugh, damn you structs! tri.ChangeOrientation(); m.Triangles[i] = tri; } }
private static Mesh.Triangle[] ThickenSimple(Mesh.Triangle tri, Dictionary <Vector3D, Vector3D> normalMap) { List <Mesh.Triangle> result = new List <Mesh.Triangle>(); System.Tuple <Vector3D, Vector3D> a = ThickenSimple(tri.a, normalMap); System.Tuple <Vector3D, Vector3D> b = ThickenSimple(tri.b, normalMap); System.Tuple <Vector3D, Vector3D> c = ThickenSimple(tri.c, normalMap); result.Add(new Mesh.Triangle(a.Item1, c.Item1, b.Item1)); // For consistent orientations result.Add(new Mesh.Triangle(a.Item2, b.Item2, c.Item2)); return(result.ToArray()); }
/// <summary> /// Assumes we are in the UHS model. /// </summary> private static Mesh.Triangle[] Thicken(Mesh.Triangle tri, Sphere normal) { List <Mesh.Triangle> result = new List <Mesh.Triangle>(); System.Tuple <Vector3D, Vector3D> a = Thicken(tri.a, normal); System.Tuple <Vector3D, Vector3D> b = Thicken(tri.b, normal); System.Tuple <Vector3D, Vector3D> c = Thicken(tri.c, normal); if (a == null || b == null || c == null || a.Item1.DNE || a.Item2.DNE || b.Item1.DNE || b.Item2.DNE || c.Item1.DNE || c.Item2.DNE) { System.Console.WriteLine("bah"); } result.Add(new Mesh.Triangle(a.Item1, c.Item1, b.Item1)); // For consistent orientations result.Add(new Mesh.Triangle(a.Item2, b.Item2, c.Item2)); return(result.ToArray()); }
private static void WriteTriangle(StreamWriter sw, Mesh.Triangle tri) { Vector3D v1 = tri.b - tri.a; Vector3D v2 = tri.c - tri.a; // See http://en.wikipedia.org/wiki/STL_format#The_Facet_Normal // about how to do the normals correctly. //Vector3D n = v1.Cross( v2 ); Vector3D n = new Vector3D(0, 0, 1); n.Normalize(); sw.WriteLine(" facet normal {0:e6} {1:e6} {2:e6}", n.X, n.Y, n.Z); sw.WriteLine(" outer loop"); sw.WriteLine(" vertex {0:e6} {1:e6} {2:e6}", tri.a.X, tri.a.Y, tri.a.Z); sw.WriteLine(" vertex {0:e6} {1:e6} {2:e6}", tri.b.X, tri.b.Y, tri.b.Z); sw.WriteLine(" vertex {0:e6} {1:e6} {2:e6}", tri.c.X, tri.c.Y, tri.c.Z); sw.WriteLine(" endloop"); sw.WriteLine(" endfacet"); /* * " facet normal {0:e6} {1:e6} {2:e6}" -f $n * " outer loop" * " vertex {0:e6} {1:e6} {2:e6}" -f $v1 * " vertex {0:e6} {1:e6} {2:e6}" -f $v2 * " vertex {0:e6} {1:e6} {2:e6}" -f $v3 * " endloop" * " endfacet" * * facet normal 0.000000e+000 0.000000e+000 -1.000000e+000 * outer loop * vertex 4.500000e-001 4.500000e-001 4.500000e-001 * vertex 4.500000e-001 7.500000e-001 4.500000e-001 * vertex 7.500000e-001 7.500000e-001 4.500000e-001 * endloop * endfacet */ }
private static Mesh.Triangle Transform(Mesh.Triangle tri) { return(Transform(tri, Transform)); }
/// <summary> /// Create an STL file for a cell. /// Currently only works for cells with both hyperideal vertices and cells. /// </summary> public static void HoneycombHyperidealLegs(HoneycombDef def, int lod, Dictionary <Vector3D, H3.Cell> complete) { int p = def.P; int q = def.Q; int r = def.R; m_div = TextureHelper.SetLevels(lod); 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. 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 * m_div); points2 = H3Models.Ball.GeodesicPoints(e2.Start, e2.End, 2 * m_div); } else { points1 = H3Models.UHS.GeodesicPoints(e1.Start, e1.End, 2 * m_div); points2 = H3Models.UHS.GeodesicPoints(e2.Start, e2.End, 2 * m_div); } Sphere cellSphere = simplex[0]; Sphere vertexSphere = simplex[3]; // 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]; Vector3D[] arcPoints; if (i == points1.Length - 1) //if( false ) { // NOTE: This arc is not generally geodesic! // Or is it? arcPoints = ball ? H3Models.Ball.GeodesicPoints(p1, p2, m_div) : H3Models.UHS.GeodesicPoints(p1, p2, m_div); /*Circle3D arc = cellSphere.Intersection( vertexSphere ); * double angleTot = (p1 - arc.Center).AngleTo( p2 - arc.Center ); * arcPoints = Shapeways.CalcArcPoints( arc.Center, arc.Radius, p1, arc.Normal, -angleTot, 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, m_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.Triangle tri1 = new Mesh.Triangle(p1, p2, p3); Mesh.Triangle tri2 = new Mesh.Triangle(p2, p4, p3); // We need to thicken after reflecting around, otherwise we can't apply a min thickness. /*Sphere normal = cellSphere; * Mesh.Triangle[] thickened1 = Thicken( tri1, normal ); * Mesh.Triangle[] thickened2 = Thicken( tri2, normal ); * mesh.Triangles.AddRange( thickened1 ); * mesh.Triangles.AddRange( thickened2 );*/ mesh.Triangles.Add(tri1); mesh.Triangles.Add(tri2); } } // AuxPoints will be used for multiple things. // - The first two points are for an an that will fill the gap where there is a missing face. // - We'll also store the points for the 4 edges of our fundamental triangle. List <Vector3D> auxPoints = new List <Vector3D>(); { var edge1 = allPoints.First(); var edge2 = allPoints.Last(); List <Vector3D> edge3 = new List <Vector3D>(), edge4 = new List <Vector3D>(); for (int i = 0; i < allPoints.Count; i++) { edge3.Add(allPoints[i][0]); edge4.Add(allPoints[i][allPoints[i].Length - 1]); } edge4.Reverse(); auxPoints.Add(e1.Start); auxPoints.Add(e1.End); auxPoints.AddRange(edge1.Reverse()); auxPoints.AddRange(edge2); auxPoints.AddRange(edge3); auxPoints.AddRange(edge4); } Vector3D cen = HoneycombPaper.InteriorPointBall; /* Reorientation code. Move this elsewhere. * * // Face centered orientation. * bool faceCentered = false; * if( faceCentered ) * SimplexCalcs.PrepForFacetCentering( p, q, simplex, ref cen ); * * Mobius mUHS = SimplexCalcs.FCOrientMobius( p, q ); * Mobius mBall = HoneycombPaper.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. H3.Cell[] simplices = GenCell(simplex, mesh, cen, auxPoints.ToArray(), ball); // Existing cells take precedence. foreach (H3.Cell c in simplices) { Vector3D t = c.Center; H3.Cell dummy; if (!complete.TryGetValue(t, out dummy)) { complete[t] = c; } } }
public static void S3BiHelicoid() { double cutoff = 8.0; int div = 500; Matrix4D mat = Matrix4D.MatrixToRotateinCoordinatePlane(1 * Math.PI / 4, 0, 3); Mesh m1 = S3Helicoid(div, mat, reciprocal: false); //m1.Triangles = m1.Triangles.Where( t => t.a.Abs() < cutoff && t.b.Abs() < cutoff && t.c.Abs() < cutoff ).ToList(); Mesh m2 = S3Helicoid(div, mat, reciprocal: true); //m2.Triangles = m2.Triangles.Where( t => t.a.Abs() < cutoff && t.b.Abs() < cutoff && t.c.Abs() < cutoff ).ToList(); Mesh m3 = new Mesh(); System.Action <bool> addCore = recip => { List <Vector3D> circlePoints = new List <Vector3D>(); double aOffset = 2 * Math.PI / div; for (int i = 0; i <= div; i++) { double x = Math.Sin(aOffset * i); double y = Math.Cos(aOffset * i); circlePoints.Add(recip ? new Vector3D(x, y) : new Vector3D(0, 0, x, y)); } // partial transform to R3 here. circlePoints = circlePoints.Select(p => { p = mat.RotateVector(p); p = Sterographic.S3toR3(p); return(p); }).ToList(); List <Vector3D> ePoints = new List <Vector3D>(); List <double> eRadii = new List <double>(); foreach (Vector3D pNE in circlePoints) { Sphere sphere = SphereFuncBall(Geometry.Spherical, pNE, false); ePoints.Add(sphere.Center); eRadii.Add(sphere.Radius); } Shapeways shapeways = new Shapeways(); shapeways.AddClosedCurve(ePoints.ToArray(), eRadii.ToArray()); m3.Append(shapeways.Mesh); }; addCore(false); addCore(true); for (int i = 0; i < m3.Triangles.Count; i++) { Mesh.Triangle t = m3.Triangles[i]; m3.Triangles[i] = Transform(t, SphericalModels.StereoToEquidistant); } string filename = "helicoid.stl"; File.Delete(filename); using (StreamWriter sw = File.AppendText(filename)) { STL.AppendMeshToSTL(m1, sw); STL.AppendMeshToSTL(m2, sw); STL.AppendMeshToSTL(m3, sw); } //HelicoidHelper( thinMesh, boundaryPoints ); }