private void RotateAroundAxis(IObject3D selectedItem, double rotationAngle)
        {
            var rotationVector = Vector3.Zero;

            rotationVector[RotationAxis] = -rotationAngle;
            var rotationMatrix = Matrix4X4.CreateRotation(rotationVector);

            selectedItem.Matrix = selectedItem.Matrix.ApplyAtPosition(mouseDownInfo.SelectedObjectRotationCenter, rotationMatrix);
        }
 public static void Rotate(this IObject3D item, Vector3 origin, Vector3 axis, double angle)
 {
     // move object relative to rotation
     item.Matrix *= Matrix4X4.CreateTranslation(-origin);
     // rotate it
     item.Matrix *= Matrix4X4.CreateRotation(axis, angle);
     // move it back
     item.Matrix *= Matrix4X4.CreateTranslation(origin);
 }
Example #3
0
        /// <summary>
        /// Calculate the transform to move from the plane to a scaled polygon at plane z=1 offset = 0
        /// </summary>
        /// <param name="plane">The plane to transform from</param>
        /// <param name="outputScale">The amout to scale up when transforming</param>
        /// <returns>The plane to accomplish the transform</returns>
        public static Matrix4X4 GetTransformTo0Plane(Plane plane, int outputScale = 1000)
        {
            var rotation        = new Quaternion(plane.Normal, Vector3.UnitZ);
            var flattenedMatrix = Matrix4X4.CreateRotation(rotation);

            flattenedMatrix *= Matrix4X4.CreateTranslation(0, 0, -plane.DistanceFromOrigin);

            var transformTo0Plane = flattenedMatrix * Matrix4X4.CreateScale(outputScale);

            return(transformTo0Plane);
        }
Example #4
0
        public override void OnMouseUp(MouseEventArgs mouseEvent)
        {
            base.OnMouseUp(mouseEvent);

            if (mouseEvent.Button != MouseButtons.Left)
            {
                return;
            }

            // Make sure we don't use the trace data before it is ready
            if (mouseDownPosition == mouseEvent.Position &&
                cubeTraceData != null)
            {
                Ray           ray  = world.GetRayForLocalBounds(mouseEvent.Position);
                IntersectInfo info = cubeTraceData.GetClosestIntersection(ray);

                if (info != null)
                {
                    var hitData     = GetHitData(info.HitPosition);
                    var normalAndUp = GetDirectionForFace(hitData);

                    var look = Matrix4X4.LookAt(Vector3.Zero, normalAndUp.normal, normalAndUp.up);

                    var start = new Quaternion(interactionLayer.World.RotationMatrix);
                    var end   = new Quaternion(look);

                    Task.Run(() =>
                    {
                        // TODO: stop any spinning happening in the view
                        double duration = .25;
                        var timer       = Stopwatch.StartNew();
                        var time        = timer.Elapsed.TotalSeconds;

                        while (time < duration)
                        {
                            var current = Quaternion.Slerp(start, end, time / duration);
                            UiThread.RunOnIdle(() =>
                            {
                                interactionLayer.World.RotationMatrix = Matrix4X4.CreateRotation(current);
                                Invalidate();
                            });
                            time = timer.Elapsed.TotalSeconds;
                            Thread.Sleep(10);
                        }

                        interactionLayer.World.RotationMatrix = Matrix4X4.CreateRotation(end);
                        Invalidate();
                    });
                }
            }

            interactionLayer.Focus();
        }
        public override void Draw(DrawGlContentEventArgs e)
        {
            bool shouldDrawScaleControls = controlVisible == null ? true : controlVisible();

            if (Object3DControlContext.SelectedObject3DControl != null &&
                Object3DControlContext.SelectedObject3DControl as ScaleDiameterControl == null)
            {
                shouldDrawScaleControls = false;
            }

            var selectedItem = RootSelection;

            if (selectedItem != null)
            {
                // Ensures that functions in this scope run against the original instance reference rather than the
                // current value, thus avoiding null reference errors that would occur otherwise

                if (shouldDrawScaleControls)
                {
                    // don't draw if any other control is dragging
                    var color = theme.PrimaryAccentColor.WithAlpha(e.Alpha0to255);
                    if (MouseIsOver || MouseDownOnControl)
                    {
                        GLHelper.Render(grabControlMesh, color, TotalTransform, RenderTypes.Shaded);
                    }
                    else
                    {
                        color = theme.TextColor;
                        GLHelper.Render(grabControlMesh, color.WithAlpha(e.Alpha0to255), TotalTransform, RenderTypes.Shaded);
                    }

                    Vector3 newBottomCenter = ObjectSpace.GetCenterPosition(selectedItem, placement);
                    var     rotation        = Matrix4X4.CreateRotation(new Quaternion(selectedItem.Matrix));
                    var     translation     = Matrix4X4.CreateTranslation(newBottomCenter);
                    Object3DControlContext.World.RenderRing(rotation * translation, Vector3.Zero, getDiameters[diameterIndex](), 60, color.WithAlpha(e.Alpha0to255), 2, 0, e.ZBuffered);
                }

                if (hitPlane != null)
                {
                    //Object3DControlContext.World.RenderPlane(hitPlane.Plane, Color.Red, true, 50, 3);
                    //Object3DControlContext.World.RenderPlane(initialHitPosition, hitPlane.Plane.Normal, Color.Red, true, 50, 3);
                }

                if (shouldDrawScaleControls &&
                    (MouseIsOver || MouseDownOnControl))
                {
                    DrawMeasureLines(e);
                }
            }

            base.Draw(e);
        }
