How Get The middle point of an ArcSegment in WPF - c#

What is the best solution for getting the middle point of an ArcSegment in a path and label it in WPF?

This should work:
//the given arc (or any other segments)
var arc = new ArcSegment(
point: new Point(200, 100),
size: new Size(100, 50),
rotationAngle: 90,
isLargeArc: true,
sweepDirection: SweepDirection.Counterclockwise,
isStroked: true);
//compose one or more segments into a collection
var pathcoll = new PathSegmentCollection();
pathcoll.Add(arc);
//create a figure based on the set of segments
var figure = new PathFigure();
figure.Segments = pathcoll;
//compose a collection of figures
var figcoll = new PathFigureCollection();
figcoll.Add(figure);
//create a path-geometry using the figures collection
var geom = new PathGeometry(figcoll);
double fraction = 0.5; //the relative point of the curve
Point pt; //the absolute point of the curve
Point tg; //the tangent point of the curve
geom.GetPointAtFractionLength(
fraction,
out pt,
out tg);
Hope it helps.
Cheers

Related

Filling an arc segment in WPF

I have the scenario as follows, I am drawing an arc between two points in WPF, I need to fill the arc, but I want to fill the arc sector touching the center, but WPF fills up differently. How do I get the arc filled up the way I want?
PathGeometry pathGeometry = new PathGeometry();
PathFigure pathFigure = new PathFigure();
pathFigure.StartPoint = start;
ArcSegment arc = new ArcSegment(end, new Size(radiusX, radiusY), 0.0, large, d, true); //large & d corresponds to size & direction
pathFigure.Segments.Add(arc);
pathGeometry.Figures.Add(pathFigure);
SolidColorBrush fill = new SolidColorBrush(color);
drawingContext.DrawGeometry(fill, pen, pathGeometry);
My code produces the output as this :
whereas my requirement is this one:
Is there a way of getting this done? TIA.
Sure, you just need at add a line segment to go along with your arc segment.
PathGeometry pathGeometry = new PathGeometry();
PathFigure pathFigure = new PathFigure();
pathFigure.StartPoint = start;
ArcSegment arc = new ArcSegment(end, new Size(radiusX, radiusY), 0.0, large, d, true); //large & d corresponds to size & direction
pathFigure.Segments.Add(arc);
//line segment takes the path to the origin
LineSegment line = new LineSegment(new Point(originX, originY), true);
pathFigure.Segments.Add(line );
pathGeometry.Figures.Add(pathFigure);
SolidColorBrush fill = new SolidColorBrush(color);
drawingContext.DrawGeometry(fill, pen, pathGeometry);

How can I draw a segment of a semi circle with a consistant radius using an ArcSegment

I am trying to write my own pie chart control using WPF. I have created a function that returns a Path object representing a segment of the pie chart. When I use this to generate a graph where the largest slice is less then or equal to 50% of the graph it renders fine and looks like so:
But when one of the arcsegments has an angle of more then PI radians the radius is not equal all the way round and it looks like this:
Here is the code that I have written to draw each segment of the chart:
public Path CreateSlice(Point centerPoint, double radius, ref Point initialPoint, ref double totalAngle, double sliceAngle, Color sliceColor)
{
//A set of connected simple graphics objects that makes up the Slice graphic
Path slicePath = new Path();
slicePath.Fill = new SolidColorBrush(sliceColor);
slicePath.Stroke = new SolidColorBrush(Colors.White);
slicePath.StrokeThickness = 2;
PathGeometry pathGeometry = new PathGeometry();
PathFigureCollection pathFigureCollection = new PathFigureCollection();
PathSegmentCollection sliceSegmentCollection = new PathSegmentCollection();
//The path figure describes the shape in the slicePath object using geometry
PathFigure pathFigure = new PathFigure();
pathFigure.StartPoint = centerPoint;//Set the start point to the center of the pie chart
//Representing the first line of the slice from the centerpoint to the initial point
//I.E one length of the slice
LineSegment firstLineSegment = new LineSegment();
firstLineSegment.Point = initialPoint;
sliceSegmentCollection.Add(firstLineSegment);
//Calculate the next point along the circle that the slice should arc too.
//This point is calculated using the total angle used of the pie chart including this slice
totalAngle += sliceAngle;
//Calculate the X and Y coordinates of the next point
double x = centerPoint.X + radius * Math.Cos(totalAngle);
double y = centerPoint.Y + radius * Math.Sin(totalAngle);
initialPoint = new Point(x, y);
//Represents the arc segment of the slice
ArcSegment sliceArcSegment = new ArcSegment();
sliceArcSegment.Point = initialPoint;
sliceArcSegment.SweepDirection = SweepDirection.Clockwise;
sliceArcSegment.Size = new Size(radius, radius);
sliceSegmentCollection.Add(sliceArcSegment);
//Representing the second line of the slice from the second point back to the center
LineSegment secondLineSegment = new LineSegment();
secondLineSegment.Point = centerPoint;
sliceSegmentCollection.Add(secondLineSegment);
pathFigure.Segments = sliceSegmentCollection;
pathFigureCollection.Add(pathFigure);
pathGeometry.Figures = pathFigureCollection;
slicePath.Data = pathGeometry;
return slicePath;
}
Could anyone help me figure out how to draw a arcsegment with a uniform radius all the way round please.
The IsLargeArc property should be set in these cases to tell the arc to be greater than 180 degrees. Looking at your figure it is capping the degrees at 180 and shifting the expected center point of the Arc.

