// dxf.cpp
// Copyright (c) 2009, Dan Heeks
// This program is released under the BSD license. See the file COPYING for details.
// modified 2018 wandererfan

#include "PreCompiled.h"

//required by windows for M_PI definition
#define _USE_MATH_DEFINES
#include <cmath>

#include <iomanip>

#include <src/Build/Version.h>
#include <Base/Console.h>
#include <Base/FileInfo.h>
#include <Base/Parameter.h>
#include <Base/Vector3D.h>
#include "dxf.h"

using namespace std;

Base::Vector3d toVector3d(const double* a)
{
    Base::Vector3d result;
    result.x = a[0];
    result.y = a[1];
    result.z = a[2];
    return result;
}

CDxfWrite::CDxfWrite(const char* filepath) :
//TODO: these should probably be parms in config file
m_entityHandle(0x300),
m_layerHandle(0x30),
m_blockHandle(0x210),
m_blkRecordHandle(0x110),
m_polyOverride(false),
m_layerName("none")
{
    // start the file
    m_fail = false;
    m_version = 12;
    m_ofs = new ofstream(filepath, ios::out);
    m_ssBlock     = new std::ostringstream();
    m_ssBlkRecord = new std::ostringstream();
    m_ssEntity    = new std::ostringstream();
    m_ssLayer     = new std::ostringstream();

    if(!(*m_ofs)){
        m_fail = true;
        return;
    }
    m_ofs->imbue(std::locale("C"));
}

CDxfWrite::~CDxfWrite()
{
    delete m_ofs;
    delete m_ssBlock;
    delete m_ssBlkRecord;
    delete m_ssEntity;
    delete m_ssLayer;
}

void CDxfWrite::init(void)
{
    writeHeaderSection();
    makeBlockRecordTableHead();
    makeBlockSectionHead();
}

//! assemble pieces into output file
void CDxfWrite::endRun(void)
{
    makeLayerTable();
    makeBlockRecordTableBody();
    
    writeClassesSection();
    writeTablesSection();
    writeBlocksSection();
    writeEntitiesSection();
    writeObjectsSection();

    (*m_ofs) << "  0"         << endl;
    (*m_ofs) << "EOF";
}

//***************************
//writeHeaderSection
//added by Wandererfan 2018 (wandererfan@gmail.com) for FreeCAD project
void CDxfWrite::writeHeaderSection(void)
{
    std::stringstream ss;
    ss << "FreeCAD v" << FCVersionMajor << "." << FCVersionMinor << " " << FCRevision; 

    //header & version
    (*m_ofs) << "999"      << endl;
    (*m_ofs) << ss.str()   << endl;

    //static header content
    ss.str("");
    ss.clear();
    ss << "header" << m_version << ".rub";
    std::string fileSpec = m_dataDir + ss.str();
    (*m_ofs) << getPlateFile(fileSpec);
}

//***************************
//writeClassesSection
//added by Wandererfan 2018 (wandererfan@gmail.com) for FreeCAD project
void CDxfWrite::writeClassesSection(void)
{
    if (m_version < 14) {
        return;
    }
    
    //static classes section content
    std::stringstream ss;
    ss << "classes" << m_version << ".rub";
    std::string fileSpec = m_dataDir + ss.str();
    (*m_ofs) << getPlateFile(fileSpec);
}

//***************************
//writeTablesSection
//added by Wandererfan 2018 (wandererfan@gmail.com) for FreeCAD project
void CDxfWrite::writeTablesSection(void)
{
    //static tables section head end content
    std::stringstream ss;
    ss << "tables1" << m_version << ".rub";
    std::string fileSpec = m_dataDir + ss.str();
    (*m_ofs) << getPlateFile(fileSpec);

    (*m_ofs) << (*m_ssLayer).str();

    //static tables section tail end content
    ss.str("");
    ss.clear();
    ss << "tables2" << m_version << ".rub";
    fileSpec = m_dataDir + ss.str();
    (*m_ofs) << getPlateFile(fileSpec);

    if (m_version > 12) {
        (*m_ofs) << (*m_ssBlkRecord).str();
        (*m_ofs) << "  0"      << endl;
        (*m_ofs) << "ENDTAB"   << endl;
    }
    (*m_ofs) << "  0"      << endl;
    (*m_ofs) << "ENDSEC"   << endl;
}

//***************************
//makeLayerTable
//added by Wandererfan 2018 (wandererfan@gmail.com) for FreeCAD project
void CDxfWrite::makeLayerTable(void)
{
    std::string tablehash = getLayerHandle();
    (*m_ssLayer) << "  0"      << endl;
    (*m_ssLayer) << "TABLE"    << endl;
    (*m_ssLayer) << "  2"      << endl;
    (*m_ssLayer) << "LAYER"    << endl;
    (*m_ssLayer) << "  5"      << endl;
    (*m_ssLayer) << tablehash  << endl;
    if (m_version > 12) {
        (*m_ssLayer) << "330"      << endl;
        (*m_ssLayer) << 0          << endl;
        (*m_ssLayer) << "100"      << endl;
        (*m_ssLayer) << "AcDbSymbolTable"   << endl;
    }
    (*m_ssLayer) << " 70"      << endl;
    (*m_ssLayer) << m_layerList.size() + 1 << endl;

    (*m_ssLayer) << "  0"      << endl;
    (*m_ssLayer) << "LAYER"    << endl;
    (*m_ssLayer) << "  5"      << endl;
    (*m_ssLayer) << getLayerHandle()  << endl;
    if (m_version > 12) {
        (*m_ssLayer) << "330"      << endl;
        (*m_ssLayer) << tablehash  << endl;
        (*m_ssLayer) << "100"      << endl;
        (*m_ssLayer) << "AcDbSymbolTableRecord"      << endl;
        (*m_ssLayer) << "100"      << endl;
        (*m_ssLayer) << "AcDbLayerTableRecord"      << endl;
    }
    (*m_ssLayer) << "  2"      << endl;
    (*m_ssLayer) << "0"        << endl;
    (*m_ssLayer) << " 70"      << endl;
    (*m_ssLayer) << "   0"     << endl;
    (*m_ssLayer) << " 62"      << endl;
    (*m_ssLayer) << "   7"     << endl;
    (*m_ssLayer) << "  6"      << endl;
    (*m_ssLayer) << "CONTINUOUS" << endl;

    for (auto& l: m_layerList) {
        (*m_ssLayer) << "  0"      << endl;
        (*m_ssLayer) << "LAYER"      << endl;
        (*m_ssLayer) << "  5"      << endl;
        (*m_ssLayer) << getLayerHandle() << endl;
        if (m_version > 12) {
            (*m_ssLayer) << "330"      << endl;
            (*m_ssLayer) << tablehash  << endl;
            (*m_ssLayer) << "100"      << endl;
            (*m_ssLayer) << "AcDbSymbolTableRecord"      << endl;
            (*m_ssLayer) << "100"      << endl;
            (*m_ssLayer) << "AcDbLayerTableRecord"      << endl;
        }
        (*m_ssLayer) << "  2"      << endl;
        (*m_ssLayer) << l << endl;
        (*m_ssLayer) << " 70"      << endl;
        (*m_ssLayer) << "    0"      << endl;
        (*m_ssLayer) << " 62"      << endl;
        (*m_ssLayer) << "    7"      << endl;
        (*m_ssLayer) << "  6"      << endl;
        (*m_ssLayer) << "CONTINUOUS"      << endl;
    }
    (*m_ssLayer) << "  0"      << endl;
    (*m_ssLayer) << "ENDTAB"   << endl;
}

//***************************
//makeBlockRecordTableHead
//added by Wandererfan 2018 (wandererfan@gmail.com) for FreeCAD project
void CDxfWrite::makeBlockRecordTableHead(void)
{
    if (m_version < 14) {
        return;
    }
        std::string tablehash = getBlkRecordHandle();
        m_saveBlockRecordTableHandle = tablehash;
        (*m_ssBlkRecord) << "  0"      << endl;
        (*m_ssBlkRecord) << "TABLE"      << endl;
        (*m_ssBlkRecord) << "  2"      << endl;
        (*m_ssBlkRecord) << "BLOCK_RECORD"      << endl;
        (*m_ssBlkRecord) << "  5"      << endl;
        (*m_ssBlkRecord) << tablehash  << endl;
        (*m_ssBlkRecord) << "330"      << endl;
        (*m_ssBlkRecord) << "0"        << endl;
        (*m_ssBlkRecord) << "100"      << endl;
        (*m_ssBlkRecord) << "AcDbSymbolTable"      << endl;
        (*m_ssBlkRecord) << "  70"      << endl;
        (*m_ssBlkRecord) << (m_blockList.size() + 5)   << endl;
        
        m_saveModelSpaceHandle = getBlkRecordHandle();
        (*m_ssBlkRecord) << "  0"      << endl;
        (*m_ssBlkRecord) << "BLOCK_RECORD"      << endl;
        (*m_ssBlkRecord) << "  5"      << endl;
        (*m_ssBlkRecord) << m_saveModelSpaceHandle  << endl;
        (*m_ssBlkRecord) << "330"      << endl;
        (*m_ssBlkRecord) << tablehash  << endl;
        (*m_ssBlkRecord) << "100"      << endl;
        (*m_ssBlkRecord) << "AcDbSymbolTableRecord"      << endl;
        (*m_ssBlkRecord) << "100"      << endl;
        (*m_ssBlkRecord) << "AcDbBlockTableRecord"      << endl;
        (*m_ssBlkRecord) << "  2"      << endl;
        (*m_ssBlkRecord) << "*MODEL_SPACE"   << endl;
//        (*m_ssBlkRecord) << "  1"      << endl;
//        (*m_ssBlkRecord) << " "        << endl;

        m_savePaperSpaceHandle = getBlkRecordHandle();
        (*m_ssBlkRecord) << "  0"      << endl;
        (*m_ssBlkRecord) << "BLOCK_RECORD"  << endl;
        (*m_ssBlkRecord) << "  5"      << endl;
        (*m_ssBlkRecord) << m_savePaperSpaceHandle  << endl;
        (*m_ssBlkRecord) << "330"      << endl;
        (*m_ssBlkRecord) << tablehash  << endl;
        (*m_ssBlkRecord) << "100"      << endl;
        (*m_ssBlkRecord) << "AcDbSymbolTableRecord"      << endl;
        (*m_ssBlkRecord) << "100"      << endl;
        (*m_ssBlkRecord) << "AcDbBlockTableRecord"      << endl;
        (*m_ssBlkRecord) << "  2"      << endl;
        (*m_ssBlkRecord) << "*PAPER_SPACE"   << endl;
//        (*m_ssBlkRecord) << "  1"      << endl;
//        (*m_ssBlkRecord) << " "        << endl;
}
 
//***************************
//makeBlockRecordTableBody
//added by Wandererfan 2018 (wandererfan@gmail.com) for FreeCAD project
void CDxfWrite::makeBlockRecordTableBody(void)
{
    if (m_version < 14) {
        return;
    }
    
    int iBlkRecord = 0;
    for (auto& b: m_blockList) {
        (*m_ssBlkRecord) << "  0"      << endl;
        (*m_ssBlkRecord) << "BLOCK_RECORD"      << endl;
        (*m_ssBlkRecord) << "  5"      << endl;
        (*m_ssBlkRecord) << m_blkRecordList.at(iBlkRecord)      << endl;
        (*m_ssBlkRecord) << "330"      << endl;
        (*m_ssBlkRecord) << m_saveBlockRecordTableHandle  << endl;
        (*m_ssBlkRecord) << "100"      << endl;
        (*m_ssBlkRecord) << "AcDbSymbolTableRecord"      << endl;
        (*m_ssBlkRecord) << "100"      << endl;
        (*m_ssBlkRecord) << "AcDbBlockTableRecord"      << endl;
        (*m_ssBlkRecord) << "  2"      << endl;
        (*m_ssBlkRecord) << b          << endl;
//        (*m_ssBlkRecord) << " 70"      << endl;
//        (*m_ssBlkRecord) << "    0"      << endl;
        iBlkRecord++;
    }
}

//***************************
//makeBlockSectionHead
//added by Wandererfan 2018 (wandererfan@gmail.com) for FreeCAD project
void CDxfWrite::makeBlockSectionHead(void)
{
    (*m_ssBlock) << "  0"          << endl;
    (*m_ssBlock) << "SECTION"      << endl;
    (*m_ssBlock) << "  2"          << endl;
    (*m_ssBlock) << "BLOCKS"       << endl;
    (*m_ssBlock) << "  0"          << endl;
    (*m_ssBlock) << "BLOCK"        << endl;
    (*m_ssBlock) << "  5"          << endl;
    m_currentBlock = getBlockHandle();
    (*m_ssBlock) << m_currentBlock << endl;
    if (m_version > 12) {
        (*m_ssBlock) << "330"      << endl;
        (*m_ssBlock) << m_saveModelSpaceHandle << endl;
        (*m_ssBlock) << "100"      << endl;
        (*m_ssBlock) << "AcDbEntity"      << endl;
    }
    (*m_ssBlock) << "  8"          << endl;
    (*m_ssBlock) << "0"            << endl;
    if (m_version > 12) {
        (*m_ssBlock) << "100"      << endl;
        (*m_ssBlock) << "AcDbBlockBegin"  << endl;
    }
    (*m_ssBlock) << "  2"          << endl;
    (*m_ssBlock) << "*MODEL_SPACE" << endl;
    (*m_ssBlock) << " 70"          << endl;
    (*m_ssBlock) << "   0"         << endl;
    (*m_ssBlock) << " 10"          << endl;
    (*m_ssBlock) << 0.0            << endl;
    (*m_ssBlock) << " 20"          << endl; 
    (*m_ssBlock) << 0.0            << endl;
    (*m_ssBlock) << " 30"          << endl;
    (*m_ssBlock) << 0.0            << endl;
    (*m_ssBlock) << "  3"          << endl;
    (*m_ssBlock) << "*MODEL_SPACE" << endl;
    (*m_ssBlock) << "  1"          << endl;
    (*m_ssBlock) << " "            << endl;
    (*m_ssBlock) << "  0"          << endl;
    (*m_ssBlock) << "ENDBLK"       << endl;
    (*m_ssBlock) << "  5"          << endl;
    (*m_ssBlock) << getBlockHandle()   << endl;
    if (m_version > 12) {
        (*m_ssBlock) << "330"      << endl;
        (*m_ssBlock) << m_saveModelSpaceHandle << endl;
        (*m_ssBlock) << "100"      << endl;
        (*m_ssBlock) << "AcDbEntity"  << endl;
    }
    (*m_ssBlock) << "  8"          << endl;
    (*m_ssBlock) << "0"            << endl;
    if (m_version > 12) {
        (*m_ssBlock) << "100"      << endl;
        (*m_ssBlock) << "AcDbBlockEnd"      << endl;
    }

    (*m_ssBlock) << "  0"          << endl;
    (*m_ssBlock) << "BLOCK"        << endl;
    (*m_ssBlock) << "  5"          << endl;
    m_currentBlock = getBlockHandle();
    (*m_ssBlock) << m_currentBlock << endl;
    if (m_version > 12) {
        (*m_ssBlock) << "330"      << endl;
        (*m_ssBlock) << m_savePaperSpaceHandle << endl;
        (*m_ssBlock) << "100"      << endl;
        (*m_ssBlock) << "AcDbEntity"      << endl;
        (*m_ssBlock) << " 67"          << endl;
        (*m_ssBlock) << "1"            << endl;
    }
    (*m_ssBlock) << "  8"          << endl;
    (*m_ssBlock) << "0"            << endl;
    if (m_version > 12) {
        (*m_ssBlock) << "100"      << endl;
        (*m_ssBlock) << "AcDbBlockBegin"  << endl;
    }
    (*m_ssBlock) << "  2"          << endl;
    (*m_ssBlock) << "*PAPER_SPACE" << endl;
    (*m_ssBlock) << " 70"          << endl;
    (*m_ssBlock) << "   0"         << endl;
    (*m_ssBlock) << " 10"          << endl;
    (*m_ssBlock) << 0.0            << endl;
    (*m_ssBlock) << " 20"          << endl; 
    (*m_ssBlock) << 0.0            << endl;
    (*m_ssBlock) << " 30"          << endl;
    (*m_ssBlock) << 0.0            << endl;
    (*m_ssBlock) << "  3"          << endl;
    (*m_ssBlock) << "*PAPER_SPACE" << endl;
    (*m_ssBlock) << "  1"          << endl;
    (*m_ssBlock) << " "            << endl;
    (*m_ssBlock) << "  0"          << endl;
    (*m_ssBlock) << "ENDBLK"       << endl;
    (*m_ssBlock) << "  5"          << endl;
    (*m_ssBlock) << getBlockHandle()   << endl;
    if (m_version > 12) {
        (*m_ssBlock) << "330"      << endl;
        (*m_ssBlock) << m_savePaperSpaceHandle << endl;
        (*m_ssBlock) << "100"      << endl;
        (*m_ssBlock) << "AcDbEntity"      << endl;
        (*m_ssBlock) << " 67"      << endl;      //paper_space flag
        (*m_ssBlock) << "    1"    << endl;
    }
    (*m_ssBlock) << "  8"          << endl;
    (*m_ssBlock) << "0"            << endl;
    if (m_version > 12) {
        (*m_ssBlock) << "100"      << endl;
        (*m_ssBlock) << "AcDbBlockEnd" << endl;
    }
}

std::string CDxfWrite::getPlateFile(std::string fileSpec)
{
    std::stringstream outString;
    Base::FileInfo fi(fileSpec);
    if (!fi.isReadable()) {
        Base::Console().Message("dxf unable to open %s!\n",fileSpec.c_str());
    } else {
        string line;
        ifstream inFile (fi.filePath().c_str());

        while (!inFile.eof())
        {
            getline(inFile,line);
            if (!inFile.eof()) {
                outString << line << '\n';
            }
        }
    }
    return outString.str();
}

std::string CDxfWrite::getEntityHandle(void)
{
    m_entityHandle++;
    std::stringstream ss;
    ss << std::uppercase << std::hex << std::setfill('0') << std::setw(2);
    ss << m_entityHandle;
    return ss.str();
}

std::string CDxfWrite::getLayerHandle(void)
{
    m_layerHandle++;
    std::stringstream ss;
    ss << std::uppercase << std::hex << std::setfill('0') << std::setw(2);
    ss << m_layerHandle;
    return ss.str();
}

std::string CDxfWrite::getBlockHandle(void)
{
    m_blockHandle++;
    std::stringstream ss;
    ss << std::uppercase << std::hex << std::setfill('0') << std::setw(2);
    ss << m_blockHandle;
    return ss.str();
}

std::string CDxfWrite::getBlkRecordHandle(void)
{
    m_blkRecordHandle++;
    std::stringstream ss;
    ss << std::uppercase << std::hex << std::setfill('0') << std::setw(2);
    ss << m_blkRecordHandle;
    return ss.str();
}

void CDxfWrite::addBlockName(std::string b, std::string h) 
{
    m_blockList.push_back(b);
    m_blkRecordList.push_back(h);
}

void CDxfWrite::setLayerName(std::string s)
{
   m_layerName = s;
   m_layerList.push_back(s);
}

void CDxfWrite::writeLine(const double* s, const double* e)
{
    putLine(toVector3d(s),toVector3d(e),m_ssEntity, getEntityHandle(), m_saveModelSpaceHandle);
}

void CDxfWrite::putLine(const Base::Vector3d s, const Base::Vector3d e,
                          std::ostringstream* outStream, const std::string handle,
                          const std::string ownerHandle)
{
    (*outStream) << "  0"       << endl;
    (*outStream) << "LINE"      << endl;
    (*outStream) << "  5"       << endl;
    (*outStream) << handle      << endl;
    if (m_version > 12) {
        (*outStream) << "330"      << endl;
        (*outStream) << ownerHandle  << endl;
        (*outStream) << "100"      << endl;
        (*outStream) << "AcDbEntity"      << endl;
    }
    (*outStream) << "  8"       << endl;    // Group code for layer name
    (*outStream) << getLayerName()  << endl;    // Layer number
    if (m_version > 12) {
        (*outStream) << "100"      << endl;
        (*outStream) << "AcDbLine" << endl;
    }
    (*outStream) << " 10"       << endl;    // Start point of line
    (*outStream) << s.x         << endl;    // X in WCS coordinates
    (*outStream) << " 20"       << endl;
    (*outStream) << s.y         << endl;    // Y in WCS coordinates
    (*outStream) << " 30"       << endl;
    (*outStream) << s.z         << endl;    // Z in WCS coordinates
    (*outStream) << " 11"       << endl;    // End point of line
    (*outStream) << e.x         << endl;    // X in WCS coordinates
    (*outStream) << " 21"       << endl;
    (*outStream) << e.y         << endl;    // Y in WCS coordinates
    (*outStream) << " 31"       << endl;
    (*outStream) << e.z         << endl;    // Z in WCS coordinates
}


//***************************
//writeLWPolyLine  (Note: LWPolyline might not be supported in R12
//added by Wandererfan 2018 (wandererfan@gmail.com) for FreeCAD project
void CDxfWrite::writeLWPolyLine(const LWPolyDataOut &pd)
{
    (*m_ssEntity) << "  0"               << endl;
    (*m_ssEntity) << "LWPOLYLINE"     << endl;
    (*m_ssEntity) << "  5"      << endl;
    (*m_ssEntity) << getEntityHandle() << endl;
    if (m_version > 12) {
        (*m_ssEntity) << "330"      << endl;
        (*m_ssEntity) << m_saveModelSpaceHandle  << endl;
        (*m_ssEntity) << "100"      << endl;
        (*m_ssEntity) << "AcDbEntity"      << endl;
    }
    if (m_version > 12) {
        (*m_ssEntity) << "100"            << endl;    //100 groups are not part of R12
        (*m_ssEntity) << "AcDbPolyline"   << endl;
    }
    (*m_ssEntity) << "  8"            << endl;    // Group code for layer name
    (*m_ssEntity) << getLayerName()   << endl;    // Layer name
    (*m_ssEntity) << " 90"            << endl;
    (*m_ssEntity) << pd.nVert         << endl;    // number of vertices
    (*m_ssEntity) << " 70"            << endl;
    (*m_ssEntity) << pd.Flag          << endl;
    (*m_ssEntity) << " 43"            << endl;
    (*m_ssEntity) << "0"              << endl;    //Constant width opt
//    (*m_ssEntity) << pd.Width         << endl;    //Constant width opt
//    (*m_ssEntity) << " 38"            << endl;
//    (*m_ssEntity) << pd.Elev          << endl;    // Elevation
//    (*m_ssEntity) << " 39"            << endl;
//    (*m_ssEntity) << pd.Thick         << endl;    // Thickness
    for (auto& p: pd.Verts) {
        (*m_ssEntity) << " 10"        << endl;    // Vertices
        (*m_ssEntity) << p.x          << endl;
        (*m_ssEntity) << " 20"        << endl;
        (*m_ssEntity) << p.y          << endl;
    } 
    for (auto& s: pd.StartWidth) {
        (*m_ssEntity) << " 40"        << endl;
        (*m_ssEntity) << s            << endl;    // Start Width
    }
    for (auto& e: pd.EndWidth) {
        (*m_ssEntity) << " 41"        << endl;
        (*m_ssEntity) << e            << endl;    // End Width
    }
    for (auto& b: pd.Bulge) {                // Bulge
        (*m_ssEntity) << " 42"        << endl;
        (*m_ssEntity) << b            << endl;
    }
//    (*m_ssEntity) << "210"            << endl;    //Extrusion dir
//    (*m_ssEntity) << pd.Extr.x        << endl;
//    (*m_ssEntity) << "220"            << endl;
//    (*m_ssEntity) << pd.Extr.y        << endl;
//    (*m_ssEntity) << "230"            << endl;
//    (*m_ssEntity) << pd.Extr.z        << endl;
}

//***************************
//writePolyline
//added by Wandererfan 2018 (wandererfan@gmail.com) for FreeCAD project
void CDxfWrite::writePolyline(const LWPolyDataOut &pd)
{
    (*m_ssEntity) << "  0"            << endl;
    (*m_ssEntity) << "POLYLINE"       << endl;
    (*m_ssEntity) << "  5"      << endl;
    (*m_ssEntity) << getEntityHandle() << endl;
    if (m_version > 12) {
        (*m_ssEntity) << "330"      << endl;
        (*m_ssEntity) << m_saveModelSpaceHandle  << endl;
        (*m_ssEntity) << "100"      << endl;
        (*m_ssEntity) << "AcDbEntity"      << endl;
    }
    (*m_ssEntity) << "  8"            << endl;
    (*m_ssEntity) << getLayerName()       << endl;    // Layer name
    if (m_version > 12) {
        (*m_ssEntity) << "100"            << endl;    //100 groups are not part of R12
        (*m_ssEntity) << "AcDbPolyline"   << endl;
    }
    (*m_ssEntity) << " 66"            << endl;
    (*m_ssEntity) << "     1"         << endl;    // vertices follow
    (*m_ssEntity) << " 10"            << endl;
    (*m_ssEntity) << "0.0"            << endl;
    (*m_ssEntity) << " 20"            << endl;
    (*m_ssEntity) << "0.0"            << endl;
    (*m_ssEntity) << " 30"            << endl;
    (*m_ssEntity) << "0.0"            << endl;
    (*m_ssEntity) << " 70"            << endl;
    (*m_ssEntity) << "0"              << endl;
    for (auto& p: pd.Verts) {
        (*m_ssEntity) << "  0"        << endl;
        (*m_ssEntity) << "VERTEX"     << endl;
        (*m_ssEntity) << "  5"      << endl;
        (*m_ssEntity) << getEntityHandle() << endl;
        (*m_ssEntity) << "  8"        << endl;
        (*m_ssEntity) << getLayerName()   << endl;
        (*m_ssEntity) << " 10"        << endl;
        (*m_ssEntity) << p.x          << endl;
        (*m_ssEntity) << " 20"        << endl;
        (*m_ssEntity) << p.y          << endl;
        (*m_ssEntity) << " 30"        << endl;
        (*m_ssEntity) << "0.0"        << endl;
    } 
    (*m_ssEntity) << "  0"            << endl;
    (*m_ssEntity) << "SEQEND"         << endl;
    (*m_ssEntity) << "  5"            << endl;
    (*m_ssEntity) << getEntityHandle()      << endl;
    (*m_ssEntity) << "  8"            << endl;
    (*m_ssEntity) << getLayerName()       << endl;
}

void CDxfWrite::writePoint(const double* s)
{
    (*m_ssEntity) << "  0"            << endl;
    (*m_ssEntity) << "POINT"          << endl;
    (*m_ssEntity) << "  5"      << endl;
    (*m_ssEntity) << getEntityHandle() << endl;
    if (m_version > 12) {
        (*m_ssEntity) << "330"      << endl;
        (*m_ssEntity) << m_saveModelSpaceHandle  << endl;
        (*m_ssEntity) << "100"      << endl;
        (*m_ssEntity) << "AcDbEntity"      << endl;
    }
    (*m_ssEntity) << "  8"            << endl;    // Group code for layer name
    (*m_ssEntity) << getLayerName()       << endl;    // Layer name
    if (m_version > 12) {
        (*m_ssEntity) << "100"       << endl;
        (*m_ssEntity) << "AcDbPoint" << endl;
    }
    (*m_ssEntity) << " 10"            << endl;
    (*m_ssEntity) << s[0]             << endl;    // X in WCS coordinates
    (*m_ssEntity) << " 20"            << endl;
    (*m_ssEntity) << s[1]             << endl;    // Y in WCS coordinates
    (*m_ssEntity) << " 30"            << endl;
    (*m_ssEntity) << s[2]             << endl;    // Z in WCS coordinates
}

void CDxfWrite::writeArc(const double* s, const double* e, const double* c, bool dir)
{
    double ax = s[0] - c[0];
    double ay = s[1] - c[1];
    double bx = e[0] - c[0];
    double by = e[1] - c[1];

    double start_angle = atan2(ay, ax) * 180/M_PI;
    double end_angle = atan2(by, bx) * 180/M_PI;
    double radius = sqrt(ax*ax + ay*ay);
    if(!dir){
        double temp = start_angle;
        start_angle = end_angle;
        end_angle = temp;
    }
    (*m_ssEntity) << "  0"       << endl;
    (*m_ssEntity) << "ARC"       << endl;
    (*m_ssEntity) << "  5"      << endl;
    (*m_ssEntity) << getEntityHandle() << endl;
    if (m_version > 12) {
        (*m_ssEntity) << "330"      << endl;
        (*m_ssEntity) << m_saveModelSpaceHandle  << endl;
        (*m_ssEntity) << "100"      << endl;
        (*m_ssEntity) << "AcDbEntity"      << endl;
    }
    (*m_ssEntity) << "  8"       << endl;    // Group code for layer name
    (*m_ssEntity) << getLayerName()  << endl;    // Layer number
//    (*m_ssEntity) << " 62"          << endl;
//    (*m_ssEntity) << "     0"       << endl;
     if (m_version > 12) {
        (*m_ssEntity) << "100"          << endl;
        (*m_ssEntity) << "AcDbCircle"   << endl;
    }
    (*m_ssEntity) << " 10"       << endl;    // Centre X
    (*m_ssEntity) << c[0]        << endl;    // X in WCS coordinates
    (*m_ssEntity) << " 20"       << endl;
    (*m_ssEntity) << c[1]        << endl;    // Y in WCS coordinates
    (*m_ssEntity) << " 30"       << endl;
    (*m_ssEntity) << c[2]        << endl;    // Z in WCS coordinates
    (*m_ssEntity) << " 40"       << endl;    //
    (*m_ssEntity) << radius      << endl;    // Radius

    if (m_version > 12) {
        (*m_ssEntity) << "100"      << endl;
        (*m_ssEntity) << "AcDbArc" << endl;
    }
    (*m_ssEntity) << " 50"       << endl;
    (*m_ssEntity) << start_angle << endl;    // Start angle
    (*m_ssEntity) << " 51"       << endl;
    (*m_ssEntity) << end_angle   << endl;    // End angle
}

