Uniform circular motion - c#

I need to implement a simple animation of a ball moving in uniform circular motion. I've tried several formulas and the following version seems the best so far.However, there are still 2 issues and I really can't figure out what's wrong.
First, a couple of seconds right after the program starts, the ball moves erratically. I think that the values for theta (the angle in radians) are not computed correctly, but I don't know why.
Secondly, the movement becomes more uniform after a while, but it seems to decrease over time.
The value for 'speed' indicates the number of seconds it takes to do a full revolution.
What I want is an uniform, correct circular movement (according to the value of speed) and without the jerkiness in the beginning.
My code so far:
public partial class ServerForm : Form
{
Stopwatch watch;
//Angular velocity
float angularVelocity;
//Angle
float theta = 20;
//Speed - time to complete a full revolution, in seconds
private float speed = 3;
//Circle center
private int centerX = 250;
private int centerY = 200;
//Circle radius
private float R = 120;
//Current position
private LocationData currentLocation;
public ServerForm()
{
InitializeComponent();
}
public void UpdateUI()
{
currentLocation.CoordX = (float)(centerX + Math.Cos(theta) * R);
currentLocation.CoordY = (float)(centerY + Math.Sin(theta) * R);
currentLocation.Speed = speed;
try
{
this.Invoke(new Action(() => { this.Invalidate(); }));
}
catch (Exception ex)
{
watch.Stop();
Application.Exit();
}
theta += (float)((angularVelocity * 1000 / watch.ElapsedMilliseconds));
//Console.Out.WriteLine("elapsed miliseconds: " + watch.ElapsedMilliseconds + " theta = " + theta);
}
protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics;
Brush color = new SolidBrush(Color.BlueViolet);
g.FillEllipse(color, currentLocation.CoordX, currentLocation.CoordY, 30, 30);
//Draw circle & center
g.DrawEllipse(new Pen(color), centerX, centerY, 5, 5);
float x = centerX - R;
float y = centerY - R;
float width = 2 * R;
float height = 2 * R;
g.DrawEllipse(new Pen(color), x, y, width, height);
base.OnPaint(e);
}
private void button1_Click(object sender, EventArgs e)
{
if (!String.IsNullOrEmpty(textSpeed.Text))
{
ResetValues(float.Parse(textSpeed.Text));
}
}
private void ResetValues(float newSpeed)
{
speed = newSpeed;
angularVelocity = (float)(2 * Math.PI / speed); // radians / sec
//Start at the top
currentLocation.CoordX = centerX;
currentLocation.CoordY = centerY - R;
theta = 90;
watch.Restart();
}
private void ServerForm_Load(object sender, EventArgs e)
{
watch = new Stopwatch();
timer1.Enabled = true;
timer1.Interval = 100;
timer1.Tick += timer1_Tick;
currentLocation = new LocationData();
ResetValues(speed);
}
void timer1_Tick(object sender, EventArgs e)
{
UpdateUI();
}
}
LocationData is just a class holding the coordinates & current speed.
Are the units for time & angular velocity (and the transformations to use miliseconds) correct?
I changed BackgroundWorker to Timer, but I still get that erratic motion and the movement slows down after a while.

Try using a System.Windows.Forms.Timer instead of a BackgroundWorker. I believe you'll get more consistent results. This is definitely not a good case for using a BackgroundWorker.
Here's a more-or-less complete solution. Note that I'm scaling the swing radius and the ball radius by the size of the Form.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
SetStyle(ControlStyles.Opaque, true);
SetStyle(ControlStyles.ResizeRedraw, true);
SetStyle(ControlStyles.DoubleBuffer, true);
SetStyle(ControlStyles.UserPaint, true);
}
private void Form1_Load(object sender, System.EventArgs e)
{
_stopwatch.Start();
}
private void Timer1_Tick(object sender, System.EventArgs e)
{
Invalidate();
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.Clear(BackColor);
const float rotationTime = 2000f;
var elapsedTime = (float) _stopwatch.ElapsedMilliseconds;
var swingRadius = Math.Min(ClientSize.Width, ClientSize.Height) / 4f;
var theta = Math.PI * 2f * elapsedTime / rotationTime;
var ballRadius = Math.Min(ClientSize.Width, ClientSize.Height) / 10f;
var ballCenterX = (float) ((ClientSize.Width / 2f) + (swingRadius * Math.Cos(theta)));
var ballCenterY = (float) ((ClientSize.Height / 2f) + (swingRadius * Math.Sin(theta)));
var ballLeft = ballCenterX - ballRadius;
var ballTop = ballCenterY - ballRadius;
var ballWidth = ballRadius * 2f;
var ballHeight = ballRadius * 2f;
e.Graphics.FillEllipse(Brushes.Red, ballLeft, ballTop, ballWidth, ballHeight);
e.Graphics.DrawEllipse(Pens.Black, ballLeft, ballTop, ballWidth, ballHeight);
}
private readonly Stopwatch _stopwatch = new Stopwatch();
}

