I've searched and not found this question answered on SO, so I'm asking it here directly.
Does anyone have a clean method to create an infinitely scrolling gradient background? (the gradient shifts, so you can follow the colors from one side/corner to the other)
I've done this in VB like 15 years ago, but it's been so long since I touched VB it's all greek to me.
Assuming someone has done something like this in C# before-- Think demo scene kind of animation.
The VB code snippet is from a working form background I did many years ago, it doesn't scroll so much as bounce back and forth from edge to edge.
Private Sub picCanvas_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles Panel1.Paint
Dim rect As New Rectangle(-10, -10, Me.ClientSize.Width + 20, Me.ClientSize.Height + 20)
Dim halfw As Integer = CType(Me.ClientSize.Width, Integer)
Dim br As New LinearGradientBrush(New Point(-120, 500), New Point(Me.ClientSize.Width + 120, 0), Color.Red, Color.Blue)
Dim color_blend As New ColorBlend
color_blend.Colors = New Color() {Color.Black, Color.Purple, Color.Teal, Color.Purple, Color.Black}
m_Theta += m_Delta
color_blend.Positions = New Single() {0, 0.01, m_Theta, 0.99, 1}
br.InterpolationColors = color_blend
e.Graphics.FillRectangle(br, rect)
br.Dispose()
If (m_Theta > 0.75) Or (m_Theta < 0.25) Then m_Delta = -m_Delta
End Sub
I would greatly appreciate any help in getting this kind of thing to work in WinForms using only GDI and brushes, no XML or anything please ^^/
I'm not exactly sure this is what you're trying to do, anyway, from the semi-pseudo code presented here, it appears you want to shift the position of a gradient fill along an axis.
It appears the fill is meant to be inclined, so I've added means to determine a rotation angle.
I've kept the LinearGradientBrush to generate the blended fill, though the combination of GraphicsPath and PathGradientBrush is probably more flexible.
To move the gradient fill, I've used a standard System.Windows.Forms.Timer. It's used to translate the fill, incrementing a value that is then set to the translation components of a Matrix in the OnPaint override of a double-buffered Form used as canvas (of course, you can use a PictureBox instead)
The Matrix is also used to rotate the fill, in case it's needed
The Timer's Tick handler also verifies other conditions (bool Fields), that can be used to alter the fill:
useThetaShift enables semi-dynamic motions of the blend intervals (the Position Property)
useTriangular enables and alternate blending feature, generated by the SetBlendTriangularShape() method, which considers only the starting and ending Colors of the LinearGradientBrush and defines the center point of the Colors' fall-off
The sample Form shown here can also be set to auto-scroll, the blending is extended to the DisplayRectangle
The blend is animated also when a modal Dialog is shown (you mentioned an About Window...)
internal class SomeForm : Form {
private System.Windows.Forms.Timer gradientTimer = null;
public SomeForm() {
InitializeComponent();
if (components is null) components = new Container();
ResizeRedraw = true;
startColor = blendColors[0];
meanColor = blendColors[1];
endColor = blendColors[blendColors.Length - 1];
gradientTimer = new System.Windows.Forms.Timer(components) { Interval = 100 };
gradientTimer.Tick += GradientTimer_Tick;
gradientTimer.Start();
}
float theta = .0f;
float delta = .005f;
float tringularShift = .25f;
float tringularShiftDelta = .015f;
float speed = 7.5f;
float rotation = 0f;
private Color[] blendColors = new[]{
Color.Black, Color.Purple, Color.Teal, Color.Purple, Color.Black
};
Color startColor = Color.Empty;
Color endColor = Color.Empty;
Color meanColor = Color.Empty;
PointF translateMx = PointF.Empty;
bool useThetaShift = false;
bool useTriangular = false;
private void GradientTimer_Tick(object sender, EventArgs e)
{
if (useTriangular) {
tringularShift += tringularShiftDelta;
tringularShift = Math.Max(Math.Min(tringularShift, 1.0f), .35f);
if ((tringularShift >= 1.0f) | (tringularShift <= .35f)) tringularShiftDelta*= -1;
}
if (useThetaShift) {
theta += delta;
theta = Math.Max(Math.Min(theta, .15f), 0f);
if ((theta >= .15f) | (theta <= 0f)) delta*= -1;
}
translateMx = PointF.Add(translateMx, new SizeF(speed, speed));
if (Math.Abs(translateMx.X) >= short.MaxValue) translateMx = PointF.Empty;
Invalidate();
}
protected override void OnPaint(PaintEventArgs e)
{
var display = DisplayRectangle;
using (var mx = new Matrix(1f, 0f, 0f, 1f, translateMx.X, translateMx.Y))
using (var brush = new LinearGradientBrush(display, startColor, endColor, rotation)) {
var colorBlend = new ColorBlend(blendColors.Length) {
Colors = blendColors,
Positions = new float[] { .0f, .25f + theta, .5f + theta, .75f + theta, 1.0f },
};
brush.InterpolationColors = colorBlend;
mx.Rotate(rotation);
brush.Transform = mx;
if (useTriangular) brush.SetBlendTriangularShape(.5f, tringularShift);
e.Graphics.FillRectangle(brush, display);
}
base.OnPaint(e);
}
protected override void OnFormClosing(FormClosingEventArgs e) {
// Move to OnFormClosed() if this action can be canceled
gradientTimer.Stop();
base.OnFormClosing(e);
}
}
I cannot post an animation here, because of the size. You can see how it work directly on Imgur:
Animated LinearGradientPath
I am working on a project in which I need to use recursion to draw a Hilbert Curve fractal in a Windows Forms application in C#. I must use GDI+ graphics for this, but I am new to GDI+ graphics. Below is my entire code for the Form class that actually draws the curve. At the end of this post, I have included photos demonstrating my erroneous output and the expected output.
The DrawRelative() function is supposed to draw the next line segment from the current [x,y] coordinates to the new [x,y] coordinates, which are calculated by adding the xDistance and yDistance values passed into the DrawRelative() function to the xCurrent and yCurrent class properties.
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 HilbertCurveFractal
{
public partial class FractalDisplay : Form
{
public int MaxDepth { get; set; }
public int CurveType { get; set; }
public int xCurrent { get; set; }
public int yCurrent { get; set; }
public int xLength { get; set; }
public int yLength { get; set; }
public FractalDisplay(int DepthValue, int SelectedCurve)
{
InitializeComponent();
MaxDepth = DepthValue;
CurveType = SelectedCurve;
xCurrent = 250;
yCurrent = 250;
xLength = 0;
yLength = 2;
}
private void FractalDisplay_Load(object sender, EventArgs e)
{
this.DoubleBuffered = true;
if (CurveType == 1) // Run the Hilbert Curve Generator
{
GenerateHilbertCurve(MaxDepth, xLength, yLength);
}
else if (CurveType == 2) // Run the Koch Curve Generator
{
}
else if (CurveType == 3) // Run the Sierpinski Curve Generator
{
}
else
{
MessageBox.Show("Error! - No Curve Type Selected. Ending Program.");
Application.Exit();
}
}
private void GenerateHilbertCurve(int depth, int xDistance, int yDistance)
{
//if (depth == 0) // Base Case
//{
// return;
//}
//else { }
if (depth > 0)
{
GenerateHilbertCurve(depth - 1, yDistance, xDistance);
}
else { }
// Draw Part of Curve Here
DrawRelative(xDistance, yDistance);
if (depth > 0)
{
GenerateHilbertCurve(depth - 1, xDistance, yDistance);
}
else { }
// Draw Part of Curve Here
DrawRelative(yDistance, xDistance);
if (depth > 0)
{
GenerateHilbertCurve(depth - 1, xDistance, yDistance);
}
else { }
// Draw Part of Curve Here
DrawRelative((- 1 * xDistance), (-1 * yDistance));
if (depth > 0)
{
GenerateHilbertCurve(depth - 1, (-1 * yDistance), (-1 * xDistance));
}
else { }
}
// Create a New Paint Event Handler
private void DrawRelative(int xDistance, int yDistance)
{
xLength = xDistance;
yLength = yDistance;
this.Paint += new PaintEventHandler(HilbertCurve_Paint);
}
// Perform the Actual Drawing
private void HilbertCurve_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
{
// Discover where the new X and Y points will be
int xNew, yNew;
xNew = xCurrent + xLength;
yNew = yCurrent + yLength;
// Paint from the current position of X and Y to the new positions of X and Y
e.Graphics.DrawLine(Pens.Red, xCurrent, yCurrent, xNew, yNew);
// Update the Current Location of X and Y
xCurrent = xNew;
yCurrent = yNew;
}
}
}
The first photo (below) is the incorrect output from the Hilbert Curve function given a MaxDepth of 1.
The second photo (below) represents what I should be getting from this set of functions (given a MaxDepth value of 1 passed in).
Because it seems like the algorithm for recursion is coded correctly, I suspect that I am not using the GDI+ graphics in the proper way, or my class properties are being updated/set incorrectly somewhere in the recursive calls. What can I do to fix my drawing algorithm? Thank you in advance.
To be honest, I didn't initially understand the implementation you have for generating the points for the Hilbert curve. I'm familiar with a couple of different approaches, neither of which look like that.
But, that's an entirely different question. Your main issue at hand is really just that you don't understand how the drawing mechanism in Winforms works. Briefly: there's a Paint event, which your code should handle by drawing what needs to be drawn. Subscribing to the Paint event doesn't cause anything to happen; it's just a way of registering to be notified when drawing is supposed to occur.
Typically, one would use the Designer to subscribe to the event, by navigating to the "Events" tab of the Properties pane for an object in the Designer (e.g. your Form) and selecting the appropriate event handler (or double-clicking in the empty box next to the event to have the Designer automatically insert an empty handler for you to fill in). You can also, when handling the Paint event in your own object, simply override the OnPaint() method.
In either case, the correct technique is to establish the prerequisites for drawing, then call Invalidate() which causes the framework to then raise the Paint event, at which time you can actually draw what you want to draw.
Note that between commenter TaW and me, we have suggested two different approaches to drawing: I suggested pre-computing all of the necessary data for drawing, and then just draw that when the Paint event is raised; TaW has suggested calling the recursive method from the Paint event handler, and drawing directly as you traverse the recursive algorithm.
Both techniques are fine, but of course there are pros and cons to either, having mostly to do with the classic trade-off of time and space. With the former technique, the cost to generate the curve is incurred only once, when the parameters for the curve change. Drawing occurs more quickly, because all the code has to do is draw the pre-existing data. With the latter technique, there is no need to store the data, as each new point generated is used immediately, but of course this means all of the points have to be regenerated every time the window is redrawn.
For this particular application, in practice I don't think it matters much. At typical screen resolutions, you won't be able to make out the features of the curve long before you start to hit the limits of data storage for the points to draw. Similarly, the execution of the algorithm is so fast that there's really no harm in recalculating the points each time the window needs to be redrawn. Just keep in mind that these are trade-offs you may have to judge more closely in other scenarios.
Okay, so what's all that mean? Well, when I converted it to something that used the Graphics class correctly, I couldn't get your implementation to draw a Hilbert curve, so I changed that part of the code to use an implementation I know works. You can find a detailed discussion on how this particular implementation works here: Hilbert Curve
Concepts & Implementation
Below, I have provided two different versions of that particular Hilbert curve implementation, the first using the "retained" approach (i.e. generate the data, then draw it), and the second using the "immediate" approach (i.e. generate the data every time you want to draw the window, as the drawing is occurring):
"Retained" method:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
DoubleBuffered = true;
}
private PointF[] _points;
private void FractalDisplay_Load(object sender, EventArgs e)
{
Redraw();
}
private void Redraw()
{
List<PointF> points = new List<PointF>();
GenerateHilbert(0, 0, 1, 0, 0, 1, (int)numericUpDown1.Value, points);
_points = points.ToArray();
Invalidate();
}
private void GenerateHilbert(PointF origin, float xi, float xj, float yi, float yj, int depth, List<PointF> points)
{
if (depth <= 0)
{
PointF current = origin + new SizeF((xi + yi) / 2, (xj + yj) / 2);
points.Add(current);
}
else
{
GenerateHilbert(origin, yi / 2, yj / 2, xi / 2, xj / 2, depth - 1, points);
GenerateHilbert(origin + new SizeF(xi / 2, xj / 2), xi / 2, xj / 2, yi / 2, yj / 2, depth - 1, points);
GenerateHilbert(origin + new SizeF(xi / 2 + yi / 2, xj / 2 + yj / 2), xi / 2, xj / 2, yi / 2, yj / 2, depth - 1, points);
GenerateHilbert(origin + new SizeF(xi / 2 + yi, xj / 2 + yj), -yi / 2, -yj / 2, -xi / 2, -xj / 2, depth - 1, points);
}
}
// Perform the Actual Drawing
private void HilbertCurve_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
{
if (_points != null)
{
float scale = Math.Min(ClientSize.Width, ClientSize.Height);
e.Graphics.ScaleTransform(scale, scale);
using (Pen pen = new Pen(Color.Red, 1 / scale))
{
e.Graphics.DrawLines(pen, _points);
}
}
}
private void numericUpDown1_ValueChanged(object sender, EventArgs e)
{
Redraw();
}
protected override void OnClientSizeChanged(EventArgs e)
{
base.OnClientSizeChanged(e);
Invalidate();
}
}
"Immediate" method:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
DoubleBuffered = true;
}
private void Redraw()
{
Invalidate();
}
private PointF GenerateHilbert(PointF origin, float xi, float xj, float yi, float yj, int depth,
PointF? previous, Graphics graphics, Pen pen)
{
if (depth <= 0)
{
PointF current = origin + new SizeF((xi + yi) / 2, (xj + yj) / 2);
if (previous != null)
{
graphics.DrawLine(pen, previous.Value, current);
}
return current;
}
else
{
previous = GenerateHilbert(origin, yi / 2, yj / 2, xi / 2, xj / 2, depth - 1, previous, graphics, pen);
previous = GenerateHilbert(origin + new SizeF(xi / 2, xj / 2), xi / 2, xj / 2, yi / 2, yj / 2, depth - 1, previous, graphics, pen);
previous = GenerateHilbert(origin + new SizeF(xi / 2 + yi / 2, xj / 2 + yj / 2), xi / 2, xj / 2, yi / 2, yj / 2, depth - 1, previous, graphics, pen);
return GenerateHilbert(origin + new SizeF(xi / 2 + yi, xj / 2 + yj), -yi / 2, -yj / 2, -xi / 2, -xj / 2, depth - 1, previous, graphics, pen);
}
}
// Perform the Actual Drawing
private void HilbertCurve_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
{
float scale = Math.Min(ClientSize.Width, ClientSize.Height);
e.Graphics.ScaleTransform(scale, scale);
using (Pen pen = new Pen(Color.Red, 1 / scale))
{
GenerateHilbert(new PointF(), 1, 0, 0, 1, (int)numericUpDown1.Value, null, e.Graphics, pen);
}
}
private void numericUpDown1_ValueChanged(object sender, EventArgs e)
{
Redraw();
}
protected override void OnClientSizeChanged(EventArgs e)
{
base.OnClientSizeChanged(e);
Invalidate();
}
}
In both examples I've made some other changes which are not strictly needed for the purpose of illustrating the techniques, but which are still useful:
The curve itself is computed in unit space (i.e. a square of side length of 1), and then drawn by scaling the drawing to fit the window.
Where it makes sense, individual coordinates are passed as whole PointF values instead. This simplifies reuse of the values and adding new offsets to the X and Y values.
Since the drawing is now scaled to the window, the window is redrawn if its size changes.
For simplicity, this Form is self-contained, with a NumericUpDownControl that determines the recursion depth. I didn't include instantiation of this control; I presume you can add the appropriate control yourself in the Designer, to make the above compile.
Addendum:
I've had a chance to look over the other examples on the Internet of the algorithm that you tried to implement. Now that I understand what the basic mechanism of the algorithm is, I was able to fix your version so that it works (the main problem was that you were using instance fields to store the deltas for the algorithm, but also using the same fields to initialize the algorithm, so once the algorithm ran once, subsequent executions wouldn't work). So for the sake of completeness, here is a second "retained" version of the code, using your preferred algorithm instead of the one I used above:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
DoubleBuffered = true;
}
private PointF _previousPoint;
private PointF[] _points;
private void FractalDisplay_Load(object sender, EventArgs e)
{
Redraw();
}
private void Redraw()
{
List<PointF> points = new List<PointF>();
// Start here, to provide a bit of margin within the client area of the window
_previousPoint = new PointF(0.025f, 0.025f);
points.Add(_previousPoint);
int depth = (int)numericUpDown1.Value;
float gridCellCount = (float)(Math.Pow(2, depth) - 1);
// Use only 95% of the available space in the client area. Scale
// the delta for drawing to fill that 95% width/height exactly,
// according to the number of grid cells the given depth will
// produce in each direction.
GenerateHilbert3(depth, 0, 0.95f / gridCellCount, points);
_points = points.ToArray();
Invalidate();
}
private void GenerateHilbert(int depth, float xDistance, float yDistance, List<PointF> points)
{
if (depth < 1)
{
return;
}
GenerateHilbert(depth - 1, yDistance, xDistance, points);
DrawRelative(xDistance, yDistance, points);
GenerateHilbert(depth - 1, xDistance, yDistance, points);
DrawRelative(yDistance, xDistance, points);
GenerateHilbert(depth - 1, xDistance, yDistance, points);
DrawRelative(-xDistance, -yDistance, points);
GenerateHilbert(depth - 1, -yDistance, -xDistance, points);
}
private void DrawRelative(float xDistance, float yDistance, List<PointF> points)
{
// Discover where the new X and Y points will be
PointF currentPoint = _previousPoint + new SizeF(xDistance, yDistance);
// Paint from the current position of X and Y to the new positions of X and Y
points.Add(currentPoint);
// Update the Current Location of X and Y
_previousPoint = currentPoint;
}
// Perform the Actual Drawing
private void HilbertCurve_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
{
if (_points != null)
{
float scale = Math.Min(ClientSize.Width, ClientSize.Height);
e.Graphics.ScaleTransform(scale, scale);
using (Pen pen = new Pen(Color.Red, 1 / scale))
{
e.Graphics.DrawLines(pen, _points);
}
}
}
private void numericUpDown1_ValueChanged(object sender, EventArgs e)
{
Redraw();
}
protected override void OnClientSizeChanged(EventArgs e)
{
base.OnClientSizeChanged(e);
Invalidate();
}
}
As before, I've modified your implementation slightly, so that the drawing is scaled to fit within the window at all depths. This involves drawing into the unit square and then setting the transform appropriately according to the window size.
In addition to fixing the basic usage of Graphics, and the issue with the xLength and yLength fields, I also fixed a minor bug in your code (where you were recursing one level too deep) and cleaned up the recursion a bit (there's no need to repeat the depth checkā¦just do it once, at the beginning of the recursive method).
It is of course possible to implement this in the "immediate" style as well. I think between this new code example, and the "immediate" method example above, I can leave that exercise to the reader. :)
Here is the fractal generator I came up with after heeding the advice of #Peter Duniho - The code shown does not include the form that actually gets the depth level (maxDepth) of recursion requested by the user.
public partial class HilbertDisplay : Form
{
private int maxDepth;
private int xCurrent = 0;
private int yCurrent = 0;
private int xNew = 0;
private int yNew = 0;
public HilbertDisplay(int depthEntered)
{
InitializeComponent();
maxDepth = depthEntered;
}
private void HilbertDisplay_Load(object sender, EventArgs e)
{
this.DoubleBuffered = true;
this.Update();
}
// Perform the Drawing
private void HilbertDisplay_Paint(object sender, PaintEventArgs e)
{
// Run the Hilbert Curve Generator
// Use a line segment length of 10 for Y
GenerateHilbertCurve(maxDepth, 0, 10, e);
}
// The Recursive Hilbert Curve Generator
private void GenerateHilbertCurve(int depth, int xDistance, int yDistance, PaintEventArgs e)
{
if (depth < 1)
{
return;
}
else
{
GenerateHilbertCurve(depth - 1, yDistance, xDistance, e);
// Paint from the current position of X and Y to the new positions of X and Y
FindPointRelative(xDistance, yDistance);
e.Graphics.DrawLine(Pens.Red, xCurrent, yCurrent, xNew, yNew); // Draw Part of Curve Here
UpdateCurrentLocation();
GenerateHilbertCurve(depth - 1, xDistance, yDistance, e);
// Paint from the current position of X and Y to the new positions of X and Y
FindPointRelative(yDistance, xDistance);
e.Graphics.DrawLine(Pens.Blue, xCurrent, yCurrent, xNew, yNew); // Draw Part of Curve Here
UpdateCurrentLocation();
GenerateHilbertCurve(depth - 1, xDistance, yDistance, e);
// Paint from the current position of X and Y to the new positions of X and Y
FindPointRelative(-xDistance, -yDistance);
e.Graphics.DrawLine(Pens.Green, xCurrent, yCurrent, xNew, yNew); // Draw Part of Curve Here
UpdateCurrentLocation();
GenerateHilbertCurve(depth - 1, (-1 * yDistance), (-1 * xDistance), e);
}
}
private void FindPointRelative(int xDistance, int yDistance)
{
// Discover where the new X and Y points will be
xNew = xCurrent + xDistance;
yNew = yCurrent + yDistance;
return;
}
private void UpdateCurrentLocation()
{
// Update the Current Location of X and Y
xCurrent = xNew;
yCurrent = yNew;
return;
}
}
This code, unlike that of #Peter Duniho, does not account for the form's size. This depicts a Hilbert Curve fractal up to a recursion depth of 6 or 7 on my laptop (due to limitations on window size made by my laptop screen size/resolution).
I know that my solution is not as elegant as that of #Peter Duniho, but as this is for an assignment I did not want to simply copy his code. I made edits based on his suggestions, especially in regard to the Paint event.
On MouseDownEvent I set upper left corner of Ellipse I'm trying to draw.
public MyCircle(Point location)
{
ellipseObject = new Ellipse
{
Stroke = Brushes.Black,
StrokeThickness = 2,
Margin = new Thickness(location.X, location.Y, 0, 0)
};
}
Then on MouseMoveEvent I update Width and Height properties and it works fine as long as I don't move mouse above or/and to the left of my Ellipse upper left corner, in that case I'm getting exception that these properties can't be negative (which of course makes perfect sense).
public void Draw(Point location)
{
if (ellipseObject != null)
{
ellipseObject.Width = location.X - ellipseObject.Margin.Left;
ellipseObject.Height = location.Y - ellipseObject.Margin.Top;
}
}
The problem doesn't exist with drawing lines:
public void Draw(Point location)
{
lineObject.X2 = location.X;
lineObject.Y2 = location.Y;
}
I know it's trivial, but I'm completely stuck on this. How should I handle drawing Ellipses?
I had this EXACT problem when trying to create a crop tool. Problem is that you need to create if statements for when the cursor goes negative X or negative Y from your starting point. For starters, you'll need to have a global Point that you mark as your 'start' point. Also specify a global current point position that we'll talk about in a minute.
public Point startingPoint;
public Point currentPoint;
Then, make sure you have an onMouseDown event on whatever control you are trying to put the ellipse on.
private void control_MouseDown(object sender, MouseEventArgs e)
{
startingPoint.X = e.X;
startingPoint.Y = e.Y;
}
Then, you need to create if statements in your MouseMove event to check with point (current mouse position, or starting point) has a lower X/Y value
private void control_MouseMove(object sender, MouseEventArgs e)
{
//The below point is what we'll draw the ellipse with.
Point ellipsePoint;
Ellipse ellipseObject = new Ellipse();
currentPoint.X = e.X;
currentPoint.Y = e.Y;
//Then we need to get the proper width/height;
if (currentPoint.X >= startingPoint.X)
{
ellipsePoint.X = startingPoint.X;
ellipseObject.Width = currentPoint.X - startingPoint.X;
}
else
{
ellipsePoint.X = currentPoint.X;
ellipseObject.Width = startingPoint.X - currentPoint.X;
}
if (currentPoint.Y >= startingPoint.Y)
{
ellipsePoint.Y = startingPoint.Y;
ellipseObject.Height = currentPoint.Y - startingPoint.Y;
}
else
{
ellipsePoint.Y = currentPoint.Y;
ellipseObject.Height = startingPoint.Y - currentPoint.Y;
}
ellipseObject.Stroke = Brushes.Black;
ellipseObject.StrokeThickness = 2;
ellipseObject.Margin = new Thickness(ellipsePoint.X, ellipsePoint.Y, 0, 0);
}
Hope this helps!
Save the origin point separately and set the X and Y properties of the Ellipse's Margin to the mouse position and the Width and Height to the distances between the mouse and origin point.
Untested:
public MyCircle(Point location)
{
ellipseObject = new Ellipse
{
Stroke = Brushes.Black,
StrokeThickness = 2,
Margin = new Thickness(location.X, location.Y, 0, 0)
Tag = new Point(location.X, location.Y)
};
}
public void Draw(Point location)
{
if (ellipseObject != null)
{
Point o = (Point)ellipseObject.Tag;
double x = Math.Min(location.X, o.Left);
double y = Math.Min(location.Y, o.Top);
double width = Math.Abs(Math.Max(location.X, o.Left) - x);
double height = Math.Abs(Math.Max(location.Y, o.Top) - y);
ellipseObject.Margin.X = x;
ellipseObject.Margin.Y = y;
ellipseObject.Width = width;
ellipseObject.Height = height;
}
}
I captured a video and took out a frame of that, converted it to Bitmap and now I can show it on picture box.
I have some float points which is the return values of GoodFeaturesToTrack() function from image class.
Now I want to draw/show those points/marks on different Xi,Yi over my picture;
How is it possible to do it? which command I have to use?
You could use builtin OpenCV functions to render around the feature points found, before you convert your image to a normal bitmap. This is also going to be much faster, as the image class will work with the raw memory rather than issue graphics calls.
Here's an (incomplete) example to illustrate the point. Note: you might need to adjust the calls to the CV signatures declared by your wrapper:
private int maxPointCount = 16;
private CvPoint2D32f[] points = new CvPoint2D32f[maxPointCount];
private CvImage grayImage = new CvImage(size, CvColorDepth.U8, CvChannels.One);
private CvImage eigenValues = new CvImage(size, CvColorDepth.F32, CvChannels.One);
private CvImage tempImage = new CvImage(size, CvColorDepth.F32, CvChannels.One);
public int FeatureRadius { get; set; }
private CvScalar featureColor;
public Color FeatureColor
{
get
{
return Color.FromArgb((byte)featureColor.Value2, (byte)featureColor.Value1, (byte)featureColor.Value0);
}
set
{
featureColor.Value0 = value.B;
featureColor.Value1 = value.G;
featureColor.Value2 = value.R;
}
}
public void Process(CvImage input, CvImage output)
{
CV.ConvertImage(input, grayImage);
CV.GoodFeaturesToTrack(grayImage, eigenValues, tempImage, points, ref maxPointCount, 0.01, 10, IntPtr.Zero, 3, 0, 0.04);
CV.Copy(input, output);
// This draws a circle around the feature points found
for (int i = 0; i < pointCount; i++)
CV.Circle(output, new CvPoint((int)points[i].X, (int)points[i].Y), FeatureRadius, featureColor);
}
Add a handler for the PictureBox.Paint event and do your drawing there. If you need to refresh the drawing call Invalidate() on your PictureBox control to redraw.
void PictureBox_Paint(object sender, PaintEventArgs e) {
// draw points from var pointsList = List<Point>
foreach (Point p in pointsList) {
e.Graphics.DrawEllipse(Pens.Yellow, p.X - 2, p.Y - 2, 4, 4);
}
}