/// <inheritdoc/> public bool IsTrueInAllPictures(Pictures pictures, Theorem theorem) { #region Ensure all inner objects are constructed // Go through the inner objects var objectsToConstruct = theorem.GetInnerConfigurationObjects() // And the object needed to construct them .GetDefiningObjects() // That are constructible .OfType <ConstructedConfigurationObject>() // That are not already constructed .Where(innerObject => !pictures.First().Contains(innerObject)); // Construct all of them foreach (var constructedObject in objectsToConstruct) { // Safely perform var data = GeneralUtilities.TryExecute( // The construction of the object () => _constructor.Construct(pictures, constructedObject, addToPictures: true), // Ignore any exception in this step (it will be solved in the next one) (InconsistentPicturesException _) => { }); // If there has been an inconsistency, we're sad and say this theorem is not true if (data == null) { return(false); } // If there is an inconstructible object, the same if (data.InconstructibleObject != null) { return(false); } } #endregion // If we got here, all needed objects are constructed. We need the theorem objects var theoremObjects = new Dictionary <TheoremObject, Dictionary <Picture, IAnalyticObject> >(); #region Construct theorem objects // Get the inner base theorem objects, i.e. Line / Circle / Point // Then we will reconstruct the other i.e., LineSegment later... var baseTheoremObjects = theorem.InvolvedObjects // Each theorem object can bring more of them .SelectMany(theoremObject => theoremObject switch { // If the object is already base, we return it BaseTheoremObject _ => theoremObject.ToEnumerable(), // If we have a line segment, then its inner objects are points LineSegmentTheoremObject lineSegment => lineSegment.PointSet, // Unhandled cases _ => throw new ConstructorException($"Unhandled type of {nameof(TheoremObject)}: {theoremObject.GetType()}") })
/// <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); } }