Add color on DXF import, refactor code

Colors as assigned to imported drawing entities if they are not merged
with other entities.
The code has been refactored to remove much duplication in reading of
attributes.
The code gives brief messages on the Python console about unsupported
DXF festures and also issues some new errors.
There is no support yet for making colors 1-9 and 250-255 contrast with
the creeen background color. Colors are generated by code rather than a
lookup table; this code can eventually modify the colors it generates to
contrast with a specific background color.
This commit is contained in:
Kevin Martin
2023-11-24 11:44:17 -05:00
committed by Yorik van Havre
parent 5ac40155b0
commit abd411f934
6 changed files with 1193 additions and 2050 deletions

View File

@@ -55,12 +55,15 @@
#include <App/Annotation.h>
#include <App/Application.h>
#include <App/Document.h>
#include <App/DocumentObjectPy.h>
#include <App/FeaturePythonPyImp.h>
#include <Base/Console.h>
#include <Base/Interpreter.h>
#include <Base/Matrix.h>
#include <Base/Parameter.h>
#include <Base/Vector3D.h>
#include <Base/PlacementPy.h>
#include <Base/VectorPy.h>
#include <Mod/Part/App/PartFeature.h>
#include "ImpExpDxf.h"
@@ -92,17 +95,12 @@ void ImpExpDxfRead::setOptions()
optionScaling = hGrp->GetFloat("dxfScaling", 1.0);
}
gp_Pnt ImpExpDxfRead::makePoint(const double* p)
gp_Pnt ImpExpDxfRead::makePoint(const double point3d[3]) const
{
double sp1(p[0]);
double sp2(p[1]);
double sp3(p[2]);
if (optionScaling != 1.0) {
sp1 = sp1 * optionScaling;
sp2 = sp2 * optionScaling;
sp3 = sp3 * optionScaling;
return {point3d[0] * optionScaling, point3d[1] * optionScaling, point3d[2] * optionScaling};
}
return {sp1, sp2, sp3};
return {point3d[0], point3d[1], point3d[2]};
}
void ImpExpDxfRead::OnReadLine(const double* s, const double* e, bool /*hidden*/)
@@ -333,13 +331,27 @@ void ImpExpDxfRead::OnReadText(const double* point,
PyObject* placement = new Base::PlacementPy(Base::Placement(insertionPoint, rot));
draftModule = PyImport_ImportModule("Draft");
if (draftModule != nullptr) {
PyObject_CallMethod(draftModule, "make_text", "sOif", text, placement, 0, height);
// returns a wrapped App::FeaturePython
auto builtText = static_cast<App::FeaturePythonPyT<App::DocumentObjectPy>*>(
PyObject_CallMethod(draftModule,
"make_text",
"sOif",
text,
placement,
0,
height));
if (builtText != nullptr) {
ApplyGuiStyles(
static_cast<App::FeaturePython*>(builtText->getDocumentObjectPtr()));
}
}
// We own all the return values so we must release them.
Py_DECREF(placement);
Py_XDECREF(draftModule);
}
// else std::cout << "skipped text in block: " << LayerName() << std::endl;
else {
UnsupportedFeature("TEXT or MTEXT in BLOCK definition");
}
}
}
@@ -395,27 +407,36 @@ void ImpExpDxfRead::OnReadDimension(const double* s,
double /*rotation*/)
{
if (optionImportAnnotations) {
Base::Interpreter().runString("import Draft");
Base::Interpreter().runStringArg("p1=FreeCAD.Vector(%f,%f,%f)",
s[0] * optionScaling,
s[1] * optionScaling,
s[2] * optionScaling);
Base::Interpreter().runStringArg("p2=FreeCAD.Vector(%f,%f,%f)",
e[0] * optionScaling,
e[1] * optionScaling,
e[2] * optionScaling);
Base::Interpreter().runStringArg("p3=FreeCAD.Vector(%f,%f,%f)",
point[0] * optionScaling,
point[1] * optionScaling,
point[2] * optionScaling);
Base::Interpreter().runString("Draft.makeDimension(p1,p2,p3)");
PyObject* draftModule = nullptr;
PyObject* start = new Base::VectorPy(Base::Vector3d(s[0], s[1], s[2]) * optionScaling);
PyObject* end = new Base::VectorPy(Base::Vector3d(e[0], e[1], e[2]) * optionScaling);
PyObject* lineLocation =
new Base::VectorPy(Base::Vector3d(point[0], point[1], point[2]) * optionScaling);
draftModule = PyImport_ImportModule("Draft");
if (draftModule != nullptr) {
// returns a wrapped App::FeaturePython
auto builtDim = static_cast<App::FeaturePythonPyT<App::DocumentObjectPy>*>(
PyObject_CallMethod(draftModule,
"make_linear_dimension",
"OOO",
start,
end,
lineLocation));
if (builtDim != nullptr) {
ApplyGuiStyles(static_cast<App::FeaturePython*>(builtDim->getDocumentObjectPtr()));
}
}
// We own all the return values so we must release them.
Py_DECREF(start);
Py_DECREF(end);
Py_DECREF(lineLocation);
Py_XDECREF(draftModule);
}
}
void ImpExpDxfRead::AddObject(Part::TopoShape* shape)
{
// std::cout << "layer:" << LayerName() << std::endl;
std::vector<Part::TopoShape*> vec;
if (layers.count(LayerName())) {
vec = layers[LayerName()];
@@ -427,6 +448,7 @@ void ImpExpDxfRead::AddObject(Part::TopoShape* shape)
Part::Feature* pcFeature =
static_cast<Part::Feature*>(document->addObject("Part::Feature", "Shape"));
pcFeature->Shape.setValue(shape->getShape());
ApplyGuiStyles(pcFeature);
}
}
}
@@ -517,10 +539,7 @@ void gPntToTuple(double* result, gp_Pnt& p)
point3D gPntTopoint3D(gp_Pnt& p)
{
point3D result;
result.x = p.X();
result.y = p.Y();
result.z = p.Z();
point3D result = {p.X(), p.Y(), p.Z()};
return result;
}

