Precise pixel movement in Canvas - c#

I am doing some sort of drawing software in WPF, and I have certain visual elements in a Canvas like for example Rectangles and Lines. I have implemented dragging of those elements around the Canvas to move them. The motion must be aligned to pixels, I read WPF uses points and not pixels so it has become a concern of mine to know whether my lines or rectangles are aligned to pixels. I tried using SnapsToPixels, but I'm not sure it will do the trick, or if it will do it when I'm moving the visuals around.
Finally, I must implement moving visuals with the keyboard, a single cursor stroke means move the visual exactly one pixel, how can I do this from the code behind? I assume doing something like:
Canvas.SetLeft(visual) = Canvas.GetLeft(visual) + 1;
Will only add one point to its position, and not one pixel, how can I move exactly one pixel in the Canvas?
Thank you very much.

It might help to use SnapToDevicePixels for your canvas.

Is this what you are looking for?
Matrix m =
PresentationSource.FromVisual(Application.Current.MainWindow)
.CompositionTarget.TransformToDevice;
double pixelSizeX = m.M11;
double pixelSizeY = m.M22;

Related

Linerenderer on canvas doesn't show properly

I am currently working on a game, and i've run into the following problem:
I want to draw some results as a graph on top of my Canvas and so far i've found out that my Canvas needs to be in Screenspace-Camera for this to work, as the LineRenderer is a 3D-Object and will be covered by Screenspace-Overlay otherwise.
And i've actually got it to work with this, looks like the following:
But the problem i've encountered is that if i increase the screen size, e.g. by stretching the GameView or maximizing it, the line disappears, even though it has a negative z compared to all my UI elements and therefore appears in front of them in the EditorView:
If i try to fix this by applying a greater negative z-Value relative to the screen size the lines get distorted, as they are getting closer and closer to the camera, and changing their alignment from view to z-Axis didn't help either.
What makes this even more confusing is that this happen to lines that are drawn lower (smaller y-value) first, meaning a line at the bottom of my graph disappears earlier. I really don't know why this is happening. Any help would be appreciated.
For 3D objects mixed with UI elements I recommend using a separate camera with greater depth than the camera drawing standard ui elements. This way your 3D objects will always be rendered on top of the UI elements and you wont have to worry about Z positions.

How can I draw a Line animation to outline various shapes?

