Proxy graphic in DXF binary chunk interpretation

by Fenton Webb

Issue

How to interpret the proxy graphic binary data enclosed in AutoCAD DXF files?

Solution

Binary data enclosed in DXF files is stored in blocks of up to 256 bytes under DXF code 310
(i.e., kDxfBinaryChunk). A complete description of standard binary chunks is available in
DXF binary chunk interpretation.
This document describes the proxy graphics binary chunk.

The DXF ENTITY proxy-graphics section is present for non-core AutoCAD entities (custom entities or entities defined in ARX/DBX modules).
It is always preceded by a DXF code 92 (an int32) which indicates the length of the following binary chunk.

The binary chunk contains the proxy graphics generated when the entity’s saveAs() method was called
by AutoCAD during a DXFOUT. It also contains non-graphical metadata (bounding box, color, etc.).

The entity’s saveAs() may call worldDraw(). If worldDraw() returns
false, the system calls viewportDraw() to generate viewport-dependent graphics.
Thus the proxy graphic binary chunk holds the primitive calls recorded during worldDraw() or
viewportDraw(). The chunk is similar in spirit to a Windows Metafile: it contains drawing instructions
rather than a bitmap.

Depending on AutoCAD settings (for example, “Proxy Graphic Never Saved”) and whether the ARX/DBX module
was present at the time of DXFOUT, the proxy graphics chunk can contain:

  • A bounding box with class name and logical application name.
  • The last known entity graphic description.

The proxy graphics binary chunk is the serialized output of the AcGiWorldDraw/AcGiViewportDraw vector-collector classes
invoked during DXFOUT. Its high-level form is:


{Header}[{Command} [{Command} [{Command} [...]]]]
  

Each {Command} packet has the form:


{Command Packet Length} {Command OPCODE} [{Command Argument} {Command Argument} ...]
  

Interpretation Rules

Header Interpretation

To interpret the {Header}:


{int32 / Length of the binary chunk} {int32 / Number of commands}
  
Command Packet Interpretation

To interpret each {Command} packet:


{int32 / Command Packet Length} {int32 / Command OPCODE} [{Command Argument} {Command Argument} ...]
  

The {Command Argument}(s) depend on the OPCODE — see the opcode list below.

Command Opcodes

OPCODE = 0 (kAcGiOpBad)

<No AcGiGeometry primitive equivalent>

Should not happen. If it does, skip ahead by the packet length. Present for backward compatibility (R13/R14).


{Nothing}
    
OPCODE = 1 (kAcGiOpSetExtents)

<No AcGiGeometry primitive equivalent>

Defines an extent by providing two 3D points (min and max).


{AcGePoint3d / Min point} {AcGePoint3d / Max point}
    
OPCODE = 2 (kAcGiOpCircle1)

<AcGiGeometry::circle (const AcGePoint3d &center, double radius, const AcGeVector3d &normal)>

Displays a circle primitive with center and radius.


{AcGePoint3d / Center} {double / Radius} {AcGeVector3d / Normal}
    
OPCODE = 3 (kAcGiOpCircle2)

<AcGiGeometry::circle (const AcGePoint3d &pt1, const AcGePoint3d &pt2, const AcGePoint3d &pt3)>

Circle defined by three points on its circumference.


{AcGePoint3d / 1st point} {AcGePoint3d / 2nd point} {AcGePoint3d / 3rd point}
    
OPCODE = 4 (kAcGiOpCircularArc1)

<AcGiGeometry::circularArc (const AcGePoint3d &center, double radius, const AcGeVector3d &normal, const AcGeVector3d &startVector, double sweepAngle, AcGiArcType arcType)>

Arc defined by center, radius, normal, start vector, angle and arc type.


{AcGePoint3d / Center} {double / Radius} {AcGeVector3d / Normal} {AcGeVector3d / Start vector} {double / Angle} {int32 / Arc type}
    
OPCODE = 5 (kAcGiOpCircularArc2)

<AcGiGeometry::circularArc (const AcGePoint3d &start, const AcGePoint3d &point, const AcGePoint3d &end, AcGiArcType arcType)>

Arc defined by three points and an arc type.


