我有一组线,并且所有线都连接在一起,从而形成自定义形状。我也需要用一种颜色填充它。 一行包含以下内容:
"StartX":800.0,
"StartY":600.0,
"EndX":0.0,
"EndY":800.0,
"Radius":800.0
我看到您可以从点构建多边形,但是我需要一些曲线,因为它们具有半径。
<Polygon Points="50, 100 200, 100 200, 200 300, 30"
Stroke="Black" StrokeThickness="4"
Fill="Yellow" />
如何在画布上绘制自定义内容? 我忘了提一下,我将以编程方式而不是xaml来构建此形状。
这是一个行数组示例和预期结果:
"$values":[
{
"StartX":0.0,
"StartY":0.0,
"EndX":800.0,
"EndY":0.0,
"Radius":0.0
},
{
"StartX":800.0,
"StartY":0.0,
"EndX":800.0,
"EndY":400.0,
"Radius":0.0
},
{
"StartX":800.0,
"StartY":400.0,
"EndX":700.0,
"EndY":400.0,
"Radius":0.0
},
{
"StartX":700.0,
"StartY":400.0,
"EndX":372.727272727273,
"EndY":497.115756874933,
"Radius":600.0
},
{
"StartX":372.727272727273,
"StartY":497.115756874933,
"EndX":100.0,
"EndY":578.045554270711,
"Radius":500.0
},
{
"StartX":100.0,
"StartY":578.045554270711,
"EndX":0.0,
"EndY":578.045554270711,
"Radius":0.0
},
{
"StartX":0.0,
"StartY":578.045554270711,
"EndX":0.0,
"EndY":0.0,
"Radius":0.0
}
]
图像为:
答案 0 :(得分:0)
您应该使用Path
控件并使其适应您的需求。
从CodeProject Code project article获得:
public class RoundedCornersPolygon : Shape
{
private readonly Path _path;
#region Properties
private PointCollection _points;
/// <summary>
/// Gets or sets a collection that contains the points of the polygon.
/// </summary>
public PointCollection Points
{
get { return _points; }
set
{
_points = value;
RedrawShape();
}
}
private bool _isClosed;
/// <summary>
/// Gets or sets a value that specifies if the polygon will be closed or not.
/// </summary>
public bool IsClosed
{
get
{
return _isClosed;
}
set
{
_isClosed = value;
RedrawShape();
}
}
private bool _useRoundnessPercentage;
/// <summary>
/// Gets or sets a value that specifies if the ArcRoundness property value will be used as a percentage of the connecting segment or not.
/// </summary>
public bool UseRoundnessPercentage
{
get
{
return _useRoundnessPercentage;
}
set
{
_useRoundnessPercentage = value;
RedrawShape();
}
}
private double _arcRoundness;
/// <summary>
/// Gets or sets a value that specifies the arc roundness.
/// </summary>
public double ArcRoundness
{
get
{
return _arcRoundness;
}
set
{
_arcRoundness = value;
RedrawShape();
}
}
public Geometry Data
{
get
{
return _path.Data;
}
}
#endregion
public RoundedCornersPolygon()
{
var geometry = new PathGeometry();
geometry.Figures.Add(new PathFigure());
_path = new Path {Data = geometry};
Points = new PointCollection();
Points.Changed += Points_Changed;
}
private void Points_Changed(object sender, EventArgs e)
{
RedrawShape();
}
#region Implementation of Shape
protected override Geometry DefiningGeometry
{
get
{
return _path.Data;
}
}
#endregion
#region Private Methods
/// <summary>
/// Redraws the entire shape.
/// </summary>
private void RedrawShape()
{
var pathGeometry = _path.Data as PathGeometry;
if (pathGeometry == null) return;
var pathFigure = pathGeometry.Figures[0];
pathFigure.Segments.Clear();
for (int counter = 0; counter < Points.Count; counter++)
{
switch (counter)
{
case 0:
AddPointToPath(Points[counter], null, null);
break;
case 1:
AddPointToPath(Points[counter], Points[counter - 1], null);
break;
default:
AddPointToPath(Points[counter], Points[counter - 1], Points[counter - 2]);
break;
}
}
if (IsClosed)
CloseFigure(pathFigure);
}
/// <summary>
/// Adds a point to the shape
/// </summary>
/// <param name="currentPoint">The current point added</param>
/// <param name="prevPoint">Previous point</param>
/// <param name="prevPrevPoint">The point before the previous point</param>
private void AddPointToPath(Point currentPoint, Point? prevPoint, Point? prevPrevPoint)
{
if (Points.Count == 0)
return;
var pathGeometry = _path.Data as PathGeometry;
if(pathGeometry == null) return;
var pathFigure = pathGeometry.Figures[0];
//the first point of a polygon
if (prevPoint == null)
{
pathFigure.StartPoint = currentPoint;
}
//second point of the polygon, only a line will be drawn
else if (prevPrevPoint == null)
{
var lines = new LineSegment { Point = currentPoint };
pathFigure.Segments.Add(lines);
}
//third point and above
else
{
ConnectLinePoints(pathFigure, prevPrevPoint.Value, prevPoint.Value, currentPoint, ArcRoundness, UseRoundnessPercentage);
}
}
/// <summary>
/// Adds the segments necessary to close the shape
/// </summary>
/// <param name="pathFigure"></param>
private void CloseFigure(PathFigure pathFigure)
{
//No need to visually close the figure if we don't have at least 3 points.
if (Points.Count < 3)
return;
Point backPoint, nextPoint;
if (UseRoundnessPercentage)
{
backPoint = GetPointAtDistancePercent(Points[Points.Count - 1], Points[0], ArcRoundness, false);
nextPoint = GetPointAtDistancePercent(Points[0], Points[1], ArcRoundness, true);
}
else
{
backPoint = GetPointAtDistance(Points[Points.Count - 1], Points[0], ArcRoundness, false);
nextPoint = GetPointAtDistance(Points[0], Points[1], ArcRoundness, true);
}
ConnectLinePoints(pathFigure, Points[Points.Count - 2], Points[Points.Count - 1], backPoint, ArcRoundness, UseRoundnessPercentage);
var line2 = new QuadraticBezierSegment { Point1 = Points[0], Point2 = nextPoint };
pathFigure.Segments.Add(line2);
pathFigure.StartPoint = nextPoint;
}
/// <summary>
/// Method used to connect 2 segments with a common point, defined by 3 points and aplying an arc segment between them
/// </summary>
/// <param name="pathFigure"></param>
/// <param name="p1">First point, of the first segment</param>
/// <param name="p2">Second point, the common point</param>
/// <param name="p3">Third point, the second point of the second segment</param>
/// <param name="roundness">The roundness of the arc</param>
/// <param name="usePercentage">A value that indicates if the roundness of the arc will be used as a percentage or not</param>
private static void ConnectLinePoints(PathFigure pathFigure, Point p1, Point p2, Point p3, double roundness, bool usePercentage)
{
//The point on the first segment where the curve will start.
Point backPoint;
//The point on the second segment where the curve will end.
Point nextPoint;
if (usePercentage)
{
backPoint = GetPointAtDistancePercent(p1, p2, roundness, false);
nextPoint = GetPointAtDistancePercent(p2, p3, roundness, true);
}
else
{
backPoint = GetPointAtDistance(p1, p2, roundness, false);
nextPoint = GetPointAtDistance(p2, p3, roundness, true);
}
int lastSegmentIndex = pathFigure.Segments.Count - 1;
//Set the ending point of the first segment.
((LineSegment)(pathFigure.Segments[lastSegmentIndex])).Point = backPoint;
//Create and add the curve.
var curve = new QuadraticBezierSegment { Point1 = p2, Point2 = nextPoint };
pathFigure.Segments.Add(curve);
//Create and add the new segment.
var line = new LineSegment { Point = p3 };
pathFigure.Segments.Add(line);
}
/// <summary>
/// Gets a point on a segment, defined by two points, at a given distance.
/// </summary>
/// <param name="p1">First point of the segment</param>
/// <param name="p2">Second point of the segment</param>
/// <param name="distancePercent">Distance percent to the point</param>
/// <param name="firstPoint">A value that indicates if the distance is calculated by the first or the second point</param>
/// <returns></returns>
private static Point GetPointAtDistancePercent(Point p1, Point p2, double distancePercent, bool firstPoint)
{
double rap = firstPoint ? distancePercent / 100 : (100 - distancePercent) / 100;
return new Point(p1.X + (rap * (p2.X - p1.X)), p1.Y + (rap * (p2.Y - p1.Y)));
}
/// <summary>
/// Gets a point on a segment, defined by two points, at a given distance.
/// </summary>
/// <param name="p1">First point of the segment</param>
/// <param name="p2">Second point of the segment</param>
/// <param name="distance">Distance to the point</param>
/// <param name="firstPoint">A value that indicates if the distance is calculated by the first or the second point</param>
/// <returns>The point calculated.</returns>
private static Point GetPointAtDistance(Point p1, Point p2, double distance, bool firstPoint)
{
double segmentLength = Math.Sqrt(Math.Pow((p2.X - p1.X), 2) + Math.Pow((p2.Y - p1.Y), 2));
//The distance cannot be greater than half of the length of the segment
if (distance > (segmentLength / 2))
distance = segmentLength / 2;
double rap = firstPoint ? distance / segmentLength : (segmentLength - distance) / segmentLength;
return new Point(p1.X + (rap * (p2.X - p1.X)), p1.Y + (rap * (p2.Y - p1.Y)));
}
#endregion
}
像这样在您的视图中使用它:
... xmlns:CustomRoundedCornersPolygon="clr-namespace:CustomRoundedCornersPolygon" />
<CustomRoundedCornersPolygon:RoundedCornersPolygon Points="50, 100 200, 100 200, 200 300, 30"
StrokeThickness="1" ArcRoundness="25" UseRoundnessPercentage="False" Stroke="Black" IsClosed="True">
</CustomRoundedCornersPolygon:RoundedCornersPolygon>
我刚刚尝试过,它可以工作。试试看。
答案 1 :(得分:0)
我能够使用ArcSegment
来实现它为此,我需要一个起点和一个包含所有ArcSegments的PathSegmentCollection。 弧段具有弧的终点,扫掠方向(顺时针或逆时针),isLargeArc(如果它表示所描述的圆的大弧或小弧)和大小(这是x和y上的半径)弧段,因为弧段实际上是椭圆弧
所以我有这样的东西:
foreach (var line in contour)
{
pathSegmentCollection.Add(new ArcSegment
{
Size = new Size(line.Radius, line.Radius),
Point = new Point(line.EndX, line.EndY),
IsLargeArc = false,
SweepDirection = line.LineType == LineType.Clockwise ? SweepDirection.Clockwise : SweepDirection.Counterclockwise
});
}