Question

I'm trying to draw an imported DXF file using WPF over a canvas. I've been using DXFLib (available here) to read and parse various files and it seems to work really well.

I'm now in the process to draw all the entities in the DXF but I'm stuck with ARC. My problem is similar to the one in this post (some arcs are drawn in wrong direction):

DXF Parser : Ellipses angle direction

In the DXF file the ARC is stored as: Center, Radius, StartAngle, EndAngle; in WPF the ArcSegment is instead described as: StartPoint, EndPoint, Size, IsLargeArc and SweepDirection.

The latter is the one that I think cause the problem, as I'm not able to determine direction by the DXF File. In the above question is stated to use the "Extrude Direction" which it happen NOT to be included in my file.

Following the relevant code:

private Load(string filename)
{
    DXFLib.DXFDocument doc = new DXFLib.DXFDocument();
    doc.Load(filename);

    if (doc.Entities.Count > 0)
    {
        foreach (DXFLib.DXFEntity entity in doc.Entities)
        {
            if (entity is DXFLib.DXFLine)
            {
                DXFLib.DXFLine line = (DXFLib.DXFLine)entity;
                PointF start = new PointF((float)line.Start.X, (float)line.Start.Y);
                PointF end = new PointF((float)line.End.X, (float)line.End.Y);

                Line drawLine = new Line();
                drawLine.Stroke = System.Windows.Media.Brushes.LightSteelBlue;
                drawLine.X1 = end.X * scaleX;
                drawLine.X2 = start.X * scaleX;
                drawLine.Y1 = end.Y * scaleY;
                drawLine.Y2 = start.Y * scaleY;
                drawLine.StrokeThickness = 1;
                canvas.Children.Add(drawLine);
            }
            else if (entity is DXFLib.DXFCircle)
            {
                DXFLib.DXFCircle circle = (DXFLib.DXFCircle)entity;
                Ellipse drawCircle = new Ellipse();
                SolidColorBrush solidBrush = new SolidColorBrush();
                solidBrush.Color = System.Windows.Media.Color.FromArgb(255, 255, 255, 0);
                drawCircle.Fill = solidBrush;
                drawCircle.StrokeThickness = 1;
                drawCircle.Stroke = System.Windows.Media.Brushes.RoyalBlue;
                drawCircle.Width = circle.Radius * 2 * scaleX;
                drawCircle.Height = circle.Radius * 2 * scaleY;
                drawCircle.Margin = new Thickness((circle.Center.X.Value - circle.Radius) * scaleX, (circle.Center.Y.Value - circle.Radius) * scaleY, 0, 0);
                canvas.Children.Add(drawCircle);                            
            }
            else if (entity is DXFLib.DXFArc)
            {
                DXFLib.DXFArc arc = (DXFLib.DXFArc)entity;

                Path path = new Path();
                path.Stroke = System.Windows.Media.Brushes.Black;
                path.StrokeThickness = 1;

                System.Windows.Point endPoint = new System.Windows.Point(
                    (arc.Center.X.Value + Math.Cos(arc.EndAngle * Math.PI / 180) * arc.Radius) * scaleX,
                    (arc.Center.Y.Value + Math.Sin(arc.EndAngle * Math.PI / 180) * arc.Radius) * scaleY);

                System.Windows.Point startPoint = new System.Windows.Point(
                    (arc.Center.X.Value + Math.Cos(arc.StartAngle * Math.PI / 180) * arc.Radius) * scaleX,
                    (arc.Center.Y.Value + Math.Sin(arc.StartAngle * Math.PI / 180) * arc.Radius) * scaleY);

                ArcSegment arcSegment = new ArcSegment();
                double sweep = 0.0;
                if (arc.EndAngle < arc.StartAngle)
                    sweep = (360 + arc.EndAngle) - arc.StartAngle;
                else sweep = Math.Abs(arc.EndAngle - arc.StartAngle);

                arcSegment.IsLargeArc = sweep >= 180;
                arcSegment.Point = endPoint;
                arcSegment.Size = new System.Windows.Size(arc.Radius * scaleX, arc.Radius * scaleY);
                arcSegment.SweepDirection = arc.ExtrusionDirection.Z >= 0 ? SweepDirection.Clockwise : SweepDirection.Counterclockwise;

                PathGeometry geometry = new PathGeometry();
                PathFigure pathFigure = new PathFigure();
                pathFigure.StartPoint = startPoint;
                pathFigure.Segments.Add(arcSegment);
                geometry.Figures.Add(pathFigure);

                path.Data = geometry;
                canvas.Children.Add(path);    
            }
        }
    }
}

