/// <summary>
        /// Calculate answer step distance.
        /// DP matching
        /// </summary>
        /// <param name="group1"></param>
        /// <param name="group2"></param>
        /// <returns></returns>
        private DPMatchingResult CalcAnswerStepDistance(AnswerStep group1, AnswerStep group2)
        {
            // parameters ---------------------
            // Gap cost for DP matching
            double gapCost = 100.0;
            // --------------------------------

            List<AnalysisPenStroke> strokes1 = group1.GetNormalizedStrokes(AnswerSheetAnalyzer.NormarizingHeight, true);
            List<AnalysisPenStroke> strokes2 = group2.GetNormalizedStrokes(AnswerSheetAnalyzer.NormarizingHeight, true);

            if (group1.Strokes.Count == 0 && group2.Strokes.Count == 0)
            {
                return new DPMatchingResult(0.0, new List<int[]>());
            }
            if (group1.Strokes.Count == 0)
            {
                return new DPMatchingResult(1.0 / ((double)strokes2.Count * gapCost), new List<int[]>());
            }
            if (group2.Strokes.Count == 0)
            {
                return new DPMatchingResult(1.0 / ((double)strokes1.Count * gapCost), new List<int[]>());
            }

            double[,] m = new double[strokes1.Count, strokes2.Count];
            // from which direction?
            // 0->match, -1->fromStroke1, -2->fromStroke2
            int[,] from = new int[strokes1.Count, strokes2.Count];
            for (int i = 0, ilen = strokes1.Count; i < ilen; i++)
            {
                m[i, 0] = (double)i * gapCost;
                from[i, 0] = -1;
            }
            for (int i = 0, ilen = strokes2.Count; i < ilen; i++)
            {
                m[0, i] = (double)i * gapCost;
                from[0, i] = -2;
            }
            from[0, 0] = 0;
            for (int i = 1, ilen = strokes1.Count; i < ilen; i++)
            {
                for (int k = 1, klen = strokes2.Count; k < klen; k++)
                {
                    double d = 0.0;
                    d = CalcStrokeDistance(strokes1[i], strokes2[k]).Distance;
                    //Console.WriteLine("Stroke Sequence Distance: " + d);
                    //double[] scores = new double[] { m[i - 1, k] + gapCost, m[i - 1, k - 1] + d, m[i, k - 1] + gapCost };
                    //Console.WriteLine("Stroke Sequence Distance Candidates: " + scores[0] + "," + scores[1] + "," + scores[2]);
                    //m[i, k] = scores.Min();

                    double from_grp1 = m[i - 1, k] + gapCost;
                    double from_grp2 = m[i, k - 1] + gapCost;
                    double from_match = m[i - 1, k - 1] + d;
                    if (from_match <= from_grp1 && from_match <= from_grp2)
                    {
                        m[i, k] = from_match;
                    }
                    else if (from_grp1 <= from_grp2 && from_grp1 <= from_match)
                    {
                        from[i, k] = -1;
                        m[i, k] = from_grp1;
                    }
                    else if (from_grp2 <= from_grp1 && from_grp2 <= from_match)
                    {
                        from[i, k] = -2;
                        m[i, k] = from_grp2;
                    }
                }
            }

            // generate matching list
            // {group1Pos, group2Pos, distance, joinCount}
            List<int[]> matchingList = new List<int[]>();
            int g1Pos = strokes1.Count - 1;
            int g2Pos = strokes2.Count - 1;
            for (int i = strokes1.Count - 1, k = strokes2.Count - 1; i != 0 || k != 0; )
            {
                int[] match = null;
                switch (from[i, k])
                {
                    case -1:
                        match = new int[4] { g1Pos, -1, -1, 0 };
                        i--;
                        g1Pos--;
                        break;
                    case -2:
                        match = new int[4] { -1, g2Pos, -1, 0 };
                        k--;
                        g2Pos--;
                        break;
                    default:
                        match = new int[4] { g1Pos, g2Pos, -1, from[i, k] };
                        i--;
                        k--;
                        g1Pos--;
                        g2Pos--;
                        break;
                }
                matchingList.Add(match);
            }
            matchingList.Reverse();
            DPMatchingResult res = new DPMatchingResult(m[strokes1.Count - 1, strokes2.Count - 1] / (Math.Min(strokes1.Count, strokes2.Count)), matchingList);

            return res;
            //return m[strokes1.Count - 1, strokes2.Count - 1] / (Math.Min(strokes1.Count, strokes2.Count));
        }
        /// <summary>
        /// Group answer step. Result is set to this.answerGroupList.
        /// Weighted manhattan distance method
        /// </summary>
        /// <param name="strokes"></param>
        /// <returns>Result of grouping</returns>
        public List<AnswerStep> GroupAnswerStep(List<AnalysisPenStroke> strokes)
        {
            List<AnswerStep> ansGroupList = new List<AnswerStep>();

            // grouping by using weighted manhattan distance (WMD)
            // AnsGrp:0 - AnsGrp:N
            AnswerStep ansGroup = new AnswerStep(0);
            for (int i = 0, ilen = strokes.Count - 1; i < ilen; i++)
            {
                ansGroup.Strokes.Add(strokes[i]);
                double d = CommonFunction.WeightedManhattanDistance(strokes[i].CenterPoint, strokes[i + 1].CenterPoint, 1.0 - GROUP_WM_YWEIGHT, GROUP_WM_YWEIGHT);
                //Console.WriteLine("WMDistance: " + d);
                if (d > GROUP_WM_THRES)
                {
                    ansGroupList.Add(ansGroup);
                    ansGroup = new AnswerStep(ansGroupList.Count);
                }
            }
            ansGroup.Strokes.Add(strokes[strokes.Count - 1]);
            ansGroupList.Add(ansGroup);

            // for reworking
            bool combined = true;
            while (combined)
            {
                combined = false;
                for (int i = 0, ilen = ansGroupList.Count; i < ilen; i++)
                {
                    for (int k = i + 1; k < ilen; k++)
                    {
                        Rect ri = ansGroupList[i].GetBounds();
                        Rect rk = ansGroupList[k].GetBounds();
                        // Overlap condition
                        if (rk.Top < ri.Bottom && ri.Top < rk.Bottom)
                        {
                            // calculate y-axis overlap amount
                            double overlap = 0.0;
                            if (ri.Top <= rk.Top && ri.Bottom <= rk.Bottom)
                            {
                                overlap = (ri.Bottom - rk.Top) / ((ri.Height < rk.Height) ? ri.Height : rk.Height);
                            }
                            else if (rk.Top <= ri.Top && rk.Bottom <= ri.Bottom)
                            {
                                overlap = (rk.Bottom - ri.Top) / ((ri.Height < rk.Height) ? ri.Height : rk.Height);
                            }
                            else
                            {
                                overlap = 1.0;
                            }

                            if (GROUP_WM_OVERLAP_Y < overlap)
                            {
                                // calculate x-axis distance
                                double dx = GROUP_WM_THRES / (1.0 - GROUP_WM_YWEIGHT);
                                if (rk.Left < ri.Right + dx && ri.Left - dx < rk.Right)
                                {
                                    // combine answer step group
                                    ansGroupList[i].Strokes.AddRange(ansGroupList[k].Strokes);
                                    ansGroupList.RemoveAt(k);

                                    combined = true;
                                    break;
                                }
                            }
                        }
                    }
                    if (combined) break;
                }
            }
            // reassign group ID
            for (int i = 0, ilen = ansGroupList.Count; i < ilen; i++)
            {
                ansGroupList[i].GroupID = i;
            }

            return ansGroupList;
        }
        /// <summary>
        /// Calculate answer process similarity
        /// </summary>
        /// <param name="group1"></param>
        /// <param name="group2"></param>
        /// <returns>Matching result</returns>
        public DPMatchingResult CalcAnswerProcessSimilarity(List<AnswerStep> group1, List<AnswerStep> group2)
        {
            // parameters----------------
            // dynamic programming matching
            double gapCost = 40.0;
            // weight of join cost. 1: without JOIN penalty, 0: with JOIN penalty same as gapCost
            double joinCostWeight = 1.0;
            // ---------------------------

            if (group1.Count == 0 && group2.Count == 0)
            {
                DPMatchingResult result = new DPMatchingResult(0.0, new List<int[]>());
                return result;
            }
            if (group1.Count == 0)
            {
                DPMatchingResult result = new DPMatchingResult(1.0 / ((double)group2.Count * gapCost), new List<int[]>());
                return result;
            }
            if (group2.Count == 0)
            {
                DPMatchingResult result = new DPMatchingResult(1.0 / ((double)group1.Count * gapCost), new List<int[]>());
                return result;
            }

            // DP matching
            double[,] m = new double[group1.Count + 1, group2.Count + 1];
            // from which direction?
            // 0-n->Match, -1->fromGRP1Skip, -2->fromGRP2Skip
            int[,] from = new int[group1.Count + 1, group2.Count + 1];
            double[,] dm = new double[group1.Count, group2.Count];

            m[0, 0] = 0.0;
            // implememtation of corner case
            // entry point for part matching
            for (int i = 1, ilen = group1.Count + 1; i < ilen; i++)
            {
                m[i, 0] = (double)i * gapCost;
                from[i, 0] = -1;
            }
            for (int i = 0, ilen = group2.Count + 1; i < ilen; i++)
            {
                m[0, i] = (double)i * gapCost;
                from[0, i] = -2;
            }
            from[0, 0] = 0;

            for (int i = 1, ilen = group1.Count + 1; i < ilen; i++)
            {
                for (int k = 1, klen = group2.Count + 1; k < klen; k++)
                {
                    double distance_match = double.MaxValue;

                    if (from[i - 1, k - 1] >= 0)
                    {
                        // cannot join
                        distance_match = CalcAnswerStepDistance(group1[i - 1], group2[k - 1]).Distance;
                        from[i, k] = 0;
                    }
                    else if (from[i - 1, k - 1] == -1)
                    {
                        // can join at group 1
                        for (int n = 0; from[i - 1 - n, k - 1] == -1; n++)
                        {
                            // n-> number of joins
                            AnswerStep join_group = new AnswerStep(i - 1, group1[i - 1].Strokes);
                            for (int o = 1; o <= n; o++)
                            {
                                join_group.JoinStrokes(group1[i - 1 - o]);
                            }
                            double distance_tmp = CalcAnswerStepDistance(join_group, group2[k - 1]).Distance;
                            if (distance_tmp < distance_match)
                            {
                                distance_match = distance_tmp;
                                from[i, k] = n;
                            }
                        }
                    }
                    else if (from[i - 1, k - 1] == -2)
                    {
                        // can join at group 2
                        for (int n = 0; from[i - 1, k - 1 - n] == -2; n++)
                        {
                            // n-> number of joins
                            AnswerStep join_group = new AnswerStep(k - 1, group2[k - 1].Strokes);
                            for (int o = 1; o <= n; o++)
                            {
                                join_group.JoinStrokes(group2[k - 1 - o]);
                            }
                            double distance_tmp = CalcAnswerStepDistance(group1[i - 1], join_group).Distance;
                            if (distance_tmp < distance_match)
                            {
                                distance_match = distance_tmp;
                                from[i, k] = n;
                            }
                        }
                    }
                    dm[i - 1, k - 1] = distance_match;
                    //Console.WriteLine("AnswerStep Distance: " + distance_match.ToString());

                    double from_grp1 = m[i - 1, k] + gapCost;
                    double from_grp2 = m[i, k - 1] + gapCost;
                    double from_match = m[i - 1, k - 1] + distance_match - ((double)from[i, k] * gapCost * joinCostWeight);
                    if (1 < i && 1 < k)
                    {
                        // can match, skip
                        ;
                    }
                    else if (i == 1 && k == 1)
                    {
                        // can only match
                        from_grp1 = double.MaxValue;
                        from_grp2 = double.MaxValue;
                    }
                    else if (k == 1)
                    {
                        // can match and group 1 skip
                        from_grp2 = double.MaxValue;
                    }
                    else if (i == 1)
                    {
                        // can match and group 2 skip
                        from_grp1 = double.MaxValue;
                    }

                    if (from_match <= from_grp1 && from_match <= from_grp2)
                    {
                        m[i, k] = from_match;
                    }
                    else if (from_grp1 <= from_grp2 && from_grp1 <= from_match)
                    {
                        from[i, k] = -1;
                        m[i, k] = from_grp1;
                    }
                    else if (from_grp2 <= from_grp1 && from_grp2 <= from_match)
                    {
                        from[i, k] = -2;
                        m[i, k] = from_grp2;
                    }
                }
            }

            // generate matching list
            // {group1Pos, group2Pos, distance, joinCount}
            List<int[]> matchingList = new List<int[]>();
            int g1pos = group1.Count - 1;
            int g2pos = group2.Count - 1;
            //int matchCnt = 0;
            for (int i = group1.Count, k = group2.Count; i != 0 || k != 0; )
            {
                int[] match = null;
                switch (from[i, k])
                {
                    case -1:
                        match = new int[4] { g1pos, -1, -1, 0 };
                        i--;
                        g1pos--;
                        break;
                    case -2:
                        match = new int[4] { -1, g2pos, -1, 0 };
                        k--;
                        g2pos--;
                        break;
                    default:
                        // match
                        match = new int[4] { g1pos, g2pos, (int)(dm[g1pos, g2pos] * 100.0), from[i,k] };
                        i--;
                        k--;
                        g1pos--;
                        g2pos--;
                        //matchCnt++;
                        break;
                }
                matchingList.Add(match);
            }
            matchingList.Reverse();

            //DPMatchingResult res = new DPMatchingResult(m[group1.Count, group2.Count] / (group1.Count + group2.Count + 1.0), matchingList);
            DPMatchingResult res = new DPMatchingResult(m[group1.Count, group2.Count] / (Math.Min(group1.Count, group2.Count) + 1.0), matchingList);
            return res;
        }
示例#4
0
        /// <summary>
        /// Join an answer step after the answer step
        /// </summary>
        /// <param name="step"></param>
        public void JoinStrokes(AnswerStep step)
        {
            // Calculate offset value for coordinate transformation
            Rect orgBounds = this.GetBounds();
            Rect addBounds = step.GetBounds();
            Point endPoint = new Point(orgBounds.Left, (orgBounds.Top + orgBounds.Bottom) / 2.0);
            Point startPoint = new Point(addBounds.Left, (addBounds.Top + addBounds.Bottom) / 2.0);
            Point offsetPoint = new Point(endPoint.X - startPoint.X, endPoint.Y - startPoint.Y);

            // transform coordinates to join anter the origin strokes
            foreach (AnalysisPenStroke addStroke in step.Strokes)
            {
                AnalysisPenStroke s = new AnalysisPenStroke();
                foreach (AnalysisPenPoint addPoint in addStroke.Points)
                {
                    AnalysisPenPoint p = new AnalysisPenPoint(addPoint.Time, addPoint.X + offsetPoint.X, addPoint.Y + offsetPoint.Y);
                    s.Points.Add(p);
                }
                this.Strokes.Add(s);
            }
        }