Ejemplo n.º 1
0
        public static void MakeLowestFaceFlat(this InteractiveScene scene, IObject3D objectToLayFlatGroup)
        {
            var preLayFlatMatrix = objectToLayFlatGroup.Matrix;

            bool firstVertex = true;

            IObject3D objectToLayFlat = objectToLayFlatGroup;

            Vector3Float lowestPosition       = Vector3Float.PositiveInfinity;
            Vector3Float sourceVertexPosition = Vector3Float.NegativeInfinity;
            IObject3D    itemToLayFlat        = null;
            Mesh         meshWithLowest       = null;

            var items = objectToLayFlat.VisibleMeshes().Where(i => i.OutputType != PrintOutputTypes.Support);

            if (!items.Any())
            {
                items = objectToLayFlat.VisibleMeshes();
            }

            // Process each child, checking for the lowest vertex
            foreach (var itemToCheck in items)
            {
                var meshToCheck = itemToCheck.Mesh.GetConvexHull(false);

                if (meshToCheck == null &&
                    meshToCheck.Vertices.Count < 3)
                {
                    continue;
                }

                // find the lowest point on the model
                for (int testIndex = 0; testIndex < meshToCheck.Vertices.Count; testIndex++)
                {
                    var vertex         = meshToCheck.Vertices[testIndex];
                    var vertexPosition = vertex.Transform(itemToCheck.WorldMatrix());
                    if (firstVertex)
                    {
                        meshWithLowest       = meshToCheck;
                        lowestPosition       = vertexPosition;
                        sourceVertexPosition = vertex;
                        itemToLayFlat        = itemToCheck;
                        firstVertex          = false;
                    }
                    else if (vertexPosition.Z < lowestPosition.Z)
                    {
                        meshWithLowest       = meshToCheck;
                        lowestPosition       = vertexPosition;
                        sourceVertexPosition = vertex;
                        itemToLayFlat        = itemToCheck;
                    }
                }
            }

            if (meshWithLowest == null)
            {
                // didn't find any selected mesh
                return;
            }

            int    faceToLayFlat            = -1;
            double largestAreaOfAnyFace     = 0;
            var    facesSharingLowestVertex = meshWithLowest.Faces
                                              .Select((face, i) => new { face, i })
                                              .Where(faceAndIndex => meshWithLowest.Vertices[faceAndIndex.face.v0] == sourceVertexPosition ||
                                                     meshWithLowest.Vertices[faceAndIndex.face.v1] == sourceVertexPosition ||
                                                     meshWithLowest.Vertices[faceAndIndex.face.v2] == sourceVertexPosition)
                                              .Select(j => j.i);

            var lowestFacesByAngle = facesSharingLowestVertex.OrderBy(i =>
            {
                var face        = meshWithLowest.Faces[i];
                var worldNormal = face.normal.TransformNormal(itemToLayFlat.WorldMatrix());
                return(worldNormal.CalculateAngle(-Vector3Float.UnitZ));
            });

            // Check all the faces that are connected to the lowest point to find out which one to lay flat.
            foreach (var faceIndex in lowestFacesByAngle)
            {
                var face = meshWithLowest.Faces[faceIndex];

                var worldNormal       = face.normal.TransformNormal(itemToLayFlat.WorldMatrix());
                var worldAngleDegrees = MathHelper.RadiansToDegrees(worldNormal.CalculateAngle(-Vector3Float.UnitZ));

                double largestAreaFound   = 0;
                var    faceVeretexIndices = new int[] { face.v0, face.v1, face.v2 };

                foreach (var vi in faceVeretexIndices)
                {
                    if (meshWithLowest.Vertices[vi] != lowestPosition)
                    {
                        var planSurfaceArea = 0.0;
                        foreach (var coPlanarFace in meshWithLowest.GetCoplanerFaces(faceIndex))
                        {
                            planSurfaceArea += meshWithLowest.GetSurfaceArea(coPlanarFace);
                        }

                        if (largestAreaOfAnyFace == 0 ||
                            (planSurfaceArea > largestAreaFound &&
                             worldAngleDegrees < 45))
                        {
                            largestAreaFound = planSurfaceArea;
                        }
                    }
                }

                if (largestAreaFound > largestAreaOfAnyFace)
                {
                    largestAreaOfAnyFace = largestAreaFound;
                    faceToLayFlat        = faceIndex;
                }
            }

            double maxDistFromLowestZ = 0;
            var    lowestFace         = meshWithLowest.Faces[faceToLayFlat];
            var    lowestFaceIndices  = new int[] { lowestFace.v0, lowestFace.v1, lowestFace.v2 };
            var    faceVertices       = new List <Vector3Float>();

            foreach (var vertex in lowestFaceIndices)
            {
                var vertexPosition = meshWithLowest.Vertices[vertex].Transform(itemToLayFlat.WorldMatrix());
                faceVertices.Add(vertexPosition);
                maxDistFromLowestZ = Math.Max(maxDistFromLowestZ, vertexPosition.Z - lowestPosition.Z);
            }

            if (maxDistFromLowestZ > .001)
            {
                var xPositive   = (faceVertices[1] - faceVertices[0]).GetNormal();
                var yPositive   = (faceVertices[2] - faceVertices[0]).GetNormal();
                var planeNormal = xPositive.Cross(yPositive).GetNormal();

                // this code takes the minimum rotation required and looks much better.
                Quaternion rotation        = new Quaternion(planeNormal, new Vector3Float(0, 0, -1));
                Matrix4X4  partLevelMatrix = Matrix4X4.CreateRotation(rotation);

                // rotate it
                objectToLayFlat.Matrix = objectToLayFlatGroup.ApplyAtBoundsCenter(partLevelMatrix);
            }

            if (objectToLayFlatGroup is Object3D object3D)
            {
                AxisAlignedBoundingBox bounds = object3D.GetAxisAlignedBoundingBox(Matrix4X4.Identity, (item) =>
                {
                    return(item.OutputType != PrintOutputTypes.Support);
                });
                Vector3 boundsCenter = (bounds.MaxXYZ + bounds.MinXYZ) / 2;

                object3D.Matrix *= Matrix4X4.CreateTranslation(new Vector3(0, 0, -boundsCenter.Z + bounds.ZSize / 2));
            }
            else
            {
                PlatingHelper.PlaceOnBed(objectToLayFlatGroup);
            }

            scene.UndoBuffer.Add(new TransformCommand(objectToLayFlatGroup, preLayFlatMatrix, objectToLayFlatGroup.Matrix));
        }
