Download src/Mod/Import/App/dxf/dxf.cpp from AbdulElahGwaith/FreeCAD: direct link, hf CLI and curl.
- Browser
- Download file 112 kB
-
https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/Import/App/dxf/dxf.cpp
- Command line
-
hf download hf://AbdulElahGwaith/FreeCAD/src/Mod/Import/App/dxf/dxf.cpp
-
curl -L -o dxf.cpp https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/Import/App/dxf/dxf.cpp
112 kB
| // SPDX-License-Identifier: BSD-3-Clause | |
| // dxf.cpp | |
| // Copyright (c) 2009, Dan Heeks | |
| // This program is released under the BSD license. See the file COPYING for details. | |
| // modified 2018 wandererfan | |
| using namespace std; | |
| namespace | |
| { | |
| std::string DxfUnitToString(DxfUnits::eDxfUnits_t unit) | |
| { | |
| switch (unit) { | |
| case DxfUnits::eInches: | |
| return "Inches"; | |
| case DxfUnits::eFeet: | |
| return "Feet"; | |
| case DxfUnits::eMiles: | |
| return "Miles"; | |
| case DxfUnits::eMillimeters: | |
| return "Millimeters"; | |
| case DxfUnits::eCentimeters: | |
| return "Centimeters"; | |
| case DxfUnits::eMeters: | |
| return "Meters"; | |
| case DxfUnits::eKilometers: | |
| return "Kilometers"; | |
| case DxfUnits::eMicroinches: | |
| return "Microinches"; | |
| case DxfUnits::eMils: | |
| return "Mils"; | |
| case DxfUnits::eYards: | |
| return "Yards"; | |
| case DxfUnits::eAngstroms: | |
| return "Angstroms"; | |
| case DxfUnits::eNanometers: | |
| return "Nanometers"; | |
| case DxfUnits::eMicrons: | |
| return "Microns"; | |
| case DxfUnits::eDecimeters: | |
| return "Decimeters"; | |
| case DxfUnits::eDekameters: | |
| return "Dekameters"; | |
| case DxfUnits::eHectometers: | |
| return "Hectometers"; | |
| case DxfUnits::eGigameters: | |
| return "Gigameters"; | |
| case DxfUnits::eAstronomicalUnits: | |
| return "Astronomical Units"; | |
| case DxfUnits::eLightYears: | |
| return "Light Years"; | |
| case DxfUnits::eParsecs: | |
| return "Parsecs"; | |
| case DxfUnits::eUnspecified: | |
| default: | |
| return "Unspecified"; | |
| } | |
| } | |
| } // namespace | |
| static Base::Vector3d MakeVector3d(const double coordinates[3]) | |
| { | |
| // NOLINTNEXTLINE(readability/nolint) | |
| // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) | |
| return Base::Vector3d(coordinates[0], coordinates[1], coordinates[2]); | |
| } | |
| 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_fail(false) | |
| , m_ssBlock(new std::ostringstream()) | |
| , m_ssBlkRecord(new std::ostringstream()) | |
| , m_ssEntity(new std::ostringstream()) | |
| , m_ssLayer(new std::ostringstream()) | |
| , m_version(12) | |
| , 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 | |
| Base::FileInfo fi(filepath); | |
| m_ofs = new Base::ofstream(fi, ios::out); | |
| if (!(*m_ofs)) { | |
| m_fail = true; | |
| return; | |
| } | |
| m_ofs->imbue(std::locale("C")); | |
| // use lots of digits to avoid rounding errors | |
| m_ssEntity->setf(std::ios::fixed); | |
| m_ssEntity->precision(9); | |
| } | |
| 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()); | |
| 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(const std::string& name, const std::string& blkRecordHandle) | |
| { | |
| m_blockList.push_back(name); | |
| m_blkRecordList.push_back(blkRecordHandle); | |
| } | |
| void CDxfWrite::setLayerName(std::string name) | |
| { | |
| m_layerName = name; | |
| m_layerList.push_back(name); | |
| } | |
| void CDxfWrite::writeLine(const double* start, const double* end) | |
| { | |
| putLine(toVector3d(start), toVector3d(end), m_ssEntity, getEntityHandle(), m_saveModelSpaceHandle); | |
| } | |
| void CDxfWrite::putLine( | |
| const Base::Vector3d& start, | |
| const Base::Vector3d& end, | |
| 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) << start.x << endl; // X in WCS coordinates | |
| (*outStream) << " 20" << endl; | |
| (*outStream) << start.y << endl; // Y in WCS coordinates | |
| (*outStream) << " 30" << endl; | |
| (*outStream) << start.z << endl; // Z in WCS coordinates | |
| (*outStream) << " 11" << endl; // End point of line | |
| (*outStream) << end.x << endl; // X in WCS coordinates | |
| (*outStream) << " 21" << endl; | |
| (*outStream) << end.y << endl; // Y in WCS coordinates | |
| (*outStream) << " 31" << endl; | |
| (*outStream) << end.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) << p.z << 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* point) | |
| { | |
| (*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) << point[0] << endl; // X in WCS coordinates | |
| (*m_ssEntity) << " 20" << endl; | |
| (*m_ssEntity) << point[1] << endl; // Y in WCS coordinates | |
| (*m_ssEntity) << " 30" << endl; | |
| (*m_ssEntity) << point[2] << endl; // Z in WCS coordinates | |
| } | |
| //! arc from 3 points - start, end, center. dir true if arc is AntiClockwise. unspecified assumption | |
| //! is that points are on XY plane in coord system OXYZ. | |
| void CDxfWrite::writeArc(const double* start, const double* end, const double* center, bool dir) | |
| { | |
| double ax = start[0] - center[0]; | |
| double ay = start[1] - center[1]; | |
| double bx = end[0] - center[0]; | |
| double by = end[1] - center[1]; | |
| double start_angle = Base::toDegrees(atan2(ay, ax)); | |
| double end_angle = Base::toDegrees(atan2(by, bx)); | |
| 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) << center[0] << endl; // X in WCS coordinates | |
| (*m_ssEntity) << " 20" << endl; | |
| (*m_ssEntity) << center[1] << endl; // Y in WCS coordinates | |
| (*m_ssEntity) << " 30" << endl; | |
| (*m_ssEntity) << center[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* center, 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) << center[0] << endl; // X in WCS coordinates | |
| (*m_ssEntity) << " 20" << endl; | |
| (*m_ssEntity) << center[1] << endl; // Y in WCS coordinates | |
| (*m_ssEntity) << " 30" << endl; | |
| (*m_ssEntity) << center[2] << endl; // Z in WCS coordinates | |
| (*m_ssEntity) << " 40" << endl; // | |
| (*m_ssEntity) << radius << endl; // Radius | |
| } | |
| void CDxfWrite::writeEllipse( | |
| const double* center, | |
| double major_radius, | |
| double minor_radius, | |
| double rotation, | |
| double start_angle, | |
| double end_angle, | |
| bool endIsCW | |
| ) | |
| { | |
| Base::Vector3d m(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) << center[0] << endl; // X in WCS coordinates | |
| (*m_ssEntity) << " 20" << endl; | |
| (*m_ssEntity) << center[1] << endl; // Y in WCS coordinates | |
| (*m_ssEntity) << " 30" << endl; | |
| (*m_ssEntity) << center[2] << endl; // Z in WCS coordinates | |
| (*m_ssEntity) << " 11" << endl; // | |
| (*m_ssEntity) << m.x << endl; // Major X | |
| (*m_ssEntity) << " 21" << endl; | |
| (*m_ssEntity) << m.y << endl; // Major Y | |
| (*m_ssEntity) << " 31" << endl; | |
| (*m_ssEntity) << m.z << 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 | |
| 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* lineDefPoint, | |
| const double* extLine1, | |
| const double* extLine2, | |
| const char* dimText, | |
| int type | |
| ) | |
| { | |
| Base::Vector3d e1S(MakeVector3d(extLine1)); | |
| Base::Vector3d e2S(MakeVector3d(extLine2)); | |
| // point on DimLine (somewhere!) | |
| Base::Vector3d dl(MakeVector3d(lineDefPoint)); | |
| Base::Vector3d perp = dl.DistanceToLineSegment(e2S, e1S); | |
| Base::Vector3d e1E = e1S - perp; | |
| Base::Vector3d e2E = e2S - perp; | |
| Base::Vector3d para = e1E - e2E; | |
| if (type == ALIGNED) { | |
| // NOP | |
| } | |
| else if (type == HORIZONTAL) { | |
| double x = extLine1[0]; | |
| double y = lineDefPoint[1]; | |
| e1E = Base::Vector3d(x, y, 0.0); | |
| x = extLine2[0]; | |
| e2E = Base::Vector3d(x, y, 0.0); | |
| perp = Base::Vector3d(0, -1, 0); // down | |
| para = Base::Vector3d(1, 0, 0); // right | |
| if (lineDefPoint[1] > extLine1[1]) { | |
| perp = Base::Vector3d(0, 1, 0); // up | |
| } | |
| if (extLine1[0] > extLine2[0]) { | |
| para = Base::Vector3d(-1, 0, 0); // left | |
| } | |
| } | |
| else if (type == VERTICAL) { | |
| double x = lineDefPoint[0]; | |
| double y = extLine1[1]; | |
| e1E = Base::Vector3d(x, y, 0.0); | |
| y = extLine2[1]; | |
| e2E = Base::Vector3d(x, y, 0.0); | |
| perp = Base::Vector3d(1, 0, 0); | |
| para = Base::Vector3d(0, 1, 0); | |
| if (lineDefPoint[0] < extLine1[0]) { | |
| perp = Base::Vector3d(-1, 0, 0); | |
| } | |
| if (extLine1[1] > extLine2[1]) { | |
| para = Base::Vector3d(0, -1, 0); | |
| } | |
| } | |
| 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(lineDefPoint), | |
| 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(MakeVector3d(startExt1)); // apex | |
| Base::Vector3d e2S(MakeVector3d(startExt2)); | |
| Base::Vector3d e1E(MakeVector3d(endExt1)); | |
| Base::Vector3d e2E(MakeVector3d(endExt2)); | |
| 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 * std::numbers::pi; | |
| } | |
| if (endAngle < 0) { | |
| endAngle += 2 * std::numbers::pi; | |
| } | |
| Base::Vector3d startOff(cos(startAngle + offset), sin(startAngle + offset), 0.0); | |
| Base::Vector3d endOff(cos(endAngle - offset), sin(endAngle - offset), 0.0); | |
| startAngle = Base::toDegrees(startAngle); | |
| endAngle = Base::toDegrees(endAngle); | |
| Base::Vector3d linePt(MakeVector3d(lineDefPoint)); | |
| 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(MakeVector3d(centerPoint)); | |
| Base::Vector3d a(MakeVector3d(arcPoint)); | |
| 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(MakeVector3d(arcPoint1)); | |
| Base::Vector3d a2(MakeVector3d(arcPoint2)); | |
| 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); | |
| } | |
| const DxfUnits DxfUnits::Instance; | |
| CDxfRead::CDxfRead(const std::string& filepath) | |
| : 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 the Layer objects which are referenced by pointer from the Layers table. | |
| for (auto& pair : Layers) { | |
| delete pair.second; | |
| } | |
| } | |
| // Static member initializers | |
| const std::string CDxfRead::LineTypeByLayer("BYLAYER"); // NOLINT(runtime/string) | |
| const std::string CDxfRead::LineTypeByBlock("BYBLOCK"); // NOLINT(runtime/string) | |
| const std::string CDxfRead::DefaultLineType("CONTINUOUS"); // NOLINT(runtime/string) | |
| // | |
| // Setup for ProcessCommonEntityAttribute | |
| void CDxfRead::Setup3DVectorAttribute(eDXFGroupCode_t x_record_type, Base::Vector3d& destination) | |
| { | |
| SetupScaledDoubleAttribute((eDXFGroupCode_t)(x_record_type + eXOffset), destination.x); | |
| SetupScaledDoubleAttribute((eDXFGroupCode_t)(x_record_type + eYOffset), destination.y); | |
| SetupScaledDoubleAttribute((eDXFGroupCode_t)(x_record_type + eZOffset), destination.z); | |
| } | |
| void CDxfRead::Setup3DCoordinatesIntoLists( | |
| eDXFGroupCode_t x_record_type, | |
| list<double>& x_destination, | |
| list<double>& y_destination, | |
| list<double>& z_destination | |
| ) | |
| { | |
| SetupScaledDoubleIntoList((eDXFGroupCode_t)(x_record_type + eXOffset), x_destination); | |
| SetupScaledDoubleIntoList((eDXFGroupCode_t)(x_record_type + eYOffset), y_destination); | |
| SetupScaledDoubleIntoList((eDXFGroupCode_t)(x_record_type + eZOffset), z_destination); | |
| } | |
| void CDxfRead::SetupScaledDoubleAttribute(eDXFGroupCode_t x_record_type, double& destination) | |
| { | |
| m_coordinate_attributes.emplace(x_record_type, std::pair(&ProcessScaledDouble, &destination)); | |
| } | |
| void CDxfRead::SetupScaledDoubleIntoList(eDXFGroupCode_t x_record_type, list<double>& destination) | |
| { | |
| m_coordinate_attributes.emplace( | |
| x_record_type, | |
| std::pair(&ProcessScaledDoubleIntoList, &destination) | |
| ); | |
| } | |
| void CDxfRead::Setup3DDirectionAttribute(eDXFGroupCode_t x_record_type, Base::Vector3d& destination) | |
| { | |
| SetupValueAttribute((eDXFGroupCode_t)(x_record_type + eXOffset), destination.x); | |
| SetupValueAttribute((eDXFGroupCode_t)(x_record_type + eYOffset), destination.y); | |
| SetupValueAttribute((eDXFGroupCode_t)(x_record_type + eZOffset), destination.z); | |
| } | |
| void CDxfRead::SetupStringAttribute(eDXFGroupCode_t x_record_type, std::string& destination) | |
| { | |
| m_coordinate_attributes.emplace(x_record_type, std::pair(&ProcessStdString, &destination)); | |
| } | |
| template<typename T> | |
| void CDxfRead::SetupValueAttribute(eDXFGroupCode_t record_type, T& destination) | |
| { | |
| m_coordinate_attributes.emplace(record_type, std::pair(&ProcessValue<T>, &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 = 0; | |
| ss >> value; | |
| if (ss.fail()) { | |
| object->ImportError( | |
| "Unable to parse value '%s', using zero as its value\n", | |
| object->m_record_data | |
| ); | |
| } | |
| *static_cast<double*>(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 = 0; | |
| ss >> value; | |
| if (ss.fail()) { | |
| object->ImportError( | |
| "Unable to parse value '%s', using zero as its value\n", | |
| object->m_record_data | |
| ); | |
| } | |
| static_cast<std::list<double>*>(target)->push_back(object->mm(value)); | |
| } | |
| template<typename T> | |
| bool CDxfRead::ParseValue(CDxfRead* object, void* target) | |
| { | |
| std::istringstream ss; | |
| ss.imbue(std::locale("C")); | |
| ss.str(object->m_record_data); | |
| ss >> *static_cast<T*>(target); | |
| if (ss.fail()) { | |
| object->ImportError( | |
| "Unable to parse value '%s', using zero as its value\n", | |
| object->m_record_data | |
| ); | |
| *static_cast<T*>(target) = 0; | |
| return false; | |
| } | |
| // TODO: Verify nothing it left but whitespace in ss. | |
| return true; | |
| } | |
| void CDxfRead::ProcessStdString(CDxfRead* object, void* target) | |
| { | |
| *static_cast<std::string*>(target) = object->m_record_data; | |
| } | |
| void CDxfRead::InitializeAttributes() | |
| { | |
| m_coordinate_attributes.clear(); | |
| } | |
| 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 != eObjectType) { | |
| ProcessAttribute(); | |
| } | |
| repeat_last_record(); | |
| } | |
| void CDxfRead::ProcessAllEntityAttributes() | |
| { | |
| ProcessAllAttributes(); | |
| ResolveEntityAttributes(); | |
| } | |
| void CDxfRead::ResolveEntityAttributes() | |
| { | |
| m_entityAttributes.ResolveBylayerAttributes(*this); | |
| if (m_entityAttributes.m_paperSpace) { | |
| m_stats.entityCounts["ENTITIES_IN_PAPERSPACE"]++; | |
| } | |
| // TODO: Look at the space and layer (hidden/frozen?) and options and return false if the entity | |
| // is not needed. | |
| // TODO: INSERT must not call this because an INSERT on a hidden layer should always be | |
| // honoured. | |
| // Calculate the net entity transformation. | |
| // This is to handle the Object Coordinate System used in many DXF records. Note that versions | |
| // before R13 used the term ECS (Entity Coordinate System) instead. Here's who uses OCS: Lines | |
| // and Points use WCS except they can be extruded (have nonzero Thickness (39)) which occurs in | |
| // the OCS Z direction all 3D objects use WCS entirely Dimensions use a mix of OCS and WCS, | |
| // Circle, Arc, Dolid, Trace, Text, Attib, Attdef, Shape, Insert, (lw)Polyline/Vertex, hatch, | |
| // image all use the OCS | |
| // | |
| // The transformed Z axis is in EntityNormalVector, but we rescale it in case the DXF contains | |
| // an unnormalized value | |
| if (EntityNormalVector.IsNull()) { | |
| ImportError("Entity has zero-length extrusion direction\n"); | |
| } | |
| EntityNormalVector.Normalize(); | |
| // Apply the Arbitrary Axis Algorithm to determine the X and Y directions | |
| // The purpose of this algorithm is to calculate a conventional 3d orientation based only on a Z | |
| // direction, while avoiding taking the cross product of two vectors that are nearly parallel, | |
| // which would be subject to a lot of numerical inaccuracy. In this case, "close to" the Z axis | |
| // means the X and Y components of EntityNormalVector are less than 1/64, a value chosen because | |
| // it is exactly representable in all binary floating-point systems. | |
| Base::Vector3d xDirection; | |
| if (EntityNormalVector.x < ArbitraryAxisAlgorithmThreshold | |
| && EntityNormalVector.y < ArbitraryAxisAlgorithmThreshold) { | |
| // The Z axis is close to the UCS Z axis, the X direction is UCSY × OCSZ | |
| static const Base::Vector3d UCSYAxis(0, 1, 0); | |
| xDirection = UCSYAxis % EntityNormalVector; | |
| } | |
| else { | |
| // otherwise, the X direction is UCSZ × OCSZ | |
| static const Base::Vector3d UCSZAxis(0, 0, 1); | |
| xDirection = UCSZAxis % EntityNormalVector; | |
| } | |
| OCSOrientationTransform.setCol(0, xDirection); | |
| // In all cases the Y direction is the Zdirection × XDirection which gives a right-hand | |
| // orthonormal coordinate system | |
| OCSOrientationTransform.setCol(1, EntityNormalVector % xDirection); | |
| // and EntityNormalVector is of course the direction of the Z axis in the UCS. | |
| OCSOrientationTransform.setCol(2, EntityNormalVector); | |
| } | |
| // | |
| // The individual Entity reader functions | |
| // These return false if they catch an exception and ignore it because of ignore_errors. | |
| bool CDxfRead::ReadLine() | |
| { | |
| Base::Vector3d start; | |
| Base::Vector3d end; | |
| Setup3DVectorAttribute(ePrimaryPoint, start); | |
| Setup3DVectorAttribute(ePoint2, end); | |
| ProcessAllEntityAttributes(); | |
| OnReadLine(start, end, LineTypeIsHidden()); | |
| return true; | |
| } | |
| bool CDxfRead::ReadPoint() | |
| { | |
| Base::Vector3d location; | |
| Setup3DVectorAttribute(ePrimaryPoint, location); | |
| ProcessAllEntityAttributes(); | |
| OnReadPoint(location); | |
| return true; | |
| } | |
| bool CDxfRead::ReadArc() | |
| { | |
| double start_angle_degrees = 0; | |
| double end_angle_degrees = 0; | |
| double radius = 0; | |
| Base::Vector3d centre; | |
| Base::Vector3d extrusionDirection(0, 0, 1); | |
| Setup3DVectorAttribute(ePrimaryPoint, centre); | |
| SetupScaledDoubleAttribute(eFloat1, radius); | |
| SetupValueAttribute(eAngleDegrees1, start_angle_degrees); | |
| SetupValueAttribute(eAngleDegrees2, end_angle_degrees); | |
| Setup3DVectorAttribute(eExtrusionDirection, extrusionDirection); | |
| ProcessAllEntityAttributes(); | |
| OnReadArc( | |
| start_angle_degrees, | |
| end_angle_degrees, | |
| radius, | |
| centre, | |
| extrusionDirection.z, | |
| 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(eExtrusionDirection, sd.norm); | |
| SetupValueAttribute(eInteger1, sd.flag); | |
| SetupValueAttribute(eInteger2, sd.degree); | |
| SetupValueAttribute(eInteger3, sd.knots); | |
| SetupValueAttribute(eInteger4, sd.control_points); | |
| SetupValueAttribute(eInteger5, sd.fit_points); | |
| SetupScaledDoubleIntoList(eFloat1, sd.knot); | |
| SetupScaledDoubleIntoList(eFloat2, sd.weight); | |
| Setup3DCoordinatesIntoLists(ePrimaryPoint, sd.controlx, sd.controly, sd.controlz); | |
| Setup3DCoordinatesIntoLists(ePoint2, sd.fitx, sd.fity, sd.fitz); | |
| Setup3DCoordinatesIntoLists(ePoint3, sd.starttanx, sd.starttany, sd.starttanz); | |
| Setup3DCoordinatesIntoLists(ePoint4, sd.endtanx, sd.endtany, sd.endtanz); | |
| ProcessAllEntityAttributes(); | |
| OnReadSpline(sd); | |
| return true; | |
| } | |
| bool CDxfRead::ReadCircle() | |
| { | |
| double radius = 0.0; | |
| Base::Vector3d centre; | |
| Setup3DVectorAttribute(ePrimaryPoint, centre); | |
| SetupScaledDoubleAttribute(eFloat1, radius); | |
| ProcessAllEntityAttributes(); | |
| OnReadCircle(centre, radius, LineTypeIsHidden()); | |
| return true; | |
| } | |
| bool CDxfRead::ReadText() | |
| { | |
| Base::Vector3d insertionPoint; | |
| // NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers) | |
| double height = 0.03082; | |
| double rotation = 0; | |
| std::string textPrefix; | |
| Setup3DVectorAttribute(ePrimaryPoint, insertionPoint); | |
| SetupScaledDoubleAttribute(eFloat1, height); | |
| SetupValueAttribute(eAngleDegrees1, rotation); | |
| while (get_next_record() && m_record_type != eObjectType) { | |
| if (!ProcessAttribute()) { | |
| switch (m_record_type) { | |
| case eExtraText: | |
| // 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. | |
| case ePrimaryText: | |
| // final text is treated the same. | |
| // ORDER: We are asusming the type 1 record follows all the type 3's. | |
| textPrefix.append(m_record_data); | |
| break; | |
| default: | |
| break; | |
| } | |
| } | |
| } | |
| ResolveEntityAttributes(); | |
| if ((this->*stringToUTF8)(textPrefix)) { | |
| OnReadText(insertionPoint, height, textPrefix, rotation); | |
| } | |
| else { | |
| ImportError("Unable to process encoding for TEXT/MTEXT '%s'\n", textPrefix); | |
| } | |
| repeat_last_record(); | |
| return true; | |
| } | |
| bool CDxfRead::ReadEllipse() | |
| { | |
| Base::Vector3d centre; | |
| Base::Vector3d majorAxisEnd; // relative to centre | |
| double eccentricity = 0; | |
| double startAngleRadians = 0; | |
| double endAngleRadians = 2 * std::numbers::pi; | |
| Setup3DVectorAttribute(ePrimaryPoint, centre); | |
| Setup3DVectorAttribute(ePoint2, majorAxisEnd); | |
| SetupValueAttribute(eFloat1, eccentricity); | |
| SetupValueAttribute(eFloat2, startAngleRadians); | |
| SetupValueAttribute(eFloat3, endAngleRadians); | |
| ProcessAllEntityAttributes(); | |
| OnReadEllipse(centre, majorAxisEnd, eccentricity, startAngleRadians, endAngleRadians); | |
| return true; | |
| } | |
| bool CDxfRead::ReadLwPolyLine() | |
| { | |
| VertexInfo currentVertex; | |
| list<VertexInfo> 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(ePrimaryPoint, currentVertex.location); | |
| SetupValueAttribute(eFloat3, currentVertex.bulge); | |
| SetupValueAttribute(eInteger1, flags); | |
| while (get_next_record() && m_record_type != eObjectType) { | |
| if ((m_record_type == ePrimaryPoint + eXOffset && have_x) | |
| || (m_record_type == ePrimaryPoint + eYOffset && 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 = Base::Vector3d(); | |
| currentVertex.bulge = 0.0; | |
| have_x = m_record_type == ePrimaryPoint + eXOffset; | |
| have_y = m_record_type == ePrimaryPoint + eYOffset; | |
| } | |
| else if (m_record_type == ePrimaryPoint + eXOffset) { | |
| have_x = true; | |
| } | |
| else if (m_record_type == ePrimaryPoint + eYOffset) { | |
| 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); | |
| } | |
| ResolveEntityAttributes(); | |
| OnReadPolyline(vertices, flags); | |
| repeat_last_record(); | |
| return true; | |
| } | |
| bool CDxfRead::ReadPolyLine() | |
| { | |
| VertexInfo currentVertex; | |
| list<VertexInfo> vertices; | |
| int flags = 0; | |
| SetupValueAttribute(eInteger1, flags); | |
| ProcessAllEntityAttributes(); | |
| // 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(ePrimaryPoint, currentVertex.location); | |
| SetupValueAttribute(eFloat3, currentVertex.bulge); | |
| while (get_next_record() && m_record_type == eObjectType && IsObjectName("VERTEX")) { | |
| // Set vertex defaults | |
| currentVertex.location = Base::Vector3d(); | |
| currentVertex.bulge = 0.0; | |
| ProcessAllEntityAttributes(); | |
| vertices.push_back(currentVertex); | |
| } | |
| if (!IsObjectName("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() | |
| { | |
| Base::Vector3d center; | |
| Base::Vector3d scale(1, 1, 1); | |
| double rotationDegrees = 0.0; | |
| std::string blockName; | |
| Setup3DVectorAttribute(ePrimaryPoint, center); | |
| SetupValueAttribute(eFloat2, scale.x); | |
| SetupValueAttribute(eFloat3, scale.y); | |
| SetupValueAttribute(eFloat4, scale.z); | |
| SetupValueAttribute(eAngleDegrees1, rotationDegrees); | |
| SetupStringAttribute(eName, blockName); | |
| ProcessAllEntityAttributes(); | |
| OnReadInsert(center, scale, blockName, Base::toRadians(rotationDegrees)); | |
| return (true); | |
| } | |
| bool CDxfRead::ReadDimension() | |
| { | |
| Base::Vector3d start; | |
| Base::Vector3d end; | |
| Base::Vector3d linePosition; | |
| Base::Vector3d textPosition; | |
| 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(ePoint4, start); // WCS | |
| Setup3DVectorAttribute(ePoint5, end); // WCS | |
| Setup3DVectorAttribute(ePrimaryPoint, linePosition); // WCS | |
| Setup3DVectorAttribute(ePoint2, textPosition); // OCS | |
| SetupValueAttribute(eAngleDegrees1, rotation); | |
| SetupValueAttribute(eInteger1, dimensionType); | |
| ProcessAllEntityAttributes(); | |
| dimensionType &= eTypeMask; // Remove flags | |
| switch ((eDimensionType_t)dimensionType) { | |
| case eLinear: | |
| case eAligned: | |
| OnReadDimension(start, end, linePosition, dimensionType, Base::toRadians(rotation)); | |
| break; | |
| default: | |
| UnsupportedFeature("Dimension type '%d'", dimensionType); | |
| break; | |
| } | |
| return true; | |
| } | |
| bool CDxfRead::ReadUnknownEntity() | |
| { | |
| ProcessAllEntityAttributes(); | |
| UnsupportedFeature("Entity type '%s'", m_current_entity_name.c_str()); | |
| return true; | |
| } | |
| bool CDxfRead::ReadBlockInfo() | |
| { | |
| int blockType = 0; | |
| std::string blockName; | |
| InitializeAttributes(); | |
| // Both 2 and 3 are the block name. | |
| SetupStringAttribute(eName, blockName); | |
| SetupStringAttribute(eExtraText, blockName); | |
| SetupValueAttribute(eInteger1, blockType); | |
| ProcessAllAttributes(); | |
| return OnReadBlock(blockName, blockType); | |
| } | |
| bool CDxfRead::ReadBlockContents() | |
| { | |
| while (get_next_record() && m_record_type == eObjectType && !IsObjectName("ENDBLK")) { | |
| if (IgnoreErrors()) { | |
| try { | |
| if (!ReadEntity()) { | |
| return false; | |
| } | |
| } | |
| catch (...) { | |
| } | |
| } | |
| else { | |
| if (!ReadEntity()) { | |
| return false; | |
| } | |
| } | |
| } | |
| return true; | |
| } | |
| bool CDxfRead::SkipBlockContents() | |
| { | |
| while (get_next_record() && m_record_type == eObjectType && !IsObjectName("ENDBLK")) { | |
| if (IgnoreErrors()) { | |
| try { | |
| ProcessAllAttributes(); | |
| } | |
| catch (...) { | |
| } | |
| } | |
| else { | |
| ProcessAllAttributes(); | |
| } | |
| } | |
| return true; | |
| } | |
| template<typename... args> | |
| void CDxfRead::UnsupportedFeature(const char* format, args&&... argValuess) | |
| { | |
| // NOLINTNEXTLINE(runtime/printf) | |
| std::string formattedMessage = fmt::sprintf(format, std::forward<args>(argValuess)...); | |
| m_stats.unsupportedFeatures[formattedMessage].emplace_back( | |
| m_current_entity_line_number, | |
| m_current_entity_handle | |
| ); | |
| } | |
| bool CDxfRead::get_next_record() | |
| { | |
| if (m_repeat_last_record) { | |
| m_repeat_last_record = false; | |
| return m_not_eof; | |
| } | |
| do { | |
| if ((*m_ifs).eof()) { | |
| m_not_eof = false; | |
| return false; | |
| } | |
| std::getline(*m_ifs, m_record_data); | |
| ++m_line; | |
| int temp = 0; | |
| if (!ParseValue<int>(this, &temp)) { | |
| ImportError( | |
| "CDxfRead::get_next_record() Failed to get integer record type from '%s'\n", | |
| m_record_data | |
| ); | |
| return false; | |
| } | |
| m_record_type = (eDXFGroupCode_t)temp; | |
| if ((*m_ifs).eof()) { | |
| return false; | |
| } | |
| std::getline(*m_ifs, m_record_data); | |
| ++m_line; | |
| } while (m_record_type == eComment); | |
| // Remove any carriage return at the end of m_str which may occur because of inconsistent | |
| // handling of LF vs. CRLF line termination. | |
| auto last = m_record_data.rbegin(); | |
| if (last != m_record_data.rend() && *last == '\r') { | |
| m_record_data.pop_back(); | |
| } | |
| // 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; | |
| } | |
| // | |
| // Intercepts for On... calls to derived class | |
| // (These have distinct signatures from the ones they call) | |
| bool CDxfRead::ExplodePolyline(std::list<VertexInfo>& 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 = ((1.0 / startVertex->bulge) - startVertex->bulge) / 2; | |
| double cx = ((startVertex->location.x + endVertex->location.x) | |
| - ((endVertex->location.y - startVertex->location.y) * cot)) | |
| / 2; | |
| double cy = ((startVertex->location.y + endVertex->location.y) | |
| + ((endVertex->location.x - startVertex->location.x) * cot)) | |
| / 2; | |
| Base::Vector3d pc(cx, cy, (startVertex->location.z + endVertex->location.z) / 2); | |
| OnReadArc(startVertex->location, endVertex->location, pc, startVertex->bulge >= 0, false); | |
| } | |
| else { | |
| OnReadLine(startVertex->location, endVertex->location, false); | |
| } | |
| } | |
| // elsethis is the first loop iteration on an open shape, endVertex is the first point, and | |
| // there is no closure line to draw engin there. | |
| startVertex = endVertex; | |
| } | |
| return true; | |
| } | |
| void CDxfRead::OnReadArc( | |
| double start_angle, | |
| double end_angle, | |
| double radius, | |
| const Base::Vector3d& center, | |
| double z_extrusion_dir, | |
| bool hidden | |
| ) | |
| { | |
| Base::Vector3d temp(center); | |
| // Calculate the start and end points of the arc | |
| Base::Vector3d start(center); | |
| start.x += radius * cos(Base::toRadians(start_angle)); | |
| start.y += radius * sin(Base::toRadians(start_angle)); | |
| Base::Vector3d end(center); | |
| end.x += radius * cos(Base::toRadians(end_angle)); | |
| end.y += radius * sin(Base::toRadians(end_angle)); | |
| if (z_extrusion_dir < 0) { | |
| // This is a dumbed-down handling of general OCS. This only works | |
| // for arcs drawn exactly upside down (i.e. with the extrusion vector | |
| // being (0, 0, <0). | |
| // We treat this as 180-degree mirroring through the YZ plane | |
| // TODO: I don't even think this is correct, but it is functionally what the | |
| // code did before. | |
| temp.x = -temp.x; | |
| start.x = -start.x; | |
| end.x = -end.x; | |
| } | |
| OnReadArc(start, end, temp, true, hidden); | |
| } | |
| void CDxfRead::OnReadCircle(const Base::Vector3d& center, double radius, bool hidden) | |
| { | |
| // OnReadCircle wants a start point, so we pick an arbitrary point on the circumference | |
| Base::Vector3d start(center); | |
| start.x += radius; | |
| OnReadCircle( | |
| start, | |
| center, | |
| false, | |
| hidden | |
| ); // false to change direction because otherwise the arc length is zero | |
| } | |
| // NOLINTBEGIN(bugprone-easily-swappable-parameters) | |
| void CDxfRead::OnReadEllipse( | |
| const Base::Vector3d& center, | |
| const Base::Vector3d& majorAxisEnd, | |
| double ratio, | |
| double start_angle, | |
| double end_angle | |
| ) | |
| // NOLINTEND(bugprone-easily-swappable-parameters) | |
| { | |
| double major_radius = majorAxisEnd.Length(); | |
| 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(majorAxisEnd.y, majorAxisEnd.x); | |
| OnReadEllipse(center, major_radius, minor_radius, rotation, start_angle, end_angle, true); | |
| } | |
| bool CDxfRead::ReadVersion() | |
| { | |
| static const std::vector<std::string> 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 | |
| auto first = VersionNames.cbegin(); | |
| auto last = VersionNames.cend(); | |
| auto 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; | |
| } | |
| m_stats.dxfVersion = m_record_data; | |
| return ResolveEncoding(); | |
| } | |
| bool CDxfRead::ReadDWGCodePage() | |
| { | |
| get_next_record(); // Get the value for the variable | |
| assert(m_CodePage.empty()); // If not, we have found two DWGCODEPAGE variables or DoRead | |
| // was called twice on the same CDxfRead object. | |
| m_CodePage = m_record_data; | |
| return ResolveEncoding(); | |
| } | |
| bool CDxfRead::ResolveEncoding() | |
| { | |
| if (m_version >= R2007) { // Note this does not include RUnknown, but does include RLater | |
| m_encoding = "utf_8"; | |
| stringToUTF8 = &CDxfRead::UTF8ToUTF8; | |
| } | |
| else if (m_CodePage.empty()) { | |
| // cp1252 | |
| m_encoding = "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. | |
| m_encoding = m_CodePage; | |
| std::transform(m_encoding.begin(), m_encoding.end(), m_encoding.begin(), ::tolower); | |
| // Add mapping for common non-standard encoding names. | |
| if (m_encoding == "8859_1") { | |
| m_encoding = "iso-8859-1"; // Replace with a name Python understands | |
| } | |
| // NOLINTNEXTLINE(readability/nolint) | |
| if (m_encoding.rfind(ANSI_ENCODING_PREFIX, 0) == 0 && m_encoding.rfind("ansi_x3", 0) != 0) { | |
| m_encoding.replace(0, (sizeof ANSI_ENCODING_PREFIX) - 1, "cp"); | |
| } | |
| // 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) { | |
| // PyCodec_Decoder failed, which means Python could not find the encoding. | |
| // This sets a Python LookupError. We clear this low-level error because | |
| // our caller will throw a more informative, high-level exception. | |
| PyErr_Clear(); | |
| return false; | |
| } | |
| 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); | |
| } | |
| m_stats.dxfEncoding = m_encoding; | |
| return !m_encoding.empty(); | |
| } | |
| // NOLINTNEXTLINE(readability/nolint) | |
| // NOLINTNEXTLINE(readability-convert-member-functions-to-static) | |
| bool CDxfRead::UTF8ToUTF8(std::string& /*encoded*/) const | |
| { | |
| return true; | |
| } | |
| bool CDxfRead::GeneralToUTF8(std::string& encoded) const | |
| { | |
| Base::PyGILStateLocker lock; | |
| PyObject* decoded | |
| = PyUnicode_Decode(encoded.c_str(), (Py_ssize_t)encoded.length(), m_encoding.c_str(), "strict"); | |
| if (decoded == nullptr) { | |
| return false; | |
| } | |
| const char* converted = PyUnicode_AsUTF8(decoded); | |
| // converted has the same lifetime as decoded so we don't have to delete it. | |
| if (converted != nullptr) { | |
| encoded = converted; | |
| } | |
| Py_DECREF(decoded); | |
| return converted != nullptr; | |
| } | |
| void CDxfRead::DoRead(const bool ignore_errors /* = false */) | |
| { | |
| m_ignore_errors = ignore_errors; | |
| if (m_fail) { | |
| return; | |
| } | |
| try { | |
| StartImport(); | |
| // Loop reading the sections. | |
| while (get_next_record()) { | |
| if (m_record_type != eObjectType) { | |
| ImportError( | |
| "Found type %d record when expecting start of a SECTION or EOF\n", | |
| (int)m_record_type | |
| ); | |
| continue; | |
| } | |
| if (IsObjectName("EOF")) { // TODO: Check for drivel beyond EOF record | |
| break; | |
| } | |
| if (!IsObjectName("SECTION")) { | |
| ImportError( | |
| "Found %s record when expecting start of a SECTION\n", | |
| m_record_data.c_str() | |
| ); | |
| continue; | |
| } | |
| if (!ReadSection()) { | |
| throw Base::Exception("Failed to read DXF section (returned false)."); | |
| } | |
| } | |
| FinishImport(); | |
| } | |
| catch (const Base::Exception& e) { | |
| // This catches specific FreeCAD exceptions and re-throws them. | |
| throw; | |
| } | |
| catch (const std::exception& e) { | |
| // This catches all standard C++ exceptions and converts them | |
| // to a FreeCAD exception, which the binding layer can handle. | |
| throw Base::Exception(e.what()); | |
| } | |
| catch (...) { | |
| // This is a catch-all for any other non-standard C++ exceptions. | |
| throw Base::Exception("An unknown, non-standard C++ exception occurred during DXF import."); | |
| } | |
| } | |
| bool CDxfRead::ReadSection() | |
| { | |
| if (!get_next_record()) { | |
| throw Base::Exception("Unexpected end of file after SECTION tag."); | |
| } | |
| if (m_record_type != eName) { | |
| ImportError("Ignored SECTION with no name record\n"); | |
| return ReadIgnoredSection(); | |
| } | |
| if (IsObjectName("HEADER")) { | |
| if (!ReadHeaderSection()) { | |
| throw Base::Exception("Failed while reading HEADER section."); | |
| } | |
| return true; | |
| } | |
| if (IsObjectName("TABLES")) { | |
| if (!ReadTablesSection()) { | |
| throw Base::Exception("Failed while reading TABLES section."); | |
| } | |
| return true; | |
| } | |
| if (IsObjectName("BLOCKS")) { | |
| if (!ReadBlocksSection()) { | |
| throw Base::Exception("Failed while reading BLOCKS section."); | |
| } | |
| return true; | |
| } | |
| if (IsObjectName("ENTITIES")) { | |
| if (!ReadEntitiesSection()) { | |
| throw Base::Exception("Failed while reading ENTITIES section."); | |
| } | |
| return true; | |
| } | |
| if (!ReadIgnoredSection()) { | |
| throw Base::Exception("Failed while reading an unknown/ignored section."); | |
| } | |
| return true; | |
| } | |
| void CDxfRead::ProcessLayerReference(CDxfRead* object, void* target) | |
| { | |
| if (!object->Layers.contains(object->m_record_data)) { | |
| object->ImportError("First reference to missing Layer '%s'", object->m_record_data); | |
| // Synthesize the Layer so we don't get the same error again. | |
| // We need to take copies of the string arguments because MakeLayer uses them as move | |
| // inputs. | |
| object->Layers[object->m_record_data] | |
| = object->MakeLayer(object->m_record_data, DefaultColor, std::string(DefaultLineType)); | |
| } | |
| *static_cast<Layer**>(target) = object->Layers.at(object->m_record_data); | |
| } | |
| bool CDxfRead::ReadEntity() | |
| { | |
| m_current_entity_line_number = m_line; | |
| m_current_entity_name = m_record_data; | |
| InitializeAttributes(); | |
| m_entityAttributes.SetDefaults(); | |
| m_current_entity_handle.clear(); | |
| SetupStringAttribute(eHandle, m_current_entity_handle); | |
| EntityNormalVector.Set(0, 0, 1); | |
| Setup3DVectorAttribute(eExtrusionDirection, EntityNormalVector); | |
| SetupStringAttribute(eLinetypeName, m_entityAttributes.m_LineType); | |
| m_coordinate_attributes.emplace( | |
| eLayerName, | |
| std::pair(&ProcessLayerReference, &m_entityAttributes.m_Layer) | |
| ); | |
| SetupValueAttribute( | |
| eCoordinateSpace, | |
| m_entityAttributes.m_paperSpace | |
| ); // TODO: Ensure the stream is noboolalpha (for that | |
| // matter ensure the stream has the "C" locale | |
| SetupValueAttribute(eColor, m_entityAttributes.m_Color); | |
| m_stats.entityCounts[m_record_data]++; | |
| // The entity record is already the current record and is already checked as a type 0 record | |
| if (IsObjectName("LINE")) { | |
| return ReadLine(); | |
| } | |
| if (IsObjectName("ARC")) { | |
| return ReadArc(); | |
| } | |
| if (IsObjectName("CIRCLE")) { | |
| return ReadCircle(); | |
| } | |
| if (IsObjectName("MTEXT")) { | |
| return ReadText(); | |
| } | |
| if (IsObjectName("TEXT")) { | |
| return ReadText(); | |
| } | |
| if (IsObjectName("ELLIPSE")) { | |
| return ReadEllipse(); | |
| } | |
| if (IsObjectName("SPLINE")) { | |
| return ReadSpline(); | |
| } | |
| if (IsObjectName("LWPOLYLINE")) { | |
| return ReadLwPolyLine(); | |
| } | |
| if (IsObjectName("POLYLINE")) { | |
| return ReadPolyLine(); | |
| } | |
| if (IsObjectName("POINT")) { | |
| return ReadPoint(); | |
| } | |
| if (IsObjectName("INSERT")) { | |
| return ReadInsert(); | |
| } | |
| if (IsObjectName("DIMENSION")) { | |
| return ReadDimension(); | |
| } | |
| 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 == eObjectType && IsObjectName("ENDSEC")) { | |
| if (m_unitScalingFactor == 0.0) { | |
| // Neither INSUNITS nor MEASUREMENT found, assume 1 DXF unit = 1mm | |
| // TODO: Perhaps this default should depend on the current project's unit system | |
| m_unitScalingFactor = m_additionalScaling; | |
| m_stats.fileUnits = "Unspecified (Defaulting to 1:1)"; | |
| } | |
| m_stats.finalScalingFactor = m_unitScalingFactor; | |
| return true; | |
| } | |
| if (m_record_type != eVariableName) { | |
| continue; // Quietly ignore unknown record types | |
| } | |
| // Store the variable name before we try to read its value. | |
| std::string currentVarName = m_record_data; | |
| if (!ReadVariable()) { | |
| // If ReadVariable returns false, throw an exception with the variable name. | |
| throw Base::Exception("Failed while reading value for HEADER variable: " + currentVarName); | |
| } | |
| } | |
| // If the loop finishes without finding ENDSEC, it's an error. | |
| throw Base::Exception("Unexpected end of file inside HEADER section."); | |
| } | |
| bool CDxfRead::ReadVariable() | |
| { | |
| if (IsVariableName("$INSUNITS")) { | |
| get_next_record(); // Get the value for the variable | |
| int varValue = 0; | |
| if (!ParseValue<int>(this, &varValue)) { | |
| ImportError("Failed to get integer from INSUNITS value '%s'\n", m_record_data); | |
| } | |
| else { | |
| auto units = DxfUnits::eDxfUnits_t(varValue); | |
| if (!DxfUnits::IsValid(units)) { | |
| units = DxfUnits::eUnspecified; | |
| } | |
| m_unitScalingFactor = DxfUnits::Factor(units) * m_additionalScaling; | |
| m_stats.scalingSource = "$INSUNITS"; | |
| m_stats.fileUnits = DxfUnitToString(units); | |
| } | |
| return true; | |
| } | |
| if (IsVariableName("$MEASUREMENT")) { | |
| get_next_record(); | |
| int varValue = 1; | |
| if (m_unitScalingFactor == 0.0 && ParseValue<int>(this, &varValue)) { | |
| auto units = (varValue != 0 ? DxfUnits::eMillimeters : DxfUnits::eInches); | |
| m_unitScalingFactor = DxfUnits::Factor(units) * m_additionalScaling; | |
| m_stats.scalingSource = "$MEASUREMENT"; | |
| m_stats.fileUnits = DxfUnitToString(units); | |
| } | |
| return true; | |
| } | |
| if (IsVariableName("$ACADVER")) { | |
| return ReadVersion(); | |
| } | |
| if (IsVariableName("$DWGCODEPAGE")) { | |
| return ReadDWGCodePage(); | |
| } | |
| // any other variable, skip its value | |
| return get_next_record(); | |
| } | |
| 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 != eObjectType) { | |
| continue; // Ignore any non-type-0 contents in the section. | |
| } | |
| if (IsObjectName("ENDSEC")) { | |
| return true; | |
| } | |
| if (!IsObjectName("TABLE")) { | |
| continue; // Ignore any type-0 non-TABLE contents in the section | |
| } | |
| get_next_record(); | |
| if (m_record_type != eName) { | |
| ImportError("Found unexpected type %d record instead of table name\n", (int)m_record_type); | |
| } | |
| else if (IsObjectName("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 == eObjectType && IsObjectName("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 != eObjectType) { | |
| continue; // quietly ignore non-type-0 records; | |
| } | |
| if (IsObjectName("ENDSEC")) { | |
| // End of section | |
| return true; | |
| } | |
| if (!IsObjectName("BLOCK")) { | |
| continue; // quietly ignore non-BLOCK records | |
| } | |
| if (!ReadBlockInfo()) { | |
| ImportError("CDxfRead::DoRead() Failed to read block\n"); | |
| } | |
| } | |
| 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 != eObjectType) { | |
| continue; // quietly ignore non-type-0 records; | |
| } | |
| if (IsObjectName("ENDSEC")) { | |
| // End of section | |
| return true; | |
| } | |
| if (IgnoreErrors()) { | |
| try { | |
| if (!ReadEntity()) { | |
| return false; | |
| } | |
| } | |
| catch (const Base::Exception& e) { | |
| e.reportException(); | |
| } | |
| catch (...) { | |
| ImportError("CDxfRead::ReadEntity raised unknown exception\n"); | |
| } | |
| } | |
| else { | |
| if (!ReadEntity()) { | |
| return false; | |
| } | |
| } | |
| } | |
| return false; | |
| } | |
| bool CDxfRead::ReadLayer() | |
| { | |
| std::string layername; | |
| ColorIndex_t layerColor = DefaultColor; | |
| int layerFlags = 0; | |
| std::string lineTypeName(DefaultLineType); | |
| InitializeAttributes(); | |
| SetupStringAttribute(eName, layername); | |
| SetupValueAttribute(eColor, layerColor); | |
| SetupValueAttribute(eInteger1, layerFlags); | |
| SetupStringAttribute(eLinetypeName, lineTypeName); | |
| ProcessAllAttributes(); | |
| if (layername.empty()) { | |
| ImportError("CDxfRead::ReadLayer() - no layer name\n"); | |
| return false; | |
| } | |
| if ((layerFlags & 0x01) != 0) { | |
| // Frozen layers are implicitly hidden which we don't do yet. | |
| // TODO: Should have an import option to omit frozen layers. | |
| UnsupportedFeature("Frozen layers"); | |
| } | |
| Layers[layername] = MakeLayer(layername, layerColor, std::move(lineTypeName)); | |
| return true; | |
| } | |
| CDxfRead::Layer* CDxfRead::MakeLayer(const std::string& name, ColorIndex_t color, std::string&& lineType) | |
| { | |
| return new Layer(name, color, std::move(lineType)); | |
| } | |
| 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 != eObjectType) { | |
| continue; // quietly ignore non-type-0 records; this table has some preamble | |
| } | |
| if (IsObjectName("TABLE") || IsObjectName("ENDSEC")) { | |
| // End of table | |
| repeat_last_record(); | |
| return true; | |
| } | |
| if (!IsObjectName("LAYER")) { | |
| continue; // quietly ignore non-LAYER records | |
| } | |
| if (!ReadLayer()) { | |
| ImportError("CDxfRead::DoRead() Failed to read layer\n"); | |
| } | |
| } | |
| 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 == eObjectType && (IsObjectName("TABLE") || IsObjectName("ENDSEC"))) { | |
| repeat_last_record(); | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| // NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers) | |
| 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; | |
| } | |
| 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; | |
| } | |
| // 8 and beyond yield the black level | |
| return blackLevel; | |
| } | |
| inline static Base::Color wheel(int hue, double blackLevel, double multiplier = 1.0) | |
| { | |
| return Base::Color( | |
| (float)(level(hue - 0, blackLevel) * multiplier), | |
| (float)(level(hue - 8, blackLevel) * multiplier), | |
| (float)(level(hue - 16, blackLevel) * multiplier) | |
| ); | |
| } | |
| Base::Color CDxfRead::ObjectColor(ColorIndex_t index) | |
| { | |
| // 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. | |
| Base::Color result = Base::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 = Base::Color(); | |
| } | |
| else if (index < 7) { | |
| result = wheel((index - 1) * 4, 0x00); | |
| } | |
| else if (index == 7) { | |
| // DXF color 7 is "black/white" and should adapt to the background. | |
| // Since we cannot easily query the background theme from here, we will use a | |
| // neutral mid-gray, which is visible on both light and dark themes. | |
| result = Base::Color(0.5f, 0.5f, 0.5f); | |
| } | |
| else if (index == 8) { | |
| result = Base::Color(0.5, 0.5, 0.5); | |
| } | |
| else if (index == 9) { | |
| result = Base::Color(0.75, 0.75, 0.75); | |
| } | |
| else if (index >= 250) { | |
| auto brightness = (float)((index - 250 + (255 - index) * 0.2) / 5); | |
| result = Base::Color(brightness, brightness, brightness); | |
| } | |
| else { | |
| static const std::array<float, 5> fades = {1.00F, 0.74F, 0.50F, 0.40F, 0.30F}; | |
| return wheel(index / 10 - 1, (index & 1) != 0 ? 0.69 : 0, fades[(index / 2) % 5]); | |
| } | |
| // 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; | |
| } | |
| // NOLINTEND(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers) | |
| template void CDxfRead::UnsupportedFeature<>(const char*); | |