Example #6
0
        public static void RenderDirectionAxis(this WorldView world, DirectionAxis axis, Matrix4X4 matrix, double size)
        {
            GLHelper.PrepareFor3DLineRender(true);

            Frustum frustum = world.GetClippingFrustum();
            Vector3 length  = axis.Normal * size;
            var     color   = Agg.Color.Red;

            // draw center line
            {
                var     min   = axis.Origin - length;
                Vector3 start = Vector3Ex.Transform(min, matrix);

                var     max = axis.Origin + length;
                Vector3 end = Vector3Ex.Transform(max, matrix);

                world.Render3DLineNoPrep(frustum, start, end, color, 1);
            }

            var perpendicular = Vector3.GetPerpendicular(axis.Normal, Vector3.Zero).GetNormal();
            // draw some lines to mark the rotation plane
            int  count    = 20;
            bool first    = true;
            var  firstEnd = Vector3.Zero;
            var  lastEnd  = Vector3.Zero;
            var  center   = Vector3Ex.Transform(axis.Origin, matrix);

            for (int i = 0; i < count; i++)
            {
                var rotation = size / 4 * Vector3Ex.Transform(perpendicular, Matrix4X4.CreateRotation(axis.Normal, MathHelper.Tau * i / count));
                // draw center line
                var     max = axis.Origin + rotation;
                Vector3 end = Vector3Ex.Transform(max, matrix);

                world.Render3DLineNoPrep(frustum, center, end, color, 1);
                if (!first)
                {
                    world.Render3DLineNoPrep(frustum, end, lastEnd, color, 1);
                }
                else
                {
                    firstEnd = end;
                }

                lastEnd = end;
                first   = false;
            }

            world.Render3DLineNoPrep(frustum, firstEnd, lastEnd, color, 1);

            GL.Enable(EnableCap.Lighting);
        }
        public override void SetPosition(IObject3D selectedItem, MeshSelectInfo selectInfo)
        {
            var    topPosition = GetTopPosition(selectedItem);
            double distBetweenPixelsWorldSpace = Object3DControlContext.World.GetWorldUnitsPerScreenPixelAtPosition(topPosition);

            Vector3 arrowCenter = topPosition;

            arrowCenter.Z += arrowSize / 2 * distBetweenPixelsWorldSpace;

            var rotation = Matrix4X4.CreateRotation(new Quaternion(selectedItem.Matrix));

            TotalTransform = rotation * Matrix4X4.CreateScale(distBetweenPixelsWorldSpace) * Matrix4X4.CreateTranslation(arrowCenter);
        }
Example #8
0
        //Mouse drag, calculate rotation
        public void OnMouseMove(Vector2 mousePosition)
        {
            switch (CurrentTrackingType)
            {
            case TrackBallTransformType.Rotation:
                Quaternion activeRotationQuaternion = GetRotationForMove(ScreenCenter, world, TrackBallRadius, mouseDownPosition, mousePosition, false);

                if (activeRotationQuaternion != Quaternion.Identity)
                {
                    mouseDownPosition    = mousePosition;
                    world.RotationMatrix = world.RotationMatrix * Matrix4X4.CreateRotation(activeRotationQuaternion);
                    OnTransformChanged(null);
                }

                break;

            case TrackBallTransformType.Translation:
            {
                Vector2 mouseDelta  = mousePosition - lastTranslationMousePosition;
                Vector2 scaledDelta = mouseDelta / this.ScreenCenter.X * 4.75;
                Vector3 offset      = new Vector3(scaledDelta.X, scaledDelta.Y, 0);
                offset = Vector3Ex.TransformPosition(offset, Matrix4X4.Invert(world.RotationMatrix));
                offset = Vector3Ex.TransformPosition(offset, localToScreenTransform);
                world.TranslationMatrix      = world.TranslationMatrix * Matrix4X4.CreateTranslation(offset);
                lastTranslationMousePosition = mousePosition;
                OnTransformChanged(null);
            }
            break;

            case TrackBallTransformType.Scale:
            {
                Vector2 mouseDelta = mousePosition - lastScaleMousePosition;
                double  zoomDelta  = 1;
                if (mouseDelta.Y < 0)
                {
                    zoomDelta = 1 - (-1 * mouseDelta.Y / 100);
                }
                else if (mouseDelta.Y > 0)
                {
                    zoomDelta = 1 + (1 * mouseDelta.Y / 100);
                }
                world.Scale            = world.Scale * zoomDelta;
                lastScaleMousePosition = mousePosition;
                OnTransformChanged(null);
            }
            break;

            default:
                throw new NotImplementedException();
            }
        }
Example #9
0
        private void RenderLine(Matrix4X4 transform, Vector3 start, Vector3 end, Color color, bool zBuffered = true)
        {
            Vector3 lineCenter = (start + end) / 2;

            Vector3   delta           = start - end;
            Matrix4X4 rotateTransform = Matrix4X4.CreateRotation(new Quaternion(delta.GetNormal(), Vector3.UnitX + new Vector3(.0001, -.00001, .00002)));
            Matrix4X4 scaleTransform  = Matrix4X4.CreateScale((end - start).Length, 1, 1);
            Matrix4X4 lineTransform   = scaleTransform * rotateTransform * Matrix4X4.CreateTranslation(lineCenter) * transform;

            if (zBuffered)
            {
                GLHelper.Render(lineMesh, Color.Black, lineTransform, RenderTypes.Shaded);
                //drawEvent.graphics2D.Line(cornerPositionScreen, cornerPositionCcwScreen, Color.Gray);
            }
            else
            {
                // render on top of everything very lightly
                GLHelper.Render(lineMesh, new Color(Color.Black, 5), lineTransform, RenderTypes.Shaded);
            }
        }
Example #10
0
        public void SetPrintLevelingEquation(Vector3 position0, Vector3 position1, Vector3 position2, Vector2 bedCenter)
        {
            if (position0 == position1 || position1 == position2 || position2 == position0)
            {
                return;
            }

            Plane planeOfPoints = new Plane(position0, position1, position2);

            Ray    ray = new Ray(new Vector3(bedCenter, 0), Vector3.UnitZ);
            bool   inFront;
            double distanceToPlaneAtBedCenter = planeOfPoints.GetDistanceToIntersection(ray, out inFront);

            Matrix4X4 makePointsFlatMatrix = Matrix4X4.CreateTranslation(-bedCenter.x, -bedCenter.y, -distanceToPlaneAtBedCenter);

            makePointsFlatMatrix *= Matrix4X4.CreateRotation(planeOfPoints.PlaneNormal, Vector3.UnitZ);
            makePointsFlatMatrix *= Matrix4X4.CreateTranslation(bedCenter.x, bedCenter.y, 0);            //distanceToPlaneAtBedCenter);

            bedLevelMatrix = Matrix4X4.Invert(makePointsFlatMatrix);
        }
