private void CalculateSamples() { var workingSegment = new CubicBezierSegment(); workingSegment.StartPoint = _segment.StartPoint; workingSegment.StartControlPoint = _segment.StartControlPoint; workingSegment.EndControlPoint = _segment.EndControlPoint; workingSegment.EndPoint = _segment.EndPoint; _length = 0.0f; // First, break the working segment down into a discrete number of segments. We only care about the approximate // length of these segments, not the curve segments themselves. CubicBezierSegment leftSegment = new CubicBezierSegment(), rightSegment = new CubicBezierSegment(); for (int i = 0; i < NumberOfSamples; ++i) { // Subdivide another part off the curve. We take 1/64 of the curve on the first loop, and keep the // remaining 63/64 of the curve. On the next loop, we want 1/63 of the curve and so on, to keep the // segment size consistent. double splitT = 1 / (double)(NumberOfSamples - i); workingSegment.Subdivide(leftSegment, rightSegment, splitT); float nodeLength = leftSegment.CalculateLength(); _length += nodeLength; SegmentSample sample = new SegmentSample(); sample.Length = nodeLength; sample.StartTime = 0.0f; sample.EndTime = 1.0f; _samples.Add(sample); // Copy the right curve segment into the working curve segment. workingSegment.StartPoint = rightSegment.StartPoint; workingSegment.StartControlPoint = rightSegment.StartControlPoint; workingSegment.EndControlPoint = rightSegment.EndControlPoint; workingSegment.EndPoint = rightSegment.EndPoint; } // Knowing what the length of the curve segments are, and a total approximate length, we can calculate the // the start/end times of each segment. These will be the values we use for our runtime lookup. float runningLength = _samples[0].Length; for (int i = 1; i < _samples.Count; ++i) { float t = (float)runningLength / _length; SegmentSample sample = _samples[i]; sample.StartTime = t; _samples[i] = sample; SegmentSample oldSample = _samples[i - 1]; oldSample.EndTime = t; _samples[i - 1] = oldSample; runningLength += sample.Length; } }
/// <summary> /// Calculates the length of a CubicBezierSegment. Note that because this uses a numerical method to get the length, /// it won't be completely accurate. /// </summary> /// <returns>The length of the bezier segment.</returns> /// <param name="segment">The segment to calculate the length of.</param> public static float CalculateLength(this CubicBezierSegment segment) { // Subdividing the segment into 4 pieces, then getting an approximate length on those 4 pieces, gives // a fairly accurate length (< 1% error), and is very quick. CubicBezierSegment l = new CubicBezierSegment(), r = new CubicBezierSegment(), ll = new CubicBezierSegment(), lr = new CubicBezierSegment(), rl = new CubicBezierSegment(), rr = new CubicBezierSegment(); segment.Subdivide(l, r, 0.5); l.Subdivide(ll, lr, 0.5); r.Subdivide(rl, rr, 0.5); return(ll.CalculateApproximateLength() + lr.CalculateApproximateLength() + rl.CalculateApproximateLength() + rr.CalculateApproximateLength()); }