void CDxfWrite::writeCircle(const double* c, double radius)
{
    (*m_ssEntity) << "  0"       << endl;
    (*m_ssEntity) << "CIRCLE"    << endl;
    (*m_ssEntity) << "  5"      << endl;
    (*m_ssEntity) << getEntityHandle() << endl;
    if (m_version > 12) {
        (*m_ssEntity) << "330"      << endl;
        (*m_ssEntity) << m_saveModelSpaceHandle  << endl;
        (*m_ssEntity) << "100"      << endl;
        (*m_ssEntity) << "AcDbEntity"      << endl;
    }
    (*m_ssEntity) << "  8"       << endl;    // Group code for layer name
    (*m_ssEntity) << getLayerName()  << endl;    // Layer number
     if (m_version > 12) {
        (*m_ssEntity) << "100"          << endl;
        (*m_ssEntity) << "AcDbCircle"   << endl;
    }
    (*m_ssEntity) << " 10"       << endl;    // Centre X
    (*m_ssEntity) << c[0]        << endl;    // X in WCS coordinates
    (*m_ssEntity) << " 20"       << endl;
    (*m_ssEntity) << c[1]        << endl;    // Y in WCS coordinates
//    (*m_ssEntity) << " 30"       << endl;
//    (*m_ssEntity) << c[2]        << endl;    // Z in WCS coordinates
    (*m_ssEntity) << " 40"       << endl;    //
    (*m_ssEntity) << radius      << endl;    // Radius
}

void CDxfWrite::writeEllipse(const double* c, double major_radius, double minor_radius, 
                             double rotation, double start_angle, double end_angle,
                             bool endIsCW)
{
    double m[3];
    m[2]=0;
    m[0] = major_radius * sin(rotation);
    m[1] = major_radius * cos(rotation);

    double ratio = minor_radius/major_radius;

    if(!endIsCW){                          //end is NOT CW from start
        double temp = start_angle;
        start_angle = end_angle;
        end_angle = temp;
    }
    (*m_ssEntity) << "  0"       << endl;
    (*m_ssEntity) << "ELLIPSE"   << endl;
    (*m_ssEntity) << "  5"      << endl;
    (*m_ssEntity) << getEntityHandle() << endl;
    if (m_version > 12) {
        (*m_ssEntity) << "330"      << endl;
        (*m_ssEntity) << m_saveModelSpaceHandle  << endl;
        (*m_ssEntity) << "100"      << endl;
        (*m_ssEntity) << "AcDbEntity"      << endl;
    }
    (*m_ssEntity) << "  8"       << endl;    // Group code for layer name
    (*m_ssEntity) << getLayerName()  << endl;    // Layer number
     if (m_version > 12) {
        (*m_ssEntity) << "100"          << endl;
        (*m_ssEntity) << "AcDbEllipse"   << endl;
    }
    (*m_ssEntity) << " 10"       << endl;    // Centre X
    (*m_ssEntity) << c[0]        << endl;    // X in WCS coordinates
    (*m_ssEntity) << " 20"       << endl;
    (*m_ssEntity) << c[1]        << endl;    // Y in WCS coordinates
    (*m_ssEntity) << " 30"       << endl;
    (*m_ssEntity) << c[2]        << endl;    // Z in WCS coordinates
    (*m_ssEntity) << " 11"       << endl;    //
    (*m_ssEntity) << m[0]        << endl;    // Major X
    (*m_ssEntity) << " 21"       << endl;
    (*m_ssEntity) << m[1]        << endl;    // Major Y
    (*m_ssEntity) << " 31"       << endl;
    (*m_ssEntity) << m[2]        << endl;    // Major Z
    (*m_ssEntity) << " 40"       << endl;    //
    (*m_ssEntity) << ratio       << endl;    // Ratio
//    (*m_ssEntity) << "210"       << endl;    //extrusion dir??
//    (*m_ssEntity) << "0"         << endl;
//    (*m_ssEntity) << "220"       << endl;
//    (*m_ssEntity) << "0"         << endl;
//    (*m_ssEntity) << "230"       << endl;
//    (*m_ssEntity) << "1"         << endl;
    (*m_ssEntity) << " 41"       << endl;
    (*m_ssEntity) << start_angle << endl;    // Start angle (radians [0..2pi])
    (*m_ssEntity) << " 42"       << endl;
    (*m_ssEntity) << end_angle   << endl;    // End angle
}

//***************************
//writeSpline
//added by Wandererfan 2018 (wandererfan@gmail.com) for FreeCAD project
void CDxfWrite::writeSpline(const SplineDataOut &sd)
{
    (*m_ssEntity) << "  0"          << endl;
    (*m_ssEntity) << "SPLINE"       << endl;
    (*m_ssEntity) << "  5"      << endl;
    (*m_ssEntity) << getEntityHandle() << endl;
    if (m_version > 12) {
        (*m_ssEntity) << "330"      << endl;
        (*m_ssEntity) << m_saveModelSpaceHandle  << endl;
        (*m_ssEntity) << "100"      << endl;
        (*m_ssEntity) << "AcDbEntity"      << endl;
    }
    (*m_ssEntity) << "  8"          << endl;    // Group code for layer name
    (*m_ssEntity) << getLayerName()     << endl;    // Layer name
    if (m_version > 12) {
        (*m_ssEntity) << "100"          << endl;
        (*m_ssEntity) << "AcDbSpline"   << endl;
    }
    (*m_ssEntity) << "210"          << endl;
    (*m_ssEntity) << "0"            << endl;
    (*m_ssEntity) << "220"          << endl;
    (*m_ssEntity) << "0"            << endl;
    (*m_ssEntity) << "230"          << endl;
    (*m_ssEntity) << "1"            << endl;

    (*m_ssEntity) << " 70"          << endl;
    (*m_ssEntity) << sd.flag        << endl;      //flags
    (*m_ssEntity) << " 71"          << endl; 
    (*m_ssEntity) << sd.degree      << endl;
    (*m_ssEntity) << " 72"          << endl;
    (*m_ssEntity) << sd.knots       << endl;
    (*m_ssEntity) << " 73"          << endl;
    (*m_ssEntity) << sd.control_points   << endl;
    (*m_ssEntity) << " 74"          << endl; 
    (*m_ssEntity) << 0              << endl;

//    (*m_ssEntity) << " 12"          << endl;
//    (*m_ssEntity) << sd.starttan.x  << endl;
//    (*m_ssEntity) << " 22"          << endl;
//    (*m_ssEntity) << sd.starttan.y  << endl;
//    (*m_ssEntity) << " 32"          << endl;
//    (*m_ssEntity) << sd.starttan.z  << endl;
//    (*m_ssEntity) << " 13"          << endl;
//    (*m_ssEntity) << sd.endtan.x    << endl;
//    (*m_ssEntity) << " 23"          << endl;
//    (*m_ssEntity) << sd.endtan.y    << endl;
//    (*m_ssEntity) << " 33"          << endl;
//    (*m_ssEntity) << sd.endtan.z    << endl;

    for (auto& k: sd.knot) {
        (*m_ssEntity) << " 40"      << endl;  
        (*m_ssEntity) << k          << endl;  
    }

    for (auto& w : sd.weight) {
        (*m_ssEntity) << " 41"      << endl;  
        (*m_ssEntity) << w          << endl;  
    }

    for (auto& c: sd.control) {
        (*m_ssEntity) << " 10"      << endl;
        (*m_ssEntity) << c.x        << endl;    // X in WCS coordinates
        (*m_ssEntity) << " 20"      << endl;
        (*m_ssEntity) << c.y        << endl;    // Y in WCS coordinates
        (*m_ssEntity) << " 30"      << endl;
        (*m_ssEntity) << c.z        << endl;    // Z in WCS coordinates
    }
    for (auto& f: sd.fit) {
        (*m_ssEntity) << " 11"      << endl;
        (*m_ssEntity) << f.x        << endl;    // X in WCS coordinates
        (*m_ssEntity) << " 21"      << endl;
        (*m_ssEntity) << f.y        << endl;    // Y in WCS coordinates
        (*m_ssEntity) << " 31"      << endl;
        (*m_ssEntity) << f.z        << endl;    // Z in WCS coordinates
    }
}

//***************************
//writeVertex
//added by Wandererfan 2018 (wandererfan@gmail.com) for FreeCAD project
void CDxfWrite::writeVertex(double x, double y, double z)
{
    (*m_ssEntity) << "  0"          << endl;
    (*m_ssEntity) << "VERTEX"       << endl;
    (*m_ssEntity) << "  5"      << endl;
    (*m_ssEntity) << getEntityHandle() << endl;
    if (m_version > 12) {
        (*m_ssEntity) << "330"      << endl;
        (*m_ssEntity) << m_saveModelSpaceHandle  << endl;
        (*m_ssEntity) << "100"      << endl;
        (*m_ssEntity) << "AcDbEntity"      << endl;
    }
    (*m_ssEntity) << "  8"          << endl;
    (*m_ssEntity) << getLayerName()     << endl;
    if (m_version > 12) {
        (*m_ssEntity) << "100"          << endl;
        (*m_ssEntity) << "AcDbVertex"   << endl;
    }
    (*m_ssEntity) << " 10"          << endl;
    (*m_ssEntity) << x              << endl;
    (*m_ssEntity) << " 20"          << endl; 
    (*m_ssEntity) << y              << endl;
    (*m_ssEntity) << " 30"          << endl;
    (*m_ssEntity) << z              << endl;
    (*m_ssEntity) << " 70"          << endl;
    (*m_ssEntity) << 0              << endl;
}

void CDxfWrite::writeText(const char* text, const double* location1, const double* location2,
                          const double height, const int horizJust)
{
    putText(text, toVector3d(location1), toVector3d(location2),
            height, horizJust, 
            m_ssEntity, getEntityHandle(), m_saveModelSpaceHandle);
}                                     

//***************************
//putText
//added by Wandererfan 2018 (wandererfan@gmail.com) for FreeCAD project
void CDxfWrite::putText(const char* text, const Base::Vector3d location1, const Base::Vector3d location2,
                          const double height, const int horizJust,
                          std::ostringstream* outStream, const std::string handle,
                          const std::string ownerHandle)
{
    (void) location2;

    (*outStream) << "  0"          << endl;
    (*outStream) << "TEXT"         << endl;
    (*outStream) << "  5"      << endl;
    (*outStream) << handle << endl;
    if (m_version > 12) {
        (*outStream) << "330"      << endl;
        (*outStream) << ownerHandle  << endl;
        (*outStream) << "100"      << endl;
        (*outStream) << "AcDbEntity"      << endl;
    }
    (*outStream) << "  8"          << endl;
    (*outStream) << getLayerName()     << endl;
    if (m_version > 12) {
        (*outStream) << "100"          << endl;
        (*outStream) << "AcDbText"     << endl;
    }
//    (*outStream) << " 39"          << endl;
//    (*outStream) << 0              << endl;     //thickness
    (*outStream) << " 10"          << endl;     //first alignment point
    (*outStream) << location1.x    << endl;
    (*outStream) << " 20"          << endl; 
    (*outStream) << location1.y    << endl;
    (*outStream) << " 30"          << endl;
    (*outStream) << location1.z    << endl;
    (*outStream) << " 40"          << endl;
    (*outStream) << height         << endl;
    (*outStream) << "  1"          << endl;
    (*outStream) << text           << endl;
//    (*outStream) << " 50"          << endl;
//    (*outStream) << 0              << endl;    //rotation
//    (*outStream) << " 41"          << endl;
//    (*outStream) << 1              << endl;
//    (*outStream) << " 51"          << endl;
//    (*outStream) << 0              << endl;

    (*outStream) << "  7"          << endl;
    (*outStream) << "STANDARD"     << endl;    //style
//    (*outStream) << " 71"          << endl;  //default
//    (*outStream) << "0"            << endl;
    (*outStream) << " 72"          << endl;
    (*outStream) << horizJust      << endl;
////    (*outStream) << " 73"          << endl;
////    (*outStream) << "0"            << endl;
    (*outStream) << " 11"          << endl;    //second alignment point
    (*outStream) << location2.x    << endl;
    (*outStream) << " 21"          << endl; 
    (*outStream) << location2.y    << endl;
    (*outStream) << " 31"          << endl;
    (*outStream) << location2.z    << endl;
//    (*outStream) << "210"          << endl;
//    (*outStream) << "0"            << endl;
//    (*outStream) << "220"          << endl;
//    (*outStream) << "0"            << endl;
//    (*outStream) << "230"          << endl;
//    (*outStream) << "1"            << endl;
    if (m_version > 12) {
        (*outStream) << "100"          << endl;
        (*outStream) << "AcDbText"     << endl;
    }
    
}

void CDxfWrite::putArrow(Base::Vector3d arrowPos, Base::Vector3d barb1Pos, Base::Vector3d barb2Pos,
                         std::ostringstream* outStream, const std::string handle,
                         const std::string ownerHandle)
{
    (*outStream) << "  0"          << endl;
    (*outStream) << "SOLID"        << endl;
    (*outStream) << "  5"          << endl;
    (*outStream) << handle         << endl;
    if (m_version > 12) {
        (*outStream) << "330"      << endl;
        (*outStream) << ownerHandle << endl;
        (*outStream) << "100"      << endl;
        (*outStream) << "AcDbEntity"      << endl;
    }
    (*outStream) << "  8"          << endl;
    (*outStream) << "0"            << endl;
    (*outStream) << " 62"          << endl;
    (*outStream) << "     0"       << endl;
    if (m_version > 12) {
        (*outStream) << "100"      << endl;
        (*outStream) << "AcDbTrace" << endl;
    }
    (*outStream) << " 10"          << endl;
    (*outStream) << barb1Pos.x     << endl;
    (*outStream) << " 20"          << endl;
    (*outStream) << barb1Pos.y     << endl;
    (*outStream) << " 30"          << endl;
    (*outStream) << barb1Pos.z     << endl;
    (*outStream) << " 11"          << endl;
    (*outStream) << barb2Pos.x     << endl;
    (*outStream) << " 21"          << endl;
    (*outStream) << barb2Pos.y     << endl;
    (*outStream) << " 31"          << endl;
    (*outStream) << barb2Pos.z     << endl;
    (*outStream) << " 12"          << endl;
    (*outStream) << arrowPos.x     << endl;
    (*outStream) << " 22"          << endl;
    (*outStream) << arrowPos.y     << endl;
    (*outStream) << " 32"          << endl;
    (*outStream) << arrowPos.z     << endl;
    (*outStream) << " 13"          << endl;
    (*outStream) << arrowPos.x     << endl;
    (*outStream) << " 23"          << endl;
    (*outStream) << arrowPos.y     << endl;
    (*outStream) << " 33"          << endl;
    (*outStream) << arrowPos.z     << endl;
}