Related

How to draw a custom slider control?

I created a slider bar user control but at run time when I move the slider to the left or right why it's not getting to the end or swallow?
In the user control designer I added a pictureBox control :
Then in the code I did :
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Extract
{
public partial class Slider : UserControl
{
public float Height;
public float Min = 0.0f;
public float Max = 1.0f;
private float defaultValue = 0.1f;
public Slider()
{
InitializeComponent();
}
private void sliderControl_Paint(object sender, PaintEventArgs e)
{
float bar_size = 0.45f;
float x = Bar(defaultValue);
int y = (int)(sliderControl.Height * bar_size);
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
e.Graphics.FillRectangle(Brushes.DimGray, 0, y, sliderControl.Width, y / 2);
e.Graphics.FillRectangle(Brushes.Red, 0, y, x, sliderControl.Height - 2 * y);
using (Pen pen = new Pen(Color.Black, 8))
{
e.Graphics.FillRectangle(Brushes.Red, 0, y, x, y / 2);
FillCircle(e.Graphics, Brushes.Red, x, y + y / 4, y / 2);
}
using (Pen pen = new Pen(Color.White, 5))
{
DrawCircle(e.Graphics, pen, x, y + y / 4, y/ 2);
}
}
public static void DrawCircle(Graphics g, Pen pen,
float centerX, float centerY, float radius)
{
g.DrawEllipse(pen, centerX - radius, centerY - radius,
radius + radius, radius + radius);
}
public static void FillCircle(Graphics g, Brush brush,
float centerX, float centerY, float radius)
{
g.FillEllipse(brush, centerX - radius, centerY - radius,
radius + radius, radius + radius);
}
private float Bar(float value)
{
return (sliderControl.Width - 24) * (value - Min) / (float)(Max - Min);
}
private void Thumb(float value)
{
if (value < Min) value = Min;
if (value > Max) value = Max;
defaultValue = value;
sliderControl.Refresh();
}
private float SliderWidth(int x)
{
return Min + (Max - Min) * x / (float)(sliderControl.Width);
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
MaintainPictureBoxSize();
}
private void MaintainPictureBoxSize()
{
sliderControl.SizeMode = PictureBoxSizeMode.Normal;
sliderControl.Location = new Point();
sliderControl.Size = new Size();
var clientSize = this.ClientSize;
if (sliderControl.Image == null)
sliderControl.Size = clientSize;
else
{
Size s = sliderControl.Image.Size;
sliderControl.Size = new Size(
clientSize.Width > s.Width ? clientSize.Width : s.Width,
clientSize.Height > s.Height ? clientSize.Height : s.Height);
}
}
bool mouse = false;
private void sliderControl_MouseDown(object sender, MouseEventArgs e)
{
mouse = true;
Thumb(SliderWidth(e.X));
}
private void sliderControl_MouseMove(object sender, MouseEventArgs e)
{
if (!mouse) return;
Thumb(SliderWidth(e.X));
}
private void sliderControl_MouseUp(object sender, MouseEventArgs e)
{
mouse = false;
}
}
}
When I drag the control to the form1 designer and then running the application then when I drag the slider for example to the left or to the right the circle of the slider is partly swallow.
and if I resize the control in form1 designer to be smaller and then running the application to left it swallow as before but to the right it's not getting to the end at all.
The easiest way to explain it is to show an image:
Now, inside the picture box, imagine the thumb circle to be in the left most and right most positions. This means that the bar must start at x = radius and that the width of the bar must be the width of the picture box minus twice the radius.
Everything must be drawn inside the picture box (dotted line). But this needs not to be in a PictureBox placed on a UserControl. Let's derive the slider from Control instead.
public class Slider : Control
{
...
}
Now, after having compiled this code for the first time, this Slider automatically appears in the Toolbox window and is ready to be placed on a form in the forms designer.
Since we want to be able to set its properties in the properties window and we want to be able to read the current value after sliding, let's add an event and some properties.
public event EventHandler ValueChanged;
private float _min = 0.0f;
public float Min
{
get => _min;
set {
_min = value;
RecalculateParameters();
}
}
private float _max = 1.0f;
public float Max
{
get => _max;
set {
_max = value;
RecalculateParameters();
}
}
private float _value = 0.3f;
public float Value
{
get => _value;
set {
_value = value;
ValueChanged?.Invoke(this, EventArgs.Empty);
RecalculateParameters();
}
}
This requires some fields and the RecalculateParameters method.
private float _radius;
private PointF _thumbPos;
private SizeF _barSize;
private PointF _barPos;
private void RecalculateParameters()
{
_radius = 0.5f * ClientSize.Height;
_barSize = new SizeF(ClientSize.Width - 2f * _radius, 0.5f * ClientSize.Height);
_barPos = new PointF(_radius, (ClientSize.Height - _barSize.Height) / 2);
_thumbPos = new PointF(
_barSize.Width / (Max - Min) * Value + _barPos.X,
_barPos.Y + 0.5f * _barSize.Height);
Invalidate();
}
Inside this derived control we override the event handlers (On... methods) instead of subscribing to the events:
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
e.Graphics.FillRectangle(Brushes.DimGray,
_barPos.X, _barPos.Y, _barSize.Width, _barSize.Height);
e.Graphics.FillRectangle(Brushes.Red,
_barPos.X, _barPos.Y, _thumbPos.X - _barPos.X, _barSize.Height);
e.Graphics.FillCircle(Brushes.White, _thumbPos.X, _thumbPos.Y, _radius);
e.Graphics.FillCircle(Brushes.Red, _thumbPos.X, _thumbPos.Y, 0.7f * _radius);
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
RecalculateParameters();
}
Now let's compile this code and let's add a slider to a form. See how we can resize it in the designer.
Note also, that in the properties window we see the new Slider properties Max, Min and Value in the "Misc" section. We can change them here and the thumb position is automatically updated.
We still need the code to enable moving the slider. When we click on the thumb, we might have clicked a bit off its center. It feels natural to keep this offset while moving the mouse. Therefore, we store this difference in a variable _delta.
bool _moving = false;
SizeF _delta;
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
// Difference between tumb and mouse position.
_delta = new SizeF(e.Location.X - _thumbPos.X, e.Location.Y - _thumbPos.Y);
if (_delta.Width * _delta.Width + _delta.Height * _delta.Height <= _radius * _radius) {
// Clicking inside thumb.
_moving = true;
}
}
We also calculate the distance of the mouse position to the thumb position in OnMouseDown by using the Pythagorean theorem. Only if the mouse is inside the thumb, we initiate moving the thumb by setting _moving = true;
In OnMouseMove we calculate and set the new Value. This automatically triggers recalculating the parameters and redraws the slider.
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
if (_moving) {
float thumbX = e.Location.X - _delta.Width;
if (thumbX < _barPos.X) {
thumbX = _barPos.X;
} else if (thumbX > _barPos.X + _barSize.Width) {
thumbX = _barPos.X + _barSize.Width;
}
Value = (thumbX - _barPos.X) * (Max - Min) / _barSize.Width;
}
}
protected override void OnMouseUp(MouseEventArgs e)
{
base.OnMouseUp(e);
_moving = false;
}
We can test the slider by adding a TextBox to the form and responding to the ValueChanged event. We can add an event handler by switching the properties window to "Events" by clicking on the flash symbol and then double click on ValueChanged in the "Misc" section.
private void Slider1_ValueChanged(object sender, EventArgs e)
{
textBox1.Text = slider1.Value.ToString();
}
Now, when we move the thumb, the text box displays the values.
Here again the whole code of the slider (using C# 10.0 file scoped namespaces):
using System.Drawing.Drawing2D;
namespace WinFormsSliderBar;
public class Slider : Control
{
private float _radius;
private PointF _thumbPos;
private SizeF _barSize;
private PointF _barPos;
public event EventHandler ValueChanged;
public Slider()
{
// This reduces flicker
DoubleBuffered = true;
}
private float _min = 0.0f;
public float Min
{
get => _min;
set {
_min = value;
RecalculateParameters();
}
}
private float _max = 1.0f;
public float Max
{
get => _max;
set {
_max = value;
RecalculateParameters();
}
}
private float _value = 0.3f;
public float Value
{
get => _value;
set {
_value = value;
ValueChanged?.Invoke(this, EventArgs.Empty);
RecalculateParameters();
}
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
e.Graphics.FillRectangle(Brushes.DimGray,
_barPos.X, _barPos.Y, _barSize.Width, _barSize.Height);
e.Graphics.FillRectangle(Brushes.Red,
_barPos.X, _barPos.Y, _thumbPos.X - _barPos.X, _barSize.Height);
e.Graphics.FillCircle(Brushes.White, _thumbPos.X, _thumbPos.Y, _radius);
e.Graphics.FillCircle(Brushes.Red, _thumbPos.X, _thumbPos.Y, 0.7f * _radius);
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
RecalculateParameters();
}
private void RecalculateParameters()
{
_radius = 0.5f * ClientSize.Height;
_barSize = new SizeF(ClientSize.Width - 2f * _radius, 0.5f * ClientSize.Height);
_barPos = new PointF(_radius, (ClientSize.Height - _barSize.Height) / 2);
_thumbPos = new PointF(
_barSize.Width / (Max - Min) * Value + _barPos.X,
_barPos.Y + 0.5f * _barSize.Height);
Invalidate();
}
bool _moving = false;
SizeF _delta;
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
// Difference between tumb and mouse position.
_delta = new SizeF(e.Location.X - _thumbPos.X, e.Location.Y - _thumbPos.Y);
if (_delta.Width * _delta.Width + _delta.Height * _delta.Height <= _radius * _radius) {
// Clicking inside thumb.
_moving = true;
}
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
if (_moving) {
float thumbX = e.Location.X - _delta.Width;
if (thumbX < _barPos.X) {
thumbX = _barPos.X;
} else if (thumbX > _barPos.X + _barSize.Width) {
thumbX = _barPos.X + _barSize.Width;
}
Value = (thumbX - _barPos.X) * (Max - Min) / _barSize.Width;
}
}
protected override void OnMouseUp(MouseEventArgs e)
{
base.OnMouseUp(e);
_moving = false;
}
}
and the graphic extensions for drawing circles:
namespace WinFormsSliderBar;
public static class GraphicsExtensions
{
public static void DrawCircle(this Graphics g, Pen pen,
float centerX, float centerY, float radius)
{
g.DrawEllipse(pen, centerX - radius, centerY - radius,
radius + radius, radius + radius);
}
public static void FillCircle(this Graphics g, Brush brush,
float centerX, float centerY, float radius)
{
g.FillEllipse(brush, centerX - radius, centerY - radius,
radius + radius, radius + radius);
}
}

