diff --git a/src/base/brep_utils.cpp b/src/base/brep_utils.cpp index f5ac725e..1b2f1197 100644 --- a/src/base/brep_utils.cpp +++ b/src/base/brep_utils.cpp @@ -36,6 +36,15 @@ void BRepUtils::addShape(TopoDS_Shape* ptrTargetShape, const TopoDS_Shape& shape builder.Add(*ptrTargetShape, shape); } +TopoDS_Edge BRepUtils::makeEdge(const Handle(Poly_Polygon3D)& polygon) +{ + TopoDS_Edge edge; + BRep_Builder builder; + builder.MakeEdge(edge); + builder.UpdateEdge(edge, polygon); + return edge; +} + TopoDS_Face BRepUtils::makeFace(const Handle(Poly_Triangulation)& mesh) { TopoDS_Face face; diff --git a/src/base/brep_utils.h b/src/base/brep_utils.h index 98d6952c..76830e89 100644 --- a/src/base/brep_utils.h +++ b/src/base/brep_utils.h @@ -8,7 +8,9 @@ #include "occ_brep_mesh_parameters.h" +#include #include +#include #include #include #include @@ -26,6 +28,9 @@ struct BRepUtils { // Adds 'shape' in target shape 'ptrTargetShape' static void addShape(TopoDS_Shape* ptrTargetShape, const TopoDS_Shape& shape); + // Creates a non-geometric TopoDS_Edge wrapping 'polygon' + static TopoDS_Edge makeEdge(const Handle(Poly_Polygon3D)& polygon); + // Creates a non-geometric TopoDS_Face wrapping triangulation 'mesh' static TopoDS_Face makeFace(const Handle(Poly_Triangulation)& mesh); diff --git a/src/base/mesh_utils.cpp b/src/base/mesh_utils.cpp index 82ac14a7..0f1166d2 100644 --- a/src/base/mesh_utils.cpp +++ b/src/base/mesh_utils.cpp @@ -6,17 +6,32 @@ #include "mesh_utils.h" #include "math_utils.h" -#include + +#include #include +#include namespace Mayo { +namespace MeshUtils { + +namespace { -double MeshUtils::triangleSignedVolume(const gp_XYZ& p1, const gp_XYZ& p2, const gp_XYZ& p3) +TColStd_Array1OfReal createArray1OfReal(int count) +{ + if (count > 0) + return TColStd_Array1OfReal(1 , count); + else + return TColStd_Array1OfReal(); +} + +} // namespace + +double triangleSignedVolume(const gp_XYZ& p1, const gp_XYZ& p2, const gp_XYZ& p3) { return p1.Dot(p2.Crossed(p3)) / 6.0f; } -double MeshUtils::triangleArea(const gp_XYZ& p1, const gp_XYZ& p2, const gp_XYZ& p3) +double triangleArea(const gp_XYZ& p1, const gp_XYZ& p2, const gp_XYZ& p3) { const double ax = p2.X() - p1.X(); const double ay = p2.Y() - p1.Y(); @@ -30,7 +45,7 @@ double MeshUtils::triangleArea(const gp_XYZ& p1, const gp_XYZ& p2, const gp_XYZ& return 0.5 * std::sqrt(cx*cx + cy*cy + cz*cz); } -double MeshUtils::triangulationVolume(const Handle_Poly_Triangulation& triangulation) +double triangulationVolume(const Handle_Poly_Triangulation& triangulation) { if (!triangulation) return 0; @@ -49,7 +64,7 @@ double MeshUtils::triangulationVolume(const Handle_Poly_Triangulation& triangula return std::abs(volume); } -double MeshUtils::triangulationArea(const Handle_Poly_Triangulation& triangulation) +double triangulationArea(const Handle_Poly_Triangulation& triangulation) { if (!triangulation) return 0; @@ -68,7 +83,7 @@ double MeshUtils::triangulationArea(const Handle_Poly_Triangulation& triangulati return area; } -void MeshUtils::setNode(const Handle_Poly_Triangulation& triangulation, int index, const gp_Pnt& pnt) +void setNode(const Handle_Poly_Triangulation& triangulation, int index, const gp_Pnt& pnt) { #if OCC_VERSION_HEX >= 0x070600 triangulation->SetNode(index, pnt); @@ -77,7 +92,7 @@ void MeshUtils::setNode(const Handle_Poly_Triangulation& triangulation, int inde #endif } -void MeshUtils::setTriangle(const Handle_Poly_Triangulation& triangulation, int index, const Poly_Triangle& triangle) +void setTriangle(const Handle_Poly_Triangulation& triangulation, int index, const Poly_Triangle& triangle) { #if OCC_VERSION_HEX >= 0x070600 triangulation->SetTriangle(index, triangle); @@ -86,7 +101,7 @@ void MeshUtils::setTriangle(const Handle_Poly_Triangulation& triangulation, int #endif } -void MeshUtils::setNormal(const Handle_Poly_Triangulation& triangulation, int index, const Poly_Triangulation_NormalType& n) +void setNormal(const Handle_Poly_Triangulation& triangulation, int index, const Poly_Triangulation_NormalType& n) { #if OCC_VERSION_HEX >= 0x070600 triangulation->SetNormal(index, n); @@ -98,7 +113,7 @@ void MeshUtils::setNormal(const Handle_Poly_Triangulation& triangulation, int in #endif } -void MeshUtils::setUvNode(const Handle_Poly_Triangulation& triangulation, int index, double u, double v) +void setUvNode(const Handle_Poly_Triangulation& triangulation, int index, double u, double v) { #if OCC_VERSION_HEX >= 0x070600 triangulation->SetUVNode(index, gp_Pnt2d{u, v}); @@ -107,7 +122,7 @@ void MeshUtils::setUvNode(const Handle_Poly_Triangulation& triangulation, int in #endif } -void MeshUtils::allocateNormals(const Handle_Poly_Triangulation& triangulation) +void allocateNormals(const Handle_Poly_Triangulation& triangulation) { #if OCC_VERSION_HEX >= 0x070600 triangulation->AddNormals(); @@ -117,8 +132,18 @@ void MeshUtils::allocateNormals(const Handle_Poly_Triangulation& triangulation) #endif } +const Poly_Array1OfTriangle& triangles(const Handle_Poly_Triangulation& triangulation) +{ +#if OCC_VERSION_HEX < 0x070600 + return triangulation->Triangles(); +#else + // Note: Poly_Triangulation::Triangles() was deprecated starting from OpenCascade v7.6.0 + return triangulation->InternalTriangles(); +#endif +} + // Adapted from http://cs.smith.edu/~jorourke/Code/polyorient.C -MeshUtils::Orientation MeshUtils::orientation(const AdaptorPolyline2d& polyline) +MeshUtils::Orientation orientation(const AdaptorPolyline2d& polyline) { const int pntCount = polyline.pointCount(); if (pntCount < 2) @@ -172,7 +197,7 @@ MeshUtils::Orientation MeshUtils::orientation(const AdaptorPolyline2d& polyline) } } -gp_Vec MeshUtils::directionAt(const AdaptorPolyline3d& polyline, int i) +gp_Vec directionAt(const AdaptorPolyline3d& polyline, int i) { const int pntCount = polyline.pointCount(); if (pntCount > 1) { @@ -191,4 +216,63 @@ gp_Vec MeshUtils::directionAt(const AdaptorPolyline3d& polyline, int i) return gp_Vec(); } +Polygon3dBuilder::Polygon3dBuilder(int nodeCount, ParametersOption option) +#if OCC_VERSION_HEX >= 0x070500 + : m_polygon(new Poly_Polygon3D(nodeCount, option == ParametersOption::With)), + m_ptrNodes(&m_polygon->ChangeNodes()), + m_ptrParams(option == ParametersOption::With ? &m_polygon->ChangeParameters() : nullptr) +#else + : m_nodes(1, nodeCount), + m_params(std::move(createArray1OfReal(option == ParametersOption::With ? nodeCount : 0))), + m_ptrNodes(&m_nodes), + m_ptrParams(option == ParametersOption::With ? &m_params : nullptr) +#endif +{ + assert(m_ptrNodes); + assert( + (option == ParametersOption::None && !m_ptrParams) + || (option == ParametersOption::With && m_ptrParams) + ); +} + +void Polygon3dBuilder::setNode(int i, const gp_Pnt &pnt) +{ + if (m_isFinalized) + throw std::runtime_error("Can't call setNode() on finalized Polygon3dBuilder object"); + + m_ptrNodes->ChangeValue(i) = pnt; +} + +void Polygon3dBuilder::setParameter(int i, double u) +{ + if (m_isFinalized) + throw std::runtime_error("Can't call setParameter() on finalized Polygon3dBuilder object"); + + if (m_ptrParams) + m_ptrParams->ChangeValue(i) = u; +} + +void Polygon3dBuilder::finalize() +{ + if (m_isFinalized) + return; + +#if OCC_VERSION_HEX < 0x070500 + if (m_ptrParams) + m_polygon = new Poly_Polygon3D(m_nodes, m_params); + else + m_polygon = new Poly_Polygon3D(m_nodes); +#endif + m_isFinalized = true; +} + +OccHandle Polygon3dBuilder::get() const +{ + if (!m_isFinalized) + throw std::runtime_error("Can't call get() on non finalized Polygon3dBuilder object"); + + return m_polygon; +} + +} // namespace MeshUtils } // namespace Mayo diff --git a/src/base/mesh_utils.h b/src/base/mesh_utils.h index 85aecd00..791045fc 100644 --- a/src/base/mesh_utils.h +++ b/src/base/mesh_utils.h @@ -6,6 +6,9 @@ #pragma once +#include "occ_handle.h" + +#include #include #include class gp_XYZ; @@ -13,56 +16,76 @@ class gp_XYZ; namespace Mayo { // Provides helper functions for mesh and triangle objects -struct MeshUtils { - static double triangleSignedVolume(const gp_XYZ& p1, const gp_XYZ& p2, const gp_XYZ& p3); - static double triangleArea(const gp_XYZ& p1, const gp_XYZ& p2, const gp_XYZ& p3); +namespace MeshUtils { + +double triangleSignedVolume(const gp_XYZ& p1, const gp_XYZ& p2, const gp_XYZ& p3); +double triangleArea(const gp_XYZ& p1, const gp_XYZ& p2, const gp_XYZ& p3); - static double triangulationVolume(const Handle_Poly_Triangulation& triangulation); - static double triangulationArea(const Handle_Poly_Triangulation& triangulation); +double triangulationVolume(const Handle_Poly_Triangulation& triangulation); +double triangulationArea(const Handle_Poly_Triangulation& triangulation); #if OCC_VERSION_HEX >= 0x070600 - using Poly_Triangulation_NormalType = gp_Vec3f; +using Poly_Triangulation_NormalType = gp_Vec3f; #else - using Poly_Triangulation_NormalType = gp_Vec; +using Poly_Triangulation_NormalType = gp_Vec; #endif - static void setNode(const Handle_Poly_Triangulation& triangulation, int index, const gp_Pnt& pnt); - static void setTriangle(const Handle_Poly_Triangulation& triangulation, int index, const Poly_Triangle& triangle); - static void setNormal(const Handle_Poly_Triangulation& triangulation, int index, const Poly_Triangulation_NormalType& n); - static void setUvNode(const Handle_Poly_Triangulation& triangulation, int index, double u, double v); - static void allocateNormals(const Handle_Poly_Triangulation& triangulation); +void setNode(const Handle_Poly_Triangulation& triangulation, int index, const gp_Pnt& pnt); +void setTriangle(const Handle_Poly_Triangulation& triangulation, int index, const Poly_Triangle& triangle); +void setNormal(const Handle_Poly_Triangulation& triangulation, int index, const Poly_Triangulation_NormalType& n); +void setUvNode(const Handle_Poly_Triangulation& triangulation, int index, double u, double v); +void allocateNormals(const Handle_Poly_Triangulation& triangulation); - static const Poly_Array1OfTriangle& triangles(const Handle_Poly_Triangulation& triangulation) { -#if OCC_VERSION_HEX < 0x070600 - return triangulation->Triangles(); -#else - // Note: Poly_Triangulation::Triangles() was deprecated starting from OpenCascade v7.6.0 - return triangulation->InternalTriangles(); +const Poly_Array1OfTriangle& triangles(const Handle_Poly_Triangulation& triangulation); + +enum class Orientation { + Unknown, + Clockwise, + CounterClockwise +}; + +class AdaptorPolyline2d { +public: + virtual gp_Pnt2d pointAt(int index) const = 0; + virtual int pointCount() const = 0; + virtual bool empty() const { return this->pointCount() <= 0; } +}; + +class AdaptorPolyline3d { +public: + virtual const gp_Pnt& pointAt(int i) const = 0; + virtual int pointCount() const = 0; + virtual int empty() const { return this->pointCount() <= 0; } +}; + +Orientation orientation(const AdaptorPolyline2d& polyline); +gp_Vec directionAt(const AdaptorPolyline3d& polyline, int i); + +// Provides helper to create Poly_Polygon3D objects +// Poly_Polygon3D class interface changed from OpenCascade 7.4 to 7.5 version so using this class +// directly might cause compilation errors +// Prefer Polygon3dBuilder so application code doesn't have to care about OpenCascade version +class Polygon3dBuilder { +public: + enum class ParametersOption { None, With }; + + Polygon3dBuilder(int nodeCount, ParametersOption option = ParametersOption::None); + + void setNode(int i, const gp_Pnt& pnt); + void setParameter(int i, double u); + void finalize(); + OccHandle get() const; + +private: + bool m_isFinalized = false; + OccHandle m_polygon; +#if OCC_VERSION_HEX < 0x070500 + TColgp_Array1OfPnt m_nodes; + TColStd_Array1OfReal m_params; #endif - } - - enum class Orientation { - Unknown, - Clockwise, - CounterClockwise - }; - - class AdaptorPolyline2d { - public: - virtual gp_Pnt2d pointAt(int index) const = 0; - virtual int pointCount() const = 0; - virtual bool empty() const { return this->pointCount() <= 0; } - }; - - class AdaptorPolyline3d { - public: - virtual const gp_Pnt& pointAt(int i) const = 0; - virtual int pointCount() const = 0; - virtual int empty() const { return this->pointCount() <= 0; } - }; - - static Orientation orientation(const AdaptorPolyline2d& polyline); - static gp_Vec directionAt(const AdaptorPolyline3d& polyline, int i); + TColgp_Array1OfPnt* m_ptrNodes = nullptr; + TColStd_Array1OfReal* m_ptrParams = nullptr; }; +} // namespace MeshUtils } // namespace Mayo diff --git a/src/base/property_value_conversion.cpp b/src/base/property_value_conversion.cpp index 4e95b8d6..e44ae8c4 100644 --- a/src/base/property_value_conversion.cpp +++ b/src/base/property_value_conversion.cpp @@ -33,18 +33,18 @@ namespace { static std::string toString(double value, int prec = 6) { #if __cpp_lib_to_chars - char buff[64] = {}; - auto toCharsFormat = std::chars_format::general; - auto resToChars = std::to_chars(std::begin(buff), std::end(buff), value, toCharsFormat, prec); - if (resToChars.ec != std::errc()) - throw std::runtime_error("value_too_large"); + char buff[64] = {}; + auto toCharsFormat = std::chars_format::general; + auto resToChars = std::to_chars(std::begin(buff), std::end(buff), value, toCharsFormat, prec); + if (resToChars.ec != std::errc()) + throw std::runtime_error("value_too_large"); - return std::string(buff, resToChars.ptr - buff); + return std::string(buff, resToChars.ptr - buff); #else - std::stringstream sstr; - sstr.precision(prec); - sstr << value; - return sstr.str(); + std::stringstream sstr; + sstr.precision(prec); + sstr << value; + return sstr.str(); #endif } diff --git a/src/base/string_conv.h b/src/base/string_conv.h index 166ccb67..4570ee52 100644 --- a/src/base/string_conv.h +++ b/src/base/string_conv.h @@ -6,6 +6,7 @@ #pragma once +#include #include #include #include @@ -136,6 +137,13 @@ template<> struct StringConv } }; +// std::string_view -> NCollection_Utf8String +template<> struct StringConv { + static auto to(std::string_view str) { + return NCollection_Utf8String(str.data(), static_cast(str.size())); + } +}; + // -- // -- Handle(TCollection_HAsciiString) -> X // -- @@ -180,6 +188,13 @@ template<> struct StringConv { } }; +// std::string -> NCollection_Utf8String +template<> struct StringConv { + static auto to(const std::string& str) { + return NCollection_Utf8String(str.c_str(), static_cast(str.size())); + } +}; + // -- // -- TCollection_AsciiString -> X // -- diff --git a/src/io_dxf/dxf.cpp b/src/io_dxf/dxf.cpp index 730be108..2260ca36 100644 --- a/src/io_dxf/dxf.cpp +++ b/src/io_dxf/dxf.cpp @@ -3,28 +3,138 @@ // This program is released under the BSD license. See the file COPYING for details. // modified 2018 wandererfan -// MAYO: file initially taken from FreeCad/src/Mod/Import/App/dxf.cpp -- commit #1ac35d2 +// MAYO: file taken from FreeCad/src/Mod/Import/App/dxf.cpp -- commit #55292e9 #if defined(_MSC_VER) && !defined(_USE_MATH_DEFINES) //required by windows for M_PI definition # define _USE_MATH_DEFINES #endif +#include +#include +#include #include +#if __cpp_lib_to_chars +# include +#endif +#include #include +#include #include "../base/filepath.h" #include "dxf.h" +#include + namespace { -template -void safe_strcpy(char (&dst)[N1], const char (&src)[N2]) { - strncpy(dst, src, std::min(N1, N2)); -} +class ScopedCLocale { +public: + ScopedCLocale(int category) + : m_category(category), + m_savedLocale(std::setlocale(category, nullptr)) + { + std::setlocale(category, "C"); + } + + ~ScopedCLocale() + { + std::setlocale(m_category, m_savedLocale); + } + +private: + int m_category = 0; + const char* m_savedLocale = nullptr; +}; } // namespace +namespace DxfPrivate { + +template bool isStringToErrorValue(T value) +{ + if constexpr(std::is_same_v) { + return value == std::numeric_limits::max(); + } + else if constexpr(std::is_same_v) { + return value == std::numeric_limits::max(); + } + else if constexpr(std::is_same_v) { + constexpr double dMax = std::numeric_limits::max(); + return std::abs(value - dMax) * 1000000000000. <= std::min(std::abs(value), std::abs(dMax)); + } + else { + return false; + } +} + +template +T stringToNumeric(const std::string& line, StringToErrorMode errorMode) +{ +#if __cpp_lib_to_chars + T value; + auto [ptr, err] = std::from_chars(line.c_str(), line.c_str() + line.size(), value); + if (err == std::errc()) + return value; +#else + try { + if constexpr(std::is_same_v) { + return std::stoi(line); + } + else if constexpr(std::is_same_v) { + return std::stoul(line); + } + else if constexpr(std::is_same_v) { + return std::stod(line); + } + else { + if (errorMode == StringToErrorMode::ReturnErrorValue) + return std::numeric_limits::max(); + else + throw std::runtime_error("Failed to fetch numeric value from line:\n" + line); + } + } catch (...) { +#endif + + if (errorMode == StringToErrorMode::ReturnErrorValue) { + return std::numeric_limits::max(); + } + else { + std::string strTypeName = "?"; + if constexpr(std::is_same_v) + strTypeName = "int"; + else if constexpr(std::is_same_v) + strTypeName = "unsigned"; + else if constexpr(std::is_same_v) + strTypeName = "double"; + + throw std::runtime_error("Failed to fetch " + strTypeName + " value from line:\n" + line); + } + +#ifndef __cpp_lib_to_chars + } +#endif +} + +int stringToInt(const std::string& line, StringToErrorMode errorMode) +{ + return stringToNumeric(line, errorMode); +} + +unsigned stringToUnsigned(const std::string& line, StringToErrorMode errorMode) +{ + return stringToNumeric(line, errorMode); +} + +double stringToDouble(const std::string& line, StringToErrorMode errorMode) +{ + return stringToNumeric(line, errorMode); +} + +} // namespace DxfPrivate + +using namespace DxfPrivate; + Base::Vector3d toVector3d(const double* a) { Base::Vector3d result; @@ -34,27 +144,27 @@ Base::Vector3d toVector3d(const double* a) return result; } -CDxfWrite::CDxfWrite(const char* filepath) : -m_ofs(filepath, std::ios::out), -//TODO: these should probably be parameters in config file -//handles: -//boilerplate 0 - A00 -//used by dxf.cpp A01 - FFFE -//ACAD HANDSEED FFFF +CDxfWrite::CDxfWrite(const char* filepath) + : m_ofs(filepath, std::ios::out), + //TODO: these should probably be parameters in config file + //handles: + //boilerplate 0 - A00 + //used by dxf.cpp A01 - FFFE + //ACAD HANDSEED FFFF -m_handle(0xA00), //room for 2560 handles in boilerplate files -//m_entityHandle(0x300), //don't need special ranges for handles -//m_layerHandle(0x30), -//m_blockHandle(0x210), -//m_blkRecordHandle(0x110), -m_polyOverride(false), -m_layerName("none") + m_handle(0xA00), //room for 2560 handles in boilerplate files + //m_entityHandle(0x300), //don't need special ranges for handles + //m_layerHandle(0x30), + //m_blockHandle(0x210), + //m_blkRecordHandle(0x110), + m_polyOverride(false), + m_layerName("none") { // start the file m_fail = false; m_version = 12; - if(!m_ofs) + if (!m_ofs) m_fail = true; else m_ofs.imbue(std::locale::classic()); @@ -64,7 +174,7 @@ CDxfWrite::~CDxfWrite() { } -void CDxfWrite::init(void) +void CDxfWrite::init() { writeHeaderSection(); makeBlockRecordTableHead(); @@ -72,7 +182,7 @@ void CDxfWrite::init(void) } //! assemble pieces into output file -void CDxfWrite::endRun(void) +void CDxfWrite::endRun() { makeLayerTable(); makeBlockRecordTableBody(); @@ -90,18 +200,9 @@ void CDxfWrite::endRun(void) //*************************** //writeHeaderSection //added by Wandererfan 2018 (wandererfan@gmail.com) for FreeCAD project -void CDxfWrite::writeHeaderSection(void) +void CDxfWrite::writeHeaderSection() { std::stringstream ss; -#if 0 - ss << "FreeCAD v" - << App::Application::Config()["BuildVersionMajor"] - << "." - << App::Application::Config()["BuildVersionMinor"] - << " " - << App::Application::Config()["BuildRevision"]; -#endif - //header & version m_ofs << "999" << std::endl; m_ofs << ss.str() << std::endl; @@ -117,7 +218,7 @@ void CDxfWrite::writeHeaderSection(void) //*************************** //writeClassesSection //added by Wandererfan 2018 (wandererfan@gmail.com) for FreeCAD project -void CDxfWrite::writeClassesSection(void) +void CDxfWrite::writeClassesSection() { if (m_version < 14) { return; @@ -133,7 +234,7 @@ void CDxfWrite::writeClassesSection(void) //*************************** //writeTablesSection //added by Wandererfan 2018 (wandererfan@gmail.com) for FreeCAD project -void CDxfWrite::writeTablesSection(void) +void CDxfWrite::writeTablesSection() { //static tables section head end content std::stringstream ss; @@ -162,7 +263,7 @@ void CDxfWrite::writeTablesSection(void) //*************************** //makeLayerTable //added by Wandererfan 2018 (wandererfan@gmail.com) for FreeCAD project -void CDxfWrite::makeLayerTable(void) +void CDxfWrite::makeLayerTable() { std::string tablehash = getLayerHandle(); m_ssLayer << " 0" << std::endl; @@ -230,63 +331,63 @@ void CDxfWrite::makeLayerTable(void) //*************************** //makeBlockRecordTableHead //added by Wandererfan 2018 (wandererfan@gmail.com) for FreeCAD project -void CDxfWrite::makeBlockRecordTableHead(void) +void CDxfWrite::makeBlockRecordTableHead() { if (m_version < 14) { return; } - std::string tablehash = getBlkRecordHandle(); - m_saveBlockRecordTableHandle = tablehash; - m_ssBlkRecord << " 0" << std::endl; - m_ssBlkRecord << "TABLE" << std::endl; - m_ssBlkRecord << " 2" << std::endl; - m_ssBlkRecord << "BLOCK_RECORD" << std::endl; - m_ssBlkRecord << " 5" << std::endl; - m_ssBlkRecord << tablehash << std::endl; - m_ssBlkRecord << "330" << std::endl; - m_ssBlkRecord << "0" << std::endl; - m_ssBlkRecord << "100" << std::endl; - m_ssBlkRecord << "AcDbSymbolTable" << std::endl; - m_ssBlkRecord << " 70" << std::endl; - m_ssBlkRecord << (m_blockList.size() + 5) << std::endl; - - m_saveModelSpaceHandle = getBlkRecordHandle(); - m_ssBlkRecord << " 0" << std::endl; - m_ssBlkRecord << "BLOCK_RECORD" << std::endl; - m_ssBlkRecord << " 5" << std::endl; - m_ssBlkRecord << m_saveModelSpaceHandle << std::endl; - m_ssBlkRecord << "330" << std::endl; - m_ssBlkRecord << tablehash << std::endl; - m_ssBlkRecord << "100" << std::endl; - m_ssBlkRecord << "AcDbSymbolTableRecord" << std::endl; - m_ssBlkRecord << "100" << std::endl; - m_ssBlkRecord << "AcDbBlockTableRecord" << std::endl; - m_ssBlkRecord << " 2" << std::endl; - m_ssBlkRecord << "*MODEL_SPACE" << std::endl; -// m_ssBlkRecord << " 1" << std::endl; -// m_ssBlkRecord << " " << std::endl; - - m_savePaperSpaceHandle = getBlkRecordHandle(); - m_ssBlkRecord << " 0" << std::endl; - m_ssBlkRecord << "BLOCK_RECORD" << std::endl; - m_ssBlkRecord << " 5" << std::endl; - m_ssBlkRecord << m_savePaperSpaceHandle << std::endl; - m_ssBlkRecord << "330" << std::endl; - m_ssBlkRecord << tablehash << std::endl; - m_ssBlkRecord << "100" << std::endl; - m_ssBlkRecord << "AcDbSymbolTableRecord" << std::endl; - m_ssBlkRecord << "100" << std::endl; - m_ssBlkRecord << "AcDbBlockTableRecord" << std::endl; - m_ssBlkRecord << " 2" << std::endl; - m_ssBlkRecord << "*PAPER_SPACE" << std::endl; -// m_ssBlkRecord << " 1" << std::endl; -// m_ssBlkRecord << " " << std::endl; + std::string tablehash = getBlkRecordHandle(); + m_saveBlockRecordTableHandle = tablehash; + m_ssBlkRecord << " 0" << std::endl; + m_ssBlkRecord << "TABLE" << std::endl; + m_ssBlkRecord << " 2" << std::endl; + m_ssBlkRecord << "BLOCK_RECORD" << std::endl; + m_ssBlkRecord << " 5" << std::endl; + m_ssBlkRecord << tablehash << std::endl; + m_ssBlkRecord << "330" << std::endl; + m_ssBlkRecord << "0" << std::endl; + m_ssBlkRecord << "100" << std::endl; + m_ssBlkRecord << "AcDbSymbolTable" << std::endl; + m_ssBlkRecord << " 70" << std::endl; + m_ssBlkRecord << (m_blockList.size() + 5) << std::endl; + + m_saveModelSpaceHandle = getBlkRecordHandle(); + m_ssBlkRecord << " 0" << std::endl; + m_ssBlkRecord << "BLOCK_RECORD" << std::endl; + m_ssBlkRecord << " 5" << std::endl; + m_ssBlkRecord << m_saveModelSpaceHandle << std::endl; + m_ssBlkRecord << "330" << std::endl; + m_ssBlkRecord << tablehash << std::endl; + m_ssBlkRecord << "100" << std::endl; + m_ssBlkRecord << "AcDbSymbolTableRecord" << std::endl; + m_ssBlkRecord << "100" << std::endl; + m_ssBlkRecord << "AcDbBlockTableRecord" << std::endl; + m_ssBlkRecord << " 2" << std::endl; + m_ssBlkRecord << "*MODEL_SPACE" << std::endl; + // m_ssBlkRecord << " 1" << std::endl; + // m_ssBlkRecord << " " << std::endl; + + m_savePaperSpaceHandle = getBlkRecordHandle(); + m_ssBlkRecord << " 0" << std::endl; + m_ssBlkRecord << "BLOCK_RECORD" << std::endl; + m_ssBlkRecord << " 5" << std::endl; + m_ssBlkRecord << m_savePaperSpaceHandle << std::endl; + m_ssBlkRecord << "330" << std::endl; + m_ssBlkRecord << tablehash << std::endl; + m_ssBlkRecord << "100" << std::endl; + m_ssBlkRecord << "AcDbSymbolTableRecord" << std::endl; + m_ssBlkRecord << "100" << std::endl; + m_ssBlkRecord << "AcDbBlockTableRecord" << std::endl; + m_ssBlkRecord << " 2" << std::endl; + m_ssBlkRecord << "*PAPER_SPACE" << std::endl; + // m_ssBlkRecord << " 1" << std::endl; + // m_ssBlkRecord << " " << std::endl; } - + //*************************** //makeBlockRecordTableBody //added by Wandererfan 2018 (wandererfan@gmail.com) for FreeCAD project -void CDxfWrite::makeBlockRecordTableBody(void) +void CDxfWrite::makeBlockRecordTableBody() { if (m_version < 14) { return; @@ -306,8 +407,8 @@ void CDxfWrite::makeBlockRecordTableBody(void) m_ssBlkRecord << "AcDbBlockTableRecord" << std::endl; m_ssBlkRecord << " 2" << std::endl; m_ssBlkRecord << b << std::endl; -// m_ssBlkRecord << " 70" << std::endl; -// m_ssBlkRecord << " 0" << std::endl; + // m_ssBlkRecord << " 70" << std::endl; + // m_ssBlkRecord << " 0" << std::endl; iBlkRecord++; } } @@ -315,7 +416,7 @@ void CDxfWrite::makeBlockRecordTableBody(void) //*************************** //makeBlockSectionHead //added by Wandererfan 2018 (wandererfan@gmail.com) for FreeCAD project -void CDxfWrite::makeBlockSectionHead(void) +void CDxfWrite::makeBlockSectionHead() { m_ssBlock << " 0" << std::endl; m_ssBlock << "SECTION" << std::endl; @@ -432,8 +533,7 @@ std::string CDxfWrite::getPlateFile(const std::string& fileSpec) std::string line; std::ifstream inFile (fpath); - while (!inFile.eof()) - { + while (!inFile.eof()) { std::getline(inFile,line); if (!inFile.eof()) { outString << line << '\n'; @@ -443,7 +543,7 @@ std::string CDxfWrite::getPlateFile(const std::string& fileSpec) return outString.str(); } -std::string CDxfWrite::getHandle(void) +std::string CDxfWrite::getHandle() { m_handle++; std::stringstream ss; @@ -452,24 +552,44 @@ std::string CDxfWrite::getHandle(void) return ss.str(); } -std::string CDxfWrite::getEntityHandle(void) +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(void) +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(void) +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(void) +std::string CDxfWrite::getBlkRecordHandle() { return getHandle(); + // m_blkRecordHandle++; + // std::stringstream ss; + // ss << std::uppercase << std::hex << std::setfill('0') << std::setw(2); + // ss << m_blkRecordHandle; + // return ss.str(); } void CDxfWrite::addBlockName(std::string b, std::string h) @@ -480,8 +600,8 @@ void CDxfWrite::addBlockName(std::string b, std::string h) void CDxfWrite::setLayerName(std::string s) { - m_layerName = s; - m_layerList.push_back(s); + m_layerName = s; + m_layerList.push_back(s); } void CDxfWrite::writeLine(const double* s, const double* e) @@ -489,9 +609,11 @@ void CDxfWrite::writeLine(const double* s, const double* e) putLine(toVector3d(s),toVector3d(e),m_ssEntity, getEntityHandle(), m_saveModelSpaceHandle); } -void CDxfWrite::putLine(const Base::Vector3d& s, const Base::Vector3d& e, - std::ostringstream& outStream, const std::string& handle, - const std::string& ownerHandle) +void CDxfWrite::putLine(const Base::Vector3d& s, + const Base::Vector3d& e, + std::ostringstream& outStream, + const std::string& handle, + const std::string& ownerHandle) { outStream << " 0" << std::endl; outStream << "LINE" << std::endl; @@ -551,11 +673,11 @@ void CDxfWrite::writeLWPolyLine(const LWPolyDataOut &pd) m_ssEntity << pd.Flag << std::endl; m_ssEntity << " 43" << std::endl; m_ssEntity << "0" << std::endl; //Constant width opt -// m_ssEntity << pd.Width << std::endl; //Constant width opt -// m_ssEntity << " 38" << std::endl; -// m_ssEntity << pd.Elev << std::endl; // Elevation -// m_ssEntity << " 39" << std::endl; -// m_ssEntity << pd.Thick << std::endl; // Thickness + // m_ssEntity << pd.Width << std::endl; //Constant width opt + // m_ssEntity << " 38" << std::endl; + // m_ssEntity << pd.Elev << std::endl; // Elevation + // m_ssEntity << " 39" << std::endl; + // m_ssEntity << pd.Thick << std::endl; // Thickness for (auto& p: pd.Verts) { m_ssEntity << " 10" << std::endl; // Vertices m_ssEntity << p.x << std::endl; @@ -574,12 +696,12 @@ void CDxfWrite::writeLWPolyLine(const LWPolyDataOut &pd) m_ssEntity << " 42" << std::endl; m_ssEntity << b << std::endl; } -// m_ssEntity << "210" << std::endl; //Extrusion dir -// m_ssEntity << pd.Extr.x << std::endl; -// m_ssEntity << "220" << std::endl; -// m_ssEntity << pd.Extr.y << std::endl; -// m_ssEntity << "230" << std::endl; -// m_ssEntity << pd.Extr.z << std::endl; + // m_ssEntity << "210" << std::endl; //Extrusion dir + // m_ssEntity << pd.Extr.x << std::endl; + // m_ssEntity << "220" << std::endl; + // m_ssEntity << pd.Extr.y << std::endl; + // m_ssEntity << "230" << std::endl; + // m_ssEntity << pd.Extr.z << std::endl; } //*************************** @@ -671,7 +793,7 @@ void CDxfWrite::writeArc(const double* s, const double* e, const double* c, bool double start_angle = atan2(ay, ax) * 180/M_PI; double end_angle = atan2(by, bx) * 180/M_PI; double radius = sqrt(ax*ax + ay*ay); - if(!dir){ + if (!dir){ double temp = start_angle; start_angle = end_angle; end_angle = temp; @@ -688,9 +810,9 @@ void CDxfWrite::writeArc(const double* s, const double* e, const double* c, bool } m_ssEntity << " 8" << std::endl; // Group code for layer name m_ssEntity << getLayerName() << std::endl; // Layer number -// m_ssEntity << " 62" << std::endl; -// m_ssEntity << " 0" << std::endl; - if (m_version > 12) { + // m_ssEntity << " 62" << std::endl; + // m_ssEntity << " 0" << std::endl; + if (m_version > 12) { m_ssEntity << "100" << std::endl; m_ssEntity << "AcDbCircle" << std::endl; } @@ -727,7 +849,7 @@ void CDxfWrite::writeCircle(const double* c, double radius) } m_ssEntity << " 8" << std::endl; // Group code for layer name m_ssEntity << getLayerName() << std::endl; // Layer number - if (m_version > 12) { + if (m_version > 12) { m_ssEntity << "100" << std::endl; m_ssEntity << "AcDbCircle" << std::endl; } @@ -735,14 +857,18 @@ void CDxfWrite::writeCircle(const double* c, double radius) m_ssEntity << c[0] << std::endl; // X in WCS coordinates m_ssEntity << " 20" << std::endl; m_ssEntity << c[1] << std::endl; // Y in WCS coordinates -// m_ssEntity << " 30" << std::endl; -// m_ssEntity << c[2] << std::endl; // Z in WCS coordinates + // m_ssEntity << " 30" << std::endl; + // m_ssEntity << c[2] << std::endl; // Z in WCS coordinates m_ssEntity << " 40" << std::endl; // m_ssEntity << radius << std::endl; // Radius } -void CDxfWrite::writeEllipse(const double* c, double major_radius, double minor_radius, - double rotation, double start_angle, double end_angle, +void CDxfWrite::writeEllipse(const double* c, + double major_radius, + double minor_radius, + double rotation, + double start_angle, + double end_angle, bool endIsCW) { double m[3]; @@ -752,7 +878,7 @@ void CDxfWrite::writeEllipse(const double* c, double major_radius, double minor_ double ratio = minor_radius/major_radius; - if(!endIsCW){ //end is NOT CW from start + if (!endIsCW){ //end is NOT CW from start double temp = start_angle; start_angle = end_angle; end_angle = temp; @@ -769,7 +895,7 @@ void CDxfWrite::writeEllipse(const double* c, double major_radius, double minor_ } m_ssEntity << " 8" << std::endl; // Group code for layer name m_ssEntity << getLayerName() << std::endl; // Layer number - if (m_version > 12) { + if (m_version > 12) { m_ssEntity << "100" << std::endl; m_ssEntity << "AcDbEllipse" << std::endl; } @@ -787,12 +913,12 @@ void CDxfWrite::writeEllipse(const double* c, double major_radius, double minor_ m_ssEntity << m[2] << std::endl; // Major Z m_ssEntity << " 40" << std::endl; // m_ssEntity << ratio << std::endl; // Ratio -// m_ssEntity << "210" << std::endl; //extrusion dir?? -// m_ssEntity << "0" << std::endl; -// m_ssEntity << "220" << std::endl; -// m_ssEntity << "0" << std::endl; -// m_ssEntity << "230" << std::endl; -// m_ssEntity << "1" << std::endl; + // m_ssEntity << "210" << std::endl; //extrusion dir?? + // m_ssEntity << "0" << std::endl; + // m_ssEntity << "220" << std::endl; + // m_ssEntity << "0" << std::endl; + // m_ssEntity << "230" << std::endl; + // m_ssEntity << "1" << std::endl; m_ssEntity << " 41" << std::endl; m_ssEntity << start_angle << std::endl; // Start angle (radians [0..2pi]) m_ssEntity << " 42" << std::endl; @@ -838,18 +964,18 @@ void CDxfWrite::writeSpline(const SplineDataOut &sd) m_ssEntity << " 74" << std::endl; m_ssEntity << 0 << std::endl; -// m_ssEntity << " 12" << std::endl; -// m_ssEntity << sd.starttan.x << std::endl; -// m_ssEntity << " 22" << std::endl; -// m_ssEntity << sd.starttan.y << std::endl; -// m_ssEntity << " 32" << std::endl; -// m_ssEntity << sd.starttan.z << std::endl; -// m_ssEntity << " 13" << std::endl; -// m_ssEntity << sd.endtan.x << std::endl; -// m_ssEntity << " 23" << std::endl; -// m_ssEntity << sd.endtan.y << std::endl; -// m_ssEntity << " 33" << std::endl; -// m_ssEntity << sd.endtan.z << std::endl; + // m_ssEntity << " 12" << std::endl; + // m_ssEntity << sd.starttan.x << std::endl; + // m_ssEntity << " 22" << std::endl; + // m_ssEntity << sd.starttan.y << std::endl; + // m_ssEntity << " 32" << std::endl; + // m_ssEntity << sd.starttan.z << std::endl; + // m_ssEntity << " 13" << std::endl; + // m_ssEntity << sd.endtan.x << std::endl; + // m_ssEntity << " 23" << std::endl; + // m_ssEntity << sd.endtan.y << std::endl; + // m_ssEntity << " 33" << std::endl; + // m_ssEntity << sd.endtan.z << std::endl; for (auto& k: sd.knot) { m_ssEntity << " 40" << std::endl; @@ -910,21 +1036,33 @@ void CDxfWrite::writeVertex(double x, double y, double z) m_ssEntity << 0 << std::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); +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 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; @@ -944,8 +1082,8 @@ void CDxfWrite::putText(const char* text, const Base::Vector3d& location1, const outStream << "100" << std::endl; outStream << "AcDbText" << std::endl; } -// outStream << " 39" << std::endl; -// outStream << 0 << std::endl; //thickness + // outStream << " 39" << std::endl; + // outStream << 0 << std::endl; //thickness outStream << " 10" << std::endl; //first alignment point outStream << location1.x << std::endl; outStream << " 20" << std::endl; @@ -956,33 +1094,33 @@ void CDxfWrite::putText(const char* text, const Base::Vector3d& location1, const outStream << height << std::endl; outStream << " 1" << std::endl; outStream << text << std::endl; -// outStream << " 50" << std::endl; -// outStream << 0 << std::endl; //rotation -// outStream << " 41" << std::endl; -// outStream << 1 << std::endl; -// outStream << " 51" << std::endl; -// outStream << 0 << std::endl; + // outStream << " 50" << std::endl; + // outStream << 0 << std::endl; //rotation + // outStream << " 41" << std::endl; + // outStream << 1 << std::endl; + // outStream << " 51" << std::endl; + // outStream << 0 << std::endl; outStream << " 7" << std::endl; outStream << "STANDARD" << std::endl; //style -// outStream << " 71" << std::endl; //default -// outStream << "0" << std::endl; + // outStream << " 71" << std::endl; //default + // outStream << "0" << std::endl; outStream << " 72" << std::endl; outStream << horizJust << std::endl; -//// outStream << " 73" << std::endl; -//// outStream << "0" << std::endl; + //// outStream << " 73" << std::endl; + //// outStream << "0" << std::endl; outStream << " 11" << std::endl; //second alignment point outStream << location2.x << std::endl; outStream << " 21" << std::endl; outStream << location2.y << std::endl; outStream << " 31" << std::endl; outStream << location2.z << std::endl; -// outStream << "210" << std::endl; -// outStream << "0" << std::endl; -// outStream << "220" << std::endl; -// outStream << "0" << std::endl; -// outStream << "230" << std::endl; -// outStream << "1" << std::endl; + // outStream << "210" << std::endl; + // outStream << "0" << std::endl; + // outStream << "220" << std::endl; + // outStream << "0" << std::endl; + // outStream << "230" << std::endl; + // outStream << "1" << std::endl; if (m_version > 12) { outStream << "100" << std::endl; outStream << "AcDbText" << std::endl; @@ -990,8 +1128,11 @@ void CDxfWrite::putText(const char* text, const Base::Vector3d& location1, const } -void CDxfWrite::putArrow(const Base::Vector3d& arrowPos, const Base::Vector3d& barb1Pos, const Base::Vector3d& barb2Pos, - std::ostringstream& outStream, const std::string& handle, +void CDxfWrite::putArrow(const Base::Vector3d& arrowPos, + const Base::Vector3d& barb1Pos, + const Base::Vector3d& barb2Pos, + std::ostringstream& outStream, + const std::string& handle, const std::string& ownerHandle) { outStream << " 0" << std::endl; @@ -1044,9 +1185,12 @@ void CDxfWrite::putArrow(const Base::Vector3d& arrowPos, const Base::Vector3d& b #define ALIGNED 0 #define HORIZONTAL 1 #define VERTICAL 2 -void CDxfWrite::writeLinearDim(const double* textMidPoint, const double* lineDefPoint, - const double* extLine1, const double* extLine2, - const char* dimText, int type) +void CDxfWrite::writeLinearDim(const double* textMidPoint, + const double* lineDefPoint, + const double* extLine1, + const double* extLine2, + const char* dimText, + int type) { m_ssEntity << " 0" << std::endl; m_ssEntity << "DIMENSION" << std::endl; @@ -1083,17 +1227,17 @@ void CDxfWrite::writeLinearDim(const double* textMidPoint, const double* lineDef m_ssEntity << 1 << std::endl; // dimType1 = Aligned } if ( (type == HORIZONTAL) || - (type == VERTICAL) ) { + (type == VERTICAL) ) { m_ssEntity << " 70" << std::endl; m_ssEntity << 32 << std::endl; // dimType0 = Aligned + 32 (bit for unique block)? } -// m_ssEntity << " 71" << std::endl; // not R12 -// m_ssEntity << 1 << std::endl; // attachPoint ??1 = topleft + // m_ssEntity << " 71" << std::endl; // not R12 + // m_ssEntity << 1 << std::endl; // attachPoint ??1 = topleft m_ssEntity << " 1" << std::endl; m_ssEntity << dimText << std::endl; m_ssEntity << " 3" << std::endl; m_ssEntity << "STANDARD" << std::endl; //style -//linear dims + //linear dims if (m_version > 12) { m_ssEntity << "100" << std::endl; m_ssEntity << "AcDbAlignedDimension" << std::endl; @@ -1115,27 +1259,27 @@ void CDxfWrite::writeLinearDim(const double* textMidPoint, const double* lineDef m_ssEntity << " 50" << std::endl; m_ssEntity << "90" << std::endl; } - if ( (type == HORIZONTAL) || - (type == VERTICAL) ) { + if ( (type == HORIZONTAL) || (type == VERTICAL) ) { m_ssEntity << "100" << std::endl; m_ssEntity << "AcDbRotatedDimension" << std::endl; } } writeDimBlockPreamble(); - writeLinearDimBlock(textMidPoint,lineDefPoint, - extLine1, extLine2, - dimText, type); + 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) +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" << std::endl; m_ssEntity << "DIMENSION" << std::endl; @@ -1172,14 +1316,14 @@ void CDxfWrite::writeAngularDim(const double* textMidPoint, const double* lineDe m_ssEntity << " 70" << std::endl; m_ssEntity << 2 << std::endl; // dimType 2 = Angular 5 = Angular 3 point - // +32 for block?? (not R12) -// m_ssEntity << " 71" << std::endl; // not R12? not required? -// m_ssEntity << 5 << std::endl; // attachPoint 5 = middle + // +32 for block?? (not R12) + // m_ssEntity << " 71" << std::endl; // not R12? not required? + // m_ssEntity << 5 << std::endl; // attachPoint 5 = middle m_ssEntity << " 1" << std::endl; m_ssEntity << dimText << std::endl; m_ssEntity << " 3" << std::endl; m_ssEntity << "STANDARD" << std::endl; //style -//angular dims + //angular dims if (m_version > 12) { m_ssEntity << "100" << std::endl; m_ssEntity << "AcDb2LineAngularDimension" << std::endl; @@ -1212,9 +1356,12 @@ void CDxfWrite::writeAngularDim(const double* textMidPoint, const double* lineDe m_ssEntity << " 36" << std::endl; m_ssEntity << lineDefPoint[2] << std::endl; writeDimBlockPreamble(); - writeAngularDimBlock(textMidPoint, lineDefPoint, - startExt1, endExt1, - startExt2, endExt2, + writeAngularDimBlock(textMidPoint, + lineDefPoint, + startExt1, + endExt1, + startExt2, + endExt2, dimText); writeBlockTrailer(); } @@ -1222,9 +1369,10 @@ void CDxfWrite::writeAngularDim(const double* textMidPoint, const double* lineDe //*************************** //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) +void CDxfWrite::writeRadialDim(const double* centerPoint, + const double* textMidPoint, + const double* arcPoint, + const char* dimText) { m_ssEntity << " 0" << std::endl; m_ssEntity << "DIMENSION" << std::endl; @@ -1258,13 +1406,13 @@ void CDxfWrite::writeRadialDim(const double* centerPoint, const double* textMidP m_ssEntity << textMidPoint[2] << std::endl; m_ssEntity << " 70" << std::endl; m_ssEntity << 4 << std::endl; // dimType 4 = Radius -// m_ssEntity << " 71" << std::endl; // not R12 -// m_ssEntity << 1 << std::endl; // attachPoint 5 = middle center + // m_ssEntity << " 71" << std::endl; // not R12 + // m_ssEntity << 1 << std::endl; // attachPoint 5 = middle center m_ssEntity << " 1" << std::endl; m_ssEntity << dimText << std::endl; m_ssEntity << " 3" << std::endl; m_ssEntity << "STANDARD" << std::endl; //style -//radial dims + //radial dims if (m_version > 12) { m_ssEntity << "100" << std::endl; m_ssEntity << "AcDbRadialDimension" << std::endl; @@ -1286,9 +1434,10 @@ void CDxfWrite::writeRadialDim(const double* centerPoint, const double* textMidP //*************************** //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) +void CDxfWrite::writeDiametricDim(const double* textMidPoint, + const double* arcPoint1, + const double* arcPoint2, + const char* dimText) { m_ssEntity << " 0" << std::endl; m_ssEntity << "DIMENSION" << std::endl; @@ -1322,13 +1471,13 @@ void CDxfWrite::writeDiametricDim(const double* textMidPoint, m_ssEntity << textMidPoint[2] << std::endl; m_ssEntity << " 70" << std::endl; m_ssEntity << 3 << std::endl; // dimType 3 = Diameter -// m_ssEntity << " 71" << std::endl; // not R12 -// m_ssEntity << 5 << std::endl; // attachPoint 5 = middle center + // m_ssEntity << " 71" << std::endl; // not R12 + // m_ssEntity << 5 << std::endl; // attachPoint 5 = middle center m_ssEntity << " 1" << std::endl; m_ssEntity << dimText << std::endl; m_ssEntity << " 3" << std::endl; m_ssEntity << "STANDARD" << std::endl; //style -//diametric dims + //diametric dims if (m_version > 12) { m_ssEntity << "100" << std::endl; m_ssEntity << "AcDbDiametricDimension" << std::endl; @@ -1350,7 +1499,7 @@ void CDxfWrite::writeDiametricDim(const double* textMidPoint, //*************************** //writeDimBlockPreamble //added by Wandererfan 2018 (wandererfan@gmail.com) for FreeCAD project -void CDxfWrite::writeDimBlockPreamble(void) +void CDxfWrite::writeDimBlockPreamble() { if (m_version > 12) { std::string blockName("*"); @@ -1395,7 +1544,7 @@ void CDxfWrite::writeDimBlockPreamble(void) //*************************** //writeBlockTrailer //added by Wandererfan 2018 (wandererfan@gmail.com) for FreeCAD project -void CDxfWrite::writeBlockTrailer(void) +void CDxfWrite::writeBlockTrailer() { m_ssBlock << " 0" << std::endl; m_ssBlock << "ENDBLK" << std::endl; @@ -1407,8 +1556,8 @@ void CDxfWrite::writeBlockTrailer(void) m_ssBlock << "100" << std::endl; m_ssBlock << "AcDbEntity" << std::endl; } -// m_ssBlock << " 67" << std::endl; -// m_ssBlock << "1" << std::endl; + // m_ssBlock << " 67" << std::endl; + // m_ssBlock << "1" << std::endl; m_ssBlock << " 8" << std::endl; m_ssBlock << getLayerName() << std::endl; if (m_version > 12) { @@ -1420,9 +1569,12 @@ void CDxfWrite::writeBlockTrailer(void) //*************************** //writeLinearDimBlock //added by Wandererfan 2018 (wandererfan@gmail.com) for FreeCAD project -void CDxfWrite::writeLinearDimBlock(const double* textMidPoint, const double* dimLine, - const double* e1Start, const double* e2Start, - const char* dimText, int type) +void CDxfWrite::writeLinearDimBlock(const double* textMidPoint, + const double* dimLine, + const double* e1Start, + const double* e2Start, + const char* dimText, + int type) { Base::Vector3d e1S(e1Start[0],e1Start[1],e1Start[2]); Base::Vector3d e2S(e2Start[0],e2Start[1],e2Start[2]); @@ -1436,7 +1588,8 @@ void CDxfWrite::writeLinearDimBlock(const double* textMidPoint, const double* di angle = angle * 180.0 / M_PI; if (type == ALIGNED) { //NOP - } else if (type == HORIZONTAL) { + } + else if (type == HORIZONTAL) { double x = e1Start[0]; double y = dimLine[1]; e1E = Base::Vector3d(x, y, 0.0); @@ -1451,7 +1604,8 @@ void CDxfWrite::writeLinearDimBlock(const double* textMidPoint, const double* di para = Base::Vector3d(-1, 0, 0); //left } angle = 0; - } else if (type == VERTICAL) { + } + else if (type == VERTICAL) { double x = dimLine[0]; double y = e1Start[1]; e1E = Base::Vector3d(x, y, 0.0); @@ -1471,44 +1625,45 @@ void CDxfWrite::writeLinearDimBlock(const double* textMidPoint, const double* di 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(e2S, e2E, m_ssBlock, getBlockHandle(), m_saveBlkRecordHandle); - putLine(e1S, e1E, - m_ssBlock, getBlockHandle(), - m_saveBlkRecordHandle); + putLine(e1S, e1E, m_ssBlock, getBlockHandle(), m_saveBlkRecordHandle); - putLine(e1E, e2E, - m_ssBlock, getBlockHandle(), - m_saveBlkRecordHandle); + putLine(e1E, e2E, m_ssBlock, getBlockHandle(), m_saveBlkRecordHandle); - putText(dimText,toVector3d(textMidPoint), toVector3d(dimLine),3.5,1, - m_ssBlock,getBlockHandle(),m_saveBlkRecordHandle); + putText(dimText, + toVector3d(textMidPoint), + toVector3d(dimLine), + 3.5, + 1, + m_ssBlock, + getBlockHandle(), + m_saveBlkRecordHandle); perp.Normalize(); para.Normalize(); Base::Vector3d arrowStart = e1E; Base::Vector3d barb1 = arrowStart + perp*arrowWidth - para*arrowLen; Base::Vector3d barb2 = arrowStart - perp*arrowWidth - para*arrowLen; - putArrow(arrowStart, barb1, barb2, - m_ssBlock,getBlockHandle(),m_saveBlkRecordHandle); + 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); + 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) +void CDxfWrite::writeAngularDimBlock(const double* textMidPoint, + const double* lineDefPoint, + const double* startExt1, + const double* endExt1, + const double* startExt2, + const double* endExt2, + const char* dimText) { Base::Vector3d e1S(startExt1[0],startExt1[1],startExt1[2]); //apex Base::Vector3d e2S(startExt2[0],startExt2[1],startExt2[2]); @@ -1522,10 +1677,10 @@ void CDxfWrite::writeAngularDimBlock(const double* textMidPoint, const double* l double span = fabs(endAngle - startAngle); double offset = span * 0.10; if (startAngle < 0) { - startAngle += 2.0 * M_PI; + startAngle += 2.0 * M_PI; } if (endAngle < 0) { - endAngle += 2.0 * M_PI; + endAngle += 2.0 * M_PI; } Base::Vector3d startOff(cos(startAngle + offset),sin(startAngle + offset),0.0); Base::Vector3d endOff(cos(endAngle - offset),sin(endAngle - offset),0.0); @@ -1547,8 +1702,8 @@ void CDxfWrite::writeAngularDimBlock(const double* textMidPoint, const double* l } m_ssBlock << " 8" << std::endl; m_ssBlock << "0" << std::endl; -// m_ssBlock << " 62" << std::endl; -// m_ssBlock << " 0" << std::endl; + // m_ssBlock << " 62" << std::endl; + // m_ssBlock << " 0" << std::endl; if (m_version > 12) { m_ssBlock << "100" << std::endl; m_ssBlock << "AcDbCircle" << std::endl; @@ -1570,8 +1725,14 @@ void CDxfWrite::writeAngularDimBlock(const double* textMidPoint, const double* l m_ssBlock << " 51" << std::endl; m_ssBlock << endAngle << std::endl; //end angle - putText(dimText,toVector3d(textMidPoint), toVector3d(textMidPoint),3.5,1, - m_ssBlock,getBlockHandle(),m_saveBlkRecordHandle); + putText(dimText, + toVector3d(textMidPoint), + toVector3d(textMidPoint), + 3.5, + 1, + m_ssBlock, + getBlockHandle(), + m_saveBlkRecordHandle); e1.Normalize(); e2.Normalize(); @@ -1591,28 +1752,36 @@ void CDxfWrite::writeAngularDimBlock(const double* textMidPoint, const double* l 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); + 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); + 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(), +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); + putText(dimText, + toVector3d(textMidPoint), + toVector3d(textMidPoint), + 3.5, + 1, + m_ssBlock, + getBlockHandle(), + m_saveBlkRecordHandle); Base::Vector3d c(centerPoint[0],centerPoint[1],centerPoint[2]); Base::Vector3d a(arcPoint[0],arcPoint[1],arcPoint[2]); @@ -1625,23 +1794,31 @@ void CDxfWrite::writeRadialDimBlock(const double* centerPoint, const double* tex 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); + 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(), +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); + putText(dimText, + toVector3d(textMidPoint), + toVector3d(textMidPoint), + 3.5, + 1, + m_ssBlock, + getBlockHandle(), + m_saveBlkRecordHandle); Base::Vector3d a1(arcPoint1[0],arcPoint1[1],arcPoint1[2]); Base::Vector3d a2(arcPoint2[0],arcPoint2[1],arcPoint2[2]); @@ -1654,21 +1831,18 @@ void CDxfWrite::writeDiametricDimBlock(const double* textMidPoint, 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); + 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); - + putArrow(arrowStart, barb1, barb2, m_ssBlock, getBlockHandle(), m_saveBlkRecordHandle); } //*************************** //writeBlocksSection //added by Wandererfan 2018 (wandererfan@gmail.com) for FreeCAD project -void CDxfWrite::writeBlocksSection(void) +void CDxfWrite::writeBlocksSection() { if (m_version < 14) { std::stringstream ss; @@ -1687,7 +1861,7 @@ void CDxfWrite::writeBlocksSection(void) //*************************** //writeEntitiesSection //added by Wandererfan 2018 (wandererfan@gmail.com) for FreeCAD project -void CDxfWrite::writeEntitiesSection(void) +void CDxfWrite::writeEntitiesSection() { std::stringstream ss; ss << "entities" << m_version << ".rub"; @@ -1705,7 +1879,7 @@ void CDxfWrite::writeEntitiesSection(void) //*************************** //writeObjectsSection //added by Wandererfan 2018 (wandererfan@gmail.com) for FreeCAD project -void CDxfWrite::writeObjectsSection(void) +void CDxfWrite::writeObjectsSection() { if (m_version < 14) { return; @@ -1720,17 +1894,15 @@ CDxfRead::CDxfRead(const char* filepath) : m_ifs(filepath) { // start the file - memset( m_str, '\0', sizeof(m_str) ); - memset( m_unused_line, '\0', sizeof(m_unused_line) ); m_fail = false; - m_aci = 0; + m_ColorIndex = 0; m_eUnits = eMillimeters; m_measurement_inch = false; - safe_strcpy(m_layer_name, "0"); // Default layer name - memset( m_section_name, '\0', sizeof(m_section_name) ); - memset( m_block_name, '\0', sizeof(m_block_name) ); + m_layer_name = "0"; // Default layer name m_ignore_errors = true; + m_version = RUnknown; + if (!m_ifs) m_fail = true; else @@ -1741,136 +1913,130 @@ CDxfRead::~CDxfRead() { } -double CDxfRead::mm( double value ) const +double CDxfRead::mm(double value) const { - if(m_measurement_inch) - { + // re #6461 + // this if handles situation of malformed DXF file where + // MEASUREMENT specifies English units, but + // INSUNITS specifies millimeters or is not specified + //(millimeters is our default) + if (m_measurement_inch && (m_eUnits == eMillimeters)) { value *= 25.4; } - switch(m_eUnits) - { - case eUnspecified: return(value); // We don't know any better. - case eInches: return(value * 25.4); - case eFeet: return(value * 25.4 * 12); - case eMiles: return(value * 1609344.0); - case eMillimeters: return(value); - case eCentimeters: return(value * 10.0); - case eMeters: return(value * 1000.0); - case eKilometers: return(value * 1000000.0); - case eMicroinches: return(value * 25.4 / 1000.0); - case eMils: return(value * 25.4 / 1000.0); - case eYards: return(value * 3 * 12 * 25.4); - case eAngstroms: return(value * 0.0000001); - case eNanometers: return(value * 0.000001); - case eMicrons: return(value * 0.001); - case eDecimeters: return(value * 100.0); - case eDekameters: return(value * 10000.0); - case eHectometers: return(value * 100000.0); - case eGigameters: return(value * 1000000000000.0); - case eAstronomicalUnits: return(value * 149597870690000.0); - case eLightYears: return(value * 9454254955500000000.0); - case eParsecs: return(value * 30856774879000000000.0); - default: return(value); // We don't know any better. + switch (m_eUnits) { + case eUnspecified: + return (value * 1.0); // We don't know any better. + case eInches: + return (value * 25.4); + case eFeet: + return (value * 25.4 * 12); + case eMiles: + return (value * 1609344.0); + case eMillimeters: + return (value * 1.0); + case eCentimeters: + return (value * 10.0); + case eMeters: + return (value * 1000.0); + case eKilometers: + return (value * 1000000.0); + case eMicroinches: + return (value * 25.4 / 1000.0); + case eMils: + return (value * 25.4 / 1000.0); + case eYards: + return (value * 3 * 12 * 25.4); + case eAngstroms: + return (value * 0.0000001); + case eNanometers: + return (value * 0.000001); + case eMicrons: + return (value * 0.001); + case eDecimeters: + return (value * 100.0); + case eDekameters: + return (value * 10000.0); + case eHectometers: + return (value * 100000.0); + case eGigameters: + return (value * 1000000000000.0); + case eAstronomicalUnits: + return (value * 149597870690000.0); + case eLightYears: + return (value * 9454254955500000000.0); + case eParsecs: + return (value * 30856774879000000000.0); + default: + return (value * 1.0); // We don't know any better. } // End switch } // End mm() method +const Dxf_STYLE* CDxfRead::findStyle(const std::string& name) const +{ + if (name.empty()) + return nullptr; + + auto itFound = m_mapStyle.find(name); + return itFound != m_mapStyle.cend() ? &itFound->second : nullptr; +} bool CDxfRead::ReadLine() { - double s[3] = {0, 0, 0}; - double e[3] = {0, 0, 0}; + DxfCoords s = {}; + DxfCoords e = {}; bool hidden = false; - while(!m_ifs.eof()) - { + while (!m_ifs.eof()) { get_line(); - int n; - - if(sscanf(m_str, "%d", &n) != 1) - { + const int n = stringToInt(m_str, StringToErrorMode::ReturnErrorValue); + if (n == 0) { + // next item found, so finish with line + ResolveColorIndex(); + OnReadLine(s, e, hidden); + return true; + } + else if (isStringToErrorValue(n)) { this->ReportError_readInteger("DXF::ReadLine()"); return false; } - std::istringstream ss; - ss.imbue(std::locale("C")); - switch(n){ - case 0: - // next item found, so finish with line - DerefACI(); - OnReadLine(s, e, hidden); - hidden = false; - return true; - - case 8: // Layer name follows - get_line(); - safe_strcpy(m_layer_name, m_str); - break; - - case 6: // line style name follows - get_line(); - if(m_str[0] == 'h' || m_str[0] == 'H')hidden = true; - break; - - case 10: - // start x - get_line(); - ss.str(m_str); ss >> s[0]; s[0] = mm(s[0]); if(ss.fail()) return false; - break; - case 20: - // start y - get_line(); - ss.str(m_str); ss >> s[1]; s[1] = mm(s[1]); if(ss.fail()) return false; - break; - case 30: - // start z - get_line(); - ss.str(m_str); ss >> s[2]; s[2] = mm(s[2]); if(ss.fail()) return false; - break; - case 11: - // end x - get_line(); - ss.str(m_str); ss >> e[0]; e[0] = mm(e[0]); if(ss.fail()) return false; - break; - case 21: - // end y - get_line(); - ss.str(m_str); ss >> e[1]; e[1] = mm(e[1]); if(ss.fail()) return false; - break; - case 31: - // end z - get_line(); - ss.str(m_str); ss >> e[2]; e[2] = mm(e[2]); if(ss.fail()) return false; - break; - case 62: - // color index - get_line(); - ss.str(m_str); ss >> m_aci; if(ss.fail()) return false; - break; - - case 100: - case 39: - case 210: - case 220: - case 230: - // skip the next line - get_line(); - break; - default: - // skip the next line - get_line(); - break; + get_line(); + switch (n) { + case 6: // line style name follows + if (!m_str.empty() && (m_str[0] == 'h' || m_str[0] == 'H')) { + hidden = true; + } + break; + case 10: case 20: case 30: + // start coords + HandleCoordCode(n, &s); + break; + case 11: case 21: case 31: + // end coords + HandleCoordCode<11, 21, 31>(n, &e); + break; + case 100: + case 39: + case 210: + case 220: + case 230: + // skip the next line + break; + default: + HandleCommonGroupCode(n); + break; } } try { - DerefACI(); + ResolveColorIndex(); OnReadLine(s, e, false); } - catch(...) - { - if (! IgnoreErrors()) throw; // Re-throw the exception. + catch (...) { + if (!IgnoreErrors()) { + throw; // Re-throw the exception. + } } return false; @@ -1878,78 +2044,52 @@ bool CDxfRead::ReadLine() bool CDxfRead::ReadPoint() { - double s[3] = {0, 0, 0}; + DxfCoords s = {}; - while(!m_ifs.eof()) - { + while (!m_ifs.eof()) { get_line(); - int n; - - if(sscanf(m_str, "%d", &n) != 1) - { + const int n = stringToInt(m_str, StringToErrorMode::ReturnErrorValue); + if (n == 0) { + // next item found, so finish with line + ResolveColorIndex(); + OnReadPoint(s); + return true; + } + else if (isStringToErrorValue(n)) { this->ReportError_readInteger("DXF::ReadPoint()"); return false; } - std::istringstream ss; - ss.imbue(std::locale("C")); - switch(n){ - case 0: - // next item found, so finish with line - DerefACI(); - OnReadPoint(s); - return true; - - case 8: // Layer name follows - get_line(); - safe_strcpy(m_layer_name, m_str); - break; - - case 10: - // start x - get_line(); - ss.str(m_str); ss >> s[0]; s[0] = mm(s[0]); if(ss.fail()) return false; - break; - case 20: - // start y - get_line(); - ss.str(m_str); ss >> s[1]; s[1] = mm(s[1]); if(ss.fail()) return false; - break; - case 30: - // start z - get_line(); - ss.str(m_str); ss >> s[2]; s[2] = mm(s[2]); if(ss.fail()) return false; - break; - - case 62: - // color index - get_line(); - ss.str(m_str); ss >> m_aci; if(ss.fail()) return false; - break; + get_line(); + switch (n){ + case 10: + case 20: + case 30: + // start coords + HandleCoordCode(n, &s); + break; - case 100: - case 39: - case 210: - case 220: - case 230: - // skip the next line - get_line(); - break; - default: - // skip the next line - get_line(); - break; + case 100: + case 39: + case 210: + case 220: + case 230: + // skip the next line + break; + default: + HandleCommonGroupCode(n); + break; } - } try { - DerefACI(); + ResolveColorIndex(); OnReadPoint(s); } - catch(...) - { - if (! IgnoreErrors()) throw; // Re-throw the exception. + catch (...) { + if (!IgnoreErrors()) { + throw; // Re-throw the exception. + } } return false; @@ -1960,98 +2100,70 @@ bool CDxfRead::ReadArc() double start_angle = 0.0;// in degrees double end_angle = 0.0; double radius = 0.0; - double c[3] = {0,0,0}; // centre + DxfCoords c = {}; // centre double z_extrusion_dir = 1.0; bool hidden = false; - while(!m_ifs.eof()) - { + while (!m_ifs.eof()) { get_line(); - int n; - if(sscanf(m_str, "%d", &n) != 1) - { + const int n = stringToInt(m_str, StringToErrorMode::ReturnErrorValue); + if (n == 0) { + // next item found, so finish with arc + ResolveColorIndex(); + OnReadArc(start_angle, end_angle, radius, c, z_extrusion_dir, hidden); + hidden = false; + return true; + } + else if (isStringToErrorValue(n)) { this->ReportError_readInteger("DXF::ReadArc()"); return false; } - std::istringstream ss; - ss.imbue(std::locale("C")); - switch(n){ - case 0: - // next item found, so finish with arc - DerefACI(); - OnReadArc(start_angle, end_angle, radius, c,z_extrusion_dir, hidden); - hidden = false; - return true; - - case 8: // Layer name follows - get_line(); - safe_strcpy(m_layer_name, m_str); - break; - - case 6: // line style name follows - get_line(); - if(m_str[0] == 'h' || m_str[0] == 'H')hidden = true; - break; - - case 10: - // centre x - get_line(); - ss.str(m_str); ss >> c[0]; c[0] = mm(c[0]); if(ss.fail()) return false; - break; - case 20: - // centre y - get_line(); - ss.str(m_str); ss >> c[1]; c[1] = mm(c[1]); if(ss.fail()) return false; - break; - case 30: - // centre z - get_line(); - ss.str(m_str); ss >> c[2]; c[2] = mm(c[2]); if(ss.fail()) return false; - break; - case 40: - // radius - get_line(); - ss.str(m_str); ss >> radius; radius = mm(radius); if(ss.fail()) return false; - break; - case 50: - // start angle - get_line(); - ss.str(m_str); ss >> start_angle; if(ss.fail()) return false; - break; - case 51: - // end angle - get_line(); - ss.str(m_str); ss >> end_angle; if(ss.fail()) return false; - break; - case 62: - // color index - get_line(); - ss.str(m_str); ss >> m_aci; if(ss.fail()) return false; - break; - + get_line(); + switch (n){ + case 6: // line style name follows + if (!m_str.empty() && (m_str[0] == 'h' || m_str[0] == 'H')) { + hidden = true; + } + break; + case 10: + case 20: + case 30: + // centre coords + HandleCoordCode(n, &c); + break; + case 40: + // radius + radius = mm(stringToDouble(m_str)); + break; + case 50: + // start angle + start_angle = mm(stringToDouble(m_str)); + break; + case 51: + // end angle + end_angle = mm(stringToDouble(m_str)); + break; - case 100: - case 39: - case 210: - case 220: - // skip the next line - get_line(); - break; - case 230: - //Z extrusion direction for arc - get_line(); - ss.str(m_str); ss >> z_extrusion_dir; if(ss.fail()) return false; - break; + case 100: + case 39: + case 210: + case 220: + // skip the next line + break; + case 230: + //Z extrusion direction for arc + z_extrusion_dir = mm(stringToDouble(m_str)); + break; - default: - // skip the next line - get_line(); - break; + default: + HandleCommonGroupCode(n); + break; } } - DerefACI(); + + ResolveColorIndex(); OnReadArc(start_angle, end_angle, radius, c, z_extrusion_dir, false); return false; } @@ -2059,180 +2171,123 @@ bool CDxfRead::ReadArc() bool CDxfRead::ReadSpline() { struct SplineData sd; - sd.norm[0] = 0; - sd.norm[1] = 0; - sd.norm[2] = 1; + sd.norm = {0., 0., 1.}; sd.degree = 0; sd.knots = 0; sd.flag = 0; sd.control_points = 0; sd.fit_points = 0; - double temp_double; - - while(!m_ifs.eof()) - { + while (!m_ifs.eof()) { get_line(); - int n; - if(sscanf(m_str, "%d", &n) != 1) - { + const int n = stringToInt(m_str, StringToErrorMode::ReturnErrorValue); + if (n == 0) { + // next item found, so finish with Spline + ResolveColorIndex(); + OnReadSpline(sd); + return true; + } + else if (isStringToErrorValue(n)) { this->ReportError_readInteger("DXF::ReadSpline()"); return false; } - std::istringstream ss; - ss.imbue(std::locale("C")); - switch(n){ - case 0: - // next item found, so finish with Spline - DerefACI(); - OnReadSpline(sd); - return true; - case 8: // Layer name follows - get_line(); - safe_strcpy(m_layer_name, m_str); - break; - case 62: - // color index - get_line(); - ss.str(m_str); ss >> m_aci; if(ss.fail()) return false; - break; - case 210: - // normal x - get_line(); - ss.str(m_str); ss >> sd.norm[0]; if(ss.fail()) return false; - break; - case 220: - // normal y - get_line(); - ss.str(m_str); ss >> sd.norm[1]; if(ss.fail()) return false; - break; - case 230: - // normal z - get_line(); - ss.str(m_str); ss >> sd.norm[2]; if(ss.fail()) return false; - break; - case 70: - // flag - get_line(); - ss.str(m_str); ss >> sd.flag; if(ss.fail()) return false; - break; - case 71: - // degree - get_line(); - ss.str(m_str); ss >> sd.degree; if(ss.fail()) return false; - break; - case 72: - // knots - get_line(); - ss.str(m_str); ss >> sd.knots; if(ss.fail()) return false; - break; - case 73: - // control points - get_line(); - ss.str(m_str); ss >> sd.control_points; if(ss.fail()) return false; - break; - case 74: - // fit points - get_line(); - ss.str(m_str); ss >> sd.fit_points; if(ss.fail()) return false; - break; - case 12: - // starttan x - get_line(); - ss.str(m_str); ss >> temp_double; temp_double = mm(temp_double); if(ss.fail()) return false; - sd.starttanx.push_back(temp_double); - break; - case 22: - // starttan y - get_line(); - ss.str(m_str); ss >> temp_double; temp_double = mm(temp_double); if(ss.fail()) return false; - sd.starttany.push_back(temp_double); - break; - case 32: - // starttan z - get_line(); - ss.str(m_str); ss >> temp_double; temp_double = mm(temp_double); if(ss.fail()) return false; - sd.starttanz.push_back(temp_double); - break; - case 13: - // endtan x - get_line(); - ss.str(m_str); ss >> temp_double; temp_double = mm(temp_double); if(ss.fail()) return false; - sd.endtanx.push_back(temp_double); - break; - case 23: - // endtan y - get_line(); - ss.str(m_str); ss >> temp_double; temp_double = mm(temp_double); if(ss.fail()) return false; - sd.endtany.push_back(temp_double); - break; - case 33: - // endtan z - get_line(); - ss.str(m_str); ss >> temp_double; temp_double = mm(temp_double); if(ss.fail()) return false; - sd.endtanz.push_back(temp_double); - break; - case 40: - // knot - get_line(); - ss.str(m_str); ss >> temp_double; temp_double = mm(temp_double); if(ss.fail()) return false; - sd.knot.push_back(temp_double); - break; - case 41: - // weight - get_line(); - ss.str(m_str); ss >> temp_double; temp_double = mm(temp_double); if(ss.fail()) return false; - sd.weight.push_back(temp_double); - break; - case 10: - // control x - get_line(); - ss.str(m_str); ss >> temp_double; temp_double = mm(temp_double); if(ss.fail()) return false; - sd.controlx.push_back(temp_double); - break; - case 20: - // control y - get_line(); - ss.str(m_str); ss >> temp_double; temp_double = mm(temp_double); if(ss.fail()) return false; - sd.controly.push_back(temp_double); - break; - case 30: - // control z - get_line(); - ss.str(m_str); ss >> temp_double; temp_double = mm(temp_double); if(ss.fail()) return false; - sd.controlz.push_back(temp_double); - break; - case 11: - // fit x - get_line(); - ss.str(m_str); ss >> temp_double; temp_double = mm(temp_double); if(ss.fail()) return false; - sd.fitx.push_back(temp_double); - break; - case 21: - // fit y - get_line(); - ss.str(m_str); ss >> temp_double; temp_double = mm(temp_double); if(ss.fail()) return false; - sd.fity.push_back(temp_double); - break; - case 31: - // fit z - get_line(); - ss.str(m_str); ss >> temp_double; temp_double = mm(temp_double); if(ss.fail()) return false; - sd.fitz.push_back(temp_double); - break; - case 42: - case 43: - case 44: - // skip the next line - get_line(); - break; - default: - // skip the next line - get_line(); - break; + + get_line(); + switch (n) { + case 210: + case 220: + case 230: + // normal coords + HandleCoordCode<210, 220, 230>(n, &sd.norm); + break; + case 70: + // flag + sd.flag = stringToInt(m_str); + break; + case 71: + // degree + sd.degree = stringToInt(m_str); + break; + case 72: + // knots + sd.knots = stringToInt(m_str); + break; + case 73: + // control points + sd.control_points = stringToInt(m_str); + break; + case 74: + // fit points + sd.fit_points = stringToInt(m_str); + break; + case 12: + // starttan x + sd.starttanx.push_back(mm(stringToDouble(m_str))); + break; + case 22: + // starttan y + sd.starttany.push_back(mm(stringToDouble(m_str))); + break; + case 32: + // starttan z + sd.starttanz.push_back(mm(stringToDouble(m_str))); + break; + case 13: + // endtan x + sd.endtanx.push_back(mm(stringToDouble(m_str))); + break; + case 23: + // endtan y + sd.endtany.push_back(mm(stringToDouble(m_str))); + break; + case 33: + // endtan z + sd.endtanz.push_back(mm(stringToDouble(m_str))); + break; + case 40: + // knot + sd.knot.push_back(mm(stringToDouble(m_str))); + break; + case 41: + // weight + sd.weight.push_back(mm(stringToDouble(m_str))); + break; + case 10: + // control x + sd.controlx.push_back(mm(stringToDouble(m_str))); + break; + case 20: + // control y + sd.controly.push_back(mm(stringToDouble(m_str))); + break; + case 30: + // control z + sd.controlz.push_back(mm(stringToDouble(m_str))); + break; + case 11: + // fit x + sd.fitx.push_back(mm(stringToDouble(m_str))); + break; + case 21: + // fit y + sd.fity.push_back(mm(stringToDouble(m_str))); + break; + case 31: + // fit z + sd.fitz.push_back(mm(stringToDouble(m_str))); + break; + case 42: + case 43: + case 44: + // skip the next line + break; + default: + HandleCommonGroupCode(n); + break; } } - DerefACI(); + + ResolveColorIndex(); OnReadSpline(sd); return false; } @@ -2241,271 +2296,342 @@ bool CDxfRead::ReadSpline() bool CDxfRead::ReadCircle() { double radius = 0.0; - double c[3] = {0,0,0}; // centre + DxfCoords c = {}; // centre bool hidden = false; - while(!m_ifs.eof()) - { + while (!m_ifs.eof()) { get_line(); - int n; - if(sscanf(m_str, "%d", &n) != 1) - { + const int n = stringToInt(m_str, StringToErrorMode::ReturnErrorValue); + if (n == 0) { + // next item found, so finish with Circle + ResolveColorIndex(); + OnReadCircle(c, radius, hidden); + return true; + } + else if (isStringToErrorValue(n)) { this->ReportError_readInteger("DXF::ReadCircle()"); return false; } - std::istringstream ss; - ss.imbue(std::locale("C")); - switch(n){ - case 0: - // next item found, so finish with Circle - DerefACI(); - OnReadCircle(c, radius, hidden); - hidden = false; - return true; - - case 6: // line style name follows - get_line(); - if(m_str[0] == 'h' || m_str[0] == 'H')hidden = true; - break; - - case 8: // Layer name follows - get_line(); - safe_strcpy(m_layer_name, m_str); - break; - case 10: - // centre x - get_line(); - ss.str(m_str); ss >> c[0]; c[0] = mm(c[0]); if(ss.fail()) return false; - break; - case 20: - // centre y - get_line(); - ss.str(m_str); ss >> c[1]; c[1] = mm(c[1]); if(ss.fail()) return false; - break; - case 30: - // centre z - get_line(); - ss.str(m_str); ss >> c[2]; c[2] = mm(c[2]); if(ss.fail()) return false; - break; - case 40: - // radius - get_line(); - ss.str(m_str); ss >> radius; radius = mm(radius); if(ss.fail()) return false; - break; - case 62: - // color index - get_line(); - ss.str(m_str); ss >> m_aci; if(ss.fail()) return false; - break; + get_line(); + switch (n){ + case 6: // line style name follows + if (!m_str.empty() && (m_str[0] == 'h' || m_str[0] == 'H')) { + hidden = true; + } + break; + case 10: case 20: case 30: + // centre coords + HandleCoordCode(n, &c); + break; + case 40: + // radius + radius = mm(stringToDouble(m_str)); + break; - case 100: - case 39: - case 210: - case 220: - case 230: - // skip the next line - get_line(); - break; - default: - // skip the next line - get_line(); - break; + case 100: + case 39: + case 210: + case 220: + case 230: + // skip the next line + break; + default: + HandleCommonGroupCode(n); + break; } } - DerefACI(); + + ResolveColorIndex(); OnReadCircle(c, radius, false); return false; } - -bool CDxfRead::ReadText() +bool CDxfRead::ReadMText() { - double c[3]; // coordinate - double height = 0.03082; - double rotation = 0.; // degrees - char text[1024] = {}; + Dxf_MTEXT text; + bool withinAcadColumnInfo = false; + bool withinAcadColumns = false; + bool withinAcadDefinedHeight = false; + + while (!m_ifs.eof()) { + get_line(); + const int n = stringToInt(m_str, StringToErrorMode::ReturnErrorValue); + if (n == 0) { + ResolveColorIndex(); + // Replace \P by \n + size_t pos = text.str.find("\\P", 0); + while (pos != std::string::npos) { + text.str.replace(pos, 2, "\n"); + pos = text.str.find("\\P", pos + 1); + } - memset( c, 0, sizeof(c) ); + text.str = this->toUtf8(text.str); + OnReadMText(text); + return true; + } - while(!m_ifs.eof()) - { get_line(); - int n; - if(sscanf(m_str, "%d", &n) != 1) - { - this->ReportError_readInteger("DXF::ReadText()"); + + auto fnMatchExtensionBegin = [=](std::string_view extName, bool& tag) { + if (!tag && m_str == extName) { + tag = true; + return true; + } + return false; + }; + + auto fnMatchExtensionEnd = [=](std::string_view extName, bool& tag) { + if (tag && m_str == extName) { + tag = false; + return true; + } return false; + }; + + if (fnMatchExtensionBegin("ACAD_MTEXT_COLUMN_INFO_BEGIN", withinAcadColumnInfo)) { + text.acadHasColumnInfo = true; + continue; // Skip + } + + if (fnMatchExtensionEnd("ACAD_MTEXT_COLUMN_INFO_END", withinAcadColumnInfo)) + continue; // Skip + + if (fnMatchExtensionBegin("ACAD_MTEXT_COLUMNS_BEGIN", withinAcadColumns)) + continue; // Skip + + if (fnMatchExtensionEnd("ACAD_MTEXT_COLUMNS_END", withinAcadColumns)) + continue; // Skip + + if (fnMatchExtensionBegin("ACAD_MTEXT_DEFINED_HEIGHT_BEGIN", withinAcadDefinedHeight)) { + text.acadHasDefinedHeight = true; + continue; // Skip } - std::istringstream ss; - ss.imbue(std::locale("C")); - switch(n){ - case 0: - DerefACI(); - OnReadText(c, height * 25.4 / 72.0, rotation, text); - return(true); - - case 8: // Layer name follows - get_line(); - safe_strcpy(m_layer_name, m_str); - break; - case 10: - // centre x - get_line(); - ss.str(m_str); ss >> c[0]; c[0] = mm(c[0]); if(ss.fail()) return false; + if (fnMatchExtensionEnd("ACAD_MTEXT_DEFINED_HEIGHT_END", withinAcadDefinedHeight)) + continue; // Skip + + if (withinAcadColumnInfo) { + // 1040/1070 extended data code was found at beginning of current iteration + const int xn = stringToInt(m_str, StringToErrorMode::ReturnErrorValue); + get_line(); // Skip 1040/1070 line + get_line(); // Get value line of extended data code + switch (xn) { + case 75: { // 1070 + const int t = stringToInt(m_str); + if (0 <= t && t <= 2) + text.acadColumnInfo_Type = static_cast(t); + } break; - case 20: - // centre y - get_line(); - ss.str(m_str); ss >> c[1]; c[1] = mm(c[1]); if(ss.fail()) return false; + case 76: // 1070 + text.acadColumnInfo_Count = stringToInt(m_str); break; - case 30: - // centre z - get_line(); - ss.str(m_str); ss >> c[2]; c[2] = mm(c[2]); if(ss.fail()) return false; + case 78: // 1070 + text.acadColumnInfo_FlowReversed = stringToInt(m_str) != 0; break; - case 40: - // text height - get_line(); - ss.str(m_str); ss >> height; height = mm(height); if(ss.fail()) return false; + case 79: // 1070 + text.acadColumnInfo_AutoHeight = stringToInt(m_str) != 0; break; - case 1: - // text - get_line(); - safe_strcpy(text, m_str); + case 48: // 1040 + text.acadColumnInfo_Width = mm(stringToDouble(m_str)); break; - - case 50: - // text rotation - get_line(); - ss.str(m_str); ss >> rotation; if(ss.fail()) return false; + case 49: // 1040 + text.acadColumnInfo_GutterWidth = mm(stringToDouble(m_str)); break; + } // endswitch - case 62: - // color index - get_line(); - ss.str(m_str); ss >> m_aci; if(ss.fail()) return false; - break; + continue; // Skip + } - case 100: - case 39: - case 210: - case 220: - case 230: - // skip the next line - get_line(); - break; - default: - // skip the next line - get_line(); - break; + if (withinAcadDefinedHeight) { + // 1040/1070 extended data code was found at beginning of current iteration + const int xn = stringToInt(m_str, StringToErrorMode::ReturnErrorValue); + get_line(); // Skip 1040/1070 line + get_line(); // Get value line of extended data code + if (xn == 46) + text.acadDefinedHeight = mm(stringToDouble(m_str)); + + continue; // Skip + } + + switch (n) { + case 10: case 20: case 30: + // centre coords + HandleCoordCode(n, &text.insertionPoint); + break; + case 40: + // text height + text.height = mm(stringToDouble(m_str)); + break; + case 50: + // text rotation + text.rotationAngle = stringToDouble(m_str); + break; + case 3: + // Additional text that goes before the type 1 text + // Note that if breaking the text into type-3 records splits a UFT-8 encoding we do + // the decoding after splicing the lines together. I'm not sure if this actually + // occurs, but handling the text this way will treat this condition properly. + text.str.append(m_str); + break; + case 1: + // final text + text.str.append(m_str); + break; + + case 71: { + // attachment point + const int ap = stringToInt(m_str); + if (ap >= 1 && ap <= 9) + text.attachmentPoint = static_cast(ap); + } + break; + + case 11: case 21: case 31: + // X-axis direction vector + HandleCoordCode<11, 21, 31>(n, &text.xAxisDirectionVector); + break; + + case 210: case 220: case 230: + // extrusion direction + HandleCoordCode<210, 220, 230>(n, &text.extrusionDirection); + break; + + default: + HandleCommonGroupCode(n); + break; } } return false; } +bool CDxfRead::ReadText() +{ + Dxf_TEXT text; + + while (!m_ifs.eof()) { + get_line(); + const int n = stringToInt(m_str, StringToErrorMode::ReturnErrorValue); + if (n == 0) { + ResolveColorIndex(); + OnReadText(text); + return true; + } + + get_line(); + + switch (n) { + case 10: case 20: case 30: + HandleCoordCode(n, &text.firstAlignmentPoint); + break; + case 40: + text.height = mm(stringToDouble(m_str)); + break; + case 1: + text.str = this->toUtf8(m_str); + break; + case 50: + text.rotationAngle = stringToDouble(m_str); + break; + case 41: + text.relativeXScaleFactorWidth = stringToDouble(m_str); + break; + case 51: + text.obliqueAngle = stringToDouble(m_str); + break; + case 7: + text.styleName = m_str; + break; + case 72: { + const int hjust = stringToInt(m_str); + if (hjust >= 0 && hjust <= 5) + text.horizontalJustification = static_cast(hjust); + } + break; + case 11: case 21: case 31: + HandleCoordCode<11, 21, 31>(n, &text.secondAlignmentPoint); + break; + case 210: case 220: case 230: + HandleCoordCode<210, 220, 230>(n, &text.extrusionDirection); + break; + case 73: { + const int vjust = stringToInt(m_str); + if (vjust >= 0 && vjust <= 3) + text.verticalJustification = static_cast(vjust); + } + break; + default: + HandleCommonGroupCode(n); + break; + } + } + + return false; +} bool CDxfRead::ReadEllipse() { - double c[3] = {0,0,0}; // centre - double m[3] = {0,0,0}; //major axis point - double ratio=0; //ratio of major to minor axis - double start=0; //start of arc - double end=0; // end of arc + DxfCoords c = {}; // centre + DxfCoords m = {}; //major axis point + double ratio = 0; //ratio of major to minor axis + double start = 0; //start of arc + double end = 0; // end of arc - while(!m_ifs.eof()) - { + while (!m_ifs.eof()) { get_line(); - int n; - if(sscanf(m_str, "%d", &n) != 1) - { + const int n = stringToInt(m_str, StringToErrorMode::ReturnErrorValue); + if (n == 0) { + // next item found, so finish with Ellipse + ResolveColorIndex(); + OnReadEllipse(c, m, ratio, start, end); + return true; + } + else if (isStringToErrorValue(n)) { this->ReportError_readInteger("DXF::ReadEllipse()"); return false; } - std::istringstream ss; - ss.imbue(std::locale("C")); - switch(n){ - case 0: - // next item found, so finish with Ellipse - DerefACI(); - OnReadEllipse(c, m, ratio, start, end); - return true; - case 8: // Layer name follows - get_line(); - safe_strcpy(m_layer_name, m_str); - break; - case 10: - // centre x - get_line(); - ss.str(m_str); ss >> c[0]; c[0] = mm(c[0]); if(ss.fail()) return false; - break; - case 20: - // centre y - get_line(); - ss.str(m_str); ss >> c[1]; c[1] = mm(c[1]); if(ss.fail()) return false; - break; - case 30: - // centre z - get_line(); - ss.str(m_str); ss >> c[2]; c[2] = mm(c[2]); if(ss.fail()) return false; - break; - case 11: - // major x - get_line(); - ss.str(m_str); ss >> m[0]; m[0] = mm(m[0]); if(ss.fail()) return false; - break; - case 21: - // major y - get_line(); - ss.str(m_str); ss >> m[1]; m[1] = mm(m[1]); if(ss.fail()) return false; - break; - case 31: - // major z - get_line(); - ss.str(m_str); ss >> m[2]; m[2] = mm(m[2]); if(ss.fail()) return false; - break; - case 40: - // ratio - get_line(); - ss.str(m_str); ss >> ratio; if(ss.fail()) return false; - break; - case 41: - // start - get_line(); - ss.str(m_str); ss >> start; if(ss.fail()) return false; - break; - case 42: - // end - get_line(); - ss.str(m_str); ss >> end; if(ss.fail()) return false; - break; - case 62: - // color index - get_line(); - ss.str(m_str); ss >> m_aci; if(ss.fail()) return false; - break; - case 100: - case 210: - case 220: - case 230: - // skip the next line - get_line(); - break; - default: - // skip the next line - get_line(); - break; + get_line(); + switch (n) { + case 10: case 20: case 30: + // centre coords + HandleCoordCode(n, &c); + break; + case 11: case 21: case 31: + // major coords + HandleCoordCode<11, 21, 31>(n, &m); + break; + case 40: + // ratio + ratio = stringToDouble(m_str); + break; + case 41: + // start + start = stringToDouble(m_str); + break; + case 42: + // end + end = stringToDouble(m_str); + break; + case 100: + case 210: + case 220: + case 230: + // skip the next line + break; + default: + HandleCommonGroupCode(n); + break; } } - DerefACI(); + + ResolveColorIndex(); OnReadEllipse(c, m, ratio, start, end); return false; } - +// TODO Remove this(refactoring of CDxfRead::ReadLwPolyLine() static bool poly_prev_found = false; static double poly_prev_x; static double poly_prev_y; @@ -2517,29 +2643,28 @@ static double poly_first_x; static double poly_first_y; static double poly_first_z; -static void AddPolyLinePoint(CDxfRead* dxf_read, double x, double y, double z, bool bulge_found, double bulge) +// TODO Remove this(refactoring of CDxfRead::ReadLwPolyLine() +static void +AddPolyLinePoint(CDxfRead* dxf_read, double x, double y, double z, bool bulge_found, double bulge) { try { - if(poly_prev_found) - { + if (poly_prev_found) { bool arc_done = false; - if(poly_prev_bulge_found) - { + if (poly_prev_bulge_found) { double cot = 0.5 * ((1.0 / poly_prev_bulge) - poly_prev_bulge); double cx = ((poly_prev_x + x) - ((y - poly_prev_y) * cot)) / 2.0; double cy = ((poly_prev_y + y) + ((x - poly_prev_x) * cot)) / 2.0; - double ps[3] = {poly_prev_x, poly_prev_y, poly_prev_z}; - double pe[3] = {x, y, z}; - double pc[3] = {cx, cy, (poly_prev_z + z)/2.0}; + const DxfCoords ps = {poly_prev_x, poly_prev_y, poly_prev_z}; + const DxfCoords pe = {x, y, z}; + const DxfCoords pc = {cx, cy, (poly_prev_z + z)/2.0}; dxf_read->OnReadArc(ps, pe, pc, poly_prev_bulge >= 0, false); arc_done = true; } - if(!arc_done) - { - double s[3] = {poly_prev_x, poly_prev_y, poly_prev_z}; - double e[3] = {x, y, z}; + if (!arc_done) { + const DxfCoords s = {poly_prev_x, poly_prev_y, poly_prev_z}; + const DxfCoords e = {x, y, z}; dxf_read->OnReadLine(s, e, false); } } @@ -2548,8 +2673,7 @@ static void AddPolyLinePoint(CDxfRead* dxf_read, double x, double y, double z, b poly_prev_x = x; poly_prev_y = y; poly_prev_z = z; - if(!poly_first_found) - { + if (!poly_first_found) { poly_first_x = x; poly_first_y = y; poly_first_z = z; @@ -2558,18 +2682,21 @@ static void AddPolyLinePoint(CDxfRead* dxf_read, double x, double y, double z, b poly_prev_bulge_found = bulge_found; poly_prev_bulge = bulge; } - catch(...) - { - if (! dxf_read->IgnoreErrors()) throw; // Re-throw it. + catch (...) { + if (!dxf_read->IgnoreErrors()) { + throw; // Re-throw it. + } } } +// TODO Remove this(refactoring of CDxfRead::ReadLwPolyLine() static void PolyLineStart() { poly_prev_found = false; poly_first_found = false; } +// TODO Reimplement this function(refactoring of CDxfRead::ReadLwPolyLine() bool CDxfRead::ReadLwPolyLine() { PolyLineStart(); @@ -2585,89 +2712,95 @@ bool CDxfRead::ReadLwPolyLine() int flags; bool next_item_found = false; - while(!m_ifs.eof() && !next_item_found) - { + while (!m_ifs.eof() && !next_item_found) { get_line(); - int n; - if(sscanf(m_str, "%d", &n) != 1) - { + const int n = stringToInt(m_str); + if (isStringToErrorValue(n)) { this->ReportError_readInteger("DXF::ReadLwPolyLine()"); return false; } + std::istringstream ss; - ss.imbue(std::locale("C")); - switch(n){ - case 0: - // next item found - - DerefACI(); - if(x_found && y_found){ - // add point - AddPolyLinePoint(this, x, y, z, bulge_found, bulge); - bulge_found = false; - x_found = false; - y_found = false; - } - next_item_found = true; - break; - case 8: // Layer name follows - get_line(); - safe_strcpy(m_layer_name, m_str); - break; - case 10: - // x - get_line(); - if(x_found && y_found){ - // add point - AddPolyLinePoint(this, x, y, z, bulge_found, bulge); - bulge_found = false; - x_found = false; - y_found = false; - } - ss.str(m_str); ss >> x; x = mm(x); if(ss.fail()) return false; - x_found = true; - break; - case 20: - // y - get_line(); - ss.str(m_str); ss >> y; y = mm(y); if(ss.fail()) return false; - y_found = true; - break; - case 38: - // elevation - get_line(); - ss.str(m_str); ss >> z; z = mm(z); if(ss.fail()) return false; - break; - case 42: - // bulge - get_line(); - ss.str(m_str); ss >> bulge; if(ss.fail()) return false; - bulge_found = true; - break; - case 70: - // flags - get_line(); - if(sscanf(m_str, "%d", &flags) != 1)return false; - closed = ((flags & 1) != 0); - break; - case 62: - // color index - get_line(); - ss.str(m_str); ss >> m_aci; if(ss.fail()) return false; - break; - default: - // skip the next line - get_line(); - break; + ss.imbue(std::locale::classic()); + switch (n){ + case 0: + // next item found + ResolveColorIndex(); + if (x_found && y_found){ + // add point + AddPolyLinePoint(this, x, y, z, bulge_found, bulge); + bulge_found = false; + x_found = false; + y_found = false; + } + next_item_found = true; + break; + case 10: + // x + get_line(); + if (x_found && y_found) { + // add point + AddPolyLinePoint(this, x, y, z, bulge_found, bulge); + bulge_found = false; + x_found = false; + y_found = false; + } + ss.str(m_str); + ss >> x; + x = mm(x); + if (ss.fail()) { + return false; + } + x_found = true; + break; + case 20: + // y + get_line(); + ss.str(m_str); + ss >> y; + y = mm(y); + if (ss.fail()) { + return false; + } + y_found = true; + break; + case 38: + // elevation + get_line(); + ss.str(m_str); + ss >> z; + z = mm(z); + if (ss.fail()) { + return false; + } + break; + case 42: + // bulge + get_line(); + ss.str(m_str); + ss >> bulge; + if (ss.fail()) { + return false; + } + bulge_found = true; + break; + case 70: + // flags + get_line(); + flags = stringToInt(m_str); + closed = ((flags & 1) != 0); + break; + default: + get_line(); + HandleCommonGroupCode(n); + break; } } - if(next_item_found) - { - if(closed && poly_first_found) - { + if (next_item_found) { + if (closed && poly_first_found) { // repeat the first point - DerefACI(); + ResolveColorIndex(); AddPolyLinePoint(this, poly_first_x, poly_first_y, poly_first_z, false, 0.0); } return true; @@ -2676,75 +2809,93 @@ bool CDxfRead::ReadLwPolyLine() return false; } - -bool CDxfRead::ReadVertex(double *pVertex, bool *bulge_found, double *bulge) +bool CDxfRead::ReadVertex(Dxf_VERTEX* vertex) { bool x_found = false; bool y_found = false; - - double x = 0.0; - double y = 0.0; - double z = 0.0; - *bulge = 0.0; - *bulge_found = false; - - pVertex[0] = 0.0; - pVertex[1] = 0.0; - pVertex[2] = 0.0; - - while(!m_ifs.eof()) { + while (!m_ifs.eof()) { get_line(); - int n; - if(sscanf(m_str, "%d", &n) != 1) { + const int n = stringToInt(m_str, StringToErrorMode::ReturnErrorValue); + if (n == 0) { + ResolveColorIndex(); + put_line(m_str); // read one line too many. put it back. + return x_found && y_found; + } + else if (isStringToErrorValue(n)) { this->ReportError_readInteger("DXF::ReadVertex()"); return false; } - std::istringstream ss; - ss.imbue(std::locale("C")); - switch(n){ - case 0: - DerefACI(); - put_line(m_str); // read one line too many. put it back. - return(x_found && y_found); - break; - case 8: // Layer name follows - get_line(); - safe_strcpy(m_layer_name, m_str); + get_line(); + switch (n){ + case 10: case 20: case 30: + // coords + x_found = x_found || n == 10; + y_found = y_found || n == 20; + HandleCoordCode(n, &vertex->point); break; - - case 10: - // x - get_line(); - ss.str(m_str); ss >> x; pVertex[0] = mm(x); if(ss.fail()) return false; - x_found = true; + case 42: { + // bulge + const int bulge = stringToInt(m_str); + if (bulge == 0) + vertex->bulge = Dxf_VERTEX::Bulge::StraightSegment; + else + vertex->bulge = Dxf_VERTEX::Bulge::SemiCircle; + } break; - case 20: - // y - get_line(); - ss.str(m_str); ss >> y; pVertex[1] = mm(y); if(ss.fail()) return false; - y_found = true; + case 70: + // flags + vertex->flags = stringToUnsigned(m_str); break; - case 30: - // z - get_line(); - ss.str(m_str); ss >> z; pVertex[2] = mm(z); if(ss.fail()) return false; + default: + HandleCommonGroupCode(n); break; + } + } - case 42: - get_line(); - *bulge_found = true; - ss.str(m_str); ss >> *bulge; if(ss.fail()) return false; - break; - case 62: - // color index + return false; +} + +bool CDxfRead::ReadSolid() +{ + Dxf_SOLID solid; + + while (!m_ifs.eof()) { get_line(); - ss.str(m_str); ss >> m_aci; if(ss.fail()) return false; - break; + const int n = stringToInt(m_str, StringToErrorMode::ReturnErrorValue); + if (n == 0) { + ResolveColorIndex(); + OnReadSolid(solid); + return true; + } + else if (isStringToErrorValue(n)) { + this->ReportError_readInteger("DXF::ReadSolid()"); + return false; + } + get_line(); + switch (n) { + case 10: case 20: case 30: + HandleCoordCode<10, 20, 30>(n, &solid.corner1); + break; + case 11: case 21: case 31: + HandleCoordCode<11, 21, 31>(n, &solid.corner2); + break; + case 12: case 22: case 32: + HandleCoordCode<12, 22, 32>(n, &solid.corner3); + break; + case 13: case 23: case 33: + HandleCoordCode<13, 23, 33>(n, &solid.corner4); + solid.hasCorner4 = true; + break; + case 39: + solid.thickness = stringToDouble(m_str); + break; + case 210: case 220: case 230: + HandleCoordCode<210, 220, 230>(n, &solid.extrusionDirection); + break; default: - // skip the next line - get_line(); + HandleCommonGroupCode(n); break; } } @@ -2752,658 +2903,615 @@ bool CDxfRead::ReadVertex(double *pVertex, bool *bulge_found, double *bulge) return false; } +bool CDxfRead::ReadSection() +{ + m_section_name.clear(); + get_line(); + get_line(); + if (m_str != "ENTITIES") + m_section_name = m_str; + m_block_name.clear(); + return true; +} -bool CDxfRead::ReadPolyLine() +bool CDxfRead::ReadTable() { - PolyLineStart(); + get_line(); + get_line(); + return true; +} - bool closed = false; - int flags; - bool first_vertex_section_found = false; - double first_vertex[3] = {0,0,0}; - bool bulge_found; - double bulge; +bool CDxfRead::ReadEndSec() +{ + m_section_name.clear(); + m_block_name.clear(); + return true; +} - while(!m_ifs.eof()) - { +bool CDxfRead::ReadPolyLine() +{ + Dxf_POLYLINE polyline; + while (!m_ifs.eof()) { get_line(); - int n; - if(sscanf(m_str, "%d", &n) != 1) - { + const int n = stringToInt(m_str, StringToErrorMode::ReturnErrorValue); + if (isStringToErrorValue(n)) { this->ReportError_readInteger("DXF::ReadPolyLine()"); return false; } - std::istringstream ss; - ss.imbue(std::locale("C")); - switch(n){ - case 0: - // next item found - DerefACI(); - get_line(); - if (! strcmp(m_str,"VERTEX")) - { - double vertex[3] = {0,0,0}; - if (CDxfRead::ReadVertex(vertex, &bulge_found, &bulge)) - { - if(!first_vertex_section_found) { - first_vertex_section_found = true; - memcpy(first_vertex, vertex, 3*sizeof(double)); - } - AddPolyLinePoint(this, vertex[0], vertex[1], vertex[2], bulge_found, bulge); - break; - } - } - if (! strcmp(m_str,"SEQEND")) - { - if(closed && first_vertex_section_found) { - AddPolyLinePoint(this, first_vertex[0], first_vertex[1], first_vertex[2], 0, 0); - } - first_vertex_section_found = false; - PolyLineStart(); - return(true); - } - break; - case 70: - // flags - get_line(); - if(sscanf(m_str, "%d", &flags) != 1)return false; - closed = ((flags & 1) != 0); - break; - case 62: - // color index - get_line(); - ss.str(m_str); ss >> m_aci; if(ss.fail()) return false; - break; - default: - // skip the next line - get_line(); - break; + + get_line(); + switch (n) { + case 0: + // next item found + ResolveColorIndex(); + if (m_str == "VERTEX") { + Dxf_VERTEX vertex; + if (ReadVertex(&vertex)) + polyline.vertices.push_back(std::move(vertex)); + } + + if (m_str == "SEQEND") { + OnReadPolyline(polyline); + return true; + } + + break; + case 70: + // flags + polyline.flags = stringToUnsigned(m_str); + break; + default: + HandleCommonGroupCode(n); + break; } } return false; } -void CDxfRead::OnReadArc(double start_angle, double end_angle, double radius, const double* c, double z_extrusion_dir, bool hidden){ - double s[3] = {0,0,0}, e[3] = {0,0,0}, temp[3] = {0,0,0}; - if (z_extrusion_dir==1.0) - { - temp[0] =c[0]; - temp[1] =c[1]; - temp[2] =c[2]; - s[0] = c[0] + radius * cos(start_angle * M_PI/180); - s[1] = c[1] + radius * sin(start_angle * M_PI/180); - s[2] = c[2]; - e[0] = c[0] + radius * cos(end_angle * M_PI/180); - e[1] = c[1] + radius * sin(end_angle * M_PI/180); - e[2] = c[2]; - } - else - { - temp[0] =-c[0]; - temp[1] =c[1]; - temp[2] =c[2]; - - e[0] = -(c[0] + radius * cos(start_angle * M_PI/180)); - e[1] = (c[1] + radius * sin(start_angle * M_PI/180)); - e[2] = c[2]; - s[0] = -(c[0] + radius * cos(end_angle * M_PI/180)); - s[1] = (c[1] + radius * sin(end_angle * M_PI/180)); - s[2] = c[2]; - +void CDxfRead::OnReadArc(double start_angle, + double end_angle, + double radius, + const DxfCoords& c, + double z_extrusion_dir, + bool hidden) +{ + DxfCoords s = {}; + DxfCoords e = {}; + DxfCoords temp = {}; + if (z_extrusion_dir == 1.0) { + temp.x = c.x; + temp.y = c.y; + temp.z = c.z; + s.x = c.x + radius * cos(start_angle * M_PI / 180); + s.y = c.y + radius * sin(start_angle * M_PI / 180); + s.z = c.z; + e.x = c.x + radius * cos(end_angle * M_PI / 180); + e.y = c.y + radius * sin(end_angle * M_PI / 180); + e.z = c.z; + } + else { + temp.x = -c.x; + temp.y = c.y; + temp.z = c.z; + + e.x = -(c.x + radius * cos(start_angle * M_PI/180)); + e.y = (c.y + radius * sin(start_angle * M_PI/180)); + e.z = c.z; + s.x = -(c.x + radius * cos(end_angle * M_PI/180)); + s.y = (c.y + radius * sin(end_angle * M_PI/180)); + s.z = c.z; } + OnReadArc(s, e, temp, true, hidden); } -void CDxfRead::OnReadCircle(const double* c, double radius, bool hidden){ - double s[3]; - double start_angle = 0; - s[0] = c[0] + radius * cos(start_angle * M_PI/180); - s[1] = c[1] + radius * sin(start_angle * M_PI/180); - s[2] = c[2]; +void CDxfRead::OnReadCircle(const DxfCoords& c, double radius, bool hidden) +{ + constexpr double start_angle = 0; + const DxfCoords s = { + c.x + radius * cos(start_angle * M_PI / 180), + c.y + radius * sin(start_angle * M_PI / 180), + c.z + }; - OnReadCircle(s, c, false, hidden); //false to change direction because otherwise the arc length is zero + const bool dir = false; // 'false' to change direction because otherwise the arc length is zero + OnReadCircle(s, c, dir, hidden); } -void CDxfRead::OnReadEllipse(const double* c, const double* m, double ratio, double start_angle, double end_angle){ - double major_radius = sqrt(m[0]*m[0] + m[1]*m[1] + m[2]*m[2]); - double minor_radius = major_radius * ratio; - - //Since we only support 2d stuff, we can calculate the rotation from the major axis x and y value only, - //since z is zero, major_radius is the vector length - - double rotation = atan2(m[1]/major_radius,m[0]/major_radius); +void CDxfRead::OnReadEllipse(const DxfCoords& c, + const DxfCoords& m, + double ratio, + double start_angle, + double end_angle) +{ + const double major_radius = sqrt(m.x * m.x + m.y * m.y + m.z * m.z); + const 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 + const double rotation = atan2(m.y / major_radius, m.x / major_radius); OnReadEllipse(c, major_radius, minor_radius, rotation, start_angle, end_angle, true); } - bool CDxfRead::ReadInsert() { - double c[3] = {0,0,0}; // coordinate - double s[3] = {1,1,1}; // scale - double rot = 0.0; // rotation - char name[1024] = {0}; + Dxf_INSERT insert; - while(!m_ifs.eof()) - { + while (!m_ifs.eof()) { get_line(); - int n; - if(sscanf(m_str, "%d", &n) != 1) - { + const int n = stringToInt(m_str, StringToErrorMode::ReturnErrorValue); + if (n == 0) { + // next item found + ResolveColorIndex(); + OnReadInsert(insert); + return true; + } + else if (isStringToErrorValue(n)) { this->ReportError_readInteger("DXF::ReadInsert()"); return false; } - std::istringstream ss; - ss.imbue(std::locale("C")); - switch(n){ - case 0: - // next item found - DerefACI(); - OnReadInsert(c, s, name, rot * M_PI/180); - return(true); - case 8: - // Layer name follows - get_line(); - safe_strcpy(m_layer_name, m_str); - break; - case 10: - // coord x - get_line(); - ss.str(m_str); ss >> c[0]; c[0] = mm(c[0]); if(ss.fail()) return false; - break; - case 20: - // coord y - get_line(); - ss.str(m_str); ss >> c[1]; c[1] = mm(c[1]); if(ss.fail()) return false; - break; - case 30: - // coord z - get_line(); - ss.str(m_str); ss >> c[2]; c[2] = mm(c[2]); if(ss.fail()) return false; - break; - case 41: - // scale x - get_line(); - ss.str(m_str); ss >> s[0]; if(ss.fail()) return false; - break; - case 42: - // scale y - get_line(); - ss.str(m_str); ss >> s[1]; if(ss.fail()) return false; - break; - case 43: - // scale z - get_line(); - ss.str(m_str); ss >> s[2]; if(ss.fail()) return false; - break; - case 50: - // rotation - get_line(); - ss.str(m_str); ss >> rot; if(ss.fail()) return false; - break; - case 2: - // block name - get_line(); - safe_strcpy(name, m_str); - break; - case 62: - // color index - get_line(); - ss.str(m_str); ss >> m_aci; if(ss.fail()) return false; - break; - case 100: - case 39: - case 210: - case 220: - case 230: - // skip the next line - get_line(); - break; - default: - // skip the next line - get_line(); - break; + + get_line(); + switch (n){ + case 2: + insert.blockName = m_str; + break; + case 10: case 20: case 30: + HandleCoordCode(n, &insert.insertPoint); + break; + case 41: + insert.scaleFactor.x = stringToDouble(m_str); + break; + case 42: + insert.scaleFactor.y = stringToDouble(m_str); + break; + case 43: + insert.scaleFactor.z = stringToDouble(m_str); + break; + case 50: + insert.rotationAngle = stringToDouble(m_str); + break; + case 70: + insert.columnCount = stringToInt(m_str); + break; + case 71: + insert.rowCount = stringToInt(m_str); + break; + case 44: + insert.columnSpacing = mm(stringToDouble(m_str)); + break; + case 45: + insert.rowSpacing = mm(stringToDouble(m_str)); + break; + case 210: case 220: case 230: + HandleCoordCode<210, 220, 230>(n, &insert.extrusionDirection); + break; + default: + HandleCommonGroupCode(n); + break; } } + return false; } - bool CDxfRead::ReadDimension() { - double s[3] = {0,0,0}; // startpoint - double e[3] = {0,0,0}; // endpoint - double p[3] = {0,0,0}; // dimpoint + DxfCoords s = {}; // startpoint + DxfCoords e = {}; // endpoint + DxfCoords p = {}; // dimpoint double rot = -1.0; // rotation - while(!m_ifs.eof()) - { + while (!m_ifs.eof()) { get_line(); - int n; - if(sscanf(m_str, "%d", &n) != 1) - { + const int n = stringToInt(m_str, StringToErrorMode::ReturnErrorValue); + if (n == 0) { + // next item found + ResolveColorIndex(); + OnReadDimension(s, e, p, rot * M_PI/180); + return true; + } + else if (isStringToErrorValue(n)) { this->ReportError_readInteger("DXF::ReadDimension()"); return false; } - std::istringstream ss; - ss.imbue(std::locale("C")); - switch(n){ - case 0: - // next item found - DerefACI(); - OnReadDimension(s, e, p, rot * M_PI/180); - return(true); - case 8: - // Layer name follows - get_line(); - safe_strcpy(m_layer_name, m_str); - break; - case 13: - // start x - get_line(); - ss.str(m_str); ss >> s[0]; s[0] = mm(s[0]); if(ss.fail()) return false; - break; - case 23: - // start y - get_line(); - ss.str(m_str); ss >> s[1]; s[1] = mm(s[1]); if(ss.fail()) return false; - break; - case 33: - // start z - get_line(); - ss.str(m_str); ss >> s[2]; s[2] = mm(s[2]); if(ss.fail()) return false; - break; - case 14: - // end x - get_line(); - ss.str(m_str); ss >> e[0]; e[0] = mm(e[0]); if(ss.fail()) return false; - break; - case 24: - // end y - get_line(); - ss.str(m_str); ss >> e[1]; e[1] = mm(e[1]); if(ss.fail()) return false; - break; - case 34: - // end z - get_line(); - ss.str(m_str); ss >> e[2]; e[2] = mm(e[2]); if(ss.fail()) return false; - break; - case 10: - // dimline x - get_line(); - ss.str(m_str); ss >> p[0]; p[0] = mm(p[0]); if(ss.fail()) return false; - break; - case 20: - // dimline y - get_line(); - ss.str(m_str); ss >> p[1]; p[1] = mm(p[1]); if(ss.fail()) return false; - break; - case 30: - // dimline z - get_line(); - ss.str(m_str); ss >> p[2]; p[2] = mm(p[2]); if(ss.fail()) return false; - break; - case 50: - // rotation - get_line(); - ss.str(m_str); ss >> rot; if(ss.fail()) return false; - break; - case 62: - // color index - get_line(); - ss.str(m_str); ss >> m_aci; if(ss.fail()) return false; - break; - case 100: - case 39: - case 210: - case 220: - case 230: - // skip the next line - get_line(); - break; - default: - // skip the next line - get_line(); - break; + + get_line(); + switch (n){ + case 13: case 23: case 33: + // start coords + HandleCoordCode<13, 23, 33>(n, &s); + break; + case 14: case 24: case 34: + // end coords + HandleCoordCode<14, 24, 34>(n, &e); + break; + case 10: case 20: case 30: + // dimline coords + HandleCoordCode<10, 20, 30>(n, &p); + break; + case 50: + // rotation + rot = stringToDouble(m_str); + break; + case 100: + case 39: + case 210: + case 220: + case 230: + // skip the next line + break; + default: + HandleCommonGroupCode(n); + break; } } + return false; } bool CDxfRead::ReadBlockInfo() { - while(!m_ifs.eof()) - { + while (!m_ifs.eof()) { get_line(); - int n; - if(sscanf(m_str, "%d", &n) != 1) - { + const int n = stringToInt(m_str, StringToErrorMode::ReturnErrorValue); + if (isStringToErrorValue(n)) { this->ReportError_readInteger("DXF::ReadBlockInfo()"); return false; } - std::istringstream ss; - ss.imbue(std::locale("C")); - switch(n){ - case 2: - // block name - get_line(); - safe_strcpy(m_block_name, m_str); - return true; - case 3: - // block name too??? - get_line(); - safe_strcpy(m_block_name, m_str); - return true; - default: - // skip the next line - get_line(); - break; + + get_line(); + switch (n){ + case 2: + // block name + m_block_name = m_str; + return true; + case 3: + // block name too??? + m_block_name = m_str; + return true; + default: + // skip the next line + break; } } + return false; } - void CDxfRead::get_line() { - if (m_unused_line[0] != '\0') - { - safe_strcpy(m_str, m_unused_line); - memset( m_unused_line, '\0', sizeof(m_unused_line)); + if (!m_unused_line.empty()) { + m_str = m_unused_line; + m_unused_line.clear(); return; } - m_ifs.getline(m_str, 1024); - ++m_lineNum; + std::getline(m_ifs, m_str); + m_gcount = m_str.size(); + ++m_line_nb; - char str[1024]; - size_t len = strlen(m_str); - int j = 0; - bool non_white_found = false; - for(size_t i = 0; i= size) ? size - 1 : ret; - memcpy(dst, src, len); - dst[len] = '\0'; + ++itNonSpace; } + + m_str.erase(m_str.begin(), itNonSpace); } -void CDxfRead::put_line(const char *value) +void CDxfRead::put_line(const std::string& value) { - dxf_strncpy( m_unused_line, value, sizeof(m_unused_line) ); + m_unused_line = value; } - -bool CDxfRead::ReadUnits() +bool CDxfRead::ReadInsUnits() { get_line(); // Skip to next line. get_line(); // Skip to next line. - int n = 0; - if(sscanf(m_str, "%d", &n) == 1) - { - m_eUnits = eDxfUnits_t( n ); - return(true); - } // End if - then - else - { + const int n = stringToInt(m_str, StringToErrorMode::ReturnErrorValue); + if (!isStringToErrorValue(n)) { + m_eUnits = static_cast(n); + return true; + } + else { this->ReportError_readInteger("DXF::ReadUnits()"); - return(false); + return false; } } +bool CDxfRead::ReadMeasurement() +{ + get_line(); + get_line(); + const int n = stringToInt(m_str, StringToErrorMode::ReturnErrorValue); + if (n == 0) + m_measurement_inch = true; + + return true; +} bool CDxfRead::ReadLayer() { std::string layername; - int aci = -1; + ColorIndex_t colorIndex = -1; - while(!m_ifs.eof()) - { + while (!m_ifs.eof()) { get_line(); - int n; + const int n = stringToInt(m_str, StringToErrorMode::ReturnErrorValue); + if (n == 0) { + if (layername.empty()) { + this->ReportError_readInteger("DXF::ReadLayer() - no layer name"); + return false; + } - if(sscanf(m_str, "%d", &n) != 1) - { + m_layer_ColorIndex_map[layername] = colorIndex; + return true; + } + else if (isStringToErrorValue(n)) { this->ReportError_readInteger("DXF::ReadLayer()"); return false; } - std::istringstream ss; - ss.imbue(std::locale("C")); - switch(n){ - case 0: // next item found, so finish with line - if (layername.empty()) - return false; + get_line(); + switch (n) { + case 2: // Layer name follows + layername = m_str; + break; + case 62: + // layer color ; if negative, layer is off + colorIndex = stringToInt(m_str); + break; + case 6: // linetype name + case 70: // layer flags + case 100: + case 290: + case 370: + case 390: + // skip the next line + break; + default: + // skip the next line + break; + } + } - m_layer_aci[layername] = aci; - return true; + return false; +} - case 2: // Layer name follows - get_line(); - layername = m_str; - break; +bool CDxfRead::ReadStyle() +{ + Dxf_STYLE style; - case 62: - // layer color ; if negative, layer is off - get_line(); - if(sscanf(m_str, "%d", &aci) != 1)return false; - break; + while (!m_ifs.eof()) { + get_line(); + const int n = stringToInt(m_str, StringToErrorMode::ReturnErrorValue); + if (n == 0) { + if (style.name.empty()) { + this->ReportError_readInteger("DXF::ReadStyle() - no style name"); + return false; + } - case 6: // linetype name - case 70: // layer flags - case 100: - case 290: - case 370: - case 390: - // skip the next line - get_line(); - break; - default: - // skip the next line - get_line(); - break; + m_mapStyle.insert({ style.name, style }); + return true; + } + else if (isStringToErrorValue(n)) { + this->ReportError_readInteger("DXF::ReadStyle()"); + return false; + } + + get_line(); + switch (n) { + case 2: + style.name = m_str; + break; + case 40: + style.fixedTextHeight = mm(stringToDouble(m_str)); + break; + case 41: + style.widthFactor = stringToDouble(m_str); + break; + case 50: + style.obliqueAngle = stringToDouble(m_str); + break; + case 3: + style.primaryFontFileName = m_str; + break; + case 4: + style.bigFontFileName = m_str; + break; + default: + break; // skip the next line } } + return false; } -void CDxfRead::DoRead(const bool ignore_errors /* = false */ ) +bool CDxfRead::ReadAcadVer() { - m_ignore_errors = ignore_errors; - if(m_fail)return; + static const std::vector VersionNames = { + // This table is indexed by eDXFVersion_t - (ROlder+1) + "AC1006", + "AC1009", + "AC1012", + "AC1014", + "AC1015", + "AC1018", + "AC1021", + "AC1024", + "AC1027", + "AC1032" + }; + assert(VersionNames.size() == RNewer - ROlder - 1); get_line(); + get_line(); + auto first = VersionNames.cbegin(); + auto last = VersionNames.cend(); + auto found = std::lower_bound(first, last, m_str); + if (found == last) { + m_version = RNewer; + } + else if (*found == m_str) { + m_version = (eDXFVersion_t)(std::distance(first, found) + (ROlder + 1)); + } + else if (found == first) { + m_version = ROlder; + } + else { + m_version = RUnknown; + } - while(!m_ifs.eof()) - { - m_aci = 256; + return ResolveEncoding(); +} - if (!strcmp( m_str, "$INSUNITS" )){ - if (!ReadUnits())return; - continue; - } // End if - then +bool CDxfRead::ReadDwgCodePage() +{ + get_line(); + get_line(); + m_CodePage = m_str; - if (!strcmp( m_str, "$MEASUREMENT" )){ - get_line(); - get_line(); - int n = 1; - if(sscanf(m_str, "%d", &n) == 1) - { - if(n == 0)m_measurement_inch = true; - } - continue; - } // End if - then - - else if(!strcmp(m_str, "0")) - { - get_line(); - if (!strcmp( m_str, "SECTION" )){ - safe_strcpy(m_section_name, ""); - get_line(); - get_line(); - if (strcmp( m_str, "ENTITIES" )) - safe_strcpy(m_section_name, m_str); - safe_strcpy(m_block_name, ""); - - } // End if - then - else if (!strcmp( m_str, "TABLE" )){ - get_line(); - get_line(); - } - - else if (!strcmp( m_str, "LAYER" )){ - if(!ReadLayer()) - { - this->ReportError("DXF::DoRead() - Failed to read layer"); - //return; Some objects or tables can have "LAYER" as name... - } - continue; - } - - else if (!strcmp( m_str, "BLOCK" )) { - if(!ReadBlockInfo()) - { - this->ReportError("DXF::DoRead() - Failed to read block info"); - return; - } - continue; - } // End if - then - - else if (!strcmp( m_str, "ENDSEC" )){ - safe_strcpy(m_section_name, ""); - safe_strcpy(m_block_name, ""); - } // End if - then - else if(!strcmp(m_str, "LINE")){ - if(!ReadLine()) - { - this->ReportError("DXF::DoRead() - Failed to read line"); - return; - } - continue; - } - else if(!strcmp(m_str, "ARC")){ - if(!ReadArc()) - { - this->ReportError("DXF::DoRead() - Failed to read arc"); - return; - } - continue; - } - else if(!strcmp(m_str, "CIRCLE")){ - if(!ReadCircle()) - { - this->ReportError("DXF::DoRead() - Failed to read circle"); - return; - } - continue; - } - else if(!strcmp(m_str, "MTEXT")){ - if(!ReadText()) - { - this->ReportError("DXF::DoRead() - Failed to read text"); - return; - } - continue; - } - else if(!strcmp(m_str, "TEXT")){ - if(!ReadText()) - { - this->ReportError("DXF::DoRead() - Failed to read text"); - return; - } - continue; - } - else if(!strcmp(m_str, "ELLIPSE")){ - if(!ReadEllipse()) - { - this->ReportError("DXF::DoRead() - Failed to read ellipse"); - return; - } - continue; - } - else if(!strcmp(m_str, "SPLINE")){ - if(!ReadSpline()) - { - this->ReportError("DXF::DoRead() - Failed to read spline"); - return; - } - continue; - } - else if (!strcmp(m_str, "LWPOLYLINE")) { - if(!ReadLwPolyLine()) - { - this->ReportError("DXF::DoRead() - Failed to read LW Polyline"); - return; - } - continue; - } - else if (!strcmp(m_str, "POLYLINE")) { - if(!ReadPolyLine()) - { - this->ReportError("DXF::DoRead() - Failed to read Polyline"); + return ResolveEncoding(); +} + +bool CDxfRead::ResolveEncoding() +{ + // + // See https://ezdxf.readthedocs.io/en/stable/dxfinternals/fileencoding.html# + // + + if (m_version >= R2007) { // Note this does not include RUnknown, but does include RLater + return this->setSourceEncoding("UTF8"); + } + else { + std::transform(m_CodePage.cbegin(), m_CodePage.cend(), m_CodePage.begin(), [](char c) { + return std::toupper(c, std::locale::classic()); + }); + // ANSI_1252 by default if $DWGCODEPAGE is not set + if (m_CodePage.empty()) + m_CodePage = "ANSI_1252"; + + return this->setSourceEncoding(m_CodePage); + } +} + +void CDxfRead::HandleCommonGroupCode(int n) +{ + switch (n) { + case 8: + // layer name + m_layer_name = m_str; + break; + case 62: + // color index + m_ColorIndex = stringToInt(m_str); + break; + } +} + +void CDxfRead::DoRead(bool ignore_errors) +{ + m_ignore_errors = ignore_errors; + m_gcount = 0; + if (m_fail) + return; + + std::unordered_map> mapHeaderVarHandler; + mapHeaderVarHandler.insert({ "$INSUNITS", [=]{ return ReadInsUnits(); } }); + mapHeaderVarHandler.insert({ "$MEASUREMENT", [=]{ return ReadMeasurement(); } }); + mapHeaderVarHandler.insert({ "$ACADVER", [=]{ return ReadAcadVer(); } }); + mapHeaderVarHandler.insert({ "$DWGCODEPAGE", [=]{ return ReadDwgCodePage(); } }); + + std::unordered_map> mapEntityHandler; + mapEntityHandler.insert({ "ARC", [=]{ return ReadArc(); } }); + mapEntityHandler.insert({ "BLOCK", [=]{ return ReadBlockInfo(); } }); + mapEntityHandler.insert({ "CIRCLE", [=]{ return ReadCircle(); } }); + mapEntityHandler.insert({ "DIMENSION", [=]{ return ReadDimension(); } }); + mapEntityHandler.insert({ "ELLIPSE", [=]{ return ReadEllipse(); } }); + mapEntityHandler.insert({ "INSERT", [=]{ return ReadInsert(); } }); + mapEntityHandler.insert({ "LAYER", [=]{ return ReadLayer(); } }); + mapEntityHandler.insert({ "LINE", [=]{ return ReadLine(); } }); + mapEntityHandler.insert({ "LWPOLYLINE", [=]{ return ReadLwPolyLine(); } }); + mapEntityHandler.insert({ "MTEXT", [=]{ return ReadMText(); } }); + mapEntityHandler.insert({ "POINT", [=]{ return ReadPoint(); } }); + mapEntityHandler.insert({ "POLYLINE", [=]{ return ReadPolyLine(); } }); + mapEntityHandler.insert({ "SECTION", [=]{ return ReadSection(); } }); + mapEntityHandler.insert({ "SOLID", [=]{ return ReadSolid(); } }); + mapEntityHandler.insert({ "SPLINE", [=]{ return ReadSpline(); } }); + mapEntityHandler.insert({ "STYLE", [=]{ return ReadStyle(); } }); + mapEntityHandler.insert({ "TEXT", [=]{ return ReadText(); } }); + mapEntityHandler.insert({ "TABLE", [=]{ return ReadTable(); } }); + mapEntityHandler.insert({ "ENDSEC", [=]{ return ReadEndSec(); } }); + + get_line(); + + ScopedCLocale _(LC_NUMERIC); + while (!m_ifs.eof()) { + m_ColorIndex = ColorBylayer; // Default + + { // Handle header variable + auto itHandler = mapHeaderVarHandler.find(m_str); + if (itHandler != mapHeaderVarHandler.cend()) { + const auto& fn = itHandler->second; + if (fn()) + continue; + else return; - } - continue; } - else if (!strcmp(m_str, "POINT")) { - if(!ReadPoint()) - { - this->ReportError("DXF::DoRead() - Failed to read Point"); - return; + } + + if (m_str == "0") { + get_line(); + if (m_str == "0") + get_line(); // Skip again + + auto itHandler = mapEntityHandler.find(m_str); + if (itHandler != mapEntityHandler.cend()) { + const auto& fn = itHandler->second; + bool okRead = false; + std::string exceptionMsg; + try { + okRead = fn(); + } catch (const std::runtime_error& err) { + exceptionMsg = err.what(); } - continue; - } - else if (!strcmp(m_str, "INSERT")) { - if(!ReadInsert()) - { - this->ReportError("DXF::DoRead() - Failed to read Insert"); - return; + + if (okRead) { + continue; } - continue; - } - else if (!strcmp(m_str, "DIMENSION")) { - if(!ReadDimension()) - { - this->ReportError("DXF::DoRead() - Failed to read Dimension"); - return; + else { + std::string errMsg = "DXF::DoRead() - Failed to read " + m_str; + if (!exceptionMsg.empty()) + errMsg += "\nError: " + exceptionMsg; + + this->ReportError(errMsg); + if (m_str == "LAYER") // Some objects or tables can have "LAYER" as name... + continue; + else + return; } - continue; } } get_line(); } + AddGraphics(); } -void CDxfRead::DerefACI() +void CDxfRead::ResolveColorIndex() { - - if (m_aci == 256) // if color = layer color, replace by color from layer - { - m_aci = m_layer_aci[std::string(m_layer_name)]; - } + if (m_ColorIndex == ColorBylayer) // if color = layer color, replace by color from layer + m_ColorIndex = m_layer_ColorIndex_map[m_layer_name]; } void CDxfRead::ReportError_readInteger(const char* context) @@ -3422,29 +3530,28 @@ void CDxfRead::ReportError_readInteger(const char* context) std::streamsize CDxfRead::gcount() const { - return m_ifs.gcount(); + // std::getline() doesn't affect std::istream::gcount + //return m_ifs.gcount(); + return m_gcount; } std::string CDxfRead::LayerName() const { std::string result; - if (strlen(m_section_name) > 0) - { + if (!m_section_name.empty()) { result.append(m_section_name); result.append(" "); } - if (strlen(m_block_name) > 0) - { + if (!m_block_name.empty()) { result.append(m_block_name); result.append(" "); } - if (strlen(m_layer_name) > 0) - { + if (!m_layer_name.empty()) { result.append(m_layer_name); } - return(result); + return result; } diff --git a/src/io_dxf/dxf.h b/src/io_dxf/dxf.h index 9cd8b05e..39344d83 100644 --- a/src/io_dxf/dxf.h +++ b/src/io_dxf/dxf.h @@ -3,30 +3,28 @@ // This program is released under the BSD license. See the file COPYING for details. // modified 2018 wandererfan -// MAYO: file initially taken from FreeCad/src/Mod/Import/App/dxf.h -- commit #47d5707 +// MAYO: file taken from FreeCad/src/Mod/Import/App/dxf.h -- commit #55292e9 #pragma once #include +#include +#include +#include +#include +#include #include -#include -#include +#include +#include #include -#include #include -#include -#include -#include -#include +#include +#include +#include #include "freecad.h" -//Following is required to be defined on Ubuntu with OCC 6.3.1 -#ifndef HAVE_IOSTREAM -#define HAVE_IOSTREAM -#endif - -typedef int Aci_t; // AutoCAD color index +typedef int ColorIndex_t; // DXF color index typedef enum { @@ -53,11 +51,233 @@ typedef enum eParsecs } eDxfUnits_t; +struct DxfCoords { + double x; + double y; + double z; +}; + +struct DxfScale { + double x; + double y; + double z; +}; + +struct Dxf_STYLE { + // Code: 2 + std::string name; + // Code: 40 + double fixedTextHeight = 0; + // Code: 41 + double widthFactor = 1.; + // Code: 50 + // AutoCad documentation doesn't specify units, but "Group Codes in Numerical Order" section + // states that codes 50-58 are in degrees + double obliqueAngle = 0.; + // Code: 3 + std::string primaryFontFileName; + // Code: 4 + std::string bigFontFileName; + + // TODO Code 70(standard flag values) + // TODO Code 71(text generation flags) + // TODO Code 42(last height used) +}; + +struct Dxf_TEXT { + // Code: 39 + double thickness = 0.; + // Code: 10, 20, 30 + DxfCoords firstAlignmentPoint = {}; + // Code: 40 + double height = 0.; + // Code: 1 + std::string str; + // Code: 50 + // AutoCad documentation doesn't specify units, but "Group Codes in Numerical Order" section + // states that codes 50-58 are in degrees + double rotationAngle = 0.; + // Code: 41 + // "This value is also adjusted when fit-type text is used" + double relativeXScaleFactorWidth = 1.; + // Code: 51 + // AutoCad documentation doesn't specify units, but "Group Codes in Numerical Order" section + // states that codes 50-58 are in degrees + double obliqueAngle = 0.; + // Code: 7 + std::string styleName; + + // TODO Code 71(text generation flags) + + enum class HorizontalJustification { + Left = 0, Center = 1, Right = 2, Aligned = 3, Middle = 4, Fit = 5 + }; + // Code: 72 + HorizontalJustification horizontalJustification = HorizontalJustification::Left; + // Code: 11, 21, 31 + DxfCoords secondAlignmentPoint = {}; + // Code: 210, 220, 230 + DxfCoords extrusionDirection = {0., 0., 1.}; + + enum class VerticalJustification { + Baseline = 0, Bottom = 1, Middle = 2, Top = 3 + }; + // Code: 73 + VerticalJustification verticalJustification = VerticalJustification::Baseline; +}; + +struct Dxf_MTEXT { + enum class AttachmentPoint { + TopLeft = 1, TopCenter, TopRight, + MiddleLeft, MiddleCenter, MiddleRight, + BottomLeft, BottomCenter, BottomRight + }; + + // Code: 10, 20, 30 + DxfCoords insertionPoint = {}; + // Code: 40 + double height = 0.; + // Code: 41 + double referenceRectangleWidth = 0; + // Code 71 + AttachmentPoint attachmentPoint = AttachmentPoint::TopLeft; + + // TODO Code 72(drawing direction) + + // Code: 1, 3 + std::string str; + + // TODO Code 7(text sytle name) + + // Code: 210, 220, 230 + DxfCoords extrusionDirection = {0., 0., 1.}; + + // Code: 11, 21, 31 + DxfCoords xAxisDirectionVector; // WCS + + // NOTE AutoCad documentation states that codes 42, 43 are "read-only, ignored if supplied" + + double rotationAngle = 0.; // radians(AutoCad documentation) + + // TODO Code 73(line spacing style) + // TODO Code 44(line spacing factor) + // TODO Code 90(background fill setting) + // TODO Code 420-429(background color, if RGB) + // TODO Code 430-439(background color, if name) + // TODO Code 45(fill box scale) + // TODO Code 63(background fill color) + // TODO Code 441(transparency of background fill color) + // TODO Codes for columns: 75, 76, 78, 79, 48, 49, 50 + + enum class ColumnType { None = 0, Static, Dynamic }; + bool acadHasColumnInfo = false; + ColumnType acadColumnInfo_Type = ColumnType::None; + bool acadColumnInfo_AutoHeight = false; + int acadColumnInfo_Count = 0; + bool acadColumnInfo_FlowReversed = false; + double acadColumnInfo_Width = 0.; + double acadColumnInfo_GutterWidth = 0.; + + bool acadHasDefinedHeight = false; + double acadDefinedHeight = 0.; + +}; + +struct Dxf_VERTEX { + enum class Bulge { StraightSegment = 0, SemiCircle = 1 }; + enum Flag { + None = 0, + ExtraVertex = 1, + CurveFitTangent = 2, + NotUsed = 4, + SplineVertex = 8, + SplineFrameControlPoint = 16, + Polyline3dVertex = 32, + Polygon3dVertex = 64, + PolyfaceMesgVertex = 128 + }; + using Flags = unsigned; + + DxfCoords point = {}; + Bulge bulge = Bulge::StraightSegment; + Flags flags = Flag::None; +}; + +struct Dxf_POLYLINE { + enum Flag { + None = 0, + Closed = 1, + CurveFit = 2, + SplineFit = 4, + Polyline3d = 8, + PolygonMesh3d = 16, + PolygonMeshClosedNDir = 32, + PolyfaceMesh = 64, + ContinuousLinetypePattern = 128 + }; + using Flags = unsigned; + + enum Type { + NoSmoothSurfaceFitted = 0, + QuadraticBSplineSurface = 5, + CubicBSplineSurface = 6, + BezierSurface = 8 + }; + + double elevation = 0.; + double thickness = 0.; + Flags flags = Flag::None; + double defaultStartWidth = 0.; + double defaultEndWidth = 0.; + int polygonMeshMVertexCount = 0; + int polygonMeshNVertexCount = 0; + double smoothSurfaceMDensity = 0.; + double smoothSurfaceNDensity = 0.; + double extrusionDir[3] = { 0., 0., 1. }; + std::vector vertices; +}; + +struct Dxf_INSERT { + // Code: 2 + std::string blockName; + // Code: 10, 20, 30 + DxfCoords insertPoint = {}; // OCS + // Code: 41, 42, 43 + DxfScale scaleFactor = { 1., 1., 1. }; + // Code: 50 + double rotationAngle = 0.; + // Code: 70 + int columnCount = 1; + // Code: 71 + int rowCount = 1; + // Code: 44 + double columnSpacing = 0.; + // Code: 45 + double rowSpacing = 0.; + // Code: 210, 220, 230 + DxfCoords extrusionDirection = { 0., 0., 1. }; +}; + +struct Dxf_SOLID { + // Code: 10, 20, 30 + DxfCoords corner1; + // Code: 11, 21, 31 + DxfCoords corner2; + // Code: 12, 22, 32 + DxfCoords corner3; + // Code: 13, 23, 33 + DxfCoords corner4; + bool hasCorner4 = false; + // Code: 39 + double thickness = 0.; + // Code: 210, 220, 230 + DxfCoords extrusionDirection = { 0., 0., 1. }; +}; //spline data for reading struct SplineData { - double norm[3]; + DxfCoords norm; int degree; int knots; int control_points; @@ -118,9 +338,26 @@ struct LWPolyDataOut std::vector Bulge; point3D Extr; }; +typedef enum +{ + RUnknown, + ROlder, + R10, + R11_12, + R13, + R14, + R2000, + R2004, + R2007, + R2010, + R2013, + R2018, + RNewer, +} eDXFVersion_t; //******************** -class CDxfWrite{ +class CDxfWrite +{ private: std::ofstream m_ofs; bool m_fail; @@ -130,29 +367,45 @@ class CDxfWrite{ std::ostringstream m_ssLayer; protected: - void putLine(const Base::Vector3d& s, const Base::Vector3d& e, - std::ostringstream& outStream, const std::string& handle, + void putLine(const Base::Vector3d& s, + const Base::Vector3d& e, + std::ostringstream& outStream, + const std::string& handle, const std::string& ownerHandle); - void 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, + void 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 putArrow(const Base::Vector3d& arrowPos, const Base::Vector3d& barb1Pos, const Base::Vector3d& barb2Pos, - std::ostringstream& outStream, const std::string& handle, + void putArrow(const Base::Vector3d& arrowPos, + const Base::Vector3d& barb1Pos, + const Base::Vector3d& barb2Pos, + std::ostringstream& outStream, + const std::string& handle, const std::string& ownerHandle); //! copy boiler plate file std::string getPlateFile(const std::string& fileSpec); - void setDataDir(const std::string& s) { m_dataDir = s; } - std::string getHandle(void); - std::string getEntityHandle(void); - std::string getLayerHandle(void); - std::string getBlockHandle(void); - std::string getBlkRecordHandle(void); + void setDataDir(const std::string& s) + { + m_dataDir = s; + } + std::string getHandle(); + std::string getEntityHandle(); + std::string getLayerHandle(); + std::string getBlockHandle(); + std::string getBlkRecordHandle(); std::string m_optionSource; int m_version; int m_handle; + int m_entityHandle; + int m_layerHandle; + int m_blockHandle; + int m_blkRecordHandle; bool m_polyOverride; std::string m_saveModelSpaceHandle; @@ -170,94 +423,165 @@ class CDxfWrite{ CDxfWrite(const char* filepath); ~CDxfWrite(); - void init(void); - void endRun(void); + void init(); + void endRun(); - bool Failed(){return m_fail;} + bool Failed() + { + return m_fail; + } // void setOptions(void); // bool isVersionValid(int vers); - std::string getLayerName() { return m_layerName; } + std::string getLayerName() + { + return m_layerName; + } void setLayerName(std::string s); - void setVersion(int v) { m_version = v;} - void setPolyOverride(bool b) { m_polyOverride = b; } + void setVersion(int v) + { + m_version = v; + } + void setPolyOverride(bool b) + { + m_polyOverride = b; + } void addBlockName(std::string s, std::string blkRecordHandle); void writeLine(const double* s, const double* e); void writePoint(const double*); void writeArc(const double* s, const double* e, const double* c, bool dir); - void writeEllipse(const double* c, double major_radius, double minor_radius, - double rotation, double start_angle, double end_angle, bool endIsCW); + void writeEllipse(const double* c, + double major_radius, + double minor_radius, + double rotation, + double start_angle, + double end_angle, + bool endIsCW); void writeCircle(const double* c, double radius ); - void writeSpline(const SplineDataOut &sd); - void writeLWPolyLine(const LWPolyDataOut &pd); - void writePolyline(const LWPolyDataOut &pd); + void writeSpline(const SplineDataOut& sd); + void writeLWPolyLine(const LWPolyDataOut& pd); + void writePolyline(const LWPolyDataOut& pd); void writeVertex(double x, double y, double z); - void writeText(const char* text, const double* location1, const double* location2, - const double height, const int horizJust); - void writeLinearDim(const double* textMidPoint, const double* lineDefPoint, - const double* extLine1, const double* extLine2, - const char* dimText, int type); - void writeLinearDimBlock(const double* textMidPoint, const double* lineDefPoint, - const double* extLine1, const double* extLine2, - const char* dimText, int type); - void writeAngularDim(const double* textMidPoint, const double* lineDefPoint, - const double* startExt1, const double* endExt1, - const double* startExt2, const double* endExt2, - const char* dimText); - void writeAngularDimBlock(const double* textMidPoint, const double* lineDefPoint, - const double* startExt1, const double* endExt1, - const double* startExt2, const double* endExt2, - const char* dimText); - void writeRadialDim(const double* centerPoint, const double* textMidPoint, - const double* arcPoint, - const char* dimText); - void writeRadialDimBlock(const double* centerPoint, const double* textMidPoint, - const double* arcPoint, const char* dimText); - void writeDiametricDim(const double* textMidPoint, - const double* arcPoint1, const double* arcPoint2, - const char* dimText); - void writeDiametricDimBlock(const double* textMidPoint, - const double* arcPoint1, const double* arcPoint2, + void writeText(const char* text, + const double* location1, + const double* location2, + const double height, + const int horizJust); + void writeLinearDim(const double* textMidPoint, + const double* lineDefPoint, + const double* extLine1, + const double* extLine2, + const char* dimText, + int type); + void writeLinearDimBlock(const double* textMidPoint, + const double* lineDefPoint, + const double* extLine1, + const double* extLine2, + const char* dimText, + int type); + void writeAngularDim(const double* textMidPoint, + const double* lineDefPoint, + const double* startExt1, + const double* endExt1, + const double* startExt2, + const double* endExt2, const char* dimText); + void writeAngularDimBlock(const double* textMidPoint, + const double* lineDefPoint, + const double* startExt1, + const double* endExt1, + const double* startExt2, + const double* endExt2, + const char* dimText); + void writeRadialDim(const double* centerPoint, + const double* textMidPoint, + const double* arcPoint, + const char* dimText); + void writeRadialDimBlock(const double* centerPoint, + const double* textMidPoint, + const double* arcPoint, + const char* dimText); + void writeDiametricDim(const double* textMidPoint, + const double* arcPoint1, + const double* arcPoint2, + const char* dimText); + void writeDiametricDimBlock(const double* textMidPoint, + const double* arcPoint1, + const double* arcPoint2, + const char* dimText); void writeDimBlockPreamble(); - void writeBlockTrailer(void); - - void writeHeaderSection(void); - void writeTablesSection(void); - void writeBlocksSection(void); - void writeEntitiesSection(void); - void writeObjectsSection(void); - void writeClassesSection(void); - - void makeLayerTable(void); - void makeBlockRecordTableHead(void); - void makeBlockRecordTableBody(void); - void makeBlockSectionHead(void); + void writeBlockTrailer(); + + void writeHeaderSection(); + void writeTablesSection(); + void writeBlocksSection(); + void writeEntitiesSection(); + void writeObjectsSection(); + void writeClassesSection(); + + void makeLayerTable(); + void makeBlockRecordTableHead(); + void makeBlockRecordTableBody(); + void makeBlockSectionHead(); }; +namespace DxfPrivate { + +enum class StringToErrorMode { Throw = 0x1, ReturnErrorValue = 0x2 }; + +double stringToDouble( + const std::string& line, + StringToErrorMode errorMode = StringToErrorMode::Throw +); + +int stringToInt( + const std::string& line, + StringToErrorMode errorMode = StringToErrorMode::Throw +); + +unsigned stringToUnsigned( + const std::string& line, + StringToErrorMode errorMode = StringToErrorMode::Throw +); + +} // namespace DxfPrivate + // derive a class from this and implement it's virtual functions -class CDxfRead{ +class CDxfRead +{ private: std::ifstream m_ifs; bool m_fail; - char m_str[1024]; - char m_unused_line[1024]; + std::string m_str; + std::string m_unused_line; eDxfUnits_t m_eUnits; bool m_measurement_inch; - char m_layer_name[1024]; - char m_section_name[1024]; - char m_block_name[1024]; + std::string m_layer_name; + std::string m_section_name; + std::string m_block_name; bool m_ignore_errors; + std::streamsize m_gcount = 0; + int m_line_nb = 0; + + // Mapping from layer name -> layer color index + std::unordered_map m_layer_ColorIndex_map; + const ColorIndex_t ColorBylayer = 256; + + // Map styleName to Style object + std::unordered_map m_mapStyle; - typedef std::map< std::string,Aci_t > LayerAciMap_t; - LayerAciMap_t m_layer_aci; // layer names -> layer color aci map + bool ReadInsUnits(); + bool ReadMeasurement(); + bool ReadAcadVer(); + bool ReadDwgCodePage(); - bool ReadUnits(); bool ReadLayer(); + bool ReadStyle(); bool ReadLine(); + bool ReadMText(); bool ReadText(); bool ReadArc(); bool ReadCircle(); @@ -266,49 +590,124 @@ class CDxfRead{ bool ReadSpline(); bool ReadLwPolyLine(); bool ReadPolyLine(); - bool ReadVertex(double *pVertex, bool *bulge_found, double *bulge); - void OnReadArc(double start_angle, double end_angle, double radius, const double* c, double z_extrusion_dir, bool hidden); - void OnReadCircle(const double* c, double radius, bool hidden); - void OnReadEllipse(const double* c, const double* m, double ratio, double start_angle, double end_angle); + bool ReadVertex(Dxf_VERTEX* vertex); + bool ReadSolid(); + bool ReadSection(); + bool ReadTable(); + bool ReadEndSec(); + + void OnReadArc( + double start_angle, + double end_angle, + double radius, + const DxfCoords& c, + double z_extrusion_dir, + bool hidden + ); + void OnReadCircle(const DxfCoords& c, double radius, bool hidden); + void OnReadEllipse( + const DxfCoords& c, + const DxfCoords& m, + double ratio, + double start_angle, + double end_angle + ); bool ReadInsert(); bool ReadDimension(); bool ReadBlockInfo(); - void put_line(const char *value); - void DerefACI(); + bool ResolveEncoding(); + + template + void HandleCoordCode(int n, DxfCoords* coords) + { + switch (n) { + case XCode: + coords->x = mm(DxfPrivate::stringToDouble(m_str)); + break; + case YCode: + coords->y = mm(DxfPrivate::stringToDouble(m_str)); + break; + case ZCode: + coords->z = mm(DxfPrivate::stringToDouble(m_str)); + break; + } + } + + void HandleCommonGroupCode(int n); + + void put_line(const std::string& value); + void ResolveColorIndex(); void ReportError_readInteger(const char* context); protected: - Aci_t m_aci; // manifest color name or 256 for layer color - int m_lineNum = 0; + ColorIndex_t m_ColorIndex; + eDXFVersion_t m_version; // Version from $ACADVER variable in DXF std::streamsize gcount() const; virtual void get_line(); - virtual void ReportError(const char* /*msg*/) {} + virtual void ReportError(const std::string& /*msg*/) {} + + virtual bool setSourceEncoding(const std::string& /*codepage*/) { return true; } + virtual std::string toUtf8(const std::string& strSource) { return strSource; } + +private: + std::string m_CodePage; // Code Page name from $DWGCODEPAGE or null if none/not read yet public: CDxfRead(const char* filepath); // this opens the file virtual ~CDxfRead(); // this closes the file - bool Failed(){return m_fail;} - void DoRead(const bool ignore_errors = false); // this reads the file and calls the following functions + bool IgnoreErrors() const { return m_ignore_errors; } + bool Failed() const { return m_fail; } - double mm( double value ) const; + double mm(double value) const; - bool IgnoreErrors() const { return(m_ignore_errors); } + const Dxf_STYLE* findStyle(const std::string& name) const; - virtual void OnReadLine(const double* /*s*/, const double* /*e*/, bool /*hidden*/){} - virtual void OnReadPoint(const double* /*s*/){} - virtual void OnReadText(const double* /*point*/, const double /*height*/, double /*rotation*/, const char* /*text*/){} - virtual void OnReadArc(const double* /*s*/, const double* /*e*/, const double* /*c*/, bool /*dir*/, bool /*hidden*/){} - virtual void OnReadCircle(const double* /*s*/, const double* /*c*/, bool /*dir*/, bool /*hidden*/){} - virtual void OnReadEllipse(const double* /*c*/, double /*major_radius*/, double /*minor_radius*/, double /*rotation*/, double /*start_angle*/, double /*end_angle*/, bool /*dir*/){} - virtual void OnReadSpline(struct SplineData& /*sd*/){} - virtual void OnReadInsert(const double* /*point*/, const double* /*scale*/, const char* /*name*/, double /*rotation*/){} - virtual void OnReadDimension(const double* /*s*/, const double* /*e*/, const double* /*point*/, double /*rotation*/){} - virtual void AddGraphics() const { } + void DoRead(bool ignore_errors = false); // this reads the file and calls the following functions - std::string LayerName() const; + virtual void OnReadLine(const DxfCoords& s, const DxfCoords& e, bool hidden) = 0; + + virtual void OnReadPolyline(const Dxf_POLYLINE&) = 0; + + virtual void OnReadPoint(const DxfCoords& s) = 0; + + virtual void OnReadText(const Dxf_TEXT&) = 0; + + virtual void OnReadMText(const Dxf_MTEXT&) = 0; + + virtual void OnReadArc( + const DxfCoords& s, const DxfCoords& e, const DxfCoords& c, bool dir, bool hidden + ) = 0; + virtual void OnReadCircle(const DxfCoords& s, const DxfCoords& c, bool dir, bool hidden) = 0; + + virtual void OnReadEllipse( + const DxfCoords& c, + double major_radius, + double minor_radius, + double rotation, + double start_angle, + double end_angle, + bool dir + ) = 0; + + virtual void OnReadSpline(struct SplineData& sd) = 0; + + virtual void OnReadInsert(const Dxf_INSERT& ins) = 0; + + virtual void OnReadSolid(const Dxf_SOLID& solid) = 0; + + virtual void OnReadDimension( + const DxfCoords& s, + const DxfCoords& e, + const DxfCoords& point, + double rotation + ) = 0; + + virtual void AddGraphics() const = 0; + + std::string LayerName() const; }; diff --git a/src/io_dxf/io_dxf.cpp b/src/io_dxf/io_dxf.cpp index 1d7b29da..097587b1 100644 --- a/src/io_dxf/io_dxf.cpp +++ b/src/io_dxf/io_dxf.cpp @@ -11,11 +11,13 @@ #include "../base/document.h" #include "../base/filepath.h" #include "../base/math_utils.h" +#include "../base/mesh_utils.h" #include "../base/messenger.h" #include "../base/property_builtins.h" #include "../base/property_enumeration.h" #include "../base/task_progress.h" #include "../base/string_conv.h" +#include "../base/tkernel_utils.h" #include "../base/unit_system.h" #include "aci_table.h" #include "dxf.h" @@ -25,20 +27,29 @@ #include #include #include +#include #include +#include +#include +#include +#include #include #include #include #include #include +#include #include #include #include #include +#include #include #include +#include + namespace Mayo { namespace IO { @@ -49,6 +60,15 @@ bool startsWith(std::string_view str, std::string_view prefix) return str.substr(0, prefix.size()) == prefix; } +std::string toLowerCase_C(const std::string& str) +{ + std::string lstr = str; + for (char& c : lstr) + c = std::tolower(c, std::locale::classic()); + + return lstr; +} + const Enumeration& systemFontNames() { static Enumeration fontNames; @@ -74,9 +94,12 @@ class DxfReader::Internal : public CDxfRead { TaskProgress* m_progress = nullptr; std::uintmax_t m_fileSize = 0; std::uintmax_t m_fileReadSize = 0; + Resource_FormatType m_srcEncoding = Resource_ANSI; protected: void get_line() override; + bool setSourceEncoding(const std::string& codepage) override; + std::string toUtf8(const std::string& strSource) override; public: Internal(const FilePath& filepath, TaskProgress* progress = nullptr); @@ -86,22 +109,26 @@ class DxfReader::Internal : public CDxfRead { const auto& layers() const { return m_layers; } // CDxfRead's virtual functions - void OnReadLine(const double* s, const double* e, bool hidden) override; - void OnReadPoint(const double* s) override; - void OnReadText(const double* point, const double height, double rotation, const char* text) override; - void OnReadArc(const double* s, const double* e, const double* c, bool dir, bool hidden) override; - void OnReadCircle(const double* s, const double* c, bool dir, bool hidden) override; - void OnReadEllipse(const double* c, double major_radius, double minor_radius, double rotation, double start_angle, double end_angle, bool dir) override; + void OnReadLine(const DxfCoords& s, const DxfCoords& e, bool hidden) override; + void OnReadPolyline(const Dxf_POLYLINE& polyline) override; + void OnReadPoint(const DxfCoords& s) override; + void OnReadText(const Dxf_TEXT& text) override; + void OnReadMText(const Dxf_MTEXT& text) override; + void OnReadArc(const DxfCoords& s, const DxfCoords& e, const DxfCoords& c, bool dir, bool hidden) override; + void OnReadCircle(const DxfCoords& s, const DxfCoords& c, bool dir, bool hidden) override; + void OnReadEllipse(const DxfCoords& c, double major_radius, double minor_radius, double rotation, double start_angle, double end_angle, bool dir) override; void OnReadSpline(struct SplineData& sd) override; - void OnReadInsert(const double* point, const double* scale, const char* name, double rotation) override; - void OnReadDimension(const double* s, const double* e, const double* point, double rotation) override; + void OnReadInsert(const Dxf_INSERT& ins) override; + void OnReadDimension(const DxfCoords& s, const DxfCoords& e, const DxfCoords& point, double rotation) override; + void OnReadSolid(const Dxf_SOLID& solid) override; - void ReportError(const char* msg) override; + void ReportError(const std::string& msg) override; + void AddGraphics() const override; static Handle_Geom_BSplineCurve createSplineFromPolesAndKnots(struct SplineData& sd); static Handle_Geom_BSplineCurve createInterpolationSpline(struct SplineData& sd); - gp_Pnt toPnt(const double* coords) const; + gp_Pnt toPnt(const DxfCoords& coords) const; void addShape(const TopoDS_Shape& shape); }; @@ -153,7 +180,7 @@ TDF_LabelSequence DxfReader::transfer(DocumentPtr doc, TaskProgress* progress) Handle_XCAFDoc_ColorTool colorTool = doc->xcaf().colorTool(); Handle_XCAFDoc_LayerTool layerTool = doc->xcaf().layerTool(); std::unordered_map mapLayerNameLabel; - std::unordered_map mapAciColorLabel; + std::unordered_map mapAciColorLabel; auto fnAddRootShape = [&](const TopoDS_Shape& shape, const std::string& shapeName, TDF_Label layer) { const TDF_Label labelShape = shapeTool->NewShape(); @@ -166,7 +193,7 @@ TDF_LabelSequence DxfReader::transfer(DocumentPtr doc, TaskProgress* progress) return labelShape; }; - auto fnAddAci = [&](Aci_t aci) { + auto fnAddAci = [&](ColorIndex_t aci) -> TDF_Label { auto it = mapAciColorLabel.find(aci); if (it != mapAciColorLabel.cend()) return it->second; @@ -174,7 +201,8 @@ TDF_LabelSequence DxfReader::transfer(DocumentPtr doc, TaskProgress* progress) if (0 <= aci && CppUtils::cmpLess(aci, std::size(aciTable))) { const RGB_Color& c = aciTable[aci].second; const TDF_Label colorLabel = colorTool->AddColor( - Quantity_Color(c.r / 255., c.g / 255., c.b / 255., Quantity_TOC_RGB)); + Quantity_Color(c.r / 255., c.g / 255., c.b / 255., Quantity_TOC_RGB) + ); mapAciColorLabel.insert({ aci, colorLabel }); return colorLabel; } @@ -195,6 +223,12 @@ TDF_LabelSequence DxfReader::transfer(DocumentPtr doc, TaskProgress* progress) progress->setValue(MathUtils::toPercent(iShape, 0, shapeCount)); }; + auto fnSetShapeColor = [=](const TDF_Label& labelShape, int aci) { + const TDF_Label labelColor = fnAddAci(aci); + if (!labelColor.IsNull()) + colorTool->SetColor(labelShape, labelColor, XCAFDoc_ColorGen); + }; + if (!m_params.groupLayers) { for (const auto& [layerName, vecEntity] : m_layers) { if (startsWith(layerName, "BLOCKS")) @@ -225,7 +259,7 @@ TDF_LabelSequence DxfReader::transfer(DocumentPtr doc, TaskProgress* progress) const TDF_Label compLabel = fnAddRootShape(comp, layerName, layerLabel); // Check if all entities have the same color bool uniqueColor = true; - const Aci_t aci = !vecEntity.empty() ? vecEntity.front().aci : -1; + const ColorIndex_t aci = !vecEntity.empty() ? vecEntity.front().aci : -1; for (const Entity& entity : vecEntity) { uniqueColor = entity.aci == aci; if (!uniqueColor) @@ -233,13 +267,13 @@ TDF_LabelSequence DxfReader::transfer(DocumentPtr doc, TaskProgress* progress) } if (uniqueColor) { - colorTool->SetColor(compLabel, fnAddAci(aci), XCAFDoc_ColorGen); + fnSetShapeColor(compLabel, aci); } else { for (const Entity& entity : vecEntity) { if (!entity.shape.IsNull()) { const TDF_Label entityLabel = shapeTool->AddSubShape(compLabel, entity.shape); - colorTool->SetColor(entityLabel, fnAddAci(entity.aci), XCAFDoc_ColorGen); + fnSetShapeColor(entityLabel, entity.aci); } } } @@ -277,6 +311,83 @@ void DxfReader::Internal::get_line() m_progress->setValue(MathUtils::toPercent(m_fileReadSize, 0, m_fileSize)); } +bool DxfReader::Internal::setSourceEncoding(const std::string& codepage) +{ + std::optional encoding; + +#if OCC_VERSION_HEX >= OCC_VERSION_CHECK(7, 4, 0) + if (codepage == "UTF8") + encoding = Resource_FormatType_UTF8; + else if (codepage == "ANSI_932") // Japanese + encoding = Resource_FormatType_SJIS; + else if (codepage == "ANSI_936") // UnifiedChinese + encoding = Resource_FormatType_GB; + else if (codepage == "ANSI_949") // Korean + encoding = Resource_FormatType_EUC; +#endif + +#if OCC_VERSION_HEX >= OCC_VERSION_CHECK(7, 5, 0) + if (encoding) + ; // Encoding already found above + else if (codepage == "ANSI_936") // UnifiedChinese + encoding = Resource_FormatType_GBK; + else if (codepage == "ANSI_950") // TradChinese + encoding = Resource_FormatType_Big5; + else if (codepage == "ANSI_1250") + encoding = Resource_FormatType_CP1250; + else if (codepage == "ANSI_1251") + encoding = Resource_FormatType_CP1251; + else if (codepage == "ANSI_1252") + encoding = Resource_FormatType_CP1252; + else if (codepage == "ANSI_1253") + encoding = Resource_FormatType_CP1253; + else if (codepage == "ANSI_1254") + encoding = Resource_FormatType_CP1254; + else if (codepage == "ANSI_1255") + encoding = Resource_FormatType_CP1255; + else if (codepage == "ANSI_1256") + encoding = Resource_FormatType_CP1256; + else if (codepage == "ANSI_1257") + encoding = Resource_FormatType_CP1257; + else if (codepage == "ANSI_1258") + encoding = Resource_FormatType_CP1258; +#endif + +#if OCC_VERSION_HEX >= OCC_VERSION_CHECK(7, 6, 0) + if (encoding) + ; // Encoding already found above + else if (codepage == "ANSI_850" || codepage == "DOS850") + encoding = Resource_FormatType_CP850; +#endif + + if (encoding) { + m_srcEncoding = encoding.value(); + } + else { + m_srcEncoding = Resource_ANSI; + m_messenger->emitWarning("Codepage " + codepage + " not supported"); + } + + return true; +} + +std::string DxfReader::Internal::toUtf8(const std::string& strSource) +{ + if (m_srcEncoding == Resource_ANSI) // Resource_ANSI is a pass-through(OpenCascade) + return strSource; + +#if OCC_VERSION_HEX >= OCC_VERSION_CHECK(7, 4, 0) + if (m_srcEncoding == Resource_FormatType_UTF8) + return strSource; + + TCollection_ExtendedString extStr; + Resource_Unicode::ConvertFormatToUnicode(m_srcEncoding, strSource.c_str(), extStr); + return to_stdString(extStr); +#else + return strSource; +#endif +} + DxfReader::Internal::Internal(const FilePath& filepath, TaskProgress* progress) : CDxfRead(filepath.u8string().c_str()), m_progress(progress) @@ -285,7 +396,7 @@ DxfReader::Internal::Internal(const FilePath& filepath, TaskProgress* progress) m_fileSize = filepathFileSize(filepath); } -void DxfReader::Internal::OnReadLine(const double* s, const double* e, bool /*hidden*/) +void DxfReader::Internal::OnReadLine(const DxfCoords& s, const DxfCoords& e, bool /*hidden*/) { const gp_Pnt p0 = this->toPnt(s); const gp_Pnt p1 = this->toPnt(e); @@ -296,41 +407,149 @@ void DxfReader::Internal::OnReadLine(const double* s, const double* e, bool /*hi this->addShape(edge); } -void DxfReader::Internal::OnReadPoint(const double* s) +void DxfReader::Internal::OnReadPolyline(const Dxf_POLYLINE& polyline) +{ + const auto& vertices = polyline.vertices; + const bool isPolylineClosed = polyline.flags & Dxf_POLYLINE::Flag::Closed; + const int nodeCount = CppUtils::safeStaticCast(vertices.size() + (isPolylineClosed ? 1 : 0)); + MeshUtils::Polygon3dBuilder polygonBuilder(nodeCount); + for (unsigned i = 0; i < vertices.size(); ++i) + polygonBuilder.setNode(i + 1, this->toPnt(vertices.at(i).point)); + + if (isPolylineClosed) + polygonBuilder.setNode(nodeCount, this->toPnt(vertices.at(0).point)); + + polygonBuilder.finalize(); + this->addShape(BRepUtils::makeEdge(polygonBuilder.get())); +} + +void DxfReader::Internal::OnReadPoint(const DxfCoords& s) { const TopoDS_Vertex vertex = BRepBuilderAPI_MakeVertex(this->toPnt(s)); this->addShape(vertex); } -void DxfReader::Internal::OnReadText(const double* point, const double height, double rotation, const char* text) +void DxfReader::Internal::OnReadText(const Dxf_TEXT& text) { if (!m_params.importAnnotations) return; - const gp_Pnt pt = this->toPnt(point); + const gp_Pnt pt = this->toPnt(text.firstAlignmentPoint); const std::string layerName = this->LayerName(); - if (!startsWith(layerName, "BLOCKS")) { - const std::string& fontName = m_params.fontNameForTextObjects; - const double fontHeight = 4 * height * m_params.scaling; - Font_BRepFont brepFont; - if (brepFont.Init(fontName.c_str(), Font_FA_Regular, fontHeight)) { - gp_Trsf rotTrsf; - if (rotation != 0.) - rotTrsf.SetRotation(gp_Ax1(pt, gp::DZ()), UnitSystem::radians(rotation * Quantity_Degree)); - - const gp_Ax3 locText(pt, gp::DZ(), gp::DX().Transformed(rotTrsf)); - Font_BRepTextBuilder brepTextBuilder; - const TopoDS_Shape shapeText = brepTextBuilder.Perform(brepFont, text, locText); - this->addShape(shapeText); - } - else { - m_messenger->emitWarning(fmt::format("Font_BRepFont is null for '{}'", fontName)); - } + if (startsWith(layerName, "BLOCKS")) + return; + + const Dxf_STYLE* ptrStyle = this->findStyle(text.styleName); + std::string fontName = ptrStyle ? ptrStyle->name : m_params.fontNameForTextObjects; + // "ARIAL_NARROW" -> "ARIAL NARROW" + if (toLowerCase_C(fontName) == "arial_narrow") + fontName.replace(5, 1, " "); + + const double fontHeight = 1.4 * text.height * m_params.scaling; + Font_BRepFont brepFont; + brepFont.SetWidthScaling(static_cast(text.relativeXScaleFactorWidth)); + if (!brepFont.Init(fontName.c_str(), Font_FA_Regular, fontHeight/*, Font_StrictLevel_Aliases*/)) { + m_messenger->emitWarning(fmt::format("Font_BRepFont is null for '{}'", fontName)); + return; + } + + gp_Trsf rotTrsf; + if (!MathUtils::fuzzyIsNull(text.rotationAngle)) + rotTrsf.SetRotation(gp_Ax1(pt, gp::DZ()), UnitSystem::radians(text.rotationAngle * Quantity_Degree)); + +#if 0 + // TEXT justification is subtle(eg baseline and fit modes) + // See doc https://ezdxf.readthedocs.io/en/stable/tutorials/text.html + const Dxf_TEXT::HorizontalJustification hjust = text.horizontalJustification; + Graphic3d_HorizontalTextAlignment hAlign = Graphic3d_HTA_LEFT; + switch (hjust) { + case Dxf_TEXT::HorizontalJustification::Center: + case Dxf_TEXT::HorizontalJustification::Middle: + hAlign = Graphic3d_HTA_CENTER; + break; + case Dxf_TEXT::HorizontalJustification::Right: + hAlign = Graphic3d_HTA_RIGHT; + break; + } + + const Dxf_TEXT::VerticalJustification vjust = text.verticalJustification; + Graphic3d_VerticalTextAlignment vAlign = Graphic3d_VTA_TOP; + switch (vjust) { + case Dxf_TEXT::VerticalJustification::Bottom: + vAlign = Graphic3d_VTA_BOTTOM; + break; + case Dxf_TEXT::VerticalJustification::Middle: + vAlign = Graphic3d_VTA_CENTER; + break; + } +#endif + + const gp_Ax3 locText(pt, gp::DZ(), gp::DX().Transformed(rotTrsf)); + Font_BRepTextBuilder brepTextBuilder; + const auto textStr = string_conv(text.str); + const TopoDS_Shape shapeText = brepTextBuilder.Perform(brepFont, textStr, locText); + this->addShape(shapeText); +} + +void DxfReader::Internal::OnReadMText(const Dxf_MTEXT& text) +{ + if (!m_params.importAnnotations) + return; + + const gp_Pnt pt = this->toPnt(text.insertionPoint); + const std::string layerName = this->LayerName(); + if (startsWith(layerName, "BLOCKS")) + return; + + const std::string& fontName = m_params.fontNameForTextObjects; + const double fontHeight = 1.4 * text.height * m_params.scaling; + Font_BRepFont brepFont; + if (!brepFont.Init(fontName.c_str(), Font_FA_Regular, fontHeight)) { + m_messenger->emitWarning(fmt::format("Font_BRepFont is null for '{}'", fontName)); + return; } + + gp_Trsf rotTrsf; + if (!MathUtils::fuzzyIsNull(text.rotationAngle)) + rotTrsf.SetRotation(gp_Ax1(pt, gp::DZ()), text.rotationAngle); + + const int ap = static_cast(text.attachmentPoint); + Graphic3d_HorizontalTextAlignment hAlign = Graphic3d_HTA_LEFT; + if (ap == 2 || ap == 5 || ap == 8) + hAlign = Graphic3d_HTA_CENTER; + else if (ap == 3 || ap == 6 || ap == 9) + hAlign = Graphic3d_HTA_RIGHT; + + Graphic3d_VerticalTextAlignment vAlign = Graphic3d_VTA_TOP; + if (ap == 4 || ap == 5 || ap == 6) + vAlign = Graphic3d_VTA_CENTER; + else if (ap == 7 || ap == 8 || ap == 9) + vAlign = Graphic3d_VTA_BOTTOM; + + const auto occTextStr = string_conv(text.str); + const gp_Ax3 locText(pt, gp::DZ(), gp::DX().Transformed(rotTrsf)); + Font_BRepTextBuilder brepTextBuilder; +#if OCC_VERSION_HEX >= OCC_VERSION_CHECK(7, 5, 0) + OccHandle textFormat = new Font_TextFormatter; + textFormat->SetupAlignment(hAlign, vAlign); + textFormat->Append(occTextStr, *brepFont.FTFont()); + /* Font_TextFormatter computes weird ResultWidth() so wrapping is currently broken + if (text.acadHasColumnInfo && text.acadColumnInfo_Width > 0.) { + textFormat->SetWordWrapping(true); + textFormat->SetWrapping(text.acadColumnInfo_Width); + } + */ + textFormat->Format(); + const TopoDS_Shape shapeText = brepTextBuilder.Perform(brepFont, textFormat, locText); +#else + const TopoDS_Shape shapeText = brepTextBuilder.Perform(brepFont, occTextStr, locText, hAlign, vAlign); +#endif + + this->addShape(shapeText); } // Excerpted from FreeCad/src/Mod/Import/App/ImpExpDxf -void DxfReader::Internal::OnReadArc(const double* s, const double* e, const double* c, bool dir, bool /*hidden*/) +void DxfReader::Internal::OnReadArc(const DxfCoords& s, const DxfCoords& e, const DxfCoords& c, bool dir, bool /*hidden*/) { const gp_Pnt p0 = this->toPnt(s); const gp_Pnt p1 = this->toPnt(e); @@ -347,7 +566,7 @@ void DxfReader::Internal::OnReadArc(const double* s, const double* e, const doub } // Excerpted from FreeCad/src/Mod/Import/App/ImpExpDxf -void DxfReader::Internal::OnReadCircle(const double* s, const double* c, bool dir, bool /*hidden*/) +void DxfReader::Internal::OnReadCircle(const DxfCoords& s, const DxfCoords& c, bool dir, bool /*hidden*/) { const gp_Pnt p0 = this->toPnt(s); const gp_Dir up = dir ? gp::DZ() : -gp::DZ(); @@ -364,11 +583,12 @@ void DxfReader::Internal::OnReadCircle(const double* s, const double* c, bool di // Excerpted from FreeCad/src/Mod/Import/App/ImpExpDxf void DxfReader::Internal::OnReadEllipse( - const double* c, + const DxfCoords& c, double major_radius, double minor_radius, double rotation, double /*start_angle*/, double /*end_angle*/, - bool dir) + bool dir + ) { const gp_Dir up = dir ? gp::DZ() : -gp::DZ(); const gp_Pnt pc = this->toPnt(c); @@ -412,15 +632,14 @@ void DxfReader::Internal::OnReadSpline(SplineData& sd) } // Excerpted from FreeCad/src/Mod/Import/App/ImpExpDxf -void DxfReader::Internal::OnReadInsert(const double* point, const double* scale, const char* name, double rotation) +void DxfReader::Internal::OnReadInsert(const Dxf_INSERT& ins) { - //std::cout << "Inserting block " << name << " rotation " << rotation << " pos " << point[0] << "," << point[1] << "," << point[2] << " scale " << scale[0] << "," << scale[1] << "," << scale[2] << std::endl; - const std::string prefix = std::string("BLOCKS ") + name + " "; + const std::string prefix = "BLOCKS " + ins.blockName + " "; for (const auto& [k, vecEntity] : m_layers) { if (!startsWith(k, prefix)) continue; // Skip - TopoDS_Compound comp = BRepUtils::makeEmptyCompound(); + TopoDS_Shape comp = BRepUtils::makeEmptyCompound(); for (const DxfReader::Entity& entity : vecEntity) { if (!entity.shape.IsNull()) BRepUtils::addShape(&comp, entity.shape); @@ -429,48 +648,107 @@ void DxfReader::Internal::OnReadInsert(const double* point, const double* scale, if (comp.IsNull()) continue; // Skip - auto nonNull = [](double v) { return !MathUtils::fuzzyIsNull(v) ? v : 1.; }; - const double nscale[] = { nonNull(scale[0]), nonNull(scale[1]), nonNull(scale[2]) }; - gp_Trsf trsfScale; - trsfScale.SetValues( - nscale[0], 0, 0, 0, - 0, nscale[1], 0, 0, - 0, 0, nscale[2], 0); + if (!MathUtils::fuzzyEqual(ins.scaleFactor.x, ins.scaleFactor.y) + || !MathUtils::fuzzyEqual(ins.scaleFactor.x, ins.scaleFactor.y) + ) + { + m_messenger->emitWarning( + fmt::format("OnReadInsert('{}') - non-uniform scales aren't supported({}, {}, {})", + ins.blockName, ins.scaleFactor.x, ins.scaleFactor.y, ins.scaleFactor.z + ) + ); + } + + auto fnNonNull = [](double v) { return !MathUtils::fuzzyIsNull(v) ? v : 1.; }; + const double avgScale = std::abs(fnNonNull((ins.scaleFactor.x + ins.scaleFactor.y + ins.scaleFactor.z) / 3.)); + if (!MathUtils::fuzzyEqual(avgScale, 1.)) { + gp_Trsf trsf; + trsf.SetScaleFactor(avgScale); + BRepBuilderAPI_Transform brepTrsf(comp, trsf); + if (brepTrsf.IsDone()) { + comp = brepTrsf.Shape(); + } + else { + m_messenger->emitWarning( + fmt::format("OnReadInsert('{}') - scaling failed({}, {}, {})", + ins.blockName, ins.scaleFactor.x, ins.scaleFactor.y, ins.scaleFactor.z + ) + ); + } + } + gp_Trsf trsfRotZ; - trsfRotZ.SetRotation(gp::OZ(), rotation); + if (!MathUtils::fuzzyIsNull(ins.rotationAngle)) + trsfRotZ.SetRotation(gp::OZ(), ins.rotationAngle); + gp_Trsf trsfMove; - trsfMove.SetTranslation(this->toPnt(point).XYZ()); - const gp_Trsf trsf = trsfScale * trsfRotZ * trsfMove; - comp.Location(trsf); + trsfMove.SetTranslation(this->toPnt(ins.insertPoint).XYZ()); + + comp.Location(trsfRotZ * trsfMove); this->addShape(comp); } } -void DxfReader::Internal::OnReadDimension(const double* s, const double* e, const double* point, double rotation) +void DxfReader::Internal::OnReadDimension(const DxfCoords& s, const DxfCoords& e, const DxfCoords& point, double rotation) { if (m_params.importAnnotations) { // TODO std::stringstream sstr; sstr << "DxfReader::OnReadDimension() - Not yet implemented" << std::endl - << " s: " << s[0] << ", " << s[1] << ", " << s[2] << std::endl - << " e: " << e[0] << ", " << e[1] << ", " << e[2] << std::endl - << " point: " << point[0] << ", " << point[1] << ", " << point[2] << std::endl + << " s: " << s.x << ", " << s.y << ", " << s.z << std::endl + << " e: " << e.x << ", " << e.y << ", " << e.z << std::endl + << " point: " << point.x << ", " << point.y << ", " << point.z << std::endl << " rotation: " << rotation << std::endl; m_messenger->emitWarning(sstr.str()); } } -void DxfReader::Internal::ReportError(const char* msg) +void DxfReader::Internal::OnReadSolid(const Dxf_SOLID& solid) +{ + const gp_Pnt p1 = this->toPnt(solid.corner1); + const gp_Pnt p2 = this->toPnt(solid.corner2); + const gp_Pnt p3 = this->toPnt(solid.corner3); + const gp_Pnt p4 = this->toPnt(solid.corner4); + + TopoDS_Face face; + try { + BRepBuilderAPI_MakeWire makeWire; + makeWire.Add(BRepBuilderAPI_MakeEdge(p1, p2)); + if (solid.hasCorner4 && !p3.IsEqual(p4, Precision::Confusion())) { + makeWire.Add(BRepBuilderAPI_MakeEdge(p2, p4)); + makeWire.Add(BRepBuilderAPI_MakeEdge(p4, p3)); + } + else { + makeWire.Add(BRepBuilderAPI_MakeEdge(p2, p3)); + } + + makeWire.Add(BRepBuilderAPI_MakeEdge(p3, p1)); + if (makeWire.IsDone()) + face = BRepBuilderAPI_MakeFace(makeWire.Wire(), true/*onlyPlane*/); + } catch (...) { + m_messenger->emitError("OnReadSolid() failed"); + } + + if (!face.IsNull()) + this->addShape(face); +} + +void DxfReader::Internal::ReportError(const std::string& msg) { m_messenger->emitError(msg); } -gp_Pnt DxfReader::Internal::toPnt(const double* coords) const +void DxfReader::Internal::AddGraphics() const +{ + // Nothing +} + +gp_Pnt DxfReader::Internal::toPnt(const DxfCoords& coords) const { - double sp1(coords[0]); - double sp2(coords[1]); - double sp3(coords[2]); - if (m_params.scaling != 1.0) { + double sp1(coords.x); + double sp2(coords.y); + double sp3(coords.z); + if (!MathUtils::fuzzyEqual(m_params.scaling, 1.)) { sp1 = sp1 * m_params.scaling; sp2 = sp2 * m_params.scaling; sp3 = sp3 * m_params.scaling; @@ -481,7 +759,7 @@ gp_Pnt DxfReader::Internal::toPnt(const double* coords) const void DxfReader::Internal::addShape(const TopoDS_Shape& shape) { - const Entity newEntity{ m_aci, shape }; + const Entity newEntity{ m_ColorIndex, shape }; const std::string layerName = this->LayerName(); auto itFound = m_layers.find(layerName); if (itFound != m_layers.end()) { diff --git a/src/io_off/io_off_reader.h b/src/io_off/io_off_reader.h index eba56187..c5e319f9 100644 --- a/src/io_off/io_off_reader.h +++ b/src/io_off/io_off_reader.h @@ -11,7 +11,6 @@ #include #include - #include namespace Mayo {