Beispiel #1
0
        /// <summary>
        /// Create a sprite that follows a Bezier path.
        ///
        /// Path format is (similar to svg) (g=guidepoint):
        /// g1p1x,g1p1y[space]g2p1x,g2p1y[space]p1x,p1y
        /// g1p2x,g1p2y[space]g2p2x,g2p2y[space]p2x,p2y
        /// etc...
        /// So it's 2 guidepoints followed by end point.
        /// This will follow simple Bezier paths created using GIMP and exported as SVG.
        /// </summary>
        /// <param name="p_startPoint">Point where sprite begins</param>
        /// <param name="p_path">String of points sprite should follow</param>
        /// <param name="p_ratio">double value indicating stepping size as a fraction of distance along path to take with each step</param>
        public BezierSprite(Point p_startPoint, string p_path, double p_ratio)
        {
            startPoint = p_startPoint;

            points = new List <Point>();
            points.Add(p_startPoint);
            points.AddRange(BezierSprite.PointsStringToList(p_path));

            if ((points.Count - 1) % 3 != 0)
            {
                throw new ArgumentException("The number of points in the path must be divisible by 3");
            }

            segmentIndex = 0;
            segmentCount = (points.Count - 1) / 3;

            lengthRatios = new List <LengthRatio>(segmentCount);
            for (int i = 0; i < segmentCount; i++)
            {
                lengthRatios.Add(new LengthRatio());
            }
            FillInSegmentLengths();
            FillInEndTangents();
            loopCount      = 0;
            remainingLoops = 1;
            traversalTime  = 0.0d;
            speed          = 0;
            completed      = false;
            ratioStep      = lengthRatios[0].ratio;
        }
Beispiel #2
0
        /// <summary>
        /// Trace the path quickly through to determine its length
        /// </summary>
        /// <param name="p_points">
        /// A <see cref="Point"/> array of 4 points specifying Bezier curve
        /// </param>
        /// <returns>
        /// A <see cref="System.Int32"/> as the length of the given curve
        /// </returns>
        static public int CalculateCurveLength(Point [] p_points)
        {
            if (p_points.Length != 4)
            {
                throw new Exception("p_points array passed to CalculateCurveLength must have exactly 4 items, this one has " + p_points.Length);
            }

            PointF pA  = p_points[0];
            double rtn = 0.0d;

            // determine what ratio gives up about 1 pixel of distance
            double accuracyRatio = BezierSprite.CalculateRatioSizeToPixelAccuracy(p_points, 1);

            // set an incrementer
            double currentRatio = accuracyRatio;

            // set a limit for when we reach the end of the path - allow some overshoot
            double topLimit = 1.0 + accuracyRatio;

            // traverse path one fraction bit at a time calculating distances between points
            while (currentRatio < topLimit)
            {
                PointF pB = CalculatePoint(p_points, currentRatio);
                rtn          += Math.Sqrt(Math.Pow(pA.X - pB.X, 2) + Math.Pow(pA.Y - pB.Y, 2));
                pA            = pB;
                currentRatio += accuracyRatio;
            }

            return((int)Math.Round(rtn));
        }
Beispiel #3
0
        /// <summary>
        /// Determine what ratio amounts to a given pixel distance
        /// </summary>
        /// <param name="p_points">
        /// A <see cref="Point"/> array of 4 points specifying the Bezier curve to analyze
        /// </param>
        /// <param name="p_pixels">
        /// A <see cref="System.Int32"/> indicating how many pixels the returned ratio should indicate
        /// </param>
        /// <returns>
        /// A <see cref="System.Double"/> representing the ratio on the curve that amounts to p_pixels distance
        /// </returns>
        static public double CalculateRatioSizeToPixelAccuracy(Point [] p_points, int p_pixels)
        {
            if (p_points.Length != 4)
            {
                throw new Exception("p_points array passed to GetRatioSizeToPixelAccuracy must have exactly 4 items, this one has " + p_points.Length);
            }

            double testRatio = 1.0d;
            PointF prevPoint = BezierSprite.CalculatePoint(p_points, 0.0d);

            // first cut test ratio in half repeatedly until we fall below the requested
            // pixel threshold, then increment the ratio back up by .01 until we hit the
            // threshold again.
            while (true)
            {
                testRatio /= 2.0d;
                PointF nextPoint = BezierSprite.CalculatePoint(p_points, testRatio);

                if (BezierSprite.Hypot(prevPoint, nextPoint) < p_pixels)
                {
                    while (true)
                    {
                        testRatio += 0.01d;
                        PointF nextPoint2 = BezierSprite.CalculatePoint(p_points, testRatio);
                        if (BezierSprite.Hypot(prevPoint, nextPoint2) > p_pixels)
                        {
                            break;
                        }
                    }
                    break;
                }
            }

            return(testRatio);
        }
