using Newtonsoft.Json; using System.Collections.Generic; using TMPro; using UnityEngine; using static JSONManager; /// <summary> /// Base-class for 1D-Facts /// </summary> public abstract class AbstractLineFact : FactWrappedCRTP<AbstractLineFact> { /// @{ <summary> /// One <see cref="Fact.Id">Id</see> of two <see cref="PointFact"/> defining <see cref="Dir"/>. /// </summary> public string Pid1, Pid2; /// @} /// <summary> /// Normalized Direction from <see cref="Pid1"/> to <see cref="Pid2"/>. /// </summary> public Vector3 Dir; /// <summary> /// \copydoc Fact.Fact() /// </summary> protected AbstractLineFact() : base() { Pid1 = null; Pid2 = null; Dir = Vector3.zero; } /// <summary> /// Copies <paramref name="fact"/> by initiating new MMT %Fact. /// </summary> /// <param name="fact">Fact to be copied</param> /// <param name="old_to_new"><c>Dictionary</c> mapping <paramref name="fact"/>.<see cref="getDependentFactIds"/> in <paramref name="fact"/>.<see cref="Fact._Facts"/> to corresponding <see cref="Fact.Id"/> in <paramref name="organizer"/> </param> /// <param name="organizer">sets <see cref="_Facts"/></param> protected AbstractLineFact(AbstractLineFact fact, Dictionary<string, string> old_to_new, FactOrganizer organizer) : base(fact, organizer) { set_public_members(old_to_new[fact.Pid1], old_to_new[fact.Pid2]); } /// <summary> /// Standard Constructor /// </summary> /// <param name="pid1">sets <see cref="AbstractLineFact.Pid1"/></param> /// <param name="pid2">sets <see cref="AbstractLineFact.Pid2"/></param> /// <param name="organizer">sets <see cref="Fact._Facts"/></param> protected AbstractLineFact(string pid1, string pid2, FactOrganizer organizer) : base(organizer) { set_public_members(pid1, pid2); } /// <summary> /// Bypasses initialization of new MMT %Fact by using existend URI, _which is not checked for existence_. /// </summary> /// <param name="pid1">sets <see cref="Pid1"/></param> /// <param name="pid2">sets <see cref="Pid2"/></param> /// <param name="backendURI">MMT URI</param> /// <param name="organizer">sets <see cref="Fact._Facts"/></param> protected AbstractLineFact(string pid1, string pid2, string backendURI, FactOrganizer organizer) : base(organizer) { set_public_members(pid1, pid2); this._URI = backendURI; } /// <summary> /// Initiates <see cref="Pid1"/>, <see cref="Pid2"/>, <see cref="Dir"/> /// </summary> /// <param name="pid1">sets <see cref="Pid1"/></param> /// <param name="pid2">sets <see cref="Pid2"/></param> private void set_public_members(string pid1, string pid2) { this.Pid1 = pid1; this.Pid2 = pid2; PointFact pf1 = _Facts[pid1] as PointFact; PointFact pf2 = _Facts[pid2] as PointFact; this.Dir = (pf2.Point - pf1.Point).normalized; } /// \copydoc Fact.hasDependentFacts public override bool hasDependentFacts() { return true; } /// \copydoc Fact.getDependentFactIds public override string[] getDependentFactIds() { return new string[] { Pid1, Pid2 }; } /// \copydoc Fact.GetHashCode public override int GetHashCode() { return this.Pid1.GetHashCode() ^ this.Pid2.GetHashCode(); } } /// <summary> /// Implements CRTP for <see cref="AbstractLineFact"/>; Escalates constructors; /// </summary> /// <typeparam name="T">class, which inherits from AbstractLineFactWrappedCRTP</typeparam> public abstract class AbstractLineFactWrappedCRTP<T> : AbstractLineFact where T : AbstractLineFactWrappedCRTP<T> { /// <summary>\copydoc Fact.Fact</summary> protected AbstractLineFactWrappedCRTP() : base() { } /// <summary>\copydoc AbstractLineFact.AbstractLineFact(AbstractLineFact, Dictionary{string, string}, FactOrganizer)</summary> protected AbstractLineFactWrappedCRTP(AbstractLineFactWrappedCRTP<T> fact, Dictionary<string, string> old_to_new, FactOrganizer organizer) : base(fact, old_to_new, organizer) { } /// <summary>\copydoc AbstractLineFact.AbstractLineFact(string, string, FactOrganizer)</summary> protected AbstractLineFactWrappedCRTP(string pid1, string pid2, FactOrganizer organizer) : base(pid1, pid2, organizer) { } /// <summary>\copydoc AbstractLineFact.AbstractLineFact(string, string, string, FactOrganizer)</summary> protected AbstractLineFactWrappedCRTP(string pid1, string pid2, string backendURI, FactOrganizer organizer) : base(pid1, pid2, backendURI, organizer) { } /// \copydoc Fact.Equivalent(Fact, Fact) protected override bool EquivalentWrapped(AbstractLineFact f1, AbstractLineFact f2) => EquivalentWrapped((T)f1, (T)f2); /// <summary>CRTP step of <see cref="EquivalentWrapped(AbstractLineFact, AbstractLineFact)"/></summary> protected abstract bool EquivalentWrapped(T f1, T f2); } /// <summary> /// Line within 3D Space of finite length /// </summary> public class LineFact : AbstractLineFactWrappedCRTP<LineFact> { /// \copydoc Fact.s_type [JsonProperty] protected static new string s_type = "LineFact"; /// <summary> Distance between <see cref="AbstractLineFact.Pid1"/> and <see cref="AbstractLineFact.Pid2"/></summary> public float Distance; /// <summary> \copydoc Fact.Fact </summary> public LineFact() : base() { Distance = 0; } /// <summary> \copydoc AbstractLineFact.AbstractLineFact(AbstractLineFact, Dictionary<string, string>, FactOrganizer) </summary> public LineFact(LineFact fact, Dictionary<string, string> old_to_new, FactOrganizer organizer) : base(fact, old_to_new, organizer) => init(old_to_new[fact.Pid1], old_to_new[fact.Pid2]); /// <summary> \copydoc AbstractLineFact.AbstractLineFact(string, string, string, FactOrganizer) </summary> public LineFact(string pid1, string pid2, string backendURI, FactOrganizer organizer) : base(pid1, pid2, backendURI, organizer) { SetDistance(); _ = this.Label; } /// <summary> \copydoc AbstractLineFact.AbstractLineFact(string, string, FactOrganizer) </summary> public LineFact(string pid1, string pid2, FactOrganizer organizer) : base(pid1, pid2, organizer) => init(pid1, pid2); /// <summary> /// Initiates <see cref="AbstractLineFact.Pid1"/>, <see cref="AbstractLineFact.Pid2"/>, <see cref="Fact._URI"/> and creates MMT %Fact Server-Side /// </summary> /// <param name="pid1">sets <see cref="AbstractLineFact.Pid1"/></param> /// <param name="pid2">sets <see cref="AbstractLineFact.Pid2"/></param> private void init(string pid1, string pid2) { SetDistance(); PointFact pf1 = _Facts[pid1] as PointFact; PointFact pf2 = _Facts[pid2] as PointFact; float v = (pf1.Point - pf2.Point).magnitude; MMTTerm lhs = new OMA( new OMS(MMTURIs.Metric), new List<MMTTerm> { new OMS(pid1), new OMS(pid2) } ); MMTTerm valueTp = new OMS(MMTURIs.RealLit); MMTTerm value = new OMF(v); MMTValueDeclaration mmtDecl = new(this.Label, lhs, valueTp, value); AddFactResponse.sendAdd(mmtDecl, out this._URI); } /// \copydoc Fact.parseFact(Scroll.ScrollFact) public new static LineFact parseFact(Scroll.ScrollFact fact) { string uri = fact.@ref.uri; string pointAUri = ((OMS)((OMA)((Scroll.ScrollValueFact)fact).lhs).arguments[0]).uri; string pointBUri = ((OMS)((OMA)((Scroll.ScrollValueFact)fact).lhs).arguments[1]).uri; if (StageStatic.stage.factState.ContainsKey(pointAUri) && StageStatic.stage.factState.ContainsKey(pointBUri)) return new LineFact(pointAUri, pointBUri, uri, StageStatic.stage.factState); //If dependent facts do not exist return null else return null; } /// \copydoc Fact.generateLabel protected override string generateLabel() => "[" + _Facts[Pid1].Label + _Facts[Pid2].Label + "]"; /// \copydoc Fact.instantiateDisplay(GameObject, Transform) public override GameObject instantiateDisplay(GameObject prefab, Transform transform) { var obj = GameObject.Instantiate(prefab, Vector3.zero, Quaternion.identity, transform); obj.transform.GetChild(0).gameObject.GetComponent<TextMeshProUGUI>().text = _Facts[this.Pid1].Label; obj.transform.GetChild(1).gameObject.GetComponent<TextMeshProUGUI>().text = _Facts[this.Pid2].Label; obj.GetComponent<FactWrapper>().fact = this; return obj; } /// \copydoc Fact.Equivalent(Fact, Fact) protected override bool EquivalentWrapped(LineFact f1, LineFact f2) { if ((f1.Pid1 == f2.Pid1 && f1.Pid2 == f2.Pid2))// || //(f1.Pid1 == f2.Pid2 && f1.Pid2 == f2.Pid1)) return true; PointFact p1f1 = (PointFact)_Facts[f1.Pid1]; PointFact p2f1 = (PointFact)_Facts[f1.Pid2]; PointFact p1f2 = (PointFact)_Facts[f2.Pid1]; PointFact p2f2 = (PointFact)_Facts[f2.Pid2]; return (p1f1.Equivalent(p1f2) && p2f1.Equivalent(p2f2)) ;//|| (p1f1.Equivalent(p2f2) && p2f1.Equivalent(p1f2)); } /// <summary> Calculates and sets <see cref="Distance"/>; <remarks> <see cref="AbstractLineFact.Pid1"/> and <see cref="AbstractLineFact.Pid2"/> needs to be set first.</remarks></summary> private void SetDistance() => this.Distance = Vector3.Distance(((PointFact)_Facts[Pid1]).Point, ((PointFact)_Facts[Pid2]).Point); } /// <summary> /// Ray within 3D Space of infinite length /// </summary> public class RayFact : AbstractLineFactWrappedCRTP<RayFact> { /// \copydoc Fact.s_type [JsonProperty] protected static new string s_type = "RayFact"; /// <summary> \copydoc Fact.Fact </summary> public RayFact() : base() { } /// <summary> \copydoc AbstractLineFact.AbstractLineFact(AbstractLineFact, Dictionary<string, string>, FactOrganizer) </summary> public RayFact(RayFact fact, Dictionary<string, string> old_to_new, FactOrganizer organizer) : base(fact, old_to_new, organizer) => init(old_to_new[fact.Pid1], old_to_new[fact.Pid2]); /// <summary> \copydoc AbstractLineFact.AbstractLineFact(string, string, string, FactOrganizer) </summary> public RayFact(string pid1, string pid2, string backendURI, FactOrganizer organizer) : base(pid1, pid2, backendURI, organizer) => _ = this.Label; /// <summary> \copydoc AbstractLineFact.AbstractLineFact(string, string, FactOrganizer) </summary> public RayFact(string pid1, string pid2, FactOrganizer organizer) : base(pid1, pid2, organizer) => init(pid1, pid2); /// <summary> /// Initiates <see cref="AbstractLineFact.Pid1"/>, <see cref="AbstractLineFact.Pid2"/>, <see cref="Fact._URI"/> and creates MMT %Fact Server-Side /// </summary> /// <param name="pid1">sets <see cref="AbstractLineFact.Pid1"/></param> /// <param name="pid2">sets <see cref="AbstractLineFact.Pid2"/></param> private void init(string pid1, string pid2) { MMTTerm tp = new OMS(MMTURIs.LineType); MMTTerm df = new OMA( new OMS(MMTURIs.LineOf), new List<MMTTerm> { new OMS(pid1), new OMS(pid2) }); AddFactResponse.sendAdd(new MMTSymbolDeclaration(this.Label, tp, df), out this._URI); ParsingDictionary.parseTermsToId[df.ToString()] = this._URI; } /// \copydoc Fact.parseFact(Scroll.ScrollFact) public new static RayFact parseFact(Scroll.ScrollFact fact) { string uri = fact.@ref.uri; if ((OMA)((Scroll.ScrollSymbolFact)fact).df == null) return null; string pointAUri = ((OMS)((OMA)((Scroll.ScrollSymbolFact)fact).df).arguments[0]).uri; string pointBUri = ((OMS)((OMA)((Scroll.ScrollSymbolFact)fact).df).arguments[1]).uri; if (StageStatic.stage.factState.ContainsKey(pointAUri) && StageStatic.stage.factState.ContainsKey(pointBUri)) return new RayFact(pointAUri, pointBUri, uri, StageStatic.stage.factState); //If dependent facts do not exist return null else return null; } /// \copydoc Fact.generateLabel protected override string generateLabel() { // TODO this string is too large to properly depict on scrolls. // return "]" + _Facts[Pid1].Label + _Facts[Pid2].Label + "["; return _Facts[Pid1].Label + _Facts[Pid2].Label; } /// \copydoc Fact.instantiateDisplay(GameObject, Transform) public override GameObject instantiateDisplay(GameObject prefab, Transform transform) { var obj = GameObject.Instantiate(prefab, Vector3.zero, Quaternion.identity, transform); obj.transform.GetChild(0).gameObject.GetComponent<TextMeshProUGUI>().text = this.Label; obj.GetComponent<FactWrapper>().fact = this; return obj; } /// \copydoc Fact.Equivalent(Fact, Fact) protected override bool EquivalentWrapped(RayFact f1, RayFact f2) { if (!Math3d.IsApproximatelyParallel(f1.Dir, f2.Dir)) return false; PointFact p1f1 = (PointFact)_Facts[f1.Pid1]; PointFact p1f2 = (PointFact)_Facts[f2.Pid1]; PointFact p2f2 = (PointFact)_Facts[f2.Pid2]; return Math3d.IsPointApproximatelyOnLine(p1f1.Point, f1.Dir, p1f2.Point) && Math3d.IsPointApproximatelyOnLine(p1f1.Point, f1.Dir, p2f2.Point); } }