Exemplo n.º 1
0
        /**
         * Computes the range of substrokes to use when computing matches based on looseness.
         * When matching, sub strokes are matched up with one another to find the best
         * matching.  But if two substrokes are +/-  beyond this range, then the comparison
         * is short-circuited for some computation savings.
         *
         * @param subStrokeCount the substroke count of the input character
         * @param looseness the looseness, 0-1
         * @return the range
         */
        private int getSubStrokesRange(int subStrokeCount)
        {
            // Return the maximum if looseness = 1.0.
            // Otherwise we'd have to ensure that the floating point value led to exactly the right int count.
            if (looseness == 1.0)
            {
                return CharacterDescriptor.MAX_CHARACTER_SUB_STROKE_COUNT;
            }

            // We use a CubicCurve that grows slowly at first and then rapidly near the end to the maximum.

            double y0 = subStrokeCount * 0.25;

            double ctrl1X = 0.4;
            double ctrl1Y = 1.5 * y0;

            double ctrl2X = 0.75;
            double ctrl2Y = 1.5 * ctrl1Y;

            double[] solutions = new double[1];
            CubicCurve2D curve = new CubicCurve2D(0, y0, ctrl1X, ctrl1Y, ctrl2X, ctrl2Y, 1, CharacterDescriptor.MAX_CHARACTER_SUB_STROKE_COUNT);
            //CurveUtils.solveCubicCurveForX(curve, looseness, solutions);
            //double t = solutions[0];
            double t = curve.GetFirstSolutionForX(looseness);

            // We get the t value on the parametrized curve where the x value matches the looseness.
            // Then we compute the y value for that t.  This gives the range.

            //return (int)Math.Round(CurveUtils.getPointOnCubicCurve(curve, t).getY());
            return (int)Math.Round(curve.GetYOnCurve(t));
        }
Exemplo n.º 2
0
 /**
  * Builds a precomputed array of values to use when getting the score between two substroke lengths.
  * A ratio less than one is computed for the two lengths, and the score should be the ratio * score table's length.
  *
  * @return the length score table
  */
 private static double[] initLengthScoreTable()
 {
     // Curve grows rapidly as the ratio grows and levels off quickly.
     // This is because we don't really expect lengths to vary a lot.
     // We are really just trying to distinguish between tiny strokes and long strokes.
     CubicCurve2D curve = new CubicCurve2D(0, 0, 0.25, 1.0, 0.75, 1.0, 1.0, 1.0);
     return initCubicCurveScoreTable(curve, 100);
 }
Exemplo n.º 3
0
        /**
         * Computes a range of strokes to use based on the given looseness.
         * Only characters whose number of strokes are within the input number of strokes
         * +/- this range will be considered during comparison.  This helps cut down
         * on matching cost.
         *
         * @param strokeCount the number of input strokes
         * @param looseness the looseness, 0-1
         * @return the range
         */
        private int getStrokesRange(int strokeCount)
        {
            // Just return some extreme values if at minimum or maximum.
            // Helps to avoid possible floating point issues when near the extremes.
            if (looseness == 0.0)
            {
                return 0;
            }
            else if (looseness == 1.0)
            {
                return CharacterDescriptor.MAX_CHARACTER_STROKE_COUNT;
            }

            // We use a CubicCurve that grows slowly at first and then rapidly near the end to the maximum.
            // This is so a looseness at or near 1.0 will return a range that will consider all characters.

            double ctrl1X = 0.35;
            double ctrl1Y = strokeCount * 0.4;

            double ctrl2X = 0.6;
            double ctrl2Y = strokeCount;

            double[] solutions = new double[1];
            CubicCurve2D curve = new CubicCurve2D(0, 0, ctrl1X, ctrl1Y, ctrl2X, ctrl2Y, 1, CharacterDescriptor.MAX_CHARACTER_STROKE_COUNT);
            //CurveUtils.solveCubicCurveForX(curve, looseness, solutions);
            //double t = solutions[0];
            double t = curve.GetFirstSolutionForX(looseness);

            // We get the t value on the parametrized curve where the x value matches the looseness.
            // Then we compute the y value for that t.  This gives the range.

            //return (int)Math.Round(CurveUtils.getPointOnCubicCurve(curve, t).getY());
            return (int)Math.Round(curve.GetYOnCurve(t));
        }
Exemplo n.º 4
0
 /**
  * Builds a precomputed array of values to use when getting the score between two substroke directions.
  * Two directions should differ by 0 - Pi, and the score should be the (difference / Pi) * score table's length
  *
  * @return the direction score table
  */
 private static double[] initDirectionScoreTable()
 {
     // The curve drops as the difference grows, but rises again some at the end because
     // a stroke that is 180 degrees from the expected direction maybe OK passable.
     CubicCurve2D curve = new CubicCurve2D(0, 1.0, 0.5, 1.0, 0.25, -2.0, 1.0, 1.0);
     return initCubicCurveScoreTable(curve, 100);
 }