{AcGePoint3d / 1st point} {AcGePoint3d / 2nd point} {AcGePoint3d / 3rd point} {int32 / Arc type}
    
OPCODE = 6 (kAcGiOpPolyline)

<AcGiGeometry::polyline (uint32_t nbPoints, const AcGePoint3d* pVertexList, const AcGeVector3d* pNormal, int32_t lBaseSubEntMarker)>

Draws a polyline.


{int32 / Number of vertices} {AcGePoint3d / Vertex} [{AcGePoint3d / Vertex} [...]]
    
OPCODE = 7 (kAcGiOpPolygon)

<AcGiGeometry::polygon (uint32_t nbPoints, const AcGePoint3d* pVertexList)>

Draws a polygon.


{int32 / Number of vertices} {AcGePoint3d / Vertex} [{AcGePoint3d / Vertex} [...]]
    
OPCODE = 8 (kAcGiOpMesh)

<AcGiGeometry::mesh (uint32_t rows, uint32_t columns, const AcGePoint3d* pVertexList, const AcGiEdgeData* pEdgeData, const AcGiFaceData* pFaceData, const AcGiVertexData* pVertexData)>

Draws a mesh.


{int32 / Number of rows} {int32 / Number of columns} {Vertex List} {Edge Data Packet} {Face Data Packet} {Vertex Data Packet}
    
Calculations
  • Number of vertices: {Number of rows} * {Number of columns}
  • Number of edges: 2 * {Number of vertices} - {Number of rows} - {Number of columns}
  • Number of faces: ({Number of rows} - 1) * ({Number of columns} - 1)
{Vertex List} Structure

{AcGePoint3d / Vertex} [{AcGePoint3d / Vertex} [...]]
    
{Edge Data Packet} Structure

{int32 / Edge info type} {Edge data}
    

Edge info type is bitwise: ACGI_COLORS (0x01), ACGI_LAYER_IDS (0x100),
ACGI_LINETYPE_IDS (0x200), ACGI_MARKERS (0x20), ACGI_VIS_DATA (0x40).

{Edge data} Sub-structures (order dependent)

// ACGI_COLORS: array of int16
{int16 / Color index} [{int16 / Color index} [...]]

// ACGI_LAYER_IDS: array of ids
{AcDbHardPointerId / Layer ID} [{AcDbHardPointerId / Layer ID} [...]]

// ACGI_LINETYPE_IDS: array of ids
{AcDbHardPointerId / Linetype ID} [{AcDbHardPointerId / Linetype ID} [...]]

// ACGI_MARKERS: array of int32
{int32 / ACGI marker} [{int32 / ACGI marker} [...]]

// ACGI_VIS_DATA: array of booleans
{int8 / Visibility flag} [{int8 / Visibility flag} [...]]
    
{Face Data Packet} Structure

{int32 / Face info type} {Face data}
    

Face info type is bitwise: ACGI_COLORS (0x01), ACGI_LAYER_IDS (0x100),
ACGI_MARKERS (0x20), ACGI_NORMALS (0x80), ACGI_VIS_DATA (0x40).

{Face data} Sub-structures (order dependent)

// ACGI_COLORS
{int16 / Color index} [{int16 / Color index} [...]]

// ACGI_LAYER_IDS
{AcDbHardPointerId / Layer ID} [{AcDbHardPointerId / Layer ID} [...]]

// ACGI_MARKERS
{int32 / ACGI marker} [{int32 / ACGI marker} [...]]

// ACGI_NORMALS
{AcGeVector3d / Normal} [{AcGeVector3d / Normal} [...]]

// ACGI_VIS_DATA
{int8 / Visibility flag} [{int8 / Visibility flag} [...]]
    
{Vertex Data Packet} Structure

{int32 / Vertex info type} {Vertex data}
    

Vertex info type is bitwise: ACGI_NORMALS (0x80), ACGI_ORIENTATION (0x400).

{Vertex data} Sub-structures (order dependent)

// ACGI_NORMALS
{AcGeVector3d / Normal} [{AcGeVector3d / Normal} [...]]

// ACGI_ORIENTATION
{int32 / AcGiOrientationType} [{int32 / AcGiOrientationType} [...]]
    
OPCODE = 9 (kAcGiOpShell)

