User-Resizable and User-Rotatable shapes on Canvas with WPF - c#

I'm currently creating a drawing software using WPF Shapes on canvas.
I've created a system allowing the user to move and rotate shapes on a Canvas using a transparent canvas upon the shape (which rotate with the shape) :
The green point is used to rotate the shape, the blue zone upon the rectangle is used to move the shape. I'd like to use my 4 red points to re-size the shape.
But the shape is rotatable, so corners coordinates aren't completely relevant to resize the shape. It seems, in my opinion, to be relevant only if the rotation is equals to 0, because the Left-Top Corner can be the Bottom-Right one after a 180 degree rotation.
Right now I'm using a RotateTransform to achieve the rotation with a 0.5, 0.5 RenderTransformOrigin. I'd like to avoid the use of a ScaleTransform because I want to keep the StrokeThickness at the size it is.
All red dots are pseudo-draggable (using MouseDown, MouseMove, MouseUp events). I use a buffer point which gives me the delta in X and Y between two mouse events.
How can use the deltas to resize the shape, even if it is rotated or moved ?

You can use the deltas to resize the shape if it is rotated. The only thing you have to do is rotating the mousemovement either. As you can see:
The movement of the mouse from origin to location describes a 2-D-vector. You can rotate this vector mathematically by using this formula:
x' = cos(theta) * x - sin(theta) * y
y' = sin(theta) * x + cos(theta) * y
where x/y is the current location of the mouse relative to the origin of the resize and theta the angle of rotation which can be found in the RotateTransform-object (Angle-Property) of the shape. At this point I don't know exactly if you have to use -theta, because the vector has to rotate in the opposite direction.
You can pick x'/y' for calculating the deltas and resize the shape like if it wasn't rotated.
I did not implement this myself. This is just a general idea. Maybe I can serve with a little code if you try this and give feedback or specify the problem more deeply or update your question with some code.
Appendix:
Resizing the shape using the deltas should be easy if you can access the width- and height-property of the shape. You simply add/subtract the x-delta to/from width and/or add/subtract the y-delta to/from height, depending on the grabbed point. This isn't affected by the location of the shape within the canvas.
Maybe you have to adjust the Canvas.Left/Canvas.Top-Property of the shape. I.e. if the user grabs the left upper point and resizes it to left/up, you should subtract the deltas from left and top porperty as well. Otherwise it will expand to right/down.

Related

How to make 3d viewport available within a square?

How to make 3D Viewport within that 3D viewport square
You can use the Normalized Viewport Rectangles' approach, achieved by editing the Viewport Rect of the Camera.
The Documentation explains an example of split screen for a two-player game. You can adapt the explanation having the game in a particular area, and the GUI in the other screen space.
Normalized Viewport Rectangles
Normalized Viewport Rectangle is specifically for defining a certain
portion of the screen that the current camera view will be drawn upon.
You can put a map view in the lower-right hand corner of the screen,
or a missile-tip view in the upper-left corner. With a bit of design
work, you can use Viewport Rectangle to create some unique behaviors.
It’s easy to create a two-player split screen effect using Normalized
Viewport Rectangle. After you have created your two cameras, change
both camera’s H values to be 0.5 then set player one’s Y value to 0.5,
and player two’s Y value to 0. This will make player one’s camera
display from halfway up the screen to the top, and player two’s camera
start at the bottom and stop halfway up the screen.

c# XNA 4.0 Camera zoom and sprite resizing

