/// <summary>
        /// apply grid box to all bones in this line
        /// </summary>
        /// <param name="gridW"></param>
        /// <param name="gridH"></param>
        public void ApplyGridBox(List <BoneGroup> boneGroups, int gridW, int gridH)
        {
            //1. apply grid box to each joint
            for (int i = _joints.Count - 1; i >= 0; --i)
            {
                _joints[i].AdjustFitXY(gridW, gridH);
            }
            //2. (re) calculate slope for all bones.
            for (int i = bones.Count - 1; i >= 0; --i)
            {
                bones[i].EvaluateSlope();
            }
            //3. re-grouping
            int       j         = bones.Count;
            BoneGroup boneGroup = new BoneGroup(this); //new group

            boneGroup.slopeKind = LineSlopeKind.Other;
            //
            float approxLen = 0;
            float ypos_sum  = 0;
            float xpos_sum  = 0;

            for (int i = 0; i < j; ++i)
            {
                GlyphBone     bone    = bones[i];
                LineSlopeKind slope   = bone.SlopeKind;
                Vector2       mid_pos = bone.GetMidPoint();

                if (slope != boneGroup.slopeKind)
                {
                    //add existing to list and create a new group
                    if (boneGroup.count > 0)
                    {
                        //add existing bone group to bone-group list
                        boneGroup.approxLength = approxLen;
                        boneGroup.avg_x        = xpos_sum / boneGroup.count;
                        boneGroup.avg_y        = ypos_sum / boneGroup.count;
                        //
                        boneGroups.Add(boneGroup);
                    }
                    //
                    boneGroup            = new BoneGroup(this);
                    boneGroup.startIndex = i;
                    boneGroup.slopeKind  = slope;
                    //
                    boneGroup.count++;
                    approxLen = bone.EvaluateFitLength(); //reset
                    //
                    xpos_sum = mid_pos.X;
                    ypos_sum = mid_pos.Y;
                }
                else
                {
                    boneGroup.count++;
                    approxLen += bone.EvaluateFitLength(); //append
                    //
                    xpos_sum += mid_pos.X;
                    ypos_sum += mid_pos.Y;
                }
            }
            //
            if (boneGroup.count > 0)
            {
                boneGroup.approxLength = approxLen;
                boneGroup.avg_x        = xpos_sum / boneGroup.count;
                boneGroup.avg_y        = ypos_sum / boneGroup.count;
                boneGroups.Add(boneGroup);
            }
        }
        public void Analyze()
        {
            //check if q is upper or lower when compare with p
            //check if q is on left side or right side of p
            //then we know the direction

            //p
            double x0 = p.CentroidX;
            double y0 = p.CentroidY;
            //q
            double x1 = q.CentroidX;
            double y1 = q.CentroidY;

            if (x1 == x0)
            {
                this.SlopKind = LineSlopeKind.Vertical;
                SlopAngle     = 1;
            }
            else
            {
                SlopAngle = Math.Abs(Math.Atan2(Math.Abs(y1 - y0), Math.Abs(x1 - x0)));
                if (SlopAngle > _85degreeToRad)
                {
                    SlopKind = LineSlopeKind.Vertical;
                }
                else if (SlopAngle < _15degreeToRad)
                {
                    SlopKind = LineSlopeKind.Horizontal;
                }
                else
                {
                    SlopKind = LineSlopeKind.Other;
                }
            }
            //--------------------------------------

            int  p_outsideCount = OutSideCount(p);
            int  q_outsideCount = OutSideCount(q);
            bool p_isTip        = false;
            bool q_isTip        = false;

            if (p_outsideCount >= 2)
            {
                //tip bone
                p_isTip = true;
            }
            if (q_outsideCount >= 2)
            {
                //tipbone
                q_isTip = true;
            }
            //--------------------------------------
            //p_isTip && q_isTip is possible eg. dot or dot of  i etc.
            //--------------------------------------
            //find matching side

            if (p.e0.IsOutside)
            {
                //find matching side on q
                MarkMatchingEdge(p.e0, q);
            }
            if (p.e1.IsOutside)
            {
                //find matching side on q
                MarkMatchingEdge(p.e1, q);
            }
            if (p.e2.IsOutside)
            {
                //find matching side on q
                MarkMatchingEdge(p.e2, q);
            }


            if (q.e0.IsOutside)
            {
                //find matching side on q
                MarkMatchingEdge(q.e0, p);
            }
            if (q.e1.IsOutside)
            {
                //find matching side on q
                MarkMatchingEdge(q.e1, p);
            }
            if (q.e2.IsOutside)
            {
                //find matching side on q
                MarkMatchingEdge(q.e2, p);
            }
        }