Skip to content
Snippets Groups Projects
Select Git revision
  • 148d327cff9726e425dc0cc546e320937dfe8e59
  • master default
  • JS-based-scroll-rendering
  • Paul_Marius_Level
  • Paul_Marius_2
  • Paul_Marius
  • Andi_Mark
  • be-UnityWebView
  • gitignoreFrameitServer
  • ZimmerBSc
  • Bugfix_StageLoading
  • stages
  • MAZIFAU_Experimental
  • tsc/coneworld
  • tsc/fact-interaction
  • marcel
  • MaZiFAU_TopSort
  • mergeHelper
  • zwischenSpeichern
  • tempAndrToMaster
  • SebBranch
  • 3.0
  • v2.1
  • v2.0
  • v1.0
25 results

ScrollDetails.cs

Blame
  • user avatar
    MaZiFAU authored
    Bug Fixes:
    + invalid casting for List<dynamic>
    + slightly wrong validation for FunctionCallFacts
    + force enable studio stages for validation (not local)
    + Singelton in ScrollStatic wont be recycled due to infinite asyncs
    + SomDoc.Var: parameter binding priority reversed
    148d327c
    History
    ScrollDetails.cs 18.58 KiB
    using Newtonsoft.Json;
    using REST_JSON_API;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using TMPro;
    using UnityEngine;
    using UnityEngine.Networking;
    using static CommunicationEvents;
    
    public class ScrollDetails : MonoBehaviour
    {
        public static ScrollDetails Instance
        {
            get => _Instance;
            set
            {
                if (_Instance != value)
                    Destroy(_Instance);
             
                _Instance = value;
            }
        }
        private static ScrollDetails _Instance;
    
        public WorldCursor cursor;
        public GameObject parameterDisplayPrefab;
        public static Scroll ActiveScroll { get; private set; }
        public GameObject mmtAnswerPopUp;
        private PopupBehavior Popup;
    
        public static List<RenderedScrollFact> ParameterDisplays { get; private set; }
        private static List<ScrollAssignment> LatestBackwartsCompletions;
        private static List<Fact> LatestRenderedHints;
    
        public string currentMmtAnswer;
    
        public bool DynamicScrollDescriptionsActive = true;
        public bool AutomaticHintGenerationActive = true;
    
        private bool SendingViewDone = true;
        private bool DynamicScrollInQue = false;
        private bool MagicInQue = false;
    
        private readonly IReadOnlyList<string> NoDynamicScroll = new List<string>()
        {
            // Insert ScrollURIS that are poorly optimized
        };
    
        void Awake()
        {
            Instance = this;
    
            if (cursor == null)
                cursor = FindObjectOfType<WorldCursor>();
    
            Popup = mmtAnswerPopUp.GetComponent<PopupBehavior>();
            Popup.gameObject.SetActive(true); // force Awake
        }
    
        private void OnEnable()
        {
            ScrollFactHintEvent.AddListener(animateHint);
            NewAssignmentEvent.AddListener(NewAssignmentTrigger);
        }
    
        private void OnDisable()
        {
            ScrollFactHintEvent.RemoveListener(animateHint);
            NewAssignmentEvent.RemoveListener(NewAssignmentTrigger);
        }
    
        private void OnDestroy()
        {
            _Instance = null;
        }
    
        public void SetScroll(Scroll scroll_to_set)
        {
            ActiveScroll = scroll_to_set;
    
            Transform originalScroll = gameObject.transform.GetChild(1);
            Transform originalScrollView = originalScroll.GetChild(1);
            Transform originalViewport = originalScrollView.GetChild(0);
    
            //Clear all current ScrollFacts
            originalViewport.GetChild(0).gameObject.DestroyAllChildren();
    
            originalScroll.GetChild(0).GetComponent<TextMeshProUGUI>().text = ActiveScroll.description;
    
            //ParameterDisplays.ForEach(gameObj => Destroy(gameObj));
            ParameterDisplays = new();
            for (int i = 0; i < ActiveScroll.requiredFacts.Count; i++)
            {
                if (ActiveScroll.requiredFacts[i] is MMTGeneralFact general
                 && general.type is FUNTYPE)
                    continue;
    
                GameObject originalObj =
                    Instantiate(parameterDisplayPrefab, originalViewport.GetChild(0));
    
                RenderedScrollFact originalRSF =
                    originalObj.GetComponentInChildren<RenderedScrollFact>();
    
                ParameterDisplays.Add(originalRSF);
    
                originalRSF.Populate(ActiveScroll, i);
            }
    
            if (StageStatic.stage.solution.ScrollOverwrites
                .TryGetValue(ActiveScroll.ScrollReference, out (string, int, bool)[] population)
            )
                foreach ((string Id, int index, bool show) in population)
                {
                    ParameterDisplays[index].URI = Id;
                    ParameterDisplays[index].transform.parent.gameObject.SetActive(show);
                }
    
            foreach (int i in PrePopulateActiveScroll())
                ParameterDisplays[i].transform.parent.gameObject.SetActive(false);
        }
    
        public bool SetNextEmptyTo(FactObjectUI activator)
        {
            RenderedScrollFact check = ParameterDisplays
                .Find(RSF => RSF != null
                          && RSF.Payload != null
                          && RSF.Payload.Equals(activator));
    
            if (check != null)
            {
                check.URI = null;
                return false;
            }
    
            RenderedScrollFact empty = ParameterDisplays
                .Find(RSF => !RSF.IsSet);
    
            if (empty == null)
                return false;
    
            empty.SetByFactObject(activator);
            return true;
        }
    
        /// <summary>
        /// Secretly populates <see cref="Scroll"/>s
        /// </summary>
        /// <returns><c>Array</c> containing indicis of <see cref="Scroll.requiredFacts"/> to be hidden.</returns>
        private int[] PrePopulateActiveScroll()
        {
            switch (ActiveScroll.ScrollReference)
            {
                case MMTConstants.ScrollCannonBall2D:
                    string lid_override = StageStatic.stage.solution.MyFactSpace.Values
                        .FirstOrDefault(sol => sol is ListFact lf
                                            && lf.ListType is OMA tpOMA
                                            && tpOMA.arguments[0] is OMA tupelOMA_a
                                            && tupelOMA_a.arguments[0] is OMA tupelOMA_b
                                            && tupelOMA_b.applicant is OMS tupelOMS
                                            && tupelOMS.uri == MMTConstants.Product)
                        ?.Id;
    
                    return CaseCanonBall(lid_override);
    
                case MMTConstants.ScrollCannonBall3D:
                    lid_override = StageStatic.stage.solution.MyFactSpace.Values
                    .FirstOrDefault(sol => sol is ListFact lf
                                        && lf.ListType is OMA tpOMA
                                        && tpOMA.arguments[0] is OMA tupelOMA
                                        && tupelOMA.applicant is OMS tupelOMS
                                        && tupelOMS.uri == MMTConstants.Product)
                    ?.Id;
    
                    return CaseCanonBall(lid_override);
    
                case MMTConstants.ScrollCannonBallT3D:
                    lid_override = StageStatic.stage.solution.MyFactSpace.Values
                    .FirstOrDefault(sol => sol is ListFact lf
                                        && lf.ListType is OMA tpOMA
                                        && tpOMA.arguments[0] is OMS triOMS
                                        && triOMS.uri == MMTConstants.Triangle)
                    ?.Id;
    
                    //List<TriangleFact> TriList = StageBehaviour.GenerateMMTCollider();
                    //string[] list = new string[TriList.Count];
                    //for (int i = 0; i < 100; i++)
                    //{
                    //    list[i] = FactAdder.AddFactIfNotFound(TriList[i], out _, false, null, null).Id;
                    //}
                    //lid_override = FactAdder.AddFactIfNotFound(
                    //        new ListFact(list, null, new OMS(MMTConstants.TYPE_TO_OMS[typeof(TriangleFact)]), StageStatic.stage.factState)
                    //    , out _, false, null, null).Id;
    
                    //lid_override = FactAdder.AddFactIfNotFound(
                    //    new DynamicListFact(
                    //        TriList.ConvertAll(t => (object)t),
                    //        SOMDoc.MakeShallowList(TriList.Select(t => t.Defines()).ToArray()),
                    //        new OMS(MMTConstants.TYPE_TO_OMS[typeof(TriangleFact)]),
                    //        StageStatic.stage.factState
                    //    ),
                    //    out _, false, null, null).Id;
    
                    return CaseCanonBall(lid_override);
    
                default:
                    return new int[0];
            }
    
            static int[] CaseCanonBall(string ListId)
            {
                if (ListId == null)
                    return new int[0];
    
                RealLitFact[] reals = StageStatic.stage.solution.MyFactSpace.Values
                    .Select(sol => sol as RealLitFact)
                    .Where(sol => sol != null)
                    .ToArray();
    
                PointFact[] points = StageStatic.stage.solution.MyFactSpace.Values
                    .Select(sol => sol as PointFact)
                    .Where(sol => sol != null)
                    .ToArray();
    
                string
                    pid_ball = points[0].Id,
                    pid_vect = points[1].Id,
                    pid_grav = points[2].Id;
    
                ParameterDisplays[0].URI = pid_ball;
                ParameterDisplays[1].URI = pid_vect;
                ParameterDisplays[2].URI = pid_grav;
                ParameterDisplays[3].URI = reals[0].Id;
                ParameterDisplays[4].URI = ListId;
    
                return new int[] { 0, 1, 2, 3, 4 };
            }
        }
    
        public void MagicButtonTrigger()
        {
            StartCoroutine(_MagicButton());
    
            IEnumerator _MagicButton()
            {
                if (MagicInQue)
                    yield break;  // only need next in que to finish
    
                MagicInQue = true;
                while (!SendingViewDone || DynamicScrollInQue)
                    yield return null; // Wait for last assignment
                MagicInQue = false;
    
                yield return SendView("/scroll/apply");
    
                if (currentMmtAnswer == null)
                {
                    Debug.LogError("Magic FAILED");
                    ScrollApplicationCheckingErrorEvent.Invoke(null);
                }
                else
                {
                    if (VerboseURI)
                        Debug.Log("Magic answers:\n" + currentMmtAnswer);
    
                    ScrollApplicationInfo pushout = JsonConvert.DeserializeObject<ScrollApplicationInfo>(currentMmtAnswer);
    
                    if (pushout.acquiredFacts.Count == 0
                     || pushout.errors.Length > 0)
                    {
                        ScrollApplicationCheckingErrorEvent.Invoke(pushout.errors);
                        PushoutFactFailEvent.Invoke();
                    }
                    else
                        Popup.HidePopUp(); //close error Window
    
                    yield return __GeneratePushoutFacts(pushout.acquiredFacts);
                }
            }
    
            IEnumerator __GeneratePushoutFacts(List<MMTFact> pushoutFacts)
            {
                List<Fact> new_facts = new();
                Dictionary<string, string> old_to_new = new();
                System.DateTime parseTime = System.DateTime.UtcNow;
    
                bool samestep = false;
                for (int i = 0; i < pushoutFacts.Count; i++)
                {
                    List<Fact> new_list = Fact.MMTFactory(pushoutFacts[i].MapURIs(old_to_new));
    
                    if (new_list.Count == 0)
                    {
                        Debug.LogWarning("Parsing on pushout-fact returned empty List -> One of the dependent facts does not exist or parsing failed");
                        continue;
                    }
    
                    foreach (Fact new_fact in new_list)
                    {
                        Fact added = FactAdder.AddFactIfNotFound(new_fact, out bool exists, samestep, null, ActiveScroll.label);
                        if (!exists)
                        {
                            new_facts.Add(added);
                            AnimateExistingFactEvent.Invoke(added.Id, FactWrapper.FactMaterials.Solution);
                            samestep = true;
                        }
                        else
                        {
                            // AnimateExistingFactEvent.Invoke(_new, FactWrapper.FactMaterials.Hint); // Automaticly done in FactRecorder
                            old_to_new.Add(new_fact.Id, added.Id);
                        }
    
                        yield return null;
                    }
                }
    
                Debug.Log($"Facts parsed within {(System.DateTime.UtcNow - parseTime).TotalMilliseconds}ms");
                yield break;
            }
        }
    
        public void NewAssignmentTrigger()
        {
            if (ActiveScroll?.ScrollReference == null
             || NoDynamicScroll.Contains(ActiveScroll.ScrollReference))
                return;
    
            if (AutomaticHintGenerationActive || DynamicScrollDescriptionsActive)
                StartCoroutine(_NewAssignment());
    
            IEnumerator _NewAssignment()
            {
                if (DynamicScrollInQue)
                    yield break; // only need next in que to finish
    
                DynamicScrollInQue = true;
                while (!SendingViewDone)
                    yield return null; // if we dont wait => server will crash
                DynamicScrollInQue = false;
    
                yield return SendView("/scroll/dynamic");
    
                if (currentMmtAnswer == null)
                {
                    Debug.LogError("Dynamic Scroll FAILED");
                }
                else
                {
                    ScrollDynamicInfo scrollDynamicInfo;
                    try
                    {
                        scrollDynamicInfo = JsonConvert.DeserializeObject<ScrollDynamicInfo>(currentMmtAnswer);
                    }
                    catch (JsonSerializationException ex)
                    {
                        Debug.LogException(ex);
                        Debug.LogError("Could not Deserialize MMT aswer for /scroll/dynamic\n" + currentMmtAnswer);
                        yield break;
                    }
    
                    ScrollApplicationCheckingError[] errors = scrollDynamicInfo.errors
                        .Where(err => err.kind != "nonTotal") // expected
                        .ToArray();
    
                    if (errors.Length > 0)
                        ScrollApplicationCheckingErrorEvent.Invoke(errors);
    
                    processScrollDynamicInfo(scrollDynamicInfo);
                }
            }
        }
    
        private IEnumerator SendView(string endpoint)
        {
            SendingViewDone = false;
    
            while (ParameterDisplays == null) // Wait for server
                yield return null;
    
            string body = prepareScrollAssignments();
    
            using UnityWebRequest www = UnityWebRequest.Put(ServerAdress + endpoint, body);
            www.method = UnityWebRequest.kHttpVerbPOST;
            www.SetRequestHeader("Content-Type", "application/json");
    
            System.DateTime sendTime = System.DateTime.UtcNow;
            yield return www.SendWebRequest();
            //if (VerboseURI)
            Debug.LogFormat("Server answerd in : {0}ms"
                , (System.DateTime.UtcNow - sendTime).TotalMilliseconds);
    
            if (www.result == UnityWebRequest.Result.ConnectionError
             || www.result == UnityWebRequest.Result.ProtocolError)
            {
                Debug.LogWarning(www.error);
                currentMmtAnswer = null;
            }
            else
            {
                while (!www.downloadHandler.isDone)
                    yield return null;
    
                currentMmtAnswer = www.downloadHandler.text
                    .Replace("\"float\":null", "\"float\":0.0"); // cannot convert null to value type
            }
    
            SendingViewDone = true;
            yield break;
    
            string prepareScrollAssignments()
            {
                List<ScrollAssignment> assignmentList = new();
    
                for (int i = 0; i < ParameterDisplays.Count; i++)
                {
                    Fact tempFact = ParameterDisplays[i].Fact;
                    if (tempFact != null)
                        assignmentList.Add(new ScrollAssignment(ActiveScroll.requiredFacts[i].@ref.uri, tempFact.Id));
                }
    
                return JsonConvert.SerializeObject(new ScrollApplication(ActiveScroll.ScrollReference, assignmentList));
            }
        }
    
        private void processScrollDynamicInfo(ScrollDynamicInfo scrollDynamicInfo)
        {
            LatestBackwartsCompletions = scrollDynamicInfo.backward_completions.Count > 0
                ? scrollDynamicInfo.backward_completions[0]
                : new List<ScrollAssignment>();
    
            List<string> hintUris = LatestBackwartsCompletions
                .Select(completion => completion.fact.uri)
                .ToList();
    
            //Update Scroll, process data for later hints and update Uri-List for which hints are available
            _processRenderedScroll(scrollDynamicInfo.rendered, hintUris);
    
            if (AutomaticHintGenerationActive)
                //Show that Hint is available for ScrollParameter
                HintAvailableEvent.Invoke(hintUris);
    
            return;
    
            void _processRenderedScroll(Scroll rendered, List<string> hintUris)
            {
                if (DynamicScrollDescriptionsActive)
                { // Update scroll-description
                    Transform scroll = gameObject.transform.GetChild(1).transform;
                    scroll.GetChild(0).GetComponent<TextMeshProUGUI>().text = rendered.description;
                }
    
                LatestRenderedHints = new();
                for (int i = 0; i < rendered.requiredFacts.Count; i++)
                {
                    RenderedScrollFact RenderedScrollFact = ParameterDisplays
                        .Find(RSF => RSF.ScrollFactURI == rendered.requiredFacts[i].@ref.uri);
    
                    if (RenderedScrollFact == null)
                    {
                        Debug.Log($"Descrapancy between requiredFacts and displayed facts:" +
                            $"Could not find display with ref: {rendered.requiredFacts[i].@ref.uri}");
                        continue;
                    }
    
                    if (DynamicScrollDescriptionsActive)
                        //Update ScrollParameter label
                        RenderedScrollFact.Scroll = rendered;
    
                    //If ScrollFact is assigned -> No Hint
                    if (!RenderedScrollFact.IsSet)
                    {
                        List<Fact> HintFactList = Fact.MMTFactory(rendered.requiredFacts[i]);
    
                        foreach (Fact HintFact in HintFactList)
                        {
                            hintUris.Add(HintFact.Id); // == rendered.requiredFacts[i].@ref.uri
                            LatestRenderedHints.Add(HintFact);
                        }
                    }
                }
    
                return;
            }
        }
    
        public void animateHint(string scrollParameterUri)
        {
            if (FactRecorder.AllFacts.ContainsKey(scrollParameterUri))
                AnimateExistingFactEvent.Invoke(
                        scrollParameterUri,
                        FactWrapper.FactMaterials.Hint
                    );
    
            Fact hintFact = LatestRenderedHints.Find(x => x.Id == scrollParameterUri); // "Dictionary"
    
            ScrollAssignment suitableCompletion =
                LatestBackwartsCompletions.Find((ScrollAssignment x) => x.fact.uri == scrollParameterUri); // "Dictionary"
    
            if (suitableCompletion != null && suitableCompletion.assignment is OMS assignment)
            {
                if (FactRecorder.AllFacts.ContainsKey(assignment.uri))
                {
                    AnimateExistingFactEvent.Invoke(
                        assignment.uri,
                        FactWrapper.FactMaterials.Hint
                    );
                }
            }
            else if (hintFact != null)
            {
                if (FactRecorder.FindEquivalent(StageStatic.stage.factState.MyFactSpace, hintFact, out string found_key, out Fact _, out bool _, false))
                    // existing fact -> Animate that
                    AnimateExistingFactEvent.Invoke(
                        found_key,
                        FactWrapper.FactMaterials.Hint
                    );
    
                else
                {   // Generate new FactRepresentation and animate it
                    AnimateNonExistingFactEvent.Invoke(hintFact);
                    AnimateExistingFactEvent.Invoke(
                        scrollParameterUri,
                        FactWrapper.FactMaterials.Hint
                    );
                }
            }
        }
    }