View File

@@ -27,6 +27,7 @@
#include <App/Document.h>
#include <Mod/Part/App/TopoShape.h>
#include <Mod/Part/App/PartFeature.h>
#include "dxf.h"
@@ -83,9 +84,13 @@ public:
void setOptions();
private:
gp_Pnt makePoint(const double* p);
gp_Pnt makePoint(const double point3d[3]) const;
protected:
virtual void ApplyGuiStyles(Part::Feature*)
{}
virtual void ApplyGuiStyles(App::FeaturePython*)
{}
App::Document* document;
bool optionGroupLayers;
bool optionImportAnnotations;

File diff suppressed because it is too large Load Diff

View File

@@ -22,6 +22,8 @@
#include <vector>
#include <Base/Vector3D.h>
#include <Base/Console.h>
#include <App/Color.h>
#include <Mod/Import/ImportGlobal.h>
@@ -56,12 +58,12 @@ typedef enum
// spline data for reading
struct SplineData
{
double norm[3];
int degree;
int knots;
int control_points;
int fit_points;
int flag;
double norm[3] = {0, 0, 0};
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;
@@ -199,7 +201,7 @@ protected:
std::vector<std::string> m_blkRecordList;
public:
CDxfWrite(const char* filepath);
explicit CDxfWrite(const char* filepath);
~CDxfWrite();
void init();
@@ -309,25 +311,59 @@ public:
class ImportExport CDxfRead
{
private:
// Low-level reader members
std::ifstream* m_ifs;
int m_record_type = 0;
char m_record_data[1024] = "";
bool m_not_eof = true;
int m_line = 0;
bool m_repeat_last_record = false;
bool m_fail;
char m_str[1024];
char m_unused_line[1024];
eDxfUnits_t m_eUnits;
bool m_measurement_inch;
char m_layer_name[1024];
char m_section_name[1024];
char m_block_name[1024];
bool m_ignore_errors;
// file-level options/properties
eDxfUnits_t m_eUnits = eMillimeters;
bool m_measurement_inch = false;
// The following provide a state when reading any entity: If m_block_name is not empty the
// entity is in a BLOCK being defined, and LayerName() will return "BLOCKS xxxx" where xxxx is
// the block name. Otherwise m_layer_name will be the layer name from the entity records
// (default to "0") and LayerName() will return "ENTITIES xxxx" where xxxx is the layer name.
// This is clunky but it is a non-private interface and so difficult to change.
char m_layer_name[1024] = "0";
char m_block_name[1024] = "";
// Error-handling control
bool m_ignore_errors = true;
bool m_fail = false;
std::map<std::string, ColorIndex_t>
m_layer_ColorIndex_map; // Mapping from layer name -> layer color index
const ColorIndex_t ColorBylayer = 256;
const ColorIndex_t ColorByBlock = 0;
// Readers for various parts of the DXF file.
// 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();
bool ReadEntitiesSection();
bool ReadIgnoredSection();
// 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 ReadUnits();
bool ReadLayer();
bool ReadEntity(); // Identify entity type and read it
// Readers for specific entity types
bool ReadLine();
bool ReadText();
bool ReadArc();
@@ -337,7 +373,13 @@ private:
bool ReadSpline();
bool ReadLwPolyLine();
bool ReadPolyLine();
bool ReadVertex(double* pVertex, bool* bulge_found, double* bulge);
typedef struct
{
double location[3];
double bulge;
} VertexInfo;
bool OnReadPolyline(std::list<VertexInfo>&, int flags);
void OnReadArc(double start_angle,
double end_angle,
double radius,
@@ -352,31 +394,79 @@ private:
double end_angle);
bool ReadInsert();
bool ReadDimension();
bool ReadUnknownEntity();
// Helper for reading common attributes for entities
void InitializeAttributes();
void InitializeCommonEntityAttributes();
void Setup3DVectorAttribute(int x_record_type, double destination[3]);
void SetupScaledDoubleAttribute(int record_type, double& destination);
void SetupScaledDoubleIntoList(int record_type, std::list<double>& destination);
void Setup3DCoordinatesIntoLists(int x_record_type,
std::list<double>& x_destination,
std::list<double>& y_destination,
std::list<double>& z_destination);
void SetupStringAttribute(int record_type, char* destination);
void SetupStringAttribute(int 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 ProcessString(CDxfRead* object, void* target);
static void ProcessStdString(CDxfRead* object, void* target);
// For all types T where strean >> x and x = 0 works
template<typename T>
void SetupValueAttribute(int record_type, T& target);
// 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);
bool ProcessAttribute();
void ProcessAllAttributes();
bool ReadBlockInfo();
bool ReadVersion();
bool ReadDWGCodePage();
bool ResolveEncoding();
void get_line();
void put_line(const char* value);
void ResolveColorIndex();
bool get_next_record();
void repeat_last_record();
protected:
ColorIndex_t m_ColorIndex;
eDXFVersion_t m_version; // Version from $ACADVER variable in DXF
const char* (CDxfRead::*stringToUTF8)(const char*) const;
// common entity properties. Some properties are accumulated local to the reader method and
// passed to the ReadXxxx virtual method. Others are collected here as private values and also
// passed to ReadXxxx. Finally some of the attributes are accessed using references to
// public/protected fields or methods (such as LayerName()). Altogether a bit of a mishmash.
ColorIndex_t m_ColorIndex = 0;
char m_LineType[1024] = "";
eDXFVersion_t m_version = RUnknown; // Version from $ACADVER variable in DXF
const char* (CDxfRead::*stringToUTF8)(const char*) const = &CDxfRead::UTF8ToUTF8;
// Although this is called "ImportWarning" 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.
template<typename... args>
void ImportError(const char* format, args... argValues) const
{
Base::ConsoleSingleton::Instance().Warning(format, argValues...);
}
void UnsupportedFeature(const char* format, ...);
private:
const std::string* m_CodePage; // Code Page name from $DWGCODEPAGE or null if none/not read yet
std::map<std::string, std::pair<int, int>> m_unsupportedFeaturesNoted;
const std::string* m_CodePage =
nullptr; // 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.
const std::string* m_encoding; // A name for the encoding implied by m_version and m_CodePage
const std::string* m_encoding =
nullptr; // A name for the encoding implied by m_version and m_CodePage
const char* UTF8ToUTF8(const char* encoded) const;
const char* GeneralToUTF8(const char* encoded) const;
public:
CDxfRead(const char* filepath); // this opens the file
virtual ~CDxfRead(); // this closes the file
explicit CDxfRead(const char* filepath); // this opens the file
virtual ~CDxfRead(); // this closes the file
bool Failed()
{
@@ -433,6 +523,12 @@ public:
virtual void AddGraphics() const
{}
// These give the derived class access to common object properties
std::string LayerName() const;
bool LineTypeIsHidden() const
{
return m_LineType[0] == 'h' || m_LineType[0] == 'H';
}
App::Color ObjectColor() const; // as rgba value
};
#endif

View File

@@ -50,16 +50,20 @@
#include <gp_Vec.hxx>
#endif
//#include <App/Annotation.h>
//#include <App/Application.h>
//#include <App/Document.h>
//#include <Base/Console.h>
//#include <Base/Interpreter.h>
//#include <Base/Matrix.h>
//#include <Base/Parameter.h>
//#include <Base/Vector3D.h>
//#include <Base/PlacementPy.h>
//#include <Mod/Part/App/PartFeature.h>
// #include <App/Annotation.h>
// #include <App/Application.h>
// #include <App/Document.h>
// #include <Base/Console.h>
// #include <Base/Interpreter.h>
// #include <Base/Matrix.h>
// #include <Base/Parameter.h>
// #include <Base/Vector3D.h>
// #include <Base/PlacementPy.h>
// #include <Mod/Part/App/PartFeature.h>
#include <Gui/Application.h>
#include <Gui/ViewProvider.h>
#include <Gui/ViewProviderDocumentObject.h>
#include <Mod/Part/Gui/ViewProvider.h>
#include "ImpExpDxfGui.h"
@@ -67,5 +71,37 @@ using namespace ImportGui;
ImpExpDxfReadGui::ImpExpDxfReadGui(std::string filepath, App::Document* pcDoc)
: ImpExpDxfRead(filepath, pcDoc)
, GuiDocument(Gui::Application::Instance->getDocument(pcDoc))
{}
void ImpExpDxfReadGui::ApplyGuiStyles(Part::Feature* object)
{
PartGui::ViewProviderPart* view =
static_cast<PartGui::ViewProviderPart*>(GuiDocument->getViewProvider(object));
App::Color color = ObjectColor();
view->LineColor.setValue(color);
view->PointColor.setValue(color);
view->ShapeColor.setValue(color);
}
void ImpExpDxfReadGui::ApplyGuiStyles(App::FeaturePython* object)
{
static Base::Type PropertyColorType = App::PropertyColor::getClassTypeId();
auto view = static_cast<Gui::ViewProviderDocumentObject*>(GuiDocument->getViewProvider(object));
App::Color color = ObjectColor();
// The properties on this object depend on which Python object is wrapped around it.
// For now we look for the two colors we expect in text and dimensions, and check that they
// exist and have the correct type before setting them.
// A more general choice would be to iterate over all the properties and set all the ones of
// this type, or perhaps only if their name ends in "Color"
auto prop = view->getPropertyByName("TextColor");
if (prop != nullptr && prop->getTypeId() == PropertyColorType) {
static_cast<App::PropertyColor*>(prop)->setValue(color);
}
prop = view->getPropertyByName("LineColor");
if (prop != nullptr && prop->getTypeId() == PropertyColorType) {
static_cast<App::PropertyColor*>(prop)->setValue(color);
}
}

View File

@@ -26,6 +26,7 @@
#include <gp_Pnt.hxx>
#include <App/Document.h>
#include <Gui/Document.h>
#include <Mod/Part/App/TopoShape.h>
#include <Mod/Import/App/dxf/ImpExpDxf.h>
@@ -37,6 +38,11 @@ class ImpExpDxfReadGui: public Import::ImpExpDxfRead
{
public:
ImpExpDxfReadGui(std::string filepath, App::Document* pcDoc);
protected:
void ApplyGuiStyles(Part::Feature*);
void ApplyGuiStyles(App::FeaturePython*);
Gui::Document* GuiDocument;
};
} // namespace ImportGui