Răspuns MatrixTransform an Image

  • 24 aprilie 2012 12:15
     
     

    I have a BitmapImage. Within that image, I have 2 reference points, R1 and R2. I also know the width and height.

    On my DrawingContext, I have 2 destination points, D1 and D2. I want to draw my image so that R1 and R2 match D1 and D2 and the image gets squeezed/expanded and rotated as needed.

    I guess I would do dc.PushTransform() with MatrixTransform before doing dc.DrawImage()... Any ideas how? Thanks.


    DNS for Windows Azure dnsazure.com

Toate mesajele

  • 24 aprilie 2012 13:15
    Moderator
     
     Răspuns Are cod

    "I guess I would do dc.PushTransform() with MatrixTransform"

    Well yes but this is probably easier to understand if you use a transform group instead of building a MatrixTransform directly. Here's an example:

                TransformGroup transform = new TransformGroup();
    
                Point r1 = new Point(0, image.Height / 2.0);
                Point r2 = new Point(image.Width, image.Height / 2.0);
    
                Point d1 = new Point(20, 150); // some random D1/D2 coords
                Point d2 = new Point(80, 70);
    
                Vector v1 = r2 - r1;
                Vector v2 = d2 - d1;
    
                transform.Children.Add(new TranslateTransform(-r1.X, -r1.Y)); // change the origin to R1
                transform.Children.Add(new ScaleTransform(v2.Length / v1.Length, 1.0)); // scale along X axis
                transform.Children.Add(new RotateTransform(Vector.AngleBetween(v1, v2))); // rotate to match the direction of D1-D2
                transform.Children.Add(new TranslateTransform(d1.X, d1.Y)); // finally move the image to D1
    
                dc.PushTransform(transform);
                dc.DrawImage(image, new Rect(0, 0, image.Width, image.Height));
                dc.Pop();

    Note that scaling is done only along the X axis, it's not possible to scale along Y without additional points.
  • 24 aprilie 2012 13:44
     
     
    Wow, thanks, you made my day.

    DNS for Windows Azure dnsazure.com

  • 29 aprilie 2012 08:42
     
     

    Thanks Mike this really helps. Can I have a follow up question?

    Question 1: Instead of having R1 and R2 as reference points, but having R'1 and R'2. Should I use translate R'1 to R1 first, then translate back D'1 to D1?

    Question 2: If I have an additional point (R'3), may I know how can I do the Scale along Y also?

    (R'1 R'2 and R'3 are some points inside the image instead of on the edge of the image)

  • 30 aprilie 2012 03:01
     
      Are cod

    I have a known BitmapImage (width=400, height=100). Inside this image, I have 2 known reference points, R'1 (100,50) and R'2 (300,50). Also, I know R0 (0,0).

    (R'1 R'2 and R'3 are some points inside the image instead of on the edge of the image)

    On my DrawingContext, I have 2 destinations points, D'1 (300,300) and D'2 (400,400). I want to draw my image so that R'1 and R'2 match D'1 and D'2 and the image gets squeezed/expanded and rotated as needed. However, I don't know D0.

    I tried the code suggested by Mike. However, the image doesn't render correctly

    http://social.msdn.microsoft.com/Forums/en/wpf/thread/fffe1f6b-70c9-4ae8-9690-aad171fecef1

    TransformGroup transform = new TransformGroup(); //Point r1 = new Point(0, image.Height / 2.0); //Point r2 = new Point(image.Width, image.Height / 2.0); Point r1 = new Point(100, 50); Point r2 = new Point(300, 50); Point d1 = new Point(300, 300); Point d2 = new Point(400, 400); Vector v1 = r2 - r1; Vector v2 = d2 - d1;

    transform.Children.Add(new TranslateTransform(-r1.X, -r1.Y)); // change the origin to R1 transform.Children.Add(new ScaleTransform(v2.Length / v1.Length, 1.0)); // scale along X axis transform.Children.Add(new RotateTransform(Vector.AngleBetween(v1, v2))); // rotate to match the direction of D1-D2 transform.Children.Add(new TranslateTransform(d1.X, d1.Y)); // finally move the image to D1 dc.PushTransform(transform); dc.DrawImage(image, new Rect(0, 0, // d0 image.Width, image.Height)); // this doesn't render correctly

    dc.DrawEllipse(new SolidColorBrush(Color.FromArgb(255, 0, 0, 0)), null, d1, thickness, thickness); // this renders correctly
    dc.DrawEllipse(new SolidColorBrush(Color.FromArgb(255, 0, 0, 0)), null, d2, thickness, thickness); // this renders correctly

    dc.Pop();

    Questions 2: If I have an additional point (R'3) (200,10) , may I know how can I do the Scale along Y also?

    Questions 3: Which topic/book should I look into if I want to know more about this regarding do some AR using WPF? (DirectX, XNA ??)

    Thank you very much.

  • 30 aprilie 2012 07:46
    Moderator
     
      Are cod

    1: nope, you don't need an additional translation.

    2: for this we can use the distance between the R3/D3 points and the R1R2/D1D2 lines do determine the y scaling factor. Here's an update for the relevant part of the code:

                Point r1 = new Point(51, 28);
                Point r2 = new Point(177, 28);
                Point r3 = new Point(111, 7);
    
                Point d1 = new Point(335, 118);
                Point d2 = new Point(395, 60);
                Point d3 = new Point(345, 75);
    
                Vector vr21 = r2 - r1;
                Vector vd21 = d2 - d1;
    
                Vector vr31 = r3 - r1;
                Vector vd31 = d3 - d1;
    
                double y1 = Vector.CrossProduct(vr31, vr21) / vr21.Length;
                double y2 = Vector.CrossProduct(vd31, vd21) / vd21.Length;
    
                transform.Children.Add(new TranslateTransform(-r1.X, -r1.Y));
                transform.Children.Add(new ScaleTransform(vd21.Length / vr21.Length, y2 / y1));
                transform.Children.Add(new RotateTransform(Vector.AngleBetween(vr21, vd21)));
                transform.Children.Add(new TranslateTransform(d1.X, d1.Y));
    The use of vector cross product may need some explanation: in 2D the cross product of 2 vectors v1 and v2 happens to be equal to sin(t) * v1.Length * v2.Length. If you build a right triangle by projecting the point D3 on the line D1D2 you get that the point-line distance is sin(t) * D3D1.Length where t is the angle between D3D1 and D2D1. In the end the point-line distance can be expressed as cross(D3D1, D2D1) / D2D1.Length = sin(t) * D3D1.Length * D2D1.Length / D2D1.Length = sin(t) * D3D1.Length.
  • 4 mai 2012 09:52
     
     

    Maybe you can go back to consult Mike Dones to get a quick responce due to this is the result of you guys talking, right?