Example #11
0
        public static Matrix4X4 GetMaxFaceProjection(Face face, ImageBuffer textureToUse, Matrix4X4?initialTransform = null)
        {
            // If not set than make it identity
            var firstTransform = initialTransform == null ? Matrix4X4.Identity : (Matrix4X4)initialTransform;

            var textureCoordinateMapping = Matrix4X4.CreateRotation(new Quaternion(face.Normal, Vector3.UnitZ));

            var bounds = RectangleDouble.ZeroIntersection;

            foreach (FaceEdge faceEdge in face.FaceEdges())
            {
                var edgeStartPosition = faceEdge.FirstVertex.Position;
                var textureUv         = Vector3.Transform(edgeStartPosition, textureCoordinateMapping);
                bounds.ExpandToInclude(new Vector2(textureUv));
            }
            var centering = Matrix4X4.CreateTranslation(new Vector3(-bounds.Left, -bounds.Bottom, 0));
            var scaling   = Matrix4X4.CreateScale(new Vector3(1 / bounds.Width, 1 / bounds.Height, 1));

            return(textureCoordinateMapping * firstTransform * centering * scaling);
        }
        public override void SetPosition(IObject3D selectedItem, MeshSelectInfo selectInfo)
        {
            // create the transform for the box
            Vector3 cornerPosition = ObjectSpace.GetCornerPosition(selectedItem, quadrantIndex);

            Vector3 boxCenter = cornerPosition;

            double distBetweenPixelsWorldSpace = Object3DControlContext.World.GetWorldUnitsPerScreenPixelAtPosition(cornerPosition);

            switch (quadrantIndex)
            {
            case 0:
                boxCenter.X += selectCubeSize / 2 * distBetweenPixelsWorldSpace;
                boxCenter.Y += selectCubeSize / 2 * distBetweenPixelsWorldSpace;
                break;

            case 1:
                boxCenter.X -= selectCubeSize / 2 * distBetweenPixelsWorldSpace;
                boxCenter.Y += selectCubeSize / 2 * distBetweenPixelsWorldSpace;
                break;

            case 2:
                boxCenter.X -= selectCubeSize / 2 * distBetweenPixelsWorldSpace;
                boxCenter.Y -= selectCubeSize / 2 * distBetweenPixelsWorldSpace;
                break;

            case 3:
                boxCenter.X += selectCubeSize / 2 * distBetweenPixelsWorldSpace;
                boxCenter.Y -= selectCubeSize / 2 * distBetweenPixelsWorldSpace;
                break;
            }

            boxCenter.Z += selectCubeSize / 2 * distBetweenPixelsWorldSpace;

            var rotation = Matrix4X4.CreateRotation(new Quaternion(selectedItem.Matrix));

            var centerMatrix = Matrix4X4.CreateTranslation(boxCenter);

            centerMatrix   = rotation * Matrix4X4.CreateScale(distBetweenPixelsWorldSpace) * centerMatrix;
            TotalTransform = centerMatrix;
        }
Example #13
0
        public void OnMouseUp()
        {
            switch (currentTrackingType)
            {
            case MouseDownType.Rotation:
                currentRotationMatrix    = currentRotationMatrix * Matrix4X4.CreateRotation(activeRotationQuaternion);
                activeRotationQuaternion = Quaternion.Identity;
                break;

            case MouseDownType.Translation:
                //currentTranslationMatrix = Matrix4X4.Identity;
                break;

            case MouseDownType.Scale:
                break;

            default:
                throw new NotImplementedException();
            }
            currentTrackingType = MouseDownType.None;
        }
Example #14
0
        public static Matrix4X4 GetMaxPlaneProjection(this Mesh mesh, int face, ImageBuffer textureToUse, Matrix4X4?initialTransform = null)
        {
            // If not set than make it identity
            var firstTransform = initialTransform == null ? Matrix4X4.Identity : (Matrix4X4)initialTransform;

            var textureCoordinateMapping = Matrix4X4.CreateRotation(new Quaternion(mesh.Faces[face].normal.AsVector3(), Vector3.UnitZ));

            var bounds = RectangleDouble.ZeroIntersection;

            foreach (int vertexIndex in new int[] { mesh.Faces[face].v0, mesh.Faces[face].v1, mesh.Faces[face].v2 })
            {
                var edgeStartPosition = mesh.Vertices[vertexIndex];
                var textureUv         = edgeStartPosition.Transform(textureCoordinateMapping);
                bounds.ExpandToInclude(new Vector2(textureUv));
            }

            var centering = Matrix4X4.CreateTranslation(new Vector3(-bounds.Left, -bounds.Bottom, 0));
            var scaling   = Matrix4X4.CreateScale(new Vector3(1 / bounds.Width, 1 / bounds.Height, 1));

            return(textureCoordinateMapping * firstTransform * centering * scaling);
        }
Example #15
0
        public DepthPeeling(Mesh mesh)
        {
            gl.create_shader_program(null, meshVertexShader, meshFragmentShader, out scene_p_id);
            gl.create_shader_program(null, tex_v_shader, tex_f_shader, out tex_p_id);

            var aabb = mesh.GetAxisAlignedBoundingBox();

            mesh.Translate(-aabb.Center);
            mesh.Transform(Matrix4X4.CreateRotation(new Vector3(23, 51, 12)));
            mesh.Transform(Matrix4X4.CreateScale(1 / Math.Max(aabb.XSize, Math.Max(aabb.YSize, aabb.ZSize)) / 2));

            var screenQuad = new Mesh();

            screenQuad.CreateFace(new Vector3[]
            {
                new Vector3(-1, -1, 0),
                new Vector3(1, -1, 0),
                new Vector3(1, 1, 0),
                new Vector3(-1, 1, 0)
            });

            screenQuadVao = GLMeshVertexArrayObjectPlugin.Get(screenQuad);
        }