Win2d UWP fluid animation canvas line

I have to get a fluid animation of a line drawn with Win2d. Below is the code that simulates my problem.
MainPage.xaml:
<canvas:CanvasAnimatedControl x:Name="animatedControl"
Margin="0,30,0,0"
Height="500"
Draw="OnDraw"/>
MainPage.xaml.cs:
private void OnDraw(Microsoft.Graphics.Canvas.UI.Xaml.ICanvasAnimatedControl sender, Microsoft.Graphics.Canvas.UI.Xaml.CanvasAnimatedDrawEventArgs args)
{
CanvasDrawingSession ds = args.DrawingSession;
double height = sender.Size.Height;
double width = sender.Size.Width;
Random random = new Random();
double pointHeigth = random.Next(0, 300);
var point1 = new Vector2((float)(width / 2), (float)height);
var point2 = new Vector2((float)(width / 2), (float)height - (float)pointHeigth);
CanvasSolidColorBrush brush = new CanvasSolidColorBrush(sender, Colors.Green);
ds.DrawLine(point1, point2, brush, (float)10);
}
In this way I get a flounce animation how can I get a smooth animation of the line?
I state that I have to use the canvas with win2d.
Thanks in advance.
UPDATE (My code that doesn't work)
private Vector2 _point1;
private Vector2 _point2;
private Vector2 _targetPoint;
private TimeSpan _animationElapsedTime = TimeSpan.FromMilliseconds(100);
public MainPage()
{
this.InitializeComponent();
}
private void OnDraw(Microsoft.Graphics.Canvas.UI.Xaml.ICanvasAnimatedControl sender, Microsoft.Graphics.Canvas.UI.Xaml.CanvasAnimatedDrawEventArgs args)
{
CanvasDrawingSession ds = args.DrawingSession;
double height = sender.Size.Height;
double width = sender.Size.Width;
Random random = new Random();
double pointHeigth = random.Next(0, 300);
var _point1 = new Vector2((float)(width / 2), (float)height);
var _point2 = new Vector2((float)(width / 2), (float)height - (float)pointHeigth);
CanvasSolidColorBrush brush = new CanvasSolidColorBrush(sender, Colors.Green);
ds.DrawLine(_point1, _point2, brush, (float)10);
}
private Vector2 Tween(Vector2 sourcePoint, Vector2 targetPoint, float percentage)
{
return new Vector2(targetPoint.X + (targetPoint.X - sourcePoint.X) * percentage, targetPoint.Y + (targetPoint.Y - sourcePoint.Y) * percentage);
}
private void OnUpdate(Microsoft.Graphics.Canvas.UI.Xaml.ICanvasAnimatedControl sender, Microsoft.Graphics.Canvas.UI.Xaml.CanvasAnimatedUpdateEventArgs args)
{
_animationElapsedTime += args.Timing.ElapsedTime;
var percentage = Math.Min(_animationElapsedTime.TotalSeconds / 5.0f, 1f);
_point2 = Tween(_point1, _targetPoint, (float)percentage);
}
UPDATE 2 MyCode:
double previuosData = 0;
bool isDraw = false;
public MainPage()
{
this.InitializeComponent();
}
private void OnDraw(Microsoft.Graphics.Canvas.UI.Xaml.ICanvasAnimatedControl sender, Microsoft.Graphics.Canvas.UI.Xaml.CanvasAnimatedDrawEventArgs args)
{
if (isDraw)
return;
CanvasDrawingSession ds = args.DrawingSession;
double height = sender.Size.Height;
double width = sender.Size.Width;
CanvasSolidColorBrush brush = new CanvasSolidColorBrush(sender, Colors.Green);
Random random = new Random();
double pointHeigth = random.Next(0, 500);
var point1 = new Vector2((float)(width / 2), (float)height);
if (pointHeigth > previuosData)
{
for (double a = previuosData; a <= pointHeigth; a++)
{
isDraw = true;
var point2 = new Vector2((float)(width / 2), (float)height - (float)a);
ds.DrawLine(point1, point2, brush, (float)10);
}
isDraw = false;
}
else
{
for (double a = previuosData; a >= pointHeigth; a--)
{
isDraw = true;
var point2 = new Vector2((float)(width / 2), (float)height - (float)a);
ds.DrawLine(point1, point2, brush, (float)10);
}
isDraw = false;
}
previuosData = pointHeigth;
}
in this way I make the animation a little more fluid but with the for loop it's not enough how could I improve?
It seems you are using Random and that generates a random number from the given range in each single frame. So it means 60 times per second the second point will jump randomly up and down.
Compare to something like the following:
private bool _adding = true;
private int _currentHeightOffset = 0;
private void OnDraw(Microsoft.Graphics.Canvas.UI.Xaml.ICanvasAnimatedControl sender, Microsoft.Graphics.Canvas.UI.Xaml.CanvasAnimatedDrawEventArgs args)
{
CanvasDrawingSession ds = args.DrawingSession;
double height = sender.Size.Height;
double width = sender.Size.Width;
var point1 = new Vector2((float)(width / 2), (float)height);
var point2 = new Vector2((float)(width / 2), (float)height - (float)_currentHeight);
CanvasSolidColorBrush brush = new CanvasSolidColorBrush(sender, Colors.Green);
ds.DrawLine(point1, point2, brush, (float)10);
if (_adding)
{
_currentHeightOffset++;
}
else
{
_currentHeightOffset--;
}
if (_currentHeightOffset == 0 || _currentHeightOffset == 300 )
{
_adding = !_adding;
}
}
Now in this example, I am moving the point pixel by pixel in each frame until it reaches 300 and then go back to 0. So the result should be a line which appears to be fixed in point1 and moving and stretching up and down.
The key is, that the values are no longer random, but predictable from frame to frame.
Update
As I discussed in comments, you could do "tweening" effect between two points.
We would use the following method:
private Vector2 Tween(Vector2 sourcePoint, Vector2 targetPoint, float percentage)
{
return new Vector2(
targetPoint.X + (targetPoint.X - sourcePoint.X)*percentage,
targetPoint.Y + (targetPoint.Y - sourcePoint.Y)*percentage)
}
Now if you want to animate between these two points for 5 seconds, you could do something like:
private Vector2 _point2;
private TimeSpan _animationElapsedTime = TimeSpan.Zero;
private void OnUpdate(Microsoft.Graphics.Canvas.UI.Xaml.ICanvasAnimatedControl sender,
Microsoft.Graphics.Canvas.UI.Xaml.CanvasAnimatedUpdateEventArgs args)
{
_animationElapsedTime += args.Timing.ElapsedTime;
var percentage = Math.Min(_animationElapsedTime.TotalSeconds / 5.0f, 1f);
_point2 = Tween(_point1, _targetPoint, percentage);
}
This is just a quick sketch of how the code would look like, however, it the important points - once you select the target point (using random for example), you need to decide how long do you want the animation to take and then move the second point closer and closer to the target until the time is over.

Cannot Draw Line from Within Class - Why won't it draw?

I am new to C# and I am trying to use OOP and classes. I am trying to draw a simple sinewave and an axis line (X axis). I have gotten similar code to in the "main - Form1" but I cannot get it to draw in the form from within a class. It draws nothing! The code does compile.
What am I missing? What can I do better?
I call the class from a button click -
Drawclick(object sender, EventArgs e)
{ DrawSine Sine1 = new DrawSine(950);
}
Here is the class
class DrawSine:Form1
{
private float sinex=0;//set up variables for sine
private float countx = 0;
private float siney = 0;
private float sinex1=0;
private float siney1=0;
public float offset;
public float scalex;
public float scaley;
public DrawSine(int widthG)
{
Graphics Graphsine = this.CreateGraphics();//declare sine
Graphics Graphline = this.CreateGraphics();//declare axis
Pen Graphpen = new Pen(Brushes.Black, 1.0F);
Graphline.DrawLine(Graphpen, 0, 200, widthG, 200);//draw line
0,200 to end of form,200
int WidthG = widthG;
do //draw sine wave left to right
{
sinex += scalex * 6.28f / widthG;
siney = (float)Math.Sin(sinex);
Graphsine.DrawLine(Graphpen, sinex1, siney1 + offset,
countx, siney * 100 + offset);
sinex1 = sinex;
sinex1 = siney * 100;
countx += 1;
}
while (countx <= widthG);
}
}
This may be drawing something on the form but you cannot see because soon after the Load event, there will be a PaintBackground event and a PaintEvent as the form is rendered.
These events will effectively erase everything on the form.
2 Remedies:
Encapsulate the above draw code in a method (Let's say DrawSine())
One, Override OnPaint event and write your code there. Your painting will remain intact on form resize or form redraw.
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
DrawSine(e, 950);
}
Two, use form Shown event
private void Form1_Shown(object sender, EventArgs e)
{
DrawSine(null, 950);
}
Your original method:
public DrawSine(Graphics g, int widthG)
{
Graphics Graphsine = g??this.CreateGraphics();//declare sine
Graphics Graphline = g??this.CreateGraphics();//declare axis
Pen Graphpen = new Pen(Brushes.Black, 1.0F);
Graphline.DrawLine(Graphpen, 0, 200, widthG, 200);//draw line
//0,200 to end of form,200
int WidthG = widthG;
do //draw sine wave left to right
{
sinex += scalex * 6.28f / widthG;
siney = (float)Math.Sin(sinex);
Graphsine.DrawLine(Graphpen, sinex1, siney1 + offset,
countx, siney * 100 + offset);
sinex1 = sinex;
sinex1 = siney * 100;
countx += 1;
}
while (countx <= widthG);
}

how to animate custom property in xamarin.android

I have drawn one line.I have added AngleProperty in my view. Using that angle property i need to animate that line to that angle.
Here is my View in which line is drawn
public class DrawView : View
{
Paint paint = new Paint();
private double mvalue = 90;
public double Angle
{
get { return mvalue; }
set {
ObjectAnimator anim = ObjectAnimator.OfFloat(this, "Angle", (float)this.Angle, (float)value);
anim.SetDuration(500);
anim.Start();
mvalue = value;
}
}
public DrawView(Context context):base(context)
{
paint.Color = Color.Green;
}
protected override void OnDraw(Canvas canvas)
{
base.OnDraw(canvas);
Value = Angle* Math.Pi / 180;
var startX = 300;
var startY = 300;
var endX = 500 + 40 * Math.Sin(Value);
var endY = 500 + 40 * Math.Cos(Value);
canvas.DrawLine(startX, startY, (float)endX, (float)endY, paint);
}
}
In main Activity, i have added button in which angle is given,
public class MainActivity : Activity
{
DrawView drawview;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
drawview = new DrawView(this);
Button b = new Button(this);
b.SetHeight(50);
b.SetWidth(50);
b.Click += B_Click;
LinearLayout lay = new LinearLayout(this);
lay.AddView(b);
lay.AddView(drawview);
}
private void B_Click(object sender, System.EventArgs e)
{
drawview.Angle= 180;
}
}
Anyone please suggest how to animate that line to certain angle
Logic is correct, but there are several problems. Since you have defined a Angle property, you should use the Angle property to control the Animation instead of using Value property, then it can work fine like this. For more information, you could read the document.
protected override void OnDraw(Canvas canvas)
{
base.OnDraw(canvas);
Angle = Angle * Math.PI / 180;
var startX = 300;
var startY = 300;
var endX = 500 + 40 * Math.Sin(Angle);
var endY = 500 + 40 * Math.Cos(Angle);
canvas.DrawLine(startX, startY, (float)endX, (float)endY, paint);
}
When click a button, start the Animation:
private void B_Click(object sender, System.EventArgs e)
{
//certain angle decided by EditText's text
Int32 number = int.Parse(et.Text.ToString());
drawview.Angle = number;
}

