public static Tuple <Transform3D, Transform3D> GetTransformTo2D_custom_CLEAN(HelperClassesWPF.Controls3D.Debug3DWindow window, ITriangle triangle)
        {
            Vector3D zUp = new Vector3D(0, 0, 1);

            if (Math.Abs(Vector3D.DotProduct(triangle.NormalUnit, zUp)).IsNearValue(1))
            {
                // It's already 2D
                window.AddMessage("already 2D");
                return(new Tuple <Transform3D, Transform3D>(new TranslateTransform3D(0, 0, -triangle.Point0.Z), new TranslateTransform3D(0, 0, triangle.Point0.Z)));
            }

            // Don't bother with a double vector, just rotate the normal
            Quaternion rotation = Math3D.GetRotation(triangle.NormalUnit, zUp);

            Transform3DGroup transformTo2D = new Transform3DGroup();

            transformTo2D.Children.Add(new RotateTransform3D(new QuaternionRotation3D(rotation)));

            // Need to rotate the point so that it's parallel to the XY plane, then subtract off it's Z
            Point3D rotatedXYPlane = transformTo2D.Transform(triangle[0]);

            transformTo2D.Children.Add(new TranslateTransform3D(0, 0, -rotatedXYPlane.Z));

            Transform3DGroup transformTo3D = new Transform3DGroup();

            transformTo3D.Children.Add(new TranslateTransform3D(0, 0, rotatedXYPlane.Z));
            transformTo3D.Children.Add(new RotateTransform3D(new QuaternionRotation3D(rotation.ToReverse())));

            return(new Tuple <Transform3D, Transform3D>(transformTo2D, transformTo3D));
        }
        private static void VisualizeTranform2D_overall(ITriangle plane, Point3D[] polyPoints)
        {
            const double AXISLEN   = 3;
            const double LINETHICK = .01;
            const double DOTRAD    = .05;

            var window = new HelperClassesWPF.Controls3D.Debug3DWindow();

            window.AddAxisLines(AXISLEN, LINETHICK * .5);

            #region plane

            window.AddDots(plane.PointArray, DOTRAD * 1.1, UtilityWPF.ColorFromHex("FF0000"));
            window.AddPlane(plane, AXISLEN, UtilityWPF.ColorFromHex("FF0000"));

            window.AddLine(plane[0], plane[1], LINETHICK, Colors.White);
            window.AddLine(plane[0], plane[2], LINETHICK, Colors.Black);

            #endregion

            //var transform2D = GetTransformTo2D_custom_CLEAN(window, plane);
            var transform2D = GetTransformTo2D_custom_USES_DBLVECT(window, plane);

            #region transformed 2D - natural

            var transformed_natural = polyPoints.
                                      //Select(o => transform2D.Item1.Transform(o).ToPoint2D().ToPoint3D()).
                                      Select(o => transform2D.Item1.Transform(o)).
                                      ToArray();

            ITriangle transformedPlane_natural = new Triangle(transformed_natural[0], transformed_natural[1], transformed_natural[2]);

            window.AddDots(transformed_natural, DOTRAD, UtilityWPF.ColorFromHex("70FF70"));
            window.AddPlane(transformedPlane_natural, AXISLEN, UtilityWPF.ColorFromHex("40FF40"), UtilityWPF.ColorFromHex("C0FFC0"));

            #endregion
            #region transformed 2D - forced

            var transformed_forced = polyPoints.
                                     Select(o => transform2D.Item1.Transform(o).ToPoint2D().ToPoint3D()).
                                     //Select(o => transform2D.Item1.Transform(o)).
                                     ToArray();

            ITriangle transformedPlane_forced = new Triangle(transformed_forced[0], transformed_forced[1], transformed_forced[2]);

            window.AddDots(transformed_forced, DOTRAD, UtilityWPF.ColorFromHex("FF7070"));
            window.AddPlane(transformedPlane_forced, AXISLEN, UtilityWPF.ColorFromHex("FF4040"), UtilityWPF.ColorFromHex("FFC0C0"));

            #endregion

            #region transformed back 3D - natural

            var transformedBack_natural = transformed_natural.
                                          Select(o => transform2D.Item2.Transform(o)).
                                          ToArray();

            window.AddDots(transformedBack_natural, DOTRAD, UtilityWPF.ColorFromHex("C0FFC0"));
            window.AddPlane(new Triangle(transformedBack_natural[0], transformedBack_natural[1], transformedBack_natural[2]), AXISLEN, UtilityWPF.ColorFromHex("C0FFC0"));

            #endregion
            #region transformed back 3D - forced

            var transformedBack_forced = transformed_forced.
                                         Select(o => transform2D.Item2.Transform(o)).
                                         ToArray();

            window.AddDots(transformedBack_forced, DOTRAD, UtilityWPF.ColorFromHex("FFC0C0"));
            window.AddPlane(new Triangle(transformedBack_forced[0], transformedBack_forced[1], transformedBack_forced[2]), AXISLEN, UtilityWPF.ColorFromHex("FFC0C0"));

            #endregion

            window.Show();
        }
        public static Tuple <Transform3D, Transform3D> GetTransformTo2D_custom_USES_DBLVECT(HelperClassesWPF.Controls3D.Debug3DWindow window, ITriangle triangle)
        {
            if (Math.Abs(Vector3D.DotProduct(triangle.NormalUnit, new Vector3D(0, 0, 1))).IsNearValue(1))
            {
                // It's already 2D
                window.AddMessage("already 2D");
                return(new Tuple <Transform3D, Transform3D>(new TranslateTransform3D(0, 0, -triangle.Point0.Z), new TranslateTransform3D(0, 0, triangle.Point0.Z)));
            }

            Vector3D line1      = triangle.Point1 - triangle.Point0;
            Vector3D randomOrth = Math3D.GetOrthogonal(line1, triangle.Point2 - triangle.Point0);

            DoubleVector from = new DoubleVector(line1, randomOrth);
            DoubleVector to   = new DoubleVector(new Vector3D(1, 0, 0), new Vector3D(0, 1, 0));

            Quaternion rotation = from.GetRotation(to);

            Transform3DGroup transformTo2D = new Transform3DGroup();

            transformTo2D.Children.Add(new RotateTransform3D(new QuaternionRotation3D(rotation)));

            // Need to rotate the point so that it's parallel to the XY plane, then subtract off it's Z
            Point3D rotatedXYPlane = transformTo2D.Transform(triangle[0]);

            transformTo2D.Children.Add(new TranslateTransform3D(0, 0, -rotatedXYPlane.Z));

            Transform3DGroup transformTo3D = new Transform3DGroup();

            transformTo3D.Children.Add(new TranslateTransform3D(0, 0, rotatedXYPlane.Z));
            transformTo3D.Children.Add(new RotateTransform3D(new QuaternionRotation3D(rotation.ToReverse())));

            return(new Tuple <Transform3D, Transform3D>(transformTo2D, transformTo3D));
        }
        private void RotateTestCase_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                const double AXISLEN   = 3;
                const double LINETHICK = .01;
                const double DOTRAD    = .05;

                Color colorFrom1 = UtilityWPF.ColorFromHex("666");
                Color colorFrom2 = UtilityWPF.ColorFromHex("000");
                Color colorTo1   = UtilityWPF.ColorFromHex("FFF");
                Color colorTo2   = UtilityWPF.ColorFromHex("CCC");

                #region initial conditions

                //Vector3D from1 = new Vector3D(-1.08846101452991, 0, 0);
                //Vector3D from2 = new Vector3D(0, 0, 5.4064833988896);
                Vector3D from1 = new Vector3D(-1, 0, 0);
                Vector3D from2 = new Vector3D(0, 0, 1);
                Vector3D to1   = new Vector3D(1, 0, 0);
                Vector3D to2   = new Vector3D(0, 1, 0);

                #region RANDOM ROTATE

                // This rotation doesn't matter.  It seems to be the perfect 90 then perfect 180
                //Transform3D randRot = new RotateTransform3D(new QuaternionRotation3D(Math3D.GetRandomRotation()));
                //from1 = randRot.Transform(from1);
                //from2 = randRot.Transform(from2);
                //to1 = randRot.Transform(to1);
                //to2 = randRot.Transform(to2);

                #endregion
                #region SLIGHT ADJUST

                //double maxAngle = .1;
                //from1 = Math3D.GetRandomVector_Cone(from1, maxAngle);
                //from2 = Math3D.GetRandomVector_Cone(from2, maxAngle);
                //to1 = Math3D.GetRandomVector_Cone(to1, maxAngle);
                //to2 = Math3D.GetRandomVector_Cone(to2, maxAngle);

                #endregion

                var window = new HelperClassesWPF.Controls3D.Debug3DWindow()
                {
                    Background = new SolidColorBrush(UtilityWPF.ColorFromHex("AAA")),
                };

                window.AddLine(new Point3D(0, 0, 0), from1.ToPoint(), LINETHICK, colorFrom1);
                window.AddLine(new Point3D(0, 0, 0), from2.ToPoint(), LINETHICK, colorFrom2);

                window.AddLine(new Point3D(0, 0, 0), to1.ToPoint(), LINETHICK, colorTo1);
                window.AddLine(new Point3D(0, 0, 0), to2.ToPoint(), LINETHICK, colorTo2);

                window.Show();

                #endregion

                #region quat multiply

                window = new HelperClassesWPF.Controls3D.Debug3DWindow()
                {
                    Background = new SolidColorBrush(UtilityWPF.ColorFromHex("777")),
                };

                window.AddAxisLines(AXISLEN, LINETHICK * .5);

                Quaternion rotation_mult = Math3D.GetRotation(from1, from2, to1, to2);
                //Quaternion rotation_mult = GetRotation_fixed(window, from1, from2, to1, to2);

                ITriangle planeInit = new Triangle(new Point3D(-1, 0, 0), new Point3D(0, 0, 0), new Point3D(0, 0, 1));

                Point3D[] planeRotatedPoints = planeInit.PointArray.ToArray();
                rotation_mult.GetRotatedVector(planeRotatedPoints);

                ITriangle planeRotated = new Triangle(planeRotatedPoints[0], planeRotatedPoints[1], planeRotatedPoints[2]);

                window.AddLine(new Point3D(0, 0, 0), (planeInit[1] - planeInit[0]).ToPoint(), LINETHICK, colorFrom1);
                window.AddLine(new Point3D(0, 0, 0), (planeInit[2] - planeInit[0]).ToPoint(), LINETHICK, colorFrom2);
                window.AddLine(new Point3D(0, 0, 0), (planeRotated[1] - planeRotated[0]).ToPoint(), LINETHICK, colorTo1);
                window.AddLine(new Point3D(0, 0, 0), (planeRotated[2] - planeRotated[0]).ToPoint(), LINETHICK, colorTo2);

                window.AddPlane(planeInit, AXISLEN, UtilityWPF.ColorFromHex("00FF00"), UtilityWPF.ColorFromHex("A0FFA0"));
                window.AddPlane(planeRotated, AXISLEN, UtilityWPF.ColorFromHex("FF0000"), UtilityWPF.ColorFromHex("FFA0A0"));

                window.Show();

                #endregion

                #region tranform group

                //---------------
                // The transform group with two rotate transforms had the same issue as quaternion.multiply
                // A perfect 180 degree quaternion caused random failure
                //---------------

                //window = new HelperClassesWPF.Controls3D.Debug3DWindow();

                //window.AddAxisLines(AXISLEN, LINETHICK * .5);

                //Transform3D transform = GetRotation_transform(window, from1, from2, to1, to2);

                //planeRotatedPoints = planeInit.PointArray.
                //    Select(o => transform.Transform(o)).
                //    ToArray();

                //planeRotated = new Triangle(planeRotatedPoints[0], planeRotatedPoints[1], planeRotatedPoints[2]);

                //window.AddLine(new Point3D(0, 0, 0), (planeInit[1] - planeInit[0]).ToPoint(), LINETHICK, colorFrom1);
                //window.AddLine(new Point3D(0, 0, 0), (planeInit[2] - planeInit[0]).ToPoint(), LINETHICK, colorFrom2);
                //window.AddLine(new Point3D(0, 0, 0), (planeRotated[1] - planeRotated[0]).ToPoint(), LINETHICK, colorTo1);
                //window.AddLine(new Point3D(0, 0, 0), (planeRotated[2] - planeRotated[0]).ToPoint(), LINETHICK, colorTo2);

                //window.AddPlane(planeInit, AXISLEN, UtilityWPF.ColorFromHex("8F8"), UtilityWPF.ColorFromHex("A0FFA0"));
                //window.AddPlane(planeRotated, AXISLEN, UtilityWPF.ColorFromHex("F88"), UtilityWPF.ColorFromHex("FFA0A0"));

                //window.Show();

                #endregion
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString(), this.Title, MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }