I am drawing an image from MetaFile (emf) and then apply some rotation transformations to it all within the OnPaint of a UserControl. After applying those transformation how can I calculate the normal untransformed rectangular bounding box of this in screen coordinates? I need this to be able to resize the rotated image to the size of the UserControl.
protected override void OnPaint(PaintEventArgs e)
{
// rotate around the center of this UserControl
e.Graphics.TranslateTransform(this.Width / 2.0f, this.Height / 2.0f);
e.Graphics.RotateTransform(this.Rotation);
e.Graphics.TranslateTransform(this.Width / -2.0f, this.Height / -2.0f);
// TODO: now scale so the image so it fits exactly into this UserControl
// draw the image at the center of this UserControl
float left = (this.Width - ResourceManager.MyDrawingMetaFile.Width) / 2.0f;
float top = (this.Height - ResourceManager.MyDrawingMetaFile.Height) / 2.0f;
e.Graphics.DrawImage(Resources.MyDrawingMetaFile, left, top);
}
The whole idea behind this is that I want to display rotated .emf File in a UserControl and have the emf drawing allways fill the available space in the UserControl. Maybe there is a better approach?
The fillmode/stretchmode I am after is Uniform and UniformToFill (like in WPF's Viewbox). The emf should not be distorted an in Uniform mode the emf completely fills the usercontrol at least in one dimension, nothing is cropped. In UniformToFill the emf filles the UserControl in both dimensions and if the aspectratios do not match, the emf is cropped in one dimension.
If I understand you correctly - you need to figure out how the rotation affects the bounding box of your image, so that you can scale it accordingly.
Then you can do like this:
Stuff the four coordinates of your bounding box in a Point[].
Set up a Matrix with your rotation (.RotateAt)
Let the matrix transform the four points.
Sort the four transformed X coordinates and compare the width of the new bounding box (pts[3].X - pts[0].X after sort).
Now you know how to scale the width for a perfect fit.
Repeat step 4 for the height as well.
The way it would be done with GDI is :
BeginPath()
// Draw stuff
EndPath()
PathToRegion()
GetRgnBox()
GDI+ has equivalents - The GraphicsPath and Region classes can do the above
Related
I'm using C# WinForms.
The rotated polygon is drawn on a picturebox. The width and height of the rotated polygon is 101, 101. Now, I want to transfer the contents of rotated polygon to new bitmap of size 101,101
I tried to paint pixels of the rectangle using this code
for (int h = 0; h < pictureBox1.Image.Height; h++)
{
for (int w = 0; w < pictureBox1.Image.Width; w++)
{
if (IsPointInPolygon(rotatedpolygon, new PointF(w, h)))
{
g.FillRectangle(Brushes.Black, w, h, 1, 1); //paint pixel inside polygon
}
}
}
The pixels are painted in the following manner:
Now, how do I know which location on the rotated rectangle goes to which location in the new bitmap. That is how do i translate pixel co-ordinates of rotated rectangle to new bitmap.
or simply put, is it possible to map rows and columns from rotated rectangle to new bitmap as shown below?
Sorry, if the question is not clear.
What you asking to do is not literally possible. Look at your diagram:
On the left side, you've drawn pixels that are themselves oriented diagonally. But, that's not how the pixels actually are oriented in the source bitmap. The source bitmap will have square pixels oriented horizontally and vertically.
So, let's just look at a little bit of your original image:
Consider those four pixels. You can see in your drawing that, considered horizontally and vertically, the top and bottom pixels overlap the left and right pixels. More specifically, if we overlay the actual pixel orientations of the source bitmap with your proposed locations of source pixels, we get something like this:
As you can see, when you try to get the value of the pixel that will eventually become the top-right pixel of the target image, you are asking for the top pixel in that group of four. But that top pixel is actually made up of two different pixels in the original source image!
The bottom line: if the visual image that you are trying to copy will be rotated during the course of copying, there is no one-to-one correspondence between source and target pixels.
To be sure, resampling algorithms that handle this sort of geometric projection do apply concepts similar to that which you're proposing. But they do so in a mathematically sound way, in which pixels are necessarily merged or interpolated as necessary to map the square, horizontally- and vertically-oriented pixels from the source, to the square, horizontally- and vertically-oriented pixels in the target.
The only way you could get literally what you're asking for — to map the pixels on a one-to-one basis without any change in the actual pixel values themselves — would be to have a display that itself rotated.
Now, all that said: I claim that you're trying to solve a problem that not only is not solvable, but also is not worth solving.
Display resolution is so high on modern computing devices, even on the phone that you probably have next to you or in your pocket, that the resampling that occurs when you rotate bitmap images is of no consequence to the human perception of the bitmap.
Just do it the normal way. It will work fine.
I'd have a program that will allow you to draw lines over an image which will eventually be used for calculating distance.
To make things simple, my current image (which is in a PictureBox) is an image of a ruler. When you left click, a path is created and drawn.
Originally, to zoom in, I had it so that a new bitmap would be created with the images new size and I was able to use Graphics.ScaleTransform and it worked fine but it would just crop the image.
I needed the image to actually change width and height so now what I'm doing is just adding/subtracting a constant zoom amount to the width & height when zooming in/out.
With this approach, I can't seem to scale the graphics and the paths are skewed into different directions and not the right size when the image is zoomed in.
I completely understand why this is happening, because the image is getting larger and the graphics are staying the same, I just need whatever math is required to scale the graphics.
I've tried using Graphics.ScaleTransform as well as moving the graphics x & y to their current position + the current zoom amount (offset)
As directed by #TaW I changed the zooming functionality to calculate a new Width & Height based on the whatever zoom was applied then create a new Bitmap which contained the original image with the new width and height.
I would like to replicate the following cursor:
What I need is to draw that small red square where the pointer is everytime I move the mouse. This is a picturebox control by the way.
What would be the best way to replicate this square?
So, with the help of #CBinet I've been able to do this "pointer square". I've put the code in the Paint event of the picturebox, and in the MouseMove event I store the current point of the mouse and do the picturebox.Invalidate method.
However, now I need to place my cursor created from a file like in the first screenshot, in the bottom of the square. At this moment I have this:
As I said, I need to place the cursor in the right bottom corner of the square so it can be like the first screenshot.
What would be the best solution?
Using System.Windows.Forms.Cursor.Position, you can get the position of the cursor on the screen. Then you can draw a rectangle at that position with an arbitrary size then offset the rectangle by minus half of his size.
int x = Cursor.Position.X;
int y = Cursor.Position.Y;
int size = 10; // Arbitrary size
System.Drawing.Graphics graphics = CreateGraphics();
System.Drawing.Rectangle rectangle = new System.Drawing.Rectangle(x - (size / 2), y - (size / 2), size, size);
graphics.DrawRectangle(System.Drawing.Pens.Red, rectangle);
Note that you might have to add a reference to System.Drawing in the current project.
More informations :
Cursor.Position Property
How to: Draw Graphics on a Windows Form
Edit
If you want to position your cursor at the bottom right of the rectangle, all you have to change is the offset of your rectangle :
System.Drawing.Rectangle rectangle = new System.Drawing.Rectangle(x - size, y - size, size, size);
I want to draw a rectangle on a canvas in WPF. For drawing a line I can do this:
line.X1 = ls.P0.X;
line.Y1 = ls.P0.Y;
line.X2 = ls.P1.X;
line.Y2 = ls.P1.Y;
MyCanvas.Children.Add(line);
...in other words the location is a property of the line itself. I want to draw a rectangle the same way, i.e., assign its coordinates and add it to my canvas. But the examples I've seen online so far seem to look like this:
rect = new Rectangle
{
Stroke = Brushes.LightBlue,
StrokeThickness = 2
};
Canvas.SetLeft(rect,startPoint.X);
Canvas.SetTop(rect,startPoint.X);
canvas.Children.Add(rect);
...in other words it doesn't look like the rectangle has an inherent location, but instead its location is set by calling a method of Canvas. Is this true - Lines have inherent coordinates but Rectangles do not? Is there any way to have a rectangle in WPF with an inherent location, like a line, or do I have to roll my own (using lines)?
You could use a Path control with a RectangleGeometry like this:
var rect = new Path
{
Data = new RectangleGeometry(new Rect(x, y, width, height)),
Stroke = Brushes.LightBlue,
StrokeThickness = 2
};
canvas.Children.Add(rect);
...in other words it doesn't look like the rectangle has an inherent location, but instead its location is set by calling a method of Canvas. Is this true - Lines have inherent coordinates but Rectangles do not?
Locations in WPF are relative, which begs the question: coordinates relative to what?
Line, Rectangle, and Path all inherit from Shape, and in the case of any Shape object, the coordinates of the defining geometry are relative to the top-left corner of the Shape itself. Thus, when you create a Line object from (100, 300) to (300, 100), the resulting element is 300x300 points in size, even though the visible line has bounds of 200x200:
In this case, it is unnecessary to place the Line within a Canvas, as you are not using the coordinate system of the Canvas.
Some shapes like Line and Path allow you to place geometry at any "internal" coordinates you like. Others, like Rectangle and Ellipse, always position their defining geometry at (0, 0) internally, forcing you to use other layout properties to position the shapes within the greater scene (e.g., Canvas.Top/Left, Margin, etc.).
In your example, if you were to define a Rectangle of 200x200 points, and use the Canvas attached properties to position the rectangle at (100, 100), the resulting Rectangle element would measure 200x200, while the parent Canvas would measure itself to be at least 300x300, which is arguably more intuitive:
(shading added for clarity)
You are correct in that this is rather inconsistent. You may find it useful to always use layout properties (e.g., Canvas.Left/Top) to position shapes within a scene such that all elements are using the same coordinate system.
I've been trying to implement pinch-and-zoom using the ViewportControl class in a Windows Phone 8 Silverlight app for quite some time without any success. There are some great samples out there, such as this one, but I haven't been able to map the examples that I found to my scenario.
Pinching and zooming works fine, the problem I'm having is with the alignment of the viewport with the content, after the manipulation has completed.
The main problem I'm facing is that after my manipulation has completed, I've been unable to align the scaled content (a XAML canvas and sub tree which is the child of the ViewportControl) to the viewport. This results in the effective bounds of the viewport (the scrollable area) being offset from my content, resulting in part of my content being unreachable/unscrollable.
Here is my algorithm for the manipulation:
Pinch manipulation starts.
Apply render transform to canvas' sub tree during pinch.
Manipulation completes.
Scale main canvas to the effective size of
the render-transformed sub tree (this works as expected and the canvas is aligned with the render-transformed sub tree).
Obtain a transform between the canvas inside the ViewportControl and the viewport control itself.
Use the transform to obtain a bounding rect which (I expect) should represent a rect which overlays the content I want to scroll inside the ViewportControl,
but in the coordinate space of the hosting ViewportControl.
Apply this rect as the viewport bounds of the ViewportControl.
Set the origin of the viewport to the translated top-left coordinates of the canvas
Here is where I calculate and apply the new bounds, after the manipulation has completed:
// Obtain transform between canvas and ViewportControl
GeneralTransform gt = m_MainCanvas.TransformToVisual(m_ViewportControl);
Rect newBounds = gt.TransformBounds(new Rect(0, 0, m_MainCanvas.Width, m_MainCanvas.Height));
m_ViewportControl.Bounds = newBounds;
// set the origin of the viewport again
m_ViewportControl.SetViewportOrigin(gt.Transform(new Point(0, 0)));
This is resulting in my content being misaligned with the viewport.
Try, try as I might, I haven't been able to figure out what I'm doing wrong here... even after looking at tutorials that show how to solve this... :|
I figure that what is happening is that my rect that I'm setting the bounds to is sized correctly, but it's X and Y coordinates are off. I was hoping that this would be addressed by using the transform between the canvas and the ViewportControl itself, but apparently not.
Question: How do I go about setting the origin of the ViewportControl correctly (how do I calculate the point to pass to the SetViewportOrigin method? Can someone please explain this ratio that people are using between the scaled contents and the viewport that I see in other examples of how to crack this?
7/8/2014 Update
I made some headway here. My approach of getting a transform between the content in the ViewportControl and the control itself, then using this to obtain a rect in the control's space to use as the bounds for the viewport wasn't working. My workaround was to simply wrap the render-transformed content in a canvas which I resized the effective (render transformed) size. Then I set the bounds to that size and I finally had the nice bounce-back effect working.
The problem I'm facing now is that when I resize the canvas and reset the bounds for the viewport, the content snaps to the top-left corner of the viewport, and is no longer centered around the pinch area that the user provided.
Can anyone help me understand how the SetViewportOrigin method works on ViewportControl? I'm seeing some really odd data for the Viewport vs. the canvas after a pinch manipulation:
Canvas Size = 1025.69, 1641.11
Bounds = 0,0,1025.69,1641.11
viewport = -56,41.00,480,698
Why is the viewport offset with non-zero values (x,y = -56,41) when I don't even call SetViewportOrigin(Point)?
Here is how I'm thinking the SetViewportOrigin(Point) method works: let's say my Viewport control itself was sized to 400 x 400 pixels, and my content was 800 x 800 pixels. If I set the origin of the viewport to 100, 100 the content would be scrolled such that the first 100 vertical and 100 horizontal pixels would be clipped/masked/offscreen. Is this not how the ViewportControl works?
I figured this out and am a happy camper now. It turned out that I was setting the viewport origin to a point using the wrong coordinate space. I was thinking that if I wanted to move the content to a certain place, that I would provide that point in the coordinate space of the viewport and the content would scroll (setting the upper-left point of the content). What I figured out is that the Point data that the SetViewportOrigin method takes is in the coordinate space of the content. For example: if your content is 500 x 500 pixels wide, your viewport is 400 x 400 pixels wide, and you'd like the first 100 vertical and 100 horizontal pixels to be masked by the viewport (showing the bottom-right corner of the content), you would set the origin to be 100,100, not -100,100.
I was doing a bunch of useless conversion between coordinate spaces, trying to pass the viewport a point in its coordinate system.