I have all the necessary code to move and click the mouse via C# code, but I don't want to just set the mouse position to X and Y; that will look jerky. Instead, I want to have a smooth transition from point X1, Y1 to point X2, Y2 over Z seconds. Similar to keyframing.
I'm looking for a method similar to this:
public void TransitionMouse(int x, int y, double durationInSecs)
It will just smoothly move the mouse from its current position to x and y in durationInSecs seconds. I have a function called:
public void MoveMouse(int x, int y)
That moves the mouse to x, y immediately.
EDIT
Thanks for the help guys! Here's the finished, and tested, code:
[DllImport("user32.dll")]
static extern bool SetCursorPos(int X, int Y);
public void TransitionMouseTo(double x, double y, double durationSecs)
{
double frames = durationSecs*100;
PointF vector = new PointF();
PointF mousePos = Cursor.Position;
vector.X = (float)((x - mousePos.X) / frames);
vector.Y = (float)((y - mousePos.Y) / frames);
for (int i = 0; i < frames; i++)
{
SetCursorPos((int)(mousePos.X += vector.X), (int)(mousePos.Y += vector.Y));
Thread.Sleep((int)((durationSecs / frames) * 1000.0));
}
}
You could do this in several ways. One option would be to calculate the vector required to move the mouse every frame and apply it over the time period to the mouse position.
So, if we are at position 5,5 and want to move to 20,30 over 10 frames, our vector would be the following:
val = (target - start) / frames;
x = (20 - 5) / 10;
y = (30 - 5) / 10;
Vector = 1.5,2.5
Then, in your TransitionMouse method, you apply the vector to the mouse position slowly over whatever duration you wish, using the Thread.Sleep method to control the speed. The code might look like this:
public void TransitionMouseTo(int x, int y, int durationSecs)
{
int frames = 10;
PointF vector = new PointF();
vector.X = (x - Cursor.Position.X) / frames;
vector.Y = (y - Cursor.Position.Y) / frames;
for (int i = 0; i < frames; i++)
{
Point pos = Cursor.Position;
pos.X += vector.X;
pos.Y += vector.Y;
Cursor.Position = pos;
Thread.Sleep((durationSecs / frames) * 1000);
}
}
Another way of doing it would be to use Bresenhams algorithm to calculate all the points the mouse cursor will move through and then loop through each point and apply it to the cursor again using Thread.Sleep to keep the timing correct.
Steve
really depend on your definition of smooth, however most algorithm of "smooth" motion use spline to interpolate between 2 or more data points.
this could help
http://geekswithblogs.net/JuanDoNeblo/archive/2007/10/25/Data-Interpolation-with-SPLINE-in-Csharp.aspx
Related
I have a gameobject that occupies the whole screen just for testing purposes. I'm drawing a line btw. What I'm trying to achieve is if the mouse position hits a gameobject it will store the vector2 coordinates in a list. But raycast is not storing all the coordinates. Below is my code
private void Update()
{
if (Input.GetMouseButton(0))
{
Vector2 mousePos = Input.mousePosition;
Vector2 Pos = _camera.ScreenToWorldPoint(mousePos);
if(!mousePositions.Contains(Pos))
mousePositions.Add(Pos);
if (Physics.Raycast(Camera.main.ScreenPointToRay(mousePos), out RaycastHit hit))
{
Vector2 textureCoord = hit.textureCoord;
int pixelX = (int)(textureCoord.x * _templateDirtMask.width);
int pixelY = (int)(textureCoord.y * _templateDirtMask.height);
Vector2Int paintPixelPosition = new Vector2Int(pixelX, pixelY);
if (!linePositions.Contains(paintPixelPosition))
linePositions.Add(paintPixelPosition);
foreach (Vector2Int pos in linePositions)
{
int pixelXOffset = pos.x - (_brush.width / 2);
int pixelYOffset = pos.y - (_brush.height / 2);
for (int x = 0; x < _brush.width; x++)
{
for (int y = 0; y < _brush.height; y++)
{
_templateDirtMask.SetPixel(
pixelXOffset + x,
pixelYOffset + y,
Color.black
);
}
}
}
_templateDirtMask.Apply();
}
}
}
Everytime I checked the element count mousePositions are always greater than linePositions. I don't know what's causing this
the element count mousePositions are always greater than linePosition
well it is quite simple: In
int pixelX = (int)(textureCoord.x * _templateDirtMask.width);
int pixelY = (int)(textureCoord.y * _templateDirtMask.height);
you are casting to int and cut off any decimals after the comma (basically like doing Mathf.FloorToInt).
So you can totally have multiple mouse positions which result in float pixel positions like e.g.
1.2, 1.2
1.4, 1.7
1.02, 1.93
...
all these will map to
Vector2Int paintPixelPosition = new Vector2Int(1, 1);
Besides, you might want to look at some better line drawing algorithms like e.g. this simple one
And then note that calling SetPixel repeatedly is quite expensive. You want to do a single SetPixels call like e.g.
var pixels = _templateDirtMask.GetPixels();
foreach (Vector2Int pos in linePositions)
{
int pixelXOffset = pos.x - (_brush.width / 2);
int pixelYOffset = pos.y - (_brush.height / 2);
for (int x = 0; x < _brush.width; x++)
{
for (int y = 0; y < _brush.height; y++)
{
pixels[(pixelXOffset + x) + (pixelYOffset + y) * _templateDirtMask.width] = Color.black;
}
}
}
_templateDirtMask.SetPixels(pixels);
_templateDirtMask.Apply();
It happens because there is really could be a case, when several elements from mousePositions are associated with one elment from linePositions.
Rough example: your texture resolution is only 1x1px. In this case you linePositons will contain only one element. And this element will be associated with all elements from mosePositions.
So, relation of the number of elements in these lists depends on relation of your texture and screen resolutions.
I'm struggling to figure out why the below function is only ever moving my mouse from 0, 0 screen coords, to my final destination, even though Cursor.Position is returning the correct screen coords. If anybody could enlighten me i'd be most grateful.
public void MoveAndClick(int x, int y, int steps)
{
Point start = Cursor.Position;
PointF iterPoint = start;
PointF slope = new PointF(x - start.X, y - start.Y);
slope.X = slope.X / steps;
slope.Y = slope.Y / steps;
for(int i=0; i<steps; i++)
{
iterPoint = new PointF(iterPoint.X + slope.X, iterPoint.Y + slope.Y);
inputSim.Mouse.MoveMouseTo(iterPoint.X, iterPoint.Y);
Thread.Sleep(10);
}
inputSim.Mouse.MoveMouseTo(x, y);
inputSim.Mouse.RightButtonDown();
inputSim.Mouse.RightButtonUp();
}
Taken from https://stackoverflow.com/a/913703/2014393
DOH! Always stop working when you're sleepy and go to bed.
The InputSimulator library requires that you translate coordinates like so:
int startX = 65535 * Cursor.Position.X / Screen.PrimaryScreen.Bounds.Width;
int startY = 65535 * Cursor.Position.Y / Screen.PrimaryScreen.Bounds.Height;
I was converting the end coordinates but completely forgot to translate the starting coordinates, derp.
FlaUI allows for the mouse to move smoothly. Here is the Mouse class in FlaUI for reference.
Good evening, I know on the web there are similar questions and a few tutorials about it, but I'd like you to check my code and correct it. I mean, I'd like to know what's wrong with my project.
I have to draw a parabola graph given its equation on my main panel.
I also must include two buttons, zoom in and zoom out, which are used to reduce and enlarge the "view" panel's view (and so the parabola).
I was recommended to use a scale var.
This is my code:
note: x0, y0 are panel_main x center, y center.
I have x, y that are used to determine x,y from the equation.
xpc, ypc are converted for the window scale (so are pixels).
xmin, xmax are the extreme values that, with a certain scale, stay on the panel
I hope you can give me a hint, thanks a lot!
public void DisegnaParabola()
{
Graphics gs = panel_main.CreateGraphics();
pen.Color = Color.Black;
scale = (x0*2) / zoom; //Pixels equivalent to 1x or 1y
n_punti = (x0*2) / scale; //Number of x math points that are visible in window
xmin = -(n_punti / 2);
xmax = n_punti / 2;
precision = 1 / scale; //Increment of x to have 1px
if (asse_parabola.SelectedIndex == 0) //if Y axis
{
for (double i = xmin + precision; i < xmax; i += precision)
{
rifx = i - precision; //Old points
rifxpc = rifx * scale;
rify = (a * Math.Pow(rifx, 2)) + b * rifx + c;
rifypc = y0 - (rify * scale);
x = i; //New points
y = (a * Math.Pow(x, 2)) + b * x + c;
ypc = y0 - (y * scale);
gs.DrawLine(pen, (float)rifxpc, (float)rifypc, (float)xpc, (float)ypc);
}
}
else
{
scale = (y0*2) / zoom; //Pixels for 1y
n_punti = (y0*2) / scale; //Numbers of y in the window
ymin = -(n_punti / 2);
ymax = n_punti / 2;
for(double i=ymin+precision; i<ymax; i+=precision)
{
rify = y - precision;
rifypc = (y0*2) - rify * scale;
rifx = (a * Math.Pow(rify, 2)) + b * rify + c;
rifxpc = x0 + (rifx * scale);
y = i;
x = (a * Math.Pow(y, 2)) + b * y + c;
xpc = x0 + (x * scale);
gs.DrawLine(pen, (float)rifypc, (float)rifxpc, (float)ypc, (float)xpc);
}
}
lbl_canc.Visible = true;
}
Your question actually consists of several tasks and as usual the key is to take and break those apart..
One issue is getting the data, I will leave the details to you but show how to sparate it from the rest.
The next issue is to scale the data. I'll show you how to avoid this one altogether and scale the drawing tool instead.
And the third one is to draw them to a display surface. As you'll see this is really simple once the other issues are taken care of.
Let's start with the most important step: Collecting the data. You try to create and scale and draw them all in the same piece of code. This has many disadvantages..
Let's first collect the data in a suitable structure:
List<PointF> points = new List<PointF>();
List<T> is the collection of choice most of the time; certainly much nicer than arrays! In some method you should fill that list with your data, calculated from some formula.
Here is an example:
List<PointF> getPoints(float start, float end, int count, float ymax)
{
List<PointF> points = new List<PointF>();
float deltaX = (end - start) / count;
for (int i = 0; i < count; i++)
{
float x = i * deltaX;
// insert your own formula(s) here!
float y = ymax + (float)Math.Sin(x * somefactor) * ymax;
points.Add(new PointF(x, y));
}
return points;
}
Now for the second important part: How to scale the data? This can be done either when creating them; but again, separating the two taks makes them both a lot simpler.
So here is a function that, instead of scaling the data scales the Graphics object we will use to plot them:
void ScaleGraphics(Graphics g, List<PointF> data)
{
float xmax = data.Select(x => x.X).Max();
float ymax = data.Select(x => x.Y).Max();
float xmin = data.Select(x => x.X).Min();
float ymin = data.Select(x => x.Y).Min();
float width = Math.Abs(xmax - xmin);
float height = Math.Abs(ymax - ymin);
var vr = g.VisibleClipBounds;
g.ScaleTransform(vr.Width / width, vr.Height / height);
}
This method makes sure that all the data in our list will fit into the drawing surface. If you want to restrict them to a different size you can pass it in and change the code accordingly..
Finally we need to do the actual drawing. We do that where we should, that is in the Paint event of our drawing surface control..:
private void panel1_Paint(object sender, PaintEventArgs e)
{
if (points.Count < 2) return; // no lines to draw, yet
ScaleGraphics(e.Graphics, points);
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
using ( Pen pen = new Pen(Color.Blue )
{ Width = 1.5f , LineJoin = LineJoin.Round, MiterLimit = 1f} )
e.Graphics.DrawLines(pen, points.ToArray());
}
I'm currently working on implementing an algorithm in EmguCV in C#, meaning that I don't want to use the build in rotate function that comes with EmguCV.
I've already found the algorithm that I want to implement, but I'm kinda stuck as how to implement it. The main problem is, that I don't know how the specify the X and Y values of my Matrix to be able to do the intended calculations.
Rotation Algorithm:
http://i.stack.imgur.com/hQMxF.jpg
Right now my Code looks like this:
static void Main(string[] args) {
Mat image = CvInvoke.Imread("C:\\Users\\Leon\\Desktop\\a.jpg", LoadImageType.Grayscale);
int height = image.Height;
int width = image.Width;
//Convert to Matrix
Matrix<Byte> matrix = new Matrix<Byte>(image.Rows, image.Cols, image.NumberOfChannels);
image.CopyTo(matrix);
Matrix<Byte> newMatrix = new Matrix<Byte>(image.Rows, image.Cols, image.NumberOfChannels);
image.CopyTo(newMatrix);
for (int i = 0; i < matrix.Rows-1; i++)
{
for (int j = 0; j < matrix.Cols-1; j++)
{
}
}
CvInvoke.Imshow("abc", matrix);
CvInvoke.WaitKey(0);
}
But as I said, I'm in doubt of as how to implement the algorithm. My plan was to rotate the pixels in "matrix" and store them in "newMatrix" but I do not know how to specify the X and Y values of my matrix.
Maybe someone can help me out here.
EDIT:
There has been suggested that this answer here: "How can I get and set pixel values of an EmguCV Mat image?" will be an answer to my question. But it is not. I know that I can do Math.Cos and Math.Sin but I do not know how to specify X and Y in my Matrix. I don't have problems accessing the Data in my Matrix.
If you're trying to rotate a point (x,y) about some point (cx,cy) given the matrix in the attached image:
class Program {
/**
* #param x coordinate of point want to rotate
* #param y coordinate of point want to rotate
* #param cx x coordinate of point you want to rotate about
* #param cy y coordinate of point you want to rotate about
* #return the result of rotation {x,y}
*/
static double[] rotate(double x, double y, double cx, double cy, double angle) {
double cos_a = Math.Cos(angle);
double sin_a = Math.Sin(angle);
// move to origin
x -= cx;
y -= cy;
// rotate and then move back
return new double[] {
x*cos_a - y*sin_a + cx,
x*sin_a + y*cos_a + cy
};
}
static void Main(string[] args) {
double x = 1;
double y = 0;
double a = Math.PI / 2;
double[] r = rotate(x, y, 0, 0, a);
Console.WriteLine("new x = " + r[0]);
Console.WriteLine("new y = " + r[1]);
}
}
I've been trying to recode a C++ DirectX code to C# that would help me with Drawing a perfect circle. Currently I have this code that i translated by myself:
private void Circle(int X, int Y, int radius, int numSides, Color color)
{
Vector2[] Line = new Vector2[128];
float Step = (float)(Math.PI * 2.0 / numSides);
int Count = 0;
for (float a = 0; a < Math.PI * 2.0; a += Step)
{
float X1 = (float)(radius * Math.Cos(a) + X);
float Y1 = (float)(radius * Math.Sin(a) + Y);
float X2 = (float)(radius * Math.Cos(a + Step) + X);
float Y2 = (float)(radius * Math.Sin(a + Step) + Y);
Line[Count].X = X1;
Line[Count].Y = Y1;
Line[Count + 1].X = X2;
Line[Count + 1].Y = Y2;
Count += 2;
}
line.Begin();
line.Draw(Line, color);
line.End();
}
The problem is that the circle is drawn but also a Line from a point in the circle to the left top corner, like this.
Don't iterate with a floating point variable. They might get imprecise during the iteration. In your case, the last step is probably very close behind the upper bound (instead of hitting it exactly). So it won't get calculated and left as the default (0, 0).
So use an integer iteration variable:
for (int i = 0; i < numSides; ++i)
{
float a = i * Step;
...
}
Then, you can also get rid of Count.
Furthermore, you should make your coordinate buffer dynamic:
Vector2[] Line = new Vector2[2 * numSides];