/// <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); }
/// <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))); }