Example #16
0
        public void SubtractIcosahedronsWorks()
        {
            Vector3 centering = new Vector3(100, 100, 20);
            Mesh    meshA     = PlatonicSolids.CreateIcosahedron(35);

            meshA.Translate(centering);
            Mesh meshB = PlatonicSolids.CreateIcosahedron(35);

            Vector3   finalTransform = new Vector3(105.240172225344, 92.9716306394062, 18.4619570261172);
            Vector3   rotCurrent     = new Vector3(4.56890223673623, -2.67874102322035, 1.02768848238523);
            Vector3   scaleCurrent   = new Vector3(1.07853517569753, 0.964980885267323, 1.09290934544604);
            Matrix4X4 transformB     = Matrix4X4.CreateScale(scaleCurrent) * Matrix4X4.CreateRotation(rotCurrent) * Matrix4X4.CreateTranslation(finalTransform);

            meshB.Transform(transformB);

            Mesh meshToAdd = CsgOperations.Subtract(meshA, meshB);

            AxisAlignedBoundingBox a_aabb         = meshA.GetAxisAlignedBoundingBox();
            AxisAlignedBoundingBox b_aabb         = meshB.GetAxisAlignedBoundingBox();
            AxisAlignedBoundingBox intersect_aabb = meshToAdd.GetAxisAlignedBoundingBox();

            Assert.IsTrue(a_aabb.XSize == 40 && a_aabb.YSize == 40 && a_aabb.ZSize == 40);
            Assert.IsTrue(intersect_aabb.XSize == 40 && intersect_aabb.YSize == 40 && intersect_aabb.ZSize == 40);
        }
        //Mouse drag, calculate rotation
        public void OnMouseMove(Vector2 mousePosition, bool rotateOnZ)
        {
            switch (currentTrackingType)
            {
            case MouseDownType.Rotation:
                var activeRotationQuaternion = Quaternion.Identity;

                //Map the point to the sphere
                moveSpherePosition = MapMoveToSphere(mousePosition, rotateOnZ);

                //Return the quaternion equivalent to the rotation
                //Compute the vector perpendicular to the begin and end vectors
                var rotationStart3D = new Vector3(0, 0, 1);
                if (rotateOnZ)
                {
                    rotationStart3D = new Vector3(1, 0, 0);
                }
                Vector3 Perp = Vector3.Cross(rotationStart3D, moveSpherePosition);

                //Compute the length of the perpendicular vector
                if (Perp.Length > Epsilon)
                {
                    //if its non-zero
                    //We're ok, so return the perpendicular vector as the transform after all
                    activeRotationQuaternion.X = Perp.X;
                    activeRotationQuaternion.Y = Perp.Y;
                    activeRotationQuaternion.Z = Perp.Z;
                    //In the quaternion values, w is cosine (theta / 2), where theta is the rotation angle
                    activeRotationQuaternion.W = Vector3.Dot(rotationStart3D, moveSpherePosition);

                    mouseDownPosition    = mousePosition;
                    world.RotationMatrix = world.RotationMatrix * Matrix4X4.CreateRotation(activeRotationQuaternion);
                    OnTransformChanged(null);
                }
                break;

            case MouseDownType.Translation:
            {
                Vector2 mouseDelta  = mousePosition - lastTranslationMousePosition;
                Vector2 scaledDelta = mouseDelta / world.ScreenCenter.X * 4.75;
                Vector3 offset      = new Vector3(scaledDelta.X, scaledDelta.Y, 0);
                offset = Vector3.TransformPosition(offset, Matrix4X4.Invert(world.RotationMatrix));
                offset = Vector3.TransformPosition(offset, localToScreenTransform);
                world.TranslationMatrix      = world.TranslationMatrix * Matrix4X4.CreateTranslation(offset);
                lastTranslationMousePosition = mousePosition;
                OnTransformChanged(null);
            }
            break;

            case MouseDownType.Scale:
            {
                Vector2 mouseDelta = mousePosition - lastScaleMousePosition;
                double  zoomDelta  = 1;
                if (mouseDelta.Y < 0)
                {
                    zoomDelta = 1 - (-1 * mouseDelta.Y / 100);
                }
                else if (mouseDelta.Y > 0)
                {
                    zoomDelta = 1 + (1 * mouseDelta.Y / 100);
                }
                world.Scale            = world.Scale * zoomDelta;
                lastScaleMousePosition = mousePosition;
                OnTransformChanged(null);
            }
            break;

            default:
                throw new NotImplementedException();
            }
        }
        private Matrix4X4 GetRotationTransform(IObject3D selectedItem, out double radius)
        {
            Matrix4X4 rotationCenterTransform;

            AxisAlignedBoundingBox currentSelectedBounds = selectedItem.GetAxisAlignedBoundingBox();

            Vector3 controlCenter  = GetControlCenter(selectedItem);
            Vector3 rotationCenter = GetRotationCenter(selectedItem, currentSelectedBounds);

            if (mouseDownInfo != null)
            {
                rotationCenter = mouseDownInfo.SelectedObjectRotationCenter;
                controlCenter  = mouseDownInfo.ControlCenter;
            }

            double distBetweenPixelsWorldSpace = InteractionContext.World.GetWorldUnitsPerScreenPixelAtPosition(rotationCenter);
            double lengthFromCenterToControl   = (rotationCenter - controlCenter).Length;

            radius = lengthFromCenterToControl * (1 / distBetweenPixelsWorldSpace);

            rotationTransformScale  = distBetweenPixelsWorldSpace;
            rotationCenterTransform = Matrix4X4.CreateScale(distBetweenPixelsWorldSpace) * Matrix4X4.CreateTranslation(rotationCenter);

            var center = Vector3Ex.Transform(Vector3.Zero, rotationCenterTransform);

            switch (RotationAxis)
            {
            case 0:
            {
                rotationCenterTransform =
                    Matrix4X4.CreateTranslation(-center)
                    * Matrix4X4.CreateRotation(new Vector3(0, -MathHelper.Tau / 4, 0))
                    * Matrix4X4.CreateRotation(new Vector3(-MathHelper.Tau / 4, 0, 0))
                    * rotationCenterTransform;

                var center2 = Vector3Ex.Transform(Vector3.Zero, rotationCenterTransform);
                rotationCenterTransform *= Matrix4X4.CreateTranslation(center - center2);
            }

            break;

            case 1:
            {
                rotationCenterTransform =
                    Matrix4X4.CreateTranslation(-center)
                    * Matrix4X4.CreateRotation(new Vector3(MathHelper.Tau / 4, 0, 0))
                    * Matrix4X4.CreateRotation(new Vector3(0, MathHelper.Tau / 4, 0))
                    * rotationCenterTransform;

                var center2 = Vector3Ex.Transform(Vector3.Zero, rotationCenterTransform);
                rotationCenterTransform *= Matrix4X4.CreateTranslation(center - center2);
            }

            break;

            case 2:
                break;
            }

            return(rotationCenterTransform);
        }
