I've 5 Path: circlePath, ballpath, radiusPath, sinePath, cosinePath. First one doesn't have any animation and the rest 4 have 5 animations. Right now, it looks like this:

I want those three LineSegment to make right triangle always. cosinePath has one PointAnimationUsingKeyFrames for EndPoint and sinePath has two, one for StartPoint and the other for EndPoint. ballpath moves with a MatrixAnimationUsingPath and the radiusPath with DoubleAnimation. Here's the code for the custom control:
class Trig : FrameworkElement
{
VisualCollection children;
Path circlePath, ballpath, radiusPath, sinePath, cosinePath;
EllipseGeometry circle, ball;
LineGeometry cosine, sine;
double cx, cy, radius, padding;
public Trig() {
padding = 20;
circle = new EllipseGeometry();
sine = new LineGeometry();
cosine = new LineGeometry();
ball = new EllipseGeometry() { Center = new Point(0, 0), RadiusX = 5, RadiusY = 5 };
circlePath = new Path() { Stroke = Brushes.LightBlue, StrokeThickness = 3, Data = circle };
sinePath = new Path() { Stroke = Brushes.Blue, StrokeThickness = 1, Data = sine };
cosinePath = new Path() { Stroke = Brushes.Red, StrokeThickness = 1, Data = cosine };
ballpath = new Path() { Fill = Brushes.LightCoral, Data = ball, RenderTransform = new MatrixTransform() };
radiusPath = new Path() {
Stroke = Brushes.Black,
StrokeThickness = 0.5,
StrokeDashCap = PenLineCap.Round,
StrokeDashArray = new DoubleCollection(new double[] { 5,5}),
Data = new LineGeometry()
};
children = new VisualCollection(this) { circlePath, sinePath, cosinePath, ballpath, radiusPath };
Loaded += animate;
}
void animate(object sender, RoutedEventArgs e) {
radiusPath.RenderTransform = new RotateTransform(0, cx, cy);
var ballAnim = new MatrixAnimationUsingPath() {
Duration = TimeSpan.FromSeconds(10),
RepeatBehavior = RepeatBehavior.Forever,
PathGeometry = PathGeometry.CreateFromGeometry(circle)
};
var radiusAnim = new DoubleAnimation() {
To = 360,
Duration = TimeSpan.FromSeconds(10),
RepeatBehavior = RepeatBehavior.Forever
};
var cosineAnim = new PointAnimationUsingKeyFrames() {
KeyFrames = {
new LinearPointKeyFrame(new Point(cx + radius, cy), TimeSpan.FromSeconds(0)),
new LinearPointKeyFrame(new Point(cx, cy), TimeSpan.FromSeconds(2.5)),
new LinearPointKeyFrame(new Point(cx - radius, cy), TimeSpan.FromSeconds(5)),
},
RepeatBehavior = RepeatBehavior.Forever,
AutoReverse = true
};
var sineStartAnim = new PointAnimationUsingKeyFrames() {
KeyFrames = {
new LinearPointKeyFrame(new Point(cx + radius, cy), TimeSpan.FromSeconds(0)),
new LinearPointKeyFrame(new Point(cx, cy), TimeSpan.FromSeconds(2.5)),
new LinearPointKeyFrame(new Point(cx - radius, cy), TimeSpan.FromSeconds(5)),
},
RepeatBehavior = RepeatBehavior.Forever,
AutoReverse = true
};
var sineEndAnim = new PointAnimationUsingKeyFrames() {
KeyFrames = {
new LinearPointKeyFrame(new Point(cx + radius, cy), TimeSpan.FromSeconds(0)),
new LinearPointKeyFrame(new Point(cx, cy + radius), TimeSpan.FromSeconds(2.5)),
new LinearPointKeyFrame(new Point(cx - radius, cy), TimeSpan.FromSeconds(5)),
new LinearPointKeyFrame(new Point(cx, cy - radius), TimeSpan.FromSeconds(7.5)),
new LinearPointKeyFrame(new Point(cx + radius, cy), TimeSpan.FromSeconds(10))
},
RepeatBehavior = RepeatBehavior.Forever
};
radiusPath.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, radiusAnim);
ballpath.RenderTransform.BeginAnimation(MatrixTransform.MatrixProperty, ballAnim);
cosine.BeginAnimation(LineGeometry.EndPointProperty, cosineAnim);
sine.BeginAnimation(LineGeometry.EndPointProperty, sineEndAnim);
sine.BeginAnimation(LineGeometry.StartPointProperty, sineStartAnim);
}
protected override Size MeasureOverride(Size availableSize) {
cx = availableSize.Width / 2;
cy = availableSize.Height / 2;
circle.Center = new Point(cx, cy);
radius = Math.Min((availableSize.Width - 2 * padding) / 2, (availableSize.Height - 2 * padding) / 2);
circle.RadiusX = circle.RadiusY = radius;
((LineGeometry)radiusPath.Data).StartPoint = new Point(cx, cy);
((LineGeometry)radiusPath.Data).EndPoint = new Point(cx + radius, cy);
cosine.StartPoint = new Point(cx, cy);
cosine.EndPoint = new Point(cx + radius, cy);
sine.StartPoint = sine.EndPoint = new Point(cx + radius, cy);
foreach (UIElement child in children)
child.Measure(availableSize);
return availableSize;
}
protected override Size ArrangeOverride(Size finalSize) {
foreach (UIElement child in children)
child.Arrange(new Rect(child.DesiredSize));
return finalSize;
}
protected override Visual GetVisualChild(int index) => children[index];
protected override int VisualChildrenCount => children.Count;
}
and in MainWindow.xaml, I've used that like this:
<Grid Margin="20">
<cc:Trig />
</Grid>
Is there any easier way to do this type of animations?
EDIT
If I replace sineEndAnim with this
var sineEndAnim = new PointAnimationUsingPath() {
PathGeometry = PathGeometry.CreateFromGeometry(circle),
Duration = TimeSpan.FromSeconds(10),
RepeatBehavior = RepeatBehavior.Forever
};
the StartPoint move along the ball. Now the EndPoint of cosine and StarPoint of sine is the problem!