<AcGiGeometry::shell (uint32_t nbVertex, const AcGePoint3d* pVertexList, uint32_t faceListSize, const int32_t* pFaceList, const AcGiEdgeData* pEdgeData, const AcGiFaceData* pFaceData, const AcGiVertexData* pVertexData, const resbuf* pResBuf)>

Draws a shell.


{int32 / Number of vertices} {Vertex List} {int32 / Face list size} {Face list} {Edge Data Packet} {Face Data Packet} {Vertex Data Packet}
    
{Vertex List}

{AcGePoint3d / Number of vertices} [{AcGePoint3d / Vertex} [...]]
    
{Face list}

{int32 / Face vertex count} {int32 / Vertex index} [{int32 / Vertex index} [...]]
    

The Edge Data Packet, Face Data Packet, and Vertex Data Packet follow the same definitions as OPCODE 8 (kAcGiOpMesh).

OPCODE = 10 (kAcGiOpText1)

<AcGiGeometry::text (const AcGePoint3d &position, const AcGeVector3d &normal, const AcGeVector3d &direction, double height, double width, double oblique, const char* pMsg)>

Draws text.


{AcGePoint3d / Position} {AcGeVector3d / Normal} {AcGeVector3d / Direction}
{double / Height} {double / Width} {double / Oblique} {char* / Text string}
    
OPCODE = 11 (kAcGiOpText2)

<AcGiGeometry::text (const AcGePoint3d &position, const AcGeVector3d &normal, const AcGeVector3d &direction, const char* pMsg, int32_t length, bool raw, const AcGiTextStyle &pTextStyle)>

Draws text with style and options.


{AcGePoint3d / Position} {AcGeVector3d / Normal} {AcGeVector3d / Direction}
{char* / Text string} {int32 / length} {int32 / Raw code} {double / Text size}
{double / Xscale} {double / Obliquing angle} {double / Tracking percentage}
{int32 / IsBackward} {int32 / IsUpsideDown} {int32 / IsVertical}
{int32 / IsUnderlined} {int32 / IsOverlined} {char* / Font name}
{char* / Big font name}
    
OPCODE = 12 (kAcGiOpXLine)

<AcGiGeometry::xline (const AcGePoint3d &oneXlinePoint, const AcGePoint3d &aDifferentXlinePoint)>

Constructs an xline passing through two points.


{AcGePoint3d / 1st point} {AcGePoint3d / 2nd point}
    
OPCODE = 13 (kAcGiOpRay)

<AcGiGeometry::ray (const AcGePoint3d &raysStartingPoint, const AcGePoint3d &aDifferentRayPoint)>

Ray starting at one point and passing through a second point.


{AcGePoint3d / 1st point} {AcGePoint3d / 2nd point}
    
OPCODE = 14 (kAcGiOpColor)

<AcGiSubEntityTraits::setColor (uint16_t color)>


{uint32 / Color index}
    
OPCODE = 15 (kAcGiOpLayerName)

<AcGiSubEntityTraits::setLayer (const AcDbObjectId layerId)>


{char* / Layer name}
    
OPCODE = 16 (kAcGiOpLayerIndex)

<AcGiSubEntityTraits::setLayer (const AcDbObjectId layerId)>


{AcDbHardPointerId / Layer ID}
    
OPCODE = 17 (kAcGiOpLineTypeName)

<AcGiSubEntityTraits::setLineType (const AcDbObjectId linetypeId)>


{char* / Linetype name}
    
OPCODE = 18 (kAcGiOpLineTypeIndex)

<AcGiSubEntityTraits::setLineType (const AcDbObjectId linetypeId)>


{AcDbHardPointerId / Linetype ID}
    
OPCODE = 19 (kAcGiOpSelectionMarker)

<AcGiSubEntityTraits::setSelectionMarker (int32_t markerId)>


{uint32 / ACGI marker}
    
OPCODE = 20 (kAcGiOpFillType)

<AcGiSubEntityTraits::setFillType (AcGiFillType fill)>


{uint32 / AcGiFillType}
    
OPCODE = 21 (kAcGiBoundingBoxSave)

<No AcGiGeometry primitive equivalent>


{Nothing}
    