I am trying to draw a line animation outline various shapes as in the image below. I am very well aware that it's best practice that I mention what I've been able to achieve to get specific help, but I am not sure where to begin, just that I know that using a Line Renderer could be a good approach to achieving this. That said, how can I achieve this?
UPDATE
I think I didn't explain a few things clearly enough. I am interested in animating the outline of objects without arrows, just a line traced round the outline like the image below:
I would do the following: (pseudocode, untested)
For every prefab or gameobject, store a List of edges that define your outline.
I wouldn't recommend using the mesh's edges, it's probably better to have a specific predefined list of edges per shape to avoid the inner edges of the object. Every entry in the list is defined by two Vector3's which are the two vertices.
List<Vector3[]> outline = new List<Vector3[]>();
Now, you have many ways to actually draw the arrows, like having them as individual gameobjects (probably not a good idea), particle system, or just drawn automatically from the parent objects update function. I would recommend the latter.
Now you would store a bunch of floats that define where your arrows are
public List<float> arrow_locations = new List<float>();
//adding one arrow
arrow_locations.Add(0.0);
//now in the update function of your parent object, update the arrow locations
private float cycle = 0.0f;
void Update()
{
float segment_size = 360.0f/outline.Count;
for(int i=0; i < arrow_locations.Count; i++)
{
arrow_locations[i] += 0.05f; //speed of spinning
if( arrow_locations[i] >= 360.0f ) arrow_locations[i] = 0;
//now to get the actual location of the arrow
int which_edge = Mathf.Floor((arrow_locations[i]/360.0f)*outline.Count);
//this will give us a number 0..1 telling us where along the edge the arrow is
float weight_within_edge=(arrow_locations[i] - segment_size*which_edge)/segment_size;
//here we lerp between the two vertices of the edge
Vector3 new_loc = outline[which_edge][0]*(1.0-weight_within_edge) + outline[which_edge][1]*(weight_within_edge);
//now that we have the location of the arrow, draw it
//note, you can get more efficient if using instancing for all arrows
//You can also use line drawing, but i wouldn't recommend that.
DrawMesh(arrow_mesh, new_loc, Quaternion.identity);
}
}
Please note, that when you have the positions of the arrows, you can opt to draw them in 2D in the UI by projecting them onto the camera plane. The lines aside from the arrows are themselves static, so you can draw them as part of the mesh very easily. Also note, I make no mention of the objects position, all values should probably be defined in local space, then transformed with the object. You can transform the drawn stuff in the the DrawMesh function by supplying a a transform matrix.
I think a shader with a parameterized radial mask would be the best way to do this. I have never done one myself, so I only have a general idea of how it's done, but here is how it would work AFAIK:
Create some kind of cell shader that can draw the edges of objects.
Create a filter/mask that has an angle shape extruding radially from the center to the edges; you can control the shape/angle using a parameter. Unity already has something similar to this in the Tanks! tutorial - Tank Health lesson.
Note: The tutorial might even be exactly this idea, but I don't remember with enough details to confirm; I'll update the answer after I take a look again.
The tutorial has the same idea, but it applies it using unity's builtin UI stuff.
Using this mask, only the masked area of the shape's edge will be drawn the screen.
By increasing the angle parameter of the mask over time, you can create the effect of the edge of the object getting revealed radially over time. Which seems to be exactly what you want.
To help visualize, a very professional diagram made in paint:
light blue = mask.
dark blue = "revealed" part of the mask (angle parameter). Plus how it would behave if the angle is increased (arrow).
green = object.
black = outline being drawn to the screen.

How to animate a simple 3D shape using Helix Toolkit?

I'm trying to animate a simple rectangular shape so that it scales in size in a certain direction. As it is, I am making a rectangle that extends from point A to B. The end goal is to animate it so that it starts at A and is transformed to be the length required to get to B.
I'm pretty new to animation in general, so this process seems finicky to me.
Right now I am:
Creating a vector between the start and end point
Finding the 8 corners of the rectangle along that vector
Creating 2 triangles for each face of the rectangle
Rendering the shape
This is all being done by using a MeshBuilder object and adding the triangles and points individually.
So, the way I'm creating the prism doesn't really help for what I need to do. Ideally I suppose, I would just create a short prism aligned between the points, and then just extend the rectangle to be the right length in an animation.
Any thoughts?
I solved this is a sense by scaling the 3D object from a size of 0 in the X/Y/Z to 1.0. So instead of the prism "extending" from A to B, it more or less "grows" to B.
Note that the ScaleTransform3D needed to have the CenterXYZ properties set to the coordinates of point A in order for it to be anchored to the correct position.
If I find a better solution, I'll update this answer later.

C# Monogame Performance when Drawing Thousands of SpriteBatch.DrawString()

