Ejemplo n.º 1
0
        /// <summary>
        /// Constructs analytic objects having a given layout. These objects are arranged randomly in a
        /// way that objects not too close or far from each other and the generated layouts are quite uniform,
        /// for example a random triangle is scalene and acute, a random quadrilateral has its angles ordered
        /// uniformly. This is good for finding theorems in multiple pictures, because it makes it very improbable
        /// that the limited imprecise floating point arithmetics will fail.
        /// </summary>
        /// <param name="layout">The layout of loose objects to be drawn.</param>
        /// <returns>The constructed analytic objects. Their count depends on the layout.</returns>
        public static IAnalyticObject[] ConstructUniformLayout(LooseObjectLayout layout)
        {
            // Switch based on the layout
            switch (layout)
            {
            // In line segment case everything is fixed
            case LooseObjectLayout.LineSegment:

                // Return the points in an array
                return(new IAnalyticObject[] { new Point(0, 0), new Point(1, 0) });

            // With three points we'll create a random acute scalene triangle
            case LooseObjectLayout.Triangle:
            {
                // Create the points
                var(point1, point2, point3) = RandomLayoutsHelpers.ConstructRandomScaleneAcuteTriangle();

                // Return them in an array
                return(new IAnalyticObject[] { point1, point2, point3 });
            }

            // In quadrilateral case we will create a random uniform convex one
            case LooseObjectLayout.Quadrilateral:
            {
                // Create the points
                var(point1, point2, point3, point4) = RandomLayoutsHelpers.ConstructRandomUniformConvexQuadrilateral();

                // Return them in an array
                return(new IAnalyticObject[] { point1, point2, point3, point4 });
            }

            // In cyclic quadrilateral case we will create a random uniform one
            case LooseObjectLayout.CyclicQuadrilateral:
            {
                // Create the points
                var(point1, point2, point3, point4) = RandomLayoutsHelpers.ConstructRandomUniformCyclicQuadrilateral();

                // Return them in an array
                return(new IAnalyticObject[] { point1, point2, point3, point4 });
            }

            // In line and point case the line is fixed and the point is arbitrary
            case LooseObjectLayout.LineAndPoint:
            {
                // Create the objects
                var(line, point) = RandomLayoutsHelpers.ConstructLineAndRandomPointNotLyingOnIt();

                // Return them in an array
                return(new IAnalyticObject[] { line, point });
            }

            // In line and two points case the line is fixed and the points are arbitrary
            case LooseObjectLayout.LineAndTwoPoints:
            {
                // Create the objects
                var(line, point1, point2) = RandomLayoutsHelpers.ConstructLineAndTwoRandomPointsNotLyingOnIt();

                // Return them in an array
                return(new IAnalyticObject[] { line, point1, point2 });
            }

            // In right triangle case the right angle will be at the first point
            case LooseObjectLayout.RightTriangle:
            {
                // Create the points
                var(point1, point2, point3) = RandomLayoutsHelpers.ConstructRandomRightTriangle();

                // Return them in an array
                return(new IAnalyticObject[] { point1, point2, point3 });
            }

            // Unhandled cases
            default:
                throw new ConstructorException($"Unhandled value of {nameof(LooseObjectLayout)}: {layout}.");
            }
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Constructs a given <see cref="Configuration"/> to a given number of pictures, using the
        /// loose object drawing that is in some cases more flexible than the drawing from  the method
        /// <see cref="LooseObjectLayoutDrawing.ConstructUniformLayout(LooseObjectLayout)"/>. These cases
        /// are two: Triangle, when <see cref="RandomLayoutsHelpers.ConstructNiceAcuteTriangle"/> is used,
        /// and Quadrilateral, when <see cref="RandomLayoutsHelpers.ConstructRandomConvexQuadrilateralWithHorizontalSide"/>
        /// or <see cref="RandomLayoutsHelpers.ConstructRandomConvexQuadrilateralWithHorizontalDiagonal"/>
        /// if the picture is symmetric in such a way that it would look better with a horizontal diagonal,
        /// or <see cref="RandomLayoutsHelpers.ConstructRandomConvexQuadrilateralWithHorizontalSide"/> otherwise.
        /// </summary>
        /// <param name="configuration">The configuration to be constructed.</param>
        /// <param name="theorem">The theorem that is used to determine symmetry be constructed.</param>
        /// <param name="numberOfPictures">The number of <see cref="Picture"/>s where the configuration should be drawn.</param>
        /// <returns>The tuple consisting of the pictures and the construction data.</returns>
        /// <exception cref="ConstructorException">Thrown when the construction couldn't be carried out correctly.</exception>
        public static PicturesOfConfiguration ConstructWithFlexibleLayoutRespectingSymmetry(this IGeometryConstructor constructor, Configuration configuration, Theorem theorem, int numberOfPictures)
        {
            #region Loose object layout construction

            // Prepare the function that will construct the loose objects
            IAnalyticObject[] LooseObjectsConstructor()
            {
                // The layout is drawn based on its type
                switch (configuration.LooseObjectsHolder.Layout)
                {
                // We want to draw less uniform triangles
                case LooseObjectLayout.Triangle:
                {
                    // Create the points
                    var(point1, point2, point3) = RandomLayoutsHelpers.ConstructNiceAcuteTriangle();

                    // Return them in an array
                    return(new IAnalyticObject[] { point1, point2, point3 });
                }

                // In quadrilateral cases we need to have a look at its symmetry
                case LooseObjectLayout.Quadrilateral:
                {
                    // Assume we're drawing a quadrilateral ABCD
                    var A = configuration.LooseObjects[0];
                    var B = configuration.LooseObjects[1];
                    var C = configuration.LooseObjects[2];
                    var D = configuration.LooseObjects[3];

                    // We will use a simple local function that checks whether a mapping is symmetric
                    bool IsSymmetric(IReadOnlyDictionary <LooseConfigurationObject, LooseConfigurationObject> mapping)
                    // Take the real mappings
                    => theorem.GetSymmetryMappings(configuration)
                    // And have a look whether any behaves as our
                    .Any(symmetryMapping => mapping.All(pair => symmetryMapping[pair.Key].Equals(pair.Value)));

                    // Find out if the mapping for when the diagonal BD is horizontal is symmetric.
                    // In that case, we need to exchange B and D
                    var canDiagonalBeHorizontal = IsSymmetric(new Dictionary <LooseConfigurationObject, LooseConfigurationObject>
                        {
                            { A, A },
                            { B, D },
                            { C, C },
                            { D, B }
                        });

                    // Find out if the mapping for when the side AB is horizontal is symmetric.
                    // In that case, we need to exchange A, B and also C, D
                    var canSideBeHorizontal = IsSymmetric(new Dictionary <LooseConfigurationObject, LooseConfigurationObject>
                        {
                            { A, B },
                            { B, A },
                            { C, D },
                            { D, C }
                        });

                    // We will want to have horizontal mapping preferably, i.e. if it is possible or the diagonal is not
                    if (canSideBeHorizontal || !canDiagonalBeHorizontal)
                    {
                        // Construct the layout
                        var(point1, point2, point3, point4) = RandomLayoutsHelpers.ConstructRandomConvexQuadrilateralWithHorizontalSide();

                        // Return the points in an array
                        return(new IAnalyticObject[] { point1, point2, point3, point4 });
                    }
                    // Otherwise the side cannot be horizontal, but at least the diagonal can be
                    else
                    {
                        // Construct the layout
                        var(point1, point2, point3, point4) = RandomLayoutsHelpers.ConstructRandomConvexQuadrilateralWithHorizontalDiagonal();

                        // Return the points in an array
                        return(new IAnalyticObject[] { point1, point2, point3, point4 });
                    }
                }

                // By default fall-back to the default uniform layout
                default:
                    return(LooseObjectLayoutDrawing.ConstructUniformLayout(configuration.LooseObjectsHolder.Layout));
                }
            }

            #endregion

            try
            {
                // Try to construct the configuration where the passed theorem holds using our custom layout drawer
                var(pictures, constructionData) = constructor.Construct(configuration, numberOfPictures, LooseObjectsConstructor);

                // Make sure there is no inconstructible object
                if (constructionData.InconstructibleObject != default)
                {
                    throw new ConstructionException("The configuration cannot be constructed, because it contains an inconstructible object.");
                }

                // Make sure there are no duplicates
                if (constructionData.Duplicates != default)
                {
                    throw new ConstructionException("The configuration cannot be constructed, because it contains duplicate objects");
                }

                // If everything is correct, return the pictures
                return(pictures);
            }
            catch (InconsistentPicturesException e)
            {
                // If there is an inconsistency problem, re-throw it with a better exception
                throw new ConstructionException("Construction of the configuration failed due to inconsistencies.", e);
            }
        }