//***************************
//writeLinearDim
//added by Wandererfan 2018 (wandererfan@gmail.com) for FreeCAD project
void CDxfWrite::writeLinearDim(const double* textMidPoint, const double* lineDefPoint,
                         const double* extLine1, const double* extLine2,
                         const char* dimText)
{
    (*m_ssEntity) << "  0"          << endl;
    (*m_ssEntity) << "DIMENSION"    << endl;
    (*m_ssEntity) << "  5"      << endl;
    (*m_ssEntity) << getEntityHandle() << endl;
    if (m_version > 12) {
        (*m_ssEntity) << "330"      << endl;
        (*m_ssEntity) << m_saveModelSpaceHandle  << endl;
        (*m_ssEntity) << "100"      << endl;
        (*m_ssEntity) << "AcDbEntity"      << endl;
    }
    (*m_ssEntity) << "  8"          << endl;
    (*m_ssEntity) << getLayerName()     << endl;
    if (m_version > 12) {
        (*m_ssEntity) << "100"          << endl;
        (*m_ssEntity) << "AcDbDimension"     << endl;
    }
    (*m_ssEntity) << "  2"          << endl;
    (*m_ssEntity) << "*" << getLayerName()     << endl;     // blockName
    (*m_ssEntity) << " 10"          << endl;     //dimension line definition point
    (*m_ssEntity) << lineDefPoint[0]    << endl;
    (*m_ssEntity) << " 20"          << endl; 
    (*m_ssEntity) << lineDefPoint[1]    << endl;
    (*m_ssEntity) << " 30"          << endl;
    (*m_ssEntity) << lineDefPoint[2]    << endl;
    (*m_ssEntity) << " 11"          << endl;     //text mid point
    (*m_ssEntity) << textMidPoint[0]    << endl;
    (*m_ssEntity) << " 21"          << endl; 
    (*m_ssEntity) << textMidPoint[1]    << endl;
    (*m_ssEntity) << " 31"          << endl;
    (*m_ssEntity) << textMidPoint[2]    << endl;
    (*m_ssEntity) << " 70"          << endl;
    (*m_ssEntity) << 1              << endl;    // dimType1 = Aligned
//    (*m_ssEntity) << " 71"          << endl;    // not R12
//    (*m_ssEntity) << 1              << endl;    // attachPoint ??1 = topleft
    (*m_ssEntity) << "  1"          << endl;
    (*m_ssEntity) << dimText        << endl;    
    (*m_ssEntity) << "  3"          << endl;
    (*m_ssEntity) << "STANDARD"     << endl;    //style
//linear dims
    if (m_version > 12) {
        (*m_ssEntity) << "100"          << endl;
        (*m_ssEntity) << "AcDbAlignedDimension"     << endl;
    }
    (*m_ssEntity) << " 13"          << endl;
    (*m_ssEntity) << extLine1[0]    << endl;
    (*m_ssEntity) << " 23"          << endl; 
    (*m_ssEntity) << extLine1[1]    << endl;
    (*m_ssEntity) << " 33"          << endl;
    (*m_ssEntity) << extLine1[2]    << endl;
    (*m_ssEntity) << " 14"          << endl;
    (*m_ssEntity) << extLine2[0]    << endl;
    (*m_ssEntity) << " 24"          << endl; 
    (*m_ssEntity) << extLine2[1]    << endl;
    (*m_ssEntity) << " 34"          << endl;
    (*m_ssEntity) << extLine2[2]    << endl;

    writeDimBlockPreamble();
    writeLinearDimBlock(textMidPoint,lineDefPoint,
                                        extLine1, extLine2,
                                        dimText);
    writeBlockTrailer();
}

//***************************
//writeAngularDim
//added by Wandererfan 2018 (wandererfan@gmail.com) for FreeCAD project
void CDxfWrite::writeAngularDim(const double* textMidPoint, const double* lineDefPoint,
                         const double* startExt1, const double* endExt1,
                         const double* startExt2, const double* endExt2,
                         const char* dimText)
{
    (*m_ssEntity) << "  0"          << endl;
    (*m_ssEntity) << "DIMENSION"    << endl;
    (*m_ssEntity) << "  5"      << endl;
    (*m_ssEntity) << getEntityHandle() << endl;
    if (m_version > 12) {
        (*m_ssEntity) << "330"      << endl;
        (*m_ssEntity) << m_saveModelSpaceHandle  << endl;
        (*m_ssEntity) << "100"      << endl;
        (*m_ssEntity) << "AcDbEntity"      << endl;
    }
    (*m_ssEntity) << "  8"          << endl;
    (*m_ssEntity) << getLayerName()     << endl;
    if (m_version > 12) {
        (*m_ssEntity) << "100"          << endl;
        (*m_ssEntity) << "AcDbDimension"     << endl;
    }
    (*m_ssEntity) << "  2"          << endl;
    (*m_ssEntity) << "*" << getLayerName()     << endl;     // blockName

    (*m_ssEntity) << " 10"          << endl;
    (*m_ssEntity) << endExt2[0]     << endl;
    (*m_ssEntity) << " 20"          << endl; 
    (*m_ssEntity) << endExt2[1]     << endl;
    (*m_ssEntity) << " 30"          << endl;
    (*m_ssEntity) << endExt2[2]     << endl;

    (*m_ssEntity) << " 11"          << endl;
    (*m_ssEntity) << textMidPoint[0]  << endl;
    (*m_ssEntity) << " 21"          << endl; 
    (*m_ssEntity) << textMidPoint[1]  << endl;
    (*m_ssEntity) << " 31"          << endl;
    (*m_ssEntity) << textMidPoint[2]  << endl;

    (*m_ssEntity) << " 70"          << endl;
    (*m_ssEntity) << 2             << endl;    // dimType 2 = Angular  5 = Angular 3 point
                                           // +32 for block?? (not R12)
//    (*m_ssEntity) << " 71"          << endl;    // not R12?  not required?
//    (*m_ssEntity) << 5              << endl;    // attachPoint 5 = middle
    (*m_ssEntity) << "  1"          << endl;
    (*m_ssEntity) << dimText        << endl;    
    (*m_ssEntity) << "  3"          << endl;
    (*m_ssEntity) << "STANDARD"     << endl;    //style
//angular dims
    if (m_version > 12) {
        (*m_ssEntity) << "100"          << endl;
        (*m_ssEntity) << "AcDb2LineAngularDimension"     << endl;
    }
    (*m_ssEntity) << " 13"           << endl;
    (*m_ssEntity) << startExt1[0]    << endl;
    (*m_ssEntity) << " 23"           << endl; 
    (*m_ssEntity) << startExt1[1]    << endl;
    (*m_ssEntity) << " 33"           << endl;
    (*m_ssEntity) << startExt1[2]    << endl;

    (*m_ssEntity) << " 14"           << endl;
    (*m_ssEntity) << endExt1[0]      << endl;
    (*m_ssEntity) << " 24"           << endl; 
    (*m_ssEntity) << endExt1[1]      << endl;
    (*m_ssEntity) << " 34"           << endl;
    (*m_ssEntity) << endExt1[2]      << endl;

    (*m_ssEntity) << " 15"           << endl;
    (*m_ssEntity) << startExt2[0]    << endl;
    (*m_ssEntity) << " 25"           << endl; 
    (*m_ssEntity) << startExt2[1]    << endl;
    (*m_ssEntity) << " 35"           << endl;
    (*m_ssEntity) << startExt2[2]    << endl;

    (*m_ssEntity) << " 16"           << endl;
    (*m_ssEntity) << lineDefPoint[0] << endl;
    (*m_ssEntity) << " 26"           << endl; 
    (*m_ssEntity) << lineDefPoint[1] << endl;
    (*m_ssEntity) << " 36"           << endl;
    (*m_ssEntity) << lineDefPoint[2] << endl;
    writeDimBlockPreamble();
    writeAngularDimBlock(textMidPoint, lineDefPoint,
                         startExt1, endExt1,
                         startExt2, endExt2,
                         dimText);
    writeBlockTrailer();
}

//***************************
//writeRadialDim
//added by Wandererfan 2018 (wandererfan@gmail.com) for FreeCAD project
void CDxfWrite::writeRadialDim(const double* centerPoint, const double* textMidPoint, 
                         const double* arcPoint,
                         const char* dimText)
{
    (*m_ssEntity) << "  0"          << endl;
    (*m_ssEntity) << "DIMENSION"    << endl;
    (*m_ssEntity) << "  5"      << endl;
    (*m_ssEntity) << getEntityHandle() << endl;
    if (m_version > 12) {
        (*m_ssEntity) << "330"      << endl;
        (*m_ssEntity) << m_saveModelSpaceHandle  << endl;
        (*m_ssEntity) << "100"      << endl;
        (*m_ssEntity) << "AcDbEntity"      << endl;
    }
    (*m_ssEntity) << "  8"          << endl;
    (*m_ssEntity) << getLayerName()     << endl;
    if (m_version > 12) {
        (*m_ssEntity) << "100"          << endl;
        (*m_ssEntity) << "AcDbDimension"     << endl;
    }
    (*m_ssEntity) << "  2"          << endl;
    (*m_ssEntity) << "*" << getLayerName()     << endl;     // blockName
    (*m_ssEntity) << " 10"          << endl;     // arc center point
    (*m_ssEntity) << centerPoint[0] << endl;
    (*m_ssEntity) << " 20"          << endl; 
    (*m_ssEntity) << centerPoint[1] << endl;
    (*m_ssEntity) << " 30"          << endl;
    (*m_ssEntity) << centerPoint[2] << endl;
    (*m_ssEntity) << " 11"          << endl;     //text mid point
    (*m_ssEntity) << textMidPoint[0]   << endl;
    (*m_ssEntity) << " 21"          << endl; 
    (*m_ssEntity) << textMidPoint[1]   << endl;
    (*m_ssEntity) << " 31"          << endl;
    (*m_ssEntity) << textMidPoint[2]   << endl;
    (*m_ssEntity) << " 70"          << endl;
    (*m_ssEntity) << 4              << endl;    // dimType 4 = Radius
//    (*m_ssEntity) << " 71"          << endl;    // not R12
//    (*m_ssEntity) << 1              << endl;    // attachPoint 5 = middle center
    (*m_ssEntity) << "  1"          << endl;
    (*m_ssEntity) << dimText        << endl;    
    (*m_ssEntity) << "  3"          << endl;
    (*m_ssEntity) << "STANDARD"     << endl;    //style
//radial dims
    if (m_version > 12) {
        (*m_ssEntity) << "100"          << endl;
        (*m_ssEntity) << "AcDbRadialDimension"     << endl;
    }
    (*m_ssEntity) << " 15"          << endl;
    (*m_ssEntity) << arcPoint[0]    << endl;
    (*m_ssEntity) << " 25"          << endl; 
    (*m_ssEntity) << arcPoint[1]    << endl;
    (*m_ssEntity) << " 35"          << endl;
    (*m_ssEntity) << arcPoint[2]    << endl;
    (*m_ssEntity) << " 40"          << endl;   // leader length????
    (*m_ssEntity) << 0              << endl;

    writeDimBlockPreamble();
    writeRadialDimBlock(centerPoint, textMidPoint, arcPoint, dimText);
    writeBlockTrailer();
}

//***************************
//writeDiametricDim
//added by Wandererfan 2018 (wandererfan@gmail.com) for FreeCAD project
void CDxfWrite::writeDiametricDim(const double* textMidPoint, 
                         const double* arcPoint1, const double* arcPoint2,
                         const char* dimText)
{
    (*m_ssEntity) << "  0"          << endl;
    (*m_ssEntity) << "DIMENSION"    << endl;
    (*m_ssEntity) << "  5"      << endl;
    (*m_ssEntity) << getEntityHandle() << endl;
    if (m_version > 12) {
        (*m_ssEntity) << "330"      << endl;
        (*m_ssEntity) << m_saveModelSpaceHandle  << endl;
        (*m_ssEntity) << "100"      << endl;
        (*m_ssEntity) << "AcDbEntity"      << endl;
    }
    (*m_ssEntity) << "  8"          << endl;
    (*m_ssEntity) << getLayerName()     << endl;
    if (m_version > 12) {
        (*m_ssEntity) << "100"          << endl;
        (*m_ssEntity) << "AcDbDimension"     << endl;
    }
    (*m_ssEntity) << "  2"          << endl;
    (*m_ssEntity) << "*" << getLayerName()     << endl;     // blockName
    (*m_ssEntity) << " 10"          << endl;
    (*m_ssEntity) << arcPoint1[0]   << endl;
    (*m_ssEntity) << " 20"          << endl; 
    (*m_ssEntity) << arcPoint1[1]   << endl;
    (*m_ssEntity) << " 30"          << endl;
    (*m_ssEntity) << arcPoint1[2]   << endl;
    (*m_ssEntity) << " 11"          << endl;     //text mid point
    (*m_ssEntity) << textMidPoint[0]   << endl;
    (*m_ssEntity) << " 21"          << endl; 
    (*m_ssEntity) << textMidPoint[1]   << endl;
    (*m_ssEntity) << " 31"          << endl;
    (*m_ssEntity) << textMidPoint[2]   << endl;
    (*m_ssEntity) << " 70"          << endl;
    (*m_ssEntity) << 3              << endl;    // dimType 3 = Diameter
//    (*m_ssEntity) << " 71"          << endl;    // not R12
//    (*m_ssEntity) << 5              << endl;    // attachPoint 5 = middle center
    (*m_ssEntity) << "  1"          << endl;
    (*m_ssEntity) << dimText        << endl;    
    (*m_ssEntity) << "  3"          << endl;
    (*m_ssEntity) << "STANDARD"     << endl;    //style
//diametric dims
    if (m_version > 12) {
        (*m_ssEntity) << "100"          << endl;
        (*m_ssEntity) << "AcDbDiametricDimension"     << endl;
    }
    (*m_ssEntity) << " 15"          << endl;
    (*m_ssEntity) << arcPoint2[0]   << endl;
    (*m_ssEntity) << " 25"          << endl; 
    (*m_ssEntity) << arcPoint2[1]   << endl;
    (*m_ssEntity) << " 35"          << endl;
    (*m_ssEntity) << arcPoint2[2]   << endl;
    (*m_ssEntity) << " 40"          << endl;   // leader length????
    (*m_ssEntity) << 0              << endl;

    writeDimBlockPreamble();
    writeDiametricDimBlock(textMidPoint, arcPoint1, arcPoint2, dimText);
    writeBlockTrailer();
}