Inside my project, I have a sprite being draw of a box. I have the camera zoom out when clicking a key. When I zoom out, I want my box to scale it's dimensions so it stays consistent even though the camera has zoomed out and "shrunk" it.
I have tried multiplying the object's dimensions by 10% which seems to be the viewpoint's adjustment when zooming out, but that doesn't seem to work. Now this may sound dumb, but would scaling the sprite in the draw function also change the sprite's dimensions?
Let's say the box is 64x64 pixels. I zoom out 10% and scale the sprite. Does the sprite still have the boundaries as 64x64 or is the up-scaling also changing it's dimensions?
Scaling using SpriteBatch.Draw()s scale argument will just draw the sprite smaller/bigger, i.e. a 64x64 one will appear as 7x7 pixels (the outer pixels being alpha blended if enabled). However there are no size properties on the sprite, if you have your own rectangle, position variables for the sprite SpriteBatch.Draw() of course will not change those.
An alternative is draw the sprite in 3D space then everything is scaled when you move your camera, so the sprite will appear smaller though it will still be a 64x64 sprite.
How to draw a sprite in 3D space? Here is a good tutorial http://www.riemers.net/eng/Tutorials/XNA/Csharp/Series2/Point_sprites.php. (You will need to take time to learn about using 3D viewports, camera's etc, see here: http://msdn.microsoft.com/en-us/library/bb197901.aspx)/
To change sprite dimensions you need to change Rectangle parameter for SpriteBatch.Draw. To calculate zoom on rectange:
Rectangle scaledRect = new Rectangle(originalRectangle.X, originalRectangle.Y, (int)(originalRectangle.Width*zoom), (int)(originalRectangle.Height*zoom)); // where zoom default is 1.0f
When drawing use:
spriteBatch.Draw(Texture, scaledRect, Color.White);
Now I'm sorry to assume it, but without knowing why you doing what you doing - I think you doing something wrong.
You should use camera transformation to zoom out/in. It is done like that:
var transform = Matrix.CreateTranslation(new Vector3(-Position.X, -Position.Y, 0))* // camera position
Matrix.CreateRotationZ(_rotation)* // camera rotation, default 0
Matrix.CreateScale(new Vector3(Zoom, Zoom, 1))* // Zoom default 1
Matrix.CreateTranslation(
new Vector3(
Device.Viewport.Width*0.5f,
Device.Viewport.Height*0.5f, 0)); // Device from DeviceManager, center camera to given position
SpriteBatch.Begin( // SpriteBatch variable
SpriteSortMode.BackToFront, // Sprite sort mode - not related
BlendState.NonPremultiplied, // BelndState - not related
null,
null,
null,
null,
transformation); // set camera tranformation
It will change how sprites are displayed inside sprite batch, however - now you also must account for different mouse coordinates (if you using mouse input). To do that you must transform mouse position to transformed world matrix:
// mouse position, your tranformation matrix
public Vector2 ViewToWorld(Vector2 pos, Matrix transform)
{
return Vector2.Transform(pos, Matrix.Invert(transform));
}
I used the code without direct access to test it, so if something will not work - feel free to ask.
This is not answer to your question directly, if you could provide reason why you want re-size sprite when zooming instead of zooming camera - maybe I could better answer your question, also you should fallow markmnl link to understand world transformations and why you seem to need it in this situation.

Tracking Rotating Sprite