Example #19
0
        public override void OnMouseUp(MouseEventArgs mouseEvent)
        {
            base.OnMouseUp(mouseEvent);

            // Make sure we don't use the trace data before it is ready
            if (mouseDownPosition == mouseEvent.Position &&
                cubeTraceData != null)
            {
                Ray           ray  = world.GetRayForLocalBounds(mouseEvent.Position);
                IntersectInfo info = cubeTraceData.GetClosestIntersection(ray);

                if (info != null)
                {
                    var normal           = ((TriangleShape)info.closestHitObject).Plane.Normal;
                    var directionForward = -new Vector3(normal);

                    var directionUp = Vector3.UnitY;
                    if (directionForward.Equals(Vector3.UnitX, .001))
                    {
                        directionUp = Vector3.UnitZ;
                    }
                    else if (directionForward.Equals(-Vector3.UnitX, .001))
                    {
                        directionUp = Vector3.UnitZ;
                    }
                    else if (directionForward.Equals(Vector3.UnitY, .001))
                    {
                        directionUp = Vector3.UnitZ;
                    }
                    else if (directionForward.Equals(-Vector3.UnitY, .001))
                    {
                        directionUp = Vector3.UnitZ;
                    }
                    else if (directionForward.Equals(Vector3.UnitZ, .001))
                    {
                        directionUp = -Vector3.UnitY;
                    }

                    var look = Matrix4X4.LookAt(Vector3.Zero, directionForward, directionUp);

                    var start = new Quaternion(interactionLayer.World.RotationMatrix);
                    var end   = new Quaternion(look);

                    Task.Run(() =>
                    {
                        // TODO: stop any spinning happening in the view
                        double duration = .25;
                        var timer       = Stopwatch.StartNew();
                        var time        = timer.Elapsed.TotalSeconds;
                        while (time < duration)
                        {
                            var current = Quaternion.Slerp(start, end, time / duration);
                            UiThread.RunOnIdle(() =>
                            {
                                interactionLayer.World.RotationMatrix = Matrix4X4.CreateRotation(current);
                                Invalidate();
                            });
                            time = timer.Elapsed.TotalSeconds;
                            Thread.Sleep(10);
                        }
                        interactionLayer.World.RotationMatrix = Matrix4X4.CreateRotation(end);
                        Invalidate();
                    });
                }
            }

            interactionLayer.Focus();
        }
Example #20
0
        static public void CreatePointer(VectorPOD <ColorVertexData> colorVertexData, VectorPOD <int> indexData, Vector3 startPos, Vector3 endPos, double radius, int steps, Color color)
        {
            Vector3 direction           = endPos - startPos;
            Vector3 directionNormal     = direction.GetNormal();
            Vector3 startSweepDirection = Vector3.GetPerpendicular(startPos, endPos).GetNormal();

            int[] tubeStartIndices = new int[steps];

            for (int i = 0; i < steps; i++)
            {
                // create tube ends verts
                Vector3 tubeNormal = Vector3Ex.Transform(startSweepDirection, Matrix4X4.CreateRotation(direction, MathHelper.Tau / (steps * 2) + MathHelper.Tau / (steps) * i));
                Vector3 offset     = Vector3Ex.Transform(startSweepDirection * radius, Matrix4X4.CreateRotation(direction, MathHelper.Tau / (steps * 2) + MathHelper.Tau / (steps) * i));
                Vector3 tubeStart  = startPos + offset;
                tubeStartIndices[i] = colorVertexData.Count;
                colorVertexData.Add(new ColorVertexData(tubeStart, tubeNormal, color));
                Vector3 tubeEnd = endPos + offset;
            }

            int tipEndIndex = colorVertexData.Count;

            colorVertexData.Add(new ColorVertexData(endPos, directionNormal, color));

            for (int i = 0; i < steps; i++)
            {
                // create tube polys
                indexData.Add(tubeStartIndices[i]);
                indexData.Add(tubeStartIndices[(i + 1) % steps]);

                indexData.Add(tipEndIndex);
            }
        }
        public override void OnMouseMove(MouseEventArgs mouseEvent)
        {
            mouseOver = false;
            // find the ray for this control
            // check what face it hits
            // mark that face to draw a highlight
            base.OnMouseMove(mouseEvent);

            // rotate the view
            if (MouseDownOnWidget)
            {
                var        movePosition             = mouseEvent.Position;
                Quaternion activeRotationQuaternion = TrackBallController.GetRotationForMove(new Vector2(Width / 2, Height / 2), world, Width, lastMovePosition, movePosition, false);

                if (activeRotationQuaternion != Quaternion.Identity)
                {
                    lastMovePosition = movePosition;
                    interactionLayer.World.RotationMatrix = interactionLayer.World.RotationMatrix * Matrix4X4.CreateRotation(activeRotationQuaternion);
                    interactionLayer.Invalidate();
                }
            }
            else if (world != null &&
                     cubeTraceData != null)               // Make sure we don't use the trace data before it is ready
            {
                Ray           ray  = world.GetRayForLocalBounds(mouseEvent.Position);
                IntersectInfo info = cubeTraceData.GetClosestIntersection(ray);

                if (info != null)
                {
                    mouseOver = true;

                    DrawMouseHover(GetHitData(info.HitPosition));
                }
                else
                {
                    ResetTextures();
                }
            }
        }
