private static void HopfFibration(Tiling tiling) { int segDivisions = 10; Shapeways mesh = new Shapeways(); HashSet <Vector3D> done = new HashSet <Vector3D>(); foreach (Tile tile in tiling.Tiles) { foreach (Segment seg in tile.Boundary.Segments) { if (done.Contains(seg.Midpoint)) { continue; } // Subdivide the segment, and project points to S2. Vector3D[] points = seg.Subdivide(segDivisions).Select(v => Spherical2D.PlaneToSphere(v)).ToArray(); foreach (Vector3D point in points) { Vector3D[] circlePoints = OneHopfCircle(point); ProjectAndAddS3Points(mesh, circlePoints, shrink: false); } done.Add(seg.Midpoint); } } STL.SaveMeshToSTL(mesh.Mesh, @"D:\p4\R3\sample\out1.stl"); }
public static void EdgesToStl( H3.Cell.Edge[] edges ) { Shapeways mesh = new Shapeways(); int divisions = 10; foreach( H3.Cell.Edge edge in edges ) { Segment seg = Segment.Line( Sterographic.R3toS3( edge.Start ), Sterographic.R3toS3( edge.End ) ); Vector3D[] points = seg.Subdivide( divisions ); ProjectAndAddS3Points( mesh, points ); } for( int i = 0; i < mesh.Mesh.Triangles.Count; i++ ) { mesh.Mesh.Triangles[i] = new Mesh.Triangle( SphericalModels.GnomonicToStereo( mesh.Mesh.Triangles[i].a ), SphericalModels.GnomonicToStereo( mesh.Mesh.Triangles[i].b ), SphericalModels.GnomonicToStereo( mesh.Mesh.Triangles[i].c ) ); } STL.SaveMeshToSTL( mesh.Mesh, @"output.stl" ); }
public static void GenEuclidean() { Shapeways mesh = new Shapeways(); HashSet <H3.Cell.Edge> completed = new HashSet <H3.Cell.Edge>(); int count = 5; for (int i = -count; i < count; i++) { for (int j = -count; j < count; j++) { for (int k = -count; k < count; k++) { // Offset double io = i + 0.5; double jo = j + 0.5; double ko = k + 0.5; // Do every edge emanating from this point. AddEuclideanEdge(mesh, completed, new Vector3D(io, jo, ko), new Vector3D(io + 1, jo, ko)); AddEuclideanEdge(mesh, completed, new Vector3D(io, jo, ko), new Vector3D(io - 1, jo, ko)); AddEuclideanEdge(mesh, completed, new Vector3D(io, jo, ko), new Vector3D(io, jo + 1, ko)); AddEuclideanEdge(mesh, completed, new Vector3D(io, jo, ko), new Vector3D(io, jo - 1, ko)); AddEuclideanEdge(mesh, completed, new Vector3D(io, jo, ko), new Vector3D(io, jo, ko + 1)); AddEuclideanEdge(mesh, completed, new Vector3D(io, jo, ko), new Vector3D(io, jo, ko - 1)); } } } STL.SaveMeshToSTL(mesh.Mesh, "d:\\temp\\434.stl"); }
private static void HopfFibration(Tiling tiling) { int segDivisions = 10; int circleDivisions = 125; Shapeways mesh = new Shapeways(); HashSet <Vector3D> done = new HashSet <Vector3D>(); foreach (Tile tile in tiling.Tiles) { foreach (Segment seg in tile.Boundary.Segments) { if (done.Contains(seg.Midpoint)) { continue; } // Subdivide the segment, and project points to S2. Vector3D[] points = seg.Subdivide(segDivisions).Select(v => Spherical2D.PlaneToSphere(v)).ToArray(); foreach (Vector3D point in points) { // Get the hopf circle and add to mesh. // http://en.wikipedia.org/wiki/Hopf_fibration#Explicit_formulae double a = point.X; double b = point.Y; double c = point.Z; double factor = 1 / (Math.Sqrt(1 + c)); if (Tolerance.Equal(c, -1)) { continue; } List <Vector3D> circlePoints = new List <Vector3D>(); double angleInc = 2 * Math.PI / circleDivisions; double angle = 0; for (int i = 0; i <= circleDivisions; i++) { double sinTheta = Math.Sin(angle); double cosTheta = Math.Cos(angle); circlePoints.Add(new Vector3D( (1 + c) * cosTheta, a * sinTheta - b * cosTheta, a * cosTheta + b * sinTheta, (1 + c) * sinTheta)); angle += angleInc; } bool shrink = false; ProjectAndAddS3Points(mesh, circlePoints.ToArray(), shrink); } done.Add(seg.Midpoint); } } STL.SaveMeshToSTL(mesh.Mesh, @"D:\p4\R3\sample\out1.stl"); }
/// <summary> /// Add a finite (truncated) banana to our mesh. Passed in edge should be in Ball model. /// </summary> public static void AddBanana( Shapeways mesh, Vector3D e1, Vector3D e2, H3.Settings settings ) { Vector3D e1UHS = H3Models.BallToUHS( e1 ); Vector3D e2UHS = H3Models.BallToUHS( e2 ); // Endpoints of the goedesic on the z=0 plane. Vector3D z1, z2; H3Models.UHS.GeodesicIdealEndpoints( e1UHS, e2UHS, out z1, out z2 ); // XXX - Do we want to do a better job worrying about rotation here? // (multiply by complex number with certain imaginary part as well) //Vector3D z3 = ( z1 + z2 ) / 2; //if( Infinity.IsInfinite( z3 ) ) // z3 = new Vector3D( 1, 0 ); Vector3D z3 = new Vector3D( Math.E, Math.PI ); // This should vary the rotations a bunch. // Find the Mobius we need. // We'll do this in two steps. // (1) Find a mobius taking z1,z2 to origin,inf // (2) Deal with scaling e1 to a height of 1. Mobius m1 = new Mobius( z1, z3, z2 ); Vector3D e1UHS_transformed = m1.ApplyToQuaternion( e1UHS ); double scale = 1.0 / e1UHS_transformed.Z; Mobius m2 = Mobius.Scale( scale ); Mobius m = m2 * m1; // Compose them (multiply in reverse order). Vector3D e2UHS_transformed = m.ApplyToQuaternion( e2UHS ); // Make our truncated cone. // For regular tilings, we really would only need to do this once for a given LOD. List<Vector3D> points = new List<Vector3D>(); double logHeight = Math.Log( e2UHS_transformed.Z ); if( logHeight < 0 ) throw new System.Exception( "impl issue" ); int div1, div2; H3Models.Ball.LOD_Finite( e1, e2, out div1, out div2, settings ); double increment = logHeight / div1; for( int i=0; i<=div1; i++ ) { double h = increment * i; // This is to keep different bananas from sharing exactly coincident vertices. double tinyOffset = 0.001; if( i == 0 ) h -= tinyOffset; if( i == div1 ) h += tinyOffset; Vector3D point = new Vector3D( 0, 0, Math.Exp( h ) ); points.Add( point ); } Shapeways tempMesh = new Shapeways(); tempMesh.Div = div2; tempMesh.AddCurve( points.ToArray(), v => H3Models.UHS.SizeFunc( v, settings.AngularThickness ) ); // Unwind the transforms. TakePointsBack( tempMesh.Mesh, m.Inverse(), settings ); mesh.Mesh.Triangles.AddRange( tempMesh.Mesh.Triangles ); }
/// <summary> /// Function I was using for lamp project, probably a bit out of date. /// </summary> public static void AddToMeshLamp( Shapeways mesh, Vector3D v1, Vector3D v2 ) { // need to get these from CalcBallArc Vector3D center = Vector3D.DneVector(); double radius = double.NaN; Vector3D normal = Vector3D.DneVector(); double angleTot = double.NaN; double length1 = Scale( 2.1 ); // 12-end piece //double length1 = Scale( 1.6 ); // 6-end piece //double length1 = Scale( 1.4 ); // 4-end piece double length2 = Scale( 0.5 ); double outerRadStart = Scale( 0.0625 / 2 ); double outerRadEnd = Scale( 0.25 / 2 ); System.Func<Vector3D, double> outerSizeFunc = v => { double angle = (v1 - center).AngleTo( v - center ); double len = radius * angle; return outerRadStart + (outerRadEnd - outerRadStart) * (len / length1); }; System.Func<Vector3D, double> outerSizeFunc2 = v => { double angle = (v2 - center).AngleTo( v - center ); double len = radius * angle; return outerRadStart + (outerRadEnd - outerRadStart) * (len / length1); }; System.Func<Vector3D, double> innerSizeFunc = v => { // Very slightly bigger than 1/8 inch OD. return Scale( 0.13 / 2 ); }; Vector3D[] outerPoints = Shapeways.CalcArcPoints( center, radius, v1, normal, length1 / radius ); Vector3D[] innerPoints = Shapeways.CalcArcPoints( center, radius, outerPoints[outerPoints.Length - 1], normal * -1, length2 / radius ); mesh.AddCornucopia( outerPoints, outerSizeFunc, innerPoints, innerSizeFunc ); outerPoints = Shapeways.CalcArcPoints( center, radius, v2, normal * -1, length1 / radius ); innerPoints = Shapeways.CalcArcPoints( center, radius, outerPoints[outerPoints.Length - 1], normal, length2 / radius ); mesh.AddCornucopia( outerPoints, outerSizeFunc2, innerPoints, innerSizeFunc ); m_inventory.AddRod( Rod.Create( radius, angleTot ) ); }
/// <summary> /// Helper to project points from S3 -> S2, then add an associated curve. /// </summary> private static void ProjectAndAddS3Points(Shapeways mesh, Vector3D[] pointsS3, bool shrink) { // Project to S3, then to R3. List <Vector3D> projected = new List <Vector3D>(); foreach (Vector3D v in pointsS3) { v.Normalize(); Vector3D c = v.ProjectTo3DSafe(1.0); // Pull R3 into a smaller open disk. if (shrink) { double mag = Math.Atan(c.Abs()); c.Normalize(); c *= mag; } projected.Add(c); } System.Func <Vector3D, double> sizeFunc = v => { // Constant thickness. // return 0.08; double sphericalThickness = 0.002; double abs = v.Abs(); if (shrink) { abs = Math.Tan(abs); // The unshrunk abs. } // The thickness at this vector location. double result = Spherical2D.s2eNorm(Spherical2D.e2sNorm(abs) + sphericalThickness) - abs; if (shrink) { result *= Math.Atan(abs) / abs; // shrink it back down. } return(result); }; mesh.AddCurve(projected.ToArray(), sizeFunc); }
private static void AddEuclideanEdge(Shapeways mesh, HashSet <H3.Cell.Edge> completed, Vector3D start, Vector3D end) { double cutoff = 1.75; if (Math.Abs(start.X) > cutoff || Math.Abs(start.Y) > cutoff || Math.Abs(start.Z) > cutoff || Math.Abs(end.X) > cutoff || Math.Abs(end.Y) > cutoff || Math.Abs(end.Z) > cutoff) { return; } if (mesh != null) { AddEuclideanEdgeToMesh(mesh, completed, start, end); return; } completed.Add(new H3.Cell.Edge(start, end)); }
public static void GenEuclidean() { Shapeways mesh = new Shapeways(); HashSet <H3.Cell.Edge> completed = new HashSet <H3.Cell.Edge>(new H3.Cell.EdgeEqualityComparer()); int count = 2; for (int i = -count; i < count; i++) { for (int j = -count; j < count; j++) { for (int k = -count; k < count; k++) { // Offset double io = i + 0.5; double jo = j + 0.5; double ko = k + 0.5; Vector3D p = new Vector3D(io, jo, ko); // Add a sphere for this point. Sphere s = new Sphere() { Center = p, Radius = 0.05 }; mesh.AddSphere(s.Center, s.Radius); // Do every edge emanating from this point. AddEuclideanEdge(mesh, completed, p, new Vector3D(io + 1, jo, ko)); AddEuclideanEdge(mesh, completed, p, new Vector3D(io - 1, jo, ko)); AddEuclideanEdge(mesh, completed, p, new Vector3D(io, jo + 1, ko)); AddEuclideanEdge(mesh, completed, p, new Vector3D(io, jo - 1, ko)); AddEuclideanEdge(mesh, completed, p, new Vector3D(io, jo, ko + 1)); AddEuclideanEdge(mesh, completed, p, new Vector3D(io, jo, ko - 1)); } } } STL.SaveMeshToSTL(mesh.Mesh, "434.stl"); //PovRay.WriteEdges( new PovRay.Parameters() { AngularThickness = .05 }, Geometry.Euclidean, completed.ToArray(), "434.pov", append: false ); }
private static void AddEuclideanEdge(Shapeways mesh, HashSet <H3.Cell.Edge> completed, Vector3D start, Vector3D end) { H3.Cell.Edge edge = new H3.Cell.Edge(start, end); if (completed.Contains(edge)) { return; } Shapeways tempMesh = new Shapeways(); Segment seg = Segment.Line(start, end); int div = 20 - (int)(start.Abs() * 4); if (div < 1) { div = 1; } tempMesh.AddCurve(seg.Subdivide(div), .05); Transform(tempMesh.Mesh); mesh.Mesh.Triangles.AddRange(tempMesh.Mesh.Triangles); completed.Add(edge); }
private static void ShapewaysPolytopes() { VEF loader = new VEF(); loader.Load( @"C:\Users\roice\Documents\projects\vZome\VefProjector\data\24cell-cellFirst.vef" ); int divisions = 25; Shapeways mesh = new Shapeways(); //int count = 0; foreach( Edge edge in loader.Edges ) { Segment seg = Segment.Line( loader.Vertices[edge.V1].ConvertToReal(), loader.Vertices[edge.V2].ConvertToReal() ); Vector3D[] points = seg.Subdivide( divisions ); bool shrink = true; ProjectAndAddS3Points( mesh, points, shrink ); //if( count++ > 10 ) // break; } STL.SaveMeshToSTL( mesh.Mesh, @"D:\p4\R3\sample\out1.stl" ); }
/// <summary> /// Helper to project points from S3 -> S2, then add an associated curve. /// XXX - Not completely correct. /// </summary> private static void ProjectAndAddS3Points( Shapeways mesh, Vector3D[] pointsS3, bool shrink ) { List<Vector3D> projected = new List<Vector3D>(); foreach( Vector3D v in pointsS3 ) { v.Normalize(); Vector3D c = v.ProjectTo3DSafe( 1.0 ); // Pull R3 into a smaller open disk. if( shrink ) { double mag = Math.Atan( c.Abs() ); c.Normalize(); c *= mag; } projected.Add( c ); } System.Func<Vector3D, double> sizeFunc = v => { // Constant thickness. // return 0.08; double sphericalThickness = 0.05; double abs = v.Abs(); if( shrink ) abs = Math.Tan( abs ); // The unshrunk abs. // The thickness at this vector location. double result = Spherical2D.s2eNorm( Spherical2D.e2sNorm( abs ) + sphericalThickness ) - abs; if( shrink ) result *= Math.Atan( abs ) / abs; // shrink it back down. return result; }; mesh.AddCurve( projected.ToArray(), sizeFunc ); }
private static void ProjectAndAddS3Points( Shapeways mesh, Vector3D[] pointsS3 ) { double r = 0.02; List<Vector3D> projected = new List<Vector3D>(); List<double> radii = new List<double>(); foreach( Vector3D v in pointsS3 ) { v.Normalize(); Vector3D c = v.ProjectTo3DSafe( 1.0 ); Vector3D p; double d; H3Models.Ball.DupinCyclideSphere( c, r, Geometry.Spherical, out p, out d ); projected.Add( p ); radii.Add( d ); } mesh.AddCurve( projected.ToArray(), radii.ToArray() ); }
/// <summary> /// Our approach will be: /// (1) Generate a portion of one cell. /// (2) Reflect all facets in the central facet, to get things filled-in inside the central facet. (Trim small edges here?) /// (3) Copy this region around the plane, and go back to step (2) if density is not high enough. /// (4) Map to Ball, trimming edges that become too small. /// NOTE: All verts are on the boundary, so we can reflect around // in circles on the plane at infinity, rather than spheres. /// </summary> public static void GenerateExotic( EHoneycomb honeycomb, H3.Settings settings ) { settings.AngularThickness = 0.17; Tiling tiling; Tile baseTile; GetAssociatedTiling( honeycomb, out tiling, out baseTile ); List<H3.Cell.Edge> edges = new List<H3.Cell.Edge>(); foreach( Segment seg in baseTile.Boundary.Segments ) edges.Add( new H3.Cell.Edge( seg.P1, seg.P2 ) ); settings.Position = Polytope.Projection.FaceCentered; double scale = 1; Vector3D offset = new Vector3D(); if( settings.Position == Polytope.Projection.FaceCentered ) { scale = FaceCenteredScale( baseTile.VertexCircle ); offset = new Vector3D(); } else if( settings.Position == Polytope.Projection.EdgeCentered ) { scale = EdgeCenteredScale( baseTile ); offset = baseTile.Boundary.Segments[0].Midpoint; } int iterations = m_params.Iterations; for( int i=0; i<iterations; i++ ) edges = DoOneStep( edges, tiling, baseTile.VertexCircle ); edges = CopyAndProject( edges, tiling, scale, offset ); if( m_params.RemoveDangling ) { Dictionary<H3.Cell.Edge,int> edgeDict = edges.ToDictionary( e => e, e => 1 ); H3.RemoveDanglingEdgesRecursive( edgeDict ); edges = edgeDict.Keys.ToList(); } string outputFileName = H3.m_baseDir + Honeycomb.String( honeycomb, false ); System.IO.File.Delete( outputFileName ); if( m_params.Output == H3.Output.STL ) { outputFileName += ".stl"; // Now mesh the edges. Shapeways mesh = new Shapeways(); foreach( H3.Cell.Edge edge in edges ) { // Append to the file vs. writing out all at once because I was running out of memory otherwise. mesh = new Shapeways(); int div; H3Models.Ball.LODThin( edge.Start, edge.End, out div ); mesh.Div = div; H3.Util.AddToMeshInternal( mesh, edge.Start, edge.End ); mesh.Mesh.Scale( settings.Scale ); STL.AppendMeshToSTL( mesh.Mesh, outputFileName ); } } else { outputFileName += ".pov"; PovRay.WriteH3Edges( new PovRay.Parameters() { AngularThickness = settings.AngularThickness, Halfspace = settings.Halfspace, ThinEdges = settings.ThinEdges, }, edges.ToArray(), outputFileName ); } }
private static void HopfFibration( Tiling tiling ) { int segDivisions = 10; Shapeways mesh = new Shapeways(); HashSet<Vector3D> done = new HashSet<Vector3D>(); foreach( Tile tile in tiling.Tiles ) foreach( Segment seg in tile.Boundary.Segments ) { if( done.Contains( seg.Midpoint ) ) continue; // Subdivide the segment, and project points to S2. Vector3D[] points = seg.Subdivide( segDivisions ).Select( v => Spherical2D.PlaneToSphere( v ) ).ToArray(); foreach( Vector3D point in points ) { Vector3D[] circlePoints = OneHopfCircle( point ); ProjectAndAddS3Points( mesh, circlePoints, shrink: false ); } done.Add( seg.Midpoint ); } STL.SaveMeshToSTL( mesh.Mesh, @"D:\p4\R3\sample\out1.stl" ); }
public static void Generate(EHoneycomb honeycomb, H3.Settings settings) { // XXX - Block the same as in H3. Share code better. H3.Cell template = null; { int p, q, r; Honeycomb.PQR(honeycomb, out p, out q, out r); // Get data we need to generate the honeycomb. Polytope.Projection projection = Polytope.Projection.FaceCentered; double phi, chi, psi; H3.HoneycombData(honeycomb, out phi, out chi, out psi); H3.SetupCentering(honeycomb, settings, phi, chi, psi, ref projection); Tiling tiling = new Tiling(); TilingConfig config = new TilingConfig(p, q); tiling.GenerateInternal(config, projection); H3.Cell first = new H3.Cell(p, H3.GenFacets(tiling)); first.ToSphere(); // Work in ball model. first.ScaleToCircumSphere(1.0); first.ApplyMobius(settings.Mobius); template = first; } // Center Vector3D center = template.Center; // Face H3.Cell.Facet facet = template.Facets[0]; Sphere s = H3Models.Ball.OrthogonalSphereInterior(facet.Verts[0], facet.Verts[1], facet.Verts[2]); Vector3D face = s.Center; face.Normalize(); face *= DistOriginToOrthogonalSphere(s.Radius); // Edge Circle3D c; H3Models.Ball.OrthogonalCircleInterior(facet.Verts[0], facet.Verts[1], out c); Vector3D edge = c.Center; edge.Normalize(); edge *= DistOriginToOrthogonalSphere(c.Radius); // Vertex Vector3D vertex = facet.Verts[0]; Tet fundamental = new Tet(center, face, edge, vertex); // Recurse. int level = 1; Dictionary <Tet, int> completedTets = new Dictionary <Tet, int>(new TetEqualityComparer()); completedTets.Add(fundamental, level); List <Tet> tets = new List <Tet>(); tets.Add(fundamental); ReflectRecursive(level, tets, completedTets, settings); Shapeways mesh = new Shapeways(); foreach (KeyValuePair <Tet, int> kvp in completedTets) { if (Utils.Odd(kvp.Value)) { continue; } Tet tet = kvp.Key; // XXX - really want sphere surfaces here. mesh.Mesh.Triangles.Add(new Mesh.Triangle(tet.Verts[0], tet.Verts[1], tet.Verts[2])); mesh.Mesh.Triangles.Add(new Mesh.Triangle(tet.Verts[0], tet.Verts[3], tet.Verts[1])); mesh.Mesh.Triangles.Add(new Mesh.Triangle(tet.Verts[0], tet.Verts[2], tet.Verts[3])); mesh.Mesh.Triangles.Add(new Mesh.Triangle(tet.Verts[1], tet.Verts[3], tet.Verts[2])); } mesh.Mesh.Scale(settings.Scale); STL.SaveMeshToSTL(mesh.Mesh, H3.m_baseDir + "fundamental" + ".stl"); }
/// <summary> /// Add an ideal banana to our mesh. Passed in edge should be in Ball model. /// </summary> public static void AddIdealBanana( Shapeways mesh, Vector3D e1, Vector3D e2, H3.Settings settings ) { Vector3D z1 = H3Models.BallToUHS( e1 ); Vector3D z2 = H3Models.BallToUHS( e2 ); // Mobius taking z1,z2 to origin,inf Complex dummy = new Complex( Math.E, Math.PI ); Mobius m = new Mobius( z1, dummy, z2 ); // Make our truncated cone. We need to deal with the two ideal endpoints specially. List<Vector3D> points = new List<Vector3D>(); double logHeight = 2; // XXX - magic number, and going to cause problems for infinity checks if too big. int div1, div2; H3Models.Ball.LOD_Ideal( e1, e2, out div1, out div2, settings ); double increment = logHeight / div1; for( int i=-div1; i<=div1; i+=2 ) points.Add( new Vector3D( 0, 0, Math.Exp( increment * i ) ) ); Shapeways tempMesh = new Shapeways(); tempMesh.Div = div2; System.Func<Vector3D, double> sizeFunc = v => H3Models.UHS.SizeFunc( v, settings.AngularThickness ); //Mesh.OpenCylinder... pass in two ideal endpoints? tempMesh.AddCurve( points.ToArray(), sizeFunc, new Vector3D(), Infinity.InfinityVector ); // Unwind the transforms. TakePointsBack( tempMesh.Mesh, m.Inverse(), settings ); mesh.Mesh.Triangles.AddRange( tempMesh.Mesh.Triangles ); }
/// <summary> /// A helper for adding a sphere. center should be passed in the ball model. /// The approach is similar to how we do the bananas below. /// </summary> public static void AddSphere( Shapeways mesh, Vector3D center, H3.Settings settings ) { Vector3D centerUHS = H3Models.BallToUHS( center ); // Find the Mobius we need. // We'll do this in two steps. // (1) Find a mobius taking center to (0,0,h). // (2) Deal with scaling to a height of 1. Vector3D flattened = centerUHS; flattened.Z = 0; Mobius m1 = new Mobius( flattened, Complex.One, Infinity.InfinityVector ); Vector3D centerUHS_transformed = m1.ApplyToQuaternion( centerUHS ); double scale = 1.0 / centerUHS_transformed.Z; Mobius m2 = new Mobius( scale, Complex.Zero, Complex.Zero, Complex.One ); Mobius m = m2 * m1; // Compose them (multiply in reverse order). // Add the sphere at the Ball origin. // It will *always* be generated with the same radius. Shapeways tempMesh = new Shapeways(); tempMesh.AddSphere( new Vector3D(), H3Models.Ball.SizeFunc( new Vector3D(), settings.AngularThickness ) ); // Unwind the transforms. for( int i=0; i<tempMesh.Mesh.Triangles.Count; i++ ) { tempMesh.Mesh.Triangles[i] = new Mesh.Triangle( H3Models.BallToUHS( tempMesh.Mesh.Triangles[i].a ), H3Models.BallToUHS( tempMesh.Mesh.Triangles[i].b ), H3Models.BallToUHS( tempMesh.Mesh.Triangles[i].c ) ); } Banana.TakePointsBack( tempMesh.Mesh, m.Inverse(), settings ); mesh.Mesh.Triangles.AddRange( tempMesh.Mesh.Triangles ); }