I am messing about in XNA and have run into a problem. I have a 48 * 48 sprite that I can keep track of its location in the game world by the top left corner of the sprite.
I want to be able to rotate the square and still keep track of the same point. For instance if I rotate 90degrees clockwise and the orginal X position was 200 the new X position should be 200 + 48(the size of the width of the image). Its fine for 90 degrees I am able to work that out in my head but each one in between is the problem!
I know there is probably some kind of formula to work this out.
Any help would be great! Oh the square is rotating on its center.
I'm just using spriteBatch.Draw()
spriteBatch.Draw( animations[currentAnimation].Texture,
Camera.WorldToScreen(WorldRectangle),
animations[currentAnimation].FrameRectangle,
color, rotationScale , new Vector2((float)frameHeight/2, (float)frameWidth/2), effect, TileMap.characterDepth);
If you have to keep track of a moving rotating sprite you can't use the top left corner, but its centroid. You already draw your sprite using the centroid to rotate it.
The problem is that the second parameter of your Draw call is a Rectangle, you sholud use a Vector2 position, instead.
You're building your application on top of a 3D graphics library. 3D graphics libraries are very good at solving this kind of problem! Break it down into smaller operations and let the library do the work for you.
First: it's easiest to think about these kinds of questions when you're working in model space rather than world space. In other words: you don't need to worry about where the rotating point is in absolute terms, you only need to worry about where it is relative to the untransformed model (in this case, your sprite without any rotation or translation).
So where is that? Simple:
var pt = new Vector3(-frameWidth / 2f, -frameHeight / 2f, 0f);
Your point of origin is the center of your sprite, so the center of your sprite in model space is (0, 0). This means that the top left corner of your sprite is half the width of the sprite in the negative x direction, and half the height of the sprite along the negative y direction.
Now create an object that represents the desired transformation. You can do this by creating a rotation matrix using XNA's built-in methods:
var transformation = Matrix.CreateRotationZ(MathHelper.ToRadians(90f));
Now apply the transformation to your original point:
var transformedPt = Vector3.Transform(pt, transformation);
This is still in model space, remember, so to get world coordinates you'll need to transform it into world space:
var transformedWorldX = transformedPt.X + spritePosition.X;
var transformedWorldY = transformedPt.Y + spritePosition.Y;
And there you go.

Transforming co-ordinates from a rectangle to a parallelogram

I am stuck on a simple yet vexing problem with basic geometry. Too bad I don;t remember my high-school co-ordinate geometry and looking for some help.
My problem is illustrated in this diagram: A rectangle rotated, scaled, and warped into a parallelogram http://img248.imageshack.us/img248/8011/transform.png
I am struggling with transforming a co-ordinate from the rectangle to a resized parallelogram. Any tips, pointers and/or code-examples would be wonderful!
Thanks,
M.
There are several steps in this transformation.
Scale about (x,y) to adjust to the final size W', H'. (Possibly unequal
scaling on X and Y axes).
Apply a shear transform to convert
the rectangle to a parallelogram
(keeping x,y invariant).
Rotate about (x,y) to align to the
final coordinate orientation.
Translate to the new location.
Create the coordinate matrices for each of these and composite (multiply) them together to create the overall transform. Wikipedia could be your starting point to know about these transformation matrices.
Tip: Might be simplest to apply a translation to move (x,y) to the origin first. Then, the shear, scaling and rotation are a lot simpler to do. Then move it to the new location.

Draw Points onto canvas using an offset?

I have an array of Point variables. When drawn using Graphics.DrawLine, they create the expected image. My problem is that 0,0 is actually the center of the image (not the top left of my canvas as expected. My X and Y coordinates in the Points can contain negative numbers.
When I try to draw this to my Image, of course I get 1/4 of the total image as the remainder is drawn outside the bounds of my canvas. How do I center this drawing correctly onto my canvas?
I know the dimensions of the image I want to draw. I know where 0,0 is (width / 2, height / 2).
I suppose I can translate each and every single Point, but that seems like the hard way to do this.
TranslateTransform() can map coordinates for you if you setup a transformation during your drawing handlers.
Graphics.TranslateTransform # MSDN
Or, map your coordinates by adding half the width and half the height of the desired viewing area to each coordinate.
Also, you may need to scale your coordinates. You may use Graphics.ScaleTransform to do this.
Graphics.ScaleTransform # MSDN
If you don't wish to use this, then you should divide X coordinates by the percent amount you wish to stretch the width, and divide Y coordinates by the percent amount you wish to stretch the height. This gives us 1 for 100%, 1.2 for 120%, 0.8 for 80%, etc.
Welcome to the Windows' version of the Cartessian Plane. Your last statement is correct. You do have to offset each and every point. The only real help you can give yourself is to make the offset logic a separate method to clean up your main drawing code.
When creating the array, add an offset to each x value equal to half of the width and an offset to y equal to half of the height. That way when the points are drawn, they're in the expected position.

Categories