OPCODE = 22 (kAcGiOpTrueColor)

<AcGiSubEntityTraits::setTrueColor (const AcCmEntityColor &color)>


{AcCmEntityColor / True color definition}
    

Comment: introduced in Tahoe (AutoCAD 2000).

OPCODE = 23 (kAcGiOpLineWeight)

<AcGiSubEntityTraits::setLineWeight (const AcDb::LineWeight lw)>


{double / Lineweight}
    
OPCODE = 24 (kAcGiOpLineTypeScale)

<AcGiSubEntityTraits::setLineTypeScale (double dScale)>


{double / Line scale}
    
OPCODE = 25 (kAcGiOpThickness)

<AcGiSubEntityTraits::setThickness (double dThickness)>


{double / Thickness}
    
OPCODE = 26 (kAcGiOpPlotStyleName)

<AcGiSubEntityTraits::setPlotStyleName (AcDb::PlotStyleNameType type, const AcDbObjectId &id)>


{int32 / PlotStyle name type} {int32 / ID}
    
OPCODE = 27 (kAcGiOpPushClipBoundary)

<AcGiGeometry::pushClipBoundary (AcGiClipBoundary* pBoundary)>


{AcGiClipBoundary / Clip Boundary}
    
AcGiClipBoundary Structure

struct AcGiClipBoundary {
  // Boundaries
  AcGeVector3d m_vNormal;
  AcGePoint3d  m_ptPoint;
  AcGePoint2dArray m_aptPoints;

  // Transforms
  AcGeMatrix3d m_xToClipSpace;
  AcGeMatrix3d m_xInverseBlockRefXForm;

  // Z clipping
  Adesk::Boolean m_bClippingFront;
  Adesk::Boolean m_bClippingBack;
  double m_dFrontClipZ;
  double m_dBackClipZ;

  Adesk::Boolean m_bDrawBoundary;
};
    
OPCODE = 28 (kAcGiOpPopClipBoundary)

<AcGiGeometry::popClipBoundary ()>


{Nothing}
    
OPCODE = 29 (kAcGiOpPushTransformM)

<No AcGiGeometry primitive equivalent>


{AcGeMatrix3d / Matrix}
    
OPCODE = 30 (kAcGiOpPushTransformV)

<No AcGiGeometry primitive equivalent>


{AcGeMatrix3d / Matrix}
    
OPCODE = 31 (kAcGiOpPopTransform)

<No AcGiGeometry primitive equivalent>


{Nothing}
    
OPCODE = 32 (kAcGiOpPlineNormal)

<AcGiViewportGeometry::polylineEye (uint32_t nbPoints, const AcGePoint3d* pPoints) or AcGiViewportGeometry::polylineDc (uint32_t nbPoints, const AcGePoint3d* pPoints)>


{int32 / Number of points} {AcGePoint3d / Point definition} [{AcGePoint3d / Point definition} [...]] {AcGeVector3d / Normal}
    
OPCODE = 33 (kAcGiOpMaxOpCodes)

<No AcGiGeometry primitive equivalent>

Should not happen. If it does, skip to packet length. Present for forward compatibility.


{Nothing}
    

Comments

3 responses to “Proxy graphic in DXF binary chunk interpretation”

  1. A reader of my book “Mastering AutoCAD Objects” (which in 1999 documented this stuff) complained that AutoCAD 2014 DXF contains additional command codes. Ha claims he found codes 33
    (no longer kAcGiOpMaxOpCodes), 38, and 51. Is thare any updated version of this document?
    Dietmar

  2. Martin Kroeker Avatar
    Martin Kroeker

    In case anybody still reads this (google searches for acad proxy graphics tend to lead here), at least opcode 38 appears to be real and to be some variation of kAcGiOpText2 with the string in UTF16.
    I am sure an update to this document would be very welcome, but cynical me suspects that this glimpse of the internals could be released only because it was known to be outdated soon.

  3. Came here looking for what opcode 51 does.
    Opcode 38 is “kAcGiOpUnicodeText2” (?) as far as I know, also opcode 36 appears to also be unicode text.

Leave a Reply

Discover more from Autodesk Developer Blog

Subscribe now to keep reading and get access to the full archive.

Continue reading