Convert Ellipse or EllipseGeometry to PathFigure on Windows Phone 8.1

I've stumbled upon converting Ellipse to PathGeometry.
I've tried to use ArcSegment to present Ellipse, but still don't know how to convert Ellipse size to ArcSegment size. I'm trying to segment Ellipse into two part, but there are other approaches, i.e. Bezier curves.
Need clues how to convert Ellipse to ArcSegment collection.
The whole process could be looking like that:
public static PathFigure ToFigures(this Ellipse ellipse)
{
var pathFigure = new PathFigure {IsClosed = true};
var arcSegment1 = new ArcSegment();
var arcSegment2 = new ArcSegment();
pathFigure.Segments.Add(arcSegment1);
pathFigure.Segments.Add(arcSegment2);
return pathFigure;
}
You could directly use an EllipseGeometry for the Path Data:
path = new Path
{
Data = new EllipseGeometry { RadiusX = 100, RadiusY = 50 }
};
Using the size of the Ellipse it would look like this:
path = new Path
{
Data = new EllipseGeometry
{
RadiusX = ellipse.ActualWidth / 2,
RadiusY = ellipse.ActualHeight / 2
}
};

GraphicsPath.AddPolygon not rendering properly

Basically I have a form and am trying to "dim" areas of it to draw focus to a certain part of the form. To do this I'm using a Form with no border and 50% opacity, aligned with the actual form. The area I am trying to mask is the dark gray area, roughly, as pictured:
To get the "U"-shaped form, I'm using a GraphicsPath with AddPolygon, calculating the points of each vertex:
var p = new GraphicsPath();
var origin = new Point(Top, Left);
var maxExtentPt = new Point(origin.X + Width, origin.Y + Height);
Point[] points = {
origin,
new Point(origin.X + leftPanel.Width, origin.Y),
new Point(origin.X + leftPanel.Width, maxExtentPt.Y - bottomPanel.Height),
new Point(maxExtentPt.X - rightPanel.Width, maxExtentPt.Y- bottomPanel.Height),
new Point(maxExtentPt.X - rightPanel.Width, origin.Y),
new Point(maxExtentPt.X, origin.Y),
maxExtentPt,
new Point(origin.X, maxExtentPt.Y),
origin
};
p.AddPolygon(points);
overlayForm.Region = new Region(p);
overlayForm.Location = PointToScreen(Point.Empty);
The three panels in the code are what I am masking, so I am using their dimensions to calculate the points. Instead of getting my expected result, the mask looks like this, with its size changing as I resize the main form (I recalculate the region on Move and Resize):
Is there some limitation of GraphicsPath.AddPolygon that I'm not aware of? I double-checked (quadruple-checked, really) the results of my calculations, including taking the coordinates for each point and plugging them into Ipe to see if the shape was actually correct... It was. But not in my program!
Edit: Here are the values of each point, when I hit a breakpoint at p.AddPolygon(points); I'm starting in the upper left-hand corner and going around clockwise:
Looks like your points are wrong after all.
Everything ought to be in the coordinates of the ClientRectangle, so
Origin should not be new Point(Top, Left) which is the Location of the Form. It should be Point.Empty or (0,0). Or you could use the leftPanel.Location.
And
maxExtentPt = new Point(origin.X + Width, origin.Y + Height);
should read:
var maxExtentPt = new Point(origin.X + ClientSize.Width, origin.Y + ClientSize.Height);
(The difference is the size of border+title..)
Let me know if that works better!
You could also try doing it this way, basing everything off the Panels:
Form overlayForm = new Form();
overlayForm.Opacity = .5;
overlayForm.BackColor = Color.DarkGray;
overlayForm.StartPosition = FormStartPosition.Manual;
overlayForm.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
var p = new GraphicsPath();
var origin = new Point(0, 0);
var maxExtentPt = new Point(this.Width, this.Height);
Point[] points = {
origin,
new Point(leftPanel.Right, origin.Y),
new Point(leftPanel.Right, bottomPanel.Top),
new Point(rightPanel.Left, bottomPanel.Top),
new Point(rightPanel.Left, origin.Y),
new Point(rightPanel.Right, origin.Y),
maxExtentPt,
new Point(origin.X, maxExtentPt.Y),
origin
};
p.AddPolygon(points);
overlayForm.Region = new Region(p);
overlayForm.Location = this.PointToScreen(new Point(0, 0));
overlayForm.Show();

