Files
create/src/Mod/Import/App/dxf/dxf.h

949 lines
35 KiB
C++

// dxf.h
// Copyright (c) 2009, Dan Heeks
// This program is released under the BSD license. See the file COPYING for details.
// modified 2018 wandererfan
#ifndef Included_dxf_h_
#define Included_dxf_h_
#ifdef _MSC_VER
#pragma warning(disable : 4251)
#endif
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iosfwd>
#include <list>
#include <map>
#include <set>
#include <string>
#include <vector>
#include <Base/Interpreter.h>
#include <Base/Matrix.h>
#include <Base/Vector3D.h>
#include <Base/Console.h>
#include <Base/Color.h>
#include <Mod/Import/ImportGlobal.h>
// For some reason Cpplint complains about some of the categories used by Clang-tidy
// However, cpplint also does not seem to use NOLINTE BEGIN and NOLINT END so we must use
// NOLINT NEXT LINE on each occurrence. [spaces added to avoid being seen by lint]
using ColorIndex_t = int; // DXF color index
// The C++ version we use does not support designated initiailzers, so we have a class to set this
// up
class DxfUnits
{
public:
using eDxfUnits_t = enum {
eUnspecified = 0, // Unspecified (No units)
eInches,
eFeet,
eMiles,
eMillimeters,
eCentimeters,
eMeters,
eKilometers,
eMicroinches,
eMils,
eYards,
eAngstroms,
eNanometers,
eMicrons,
eDecimeters,
eDekameters,
eHectometers,
eGigameters,
eAstronomicalUnits,
eLightYears,
eParsecs,
kMaxUnit
};
private:
// NOLINTNEXTLINE(readability/nolint)
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
DxfUnits()
{
// NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers)
m_factors[eInches] = 25.4;
m_factors[eFeet] = 25.4 * 12;
m_factors[eMiles] = 1609344.0;
m_factors[eMillimeters] = 1.0;
m_factors[eCentimeters] = 10.0;
m_factors[eMeters] = 1000.0;
m_factors[eKilometers] = 1000000.0;
m_factors[eMicroinches] = 25.4 / 1000.0;
m_factors[eMils] = 25.4 / 1000.0;
m_factors[eYards] = 3 * 12 * 25.4;
m_factors[eAngstroms] = 0.0000001;
m_factors[eNanometers] = 0.000001;
m_factors[eMicrons] = 0.001;
m_factors[eDecimeters] = 100.0;
m_factors[eDekameters] = 10000.0;
m_factors[eHectometers] = 100000.0;
m_factors[eGigameters] = 1000000000000.0;
m_factors[eAstronomicalUnits] = 149597870690000.0;
m_factors[eLightYears] = 9454254955500000000.0;
m_factors[eParsecs] = 30856774879000000000.0;
// NOLINTEND(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers)
}
public:
static double Factor(eDxfUnits_t enumValue)
{
// NOLINTNEXTLINE(readability/nolint)
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-constant-array-index)
return Instance.m_factors[enumValue];
}
static bool IsValid(eDxfUnits_t enumValue)
{
return enumValue > eUnspecified && enumValue <= eParsecs;
}
private:
static const DxfUnits Instance;
// NOLINTNEXTLINE(readability/nolint)
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
double m_factors[kMaxUnit];
};
// spline data for reading
struct SplineData
{
Base::Vector3d norm;
int degree = 0;
int knots = 0;
int control_points = 0;
int fit_points = 0;
int flag = 0;
std::list<double> starttanx;
std::list<double> starttany;
std::list<double> starttanz;
std::list<double> endtanx;
std::list<double> endtany;
std::list<double> endtanz;
std::list<double> knot;
std::list<double> weight;
std::list<double> controlx;
std::list<double> controly;
std::list<double> controlz;
std::list<double> fitx;
std::list<double> fity;
std::list<double> fitz;
};
//***************************
// data structures for writing
// added by Wandererfan 2018 (wandererfan@gmail.com) for FreeCAD project
struct point3D
{
double x;
double y;
double z;
};
struct SplineDataOut
{
point3D norm;
int degree;
int knots;
int control_points;
int fit_points;
int flag;
point3D starttan;
point3D endtan;
std::vector<double> knot;
std::vector<double> weight;
std::vector<point3D> control;
std::vector<point3D> fit;
};
struct LWPolyDataOut
{
double nVert;
int Flag;
double Width;
double Elev;
double Thick;
std::vector<point3D> Verts;
std::vector<double> StartWidth;
std::vector<double> EndWidth;
std::vector<double> Bulge;
point3D Extr;
};
// "using" for enums is not supported by all platforms
// https://stackoverflow.com/questions/41167119/how-to-fix-a-wsubobject-linkage-warning
enum eDXFGroupCode_t
{
eObjectType = 0,
ePrimaryText = 1,
eName = 2,
eExtraText = 3,
eLinetypeName = 6,
eTextStyleName = 7,
eLayerName = 8,
eVariableName = 9,
ePrimaryPoint = 10,
ePoint2 = 11,
ePoint3 = 12,
ePoint4 = 13,
ePoint5 = 14,
eFloat1 = 40,
eFloat2 = 41,
eFloat3 = 42,
eFloat4 = 43,
eAngleDegrees1 = 50,
eAngleDegrees2 = 51,
eColor = 62,
eCoordinateSpace = 67,
eInteger1 = 70,
eInteger2 = 71,
eInteger3 = 72,
eInteger4 = 73,
eInteger5 = 74,
eUCSOrigin = 110,
eUCSXDirection = 111,
eUCSYDirection = 112,
eExtrusionDirection = 210,
eComment = 999,
// The following apply to points and directions in text DXF files to identify the three
// coordinates
eXOffset = 0,
eYOffset = 10,
eZOffset = 20
};
enum eDXFVersion_t
{
RUnknown,
ROlder,
R10,
R11_12,
R13,
R14,
R2000,
R2004,
R2007,
R2010,
R2013,
R2018,
RNewer,
};
enum eDimensionType_t
{
eLinear = 0, // Rotated, Horizontal, or Vertical
eAligned = 1,
eAngular = 2,
eDiameter = 3,
eRadius = 4,
eAngular3Point = 5,
eOrdinate = 6,
eTypeMask = 0xF,
eOnlyBlockReference = 32,
eOrdianetIsXType = 64,
eUserTextLocation = 128
};
//********************
class ImportExport CDxfWrite
{
private:
std::ofstream* m_ofs;
bool m_fail;
std::ostringstream* m_ssBlock;
std::ostringstream* m_ssBlkRecord;
std::ostringstream* m_ssEntity;
std::ostringstream* m_ssLayer;
protected:
static Base::Vector3d toVector3d(const double* coordinatesXYZ)
{
// NOLINTNEXTLINE(readability/nolint)
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
return Base::Vector3d(coordinatesXYZ[0], coordinatesXYZ[1], coordinatesXYZ[2]);
}
void putLine(const Base::Vector3d& start,
const Base::Vector3d& end,
std::ostringstream* outStream,
const std::string& handle,
const std::string& ownerHandle);
void putText(const char* text,
const Base::Vector3d& location1,
const Base::Vector3d& location2,
double height,
int horizJust,
std::ostringstream* outStream,
const std::string& handle,
const std::string& ownerHandle);
void putArrow(Base::Vector3d& arrowPos,
Base::Vector3d& barb1Pos,
Base::Vector3d& barb2Pos,
std::ostringstream* outStream,
const std::string& handle,
const std::string& ownerHandle);
//! copy boiler plate file
std::string getPlateFile(std::string fileSpec);
void setDataDir(const std::string& dirName)
{
m_dataDir = dirName;
}
std::string getHandle();
std::string getEntityHandle();
std::string getLayerHandle();
std::string getBlockHandle();
std::string getBlkRecordHandle();
// NOLINTBEGIN(cppcoreguidelines-non-private-member-variables-in-classes)
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;
std::string m_savePaperSpaceHandle;
std::string m_saveBlockRecordTableHandle;
std::string m_saveBlkRecordHandle;
std::string m_currentBlock;
std::string m_dataDir;
std::string m_layerName;
std::vector<std::string> m_layerList;
std::vector<std::string> m_blockList;
std::vector<std::string> m_blkRecordList;
// NOLINTEND(cppcoreguidelines-non-private-member-variables-in-classes)
public:
explicit CDxfWrite(const char* filepath);
CDxfWrite(const CDxfWrite&) = delete;
CDxfWrite(const CDxfWrite&&) = delete;
CDxfWrite& operator=(const CDxfWrite&) = delete;
CDxfWrite& operator=(const CDxfWrite&&) = delete;
~CDxfWrite();
void init();
void endRun();
bool Failed() const
{
return m_fail;
}
// void setOptions(void);
// bool isVersionValid(int vers);
std::string getLayerName()
{
return m_layerName;
}
void setLayerName(std::string name);
void setVersion(int version)
{
m_version = version;
}
void setPolyOverride(bool setting)
{
m_polyOverride = setting;
}
void addBlockName(const std::string& name, const std::string& blkRecordHandle);
void writeLine(const double* start, const double* end);
void writePoint(const double*);
void writeArc(const double* start, const double* end, const double* center, bool dir);
void writeEllipse(const double* center,
double major_radius,
double minor_radius,
double rotation,
double start_angle,
double end_angle,
bool endIsCW);
void writeCircle(const double* center, double radius);
void writeSpline(const SplineDataOut& sd);
void writeLWPolyLine(const LWPolyDataOut& pd);
void writePolyline(const LWPolyDataOut& pd);
// NOLINTNEXTLINE(readability/nolint)
// NOLINTNEXTLINE(readability-identifier-length)
void writeVertex(double x, double y, double z);
void writeText(const char* text,
const double* location1,
const double* location2,
double height,
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 writeHeaderSection();
void writeTablesSection();
void writeBlocksSection();
void writeEntitiesSection();
void writeObjectsSection();
void writeClassesSection();
void makeLayerTable();
void makeBlockRecordTableHead();
void makeBlockRecordTableBody();
void makeBlockSectionHead();
};
// derive a class from this and implement it's virtual functions
class ImportExport CDxfRead
{
private:
// Low-level reader members
std::ifstream* m_ifs; // TODO: gsl::owner<ifstream>
// https://stackoverflow.com/questions/41167119/how-to-fix-a-wsubobject-linkage-warning
eDXFGroupCode_t m_record_type = eObjectType;
std::string m_record_data;
bool m_not_eof = true;
int m_line = 0;
bool m_repeat_last_record = false;
// The scaling from DXF units to millimetres.
// This does not include the dxfScaling option
// This has the value 0.0 if no units have been specified.
// If it is still 0 after reading the HEADER section, it iw set to comething sensible.
double m_unitScalingFactor = 0.0;
protected:
// An additional scaling factor which can be modified before readDXF is called, and will be
// incorporated into m_unitScalingFactor.
void SetAdditionalScaling(double scaling)
{
m_additionalScaling = scaling <= 0.0 ? 1.0 : scaling;
}
private:
// The following are options which can be altered before DoRead is called.
// An additional scaling factor to apply after any scaling implied by the DXF file header
// (dxfScaling)
double m_additionalScaling = 1.0;
// This group of options control which attributes are preserved on import
protected:
// NOLINTBEGIN(cppcoreguidelines-non-private-member-variables-in-classes)
// Place entities in drawing layers as coded in the DXF file.
// Otherwise place objects as top-level drawing objects (but still honour BYLAYER attributes).
// (dxfUseDraftVisGroups)
bool m_preserveLayers = true;
// Colour entities as coded in the DXF file.
// Otherwise leave everything as the default FreeCAD colours.
// (dxfGetOriginalColors)
bool m_preserveColors = true;
// Control object simplification (preserve types and individual identities)
using eEntityMergeType_t = enum {
// Merge shapes (lines, arcs, circles, etc) into a single compound object when attributes
// match
MergeShapes,
// Make individual dearing shapes for each DXF entity
SingleShapes,
// Make appropriately-types Draft objects for each DXF entity, giving superior property
// access.
DraftObjects
};
// This is a combination of ui options:
// "Group layers into blocks" (groupLayers) checkbox which means MergeShapes,
// "Simple part shapes" (dxfCreatePart) radio button which means SingleShapes
// "Draft objects" (dxfCreateDraft) radio button which means DraftObjects
// We do not support (dxfCreateSketch).
// (joingeometry) is described as being slow so I think the implication is that it only joins
// linear entities that meet end-to-end to form them into wire objects. As such it is
// intermediate between MergeShapes and SingleShapes and is not implemented.
// TODO: The options structure allows conflicting settings for these options because
// (groupLayers) and (joingeometry) are separate options, not part of the radio button set. The
// others are excluded from having several set by the UI structure but code can set multiple
// conflicting values.
eEntityMergeType_t m_mergeOption = DraftObjects;
// This group of options controls the import of certain types of entities or entities having
// certain properties
// Import text and dimensions (dxftext)
bool m_importAnnotations = true;
// Import Point tntities
bool m_importPoints = true;
// Import entities in Paper space
bool m_importPaperSpaceEntities = false;
// Import hidden blocks (the will not affect the result for standard import because such blocks
// are never INSERTed but a custom derived class might want to access these block definitions.
bool m_importHiddenBlocks = false;
// Import content on Frozen layers
bool m_importFrozenLayers = false;
// Import content on Hidden layers. Note that an INSERT on a hidden layer would still be
// expanded, but the resulting entities would not appear if placed on a hidden layer.
bool m_importHiddenLayers = true;
// NOLINTEND(cppcoreguidelines-non-private-member-variables-in-classes)
// TODO: options still to implement:
// Import Hatch boundaries as wires (FC doesn't support hatching (???) but this option will draw
// a polygon of the boundary of
// the hatched area
// Render Polylines with width. If the width were constant we could use the View line width
// property except that is in
// pixels and does not scale. The legacy importer replaces the polyline with a face whose
// side edges are the original polyline offset by half the width. If the polyline is open
// these edges are joined by endcaps, otherwise they each join to their ends to form a face
// with a hole in it.
// TODO: Other options we might want:
// Import SOLIDs (as faces)
struct VertexInfo
{
Base::Vector3d location;
double bulge = 0;
};
bool ExplodePolyline(std::list<VertexInfo>&, int flags);
bool ReadBlockContents();
bool SkipBlockContents();
private:
// Error-handling control
bool m_ignore_errors = true;
bool m_fail = false;
// These could be private to CommonEntityAttributes but in the long run I want to make all
// the DXF definitions available to the CDxfWrite as well.
// The Default values are used if a Layer definition by error is missing an attribute,
// and also if we synthesize a layer that has no definition.
static const ColorIndex_t ColorBylayer = 256;
static const ColorIndex_t ColorByBlock = 0;
static const ColorIndex_t DefaultColor = 0;
static const std::string LineTypeByLayer;
static const std::string LineTypeByBlock;
static const std::string DefaultLineType;
// Readers for various parts of the DXF file.
bool ReadSection();
// Section readers (sections are identified by the type-2 (name) record they start with and each
// has its own reader function
bool ReadHeaderSection();
bool ReadTablesSection();
bool ReadBlocksSection();
protected:
virtual bool ReadEntitiesSection();
private:
bool ReadIgnoredSection();
// The Header section consists of multipel variables, only a few of which we give special
// handling.
bool ReadVariable();
bool ReadVersion();
bool ReadDWGCodePage();
// The Tables section consists of several tables (again identified by their type-2 record asfter
// th 0-TABLE record) each with its own reader
bool ReadLayerTable();
// Some tables we ignore completely, using this method, which some of the above are
// inline-defined to.
bool ReadIgnoredTable();
bool ReadLayer();
bool ReadEntity(); // Identify entity type and read it
// Readers for specific entity types
bool ReadLine();
bool ReadText();
bool ReadArc();
bool ReadCircle();
bool ReadEllipse();
bool ReadPoint();
bool ReadSpline();
bool ReadLwPolyLine();
bool ReadPolyLine();
void OnReadArc(double start_angle,
double end_angle,
double radius,
const Base::Vector3d& center,
double z_extrusion_dir,
bool hidden);
void OnReadCircle(const Base::Vector3d& center, double radius, bool hidden);
void OnReadEllipse(const Base::Vector3d& center,
const Base::Vector3d& majorAxisEnd,
double ratio,
double start_angle,
double end_angle);
bool ReadInsert();
bool ReadDimension();
bool ReadUnknownEntity();
// Helper for reading common attributes for entities
void InitializeAttributes();
void InitializeCommonEntityAttributes();
void Setup3DVectorAttribute(eDXFGroupCode_t x_record_type, Base::Vector3d& destination);
void Setup3DDirectionAttribute(eDXFGroupCode_t x_record_type, Base::Vector3d& destination);
void SetupScaledDoubleAttribute(eDXFGroupCode_t record_type, double& destination);
void SetupScaledDoubleIntoList(eDXFGroupCode_t record_type, std::list<double>& destination);
void Setup3DCoordinatesIntoLists(eDXFGroupCode_t x_record_type,
std::list<double>& x_destination,
std::list<double>& y_destination,
std::list<double>& z_destination);
void SetupStringAttribute(eDXFGroupCode_t record_type, std::string& destination);
std::map<int, std::pair<void (*)(CDxfRead*, void*), void*>> m_coordinate_attributes;
static void ProcessScaledDouble(CDxfRead* object, void* target);
static void ProcessScaledDoubleIntoList(CDxfRead* object, void* target);
static void ProcessStdString(CDxfRead* object, void* target);
static void ProcessLayerReference(CDxfRead* object, void* target);
// For all types T where strean >> x and x = 0 works
template<typename T>
void SetupValueAttribute(eDXFGroupCode_t record_type, T& destination);
// TODO: Once all compilers used for FreeCAD support class-level template specializations,
// SetupValueAttribute could have specializations and replace SetupStringAttribute etc.
// The template specialization is required to handle the (char *) case, which would
// otherwise try to read the actual pointer from the stream, or... what?
// The specialization would also handle the default value when it cannot be zero.
template<typename T>
static void ProcessValue(CDxfRead* object, void* target)
{
ParseValue<T>(object, target);
}
template<typename T>
static bool ParseValue(CDxfRead* object, void* target);
bool ProcessAttribute();
void ProcessAllAttributes();
void ProcessAllEntityAttributes();
void ResolveEntityAttributes();
bool ReadBlockInfo();
bool ResolveEncoding();
bool get_next_record();
void repeat_last_record();
bool (CDxfRead::*stringToUTF8)(std::string&) const = &CDxfRead::UTF8ToUTF8;
protected:
// NOLINTBEGIN(cppcoreguidelines-non-private-member-variables-in-classes)
eDXFVersion_t m_version = RUnknown; // Version from $ACADVER variable in DXF
// NOLINTEND(cppcoreguidelines-non-private-member-variables-in-classes)
// Although this is called "ImportError" it is just a wrapper to write a warning eithout any
// additional information such as a line number and as such, may be split into a basic
// message-writer and something that adds a line number.
//
// The "Developer" methods stick a line break into the output window.
// The "User" methods show up in the notification popup window.
// "Critical" causes a popup messagebox
// "Warnings" show up in yellow in the output window and with a warning icon in the notification
// popup "Error" show up in red in the output window and with an error icon in the notification
// popup "Notification" show up in black in the output window and an information icon in the
// notification popup "Log" goes to a log somewhere and not to the screen/user at all
template<typename... args>
static void ImportError(const char* format, args&&... argValues)
{
Base::ConsoleSingleton::instance().warning(format, std::forward<args>(argValues)...);
}
template<typename... args>
static void ImportObservation(const char* format, args&&... argValues)
{
Base::ConsoleSingleton::instance().message(format, std::forward<args>(argValues)...);
}
template<typename... args>
void UnsupportedFeature(const char* format, args&&... argValues);
private:
std::map<std::string, std::pair<int, int>> m_unsupportedFeaturesNoted;
std::string m_CodePage; // Code Page name from $DWGCODEPAGE or null if none/not read yet
// The following was going to be python's canonical name for the encoding, but this is (a) not
// easily found and (b) does not speed up finding the encoding object.
std::string m_encoding; // A name for the encoding implied by m_version and m_CodePage
bool UTF8ToUTF8(std::string& encoded) const;
bool GeneralToUTF8(std::string& encoded) const;
// Compare with specific object name for eObjectType records
bool IsObjectName(const char* testName) const
{
return m_record_data == testName;
}
// Compare with specific variable name for eVariableName records
bool IsVariableName(const char* testName) const
{
return m_record_data == testName;
}
// Layer management
protected:
class Layer
{
public:
Layer(const std::string& name, ColorIndex_t color, std::string&& lineType)
: Name(name)
, Color(color < 0 ? -color : color)
, LineType(lineType)
, Hidden(color < 0)
{}
Layer(const Layer&) = delete;
Layer(Layer&&) = delete;
void operator=(const Layer&) = delete;
void operator=(Layer&&) = delete;
virtual ~Layer() = default;
const std::string Name;
const ColorIndex_t Color;
const std::string LineType;
const bool Hidden;
};
std::map<std::string, Layer*> Layers;
virtual Layer* MakeLayer(const std::string& name, ColorIndex_t color, std::string&& lineType);
// Entity attribute management
class CommonEntityAttributes
{
public:
Layer* m_Layer {nullptr};
ColorIndex_t m_Color {0};
std::string m_LineType;
bool m_paperSpace {false};
void SetDefaults()
{
// There is some uncertainty in the documentation. If the line type is omitted it should
// act as by-layer.
// The question is whether an explicit line type "BYLAYER" should also mean by-layer. If
// this is the case we should set m_LineType to "BYLAYER" and only check for "BYLAYER"
// in ResolveByLayerAttributes. If this is not the case and "BYLAYER" can actually refer
// to a line type of that name, then we should init m_LineType to empty and check only
// for a zero-length line type to trigger by-layer line type. An experiment in R13
// reveals that it is not possible to make a line type BYLAYER so the former is what we
// do.
m_Color = ColorBylayer;
m_LineType = LineTypeByLayer;
m_paperSpace = false;
}
void ResolveBylayerAttributes(const CDxfRead& reader)
{
// This should be called after the entire entity is read in case the layer reference
// name comes after a BYLAYER color. Also we can't be sure of BYLAYER line type until
// the entity ends
if (m_Color == ColorBylayer) {
m_Color = m_Layer != nullptr ? m_Layer->Color : DefaultColor;
}
if (m_LineType == LineTypeByLayer) {
m_LineType = m_Layer != nullptr ? m_Layer->LineType : DefaultLineType;
}
// We this point we not longer have any use for m_Layer except as a destination for the
// entity. If the import is not preserving layers, we clear out that field here so all
// entities get placed at the drawing root and are subject to compounding despite being
// originally in different layers.
if (!reader.m_preserveColors) {
m_Color = DefaultColor;
}
if (!reader.m_preserveLayers) {
m_Layer = nullptr;
}
}
void ResolveByBlockAttributes(const CommonEntityAttributes& insertAttributes)
{
if (m_Color == ColorByBlock) {
m_Color = insertAttributes.m_Color;
}
if (m_LineType == LineTypeByBlock) {
m_LineType = insertAttributes.m_LineType;
}
}
bool operator<(const CommonEntityAttributes& second) const
{
// TODO: This is here so we can use CommonEntityAttributes as a key in a std::map
// It's been a while and I don't know if it is (currently) considered suspect to compare
// for inequality two pointers that don't point within the same array, but I'm also not
// sure what to do instead.
return m_Layer < second.m_Layer
|| (m_Layer == second.m_Layer
&& (m_Color < second.m_Color
|| (m_Color == second.m_Color
&& (m_LineType < second.m_LineType
|| (m_LineType == second.m_LineType && !m_paperSpace
&& second.m_paperSpace)))));
}
};
CommonEntityAttributes m_entityAttributes;
// Coordinate transform management.
private:
// The normal vector as read from the eExtrusionDirection attributes, defaults to (0, 0, 1)
// Reading this is set up in ReadEntity and done by ProcessAll(Entity)Attributes
Base::Vector3d EntityNormalVector;
static constexpr double ArbitraryAxisAlgorithmThreshold = 1 / 64.0;
protected:
// NOLINTBEGIN(cppcoreguidelines-non-private-member-variables-in-classes)
// The transform implied by the Normal vector of the entity in EntityNormalVector.
// This is a pure 3d rotation transformation (that is, no scaling or translation)
// which maps the Z axis to the normal vector from the DXF records,
// and the X and Y axes according to the Arbitrary Axis Algorithm in the DXF documentation
// This is calculated from EntityNormalVector in ResolveEntityAttributes which is
// called by either ProcessAllEntityAttributes or custom-processed entities after they
// are finished calling ProcessAttribute.
Base::Matrix4D OCSOrientationTransform;
// NOLINTEND(cppcoreguidelines-non-private-member-variables-in-classes)
public:
explicit CDxfRead(const std::string& filepath); // this opens the file
CDxfRead(const CDxfRead&) = delete;
CDxfRead(const CDxfRead&&) = delete;
CDxfRead& operator=(const CDxfRead&) = delete;
CDxfRead& operator=(const CDxfRead&&) = delete;
virtual ~CDxfRead();
bool Failed() const
{
return m_fail;
}
void
DoRead(bool ignore_errors = false); // this reads the file and calls the following functions
virtual void StartImport()
{}
virtual void FinishImport()
{}
private:
double mm(double value) const
{
if (m_unitScalingFactor == 0.0) {
// No scaling factor has been specified.
// TODO: Resolve this once we know the HEADER is complete
return value;
}
return m_unitScalingFactor * value;
}
public:
bool IgnoreErrors() const
{
return (m_ignore_errors);
}
virtual bool OnReadBlock(const std::string& /*name*/, int /*flags*/)
{
return SkipBlockContents();
}
virtual void
OnReadLine(const Base::Vector3d& /*start*/, const Base::Vector3d& /*end*/, bool /*hidden*/)
{}
virtual void OnReadPoint(const Base::Vector3d& /*start*/)
{}
virtual void OnReadText(const Base::Vector3d& /*point*/,
const double /*height*/,
const std::string& /*text*/,
const double /*rotation*/)
{}
virtual void OnReadArc(const Base::Vector3d& /*start*/,
const Base::Vector3d& /*end*/,
const Base::Vector3d& /*center*/,
bool /*dir*/,
bool /*hidden*/)
{}
virtual void OnReadCircle(const Base::Vector3d& /*start*/,
const Base::Vector3d& /*center*/,
bool /*dir*/,
bool /*hidden*/)
{}
virtual void OnReadEllipse(const Base::Vector3d& /*center*/,
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 Base::Vector3d& /*point*/,
const Base::Vector3d& /*scale*/,
const std::string& /*name*/,
double /*rotation*/)
{}
virtual void OnReadDimension(const Base::Vector3d& /*start*/,
const Base::Vector3d& /*end*/,
const Base::Vector3d& /*point*/,
double /*rotation*/)
{}
virtual void OnReadPolyline(std::list<VertexInfo>& /*vertices*/, int /*flags*/)
{}
// These give the derived class access to common object properties
bool LineTypeIsHidden() const
{
return m_entityAttributes.m_LineType[0] == 'h' || m_entityAttributes.m_LineType[0] == 'H';
}
static Base::Color ObjectColor(ColorIndex_t colorIndex); // as rgba value
#ifdef DEBUG
protected:
static PyObject* PyObject_GetAttrString(PyObject* o, const char* attr_name)
{
PyObject* result = ::PyObject_GetAttrString(o, attr_name);
if (result == nullptr) {
ImportError("Unable to get Attribute '%s'\n", attr_name);
PyErr_Clear();
}
return result;
}
static void PyObject_SetAttrString(PyObject* o, const char* attr_name, PyObject* v)
{
if (::PyObject_SetAttrString(o, attr_name, v) != 0) {
ImportError("Unable to set Attribute '%s'\n", attr_name);
PyErr_Clear();
}
}
#endif
};
#endif