1

I'm attempting to create a custom shape in WPF using a series of arc segments. The shape is essentially a circle with broken segments, something like this

enter image description here

whilst I know I can achieve something similar using an Ellipse and setting the dash array property, I need to deal with changing thickness and radius, i.e., I have to keep the number of visible segments constant and this becomes difficult using the dash array property since its relative to stroke thickness.

Therefore I'm attempting to create a custom shape similar to the SO question here. The shape described in that answer is only concerned with drawing a single arc whereas I need to draw a series arcs with gaps inbetween. This is what I'm attempting

 for ( int i = 1; i <= Segments; i++ )
 {
    var endpoint = PolarToCartesian(sweepAngle * i, Radius);    
    var drawArcSegment = i % 2 == 0;

    if (drawArcSegment)
    {
        var arcSegment = new ArcSegment
        {
           Point = endpoint,
           Size = new Size(Radius, Radius),
           IsLargeArc = false,
           SweepDirection = SweepDirection.Clockwise
        };
    }
    else
    {
        // WHAT TO DO HERE?
        // Need to either draw an arc segment that can't be seen but I can't apply
        // style properties to an arc segment OR need to move the current point of the
        // parent path figure to a new point that is the start of the following segment
    }
 }

Is this possible? Am I approaching this in the right manner?

James B
  • 8,975
  • 13
  • 45
  • 83

2 Answers2

3

It's not really difficult to calculate an appropriate stroke dash pattern.

Given a Path with an EllipseGeometry (where the radius is more precisely defined than for an Ellipse element)

<Path x:Name="path" StrokeThickness="20" Stroke="Black">
    <Path.Data>
        <EllipseGeometry RadiusX="100" RadiusY="100"/>
    </Path.Data>
</Path>

you can calculate the StrokeDashArray and StrokeDashOffset properties like this:

var ellipse = (EllipseGeometry)path.Data;
var strokeLength = 2 * Math.PI * ellipse.RadiusX / path.StrokeThickness;

var numSegments = 8;
var relativeSegmentLength = 0.75;

var segmentLength = strokeLength / numSegments * relativeSegmentLength;
var gapLength = strokeLength / numSegments * (1 - relativeSegmentLength);

path.StrokeDashArray = new DoubleCollection { segmentLength, gapLength };
path.StrokeDashOffset = -gapLength / 2;
Clemens
  • 123,504
  • 12
  • 155
  • 268
2

Start a new PathFigure for each ArcSegment, so the path doesn't have to be continuous.

Or use Clemens' answer, it's nicer.

public PathGeometry CreateGeometry(int segmentCount, double radius)
{
    double sweepAngle = 360.0 / (double)segmentCount;
    double segmentAngle = sweepAngle / 2;

    double startAngleOffset = segmentAngle * 0.3;
    double endAngleOffset = segmentAngle * 1.7;

    var pg = new PathGeometry();

    for (int i = 0; i < segmentCount; ++i)
    {
        double currentSegmentAngle = i * sweepAngle;

        pg.Figures.Add(new PathFigure
        {
            StartPoint = PolarToCartesian(currentSegmentAngle + startAngleOffset, radius),
            Segments = {
                new ArcSegment{
                    Size = new Size(radius, radius),
                    SweepDirection = SweepDirection.Clockwise,
                    IsLargeArc = false,
                    Point = PolarToCartesian(currentSegmentAngle + endAngleOffset, radius)
                }
            }
        });
    }

    return pg;
}

The stroke thickness isn't quite the same as yours but you can figure that out. The line caps in this are radii. In yours, they're not: Yours are as if somebody erased a wide line across radius. If you want that, you can't let the stroke do the work for you. You'll have to draw closed and filled PathFigures, each with two arcs and two end lines.

enter image description here