예제 #1
0
        public void Clone_State_ReturnsClone()
        {
            var a = new SubstitutionAction
            {
                ReplacedGlyphCount  = 2,
                SkippedGlyphCount   = 2,
                ReplacementGlyphIds = new ushort[] { 1, 2, 3 }
            };

            var b = a.Clone();

            var comparer = new TransitionActionEqualityComparer();

            Assert.IsTrue(comparer.Equals(a, b));
        }
예제 #2
0
        public void Clone_Action_ReturnsClone()
        {
            var a = new AnchorPointToAnchorPointAction
            {
                CurrentGlyphAnchorPoint = new AnchorPoint {
                    X = 1, Y = 2
                },
                PreviousGlyphAnchorPoint = new AnchorPoint {
                    X = 2, Y = 3
                },
            };

            var b = a.Clone();

            var comparer = new TransitionActionEqualityComparer();

            Assert.IsTrue(comparer.Equals(a, b));
        }
예제 #3
0
        public void Clone_State_ReturnsClone()
        {
            var a = new PositioningAdjustmentAction
            {
                PositionChanges = new[]
                {
                    new GlyphPositionChange {
                        AdvanceX = 1, AdvanceY = 2, OffsetX = 3, OffsetY = 4
                    },
                    new GlyphPositionChange {
                        AdvanceX = 2, AdvanceY = 3, OffsetX = 4, OffsetY = 5
                    }
                }
            };

            var b = a.Clone();

            var comparer = new TransitionActionEqualityComparer();

            Assert.IsTrue(comparer.Equals(a, b));
        }