//***************************
//writeDimBlockPreamble
//added by Wandererfan 2018 (wandererfan@gmail.com) for FreeCAD project
void CDxfWrite::writeDimBlockPreamble(void)
{
    if (m_version > 12) {
        std::string blockName("*");
        blockName += getLayerName();
        m_saveBlkRecordHandle = getBlkRecordHandle();
        addBlockName(blockName,m_saveBlkRecordHandle);
    }

    m_currentBlock = getBlockHandle();
    (*m_ssBlock) << "  0"          << endl;
    (*m_ssBlock) << "BLOCK"        << endl;
    (*m_ssBlock) << "  5"      << endl;
    (*m_ssBlock) << m_currentBlock << endl;
    if (m_version > 12) {
        (*m_ssBlock) << "330"      << endl;
        (*m_ssBlock) << m_saveBlkRecordHandle << endl;
        (*m_ssBlock) << "100"      << endl;
        (*m_ssBlock) << "AcDbEntity"      << endl;
    }
    (*m_ssBlock) << "  8"          << endl;
    (*m_ssBlock) << getLayerName() << endl;
    if (m_version > 12) {
        (*m_ssBlock) << "100"          << endl;
        (*m_ssBlock) << "AcDbBlockBegin"  << endl;
    }
    (*m_ssBlock) << "  2"          << endl;
    (*m_ssBlock) << "*" << getLayerName()     << endl;     // blockName
    (*m_ssBlock) << " 70"          << endl;
    (*m_ssBlock) << "   1"         << endl;
    (*m_ssBlock) << " 10"          << endl;
    (*m_ssBlock) << 0.0            << endl;
    (*m_ssBlock) << " 20"          << endl; 
    (*m_ssBlock) << 0.0            << endl;
    (*m_ssBlock) << " 30"          << endl;
    (*m_ssBlock) << 0.0            << endl;
    (*m_ssBlock) << "  3"          << endl;
    (*m_ssBlock) << "*" << getLayerName()     << endl;     // blockName
    (*m_ssBlock) << "  1"          << endl;
    (*m_ssBlock) << " "            << endl;
}

//***************************
//writeBlockTrailer
//added by Wandererfan 2018 (wandererfan@gmail.com) for FreeCAD project
void CDxfWrite::writeBlockTrailer(void)
{
    (*m_ssBlock) << "  0"    << endl;
    (*m_ssBlock) << "ENDBLK" << endl;
    (*m_ssBlock) << "  5"      << endl;
    (*m_ssBlock) << getBlockHandle() << endl;
    if (m_version > 12) {
        (*m_ssBlock) << "330"    << endl;
        (*m_ssBlock) << m_saveBlkRecordHandle    << endl;
        (*m_ssBlock) << "100"    << endl;
        (*m_ssBlock) << "AcDbEntity"    << endl;
    }
//    (*m_ssBlock) << " 67"    << endl;
//    (*m_ssBlock) << "1"    << endl;
    (*m_ssBlock) << "  8"    << endl;
    (*m_ssBlock) << getLayerName() << endl;
    if (m_version > 12) {
        (*m_ssBlock) << "100"    << endl;
        (*m_ssBlock) << "AcDbBlockEnd"    << endl;
    }
}

//***************************
//writeLinearDimBlock
//added by Wandererfan 2018 (wandererfan@gmail.com) for FreeCAD project
void CDxfWrite::writeLinearDimBlock(const double* textMidPoint, const double* dimLine,
                         const double* e1Start, const double* e2Start,
                         const char* dimText)
{
    Base::Vector3d e1S(e1Start[0],e1Start[1],e1Start[2]);
    Base::Vector3d e2S(e2Start[0],e2Start[1],e2Start[2]);
    Base::Vector3d dl(dimLine[0],dimLine[1],dimLine[2]);      //point on DimLine (somewhere!)
    Base::Vector3d perp = dl.DistanceToLineSegment(e2S,e1S);
    Base::Vector3d e1E = e1S - perp;
    Base::Vector3d e2E = e2S - perp;
    Base::Vector3d para = e1E - e2E;
    Base::Vector3d X(1.0,0.0,0.0);
    double angle = para.GetAngle(X);
    angle = angle * 180.0 / M_PI;
    double arrowLen = 5.0;             //magic number
    double arrowWidth = arrowLen/6.0/2.0;   //magic number calc!

    putLine(e2S, e2E,
            m_ssBlock, getBlockHandle(),
            m_saveBlkRecordHandle);

    putLine(e1S, e1E,
            m_ssBlock, getBlockHandle(),
            m_saveBlkRecordHandle);

    putLine(e1E, e2E,
            m_ssBlock, getBlockHandle(),
            m_saveBlkRecordHandle);

    putText(dimText,toVector3d(textMidPoint), toVector3d(dimLine),3.5,1,
            m_ssBlock,getBlockHandle(),m_saveBlkRecordHandle);

    perp.Normalize();
    para.Normalize();
    Base::Vector3d arrowStart = e1E;
    Base::Vector3d barb1 = arrowStart + perp*arrowWidth - para*arrowLen;
    Base::Vector3d barb2 = arrowStart - perp*arrowWidth - para*arrowLen;
    putArrow(arrowStart, barb1, barb2,
             m_ssBlock,getBlockHandle(),m_saveBlkRecordHandle);


    arrowStart = e2E;
    barb1 = arrowStart + perp*arrowWidth + para*arrowLen;
    barb2 = arrowStart - perp*arrowWidth + para*arrowLen;
    putArrow(arrowStart, barb1, barb2,
             m_ssBlock,getBlockHandle(),m_saveBlkRecordHandle);
}

//***************************
//writeAngularDimBlock
//added by Wandererfan 2018 (wandererfan@gmail.com) for FreeCAD project
void CDxfWrite::writeAngularDimBlock(const double* textMidPoint, const double* lineDefPoint,
                         const double* startExt1, const double* endExt1,
                         const double* startExt2, const double* endExt2,
                         const char* dimText)
{
    Base::Vector3d e1S(startExt1[0],startExt1[1],startExt1[2]);   //apex
    Base::Vector3d e2S(startExt2[0],startExt2[1],startExt2[2]);
    Base::Vector3d e1E(endExt1[0],endExt1[1],endExt1[2]);
    Base::Vector3d e2E(endExt2[0],endExt2[1],endExt2[2]);
    Base::Vector3d e1 = e1E - e1S;
    Base::Vector3d e2 = e2E - e2S;

    double startAngle = atan2(e2.y,e2.x);
    double endAngle = atan2(e1.y,e1.x);
    double span = fabs(endAngle - startAngle);
    double offset = span * 0.10;
    if (startAngle < 0) {
         startAngle += 2.0 * M_PI;
    }
    if (endAngle < 0) {
         endAngle += 2.0 * M_PI;
    }
    Base::Vector3d startOff(cos(startAngle + offset),sin(startAngle + offset),0.0);
    Base::Vector3d endOff(cos(endAngle - offset),sin(endAngle - offset),0.0);
    startAngle = startAngle * 180.0 / M_PI;
    endAngle = endAngle * 180.0 / M_PI;
    
    Base::Vector3d linePt(lineDefPoint[0],lineDefPoint[1],lineDefPoint[2]);
    double radius = (e2S - linePt).Length();

    (*m_ssBlock) << "  0"          << endl;
    (*m_ssBlock) << "ARC"          << endl;       //dimline arc
    (*m_ssBlock) << "  5"          << endl;
    (*m_ssBlock) << getBlockHandle() << endl;
    if (m_version > 12) {
        (*m_ssBlock) << "330"      << endl;
        (*m_ssBlock) << m_saveBlkRecordHandle << endl;
        (*m_ssBlock) << "100"      << endl;
        (*m_ssBlock) << "AcDbEntity"      << endl;
    }
    (*m_ssBlock) << "  8"          << endl;
    (*m_ssBlock) << "0"            << endl;
//    (*m_ssBlock) << " 62"          << endl;
//    (*m_ssBlock) << "     0"       << endl;
    if (m_version > 12) {
        (*m_ssBlock) << "100"      << endl;
        (*m_ssBlock) << "AcDbCircle" << endl;
    }
    (*m_ssBlock) << " 10"          << endl;
    (*m_ssBlock) << startExt2[0]   << endl;      //arc center
    (*m_ssBlock) << " 20"          << endl;
    (*m_ssBlock) << startExt2[1]   << endl;
    (*m_ssBlock) << " 30"          << endl;
    (*m_ssBlock) << startExt2[2]   << endl;
    (*m_ssBlock) << " 40"          << endl;
    (*m_ssBlock) << radius         << endl;      //radius
    if (m_version > 12) {
        (*m_ssBlock) << "100"      << endl;
        (*m_ssBlock) << "AcDbArc" << endl;
    }
    (*m_ssBlock) << " 50"          << endl;
    (*m_ssBlock) << startAngle     << endl;            //start angle
    (*m_ssBlock) << " 51"          << endl;
    (*m_ssBlock) << endAngle       << endl;            //end angle

    putText(dimText,toVector3d(textMidPoint), toVector3d(textMidPoint),3.5,1,
            m_ssBlock,getBlockHandle(),m_saveBlkRecordHandle);

    e1.Normalize();
    e2.Normalize();
    Base::Vector3d arrow1Start = e1S + e1 * radius;
    Base::Vector3d arrow2Start = e2S + e2 * radius;

    //wf: idk why the Tan pts have to be reversed.  something to do with CW angles in Dxf?
    Base::Vector3d endTan = e1S + (startOff * radius);
    Base::Vector3d startTan = e2S + (endOff * radius);
    Base::Vector3d tanP1 = (arrow1Start - startTan).Normalize();
    Base::Vector3d perp1(-tanP1.y,tanP1.x,tanP1.z); 
    Base::Vector3d tanP2 = (arrow2Start - endTan).Normalize();
    Base::Vector3d perp2(-tanP2.y,tanP2.x,tanP2.z); 
    double arrowLen = 5.0;                  //magic number
    double arrowWidth = arrowLen/6.0/2.0;   //magic number calc!

    Base::Vector3d barb1 = arrow1Start + perp1*arrowWidth - tanP1*arrowLen;
    Base::Vector3d barb2 = arrow1Start - perp1*arrowWidth - tanP1*arrowLen;

    putArrow(arrow1Start, barb1, barb2,
             m_ssBlock,getBlockHandle(),m_saveBlkRecordHandle);

    barb1 = arrow2Start + perp2*arrowWidth - tanP2*arrowLen;
    barb2 = arrow2Start - perp2*arrowWidth - tanP2*arrowLen;

    putArrow(arrow2Start, barb1, barb2,
             m_ssBlock,getBlockHandle(),m_saveBlkRecordHandle);
}

//***************************
//writeRadialDimBlock
//added by Wandererfan 2018 (wandererfan@gmail.com) for FreeCAD project
void CDxfWrite::writeRadialDimBlock(const double* centerPoint, const double* textMidPoint, 
                         const double* arcPoint, const char* dimText)
{
    putLine(toVector3d(centerPoint), toVector3d(arcPoint),
            m_ssBlock, getBlockHandle(),
            m_saveBlkRecordHandle);

    putText(dimText,toVector3d(textMidPoint), toVector3d(textMidPoint),3.5,1,
            m_ssBlock,getBlockHandle(),m_saveBlkRecordHandle);

    Base::Vector3d c(centerPoint[0],centerPoint[1],centerPoint[2]);
    Base::Vector3d a(arcPoint[0],arcPoint[1],arcPoint[2]);
    Base::Vector3d para = a - c;
    double arrowLen = 5.0;                  //magic number
    double arrowWidth = arrowLen/6.0/2.0;   //magic number calc!
    para.Normalize();
    Base::Vector3d perp(-para.y,para.x,para.z);
    Base::Vector3d arrowStart = a;
    Base::Vector3d barb1 = arrowStart + perp*arrowWidth - para*arrowLen;
    Base::Vector3d barb2 = arrowStart - perp*arrowWidth - para*arrowLen;

    putArrow(arrowStart, barb1, barb2,
             m_ssBlock,getBlockHandle(),m_saveBlkRecordHandle);
}

//***************************
//writeDiametricDimBlock
//added by Wandererfan 2018 (wandererfan@gmail.com) for FreeCAD project
void CDxfWrite::writeDiametricDimBlock(const double* textMidPoint, 
                         const double* arcPoint1, const double* arcPoint2,
                         const char* dimText)
{
    putLine(toVector3d(arcPoint1), toVector3d(arcPoint2),
            m_ssBlock, getBlockHandle(),
            m_saveBlkRecordHandle);

    putText(dimText,toVector3d(textMidPoint), toVector3d(textMidPoint),3.5,1,
            m_ssBlock,getBlockHandle(),m_saveBlkRecordHandle);

    Base::Vector3d a1(arcPoint1[0],arcPoint1[1],arcPoint1[2]);
    Base::Vector3d a2(arcPoint2[0],arcPoint2[1],arcPoint2[2]);
    Base::Vector3d para = a2 - a1;
    double arrowLen = 5.0;                  //magic number
    double arrowWidth = arrowLen/6.0/2.0;   //magic number calc!
    para.Normalize();
    Base::Vector3d perp(-para.y,para.x,para.z);
    Base::Vector3d arrowStart = a1;
    Base::Vector3d barb1 = arrowStart + perp*arrowWidth + para*arrowLen;
    Base::Vector3d barb2 = arrowStart - perp*arrowWidth + para*arrowLen;

    putArrow(arrowStart, barb1, barb2,
             m_ssBlock,getBlockHandle(),m_saveBlkRecordHandle);

    arrowStart = a2;
    barb1 = arrowStart + perp*arrowWidth - para*arrowLen;
    barb2 = arrowStart - perp*arrowWidth - para*arrowLen;
    putArrow(arrowStart, barb1, barb2,
             m_ssBlock,getBlockHandle(),m_saveBlkRecordHandle);

}

//***************************
//writeBlocksSection
//added by Wandererfan 2018 (wandererfan@gmail.com) for FreeCAD project
void CDxfWrite::writeBlocksSection(void)
{
    if (m_version < 14) {
        std::stringstream ss;
        ss << "blocks1" << m_version << ".rub";
        std::string fileSpec = m_dataDir + ss.str();
        (*m_ofs) << getPlateFile(fileSpec);
    }
    
    //write blocks content
    (*m_ofs) << (*m_ssBlock).str();

    (*m_ofs) << "  0"      << endl;
    (*m_ofs) << "ENDSEC"   << endl;
}

