예제 #1
0
        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);
        }