public void SetMathList(IMathList mathList) { _mathList = mathList; Latex = MathListBuilder.MathListToString(mathList); InvalidateIntrinsicContentSize(); SetNeedsLayout(); }
static void InsertAtAtomIndexAndAdvance(this IMathList self, int atomIndex, IMathAtom atom, ref MathListIndex advance, MathListSubIndexType advanceType) { if (atomIndex < 0 || atomIndex > self.Count) { throw new IndexOutOfRangeException($"Index {atomIndex} is out of bounds for list of size {self.Atoms.Count}"); } // Test for placeholder to the right of index, e.g. \sqrt{‸■} -> \sqrt{2‸} if (atomIndex < self.Count && self[atomIndex] is MathAtom placeholder && placeholder?.AtomType is Enumerations.MathAtomType.Placeholder) { if (placeholder.Superscript is IMathList super) { if (atom.Superscript != null) { super.Append(atom.Superscript); } atom.Superscript = super; } if (placeholder.Subscript is IMathList sub) { if (atom.Subscript != null) { sub.Append(atom.Subscript); } atom.Subscript = sub; } self[atomIndex] = atom; }
public void TestRadical() { string input = @"\sqrt[3]2"; var list = MathLists.FromString(input); Assert.Single(list); var radical = list[0] as Radical; CheckAtomTypeAndNucleus(radical, MathAtomType.Radical, ""); IMathList subList = radical.Radicand; Assert.Single(subList); var atom = subList[0]; CheckAtomTypeAndNucleus(atom, MathAtomType.Number, "2"); var degree = radical.Degree; Assert.Single(degree); CheckAtomTypeAndNucleus(degree[0], MathAtomType.Number, "3"); var latex = MathListBuilder.MathListToString(list); Assert.Equal(@"\sqrt[3]{2}", latex); }
public MathList(IMathList cloneMe, bool finalize) : this() { if (!finalize) { foreach (var atom in cloneMe.Atoms) { var cloneAtom = AtomCloner.Clone(atom, finalize); Add(cloneAtom); } } else { IMathAtom prevNode = null; foreach (var atom in cloneMe.Atoms) { var newNode = AtomCloner.Clone(atom, finalize); if (atom.IndexRange == Range.Zero) { int prevIndex = prevNode is null ? 0 : prevNode.IndexRange.Location + prevNode.IndexRange.Length; newNode.IndexRange = new Range(prevIndex, 1); } switch (newNode.AtomType) { case MathAtomType.BinaryOperator: switch (prevNode?.AtomType) { case null: case MathAtomType.BinaryOperator: case MathAtomType.Relation: case MathAtomType.Open: case MathAtomType.Punctuation: case MathAtomType.LargeOperator: newNode.AtomType = MathAtomType.UnaryOperator; break; } break; case MathAtomType.Relation: case MathAtomType.Punctuation: case MathAtomType.Close: if (prevNode != null && prevNode.AtomType == MathAtomType.BinaryOperator) { prevNode.AtomType = MathAtomType.UnaryOperator; } break; case MathAtomType.Number: if (prevNode != null && prevNode.AtomType == MathAtomType.Number && prevNode.Subscript == null && prevNode.Superscript == null) { prevNode.Fuse(newNode); continue; // do not add the new node; we fused it instead. } break; } Add(newNode); prevNode = newNode; } } }
/// <summary>Deep copy</summary> public static IMathList Clone(IMathList target, bool finalize) { if (target == null) { return(null); } return(new MathList((MathList)target, finalize)); }
public static IFraction Fraction(IMathList numerator, IMathList denominator) { var fraction = new Fraction { Numerator = numerator, Denominator = denominator }; return(fraction); }
internal IMathAtom BuildTable(string environment, IMathList firstList, bool isRow, char stopChar) { var oldEnv = _currentEnvironment; _currentEnvironment = new TableEnvironmentProperties(environment); int currentRow = 0; int currentColumn = 0; var rows = new List <List <IMathList> > { new List <IMathList>() }; if (firstList != null) { rows[currentRow].Add(firstList); if (isRow) { _currentEnvironment.NRows++; currentRow++; rows.Add(new List <IMathList>()); } else { currentColumn++; } } while (HasCharacters && !(_currentEnvironment.Ended)) { var list = BuildInternal(false, stopChar); if (list == null) { return(null); } rows[currentRow].Add(list); currentColumn++; if (_currentEnvironment.NRows > currentRow) { currentRow = _currentEnvironment.NRows; rows.Add(new List <IMathList>()); currentColumn = 0; } } if (_currentEnvironment.Name != null && !_currentEnvironment.Ended) { SetError(@"Missing \end"); return(null); } var tableResult = MathAtoms.Table(_currentEnvironment.Name, rows); if (tableResult.Error is string error) { SetError(error); return(null); } _currentEnvironment = oldEnv; return(tableResult.Value); }
/// <summary>Safe to call with a null list. Types cannot be null however.</summary> private void CheckAtomTypes(IMathList list, params MathAtomType[] types) { int atomCount = (list == null) ? 0 : list.Atoms.Count; Assert.Equal(types.Count(), atomCount); for (int i = 0; i < atomCount; i++) { var atom = list[i]; Assert.NotNull(atom); Assert.Equal(atom.AtomType, types[i]); } }
///<summary>Iteratively expands all groups in the list.</summary> private void ExpandGroups(IMathList list) { for (int i = 0; i < list.Count; i++) { if (list[i].AtomType == MathAtomType.Group) { var group = (Group)list[i]; for (int j = group.InnerList.Count - 1; j >= 0; j--) { list.Insert(i + 1, group.InnerList[j]); } list.RemoveAt(i); } } }
public static void CheckListContents(IMathList list) { Assert.Equal(10, list.Atoms.Count); var atom0 = list.Atoms[0]; Assert.Equal(MathAtomType.UnaryOperator, atom0.AtomType); Assert.Equal("\u2212", atom0.Nucleus); Assert.Equal(new Range(0, 1), atom0.IndexRange); var atom1 = list.Atoms[1]; Assert.Equal(MathAtomType.Number, atom1.AtomType); Assert.Equal("52", atom1.Nucleus); Assert.Equal(new Range(1, 2), atom1.IndexRange); var atom2 = list.Atoms[2]; Assert.Equal(MathAtomType.Variable, atom2.AtomType); Assert.Equal("x", atom2.Nucleus); Assert.Equal(new Range(3, 1), atom2.IndexRange); var superScript = atom2.Superscript; Assert.Equal(3, superScript.Atoms.Count); var super0 = superScript.Atoms[0]; Assert.Equal(MathAtomType.Number, super0.AtomType); Assert.Equal("13", super0.Nucleus); Assert.Equal(new Range(0, 2), super0.IndexRange); var super1 = superScript.Atoms[1]; Assert.Equal(MathAtomType.BinaryOperator, super1.AtomType); Assert.Equal("+", super1.Nucleus); Assert.Equal(new Range(2, 1), super1.IndexRange); var super2 = superScript.Atoms[2]; Assert.Equal(MathAtomType.Variable, super2.AtomType); Assert.Equal("y", super2.Nucleus); Assert.Equal(new Range(3, 1), super2.IndexRange); }
public static IMathAtom AtomAt(this IMathList self, MathListIndex index) { if (index is null || index.AtomIndex >= self.Atoms.Count) { return(null); } var atom = self.Atoms[index.AtomIndex]; switch (index.SubIndexType) { case MathListSubIndexType.None: case MathListSubIndexType.Nucleus: return(atom); case MathListSubIndexType.Subscript: return(atom.Subscript.AtomAt(index.SubIndex)); case MathListSubIndexType.Superscript: return(atom.Superscript.AtomAt(index.SubIndex)); case MathListSubIndexType.Radicand: case MathListSubIndexType.Degree: if (atom is Radical radical && radical.AtomType == Enumerations.MathAtomType.Radical) { if (index.SubIndexType == MathListSubIndexType.Degree) { return(radical.Degree.AtomAt(index.SubIndex)); } else { return(radical.Radicand.AtomAt(index.SubIndex)); } } else { return(null); }
public static MathListDisplay<TFont, TGlyph> CreateLine<TFont, TGlyph>(this TypesettingContext<TFont, TGlyph> context, IMathList list, TFont font, LineStyle style) where TFont: MathFont<TGlyph> { return Typesetter<TFont, TGlyph>.CreateLine(list, font, context, style); }
public Underline(Underline cloneMe, bool finalize) : base(cloneMe, finalize) { this.InnerList = AtomCloner.Clone(cloneMe.InnerList, finalize); }
public static void RemoveAtoms(this IMathList self, MathListRange?nullableRange) { if (!nullableRange.HasValue) { return; } var range = nullableRange.GetValueOrDefault(); var start = range.Start; switch (start.SubIndexType) { case MathListSubIndexType.None: self.RemoveAtoms(new Range(start.AtomIndex, range.Length)); break; case MathListSubIndexType.Nucleus: throw new NotSupportedException("Nuclear fission is not supported"); case MathListSubIndexType.Radicand: case MathListSubIndexType.Degree: if (!(self.Atoms[start.AtomIndex] is Radical radical && radical.AtomType == Enumerations.MathAtomType.Radical)) { throw new SubIndexTypeMismatchException($"No radical found at index {start.AtomIndex}"); } if (start.SubIndexType == MathListSubIndexType.Degree) { radical.Degree.RemoveAtoms(range.SubIndexRange); } else { radical.Radicand.RemoveAtoms(range.SubIndexRange); } break; case MathListSubIndexType.Numerator: case MathListSubIndexType.Denominator: if (!(self.Atoms[start.AtomIndex] is Fraction frac && frac.AtomType == Enumerations.MathAtomType.Fraction)) { throw new SubIndexTypeMismatchException($"No fraction found at index {start.AtomIndex}"); } if (start.SubIndexType == MathListSubIndexType.Numerator) { frac.Numerator.RemoveAtoms(range.SubIndexRange); } else { frac.Denominator.RemoveAtoms(range.SubIndexRange); } break; case MathListSubIndexType.Subscript: var current = self.Atoms[start.AtomIndex]; if (current.Subscript == null) { throw new SubIndexTypeMismatchException($"No subscript for atom at index {start.AtomIndex}"); } current.Subscript.RemoveAtoms(range.SubIndexRange); break; case MathListSubIndexType.Superscript: current = self.Atoms[start.AtomIndex]; if (current.Superscript == null) { throw new SubIndexTypeMismatchException($"No superscript for atom at index {start.AtomIndex}"); } current.Superscript.RemoveAtoms(range.SubIndexRange); break; } }
internal static void CheckClone(IMathList original, IMathList clone) { Assert.Equal(original, clone); Assert.False(ReferenceEquals(original, clone)); }
public Accent(Accent cloneMe, bool finalize) : base(cloneMe, finalize) { InnerList = AtomCloner.Clone(cloneMe.InnerList, finalize); }
public MathSource(IMathList mathList) { LaTeX = MathListBuilder.MathListToString(mathList); MathList = mathList; ErrorMessage = null; }
public static void Insert(this IMathList self, MathListIndex index, IMathAtom atom) { index = index ?? MathListIndex.Level0Index(0); if (index.AtomIndex > self.Atoms.Count) { throw new IndexOutOfRangeException($"Index {index.AtomIndex} is out of bounds for list of size {self.Atoms.Count}"); } switch (index.SubIndexType) { case MathListSubIndexType.None: self.Insert(index.AtomIndex, atom); break; case MathListSubIndexType.Nucleus: var currentAtom = self.Atoms[index.AtomIndex]; if (currentAtom.Subscript == null && currentAtom.Superscript == null) { throw new SubIndexTypeMismatchException("Nuclear fusion is not supported if there are neither subscripts nor superscripts in the current atom."); } if (atom.Subscript != null || atom.Superscript != null) { throw new ArgumentException("Cannot fuse with an atom that already has a subscript or a superscript"); } atom.Subscript = currentAtom.Subscript; atom.Superscript = currentAtom.Superscript; currentAtom.Subscript = null; currentAtom.Superscript = null; self.Insert(index.AtomIndex + index.SubIndex?.AtomIndex ?? 0, atom); break; case MathListSubIndexType.Degree: case MathListSubIndexType.Radicand: if (!(self.Atoms[index.AtomIndex] is Radical radical && radical.AtomType == Enumerations.MathAtomType.Radical)) { throw new SubIndexTypeMismatchException($"No radical found at index {index.AtomIndex}"); } if (index.SubIndexType == MathListSubIndexType.Degree) { radical.Degree.Insert(index.SubIndex, atom); } else { radical.Radicand.Insert(index.SubIndex, atom); } break; case MathListSubIndexType.Numerator: case MathListSubIndexType.Denominator: if (!(self.Atoms[index.AtomIndex] is Fraction frac && frac.AtomType == Enumerations.MathAtomType.Fraction)) { throw new SubIndexTypeMismatchException($"No fraction found at index {index.AtomIndex}"); } if (index.SubIndexType == MathListSubIndexType.Numerator) { frac.Numerator.Insert(index.SubIndex, atom); } else { frac.Denominator.Insert(index.SubIndex, atom); } break; case MathListSubIndexType.Subscript: var current = self.Atoms[index.AtomIndex]; if (current.Subscript == null) { throw new SubIndexTypeMismatchException($"No subscript for atom at index {index.AtomIndex}"); } current.Subscript.Insert(index.SubIndex, atom); break; case MathListSubIndexType.Superscript: current = self.Atoms[index.AtomIndex]; if (current.Superscript == null) { throw new SubIndexTypeMismatchException($"No superscript for atom at index {index.AtomIndex}"); } current.Superscript.Insert(index.SubIndex, atom); break; } }
public static void RemoveAt(this IMathList self, MathListIndex index) { index = index ?? MathListIndex.Level0Index(0); if (index.AtomIndex > self.Atoms.Count) { throw new IndexOutOfRangeException($"Index {index.AtomIndex} is out of bounds for list of size {self.Atoms.Count}"); } switch (index.SubIndexType) { case MathListSubIndexType.None: self.RemoveAt(index.AtomIndex); break; case MathListSubIndexType.Nucleus: var currentAtom = self.Atoms[index.AtomIndex]; if (currentAtom.Subscript == null && currentAtom.Superscript == null) { throw new SubIndexTypeMismatchException("Nuclear fission is not supported if there are no subscripts or superscripts."); } if (index.AtomIndex > 0) { var previous = self.Atoms[index.AtomIndex - 1]; if (previous.Subscript != null && previous.Superscript != null) { previous.Superscript = currentAtom.Superscript; previous.Subscript = currentAtom.Subscript; self.RemoveAt(index.AtomIndex); break; } } // no previous atom or the previous atom sucks (has sub/super scripts) currentAtom.Nucleus = ""; break; case MathListSubIndexType.Radicand: case MathListSubIndexType.Degree: if (!(self.Atoms[index.AtomIndex] is Radical radical && radical.AtomType == Enumerations.MathAtomType.Radical)) { throw new SubIndexTypeMismatchException($"No radical found at index {index.AtomIndex}"); } if (index.SubIndexType == MathListSubIndexType.Degree) { radical.Degree.RemoveAt(index.SubIndex); } else { radical.Radicand.RemoveAt(index.SubIndex); } break; case MathListSubIndexType.Numerator: case MathListSubIndexType.Denominator: if (!(self.Atoms[index.AtomIndex] is Fraction frac && frac.AtomType == Enumerations.MathAtomType.Fraction)) { throw new SubIndexTypeMismatchException($"No fraction found at index {index.AtomIndex}"); } if (index.SubIndexType == MathListSubIndexType.Numerator) { frac.Numerator.RemoveAt(index.SubIndex); } else { frac.Denominator.RemoveAt(index.SubIndex); } break; case MathListSubIndexType.Subscript: var current = self.Atoms[index.AtomIndex]; if (current.Subscript == null) { throw new SubIndexTypeMismatchException($"No subscript for atom at index {index.AtomIndex}"); } current.Subscript.RemoveAt(index.SubIndex); break; case MathListSubIndexType.Superscript: current = self.Atoms[index.AtomIndex]; if (current.Superscript == null) { throw new SubIndexTypeMismatchException($"No superscript for atom at index {index.AtomIndex}"); } current.Superscript.RemoveAt(index.SubIndex); break; } }
public Group(Group cloneMe, bool finalize) : base(cloneMe, finalize) { InnerList = AtomCloner.Clone(cloneMe.InnerList, finalize); }
public Radical(Radical cloneMe, bool finalize) : base(cloneMe, finalize) { Radicand = AtomCloner.Clone(cloneMe.Radicand, finalize); Degree = AtomCloner.Clone(cloneMe.Degree, finalize); }
internal static void CheckClone(IMathList original, IMathList clone) => MathListTest.CheckClone(original, clone);
public static string MathListToString(IMathList mathList) { var builder = new StringBuilder(); var currentFontStyle = FontStyle.Default; foreach (var atom in mathList) { if (currentFontStyle != atom.FontStyle) { if (currentFontStyle != FontStyle.Default) { // close the previous font style builder.Append("}"); } if (atom.FontStyle != FontStyle.Default) { // open a new font style var fontStyleName = atom.FontStyle.FontName(); builder.Append(@"\" + fontStyleName + "{"); } } currentFontStyle = atom.FontStyle; switch (atom.AtomType) { case MathAtomType.Fraction: { var fraction = (IFraction)atom; var numerator = MathListToString(fraction.Numerator); var denominator = MathListToString(fraction.Denominator); if (fraction.HasRule) { builder.Append(@"\frac{" + numerator + "}{" + denominator + "}"); } else { string command = null; if (fraction.LeftDelimiter == null && fraction.RightDelimiter == null) { command = "atop"; } else if (fraction.LeftDelimiter == "(" && fraction.RightDelimiter == ")") { command = "choose"; } else if (fraction.LeftDelimiter == "{" && fraction.RightDelimiter == "}") { command = "brace"; } else if (fraction.LeftDelimiter == "[" && fraction.RightDelimiter == "]") { command = "brack"; } else { command = $"atopwithdelims{fraction.LeftDelimiter}{fraction.RightDelimiter}"; } builder.Append("{" + numerator + @" \" + command + " " + denominator + "}"); } } break; case MathAtomType.Radical: { builder.Append(@"\sqrt"); var radical = (IRadical)atom; if (radical.Degree != null) { builder.Append($"[{MathListToString(radical.Degree)}]"); } builder.Append("{" + MathListToString(radical.Radicand) + "}"); break; } case MathAtomType.Inner: { var inner = (IMathInner)atom; if (inner.LeftBoundary == null && inner.RightBoundary == null) { builder.Append("{" + MathListToString(inner.InnerList) + "}"); } else { if (inner.LeftBoundary == null) { builder.Append(@"\left. "); } else { builder.Append(@"\left" + DelimiterToString(inner.LeftBoundary) + " "); } builder.Append(MathListToString(inner.InnerList)); if (inner.RightBoundary == null) { builder.Append(@"\right. "); } else { builder.Append(@"\right" + DelimiterToString(inner.RightBoundary) + " "); } } break; } case MathAtomType.Table: { var table = (IMathTable)atom; if (table.Environment != null) { builder.Append(@"\begin{" + table.Environment + "}"); } for (int i = 0; i < table.NRows; i++) { var row = table.Cells[i]; for (int j = 0; j < row.Count; j++) { var cell = row[j]; if (table.Environment == "matrix") { if (cell.Count >= 1 && cell[0].AtomType == MathAtomType.Style) { // remove the first atom. var atoms = cell.Atoms.GetRange(1, cell.Count - 1); cell = MathLists.WithAtoms(atoms.ToArray()); } } if (table.Environment == "eqalign" || table.Environment == "aligned" || table.Environment == "split") { if (j == 1 && cell.Count >= 1 && cell[0].AtomType == MathAtomType.Ordinary && string.IsNullOrEmpty(cell[0].Nucleus)) { // empty nucleus added for spacing. Remove it. var atoms = cell.Atoms.GetRange(1, cell.Count - 1); cell = MathLists.WithAtoms(atoms.ToArray()); } } builder.Append(MathListToString(cell)); if (j < row.Count - 1) { builder.Append("&"); } } if (i < table.NRows - 1) { builder.Append(@"\\ "); } } if (table.Environment != null) { builder.Append(@"\end{" + table.Environment + "}"); } break; } case MathAtomType.Overline: { var over = (IOverline)atom; builder.Append(@"\overline{" + MathListToString(over.InnerList) + "}"); break; } case MathAtomType.Underline: { var under = (IUnderline)atom; builder.Append(@"\underline{" + MathListToString(under.InnerList) + "}"); break; } case MathAtomType.Accent: { var accent = (IAccent)atom; var list = accent.InnerList; accent.InnerList = null; //for lookup builder.Append(@"\" + MathAtoms.Commands[(MathAtom)atom] + "{" + MathListToString(list) + "}"); break; } case MathAtomType.LargeOperator: { var op = (LargeOperator)atom; var command = MathAtoms.LatexSymbolNameForAtom(op); var originalOperator = (LargeOperator)MathAtoms.ForLatexSymbolName(command); builder.Append($@"\{command} "); if (originalOperator.Limits != op.Limits) { switch (op.Limits) { case true: builder.Append(@"\limits "); break; case false: if (!op.NoLimits) { builder.Append(@"\nolimits "); } break; case null: break; } } break; } case MathAtomType.Space: { var space = (ISpace)atom; var intSpace = (int)space.Length; if (SpaceToCommands.ContainsKey(intSpace) && intSpace == space.Length) { var command = SpaceToCommands[intSpace]; builder.Append(@"\" + command + " "); } else { if (space.IsMu) { builder.Append(@"\mkern" + space.Length.ToString("0.0") + "mu"); } else { builder.Append(@"\kern" + space.Length.ToString("0.0") + "pt"); } } break; } case MathAtomType.Style: { var style = (IStyle)atom; var command = StyleToCommands[style.LineStyle]; builder.Append(@"\" + command + " "); break; } case MathAtomType.Color: { var color = (IColor)atom; builder.Append($@"\color{{{color.ColorString}}}{{{MathListToString(color.InnerList)}}}"); break; } case MathAtomType.Group: builder.AppendInBraces(MathListToString(((Group)atom).InnerList), NullHandling.EmptyContent); break; case MathAtomType.Prime: builder.Append('\'', ((Prime)atom).Length); break; default: { var aNucleus = atom.Nucleus; if (String.IsNullOrEmpty(aNucleus)) { builder.Append(@"{}"); } else if (aNucleus == "\u2236") { builder.Append(":"); } else if (aNucleus == "\u2212") { builder.Append("-"); } else { var command = MathAtoms.LatexSymbolNameForAtom((MathAtom)atom); if (command == null) { if (atom is Extension.I_ExtensionAtom ext) { builder.Append(Extension._MathListDestructor.MathAtomToString(ext)); } else { builder.Append(aNucleus); } } else { builder.Append(@"\" + command + " "); } } break; } } if (atom.Subscript != null) { var scriptString = MathListToString(atom.Subscript); builder.Append(scriptString.Length == 1 ? $"_{scriptString}" : $"_{{{scriptString}}}"); } if (atom.Superscript != null) { var scriptString = MathListToString(atom.Superscript); builder.Append(scriptString.Length == 1 ? $"^{scriptString}" : $"^{{{scriptString}}}"); } } if (currentFontStyle != FontStyle.Default) { builder.Append("}"); } return(builder.ToString()); }
/*public RaiseBox(RaiseBox cloneMe, bool finalize) : base(cloneMe, finalize) => * InnerList = cloneMe.InnerList;*/ protected override void CopyPropertiesFrom(RaiseBox oldAtom) { InnerList = oldAtom.InnerList; Raise = oldAtom.Raise; }