public static Mesh Revolve(this IVertexSource source, int angleSteps = 30, double angleStart = 0, double angleEnd = MathHelper.Tau, bool revolveAroundZ = true) { angleStart = MathHelper.Range0ToTau(angleStart); angleEnd = MathHelper.Range0ToTau(angleEnd); // make sure we close 360 shapes angleStart = fixCloseAngles(angleStart); angleEnd = fixCloseAngles(angleEnd); if (angleStart == 0 && angleEnd == MathHelper.Tau) { angleSteps = Math.Max(angleSteps, 3); } else { angleSteps = Math.Max(angleSteps, 1); } // convert to clipper polygons and scale so we can ensure good shapes Polygons polygons = source.CreatePolygons(); if (polygons.Select(poly => poly.Where(pos => pos.X < 0)).Any()) { // ensure good winding and consistent shapes polygons = polygons.GetCorrectedWinding(); var bounds = polygons.GetBounds(); bounds.Inflate(10); // clip against x=0 left and right var leftClip = new Polygon(); leftClip.Add(new IntPoint(0, bounds.Bottom)); leftClip.Add(new IntPoint(0, bounds.Top)); leftClip.Add(new IntPoint(bounds.Left, bounds.Top)); leftClip.Add(new IntPoint(bounds.Left, bounds.Bottom)); var rightStuff = polygons.Subtract(leftClip); var rightClip = new Polygon(); rightClip.Add(new IntPoint(0, bounds.Top)); rightClip.Add(new IntPoint(0, bounds.Bottom)); rightClip.Add(new IntPoint(bounds.Right, bounds.Bottom)); rightClip.Add(new IntPoint(bounds.Right, bounds.Top)); var leftStuff = polygons.Subtract(rightClip); // mirror left material across the origin var leftAdd = leftStuff.Scale(-1, 1); if (leftAdd.Count > 0) { if (rightStuff.Count > 0) { polygons = rightStuff.Union(leftAdd); } else { polygons = leftAdd; } } else { // there is nothing on the left polygons = rightStuff; } } // convert the data back to PathStorage VertexStorage cleanedPath = polygons.CreateVertexStorage(); var mesh = new Mesh(); var hasStartAndEndFaces = angleStart > 0.000001; hasStartAndEndFaces |= angleEnd < MathHelper.Tau - 0.000001; // check if we need to make closing faces if (hasStartAndEndFaces) { // make a face for the start Mesh extrudedVertexSource = cleanedPath.TriangulateFaces(); if (revolveAroundZ) { extrudedVertexSource.Transform(Matrix4X4.CreateRotationX(MathHelper.Tau / 4)); extrudedVertexSource.Transform(Matrix4X4.CreateRotationZ(angleStart)); } else { extrudedVertexSource.Transform(Matrix4X4.CreateRotationY(angleStart)); } mesh.CopyFaces(extrudedVertexSource); } // make the outside shell double angleDelta = (angleEnd - angleStart) / angleSteps; double currentAngle = angleStart; if (!hasStartAndEndFaces) { angleSteps--; } for (int i = 0; i < angleSteps; i++) { AddRevolveStrip(cleanedPath, mesh, currentAngle, currentAngle + angleDelta, revolveAroundZ); currentAngle += angleDelta; } if (!hasStartAndEndFaces) { if (((angleEnd - angleStart) < .0000001 || (angleEnd - MathHelper.Tau - angleStart) < .0000001) && (angleEnd - currentAngle) > .0000001) { // make sure we close the shape exactly AddRevolveStrip(cleanedPath, mesh, currentAngle, angleStart, revolveAroundZ); } } else // add the end face { // make a face for the end Mesh extrudedVertexSource = cleanedPath.TriangulateFaces(); if (revolveAroundZ) { extrudedVertexSource.Transform(Matrix4X4.CreateRotationX(MathHelper.Tau / 4)); extrudedVertexSource.Transform(Matrix4X4.CreateRotationZ(currentAngle)); } else { extrudedVertexSource.Transform(Matrix4X4.CreateRotationY(angleStart)); } extrudedVertexSource.ReverseFaces(); mesh.CopyFaces(extrudedVertexSource); } mesh.CleanAndMerge(); // return the completed mesh return(mesh); }