Search code examples
c#wpfimagexamlsystem.drawing

How to rotate and resize a PNG image by two changing coordinates?


If there is a PNG image connects two coordinates (like a rope),

coordinateA.x;
coordinateA.y;

coordinateB.x;
coordinateB.y;

The two coordinates will keep changing all the time, so the PNG image connects between two coordinates needs to resize and rotate.

Any sample code to make this easier?


Solution

  • This is my approach:

    Coordinates.cs:

    public class Coordinates : DependencyObject
    {
        public Coordinates(double x, double y)
        {
            X = x;
            Y = y;
        }
        public Coordinates()
        {
            X = 0;
            Y = 0;
        }
        //X Dependency Property
        public double X
        {
            get { return (double)GetValue(XProperty); }
            set { SetValue(XProperty, value); }
        }
        public static readonly DependencyProperty XProperty =
            DependencyProperty.Register("X", typeof(double), typeof(Coordinates), new UIPropertyMetadata(0d));
        //Y Dependency Property
        public double Y
        {
            get { return (double)GetValue(YProperty); }
            set { SetValue(YProperty, value); }
        }
        public static readonly DependencyProperty YProperty =
            DependencyProperty.Register("Y", typeof(double), typeof(Coordinates), new UIPropertyMetadata(0d));
    }
    

    Geometry.cs:

    public class ConnectorLocationConverter : IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            var start = (Coordinates)values[0];
            var end = (Coordinates)values[1];
            return new Thickness(start.X, start.Y, 0, 0);
        }
        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            return null;
        }
    }
    public class ConnectorAngleConverter : IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            var start = (Coordinates)values[0];
            var end = (Coordinates)values[1];
            //dy/dx = tan(t) => t = arcTan(dy/dx)
            double t = Math.Atan2(
                        Math.Abs(start.Y - end.Y),
                        Math.Abs(start.X - end.X)) * 180 / Math.PI;
            if (end.X <= start.X && end.Y >= start.Y) return 180 - t;
            if (end.X >= start.X && end.Y <= start.Y) return -t;
            if (end.X <= start.X && end.Y <= start.Y) return 180 + t;
            return t;
        }
        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            return null;
        }
    }
    public class ConnectorWidthConverter : IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            var start = (Coordinates)values[0];
            var end = (Coordinates)values[1];
            //get side for states
            return Math.Abs(start.X - end.X);
        }
        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            return null;
        }
    }
    public class ConnectorHeightConverter : IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            var start = (Coordinates)values[0];
            var end = (Coordinates)values[1];
            //get side for states
            return Math.Abs(start.Y - end.Y);
        }
        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            return null;
        }
    }
    

    MainWindow.xaml.cs:

        //nothing fancy here
        public MainWindow()
        {
            Start = new Coordinates();
            End = new Coordinates();
            DataContext = this;
            InitializeComponent();
        }
        //Start Dependency Property
        public Coordinates Start
        {
            get { return (Coordinates)GetValue(StartProperty); }
            set { SetValue(StartProperty, value); }
        }
        public static readonly DependencyProperty StartProperty =
            DependencyProperty.Register("Start", typeof(Coordinates), typeof(MainWindow), new UIPropertyMetadata(null));
        //End Dependency Property
        public Coordinates End
        {
            get { return (Coordinates)GetValue(EndProperty); }
            set { SetValue(EndProperty, value); }
        }
        public static readonly DependencyProperty EndProperty =
            DependencyProperty.Register("End", typeof(Coordinates), typeof(MainWindow), new UIPropertyMetadata(null));
    
        //--------------------------------------------------
        //Click causes the mouse position stick to Start/End
        //--------------------------------------------------
        bool flag = false;
        private void Canvas_MouseMove(object sender, MouseEventArgs e)
        {
            var p = e.GetPosition(this);
            if(flag) Start = new Coordinates(p.X, p.Y);
            else End = new Coordinates(p.X, p.Y);
        }
    
        private void Canvas_MouseDown(object sender, MouseButtonEventArgs e)
        {
            flag = !flag;
        }
    

    And finally MainWindow.xaml:

    <Window x:Class="WpfTest.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:local="clr-namespace:WpfTest"
            Title="MainWindow" Height="650" Width="825">
        <Window.Resources>
            <local:ConnectorLocationConverter x:Key="connectorLocationConverter"/>
            <local:ConnectorAngleConverter x:Key="connectorAngleConverter"/>
            <local:ConnectorWidthConverter x:Key="connectorWidthConverter"/>
            <local:ConnectorHeightConverter x:Key="connectorHeightConverter"/>
        </Window.Resources>
        <Canvas Background="#2000"
            MouseMove="Canvas_MouseMove" MouseDown="Canvas_MouseDown">
            <Canvas Width="0" Height="0">
                <Image Source="/WpfTest;component/im.png" Stretch="Fill">
    
                    <Image.Margin>
                        <MultiBinding Converter="{StaticResource connectorLocationConverter}">
                            <Binding Path="Start"/>
                            <Binding Path="End"/>
                        </MultiBinding>
                    </Image.Margin>
    
                    <Image.Width>
                        <MultiBinding Converter="{StaticResource connectorWidthConverter}">
                            <Binding Path="Start"/>
                            <Binding Path="End"/>
                        </MultiBinding>
                    </Image.Width>
    
                    <Image.Height>
                        <MultiBinding Converter="{StaticResource connectorHeightConverter}">
                            <Binding Path="Start"/>
                            <Binding Path="End"/>
                        </MultiBinding>
                    </Image.Height>
    
                    <Image.RenderTransform>
                        <RotateTransform CenterX="0" CenterY="0">
                            <RotateTransform.Angle>
                                <MultiBinding Converter="{StaticResource connectorAngleConverter}">
                                    <Binding Path="Start"/>
                                    <Binding Path="End"/>
                                </MultiBinding>
                            </RotateTransform.Angle>
                        </RotateTransform>
                    </Image.RenderTransform>
    
                </Image>
            </Canvas>
        </Canvas>
    </Window>