Составление последовательных вращений вокруг разных центров? - PullRequest
1 голос
/ 21 января 2010

Следующий код рисует линию, поворачивает ее на 30 градусов вокруг своего левого конца, восстанавливает ее в исходное положение, поворачивает на 30 градусов вокруг правого конца и затем повторяет несколько раз.

Как я могу упорядочить эти повороты, не восстанавливая линию в исходное положение между ними? Первый поворот (вокруг левой конечной точки) вызывает перемещение правой конечной точки; поэтому я хотел бы, чтобы следующий поворот был вокруг его новой позиции.

Чистый эффект последовательности должен заключаться в том, чтобы отрезок линии "шел" вперед.

(Обратите внимание, что этот код использует один и тот же угол снова и снова. Но мне нужно решение, которое также будет работать, если угол каждый раз будет другим.)

XAML: -

<UserControl x:Class="Rotation.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
    <Grid x:Name="LayoutRoot">
        <Canvas Width="500" Height="500">
            <Line Name="TheLine" X1="100" Y1="200" X2="200" Y2="200" Stroke="Black" StrokeThickness="5"></Line>           
        </Canvas>
    </Grid>
</UserControl>

Код: -

using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace Rotation
{
    public partial class MainPage : UserControl
    {
        double x1, y1, x2, y2;

        public MainPage()
        {
            InitializeComponent();

            for (int i = 0; i < 5; i++)
            {
                _animations.Add(() => { return rot(true, -30); });
                _animations.Add(() => { return rot(false, 30); });
            }
            _enumerator = _animations.GetEnumerator();

            x1 = TheLine.X1;
            x2 = TheLine.X2;
            y1 = TheLine.Y1;
            y2 = TheLine.Y2;

            this.Loaded += delegate(object sender, RoutedEventArgs e)
            {
                RunNextAnimation();
            };
        }

        List<Func<Storyboard>> _animations = new List<Func<Storyboard>>();
        IEnumerator<Func<Storyboard>> _enumerator;

        public void AnimationCompleted(object sender, EventArgs args)
        {
            RunNextAnimation();
        }

        void RunNextAnimation()
        {
            if (_enumerator.MoveNext())
            {
                Func<Storyboard> fn = _enumerator.Current;
                if (fn != null)
                {
                    Storyboard board = fn();
                    board.Completed += AnimationCompleted;
                    board.Begin();
                }
            }
        }


        public Storyboard rot(bool aroundLeft, double angle)
        {
            Storyboard board = new Storyboard();
            int duration = 5;


            if (true)
            {
                RotateTransform rot = new RotateTransform();

                if (aroundLeft)
                {
                    rot.CenterX = x1;
                    rot.CenterY = y1;
                }
                else
                {
                    rot.CenterX = x2;
                    rot.CenterY = y2;
                }

                TheLine.RenderTransform = rot;

                DoubleAnimation an = new DoubleAnimation();
                an.Duration = new Duration(new TimeSpan(0, 0, duration));
                an.From = 0;
                an.To = angle;

                board.Children.Add(an);
                Storyboard.SetTarget(an, TheLine);
                Storyboard.SetTargetProperty(an, new PropertyPath("(UIElement.RenderTransform).Angle"));
            }

            return board;
        }


    }
}

1 Ответ

1 голос
/ 22 января 2010

Я нашел один способ сделать это: при каждом повороте вычислять, куда будут перемещаться конечные точки линии. Затем перед началом следующего вращения переместите линию так, чтобы ее положение и угол отражали нужный центр этого вращения.

Вот код, чтобы сделать это. Вместо линии я теперь использую холст, содержащий составную фигуру, которая имеет более общее назначение.

XAML:

<UserControl x:Class="Rotation.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
    <Grid x:Name="LayoutRoot">
        <Canvas Width="500" Height="500">
            <Canvas Name="TheRect" Canvas.Left="100" Canvas.Top="100" Width="100" Height="20">
                <Rectangle Canvas.Left="0" Canvas.Top="0" Width="100" Height="20" Stroke="Black" StrokeThickness="1"></Rectangle>
                <Ellipse Width="10" Height="10" Fill="Blue" Canvas.Left="0" Canvas.Top="0" />
            </Canvas>
        </Canvas>
    </Grid>
</UserControl>

C #:

using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace Rotation
{
    public partial class MainPage : UserControl
    {
        double x1, y1, x2, y2, w;
        double lastAngle;

        public MainPage()
        {
            InitializeComponent();

            for (int i = 0; i < 10; i++)
            {
                _animations.Add(() => { return rot(true, 30); });
                _animations.Add(() => { return rot(false, -30); });
            }
            _enumerator = _animations.GetEnumerator();

            x1 = (double)TheRect.GetValue(Canvas.LeftProperty);
            y1 = (double)TheRect.GetValue(Canvas.TopProperty);
            w = (double)TheRect.GetValue(Canvas.WidthProperty);
            x2 = x1 + w;
            y2 = y1;
            lastAngle = 0.0;

            this.Loaded += delegate(object sender, RoutedEventArgs e)
            {
                RunNextAnimation();
            };
        }

        List<Func<Storyboard>> _animations = new List<Func<Storyboard>>();
        IEnumerator<Func<Storyboard>> _enumerator;

        public void AnimationCompleted(object sender, EventArgs args)
        {
            RunNextAnimation();
        }

        void RunNextAnimation()
        {
            if (_enumerator.MoveNext())
            {
                Func<Storyboard> fn = _enumerator.Current;
                if (fn != null)
                {
                    Storyboard board = fn();
                    board.Completed += AnimationCompleted;
                    board.Begin();
                }
            }
        }


        public Storyboard rot(bool aroundLeft, double angle)
        {
            Storyboard board = new Storyboard();
            int duration = 5;


            if (true)
            {
                TheRect.SetValue(Canvas.LeftProperty, aroundLeft ? x1 : x1 - w*(1 - Math.Cos(lastAngle * Math.PI / 180)));
                TheRect.SetValue(Canvas.TopProperty, aroundLeft ? y1 : y2);

                RotateTransform rot = new RotateTransform();
                rot.CenterX = aroundLeft ? 0 : w;
                rot.CenterY = aroundLeft ? 0 : 0;
                rot.Angle = aroundLeft ? lastAngle : -lastAngle;

                TheRect.RenderTransform = rot;

                DoubleAnimation an = new DoubleAnimation();
                an.Duration = new Duration(new TimeSpan(0, 0, duration));
                an.From = lastAngle;
                an.To = lastAngle + angle;

                board.Children.Add(an);
                Storyboard.SetTarget(an, TheRect);
                Storyboard.SetTargetProperty(an, new PropertyPath("(UIElement.RenderTransform).Angle"));

                // and for next time around:
                lastAngle += angle;

                if (aroundLeft)
                {
                    // rotating will move x2,y2; compute the updated values for next time
                    double x0 = x2 - x1;
                    double y0 = y2 - y1;

                    double sin = Math.Sin(angle * Math.PI / 180.0);
                    double cos = Math.Cos(angle * Math.PI / 180.0);

                    x2 = x1 + (x0 * cos) - (y0 * sin);
                    y2 = y1 + (x0 * sin) + (y0 * cos);
                }
                else
                {
                    // rotating will move x1, y1; compute the updated values for next time
                    double x0 = x1 - x2;
                    double y0 = y1 - y2;

                    double sin = Math.Sin(angle * Math.PI / 180.0);
                    double cos = Math.Cos(angle * Math.PI / 180.0);

                    x1 = x2 + (x0 * cos) - (y0 * sin);
                    y1 = y2 + (x0 * sin) + (y0 * cos);
                }

            }

            return board;
        }


    }
}
...