Ejemplo n.º 2
0
        public static void MakeLowestFaceFlat(this InteractiveScene scene, IObject3D objectToLayFlatGroup)
        {
            bool firstVertex = true;

            IObject3D objectToLayFlat = objectToLayFlatGroup;

            IVertex   lowestVertex         = null;
            Vector3   lowestVertexPosition = Vector3.Zero;
            IObject3D itemToLayFlat        = null;

            // Process each child, checking for the lowest vertex
            foreach (var itemToCheck in objectToLayFlat.VisibleMeshes())
            {
                var meshToCheck = itemToCheck.Mesh.GetConvexHull(false);

                if (meshToCheck == null &&
                    meshToCheck.Vertices.Count < 3)
                {
                    continue;
                }

                // find the lowest point on the model
                for (int testIndex = 0; testIndex < meshToCheck.Vertices.Count; testIndex++)
                {
                    var     vertex         = meshToCheck.Vertices[testIndex];
                    Vector3 vertexPosition = Vector3.Transform(vertex.Position, itemToCheck.WorldMatrix());
                    if (firstVertex)
                    {
                        lowestVertex         = meshToCheck.Vertices[testIndex];
                        lowestVertexPosition = vertexPosition;
                        itemToLayFlat        = itemToCheck;
                        firstVertex          = false;
                    }
                    else if (vertexPosition.Z < lowestVertexPosition.Z)
                    {
                        lowestVertex         = meshToCheck.Vertices[testIndex];
                        lowestVertexPosition = vertexPosition;
                        itemToLayFlat        = itemToCheck;
                    }
                }
            }

            if (lowestVertex == null)
            {
                // didn't find any selected mesh
                return;
            }

            PolygonMesh.Face faceToLayFlat        = null;
            double           lowestAngleOfAnyFace = double.MaxValue;

            // Check all the faces that are connected to the lowest point to find out which one to lay flat.
            foreach (var face in lowestVertex.ConnectedFaces())
            {
                double biggestAngleToFaceVertex = double.MinValue;
                foreach (IVertex faceVertex in face.Vertices())
                {
                    if (faceVertex != lowestVertex)
                    {
                        Vector3 faceVertexPosition = Vector3.Transform(faceVertex.Position, itemToLayFlat.WorldMatrix());
                        Vector3 pointRelLowest     = faceVertexPosition - lowestVertexPosition;
                        double  xLeg  = new Vector2(pointRelLowest.X, pointRelLowest.Y).Length;
                        double  yLeg  = pointRelLowest.Z;
                        double  angle = Math.Atan2(yLeg, xLeg);
                        if (angle > biggestAngleToFaceVertex)
                        {
                            biggestAngleToFaceVertex = angle;
                        }
                    }
                }
                if (biggestAngleToFaceVertex < lowestAngleOfAnyFace)
                {
                    lowestAngleOfAnyFace = biggestAngleToFaceVertex;
                    faceToLayFlat        = face;
                }
            }

            double         maxDistFromLowestZ = 0;
            List <Vector3> faceVertices       = new List <Vector3>();

            foreach (IVertex vertex in faceToLayFlat.Vertices())
            {
                Vector3 vertexPosition = Vector3.Transform(vertex.Position, itemToLayFlat.WorldMatrix());
                faceVertices.Add(vertexPosition);
                maxDistFromLowestZ = Math.Max(maxDistFromLowestZ, vertexPosition.Z - lowestVertexPosition.Z);
            }

            if (maxDistFromLowestZ > .001)
            {
                Vector3 xPositive   = (faceVertices[1] - faceVertices[0]).GetNormal();
                Vector3 yPositive   = (faceVertices[2] - faceVertices[0]).GetNormal();
                Vector3 planeNormal = Vector3.Cross(xPositive, yPositive).GetNormal();

                // this code takes the minimum rotation required and looks much better.
                Quaternion rotation        = new Quaternion(planeNormal, new Vector3(0, 0, -1));
                Matrix4X4  partLevelMatrix = Matrix4X4.CreateRotation(rotation);

                // rotate it
                objectToLayFlat.Matrix = objectToLayFlatGroup.ApplyAtBoundsCenter(partLevelMatrix);
            }

            PlatingHelper.PlaceOnBed(objectToLayFlatGroup);
        }