Beispiel #4
0
        /// <summary>
        /// Returns the sprites next position on the path using the following formula:
        ///
        /// Bx(t) = (1-t)^3  x P1x + 3 x (1-t)^2 x t x P2x + 3 x (1-t) x t^2 x P3x + t^3 x P4x
        /// By(t) = (1-t)^3  x P1y + 3 x (1-t)^2 x t x P2y + 3 x (1-t) x t^2 x P3y + t^3 x P4y
        ///
        /// http://upload.wikimedia.org/math/2/d/5/2d5e5d58562d8ec2c35f16df98d2b974.png
        /// B(t)=(1-t)^2)*p0 + 2(1-t)*t*p1 + t^2(p2)
        /// quadratic: http://upload.wikimedia.org/wikipedia/commons/b/bf/Bezier_2_big.png
        /// cubic: http://upload.wikimedia.org/wikipedia/commons/c/c1/Bezier_3_big.png
        /// </summary>
        /// <returns>Point indicating next position of sprite</returns>
        protected override Point GetNewPosition()
        {
            currentPoint =
                BezierSprite.CalculatePoint(
                    points.GetRange(segmentIndex * 3, 4).ToArray(),
                    ratio
                    );

            // if approaching end of current curve, then get pre-calculated angle
            if ((1.0d - ratio) < ratioStep)
            {
                tangentAngle = lengthRatios[segmentIndex].endTangent;
            }
            else
            if (ratio < ratioStep)             // if just past the start of next curve, then use pre-calculated angle
            {
                tangentAngle = lengthRatios[segmentIndex].startTangent;
            }
            else             // else get angle along the curve based on ratio
            {
                tangentAngle = CalculateTangentAngle(points.GetRange(segmentIndex * 3, 4).ToArray(), ratio, currentPoint);
            }
//Console.WriteLine("tangentAngle={0}, seg={1}, ratio={2}", tangentAngle, segmentIndex, ratio);
            ratio = (double)(ratio + ratioStep);

            if (ratio > 1.0d)
            {
                ratio -= 1.0d;
                segmentIndex++;
                if (segmentIndex >= segmentCount)
                {
                    if (remainingLoops != -1)
                    {
                        if (--remainingLoops > 0)
                        {
                            segmentIndex = 0;
                        }
                        else
                        {
                            completed = true;
                        }
                    }
                    else
                    {
                        segmentIndex = 0;
                    }

                    loopCount++;
                }
                else
                {
                    ratioStep = lengthRatios[segmentIndex].ratio;
                }
            }

            return(new Point((int)Math.Round(currentPoint.X), (int)Math.Round(currentPoint.Y)));
        }
Beispiel #5
0
        /// <summary>
        /// Determine and store the length of each curve segment along the whole path
        ///
        /// Assumption:
        ///     points has 3*n+1 points in it.
        ///
        /// </summary>
        private void FillInSegmentLengths()
        {
            if (lengthRatios == null)
            {
                throw new Exception("lengthRatios must be defined assigned before calling FillInEndTangents");
            }

            pathLength = 0;

            for (int i = 0; i < segmentCount; i++)
            {
                LengthRatio lr = new LengthRatio();

                lr.length =
                    BezierSprite.CalculateCurveLength(
                        points.GetRange(i * 3, 4).ToArray()                         // there are 4 points to each segment. * 3 because we start each next segment with the end point of the previous segment
                        );

                lr.ratio        = defaultRatio;          // default value since we haven't calc'd ratios yet
                lengthRatios[i] = lr;

                pathLength += lr.length;                 // calc length of whole path
            }
        }