The XAML file is sample as:

<Window x:Class="DxfViewer.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Canvas Name="canvas">
        <Canvas.LayoutTransform>
            <ScaleTransform ScaleX="1" ScaleY="-1" CenterX=".5" CenterY=".5" />
        </Canvas.LayoutTransform>
    </Canvas>
</Window>

** UPDATE **

The issue has been resolved in the following way:

ArcSegment arcSegment = new ArcSegment();
double sweep = 0.0;
if (arc.EndAngle < arc.StartAngle)
    sweep = (360 + arc.EndAngle) - arc.StartAngle;
else sweep = Math.Abs(arc.EndAngle - arc.StartAngle);

arcSegment.IsLargeArc = sweep >= 180;
arcSegment.Point = endPoint;
arcSegment.Size = new System.Windows.Size(arc.Radius * scaleX, arc.Radius * scaleY);
arcSegment.SweepDirection = arc.ExtrusionDirection.Z >= 0 ? SweepDirection.Clockwise : SweepDirection.Counterclockwise;
Was it helpful?

Solution

Here is the DXF reference for arcs according to AutoDesk:

Group codes Description
100

Subclass marker (AcDbCircle)
39

Thickness (optional; default = 0)
10

Center point (in OCS)
DXF: X value; APP: 3D point
20, 30

DXF: Y and Z values of center point (in OCS)
40

Radius
100

Subclass marker (AcDbArc)
50

Start angle
51

End angle
210

Extrusion direction. (optional; default = 0, 0, 1)
DXF: X value; APP: 3D vector
220, 230

DXF: Y and Z values of extrusion direction (optional)

The author of DXFLib accounted for these in his DxfArc class, so are these values not set at all at runtime? I don't see anything in his code for setting a default value, which probably needs to be added since AutoCAD is making the assumption.

The extrusion direction has to be stored as values 220, 230, otherwise the default should ALWAYS work. If that's not the case, I'd take a close look at how these DFX files are being run. Are they an earlier release that doesn't support this operation?

Update:

I think the DxfArc class really should be modified to set a null ExtrusionDirection to {0, 0, 1} based on the code and your project. I modified your main routine with the following changes and it appears to work correctly:

// Changing the class will make this less ugly
var arc = (DXFLib.DXFArc)entity;
if (arc.ExtrusionDirection.X == null ||
    arc.ExtrusionDirection.Y == null ||
    arc.ExtrusionDirection.Z == null)
{
    arc.ExtrusionDirection.X = 0;
    arc.ExtrusionDirection.Y = 0;
    arc.ExtrusionDirection.Z = 1;
}

arcSegment.SweepDirection = arc.ExtrusionDirection.Z > 0
    ? SweepDirection.Clockwise
    : SweepDirection.Counterclockwise;

When you look at the Arc in AutoCAD, the ExtrusionDirection is listed as 'Normal' under the entity's properties.

OTHER TIPS

I would like to modify your solution since the situation was exactly same in my case and this solved it.

ARC goes always counter clockwise around the extrusion vector, which is (0, 0, 1) by default and the usual case for 2D arcs.

    arcSegment.SweepDirection = arc.ExtrusionDirection.Z > 0
    ? SweepDirection.Counterclockwise
    : SweepDirection.Clockwise;


Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top