public void ExtrudeCircular()
        {
            this.coords = new List <Coord>();
            this.faces  = new List <MeshmerizerFace>();

            int step  = 0;
            int steps = 24;

            float twistBegin = this.twistBegin / 360.0f * twoPi;
            float twistEnd   = this.twistEnd / 360.0f * twoPi;
            float twistTotal = twistEnd - twistBegin;

            // if the profile has a lot of twist, add more layers otherwise the layers may overlap
            // and the resulting mesh may be quite inaccurate. This method is arbitrary and doesn't
            // accurately match the viewer
            float twistTotalAbs = Math.Abs(twistTotal);

            if (twistTotalAbs > 0.01f)
            {
                if (twistTotalAbs > Math.PI * 1.5f)
                {
                    steps *= 2;
                }
                if (twistTotalAbs > Math.PI * 3.0f)
                {
                    steps *= 2;
                }
            }

            float yPathScale             = this.holeSizeY * 0.5f;
            float pathLength             = this.pathCutEnd - this.pathCutBegin;
            float totalSkew              = this.skew * 2.0f * pathLength;
            float skewStart              = this.pathCutBegin * 2.0f * this.skew - this.skew;
            float xOffsetTopShearXFactor = this.topShearX * (0.25f + 0.5f * (0.5f - this.holeSizeY));
            float yShearCompensation     = 1.0f + Math.Abs(this.topShearY) * 0.25f;

            // It's not quite clear what pushY (Y top shear) does, but subtracting it from the start and end
            // angles appears to approximate it's effects on path cut. Likewise, adding it to the angle used
            // to calculate the sine for generating the path radius appears to approximate it's effects there
            // too, but there are some subtle differences in the radius which are noticeable as the prim size
            // increases and it may affect megaprims quite a bit. The effect of the Y top shear parameter on
            // the meshes generated with this technique appear nearly identical in shape to the same prims when
            // displayed by the viewer.

            float startAngle = (twoPi * this.pathCutBegin * this.revolutions) - this.topShearY * 0.9f;
            float endAngle   = (twoPi * this.pathCutEnd * this.revolutions) - this.topShearY * 0.9f;
            float stepSize   = twoPi / this.stepsPerRevolution;

            step = (int)(startAngle / stepSize);
            int   firstStep = step;
            float angle     = startAngle;
            float hollow    = this.hollow;

            // sanity checks
            float initialProfileRot = 0.0f;

            if (this.sides == 3)
            {
                initialProfileRot = (float)Math.PI;
                if (this.hollowSides == 4)
                {
                    if (hollow > 0.7f)
                    {
                        hollow = 0.7f;
                    }
                    hollow *= 0.707f;
                }
                else
                {
                    hollow *= 0.5f;
                }
            }
            else if (this.sides == 4)
            {
                initialProfileRot = 0.25f * (float)Math.PI;
                if (this.hollowSides != 4)
                {
                    hollow *= 0.707f;
                }
            }
            else if (this.sides > 4)
            {
                initialProfileRot = (float)Math.PI;
                if (this.hollowSides == 4)
                {
                    if (hollow > 0.7f)
                    {
                        hollow = 0.7f;
                    }
                    hollow /= 0.7f;
                }
            }

            bool needEndFaces = false;

            if (this.pathCutBegin != 0.0 || this.pathCutEnd != 1.0)
            {
                needEndFaces = true;
            }
            else if (this.taperX != 0.0 || this.taperY != 0.0)
            {
                needEndFaces = true;
            }
            else if (this.skew != 0.0)
            {
                needEndFaces = true;
            }
            else if (twistTotal != 0.0)
            {
                needEndFaces = true;
            }

            MeshmerizerProfile profile = new MeshmerizerProfile(this.sides, this.profileStart, this.profileEnd, hollow, this.hollowSides, needEndFaces);

            if (initialProfileRot != 0.0f)
            {
                profile.AddRot(Quaternion.CreateFromAxisAngle(new Vector3(0.0f, 0.0f, 1.0f), initialProfileRot));
            }

            bool done = false;

            while (!done) // loop through the length of the path and add the layers
            {
                bool isEndLayer = false;
                if (angle == startAngle || angle >= endAngle)
                {
                    isEndLayer = true;
                }

                MeshmerizerProfile newLayer = profile.Clone(isEndLayer && needEndFaces);

                float xProfileScale = (1.0f - Math.Abs(this.skew)) * this.holeSizeX;
                float yProfileScale = this.holeSizeY;

                float percentOfPath   = angle / (twoPi * this.revolutions);
                float percentOfAngles = (angle - startAngle) / (endAngle - startAngle);

                if (this.taperX > 0.01f)
                {
                    xProfileScale *= 1.0f - percentOfPath * this.taperX;
                }
                else if (this.taperX < -0.01f)
                {
                    xProfileScale *= 1.0f + (1.0f - percentOfPath) * this.taperX;
                }

                if (this.taperY > 0.01f)
                {
                    yProfileScale *= 1.0f - percentOfPath * this.taperY;
                }
                else if (this.taperY < -0.01f)
                {
                    yProfileScale *= 1.0f + (1.0f - percentOfPath) * this.taperY;
                }

                if (xProfileScale != 1.0f || yProfileScale != 1.0f)
                {
                    newLayer.Scale(xProfileScale, yProfileScale);
                }

                float radiusScale = 1.0f;
                if (this.radius > 0.001f)
                {
                    radiusScale = 1.0f - this.radius * percentOfPath;
                }
                else if (this.radius < 0.001f)
                {
                    radiusScale = 1.0f + this.radius * (1.0f - percentOfPath);
                }

                float twist = twistBegin + twistTotal * percentOfPath;

                float xOffset = 0.5f * (skewStart + totalSkew * percentOfAngles);
                xOffset += (float)Math.Sin(angle) * xOffsetTopShearXFactor;

                float yOffset = yShearCompensation * (float)Math.Cos(angle) * (0.5f - yPathScale) * radiusScale;

                float zOffset = (float)Math.Sin(angle + this.topShearY) * (0.5f - yPathScale) * radiusScale;

                // next apply twist rotation to the profile layer
                if (twistTotal != 0.0f || twistBegin != 0.0f)
                {
                    newLayer.AddRot(Quaternion.CreateFromAxisAngle(new Vector3(0.0f, 0.0f, 1.0f), twist));
                }

                // now orient the rotation of the profile layer relative to it's position on the path
                // adding taperY to the angle used to generate the quat appears to approximate the viewer
                //newLayer.AddRot(new Quaternion(new MeshmerizerVertex(1.0f, 0.0f, 0.0f), angle + this.topShearY * 0.9f));
                newLayer.AddRot(Quaternion.CreateFromAxisAngle(new Vector3(1.0f, 0.0f, 0.0f), angle + this.topShearY));
                newLayer.AddPos(xOffset, yOffset, zOffset);

                if (angle == startAngle)
                {
                    newLayer.FlipNormals();
                }

                // append the layer and fill in the sides

                int coordsLen = this.coords.Count;
                newLayer.AddValue2Faces(coordsLen);

                this.coords.AddRange(newLayer.coords);

                if (isEndLayer)
                {
                    this.faces.AddRange(newLayer.faces);
                }

                // fill faces between layers

                int             numVerts = newLayer.coords.Count;
                MeshmerizerFace newFace  = new MeshmerizerFace();
                if (step > firstStep)
                {
                    for (int i = coordsLen; i < this.coords.Count - 1; i++)
                    {
                        newFace.v1 = i;
                        newFace.v2 = i - numVerts;
                        newFace.v3 = i - numVerts + 1;
                        this.faces.Add(newFace);

                        newFace.v2 = i - numVerts + 1;
                        newFace.v3 = i + 1;
                        this.faces.Add(newFace);
                    }

                    newFace.v1 = coordsLen - 1;
                    newFace.v2 = coordsLen - numVerts;
                    newFace.v3 = coordsLen;
                    this.faces.Add(newFace);

                    newFace.v1 = coordsLen + numVerts - 1;
                    newFace.v2 = coordsLen - 1;
                    newFace.v3 = coordsLen;
                    this.faces.Add(newFace);
                }

                // calculate terms for next iteration
                // calculate the angle for the next iteration of the loop

                if (angle >= endAngle)
                {
                    done = true;
                }
                else
                {
                    step += 1;
                    angle = stepSize * step;
                    if (angle > endAngle)
                    {
                        angle = endAngle;
                    }
                }
            }
        }
        public void ExtrudeLinear()
        {
            this.coords = new List <Coord>();
            this.faces  = new List <MeshmerizerFace>();

            int step  = 0;
            int steps = 1;

            float length        = this.pathCutEnd - this.pathCutBegin;
            float twistBegin    = this.twistBegin / 360.0f * twoPi;
            float twistEnd      = this.twistEnd / 360.0f * twoPi;
            float twistTotal    = twistEnd - twistBegin;
            float twistTotalAbs = Math.Abs(twistTotal);

            if (twistTotalAbs > 0.01f)
            {
                steps += (int)(twistTotalAbs * 3.66); //  dahlia's magic number
            }
            float start    = -0.5f;
            float stepSize = length / (float)steps;
            float percentOfPathMultiplier = stepSize;
            float xProfileScale           = 1.0f;
            float yProfileScale           = 1.0f;
            float xOffset = 0.0f;
            float yOffset = 0.0f;
            float zOffset = start;
            float xOffsetStepIncrement = this.topShearX / steps;
            float yOffsetStepIncrement = this.topShearY / steps;

            float percentOfPath = this.pathCutBegin;

            zOffset += percentOfPath;

            float hollow = this.hollow;

            // sanity checks
            float initialProfileRot = 0.0f;

            if (this.sides == 3)
            {
                if (this.hollowSides == 4)
                {
                    if (hollow > 0.7f)
                    {
                        hollow = 0.7f;
                    }
                    hollow *= 0.707f;
                }
                else
                {
                    hollow *= 0.5f;
                }
            }
            else if (this.sides == 4)
            {
                initialProfileRot = 1.25f * (float)Math.PI;
                if (this.hollowSides != 4)
                {
                    hollow *= 0.707f;
                }
            }
            else if (this.sides == 24 && this.hollowSides == 4)
            {
                hollow *= 1.414f;
            }

            MeshmerizerProfile profile = new MeshmerizerProfile(this.sides, this.profileStart, this.profileEnd, hollow, this.hollowSides, true);

            if (initialProfileRot != 0.0f)
            {
                profile.AddRot(Quaternion.CreateFromAxisAngle(new Vector3(0.0f, 0.0f, 1.0f), initialProfileRot));
            }

            bool done = false;

            while (!done)
            {
                MeshmerizerProfile newLayer = profile.Clone();

                if (this.taperX == 0.0f)
                {
                    xProfileScale = 1.0f;
                }
                else if (this.taperX > 0.0f)
                {
                    xProfileScale = 1.0f - percentOfPath * this.taperX;
                }
                else
                {
                    xProfileScale = 1.0f + (1.0f - percentOfPath) * this.taperX;
                }

                if (this.taperY == 0.0f)
                {
                    yProfileScale = 1.0f;
                }
                else if (this.taperY > 0.0f)
                {
                    yProfileScale = 1.0f - percentOfPath * this.taperY;
                }
                else
                {
                    yProfileScale = 1.0f + (1.0f - percentOfPath) * this.taperY;
                }

                if (xProfileScale != 1.0f || yProfileScale != 1.0f)
                {
                    newLayer.Scale(xProfileScale, yProfileScale);
                }

                float twist = twistBegin + twistTotal * percentOfPath;
                if (twist != 0.0f)
                {
                    newLayer.AddRot(Quaternion.CreateFromAxisAngle(new Vector3(0.0f, 0.0f, 1.0f), twist));
                }

                newLayer.AddPos(xOffset, yOffset, zOffset);

                if (step == 0)
                {
                    newLayer.FlipNormals();
                }

                // append this layer

                int coordsLen = this.coords.Count;
                newLayer.AddValue2Faces(coordsLen);

                this.coords.AddRange(newLayer.coords);

                if (percentOfPath <= this.pathCutBegin || percentOfPath >= this.pathCutEnd)
                {
                    this.faces.AddRange(newLayer.faces);
                }

                // fill faces between layers

                int             numVerts = newLayer.coords.Count;
                MeshmerizerFace newFace  = new MeshmerizerFace();
                if (step > 0)
                {
                    for (int i = coordsLen; i < this.coords.Count - 1; i++)
                    {
                        newFace.v1 = i;
                        newFace.v2 = i - numVerts;
                        newFace.v3 = i - numVerts + 1;
                        this.faces.Add(newFace);

                        newFace.v2 = i - numVerts + 1;
                        newFace.v3 = i + 1;
                        this.faces.Add(newFace);
                    }

                    newFace.v1 = coordsLen - 1;
                    newFace.v2 = coordsLen - numVerts;
                    newFace.v3 = coordsLen;
                    this.faces.Add(newFace);

                    newFace.v1 = coordsLen + numVerts - 1;
                    newFace.v2 = coordsLen - 1;
                    newFace.v3 = coordsLen;
                    this.faces.Add(newFace);
                }

                // calc the step for the next iteration of the loop

                if (step < steps)
                {
                    step          += 1;
                    percentOfPath += percentOfPathMultiplier;
                    xOffset       += xOffsetStepIncrement;
                    yOffset       += yOffsetStepIncrement;
                    zOffset       += stepSize;
                    if (percentOfPath > this.pathCutEnd)
                    {
                        done = true;
                    }
                }
                else
                {
                    done = true;
                }
            }
        }