diff --git a/src/Base/Console.cpp b/src/Base/Console.cpp
index ec51800014..3a970657a8 100644
--- a/src/Base/Console.cpp
+++ b/src/Base/Console.cpp
@@ -1,5 +1,5 @@
/***************************************************************************
- * (c) Jürgen Riegel (juergen.riegel@web.de) 2002 *
+ * (c) Jürgen Riegel (juergen.riegel@web.de) 2002 *
* *
* This file is part of the FreeCAD CAx development system. *
* *
@@ -10,12 +10,12 @@
* for detail see the LICENCE text file. *
* *
* FreeCAD is distributed in the hope that it will be useful, *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
- * License along with FreeCAD; if not, write to the Free Software *
+ * License along with FreeCAD; if not, write to the Free Software *
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* USA *
* *
@@ -49,7 +49,7 @@ using namespace Base;
//=========================================================================
namespace Base {
-
+
class ConsoleEvent : public QEvent {
public:
ConsoleSingleton::FreeCAD_ConsoleMsgType msgtype;
@@ -141,7 +141,7 @@ ConsoleSingleton::~ConsoleSingleton()
//**************************************************************************
// methods
-/**
+/**
* sets the console in a special mode
*/
void ConsoleSingleton::SetConsoleMode(ConsoleMode m)
@@ -150,7 +150,7 @@ void ConsoleSingleton::SetConsoleMode(ConsoleMode m)
_bVerbose = true;
}
-/**
+/**
* unsets the console from a special mode
*/
void ConsoleSingleton::UnsetConsoleMode(ConsoleMode m)
@@ -165,7 +165,7 @@ void ConsoleSingleton::UnsetConsoleMode(ConsoleMode m)
* The return value is an OR'ed value of all message types that have changed their state. For example
* @code
* // switch off warnings and error messages
- * ConsoleMsgFlags ret = Base::Console().SetEnabledMsgType("myObs",
+ * ConsoleMsgFlags ret = Base::Console().SetEnabledMsgType("myObs",
* ConsoleMsgType::MsgType_Wrn|ConsoleMsgType::MsgType_Err, false);
* // do something without notifying observer myObs
* ...
@@ -236,10 +236,10 @@ void ConsoleSingleton::SetConnectionMode(ConnectionMode mode)
}
/** Prints a Message
- * This method issues a Message.
+ * This method issues a Message.
* Messages are used to show some non vital information. That means when
- * FreeCAD is running in GUI mode a Message appears on the status bar.
- * In console mode a message is printed to the console.
+ * FreeCAD is running in GUI mode a Message appears on the status bar.
+ * In console mode a message is printed to the console.
* \par
* You can use a printf like interface like:
* \code
@@ -251,8 +251,8 @@ void ConsoleSingleton::SetConnectionMode(ConnectionMode mode)
*/
void ConsoleSingleton::Message( const char *pMsg, ... )
{
- char format[4024];
- const unsigned int format_len = 4024;
+ char format[BufferSize];
+ const unsigned int format_len = BufferSize;
va_list namelessVars;
va_start(namelessVars, pMsg); // Get the "..." vars
@@ -266,7 +266,7 @@ void ConsoleSingleton::Message( const char *pMsg, ... )
}
/** Prints a Message
- * This method issues a Warning.
+ * This method issues a Warning.
* Messages are used to get the users attention. That means when
* FreeCAD is in GUI mode a Message Box pops up. In console
* mode a colored message is returned to the console! Don't use this carelessly.
@@ -282,8 +282,8 @@ void ConsoleSingleton::Message( const char *pMsg, ... )
*/
void ConsoleSingleton::Warning( const char *pMsg, ... )
{
- char format[4024];
- const unsigned int format_len = 4024;
+ char format[BufferSize];
+ const unsigned int format_len = BufferSize;
va_list namelessVars;
va_start(namelessVars, pMsg); // Get the "..." vars
@@ -297,10 +297,10 @@ void ConsoleSingleton::Warning( const char *pMsg, ... )
}
/** Prints a Message
- * This method issues an Error which makes some execution impossible.
- * Errors are used to get the users attention. That means when FreeCAD
+ * This method issues an Error which makes some execution impossible.
+ * Errors are used to get the users attention. That means when FreeCAD
* is running in GUI mode an Error Message Box pops up. In console
- * mode a colored message is printed to the console! Don't use this carelessly.
+ * mode a colored message is printed to the console! Don't use this carelessly.
* For information purposes the 'Log' or 'Message' methods are more appropriate.
* \par
* You can use a printf like interface like:
@@ -313,8 +313,8 @@ void ConsoleSingleton::Warning( const char *pMsg, ... )
*/
void ConsoleSingleton::Error( const char *pMsg, ... )
{
- char format[4024];
- const unsigned int format_len = 4024;
+ char format[BufferSize];
+ const unsigned int format_len = BufferSize;
va_list namelessVars;
va_start(namelessVars, pMsg); // Get the "..." vars
@@ -331,7 +331,7 @@ void ConsoleSingleton::Error( const char *pMsg, ... )
/** Prints a Message
* This method is appropriate for development and tracking purposes.
* It can be used to track execution of algorithms and functions.
- * The normal user doesn't need to see it, it's more for developers
+ * The normal user doesn't need to see it, it's more for developers
* and experienced users. So in normal user mode the logging is switched off.
* \par
* You can use a printf-like interface for example:
@@ -346,8 +346,8 @@ void ConsoleSingleton::Error( const char *pMsg, ... )
void ConsoleSingleton::Log( const char *pMsg, ... )
{
- char format[4024];
- const unsigned int format_len = 4024;
+ char format[BufferSize];
+ const unsigned int format_len = BufferSize;
if (_bVerbose)
{
@@ -373,8 +373,8 @@ const char* ConsoleSingleton::Time(void)
{
struct tm *newtime;
time_t aclock;
- time( &aclock ); // Get time in seconds
- newtime = localtime( &aclock ); // Convert time to struct tm form
+ time( &aclock ); // Get time in seconds
+ newtime = localtime( &aclock ); // Convert time to struct tm form
char* st = asctime( newtime );
st[24] = 0;
return st;
@@ -386,7 +386,7 @@ const char* ConsoleSingleton::Time(void)
// Observer stuff
/** Attaches an Observer to Console
- * Use this method to attach a ConsoleObserver derived class to
+ * Use this method to attach a ConsoleObserver derived class to
* the Console. After the observer is attached all messages will also
* be forwarded to it.
* @see ConsoleObserver
@@ -703,8 +703,8 @@ PyObject *ConsoleSingleton::sPyGetStatus(PyObject * /*self*/, PyObject *args)
{
char *pstr1;
char *pstr2;
- if (!PyArg_ParseTuple(args, "ss", &pstr1, &pstr2)) // convert args: Python->C
- return NULL; // NULL triggers exception
+ if (!PyArg_ParseTuple(args, "ss", &pstr1, &pstr2)) // convert args: Python->C
+ return NULL; // NULL triggers exception
PY_TRY{
bool b=false;
@@ -723,7 +723,7 @@ PyObject *ConsoleSingleton::sPyGetStatus(PyObject * /*self*/, PyObject *args)
b = pObs->bMsg;
else if(strcmp(pstr2,"Err") == 0)
b = pObs->bErr;
-
+
return Py_BuildValue("i",b?1:0);
}PY_CATCH;
}
@@ -733,8 +733,8 @@ PyObject *ConsoleSingleton::sPySetStatus(PyObject * /*self*/, PyObject *args)
char *pstr1;
char *pstr2;
int Bool;
- if (!PyArg_ParseTuple(args, "ssi", &pstr1, &pstr2,&Bool)) // convert args: Python->C
- return NULL; // NULL triggers exception
+ if (!PyArg_ParseTuple(args, "ssi", &pstr1, &pstr2,&Bool)) // convert args: Python->C
+ return NULL; // NULL triggers exception
PY_TRY{
ConsoleObserver *pObs = Instance().Get(pstr1);
@@ -754,7 +754,7 @@ PyObject *ConsoleSingleton::sPySetStatus(PyObject * /*self*/, PyObject *args)
Py_INCREF(Py_None);
return Py_None;
} else {
- Py_Error(Base::BaseExceptionFreeCADError,"Unknown Console Type");
+ Py_Error(Base::BaseExceptionFreeCADError,"Unknown Console Type");
}
} PY_CATCH;
@@ -887,7 +887,7 @@ void ConsoleObserverStd::Log (const char *sErr)
}
}
-RedirectStdOutput::RedirectStdOutput()
+RedirectStdOutput::RedirectStdOutput()
{
buffer.reserve(80);
}
@@ -909,7 +909,7 @@ int RedirectStdOutput::sync()
return 0;
}
-RedirectStdLog::RedirectStdLog()
+RedirectStdLog::RedirectStdLog()
{
buffer.reserve(80);
}
@@ -931,7 +931,7 @@ int RedirectStdLog::sync()
return 0;
}
-RedirectStdError::RedirectStdError()
+RedirectStdError::RedirectStdError()
{
buffer.reserve(80);
}
diff --git a/src/Base/Console.h b/src/Base/Console.h
index 9c290b0f1e..51c2ebd80f 100644
--- a/src/Base/Console.h
+++ b/src/Base/Console.h
@@ -1,5 +1,5 @@
/***************************************************************************
- * (c) Jürgen Riegel (juergen.riegel@web.de) 2002 *
+ * (c) Jürgen Riegel (juergen.riegel@web.de) 2002 *
* *
* This file is part of the FreeCAD CAx development system. *
* *
@@ -10,18 +10,18 @@
* for detail see the LICENCE text file. *
* *
* FreeCAD is distributed in the hope that it will be useful, *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
- * License along with FreeCAD; if not, write to the Free Software *
+ * License along with FreeCAD; if not, write to the Free Software *
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* USA *
* *
* Juergen Riegel 2002 *
***************************************************************************/
-
+
@@ -45,11 +45,11 @@
#ifdef FC_DEBUG
/// switch on the logging of python object creation and destruction
-# undef FC_LOGPYOBJECTS
+# undef FC_LOGPYOBJECTS
/// switch on the logging of Feature update and execution
-# define FC_LOGFEATUREUPDATE
+# define FC_LOGFEATUREUPDATE
/// switch on the logging of the Update execution through Doc, App, GuiApp and GuiDoc
-# undef FC_LOGUPDATECHAIN
+# undef FC_LOGUPDATECHAIN
#endif
/////////////////////////////////////////////////////////////////////////////////////
@@ -57,7 +57,7 @@
/** \page LogLevelPage Tag based log helpers
* Simple tag based log and timing helper macros and functions.
*
- * \section Motivation
+ * \section Motivation
*
* FreeCAD Base::Console() is capable of outputting to different targets, and has
* some basic enable/disable control of different types of logs. There is,
@@ -84,7 +84,7 @@
* the same source file.
*
* Predefined log levels are,
- *
+ *
* \code{.c}
* #define FC_LOGLEVEL_ERR 0
* #define FC_LOGLEVEL_WARN 1
@@ -118,7 +118,7 @@
* tag log levels, and \c FreeCAD.getLogLevel(tag), which outputs only integer
* log level.
*
- * You can fine tune how the log is output by passing extra parameters to
+ * You can fine tune how the log is output by passing extra parameters to
* #FC_LOG_LEVEL_INIT(). All the extra parameters are boolean value, which are
* shown blew along with their default values.
*
@@ -157,7 +157,7 @@
* and \c FC_TRACE uses Base::Console().Log(), same as \c FC_LOG. These macros
* checks the log level defined in \c FC_LOG_LEVEL_INIT to decide whether to
* print log or not. \c msg here shall be a C++ streaming expression. End of
- * line will be automatically appended by default.
+ * line will be automatically appended by default.
*
* \code
* FC_ERR("error: " << code << ". exiting")
@@ -171,7 +171,7 @@
* \code{.c}
* void operation() {
* FC_TIME_INIT(t);
- *
+ *
* //do stuff
*
* FC_TIME_LOG(t,"operation done.");
@@ -182,7 +182,7 @@
*
* \code
* operation done. time: 1.12s
- * \endcode
+ * \endcode
*
* Every time you call \c FC_TIME_LOG it will calculate the time duration
* between this call and the last \c FC_TIME_LOG or \c FC_TIME_INIT. Time
@@ -194,7 +194,7 @@
* \code{.cpp}
* void operation() {
* FC_TIME_INIT2(t,t1);
- *
+ *
* //do stage 1
*
* FC_TIME_LOG(t1,"stage1");
@@ -234,7 +234,7 @@
* };
*
* void operation1(Timing &timing) {
- *
+ *
* FC_TIME_INIT(t);
*
* for(...) {
@@ -245,7 +245,7 @@
* FC_DURATION_PLUS(timing.d1_1,t1);
*
* // do step 2
- *
+ *
* FC_DURATION_PLUS(timing.d1_2,t1);
* }
*
@@ -255,7 +255,7 @@
* }
*
* void operation2(Timing &timing) {
- *
+ *
* FC_TIME_INIT(t);
*
* // do stuff
@@ -264,7 +264,7 @@
* }
*
* void operation() {
- *
+ *
* Timing timing;
*
* FC_TIME_INIT(t);
@@ -273,7 +273,7 @@
* operation1(timing);
*
* // do other stuff
- *
+ *
* operation2(timing);
* }
*
@@ -285,10 +285,10 @@
* }
* \endcode
*
- * You can also use FC_DURATION_MSG, FC_DURATION_TRACE as usual.
+ * You can also use FC_DURATION_MSG, FC_DURATION_TRACE as usual.
*
* If you use only macros provided here to do timing, the entire timing code
- * can be compiled out by defining \c FC_LOG_NO_TIMING before including
+ * can be compiled out by defining \c FC_LOG_NO_TIMING before including
* \c App/Console.h.
*
* \section Customization
@@ -300,7 +300,7 @@
* function returns a pointer to an integer representing the log level. Python
* developer or end-user can set/get the same tag based log level using
* FreeCAD.setLogLevel/getLogLevel. Any change to the log level is reflected
- * through the pointer returned by Base::Console().GetLogLevel(). What
+ * through the pointer returned by Base::Console().GetLogLevel(). What
* \c FC_LOG_LEVEL_INIT(tag) does is to define a class Base::LogLevel, and then
* a file static instance of that class to store the pointer to the desired tag
* log level. The class and instance name is predefined. Various log macros
@@ -326,7 +326,7 @@
*
* You can also define your own log levels the same way. Macro
* #_FC_PRINT(_instance,_l,_func,_msg) checks to see if the log shall proceed,
- * where \c _instance is the static loglevel instance name (default is
+ * where \c _instance is the static loglevel instance name (default is
* \c FC_LOG_INSTANCE), and \c _l is the log level constant to be checked,
* \c _func is the Base::Console() function to print the log.
*
@@ -428,7 +428,7 @@
# define FC_DURATION_PLUS(...) do{}while(0)
#endif //FC_LOG_NO_TIMING
-
+
namespace Base {
class ConsoleSingleton;
} // namespace Base
@@ -449,7 +449,7 @@ namespace Base {
#endif
/** The console observer class
- * This class distribute the Messages issued to the FCConsole class.
+ * This class distribute the Messages issued to the FCConsole class.
* If you need to catch some of the Messages you need to inherit from
* this class and implement some of the methods.
* @see Console
@@ -475,7 +475,7 @@ public:
/** The console class
- * This class manage all the stdio stuff. This includes
+ * This class manage all the stdio stuff. This includes
* Messages, Warnings, Log entries and Errors. The incoming
* Messages are distributed with the FCConsoleObserver. The
* FCConsole class itself makes no IO, it's more like a manager.
@@ -495,18 +495,18 @@ class BaseExport ConsoleSingleton
{
public:
-
+ static const unsigned int BufferSize = 4024;
// exported functions goes here +++++++++++++++++++++++++++++++++++++++
- /// Prints a Message
+ /// Prints a Message
virtual void Message ( const char * pMsg, ... ) ;
- /// Prints a warning Message
+ /// Prints a warning Message
virtual void Warning ( const char * pMsg, ... ) ;
- /// Prints a error Message
+ /// Prints a error Message
virtual void Error ( const char * pMsg, ... ) ;
- /// Prints a log Message
+ /// Prints a log Message
virtual void Log ( const char * pMsg, ... ) ;
- /// Delivers a time/date string
+ /// Delivers a time/date string
const char* Time(void);
/// Attaches an Observer to FCConsole
void AttachObserver(ConsoleObserver *pcObserver);
@@ -548,7 +548,7 @@ public:
return level<0?_defaultLogLevel:level;
}
- /// singleton
+ /// singleton
static ConsoleSingleton &Instance(void);
// retrieval of an observer by name
@@ -560,7 +560,7 @@ public:
void EnableRefresh(bool enable);
protected:
- // python exports goes here +++++++++++++++++++++++++++++++++++++++++++
+ // python exports goes here +++++++++++++++++++++++++++++++++++++++++++
// static python wrapper of the exported functions
static PyObject *sPyLog (PyObject *self,PyObject *args);
static PyObject *sPyMessage (PyObject *self,PyObject *args);
@@ -582,7 +582,7 @@ private:
static void Destruct(void);
static ConsoleSingleton *_pcSingleton;
- // observer processing
+ // observer processing
void NotifyMessage(const char *sMsg);
void NotifyWarning(const char *sMsg);
void NotifyError (const char *sMsg);
@@ -598,9 +598,9 @@ private:
};
/** Access to the Console
- * This method is used to gain access to the one and only instance of
+ * This method is used to gain access to the one and only instance of
* the ConsoleSingleton class.
- */
+ */
inline ConsoleSingleton &Console(void){
return ConsoleSingleton::Instance();
}
@@ -617,7 +617,7 @@ public:
};
-/** LogLevel helper class */
+/** LogLevel helper class */
class BaseExport LogLevel {
public:
std::string tag;
@@ -652,7 +652,7 @@ public:
/** The LoggingConsoleObserver class
* This class is used by the main modules to write Console messages and logs to a file
- */
+ */
class BaseExport ConsoleObserverFile : public ConsoleObserver
{
public:
@@ -677,9 +677,9 @@ public:
ConsoleObserverStd();
virtual ~ConsoleObserverStd();
virtual void Warning(const char *sWarn);
- virtual void Message(const char *sMsg);
- virtual void Error (const char *sErr);
- virtual void Log (const char *sErr);
+ virtual void Message(const char *sMsg);
+ virtual void Error (const char *sErr);
+ virtual void Log (const char *sErr);
const char* Name(void){return "Console";}
protected:
bool useColorStderr;
@@ -725,6 +725,6 @@ private:
};
-} // namespace Base
+} // namespace Base
#endif // BASE_CONSOLE_H
diff --git a/src/Mod/Fem/femguiobjects/_ViewProviderFemMaterial.py b/src/Mod/Fem/femguiobjects/_ViewProviderFemMaterial.py
index 325214b26d..dbbfc056c2 100644
--- a/src/Mod/Fem/femguiobjects/_ViewProviderFemMaterial.py
+++ b/src/Mod/Fem/femguiobjects/_ViewProviderFemMaterial.py
@@ -1,6 +1,6 @@
# ***************************************************************************
-# * *
-# * Copyright (c) 2013 Juergen Riegel *
+# * Copyright (c) 2013 Juergen Riegel *
+# * Copyright (c) 2016 Bernd Hahnebach *
# * *
# * This program is free software; you can redistribute it and/or modify *
# * it under the terms of the GNU Lesser General Public License (LGPL) *
@@ -77,10 +77,12 @@ class _ViewProviderFemMaterial:
FreeCADGui.Control.closeDialog()
return True
- # overwrite the doubleClicked of material object python to make sure no other Material taskd (and thus no selection observer) is still active
+ # overwrite the doubleClicked of material object python to make sure no other Material taskd
+ # (and thus no selection observer) is still active
def doubleClicked(self, vobj):
guidoc = FreeCADGui.getDocument(vobj.Object.Document)
- # check if another VP is in edit mode, https://forum.freecadweb.org/viewtopic.php?t=13077#p104702
+ # check if another VP is in edit mode
+ # https://forum.freecadweb.org/viewtopic.php?t=13077#p104702
if not guidoc.getInEdit():
guidoc.setEdit(vobj.Object.Name)
else:
@@ -106,31 +108,79 @@ class _TaskPanelFemMaterial:
self.material = self.obj.Material # FreeCAD material dictionary of current material
self.card_path = ''
self.materials = {} # { card_path : FreeCAD material dict }
+ self.icons = {} # { card_path : icon_path }
# mat_card is the FCMat file
# card_name is the file name of the mat_card
# card_path is the whole file path of the mat_card
# material_name is the value of the key name in FreeCAD material dictionary
- # they might not match because of special letters in the material_name which are changed in the card_name to english standard characters
+ # they might not match because of special letters in the material_name
+ # which are changed in the card_name to english standard characters
self.has_transient_mat = False
# parameter widget
- self.parameterWidget = FreeCADGui.PySideUic.loadUi(FreeCAD.getHomePath() + "Mod/Fem/Resources/ui/Material.ui")
+ self.parameterWidget = FreeCADGui.PySideUic.loadUi(
+ FreeCAD.getHomePath() + "Mod/Fem/Resources/ui/Material.ui"
+ )
# globals
- QtCore.QObject.connect(self.parameterWidget.cb_materials, QtCore.SIGNAL("activated(int)"), self.choose_material)
- QtCore.QObject.connect(self.parameterWidget.chbu_allow_edit, QtCore.SIGNAL("clicked()"), self.toggleInputFieldsReadOnly)
- QtCore.QObject.connect(self.parameterWidget.pushButton_editMat, QtCore.SIGNAL("clicked()"), self.edit_material)
+ QtCore.QObject.connect(
+ self.parameterWidget.cb_materials,
+ QtCore.SIGNAL("activated(int)"),
+ self.choose_material
+ )
+ QtCore.QObject.connect(
+ self.parameterWidget.chbu_allow_edit,
+ QtCore.SIGNAL("clicked()"),
+ self.toggleInputFieldsReadOnly
+ )
+ QtCore.QObject.connect(
+ self.parameterWidget.pushButton_editMat,
+ QtCore.SIGNAL("clicked()"),
+ self.edit_material
+ )
# basic properties must be provided
- QtCore.QObject.connect(self.parameterWidget.input_fd_density, QtCore.SIGNAL("editingFinished()"), self.density_changed)
+ QtCore.QObject.connect(
+ self.parameterWidget.input_fd_density,
+ QtCore.SIGNAL("editingFinished()"),
+ self.density_changed
+ )
# mechanical properties
- QtCore.QObject.connect(self.parameterWidget.input_fd_young_modulus, QtCore.SIGNAL("editingFinished()"), self.ym_changed)
- QtCore.QObject.connect(self.parameterWidget.spinBox_poisson_ratio, QtCore.SIGNAL("editingFinished()"), self.pr_changed)
+ QtCore.QObject.connect(
+ self.parameterWidget.input_fd_young_modulus,
+ QtCore.SIGNAL("editingFinished()"),
+ self.ym_changed
+ )
+ QtCore.QObject.connect(
+ self.parameterWidget.spinBox_poisson_ratio,
+ QtCore.SIGNAL("editingFinished()"),
+ self.pr_changed
+ )
# thermal properties
- QtCore.QObject.connect(self.parameterWidget.input_fd_thermal_conductivity, QtCore.SIGNAL("editingFinished()"), self.tc_changed)
- QtCore.QObject.connect(self.parameterWidget.input_fd_expansion_coefficient, QtCore.SIGNAL("editingFinished()"), self.tec_changed)
- QtCore.QObject.connect(self.parameterWidget.input_fd_specific_heat, QtCore.SIGNAL("editingFinished()"), self.sh_changed)
+ QtCore.QObject.connect(
+ self.parameterWidget.input_fd_thermal_conductivity,
+ QtCore.SIGNAL("editingFinished()"),
+ self.tc_changed
+ )
+ QtCore.QObject.connect(
+ self.parameterWidget.input_fd_expansion_coefficient,
+ QtCore.SIGNAL("editingFinished()"),
+ self.tec_changed
+ )
+ QtCore.QObject.connect(
+ self.parameterWidget.input_fd_specific_heat,
+ QtCore.SIGNAL("editingFinished()"),
+ self.sh_changed
+ )
# fluidic properties, only volumetric thermal expansion coeff makes sense
- QtCore.QObject.connect(self.parameterWidget.input_fd_kinematic_viscosity, QtCore.SIGNAL("editingFinished()"), self.kinematic_viscosity_changed)
- QtCore.QObject.connect(self.parameterWidget.input_fd_vol_expansion_coefficient, QtCore.SIGNAL("editingFinished()"), self.vtec_changed)
+ QtCore.QObject.connect(
+ self.parameterWidget.input_fd_kinematic_viscosity,
+ QtCore.SIGNAL("editingFinished()"),
+ self.kinematic_viscosity_changed
+ )
+ QtCore.QObject.connect(
+ self.parameterWidget.input_fd_vol_expansion_coefficient,
+ QtCore.SIGNAL("editingFinished()"),
+ self.vtec_changed
+ )
# init all parameter input files with read only
self.parameterWidget.chbu_allow_edit.setCheckState(QtCore.Qt.CheckState.Unchecked)
@@ -147,8 +197,10 @@ class _TaskPanelFemMaterial:
self.parameterWidget.label_vol_expansion_coefficient.setVisible(0)
self.parameterWidget.input_fd_vol_expansion_coefficient.setVisible(0)
- # fill self.materials dict and fill the combobox with material cards
+ # get all available materials (fill self.materials and self.icons)
self.import_materials()
+ # fill the material comboboxes with material cards
+ self.add_cards_to_combo_box()
# search for exact this mat_card in all known cards, choose the current material
self.card_path = self.get_material_card(self.material)
@@ -156,22 +208,38 @@ class _TaskPanelFemMaterial:
if not self.card_path:
# we have not found our material in self.materials dict :-(
# we're going to add a user-defined temporary material: a document material
- FreeCAD.Console.PrintMessage("Previously used material card cannot be found in material directories. Add document material.\n")
+ FreeCAD.Console.PrintMessage(
+ "Previously used material card cannot be found in material directories. "
+ "Add document material.\n"
+ )
self.card_path = '_document_material'
self.materials[self.card_path] = self.material
- self.parameterWidget.cb_materials.addItem(QtGui.QIcon(":/icons/help-browser.svg"), self.card_path, self.card_path)
+ self.parameterWidget.cb_materials.addItem(
+ QtGui.QIcon(":/icons/help-browser.svg"),
+ self.card_path,
+ self.card_path
+ )
index = self.parameterWidget.cb_materials.findData(self.card_path)
# print(index)
- self.choose_material(index) # fill input fields and set the current material in the cb widget
+ # fill input fields and set the current material in the cb widget
+ self.choose_material(index)
else:
# we found our exact material in self.materials dict :-)
- FreeCAD.Console.PrintMessage("Previously used material card was found in material directories. We will use this material.\n")
+ FreeCAD.Console.PrintMessage(
+ "Previously used material card was found in material directories. "
+ "We will use this material.\n"
+ )
index = self.parameterWidget.cb_materials.findData(self.card_path)
# print(index)
- self.choose_material(index) # fill input fields and set the current material in the cb widget
+ # fill input fields and set the current material in the cb widget
+ self.choose_material(index)
# geometry selection widget
- self.selectionWidget = FemSelectionWidgets.GeometryElementsSelection(obj.References, ['Solid', 'Face', 'Edge'], False) # start with Solid in list!
+ self.selectionWidget = FemSelectionWidgets.GeometryElementsSelection(
+ obj.References,
+ ['Solid', 'Face', 'Edge'],
+ False
+ ) # start with Solid in list!
# form made from param and selection widget
self.form = [self.parameterWidget, self.selectionWidget]
@@ -179,10 +247,11 @@ class _TaskPanelFemMaterial:
# check references, has to be after initialisation of selectionWidget
self.selectionWidget.has_equal_references_shape_types()
- # leave task panel **********************************************
+ # leave task panel ***************************************************************************
def accept(self):
# print(self.material)
if self.selectionWidget.has_equal_references_shape_types():
+ self.do_not_set_thermal_zeros()
self.obj.Material = self.material
self.obj.References = self.selectionWidget.references
self.recompute_and_set_back_all()
@@ -200,11 +269,35 @@ class _TaskPanelFemMaterial:
FreeCADGui.Selection.removeObserver(self.selectionWidget.sel_server)
doc.resetEdit()
- # choose material ***********************************************
+ def do_not_set_thermal_zeros(self):
+ ''' thermal material parameter are set to 0.0 if not available
+ this leads to wrong material values and to not finding the card
+ on reopen the task pane, thus do not write thermal parameter,
+ if they are 0.0
+ '''
+ if Units.Quantity(self.material['ThermalConductivity']) == 0.0:
+ self.material.pop('ThermalConductivity', None)
+ FreeCAD.Console.PrintMessage(
+ 'Zero ThermalConductivity value. It is not saved in the card data.\n'
+ )
+ if Units.Quantity(self.material['ThermalExpansionCoefficient']) == 0.0:
+ self.material.pop('ThermalExpansionCoefficient', None)
+ FreeCAD.Console.PrintMessage(
+ 'Zero ThermalExpansionCoefficient value. It is not saved in the card data.\n'
+ )
+ if Units.Quantity(self.material['SpecificHeat']) == 0.0:
+ self.material.pop('SpecificHeat', None)
+ FreeCAD.Console.PrintMessage(
+ 'Zero SpecificHeat value. It is not saved in the card data.\n'
+ )
+
+ # choose material ****************************************************************************
def get_material_card(self, material):
for a_mat in self.materials:
unmatched_items = set(self.materials[a_mat].items()) ^ set(material.items())
# print(a_mat + ' --> unmatched_items = ' + str(len(unmatched_items)))
+ # if len(unmatched_items) < 4:
+ # print(unmatched_items)
if len(unmatched_items) == 0:
return a_mat
return ""
@@ -212,7 +305,7 @@ class _TaskPanelFemMaterial:
def choose_material(self, index):
if index < 0:
return
- self.card_path = self.parameterWidget.cb_materials.itemData(index) # returns the whole path !
+ self.card_path = self.parameterWidget.cb_materials.itemData(index) # returns whole path
# print('choose_material: ' + self.card_path)
self.material = self.materials[self.card_path]
self.check_material_keys()
@@ -237,28 +330,52 @@ class _TaskPanelFemMaterial:
def add_transient_material(self):
self.has_transient_mat = True
self.card_path = '_transient_material'
- self.parameterWidget.cb_materials.addItem(QtGui.QIcon(":/icons/help-browser.svg"), self.card_path, self.card_path)
+ self.parameterWidget.cb_materials.addItem(
+ QtGui.QIcon(":/icons/help-browser.svg"),
+ self.card_path,
+ self.card_path
+ )
self.set_transient_material()
- # how to edit a material ****************************************
+ # how to edit a material *********************************************************************
def edit_material(self):
# opens the material editor to choose a material or edit material params
# self.print_material_params()
import MaterialEditor
new_material_params = self.material.copy()
new_material_params = MaterialEditor.editMaterial(new_material_params)
- # if the material editor was canceled a empty params dict will be returned, do not change the self.material
+ # if the material editor was canceled a empty params dict will be returned
+ # do not change the self.material
# self.print_material_params(new_material_params)
- if new_material_params: # returns True if dict is not empty (do not use 'is True', this would return False for a non empty dict)
+ # check if dict is not empty (do not use 'is True'
+ if new_material_params:
self.material = new_material_params
- self.check_material_keys()
- self.set_mat_params_in_input_fields(self.material)
- if self.has_transient_mat is False:
- self.add_transient_material()
+ self.card_path = self.get_material_card(self.material)
+ # print('card_path: ' + self.card_path)
+ self.check_material_keys()
+ self.set_mat_params_in_input_fields(self.material)
+ if not self.card_path:
+ if self.has_transient_mat is False:
+ self.add_transient_material()
+ else:
+ self.set_transient_material()
+ else:
+ # we found our exact material in self.materials dict :-)
+ FreeCAD.Console.PrintMessage(
+ "Material card was found in material directories. "
+ "We will use this material.\n"
+ )
+ index = self.parameterWidget.cb_materials.findData(self.card_path)
+ # print(index)
+ # set the current material in the cb widget
+ self.choose_material(index)
else:
- self.set_transient_material()
+ FreeCAD.Console.PrintMessage(
+ 'No changes where made by the material editor.\n'
+ )
# self.print_material_params()
- # material editor returns the mat_dict only not a card_path, if a standard FreeCAD mat_card was used
+ # material editor returns the mat_dict only not a card_path
+ # if a standard FreeCAD mat_card was used
def toggleInputFieldsReadOnly(self):
if self.parameterWidget.chbu_allow_edit.isChecked():
@@ -280,10 +397,11 @@ class _TaskPanelFemMaterial:
self.parameterWidget.input_fd_kinematic_viscosity.setReadOnly(True)
self.parameterWidget.input_fd_vol_expansion_coefficient.setReadOnly(True)
- # material parameter input fields *******************************
+ # material parameter input fields ************************************************************
def print_material_params(self, material=None):
# in rare cases we gone pass a empty dict
- # in such a case a empty dict should be printed and not self.material thus we check for None
+ # in such a case a empty dict should be printed and not self.material
+ # thus we check for None
if material is None:
material = self.material
if not material:
@@ -297,72 +415,131 @@ class _TaskPanelFemMaterial:
def check_material_keys(self):
# FreeCAD units definition is at file end of src/Base/Unit.cpp
if not self.material:
- print('For some reason all material data is empty!')
+ FreeCAD.Console.PrintMessage('For some reason all material data is empty!\n')
self.material['Name'] = 'Empty'
if 'Density' in self.material:
if 'Density' not in str(Units.Unit(self.material['Density'])):
- print('Density in material data seems to have no unit or a wrong unit (reset the value): ' + self.material['Name'])
+ FreeCAD.Console.PrintMessage(
+ 'Density in material data seems to have no unit '
+ 'or a wrong unit (reset the value): {}\n'
+ .format(self.material['Name'])
+ )
self.material['Density'] = '0 kg/m^3'
else:
- print('Density not found in material data of: ' + self.material['Name'])
+ FreeCAD.Console.PrintMessage(
+ 'Density not found in material data of: {}\n'
+ .format(self.material['Name'])
+ )
self.material['Density'] = '0 kg/m^3'
if self.obj.Category == 'Solid':
# mechanical properties
if 'YoungsModulus' in self.material:
- if 'Pressure' not in str(Units.Unit(self.material['YoungsModulus'])): # unit type of YoungsModulus is Pressure
- print('YoungsModulus in material data seems to have no unit or a wrong unit (reset the value): ' + self.material['Name'])
+ # unit type of YoungsModulus is Pressure
+ if 'Pressure' not in str(Units.Unit(self.material['YoungsModulus'])):
+ FreeCAD.Console.PrintMessage(
+ 'YoungsModulus in material data seems to have no unit '
+ 'or a wrong unit (reset the value): {}\n'
+ .format(self.material['Name'])
+ )
self.material['YoungsModulus'] = '0 MPa'
else:
- print('YoungsModulus not found in material data of: ' + self.material['Name'])
+ FreeCAD.Console.PrintMessage(
+ 'YoungsModulus not found in material data of: {}\n'
+ .format(self.material['Name'])
+ )
self.material['YoungsModulus'] = '0 MPa'
if 'PoissonRatio' in self.material:
# PoissonRatio does not have a unit, but it is checked it there is no value at all
try:
float(self.material['PoissonRatio'])
except:
- print('PoissonRatio has wrong or no data (reset the value): ' + self.material['PoissonRatio'])
+ FreeCAD.Console.PrintMessage(
+ 'PoissonRatio has wrong or no data (reset the value): {}\n'
+ .format(self.material['PoissonRatio'])
+ )
self.material['PoissonRatio'] = '0'
else:
- print('PoissonRatio not found in material data of: ' + self.material['Name'])
+ FreeCAD.Console.PrintMessage(
+ 'PoissonRatio not found in material data of: {}\n'
+ .format(self.material['Name'])
+ )
self.material['PoissonRatio'] = '0'
if self.obj.Category == 'Fluid':
# Fluidic properties
if 'KinematicViscosity' in self.material:
- if 'KinematicViscosity' not in str(Units.Unit(self.material['KinematicViscosity'])):
- print('KinematicViscosity in material data seems to have no unit or a wrong unit (reset the value): ' + self.material['Name'])
+ ki_vis = self.material['KinematicViscosity']
+ if 'KinematicViscosity' not in str(Units.Unit(ki_vis)):
+ FreeCAD.Console.PrintMessage(
+ 'KinematicViscosity in material data seems to have no unit '
+ 'or a wrong unit (reset the value): {}\n'
+ .format(self.material['Name'])
+ )
self.material['KinematicViscosity'] = '0 m^2/s'
else:
- print('KinematicViscosity not found in material data of: ' + self.material['Name'])
+ FreeCAD.Console.PrintMessage(
+ 'KinematicViscosity not found in material data of: {}\n'
+ .format(self.material['Name'])
+ )
self.material['KinematicViscosity'] = '0 m^2/s'
if 'VolumetricThermalExpansionCoefficient' in self.material:
- # unit type of VolumetricThermalExpansionCoefficient is ThermalExpansionCoefficient
- if 'VolumetricThermalExpansionCoefficient' not in str(Units.Unit(self.material['VolumetricThermalExpansionCoefficient'])):
- print('VolumetricThermalExpansionCoefficient in material data seems to have no unit or a wrong unit (reset the value): ' + self.material['Name'])
+ # unit type VolumetricThermalExpansionCoefficient is ThermalExpansionCoefficient
+ vol_ther_ex_co = self.material['VolumetricThermalExpansionCoefficient']
+ if 'VolumetricThermalExpansionCoefficient' not in str(Units.Unit(vol_ther_ex_co)):
+ FreeCAD.Console.PrintMessage(
+ 'VolumetricThermalExpansionCoefficient in material data '
+ 'seems to have no unit or a wrong unit (reset the value): {}\n'
+ .format(self.material['Name'])
+ )
self.material['VolumetricThermalExpansionCoefficient'] = '0 m/m/K'
else:
- print('VolumetricThermalExpansionCoefficient not found in material data of: ' + self.material['Name'])
+ FreeCAD.Console.PrintMessage(
+ 'VolumetricThermalExpansionCoefficient not found in material data of: {}\n'
+ .format(self.material['Name'])
+ )
self.material['VolumetricThermalExpansionCoefficient'] = '0 m/m/K'
# Thermal properties
if 'ThermalConductivity' in self.material:
if 'ThermalConductivity' not in str(Units.Unit(self.material['ThermalConductivity'])):
- print('ThermalConductivity in material data seems to have no unit or a wrong unit (reset the value): ' + self.material['Name'])
+ FreeCAD.Console.PrintMessage(
+ 'ThermalConductivity in material data seems to have no unit '
+ 'or a wrong unit (reset the value): {}\n'
+ .format(self.material['Name'])
+ )
self.material['ThermalConductivity'] = '0 W/m/K'
else:
- print('ThermalConductivity not found in material data of: ' + self.material['Name'])
+ FreeCAD.Console.PrintMessage(
+ 'ThermalConductivity not found in material data of: {}\n'
+ .format(self.material['Name'])
+ )
self.material['ThermalConductivity'] = '0 W/m/K'
if 'ThermalExpansionCoefficient' in self.material:
- if 'ThermalExpansionCoefficient' not in str(Units.Unit(self.material['ThermalExpansionCoefficient'])):
- print('ThermalExpansionCoefficient in material data seems to have no unit or a wrong unit (reset the value): ' + self.material['Name'])
+ the_ex_co = self.material['ThermalExpansionCoefficient']
+ if 'ThermalExpansionCoefficient' not in str(Units.Unit(the_ex_co)):
+ FreeCAD.Console.PrintMessage(
+ 'ThermalExpansionCoefficient in material data seems to have no unit '
+ 'or a wrong unit (reset the value): {}\n'
+ .format(self.material['Name'])
+ )
self.material['ThermalExpansionCoefficient'] = '0 um/m/K'
else:
- print('ThermalExpansionCoefficient not found in material data of: ' + self.material['Name'])
+ FreeCAD.Console.PrintMessage(
+ 'ThermalExpansionCoefficient not found in material data of: {}\n'
+ .format(self.material['Name'])
+ )
self.material['ThermalExpansionCoefficient'] = '0 um/m/K'
if 'SpecificHeat' in self.material:
if 'SpecificHeat' not in str(Units.Unit(self.material['SpecificHeat'])):
- print('SpecificHeat in material data seems to have no unit or a wrong unit (reset the value): ' + self.material['Name'])
+ FreeCAD.Console.PrintMessage(
+ 'SpecificHeat in material data seems to have no unit '
+ 'or a wrong unit (reset the value): {}\n'
+ .format(self.material['Name'])
+ )
self.material['SpecificHeat'] = '0 J/kg/K'
else:
- print('SpecificHeat not found in material data of: ' + self.material['Name'])
+ FreeCAD.Console.PrintMessage(
+ 'SpecificHeat not found in material data of: {}\n'
+ .format(self.material['Name'])
+ )
self.material['SpecificHeat'] = '0 J/kg/K'
# mechanical input fields
@@ -392,7 +569,8 @@ class _TaskPanelFemMaterial:
# density has changed
material = self.material
value_in_kg_per_m3 = value * 1e9
- material['Density'] = unicode(value_in_kg_per_m3) + " kg/m^3" # SvdW:Keep density in SI units for easier readability
+ # SvdW:Keep density in SI units for easier readability
+ material['Density'] = unicode(value_in_kg_per_m3) + " kg/m^3"
self.material = material
if self.has_transient_mat is False:
self.add_transient_material()
@@ -442,7 +620,9 @@ class _TaskPanelFemMaterial:
def tec_changed(self):
value = self.parameterWidget.input_fd_expansion_coefficient.property("rawValue")
- old_tec = Units.Quantity(self.material['ThermalExpansionCoefficient']).getValueAs("um/m/K")
+ old_tec = Units.Quantity(
+ self.material['ThermalExpansionCoefficient']
+ ).getValueAs("um/m/K")
variation = 0.001
if value:
if not (1 - variation < float(old_tec) / value < 1 + variation):
@@ -475,14 +655,16 @@ class _TaskPanelFemMaterial:
# fluidic input fields
def vtec_changed(self):
value = self.parameterWidget.input_fd_vol_expansion_coefficient.property("rawValue")
- old_vtec = Units.Quantity(self.material['VolumetricThermalExpansionCoefficient']).getValueAs("m/m/K")
+ old_vtec = Units.Quantity(
+ self.material['VolumetricThermalExpansionCoefficient']
+ ).getValueAs("m/m/K")
variation = 0.001
if value:
if not (1 - variation < float(old_vtec) / value < 1 + variation):
# VolumetricThermalExpansionCoefficient has changed
material = self.material
- value_in_one_per_K = value
- material['VolumetricThermalExpansionCoefficient'] = unicode(value_in_one_per_K) + " m/m/K"
+ value_in_one_per_K = unicode(value) + " m/m/K"
+ material['VolumetricThermalExpansionCoefficient'] = value_in_one_per_K
self.material = material
if self.has_transient_mat is False:
self.add_transient_material()
@@ -521,7 +703,8 @@ class _TaskPanelFemMaterial:
nu_with_new_unit = nu.getValueAs(nu_new_unit)
q = FreeCAD.Units.Quantity("{} {}".format(nu_with_new_unit, nu_new_unit))
self.parameterWidget.input_fd_kinematic_viscosity.setText(q.UserString)
- # For isotropic materials the volumetric thermal expansion coefficient is three times the linear coefficient:
+ # For isotropic materials the volumetric thermal expansion coefficient
+ # is three times the linear coefficient:
if 'VolumetricThermalExpansionCoefficient' in matmap: # linear, only for solid
vtec_new_unit = "m/m/K"
vtec = FreeCAD.Units.Quantity(matmap['VolumetricThermalExpansionCoefficient'])
@@ -532,7 +715,9 @@ class _TaskPanelFemMaterial:
density_new_unit = "kg/m^3"
density = FreeCAD.Units.Quantity(matmap['Density'])
density_with_new_unit = density.getValueAs(density_new_unit)
- # self.parameterWidget.input_fd_density.setText("{} {}".format(density_with_new_unit, density_new_unit))
+ # self.parameterWidget.input_fd_density.setText(
+ # "{} {}".format(density_with_new_unit, density_new_unit)
+ # )
q = FreeCAD.Units.Quantity("{} {}".format(density_with_new_unit, density_new_unit))
self.parameterWidget.input_fd_density.setText(q.UserString)
# thermal properties
@@ -555,7 +740,20 @@ class _TaskPanelFemMaterial:
q = FreeCAD.Units.Quantity("{} {}".format(sh_with_new_unit, sh_new_unit))
self.parameterWidget.input_fd_specific_heat.setText(q.UserString)
- # material import and export ************************************
+ # fill the combo box with cards **************************************************************
+ def add_cards_to_combo_box(self):
+ # fill combobox, in combo box the card name is used not the material name
+ from os.path import basename
+ self.parameterWidget.cb_materials.clear()
+ card_name_list = []
+ for a_path in self.materials:
+ card_name = basename(a_path[:-(len(".FCMat"))])
+ card_name_list.append([card_name, a_path, self.icons[a_path]])
+ card_name_list.sort()
+ for mat in card_name_list:
+ self.parameterWidget.cb_materials.addItem(QtGui.QIcon(mat[2]), mat[0], mat[1])
+
+ # material card handling *********************************************************************
def print_materialsdict(self):
print('\n\n')
for mat_card in self.materials:
@@ -564,10 +762,9 @@ class _TaskPanelFemMaterial:
print('\n\n')
def import_materials(self):
- self.pathList = []
- self.parameterWidget.cb_materials.clear()
-
- self.fem_prefs = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Material/Resources")
+ self.fem_prefs = FreeCAD.ParamGet(
+ "User parameter:BaseApp/Preferences/Mod/Material/Resources"
+ )
if self.obj.Category == 'Fluid':
self.import_fluid_materials()
else:
@@ -607,18 +804,15 @@ class _TaskPanelFemMaterial:
self.add_cards_from_a_dir(custom_mat_dir, ":/icons/user.svg")
def add_cards_from_a_dir(self, mat_dir, icon):
+ # fill self.materials and self.icons
import glob
- import os
- import Material
- mat_file_extension = ".FCMat"
- ext_len = len(mat_file_extension)
- dir_path_list = glob.glob(mat_dir + '/*' + mat_file_extension)
- self.pathList = self.pathList + dir_path_list
- card_name_list = []
+ from importFCMat import read
+ dir_path_list = glob.glob(mat_dir + '/*' + ".FCMat")
+
for a_path in dir_path_list:
- card_name = os.path.basename(a_path[:-ext_len])
- self.materials[a_path] = Material.importFCMat(a_path)
- card_name_list.append([card_name, a_path])
- card_name_list.sort()
- for mat in card_name_list:
- self.parameterWidget.cb_materials.addItem(QtGui.QIcon(icon), mat[0], mat[1])
+ mat_dict = read(a_path)
+ # check if the dict exists in materials
+ # TODO if the unit is different two cards would be different too
+ if mat_dict not in self.materials.values():
+ self.materials[a_path] = mat_dict
+ self.icons[a_path] = icon
diff --git a/src/Mod/Material/Material.py b/src/Mod/Material/Material.py
index 837bb8c718..d62548ca39 100644
--- a/src/Mod/Material/Material.py
+++ b/src/Mod/Material/Material.py
@@ -20,6 +20,9 @@
# * *
# ***************************************************************************
+import FreeCAD
+
+
# here the usage description if you use this tool from the command line ("__main__")
CommandlineUsage = """Material - Tool to work with FreeCAD Material definition cards
@@ -49,8 +52,9 @@ Version:
"""
-# see comments in module importFCMat, there is an independent parser implementation for reading and writing FCMat files
-# inside FreeCAD a mixture of these parsers and the ones in importFCMat.py is used
+# see comments in module importFCMat, there is an independent parser implementation
+# for reading and writing FCMat files
+# inside FreeCAD mostly the one from importFCMat.py is used
def importFCMat(fileName):
@@ -60,6 +64,10 @@ def importFCMat(fileName):
except ImportError:
import configparser
+ FreeCAD.Console.PrintError(
+ 'This mat card reader is probably depretiated and not widely used in FreeCAD. '
+ 'See comment in Material.py module.\n'
+ )
Config = configparser.RawConfigParser()
Config.optionxform = str
Config.read(fileName)
@@ -81,6 +89,10 @@ def exportFCMat(fileName, matDict):
import string
Config = configparser.RawConfigParser()
+ FreeCAD.Console.PrintError(
+ 'This mat card writer is probably depretiated and not widely used in FreeCAD. '
+ 'See comment in Material.py module.\n'
+ )
# create groups
for x in matDict.keys():
grp, key = string.split(x, sep='_')
@@ -104,7 +116,8 @@ def getMaterialAttributeStructure(withSpaces=None):
''''''
# material properties
- # see the following resources in the FreeCAD wiki for more information about the material specific properties:
+ # see the following resources in the FreeCAD wiki for more information
+ # about the material specific properties:
# https://www.freecadweb.org/wiki/Material_data_model
# https://www.freecadweb.org/wiki/Material
@@ -115,7 +128,8 @@ def getMaterialAttributeStructure(withSpaces=None):
tree = ElementTree.parse(infile)
if withSpaces:
- # on attributes, add a space before a capital letter, will be used for better display in the ui
+ # on attributes, add a space before a capital letter
+ # will be used for better display in the ui
import re
root = tree.getroot()
for group in root.getchildren():
@@ -131,7 +145,7 @@ def read_cards_from_path(cards_path):
from os.path import isfile, join, basename, splitext
from importFCMat import read
only_files = [f for f in listdir(cards_path) if isfile(join(cards_path, f))]
- mat_files = [f for f in only_files if basename(splitext(f)[1]) == '.FCMat' or basename(splitext(f)[1]) == '.fcmat']
+ mat_files = [f for f in only_files if basename(splitext(f)[1]).upper() == '.FCMAT']
# print(mat_files)
mat_cards = []
for f in sorted(mat_files):
diff --git a/src/Mod/Material/MaterialEditor.py b/src/Mod/Material/MaterialEditor.py
index f10ea58096..b9cde2a69a 100644
--- a/src/Mod/Material/MaterialEditor.py
+++ b/src/Mod/Material/MaterialEditor.py
@@ -20,23 +20,21 @@
# * *
# ***************************************************************************
-
-from __future__ import print_function
-import FreeCAD
-import FreeCADGui
-from Material import getMaterialAttributeStructure
-import os
-from PySide import QtCore, QtGui
-# from PySide import QtUiTools, QtSvg
-import sys
-if sys.version_info.major >= 3:
- unicode = str
-
-
__title__ = "FreeCAD material editor"
__author__ = "Yorik van Havre"
__url__ = "http://www.freecadweb.org"
+import os
+import sys
+from PySide import QtCore, QtGui
+# from PySide import QtUiTools, QtSvg
+
+import FreeCAD
+import FreeCADGui
+
+if sys.version_info.major >= 3:
+ unicode = str
+
class MaterialEditor:
@@ -78,7 +76,8 @@ class MaterialEditor:
standardButtons.button(QtGui.QDialogButtonBox.Ok).setAutoDefault(False)
standardButtons.button(QtGui.QDialogButtonBox.Cancel).setAutoDefault(False)
self.updateCards()
- # TODO allow to enter a custom property by pressing Enter in the lineedit (currently closes the dialog)
+ # TODO allow to enter a custom property by pressing Enter in the lineedit
+ # currently closes the dialog
standardButtons.rejected.connect(self.reject)
standardButtons.accepted.connect(self.accept)
@@ -119,6 +118,7 @@ class MaterialEditor:
treeView.setColumnWidth(1, 250)
treeView.setColumnHidden(2, True)
+ from Material import getMaterialAttributeStructure
tree = getMaterialAttributeStructure(True)
MatPropDict = tree.getroot()
@@ -149,9 +149,12 @@ class MaterialEditor:
def updateContents(self, data):
'''updates the contents of the editor with the given data, can be:
- - the name of a card, if material is changed in editors combo box
- - a dictionary, if the editor was called with data.'''
+ - a dictionary, if the editor was called with data
+ - a string, the name of a card, if material is changed in editors combo box
+ the material property keys where added to the editor already
+ not known material property keys will be added to the user defined group'''
+ # print type(data)
if isinstance(data, dict):
# a standard material property dict is provided
model = self.widget.treeView.model()
@@ -182,18 +185,21 @@ class MaterialEditor:
self.customprops.append(k)
elif isinstance(data, unicode):
- # a card name is provided, search card, read material data and call this def once more with std material property dict
+ # a card name is provided, search card, read material data and call
+ # this def once more with std material property dict
k = str(data)
if k:
if k in self.cards:
- import importFCMat
- d = importFCMat.read(self.cards[k])
+ from importFCMat import read
+ d = read(self.cards[k])
if d:
self.updateContents(d)
def getMaterialResources(self):
- self.fem_prefs = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Material/Resources")
+ self.fem_prefs = FreeCAD.ParamGet(
+ "User parameter:BaseApp/Preferences/Mod/Material/Resources"
+ )
use_built_in_materials = self.fem_prefs.GetBool("UseBuiltInMaterials", True)
use_mat_from_config_dir = self.fem_prefs.GetBool("UseMaterialsFromConfigDir", True)
use_mat_from_custom_dir = self.fem_prefs.GetBool("UseMaterialsFromCustomDir", True)
@@ -203,7 +209,10 @@ class MaterialEditor:
# FreeCAD returns paths with / at the end, thus not os.sep is needed on first +
self.resources = []
if use_built_in_materials:
- self.resources.append(FreeCAD.getResourceDir() + "Mod" + os.sep + "Material" + os.sep + "StandardMaterial")
+ res_dir = FreeCAD.getResourceDir()
+ self.resources.append(
+ res_dir + "Mod" + os.sep + "Material" + os.sep + "StandardMaterial"
+ )
if use_mat_from_config_dir:
self.resources.append(FreeCAD.ConfigGet("UserAppData") + "Material")
if use_mat_from_custom_dir:
@@ -220,12 +229,14 @@ class MaterialEditor:
def outputCards(self):
print('material cards:')
- for card in self.cards:
+ for card in sorted(self.cards.keys()):
print(' ' + card + ': ' + self.cards[card])
print('\n')
def updateCards(self):
- "updates the contents of the materials combo with existing material cards"
+
+ '''updates the contents of the materials combo with existing material cards'''
+
self.getMaterialResources()
self.cards = {}
for p in self.resources:
@@ -238,11 +249,12 @@ class MaterialEditor:
if self.cards:
self.widget.ComboMaterial.clear()
self.widget.ComboMaterial.addItem("") # add a blank item first
- for k, i in self.cards.items():
- self.widget.ComboMaterial.addItem(k)
+ for card in sorted(self.cards.keys()):
+ self.widget.ComboMaterial.addItem(card) # all keys in self.cards are unicode
def openProductURL(self):
- "opens the contents of the ProductURL field in an external browser."
+
+ '''opens the contents of the ProductURL field in an external browser.'''
model = self.widget.treeView.model()
item = model.findItems(translate("Material", "Product URL"),
@@ -383,7 +395,8 @@ class MaterialEditor:
kk = group.child(row, 0).text()
ii = group.child(row, 1).text()
- # TODO the following should be translated back to english,since text(0) could be translated
+ # TODO the following should be translated back to english
+ # since text(0) could be translated
matkey = self.collapseKey(str(kk))
matvalue = unicode(ii)
if matvalue or (matkey == 'Name'):
@@ -395,9 +408,10 @@ class MaterialEditor:
def outputDict(self, d):
print('MaterialEditor dictionary')
for param in d:
- print(' ' + param + ' : ' + d[param])
+ print(' {} : {}'.format(param, d[param]))
- '''def setTexture(self, pattern):
+ '''
+ def setTexture(self, pattern):
"displays a texture preview if needed"
self.widget.PreviewVector.hide()
if pattern:
@@ -409,16 +423,23 @@ class MaterialEditor:
pattern = DrawingPatterns.buildFileSwatch(pattern, size=96, png=True)
if pattern:
self.widget.PreviewVector.setPixmap(QtGui.QPixmap(pattern))
- self.widget.PreviewVector.show()'''
+ self.widget.PreviewVector.show()
+ '''
def openfile(self):
"Opens a FCMat file"
- filetuple = QtGui.QFileDialog.getOpenFileName(QtGui.QApplication.activeWindow(), 'Open FreeCAD Material file', self.directory, '*.FCMat')
- filename = filetuple[0] # a tuple of two empty strings returns True, so use the filename directly
+ filetuple = QtGui.QFileDialog.getOpenFileName(
+ QtGui.QApplication.activeWindow(),
+ 'Open FreeCAD Material file',
+ self.directory,
+ '*.FCMat'
+ )
+ # a tuple of two empty strings returns True, so use the filename directly
+ filename = filetuple[0]
if filename:
- import importFCMat
+ from importFCMat import read
self.directory = os.path.dirname(filename)
- d = importFCMat.read(filename)
+ d = read(filename)
if d:
self.updateContents(d)
@@ -436,18 +457,21 @@ class MaterialEditor:
name = name.encode("utf8")
if not name:
name = "Material"
- filetuple =\
- QtGui.QFileDialog.getSaveFileName(QtGui.QApplication.activeWindow(),
- 'Save FreeCAD Material file',
- self.directory + '/' + name + '.FCMat', '*.FCMat')
- filename = filetuple[0] # a tuple of two empty strings returns True, so use the filename directly
+ filetuple = QtGui.QFileDialog.getSaveFileName(
+ QtGui.QApplication.activeWindow(),
+ 'Save FreeCAD Material file',
+ self.directory + '/' + name + '.FCMat',
+ '*.FCMat'
+ )
+ # a tuple of two empty strings returns True, so use the filename directly
+ filename = filetuple[0]
if filename:
self.directory = os.path.dirname(filename)
d = self.getDict()
# self.outputDict(d)
if d:
- import importFCMat
- importFCMat.write(filename, d)
+ from importFCMat import write
+ write(filename, d)
self.updateCards()
def show(self):
@@ -567,7 +591,7 @@ def matProperWidget(parent=None, matproperty=None, Type="String", Value=None,
quantity = FreeCAD.Units.Quantity(1, unit)
widget.setProperty('unit', quantity.getUserPreferred()[2])
else:
- FreeCAD.Console.PrintError('Not known unit for property: ' + matproperty + '\n')
+ FreeCAD.Console.PrintError('Not known unit for property: {}\n'.format(matproperty))
elif Type == "Integer":
@@ -644,9 +668,11 @@ def editMaterial(material):
"""editMaterial(material): opens the editor to edit the contents
of the given material dictionary. Returns the modified material dictionary."""
# if the material editor is opened with this def the combo box with the card name is empty
- # this makes sense, because the editor was not opened with a card but with material dictionary instead
+ # this makes sense ...
+ # because the editor was not opened with a card but with material dictionary instead
# TODO: add some text in combo box, may be "custom material data" or "user material data"
- # TODO: all card could be checked if one fits exact ALL provided data and than this card name could be displayed
+ # TODO: all card could be checked if one fits exact ALL provided data
+ # than this card name could be displayed
editor = MaterialEditor(material=material)
result = editor.exec_()
if result:
@@ -660,12 +686,19 @@ def editMaterial(material):
import MaterialEditor
MaterialEditor.openEditor()
-doc = FreeCAD.open(FreeCAD.ConfigGet("AppHomePath") + 'data/examples/FemCalculixCantilever3D.FCStd')
+doc = FreeCAD.open(
+ FreeCAD.ConfigGet("AppHomePath") + 'data/examples/FemCalculixCantilever3D.FCStd'
+)
import MaterialEditor
MaterialEditor.openEditor('SolidMaterial', 'Material')
import MaterialEditor
-MaterialEditor.editMaterial({'Density': '1234.0 kg/m^3', 'Name': 'My-Material-Data', 'PoissonRatio': '0.66', 'YoungsModulus': '123456 MPa'})
+MaterialEditor.editMaterial({
+ 'Density': '1234.0 kg/m^3',
+ 'Name': 'My-Material-Data',
+ 'PoissonRatio': '0.66',
+ 'YoungsModulus': '123456 MPa'
+})
import MaterialEditor
MaterialEditor.editMaterial('ABS')
diff --git a/src/Mod/Material/importFCMat.py b/src/Mod/Material/importFCMat.py
index 4e14ca5e81..f6e6ddfe52 100644
--- a/src/Mod/Material/importFCMat.py
+++ b/src/Mod/Material/importFCMat.py
@@ -78,13 +78,16 @@ def decode(name):
return decodedName
-# the reader and writer do not use some Library to read and write the ini file format, they are implemented here
+# the reader and writer do not use some Library to read and write the ini file format
+# they are implemented here
# thus non standard ini files will be read and written too
-# in standard ini file format a = in the value without any encapsulation or string quotes is not allowed (AFAIK)
+# in standard ini file format:
+# a = in the value without any encapsulation or string quotes is not allowed (AFAIK)
# https://en.wikipedia.org/wiki/INI_file
# http://www.docuxplorer.com/WebHelp/INI_File_Format.htm
# mainly this parser here is used in FreeCAD
-# in the module Material.py is another implementation of reading and writing FCMat files which uses the module ConfigParser
+# in the module Material.py is another implementation of reading and writing FCMat files
+# this implementation uses the ConfigParser module
# in ViewProviderFemMaterial in add_cards_from_a_dir() the parser from Material.py is used
# since this mixture seams to be there for ages it should not be changed for 0.18
# TODO: get rid of this mixture in FreeCAD 0.19
@@ -117,7 +120,10 @@ def read(filename):
v = v.decode('utf-8')
card_name_content = v
if card_name_content != d["CardName"]:
- FreeCAD.Console.PrintError("File CardName (" + card_name_file + ") is not content CardName (" + card_name_content + ")\n")
+ FreeCAD.Console.PrintLog(
+ "File CardName ( {} ) is not content CardName ( {} )\n"
+ .format(card_name_file, card_name_content)
+ )
elif ln == 1:
v = line.split(";")[1].strip() # Line 2
if hasattr(v, "decode"):
@@ -128,7 +134,9 @@ def read(filename):
# # might be a comment too ?
# [ is a Section
if line[0] not in ";#[":
- k = line.split("=", 1) # only split once on first occurrence, a link could contain a = and thus would be splitted
+ # split once on first occurrence
+ # a link could contain a = and thus would be split
+ k = line.split("=", 1)
if len(k) == 2:
v = k[1].strip()
if hasattr(v, "decode"):
@@ -166,12 +174,18 @@ def write(filename, dictionary, write_group_section=True):
user[k] = i
# delete empty properties
for group in contents:
- for k in list(group.keys()): # iterating over a dict and changing it is not allowed, thus we iterate over a list of the keys
+ # iterating over a dict and changing it is not allowed
+ # thus it is iterated over a list of the keys
+ for k in list(group.keys()):
if group[k] == '':
del group[k]
# card writer
- rev = FreeCAD.ConfigGet("BuildVersionMajor") + "." + FreeCAD.ConfigGet("BuildVersionMinor") + "." + FreeCAD.ConfigGet("BuildRevision")
+ rev = "{}.{}.{}".format(
+ FreeCAD.ConfigGet("BuildVersionMajor"),
+ FreeCAD.ConfigGet("BuildVersionMinor"),
+ FreeCAD.ConfigGet("BuildRevision")
+ )
if isinstance(filename, unicode):
if sys.version_info.major < 3:
filename = filename.encode(sys.getfilesystemencoding())
@@ -182,7 +196,8 @@ def write(filename, dictionary, write_group_section=True):
# write header
# first five lines are the same in any card file, see comment above read def
if header["CardName"] != card_name_file:
- FreeCAD.Console.PrintMessage("File CardName is used: " + card_name_file + " \n") # CardName is the MatCard file name
+ # CardName is the MatCard file name
+ FreeCAD.Console.PrintMessage("File CardName is used: {}\n".format(card_name_file))
if sys.version_info.major >= 3:
f.write("; " + card_name_file + "\n")
f.write("; " + header["AuthorAndLicense"] + "\n")
diff --git a/src/Mod/Material/materials-editor.ui b/src/Mod/Material/materials-editor.ui
index 7b29f1e6cf..ef1990540c 100644
--- a/src/Mod/Material/materials-editor.ui
+++ b/src/Mod/Material/materials-editor.ui
@@ -17,77 +17,70 @@
-
-
-
-
-
-
-
-
- 22
- 22
-
-
-
- Opens the Product URL of this material in an external browser
-
-
-
-
-
-
- -
-
-
- Material card:
-
-
-
- -
-
-
-
- 120
- 0
-
-
-
- Existing material cards
-
-
-
- -
-
-
- Qt::Horizontal
-
-
-
- 40
- 20
-
-
-
-
- -
-
-
- Opens an existing material card
-
-
- Open...
-
-
-
- -
-
-
- Saves this material as a card
-
-
- Save as...
-
-
-
-
+
+
+ Material card
+
+
+ -
+
+
-
+
+
+
+ 22
+ 22
+
+
+
+ Opens the Product URL of this material in an external browser
+
+
+
+
+
+
+ -
+
+
+
+ 120
+ 0
+
+
+
+ Existing material cards
+
+
+
+
+
+ -
+
+
-
+
+
+ Opens an existing material card
+
+
+ Open...
+
+
+
+ -
+
+
+ Saves this material as a card
+
+
+ Save as...
+
+
+
+
+
+
+
-
@@ -138,19 +131,28 @@
-
-
-
- QLayout::SetMaximumSize
+
+
+ Material parameter
-
-
-
-
-
+
+ -
+
+
+ QLayout::SetMaximumSize
+
+
-
+
+
+
+
+
+
-
- Add / Remove
+ Add / remove parameter
-
diff --git a/src/Mod/Mesh/App/Core/Algorithm.cpp b/src/Mod/Mesh/App/Core/Algorithm.cpp
index 1eb86d511e..6b55b84da4 100644
--- a/src/Mod/Mesh/App/Core/Algorithm.cpp
+++ b/src/Mod/Mesh/App/Core/Algorithm.cpp
@@ -1746,6 +1746,24 @@ std::set MeshRefPointToFacets::NeighbourPoints(const std::vector<
return nb;
}
+std::set MeshRefPointToFacets::NeighbourPoints(unsigned long pos) const
+{
+ std::set p;
+ const std::set& vf = _map[pos];
+ for (std::set::const_iterator it = vf.begin(); it != vf.end(); ++it) {
+ unsigned long p1, p2, p3;
+ _rclMesh.GetFacetPoints(*it, p1, p2, p3);
+ if (p1 != pos)
+ p.insert(p1);
+ if (p2 != pos)
+ p.insert(p2);
+ if (p3 != pos)
+ p.insert(p3);
+ }
+
+ return p;
+}
+
void MeshRefPointToFacets::Neighbours (unsigned long ulFacetInd, float fMaxDist, MeshCollector& collect) const
{
std::set visited;
diff --git a/src/Mod/Mesh/App/Core/Algorithm.h b/src/Mod/Mesh/App/Core/Algorithm.h
index 8ea4861a5a..5ac8bb2d92 100644
--- a/src/Mod/Mesh/App/Core/Algorithm.h
+++ b/src/Mod/Mesh/App/Core/Algorithm.h
@@ -375,6 +375,7 @@ public:
const std::set& operator[] (unsigned long) const;
MeshFacetArray::_TConstIterator GetFacet (unsigned long) const;
std::set NeighbourPoints(const std::vector& , int level) const;
+ std::set NeighbourPoints(unsigned long) const;
void Neighbours (unsigned long ulFacetInd, float fMaxDist, MeshCollector& collect) const;
Base::Vector3f GetNormal(unsigned long) const;
void AddNeighbour(unsigned long, unsigned long);
diff --git a/src/Mod/Mesh/App/Core/Degeneration.cpp b/src/Mod/Mesh/App/Core/Degeneration.cpp
index 297b5f41b5..91ec5189ac 100644
--- a/src/Mod/Mesh/App/Core/Degeneration.cpp
+++ b/src/Mod/Mesh/App/Core/Degeneration.cpp
@@ -576,6 +576,13 @@ bool MeshRemoveNeedles::Fixup()
vf.erase(neighbour);
ce._changeFacets.insert(ce._changeFacets.begin(), vf.begin(), vf.end());
+ // get adjacent points
+ std::set vv;
+ vv = vf_it.NeighbourPoints(ce._fromPoint);
+ ce._adjacentFrom.insert(ce._adjacentFrom.begin(), vv.begin(),vv.end());
+ vv = vf_it.NeighbourPoints(ce._toPoint);
+ ce._adjacentTo.insert(ce._adjacentTo.begin(), vv.begin(),vv.end());
+
if (topAlg.IsCollapseEdgeLegal(ce)) {
topAlg.CollapseEdge(ce);
for (auto it : ce._removeFacets) {
diff --git a/src/Mod/Mesh/App/Core/Elements.h b/src/Mod/Mesh/App/Core/Elements.h
index ead0169d97..951d63ce30 100644
--- a/src/Mod/Mesh/App/Core/Elements.h
+++ b/src/Mod/Mesh/App/Core/Elements.h
@@ -75,6 +75,8 @@ struct MeshExport EdgeCollapse
{
unsigned long _fromPoint;
unsigned long _toPoint;
+ std::vector _adjacentFrom; // adjacent points to _fromPoint
+ std::vector _adjacentTo; // adjacent points to _toPoint
std::vector _removeFacets;
std::vector _changeFacets;
};
diff --git a/src/Mod/Mesh/App/Core/TopoAlgorithm.cpp b/src/Mod/Mesh/App/Core/TopoAlgorithm.cpp
index 6ad518dd84..491f3d0a94 100644
--- a/src/Mod/Mesh/App/Core/TopoAlgorithm.cpp
+++ b/src/Mod/Mesh/App/Core/TopoAlgorithm.cpp
@@ -955,6 +955,18 @@ bool MeshTopoAlgorithm::CollapseEdge(unsigned long ulFacetPos, unsigned long ulN
bool MeshTopoAlgorithm::IsCollapseEdgeLegal(const EdgeCollapse& ec) const
{
+ // http://stackoverflow.com/a/27049418/148668
+ // Check connectivity
+ //
+ std::vector commonPoints;
+ std::set_intersection(ec._adjacentFrom.begin(), ec._adjacentFrom.end(),
+ ec._adjacentTo.begin(), ec._adjacentTo.end(),
+ std::back_insert_iterator >(commonPoints));
+ if (commonPoints.size() > 2) {
+ return false;
+ }
+
+ // Check geometry
std::vector::const_iterator it;
for (it = ec._changeFacets.begin(); it != ec._changeFacets.end(); ++it) {
MeshFacet f = _rclMesh._aclFacetArray[*it];
diff --git a/src/Mod/Sketcher/App/ConstraintPy.xml b/src/Mod/Sketcher/App/ConstraintPy.xml
index 4c264a2635..5ef77d5aaa 100644
--- a/src/Mod/Sketcher/App/ConstraintPy.xml
+++ b/src/Mod/Sketcher/App/ConstraintPy.xml
@@ -1,13 +1,13 @@
-
@@ -28,7 +28,7 @@
-
+
Position of first geometry index the Constraint refers to
@@ -40,7 +40,7 @@
-
+
Position of second geometry index the Constraint refers to
@@ -52,7 +52,7 @@
-
+
Position of third geometry index the Constraint refers to
diff --git a/src/Mod/Sketcher/App/ConstraintPyImp.cpp b/src/Mod/Sketcher/App/ConstraintPyImp.cpp
index 99c85f96d8..a309e9c66e 100644
--- a/src/Mod/Sketcher/App/ConstraintPyImp.cpp
+++ b/src/Mod/Sketcher/App/ConstraintPyImp.cpp
@@ -31,7 +31,7 @@ using namespace Sketcher;
PyObject *ConstraintPy::PyMake(struct _typeobject *, PyObject *, PyObject *) // Python wrapper
{
- // create a new instance of ConstraintPy and the Twin object
+ // create a new instance of ConstraintPy and the Twin object
return new ConstraintPy(new Constraint);
}
@@ -108,18 +108,18 @@ int ConstraintPy::PyInit(PyObject* args, PyObject* /*kwd*/)
}
else if (strstr(ConstraintType,"InternalAlignment") != NULL) {
this->getConstraintPtr()->Type = InternalAlignment;
-
+
valid = true;
if(strstr(ConstraintType,"EllipseMajorDiameter") != NULL)
this->getConstraintPtr()->AlignmentType=EllipseMajorDiameter;
else if(strstr(ConstraintType,"EllipseMinorDiameter") != NULL)
- this->getConstraintPtr()->AlignmentType=EllipseMinorDiameter;
+ this->getConstraintPtr()->AlignmentType=EllipseMinorDiameter;
else {
this->getConstraintPtr()->AlignmentType=Undef;
valid = false;
}
}
-
+
if (valid) {
this->getConstraintPtr()->First = FirstIndex;
this->getConstraintPtr()->Second = SecondIndex;
@@ -200,11 +200,11 @@ int ConstraintPy::PyInit(PyObject* args, PyObject* /*kwd*/)
}
else if (strstr(ConstraintType,"InternalAlignment") != NULL) {
this->getConstraintPtr()->Type = InternalAlignment;
-
+
valid = true;
-
+
if(strstr(ConstraintType,"EllipseFocus1") != NULL)
- this->getConstraintPtr()->AlignmentType=EllipseFocus1;
+ this->getConstraintPtr()->AlignmentType=EllipseFocus1;
else if(strstr(ConstraintType,"EllipseFocus2") != NULL)
this->getConstraintPtr()->AlignmentType=EllipseFocus2;
else {
@@ -324,13 +324,13 @@ int ConstraintPy::PyInit(PyObject* args, PyObject* /*kwd*/)
valid = true;
if(strstr(ConstraintType,"BSplineControlPoint") != NULL) {
- this->getConstraintPtr()->AlignmentType=BSplineControlPoint;
+ this->getConstraintPtr()->AlignmentType=BSplineControlPoint;
}
else {
this->getConstraintPtr()->AlignmentType=Undef;
valid = false;
}
-
+
if (valid) {
this->getConstraintPtr()->First = intArg1;
this->getConstraintPtr()->FirstPos = (Sketcher::PointPos) intArg2;
@@ -338,7 +338,7 @@ int ConstraintPy::PyInit(PyObject* args, PyObject* /*kwd*/)
this->getConstraintPtr()->InternalAlignmentIndex = intArg4;
return 0;
}
-
+
}
if (valid) {
this->getConstraintPtr()->First = intArg1;
@@ -521,7 +521,7 @@ std::string ConstraintPy::representation(void) const
break;
case Symmetric : result << "'Symmetric'>"; break;
case SnellsLaw : result << "'SnellsLaw'>"; break;
- case InternalAlignment :
+ case InternalAlignment :
switch(this->getConstraintPtr()->AlignmentType) {
case Undef : result << "'InternalAlignment:Undef'>";break;
case EllipseMajorDiameter : result << "'InternalAlignment:EllipseMajorDiameter'>";break;
@@ -583,6 +583,25 @@ Py::Long ConstraintPy::getFirstPos(void) const
return Py::Long(static_cast(this->getConstraintPtr()->FirstPos));
}
+void ConstraintPy::setFirstPos(Py::Long arg)
+{
+ #if PY_MAJOR_VERSION < 3
+ int pos = Py::Int(arg);
+ #else
+ int pos = arg;
+ #endif
+
+ if(pos>=Sketcher::none && pos<=Sketcher::mid) {
+ this->getConstraintPtr()->FirstPos = (Sketcher::PointPos)pos;
+ }
+ else {
+ std::stringstream str;
+ str << "Invalid PointPos parameter: " << arg << std::endl;
+
+ PyErr_SetString(PyExc_TypeError, str.str().c_str());
+ }
+}
+
Py::Long ConstraintPy::getSecond(void) const
{
return Py::Long(this->getConstraintPtr()->Second);
@@ -602,6 +621,25 @@ Py::Long ConstraintPy::getSecondPos(void) const
return Py::Long(static_cast(this->getConstraintPtr()->SecondPos));
}
+void ConstraintPy::setSecondPos(Py::Long arg)
+{
+ #if PY_MAJOR_VERSION < 3
+ int pos = Py::Int(arg);
+ #else
+ int pos = arg;
+ #endif
+
+ if(pos>=Sketcher::none && pos<=Sketcher::mid) {
+ this->getConstraintPtr()->SecondPos = (Sketcher::PointPos)pos;
+ }
+ else {
+ std::stringstream str;
+ str << "Invalid PointPos parameter: " << arg << std::endl;
+
+ PyErr_SetString(PyExc_TypeError, str.str().c_str());
+ }
+}
+
Py::Long ConstraintPy::getThird(void) const
{
return Py::Long(this->getConstraintPtr()->Third);
@@ -621,6 +659,25 @@ Py::Long ConstraintPy::getThirdPos(void) const
return Py::Long(static_cast(this->getConstraintPtr()->ThirdPos));
}
+void ConstraintPy::setThirdPos(Py::Long arg)
+{
+ #if PY_MAJOR_VERSION < 3
+ int pos = Py::Int(arg);
+ #else
+ int pos = arg;
+ #endif
+
+ if(pos>=Sketcher::none && pos<=Sketcher::mid) {
+ this->getConstraintPtr()->ThirdPos = (Sketcher::PointPos)pos;
+ }
+ else {
+ std::stringstream str;
+ str << "Invalid PointPos parameter: " << arg << std::endl;
+
+ PyErr_SetString(PyExc_TypeError, str.str().c_str());
+ }
+}
+
Py::String ConstraintPy::getName(void) const
{
return Py::String(this->getConstraintPtr()->Name);
@@ -653,5 +710,5 @@ PyObject *ConstraintPy::getCustomAttributes(const char* /*attr*/) const
int ConstraintPy::setCustomAttributes(const char* /*attr*/, PyObject* /*obj*/)
{
- return 0;
+ return 0;
}
diff --git a/src/Mod/Sketcher/App/Sketch.cpp b/src/Mod/Sketcher/App/Sketch.cpp
index 6c34c07e2f..aaff3c7087 100644
--- a/src/Mod/Sketcher/App/Sketch.cpp
+++ b/src/Mod/Sketcher/App/Sketch.cpp
@@ -1488,10 +1488,16 @@ int Sketch::addConstraint(const Constraint *constraint)
int Sketch::addConstraints(const std::vector &ConstraintList)
{
int rtn = -1;
+ int cid = 0;
- for (std::vector::const_iterator it = ConstraintList.begin();it!=ConstraintList.end();++it)
+ for (std::vector::const_iterator it = ConstraintList.begin();it!=ConstraintList.end();++it,++cid) {
rtn = addConstraint (*it);
+ if(rtn == -1) {
+ Base::Console().Error("Sketcher constraint number %d is malformed!\n",cid);
+ }
+ }
+
return rtn;
}
@@ -1504,6 +1510,10 @@ int Sketch::addConstraints(const std::vector &ConstraintList,
for (std::vector::const_iterator it = ConstraintList.begin();it!=ConstraintList.end();++it,++cid) {
if (!unenforceableConstraints[cid] && (*it)->Type != Block) {
rtn = addConstraint (*it);
+
+ if(rtn == -1) {
+ Base::Console().Error("Sketcher constraint number %d is malformed!\n",cid);
+ }
}
else {
++ConstraintsCounter; // For correct solver redundant reporting
diff --git a/src/Mod/Sketcher/App/planegcs/GCS.cpp b/src/Mod/Sketcher/App/planegcs/GCS.cpp
index f6ad9f0c86..4836d0fb7e 100644
--- a/src/Mod/Sketcher/App/planegcs/GCS.cpp
+++ b/src/Mod/Sketcher/App/planegcs/GCS.cpp
@@ -37,7 +37,7 @@
#ifdef _GCS_DEBUG
#define EIGEN_DEFAULT_IO_FORMAT Eigen::IOFormat(3,0,",",",\n","[","]","[","]")
/* Parameters:
- *
+ *
* StreamPrecision,
* int _flags = 0,
* const std::string & _coeffSeparator = " ",
@@ -108,184 +108,279 @@
typedef Eigen::FullPivHouseholderQR::IntDiagSizeVectorType MatrixIndexType;
-#ifdef _GCS_DEBUG
-void LogMatrix(std::string str, Eigen::MatrixXd matrix )
-{
-#ifdef _DEBUG_TO_FILE
- std::ofstream stream;
- stream.open("GCS_debug.txt", std::ofstream::out | std::ofstream::app);
-#else
- // Debug code starts
- std::stringstream stream;
-#endif
-
- stream << '\n' << " " << str << " =" << '\n';
- stream << "[" << '\n';
- stream << matrix << '\n' ;
- stream << "]" << '\n';
-
-#ifdef _DEBUG_TO_FILE
- stream.flush();
- stream.close();
-#else
- const std::string tmp = stream.str();
-
- Base::Console().Log(tmp.c_str());
-#endif
-}
-
-void LogMatrix(std::string str, MatrixIndexType matrix )
-{
- #ifdef _DEBUG_TO_FILE
- std::ofstream stream;
- stream.open("GCS_debug.txt", std::ofstream::out | std::ofstream::app);
- #else
- // Debug code starts
- std::stringstream stream;
- #endif
-
- stream << '\n' << " " << str << " =" << '\n';
- stream << "[" << '\n';
- stream << matrix << '\n' ;
- stream << "]" << '\n';
-
- #ifdef _DEBUG_TO_FILE
- stream.flush();
- stream.close();
- #else
- const std::string tmp = stream.str();
-
- Base::Console().Log(tmp.c_str());
- #endif
-}
-#endif
-
-void LogString(std::string str)
-{
- #ifdef _DEBUG_TO_FILE
- std::ofstream stream;
- stream.open("GCS_debug.txt", std::ofstream::out | std::ofstream::app);
- #else
- // Debug code starts
- std::stringstream stream;
- #endif
-
- stream << str << std::endl;
-
- #ifdef _DEBUG_TO_FILE
- stream.flush();
- stream.close();
- #else
- const std::string tmp = stream.str();
-
- Base::Console().Log(tmp.c_str());
- #endif
-}
-
#ifndef EIGEN_STOCK_FULLPIVLU_COMPUTE
namespace Eigen {
-typedef Matrix MatrixdType;
+ typedef Matrix MatrixdType;
-template<>
-FullPivLU& FullPivLU::compute(const MatrixdType& matrix)
-{
- m_isInitialized = true;
- m_lu = matrix;
-
- const Index size = matrix.diagonalSize();
- const Index rows = matrix.rows();
- const Index cols = matrix.cols();
-
- // will store the transpositions, before we accumulate them at the end.
- // can't accumulate on-the-fly because that will be done in reverse order for the rows.
- m_rowsTranspositions.resize(matrix.rows());
- m_colsTranspositions.resize(matrix.cols());
- Index number_of_transpositions = 0; // number of NONTRIVIAL transpositions, i.e. m_rowsTranspositions[i]!=i
-
- m_nonzero_pivots = size; // the generic case is that in which all pivots are nonzero (invertible case)
- m_maxpivot = RealScalar(0);
- RealScalar cutoff(0);
-
- for(Index k = 0; k < size; ++k)
- {
- // First, we need to find the pivot.
-
- // biggest coefficient in the remaining bottom-right corner (starting at row k, col k)
- Index row_of_biggest_in_corner, col_of_biggest_in_corner;
- RealScalar biggest_in_corner;
- biggest_in_corner = m_lu.bottomRightCorner(rows-k, cols-k)
- .cwiseAbs()
- .maxCoeff(&row_of_biggest_in_corner, &col_of_biggest_in_corner);
- row_of_biggest_in_corner += k; // correct the values! since they were computed in the corner,
- col_of_biggest_in_corner += k; // need to add k to them.
-
- // when k==0, biggest_in_corner is the biggest coeff absolute value in the original matrix
- if(k == 0) cutoff = biggest_in_corner * NumTraits::epsilon();
-
- // if the pivot (hence the corner) is "zero", terminate to avoid generating nan/inf values.
- // Notice that using an exact comparison (biggest_in_corner==0) here, as Golub-van Loan do in
- // their pseudo-code, results in numerical instability! The cutoff here has been validated
- // by running the unit test 'lu' with many repetitions.
- if(biggest_in_corner < cutoff)
+ template<>
+ FullPivLU& FullPivLU::compute(const MatrixdType& matrix)
{
- // before exiting, make sure to initialize the still uninitialized transpositions
- // in a sane state without destroying what we already have.
- m_nonzero_pivots = k;
- for(Index i = k; i < size; ++i)
- {
- m_rowsTranspositions.coeffRef(i) = i;
- m_colsTranspositions.coeffRef(i) = i;
- }
- break;
+ m_isInitialized = true;
+ m_lu = matrix;
+
+ const Index size = matrix.diagonalSize();
+ const Index rows = matrix.rows();
+ const Index cols = matrix.cols();
+
+ // will store the transpositions, before we accumulate them at the end.
+ // can't accumulate on-the-fly because that will be done in reverse order for the rows.
+ m_rowsTranspositions.resize(matrix.rows());
+ m_colsTranspositions.resize(matrix.cols());
+ Index number_of_transpositions = 0; // number of NONTRIVIAL transpositions, i.e. m_rowsTranspositions[i]!=i
+
+ m_nonzero_pivots = size; // the generic case is that in which all pivots are nonzero (invertible case)
+ m_maxpivot = RealScalar(0);
+ RealScalar cutoff(0);
+
+ for(Index k = 0; k < size; ++k)
+ {
+ // First, we need to find the pivot.
+
+ // biggest coefficient in the remaining bottom-right corner (starting at row k, col k)
+ Index row_of_biggest_in_corner, col_of_biggest_in_corner;
+ RealScalar biggest_in_corner;
+ biggest_in_corner = m_lu.bottomRightCorner(rows-k, cols-k)
+ .cwiseAbs()
+ .maxCoeff(&row_of_biggest_in_corner, &col_of_biggest_in_corner);
+ row_of_biggest_in_corner += k; // correct the values! since they were computed in the corner,
+ col_of_biggest_in_corner += k; // need to add k to them.
+
+ // when k==0, biggest_in_corner is the biggest coeff absolute value in the original matrix
+ if(k == 0) cutoff = biggest_in_corner * NumTraits::epsilon();
+
+ // if the pivot (hence the corner) is "zero", terminate to avoid generating nan/inf values.
+ // Notice that using an exact comparison (biggest_in_corner==0) here, as Golub-van Loan do in
+ // their pseudo-code, results in numerical instability! The cutoff here has been validated
+ // by running the unit test 'lu' with many repetitions.
+ if(biggest_in_corner < cutoff)
+ {
+ // before exiting, make sure to initialize the still uninitialized transpositions
+ // in a sane state without destroying what we already have.
+ m_nonzero_pivots = k;
+ for(Index i = k; i < size; ++i)
+ {
+ m_rowsTranspositions.coeffRef(i) = i;
+ m_colsTranspositions.coeffRef(i) = i;
+ }
+ break;
+ }
+
+ if(biggest_in_corner > m_maxpivot) m_maxpivot = biggest_in_corner;
+
+ // Now that we've found the pivot, we need to apply the row/col swaps to
+ // bring it to the location (k,k).
+
+ m_rowsTranspositions.coeffRef(k) = row_of_biggest_in_corner;
+ m_colsTranspositions.coeffRef(k) = col_of_biggest_in_corner;
+ if(k != row_of_biggest_in_corner) {
+ m_lu.row(k).swap(m_lu.row(row_of_biggest_in_corner));
+ ++number_of_transpositions;
+ }
+ if(k != col_of_biggest_in_corner) {
+ m_lu.col(k).swap(m_lu.col(col_of_biggest_in_corner));
+ ++number_of_transpositions;
+ }
+
+ // Now that the pivot is at the right location, we update the remaining
+ // bottom-right corner by Gaussian elimination.
+
+ if(k= 0; --k)
+ m_p.applyTranspositionOnTheRight(k, m_rowsTranspositions.coeff(k));
+
+ m_q.setIdentity(cols);
+ for(Index k = 0; k < size; ++k)
+ m_q.applyTranspositionOnTheRight(k, m_colsTranspositions.coeff(k));
+
+ m_det_pq = (number_of_transpositions%2) ? -1 : 1;
+ return *this;
}
- if(biggest_in_corner > m_maxpivot) m_maxpivot = biggest_in_corner;
-
- // Now that we've found the pivot, we need to apply the row/col swaps to
- // bring it to the location (k,k).
-
- m_rowsTranspositions.coeffRef(k) = row_of_biggest_in_corner;
- m_colsTranspositions.coeffRef(k) = col_of_biggest_in_corner;
- if(k != row_of_biggest_in_corner) {
- m_lu.row(k).swap(m_lu.row(row_of_biggest_in_corner));
- ++number_of_transpositions;
- }
- if(k != col_of_biggest_in_corner) {
- m_lu.col(k).swap(m_lu.col(col_of_biggest_in_corner));
- ++number_of_transpositions;
- }
-
- // Now that the pivot is at the right location, we update the remaining
- // bottom-right corner by Gaussian elimination.
-
- if(k= 0; --k)
- m_p.applyTranspositionOnTheRight(k, m_rowsTranspositions.coeff(k));
-
- m_q.setIdentity(cols);
- for(Index k = 0; k < size; ++k)
- m_q.applyTranspositionOnTheRight(k, m_colsTranspositions.coeff(k));
-
- m_det_pq = (number_of_transpositions%2) ? -1 : 1;
- return *this;
-}
-
} // Eigen
#endif
namespace GCS
{
+class SolverReportingManager
+{
+public:
+ SolverReportingManager(SolverReportingManager const&) = delete;
+ SolverReportingManager(SolverReportingManager&&) = delete;
+ SolverReportingManager& operator=(SolverReportingManager const&) = delete;
+ SolverReportingManager& operator=(SolverReportingManager &&) = delete;
+
+ static SolverReportingManager& Manager();
+
+ inline void LogString(const std::string& str);
+
+ inline void LogToConsole(const std::string& str);
+
+ inline void LogToFile(const std::string& str);
+
+ void LogQRSystemInformation(const System &system, int paramsNum = 0, int constrNum = 0, int rank = 0);
+
+ void LogMatrix(std::string str, Eigen::MatrixXd matrix);
+ void LogMatrix(std::string str, MatrixIndexType matrix );
+
+private:
+ SolverReportingManager();
+ ~SolverReportingManager();
+
+ inline void initStream();
+ inline void flushStream();
+
+private:
+ #ifdef _DEBUG_TO_FILE
+ std::ofstream stream;
+ #endif
+};
+
+SolverReportingManager::SolverReportingManager()
+{
+ initStream();
+}
+
+SolverReportingManager::~SolverReportingManager()
+{
+ #ifdef _DEBUG_TO_FILE
+ stream.flush();
+ stream.close();
+ #endif
+}
+
+void SolverReportingManager::initStream()
+{
+ #ifdef _DEBUG_TO_FILE
+ if(!stream.is_open()) {
+ stream.open("GCS_debug.txt", std::ofstream::out | std::ofstream::app);
+ }
+ #endif
+}
+
+void SolverReportingManager::flushStream()
+{
+ // Akwardly in some systems flushing does not force the write to the file, requiring a close
+ #ifdef _DEBUG_TO_FILE
+ stream.flush();
+ stream.close();
+ #endif
+}
+
+SolverReportingManager& SolverReportingManager::Manager()
+{
+ static SolverReportingManager theInstance;
+
+ return theInstance;
+}
+
+void SolverReportingManager::LogToConsole(const std::string& str)
+{
+ if(str.size() < Base::Console().BufferSize)
+ Base::Console().Log(str.c_str());
+ else
+ Base::Console().Log("SolverReportingManager - Too long string suppressed");
+}
+
+void SolverReportingManager::LogToFile(const std::string& str)
+{
+ #ifdef _DEBUG_TO_FILE
+ initStream();
+
+ stream << str << std::endl;
+
+ flushStream();
+ #else
+ (void)(str); // silence unused parameter
+ LogToConsole("Debugging to file not enabled!");
+ #endif
+}
+
+void SolverReportingManager::LogString(const std::string& str)
+{
+ LogToConsole(str);
+
+ #ifdef _DEBUG_TO_FILE
+ LogToFile(str);
+ #endif
+}
+
+void SolverReportingManager::LogQRSystemInformation(const System &system, int paramsNum, int constrNum, int rank)
+{
+
+ std::stringstream tempstream;
+
+ tempstream << (system.qrAlgorithm==EigenSparseQR?"EigenSparseQR":(system.qrAlgorithm==EigenDenseQR?"DenseQR":""));
+
+ if (paramsNum > 0) {
+ tempstream
+ #ifdef EIGEN_SPARSEQR_COMPATIBLE
+ << ", Threads: " << Eigen::nbThreads()
+ #endif
+ #ifdef EIGEN_VECTORIZE
+ << ", Vectorization: On"
+ #endif
+ << ", Pivot Threshold: " << system.qrpivotThreshold
+ << ", Params: " << paramsNum
+ << ", Constr: " << constrNum
+ << ", Rank: " << rank
+ << std::endl;
+ }
+ else {
+ tempstream
+ #ifdef EIGEN_SPARSEQR_COMPATIBLE
+ << ", Threads: " << Eigen::nbThreads()
+ #endif
+ #ifdef EIGEN_VECTORIZE
+ << ", Vectorization: On"
+ #endif
+ << ", Empty Sketch, nothing to solve"
+ << std::endl;
+ }
+
+ LogString(tempstream.str());
+
+}
+
+
+#ifdef _GCS_DEBUG
+void SolverReportingManager::LogMatrix(std::string str, Eigen::MatrixXd matrix )
+{
+ std::stringstream tempstream;
+
+ tempstream << '\n' << " " << str << " =" << '\n';
+ tempstream << "[" << '\n';
+ tempstream << matrix << '\n' ;
+ tempstream << "]" << '\n';
+
+ LogString(tempstream.str());
+
+}
+
+void SolverReportingManager::LogMatrix(std::string str, MatrixIndexType matrix )
+{
+ std::stringstream tempstream;
+
+ stream << '\n' << " " << str << " =" << '\n';
+ stream << "[" << '\n';
+ stream << matrix << '\n' ;
+ stream << "]" << '\n';
+
+ LogString(tempstream.str());
+}
+#endif
+
+
typedef boost::adjacency_list Graph;
///////////////////////////////////////
@@ -914,7 +1009,7 @@ int System::addConstraintEqualRadii(Ellipse &e1, Ellipse &e2, int tagId, bool dr
int System::addConstraintEqualRadii(ArcOfHyperbola &a1, ArcOfHyperbola &a2, int tagId, bool driving)
{
addConstraintEqual(a1.radmin, a2.radmin, tagId, driving);
-
+
Constraint *constr = new ConstraintEqualMajorAxesConic(&a1,&a2);
constr->setTag(tagId);
constr->setDriving(driving);
@@ -967,7 +1062,7 @@ int System::addConstraintInternalAlignmentPoint2Ellipse(Ellipse &e, Point &p1, I
{
Constraint *constr = new ConstraintInternalAlignmentPoint2Ellipse(e, p1, alignmentType);
constr->setTag(tagId);
- constr->setDriving(driving);
+ constr->setDriving(driving);
return addConstraint(constr);
}
@@ -1125,7 +1220,7 @@ int System::addConstraintInternalAlignmentHyperbolaMinorDiameter(Hyperbola &e, P
double X_F1=*e.focus1.x;
double Y_F1=*e.focus1.y;
double b=*e.radmin;
-
+
// Same idea as for major above, but for minor
// DMC=(P1-PA)*(P1-PA)-(P2-PA)*(P2-PA)
double closertopositiveminor= pow(-X_1 + X_c + b*(Y_F1 - Y_c)/sqrt(pow(X_F1 - X_c, 2) +
@@ -1140,7 +1235,7 @@ int System::addConstraintInternalAlignmentHyperbolaMinorDiameter(Hyperbola &e, P
b*(X_F1 - X_c)/sqrt(pow(X_F1 - X_c, 2) + pow(Y_F1 - Y_c, 2)) + (Y_F1 -
Y_c)*(-pow(b, 2) + pow(X_F1 - X_c, 2) + pow(Y_F1 - Y_c,
2))/sqrt(pow(X_F1 - X_c, 2) + pow(Y_F1 - Y_c, 2)), 2);
-
+
if(closertopositiveminor<0){
addConstraintInternalAlignmentPoint2Hyperbola(e,p2,HyperbolaPositiveMinorX,tagId, driving);
addConstraintInternalAlignmentPoint2Hyperbola(e,p2,HyperbolaPositiveMinorY,tagId, driving);
@@ -3664,6 +3759,45 @@ void System::undoSolution()
resetToReference();
}
+void System::makeReducedJacobian(Eigen::MatrixXd &J,
+ std::map &jacobianconstraintmap,
+ GCS::VEC_pD &pdiagnoselist,
+ std::map< int , int> &tagmultiplicity)
+{
+ // construct specific parameter list for diagonose ignoring driven constraint parameters
+ for (int j=0; j < int(plist.size()); j++) {
+ auto result1 = std::find(std::begin(pdrivenlist), std::end(pdrivenlist), plist[j]);
+
+ if (result1 == std::end(pdrivenlist)) {
+ pdiagnoselist.push_back(plist[j]);
+ }
+ }
+
+
+ J = Eigen::MatrixXd::Zero(clist.size(), pdiagnoselist.size());
+
+ int jacobianconstraintcount=0;
+ int allcount=0;
+ for (std::vector::iterator constr=clist.begin(); constr != clist.end(); ++constr) {
+ (*constr)->revertParams();
+ ++allcount;
+ if ((*constr)->getTag() >= 0 && (*constr)->isDriving()) {
+ jacobianconstraintcount++;
+ for (int j=0; j < int(pdiagnoselist.size()); j++) {
+ J(jacobianconstraintcount-1,j) = (*constr)->grad(pdiagnoselist[j]);
+ }
+
+ // parallel processing: create tag multiplicity map
+ if(tagmultiplicity.find((*constr)->getTag()) == tagmultiplicity.end())
+ tagmultiplicity[(*constr)->getTag()] = 0;
+ else
+ tagmultiplicity[(*constr)->getTag()]++;
+
+ jacobianconstraintmap[jacobianconstraintcount-1] = allcount-1;
+ }
+ }
+}
+
int System::diagnose(Algorithm alg)
{
// Analyses the constrainess grad of the system and provides feedback
@@ -3681,16 +3815,15 @@ int System::diagnose(Algorithm alg)
dofs = -1;
return dofs;
}
-#ifdef _GCS_DEBUG
+
#ifdef _DEBUG_TO_FILE
- std::ofstream stream;
- stream.open("GCS_debug.txt", std::ofstream::out | std::ofstream::app);
- stream << "GCS::System::diagnose()" << std::endl;
- stream.flush();
- stream.close();
-#endif
+SolverReportingManager::Manager().LogToFile("GCS::System::diagnose()\n");
#endif
+ // Input parameters' lists:
+ // plist => list of all the parameters of the system, e.g. each coordinate of a point
+ // pdrivenlist => list of the parameters that are driven by other parameters (e.g. value of driven constraints)
+
// When adding an external geometry or a constraint on an external geometry the array 'plist' is empty.
// So, we must abort here because otherwise we would create an invalid matrix and make the application
// eventually crash. This fixes issues #0002372/#0002373.
@@ -3704,44 +3837,30 @@ int System::diagnose(Algorithm alg)
conflictingTags.clear();
redundantTags.clear();
- // construct specific parameter list for diagonose ignoring driven constraint parameters
- GCS::VEC_pD pdiagnoselist;
- for (int j=0; j < int(plist.size()); j++) {
- auto result1 = std::find(std::begin(pdrivenlist), std::end(pdrivenlist), plist[j]);
+ // This QR diagnosis uses a reduced Jacobian matrix to calculate the rank of the system and identify
+ // conflicting and redundant constraints.
+ //
+ // reduced Jacobian matrix
+ // The Jacobian has been reduced to:
+ // 1. only contain driving constraints, but keep a full size (zero padded).
+ // 2. remove the parameters of the values of driven constraints.
+ Eigen::MatrixXd J;
- if (result1 == std::end(pdrivenlist))
- pdiagnoselist.push_back(plist[j]);
- }
-
- // map tag to a tag multiplicity (the number of solver constraints associated with the same tag)
- std::map< int , int> tagmultiplicity;
-
- Eigen::MatrixXd J = Eigen::MatrixXd::Zero(clist.size(), pdiagnoselist.size());
-
- // The jacobian has been reduced to only contain driving constraints. Identification
- // of constraint indices from this reduced jacobian requires a mapping.
+ // maps the index of the rows of the reduced jacobian matrix (solver constraints) to
+ // the index those constraints would have in a full size Jacobian matrix
std::map jacobianconstraintmap;
-
- int jacobianconstraintcount=0;
- int allcount=0;
- for (std::vector::iterator constr=clist.begin(); constr != clist.end(); ++constr) {
- (*constr)->revertParams();
- ++allcount;
- if ((*constr)->getTag() >= 0 && (*constr)->isDriving()) {
- jacobianconstraintcount++;
- for (int j=0; j < int(pdiagnoselist.size()); j++) {
- J(jacobianconstraintcount-1,j) = (*constr)->grad(pdiagnoselist[j]);
- }
-
- // parallel processing: create tag multiplicity map
- if(tagmultiplicity.find((*constr)->getTag()) == tagmultiplicity.end())
- tagmultiplicity[(*constr)->getTag()] = 0;
- else
- tagmultiplicity[(*constr)->getTag()]++;
-
- jacobianconstraintmap[jacobianconstraintcount-1] = allcount-1;
- }
- }
+
+ // list of parameters to be diagnosed in this routine (removes value parameters from driven constraints)
+ GCS::VEC_pD pdiagnoselist;
+
+ // tag multiplicity gives the number of solver constraints associated with the same tag
+ // A tag generally corresponds to the Sketcher constraint index - There are special tag values, like 0 and -1.
+ std::map< int , int> tagmultiplicity;
+
+
+ makeReducedJacobian(J, jacobianconstraintmap, pdiagnoselist, tagmultiplicity);
+
+ // QR decomposition method selection: SparseQR vs DenseQR
#ifdef EIGEN_SPARSEQR_COMPATIBLE
Eigen::SparseMatrix SJ;
@@ -3762,8 +3881,10 @@ int System::diagnose(Algorithm alg)
}
#endif
+
+
#ifdef _GCS_DEBUG
- LogMatrix("J",J);
+ SolverReportingManager::Manager().LogMatrix("J",J);
#endif
Eigen::MatrixXd R;
@@ -3773,6 +3894,7 @@ int System::diagnose(Algorithm alg)
Eigen::MatrixXd R2; // Intended for a trapezoidal matrix, where R is the top triangular matrix of the R2 trapezoidal matrix
#endif
+
int paramsNum = 0;
int constrNum = 0;
int rank = 0;
@@ -3780,7 +3902,7 @@ int System::diagnose(Algorithm alg)
if(qrAlgorithm==EigenDenseQR){
if (J.rows() > 0) {
- qrJT.compute(J.topRows(jacobianconstraintcount).transpose());
+ qrJT.compute(J.topRows(jacobianconstraintmap.size()).transpose());
//Eigen::MatrixXd Q = qrJT.matrixQ ();
paramsNum = qrJT.rows();
@@ -3803,7 +3925,7 @@ int System::diagnose(Algorithm alg)
#ifdef EIGEN_SPARSEQR_COMPATIBLE
else if(qrAlgorithm==EigenSparseQR){
if (SJ.rows() > 0) {
- auto SJT = SJ.topRows(jacobianconstraintcount).transpose();
+ auto SJT = SJ.topRows(jacobianconstraintmap.size()).transpose();
if (SJT.rows() > 0 && SJT.cols() > 0) {
SqrJT.compute(SJT);
// Do not ask for Q Matrix!!
@@ -3838,53 +3960,22 @@ int System::diagnose(Algorithm alg)
#endif
if(debugMode==IterationLevel) {
- std::stringstream stream;
-
- stream << (qrAlgorithm==EigenSparseQR?"EigenSparseQR":(qrAlgorithm==EigenDenseQR?"DenseQR":""));
-
- if (J.rows() > 0) {
- stream
-#ifdef EIGEN_SPARSEQR_COMPATIBLE
- << ", Threads: " << Eigen::nbThreads()
-#endif
-#ifdef EIGEN_VECTORIZE
- << ", Vectorization: On"
-#endif
- << ", Pivot Threshold: " << qrpivotThreshold
- << ", Params: " << paramsNum
- << ", Constr: " << constrNum
- << ", Rank: " << rank;
- }
- else {
- stream
-#ifdef EIGEN_SPARSEQR_COMPATIBLE
- << ", Threads: " << Eigen::nbThreads()
-#endif
-#ifdef EIGEN_VECTORIZE
- << ", Vectorization: On"
-#endif
- << ", Empty Sketch, nothing to solve";
- }
-
- const std::string tmp = stream.str();
-
- LogString(tmp);
-
+ SolverReportingManager::Manager().LogQRSystemInformation(*this, paramsNum, constrNum, rank);
}
if (J.rows() > 0) {
#ifdef _GCS_DEBUG_SOLVER_JACOBIAN_QR_DECOMPOSITION_TRIANGULAR_MATRIX
- LogMatrix("R", R);
+ SolverReportingManager::Manager().LogMatrix("R", R);
- LogMatrix("R2", R2);
+ SolverReportingManager::Manager().LogMatrix("R2", R2);
if(qrAlgorithm == EigenDenseQR){ // There is no rowsTranspositions in SparseQR. obtaining Q is buggy in Eigen for SparseQR
- LogMatrix("Q", Q);
- LogMatrix("RowTransp", qrJT.rowsTranspositions());
+ SolverReportingManager::Manager().LogMatrix("Q", Q);
+ SolverReportingManager::Manager().LogMatrix("RowTransp", qrJT.rowsTranspositions());
}
#ifdef SPARSE_Q_MATRIX
else if(qrAlgorithm == EigenSparseQR) {
- LogMatrix("Q", Q);
+ SolverReportingManager::Manager().LogMatrix("Q", Q);
}
#endif
#endif
@@ -3912,7 +4003,7 @@ int System::diagnose(Algorithm alg)
rowPermutations.applyTranspositionOnTheRight(k, rowTranspositions.coeff(k));
}
#ifdef EIGEN_SPARSEQR_COMPATIBLE
- else if(qrAlgorithm==EigenSparseQR){
+ else if(qrAlgorithm==EigenSparseQR){
// J.P = Q.R, see https://eigen.tuxfamily.org/dox/classEigen_1_1SparseQR.html
// There is no rowsTransposition in this QR decomposition.
// TODO: This detection method won't work for SparseQR
@@ -3944,7 +4035,7 @@ int System::diagnose(Algorithm alg)
#ifdef _GCS_DEBUG_SOLVER_JACOBIAN_QR_DECOMPOSITION_TRIANGULAR_MATRIX
std::string tmp = stream.str();
- LogString(tmp);
+ SolverReportingManager::Manager().LogString(tmp);
#endif
// If not independent, must be dependent
@@ -3992,7 +4083,7 @@ int System::diagnose(Algorithm alg)
stream << "]" << std::endl;
tmp = stream.str();
- LogString(tmp);
+ SolverReportingManager::Manager().LogString(tmp);
#endif
for( auto param : depParamCols) {
pdependentparameters.push_back(pdiagnoselist[param]);
@@ -4024,7 +4115,7 @@ int System::diagnose(Algorithm alg)
origCol=SqrJT.colsPermutation().indices()[row];
#endif
//conflictGroups[j-rank].push_back(clist[origCol]);
- conflictGroups[j-rank].push_back(clist[jacobianconstraintmap[origCol]]);
+ conflictGroups[j-rank].push_back(clist[jacobianconstraintmap.at(origCol)]);
}
}
int origCol = 0;
@@ -4037,9 +4128,9 @@ int System::diagnose(Algorithm alg)
origCol=SqrJT.colsPermutation().indices()[j];
#endif
//conflictGroups[j-rank].push_back(clist[origCol]);
- conflictGroups[j-rank].push_back(clist[jacobianconstraintmap[origCol]]);
+ conflictGroups[j-rank].push_back(clist[jacobianconstraintmap.at(origCol)]);
}
-
+
#ifdef _GCS_DEBUG_SOLVER_JACOBIAN_QR_DECOMPOSITION_TRIANGULAR_MATRIX
stream.flush();
@@ -4047,16 +4138,16 @@ int System::diagnose(Algorithm alg)
stream << "ConflictGroups: [";
for(auto group :conflictGroups) {
stream << "[";
-
+
for(auto c :group)
stream << c->getTag();
-
+
stream << "]";
}
stream << "]" << std::endl;
tmp = stream.str();
- LogString(tmp);
+ SolverReportingManager::Manager().LogString(tmp);
#endif
// try to remove the conflicting constraints and solve the
@@ -4084,12 +4175,12 @@ int System::diagnose(Algorithm alg)
it != conflictingMap.end(); ++it) {
if (static_cast(it->second.size()) > maxPopularity ||
(static_cast(it->second.size()) == maxPopularity && mostPopular &&
- tagmultiplicity[it->first->getTag()] < tagmultiplicity[mostPopular->getTag()]) ||
+ tagmultiplicity.at(it->first->getTag()) < tagmultiplicity.at(mostPopular->getTag())) ||
(static_cast(it->second.size()) == maxPopularity && mostPopular &&
- tagmultiplicity[it->first->getTag()] == tagmultiplicity[mostPopular->getTag()] &&
+ tagmultiplicity.at(it->first->getTag()) == tagmultiplicity.at(mostPopular->getTag()) &&
it->first->getTag() > mostPopular->getTag())
-
+
) {
mostPopular = it->first;
maxPopularity = it->second.size();
diff --git a/src/Mod/Sketcher/App/planegcs/GCS.h b/src/Mod/Sketcher/App/planegcs/GCS.h
index b6d227d683..1e8bc8cb43 100644
--- a/src/Mod/Sketcher/App/planegcs/GCS.h
+++ b/src/Mod/Sketcher/App/planegcs/GCS.h
@@ -51,18 +51,18 @@ namespace GCS
LevenbergMarquardt = 1,
DogLeg = 2
};
-
+
enum DogLegGaussStep {
FullPivLU = 0,
LeastNormFullPivLU = 1,
LeastNormLdlt = 2
};
-
+
enum QRAlgorithm {
EigenDenseQR = 0,
EigenSparseQR = 1
};
-
+
enum DebugMode {
NoDebug = 0,
Minimal = 1,
@@ -77,7 +77,7 @@ namespace GCS
VEC_pD plist; // list of the unknown parameters
VEC_pD pdrivenlist; // list of parameters of driven constraints
MAP_pD_I pIndex;
-
+
VEC_pD pdependentparameters; // list of dependent parameters by the system
std::vector clist;
@@ -107,6 +107,8 @@ namespace GCS
int solve_LM(SubSystem *subsys, bool isRedundantsolving=false);
int solve_DL(SubSystem *subsys, bool isRedundantsolving=false);
+ void makeReducedJacobian(Eigen::MatrixXd &J, std::map &jacobianconstraintmap, GCS::VEC_pD &pdiagnoselist, std::map< int , int> &tagmultiplicity);
+
#ifdef _GCS_EXTRACT_SOLVER_SUBSYSTEM_
void extractSubsystem(SubSystem *subsys, bool isRedundantsolving);
#endif
@@ -122,18 +124,18 @@ namespace GCS
double qrpivotThreshold;
DebugMode debugMode;
double LM_eps;
- double LM_eps1;
+ double LM_eps1;
double LM_tau;
double DL_tolg;
- double DL_tolx;
+ double DL_tolx;
double DL_tolf;
double LM_epsRedundant;
- double LM_eps1Redundant;
+ double LM_eps1Redundant;
double LM_tauRedundant;
double DL_tolgRedundant;
- double DL_tolxRedundant;
- double DL_tolfRedundant;
-
+ double DL_tolxRedundant;
+ double DL_tolfRedundant;
+
public:
System();
/*System(std::vector clist_);*/
@@ -227,7 +229,7 @@ namespace GCS
double* n1, double* n2,
bool flipn1, bool flipn2,
int tagId, bool driving = true);
-
+
// internal alignment constraints
int addConstraintInternalAlignmentPoint2Ellipse(Ellipse &e, Point &p1, InternalAlignmentType alignmentType, int tagId=0, bool driving = true);
int addConstraintInternalAlignmentEllipseMajorDiameter(Ellipse &e, Point &p1, Point &p2, int tagId=0, bool driving = true);
@@ -251,7 +253,7 @@ namespace GCS
// If there's only one, a signed value is returned.
// Effectively, it calculates the error of a UI constraint
double calculateConstraintErrorByTag(int tagId);
-
+
void rescaleConstraint(int id, double coeff);
void declareUnknowns(VEC_pD ¶ms);
diff --git a/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp b/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp
index d6acce8812..8fd6469139 100644
--- a/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp
+++ b/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp
@@ -1749,7 +1749,10 @@ std::set ViewProviderSketch::detectPreselectionConstr(const SoPickedPoint *
b != edit->combinedConstrBoxes[constrIdsStr].end(); ++b) {
#ifdef FC_DEBUG
- Base::Console().Log("Abs(%f,%f),Trans(%f,%f),Coords(%d,%d),iCoords(%f,%f),icon(%d,%d),isize(%d,%d),boundingbox([%d,%d],[%d,%d])\n", absPos[0],absPos[1],trans[0], trans[1], cursorPos[0], cursorPos[1], iconCoords[0], iconCoords[1], iconX, iconY, iconSize[0], iconSize[1], b->first.topLeft().x(),b->first.topLeft().y(),b->first.bottomRight().x(),b->first.bottomRight().y());
+ // Useful code to debug coordinates and bounding boxes that does not need to be compiled in for
+ // any debug operations.
+
+ /*Base::Console().Log("Abs(%f,%f),Trans(%f,%f),Coords(%d,%d),iCoords(%f,%f),icon(%d,%d),isize(%d,%d),boundingbox([%d,%d],[%d,%d])\n", absPos[0],absPos[1],trans[0], trans[1], cursorPos[0], cursorPos[1], iconCoords[0], iconCoords[1], iconX, iconY, iconSize[0], iconSize[1], b->first.topLeft().x(),b->first.topLeft().y(),b->first.bottomRight().x(),b->first.bottomRight().y());*/
#endif
if (b->first.contains(iconX, iconY)) {
diff --git a/src/Mod/TechDraw/App/DrawUtil.cpp b/src/Mod/TechDraw/App/DrawUtil.cpp
index 8f6b6feffb..9bffceb6b8 100644
--- a/src/Mod/TechDraw/App/DrawUtil.cpp
+++ b/src/Mod/TechDraw/App/DrawUtil.cpp
@@ -27,13 +27,14 @@
# include
# include
#include
+#include
# include
# include
# include
# include
# include
#include
-
+#include
#include
#include
@@ -74,22 +75,24 @@ using namespace TechDraw;
/*static*/ int DrawUtil::getIndexFromName(std::string geomName)
{
+// Base::Console().Message("DU::getIndexFromName(%s)\n", geomName.c_str());
boost::regex re("\\d+$"); // one of more digits at end of string
boost::match_results what;
boost::match_flag_type flags = boost::match_default;
- char* endChar;
+// char* endChar;
std::string::const_iterator begin = geomName.begin();
std::string::const_iterator end = geomName.end();
std::stringstream ErrorMsg;
if (!geomName.empty()) {
if (boost::regex_search(begin, end, what, re, flags)) {
- return int (std::strtol(what.str().c_str(), &endChar, 10)); //TODO: use std::stoi() in c++11
+ return int (std::stoi(what.str()));
} else {
ErrorMsg << "getIndexFromName: malformed geometry name - " << geomName;
throw Base::ValueError(ErrorMsg.str());
}
} else {
+ Base::Console().Log("DU::getIndexFromName(%s) - empty geometry name\n",geomName.c_str());
throw Base::ValueError("getIndexFromName - empty geometry name");
}
}
@@ -327,6 +330,15 @@ std::string DrawUtil::formatVector(const gp_Pnt& v)
return result;
}
+std::string DrawUtil::formatVector(const QPointF& v)
+{
+ std::string result;
+ std::stringstream builder;
+ builder << std::fixed << std::setprecision(3) ;
+ builder << " (" << v.x() << "," << v.y() << ") ";
+ result = builder.str();
+ return result;
+}
//! compare 2 vectors for sorting - true if v1 < v2
bool DrawUtil::vectorLess(const Base::Vector3d& v1, const Base::Vector3d& v2)
diff --git a/src/Mod/TechDraw/App/DrawUtil.h b/src/Mod/TechDraw/App/DrawUtil.h
index 485a2c2686..3445dfc7f6 100644
--- a/src/Mod/TechDraw/App/DrawUtil.h
+++ b/src/Mod/TechDraw/App/DrawUtil.h
@@ -27,6 +27,7 @@
#include
#include
+#include
#include
#include
@@ -74,6 +75,8 @@ class TechDrawExport DrawUtil {
static std::string formatVector(const gp_Dir& v);
static std::string formatVector(const gp_Vec& v);
static std::string formatVector(const gp_Pnt& v);
+ static std::string formatVector(const QPointF& v);
+
static bool vectorLess(const Base::Vector3d& v1, const Base::Vector3d& v2);
static Base::Vector3d toR3(const gp_Ax2 fromSystem, const Base::Vector3d fromPoint);
static bool checkParallel(const Base::Vector3d v1, const Base::Vector3d v2, double tolerance = FLT_EPSILON);
diff --git a/src/Mod/TechDraw/App/DrawViewDimension.cpp b/src/Mod/TechDraw/App/DrawViewDimension.cpp
index c3a1eb1b26..13e59fac08 100644
--- a/src/Mod/TechDraw/App/DrawViewDimension.cpp
+++ b/src/Mod/TechDraw/App/DrawViewDimension.cpp
@@ -41,6 +41,7 @@
#include
#include
+#include
#include
#include
#include
@@ -107,7 +108,7 @@ DrawViewDimension::DrawViewDimension(void)
ADD_PROPERTY_TYPE(UnderTolerance ,(0.0),"",App::Prop_None,"- Tolerance value");
//hide the properties the user can't edit in the property editor
- References2D.setStatus(App::Property::Hidden,true);
+// References2D.setStatus(App::Property::Hidden,true);
References3D.setStatus(App::Property::Hidden,true);
//hide the DrawView properties that don't apply to Dimensions
@@ -208,24 +209,26 @@ short DrawViewDimension::mustExecute() const
App::DocumentObjectExecReturn *DrawViewDimension::execute(void)
{
+// Base::Console().Message("DVD::execute() - %s\n", getNameInDocument());
if (!keepUpdated()) {
return App::DocumentObject::StdReturn;
}
- if (!has2DReferences()) { //too soon
- return App::DocumentObject::StdReturn;
- }
-
- if (!getViewPart()->hasGeometry()) { //happens when loading saved document
- Base::Console().Log("INFO - DVD::getDimValue ViewPart has no Geometry yet\n");
+ //any empty Reference2D??
+ if (!has2DReferences()) { //too soon?
return App::DocumentObject::StdReturn;
}
- if (!checkReferences2D()) {
- Base::Console().Log("Error: DVD - %s - 2D references are corrupt\n",getNameInDocument());
+ //can't do anything until Source has geometry
+ if (!getViewPart()->hasGeometry()) { //happens when loading saved document
return App::DocumentObject::StdReturn;
}
-
+
+ //now we can check if Reference2ds have valid targets.
+ if (!checkReferences2D()) {
+ return App::DocumentObject::StdReturn;
+ }
+
const std::vector &subElements = References2D.getSubValues();
if ( Type.isValue("Distance") ||
@@ -433,6 +436,7 @@ App::DocumentObjectExecReturn *DrawViewDimension::execute(void)
std::string DrawViewDimension::getFormatedValue(bool obtuse)
{
+// Base::Console().Message("DVD::getFormatedValue()\n");
std::string result;
if (Arbitrary.getValue()) {
return FormatSpec.getStrValue();
@@ -554,14 +558,13 @@ std::string DrawViewDimension::getFormatedValue(bool obtuse)
//!NOTE: this returns the Dimension value in internal units (ie mm)!!!!
double DrawViewDimension::getDimValue()
{
+// Base::Console().Message("DVD::getDimValue()\n");
double result = 0.0;
if (!has2DReferences()) { //happens during Dimension creation
- Base::Console().Log("INFO - DVD::getDimValue - Dimension has no References\n");
return result;
}
if (!getViewPart()->hasGeometry()) { //happens when loading saved document
- Base::Console().Log("INFO - DVD::getDimValue ViewPart has no Geometry yet\n");
return result;
}
@@ -586,7 +589,7 @@ double DrawViewDimension::getDimValue()
} else {
// Projected Values
if (!checkReferences2D()) {
- Base::Console().Log("Error: DVD - %s - 2D references are corrupt\n",getNameInDocument());
+ Base::Console().Warning("Error: DVD::getDimValue - %s - 2D references are corrupt\n",getNameInDocument());
return result;
}
if ( Type.isValue("Distance") ||
@@ -633,6 +636,7 @@ double DrawViewDimension::getDimValue()
pointPair DrawViewDimension::getPointsOneEdge()
{
+// Base::Console().Message("DVD::getPointsOneEdge() - %s\n",getNameInDocument());
pointPair result;
const std::vector &subElements = References2D.getSubValues();
@@ -643,7 +647,7 @@ pointPair DrawViewDimension::getPointsOneEdge()
if (geom && geom->geomType == TechDrawGeometry::GeomType::GENERIC) {
gen = static_cast(geom);
} else {
- Base::Console().Log("Error: DVD - %s - 2D references are corrupt\n",getNameInDocument());
+ Base::Console().Error("Error: DVD - %s - 2D references are corrupt\n",getNameInDocument());
return result;
}
result.first = DrawUtil::vector23(gen->points[0]);
@@ -653,6 +657,7 @@ pointPair DrawViewDimension::getPointsOneEdge()
pointPair DrawViewDimension::getPointsTwoEdges()
{
+// Base::Console().Message("DVD::getPointsTwoEdges() - %s\n",getNameInDocument());
pointPair result;
const std::vector &subElements = References2D.getSubValues();
@@ -671,6 +676,7 @@ pointPair DrawViewDimension::getPointsTwoEdges()
pointPair DrawViewDimension::getPointsTwoVerts()
{
+// Base::Console().Message("DVD::getPointsTwoVerts() - %s\n",getNameInDocument());
pointPair result;
const std::vector &subElements = References2D.getSubValues();
@@ -690,6 +696,7 @@ pointPair DrawViewDimension::getPointsTwoVerts()
pointPair DrawViewDimension::getPointsEdgeVert()
{
+// Base::Console().Message("DVD::getPointsEdgeVert() - %s\n",getNameInDocument());
pointPair result;
const std::vector &subElements = References2D.getSubValues();
@@ -706,7 +713,7 @@ pointPair DrawViewDimension::getPointsEdgeVert()
}
if ((v == nullptr) ||
(e == nullptr) ) {
- Base::Console().Log("Error: DVD - %s - 2D references are corrupt\n",getNameInDocument());
+ Base::Console().Error("Error: DVD - %s - 2D references are corrupt\n",getNameInDocument());
return result;
}
result = closestPoints(e->occEdge,v->occVertex);
@@ -781,28 +788,42 @@ int DrawViewDimension::getRefType3(const std::string g1,
}
-//! validate 2D references - only checks if they exist, not if they are the right type
+//! validate 2D references - only checks if the target exists
bool DrawViewDimension::checkReferences2D() const
{
+// Base::Console().Message("DVD::checkReFerences2d() - %s\n",getNameInDocument());
bool result = true;
- //const std::vector &objects = References2D.getValues();
- const std::vector &subElements = References2D.getSubValues();
-
- for (auto& s: subElements) {
- int idx = DrawUtil::getIndexFromName(s);
- if (DrawUtil::getGeomTypeFromName(s) == "Edge") {
- TechDrawGeometry::BaseGeom* geom = getViewPart()->getProjEdgeByIndex(idx);
- if (geom == nullptr) {
- result = false;
- break;
- }
- } else if (DrawUtil::getGeomTypeFromName(s) == "Vertex") {
- TechDrawGeometry::Vertex* v = getViewPart()->getProjVertexByIndex(idx);
- if (v == nullptr) {
- result = false;
- break;
+ const std::vector &objects = References2D.getValues();
+ if (!objects.empty()) {
+ const std::vector &subElements = References2D.getSubValues();
+ if (!subElements.empty()) {
+ for (auto& s: subElements) {
+ if (!s.empty()) {
+ int idx = DrawUtil::getIndexFromName(s);
+ if (DrawUtil::getGeomTypeFromName(s) == "Edge") {
+ TechDrawGeometry::BaseGeom* geom = getViewPart()->getProjEdgeByIndex(idx);
+ if (geom == nullptr) {
+ result = false;
+ break;
+ }
+ } else if (DrawUtil::getGeomTypeFromName(s) == "Vertex") {
+ TechDrawGeometry::Vertex* v = getViewPart()->getProjVertexByIndex(idx);
+ if (v == nullptr) {
+ result = false;
+ break;
+ }
+ }
+ } else {
+ result = false;
+ }
}
+ } else {
+ Base::Console().Log("DVD::checkRegerences2d() - %s - subelements empty!\n",getNameInDocument());
+ result = false;
}
+ } else {
+ Base::Console().Log("DVD::checkRegerences2d() - %s - objects empty!\n",getNameInDocument());
+ result = false;
}
return result;
}
@@ -913,16 +934,25 @@ bool DrawViewDimension::leaderIntersectsArc(Base::Vector3d s, Base::Vector3d poi
return result;
}
+//are there non-blank references?
bool DrawViewDimension::has2DReferences(void) const
{
+// Base::Console().Message("DVD::has2DReferences() - %s\n",getNameInDocument());
bool result = false;
+
const std::vector &objects = References2D.getValues();
const std::vector &SubNames = References2D.getSubValues();
if (!objects.empty()) {
App::DocumentObject* testRef = objects.at(0);
if (testRef != nullptr) {
if (!SubNames.empty()) {
- result = true;
+ result = true; //not empty is good
+ for (auto& s: SubNames) { //but check individual entries
+ if (s.empty()) {
+ result = false;
+ break;
+ }
+ }
}
}
}
diff --git a/src/Mod/TechDraw/App/DrawViewPart.cpp b/src/Mod/TechDraw/App/DrawViewPart.cpp
index 620df235e6..c99f250907 100644
--- a/src/Mod/TechDraw/App/DrawViewPart.cpp
+++ b/src/Mod/TechDraw/App/DrawViewPart.cpp
@@ -159,6 +159,8 @@ DrawViewPart::~DrawViewPart()
TopoDS_Shape DrawViewPart::getSourceShape(void) const
{
+// Base::Console().Message("DVP::getSourceShape() - %s\n", getNameInDocument());
+
TopoDS_Shape result;
const std::vector& links = Source.getValues();
if (links.empty()) {
@@ -203,6 +205,7 @@ TopoDS_Shape DrawViewPart::getSourceShape(void) const
std::vector DrawViewPart::getShapesFromObject(App::DocumentObject* docObj) const
{
+// Base::Console().Message("DVP::getShapesFromObject() - %s\n", getNameInDocument());
std::vector result;
App::GroupExtension* gex = dynamic_cast(docObj);
App::Property* gProp = docObj->getPropertyByName("Group");
@@ -250,6 +253,7 @@ std::vector DrawViewPart::getShapesFromObject(App::DocumentObject*
TopoDS_Shape DrawViewPart::getSourceShapeFused(void) const
{
+// Base::Console().Message("DVP::getSourceShapeFused() - %s\n", getNameInDocument());
TopoDS_Shape baseShape = getSourceShape();
if (!baseShape.IsNull()) {
TopoDS_Iterator it(baseShape);
@@ -272,6 +276,7 @@ TopoDS_Shape DrawViewPart::getSourceShapeFused(void) const
App::DocumentObjectExecReturn *DrawViewPart::execute(void)
{
+// Base::Console().Message("DVP::execute() - %s\n", getNameInDocument());
if (!keepUpdated()) {
return App::DocumentObject::StdReturn;
}
@@ -382,6 +387,7 @@ void DrawViewPart::onChanged(const App::Property* prop)
//note: slightly different than routine with same name in DrawProjectSplit
TechDrawGeometry::GeometryObject* DrawViewPart::buildGeometryObject(TopoDS_Shape shape, gp_Ax2 viewAxis)
{
+// Base::Console().Message("DVP::buildGO() - %s\n", getNameInDocument());
TechDrawGeometry::GeometryObject* go = new TechDrawGeometry::GeometryObject(getNameInDocument(), this);
go->setIsoCount(IsoCount.getValue());
go->isPerspective(Perspective.getValue());
@@ -436,11 +442,15 @@ TechDrawGeometry::GeometryObject* DrawViewPart::buildGeometryObject(TopoDS_Shape
go->extractGeometry(TechDrawGeometry::ecUVISO,
false);
}
+
auto end = chrono::high_resolution_clock::now();
auto diff = end - start;
double diffOut = chrono::duration (diff).count();
Base::Console().Log("TIMING - %s DVP spent: %.3f millisecs in GO::extractGeometry\n",getNameInDocument(),diffOut);
-
+ const std::vector & edges = go->getEdgeGeometry();
+ if (edges.empty()) {
+ Base::Console().Log("DVP::buildGO - NO extracted edges!\n");
+ }
bbox = go->calcBoundingBox();
return go;
}
diff --git a/src/Mod/TechDraw/App/GeometryObject.cpp b/src/Mod/TechDraw/App/GeometryObject.cpp
index 30c79364be..1bf2b8e454 100644
--- a/src/Mod/TechDraw/App/GeometryObject.cpp
+++ b/src/Mod/TechDraw/App/GeometryObject.cpp
@@ -162,7 +162,8 @@ void GeometryObject::clear()
void GeometryObject::projectShape(const TopoDS_Shape& input,
const gp_Ax2 viewAxis)
{
- // Clear previous Geometry
+// Base::Console().Message("GO::projectShape()\n");
+ // Clear previous Geometry
clear();
auto start = chrono::high_resolution_clock::now();
@@ -183,6 +184,7 @@ void GeometryObject::projectShape(const TopoDS_Shape& input,
}
brep_hlr->Update();
brep_hlr->Hide();
+
}
catch (Standard_Failure e) {
Base::Console().Error("GO::projectShape - OCC error - %s - while projecting shape\n",
@@ -386,6 +388,7 @@ void GeometryObject::extractGeometry(edgeClass category, bool visible)
//! update edgeGeom and vertexGeom from Compound of edges
void GeometryObject::addGeomFromCompound(TopoDS_Shape edgeCompound, edgeClass category, bool visible)
{
+// Base::Console().Message("GO::addGeomFromCompound()\n");
if(edgeCompound.IsNull()) {
Base::Console().Log("TechDraw::GeometryObject::addGeomFromCompound edgeCompound is NULL\n");
return; // There is no OpenCascade Geometry to be calculated
@@ -393,7 +396,8 @@ void GeometryObject::addGeomFromCompound(TopoDS_Shape edgeCompound, edgeClass ca
BaseGeom* base;
TopExp_Explorer edges(edgeCompound, TopAbs_EDGE);
- for (int i = 1 ; edges.More(); edges.Next(),i++) {
+ int i = 1;
+ for ( ; edges.More(); edges.Next(),i++) {
const TopoDS_Edge& edge = TopoDS::Edge(edges.Current());
if (edge.IsNull()) {
//Base::Console().Log("INFO - GO::addGeomFromCompound - edge: %d is NULL\n",i);
@@ -406,7 +410,7 @@ void GeometryObject::addGeomFromCompound(TopoDS_Shape edgeCompound, edgeClass ca
base = BaseGeom::baseFactory(edge);
if (base == nullptr) {
- Base::Console().Message("Error - GO::addGeomFromCompound - baseFactory failed for edge: %d\n",i);
+ Base::Console().Log("Error - GO::addGeomFromCompound - baseFactory failed for edge: %d\n",i);
throw Base::ValueError("GeometryObject::addGeomFromCompound - baseFactory failed");
}
base->classOfEdge = category;
@@ -533,18 +537,22 @@ bool GeometryObject::isWithinArc(double theta, double first,
Base::BoundBox3d GeometryObject::calcBoundingBox() const
{
+// Base::Console().Message("GO::calcBoundingBox() - edges: %d\n", edgeGeom.size());
Bnd_Box testBox;
testBox.SetGap(0.0);
- for (std::vector::const_iterator it( edgeGeom.begin() );
- it != edgeGeom.end(); ++it) {
- BRepBndLib::Add((*it)->occEdge, testBox);
+ if (!edgeGeom.empty()) {
+ for (std::vector::const_iterator it( edgeGeom.begin() );
+ it != edgeGeom.end(); ++it) {
+ BRepBndLib::Add((*it)->occEdge, testBox);
+ }
}
+
+ double xMin = 0,xMax = 0,yMin = 0,yMax = 0, zMin = 0, zMax = 0;
if (testBox.IsVoid()) {
Base::Console().Log("INFO - GO::calcBoundingBox - testBox is void\n");
+ } else {
+ testBox.Get(xMin,yMin,zMin,xMax,yMax,zMax);
}
- double xMin,xMax,yMin,yMax,zMin,zMax;
- testBox.Get(xMin,yMin,zMin,xMax,yMax,zMax);
-
Base::BoundBox3d bbox(xMin,yMin,zMin,xMax,yMax,zMax);
return bbox;
}
diff --git a/src/Mod/TechDraw/Gui/QGIViewDimension.cpp b/src/Mod/TechDraw/Gui/QGIViewDimension.cpp
index c9ca3b0652..b3fc660cac 100644
--- a/src/Mod/TechDraw/Gui/QGIViewDimension.cpp
+++ b/src/Mod/TechDraw/Gui/QGIViewDimension.cpp
@@ -516,6 +516,9 @@ void QGIViewDimension::draw()
const char *dimType = dim->Type.getValueAsString();
+ datumLabel->show();
+ show();
+
if (strcmp(dimType, "Distance") == 0 ||
strcmp(dimType, "DistanceX") == 0 ||
strcmp(dimType, "DistanceY") == 0) {