Example #22
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);
        }
Example #23
0
 public void Rotate(Quaternion rotation)
 {
     currentRotationMatrix = currentRotationMatrix * Matrix4X4.CreateRotation(rotation);
 }
Example #24
0
 public void Rotate(Quaternion rotation)
 {
     currentRotationMatrix = currentRotationMatrix * Matrix4X4.CreateRotation(rotation);
     OnTransformChanged(null);
 }
Example #25
0
        static public void CreateCylinder(VectorPOD <ColorVertexData> colorVertexData, VectorPOD <int> indexData, Vector3 startPos, Vector3 endPos, double radius, int steps, RGBA_Bytes color, double layerHeight)
        {
            Vector3 direction           = endPos - startPos;
            Vector3 directionNormal     = direction.GetNormal();
            Vector3 startSweepDirection = Vector3.GetPerpendicular(startPos, endPos).GetNormal();

            int[] tubeStartIndices = new int[steps];
            int[] tubeEndIndices   = new int[steps];

            int[] capStartIndices = new int[steps];
            int[] capEndIndices   = new int[steps];

            double halfHeight = layerHeight / 2 + (layerHeight * .1);
            double halfWidth  = (radius * radius) / halfHeight;
            double zScale     = halfHeight / radius;
            double xScale     = halfWidth / radius;

            Vector3 scale = new Vector3(xScale, xScale, zScale);

            for (int i = 0; i < steps; i++)
            {
                // create tube ends verts
                Vector3 tubeNormal = Vector3.Transform(startSweepDirection, Matrix4X4.CreateRotation(direction, MathHelper.Tau / (steps * 2) + MathHelper.Tau / (steps) * i));
                Vector3 offset     = Vector3.Transform(startSweepDirection * radius, Matrix4X4.CreateRotation(direction, MathHelper.Tau / (steps * 2) + MathHelper.Tau / (steps) * i));
                offset *= scale;

                Vector3 tubeStart = startPos + offset;
                tubeStartIndices[i] = colorVertexData.Count;
                colorVertexData.Add(new ColorVertexData(tubeStart, tubeNormal, color));

                Vector3 tubeEnd = endPos + offset;
                tubeEndIndices[i] = colorVertexData.Count;
                colorVertexData.Add(new ColorVertexData(tubeEnd, tubeNormal, color));

                // create cap verts
                Vector3 rotateAngle    = Vector3.Cross(direction, startSweepDirection);
                Vector3 capStartNormal = Vector3.Transform(startSweepDirection, Matrix4X4.CreateRotation(rotateAngle, MathHelper.Tau / 8));
                capStartNormal = Vector3.Transform(capStartNormal, Matrix4X4.CreateRotation(direction, MathHelper.Tau / (steps * 2) + MathHelper.Tau / (steps) * i));
                capStartNormal = (capStartNormal * scale).GetNormal();
                Vector3 capStartOffset = capStartNormal * radius;
                capStartOffset *= scale;
                Vector3 capStart = startPos + capStartOffset;
                capStartIndices[i] = colorVertexData.Count;
                colorVertexData.Add(new ColorVertexData(capStart, capStartNormal, color));

                Vector3 capEndNormal = Vector3.Transform(startSweepDirection, Matrix4X4.CreateRotation(-rotateAngle, MathHelper.Tau / 8));
                capEndNormal = Vector3.Transform(capEndNormal, Matrix4X4.CreateRotation(direction, MathHelper.Tau / (steps * 2) + MathHelper.Tau / (steps) * i));
                capEndNormal = (capEndNormal * scale).GetNormal();
                Vector3 capEndOffset = capEndNormal * radius;
                capEndOffset *= scale;
                Vector3 capEnd = endPos + capEndOffset;
                capEndIndices[i] = colorVertexData.Count;
                colorVertexData.Add(new ColorVertexData(capEnd, capEndNormal, color));
            }

            int     tipStartIndex = colorVertexData.Count;
            Vector3 tipOffset     = directionNormal * radius;

            tipOffset *= scale;
            colorVertexData.Add(new ColorVertexData(startPos - tipOffset, -directionNormal, color));
            int tipEndIndex = colorVertexData.Count;

            colorVertexData.Add(new ColorVertexData(endPos + tipOffset, directionNormal, color));

            for (int i = 0; i < steps; i++)
            {
                // create tube polys
                indexData.Add(tubeStartIndices[i]);
                indexData.Add(tubeEndIndices[i]);
                indexData.Add(tubeEndIndices[(i + 1) % steps]);

                indexData.Add(tubeStartIndices[i]);
                indexData.Add(tubeEndIndices[(i + 1) % steps]);
                indexData.Add(tubeStartIndices[(i + 1) % steps]);

                // create start cap polys
                indexData.Add(tubeStartIndices[i]);
                indexData.Add(capStartIndices[i]);
                indexData.Add(capStartIndices[(i + 1) % steps]);

                indexData.Add(tubeStartIndices[i]);
                indexData.Add(capStartIndices[(i + 1) % steps]);
                indexData.Add(tubeStartIndices[(i + 1) % steps]);

                // create end cap polys
                indexData.Add(tubeEndIndices[i]);
                indexData.Add(capEndIndices[i]);
                indexData.Add(capEndIndices[(i + 1) % steps]);

                indexData.Add(tubeEndIndices[i]);
                indexData.Add(capEndIndices[(i + 1) % steps]);
                indexData.Add(tubeEndIndices[(i + 1) % steps]);

                // create start tip polys
                indexData.Add(tipStartIndex);
                indexData.Add(capStartIndices[i]);
                indexData.Add(capStartIndices[(i + 1) % steps]);

                // create end tip polys
                indexData.Add(tipEndIndex);
                indexData.Add(capEndIndices[i]);
                indexData.Add(capEndIndices[(i + 1) % steps]);
            }
        }