Exemplo n.º 5
0
        /**
         * A common helper method to initialize a table of precomputed score values using a cubic curve
         * @param curve the curve
         * @param numSamples the number of samples to compute
         * @return the score array table
         */
        private static double[] initCubicCurveScoreTable(CubicCurve2D curve, int numSamples)
        {
            double x1 = curve.X1;
            double x2 = curve.X2;

            double range = x2 - x1;

            double x = x1;
            double xInc = range / numSamples;  // even incrementer to increment x value by when sampling across the curve

            double[] scoreTable = new double[numSamples];

            // For use to pass into with solveCubicCurve
            double[] solutions = new double[1];

            // Sample evenly across the curve and set the samples into the table.
            for (int i = 0; i < numSamples; i++)
            {
                //CurveUtils.solveCubicCurveForX(curve, Math.Min(x, x2), solutions);
                //double t = solutions[0];
                double t = curve.GetFirstSolutionForX(Math.Min(x, x2));
                //scoreTable[i] = CurveUtils.getPointOnCubicCurve(curve, t).getY();
                scoreTable[i] = curve.GetYOnCurve(t);

                x += xInc;
            }

            return scoreTable;
        }
Exemplo n.º 6
0
        private float WeightedEvalForTwoKeys(FRichCurveKey key1, FRichCurveKey key2, float inTime)
        {
            var diff     = key2.Time - key1.Time;
            var alpha    = (inTime - key1.Time) / diff;
            var p0       = key1.Value;
            var p3       = key2.Value;
            var oneThird = 1f / 3f;
            var time1    = key1.Time;
            var time2    = key2.Time;
            var x        = time2 - time1;
            var angle    = Math.Atan(key1.LeaveTangent);
            var cosAngle = Math.Cos(angle);
            var sinAngle = Math.Sin(angle);

            double leaveWeight = key1.LeaveTangentWeight;

            if (key1.TangentWeightMode is RCTWM_WeightedNone or RCTWM_WeightedArrive)
            {
                var leaveTangentNormalized = key1.LeaveTangent;
                var y = leaveTangentNormalized * x;

                leaveWeight = Math.Sqrt(x * x + y * y) * oneThird;
            }

            var key1TanX = cosAngle * leaveWeight + time1;
            var key1TanY = sinAngle * leaveWeight + key1.Value;

            angle    = Math.Atan(key2.ArriveTangent);
            cosAngle = Math.Cos(angle);
            sinAngle = Math.Cos(angle);

            double arriveWeight = key2.ArriveTangentWeight;

            if (key2.TangentWeightMode is RCTWM_WeightedNone or RCTWM_WeightedLeave)
            {
                var arriveTangentNormalized = key2.ArriveTangent;
                var y = arriveTangentNormalized * x;
                arriveWeight = Math.Sqrt(x * x + y * y) * oneThird;
            }

            var key2TanX = -cosAngle * arriveWeight + time2;
            var key2TanY = -sinAngle * arriveWeight + key2.Value;

            // Normalize the time range
            var rangeX = time2 - time1;

            var dx1 = key1TanX - time1;
            var dx2 = key2TanX - time1;

            // Normalize values
            var normalizedX1 = dx1 / rangeX;
            var normalizedX2 = dx2 / rangeX;

            var results = new double[3];

            BezierToPower(0.0, normalizedX1, normalizedX2, 1.0, out double[] coeff);
            coeff[0] -= alpha;

            var   numResults = CubicCurve2D.SolveCubic(ref coeff, ref results);
            float newInterp;

            if (numResults == 1)
            {
                newInterp = (float)results[0];
            }
            else
            {
                newInterp = float.MinValue;
                foreach (var result in results)
                {
                    if (result is >= 0.0f and <= 1.0f)
                    {
                        if (newInterp < 0.0f || result > newInterp)
                        {
                            newInterp = (float)result;
                        }
                    }
                }

                if (newInterp == float.MinValue)
                {
                    newInterp = 0f;
                }
            }

            var outVal = BezierInterp(p0, (float)key1TanY, (float)key2TanY, p3, newInterp);

            return(outVal);
        }