//***************************
//writeEntitiesSection
//added by Wandererfan 2018 (wandererfan@gmail.com) for FreeCAD project
void CDxfWrite::writeEntitiesSection(void)
{
    std::stringstream ss;
    ss << "entities" << m_version << ".rub";
    std::string fileSpec = m_dataDir + ss.str();
    (*m_ofs) << getPlateFile(fileSpec);
    
    //write entities content
    (*m_ofs) << (*m_ssEntity).str();
    

    (*m_ofs) << "  0"      << endl;
    (*m_ofs) << "ENDSEC"   << endl;
}

//***************************
//writeObjectsSection
//added by Wandererfan 2018 (wandererfan@gmail.com) for FreeCAD project
void CDxfWrite::writeObjectsSection(void)
{
    if (m_version < 14) {
        return;
    }
    std::stringstream ss;
    ss << "objects" << m_version << ".rub";
    std::string fileSpec = m_dataDir + ss.str();
    (*m_ofs) << getPlateFile(fileSpec);
}

CDxfRead::CDxfRead(const char* filepath)
{
    // start the file
    memset( m_str, '\0', sizeof(m_str) );
    memset( m_unused_line, '\0', sizeof(m_unused_line) );
    m_fail = false;
    m_aci = 0;
    m_eUnits = eMillimeters;
    m_measurement_inch = false;
    strcpy(m_layer_name, "0");  // Default layer name
    memset( m_section_name, '\0', sizeof(m_section_name) );
    memset( m_block_name, '\0', sizeof(m_block_name) );
    m_ignore_errors = true;

    m_ifs = new ifstream(filepath);
    if(!(*m_ifs)){
        m_fail = true;
        printf("DXF file didn't load\n");
        return;
    }
    m_ifs->imbue(std::locale("C"));

}

CDxfRead::~CDxfRead()
{
    delete m_ifs;
}

double CDxfRead::mm( double value ) const
{
    if(m_measurement_inch)
    {
        value *= 0.0393700787401575;
    }

    switch(m_eUnits)
    {
        case eUnspecified:  return(value * 1.0);    // We don't know any better.
        case eInches:       return(value * 25.4);
        case eFeet:     return(value * 25.4 * 12);
        case eMiles:        return(value *  1609344.0);
        case eMillimeters:  return(value * 1.0);
        case eCentimeters:  return(value * 10.0);
        case eMeters:       return(value * 1000.0);
        case eKilometers:   return(value * 1000000.0);
        case eMicroinches:  return(value * 25.4 / 1000.0);
        case eMils:     return(value * 25.4 / 1000.0);
        case eYards:        return(value * 3 * 12 * 25.4);
        case eAngstroms:    return(value * 0.0000001);
        case eNanometers:   return(value * 0.000001);
        case eMicrons:      return(value * 0.001);
        case eDecimeters:   return(value * 100.0);
        case eDekameters:   return(value * 10000.0);
        case eHectometers:  return(value * 100000.0);
        case eGigameters:   return(value * 1000000000000.0);
        case eAstronomicalUnits: return(value * 149597870690000.0);
        case eLightYears:   return(value * 9454254955500000000.0);
        case eParsecs:      return(value * 30856774879000000000.0);
        default:        return(value * 1.0);    // We don't know any better.
    } // End switch
} // End mm() method


bool CDxfRead::ReadLine()
{
    double s[3] = {0, 0, 0};
    double e[3] = {0, 0, 0};
    bool hidden = false;

    while(!((*m_ifs).eof()))
    {
        get_line();
        int n;

        if(sscanf(m_str, "%d", &n) != 1)
        {
            printf("CDxfRead::ReadLine() Failed to read integer from '%s'\n", m_str );
            return false;
        }

        std::istringstream ss;
        ss.imbue(std::locale("C"));
        switch(n){
            case 0:
                // next item found, so finish with line
                    DerefACI();
                    OnReadLine(s, e, hidden);
                    hidden = false;
                return true;

            case 8: // Layer name follows
                get_line();
                strcpy(m_layer_name, m_str);
                break;

            case 6: // line style name follows
                get_line();
                if(m_str[0] == 'h' || m_str[0] == 'H')hidden = true;
                break;

            case 10:
                // start x
                get_line();
                ss.str(m_str); ss >> s[0]; s[0] = mm(s[0]); if(ss.fail()) return false;
                break;
            case 20:
                // start y
                get_line();
                ss.str(m_str); ss >> s[1]; s[1] = mm(s[1]); if(ss.fail()) return false;
                break;
            case 30:
                // start z
                get_line();
                ss.str(m_str); ss >> s[2]; s[2] = mm(s[2]); if(ss.fail()) return false;
                break;
            case 11:
                // end x
                get_line();
                ss.str(m_str); ss >> e[0]; e[0] = mm(e[0]); if(ss.fail()) return false;
                break;
            case 21:
                // end y
                get_line();
                ss.str(m_str); ss >> e[1]; e[1] = mm(e[1]); if(ss.fail()) return false;
                break;
            case 31:
                // end z
                get_line();
                ss.str(m_str); ss >> e[2]; e[2] = mm(e[2]); if(ss.fail()) return false;
                break;
                case 62:
                // color index
                get_line();
                ss.str(m_str); ss >> m_aci; if(ss.fail()) return false;
                break;

            case 100:
            case 39:
            case 210:
            case 220:
            case 230:
                // skip the next line
                get_line();
                break;
            default:
                // skip the next line
                get_line();
                break;
        }
    }

    try {
        DerefACI();
        OnReadLine(s, e, false);
    }
    catch(...)
    {
        if (! IgnoreErrors()) throw;    // Re-throw the exception.
    }

    return false;
}

bool CDxfRead::ReadPoint()
{
    double s[3] = {0, 0, 0};

    while(!((*m_ifs).eof()))
    {
        get_line();
        int n;

        if(sscanf(m_str, "%d", &n) != 1)
        {
            printf("CDxfRead::ReadPoint() Failed to read integer from '%s'\n", m_str );
            return false;
        }

        std::istringstream ss;
        ss.imbue(std::locale("C"));
        switch(n){
            case 0:
                // next item found, so finish with line
                    DerefACI();
                OnReadPoint(s);
                return true;

            case 8: // Layer name follows
                get_line();
                strcpy(m_layer_name, m_str);
                break;

            case 10:
                // start x
                get_line();
                ss.str(m_str); ss >> s[0]; s[0] = mm(s[0]); if(ss.fail()) return false;
                break;
            case 20:
                // start y
                get_line();
                ss.str(m_str); ss >> s[1]; s[1] = mm(s[1]); if(ss.fail()) return false;
                break;
            case 30:
                // start z
                get_line();
                ss.str(m_str); ss >> s[2]; s[2] = mm(s[2]); if(ss.fail()) return false;
                break;

                case 62:
                // color index
                get_line();
                ss.str(m_str); ss >> m_aci; if(ss.fail()) return false;
                break;

            case 100:
            case 39:
            case 210:
            case 220:
            case 230:
                // skip the next line
                get_line();
                break;
            default:
                // skip the next line
                get_line();
                break;
        }

    }

    try {
        DerefACI();
        OnReadPoint(s);
    }
    catch(...)
    {
        if (! IgnoreErrors()) throw;    // Re-throw the exception.
    }

    return false;
}

bool CDxfRead::ReadArc()
{
    double start_angle = 0.0;// in degrees
    double end_angle = 0.0;
    double radius = 0.0;
    double c[3] = {0,0,0}; // centre
    double z_extrusion_dir = 1.0;
    bool hidden = false;
    
    while(!((*m_ifs).eof()))
    {
        get_line();
        int n;
        if(sscanf(m_str, "%d", &n) != 1)
        {
            printf("CDxfRead::ReadArc() Failed to read integer from '%s'\n", m_str);
            return false;
        }

        std::istringstream ss;
        ss.imbue(std::locale("C"));
        switch(n){
            case 0:
                // next item found, so finish with arc
                    DerefACI();
                    OnReadArc(start_angle, end_angle, radius, c,z_extrusion_dir, hidden);
                    hidden = false;
                    return true;

            case 8: // Layer name follows
                get_line();
                strcpy(m_layer_name, m_str);
                break;

            case 6: // line style name follows
                get_line();
                if(m_str[0] == 'h' || m_str[0] == 'H')hidden = true;
                break;

            case 10:
                // centre x
                get_line();
                ss.str(m_str); ss >> c[0]; c[0] = mm(c[0]); if(ss.fail()) return false;
                break;
            case 20:
                // centre y
                get_line();
                ss.str(m_str); ss >> c[1]; c[1] = mm(c[1]); if(ss.fail()) return false;
                break;
            case 30:
                // centre z
                get_line();
                ss.str(m_str); ss >> c[2]; c[2] = mm(c[2]); if(ss.fail()) return false;
                break;
            case 40:
                // radius
                get_line();
                ss.str(m_str); ss >> radius; radius = mm(radius); if(ss.fail()) return false;
                break;
            case 50:
                // start angle
                get_line();
                ss.str(m_str); ss >> start_angle; if(ss.fail()) return false;
                break;
            case 51:
                // end angle
                get_line();
                ss.str(m_str); ss >> end_angle; if(ss.fail()) return false;
                break;
                case 62:
                // color index
                get_line();
                ss.str(m_str); ss >> m_aci; if(ss.fail()) return false;
                break;



            case 100:
            case 39:
            case 210:
            case 220:
                // skip the next line
                get_line();
                break;
            case 230:
                //Z extrusion direction for arc 
                get_line();
                ss.str(m_str); ss >> z_extrusion_dir; if(ss.fail()) return false;                                
                break;

            default:
                // skip the next line
                get_line();
                break;
        }
    }
    DerefACI();
    OnReadArc(start_angle, end_angle, radius, c, z_extrusion_dir, false);
    return false;
}

bool CDxfRead::ReadSpline()
{
    struct SplineData sd;
    sd.norm[0] = 0;
    sd.norm[1] = 0;
    sd.norm[2] = 1;
    sd.degree = 0;
    sd.knots = 0;
    sd.flag = 0;
    sd.control_points = 0;
    sd.fit_points = 0;

    double temp_double;

    while(!((*m_ifs).eof()))
    {
        get_line();
        int n;
        if(sscanf(m_str, "%d", &n) != 1)
        {
            printf("CDxfRead::ReadSpline() Failed to read integer from '%s'\n", m_str);
            return false;
        }
        std::istringstream ss;
        ss.imbue(std::locale("C"));
        switch(n){
            case 0:
                // next item found, so finish with Spline
                    DerefACI();
                OnReadSpline(sd);
                return true;
            case 8: // Layer name follows
                get_line();
                strcpy(m_layer_name, m_str);
                break;
                case 62:
                // color index
                get_line();
                ss.str(m_str); ss >> m_aci; if(ss.fail()) return false;
                break;
            case 210:
                // normal x
                get_line();
                ss.str(m_str); ss >> sd.norm[0]; if(ss.fail()) return false;
                break;
            case 220:
                // normal y
                get_line();
                ss.str(m_str); ss >> sd.norm[1]; if(ss.fail()) return false;
                break;
            case 230:
                // normal z
                get_line();
                ss.str(m_str); ss >> sd.norm[2]; if(ss.fail()) return false;
                break;
            case 70:
                // flag
                get_line();
                ss.str(m_str); ss >> sd.flag; if(ss.fail()) return false;
                break;
            case 71:
                // degree
                get_line();
                ss.str(m_str); ss >> sd.degree; if(ss.fail()) return false;
                break;
            case 72:
                // knots
                get_line();
                ss.str(m_str); ss >> sd.knots; if(ss.fail()) return false;
                break;
            case 73:
                // control points
                get_line();
                ss.str(m_str); ss >> sd.control_points; if(ss.fail()) return false;
                break;
            case 74:
                // fit points
                get_line();
                ss.str(m_str); ss >> sd.fit_points; if(ss.fail()) return false;
                break;
            case 12:
                // starttan x
                get_line();
                ss.str(m_str); ss >> temp_double; temp_double = mm(temp_double); if(ss.fail()) return false;
                sd.starttanx.push_back(temp_double);
                break;
            case 22:
                // starttan y
                get_line();
                ss.str(m_str); ss >> temp_double; temp_double = mm(temp_double); if(ss.fail()) return false;
                sd.starttany.push_back(temp_double);
                break;
            case 32:
                // starttan z
                get_line();
                ss.str(m_str); ss >> temp_double; temp_double = mm(temp_double); if(ss.fail()) return false;
                sd.starttanz.push_back(temp_double);
                break;
            case 13:
                // endtan x
                get_line();
                ss.str(m_str); ss >> temp_double; temp_double = mm(temp_double); if(ss.fail()) return false;
                sd.endtanx.push_back(temp_double);
                break;
            case 23:
                // endtan y
                get_line();
                ss.str(m_str); ss >> temp_double; temp_double = mm(temp_double); if(ss.fail()) return false;
                sd.endtany.push_back(temp_double);
                break;
            case 33:
                // endtan z
                get_line();
                ss.str(m_str); ss >> temp_double; temp_double = mm(temp_double); if(ss.fail()) return false;
                sd.endtanz.push_back(temp_double);
                break;
            case 40:
                // knot
                get_line();
                ss.str(m_str); ss >> temp_double; temp_double = mm(temp_double); if(ss.fail()) return false;
                sd.knot.push_back(temp_double);
                break;
            case 41:
                // weight
                get_line();
                ss.str(m_str); ss >> temp_double; temp_double = mm(temp_double); if(ss.fail()) return false;
                sd.weight.push_back(temp_double);
                break;
            case 10:
                // control x
                get_line();
                ss.str(m_str); ss >> temp_double; temp_double = mm(temp_double); if(ss.fail()) return false;
                sd.controlx.push_back(temp_double);
                break;
            case 20:
                // control y
                get_line();
                ss.str(m_str); ss >> temp_double; temp_double = mm(temp_double); if(ss.fail()) return false;
                sd.controly.push_back(temp_double);
                break;
            case 30:
                // control z
                get_line();
                ss.str(m_str); ss >> temp_double; temp_double = mm(temp_double); if(ss.fail()) return false;
                sd.controlz.push_back(temp_double);
                break;
            case 11:
                // fit x
                get_line();
                ss.str(m_str); ss >> temp_double; temp_double = mm(temp_double); if(ss.fail()) return false;
                sd.fitx.push_back(temp_double);
                break;
            case 21:
                // fit y
                get_line();
                ss.str(m_str); ss >> temp_double; temp_double = mm(temp_double); if(ss.fail()) return false;
                sd.fity.push_back(temp_double);
                break;
            case 31:
                // fit z
                get_line();
                ss.str(m_str); ss >> temp_double; temp_double = mm(temp_double); if(ss.fail()) return false;
                sd.fitz.push_back(temp_double);
                break;
            case 42:
            case 43:
            case 44:
                // skip the next line
                get_line();
                break;
            default:
                // skip the next line
                get_line();
                break;
        }
    }
    DerefACI();
    OnReadSpline(sd);
    return false;
}