How to draw bezier curve by several points?

I have several points, and I try to draw Bezier curve using code below
PathFigure pf = new PathFigure(points.From, ps, false); //ps - list of Bezier segments
PathFigureCollection pfc = new PathFigureCollection();
pfc.Add(pf);
var pge = new PathGeometry();
pge.Figures = pfc;
Path p = new Path();
p.Data = pge;
p.Stroke = new SolidColorBrush(Color.FromRgb(244, 111, 011));
My Bezier segments look like this
1,2,3 points - first segment
3,4,5 points - second
5,6,7.. ..
But I got this strange curve (here is 3 big (Nodes) and 7 small ellipse (is my points)):
The line you're getting is the union of three distinct Bezier curves - one for each group of three points. (One for each "Bezier segment"?)
If you want a single smooth curve, you need to pass your 9 (or more) points as a single collection of points (single "Bezier segment"?), not as groups of three points.
Edit: Apparently BezierSegment only supports three points, so no wonder this doesn't work. Even 'PolyBezierSegment' just gives a collection of Bezier segments rather than a single smooth Bezier...
So since WPF doesn't give you anything useful, I knocked something together using the maths here. It's a numeric solution, but it seems to be pretty performant even with enough points to look nice and smooth:
PolyLineSegment GetBezierApproximation(Point[] controlPoints, int outputSegmentCount)
{
Point[] points = new Point[outputSegmentCount + 1];
for (int i = 0; i <= outputSegmentCount; i++)
{
double t = (double)i / outputSegmentCount;
points[i] = GetBezierPoint(t, controlPoints, 0, controlPoints.Length);
}
return new PolyLineSegment(points, true);
}
Point GetBezierPoint(double t, Point[] controlPoints, int index, int count)
{
if (count == 1)
return controlPoints[index];
var P0 = GetBezierPoint(t, controlPoints, index, count - 1);
var P1 = GetBezierPoint(t, controlPoints, index + 1, count - 1);
return new Point((1 - t) * P0.X + t * P1.X, (1 - t) * P0.Y + t * P1.Y);
}
Using this,
private void Grid_Loaded(object sender, RoutedEventArgs e)
{
Point[] points = new[] {
new Point(0, 200),
new Point(0, 0),
new Point(300, 0),
new Point(350, 200),
new Point(400, 0)
};
var b = GetBezierApproximation(points, 256);
PathFigure pf = new PathFigure(b.Points[0], new[] { b }, false);
PathFigureCollection pfc = new PathFigureCollection();
pfc.Add(pf);
var pge = new PathGeometry();
pge.Figures = pfc;
Path p = new Path();
p.Data = pge;
p.Stroke = new SolidColorBrush(Color.FromRgb(255, 0, 0));
((Grid)sender).Children.Add(p);
}
gives
Since each of your curves has one control point (a point that influences the curve but isn't necessarily on the curve), you're using quadratic Bézier curves.
If you want to draw two quadratic curves that share an endpoint, and you want the joint to appear smooth, the control points on each side of the shared endpoint must be collinear with the endpoint. That is, the two control points and the endpoint between them must all lie on a straight line. Example:
The solid black discs are the endpoints. The hollow circles are the control points. The solid black line is the curve. The dotted lines show that each endpoint is collinear (on a straight line with) the control point on either side.

Categories