using Newtonsoft.Json; using System.Collections.Generic; using TMPro; using UnityEngine; using static SOMDocManager; /// <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; /// @} [JsonIgnore] public PointFact Point1 { get => (PointFact)FactOrganizer.AllFacts[Pid1]; } [JsonIgnore] public PointFact Point2 { get => (PointFact)FactOrganizer.AllFacts[Pid2]; } /// <summary> Distance between <see cref="AbstractLineFact.Pid1"/> and <see cref="AbstractLineFact.Pid2"/></summary> [JsonIgnore] public float Distance; /// <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> /// 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) { this._URI = backendURI; set_public_members(pid1, pid2); } /// <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; Vector3 diff = (Point1.Point - Point2.Point); this.Dir = diff.normalized; this.Distance = diff.magnitude; } protected override void RecalculateTransform() { Position = Vector3.Lerp(Point1.Point, Point2.Point, 0.5f); Rotation = Quaternion.LookRotation(Dir, Vector3.up); LocalScale = new Vector3(1, 1, Distance); } /// \copydoc Fact.hasDependentFacts public override bool HasDependentFacts => true; /// \copydoc Fact.getDependentFactIds protected override string[] GetGetDependentFactIds() => new string[] { Pid1, Pid2 }; } /// <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(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> \copydoc Fact.Fact </summary> public LineFact() : base() { } /// <summary> \copydoc AbstractLineFact.AbstractLineFact(string, string, string, FactOrganizer) </summary> public LineFact(string pid1, string pid2, string backendURI, FactOrganizer organizer) : base(pid1, pid2, backendURI, organizer) => _ = this.Label; /// <summary> \copydoc AbstractLineFact.AbstractLineFact(string, string, FactOrganizer) </summary> public LineFact(string pid1, string pid2, FactOrganizer organizer) : base(pid1, pid2, organizer) { SendToMMT(); } public override MMTDeclaration MakeMMTDeclaration() { SOMDoc lhs = new OMA( new OMS(MMT_OMS_URI.Metric), new List<SOMDoc> { new OMS(Pid1), new OMS(Pid2) } ); SOMDoc valueTp = new OMS(MMT_OMS_URI.RealLit); SOMDoc value = new OMF(Distance); return new MMTValueDeclaration(this.Label, lhs, valueTp, value); } /// \copydoc Fact.parseFact(ScrollFact) public new static LineFact parseFact(MMTDeclaration fact) { string pointAUri = ((OMS)((OMA)((MMTValueDeclaration)fact).lhs).arguments[0]).uri; string pointBUri = ((OMS)((OMA)((MMTValueDeclaration)fact).lhs).arguments[1]).uri; if (!FactOrganizer.AllFacts.ContainsKey(pointAUri) || !FactOrganizer.AllFacts.ContainsKey(pointBUri)) return null; return new LineFact(pointAUri, pointBUri, fact.@ref.uri, StageStatic.stage.factState); } /// \copydoc Fact.generateLabel protected override string generateLabel() => "[" + Point1.Label + Point2.Label + "]"; /// \copydoc Fact.Equivalent(Fact, Fact) protected override bool EquivalentWrapped(LineFact f1, LineFact f2) => DependentFactsEquivalent(f1, f2); protected override Fact _ReInitializeMe(Dictionary<string, string> old_to_new, FactOrganizer organizer) => new LineFact(old_to_new[this.Pid1], old_to_new[this.Pid2], organizer); } /// <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(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) { SendToMMT(); } public override MMTDeclaration MakeMMTDeclaration() { SOMDoc type = new OMS(MMT_OMS_URI.LineType); SOMDoc defines = new OMA( new OMS(MMT_OMS_URI.LineOf), new List<SOMDoc> { new OMS(Pid1), new OMS(Pid2) }); return new MMTSymbolDeclaration(this.Label, type, defines); } /// \copydoc Fact.parseFact(ScrollFact) public new static RayFact parseFact(MMTDeclaration fact) { if (((MMTSymbolDeclaration)fact).defines is not OMA defines) return null; string pointAUri = ((OMS)defines.arguments[0]).uri; string pointBUri = ((OMS)defines.arguments[1]).uri; if (!FactOrganizer.AllFacts.ContainsKey(pointAUri) || !FactOrganizer.AllFacts.ContainsKey(pointBUri)) return null; return new RayFact(pointAUri, pointBUri, fact.@ref.uri, StageStatic.stage.factState); } protected override void RecalculateTransform() { base.RecalculateTransform(); LocalScale = new Vector3(1, 1, 2048); } /// \copydoc Fact.generateLabel protected override string generateLabel() { // TODO this string is too large to properly depict on scrolls. // return "]" + Point1.Label + Point2.Label + "["; return Point1.Label + Point2.Label; } /// \copydoc Fact.Equivalent(Fact, Fact) protected override bool EquivalentWrapped(RayFact f1, RayFact f2) { if (!Math3d.IsApproximatelyParallel(f1.Dir, f2.Dir)) return false; return Math3d.IsPointApproximatelyOnLine(f1.Point1.Point, f1.Dir, f2.Point1.Point) && Math3d.IsPointApproximatelyOnLine(f1.Point1.Point, f1.Dir, f2.Point2.Point); } protected override Fact _ReInitializeMe(Dictionary<string, string> old_to_new, FactOrganizer organizer) => new RayFact(old_to_new[this.Pid1], old_to_new[this.Pid2], organizer); }