bool CDxfRead::ReadCircle()
{
    double radius = 0.0;
    double c[3] = {0,0,0}; // centre
    bool hidden = false;

    while(!((*m_ifs).eof()))
    {
        get_line();
        int n;
        if(sscanf(m_str, "%d", &n) != 1)
        {
            printf("CDxfRead::ReadCircle() Failed to read integer from '%s'\n", m_str);
            return false;
        }
        std::istringstream ss;
        ss.imbue(std::locale("C"));
        switch(n){
            case 0:
                // next item found, so finish with Circle
                DerefACI();
                OnReadCircle(c, radius, hidden);
                hidden = false;
                return true;

            case 6: // line style name follows
                get_line();
                if(m_str[0] == 'h' || m_str[0] == 'H')hidden = true;
                break;

            case 8: // Layer name follows
                get_line();
                strcpy(m_layer_name, m_str);
                break;

            case 10:
                // centre x
                get_line();
                ss.str(m_str); ss >> c[0]; c[0] = mm(c[0]); if(ss.fail()) return false;
                break;
            case 20:
                // centre y
                get_line();
                ss.str(m_str); ss >> c[1]; c[1] = mm(c[1]); if(ss.fail()) return false;
                break;
            case 30:
                // centre z
                get_line();
                ss.str(m_str); ss >> c[2]; c[2] = mm(c[2]); if(ss.fail()) return false;
                break;
            case 40:
                // radius
                get_line();
                ss.str(m_str); ss >> radius; radius = mm(radius); if(ss.fail()) return false;
                break;
                case 62:
                // color index
                get_line();
                ss.str(m_str); ss >> m_aci; if(ss.fail()) return false;
                break;

            case 100:
            case 39:
            case 210:
            case 220:
            case 230:
                // skip the next line
                get_line();
                break;
            default:
                // skip the next line
                get_line();
                break;
        }
    }
    DerefACI();
    OnReadCircle(c, radius, false);
    return false;
}


bool CDxfRead::ReadText()
{
    double c[3]; // coordinate
    double height = 0.03082;

    memset( c, 0, sizeof(c) );

    while(!((*m_ifs).eof()))
    {
        get_line();
        int n;
        if(sscanf(m_str, "%d", &n) != 1)
        {
            printf("CDxfRead::ReadText() Failed to read integer from '%s'\n", m_str);
            return false;
        }
        std::istringstream ss;
        ss.imbue(std::locale("C"));
        switch(n){
            case 0:
                return false;
            case 8: // Layer name follows
                get_line();
                strcpy(m_layer_name, m_str);
                break;

            case 10:
                // centre x
                get_line();
                ss.str(m_str); ss >> c[0]; c[0] = mm(c[0]); if(ss.fail()) return false;
                break;
            case 20:
                // centre y
                get_line();
                ss.str(m_str); ss >> c[1]; c[1] = mm(c[1]); if(ss.fail()) return false;
                break;
            case 30:
                // centre z
                get_line();
                ss.str(m_str); ss >> c[2]; c[2] = mm(c[2]); if(ss.fail()) return false;
                break;
            case 40:
                // text height
                get_line();
                ss.str(m_str); ss >> height; height = mm(height); if(ss.fail()) return false;
                break;
            case 1:
                // text
                get_line();
                DerefACI();
                OnReadText(c, height * 25.4 / 72.0, m_str);
                return(true);

            case 62:
                // color index
                get_line();
                ss.str(m_str); ss >> m_aci; if(ss.fail()) return false;
                break;

            case 100:
            case 39:
            case 210:
            case 220:
            case 230:
                // skip the next line
                get_line();
                break;
            default:
                // skip the next line
                get_line();
                break;
        }
    }

    return false;
}


bool CDxfRead::ReadEllipse()
{
    double c[3] = {0,0,0}; // centre
    double m[3] = {0,0,0}; //major axis point
    double ratio=0; //ratio of major to minor axis
    double start=0; //start of arc
    double end=0;  // end of arc

    while(!((*m_ifs).eof()))
    {
        get_line();
        int n;
        if(sscanf(m_str, "%d", &n) != 1)
        {
            printf("CDxfRead::ReadEllipse() Failed to read integer from '%s'\n", m_str);
            return false;
        }
        std::istringstream ss;
        ss.imbue(std::locale("C"));
        switch(n){
            case 0:
                // next item found, so finish with Ellipse
                    DerefACI();
                OnReadEllipse(c, m, ratio, start, end);
                return true;
            case 8: // Layer name follows
                get_line();
                strcpy(m_layer_name, m_str);
                break;

            case 10:
                // centre x
                get_line();
                ss.str(m_str); ss >> c[0]; c[0] = mm(c[0]); if(ss.fail()) return false;
                break;
            case 20:
                // centre y
                get_line();
                ss.str(m_str); ss >> c[1]; c[1] = mm(c[1]); if(ss.fail()) return false;
                break;
            case 30:
                // centre z
                get_line();
                ss.str(m_str); ss >> c[2]; c[2] = mm(c[2]); if(ss.fail()) return false;
                break;
            case 11:
                // major x
                get_line();
                ss.str(m_str); ss >> m[0]; m[0] = mm(m[0]); if(ss.fail()) return false;
                break;
            case 21:
                // major y
                get_line();
                ss.str(m_str); ss >> m[1]; m[1] = mm(m[1]); if(ss.fail()) return false;
                break;
            case 31:
                // major z
                get_line();
                ss.str(m_str); ss >> m[2]; m[2] = mm(m[2]); if(ss.fail()) return false;
                break;
            case 40:
                // ratio
                get_line();
                ss.str(m_str); ss >> ratio; if(ss.fail()) return false;
                break;
            case 41:
                // start
                get_line();
                ss.str(m_str); ss >> start; if(ss.fail()) return false;
                break;
            case 42:
                // end
                get_line();
                ss.str(m_str); ss >> end; if(ss.fail()) return false;
                break;
                case 62:
                // color index
                get_line();
                ss.str(m_str); ss >> m_aci; if(ss.fail()) return false;
                break;
            case 100:
            case 210:
            case 220:
            case 230:
                // skip the next line
                get_line();
                break;
            default:
                // skip the next line
                get_line();
                break;
        }
    }
    DerefACI();
    OnReadEllipse(c, m, ratio, start, end);
    return false;
}


static bool poly_prev_found = false;
static double poly_prev_x;
static double poly_prev_y;
static double poly_prev_z;
static bool poly_prev_bulge_found = false;
static double poly_prev_bulge;
static bool poly_first_found = false;
static double poly_first_x;
static double poly_first_y;
static double poly_first_z;

static void AddPolyLinePoint(CDxfRead* dxf_read, double x, double y, double z, bool bulge_found, double bulge)
{

    try {
        if(poly_prev_found)
        {
            bool arc_done = false;
            if(poly_prev_bulge_found)
            {
                double cot = 0.5 * ((1.0 / poly_prev_bulge) - poly_prev_bulge);
                double cx = ((poly_prev_x + x) - ((y - poly_prev_y) * cot)) / 2.0;
                double cy = ((poly_prev_y + y) + ((x - poly_prev_x) * cot)) / 2.0;
                double ps[3] = {poly_prev_x, poly_prev_y, poly_prev_z};
                double pe[3] = {x, y, z};
                double pc[3] = {cx, cy, (poly_prev_z + z)/2.0};
                dxf_read->OnReadArc(ps, pe, pc, poly_prev_bulge >= 0, false);
                arc_done = true;
            }

            if(!arc_done)
            {
                double s[3] = {poly_prev_x, poly_prev_y, poly_prev_z};
                double e[3] = {x, y, z};
                dxf_read->OnReadLine(s, e, false);
            }
        }

        poly_prev_found = true;
        poly_prev_x = x;
        poly_prev_y = y;
        poly_prev_z = z;
        if(!poly_first_found)
        {
            poly_first_x = x;
            poly_first_y = y;
            poly_first_z = z;
            poly_first_found = true;
        }
        poly_prev_bulge_found = bulge_found;
        poly_prev_bulge = bulge;
    }
    catch(...)
    {
        if (! dxf_read->IgnoreErrors()) throw;  // Re-throw it.
    }
}

static void PolyLineStart()
{
    poly_prev_found = false;
    poly_first_found = false;
}

bool CDxfRead::ReadLwPolyLine()
{
    PolyLineStart();

    bool x_found = false;
    bool y_found = false;
    double x = 0.0;
    double y = 0.0;
    double z = 0.0;
    bool bulge_found = false;
    double bulge = 0.0;
    bool closed = false;
    int flags;
    bool next_item_found = false;

    while(!((*m_ifs).eof()) && !next_item_found)
    {
        get_line();
        int n;
        if(sscanf(m_str, "%d", &n) != 1)
        {
            printf("CDxfRead::ReadLwPolyLine() Failed to read integer from '%s'\n", m_str);
            return false;
        }
        std::istringstream ss;
        ss.imbue(std::locale("C"));
        switch(n){
            case 0:
                // next item found

                    DerefACI();
                    if(x_found && y_found){
                    // add point
                    AddPolyLinePoint(this, x, y, z, bulge_found, bulge);
                    bulge_found = false;
                    x_found = false;
                    y_found = false;
                }
                next_item_found = true;
                break;
            case 8: // Layer name follows
                get_line();
                strcpy(m_layer_name, m_str);
                break;
            case 10:
                // x
                get_line();
                if(x_found && y_found){
                    // add point
                    AddPolyLinePoint(this, x, y, z, bulge_found, bulge);
                    bulge_found = false;
                    x_found = false;
                    y_found = false;
                }
                ss.str(m_str); ss >> x; x = mm(x); if(ss.fail()) return false;
                x_found = true;
                break;
            case 20:
                // y
                get_line();
                ss.str(m_str); ss >> y; y = mm(y); if(ss.fail()) return false;
                y_found = true;
                break;
            case 38: 
                // elevation
                get_line();
                ss.str(m_str); ss >> z; z = mm(z); if(ss.fail()) return false;
                break;
            case 42:
                // bulge
                get_line();
                ss.str(m_str); ss >> bulge; if(ss.fail()) return false;
                bulge_found = true;
                break;
            case 70:
                // flags
                get_line();
                if(sscanf(m_str, "%d", &flags) != 1)return false;
                closed = ((flags & 1) != 0);
                break;
                case 62:
                // color index
                get_line();
                ss.str(m_str); ss >> m_aci; if(ss.fail()) return false;
                break;
            default:
                // skip the next line
                get_line();
                break;
        }
    }

    if(next_item_found)
    {
        if(closed && poly_first_found)
        {
            // repeat the first point
                DerefACI();
            AddPolyLinePoint(this, poly_first_x, poly_first_y, poly_first_z, false, 0.0);
        }
        return true;
    }

    return false;
}


bool CDxfRead::ReadVertex(double *pVertex, bool *bulge_found, double *bulge)
{
    bool x_found = false;
    bool y_found = false;

    double x = 0.0;
    double y = 0.0;
    double z = 0.0;
    *bulge = 0.0;
    *bulge_found = false;

    pVertex[0] = 0.0;
    pVertex[1] = 0.0;
    pVertex[2] = 0.0;

    while(!(*m_ifs).eof()) {
        get_line();
        int n;
        if(sscanf(m_str, "%d", &n) != 1) {
            printf("CDxfRead::ReadVertex() Failed to read integer from '%s'\n", m_str);
            return false;
        }
        std::istringstream ss;
        ss.imbue(std::locale("C"));
        switch(n){
        case 0:
        DerefACI();
            put_line(m_str);    // read one line too many.  put it back.
            return(x_found && y_found);
            break;

        case 8: // Layer name follows
            get_line();
            strcpy(m_layer_name, m_str);
            break;

        case 10:
            // x
            get_line();
            ss.str(m_str); ss >> x; pVertex[0] = mm(x); if(ss.fail()) return false;
            x_found = true;
            break;
        case 20:
            // y
            get_line();
            ss.str(m_str); ss >> y; pVertex[1] = mm(y); if(ss.fail()) return false;
            y_found = true;
            break;
        case 30:
            // z
            get_line();
            ss.str(m_str); ss >> z; pVertex[2] = mm(z); if(ss.fail()) return false;
            break;

        case 42:
            get_line();
            *bulge_found = true;
            ss.str(m_str); ss >> *bulge; if(ss.fail()) return false;
            break;
    case 62:
        // color index
        get_line();
        ss.str(m_str); ss >> m_aci; if(ss.fail()) return false;
        break;

        default:
            // skip the next line
            get_line();
            break;
        }
    }

    return false;
}



bool CDxfRead::ReadPolyLine()
{
    PolyLineStart();

    bool closed = false;
    int flags;
    bool first_vertex_section_found = false;
    double first_vertex[3] = {0,0,0};
    bool bulge_found;
    double bulge;

    while(!(*m_ifs).eof())
    {
        get_line();
        int n;
        if(sscanf(m_str, "%d", &n) != 1)
        {
            printf("CDxfRead::ReadPolyLine() Failed to read integer from '%s'\n", m_str);
            return false;
        }
        std::istringstream ss;
        ss.imbue(std::locale("C"));
        switch(n){
            case 0:
                // next item found
                    DerefACI();
                get_line();
                if (! strcmp(m_str,"VERTEX"))
                {
                    double vertex[3] = {0,0,0};
                    if (CDxfRead::ReadVertex(vertex, &bulge_found, &bulge))
                    {
                        if(!first_vertex_section_found) {
                            first_vertex_section_found = true;
                            memcpy(first_vertex, vertex, 3*sizeof(double));
                        }
                        AddPolyLinePoint(this, vertex[0], vertex[1], vertex[2], bulge_found, bulge);
                        break;
                    }
                }
                if (! strcmp(m_str,"SEQEND"))
                {
                    if(closed && first_vertex_section_found) {
                        AddPolyLinePoint(this, first_vertex[0], first_vertex[1], first_vertex[2], 0, 0);
                    }
                    first_vertex_section_found = false;
                    PolyLineStart();
                    return(true);
                }
                break;
            case 70:
                // flags
                get_line();
                if(sscanf(m_str, "%d", &flags) != 1)return false;
                closed = ((flags & 1) != 0);
                break;
                case 62:
                // color index
                get_line();
                ss.str(m_str); ss >> m_aci; if(ss.fail()) return false;
                break;
            default:
                // skip the next line
                get_line();
                break;
        }
    }

    return false;
}

