public void Generate() { poly = poly.Transform(Position, Rotation, Scale); var mesh = PolyMeshBuilder.BuildMeshFromConwayPoly(poly, false, null, ColorMethod); GetComponent <MeshFilter>().mesh = mesh; }
public void Generate() { poly = JohnsonPoly.Polygon(sides); poly = poly.Loft(new OpParams(0.5f)); var gableSel = FaceSelections.Existing; for (int i = 0; i < Iterations; i++) { poly = poly.Gable(new OpParams(Bud1, Bud2, FaceSelections.Existing)); poly.ClearTags(); var newFaces = poly.GetFaceSelection(FaceSelections.New); poly.TagFaces("fork1", filter: x => x.index == newFaces.FirstOrDefault(), introvert: true); poly.TagFaces("fork2", filter: x => x.index == newFaces.LastOrDefault(), introvert: true); poly = poly.Loft(new OpParams(BranchLengthScale, BranchLength, FaceSelections.New)); // poly = poly.Loft(new OpParams((5f-i)/10f, (BranchLength-i)/BranchLengthScale, FaceSelections.New)); poly = poly.FaceSlide(new OpParams(amount1, direction1 * i, selectByTags: "fork1")); poly = poly.FaceSlide(new OpParams(amount2, direction2 * i, selectByTags: "fork2")); } // poly = poly.Transform(Position, Rotation, Scale); var mesh = PolyMeshBuilder.BuildMeshFromConwayPoly(poly, false, null, ColorMethod); GetComponent <MeshFilter>().mesh = mesh; }
public void Generate() { var wythoff = new WythoffPoly(PolyType, PrismP, PrismQ); wythoff.BuildFaces(); poly = new ConwayPoly(wythoff); if (ApplyOp) { var o1 = new OpParams { valueA = op1Amount1, valueB = op1Amount2, facesel = op1Facesel }; poly = poly.ApplyOp(op1, o1); var o2 = new OpParams { valueA = op2Amount1, valueB = op2Amount2, facesel = op2Facesel }; poly = poly.ApplyOp(op2, o2); } if (Canonicalize) { poly = poly.Canonicalize(0.01, 0.01); } poly = poly.LoftAlongProfile(new OpParams(DomeDepth, DomeHeight, domeFaceSel), Profile, Shear, shearDirection, flipProfile: FlipProfile); // poly = poly.Transform(Position, Rotation, Scale); var mesh = PolyMeshBuilder.BuildMeshFromConwayPoly(poly, false, Colors, ColorMethod); GetComponent <MeshFilter>().mesh = mesh; }
public void Generate() { poly = WatermanPoly.Build(1f, root, c, MergeFaces); if (ApplyOps) { var o1 = new OpParams { valueA = op1Amount1, valueB = op1Amount2, facesel = op1Facesel }; poly = poly.ApplyOp(op1, o1); var o2 = new OpParams { valueA = op2Amount1, valueB = op2Amount2, facesel = op2Facesel }; poly = poly.ApplyOp(op2, o2); } if (Canonicalize) { poly = poly.Canonicalize(0.1, 0.1); } poly = poly.Transform(Position, Rotation, Scale); var mesh = PolyMeshBuilder.BuildMeshFromConwayPoly(poly, false, Colors, ColorMethod); GetComponent <MeshFilter>().mesh = mesh; }
public void Generate() { var wythoff = new WythoffPoly(PolyType, PrismP, PrismQ); wythoff.BuildFaces(); poly = new ConwayPoly(wythoff); if (ApplyOp) { var o1 = new OpParams { valueA = op1Amount1, valueB = op1Amount2, facesel = op1Facesel }; poly = poly.ApplyOp(op1, o1); } if (Canonicalize) { poly = poly.Canonicalize(0.01, 0.01); } poly = poly.Transform(Position, Rotation, Scale); var pos = SlicePosition; if (animateSlice > 0) { pos.y = .1f + transform.position.y + ((Time.time % 2f) * animateSlice); } var result = poly.SliceByPlane(new Plane(Quaternion.Euler(SliceRotation) * Vector3.up, pos), Cap, includeTop, includeBottom); poly = result.bottom; var top = result.top.Transform(topTransform); poly.Append(top); // var sliceWythoff = new WythoffPoly(PolyTypes.Cube, 3, 3); // sliceWythoff.BuildFaces(); // var slicePoly = new ConwayPoly(wythoff); // slicePoly = slicePoly.Transform(SlicePosition, SliceRotation, Vector3.one * SliceScale); // var result = poly.SliceByPoly(slicePoly, Cap); // poly = result.outside; // var top = result.outside.Transform(topTransform); if (Weld) { poly = poly.Weld(0.0001f); } if (ApplyOp) { var o2 = new OpParams { valueA = op2Amount1, valueB = op2Amount2, facesel = op2Facesel }; poly = poly.ApplyOp(op2, o2); } var mesh = PolyMeshBuilder.BuildMeshFromConwayPoly(poly, false, null, ColorMethod); GetComponent <MeshFilter>().mesh = mesh; }
private ConwayPoly MakeAntenna(ConwayPoly spaceship) { var allNew = spaceship.FaceselToFaceFilterFunc(FaceSelections.AllNew); var facingUp = spaceship.FaceselToFaceFilterFunc(FaceSelections.FacingUp); Func <FilterParams, bool> newAndFacingUp = x => allNew(x) && facingUp(x); var topSurfaces = spaceship.FaceKeep(new OpParams { filterFunc = newAndFacingUp }); var antennaGroup = new ConwayPoly(); var antenna = JohnsonPoly.Pyramid(4); foreach (var face in topSurfaces.Faces) { float radius = Random.Range(0.01f, 0.05f); float height = Random.Range(0.25f, 2f); float offset = Random.value < .5 ? 0 : Random.value < 0.5 ? .5f : -.5f; antennaGroup.Append(antenna.Transform( face.GetPolarPoint(90, offset), Vector3.zero, new Vector3(radius, height, radius) )); } return(antennaGroup); }
void Rebuild() { // Build the initial poly polymorphSkinnedMeshRenderer = gameObject.GetComponent <SkinnedMeshRenderer>(); poly = JohnsonPoly.Build(JohnsonPolyType, sides); poly.Recenter(); poly = poly.ApplyOp(PreMorph.op, new OpParams(PreMorph.opAmount1, PreMorph.opAmount2, PreMorph.opFacesel)); // Build the mesh without any morph ops var baseMesh = MakeMorphTarget(0, 0); baseMesh.ClearBlendShapes(); baseMesh = AddBlendShapeFrameFromMesh("Base", baseMesh, baseMesh); baseMesh.RecalculateNormals(); baseMesh.RecalculateTangents(); // Build the morphed meshes for (var i = 0; i < Morphs.Count; i++) { var morphedMesh = MakeMorphTarget(i, 1); baseMesh = AddBlendShapeFrameFromMesh(i.ToString(), baseMesh, morphedMesh); // baseMesh.RecalculateNormals(); // baseMesh.RecalculateTangents(); } polymorphSkinnedMeshRenderer.sharedMesh = baseMesh; initialized = true; }
public void Build(int p, int q) { P = p; Q = q; switch (ShapeType) { case PolyHydraEnums.ShapeTypes.Uniform: var wythoff = new WythoffPoly(PolyType, P, Q); wythoff.BuildFaces(); poly = new ConwayPoly(wythoff); break; case PolyHydraEnums.ShapeTypes.Johnson: poly = JohnsonPoly.Build(JohnsonPolyType, P); break; case PolyHydraEnums.ShapeTypes.Grid: poly = Grids.Grids.MakeGrid(GridType, GridShape, P, Q); break; case PolyHydraEnums.ShapeTypes.Other: poly = JohnsonPoly.BuildOther(OtherPolyType, P, Q); break; case PolyHydraEnums.ShapeTypes.Waterman: poly = WatermanPoly.Build(1f, P, Q, true); break; } }
public void Generate() { poly = Grids.Grids.MakeGrid(GridType, GridShape, width, depth); poly.ClearTags(); poly.TagFaces("#FFFFFF", filter: whiteFilter); poly.TagFaces("#000000", filter: blackFilter); if (ApplyOp) { var o1 = new OpParams { valueA = op1Amount1, valueB = op1Amount2, facesel = op1Facesel }; poly = poly.ApplyOp(op1, o1); var o2 = new OpParams { valueA = op2Amount1, valueB = op2Amount2, facesel = op2Facesel }; poly = poly.ApplyOp(op2, o2); var o3 = new OpParams { valueA = op3Amount1, valueB = op3Amount2, facesel = op3Facesel }; poly = poly.ApplyOp(op3, o3); } var mesh = PolyMeshBuilder.BuildMeshFromConwayPoly(poly, false, meshColors, ColorMethod); GetComponent <MeshFilter>().mesh = mesh; }
public virtual void Generate() { float animMultiplier = Mathf.Abs(Mathf.Sin(Time.time * AnimationSpeed) * AnimationAmount); if (ApplyOp) { for (var i = 0; i < op1iterations; i++) { poly = poly.ApplyOp(op1, new OpParams { valueA = op1Amount1 * (op1Animate ? animMultiplier : 1), valueB = op1Amount2, facesel = op1Facesel }); } for (var i = 0; i < op2iterations; i++) { poly = poly.ApplyOp(op2, new OpParams { valueA = op2Amount1 * (op2Animate ? animMultiplier : 1), valueB = op2Amount2, facesel = op2Facesel }); } for (var i = 0; i < op3iterations; i++) { poly = poly.ApplyOp(op3, new OpParams { valueA = op3Amount1 * (op3Animate ? animMultiplier : 1), valueB = op3Amount2, facesel = op3Facesel }); } } AfterAllOps(); Color[] colors = null; if (UseCustomColors) { colors = Enumerable.Range(0, 8).Select(x => Colors.Evaluate(((x / 8f) * ColorRange + ColorOffset) % 1)).ToArray(); } var mesh = PolyMeshBuilder.BuildMeshFromConwayPoly(poly, false, colors, ColorMethod, UVMethod); if (Rescale) { var size = mesh.bounds.size; var maxDimension = Mathf.Max(size.x, size.y, size.z); var scale = (1f / maxDimension) * 2f; if (scale > 0 && scale != Mathf.Infinity) { transform.localScale = new Vector3(scale, scale, scale); } else { Debug.LogError("Failed to rescale"); } } GetComponent <MeshFilter>().mesh = mesh; }
public override void Generate() { var wythoff = new WythoffPoly(PolyType, PrismP, PrismQ); wythoff.BuildFaces(); poly = new ConwayPoly(wythoff); base.Generate(); }
public void Generate() { var wythoff = new WythoffPoly(PolyType, PrismP, PrismQ); wythoff.BuildFaces(); poly = new ConwayPoly(wythoff); if (ApplyOp) { var o1 = new OpParams { valueA = op1Amount1, valueB = op1Amount2, facesel = op1Facesel }; poly = poly.ApplyOp(op1, o1); } if (Canonicalize) { poly = poly.Canonicalize(0.01, 0.01); } poly = poly.Transform(Position, Rotation, Scale); var pos = SlicePosition; if (animateSlice > 0) { pos.y = .1f + transform.position.y + ((Time.time % 2f) * animateSlice); } var rot = Quaternion.Euler(SliceRotation) * Vector3.up; var slicePlane = new Plane(rot, pos); var result = poly.SliceByPlane(slicePlane, Cap, includeTop, includeBottom); poly = result.bottom; var slicePoint = slicePlane.normal * (-slicePlane.distance + SegmentHeight); slicePlane.SetNormalAndPosition(slicePlane.normal, slicePoint); var(top, segment, _) = result.top.SliceByPlane(slicePlane, Cap, includeTop, includeBottom); segment = segment.Transform(SegmentTransform); poly.Append(top); poly.Append(segment); if (Weld) { poly = poly.Weld(0.0001f); } if (ApplyOp) { var o2 = new OpParams { valueA = op2Amount1, valueB = op2Amount2, facesel = op2Facesel }; poly = poly.ApplyOp(op2, o2); } var mesh = PolyMeshBuilder.BuildMeshFromConwayPoly(poly, false, null, ColorMethod); GetComponent <MeshFilter>().mesh = mesh; }
private static ConwayPoly _MakeDipyramid(int sides, float height) { ConwayPoly poly = _MakePyramid(sides, height); poly = poly.Kis(new OpParams { valueA = height, facesel = FaceSelections.Existing }); return(poly); }
// Returns true if at least one face matches the facesel rule but all of them public bool FaceSelectionIsValid(ConwayPoly.FaceSelections facesel) { if (ConwayOperators.Count == 0 && PolyType > 0) { _conwayPoly = new ConwayPoly(WythoffPoly); // We need a conway poly } int includedFaceCount = Enumerable.Range(0, _conwayPoly.Faces.Count).Count(x => _conwayPoly.IncludeFace(x, facesel)); return(includedFaceCount > 0 && includedFaceCount < _conwayPoly.Faces.Count); }
private static ConwayPoly _MakeBicupola(int sides, float height, bool gyro) { if (sides < 3) { sides = 3; } ConwayPoly poly = _MakeCupola(sides, height, true, gyro); return(poly); }
public static ConwayPoly ElongatedCupola(int sides) { ConwayPoly poly = Cupola(sides); float bodyHeight = _CalcSideLength(sides * 2); poly = poly.Loft(new OpParams { facesel = FaceSelections.OnlyFirst, valueB = bodyHeight }); return(poly); }
public static ConwayPoly GyroelongatedDipyramid(int sides) { ConwayPoly poly = GyroelongatedPyramid(sides); float height = _CalcPyramidHeight(sides); poly = poly.Kis(new OpParams { facesel = FaceSelections.FacingStraightDown, valueA = height }); return(poly); }
public void Generate() { var wythoff = new WythoffPoly(PolyType, PrismP, PrismQ); wythoff.BuildFaces(); var poly = new ConwayPoly(wythoff); ConwayPoly d, x, xd, dx, dxd; var o = new OpParams { valueA = op1Amount1, valueB = op1Amount2, facesel = op1Facesel }; d = poly.Dual(); x = poly.ApplyOp(op, o); xd = d.ApplyOp(op, o); dx = poly.ApplyOp(op, o); dx = dx.Dual(); dxd = d.ApplyOp(op, o); dxd = dxd.Dual(); if (Canonicalize) { x = x.Canonicalize(0.01, 0.01); dx = dx.Canonicalize(0.01, 0.01); dxd = dxd.Canonicalize(0.01, 0.01); xd = xd.Canonicalize(0.01, 0.01); } Debug.Log($"x: {x.vef.v},{x.vef.e},{x.vef.f}"); Debug.Log($"xd: {xd.vef.v},{xd.vef.e},{xd.vef.f}"); Debug.Log($"dx: {dx.vef.v},{dx.vef.e},{dx.vef.f}"); Debug.Log($"dxd: {dxd.vef.v},{dxd.vef.e},{dxd.vef.f}"); var allPoly = new ConwayPoly(); x = x.Transform(-Vector3.left); xd = xd.Transform(Vector3.left * 2); dx = dx.Transform(Vector3.left * 4); dxd = dxd.Transform(Vector3.left * 6); allPoly.Append(x); allPoly.Append(xd); allPoly.Append(dx); allPoly.Append(dxd); allPoly.Recenter(); allPoly = allPoly.Transform(Position, Rotation, Scale); var mesh = PolyMeshBuilder.BuildMeshFromConwayPoly(allPoly, false, null, ColorMethod); GetComponent <MeshFilter>().mesh = mesh; }
public void Generate() { Func <FilterParams, bool> pickRandomly = x => { // Sets random seed based on face index // So that the results are the same if we call more than once Random.InitState(x.index); return(Random.value < Density); }; // Generate the ground grid and extrude random buildings var grid = Grids.Grids.MakeGrid(GridType, GridShape, width, depth); grid = grid.VertexRotate(new OpParams(Jitter, randomValues: true)); var floors = grid.FaceKeep(new OpParams(.1f, pickRandomly)); var houses = floors.Loft(new OpParams(0, x => Random.Range(.5f, 1.5f))); var(walls, roofs) = houses.Split(new OpParams(FaceSelections.Existing)); // Make window holes walls = walls.Loft(new OpParams(0.75f, FaceSelections.AllNew)); walls = walls.FaceSlide(new OpParams(0.15f, FaceSelections.Existing)); walls = walls.FaceRemove(new OpParams(FaceSelections.Existing)); // Thicken the walls walls = walls.Shell(0.025f); // Add domes to the roofs var domes = roofs.LoftAlongProfile(FaceSelections.All, DomeHeight, DomeSegments, easingType); // Make nice patterns on the ground var ground = grid.Dual(); ground = ground.Bevel(new OpParams(0.25f)); ground = ground.Medial(new OpParams(3f)); // Add some edging around buildings var edging = floors.Transform(new Vector3(0, .03f, 0)); edging = edging.FaceScale(new OpParams(0.25f)); edging.SetFaceRoles(ConwayPoly.Roles.Existing); // Assemble everything var town = new ConwayPoly(); town.Append(edging); town.Append(ground); town.Append(walls); town.Append(domes); var mesh = PolyMeshBuilder.BuildMeshFromConwayPoly(town, false, Colors, ColorMethod); GetComponent <MeshFilter>().mesh = mesh; }
public static ConwayPoly GyroelongatedRotunda() { int sides = 10; float bodyHeight = _CalcAntiprismHeight(sides); ConwayPoly poly = Rotunda(); poly = poly.Lace(new OpParams { facesel = FaceSelections.FacingDown, valueA = 0, valueB = bodyHeight }); return(poly); }
public static ConwayPoly ElongatedRotunda() { int sides = 10; float bodyHeight = _CalcSideLength(sides); ConwayPoly poly = Rotunda(); poly = poly.Loft(new OpParams { valueB = bodyHeight, facesel = FaceSelections.FacingDown }); return(poly); }
public static ConwayPoly GyroelongatedPyramid(int sides) { float height = _CalcSideLength(sides); ConwayPoly poly = Antiprism(sides); height = _CalcPyramidHeight(sides); poly = poly.Kis(new OpParams { facesel = FaceSelections.FacingStraightUp, valueA = height }); return(poly); }
// Base forms are pyramids, cupolae and rotundae. // For each base we have elongate and bi. For each bi form we can also gyroelongate // Bi can come in ortho and gyro flavours in most cases. // You can combine bases i.e. cupolarotunda. These also come in ortho and gyro flavours. // Gyrobifastigium is just trying to be weird // Prisms can be augmented and diminished. Also bi, tri, para and meta // Truncation is a thing.and can be combined with augment/diminish. // Phew! Then stuff gets weirder. public static ConwayPoly ElongatedPyramid(int sides) { float height = _CalcSideLength(sides); ConwayPoly poly = _MakePrism(sides, height); height = _CalcPyramidHeight(sides); poly = poly.Kis(new OpParams { valueA = height, facesel = FaceSelections.FacingUp }); return(poly); }
public static ConwayPoly ElongatedBicupola(int sides, bool gyro) { ConwayPoly poly = ElongatedCupola(sides); Face bottom = poly.Faces[sides * 2]; int i = 0; var middleVerts = bottom.GetVertices(); poly.Faces.Remove(bottom); poly.FaceRoles.RemoveAt(poly.FaceRoles.Count - 1); poly.FaceTags.RemoveAt(poly.FaceRoles.Count - 1); float baseOffset = -(_CalcSideLength(sides * 2) + _CalcCupolaHeight(sides)); float angleOffset = gyro ? 0.75f : 0.25f; ConwayPoly cap2 = _MakePolygon(sides, false, angleOffset, baseOffset, _CalcCupolaCapRadius(sides)); poly.Append(cap2); var edge2 = poly.Faces.Last().Halfedge.Prev; int edgeOffset = gyro ? 0 : 1; while (true) { var side1 = new List <Vertex> { middleVerts[PolyUtils.ActualMod(i * 2 - 1 - edgeOffset, sides * 2)], middleVerts[PolyUtils.ActualMod(i * 2 - edgeOffset, sides * 2)], edge2.Vertex }; poly.Faces.Add(side1); poly.FaceRoles.Add(ConwayPoly.Roles.New); poly.FaceTags.Add(new HashSet <Tuple <string, ConwayPoly.TagType> >()); var side2 = new List <Vertex> { middleVerts[PolyUtils.ActualMod(i * 2 - edgeOffset, sides * 2)], middleVerts[PolyUtils.ActualMod(i * 2 + 1 - edgeOffset, sides * 2)], edge2.Next.Vertex, edge2.Vertex, }; poly.Faces.Add(side2); poly.FaceRoles.Add(ConwayPoly.Roles.NewAlt); poly.FaceTags.Add(new HashSet <Tuple <string, ConwayPoly.TagType> >()); i++; edge2 = edge2.Next; if (i == sides) { break; } } poly.Halfedges.MatchPairs(); return(poly); }
public virtual void AfterAllOps() { if (Canonicalize) { poly = poly.Canonicalize(0.01, 0.01); } if (Position != Vector3.zero || Rotation != Vector3.zero || Scale != Vector3.one) { poly = poly.Transform(Position, Rotation, Scale); } }
public static Color32 CalcFaceColor( ConwayPoly conway, Color[] colors, PolyHydraEnums.ColorMethods colorMethod, int i) { Color32 color; var face = conway.Faces[i]; var faceRole = conway.FaceRoles[i]; switch (colorMethod) { case PolyHydraEnums.ColorMethods.ByRole: color = colors[(int)faceRole]; break; case PolyHydraEnums.ColorMethods.BySides: color = colors[face.Sides % colors.Length]; break; case PolyHydraEnums.ColorMethods.ByFaceDirection: color = colors[CalcDirectionIndex(face, colors.Length - 1)]; break; case PolyHydraEnums.ColorMethods.ByTags: var c = new Color(); if (conway.FaceTags[i].Count > 0) { string htmlColor = conway.FaceTags[i].First(t => t.Item1.StartsWith("#")).Item1; if (!(ColorUtility.TryParseHtmlString(htmlColor, out c))) { if (!ColorUtility.TryParseHtmlString(htmlColor.Replace("#", ""), out c)) { c = Color.white; } } color = c; } else { color = Color.white; } break; default: color = Color.white; break; } return(color); }
public void FindOpMatches(ConwayPoly sourcePoly, string sourcePolyName, ConwayPoly targetPoly, string targetPolyName) { // int v = poly.Vertices.Count; // int e = poly.EdgeCount; // int f = poly.Faces.Count; // // // Compare predicted and actual v,e,f // Debug.Log($"{v} {e} {f} = {PolyHydraEnums.CalcVef(poly, op2)}"); // Loops through all Conway ops and records which ones // Result in matching Vertex, Edge and Face counts var matches = new Dictionary <int[], List <Ops> >(); // For all ops use: // var opCount = ((Ops[]) Enum.GetValues(typeof(Ops))).Length; // Just the conway ops var opCount = 34; var targetFaceTypes = targetPoly.GetFaceCountsByType(); for (var i = 1; i < 34; i++) { var o = new OpParams { valueA = 0.2f, valueB = 0.2f, facesel = FaceSelections.All }; var newPoly = sourcePoly.ApplyOp((Ops)i, o); var sourceFaceTypes = newPoly.GetFaceCountsByType(); if (!sourceFaceTypes.SequenceEqual(targetFaceTypes)) { continue; } if (!matches.ContainsKey(sourceFaceTypes)) { matches[sourceFaceTypes] = new List <Ops>(); } matches[sourceFaceTypes].Add((Ops)i); } if (matches.Count < 1) { return; } Debug.Log($"Matching {sourcePolyName} and {targetPolyName}: {matches.Count} matches"); foreach (var match in matches) { // if (match.Value.Count < 2) continue; var lst = string.Join(",", match.Value); Debug.Log($"{lst}"); } }
public static (int v, int e, int f) CalcVef(ConwayPoly poly, Ops op) { var matrix = OpConfigs[op].matrix; int v = poly.Vertices.Count; int e = poly.EdgeCount; int f = poly.Faces.Count; return( (matrix[0, 0] * v) + (matrix[0, 1] * e) + (matrix[0, 2] * f), (matrix[1, 0] * v) + (matrix[1, 1] * e) + (matrix[1, 2] * f), (matrix[2, 0] * v) + (matrix[2, 1] * e) + (matrix[2, 2] * f) ); }
public ConwayPoly GenerateWythoff(Uniform polyType) { var wythoff = new WythoffPoly(polyType.Wythoff); wythoff.BuildFaces(); var poly = new ConwayPoly(wythoff); // var o = new OpParams {valueA = .2f, valueB = .2f, facesel = FaceSelections.All}; // poly = poly.ApplyOp(op, o); // var mesh = PolyMeshBuilder.BuildMeshFromConwayPoly(poly, false, null, ColorMethod); // GetComponent<MeshFilter>().mesh = mesh; return(poly); }
// Essentially Kis only on non-triangular faces // Returns the original number of sides of each face to be used elsewhere // TODO Detect convex faces and use fan triangulation to save on a vertex? public List <int> KisTriangulate() { var faceRoles = new List <ConwayPoly.Roles>(); var vertexRoles = new List <ConwayPoly.Roles>(); var newVerts = _conwayPoly.Faces.Select(f => f.Centroid); var vertexPoints = Enumerable.Concat(_conwayPoly.Vertices.Select(v => v.Position), newVerts); vertexRoles.Concat(Enumerable.Repeat(ConwayPoly.Roles.Existing, vertexPoints.Count())); var originalFaceSides = new List <int>(); // vertex lookup var vlookup = new Dictionary <string, int>(); int n = _conwayPoly.Vertices.Count; for (int i = 0; i < n; i++) { vlookup.Add(_conwayPoly.Vertices[i].Name, i); } var faceIndices = new List <IEnumerable <int> >(); // faces as vertex indices for (int i = 0; i < _conwayPoly.Faces.Count; i++) { int faceSides = _conwayPoly.Faces[i].Sides; if (_conwayPoly.Faces[i].Sides <= 3) { faceIndices.Add(_conwayPoly.ListFacesByVertexIndices()[i]); originalFaceSides.Add(faceSides); faceRoles.Add(_conwayPoly.FaceRoles[i]); } else { foreach (var edge in _conwayPoly.Faces[i].GetHalfedges()) { // create new face from edge start, edge end and centroid faceIndices.Add( new[] { vlookup[edge.Prev.Vertex.Name], vlookup[edge.Vertex.Name], i + n } ); originalFaceSides.Add(faceSides); faceRoles.Add(_conwayPoly.FaceRoles[i]); } } } _conwayPoly = new ConwayPoly(vertexPoints, faceIndices, faceRoles, vertexRoles); return(originalFaceSides); }