Exemplo n.º 7
0
 internal CubicIterator(CubicCurve2D q, AffineTransform at)
 {
     this.Cubic  = q;
     this.Affine = at;
 }
        private void Next(bool doNext)
        {
            int level;

            if (HoldIndex >= HoldEnd)
            {
                if (doNext)
                {
                    Src.Next();
                }
                if (Src.Done)
                {
                    Done_Renamed = true;
                    return;
                }
                HoldType   = Src.CurrentSegment(Hold);
                LevelIndex = 0;
                Levels[0]  = 0;
            }

            switch (HoldType)
            {
            case PathIterator_Fields.SEG_MOVETO:
            case PathIterator_Fields.SEG_LINETO:
                Curx = Hold[0];
                Cury = Hold[1];
                if (HoldType == PathIterator_Fields.SEG_MOVETO)
                {
                    Movx = Curx;
                    Movy = Cury;
                }
                HoldIndex = 0;
                HoldEnd   = 0;
                break;

            case PathIterator_Fields.SEG_CLOSE:
                Curx      = Movx;
                Cury      = Movy;
                HoldIndex = 0;
                HoldEnd   = 0;
                break;

            case PathIterator_Fields.SEG_QUADTO:
                if (HoldIndex >= HoldEnd)
                {
                    // Move the coordinates to the end of the array.
                    HoldIndex           = Hold.Length - 6;
                    HoldEnd             = Hold.Length - 2;
                    Hold[HoldIndex + 0] = Curx;
                    Hold[HoldIndex + 1] = Cury;
                    Hold[HoldIndex + 2] = Hold[0];
                    Hold[HoldIndex + 3] = Hold[1];
                    Hold[HoldIndex + 4] = Curx = Hold[2];
                    Hold[HoldIndex + 5] = Cury = Hold[3];
                }

                level = Levels[LevelIndex];
                while (level < Limit)
                {
                    if (QuadCurve2D.GetFlatnessSq(Hold, HoldIndex) < Squareflat)
                    {
                        break;
                    }

                    EnsureHoldCapacity(4);
                    QuadCurve2D.Subdivide(Hold, HoldIndex, Hold, HoldIndex - 4, Hold, HoldIndex);
                    HoldIndex -= 4;

                    // Now that we have subdivided, we have constructed
                    // two curves of one depth lower than the original
                    // curve.  One of those curves is in the place of
                    // the former curve and one of them is in the next
                    // set of held coordinate slots.  We now set both
                    // curves level values to the next higher level.
                    level++;
                    Levels[LevelIndex] = level;
                    LevelIndex++;
                    Levels[LevelIndex] = level;
                }

                // This curve segment is flat enough, or it is too deep
                // in recursion levels to try to flatten any more.  The
                // two coordinates at holdIndex+4 and holdIndex+5 now
                // contain the endpoint of the curve which can be the
                // endpoint of an approximating line segment.
                HoldIndex += 4;
                LevelIndex--;
                break;

            case PathIterator_Fields.SEG_CUBICTO:
                if (HoldIndex >= HoldEnd)
                {
                    // Move the coordinates to the end of the array.
                    HoldIndex           = Hold.Length - 8;
                    HoldEnd             = Hold.Length - 2;
                    Hold[HoldIndex + 0] = Curx;
                    Hold[HoldIndex + 1] = Cury;
                    Hold[HoldIndex + 2] = Hold[0];
                    Hold[HoldIndex + 3] = Hold[1];
                    Hold[HoldIndex + 4] = Hold[2];
                    Hold[HoldIndex + 5] = Hold[3];
                    Hold[HoldIndex + 6] = Curx = Hold[4];
                    Hold[HoldIndex + 7] = Cury = Hold[5];
                }

                level = Levels[LevelIndex];
                while (level < Limit)
                {
                    if (CubicCurve2D.GetFlatnessSq(Hold, HoldIndex) < Squareflat)
                    {
                        break;
                    }

                    EnsureHoldCapacity(6);
                    CubicCurve2D.Subdivide(Hold, HoldIndex, Hold, HoldIndex - 6, Hold, HoldIndex);
                    HoldIndex -= 6;

                    // Now that we have subdivided, we have constructed
                    // two curves of one depth lower than the original
                    // curve.  One of those curves is in the place of
                    // the former curve and one of them is in the next
                    // set of held coordinate slots.  We now set both
                    // curves level values to the next higher level.
                    level++;
                    Levels[LevelIndex] = level;
                    LevelIndex++;
                    Levels[LevelIndex] = level;
                }

                // This curve segment is flat enough, or it is too deep
                // in recursion levels to try to flatten any more.  The
                // two coordinates at holdIndex+6 and holdIndex+7 now
                // contain the endpoint of the curve which can be the
                // endpoint of an approximating line segment.
                HoldIndex += 6;
                LevelIndex--;
                break;
            }
        }