I'm currently creating a large map, that consists of a lot of rectangles (33,844), that all have a unique name (label), which I'm drawing on top of them using a SpriteFont.
Drawing all of the rectangles takes no performance hit at all. But, as soon as I try to write all of their labels with DrawString(), my performance goes into the dumps.
In my head, I would like to draw all my rectangles and text to one texture all at once, and only have to keep redrawing that entire finished texture. My issue is, this is an enormous map, and some of the coordinates for the rectangles are very high (example: one slot's x is 14869 and y is 23622), and they're far bigger than a Texture2D allows.
Since this is a map, I really only need to draw the entire thing once, and then allow the user to scroll/move around it. There's no need for me to continually redraw all of the individual rectangles and their labels.
Does anyone have experience with this type of situation?
Try to only render the labels that you can see on the screen and if you can zoom back far enough, just don't render them.
Textrendering is expensive, since it is basically creating a rectangle to draw on for every character in the font and then applying the same RGBA texture to it. So depending on the number of characters you write, the number of rectangles increases. This means four new vertices per character.
Depending on what you write you could simply create a texture with the text already on it and render that, but it won't be very dynamic.
EDIT: I need to clarify something.
There's no need for me to continually redraw all of the individual rectangles and their labels.
This is wrong. You have to draw the whole thing every frame. Sure, it doesn't increase memorywise, but it still is a lot to render and you will need to render it every frame.
But as I said: Try to only render the labels and the rectangles that collide with the screenboundaries, then you should be fine.
There are two ways to solve your problem.
You can either render your map to a RenderTarget(2D/3D) or you can cull the rectangles/text that are offscreen. However, I am not 100% sure that RenderTargets can go as large as you would need, but you could always segment your map into multiple smaller RenderTargets.
From more information on RenderTargets, you might want to check out RB Whitaker's article on them, http://rbwhitaker.wikidot.com/render-to-texture
Culling, in case you are familiar with the term when used in this context, means to only render what is visible to the end-user. There are various ways that culling can be implemented. This does however require you to have already implemented a camera (or some type of view region) and you perform a basic axis-aligned bounding box collision (AABB collision, which MonoGame's Rectangle supports out of the box) of the rectangles against the camera's viewport and only render it if there is a collision.
A basic implementation of culling would look something like this:
Rectangle myRect = new Rectangle(100, 100, 48, 32);
public void DrawMapItem(SpriteBatch batch, Rectangle viewRegion)
{
if (viewRegion.Contains(myRect))
{
//Render your object here with the SpriteBatch
}
}
Where 'viewRegion' is the area of you world that the camera/end-user can actually see.
You can also combine the two methods, and render the map to multiple render targets, and then cull the render targets.
Hope this helps!

Snaping circle to rectangle/line

I am trying to implement some kind of snapping functionality in WPF for a circle (which represents my mouse) and it should snap to another object (normally this would be a line or a rectangle).
Is there a way to do this kind of functionality with WPF without doing all the calculations on my own and if not is there an easy way (library?) to get this kind of information?
Edit: I want to snap the border of the circle to the border of the rectangle/line.
As a first step, you should find the point on the rectangle that is the closest to the cursor, and the distance between the two: extending the edges of the rectangle, you partition the plane into 9 regions. Depending on the region where the cursor lies, the searched distance will be the distance to a corner (Euclidean distance formula) or the distance to an edge (difference of abscissas or ordinates).
Subtract the circle radius from this distance. This will tell you if you are close enough for a snap.
When a snap is possible, move the cursor along the line from the current cursor position to the closest point until you hit the corner or edge. You will need to use the parametric equation of the line segment.
The complete discussion requires some care but only involves simple math.
A similar approach is possible to snap to a line segment. Here is a trick: if you rotate the line segment to make it horizontal, you can consider the line segment as a degenerate rectangle and use the same snapping algorithm. Rotate the line segment and the cursor, apply the snapping logics and then counter-rotate the updated cursor.
That kind of functionality only takes a few lines of code to replicate... I doubt that you'll find a 'library' of code to do it for you. The method is as follows:
Keep a collection that contains the 4 Points that form each shape's bounding box. You then need to handle to MouseMove event on the Canvas, or shape container. In this event, you simply need to ascertain whether the current mouse position is within a certain distance from any of the shape edges... you'll have a little bit more work to do with non-rectangular shapes to calculate their edges, but the principal is the same.
If you detect the presence of a nearby shape, then you simply need to change the value of the nearest dimension to that of the nearby shape... the snap. That's it... much easier than you think.

Categories