Beispiel #1
0
        public static void RemoveAt(this MathList self, ref MathListIndex 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 var _ when index.SubIndex is null:
                throw new InvalidCodePathException("index.SubIndex is null despite non-None subindex type");

            case MathListSubIndexType.BetweenBaseAndScripts:
                var currentAtom = self.Atoms[index.AtomIndex];
                if (currentAtom.Subscript.IsEmpty() && currentAtom.Superscript.IsEmpty())
                {
                    throw new SubIndexTypeMismatchException(index);
                }
                var downIndex = index.LevelDown();
                if (downIndex is null)
                {
                    throw new InvalidCodePathException("downIndex is null");
                }
                if (index.AtomIndex > 0 &&
                    self.Atoms[index.AtomIndex - 1] is MathAtom previous &&
                    previous.Subscript.IsEmpty() &&
                    previous.Superscript.IsEmpty() &&
                    previous switch
                {
                    Atoms.BinaryOperator _ => false,
                    Atoms.UnaryOperator _ => false,
                    Atoms.Relation _ => false,
                    Atoms.Punctuation _ => false,
                    Atoms.Space _ => false,
                    _ => true
                })
                {
                    previous.Superscript.Append(currentAtom.Superscript);
                    previous.Subscript.Append(currentAtom.Subscript);
                    self.RemoveAt(index.AtomIndex);
                    // it was in the nucleus and we removed it, get out of the nucleus and get in the nucleus of the previous one.
                    index = downIndex.Previous is MathListIndex downPrev
              ? downPrev.LevelUpWithSubIndex(MathListSubIndexType.BetweenBaseAndScripts, MathListIndex.Level0Index(1))
              : downIndex;

                    break;
                }
                // insert placeholder since we couldn't place the scripts in previous atom
                var insertionAtom = LaTeXSettings.Placeholder;
                insertionAtom.Subscript.Append(currentAtom.Subscript);
                insertionAtom.Superscript.Append(currentAtom.Superscript);
                self.RemoveAt(index.AtomIndex);
                index = downIndex;
                self.InsertAndAdvance(ref index, insertionAtom, MathListSubIndexType.None);
                index = index.Previous ?? throw new InvalidCodePathException("Cannot go back after insertion?");
                return;
Beispiel #2
0
        public static MathListIndex IndexForPoint <TFont, TGlyph>(this FractionDisplay <TFont, TGlyph> self, TypesettingContext <TFont, TGlyph> context, PointF point) where TFont : IFont <TGlyph>
        {
            // We can be before or after the fraction
            if (point.X < self.Position.X - PixelDelta)
            {
                //We are before the fraction, so
                return(MathListIndex.Level0Index(self.Range.Location));
            }
            else if (point.X > self.Position.X + self.Width + PixelDelta)
            {
                //We are after the fraction
                return(MathListIndex.Level0Index(self.Range.End));
            }

            if (point.Y > self.LinePosition + PixelDelta)
            {
                return(MathListIndex.IndexAtLocation(self.Range.Location, MathListSubIndexType.Numerator, self.Numerator.IndexForPoint(context, point)));
            }
            else if (point.Y < self.LinePosition - PixelDelta)
            {
                return(MathListIndex.IndexAtLocation(self.Range.Location, MathListSubIndexType.Denominator, self.Denominator.IndexForPoint(context, point)));
            }
            if (point.X > self.Position.X + self.Width / 2)
            {
                return(MathListIndex.Level0Index(self.Range.End));
            }

            return(MathListIndex.Level0Index(self.Range.Location));
        }
        public static MathListIndex IndexForPoint <TFont, TGlyph>(this FractionDisplay <TFont, TGlyph> self, TypesettingContext <TFont, TGlyph> context, PointF point) where TFont : IFont <TGlyph>
        {
            // We can be before or after the fraction
            if (point.X < self.Position.X - PixelDelta)
            {
                //We are before the fraction, so
                return(MathListIndex.Level0Index(self.Range.Location));
            }
            else if (point.X > self.Position.X + self.Width + PixelDelta)
            {
                //We are after the fraction
                return(MathListIndex.Level0Index(self.Range.End));
            }

            //We can be either near the numerator or denominator
            var numeratorDistance   = DistanceFromPointToRect(point, self.Numerator.DisplayBounds);
            var denominatorDistance = DistanceFromPointToRect(point, self.Denominator.DisplayBounds);

            if (numeratorDistance < denominatorDistance)
            {
                return(MathListIndex.IndexAtLocation(self.Range.Location, self.Numerator.IndexForPoint(context, point), MathListSubIndexType.Numerator));
            }
            else
            {
                return(MathListIndex.IndexAtLocation(self.Range.Location, self.Denominator.IndexForPoint(context, point), MathListSubIndexType.Denominator));
            }
        }
Beispiel #4
0
        public static MathListIndex IndexForPoint <TFont, TGlyph>(this RadicalDisplay <TFont, TGlyph> self, TypesettingContext <TFont, TGlyph> context, PointF point) where TFont : IFont <TGlyph>
        {
            // We can be before or after the radical
            if (point.X < self.Position.X - PixelDelta)
            {
                //We are before the radical, so
                return(MathListIndex.Level0Index(self.Range.Location));
            }
            else if (point.X > self.Position.X + self.Width + PixelDelta)
            {
                //We are after the radical
                return(MathListIndex.Level0Index(self.Range.End));
            }

            //We can be either near the degree or the radicand
            var degreeRect       = self.Degree != null ? new RectangleF(self.Degree.Position, self.Degree.DisplayBounds.Size) : default;
            var radicandRect     = new RectangleF(self.Radicand.Position, self.Radicand.DisplayBounds.Size);
            var degreeDistance   = DistanceFromPointToRect(point, degreeRect);
            var radicandDistance = DistanceFromPointToRect(point, radicandRect);

            if (degreeDistance < radicandDistance)
            {
                if (self.Degree != null)
                {
                    return(MathListIndex.IndexAtLocation(self.Range.Location, MathListSubIndexType.Degree, self.Degree.IndexForPoint(context, point)));
                }
                return(MathListIndex.Level0Index(self.Range.Location));
            }
            else
            {
                return(MathListIndex.IndexAtLocation(self.Range.Location, MathListSubIndexType.Radicand, self.Radicand.IndexForPoint(context, point)));
            }
        }
Beispiel #5
0
        public static MathListIndex IndexForPoint <TFont, TGlyph>(this TextLineDisplay <TFont, TGlyph> self, TypesettingContext <TFont, TGlyph> context, PointF point) where TFont : IFont <TGlyph>
        {
            // Convert the point to the reference of the CTLine
            var relativePoint   = new PointF(point.X - self.Position.X, point.Y - self.Position.Y);
            var runsAndIndicies =
                self.Runs
                .Select(run => ValueTuple.Create(run, run.Run.GlyphIndexForXOffset(context, relativePoint.Plus(run.Position).X)))
                .Where(x => x.Item2.HasValue)
                .ToArray();

            if (runsAndIndicies.Length == 0)
            {
                return(null);
            }
            var(r, nindex) = runsAndIndicies.Single();
            var index   = nindex.GetValueOrDefault();
            var diffLng = r.Run.Length != r.Range.Length;

            if (index < 0 || (!diffLng && index > self.Range.Length) || (diffLng && index > r.Run.Length))
            {
                throw new InvalidCodePathException($"Returned index out of range: {index}, range ({self.Range.Location}, {self.Range.Length})");
            }
            if (!diffLng)
            {
                return(MathListIndex.Level0Index(self.Range.Location + index));
            }
            if (index > r.Run.Length / 2)
            {
                return(MathListIndex.Level0Index(self.Range.End));
            }

            return(MathListIndex.Level0Index(self.Range.Location));
        }
Beispiel #6
0
        public static MathListIndex IndexForPoint <TFont, TGlyph>(this RadicalDisplay <TFont, TGlyph> self, TypesettingContext <TFont, TGlyph> context, PointF point) where TFont : IFont <TGlyph>
        {
            // We can be before or after the radical
            if (point.X < self.Position.X - PixelDelta)
            {
                //We are before the radical, so
                return(MathListIndex.Level0Index(self.Range.Location));
            }
            else if (point.X > self.Position.X + self.Width + PixelDelta)
            {
                //We are after the radical
                return(MathListIndex.Level0Index(self.Range.End));
            }

            //We can be either near the degree or the radicand
            var degreeDistance   = DistanceFromPointToRect(point, self.Degree?.DisplayBounds ?? default);
            var radicandDistance = DistanceFromPointToRect(point, self.Radicand.DisplayBounds);

            if (degreeDistance < radicandDistance)
            {
                return(MathListIndex.IndexAtLocation(self.Range.Location, self.Degree.IndexForPoint(context, point), MathListSubIndexType.Numerator));
            }
            else
            {
                return(MathListIndex.IndexAtLocation(self.Range.Location, self.Radicand.IndexForPoint(context, point), MathListSubIndexType.Denominator));
            }
        }
Beispiel #7
0
   public static MathListIndex IndexForPoint <TFont, TGlyph>(
       this IGlyphDisplay <TFont, TGlyph> self,
       TypesettingContext <TFont, TGlyph> _,
       PointF point) where TFont : IFont <TGlyph> =>
   point.X > self.Position.X + self.Width / 2
 ? MathListIndex.Level0Index(self.Range.End)
   : MathListIndex.Level0Index(self.Range.Location);
Beispiel #8
0
 public static void HighlightCharacterAt <TFont, TGlyph>(
     this IGlyphDisplay <TFont, TGlyph> self,
     MathListIndex index, Color color) where TFont : IFont <TGlyph>
 {
     if (index.SubIndexType != MathListSubIndexType.None)
     {
         throw new ArgumentException
                   ("The subindex must be none to get the highlight a character in it.", nameof(index));
     }
     self.Highlight(color);
 }
Beispiel #9
0
   public static PointF?PointForIndex <TFont, TGlyph>(
       this IGlyphDisplay <TFont, TGlyph> self,
       TypesettingContext <TFont, TGlyph> _,
       MathListIndex index) where TFont : IFont <TGlyph> =>
   index.SubIndexType != MathListSubIndexType.None
 ? throw new ArgumentException
             ("The subindex must be none to get the closest point for it.", nameof(index))
         : index.AtomIndex == self.Range.End
         // draw a caret after the glyph
 ? self.Position.Plus(new PointF(self.DisplayBounds().Right, 0))
         // draw a caret before the glyph
         : self.Position;
Beispiel #10
0
        public static PointF?PointForIndex <TFont, TGlyph>(this ListDisplay <TFont, TGlyph> self, TypesettingContext <TFont, TGlyph> context, MathListIndex index) where TFont : IFont <TGlyph>
        {
            if (index is null)
            {
                return(null);
            }

            PointF?position;

            if (index.AtomIndex == self.Range.End)
            {
                // Special case the edge of the range
                position = new PointF(self.Width, 0);
            }
            else if (self.Range.Contains(index.AtomIndex) && self.SubDisplayForIndex(index) is IDisplay <TFont, TGlyph> display)
            {
                switch (index.SubIndexType)
                {
                case MathListSubIndexType.Nucleus:
                    var nucleusPosition = index.AtomIndex + index.SubIndex.AtomIndex;
                    position = display.PointForIndex(context, MathListIndex.Level0Index(nucleusPosition));
                    break;

                case MathListSubIndexType.None:
                    position = display.PointForIndex(context, index);
                    break;

                default:
                    // Recurse
                    position = display.PointForIndex(context, index.SubIndex);
                    break;
                }
            }
            else
            {
                // Outside the range
                return(null);
            }

            if (position is PointF found)
            {
                // Convert bounds from our coordinate system before returning
                found.X += self.Position.X;
                found.Y += self.Position.Y;
                return(found);
            }
            else
            {
                // We didn't find the position
                return(null);
            }
        }
Beispiel #11
0
   public static MathListIndex IndexForPoint <TFont, TGlyph>(
       this InnerDisplay <TFont, TGlyph> self,
       TypesettingContext <TFont, TGlyph> context,
       PointF point) where TFont : IFont <TGlyph> =>
   // We can be before or after the inner
   point.X <self.Position.X + (self.Left?.Width / 2 ?? 0)
            //We are before the inner, so
 ? MathListIndex.Level0Index(self.Range.Location)
            : point.X> self.Position.X + self.Width - (self.Right?.Width / 2 ?? 0)
   //We are after the inner
 ? MathListIndex.Level0Index(self.Range.End)
   : MathListIndex.IndexAtLocation(self.Range.Location,
                                   MathListSubIndexType.Inner, self.Inner.IndexForPoint(context, point));
   public static MathListIndex IndexForPoint <TFont, TGlyph>(
       this LargeOpLimitsDisplay <TFont, TGlyph> self,
       TypesettingContext <TFont, TGlyph> context,
       PointF point) where TFont : IFont <TGlyph> =>
   // We can be before or after the large operator
   point.X <self.Position.X - PixelDelta
            // We are before the large operator, so
            ?MathListIndex.Level0Index(self.Range.Location)
            : point.X> self.Position.X + self.Width + PixelDelta
   // We are after the large operator
 ? MathListIndex.Level0Index(self.Range.End)
   : self.UpperLimit is
   {
Beispiel #13
0
   public static MathListIndex IndexForPoint <TFont, TGlyph>(
       this RadicalDisplay <TFont, TGlyph> self,
       TypesettingContext <TFont, TGlyph> context,
       PointF point) where TFont : IFont <TGlyph> =>
   // We can be before or after the radical
   point.X <self.Position.X - PixelDelta
            //We are before the radical, so
            ?MathListIndex.Level0Index(self.Range.Location)
            : point.X> self.Position.X + self.Width + PixelDelta
   //We are after the radical
 ? MathListIndex.Level0Index(self.Range.End)
   //We can be either near the degree or the radicand
   : DistanceFromPointToRect(point, self.Degree != null ? new RectangleF(self.Degree.Position, self.Degree.DisplayBounds().Size) : default)
   < DistanceFromPointToRect(point, new RectangleF(self.Radicand.Position, self.Radicand.DisplayBounds().Size))
 ? self.Degree != null
   ? MathListIndex.IndexAtLocation(self.Range.Location, MathListSubIndexType.Degree, self.Degree.IndexForPoint(context, point))
   : MathListIndex.Level0Index(self.Range.Location)
   : MathListIndex.IndexAtLocation(self.Range.Location, MathListSubIndexType.Radicand, self.Radicand.IndexForPoint(context, point));
Beispiel #14
0
 static void InsertAtAtomIndexAndAdvance(this MathList self, int atomIndex, MathAtom 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 Atoms.Placeholder placeholder)
     {
         atom.Superscript.Append(placeholder.Superscript);
         atom.Subscript.Append(placeholder.Subscript);
         self[atomIndex] = atom;
     }
     else
     {
         self.Insert(atomIndex, atom);
     }
     advance = advanceType switch
     {
         MathListSubIndexType.None => advance.Next,
         _ => advance.LevelUpWithSubIndex(advanceType, MathListIndex.Level0Index(0)),
     };
 }
Beispiel #15
0
        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 MathListIndex IndexForPoint <TFont, TGlyph>(this TextLineDisplay <TFont, TGlyph> self, TypesettingContext <TFont, TGlyph> context, PointF point) where TFont : IFont <TGlyph>
        {
            // Convert the point to the reference of the CTLine
            var relativePoint = new PointF(point.X - self.Position.X, point.Y - self.Position.Y);
            var indices       = self.Runs.Select(run => run.Run.GlyphIndexForXOffset(context, relativePoint.Plus(run.Position).X)).Where(x => x.HasValue);

            if (indices.IsEmpty())
            {
                return(null);
            }
            var index = indices.Single().GetValueOrDefault();
            // The index returned is in UTF-16, translate to codepoint index.
            // NSUInteger codePointIndex = stringIndexToCodePointIndex(self.attributedString.string, index);
            // Convert the code point index to an index into the mathlist
            var mlIndex = self.StringIndexToMathListIndex(index);

            // index will be between 0 and _range.length inclusive
            if (mlIndex < 0 || mlIndex > self.Range.Length)
            {
                throw new InvalidCodePathException($"Returned index out of range: {index}, range ({self.Range.Location}, {self.Range.Length})");
            }
            // translate to the current index
            return(MathListIndex.Level0Index(self.Range.Location + mlIndex));
        }
 public SubIndexTypeMismatchException(Type atomType, MathListIndex index) : base(
         $"{atomType} not found at index {index.AtomIndex}.")
 {
 }
 public static PointF?PointForIndex <TFont, TGlyph>(this FractionDisplay <TFont, TGlyph> self, TypesettingContext <TFont, TGlyph> context, MathListIndex index) where TFont : IFont <TGlyph>
 {
     if (index.SubIndexType != MathListSubIndexType.None)
     {
         throw Arg("The subindex must be none to get the closest point for it.", nameof(index));
     }
     // draw a caret after the fraction
     return(new PointF(self.DisplayBounds.Right, self.Position.Y));
 }
Beispiel #19
0
        public static MathListIndex?IndexForPoint <TFont, TGlyph>
            (this ListDisplay <TFont, TGlyph> self, TypesettingContext <TFont, TGlyph> context, PointF point)
            where TFont : IFont <TGlyph>
        {
            // The origin of for the subelements of a MathList is the current position,
            // so translate the current point to our origin.
            var translatedPoint = new PointF(point.X - self.Position.X, point.Y - self.Position.Y);

            IDisplay <TFont, TGlyph>?closest = null;
            var   xbounds     = new List <IDisplay <TFont, TGlyph> >();
            float minDistance = float.MaxValue;

            foreach (var display in self.Displays)
            {
                var bounds     = display.DisplayBounds();
                var rect       = new RectangleF(display.Position, bounds.Size);
                var maxBoundsX = rect.Right;
                if (rect.X - PixelDelta <= translatedPoint.X && translatedPoint.X <= maxBoundsX + PixelDelta)
                {
                    xbounds.Add(display);
                }
                var distance = DistanceFromPointToRect(translatedPoint, rect);
                if (distance < minDistance)
                {
                    closest     = display;
                    minDistance = distance;
                }
            }
            IDisplay <TFont, TGlyph>?displayWithPoint;

            switch (xbounds.Count)
            {
            case 0:
                if (translatedPoint.X <= -PixelDelta)
                {
                    // All the way to the left
                    return(self.Range.Location < 0
                   ? null
                   : MathListIndex.Level0Index(self.Range.Location));
                }
                else if (translatedPoint.X >= self.Width + PixelDelta)
                {
                    // if closest is a script
                    if (closest is ListDisplay <TFont, TGlyph> ld && ld.LinePosition != LinePosition.Regular)
                    {
                        // then we try to find its parent
                        var parent = self.Displays.FirstOrDefault(d => d.HasScript && d.Range.Contains(ld.IndexInParent));
                        if (parent != null)
                        {
                            return(MathListIndex.Level0Index(parent.Range.End));
                        }
                    }
                    // All the way to the right
                    return
                        (self.Range.End < 0
              ? null
              : self.Displays.Count == 1 &&
                         self.Displays[0] is TextLineDisplay <TFont, TGlyph> {
                        Atoms : var atoms
                    } &&
                         atoms.Count == 1 &&
                         atoms[0] is Atom.Atoms.Placeholder
              ? MathListIndex.Level0Index(self.Range.Location)
                         : MathListIndex.Level0Index(self.Range.End));
                }
Beispiel #20
0
        public static MathListIndex IndexForPoint <TFont, TGlyph>(this ListDisplay <TFont, TGlyph> self, TypesettingContext <TFont, TGlyph> context, PointF point) where TFont : IFont <TGlyph>
        {
            // The origin of for the subelements of a MathList is the current position, so translate the current point to our origin.
            var translatedPoint = new PointF(point.X - self.Position.X, point.Y - self.Position.Y);

            IDisplay <TFont, TGlyph> closest = null;
            var   xbounds     = new List <IDisplay <TFont, TGlyph> >();
            float minDistance = float.MaxValue;

            foreach (var display in self.Displays)
            {
                var bounds     = display.DisplayBounds;
                var rect       = new RectangleF(display.Position, bounds.Size);
                var maxBoundsX = rect.Right;
                if (rect.X - PixelDelta <= translatedPoint.X && translatedPoint.X <= maxBoundsX + PixelDelta)
                {
                    xbounds.Add(display);
                }
                var distance = DistanceFromPointToRect(translatedPoint, rect);
                if (distance < minDistance)
                {
                    closest     = display;
                    minDistance = distance;
                }
            }
            IDisplay <TFont, TGlyph> displayWithPoint;

            switch (xbounds.Count)
            {
            case 0:
                if (translatedPoint.X <= -PixelDelta)
                {
                    // All the way to the left
                    return(self.Range.Location < 0 ? null : MathListIndex.Level0Index(self.Range.Location));
                }
                else if (translatedPoint.X >= self.Width + PixelDelta)
                {
                    // if closest is a script
                    if (closest != null && closest is ListDisplay <TFont, TGlyph> ld &&
                        ld.LinePosition != Enumerations.LinePosition.Regular)
                    {
                        // then we try to find its parent
                        var parent = self.Displays.FirstOrDefault(d => d.HasScript && d.Range.Contains(ld.IndexInParent));

                        if (parent != null)
                        {
                            return(MathListIndex.Level0Index(parent.Range.End));
                        }
                    }
                    // All the way to the right
                    return(self.Range.End < 0 ? null : MathListIndex.Level0Index(self.Range.End));
                }
                else
                {
                    // It is within the ListDisplay but not within the X bounds of any sublist. Use the closest in that case.
                    displayWithPoint = closest;
                }
                break;

            case 1:
                displayWithPoint = xbounds[0];
                var rect = new RectangleF(displayWithPoint.Position, displayWithPoint.DisplayBounds.Size);
                if (translatedPoint.X >= self.Width - PixelDelta)
                {
                    //The point is close to the end. Only use the selected X bounds if the Y is within range.
                    if (translatedPoint.Y <= rect.YMin() - PixelDelta)
                    {
                        //The point is less than the Y including the delta. Move the cursor to the end rather than in this atom.
                        return(MathListIndex.Level0Index(self.Range.End));
                    }
                }
                break;

            default:
                //Use the closest since there are more than 2 sublists which have this X position.
                displayWithPoint = closest;
                break;
            }
            if (displayWithPoint is null)
            {
                return(null);
            }

            var index = displayWithPoint.IndexForPoint(context, translatedPoint);

            if (displayWithPoint is ListDisplay <TFont, TGlyph> closestLine)
            {
                if (closestLine.LinePosition is Enumerations.LinePosition.Regular)
                {
                    throw Arg($"{nameof(ListDisplay<TFont, TGlyph>)} {nameof(ListDisplay<TFont, TGlyph>.LinePosition)} {nameof(Enumerations.LinePosition.Regular)} " +
                              $"inside an {nameof(ListDisplay<TFont, TGlyph>)} - shouldn't happen", nameof(self));
                }
                // This is a subscript or a superscript, return the right type of subindex
                var indexType = closestLine.LinePosition is Enumerations.LinePosition.Subscript ? MathListSubIndexType.Subscript : MathListSubIndexType.Superscript;
                // The index of the atom this denotes.
                if (closestLine.IndexInParent is int.MinValue)
                {
                    throw Arg($"Index was not set for a {indexType} in the {nameof(ListDisplay<TFont, TGlyph>)}.", nameof(self));
                }
                return(MathListIndex.IndexAtLocation(closestLine.IndexInParent, indexType, index));
            }
            else if (displayWithPoint.HasScript)
            {
                //The display list has a subscript or a superscript. If the index is at the end of the atom, then we need to put it before the sub/super script rather than after.
                if (index?.AtomIndex == displayWithPoint.Range.End)
                {
                    return(MathListIndex.IndexAtLocation(index.AtomIndex - 1, MathListSubIndexType.BetweenBaseAndScripts, MathListIndex.Level0Index(1)));
                }
            }
            return(index);
        }
Beispiel #21
0
        public static PointF?PointForIndex <TFont, TGlyph>(this RadicalDisplay <TFont, TGlyph> self, TypesettingContext <TFont, TGlyph> context, MathListIndex index) where TFont : IFont <TGlyph>
        {
            if (index.SubIndexType != MathListSubIndexType.None)
            {
                throw Arg("The subindex must be none to get the closest point for it.", nameof(index));
            }

            if (index.AtomIndex == self.Range.End)
            {
                // draw a caret after the radical
                return(self.Position.Plus(new PointF(self.DisplayBounds.Right, 0)));
            }
            // draw a caret before the radical
            return(self.Position);
        }
Beispiel #22
0
        ///<summary>The bounds of the display indicated by the given index</summary>
        public static PointF?PointForIndex <TFont, TGlyph>(this IDisplay <TFont, TGlyph> display, FrontEnd.TypesettingContext <TFont, TGlyph> context, MathListIndex index) where TFont : IFont <TGlyph>
        {
            switch (display)
            {
            case TextLineDisplay <TFont, TGlyph> text:
                return(text.PointForIndex(context, index));

            case FractionDisplay <TFont, TGlyph> frac:
                return(frac.PointForIndex(context, index));

            case RadicalDisplay <TFont, TGlyph> radical:
                return(radical.PointForIndex(context, index));

            case ListDisplay <TFont, TGlyph> list:
                return(list.PointForIndex(context, index));

            default:
                return(null);
            }
        }
Beispiel #23
0
        public static void HighlightCharacterAt <TFont, TGlyph>(this IDisplay <TFont, TGlyph> display, MathListIndex index, Structures.Color color) where TFont : IFont <TGlyph>
        {
            switch (display)
            {
            case TextLineDisplay <TFont, TGlyph> text:
                text.HighlightCharacterAt(index, color);
                break;

            case FractionDisplay <TFont, TGlyph> frac:
                frac.HighlightCharacterAt(index, color);
                break;

            case RadicalDisplay <TFont, TGlyph> radical:
                radical.HighlightCharacterAt(index, color);
                break;

            case ListDisplay <TFont, TGlyph> list:
                list.HighlightCharacterAt(index, color);
                break;

            default:
                break;
            }
        }
Beispiel #24
0
 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;
     }
Beispiel #25
0
        public static PointF?PointForIndex <TFont, TGlyph>(this ListDisplay <TFont, TGlyph> self, TypesettingContext <TFont, TGlyph> context, MathListIndex index) where TFont : IFont <TGlyph>
        {
            if (index is null)
            {
                return(null);
            }

            PointF?position    = null;
            var    nonScripted =
                self.Displays
                .Where(d => !(d is ListDisplay <TFont, TGlyph> ld &&
                              ld.LinePosition != Enumerations.LinePosition.Regular))
                .ToArray();

            if (index.SubIndexType == MathListSubIndexType.None &&
                nonScripted.Length > 0 &&
                nonScripted.All(d => d.Range.End <= index.AtomIndex))
            {
                position = new PointF(self.Width, 0);
            }
            else
            {
                if (index.AtomIndex == self.Range.End)
                {
                    // Special case the edge of the range
                    position = new PointF(self.Width, 0);
                }
                else if (self.Range.Contains(index.AtomIndex) && self.SubDisplayForIndex(index) is IDisplay <TFont, TGlyph> display)
                {
                    switch (index.SubIndexType)
                    {
                    case MathListSubIndexType.BetweenBaseAndScripts:
                        var nucleusPosition = index.AtomIndex + index.SubIndex.AtomIndex;
                        position = display.PointForIndex(context, MathListIndex.Level0Index(nucleusPosition));
                        break;

                    case MathListSubIndexType.None:
                        if (!display.HasScript)
                        {
                            position = display.PointForIndex(context, index);
                        }
                        else
                        {
                            var mainPosition = display.PointForIndex(context, index);
                            position = self.Displays.SingleOrDefault(d =>
                                                                     d is ListDisplay <TFont, TGlyph> ld && ld.IndexInParent == index.AtomIndex - 1)
                                       is IDisplay <TFont, TGlyph> scripted && mainPosition != null
                  ? new PointF(mainPosition.Value.X + scripted.Width, 0)
                  : mainPosition;
                        }
                        break;

                    default:
                        // Recurse
                        position = display.PointForIndex(context, index.SubIndex);
                        break;
                    }
                }
                else
                {
                    // Outside the range
                    return(null);
                }
            }
            if (position is PointF found)
            {
                // Convert bounds from our coordinate system before returning
                found.X += self.Position.X;
                found.Y += self.Position.Y;
                return(found);
            }
            else
            {
                // We didn't find the position
                return(null);
            }
        }
Beispiel #26
0
 public static void HighlightCharacterAt <TFont, TGlyph>(this ListDisplay <TFont, TGlyph> self, MathListIndex index, Color color) where TFont : IFont <TGlyph>
 {
     if (index is null)
     {
         return;
     }
     if (self.Range.Contains(index.AtomIndex) && self.SubDisplayForIndex(index) is IDisplay <TFont, TGlyph> display)
     {
         if (index.SubIndexType is MathListSubIndexType.BetweenBaseAndScripts || index.SubIndexType is MathListSubIndexType.None)
         {
             display.HighlightCharacterAt(index, color);
         }
         else
         {
             // Recurse
             display.HighlightCharacterAt(index.SubIndex, color);
         }
     }
 }
Beispiel #27
0
        /// <summary>Inserts <paramref name="atom"/> and modifies <paramref name="index"/> to advance to the next position.</summary>
        public static void InsertAndAdvance(this MathList self, ref MathListIndex index, MathAtom atom, MathListSubIndexType advanceType)
        {
            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.InsertAtAtomIndexAndAdvance(index.AtomIndex, atom, ref index, advanceType);
                break;

            case var _ when index.SubIndex is null:
                throw new InvalidCodePathException("index.SubIndex is null despite non-None subindex type");

            case MathListSubIndexType.BetweenBaseAndScripts:
                var currentAtom = self.Atoms[index.AtomIndex];
                if (currentAtom.Subscript.IsEmpty() && currentAtom.Superscript.IsEmpty())
                {
                    throw new SubIndexTypeMismatchException(index);
                }
                if (atom.Subscript.IsNonEmpty() || atom.Superscript.IsNonEmpty())
                {
                    throw new ArgumentException("Cannot fuse with an atom that already has a subscript or a superscript");
                }
                atom.Subscript.Append(currentAtom.Subscript);
                atom.Superscript.Append(currentAtom.Superscript);
                currentAtom.Subscript.Clear();
                currentAtom.Superscript.Clear();
                var atomIndex = index.AtomIndex;
                // Prevent further subindexing inside BetweenBaseAndScripts
                if (advanceType != MathListSubIndexType.None &&
                    index.LevelDown() is MathListIndex levelDown)
                {
                    index = levelDown.Next;
                }
                self.InsertAtAtomIndexAndAdvance(atomIndex + 1, atom, ref index, advanceType);
                break;

            case MathListSubIndexType.Degree:
            case MathListSubIndexType.Radicand:
                if (!(self.Atoms[index.AtomIndex] is Atoms.Radical radical))
                {
                    throw new SubIndexTypeMismatchException(typeof(Atoms.Radical), index);
                }
                if (index.SubIndexType == MathListSubIndexType.Degree)
                {
                    radical.Degree.InsertAndAdvance(ref index.SubIndex, atom, advanceType);
                }
                else
                {
                    radical.Radicand.InsertAndAdvance(ref index.SubIndex, atom, advanceType);
                }
                break;

            case MathListSubIndexType.Numerator:
            case MathListSubIndexType.Denominator:
                if (!(self.Atoms[index.AtomIndex] is Atoms.Fraction frac))
                {
                    throw new SubIndexTypeMismatchException(typeof(Atoms.Fraction), index);
                }
                if (index.SubIndexType == MathListSubIndexType.Numerator)
                {
                    frac.Numerator.InsertAndAdvance(ref index.SubIndex, atom, advanceType);
                }
                else
                {
                    frac.Denominator.InsertAndAdvance(ref index.SubIndex, atom, advanceType);
                }
                break;

            case MathListSubIndexType.Subscript:
                self.Atoms[index.AtomIndex].Subscript.InsertAndAdvance(ref index.SubIndex, atom, advanceType);
                break;

            case MathListSubIndexType.Superscript:
                self.Atoms[index.AtomIndex].Superscript.InsertAndAdvance(ref index.SubIndex, atom, advanceType);
                break;

            case MathListSubIndexType.Inner:
                if (!(self.Atoms[index.AtomIndex] is Atoms.Inner inner))
                {
                    throw new SubIndexTypeMismatchException(typeof(Atoms.Inner), index);
                }
                inner.InnerList.InsertAndAdvance(ref index.SubIndex, atom, advanceType);
                break;

            default:
                throw new SubIndexTypeMismatchException(index);
            }
        }
Beispiel #28
0
 public static IDisplay <TFont, TGlyph> SubDisplayForIndex <TFont, TGlyph>(this ListDisplay <TFont, TGlyph> self, MathListIndex index) where TFont : IFont <TGlyph>
 {
     // Inside the range
     if (index.SubIndexType is MathListSubIndexType.Superscript || index.SubIndexType is MathListSubIndexType.Subscript)
     {
         foreach (var display in self.Displays)
         {
             if (display is ListDisplay <TFont, TGlyph> list && index.AtomIndex == list.IndexInParent &&
                 // This is the right character for the sub/superscript, check that it's type matches the index
                 ((list.LinePosition is Enumerations.LinePosition.Subscript && index.SubIndexType is MathListSubIndexType.Subscript) ||
                  (list.LinePosition is Enumerations.LinePosition.Superscript && index.SubIndexType is MathListSubIndexType.Superscript)))
             {
                 return(list);
             }
             else
             {
             }
         }
   public SubIndexTypeMismatchException(MathListIndex index) : base(
           Array.IndexOf(typeof(MathListSubIndexType).GetEnumValues(), index.SubIndexType) == -1
 ? $"{index.SubIndexType} is an invalid subindex type."
 : $"{index.SubIndexType} not found at index {index.AtomIndex}.")
   {
   }
Beispiel #30
0
        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;
            }
        }