// 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 #include #include #include #include #include "dxf.h" #include #include #include #include #include #include #include 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 parameters in config file // handles: // boilerplate 0 - A00 // used by dxf.cpp A01 - FFFE // ACAD HANDSEED FFFF m_handle(0xA00) , // room for 2560 handles in boilerplate files // m_entityHandle(0x300), //don't need special ranges for handles // m_layerHandle(0x30), // m_blockHandle(0x210), // m_blkRecordHandle(0x110), m_polyOverride(false) , m_layerName("none") { // start the file m_fail = false; m_version = 12; Base::FileInfo fi(filepath); m_ofs = new Base::ofstream(fi, 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() { writeHeaderSection(); makeBlockRecordTableHead(); makeBlockSectionHead(); } //! assemble pieces into output file void CDxfWrite::endRun() { 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() { std::stringstream ss; ss << "FreeCAD v" << App::Application::Config()["BuildVersionMajor"] << "." << App::Application::Config()["BuildVersionMinor"] << " " << App::Application::Config()["BuildRevision"]; // 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() { 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() { // 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() { 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() { 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() { 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() { (*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::getHandle() { m_handle++; std::stringstream ss; ss << std::uppercase << std::hex << std::setfill('0') << std::setw(2); ss << m_handle; return ss.str(); } std::string CDxfWrite::getEntityHandle() { return getHandle(); // 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() { return getHandle(); // 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() { return getHandle(); // 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() { return getHandle(); // 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] = {major_radius * sin(rotation), major_radius * cos(rotation), 0}; 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& center : sd.control) { (*m_ssEntity) << " 10" << endl; (*m_ssEntity) << center.x << endl; // X in WCS coordinates (*m_ssEntity) << " 20" << endl; (*m_ssEntity) << center.y << endl; // Y in WCS coordinates (*m_ssEntity) << " 30" << endl; (*m_ssEntity) << center.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 #define ALIGNED 0 #define HORIZONTAL 1 #define VERTICAL 2 void CDxfWrite::writeLinearDim(const double* textMidPoint, const double* lineDefPoint, const double* extLine1, const double* extLine2, const char* dimText, int type) { (*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; if (type == ALIGNED) { (*m_ssEntity) << " 70" << endl; (*m_ssEntity) << 1 << endl; // dimType1 = Aligned } if ((type == HORIZONTAL) || (type == VERTICAL)) { (*m_ssEntity) << " 70" << endl; (*m_ssEntity) << 32 << endl; // dimType0 = Aligned + 32 (bit for unique block)? } // (*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; if (m_version > 12) { if (type == VERTICAL) { (*m_ssEntity) << " 50" << endl; (*m_ssEntity) << "90" << endl; } if ((type == HORIZONTAL) || (type == VERTICAL)) { (*m_ssEntity) << "100" << endl; (*m_ssEntity) << "AcDbRotatedDimension" << endl; } } writeDimBlockPreamble(); writeLinearDimBlock(textMidPoint, lineDefPoint, extLine1, extLine2, dimText, type); 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() { 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() { (*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, int type) { 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; if (type == ALIGNED) { // NOP } else if (type == HORIZONTAL) { double x = e1Start[0]; double y = dimLine[1]; e1E = Base::Vector3d(x, y, 0.0); x = e2Start[0]; e2E = Base::Vector3d(x, y, 0.0); perp = Base::Vector3d(0, -1, 0); // down para = Base::Vector3d(1, 0, 0); // right if (dimLine[1] > e1Start[1]) { perp = Base::Vector3d(0, 1, 0); // up } if (e1Start[0] > e2Start[0]) { para = Base::Vector3d(-1, 0, 0); // left } angle = 0; } else if (type == VERTICAL) { double x = dimLine[0]; double y = e1Start[1]; e1E = Base::Vector3d(x, y, 0.0); y = e2Start[1]; e2E = Base::Vector3d(x, y, 0.0); perp = Base::Vector3d(1, 0, 0); para = Base::Vector3d(0, 1, 0); if (dimLine[0] < e1Start[0]) { perp = Base::Vector3d(-1, 0, 0); } if (e1Start[1] > e2Start[1]) { para = Base::Vector3d(0, -1, 0); } angle = 90; } 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 center(centerPoint[0], centerPoint[1], centerPoint[2]); Base::Vector3d a(arcPoint[0], arcPoint[1], arcPoint[2]); Base::Vector3d para = a - center; 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() { 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() { 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() { 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 m_ifs = new ifstream(filepath); if (!(*m_ifs)) { m_fail = true; ImportError("DXF file didn't load\n"); return; } m_ifs->imbue(std::locale("C")); } CDxfRead::~CDxfRead() { delete m_ifs; delete m_CodePage; delete m_encoding; } double CDxfRead::mm(double value) const { // re #6461 // this if handles situation of malformed DXF file where // MEASUREMENT specifies English units, but // INSUNITS specifies millimeters or is not specified //(millimeters is our default) if (m_measurement_inch && (m_eUnits == eMillimeters)) { value *= 25.4; } 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 // // Setup for ProcessCommonEntityAttribute void CDxfRead::Setup3DVectorAttribute(int x_record_type, double destination[3]) { SetupScaledDoubleAttribute(x_record_type, destination[0]); SetupScaledDoubleAttribute(x_record_type + 10, destination[1]); SetupScaledDoubleAttribute(x_record_type + 20, destination[2]); destination[0] = destination[1] = destination[2] = 0.0; } void CDxfRead::Setup3DCoordinatesIntoLists(int x_record_type, list& x_destination, list& y_destination, list& z_destination) { SetupScaledDoubleIntoList(x_record_type, x_destination); SetupScaledDoubleIntoList(x_record_type + 10, y_destination); SetupScaledDoubleIntoList(x_record_type + 20, z_destination); } void CDxfRead::SetupScaledDoubleAttribute(int x_record_type, double& destination) { m_coordinate_attributes.emplace(x_record_type, std::pair(&ProcessScaledDouble, &destination)); } void CDxfRead::SetupScaledDoubleIntoList(int x_record_type, list& destination) { m_coordinate_attributes.emplace(x_record_type, std::pair(&ProcessScaledDoubleIntoList, &destination)); } void CDxfRead::SetupStringAttribute(int x_record_type, char* destination) { m_coordinate_attributes.emplace(x_record_type, std::pair(&ProcessString, destination)); } void CDxfRead::SetupStringAttribute(int x_record_type, std::string& destination) { m_coordinate_attributes.emplace(x_record_type, std::pair(&ProcessStdString, &destination)); } template void CDxfRead::SetupValueAttribute(int record_type, T& destination) { m_coordinate_attributes.emplace(record_type, std::pair(&ProcessValue, &destination)); } // // Static processing helpers for ProcessCommonEntityAttribute void CDxfRead::ProcessScaledDouble(CDxfRead* object, void* target) { std::istringstream ss; ss.imbue(std::locale("C")); ss.str(object->m_record_data); double value; ss >> value; if (ss.fail()) { object->ImportError("Unable to parse value '%s', using zero as its value\n", object->m_record_data); value = 0; } *static_cast(target) = object->mm(value); } void CDxfRead::ProcessScaledDoubleIntoList(CDxfRead* object, void* target) { std::istringstream ss; ss.imbue(std::locale("C")); ss.str(object->m_record_data); double value; ss >> value; if (ss.fail()) { object->ImportError("Unable to parse value '%s', using zero as its value\n", object->m_record_data); value = 0; } static_cast*>(target)->push_back(object->mm(value)); } template void CDxfRead::ProcessValue(CDxfRead* object, void* target) { std::istringstream ss; ss.imbue(std::locale("C")); ss.str(object->m_record_data); ss >> *static_cast(target); if (ss.fail()) { object->ImportError("Unable to parse value '%s', using zero as its value\n", object->m_record_data); *static_cast(target) = 0; } } void CDxfRead::ProcessStdString(CDxfRead* object, void* target) { *static_cast(target) = object->m_record_data; } void CDxfRead::ProcessString(CDxfRead* object, void* target) { strcpy(static_cast(target), object->m_record_data); } void CDxfRead::InitializeAttributes() { m_coordinate_attributes.clear(); } void CDxfRead::InitializeCommonEntityAttributes() { InitializeAttributes(); strcpy(m_layer_name, "0"); m_LineType[0] = '\0'; m_ColorIndex = ColorBylayer; SetupStringAttribute(6, m_LineType); SetupStringAttribute(8, m_layer_name); SetupValueAttribute(62, m_ColorIndex); } bool CDxfRead::ProcessAttribute() { auto found = m_coordinate_attributes.find(m_record_type); if (found != m_coordinate_attributes.end()) { (*found->second.first)(this, found->second.second); return true; } return false; } void CDxfRead::ProcessAllAttributes() { while (get_next_record() && m_record_type != 0) { ProcessAttribute(); } repeat_last_record(); } // // The individual Entity reader functions // These return false if they catch an exception and ignore it because of ignore_errors. bool CDxfRead::ReadLine() { double start[3], end[3]; Setup3DVectorAttribute(10, start); Setup3DVectorAttribute(11, end); ProcessAllAttributes(); OnReadLine(start, end, LineTypeIsHidden()); return true; } bool CDxfRead::ReadPoint() { double location[3]; Setup3DVectorAttribute(10, location); ProcessAllAttributes(); OnReadPoint(location); return true; } bool CDxfRead::ReadArc() { double start_angle_degrees = 0; double end_angle_degrees = 0; double radius = 0; double centre[3]; double z_extrusion_dir = 1.0; Setup3DVectorAttribute(10, centre); SetupScaledDoubleAttribute(40, radius); SetupValueAttribute(50, start_angle_degrees); SetupValueAttribute(51, end_angle_degrees); SetupValueAttribute(230, z_extrusion_dir); ProcessAllAttributes(); OnReadArc(start_angle_degrees, end_angle_degrees, radius, centre, z_extrusion_dir, LineTypeIsHidden()); return true; } bool CDxfRead::ReadSpline() { struct SplineData sd; sd.degree = 0; sd.knots = 0; sd.flag = 0; sd.control_points = 0; sd.fit_points = 0; Setup3DVectorAttribute(210, sd.norm); SetupValueAttribute(70, sd.flag); SetupValueAttribute(71, sd.degree); SetupValueAttribute(72, sd.knots); SetupValueAttribute(73, sd.control_points); SetupValueAttribute(74, sd.fit_points); SetupScaledDoubleIntoList(40, sd.knot); SetupScaledDoubleIntoList(41, sd.weight); Setup3DCoordinatesIntoLists(10, sd.controlx, sd.controly, sd.controlz); Setup3DCoordinatesIntoLists(11, sd.fitx, sd.fity, sd.fitz); Setup3DCoordinatesIntoLists(12, sd.starttanx, sd.starttany, sd.starttanz); Setup3DCoordinatesIntoLists(12, sd.endtanx, sd.endtany, sd.endtanz); ProcessAllAttributes(); OnReadSpline(sd); return true; } bool CDxfRead::ReadCircle() { double radius = 0.0; double centre[3]; Setup3DVectorAttribute(10, centre); SetupScaledDoubleAttribute(40, radius); ProcessAllAttributes(); OnReadCircle(centre, radius, LineTypeIsHidden()); return true; } bool CDxfRead::ReadText() { double insertionPoint[3]; double height = 0.03082; double rotation = 0; std::string textPrefix; Setup3DVectorAttribute(10, insertionPoint); SetupScaledDoubleAttribute(40, height); SetupValueAttribute(50, rotation); while (get_next_record() && m_record_type != 0) { if (!ProcessAttribute()) { switch (m_record_type) { case 3: // Additional text that goes before the type 1 text // Note that if breaking the text into type-3 records splits a UFT-8 encoding we // do the decoding after splicing the lines together. I'm not sure if this // actually occurs, but handling the text this way will treat this condition // properly. textPrefix.append(m_record_data); break; case 1: // final text textPrefix.append(m_record_data); break; } } } const char* utfStr = (this->*stringToUTF8)(textPrefix.c_str()); if (utfStr != nullptr) { OnReadText(insertionPoint, height * 25.4 / 72.0, utfStr, rotation); if (utfStr != textPrefix.c_str()) { delete utfStr; } } else { ImportError("Unable to process encoding for TEXT/MTEXT '%s'", textPrefix.c_str()); } repeat_last_record(); return true; } bool CDxfRead::ReadEllipse() { double centre[3]; double majorAxisEnd[3]; // relative to centre double eccentricity = 0; double startAngleRadians = 0; double endAngleRadians = 2 * M_PI; Setup3DVectorAttribute(10, centre); Setup3DVectorAttribute(11, majorAxisEnd); SetupValueAttribute(40, eccentricity); SetupValueAttribute(41, startAngleRadians); SetupValueAttribute(42, endAngleRadians); ProcessAllAttributes(); OnReadEllipse(centre, majorAxisEnd, eccentricity, startAngleRadians, endAngleRadians); return true; } bool CDxfRead::ReadLwPolyLine() { VertexInfo currentVertex = {{0, 0, 0}, 0}; list vertices; int flags = 0; bool have_x = false; bool have_y = false; // The documentation for LZPOLYLINE does not specify how you know if you have a new vertex. // It also does not specify where the bulge, line-width, etc for a particular segment are placed // relative to the two end vertices. // We assume here that if we see an X or Y coordinate but we already have the same coordinate, // a new vertex is starting, and any previous vertex is completely specified. Furthermore, line // attributes like bulge are placed between the X/Y coordinates for the vertex that starts the // stroke and the X/Y coordinates for the vertex that ends the stroke or the end of the entity. // In the latter case the stroke attributes apply to the closure stroke (if any) which ends at // the first vertex. Setup3DVectorAttribute(10, currentVertex.location); SetupScaledDoubleAttribute(42, currentVertex.bulge); SetupValueAttribute(70, flags); while (get_next_record() && m_record_type != 0) { if ((m_record_type == 10 && have_x) || (m_record_type == 21 && have_y)) { // Starting a new vertex and there is a previous vertex. Save it and init a new one. vertices.push_back(currentVertex); currentVertex.location[0] = 0.0; currentVertex.location[1] = 0.0; currentVertex.location[2] = 0.0; currentVertex.bulge = 0.0; have_x = m_record_type == 10; have_y = m_record_type == 20; } else if (m_record_type == 10) { have_x = true; } else if (m_record_type == 10) { have_y = true; } ProcessAttribute(); } // At the end of the entity if we have vertex information use this as the final vertex. // (else it was a line with no vertices at all) if (have_x || have_y) { vertices.push_back(currentVertex); } OnReadPolyline(vertices, flags); repeat_last_record(); return true; } bool CDxfRead::ReadPolyLine() { VertexInfo currentVertex = {{0, 0, 0}, 0}; list vertices; int flags = 0; SetupValueAttribute(70, flags); ProcessAllAttributes(); // We are now followed by a series of VERTEX entities followed by ENDSEQ. // To avoid eating and discarding the rest of the entieies if ENDSEQ is missing, // we quit on any unknown type-0 record. Setup3DVectorAttribute(10, currentVertex.location); SetupScaledDoubleAttribute(42, currentVertex.bulge); while (get_next_record() && m_record_type == 0 && !strcmp(m_record_data, "VERTEX")) { // Set vertex defaults currentVertex.location[0] = 0.0; currentVertex.location[1] = 0.0; currentVertex.location[2] = 0.0; currentVertex.bulge = 0.0; ProcessAllAttributes(); vertices.push_back(currentVertex); } if (strcmp(m_record_data, "SEQEND")) { ImportError("POLYLINE ends with '%s' record rather than 'SEQEND'\n", m_record_data); repeat_last_record(); } OnReadPolyline(vertices, flags); return true; } bool CDxfRead::ReadInsert() { double center[3]; double scale[3] = {1, 1, 1}; double rotationDegrees = 0.0; char blockName[1024] = {0}; Setup3DVectorAttribute(10, center); SetupValueAttribute(41, scale[0]); SetupValueAttribute(42, scale[1]); SetupValueAttribute(43, scale[2]); SetupValueAttribute(50, rotationDegrees); SetupStringAttribute(2, blockName); ProcessAllAttributes(); OnReadInsert(center, scale, blockName, rotationDegrees * M_PI / 180); return (true); } bool CDxfRead::ReadDimension() { double start[3]; double end[3]; double linePosition[3]; double textPosition[3]; double rotation = 0; int dimensionType = 0; // This appears to default to zero // Per documentation: // 10 is the "dimension line location" // 11 is the midpoint of the dimension text // 13 is the start point of the 1st extension line // 14 is the start point of the 2nd extension line // 50 is the rotation of the dimension (the direction of the dimension line) // 52 (if present) is the angle relative to the dimension line for the extension lines; default // 90 degrees Setup3DVectorAttribute(13, start); // WCS Setup3DVectorAttribute(14, end); // WCS Setup3DVectorAttribute(10, linePosition); // WCS Setup3DVectorAttribute(11, textPosition); // OCS SetupValueAttribute(50, rotation); SetupValueAttribute(70, dimensionType); ProcessAllAttributes(); dimensionType &= ~0xE0; // Remove flags switch (dimensionType) { case 0: case 1: OnReadDimension(start, end, linePosition, rotation * M_PI / 180); break; default: UnsupportedFeature("Dimension type '%d'", dimensionType); break; } return (true); } bool CDxfRead::ReadUnknownEntity() { UnsupportedFeature("Entity type '%s'", m_record_data); ProcessAllAttributes(); return true; } bool CDxfRead::ReadBlockInfo() { int blockType = 0; InitializeAttributes(); // Both 2 and 3 are the block name. SetupStringAttribute(2, m_block_name); SetupStringAttribute(3, m_block_name); SetupValueAttribute(70, blockType); ProcessAllAttributes(); // Read the entities in the block definition. // These are processed just like in-drawing entities but the code can // recognize that we are in the BLOCKS section, and which block we are defining, // by looking at the result of LayerName() // Note that the Layer Name in the block entities is ignored even though it // should be used to resolve BYLAYER attributes and also for placing the entity // when the block is inserted. if (blockType & 0x04) { // Note that this doesn't mean there are not entities in the block. I don't // know if the external reference can be cached because there are two other bits // here, 0x10 and 0x20, that seem to handle "resolved" external references. UnsupportedFeature("External (xref) BLOCK"); } while (get_next_record() && m_record_type != 0 && strcmp(m_record_data, "ENDBLK")) { if (blockType & 0x01) { // It is an anonymous block used to build dimensions, hatches, etc so we don't need it // and don't want to complaining about unhandled entity types. while (get_next_record() && m_record_type != 0) {} repeat_last_record(); } else if (IgnoreErrors()) { try { if (!ReadEntity()) { return false; } } catch (...) { } } else { if (!ReadEntity()) { return false; } } } return true; } void CDxfRead::UnsupportedFeature(const char* format, ...) { // Upcoming C++20 std::format might simplify some of this code va_list args; std::string formattedMessage; va_start(args, format); // Make room in formattedMessage for the formatted text and its terminating null byte formattedMessage.resize((size_t)vsnprintf(nullptr, 0, format, args) + 1); va_end(args); va_start(args, format); vsprintf(formattedMessage.data(), format, args); va_end(args); // Remove the null byte formattedMessage.pop_back(); // We place these formatted messages in a map, count their occurrences and not their first // occurrence. if (m_unsupportedFeaturesNoted[formattedMessage].first++ == 0) { m_unsupportedFeaturesNoted[formattedMessage].second = m_line; } } bool CDxfRead::get_next_record() { if (m_repeat_last_record) { m_repeat_last_record = false; return m_not_eof; } if ((*m_ifs).eof()) { m_not_eof = false; return false; } m_ifs->getline(m_record_data, 1024); ++m_line; if (sscanf(m_record_data, "%d", &m_record_type) != 1) { ImportError("CDxfRead::get_next_record() Failed to get integer record type from '%s'\n", m_record_data); return (false); } if ((*m_ifs).eof()) { return false; } m_ifs->getline(m_record_data, 1024); ++m_line; // Remove any carriage return at the end of m_str which may occur because of inconsistent // handling of LF vs. CRLF line termination. size_t len = strlen(m_record_data); if (len > 0 && m_record_data[len - 1] == '\r') { m_record_data[len - 1] = '\0'; } // The code that was here just blindly trimmed leading white space, but if you have, for // instance, a TEXT entity whose text starts with spaces, or, more plausibly, a long TEXT entity // where the text is broken into one or more type-3 records with a final type-1 and the break // happens to be just before a space, this would be wrong. return true; } void CDxfRead::repeat_last_record() { m_repeat_last_record = true; } bool CDxfRead::ReadUnits() { get_next_record(); // Get the value for the variable int n = 0; if (sscanf(m_record_data, "%d", &n) == 1) { m_eUnits = eDxfUnits_t(n); return (true); } // End if - then else { ImportError("CDxfRead::ReadUnits() Failed to get integer from '%s'\n", m_record_data); return (false); } } // // Intercepts for On... calls to derived class // (These have distinct signatures from the ones they call) bool CDxfRead::OnReadPolyline(std::list& vertices, int flags) { if (vertices.size() < 2) { // TODO: Warning return true; } bool closed = ((flags & 1) != 0); auto startVertex = vertices.end(); if (closed) { // If the shape is closed, point at the last vertex. The first stroke drawn will be the // closure. --startVertex; } for (auto endVertex = vertices.begin(); endVertex != vertices.end(); endVertex++) { if (startVertex != vertices.end()) { if (startVertex->bulge != 0.0) { // Bulge is 1/4 tan(arc angle), positive for CCW arc. double cot = 0.5 * ((1.0 / startVertex->bulge) - startVertex->bulge); double cx = ((startVertex->location[0] + endVertex->location[0]) - ((endVertex->location[1] - startVertex->location[1]) * cot)) / 2.0; double cy = ((startVertex->location[1] + endVertex->location[1]) + ((endVertex->location[0] - startVertex->location[0]) * cot)) / 2.0; double pc[3] = {cx, cy, (startVertex->location[2] + endVertex->location[2]) / 2.0}; OnReadArc(startVertex->location, endVertex->location, pc, startVertex->bulge >= 0, false); } else { OnReadLine(startVertex->location, endVertex->location, false); } } startVertex = endVertex; } return true; } void CDxfRead::OnReadArc(double start_angle, double end_angle, double radius, const double* center, 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] = center[0]; temp[1] = center[1]; temp[2] = center[2]; s[0] = center[0] + radius * cos(start_angle * M_PI / 180); s[1] = center[1] + radius * sin(start_angle * M_PI / 180); s[2] = center[2]; e[0] = center[0] + radius * cos(end_angle * M_PI / 180); e[1] = center[1] + radius * sin(end_angle * M_PI / 180); e[2] = center[2]; } else { temp[0] = -center[0]; temp[1] = center[1]; temp[2] = center[2]; e[0] = -(center[0] + radius * cos(start_angle * M_PI / 180)); e[1] = (center[1] + radius * sin(start_angle * M_PI / 180)); e[2] = center[2]; s[0] = -(center[0] + radius * cos(end_angle * M_PI / 180)); s[1] = (center[1] + radius * sin(end_angle * M_PI / 180)); s[2] = center[2]; } OnReadArc(s, e, temp, true, hidden); } void CDxfRead::OnReadCircle(const double* center, double radius, bool hidden) { // OnReadCircle wants a start point, so we pick an arbitrary point on the circunference double s[3] = {center[0] + radius, center[1], center[2]}; OnReadCircle(s, center, false, hidden); // false to change direction because otherwise the arc length is zero } void CDxfRead::OnReadEllipse(const double* center, 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(center, major_radius, minor_radius, rotation, start_angle, end_angle, true); } bool CDxfRead::ReadVersion() { static const std::vector VersionNames = { // This table is indexed by eDXFVersion_t - (ROlder+1) "AC1006", "AC1009", "AC1012", "AC1014", "AC1015", "AC1018", "AC1021", "AC1024", "AC1027", "AC1032"}; assert(VersionNames.size() == RNewer - ROlder - 1); get_next_record(); // Get the value for the variable std::vector::const_iterator first = VersionNames.cbegin(); std::vector::const_iterator last = VersionNames.cend(); std::vector::const_iterator found = std::lower_bound(first, last, m_record_data); if (found == last) { m_version = RNewer; } else if (*found == m_record_data) { m_version = (eDXFVersion_t)((int)std::distance(first, found) + (ROlder + 1)); } else if (found == first) { m_version = ROlder; } else { m_version = RUnknown; } return ResolveEncoding(); } bool CDxfRead::ReadDWGCodePage() { get_next_record(); // Get the value for the variable assert(m_CodePage == nullptr); // If not, we have found two DWGCODEPAGE variables or DoRead was // called twice on the same CDxfRead object. m_CodePage = new std::string(m_record_data); return ResolveEncoding(); } bool CDxfRead::ResolveEncoding() { if (m_encoding != nullptr) { delete m_encoding; m_encoding = nullptr; } if (m_version >= R2007) { // Note this does not include RUnknown, but does include RLater m_encoding = new std::string("utf_8"); stringToUTF8 = &CDxfRead::UTF8ToUTF8; } else if (m_CodePage == nullptr) { // cp1252 m_encoding = new std::string("cp1252"); stringToUTF8 = &CDxfRead::GeneralToUTF8; } else { // Codepage names may be of the form "ansi_1252" which we map to "cp1252" but we don't map // "ansi_x3xxxx" (which happens to mean "ascii") // Also some DXF files have the codepage name in uppercase so we lowercase it. std::string* p = new std::string(*m_CodePage); std::transform(p->begin(), p->end(), p->begin(), ::tolower); if (strncmp(p->c_str(), "ansi_", 5) == 0 && strncmp(p->c_str(), "ansi_x3", 7) != 0) { p->replace(0, 5, "cp"); } m_encoding = p; // At this point we want to recognize synonyms for "utf_8" and use the custom decoder // function. This is because this is one of the common cases and our decoder function is a // fast no-op. We don't actually use the decoder function we get from PyCodec_Decoder // because to call it we have to convert the (char *) text into a 'bytes' object first so we // can pass it to the function using PyObject_Callxxx(), getting the PYObject containing the // Python string, which we then decode back to UTF-8. It is simpler to call // PyUnicode_DecodeXxxx which takes a (const char *) and is just a direct c++ callable. Base::PyGILStateLocker lock; PyObject* pyDecoder = PyCodec_Decoder(m_encoding->c_str()); if (pyDecoder == nullptr) { return false; // A key error exception will have been placed. } PyObject* pyUTF8Decoder = PyCodec_Decoder("utf_8"); assert(pyUTF8Decoder != nullptr); if (pyDecoder == pyUTF8Decoder) { stringToUTF8 = &CDxfRead::UTF8ToUTF8; } else { stringToUTF8 = &CDxfRead::GeneralToUTF8; } Py_DECREF(pyDecoder); Py_DECREF(pyUTF8Decoder); } return m_encoding != nullptr; } const char* CDxfRead::UTF8ToUTF8(const char* encoded) const { return encoded; } const char* CDxfRead::GeneralToUTF8(const char* encoded) const { Base::PyGILStateLocker lock; PyObject* decoded = PyUnicode_Decode(encoded, strlen(encoded), m_encoding->c_str(), "strict"); if (decoded == nullptr) { return nullptr; } Py_ssize_t len; const char* converted = PyUnicode_AsUTF8AndSize(decoded, &len); char* result = nullptr; if (converted != nullptr) { // converted only has lifetime of decoded so we must save a copy. result = (char*)malloc(len + 1); if (result == nullptr) { PyErr_SetString(PyExc_MemoryError, "Out of memory"); } else { memcpy(result, converted, len + 1); } } Py_DECREF(decoded); return result; } void CDxfRead::DoRead(const bool ignore_errors /* = false */) { m_ignore_errors = ignore_errors; if (m_fail) { return; } // Loop reading and identifying the sections. while (get_next_record()) { if (m_record_type != 0) { ImportError("Found type %d record when expecting start of a SECTION or EOF\n", m_record_type); continue; } if (!strcmp(m_record_data, "EOF")) { // TODO: Check for drivel beyond EOF record break; } if (strcmp(m_record_data, "SECTION")) { ImportError("Found %s record when expecting start of a SECTION\n", m_record_data); continue; } if (!get_next_record()) { ImportError("Unclosed SECTION at end of file\n"); return; } if (m_record_type != 2) { ImportError("Ignored SECTION with no name record\n"); if (!ReadIgnoredSection()) { return; } } else if (!strcmp(m_record_data, "HEADER")) { if (!ReadHeaderSection()) { return; } } else if (!strcmp(m_record_data, "TABLES")) { if (!ReadTablesSection()) { return; } } else if (!strcmp(m_record_data, "BLOCKS")) { if (!ReadBlocksSection()) { return; } } else if (!strcmp(m_record_data, "ENTITIES")) { if (!ReadEntitiesSection()) { return; } } else { if (!ReadIgnoredSection()) { return; } } } AddGraphics(); // FLush out any unsupported features messages auto i = m_unsupportedFeaturesNoted.begin(); if (i != m_unsupportedFeaturesNoted.end()) { ImportError("Unsupported DXF features:\n"); do { ImportError("%s: %d time(s) first at line %d\n", i->first, i->second.first, i->second.second); } while (++i != m_unsupportedFeaturesNoted.end()); } } bool CDxfRead::ReadEntity() { InitializeCommonEntityAttributes(); // The entity record is already the current record and is already checked as a type 0 record if (!strcmp(m_record_data, "LINE")) { return ReadLine(); } else if (!strcmp(m_record_data, "ARC")) { return ReadArc(); } else if (!strcmp(m_record_data, "CIRCLE")) { return ReadCircle(); } else if (!strcmp(m_record_data, "MTEXT")) { return ReadText(); } else if (!strcmp(m_record_data, "TEXT")) { return ReadText(); } else if (!strcmp(m_record_data, "ELLIPSE")) { return ReadEllipse(); } else if (!strcmp(m_record_data, "SPLINE")) { return ReadSpline(); } else if (!strcmp(m_record_data, "LWPOLYLINE")) { return ReadLwPolyLine(); } else if (!strcmp(m_record_data, "POLYLINE")) { return ReadPolyLine(); } else if (!strcmp(m_record_data, "POINT")) { return ReadPoint(); } else if (!strcmp(m_record_data, "INSERT")) { return ReadInsert(); } else if (!strcmp(m_record_data, "DIMENSION")) { return ReadDimension(); } else { return ReadUnknownEntity(); } } bool CDxfRead::ReadHeaderSection() { // Read to the next ENDSEC record marking the end of the section. // This section contains variables, most of which we ignore. Each one is a type-9 record giving // the variable name, followed by a single record giving the value; the record type depends on // the variable's data type. while (get_next_record()) { if (m_record_type == 0 && !strcmp(m_record_data, "ENDSEC")) { return true; } else if (m_record_type != 9) { continue; // Quietly ignore unknown record types } if (!strcmp(m_record_data, "$INSUNITS")) { if (!ReadUnits()) { return false; } } else if (!strcmp(m_record_data, "$MEASUREMENT")) { get_next_record(); int n = 1; if (sscanf(m_record_data, "%d", &n) == 1) { if (n == 0) { m_measurement_inch = true; } } } else if (!strcmp(m_record_data, "$ACADVER")) { if (!ReadVersion()) { return false; } } else if (!strcmp(m_record_data, "$DWGCODEPAGE")) { if (!ReadDWGCodePage()) { return false; } } else { // any other variable, skip its value if (!get_next_record()) { return false; } } } return false; } bool CDxfRead::ReadTablesSection() { // Read to the next ENDSEC record marking the end of the section. // This section contains various tables, many of which we ignore. Each one is a type-0 TABLE // record followed by a type-2 (name) record giving the table name, followed by the table // contents. Each set of contents is terminates by the next type-0 TABLE or ENDSEC directive. while (get_next_record()) { if (m_record_type != 0) { continue; // Ignore any non-type-0 contents in the section. } if (!strcmp(m_record_data, "ENDSEC")) { return true; } if (strcmp(m_record_data, "TABLE")) { continue; // Ignore any type-0 non-TABLE contents in the section } get_next_record(); if (m_record_type != 2) { ImportError("Found unexpected type %d record instead of table name\n", m_record_type); } else if (!strcmp(m_record_data, "LAYER")) { if (!ReadLayerTable()) { return false; } } else { if (!ReadIgnoredTable()) { return false; } } } return false; } bool CDxfRead::ReadIgnoredSection() { // Read to the next ENDSEC record marking the end of the section. while (get_next_record()) { if (m_record_type == 0 && !strcmp(m_record_data, "ENDSEC")) { return true; } } return false; } bool CDxfRead::ReadBlocksSection() { // Read to the next ENDSEC record marking the end of the section. // Within this section we should find type-0 BLOCK groups while (get_next_record()) { if (m_record_type != 0) { continue; // quietly ignore non-type-0 records; } if (!strcmp(m_record_data, "ENDSEC")) { // End of section return true; } if (strcmp(m_record_data, "BLOCK")) { continue; // quietly ignore non-BLOCK records } if (!ReadBlockInfo()) { ImportError("CDxfRead::DoRead() Failed to read block\n"); } m_block_name[0] = '\0'; } return false; } bool CDxfRead::ReadEntitiesSection() { // Read to the next ENDSEC record marking the end of the section. // Within this section we should find type-0 BLOCK groups while (get_next_record()) { if (m_record_type != 0) { continue; // quietly ignore non-type-0 records; } if (!strcmp(m_record_data, "ENDSEC")) { // End of section return true; } if (IgnoreErrors()) { try { if (!ReadEntity()) { return false; } } catch (...) { } } else { if (!ReadEntity()) { return false; } } } return false; } bool CDxfRead::ReadLayer() { std::string layername; ColorIndex_t layerColor = 0; int layerFlags = 0; InitializeAttributes(); SetupStringAttribute(2, layername); SetupValueAttribute(62, layerColor); SetupValueAttribute(70, layerFlags); ProcessAllAttributes(); if (layername.empty()) { ImportError("CDxfRead::ReadLayer() - no layer name\n"); return false; } if (layerFlags & 0x01) { // Frozen layers are implicitly hidden which we don't do yet. // TODO: Should have an import option to omit frozen layers. UnsupportedFeature("Frozen layers"); } if (layerColor < 0) { UnsupportedFeature("Hidden layers"); layerColor = -layerColor; } m_layer_ColorIndex_map[layername] = layerColor; return true; } bool CDxfRead::ReadLayerTable() { // Read to the next TABLE record indicating another table in the TABLES section, or to the // ENDSEC record marking the end of the TABLES section. This table contains a series of type-0 // LAYER groups while (get_next_record()) { if (m_record_type != 0) { continue; // quietly ignore non-type-0 records; this table has some preamble } if (!strcmp(m_record_data, "TABLE") || !strcmp(m_record_data, "ENDSEC")) { // End of table repeat_last_record(); return true; } if (strcmp(m_record_data, "LAYER")) { continue; // quietly ignore non-LAYER records } if (!ReadLayer()) { ImportError("CDxfRead::DoRead() Failed to read layer\n"); } continue; } return false; } bool CDxfRead::ReadIgnoredTable() { // Read to the next TABLE record indicating another table in the TABLES section, or to the // ENDSEC record marking the end of the TABLES section. while (get_next_record()) { if (m_record_type == 0 && (!strcmp(m_record_data, "TABLE") || !strcmp(m_record_data, "ENDSEC"))) { repeat_last_record(); return true; } } return false; } std::string CDxfRead::LayerName() const { std::string result; if (strlen(m_block_name) > 0) { result.append("BLOCKS "); result.append(m_block_name); result.append(" "); } else if (strlen(m_layer_name) > 0) { result.append("ENTITIES "); result.append(m_layer_name); } return (result); } inline static double level(int distance, double blackLevel) { // Distance is the number of 24ths around the color wheel between the desired hue and // the primary hue in question. Coming in it is a ordinate difference and so can be negative // so the first thing we do is take its absolute value. if (distance < 0) { distance = -distance; } // If the distance is greater than 12, it is measuring the long way around the color wheel so we // reduce it to measuring along the short way instead if (distance > 12) { distance = 24 - distance; } if (distance <= 4) { // A distance 4 or less givs full intensity of the primary color return 1.0; } else if (distance < 8) { // Between 4 and 8 gives a blend of the full primary and the black level return ((8 - distance) + blackLevel * (distance - 4)) / 4; } else { // 8 and beyond yield the black level return blackLevel; } } inline static App::Color wheel(int hue, double blackLevel, double multiplier = 1.0) { return App::Color(level(hue - 0, blackLevel) * multiplier, level(hue - 8, blackLevel) * multiplier, level(hue - 16, blackLevel) * multiplier); } App::Color CDxfRead::ObjectColor() const { int index = m_ColorIndex; if (index == ColorBylayer) // if color = layer color, replace by color from layer { auto key = std::string(m_layer_name); index = m_layer_ColorIndex_map.count(key) > 0 ? m_layer_ColorIndex_map.at(key) : 0; } // TODO: If it is ColorByBlock we need to use the color of the INSERT entity. // This is tricky because a block can itself contain INSERT entities and we don't currently // record the required information. IIRC INSERT in a block will do something strange like // try to insert the block into the main drawing instead of into the block being defined. // The first 7 colors (1-7) have ad hoc names red, yellow, green, cyan, blue, magenta, and // black. 8, 9, 250-254 are lightening shades of gray. These are rendered by the app in a manner // to contrast with the background color. // For others, (color/10) determines the hue around the // color circle, with even numbers fading to black on the tens digit, // and odd numberd being blended with AA and again fading to black. // The fade is FF BD 81 68 4F (100%, 74%, 50%, 40%, 30%) indexed by (index/2)%5 // The AA fades as AA 7E 56 45 35 which is almost the exact same percentages. // For hue, (index-10)/10 : 0 is ff0000, and each step linearly adds green until 4 is pure // yellow ffff00, then red starts to fade... until but not including 24 which is back to ff0000. App::Color result = App::Color(); if (index == 0) { // Technically, 0 is BYBLOCK and not a real color, but all that means is that an object in a // block cannot specifically ask to be black. These colors are all contrasted to the // background so there is no objective black colour, through 255 is an objective white. result = App::Color(); } else if (index < 7) { result = wheel((index - 1) * 4, 0x00); } else if (index == 7) { result = App::Color(1, 1, 1); } else if (index == 8) { result = App::Color(0.5, 0.5, 0.5); } else if (index == 9) { result = App::Color(0.75, 0.75, 0.75); } else if (index >= 250) { int brightness = (index - 250 + (255 - index) * 0.2) / 5; result = App::Color(brightness, brightness, brightness); } else { int fade = (index / 2) % 5; static double fades[5] = {1.00, 0.74, 0.50, 0.40, 0.30}; return wheel(index / 10 - 1, (index & 1) ? 0.69 : 0, fades[fade]); } // TODO: These colors are modified to contrast with the background. In the original program // this is just a rendering feature, but FreeCAD does not support this so the user has the // option of modifying the colors to contrast with the background at time of import. return result; }