void CDxfRead::OnReadArc(double start_angle, double end_angle, double radius, const double* c, double z_extrusion_dir, bool hidden){
    double s[3] = {0,0,0}, e[3] = {0,0,0}, temp[3] = {0,0,0};
    if (z_extrusion_dir==1.0)
  {
    temp[0] =c[0];
    temp[1] =c[1];
    temp[2] =c[2];
    s[0] = c[0] + radius * cos(start_angle * M_PI/180);
    s[1] = c[1] + radius * sin(start_angle * M_PI/180);
    s[2] = c[2];
    e[0] = c[0] + radius * cos(end_angle * M_PI/180);
    e[1] = c[1] + radius * sin(end_angle * M_PI/180);
    e[2] = c[2];
   }
    else
    {
    temp[0] =-c[0];
    temp[1] =c[1];
    temp[2] =c[2];
    
    e[0] = -(c[0] + radius * cos(start_angle * M_PI/180));
    e[1] = (c[1] + radius * sin(start_angle * M_PI/180));
    e[2] = c[2];
    s[0] = -(c[0] + radius * cos(end_angle * M_PI/180));
    s[1] = (c[1] + radius * sin(end_angle * M_PI/180));
    s[2] = c[2];

    }
    OnReadArc(s, e, temp, true, hidden);
}

void CDxfRead::OnReadCircle(const double* c, double radius, bool hidden){
    double s[3];
    double start_angle = 0;
    s[0] = c[0] + radius * cos(start_angle * M_PI/180);
    s[1] = c[1] + radius * sin(start_angle * M_PI/180);
    s[2] = c[2];

    OnReadCircle(s, c, false, hidden); //false to change direction because otherwise the arc length is zero
}

void CDxfRead::OnReadEllipse(const double* c, const double* m, double ratio, double start_angle, double end_angle){
    double major_radius = sqrt(m[0]*m[0] + m[1]*m[1] + m[2]*m[2]);
    double minor_radius = major_radius * ratio;

    //Since we only support 2d stuff, we can calculate the rotation from the major axis x and y value only,
    //since z is zero, major_radius is the vector length

    double rotation = atan2(m[1]/major_radius,m[0]/major_radius);


    OnReadEllipse(c, major_radius, minor_radius, rotation, start_angle, end_angle, true);
}


bool CDxfRead::ReadInsert()
{
    double c[3] = {0,0,0}; // coordinate
    double s[3] = {1,1,1}; // scale
    double rot = 0.0; // rotation
    char name[1024] = {0};

    while(!((*m_ifs).eof()))
    {
        get_line();
        int n;
        if(sscanf(m_str, "%d", &n) != 1)
        {
            printf("CDxfRead::ReadInsert() Failed to read integer from '%s'\n", m_str);
            return false;
        }
        std::istringstream ss;
        ss.imbue(std::locale("C"));
        switch(n){
            case 0: 
                // next item found
                DerefACI();
                OnReadInsert(c, s, name, rot * M_PI/180);
                return(true);
            case 8: 
                // Layer name follows
                get_line();
                strcpy(m_layer_name, m_str);
                break;
            case 10:
                // coord x
                get_line();
                ss.str(m_str); ss >> c[0]; c[0] = mm(c[0]); if(ss.fail()) return false;
                break;
            case 20:
                // coord y
                get_line();
                ss.str(m_str); ss >> c[1]; c[1] = mm(c[1]); if(ss.fail()) return false;
                break;
            case 30:
                // coord z
                get_line();
                ss.str(m_str); ss >> c[2]; c[2] = mm(c[2]); if(ss.fail()) return false;
                break;
            case 41:
                // scale x
                get_line();
                ss.str(m_str); ss >> s[0]; if(ss.fail()) return false;
                break;
            case 42:
                // scale y
                get_line();
                ss.str(m_str); ss >> s[1]; if(ss.fail()) return false;
                break;
            case 43:
                // scale z
                get_line();
                ss.str(m_str); ss >> s[2]; if(ss.fail()) return false;
                break;
            case 50:
                // rotation
                get_line();
                ss.str(m_str); ss >> rot; if(ss.fail()) return false;
                break;
            case 2:
                // block name
                get_line();
                strcpy(name, m_str);
                break;
            case 62:
                // color index
                get_line();
                ss.str(m_str); ss >> m_aci; if(ss.fail()) return false;
                break;
            case 100:
            case 39:
            case 210:
            case 220:
            case 230:
                // skip the next line
                get_line();
                break;
            default:
                // skip the next line
                get_line();
                break;
        }
    }
    return false;
}


bool CDxfRead::ReadDimension()
{
    double s[3] = {0,0,0}; // startpoint
    double e[3] = {0,0,0}; // endpoint
    double p[3] = {0,0,0}; // dimpoint
    double rot = -1.0; // rotation

    while(!((*m_ifs).eof()))
    {
        get_line();
        int n;
        if(sscanf(m_str, "%d", &n) != 1)
        {
            printf("CDxfRead::ReadInsert() Failed to read integer from '%s'\n", m_str);
            return false;
        }
        std::istringstream ss;
        ss.imbue(std::locale("C"));
        switch(n){
            case 0: 
                // next item found
                DerefACI();
                OnReadDimension(s, e, p, rot * M_PI/180);
                return(true);
            case 8: 
                // Layer name follows
                get_line();
                strcpy(m_layer_name, m_str);
                break;
            case 13:
                // start x
                get_line();
                ss.str(m_str); ss >> s[0]; s[0] = mm(s[0]); if(ss.fail()) return false;
                break;
            case 23:
                // start y
                get_line();
                ss.str(m_str); ss >> s[1]; s[1] = mm(s[1]); if(ss.fail()) return false;
                break;
            case 33:
                // start z
                get_line();
                ss.str(m_str); ss >> s[2]; s[2] = mm(s[2]); if(ss.fail()) return false;
                break;
            case 14:
                // end x
                get_line();
                ss.str(m_str); ss >> e[0]; e[0] = mm(e[0]); if(ss.fail()) return false;
                break;
            case 24:
                // end y
                get_line();
                ss.str(m_str); ss >> e[1]; e[1] = mm(e[1]); if(ss.fail()) return false;
                break;
            case 34:
                // end z
                get_line();
                ss.str(m_str); ss >> e[2]; e[2] = mm(e[2]); if(ss.fail()) return false;
                break;
            case 10:
                // dimline x
                get_line();
                ss.str(m_str); ss >> p[0]; p[0] = mm(p[0]); if(ss.fail()) return false;
                break;
            case 20:
                // dimline y
                get_line();
                ss.str(m_str); ss >> p[1]; p[1] = mm(p[1]); if(ss.fail()) return false;
                break;
            case 30:
                // dimline z
                get_line();
                ss.str(m_str); ss >> p[2]; p[2] = mm(p[2]); if(ss.fail()) return false;
                break;
            case 50:
                // rotation
                get_line();
                ss.str(m_str); ss >> rot; if(ss.fail()) return false;
                break;
            case 62:
                // color index
                get_line();
                ss.str(m_str); ss >> m_aci; if(ss.fail()) return false;
                break;
            case 100:
            case 39:
            case 210:
            case 220:
            case 230:
                // skip the next line
                get_line();
                break;
            default:
                // skip the next line
                get_line();
                break;
        }
    }
    return false;
}


bool CDxfRead::ReadBlockInfo()
{
    while(!((*m_ifs).eof()))
    {
        get_line();
        int n;
        if(sscanf(m_str, "%d", &n) != 1)
        {
            printf("CDxfRead::ReadBlockInfo() Failed to read integer from '%s'\n", m_str);
            return false;
        }
        std::istringstream ss;
        ss.imbue(std::locale("C"));
        switch(n){
            case 2:
                // block name
                get_line();
                strcpy(m_block_name, m_str);
                return true;
            case 3:
                // block name too???
                get_line();
                strcpy(m_block_name, m_str);
                return true;
            default:
                // skip the next line
                get_line();
                break;
        }
    }
    return false;
}


void CDxfRead::get_line()
{
    if (m_unused_line[0] != '\0')
    {
        strcpy(m_str, m_unused_line);
        memset( m_unused_line, '\0', sizeof(m_unused_line));
        return;
    }

    m_ifs->getline(m_str, 1024);

    char str[1024];
    size_t len = strlen(m_str);
    int j = 0;
    bool non_white_found = false;
    for(size_t i = 0; i<len; i++){
        if(non_white_found || (m_str[i] != ' ' && m_str[i] != '\t')){
            if(m_str[i] != '\r')
            {
                str[j] = m_str[i]; j++;
            }
            non_white_found = true;
        }
    }
    str[j] = 0;
    strcpy(m_str, str);
}

void CDxfRead::put_line(const char *value)
{
    strcpy( m_unused_line, value );
}


bool CDxfRead::ReadUnits()
{
    get_line(); // Skip to next line.
    get_line(); // Skip to next line.
    int n = 0;
    if(sscanf(m_str, "%d", &n) == 1)
    {
        m_eUnits = eDxfUnits_t( n );
        return(true);
    } // End if - then
    else
    {
        printf("CDxfRead::ReadUnits() Failed to get integer from '%s'\n", m_str);
        return(false);
    }
}


bool CDxfRead::ReadLayer()
{
    std::string layername;
    int aci = -1;

    while(!((*m_ifs).eof()))
    {
        get_line();
        int n;

        if(sscanf(m_str, "%d", &n) != 1)
        {
            printf("CDxfRead::ReadLayer() Failed to read integer from '%s'\n", m_str );
            return false;
        }

        std::istringstream ss;
        ss.imbue(std::locale("C"));
        switch(n){
            case 0: // next item found, so finish with line
                    if (layername.empty())
                {
                    printf("CDxfRead::ReadLayer() - no layer name\n");
                    return false;
                }
                    m_layer_aci[layername] = aci;
                return true;

            case 2: // Layer name follows
                get_line();
                layername = m_str;
                break;

            case 62:
                // layer color ; if negative, layer is off
                get_line();
                if(sscanf(m_str, "%d", &aci) != 1)return false;
                break;

            case 6: // linetype name
            case 70: // layer flags
            case 100:
            case 290:
            case 370:
            case 390:
                // skip the next line
                get_line();
                break;
            default:
                // skip the next line
                get_line();
                break;
        }
    }
    return false;
}

void CDxfRead::DoRead(const bool ignore_errors /* = false */ )
{
    m_ignore_errors = ignore_errors;
    if(m_fail)return;

    get_line();

    while(!((*m_ifs).eof()))
    {
        if (!strcmp( m_str, "$INSUNITS" )){
            if (!ReadUnits())return;
            continue;
        } // End if - then

        if (!strcmp( m_str, "$MEASUREMENT" )){
            get_line();
            get_line();
            int n = 1;
            if(sscanf(m_str, "%d", &n) == 1)
            {
                if(n == 0)m_measurement_inch = true;
            }
            continue;
        } // End if - then
        
        else if(!strcmp(m_str, "0"))
        {
            get_line();
            if (!strcmp( m_str, "SECTION" )){
              strcpy(m_section_name, "");
              get_line();
              get_line();
              if (strcmp( m_str, "ENTITIES" ))
                strcpy(m_section_name, m_str);
              strcpy(m_block_name, "");

        } // End if - then
        else if (!strcmp( m_str, "TABLE" )){
              get_line();
              get_line();
        }

        else if (!strcmp( m_str, "LAYER" )){
              get_line();
              get_line();
              if(!ReadLayer())
                {
                  printf("CDxfRead::DoRead() Failed to read layer\n");
                  //return; Some objects or tables can have "LAYER" as name...
                }
              continue;     
        }
        
        else if (!strcmp( m_str, "BLOCK" )) {
            if(!ReadBlockInfo())
            {
                printf("CDxfRead::DoRead() Failed to read block info\n");
                return;
            }
            continue;
        } // End if - then

        else if (!strcmp( m_str, "ENDSEC" )){
                    strcpy(m_section_name, "");
                    strcpy(m_block_name, "");
                } // End if - then
        else if(!strcmp(m_str, "LINE")){
                if(!ReadLine())
                {
                    printf("CDxfRead::DoRead() Failed to read line\n");
                    return;
                }
                continue;
            }
            else if(!strcmp(m_str, "ARC")){
                if(!ReadArc())
                {
                    printf("CDxfRead::DoRead() Failed to read arc\n");
                    return;
                }
                continue;
            }
            else if(!strcmp(m_str, "CIRCLE")){
                if(!ReadCircle())
                {
                    printf("CDxfRead::DoRead() Failed to read circle\n");
                    return;
                }
                continue;
            }
            else if(!strcmp(m_str, "MTEXT")){
                if(!ReadText())
                {
                    printf("CDxfRead::DoRead() Failed to read text\n");
                    return;
                }
                continue;
            }
            else if(!strcmp(m_str, "TEXT")){
                if(!ReadText())
                {
                    printf("CDxfRead::DoRead() Failed to read text\n");
                    return;
                }
                continue;
            }
            else if(!strcmp(m_str, "ELLIPSE")){
                if(!ReadEllipse())
                {
                    printf("CDxfRead::DoRead() Failed to read ellipse\n");
                    return;
                }
                continue;
            }
            else if(!strcmp(m_str, "SPLINE")){
                if(!ReadSpline())
                {
                    printf("CDxfRead::DoRead() Failed to read spline\n");
                    return;
                }
                continue;
            }
            else if (!strcmp(m_str, "LWPOLYLINE")) {
                if(!ReadLwPolyLine())
                {
                    printf("CDxfRead::DoRead() Failed to read LW Polyline\n");
                    return;
                }
                continue;
            }
            else if (!strcmp(m_str, "POLYLINE")) {
                if(!ReadPolyLine())
                {
                    printf("CDxfRead::DoRead() Failed to read Polyline\n");
                    return;
                }
                continue;
            }
            else if (!strcmp(m_str, "POINT")) {
                if(!ReadPoint())
                {
                    printf("CDxfRead::DoRead() Failed to read Point\n");
                    return;
                }
                continue;
            }
            else if (!strcmp(m_str, "INSERT")) {
                if(!ReadInsert())
                {
                    printf("CDxfRead::DoRead() Failed to read Insert\n");
                    return;
                }
                continue;
            }
            else if (!strcmp(m_str, "DIMENSION")) {
                if(!ReadDimension())
                {
                    printf("CDxfRead::DoRead() Failed to read Dimension\n");
                    return;
                }
                continue;
            }
        }

        get_line();
    }
    AddGraphics();
}


void  CDxfRead::DerefACI()
{

    if (m_aci == 256) // if color = layer color, replace by color from layer
    {
         m_aci = m_layer_aci[std::string(m_layer_name)];
    }
}

std::string CDxfRead::LayerName() const
{
    std::string result;

    if (strlen(m_section_name) > 0)
    {
        result.append(m_section_name);
        result.append(" ");
    }

    if (strlen(m_block_name) > 0)
    {
        result.append(m_block_name);
        result.append(" ");
    }

    if (strlen(m_layer_name) > 0)
    {
        result.append(m_layer_name);
    }

    return(result);
}