Example #26
0
        public static void Render3DLineNoPrep(this WorldView world, Frustum clippingFrustum, Vector3 start, Vector3 end, Color color, double width = 1)
        {
            if (clippingFrustum.ClipLine(ref start, ref end))
            {
                double unitsPerPixelStart = world.GetWorldUnitsPerScreenPixelAtPosition(start);
                double unitsPerPixelEnd   = world.GetWorldUnitsPerScreenPixelAtPosition(end);

                Vector3   delta           = start - end;
                var       deltaLength     = delta.Length;
                Matrix4X4 rotateTransform = Matrix4X4.CreateRotation(new Quaternion(Vector3.UnitX + new Vector3(.0001, -.00001, .00002), -delta / deltaLength));
                Matrix4X4 scaleTransform  = Matrix4X4.CreateScale(deltaLength, 1, 1);
                Vector3   lineCenter      = (start + end) / 2;
                Matrix4X4 lineTransform   = scaleTransform * rotateTransform * Matrix4X4.CreateTranslation(lineCenter);

                var startScale = unitsPerPixelStart * width;
                var endScale   = unitsPerPixelEnd * width;
                for (int i = 0; i < unscaledLineMesh.Vertices.Count; i++)
                {
                    var vertexPosition = unscaledLineMesh.Vertices[i];
                    if (vertexPosition.X < 0)
                    {
                        scaledLineMesh.Vertices[i] = new Vector3Float(vertexPosition.X, vertexPosition.Y * startScale, vertexPosition.Z * startScale);
                    }
                    else
                    {
                        scaledLineMesh.Vertices[i] = new Vector3Float(vertexPosition.X, vertexPosition.Y * endScale, vertexPosition.Z * endScale);
                    }
                }

                if (true)
                {
                    GL.Color4(color.Red0To255, color.Green0To255, color.Blue0To255, color.Alpha0To255);

                    if (color.Alpha0To1 < 1)
                    {
                        GL.Enable(EnableCap.Blend);
                    }
                    else
                    {
                        //GL.Disable(EnableCap.Blend);
                    }

                    GL.MatrixMode(MatrixMode.Modelview);
                    GL.PushMatrix();
                    GL.MultMatrix(lineTransform.GetAsFloatArray());

                    GL.Begin(BeginMode.Triangles);
                    for (int faceIndex = 0; faceIndex < scaledLineMesh.Faces.Count; faceIndex++)
                    {
                        var face     = scaledLineMesh.Faces[faceIndex];
                        var vertices = scaledLineMesh.Vertices;
                        var position = vertices[face.v0];
                        GL.Vertex3(position.X, position.Y, position.Z);
                        position = vertices[face.v1];
                        GL.Vertex3(position.X, position.Y, position.Z);
                        position = vertices[face.v2];
                        GL.Vertex3(position.X, position.Y, position.Z);
                    }
                    GL.End();
                    GL.PopMatrix();
                }
                else
                {
                    scaledLineMesh.MarkAsChanged();

                    GLHelper.Render(scaledLineMesh, color, lineTransform, RenderTypes.Shaded);
                }
            }
        }
        static public void CreateCylinder(VectorPOD <ColorVertexData> colorVertexData, VectorPOD <int> indexData, Vector3 startPos, Vector3 endPos, double radius, int steps, Color color, double layerHeight)
        {
            var direction           = endPos - startPos;
            var directionNormal     = direction.GetNormal();
            var startSweepDirection = Vector3.GetPerpendicular(startPos, endPos).GetNormal();

            int[] tubeStartIndices = new int[steps];
            int[] tubeEndIndices   = new int[steps];

            int[] capStartIndices = new int[steps];
            int[] capEndIndices   = new int[steps];

            double halfHeight = layerHeight / 2 + (layerHeight * .1);
            double halfWidth  = radius;
            double zScale     = halfHeight / radius;
            double xScale     = halfWidth / radius;

            // Adjust start/end positions to be centered on Z for the given layer height
            startPos.Z -= halfHeight;
            endPos.Z   -= halfHeight;

            var scale               = new Vector3(xScale, xScale, zScale);
            var rotateAngle         = Vector3Ex.Cross(startSweepDirection, direction);
            var startCapStartNormal = Vector3Ex.Transform(startSweepDirection, Matrix4X4.CreateRotation(rotateAngle, MathHelper.Tau / 8));
            var startCapEndNormal   = Vector3Ex.Transform(startSweepDirection, Matrix4X4.CreateRotation(-rotateAngle, MathHelper.Tau / 8));

            for (int i = 0; i < steps; i++)
            {
                var rotationMatrix = Matrix4X4.CreateRotation(direction, MathHelper.Tau / (steps * 2) + MathHelper.Tau / (steps) * i);
                // create tube ends verts
                var tubeNormal = Vector3Ex.Transform(startSweepDirection, rotationMatrix);
                var offset     = Vector3Ex.Transform(startSweepDirection * radius, rotationMatrix) * scale;

                var tubeStart = startPos + offset;
                tubeStartIndices[i] = colorVertexData.Count;
                colorVertexData.Add(new ColorVertexData(tubeStart, tubeNormal, color));

                var tubeEnd = endPos + offset;
                tubeEndIndices[i] = colorVertexData.Count;
                colorVertexData.Add(new ColorVertexData(tubeEnd, tubeNormal, color));

                // create cap verts
                var capStartNormal = Vector3Ex.Transform(startCapStartNormal, rotationMatrix);
                capStartNormal = (capStartNormal * scale).GetNormal();
                var capStartOffset = capStartNormal * radius * scale;
                var capStart       = startPos + capStartOffset;
                capStartIndices[i] = colorVertexData.Count;
                colorVertexData.Add(new ColorVertexData(capStart, capStartNormal, color));

                var capEndNormal = Vector3Ex.Transform(startCapEndNormal, rotationMatrix);
                capEndNormal = (capEndNormal * scale).GetNormal();
                var capEndOffset = capEndNormal * radius * scale;
                var capEnd       = endPos + capEndOffset;
                capEndIndices[i] = colorVertexData.Count;
                colorVertexData.Add(new ColorVertexData(capEnd, capEndNormal, color));
            }

            int tipStartIndex = colorVertexData.Count;
            var tipOffset     = directionNormal * radius;

            tipOffset *= scale;
            colorVertexData.Add(new ColorVertexData(startPos - tipOffset, -directionNormal, color));
            int tipEndIndex = colorVertexData.Count;

            colorVertexData.Add(new ColorVertexData(endPos + tipOffset, directionNormal, color));

            for (int i = 0; i < steps; i++)
            {
                // create tube polys
                indexData.Add(tubeStartIndices[i]);
                indexData.Add(tubeEndIndices[i]);
                indexData.Add(tubeEndIndices[(i + 1) % steps]);

                indexData.Add(tubeStartIndices[i]);
                indexData.Add(tubeEndIndices[(i + 1) % steps]);
                indexData.Add(tubeStartIndices[(i + 1) % steps]);

                // create start cap polys
                indexData.Add(tubeStartIndices[i]);
                indexData.Add(capStartIndices[i]);
                indexData.Add(capStartIndices[(i + 1) % steps]);

                indexData.Add(tubeStartIndices[i]);
                indexData.Add(capStartIndices[(i + 1) % steps]);
                indexData.Add(tubeStartIndices[(i + 1) % steps]);

                // create end cap polys
                indexData.Add(tubeEndIndices[i]);
                indexData.Add(capEndIndices[i]);
                indexData.Add(capEndIndices[(i + 1) % steps]);

                indexData.Add(tubeEndIndices[i]);
                indexData.Add(capEndIndices[(i + 1) % steps]);
                indexData.Add(tubeEndIndices[(i + 1) % steps]);

                // create start tip polys
                indexData.Add(tipStartIndex);
                indexData.Add(capStartIndices[i]);
                indexData.Add(capStartIndices[(i + 1) % steps]);

                // create end tip polys
                indexData.Add(tipEndIndex);
                indexData.Add(capEndIndices[i]);
                indexData.Add(capEndIndices[(i + 1) % steps]);
            }
        }
