I answered a question on how to set up a Chart to look like a regular mathematical graph, i.e. with the axes centered and with nice arrows to the top and to the right..
However I found the built-in AxisArrowStyle.Triangle to be rather big and found no way to make it smaller.
Lines - A line-shaped arrow is used for the relevant axis.
None - No arrow is used for the relevant axis.
SharpTriangle - A sharp triangular arrow is used for the relevant axis.
Triangle - A triangular arrow is used for the relevant axis.
Here is the original look of it:
So how can we fix this?
The Chart's axis.AxisArrowStyle enumeration doesn't let us pick a smaller arrow, only a slimmer one.
So we need to draw it ourselves:
Here is a simple but effective piece of code that achieves just that:
private void chart1_PrePaint(object sender, ChartPaintEventArgs e)
{
if (e.ChartElement.ToString().StartsWith("ChartArea-") )
{
// get the positions of the axes' ends:
ChartArea CA = chart1.ChartAreas[0];
float xx = (float)CA.AxisX.ValueToPixelPosition(CA.AxisX.Maximum);
float xy = (float)CA.AxisY.ValueToPixelPosition(CA.AxisY.Crossing);
float yx = (float)CA.AxisX.ValueToPixelPosition(CA.AxisX.Crossing);
float yy = (float)CA.AxisY.ValueToPixelPosition(CA.AxisY.Maximum);
// a simple arrowhead geometry:
int arrowSize = 18; // size in pixels
Point[] arrowPoints = new Point[3] { new Point(-arrowSize, -arrowSize / 2),
new Point(-arrowSize, arrowSize / 2), Point.Empty };
// draw the two arrows by moving and/or rotating the graphics object:
e.ChartGraphics.Graphics.TranslateTransform(xx + arrowSize, xy);
e.ChartGraphics.Graphics.FillPolygon(Brushes.Black, arrowPoints);
e.ChartGraphics.Graphics.TranslateTransform(yx -xx -arrowSize, yy -xy -arrowSize);
e.ChartGraphics.Graphics.RotateTransform(-90);
e.ChartGraphics.Graphics.FillPolygon(Brushes.Black, arrowPoints);
e.ChartGraphics.Graphics.ResetTransform();
}
}
Related
OxyPlot is a cross-platform plotting library for .NET, very convenient for making plots,
Now there's a situation here, I have to draw a 95% confidence ellipse to an XY scatter plot.
Oxyplot provides with following annotation:-
Given here Ellipse Annotation(OxyPlot.Annotations) gives only following properties to add ellipse-
We don't have any rotation property or method here, IRender provides several draw methods to override but none of the methods have double angled rotation argument or so. Neither the documentation has provides any direct solution to it:-
Then how to draw this:-
*I was facing this issue for one of my assignment, and came up with a solution after going through the following forums discussion to get hints on how to generate such an ellipse.
https://github.com/oxyplot/oxyplot/issues/268
https://oxyplot.userecho.com/en/communities/1/topics/598-ellipse-annotation-rotation
Please add more solutions if anyone else has :-
Based on the link shared (in Quest.) best and easiest solution here was to draw an ellipse using PolygonAnnotation, which takes List of co-ordinate points,
Let's say if you give four co-ordinate points A,B,C,D--- polygonAnnotation will give me a closed 4-gon~quadrilateral sort of structure based on kind of points taken.
Now if you increase the number of points from 4 to 6--- it will give you hexagon, and so on.
Now at pixel level you can give infinite-number/discrete-number of points eclipsing over 360 degree.
So here we need an algorithm/equation of point on an 2D ellipse- given following inputs (based on this case):-
Center of ellipse (h,k)
rotation angle of the ellipse axis
major axis (a)
minor axis (b)
theta angle from the x-axis
private void GeneratePolygonAsEllipse(PolygonAnnotation polygonAnnotation)
{
double step = 2 * Math.PI / 200;
var h = xCenter;
var k = yCenter;
var rotation = AngleOfRotation;
var a = MajorAxisLength;
var b = MinorAxisLength;
for (double theta = 0; theta < 2 * Math.PI; theta += step)
{
var x = a * Math.Cos(rotation) * Math.Cos(theta) + b * Math.Sin(rotation) * Math.Sin(theta) + h;
var y = b * Math.Cos(rotation) * Math.Sin(theta) + a * Math.Sin(rotation) * Math.Cos(theta) + k;
polygonAnnotation.Points.Add(new DataPoint(x, y));
}
}
I hope above stipulated sample method equation can be useful to other folks like me looking for solution. I couldn't find direct solution anywhere else so I have added my solution here, that can be used as reference.
Result:-
if anyone can come-up with other solutions like how to use IRender or anything else, would be great to look at them.
this is precisely what I want multiple markers and covering them is a rectangle polygonI am having multiple latitude and longitude points. I need to plot a polygon in my Windows form application covering all those specified. Something like this.
It consists of a polygon and rectangle. Avoid polygon I just want the rectangle.
As hinted by #Taw, you can draw the rectangle with following co-ordinates:
List<PointF> ptlist = new List<PointF>();
// Add points to the list here
ptlist.Sort((p1, p2) => (p1.X.CompareTo(p2.X))); //Sort by X
float left = ptlist[0].X
float right = ptlist[ptlist.Count - 1].X
ptlist.Sort((p1, p2) => (p1.Y.CompareTo(p2.Y))); //Sort by Y
float top = ptlist[0].Y
float bottom = ptlist[ptlist.Count - 1].Y
// Use left, top and right, bottom to draw your rectangle.
Instead of sort, you may also write a simple code to find minimum and maximum of the list for efficiency.
I have a windows universal app where I am rendering a scene with DirectX. I want to make use of the Scrollviewer and therefore I render my scene behind the Scrollviewer and want to calculate the scene transformation based on the Scrollviewer. It works fine so far, especially the translation and scrolling. But when I zoom in, the scene jumps around in two special situations:
The scene had enough space and was centered and now scrolling is required.
The opposite direction.
More or less I use the following code:
float zoom = scrollViewer.ZoomFactor;
float inverseZoom = 1f / scrollViewer.ZoomFactor;
float scaledContentW = Document.Size.X * scrollViewer.ZoomFactor;
float scaledContentH = Document.Size.Y * scrollViewer.ZoomFactor;
float translateX;
float translateY;
if (scaledContentW < scrollViewer.ViewportWidth)
{
translateX = ((float)scrollViewer.ViewportWidth * inverseZoom - Document.Size.X) * 0.5f;
}
else
{
translateX = -inverseZoom * (float)scrollViewer.HorizontalOffset;
}
if (scaledContentH < scrollViewer.ViewportHeight)
{
translateY = ((float)scrollViewer.ViewportHeight * inverseZoom - Document.Size.Y) * 0.5f;
}
else
{
translateY = -inverseZoom * (float)scrollViewer.VerticalOffset;
}
float visibleX = inverseZoom * (float)scrollViewer.HorizontalOffset;
float visibleY = inverseZoom * (float)scrollViewer.VerticalOffset; ;
float visibleW = Math.Min(Document.Size.X, inverseZoom * (float)scrollViewer.ViewportWidth);
float visibleH = Math.Min(Document.Size.Y, inverseZoom * (float)scrollViewer.ViewportHeight);
Rect2 visibleRect = new Rect2(visibleX, visibleY, visibleW, visibleH);
transform =
Matrix3x2.CreateTranslation(
translateX,
translateY) *
Matrix3x2.CreateScale(zoom);
You can get an example here: https://github.com/SebastianStehle/Win2DZoomTest
To be sure that my eyes are not broken I was zooming around and have written the translation and zoom values to a file. You can see it here:
https://www.dropbox.com/s/9ak6ohg4zb1mnxa/Test.png?dl=0
The meaning of the columns is the following:
Column 1: The computed zoom value of the transformation matrix (M11) = ScrollViewer.ZoomFactor
Column 2: The computed x offset of the matrix (See above)
Column 3: The x value of the result of matrix * vector (500, 500), here: Colum1 * 500 + Column2
You see, that the matrix values look good, but when applying the transformation you get this little jump to the right for some milliseconds. One theory was, that the viewport might change because the scrollbar becomes visible. But this is not the case. I also tried fixed values here, made the scrollbars visible and even created a custom template for the scrollviewer with no scrollbars at all.
Btw: This is a cross post, I also asked the question here: https://github.com/Microsoft/Win2D/issues/125
You see this behavior because when you zoom bigger than the ScrollViewer's size, the zoom center point is moved. To fix this, you just need to subscribe to the ScrollViewer's LayoutUpdated event and inside the handler, manually keep its content in the center.
private void ScrollViewer_LayoutUpdated(object sender, object e)
{
this.ScrollViewer.ChangeView(this.ScrollViewer.ScrollableWidth / 2, this.ScrollViewer.ScrollableHeight / 2, this.ScrollViewer.ZoomFactor, true);
}
This should fix the jumpy movement on the two drawed Rectangles from Win2D.
Update
Just to prove my point, the jumpy behavior is most likely due to unusual translate x and/or y value change when the content size goes over the size of the ScrollViewer. So I wrote some code to log these values on the screen as shown below -
...
this.Test1.Text += ((float)translateX).ToString() + " ";
this.Test2.Text += ((float)translateY).ToString() + " ";
session.Transform =
Matrix3x2.CreateTranslation(
(float)translateX,
(float)translateY) *
Matrix3x2.CreateScale((float)zoom);
Now look at the numbers on the image above. What I did was I tried zooming in until the jumpy scene occurred. See the highlighted translate y value? It is slightly greater than its previous value, which is against the declining trend.
So to fix this, you will need to be able to skip these unusual values caused by ScrollViewer.
Here's 2 methods available;
if(rectangle.Intersects(otherRectangle))
{
//collision stuff
}
Catch: Only works with non-rotating rectangles.
if(Vector2.Distance(player.pos, enemy.pos) < 50)
{
//collision stuff
}
Catch: Only works with circles.
What I want is to calculate x and y in this image:
Facts
The width and length of both rectangles is defined, along with their rotations.
I can calculate D using the Pythagorean theorem.
But the TRUE distance is D - (X + Y).
General approach
Evidently x and y can be calculated using the Cosine rule.
But I only have the width or length and the angle between the two shapes.
Complication
Plus, this needs to work for any rotation.
The rectangle on the left could be rotated in any direction, and x would be different depending on said rotation.
Question
How would I calculate x and y?
I just want an effective collision detection method more complex than bounding boxes and Pythagoras' theorem.
One approach is to rotate the line with the inverse angle and check with the axis-aligned box:
class RotatedBox
{
...
float CalcIntersectionLength(Vector2 lineTo) //assume that the line starts at the box' origin
{
Matrix myTransform = Matrix.CreateRotationZ(-this.RotationAngle);
var lineDirection = Vector2.Transform(lineTo -this.Center, myTransform);
lineDirection.Normalize();
var distanceToHitLeftOrRight = this.Width / 2 / Math.Abs(lineDirection.X);
var distanceToHitTopOrBottom = this.Height / 2 / Math.Abbs(lineDirection.Y);
return Math.Min(distanceToHitLeftOrRight, distanceToHitTopOrBottom);
}
}
Now you can calculate the actual distance with
var distance = (box1.Center - box2.Center).Length
- box1.CalcIntersectionLength(box2.Center)
- box2.CalcIntersectionLength(box1.Center);
Be sure that the rotation direction matches your visualization.
How to draw the spring like shape using c# drawing class
alt text http://img812.imageshack.us/img812/373/spring.jpg
First of all you'd need to think of a formula that would represent the spring. You could draw a circle and as you're going around it, let the X increase a bit. For instance:
for (double i = 0; i < 50; i += 0.01)
{
int x = (int)(Math.Sin(i) * 10 + i * 3);
int y =(int)(Math.Cos(i) * 10 + 50);
}
See the i variable there as time, and the result x and y the coordinates to draw; you'd traverse the path of the spring in small steps.
You could then create a new Bitmap and use the SetPixel method on those coordinates, and in the OnPaint method of your form, draw the bitmap on it.
If you're any good with math (I'm not :P) you might be able to only plot pixels inside the bitmap - the above example doesn't solve the problem of the minimum and maximum values for i.
This is more of a math problem than a C# one. What you want is to derive a Parametric equation for the curve you wish to draw.
With that go and fill an array of Point objects with values for the parametric equation on a certain interval with a certain step (the smaller the step the more the final drawing will look like the actual shape). Then you can use g.DrawLines (MSDN: DrawLines) to draw the actual curve on a surface.
You can edit the width, color and other properties of the line by modifying parameters of the Pen object.
Your actual code would look like this:
void DrawSpring (Graphics g)
{
List<Point> points = new List<Point>();
double step = 0.01;
for(double t = -2; t < 2; t += step)
{
Point p = new Point();
p.X = XPartOfTheEquation(t);
p.Y = YPartOfTheEquation(t);
points.Add(p);
}
g.DrawLines(new Pen(new SolidBrush(Color.Black), 2f), points.ToArray());
}