예제 #4
0
        /// <summary>
        /// Normalizes the specified input machine.
        /// </summary>
        /// <param name="inputMachine">The input machine. This state machine will not be changed (a copy of all involved objects is constructed).</param>
        /// <param name="allowedGlyphIds">The set of glyph IDs, which are allowed to have transitions in the machine. If null, all glyphs are allowed.</param>
        /// <returns>
        /// The normalized machine.
        /// </returns>
        public StateMachine Normalize(StateMachine inputMachine, IEnumerable <ushort> allowedGlyphIds)
        {
            var allowedGlyphIdsSet =
                allowedGlyphIds != null ?
                new HashSet <ushort>(allowedGlyphIds) :
                new HashSet <ushort>(Enumerable.Range(ushort.MinValue, ushort.MaxValue - ushort.MinValue + 1).Select(p => (ushort)p));

            var queue = new Queue <State>();

            queue.Enqueue(inputMachine.EntryState);

            var visitedOrQueuedStates = new HashSet <State> {
                inputMachine.EntryState
            };
            var oldToNewMap = new Dictionary <State, State>();

            var transitionsByTargetState = new Dictionary <State, HashSet <ITransition> >();
            var transitionComparer       = new TransitionNonrecursiveEqualityComparer();

            var actionComparer = new TransitionActionEqualityComparer();

            while (queue.Any())
            {
                var currentState = queue.Dequeue();

                State newState;
                if (oldToNewMap.ContainsKey(currentState))
                {
                    newState = oldToNewMap[currentState];
                }
                else
                {
                    newState = new State();
                    oldToNewMap.Add(currentState, newState);
                }

                var groupedTransitions = currentState.Transitions.GroupBy(p => p.TargetState);
                foreach (var transitionGroup in groupedTransitions)
                {
                    var glyphIdsToMerge = new List <ushort>();

                    // Transitions that can't be merged (fallback and always transitions, or transitions that don't match others)
                    // have to be cloned as they are.
                    var transitionsToClone = new List <ITransition>();

                    var   oldTargetState = transitionGroup.First().TargetState;
                    State newTargetState;
                    if (oldToNewMap.ContainsKey(oldTargetState))
                    {
                        newTargetState = oldToNewMap[oldTargetState];
                    }
                    else
                    {
                        newTargetState = new State();
                        oldToNewMap[oldTargetState] = newTargetState;
                    }

                    ITransition lastMergedTransition = null;
                    foreach (var transition in transitionGroup)
                    {
                        if (transition is SimpleTransition || transition is SetTransition)
                        {
                            // Only transitions which share all attributes but target and transition number can be merged.
                            if (
                                lastMergedTransition == null ||
                                (
                                    transition.IsFallback == lastMergedTransition.IsFallback &&
                                    transition.HeadShift == lastMergedTransition.HeadShift &&
                                    transition.LookupFlags == lastMergedTransition.LookupFlags &&
                                    actionComparer.Equals(transition.Action, lastMergedTransition.Action)
                                )
                                )
                            {
                                if (transition is SimpleTransition)
                                {
                                    glyphIdsToMerge.Add((transition as SimpleTransition).GlyphId);
                                }
                                else
                                {
                                    glyphIdsToMerge.AddRange((transition as SetTransition).GlyphIdSet);
                                }

                                lastMergedTransition = transition;
                            }
                            else
                            {
                                transitionsToClone.Add(transition);
                            }
                        }
                        else
                        {
                            transitionsToClone.Add(transition);
                        }
                    }

                    var filteredGlyphIdsToMerge = new HashSet <ushort>(glyphIdsToMerge);
                    filteredGlyphIdsToMerge.IntersectWith(allowedGlyphIdsSet);

                    var newTransitions = new List <ITransition>();

                    foreach (var transition in transitionsToClone)
                    {
                        var newTransition = transition.Clone();
                        newTransition.TargetState = newTargetState;
                        newTransition             = this.AddTransitionOrGetExisting(transitionsByTargetState, newTargetState, newTransition, transitionComparer);

                        if (transition is SimpleTransition)
                        {
                            if (allowedGlyphIdsSet.Contains(((SimpleTransition)transition).GlyphId))
                            {
                                newTransitions.Add(newTransition);
                            }
                        }
                        else if (transition is SetTransition)
                        {
                            ((SetTransition)transition).GlyphIdSet.IntersectWith(allowedGlyphIdsSet);

                            if (((SetTransition)transition).GlyphIdSet.Count > 0)
                            {
                                newTransitions.Add(newTransition);
                            }
                        }
                        else
                        {
                            newTransitions.Add(newTransition);
                        }
                    }

                    // If there are no glyphs remaining in the set, no transition will be added.
                    if (filteredGlyphIdsToMerge.Count == 1)
                    {
                        var newTransition = (ITransition) new SimpleTransition {
                            GlyphId     = filteredGlyphIdsToMerge.Single(),
                            HeadShift   = lastMergedTransition.HeadShift,
                            LookupFlags = lastMergedTransition.LookupFlags,
                            Action      = lastMergedTransition.Action == null ? null : lastMergedTransition.Action.Clone(),
                            TargetState = newTargetState
                        };
                        newTransition = this.AddTransitionOrGetExisting(transitionsByTargetState, newTargetState, newTransition, transitionComparer);
                        newTransitions.Add(newTransition);
                    }
                    else if (filteredGlyphIdsToMerge.Count > 1)
                    {
                        var newTransition = (ITransition) new SetTransition
                        {
                            GlyphIdSet  = filteredGlyphIdsToMerge,
                            HeadShift   = lastMergedTransition.HeadShift,
                            LookupFlags = lastMergedTransition.LookupFlags,
                            Action      = lastMergedTransition.Action == null ? null : lastMergedTransition.Action.Clone(),
                            TargetState = newTargetState
                        };
                        newTransition = this.AddTransitionOrGetExisting(transitionsByTargetState, newTargetState, newTransition, transitionComparer);
                        newTransitions.Add(newTransition);
                    }

                    foreach (var newTransition in newTransitions)
                    {
                        newState.Transitions.Add(newTransition);
                    }

                    if (!visitedOrQueuedStates.Contains(oldTargetState))
                    {
                        queue.Enqueue(oldTargetState);
                        visitedOrQueuedStates.Add(oldTargetState);
                    }
                }
            }

            var unsortedMachine = new StateMachine(oldToNewMap[inputMachine.EntryState]);

            var sorter = new StateMachineTransitionSorter();

            return(sorter.SortTransitions(unsortedMachine));
        }