Example #28
0
 public Rotate(CsgObject objectToRotate, Vector3 radians, string name = "")
     : base(objectToRotate, name)
 {
     rotateCache = radians;
     transform   = Matrix4X4.CreateRotation(radians);
 }
Example #29
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));
        }
        public static void Render3DLineNoPrep(this WorldView world,
                                              Frustum clippingFrustum,
                                              Vector3 start,
                                              Vector3 end,
                                              Color color,
                                              double width    = 1,
                                              bool startArrow = false,
                                              bool endArrow   = false)
        {
            if (clippingFrustum.ClipLine(ref start, ref end))
            {
                double startScale = world.GetWorldUnitsPerScreenPixelAtPosition(start);
                double endScale   = world.GetWorldUnitsPerScreenPixelAtPosition(end);

                Vector3 delta       = start - end;
                var     normal      = delta.GetNormal();
                var     arrowWidth  = 3 * width;
                var     arrowLength = 6 * width;
                if (startArrow || endArrow)
                {
                    // move the start and end points
                    if (startArrow)
                    {
                        start -= normal * startScale * arrowLength;
                    }

                    if (endArrow)
                    {
                        end += normal * endScale * arrowLength;
                    }

                    delta = start - end;
                }

                var       deltaLength     = delta.Length;
                var       rotateTransform = Matrix4X4.CreateRotation(new Quaternion(Vector3.UnitX + new Vector3(.0001, -.00001, .00002), -delta / deltaLength));
                var       scaleTransform  = Matrix4X4.CreateScale(deltaLength, 1, 1);
                Vector3   lineCenter      = (start + end) / 2;
                Matrix4X4 lineTransform   = scaleTransform * rotateTransform * Matrix4X4.CreateTranslation(lineCenter);

                var startWidth = startScale * width;
                var endWidth   = endScale * width;
                for (int i = 0; i < unscaledLineMesh.Vertices.Count; i++)
                {
                    var vertexPosition = unscaledLineMesh.Vertices[i];
                    if (vertexPosition.X < 0)
                    {
                        scaledLineMesh.Vertices[i] = new Vector3Float(vertexPosition.X, vertexPosition.Y * startWidth, vertexPosition.Z * startWidth);
                    }
                    else
                    {
                        scaledLineMesh.Vertices[i] = new Vector3Float(vertexPosition.X, vertexPosition.Y * endWidth, vertexPosition.Z * endWidth);
                    }
                }

                // render the line mesh directly
                GL.Color4(color.Red0To255, color.Green0To255, color.Blue0To255, color.Alpha0To255);
                GL.Enable(EnableCap.Blend);

                GL.MatrixMode(MatrixMode.Modelview);
                GL.PushMatrix();
                GL.MultMatrix(lineTransform.GetAsFloatArray());

                GL.Begin(BeginMode.Triangles);
                for (int faceIndex = 0; faceIndex < scaledLineMesh.Faces.Count; faceIndex++)
                {
                    var face     = scaledLineMesh.Faces[faceIndex];
                    var vertices = scaledLineMesh.Vertices;
                    var position = vertices[face.v0];
                    GL.Vertex3(position.X, position.Y, position.Z);
                    position = vertices[face.v1];
                    GL.Vertex3(position.X, position.Y, position.Z);
                    position = vertices[face.v2];
                    GL.Vertex3(position.X, position.Y, position.Z);
                }
                GL.End();

                GL.PopMatrix();

                // render the arrows if any
                if (startArrow)
                {
                    RenderHead(start, startScale, normal, arrowWidth, arrowLength, false);
                }

                if (endArrow)
                {
                    RenderHead(end, startScale, normal, arrowWidth, arrowLength, true);
                }
            }
        }