private Transform CalcEdgeTransform(int edgeIndex)
        {
            ShapeEdge edge       = this.shape.Structure.Edges[edgeIndex];
            Vector    pos1       = this.shape.VertexPositions[edge.Index1];
            Vector    pos2       = this.shape.VertexPositions[edge.Index2];
            Vector    middlePos  = 0.5 * (pos1 + pos2);
            double    edgeLength = pos1.DistanceToPoint(pos2);
            double    angle      = Vector.AngleBetween(Vector.UnitX, pos2 - pos1);

            TransformGroup result = new TransformGroup();

            result.Children.Add(new TranslateTransform(-0.5, -0.5));
            result.Children.Add(new ScaleTransform(edgeLength, this.shape.EdgeWidths[edgeIndex]));
            result.Children.Add(new RotateTransform(MathHelper.ToDegrees(angle)));
            result.Children.Add(new TranslateTransform(middlePos.X, middlePos.Y));

            return(result);
        }
        static void LengthAngleDependenceExperiment(
            VertexConstraints constraint1, VertexConstraints constraint2, string fileName)
        {
            const int GeneratedPointCount = 100000;

            List <Vector> lengthAnglePoints = new List <Vector>();
            double        maxLength         = 0;

            for (int i = 0; i < GeneratedPointCount; ++i)
            {
                double randomX1 = constraint1.MinCoord.X + Random.Double() * (constraint1.MaxCoord.X - constraint1.MinCoord.X);
                double randomY1 = constraint1.MinCoord.Y + Random.Double() * (constraint1.MaxCoord.Y - constraint1.MinCoord.Y);
                double randomX2 = constraint2.MinCoord.X + Random.Double() * (constraint2.MaxCoord.X - constraint2.MinCoord.X);
                double randomY2 = constraint2.MinCoord.Y + Random.Double() * (constraint2.MaxCoord.Y - constraint2.MinCoord.Y);
                Vector vector1  = new Vector(randomX1, randomY1);
                Vector vector2  = new Vector(randomX2, randomY2);
                if (vector1 == vector2)
                {
                    continue;
                }

                Vector diff   = vector2 - vector1;
                double length = diff.Length;
                double angle  = Vector.AngleBetween(Vector.UnitX, diff);
                lengthAnglePoints.Add(new Vector(length, angle));

                maxLength = Math.Max(maxLength, length);
            }

            //ShapeConstraints constraintSet = ShapeConstraints.CreateFromConstraints(
            //    CreateSimpleShapeModel1(),
            //    new[] { constraint1, constraint2 },
            //    new[] { new EdgeConstraints(1) },
            //    1,
            //    1);
            BoxSetLengthAngleConstraints lengthAngleConstraints =
                BoxSetLengthAngleConstraints.FromVertexConstraints(constraint1, constraint2, 2, 0);

            const int lengthImageSize = 360;
            const int angleImageSize  = 360;
            double    lengthScale     = (lengthImageSize - 20) / maxLength;

            //Image2D<bool> myAwesomeMask = new Image2D<bool>(lengthImageSize, angleImageSize);
            //LengthAngleSpaceSeparatorSet myAwesomeSeparator = new LengthAngleSpaceSeparatorSet(constraint1, constraint2);
            //for (int i = 0; i < lengthImageSize; ++i)
            //    for (int j = 0; j < angleImageSize; ++j)
            //    {
            //        double length = i / lengthScale;
            //        double angle = MathHelper.ToRadians(j - 180.0);
            //        if (myAwesomeSeparator.IsInside(length, angle))
            //            myAwesomeMask[i, j] = true;
            //    }

            using (Bitmap image = new Bitmap(lengthImageSize, angleImageSize))
                using (Graphics graphics = Graphics.FromImage(image))
                {
                    graphics.Clear(Color.White);

                    // Draw generated points
                    for (int i = 0; i < lengthAnglePoints.Count; ++i)
                    {
                        DrawLengthAngle(graphics, Pens.Black, lengthAnglePoints[i].X, lengthAnglePoints[i].Y, lengthScale, 1);
                    }

                    // Draw estimated ranges
                    foreach (BoxLengthAngleConstraints child in lengthAngleConstraints.ChildConstraints)
                    {
                        DrawLengthAngleConstraintBox(graphics, Pens.Green, child, lengthScale);
                    }
                    DrawLengthAngleConstraintBox(graphics, new Pen(Color.Red, 2), lengthAngleConstraints.OverallRange, lengthScale);

                    // Draw constraint corners
                    //for (int i = 0; i < 4; ++i)
                    //{
                    //    for (int j = 0; j < 4; ++j)
                    //    {
                    //        Vector diff = constraint2.Corners[j] - constraint1.Corners[i];
                    //        DrawLengthAngle(diff.Length, Vector.AngleBetween(Vector.UnitX, diff), lengthScale, 5, graphics, Pens.Blue);
                    //    }
                    //}

                    // Draw my awesome separation lines
                    //for (int i = 0; i < lengthImageSize - 1; ++i)
                    //    for (int j = 0; j < lengthImageSize - 1; ++j)
                    //    {
                    //        bool border = false;
                    //        border |= myAwesomeMask[i, j] != myAwesomeMask[i + 1, j];
                    //        border |= myAwesomeMask[i, j] != myAwesomeMask[i, j + 1];
                    //        border |= myAwesomeMask[i, j] != myAwesomeMask[i + 1, j + 1];
                    //        if (border)
                    //            image.SetPixel(i, j, Color.Orange);
                    //    }

                    //graphics.DrawString(
                    //    String.Format("Max length is {0:0.0}", maxLength), SystemFonts.DefaultFont, Brushes.Green, 5, 5);

                    image.Save(fileName);
                }
        }