C# - Rotating polygon randomly

I'm trying to rotate polygon randomly using timer. I got to draw regular polygons and rotate it to one direction. But I'm not sure about how to rotate polygon to random direction using angle or timer interval.
My Code is below:
int sides = 5;
Graphics g = e.Graphics;
nPoints = CalculateVertices(sides, radius, angle, center);
g.DrawPolygon(navypen, nPoints);
g.FillPolygon(BlueBrush, nPoints);
Point center = new Point(ClientSize.Width / 2, ClientSize.Height / 2);
for(int i = 0; i < sides; i++) {
g.DrawLine(new Pen(Color.Navy), center.X, center.Y, nPoints[i].X, nPoints[i].Y);
}
private PointF[] CalculateVertices(int sides, int radius, float startingAngle, Point center)
{
if (sides < 3) {
sides = 3;
}
List<PointF> points = new List<PointF>();
float step = 360.0f / sides;
float angle = startingAngle; //starting angle
for (double i = startingAngle; i < startingAngle + 360.0; i += step) //go in a circle
{
points.Add(DegreesToXY(angle, radius, center));
angle += step;
}
return points.ToArray();
}
private PointF DegreesToXY(float degrees, float radius, Point origin)
{
PointF xy = new PointF();
double radians = degrees * Math.PI / 180.0;
xy.X = (int)(Math.Cos(radians) * radius + origin.X);
xy.Y = (int)(Math.Sin(-radians) * radius + origin.Y);
return xy;
}
private void timer2_Tick(object sender, EventArgs e){
angle += 1;
angle_tri -= 1;
Invalidate();
}
Here is an example of drawing a list of points rotated with varying speeds, both angular and and timing..:
First a few variables:
Random rnd = new Random();
float angle = 0f;
List<Point> points = new List<Point>();
Then a Tick with varying speed and a varying angle:
private void timer1_Tick(object sender, EventArgs e)
{
angle += rnd.Next(0, 33)/ 10f;
timer1.Interval = rnd.Next(100) + 15;
pictureBox5.Invalidate();
}
Here is the Paint event of a PictureBox, which is DoubleBuffered, so it won't flicker..:
private void pictureBox5_Paint(object sender, PaintEventArgs e)
{
if (points.Count > 1)
{
Point center = new Point(
(points.Select(x => x.X).Max() + points.Select(x => x.X).Min()) / 2,
(points.Select(x => x.Y).Max() + points.Select(x => x.Y).Min()) / 2);
e.Graphics.TranslateTransform(center.X, center.Y);
e.Graphics.RotateTransform(angle);
e.Graphics.TranslateTransform(-center.X, -center.Y);
e.Graphics.DrawPolygon(Pens.DarkGreen, points.ToArray());
}
}
Note that due to the weird speed changes this is anything but smooth; you would have to find better algorithms for them than mere randomness..

Categories